feat: Komplettierung der aktuellen Rippy-Etappe
Ampel / ampel (push) Failing after 29s

- Versionsanzeige für den Windows-Worker im UI inkl. Prüfung
- Absicherung der install.sh gegen fehlendes systemd
- Serien-Episoden-Erkennung und Laufzeitabgleich anhand TMDB-Daten (Heuristik)
- Umstellung der Windows-Worker-Installation auf SchTasks (Dienst-Ersatz)
- Kodi-Bibliotheks-Refresh über JSON-RPC integriert
This commit is contained in:
Hitonabi
2026-07-26 21:13:27 +02:00
parent d15900d1eb
commit e165e2a0d3
9 changed files with 179 additions and 78 deletions
+45 -6
View File
@@ -348,13 +348,45 @@ def rip_titel_auswahl(device_path: str, output_dir: str, titel_liste: list,
return {"status": "success", "output_dir": output_dir, "files": alle_dateien}
def laengster_titel(dauern: dict):
"""Titel-Nummer mit der längsten Laufzeit — der Hauptfilm (pure Funktion)."""
def laengster_titel(dauern: dict, meta: dict = None):
"""Titel-Nummer für den Hauptfilm (pure Funktion).
Mit meta['runtime'] wird heuristisch der Titel gesucht, dessen Laufzeit am nächsten
an der TMDB-Laufzeit liegt (vermeidet 'Play All'-Tracks, die länger als der Film sind).
Ohne runtime wird klassisch der längste Titel gewählt.
"""
if not dauern:
return None
ziel_s = (meta.get("runtime") or 0) * 60 if meta else 0
if ziel_s > 0:
bester = min(dauern, key=lambda t: abs(dauern[t] - ziel_s))
if abs(dauern[bester] - ziel_s) <= 20 * 60:
return bester
return max(dauern, key=dauern.get)
def episoden_titel(dauern: dict, meta: dict) -> list:
"""Findet Episoden-Titel anhand der TMDB-Episoden-Laufzeiten.
Toleranz: +/- 10 Minuten (600 Sekunden) zum Durchschnitt.
Gibt eine sortierte Liste der gefundenen Titelnummern zurück.
"""
if not dauern or not meta or not meta.get("episode_runtimes"):
return []
runtimes_s = [r * 60 for r in meta["episode_runtimes"]]
avg_runtime = sum(runtimes_s) / len(runtimes_s)
gefundene_titel = []
for titel, dauer in dauern.items():
if abs(dauer - avg_runtime) <= 10 * 60:
gefundene_titel.append(int(titel))
return sorted(gefundene_titel)
def lies_titel_dauern(device_path: str, timeout: int = 300) -> dict:
"""Fragt die Titel-Laufzeiten der Disc ab (makemkvcon info, Robot-Mode)."""
try:
@@ -832,15 +864,14 @@ def run_makemkv(device_path: str, output_dir: str, progress_cb=None, titel: str
def rip_video(device_path: str, disc_id: str, disc_type: str = "dvd", progress_cb=None,
output_dir: str = None, nur_hauptfilm: bool = False,
titel_liste: list = None, log_cb=None) -> dict:
titel_liste: list = None, log_cb=None, meta: dict = None) -> dict:
"""Rippt eine DVD oder Blu-ray verlustfrei mit MakeMKV.
Bewusst KEIN eigener Celery-Task: der einzige Task ist worker.tasks.rip_disc,
der hier mit seinem eigenen Fortschritts-Callback durchgreift. output_dir
überschreibt das Ziel (Transcode-Fall: Roh-Rip nach /app/temp).
titel_liste: exakte Titel-Auswahl aus dem UI (schlägt nur_hauptfilm).
nur_hauptfilm: Vorab-Info-Lauf, dann nur den LÄNGSTEN Titel rippen
das löst das eingestellte (vorher wirkungslose) mainFeatureOnly ein.
nur_hauptfilm: Vorab-Info-Lauf, dann heuristisch den besten Titel rippen.
"""
if output_dir is None:
output_dir = os.path.join(RIP_OUTPUT_DIR, disc_type, disc_id)
@@ -849,10 +880,18 @@ def rip_video(device_path: str, disc_id: str, disc_type: str = "dvd", progress_c
os.makedirs(output_dir, exist_ok=True)
return rip_titel_auswahl(device_path, output_dir, titel_liste, progress_cb, log_cb)
# Serien-Erkennung: Wenn keine Titel explizit gewählt wurden und es eine Serie ist
if meta and meta.get("series") and meta.get("episode_runtimes"):
dauern = lies_titel_dauern(device_path)
episoden = episoden_titel(dauern, meta)
if episoden:
os.makedirs(output_dir, exist_ok=True)
return rip_titel_auswahl(device_path, output_dir, episoden, progress_cb, log_cb)
titel = "all"
if nur_hauptfilm:
dauern = lies_titel_dauern(device_path)
haupt = laengster_titel(dauern)
haupt = laengster_titel(dauern, meta)
if haupt is not None:
titel = str(haupt)
# Kein Titel ermittelbar → ehrlich auf 'all' zurückfallen statt raten