docs: SAVEPOINT v4.0-rc11
Der Schalter, den es nicht gibt — und vierzehn weitere Funde aus demselben Rundgang. Jeder mit der Messung, an der er haengt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d3e86d2641
commit
9050fea3f0
+219
-1
@@ -1,6 +1,224 @@
|
||||
# SAVEPOINT — Rippy
|
||||
|
||||
## Aktueller Stand: v4.0-rc10 — makemkvcon ueberlebt Rippy nicht mehr (29.08.2026)
|
||||
## Aktueller Stand: v4.0-rc11 — der Schalter, den es nicht gibt (30.08.2026)
|
||||
|
||||
> **Ein falscher Kommandozeilen-Schalter kostete einen fertigen 16,5-GB-Rip —
|
||||
> und HandBrake meldete es als Erfolg.** Dazu vierzehn weitere Funde aus
|
||||
> demselben Rundgang. 977 Tests gruen.
|
||||
|
||||
### Der Befund
|
||||
|
||||
Spartacus Disc 2, eine Blu-ray mit Kratzern. MakeMKV sichert 1 von 2 Titeln,
|
||||
die Kompression startet — und ist in derselben Sekunde vorbei:
|
||||
|
||||
```
|
||||
13:18:57 Kompression gestartet (1 Datei, Preset 'H.265 VCN 1080p')
|
||||
13:18:57 Kompression fehlgeschlagen: HandBrake endete mit Code 0
|
||||
```
|
||||
|
||||
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`. Und ein unbekannter Schalter ist fuer HandBrake kein
|
||||
Fehler: Rueckgabewert **0**. Rippy sah nur „Code 0" und keine Datei, riet auf
|
||||
„Zielordner nicht beschreibbar" — und schickte die Suche in die falsche
|
||||
Richtung.
|
||||
|
||||
Ein Test hat den Fehler festgeschrieben statt ihn zu finden:
|
||||
`assert "--audio-codec" in cmd`. Ein Kommandozeilen-Schalter ist eine externe
|
||||
Schnittstelle (Regel D) — er gehoert am echten Programm gemessen.
|
||||
|
||||
### Die Kompression
|
||||
|
||||
* **`--aencoder` statt `--audio-codec`.** Am mitgelieferten HandBrakeCLI
|
||||
1.11.2 gemessen, danach mit einem 5-Sekunden-Encode auf der echten
|
||||
Roh-Datei gegengeprueft.
|
||||
* **HandBrakes letzte Worte werden aufgehoben** (12 Zeilen gepuffert, 4 in der
|
||||
Meldung). Vorher wurde jede Zeile weggeworfen, die kein Fortschritt war —
|
||||
bei Rueckgabewert 0 blieb damit gar keine Auskunft uebrig. Dazu eine eigene
|
||||
Erkennung fuer `unknown option (...)`, die VOR allen anderen greift.
|
||||
* **Ein `ü` im Pfad toetete die Kompression.** HandBrake schreibt zwei
|
||||
Kodierungen in denselben Strom — denselben Pfad einmal als UTF-8, einmal als
|
||||
CP850. In CP850 ist `ü` das Byte 0x81, und das ist in cp1252 (was
|
||||
`text=True` auf einem deutschen Windows waehlt) **undefiniert**:
|
||||
|
||||
```
|
||||
UnicodeDecodeError: charmap codec can't decode byte 0x81 in position 785
|
||||
```
|
||||
|
||||
Betroffen war jeder Film mit „Glück", „Tür", „München", „Über", „Grün" —
|
||||
dazu `ì`, `Å`, `É`, `Ø`. Die Entscheidung liegt jetzt als `HB_LESEN` im
|
||||
gemeinsamen `rip/handbrake_aufruf.py`, weil `caps.py` HandBrake ebenfalls
|
||||
aufruft. Bewusst NICHT binaer wie bei makemkvcon: HandBrake trennt seine
|
||||
Fortschrittszeilen mit CR.
|
||||
|
||||
### Die Rohdaten
|
||||
|
||||
* **`F:\` wurde zu `F:`.** `rohdaten.kandidaten` strich den Schluss-Trenner ab
|
||||
und verband mit dem Rest weiter. `F:` ohne Trenner ist unter Windows der
|
||||
AKTUELLE Ordner auf Laufwerk F, nicht dessen Wurzel:
|
||||
|
||||
```
|
||||
verbinden("F:", job_id) -> F:1aa41fef-... isdir: False
|
||||
verbinden("F:\", job_id) -> F:\1aa41fef-... isdir: True
|
||||
```
|
||||
|
||||
16,5 GB Rohschnitt unsichtbar, und der Wiederholen-Dialog bot nur „Neu
|
||||
rippen" an: Stunden am beschaedigten Datentraeger fuer etwas, das dalag. Die
|
||||
Falle steht woertlich im Kopf von `pfade.verbinden`.
|
||||
* **Gesucht wurde unter der heutigen Einstellung, nicht unter der Wahl des
|
||||
Jobs.** Der Rip-Dialog laesst pro Rip waehlen (seit v3.15); die Wahl steht in
|
||||
`meta["work_dir"]`. Genau dafuer wurde `rohdaten.py` am 26.07. gebaut —
|
||||
repariert wurde damals die Kandidatenliste, nicht der Aufrufer.
|
||||
* **Zwei Speicher fuer dieselben Ordner.** Die Oberflaeche schreibt
|
||||
`outputDir`/`workDir` in die Datenbank, `betrieb` liest
|
||||
`storage.medien`/`storage.temp` aus der Konfigurationsdatei — und die
|
||||
schreibt niemand. An der laufenden Instanz gemessen:
|
||||
|
||||
```
|
||||
eingestellt E:\Rippy (beides)
|
||||
angezeigt C:\Users\TobisPC\Videos\Rippy und ...\_arbeit
|
||||
```
|
||||
|
||||
Neue Bruecke `betrieb.mit_einstellungen` — dieselbe Reihenfolge, die der
|
||||
Worker seit jeher benutzt.
|
||||
|
||||
### Das Laufwerk
|
||||
|
||||
* **Rippy kannte den Grund und behielt ihn fuer sich.** Nach dem Rip mit 29
|
||||
Lesefehlern und einem gescheiterten Auswurf beantwortete das Laufwerk nichts
|
||||
mehr, was mit dem MEDIUM zu tun hat — die Geraete-Auskunft aber schon:
|
||||
|
||||
```
|
||||
CreateFileW mit GENERIC_READ -> Win32-Fehler 1
|
||||
IOCTL_STORAGE_CHECK_VERIFY2 -> Win32-Fehler 1
|
||||
IOCTL_CDROM_DISK_TYPE -> Win32-Fehler 50
|
||||
CreateFileW mit Zugriff 0 -> geht
|
||||
IOCTL_STORAGE_QUERY_PROPERTY -> geht
|
||||
```
|
||||
|
||||
Im UI stand eine vollstaendige Laufwerkskarte mit Modell und Seriennummer,
|
||||
daneben „unknown" — und kein Rip startbar. Commander: *„jetzt erkennt rippy
|
||||
die disk garnicht mehr (im log steht zwar erkannt, aber ein start des rips
|
||||
ist nicht möglich)"*. Jetzt gibt es `ZUGRIFFS_GRUENDE` mit gemessenem
|
||||
Klartext je Fehlernummer, der Grund reist im Laufwerks-Eintrag mit, und die
|
||||
Wache schreibt ihn einmal je Wechsel ins Protokoll — samt „antwortet
|
||||
wieder". Auch eine fehlgeschlagene Disc-Erkennung landet dort, statt nur auf
|
||||
einer Konsole, die niemand sieht.
|
||||
* **Der Linux-Treiber nannte ein unzugaengliches Laufwerk „empty"**, waehrend
|
||||
Windows richtig „unknown" sagt. Dieselbe Lage, zwei Antworten. Angeglichen;
|
||||
der Feld-Paritaetstest deckt das neue Feld mit ab.
|
||||
* **ctypes ohne Typangaben.** `CreateFileW`, `DeviceIoControl` und
|
||||
`CloseHandle` hatten weder `restype` noch `argtypes` — ctypes nimmt dann
|
||||
32-Bit-`c_int` fuer einen 64-Bit-HANDLE, hin wie zurueck. Die Tuecke: Mit
|
||||
`restype` aendert sich der Fehlerwert von -1 auf 0xFFFFFFFFFFFFFFFF, die
|
||||
alte Pruefung haette stillschweigend aufgehoert zu greifen. Am echten
|
||||
Laufwerk gegengeprueft, Fehlerpfad eingeschlossen.
|
||||
* **Der teure Vor-Scan, den niemand las.** Bei jeder eingelegten Disc lief ein
|
||||
`makemkvcon info` mit 120 s Zeitgrenze — auf einer Blu-ray 20 bis 120
|
||||
Sekunden „Disc wird gelesen". Commander: *„das erkennen der disk dauert sehr
|
||||
sehr lange. Das ging mal viel schneller."* Es ging schneller, weil der Zweig
|
||||
unter Windows NIE lief (`shutil.which`, repariert am 28.08.). Und 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.
|
||||
|
||||
### Drei Notbremsen
|
||||
|
||||
* **Endlosschleife vor jedem Rip.** `_frei_bytes` suchte den naechsten
|
||||
vorhandenen Ordner selbst. `os.path.dirname("Q:\\")` gibt sich SELBST
|
||||
zurueck — an einem freien Laufwerksbuchstaben gemessen, nach drei Runden
|
||||
festgefahren. Ein Arbeitsverzeichnis auf einer abgezogenen Platte haette den
|
||||
Job stumm haengen lassen, in `_platz_pruefen`, also VOR dem Rip.
|
||||
`pfade.naechster_vorhandener` macht es seit V2-1 richtig; es war die ganze
|
||||
Zeit da.
|
||||
* **Laufwerks- und UNC-Wurzeln** bleiben in `naechster_vorhandener` jetzt
|
||||
absolut — dieselbe Falle wie bei den Rohdaten, zweiter Fundort.
|
||||
* **`rmdir /s /q` aus der Registry.** Das Aufraeum-Skript der Deinstallation
|
||||
baut seinen Loeschbefehl aus `InstallLocation`, also aus dem `--ziel` beim
|
||||
Installieren. Waere das eine Laufwerks-Wurzel, loeschte die Deinstallation
|
||||
das Laufwerk; der noetige rstrip macht den Fall erst scharf. Nicht
|
||||
beobachtet — aber nicht wiedergutzumachen.
|
||||
|
||||
### Lesefehler heissen jetzt Lesefehler
|
||||
|
||||
MakeMKV sicherte 1 von 2 Titeln, beendete sich mit 0, und Rippy schrieb „Rip
|
||||
fertig". Dass ein Titel fehlt, stand nur in Zeilen, die niemand liest. Jetzt
|
||||
gibt es eine Warnung nach dem Rip — auch und gerade dann, wenn er als Erfolg
|
||||
endet — mit Abhilfe: reinigen, anderes Laufwerk. Und **die MSG-Nummer steht im
|
||||
Protokoll**: MakeMKVs Texte sind uebersetzt, die Nummern nicht.
|
||||
|
||||
### Zwei Funde aus der Gegenprobe am laufenden Rippy
|
||||
|
||||
Die erste Fassung von rc11 war installiert, als der Commander meldete: *„nun
|
||||
öffnen sich diverse fenster im hintergrund, gehen ganz kurz auf und dann
|
||||
wieder zu. Das laufwerk hört auch einfach auf zu lesen."* Beides waren
|
||||
Altlasten, die erst jetzt sichtbar wurden.
|
||||
|
||||
**Die aufblitzenden Fenster.** Prozesserzeugung 20 Sekunden lang mitgeschnitten:
|
||||
|
||||
```
|
||||
14:54:40 timeout.exe timeout 4 ls -d C:\Users\...\d7ee6c06-...
|
||||
14:54:40 WindowsTerminal.exe
|
||||
```
|
||||
|
||||
`rohdaten.pruefen` fragt „gibt es dieses Verzeichnis?" mit `timeout N ls -d`.
|
||||
Unter Linux ist das genau richtig: `os.path.isdir` kann an einem toten
|
||||
CIFS-Mount im Kernel haengen (Zustand D, 26.07.2026), ein Kind-PROZESS laesst
|
||||
sich abbrechen. Unter Windows ist es dreifach falsch — `timeout.exe` gibt es
|
||||
dort, sie wartet aber nur Sekunden ab und kennt weder `ls` noch `-d`; sie
|
||||
braucht eine Konsole, und die reisst Windows auf; und ihr Rueckgabewert ist
|
||||
nie 0, also lautete die Antwort **„weg" fuer jedes Verzeichnis**. Rohdaten
|
||||
waren unter Windows grundsaetzlich unsichtbar — der zweite, tiefere Grund fuer
|
||||
„Auf der Platte liegt zu diesem Job nichts (mehr)". Neu: `nativ_nachsehen()`,
|
||||
und dieselbe Absicherung fuer die zwei gleichartigen Aufrufe in `mounts.py`.
|
||||
|
||||
**Das Laufwerk hoert auf zu lesen.** Aus dem Protokoll:
|
||||
|
||||
```
|
||||
12:49:52 bluray-Rip gestartet
|
||||
12:50:09 [watcher] Laufwerk G: beantwortet keine Medien-Abfragen mehr
|
||||
12:50:12 MSG 2003 SCSI-Fehler ILLEGAL REQUEST:INVALID FIELD IN CDB
|
||||
12:50:12 MSG 5010 Das Oeffnen der Disk schlug fehl
|
||||
12:50:12 makemkvcon endete mit Code 11
|
||||
```
|
||||
|
||||
Der Waechter fragt alle drei Sekunden `device_info` ab — drei `CreateFileW`
|
||||
plus IOCTLs auf ein Geraet, das waehrenddessen makemkvcon gehoert.
|
||||
`_auto_prescan` haelt sich seit dem 29.08.2026 an die Regel *„Es gibt keinen
|
||||
Grund, waehrend eines Rips zu scannen"*; die Laufwerksabfrage tat es nicht.
|
||||
Jetzt gilt waehrend eines Rips der letzte bekannte Stand — das ist keine
|
||||
Notluege, denn am Laufwerk aendert sich in der Zeit nichts.
|
||||
|
||||
Sichtbar wurden beide erst durch die neue Protokollzeile aus demselben
|
||||
Rundgang. Ein Fehler, der sich meldet, sieht aus wie ein neuer.
|
||||
|
||||
### Geprueft, nichts gefunden
|
||||
|
||||
FLAC samt MusicBrainz-Tags mit Umlauten (landen korrekt als UTF-8) · die
|
||||
makemkvcon-Aufrufe in `schluessel.py` (haben `errors="replace"` bereits) · die
|
||||
Laufwerksliste in `main.py` (rstrip nur fuer den Anzeigenamen) · die uebrigen
|
||||
`dirname`-Schleifen (es gab nur die eine) · `unbekanntes_preset` (Wortlaut und
|
||||
Rueckgabewert 2 gemessen — korrekt).
|
||||
|
||||
### Zwei Tests, die gelogen haben
|
||||
|
||||
* `assert "--audio-codec" in cmd` — schrieb den Fehler fest, statt ihn zu
|
||||
finden.
|
||||
* `lambda: {}` als Doppelgaenger fuer
|
||||
`get_settings(key="ui", bei_fehler_leer=False)` — brach, sobald ein Aufrufer
|
||||
einen Parameter benutzte, und zeigte dann auf den Code statt auf sich
|
||||
selbst. Ein Doppelgaenger muss die Schnittstelle abbilden, die er ersetzt,
|
||||
nicht nur den einen Aufruf, den es gerade gibt.
|
||||
|
||||
---
|
||||
|
||||
## v4.0-rc10 — makemkvcon ueberlebt Rippy nicht mehr (29.08.2026)
|
||||
|
||||
> **Ein liegengebliebener `makemkvcon` haelt das Laufwerk fest — jeder spaetere
|
||||
> Rip scheitert dann.** 954 Tests gruen.
|
||||
|
||||
Reference in New Issue
Block a user