Commit Graph

73 Commits

Author SHA1 Message Date
Hitonabi 2a90538473 fix: Deinstaller, Verwaltungsfenster, Dashboard-Widerspruch, Mount-Tempo
Ampel / ampel (push) Successful in 30s
Vier Meldungen des Commanders, alle nachgemessen.

1. DEINSTALLER LIEF NICHT MEHR - reproduziert mit echtem PowerShell:

     Der Typ [System.Windows.Forms.MessageBox] wurde nicht gefunden.

   `Add-Type -AssemblyName System.Windows.Forms` stand EINE ZEILE ZU SPAET, die
   MessageBox wurde davor benutzt. Der Deinstaller starb also in seiner ersten
   Arbeitszeile, jedes Mal. Zwei weitere Maengel gleich mit:
   Kodierung war ASCII trotz Umlauten, und `Remove-Item -Recurse -Force
   $PSScriptRoot` loescht den Ordner, in dem das laufende Skript liegt - das
   klappt auf Windows nicht zuverlaessig (venv-DLLs sind geladen). Jetzt raeumt
   ein losgeloestes cmd nach, sobald PowerShell weg ist. Der erzeugte Deinstaller
   ist gegengeprueft: parst, BOM da, Umlaute intakt.

2. VERWALTUNGSFENSTER (Doppelklick aufs Tray). Zeigt Status, Aufgaben und Log,
   plus Knoepfe fuer Rippy, Log-in-Rippy und Deinstallieren - Deinstallieren geht
   damit auch aus dem Tray-Menue. Eigener PROZESS statt Fenster im Tray, weil
   pystray und tkinter beide den Haupt-Thread wollen; tkinter statt WinForms,
   weil es bei jeder Windows-Python-Installation dabei ist. Headless gerendert
   und angesehen. Alles Fachliche kommt von Rippy (/capabilities, /jobs), damit
   dort nicht eine zweite, abweichende Wahrheit steht.

3. DASHBOARD-WIDERSPRUCH. Oben stand "Akira im Laufwerk erkannt", die
   Server-Status-Karte gleichzeitig "Bereit - keine Disc in Arbeit / Disc
   einlegen". Zwei Aussagen, ein Blick. Die Karte fragt jetzt /devices und sagt
   "Disc erkannt - wartet auf Rippen starten" samt Titel.

4. MOUNT-TEMPO. Der Commander: "150 Sekunden? Das ist verrueckt langsam." Recht
   hat er. Gemessen ging die Zeit fast komplett in einen FEHLVERSUCH: `umount -l`
   ist lazy, der Abbau passiert spaeter, und wer direkt danach mountet, riskiert
   dass der Abbau hinter dem neuen Mount landet. Der erste Reparaturversuch
   scheiterte dadurch regelmaessig - und der zweite kostete 30 s mount-Timeout
   plus Pruefungen. Jetzt werden 1,5 s auf den Abbau gewartet, bevor neu
   gemountet wird; die Wache prueft erstmals nach 3 s statt 10 s und benutzt zum
   ERKENNEN die einfache schnelle Probe (die Doppelprobe steckt dort, wo der
   Wettlauf lauert: direkt nach dem Mount).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:52:53 +02:00
Hitonabi 87484d6863 feat(worker): der externe Worker wird erwachsen - Log in Rippy, Anzeige, Slots
Ampel / ampel (push) Successful in 28s
Commander 26.07.2026: "der externe Encoder Worker ist ein bisschen duenn - der
koennte noch viel mehr." Drei Punkte, alle am Tray.

