Scanning + computer vision experiments
This commit is contained in:
parent
a595e437b6
commit
6796588213
176
cvtest.py
Normal file
176
cvtest.py
Normal file
|
@ -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")
|
160
poetry.lock
generated
160
poetry.lock
generated
|
@ -31,6 +31,14 @@ optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "19.1.0"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "The uncompromising code formatter."
|
description = "The uncompromising code formatter."
|
||||||
|
@ -74,6 +82,17 @@ optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "0.4.1"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Better living through Python with decorators"
|
description = "Better living through Python with decorators"
|
||||||
|
@ -124,6 +143,14 @@ version = "0.23"
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
zipp = ">=0.5"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "IPython Kernel for Jupyter"
|
description = "IPython Kernel for Jupyter"
|
||||||
|
@ -143,20 +170,20 @@ category = "dev"
|
||||||
description = "IPython: Productive Interactive Computing"
|
description = "IPython: Productive Interactive Computing"
|
||||||
name = "ipython"
|
name = "ipython"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.3"
|
python-versions = ">=3.5"
|
||||||
version = "6.2.1"
|
version = "7.8.0"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
appnope = "*"
|
appnope = "*"
|
||||||
|
backcall = "*"
|
||||||
colorama = "*"
|
colorama = "*"
|
||||||
decorator = "*"
|
decorator = "*"
|
||||||
jedi = ">=0.10"
|
jedi = ">=0.10"
|
||||||
pexpect = "*"
|
pexpect = "*"
|
||||||
pickleshare = "*"
|
pickleshare = "*"
|
||||||
prompt-toolkit = ">=1.0.4,<2.0.0"
|
prompt-toolkit = ">=2.0.0,<2.1.0"
|
||||||
pygments = "*"
|
pygments = "*"
|
||||||
setuptools = ">=18.5"
|
setuptools = ">=18.5"
|
||||||
simplegeneric = ">0.8"
|
|
||||||
traitlets = ">=4.2"
|
traitlets = ">=4.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -258,14 +285,14 @@ category = "dev"
|
||||||
description = "Jupyter terminal console"
|
description = "Jupyter terminal console"
|
||||||
name = "jupyter-console"
|
name = "jupyter-console"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = ">=3.5"
|
||||||
version = "5.2.0"
|
version = "6.0.0"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
ipykernel = "*"
|
ipykernel = "*"
|
||||||
ipython = "*"
|
ipython = "*"
|
||||||
jupyter-client = "*"
|
jupyter-client = "*"
|
||||||
prompt-toolkit = ">=1.0.0,<2.0.0"
|
prompt-toolkit = ">=2.0.0,<2.1.0"
|
||||||
pygments = "*"
|
pygments = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -279,6 +306,17 @@ version = "4.5.0"
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
traitlets = "*"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Safely add untrusted strings to HTML/XML markup."
|
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.*"
|
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
|
||||||
version = "1.1.1"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "McCabe checker, plugin for flake8"
|
description = "McCabe checker, plugin for flake8"
|
||||||
|
@ -369,6 +422,25 @@ terminado = ">=0.8.1"
|
||||||
tornado = ">=5.0"
|
tornado = ">=5.0"
|
||||||
traitlets = ">=4.2.1"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Utilities for writing pandoc filters in python"
|
description = "Utilities for writing pandoc filters in python"
|
||||||
|
@ -435,12 +507,12 @@ python-versions = "*"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "dev"
|
||||||
description = "Library for building powerful interactive command lines in Python"
|
description = "Library for building powerful interactive command lines in Python"
|
||||||
name = "prompt-toolkit"
|
name = "prompt-toolkit"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "1.0.14"
|
version = "2.0.9"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
six = ">=1.9.0"
|
six = ">=1.9.0"
|
||||||
|
@ -480,7 +552,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "dev"
|
||||||
description = "Pygments is a syntax highlighting package written in Python."
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -489,28 +561,23 @@ version = "2.4.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "A Python module for collection of common interactive command line user interfaces, based on Inquirer.js"
|
description = "Python library to access and use image scanners (Linux/Windows/etc)"
|
||||||
name = "pyinquirer"
|
name = "pyinsane2"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "1.0.3"
|
version = "2.0.13"
|
||||||
|
|
||||||
[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"
|
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
Pillow = "*"
|
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]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Persistent/Functional/Immutable data structures"
|
description = "Persistent/Functional/Immutable data structures"
|
||||||
|
@ -541,7 +608,7 @@ setuptools = "*"
|
||||||
six = ">=1.10.0"
|
six = ">=1.10.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "main"
|
||||||
description = "Extensions to the standard Python datetime module"
|
description = "Extensions to the standard Python datetime module"
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -593,14 +660,6 @@ jupyter-core = "*"
|
||||||
pygments = "*"
|
pygments = "*"
|
||||||
traitlets = "*"
|
traitlets = "*"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
category = "main"
|
|
||||||
description = "Alternative regular expression module, to replace re."
|
|
||||||
name = "regex"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
version = "2019.08.19"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Send file to trash natively under Mac OS X, Windows and Linux."
|
description = "Send file to trash natively under Mac OS X, Windows and Linux."
|
||||||
|
@ -609,14 +668,6 @@ optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "1.5.0"
|
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]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Python 2 and 3 compatibility utilities"
|
description = "Python 2 and 3 compatibility utilities"
|
||||||
|
@ -676,7 +727,7 @@ ipython-genutils = "*"
|
||||||
six = "*"
|
six = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "dev"
|
||||||
description = "Measures number of Terminal column cells of wide-character codes"
|
description = "Measures number of Terminal column cells of wide-character codes"
|
||||||
name = "wcwidth"
|
name = "wcwidth"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -715,7 +766,7 @@ version = "0.6.0"
|
||||||
more-itertools = "*"
|
more-itertools = "*"
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "a0b14789772e6aef430877c07f16231d14758c6a230276d81cb6466f3cd97c02"
|
content-hash = "7e200ef69278ec9f82a2bb37e54d6966e2190cd269bb1d714987dc4db9ab94f5"
|
||||||
python-versions = "^3.7"
|
python-versions = "^3.7"
|
||||||
|
|
||||||
[metadata.hashes]
|
[metadata.hashes]
|
||||||
|
@ -723,17 +774,20 @@ appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "
|
||||||
appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"]
|
appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"]
|
||||||
atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"]
|
atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"]
|
||||||
attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"]
|
attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"]
|
||||||
|
backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"]
|
||||||
black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"]
|
black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"]
|
||||||
bleach = ["213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"]
|
bleach = ["213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"]
|
||||||
click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"]
|
click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"]
|
||||||
colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"]
|
colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"]
|
||||||
|
cycler = ["1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d", "cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"]
|
||||||
decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"]
|
decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"]
|
||||||
defusedxml = ["6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", "f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"]
|
defusedxml = ["6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", "f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"]
|
||||||
entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"]
|
entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"]
|
||||||
flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"]
|
flake8 = ["19241c1cbc971b9962473e4438a2ca19749a7dd002dd1a946eaba171b4114548", "8e9dfa3cecb2400b3738a42c54c3043e821682b9c840b0448c0503f781130696"]
|
||||||
importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"]
|
importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"]
|
||||||
|
imutils = ["857af6169d90e4a0a814130b9b107f5d611150ce440107e1c1233521c6fb1e2b"]
|
||||||
ipykernel = ["167c3ef08450f5e060b76c749905acb0e0fbef9365899377a4a1eae728864383", "b503913e0b4cce7ed2de965457dfb2edd633e8234161a60e23f2fe2161345d12"]
|
ipykernel = ["167c3ef08450f5e060b76c749905acb0e0fbef9365899377a4a1eae728864383", "b503913e0b4cce7ed2de965457dfb2edd633e8234161a60e23f2fe2161345d12"]
|
||||||
ipython = ["51c158a6c8b899898d1c91c6b51a34110196815cc905f9be0fa5878e19355608", "fcc6d46f08c3c4de7b15ae1c426e15be1b7932bcda9d83ce1a4304e8c1129df3"]
|
ipython = ["c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", "dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1"]
|
||||||
ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"]
|
ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"]
|
||||||
ipywidgets = ["13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516", "e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"]
|
ipywidgets = ["13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516", "e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"]
|
||||||
jedi = ["786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", "ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"]
|
jedi = ["786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", "ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"]
|
||||||
|
@ -741,15 +795,19 @@ jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "1
|
||||||
jsonschema = ["5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f", "8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d"]
|
jsonschema = ["5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f", "8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d"]
|
||||||
jupyter = ["3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", "5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", "d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"]
|
jupyter = ["3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", "5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", "d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"]
|
||||||
jupyter-client = ["6a6d415c62179728f6d9295b37356d8f6833e9e01c2b6e1901dc555571f57b21", "f406f214f9daa92be110d5b83d62f3451ffc73d3522db7350f0554683533ab18"]
|
jupyter-client = ["6a6d415c62179728f6d9295b37356d8f6833e9e01c2b6e1901dc555571f57b21", "f406f214f9daa92be110d5b83d62f3451ffc73d3522db7350f0554683533ab18"]
|
||||||
jupyter-console = ["3f928b817fc82cda95e431eb4c2b5eb21be5c483c2b43f424761a966bb808094", "545dedd3aaaa355148093c5609f0229aeb121b4852995c2accfa64fe3e0e55cd"]
|
jupyter-console = ["308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5", "cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"]
|
||||||
jupyter-core = ["2c6e7c1e9f2ac45b5c2ceea5730bc9008d92fe59d0725eac57b04c0edfba24f7", "f4fa22d6cf25f34807c995f22d2923693575c70f02557bcbfbe59bd5ec8d8b84"]
|
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"]
|
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"]
|
mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"]
|
||||||
mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"]
|
mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"]
|
||||||
more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"]
|
more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"]
|
||||||
nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"]
|
nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"]
|
||||||
nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"]
|
nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"]
|
||||||
notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"]
|
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"]
|
pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"]
|
||||||
parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"]
|
parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"]
|
||||||
pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"]
|
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"]
|
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"]
|
pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"]
|
||||||
prometheus-client = ["71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"]
|
prometheus-client = ["71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"]
|
||||||
prompt-toolkit = ["7281b5199235adaef6980942840c43753e4ab20dfe41338da634fb41c194f9d8", "82c7f8e07d7a0411ff5367a5a8ff520f0112b9179f3e599ee8ad2ad9b943d911", "cc66413b1b4b17021675d9f2d15d57e640b06ddfd99bb724c73484126d22622f"]
|
prompt-toolkit = ["11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", "2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", "977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"]
|
||||||
ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"]
|
ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"]
|
||||||
py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"]
|
py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"]
|
||||||
pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"]
|
pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"]
|
||||||
pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"]
|
pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", "d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"]
|
||||||
pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"]
|
pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"]
|
||||||
pyinquirer = ["c9a92d68d7727fbd886a7908c08fd9e9773e5dc211bf5cbf836ba90d366dee51"]
|
pyinsane2 = ["0d519531d552e4512776225eb400a6a4a9bfc83a08918ec7fea19cb2fa7ec4ee", "a172fc2947ce9d3ed0b3f3b204b5e2fd57658f672d346c54f7f29d727fe56d0c", "df3ffdb2abd52fb4b5ca1f47df8c39a995ce04bf1f6f09250cb9e77cb0dddfb8"]
|
||||||
pyinsane = ["11df7b8abc0875a00cf2257b736fcf7b7913bd93e0428bf5b7decb9f143df74c"]
|
pyparsing = ["6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", "d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"]
|
||||||
pyrsistent = ["34b47fa169d6006b32e99d4b3c4031f155e6e68ebcc107d6454852e8e0ee6533"]
|
pyrsistent = ["34b47fa169d6006b32e99d4b3c4031f155e6e68ebcc107d6454852e8e0ee6533"]
|
||||||
pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"]
|
pytest = ["3f193df1cfe1d1609d4c583838bea3d532b18d6160fd3f55c9447fdca30848ec", "e246cf173c01169b9617fc07264b7b1316e78d7a650055235d6d897bc80d9660"]
|
||||||
python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"]
|
python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"]
|
||||||
|
@ -772,9 +830,7 @@ pywin32 = ["0443e9bb196e72480f50cbddc2cf98fbb858a77d02e281ba79489ea3287b36e9", "
|
||||||
pywinpty = ["0e01321e53a230233358a6d608a1a8bc86c3882cf82769ba3c62ca387dc9cc51", "333e0bc5fca8ad9e9a1516ebedb2a65da38dc1f399f8b2ea57d6cccec1ff2cc8", "3ca3123aa6340ab31bbf9bd012b92e72f9ec905e4c9ee152cc997403e1778cd3", "44a6dddcf2abf402e22f87e2c9a341f7d0b296afbec3d28184c8de4d7f514ee4", "53d94d574c3d4da2df5b1c3ae728b8d90e4d33502b0388576bbd4ddeb4de0f77", "c3955f162c53dde968f3fc11361658f1d83b683bfe601d4b6f94bb01ea4300bc", "cec9894ecb34de3d7b1ca121dd98433035b9f8949b5095e84b103b349231509c", "dcd45912e2fe2e6f72cee997a4da6ed1ad2056165a277ce5ec7f7ac98dcdf667", "f2bcdd9a2ffd8b223752a971b3d377fb7bfed85f140ec9710f1218d760f2ccb7"]
|
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"]
|
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"]
|
qtconsole = ["40d5d8e00d070ea266dbf6f0da74c4b9597b8b8d67cd8233c3ffd8debf923703", "b91e7412587e6cfe1644696538f73baf5611e837be5406633218443b2827c6d9"]
|
||||||
regex = ["1e9f9bc44ca195baf0040b1938e6801d2f3409661c15fe57f8164c678cfc663f", "587b62d48ca359d2d4f02d486f1f0aa9a20fbaf23a9d4198c4bed72ab2f6c849", "835ccdcdc612821edf132c20aef3eaaecfb884c9454fdc480d5887562594ac61", "93f6c9da57e704e128d90736430c5c59dd733327882b371b0cae8833106c2a21", "a46f27d267665016acb3ec8c6046ec5eae8cf80befe85ba47f43c6f5ec636dcd", "c5c8999b3a341b21ac2c6ec704cfcccbc50f1fedd61b6a8ee915ca7fd4b0a557", "d4d1829cf97632673aa49f378b0a2c3925acd795148c5ace8ef854217abbee89", "d96479257e8e4d1d7800adb26bf9c5ca5bab1648a1eddcac84d107b73dc68327", "f20f4912daf443220436759858f96fefbfc6c6ba9e67835fd6e4e9b73582791a", "f2b37b5b2c2a9d56d9e88efef200ec09c36c7f323f9d58d0b985a90923df386d", "fe765b809a1f7ce642c2edeee351e7ebd84391640031ba4b60af8d91a9045890"]
|
|
||||||
send2trash = ["60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", "f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"]
|
send2trash = ["60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", "f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"]
|
||||||
simplegeneric = ["dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"]
|
|
||||||
six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"]
|
six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"]
|
||||||
terminado = ["d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460", "de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"]
|
terminado = ["d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460", "de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"]
|
||||||
testpath = ["46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109", "b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"]
|
testpath = ["46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109", "b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"]
|
||||||
|
|
|
@ -6,8 +6,10 @@ authors = ["Jeremy Penner <jeremy@sporktania.com>"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.7"
|
python = "^3.7"
|
||||||
PyInquirer = "^1.0"
|
pyinsane2 = "^2.0"
|
||||||
pyinsane = "^1.4"
|
opencv-python = "^4.1"
|
||||||
|
imutils = "^0.5.3"
|
||||||
|
matplotlib = "^3.1"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^3.0"
|
pytest = "^3.0"
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
__version__ = "0.1.0"
|
__version__ = "0.1.0"
|
||||||
|
|
||||||
from cuebin import CueBin
|
# from cuebin import CueBin
|
||||||
|
|
||||||
# from file import restore, backup
|
# from file import restore, backup
|
||||||
import glob
|
# import glob
|
||||||
|
|
||||||
for cuefile in glob.glob("D:\\ROMs\\PC\\*.cue"):
|
# for cuefile in glob.glob("D:\\ROMs\\PC\\*.cue"):
|
||||||
CueBin(cuefile).fixAll()
|
# CueBin(cuefile).fixAll()
|
||||||
# restore(cuefile)
|
# restore(cuefile)
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
from PyInquirer import prompt
|
|
||||||
|
|
||||||
|
|
||||||
def prompt1(q):
|
|
||||||
q = q.copy()
|
|
||||||
q["name"] = "response"
|
|
||||||
return prompt([q])["response"]
|
|
20
romtool/scan.py
Normal file
20
romtool/scan.py
Normal file
|
@ -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]
|
837
sw
Normal file
837
sw
Normal file
|
@ -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 = '<a href="' + meta['_URL'] + '">'
|
||||||
|
if site:
|
||||||
|
link += site
|
||||||
|
else:
|
||||||
|
link += 'this Geocities site'
|
||||||
|
link += '</a>'
|
||||||
|
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/<filename>')
|
||||||
|
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 <a href="' + url + '">Glorious Trainwrecks</a>.)'
|
||||||
|
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 <a href="' + url + '">Glorious Trainwrecks</a>.)'
|
||||||
|
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:])
|
||||||
|
|
BIN
testimg.jpg
Normal file
BIN
testimg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 MiB |
BIN
testimg2.jpg
Normal file
BIN
testimg2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 MiB |
BIN
testimg3.jpg
Normal file
BIN
testimg3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 MiB |
Loading…
Reference in a new issue