Files
rippy/SAVEPOINT.md
T
Hitonabi 1eb1c91dd8
Ampel / ampel (push) Successful in 28s
docs(savepoint): v3.15 - Aufraeum-Runde mit gemessenen Zahlen
Fuenf Vorgaben, was daraus wurde:

Codebase sauber   -> vier tote Routen + zwei Module weg (main.py 1726 -> 1682
                     Zeilen), Tests halten sie draussen
UI schnell        -> /capabilities 1,010 s -> 0,003 s; Dashboard-Aufbau
                     1,03 s -> 0,028 s. Alle 15 Ladeendpunkte unter 10 ms
UI selbsterklaerend -> Wizard-Kasten "Was Rippy gerade sieht" mit
                     Handlungsanweisung je Problem, sichtbare Keys mit Pruefung
Idiotensicher     -> Wizard empfiehlt nach GEMESSENER CPU statt H.265 blind;
                     4K-Falle ist damit zu
Externe Worker    -> auf Windows gegengeprueft (Kerne/Modell korrekt, encoders
                     ohne HandBrake korrekt leer, SIMD ehrlich "unbekannt")

Dazu die offene 4K-Frage aus v3.14 beantwortet: Kompression ist je Disc-Typ
abwaehlbar, 4K kann verlustfrei bleiben waehrend DVD/Blu-ray weiter schrumpfen.

ARM-Vergleich drin: fast alles, was ARM automatisch macht, macht Rippy schon -
und meist gruendlicher. Zwei echte Luecken benannt und NICHT gebaut
(ISO-Sicherung fuer Datentraeger, mehrere Laufwerke gleichzeitig), weil das
Funktionen sind und keine Reparatur. Ehrlich dabei: ob Rippy parallel rippen
kann, ist unbewiesen - mit einem Laufwerk nicht testbar.

Offene Punkte stehen unter "NOCH OFFEN", darunter der VM-CPU-Typ (qemu64 statt
host - AVX2 waere ein VM-Neustart und 2-4x schneller).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 21:51:23 +02:00

57 KiB
Raw Blame History

SAVEPOINT — Rippy

Aktueller Stand: v3.15 — Aufräum-Runde: Tempo, tote Routen, 4K-Entscheidung (25.07.2026, 20:00)

Deployt und live gegengeprüft. Auftrag war „bau alles so um, dass es sauber ist" mit fünf Vorgaben: saubere Codebase, selbsterklärendes UI, externe Worker, kein Laggen, idiotensicher. Was noch offen ist, steht unten.

Das „Laggen" hatte genau eine Ursache — gemessen und behoben

Über alle 15 Endpunkte gemessen, die das UI beim Laden braucht:

Endpunkt vorher nachher
/capabilities 1,010 s 0,003 s
/system/updates 0,491 s unverändert (hängt am Knopf)
/metadata/status 0,412 s unverändert (hängt am Knopf)
die anderen 12 < 0,025 s < 0,010 s

Nur /capabilities schlug beim Seitenaufbau zu — und fünf Stellen holen ihn (Dashboard, Einstellungen, Worker-Tab, Wizard, Rip-Dialog). Jede Seite zahlte eine Sekunde. Ursache ist kein Fehler, sondern das Wesen des Celery-Pings: er sammelt Antworten bis zum Timeout und kann nicht früher aufhören. Den Timeout zu kürzen würde Antworten langsamer Remote-Worker verschlucken — also genau die Maschinen, um die es beim externen Encoding geht. Jetzt pingt eine Hintergrund-Schleife im 5-s-Takt, der Endpunkt liest ab. Ist der Vorrat älter als 30 s, wird einmal synchron gepingt: lieber langsam als falsch („alles offline", obwohl alles läuft). Kompletter Dashboard-Aufbau: 1,03 s → 0,028 s.

4K ist entschieden — Kompression je Disc-Typ abwählbar

Die offene Frage aus v3.14 ist gebaut. Bisher gab es nur transcodeEnabled: alles oder nichts. Jetzt kann das Preset eines Disc-Typs auf den Reservewert keine stehen → die verlustfreie Datei bleibt stehen. Damit ist die sinnvolle Einstellung für diese Maschine erstmals möglich: 4K verlustfrei behalten, DVD und Blu-ray weiter schrumpfen.

komprimieren_fuer() ist die neue reine Funktion; preset_fuer() überspringt den Reservewert bewusst und gibt ihn NIE als Preset-Namen zurück — sonst bekäme HandBrake --preset keine. Gegengeprüft: keiner der 90 echten Presets aus --preset-list heißt so, und die fünf im UI angebotenen Namen existieren alle.

Der Wizard läuft nicht mehr in die 4K-Falle

Er hatte die Zahlen längst vorliegen (/capabilities meldet cpu_simd und cpu_kerne) — benutzt hat er sie nicht und H.265 als Standard vorgeschlagen. Auf einer CPU ohne AVX2 sind das ein bis zwei Tage pro 4K-Film.

Jetzt entscheidet die gemessene Leistung: schwache CPU → 4K nicht komprimieren, Blu-ray/DVD auf H.264. Stark oder Hardware-Encoder → H.265 durchgehend. Und er schreibt alle vier Preset-Felder statt nur des allgemeinen; vorher fiel 4K auf ein 1080p-Preset zurück.

Dazu gehärtet: Kasten „Was Rippy gerade sieht" (Laufwerk, Worker mit Kernen/SIMD, freier Platz) — jede Zeile mit Handlungsanweisung statt nur einem Kreuz. Lädt parallel und wiederholt im 5-s-Takt, weil der Worker beim ersten Start noch hochläuft. API-Keys sind sichtbar statt Punkte (kopierte Keys, keine Passwörter — Tippfehler sieht man in Punkten nicht) und werden direkt nach dem Speichern geprüft, mit der Wahl „Key korrigieren" oder „Trotzdem fertigstellen".

Gewarnt wird nur, wenn es belegt ist: schwacheEncoderCpu() verlangt mindestens einen Worker mit BEKANNTER SIMD-Stufe und keinen mit Hardware-Encoder. Ein Windows-Worker meldet unbekannt (kein /proc/cpuinfo) → dann wird geschwiegen statt falsch gewarnt.

Vier tote Routen raus

POST /prescan, POST /jellyfin/format (+ nfo_generator.py und image_downloader.py, die sonst nichts nutzte), GET /stream/jobs, GET /worker-setup/windows-gui. Jede ein Überrest eines ersetzten Entwurfs, keine mit Aufrufer. main.py: 1726 → 1682 Zeilen, dazu 279 Zeilen in zwei gelöschten Modulen. Tests halten beide Seiten fest: die vier müssen WEG bleiben, die drei für die Worker-Installation (/worker-setup/paket, /windows, /windows-exe) müssen DA sein.

Externe Worker — vorbereitet, vom Commander zu testen

Auf Windows gegengeprüft (dieser PC): cpu_kerne 16 und CPU-Modell kommen korrekt durch, encoders ist ohne installiertes HandBrake korrekt leer, cpu_simd ehrlich unbekannt → keine falsche Warnung. Ein GPU-Worker braucht ein HandBrake-Build mit nvenc_*/qsv_*; die neue Anzeige nennt die ungefilterte HandBrake-Auskunft, damit das nachprüfbar ist.

ARM-Vergleich (Vorbild-Projekt)

Fast alles, was ARM automatisch macht, macht Rippy schon — und meist gründlicher: Metadaten aus drei Quellen statt nur OMDb, Episoden-Erkennung per Laufzeitabgleich, Kompression auf eine eigene Queue routbar. Bezeichnend: ARMs Auto-Auswurf war bei Rippy nur behauptet und ist erst in v3.14 echt geworden. Zwei Lücken bleiben, beide nicht gebaut, als Vorschlag:

  • ISO-Sicherung für Datenträger, die weder Film noch Audio-CD sind — daran scheitert Rippy heute.
  • Mehrere Laufwerke gleichzeitig. Rippy sieht sie (/dev/sr[0-9]*), ob parallel gerippt wird, ist unbewiesen — mit einem Laufwerk nicht testbar.

NOCH OFFEN

  1. Externe Worker im Praxistest (Commander).
  2. ISO-Sicherung und Mehr-Laufwerk-Betrieb — siehe ARM-Vergleich.
  3. „Job aus der Liste entfernen" sagt nicht, wie viel daneben liegen bleibt (v3.14). Der Rohschnitt verwaist dabei unsichtbar.
  4. Discord-Webhook und MakeMKV-Key liegen im Klartext in der settings-Tabelle. Für Heimnetz erwartbar, aber der Webhook ist eine echte Zugangsberechtigung.
  5. Der VM-CPU-Typ steht auf qemu64. host würde AVX2 freischalten und jeden Software-Encode 24× beschleunigen. Ein VM-Neustart, nicht gemacht.

Vorheriger Stand: v3.14 — Durchsicht: vier Placebos und ein unwirksamer Schutz (25.07.2026, 18:40)

Stand 19:30: alles committet, Ampel grün (a1aabd5), DEPLOYT und live gegengeprüft. Der Akira-Encode wurde um 18:31 abgebrochen (Nachtrag im Block darunter), danach der Job aus der Liste gelöscht und die Rohdaten entsorgt. Es läuft nichts, die Platte hat 111 GB frei. Was noch zu entscheiden ist, steht unten unter „NOCH OFFEN".

DER WICHTIGSTE FUND: der Platten-Schutz aus c065967 greift nicht

_original_aufheben() entschied per os.stat().st_dev, ob umgehängt oder kopiert werden muss. Auf der VM gemessen — beides gleichzeitig wahr:

st_dev /app/temp  = 2050
st_dev /app/media = 2050        → identisch
os.rename(...)    → EXDEV, "Invalid cross-device link"

Der Kernel vergleicht bei rename() den Mount, nicht das Gerät. /app/temp (Docker-Volume) und /app/media (Bind-Mount) sind zwei Mounts DERSELBEN ext4-Partition. Die Prüfung sah deshalb „gleiches Dateisystem", übersprang die Platzprüfung, und shutil.move kopierte doch — 75 GB bei 37 GB frei. Der Schutz hätte genau den Schaden zugelassen, gegen den er gebaut wurde.

Behoben: os.rename wird jetzt VERSUCHT statt vorhergesagt. Klappt es, ist es umgehängt. Kommt EXDEV, steht die Kopie fest — und erst dann wird der Platz geprüft. Vier Tests dazu (test_original_aufheben.py), inklusive des Falls, der die Platte füllte.

GEMESSEN, Stand 18:30 (alles per Befehl auf der VM geprüft)

  • Container: alle 5 Up, api/postgres/redis healthy. api/ui/worker seit 18:00:39 (Deploy von 8bb075c), postgres/redis älter.
  • Platte: 148 G, 105 G belegt, 37 G frei = 75 G Rohschnitt + 4,9 G Medien (Evangelion) + ~25 G System. Keine Kopier-Reste, kein original-Ordner.
  • Akira-Job: failed, „Abgebrochen durch Nutzer" (Abbruch 18:30:17 angefordert, Worker bestätigt 18:33:38 → 3,4 Minuten Verzug, siehe unten). Es läuft kein HandBrake mehr (per /proc geprüft, Stand 19:06).
  • Der Encode war bei 1,44 %, gemessen an der Leseposition im Quellstrom (/proc/<pid>/fdinfo/3: 1.145.940.149 von 79.604.951.639 Bytes) — exakter als jede Fortschrittsanzeige. Zwei Tempo-Fenster ergaben 395 und 784 KB/s → 2855 h für den Film, bei 3,84 von 4 gesättigten Kernen.
  • Achtung bei Prozess-Suchen per /proc: Ein case "$c" in *HandBrake*) trifft die eigene Shell mit, weil deren Kommandozeile das Wort enthält. Zwei Fehlalarme in dieser Sitzung. Immer die PID gegenprüfen.
  • Die Ursache dafür ist neu und behebbar: Die VM läuft auf dem generischen QEMU-CPU-Modell (QEMU Virtual CPU version 2.5+), grep -c avx2 /proc/cpuinfo = 0, nur bis sse4_2. x265 lebt von AVX2. Abhilfe: CPU-Typ von VM 106 in Proxmox auf host stellen (braucht VM-Neustart).
  • HandBrake im Worker-Image kann: svt_av1, x264, x265 (je 10/12-bit), mpeg4/2, VP8/9, theora — und keinen einzigen Hardware-Encoder.
  • Ampel grün für b526a0a und 8bb075c (Gitea-API abgefragt).

