"""Command line: run a scan, serve in development, or export keeps. python -m mediashelf.cli scan --full python -m mediashelf.cli serve --port 8080 python -m mediashelf.cli export-keeps > keeps.json """ from __future__ import annotations import argparse import json import logging import sys from . import ingest, keeps, providers from .app import create_app from .config import Config from .db import Database def _load_dotenv(path=".env"): """Minimal .env support for development. Production uses the stack env.""" import os try: with open(path) as fh: for line in fh: line = line.strip() if not line or line.startswith("#") or "=" not in line: continue k, v = line.split("=", 1) os.environ.setdefault(k.strip(), v.strip()) except OSError: pass def cmd_scan(args) -> int: cfg = Config.from_env() db = Database(cfg.database_path) db.migrate() media = providers.build_media_provider(cfg) history, degraded = providers.build_history_provider(cfg, media) if degraded: print("WARNING: history degraded — %s" % degraded, file=sys.stderr) print("history source: %s" % (history.name if history else "none"), file=sys.stderr) result = ingest.Ingest(db, cfg, media, history).run( "full" if args.full else "incremental", "manual") print(json.dumps({ "status": result.status, "scan_id": result.scan_id, "items_seen": result.items_seen, "items_added": result.items_added, "items_updated": result.items_updated, "items_missing": result.items_missing, "events_added": result.events_added, "warnings": len(result.warnings or []), "error": result.error, }, indent=2)) return 0 if result.status == "succeeded" else 1 def cmd_serve(args) -> int: app = create_app(start_scheduler=not args.no_scheduler) app.run(host=args.host, port=args.port, debug=args.debug, threaded=True) return 0 def cmd_export_keeps(args) -> int: cfg = Config.from_env() db = Database(cfg.database_path) db.migrate() json.dump(keeps.export(db), sys.stdout, indent=2) return 0 def cmd_import_keeps(args) -> int: cfg = Config.from_env() db = Database(cfg.database_path) db.migrate() with open(args.path) as fh: payload = json.load(fh) print(json.dumps(keeps.import_(db, payload), indent=2)) return 0 def main(argv=None) -> int: _load_dotenv() logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-7s %(name)s: %(message)s") p = argparse.ArgumentParser(prog="mediashelf") sub = p.add_subparsers(dest="cmd", required=True) s = sub.add_parser("scan", help="run a scan now") s.add_argument("--full", action="store_true", help="full sweep rather than incremental") s.set_defaults(func=cmd_scan) s = sub.add_parser("serve", help="development server") s.add_argument("--host", default="127.0.0.1") s.add_argument("--port", type=int, default=8080) s.add_argument("--debug", action="store_true") s.add_argument("--no-scheduler", action="store_true") s.set_defaults(func=cmd_serve) s = sub.add_parser("export-keeps", help="write keep marks to stdout as JSON") s.set_defaults(func=cmd_export_keeps) s = sub.add_parser("import-keeps", help="restore keep marks from a JSON export") s.add_argument("path") s.set_defaults(func=cmd_import_keeps) args = p.parse_args(argv) return args.func(args) if __name__ == "__main__": sys.exit(main())