Zwei Commander-Befunde, beide mit derselben Wurzel: Rippy hat sich selbst
ausgebremst und dann geschwiegen.
## "Wenn der Worker installiert ist, wird dieser Bereich oft neu geladen"
Gemessen statt geraten. Die Antworten von /jobs und /capabilities waren ueber
zwanzig Sekunden byteweise identisch, alle Endpunkte antworteten unter 30 ms -
es wurde also gar nichts neu geladen. Im nginx-Log standen dagegen 97 Antworten
mit HTTP 429.
Drei Fehler griffen ineinander:
1. Das Limit war zu klein fuer Rippy selbst: 100 Anfragen/min, waehrend ein
offener Tab 111/min verursacht (Dashboard 75 + Log-Kasten 24 + Laufwerke 12)
und der Windows-Tray weitere 12/min dazulegt.
2. Der nginx gab die Client-Adresse nicht weiter. Fuer die API kam damit ALLES
von 172.19.0.6 - Browser, zweiter Tab und Tray teilten sich einen Eimer
(812 von 876 Anfragen). Das erklaert die Kopplung an den Worker: tray.py
fragt /api/jobs ueber Port 80, also durch denselben Proxy.
3. Ein abgewiesener Abruf leerte das UI. `catch(() => [])` heisst "es gibt
keine Jobs" - richtig waere "ich weiss gerade nichts Neues". Fuer einen Takt
stand "Keine Jobs", die Zaehler sprangen auf (0), vier Sekunden spaeter war
alles zurueck.
Behoben: X-Real-IP im nginx, Grenze auf 600/min mit vorgerechneter Herleitung,
jeder Fehlschlag laesst den alten Stand stehen (null statt []), axios bekommt
eine Zeitgrenze, und das Dashboard trennt schnelle Daten (Jobs/Laufwerke, 4 s)
von langsamen (Hardware/Worker/Ablagen, 12 s) - 75/min werden zu 30/min.
Ein greifendes Limit steht ab jetzt im Log, gedrosselt auf eine Meldung pro
Client und Minute.
## "Hier gibt es den Button 'neu' aber WAS wird dann gemacht?"
Immer die Komprimierung - auch bei einem Job, dessen RIP abgebrochen war. Am
26.07.2026 waeren aus 5,1 GB Bruchstueck (von rund 40 GB) brav ein Film
geworden, der bei 12 % aufhoert.
Die Phase war nach `status = "failed"` nicht mehr feststellbar, also wird sie
jetzt vermerkt (rip_fertig in den Job-Metadaten: false beim Rip-Start, true bei
der Uebergabe an die Kompression). Daraus folgt die Beschriftung: "Neu
komprimieren", "Neu rippen" - oder bei Bestandsjobs ohne Vermerk ein Dialog,
der beide Wege erklaert und die Groesse der Rohdaten als Entscheidungshilfe
nennt. Geraten wird nicht. Fuer den Rip-Fall gibt es POST
/jobs/{id}/retry-rip: neuer Job mit neuer ID (sonst laege das Bruchstueck im
Roh-Verzeichnis des neuen Rips), Titel/Ablage/Sprachwahl uebernommen, mit
ehrlicher Absage wenn keine Disc im Laufwerk liegt.
10 neue Tests (289 gruen), darunter eine Kopplungspruefung: der Name der
Phasen-Marke muss in worker/tasks.py und api/phasen.py zusammenpassen - genau
diese Sorte Auseinanderdriften hat die Zombie-Erkennung ein Release lang blind
gemacht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander: "Das ist ja quatsch. Mein PC hat das Ziel als Worker direkt auf dem
PC eingebunden."
Nachgemessen: Die Warnung hatte SACHLICH recht, war aber unbrauchbar. Sein PC hat
die Freigabe wirklich als Netzlaufwerk (X:) gemountet - nur lag das
ARBEITSVERZEICHNIS darauf und die ABLAGE nicht. Das Ziel war weiter
/app/media/movies, also die VM-Platte, und dorthin kommt sein PC nicht. Beleg aus
der Worker-Meldung:
pfad_map = /app/media/rippy=\192.168.178.62\rippy
Ziel = /app/media/movies <- nicht abgedeckt, richtig gewarnt
Die alte Meldung nannte den Container-Pfad und die Mapping-Zeichenkette - also
genau die zwei Dinge, die ein Nicht-Entwickler nicht deuten kann. Jetzt steht der
Handgriff drin, mit dem NAMEN der Freigabe, die dieser Worker wirklich erreicht
("Einstellungen -> Ripping -> Ablage auf 'rippy' stellen").
Dazu ein Knopf, der das Ziel NUR FUER DIESEN RIP auf die Freigabe legt
(/app/media/movies -> /app/media/rippy/movies), ohne die globale Ablage
anzufassen. Wer den Rip jetzt starten will, will jetzt eine Loesung und keine
Wegbeschreibung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An der echten Akira-Blu-ray gemessen, nachdem die Sprachauswahl live lief:
Ton: deu 4x, jpn 2x, und 4x
Untertitel: deu 4x, und 8x
MakeMKV liefert die Namen englisch ("German", "Japanese") und fuer Spuren ohne
Sprach-Kennzeichnung "Undetermined" - das sind typischerweise Audiokommentare.
Der Commander liest alles auf Deutsch (AGENTS Grundregel 4), und
"Undetermined" ist fuer einen Nicht-Entwickler schlicht keine Auskunft. Jetzt
steht dort "Ohne Sprachangabe", und die zwei Dutzend haeufigsten Codes haben
deutsche Namen. Unbekannte Codes behalten MakeMKVs Namen - geraten wird nichts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Weitergabe nicht durch Lesen geprueft, sondern indem ich mich wie ein fremder
Rechner verhalten habe: frischer `git clone` von Gitea in ein leeres Verzeichnis,
dann `./install.sh --nur-pruefen`. Ergebnis: 2,6 MB, alles gruen, Laufwerk samt
richtigem sg-Knoten ueber die SCSI-Adresse erkannt. Der Weg selbst traegt.
Drei Luecken sind dabei aufgefallen:
1. IN BEISPIELBEFEHLEN STAND MEINE IP. `install.ps1` und
`remote-transcode-worker.yml` nannten im Aufruf-Beispiel 192.168.178.162 -
also genau die Zeile, die ein Fremder kopiert. Jetzt Platzhalter, beim
Windows-Installer mit dem Hinweis, wo die richtige IP steht (und dass es NICHT
die des eigenen PCs ist - der haeufigste Irrtum).
2. DER ASSISTENT FRAGTE DEN MAKEMKV-BETA-KEY NICHT. Er stand in der README, in
der .env und in den Einstellungen - nur nicht dort, wo man beim Einrichten
hinsieht. Ein Fremder installiert also, legt eine Blu-ray ein und bekommt
spaeter einen Fehlschlag, ohne dass ihn jemand darauf hingewiesen haette. Das
ist die wahrscheinlichste Stolperstelle einer frischen Installation. Jetzt
fragt der Assistent ihn ab, mit dem Unterschied im Klartext: DVDs gehen ohne,
Blu-ray braucht ihn - und er ist NICHT der Disc-Schluessel einer 4K-Disc.
3. Eine Docstring nannte meine NAS-IP als Beispiel - jetzt neutral (//NAS/rippy).
Tests und Log-Beispiele behalten die echten Namen: Sie dokumentieren Messungen,
und genau das ist ihr Wert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander-Anforderung: "Die Disc hat Material in X Sprachen und X Untertiteln -
Rippy muss VOR dem Rip fragen: Was genau willst du haben? In der Automatik muss
das ebenfalls einstellbar sein."
Die Auskunft lag laengst vor und wurde weggeworfen: Derselbe Titel-Scan, der die
Titel-Tabelle fuellt, liefert in derselben makemkvcon-Ausgabe die Streams mit.
Ein zweiter Info-Lauf haette eine Minute Wartezeit gekostet - jetzt kommt beides
aus einem Aufruf.
Format an der Akira-Blu-ray im Laufwerk gemessen (AGENTS Regel D):
SINFO:<titel>,<stream>,<attribut>,<code>,"<wert>"
1 = Typ ("Audio"/"Subtitles"), 3 = Sprachcode, 4 = Sprachname,
6 = Codec, 14 = Kanaele, 30 = Beschreibung
DIE FALLE dabei: Die Sprache steht in 3/4, NICHT in 28/29. Die tragen auf JEDEM
Stream "eng"/"English" - auch auf einer deutschen Tonspur und auf dem
Videostream; das ist MakeMKVs eigene Anzeigesprache. Wer 28 nimmt, haelt jede
Disc fuer englisch. Ein Test haelt das fest.
Angewendet wird die Wahl bei der KOMPRESSION, nicht beim Rippen - drei Gruende:
der Rip bleibt vollstaendig und verlustfrei (Muss-Feature laut KONZEPT); HandBrake
hat dafuer dokumentierte Schalter (--audio-lang-list / --subtitle-lang-list, die
genau die ISO-639-2-Codes nehmen, die MakeMKV liefert - beides gegengeprueft);
und wer spaeter andere Sprachen will, komprimiert neu statt die Disc wieder
einzulegen. Genau das sagt der Dialog auch, sonst glaubt man, es werde
unvollstaendig gerippt.
Nichts angeklickt heisst "alles behalten" - das Verhalten von vorher. Auch die
Einstellung ist bewusst LEER vorbelegt: Ein stilles "deu" wuerde bei einem
japanischen Original die Originaltonspur wegwerfen, ohne dass jemand gefragt hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander: "Der Server Status muss dringend ueberarbeitet werden, das Dashboard
soll ja quasi alles auf einen Blick zeigen." Berechtigt - die Karte hiess "Echte
Live-Daten" und enthielt drei Angaben, von denen zwei erfunden waren:
1. "Auslastung: 0 % (Aktiv)" war NICHT die CPU-Last, sondern der Fortschritt des
Jobs - bzw. eine feste 15 bzw. 5, wenn keiner lief. Rippy misst nirgends
CPU-Last, also wird sie auch nicht behauptet.
2. Die Verlaufskurve daneben war Math.random(). Reine Dekoration, die wie eine
Messung aussah.
3. "N Worker Online" zaehlte die registrierten Eintraege aus /system/info - auch
Leichen alter Container-Rebuilds. Die echte Erreichbarkeit steht in
/capabilities (Celery-Ping) und wird jetzt von dort geholt.
Statt dessen: laufende Phase im Klartext mit RESTZEIT, jeder erreichbare Worker
mit Kernen/Vektorbefehlen/extern, freier Platz mit Warnung, wenn er nicht mehr
fuer eine Disc reicht. Und wenn kein Worker antwortet, steht das rot da statt
"1 Worker Online".
Restzeit auch im aktiven Rip-Banner und in der Job-Tabelle. Solange die
Datenlage duenn ist, steht dort "Restzeit wird gemessen" - keine erfundene Zahl.
Job entfernen fragt jetzt vorher nach den Rohdaten und nennt die GB, die daneben
liegen bleiben und danach nicht mehr erreichbar sind - auf Wunsch loescht es sie
mit. Der Fall aus v3.14, bei dem 75 GB unsichtbar verwaisten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vier Commander-Punkte auf einmal, alle am selben Ort.
Presets (Punkt 1 + 3b): Die drei Auswahllisten in Einstellungen kommen jetzt
vom Worker statt aus dieser Datei, gefiltert auf die passende Aufloesung - die
4K-Liste bietet keine 1080p-Presets mehr als Normalfall an (genau dieser Griff
rechnete in v3.12 eine 4K-UHD auf 1080p herunter; bewusstes Verkleinern steht
jetzt in einer eigenen, benannten Gruppe). Knopf "Bestes waehlen" uebernimmt die
Empfehlung fuer alle drei Disc-Typen, und unter jeder Liste steht, WARUM. Ein
gespeicherter Wert, den der Worker nicht kennt, wird benannt statt verschluckt -
so faellt eine falsche Bestandseinstellung ueberhaupt auf.
Der Wizard entscheidet nicht mehr selbst, sondern nimmt dieselbe Empfehlung.
Damit gibt es nur noch EINE Stelle mit dieser Logik, und die hat Tests.
Warnung vor dem Start (Punkt 6 des Savepoints): Waehlt man einen externen
Encoder, der Quelle oder Ziel nicht erreicht, steht das JETZT im Rip-Dialog -
nicht erst nach einer Stunde Rip. Geprueft wird mit derselben Regel, die
pfad_lokal() im Worker anwendet. Bei "Automatisch" wird genannt, welcher Worker
die Aufgabe kaputtmachen koennte: die geteilte Queue nimmt den ersten freien.
Datei-Browser (Punkt 7): "Warum wird hier der Datei Browser noch angezeigt - das
ist doch quatsch." Er ist nicht wirklich redundant (nur ueber ihn geht ein
freier Zielordner fuer EINEN Rip), aber er ueberschrieb die Schnellwahl
stillschweigend. Jetzt eingeklappt - wer ihn aufklappt, entscheidet bewusst, und
/browse wird erst dann geholt. Nebenbefund: browseFiles wurde geladen und NIE
angezeigt, samt ungenutztem File-Icon - beides raus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Aus dem gescheiterten Test-Rip des Commanders (Job 95afdc89) und seiner
UI-Durchsicht. Der Reihe nach.
## 1. Der externe Encoder wurde SEHR WOHL gesehen (Diagnose-Korrektur)
Der Commander schloss aus der Meldung, das System sehe seinen PC nicht. Live
gemessen sagt das Gegenteil:
meta.transcode_node = tobisnicerpc@TobisNicerPC
VM-Worker-Log = nur rip_disc, KEIN transcode_files
22:19:41.271 Kompression eingereiht
22:19:41.453 "Keine Roh-MKVs gefunden" <- 182 ms spaeter
Das Routing hat funktioniert. Sein PC nahm die Aufgabe an und lehnte sie sofort
ab, weil er /app/media/rippy/<job> nicht finden kann - ein Pfad, der nur INNEN
im Container existiert. Der Rip war vollstaendig (79,6 GB auf der NAS).
## 2. Der Hardware-Encoder-Bug war meiner
leite_backends_ab() verlangte zusaetzlich ein Geraet: /dev/dri fuer
VAAPI/QSV/VCE bzw. nvidia-smi fuer NVENC. Unter WINDOWS gibt es beides nicht -
der PC des Commanders (RX 9070 XT) meldete deshalb nur CPU-Encoder, obwohl
HandBrakes Windows-Build vce_*/nvenc_*/qsv_* beherrscht. Der Bug traf genau den
Anwendungsfall, fuer den externe Worker gedacht sind.
Die Pruefung war ausserdem ueberfluessig: HandBrake probiert Hardware beim
Start selbst an und listet nur Nutzbares - auf der VM belegt durch
"qsv: not available on this system" bei gleichzeitig fehlendem qsv_* in --help.
Jetzt ist HandBrakes Liste die einzige Auskunft.
Dazu nach FAMILIE unterschieden (nvenc/qsv/vce/vaapi) statt alles in "vaapi" zu
werfen - eine AMD-Karte lief unter dem Intel-Namen. Hardware-AV1 bekommt eine
eigene Kennung, weil es die beste Kombination aus Tempo und Groesse ist und
sonst unsichtbar bliebe.
## 3. Vektorbefehle unter Windows - gemessen, nicht "unbekannt"
Es gibt dort kein /proc/cpuinfo, also meldete der Windows-Worker
"Vektorbefehle unbekannt" - gerade auf der Maschine, die encodieren soll.
Jetzt ueber IsProcessorFeaturePresent (kernel32, winnt.h dokumentiert) plus
CPU-Name aus der Registry. Auf dem Commander-PC gegengeprueft:
vorher: AMD64 Family 26 Model 68 Stepping 0 / Vektorbefehle unbekannt
jetzt: AMD Ryzen 7 9700X 8-Core Processor / avx512f / 16 Kerne
Damit verschwindet die AVX2-Warnung fuer diesen Worker von selbst - genau das
hatte der Commander gefordert.
## 4. Ehrliche Fehlermeldung statt "Keine Roh-MKVs gefunden"
Neues _erreichbarkeit_pruefen() unterscheidet jetzt drei Faelle statt einem:
Pfad nicht erreichbar UND kein Mapping gesetzt (nennt RIPPY_PATH_MAP samt
Beispiel), Mapping gesetzt aber greift nicht, oder Pfad einfach nicht da. Und
es prueft BEIDE Pfade - im Fall des Commanders lag die Quelle auf der
erreichbaren NAS, das ZIEL aber auf der VM-Platte ohne Samba; das waere erst
beim Schreiben nach Stunden Rechenzeit aufgefallen.
Jede Meldung sagt jetzt ausdruecklich, dass die Rohdaten NICHT verloren sind
und "Neu komprimieren" genuegt.
## 5. Persoenliche Werte raus
Vier Platzhalter trugen die Umgebung des Commanders (192.168.178.20,
TOBIS-PC, seine Jellyfin- und Rippy-IP). In einem Repo, das weitergegeben
wird, hat das nichts verloren.
Nebenbei dreimal in die eigene Falle getappt: deutsche Anfuehrungszeichen in
DOPPELT gequoteten Python-Strings beenden den String vorzeitig. Der Bestand
nutzt dafuer einfach gequotete f-Strings - jetzt auch meine.
NOCH OFFEN und wichtig: RIPPY_PATH_MAP wird von niemandem GESETZT (Installer,
UI, Compose). Ein Windows-Worker kann damit grundsaetzlich nicht transcodieren.
Das ist der naechste Schritt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander-Rueckmeldung: Umlaute fehlen. Ausloeser war sichtbar der Installer -
"Diese Maschine uebernimmt die Video-Kompression fuer Rippy" stand woertlich im
Screenshot.
## Die .ps1-Falle war loesbar, nicht unumgehbar
Bisher galt: ausgelieferte .ps1 MUESSEN ASCII sein, weil PowerShell 5.1 sie
ohne BOM als ANSI liest. Das ist nur die halbe Wahrheit - gemessen mit echtem
powershell.exe 5.1:
ohne BOM: $s = "Größe: äöü" + Unerwartetes Token -> Skript kaputt
mit BOM: Groesse: aeoeue + laeuft, Length 16 korrekt
Alle drei Skripte sind jetzt UTF-8 MIT BOM und tragen echte Umlaute; unter 5.1
gegengeprueft (BOM vorhanden, Parser fehlerfrei, Text korrekt gelesen). Der
Kopfkommentar sagt das jetzt richtig statt "ASCII-only".
## Umstellung: Text ja, Bezeichner nein
Umlaute gehoeren in Kommentare und Anzeigetexte, nicht in Funktionsnamen oder
Datenschluessel. Deshalb je Sprache das passende Werkzeug:
- Python: ueber den TOKENIZER - angefasst wurden ausschliesslich COMMENT- und
STRING-Tokens. 156 Stellen. Code ist damit garantiert unberuehrt.
- TypeScript: nur // und /* */ Kommentare sowie JSX-Text (kann per Definition
kein Bezeichner sein). 24 Stellen. `const waehlen`, `let laeuft`, `plaetze`,
`GeraetInfo` sind nachweislich unversehrt.
- install.sh: Anzeigetext, aber die Shell-Funktionen (gruen/rot/gelb/titel) und
der Schalter --nur-pruefen bleiben ASCII - das sind Schnittstellen.
- Markdown: 0 Aenderungen, die Doku hatte schon Umlaute.
ZWEI FEHLER MEINES KONVERTERS, beide von Werkzeugen gefangen:
1. In f-Strings steht in {...} CODE, kein Text. Aus f"{groesse}" wurde
f"{größe}", waehrend die Variable groesse hiess - Ruff meldete F821
"Undefined name". Der Konverter lagert Einsetzungen jetzt aus.
2. Ein Dict-Schluessel wurde umbenannt: die Wortliste enthaelt das PRAEFIX
"uebersprung", der Tabu-Schutz prueft aber ganze Woerter. bericht[...] ist
wieder ASCII - Umlaute in Datenschluesseln brechen JSON-Runden und DB-Felder.
## Installer im Rippy-Look
Statt hellgrau jetzt dieselben Toene wie das Web-UI (aus lib/design.ts
uebernommen): slate-900 Flaeche, dunkle Eingabefelder, Amber-Hauptknopf wie
"Los geht's" im Wizard. Oben ein Kopfbereich mit dem Farbverlauf
amber -> indigo -> purple und dem Disc-Symbol - in WinForms per Paint-Ereignis
gezeichnet, weil es dort keine Verlaeufe von der Stange gibt.
Geprueft, nicht gehofft: Das Fenster wurde headless in ein PNG gerendert
(DrawToBitmap) und angesehen - Verlauf, Symbol, Umlaute und Farben sitzen.
RippyWorkerSetup.exe neu gebaut (57344 -> 60416 Bytes); Umlaute und das
requireAdministrator-Manifest sind in der .exe verifiziert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander-Wunsch: nicht ins Benutzerprofil, sondern wie jedes andere Programm
nach C:\Program Files - und selbst waehlbar, mit diesem Standard.
## Was neu ist
- Feld "Installieren nach" mit Knopf "Aendern ..." (Ordner-Dialog). Standard
ist [Environment]::GetFolderPath("ProgramFiles") + "Rippy Worker" - der
ECHTE Pfad; deutsche Windows-Versionen zeigen im Explorer "Programme", der
Pfad heisst trotzdem "Program Files".
- Der Ordner-Dialog liefert den ELTERN-Ordner, "Rippy Worker" haengt der
Installer selbst an. Sonst koennte die Deinstallation einen fremden Ordner
mitloeschen - sie macht Remove-Item -Recurse auf das Zielverzeichnis.
- install.ps1 bekommt -InstallDir mit demselben Standard.
## Drei Folgen, die mitbehandelt werden mussten
1. RECHTE. In C:\Program Files darf nur ein Administrator schreiben. Die .exe
traegt jetzt ein requireAdministrator-Manifest (-requireAdmin in
build-exe.ps1), fragt also beim Doppelklick EINMAL per UAC - danach stimmen
die Rechte fuer alles Weitere. Verifiziert: "requireAdministrator" steckt im
Manifest, "asInvoker" nicht mehr. Zusaetzlich prueft der Installer VOR dem
Entpacken, ob er dort schreiben darf, und nennt bei Nein die zwei Wege
(als Administrator starten ODER Ziel ins eigene Profil legen) - statt
irgendwo tief "Zugriff verweigert" zu produzieren.
2. DAS LOG. tray.py schrieb worker.log NEBEN das Programm. Unter Program Files
darf ein normaler Benutzer das nicht - der Worker waere beim Starten
gescheitert, und zwar still. Das Log liegt jetzt in
%LOCALAPPDATA%\Rippy Worker\worker.log, also da, wo veraenderliche Daten
hingehoeren. Rueckfall auf das Programmverzeichnis bleibt fuer
Installationen ins eigene Profil.
3. DER AUTOSTART. Bei einer Installation unter Programme ist es eine
Installation FUER DIE MASCHINE - der Autostart geht deshalb in den
Autostart-Ordner aller Benutzer (CommonStartup). Das loest zugleich ein
Rechte-Problem: erhoeht man ueber ein FREMDES Administratorkonto, waere
GetFolderPath("Startup") der Ordner dieses Admins, also der falsche. Bei
einem Ziel im eigenen Profil bleibt es persoenlich. uninstall.ps1 raeumt
beide Orte ab.
## Geprueft
- Leerzeichen im Pfad: die erzeugte .bat mit cd /d "%~dp0", PATH-Erweiterung
und relativem Aufruf laeuft aus "C:\...\Rippy Test Ordner" korrekt durch.
- Alle drei .ps1 ASCII-rein und fehlerfrei geparst.
- RippyWorkerSetup.exe neu gebaut (52736 -> 57344 Bytes, Version 1.0.1.0).
UI: Der Copy-Paste-Befehl fuer den CLI-Weg sagt jetzt, dass PowerShell als
Administrator laufen muss, und nennt -InstallDir als Ausweg ohne Adminrechte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Loest die offene Frage aus dem Savepoint ("UHD gar nicht komprimieren?") und
nimmt dem Wizard die Falle, in die er bisher fuehrte.
## Kompression je Disc-Typ abwaehlbar
Bisher gab es nur den globalen Schalter transcodeEnabled: alles komprimieren
oder nichts. Wer 4K verlustfrei behalten und DVDs trotzdem schrumpfen wollte,
hatte keine Moeglichkeit - obwohl genau das die vernuenftige Einstellung fuer
diese Maschine ist (4K-HEVC = gemessene 28-55 h je Film ohne AVX2).
Jetzt kann das Preset eines Disc-Typs auf den Reservewert "keine" stehen, dann
bleibt die verlustfreie Datei aus dem Rip stehen. Neue reine Funktion
komprimieren_fuer(disc_type, einstellungen); der globale Schalter schlaegt
weiter alles. preset_fuer() ueberspringt den Reservewert bewusst und gibt ihn
NIE als Preset-Namen zurueck - sonst bekaeme HandBrake `--preset keine` und
wuerde scheitern. Wer ueber "Neu komprimieren" ausdruecklich doch komprimieren
will, bekommt so ein brauchbares Preset statt eines Fehlers.
Der Reservewert kollidiert mit keinem echten Namen: gegengeprueft gegen alle
90 Presets aus `HandBrakeCLI --preset-list` im Worker-Image. Bei der
Gelegenheit auch die fuenf im UI angebotenen Namen geprueft - alle echt.
## Der Wizard empfiehlt nach GEMESSENER Rechenleistung
Vorher stand H.265 als Standard drin. Auf einer CPU ohne AVX2 sind das ein bis
zwei Tage pro 4K-Film - genau der Lauf, der am 25.07.2026 abgebrochen werden
musste. Der Wizard hatte die Zahlen sogar schon vorliegen (/capabilities
meldet cpu_simd und cpu_kerne), nur benutzt hat er sie nicht.
Jetzt: schwache CPU -> 4K wird nicht komprimiert, Blu-ray/DVD gehen auf H.264
(schneller als H.265). Starke CPU oder Hardware-Encoder -> H.265 durchgehend.
Der Wizard schreibt dabei ALLE vier Preset-Felder, nicht nur das allgemeine -
vorher fiel 4K auf ein 1080p-Preset zurueck und die Aufloesung war weg.
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" (dort gibt es kein /proc/cpuinfo) - dann wird
geschwiegen statt falsch gewarnt. Auf Windows gegengeprueft: 16 Kerne und
CPU-Modell kommen korrekt durch, encoders ist ohne HandBrake leer.
## Weitere Wizard-Haerten
- Kasten "Was Rippy gerade sieht": Laufwerk, Worker (mit Kernen/SIMD), freier
Platz. Jede Zeile hat bei Problemen eine HANDLUNGSANWEISUNG statt nur eines
Kreuzes - kein Laufwerk, kein Worker und wenig Platz sind die drei Faelle, in
denen man vorher ratlos dastand.
- Laedt alle drei Quellen parallel (Promise.allSettled) und wiederholt im
5-s-Takt: beim ersten Start laeuft der Worker noch hoch, vorher stand dort
dauerhaft "Noch kein Worker gemeldet" ohne Aussicht.
- API-Keys sind SICHTBAR statt als Punkte: das sind kopierte Keys, keine
Passwoerter, und einen Tippfehler sieht man in Punkten nicht.
- Keys werden direkt nach dem Speichern geprueft (/metadata/status). Ein
falsch kopierter Key faellt sofort auf, statt erst beim ersten Rip als
"Unknown Disc" - mit der Wahl "Key korrigieren" oder "Trotzdem fertigstellen".
## Einstellungen -> Verarbeitung
Alle drei Preset-Auswahlen bekommen "Nicht komprimieren", und beim 4K-Feld
erscheint die AVX2-Warnung mit der gemessenen Zahl - aber nur, wenn die
Maschine sie wirklich braucht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Modulkopf von caps.py verspricht "ehrlich erkannt, nicht behauptet" -
erkenne_encoder() tat aber drei Mal das Gegenteil:
1. cpu-x264 und cpu-x265 standen fest verdrahtet in der Liste ("immer
dabei"). Ein Rip-Worker OHNE HandBrake behauptete damit, komprimieren zu
koennen; jeder Transcode dort endete sofort mit "nicht installiert".
2. 'vaapi' wurde allein wegen /dev/dri gemeldet, ohne zu pruefen, ob
HandBrake das ueberhaupt kann. Auf der VM gemessen: das Worker-Image
kennt svt_av1/x264/x265/mpeg4/mpeg2/VP8/VP9/theora und KEINEN einzigen
Hardware-Encoder. Rippy haette VAAPI versprochen und dann versagt.
3. Ueber die Rechenleistung sagte es gar nichts. Genau deshalb war nicht zu
sehen, dass ein 4K-Encode auf dieser Maschine Tage braucht: CPU-Modell
ist das generische "QEMU Virtual CPU version 2.5+", grep -c avx2
/proc/cpuinfo = 0, nur bis sse4_2. x265 lebt von AVX2. Gemessen an der
Leseposition des Quellstroms: 28-55 h fuer einen Film, 3,84 von 4 Kernen
gesaettigt. Abhilfe waere der CPU-Typ 'host' in Proxmox.
Jetzt wird die Encoder-Liste aus `HandBrakeCLI --help` geparst (Abschnitt
-e/--encoder, Formatstrings im Worker-Image gemessen - AGENTS Regel D), und
Hardware zaehlt nur, wenn Geraet UND HandBrake-Unterstuetzung da sind. Neu
gemeldet: cpu-av1 (svt_av1 ist wirklich verfuegbar) sowie CPU-Modell,
Kernzahl und Vektorbefehlsstufe je Worker.
UI: Kerne/Vektorbefehle stehen bei jedem Worker (Worker-Tab und
Einstellungen -> System), dazu die ungefilterte HandBrake-Auskunft und eine
sichtbare Warnung, wenn AVX2 fehlt - inklusive des Hinweises auf den
VM-CPU-Typ. Der Warntext liegt in lib/encoder.ts, damit er nicht doppelt
im Code steht.
Mit im gleichen Zug (dieselbe Datei): der Schalter "Alle Tracks rippen" ist
entfallen. Er war ein Placebo - abcde bekommt keine Track-Auswahl und es gab
auch keine Oberflaeche, um eine Teilmenge zu waehlen. Eine Audio-CD wurde
also immer vollstaendig gerippt, egal wie der Schalter stand. An seiner
Stelle steht jetzt die Wahrheit.
7 Tests, Testdaten sind echte HandBrake-Ausgaben von der VM. Der Parser ist
zusaetzlich gegen die vollen 697 Zeilen der echten --help gegengeprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander-Vorgabe 25.07.2026: "Du sollst es nicht automatisch setzen, es soll
auswaehlbar sein - z. B. beim Rippen starten, oder VORHER wenn Vollautomatik
eingeschaltet ist."
Bisher gab es nur die globale Einstellung workDir, und die war ein freies
Textfeld - man musste den Container-Pfad (/app/media/...) kennen. Beim
Akira-Rip landeten deshalb 74 GB Rohdaten auf der 148-GB-VM-Platte, obwohl
eine NAS-Freigabe mit 2,3 TB eingehaengt war.
- RipTargetModal: neue Auswahl "Arbeitsverzeichnis fuer die Rohdaten",
gespeist aus /storage-targets (mit Kennzeichnung als Netzwerk-Freigabe und
freiem Platz je Ziel). Leer = "Standard aus den Einstellungen", der Wert
wird zur Orientierung mit angezeigt. Bei Musik ausgeblendet - CD-Rips gehen
direkt als FLAC ins Ziel, ohne Roh-Zwischenstufe.
- POST /jobs nimmt work_dir entgegen, mit derselben Pfad-Haerte wie das Ziel
(_validiere_ziel: muss unter /app/media liegen), und legt es in die
Job-Metadaten.
- _arbeitsverzeichnis(einstellungen, job_wahl) im Worker: Wahl dieses Rips ->
Setting -> Container-Default. Damit bleibt die Einstellung genau das, was
bei Vollautomatik-Rips greift, weil dort niemand gefragt wird. Der Text im
Einstellungen-Tab sagt das jetzt auch so.
- posixpath statt os.path in _arbeitsverzeichnis: das sind immer
Container-Pfade, auch wenn ein nativer Windows-Worker das Modul laedt (der
uebersetzt erst spaeter per pfad_lokal). os.path.normpath machte unter
Windows Backslashes daraus, wodurch die MEDIA_ROOT-Pruefung nicht mehr
griff - lokal als Testfehler aufgefallen.
- Test deckt die Reihenfolge und die Ausbruchsversuche ab.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei Befunde aus dem ersten echten UHD-Durchlauf (Akira). Der Rip und die
Kompression liefen sauber durch - danach hat "Original behalten" die Platte
vollgeschrieben und den fertigen Job als fehlgeschlagen markiert.
1. ORIGINAL-AUFHEBEN DARF DEN JOB NICHT MEHR TOETEN (tasks.py)
Vorher stand dort ein nacktes shutil.move(raw_dir, ziel). Arbeits-
verzeichnis (/app/temp, Docker-Volume) und Ziel (/app/media, Bind-Mount)
sind VERSCHIEDENE Dateisysteme - os.rename scheitert mit EXDEV, shutil.move
faellt auf Kopieren zurueck. Ergebnis am 25.07.: 74-GB-Vollkopie auf
dieselbe Platte, Abbruch bei 41 GB mit ENOSPC, Platte 100 % voll, Worker-
Container startete nicht mehr ("failed to mount: no space left on device"),
und der Job galt als FEHLGESCHLAGEN - obwohl die komprimierte Datei
(4,8 GB) fertig und in Ordnung war. Der Nutzer sah nur eine leere Queue.
Jetzt: _original_aufheben() prueft erst, ob ueberhaupt kopiert werden muss
(gleiches Dateisystem -> reines Umhaengen), prueft sonst den freien Platz
VORHER, faengt jeden OSError ab, raeumt eine halbe Kopie weg und meldet das
als WARNUNG. Der Job bleibt erfolgreich, die Roh-Datei bleibt liegen.
Zwei Tests decken beide Wege ab.
2. ARBEITSVERZEICHNIS IST JETZT WAEHLBAR (Settings.tsx)
Es war ein freies Textfeld - man musste den Container-Pfad (/app/media/...)
KENNEN, um eine Netzwerk-Freigabe zu treffen. Genau daran ist es
gescheitert, weshalb der 74-GB-Rohschnitt ueberhaupt erst auf der VM-Platte
landete. Jetzt eine Auswahl aus /storage-targets (dieselbe Liste wie bei
den Speicherzielen), inklusive Kennzeichnung als Netzwerk-Freigabe und
freiem Platz je Ziel.
3. DIALOGE KLEBTEN IM PANEL (ui/Modal.tsx)
"Rippen starten" im Laufwerke-Tab oeffnete den Dialog INNERHALB des
Bereichs, teils abgeschnitten. Ursache: .glass-panel in index.css setzt
backdrop-filter: blur(16px), und ein Element mit backdrop-filter wird zum
Bezugsrahmen fuer position: fixed seiner Nachfahren - das Modal war damit
an der Card ausgerichtet statt am Fenster. Modal rendert jetzt per
createPortal an document.body. Behebt es fuer ALLE Dialoge auf einmal.
Aufgeraeumt: die abgebrochene 41-GB-Teilkopie unter
"/app/media/movies/Akira (1988)/original/" geloescht (nachweislich
unvollstaendig - 41 GB gegen 79,6 GB Quelle, Quelle intakt). Platte wieder
bei 78 %, Container laufen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Commander-Einwand: .vbs ist abgekuendigt (Win11 24H2 Feature-on-Demand) und
wird oft als gefaehrlich geflaggt. Loesung: echte .exe.
- RippyWorkerSetup.exe (50 KB): install-gui.ps1 via ps2exe kompiliert —
eingebettetes Rippy-Disc-Icon (deploy/worker-windows/rippy.ico), kein
Konsolenfenster (-noConsole), WinForms (-STA). E2E getestet: Fenster
oeffnet, conhost-Zaehler unveraendert (kein Konsolenfenster).
Vorgebaut auf Windows (build-exe.ps1) + committet — eine Windows-.exe
kann nicht von Linux (Rippy-Host) gebaut werden. WICHTIG: friert KEINE
Versionen ein — HandBrake wird zur Laufzeit (GitHub latest) und der
Worker-Code live von Rippy geholt; nur bei GUI-Aenderungen neu bauen.
- API: GET /worker-setup/windows-exe (FileResponse); Dockerfile kopiert die
.exe ins Image. .gitattributes: *.exe/*.ico als binary.
- UI (Worker -> Windows): 'Installer herunterladen (.exe)' als Haupt-Weg
(generisch, GUI fragt Adresse ab; die einzutragende IP wird als Hinweis
angezeigt). .vbs-Erzeugung entfernt. CLI bleibt Profi-Alternative.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commander-Wunsch: kein Konsolenfenster + Icon.
- Starter ist jetzt eine .vbs (statt .bat): wscript startet PowerShell mit
Fensterstil 0 = KOMPLETT versteckt, kein Konsolenfenster. Nur das
WinForms-Installer-Fenster erscheint. Download-Datei: rippy-worker-setup.vbs
(weiter CLIENT-seitig mit eingetragener LAN-IP erzeugt).
- install-gui.ps1: Fenster-Icon = Rippy-Disc (zur Laufzeit gezeichnet,
Indigo-Kreis + weisses Loch) — erscheint in Titelleiste + Taskleiste.
(Die Datei selbst kann Windows-bedingt kein Icon tragen — nur .exe/.lnk.)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commander-Wunsch: die CLI-Installation ist grausam. Jetzt:
- install-gui.ps1 (WinForms, ASCII-only): Fenster mit Feldern (Rippy-IP,
Worker-Name, Autostart-Checkbox), Install-Knopf mit Live-Log, danach
'Worker starten'-Knopf. Gleiche Schritte wie der CLI-Installer
(Worker-Code + HandBrake latest + venv + Tray/Start/Uninstall).
Braucht nur Python 3.10+, keine externe GUI-Runtime.
- API: GET /worker-setup/windows-gui serviert die GUI; Dockerfile kopiert
sie ins worker_dist/.
- UI (Worker-Tab, Windows): 'Grafischen Installer herunterladen (.bat)' als
Haupt-Weg — die .bat wird CLIENT-seitig mit der eingetragenen LAN-IP
erzeugt (Blob-Download), Doppelklick laedt+startet die GUI von Rippy.
Der CLI-Befehl bleibt als Profi-Alternative.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1) Speicher-Mounts robust (Befund: toter CIFS-Mount nach NAS-Ausfall/Rebuild —
mounted:false, verschwand aus 'Verfuegbare Ziele', Neu-Anlegen -> 409, man
sass fest):
- mounts.py: ist_erreichbar() (listdir, soft-Mount bricht schnell ab),
ist_gemountet() faengt OSError toter Mounts, aushaengen() mit
umount -l Fallback, reparieren() (lazy abhaengen + frisch mounten).
- /storage-mounts liefert 'reachable'; POST bei existierendem Namen:
aktiv -> 409, tot -> automatische Reparatur mit neuen Angaben;
neuer POST /storage-mounts/{name}/repair (gespeicherte Zugangsdaten).
- /storage-targets crasht nicht mehr an totem Mount (os.path.ismount
OSError abgefangen).
- UI: eigene 'Netzwerk-Mounts'-Liste mit Status (aktiv/nicht erreichbar/
getrennt) + Reparieren- und Entfernen-Knopf — tote Mounts sind sichtbar
und wiederherstellbar statt zu verschwinden.
2) HandBrake-Versionen konsistent (Befund: Docker 1.6.1, Windows-Skript
fest 1.9.2, Update-Check meldet 1.11.2 — verwirrend):
- Windows-Installer zieht jetzt DYNAMISCH die neueste Version (GitHub
latest, Fallback 1.11.2) — passt zum Update-Check.
- Update-UI erklaert klar: Docker = stabiles Debian-Paket (bewusst aelter,
kein Fehler), Windows = neueste. MakeMKV-Update zeigt den Befehl.
3) Encoder-/Worker-Auswahl beim Rip (Feature):
- Celery worker_direct=True: jeder Worker konsumiert zusaetzlich seine
Direkt-Queue. API-Helper transcode_queue(node) routet gezielt an den
gewaehlten Worker, faellt aber sicher auf die geteilte transcode-Queue
zurueck, wenn er offline ist (kein Haengenbleiben).
- /capabilities liefert den Celery-Node je Worker; POST /jobs nimmt
transcode_node (-> Job-meta); rip_disc + retry-transcode routen danach.
- Rip-Dialog: Encoder-/Worker-Dropdown, sichtbar ab 2 Online-Workern.
- ping_worker-Task zum Verifizieren des gezielten Routings.
- Nebenfund gefixt: DeviceDiscovery leitete die Titel-Auswahl (titles)
gar nicht an die API weiter — jetzt titles + transcode_node.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Befund (Commander, Screenshot): Das Feld 'Rippy-Adresse' fuellte sich
automatisch mit window.location.hostname — also der Adresse, ueber die man
das UI geoeffnet hat (bei ihm die eigene PC-IP hinter einem Forward/Proxy,
192.168.178.98). Das ist NICHT zwingend die direkte LAN-IP der
Rippy-Maschine, die der Worker fuer Redis/Postgres (6379/5432) braucht —
der generierte Befehl zeigte damit auf den falschen Host ('Verbindung
verweigert').
Fix:
- Kein Auto-Default mehr (Feld leer); leeres Feld -> sichtbarer
Platzhalter <RIPPY-IP> im Befehl (erkennbar unvollstaendig statt still
falsch).
- Label auf 'LAN-IP der Rippy-Maschine (Docker-Host)' geschaerft +
dauerhaft sichtbarer Hilfetext: die IP der Rippy-Maschine, NICHT dieses
PCs, und auch dann die direkte LAN-IP wenn das UI ueber Domain/Proxy
geoeffnet wurde.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- tray.py (nur nativer Windows-Worker; Docker unberuehrt): pystray+Pillow-
Tray neben der Uhr — Status, Worker starten/stoppen, Rippy oeffnen,
Log anzeigen, Beenden (stoppt den Worker mit). Celery laeuft als
Kind-Prozess ohne Konsolenfenster (CREATE_NO_WINDOW), Log in worker.log.
- install.ps1 erzeugt jetzt start-tray.bat (pythonw, empfohlen),
start-worker.bat (Konsole/Debug) und uninstall.ps1: stoppt alle
Prozesse aus dem Ordner, loescht die Autostart-Aufgabe, meldet den
Worker per DELETE /workers/{name} in Rippy ab und entfernt den Ordner
komplett. -Autostart registriert die Tray-Variante (onlogon).
- UI-Beschreibung + Anleitung entsprechend ergaenzt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1. Update-Erkennung (Commander-Frage 'wie kriegen wir Updates mit?'):
GET /system/updates vergleicht installierte Versionen (Worker-Info) mit
makemkv.com (Versionsnummer der Download-Seite) und der HandBrake-
GitHub-Release-API (releases/latest), 12 h gecacht. Einstellungen ->
System: 'Auf Updates prüfen' + fertiger Update-Befehl bei MakeMKV.
Selbst-Update gibt es bewusst NICHT (waere Docker-Socket-Zugriff);
dafuer ist MAKEMKV_VERSION jetzt per .env uebersteuerbar — Update ohne
Code-Aenderung. HandBrake-Abweichung wird als Info dargestellt
(Debian-Paket hinkt der offiziellen Version bewusst hinterher).
2. Worker-Anbindung: editierbares Adressfeld statt stillschweigend
window.location.hostname — Befund: ueber die externe Domain kopierte
Befehle liefen in den Reverse-Proxy (401) und Worker koennen Redis/
Postgres eh nur im Heimnetz erreichen. Amber-Warnung bei oeffentlich
aussehender Adresse, Anleitung ergaenzt.
3. 'Einstellungen speichern' ist im Worker-Tab ausgeblendet — dort gibt
es nichts zu speichern, der Knopf verwirrte nur.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Volle Titel-Auswahl vor dem Rip (Etappe-12-Rest):
- worker.tasks.scan_tracks: makemkvcon info -> Titel-Liste (Dauer/Groesse/
Kapitel, apdefs-Attr 8/9/11) in die settings-Tabelle 'tracks:<device>';
API: POST /devices/{n}/scan-tracks + GET /devices/{n}/tracks (Polling).
- Rip-Dialog: 'Disc scannen' -> Tabelle mit Checkboxen, Vorauswahl ab
5 min, Groessen-Summe; gewaehlte Titel als meta.titles in den Job.
- rip_titel_auswahl: ein makemkvcon-Aufruf je Titel (mkv kann nur einen
Titel oder all), Fortschritt anteilig. Parser-Tests dabei.
Nativer Windows-Worker OHNE Docker (Commander-Wunsch):
- Die Rippy-Instanz versorgt sich selbst: GET /worker-setup/windows
liefert install.ps1, /worker-setup/paket den Worker-Code als Zip
(api-Dockerfile kopiert docker/worker/*.py als worker_dist/ ins Image).
- install.ps1: Python-Check, venv + requirements, HandBrakeCLI 1.9.2 vom
offiziellen GitHub-Release (URL per HEAD verifiziert), start-worker.bat
mit celery -Q transcode --pool=solo (Celery-Doku: Windows nur solo),
optional -Autostart via schtasks onlogon.
- tasks.py laedt jetzt auch ohne fcntl (Import-Guard) — rip_disc ist auf
solchen Workern hart verriegelt (Klartext-Fehler statt Crash).
- Einstellungen -> Worker: Varianten-Umschalter Linux(Docker)/Windows(nativ)
mit passenden Copy-Paste-Befehlen.
Anleitungs-Tab: Rippy erklaert sich selbst (Disc-Weg, Serien, NAS,
Media-Server, Benachrichtigungen, Key, Worker, Downloads, FAQ).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
- GET /jobs/{id}/files: Dateiliste aus job.output_path (Name + Groesse)
- GET /jobs/{id}/files/{name}: FileResponse-Stream; Validierung strikt —
output_path muss unter /app/media liegen, nackter Dateiname (kein
Slash/.., kein Dotfile), realpath-Check gegen Symlink-Ausbrueche.
Mit Test (test_dateiname_validierung_blockt_pfad_tricks).
- UI: 'Download'-Knopf in der Aktion-Spalte bei fertigen Jobs; die
Dateiliste mit Groessen + Download-Links lebt im Job-Detail-Popup
(ein Dropdown wuerde im overflow-x-auto-Tabellencontainer clippen).
- nginx: proxy_buffering off + proxy_read_timeout 3600s waren fuer SSE
schon gesetzt — grosse Downloads brauchen keine Aenderung.
Wunsch aus der Uebernahme-Session (Commander-Sammelliste 24.07.).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commander-Wuensche (23.07. Nacht):
- "Nicht korrekt?!"-Knopf direkt an der Disc-Karte: Korrektur-Suche als
Popup (MetadataKorrektur) — kein Seitenwechsel mehr noetig; Wahl gilt
per Disc-Fingerabdruck weiter 30 Tage
- Einstellungen -> Worker: verbundene Encoding-Maschinen mit ECHTER
Erreichbarkeit (Celery-Ping + minuetlicher Herzschlag/last_seen),
Encoder-Badges, Copy-Paste-Anbindung fuer neue Maschinen
(Linux/Docker + Docker Desktop; SSH-Ein-Klick als Ausbaustufe)
- JSX-0-Falle gefixt: {0 && ...} renderte nackte "0" unter GENRE/LAUFZEIT
- ruff.toml: Regelsatz gepinnt — lokal aktualisierte Ruff-Version zog
ploetzlich Stilregeln und faerbte den Baum rot; Ampel und lokal
urteilen jetzt identisch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
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>
Commander-Befund: Disc wurde erkannt, aber das UI verriet nicht WAS.
- Watcher stoesst beim Einlegen (und beim Start mit bereits eingelegter
Disc) automatisch den Pre-Scan an, Ergebnis landet im DISC_CACHE und
im Log ("Disc erkannt: Titel (Jahr) [typ, Confidence]")
- GET /devices liefert das disc-Feld mit Titel/Jahr/Typ/Confidence/Poster
- UI: prominente Karte "Im Laufwerk erkannt" mit Poster (TMDB relativ
oder OMDb absolut), Beschreibung und Rippen-Start (Ziel-Dialog)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- POST /jobs nimmt target_dir (validiert unter /app/media, .. fliegt raus)
- GET /storage-targets: Verzeichnisse unter /app/media inkl. Mount-Flag
und freiem Platz — NFS/SMB-Shares unter /srv/rippy/media erscheinen
dank rslave-Bind automatisch
- jobs.target_dir (Mini-Migration via ADD COLUMN IF NOT EXISTS)
- Worker: rip_disc(target_dir) mit eigener Validierung; CD/Video/
Transcode-Pfade legen im gewaehlten Ziel ab
- UI: "Rippen starten" oeffnet den Ziel-Dialog (Filme/Serien/Musik oder
eigener Pfad); Modal-Bugfix: customPath wurde still ignoriert
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
Vorher lief der Vite-ENTWICKLUNGSSERVER als Produktion, und vier Dateien
riefen hartkodiert http://localhost:8000 auf — vom PC aus fragte der Browser
damit den PC selbst nach Laufwerken (deshalb blieb das UI immer leer);
Settings nutzte ein nie gesetztes VITE_API_URL. Die Logs-Seite zeigte fest
einprogrammierte Fantasie-Einträge ("The Matrix gerippt").
- lib/api.ts: axios-Instanz mit baseURL /api; nginx proxied /api → api:8000
(ein Origin, kein CORS), SSE-tauglich (proxy_buffering off)
- Dockerfile: Vite-Build → nginx:alpine statt npm run dev
- Logs + LiveLog von echtem /logs-Endpoint; MetadataPreview: echte Laufwerke
aus /devices statt erfundener /dev/bluray-Optionen, startet echten Job
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- DeviceDiscovery: auto device detection with type and status
- LogsPage: comprehensive logs view with filters and stats
- RipTargetModal: target directory selection wizard
- ConfirmDialog: generic confirmation dialog component
- LiveLogSection: real-time job progress and logs
- Update App.tsx to include new pages