diff --git a/cvtest.py b/cvtest.py new file mode 100644 index 0000000..3302080 --- /dev/null +++ b/cvtest.py @@ -0,0 +1,176 @@ +import cv2 +import imutils +import matplotlib.pyplot as plt +import numpy as np +from mpl_toolkits.mplot3d import Axes3D +from matplotlib import cm +from matplotlib import colors + +RESIZE_RATIO = 0.07 + + +def shrink(img): + return cv2.resize(img, None, fx=RESIZE_RATIO, fy=RESIZE_RATIO) + + +def noop(*x): + pass + + +def darken_color(img, sat=20, val=225): + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + h, s, v = cv2.split(hsv) + s[s > sat] = 255 + v[v < val] = 0 + hsv2 = cv2.merge((h, s, v)) + + return cv2.cvtColor(hsv2, cv2.COLOR_HSV2BGR) + + +def interactive_edge(filename): + img = shrink(cv2.imread(filename)) + + cv2.namedWindow("image") + cv2.createTrackbar("threshold1", "image", 5, 50, noop) + cv2.createTrackbar("threshold2", "image", 20, 50, noop) + + while 1: + threshold1 = cv2.getTrackbarPos("threshold1", "image") + threshold2 = cv2.getTrackbarPos("threshold2", "image") + edged = edge_img(img, threshold1 * 10, threshold2 * 10) + # contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + # contours = imutils.grab_contours(contours) + # shapes = cv2.drawContours(edged.copy(), contours, -1, (255, 255, 255), -1) + cv2.imshow("image", edged) + if cv2.waitKey(0) < 0: + break + cv2.destroyWindow("image") + + +def edge_img(orig, t1=50, t2=200): + img = cv2.GaussianBlur(orig.copy(), (5, 5), 0) + img = darken_color(img) + + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + gray_blurred = cv2.GaussianBlur(gray, (5, 5), 0) + return cv2.Canny(gray_blurred, t1, t2) + + +def approximate_contour(c): + peri = cv2.arcLength(c, True) + return cv2.approxPolyDP(c, 0.02 * peri, True) + + +def group_contours(contours): + remaining = contours.copy() + groups = [] + while len(remaining) > 0: + group = [remaining[0]] + groups.append(group) + grew = False + remaining = remaining[1:] + while True: + nextremaining = [] + l, t, r, b = cv2.boundingRect(remaining[0]) + for c in remaining: + l2, t2, r2, b2 = cv2.boundingRect(c) + if r2 < l or l2 > r or t2 > b or b2 < t: + # they do not intersect + nextremaining.append(c) + else: + # they do intersect + grew = True + group.append(c) + l = min(l, l2) + t = min(t, t2) + r = max(r, r2) + b = max(b, b2) + remaining = nextremaining + if not grew: + break + return groups + + +def autocrop_contour(orig, contour): + approx = approximate_contour(contour) + if len(approx) == 4: + # It's a rectangle - perspective-correct it + pass + else: + # It's a disc - crop around it + pass + + +def interactive_contour(filename): + orig = cv2.imread(filename) + orig = shrink(orig) + + edge = edge_img(orig) + contours = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + contours = imutils.grab_contours(contours) + contours = sorted(contours, key=cv2.contourArea, reverse=True) + contours = [approximate_contour(c) for c in contours] + + cv2.namedWindow("image") + cv2.createTrackbar("contour", "image", 0, len(contours) - 1, noop) + + while True: + icontour = cv2.getTrackbarPos("contour", "image") + img = cv2.drawContours(orig.copy(), contours, icontour, (255, 0, 255), 3) + cv2.imshow("image", img) + if cv2.waitKey() < 0: + break + + +def get_pixel_colors(rgbimg): + pixel_colors = rgbimg.reshape((np.shape(rgbimg)[0] * np.shape(rgbimg)[1], 3)) + norm = colors.Normalize(vmin=-1.0, vmax=1.0) + norm.autoscale(pixel_colors) + return norm(pixel_colors).tolist() + + +def plot_hsv(rgbimg): + hsv = cv2.cvtColor(rgbimg.copy(), cv2.COLOR_BGR2HSV) + + h, s, v = cv2.split(hsv) + fig = plt.figure() + axis = fig.add_subplot(1, 1, 1, projection="3d") + axis.scatter( + h.flatten(), + s.flatten(), + v.flatten(), + facecolors=get_pixel_colors(rgbimg), + marker=".", + ) + axis.set_xlabel("Hue") + axis.set_ylabel("Sat") + axis.set_zlabel("Val") + plt.show() + + +def interactive_darken(filename): + img = cv2.imread(filename) + img = shrink(img) + hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + cv2.namedWindow("image") + cv2.createTrackbar("Sat", "image", 30, 255, noop) + cv2.createTrackbar("Val", "image", 225, 255, noop) + + while True: + sat = cv2.getTrackbarPos("Sat", "image") + val = cv2.getTrackbarPos("Val", "image") + + h, s, v = cv2.split(hsv) + s[s > sat] = 255 + v[v < val] = 0 + hsv2 = cv2.merge((h, s, v)) + + bgr = cv2.cvtColor(hsv2, cv2.COLOR_HSV2BGR) + cv2.imshow("image", bgr) + if cv2.waitKey() < 0: + break + + +if __name__ == "__main__": + interactive_edge("testimg3.jpg") diff --git a/poetry.lock b/poetry.lock index 5e18298..a7e23ea 100644 --- a/poetry.lock +++ b/poetry.lock @@ -31,6 +31,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "19.1.0" +[[package]] +category = "dev" +description = "Specifications for callback functions passed in to an API" +name = "backcall" +optional = false +python-versions = "*" +version = "0.1.0" + [[package]] category = "dev" description = "The uncompromising code formatter." @@ -74,6 +82,17 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.4.1" +[[package]] +category = "main" +description = "Composable style cycles" +name = "cycler" +optional = false +python-versions = "*" +version = "0.10.0" + +[package.dependencies] +six = "*" + [[package]] category = "dev" description = "Better living through Python with decorators" @@ -124,6 +143,14 @@ version = "0.23" [package.dependencies] zipp = ">=0.5" +[[package]] +category = "main" +description = "A series of convenience functions to make basic image processing functions such as translation, rotation, resizing, skeletonization, displaying Matplotlib images, sorting contours, detecting edges, and much more easier with OpenCV and both Python 2.7 and Python 3." +name = "imutils" +optional = false +python-versions = "*" +version = "0.5.3" + [[package]] category = "dev" description = "IPython Kernel for Jupyter" @@ -143,20 +170,20 @@ category = "dev" description = "IPython: Productive Interactive Computing" name = "ipython" optional = false -python-versions = ">=3.3" -version = "6.2.1" +python-versions = ">=3.5" +version = "7.8.0" [package.dependencies] appnope = "*" +backcall = "*" colorama = "*" decorator = "*" jedi = ">=0.10" pexpect = "*" pickleshare = "*" -prompt-toolkit = ">=1.0.4,<2.0.0" +prompt-toolkit = ">=2.0.0,<2.1.0" pygments = "*" setuptools = ">=18.5" -simplegeneric = ">0.8" traitlets = ">=4.2" [[package]] @@ -258,14 +285,14 @@ category = "dev" description = "Jupyter terminal console" name = "jupyter-console" optional = false -python-versions = "*" -version = "5.2.0" +python-versions = ">=3.5" +version = "6.0.0" [package.dependencies] ipykernel = "*" ipython = "*" jupyter-client = "*" -prompt-toolkit = ">=1.0.0,<2.0.0" +prompt-toolkit = ">=2.0.0,<2.1.0" pygments = "*" [[package]] @@ -279,6 +306,17 @@ version = "4.5.0" [package.dependencies] traitlets = "*" +[[package]] +category = "main" +description = "A fast implementation of the Cassowary constraint solver" +name = "kiwisolver" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.0" + +[package.dependencies] +setuptools = "*" + [[package]] category = "dev" description = "Safely add untrusted strings to HTML/XML markup." @@ -287,6 +325,21 @@ optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" version = "1.1.1" +[[package]] +category = "main" +description = "Python plotting package" +name = "matplotlib" +optional = false +python-versions = ">=3.6" +version = "3.1.1" + +[package.dependencies] +cycler = ">=0.10" +kiwisolver = ">=1.0.1" +numpy = ">=1.11" +pyparsing = ">=2.0.1,<2.0.4 || >2.0.4,<2.1.2 || >2.1.2,<2.1.6 || >2.1.6" +python-dateutil = ">=2.1" + [[package]] category = "dev" description = "McCabe checker, plugin for flake8" @@ -369,6 +422,25 @@ terminado = ">=0.8.1" tornado = ">=5.0" traitlets = ">=4.2.1" +[[package]] +category = "main" +description = "NumPy is the fundamental package for array computing with Python." +name = "numpy" +optional = false +python-versions = ">=3.5" +version = "1.17.2" + +[[package]] +category = "main" +description = "Wrapper package for OpenCV python bindings." +name = "opencv-python" +optional = false +python-versions = "*" +version = "4.1.1.26" + +[package.dependencies] +numpy = ">=1.11.1" + [[package]] category = "dev" description = "Utilities for writing pandoc filters in python" @@ -435,12 +507,12 @@ python-versions = "*" version = "0.7.1" [[package]] -category = "main" +category = "dev" description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" optional = false python-versions = "*" -version = "1.0.14" +version = "2.0.9" [package.dependencies] six = ">=1.9.0" @@ -480,7 +552,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.1.1" [[package]] -category = "main" +category = "dev" description = "Pygments is a syntax highlighting package written in Python." name = "pygments" optional = false @@ -489,28 +561,23 @@ version = "2.4.2" [[package]] category = "main" -description = "A Python module for collection of common interactive command line user interfaces, based on Inquirer.js" -name = "pyinquirer" +description = "Python library to access and use image scanners (Linux/Windows/etc)" +name = "pyinsane2" optional = false python-versions = "*" -version = "1.0.3" - -[package.dependencies] -Pygments = ">=2.2.0" -prompt_toolkit = "1.0.14" -regex = ">=2016.11.21" - -[[package]] -category = "main" -description = "Pure Python implementation of the Sane API (using ctypes) and abstration layer" -name = "pyinsane" -optional = false -python-versions = "*" -version = "1.4.0" +version = "2.0.13" [package.dependencies] Pillow = "*" +[[package]] +category = "main" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.2" + [[package]] category = "dev" description = "Persistent/Functional/Immutable data structures" @@ -541,7 +608,7 @@ setuptools = "*" six = ">=1.10.0" [[package]] -category = "dev" +category = "main" description = "Extensions to the standard Python datetime module" name = "python-dateutil" optional = false @@ -593,14 +660,6 @@ jupyter-core = "*" pygments = "*" traitlets = "*" -[[package]] -category = "main" -description = "Alternative regular expression module, to replace re." -name = "regex" -optional = false -python-versions = "*" -version = "2019.08.19" - [[package]] category = "dev" description = "Send file to trash natively under Mac OS X, Windows and Linux." @@ -609,14 +668,6 @@ optional = false python-versions = "*" version = "1.5.0" -[[package]] -category = "dev" -description = "Simple generic functions (similar to Python's own len(), pickle.dump(), etc.)" -name = "simplegeneric" -optional = false -python-versions = "*" -version = "0.8.1" - [[package]] category = "main" description = "Python 2 and 3 compatibility utilities" @@ -676,7 +727,7 @@ ipython-genutils = "*" six = "*" [[package]] -category = "main" +category = "dev" description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false @@ -715,7 +766,7 @@ version = "0.6.0" more-itertools = "*" [metadata] -content-hash = "a0b14789772e6aef430877c07f16231d14758c6a230276d81cb6466f3cd97c02" +content-hash = "7e200ef69278ec9f82a2bb37e54d6966e2190cd269bb1d714987dc4db9ab94f5" python-versions = "^3.7" [metadata.hashes] @@ -723,17 +774,20 @@ appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", " appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"] attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] +backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] bleach = ["213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"] click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] +cycler = ["1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d", "cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"] decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"] defusedxml = ["6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", "f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"] entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"] importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"] +imutils = ["857af6169d90e4a0a814130b9b107f5d611150ce440107e1c1233521c6fb1e2b"] ipykernel = ["167c3ef08450f5e060b76c749905acb0e0fbef9365899377a4a1eae728864383", "b503913e0b4cce7ed2de965457dfb2edd633e8234161a60e23f2fe2161345d12"] -ipython = ["51c158a6c8b899898d1c91c6b51a34110196815cc905f9be0fa5878e19355608", "fcc6d46f08c3c4de7b15ae1c426e15be1b7932bcda9d83ce1a4304e8c1129df3"] +ipython = ["c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", "dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1"] ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] ipywidgets = ["13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516", "e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"] jedi = ["786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", "ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"] @@ -741,15 +795,19 @@ jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "1 jsonschema = ["5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f", "8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d"] jupyter = ["3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", "5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", "d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"] jupyter-client = ["6a6d415c62179728f6d9295b37356d8f6833e9e01c2b6e1901dc555571f57b21", "f406f214f9daa92be110d5b83d62f3451ffc73d3522db7350f0554683533ab18"] -jupyter-console = ["3f928b817fc82cda95e431eb4c2b5eb21be5c483c2b43f424761a966bb808094", "545dedd3aaaa355148093c5609f0229aeb121b4852995c2accfa64fe3e0e55cd"] +jupyter-console = ["308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5", "cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"] jupyter-core = ["2c6e7c1e9f2ac45b5c2ceea5730bc9008d92fe59d0725eac57b04c0edfba24f7", "f4fa22d6cf25f34807c995f22d2923693575c70f02557bcbfbe59bd5ec8d8b84"] +kiwisolver = ["05b5b061e09f60f56244adc885c4a7867da25ca387376b02c1efc29cc16bcd0f", "26f4fbd6f5e1dabff70a9ba0d2c4bd30761086454aa30dddc5b52764ee4852b7", "3b2378ad387f49cbb328205bda569b9f87288d6bc1bf4cd683c34523a2341efe", "400599c0fe58d21522cae0e8b22318e09d9729451b17ee61ba8e1e7c0346565c", "47b8cb81a7d18dbaf4fed6a61c3cecdb5adec7b4ac292bddb0d016d57e8507d5", "53eaed412477c836e1b9522c19858a8557d6e595077830146182225613b11a75", "58e626e1f7dfbb620d08d457325a4cdac65d1809680009f46bf41eaf74ad0187", "5a52e1b006bfa5be04fe4debbcdd2688432a9af4b207a3f429c74ad625022641", "5c7ca4e449ac9f99b3b9d4693debb1d6d237d1542dd6a56b3305fe8a9620f883", "682e54f0ce8f45981878756d7203fd01e188cc6c8b2c5e2cf03675390b4534d5", "79bfb2f0bd7cbf9ea256612c9523367e5ec51d7cd616ae20ca2c90f575d839a2", "7f4dd50874177d2bb060d74769210f3bce1af87a8c7cf5b37d032ebf94f0aca3", "8944a16020c07b682df861207b7e0efcd2f46c7488619cb55f65882279119389", "8aa7009437640beb2768bfd06da049bad0df85f47ff18426261acecd1cf00897", "939f36f21a8c571686eb491acfffa9c7f1ac345087281b412d63ea39ca14ec4a", "9733b7f64bd9f807832d673355f79703f81f0b3e52bfce420fc00d8cb28c6a6c", "a02f6c3e229d0b7220bd74600e9351e18bc0c361b05f29adae0d10599ae0e326", "a0c0a9f06872330d0dd31b45607197caab3c22777600e88031bfe66799e70bb0", "acc4df99308111585121db217681f1ce0eecb48d3a828a2f9bbf9773f4937e9e", "b64916959e4ae0ac78af7c3e8cef4becee0c0e9694ad477b4c6b3a536de6a544", "d3fcf0819dc3fea58be1fd1ca390851bdb719a549850e708ed858503ff25d995", "d52e3b1868a4e8fd18b5cb15055c76820df514e26aa84cc02f593d99fef6707f", "db1a5d3cc4ae943d674718d6c47d2d82488ddd94b93b9e12d24aabdbfe48caee", "e3a21a720791712ed721c7b95d433e036134de6f18c77dbe96119eaf7aa08004", "e8bf074363ce2babeb4764d94f8e65efd22e6a7c74860a4f05a6947afc020ff2", "f16814a4a96dc04bf1da7d53ee8d5b1d6decfc1a92a63349bb15d37b6a263dd9", "f2b22153870ca5cf2ab9c940d7bc38e8e9089fa0f7e5856ea195e1cf4ff43d5a", "f790f8b3dff3d53453de6a7b7ddd173d2e020fb160baff578d578065b108a05f"] markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] +matplotlib = ["1febd22afe1489b13c6749ea059d392c03261b2950d1d45c17e3aed812080c93", "31a30d03f39528c79f3a592857be62a08595dec4ac034978ecd0f814fa0eec2d", "4442ce720907f67a79d45de9ada47be81ce17e6c2f448b3c64765af93f6829c9", "796edbd1182cbffa7e1e7a97f1e141f875a8501ba8dd834269ae3cd45a8c976f", "934e6243df7165aad097572abf5b6003c77c9b6c480c3c4de6f2ef1b5fdd4ec0", "bab9d848dbf1517bc58d1f486772e99919b19efef5dd8596d4b26f9f5ee08b6b", "c1fe1e6cdaa53f11f088b7470c2056c0df7d80ee4858dadf6cbe433fcba4323b", "e5b8aeca9276a3a988caebe9f08366ed519fff98f77c6df5b64d7603d0e42e36", "ec6bd0a6a58df3628ff269978f4a4b924a0d371ad8ce1f8e2b635b99e482877a"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"] more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"] nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"] nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"] notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"] +numpy = ["05dbfe72684cc14b92568de1bc1f41e5f62b00f714afc9adee42f6311738091f", "0d82cb7271a577529d07bbb05cb58675f2deb09772175fab96dc8de025d8ac05", "10132aa1fef99adc85a905d82e8497a580f83739837d7cbd234649f2e9b9dc58", "12322df2e21f033a60c80319c25011194cd2a21294cc66fee0908aeae2c27832", "16f19b3aa775dddc9814e02a46b8e6ae6a54ed8cf143962b4e53f0471dbd7b16", "3d0b0989dd2d066db006158de7220802899a1e5c8cf622abe2d0bd158fd01c2c", "438a3f0e7b681642898fd7993d38e2bf140a2d1eafaf3e89bb626db7f50db355", "5fd214f482ab53f2cea57414c5fb3e58895b17df6e6f5bca5be6a0bb6aea23bb", "73615d3edc84dd7c4aeb212fa3748fb83217e00d201875a47327f55363cef2df", "7bd355ad7496f4ce1d235e9814ec81ee3d28308d591c067ce92e49f745ba2c2f", "7d077f2976b8f3de08a0dcf5d72083f4af5411e8fddacd662aae27baa2601196", "a4092682778dc48093e8bda8d26ee8360153e2047826f95a3f5eae09f0ae3abf", "b458de8624c9f6034af492372eb2fee41a8e605f03f4732f43fc099e227858b2", "e70fc8ff03a961f13363c2c95ef8285e0cf6a720f8271836f852cc0fa64e97c8", "ee8e9d7cad5fe6dde50ede0d2e978d81eafeaa6233fb0b8719f60214cf226578", "f4a4f6aba148858a5a5d546a99280f71f5ee6ec8182a7d195af1a914195b21a2"] +opencv-python = ["01505b131dc35f60e99a5da98b77156e37f872ae0ff5596e5e68d526bb572d3c", "0478a1037505ddde312806c960a5e8958d2cf7a2885e8f2f5dde74c4028e0b04", "17810b89f9ef8e8537e75332acf533e619e26ccadbf1b73f24bf338f2d327ddd", "19ad2ea9fb32946761b47b9d6eed51876a8329da127f27788263fecd66651ba0", "1a250edb739baf3e7c25d99a2ee252aac4f59a97e0bee39237eaa490fd0281d3", "3505468970448f66cd776cb9e179570c87988f94b5cf9bcbc4c2d88bd88bbdf1", "4e04a91da157885359f487534433340b2d709927559c80acf62c28167e59be02", "5a49cffcdec5e37217672579c3343565926d999642844efa9c6a031ed5f32318", "604b2ce3d4a86480ced0813da7fba269b4605ad9fea26cd2144d8077928d4b49", "61cbb8fa9565a0480c46028599431ad8f19181a7fac8070a700515fd54cd7377", "62d7c6e511c9454f099616315c695d02a584048e1affe034b39160db7a2ae34d", "6555272dd9efd412d17cdc1a4f4c2da5753c099d95d9ff01aca54bb9782fb5cf", "67d994c6b2b14cb9239e85dc7dfa6c08ef7cf6eb4def80c0af6141dfacc8cbb9", "68c9cbe538666c4667523821cc56caee49389bea06bae4c0fc2cd68bd264226a", "822ad8f628a9498f569c57d30865f5ef9ee17824cee0a1d456211f742028c135", "82d972429eb4fee22c1dc4204af2a2e981f010e5e4f66daea2a6c68381b79184", "9128924f5b58269ee221b8cf2d736f31bd3bb0391b92ee8504caadd68c8176a2", "9172cf8270572c494d8b2ae12ef87c0f6eed9d132927e614099f76843b0c91d7", "952bce4d30a8287b17721ddaad7f115dab268efee8576249ddfede80ec2ce404", "a8147718e70b1f170a3d26518e992160137365a4db0ed82a9efd3040f9f660d4", "bfdb636a3796ff223460ea0fcfda906b3b54f4bef22ae433a5b67e66fab00b25", "c9c3f27867153634e1083390920067008ebaaab78aeb09c4e0274e69746cb2c8", "d69be21973d450a4662ae6bd1b3df6b1af030e448d7276380b0d1adf7c8c2ae6", "db1479636812a6579a3753b72a6fefaa73190f32bf7b19e483f8bc750cebe1a5", "db8313d755962a7dd61e5c22a651e0743208adfdb255c6ec8904ce9cb02940c6", "e4625a6b032e7797958aeb630d6e3e91e3896d285020aae612e6d7b342d6dfea", "e8397a26966a1290836a52c34b362aabc65a422b9ffabcbbdec1862f023ccab8"] pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"] parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] @@ -757,14 +815,14 @@ pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca pillow = ["0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de", "0ab7c5b5d04691bcbd570658667dd1e21ca311c62dcfd315ad2255b1cd37f64f", "0b3e6cf3ea1f8cecd625f1420b931c83ce74f00c29a0ff1ce4385f99900ac7c4", "0c6ce6ae03a50b0306a683696234b8bc88c5b292d4181ae365b89bd90250ab08", "1454ee7297a81c8308ad61d74c849486efa1badc543453c4b90db0bf99decc1c", "23efd7f83f2ad6036e2b9ef27a46df7e333de1ad9087d341d87e12225d0142b2", "365c06a45712cd723ec16fa4ceb32ce46ad201eb7bbf6d3c16b063c72b61a3ed", "38301fbc0af865baa4752ddae1bb3cbb24b3d8f221bf2850aad96b243306fa03", "3aef1af1a91798536bbab35d70d35750bd2884f0832c88aeb2499aa2d1ed4992", "3c86051d41d1c8b28b9dde08ac93e73aa842991995b12771b0af28da49086bbf", "3fe0ab49537d9330c9bba7f16a5f8b02da615b5c809cdf7124f356a0f182eccd", "406c856e0f6fc330322a319457d9ff6162834050cda2cf1eaaaea4b771d01914", "45a619d5c1915957449264c81c008934452e3fd3604e36809212300b2a4dab68", "49f90f147883a0c3778fd29d3eb169d56416f25758d0f66775db9184debc8010", "504f5334bfd974490a86fef3e3b494cd3c332a8a680d2f258ca03388b40ae230", "51fe9cfcd32c849c6f36ca293648f279fc5097ca8dd6e518b10df3a6a9a13431", "571b5a758baf1cb6a04233fb23d6cf1ca60b31f9f641b1700bfaab1194020555", "5ac381e8b1259925287ccc5a87d9cf6322a2dc88ae28a97fe3e196385288413f", "6052a9e9af4a9a2cc01da4bbee81d42d33feca2bde247c4916d8274b12bb31a4", "6153db744a743c0c8c91b8e3b9d40e0b13a5d31dbf8a12748c6d9bfd3ddc01ad", "6fd63afd14a16f5d6b408f623cc2142917a1f92855f0df997e09a49f0341be8a", "70acbcaba2a638923c2d337e0edea210505708d7859b87c2bd81e8f9902ae826", "70b1594d56ed32d56ed21a7fbb2a5c6fd7446cdb7b21e749c9791eac3a64d9e4", "76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686", "7b2ec162c87fc496aa568258ac88631a2ce0acfe681a9af40842fc55deaedc99", "7b403ea842b70c4fa0a4969a5d8d86e932c941095b7cda077ea68f7b98ead30b", "7be698a28175eae5354da94f5f3dc787d5efae6aca7ad1f286a781afde6a27dd", "7cee2cef07c8d76894ebefc54e4bb707dfc7f258ad155bd61d87f6cd487a70ff", "7d16d4498f8b374fc625c4037742fbdd7f9ac383fd50b06f4df00c81ef60e829", "82840783842b27933cc6388800cb547f31caf436f7e23384d456bdf5fc8dfe49", "8755e600b33f4e8c76a590b42acc35d24f4dc801a5868519ce569b9462d77598", "9159285ab4030c6f85e001468cb5886de05e6bd9304e9e7d46b983f7d2fad0cc", "b50bc1780681b127e28f0075dfb81d6135c3a293e0c1d0211133c75e2179b6c0", "b5aa19f1da16b4f5e47b6930053f08cba77ceccaed68748061b0ec24860e510c", "bd0582f831ad5bcad6ca001deba4568573a4675437db17c4031939156ff339fa", "cdd53acd3afb9878a2289a1b55807871f9877c81174ae0d3763e52f907131d25", "cfd40d8a4b59f7567620410f966bb1f32dc555b2b19f82a91b147fac296f645c", "e150c5aed6e67321edc6893faa6701581ca2d393472f39142a00e551bcd249a5", "e3ae410089de680e8f84c68b755b42bc42c0ceb8c03dbea88a5099747091d38e", "e403b37c6a253ebca5d0f2e5624643997aaae529dc96299162418ef54e29eb70", "e9046e559c299b395b39ac7dbf16005308821c2f24a63cae2ab173bd6aa11616", "ef6be704ae2bc8ad0ebc5cb850ee9139493b0fc4e81abcc240fb392a63ebc808", "f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b"] pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"] prometheus-client = ["71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"] -prompt-toolkit = ["7281b5199235adaef6980942840c43753e4ab20dfe41338da634fb41c194f9d8", "82c7f8e07d7a0411ff5367a5a8ff520f0112b9179f3e599ee8ad2ad9b943d911", "cc66413b1b4b17021675d9f2d15d57e640b06ddfd99bb724c73484126d22622f"] +prompt-toolkit = ["11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", "2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", "977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"] ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"] pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] -pyinquirer = ["c9a92d68d7727fbd886a7908c08fd9e9773e5dc211bf5cbf836ba90d366dee51"] -pyinsane = ["11df7b8abc0875a00cf2257b736fcf7b7913bd93e0428bf5b7decb9f143df74c"] +pyinsane2 = ["0d519531d552e4512776225eb400a6a4a9bfc83a08918ec7fea19cb2fa7ec4ee", "a172fc2947ce9d3ed0b3f3b204b5e2fd57658f672d346c54f7f29d727fe56d0c", "df3ffdb2abd52fb4b5ca1f47df8c39a995ce04bf1f6f09250cb9e77cb0dddfb8"] +pyparsing = ["6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", "d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"] pyrsistent = ["34b47fa169d6006b32e99d4b3c4031f155e6e68ebcc107d6454852e8e0ee6533"] pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"] python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] @@ -772,9 +830,7 @@ pywin32 = ["0443e9bb196e72480f50cbddc2cf98fbb858a77d02e281ba79489ea3287b36e9", " pywinpty = ["0e01321e53a230233358a6d608a1a8bc86c3882cf82769ba3c62ca387dc9cc51", "333e0bc5fca8ad9e9a1516ebedb2a65da38dc1f399f8b2ea57d6cccec1ff2cc8", "3ca3123aa6340ab31bbf9bd012b92e72f9ec905e4c9ee152cc997403e1778cd3", "44a6dddcf2abf402e22f87e2c9a341f7d0b296afbec3d28184c8de4d7f514ee4", "53d94d574c3d4da2df5b1c3ae728b8d90e4d33502b0388576bbd4ddeb4de0f77", "c3955f162c53dde968f3fc11361658f1d83b683bfe601d4b6f94bb01ea4300bc", "cec9894ecb34de3d7b1ca121dd98433035b9f8949b5095e84b103b349231509c", "dcd45912e2fe2e6f72cee997a4da6ed1ad2056165a277ce5ec7f7ac98dcdf667", "f2bcdd9a2ffd8b223752a971b3d377fb7bfed85f140ec9710f1218d760f2ccb7"] pyzmq = ["01636e95a88d60118479041c6aaaaf5419c6485b7b1d37c9c4dd424b7b9f1121", "021dba0d1436516092c624359e5da51472b11ba8edffa334218912f7e8b65467", "0463bd941b6aead494d4035f7eebd70035293dd6caf8425993e85ad41de13fa3", "05fd51edd81eed798fccafdd49c936b6c166ffae7b32482e4d6d6a2e196af4e6", "1fadc8fbdf3d22753c36d4172169d184ee6654f8d6539e7af25029643363c490", "22efa0596cf245a78a99060fe5682c4cd00c58bb7614271129215c889062db80", "260c70b7c018905ec3659d0f04db735ac830fe27236e43b9dc0532cf7c9873ef", "2762c45e289732d4450406cedca35a9d4d71e449131ba2f491e0bf473e3d2ff2", "2fc6cada8dc53521c1189596f1898d45c5f68603194d3a6453d6db4b27f4e12e", "343b9710a61f2b167673bea1974e70b5dccfe64b5ed10626798f08c1f7227e72", "41bf96d5f554598a0632c3ec28e3026f1d6591a50f580df38eff0b8067efb9e7", "856b2cdf7a1e2cbb84928e1e8db0ea4018709b39804103d3a409e5584f553f57", "85b869abc894672de9aecdf032158ea8ad01e2f0c3b09ef60e3687fb79418096", "93f44739db69234c013a16990e43db1aa0af3cf5a4b8b377d028ff24515fbeb3", "98fa3e75ccb22c0dc99654e3dd9ff693b956861459e8c8e8734dd6247b89eb29", "9a22c94d2e93af8bebd4fcf5fa38830f5e3b1ff0d4424e2912b07651eb1bafb4", "a7d3f4b4bbb5d7866ae727763268b5c15797cbd7b63ea17f3b0ec1067da8994b", "b645a49376547b3816433a7e2d2a99135c8e651e50497e7ecac3bd126e4bea16", "cf0765822e78cf9e45451647a346d443f66792aba906bc340f4e0ac7870c169c", "dc398e1e047efb18bfab7a8989346c6921a847feae2cad69fedf6ca12fb99e2c", "dd5995ae2e80044e33b5077fb4bc2b0c1788ac6feaf15a6b87a00c14b4bdd682", "e03fe5e07e70f245dc9013a9d48ae8cc4b10c33a1968039c5a3b64b5d01d083d", "ea09a306144dff2795e48439883349819bef2c53c0ee62a3c2fae429451843bb", "f4e37f33da282c3c319849877e34f97f0a3acec09622ec61b7333205bdd13b52", "fa4bad0d1d173dee3e8ef3c3eb6b2bb6c723fc7a661eeecc1ecb2fa99860dd45"] qtconsole = ["40d5d8e00d070ea266dbf6f0da74c4b9597b8b8d67cd8233c3ffd8debf923703", "b91e7412587e6cfe1644696538f73baf5611e837be5406633218443b2827c6d9"] -regex = ["1e9f9bc44ca195baf0040b1938e6801d2f3409661c15fe57f8164c678cfc663f", "587b62d48ca359d2d4f02d486f1f0aa9a20fbaf23a9d4198c4bed72ab2f6c849", "835ccdcdc612821edf132c20aef3eaaecfb884c9454fdc480d5887562594ac61", "93f6c9da57e704e128d90736430c5c59dd733327882b371b0cae8833106c2a21", "a46f27d267665016acb3ec8c6046ec5eae8cf80befe85ba47f43c6f5ec636dcd", "c5c8999b3a341b21ac2c6ec704cfcccbc50f1fedd61b6a8ee915ca7fd4b0a557", "d4d1829cf97632673aa49f378b0a2c3925acd795148c5ace8ef854217abbee89", "d96479257e8e4d1d7800adb26bf9c5ca5bab1648a1eddcac84d107b73dc68327", "f20f4912daf443220436759858f96fefbfc6c6ba9e67835fd6e4e9b73582791a", "f2b37b5b2c2a9d56d9e88efef200ec09c36c7f323f9d58d0b985a90923df386d", "fe765b809a1f7ce642c2edeee351e7ebd84391640031ba4b60af8d91a9045890"] send2trash = ["60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", "f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"] -simplegeneric = ["dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"] six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] terminado = ["d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460", "de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"] testpath = ["46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109", "b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"] diff --git a/pyproject.toml b/pyproject.toml index 0cf1a28..700aa22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,8 +6,10 @@ authors = ["Jeremy Penner "] [tool.poetry.dependencies] python = "^3.7" -PyInquirer = "^1.0" -pyinsane = "^1.4" +pyinsane2 = "^2.0" +opencv-python = "^4.1" +imutils = "^0.5.3" +matplotlib = "^3.1" [tool.poetry.dev-dependencies] pytest = "^3.0" diff --git a/romtool/__init__.py b/romtool/__init__.py index 335325d..5f61bd4 100644 --- a/romtool/__init__.py +++ b/romtool/__init__.py @@ -1,10 +1,10 @@ __version__ = "0.1.0" -from cuebin import CueBin +# from cuebin import CueBin # from file import restore, backup -import glob +# import glob -for cuefile in glob.glob("D:\\ROMs\\PC\\*.cue"): - CueBin(cuefile).fixAll() - # restore(cuefile) +# for cuefile in glob.glob("D:\\ROMs\\PC\\*.cue"): +# CueBin(cuefile).fixAll() +# restore(cuefile) diff --git a/romtool/menu.py b/romtool/menu.py deleted file mode 100644 index fc0f6a9..0000000 --- a/romtool/menu.py +++ /dev/null @@ -1,7 +0,0 @@ -from PyInquirer import prompt - - -def prompt1(q): - q = q.copy() - q["name"] = "response" - return prompt([q])["response"] diff --git a/romtool/scan.py b/romtool/scan.py new file mode 100644 index 0000000..e69eff9 --- /dev/null +++ b/romtool/scan.py @@ -0,0 +1,20 @@ +import pyinsane2 +import cv2 +import imutils + +pyinsane2.init() +scanner = pyinsane2.get_devices()[0] + + +def scan(): + pyinsane2.set_scanner_opt(scanner, "resolution", [600]) + pyinsane2.set_scanner_opt(scanner, "mode", ["Color"]) + pyinsane2.maximize_scan_area(scanner) + + scan = scanner.scan() + try: + while True: + scan.scan.read() + except EOFError: + pass + return scan.images[0] diff --git a/sw b/sw new file mode 100644 index 0000000..a8cb4fb --- /dev/null +++ b/sw @@ -0,0 +1,837 @@ +#!/usr/bin/env python +import internetarchive +import json +import sys +import os +from glob import glob as pyglob +import sh +import re +import requests +from urllib import urlretrieve +from urlparse import urljoin +import unicodedata +from flask import Flask, render_template, request, make_response, send_from_directory +import requests +from bs4 import BeautifulSoup +from binascii import a2b_base64 +from ConfigParser import SafeConfigParser + +VALID_OPTIONS = set(['user', 'dbdir', 'db', 'dosbox_install', 'default_collection']) +def load_config(): + config = SafeConfigParser() + files = [os.path.expanduser('~/.config/sw.ini')] + path = os.getcwd() + while path != '/': + files.insert(1, os.path.join(path, 'sw.ini')) + path = os.path.dirname(path) + config.read(files) + for k, v in config.items('config'): + if k.lower() in VALID_OPTIONS: + globals()[k.upper()] = v + +load_config() +if 'DB' not in globals(): + DB=os.path.join(DBDIR, 'ia.json') + + +# DEFAULT_COLLECTION = 'softwarelibrary_msdos_shareware' +# DEFAULT_COLLECTION = 'open_source_software' +# DEFAULT_COLLECTION = 'softwarelibrary_win3_shareware' +# DEFAULT_COLLECTION = 'glorious_trainwrecks' + +def genid(name, spec): + # return 'msdos_' + name + '_shareware' + # return 'actionpoint_' + name + # return 'win3_' + name # + '_knp' + return 'gtrwx_' + name + +def gen_metadata(spec, orig_file): + return + +def fixklik(spec): + klikfiles = {'exe': None, 'gam': None, 'img': None} + for line in sh.unzip('-l', spec['upload']).splitlines(): + match = re.match(r"\s*\d+\s+\S+\s\S+\s+(.+)", line) + file = match.group(1) if match else '' + for ext in klikfiles.iterkeys(): + if file.lower().endswith('.' + ext): + if klikfiles[ext]: + return + klikfiles[ext] = file + for fn in klikfiles.itervalues(): + if not fn or not klikfiles['exe'] or fn[:-3] != klikfiles['exe'][:-3]: + return + exe = klikfiles['exe'] + if '/' in exe: + unzipdir = os.path.join(DBDIR, 'tmp') + if os.path.exists(unzipdir): + sh.rm('-r', unzipdir) + sh.mkdir('-p', unzipdir) + dirOld = os.getcwd() + zipfile = os.path.abspath(spec['upload']) + os.chdir(unzipdir) + sh.unzip('-q', zipfile) + print exe, exe[:exe.rfind('/')], zipfile + zipdir(exe[:exe.rfind('/')], zipfile) + exe = exe[exe.rfind('/') + 1:] + spec['upload'] = zipfile + os.chdir(dirOld) + spec['emulator_start'] = 'd:\\runapp ' + exe + spec['dosbox_drive_d'] = 'emularity_win31/win31.zip' + + +def gen_metadata_json(spec, orig_file): + spec['subject'] = 'klik & play;Geocities' + print orig_file, os.path.split(orig_file)[1] + try: + gamesjson_path = os.path.join(os.path.split(orig_file)[0], "games.json") + print gamesjson_path + with open(gamesjson_path, 'rt') as f: + meta = json.load(f) + print meta + if os.path.split(orig_file)[1] in meta: + vals = meta[os.path.split(orig_file)[1]] + if len(vals) >= 1: + spec['title'] = vals[0] + if len(vals) >= 2: + spec['description'] = vals[1] + if len(vals) >= 3: + spec['creator'] = vals[2] + site = None + if '_AUTHOR' in meta: + if 'creator' not in spec: + spec['creator'] = meta['_AUTHOR'] + site = meta['_AUTHOR'] + if '_SITE' in meta: + site = meta['_SITE'] + if '_URL' in meta: + link = '' + if site: + link += site + else: + link += 'this Geocities site' + link += '' + spec['description'] = (spec.get('description', '') + """ + +(Retrieved from """ + link + """.)""") + except: + pass + +SPEC_DEFAULTS = { + 'emulator': 'dosbox', + 'emulator_ext': 'zip', + 'collection': DEFAULT_COLLECTION, + 'mediatype': 'software', + 'dosbox_drive_d': 'emularity_win31/win31.zip' +} + +def genid_piratekart(name, spec): + spec['creator'] = 'ShaperMC' + default_tags = ["glorious trainwrecks", "klik & play", "The 100-in-1 Klik & Play Pirate Kart"] + + def matchheader(start, line): + match = re.match(start + ".*: *(.*)", line) + if match: + return match.group(1) + + gametxt = patchstr(sh.unzip('-p', '-C', spec['upload'], 'game.txt', _encoding='cp437')) + for line in gametxt.split('\n'): + if matchheader("D", line): + spec['description'] = matchheader("D", line).strip() + elif matchheader("N", line): + spec['title'] = matchheader("N", line).strip() + elif matchheader("(?:G|Tag)", line): + tags = [tag.strip() for tag in matchheader("(?:G|Tag)", line).split(',')] + print tags + tags = tags + default_tags + spec['subject'] = ";".join(tags) + for tag in tags: + if tag.startswith("kotmk"): + name = tag + "_" + name + break +def loaddb(): + with open(DB, 'rt') as f: + return json.load(f) + +def savedb(db): + with open(DB, 'wt') as f: + json.dump(db, f) + +def scan(db): + """Finds everything I've uploaded and adds it to the DB.""" + for result in internetarchive.search.Search('uploader:(jeremy@sporktania.com)', ['identifier', 'title', 'collection']): + id = result['identifier'] + if id not in db: + db[id] = {'status': 'unknown'} + db[id]['title'] = result['title'] + db[id]['collection'] = result['collection'] + +def diff(db, newdb_filename): + """Returns a db with all of the items in newdb that don't exist in db.""" + with open(newdb_filename, 'rt') as f: + newdb = json.load(f) + diffdb = {} + for id, val in newdb.iteritems(): + if db.get(id) != val: + diffdb[id] = val + return diffdb + +def check(db, collection=None, ignore=set(['fine', 'dark'])): + """Prints items and their status, for reporting non-working items to Jason.""" + status = {'reshoot': []} + for id, val in db.iteritems(): + if collection != None and collection not in val.get('collection', []): + continue + if val['status'] not in status: + status[val['status']] = [] + if val.get('reshoot'): + status['reshoot'].append(id) + status[val['status']].append(id) + for s, ids in status.iteritems(): + if s not in ignore: + print "\n" + print s + ":" + for id in ids: + print "http://archive.org/details/" + id + +def setstatus(db, id, status, title=None): + if id not in db: + db[id] = {} + db[id]['status'] = status + if title: + db[id]['title'] = title + savedb(db) + +def patchstr(s): + """Remove line-drawing (non-ascii) characters from a string""" + return "".join(ch for ch in unicode(s) if unicodedata.category(ch)[0]!="C" or ch == '\n') + +def safefilename(s): + return re.sub('[^a-zA-Z0-9_-]', '_', s) +def fix(spec): + """Tries to generate an ID and description""" + print "fix", spec + if 'upload' in spec: + fullname, ext = os.path.splitext(spec['upload']) + spec['upload'] = fullname + ext.lower() + name = safefilename(os.path.basename(fullname)) + print name + if 'id' not in spec: + spec['id'] = genid(name, spec) + if 'description' not in spec: + try: + spec['description'] = patchstr(sh.unzip('-p', '-C', spec['upload'], 'FILE_ID.DIZ', _encoding='cp437')) + except sh.ErrorReturnCode: + pass + +def defprompt(name): + def decorator(func): + func.name = name + return func + return decorator + +def mvprompt(name): + @defprompt(name) + def mv(spec): + destpath = os.path.join(os.path.dirname(spec['upload']), name) + sh.mkdir('-p', destpath) + sh.mv(spec['upload'], destpath) + return mv + +def dbinst(zip): + sh.rm('-r', DOSBOX_INSTALL) + sh.mkdir('-p', os.path.join(DOSBOX_INSTALL, 'INSTALL')) + sh.cp(zip, DOSBOX_INSTALL) + dirOld = os.getcwd() + os.chdir(os.path.join(DOSBOX_INSTALL, 'INSTALL')) + sh.unzip('-q', '../' + os.path.basename(zip)) + sh.open('../..') + os.chdir(dirOld) + +def dbinstexe(exe): + sh.rm('-r', DOSBOX_INSTALL) + sh.mkdir('-p', os.path.join(DOSBOX_INSTALL, 'INSTALL')) + sh.cp(exe, os.path.join(DOSBOX_INSTALL, 'INSTALL')) + sh.open(os.path.join(DOSBOX_INSTALL, '..')) + +@defprompt('pkginst') +def pkginstprompt(spec): + if pkginst(spec): + return "sync" if prompt(spec) else "ignore" + return "ignore" + +@defprompt('dbinst') +def dbinstprompt(spec): + dbinst(spec['upload']) + x = raw_input("Hit Enter when done (x to cancel): ") + if x == 'x': + return 'ignore' + return pkginstprompt(spec) + +@defprompt('skip') +def skipprompt(spec): + pass + +@defprompt('metadata') +def sync_only_metadata(spec): + del spec['upload'] + sync(spec, ensure_new=False) + +@defprompt('win31') +def win31prompt(spec): + spec['dosbox_drive_d'] = 'emularity_win31/win31.zip' + return 'ignore' + +@defprompt('dos') +def dosprompt(spec): + spec['dosbox_drive_d'] = None + return 'ignore' + +@defprompt('maxcpu') +def maxcpuprompt(spec): + unzipdir = os.path.join(DBDIR, 'tmp') + if os.path.exists(unzipdir): + sh.rm('-r', unzipdir) + sh.mkdir('-p', unzipdir) + dirOld = os.getcwd() + zipfile = os.path.abspath(spec['upload']) + os.chdir(unzipdir) + sh.unzip('-q', zipfile) + sh.cp(os.path.join(DBDIR, 'maxcpu.dosbox.conf'), 'dosbox.conf') + zipdir('.', zipfile) + os.chdir(dirOld) + return 'ignore' + +def markprompt(db, status): + @defprompt(status) + def mark(spec): + setstatus(db, spec['id'], status, spec.get('title')) + return mark + +def run_prompts(input, spec, prompts): + while True: + if prompts: + for k, v in prompts.iteritems(): + print k + '=' + v.name, + print '' + result = raw_input(input) + if result in prompts: + result = prompts[result](spec) + if result != "ignore": + return (False, result) + else: + return (True, result) + +def prompt(spec, prompts={}, prompt_title=True): + if 'upload' in spec and ('title' not in spec or 'emulator_start' not in spec): + print spec['id'] + if 'description' in spec: + print spec['description'] + try: + print sh.grep(sh.unzip('-l', spec['upload']), '-i', '.[bec][axo][tem]') + except sh.ErrorReturnCode: + print "Unexpected error:", sys.exc_info()[0] + print "no exe found, skipping" + return None + + (isexe, exe) = run_prompts('EXE: ', spec, prompts) + if isexe: + if spec.get('dosbox_drive_d', SPEC_DEFAULTS.get('dosbox_drive_d')): + spec['emulator_start'] = 'd:\\runapp ' + exe + else: + spec['emulator_start'] = exe + if 'title' not in spec and prompt_title: + spec['title'] = raw_input('Title: ') + return True + elif exe == 'sync': + return True + if 'upload' in spec and 'emulator_start' in spec and 'title' in spec: + return True + +def pkginst(spec, uploadPath=None, prompts={}): + print sh.ls('-al', DOSBOX_INSTALL) + (success, d) = run_prompts('Dir: ', spec, prompts) + if not success: + return d == 'sync' + + dirToZip = os.path.join(DOSBOX_INSTALL, d) + if not os.path.exists(dirToZip): + return False + name = spec.get('id') + if not name: + name = raw_input('ID: ') + if not uploadPath: + uploadPath = os.path.join(DBDIR, name + '.zip') + zipdir(dirToZip, uploadPath) + spec['upload'] = uploadPath + return True + +def zipdir(dirToZip, zipPath): + dirOld = os.getcwd() + zipPath = os.path.abspath(zipPath) + os.chdir(dirToZip) + if os.path.exists(zipPath): + sh.rm(zipPath) + sh.zip('-r', zipPath, sh.glob('./*')) + os.chdir(dirOld) + +def sync(spec, db=None, ensure_new=True): + print json.dumps(spec) + + item = internetarchive.get_item(spec['id']) + if ensure_new and item.exists: + raise Exception("Item " + spec['id'] + " already exists!") + + mdOld = item.metadata.get('metadata', {}) + mdNew = {} + for key in ['title', 'description', 'emulator_start', 'emulator', 'emulator_ext', 'dosbox_drive_d', 'subject', 'creator']: + if key in spec and (key not in mdOld or mdOld[key] != spec[key]): + mdNew[key] = spec[key] + + for key, value in SPEC_DEFAULTS.iteritems(): + if key not in mdOld and key not in spec: + mdNew[key] = value + + try: + if 'upload' in spec: + print "uploading", spec['upload'], "to", spec['id'] + item.upload(spec['upload'], metadata=mdNew) + if mdNew and mdOld: + print "updating metadata for", spec['id'], mdNew + item.modify_metadata(mdNew) + except requests.exceptions.HTTPError as e: + print e + print e.response + raise + + if db: + setstatus(db, spec['id'], 'unknown', spec['title']) + +def fixprompt(spec, extraprompts={}): + prompts = {'s': skipprompt, 'i': dbinstprompt, 'p': pkginstprompt, 'z': sync_only_metadata, 'd': dosprompt}#, 'w': win31prompt} + prompts.update(extraprompts) + fix(spec) + return prompt(spec, prompts) + +def fixpromptsync(spec, db=None, extraprompts={}): + if fixprompt(spec, extraprompts): + sync(spec, db) + return True + +def reinstall(id, db): + # download the zip + item = internetarchive.get_item(id) + zipfile = item.get_files(formats='ZIP')[0] + zipfilename = os.path.join(DBDIR, zipfile.name) + if os.path.exists(zipfilename): + os.unlink(zipfilename) + print id, db.get(id, {}).get('title') + print "downloading", zipfile.name + zipfile.download(zipfilename) + + # install and reupload + dbinst(zipfilename) + raw_input('Hit enter when finished:') + spec = {'id': id} + @defprompt('exe') + def exeprompt(spec): + spec['upload'] = zipfilename + prompt(spec, prompt_title=False) + del spec['upload'] + sync(spec, db) + prompts = {'w': markprompt(db, 'windows'), + 'i': markprompt(db, 'inappropriate'), + 'b': markprompt(db, 'busted'), + 'e': exeprompt} + if pkginst(spec, zipfilename, prompts): + prompt(spec, prompt_title=False, prompts={'m': maxcpuprompt}) + sync(spec) + + setstatus(db, id, 'unknown') + savedb(db) + +def installers(db): + for id, val in db.iteritems(): + if val.get('status') == 'installer': + reinstall(id, db) + +def batchfix(db): + for id, val in db.iteritems(): + if val['status'] == 'sync': + sync({'id': id, 'emulator': 'dosbox-sync'}) + setstatus(db, id, 'unknown') + db[id]['reshoot'] = True + savedb(db) + +def updatestatus(db, statusfrom, statusto): + for id, val in db.iteritems(): + if val['status'] == statusfrom: + val['status'] = statusto + savedb(db) + +def clearreshoot(db): + for id, val in db.iteritems(): + if val.get('reshoot'): + del val['reshoot'] + savedb(db) + +def iterfiles(ext, dir='.'): + return [fn for fn in os.listdir(dir) if re.search(r'\.' + ext + r'$', fn, re.IGNORECASE)] + +def iterzipfiles(dir='.'): + return iterfiles('zip') + +def zips(db, prefix, ext='zip'): + prompts = { + 'u': mvprompt('uploaded'), + 'm': mvprompt('multi'), + 'n': mvprompt('notapprop'), + 'd': mvprompt('dup'), + 'z': sync_only_metadata + } + for zipfile in iterfiles(ext): + spec = {'upload': zipfile} + gen_metadata(spec, zipfile) + filename = os.path.basename(zipfile) + description = None + # description = patchstr(sh.sed(sh.grep('-i', '^' + filename, 'FILES1.BBS'), '-e', 's/^' + filename + r'[^ ]* *//')) + print description + if fixprompt(spec, extraprompts=prompts): + spec['title'] = prefix + spec['title'] + if not spec.get('description'): + spec['description'] = description + print spec['description'] + sync(spec, db) + mvprompt('uploaded')(spec) + +def exes(db, prefix): + prompts = { + 'u': mvprompt('uploaded'), + 'm': mvprompt('multi'), + 'n': mvprompt('notapprop'), + 'd': mvprompt('dup') + } + for exefile in iterfiles('exe'): + dbinstexe(exefile) + x = raw_input("Hit Enter when done (x to cancel): ") + if x == 'x': + continue + spec = {'upload': exefile} + fix(spec) + if pkginst(spec, prompts=prompts): + if prompt(spec): + sync(spec, db) + mvprompt('uploaded')(spec) + +def serve(db): + app = Flask(__name__) + app.config['DEBUG'] = True + app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0 + ctx = {'db': db} + + def gen_tmpl(page): + def from_tmpl(): + return render_template(page + '.html', **ctx) + from_tmpl.__name__ = "from_tmpl_" + page + app.route('/' + page)(from_tmpl) + + for page in ['check']: + gen_tmpl(page) + + @app.route('/db', methods=['PUT']) + def putdb(): + if request.json: + ctx['db'] = request.json + savedb(ctx['db']) + return "OK cool" + + @app.route('/', defaults={'filename': 'index.html'}) + @app.route('/static/') + def staticfile(filename): + return send_from_directory(os.path.join(DBDIR, 'static'), filename) + + @app.route('/screenshot', methods=['POST']) + def postscreenshot(): + dataUris = request.form.getlist('image') + url = request.form['url'] + + name = re.match('.*/([^/]+)/?$', url).group(1) + dirname = os.path.join(DBDIR, name) + + if len(dataUris) > 1: + dirindex = 1 + while os.path.exists(os.path.join(dirname, 'animation' + str(dirindex))): + dirindex += 1 + dirname = os.path.join(dirname, 'animation' + str(dirindex)) + + if not os.path.exists(dirname): + os.makedirs(dirname) + + print dirname, len(dataUris) + filenames = [] + index = 1 + for dataUri in dataUris: + data = a2b_base64(dataUri[dataUri.index(',')+1:]) + while os.path.exists(os.path.join(dirname, 'screen' + str(index) + '.png')): + index += 1 + filename = os.path.join(dirname, 'screen' + str(index) + '.png') + with open(filename, 'wb') as f: + f.write(data) + filenames.append(filename) + + if len(filenames) > 1: + # generate an animated giiiffff + gifname = dirname + '.gif' + filenames.append(gifname) + sh.gm('convert', '-delay', 5, '-loop', 0, *filenames) + sh.gifsicle('--batch', '-O3', gifname) + + return "yup good" + + import ssl + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + context.load_cert_chain(os.path.join(DBDIR, 'server.crt'), os.path.join(DBDIR, 'server.key')) + app.run(ssl_context=context) + +def join_elements(elements): + text = '' + for element in elements: + textAdd = '' + if element.name == 'p': + if text: + textAdd = '\n\n' + textAdd += join_elements(element.contents) + elif element.name == 'br': + textAdd = '' + elif unicode(element).strip(): + textAdd = unicode(element) + if textAdd: + text += textAdd + + return text + +def scrapefile(project, url, spec): + fn = os.path.join(DBDIR, project, url.split('/')[-1]) + if not os.path.exists(fn): + dirn = os.path.split(fn)[0] + if not os.path.exists(dirn): + os.makedirs(dirn) + urlretrieve(url, fn) + if not spec: + spec = {} + spec['upload'] = fn + return spec + +def scrapegame(url, spec=None): + soup = BeautifulSoup(requests.get(url).text) + content = soup.find('div', 'node').find('div', 'content') + descElems = [e for e in content.contents if e.name != 'div' and not (e.name == 'table' and e['id'] == 'attachments')] + desc = join_elements(descElems) + desc += '\n\n(Retrieved from Glorious Trainwrecks.)' + title = soup.find('div', id='center').h2.contents[0] + if content.find('div', 'field-field-gamefile'): + dlurl = content.find('div', 'field-field-gamefile').a['href'] + else: + for attachment_link in content.find('table', id="attachments").find_all("a"): + dlurl = attachment_link['href'] + if dlurl.lower().endswith('.zip'): + break + user = soup.find('span', 'submitted').a.contents[0] + tags = 'glorious trainwrecks;Klik & Play' + legacy_event = content.find('div', 'field-field-field-event') + if legacy_event and legacy_event.find('div', 'field-item').contents[0] == 'Pirate Kart 2': + tags += ";The 529 in 1 Klik and Play Pirate Kart Part II: Klik Harder" + event = content.find('div', 'field-field-event-created-for') + if event and not event.a.contents[0].startswith("THE 371-"): + tags += ';' + event.a.contents[0] + if soup.find('div', 'terms'): + for taglink in soup.find('div', 'terms').find_all('a'): + tags += ';' + taglink.contents[0] + spec = scrapefile('gtrwx', dlurl, spec) + spec.update({ + 'title': title, + 'creator': user, + 'description': desc, + 'subject': tags + }) + return spec + +def scrapecomment(url, spec=None): + comment_id = url.split("#")[1] + soup = BeautifulSoup(requests.get(url).text) + comment = soup.find('a', id=comment_id).find_next_sibling('div', 'comment') + user = comment.find('span', 'submitted').a.contents[0] + title = comment.h3.a.contents[0] + event_title = soup.find('div', id='center').h2.contents[0] + content = comment.find('div', 'content') + desc = join_elements([e for e in content.contents if not (e.name == 'table' and e['id'] == 'attachments')]) + desc += '\n\n(Retrieved from Glorious Trainwrecks.)' + attachments = [a['href'] for a in content.find_all('a') if a['href'].split('.')[-1].lower() not in ['png', 'jpg']] + if len(attachments) == 0: + return None + if len(attachments) > 1: + iattach = 1 + for attachment in attachments: + print iattach, attachment + iattach += 1 + iattach = raw_input('Which #: ') + try: + iattach = int(iattach) + attachment = attachments[iattach - 1] + except: + return None + else: + attachment = attachments[0] + + spec = scrapefile('gtrwx', urljoin(url, attachment), spec) + spec.update({ + 'title': title, + 'creator': user, + 'description': desc, + 'subject': 'glorious trainwrecks;Klik & Play;' + event_title + }) + return spec + +def scrapeuser(username): + games = [] + url = 'http://www.glorioustrainwrecks.com/games/*/' + username + while url: + soup = BeautifulSoup(requests.get(url).text) + for td in soup.find_all('td', 'view-field-node-title'): + games.append((urljoin(url, td.a['href']), td.a.contents[0])) + nextLink = soup.find(title='Go to next page') + if nextLink: + url = urljoin(url, nextLink['href']) + else: + url = None + return games + +def runcmd(db, cmd, args): + if cmd == 'scan': + scan(db) + savedb(db) + + elif cmd == 'check': + check(db) + + elif cmd == 'diff': + diffdb = diff(db, args[0]) + runcmd(diffdb, args[1], args[2:]) + savedb(db) + + elif cmd == 'mergedb': + with open(args[0], 'rt') as f: + dbnew = json.load(f) + db.update(dbnew) + savedb(db) + + elif cmd == 'dumpdb': + print json.dumps(db) + + elif cmd == 'db': + global DB + db_old = DB + DB = os.path.abspath(args[0]) + runcmd(loaddb(), args[1], args[2:]) + DB = db_old + + elif cmd == 'sync': + sync(json.loads(args[0])) + + elif cmd == 'pkginst': + spec = {} + pkginst(spec) + fixpromptsync(spec, db) + + elif cmd == 'zip': + spec = {'upload': args[0]} + gen_metadata(spec, args[0]) + fixpromptsync(spec, db) + + elif cmd == 'gtgame': + if '#' in args[0]: + spec = scrapecomment(args[0]) + else: + spec = scrapegame(args[0]) + fixklik(spec) + fixpromptsync(spec, db) + + elif cmd == 'gtuser': + games = scrapeuser(args[0]) + for url, title in games: + spec = scrapegame(url) + if spec: + fixklik(spec) + fixpromptsync(spec, db) + + elif cmd == 'zips': + prefix = args[0] + ": " if len(args) > 0 else "" + zips(db, prefix) + + elif cmd == 'exezips': + prefix = args[0] + ": " if len(args) > 0 else "" + zips(db, prefix, 'exe') + + elif cmd == 'exes': + prefix = args[0] + ": " if len(args) > 0 else "" + exes(db, prefix) + + elif cmd == 'dir': + d = args[0] + if d.endswith('/'): + d = d[:-1] + zipf = os.path.join(DBDIR, safefilename(os.path.basename(d)) + '.zip') + zipdir(d, zipf) + spec = {'upload': zipf} + gen_metadata(spec, d) + if len(args) > 1: + spec['id'] = args[1] + fixpromptsync(spec, db) + + elif cmd == 'dirs': + for d in os.listdir('.'): + if os.path.isdir(d): + zipf = os.path.join(DBDIR, safefilename(os.path.basename(d)) + '.zip') + zipdir(d, zipf) + spec = {'upload': zipf} + gen_metadata(spec, d) + fixpromptsync(spec, db) + + elif cmd == 'installers': + try: + installers(db) + except (KeyboardInterrupt, SystemExit): + pass + savedb(db) + + elif cmd == 'batchfix': + batchfix(db) + + elif cmd == 'setstatus': + updatestatus(db, args[0], args[1]) + + elif cmd == 'clearreshoot': + clearreshoot(db) + + elif cmd == 'fixext': + for id, val in db.iteritems(): + if val.get('status') == 'unknown': + item = internetarchive.get_item(id) + zipfile = item.get_files(formats='ZIP')[0] + md = item.metadata.get('metadata', {}) + print "checking", id, zipfile.name, md.get('emulator_ext') + if os.path.splitext(zipfile.name)[1] == '.ZIP' and md.get('emulator_ext') == 'zip': + print "fixing", id + spec = {'id': id, 'emulator_ext': 'ZIP'} + sync(spec) + + elif cmd == 'reinstall': + reinstall(args[0], db) + savedb(db) + + elif cmd == 'serve': + serve(db) + +if __name__ == '__main__': + runcmd(loaddb(), sys.argv[1], sys.argv[2:]) + diff --git a/testimg.jpg b/testimg.jpg new file mode 100644 index 0000000..2cede93 Binary files /dev/null and b/testimg.jpg differ diff --git a/testimg2.jpg b/testimg2.jpg new file mode 100644 index 0000000..e4826c3 Binary files /dev/null and b/testimg2.jpg differ diff --git a/testimg3.jpg b/testimg3.jpg new file mode 100644 index 0000000..81ed440 Binary files /dev/null and b/testimg3.jpg differ