feat(install): ein Befehl statt Checkliste - install.sh nimmt die Handarbeit ab
Ampel / ampel (push) Successful in 28s

Commander-Rueckmeldung: "Das Docker Deployment ist mir zu kompliziert." Zu
Recht - die Installation war eine Checkliste aus sechs Schritten, von denen
zwei nur mit Fachwissen zu schaffen waren.

## Neu: sudo ./install.sh

Nimmt genau die Schritte ab, an denen man scheitern konnte:

1. Prueft Voraussetzungen (Linux, Docker, Compose) und nennt bei jedem Mangel
   den Installationsbefehl.
2. FINDET DAS LAUFWERK SELBST. Das war der schlimmste Punkt: MakeMKV braucht
   ZWEI Geraeteknoten, und die sg-Nummer ist je Host anders. Das Skript gleicht
   sie ueber die SCSI-Adresse in /sys ab statt zu raten. Auf der VM
   gegengeprueft: sr0 -> 3:0:0:0, sg1 -> 3:0:0:0, also dasselbe Geraet -
   korrekt als /dev/sr0 + /dev/sg1 erkannt.
3. Legt Ablage- und MakeMKV-Verzeichnis an.
4. Richtet die Mount-Propagation ein UND macht sie per systemd-.mount-Unit
   neustart-fest. Vorher stand in der README nur "reboot-fest persistieren" -
   ohne zu sagen wie; nach einem Reboot scheiterte das Einhaengen von
   NAS-Freigaben aus dem UI stillschweigend.
5. Schreibt die .env, ueberschreibt aber NIE einen bestehenden Wert.
6. Baut und startet, und nennt bei Fehlschlag die drei haeufigsten Ursachen
   mit Diagnosebefehl.

Wiederholbar (mehrfach ausfuehren aendert nichts kaputt) und damit auch der
Update-Weg: git pull && sudo ./install.sh

## Zwei Fallgruben, die beim Testen auffielen - beide meine eigenen

- --nur-pruefen verlangte root und brach ab. Ein Pruef-Modus, der nichts
  aendert, darf daran nicht scheitern - sonst kann man vor der Installation
  nicht nachsehen, ob alles passt. Behoben.
