diff --git a/README.md b/README.md index 396488d..dd2bd10 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,8 @@ as private or upload images. You may want to run this before starting the server That's it - you've set up Photolib! Next steps from here would be to the other command line tools to import media. Besides browsing, most interaction with Photolib will be done using its CLI tool, `photocli`. Note that server address, -username, and password details need to be provided via flags not shown here to in a config file. See `photocli --help` -for more information. +username, and password details need to be provided to the cli via a url flag not shown here. See `photocli --help` for +more information. Commands diff --git a/photoapp/cli.py b/photoapp/cli.py index 9067cd9..2938722 100644 --- a/photoapp/cli.py +++ b/photoapp/cli.py @@ -7,8 +7,44 @@ from photoapp.utils import get_extension from photoapp.types import known_extensions, PhotoStatus from photoapp.common import pwhash from photoapp.ingest import get_photosets +from urllib.parse import urlparse from tabulate import tabulate from concurrent.futures import ThreadPoolExecutor, as_completed +import appdirs + + +APPNAME = "photocli" +DEFAULT_CONFIG = {"uri": None, + "lastbatch": None} + + +class CliConfig(dict): + def __init__(self, appname, default): + self.confdir = appdirs.user_config_dir(appname) + self.confpath = os.path.join(self.confdir, "cli.json") + super().__init__(**default) + self.init_config() + + def init_config(self): + os.makedirs(self.confdir, exist_ok=True) + if os.path.exists(self.confpath): + self.load() + else: + self.write() + + def load(self): + with open(self.confpath) as f: + self.update(**json.load(f)) + + def write(self): + with open(self.confpath, "w") as f: + json.dump(self, f, indent=4) + + def __setitem__(self, key, value): + if key not in self: + raise KeyError(key) + super().__setitem__(key, value) + self.write() class PhotoApiClient(object): @@ -86,9 +122,7 @@ def confirm(message, options=None, accept=None, confirm_msg="Are you sure?"): def get_args(): parser = argparse.ArgumentParser(description="photo library cli") # TODO nicer uri parser - parser.add_argument("--host", required=True, help="photo library server address") - parser.add_argument("--user", required=True) - parser.add_argument("--password", required=True) + parser.add_argument("--host", default=os.environ.get("PHOTOLIB_URI", None), help="photo api server address") parser.add_argument("-y", "--yes", action="store_true", help="assume yes for all prompts") sp_action = parser.add_subparsers(dest="action", help="action to take") @@ -122,13 +156,22 @@ def get_args(): p_delete = p_useraction.add_parser('delete', help='delete users') p_delete.add_argument("-u", "--username", help="username", required=True) - return parser.parse_args() + return parser.parse_args(), parser def main(): - args = get_args() + config = CliConfig(APPNAME, DEFAULT_CONFIG) + args, parser = get_args() - client = PhotoApiClient(args.host, (args.user, args.password, )) + uri = urlparse(args.host or config["uri"]) + + if not uri.netloc: + parser.error("need --host, $PHOTOLIB_URI, or config file") + + config["uri"] = uri.geturl() + + port = f":{uri.port}" if uri.port else "" + client = PhotoApiClient(f"{uri.scheme}://{uri.hostname}{port}", (uri.username, uri.password, )) if args.action == "checkdupes": hashes = {} diff --git a/requirements.txt b/requirements.txt index 6dd9144..c13290d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +appdirs==1.4.3 backports.functools-lru-cache==1.5 boto3==1.9.183 botocore==1.12.183