Files
rippy/KONZEPT.md
T
HitonabiandClaude Opus 5 20675a98fa docs: Konzept fuer Rippy v2 + drei Commander-Entscheide (28.08.2026)
WAS: KONZEPT-V2.md neu — Systemarchitektur, Daten-/Queue-Strategie, die
drei Betriebsmodi, API-/Event-Design, Migrationsplan V2-0 bis V2-7.
KONZEPT.md §10 und ROADMAP.md ziehen nach.

WARUM: Rippy soll drei Betriebsarten bekommen statt einer — Docker,
native Windows-App ohne Docker, Headless-Linux-Dienst; alle mit
demselben Webinterface. Das traegt technisch nur, wenn es EINEN Kern
gibt, dessen Betriebsmodus nur die Auswahl der Treiber hinter vier
Ports ist (Store, Queue, Bus, Drives).

Drei Entscheide des Commanders sind eingearbeitet:

- Reihenfolge: Echtzeit (SSE statt Polling) VOR der Windows-App. Die
  Windows-App braucht SQLite und die lokale Queue ohnehin.
- Disc-Schluessel: automatischer Abruf MIT Rueckfallebene, als Kette in
  drei Stufen (eigener Bestand -> Abruf -> Import von Hand). Das
  verschiebt die Grenze aus KONZEPT.md §10 vom 25.07.2026 bewusst —
  deshalb steht sie dort jetzt ausdruecklich fortgeschrieben statt
  still ersetzt (AGENTS Regel B). Bezugsadresse leer vorbelegt,
  Fehlschlag laut, Bestand wird nie still ueberschrieben.
- Speicherziele: der Host mountet, Rippy prueft und erzeugt die
  kopierbare Zeile. Raeumt die URSACHE der CIFS-Ausfaelle ab (Befund
  26.07.2026: die Verbindung lebt in der Netz-Namespace des
  api-Containers und stirbt mit ihm) statt weiter das Symptom zu
  heilen. SYS_ADMIN, DAC_READ_SEARCH, apparmor:unconfined und die
  Mount-Wache fallen damit weg.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 08:38:03 +02:00

