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:
Hitonabi
2026-07-25 21:04:42 +02:00
parent e0cb7b3ddc
commit 883c1c290b
8 changed files with 328 additions and 21 deletions
@@ -14,6 +14,7 @@ interface WorkerInfo {
const ENCODER_LABELS: Record<string, string> = {
'cpu-x264': 'CPU · H.264 (x264)',
'cpu-x265': 'CPU · H.265 (x265)',
'cpu-av1': 'CPU · AV1 (SVT-AV1)',
'vaapi': 'Hardware · VAAPI (AMD/Intel)',
'nvenc': 'Hardware · NVENC (NVIDIA)',
}
+2 -1
View File
@@ -38,7 +38,8 @@ interface WorkerWahl {
}
const ENCODER_KURZ: Record<string, string> = {
'cpu-x264': 'H.264', 'cpu-x265': 'H.265', 'vaapi': 'VAAPI⚡', 'nvenc': 'NVENC⚡',
'cpu-x264': 'H.264', 'cpu-x265': 'H.265', 'cpu-av1': 'AV1',
'vaapi': 'VAAPI⚡', 'nvenc': 'NVENC⚡',
}
interface TitelInfo {
@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import { Server, RefreshCw, Copy, CheckCircle, Trash2, Download } from 'lucide-react'
import { api } from '../lib/api'
import { simdWarnung } from '../lib/encoder'
import { useToast } from '../context/ToastContext'
import { Card, CardHeader, CardTitle, CardContent } from './ui/Card'
import { Button } from './ui/Button'
@@ -16,16 +17,22 @@ interface WorkerInfo {
ip?: string
makemkv?: string
handbrake?: string
cpu_modell?: string
cpu_kerne?: string
cpu_simd?: string
handbrake_encoder?: string
}
}
const ENCODER_LABELS: Record<string, string> = {
'cpu-x264': 'CPU · H.264',
'cpu-x265': 'CPU · H.265',
'cpu-av1': 'CPU · AV1 (SVT)',
'vaapi': 'GPU · VAAPI (AMD/Intel)',
'nvenc': 'GPU · NVENC (NVIDIA)',
}
function relativeZeit(iso?: string): string {
if (!iso) return 'nie'
const sekunden = Math.floor((Date.now() - new Date(iso).getTime()) / 1000)
@@ -134,6 +141,18 @@ export default function WorkerVerwaltung() {
{w.info?.ip ? ` · IP ${w.info.ip}` : ''}
{w.info?.hostname && w.info.hostname !== w.name ? ` · ID ${w.info.hostname}` : ''}
</p>
{(w.info?.cpu_kerne || w.info?.cpu_simd) && (
<p className="text-xs text-slate-500 dark:text-slate-400" title={w.info?.cpu_modell || ''}>
{w.info?.cpu_kerne ? `${w.info.cpu_kerne} Kerne` : ''}
{w.info?.cpu_kerne && w.info?.cpu_simd ? ' · ' : ''}
{w.info?.cpu_simd ? `Vektorbefehle ${w.info.cpu_simd}` : ''}
</p>
)}
{simdWarnung(w.info?.cpu_simd) && (
<p className="mt-1 text-xs text-amber-600 dark:text-amber-400 max-w-xl">
{simdWarnung(w.info?.cpu_simd)}
</p>
)}
</div>
</div>
<div className="flex items-center gap-2">