fix(caps,ui): Encoder werden gemessen statt behauptet
Der Modulkopf von caps.py verspricht "ehrlich erkannt, nicht behauptet" -
erkenne_encoder() tat aber drei Mal das Gegenteil:
1. cpu-x264 und cpu-x265 standen fest verdrahtet in der Liste ("immer
dabei"). Ein Rip-Worker OHNE HandBrake behauptete damit, komprimieren zu
koennen; jeder Transcode dort endete sofort mit "nicht installiert".
2. 'vaapi' wurde allein wegen /dev/dri gemeldet, ohne zu pruefen, ob
HandBrake das ueberhaupt kann. Auf der VM gemessen: das Worker-Image
kennt svt_av1/x264/x265/mpeg4/mpeg2/VP8/VP9/theora und KEINEN einzigen
Hardware-Encoder. Rippy haette VAAPI versprochen und dann versagt.
3. Ueber die Rechenleistung sagte es gar nichts. Genau deshalb war nicht zu
sehen, dass ein 4K-Encode auf dieser Maschine Tage braucht: CPU-Modell
ist das generische "QEMU Virtual CPU version 2.5+", grep -c avx2
/proc/cpuinfo = 0, nur bis sse4_2. x265 lebt von AVX2. Gemessen an der
Leseposition des Quellstroms: 28-55 h fuer einen Film, 3,84 von 4 Kernen
gesaettigt. Abhilfe waere der CPU-Typ 'host' in Proxmox.
Jetzt wird die Encoder-Liste aus `HandBrakeCLI --help` geparst (Abschnitt
-e/--encoder, Formatstrings im Worker-Image gemessen - AGENTS Regel D), und
Hardware zaehlt nur, wenn Geraet UND HandBrake-Unterstuetzung da sind. Neu
gemeldet: cpu-av1 (svt_av1 ist wirklich verfuegbar) sowie CPU-Modell,
Kernzahl und Vektorbefehlsstufe je Worker.
UI: Kerne/Vektorbefehle stehen bei jedem Worker (Worker-Tab und
Einstellungen -> System), dazu die ungefilterte HandBrake-Auskunft und eine
sichtbare Warnung, wenn AVX2 fehlt - inklusive des Hinweises auf den
VM-CPU-Typ. Der Warntext liegt in lib/encoder.ts, damit er nicht doppelt
im Code steht.
Mit im gleichen Zug (dieselbe Datei): der Schalter "Alle Tracks rippen" ist
entfallen. Er war ein Placebo - abcde bekommt keine Track-Auswahl und es gab
auch keine Oberflaeche, um eine Teilmenge zu waehlen. Eine Audio-CD wurde
also immer vollstaendig gerippt, egal wie der Schalter stand. An seiner
Stelle steht jetzt die Wahrheit.
7 Tests, Testdaten sind echte HandBrake-Ausgaben von der VM. Der Parser ist
zusaetzlich gegen die vollen 697 Zeilen der echten --help gegengeprueft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""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([], dri_da=True, nvidia_da=True) == []
|
||||
|
||||
|
||||
def test_backends_aus_echter_liste():
|
||||
encoder = caps.parse_encoder_liste(HB_HILFE_ECHT)
|
||||
# Kein /dev/dri, kein NVIDIA — und HandBrake kann hier ohnehin keine Hardware
|
||||
assert caps.leite_backends_ab(encoder, dri_da=False, nvidia_da=False) == [
|
||||
"cpu-x264", "cpu-x265", "cpu-av1",
|
||||
]
|
||||
|
||||
|
||||
def test_hardware_nur_wenn_geraet_UND_handbrake_es_kann():
|
||||
"""Befund 25.07.2026: Vorher genügte /dev/dri, um 'vaapi' zu melden. Das
|
||||
Worker-Image kann laut eigener Auskunft aber gar keinen Hardware-Encoder —
|
||||
Rippy hätte VAAPI versprochen und wäre beim Encodieren gescheitert."""
|
||||
ohne_hw = caps.parse_encoder_liste(HB_HILFE_ECHT)
|
||||
assert "vaapi" not in caps.leite_backends_ab(ohne_hw, dri_da=True, nvidia_da=True)
|
||||
assert "nvenc" not in caps.leite_backends_ab(ohne_hw, dri_da=True, nvidia_da=True)
|
||||
|
||||
mit_hw = caps.parse_encoder_liste(HB_HILFE_MIT_HARDWARE)
|
||||
# Gerät fehlt → trotz Können kein Hardware-Backend
|
||||
assert caps.leite_backends_ab(mit_hw, dri_da=False, nvidia_da=False) == [
|
||||
"cpu-x264", "cpu-x265",
|
||||
]
|
||||
# Beides da → beides gemeldet
|
||||
beides = caps.leite_backends_ab(mit_hw, dri_da=True, nvidia_da=True)
|
||||
assert "vaapi" in beides # qsv_* vorhanden
|
||||
assert "nvenc" in beides
|
||||
|
||||
|
||||
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"
|
||||
Reference in New Issue
Block a user