Commit Graph
4 Commits
Author SHA1 Message Date
HitonabiandClaude Opus 5 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>
2026-08-28 13:09:01 +02:00
HitonabiandClaude Opus 5 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>
2026-08-28 12:14:15 +02:00
HitonabiandClaude Opus 5 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>
2026-08-28 09:03:46 +02:00
HitonabiandClaude Opus 5 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>
2026-08-28 08:38:03 +02:00