a9cfcb272a
Ampel / ampel (push) Successful in 32s
Regel B: keine stille Aenderung an einer KONZEPT-Vorgabe. Das Muss-Feature "Rate-Limiting pro Client-IP" bleibt, nur die Zahl aendert sich - mit der Rechnung, die zeigt warum: 100/min lag unter Rippys eigener Last (ein offener Tab braucht 111/min, ein Windows-Worker 12 dazu). Festgehalten durch test_grenze_deckt_die_eigene_last_ab, damit die Zahl nicht unbemerkt zurueck unter die Grundlast wandert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
223 lines
13 KiB
Markdown
223 lines
13 KiB
Markdown
# 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 (5–15s)** | **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).
|