- .env.example hatte OPTICAL_SG=/dev/sg1 UNKOMMENTIERT vorbelegt. Damit haette
  der Installer den erkannten Wert nicht eingetragen ("steht schon drin") und
  auf jedem fremden Host still eine kaputte Konfiguration hinterlassen - genau
  das, was er verhindern soll. Beide Geraetezeilen sind jetzt auskommentiert;
  Compose hat ohnehin Vorgaben. Dazu eine Gegenprobe im Skript: zeigt ein
  wirksamer Wert auf ein Geraet, das es hier nicht gibt (".env von einem
  anderen Rechner"), wird das benannt statt kryptisch von Docker gemeldet.

## README neu aufgebaut

Vorher 293 Zeilen, in denen der Schnellstart zwischen lsscsi, sg-Knoten,
USB-Passthrough und mount --make-rshared begraben war. Jetzt: Installation in
zwei Zeilen oben, dann eine Tabelle "Wenn etwas nicht geht" mit den vier
Faellen, die praktisch alles abdecken. Alles Technische steht darunter in
aufklappbaren Abschnitten - inklusive der Handarbeits-Variante fuer die, die
sie wollen.

NEU und ausdruecklich gewuenscht: Abschnitt "Rippy schneller machen" mit dem
VM-CPU-Typ. Erklaert, warum Virtualisierer eine generische CPU ohne AVX2
geben, was das kostet (gemessene 28-55 h je 4K-Film), die genauen Schritte in
Proxmox (herunterfahren - Hardware/Processors/Type auf 'host' - starten, bzw.
qm set <vmid> --cpu host), warum ein Neustart von innen NICHT genuegt, und wie
man nachprueft: Rippy zeigt die Vektorbefehle seit v3.14 selbst an. Dazu der
Nachteil (keine Live-Migration auf andere CPUs) und die Alternative x86-64-v3
fuer Cluster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-25 21:59:23 +02:00
parent 1eb1c91dd8
commit ca91e5b693
3 changed files with 664 additions and 272 deletions
+11 -5
View File
@@ -7,11 +7,17 @@ POSTGRES_PASSWORD=rippy
# Optisches Laufwerk (Host-Geräteknoten) — je Rechner UNTERSCHIEDLICH! # Optisches Laufwerk (Host-Geräteknoten) — je Rechner UNTERSCHIEDLICH!
# Der Worker braucht den SCSI-CD-ROM-Knoten (meist /dev/sr0) UND den passenden # Der Worker braucht den SCSI-CD-ROM-Knoten (meist /dev/sr0) UND den passenden
# generischen sg-Knoten des Laufwerks. Die sg-Nummer variiert je Host — auf DIESER # generischen sg-Knoten des Laufwerks.
# Maschine ermitteln: lsscsi -g (oder: ls -l /dev/sg*). Hier den HOST-Pfad des #
# Blu-ray/DVD-Laufwerks eintragen; der Default passt für den Ursprungs-Host. # BEWUSST AUSKOMMENTIERT: Die sg-Nummer ist JE HOST ANDERS. Ein vorbelegter Wert
OPTICAL_SR=/dev/sr0 # wäre auf den meisten Rechnern falsch — und zwar unbemerkt, weil er dann „schon
OPTICAL_SG=/dev/sg1 # gesetzt" aussieht. `install.sh` findet beide Knoten selbst (Abgleich über die
# SCSI-Adresse in /sys) und trägt sie hier ein.
#
# Von Hand ermitteln, falls gewünscht: lsscsi -g
# Ohne Eintrag gelten die Compose-Vorgaben /dev/sr0 und /dev/sg1.
#OPTICAL_SR=/dev/sr0
#OPTICAL_SG=/dev/sg1
# (JWT/Auth wurde am 24.07.2026 komplett entfernt — Rippy ist Heimnetz-only, # (JWT/Auth wurde am 24.07.2026 komplett entfernt — Rippy ist Heimnetz-only,
# Commander-Entscheid, siehe KONZEPT.md Abschnitt 10. Härtung für exponierte # Commander-Entscheid, siehe KONZEPT.md Abschnitt 10. Härtung für exponierte
+350 -267
View File
@@ -2,291 +2,374 @@
Disc rein → automatisch erkannt (Titel, Poster, Metadaten) → verlustfrei Disc rein → automatisch erkannt (Titel, Poster, Metadaten) → verlustfrei
gerippt (MakeMKV) → auf Arbeitsgröße komprimiert (HandBrake) → fertig gerippt (MakeMKV) → auf Arbeitsgröße komprimiert (HandBrake) → fertig
abgelegt, wo DU willst (lokal, NAS, jede Freigabe). Modernes Web-UI, abgelegt, wo du willst (lokal, NAS, jede Freigabe). Modernes Web-UI, komplett
Echtzeit-Fortschritt, komplett in Docker, komplett lokal. in Docker, komplett lokal — keine Cloud, keine Telemetrie.
## Schnellstart ---
Voraussetzungen: Docker + Docker Compose, ein optisches Laufwerk am Host. ## Installation
Du brauchst: einen **Linux-Rechner mit Docker** und ein **optisches Laufwerk**.
```bash ```bash
git clone <repo-url> rippy && cd rippy git clone <repo-url> rippy && cd rippy
cp .env.example .env # optional: API-Keys — geht auch später im UI sudo ./install.sh
mkdir -p /srv/rippy/media # Ablage-Basis (anpassbar in docker-compose.yml) ```
Das war es. Am Ende steht die Adresse im Terminal — im Browser öffnen, der
**Einrichtungs-Assistent** übernimmt den Rest.
Zuerst nur nachsehen, ob alles passt, ohne etwas zu ändern:
```bash
./install.sh --nur-pruefen
```
<details>
<summary><b>Was <code>install.sh</code> für dich macht</b> (aufklappen)</summary>
Es nimmt genau die Schritte ab, an denen man vorher scheitern konnte:
1. **Prüft die Voraussetzungen** — Linux, Docker, Compose. Fehlt etwas, steht
der Installationsbefehl dabei.
2. **Findet dein Laufwerk selbst.** MakeMKV braucht **zwei** Geräteknoten:
`/dev/srN` und den passenden `/dev/sgM`. Welche sg-Nummer dazugehört, ist
**je Rechner anders** — das Skript gleicht sie über die SCSI-Adresse ab
statt zu raten. (Genau daran ging vorher die Handarbeit schief.)
3. **Legt die Verzeichnisse an** (`/srv/rippy/media` für die Filme,
`/srv/rippy/makemkv` für MakeMKVs Daten).
4. **Richtet die Mount-Propagation ein** und macht sie neustart-fest — sonst
funktioniert das Einhängen von NAS-Freigaben aus dem UI nach jedem Reboot
nicht mehr.
5. **Schreibt die `.env`** mit den gefundenen Werten. Bestehende Werte werden
**nie** überschrieben.
6. **Baut und startet** alles.
Das Skript ist **wiederholbar**: mehrmals ausführen ändert nichts kaputt. Es
ist auch der Update-Weg — siehe unten.
</details>
### Aktualisieren
```bash
git pull && sudo ./install.sh
```
---
## Wenn etwas nicht geht
Die vier Fälle, die praktisch alles abdecken:
| Symptom | Ursache & Lösung |
|---|---|
| **„Kein optisches Laufwerk gefunden"** | In einer **VM**? Das Laufwerk muss per **USB-Passthrough** durchgereicht werden, nicht als emuliertes CD-ROM (`media=cdrom`) — das kann keine SCSI-Kommandos, MakeMKV sieht es nie. Proxmox: `qm set <vmid> -usb0 host=<hersteller>:<produkt>,usb3=1`. Auf echter Hardware: `ls /dev/sr*` prüfen. |
| **Build bricht beim MakeMKV-Download ab** | Cloudflare drosselt manchmal. Tarballs von makemkv.com/download händisch nach `docker/worker/vendor/` legen, dann `sudo ./install.sh` erneut — der Build nimmt sie dann von dort. |
| **Kompression läuft ewig** | Deine CPU kann kein AVX2. Rippy zeigt das jetzt selbst an (Einstellungen → System, „Vektorbefehle"). Siehe **[Rippy schneller machen](#rippy-schneller-machen)**. |
| **4K-UHD: „The volume key is unknown"** | Erwartbar und **kein Fehler in Rippy**. Siehe **[4K-UHD](#4k-uhd)**. |
Logs ansehen: `docker compose logs -f` · Status: `docker compose ps`
---
## Rippy schneller machen
**Läuft Rippy in einer virtuellen Maschine, ist das hier der wirksamste
Handgriff überhaupt** — und er kostet fünf Minuten.
Virtualisierer geben der VM standardmäßig eine **generische CPU** (bei
Proxmox/KVM heißt sie `kvm64` bzw. `qemu64`). Die kann absichtlich nur alte
Befehlssätze, damit sich eine VM zwischen verschiedenen Wirten verschieben
lässt. Der Preis: **kein AVX2** — und genau davon lebt der Video-Encoder x265.
Was das ausmacht, ist auf der Rippy-Maschine gemessen worden: ein 4K-Film in
H.265 brauchte **28 bis 55 Stunden**, bei voll ausgelasteten Kernen. Mit AVX2
sind typisch **2- bis 4-mal schneller** drin.
**So stellst du es um (Proxmox):**
1. VM **herunterfahren.** Ein Neustart von innen genügt nicht — Proxmox
übernimmt Hardware-Änderungen nur bei einem echten Stopp.
2. Im Web-UI: VM auswählen → **Hardware****Processors** → Doppelklick →
**Type** auf **`host`** stellen → OK.
Oder auf der Proxmox-Konsole:
```bash
qm set <vmid> --cpu host
```
3. VM **starten.**
**Nachprüfen — Rippy sagt es dir selbst:** Einstellungen → System, Zeile
„Vektorbefehle" beim Worker. Steht dort jetzt `avx2` oder `avx512f` statt
`sse4_2`, hat es geklappt und die amberfarbene Warnung verschwindet.
Auf der Kommandozeile: `grep -o avx2 /proc/cpuinfo | head -1`.
<details>
<summary>Nachteile von <code>host</code> — der Vollständigkeit halber</summary>
Die VM sieht dann die echte CPU. Dadurch lässt sie sich nicht mehr auf einen
Wirt mit anderem Prozessor **live** verschieben. In einem Heim-Cluster mit
einem einzigen Wirt ist das ohne Bedeutung. Wer mehrere Wirte hat und
Live-Migration nutzt, wählt statt `host` das neueste Modell, das **alle**
Wirte beherrschen (z. B. `x86-64-v3` — das enthält AVX2).
</details>
**Kein AVX2 möglich?** Dann zwei Auswege, beide im UI:
- **4K nicht komprimieren** (Einstellungen → Verarbeitung → Preset für 4K-UHD →
„Nicht komprimieren"). Die verlustfreie Datei bleibt stehen: beste Qualität,
20100 GB je Film. Der Einrichtungs-Assistent wählt das bei schwacher CPU
von selbst.
- **Andere Maschine komprimieren lassen** — siehe
[Weitere Maschinen als Worker](#weitere-maschinen-als-worker).
---
## Der Alltag
### Rippen
Disc einlegen. Rippy erkennt sie, zeigt Titel und Poster, du wählst das Ziel —
oder du stellst die **Vollautomatik** an (Einstellungen → Ripping) und es
läuft ohne Nachfrage los.
- **Alle Tonspuren und Untertitel bleiben erhalten** (wichtig für Anime/O-Ton).
- **Nur Hauptfilm** auf Wunsch — der längste Titel, Extras bleiben weg.
- **Serien:** Serienname + Staffel angeben → Ablage `<Serie>/Season NN`, und
die Episoden werden per Laufzeitabgleich (TMDB) zu „Serie S01E02.mkv"
benannt. Nur bei eindeutiger Zuordnung, sonst bleiben die Namen — mit Log.
- **Audio-CDs** laufen über abcde zu FLAC mit MusicBrainz-Tags.
- Schon gerippte Discs erkennt Rippy am Fingerabdruck und warnt.
- Vor dem Start wird der freie Platz gegen die Disc-Größe geprüft.
### Kompression
Ein **eigenes Preset je Disc-Typ** (Einstellungen → Verarbeitung) — Rippy
erkennt den Typ selbst. Das ist wichtig, weil ein 1080p-Preset eine 4K-UHD
stillschweigend herunterrechnet und eine DVD sinnlos hochskaliert.
Jeder Typ kann auch auf **„Nicht komprimieren"** stehen: dann bleibt der
verlustfreie Rip liegen. Sinnvoll für 4K, wenn die Ablage groß genug ist.
Die Rohdatei wird erst nach erfolgreicher Kompression gelöscht („Original
behalten" als Option). Fehlgeschlagene Kompressionen lassen sich ohne
Neu-Rip erneut anstoßen.
### Ablage & Media-Server
Einstellungen → Ripping: dein System wählen (Jellyfin, Emby, Kodi, Plex oder
keins). Fertige Rips heißen dann „Titel (Jahr)"; für Jellyfin/Emby/Kodi legt
Rippy zusätzlich `movie.nfo` + `poster.jpg` dazu. Bei **Jellyfin/Emby** mit
Server-URL + API-Key stößt Rippy nach jedem Rip sofort einen
Bibliotheks-Scan an — Disc rein, Film erscheint im Server.
**NAS-Freigaben** hängst du unter Einstellungen → Speicherziele direkt aus dem
UI ein (NFS/SMB). Sie erscheinen sofort in der Ziel-Auswahl und werden beim
Start automatisch wieder verbunden.
⚠️ Zugangsdaten liegen unverschlüsselt in der lokalen Datenbank — bewusster
Heimnetz-Kompromiss. Lege fürs NAS einen eigenen, eingeschränkten Benutzer an.
### Benachrichtigungen
Einstellungen → Benachrichtigungen: eine Webhook-URL eintragen, „Test senden"
drücken, speichern. Rippy erkennt den Dienst an der URL selbst:
| Dienst | URL-Beispiel |
|---|---|
| Discord | `https://discord.com/api/webhooks/…` |
| Slack | `https://hooks.slack.com/services/…` |
| ntfy (Handy-Push) | `https://ntfy.sh/mein-geheimes-thema` |
| Eigenes (Home Assistant, n8n, …) | beliebige HTTPS-URL |
---
## 4K-UHD
4K braucht **zwei** Dinge, die normale BD/DVD nicht brauchen: ein
**LibreDrive-fähiges Laufwerk** (MakeMKV-Forum: „Ultimate UHD Drives Flashing
Guide") **und** den **Schlüssel dieser Pressung**.
**Der Schlüssel ist die eigentliche Hürde — aus einem überraschenden Grund.**
Am 25.07.2026 auf beiden Maschinen gemessen (Akira UHD, MKB v76, Pressung
Dezember 2020): Laufwerk und MakeMKV waren in Ordnung — „Using LibreDrive mode
(v06.3)", die Disc wurde gelesen — und trotzdem kam „The volume key is unknown
for this disc".
> **`makemkvcon` unter Linux holt Disc-Schlüssel nie aus dem Netz.
> Die Windows-Version tut es.**
Gemessen: Linux baute in keinem einzigen Lauf eine Verbindung nach draußen auf
— geprüft mit leerem und gefülltem Schlüsselspeicher, mit und ohne `--noscan`,
mit `dev:` und `disc:`, und mit erzwungener frischer Prüfung. Dieselbe Disc am
selben Laufwerk unter Windows: „Lade aktuelle HK …", Verbindung zum
Schlüssel-Server, Disc geht auf. **Ein MakeMKV-Update ändert daran nichts, und
es ist kein Fehler in Rippy.**
**Der Weg drumherum** (Einstellungen → System):
1. MakeMKV auf einem **Windows-PC** installieren (gleicher Beta-Key).
2. Laufwerk anstecken, Disc **einmal öffnen** — dabei lädt MakeMKV die
Schlüssel nach.
3. In MakeMKV unter *Preferences → General* das „MakeMKV data directory"
nachschlagen und die Datei **`_private_data.tar`** daraus bei Rippy
hochladen.
Rippy zeigt an, **wie viele Disc-Schlüssel** dieser Worker kennt. Steht dort 0,
scheitert jede unbekannte UHD. Für neue Discs den Schritt gelegentlich
wiederholen. Rippy prüft die Datei und lehnt sie ab, wenn kein einziger
Schlüssel drinsteckt — sonst ändert sich nichts und niemand versteht, warum.
**Kennt MakeMKV die Pressung selbst nicht**, bleibt die **`KEYDB.cfg`** als
Notnagel (gleicher Tab). Und: **AACS-Dumps herunterladen** — den Dump legt
MakeMKV bei jeder unbekannten Disc selbst ab, und genau den braucht man, wenn
man im MakeMKV-Forum um den Schlüssel für eine neue Pressung bittet.
> ⚠️ **Rippy liefert keine Disc-Schlüssel mit, lädt keine herunter und
> verteilt keine.** Rippy hält nur den Platz für eine Datei bereit, die du
> selbst mitbringst, und zeigt ehrlich an, was dort liegt. Der
> MakeMKV-**Beta-Key** ist die Lizenz für die *Software* — zwei völlig
> verschiedene Dinge.
⚠️ UHD-Rohdaten sind bis 100 GB groß. Ist die Platte zu klein, lege das
**Arbeitsverzeichnis** (Einstellungen → Verarbeitung) auf eine eingehängte
Freigabe. Rippy bricht sonst **vor** dem Rip mit Klartext ab statt nach 40 GB
mit voller Platte.
---
## Weitere Maschinen als Worker
Die Kompression läuft als eigener Task auf der Queue `transcode` — **jede**
Maschine im Netz kann sie übernehmen. Einstellungen → Worker zeigt für beide
Varianten einen Copy-Paste-Befehl:
- **Windows (nativ, ohne Docker)** — braucht nur Python. Der Installer kommt
als fertige `.exe` von Rippy selbst; Worker-Code und HandBrake holt er zur
Laufzeit.
- **Linux (Docker)** — `deploy/remote-transcode-worker.yml`.
Rippy zeigt für jeden Worker an, **was er wirklich kann**: Encoder, Kernzahl,
Vektorbefehle. Für einen **GPU-Worker** wichtig: das Bild braucht ein
HandBrake mit `nvenc_*` bzw. `qsv_*` — die Anzeige nennt HandBrakes
ungefilterte Auskunft, damit du das nachprüfen kannst. Das mitgelieferte
Linux-Worker-Bild hat **keinen** Hardware-Encoder.
Ohne Zusatz-Worker macht der eingebaute CPU-Worker alles selbst — Rippy bleibt
All-in-one.
---
## Für Fortgeschrittene
<details>
<summary><b>Umgebungsvariablen (.env)</b></summary>
`install.sh` füllt die hostabhängigen Werte selbst. Alles hier ist optional —
UI-Einstellungen überstimmen die Env-Variablen.
| Variable | Zweck |
|---|---|
| `TMDB_API_KEY` | Metadaten (deutsche Texte). Bequemer im Assistenten — beide Key-Arten gehen (v3-Schlüssel und v4-Token) |
| `OMDB_API_KEY` | zweite Metadaten-Quelle (Fallback) |
| `THETVDB_API_KEY` | Serien-Fallback |
| `WORKER_NAME` | Anzeigename des eingebauten Workers (Standard `rippy-hauptworker`) |
| `MAKEMKV_APP_KEY` | MakeMKV-Beta-Key. Bequemer im UI unter Einstellungen → System — gilt ab dem nächsten Rip, ohne Rebuild |
| `MAKEMKV_VERSION` | MakeMKV-Version für den Image-Build |
| `MAKEMKV_URL_BASE` | alternative Download-Quelle für den Build |
| `MAKEMKV_DATA_HOST` | Host-Verzeichnis für MakeMKVs Daten (Standard `/srv/rippy/makemkv`) — Schlüsselspeicher, `KEYDB.cfg`, AACS-Dumps. Persistent, überlebt jeden Rebuild |
| `OPTICAL_SR` / `OPTICAL_SG` | Geräteknoten des Laufwerks. **Findet `install.sh` selbst** |
| `POSTGRES_PASSWORD` | DB-Passwort (Standard `rippy`) |
</details>
<details>
<summary><b>Installation ohne install.sh (von Hand)</b></summary>
Falls du jeden Schritt selbst machen willst:
```bash
cp .env.example .env
mkdir -p /srv/rippy/media /srv/rippy/makemkv
# Laufwerksknoten ermitteln — die sg-Nummer ist je Host anders!
lsscsi -g # zeigt Modell + zugehoerigen /dev/sgN
# in die .env: OPTICAL_SR=/dev/sr0 und OPTICAL_SG=/dev/sgN
# Nur falls 'docker compose up' meldet:
# "path ... is mounted on / but it is not a shared mount"
mount --bind /srv/rippy/media /srv/rippy/media
mount --make-rshared /srv/rippy/media # neustart-fest als systemd-.mount-Unit
docker compose up -d --build docker compose up -d --build
``` ```
Dann `http://<host>` öffnen — der **Einrichtungs-Assistent** startet beim Braucht klassisches Linux-Docker mit `SYS_ADMIN` — nicht Docker Desktop,
ersten Mal automatisch (API-Keys, Verarbeitung, erkannte Hardware). rootless oder Podman. Die Mount-Propagation ist nur für das Einhängen von
NAS-Freigaben **aus dem UI** nötig; Freigaben auf dem Host einhängen geht immer.
### Laufwerk anpassen </details>
Der Worker braucht **zwei** Host-Geräteknoten: den CD-ROM-Knoten (meist <details>
`/dev/sr0`) und den passenden **SCSI-Generic-Knoten** (`/dev/sgN`) — MakeMKV <summary><b>Wie es intern funktioniert</b></summary>
spricht Laufwerke über die sg-Schicht an. Die sg-Nummer ist **je Host anders**
(auf frischen Systemen oft `/dev/sg0`).
Ermitteln, welche Knoten dein Blu-ray/DVD-Laufwerk hat: 1. **Disc-Wache** — ioctl-Polling, kein udev. Im Container läuft kein udevd,
```bash die udev-Datenbank ist leer; `udevadm info` liefert dort prinzipbedingt
lsscsi -g # zeigt Modell + zugehörigen /dev/sgN nichts.
# oder: ls -la /sys/class/scsi_generic/ 2. **Rip** — MakeMKV, verlustfrei. Der einzige Weg durch AACS: HandBrake kann
``` verschlüsselte Discs nicht lesen, deshalb sind es zwingend zwei Stufen.
Dann in der `.env` eintragen (Defaults passen für den Ursprungs-Host): 3. **Kompression** — HandBrake als eigener Celery-Task auf eigener Queue,
```bash deshalb an andere Maschinen routbar.
OPTICAL_SR=/dev/sr0 # CD-ROM-Knoten deines Laufwerks 4. **Metadaten** — Volume-Label → TMDB → OMDb → MyAnimeList, mit
OPTICAL_SG=/dev/sg0 # sg-Knoten deines Laufwerks Ähnlichkeits-Bewertung und Korrektur-Popup an der Disc-Karte.
```
`docker compose up -d` nutzt diese Werte. (Für Feineinstellungen geht weiterhin
eine `docker-compose.override.yml` mit eigenen `devices:`-Einträgen.)
**Laufwerk in einer VM?** Per USB-Passthrough anhand der Vendor-ID </details>
durchreichen (Proxmox: `qm set <vmid> -usb0 host=xxxx:yyyy,usb3=1`) —
NICHT als emuliertes CD-ROM (`media=cdrom`), das kann keine SCSI-Kommandos.
## Wie es funktioniert <details>
<summary><b>Härtung für exponierte Netze</b></summary>
1. **Disc-Wache** (ioctl-Polling, kein udev-Gefrickel) erkennt Einlegen, Rippy ist bewusst **Heimnetz-only**: keine Authentifizierung, und Redis +
identifiziert die Disc (Volume-Label → TMDB → OMDb-Fallback) und zeigt PostgreSQL veröffentlichen Ports für Remote-Worker. Im vertrauten LAN ein
sie mit Poster auf dem Dashboard. Klick auf einen Job-Titel öffnet die akzeptierter Kompromiss — **exponiere Rippy niemals ungeschützt an ein
Detail-Ansicht (Poster, Jahr, Beschreibung, Ablagepfad). öffentliches Netz.**
2. **Rip** (MakeMKV, verlustfrei — der einzige Weg durch AACS): Ziel wählst
du beim Start (Filme/Serien/Musik/eigener Pfad, inkl. Netzwerk-Ziele) —
oder du stellst die **Vollautomatik** an (Einstellungen → Ripping) und
der Rip startet ohne Nachfrage in den passenden Schnellwahl-Ordner.
Optional **nur Hauptfilm** (längster Titel, Extras bleiben weg). Bereits
gerippte Discs erkennt Rippy am Fingerabdruck und warnt. Vor dem Start
prüft Rippy den freien Platz gegen die Disc-Größe. Alle Tonspuren und
Untertitel bleiben erhalten (wichtig für Anime/O-Ton). Audio-CDs laufen
über abcde → FLAC + MusicBrainz.
**Serien**: Im Dialog „Serien" wählen → Serienname + Staffel angeben —
Ablage wird `<Serie>/Season NN`, und die Episoden werden nach dem Rip
per Laufzeitabgleich (TMDB) automatisch zu „Serie S01E02.mkv" benannt
(nur bei eindeutiger Zuordnung — sonst bleiben die Namen, mit Log).
3. **Kompression** (HandBrake, eigener Job auf eigener Queue): x265/x264,
**je Disc-Typ ein eigenes Preset** (Einstellungen → Verarbeitung) —
Rippy kennt den Typ und wählt selbst. Wichtig, weil ein 1080p-Preset
eine 4K-UHD stillschweigend herunterrechnet und ein 1080p-Preset eine
DVD sinnlos hochskaliert. Sind die drei Felder nie gespeichert worden,
gilt weiter das alte allgemeine Preset — Bestandsinstallationen ändern
ihr Verhalten also nicht von selbst.
Rohdatei wird erst nach Erfolg gelöscht
(„Original behalten" als Option). Fehlgeschlagene Kompressionen lassen
sich ohne Neu-Rip neu anstoßen.
4. **Media-Server-Ablage**: Unter Einstellungen → Ripping (oder im
Einrichtungs-Assistenten) wählst du dein System — Jellyfin, Emby, Kodi,
Plex oder keins. Fertige Rips heißen dann „Titel (Jahr)" statt
Job-UUID; für Jellyfin/Emby/Kodi legt Rippy zusätzlich movie.nfo +
poster.jpg dazu (Kodi-NFO-Schema, lesen alle drei nativ). Plex nutzt
nur die Benennung. **Jellyfin/Emby**: Server-URL + API-Key eintragen,
dann stößt Rippy nach jedem fertigen Rip sofort einen Bibliotheks-Scan
an — Disc rein, Film erscheint im Server.
5. **4K-UHD**: braucht **zwei** Dinge — ein LibreDrive-fähiges Laufwerk
(MakeMKV-Forum: „Ultimate UHD Drives Flashing Guide") **und** einen
passenden Disc-Schlüssel. Normale BD/DVD gehen mit jedem Laufwerk und
ohne Schlüssel-Datei.
**Der Schlüssel ist heute die eigentliche Hürde — und zwar aus einem
überraschenden Grund.** Am 25.07.2026 auf beiden Maschinen gemessen
(Akira UHD, MKB v76, Pressung Dezember 2020): Laufwerk und MakeMKV
waren in Ordnung — „Using LibreDrive mode (v06.3)", die Disc wurde
gelesen — und trotzdem kam „The volume key is unknown for this disc".
Die Ursache:
**`makemkvcon` unter Linux holt Disc-Schlüssel nie aus dem Netz. Die
Windows-Version tut es.**
Gemessen: Linux baute in keinem einzigen Lauf eine Verbindung nach
draußen auf — geprüft mit leerem und mit gefülltem Schlüsselspeicher,
mit und ohne `--noscan`, mit `dev:` und `disc:`, und mit erzwungener
frischer Prüfung. Dieselbe Disc am selben Laufwerk unter Windows: „Lade
aktuelle HK …", Verbindung zum Schlüssel-Server, Disc geht auf.
Ein MakeMKV-Update ändert daran nichts, und es ist kein Fehler in
Rippy.
**Der Weg drumherum:** MakeMKV einmal auf einem Windows-PC die Disc
öffnen lassen und die dabei gefüllte Datei `_private_data.tar` bei
Rippy unter **Einstellungen → System** hochladen (siehe unten). Für
Pressungen, die auch MakeMKV nicht kennt, bleibt die **`KEYDB.cfg`**
als Notnagel.
⚠️ **Rippy liefert keine Schlüssel mit, lädt keine herunter und
verteilt keine.** Rippy stellt nur den Platz für eine Datei bereit, die
du selbst mitbringst, und zeigt dir ehrlich an, was dort liegt.
⚠️ UHD-Rohdaten sind bis 100 GB groß — wenn die Platte der
Rippy-Maschine dafür zu klein ist, lege das **Arbeitsverzeichnis**
(Einstellungen → Verarbeitung) auf eine eingehängte Freigabe, z. B.
`/app/media/nas-arbeit`. Rippy bricht sonst VOR dem Rip mit einer
Klartext-Meldung ab statt nach 40 GB mit voller Platte.
## Speicherziele (NAS, Freigaben) - **Zugriff kapseln:** UI und API (`:8000`) hinter einen Reverse-Proxy mit Auth
oder nur über VPN erreichbar machen.
Unter **Einstellungen → Speicherziele** hängst du NFS- oder SMB-Freigaben - **DB/Broker abschotten:** starkes `POSTGRES_PASSWORD`; die Ports
direkt aus dem UI ein — sie erscheinen sofort in der Ziel-Auswahl beim `5432`/`6379` nur an ein internes/VPN-Interface binden statt an `0.0.0.0`;
Rippen und werden beim Start automatisch wieder verbunden. Redis mit `--requirepass` starten und das Passwort in `REDIS_URL` ergänzen.
Technik: der api-Container läuft mit `CAP_SYS_ADMIN` und einem - **CORS:** `main.py` erlaubt `*` (nötig, weil UI und API getrennte Ports
rshared-Bind auf `/srv/rippy/media`, Mounts propagieren zu allen
Containern. ⚠️ Zugangsdaten liegen unverschlüsselt in der lokalen
Postgres-DB — bewusster Heimnetz-Kompromiss; lege fürs NAS einen eigenen,
eingeschränkten Benutzer an.
## Benachrichtigungen
**Einstellungen → Benachrichtigungen**: eine Webhook-URL eintragen, „Test
senden" drücken, speichern — fertig. Rippy meldet Job-Ende (fertig,
fehlgeschlagen, abgebrochen) und erkennt den Dienst an der URL selbst:
| Dienst | URL-Beispiel | Format |
|---|---|---|
| Discord | `https://discord.com/api/webhooks/…` | `{"content": …}` |
| Slack | `https://hooks.slack.com/services/…` | `{"text": …}` |
| ntfy (Handy-Push) | `https://ntfy.sh/mein-geheimes-thema` | Roh-Text + Titel |
| Eigenes (HA, n8n, …) | beliebige HTTPS-URL | `{"title","message","level"}` |
## System, MakeMKV-Beta-Key, Disc-Schlüssel & Updates
**Einstellungen → System** zeigt die Werkzeug-Versionen jedes Workers
(MakeMKV, HandBrake), den Key-Status und den freien Speicherplatz. Der
MakeMKV-Beta-Key (wechselt ~monatlich, Forum-Thread t=1053) wird hier im
UI eingetragen und gilt ab dem **nächsten Rip** — ohne Rebuild, ohne
Neustart; er schlägt den Key aus der `.env`.
**Disc-Schlüssel für 4K-UHD** — im selben Tab, nur für UHD nötig. Der
Hauptweg zuerst:
- **Schlüsselspeicher (`_private_data.tar`)**: Rippy zeigt, **wie viele
Disc-Schlüssel** dieser Worker kennt. Steht dort 0, scheitert jede
unbekannte UHD-Disc.
**So füllst du ihn:** MakeMKV auf einem Windows-PC installieren
(gleicher Beta-Key), Laufwerk anstecken, Disc einmal öffnen — dabei lädt
MakeMKV die Schlüssel nach. Dann in MakeMKV unter *Preferences →
General* das „MakeMKV data directory" nachschlagen und die Datei
`_private_data.tar` daraus hier hochladen. Wirkt ab dem nächsten Rip;
für neue Discs gelegentlich wiederholen.
Rippy prüft die Datei und lehnt sie ab, wenn kein einziger Schlüssel
drinsteckt — sonst ändert sich nichts und niemand versteht, warum.
- **`KEYDB.cfg`** (Notnagel): nur nötig, wenn eine Pressung auch mit
gefülltem Speicher nicht aufgeht, MakeMKV sie also selbst nicht kennt.
Hochladen, Status und Entfernen genau wie oben.
- **AACS-Dumps herunterladen**: MakeMKV legt bei jeder unbekannten Disc
selbst einen Dump ab. Genau den braucht man, wenn man im MakeMKV-Forum
um den Schlüssel für eine neue Pressung bittet — hier holst du ihn dir
aus dem Container, ohne SSH.
Der Beta-Key ist die **Lizenz für die Software**, der Schlüsselspeicher
und die `KEYDB.cfg` sind **Disc-Schlüssel** — zwei völlig verschiedene
Dinge. **Rippy liefert und lädt keine Disc-Schlüssel**, es hält nur ein
persistentes Verzeichnis dafür bereit (`MAKEMKV_DATA_HOST`, siehe Tabelle
unten) — rebuild-fest, damit deine Dateien einen
`docker compose build` überleben. Der Schlüsselspeicher ist der
Zwischenspeicher **deiner eigenen** MakeMKV-Installation.
**Updates:** „Auf Updates prüfen" vergleicht mit makemkv.com und den
offiziellen HandBrake-Releases (12 h gecacht). MakeMKV-Update ohne
Code-Änderung: `MAKEMKV_VERSION=x.y.z` in die `.env`, dann
`docker compose build worker && docker compose up -d worker`.
⚠️ **Richtigstellung (25.07.2026):** Hier stand früher, neue MakeMKV-
Versionen brächten „auch die neueste Disc-Schlüssel-Datenbank mit". Das
ist widerlegt — MakeMKV liefert überhaupt keine Disc-Schlüssel mit, es
holt sie zur Laufzeit. Und die Linux-Version holt sie nie. Ein Update
lohnt für Programmfehler und neue Laufwerke, hilft aber NICHT gegen „The
volume key is unknown". Dafür brauchst du den Schlüsselspeicher (siehe
oben).
HandBrake kommt im Docker-Worker bewusst aus Debian (stabil, hinkt der
offiziellen Version hinterher); der native Windows-Worker nutzt die
aktuelle Version direkt.
## Verarbeitung & Hardware
**Einstellungen → Verarbeitung** zeigt ehrlich an, welche Encoder deine
Worker WIRKLICH haben (CPU x264/x265, VAAPI bei AMD/Intel-GPU, NVENC bei
NVIDIA) — jeder Worker meldet seine Fähigkeiten selbst beim Start.
### Optional: weitere Maschinen als Transcode-Worker
Die Kompression läuft als eigener Celery-Task auf der Queue `transcode`
JEDE Maschine im Netz kann sie übernehmen. **Einstellungen → Worker** zeigt
für beide Varianten einen Copy-Paste-Befehl:
- **Linux (Docker)**: `deploy/remote-transcode-worker.yml`
- **Windows (nativ, ohne Docker)**: braucht nur Python 3.10+ — der
Installer (`install.ps1`) kommt von der Rippy-Instanz selbst
(`/api/worker-setup/windows`), lädt Worker-Code und HandBrakeCLI
automatisch und erzeugt eine `start-worker.bat` (optional `-Autostart`).
Ohne Zusatz-Worker macht der eingebaute CPU-Worker alles selbst — Rippy
bleibt All-in-one.
## Umgebungsvariablen (.env)
| Variable | Pflicht | Zweck |
|---|---|---|
| `TMDB_API_KEY` | empfohlen | Metadaten (deutsche Texte) — alternativ im UI/Wizard; beide Key-Arten gehen (v3-Schlüssel und v4-Token) |
| `WORKER_NAME` | optional | Anzeigename des eingebauten Workers (Standard: rippy-hauptworker) |
| `OMDB_API_KEY` | optional | zweite Metadaten-Quelle (Fallback) |
| `THETVDB_API_KEY` | optional | Serien-Fallback |
| `MAKEMKV_APP_KEY` | optional | MakeMKV-Beta-Key (Forum); DVDs gehen ohne — bequemer: im UI unter Einstellungen → System pflegen |
| `MAKEMKV_URL_BASE` | optional | alternative Download-Quelle für den Image-Build |
| `MAKEMKV_DATA_HOST` | optional | Host-Verzeichnis für MakeMKVs Daten (Default `/srv/rippy/makemkv`) — hier liegen dein Schlüsselspeicher `_private_data.tar`, eine etwaige `KEYDB.cfg` und die AACS-Dumps; persistent, überlebt jeden Rebuild |
| `OPTICAL_SR` | je Host | Host-Pfad des CD-ROM-Knotens (Default `/dev/sr0`) — siehe „Laufwerk anpassen" |
| `OPTICAL_SG` | je Host | Host-Pfad des sg-Knotens des Laufwerks (Default `/dev/sg1`; oft `/dev/sg0`) |
| `POSTGRES_PASSWORD` | optional | DB-Passwort (Default `rippy`) — für exponierte Umgebungen ein starkes Passwort setzen |
UI-Einstellungen (Wizard/Settings) überstimmen die Env-Variablen.
## Rippy woanders bereitstellen
Rippy ist reines Docker Compose — es läuft auf **jedem Linux-Host mit
Docker**, nicht nur auf der Original-VM. Es gibt (noch) keine fertigen
Registry-Images; gebaut wird beim ersten `up` direkt aus dem Repo.
Voraussetzungen auf dem Ziel-Host:
1. Linux (x86_64) mit Docker + Compose-Plugin.
2. Ein optisches Laufwerk, das der Host sieht (`ls /dev/sr* /dev/sg*`).
In einer VM: per **USB-Passthrough** (Vendor-ID) durchreichen, NICHT
als emuliertes CD-ROM — siehe „Laufwerk anpassen" oben.
3. Ablage-Basis anlegen: `mkdir -p /srv/rippy/media` (oder Pfad in
`docker-compose.yml` anpassen). Der Bind nutzt **shared/slave-Propagation**
(fürs In-UI-Einhängen von NAS-Freigaben). Auf den meisten systemd-Linux-Hosts
ist `/` schon `rshared`, dann ist nichts zu tun. Bricht `docker compose up`
mit *„path … is mounted on / but it is not a shared mount"* ab, einmalig:
```bash
sudo mount --bind /srv/rippy/media /srv/rippy/media
sudo mount --make-rshared /srv/rippy/media
```
(reboot-fest als systemd-`.mount`-Unit persistieren.) Braucht einen klassischen
Linux-Docker-Host mit `SYS_ADMIN` — nicht Docker-Desktop/rootless/Podman.
4. MakeMKV-Datenverzeichnis anlegen: `mkdir -p /srv/rippy/makemkv` (oder
eigenen Pfad über `MAKEMKV_DATA_HOST` in der `.env`). Dort liegen
MakeMKVs Programmzustand, die AACS-Dumps und — falls du 4K-UHD rippst —
dein Schlüsselspeicher (`_private_data.tar`) sowie eine etwaige
`KEYDB.cfg`. Das Verzeichnis gehört dem Host, damit ein
`docker compose build` deine Dateien nicht wegwirft.
Dann wie im Schnellstart: klonen, `.env` füllen, `docker compose up -d
--build`, `http://<host>` öffnen — der Einrichtungs-Assistent führt durch
den Rest (API-Keys, Media-Server, Verarbeitung). Updates: `git pull &&
docker compose up -d --build`.
Nicht mitnehmen musst du: Gitea, Arcane, den CI-Runner — das ist die
Entwicklungs-Infrastruktur DIESER Installation, nicht Teil von Rippy.
### Härtung für fremde/exponierte Netze
Rippy ist bewusst **Heimnetz-only**: Es hat **keine Authentifizierung**, und
Redis + PostgreSQL veröffentlichen Ports auf allen Interfaces (für Remote-Worker).
Im vertrauten LAN ist das ein akzeptierter Kompromiss — **exponiere Rippy niemals
ungeschützt an ein unsicheres oder öffentliches Netz.** Wer es weiter absichern will:
- **Zugriff kapseln:** API (`:8000`) und UI hinter einen Reverse-Proxy mit Auth
(Basic-/Forward-Auth) legen oder nur über VPN erreichbar machen; Port `8000`
nicht auf `0.0.0.0` binden.
- **DB/Broker abschotten:** `POSTGRES_PASSWORD` in der `.env` auf ein starkes
Passwort setzen; die Ports `5432`/`6379` nur an ein internes/VPN-Interface binden
(z. B. `"10.0.0.5:5432:5432"`) statt an `0.0.0.0`; Redis mit `--requirepass
<geheim>` starten und das Passwort in `REDIS_URL` ergänzen.
- **CORS:** `main.py` erlaubt aktuell `*` (nötig, weil UI und API getrennte Ports
sind) — hinter einem Proxy auf die echte UI-Herkunft einschränken. sind) — hinter einem Proxy auf die echte UI-Herkunft einschränken.
- **NAS-Zugangsdaten** liegen bewusst im Klartext in der DB — ein weiterer Grund, - **NAS-Zugangsdaten** liegen im Klartext in der DB — ein weiterer Grund, den
den DB-Port nie offen ins unsichere Netz zu hängen. DB-Port nie offen ins unsichere Netz zu hängen.
</details>
<details>
<summary><b>Updates der Werkzeuge</b></summary>
„Auf Updates prüfen" (Einstellungen → System) vergleicht mit makemkv.com und
den offiziellen HandBrake-Releases, 12 h gecacht.
**MakeMKV** aktualisieren: `MAKEMKV_VERSION=x.y.z` in die `.env`, dann
`sudo ./install.sh`. Der Beta-Key wechselt etwa monatlich (Forum-Thread
t=1053) und wird im UI gepflegt — ohne Rebuild.
**HandBrake** kommt im Docker-Worker bewusst aus Debian (stabil, hinkt der
offiziellen Version hinterher); der native Windows-Worker nutzt die aktuelle.
⚠️ Ein MakeMKV-Update hilft **nicht** gegen „The volume key is unknown" —
MakeMKV liefert überhaupt keine Disc-Schlüssel mit, es holt sie zur Laufzeit,
und die Linux-Version holt sie nie. Siehe [4K-UHD](#4k-uhd).
</details>
---
## Entwicklung ## Entwicklung
CI („Ampel") läuft bei jedem Push: Ruff, pytest, Vite-Build. Es gibt genau CI („Ampel") läuft bei jedem Push: Ruff, pytest, Vite-Build. Es gibt genau
einen Branch, `main` (Single Source of Truth) — die Ampel prüft nur, einen Branch, `main` — die Ampel prüft nur, deployt wird direkt aus `main`.
deployt wird direkt aus `main`. Rot heißt: nicht deployen. Rot heißt: nicht deployen.
Regeln für Beiträge: [AGENTS.md](AGENTS.md) · Konzept: [KONZEPT.md](KONZEPT.md) · Regeln für Beiträge: [AGENTS.md](AGENTS.md) · Konzept: [KONZEPT.md](KONZEPT.md) ·
Fahrplan: [ROADMAP.md](ROADMAP.md) Fahrplan: [ROADMAP.md](ROADMAP.md) · Stand: [SAVEPOINT.md](SAVEPOINT.md)
Executable
+303
View File
@@ -0,0 +1,303 @@
#!/usr/bin/env bash
#
# Rippy einrichten — ein Befehl, keine Handarbeit.
#
# Das Skript nimmt genau die Schritte ab, an denen man vorher scheitern konnte:
# * Laufwerk finden (BEIDE Geraeteknoten — der sg-Knoten ist je Host anders,
# und ein fest eingetragener Standard ist damit auf fremden Hosts falsch)
# * Ablage- und MakeMKV-Verzeichnis anlegen
# * shared-Mount-Propagation herstellen, falls Docker sie verlangt
# * .env anlegen bzw. fehlende Werte ergaenzen, ohne Bestehendes zu ueberschreiben
# * bauen und starten
#
# Es ist absichtlich WIEDERHOLBAR: mehrfach ausfuehren aendert nichts kaputt.
# Es aendert NIE einen Wert, der schon in der .env steht.
#
# Aufruf: sudo ./install.sh
# sudo ./install.sh --nur-pruefen (nichts anfassen, nur berichten)
set -u
NUR_PRUEFEN=0
[ "${1:-}" = "--nur-pruefen" ] && NUR_PRUEFEN=1
MEDIA_DIR="${RIPPY_MEDIA_DIR:-/srv/rippy/media}"
MAKEMKV_DIR="${RIPPY_MAKEMKV_DIR:-/srv/rippy/makemkv}"
FEHLER=0
rot() { printf '\033[31m%s\033[0m\n' "$*"; }
gruen() { printf '\033[32m%s\033[0m\n' "$*"; }
gelb() { printf '\033[33m%s\033[0m\n' "$*"; }
titel() { printf '\n\033[1m%s\033[0m\n' "$*"; }
echo "======================================================================"
echo " Rippy — Einrichtung"
echo "======================================================================"
[ "$NUR_PRUEFEN" = 1 ] && gelb "Nur-Pruefen-Modus: es wird nichts geaendert."
# ---------------------------------------------------------------------------
# 1. Voraussetzungen
# ---------------------------------------------------------------------------
titel "1/6 Voraussetzungen"
if [ "$(uname -s)" != "Linux" ]; then
rot " X Das hier ist kein Linux. Rippy braucht einen Linux-Host mit Docker."
rot " (Docker Desktop unter Windows/macOS kann keine optischen Laufwerke"
rot " durchreichen — deshalb geht es dort grundsaetzlich nicht.)"
exit 1
fi
gruen " OK Linux erkannt ($(uname -r))"
if ! command -v docker >/dev/null 2>&1; then
rot " X Docker ist nicht installiert."
rot " Installation: curl -fsSL https://get.docker.com | sudo sh"
FEHLER=1
else
gruen " OK Docker gefunden ($(docker --version 2>/dev/null | head -1))"
fi
if docker compose version >/dev/null 2>&1; then
COMPOSE="docker compose"
gruen " OK Compose-Plugin gefunden"
elif command -v docker-compose >/dev/null 2>&1; then
COMPOSE="docker-compose"
gelb " ! Nur das alte docker-compose gefunden — funktioniert, ist aber veraltet."
else
rot " X Docker Compose fehlt."
rot " Installation (Debian/Ubuntu): sudo apt install docker-compose-plugin"
FEHLER=1
fi
if [ "$(id -u)" = "0" ]; then
gruen " OK Als root gestartet"
elif [ "$NUR_PRUEFEN" = 1 ]; then
# Der Pruef-Modus aendert nichts und darf deshalb NICHT an root scheitern —
# sonst kann man vor der Installation nicht nachsehen, ob alles passt.
gelb " ! Ohne root — fuer die Pruefung in Ordnung. Die echte Installation"
gelb " braucht sudo (Verzeichnisse unter /srv, Mount-Propagation)."
else
rot " X Bitte mit sudo starten: sudo ./install.sh"
rot " (Verzeichnisse unter /srv und die Mount-Propagation brauchen root.)"
rot " Nur nachsehen, ohne etwas zu aendern: ./install.sh --nur-pruefen"
FEHLER=1
fi
[ "$FEHLER" = 1 ] && { rot ""; rot "Abgebrochen — bitte das Fehlende nachholen."; exit 1; }
# ---------------------------------------------------------------------------
# 2. Laufwerk suchen
# ---------------------------------------------------------------------------
titel "2/6 Optisches Laufwerk"
# MakeMKV spricht Laufwerke ueber die SCSI-Generic-Schicht an und braucht
# deshalb ZWEI Knoten: /dev/srN und den passenden /dev/sgM. Welche sg-Nummer
# dazugehoert, ist je Host anders — deshalb wird sie hier ueber die
# SCSI-Adresse ermittelt statt geraten. Beide Knoten zeigen in /sys auf
# dasselbe Geraeteverzeichnis (z. B. 3:0:0:0), das ist der Abgleich.
SR=""
SG=""
LAUFWERK=""
for srpfad in /sys/block/sr*; do
[ -e "$srpfad" ] || continue
srname=$(basename "$srpfad")
adresse=$(basename "$(readlink -f "$srpfad/device" 2>/dev/null)" 2>/dev/null)
[ -n "$adresse" ] || continue
for sgpfad in /sys/class/scsi_generic/sg*; do
[ -e "$sgpfad" ] || continue
if [ "$(basename "$(readlink -f "$sgpfad/device" 2>/dev/null)" 2>/dev/null)" = "$adresse" ]; then
SR="/dev/$srname"
SG="/dev/$(basename "$sgpfad")"
LAUFWERK="$(cat "$srpfad/device/vendor" 2>/dev/null) $(cat "$srpfad/device/model" 2>/dev/null)"
break 2
fi
done
done
if [ -n "$SR" ]; then
gruen " OK Laufwerk gefunden: $(echo "$LAUFWERK" | tr -s ' ')"
gruen " $SR + $SG (SCSI-Adresse abgeglichen, nicht geraten)"
else
gelb " ! Kein optisches Laufwerk gefunden."
gelb " Rippy laesst sich trotzdem installieren und kann dann als reine"
gelb " KOMPRIMIER-Maschine fuer andere Rippy-Instanzen dienen."
gelb " Zum Rippen fehlt das Laufwerk. Haeufigste Ursachen:"
gelb " - In einer VM: Laufwerk per USB-Passthrough durchreichen"
gelb " (Proxmox: qm set <vmid> -usb0 host=<hersteller>:<produkt>,usb3=1)"
gelb " NICHT als emuliertes CD-ROM (media=cdrom) — das kann keine"
gelb " SCSI-Kommandos und MakeMKV sieht es nie."
gelb " - Auf echter Hardware: 'ls /dev/sr*' pruefen, Kabel/Strom."
fi
# ---------------------------------------------------------------------------
# 3. Verzeichnisse
# ---------------------------------------------------------------------------
titel "3/6 Verzeichnisse"
for d in "$MEDIA_DIR" "$MAKEMKV_DIR"; do
if [ -d "$d" ]; then
gruen " OK $d liegt schon da"
elif [ "$NUR_PRUEFEN" = 1 ]; then
gelb " ! $d fehlt (wuerde angelegt)"
else
mkdir -p "$d" && gruen " OK $d angelegt"
fi
done
echo " Ablage: $MEDIA_DIR (hier landen die fertigen Filme)"
echo " MakeMKV-Daten: $MAKEMKV_DIR (Schluesselspeicher, rebuild-fest)"
# ---------------------------------------------------------------------------
# 4. Mount-Propagation
# ---------------------------------------------------------------------------
titel "4/6 Mount-Propagation (fuer NAS-Freigaben aus dem UI)"
# Rippy haengt NAS-Freigaben aus dem UI ein. Damit die auch in den anderen
# Containern sichtbar werden, muss der Ablage-Pfad ein 'shared' Mount sein.
# Auf den meisten systemd-Hosts ist / schon rshared und es ist nichts zu tun.
propagation_ok() {
findmnt -no PROPAGATION --target "$MEDIA_DIR" 2>/dev/null | grep -q shared
}
if propagation_ok; then
gruen " OK $MEDIA_DIR ist bereits 'shared' — nichts zu tun"
elif [ "$NUR_PRUEFEN" = 1 ]; then
gelb " ! Propagation fehlt (wuerde eingerichtet)"
else
mount --bind "$MEDIA_DIR" "$MEDIA_DIR" 2>/dev/null
mount --make-rshared "$MEDIA_DIR" 2>/dev/null
if propagation_ok; then
gruen " OK Propagation eingerichtet"
# Neustart-fest machen, sonst ist es nach dem naechsten Reboot wieder weg
# und das Einhaengen aus dem UI scheitert stillschweigend.
UNIT="/etc/systemd/system/$(systemd-escape -p --suffix=mount "$MEDIA_DIR" 2>/dev/null)"
if [ -n "${UNIT:-}" ] && [ ! -e "$UNIT" ] && command -v systemctl >/dev/null 2>&1; then
cat > "$UNIT" <<UNITEOF
[Unit]
Description=Rippy-Ablage als shared mount (fuer NAS-Freigaben aus dem UI)
After=local-fs.target
[Mount]
What=$MEDIA_DIR
Where=$MEDIA_DIR
Type=none
Options=bind,rshared
[Install]
WantedBy=multi-user.target
UNITEOF
systemctl daemon-reload 2>/dev/null
systemctl enable "$(basename "$UNIT")" >/dev/null 2>&1 \
&& gruen " OK Neustart-fest gemacht ($(basename "$UNIT"))" \
|| gelb " ! Konnte nicht neustart-fest gemacht werden — nach einem Reboot"
gelb " ggf. dieses Skript erneut ausfuehren."
fi
else
gelb " ! Propagation liess sich nicht einrichten."
gelb " Rippy laeuft trotzdem — nur das Einhaengen von NAS-Freigaben AUS DEM UI"
gelb " funktioniert dann nicht. Freigaben stattdessen auf dem Host einhaengen,"
gelb " z. B. unter $MEDIA_DIR/nas. Betrifft nur klassisches Docker mit"
gelb " SYS_ADMIN — nicht rootless/Podman."
fi
fi
# ---------------------------------------------------------------------------
# 5. .env
# ---------------------------------------------------------------------------
titel "5/6 Konfiguration (.env)"
cd "$(dirname "$0")" || exit 1
if [ ! -f .env ]; then
if [ "$NUR_PRUEFEN" = 1 ]; then
gelb " ! .env fehlt (wuerde aus .env.example erzeugt)"
else
cp .env.example .env && gruen " OK .env aus .env.example erzeugt"
fi
else
gruen " OK .env liegt schon da — bestehende Werte bleiben unangetastet"
fi
# Setzt einen Wert NUR, wenn er noch nicht (unkommentiert) gesetzt ist.
setze_wenn_leer() {
schluessel="$1"; wert="$2"
[ "$NUR_PRUEFEN" = 1 ] && { echo " wuerde setzen: $schluessel=$wert"; return; }
[ -f .env ] || return
if grep -qE "^${schluessel}=.+" .env; then
echo " $schluessel steht schon in der .env — unveraendert gelassen"
else
sed -i "/^#*${schluessel}=/d" .env
printf '%s=%s\n' "$schluessel" "$wert" >> .env
gruen " OK $schluessel=$wert eingetragen"
fi
}
if [ -n "$SR" ]; then
setze_wenn_leer OPTICAL_SR "$SR"
setze_wenn_leer OPTICAL_SG "$SG"
fi
setze_wenn_leer MAKEMKV_DATA_HOST "$MAKEMKV_DIR"
echo " API-Keys (TMDB/OMDb) brauchst du hier NICHT einzutragen —"
echo " der Einrichtungs-Assistent im Browser fragt sie ab und prueft sie."
# Gegenprobe: Zeigen die WIRKSAMEN Werte auf Geraete, die es hier gibt? Faengt
# den Fall "'.env' von einem anderen Rechner kopiert" und Tippfehler ab — sonst
# startet Docker mit einem Pfad, den es nicht gibt, und die Meldung ist kryptisch.
if [ -f .env ]; then
for paar in "OPTICAL_SR:/dev/sr0" "OPTICAL_SG:/dev/sg1"; do
schluessel="${paar%%:*}"; standard="${paar##*:}"
wirksam=$(grep -E "^${schluessel}=" .env 2>/dev/null | tail -1 | cut -d= -f2-)
[ -n "${wirksam:-}" ] || wirksam="$standard"
if [ ! -e "$wirksam" ]; then
gelb " ! $schluessel zeigt auf $wirksam — dieses Geraet gibt es hier NICHT."
if [ -n "$SR" ]; then
gelb " Gefunden wurde stattdessen: $SR + $SG"
gelb " Trage das in der .env ein (oder loesche die Zeile $schluessel=)."
else
gelb " Es wurde auch kein anderes Laufwerk gefunden — siehe Schritt 2."
fi
fi
done
fi
# ---------------------------------------------------------------------------
# 6. Bauen und starten
# ---------------------------------------------------------------------------
titel "6/6 Bauen und starten"
if [ "$NUR_PRUEFEN" = 1 ]; then
gelb " ! Uebersprungen (Nur-Pruefen-Modus)."
echo ""
gruen "Pruefung fertig. Ohne --nur-pruefen wuerde jetzt gebaut und gestartet."
exit 0
fi
echo " Der erste Bau dauert je nach Internet 3-10 Minuten. Nur beim ersten Mal."
echo ""
if ! $COMPOSE up -d --build; then
rot ""
rot "Der Start ist gescheitert. Die haeufigsten Ursachen:"
rot " - Kein Platz auf der Platte: df -h /"
rot " - Port 80 schon belegt: ss -tlnp | grep ':80 '"
rot " - Download von makemkv.com gedrosselt (Cloudflare): Tarballs von"
rot " makemkv.com/download nach docker/worker/vendor/ legen, dann erneut."
rot "Die volle Meldung steht oben. Logs: $COMPOSE logs --tail=50"
exit 1
fi
IP=$(ip -4 route get 1.1.1.1 2>/dev/null | grep -oE 'src [0-9.]+' | awk '{print $2}')
[ -n "${IP:-}" ] || IP="<ip-dieses-rechners>"
echo ""
echo "======================================================================"
gruen " Fertig. Rippy laeuft."
echo "======================================================================"
echo ""
echo " Jetzt im Browser oeffnen: http://$IP"
echo ""
echo " Dort startet der Einrichtungs-Assistent. Er zeigt dir, ob Laufwerk,"
echo " Worker und Platz in Ordnung sind, und stellt die Kompression passend"
echo " zu deiner CPU ein. Mehr ist nicht zu tun."
echo ""
echo " Status ansehen: $COMPOSE ps"
echo " Logs ansehen: $COMPOSE logs -f"
echo " Aktualisieren: git pull && sudo ./install.sh"
echo ""