4195854bf8
Vier neue reine Bausteine, jeder mit Tests. Sie beantworten Fragen, die Rippy bisher geraten oder gar nicht gestellt hat. 1. caps.parse_preset_liste - die Preset-NAMEN, die das HandBrake DIESES Workers wirklich kennt (`--preset-list`, Format im Worker-Image gemessen). Damit endet das Raten: die Namen unterscheiden sich je HandBrake-Version, und ein erfundener Name laesst die Kompression scheitern. Richtigstellung zum SAVEPOINT v3.16: Dort galt es als unmoeglich, die Hardware-Preset-Namen auf der Rippy-VM zu ermitteln, weil dort kein Hardware-Encoder laeuft. Gemessen ist das falsch - die Kategorie `Hardware/` steht vollstaendig in der Liste (VCN, NVENC, QSV, MF). HandBrake trennt zwei Fragen: --preset-list nennt alle Presets, --help nur die nutzbaren Encoder. Zwei Fragen, zwei Quellen. 2. mounts.pfad_map_vorschlag/pfad_map_zeile - der fehlende Anschluss fuer RIPPY_PATH_MAP. Geraten werden muss dafuer nichts: Rippy hat die Freigabe selbst eingehaengt und kennt ihre Quelle (//host/share). Mountpunkt plus Quelle IST das Mapping. NFS gibt bewusst "" - Windows-Schreibweise ist nicht ableitbar (AGENTS Regel D). Der Test fand dabei sofort die dokumentierte Windows-Falle: os.path.join baute `/app/media\rippy` in einen Container-Pfad, das Mapping waere still wirkungslos geblieben. _mountpoint nutzt jetzt posixpath. 3. presets.empfehlung - "immer das Beste" (Commander-Anforderung) ohne Punktesystem: fuer jede Lage eine feste Reihenfolge echter Namen, genommen wird der erste, den der Worker kennt. Hardware nur, wenn die Familie wirklich gemeldet ist (vce -> Preset heisst VCN, sonst liefe eine AMD-Karte unter dem Intel-Namen). vaapi und MF werden nie empfohlen: fuer vaapi gibt es kein Preset, bei MF ist die Nutzbarkeit nicht ablesbar. 4. eta - Restzeit aus dem gemessenen Fortschritt. Messreihe je Phase (Rip und Kompression haben nichts miteinander zu tun), Stillstand verlaengert die Schaetzung, und unter zwei Messwerten oder 60 Sekunden Spanne gibt es ehrlich keine Aussage. Test mit den echten 25.07.-Werten: 1,44 % in 29 min landet bei "noch ca. 1 Tag 23 h" - genau die Angabe, deren Fehlen den 50-Stunden-Lauf unsichtbar machte. Dazu: HandBrakes "Invalid preset <Name>" wird uebersetzt. Vorher stand im UI nur "HandBrake endete mit Code 3" - dass der Preset-NAME das Problem ist, war daraus nicht zu erraten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
217 lines
8.8 KiB
Python
217 lines
8.8 KiB
Python
"""Tests der Fähigkeiten-Erkennung — reine Funktionen, keine Infrastruktur.
|
|
|
|
Die Testdaten sind ECHTE Ausgaben, am 25.07.2026 im Worker-Image der Rippy-VM
|
|
abgenommen (AGENTS Regel D). Erfundene Formate hätten hier keinen Wert: genau
|
|
daran ist die Erkennung vorher gescheitert.
|
|
"""
|
|
|
|
import caps
|
|
|
|
# Wörtlich aus `docker exec rippy-worker-1 HandBrakeCLI --help` (HandBrake im
|
|
# Worker-Image, 25.07.2026). Bemerkenswert: KEIN Hardware-Encoder dabei.
|
|
HB_HILFE_ECHT = """
|
|
-Z, --preset <string> Select preset by name
|
|
-e, --encoder <string> Select video encoder:
|
|
svt_av1
|
|
svt_av1_10bit
|
|
x264
|
|
x264_10bit
|
|
x265
|
|
x265_10bit
|
|
x265_12bit
|
|
mpeg4
|
|
mpeg2
|
|
VP8
|
|
VP9
|
|
VP9_10bit
|
|
theora
|
|
--encoder-preset <string>
|
|
Adjust video encoding settings
|
|
"""
|
|
|
|
# Wie dieselbe Stelle auf einer Maschine MIT Hardware-Encodern aussieht
|
|
# (HandBrake-Windows-Build; Struktur identisch, nur mehr Namen).
|
|
HB_HILFE_MIT_HARDWARE = """
|
|
-e, --encoder <string> Select video encoder:
|
|
x264
|
|
x265
|
|
nvenc_h264
|
|
nvenc_h265
|
|
qsv_h264
|
|
qsv_h265
|
|
--encoder-preset <string>
|
|
"""
|
|
|
|
|
|
def test_encoder_liste_wird_aus_echter_hilfe_gelesen():
|
|
encoder = caps.parse_encoder_liste(HB_HILFE_ECHT)
|
|
assert encoder == [
|
|
"svt_av1", "svt_av1_10bit",
|
|
"x264", "x264_10bit",
|
|
"x265", "x265_10bit", "x265_12bit",
|
|
"mpeg4", "mpeg2", "VP8", "VP9", "VP9_10bit", "theora",
|
|
]
|
|
# Der Block endet an der nächsten Option — sie darf nicht mitgelesen werden
|
|
assert not any("encoder-preset" in e for e in encoder)
|
|
assert not any(e.startswith("-") for e in encoder)
|
|
|
|
|
|
def test_encoder_liste_ohne_handbrake_ist_leer():
|
|
assert caps.parse_encoder_liste("") == []
|
|
assert caps.parse_encoder_liste(None) == []
|
|
assert caps.parse_encoder_liste("irgendein Text ohne Encoder-Abschnitt") == []
|
|
|
|
|
|
def test_ohne_handbrake_keine_encoder():
|
|
"""Der wichtigste Fall: vorher standen cpu-x264/cpu-x265 fest verdrahtet in
|
|
der Liste. Ein Rip-Worker ohne HandBrake behauptete damit, komprimieren zu
|
|
können — und jeder Transcode dort endete sofort mit 'nicht installiert'."""
|
|
assert caps.leite_backends_ab([]) == []
|
|
assert caps.leite_backends_ab(None) == []
|
|
|
|
|
|
def test_backends_aus_echter_liste():
|
|
"""Das Worker-Image der VM kann laut eigener Auskunft keine Hardware —
|
|
entsprechend steht hier nur CPU."""
|
|
encoder = caps.parse_encoder_liste(HB_HILFE_ECHT)
|
|
assert caps.leite_backends_ab(encoder) == ["cpu-x264", "cpu-x265", "cpu-av1"]
|
|
|
|
|
|
def test_handbrakes_liste_ist_die_auskunft_ohne_geraetepruefung():
|
|
"""Regel-Wechsel 25.07.2026, weil die alte Fassung einen Bug hatte.
|
|
|
|
Vorher wurde zusätzlich ein Gerät verlangt: /dev/dri für VAAPI/QSV/VCE bzw.
|
|
nvidia-smi für NVENC. Unter WINDOWS gibt es beides nicht — der PC des
|
|
Commanders (RX 9070 XT) meldete deshalb nur CPU-Encoder, obwohl HandBrakes
|
|
Windows-Build vce_*/nvenc_*/qsv_* beherrscht. Der Bug traf genau den
|
|
Anwendungsfall, für den externe Worker gedacht sind.
|
|
|
|
Die Prüfung war zudem überflüssig: HandBrake probiert Hardware beim Start
|
|
selbst an und listet nur Nutzbares (auf der VM belegt — 'qsv: not available
|
|
on this system', und qsv_* fehlt dann in --help). Wer die Liste hat, hat die
|
|
Antwort; eine zweite Meinung braucht es nicht.
|
|
"""
|
|
mit_hw = caps.parse_encoder_liste(HB_HILFE_MIT_HARDWARE)
|
|
backends = caps.leite_backends_ab(mit_hw)
|
|
assert "nvenc" in backends
|
|
assert "qsv" in backends
|
|
# Ohne Geräteprüfung — es gibt keine Parameter mehr, die das verhindern
|
|
assert caps.leite_backends_ab(mit_hw) == backends
|
|
|
|
|
|
def test_hardware_wird_nach_familie_unterschieden():
|
|
"""Vorher landete alles in „vaapi" — eine AMD-Karte lief damit unter dem
|
|
Intel-Namen, und das UI konnte nicht sagen, welches Preset passt."""
|
|
amd = caps.leite_backends_ab(["x265", "vce_h264", "vce_h265"])
|
|
assert "vce" in amd
|
|
assert "qsv" not in amd and "nvenc" not in amd
|
|
|
|
intel = caps.leite_backends_ab(["x265", "qsv_h265"])
|
|
assert "qsv" in intel and "vce" not in intel
|
|
|
|
|
|
def test_hardware_av1_bekommt_eigene_kennung():
|
|
"""AV1 in Hardware ist derzeit die beste Kombination aus Tempo und
|
|
Dateigröße — das darf nicht in „nvenc" verschwinden, sonst kann das UI es
|
|
nicht empfehlen."""
|
|
backends = caps.leite_backends_ab(["x265", "nvenc_h265", "nvenc_av1"])
|
|
assert "nvenc" in backends
|
|
assert "nvenc-av1" in backends
|
|
# Ohne AV1 in der Liste gibt es die Kennung auch nicht
|
|
assert "nvenc-av1" not in caps.leite_backends_ab(["nvenc_h265"])
|
|
|
|
|
|
def test_simd_stufe_erkennt_die_hoechste():
|
|
assert caps.beste_simd("fpu vme de sse4_2 avx avx2") == "avx2"
|
|
assert caps.beste_simd("sse4_2 avx avx2 avx512f") == "avx512f"
|
|
assert caps.beste_simd("fpu vme de pse tsc sse4_2") == "sse4_2"
|
|
assert caps.beste_simd("") == "unbekannt"
|
|
assert caps.beste_simd(None) == "unbekannt"
|
|
|
|
|
|
def test_simd_stufe_der_rippy_vm_ist_sse4_2():
|
|
"""Echt gemessen auf der Rippy-VM (25.07.2026): generisches QEMU-CPU-Modell,
|
|
`grep -c avx2 /proc/cpuinfo` = 0. Genau das macht Software-4K dort so lahm."""
|
|
flags_vm = (
|
|
"fpu de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 "
|
|
"clflush mmx fxsr sse sse2 syscall nx lm rep_good nopl cpuid "
|
|
"pni cx16 sse4_1 sse4_2 x2apic popcnt aes hypervisor lahf_lm"
|
|
)
|
|
assert caps.beste_simd(flags_vm) == "sse4_2"
|
|
|
|
|
|
# --- Preset-Liste: die Namen, die HandBrake wirklich kennt -------------------
|
|
|
|
# Wörtlich aus `HandBrakeCLI --preset-list` im Worker-Image (26.07.2026,
|
|
# HandBrake 1.6.1) — gekürzt auf je zwei Presets pro Kategorie, Struktur und
|
|
# Einrückung unverändert. Die zwei Logzeilen oben stehen dort wirklich mit
|
|
# drin (HandBrake schreibt sie in denselben Strom) und müssen wegfallen.
|
|
HB_PRESETS_ECHT = """[10:26:05] Compile-time hardening features are enabled
|
|
[10:26:05] qsv: not available on this system
|
|
Cannot load libnvidia-encode.so.1
|
|
General/
|
|
Very Fast 2160p60 4K AV1
|
|
AV1 video (up to 2160p60) and AAC stereo audio, in an MP4
|
|
container.
|
|
Super HQ 1080p30 Surround
|
|
High quality H.264 video (up to 1080p30) and high bit rate AAC
|
|
surround audio, in an MP4 container.
|
|
Matroska/
|
|
H.265 MKV 2160p60 4K
|
|
H.265 video (up to 2160p60) and surround audio, in an MKV
|
|
container.
|
|
H.265 MKV 1080p30
|
|
H.265 video (up to 1080p30) and surround audio, in an MKV
|
|
container.
|
|
Hardware/
|
|
AV1 QSV 2160p 4K
|
|
AV1 video (up to 2160p) and surround audio, in an MP4 container.
|
|
H.265 VCN 2160p 4K
|
|
H.265 video (up to 2160p) and surround audio, in an MP4 container.
|
|
CLI Defaults/
|
|
CLI Default
|
|
"""
|
|
|
|
|
|
def test_preset_liste_kommt_aus_der_einrueckung():
|
|
presets = caps.parse_preset_liste(HB_PRESETS_ECHT)
|
|
assert presets == [
|
|
"Very Fast 2160p60 4K AV1",
|
|
"Super HQ 1080p30 Surround",
|
|
"H.265 MKV 2160p60 4K",
|
|
"H.265 MKV 1080p30",
|
|
"AV1 QSV 2160p 4K",
|
|
"H.265 VCN 2160p 4K",
|
|
"CLI Default",
|
|
]
|
|
|
|
|
|
def test_preset_liste_nimmt_keine_beschreibung_und_kein_log():
|
|
"""Beschreibungen (8 Leerzeichen), Kategorien (0) und HandBrakes eigene
|
|
Logzeilen (0) dürfen nicht als Preset-Namen durchgehen."""
|
|
presets = caps.parse_preset_liste(HB_PRESETS_ECHT)
|
|
assert not any(p.endswith("/") for p in presets)
|
|
assert not any("container" in p for p in presets)
|
|
assert not any(p.startswith("[") for p in presets)
|
|
assert "Cannot load libnvidia-encode.so.1" not in presets
|
|
|
|
|
|
def test_preset_liste_leer_bleibt_leer():
|
|
assert caps.parse_preset_liste("") == []
|
|
assert caps.parse_preset_liste(None) == []
|
|
|
|
|
|
def test_hardware_presets_stehen_auch_ohne_hardware_in_der_liste():
|
|
"""Richtigstellung zum SAVEPOINT v3.16: Dort galt es als unmöglich, die
|
|
Hardware-Preset-Namen auf der Rippy-VM zu ermitteln, weil dort kein
|
|
Hardware-Encoder läuft. Die Messung sagt das Gegenteil — `--preset-list`
|
|
nennt sie, `--help` nennt nur die nutzbaren Encoder. Zwei Fragen, zwei
|
|
Quellen."""
|
|
presets = caps.parse_preset_liste(HB_PRESETS_ECHT)
|
|
assert "H.265 VCN 2160p 4K" in presets # AMD, aus der VM-Liste
|
|
assert "AV1 QSV 2160p 4K" in presets # Intel, aus der VM-Liste
|
|
# ... und dieselbe Maschine meldet trotzdem KEINEN Hardware-Encoder:
|
|
assert caps.leite_backends_ab(caps.parse_encoder_liste(HB_HILFE_ECHT)) == [
|
|
"cpu-x264", "cpu-x265", "cpu-av1",
|
|
]
|