fix(savetv): Batch-Umbenennung für Raw-Backlog und NAS-Skript

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.
This commit is contained in:
Homelab Cursor 2026-05-21 15:34:38 +02:00
parent 6b034c53e2
commit cacf40526c
2 changed files with 81 additions and 1 deletions

View file

@ -661,7 +661,7 @@ setInterval(load,30000);
return return
raw_title_part = m.group(1) 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() cache = _load_filminfo_cache()
matched = _find_cache_match(cache, clean_title) matched = _find_cache_match(cache, clean_title)
@ -700,6 +700,26 @@ setInterval(load,30000);
import logging import logging
logging.getLogger("savetv").warning(f"Rename fehlgeschlagen {raw_filename}: {e}") 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") @app.route("/health")
def health(): def health():
from tools import savetv from tools import savetv

View file

@ -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())