272 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# KONZEPT — Rippy
> Automatisches Ripping-System für CD, DVD und Blu-ray, gebaut für Proxmox LXC (Debian 12 Slim). Ziel: Ein modernes, eigenständiges System statt veralteter ARM-Flask-UI.
---
## 1. Ziel & Motivation
Der Commander betreibt ein Heimlab mit Proxmox LXC (Debian 12) und PBS-Backup. Das vorhandene Automatic Ripping Machine (ARM) ist funktional, aber das UI (Flask) ist veraltet, es hat keine Echtzeit-Updates (kein SSE/WebSocket), keine JWT-Auth und eine monolithische Architektur.
Rippy ist ein **komplett neues System** von Grund auf: modulare Multi-Container-Architektur, modernes WebUI, Echtzeit-Jobstatus, sichere Authentifizierung — gebaut als eigenständiges Produkt, nicht als ARM-Fork.
## 2. Zielgruppe
- **Commander** (Hauptnutzer): Technik-affin, Proxmox-Heimlab, Wert auf eigene Kontrolle und Datenschutz.
- **Zukünftig**: Weitere Heimlab-Nutzer mit ähnlicher Infrastruktur.
## 3. Kern-Idee
Ein modular aufgebautes System, das bei Disc-Einwurf automatisch den Typ erkennt, **vorbereitend Metadaten lookup und Preview bereitstellt**, das Medium rippt, Metadaten anreichert und das Ergebnis **Jellyfin-konform strukturiert ablegt**. Alles über eine REST-API, asynchrone Celery-Jobs, ein React-WebUI.
## 4. Features
| Feature | Muss | Kann | Später |
|---------|------|------|--------|
| Disc-Erkennung via udev-Event | ✔ M | | |
| Dynamischer Device-Resolver (UUID/Serial) | ✔ M | | |
| MakeMKV-Ripping (lossless) | ✔ M | | |
| Audio-Rip mit abcde + MusicBrainz | ✔ M | | |
| **Metadaten-Lookup VOR dem Ripping (Preview)** | **✔ M** | | |
| **Jellyfin-Ordnerstruktur & NFO-Generierung** | **✔ M** | | |
| **Jellyfin-Poster/Backdrop/Fanart Download** | **✔ M** | | |
| **Jellyfin-kompatible Dateibenennung** | **✔ M** | | |
| Video-Metadaten via TMDB API | ✔ M | | |
| AcoustID-Fingerprinting (chromaprint) | ✔ M | | |
| Multi-Disc-Set-Handling (Release-Group-Resolver) | ✔ M | | |
| SQLite-Cache für API-Rate-Limits | ✔ M | | |
| ~~JWT-Auth (Access 15min/Refresh 7 Tage)~~ — GESTRICHEN 24.07.2026, siehe §10 | | | |
| Rate-Limiting pro Client-IP — ~~100/min~~ **600/min** (26.07.2026, siehe Abweichungen) | ✔ M | | |
| Celery-Queue + Redis mit AOF-Persistence | ✔ M | | |
| React-UI (statisch via Nginx) | ✔ M | | |
| Proxmox LXC Template + Ansible Playbooks | ✔ M | | |
| SELinux/AppArmor Profile pro Container | ✔ M | | |
| MakeMKV als isolierte Black-Box-CLI | ✔ M | | |
| GPL-v3-Compliance-Checkliste | ✔ M | | |
| Source-Release-Endpoint | ✔ M | | |
| HandBrake-Kompression NACH dem Lossless-Rip (Stufe 2) | ✔ M | | |
| Prometheus+Grafana Monitoring | | ✔ K | |
| PBS-Snapshot-Backup-Hooks | | ✔ K | |
| Multi-Disc-Parallelisierung | | | ✔ S |
| Redis-Cluster (Horizontales Scaling) | | | ✔ S |
| PWA Mobile-App | | | ✔ S |
## 5. Mechaniken & Ablauf
**Roter Faden — was passiert, wenn der Commander eine Disc einlegt:**
1. **udev-Event** → System-Dämon (unabhängig vom API-Container) erkennt Einwurf.
2. **Device-Resolver** (udev-basiert) ermittelt UUID/Serial-Nummer des Laufwerks, erzeugt Symlink `/dev/disc/<uuid>`.
3. **Job-Erstellung** → Dämon erstellt Celery-Task, übergibt Device-Pfad und Disc-Typ.
4. **Typ-Erkennung** (CD/DVD/Blu-ray) → Auswahl des Ripping-Pfads.
5. **METADATEN-Lookup im VORFELD**:
- DVD/Blu-ray: `makejungles` liest TOC ohne Ripping.
- Title, Laufzeit, Scene-Labels werden ausgelesen.
- Video-Titel wird gegen TMDB API gegengeprüft → Confidence-Score.
- Audio-Titel wird gegen MusicBrainz gegengeprüft (bei CD).
- **Rippy zeigt eine Preview im WebUI**: Titel, Jahr, Cover, Trackliste.
- Commander bestätigt oder korrigiert manuell.
6. **Ripping** (nach Bestätigung):
- CD: `abcde` → FLAC, Metadaten via AcoustID + MusicBrainz.
- DVD/Blu-ray: `makemkvcon` → verlustfreies MKV als ZWISCHENPRODUKT in /app/temp
(MakeMKV ist der einzige Weg durch AACS — HandBrake kann verschlüsselte
Discs nicht lesen), danach **HandBrake-Kompression auf Arbeitsgröße**
(x265; Commander-Entscheid 23.07.2026: 40-GB-Rohdateien sind kein
brauchbares Endprodukt). Roh-Datei wird nach Erfolg gelöscht
(Setting keepOriginal behält sie).
7. **Post-Processing & Jellyfin-Formatierung**:
- Dateien in Jellyfin-konformer Ordnerstruktur.
- NFO-Dateien im Kodi/NFO-Schema.
- Poster/Backdrop/Fanart von TMDB.
8. **Multi-Disc-Erkennung**: Release-Group-Resolver prüft weitere Discs.
9. **Status-Push**: Worker sendet Fortschritt an UI.
10. **Abschluss**: Job-Ergebnis protokolliert, PBS-Backup-Hook.
## 6. Architektur & Tech-Stack
**Container-Design:**
- **API-Service** (FastAPI): JWT-Auth, REST-Endpoints, OpenAPI-Docs, Job-Management.
- **Worker-Service** (Celery + Redis mit AOF-Persistence): Isolierte Ripping-Jobs.
- **UI-Service** (React + Vite): Build-Step → statisch via Nginx.
- **udev-Daemon**: Separater System-Dienst für Disc-Einwurf-Events.
**Container-Security:**
- `read_only: true` für API/Worker (keine Schreibrechte außer tmpfs)
- `tmpfs` für `/app/tmp`, `/run`, `/tmp` (wichtig für Python `__pycache__`)
- Healthchecks für Postgres/Redis
- Separate Volumes für Medien und Temp-Dateien
**Neue Worker-Komponenten:**
- **NFO-Generator**: `.nfo`-Dateien im Jellyfin-Kodi-Schema.
- **TMDB Image-Downloader**: poster.jpg, fanart.jpg, backdrop.jpg.
- **Pre-Scan-Modul**: TOC-Lesung ohne Ripping + API-Query.
**LXC-Isolation:**
- Read-only Bind-Mounts für System-Bibliotheken.
- Device-Node-Read-Only für `/dev/sr*`.
- SELinux/AppArmor Profile pro Container.
- Netzwerk-Policy: UI→API (HTTPS), API↔Worker (mTLS).
**Persistenz:**
- PostgreSQL für Job-Logs, Metadaten, User-Management.
- SQLite-Cache für API-Antworten (LRU, 10k Einträge).
- NFS/Bind-Mount für Medien-Store.
## 7. UX-Flow
1. **Dashboard** — Echtzeit-Kacheln mit Job-Status, Queue-Übersicht.
2. **Job-Verlauf** — Tabelle aller Jobs mit Status, Fortschrittsbalken.
3. **Metadaten-Preview** — Nach udev-Erkennung: Titel, Jahr, Cover, Trackliste, Confidence-Score.
4. **Job-Detail** — Live-Log-Ausgabe, Fortschritt, "Abort".
5. **Ergebnis-View** — Liste gerippter Dateien, Metadaten, Jellyfin-Ordnerstruktur.
6. **Einstellungen** — API-Keys, Transcoding, Backup-Pfade, Jellyfin-Config.
7. **Geräte-Verwaltung** — Liste Laufwerke, Status, Device-Resolver.
## 8. Risiken & offene Punkte
| Risiko | Status | Behandlung |
|--------|--------|------------|
| MakeMKV-Beta-Key-Management | Gelöst | Key-Erneuerung als Cron-Job; DMCA-Ausnahme in DE |
| **Disc-Schlüssel für 4K-UHD (AACS 2.0)** | **Gelöst mit Handgriff** | Am 25.07.2026 auf beiden Maschinen gemessen: `makemkvcon` unter **Linux** ruft Disc-Schlüssel nie ab (kein einziger Verbindungsversuch, mit leerem wie gefülltem Speicher, mit und ohne `--noscan`, `dev:` wie `disc:`), die **Windows**-Version tut es (Meldung 3338). Rippy stellt ein persistentes Datenverzeichnis bereit und nimmt den Schlüsselspeicher `_private_data.tar` einer Windows-Installation sowie ersatzweise eine `KEYDB.cfg` entgegen; damit ging Akira UHD auf der VM auf. Rippy liefert, lädt und verteilt KEINE Schlüssel. Siehe §10 (25.07.2026) |
| LXC-Device-Node-Änderungen | Gelöst | udev-Resolver (UUID/Serial) |
| Pending-Queue / State-Manager | Gelöst | Redis mit AOF-Persistence |
| Hybrid-Discs | Offen | MVP erkennt nur Standard; als "Kann" notiert |
| LXC-Device-Passthrough | Gelöst | Read-only Bind-Mounts; udev-Daemon unabhängig |
| Rate-Limits der Metadaten-APIs | Gelöst | SQLite-Cache (LRU, 10k Einträge), exponential backoff |
| GPL-v3-Compliance | Gelöst | Checkliste, Source-Release-Endpoint, Black-Box-Trennung |
| Redis Single-Instance | Offen | MVP mit Single-Instance; Cluster als "Später" |
| **TMDB-Matching-Fehler bei Nischentiteln** | **Offen** | Pre-Scan Confidence-Score + manueller Korrektur-Mechanismus |
| **NFO-Format-Abhängigkeit von Jellyfin-Version** | **Hinweis** | Kodi/NFO-Schema (stabil, gut dokumentiert) |
| **Pre-Scan-Latenz (515s)** | **Hinweis** | Akzeptabel für Heim-Use-Case; parallelisierbar |
| **TMDB-Bildrechte** | **Gelöst** | TMDB API-ToS erlaubt private Nutzung |
## 9. Härtetest-Dokumentation
**Erster Haertetest (Advocatus Diaboli, GLM-4.7-Flash):**
5 BLOCKER gefunden: MakeMKV-Key-Management, LXC-Device-Resolver, Pending-Queue, Hybrid-Discs, Bind-Mount-Performance.
**Runde 1 (5 Rollen):**
- DevOps: Multi-Container-Architektur, Proxmox-HA-Template, Resource-Limits, Restart-Policy.
- Security: JWT-Refresh-Token, Rate-Limiting, mTLS, SELinux/AppArmor.
- Software Architekt: udev-basierter Device-Resolver, Unix-Socket, State-Manager.
- Media Metadata: AcoustID-Fingerprinting, Multi-Disc-Set-Handling, SQLite-Cache.
- Legal: GPL-v3-Checkliste, MakeMKV-Black-Box-Trennung, Source-Release-Endpoint.
**Zweiter Haertetest (Advocatus Diaboli, GLM-4.7-Flash):**
5 HINWEISE (keine BLOCKER): React als Build-Step+Statisch (korrigiert), Redis-AOF-Persistence (integriert), Read-only vs. Write-Konflikt (nicht relevant), Redis-Cluster (als "Später" notiert), Cold-Boot-Problematik (udev-Daemon gelöst).
**Dritte Iteration — Commander-Feedback:**
- Metadaten-Lookup VOR dem Ripping
- 100% Jellyfin-Kompatibilität
- Metadaten-Preview im UI
- Neue Risiken: TMDB-Matching-Fehler (mit Fallback), NFO-Format-Abhängigkeit (Hinweis), Pre-Scan-Latenz (Hinweis)
**Ergebnis:** Konzept ist wasserdicht für MVP-Phase.
## 10. Konzept-Fortschreibungen (dokumentierte Erweiterungen, keine Abweichungen)
- **24.07.2026 — Media-Server-Neutralität:** Die Jellyfin-Muss-Features
(Ordnerstruktur, NFO, Poster) bleiben vollständig bestehen; sie sind jetzt
über das Setting `mediaServer` auf Emby und Kodi ausgeweitet (identisches
Kodi-NFO-Schema) und für Plex auf die reine Benennung reduziert. Jellyfin
bleibt Referenz- und Empfehlungssystem im Einrichtungs-Assistenten.
- **24.07.2026 — Benachrichtigungen:** Webhook bei Job-Ende (Discord/Slack/
ntfy/generisch) als gebautes Feature — im UX-Flow Punkt 10 („Abschluss")
war das als Protokollierung angelegt, jetzt meldet Rippy aktiv.
- **23./24.07.2026 — udev → ioctl:** Der im Konzept beschriebene udev-Daemon
ist im Container prinzipbedingt nicht lauffähig; die Disc-Wache pollt per
Kernel-ioctl (3 s) — gleiches Verhalten, universell lauffähig.
- **24.07.2026 — AUTH GESTRICHEN (Commander-Entscheid):** Das Muss-Feature
„JWT-Auth" ist komplett entfernt (Endpoints, auth.py, Abhängigkeiten,
Env-Pflicht). Begründung: Rippy läuft ausschließlich im Heimnetz, das UI
hatte nie einen Login-Flow — die Auth-Oberfläche war Placebo und die
passlib/bcrypt-Abhängigkeit hat die CI-Ampel gebrochen. Rate-Limiting
(pro IP) bleibt. Wer Rippy je nach außen öffnet, stellt einen
Reverse-Proxy mit eigener Auth davor (z. B. Authelia/Caddy basicauth).
- **26.07.2026 — Rate-Limit von 100/min auf 600/min:** Das Muss-Feature
(„Rate-Limiting pro Client-IP") bleibt unverändert, nur die Zahl ändert sich.
Begründung: 100/min lag UNTER Rippys eigener Last. Nachgerechnet an den
Taktgebern im UI verursacht ein einziger offener Tab 111 Anfragen pro Minute
(Dashboard 75 + Log-Kasten 24 + Laufwerks-Suche 12), ein installierter
Windows-Worker weitere 12. Die Bremse griff also im Normalbetrieb permanent —
97 Antworten mit HTTP 429 im nginx-Log —, und weil das UI einen abgewiesenen
Abruf als „nichts da" verbuchte, leerte sich die Job-Liste im Sekundentakt.
Genau das hat der Commander als „wird oft neu geladen" gemeldet. 600/min =
10 Anfragen pro Sekunde: Luft für mehrere Tabs und Worker, während der Zweck
der Bremse (ein Skript in einer Endlosschleife, Hunderte pro Sekunde) weiter
erfüllt ist. Die Rechnung steht als Kommentar in `ratelimit.py` und ist durch
`test_grenze_deckt_die_eigene_last_ab` festgehalten — wer die Zahl senkt, muss
dort vorbei. Zweiter Teil derselben Reparatur: Hinter dem nginx ist
`request.client.host` immer der Proxy, alle Clients teilten sich also EINEN
Eimer; jetzt gilt `X-Real-IP`.
- **25.07.2026 — 4K-UHD-Disc-Schlüssel: Rippy stellt Platz bereit, keine
Schlüssel:** Das Muss-Feature „MakeMKV-Ripping (lossless)" bleibt
unverändert; ergänzt wird nur ein **persistentes MakeMKV-Datenverzeichnis**
(`MAKEMKV_DATA_HOST`, im Worker `/root/.MakeMKV`, in der API
`/app/makemkv-data`) samt Bedienung im UI. Begründung: Am 25.07.2026 auf
BEIDEN Maschinen nachgemessen — `makemkvcon` unter Linux ruft Disc-Schlüssel
nie ab, die Windows-Version tut es. (Erste Fassung dieses Eintrags behauptete,
MakeMKVs Schlüssel-Kanal sei abgeschaltet; das war falsch und wurde am selben
Tag richtiggestellt.) Rippy nimmt deshalb den Schlüsselspeicher
`_private_data.tar` einer MakeMKV-Installation entgegen und ersatzweise eine
`KEYDB.cfg`. **Rippy liefert und verteilt KEINE Disc-Schlüssel und
lädt auch keine herunter** — es hält nur den Platz für Dateien bereit,
die der Nutzer selbst mitbringt, zeigt ehrlich an, was dort liegt, und gibt
die AACS-Dumps heraus, die MakeMKV ohnehin selbst schreibt. Das ist genau
die Grenze, die `docker/api/makemkv_key.py` in Zeile 14 zieht: die
kostenlose Beta-LIZENZ der Software ist etwas anderes als das
Entschlüsseln oder Verteilen von Disc-Schlüsseln.
- **24.07.2026 — Serien-Flow:** Staffel-Ablage <Serie>/Season NN plus
Episoden-Zuordnung per Laufzeitabgleich (TMDB) — erfüllt Etappe-12-Ziel
„Serien-Episoden-Erkennung" in der ersten Ausbaustufe (nur bei
EINDEUTIGER Zuordnung wird umbenannt).
- **28.08.2026 — DISC-SCHLÜSSEL: die Grenze vom 25.07. ist verschoben
(Commander-Entscheid).** Der Eintrag vom 25.07.2026 oben sagt: *„Rippy
liefert und verteilt KEINE Disc-Schlüssel und lädt auch keine herunter."*
**Das gilt ab jetzt nicht mehr unverändert.** Auf ausdrückliche Entscheidung
des Commanders bekommt Rippy v2 einen automatischen Abruf — mit den
bisherigen Wegen als Rückfallebene. Umgesetzt als Kette in DREI Stufen, die
der Reihe nach abgearbeitet wird und anhält, sobald eine trägt:
1. **Eigener Bestand** (Schlüsselspeicher der Installation) — immer zuerst.
Gefüllt vom Windows-Knoten, der die Schlüssel über die eigene
MakeMKV-Lizenz selbst abruft (unter Linux tut `makemkvcon` das nie,
Messung 25.07.2026), und von jedem Import.
2. **Automatischer Abruf** von der konfigurierten Quelle — wenn Stufe 1
die Disc nicht kennt.
3. **Import von Hand** (`_private_data.tar`, `KEYDB.cfg` über das UI) —
unverändert aus v1.
Fünf Regeln gehören zum Entscheid dazu: (a) die Bezugsadresse steht in der
KONFIGURATION und ist LEER vorbelegt — ohne Eintrag ist Stufe 2
übersprungen; eine vorbelegte Adresse, die irgendwann tot ist, wäre genau
die Falle aus `.env.example` („lässt die Konfiguration gesund aussehen und
den Bau später scheitern"). (b) Ein funktionierender Bestand wird NIE still
überschrieben — neue Datei daneben, prüfen, dann tauschen. (c) Ein
Fehlschlag ist LAUT (kein `except: pass`, Meldung im Log und im UI).
(d) Das UI zeigt Herkunft und Alter jedes Eintrags. (e) **Rippy bringt
selbst nichts mit** — weder Installer noch Docker-Image enthalten Schlüssel
oder eine vorbelegte Bezugsadresse; was abgerufen wird, trägt der Betreiber
der Installation ein. Ausführlich in `KONZEPT-V2.md` § 7.5 und § 10.
- **28.08.2026 — SPEICHERZIELE: der Host mountet, Rippy prüft
(Commander-Entscheid).** Das Muss-Feature „NFS/Bind-Mount für Medien-Store"
(§ 6) bleibt; was wegfällt, ist das Mounten DURCH Rippy. Begründung ist der
Befund vom 26.07.2026: Die CIFS-Verbindung lebt in der Netz-Namespace des
api-Containers und stirbt mit ihm — dagegen läuft heute eine Mount-Wache.
In v2 hängt der Host ein (fstab, `.mount`-Unit oder Compose-Volume-Treiber),
und Rippy erzeugt dafür die fertige, kopierbare Zeile. Die Eingabemaske im
UI bleibt; der Knopf „Verbinden" wird zu „Zeile kopieren". Die gesamte
PRÜF-Logik aus `mounts.py` bleibt erhalten (Erreichbarkeit mit Zeitgrenze
im Kindprozess, SMB-Klartextfehler, Pfad-Map-Vorschlag). Folge:
`CAP_SYS_ADMIN`, `DAC_READ_SEARCH`, `apparmor:unconfined`,
`propagation: rshared` und die Mount-Wache fallen ersatzlos weg.
- **28.08.2026 — RIPPY v2: drei Betriebsmodi statt eines
(Commander-Auftrag).** Die Multi-Container-Architektur aus § 6 bleibt als
EINER von drei Modi bestehen. Dazu kommen eine native Windows-Installation
ohne Docker und eine Headless-Linux-Anwendung — beide mit demselben
Webinterface. Technisch tragen alle drei denselben Kern; ein Modus ist nur
die Auswahl der Treiber hinter vier Ports (Store, Queue, Bus, Drives).
Damit sind Postgres und Redis für Einzelinstallationen keine Pflicht mehr
(SQLite + lokale Queue), bleiben im verteilten Modus aber unverändert.
Vollständige Spezifikation: **`KONZEPT-V2.md`**, Etappen in `ROADMAP.md`.