a221a161f45ac55a38ca24efe0ffe7c0ed310ba5
230 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4195854bf8 |
feat(bausteine): Preset-Liste, Pfad-Vorschlag und Restzeit - alles gemessen
Vier neue reine Bausteine, jeder mit Tests. Sie beantworten Fragen, die Rippy bisher geraten oder gar nicht gestellt hat. 1. caps.parse_preset_liste - die Preset-NAMEN, die das HandBrake DIESES Workers wirklich kennt (`--preset-list`, Format im Worker-Image gemessen). Damit endet das Raten: die Namen unterscheiden sich je HandBrake-Version, und ein erfundener Name laesst die Kompression scheitern. Richtigstellung zum SAVEPOINT v3.16: Dort galt es als unmoeglich, die Hardware-Preset-Namen auf der Rippy-VM zu ermitteln, weil dort kein Hardware-Encoder laeuft. Gemessen ist das falsch - die Kategorie `Hardware/` steht vollstaendig in der Liste (VCN, NVENC, QSV, MF). HandBrake trennt zwei Fragen: --preset-list nennt alle Presets, --help nur die nutzbaren Encoder. Zwei Fragen, zwei Quellen. 2. mounts.pfad_map_vorschlag/pfad_map_zeile - der fehlende Anschluss fuer RIPPY_PATH_MAP. Geraten werden muss dafuer nichts: Rippy hat die Freigabe selbst eingehaengt und kennt ihre Quelle (//host/share). Mountpunkt plus Quelle IST das Mapping. NFS gibt bewusst "" - Windows-Schreibweise ist nicht ableitbar (AGENTS Regel D). Der Test fand dabei sofort die dokumentierte Windows-Falle: os.path.join baute `/app/media\rippy` in einen Container-Pfad, das Mapping waere still wirkungslos geblieben. _mountpoint nutzt jetzt posixpath. 3. presets.empfehlung - "immer das Beste" (Commander-Anforderung) ohne Punktesystem: fuer jede Lage eine feste Reihenfolge echter Namen, genommen wird der erste, den der Worker kennt. Hardware nur, wenn die Familie wirklich gemeldet ist (vce -> Preset heisst VCN, sonst liefe eine AMD-Karte unter dem Intel-Namen). vaapi und MF werden nie empfohlen: fuer vaapi gibt es kein Preset, bei MF ist die Nutzbarkeit nicht ablesbar. 4. eta - Restzeit aus dem gemessenen Fortschritt. Messreihe je Phase (Rip und Kompression haben nichts miteinander zu tun), Stillstand verlaengert die Schaetzung, und unter zwei Messwerten oder 60 Sekunden Spanne gibt es ehrlich keine Aussage. Test mit den echten 25.07.-Werten: 1,44 % in 29 min landet bei "noch ca. 1 Tag 23 h" - genau die Angabe, deren Fehlen den 50-Stunden-Lauf unsichtbar machte. Dazu: HandBrakes "Invalid preset <Name>" wird uebersetzt. Vorher stand im UI nur "HandBrake endete mit Code 3" - dass der Preset-NAME das Problem ist, war daraus nicht zu erraten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a468e4b6a4 |
docs(savepoint): Commander-Feedback als Landkarte - neun Punkte, neun Staende
Ampel / ampel (push) Successful in 27s
Rueckfrage des Commanders: "Was ist mit diesen ganzen Sachen?" - er wollte wissen, ob seine Liste aus der ersten Selbst-Einrichtung vollstaendig erfasst ist. Nachgeprueft: alle neun Punkte kommen im v3.16-Block vor. Einer war aber nur IMPLIZIT zu finden: dass seine RX 9070 XT wirklich encodieren soll, musste man aus zwei anderen Punkten erschliessen (Pfad-Mapping und Preset-Namen). Genau so faellt etwas durch. Jetzt steht seine Liste woertlich als Tabelle im Savepoint, jede Zeile mit Stand und Verweis auf die Prioritaetenliste. Drei erledigt, eine Frage beantwortet, fuenf offen. Aufgeteilt wo noetig: Punkt 3 war eigentlich drei Forderungen - Vektorbefehle auslesen (erledigt), das Beste empfehlen (offen), und Warnung von orange auf gruen (erledigt, wirkt fuer seinen PC ab dem Deploy). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4c78914529 |
docs(savepoint): v3.16 Uebergabe - externes Encoden konnte nie funktionieren
Ampel / ampel (push) Successful in 27s
Diese Sitzung endete wegen vollem Kontext. Der Savepoint ist so geschrieben, dass die naechste ohne Nachfragen weitermachen kann. ZUSTAND, den man kennen MUSS: Repo |
||
|
|
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> |
||
|
|
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>
|
||
|
|
61c38a0d14 |
docs(readme): eigener Abschnitt fuer Dockge, Portainer, Arcane und Shell
Ampel / ampel (push) Successful in 28s
Commander-Plan fuer die Uebergabe: "Repo auf den Dockge-Server laden, Compose reinhauen, Profit?!" - und genau da sitzt die Falle, in die jeder zuerst tappt. DER KERN, der jetzt oben im Abschnitt steht: Rippy hat KEINE Registry-Images. api, worker und ui werden aus dem Repo gebaut (build: context: .). Die Compose-Datei allein ist deshalb wertlos - in ein leeres Verzeichnis kopiert gibt es sofort "failed to read dockerfile". Daraus folgt fuer JEDE Oberflaeche dieselbe Regel: das Repo muss dort liegen, wo die Oberflaeche den Stack baut. Je Werkzeug der konkrete Weg, weil sie sich genau darin unterscheiden: - Dockge kann das Repo NICHT selbst holen -> hinein in den Stacks-Ordner klonen (Standard /opt/stacks). Dann ist die Compose des Repos die des Stacks, und man darf den Stack gerade NICHT neu in Dockge anlegen - sonst liegt eine leere Compose in einem anderen Ordner. - Portainer KANN es selbst holen -> Stacks > Add stack > Repository mit Git-URL. Web-Editor und Upload funktionieren nicht (kein Build-Kontext). Geraeteknoten dort als Stack-Umgebungsvariablen statt .env. - Arcane: Repo auf dem Host, Deploy per Shell - der Git-Sync zieht nicht selbststaendig (auf dieser Installation seit Wochen die geuebte Praxis). - Shell: unveraendert git clone + sudo ./install.sh. Dazu die Vorab-Pruefung als gemeinsamer Einstieg: ./install.sh --nur-pruefen braucht kein root, aendert nichts und nennt vor allem die ECHTEN Geraeteknoten - der einzige Punkt, der zuverlaessig zuschlaegt. RICHTIGSTELLUNG an mir selbst: Ich hatte die Mount-Propagation als Huerde dargestellt. Auf den meisten systemd-Hosts ist / schon rshared und /srv/rippy/ media erbt das - da ist nichts zu tun. Nur wenn Docker ueber "not a shared mount" klagt, braucht es die Shell. Steht jetzt so drin. Stoerungstabelle von vier auf sechs Faelle: "failed to read dockerfile" und "Worker startet nicht, /dev/sgN fehlt" ergaenzt - die beiden, die ein Oberflaechen-Nutzer als erste sieht. Interne Verweise gegengeprueft, alle vier loesen auf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
29444805a8 |
fix(build): der MakeMKV-Fallback der VM war seit Monaten tot - Kette jetzt dreistufig
Ampel / ampel (push) Successful in 28s
Beim Aufraeumen der VM-.env aufgefallen und nachgemessen (25.07.2026):
https://www.makemkv.com/download HTTP 200 <- Repo-Standard
https://www.makemkv.com/download/old HTTP 525 (Cloudflare)
web.archive.org-Schnappschuss HTTP 404 <- stand in der VM-.env
Die VM zeigte also auf eine KAPUTTE Adresse. Aufgefallen ist es nie, weil dort
die vendor/-Tarballs liegen und der Download-Zweig gar nicht erreicht wird.
Nimm die Tarballs weg, und jeder Bau scheitert mit 404 - waehrend die
Konfiguration gesund aussieht. Genau diese Sorte Fehler ist bei einer Uebergabe
teuer, weil sie erst beim Fremden zuschlaegt.
## Bereinigt (auf Commander-Wunsch, Sicherung liegt auf der VM)
.env.sicherung-vor-aufraeumen-20260725 angelegt, dann:
- MAKEMKV_URL_BASE raus -> es gilt der Compose-Standard, der liefert.
Von Docker gegengeprueft: MAKEMKV_URL_BASE: https://www.makemkv.com/download
- JWT_SECRET_KEY raus -> Ueberrest der in v3.4 ausgebauten Anmeldung, wirkungslos.
## Fallbacks BEHALTEN, aber echt gemacht (Commander-Vorgabe)
Vorher war der "Fallback" eine Zeile, die man von Hand eintragen musste - und
die hier ins Leere zeigte. Jetzt eine automatische Kette:
1. vendor/-Tarballs (braucht kein Netz, zuverlaessigster Weg)
2. MAKEMKV_URL_BASE
3. MAKEMKV_URL_FALLBACK (NEU, wird automatisch versucht wenn 2 versagt)
Stufe 3 ist ABSICHTLICH leer vorbelegt. Es gibt derzeit keine belegbare zweite
Quelle (siehe Messung oben), und eine einzutragen, die nicht liefert, waere
schlimmer als keine - genau das lag auf der VM vor. Wer eine eigene Quelle hat
(Spiegel im LAN), traegt sie ein und sie wird automatisch genutzt.
Scheitert alles, nennt die Fehlermeldung jetzt die beiden Wege, die
FUNKTIONIEREN, statt nur einen curl-Rueckgabewert zu hinterlassen.
## Geprueft, beide Zweige
- vendor-Pfad: Bau durch, 828 MB
- Download-Pfad: im FREMDEN Klon ohne Tarballs gebaut - Bau durch, 777 MB,
makemkvcon liegt unter /usr/local/bin/. Die leere Fallback-Stufe wird
korrekt uebersprungen, "Erste Quelle lieferte nicht" erschien nicht.
docker compose config validiert. README und .env.example nennen die gemessenen
HTTP-Antworten, damit niemand wieder eine 404-Adresse als Fallback eintraegt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b0205c42c9 |
refactor(install): erst ALLES pruefen, dann erst aendern
Ampel / ampel (push) Successful in 28s
Commander-Einwand: "Warum macht das install script den pre-check nicht selbst
vor der Installation?" Zu Recht - es tat es nur halb.
VORHER war Pruefen und Aendern verschraenkt:
1 Voraussetzungen pruefen (bricht ab)
2 Laufwerk pruefen (warnt)
3 Verzeichnisse AENDERT
4 Mount AENDERT
5 .env AENDERT
6 Bauen AENDERT
Nur die harten Voraussetzungen liefen vorab. Verzeichnisse wurden angelegt,
bevor klar war, ob der Rest durchlaeuft - scheiterte etwas in der Mitte, blieb
ein halb eingerichteter Rechner zurueck. Und --nur-pruefen war als if-Zweig
durch FUENF Schritte gefaedelt: so etwas laeuft zwangslaeufig aus dem Ruder,
weil jede neue Aktion daran denken muesste.
JETZT zwei klar getrennte Phasen:
Phase 1 PRUEFEN - fuenf Pruefungen, aendert NICHTS
Weiche - Probleme -> Abbruch mit Liste, nichts angefasst
Phase 2 EINRICHTEN - Verzeichnisse, Mount, .env, bauen, starten
Es gibt damit GENAU EINEN Pruef-Pfad, den beide Modi benutzen: --nur-pruefen
heisst schlicht "nach Phase 1 aufhoeren". Die Pruefung kann nicht mehr etwas
anderes behaupten als die Installation tut.
Die Weiche unterscheidet PROBLEME (verhindern die Installation) von HINWEISEN
(halten nicht auf) und fasst am Ende beides als Liste zusammen - vorher musste
man die Ausgabe rueckwaerts lesen, um zu wissen, ob es gereicht hat.
Neu mitgeprueft, weil es jetzt an einer Stelle passt:
- Ist der Elternpfad der Verzeichnisse ueberhaupt beschreibbar? (Vorher waere
das erst beim mkdir aufgefallen - mitten in der Aenderungsphase.)
- Freier Plattenplatz mit Schwelle 60 GB, samt Hinweis auf die Groessen
(Blu-ray roh ~40 GB, 4K-UHD bis 100 GB).
- Ist ueberhaupt .env.example da, also stehen wir im Rippy-Repo?
- Was in die .env geschrieben WUERDE, steht jetzt in Phase 1 als Ansage.
Auf der VM gegengeprueft: aus /tmp gestartet meldet es korrekt "kein
Rippy-Repo" und bricht ab, ohne etwas anzufassen; aus dem Repo heraus laeuft
die Pruefung sauber durch ("Alles in Ordnung, keine Einschraenkungen").
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
8f208ed0da |
fix(worker-windows): Autostart brach die ganze Installation ab
Ampel / ampel (push) Successful in 28s
Beim Commander live aufgetreten: Der Installer meldete
"FEHLER: FEHLER: Zugriff verweigert" samt dem Tipp, die IP koenne falsch sein -
und das, NACHDEM Worker-Code, Abhaengigkeiten und HandBrake schon fertig waren.
Die IP war also nie das Problem.
URSACHE: schtasks /create mit /tn "RippyWorker" legt die Aufgabe im
WURZELORDNER der Aufgabenplanung an, und das verlangt Administratorrechte. Der
Installer laeuft normal ohne. schtasks schrieb "FEHLER: Zugriff verweigert."
nach stderr, und weil im Skript $ErrorActionPreference = "Stop" steht, machte
PowerShell daraus einen TERMINIERENDEN Fehler - der catch-Block riss damit die
komplette Installation ab, obwohl nur der Autostart fehlte. Das doppelte
"FEHLER: FEHLER:" war der Hinweis: der aeussere Handler setzt sein Praefix vor
eine Meldung, die selbst schon mit "FEHLER:" begann, also aus schtasks kam.
FIX, zwei Teile:
1. Autostart ueber den Autostart-ORDNER statt schtasks. Eine Verknuepfung in
[Environment]::GetFolderPath("Startup") braucht NIE Adminrechte - und der
Nutzer kann sie dort selbst sehen und loeschen. Auf diesem Windows-PC
gegengeprueft: Verknuepfung angelegt (1047 Bytes) mit Admin=False.
2. Ein Fehlschlag beim Autostart ist jetzt eine WARNUNG in eigenem try/catch,
kein Abbruch. Der Worker ist zu dem Zeitpunkt fertig und startbar, und der
Text sagt das auch - plus den Handweg (shell:startup).
In BEIDEN Installern (install-gui.ps1 und install.ps1, dort mit -Autostart) -
der CLI-Weg hatte denselben Fehler. uninstall.ps1 raeumt jetzt die
Verknuepfung weg UND versucht weiter schtasks /delete, damit aeltere
Installationen sauber verschwinden.
RippyWorkerSetup.exe neu gebaut (50688 -> 52736 Bytes): die GUI ist in der .exe
eingebettet, ohne Rebuild wirkt der Fix beim Nutzer nicht.
Nebenbei: build-exe.ps1 hatte zwei Gedankenstriche. Ausgelieferte .ps1 muessen
reines ASCII sein (PowerShell 5.1 liest sie als ANSI) - jetzt sind alle drei
Skripte ASCII-rein und parsen fehlerfrei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b76ca5d9be |
docs(savepoint): install.sh und README-Umbau nachgetragen
Ampel / ampel (push) Successful in 28s
Der Block dokumentiert jetzt auch die letzte Runde: ein Befehl statt Checkliste, README neu aufgebaut, VM-CPU-Anleitung drin. Ehrlich als UNGETESTET markiert (neuer Punkt 6): install.sh ist nie als root durchgelaufen. Auf dieser VM sind alle root-Schritte No-Ops - Verzeichnisse liegen da, die Propagation ist schon shared - und sudo verlangt hier ein Passwort. Bewiesen sind Laufwerkserkennung (SCSI-Abgleich gegen die echte Hardware), die .env-Logik in drei Faellen und die Syntax. Das Anlegen der Verzeichnisse, mount --make-rshared und die systemd-Unit laufen erst bei einer echten Neuinstallation - das gehoert in die Uebergabe statt in eine Erfolgsmeldung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ca91e5b693 |
feat(install): ein Befehl statt Checkliste - install.sh nimmt die Handarbeit ab
Ampel / ampel (push) Successful in 28s
Commander-Rueckmeldung: "Das Docker Deployment ist mir zu kompliziert." Zu
Recht - die Installation war eine Checkliste aus sechs Schritten, von denen
zwei nur mit Fachwissen zu schaffen waren.
## Neu: sudo ./install.sh
Nimmt genau die Schritte ab, an denen man scheitern konnte:
1. Prueft Voraussetzungen (Linux, Docker, Compose) und nennt bei jedem Mangel
den Installationsbefehl.
2. FINDET DAS LAUFWERK SELBST. Das war der schlimmste Punkt: MakeMKV braucht
ZWEI Geraeteknoten, und die sg-Nummer ist je Host anders. Das Skript gleicht
sie ueber die SCSI-Adresse in /sys ab statt zu raten. Auf der VM
gegengeprueft: sr0 -> 3:0:0:0, sg1 -> 3:0:0:0, also dasselbe Geraet -
korrekt als /dev/sr0 + /dev/sg1 erkannt.
3. Legt Ablage- und MakeMKV-Verzeichnis an.
4. Richtet die Mount-Propagation ein UND macht sie per systemd-.mount-Unit
neustart-fest. Vorher stand in der README nur "reboot-fest persistieren" -
ohne zu sagen wie; nach einem Reboot scheiterte das Einhaengen von
NAS-Freigaben aus dem UI stillschweigend.
5. Schreibt die .env, ueberschreibt aber NIE einen bestehenden Wert.
6. Baut und startet, und nennt bei Fehlschlag die drei haeufigsten Ursachen
mit Diagnosebefehl.
Wiederholbar (mehrfach ausfuehren aendert nichts kaputt) und damit auch der
Update-Weg: git pull && sudo ./install.sh
## Zwei Fallgruben, die beim Testen auffielen - beide meine eigenen
- --nur-pruefen verlangte root und brach ab. Ein Pruef-Modus, der nichts
aendert, darf daran nicht scheitern - sonst kann man vor der Installation
nicht nachsehen, ob alles passt. Behoben.
- .env.example hatte OPTICAL_SG=/dev/sg1 UNKOMMENTIERT vorbelegt. Damit haette
der Installer den erkannten Wert nicht eingetragen ("steht schon drin") und
auf jedem fremden Host still eine kaputte Konfiguration hinterlassen - genau
das, was er verhindern soll. Beide Geraetezeilen sind jetzt auskommentiert;
Compose hat ohnehin Vorgaben. Dazu eine Gegenprobe im Skript: zeigt ein
wirksamer Wert auf ein Geraet, das es hier nicht gibt (".env von einem
anderen Rechner"), wird das benannt statt kryptisch von Docker gemeldet.
## README neu aufgebaut
Vorher 293 Zeilen, in denen der Schnellstart zwischen lsscsi, sg-Knoten,
USB-Passthrough und mount --make-rshared begraben war. Jetzt: Installation in
zwei Zeilen oben, dann eine Tabelle "Wenn etwas nicht geht" mit den vier
Faellen, die praktisch alles abdecken. Alles Technische steht darunter in
aufklappbaren Abschnitten - inklusive der Handarbeits-Variante fuer die, die
sie wollen.
NEU und ausdruecklich gewuenscht: Abschnitt "Rippy schneller machen" mit dem
VM-CPU-Typ. Erklaert, warum Virtualisierer eine generische CPU ohne AVX2
geben, was das kostet (gemessene 28-55 h je 4K-Film), die genauen Schritte in
Proxmox (herunterfahren - Hardware/Processors/Type auf 'host' - starten, bzw.
qm set <vmid> --cpu host), warum ein Neustart von innen NICHT genuegt, und wie
man nachprueft: Rippy zeigt die Vektorbefehle seit v3.14 selbst an. Dazu der
Nachteil (keine Live-Migration auf andere CPUs) und die Alternative x86-64-v3
fuer Cluster.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
1eb1c91dd8 |
docs(savepoint): v3.15 - Aufraeum-Runde mit gemessenen Zahlen
Ampel / ampel (push) Successful in 28s
Fuenf Vorgaben, was daraus wurde:
Codebase sauber -> vier tote Routen + zwei Module weg (main.py 1726 -> 1682
Zeilen), Tests halten sie draussen
UI schnell -> /capabilities 1,010 s -> 0,003 s; Dashboard-Aufbau
1,03 s -> 0,028 s. Alle 15 Ladeendpunkte unter 10 ms
UI selbsterklaerend -> Wizard-Kasten "Was Rippy gerade sieht" mit
Handlungsanweisung je Problem, sichtbare Keys mit Pruefung
Idiotensicher -> Wizard empfiehlt nach GEMESSENER CPU statt H.265 blind;
4K-Falle ist damit zu
Externe Worker -> auf Windows gegengeprueft (Kerne/Modell korrekt, encoders
ohne HandBrake korrekt leer, SIMD ehrlich "unbekannt")
Dazu die offene 4K-Frage aus v3.14 beantwortet: Kompression ist je Disc-Typ
abwaehlbar, 4K kann verlustfrei bleiben waehrend DVD/Blu-ray weiter schrumpfen.
ARM-Vergleich drin: fast alles, was ARM automatisch macht, macht Rippy schon -
und meist gruendlicher. Zwei echte Luecken benannt und NICHT gebaut
(ISO-Sicherung fuer Datentraeger, mehrere Laufwerke gleichzeitig), weil das
Funktionen sind und keine Reparatur. Ehrlich dabei: ob Rippy parallel rippen
kann, ist unbewiesen - mit einem Laufwerk nicht testbar.
Offene Punkte stehen unter "NOCH OFFEN", darunter der VM-CPU-Typ (qemu64 statt
host - AVX2 waere ein VM-Neustart und 2-4x schneller).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
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>
|
||
|
|
11597eb00c |
perf+cleanup(api): /capabilities war 1,0 s; vier tote Endpunkte entfernt
Ampel / ampel (push) Successful in 28s
Beides in main.py, deshalb ein Commit.
## 1. Das "Laggen" hatte genau eine Ursache
Gemessen ueber alle 15 Endpunkte, die das UI beim Laden braucht:
/capabilities 1,010 s
/system/updates 0,491 s (haengt am Knopf, nicht am Seitenaufbau)
/metadata/status 0,412 s (dito)
die anderen 12 < 0,025 s
/capabilities ist der einzige langsame, der beim SEITENAUFBAU zuschlaegt - und
fuenf Stellen holen ihn (Dashboard, Einstellungen, Worker-Tab, Wizard,
Rip-Dialog). Jede Seite zahlte eine Sekunde.
Die Ursache ist kein Fehler, sondern das Wesen des Celery-Pings: er sammelt
Antworten bis zum Timeout und kann nicht frueher aufhoeren, weil er nicht
weiss, wie viele Worker noch antworten wollen. Den Timeout zu kuerzen wuerde
Antworten langsamer Remote-Worker verschlucken - also genau die Maschinen, um
die es beim externen Encoding geht.
Jetzt pingt eine Hintergrund-Schleife im 5-s-Takt (neben Disc-Watcher und
Key-Refresh, die es dort schon gibt), der Endpunkt liest nur ab. Vorrat aelter
als 30 s - Schleife noch nicht angelaufen oder gestorben - dann EINMAL synchron
pingen: lieber langsam als falsch ("alles offline", obwohl alles laeuft).
## 2. Vier tote Endpunkte raus
Jeder ein Ueberrest eines ersetzten Entwurfs, keiner mit Aufrufer (mechanisch
gegengeprueft: alle api.*-Aufrufe des UI gegen alle Routen):
POST /prescan Metadaten-Vorschau-Seite ist seit v3.4 weg.
Die PreScan-Klasse bleibt - sie hat 5 echte
Fundstellen, der Watcher ruft sie im Prozess.
POST /jellyfin/format Macht seit v3.2 der Worker (medien.py), und
zwar an der richtigen Stelle: er kennt den
Ausgabeordner und ist nach dem Rip am Zug.
Mit ihm fallen nfo_generator.py und
image_downloader.py weg (sonst unbenutzt).
GET /stream/jobs Der unangenehmste: erst Placebo, am 23.07.
"repariert" statt entfernt - aber ein
EventSource im UI gab es nie (das Dashboard
nutzt setInterval(..., 4000)). Also keine
harmlose Leiche, sondern eine Endlosschleife
je Verbindung, die jeder aufmachen konnte.
GET /worker-setup/windows-gui Ohne Aufrufer seit die .exe den .bat-Umweg
ersetzt hat (v3.9). install-gui.ps1 selbst
lebt weiter, sie steckt in der .exe.
main.py: 1726 -> 1682 Zeilen, dazu 279 Zeilen in zwei geloeschten Modulen.
Tests halten beide Seiten fest: die vier Routen muessen WEG bleiben, und die
drei, an denen die Worker-Installation haengt (/worker-setup/paket, /windows,
/windows-exe), muessen DA sein. Ausserdem eine Doppelung entfernt - mein
eigener _sicherer_dateiname-Test aus dem Vorcommit pruefte dasselbe wie der
bestehende test_dateiname_validierung_blockt_pfad_tricks, und der war die
ganze Zeit korrekt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b546c79b9e |
docs(savepoint): deployt und aufgeraeumt - Stand 19:30 mit Belegen
Ampel / ampel (push) Successful in 28s
Der Block behauptete "nichts ist deployt" und fuehrte Fragment wie
Rohschnitt als offene Punkte. Beides ueberholt - genau solche stehen
gebliebenen Zusagen waren das Thema dieser Sitzung.
Nachgezogen mit Belegen statt Behauptungen:
-
|
||
|
|
a1aabd591d |
fix(test): Ampel rot - der Backslash-Test prueft nichts
Ampel / ampel (push) Successful in 28s
Meine eigene Zeile aus dem vorigen Commit. Im Quelltext stand "a\b.mkv" mit EINEM Backslash - Python liest \b als Backspace-Zeichen, der String enthaelt also gar keinen Backslash, und _sicherer_dateiname gab korrekt True zurueck. Der Test behauptete, den Backslash-Pfad zu pruefen, und tat es nicht. Jetzt ein Raw-String r"a\b.mkv". Warum das lokal nicht auffiel: test_api_smoke.py ueberspringt sich unter Windows selbst (main.py -> detection.py -> fcntl). Genau die Luecke, die im Savepoint als offener Punkt steht - hier hat sie sofort zugeschlagen. Lehre: Tests, die nur in der Ampel laufen, sind erst nach dem Push bewiesen, und Backslashes gehoeren in Raw-Strings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8394de6926 |
fix(transcode): "Abbrechen" wirkt sofort statt erst beim naechsten Prozent
Ampel / ampel (push) Failing after 28s
Fund des Commanders am laufenden Akira-Job (Nachtrag im vorigen Savepoint):
Job stand auf 'canceling', HandBrake lief weiter. Gemessen 3,4 Minuten
zwischen Anforderung (18:30:17) und Bestaetigung (18:33:38) - bei langsamerem
Fortschritt entsprechend mehr.
Ursache genau wie dort beschrieben: Der Abbruch wurde nur in
datei_fortschritt geprueft, und diese Closure stieg oben sofort wieder aus,
wenn sich die Prozentzahl nicht geaendert hatte ("if gesamt == letzter[0]:
return"). Bei einem Prozent je halber Stunde hing der Abbruch also an einem
Ereignis, das eine halbe Stunde lang nicht eintrat.
run_handbrake bekommt jetzt einen eigenen abbruch_cb neben progress_cb -
dasselbe Muster, das run_makemkv schon fuer log_cb benutzt, und aus
demselben Grund: der Fortschritts-Kanal verwirft Aufrufe. Er wird bei JEDER
Ausgabezeile aufgerufen und im Worker auf 5 s gedrosselt.
Nebeneffekt, der genauso wichtig ist: Er greift auch waehrend des
Scan-Durchlaufs, der ueberhaupt keine Encode-Prozente liefert. Dort war ein
Abbruch vorher grundsaetzlich unmoeglich - und seit die Prozent-Regex den
Scan korrekt ignoriert, waere das sonst sogar schlimmer geworden.
Die Leseschleife ist als _handbrake_schleife() herausgezogen, damit die
Reihenfolge (Abbruch VOR Fortschritt) ohne echtes HandBrake pruefbar ist.
Der Test belegt: zwei Scan-Zeilen genuegen fuer den Abbruch, es muss NICHT
auf eine Encode-Zeile gewartet werden.
Savepoint nachgezogen: Der Encode laeuft nicht mehr, ein Deploy ist
gefahrlos moeglich, und die alte "SOFORT ENTSCHEIDEN"-Passage (Platte
laeuft voll, wenn der Job fertig wird) ist damit gegenstandslos. Neu darin:
die drei Wege fuer UHD, und die Warnung, dass eine /proc-Suche nach
"HandBrake" die eigene Shell mittrifft - zwei Fehlalarme in dieser Sitzung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
574354131c |
docs(savepoint): v3.14 - Durchsicht, mit den zwei eigenen Fehlschluessen
SAVEPOINT bekommt den Stand v3.14: der unwirksame Platten-Schutz zuerst (wichtigster Fund), dann die gemessenen Werte, das Gebaute, die vier toten Endpunkte als Entscheidungsvorlage - und ein Abschnitt "SOFORT ENTSCHEIDEN". Der laufende Akira-Encode wird beim Fertigwerden mit dem DEPLOYTEN, alten Code versuchen, 75 GB in 37 GB zu kopieren. keepOriginal steht auf true, und den Wert hat die Aufgabe beim Start gelesen - jetzt umstellen aendert daran nichts mehr. Der Savepoint nennt die drei Optionen mit Empfehlung. Zwei Richtigstellungen an v3.13, beide durch Messung: - "progress=99 ist ein Altwert aus dem Absturz" war falsch. Beide Startpfade setzen auf 0; der Wert kam frisch vom Scan-Durchlauf. Ein Bug, kein Ueberrest. - Die Erwartung, _original_aufheben werde nur warnen, war falsch - siehe EXDEV-Fund im vorigen Commit. Ehrlich als OFFEN markiert: Was die Platte am 25.07. mittags fuellte, ist NICHT belegt. Es gibt keine Kopier-Reste, kein original-Verzeichnis und keinen einzigen Log-Eintrag von _original_aufheben, das in beiden Zweigen loggt. Der Mechanismus ist jetzt bewiesen, sein Zuschlagen an jenem Tag nicht. Lieber offen lassen als eine dritte Vermutung aufstellen. AGENTS bekommt Etappe 19 und einen neuen Abschnitt, der das wiederkehrende Muster benennt: nicht aus einem Zustandswert auf einen Mechanismus schliessen - vier belegte Faelle. Plus die Umkehrung, die diese Sitzung gekostet hat: gleiches st_dev heisst NICHT gleicher Mount. Was ausprobierbar ist, wird ausprobiert statt vorhergesagt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ef0a574a70 |
fix(worker,api): Platten-Schutz griff nicht, Fortschritt log, Auswurf tat nichts
Vier Funde aus der Durchsicht, alle auf der VM gemessen.
1. DER PLATTEN-SCHUTZ AUS
|
||
|
|
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>
|
||
|
|
e0cb7b3ddc |
feat(worker): Zombie-Erkennung - Jobs, an denen niemand arbeitet
Nach einem Absturz oder Rebuild blieb ein Job auf 'transcoding' stehen, obwohl weder ein Prozess lief noch etwas in den Queues stand. Folge: keine Anzeige, kein Download - und der Knopf "Neu komprimieren" fehlte, weil _kann_neu_komprimieren (api/main.py) status=='failed' verlangt. Der Job war unerreichbar, obwohl die Rohdateien vollstaendig dalagen. zombies.py haelt beim Worker-Start die Jobs in 'ripping'/'transcoding'/ 'canceling' gegen Celerys active/reserved/scheduled und setzt sie ehrlich auf 'failed', wenn niemand daran arbeitet. Drei Sicherungen, weil ein falsch getoeteter Job teurer ist als eine stehengebliebene Leiche: - nur beim Start (da ist "es lief nichts" eindeutig; ein periodischer Lauf koennte einen Job erwischen, der legitim in der Warteschlange wartet) - 120 s Gnadenfrist (Celery stellt unbestaetigte Aufgaben erneut zu) - Vollzaehligkeit: antworten weniger Knoten als laut Herzschlag online sind, wird NICHTS gewertet - sonst waere der laufende Job eines beschaeftigten Remote-Workers eine falsche Leiche Die Job-ID wird per Textsuche ueber die Inspektions-Antwort gefunden, nicht per Position: sie steht bei rip_disc an zweiter, bei transcode_files an erster Stelle, und Celery liefert args je nach Version als Liste oder Text. 13 Tests ohne Postgres/Redis, darunter "laufender Job wird nicht angetastet" und "schweigender Worker verhindert jedes Urteil". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5cd1876c90 |
docs(savepoint): Nachtrag - 4K-Transcode abgebrochen, zwei neue Befunde
Ampel / ampel (push) Successful in 48s
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>
|
||
|
|
b526a0a033 |
docs(savepoint): Uebergabestand v3.13 - gemessen vs. vermutet getrennt
Ampel / ampel (push) Successful in 46s
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
f74f4e54f6 |
fix(api): Job-Meta (poster_path) an die Jobliste durchreichen
Ampel / ampel (push) Successful in 32s
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> |
||
|
|
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> |
||
|
|
6a082cf77c |
fix(mounts): mounten() idempotent - stale/tote Mounts vor Re-Mount loesen
Ampel / ampel (push) Successful in 28s
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> |
||
|
|
46a8f50c34 |
fix(api): remount nicht-blockierend (Netz-Mount darf API-Start nicht haengen)
Ampel / ampel (push) Successful in 28s
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> |
||
|
|
3f47b504c2 |
feat(api): MakeMKV-Beta-Key automatisch erneuern
Ampel / ampel (push) Successful in 28s
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>
|
||
|
|
71c618073e |
docs: Anleitung auf .exe-Installer, SAVEPOINT v3.9
Ampel / ampel (push) Successful in 36s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
3aef344b95 |
docs: SAVEPOINT v3.7 — Mount-Reparatur, HandBrake-Konsistenz, Encoder-Wahl (live bewiesen)
Ampel / ampel (push) Successful in 28s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc1ceda0c2 |
fix(capabilities): Node-Zuordnung eindeutig bei mehreren Workern pro Host
Ampel / ampel (push) Successful in 28s
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> |
||
|
|
2de0899ac6 |
fix(installer): install.ps1 ASCII-sauber (Gedankenstrich zerschoss das Skript)
Ampel / ampel (push) Successful in 28s
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> |
||
|
|
47d07d6168 |
fix(mounts): Reparatur crashte an makedirs + reachable-Check auf 3s begrenzt
Ampel / ampel (push) Successful in 28s
- 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> |
||
|
|
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>
|
||
|
|
fe1387b1ec |
fix(ui): typLabel in DeviceDiscovery definiert (Laufwerks-Details-Button gefixt) & Settings Tab-Wechsel robuster gemacht
Ampel / ampel (push) Successful in 28s
|
||
|
|
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>
|
||
|
|
b130eeb967 |
feat(ui): Master-Panel 'Ripping Operations' DIREKT unter Laufwerke verschoben
Ampel / ampel (push) Successful in 28s
|
||
|
|
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
|