Files
rippy/docker-compose.yml
T
Hitonabi 8eb5653848
Ampel / ampel (push) Successful in 55s
Restefeger: Auth komplett raus, Serien-Flow + Episoden-Matching, Jellyfin-Refresh, Duplikat-Warnung, echtes Nur-Hauptfilm
AUTH ENTFERNT (Commander-Entscheid 24.07., KONZEPT §10): /token- und
/api-keys-Endpoints, auth.py, test_auth.py, passlib/bcrypt/PyJWT/
python-multipart, JWT_SECRET_KEY-Pflicht. Heimnetz-only, das UI hatte nie
einen Login — die Auth-Oberflaeche war Placebo und die passlib/bcrypt-
Falle brach die Ampel. Rate-Limit pro IP bleibt. Schnellstart laeuft
jetzt ganz ohne .env-Pflichtwerte.

Serien-Flow (Etappe-12-Kern, ARM-Wunde #395):
- Rip-Dialog: Serienname + Staffel -> Ablage <Serie>/Season NN
  (jellyfin.org/docs Naming-Schema); tvshow.nfo + poster.jpg im
  Serien-Ordner, bei Staffel 2 nicht ueberschrieben.
- Episoden-Matching per Laufzeitabgleich: HandBrakeCLI --scan
  ('+ duration:', handbrake.fr/docs) je MKV gegen TMDB-Staffel-Laufzeiten
  (GET /metadata/tv/{id}/season/{n}; tv-season-details-API).
  Ordnungserhaltend; komplette Staffel auf einer Disc klappt auch bei
  uniformen Anime-Laufzeiten (Sequenz-Stufe). Umbenannt wird NUR bei
  eindeutiger Zuordnung — sonst ehrliches Log. Mit Tests.

Weitere Punkte:
- Jellyfin/Emby-Bibliotheks-Refresh nach jedem fertigen Rip
  (POST /Library/Refresh, X-Emby-Token lt. jellyfin.org/docs) —
  URL/Key + Test-Knopf in Einstellungen -> Ripping.
- Duplikat-Warnung: Disc-Fingerabdruck (jetzt Teil des Prescan-Ergebnisses
  + der Job-Metadaten) gegen die Historie; Karte zeigt 'bereits gerippt',
  Vollautomatik ueberspringt Duplikate.
- 'Nur Hauptfilm' ECHT: makemkvcon info -> TINFO-Attr-9-Laufzeiten
  (usage.txt) -> laengster Titel -> mkv dev:X <nr>. Vorher wirkungsloses
  Setting; pro Rip im Dialog uebersteuerbar. Mit Tests.
- OMDb-Treffer eingedeutscht via TMDB /find (external_source=imdb_id,
  de-DE; find-by-id-API).
- Dashboard: Speicherplatz-Anzeige (amber < 60 GB) + CSV-Export
  (GET /jobs/export, Semikolon+BOM fuer deutsches Excel).
- Metadaten-Seite entfernt (Abnahme durch Commander-Auftrag) inkl.
  Placebo-Endpoints /metadata/lookup (scannte Dummy-Device) und
  /metadata/confirm (schrieb nie gelesenen Cache-Key).
- Doppel-Jahr-Fix: 'X (2009) (2009)' in Log und Ordnernamen.
- Remote-Worker-Blocker: redis (6379) + postgres (5432) waren NIE
  veroeffentlicht — kein Remote-Worker konnte sich je verbinden. Ports
  jetzt offen (Heimnetz-Kompromiss, kommentiert) + API_URL fuer Worker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:22:19 +02:00

174 lines
5.3 KiB
YAML

# Docker Compose für Rippy
#
# Geräte-Zugriff (22.07./23.07.-Lehre): Optische Laufwerke MÜSSEN als devices:
# eingebunden werden. Bind-Mounts unter volumes: geben dem Container zwar den
# Geräteknoten, aber KEINE Berechtigung im Device-Cgroup → jedes open() scheitert
# mit EPERM. Voraussetzung: Die VM sieht das Laufwerk (USB-Passthrough auf pve).
services:
api:
build:
context: .
dockerfile: docker/api/Dockerfile
environment:
- DATABASE_URL=postgresql://rippy:rippy@postgres:5432/rippy
- REDIS_URL=redis://redis:6379/0
- TMDB_API_KEY=${TMDB_API_KEY:-}
- THETVDB_API_KEY=${THETVDB_API_KEY:-}
- OMDB_API_KEY=${OMDB_API_KEY:-}
- LOG_LEVEL=INFO
healthcheck:
test: ["CMD-SHELL", "python -c 'import urllib.request; urllib.request.urlopen(\"http://localhost:8000/health\")'"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
ports:
- "8000:8000"
# SYS_ADMIN: die API hängt Netzwerk-Speicherziele (NFS/SMB) selbst ein
# (mounts.py) — dank rshared-Propagation unten sehen Host UND Worker
# jeden Mount sofort.
# DAC_READ_SEARCH: mount.cifs hebt diese Capability an (toggle_dac_
# capability) — sie fehlt in Dockers Default-Set, ohne sie stirbt JEDER
# SMB-Mount mit "Unable to apply new capability set" (Befund 24.07.,
# auf der VM reproduziert und mit dieser Capability bewiesen behoben).
cap_add:
- SYS_ADMIN
- DAC_READ_SEARCH
security_opt:
- apparmor:unconfined
volumes:
# /srv/rippy/media auf der VM statt anonymem Volume: rshared lässt
# Mounts aus DIESEM Container zum Host und in andere Container
# propagieren (Commander-Anforderung 23.07.: Ziele frei wählbar).
- type: bind
source: /srv/rippy/media
target: /app/media
bind:
propagation: rshared
- temp:/app/temp
devices:
- /dev/sr0:/dev/sr0
networks:
- rippy-net
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
read_only: false
worker:
build:
context: .
dockerfile: docker/worker/Dockerfile
args:
# Übersteuerbar via .env — makemkv.com ist zeitweise down (Cloudflare
# 525); dann Wayback-Snapshot als MAKEMKV_URL_BASE eintragen.
# /download = aktuelle Version, /download/old = ältere Versionen.
MAKEMKV_URL_BASE: ${MAKEMKV_URL_BASE:-https://www.makemkv.com/download}
environment:
- DATABASE_URL=postgresql://rippy:rippy@postgres:5432/rippy
- REDIS_URL=redis://redis:6379/0
- RIP_OUTPUT_DIR=/app/media
- MAKEMKV_APP_KEY=${MAKEMKV_APP_KEY}
# Anzeigename in Einstellungen → Worker (stabil über Rebuilds hinweg;
# via .env übersteuerbar, z. B. WORKER_NAME=wohnzimmer-vm)
- WORKER_NAME=${WORKER_NAME:-rippy-hauptworker}
# Für Serien-Episoden-Matching: der Worker fragt die API nach den
# Episoden-Laufzeiten der Staffel (TMDB).
- API_URL=http://api:8000
- LOG_LEVEL=INFO
volumes:
- type: bind
source: /srv/rippy/media
target: /app/media
bind:
propagation: rslave
- temp:/app/temp
devices:
- /dev/sr0:/dev/sr0
# MakeMKV spricht Laufwerke über die SCSI-Generic-Schicht an — ohne den
# sg-Knoten findet es „keine usable optical drives" (Deploy-Befund 23.07.).
# In dieser VM: sg0 = Systemplatte, sg1 = das BD-Laufwerk. Achtung: nach
# einem USB-Reconnect zur Laufzeit kann die sg-Nummer wandern → Container
# neu starten.
- /dev/sg1:/dev/sg1
networks:
- rippy-net
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
# read_only bleibt Ziel (Etappe 6), aber MakeMKV/abcde brauchen HOME +
# Settings — bis zur Härtung schreibbar, Ausgabe geht ohnehin aufs Volume.
read_only: false
tmpfs:
- /app/tmp
- /run
- /tmp
ui:
build:
context: .
dockerfile: docker/ui/Dockerfile
ports:
- "80:80"
networks:
- rippy-net
depends_on:
- api
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_USER=rippy
- POSTGRES_PASSWORD=rippy
- POSTGRES_DB=rippy
# Veröffentlicht (Befund 24.07.): Remote-Worker (GPU-Maschine, künftiger
# Windows-Worker) verbinden sich über <rippy-host>:5432/6379 — ohne
# ports: konnte sich NIE ein Remote-Worker anbinden. Heimnetz-Kompromiss,
# wie die Klartext-Credentials (README).
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- rippy-net
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rippy -d rippy"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
# Siehe postgres: Pflicht für Remote-Worker (Celery-Broker).
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- rippy-net
restart: unless-stopped
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
rippy-net:
driver: bridge
volumes:
temp:
postgres-data:
redis-data: