878b24c43b421623dfffa4827e1b22033014d702
283
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
878b24c43b |
fix: die Metadaten-Suche war seit V2-1 tot — auf Windows UND auf der VM
Ampel / ampel (push) Successful in 54s
Der Commander: "TMDB und OMDB Key sind hinterlegt, das laufwerk wird aber
auch nicht korrekt ausgelesen. Eigentlich sollte er direkt bei TMDB oder OMDB
oder JIKAN anfragen nach metadaten."
Er hat das Symptom gemeldet. Darunter lagen VIER Fehler.
## 1. Ein Phantom-Modul (der schwerste)
clients/tmdb.py:27 from db import get_settings
clients/omdb.py:36 from db import get_settings
clients/thetvdb.py:16 from db import get_settings
`docker/api/db.py` gibt es seit der Zusammenlegung in V2-1 (
|
||
|
|
ea8a043643 |
feat(setup): Fortschrittsbalken, MakeMKV-Installer mit Rechteabfrage, Updates
Ampel / ampel (push) Successful in 55s
Drei Punkte des Commanders vom 28.08.2026.
## 1. "bei der installation eine progress bar waere gut"
Die Daten dafuer waren schon da -- der Fortschritts-Rueckruf traegt seit
jeher einen Anteil, die Bruecke warf ihn nur weg.
Jetzt rechnet `Fortschritt` Abschnitt + Teil-Anteil in einen Gesamtstand um.
Die Gewichte sind gemessen: Dateien ablegen 0-8 % (rund drei Sekunden),
Ablage 8-10 %, Werkzeuge 10-97 % (sechs Sekunden bis mehrere Minuten). Ein
Balken, der bei 90 % minutenlang steht, ist schlimmer als gar keiner.
Zwei Regeln, beide mit Test:
* Er laeuft NIE zurueck. Der Werkzeug-Abschnitt kann mehrere Downloads
enthalten, jeder mit eigenem 0-bis-1 -- ohne die Regel spraenge der Balken
bei jedem neuen Werkzeug an den Abschnittsanfang. Zusaetzlich rechnet
`sicherstellen` den Anteil eines Werkzeugs auf die ganze Strecke um.
* Eine Textmeldung ohne Zahl bewegt ihn nicht. Sie ist keine Aussage ueber
den Fortschritt.
Am laufenden Fenster nachgewiesen: "Werkzeuge werden geprueft -- 36 %",
Balken bernsteinfarben, am Ende gruen bei 100 %.
## 2. "make MKV laesst sich nicht installieren weil der vorgang erhoehte
## rechte benoetigt"
Der Kern, und der Fehler lag bei uns: `subprocess.Popen` benutzt
`CreateProcess`, und das ZEIGT KEINE UAC-Abfrage -- es bricht mit
ERROR_ELEVATION_REQUIRED (740) ab. MakeMKV installiert nach Program Files und
fordert im Manifest Administratorrechte an.
`ShellExecuteW` ist der dokumentierte Weg, der die Abfrage ausloest. Damit
braucht NUR dieser eine Aufruf die Erhoehung.
Und deshalb laeuft das Setup NICHT dauerhaft erhoeht (dazu die Antwort im
Chat): Ein erhoehter Prozess sieht die eingebundenen Netzlaufwerke nicht --
auf diesem Rechner gemessen, X:, Y:, Z: ohne Erhoehung sichtbar,
EnableLinkedConnections nicht gesetzt. Genau das hatte der Commander vorher
selbst als Fehler gemeldet. Wer trotzdem erhoeht starten will, bekommt einen
Knopf -- aber nur, wenn das Schreibrecht am gewaehlten Ziel wirklich fehlt.
## 3. "wenn MakeMKV oder Handbrake schon installiert sind ein update
## verfuegbar ist. Das kann ja direkt im setup abgehandelt werden."
`veraltete()` vergleicht nach ZAHLEN (ein Zeichenvergleich hielte 1.9.2 fuer
neuer als 1.11.2 -- und HandBrake ist genau dort). `sicherstellen` holt sie
auf Wunsch mit, aber IMMER nach den fehlenden: Ein Update ist Komfort, ein
fehlendes Pflichtwerkzeug ist ein Hindernis.
Der Assistent zeigt es als eigenen Schalter mit Klartext ("MakeMKV 1.18.2 ->
1.18.4"). Der Update-Check laeuft als GETRENNTER Aufruf nach der
Voraussetzungs-Pruefung: Er kostet Netz, und makemkv.com braucht dafuer im
schlimmsten Fall gut zwanzig Sekunden -- in der Pruefung bliebe das Fenster
so lange leer. Sind die Quellen nicht erreichbar, schaltet sich der Haken ab
und sagt warum; ein Haken, der nichts tun kann, ist eine Falle.
Ampel lokal: 758 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
95beb91364 |
feat(tools): Ausweichquellen fuer MakeMKV — recherchiert und gemessen
Ampel / ampel (push) Successful in 1m1s
Commander: "bitte baue fuer MakeMKV fallback seiten ein."
Anlass war ein echter Ausfall. Am 28.08.2026 gemessen:
https://www.makemkv.com/download/ HTTP 525 (dauerhaft)
https://makemkv.com/download/ HTTP 525
http://www.makemkv.com/download/ HTTP 403
https://forum.makemkv.com/ 200 / 522 / Zeitablauf (wechselnd)
web.archive.org/web/2025id_/...exe 200, 16.432.607 Bytes
525/522 heissen: Cloudflare erreicht den Ursprungsserver nicht. Eine Stoerung
beim Hersteller -- dieselbe, die im Projekt schon einmal jeden Worker-Build
lahmgelegt hat.
## Die Kette
Version: Hersteller-Seite (massgeblich) -> Forum-Ankuendigungen -> Archiv
Datei: eigene Quelle -> Hersteller -> Internet Archive
Der Hersteller zuerst, immer. Das Archiv ist Rueckfallebene, und Rippy nennt
hinterher die Quelle, aus der die Datei kam.
Zwei Feinheiten, beide aus Messungen statt aus dem Kopf:
* Die HOECHSTE Nummer gewinnt, nicht die erste. Antwortet das Forum nicht,
meldet das Archiv einen aelteren Stand (1.18.2 statt 1.18.4) -- "neueste
Fassung" waere dann eine falsche Aussage.
* Ein zweiter Anlauf, mit knapper Zeitgrenze. Drei Abrufe am Forum:
TimeoutError, HTTP 522, dann Erfolg. Ohne Wiederholung fiel die Quelle in
zwei von drei Faellen aus; mit 20-Sekunden-Grenzen dauerte der schlimmste
Fall 46 Sekunden, in denen das Setup eingefroren aussah. Jetzt acht
Sekunden je Versuch, schlimmstenfalls gut zwanzig.
## Warum eine fremde Quelle trotzdem sicher ist
Der naheliegende Schutz geht NICHT: MakeMKV signiert seinen Installer nicht
(Get-AuthenticodeSignature -> NotSigned, an der echten Datei gemessen). Eine
Signaturpruefung waere eine, die immer fehlschlaegt -- schlimmer als keine,
weil sie Sicherheit vortaeuscht.
Geprueft wird die Versions-Ressource, ebenfalls gemessen:
CompanyName GuinpinSoft inc
FileDescription MakeMKV installer
FileVersion v1.18.4
Das ersetzt keine Signatur, faengt aber ab, was hier wirklich droht: eine
Fehlerseite mit .exe-Namen, ein abgebrochener Download, eine falsche Fassung.
Ende zu Ende nachgewiesen, ohne etwas zu installieren:
Version laut Kette: 1.18.4
Hersteller: HTTP 525 -> uebersprungen
Internet Archive: 15,7 MB in 6,1s
Pruefung: BESTANDEN (MakeMKV v1.18.4)
Die Grenze aus KONZEPT.md 6 bleibt: Rippy liefert MakeMKV weiterhin NICHT
mit. Es holt die Datei des Herstellers -- im Rueckfall aus einem Archiv, das
genau diese Datei aufbewahrt.
Ampel lokal: 733 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
480b658294 |
fix: die Laufwerks-Pruefung muss selbst einspritzbar sein
Ampel / ampel (push) Successful in 54s
Ampel-Lauf zu
|
||
|
|
16df61ea3a |
fix: pruefe_schreibrecht nahm os.path statt pfade — zum vierten Mal dieselbe Falle
Ampel / ampel (push) Failing after 54s
Ampel-Lauf zu
|
||
|
|
3d5f82b786 |
fix(windows): der Deinstallierer beendet jetzt auch den laufenden Dienst
Ampel / ampel (push) Failing after 54s
Beim Deinstallieren im laufenden Betrieb blieben liegen:
rippy.db, rippy.db-shm, rippy.db-wal
(Der Prozess kann nicht auf die Datei zugreifen, da sie von einem
anderen Prozess verwendet wird)
Der Dienst lief weiter und hielt die Datenbank offen. Damit waere der Fehler
von vorhin durch eine andere Tuer zurueckgekommen: Die naechste Installation
haette wieder den alten Zustand samt setup.done geerbt, und der
Ersteinrichtungs-Assistent waere wieder verschwunden.
Aufgefallen ist es nur, weil der Deinstallierer seit heute NENNT, was er
nicht wegbekommen hat. Vorher stand dort ein `except OSError: pass` und die
Meldung "Rippy wurde entfernt".
Gefiltert wird ueber den Installationsordner, nicht ueber den Namen:
`Rippy.exe` heisst auch die Datei, die den Auftrag gerade ausfuehrt. Die
eigene Prozesskennung und die des PyInstaller-Starters sind ausgenommen.
Nachgewiesen am laufenden Prozess: beendet, Datenbankdateien danach
loeschbar. In der Entwicklungsumgebung schlaegt der Pfadvergleich uebrigens
immer fehl -- die Werkzeuge laufen in einem App-Container, der
%LOCALAPPDATA% umleitet, waehrend die Registry den echten Pfad meldet. Das
steht jetzt als Hinweis im Docstring, damit es niemand ein zweites Mal sucht.
Ampel lokal: 713 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>
|
||
|
|
082e8b8b55 |
fix(windows): der Knopf "Rippy oeffnen" oeffnete Rippy nicht
Ampel / ampel (push) Failing after 55s
Nach einer erfolgreichen Einrichtung schloss er nur das Fenster, und danach
passierte nichts mehr: Der Dienst lief nicht, kein Fenster ging auf. Der
Commander hat Rippy anschliessend selbst gestartet ("das tool selbst laesst
sich starten") -- was den Fehler verdeckt hat.
Ein Knopf, der etwas anderes tut als seine Beschriftung sagt, ist schlimmer
als keiner. Jetzt startet main() nach einem geglueckten Assistenten das
installierte Programm.
Dazu der zweite Teil des Netzlaufwerk-Befunds: Auch Netz-ANMELDUNGEN haengen
am Token der Sitzung, nicht nur die Laufwerksbuchstaben. Ein erhoehter Prozess
kennt die Zugangsdaten zur Freigabe nicht und bekommt "Zugriff verweigert" --
also dieselbe Meldung wie bei einem echten Rechteproblem. Ein UNC-Pfad mit
Administratorrechten wird jetzt als solcher erkannt und erklaert.
Ampel lokal: 705 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>
|
||
|
|
8eca982e68 |
docs: SAVEPOINT v4.0-rc2 — Windows ist ein eigenes Produkt
Ampel / ampel (push) Successful in 55s
Die drei Befunde des Commanders und was dahinter steckte, plus der teuerste Fund des Tages: %ProgramFiles(x86)% wurde auf einer echten Maschine NIE aufgeloest, weil der Test genau die Schreibweise einspritzte, die im Muster stand. Er war gruener als die Wirklichkeit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
151376aee3 |
fix: Pfad-Regeln an EINE Stelle — dreimal dieselbe Falle war zweimal zu viel
Ampel / ampel (push) Successful in 54s
Ampel-Lauf zu
|
||
|
|
b47d21510d |
docs: Entscheid 6 — Faehigkeiten statt Modus-Name, und ein Setup das fragt
Ampel / ampel (push) Failing after 54s
Beide Befunde des Commanders vom 28.08.2026 festgehalten, mitsamt der Begruendung, warum ein 'if (windows)' an dreissig Stellen die falsche Reparatur gewesen waere: Die Oberflaeche haette weiterhin nichts ueber ihren Betrieb gewusst, nur eine zweite Sorte Vermutung gehabt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a4a045f303 |
feat(windows): ein echtes Setup — Voraussetzungs-Pruefung, Zielordner, Ablage
Commander: "Dann haette ich beim 'Setup' auch erwartet das nen echtes setup
passiert - wo will ich das hinspeichern, nen pre requirement check usw. Da
kommt garnichts Rippy geht einfach auf."
Er hatte recht. Ein Doppelklick installierte stillschweigend nach
%LOCALAPPDATA%\Rippy und oeffnete das Fenster. Der Nutzer wurde nichts
gefragt und erfuhr nichts -- auch nicht, ob sein Rechner ueberhaupt kann, was
Rippy braucht.
Jetzt: ein Assistent mit acht Pruefungen, zwei Ordner-Wahlen und drei
Schaltern. Auf diesem Rechner nachgemessen:
OK Betriebssystem Windows 11 (Build 26200)
OK Fenster (WebView2) 151.0.4129.107
OK Optisches Laufwerk \.\G:
OK HandBrakeCLI 1.11.2
! MakeMKV nicht gefunden
-> wird von makemkv.com geholt
OK Speicherplatz 59.1 GB frei
OK Schreibrecht C:\Users\...\AppData\Local\Rippy
OK Netzwerk-Port 7788 ist frei
Drei Entwurfsentscheidungen, die dazugehoeren:
* Nur ein FEHLER blockiert, eine Warnung nicht. Eine Warnung, die den Knopf
sperrt, ist eine Bevormundung; ein Fehler, der nur warnt, ist eine Falle.
Kein Laufwerk = Warnung (reine Komprimier-Maschine ist ein vorgesehener
Betriebsfall). Kein Schreibrecht = Fehler.
* Jeder Befund sagt, was zu tun ist. Ein Befund ohne Abhilfe laesst den
Nutzer ratlos zurueck -- ein Test haelt das fuer jede Pruefung fest.
* Die Pruefungen stehen in einrichtung.py, komplett ohne Fenster pruefbar.
Im Fenstermodul steht nur Aufbau und Bruecke.
WebView2 statt Win32-Dialog: Es ist ohnehin da (Entscheid 4), der Assistent
kostet damit NULL zusaetzliche Bytes. Die Seite laedt nichts aus dem Netz --
sie muss auf einem Rechner ohne Internet aufgehen, dort wird sie am
dringendsten gebraucht. Ein Test haelt das fest.
--still und --ohne-assistent gehen weiterhin den stummen Weg. Geht das
Fenster nicht auf, wird mit den Vorgaben installiert und gesagt warum -- ein
Setup, das gar nichts tut, weil sein Fenster nicht aufging, waere schlimmer
als eines, das nicht fragt.
fix(tools): %ProgramFiles(x86)% wurde auf echten Rechnern NIE aufgeloest
Beim Bauen der Pruefung aufgefallen, und es ist ein Lehrstueck:
Testumgebung {"ProgramFiles(x86)": ...} -> C:\Program Files (x86)\...
echtes Windows {"PROGRAMFILES(X86)": ...} -> ''
Windows behandelt Umgebungsvariablen ohne Ruecksicht auf die Schreibweise und
legt sie in os.environ GROSS ab. Der Vergleich in _entfalten war exakt, fand
nie eine Uebereinstimmung, und die bekannten Installationsorte fielen aus der
Kandidatenliste. MakeMKV wurde also NUR ueber die Registry-Rueckfallebene
gefunden -- wo die fehlt oder anders aussieht, fand Rippy nichts.
Aufgefallen ist es nur, weil hier zum ersten Mal eine ECHTE Umgebung benutzt
wurde. Der Test spritzte genau die Schreibweise ein, die im Muster stand: Er
war gruener als die Wirklichkeit. Beide Tests nehmen jetzt die echte
Schreibweise, und einer prueft direkt gegen os.environ. Beim Zurueckdrehen
des Fehlers rot gesehen (5 Fehlschlaege).
Ampel lokal: 691 gruen, ruff sauber. Assistent im Bildschirmfoto belegt.
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>
|
||
|
|
a5e64f8e56 |
fix(windows): der Deinstallierer liess 148 Dateien liegen und meldete Erfolg
Ampel / ampel (push) Successful in 54s
Beim ersten echten Deinstallieren auf dem Rechner des Commanders gemessen:
Uninstall-Eintrag weg
Autostart weg
Verknuepfungen weg
Dateien 149 NOCH DA (davon 148 in fenster\EBWebView\)
Gemeldet wurde: "Rippy wurde entfernt."
Zwei Ursachen, beide behoben:
## 1. WebView2 ueberlebt Rippy
WebView2 startet eigene Prozesse (msedgewebview2.exe: Renderer, GPU, Netz,
Crashpad). Stirbt Rippy, laufen sie als Waisen weiter und halten den
Zwischenspeicher offen -- acht davon liefen noch, als ich nachsah. shutil.rmtree
scheiterte an jeder gesperrten Datei.
fenster.helfer_beenden() beendet sie jetzt VOR dem Loeschen. Gefiltert wird
ueber den --user-data-dir in der Befehlszeile: msedgewebview2.exe benutzen
auch andere Programme, und die alle zu beenden waere ein Uebergriff -- der
Nutzer verloere die Fenster fremder Anwendungen. Ein Test haelt fest, dass
erst gefiltert und dann beendet wird.
## 2. Der Fehler wurde verschluckt
`except OSError: pass` in der Loeschschleife. Ein "Rippy wurde entfernt" ueber
einem halb geleerten Ordner ist eine Falschaussage -- genau die Sorte stiller
Fehlschlag, vor der AGENTS.md warnt. Jetzt wird gesammelt, was nicht ging, und
mit dem Grund genannt.
Dazu im Aufraeum-Skript: `rmdir /s /q` statt `rmdir`. Ein blosses rmdir
scheitert an JEDER verbliebenen Datei und laesst den ganzen Ordner stehen --
selbst wenn spaeter nur noch eine Sperrdatei uebrig ist.
Ampel lokal: 612 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ce4e7e6b8a |
feat(windows): das Setup richtet MakeMKV und HandBrake wirklich ein
Ampel / ampel (push) Successful in 54s
Commander: "handbrake und makemkv MUESSEN mitgeliefert werden oder waehrend
des Setups separat installiert werden! Ohne das ist das tool NICHT
einsatzfaehig."
Er hat recht, und die Luecke war real: katalog.py konnte die Werkzeuge
finden, beschaffen.py konnte sie holen -- das Setup rief beides nie auf. Wer
Rippy auf einem frischen Rechner installierte, bekam eine Oberflaeche, die
ihm sagte was fehlt, und keinen Weg, es zu aendern.
Die beiden gehen unterschiedliche Wege, und der Grund ist die LIZENZ:
* HandBrakeCLI ist GPL-2 -> mitgeliefert. Weitergabe ausdruecklich erlaubt,
solange Lizenztext und Quellverweis dabei sind (LIZENZ-HandBrake.txt liegt
daneben). Damit komprimiert Rippy auch ohne Internet.
* MakeMKV ist proprietaer -> beim Einrichten vom Hersteller geholt und mit
DESSEN Installer installiert. Weitergabe durch Dritte ist nicht erlaubt.
Das ist dieselbe Black-Box-Trennung wie in KONZEPT.md 6 und deckt sich mit
KONZEPT-V2.md 4.1 ("MakeMKV wird NICHT mitgeliefert").
Drei Regeln, die dazugehoeren:
1. Ein Setup meldet NIE Erfolg, waehrend ein Pflichtwerkzeug fehlt.
sicherstellen() liest den Bestand VOR und NACH dem Versuch und gibt
zurueck, was danach wirklich da ist -- nicht, dass es versucht wurde.
2. Ein nicht erreichbarer Download bricht die Installation nicht ab. Am
28.08.2026 antwortete makemkv.com mit HTTP 525. Rippy ist dann trotzdem
installiert und sagt im Klartext, was fehlt und wie man es beschafft.
3. Ohne HandBrake ist Rippy einsatzbereit (Rip laeuft, nur ohne Kompression),
ohne MakeMKV nicht. Nur Pflichtwerkzeuge entscheiden ueber "bereit".
Nachgemessen am fertigen Setup: HandBrake geloescht, installiert, nach DREI
Sekunden wieder da (74 MB, also aus dem Paket und nicht geladen), Meldung
"Rippy ist einsatzbereit. HandBrakeCLI 1.11.2 / MakeMKV 1.18.4".
RippySetup.exe waechst von 32,0 auf 56,4 MB.
fix(tools): der Fortschritts-Rueckruf bekommt immer einen Text
_datei_laden schickte `None` als Text, wenn sich nur der Fortschritt geaendert
hatte. Der erste echte Aufrufer starb daran:
can only concatenate str (not "NoneType") to str
Ein Rueckruf, den man nur mit einer nirgends dokumentierten Sonderbehandlung
benutzen kann, ist eine Falle. Jetzt kommt immer ein Satz, gedrosselt auf
jedes zehnte Prozent -- 24 MB in 256-KB-Haeppchen waeren sonst hundert Zeilen
im Protokoll. Zwei Tests halten beides fest.
Nicht im Repo: Die 70,9 MB HandBrakeCLI holt der BAU nach
dist/windows/vendor/ (nicht versioniert) -- dasselbe Muster wie der
vendor/-Ordner fuer MakeMKV auf der VM.
Ampel lokal: 607 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
60c0f93237 |
fix(windows): leere Symbole und der verschwundene Eintrag in "Installierte Apps"
Ampel / ampel (push) Successful in 54s
Beides vom Commander gemeldet, beides gemessen statt vermutet.
## 1. Weisses leeres Blatt statt Symbol
In deploy/worker-windows/rippy.ico steckte GENAU EIN Bild:
Bilder = 1
256x256 32 bit 5657 Bytes PNG
Windows holt sich fuer jede Stelle die passende Groesse -- 16x16 fuer die
Taskleiste, 32/48 fuer Desktop und Startmenue. Fehlt sie, skaliert die Shell
nicht zuverlaessig selbst, sondern zeigt das leere Blatt. Bei einer Datei mit
nur einem PNG-komprimierten 256er passiert das regelmaessig.
packaging/windows/icon.py baut die Datei jetzt mit neun Groessen
(16/20/24/32/40/48/64/128/256, LANCZOS beim Verkleinern). Nachgemessen an
der neuen EXE: 16x16 mit 138 Farben, 32x32 mit 264 -- kein leeres Blatt mehr.
build.py laesst ein unvollstaendiges Symbol gar nicht mehr durch, und
test_icon.py haelt die Pflichtgroessen fest (mit dem alten Stand rot gesehen).
## 2. Rippy stand nicht in "Installierte Apps"
Die Ursache war MEINE Testsuite. Gemessen:
Rippy.exe --nicht-starten -> Eintrag: DA (Rippy 2.0.0)
pytest -q -> Eintrag: WEG
test_installation_legt_die_dateien_an_... schrieb in den ECHTEN
Uninstall-Schluessel und loeschte ihn im finally wieder -- also auch den des
Commanders. Der Autostart-Eintrag ging denselben Weg.
Der Schluesselname stand als VORGABEWERT in jeder Signatur, und Vorgabewerte
wertet Python zur Definitionszeit aus: Ein Test konnte ihn gar nicht umbiegen.
Jetzt steht dort None, aufgeloest zur Laufzeit -- damit wirkt ein
monkeypatch.setattr(reg, "SCHLUESSEL", ...) ueberall.
Dazu ein Waechter (test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an):
Er merkt sich den echten Zustand vorher, laesst eine vollstaendige
Test-Installation samt Autostart laufen und prueft danach, dass sich am
echten Eintrag nichts geaendert hat.
Das ist derselbe Fehler wie vorhin bei den Verknuepfungen, nur an anderer
Stelle. Die Regel gehoert in den Code, nicht in meinen Kopf: **Ein Test
raeumt nur weg, was er selbst angelegt hat.**
Ampel lokal: 590 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e1984dc543 |
docs: SAVEPOINT — Ampel wieder gruen, und warum sie fuenf Laeufe rot war
Ampel / ampel (push) Successful in 54s
Nachgetragen: die 28 Linux-Fehler, der schwerste davon (der API-Container waere auf der VM gar nicht hochgekommen), und die Lehre daraus — ein Test, der nur auf einer Plattform greift, ist eine halbe Zusage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ca212ff835 |
fix(test): die detection-Attrappe muss auch das PAKET-Attribut ersetzen
Ampel / ampel (push) Successful in 54s
Ampel-Lauf 186: Die 28 echten Linux-Fehler waren weg, uebrig blieben fuenf
— alle in meinem neuen Test:
TypeError: drive_status() missing 1 required positional argument
`from rippy.drives import detection` sieht zuerst im PAKET nach
(`_handle_fromlist` prueft `hasattr`) und greift erst dann auf sys.modules
zurueck. Auf Linux ist das echte Modul dort laengst gesetzt, weil es
importierbar ist — die Attrappe blieb wirkungslos, und der Test lief in die
echten Funktionen.
Unter Windows fiel das nicht auf: Dort gibt es das Attribut mangels fcntl
gar nicht, also griff sys.modules. Derselbe Test, zwei Plattformen, zwei
Ergebnisse — genau die Sorte Luecke, die dieser Test eigentlich schliessen
soll.
Jetzt werden beide ersetzt. Gegengeprueft: mit kaputter Durchreiche fuenf
Fehlschlaege, danach wieder gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
bfc0cada62 |
fix(windows): kein CMD-Fenster mehr — die EXE ist ein Fenster-Programm
Ampel / ampel (push) Failing after 55s
Commander: "Unterbinde das CMD Fenster was sich beim oeffnen mitoeffnet."
Mein erster Anlauf wollte die Konsole VERSTECKEN und war falsch, aus zwei
Gruenden:
* Eine Onefile-EXE ist ZWEI Prozesse (gemessen: PID 33172 mit 8 MB, PID
33580 mit 103 MB, beide Rippy.exe). Die Pruefung "haengt genau EIN Prozess
an der Konsole" war damit nie wahr, das Fenster blieb einfach stehen.
* Selbst wenn sie ginge, waere das Fenster erst nach ein bis zwei Sekunden
weg. Ein Aufblitzen ist kein "unterbunden".
Jetzt wird die Konsole gar nicht erst erzeugt: --windowed statt --console.
Nachgemessen am fertigen Bau -- PE-Subsystem 2 (GUI) statt 3 (Konsole), und
beim Klick auf die Desktop-Verknuepfung ist genau EIN sichtbares Fenster da:
die WinForms-Oberflaeche. Kein ConsoleWindowClass-Fenster von Rippy, weder
sichtbar noch unsichtbar.
Was dazugehoert, damit daraus kein stiller Fehlschlag wird:
* Ohne Konsole ist sys.stdout None. uvicorn schreibt auf stderr und waere
mitten im Start gestorben, ohne dass irgendwo etwas stuende. einstieg.py
haengt sich deshalb an die Konsole des Aufrufers (--status in PowerShell
schreibt weiter dorthin) oder leitet nach %LOCALAPPDATA%\Rippy\rippy.log um.
* Der Installer meldet sein Ergebnis in einem Fenster, wenn keine Konsole da
ist -- sonst saehe ein gescheiterter Doppelklick aus wie einer, der nichts
tut. Bei --still und mit Konsole bleibt es bei der Textzeile.
Gegengeprueft am laufenden Programm: /api/health, / und /api/capabilities
antworten mit 200, und rippy.log faengt auf, was vorher ins Leere ging.
fix(drives): linux.py reicht die detection-Funktionen wieder durch
Die Ampel war seit Lauf 181 rot -- vier Commits lang, und ich hatte es
uebersehen. Ursache:
docker/api/main.py:54
AttributeError: module 'rippy.drives.linux' has no attribute 'drive_status'
main.py holt sich beim Import device_discovery.drive_status. Der
Windows-Treiber hat die Funktion; linux.py hatte sie nicht mehr, seit
detection (und damit fcntl) bewusst nicht mehr oben importiert wird -- sonst
waere main.py unter Windows nicht ladbar. Unter Windows lief also alles, auf
Linux waere der API-Container gar nicht hochgekommen.
Behoben mit PEP 562 (__getattr__), demselben Kniff wie in store/__init__.py:
Der Import bleibt ohne fcntl moeglich, wer die Funktionen BENUTZT bekommt sie.
Dazu drei weitere Fehler, die nur auf Linux auftraten:
* katalog.py baute Windows-Pfade mit os.path.join. Auf dem Linux-Runner wurde
daraus C:\Program Files (x86)\MakeMKV/makemkvcon64.exe. Jetzt entscheidet
der Pfad selbst ueber den Trenner, nicht die rechnende Maschine.
* verknuepfungen.py nahm os.path.dirname fuer den Arbeitsordner einer .lnk.
Eine .lnk zeigt IMMER auf einen Windows-Pfad -- also ntpath.
* waechter.py las 0.0 als Zeitpunkt statt als "noch nie geprueft". Auf einer
frisch gestarteten Maschine ist time.monotonic() klein, also fiel die erste
Laufwerks-Abfrage aus. None statt 0.0.
Alle vier haben jetzt Tests, die auf JEDER Plattform laufen -- der
Treiber-Test schiebt eine detection-Attrappe unter, der Waechter-Test stellt
eine Uhr bei 0,5 s nach, die Pfad-Tests pruefen beide Zweige. Beim
Wegnehmen der Durchreiche rot gesehen (5 Fehlschlaege), danach wieder gruen.
Damit faengt die Ampel so etwas kuenftig, ohne dass es eine Linux-Umgebung
auf dem Entwicklungsrechner braucht.
Ampel lokal: 582 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e1577a4f47 |
docs: SAVEPOINT v4.0-rc — Windows ist ein echtes Programm
Ampel / ampel (push) Failing after 55s
Der SAVEPOINT stand seit V2-3 still, waehrend elf Commits dazukamen. Nachgetragen mit den gemessenen Zahlen: 562 gruen, RippySetup.exe 31,7 MB, WebView2 151.0.4129.107, 5 Encoder auf diesem PC. Zwei Dinge stehen bewusst gross drin, weil sie sonst verloren gehen: * Der Dienst, der /api/health mit 200 beantwortete und / mit 404 -- und warum ein %TEMP%-Verzeichnis kein Ort fuer eine Oberflaeche ist. * Der Test, der den PC des Commanders getroffen hat. Ein Test, der Spuren ausserhalb von tmp_path hinterlaesst, ist kein Test, sondern ein Eingriff. Und was NICHT bewiesen ist, steht als solches da: Ein echter Rip unter Windows ist nie gelaufen, die VM haengt elf Commits zurueck. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c01ef1081c |
feat(windows): echtes Fenster statt Browser-Tab (WebView2 statt Electron)
Auf die Frage des Commanders: "Warum nutzen wir fuer Windows weiterhin
einen Browser? Warum nutzen wir kein Electron oder sowas und machen daraus
einen echten Client."
Die erste Haelfte trifft zu und ist umgesetzt. Die zweite ist abgelehnt --
gemessen, nicht geschaetzt:
pywebview + WebView2 8,0 MB in der Bau-Umgebung, 2,7 MB in der EXE
Electron 150-210 MB, dazu eine zweite Laufzeitumgebung
Windows 11 bringt die WebView2-Laufzeit mit (hier 151.0.4129.107).
Nachgemessen statt angenommen, was sie rendert:
navigator.userAgent ...Chrome/151.0.0.0 Safari/537.36 Edg/151.0.0.0
fetch ja EventSource ja CSS Grid ja Pfeilfunktionen ja
Dasselbe Chromium, das auch in Electron steckt -- nur ohne es ein zweites
Mal mitzuschleppen. RippySetup.exe waechst von 29,0 auf 31,7 MB.
Drei Dinge gehoeren dazu, nicht als Beiwerk:
* Kein stiller Rueckfall auf MSHTML. Der alte IE-Renderer stellt die
React-Oberflaeche nicht dar. Fehlt WebView2, oeffnet Rippy den Browser
und SAGT warum -- ein leeres Fenster waere schlimmer als ein Tab.
* Fenster und Tray sind getrennte Prozesse. pystray belegt unter Windows
den Haupt-Thread, das Fenster braucht ihn genauso.
* Die eigene Konsole wird versteckt, eine geerbte nie. GetConsoleProcessList
unterscheidet beides: genau ein Prozess an der Konsole heisst, sie
gehoert uns. Ohne diese Unterscheidung haette "Rippy.exe --status" im
Terminal das Terminal des Nutzers verschwinden lassen.
fix(windows): Oberflaeche neben das Programm legen statt aus %TEMP% bedienen
Beim Nachsehen im laufenden Betrieb gefunden: Ein Dienst lief eine Stunde,
/api/health gab HTTP 200, / gab 404. Im Fenster stand {"detail":"Not Found"}.
Der Server war in Ordnung. Sein Entpack-Verzeichnis war es nicht:
_MEI000074b02 31 Eintraege, 5 Ordner -- kein ui, kein api
_MEI000082e82 44 Eintraege, 16 Ordner -- vollstaendig
Eine PyInstaller-Onefile-EXE liest bei JEDER Anfrage aus %TEMP%\_MEIxxxxx.
Ein Temp-Verzeichnis ist kein Ort fuer etwas, das eine Woche liegen bleibt.
Und der Ausfall ist der schlimmstmoegliche: Die API antwortet weiter, der
Dienst gilt als gesund, nur die Oberflaeche ist weg.
Genau davor warnte ROADMAP.md beim Bau-Verfahren ("one-dir statt one-file").
Die Abweichung bleibt, die Luecke wird geschlossen: Der Installer legt die
Oberflaeche neben das Programm, daemon._ui_pfad() nimmt diese Kopie zuerst.
Zeigt sich derselbe Ausfall an den API-Modulen, ist one-dir die Antwort.
Ampel: 562 gruen, ruff sauber. Fenster mit der echten Oberflaeche im
Bildschirmfoto nachgewiesen, nicht nur der Titel geprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
069fc46f44 |
fix(test): der Installations-Test hat die ECHTEN Verknuepfungen ueberschrieben
Ampel / ampel (push) Failing after 47s
WAS: `test_installation_legt_die_dateien_an_...` installiert in ein
tmp_path — legt aber seit dem Verknuepfungs-Einbau auch Symbole auf dem
ECHTEN Desktop und im ECHTEN Startmenue an. Die kennen kein tmp_path.
DIE FOLGE, vom Commander gemeldet: Er klickte auf das Desktop-Symbol und
bekam von Windows "Diese App kann auf dem PC nicht ausgefuehrt werden".
Nachgemessen zeigte die Verknuepfung auf
...\Temp\pytest-of-TobisPC\pytest-142\test_installation_..._0\installiert\Rippy.exe
— und dort liegt die 2-KB-ATTRAPPE aus dem Test, kein Programm. Windows
hatte voellig recht.
Der `finally`-Block raeumte nur Registry und Autostart auf; die
Verknuepfungen nicht, weil es sie beim Schreiben des Tests noch nicht gab.
Als sie dazukamen, wuchs der Test stillschweigend ueber sein tmp_path
hinaus. Ein Test, der Spuren ausserhalb seines Temp-Ordners hinterlaesst,
ist kein Test, sondern ein Eingriff.
BEHOBEN mit ZWEI Sicherungen statt einer:
1. verknuepfen=False — die Funktion legt gar keine an.
2. vk.alle_anlegen ist trotzdem ersetzt. Wer den Schalter spaeter
umdreht oder die Vorgabe aendert, kann damit trotzdem nichts am
echten System anrichten.
Dazu ein Waechter: test_installation_ruehrt_die_echten_verknuepfungen_
nicht_an prueft, dass die Anlege-Funktionen bei verknuepfen=False GAR
NICHT gerufen werden. Waere er frueher dagewesen, haette der Commander
keine kaputte Verknuepfung bekommen.
DER RECHNER IST REPARIERT: neu installiert, beide Verknuepfungen zeigen
wieder auf C:\Users\...\AppData\Local\Rippy\Rippy.exe (29 MB, existiert),
Programme+Features-Eintrag ist zurueck. Start ueber die Verknuepfung:
HTTP 200 nach 2 s, Worker TobisNicerPC mit cpu-x264/x265/av1 + vce/vce-av1,
MakeMKV 1.18.4 und HandBrake 1.11.2 gefunden.
Nebenbei ausgeschlossen, bevor die Ursache klar war: Die EXE selbst ist in
Ordnung (gueltiges x64-PE, MZ/PE-Kopf, 29 MB, Hash identisch mit dem
Installat), kein Mark-of-the-Web, kein Defender-Fund, Smart App Control
aus. Der Fehler lag nicht an der Datei, sondern daran, WORAUF gezeigt wurde.
GEMESSEN: ruff sauber, 522 Tests gruen + 15 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
99c05865af |
feat(windows): Desktop-Symbol und Startmenue-Eintrag — plus --oeffnen
Ampel / ampel (push) Failing after 47s
WAS: Das Setup legt jetzt eine Verknuepfung auf dem Desktop UND im
Startmenue an, die Deinstallation nimmt beide zurueck, und `--status`
zeigt, ob sie da sind. Abwaehlbar mit --keine-verknuepfungen.
WARUM (Commander-Meldung): "es gibt kein icon aufm desktop und keinen
startmenü eintrag". Zu Recht — ein Hintergrundprogramm ohne Symbol ist
praktisch unauffindbar: Es laeuft, aber es gibt keinen Weg hinein ausser
der Adresse im Kopf.
DIE VERKNUEPFUNG ZEIGT AUF --oeffnen, NICHT AUF --dienst. Ein Doppelklick
soll Rippy OEFFNEN; ob der Dienst laeuft, ist dem Nutzer egal und er kann
es auch nicht wissen. Zeigte das Symbol auf --dienst, startete jeder Klick
einen ZWEITEN Server, der am belegten Port scheitert — das Fenster blitzt
auf, im Browser passiert nichts. Ein Symbol, das beim zweiten Klick nichts
tut, ist schlimmer als keins.
`--oeffnen` sieht deshalb erst NACH, ob jemand antwortet, startet nur wenn
noetig, und wartet dann, bis der Dienst wirklich da ist — statt sofort
einen Browser auf eine tote Adresse zu schicken. Gemessen: erster Aufruf
2 s bis HTTP 200, zweiter Aufruf keine zusaetzlichen Prozesse.
DIE ORDNER KOMMEN AUS DER REGISTRY, nicht aus %USERPROFILE%. Wer OneDrive
benutzt oder seine Ordner verschoben hat, dessen Desktop liegt woanders —
die Verknuepfung landete dann in einem Ordner, den niemand sieht, und der
Nutzer suchte ein Symbol, das es nirgends gibt.
UEBER POWERSHELL statt pywin32: Eine .lnk ist ein binaeres Shell-Objekt.
pywin32 koennte es, waere aber 20 MB fuer vier Zeilen. Denselben Weg geht
der alte Worker-Installer schon ("nativ via PowerShell").
ZWEI FEHLER, DIE DIE TESTS GEFUNDEN HABEN:
- os.makedirs stand AUSSERHALB des try. Auf einem Laufwerk, das es nicht
gibt, wirft es — aus "Verknuepfung konnte nicht angelegt werden" waere
ein Abbruch der ganzen Installation geworden.
- Der Test dafuer nahm fest Z: als "gibt es nicht". Auf dem Commander-PC
ist Z: ein NETZLAUFWERK (X, Y, Z sind belegt). Ein fest verdrahteter
Laufwerksbuchstabe ist eine Annahme ueber eine fremde Maschine; jetzt
wird zur Laufzeit ein freier gesucht.
Und der Apostroph: In PowerShell wird er innerhalb einfacher
Anfuehrungszeichen durch VERDOPPELN maskiert. Ohne das braeche ein Pfad wie
C:\Users\O'Brien\... das Skript entzwei, und der Fehler saehe aus wie ein
Syntaxfehler in Rippy.
GEMESSEN, nach echter Installation nach %LOCALAPPDATA%\Rippy:
Desktop-Symbol -> Rippy.exe --oeffnen, mit Symbol und Beschreibung
Startmenue -> ebenso
Programme+Feat. -> Rippy 2.0.0, 100 MB, Deinstallieren funktioniert
Autostart -> "...\Rippy.exe" --dienst
Start ueber das Symbol: HTTP 200 nach 2 s
Worker: TobisNicerPC, Encoder cpu-x264/x265/av1, vce, vce-av1
GEMESSEN: ruff sauber, 521 Tests gruen + 15 uebersprungen (vorher 512).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f4a8d77598 |
feat(windows): Rippy arbeitet eigenstaendig — Rippen, Werkzeuge, Fähigkeiten
Ampel / ampel (push) Failing after 48s
WAS: Der Ablauf ist aus tasks.py heraus (ablauf.py, ohne Celery), die
LocalQueue wird bedient, ein Laeufer arbeitet Auftraege im selben Prozess
ab, und Rippy meldet sich mit gemessenen Faehigkeiten selbst als Arbeiter.
WARUM: "Rippy fuer Windows soll standalone funktionieren" (Commander). Bis
hierher konnte die Windows-App alles ANZEIGEN und nichts TUN — ein Rip waere
eingereiht worden und fuer immer liegengeblieben, weil niemand ihn holt.
DIE TRENNUNG: rip_disc hing an GENAU DREI Celery-Stellen in 234 Zeilen —
self.update_state, _transcode_queue, transcode_files.apply_async. Alle drei
sind Fragen der ZUSTELLUNG, nicht des Ablaufs. Sie sind jetzt Rueckrufe:
tasks.py reicht die Celery-Fassung herein, standalone.py die lokale. OHNE
Rueckruf komprimiert derselbe Prozess weiter — genau das, was ein
Ein-Prozess-Rippy braucht. Der Ablauf selbst ist Zeile fuer Zeile derselbe;
der Docker-Betrieb merkt vom Umbau nichts (Task-Namen, Argumente, Queues
unveraendert).
EINE ZUSTELL-STELLE statt drei: celery_client.abschicken() bedient alle
Auftragsarten. Vorher rief jede Stelle send_task selbst auf — der
Standalone-Betrieb haette an drei Stellen umgebogen werden muessen, beim
naechsten Auftragstyp an einer vierten.
WEITERER BLOCKER GEFUNDEN: ablauf.py holte detect_disc_type fest aus dem
LINUX-Treiber, in einem try/except. Unter Windows waere es damit IMMER None
gewesen und Rippen "hart verriegelt" — Rippy haette alles angezeigt und
nichts gerippt, ohne dass irgendwo ein Fehler stuende. Jetzt fragt es den
Treiber-Port.
WERKZEUGE: ripping.py und caps.py suchten nur im PATH. Auf dem Commander-PC
gemessen, vorher/nachher:
vorher check_makemkv_installed() -> False (obwohl installiert)
erkenne_encoder() -> nur CPU
nachher MakeMKV 1.18.4 C:\Program Files (x86)\MakeMKV\makemkvcon64.exe
HandBrake 1.11.2 ueber die API geholt, in 2,7 s
Encoder cpu-x264, cpu-x265, cpu-av1, VCE, VCE-AV1
107 Presets, Ryzen 7 9700X, 16 Kerne, avx512f
VCE ist die Hardwarebeschleunigung der Radeon — die hat Rippy auf diesem
Rechner vorher nie gesehen, weil es HandBrake gar nicht fand.
NEUE ROUTEN: GET /system/werkzeuge (was liegt wo, in welcher Fassung, gibt
es Neueres) und POST /system/werkzeuge/{name}/holen. HandBrake kommt
vollautomatisch von GitHub. MakeMKV wird NICHT mitgeliefert — Rippy laedt
die offizielle Datei und startet sie (Black-Box-Trennung, KONZEPT.md § 6).
makemkv.com antwortete beim Bauen mit HTTP 525; das wird im Klartext
gemeldet, und eine selbst geholte Datei bleibt moeglich.
HERZSCHLAG: /capabilities las die workers-Tabelle, die bisher nur der
Celery-Herzschlag fuellte. Im Standalone-Betrieb stand dort "0 Worker" und
die Encoder-Auswahl im UI blieb LEER — auf einem Rechner, der alles kann.
Jetzt meldet sich der Prozess selbst, mit dem, was caps.py MISST.
GEMESSEN, aus der fertigen EXE (29,0 MB):
bereit nach 1 s, keine Fehler im Log
Werkzeuge: beide gefunden, mit Version und Pfad
Worker: 1 (TobisNicerPC), 5 Encoder, 107 Presets
Die ganze Kette ist als Test festgehalten (test_kette.py): zustellen ->
einreihen -> Laeufer -> ablauf -> Job endet in einem EHRLICHEN Zustand.
Ohne Laufwerk geprueft, und das ist der wichtigere Fall: Ein Rip auf ein
totes Geraet muss zuegig scheitern, nicht auf "pending" haengenbleiben.
GEMESSEN: ruff sauber, 512 Tests gruen + 15 uebersprungen (vorher 489).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
288f9eeb8d |
feat(tools): Werkzeuge finden, holen und aktuell halten — Grundlage fuer Windows
Ampel / ampel (push) Failing after 47s
WAS: rippy/tools/ mit katalog.py (finden + Version) und beschaffen.py
(herunterladen, installieren, Update-Stand). ripping.py und caps.py fragen
den Katalog statt shutil.which.
DER BEFUND, DER DAS NOETIG MACHT: Der Bestand suchte AUSSCHLIESSLICH mit
shutil.which(). Im Container stimmt das — dort liegen beide Werkzeuge in
/usr/local/bin. Unter Windows findet es NICHTS, auch wenn alles installiert
ist: Windows-Programme liegen in Program Files, nicht im PATH. Am
28.08.2026 auf dem Commander-PC nachgesehen — MakeMKV lag unter
"C:\Program Files (x86)\MakeMKV\makemkvcon64.exe", which sah es nicht.
Fuer einen Rippy, der unter Windows eigenstaendig arbeiten soll, ist das der
Unterschied zwischen "laeuft" und "laeuft nicht". Vorher/nachher gemessen:
vorher check_makemkv_installed() -> False (obwohl installiert)
nachher check_makemkv_installed() -> True
build_makemkv_cmd()[0] -> C:\Program Files (x86)\MakeMKV\makemkvcon64.exe
SUCHREIHENFOLGE, mit Begruendung im Modul: eingestellt > Rippys eigener
Werkzeug-Ordner > PATH > bekannte Installationsorte > Uninstall-Zweig der
Registry. Rippys eigener Ordner steht VOR dem System, damit eine selbst
gepflegte Fassung eine alte Systeminstallation schlaegt.
VERSION: HandBrakeCLI kann --version. makemkvcon KANN DAS NICHT (usage.txt
kennt keinen solchen Schalter) — die Version kommt aus dem Uninstall-Zweig.
Dort steht bei MakeMKV zwar eine Version, aber InstallLocation ist LEER
(nachgesehen); deshalb wird der Ordner ersatzweise aus DisplayIcon bzw.
UninstallString abgeleitet. Der Test dafuer hat sofort einen Fehler
gefunden: '"C:\...\uninstall.exe" /S' liess sich mit einem blossen
strip('"') nicht aufloesen.
BESCHAFFUNG, an echter Hardware gemessen:
HandBrake GitHub-Release-API -> 1.11.2, HandBrakeCLI-1.11.2-win-x86_64.zip
geladen, entpackt, gestartet: meldet sich als 1.11.2. 2,8 s.
MakeMKV makemkv.com antwortete mit HTTP 525 (Cloudflare) — auch mit
Browser-Kennung. Dieselbe Sperre, die im Projekt schon den
Docker-Bau lahmlegt. Deshalb: Abruf wird versucht, ein
Fehlschlag wird im Klartext gemeldet, und eine selbst geholte
Datei laesst sich weiterhin verwenden. Ein Update-Server, der
heute antwortet, darf keine Startbedingung fuer morgen sein.
DREI FALLEN, GEGEN DIE ES TESTS GIBT:
- Die .sig-Datei liegt im Release direkt neben dem ZIP und ist 566 Bytes
gross. Wer nur nach "win" filtert, laedt sie.
- "1.9.2" ist als Zeichenkette GROESSER als "1.11.2". Ein Update-Hinweis, der
ab Version zehn dauerhaft in die Irre fuehrt — und genau dort ist
HandBrake gerade.
- Eine Fehlerseite kommt oft mit HTTP 200. Sie darf keine funktionierende
Installation ersetzen: erst laden, pruefen, dann tauschen.
Und: Ist die neueste Version unbekannt (Quelle tot), steht NICHT "Update
verfuegbar" da. Eine Nichtauskunft ist keine Aussage.
GEMESSEN: ruff sauber, 489 Tests gruen + 15 uebersprungen (vorher 456).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
059183c651 |
feat(windows): V2-4 fertig — RippySetup.exe, Taskmanager-Eintrag, Deinstallation
Ampel / ampel (push) Failing after 46s
WAS: Eine 28,6-MB-Datei, drei Betriebsarten. Doppelklick installiert,
`--dienst` laeuft im Hintergrund, `--deinstallieren` raeumt sich weg.
Dazu packaging/windows/build.py, das sie baut.
DIE ZWEI COMMANDER-WUENSCHE, GEMESSEN:
1. EINTRAG IM TASKMANAGER
ProcessName Rippy
Beschreibung Rippy — automatisches Ripping
Produkt Rippy
Version 2.0.0
Der Prozessname kommt vom Dateinamen (bei der Installation kopiert sich
die Datei als Rippy.exe), die Beschreibung aus einer eingebetteten
Versions-Ressource. Ohne die steht dort nichts, und Windows SmartScreen
sieht eine EXE ohne Herausgeberangabe genauso misstrauisch wie ein Mensch.
2. EINTRAG IN PROGRAMME UND FEATURES
Aus der Registry zurueckgelesen, nicht behauptet:
DisplayName Rippy
DisplayVersion 2.0.0
Publisher Rippy
InstallLocation <Zielordner>
UninstallString "<pfad>\Rippy.exe" --deinstallieren
EstimatedSize 29241 (KiB)
NoModify/NoRepair 1
HKCU statt HKLM: Der Eintrag erscheint genauso in "Apps & Features", die
Installation laeuft aber ohne UAC-Abfrage durch.
VOLLER KREISLAUF GEMESSEN: installieren -> Rippy.exe + rippy.ico liegen da,
Registry-Eintrag da, Autostart da; Dienst starten -> nach 1 s bereit, alle
sieben Endpunkte HTTP 200, Oberflaeche kommt; deinstallieren -> nach ~4 s
sind Datei, Ordner, Registry-Eintrag, Autostart UND das Aufraeum-Skript weg.
VIER FEHLER, DIE NUR DIE FERTIGE EXE ZEIGT:
1. rippy/store baute die Engine BEIM IMPORT, mit PostgreSQL als Vorgabe.
Im Windows-Paket gibt es psycopg2 bewusst nicht -> die EXE starb sofort.
Die Engine entsteht jetzt beim ersten Zugriff. Und der Fehler war
grundsaetzlicher als der fehlende Treiber: Ein Modul, das beim Import
schon eine Verbindung aufbaut, laesst sich gar nicht mehr umstellen —
store.verbinden() kaeme immer zu spaet.
2. celery_client baute den Broker-Client beim Import. Dasselbe Muster, und
`from celery_client import celery_client` loeste es sogar dann aus, wenn
nie ein Rip angestossen wird. Jetzt traege, mit KeinBroker als klarer
Ansage statt eines Importfehlers.
3. Die Selbstloeschung beim Deinstallieren ging als EIN Argument an cmd.
Python maskiert dabei die inneren Anfuehrungszeichen — cmd suchte einen
Dateinamen mit Backslashes davor, fand nichts und meldete nichts (>nul).
Registry und Autostart waren weg, die 28-MB-Datei blieb liegen: ein still
scheiternder Hintergrundprozess, wie ihn AGENTS.md beschreibt. Jetzt ein
Aufraeum-Skript, das 15-mal versucht (die Onefile-EXE laeuft als ZWEI
Prozesse; der Starter haelt die Datei nach dem Ende noch offen).
4. deinstallieren() raeumte den STANDARD-Ordner auf statt des tatsaechlichen.
Wer nach --ziel D:\Rippy installiert hatte, dessen Dateien waeren
geblieben, waehrend ein fremder Ordner angefasst worden waere. Der Pfad
steht in der Registry (InstallLocation) und wird jetzt dort gelesen.
WAS NOCH NICHT GEHT, ausdruecklich: Ein RIP laesst sich unter Windows noch
nicht anstossen — die Zustellung laeuft weiter ueber Celery. Die Umstellung
auf die LocalQueue ist V2-5. Oberflaeche, Laufwerks-Erkennung, Auswurf und
alles Lesende laufen.
GEMESSEN: ruff sauber, 456 Tests gruen + 15 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e5254c23f0 |
feat(daemon): V2-4 (Teil 3) — rippyd laeuft unter Windows, ohne Docker
Ampel / ampel (push) Failing after 46s
WAS: `rippyd` startet Rippy als EINEN Prozess — SQLite statt Postgres, LocalQueue statt Celery/Redis, In-Process-Bus statt Redis Pub/Sub, UI von FastAPI statt von nginx. Dieselbe API, dasselbe UI, derselbe Rip-Code. GEMESSEN (Windows 11, frische Python-3.12-Umgebung, ohne Docker/Postgres/Redis): bereit nach 1 s /api/health 200 /api/settings 200 /api/devices 200 /api/logs 200 /api/jobs 200 /api/capabilities 200 /api/health/vorraete 200 / (Weboberflaeche) 200 Ereignis-Waechter gesund: true, alter_sekunden: 0.5 SSE-Strom liefert `event: snapshot` mit vollem Zustand SQLite angelegt, WAL aktiv, alle Tabellen da ZWEI FEHLER, DIE NUR DER ECHTE LAUF ZEIGT: 1. STARLETTE REICHT LIFESPAN NICHT AN MOUNT-UNTERANWENDUNGEN WEITER. Das UI ruft /api/..., im Docker-Betrieb entfernt der nginx das Praefix. Ohne nginx haengt die API als Unteranwendung unter /api — und ihr startup_event lief nie. Folge: db.init_db() nicht ausgefuehrt, "no such table: settings", HTTP 500 auf /jobs, /settings, /logs, /capabilities, und der SSE-Strom blieb stumm. /api/health antwortete trotzdem mit 200, weil die Route keine Datenbank braucht — der Fehlschlag sah also aus wie "laeuft". Behoben ueber einen eigenen lifespan, der den der Unteranwendung mit ausloest. 2. EIN print() HAT DEN GANZEN START UMGEBRACHT. UnicodeEncodeError: 'charmap' codec can't encode characters -> ERROR: Application startup failed. Exiting. Windows-Konsolen arbeiten mit cp1252; Rippys Meldungen sind deutsch und voller Umlaute und Warnzeichen. Das Bittere: Es war nicht die WARNUNG, die den Start verhindert hat, sondern der VERSUCH, sie auszugeben. Behoben mit encoding="utf-8" UND errors="replace" — eine Ausgabe darf unter keinen Umstaenden etwas abbrechen koennen. BEFUND ZUR HARDWARE (kein Codefehler): Nach dem Auswurf-Test hat sich das USB-Laufwerk vom Bus getrennt. Windows kennt es nur noch als Karteileiche (Get-PnpDevice -Class CDROM -> Status "Unknown", Win32_CDROMDrive leer), weder ein Laufwerksbuchstabe noch \.\CdRom0..4 sind da. Die leere Geraeteliste ist damit die RICHTIGE Antwort. Bei bus-versorgten USB-Laufwerken ist das bekannt — der Auswurf zieht kurz mehr Strom. GEMESSEN: ruff sauber, 440 Tests gruen + 15 uebersprungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9d95c95d79 |
feat(drives): Windows-Treiber an echter Disc BEWIESEN
Ampel / ampel (push) Failing after 46s
WAS: Die Hardware-Schicht des Windows-Treibers ist nicht mehr nur gebaut,
sondern gemessen. Messwerte stehen im Modul-Docstring, der gemessene
BD-50-Wert als Testfall in test_cdrom.py.
GEMESSEN (28.08.2026, LG BU40N extern, Laufwerk G:, echte BD-50):
drive_status() -> 4 (CDS_DISC_OK), sofort
IOCTL_CDROM_DISK_TYPE -> DATA_TRACK
IOCTL_DISK_GET_LENGTH_INFO -> 48 149 364 736 Bytes = 44,84 GiB
disc_status() -> 101 (CDS_DATA_1)
detect_disc_type() -> 'bluray'
device_info()['status'] -> 'ready'
device_info()['type'] -> 'bluray'
auswerfen_versuchen() -> True, 2,41 s
drive_status() davor / danach -> 4 / 1
DIE LETZTE ZEILE IST DER EIGENTLICHE BEWEIS. Der Auswurf haengt nicht am
Rueckgabewert des Steuercodes, sondern am ZUSTAND davor und danach: Die
Disc war drin (4) und ist danach draussen (1). Genau das fehlte in v1
monatelang — dort quittierte das ioctl Erfolg, die Schublade blieb zu, und
im Log stand "Disc ausgeworfen".
Die 44,84 GiB pruefen die Schwelle gleich mit: Eine BD-50 liegt knapp unter
den 55 GiB, ab denen classify() auf 'uhd' geht, und wird korrekt als
'bluray' eingeordnet. Wer die Schwelle senkt, macht aus jeder BD-50 eine
UHD — und Rippy waehlte dann das falsche Kompressions-Preset. Der Testfall
haelt den gemessenen Wert fest.
Offen bleibt allein die Ereignis-Erkennung (WM_DEVICECHANGE) — die wird
erst gebaut.
Nebenbei: Beim Eintragen der Messwerte sind drei echte NUL-Bytes in die
Datei geraten (eine Escape-Ebene im Schreib-Skript verschluckt). Python
lehnt so eine Datei rundheraus ab ("source code string cannot contain null
bytes") — im Binaermodus repariert und gegengeprueft.
GEMESSEN: ruff sauber, 430 Tests gruen + 14 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3d7f3b1680 |
feat(drives): V2-4 (Teil 2) — main.py laedt unter Windows, Treiber gemessen
Ampel / ampel (push) Failing after 46s
WAS: rippy/drives/__init__.py waehlt den Treiber fuer DIESE Maschine.
main.py und prescan.py fragen ihn, statt fest den Linux-Treiber zu
importieren. Damit ist der Blocker fuer den nativen Windows-Betrieb weg.
GEMESSEN, vorher (frische Python-3.12-Umgebung unter Windows):
>>> import main
ModuleNotFoundError: No module named 'fcntl'
GEMESSEN, nachher:
main.py laedt unter Windows: JA
Treiber: rippy.drives.windows
Routen: 61
Laufwerke: ['\\.\G:']
Der Import stand in rippy/drives/linux.py -> detection.py -> fcntl. Genau
dafuer gibt es den Port: Der Aufrufer sagt WAS, nicht WIE. test_treiberwahl.py
haelt ausserdem fest, dass BEIDE Treiber denselben Satz Funktionen anbieten —
sonst faellt eine fehlende erst im Betrieb als AttributeError auf, und zwar
auf der anderen Plattform.
DER TREIBER AN ECHTER HARDWARE (LG BU40N extern, Laufwerk G:, LEER):
GetDriveTypeW("G:\\") -> 5 (DRIVE_CDROM)
list_optical_devices() -> ['\\.\G:']
CreateFileW(r"\.\G:") -> Handle 380
CHECK_VERIFY2 bei leerem Laufwerk -> ERROR_NOT_READY (21)
drive_status() -> 1 (CDS_NO_DISC)
detect_disc_type() -> 'no_disc'
device_info()['status'] -> 'empty'
MEDIA_REMOVAL (entriegeln) -> Erfolg, 0,1 ms
EJECT_MEDIA -> Erfolg, 664,8 ms
Die 664,8 ms sind der Beleg, dass wirklich etwas passiert ist: Ein
abgelehnter Steuercode kommt in rund 2 ms zurueck.
BEFUND, DER EIN PLACEBO VERHINDERT HAT: IOCTL_STORAGE_LOAD_MEDIA (Schublade
einziehen) antwortet auf diesem Laufwerk mit ERROR_INVALID_FUNCTION. Bei
flachen und externen Laufwerken ist das die Regel. Deshalb gibt es bewusst
KEINE einziehen()-Funktion — ein Knopf "Schublade schliessen", der auf der
Haelfte aller Laufwerke stillschweigend nichts tut, waere genau die Sorte
Placebo, die in Etappe 19 aufgeraeumt wurde.
Dazu eine DeprecationWarning behoben: '\G' im Docstring ist keine gueltige
Escape-Sequenz und waere in einer kuenftigen Python-Version ein harter Fehler.
NOCH NICHT gemessen: das Verhalten MIT eingelegter Disc (Typ-Erkennung ueber
die Groesse, und ein Auswurf, bei dem wirklich etwas drin ist).
GEMESSEN: ruff sauber, 429 Tests gruen + 13 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
b723fee3a2 |
feat(drives): V2-4 (Teil 1) — Windows-Laufwerkstreiber, ohne Hardware geprueft
Ampel / ampel (push) Successful in 45s
WAS: rippy/drives/windows.py mit denselben zwei Vertraegen wie die Linux-Fassung (auswerfen_versuchen gibt False zurueck, eject wirft). Dazu win_ioctl.py: die Win32-Steuercodes, HERGELEITET statt abgeschrieben. WARUM ctypes und nicht pywin32: 20 MB zusaetzliche Abhaengigkeit, die in jedes PyInstaller-Paket muss und bei jedem Python-Wechsel neu passen will. Gebraucht werden fuenf Funktionen aus kernel32 — die spricht ctypes direkt an. DIE STEUERCODES WERDEN AUSGERECHNET, NICHT ABGETIPPT: win_ioctl.py enthaelt das CTL_CODE-Makro aus winioctl.h als Funktion; jede Konstante wird daraus gebildet. test_win_ioctl.py rechnet das Ergebnis gegen die in der Microsoft-Dokumentation stehenden Zahlen. Grund: Ein falscher IOCTL-Code meldet kein "unbekannter Befehl", sondern ERROR_INVALID_FUNCTION — und das liest sich wie "dieses Laufwerk kann das nicht". Man sucht dann am Geraet statt an einer Zahl. AGENTS Regel D, ernst genommen. DIE ZWEI PORT-REGELN GELTEN AUCH HIER: - Auswerfen heisst entriegeln, auswerfen, NACHSEHEN. Unter Linux wurde am 26.07.2026 gemessen, dass ein verriegeltes Laufwerk den Auswurf mit ERFOLG quittiert und nichts tut. IOCTL_STORAGE_EJECT_MEDIA meldet ebenfalls nur die Annahme des Befehls — also wird auch hier nachgesehen (CHECK_VERIFY2 muss ERROR_NOT_READY liefern). - Ein unzugaengliches Laufwerk ist UNBEKANNT, nicht leer. device_info kennt deshalb drei Zustaende, nicht zwei. EIN FEHLER, DEN DIE TESTS SOFORT GEFANGEN HABEN: Win32Fehler setzte self.winerror VOR super().__init__(). Nachgemessen: Ein zweiargumentiges OSError.__init__(code, text) setzt winerror wieder auf None. Damit erkannte _medium_da ERROR_NOT_READY nicht mehr, hielt jedes leere Laufwerk fuer einen echten Fehler und meldete "unbekannt" statt "leer". Vier Tests rot, Ursache in einer Minute gemessen statt geraten. ⚠️ WAS HIER NICHT BEWIESEN IST: dass echte Hardware sich so verhaelt. Das Laufwerk ist derzeit nirgends angeschlossen. Der Ablauf, die Fehlerunterscheidung und die Typ-Zuordnung sind gegen ein nachgebautes Laufwerk geprueft (51 Tests, laufen auf jeder Plattform) — die Hardware- Schicht gilt bis zu einer Messung als GEBAUT, nicht als BEWIESEN. GEMESSEN: ruff sauber, 400 Tests gruen + 4 uebersprungen (vorher 378). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4337253797 |
docs: SAVEPOINT v4.0-beta — V2-0 bis V2-3 stehen, Laufwerk fehlt an der VM
Ampel / ampel (push) Successful in 41s
Vier Etappen gebaut und deployt, Polling ist weg (121 Anfragen/min je Tab -> ~2 einmalige Abrufe), SSE-Strom live belegt. Wichtig fuer die naechste Sitzung: Das optische Laufwerk haengt NICHT mehr an der VM. Rippy laeuft als reine Komprimier-Maschine. Und der Vorfall, der das aufgedeckt hat, ist dokumentiert — ein devices:-Eintrag in compose ist eine Startbedingung, und die hat api, worker und ui stillgelegt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
05ab655116 |
fix: ein fehlendes Laufwerk darf Rippy nicht am Starten hindern
Ampel / ampel (push) Successful in 40s
WAS: Die `devices:`-Eintraege fliegen aus docker-compose.yml. Was dieser Host wirklich hat, ermittelt deploy/geraete-override.sh und schreibt es in docker-compose.override.yml — die zieht Compose von allein dazu, der Befehl bleibt `docker compose up -d`. WARUM (Vorfall heute, 28.08.2026): Das USB-Laufwerk haengt nicht mehr an der VM. Ein ganz normaler Deploy legte daraufhin api, worker UND ui still: Error response from daemon: error gathering device information while adding custom device "/dev/sr0": no such file or directory Ein `devices:`-Eintrag ist eine STARTBEDINGUNG — und keiner der drei Container braucht zum STARTEN ein Laufwerk. Rippy war unten, und die Meldung stand mitten im Build-Rauschen (dieselbe Klasse wie das geschluckte `|| echo` beim .env-Kopieren am 26.07.). DER WIDERSPRUCH WAR AELTER ALS DER VORFALL: install.sh sagt bei fehlendem Laufwerk ausdruecklich "die Installation laeuft trotzdem durch; diese Maschine dient dann als reine KOMPRIMIER-Maschine" — die Compose-Datei sah das anders. Eine Maschine ohne Laufwerk ist ein VORGESEHENER Betriebsfall: der Windows-Worker und der GPU-Encoder sind genau das. Das Skript uebernimmt die Erkennung aus install.sh unveraendert: sr- und sg-Knoten ueber die SCSI-Adresse in /sys abgeglichen, nicht geraten. Und es schreibt die Override AUCH dann, wenn kein Laufwerk da ist — sonst bliebe eine alte Datei mit /dev/sr0 liegen und der Fehler waere derselbe, nur schwerer zu finden, weil sie gitignored ist und in keinem Diff auftaucht. deploy.sh ruft es vor dem Start auf. 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>
|
||
|
|
f097a0b59d |
feat(bus): Waechter — Worker-Aenderungen werden zu Ereignissen
Ampel / ampel (push) Successful in 43s
WAS: rippy/bus/waechter.py sieht im Takt von 1 s in der Datenbank nach und meldet Aenderungen an den Bus: job.created / job.phase / job.progress / job.finished und log.line. 12 Tests, ohne Datenbank lauffaehig. WARUM ES IHN BRAUCHT: Der Bus verteilt innerhalb EINES Prozesses. Der Worker ist aber ein eigener Prozess, im Docker-Betrieb ein eigener Container, im verteilten Betrieb ein anderer Rechner. Ohne Bruecke wuesste die API nichts vom Fortschritt eines Rips. WARUM UEBER DIE DATENBANK und nicht per HTTP-Rueckruf oder Redis: - HTTP-Rueckruf: jeder Fortschrittswert haengt an einer Verbindung, ein kurzer API-Neustart liesse Ereignisse verschwinden, und der Worker braeuchte zusaetzliche Konfiguration. - Redis Pub/Sub: sauber, kommt im verteilten Betrieb (V2-5) — aber der Standalone-Betrieb hat bewusst KEIN Redis, das war der Punkt von V2-2. - Die Datenbank ist ohnehin die Wahrheit ueber den Job-Zustand, ist in JEDEM Modus da, und der Worker schreibt dort sowieso hin. "Ist das nicht wieder Polling?" Doch — aber einmal, lokal und billig: vorher N Tabs x 9 Endpunkte alle 4-5 s ueber HTTP durch nginx durch das Rate-Limit; jetzt EINE Abfrage pro Sekunde von vier Spalten. Das Ziel war nie "nirgendwo nachfragen", sondern: der Browser fragt nicht mehr. DER TEST HAT EINEN ECHTEN FEHLER GEFUNDEN: Der Docstring versprach "erst neue Jobs, dann Aenderungen", die Schleife lieferte aber die Reihenfolge des dicts — ein Client haette ein job.progress fuer einen Job bekommen koennen, den er noch gar nicht kennt. Jetzt drei getrennte Durchlaeufe. Das Versprechen war richtig, der Code nicht. Und er stirbt nicht still: Fehler gehen ins Log UND als system.notice an den Bus (erster Fehlschlag, danach jeder zehnte), und lebt_seit_sekunden() macht sein Alter abfragbar — AGENTS.md, "wer einen Vorrat anlegt, macht sein Alter abfragbar". GEMESSEN: ruff sauber, 371 Tests gruen + 3 uebersprungen (vorher 359). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1fbe2cfc55 |
fix(test): Snapshot-Tests brauchten eine Datenbank — die Ampel hat keine
Ampel / ampel (push) Successful in 40s
WAS: test_snapshot_* ersetzen den Store per monkeypatch, statt
db.list_jobs() wirklich aufzurufen.
WARUM ROT (Lauf 170): Die Ampel startet KEINE Postgres. Mein neuer Test
war der erste im ganzen Repo, der eine Datenbank angefasst hat — alle
anderen in test_api_smoke.py pruefen nur Importe und Routen. Auf der VM
und lokal waere es nie aufgefallen: dort ist eine DB da bzw. der ganze
Test wird uebersprungen (kein fcntl unter Windows).
Geprueft wird die Snapshot-LOGIK ("konnte nicht nachsehen" ergibt None,
nicht []), nicht die Datenbank. Also gehoert die Datenbank da nicht rein.
Dazu ein zweiter Test: der Snapshot muss alle vier Bereiche liefern
(jobs/workers/logs/devices) — ein Client, dem einer fehlt, muesste den
Rest raten und fiele auf Polling zurueck.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
55db9eb13f |
feat(api): V2-3 (Teil 1) — Ereignis-Bus und SSE-Endpunkt /api/v2/events
Ampel / ampel (push) Failing after 41s
WAS: rippy/bus mit Ereignis-Schema, In-Process-Treiber und Ringpuffer.
Dazu der SSE-Strom in der API: erst ein Snapshot, danach nur Deltas,
mit lueckenloser Wiederaufnahme ueber Last-Event-ID.
WARUM: Ein offener Tab plus ein Worker verursachen heute rund 133
Anfragen pro Minute (Dashboard 75 + Log-Kasten 24 + Laufwerke 12 +
Worker-Liste 4 + Log-Seite 6 + Tray 12). Das Rate-Limit stand einmal
UNTER dieser Zahl — daher die sich leerende Job-Liste im Sekundentakt.
Mit einer offenen Verbindung sind es null.
DREI EIGENSCHAFTEN, ALLE AUS v1-FEHLERN:
1. Der Bus traegt NUR Nachrichten ueber Aenderungen, nie den Zustand.
Wer den Zustand will, fragt den Store. Damit kann ein verpasstes
Ereignis auch keinen Zustand loeschen — anders als beim fuenffachen
`catch(() => [])` im alten UI, wo jeder fehlgeschlagene Abruf
"es gibt keine Jobs" bedeutete.
2. Eine zu grosse Luecke wird ANGESAGT, nicht verschluckt. nachliefern()
gibt None ("hol dir ein ganzes Bild") statt [] ("nichts verpasst") —
dieselbe Unterscheidung wie timeout-Rueckgabe 124 bei den Netzpfaden.
Stillschweigend weiterzumachen waere schlimmer: Das UI hielte sich
fuer aktuell und waere es nicht.
3. Der Snapshot meldet unlesbare Laufwerke als None, nicht als leere
Liste. "Konnte nicht nachsehen" ist etwas anderes als "gibt es nicht".
Ein unbekannter Ereignistyp fliegt beim Senden HOCH statt durchzugehen.
Ein Tippfehler waere sonst der stillste aller Fehlschlaege: Nachricht
raus, kein Empfaenger, nirgends ein Hinweis.
Ein langsamer Zuhoerer (Tab im Hintergrund, lahmes Handy) bremst den
Sender nicht — er wird markiert und bekommt beim naechsten Mal einen
Snapshot. Ein Rip darf nicht auf einen Browser warten.
GEMESSEN: ruff sauber, 359 Tests gruen + 3 uebersprungen (vorher 346).
13 neue Bus-Tests laufen auf jeder Plattform; die vier SSE-Tests haengen
an main.py und laufen damit in der Ampel.
NOCH OFFEN in V2-3: das UI auf den Strom umstellen (neun setInterval)
und die Ereignisse an den Zustandsaenderungen ausloesen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
d2645f87e8 |
feat(core): V2-2 — SQLite hinter demselben Port, Auftrags-Queue mit Lease
Ampel / ampel (push) Successful in 39s
WAS: Der Store spricht jetzt beide Datenbanken (PostgreSQL wie bisher, SQLite fuer den Standalone-Betrieb). Dazu die LocalQueue: Auftraege als Tabelle, Vergabe per bedingtem UPDATE, Lease statt Zombie-Jagd. Und die Konfigurationsschicht mit EINER Praezedenz fuer alle drei Betriebsarten. WARUM: Ohne SQLite und ohne broker-lose Queue gibt es keinen Standalone- Betrieb — und ohne den keine Windows-App und keine Headless-Variante. Beides haengt an dieser Etappe. DER GRUNDSATZ (KONZEPT-V2.md §3.1): Die Datenbank ist die Wahrheit ueber den Job-Zustand, der Broker ist nur der Wecker. Daraus folgt die ganze Queue: Ein Auftrag ist eine Zeile mit claimed_by und lease_until, ein Knoten uebernimmt ihn per bedingtem UPDATE (es gewinnt genau EINER, auch wenn zehn gleichzeitig fragen), und er haelt ihn per Lease am Leben. Laeuft die Lease ab, ist der Auftrag frei — egal ob Absturz, Netzausfall oder gezogener Stecker. Damit gibt es den Zustand "laeuft, aber niemand arbeitet daran" nicht mehr, den v1 mit zombies.py (206 Zeilen + 263 Zeilen Tests) nachtraeglich einsammeln musste. Er kann hoechstens 60 Sekunden bestehen und heilt sich dann selbst. Beides ist geprueft: zwei Knoten bekommen NICHT denselben Auftrag, und eine abgelaufene Lease gibt ihn wirklich wieder her. KEINE QUEUE-BIBLIOTHEK (Taskiq/ARQ/Celery-lite), Begruendung im Modul-Docstring: Der teure Teil ist kein Task, sondern ein 30-90-Minuten- Subprozess mit Fortschritts-Parsing. Was die Bibliotheken loesen, ist nicht das Problem; was das Problem ist, muss man ohnehin selbst bauen. ABWEICHUNG VOM ENTWURF — KEIN ALEMBIC (in KONZEPT-V2.md §3.2 vermerkt): Die Datenbank auf der VM gibt es schon, Alembic muesste sie erst stempeln — ein Handgriff auf einer laufenden Installation, den beim naechsten Deploy jemand vergisst. Und es gibt hier nichts zu versionieren: drei nachgetragene Spalten. store.migrieren() fragt stattdessen per inspect() nach und legt nur Fehlendes an; das laeuft auf beiden Dialekten. Der alte Weg (ADD COLUMN IF NOT EXISTS) war korrekte Postgres-Syntax und waere auf SQLite gebrochen. EIN KONSTRUKTIONSFEHLER, SELBST GEFUNDEN: lokal.py hatte anfangs `from rippy.store import engine` — ein Import bindet den Wert EINMAL, ein spaeteres store.verbinden() waere nie angekommen, und das Modul haette weiter mit der alten Datenbank gesprochen. Jetzt wird store.engine zur Aufrufzeit gelesen; die Warnung steht an beiden Stellen im Code. GEMESSEN: ruff sauber, 346 Tests gruen + 3 uebersprungen (vorher 301). Neu: 30 Tests gegen ECHTES SQLite (kein Fake) — inklusive der Gegenprobe, dass WAL und busy_timeout wirklich gesetzt sind, und dass migrieren() eine weggenommene Spalte zurueckholt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7558be4854 |
fix: CRLF in gui.py wiederherstellen (Zeilenenden-Falle in V2-1)
Ampel / ampel (push) Successful in 39s
WAS: docker/worker/gui.py war CRLF und ist beim Umstellen des db-Imports komplett auf LF gekippt — 1520 Zeilen Diff-Rauschen fuer EINE geaenderte Zeile. Zeilenenden zurueckgedreht, der Inhalt bleibt. WARUM ES PASSIERT IST: Mein Umschreib-Helfer las mit io.open(p, encoding="utf-8") — im Textmodus wandelt Python CRLF beim LESEN zu LF — und schrieb mit newline="" zurueck, also LF. Betroffen war nur gui.py; die uebrigen angefassten Dateien sind ohnehin LF. Richtig waere newline="" AUCH beim Lesen gewesen (dann bleiben die Zeilenenden im String stehen) oder gleich der Binaermodus. Das steht so schon in den Projekt-Notizen und ist mir trotzdem passiert, weil der Helfer generisch war und die Datei nicht danach aussah. Gegenprobe: `file docker/worker/gui.py` sagt wieder "with CRLF line terminators", und `git diff HEAD~2 -- gui.py` zeigt genau eine Zeile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dd1d0b7365 |
refactor(core): V2-1 — die vier Ports, ein Store, ein Laufwerks-Treiber
Ampel / ampel (push) Successful in 40s
WAS: rippy/ports.py beschreibt Store/Queue/Bus/Drives als Protocol. Zwei
weitere Doppelungen sind zusammengelegt: db.py (API+Worker) wird
rippy/store, und der Auswurf (api/devices.py + ripping.wirf_disc_aus)
wird rippy/drives/linux. Verhalten unveraendert.
WARUM: Drei Betriebsarten tragen nur, wenn ein Modus die Auswahl der
Treiber hinter vier Nahtstellen ist statt ein eigener Codestand
(KONZEPT-V2.md §1). Diese Etappe zieht die Nahtstellen ein, ohne schon
einen zweiten Treiber zu haben — die kommen in V2-2 (SQLite/LocalQueue)
und V2-4 (Windows).
DIE UNANGENEHMERE DOPPELUNG WAR DER AUSWURF: Er stand zweimal da, mit
UNTERSCHIEDLICHEN Vertraegen — devices.eject wirft OSError, ripping.
wirf_disc_aus gibt False zurueck und wirft nie. Beides ist richtig fuer
seine Seite (Browser-Meldung gegen "ein Rip stirbt nicht an einer
klemmenden Schublade"). Jetzt liegt EINE Mechanik darunter
(auswerfen_mit_grund) und beide Vertraege unveraendert darueber.
Die API-Fassung war ausserdem NIE getestet — jetzt schon, inklusive
"reicht ENOENT/EPERM unveraendert weiter".
get_settings hatte den einzigen echten Verhaltensunterschied der beiden
db.py: die Worker-Fassung schluckte jeden Fehler und gab {} zurueck.
Nicht still entschieden, sondern sichtbar gemacht — der Parameter
bei_fehler_leer steht jetzt in der Signatur, mit der offenen Frage im
Docstring. {} heisst fuer den Aufrufer "nichts gesetzt", nicht "konnte
nicht nachsehen"; das ist dieselbe Klasse wie catch(() => []) im alten
UI. Zu entscheiden in V2-2.
ZWEITER BEINAHE-FEHLER DIESER ETAPPE: linux.py importierte detection
auf Modulebene — und das zieht fcntl. Damit waere ripping.py und ueber
es der NATIVE WINDOWS-WORKER nicht mehr ladbar gewesen. Diesmal haben
die Tests es sofort gefangen (4 Sammelfehler). Behoben an der Wurzel:
Konstanten und die reine classify() leben jetzt in drives/cdrom.py,
ganz ohne fcntl. Nebengewinn — die classify-Tests liefen bisher NUR in
der Ampel ("erst nach dem Push bewiesen") und laufen jetzt ueberall.
Ausserdem: .dockerignore-Testmuster brauchen **, sonst greifen sie nur
in der obersten Ebene. Im laufenden Container nachgezaehlt: 30 test_*.py
lagen in den Images.
GEMESSEN: ruff sauber, 301 Tests gruen + 1 uebersprungen (vorher 290;
+4 neue eject-Tests, +7 classify-Tests die jetzt lokal laufen). Kein
Modul liegt mehr doppelt im Repo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
edfe0d4313 |
docs: SAVEPOINT fuer v4.0-alpha — v2-Konzept entschieden, Etappe V2-0 gruen
Ampel / ampel (push) Successful in 39s
WAS: Neuer Kopfeintrag mit dem gemessenen Zustand (
|
||
|
|
b98dc5eef5 |
refactor(core): V2-0 — gemeinsames Paket rippy statt drei Zwillingsdateien
Ampel / ampel (push) Successful in 41s
WAS: Neues Paket src/rippy (core/drives/rip). detection.py,
makemkv_daten.py und notify.py lagen je ZWEIMAL im Repo — unter
docker/api UND docker/worker, byte-identisch. Jetzt gibt es sie einmal;
beide Container importieren dieselbe Datei. Verhalten unveraendert.
WARUM: Es gab kein geteiltes Paket zwischen den Containern, deshalb die
Kopien. docker/api/db.py sagt es im Kopfkommentar selbst: "Wer die
Struktur aendert, aendert BEIDE Dateien." Ein Waechter-Test
(test_zwillinge_sind_byteweise_identisch) hat das mechanisch
abgesichert — er war noetig, weil die Konstruktion falsch war. Erste
Etappe des v2-Plans (KONZEPT-V2.md §8.3).
IM EINZELNEN:
- src/rippy/{core/notify, drives/detection, rip/makemkv_daten}.py
- conftest.py in der Wurzel legt src/ auf den sys.path (die Ampel ruft
pytest dort auf).
- Beide Dockerfiles kopieren src/rippy nach /app/rippy — /app ist
Arbeitsverzeichnis und uvicorn-App-Dir, also ohne PYTHONPATH findbar.
- worker_setup_paket packt das Paket ausdruecklich mit ins Zip: die
Schleife sah nur die oberste Ebene, ein Unterordner waere nie
mitgekommen und der Windows-Worker beim Start gestorben.
- Die zwei Test-Dateien fuer makemkv_daten sind zu einer verschmolzen
(die Faelle aus beiden). Damit entfaellt auch der importlib-Umweg im
Worker-Test: der war noetig, weil bei "pytest -q" aus der Wurzel
docker/api zuerst eingesammelt wird und jeder weitere Import nur noch
den sys.modules-Cache trifft — die Worker-Kopie wurde also nie
angefasst. Netto -13 doppelte Tests, +2 neue (siehe unten).
EIN FEHLER, DEN DER UMBAU FAST AUSGELIEFERT HAETTE: In tasks.py steht
der detection-Import in einem "try/except ImportError" — absichtlich,
denn der native Windows-Worker hat kein fcntl und soll trotzdem
starten. Das except verschluckt aber JEDEN ImportError, auch einen
falschen Modulpfad. Auf Linux haette der Worker ab sofort still
detect_disc_type=None gesetzt und jeden Rip verweigert, ohne dass
irgendwo ein Fehler stuende — genau die Klasse "still scheiternder
Hintergrund-Prozess" aus AGENTS.md. Gefunden ueber eine zweite Suche
mit eingerueckten Treffern.
Waechter dagegen: src/rippy/test_paket.py prueft mit
importlib.util.find_spec (fuehrt nichts aus, laeuft also auch auf
Windows ohne fcntl), dass es die drei Modulpfade wirklich gibt. Beim
Wegnehmen von detection.py gegengeprueft: Test wird rot, nach
Wiederherstellen gruen.
GEMESSEN: ruff sauber, 290 Tests gruen + 1 uebersprungen (lokal, ohne
die zwei Linux-only-Module). Kein Modul liegt noch doppelt im Repo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
20675a98fa |
docs: Konzept fuer Rippy v2 + drei Commander-Entscheide (28.08.2026)
WAS: KONZEPT-V2.md neu — Systemarchitektur, Daten-/Queue-Strategie, die drei Betriebsmodi, API-/Event-Design, Migrationsplan V2-0 bis V2-7. KONZEPT.md §10 und ROADMAP.md ziehen nach. WARUM: Rippy soll drei Betriebsarten bekommen statt einer — Docker, native Windows-App ohne Docker, Headless-Linux-Dienst; alle mit demselben Webinterface. Das traegt technisch nur, wenn es EINEN Kern gibt, dessen Betriebsmodus nur die Auswahl der Treiber hinter vier Ports ist (Store, Queue, Bus, Drives). Drei Entscheide des Commanders sind eingearbeitet: - Reihenfolge: Echtzeit (SSE statt Polling) VOR der Windows-App. Die Windows-App braucht SQLite und die lokale Queue ohnehin. - Disc-Schluessel: automatischer Abruf MIT Rueckfallebene, als Kette in drei Stufen (eigener Bestand -> Abruf -> Import von Hand). Das verschiebt die Grenze aus KONZEPT.md §10 vom 25.07.2026 bewusst — deshalb steht sie dort jetzt ausdruecklich fortgeschrieben statt still ersetzt (AGENTS Regel B). Bezugsadresse leer vorbelegt, Fehlschlag laut, Bestand wird nie still ueberschrieben. - Speicherziele: der Host mountet, Rippy prueft und erzeugt die kopierbare Zeile. Raeumt die URSACHE der CIFS-Ausfaelle ab (Befund 26.07.2026: die Verbindung lebt in der Netz-Namespace des api-Containers und stirbt mit ihm) statt weiter das Symptom zu heilen. SYS_ADMIN, DAC_READ_SEARCH, apparmor:unconfined und die Mount-Wache fallen damit weg. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a14449ef85 |
fix(worker-ui): fix syntax error in gui.py and ruff error in test_verwaltung.py
Ampel / ampel (push) Successful in 39s
|
||
|
|
4343b0901d |
feat(worker-ui): adjust thumbnails, log view, and button logic to match web dashboard
Ampel / ampel (push) Failing after 40s
|
||
|
|
c3e77399ad |
fix(worker): mock ntpath.dirname in test_erreichbarkeit so it passes on Linux CI runner
Ampel / ampel (push) Failing after 39s
|
||
|
|
75426224d1 |
fix(worker): ensure sys.path in test_verwaltung for pytest importlib mode
Ampel / ampel (push) Failing after 39s
|
||
|
|
e5ff5373bb |
fix(worker): restore pure logic module verwaltung.py and its tests (SoC)
Ampel / ampel (push) Failing after 39s
|
||
|
|
9b30c0a490 |
chore: remove obsolete test_verwaltung.py
Ampel / ampel (push) Failing after 40s
|
||
|
|
061f79ecda |
fix: ruff linter errors (multiple statements, bare except, unused vars)
Ampel / ampel (push) Failing after 38s
|