Der doppelte Checkout (Worktree + Hauptordner) blockierte den Start neuer Sitzungen auf dem Branch. Der Branch selbst und main bleiben unveraendert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1132 lines
60 KiB
Markdown
1132 lines
60 KiB
Markdown
# 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, **zweite Fassung** · Branch `worktree-windows-electron`
|
||
|
||
**Was die zweite Fassung vom selben Tag unterscheidet:** Das Konzept wurde
|
||
geprüft, tagesaktuell gegenrecherchiert und nachgemessen. Vier Dinge haben
|
||
sich geändert: (1) Ein Faktenfehler ist korrigiert — das Gitea des Commanders
|
||
ist entgegen der ersten Fassung **öffentlich erreichbar**, die Update-Frage
|
||
löst sich damit fast von selbst (§ 3.6, Entscheid 6). (2) Die letzte
|
||
technische Unbekannte ist gemessen — `node:sqlite` funktioniert in Electron 44,
|
||
im echten Einsatzkontext (§ 3.4). (3) Die Disc-Erkennung ist vom Kopf auf die
|
||
Füße gestellt — der Prüf-Takt ist Plan A, das Windows-Ereignis die spätere
|
||
Verfeinerung (§ 5). (4) Drei Entscheide des Commanders sind eingearbeitet:
|
||
Update-Quelle, Zielgruppe, und der Zeitpunkt des Aufräumens (Entscheide 6, 7
|
||
und 9).
|
||
|
||
---
|
||
|
||
## 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`), am 30.08.2026 gegen das
|
||
Netz geprüft 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. Die erste
|
||
Fassung dieses Konzepts hat es selbst vorgemacht: Sie behauptete, Gitea sei
|
||
von außen nicht erreichbar — eine Messung von dreißig Sekunden zeigte das
|
||
Gegenteil (§ 3.6).
|
||
|
||
---
|
||
|
||
## 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 <pfad>` 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)
|
||
|
||
Neun Entscheide, alle am selben Tag — die Entscheide 6, 7 und 9 in ihrer
|
||
heutigen Form aus der Prüfung der ersten Fassung. 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
|
||
|
||
Der Branch `worktree-windows-electron` trägt v5. `main` bleibt unberührt und
|
||
jederzeit deploybar — deploybar heißt: Die VM zieht von Gitea, nicht von
|
||
diesem Ordner. Welcher Branch hier gerade ausgecheckt ist, ist für den Deploy
|
||
egal.
|
||
|
||
*Nachtrag 30.08.2026:* Anfangs lebte der Branch in einem Git-Worktree unter
|
||
`.claude/worktrees/windows-electron`, damit die Konzept-Arbeit `main` nicht
|
||
anfassen musste. Der Worktree ist aufgelöst — Git erlaubt denselben Branch
|
||
nur an einem Ort, und der doppelte Checkout blockierte den Start neuer
|
||
Sitzungen auf dem Branch. Seitdem wird v5 normal im Projektordner
|
||
ausgecheckt; der Branch-Name erinnert noch an die Herkunft.
|
||
|
||
### Entscheid 5 — Kein Code-Signing-Zertifikat
|
||
|
||
*„ist egal, brauchen wir nich"*
|
||
|
||
Die EXE bleibt unsigniert. Zwei Folgen, beide bewusst getragen:
|
||
|
||
1. **SmartScreen.** Beim ersten Start eines Downloads zeigt Windows den
|
||
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 — samt der Angabe, woran man die echte Datei erkennt
|
||
(Größe und Prüfsumme stehen beim Release).
|
||
2. **Updates ohne Echtheitsprüfung.** Ohne Signatur kann `electron-updater`
|
||
ein heruntergeladenes Update nicht auf Echtheit prüfen. Die Absicherung
|
||
liegt damit vollständig beim Transportweg — deshalb die HTTPS-Pflicht in
|
||
§ 6.8, Regel 1.
|
||
|
||
### Entscheid 6 — Updates aus dem öffentlichen Gitea
|
||
|
||
*„Gerne das Gitea Release, mit Möglichkeit dass auch EXTERNE diese Updates
|
||
fahren können"* — und in der zweiten Fassung konkretisiert: **das öffentliche
|
||
Gitea direkt, kein Spiegel.**
|
||
|
||
Die erste Fassung hielt das für ein Infrastruktur-Problem („Gitea ist von
|
||
außen nicht erreichbar") und entwarf drei Auswege. Die Messung in § 3.6 hat
|
||
das erledigt: **Gitea ist längst öffentlich erreichbar** —
|
||
`https://git.tobisniceshomelab.ddnsfree.com`, mit gültigem TLS-Zertifikat,
|
||
Lesen ohne Anmeldung. Die HTTPS-Pflicht aus § 6.8 ist damit erfüllbar, und
|
||
zwar für den Commander **und** für Externe, ohne dass irgendetwas neu
|
||
aufgebaut wird.
|
||
|
||
Was bleibt:
|
||
|
||
- Die Update-Adresse steht in der Konfiguration, vorbelegt mit den Releases
|
||
dieses Gitea. Wer einen eigenen Server hat, trägt seine eigene Adresse ein —
|
||
`electron-updater` braucht nur einen statischen HTTPS-Ort.
|
||
- Jedes Release besteht aus **drei Dateien**, nicht zwei: `latest.yml`
|
||
(die Update-Auskunft), das Setup und die `.blockmap` (macht aus einem
|
||
Voll-Download einen Differenz-Download).
|
||
- ⚠️ **Ein Handgriff für Etappe W-6:** Gitea-Release-Downloads hängen an
|
||
ihrem Tag (`/releases/download/v5.0.1/...`) — `electron-updater` braucht
|
||
für `latest.yml` aber eine **feste** Adresse. Ob Gitea 1.27 das
|
||
GitHub-Muster `releases/latest/download/...` bedient, ist ungeprüft (2024
|
||
war es dort noch ein offener Wunsch). Falls nein, ist die Rückfallebene
|
||
simpel: ein festes Update-Tag, dessen drei Dateien der Bau bei jedem
|
||
Release ersetzt. Beides ist eine Bau-Einstellung, keine Programmänderung.
|
||
|
||
### Entscheid 7 — Der alte Windows-Weg wird entfernt, sofort, 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. Die erste Fassung hatte das Aufräumen auf „erst wenn v5
|
||
installierbar ist" gelegt; der Commander hat bei der Prüfung der zweiten
|
||
Fassung anders entschieden: **sofort, nicht erst nach Etappe W-6.**
|
||
|
||
Was das bedeutet, gehört ehrlich hingeschrieben: Zwischen dem Aufräumen und
|
||
W-6 liefert das Repo **kein installierbares Windows-Rippy**. Die auf dem
|
||
Commander-PC installierte rc11-Fassung läuft lokal weiter, bekommt aber keine
|
||
Reparaturen mehr; wer in der Zwischenzeit rippen will, nutzt sie oder die
|
||
Docker-Version. Das ist der Preis für ein Repo ohne Zwitter — und er ist
|
||
bewusst bezahlt.
|
||
|
||
**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.
|
||
|
||
### Entscheid 8 — Der Remote-Worker bleibt bei Docker
|
||
|
||
*„Das soll NICHT Teil des neuen Standalone-Produktes sein, da dieser NUR für
|
||
die Docker-Variante verwendet wird."*
|
||
|
||
Der „Rippy Worker" auf einem Hilfsrechner ist eine Funktion der
|
||
Docker-Installation, kein Standalone-Rippy. Er wird weder abgeräumt noch nach
|
||
v5 übernommen. Warum das so ist und was genau er tut, steht in § 11.1 — dort,
|
||
wo die Unterscheidung gebraucht wird.
|
||
|
||
### Entscheid 9 — Zielgruppe: Bekannte mit Link
|
||
|
||
Neu in der zweiten Fassung: Rippy v5 wird **kein öffentliches Produkt**. Der
|
||
Commander gibt die Download-Adresse gezielt weiter — Familie, Freunde. Keine
|
||
öffentliche Ankündigung, kein Support-Anspruch Fremder.
|
||
|
||
Konsequenzen:
|
||
|
||
- Anleitung, SmartScreen-Erklärung und Prüfsummen bleiben **schlank** — sie
|
||
müssen einen wohlwollenden Bekannten durchbringen, kein anonymes Publikum.
|
||
- Die **Lizenztexte gehören trotzdem dazu.** Weitergabe an Bekannte ist
|
||
Weitergabe: HandBrake (GPL-2) und FLAC (BSD-artig) verlangen Lizenztext und
|
||
Quellverweis im Paket (§ 9). Das ist keine Formalie für später, sondern
|
||
Bestandteil von Etappe W-6.
|
||
- Das Repo bleibt lesbar wie bisher; beworben wird nichts. Sollte v5 eines
|
||
Tages doch öffentlich werden, ändert sich am Programm nichts — nur an
|
||
Anleitung und Sorgfaltstiefe.
|
||
|
||
---
|
||
|
||
## 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. Die Skripte liegen in
|
||
`beweise/` und sind dort nachstellbar dokumentiert.
|
||
|
||
### 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)
|
||
```
|
||
|
||
Zur Einordnung, am 30.08.2026 gegen das Netz geprüft: Electron 44 ist die
|
||
aktuelle stabile Linie, koffi 3.1.6 die aktuelle Version, Node 24 ist LTS.
|
||
Das Konzept baut also nicht auf Auslaufendem auf — was Electron-Versionen
|
||
betrifft, gilt aber die Pflege-Regel in § 6.8.
|
||
|
||
### 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 — in Electron gemessen
|
||
|
||
Node 24 bringt SQLite mit (`node:sqlite`, `DatabaseSync`). Keine
|
||
`better-sqlite3`-Kompilierung, kein `node-gyp`, kein Visual-Studio-Build in der
|
||
Bau-Kette.
|
||
|
||
Die erste Fassung musste hier eine Warnung setzen: Electron kompiliert sein
|
||
Node mit eigenen Flags, und ob `node:sqlite` dort vorhanden ist, war nur für
|
||
Node pur gemessen. **Das ist erledigt — am 30.08.2026 in Electron 44.0.0
|
||
gemessen, und zwar im `utilityProcess`**, also exakt dem Kontext, in dem
|
||
`kern/speicher/db.ts` laufen wird (§ 4.1):
|
||
|
||
```
|
||
KERN-ANTWORT: {"ok":true,"wert":"geht","node":"24.18.1"}
|
||
Kern beendet mit 0
|
||
```
|
||
|
||
Anlegen, Schreiben, Lesen — alles grün. Die Rückfallebene `better-sqlite3`
|
||
wird nicht gebraucht. Skripte: `beweise/sqlite-main.js` + `sqlite-kern.js`.
|
||
|
||
### 3.5 Ein Nebenbefund für die Bau-Kette — zweimal bestätigt
|
||
|
||
`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. **Electron
|
||
selbst trifft die Sperre härter:** Sein Install-Skript lädt die eigentliche
|
||
Programmdatei herunter; wird es geblockt, liegt nach `npm install` ein Paket
|
||
**ohne `dist/electron.exe`** da, und nichts startet. Bei der sqlite-Messung
|
||
(§ 3.4) selbst hineingelaufen; `node node_modules/electron/install.js` holt
|
||
die Binary nach. Beides gehört in die Bau-Anleitung, sonst sucht jemand eines
|
||
Tages an der falschen Stelle.
|
||
|
||
### 3.6 Die Update-Quelle — gemessen statt vermutet
|
||
|
||
Die erste Fassung behauptete: *„Gitea läuft auf `192.168.178.153` im Heimnetz.
|
||
Von außen ist das nicht erreichbar."* Am 30.08.2026 von außerhalb des
|
||
Heimnetzes gemessen:
|
||
|
||
```
|
||
https://git.tobisniceshomelab.ddnsfree.com/Hitonabi/rippy
|
||
-> lädt anonym, ohne Anmeldung, über den Reverse-Proxy des Commanders
|
||
-> TLS-Zertifikat gültig
|
||
-> Gitea 1.27.2 (/api/v1/version)
|
||
-> bisher 0 Releases, 0 Tags — der Release-Weg ist frei
|
||
```
|
||
|
||
Die interne Adresse `192.168.178.153:3000` existiert daneben weiter; für
|
||
Updates zählt die öffentliche. Damit ist Entscheid 6 keine Infrastruktur-Frage
|
||
mehr, sondern eine Bau-Einstellung. Offen bleibt nur die
|
||
Fest-Adresse-Feinheit für `latest.yml` (Entscheid 6, ⚠️-Punkt) — die wird in
|
||
W-6 am ersten echten Release gemessen.
|
||
|
||
---
|
||
|
||
## 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. Und § 3.4 hat gezeigt: `node:sqlite` steht genau
|
||
dort zur Verfügung.
|
||
|
||
**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 (§ 5, Schritt 1)
|
||
│ │ │ └── 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
|
||
│ │ │ ├── schluessel.ts 4K-Schlüsselkette, Beta-Key samt Ablaufdatum (§ 9.1)
|
||
│ │ │ └── leine.ts Job Object (§ 3.3)
|
||
│ │ └── speicher/
|
||
│ │ ├── db.ts SQLite (node:sqlite — § 3.4 gemessen)
|
||
│ │ ├── 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
|
||
│ Wache im 3-Sekunden-Takt (IOCTL_STORAGE_CHECK_VERIFY2 — § 3.2)
|
||
│ Verfeinerung später: WM_DEVICECHANGE-Ereignis (siehe unten)
|
||
▼
|
||
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
|
||
▼
|
||
```
|
||
|
||
**Warum der Takt Plan A ist und das Ereignis nicht.** Die erste Fassung
|
||
versprach „Ereignis, kein Poll" — die Prüfung hat die Reihenfolge umgedreht.
|
||
`WM_DEVICECHANGE` ist eine **Fenster**-Nachricht: Sie wird nur an Fenster mit
|
||
Nachrichtenschleife zugestellt, und der Kern hat keines. Beide Auswege sind
|
||
machbar, aber krumm — entweder fängt der Main-Prozess die Nachricht am
|
||
Fenster ab und muss dann die Frage „welches Laufwerk?" aus einer
|
||
Zeiger-Struktur klauben, oder der Kern baut sich per koffi ein unsichtbares
|
||
Nachrichten-Fenster samt eigener Schleife. Der 3-Sekunden-Takt dagegen ist
|
||
eine Zeile (`IOCTL_STORAGE_CHECK_VERIFY2`, in § 3.2 gemessen, praktisch
|
||
kostenlos), und bei einem Vorgang, der 30–90 Minuten dauert, sind drei
|
||
Sekunden Erkennungsverzögerung bedeutungslos. Also: **Takt zuerst, Ereignis
|
||
als spätere Verfeinerung** — und wenn das Ereignis kommt, bleibt der Takt als
|
||
Rückfallebene bestehen, falls die Nachricht ausbleibt.
|
||
|
||
**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 öffentlichen 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 — das gilt
|
||
ausdrücklich auch, wenn das Heimnetz des Commanders gerade nicht antwortet.
|
||
|
||
**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. (Die Prüfsumme aus `latest.yml` sichert
|
||
nur die Übertragung — sie kommt über denselben Kanal wie die Datei und ist
|
||
deshalb kein Ersatz für den sicheren Kanal selbst.)
|
||
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.
|
||
|
||
**Und eine Pflege-Regel, die das Selbst-Update erst wertvoll macht:**
|
||
Electron bringt etwa alle acht Wochen eine neue Hauptversion und pflegt nur
|
||
die letzten drei — wer stehen bleibt, sitzt nach rund einem halben Jahr auf
|
||
einem Chromium mit bekannten Sicherheitslücken. Deshalb: **Bei jedem
|
||
Rippy-Release wird geprüft, ob ein Electron-Sprung ansteht.** Das Verteilen
|
||
kostet dank Selbst-Update nichts; das Nicht-Verteilen kostet irgendwann
|
||
Vertrauen.
|
||
|
||
### 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 |
|
||
|
||
Zu Windows 10, tagesaktuell eingeordnet: Die Verbraucher-Sicherheitsupdates
|
||
(ESU) enden im Oktober 2026, die Chromium-Familie läuft auf Windows 10 aber
|
||
erklärtermaßen weiter (Edge/WebView2 bis mindestens Oktober 2028). Electron 44
|
||
läuft dort. Für Rippy heißt das: bauen ja, und ein Satz in der Anleitung, dass
|
||
Windows 10 selbst ausläuft — mehr nicht.
|
||
|
||
**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.
|
||
|
||
Zum SmartScreen-Hinweis beim ersten Start und zur Update-Folge der fehlenden
|
||
Signatur: beides steht bei Entscheid 5 — die Anleitung erklärt den Weg, das
|
||
Release nennt Größe und Prüfsumme, und Updates laufen nur über HTTPS (§ 6.8).
|
||
|
||
---
|
||
|
||
## 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 Lizenztexte und Quellverweise für HandBrake und FLAC liegen dem Setup bei
|
||
— wegen Entscheid 9 wird die EXE weitergegeben, und damit ist das Pflicht,
|
||
nicht Kür.
|
||
|
||
**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 defekt — der Hersteller verweist selbst auf den
|
||
> Beta-Key.** *(Am 30.08.2026 gegen Forum und Fachpresse geprüft.)*
|
||
|
||
Zur Einordnung, die in der ersten Fassung fehlte: Das ist **kein frischer
|
||
Unfall, sondern ein Dauerzustand seit Sommer 2025** — das Kaufsystem fällt
|
||
seither immer wieder aus, und der Beta-Key wurde bisher stets kurz vor Ablauf
|
||
verlängert (zuletzt Anfang August 2026, gültig bis Ende September). Rippy
|
||
plant also nicht für eine einmalige Panne, sondern für einen Rhythmus. Das
|
||
Restrisiko — der Hersteller verschwindet ganz — kann kein Programm der Welt
|
||
auffangen; was ein Programm auffangen kann, ist das Überraschtwerden:
|
||
|
||
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. Ohne gültigen Key geht keine Blu-ray und keine 4K-Disc mehr
|
||
auf; DVDs laufen weiter.
|
||
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` |
|
||
| `makemkv_daten.py` | Die 4K-Schlüsselkette: Beta-Key-Abruf aus dem Forum-Thread, Schlüsselspeicher (`_private_data.tar`), KEYDB als Notnagel — samt der dort dokumentierten Fehldiagnosen | `werkzeuge/schluessel.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 — und zwar sofort.** 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. **Entscheid 8:
|
||
B bleibt bei Docker** — es wird weder abgeräumt (es 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).
|
||
|
||
**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** |
|
||
|
||
### 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. Wichtig fürs
|
||
Löschen von `makemkv_daten.py`-Verwandten: Die Schlüsselketten-Erkenntnisse
|
||
(§ 10) VOR dem Löschen in v5-Notizen sichern, falls W-7 noch nicht begonnen
|
||
hat — das Wissen darf nicht nur im Git-Verlauf liegen.
|
||
|
||
### 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. **Jetzt, nicht erst wenn v5 steht.** Commander-Entscheid vom 30.08.2026
|
||
(zweite Fassung): Das Aufräumen wartet **nicht** auf Etappe W-6. Die
|
||
Konsequenz steht bei Entscheid 7 — zwischen Aufräumen und W-6 liefert das
|
||
Repo kein installierbares Windows-Rippy; die installierte rc11-Fassung
|
||
läuft lokal weiter, bekommt aber keine Reparaturen mehr.
|
||
2. **Ein eigener Branch**, nicht direkt auf `main`. Der Umfang ist zu groß für
|
||
einen Freihand-Commit.
|
||
3. **In der Reihenfolge A → C → (B bleibt).** Erst die eindeutigen Dateien,
|
||
dann die verstreuten Zweige — sonst sucht man Aufrufer, die es noch gibt.
|
||
4. **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.
|
||
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.
|
||
|
||
**Parallel dazu, unabhängig von den Etappen:** das Aufräumen von `main`
|
||
(§ 11.4) — es startet sofort und braucht auf nichts hiervon zu warten.
|
||
|
||
| # | Titel | Inhalt | Fertig, wenn |
|
||
|---|-------|--------|--------------|
|
||
| **W-0** | **Gerüst** | Electron 44 + TypeScript + Vite + Vitest, drei Prozesse, IPC, `node:sqlite` anbinden (§ 3.4 — bereits bewiesen), Prozess-Leine, CI-Ampel, npm-Skript-Sperre in der Bau-Anleitung (§ 3.5) | 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 im 3-Sekunden-Takt (§ 5), 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 gegen das öffentliche Gitea (inkl. Messung der Fest-Adresse für `latest.yml`, Entscheid 6), Lizenztexte beilegen (Entscheid 9), 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 (`werkzeuge/schluessel.ts`), WM_DEVICECHANGE als Wache-Verfeinerung (§ 5) | 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. Einordnung: Dauerzustand seit Sommer 2025, der Key wurde bisher stets verlängert. **Bleibt der akuteste Termin im Dokument.** |
|
||
| **Kein installierbares Windows-Rippy zwischen Aufräumen und W-6** | sicher × akzeptiert | Folge von Entscheid 7 („sofort"). Die installierte rc11-EXE läuft lokal weiter; die Docker-Version deckt die VM ab. Bewusst bezahlter Preis |
|
||
| Neubau dauert länger als eine Reparatur | sicher | Deshalb Entscheid 2: Die Docker-Version läuft weiter. Es entsteht keine Lücke auf der VM |
|
||
| Electron-Paket ~150–200 MB | sicher, gering | Ein einmaliger Download. Der Gegenwert ist eine Werkzeugkette statt zweier |
|
||
| **Electron altert schnell** (alle ~8 Wochen neue Hauptversion, nur 3 gepflegt) | sicher × planbar | Pflege-Regel in § 6.8: bei jedem Rippy-Release den Electron-Sprung prüfen. Das Selbst-Update macht das Verteilen billig |
|
||
| SmartScreen bei unsignierter EXE | mittel | Entscheid 5: kein Zertifikat. Anleitung + Prüfsumme beim Release — reicht für die Zielgruppe aus Entscheid 9 |
|
||
| **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 |
|
||
| **Feste `latest.yml`-Adresse im Gitea** | niedrig × messbar | Entscheid 6, ⚠️-Punkt — in W-6 am ersten Release messen; Rückfallebene festes Update-Tag. Eine Bau-Einstellung, keine Programmänderung |
|
||
| Updates hängen am Heimnetz des Commanders | niedrig | § 6.8 Regel 3 — ein stummer Server ist eine Protokollzeile, kein Fehler. Wer eine eigene Quelle will, trägt sie ein |
|
||
| **Aufräumen von `main` reißt Docker mit** | mittel × vermeidbar | § 11 — drei Teile sauber getrennt, Remote-Worker bleibt, eigener Branch, Ampel nach jedem Schritt |
|
||
| koffi + Electron + npm-Install-Sperre | niedrig | § 3.5 gemessen: koffi lädt mit vorgebauten Binärdateien, Electron braucht `install.js` von Hand. Beides steht in der Bau-Anleitung |
|
||
| 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 |
|
||
| ~~`node:sqlite` in Electron ungeprüft~~ | **gelöst** | § 3.4 — am 30.08.2026 im `utilityProcess` von Electron 44 gemessen. Rückfallebene `better-sqlite3` nicht gebraucht |
|
||
| ~~Öffentliche Update-Quelle für Externe~~ | **gelöst** | § 3.6 — Gitea ist öffentlich per HTTPS erreichbar, Entscheid 6 nutzt es direkt |
|
||
|
||
### Stand der Entscheidungen (30.08.2026, zweite Fassung)
|
||
|
||
Alle offenen Punkte der ersten Fassung sind entschieden **und** die Prüfung
|
||
hat zwei davon zusätzlich vereinfacht: Code-Signing **nein** (Entscheid 5) ·
|
||
Update-Quelle **öffentliches Gitea, direkt** (Entscheid 6, durch Messung § 3.6
|
||
von einer Infrastruktur-Frage zu einer Bau-Einstellung geschrumpft) · alter
|
||
Windows-Weg **wird sofort entfernt** (Entscheid 7) · Remote-Worker **bleibt
|
||
bei Docker** (Entscheid 8) · Zielgruppe **Bekannte mit Link** (Entscheid 9).
|
||
Was daraus an Arbeit folgt, steht in § 11 und § 12.
|
||
|
||
---
|
||
|
||
## 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.
|
||
- **Kein öffentlicher Auftritt.** Entscheid 9: Weitergabe per Link an
|
||
Bekannte, kein Marketing, kein Support-Versprechen an Fremde.
|
||
|
||
---
|
||
|
||
## 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, zweite Fassung · 30.08.2026 · Branch `worktree-windows-electron`*
|