# 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–13 | 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) Vier Fragen wurden gestellt, vier sind beantwortet. 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. --- ## 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. 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 mit dem Gitea des Commanders. Ein nicht erreichbarer Update-Server ist kein Fehler, sondern eine Zeile im Protokoll. ### 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.** Eine unsignierte EXE bekommt beim ersten Start eine > Warnung („Der Computer wurde geschützt"). Ein Code-Signing-Zertifikat kostet > etwa 200–400 € im Jahr. Für den Eigengebrauch ist die Warnung hinnehmbar; > das gehört in die Anleitung, nicht unter den Teppich. **Offener Punkt für den > Commander.** --- ## 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. 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. --- ## 12. 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 | § 8 — Anleitung, oder Zertifikat. **Entscheidung offen** | | 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 | ### Offene Punkte für den Commander 1. **Code-Signing-Zertifikat** — ja oder nein? (~200–400 €/Jahr, sonst SmartScreen-Warnung beim ersten Start) 2. **Update-Quelle** — Gitea-Releases auf `192.168.178.153`, oder ein Ordner im Netz? 3. **Der alte Windows-Weg** — bleibt `RippySetup.exe` (PyInstaller) parallel installierbar, bis v5 W-6 erreicht hat? *(Empfehlung: ja, und v5 bekommt einen eigenen Installationsordner, damit sich die beiden nicht überschreiben.)* --- ## 13. 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`*