# KONZEPT — Rippy für Windows > **Rippy v5.** Ein eigenständiges Windows-Programm, gebaut mit Electron und > TypeScript. Grüne Wiese: kein Docker-Code, kein Python, keine Container-Reste. > > Stand: 30.08.2026 · Branch `worktree-windows-electron` --- ## 0. Leseanleitung | § | Frage | |---|-------| | 1 | Warum wird neu gebaut statt repariert? | | 2 | Was hat der Commander entschieden? | | 3 | Was wurde gemessen, bevor entschieden wurde? | | 4–5 | Wie ist das Programm aufgebaut, und was passiert, wenn eine Disc reingeht? | | 6–9 | Features, Oberfläche, Setup, Werkzeuge | | 10 | Was kommt aus dem alten Rippy mit? | | 11 | Was aus `main` entfernt wird | | 12–14 | Etappen, Risiken, was NICHT gebaut wird | **Was dieses Dokument NICHT tut:** Es erfindet keine Messungen. Alles, was hier als Zahl oder Verhalten steht, ist entweder in § 3 an dieser Maschine gemessen, im alten Rippy belegt (`SAVEPOINT.md`, `AGENTS.md`) oder ausdrücklich als **Annahme** markiert. Die Trennung ist wichtig, weil dieses Projekt schon mehrfach teuer dafür bezahlt hat, dass jemand eine plausible Erklärung für eine gemessene gehalten hat. --- ## 1. Warum neu gebaut wird Die heutige Windows-Fassung ist **kein Windows-Programm**. Sie ist der Docker-Code in einer EXE: ``` RippySetup.exe (PyInstaller) ├─ docker/api/ → FastAPI, uvicorn auf 127.0.0.1:7788 ├─ docker/worker/ → der Celery-Worker, nur ohne Celery ├─ docker/ui/dist/ → dieselbe React-Oberfläche wie im Browser └─ pywebview → ein Fenster davor ``` Das Ergebnis steht im `SAVEPOINT.md`: **elf Release-Kandidaten in drei Tagen.** Und die Funde sind fast alle vom selben Typ — Linux-Annahmen, die unter Windows etwas anderes bedeuten: | Fund | Was passierte | |------|---------------| | `timeout 4 ls -d ` als Verzeichnisprüfung | Unter Windows gibt es `timeout.exe`, aber sie kennt weder `ls` noch `-d`. Antwort war **„weg" für jedes Verzeichnis** — Rohdaten waren grundsätzlich unsichtbar. Dazu blitzten Konsolenfenster auf. | | `shutil.which("makemkvcon")` | Findet unter Windows nie etwas — Programme liegen in `Program Files`, nicht im PATH. Die 4K-Schlüssel-Automatik lief deshalb **nie** an. | | `os.path.isdir("/app")` | Rippy hielt sich für einen fremden Docker-Worker. Zweimal gefunden, an zwei Stellen. | | Routen `/dev/{name}` | Das UI ruft mit `G` auf, gebaut war `/dev/G`. **Auswerfen und Disc-Scan antworteten immer mit 404.** | | `F:\` wurde zu `F:` | Unter Windows ist `F:` ohne Trenner der *aktuelle Ordner* auf F, nicht dessen Wurzel. 16,5 GB Rohschnitt unsichtbar; der Dialog bot nur „Neu rippen" an. | | `makemkvcon` überlebte den Elternprozess | Windows räumt Kindprozesse nicht auf. Zwei Waisen hielten das Laufwerk fest. | | Zwei Speicher für dieselben Ordner | Die Oberfläche schrieb `outputDir` in die Datenbank, der Betrieb las `storage.medien` aus einer Konfigurationsdatei, die niemand schrieb. Eingestellt `E:\Rippy`, angezeigt `C:\Users\...\Videos\Rippy`. | Diese Liste ist nicht das Problem — sie ist das **Symptom**. Jeder einzelne Fund war eine Stelle, an der Code, der für einen Linux-Container geschrieben wurde, unter Windows etwas anderes bedeutet. Solange die Grundlage ein umgebogener Container ist, ist der nächste Fund dieser Sorte nur eine Frage der Zeit. **Die Entscheidung ist deshalb nicht „das ist schlecht gebaut", sondern: das ist am falschen Ort gebaut.** Ein Windows-Programm fängt bei Windows an, nicht bei einem Container, dem man Windows beibringt. --- ## 2. Die Entscheidungen (Commander, 30.08.2026) Acht Entscheide, alle am selben Tag. Die ersten vier legen die Architektur fest, die naechsten drei beantworten die Punkte, die das Konzept offengelassen hatte, und der achte steht bei der Sache, zu der er gehoert (Paragraph 11.1). Sie stehen hier, damit später nachvollziehbar ist, was Entscheidung war und was Vorschlag. ### Entscheid 1 — Alles TypeScript/Node Es gibt **keinen Python-Anteil** in Rippy für Windows. Kein Sidecar, keine mitgelieferte Laufzeit, keine IPC-Brücke zwischen zwei Sprachen. Eine Sprache, eine Werkzeugkette, ein Setup. *Was das kostet:* Die rund 18.000 Zeilen Python werden nicht übernommen. Was übernommen wird, ist ihr **Wissen** — die Parser-Regeln, die gemessenen Fallen, die Kommandozeilen (§ 10). Das ist der teure Teil an ihnen, und der wandert vollständig mit. *Was das bringt:* Der ganze Fehlertyp aus § 1 kann nicht mehr entstehen. Es gibt keinen Container-Pfad, den jemand vergessen könnte, weil es nie einen gab. ### Entscheid 2 — Die Docker-Version bleibt unangetastet Rippy auf der VM läuft weiter wie heute. Es wird nicht angefasst, nicht migriert, nicht mitgepflegt. Zwei getrennte Produkte, keine geteilten Module. *Begründung:* Geteilte Module zwischen zwei Betriebssystemen sind genau die Konstruktion, an der v1 gelitten hat — drei Dateien lagen byte-identisch doppelt im Repo, und `db.py` schrieb es sogar in den Kopfkommentar: *„Wer die Struktur ändert, ändert BEIDE Dateien."* ### Entscheid 3 — Reiner Einzelplatz Ein Rechner macht alles: erkennen, rippen, komprimieren, ablegen. Kein Server, keine Netz-Queue, keine Worker-Verwaltung, keine Pfad-Übersetzung zwischen Maschinen. *Was damit ersatzlos wegfällt:* Celery, Redis, Postgres, die Worker-Tabelle, `worker_direct`, die Lease-Mechanik über Netz, `RIPPY_PATH_MAP`, die Zombie-Erkennung, die Mount-Wache, das Rate-Limit (es gibt keinen fremden Client mehr), `/worker-setup/*`, die Encoder-Auswahl je Knoten. Grob geschätzt sind das **40 % der heutigen Komplexität** — und zwar der Teil, der die meisten Fehlerbilder erzeugt hat. *Was bleibt:* Die Ablage darf trotzdem auf dem NAS liegen. Ein UNC-Pfad (`\\nas\medien`) oder ein verbundenes Netzlaufwerk ist für Rippy nur ein Pfad. Windows mountet, Rippy prüft — dieselbe Trennung wie in Entscheid 3 des v2-Konzepts, nur ohne Container drumherum. ### Entscheid 4 — Eigener Branch im selben Gitea-Repo Ein Git-Worktree unter `.claude/worktrees/windows-electron` auf dem Branch `worktree-windows-electron`. `main` bleibt unberührt und jederzeit deploybar. ### Entscheid 5 — Kein Code-Signing-Zertifikat *„ist egal, brauchen wir nich"* Die EXE bleibt unsigniert. Folge: Beim ersten Start eines Downloads zeigt Windows den SmartScreen-Hinweis („Der Computer wurde geschützt"); über **Weitere Informationen → Trotzdem ausführen** geht es weiter. Das gehört in die Anleitung, nicht unter den Teppich. Eine Folge, die dazugehört und in § 6.8 steht: Ohne Signatur kann `electron-updater` ein heruntergeladenes Update **nicht** auf Echtheit prüfen. Die Absicherung liegt damit vollständig beim Transportweg — Updates werden nur über **HTTPS** geladen, nie über einfaches HTTP. ### Entscheid 6 — Updates aus Gitea, auch für Fremde erreichbar *„Gerne das Gitea Release, mit Möglichkeit dass auch EXTERNE diese Updates fahren können"* Zwei Teile, und der zweite hat eine Bedingung, die Rippy nicht selbst lösen kann: 1. **Rippys Seite** — die Update-Adresse steht in der Konfiguration, vorbelegt mit dem Gitea des Commanders. `electron-updater` braucht dafür nur einen statischen HTTPS-Ort mit zwei Dateien (`latest.yml` und das Setup). Damit ist Rippy von der Frage unabhängig, *wo* dieser Ort liegt. 2. **Die Netz-Seite** — ⚠️ **Gitea läuft auf `192.168.178.153` im Heimnetz. Von außen ist das nicht erreichbar.** „Externe können Updates fahren" verlangt also einen von drei Wegen, und die Wahl gehört dem Commander: | Weg | Was zu tun ist | Bewertung | |-----|----------------|-----------| | **Gitea veröffentlichen** | Reverse-Proxy + DynDNS + TLS-Zertifikat | Ein Dienst im Heimnetz wird öffentlich — die Angriffsfläche wächst. Nur mit Bedacht | | **Öffentlicher Spiegel** | Der Bau schiebt das Release zusätzlich zu einem öffentlichen Ort (GitHub-Release, Objektspeicher, gemieteter Webspace) | **Empfehlung.** Gitea bleibt privat, nur die fertigen Dateien liegen draußen | | **Jeder trägt seine eigene Quelle ein** | Nichts — die Konfiguration kann es schon | Für einzelne Nutzer mit eigenem Server. Skaliert nicht | Rippy wird für **alle drei** gebaut: eine Adresse in der Konfiguration, sonst nichts. Die Entscheidung kann später fallen, ohne das Programm anzufassen. ### Entscheid 7 — Der alte Windows-Weg wird entfernt, auch aus `main` *„Nein — direkt weg, auch aus dem Original Repo. Das neue Standalone Windows Rippy wird ein komplett neues Produkt und kein Aufbau auf die Docker-Architektur."* Die PyInstaller-Fassung bleibt **nicht** parallel installierbar, und ihr Code verlässt das Repo. Das ist die schärfere Fassung von Entscheid 2: Nicht nur werden die beiden Produkte nicht verwandt sein — der Zwitter dazwischen wird abgeräumt. **Das ist kein Widerspruch zu Entscheid 2.** „Docker bleibt unangetastet" meint die Docker-*Funktion*; der Windows-Standalone-Zweig ist gerade nicht Docker, sondern der Fremdkörper darin. Was genau entfernt wird, steht in § 11 — mit einer Unterscheidung, die beim Aufräumen entscheidend ist. --- ## 3. Was gemessen wurde, bevor entschieden wurde Electron bedeutet: Die Arbeit macht Node, nicht Python. Damit hängt das ganze Konzept an einer Frage, die vorher niemand geprüft hat: > **Kann Node überhaupt alles, was Rippy am Laufwerk braucht?** Wäre die Antwort nein, wäre das Konzept wertlos. Also gemessen — am 30.08.2026, auf diesem Rechner, am echten Laufwerk. ### 3.1 Die Werkzeuglage ``` Node v24.19.0 npm 11.17.0 Electron 44.0.0 (Chromium 152.0.7977.54 · Node 24.18.1) WebView2 151.0.4129.107 Laufwerk G: HL-DT-ST BD-RE BU40N USB Device (Medium eingelegt) ``` ### 3.2 Win32 aus Node — die Laufwerks-Steuerung `koffi` 3.1.6 installiert, dieselben Steuercodes wie in `src/rippy/drives/win_ioctl.py` (die dort aus dem `CTL_CODE`-Makro hergeleitet sind, nicht abgeschrieben). Nur lesende Aufrufe, nichts ausgeworfen: ``` 1. GetLogicalDrives + GetDriveTypeW -> optische Laufwerke: [ 'G' ] 2. CreateFileW \\.\G: (Zugriff 0) -> offen CreateFileW \\.\G: (GENERIC_READ) -> offen 3. IOCTL_STORAGE_QUERY_PROPERTY -> Hersteller: HL-DT-ST | Modell: BD-RE BU40N | Rev: 1.03 | Serial: 0025114C0149 4. IOCTL_STORAGE_CHECK_VERIFY2 -> Medium eingelegt 5. IOCTL_CDROM_DISK_TYPE -> Win32-Fehler 50 6. IOCTL_DISK_GET_LENGTH_INFO -> 33.759.690.752 Bytes (33,76 GB) => Blu-ray ``` **Punkt 5 ist kein Fehlschlag, sondern eine Bestätigung.** Fehler 50 ist `ERROR_NOT_SUPPORTED` — genau das, was auch der Python-Treiber an diesem Laufwerk sieht (`SAVEPOINT.md`, rc11). Node verhält sich identisch. Und die Lehre daraus steht schon im alten Code: **Die Disc-Einordnung darf sich nicht auf `IOCTL_CDROM_DISK_TYPE` verlassen — die Größe ist der verlässliche Weg.** ### 3.3 Die Prozess-Leine — überlebt makemkvcon einen Absturz? Der Befund aus rc10: Windows räumt Kindprozesse nicht auf. Ein hart beendeter Rippy hinterlässt ein `makemkvcon`, das das Laufwerk festhält. Die Lösung dort war eine Windows-Arbeitsgruppe (Job Object) mit `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`. In Node nachgestellt — Job Object gesetzt, Kind gestartet, Eltern mit `taskkill /F` **ohne** `/T` abgeschossen (genau das, was bei einem Absturz und beim Drüber-Installieren passiert): ``` 1. CreateJobObjectW -> Arbeitsgruppe angelegt 2. SetInformationJobObject -> KILL_ON_JOB_CLOSE gesetzt 3. AssignProcessToJobObject -> dieser Prozess hängt in der Gruppe 4. Kindprozess gestartet, PID 16040 vor dem Abschuss: Eltern lebt: True Kind lebt: True taskkill /F /PID 29488 -> ERFOLGREICH danach: Eltern lebt: False Kind lebt: False ``` ### 3.4 Datenbank ohne native Abhängigkeit ``` node:sqlite eingebaut -> geht ``` Node 24 bringt SQLite mit (`node:sqlite`, `DatabaseSync`). Keine `better-sqlite3`-Kompilierung, kein `node-gyp`, kein Visual-Studio-Build in der Bau-Kette. > ⚠️ **Ungeprüft:** Electron kompiliert sein Node mit eigenen Flags. Ob > `node:sqlite` in Electron 44 vorhanden ist, ist an *dieser* Stelle **nicht** > gemessen — nur in Node 24.19.0 selbst. Das ist der erste Handgriff in > Etappe W-0. Rückfallebene: `better-sqlite3` (bewährt, aber nativ und damit > eine Kompilierung in der Bau-Kette). ### 3.5 Ein Nebenbefund für die Bau-Kette `npm` 11 blockiert Install-Skripte fremder Pakete standardmäßig: ``` npm warn allow-scripts koffi@3.1.6 (install: node ./cnoke.cjs -P . --prebuild --release) ``` `koffi` lädt trotzdem — es bringt vorgebaute Binärdateien mit. Aber die Warnung gehört in die Bau-Anleitung, sonst sucht jemand eines Tages an der falschen Stelle. --- ## 4. Aufbau ### 4.1 Drei Teile, drei Prozesse ``` ┌──────────────────────────────────────────────────────────────┐ │ Rippy.exe — Electron Main-Prozess │ │ ├─ Tray-Symbol, Fensterverwaltung, Autostart, Selbst-Update │ │ └─ hält die Prozess-Leine (§ 3.3) — ALLES stirbt mit ihm │ └───────────┬─────────────────────────────┬────────────────────┘ │ IPC │ MessagePort ┌───────────▼──────────────┐ ┌──────────▼───────────────────────┐ │ Fenster (Renderer) │ │ Kern (utilityProcess) │ │ React + Tailwind │◄──┤ die GANZE Arbeit │ │ zeigt an, sonst nichts │ │ ├─ Laufwerkswache (koffi/Win32) │ │ │ │ ├─ Ablauf-Steuerung │ │ kein Node-Zugriff │ │ ├─ Auftragstabelle (SQLite) │ │ (contextIsolation) │ │ └─ Werkzeuge als Kindprozesse: │ │ │ │ makemkvcon · HandBrakeCLI │ └──────────────────────────┘ └──────────────────────────────────┘ ``` **Warum drei und nicht einer:** 1. **Ein Rip dauert 30–90 Minuten.** Läuft er im Main-Prozess, friert die Oberfläche ein — Electrons eigene Dokumentation sagt das ausdrücklich. 2. **Der Kern darf abstürzen, ohne das Fenster mitzureißen.** Dann steht dort „Der Kern ist abgestürzt, ich starte ihn neu" statt eines toten Fensters. 3. **Das Fenster darf zugehen, ohne den Rip zu beenden.** Rippy lebt dann im Tray weiter — genau wie heute, nur ohne den Umweg über einen HTTP-Server. **Warum `utilityProcess` und nicht `child_process.fork`:** Electrons eigene Empfehlung. `utilityProcess` kann einen `MessagePort` direkt zum Renderer aufbauen — der Fortschritt eines Rips geht damit ohne Umweg über den Main-Prozess ins Fenster. **Was es NICHT gibt:** keinen HTTP-Server, keinen Port, kein `localhost:7788`, keine REST-API, kein SSE. Fenster und Kern reden über Electrons IPC. Ein lokaler Webserver in einer Einzelplatz-Anwendung ist eine offene Tür ohne Gegenwert — und er war der Grund für das Rate-Limit, den Proxy-Kummer und die Polling-Last im alten Rippy. ### 4.2 Ordner ``` rippy-windows/ ├── package.json ├── electron.vite.config.ts ├── src/ │ ├── haupt/ Electron Main │ │ ├── index.ts Start, Einzelinstanz-Sperre, Leine setzen │ │ ├── fenster.ts Fenster anlegen, Zustand merken │ │ ├── tray.ts Symbol + Kontextmenü │ │ ├── autostart.ts HKCU\...\Run │ │ └── update.ts electron-updater │ │ │ ├── kern/ utilityProcess — die Arbeit │ │ ├── laufwerk/ │ │ │ ├── win32.ts DER EINZIGE ORT MIT koffi │ │ │ ├── wache.ts Einwurf/Auswurf erkennen │ │ │ └── disc.ts Typ, Label, Größe, TOC │ │ ├── metadaten/ │ │ │ ├── tmdb.ts tvdb.ts omdb.ts musicbrainz.ts │ │ │ ├── discmerkmale.ts Label + Laufzeiten + BDMV-Titel │ │ │ └── zuordnung.ts Treffer + Sicherheitsgrad │ │ ├── rip/ │ │ │ ├── makemkv.ts Kommandobau + Aufruf │ │ │ ├── parser.ts TINFO/SINFO/PRGV/MSG (rein, testbar) │ │ │ ├── audiocd.ts Roh-Sektoren + FLAC │ │ │ └── iso.ts Datendiscs 1:1 sichern │ │ ├── komprimieren/ │ │ │ ├── handbrake.ts Kommandobau + Aufruf + Fortschritt │ │ │ ├── encoder.ts GEMESSEN, nicht behauptet │ │ │ └── presets.ts │ │ ├── ablage/ │ │ │ ├── struktur.ts Jellyfin/Emby/Kodi/Plex │ │ │ ├── nfo.ts poster.ts medienserver.ts │ │ ├── ablauf/ │ │ │ ├── pipeline.ts die Verkettung — NUR HIER │ │ │ ├── zustand.ts Zustandsmaschine │ │ │ └── auftraege.ts Warteschlange, Slots │ │ ├── werkzeuge/ │ │ │ ├── katalog.ts wo liegt makemkvcon/HandBrakeCLI/flac │ │ │ ├── beschaffen.ts holen, prüfen, einrichten │ │ │ └── leine.ts Job Object (§ 3.3) │ │ └── speicher/ │ │ ├── db.ts SQLite │ │ ├── einstellungen.ts EINE Quelle (§ 6.7) │ │ └── bibliothek.ts │ │ │ ├── gemeinsam/ Typen + Ereignis-Schema — Fenster UND Kern │ └── fenster/ React │ ├── bau/ electron-builder, NSIS-Skript, Icon └── test/ Vitest ``` ### 4.3 Sechs Regeln, die den Aufbau tragen Jede ist aus einem bezahlten Fehler abgeleitet. Sie sind mechanisch prüfbar und bekommen je einen Wächter-Test. **R1 — Win32 lebt nur in `laufwerk/win32.ts`.** Kein `koffi`-Import irgendwo sonst. Ein Wächter-Test greift bei jedem anderen Fundort. *(Grund: In v1 rief `main.py` `eject()` direkt auf — deshalb brauchte der API-Container Geräte-Zugriff und `SYS_ADMIN`.)* **R2 — Ein Fehlschlag löscht nie einen Zustand.** Kein `catch { return [] }`. Wer nichts Neues weiß, behält, was er wusste. *(Grund: Fünfmal `catch(() => [])` im alten UI — jeder verpasste Abruf hieß „es gibt keine Jobs", die Liste leerte sich im Sekundentakt. Der Commander meldete es als „wird oft neu geladen".)* **R3 — Auswerfen heißt: entriegeln, auswerfen, nachsehen.** In dieser Reihenfolge. Kein Rückgabewert gilt als Beweis. *(Grund: `CDROMEJECT` quittiert auf einem verriegelten Laufwerk Erfolg und tut nichts. MakeMKV verriegelt die Tür während des Rips.)* **R4 — Jede Hintergrundschleife meldet ihren Fehler.** Kein stilles `catch {}`. *(Grund: Ein `except Exception: pass` in einer Vorrats-Schleife hat einmal eine Stunde gekostet.)* **R5 — Externe Schnittstellen werden gemessen, nicht erinnert.** Jeder Kommandozeilen-Schalter, jeder IOCTL-Code trägt im Kommentar, wo er herkommt. *(Grund: `--audio-codec` gibt es bei HandBrakeCLI nicht — es heißt `-E`/`--aencoder`. Ein unbekannter Schalter ist für HandBrake kein Fehler: Rückgabewert 0. Das kostete einen fertigen 16,5-GB-Rip, und ein Test hatte den Fehler festgeschrieben statt ihn zu finden.)* **R6 — Alles läuft an der Leine.** Jeder Werkzeugaufruf hängt in der Arbeitsgruppe aus § 3.3. Ausgenommen ist nur das Aufräum-Skript der Deinstallation — es muss Rippy überleben. --- ## 5. Der Ablauf — was passiert, wenn eine Disc reingeht Der rote Faden des Commanders: **so viel Arbeit abnehmen wie möglich.** ``` 1. Disc rein │ WM_DEVICECHANGE / DBT_DEVICEARRIVAL (Ereignis, kein Poll) │ Rückfallebene: 3-Sekunden-Takt, falls die Nachricht ausbleibt ▼ 2. Was ist das? │ Größe (33,76 GB → Blu-ray) · Audio-TOC? · Dateisystem lesbar? │ → Film / Serie / Musik / Daten ▼ 3. Was ist da drauf? ← DER SCHRITT, DER HEUTE ZU DÜNN IST │ Disc-Label · Titel-Laufzeiten · BDMV-Titel · DVD-IFO · CD-DiscID │ → TMDb / TVDb / OMDb / MusicBrainz │ → Treffer mit SICHERHEITSGRAD ▼ 4. Sicher genug? │ ja → sofort losrippen, nur Bescheid sagen (Zero-Click) │ nein → fragen: Fenster hoch, Vorschläge zur Wahl ▼ 5. Rippen makemkvcon, verlustfrei, Fortschritt aus PRGV ▼ 6. Komprimieren HandBrakeCLI, Encoder GEMESSEN, Preset je Disc-Typ ▼ 7. Ablegen Zielstruktur · NFO · Poster/Fanart ▼ 8. Melden Medienserver anstoßen · Benachrichtigung · Auswerfen ▼ 9. Merken Bibliothek fortschreiben — beim nächsten Mal erkannt ``` **Was an Schritt 3 neu ist.** Heute nimmt Rippy im Wesentlichen das Disc-Label und die längste Laufzeit. Das reicht bei „PIRATES_OF_THE_CARIBBEAN", aber nicht bei „LOGICAL_VOLUME_ID" — und dann fragt Rippy, obwohl die Antwort auf der Disc steht. Vier Quellen kommen dazu: | Quelle | Wo | Was sie liefert | |--------|-----|-----------------| | **BDMV-Metadaten** | `BDMV/META/DL/bdmt_*.xml` auf der Blu-ray | Der vom Studio hinterlegte Disc-Titel, oft mit Sprachvarianten. **Deutlich besser als das Volume-Label.** | | **Titel-Struktur** | makemkvcon `info` | Die Anzahl und Länge der Titel ist ein Fingerabdruck: 1 langer + viele kurze = Film; 6–13 gleich lange = Serienstaffel. | | **DVD-Struktur** | `VIDEO_TS`, IFO-Dateien | Kapitelzahl und Titelsatz-Aufbau | | **CD-DiscID** | Audio-TOC | Bereits gebaut und bewiesen (rc8): Spurlage → MusicBrainz → Album, Interpret, Jahr, jeder Titel. | Dazu ein **Sicherheitsgrad**, der ehrlich ist. Rippy sagt nicht „das ist Akira", sondern „das ist sehr wahrscheinlich Akira (1988)" — und fragt nur, wenn es das nicht sagen kann. Die Schwelle ist einstellbar; wer will, lässt Rippy immer fragen, und wer will, lässt es immer durchlaufen. **Und wenn Rippy sich irrt:** Der Titel lässt sich nachträglich ändern, und die Ablage wandert mit. Das ist der Unterschied zwischen „falsch geraten" und „falsch abgelegt". --- ## 6. Die Features ### 6.1 Automatische Erkennung (§ 5, Schritt 2–4) Siehe oben. Kernsatz: **Rippy fragt nur, wenn es wirklich nicht weiß.** ### 6.2 Rippen `makemkvcon` im Robot-Modus (`--robot --messages=-stdout --progress=-same`), verlustfrei. Der Parser (TINFO/SINFO/PRGV/MSG) wandert aus dem Python-Code nach TypeScript — er ist reine Textverarbeitung und damit vollständig testbar, ohne Laufwerk. Was aus dem alten Rippy zwingend mitkommt: - **Lesefehler heißen Lesefehler.** MakeMKV kann 1 von 2 Titeln sichern und sich trotzdem mit 0 beenden. Rippy warnt nach dem Rip — auch und gerade dann, wenn er als Erfolg endete — und nennt die **MSG-Nummer**: MakeMKVs Texte sind übersetzt, die Nummern nicht. - **Erst nachsehen, dann rippen.** Liegt keine Disc drin, sagt Rippy das in einem Satz, statt zwei Minuten in makemkvcon zu laufen und mit Code 11 zu enden. Aber: Wenn das Nachsehen selbst scheitert, wird trotzdem gerippt — „ich weiß es nicht" darf nie zu „es geht nicht" werden. - **Die Ausgabe binär lesen.** makemkvcon mischt Kodierungen; ein `ü` im Pfad hat die Kompression einmal getötet (`UnicodeDecodeError` auf Byte 0x81). ### 6.3 Audio-CDs Der Weg aus rc8, bewiesen: Roh-Sektoren über Win32 lesen (eine Audio-CD hat kein Dateisystem — die `Track01.cda` sind 44-Byte-Platzhalter), dann durch `flac.exe`. Dazu MusicBrainz über die DiscID. Zwei Fallen, beide gemessen und beide dokumentiert: Die DiscID rechnet **mit** den 150 Frames Vorlauf, das Lesen **ohne**. Und die Leseadresse zählt in 2048er-Einheiten, obwohl ein Audio-Sektor 2352 Bytes hat. ### 6.4 ISO-Backup für Datendiscs *(neu)* Die einzige echte Lücke gegenüber ARM. Wird eine Disc weder als Film noch als Musik erkannt, wird sie 1:1 gesichert statt abgelehnt: sequenziell aus `\\.\G:` lesen, Länge aus `IOCTL_DISK_GET_LENGTH_INFO` (in § 3.2 gemessen: liefert exakte Bytes). Kein Fremdwerkzeug nötig. ### 6.5 Mehrere Laufwerke gleichzeitig *(neu)* Laufwerke sind eigenständige Objekte mit stabiler Kennung (Seriennummer, nicht Buchstabe — `0025114C0149` in § 3.2 gemessen). Ein Rip-Auftrag belegt genau ein Laufwerk; die Zahl gleichzeitiger Rips und Kompressionen ist einstellbar. > ⚠️ **Annahme, zu messen bevor sie festgeschrieben wird:** Mehrere > `makemkvcon`-Prozesse teilen sich ein Datenverzeichnis > (`_private_data.tar`, `settings.conf`). Der Entwurf sieht deshalb vor: > **Rip-Phase parallel, Schlüssel-Phase serialisiert.** Das ist aus der > Aktenlage abgeleitet, nicht gemessen — und gehört an zwei Laufwerken geprüft, > bevor die Grenze im Code steht. ### 6.6 Bibliothek *(neu)* Eine Ansicht über alles Gerippte: Cover, Größe, Datum, Ablageort, Dauer des Vorgangs. Und der praktische Teil: **Rippy erkennt beim Einlegen, wenn eine Disc schon einmal durchgelaufen ist** (über die Disc-Merkmale aus § 5) und fragt nach, statt stumm zu doppeln. ### 6.7 Arbeitsverzeichnis und Ablage — eine Quelle Der Fund aus rc11: Die Oberfläche schrieb in die Datenbank, der Betrieb las aus einer Konfigurationsdatei, die niemand schrieb. Eingestellt `E:\Rippy`, angezeigt `C:\Users\...\Videos\Rippy`. In v5 gibt es **genau einen Speicher für Einstellungen** (`speicher/einstellungen.ts`, SQLite). Ein Wächter-Test prüft mechanisch, dass kein zweiter Ort entsteht. Dazu die Pfad-Regeln aus rc11, als reine Funktionen mit Tests: - Eine Laufwerkswurzel behält ihren Trenner (`F:\`, nicht `F:`) - UNC-Wurzeln bleiben absolut - Verzeichnisprüfungen sind **windows-nativ** — kein `timeout ls -d` - Pro Rip darf ein anderes Arbeitsverzeichnis gewählt werden, und **gesucht wird später unter der Wahl des Jobs**, nicht unter der heutigen Einstellung ### 6.8 Selbst-Update *(neu)* `electron-updater` gegen die Releases im Gitea (Entscheid 6). Rippy prüft beim Start und danach täglich, lädt im Hintergrund und installiert beim nächsten Start — **nie mitten in einem Rip**. Die Quelle steht in der Konfiguration und ist vorbelegt. Ein nicht erreichbarer Update-Server ist kein Fehler, sondern eine Zeile im Protokoll. **Vier Regeln, die zum Entscheid gehören:** 1. **Nur HTTPS.** Ohne Code-Signing (Entscheid 5) kann `electron-updater` ein heruntergeladenes Update nicht auf Echtheit prüfen — die Absicherung liegt damit ganz beim Transportweg. Eine `http://`-Update-Adresse wird abgelehnt, nicht stillschweigend akzeptiert. 2. **Nie während eines Rips.** Weder herunterladen noch installieren. Ein Neustart mitten in einem 90-Minuten-Rip wäre der teuerste Fehler, den ein Update-Mechanismus machen kann. 3. **Ein Fehlschlag ist sichtbar, aber nicht laut.** Die Prüfung darf still scheitern (WLAN weg, Server aus) — aber der Zeitpunkt der letzten erfolgreichen Prüfung steht im Über-Dialog. Wer nie prüft, weiß sonst nicht, dass er nie prüft. 4. **Die Version, die läuft, ist ablesbar.** Im Fenster und im Protokollkopf. Eine Fehlermeldung ohne Versionsnummer ist bei einem sich selbst aktualisierenden Programm die halbe Miete verloren. ### 6.9 Erst-Einrichtung *(neu gedacht)* Der Befund des Commanders zur heutigen Fassung: *„Dann hätte ich beim Setup auch erwartet, dass ein echtes Setup passiert — wo will ich das hinspeichern, ein Pre-Requirement-Check usw. Da kommt gar nichts, Rippy geht einfach auf."* Der Assistent läuft **nach** der Installation, im Programm selbst, in fünf Schritten: | Schritt | Was passiert | |---------|--------------| | 1 Willkommen | Was Rippy tut, in vier Sätzen | | 2 Prüfung | Laufwerke · Werkzeuge · Plattenplatz · Internet · WebView2 — **jeder Befund sagt, was zu tun ist** | | 3 Werkzeuge | Was fehlt, wird geholt (§ 9) — mit Fortschritt, nicht mit einem eingefrorenen Fenster | | 4 Ordner | Ablage und Arbeitsverzeichnis, mit echten Windows-Ordnerdialogen und Platzanzeige | | 5 Automatik | Wie viel soll Rippy allein entscheiden? Je Disc-Typ einstellbar. | Zwei Regeln, die aus dem alten Setup übernommen werden: - **Nur ein Fehler blockiert.** Eine Warnung, die den Knopf sperrt, ist Bevormundung; ein Fehler, der nur warnt, ist eine Falle. Kein optisches Laufwerk ist eine **Warnung** — eine reine Komprimier-Maschine ist ein vorgesehener Betriebsfall. - **Ein Setup meldet nie Erfolg, während ein Pflichtwerkzeug fehlt.** Der Bestand wird VOR und NACH dem Versuch gelesen; berichtet wird, was danach wirklich da ist. ### 6.10 Was unverändert erhalten bleibt Alles, was der Commander heute benutzt: Kompression je Disc-Typ abwählbar (4K verlustfrei durchreichen) · Preset-Wahl je Typ · Sprachwahl für Ton und Untertitel vor dem Rip · Original behalten · Vollautomatik · Nur-Hauptfeature · automatischer Auswurf · Medienserver-Refresh (Jellyfin/Emby/Plex/Kodi) · NFO + Poster + Fanart · Serien-Staffelablage mit Episoden-Zuordnung per Laufzeitabgleich (**nur bei eindeutiger Zuordnung wird umbenannt**) · Webhook-Benachrichtigungen (Discord/Slack/ntfy/generisch) · Live-Log · Job-Detailansicht · Wiederholen ab Rip oder ab Kompression · 4K-Schlüsselkette in drei Stufen · Restzeit-Schätzung. --- ## 7. Die Oberfläche **Entscheid:** Neu bauen, Look behalten. Wiedererkennbar bleibt die Design-Sprache — dunkel/hell, die Kachel-Anordnung, das Kino-Banner, die Farbwelt. Neu ist, dass die Oberfläche eine **Einzelplatz-Anwendung** bedient statt eines Docker-Clusters. **Was verschwindet:** Worker-Verwaltung · Container-Platte · „Prüfen: `docker compose ps`" · Speicherziele-Einhängen · Pfad-Map · Worker-Setup-Paket · Encoder-Auswahl je Knoten · alles, was einen zweiten Rechner voraussetzt. **Was dazukommt:** - Echte **Windows-Ordnerdialoge** statt eines nachgebauten Dateibrowsers im Web. Das ist ein spürbarer Unterschied im Alltag: Der Nutzer sieht seine Netzlaufwerke, seine Schnellzugriffe, seine gewohnte Oberfläche. - **Tray-Symbol mit Zustand** — auf einen Blick: läuft ein Rip, wie weit, wie lange noch. Rechtsklick: Öffnen, Pause, Beenden. - **Windows-Benachrichtigungen** (echte Toasts, nicht Browser-Popups), mit Klick direkt in den Job. - **Fortschritt in der Taskleiste** — Windows kann einen Fortschrittsbalken über dem Symbol zeigen. Bei einem 90-Minuten-Rip ist das genau die Information, die man will, ohne das Fenster zu öffnen. - **Die Bibliothek** als eigene Ansicht (§ 6.6). **Was die Oberfläche NIE tut:** Eine Liste auf leer setzen, weil eine Nachricht ausblieb (R2). Bei einem Aussetzer bleibt der letzte Stand stehen, und ein Banner sagt, dass gerade nichts Neues kommt. --- ## 8. Setup, Autostart, Deinstallation **Entscheid: pro Benutzer, ohne Administratorrechte, Windows 10 (ab 1809) und 11, x64.** | | | |---|---| | Installer | `electron-builder` → NSIS, ein `RippySetup.exe` | | Ziel | `%LOCALAPPDATA%\Programs\Rippy` — kein UAC-Dialog | | Autostart | `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` | | Update | `electron-updater`, NSIS-Ziel (§ 6.8) | | Deinstallation | Über die Windows-Programmliste, räumt **wirklich** auf | **Warum kein Windows-Dienst:** Ein Dienst startet vor der Anmeldung — klingt besser, kostet aber bei jeder Installation und jedem Update eine Administrator-Abfrage, und er kann kein Tray-Symbol zeigen (keine Benutzersitzung). Für einen Rechner, an dem jemand sitzt und Discs einlegt, ist der Autostart-Eintrag der richtige Weg. **Drei Dinge, die aus den alten Fehlern übernommen werden:** 1. **Drüber-Installieren funktioniert.** Rippy läuft fast immer (Autostart) — der Installer beendet die laufende Fassung, bevor er kopiert. Sonst landet die neue Version als `Rippy.exe.neu` daneben. *(Genau das ist passiert.)* 2. **Die Deinstallation räumt die WebView-Reste weg.** Beim letzten Mal blieben **148 Dateien** liegen, weil die Renderer-Hilfsprozesse den Zwischenspeicher offen hielten — und der Deinstallierer meldete trotzdem „Rippy wurde entfernt." 3. **Das Aufräum-Skript darf keine Laufwerkswurzel löschen.** Wenn der Installationsort aus der Registry eine Wurzel wäre, löschte die Deinstallation das Laufwerk. Nicht beobachtet — aber nicht wiedergutzumachen. > **SmartScreen — entschieden (Entscheid 5): kein Zertifikat.** Die unsignierte > EXE bekommt beim ersten Start eine Warnung („Der Computer wurde geschützt"); > über **Weitere Informationen → Trotzdem ausführen** geht es weiter. Das > gehört in die Anleitung, nicht unter den Teppich — und die Anleitung sagt > auch, woran man erkennt, dass die Datei aus der richtigen Quelle stammt > (Größe und Prüfsumme stehen beim Release). > > Die Folge für den Update-Weg steht in § 6.8: Ohne Signatur ist der > Transportweg die einzige Absicherung, also **nur HTTPS**. --- ## 9. Die Werkzeuge Der Entscheid vom 28.08.2026 gilt unverändert: *„handbrake und makemkv MÜSSEN mitgeliefert werden oder während des Setups separat installiert werden."* Die Wege unterscheiden sich, und der Grund ist die **Lizenz**, nicht die Bequemlichkeit. | | Weg | Warum | |---|---|---| | **HandBrakeCLI** | **mitgeliefert** (~35 MB) | GPL-2 erlaubt die Weitergabe, solange Lizenztext und Quellverweis dabei sind. Damit komprimiert Rippy auch ohne Internet. | | **FLAC** | **mitgeliefert** (~2 MB) | Xiph-Lizenz (BSD-artig), Weitergabe erlaubt. ⚠️ `flac.exe` braucht `libFLAC.dll` daneben — mit nur der EXE endet jeder Aufruf ohne eine Zeile Ausgabe. | | **MakeMKV** | **beim Einrichten geholt** | Proprietär — die Lizenz erlaubt Dritten keine Weitergabe. Rippy lädt die offizielle Datei vom Hersteller und startet dessen Installer. | **Die Bezugskette für MakeMKV** (aus dem 28.08.-Entscheid, unverändert): eigene Quelle → Hersteller-Seite → Hersteller-Forum → Internet Archive. Mit zwei Feinheiten, beide gemessen: **Die höchste Versionsnummer gewinnt, nicht die erste** (das Archiv meldet sonst 1.18.2 statt 1.18.4), und **jeder Versuch bekommt eine knappe Zeitgrenze** (8 s), weil das Forum in zwei von drei Abrufen ausfiel. MakeMKV signiert seinen Installer nicht (`NotSigned`, an der echten Datei gemessen) — geprüft wird deshalb die Versions-Ressource (`CompanyName: GuinpinSoft inc`). Das ersetzt keine Signatur, fängt aber ab, was hier wirklich droht: eine Fehlerseite mit `.exe`-Namen, ein abgebrochener Download, eine falsche Fassung. ### 9.1 Der Beta-Key — ein Termin, kein Detail > **MakeMKVs freier Beta-Key ist bis Ende September 2026 gültig. Die Kaufseite > für die Dauerlizenz ist seit Mai 2026 defekt.** Das trifft Rippy in etwa vier Wochen, und ohne gültigen Key geht **keine Blu-ray und keine 4K-Disc** mehr auf (DVDs laufen weiter). Rippy v5 geht damit so um: 1. **Der Key wird automatisch geholt** (wie heute) — aus dem Forum-Thread, in dem der Hersteller ihn veröffentlicht. 2. **Rippy zeigt das Ablaufdatum im Dashboard** und warnt **sieben Tage vorher** — statt dass ein Rip mitten in der Nacht scheitert und niemand weiß, warum. 3. **Ein abgelaufener Key ist eine klare Meldung**, kein rätselhafter MakeMKV-Fehlercode. 4. **Ein eingetragener Dauerlizenz-Schlüssel schlägt alles.** Wer eine Lizenz hat, trägt sie ein und ist raus aus dem Thema. --- ## 10. Was aus dem alten Rippy mitkommt **Kein Code. Aber jede teuer bezahlte Erkenntnis.** Das ist der Kern des Grüne-Wiese-Ansatzes: Der Python-Code wird nicht portiert, aber er ist die beste verfügbare Dokumentation darüber, wie sich MakeMKV, HandBrake und Windows-Laufwerke wirklich verhalten. Diese Tabelle ist die Arbeitsliste für den Neubau. | Herkunft | Was übernommen wird | Wohin | |----------|--------------------|-------| | `ripping.py` | TINFO/SINFO/PRGV/MSG-Parser, Kommandobau, `laengster_titel`, `episoden_titel`, Sprach-Zusammenfassung | `rip/parser.ts`, `rip/makemkv.ts` | | `win_ioctl.py` | Die hergeleiteten Steuercodes samt Herleitung — **in § 3.2 aus Node bestätigt** | `laufwerk/win32.ts` | | `windows.py`, `winlauf.py` | Zugriffsgründe je Fehlernummer, `CREATE_NO_WINDOW`, Standby verhindern, die Prozess-Leine | `laufwerk/`, `werkzeuge/leine.ts` | | `caps.py` | Encoder **messen** statt behaupten (`HandBrakeCLI --help` auswerten) | `komprimieren/encoder.ts` | | `handbrake_aufruf.py` | `--aencoder` (nicht `--audio-codec`), Container aus dem Preset erzwingen, letzte 12 Zeilen puffern, `unknown option`-Erkennung | `komprimieren/handbrake.ts` | | `medien.py` | Zielstruktur, NFO-Schema, Episoden-Zuordnung per Laufzeit, Medienserver-Refresh | `ablage/` | | `prescan.py`, `clients/*` | TMDb/TVDb/OMDb/MusicBrainz/Jikan-Abfragen, Zwischenspeicher, Confidence | `metadaten/` | | `pfade.py` | Laufwerkswurzeln, UNC, `naechster_vorhandener` **ohne Endlosschleife** | `speicher/pfade.ts` | | `musicbrainz_cd.py`, `audio_cd.py` | DiscID-Rechnung (mit/ohne 150 Frames), 2048-vs-2352-Falle | `rip/audiocd.ts` | | `presets.py`, `eta.py`, `phasen.py` | Preset-Zuordnung je Disc-Typ, Restzeit, Phasenmodell | `komprimieren/`, `ablauf/` | | `beschaffen.py`, `katalog.py` | Suchreihenfolge für Werkzeuge, Registry-Auswertung, Ausweichquellen | `werkzeuge/` | | `AGENTS.md` | Die sechs Regeln aus § 4.3 | Wächter-Tests | **Die Tests wandern sinngemäß mit.** Von den 977 heutigen Tests ist der größte Teil reine Textverarbeitung (Parser, Pfade, Kommandobau) — die Testfälle gelten unverändert, nur die Sprache wechselt. Das ist kein Nebeneffekt, sondern die Absicherung: Ein neu geschriebener Parser, der dieselben Testfälle besteht wie der alte, hat dieselben Fallen abgedeckt. --- ## 11. Was aus `main` entfernt wird **Entscheid 7.** Die Arbeit gehört in den `main`-Branch, nicht hierher — dieser Abschnitt ist die Arbeitsliste dafür, damit sie nicht aus dem Gedächtnis gemacht wird. ### 11.1 Der Windows-Anteil in `main` sind DREI Dinge, nicht eins Diese Unterscheidung ist der ganze Abschnitt. Wer sie überspringt, löscht entweder zu wenig oder reißt die Docker-Version mit. **Zuerst in Klartext, ohne Dateinamen:** > **A ist Rippy. B ist ein Handlanger für die VM. C sind Notizzettel, die > wegen A eingeklebt wurden.** **A — „Rippy" auf einem Windows-PC.** Das Programm, das man heute kennt: `RippySetup.exe`, Doppelklick, Symbol in der Taskleiste, ein Fenster geht auf. Disc rein, Rippy erkennt sie, rippt, komprimiert, legt ab. Ein vollständiges Rippy, das alles allein macht. **Genau das wird durch v5 ersetzt.** **B — „Rippy Worker" auf einem Hilfsrechner.** Ein anderes Programm, `RippyWorkerSetup.exe`. Es **rippt nichts** und hat keine Bedienoberfläche. Beim Installieren fragt es drei Dinge: *IP der Rippy-VM? Name dieses Helfers? Wie viele Kerne?* Danach wartet es. Will die **Docker-Rippy auf der VM** einen Film komprimieren und ist selbst zu langsam, schickt sie die Arbeit dorthin. ``` VM (Docker-Rippy) Ein Windows-PC ├─ Disc einlesen ├─ rippen └─ komprimieren? zu langsam ──────► B: „Rippy Worker" komprimiert und Ergebnis ◄───────────────────── schickt zurück ``` B läuft nur *zufällig* auf Windows — es gehört zur Docker-Welt. **C — Notizzettel im Docker-Code.** Keine eigenen Dateien, sondern verstreute Stellen *mitten in* Dateien, die Docker jeden Tag benutzt, wo jemand schrieb: „falls wir gerade auf Windows laufen, mach es anders". Eingebaut wurden sie, damit **A** funktioniert. **Der Unterschied zwischen A und C ist nicht der Inhalt, sondern der Ort:** | | Bild | Aufräumen heißt | |---|------|-----------------| | **A** | Ganze Ordner, auf denen „Windows" steht. Docker schlägt sie nie auf. | **Ordner wegwerfen.** Ganze Dateien löschen, fertig. | | **C** | Einzelne Seiten mit Windows-Notizen, die in Ordnern liegen, die Docker täglich benutzt. | **Seiten einzeln durchgehen.** Die Datei bleibt, ein paar Zeilen gehen — und bei jeder Zeile ist zu prüfen, ob wirklich nur A sie brauchte. | Deshalb ist C der heikle Teil: Erwischt man eine Zeile, die Docker doch braucht, geht auf der VM etwas kaputt. Und manche Windows-Zeile braucht sogar **B** weiter — etwa die Regel, dass eine Laufwerkswurzel `F:\` mit Trenner geschrieben werden muss (§ 1). Der Handlanger läuft ja auf Windows. **In Dateien ausgedrückt:** | | Was es ist | Schicksal | |---|---|---| | **A** | **Die Standalone-App** — PyInstaller-Setup, Tray, WebView2-Fenster, Einrichtungs-Assistent, lokale Queue, Werkzeug-Beschaffung, Win32-Treiber | **weg** — v5 ersetzt sie | | **B** | **Der Remote-Encode-Worker** — `deploy/worker-windows/`, `/worker-setup/*`, Pfad-Map | **bleibt** | | **C** | **Windows-Zweige im Docker-Code** — die `os.name == "nt"`-Stellen in `main.py`, `betrieb.py`, `ablauf.py`, `rohdaten.py`, dazu die Fähigkeiten-Auskunft im UI | **einzeln prüfen** | > **Entscheid 8 (30.08.2026) — B bleibt bei Docker und kommt nicht nach v5.** > > Commander: *„Das soll NICHT Teil des neuen Standalone-Produktes sein, da > dieser NUR für die Docker-Variante verwendet wird."* > > Damit ist bestätigt, was zuvor nur Auslegung war: Der Remote-Worker ist kein > Standalone-Rippy, sondern eine **Funktion der Docker-Installation**. Er wird > weder abgeräumt (er ist Docker, und Docker bleibt unangetastet) noch nach v5 > übernommen (v5 ist Einzelplatz, Entscheid 3 — es gibt dort keinen zweiten > Knoten, dem man etwas schicken könnte). ### 11.2 Teil A — die Standalone-App (weg) ``` src/rippy/windows_app.py Installer, Dienst, Deinstallation src/rippy/fenster.py WebView2-Fenster src/rippy/setup_fenster.py Einrichtungs-Assistent src/rippy/einrichtung.py Voraussetzungs-Prüfungen src/rippy/standalone.py lokale Zustellung statt Celery src/rippy/drives/windows.py Win32-Laufwerkstreiber src/rippy/drives/win_ioctl.py die Steuercodes src/rippy/platform/winlauf.py CREATE_NO_WINDOW, Waisen, Job Object src/rippy/platform/win_registry.py src/rippy/platform/verknuepfungen.py src/rippy/platform/dateiangaben.py src/rippy/tools/ Werkzeug-Katalog + Beschaffung (Windows-only) src/rippy/rip/audio_cd.py Audio-CD über Win32 src/rippy/rip/musicbrainz_cd.py DiscID-Rechnung packaging/windows/ der PyInstaller-Bau dist/windows/ die gebaute EXE + vendor/ + die zugehörigen test_*.py ``` Dazu: `src/rippy/queue/lokal.py` und `queue/laeufer.py` (die lokale Queue hat außerhalb der Standalone-App keinen Aufrufer) und `bus/waechter.py`, soweit er nur die Windows-Disc-Wache bedient. **Die Steuercodes und Parser sterben nicht — sie sind bereits nach v5 übernommen** (§ 3.2 hat sie aus Node bestätigt, § 10 listet den Rest). Was gelöscht wird, ist die Python-Fassung, nicht das Wissen. ### 11.3 Teil C — die verstreuten Zweige (einzeln prüfen) Hier ist blindes Löschen gefährlich, weil manches auch **ohne** die Standalone-App gebraucht wird — vom Remote-Worker (Teil B) oder von der Pfad-Behandlung allgemein. | Fundort | Prüfen | |---------|--------| | `docker/api/main.py` — `betrieb.windows_laufwerke()`, die Browse-Sonderfälle, `/betrieb`-Fähigkeiten | Die Fähigkeiten-Auskunft bleibt sinnvoll (Docker-AiO vs. verteilt), nur die Windows-Werte fallen weg | | `src/rippy/betrieb.py` | Ganz raus oder auf Docker-Fälle eindampfen | | `docker/worker/ablauf.py`, `rohdaten.py` | Die Windows-Pfadzweige raus — aber `nativ_nachsehen()` prüfen: gilt es auch für den Remote-Worker? | | `docker/api/rohdaten.py`, `mounts.py` | dito | | `src/rippy/pfade.py` | **Bleibt.** Laufwerkswurzeln und UNC braucht auch der Remote-Worker | | UI: `useBetrieb`, `WorkerVerwaltung`, `Anleitung`, `Settings` | Nur die Standalone-Texte raus; die Worker-Verwaltung bedient Teil B | | `src/rippy/test_keine_container_reste.py` | Der Wächter-Test wird gegenstandslos — mit weg | ### 11.4 Wie das gemacht wird 1. **Ein eigener Branch**, nicht direkt auf `main`. Der Umfang ist zu groß für einen Freihand-Commit. 2. **In der Reihenfolge A → C → (B bleibt).** Erst die eindeutigen Dateien, dann die verstreuten Zweige — sonst sucht man Aufrufer, die es noch gibt. 3. **Die Ampel nach jedem Schritt.** `AGENTS.md` Regel A gilt unverändert; ein Aufräumen, das die Tests rot macht, ist nicht fertig, sondern angefangen. 4. **Erst wenn v5 die Etappe W-6 erreicht hat.** Solange v5 nicht installierbar ist, ist die alte Fassung das einzige Windows-Rippy, das es gibt. Sie zu löschen, bevor der Ersatz steht, wäre eine Lücke ohne Gegenwert — der Entscheid nennt kein Datum, nur die Richtung. 5. **Ein SAVEPOINT-Eintrag** dazu, mit der Zahl der entfernten Zeilen und dem Hinweis, wo das Wissen jetzt liegt. --- ## 12. Etappen **Grundregel:** Jede Etappe endet mit grüner Ampel und einem Programm, das man starten kann. Kein großer Umbau, nach dem monatelang nichts geht. | # | Titel | Inhalt | Fertig, wenn | |---|-------|--------|--------------| | **W-0** | **Gerüst** | Electron 44 + TypeScript + Vite + Vitest, drei Prozesse, IPC, `node:sqlite` in Electron **prüfen** (§ 3.4), Prozess-Leine, CI-Ampel | Ein Fenster geht auf, der Kern läuft, ein Testlauf ist grün | | **W-1** | **Laufwerk** | `laufwerk/win32.ts` aus dem § 3.2-Beweis, Wache über `WM_DEVICECHANGE`, Disc-Typ, Verriegeln, Auswerfen mit Nachsehen | Disc rein → Rippy sagt korrekt, was es ist. Auswerfen wirkt (nachgemessen, nicht geglaubt) | | **W-2** | **Rippen** | makemkvcon-Aufruf, Parser, Fortschritt, Abbruch, Lesefehler-Warnung, Werkzeug-Beschaffung | Eine DVD und eine Blu-ray laufen durch, verlustfrei | | **W-3** | **Komprimieren + Ablegen** | HandBrake, Encoder gemessen, Presets, Zielstruktur, NFO, Poster, Medienserver | Ein Film liegt fertig in Jellyfin und ist dort sichtbar | | **W-4** | **Metadaten-Automatik** | Die vier Quellen aus § 5, Sicherheitsgrad, Nachkorrektur | Fünf verschiedene Discs richtig erkannt, ohne Nachfrage | | **W-5** | **Oberfläche** | Dashboard, Einstellungen, Logs, Bibliothek, Tray, Windows-Toasts, Taskleisten-Fortschritt | Der Commander bedient Rippy einen Abend lang ohne Rückfrage | | **W-6** | **Setup + Update** | NSIS, Autostart, Erst-Einrichtung, Selbst-Update, Deinstallation | Frischer Rechner: Setup → Disc rein → MKV raus. Und: drüber-installieren geht | | **W-7** | **Der Rest** | Audio-CD, ISO-Backup, mehrere Laufwerke, 4K-Schlüsselkette | Je Feature ein Nachweis | Reihenfolge-Begründung: W-1 bis W-3 sind die Kette, ohne die Rippy nichts tut. W-4 und W-5 machen sie bequem. W-6 macht sie weitergebbar. W-7 sind die Ergänzungen, die ohne die Kette keinen Sinn hätten. --- ## 13. Risiken und offene Punkte | Risiko | Bewertung | Behandlung | |--------|-----------|------------| | **MakeMKV-Beta-Key läuft Ende September 2026 ab** | hoch × sicher | § 9.1 — Ablaufdatum sichtbar, Warnung 7 Tage vorher, klare Meldung, Dauerlizenz eintragbar. **Das ist der akuteste Punkt im ganzen Dokument.** | | `node:sqlite` in Electron ungeprüft | mittel × unbekannt | Erster Handgriff in W-0. Rückfallebene `better-sqlite3` | | Neubau dauert länger als eine Reparatur | sicher | Deshalb Entscheid 2: Die Docker-Version läuft weiter. Es entsteht keine Lücke | | Electron-Paket ~150–200 MB | sicher, gering | Ein einmaliger Download. Der Gegenwert ist eine Werkzeugkette statt zweier | | SmartScreen bei unsignierter EXE | mittel | Entscheid 5: kein Zertifikat. Anleitung + Prüfsumme beim Release | | **Update ohne Signaturprüfung** | mittel × Folge von Entscheid 5 | § 6.8 — nur HTTPS, `http://` wird abgelehnt. Der Transportweg ist die einzige Absicherung, deshalb darf er nicht optional sein | | **Öffentliche Update-Quelle für Externe** | offen × Netz-Frage | Entscheid 6 — Gitea liegt im LAN. Rippy ist für alle drei Wege gebaut (Adresse in der Konfiguration); die Wahl ist eine Infrastruktur-Entscheidung, keine Programmänderung | | **Aufräumen von `main` reißt Docker mit** | mittel × vermeidbar | § 11 — drei Teile sauber getrennt, Remote-Worker bleibt, Ampel nach jedem Schritt, und erst **nach** v5 W-6 | | koffi + Electron + npm-Install-Sperre | niedrig | § 3.5 gemessen: koffi lädt mit vorgebauten Binärdateien. In W-0 im Electron-Kontext gegenprüfen | | Parallel-Rip vs. MakeMKV-Datenverzeichnis | mittel | § 6.5 — **messen, bevor die Grenze festgeschrieben wird** | | Ein neu geschriebener Parser bringt neue Fehler | mittel | Die alten Testfälle wandern mit (§ 10). Ein Parser, der sie besteht, kennt dieselben Fallen | | 4K-UHD-Schlüssel | gelöst | Unter Windows holt makemkvcon sie selbst — der strukturelle Vorteil dieser Plattform, gemessen 25.07.2026 | ### Alle drei offenen Punkte sind entschieden (30.08.2026) Code-Signing: **nein** (Entscheid 5) · Update-Quelle: **Gitea, auch für Externe** (Entscheid 6) · alter Windows-Weg: **wird entfernt** (Entscheid 7) · der Remote-Worker bleibt bei Docker (Entscheid 8, § 11.1). Was daraus an Arbeit folgt, steht in § 11. --- ## 14. Was NICHT gebaut wird - **Kein Python.** Auch nicht „nur für den einen Parser". - **Kein HTTP-Server, kein Port, kein localhost.** Fenster und Kern reden über IPC. - **Keine Docker-Kompatibilität.** Kein geteiltes Modul, kein gemeinsames Schema, keine Container-Pfade. - **Kein Linux, kein macOS.** „100 % für Windows gebaut" heißt: Wo eine Windows-Lösung besser ist als eine portable, gewinnt die Windows-Lösung. - **Kein ARM-Fork.** Rippy bleibt ein Eigenbau. - **Keine Auth.** Einzelplatz, kein Netzdienst — es gibt nichts zu schützen. --- ## Anhang — Verhältnis zu den bestehenden Konzepten `KONZEPT.md` (v1) und `KONZEPT-V2.md` (v2) **bleiben gültig für die Docker-Version**. Dieses Dokument ersetzt sie nicht, es steht daneben. Drei Punkte aus v2 werden für Windows bewusst anders entschieden — jeweils mit Begründung: | v2 sagt | v5 macht | Warum | |---------|----------|-------| | Entscheid 4: *kein Electron, pywebview + WebView2* (8 MB statt 150 MB) | **Electron** | Die Rechnung war richtig und die Schlussfolgerung trotzdem falsch: Sie hat nur die Fenster-Frage betrachtet. Der Preis für pywebview war nicht 0 MB, sondern **eine zweite Werkzeugkette** — PyInstaller, hidden imports, `pythonnet`, und ein Kern, der aus Container-Code bestand. Electron kostet 150 MB und schenkt uns *eine* Sprache von der Oberfläche bis zum Laufwerk. | | Eine Anwendung, drei Verdrahtungen (Ports/Treiber) | **Ein Windows-Programm** | Die Ports-Architektur ist richtig für ein Produkt, das drei Betriebsarten tragen muss. Bei Entscheid 2 (Docker bleibt getrennt) trägt sie nichts mehr — sie wäre Abstraktion auf Vorrat. | | REST + SSE als Schnittstelle | **IPC** | Ein lokaler Webserver in einer Einzelplatz-Anwendung ist eine offene Tür ohne Gegenwert. | --- *Rippy v5 · Konzept · 30.08.2026 · Branch `worktree-windows-electron`*