Der im Uebergabestand als "laeuft" beschriebene 4K-Lauf wurde um 18:31 vom
Commander abgebrochen. Damit war die Aussage im Block darueber ueberholt -
nachgezogen, statt sie stehen zu lassen.
Zwei Befunde, beide gemessen:
1. 4K-HEVC ist auf dieser CPU nicht machbar: 18:02 bis 18:31 ergab 1 Prozent,
hochgerechnet rund 50 Stunden fuer den Film. Zu entscheiden: UHD gar nicht
komprimieren (Roh-MKV behalten), Hardware-Encoder (Remote-Worker mit GPU),
oder bewusst bei 1080p bleiben. Preset-je-Disc-Typ ist richtig, reicht
allein aber nicht.
2. Abbruch wirkt verzoegert (FEHLER, nicht behoben): Job steht seit 18:31 auf
"canceling", HandBrake lief um 18:35 immer noch. Der Abbruch wird nur in
datei_fortschritt geprueft, und die Closure laeuft nur bei geaenderter
Prozentzahl ("if gesamt == letzter[0]: return"). Bei 1 Prozent alle 30
Minuten sieht "Abbrechen" bis zu eine halbe Stunde lang wirkungslos aus.
Der Abbruch gehoert unabhaengig vom Fortschritt geprueft.
Ausserdem festgehalten: die 1080p-Fassung wurde von HandBrake beim Start auf
0 Bytes gekuerzt, im Akira-Ordner liegt derzeit ein unbrauchbares Fragment.
Der 75-GB-Rohschnitt ist unversehrt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Uebergabe an eine neue Sitzung. Der Block trennt bewusst, was per Befehl auf
der VM geprueft wurde, von dem was offen bzw. nur erwartet ist - inklusive
zweier Fehlschluesse dieser Sitzung, damit die naechste Sitzung weiss, welchen
Aussagen sie nicht blind glauben soll.
Kern:
- Ein 4K-HandBrake-Lauf laeuft GERADE (Akira, Preset "H.265 MKV 2160p60 4K").
Die vorherige 1080p-Datei wurde dabei auf 0 Bytes gekuerzt; der 75-GB-
Rohschnitt liegt unversehrt in /app/temp/raw.
- progress=99 am Job ist ein Altwert aus dem Absturz, NICHT der echte Stand.
- Noch nicht bewiesen: dass _original_aufheben() im Ernstfall nur warnt statt
die Platte vollzuschreiben. Genau dieser Pfad ist neu.
- Gefunden, nicht gebaut: Zombie-Erkennung. Nach dem Absturz stand der Job auf
"transcoding", obwohl kein Prozess lief und beide Celery-Queues leer waren.
_kann_neu_komprimieren verlangt status == "failed", deshalb fehlt dem Nutzer
auch der "Neu komprimieren"-Knopf.
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>
Rueckfrage des Commanders beim ersten echten UHD-Rip: "merkt Rippy, dass es
eine UHD ist, und nimmt direkt das 4K-Preset?" Antwort war nein. Die
Kompression fragte den Disc-Typ ueberhaupt nicht:
preset = einstellungen.get("transcodePreset") or DEFAULT_HB_PRESET
Live eingestellt war "HQ 1080p30 Surround". Der gerade laufende Akira-Rip
waere also verlustfrei in 4K gerippt und danach auf 1080p heruntergerechnet
worden - und mit keepOriginal=False waere der 4K-Rohschnitt danach geloescht
worden. Umgekehrt wurde eine DVD auf 1080p hochskaliert, was nichts bringt.
- preset_fuer(disc_type, einstellungen) in ripping.py, pure und getestet.
Reihenfolge: Preset des Disc-Typs -> allgemeines transcodePreset ->
DEFAULT_HB_PRESET. Bestandsinstallationen aendern ihr Verhalten NICHT,
solange die neuen Felder nicht gespeichert sind.
- Drei Einstellungen: transcodePresetDvd / transcodePresetBluray /
transcodePresetUhd. transcodePreset bleibt als Rueckfall bestehen.
- transcode_files holt den Disc-Typ aus dem Job-Datensatz und schreibt ihn
mit ins Log ("Disc-Typ 'uhd', Preset '...'").
- UI: drei Auswahlfelder statt einem, mit Klartext dazu, warum eine 4K-UHD
auf ein 2160p-Preset gehoert.
Preset-Namen stammen aus "HandBrakeCLI --preset-list" im Worker-Image
(HandBrake 1.6.1) - nicht aus dem Kopf (AGENTS Regel D):
H.265 MKV 2160p60 4K, HQ 2160p60 4K HEVC Surround,
Super HQ 2160p60 4K HEVC Surround, H.265 MKV 1080p30, HQ 1080p30 Surround,
Super HQ 1080p30 Surround, H.265 MKV 576p25, H.265 MKV 480p30,
HQ 576p25 Surround.
Sofortmassnahme am laufenden Job (auf Ansage des Commanders): keepOriginal
auf True gesetzt - nur dieses eine Feld, gegengeprueft dass kein anderer
Schluessel veraendert wurde. Damit ueberlebt der 4K-Rohschnitt die
Kompression.
ACHTUNG - Deploy bewusst NICHT ausgefuehrt: docker compose up -d --build
wuerde den Worker-Container neu erstellen und den laufenden Akira-Rip
abbrechen. Erst nach Abschluss des Jobs deployen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Richtigstellung des Vortags-Befunds. Dort stand, MakeMKVs Schluessel-Kanal
sei abgeschaltet. Das war FALSCH: die Herleitung stuetzte sich auf zwei
Hostnamen aus alten Forumsbeitraegen (hkdata.fairuse.org,
hkdata.crabdance.com), die zwar wirklich nicht mehr aufloesen, von MakeMKV
aber laengst nicht mehr benutzt werden. Aufgedeckt durch den Einwand des
Commanders, unter Windows ginge es sofort.
Gegenprobe mit demselben Laufwerk und derselben Disc (Akira UHD, MKB v76):
Linux (Worker) Windows
Verbindungen KEINE EINZIGE 185.84.108.20:443
Meldung 3338 nie "Downloading latest HK"
_private_data.tar 2048 B, 0 Keys 6,4 MB, 604 Keys
Disc volume key unknown TCOUNT:5, geht auf
Gegengeprueft mit leerem UND gefuelltem Speicher, mit und ohne --noscan,
mit dev:/dev/sr0 und disc:0, mit geloeschter update.conf. Linux fragt nie.
Die Meldungsvorlage "Downloading latest %1 to %2 ..." steckt sehr wohl im
Linux-Binary - sie loest nur nicht aus. Gleiches Symptom im MakeMKV-Forum,
seit Jahren offen (t=25782, t=34022). Der Dienst lebt; der Worker erreicht
185.84.108.20:443 sogar problemlos.
BEWIESEN: Nach Uebernahme des Windows-Schluesselspeichers oeffnet
makemkvcon auf der VM die Akira-UHD - "Operation successfully completed",
TCOUNT:5, fuenf Titel, identisch zum Windows-Ergebnis. Erster belegter
UHD-Disc-Zugriff auf der Rippy-Maschine.
- makemkv_daten.py (beide Zwillinge): zaehle_schluessel,
private_data_pruefen, schluesselspeicher_status, private_data_schreiben.
Die Pruefung lehnt einen Speicher OHNE hkd_*.bin ab - sonst laedt jemand
den leeren Vorrat einer frischen Installation hoch, nichts aendert sich,
und niemand versteht warum. Modulkopf komplett neu, inkl. der
Fehldiagnose als Warnung fuer spaeter.
- API: GET/POST /system/keystore. Der Rohkoerper der Anfrage IST die Datei
(binaer - JSON/Base64 waere Ballast, Multipart kann die API nicht).
Groessengrenze 64 MB = client_max_body_size in nginx.conf.
- UI: neuer Block "Disc-Schluessel fuer 4K-UHD" UEBER dem KEYDB-Block, mit
Schluessel-Anzahl, Upload und Anleitung fuer den Windows-Weg. KEYDB.cfg
ist jetzt als Notnagel beschriftet. Worker-Plakette zeigt die Anzahl;
0 heisst sichtbar "4K-UHD scheitert".
- tasks.py: UHD-Fehlertext sagt den Windows-Weg an und nennt die Anzahl
bekannter Schluessel dieses Workers.
- caps.py meldet schluessel je Worker.
- Alle Falschaussagen korrigiert: UI (3), Anleitung (2), README (3),
KONZEPT §8 + §10, Worker-Dockerfile, makemkv_key.py (dort stand "Den
AACS-Schluessel zieht MakeMKV via LibreDrive ohnehin selbst aus dem
Laufwerk" - gilt fuer Blu-ray, NICHT fuer UHD).
- SAVEPOINT v3.11, ROADMAP Etappe 18 (Etappe 17 mit Nachtrag), AGENTS.
Offen: voller UHD-Rip inkl. Transcode-E2E; und ob sich der Abruf unter
Linux doch anstossen laesst.
Quellen (AGENTS Regel D):
- Linux laedt keine Hashed Keys, gleiches Symptom:
https://forum.makemkv.com/forum/viewtopic.php?t=25782https://forum.makemkv.com/forum/viewtopic.php?t=34022
- Schluessel als hkd_*.bin in _private_data.tar:
https://forum.makemkv.com/forum/viewtopic.php?t=32675
- Meldungsformat: https://www.makemkv.com/developers/usage.txt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4K-UHD scheiterte an "The volume key is unknown for this disc". Am 25.07.2026
im Worker-Container nachgemessen: Laufwerk und MakeMKV sind in Ordnung
(LibreDrive v06.3 / Meldung 1011, Disc wird gelesen, AACS-Dump geschrieben) -
MakeMKV versucht gar nicht erst, online einen Schluessel zu holen. Belege:
_private_data.tar enthaelt nur die Index-Datei und KEINE hkd_*.bin; auch mit
geloeschter update.conf (Meldung 5074 belegt den Web-Kontakt) und mit
app_UpdateEnable="1" kam keiner; hkdata.fairuse.org und hkdata.crabdance.com
loesen weltweit nicht mehr auf (NXDOMAIN gegen Fritz!Box, 8.8.8.8, 1.1.1.1).
Betroffen war Akira UHD (MKB v76, Pressung Dez. 2020) - also gerade KEINE
Neuerscheinung. Der bisherige Fehlertext ("Disc neuer als die
Schluessel-Datenbank, mit einem der naechsten Updates rippbar") war falsch.
Einziger heute funktionierender Weg ist eine vom Nutzer selbst mitgebrachte
KEYDB.cfg. Rippy liefert KEINE Schluessel mit, laedt keine herunter und
verteilt keine - es stellt nur den Platz bereit und zeigt an, was dort liegt.
- Datenverzeichnis persistent gemountet (MAKEMKV_DATA_HOST, Default
/srv/rippy/makemkv): KEYDB.cfg und AACS-Dumps ueberleben jeden Rebuild.
Vorher loeschte jeder "up -d --build" beides - inklusive des Dumps, auf den
die Fehlermeldung selbst verwies.
- entrypoint.sh und tasks.py schreiben settings.conf ergaenzend statt
zerstoerend. Der entrypoint bricht bei nicht beschreibbarem Verzeichnis
nicht mehr ab - mit "restart: unless-stopped" waere das ein Crashloop
gewesen, in dem auch reines DVD-Rippen tot ist.
- Neues Zwillings-Modul makemkv_daten.py (docker/api + docker/worker,
byteweise identisch; test_zwillinge_sind_byteweise_identisch wacht darueber
und wurde durch absichtliches Verstellen als wirksam nachgewiesen).
- API: GET/POST/DELETE /system/keydb, GET /system/aacs-dumps(/{dateiname}).
JSON-Body statt Multipart - python-multipart ist bewusst nicht installiert
und wuerde die API beim Import toeten. nginx client_max_body_size 64m,
sonst scheitert der Upload mit 413, bevor die API ihn sieht.
- UI (Einstellungen -> System): Status, Hochladen per Datei-Dialog, Entfernen,
Dump-Download, KEYDB-Plakette je Worker (nur wo das Verzeichnis wirklich
gemountet ist - ein Remote-Transcode-Worker truege sonst eine Warnung,
die ihn nichts angeht).
- parse_msg() + log_cb: MakeMKV-Meldungen landen im Rippy-Log (gedrosselt:
Code 1003 raus, keine Wiederholungen, max. 40 je Rip). Nebenbei behoben:
der alte Parser (split(",", 4)[3]) schnitt jede Meldung am ersten Komma ab.
- Fuenf Stellen richtiggestellt, die behaupteten, MakeMKV-Updates braechten
die neueste Disc-Schluessel-Datenbank mit (UI, Anleitung, README,
Worker-Dockerfile, makemkv_key.py).
NICHT bewiesen: ein erfolgreicher UHD-Rip - es lag keine KEYDB.cfg mit dem
Akira-Schluessel vor. Belegt sind der Befund und die neue Mechanik. So steht
es auch im SAVEPOINT und in der ROADMAP.
Quellen (AGENTS Regel D):
- Datenverzeichnis + Dateiname GROSS/case-sensitiv:
https://forum.makemkv.com/forum/viewtopic.php?t=30636
- hkd_*.bin in _private_data.tar:
https://forum.makemkv.com/forum/viewtopic.php?t=32675
- headless settings.conf / app_UpdateEnable:
https://forum.makemkv.com/forum/viewtopic.php?t=20364
- KEYDB.cfg-Zeilenformat (libaacs):
https://github.com/ShiftMediaProject/libaacs/blob/master/KEYDB.cfg
- MSG-/PRGV-Format: https://www.makemkv.com/developers/usage.txt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GET /jobs nutzt response_model=List[Job]; das Job-Model hatte kein meta-Feld,
also schnitt FastAPI die Disc-Metadaten (poster_path) weg -> Dashboard.tsx
bekam job.meta = undefined -> Filmstreifen-Platzhalter statt TMDB-Poster,
sowohl in der Jobliste als auch im aktiven Rip-Header (beide aus /jobs).
Additiv: meta: Optional[Dict] ins Job-Model + in _job_row_to_model parsen
(json.loads wie im Detail-Endpunkt, defensiv gegen kaputtes JSON). Keine
UI-Aenderung noetig -- posterUrl() rendert dann die vorhandenen Poster.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aus dem Weitergabe-Review. Kein Verhaltenswechsel fuer den Ursprungs-Host
(alle Defaults = bisheriges Verhalten):
Portabilitaet:
- Laufwerk-Geraeteknoten via .env parametrisiert (OPTICAL_SR/OPTICAL_SG,
Defaults sr0/sg1) - der #1-Blocker: auf Fremdhosts liegt das sg-Node woanders.
- deploy.sh generisch (RIPPY_VM/RIPPY_REPO_URL/... aus der Umgebung) - keine
festen Besitzer-Adressen (interne IP + DDNS) mehr im Repo.
- POSTGRES_PASSWORD in compose durchverdrahtet (Default rippy) - war No-op.
Aufraeumen (toter/irrefuehrender Code):
- udev/ entfernt (fehlende Regeldatei, falscher Container-Name; durch ioctl-
Polling ersetzt - reine Altlast).
- docker/postgres/*, docker/redis/*, init.sql entfernt (nie eingebunden).
Onboarding:
- FirstRunWizard verdrahtet: App.tsx fragt GET /setup und zeigt den
Einrichtungs-Assistenten beim ersten Start (war gebaut, aber nie gerendert).
Doku:
- README: Laufwerk-Abschnitt auf .env, ENV-Tabelle (OPTICAL_*/POSTGRES),
rshared-Anleitung, neuer Abschnitt "Haertung fuer fremde/exponierte Netze".
- config_validation.py: TMDB Pflicht->empfohlen, Zeichensalat-Tippfehler gefixt.
- .env.example: TMDB-Wording, OPTICAL_*-Variablen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Folgefix zum remount-Vorfall 24.07.: mounten() fiel bei einem TOTEN Mount
(os.path.ismount wirft OSError) auf den echten `mount` durch und stapelte auf die
Leiche. Ueber viele Neustarts (via rshared propagiert, ueberlebt Container-Recreate)
wuchs das auf 12 Schichten; die tote oberste blockierte jeden Zugriff (ls-Timeout,
obwohl SMB-445 offen) -> Medien-Mount unbrauchbar.
Fix: _stale_mounts_loesen(ziel) loest per lazy `umount -l` alle Schichten, bevor neu
gemountet wird -> kein Stapeln mehr, Re-Mount idempotent. Ein gesunder Mount wird
weiterhin frueh erkannt (os.path.ismount) und unangetastet gelassen.
Tests (test_mounts_helpers.py): Loesch-Schleife bis leer (monkeypatch) + Verdrahtung.
Ruff gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vorfall 24.07.: Beim API-Start blockierte der synchrone CIFS-Schreibtest in
alle_remounten()/mounten() im Kernel (wait_for_response), als der SMB-Server
langsam war -> ~5 min "Waiting for application startup", kein Endpoint bedient
(bis der soft-Mount per Timeout abbrach). startup_event() lief isoliert sauber,
also war es der blockierende Netz-Mount, nicht die App-Logik.
Fix: remount als Hintergrund-Task (asyncio.create_task) statt await -> die API
kommt sofort hoch, die Mounts stellen sich her sobald der Server antwortet.
Test (test_api_smoke.py): haelt den Nicht-blockierend-Vertrag per Quelltext-
Inspektion fest, im Stil der anderen Verdrahtungs-Tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der kostenlose Beta-Key wechselt ~monatlich und laeuft zum Monatsende ab; bisher
musste er von Hand nachgetragen werden, sonst blockt Blu-ray-Ripping irgendwann
still. Neu: taeglicher Forum-Abgleich (t=1053) -> Key in die Settings
(makemkvAppKey). Der Worker liest ihn VOR jedem Rip (tasks.py) -> wirkt ohne
Rebuild ab dem naechsten Rip.
- docker/api/makemkv_key.py: extract_key (pure, testbar) + fetch_current_key +
refresh_once/refresh_loop; loggt in die App-Logs (add_log).
- docker/api/main.py: refresh_loop als startup-Task.
- docker/api/test_makemkv_key.py: Parser-Tests - fingen den Bug, dass Beta-Keys
laenger als 64 Zeichen sind (Regex {50,} statt {64}).
Betrifft nur die Software-Lizenz (oeffentlicher Gratis-Key), NICHT Disc-Schluessel
- die zieht MakeMKV via LibreDrive selbst. Ruff gruen, 4 Tests gruen, Live-Fetch
gegen das echte Forum verifiziert.
Co-Authored-By: Claude Opus 4.8 <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>
Commander-Wunsch: das UI soll die Werkzeug-Versionslage genau so zeigen,
wie sie ist. Klarstellung im System-Tab:
- Hinweis bei 'Installierte Werkzeuge': MakeMKV aktuell haltbar (wichtig
wegen Schluessel-DB), HandBrake im Docker-Worker bewusst Debian-stabil
(kein separates Update), native Worker holen die neueste.
- Update-Check: MakeMKV mit gruenem '✓ aktuell' bzw. Update-Befehl (echtes
To-do). HandBrake NICHT mehr als 'Update noetig' alarmieren — neutral als
'Debian-Version, bewusst so' + Info, dass die nativen Worker die neueste
nutzen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Laufen zwei Worker auf demselben Rechner (Befund 24.07.: alter + neuer
Windows-Worker auf TobisNicerPC), war die Node-Zuordnung mehrdeutig — der
Hostname-Match traf irgendeinen. Jetzt disambiguiert der Name-Teil des
Celery-Knotens (WORKER_NAME), sonst Fallback auf den ersten Host-Treffer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PowerShell 5.1 liest .ps1 als ANSI, nicht UTF-8. Der Gedankenstrich '—' in
einem Write-Host-String dekodierte zu 'â€"' — das eingebettete Anfuehrungs-
zeichen zerriss die Zeichenkette und der Parser scheiterte (unerwartetes
Token). Alle Nicht-ASCII-Zeichen durch ASCII ersetzt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- mounten(): FileExistsError von os.makedirs abgefangen und ismount OSError
toleriert — ein toter Mount taeuschte isdir, die Reparatur brach ab.
- ist_erreichbar(): 'timeout 3 ls' statt os.listdir — der direkte Zugriff
blockierte sonst ~10s bis zum CIFS-Timeout (traege Speicherziele-Seite).
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>
Commander-Entscheid 24.07.: nur noch EIN Branch. Der stable-Zwischenbranch
war vestigial — die VM deployt ohnehin aus main (git pull), das Gruen-Gate
hat den Live-Deploy nie real gegated.
- ci.yml: Beförderungs-Schritt (push -> stable) entfernt; die Ampel prueft
nur noch (Ruff/pytest/Vite-Build), Rot heisst weiterhin: nicht deployen.
- deploy.sh: klont/resettet auf main statt stable.
- AGENTS §C, README (Entwicklung), DESIGN-2.0-Briefing: auf main-only
umgeschrieben. Design-2.0-Briefing als ERLEDIGT markiert.
- SAVEPOINT v3.6.
Branches stable / design-2.0 / kernumbau-2026-07-23 werden nach diesem
Push geloescht (Inhalte vollstaendig in main).
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>
Cloudflare drosselt BuildKit-Downloads von makemkv.com hartnaeckig (24.07.
viermal, auch MIT --retry-all-errors, waehrend dieselben URLs ausserhalb
des Builds laden). Dauerhafter Ausweg statt Mirror-Hack: Tarballs einmal
nach docker/worker/vendor/ legen (untracked, .gitignore) — der Build nutzt
sie dann direkt und faellt sonst auf curl+Retry zurueck.
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>
Cloudflare drosselte am 24.07. wiederholt GENAU die BuildKit-Downloads
(ausserhalb des Builds gingen dieselben URLs mit 200 durch) — drei
Deploy-Anlaeufe starben an curl exit 22. Zwei Gegenmittel:
- curl --retry 5 --retry-delay 15 --retry-all-errors im Download-RUN
(curl-Doku; --retry-all-errors wiederholt auch bei 4xx/5xx)
- Lokal-Mirror-Rezept im Kommentar: Tarballs von Hand laden, per
nginx:alpine servieren, MAKEMKV_URL_BASE aufs LAN zeigen — so wurde
der 1.18.4-Build auf der VM letztlich gebaut.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diagnose auf der VM (Summer-Wars-UHD, MKB v82):
- 'Using LibreDrive mode (v06.3)' — der BU40N-Crossflash IST erledigt,
das Laufwerk liest UHD. Der Fehlschlag kam von 'The volume key is
unknown for this disc': die Disc ist neuer als MakeMKVs
Schluessel-Datenbank — mit 1.17.7 UND der aktuellsten 1.18.4 bewiesen
(beide Versionen live gegen die Disc getestet).
Konsequenzen:
- MakeMKV-Pin von 1.17.7 auf 1.18.4 gehoben: der 1.18er Scan-Haenger
(Forum t=38128) wird ueberall mit --noscan umgangen (info-Lauf mit
1.18.4 auf der BU40N sauber durchgelaufen), die Flash-Faehigkeit von
1.17.7 wird nicht mehr gebraucht. Aktuelle Version = aktuellste
AACS-Key-DB. URL-Base -> /download (dort liegt nur die aktuelle).
- run_makemkv reicht KRITISCHE Meldungen (volume key unknown, Key
abgelaufen, too old version) in den Fehlertext durch — vorher stand
da nur die nichtssagende letzte Zeile 'Failed to open disc'.
- UHD-Klartext in tasks.py unterscheidet jetzt die zwei Faelle:
volume-key-unknown (MakeMKV/Disc-Alter, AACS-Dump aus /root/.MakeMKV/
im MakeMKV-Forum einreichen) vs. Failed-to-open ohne LibreDrive
(Firmware-Hinweis).
- SAVEPOINT: Flash-Status korrigiert (war als offen dokumentiert).
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>