fix(v5): Die drei Funde aus dem ersten Commander-Test
Ampel / ampel (push) Successful in 1m25s

FUND 1 — 'Key korrekt, aber als ungueltig angezeigt': Stimmt beides.
Live gemessen: Der aktuelle Forum-Key ist in Ordnung, aber MakeMKV
1.18.4 ist ZU ALT fuer ihn — makemkvcon meldet 5020 UND 5021 zusammen,
und die alte Pruefung brach beim ersten Code ab und beschuldigte den
Schluessel. urteilAusAusgabe sammelt jetzt ALLE Codes (5021 schlaegt
5020, MakeMKV-Version aus MSG 1005 wird mitgelesen) und die Kachel
sagt den wahren Satz: 'Der Key ist in Ordnung — MakeMKV 1.18.4 ist zu
alt. Rippy hat ihn entfernt, damit der Beta-Modus weiterlaeuft (Rips
klappen trotzdem). Abhilfe: MakeMKV aktualisieren.' Neues Feld
weiterBetrieb: Beta-Modus ist GELB (Warnung), nicht rot. Test mit der
echten Ausgabe als Fixture.

FUND 2 — 'Komprimieren auf CPU statt GPU': Der Default 'H.265 MKV
1080p30' ist ein CPU-Preset. Neue GEMESSENE Empfehlung
(gemeinsam/preset-empfehlung.ts): aus den echten Backends und der
echten Preset-Liste des eigenen HandBrake — VCN vor NVENC vor QSV vor
CPU, nur Namen die WIRKLICH in der Liste stehen, HandBrake-Presets
deckeln die Aufloesung nur (DVD bleibt SD). Kette: eigene Wahl >
allgemeines Preset > Empfehlung > CPU-Default. Auf dem Commander-PC:
bluray/dvd -> 'H.265 VCN 1080p', uhd -> 'H.265 VCN 2160p 4K'. Die
Preset-Auswahl zeigt 'automatisch: <Empfehlung>' als Leer-Option.

FUND 3 — 'keine Alternative waehlbar': 'Aendern …'-Dialog an der
Laufwerks-Kachel (§ 5 Schritt 4): TMDb-Suche (Film + Serie, mit
Postern), Klick uebernimmt — Nutzer-Wahl gilt als 100 % und steuert
Ablage-Ordner, NFO und Poster. Neue Nachrichten metadaten-suchen /
zuordnung-setzen / metadaten-vorschlaege.

Dazu: Poster in der Laufwerks-Kachel (CSP um image.tmdb.org
erweitert), Restzeit-Schaetzung am Fortschritt.

