From cacf40526c028dc409da3eb72090d081a0f6af4c Mon Sep 17 00:00:00 2001 From: Homelab Cursor Date: Thu, 21 May 2026 15:34:38 +0200 Subject: [PATCH] =?UTF-8?q?fix(savetv):=20Batch-Umbenennung=20f=C3=BCr=20R?= =?UTF-8?q?aw-Backlog=20und=20NAS-Skript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename lief nur beim Download-Abschluss; POST /api/rename_pending benennt alle Titel_ID.mp4 auf Hetzner um. savetv_rename_nas.py für bereits kopierte NAS-Dateien. __ wird zu Bindestrich im Titel. --- homelab-ai-bot/savetv_extra_routes.py | 22 +++++++++- homelab-ai-bot/savetv_rename_nas.py | 60 +++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100755 homelab-ai-bot/savetv_rename_nas.py diff --git a/homelab-ai-bot/savetv_extra_routes.py b/homelab-ai-bot/savetv_extra_routes.py index fd119f8cc..db07186ac 100644 --- a/homelab-ai-bot/savetv_extra_routes.py +++ b/homelab-ai-bot/savetv_extra_routes.py @@ -661,7 +661,7 @@ setInterval(load,30000); return raw_title_part = m.group(1) - clean_title = raw_title_part.replace('_-_', ' - ').replace('_', ' ').strip() + clean_title = raw_title_part.replace('__', ' - ').replace('_-_', ' - ').replace('_', ' ').strip() cache = _load_filminfo_cache() matched = _find_cache_match(cache, clean_title) @@ -700,6 +700,26 @@ setInterval(load,30000); import logging logging.getLogger("savetv").warning(f"Rename fehlgeschlagen {raw_filename}: {e}") + def _rename_all_pending(): + import re as _re + renamed, skipped = [], [] + for fp in sorted(SAVETV_DIR.glob("*.mp4")): + m = _re.match(r"^(.+)_(\d{6,9})\.mp4$", fp.name) + if not m: + continue + old = fp.name + tid = m.group(2) + _rename_to_jellyfin(old, tid) + if (SAVETV_DIR / old).exists(): + skipped.append(old) + else: + renamed.append(old) + return {"renamed": renamed, "skipped": skipped} + + @app.route("/api/rename_pending", methods=["POST"]) + def api_rename_pending(): + return jsonify(_rename_all_pending()) + @app.route("/health") def health(): from tools import savetv diff --git a/homelab-ai-bot/savetv_rename_nas.py b/homelab-ai-bot/savetv_rename_nas.py new file mode 100755 index 000000000..d6b11dff2 --- /dev/null +++ b/homelab-ai-bot/savetv_rename_nas.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Benennt Raw-MP4s auf dem NAS um (Titel_ID.mp4 -> Titel (Jahr).mp4).""" +import json +import os +import re +import urllib.parse +import urllib.request + +ZIEL = os.environ.get("SAVETV_NAS_DIR", "/mnt/nas/Filme zum nachbearbeiten") +FILMINFO_API = os.environ.get("SAVETV_FILMINFO_API", "http://138.201.84.95:9443/api/filminfo") +RAW_RE = re.compile(r"^(.+)_(\d{6,9})\.mp4$") + + +def clean_title(raw_part): + return raw_part.replace("_-_", " - ").replace("_", " ").strip() + + +def filminfo(title): + url = FILMINFO_API + "?" + urllib.parse.urlencode({"title": title}) + with urllib.request.urlopen(url, timeout=60) as r: + return json.loads(r.read()) + + +def rename_one(name): + m = RAW_RE.match(name) + if not m: + return None + clean = clean_title(m.group(1)) + try: + info = filminfo(clean) + year = (info or {}).get("year", "") + except Exception as e: + print(f" filminfo fehlgeschlagen fuer {clean}: {e}") + year = "" + safe = re.sub(r'[\\/:*?"<>|]', "", clean).strip() + dest = f"{safe} ({year}).mp4" if year else f"{safe}.mp4" + src = os.path.join(ZIEL, name) + dst = os.path.join(ZIEL, dest) + if os.path.exists(dst): + print(f" Ziel existiert, loesche Raw: {name}") + os.remove(src) + return dest + os.rename(src, dst) + print(f" OK: {name} -> {dest}") + return dest + + +def main(): + if not os.path.isdir(ZIEL): + print(f"Ordner fehlt: {ZIEL}") + return 1 + pending = [n for n in os.listdir(ZIEL) if RAW_RE.match(n)] + print(f"{len(pending)} Raw-Dateien auf NAS") + for n in sorted(pending): + rename_one(n) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())