diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc index 2e0d351..201b183 100644 Binary files a/__pycache__/app.cpython-310.pyc and b/__pycache__/app.cpython-310.pyc differ diff --git a/app.py b/app.py index 7128d45..468d8e1 100644 --- a/app.py +++ b/app.py @@ -25,7 +25,7 @@ from pydantic import BaseModel APP_DIR = Path(os.environ.get("APP_DIR", "/data/app")) MOVIES_DIR = APP_DIR / "movies" -CATALOG = APP_DIR / "catalog.json" +CATALOG = MOVIES_DIR / "catalog.json" WEBHOOK = os.environ.get("DEPLOY_WEBHOOK", "") WEBHOOK_TOKEN = os.environ.get("COOLIFY_TOKEN", "") @@ -48,10 +48,6 @@ def load_catalog(): return {"movies": []} return json.loads(CATALOG.read_text(encoding="utf-8")) -def save_catalog(cat): - cat["updated"] = datetime.date.today().isoformat() - CATALOG.write_text(json.dumps(cat, ensure_ascii=False, indent=2), encoding="utf-8") - def deploy(): if not WEBHOOK: return {"deployed": False, "reason": "no webhook configured"} @@ -100,50 +96,18 @@ def torrent_hash(path: Path): return hashlib.sha1(enc(info)).hexdigest() def rebuild_catalog(): - """Rebuild catalog.json from /movies folders (port of build_catalog.sh).""" - movies = [] - if MOVIES_DIR.exists(): - for entry in sorted(os.listdir(MOVIES_DIR)): - d = MOVIES_DIR / entry - if not d.is_dir() or entry.startswith("."): - continue - meta = {"id": entry} - mf = d / "meta.txt" - if mf.exists(): - for line in mf.read_text(encoding="utf-8", errors="replace").splitlines(): - line = line.strip() - if not line or ":" not in line: - continue - k, v = line.split(":", 1) - k, v = k.strip(), v.strip() - if k in ("title", "original_title", "description"): - meta[k] = v - elif k == "year": - meta["year"] = int(v) if v.isdigit() else v - elif k == "genre": - meta["genre"] = [g.strip() for g in v.split(",") if g.strip()] - elif k == "rating": - try: meta["rating"] = float(v) - except: pass - # poster - for ext in ("jpg", "png", "jpeg", "webp"): - p = d / ("poster." + ext) - if p.exists(): - meta["poster"] = "movies/%s/poster.%s" % (entry, ext) - break - # torrent - tors = list(d.glob("*.torrent")) - if tors: - try: - meta["hash"] = torrent_hash(tors[0]) - meta["torrent"] = "movies/%s/%s" % (entry, tors[0].name) - except Exception as e: - meta["torrent_error"] = str(e) - movies.append(meta) + """Rebuild catalog.json by running the project's build_catalog.sh + (keeps a single source of truth for catalog generation).""" + script = MOVIES_DIR.parent / "build_catalog.sh" + if not script.exists(): + raise HTTPException(500, "build_catalog.sh not found at %s" % script) + try: + subprocess.run(["bash", str(script)], check=True, + capture_output=True, text=True, timeout=120) + except subprocess.CalledProcessError as e: + raise HTTPException(500, "build failed: " + (e.stderr or e.stdout or str(e))) cat = load_catalog() - cat["movies"] = movies - save_catalog(cat) - return len(movies) + return len(cat.get("movies", [])) # ---------- endpoints ---------- @@ -247,16 +211,27 @@ class PatchBody(BaseModel): @app.patch("/movie/{fid}") def patch_movie(fid: str, body: PatchBody): fid = valid_id(fid) - cat = load_catalog() - m = next((x for x in cat.get("movies", []) if x.get("id") == fid), None) - if not m: + d = MOVIES_DIR / fid + if not d.exists(): raise HTTPException(404, "no such film") + mf = d / "meta.txt" + # update meta.txt fields (source of truth for build_catalog.sh) + fields = {} + if mf.exists(): + for line in mf.read_text(encoding="utf-8", errors="replace").splitlines(): + line = line.strip() + if not line or ":" not in line: + continue + k, v = line.split(":", 1) + fields[k.strip()] = v.strip() for k in ("title", "year", "description", "poster", "original_title"): v = getattr(body, k) if v is not None: - m[k] = v - save_catalog(cat) - return {"updated": fid, "deploy": deploy()} + fields[k] = str(v) + mf.write_text("\n".join("%s: %s" % (k, v) for k, v in fields.items()) + "\n", + encoding="utf-8") + n = rebuild_catalog() + return {"updated": fid, "catalog_movies": n, "deploy": deploy()} @app.post("/movie/{fid}/meta") def post_meta(fid: str, meta: UploadFile = File(None)): @@ -283,13 +258,8 @@ def post_poster(fid: str, poster: UploadFile = File(...)): if ext not in ("jpg", "jpeg", "png", "webp"): raise HTTPException(400, "bad poster ext") (d / ("poster." + ext)).write_bytes(poster.file.read()) - # update catalog poster ref - cat = load_catalog() - m = next((x for x in cat.get("movies", []) if x.get("id") == fid), None) - if m: - m["poster"] = "movies/%s/poster.%s" % (fid, ext) - save_catalog(cat) - return {"poster_updated": fid, "deploy": deploy()} + n = rebuild_catalog() + return {"poster_updated": fid, "catalog_movies": n, "deploy": deploy()} @app.post("/movie/{fid}/torrent") def post_torrent(fid: str, torrent: UploadFile = File(...)): @@ -317,16 +287,8 @@ def rename_movie(fid: str, body: RenameBody): if (MOVIES_DIR / new).exists(): raise HTTPException(409, "target %s exists" % new) d.rename(MOVIES_DIR / new) - cat = load_catalog() - for m in cat.get("movies", []): - if m.get("id") == fid: - m["id"] = new - if "poster" in m: - m["poster"] = m["poster"].replace("movies/%s/" % fid, "movies/%s/" % new) - if "torrent" in m: - m["torrent"] = m["torrent"].replace("movies/%s/" % fid, "movies/%s/" % new) - save_catalog(cat) - return {"renamed": fid, "to": new, "deploy": deploy()} + n = rebuild_catalog() + return {"renamed": fid, "to": new, "catalog_movies": n, "deploy": deploy()} @app.get("/check") def check():