1. LOG IN RIPPY STATT TXT-DATEI (ausdruecklich gewuenscht). Das Tray schrieb sein
   Log nach %LOCALAPPDATA% und oeffnete es im Editor - wer wissen wollte, warum
   der Worker nichts tut, musste sich an den PC setzen. Neue Bruecke
   (logbruecke.py) meldet die wichtigen Zeilen nach Rippy, Quelle "w:<name>",
   und die Logs-Seite hat jetzt Knoepfe je Quelle: "was macht mein PC" ist ein
   Klick. Der Filter konnte Quellen schon immer, es gab nur keinen Knopf.

   Durchgelassen wird WENIG und mit Grund: Die Job-Meldungen stehen laengst in
   Rippy (tasks.py schreibt sie selbst). Es fehlte, was DANEBEN passiert und den
   Worker unbrauchbar macht, ohne dass ein Job existiert - hochgefahren oder
   nicht, Verbindung zu Redis/Postgres, Abstuerze. Alles andere fliegt weg:
   Celery ist bei --loglevel=info gespraechig, die logs-Tabelle hat keine
   Aufraeumung, und ein zugemuelltes Log ist so unbrauchbar wie keins. Dazu eine
   Drossel (30 Zeilen/Minute), die MELDET, wieviel sie verschluckt hat.
   Zeilenformat woertlich aus dem laufenden Container abgenommen (Celery 5.4.0).

   Die lokale Datei bleibt - sie ist genau dann die einzige Auskunft, wenn Rippy
   nicht erreichbar ist.

2. DAS TRAY ZEIGT, WAS LAEUFT. Vorher stand dort "laeuft" oder "gestoppt" - auf
   einer Maschine, die stundenlang an einem Film rechnet, ist das keine Auskunft.
   Jetzt Titel, Prozent und Restzeit, geholt von Rippys /jobs. Bewusst dieselbe
   Quelle wie das Dashboard, damit im Tray nicht eine zweite, abweichende
   Schaetzung steht.

   Dazu: Windows schlaeft nicht mehr mitten im Encode ein
   (SetThreadExecutionState, ohne ES_DISPLAY_REQUIRED - der Bildschirm darf
   ausgehen). Die Sperre wird zurueckgenommen, sobald nichts laeuft, und auch bei
   einem harten Ende des Trays - sonst schlaeft der PC nie wieder ein und niemand
   weiss warum.

3. MEHRERE ENCODES GLEICHZEITIG. Der Worker lief fest mit --pool=solo und nahm
   genau EINEN Auftrag an. Der Installer fragt die Zahl jetzt (GUI: Feld neben
   dem Namen, mit der erkannten Kernzahl daneben), Vorbelegung ab 12 Kernen
   zwei, sonst einer: HandBrake nutzt schon alle Kerne, aber x265 skaliert nicht
   linear. Auf Windows gibt es keinen prefork-Pool (kein fork) - deshalb
   --pool=threads, was hier passt, weil die Arbeit ein Kind-Prozess ist und der
   Thread nur wartet.

GUI-Layout headless gerendert und angesehen (nichts ueberlappt, 16 Kerne
korrekt erkannt), beide .ps1 mit echtem PowerShell 5.1 geprueft, BOM und CRLF
erhalten, .exe neu gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 14:17:14 +02:00
Hitonabi 6db64230eb fix(dashboard): zwei Placebos raus, Restzeit rein - und Loeschen sagt die Zahl
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>
2026-07-26 12:59:08 +02:00
Hitonabi 638edc6a9f feat(ui): Presets vom Worker, Warnung vor unerreichbaren Pfaden, Browser weg
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>
2026-07-26 12:58:53 +02:00
Hitonabi fd1feaaee3 fix(ui): die Ablage war im UI ueberhaupt nicht einstellbar
Ampel / ampel (push) Successful in 28s
Der Grund, warum der erste Rip mit externem Encoder scheitern MUSSTE - und
Commander-Vorgabe: "Wenn ein externes Ziel eingehaengt ist, soll Rippy das
ausgewaehlte als Arbeitsziel verwenden. Das stellt man ja sowieso in den
Einstellungen ein. Das muss auch fuer den Automatik-Modus so sein."

Genau das ging nicht. outputDir stand in den Einstellungen, wurde von der
Vollautomatik (main.py) und der Schnellwahl (RipTargetModal) gelesen - aber es
gab NIRGENDS ein Eingabefeld. Die Ablage klebte auf /app/media, der
Container-Platte. Das Arbeitsverzeichnis hatte eine Auswahlliste der
eingehaengten Ziele, das Ziel nicht.

