Restefeger: Auth komplett raus, Serien-Flow + Episoden-Matching, Jellyfin-Refresh, Duplikat-Warnung, echtes Nur-Hauptfilm
Ampel / ampel (push) Successful in 55s
Ampel / ampel (push) Successful in 55s
AUTH ENTFERNT (Commander-Entscheid 24.07., KONZEPT §10): /token- und /api-keys-Endpoints, auth.py, test_auth.py, passlib/bcrypt/PyJWT/ python-multipart, JWT_SECRET_KEY-Pflicht. Heimnetz-only, das UI hatte nie einen Login — die Auth-Oberflaeche war Placebo und die passlib/bcrypt- Falle brach die Ampel. Rate-Limit pro IP bleibt. Schnellstart laeuft jetzt ganz ohne .env-Pflichtwerte. Serien-Flow (Etappe-12-Kern, ARM-Wunde #395): - Rip-Dialog: Serienname + Staffel -> Ablage <Serie>/Season NN (jellyfin.org/docs Naming-Schema); tvshow.nfo + poster.jpg im Serien-Ordner, bei Staffel 2 nicht ueberschrieben. - Episoden-Matching per Laufzeitabgleich: HandBrakeCLI --scan ('+ duration:', handbrake.fr/docs) je MKV gegen TMDB-Staffel-Laufzeiten (GET /metadata/tv/{id}/season/{n}; tv-season-details-API). Ordnungserhaltend; komplette Staffel auf einer Disc klappt auch bei uniformen Anime-Laufzeiten (Sequenz-Stufe). Umbenannt wird NUR bei eindeutiger Zuordnung — sonst ehrliches Log. Mit Tests. Weitere Punkte: - Jellyfin/Emby-Bibliotheks-Refresh nach jedem fertigen Rip (POST /Library/Refresh, X-Emby-Token lt. jellyfin.org/docs) — URL/Key + Test-Knopf in Einstellungen -> Ripping. - Duplikat-Warnung: Disc-Fingerabdruck (jetzt Teil des Prescan-Ergebnisses + der Job-Metadaten) gegen die Historie; Karte zeigt 'bereits gerippt', Vollautomatik ueberspringt Duplikate. - 'Nur Hauptfilm' ECHT: makemkvcon info -> TINFO-Attr-9-Laufzeiten (usage.txt) -> laengster Titel -> mkv dev:X <nr>. Vorher wirkungsloses Setting; pro Rip im Dialog uebersteuerbar. Mit Tests. - OMDb-Treffer eingedeutscht via TMDB /find (external_source=imdb_id, de-DE; find-by-id-API). - Dashboard: Speicherplatz-Anzeige (amber < 60 GB) + CSV-Export (GET /jobs/export, Semikolon+BOM fuer deutsches Excel). - Metadaten-Seite entfernt (Abnahme durch Commander-Auftrag) inkl. Placebo-Endpoints /metadata/lookup (scannte Dummy-Device) und /metadata/confirm (schrieb nie gelesenen Cache-Key). - Doppel-Jahr-Fix: 'X (2009) (2009)' in Log und Ordnernamen. - Remote-Worker-Blocker: redis (6379) + postgres (5432) waren NIE veroeffentlicht — kein Remote-Worker konnte sich je verbinden. Ports jetzt offen (Heimnetz-Kompromiss, kommentiert) + API_URL fuer Worker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""Tests für die Media-Server-Aufbereitung (Benennung + NFO)."""
|
||||
"""Tests für die Media-Server-Aufbereitung (Benennung, NFO, Serien-Matching)."""
|
||||
|
||||
import os
|
||||
|
||||
from medien import baue_nfo, sicherer_name, zielordner
|
||||
from medien import baue_nfo, matche_episoden, serien_ordner, sicherer_name, zielordner
|
||||
|
||||
|
||||
def test_sicherer_name_entfernt_verbotene_zeichen():
|
||||
@@ -55,3 +55,50 @@ def test_baue_nfo_film_mit_plot_und_genres():
|
||||
def test_baue_nfo_serie_bekommt_tvshow_wurzel():
|
||||
nfo = baue_nfo({"type": "tv"}, "Neon Genesis Evangelion", 1995)
|
||||
assert "<tvshow>" in nfo and "</tvshow>" in nfo
|
||||
|
||||
|
||||
def test_sicherer_name_haengt_jahr_nicht_doppelt_an():
|
||||
"""OMDb-Titel tragen das Jahr teils schon — kein 'X (2009) (2009)' mehr."""
|
||||
assert sicherer_name("Summer Wars (2009)", 2009) == "Summer Wars (2009)"
|
||||
assert sicherer_name("Summer Wars", 2009) == "Summer Wars (2009)"
|
||||
|
||||
|
||||
def test_serien_ordner_jellyfin_schema():
|
||||
assert serien_ordner("/app/media/series", "Neon Genesis: Evangelion", 2) == os.path.join(
|
||||
"/app/media/series", "Neon Genesis Evangelion", "Season 02"
|
||||
)
|
||||
|
||||
|
||||
def test_matche_episoden_komplette_staffel_auch_bei_uniformen_laufzeiten():
|
||||
# Anime-Standardfall: ganze Staffel auf der Disc, alle Episoden ~24 min
|
||||
dateien = [1440, 1452, 1431]
|
||||
episoden = [(1, 1440), (2, 1440), (3, 1440)]
|
||||
assert matche_episoden(dateien, episoden) == [1, 2, 3]
|
||||
|
||||
|
||||
def test_matche_episoden_teil_disc_mit_distinkten_laufzeiten():
|
||||
# Disc 2 einer Staffel: Laufzeiten unterscheiden sich klar → zuordenbar
|
||||
dateien = [1440, 2650]
|
||||
episoden = [(1, 700), (2, 1440), (3, 2650)]
|
||||
assert matche_episoden(dateien, episoden) == [2, 3]
|
||||
|
||||
|
||||
def test_matche_episoden_haelt_reihenfolge():
|
||||
# Datei 2 ist ein Doppellänger → muss auf die SPÄTERE lange Episode fallen
|
||||
dateien = [1440, 2880]
|
||||
episoden = [(1, 1440), (2, 2880)]
|
||||
assert matche_episoden(dateien, episoden) == [1, 2]
|
||||
|
||||
|
||||
def test_matche_episoden_mehrdeutig_gibt_none():
|
||||
# Alle Episoden gleich lang und mehr Kandidaten als Dateien in Toleranz →
|
||||
# Datei 1 passt auf Ep 1 UND Ep 2 gleich gut → keine eindeutige Wahl
|
||||
dateien = [1440]
|
||||
episoden = [(1, 1440), (2, 1440)]
|
||||
assert matche_episoden(dateien, episoden) is None
|
||||
|
||||
|
||||
def test_matche_episoden_ohne_treffer_gibt_none():
|
||||
dateien = [600] # 10 min passt auf keine 24-min-Episode
|
||||
episoden = [(1, 1440), (2, 1440)]
|
||||
assert matche_episoden(dateien, episoden) is None
|
||||
|
||||
Reference in New Issue
Block a user