0088948b4f584b72ba403f057bcbe2e8a5b7d489
93
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1b84ec2a45 |
fix(windows): Vollstaendiger Rundgang durch die Docker-Reste
Ampel / ampel (push) Successful in 1m20s
Commander: „Bro, du musst alles was rippy jetzt im code hat für Windows
Bauen! Jeden pfad, alles wo die tools drauf zugreifen. Diese Rippy version
MUSS 100% Windows Kompatibel sein. Prüfe bitte den kompletten Quellcode nach
Docker Resten."
Systematisch gesucht statt Fundstelle fuer Fundstelle: feste POSIX-Pfade,
Linux-Programme, POSIX-eigene Aufrufe, `shutil.which`, `posixpath` auf echten
Pfaden, Container-Texte. Sechs echte Fehler dabei.
## 1. `/dev/{name}` in drei Endpunkten — der schwerste
Das UI ruft `/devices/{id}/eject`, `/scan-tracks` und `/tracks` mit der
Kennung aus der Geraeteliste auf, unter Windows also `G`. Gebaut wurde daraus
`/dev/G` — steht in keiner Laufwerksliste. **Auswerfen und „Disc scannen"
antworteten unter Windows IMMER mit 404**, ohne dass irgendwo stand, warum.
Hin- und Rueckweg gehoeren zusammen: Beide Treiber haben jetzt `kennung()`
und `pfad_zu_kennung()`. Wer die Kennung vergibt, loest sie auch auf.
## 2. `os.path.isdir("/app")` — zum zweiten Mal
Nach `caps.py` (heute frueh) auch in `ablauf.py`: Der eigenstaendige
Windows-Rippy hielt sich fuer einen FREMDEN Worker und haette sich selbst
vorgeworfen, Container-Pfade nicht zu erreichen — auf einer Maschine ohne
Container. Die Entscheidung ist jetzt einspritzbar; vorher hing der Test
daran, ob es einen Ordner `/app` gibt.
## 3. `shutil.which` in `schluessel.py`
Ausgerechnet im Modul, das es NUR unter Windows gibt: Es suchte makemkvcon im
PATH, wo unter Windows nie ein Programm aus „Programme" steht. Die
Schluessel-Automatik fuer 4K-UHD lief damit nie an.
## 4. `posixpath.join` auf echten Pfaden
`rohdaten.py` baute `C:\Roh/datei.mkv` — gemischte Trenner, die im UI falsch
aussehen und jeden Vergleich brechen.
## 5. Container-Pfad in einer Nutzermeldung
„Roh-Datei bleibt in /app/temp erhalten" nennt jetzt den echten Ordner. Wer
die Datei retten will, sucht sonst am falschen Ort.
## 6. Container-Pfade als UI-Vorbelegung
Rip-Dialog und `useBetrieb` starteten mit `/app/media`, bis die Antwort da
war. Leer ist ehrlicher: Es behauptet nichts.
## Und HandBrakes „Code 0"
Code 0 heisst ERFOLG. Rippy meldete trotzdem „fehlgeschlagen", weil die Datei
nicht am erwarteten Ort lag: **HandBrake bestimmt den Container aus dem
PRESET, nicht aus der Endung** — ein MP4-Preset schreibt `.mp4` neben das
verlangte `.mkv`. Jetzt erzwingt `--format` den Container passend zur Endung
(an HandBrake 1.11.2 gegengeprueft), und falls doch etwas daneben liegt, wird
es gefunden statt weggeworfen.
## Der Waechter
`test_keine_container_reste.py` prueft mechanisch, dass im Windows-Weg kein
Container-Pfad ohne Begruendung steht. Die Ausnahmen stehen namentlich mit
Grund da (Linux-Zweige, benannte Rueckfaelle) — und ein zweiter Test wirft
jede Ausnahme raus, die niemand mehr braucht.
Ueber den Tokenizer, nicht ueber „faengt mit Anfuehrungszeichen an": Der
erste Anlauf blieb prompt an seinem eigenen `r\"\"\"`-Docstring haengen.
887 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e67138e9ca |
feat(ui): Laufende Disc-Erkennung ist jetzt sichtbar
Ampel / ampel (push) Successful in 1m26s
Commander: „das die disc erkennung noch läuft muss sichtbar sein"
Er hat recht, und die Wartezeit ist gemessen: `makemkvcon info` laeuft an
einer Blu-ray in seine 120-Sekunden-Grenze (Disc nach 119 s erkannt). Zwei
Minuten, in denen NICHTS zu sehen war — weder die Disc noch ein Hinweis. Das
sah aus wie ein leeres Laufwerk, also wie ein Fehler.
## Der Zustand war da, er wurde nur weggeworfen
`_auto_prescan` legt beim Start `{"_laeuft": True}` in den Vorrat.
`laufwerke_mit_disc` liess so einen Eintrag KOMPLETT weg — damit keine
halbfertige Disc-Karte mit „Wird erkannt…" als Titel und einem „Rippen
starten"-Knopf erscheint. Richtig gedacht, falsch geloest.
Jetzt gibt es ein eigenes Feld `disc_wird_erkannt`. Die Oberflaeche kann
„wird gelesen" zeigen, ohne einen Titel zu behaupten, den es noch nicht gibt:
eine Karte mit Spinner ueber der Laufwerksliste und eine eigene Zeile im
Server-Status.
## Und der Haken dahinter
Genau waehrend der Erkennung ist die Laufwerks-Auskunft am teuersten:
makemkvcon haelt das Laufwerk, und `device_info` wartet mit. Gemessen:
/devices waehrend des Scans 14,0 s
Zeitgrenze des Schnappschusses 5,0 s -> devices = None
`None` heisst „konnte nicht nachsehen", und die Oberflaeche behaelt dann
ihren Stand — nach einem frischen Laden also GAR NICHTS. Die Anzeige waere
ausgerechnet in ihrer eigenen Phase leer geblieben.
`laufwerke_notdurft()` antwortet deshalb ohne ioctl: letzter bekannter Stand
plus die aktuelle Erkennungs-Marke aus dem Vorrat. Beim Kaltstart wenigstens
der Laufwerksname — die LISTE der Laufwerke ist billig, teuer ist erst das
Anfassen. Nichts wird erfunden: kein Typ, kein Modell. Und wer noch nie
erfolgreich gelesen hat, schweigt weiter mit `None`.
Im Browser gegengeprueft: „Disc wird gelesen — Laufwerk G:" mit Spinner,
Server-Status „Disc wird gelesen — das dauert bis zu zwei Minuten", danach
der richtige Titel.
859 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
7b0c41ddfe |
fix(ui): Datum 1.1.1970, Disc nicht erkannt, kein Abbrechen — eine Ursache je Fall
Ampel / ampel (push) Successful in 1m16s
Vier Befunde aus einem Bildschirmfoto vom 29.08.2026.
## 1. „das Datum ist falsch" — und der Typ auch
TYP „DISC" STARTZEIT „1.1.1970, 01:00:00" STATUS „running"
⚠️ Das war MEIN Fehler von heute Vormittag. Ich hatte `type`, `device` und
`startTime` in das Job-Ereignis aufgenommen — mit Feldnamen, **die es in der
Datenbankzeile nicht gibt**. Dort heissen sie `disc_type` und `created_at`;
die Uebersetzung macht `_job_row_to_model`, und sie bildet auch `running` auf
`processing` ab.
Die Folge war schlimmer als das Problem davor: Statt eines FEHLENDEN Feldes
kam ein LEERES. `new Date(null)` ist der 1.1.1970, und „DISC" war der
Rueckfall, den ich zwei Stunden vorher fuer genau diesen Fall eingebaut hatte.
Aus „offensichtlich kaputt" war „sieht plausibel aus" geworden.
Und mein Test hat es nicht gefunden, weil ich seine Beispielzeile selbst
erfunden habe — mit meinen falschen Feldnamen. Ein Test, der dieselbe Annahme
macht wie der Code, prueft nichts. Der neue geht von der ECHTEN Zeile aus.
Der Waechter bekommt jetzt dieselbe Uebersetzung eingespritzt, die auch
`/jobs` benutzt.
## 2. „Es ist eine Disk im laufwerk, aber er erkennt sie dort nicht"
Die Laufwerksliste wurde an DREI Stellen gebaut: `/devices` haengte die
erkannte Disc an, der Ereignis-Waechter und der SSE-Schnappschuss nicht. Das
Dashboard liest den Schnappschuss — und schloss aus der fehlenden Disc auf ein
leeres Laufwerk, waehrend ihr Titel eine Zeile weiter oben stand.
Jetzt gibt es `laufwerke_mit_disc()`, und ein Test zaehlt die Aufrufe von
`device_info` — bei zwei ist er rot.
## 3. „man kann einen rip garnicht abbrechen"
Dieselbe Ursache wie 1: Es gab genau EINEN Abbrechen-Knopf, in der Kachel
„laufender Job". Die erscheint nur bei Status `processing`; der Strom lieferte
`running`. Keine Kachel, kein Knopf, kein Weg zurueck — und aus demselben
Grund stand „Aktiv (0)", waehrend der Rip lief.
Zusaetzlich gibt es den Knopf jetzt in der Job-Zeile selbst, wo man ihn sucht.
## 4. „bei ‚Platz für rippy' sollte eher das arbeitsverzeichnis und der
## Ablagepfad sein"
Dort stand eine nackte Zahl fuer den ERSTEN Ort. Welcher Ordner das war, sah
man nicht — und die zweite Zeile fiel ganz weg, wenn beide auf derselben
Platte lagen. Das stimmt fuer die ZAHL und war der falsche Schluss fuer den
ORT. Jetzt stehen beide da, mit Pfad, plus der Hinweis, dass der Platz
geteilt wird.
Im Browser gegengeprueft: „Disc erkannt — wartet auf ‚Rippen starten'",
BLU-RAY mit richtigem Datum, beide Pfade, keine Konsolenfehler.
852 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b2acddbdfa |
fix(windows): Drueberinstallieren, Linux-Reste im Windows-Betrieb, Ordner-Waehler
Ampel / ampel (push) Successful in 1m27s
Vier Fragen des Commanders vom 29.08.2026, drei davon mit Codefolge.
## 1. „Was passiert wenn man die Setup.exe einfach drueber installiert?"
Bis hierher: nicht zuverlaessig. Rippy startet mit Windows, laeuft also fast
immer — dann ist `Rippy.exe` gesperrt, `shutil.copy2` warf PermissionError,
und die neue Fassung landete als `Rippy.exe.neu` daneben. Dazu die Meldung
„wird beim naechsten Start uebernommen".
**Diese Zusage hat niemand eingeloest.** `.neu` kam im ganzen Projekt genau
einmal vor: an der Stelle, die es schrieb. Wer drueberinstallierte, behielt
still die alte Fassung, und das Setup meldete Erfolg.
Jetzt wird der laufende Rippy vorher beendet (`dienst_beenden` gibt es seit
der Deinstallation und wartet auch die zwei Sekunden ab, die Windows fuer die
Dateihandles braucht). Eine von einer aelteren Setup-Fassung liegengelassene
`.neu` wird dabei uebernommen. Bleibt die Datei DANN noch gesperrt, gibt es
einen klaren Fehler statt einer Zusage — Rippy im Infobereich beenden und das
Setup erneut starten.
Der Tausch laeuft bewusst im SETUP und nicht beim Dienststart: Windows sperrt
eine laufende .exe, und `Rippy.exe` waere genau die zu ersetzende Datei.
## 3. Linux-Reste im Windows-Betrieb (Docker/Headless unveraendert)
**`caps.py`: `os.path.isdir("/app")`.** Damit hielt sich der eigenstaendige
Windows-Rippy fuer einen FREMDEN Worker — und das UI warnte vor fehlender
Pfad-Uebersetzung auf einer Maschine ohne Container und ohne Freigabe.
„Extern" heisst jetzt, was es meint: Rippy laeuft woanders als dieser Worker.
**`caps.py`: `shutil.which("makemkvcon")` + `os.path.ismount(daten_dir)`.**
Beide unter Windows immer falsch (Programme liegen nicht im PATH, ein
normaler Ordner ist kein Mount). Die Schluessel-Auskunft blieb dauerhaft
„unbekannt", obwohl MakeMKV samt Datenverzeichnis da war. Der Mount-Test
bleibt fuer den Container, wo er einen Zweck hat.
**`rohdaten.py`: `/app/temp/raw` und `/app/media` fest.** Dieses Modul findet
die Rohdaten eines Jobs wieder — fuer den Wiederholen-Dialog und fuer
„Rohdaten mitloeschen". Unter Windows fand es NIE etwas: Der Dialog meldete
„keine Rohdaten", das Aufraeumen loeschte nichts, und die Bruchstuecke eines
abgebrochenen Rips blieben liegen (bei 4K-UHD bis 100 GB).
Sieben Tests wurden dabei rot, und zwar zu Recht: Sie pruefen Container-Regeln,
liefen aber unter Windows. Die Wurzeln sind jetzt einspritzbar — beide
Betriebsfaelle auf jedem Rechner pruefbar statt vom laufenden abhaengig.
## 4. „Der Durchsuchen button fehlt. Wie es der Installer auch macht"
Neu: `OrdnerWaehler` — Pfadfeld plus „Durchsuchen …", benutzt fuer Ablage und
Arbeitsverzeichnis. Es waere der DRITTE fest eingebaute Ordner-Browser
geworden (RipTargetModal, StorageMounts); dieser hier ist wiederverwendbar.
`/browse` weiss seit dem 28.08. selbst, in welchem Betrieb es laeuft.
⚠️ Beim Einbau fiel der Import unter den Tisch. `vite` pruefte das NICHT — das
Buendel blieb byte-gleich gross, und zur Laufzeit waere es der naechste leere
Bildschirm gewesen. Aufgefallen nur, weil die erwartete Anzahl Ersetzungen
nicht stimmte. Im Browser gegengeprueft: alle sieben Laufwerke, Navigation in
D:\, keine Konsolenfehler.
843 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
27c9d9a4bb |
fix(ui): Leerer Bildschirm nach „Rippen starten" — ein halber Job riss alles mit
Commander: „wenn man auf rippen starten klickt passiert irgendwas, was das
Programm nicht mag. Der Hintergrund ist einfach leer und er startet nix. Es
gibt auch keine fehlermeldung."
Nachgestellt: Dienst lokal gestartet, Disc-Karte geoeffnet, „Rippen starten"
geklickt. Browser-Konsole:
TypeError: Cannot read properties of undefined (reading 'toUpperCase')
## Er startet sehr wohl — man sieht es nur nie
Waehrend die Seite leer war, lief der Job (per API gemessen: status
`processing`, progress 12). Das „er startet nix" ist also der zweite Teil
desselben Fehlers: Die Oberflaeche war weg, bevor sie ihn zeigen konnte.
## Die Ursache
`_job_kurz` in `bus/waechter.py` trug nur `status`, `progress`, `title` und
`error` — **keinen `type`**. Das UI fuegt aus `job.created` einen halben Job
in seine Liste ein, `LiveLogSection` liest `job.type.toUpperCase()`, und React
baut bei einem Fehler im Zeichnen den GESAMTEN Baum ab. Es gab in diesem
Projekt keine einzige Fehlergrenze — also blieb ein leerer Bildschirm ohne
jede Meldung.
## Drei Reparaturen, weil es drei Fehler waren
1. **Das Ereignis traegt den Job.** `type`, `device` und `startTime` fahren
mit. Sie aendern sich ueber die Lebenszeit eines Jobs nie, kosten also kein
zusaetzliches Ereignis — und ohne `startTime` stand in der Jobliste
sekundenlang „Invalid Date".
2. **Das UI vertraegt sein Fehlen.** `LiveLogSection` und `TypeBadge` nahmen
einen vollstaendigen Job an. Zeile 108 derselben Datei hatte das
Fragezeichen laengst, Zeile 48 nicht.
3. **Eine Fehlergrenze.** Ein Fehler in einer Karte darf nicht den ganzen
Bildschirm mitnehmen — und schon gar nicht schweigend. Jetzt steht da, was
los ist, die Navigation bleibt bedienbar, und der Hinweis sagt das
Wichtigste: laufende Rips gehen weiter.
## Warum es niemand gefunden hat
`unterschiede()` bekommt die Kurzform schon fertig, und alle Tests reichen
ihre eigenen Woerterbuecher herein. **`_job_kurz` selbst war nie geprueft.**
Jetzt bewacht `UI_PFLICHTFELDER` den Vertrag mechanisch — es gibt kein
gemeinsames Typsystem zwischen Python und dem UI.
Gegengeprueft im Browser: derselbe Klick, keine Konsolenfehler, Job erscheint
als „Alle (1)".
833 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
954f293871 |
fix(windows): Rippen ging nicht — drei Container-Annahmen im Rip-Pfad
Commander: „Das Rippen der disk funktioniert nicht. makemkvcon endete mit
Code 11 — letzte Meldung: Das Öffnen der Disk schlug fehl — keine MKV-Datei
entstanden"
In diesem einen Satz steckten drei Fehler. Alle drei an seinem Laufwerk
gemessen, nicht hergeleitet.
## 1. Die Quellenangabe — das war der Abbruch
dev:\.\G: MSG:2024 „Unknown device" -> MSG:5010 Öffnen schlug fehl
dev:G: 68 TINFO-Zeilen, „Die Aufgabe wurde erfolgreich abgeschlossen"
`f"dev:{device_path}"` stand an VIER Stellen. Unter Linux richtig (/dev/sr0),
unter Windows nicht: `rippy.drives.windows` meldet den Geraete-Namensraum
`\.\G:`, den `CreateFileW` fuer die IOCTLs braucht — MakeMKV kennt ihn
nicht.
## 2. „Ö" statt „Ö"
`Ö` als UTF-8 (C3 96), gelesen als cp1252. `subprocess` stand auf `text=True`
ohne `encoding=`, nahm also die Gebietsschema-Kodierung; Rippy stellt seine
Konsole beim Start aber auf UTF-8 (sonst stirbt der Start an einer
Umlaut-Logzeile). Direkt gemessen kommen die Bytes je nach Umgebung als UTF-8
ODER cp1252 — deshalb wird jetzt bestimmt statt behauptet.
## 3. Die Ursache fehlte im Fehlertext
Gesucht wurde nach ENGLISCHEN Textbausteinen („evaluation period has
expired"). Auf einem deutschen Windows trifft keiner. Jetzt zaehlen die
Meldungs-NUMMERN, die sprachunabhaengig sind.
⚠️ Beim ersten Anlauf hatte ich 5021 aus dem Kopf mit „Volume-Key unbekannt"
beschriftet — falsch. Meine eigene Ausgabe zeigte nur meine Beschriftung
statt MakeMKVs Text, sodass es fast durchgegangen waere. Der echte Wortlaut
steht jetzt im Code.
## Nebenbefund: der Beta-Key lag zweimal am falschen Ort
`makemkv_daten.DATEN_DIR` war fest `/root/.MakeMKV`. Nach der Reparatur des
Ordners blieb MSG:5051 trotzdem stehen — weil MakeMKV unter Windows seine
Einstellungen in der REGISTRY haelt (HKCU\Software\MakeMKV), nicht in einer
settings.conf. Nachgesehen statt vermutet.
Und der wichtigste Teil davon: **Ein abgelehnter Schluessel ist schlimmer als
gar keiner.** Ohne Key las MakeMKV die Disc noch (68 TINFO), mit dem
abgelehnten verweigerte es alles (0 Titel). Deshalb wird jetzt geprueft und
bei Ablehnung zurueckgenommen.
## Beweis
Echter Rip ueber `ripping.run_makemkv`, kuerzester Titel:
Status success, Code 0, „Evangelion 2.22_t03.mkv" 217,9 MB
5036 „Das Kopieren wurde abgeschlossen. 1 Titel wurden gesichert."
828 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
678fe276f9 |
feat(windows): Arbeitsordner im Setup, Beta-Key auf Knopfdruck
Ampel / ampel (push) Successful in 1m18s
Zwei Wuensche des Commanders vom 28.08.2026.
## 1. „kannst du noch einbauen das man den arbeitsordner usw. in den
## Einstellungen setzen kann und direkt im setup?"
In den Einstellungen ging es seit
|
||
|
|
d0281f7a1b |
style: Zeilenenden zurueck auf LF (kein Inhalt geaendert)
In
|
||
|
|
35370fd552 |
fix(windows): Cover, Arbeitsverzeichnis und die zurueckgelassenen _MEI-Ordner
Drei Befunde des Commanders vom 28.08.2026, alle drei gemessen.
## 1. „Was ist mit dem Cover auf Windows Rippy?"
Es gab keins, weil es keinen Treffer gab. `_scan_video` verlangte woertliche
Gleichheit:
if movie.get("title", "").lower() == kandidat.lower():
An seiner Disc gemessen:
'Evangelion 2.22' 1 Treffer Evangelion: 2.0 You Can (Not) Advance
'Evangelion' 20 Treffer irgendein Evangelion
Der EINZIGE Treffer auf den vollen Disc-Titel war der richtige Film, mit
Poster — und wurde verworfen. Jetzt zaehlt die SPEZIFITAET der Anfrage: Wer
auf den vollen Disc-Titel hoechstens drei Treffer bekommt, hat gefragt wie
jemand, der weiss was er sucht. Ergebnis an derselben Disc:
Evangelion: 2.0 You Can (Not) Advance / 2009 / 80 % / Poster + dt. Text
## 2. „Warum heisst das hier noch container platte? … Waere es moeglich das
## Arbeitsverzeichnis zu aendern? momentan geht das nicht."
Beide Haelften gehen auf EINE Zeile zurueck: `MEDIA_ROOT = "/app/media"` war
zugleich Vorgabe UND Pfadgrenze. Auf Windows gibt es den Ordner nicht:
* `/storage-targets` fing den OSError und gab still [] zurueck — die Auswahl
hatte genau einen Eintrag. Das ist „momentan geht das nicht".
* Dessen Text war fest verdrahtet „(Container-Platte)".
* `/browse` antwortete auf jeden Pfad mit 422.
Die Grenze faellt nicht weg, sie wird betriebsabhaengig: Container und
Kopflos-Betrieb bedienen ein Netz, die native App den Menschen davor.
Gemessen auf seinem PC: 7 Laufwerke zur Auswahl, X/Y/Z mit je 2,2 TB frei.
## 3. „Failed to remove temporary directory: …_MEI0000b0882"
GEMESSEN: 20 zurueckgelassene _MEI-Ordner mit 1,1 GB. Ursache: `Popen` ohne
`env=` reicht PyInstallers Auspack-Zeiger an die Kinder weiter (--dienst und
--oeffnen). Beide laufen dann im Ordner des Elternprozesses, der sich zuerst
beendet und ihn loeschen will. Waere das TEILWEISE geglueckt, haetten Dienst
und Fenster mitten im Betrieb ihre Dateien verloren.
Nebenbefund: test_setup_fenster scheiterte in jeder Umgebung ohne pywebview.
794 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8e65411189 |
fix(windows): die Datenbank lag AUSSERHALB der Installation — daher fehlte
Ampel / ampel (push) Failing after 54s
der Ersteinrichtungs-Assistent
Der Befund des Commanders: "Ausserdem fehlt der 1st run wizzard wenn man es
installiert." Meine erste Reparatur (App.tsx las einen gescheiterten Abruf
als "erledigt") war richtig, aber nicht die Ursache. Nachgemessen:
Installation %LOCALAPPDATA%\Rippy wird deinstalliert
Datenbank %ProgramData%\Rippy BLEIBT LIEGEN
Eine "frische" Installation erbte damit die alte Datenbank samt
`setup.done = true` -- der Assistent erschien nie wieder. Gegengeprueft:
nach dem Aufraeumen meldet /api/setup wieder {"done": false}, und das
Fenster zeigt "Willkommen bei Rippy".
Der Grund fuer den alten Ort stand im Docstring: "Ein Programmordner ist
unter Windows fuer einen DIENST nicht zuverlaessig beschreibbar." Das stimmte
-- fuer den Windows-Dienst, den es nie gab. Rippy laeuft als der angemeldete
Benutzer (Autostart unter HKCU). Jetzt liegt alles, was Rippy gehoert, in
EINEM Ordner: Programm, UI, Werkzeuge, Protokoll, WebView2-Zwischenspeicher
und die Datenbank.
Dazu:
* `datenbank_umziehen()` holt eine vorhandene Datenbank vom alten Ort ab --
wer Rippy schon benutzt hat, behaelt Jobs und Einstellungen. Gibt es am
neuen Ort schon eine, bleibt sie unangetastet: Die BENUTZTE gewinnt.
* `deinstallieren()` raeumt den alten Ort mit weg, und `alte_orte()` fasst
nur einen Ordner an, der wirklich "Rippy" heisst.
* Der Ersteinrichtungs-Assistent spricht nicht mehr von "Encoding-Worker",
wenn es keine gibt -- dort steht jetzt "Kompression". Gegengeprueft: kein
"docker", kein "Container", kein "Worker" mehr im Text.
Ampel lokal: 709 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
6dfff5c99d |
fix(windows): vier Befunde aus dem ersten echten Installationslauf
Alle vom Commander gemeldet, alle nachgestellt.
## 1. Der Installer brach ab: No module named 'db'
Fehlgeschlagen: ModuleNotFoundError: No module named 'db'
Zu Recht: **docker/api/db.py gibt es nicht.** Seit der Zusammenlegung in V2-1
ist der Store `rippy.store`; main.py schreibt seitdem
`from rippy import store as db`. Ich habe mir aus einem ALIAS ein Modul
zusammengereimt und importiert -- genau die erfundene Schnittstelle, vor der
AGENTS.md Regel D warnt, nur diesmal im eigenen Code. Der Store wird jetzt
direkt benutzt; der Umweg ueber den API-Suchpfad war ohnehin ueberfluessig.
## 2. Der Ersteinrichtungs-Assistent fehlte nach der Installation
In App.tsx stand:
.catch(() => setSetupDone(true)) // API/Setup nicht erreichbar -> nicht blockieren
Das Fenster geht unmittelbar nach dem Setup auf, der Dienst braucht ein bis
zwei Sekunden bis zur ersten Antwort -- der erste Abruf scheitert also fast
immer. Aus "ich konnte nicht nachsehen" wurde "ist schon erledigt", und der
Assistent war fuer immer weg.
Dasselbe Prinzip steht nebenan in useEventStream.tsx: Ein Verbindungsabriss
ist KEINE Aussage ueber die Welt. Jetzt wird nachgefragt, bis der Server
ANTWORTET (zwei Minuten lang alle zwei Sekunden), und solange steht
"Rippy startet ..." im Fenster statt einer leeren Flaeche.
## 3. + 4. Als Administrator keine Netzlaufwerke, Netzlaufwerk = "keine Rechte"
Beides Windows-Verhalten, kein Rippy-Fehler -- aber Rippy hat ihn
hineinlaufen lassen. Auf dem Rechner nachgemessen:
X:, Y:, Z: Netzlaufwerke
EnableLinkedConnections nicht gesetzt (Standard)
C:\Program Files (x86) ohne Administrator nicht beschreibbar
Ein erhoehter Prozess bekommt ein anderes Zugriffstoken; eingebundene
Netzlaufwerke haengen am Token der Sitzung und existieren dort schlicht
nicht. Daraus wird ein Teufelskreis:
Program Files verlangt Administrator
Administrator versteckt die Netzlaufwerke
Netzlaufwerk wirkt dann wie "keine Schreibrechte"
Rippy braucht ueberhaupt keine Administratorrechte (Vorgabeordner unter
%LOCALAPPDATA%, Autostart unter HKCU). Der Assistent sagt das jetzt:
* eine eigene Pruefung "Rechte", die bei erhoehtem Start warnt und die
unsichtbaren Laufwerke beim Namen nennt
* ein fehlender Laufwerksbuchstabe wird als solcher gemeldet, nicht als
fehlendes Schreibrecht ("Laufwerk Q: ist hier nicht vorhanden")
* ein Ziel unter "Programme" erklaert den Kreis und verweist auf den
Vorgabeordner
## Dazu: zwei Schreibfallen in AGENTS.md
Deutsche Anfuehrungszeichen in doppelt gequoteten Zeichenketten (vier Mal an
einem Tag) und Bash-Heredocs, die Backslashes halbieren (fuenf Mal, zuletzt
beim Schreiben genau dieses Absatzes). Ein Test dafuer waere Rauschen gewesen
-- Python faengt beides bereits beim Import, nur mit verwirrender Meldung.
Die Regel gehoert in die Konventionen, nicht in eine Pruefung.
Ampel lokal: 703 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
66642b8d93 |
feat(ui): die Oberflaeche weiss jetzt, worauf sie laeuft
Commander-Befund 28.08.2026, zum Windows-Fenster:
Worker erreichbar: 0 von 1
Kein Worker antwortet — Pruefen: docker compose ps
Container-Platte: unbekannt
Freigaben: keine eingehaengt
Kein Satz davon ergibt auf einem Windows-PC einen Sinn. Sein Urteil: "Du hast
ja quasi nur rippy genommen und die docker installation fuer Windows gebaut.
Das gilt fuer die ganze standalone version fuer Windows, auch fuer die
settings und die Anleitung usw."
## Die Ursache war nicht die Anzeige
Die naheliegende Reparatur waere ein `if (windows)` an dreissig Stellen
gewesen -- dieselbe Falle noch einmal, nur mit einer zweiten Sorte Vermutung.
Es fehlte etwas anderes: Das UI hat nie erfahren, worauf es laeuft. config.py
kennt das Profil seit V2-2, weitergegeben wurde es nie. Also hat das UI
angenommen.
Neu: GET /betrieb meldet FAEHIGKEITEN, keinen Modus-Namen.
externe_worker Gibt es andere Maschinen, die Jobs uebernehmen?
freigaben_einhaengen Kann Rippy Netzwerk-Freigaben selbst einhaengen?
container_pfade Sind Pfade wie /app/media ueberhaupt gemeint?
werkzeuge_verwalten Kann Rippy MakeMKV/HandBrake selbst beschaffen?
Ein Modus-Name wuerde das UI zwingen, aus einem Namen auf Verhalten zu
schliessen -- und das bricht beim naechsten Betriebsfall: Ein
Docker-All-in-One hat Container-Pfade, aber keinen zweiten Worker.
## Was sich sichtbar aendert (auf Windows nachgemessen)
Server-Status "Rippy arbeitet: auf diesem Rechner" statt Worker-Zaehler
"Platz fuer Rippy: 59.2 von 232 GB frei" statt "unbekannt"
Freigaben-Block faellt weg
Einstellungen kein Reiter "Worker"; Speicherziele BLEIBT (dort steht die
Ablage -- "wo will ich das hinspeichern" war die Frage),
aber mit Pfadfeld statt Container-Auswahl und ohne die
Maske zum Einhaengen
Anleitung kein docker, kein Encoding-Worker-Abschnitt, stattdessen
der echte Ordner (C:\Users\...\Videos\Rippy)
## Zwei Fehler, die dabei aufgefallen sind
* MEDIA_ROOT = "/app/media" war in main.py fest verdrahtet. shutil.disk_usage
warf unter Windows, die Liste blieb leer -- daher "unbekannt", obwohl auf
dem Laufwerk 59 GB frei waren. Eine Nichtauskunft, die wie eine Auskunft
aussieht. platz_orte() liefert die Orte jetzt je Betrieb, und ein noch
nicht angelegter Ordner faellt auf das naechste vorhandene Elternteil
zurueck.
* Der SSE-Schnappschuss enthielt den Server-Zustand NICHT, und der Waechter
schickt ihn nur alle 15 Sekunden. Nach jedem Neuladen stand deshalb bis zu
eine Viertelminute "unbekannt" da. Jetzt ist er im Schnappschuss, und das
UI uebernimmt ihn auch von dort.
Dazu: der Windows-Skip in test_api_smoke.py ist weg. Er stammte aus der Zeit
vor V2-4, als main.py fcntl brauchte; seit der Treiberwahl ueber den Port
laedt es auf beiden Plattformen (57 Routen, gemessen). Damit laufen 20 Tests
mehr auch lokal statt nur auf der Ampel.
Ampel lokal: 654 gruen, ruff sauber. Docker-Zweig durch Unit-Tests gedeckt,
am echten Container noch nicht gegengeprueft -- das kommt beim Deploy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
95705c8d88 |
feat(ui): V2-3 (Teil 2) — das UI haengt am Ereignis-Strom, Taktgeber raus
Ampel / ampel (push) Successful in 47s
WAS: EventStreamProvider haelt EINE SSE-Verbindung fuer die ganze
Anwendung. Dashboard, Log-Kasten, Log-Seite, Laufwerksliste und
Worker-Liste beziehen ihren Zustand daraus. Sieben von neun setInterval
sind weg.
GEMESSEN AN DEN TAKTGEBERN, je offenem Tab:
Dashboard 5 Endpunkte / 4 s 75/min -> 0
Log-Kasten 2 Endpunkte / 5 s 24/min -> 0
Laufwerke 1 Endpunkt / 5 s 12/min -> 0
Log-Seite 1 Endpunkt /10 s 6/min -> 0 (+1 Abruf beim Oeffnen)
Worker-Liste 1 Endpunkt /15 s 4/min -> 0 (+1 Abruf beim Oeffnen)
------
121/min -> ~2 einmalige Abrufe
Zwei Taktgeber bleiben bewusst: FirstRunWizard (laeuft nur VOR der
Einrichtung) und RipTargetModal (nur solange der Dialog offen ist).
DIE REGEL IST UMGEZOGEN, NICHT VERSCHWUNDEN: Ein Abriss ist keine
Aussage ueber die Welt. Der Provider BEHAELT bei einem Fehler den letzten
Stand und setzt nur `verbunden` auf false; es wird nie eine Liste geleert.
Jede Komponente uebernimmt einen Wert nur, wenn er wirklich da ist —
`devices === null` heisst "konnte nicht nachsehen", nicht "keine
Laufwerke". Das war der Fehler hinter "wird oft neu geladen".
EIN PLACEBO WENIGER: Oben rechts stand ein fest verdrahtetes "ONLINE" mit
pulsierendem Punkt — es leuchtete gruen, auch wenn die API tot war. Jetzt
zeigt es LIVE oder VERBINDUNG WEG, und im Tooltip steht, wann die letzte
Meldung kam.
DER SERVER SCHIEBT JETZT AUCH DEN SERVER-ZUSTAND: Neuer Ereignistyp
system.status (Hardware, Worker, Ablageziele) im 15-Sekunden-Takt des
Waechters — EINMAL im Server statt 15/min je Tab. Nur mitgeschickte
Schluessel werden uebernommen; ein fehlender heisst "behalte deinen Stand".
DAZU EIN FORMATFEHLER GEFUNDEN UND BEHOBEN: /logs bildet ts -> timestamp
ab, mein Snapshot lieferte die rohe DB-Zeile. Das UI haette "Invalid Date"
gezeigt — und zwar NUR im Live-Betrieb, nicht beim manuellen Neuladen.
Jetzt gibt es _log_zeile() einmal, benutzt von beiden.
UND EINEN ZWEITEN: system_lesen lief per asyncio.get_event_loop() in einem
Worker-Thread — dort ist das NICHT die laufende Schleife. Die Coroutine
waere nie gelaufen. Die Schleife wird jetzt im Startup festgehalten.
GEMESSEN: ruff sauber, 378 Tests + 3 uebersprungen, `npm run build`
durch (1650 Module). Der Live-Beweis steht noch aus — er kommt mit dem
Deploy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e165e2a0d3 |
feat: Komplettierung der aktuellen Rippy-Etappe
Ampel / ampel (push) Failing after 29s
- Versionsanzeige für den Windows-Worker im UI inkl. Prüfung - Absicherung der install.sh gegen fehlendes systemd - Serien-Episoden-Erkennung und Laufzeitabgleich anhand TMDB-Daten (Heuristik) - Umstellung der Windows-Worker-Installation auf SchTasks (Dienst-Ersatz) - Kodi-Bibliotheks-Refresh über JSON-RPC integriert |
||
|
|
bfb13f44a5 |
fix(ui+api): das "Neuladen" war HTTP 429 - und "Neu" komprimierte immer
Ampel / ampel (push) Successful in 30s
Zwei Commander-Befunde, beide mit derselben Wurzel: Rippy hat sich selbst
ausgebremst und dann geschwiegen.
## "Wenn der Worker installiert ist, wird dieser Bereich oft neu geladen"
Gemessen statt geraten. Die Antworten von /jobs und /capabilities waren ueber
zwanzig Sekunden byteweise identisch, alle Endpunkte antworteten unter 30 ms -
es wurde also gar nichts neu geladen. Im nginx-Log standen dagegen 97 Antworten
mit HTTP 429.
Drei Fehler griffen ineinander:
1. Das Limit war zu klein fuer Rippy selbst: 100 Anfragen/min, waehrend ein
offener Tab 111/min verursacht (Dashboard 75 + Log-Kasten 24 + Laufwerke 12)
und der Windows-Tray weitere 12/min dazulegt.
2. Der nginx gab die Client-Adresse nicht weiter. Fuer die API kam damit ALLES
von 172.19.0.6 - Browser, zweiter Tab und Tray teilten sich einen Eimer
(812 von 876 Anfragen). Das erklaert die Kopplung an den Worker: tray.py
fragt /api/jobs ueber Port 80, also durch denselben Proxy.
3. Ein abgewiesener Abruf leerte das UI. `catch(() => [])` heisst "es gibt
keine Jobs" - richtig waere "ich weiss gerade nichts Neues". Fuer einen Takt
stand "Keine Jobs", die Zaehler sprangen auf (0), vier Sekunden spaeter war
alles zurueck.
Behoben: X-Real-IP im nginx, Grenze auf 600/min mit vorgerechneter Herleitung,
jeder Fehlschlag laesst den alten Stand stehen (null statt []), axios bekommt
eine Zeitgrenze, und das Dashboard trennt schnelle Daten (Jobs/Laufwerke, 4 s)
von langsamen (Hardware/Worker/Ablagen, 12 s) - 75/min werden zu 30/min.
Ein greifendes Limit steht ab jetzt im Log, gedrosselt auf eine Meldung pro
Client und Minute.
## "Hier gibt es den Button 'neu' aber WAS wird dann gemacht?"
Immer die Komprimierung - auch bei einem Job, dessen RIP abgebrochen war. Am
26.07.2026 waeren aus 5,1 GB Bruchstueck (von rund 40 GB) brav ein Film
geworden, der bei 12 % aufhoert.
Die Phase war nach `status = "failed"` nicht mehr feststellbar, also wird sie
jetzt vermerkt (rip_fertig in den Job-Metadaten: false beim Rip-Start, true bei
der Uebergabe an die Kompression). Daraus folgt die Beschriftung: "Neu
komprimieren", "Neu rippen" - oder bei Bestandsjobs ohne Vermerk ein Dialog,
der beide Wege erklaert und die Groesse der Rohdaten als Entscheidungshilfe
nennt. Geraten wird nicht. Fuer den Rip-Fall gibt es POST
/jobs/{id}/retry-rip: neuer Job mit neuer ID (sonst laege das Bruchstueck im
Roh-Verzeichnis des neuen Rips), Titel/Ablage/Sprachwahl uebernommen, mit
ehrlicher Absage wenn keine Disc im Laufwerk liegt.
10 neue Tests (289 gruen), darunter eine Kopplungspruefung: der Name der
Phasen-Marke muss in worker/tasks.py und api/phasen.py zusammenpassen - genau
diese Sorte Auseinanderdriften hat die Zombie-Erkennung ein Release lang blind
gemacht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
19f3dc5330 |
fix(zombies): "running" fehlte - ein abgestuerzter RIP wurde NIE gefunden
Ampel / ampel (push) Successful in 30s
Der Commander: "Ausserdem ist gerade mitten im Rip das Laufwerk ausgegangen...
glaube ich zumindest." Gemessen war es etwas anderes, und der Fund ist groesser
als der Vorfall.
WAS MESSBAR WAR:
Job 2182d525 status=running progress=12 (Rip gestartet 14:00:24)
makemkvcon laeuft NICHT (per /proc geprueft, PID gegengeprueft)
Rohdatei 5.167.382.528 Bytes, waechst in 10 s nicht
Laufwerk Status 4 (Disc drin), /dev/sr0 + /dev/sg1 da
Worker-Start 14:07:02 <- mein `docker compose up -d --build`
Das Laufwerk ist also NICHT ausgegangen. Der Rip wurde von MEINEM Deploy
getoetet: `up -d --build` baut den worker-Container neu, und der laufende Rip
stirbt mit ihm. Genau davor warnt der SAVEPOINT seit v3.18 - die Warnung half
nichts, weil sie niemand liest und nichts sie prueft.
DER EIGENTLICHE FUND: Die Zombie-Erkennung lief um 14:09:04 und meldete
`{'geprueft': 0, 'aufgeraeumt': []}` - obwohl der tote Job direkt vor ihr lag.
Ursache:
ARBEITS_STATI = ("ripping", "transcoding", "canceling") # zombies.py
db.update_job(job_id, status="running", ...) # tasks.py - der Rip
Der Rip setzt "running", gesucht wurde "ripping". Dieser Wert steht
ausschliesslich in Celerys Task-META und NIE in einer Job-Zeile (nachgeprueft:
kein einziger Schreiber im ganzen Baum). Die Zombie-Erkennung aus v3.14 wurde
gebaut, um genau einen abgestuerzten Rip zu finden - und hat ihn nie gesehen.
Besonders tueckisch: `geprueft: 0` sah bei jedem Worker-Start wie "nachgesehen,
alles gesund" aus, waehrend sie nach einem Status suchte, den es nicht gibt.
Deshalb blieb der Job auf "processing 12 %" stehen - mit einer Restzeit-Schaetzung
von 1 h 30 min obendrauf, die es fuer einen toten Prozess nicht geben duerfte.
GEBAUT:
* "running" in ARBEITS_STATI.
* Ein Test, der das Auseinanderlaufen MECHANISCH verhindert: Er liest tasks.py,
sammelt jeden Status, den der Worker per db.update_job in eine Job-Zeile
schreibt, und verlangt, dass jeder davon entweder ein Arbeitsstatus oder ein
Endzustand ist. Ein Kommentar haette das nicht verhindert.
* GET /health/arbeit + eine Sperre in deploy.sh: Laeuft ein Job, bricht der
Deploy ab (uebersteuerbar mit RIPPY_TROTZDEM=1 - dann ist es eine
Entscheidung und kein Versehen). Ein Satz Code gegen eine verlorene Stunde.
* Server-Status zeigt jetzt die eingehaengten FREIGABEN mit freiem Platz, nicht
nur die Container-Platte (Commander-Wunsch). Genau dort liegen die Rohdaten,
und bei externem Encoden muessen sie dort liegen - wer wissen wollte, ob noch
Platz fuer eine Disc ist, sah die falsche Zahl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
a448ebc4de |
fix(ui): die Pfad-Warnung sagt jetzt, was zu tun ist - und bietet einen Knopf
Ampel / ampel (push) Successful in 29s
Commander: "Das ist ja quatsch. Mein PC hat das Ziel als Worker direkt auf dem
PC eingebunden."
Nachgemessen: Die Warnung hatte SACHLICH recht, war aber unbrauchbar. Sein PC hat
die Freigabe wirklich als Netzlaufwerk (X:) gemountet - nur lag das
ARBEITSVERZEICHNIS darauf und die ABLAGE nicht. Das Ziel war weiter
/app/media/movies, also die VM-Platte, und dorthin kommt sein PC nicht. Beleg aus
der Worker-Meldung:
pfad_map = /app/media/rippy=\192.168.178.62\rippy
Ziel = /app/media/movies <- nicht abgedeckt, richtig gewarnt
Die alte Meldung nannte den Container-Pfad und die Mapping-Zeichenkette - also
genau die zwei Dinge, die ein Nicht-Entwickler nicht deuten kann. Jetzt steht der
Handgriff drin, mit dem NAMEN der Freigabe, die dieser Worker wirklich erreicht
("Einstellungen -> Ripping -> Ablage auf 'rippy' stellen").
Dazu ein Knopf, der das Ziel NUR FUER DIESEN RIP auf die Freigabe legt
(/app/media/movies -> /app/media/rippy/movies), ohne die globale Ablage
anzufassen. Wer den Rip jetzt starten will, will jetzt eine Loesung und keine
Wegbeschreibung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
848deb1c24 |
fix(ui): Sprachnamen auf Deutsch - "Undetermined" versteht niemand
Ampel / ampel (push) Successful in 29s
An der echten Akira-Blu-ray gemessen, nachdem die Sprachauswahl live lief:
Ton: deu 4x, jpn 2x, und 4x
Untertitel: deu 4x, und 8x
MakeMKV liefert die Namen englisch ("German", "Japanese") und fuer Spuren ohne
Sprach-Kennzeichnung "Undetermined" - das sind typischerweise Audiokommentare.
Der Commander liest alles auf Deutsch (AGENTS Grundregel 4), und
"Undetermined" ist fuer einen Nicht-Entwickler schlicht keine Auskunft. Jetzt
steht dort "Ohne Sprachangabe", und die zwei Dutzend haeufigsten Codes haben
deutsche Namen. Unbekannte Codes behalten MakeMKVs Namen - geraten wird nichts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
9230d0dd7a |
fix(weitergabe): drei Luecken geschlossen, die einen Fremden gestolpert haetten
Ampel / ampel (push) Successful in 29s
Weitergabe nicht durch Lesen geprueft, sondern indem ich mich wie ein fremder Rechner verhalten habe: frischer `git clone` von Gitea in ein leeres Verzeichnis, dann `./install.sh --nur-pruefen`. Ergebnis: 2,6 MB, alles gruen, Laufwerk samt richtigem sg-Knoten ueber die SCSI-Adresse erkannt. Der Weg selbst traegt. Drei Luecken sind dabei aufgefallen: 1. IN BEISPIELBEFEHLEN STAND MEINE IP. `install.ps1` und `remote-transcode-worker.yml` nannten im Aufruf-Beispiel 192.168.178.162 - also genau die Zeile, die ein Fremder kopiert. Jetzt Platzhalter, beim Windows-Installer mit dem Hinweis, wo die richtige IP steht (und dass es NICHT die des eigenen PCs ist - der haeufigste Irrtum). 2. DER ASSISTENT FRAGTE DEN MAKEMKV-BETA-KEY NICHT. Er stand in der README, in der .env und in den Einstellungen - nur nicht dort, wo man beim Einrichten hinsieht. Ein Fremder installiert also, legt eine Blu-ray ein und bekommt spaeter einen Fehlschlag, ohne dass ihn jemand darauf hingewiesen haette. Das ist die wahrscheinlichste Stolperstelle einer frischen Installation. Jetzt fragt der Assistent ihn ab, mit dem Unterschied im Klartext: DVDs gehen ohne, Blu-ray braucht ihn - und er ist NICHT der Disc-Schluessel einer 4K-Disc. 3. Eine Docstring nannte meine NAS-IP als Beispiel - jetzt neutral (//NAS/rippy). Tests und Log-Beispiele behalten die echten Namen: Sie dokumentieren Messungen, und genau das ist ihr Wert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
148ac494c2 |
feat(sprachen): Rippy fragt vor dem Rip, welche Sprachen du willst
Ampel / ampel (push) Successful in 29s
Commander-Anforderung: "Die Disc hat Material in X Sprachen und X Untertiteln -
Rippy muss VOR dem Rip fragen: Was genau willst du haben? In der Automatik muss
das ebenfalls einstellbar sein."
Die Auskunft lag laengst vor und wurde weggeworfen: Derselbe Titel-Scan, der die
Titel-Tabelle fuellt, liefert in derselben makemkvcon-Ausgabe die Streams mit.
Ein zweiter Info-Lauf haette eine Minute Wartezeit gekostet - jetzt kommt beides
aus einem Aufruf.
Format an der Akira-Blu-ray im Laufwerk gemessen (AGENTS Regel D):
SINFO:<titel>,<stream>,<attribut>,<code>,"<wert>"
1 = Typ ("Audio"/"Subtitles"), 3 = Sprachcode, 4 = Sprachname,
6 = Codec, 14 = Kanaele, 30 = Beschreibung
DIE FALLE dabei: Die Sprache steht in 3/4, NICHT in 28/29. Die tragen auf JEDEM
Stream "eng"/"English" - auch auf einer deutschen Tonspur und auf dem
Videostream; das ist MakeMKVs eigene Anzeigesprache. Wer 28 nimmt, haelt jede
Disc fuer englisch. Ein Test haelt das fest.
Angewendet wird die Wahl bei der KOMPRESSION, nicht beim Rippen - drei Gruende:
der Rip bleibt vollstaendig und verlustfrei (Muss-Feature laut KONZEPT); HandBrake
hat dafuer dokumentierte Schalter (--audio-lang-list / --subtitle-lang-list, die
genau die ISO-639-2-Codes nehmen, die MakeMKV liefert - beides gegengeprueft);
und wer spaeter andere Sprachen will, komprimiert neu statt die Disc wieder
einzulegen. Genau das sagt der Dialog auch, sonst glaubt man, es werde
unvollstaendig gerippt.
Nichts angeklickt heisst "alles behalten" - das Verhalten von vorher. Auch die
Einstellung ist bewusst LEER vorbelegt: Ein stilles "deu" wuerde bei einem
japanischen Original die Originaltonspur wegwerfen, ohne dass jemand gefragt hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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
|
||
|
|
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
|
||
|
|
60abf42359 |
feat(ui): Laufwerke & im Laufwerk erkannte Disc nach ganz oben verschoben, Ripping Queue exakt nach SC1 aufgebaut
Ampel / ampel (push) Successful in 28s
|
||
|
|
e18315814c |
feat(ui): 5-Kachel-Banner ohne schwarze Loecher gefuellt, Vollstaendige Job-Tabelle mit TMDB-Beschreibungen wiederhergestellt
Ampel / ampel (push) Successful in 28s
|