Folge im Praxistest: Rohdaten auf der NAS (die der PC erreicht), Ziel auf der
VM-Platte (die er nicht erreicht, kein Samba dort). Der externe Encoder konnte
lesen, aber nicht schreiben - und das haette man mit den vorhandenen
Bedienelementen gar nicht anders einstellen koennen.

JETZT: Ablage-Auswahl in Einstellungen -> Ripping, mit derselben Liste
eingehaengter Ziele wie das Arbeitsverzeichnis. Damit wirkt sie automatisch
auch in der Vollautomatik und in der Schnellwahl, weil beide outputDir lesen -
also genau so, wie der Commander es beschrieben hat.

DAZU die Pruefung, die den Vorfall verhindert haette: Sobald ein EXTERNER
Worker gemeldet ist, warnt das UI, wenn Ablage und Arbeitsverzeichnis nicht
beide auf einer erreichbaren Freigabe liegen. Drei Faelle, jeweils mit der
Konsequenz im Klartext:
  - Arbeitsverzeichnis auf der Container-Platte -> externer Encoder kann nicht lesen
  - Arbeit auf Freigabe, Ablage lokal -> kann lesen, nicht schreiben (der Vorfall)
  - beide auf VERSCHIEDENEN Freigaben -> laeuft, kostet aber eine Vollkopie

"Extern" ist dabei keine Heuristik ueber IPs oder Namen: caps.py meldet es
selbst (`extern`), weil /app im Rippy-Image immer existiert und ausserhalb nie.
Zusaetzlich meldet der Worker jetzt seine `pfad_map` - damit kann das UI im
naechsten Schritt sagen, ob die Uebersetzung ueberhaupt gesetzt ist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 11:58:58 +02:00
Hitonabi fe12401e34 fix(worker): Hardware-Encoder und CPU-Merkmale auf Windows - plus ehrliche Transcode-Fehler
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>
2026-07-26 11:51:47 +02:00
Hitonabi 35cfcbcb07 style: echte Umlaute im ganzen Projekt + Installer im Rippy-Look
Ampel / ampel (push) Successful in 27s
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>
2026-07-25 22:37:23 +02:00
Hitonabi 65edf8369e feat(worker-windows): Installation nach "Programme", Zielordner frei waehlbar
Ampel / ampel (push) Successful in 28s
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>
2026-07-25 22:20:50 +02:00
Hitonabi 6a17af2118 feat(transcode,ui): Kompression je Disc-Typ abwaehlbar + Wizard rechnet mit der CPU
Ampel / ampel (push) Successful in 28s
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>
2026-07-25 21:48:31 +02:00
Hitonabi 883c1c290b fix(caps,ui): Encoder werden gemessen statt behauptet
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>
2026-07-25 21:04:42 +02:00
Hitonabi 8bb075c656 feat(rip): Arbeitsverzeichnis je Rip waehlbar statt global vorgegeben
Ampel / ampel (push) Successful in 28s
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>
2026-07-25 19:59:37 +02:00
Hitonabi c065967f4d fix(ui,transcode): Dialog-Falle, Arbeitsverzeichnis waehlbar, Original-Aufheben entschaerft
Ampel / ampel (push) Successful in 28s
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>
2026-07-25 19:48:10 +02:00
Hitonabi e84afc1718 feat(transcode): ein HandBrake-Preset je Disc-Typ statt eines fuer alles
Ampel / ampel (push) Successful in 29s
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>
2026-07-25 11:38:52 +02:00
Hitonabi f449c4ee34 fix(uhd): 4K-UHD geloest - makemkvcon holt Schluessel unter Linux nie
Ampel / ampel (push) Successful in 28s
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=25782
  https://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>
