diff --git a/photoapp/api.py b/photoapp/api.py index 9c0a87c..680690b 100644 --- a/photoapp/api.py +++ b/photoapp/api.py @@ -35,16 +35,22 @@ class PhotosApiV1(object): @cherrypy.tools.json_out() def byhash(self, sha): f = db().query(Photo).filter(Photo.hash == sha).first() + if not f: + raise cherrypy.HTTPError(404) return f.to_json() @cherrypy.expose @cherrypy.tools.json_out() def set(self, uuid): s = db().query(PhotoSet).filter(PhotoSet.uuid == uuid).first() + if not s: + raise cherrypy.HTTPError(404) return s.to_json() @cherrypy.expose def download(self, uuid): f = db().query(Photo).filter(Photo.uuid == uuid).first() + if not f: + raise cherrypy.HTTPError(404) return cherrypy.lib.static.serve_file(os.path.abspath(os.path.join("./library", f.path)), f.format)#TODO no hardcode path diff --git a/photoapp/cli.py b/photoapp/cli.py new file mode 100644 index 0000000..4bbe532 --- /dev/null +++ b/photoapp/cli.py @@ -0,0 +1,68 @@ +import argparse +import requests +from requests.exceptions import HTTPError + + +class PhotoApiClient(object): + def __init__(self, base_url): + self.session = requests.Session() + self.base_url = base_url + + def byhash(self, sha): + return self.get("byhash", params={"sha": sha}).json() + + def get(self, url, **params): + resp = self.session.get(self.base_url + "/api/v1/" + url, **params) + resp.raise_for_status() + return resp + + +def main(): + parser = argparse.ArgumentParser(description="photo library cli") + parser.add_argument("-s", "--host", required=True, help="photo library server address") + + sp_action = parser.add_subparsers(dest="action", help="action to take") + + p_dupes = sp_action.add_parser("checkdupes", help="check if files/hash lists are already in the library") + p_dupes.add_argument("--sha-files", action="store_true", help="read hashes from a file instead of hashing images") + p_dupes.add_argument("files", nargs="+", help="files to check") + + p_ingest = sp_action.add_parser("ingest", help="import images into the library") + p_ingest.add_argument("files", nargs="+", help="files to import") + p_ingest.add_argument("-c", "--copy-of", help="existing uuid the imported images will be placed under") + + args = parser.parse_args() + print(args) + + client = PhotoApiClient(args.host) + + if args.action == "checkdupes": + hashes = {} + if args.sha_files: + # sha file format should look like the output of `find ./source -type f -exec shasum -a 256 {} \;`: + # e931d286a8377ea8f49ee928e793d9e1fd18a25402cac43afdee3eec3b3b18a5 source/2018-12-26 20.54.40.jpg + # 24c1cd20c961839f14d608f5719c4f380af902c4774ddb7cb9ff747f652ca302 source/2018-12-30 22.39.27.jpg + for fname in args.files: + with open(fname) as f: + for line in f.readlines(): + if not line: + continue + sha, path = line.split(maxsplit=1) + hashes[sha] = path.rstrip("\n") + else: + raise NotImplementedError("must pass --sha-files for now") + + for sha, path in hashes.items(): + # hit http://localhost:8080/api/v1/byhash?sha=afe49172f709725a4503c9219fb4c6a9db8ad0354fc493f2f500269ac6faeaf6 + # if the file is a dupe, do nothing + # if the file is original, print its path + try: + print(client.byhash(sha)) + except HTTPError as he: + print(repr(he.response.status_code)) + + # print(hashes) + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index 2d5eaa9..3bb2c63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,10 @@ backports.functools-lru-cache==1.5 +certifi==2019.6.16 +chardet==3.0.4 cheroot==6.5.2 CherryPy==18.1.1 contextlib2==0.5.5 +idna==2.8 jaraco.functools==1.20 Jinja2==2.10.1 MarkupSafe==1.0 @@ -10,7 +13,9 @@ Pillow==5.2.0 portend==2.3 python-magic==0.4.15 pytz==2018.5 +requests==2.22.0 six==1.11.0 SQLAlchemy==1.3.5 tempora==1.13 +urllib3==1.25.3 zc.lockfile==1.3.0 diff --git a/setup.py b/setup.py index 9afa1df..0616a56 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ setup(name='photoapp', "photoinfo = photoapp.image:main", "photooffset = photoapp.dateoffset:main", "photousers = photoapp.users:main", + "photocli = photoapp.cli:main", ] }, include_package_data=True,