Etappe 10: Worker rippt wirklich — MakeMKV 1.18.4 + ioctl-Disc-Erkennung

Vorher: Dockerfile unbaubar (makepkg ist ein Arch-Paket, existiert in Debian
nicht) und KEIN einziges Ripping-Tool im Image — jeder Rip endete sofort.
Disc-Erkennung via `file -L` konnte auf Block-Devices strukturell nie etwas
erkennen; ihr Test mockte sich die Ausgabe passend.

- makemkv-oss/bin 1.18.4 multi-stage (bookworm-gepinnt), EULA via
  tmp/eula_accepted, Beta-Key aus MAKEMKV_APP_KEY (entrypoint.sh)
- makemkvcon-Aufruf + PRGV-Parsing laut makemkv.com/developers/usage.txt
  (AGENTS Regel D), HandBrake raus aus dem Ripp-Pfad (KONZEPT: lossless=Muss)
- detection.py: CDROM_DISC_STATUS + BLKGETSIZE64 (cd/dvd/bluray), pure
  classify() mit ehrlichen Tests
- tasks.py: rip_disc als einziger Celery-Task, schreibt Status/Fortschritt
  nach Postgres (db.py), Ausgabe auf /app/media (Volume) statt totem /output

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-23 15:02:13 +02:00
parent b80b7681c5
commit 780d114fe4
9 changed files with 458 additions and 167 deletions
+31 -48
View File
@@ -1,25 +1,43 @@
"""Tests für ripping.py: Fortschritts-Parsing, Kommando-Bau, Disc-Erkennung.
"""Tests für ripping.py: Kommando-Bau und Fortschritts-Parsing.
Deckt genau die Stellen ab, an denen im Review 22.07. erfundene Schnittstellen
gefunden wurden (abcde-Flags, Celery-API) — damit so etwas nie wieder still liegt.
Deckt genau die Stellen ab, an denen Reviews erfundene Schnittstellen fanden
(abcde-Flags, Celery-API, HandBrake-Regex) — damit so etwas nie wieder still liegt.
Das PRGV-Format stammt aus der MakeMKV-Doku (makemkv.com/developers/usage.txt).
"""
import os
import ripping
from ripping import (
build_abcde_cmd,
build_handbrake_cmd,
detect_disc_type,
get_progress_from_line,
build_makemkv_cmd,
get_progress_from_prgv,
write_abcde_config,
)
def test_progress_parsing():
assert get_progress_from_line("Encoding: task 1 of 1, 45.50 %") == 45
assert get_progress_from_line("Encoding: task 1 of 1, 100.00 %") == 100
assert get_progress_from_line("kein Fortschritt hier") == 0
def test_makemkv_cmd_vollstaendig():
cmd = build_makemkv_cmd("/dev/sr0", "/app/media/dvd/x")
assert cmd[0] == "makemkvcon"
assert "-r" in cmd # Robot-Mode: maschinenlesbar
assert "--progress=-same" in cmd # Fortschritt im selben Stream
assert "mkv" in cmd
assert "dev:/dev/sr0" in cmd # Geräte-Notation laut Doku
assert cmd[-2:] == ["all", "/app/media/dvd/x"]
def test_prgv_parsing():
# PRGV:current,total,max — total/max ist der Gesamtfortschritt
assert get_progress_from_prgv("PRGV:100,32768,65536") == 50
assert get_progress_from_prgv("PRGV:0,65536,65536") == 100
assert get_progress_from_prgv("PRGV:0,0,65536") == 0
def test_prgv_parsing_ignoriert_fremde_zeilen():
# -1 heißt „keine Fortschrittszeile" — MSG-Zeilen dürfen NIE als 0% gelten,
# sonst springt die Anzeige ständig auf null zurück.
assert get_progress_from_prgv('MSG:1005,0,1,"MakeMKV gestartet","%1","x"') == -1
assert get_progress_from_prgv("irgendwas") == -1
assert get_progress_from_prgv("PRGV:kaputt") == -1
def test_abcde_cmd_hat_genau_ein_ausgabeformat():
@@ -34,46 +52,11 @@ def test_abcde_cmd_hat_genau_ein_ausgabeformat():
def test_abcde_config_enthaelt_zielverzeichnis():
pfad = write_abcde_config("/output/cd/test123")
pfad = write_abcde_config("/app/media/cd/test123")
try:
with open(pfad, encoding="utf-8") as f:
inhalt = f.read()
assert "OUTPUTDIR='/output/cd/test123'" in inhalt
assert "OUTPUTDIR='/app/media/cd/test123'" in inhalt
assert "INTERACTIVE=n" in inhalt
finally:
os.unlink(pfad)
def test_handbrake_cmd_vollstaendig():
cmd = build_handbrake_cmd("/dev/sr0", "/output/dvd/x/film.mkv")
assert cmd[0] == "HandBrakeCLI"
assert cmd[cmd.index("--input") + 1] == "/dev/sr0"
assert cmd[cmd.index("--output") + 1] == "/output/dvd/x/film.mkv"
assert "--preset" in cmd
class _FakeResult:
def __init__(self, stdout):
self.stdout = stdout
def test_disc_typ_erkennung(monkeypatch):
faelle = [
("UDF filesystem data 'MEIN_FILM' DVD Video", "dvd"),
("data, BDMV bluray structure", "bluray"),
("Audio CD, cda tracks", "cd"),
("irgendwas anderes", "unknown"),
]
for ausgabe, erwartet in faelle:
monkeypatch.setattr(
ripping.subprocess, "run", lambda *a, _out=ausgabe, **k: _FakeResult(_out)
)
assert detect_disc_type("/dev/sr0") == erwartet
def test_disc_typ_erkennung_fehler_gibt_unknown(monkeypatch):
def kaputt(*a, **k):
raise OSError("kein Geraet")
monkeypatch.setattr(ripping.subprocess, "run", kaputt)
assert detect_disc_type("/dev/sr0") == "unknown"