2026-07-25 11:18:09 +02:00
Hitonabi 0935766f61 feat(uhd): KEYDB.cfg-Unterstuetzung - MakeMKVs Schluessel-Kanal liefert nichts mehr
Ampel / ampel (push) Successful in 28s
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>
2026-07-25 01:26:08 +02:00
Hitonabi 0832ef777c chore(handoff): portabel machen + Doku/Onboarding glaetten (Review-Umsetzung)
Ampel / ampel (push) Successful in 27s
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>
2026-07-24 23:32:59 +02:00
Hitonabi 71c618073e docs: Anleitung auf .exe-Installer, SAVEPOINT v3.9
Ampel / ampel (push) Successful in 36s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 19:20:28 +02:00
Hitonabi 20cdfedb85 feat(installer): fertige .exe mit Rippy-Icon + ohne Konsolenfenster (statt .vbs)
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 19:15:42 +02:00
Hitonabi b1d9ece9b2 feat(installer): kein Konsolenfenster (.vbs-Starter) + Rippy-Icon im Installer-Fenster
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 19:03:19 +02:00
Hitonabi b337525bed docs+ui: grafischen Windows-Installer in Anleitung, SAVEPOINT v3.8
Ampel / ampel (push) Successful in 28s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 18:52:57 +02:00
Hitonabi 4fb29d16cf feat(worker): grafischer Windows-Installer (GUI statt CLI)
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 18:47:57 +02:00
Hitonabi 57171b4c88 ui(system): Versionslage exakt darstellen — MakeMKV aktualisierbar, Docker-HandBrake bewusst Debian-stabil
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 18:42:01 +02:00
Hitonabi 4ba02047db Drei Praxis-Bugs: tote NAS-Mounts reparierbar, HandBrake-Versionen konsistent, Encoder-Wahl beim Rip
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 18:19:57 +02:00
Hitonabi fe1387b1ec fix(ui): typLabel in DeviceDiscovery definiert (Laufwerks-Details-Button gefixt) & Settings Tab-Wechsel robuster gemacht
Ampel / ampel (push) Successful in 28s
2026-07-24 17:04:18 +02:00
Hitonabi 67a4a76afa fix(ui): Worker-Adressfeld nicht mehr mit window.location vorbelegen
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 16:53:12 +02:00
Hitonabi b130eeb967 feat(ui): Master-Panel 'Ripping Operations' DIREKT unter Laufwerke verschoben
Ampel / ampel (push) Successful in 28s
2026-07-24 16:44:33 +02:00
Hitonabi bf80f9dfd4 refactor(ui): Current Job, Ripping Queue und Job-Historie in ein einziges Master-Panel bündig unter Laufwerke zusammengeführt
Ampel / ampel (push) Successful in 27s
2026-07-24 16:43:10 +02:00
Hitonabi e0864828c2 style(ui): Ripping Queue Kachel per flex-1 vertikal gestreckt fuer exakt buendigen unteren Abschluss mit Recently Ripped
Ampel / ampel (push) Successful in 28s
2026-07-24 16:41:26 +02:00
Hitonabi 60abf42359 feat(ui): Laufwerke & im Laufwerk erkannte Disc nach ganz oben verschoben, Ripping Queue exakt nach SC1 aufgebaut
Ampel / ampel (push) Successful in 28s
2026-07-24 16:39:56 +02:00
Hitonabi e18315814c feat(ui): 5-Kachel-Banner ohne schwarze Loecher gefuellt, Vollstaendige Job-Tabelle mit TMDB-Beschreibungen wiederhergestellt
Ampel / ampel (push) Successful in 28s
2026-07-24 16:36:12 +02:00
Hitonabi 9b19b74d39 fix(ui): 100% Dark Mode erzwungen (White Mode entfernt), Echte API-Daten fuer Server Status gebunden & Tabs-Breite/Scrollbalken behoben
Ampel / ampel (push) Successful in 28s
2026-07-24 16:33:50 +02:00
Hitonabi 9bc8ad11b2 fix(ui): Zentrierung fuer Anleitung & Einstellungen gefixt, zufaellige Mockup-Filme durch echte API-Daten ersetzt
Ampel / ampel (push) Successful in 28s
2026-07-24 16:31:45 +02:00
Hitonabi 0f20d9d8f4 feat(ui): Rebuild Layout EXACTLY 1:1 matching SC1 Mockup (Top Header Nav, 5-Panel Movie Wall, 2-Column Dashboard Grid with Sparkline & Recently Ripped)
Ampel / ampel (push) Successful in 28s
2026-07-24 16:25:21 +02:00
Hitonabi f0a8455fc2 style(ui): Visuelle Hero Showcase Banner & Poster Cards Grid auf dem Dashboard
Ampel / ampel (push) Successful in 28s
2026-07-24 16:22:40 +02:00
Hitonabi 3643f74eb1 style(ui): Visuelles High-End Overhaul fuer Cinematic Cinema OS (3D Spin-Disc, Hero Banner, Radial Gauges & Poster Grid)
Ampel / ampel (push) Successful in 27s
2026-07-24 16:18:58 +02:00
Hitonabi bd00d4c728 refactor(ui): Dashboard, DeviceDiscovery & Settings auf Cinematic Cinema OS umgestellt
Ampel / ampel (push) Successful in 27s
2026-07-24 16:04:50 +02:00
Hitonabi 90b708302e refactor(ui): Modals & Unterseiten auf Primitives & dark: umgestellt 2026-07-24 16:04:46 +02:00
Hitonabi 7004d798d6 feat(ui): Primitives & Design Tokens fuer Cinematic Cinema OS 2026-07-24 16:04:41 +02:00
Hitonabi 404a002d5c Windows-Worker: Tray-Symbol + rueckstandsfreie Deinstallation
Ampel / ampel (push) Successful in 27s
- 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>
2026-07-24 15:44:29 +02:00
Hitonabi 3987c31b1e Update-Check im System-Tab, Worker-Adressfeld mit Heimnetz-Warnung, Save-Knopf raus aus dem Worker-Tab
Ampel / ampel (push) Successful in 28s
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>
2026-07-24 15:39:20 +02:00
Hitonabi 76b4286ad3 Windows-Worker: Pfad-Mapping fuer echte Transcodes + leere-Titel-Meldung + E2E-Beweis dokumentiert
Ampel / ampel (push) Successful in 27s
- pfad_lokal (tasks.py, mit Tests): RIPPY_PATH_MAP uebersetzt
  Container-Pfade (/app/temp, /app/media) auf Netzlaufwerke des nativen
  Workers — ohne Mapping unveraendert (Docker-Worker).