GEBAUT — vier Placebos entfernt bzw. echt gemacht

  1. Fortschritt log statt Wahrheit. get_progress_from_line matchte jede Zahl vor einem % — also auch HandBrakes Scan-Durchlauf, der VOR dem Encodieren bis 100 % hochläuft. Dazu warf if progress > 0 alle echten Werte unter 1,00 % weg. Ergebnis: Anzeige klebte stundenlang auf 99 %. Jetzt wird nur die Encoding:-Zeile gelesen, task N of M mitgerechnet, und -1 heißt „keine Angabe" (Muster von get_progress_from_prgv). Formatstrings aus dem Binary gelesen, nicht geraten. ⚠️ Richtigstellung zu v3.13: Dort steht, progress=99 sei ein „Altwert aus dem Absturz". Falsch — beide Startpfade setzen auf 0, der Wert war frisch vom Scan-Durchlauf geschrieben. Es war ein Bug, kein Überrest.
  2. „Automatischer Auswurf" tat nichts. Die Einstellung (Standard: ein) wurde von niemandem gelesen: DVD/Blu-ray warfen nie aus, Audio-CDs immer, weil abcde -x fest verdrahtet bekam. Jetzt entscheidet die Einstellung beides (wirf_disc_aus() per CDROMEJECT-ioctl, Linux-guarded).
  3. „Alle Tracks rippen" konnte nichts bewirken — abcde bekommt keine Track-Auswahl und es gibt keine Oberfläche dafür. Schalter entfernt, an seiner Stelle steht jetzt die Wahrheit („wird immer vollständig gerippt").
  4. Encoder-Auslese behauptete statt zu messen. cpu-x264/cpu-x265 standen fest verdrahtet drin („immer dabei") — ein Rip-Worker ohne HandBrake behauptete damit, komprimieren zu können. Und vaapi wurde allein wegen /dev/dri gemeldet, ohne zu prüfen, ob HandBrake das kann (dieses Image kann es nicht). Jetzt aus HandBrakeCLI --help geparst, plus CPU-Modell, Kernzahl und Vektorbefehlsstufe je Worker — mit sichtbarer Warnung im UI, wenn AVX2 fehlt. Genau die Angabe, deren Fehlen den 55-Stunden-Encode unsichtbar machte.

GEBAUT — Zombie-Erkennung (v3.13 Punkt 2 abgearbeitet)

zombies.py: Beim Worker-Start werden Jobs, die auf ripping/transcoding/ canceling stehen, gegen Celerys active/reserved/scheduled gehalten und ehrlich auf failed gesetzt, wenn niemand daran arbeitet. Drei Sicherungen, weil ein falsch getöteter Job teurer ist als eine stehende Leiche:

  • nur beim Start (da ist „es lief nichts" eindeutig),
  • 120 s Gnadenfrist (Celery stellt unbestätigte Aufgaben erneut zu),
  • Vollzähligkeit: antworten weniger Knoten als laut Herzschlag online sind, wird NICHTS gewertet — sonst wäre der laufende Job eines beschäftigten Remote-Workers eine falsche Leiche.

13 Tests, unter anderem: „laufender Job wird nicht angetastet" und „schweigender Worker verhindert jedes Urteil".

GEBAUT — Pfad-Prüfung gehärtet

Elf Stellen prüften mit nacktem startswith(MEDIA_ROOT). /app/media-boese/x beginnt mit /app/media, liegt aber außerhalb — betroffen waren auch /browse und /browse/mkdir, wo der Pfad vom Nutzer kommt. Neuer Zwillings-Helfer unter_wurzel() in api/main.py und worker/tasks.py, alle elf Stellen umgestellt, Tests in beiden.

Nebenbefund dabei: _zielbasis() benutzte os.path.normpath — unter Windows werden daraus Backslashes und die Prüfung greift nicht mehr; das gewählte Ziel wäre still auf den Standard zurückgefallen. Genau die Falle, die _arbeitsverzeichnis() drei Zeilen weiter dokumentiert und mit posixpath vermeidet. Live war es nie (nur aus rip_disc, das auf Windows verriegelt ist), jetzt konsistent.

GEFUNDEN, NICHT ANGETASTET — vier tote Endpunkte

Nirgends im UI referenziert (mechanisch gegengeprüft: alle api.*-Aufrufe gegen alle Routen):

Endpunkt Lage
POST /jellyfin/format ersetzt durch medien.py im Worker; zieht nfo_generator.py + image_downloader.py in der API mit, die sonst niemand nutzt
POST /prescan ohne Aufrufer (die PreScan-Klasse selbst wird woanders sehr wohl gebraucht)
GET /stream/jobs niemand konsumiert ihn; der Kommentar behauptet einen „Fix 23.07.", der ihn funktionsfähig machte — gebraucht wird er trotzdem nicht
GET /worker-setup/windows-gui seit der .exe (v3.9) unreferenziert

Bewusst nicht entfernt: test_api_smoke.py prüft /prescan als verdrahtete Route, und Entfernen ist eine Entscheidung, keine Reparatur. Der Commander entscheidet.

NOCH OFFEN / EHRLICH UNGEKLÄRT

  • Was die Platte am 25.07. mittags füllte, ist nicht belegt. Es gibt keine Kopier-Reste, kein original-Verzeichnis und keinen einzigen Log-Eintrag von _original_aufheben — das loggt in beiden Zweigen. Zwischen 10:21:50 und 17:49 steht überhaupt nichts im Log. Der EXDEV-Mechanismus ist jetzt belegt (siehe oben), dass er DAMALS zuschlug, ist es nicht.
  • tracks:/dev/sr0 meldet {"status":"done","tracks":[]} — null Titel für eine Disc, die MakeMKV mit TCOUNT:5 öffnet. Nicht weiter verfolgt.
  • Der Discord-Webhook und der MakeMKV-Key liegen im Klartext in der settings-Tabelle. Für Heimnetz erwartbar, aber der Webhook ist eine echte Zugangsberechtigung.
  • Zwei neue API-Tests laufen nur in der Ampeltest_api_smoke.py überspringt sich unter Windows selbst (main.py braucht fcntl).

GEBAUT — „Abbrechen" wirkt jetzt sofort

Der Fund aus dem Nachtrag (Commander, am laufenden Job beobachtet) ist behoben. Ursache war genau wie dort beschrieben: Der Abbruch wurde nur in datei_fortschritt geprüft, und diese Closure stieg oben sofort wieder aus, wenn sich die Prozentzahl nicht geändert hatte. Bei einem Prozent je halber Stunde sah „Abbrechen" entsprechend lange wirkungslos aus.

Jetzt gibt es in run_handbrake einen eigenen Abbruch-Kanal neben dem Fortschritts-Callback — dasselbe Muster, das run_makemkv schon für log_cb benutzt, und aus demselben Grund. Er wird bei JEDER Ausgabezeile aufgerufen und ist im Worker auf 5 Sekunden gedrosselt (ABBRUCH_INTERVALL_SEKUNDEN). Nebeneffekt: Er greift auch während des Scan-Durchlaufs, der überhaupt keine Encode-Prozente liefert — dort war ein Abbruch vorher grundsätzlich unmöglich. Die Leseschleife ist als _handbrake_schleife() herausgezogen, damit die Reihenfolge (Abbruch VOR Fortschritt) ohne echtes HandBrake testbar ist.

ERLEDIGT UM 19:30 — deployt und live gegengeprüft

a1aabd5 läuft auf der VM. Belege, nicht Behauptungen:

  • Container: alle 5 up, api/postgres/redis healthy.
  • Die neue Auslese antwortet ehrlich (GET /capabilities): cpu-x264, cpu-x265, cpu-av1kein Phantom-vaapi mehr, obwohl die alte Fassung es bei vorhandenem /dev/dri gemeldet hätte. Dazu cpu_modell: QEMU Virtual CPU version 2.5+, cpu_kerne: 4, cpu_simd: sse4_2 → die AVX2-Warnung im UI greift.
  • Zombie-Erkennung: 19:26:09, exakt 120 s nach Worker-Start, {'geprueft': 0, 'aufgeraeumt': []} — nachgesehen, nichts in Arbeit gefunden, korrekt nichts angetastet.
  • 604 Disc-Schlüssel haben den Rebuild überlebt.
  • Aufgeräumt: das unbrauchbare 4K-Fragment (67.633.152 B) gelöscht; der Akira-Job wurde um 19:07 aus der Liste entfernt, wodurch der 75-GB-Rohschnitt verwaiste (kein Job-Datensatz zeigte mehr darauf, im UI unsichtbar, „Neu komprimieren" unmöglich) — auf Entscheid des Commanders gelöscht, samt vier leerer Alt-Ordner. Platte: 37 GB → 111 GB frei.

⚠️ Merken für die Job-Verwaltung: „Job aus der Liste entfernen" löscht bewusst keine Dateien — das ist richtig, macht die Rohdaten aber unerreichbar, weil beides nur über die Job-ID verbunden ist. Ein Rohschnitt ohne Job-Zeile taucht nirgends mehr auf. Das UI sollte beim Löschen sagen, wie viel daneben liegen bleibt. Nicht gebaut.

NOCH OFFEN

1. Die UHD-Strategie (aus dem Nachtrag, unverändert gültig). Preset-je-Disc-Typ war richtig, reicht aber nicht: Software-HEVC in 4K ist auf dieser CPU keine Option. Drei Wege, keiner davon gebaut:

  • UHD gar nicht komprimieren — Roh-MKV behalten. Ehrlichste Variante, kostet Platz (75100 GB je Film, gehört dann auf die NAS).
  • Hardware-Encoder — Remote-Worker mit GPU (nvenc/vaapi). Rippy kann das schon routen (bewiesen v3.7); es fehlt die Maschine. Achtung: Das Worker-Image kann selbst keinen Hardware-Encoder, ein GPU-Worker braucht ein HandBrake-Build mit nvenc_*/qsv_* — die neue Anzeige sagt das jetzt.
  • CPU-Typ der VM auf host — schaltet AVX2 frei, bringt bei x265 typisch Faktor 24. Aus 2855 h werden damit aber immer noch Stunden bis Tage; das allein löst 4K nicht, hilft aber jedem 1080p-Encode.

2. Vier tote Endpunkte — entfernen oder behalten (Tabelle oben). Alle vier sind Überreste eines ersetzten Entwurfs: /jellyfin/format (die Arbeit macht seit v3.2 medien.py im Worker), /prescan (Metadaten-Seite ist seit v3.4 weg; die PreScan-Klasse selbst wird sehr wohl gebraucht), /stream/jobs (kein EventSource im UI — das Dashboard pollt setInterval(…, 4000)) und /worker-setup/windows-gui (seit der .exe in v3.9 ohne Aufrufer; die Datei steckt weiter in der .exe). /stream/jobs ist der unangenehmste: keine harmlose Leiche, sondern eine Endlosschleife je Verbindung, die jeder erreichen kann. Empfehlung: alle vier raus, plus nfo_generator.py und image_downloader.py in der API (nutzt sonst nichts) — und test_api_smoke.py prüft /prescan als verdrahtete Route, der Test muss also mit.

3. Akira liegt jetzt gar nicht mehr vor. Fragment und Rohschnitt sind gelöscht (siehe oben). Wer den Film will, legt die Disc neu ein — der Schlüsselspeicher steht (604 Schlüssel), der Rip dauert rund eine Stunde. Vor dem nächsten UHD-Versuch aber Punkt 1 entscheiden, sonst läuft dasselbe 50-Stunden-Rennen wieder an.


Vorheriger Stand: v3.13 — ÜBERGABE (25.07.2026, 18:05)

Dieser Block ist eine Übergabe an die nächste Sitzung. Er trennt bewusst gemessen von vermutet — in dieser Sitzung wurden zwei Behauptungen aufgestellt, die sich als falsch erwiesen (siehe „Vertrauensnotiz" unten). Im Zweifel: selbst nachmessen, nicht diesem Dokument glauben.

GEMESSEN, Stand 18:05 (alles per Befehl auf der VM geprüft)

  • Container: api/postgres/redis/ui/worker alle Up, api + postgres + redis healthy.
  • Platte: /dev/sda2 148 G, 105 G belegt, 37 G frei (75 %).
  • Läuft gerade: HandBrakeCLI --input /app/temp/raw/73b89777-…/title_t00.mkv --output "/app/media/movies/Akira (1988)/title_t00.mkv" --preset "H.265 MKV 2160p60 4K" --all-audio --all-subtitles
  • Job 73b89777-a808-426f-8159-d406d27d0ec8: status='transcoding', progress=99 (⚠️ Altwert aus dem Absturz, NICHT der echte Fortschritt — der Lauf hat um 18:02 begonnen), disc_type='uhd', output_path='/app/media/movies/Akira (1988)'.
  • Dateien: …/Akira (1988)/title_t00.mkv ist 0 Bytes (HandBrake hat die vorherige 1080p-Fassung beim Start gekürzt und schreibt gerade neu). Roh-Rip /app/temp/raw/73b89777-…/ = 75 GB, unversehrt.
  • Git: 8bb075c ist HEAD und deployt. Davor c065967, e84afc1, f449c4e, 0935766. Alle Ampeln waren grün.
  • Schlüsselspeicher: 604 Disc-Schlüssel, /system/keystore antwortet.
  • NAS: //192.168.178.62/rippy auf /srv/rippy/media/rippy gemountet, erreichbar, 2311 GB frei. Wird als Ablageziel gelistet.

NACHTRAG 18:35 — der 4K-Lauf wurde abgebrochen, zwei neue Befunde

  • 4K-HEVC ist auf dieser CPU nicht machbar. Gemessen: von 18:02 bis 18:31 kam der Lauf von 0 auf 1 % → hochgerechnet ~50 Stunden für den Film. Der Commander hat um 18:31 abgebrochen. Konsequenz, die noch zu entscheiden ist: entweder UHD gar nicht komprimieren (Roh-MKV behalten, transcodeEnabled für UHD aus), oder ein Hardware-Encoder (Remote-Worker mit GPU, nvenc/vaapi), oder bewusst bei 1080p bleiben. Das eben gebaute Preset-je-Disc-Typ ist damit richtig, aber allein nicht genug.
  • Abbruch wirkt verzögert (FEHLER, nicht gebaut). Job steht seit 18:31 auf canceling, HandBrake lief um 18:35 immer noch. Ursache: Der Abbruch wird nur in datei_fortschritt geprüft, und diese Closure läuft nur, wenn sich die PROZENTZAHL ändert (tasks.py, if gesamt == letzter[0]: return). Bei 1 % alle 30 Minuten heißt das: bis zu eine halbe Stunde, in der „Abbrechen" für den Nutzer wirkungslos aussieht. Der Abbruch muss unabhängig vom Fortschritt geprüft werden (z. B. zeitgesteuert beim Lesen der HandBrake-Ausgabe).
  • Die 1080p-Fassung ist weg. HandBrake hat sie beim Start auf 0 Bytes gekürzt; aktuell liegen dort ~64 MB 4K-Fragment. Der 75-GB-Rohschnitt ist unversehrt — es ist also nichts unwiederbringlich verloren, aber im Akira-Ordner liegt gerade eine unbrauchbare Datei.

WAS ALS NÄCHSTES ANSTEHT

  1. Ausgang des 4K-Laufs prüfen. Erwartung, die noch NICHT bewiesen ist: Der Job endet erfolgreich, und „Original behalten" (Setting steht auf true) meldet nur eine Warnung, weil 75 GB nicht neben 37 GB freien Platz passen. Genau dieser Pfad ist neu (_original_aufheben in tasks.py) und in der Praxis noch nie gelaufen. Wenn er hält, ist der Ausfall von heute Mittag strukturell behoben.
  2. Zombie-Erkennung fehlt (gefunden, NICHT gebaut). Nach dem Absturz stand der Job auf transcoding 96 %, obwohl weder ein Prozess lief noch etwas in den Celery-Queues stand (beide llen = 0). Folge: keine Anzeige, kein Download, und der „Neu komprimieren"-Knopf fehlt, weil _kann_neu_komprimieren (main.py) status == "failed" verlangt. Der Worker sollte beim Start solche Leichen erkennen und ehrlich auf failed setzen.
  3. keepOriginal steht auf true — von dieser Sitzung gesetzt, um den 4K-Rohschnitt zu retten. Bewusst entscheiden, ob das so bleibt: mit Arbeitsverzeichnis auf der NAS-Freigabe wäre es ein reines Umhängen statt einer Vollkopie.
  4. 75 GB Rohschnitt liegen weiter in /app/temp/raw/73b89777-…. Nach erfolgreichem 4K-Lauf entscheiden, ob er weg kann.

VERTRAUENSNOTIZ — zwei Fehlschlüsse dieser Sitzung

  • „MakeMKVs Schlüssel-Kanal ist abgeschaltet" — falsch. Stützte sich auf zwei tote Hostnamen aus alten Forumsbeiträgen, die MakeMKV längst nicht mehr benutzt. Richtig: makemkvcon unter Linux fragt nie, die Windows-Version schon. Korrigiert in v3.11.
  • „Celery hat die Aufgabe nach dem Deploy erneut zugestellt" — falsch. Aus dem Status transcoding geraten, ohne Prozesse oder Queues zu prüfen. Es war ein Zombie-Eintrag (Punkt 2 oben).

Beide Male war das Muster dasselbe: aus einem Zustandswert auf einen Mechanismus geschlossen, statt den Mechanismus zu messen.


Vorheriger Stand: v3.12 — Preset je Disc-Typ (25.07.2026)

  • Der Fund: Beim ersten echten UHD-Rip aufgefallen — die Kompression fragte den Disc-Typ gar nicht: preset = einstellungen.get( "transcodePreset"), ein globales Preset für alles. Live eingestellt war HQ 1080p30 Surround. Der laufende Akira-Rip wäre also verlustfrei in 4K gerippt und danach auf 1080p heruntergerechnet worden — und mit keepOriginal: False wäre der 4K-Rohschnitt anschließend gelöscht worden. Aufgefallen ist es nur, weil der Commander gefragt hat, ob Rippy das UHD-Preset automatisch nimmt.
  • Sofortmaßnahme am laufenden Job: keepOriginal auf True gesetzt (nur dieses eine Feld, gegengeprüft: kein anderer Schlüssel verändert). Damit überlebt der 4K-Rohschnitt die Kompression auf jeden Fall.
  • Gebaut: preset_fuer(disc_type, einstellungen) in ripping.py (pure, getestet) plus drei Einstellungen transcodePresetDvd / …Bluray / …Uhd. Reihenfolge: Preset des Disc-Typs → allgemeines transcodePresetDEFAULT_HB_PRESET. Bestandsinstallationen ändern ihr Verhalten nicht, solange die neuen Felder nicht gespeichert sind. transcode_files holt den Disc-Typ aus dem Job-Datensatz und schreibt ihn mit ins Log.
  • UI (Einstellungen → Verarbeitung): drei Auswahlfelder statt einem, mit Klartext dazu, warum 4K auf ein 2160p-Preset gehört. Alle Preset-Namen stammen aus HandBrakeCLI --preset-list im Worker-Image (1.6.1) — nicht geraten (AGENTS Regel D).
  • ⚠️ Deploy bewusst zurückgehalten: docker compose up -d --build würde den Worker-Container neu erstellen und den laufenden Akira-Rip abbrechen. Erst deployen, wenn der Job durch ist. Der 4K-Rohschnitt ist durch keepOriginal geschützt; danach reicht „Neu komprimieren" im UI, um mit dem richtigen Preset in 4K zu komprimieren.
  • Nebenbefund: ps gibt es im Worker-Image nicht (python-slim). Frühere Prüfungen auf laufende Rips per ps | grep lieferten deshalb still „nichts aktiv" — richtig geht es über /proc.

Vorheriger Stand: v3.11 — 4K-UHD GELÖST: Akira geht auf (25.07.2026)

  • 🎉 Der Durchbruch: Nach Übernahme des Schlüsselspeichers öffnet makemkvcon auf der VM die Akira-UHD: „Operation successfully completed", TCOUNT:5, fünf Titel — identisch zum Windows-Ergebnis. Erster belegter UHD-Disc-Zugriff auf der Rippy-Maschine.

  • Die echte Ursache — und sie ist eine andere als in v3.10: makemkvcon unter Linux ruft Disc-Schlüssel nie ab. Die Windows-Version tut es. Gegenprobe mit demselben Laufwerk und derselben Disc:

    Linux (Worker) Windows
    Verbindungen beim Disc-Öffnen keine einzige 185.84.108.20:443
    Meldung 3338 „Downloading latest HK" nie ja
    _private_data.tar 2048 B, 0 Schlüssel 6,4 MB, 604 Schlüssel
    Disc „volume key is unknown" geht auf

    Gegengeprüft mit leerem UND gefülltem Speicher, mit und ohne --noscan, mit dev:/dev/sr0 und disc:0, mit gelöschter update.conf. Immer: keine Verbindung. Die Meldungsvorlage „Downloading latest %1 to %2 ..." steckt sehr wohl im Linux-Binary — sie löst nur nie aus. Gleiches Symptom im MakeMKV-Forum, seit Jahren offen (t=25782, t=34022).

  • ⚠️ Richtigstellung zu v3.10 (direkt darunter): Dort steht, MakeMKVs Schlüssel-Kanal sei abgeschaltet. Das war falsch. Die Herleitung stützte sich auf zwei Hostnamen aus alten Forumsbeiträgen (hkdata.fairuse.org, hkdata.crabdance.com), die tatsächlich nicht mehr auflösen — MakeMKV benutzt sie aber längst nicht mehr. Der Dienst lebt, der Worker erreicht ihn sogar (Verbindungstest auf 185.84.108.20:443 erfolgreich); er wird unter Linux nur nie gefragt. Aufgedeckt hat das der Commander mit dem Einwand, unter Windows ginge es sofort. Alles in v3.10 Gebaute bleibt richtig und nötig — nur die KEYDB.cfg ist nicht der Haupt-, sondern der Ersatzweg.

  • Gebaut — Schlüsselspeicher übernehmbar: GET/POST /system/keystore plus die Helfer in makemkv_daten.py (beide Zwillinge). Der Rohkörper der Anfrage IST die Datei — binär, deshalb kein JSON und kein Base64. Die Prüfung lehnt einen Speicher ohne hkd_*.bin ab, sonst lädt jemand den leeren Vorrat einer frischen Installation hoch und wundert sich, dass nichts passiert.

  • Gebaut — UI: neuer Block „Disc-Schlüssel für 4K-UHD" über dem KEYDB-Block, mit Schlüssel-Anzahl, Upload und der Schritt-für-Schritt- Anleitung für den Windows-Umweg. KEYDB.cfg ist jetzt als Notnagel beschriftet. Die Worker-Plakette zeigt die Anzahl bekannter Schlüssel; 0 heißt sichtbar „4K-UHD scheitert".

  • Gebaut — ehrliche Texte: Der UHD-Fehlertext nennt jetzt den Windows-Weg und die Anzahl der Schlüssel dieses Workers. Alle Falschaussagen korrigiert: UI (3 Stellen), Anleitung (2), README (3), KONZEPT §8 + §10, Modulkopf von makemkv_daten.py, Worker-Dockerfile und makemkv_key.py (dort stand: „Den AACS-Schlüssel zieht MakeMKV via LibreDrive ohnehin selbst aus dem Laufwerk" — gilt für Blu-ray, NICHT für UHD).

  • So hältst du den Vorrat aktuell: Laufwerk an den Windows-PC, Disc in MakeMKV öffnen, dann _private_data.tar aus dem MakeMKV-Datenverzeichnis (Preferences → General) unter Einstellungen → System hochladen. Der Speicher der Windows-Installation liegt bereits auf der VM unter /srv/rippy/makemkv/.

  • Offen: Der volle UHD-Rip inklusive Transcode-E2E ist noch nicht durch — belegt ist der Disc-Zugriff, nicht die komplette Kette bis zur fertigen Datei. Und ob sich der Abruf unter Linux doch anstoßen lässt, ist ungeklärt; der Code dafür ist im Binary vorhanden.


Vorheriger Stand: v3.10 — 4K-UHD: KEYDB.cfg statt Warten auf MakeMKV (25.07.2026)

  • Der Befund (am 25.07. live auf der VM im Worker-Container nachgemessen — kein Verdacht, alles belegt): Eine 4K-UHD (Akira, MKB v76, Pressung Dezember 2020) scheitert mit „The volume key is unknown for this disc". Laufwerk und MakeMKV sind dabei in Ordnung: makemkvcon meldet „Using LibreDrive mode (v06.3)" und „Using direct disc access mode", liest die Disc und legt den AACS-Dump ab (Meldung 3332). Der Fehler liegt also NICHT an der Hardware.
  • Die echte Ursache — MakeMKV fragt gar nicht erst: Das Debug-Log geht ohne einen einzigen Netz-Versuch von „Loaded content hash table" direkt auf „The volume key is unknown". Beweise: /root/.MakeMKV/_private_data.tar enthielt nur die Index-Datei und KEINE einzige hkd_*.bin — es wurde also nie ein Schlüssel geladen. Auch mit erzwungener frischer Prüfung (update.conf gelöscht, Meldung 5074 belegt den Web-Kontakt) und mit app_UpdateEnable = "1" kam keiner. Und die im MakeMKV-Forum dokumentierten Schlüssel-Server hkdata.fairuse.org und hkdata.crabdance.com lösen weltweit nicht mehr auf (NXDOMAIN gegen Fritz!Box, 8.8.8.8 und 1.1.1.1).
  • Richtigstellung zu v3.3 (weiter unten korrigiert): Dort stand, die Ursache sei „Disc neuer als MakeMKVs Schlüssel-DB" und ein MakeMKV-Update werde das lösen. Das ist widerlegt — es gibt keine nachladbare Schlüssel-Datenbank mehr. Warten auf ein MakeMKV-Update hilft bei diesem Fehler nicht.
  • Der einzige Weg, der heute funktioniert: KEYDB.cfg — GROSS geschrieben (unter Linux case-sensitiv) im MakeMKV-Datenverzeichnis. Rippy macht diesen Weg jetzt begehbar, statt auf MakeMKV zu warten.
  • Gebaut — persistentes Datenverzeichnis: docker-compose.yml mountet ${MAKEMKV_DATA_HOST:-/srv/rippy/makemkv} vom Host — im Worker auf /root/.MakeMKV, in der API auf /app/makemkv-data; beide Container bekommen MAKEMKV_DATA_DIR. Damit überleben KEYDB.cfg und die AACS-Dumps jeden Rebuild. entrypoint.sh schreibt settings.conf jetzt ergänzend statt zerstörend (sonst hätte der Beta-Key den Rest überbügelt) und setzt app_UpdateEnable = "1".
  • Gebaut — eine einzige Wahrheit über das Verzeichnis: neues Zwillings-Modul makemkv_daten.py (identisch in docker/api/ und docker/worker/) mit reinen Helfern — Status lesen, Inhalt prüfen, atomar schreiben, löschen, AACS-Dumps auflisten. Nur reine Funktionen, damit die Ampel sie ohne Postgres/Redis testen kann.
  • Gebaut — Bedienung im UI: Einstellungen → System zeigt den KEYDB.cfg-Status (Pfad, Größe, Anzahl Disc-Einträge, Datum), nimmt die Datei über einen ganz normalen Datei-Dialog entgegen (der Browser liest sie und schickt den Text als JSON — serverseitig bewusst KEIN Multipart-Upload, es gibt kein python-multipart, das würde die API beim Import töten), lehnt unplausiblen Inhalt mit deutschem Klartext ab, kann die Datei wieder entfernen und die AACS-Dumps zum Download anbieten.
  • Gebaut — man sieht endlich, was MakeMKV sagt: parse_msg() in ripping.py plus Log-Callback in tasks.py schreiben MakeMKV-Meldungen ins Rippy-Log (gedrosselt: Code 1003 raus, keine Wiederholungen, max. 40 je Rip). Der UHD-Fehlertext ist ehrlich neu geschrieben. caps.py meldet zusätzlich keydb: ja | nein | unbekannt je Worker.
  • Abgrenzung, die überall durchscheint: Rippy liefert KEINE Schlüssel mit, lädt keine herunter und verteilt keine. Rippy stellt nur den Platz für eine Datei bereit, die der Nutzer selbst mitbringt, und zeigt ehrlich an, was dort liegt. Genau die Grenze zieht schon docker/api/makemkv_key.py (Zeile 14) für den Beta-Key: das ist die Software-LIZENZ, nicht das Entschlüsseln oder Verteilen von Disc-Schlüsseln.
  • ⚠️ NOCH NICHT end-to-end bewiesen — ehrlich gesagt: Zum Zeitpunkt dieser Änderung lag KEINE KEYDB.cfg vor, die den Akira-Schlüssel enthält. Belegt sind der Befund oben und die neue Mechanik (Mount, Modul, Endpunkte, UI) — nicht ein erfolgreicher UHD-Rip. Der Nachweis steht aus und braucht eine echte Schlüssel-Datei.

Vorheriger Stand: v3.9 — Windows-Installer als echte .exe (Icon, kein Konsolenfenster) (24.07.2026)

  • RippyWorkerSetup.exe ersetzt den .vbs/.bat-Weg (Commander-Einwand: .vbs ist abgekündigt + wird als gefährlich geflaggt). install-gui.ps1 via ps2exe kompiliert: eingebettetes Rippy-Disc-Icon, KEIN Konsolenfenster (-noConsole), WinForms (-STA). Vorgebaut auf Windows (deploy/worker-windows/build-exe.ps1) + committet — Windows-.exe geht nicht von Linux (Rippy-Host). API: GET /worker-setup/windows-exe. UI (Worker → Windows): „Installer herunterladen (.exe)" als Haupt-Weg. E2E bewiesen: von der VM geladen → Fenster öffnet, conhost-Zähler unverändert (kein Konsolenfenster), Icon eingebettet.
  • Wichtig — friert nichts ein: Die .exe holt HandBrake zur Laufzeit (GitHub latest) und den Worker-Code live von Rippy. Nur bei Änderung der GUI (install-gui.ps1) neu bauen (build-exe.ps1), NICHT bei Tool-Updates.
  • Update-Story dokumentiert (Commander-Frage): MakeMKV → .env-Bump + Rebuild (Update-Check zeigt es); HandBrake Docker = Debian-stabil, native Worker = latest beim Installieren.

Vorheriger Stand: v3.8 — Grafischer Windows-Installer + Versionslage klar (24.07.2026)

  • Grafischer Windows-Installer (statt CLI): install-gui.ps1 (WinForms, ASCII-only) — Fenster mit Feldern (Rippy-IP, Worker-Name, Autostart) + Install-Knopf mit Live-Log + „Worker starten". Gleiche Schritte wie der CLI-Installer (Worker-Code, HandBrake latest, venv, Tray/Start/Uninstall). UI (Worker → Windows): Knopf „Grafischen Installer herunterladen (.bat)" — die .bat wird CLIENT-seitig mit der eingetragenen LAN-IP erzeugt, Doppelklick lädt+startet die GUI von Rippy (GET /worker-setup/windows-gui). CLI-Befehl bleibt als Profi-Alternative. Bewiesen: GUI-Fenster öffnet sauber auf dem Commander-PC (Titel „Rippy Encoding-Worker - Installation").
  • Werkzeug-Versionslage exakt dargestellt (Commander-Wunsch): System-Tab erklärt jetzt klar — MakeMKV aktualisierbar (wichtig wegen Schlüssel-DB), HandBrake im Docker-Worker bewusst Debian-stabil (1.6.1, KEIN Alarm mehr), native Worker holen die neueste. Update-Check zeigt MakeMKV mit „✓ aktuell" bzw. Update-Befehl, HandBrake neutral als Info.

Vorheriger Stand: v3.7 — Drei Praxis-Bugs (Mounts, HandBrake, Encoder-Wahl) (24.07.2026)

Alle drei live auf der VM verifiziert:

  • Speicher-Mounts robust: Ein toter CIFS-Mount (NAS weg/Rebuild) war mounted:false, verschwand aus „Verfügbare Ziele" und ließ sich nicht neu anlegen („Name existiert"). Jetzt: reachable-Status (bounded timeout 3 ls — Endpoint lädt in 0,014 s statt 10 s), umount -l-Fallback, reparieren() (lazy abhängen + frisch mounten), Re-Add repariert statt 409, POST /storage-mounts/{name}/repair, eigene „Netzwerk-Mounts"-Liste im UI mit Status + Reparieren/Entfernen. Bewiesen: Reparatur des rippy- Mounts → mounted:true, writable:true.
  • HandBrake-Versionen konsistent: Windows-Installer zieht dynamisch die neueste (GitHub latest) — bewiesen: HandBrake 1.11.2 installiert (statt fest 1.9.2). Update-UI erklärt: Docker = Debian-stabil (bewusst älter), Windows = neueste. Nebenbei ein latenter Installer-Bug gefixt: PowerShell 5.1 liest .ps1 als ANSI — ein Gedankenstrich zerschoss das Skript → install.ps1 ist jetzt ASCII-only.
  • Encoder-/Worker-Wahl beim Rip: Celery worker_direct=True, gezieltes Routing (transcode_queue) mit sicherem Fallback auf die geteilte Queue. Bewiesen mit ping_worker: Task landet exakt beim gewählten Node. Dropdown im Rip-Dialog ab 2 Online-Workern. Node-Zuordnung disambiguiert bei mehreren Workern pro Host. Nebenfund gefixt: DeviceDiscovery leitete die Titel-Auswahl (titles) gar nicht weiter. ⚠️ Gezieltes Routing braucht AKTUELLEN Worker-Code (worker_direct) — ein vor dieser Version installierter Remote-Worker pingt zwar, konsumiert aber seine Direkt-Queue nicht → neu installieren.
  • Voller Transcode-E2E weiterhin durch die UHD-Key-Lage blockiert (keine entschlüsselbare Disc) — die Routing-Mechanik ist per ping_worker bewiesen.

Vorheriger Stand: v3.6 — Design 2.0 gelandet + Ein-Branch-Umstellung (24.07.2026)

  • Design 2.0 ist in main (von Gemini umgesetzt): UI von 428 theme === 'dark'-Ternaries auf Tailwind-dark: + UI-Primitives (components/ui/, lib/design.ts) umgebaut, „Cinematic Cinema OS"-Look. Ampel grün (49 Tests, Ruff, Vite-Build), Live auf der VM.
  • Nur noch EIN Branch: main (Commander-Entscheid). Der stable-Zwischenbranch samt Grün-Gate ist abgeschafft — er war vestigial, weil die VM ohnehin aus main deployt. Konsequenzen:
    • ci.yml: Beförderungs-Schritt raus, die Ampel PRÜFT nur noch.
    • deploy.sh: nutzt main statt stable.
    • AGENTS §C / README / DESIGN-Briefing entsprechend umgeschrieben.
    • Gelöschte Branches: stable, design-2.0 (gemergt), kernumbau-2026-07-23 (alt).
  • Deploy-Weg ab jetzt: push → Ampel grün prüfen → auf der VM git pull (in ~/projects/rippy) bzw. ./deploy.sh.

Vorheriger Stand: v3.5 — Track-Tabelle, nativer Windows-Worker, Anleitungs-Tab (24.07.2026, Claude)

  • Volle Titel-Auswahl vor dem Rip: „Disc scannen" im Rip-Dialog → Worker-Task scan_tracks (makemkvcon info; Ergebnis via settings-Tabelle 'tracks:', UI pollt) → Tabelle mit Checkbox/Dauer/Größe/Kapiteln, Vorauswahl ab 5 min. Gewählte Titel gehen als meta.titles in den Job; der Worker rippt sie einzeln (makemkvcon kann pro Aufruf nur einen Titel oder all), Fortschritt anteilig. Parser mit Tests (apdefs-Attr 8/9/11).
  • Nativer Windows-Worker (ohne Docker!): Einstellungen → Worker bietet jetzt ZWEI Varianten — Linux (Docker, wie gehabt) und Windows (nativ, braucht nur Python). Die Rippy-Instanz versorgt sich selbst: GET /worker-setup/windows liefert install.ps1, /worker-setup/paket den Worker-Code als Zip (beides liegt via api-Dockerfile im Image). install.ps1: venv + Abhängigkeiten, HandBrakeCLI 1.9.2 vom offiziellen GitHub-Release (URL verifiziert), start-worker.bat (celery -Q transcode --pool=solo), optional -Autostart (schtasks onlogon). tasks.py lädt auf Windows ohne fcntl (Import-Guard); rip_disc ist dort hart verriegelt. Für echte Transcodes übersetzt RIPPY_PATH_MAP die Container-Pfade aufs Netzlaufwerk (pfad_lokal, mit Tests) — Voraussetzung bleibt eine Freigabe der Rippy-Ablage (derselbe offene Infra-Punkt wie beim Linux-Remote-Worker/AI-Box). E2E-BEWEIS (24.07., Commander-PC): Installer in einem Rutsch durchgelaufen (venv, Abhängigkeiten, HandBrakeCLI 1.9.2), Worker gestartet und in Rippy erschienen: test-windows-nativ | online: True | HandBrake: 1.9.2 | IP: 192.168.178.98 — echte LAN-IP, Celery-Ping grün. Testeintrag danach wieder entfernt.
  • Tray-Symbol + rückstandsfreie Deinstallation (v3.5.1, bewiesen): tray.py (pystray/Pillow, nur Windows-Worker) — Disc-Symbol neben der Uhr mit Status, Start/Stopp, „Rippy öffnen", Log, Beenden; Celery läuft als Kind ohne Konsolenfenster. install.ps1 erzeugt start-tray.bat (empfohlen), start-worker.bat (Debug) und uninstall.ps1. E2E bewiesen auf dem Commander-PC: Frisch installiert → Tray + celery-Kind liefen → tray-test online in Rippy → uninstall.ps1 -Force stoppte alle Prozesse, meldete den Worker per DELETE /workers/{name} ab und löschte den Ordner restlos (danach: 0 Prozesse, Ordner weg, in Rippy nur noch rippy-hauptworker).
  • Anleitungs-Tab im UI: kompletter Selbsterklärer (Disc-Weg, Serien, NAS, Media-Server, Benachrichtigungen, Key, Worker, Downloads, FAQ).
  • Design 2.0 kommt bewusst in eine FRISCHE Session (Commander-Entscheid) — Infrastruktur steht (darkMode 'class'), reine Konvertierungsarbeit.

Vorheriger Stand: v3.4 — Restefeger: der Ideen-Katalog ist abgearbeitet (24.07.2026, Claude)

Commander-Entscheid: AUTH IST KOMPLETT RAUS (Heimnetz-only; das UI hatte nie einen Login, die Endpoints waren Placebo, passlib/bcrypt brach die Ampel). /token + /api-keys + auth.py + Abhängigkeiten entfernt, JWT_SECRET_KEY ist keine Pflicht mehr — der Schnellstart läuft ohne .env-Zwang. KONZEPT §10.

Neu in v3.4 (Details in ROADMAP Etappe 15):

  • Serien-Flow: Rip-Dialog fragt Serienname + Staffel → Ablage <Serie>/Season NN, tvshow.nfo/poster im Serien-Ordner, und die Episoden werden per Laufzeitabgleich (TMDB) erkannt und benannt („Serie S01E02.mkv") — nur bei eindeutiger Zuordnung, sonst ehrliches Log. Komplette Staffel auf einer Disc klappt auch bei uniformen Anime-Laufzeiten.
  • Jellyfin/Emby-Refresh nach jedem fertigen Rip (URL/API-Key + Test-Knopf in Einstellungen → Ripping) — Disc rein, Film erscheint im Server, null Klicks dazwischen.
  • Duplikat-Warnung per Disc-Fingerabdruck (Karte zeigt „bereits gerippt", Vollautomatik überspringt).
  • „Nur Hauptfilm" funktioniert jetzt wirklich (Info-Lauf → längster Titel; pro Rip im Dialog wählbar). Vorher wirkungsloses Setting.
  • OMDb-Treffer werden eingedeutscht (TMDB /find über die IMDb-ID).
  • Dashboard: Speicherplatz-Anzeige (amber < 60 GB) + CSV-Export.
  • Metadaten-Seite entfernt (Korrektur-Popup ist der einzige Weg) samt Placebo-Endpoints; Doppel-Jahr „(2009) (2009)" gefixt.
  • Remote-Worker-Blocker behoben: redis/postgres waren NIE veröffentlicht — Ports 6379/5432 jetzt offen, API_URL für Worker gesetzt. Damit sind AI-Box/Windows-Worker überhaupt erst anschließbar.

Bewusst offen (ROADMAP „Ideen-Katalog (Rest)"): volle Track-Tabelle, nativer Windows-Worker (braucht Testlauf auf Ziel-Hardware), Design-2.0- Konvertierung (Infrastruktur steht), AI-Box-NFS-Export, Kodi-Refresh.


Vorheriger Stand: v3.3 — Praxis-Feedback-Runde (24.07.2026, Claude)

Zwei echte Bugs mit Beweis gefixt:

  1. SMB-Mount „Unable to apply new capability set": mount.cifs hebt CAP_DAC_READ_SEARCH an — die fehlt in Dockers Default-Caps. Auf der VM reproduziert (Bounding-Set a82425fb, Bit 2 fehlt) und mit cap_add: DAC_READ_SEARCH bewiesen behoben (docker-compose.yml).
  2. TMDB fiel still aus: Der Client konnte nur v4-Bearer-Tokens — der eingetragene übliche v3-Key (32 Hex) bekam still 401, Suche lieferte nur OMDb. Jetzt beide Key-Arten (ist_v4_token, mit Tests); Einstellungen → APIs hat „Verbindung prüfen" mit Live-Status je Quelle (am Cache vorbei).

Neu in v3.3:

  • 4K UHD als eigener Disc-Typ (classify ≥ 55 GiB, beide detection.py, Tests): eigene Badge-Farbe überall, Prescan-Label „4K UHD".
  • UHD-Kette diagnostiziert (Nachtrag, gleicher Tag): Der BU40N-Flash IST erledigt — makemkvcon meldet „Using LibreDrive mode (v06.3)". Der Summer-Wars-Fehlschlag lag NICHT am Laufwerk, sondern an „The volume key is unknown" — auf der VM mit 1.17.7 UND der aktuellsten 1.18.4 reproduziert. Konsequenzen: MakeMKV auf 1.18.4 gehoben (Scan-Hänger durch überall genutztes --noscan umgangen, auf der BU40N sauber gelaufen; URL-Base → /download), run_makemkv reicht kritische Meldungen (volume key/Key abgelaufen) in den Fehlertext durch, UHD-Klartext unterscheidet jetzt „Laufwerk kann kein UHD" von „Disc-Schlüssel fehlt" inkl. Forum-Dump-Hinweis (MakeMKV speichert den AACS-Dump automatisch unter /root/.MakeMKV/). ⚠️ Korrigiert am 25.07.2026 (siehe v3.10 ganz oben): Die hier ursprünglich genannte Erklärung „die Disc ist neuer als MakeMKVs Schlüssel-DB, ein MakeMKV-Update löst das" ist FALSCH. Nachgemessen: MakeMKV hat gar keine nachladbare Schlüssel-DB mehr und versucht auch keinen Online-Abruf; die alten Schlüssel-Server sind tot. Der Weg zum Ziel heißt KEYDB.cfg, nicht „warten auf die nächste Version".
  • Vollautomatik (Einstellungen → Ripping): Disc erkannt → Rip startet ohne Popup in den passenden Schnellwahl-Ordner (Serie/Film/Musik).
  • Job-Verwaltung: „Neu komprimieren" nur noch, wenn Rohdaten wirklich daliegen (can_retry); Jobs einzeln löschbar (Papierkorb) + „Erledigte aufräumen" mit Bestätigungs-Dialog — Dateien bleiben immer liegen.
  • „Alle herunterladen" im Job-Detail (gestaffelte Einzel-Downloads — bewusst kein Server-seitiges 40-GB-Zip).
  • Worker zuordenbar: WORKER_NAME-Env als stabiler Anzeigename (compose: rippy-hauptworker; Remote-Worker: frei wählbar) — fixt zugleich die Offline-Leichen nach Rebuilds; IP + Container-ID werden mit angezeigt, verwaiste Einträge sind löschbar (DELETE /workers/{name}). Online-Abgleich läuft jetzt über info.hostname.
  • Disc-Karte: „Quelle: TMDB/OMDb/MyAnimeList · xx % sicher" statt des nackten „Übereinstimmung xx %"; TMDB-Metadaten sind Deutsch (language= de-DE war schon überall dran — sie kamen nur nie an, siehe Key-Bug).
  • Ripping-Tab nach Medium gegliedert (Video / Audio-CD / Allgemein) + Klartext: alle Tonspuren & Untertitel bleiben erhalten (--all-audio/ --all-subtitles) — wichtig für Anime.
  • Ordner-Verwaltung (Ex-„Dateibrowser") ist jetzt beschriftet, erklärt und standardmäßig eingeklappt.
  • Docker-Pflicht beim Worker: ehrlich im UI beantwortet; nativer Windows-Dienst steht als Ausbaustufe in der ROADMAP (Ideen-Katalog).

Vorheriger Stand: v3.2 — Universal-Komfort-Runde + Ampel entrostet (24.07.2026, Claude)

Wichtigster Befund zuerst: die Ampel war seit dem 23.07. ROT und stable hing 10 Commits hinter main — deshalb kam nichts Neues mehr auf die VM. Zwei Ursachen, beide behoben:

  1. bcrypt ≥ 4.1 bricht passlib 1.7.4 (__about__ entfernt → Selbsttest wirft „password cannot be longer than 72 bytes") → bcrypt==4.0.1 gepinnt.
  2. Der cache_keys-Test kannte den Disc-Fingerprint im Prescan-Key (23.07.) nicht → Test an das echte Format angepasst + Fingerprint-Testfall dazu.

Neu in v3.2 (Commander-Sammelauftrag, alles mit Tests / Vite-Build grün):

  • Media-Server-Integration: Setting mediaServer (Wizard + Einstellungen → Ripping): Zielordner „Titel (Jahr)" statt Job-UUID; für Jellyfin/Emby/Kodi zusätzlich movie.nfo/tvshow.nfo + poster.jpg (Worker: medien.py). Plex = nur Benennung. Job speichert Disc-Metadaten jetzt mit (jobs.meta, Migration).
  • Benachrichtigungen ECHT: notify.py in API+Worker (Discord/Slack/ntfy/ generisch, Erkennung an der URL) — das Feld war vorher ein Placebo. Meldung bei fertig/fehlgeschlagen/abgebrochen; Anleitung + „Test senden" im UI.
  • SMB-Scan-Fix: „NT_STATUS_ACCESS_DENIED" heißt jetzt im Klartext „Gast- Abfrage verweigert → Benutzer/Passwort eintragen"; Credential-Felder stehen im UI VOR dem Auflisten-Knopf. NAS-Ziel-Anlage quittiert per Toast.
  • 4K-UHD-Vorsorge: Arbeitsverzeichnis workDir konfigurierbar (auf NAS-Freigabe legbar), Platz-Check per Disc-Größe (ioctl) VOR dem Rip mit Klartext-Abbruch statt voller Platte bei 40 GB.
  • Einstellungen → System: Werkzeug-Versionen je Worker (MakeMKV/HandBrake, ENV MAKEMKV_VERSION im Image), Key-Quelle (ui/env/keiner), freier Platz; MakeMKV-Beta-Key im UI pflegbar — gilt ab dem nächsten Rip, ohne Rebuild.
  • Job-Detail-Popup (Klick auf Titel in „Neueste Jobs"): Poster, Jahr, Beschreibung, Genres, Ablagepfad, Fehler (GET /jobs/{id}/detail).
  • Download fertiger Rips im Browser: „Download"-Knopf bei fertigen Jobs (Aktion-Spalte) → Dateiliste mit Größen im Detail-Popup, Stream via GET /jobs/{id}/files/{name} (Pfad-Validierung strikt unter /app/media, realpath-Check gegen Symlink-Ausbrüche) — vorher nur per scp erreichbar.
  • UI-Feedback: Toast-System (ToastContext, ploppt oben rechts), drehende Refresh-Knöpfe, Logs-Pills um Warnung/Fehler ergänzt, Datei-Browser zeigt jetzt auch DATEIEN (grau, mit Größe) — der „leere" Bluray-Ordner war voll.
  • Favicon (Disc, Indigo-Verlauf) + Footer „Created with ❤️ by LucyAI, Claude and KrBrZ".
  • Doku: README (Media-Server, Benachrichtigungen, UHD-Arbeitsverzeichnis, „Rippy woanders bereitstellen"), ROADMAP Etappe 13 + Ideen-Katalog, KONZEPT-Fortschreibungen (Abschnitt 10), AGENTS-Stand.

Nächste Schritte: unverändert die Fäden aus v3.1 (unten) + Ideen-Katalog in der ROADMAP (Serien-Flow, Duplikat-Warnung, Jellyfin-Refresh, Auth).


Vorheriger Stand: v3.1 — E2E BEWIESEN + Universal-Runde (24.07.2026, Claude)

Der Beweis steht: Evangelion 2.22 (BD-50) komplett durch die Kette — erkannt → verlustfrei gerippt (43 GB, LibreDrive) → H.264-komprimiert → 4,8 GB Endergebnis (Hauptfilm 3,3 GB + 4 Extras), Rohdaten automatisch gelöscht. Retry-transcode dabei live bewiesen (x265 war auf 4 vCPUs zu lahm → Abbruch + Neustart mit H.264 OHNE Neu-Rip).

Seit v3.0 dazugekommen (alles deployt, Ampel grün):

  • Korrektur-Flow: „Nicht korrekt?!"-Popup an der Disc-Karte; Suche über alle Quellen (GET /metadata/search), Wahl wird 30 Tage per Disc-Fingerabdruck gemerkt (POST /metadata/override)
  • Metadaten-Kette: BD-Klartext-Titel via ro-Mount (bdmt_*.xml), Jikan/MAL (keyless, Ähnlichkeits-Score), OMDb-Qualitäts-Gate, Wort-Strip-Degradation, ehrliches Cache-TTL (unknown nie cachen)
  • Worker-Verwaltung (Einstellungen → Worker): Live-Erreichbarkeit (Celery-Ping + Minuten-Herzschlag), Copy-Paste-Anbindung neuer Maschinen
  • Speicherziele: SMB-Freigaben-Auflistung, Schreibtest beim Mount, lokaler Ordner-Browser + mkdir; Mounts via UI (CAP_SYS_ADMIN + rshared)
  • First-Run-Wizard, Job-Abbruch (kooperativ), Transcode als eigener Task auf Queue transcode (Basis Remote-GPU-Worker), ruff.toml gepinnt

Nächste Schritte: AI-Box als VAAPI-Transcode-Worker (VCN4; NFS-Export nötig, deploy/remote-transcode-worker.yml) · TMDB-Key eintragen · Arcane-GitSync-Binding auf stable (aktuell Notfall-Deploy-Weg) · Metadaten-Seite entfernen, wenn Popup abgenommen · Jellyfin-Postprocessing an Worker · Auth vor schreibende Endpoints · MAKEMKV_APP_KEY erneuern (~Ende Juli, Forum t=1053).


Vorheriger Stand: v3.0 — Kernumbau (23.07.2026, Claude)

Der Zweck existiert jetzt wirklich. Komplett-Audit ergab: „Disc rein → gerippt raus" hatte nie einen Code-Pfad (Details in ROADMAP Etappe 11).

Kernpunkte des Umbaus:

  • Rip-Kette komplett neu: POST /jobs → Celery → Worker (MakeMKV 1.18.4 verlustfrei, Etappe 10) → Postgres-Job-Status → UI. CD weiter via abcde.
  • Disc-Erkennung über Kernel-ioctls (CDROM_DISC_STATUS + Größe), Watcher pollt statt udev (im Container gibt es kein udev).
  • UI: echter Vite-Build hinter nginx mit /api-Proxy — vorher Dev-Server und hartkodiertes localhost:8000 (deshalb waren nie Laufwerke zu sehen).
  • Compose: Laufwerk als devices: (Bind-Mounts gaben EPERM), Ausgabe auf media-Volume (vorher /output → nicht gemountet + read-only-Falle).
  • Placebos entfernt: Mock-Logs, toter SSE-Stream, udevadm-Discovery, 14 Troubleshooting-Altdateien (ARCAN-*.md, Setup-Skripte, udev-Rules).

Hardware-Kette (23.07.): Laufwerk (Verbatim 4K BD RW, BU40N 1.05) hängt am Proxmox-Host; LPM-Quirk usbcore.quirks=18a5:0428:k gefixt die Serien-Disconnects (reboot-fest in GRUB). VM 106 bekommt es per USB-Passthrough host=18a5:0428. ⚠️ Firmware 1.05 kann KEIN UHD-Ripping (verschlüsselt) — Crossflash auf 1.03-MK ist der dokumentierte Weg, separater Faden. DVD/BD normal geht.

Offen: E2E mit echter Disc · Jellyfin-Post-Processing an Worker anbinden · Auth vor schreibende Endpoints · MAKEMKV_APP_KEY in Arcane-Env pflegen (monatlich).


Vorheriger Stand

v1.10 — Complete Dark Mode Implementation (22.07.2026)

Rippy läuft auf Arcane VM (192.168.178.162):

  • Arcane WebUI: http://192.168.178.162:3552
  • Rippy UI: http://192.168.178.162:80
  • Rippy API: http://192.168.178.162:8000
  • Theme Context ausgelagert (App.tsx → ThemeContext.tsx + useDarkMode.ts)
  • Config Validation mit TMDB-API-Key Pflicht
  • Cache Key Centralization (cache/keys.py)
  • Dark Mode mit Theme-Toggle und localStorage persistence (UI-Only)
  • Alle UI-Komponenten komplett auf Dark Mode aktualisiert:
    • App.tsx, Dashboard.tsx, Settings.tsx, MetadataPreview.tsx
  • Sidebar Navigation mit Dark Mode Support
  • Einstellungen-Page mit Tab-Struktur
  • Doppelte Arcane-Einträge behoben (rippy statt Rippy)
  • Import-Fixes (relative → absolute Imports)
  • Git Sync in Arcane konfiguriert
  • Commit: d2fb281 — Complete Dark Mode Implementation

Docker-Container

Container Port Status
rippy-api 8000 Healthy
rippy-worker - Running
rippy-ui 80 Running
rippy-postgres 5432 Healthy
rippy-redis 6379 Healthy

Features

  • Etappe 3: SQLite-Cache, TMDB/MusicBrainz/TheTVDB Clients, Pre-Scan, Metadaten-Preview
  • Etappe 4: Jellyfin-Formatierung (NFO-Generator, Image-Downloader)
  • Etappe 5: JWT-Auth (15min/7T), Rate-Limiting (100/min), API-Key-Management
  • Etappe 6: Docker read_only, tmpfs, healthchecks, minimale Images
  • Etappe 7: API UI Modernisiert, Import-Fixes, Doppelte Einträge behoben
  • Etappe 8: Dark Mode mit Theme-Toggle, Separation of Concerns
  • Etappe 9: Complete Dark Mode Implementation (alle UI-Komponenten)
  • Dokumentation: README, KONZEPT.md, ROADMAP.md, SAVEPOINT.md

Nächste Schritte

  • Job-Verlauf UI (aktuell nur leere Listen)
  • TMDB API Schlüssel in Arcane Environment konfigurieren
  • Proxmox LXC Template
  • Ansible Playbooks

SoC Refactoring — Abgeschlossen (Etappe 9)

  • Theme-Context: Theme-Logik aus App.tsx ausgelagert
  • Dark Mode Utility: useDarkMode Hook implementiert
  • Component-Struktur: Dark Mode props von inneren Komponenten versteckt
  • Config Validation: TMDB-API-Key ist Pflicht
  • Cache Key Centralization: cache/keys.py erstellt
  • CD-Ripping: abcde-Integration implementiert

Git-Log

d2fb281 - fix: complete dark mode implementation with all UI components
43dfce5 - feat(ui): implement dark mode with theme toggle
rippy-ui-modern - UI Modernisiert & Import-Fixes
3d87c8c - Dokumentation aktualisiert
bdb9f8d - SAVEPOINT v1.5: Sicherheit abgeschlossen
efa642e - Etappe 6: Sicherheit

v2.0 — Fix-Runde nach externem Code-Review (22.07.2026, Claude)

Alle Befunde des Reviews behoben, echte Tests eingeführt, Ampel-CI aktiv.

Gefixt:

  • Metadaten-Preview WIEDERHERGESTELLT: Beim SoC-Refactoring hatte ein Stub-Paket die echte prescan-Implementierung überschattet — die Preview lieferte immer „Unknown Disc". Echte Logik liegt jetzt im Paket, tote Altmodule (cache.py, prescan.py flach) gelöscht.
  • Fortschrittsmeldung: Celery update_state statt Aufrufe eines nie existierenden Tasks; self.send_task (erfundene API) entfernt.
  • abcde-Kommando korrigiert (-o war doppelt → CD-Ripping war nie funktionsfähig); Zielverzeichnis jetzt via OUTPUTDIR-Config; Kommando-Bau als testbare Funktion.
  • JWT: fester Schlüssel PFLICHT (kein Zufalls-Fallback pro Prozess mehr); Logout blacklistet wirklich; Cleanup löscht nur Abgelaufene (vorher: alles).
  • main.py: crashende Endpoints repariert (fehlende Imports Path/secrets, nicht existentes api_keys-Dict → ratelimit-Store), Audio-year-Bug, Admin-Zugang aus .env statt hartkodiert.
  • Ruff komplett grün (29 Funde), package-lock.json committet.
  • Tests: test_auth, test_cache_keys, test_ripping_helpers (der giftige test_health-Placebo ist raus).

ENTSCHIEDEN (22.07. abends): Das KONZEPT gilt — MakeMKV kommt

MakeMKV lossless (Muss-Feature) wird umgesetzt — als eigene Etappe 10 in der ROADMAP (Worker-Container + makemkvcon + Beta-Key, in Zed zu bauen, E2E mit echter Disc). Bis dahin bleibt HandBrake als SICHTBARER Übergang aktiv — jede*r weiß: aktuell wird transkodiert, nicht verlustfrei gesichert.

Deploy-Weg seit 22.07. abends: GRÜN-GATE (vollautomatisch)

Push auf main → Ampel prüft → grün → CI befördert auf Branch stableArcane-GitSync zieht stable und deployt. Rot deployt NIE. ./deploy.sh ist nur noch der Notfall-Hebel; SSH zur VM bleibt als Fallback erlaubt.