192 Tests gruen, Smoke gruen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-30 20:20:10 +02:00
co-authored by Claude Fable 5
parent 0e1c927e7b
commit c5dd90cc18
11 changed files with 542 additions and 56 deletions
+40 -1
View File
@@ -85,6 +85,15 @@ export interface BibliothekEintrag {
dauerS: number
}
/** Ein wählbarer Metadaten-Vorschlag (§ 5 Schritt 4: „nein → fragen"). */
export interface MetaVorschlag {
typ: 'movie' | 'tv'
tmdbId: number
titel: string
jahr: number | null
posterPfad: string
}
/** Auskunft über die Werkzeuge (Katalog + gemessene HandBrake-Listen). */
export interface WerkzeugAuskunft {
makemkvcon: string
@@ -103,12 +112,21 @@ export type KernNachricht =
| { art: 'auswurf-ergebnis'; id: string; grund: AuswurfGrund; fehler: string | null }
| { art: 'rip-status'; status: RipStatus }
| { art: 'disc-info'; info: DiscInfoStand }
| { art: 'metadaten-vorschlaege'; id: string; vorschlaege: MetaVorschlag[] }
| { art: 'einstellungen'; werte: Record<string, string> }
| { art: 'werkzeuge'; auskunft: WerkzeugAuskunft }
| { art: 'bibliothek'; eintraege: BibliothekEintrag[] }
| {
art: 'schluessel-status'
stand: { quelle: string; angenommen: boolean | null; gueltigBis: string; geholtAm: string; text: string }
stand: {
quelle: string
angenommen: boolean | null
/** Rips laufen trotzdem (Beta-Modus) — Warnung, kein Ausfall. */
weiterBetrieb: boolean
gueltigBis: string
geholtAm: string
text: string
}
}
/** Nachrichten, die das Fenster über den MessagePort an den Kern schickt. */
@@ -118,6 +136,8 @@ export type FensterNachricht =
| { art: 'rip-start'; id: string }
| { art: 'rip-abbruch'; id: string }
| { art: 'einstellung-setzen'; schluessel: string; wert: string }
| { art: 'metadaten-suchen'; id: string; text: string }
| { art: 'zuordnung-setzen'; id: string; vorschlag: MetaVorschlag }
| { art: 'einstellungen-laden' }
| { art: 'werkzeuge-laden' }
| { art: 'bibliothek-laden' }
@@ -178,11 +198,24 @@ export function istKernNachricht(wert: unknown): wert is KernNachricht {
return Array.isArray(wert.eintraege)
case 'schluessel-status':
return istObjekt(wert.stand) && typeof (wert.stand as Record<string, unknown>).text === 'string'
case 'metadaten-vorschlaege':
return typeof wert.id === 'string' && Array.isArray(wert.vorschlaege)
default:
return false
}
}
export function istMetaVorschlag(wert: unknown): wert is MetaVorschlag {
if (!istObjekt(wert)) return false
return (
(wert.typ === 'movie' || wert.typ === 'tv') &&
typeof wert.tmdbId === 'number' &&
typeof wert.titel === 'string' &&
(wert.jahr === null || typeof wert.jahr === 'number') &&
typeof wert.posterPfad === 'string'
)
}
export function istRipStatus(wert: unknown): wert is RipStatus {
if (!istObjekt(wert)) return false
return (
@@ -211,6 +244,12 @@ export function istFensterNachricht(wert: unknown): wert is FensterNachricht {
if (wert.art === 'einstellung-setzen') {
return typeof wert.schluessel === 'string' && wert.schluessel.length > 0 && typeof wert.wert === 'string'
}
if (wert.art === 'metadaten-suchen') {
return typeof wert.id === 'string' && wert.id.length > 0 && typeof wert.text === 'string'
}
if (wert.art === 'zuordnung-setzen') {
return typeof wert.id === 'string' && wert.id.length > 0 && istMetaVorschlag(wert.vorschlag)
}
return wert.art === 'einstellungen-laden' || wert.art === 'werkzeuge-laden' || wert.art === 'bibliothek-laden'
}
@@ -0,0 +1,44 @@
// Die Preset-Empfehlung — GEMESSEN, nicht behauptet (Commander-Fund
// 30.08.2026: „Das Komprimieren passiert auf der CPU, nicht auf der
// deutlich schnelleren GPU"). Der alte Default „H.265 MKV 1080p30" ist
// ein CPU-x265-Preset; auf dem Commander-PC kodiert die RX 9070 XT
// (VCN) denselben Film in 2 statt 60+ Minuten.
//
// Regeln:
// * Empfohlen wird NUR ein Preset, das in der vom eigenen HandBrake
// gemessenen Liste WIRKLICH steht (encoder.ts) — nie ein geratener
// Name (die „Invalid preset"-Falle).
// * Familienreihenfolge nach den gemessenen Backends: VCN (AMD) →
// NVENC (NVIDIA) → QSV (Intel) → CPU-Default.
// * HandBrake-Presets DECKELN die Auflösung nur — eine DVD bleibt SD,
// auch unter „…1080p" (deshalb ist die 1080p-Empfehlung auch für
// DVDs richtig; hochskaliert wird nichts).
// Liegt hier in gemeinsam/, weil Kern (Pipeline-Fallback) UND Fenster
// (Anzeige „automatisch: …") dieselbe Rechnung brauchen.
export const DEFAULT_HB_PRESET = 'H.265 MKV 1080p30'
const FAMILIEN: ReadonlyArray<readonly [backend: string, presetKennung: string]> = [
['vce', 'VCN'],
['nvenc', 'NVENC'],
['qsv', 'QSV'],
]
/** Das empfohlene Preset für diesen Disc-Typ — aus den GEMESSENEN
* Backends und Preset-Namen. Fällt auf den CPU-Default zurück. */
export function presetEmpfehlung(
discTyp: string,
backends: readonly string[],
presets: readonly string[],
): string {
const gewuenscht = discTyp === 'uhd' ? '2160p 4K' : '1080p'
for (const [backend, kennung] of FAMILIEN) {
if (!backends.includes(backend)) continue
const treffer = presets.find((p) => p.startsWith('H.265 ') && p.includes(kennung) && p.includes(gewuenscht))
if (treffer !== undefined) return treffer
// 4K-Familie ohne 4K-Preset: das 1080p-Pendant ist besser als CPU.
const kleiner = presets.find((p) => p.startsWith('H.265 ') && p.includes(kennung))
if (kleiner !== undefined) return kleiner
}
return DEFAULT_HB_PRESET
}