- Rip-Dialog: 'done' mit 0 Titeln bekommt Klartext (Disc vermutlich
  nicht entschluesselbar) statt leerer Tabelle.
- SAVEPOINT: E2E-Beweis des nativen Windows-Workers dokumentiert
  (test-windows-nativ online, HandBrake 1.9.2, echte LAN-IP).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:29:56 +02:00
Hitonabi 3a4eb25392 Track-Auswahl-Tabelle, nativer Windows-Worker (via UI angeboten), Anleitungs-Tab
Ampel / ampel (push) Successful in 29s
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>
2026-07-24 15:18:08 +02:00
Hitonabi 8eb5653848 Restefeger: Auth komplett raus, Serien-Flow + Episoden-Matching, Jellyfin-Refresh, Duplikat-Warnung, echtes Nur-Hauptfilm
Ampel / ampel (push) Successful in 55s
AUTH ENTFERNT (Commander-Entscheid 24.07., KONZEPT §10): /token- und
/api-keys-Endpoints, auth.py, test_auth.py, passlib/bcrypt/PyJWT/
python-multipart, JWT_SECRET_KEY-Pflicht. Heimnetz-only, das UI hatte nie
einen Login — die Auth-Oberflaeche war Placebo und die passlib/bcrypt-
Falle brach die Ampel. Rate-Limit pro IP bleibt. Schnellstart laeuft
jetzt ganz ohne .env-Pflichtwerte.

Serien-Flow (Etappe-12-Kern, ARM-Wunde #395):
- Rip-Dialog: Serienname + Staffel -> Ablage <Serie>/Season NN
  (jellyfin.org/docs Naming-Schema); tvshow.nfo + poster.jpg im
  Serien-Ordner, bei Staffel 2 nicht ueberschrieben.
- Episoden-Matching per Laufzeitabgleich: HandBrakeCLI --scan
  ('+ duration:', handbrake.fr/docs) je MKV gegen TMDB-Staffel-Laufzeiten
  (GET /metadata/tv/{id}/season/{n}; tv-season-details-API).
  Ordnungserhaltend; komplette Staffel auf einer Disc klappt auch bei
  uniformen Anime-Laufzeiten (Sequenz-Stufe). Umbenannt wird NUR bei
  eindeutiger Zuordnung — sonst ehrliches Log. Mit Tests.

Weitere Punkte:
- Jellyfin/Emby-Bibliotheks-Refresh nach jedem fertigen Rip
  (POST /Library/Refresh, X-Emby-Token lt. jellyfin.org/docs) —
  URL/Key + Test-Knopf in Einstellungen -> Ripping.
- Duplikat-Warnung: Disc-Fingerabdruck (jetzt Teil des Prescan-Ergebnisses
  + der Job-Metadaten) gegen die Historie; Karte zeigt 'bereits gerippt',
  Vollautomatik ueberspringt Duplikate.
- 'Nur Hauptfilm' ECHT: makemkvcon info -> TINFO-Attr-9-Laufzeiten
  (usage.txt) -> laengster Titel -> mkv dev:X <nr>. Vorher wirkungsloses
  Setting; pro Rip im Dialog uebersteuerbar. Mit Tests.
- OMDb-Treffer eingedeutscht via TMDB /find (external_source=imdb_id,
  de-DE; find-by-id-API).
- Dashboard: Speicherplatz-Anzeige (amber < 60 GB) + CSV-Export
  (GET /jobs/export, Semikolon+BOM fuer deutsches Excel).
- Metadaten-Seite entfernt (Abnahme durch Commander-Auftrag) inkl.
  Placebo-Endpoints /metadata/lookup (scannte Dummy-Device) und
  /metadata/confirm (schrieb nie gelesenen Cache-Key).
- Doppel-Jahr-Fix: 'X (2009) (2009)' in Log und Ordnernamen.
- Remote-Worker-Blocker: redis (6379) + postgres (5432) waren NIE
  veroeffentlicht — kein Remote-Worker konnte sich je verbinden. Ports
  jetzt offen (Heimnetz-Kompromiss, kommentiert) + API_URL fuer Worker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:22:19 +02:00
Hitonabi 5778ac4645 Praxis-Feedback-Runde: CIFS-Cap-Fix, TMDB v3-Keys, 4K-UHD-Typ, Vollautomatik, Job-Verwaltung, Worker-Namen
Ampel / ampel (push) Successful in 29s
Zwei bewiesene Bug-Fixes:
- SMB-Mount 'Unable to apply new capability set': mount.cifs hebt
  CAP_DAC_READ_SEARCH an, die in Dockers Default-Caps fehlt (auf der VM
  reproduziert: Bounding-Set a82425fb ohne Bit 2; mit der Capability
  verschwindet der Fehler). Fix: cap_add DAC_READ_SEARCH fuer den
  api-Container.
- TMDB fiel still aus: Client konnte nur v4-Bearer-Tokens, der uebliche
  32-Hex-v3-Key bekam 401 und die Suche lieferte nur OMDb. Jetzt beide
  Key-Arten (ist_v4_token + api_key-Query-Param lt.
  developer.themoviedb.org, mit Tests) — damit kommen auch die deutschen
  Texte an (language=de-DE war ueberall schon gesetzt). Neu:
  GET /metadata/status + 'Verbindung pruefen' in Einstellungen -> APIs
  (Live-Check am Cache vorbei).

Features aus dem Commander-Feedback:
- 4K UHD als eigener Disc-Typ: classify >= 55 GiB (BD-66/BD-100; BD-50
  bleibt bluray), beide detection.py + Tests, eigene Badge-Farbe in
  Dashboard/Laufwerken, Prescan-Label '4K UHD'. UHD-Rip-Fehler 'Failed to
  open disc' (Code 11) bekommt Klartext: LibreDrive-Firmware noetig.
- Vollautomatik (Setting autoRipStart): Disc erkannt -> Rip startet ohne
  Popup in den Schnellwahl-Ordner (Serie->Serien, sonst Filme, CD->Musik).
- Job-Verwaltung: 'Neu komprimieren' nur noch mit can_retry (Rohdaten
  liegen wirklich da), DELETE /jobs/{id} + 'Erledigte aufraeumen'
  (Dateien bleiben immer), 'Alle herunterladen' im Job-Detail
  (gestaffelte Einzel-Downloads statt Server-Zip von 40-GB-Dateien).
- Worker zuordenbar: WORKER_NAME-Env als stabiler Anzeigename (fixt auch
  die Offline-Leichen nach Rebuilds), info.hostname/ip gemeldet,
  Online-Abgleich ueber hostname, DELETE /workers/{name} + Papierkorb im
  UI, Quelle-Anzeige an der Disc-Karte ('Quelle: TMDB - 99 % sicher').
- Ripping-Tab nach Medium gegliedert (Video/Audio-CD/Allgemein) +
  Untertitel-Klartext (--all-audio/--all-subtitles bleiben komplett),
  CD -> Musik-Vorauswahl im Ziel-Dialog, Ordner-Verwaltung beschriftet
  und standardmaessig eingeklappt.
- Docs: SAVEPOINT v3.3, ROADMAP Etappe 14 + Ideen (nativer
  Windows-Worker, deutsche OMDb-Texte via TMDB-Find), README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 13:29:10 +02:00
Hitonabi ab75134931 feat: Download-Knopf fuer fertige Rips — Dateien direkt im Browser statt scp
Ampel / ampel (push) Successful in 29s
- 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>
2026-07-24 09:01:38 +02:00
Hitonabi 13c1ae6064 feat(ui): Toast-Feedback ueberall, Job-Detail-Popup, System-Tab, Wizard-Media-Server, Favicon, Footer
- ToastContext: jede Aktion quittiert sichtbar ('Erfolgreich aktualisiert'
  ploppt oben rechts rein) — Refresh-Knoepfe drehen jetzt wirklich
  (Laufwerke, Logs, Worker, Speicherziele).
- JobDetailModal: Klick auf Titel in 'Neueste Jobs' -> Poster, Jahr,
  Beschreibung, Genres, Ablagepfad, Fehler.
- Logs: Filter-Pills um Warnung + Fehler ergaenzt (vorher nur
  Alle/Info/Erfolg).
- Speicherziele: Benutzer/Passwort VOR dem 'Freigaben auflisten'-Knopf
  (Windows/NAS lehnen Gast-Abfragen ab — genau daher kam das
  NT_STATUS_ACCESS_DENIED), Hinweistext, Toasts beim Einhaengen/Entfernen.
- Datei-Browser (Speicherziele + Rip-Ziel-Dialog) zeigt Dateien grau mit
  Groesse — Ordner wirkten faelschlich leer.
- Einstellungen: neuer System-Tab (Werkzeug-Versionen je Worker, Key-Status,
  Plattenplatz, MakeMKV-Beta-Key-Feld), Media-Server-Wahl im Ripping-Tab,
  Arbeitsverzeichnis im Verarbeitung-Tab, Benachrichtigungs-Anleitung
  (Discord/ntfy/Slack) + 'Test senden'.
- First-Run-Wizard: Media-Server-Schritt (Jellyfin/Emby/Kodi/Plex/Keins).
- Favicon (Disc im Logo-Verlauf, public/favicon.svg — /vite.svg war 404)
  + Footer 'Created with (Herz) by LucyAI, Claude and KrBrZ'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 08:55:42 +02:00
Hitonabi 6517644d1f Dashboard-Korrektur-Popup, Worker-Tab mit Live-Ping, Herzschlag, Anzeige-Fixes
Ampel / ampel (push) Failing after 29s
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>
2026-07-24 08:18:44 +02:00
Hitonabi 6b7d391737 Korrektur-Flow + Freigaben-Auswahl + Schreibtest + Schnellwahl-Klartext
Ampel / ampel (push) Failing after 52s
Commander-Befunde (23.07. spaet):
- MANUELLE KORREKTUR (KONZEPT Schritt 5 endlich komplett): Metadaten-Seite
  hat jetzt Titelsuche ueber ALLE Quellen (GET /metadata/search) mit
  Poster-Kacheln; ein Klick uebernimmt den Treffer (POST /metadata/override)
  fuer Karte, Jobs UND merkt ihn sich 30 Tage per Disc-Fingerabdruck —
  dieselbe Disc wird beim naechsten Einlegen sofort richtig erkannt
- PC/NAS per Klick: SMB-Freigaben eines Rechners auflisten
  (GET /storage-mounts/shares via smbclient) statt //host/share raten
- Berechtigungs-PRUEFUNG beim Einhaengen: Schreibtest, Warnung bei
  nur-lesbaren Zielen (API + UI + Log)
- "Standard-Unterordner" verstaendlich gemacht: heisst jetzt "Schnellwahl
  beim Rippen" mit Erklaertext; kryptisches Basis-Feld raus
- Preview zeigt Poster; Fingerprint fuer Cache/Override vereinheitlicht
  (billiges Label+Groesse statt Mount-Titel)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:34:34 +02:00
Hitonabi 07be54e62a Jikan/MAL-Quelle + Job-Titel + Abbrechen-Knopf + Dark-Hover + Ordner-Verwaltung
Ampel / ampel (push) Failing after 38s
Commander-Befunde (Screenshots 23.07. abends):
- Jikan (MyAnimeList, kostenlos OHNE Key) als Anime-Quelle vor OMDb —
  waehlt per Titel-Aehnlichkeit (SequenceMatcher, Schwelle 0.55) statt
  blind Treffer 1; damit trifft "Evangelion 2.22" den exakten Film
- Job-Titel: POST /jobs uebernimmt den erkannten Disc-Titel aus dem
  DISC_CACHE — Schluss mit "Unbekannt" in der Queue
- Kooperativer Job-Abbruch: POST /jobs/{id}/cancel setzt canceling,
  der Worker prueft das Flag bei jedem Fortschritts-Update und killt
  den Encoder-Prozess sauber (RipAbbruch); UI-Knopf "Abbrechen" fuer
  laufende, Status-Badge "Wird abgebrochen"
- Dark-Mode: Job-Zeilen-Hover war hart hell (hover:bg-slate-50)
- Speicherziele: lokaler Ordner-Browser mit "Ordner anlegen"
  (GET /browse + POST /browse/mkdir, nur unter /app/media)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:22:57 +02:00
Hitonabi 90eb17d407 UX-Runde: echte Disc-Titel via BD-Metadaten, Preview-Crash, Dashboard, Ziele
Ampel / ampel (push) Failing after 38s
Commander-Befunde 23.07. abends:
1. Metadaten "klappen nicht": Wurzel = kryptisches Volume-Label + kein
   TMDB-Key. Jetzt: API mountet die Disc read-only (CAP_SYS_ADMIN ist da)
   und liest den KLARTEXT-Titel aus BDMV/META/DL/bdmt_*.xml; dazu
   progressive Suchdegradation (Titel-Varianten) und OMDb-Unscharf-Suche
   (s= + imdbID-Nachladen) — mit dem vorhandenen OMDb-Key gibt es damit
   Titel/Jahr/Poster auch ohne TMDB
2. Weisses Fenster beim Pre-Scan: Lucide-Icons sind forwardRef —
   Direktaufruf getIcon(...)({size}) crashte die Seite; als JSX gerendert
3. Dashboard sortiert: System-Leiste (Encoder + aktiver Job) oben,
   Disc/Geraete -> Stats -> Jobs, Live-Log ans Ende
4. Ziel-Dialog mit ORDNER-BROWSER (GET /browse, nur unter /app/media);
   Schnellwahl nutzt die Settings-Unterordner statt Hartkodierung;
   Tab "Verzeichnisse" in "Speicherziele" aufgegangen (redundant)

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