6f1add480ea7505228c1941aa25ca1c0073700f0
22
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d3e86d2641 |
fix(windows): Der Schalter, den es nicht gibt, und vierzehn weitere Funde
Commander: „Kompression fehlgeschlagen bei Spartacus … _t01.mkv: HandBrake
endete mit Code 0" — 16,5 GB fertiger Rohschnitt, und die Kompression war in
derselben Sekunde vorbei, in der sie begann.
Nachgestellt mit genau der Befehlszeile, die Rippy baute:
unknown option (--audio-codec)
HandBrake has exited. $? = 0
Den Schalter `--audio-codec` gibt es bei HandBrakeCLI nicht; er heisst
`-E` / `--aencoder`. Ein unbekannter Schalter ist fuer HandBrake kein Fehler,
der Rueckgabewert ist 0. Der Test dazu forderte den falschen Namen sogar ein.
Daraus wurde ein Rundgang durch den Windows-Pfad. Alles unten ist gemessen,
nichts vermutet (Regel D).
## Die Kompression
1. `--aencoder` statt `--audio-codec`. Am mitgelieferten HandBrakeCLI 1.11.2
gemessen, mit einem 5-Sekunden-Encode auf der echten Roh-Datei bestaetigt.
2. HandBrakes letzte Zeilen werden aufgehoben (12 gepuffert, 4 in der
Meldung) und `unknown option (...)` wird als eigener Fall erkannt, VOR
allen anderen. Vorher wurde jede Zeile weggeworfen, die kein Fortschritt
war — bei Rueckgabewert 0 blieb damit keine Auskunft uebrig. Die geratene
Zeile „Meist ist der Zielordner nicht beschreibbar" ist raus; sie war
falsch und hat die Suche in die falsche Richtung geschickt.
3. Ein `ue`-Umlaut im Pfad toetete die Kompression. HandBrake schreibt zwei
Kodierungen in denselben Strom (derselbe Pfad einmal UTF-8, einmal CP850).
In CP850 ist das Byte 0x81, und das ist in cp1252 — was `text=True` auf
deutschem Windows waehlt — undefiniert:
UnicodeDecodeError: charmap codec can't decode byte 0x81
Neu: `rip/handbrake_aufruf.py` mit `HB_LESEN`, benutzt von ripping.py und
caps.py. Bewusst nicht binaer wie bei makemkvcon: HandBrake trennt
Fortschrittszeilen mit CR, im Binaermodus waere der Balken weg.
## Die Rohdaten
4. `rohdaten.kandidaten` machte aus dem Arbeitsordner `F:\` ein `F:` und
verband damit weiter. Das ist unter Windows der aktuelle Ordner auf
Laufwerk F, nicht dessen Wurzel — 16,5 GB waren unsichtbar, und der
Wiederholen-Dialog bot nur „Neu rippen" an. Die Falle steht woertlich im
Kopf von `pfade.verbinden`.
5. Gesucht wurde unter der heutigen Einstellung statt unter der Wahl DIESES
Rips (`meta["work_dir"]`). Genau dafuer wurde rohdaten.py am 26.07.
gebaut; repariert wurde damals die Kandidatenliste, nicht der Aufrufer.
Neu: `_arbeitsverzeichnis_des_jobs`, benutzt an vier Stellen.
6. Zwei Speicher fuer dieselben Ordner: Die Oberflaeche schreibt
`outputDir`/`workDir` in die Datenbank, `betrieb` liest `storage.*` aus
der Konfigurationsdatei, und die schreibt niemand. Gemessen: eingestellt
`E:\Rippy`, angezeigt `C:\Users\...\Videos\Rippy`. Neu:
`betrieb.mit_einstellungen`.
## Das Laufwerk
7. `device_info` fing den OSError ab und lieferte „unknown" ohne den Grund.
Nach einem Rip mit Lesefehlern beantwortete das Laufwerk keine
Medien-Abfragen mehr (Win32-Fehler 1), die Geraete-Auskunft aber schon —
im UI stand eine volle Laufwerkskarte, kein Rip startbar, und im
Protokoll das laengst veraltete „Disc erkannt". Neu: `ZUGRIFFS_GRUENDE`,
ein Feld `grund` im Laufwerks-Eintrag und eine Protokollzeile je Wechsel.
Eine fehlgeschlagene Disc-Erkennung wird ebenfalls protokolliert.
8. Der Linux-Treiber nannte ein unzugaengliches Laufwerk „empty", waehrend
Windows richtig „unknown" sagt. Angeglichen, samt Feld-Paritaet.
9. `CreateFileW`, `DeviceIoControl` und `CloseHandle` hatten weder `restype`
noch `argtypes` — 32-Bit-`c_int` fuer einen 64-Bit-HANDLE, in beide
Richtungen. Mit `restype` aendert sich der Fehlerwert von -1 auf
0xFFFFFFFFFFFFFFFF; die Pruefung deckt jetzt beides ab. Am echten
Laufwerk gegengeprueft, Fehlerpfad eingeschlossen.
10. Der Vor-Scan lief bei JEDER eingelegten Disc ein `makemkvcon info` mit
120 s Zeitgrenze — 20 bis 120 Sekunden „Disc wird gelesen". Frueher war
das schnell, weil der Zweig unter Windows nie lief (`shutil.which`,
repariert am 28.08.). Das Ergebnis landete allein in `toc["tracks"]`,
das niemand liest: Der Rip-Dialog holt seine Liste ueber
`/devices/{id}/scan-tracks`, wenn sie gebraucht wird. Entfernt.
## Notbremsen
11. `_frei_bytes` suchte den naechsten vorhandenen Ordner selbst.
`os.path.dirname("Q:\\")` gibt sich selbst zurueck — ein
Arbeitsverzeichnis auf einer abgezogenen Platte haette den Job vor dem
Rip stumm haengen lassen. Benutzt jetzt
`pfade.naechster_vorhandener`, das den Abbruch seit V2-1 hat.
12. `naechster_vorhandener` haelt Laufwerks- und UNC-Wurzeln jetzt absolut.
13. `aufraeum_skript` baut sein `rmdir /s /q` aus `InstallLocation` in der
Registry. Waere das eine Laufwerks-Wurzel, loeschte die Deinstallation
das Laufwerk. Nicht beobachtet, aber nicht wiedergutzumachen — der
Loeschbefehl bleibt in dem Fall weg.
## Lesefehler
MakeMKV sicherte 1 von 2 Titeln, endete mit 0, und Rippy schrieb „Rip
fertig". Jetzt gibt es eine Warnung, auch wenn der Rip als Erfolg endet, und
die MSG-Nummer steht im Protokoll: MakeMKVs Texte sind uebersetzt, die
Nummern nicht.
## Aus der Gegenprobe am laufenden Rippy
Die erste Fassung dieses Standes war installiert, als der Commander meldete:
„nun oeffnen sich diverse fenster im hintergrund, gehen ganz kurz auf und dann
wieder zu. Das laufwerk hoert auch einfach auf zu lesen." Beides Altlasten,
die erst durch die neue Protokollzeile sichtbar wurden.
14. Prozesserzeugung mitgeschnitten:
14:54:40 timeout.exe timeout 4 ls -d C:\Users\...\d7ee6c06-...
14:54:40 WindowsTerminal.exe
`rohdaten.pruefen` fragt mit `timeout N ls -d`, ob es ein Verzeichnis
gibt. Unter Linux ist das richtig (os.path.isdir kann an einem toten
CIFS-Mount im Kernel haengen, ein Kindprozess laesst sich abbrechen).
Unter Windows ist es dreifach falsch: timeout.exe gibt es dort, kennt
aber weder `ls` noch `-d`; sie braucht eine Konsole, und die reisst
Windows auf; und ihr Rueckgabewert ist nie 0, die Antwort lautete also
„weg" fuer JEDES Verzeichnis. Rohdaten waren unter Windows
grundsaetzlich unsichtbar. Neu: `nativ_nachsehen()`. Die zwei
gleichartigen Aufrufe in mounts.py bekommen dieselbe Absicherung.
15. Der Waechter fragte das Laufwerk alle drei Sekunden ab — auch mitten im
Rip, also drei CreateFileW plus IOCTLs auf ein Geraet, das makemkvcon
gerade liest:
12:49:52 bluray-Rip gestartet
12:50:09 [watcher] Laufwerk G: beantwortet keine Medien-Abfragen
12:50:12 MSG 2003 SCSI-Fehler ILLEGAL REQUEST:INVALID FIELD IN CDB
12:50:12 makemkvcon endete mit Code 11
`_auto_prescan` haelt sich seit dem 29.08.2026 an die Regel „waehrend
eines Rips wird nicht gescannt"; die Laufwerksabfrage tat es nicht.
Jetzt gilt in der Zeit der letzte bekannte Stand.
## Zwei Tests, die gelogen haben
* `assert "--audio-codec" in cmd` schrieb den Fehler fest.
* `lambda: {}` als Doppelgaenger fuer `get_settings(key, bei_fehler_leer)`
brach, sobald ein Aufrufer einen Parameter benutzte — und zeigte dann auf
den Code statt auf sich selbst.
977 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
aeb3128a85 |
fix(windows): Dauerscan der Disc, aufblitzende Fenster, Docker-Einstellung uebernommen
Ampel / ampel (push) Successful in 1m30s
Commander: „liest er die disc NOCHMAL ein das macht aber keinen sinn, wenn er
sie bereits erkannt hat. Außerdem öffnen sich nun immer irgendwelche fenster
ganz kurz im hintergrund."
## Beides war dieselbe Schleife
In der Disc-Wache stand:
except OSError:
continue
Damit blieb `bekannt[pfad]` ungesetzt, und im naechsten Durchlauf war
`vorher is None` — also wieder „Erststart, liegt schon eine Disc drin". **Ein
einziger fehlgeschlagener Lesevorgang loeste einen neuen Vor-Scan aus.**
Und der scheitert regelmaessig: Waehrend der Vor-Scan laeuft, haelt
makemkvcon das Laufwerk (gemessen: /devices braucht dann 14 s statt 5). Also
Scan haelt das Laufwerk -> Statusabfrage scheitert -> naechster Durchlauf
haelt es fuer den ersten -> neuer Scan. Eine Schleife, die sich selbst am
Leben haelt.
Und weil derselbe makemkvcon-Aufruf in `prescan.py` als EINZIGER kein
`creationflags=OHNE_FENSTER` hatte, blitzte bei jeder Runde eine Konsole auf.
Das war meine Zeile von heute Vormittag.
Die Entscheidung steckt jetzt in `disc_entscheidung()` — pure Funktion, also
ohne Laufwerk pruefbar. Zwei getrennte Fragen statt einer: Haben wir dieses
Laufwerk je gelesen, und was war zuletzt drin? Ein Fehlschlag beantwortet die
zweite nicht und darf die erste nicht zuruecksetzen.
Nachgemessen: Ein Tabwechsel loest KEINEN neuen Scan aus (8 Erkennungen
vorher, 8 nachher). Meine erste Zaehlung von „7 vs 8" war ein Messfehler —
zwei verschieden grosse Abfragefenster.
## Aus dem Docker-Betrieb uebernommen
`docker/worker/entrypoint.sh` setzt vor jedem Worker-Start zwei Dinge:
`app_Key` und `app_UpdateEnable`. Der Key war schon uebernommen, die zweite
nicht — auf dem Rechner des Commanders nachgesehen: NICHT gesetzt.
Das ist MakeMKVs Web-Kontakt; darueber holt es die Disc-Schluessel fuer
4K-UHD nach (Meldung 3338, auf Windows gemessen). „Fuer Windows umschreiben"
heisst hier: Registry statt settings.conf, DWORD statt Text — die
Nachbarwerte (`app_BackupDecrypted`) zeigen die Form.
Gesetzt wird NUR, was fehlt: Wer den Web-Kontakt bewusst abgeschaltet hat,
behaelt ihn abgeschaltet. Ein Waechter-Test vergleicht kuenftig die
`app_*`-Namen aus der entrypoint.sh gegen die Windows-Seite — sonst driften
die beiden Betriebe auseinander.
871 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
954f293871 |
fix(windows): Rippen ging nicht — drei Container-Annahmen im Rip-Pfad
Commander: „Das Rippen der disk funktioniert nicht. makemkvcon endete mit
Code 11 — letzte Meldung: Das Öffnen der Disk schlug fehl — keine MKV-Datei
entstanden"
In diesem einen Satz steckten drei Fehler. Alle drei an seinem Laufwerk
gemessen, nicht hergeleitet.
## 1. Die Quellenangabe — das war der Abbruch
dev:\.\G: MSG:2024 „Unknown device" -> MSG:5010 Öffnen schlug fehl
dev:G: 68 TINFO-Zeilen, „Die Aufgabe wurde erfolgreich abgeschlossen"
`f"dev:{device_path}"` stand an VIER Stellen. Unter Linux richtig (/dev/sr0),
unter Windows nicht: `rippy.drives.windows` meldet den Geraete-Namensraum
`\.\G:`, den `CreateFileW` fuer die IOCTLs braucht — MakeMKV kennt ihn
nicht.
## 2. „Ö" statt „Ö"
`Ö` als UTF-8 (C3 96), gelesen als cp1252. `subprocess` stand auf `text=True`
ohne `encoding=`, nahm also die Gebietsschema-Kodierung; Rippy stellt seine
Konsole beim Start aber auf UTF-8 (sonst stirbt der Start an einer
Umlaut-Logzeile). Direkt gemessen kommen die Bytes je nach Umgebung als UTF-8
ODER cp1252 — deshalb wird jetzt bestimmt statt behauptet.
## 3. Die Ursache fehlte im Fehlertext
Gesucht wurde nach ENGLISCHEN Textbausteinen („evaluation period has
expired"). Auf einem deutschen Windows trifft keiner. Jetzt zaehlen die
Meldungs-NUMMERN, die sprachunabhaengig sind.
⚠️ Beim ersten Anlauf hatte ich 5021 aus dem Kopf mit „Volume-Key unbekannt"
beschriftet — falsch. Meine eigene Ausgabe zeigte nur meine Beschriftung
statt MakeMKVs Text, sodass es fast durchgegangen waere. Der echte Wortlaut
steht jetzt im Code.
## Nebenbefund: der Beta-Key lag zweimal am falschen Ort
`makemkv_daten.DATEN_DIR` war fest `/root/.MakeMKV`. Nach der Reparatur des
Ordners blieb MSG:5051 trotzdem stehen — weil MakeMKV unter Windows seine
Einstellungen in der REGISTRY haelt (HKCU\Software\MakeMKV), nicht in einer
settings.conf. Nachgesehen statt vermutet.
Und der wichtigste Teil davon: **Ein abgelehnter Schluessel ist schlimmer als
gar keiner.** Ohne Key las MakeMKV die Disc noch (68 TINFO), mit dem
abgelehnten verweigerte es alles (0 Titel). Deshalb wird jetzt geprueft und
bei Ablehnung zurueckgenommen.
## Beweis
Echter Rip ueber `ripping.run_makemkv`, kuerzester Titel:
Status success, Code 0, „Evangelion 2.22_t03.mkv" 217,9 MB
5036 „Das Kopieren wurde abgeschlossen. 1 Titel wurden gesichert."
828 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
35370fd552 |
fix(windows): Cover, Arbeitsverzeichnis und die zurueckgelassenen _MEI-Ordner
Drei Befunde des Commanders vom 28.08.2026, alle drei gemessen.
## 1. „Was ist mit dem Cover auf Windows Rippy?"
Es gab keins, weil es keinen Treffer gab. `_scan_video` verlangte woertliche
Gleichheit:
if movie.get("title", "").lower() == kandidat.lower():
An seiner Disc gemessen:
'Evangelion 2.22' 1 Treffer Evangelion: 2.0 You Can (Not) Advance
'Evangelion' 20 Treffer irgendein Evangelion
Der EINZIGE Treffer auf den vollen Disc-Titel war der richtige Film, mit
Poster — und wurde verworfen. Jetzt zaehlt die SPEZIFITAET der Anfrage: Wer
auf den vollen Disc-Titel hoechstens drei Treffer bekommt, hat gefragt wie
jemand, der weiss was er sucht. Ergebnis an derselben Disc:
Evangelion: 2.0 You Can (Not) Advance / 2009 / 80 % / Poster + dt. Text
## 2. „Warum heisst das hier noch container platte? … Waere es moeglich das
## Arbeitsverzeichnis zu aendern? momentan geht das nicht."
Beide Haelften gehen auf EINE Zeile zurueck: `MEDIA_ROOT = "/app/media"` war
zugleich Vorgabe UND Pfadgrenze. Auf Windows gibt es den Ordner nicht:
* `/storage-targets` fing den OSError und gab still [] zurueck — die Auswahl
hatte genau einen Eintrag. Das ist „momentan geht das nicht".
* Dessen Text war fest verdrahtet „(Container-Platte)".
* `/browse` antwortete auf jeden Pfad mit 422.
Die Grenze faellt nicht weg, sie wird betriebsabhaengig: Container und
Kopflos-Betrieb bedienen ein Netz, die native App den Menschen davor.
Gemessen auf seinem PC: 7 Laufwerke zur Auswahl, X/Y/Z mit je 2,2 TB frei.
## 3. „Failed to remove temporary directory: …_MEI0000b0882"
GEMESSEN: 20 zurueckgelassene _MEI-Ordner mit 1,1 GB. Ursache: `Popen` ohne
`env=` reicht PyInstallers Auspack-Zeiger an die Kinder weiter (--dienst und
--oeffnen). Beide laufen dann im Ordner des Elternprozesses, der sich zuerst
beendet und ihn loeschen will. Waere das TEILWEISE geglueckt, haetten Dienst
und Fenster mitten im Betrieb ihre Dateien verloren.
Nebenbefund: test_setup_fenster scheiterte in jeder Umgebung ohne pywebview.
794 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
be3fac53af |
fix(prescan): makemkvcon ueber den Katalog suchen, nicht ueber shutil.which
Ampel / ampel (push) Successful in 55s
Auf die Frage des Commanders: "du hast jetzt nur mein laufwerk analysiert
oder? wenn ich rippy weitergebe wie laeuft es dort?"
Berechtigt -- und beim Nachsehen fiel derselbe Fehler auf wie heute Vormittag
im Werkzeug-Katalog:
shutil.which("makemkvcon") -> None, auch bei installiertem MakeMKV
`which` sucht nur im PATH. Windows-Programme liegen in "Programme". Der
Zweig, der die Titelliste von makemkvcon holt, lief unter Windows also NIE --
unabhaengig vom Laufwerk, auf jedem Rechner. Jetzt ueber
`katalog.finden("makemkv")`, das genau dafuer da ist.
Dazu vier Tests fuer Disc-Arten, die ICH NICHT HABE, damit die Zusage nicht
an einer einzigen Disc haengt:
Blu-ray ohne BDMV/META -> None (sehr haeufig; Rueckfall aufs Label)
DVD (nur VIDEO_TS) -> None
bdmt_deu.xml vorhanden -> Titel wird gelesen
deu + eng vorhanden -> eng gewinnt (APIs suchen damit besser)
Und ein Waechter gegen shutil.which in prescan.py. Der erste Anlauf davon
fiel ueber den eigenen Erklaertext, in dem der alte Aufruf zitiert wird --
er prueft jetzt nur Code, keine Kommentare.
Ampel lokal: 774 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
878b24c43b |
fix: die Metadaten-Suche war seit V2-1 tot — auf Windows UND auf der VM
Ampel / ampel (push) Successful in 54s
Der Commander: "TMDB und OMDB Key sind hinterlegt, das laufwerk wird aber
auch nicht korrekt ausgelesen. Eigentlich sollte er direkt bei TMDB oder OMDB
oder JIKAN anfragen nach metadaten."
Er hat das Symptom gemeldet. Darunter lagen VIER Fehler.
## 1. Ein Phantom-Modul (der schwerste)
clients/tmdb.py:27 from db import get_settings
clients/omdb.py:36 from db import get_settings
clients/thetvdb.py:16 from db import get_settings
`docker/api/db.py` gibt es seit der Zusammenlegung in V2-1 (
|
||
|
|
3d7f3b1680 |
feat(drives): V2-4 (Teil 2) — main.py laedt unter Windows, Treiber gemessen
Ampel / ampel (push) Failing after 46s
WAS: rippy/drives/__init__.py waehlt den Treiber fuer DIESE Maschine.
main.py und prescan.py fragen ihn, statt fest den Linux-Treiber zu
importieren. Damit ist der Blocker fuer den nativen Windows-Betrieb weg.
GEMESSEN, vorher (frische Python-3.12-Umgebung unter Windows):
>>> import main
ModuleNotFoundError: No module named 'fcntl'
GEMESSEN, nachher:
main.py laedt unter Windows: JA
Treiber: rippy.drives.windows
Routen: 61
Laufwerke: ['\\.\G:']
Der Import stand in rippy/drives/linux.py -> detection.py -> fcntl. Genau
dafuer gibt es den Port: Der Aufrufer sagt WAS, nicht WIE. test_treiberwahl.py
haelt ausserdem fest, dass BEIDE Treiber denselben Satz Funktionen anbieten —
sonst faellt eine fehlende erst im Betrieb als AttributeError auf, und zwar
auf der anderen Plattform.
DER TREIBER AN ECHTER HARDWARE (LG BU40N extern, Laufwerk G:, LEER):
GetDriveTypeW("G:\\") -> 5 (DRIVE_CDROM)
list_optical_devices() -> ['\\.\G:']
CreateFileW(r"\.\G:") -> Handle 380
CHECK_VERIFY2 bei leerem Laufwerk -> ERROR_NOT_READY (21)
drive_status() -> 1 (CDS_NO_DISC)
detect_disc_type() -> 'no_disc'
device_info()['status'] -> 'empty'
MEDIA_REMOVAL (entriegeln) -> Erfolg, 0,1 ms
EJECT_MEDIA -> Erfolg, 664,8 ms
Die 664,8 ms sind der Beleg, dass wirklich etwas passiert ist: Ein
abgelehnter Steuercode kommt in rund 2 ms zurueck.
BEFUND, DER EIN PLACEBO VERHINDERT HAT: IOCTL_STORAGE_LOAD_MEDIA (Schublade
einziehen) antwortet auf diesem Laufwerk mit ERROR_INVALID_FUNCTION. Bei
flachen und externen Laufwerken ist das die Regel. Deshalb gibt es bewusst
KEINE einziehen()-Funktion — ein Knopf "Schublade schliessen", der auf der
Haelfte aller Laufwerke stillschweigend nichts tut, waere genau die Sorte
Placebo, die in Etappe 19 aufgeraeumt wurde.
Dazu eine DeprecationWarning behoben: '\G' im Docstring ist keine gueltige
Escape-Sequenz und waere in einer kuenftigen Python-Version ein harter Fehler.
NOCH NICHT gemessen: das Verhalten MIT eingelegter Disc (Typ-Erkennung ueber
die Groesse, und ein Auswurf, bei dem wirklich etwas drin ist).
GEMESSEN: ruff sauber, 429 Tests gruen + 13 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b98dc5eef5 |
refactor(core): V2-0 — gemeinsames Paket rippy statt drei Zwillingsdateien
Ampel / ampel (push) Successful in 41s
WAS: Neues Paket src/rippy (core/drives/rip). detection.py,
makemkv_daten.py und notify.py lagen je ZWEIMAL im Repo — unter
docker/api UND docker/worker, byte-identisch. Jetzt gibt es sie einmal;
beide Container importieren dieselbe Datei. Verhalten unveraendert.
WARUM: Es gab kein geteiltes Paket zwischen den Containern, deshalb die
Kopien. docker/api/db.py sagt es im Kopfkommentar selbst: "Wer die
Struktur aendert, aendert BEIDE Dateien." Ein Waechter-Test
(test_zwillinge_sind_byteweise_identisch) hat das mechanisch
abgesichert — er war noetig, weil die Konstruktion falsch war. Erste
Etappe des v2-Plans (KONZEPT-V2.md §8.3).
IM EINZELNEN:
- src/rippy/{core/notify, drives/detection, rip/makemkv_daten}.py
- conftest.py in der Wurzel legt src/ auf den sys.path (die Ampel ruft
pytest dort auf).
- Beide Dockerfiles kopieren src/rippy nach /app/rippy — /app ist
Arbeitsverzeichnis und uvicorn-App-Dir, also ohne PYTHONPATH findbar.
- worker_setup_paket packt das Paket ausdruecklich mit ins Zip: die
Schleife sah nur die oberste Ebene, ein Unterordner waere nie
mitgekommen und der Windows-Worker beim Start gestorben.
- Die zwei Test-Dateien fuer makemkv_daten sind zu einer verschmolzen
(die Faelle aus beiden). Damit entfaellt auch der importlib-Umweg im
Worker-Test: der war noetig, weil bei "pytest -q" aus der Wurzel
docker/api zuerst eingesammelt wird und jeder weitere Import nur noch
den sys.modules-Cache trifft — die Worker-Kopie wurde also nie
angefasst. Netto -13 doppelte Tests, +2 neue (siehe unten).
EIN FEHLER, DEN DER UMBAU FAST AUSGELIEFERT HAETTE: In tasks.py steht
der detection-Import in einem "try/except ImportError" — absichtlich,
denn der native Windows-Worker hat kein fcntl und soll trotzdem
starten. Das except verschluckt aber JEDEN ImportError, auch einen
falschen Modulpfad. Auf Linux haette der Worker ab sofort still
detect_disc_type=None gesetzt und jeden Rip verweigert, ohne dass
irgendwo ein Fehler stuende — genau die Klasse "still scheiternder
Hintergrund-Prozess" aus AGENTS.md. Gefunden ueber eine zweite Suche
mit eingerueckten Treffern.
Waechter dagegen: src/rippy/test_paket.py prueft mit
importlib.util.find_spec (fuehrt nichts aus, laeuft also auch auf
Windows ohne fcntl), dass es die drei Modulpfade wirklich gibt. Beim
Wegnehmen von detection.py gegengeprueft: Test wird rot, nach
Wiederherstellen gruen.
GEMESSEN: ruff sauber, 290 Tests gruen + 1 uebersprungen (lokal, ohne
die zwei Linux-only-Module). Kein Modul liegt noch doppelt im Repo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0c269a97ee |
fix(metadaten): exakt schlaegt unscharf - Jikan beendet die Kette nur bei Treffer
Der SAVEPOINT v3.16 vermutete, das Aehnlichkeits-Gate 0,55 sei "grosszuegig" und Jikan stehe in der Kette zu frueh (vor OMDb). Nachgerechnet - titel_aehnlichkeit ist eine reine Funktion - ergibt sich ein anderes Bild als vermutet: "Alien" vs. "Alien 9" -> 0,83 TRIFFT "Inception" vs. "Deception" -> 0,78 TRIFFT "Hero" vs. "Heroman" -> 0,73 TRIFFT "The Dark Knight" vs. "Dark Knight Rises" -> 0,69 TRIFFT Ein Kinofilm, den TMDB verpasst, bekam damit Anime-Metadaten mit Confidence 0,85 - und OMDb, das ihn kennt, wurde nie gefragt. Bemerkenswert dabei, und das widerlegt die Vermutung "einfach zu niedrig": Fuer den Fall, fuer den Jikan ueberhaupt eingebaut wurde, ist das Gate sogar zu HOCH. "Evangelion 2.22" gegen den MAL-Titel "Evangelion: 2.0 You Can (Not) Advance" ergibt 0,51 und faellt durch. Eine einzelne Zahl kann beides nicht leisten. Deshalb zwei Schwellen statt Umsortieren (Reihenfolge bleibt, AGENTS Regel B): Nur ein praktisch exakter Titel (>= 0,9) beendet die Kette. Ein unscharfer Treffer wird GEMERKT, dann wird OMDb gefragt - und erst wenn OMDb nichts hat, kommt er als VORSCHLAG mit Confidence 0,6 zum Zug. Damit gewinnt "exakt" immer gegen "unscharf", egal aus welcher Quelle. Antwort auf die Commander-Frage "JIKAN ist drin - wird das genutzt?": ja, und ab jetzt an der richtigen Stelle. Ehrlicher Vorbehalt: Live gegengeprueft ist das nicht - MyAnimeList war waehrend dieser Sitzung durchweg weg (Jikan antwortete HTTP 504 auf jede Anfrage). Die Arithmetik des Gates ist davon unberuehrt und in test_jikan_helpers.py festgehalten, die Kettenlogik in test_prescan_helpers.py. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8eb5653848 |
Restefeger: Auth komplett raus, Serien-Flow + Episoden-Matching, Jellyfin-Refresh, Duplikat-Warnung, echtes Nur-Hauptfilm
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> |
||
|
|
5778ac4645 |
Praxis-Feedback-Runde: CIFS-Cap-Fix, TMDB v3-Keys, 4K-UHD-Typ, Vollautomatik, Job-Verwaltung, Worker-Namen
Ampel / ampel (push) Successful in 29s
Zwei bewiesene Bug-Fixes:
- SMB-Mount 'Unable to apply new capability set': mount.cifs hebt
CAP_DAC_READ_SEARCH an, die in Dockers Default-Caps fehlt (auf der VM
reproduziert: Bounding-Set a82425fb ohne Bit 2; mit der Capability
verschwindet der Fehler). Fix: cap_add DAC_READ_SEARCH fuer den
api-Container.
- TMDB fiel still aus: Client konnte nur v4-Bearer-Tokens, der uebliche
32-Hex-v3-Key bekam 401 und die Suche lieferte nur OMDb. Jetzt beide
Key-Arten (ist_v4_token + api_key-Query-Param lt.
developer.themoviedb.org, mit Tests) — damit kommen auch die deutschen
Texte an (language=de-DE war ueberall schon gesetzt). Neu:
GET /metadata/status + 'Verbindung pruefen' in Einstellungen -> APIs
(Live-Check am Cache vorbei).
Features aus dem Commander-Feedback:
- 4K UHD als eigener Disc-Typ: classify >= 55 GiB (BD-66/BD-100; BD-50
bleibt bluray), beide detection.py + Tests, eigene Badge-Farbe in
Dashboard/Laufwerken, Prescan-Label '4K UHD'. UHD-Rip-Fehler 'Failed to
open disc' (Code 11) bekommt Klartext: LibreDrive-Firmware noetig.
- Vollautomatik (Setting autoRipStart): Disc erkannt -> Rip startet ohne
Popup in den Schnellwahl-Ordner (Serie->Serien, sonst Filme, CD->Musik).
- Job-Verwaltung: 'Neu komprimieren' nur noch mit can_retry (Rohdaten
liegen wirklich da), DELETE /jobs/{id} + 'Erledigte aufraeumen'
(Dateien bleiben immer), 'Alle herunterladen' im Job-Detail
(gestaffelte Einzel-Downloads statt Server-Zip von 40-GB-Dateien).
- Worker zuordenbar: WORKER_NAME-Env als stabiler Anzeigename (fixt auch
die Offline-Leichen nach Rebuilds), info.hostname/ip gemeldet,
Online-Abgleich ueber hostname, DELETE /workers/{name} + Papierkorb im
UI, Quelle-Anzeige an der Disc-Karte ('Quelle: TMDB - 99 % sicher').
- Ripping-Tab nach Medium gegliedert (Video/Audio-CD/Allgemein) +
Untertitel-Klartext (--all-audio/--all-subtitles bleiben komplett),
CD -> Musik-Vorauswahl im Ziel-Dialog, Ordner-Verwaltung beschriftet
und standardmaessig eingeklappt.
- Docs: SAVEPOINT v3.3, ROADMAP Etappe 14 + Ideen (nativer
Windows-Worker, deutsche OMDb-Texte via TMDB-Find), README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
6b7d391737 |
Korrektur-Flow + Freigaben-Auswahl + Schreibtest + Schnellwahl-Klartext
Ampel / ampel (push) Failing after 52s
Commander-Befunde (23.07. spaet): - MANUELLE KORREKTUR (KONZEPT Schritt 5 endlich komplett): Metadaten-Seite hat jetzt Titelsuche ueber ALLE Quellen (GET /metadata/search) mit Poster-Kacheln; ein Klick uebernimmt den Treffer (POST /metadata/override) fuer Karte, Jobs UND merkt ihn sich 30 Tage per Disc-Fingerabdruck — dieselbe Disc wird beim naechsten Einlegen sofort richtig erkannt - PC/NAS per Klick: SMB-Freigaben eines Rechners auflisten (GET /storage-mounts/shares via smbclient) statt //host/share raten - Berechtigungs-PRUEFUNG beim Einhaengen: Schreibtest, Warnung bei nur-lesbaren Zielen (API + UI + Log) - "Standard-Unterordner" verstaendlich gemacht: heisst jetzt "Schnellwahl beim Rippen" mit Erklaertext; kryptisches Basis-Feld raus - Preview zeigt Poster; Fingerprint fuer Cache/Override vereinheitlicht (billiges Label+Groesse statt Mount-Titel) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
37711f87c1 |
Jikan-Retry bei 504 + Cache-Verfall fuer mittelgute Prescan-Treffer
Ampel / ampel (push) Failing after 39s
Jikan (Gratis-Dienst) wirft sporadisch 504 — ein Retry mit 2s Pause; aktuell ist der Dienst ganz down, die Kette faellt sauber auf OMDb. Prescan-Cache: 0.6-0.9 verfaellt nach 1 Tag, ab 0.9 nach 7 Tagen — sonst wuerde ein frueher OMDb-Treffer bessere Quellen fuer immer blocken. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
07be54e62a |
Jikan/MAL-Quelle + Job-Titel + Abbrechen-Knopf + Dark-Hover + Ordner-Verwaltung
Ampel / ampel (push) Failing after 38s
Commander-Befunde (Screenshots 23.07. abends):
- Jikan (MyAnimeList, kostenlos OHNE Key) als Anime-Quelle vor OMDb —
waehlt per Titel-Aehnlichkeit (SequenceMatcher, Schwelle 0.55) statt
blind Treffer 1; damit trifft "Evangelion 2.22" den exakten Film
- Job-Titel: POST /jobs uebernimmt den erkannten Disc-Titel aus dem
DISC_CACHE — Schluss mit "Unbekannt" in der Queue
- Kooperativer Job-Abbruch: POST /jobs/{id}/cancel setzt canceling,
der Worker prueft das Flag bei jedem Fortschritts-Update und killt
den Encoder-Prozess sauber (RipAbbruch); UI-Knopf "Abbrechen" fuer
laufende, Status-Badge "Wird abgebrochen"
- Dark-Mode: Job-Zeilen-Hover war hart hell (hover:bg-slate-50)
- Speicherziele: lokaler Ordner-Browser mit "Ordner anlegen"
(GET /browse + POST /browse/mkdir, nur unter /app/media)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
f2d5961c33 |
Metadaten-Feinschliff: Wort-Strip-Degradation + kein Caching von "unknown"
Ampel / ampel (push) Failing after 40s
- titel_kandidaten strippt hintere Worte iterativ bis auf eins
("Evangelion 2.22" wird auch als "Evangelion" gesucht — OMDb braucht das)
- Nur Treffer ab Confidence 0.6 landen im Redis-Cache: ein gecachtes
"unknown" wurde sonst auch nach Key-Eintrag ewig wieder serviert
(Redis ist persistent; Alt-Eintraege auf der VM geloescht)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
90eb17d407 |
UX-Runde: echte Disc-Titel via BD-Metadaten, Preview-Crash, Dashboard, Ziele
Ampel / ampel (push) Failing after 38s
Commander-Befunde 23.07. abends:
1. Metadaten "klappen nicht": Wurzel = kryptisches Volume-Label + kein
TMDB-Key. Jetzt: API mountet die Disc read-only (CAP_SYS_ADMIN ist da)
und liest den KLARTEXT-Titel aus BDMV/META/DL/bdmt_*.xml; dazu
progressive Suchdegradation (Titel-Varianten) und OMDb-Unscharf-Suche
(s= + imdbID-Nachladen) — mit dem vorhandenen OMDb-Key gibt es damit
Titel/Jahr/Poster auch ohne TMDB
2. Weisses Fenster beim Pre-Scan: Lucide-Icons sind forwardRef —
Direktaufruf getIcon(...)({size}) crashte die Seite; als JSX gerendert
3. Dashboard sortiert: System-Leiste (Encoder + aktiver Job) oben,
Disc/Geraete -> Stats -> Jobs, Live-Log ans Ende
4. Ziel-Dialog mit ORDNER-BROWSER (GET /browse, nur unter /app/media);
Schnellwahl nutzt die Settings-Unterordner statt Hartkodierung;
Tab "Verzeichnisse" in "Speicherziele" aufgegangen (redundant)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
7652afe955 |
Deploy-Befunde gefixt: Disc-Typ-Verlust, giftiger Prescan-Cache, caps-Import
Ampel / ampel (push) Failing after 28s
- Pre-Scan verlor den erkannten Typ (BDs hiessen immer "DVD"): scan() reicht disc_type jetzt in die Zweige durch - GIFTIG: Cache-Key nur aus Geraetepfad — die naechste Disc im selben Laufwerk haette die Metadaten der vorherigen geerbt. Key enthaelt jetzt einen Disc-Fingerabdruck (Label|Groesse) - Worker-Faehigkeiten: import caps auf Modulebene — im worker_ready-Signal war /app nicht mehr im sys.path (ModuleNotFoundError im Deploy-Log) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2474b683e2 |
Transcode-Stufe: HandBrake komprimiert NACH dem MakeMKV-Rip + --noscan-Fix
Ampel / ampel (push) Successful in 34s
Commander-Entscheid 23.07.: 40-GB-Rohdateien sind kein brauchbares Endprodukt. Architektur bleibt zweistufig, weil HandBrake AACS nicht lesen kann: MakeMKV rippt verlustfrei nach /app/temp/raw, HandBrake macht daraus x265 auf Arbeitsgroesse in /app/media, Roh-Verzeichnis wird erst NACH komplettem Erfolg geloescht (keepOriginal behaelt es). - Worker: handbrake-cli im Image, run_handbrake + _komprimiere, Job-Status "transcoding", Settings aus Postgres (transcodeEnabled/ Preset/keepOriginal), Fortschritt je Datei aggregiert - makemkvcon: --noscan im Kommando verankert — der Geraete-Scan haengt (1.18.4) bzw. crasht (1.17.7) im Container, mit --noscan + dev:-Pfad laeuft es (Befund 23.07., DRV-Zeile beweist Laufwerks-Erkennung) - UI: Settings-Tab "Verarbeitung" (Toggle/Preset/Original behalten), Status-Badge "Komprimieren", LiveLog erkennt transcoding als aktiv - KONZEPT/ROADMAP entsprechend aktualisiert; Tests fuer HandBrake-Cmd (arbeitet auf DATEI, nie am Geraet) und Progress-Regex Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
bd6409038e |
UDF-Volume-Label-Leser (Blu-rays haben keine ISO-Bridge) + Etappe 12 (ARM)
Ampel / ampel (push) Successful in 35s
Praxis-Befund mit echter BD-50: reines UDF, der ISO-PVD-Leser griff nicht. Jetzt ECMA-167-Weg (AVDP Sektor 256 -> Main VDS -> PVD Tag-ID 1 -> Volume Identifier als d-string), mit Tests fuer das d-string-Parsing. ROADMAP Etappe 12 aus der ARM-Vollanalyse: was wir uebernehmen (Fingerprint-DB, bdmt_eng.xml, Suchdegradation, Manual Mode, Apprise, Multi-Drive, Backup-Modus) und wo wir ARM schlagen (Serien-Episoden- Matching, Main-Feature per Laufzeitabgleich). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5528e0f652 |
Metadaten-Fallback OMDb + Pre-Scan repariert + Eject-Endpoint
- clients/omdb.py: OMDb als zweite Quelle (Fallback-Kette: TMDB exakt ->
OMDb -> bester TMDB-Vorschlag mit niedriger Confidence -> unknown)
- Pre-Scan-Reparatur: Titel kam nie an — makemkvcon existiert nur im
Worker, isosize war nirgends installiert (fiel still auf "DVD" zurueck).
Jetzt: ISO-9660-Volume-Label direkt vom Medium + Label-Normalisierung
(PULP_FICTION -> Pulp Fiction), Disc-Typ ueber zentrale detection.py
- POST /devices/{name}/eject (CDROMEJECT-ioctl) mit Job-Schutz (409 wenn
auf dem Laufwerk gerade gerippt wird)
- OMDB_API_KEY in compose/.env.example; .env.example komplett ehrlich
dokumentiert (JWT-Pflicht, MakeMKV-Beta-Key-Rhythmus)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
0ea5f10b31 |
Fix-Runde nach Review: alle Befunde behoben + echte Tests
Ampel / ampel (push) Failing after 12m31s
- Metadaten-Preview WIEDERHERGESTELLT (Stub ueberschattete echte prescan-Implementierung), tote Altmodule geloescht - Celery update_state statt Phantom-Task/erfundener API - abcde-Kommando korrigiert (CD-Ripping war nie funktionsfaehig) - JWT: fester Schluessel Pflicht, echtes Logout, Cleanup nur Abgelaufene - main.py: crashende Endpoints (Path/secrets/api_keys), year-Bug, Admin-Login aus .env - Ruff gruen (29 Funde), Tests: auth/cache_keys/ripping_helpers, Placebo-test_health raus - SAVEPOINT: offene MakeMKV-Entscheidung SICHTBAR gemacht (Regel B) |
||
|
|
ba2429612a |
rippy-ui-modern: API UI Modernisiert & Import-Fixes
- Doppelte Arcane-Einträge behoben (rippy statt Rippy) - Import-Fixes: relative → absolute imports in main.py, cache/__init__.py, prescan/__init__.py, clients/__init__.py - Neue Dateien: cache.py, prescan.py, Settings.tsx - API-Port 8000 in docker-compose.yml gemappt - UI mit Sidebar, Dark Mode, Einstellungen-Tabs Fixes: #2978 (doppelte Einträge), #2888 (Import-Fehler) |