feat(v5): 4K-UHD nach der Leistung — Grafikkarte mit 4K-Preset komprimiert, nur CPU bleibt verlustfrei
WAS: presetEmpfehlung liefert für UHD das 4K-Preset der gemessenen Grafikkarten-Familie (VCN/NVENC/QSV) — sonst PRESET_KEINE (verlustfrei); komprimierenFuer folgt der Empfehlung, wenn der Typ auf „automatisch" steht. Eigene Wahl schlägt beides. Fenster: Kachel, Kompressions-Karte und Erst-Einrichtung sagen „(8 Bit)" bzw. „verlustfrei — nur CPU-Encoder". Gemessen am 12.09.2026 (HandBrake 1.11.2, RX 9070 XT, Akira-UHD-Abbild): die 4K-Hardware-Presets sind 8-Bit (vce_h265, Profil main); der 10-Bit- HEVC-Encoder vce_h265_10bit stürzt beim Start ab (Segfault, mit Profil auto und main10); AV1 10-Bit („AV1 VCN 2160p 4K", vce_av1_10bit) läuft — steht als eigene Wahl in der Liste, mit dem Hinweis auf AV1-fähige Abspielgeräte. WARUM: Durchsicht 12.09.2026, A4 — Commander-Entscheid: „Rippy analysiert doch die Performance des PCs, könnten wir das nicht davon abhängig machen?" Vorher rechnete der CPU-Default 4K auf 1080p klein, tagelang. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6b6e5c3436
commit
0fc87aa022
@@ -7,7 +7,7 @@
|
|||||||
// Werkzeuge mit Durchsuchen, Kompression mit Sprach-Auswahl, TMDb,
|
// Werkzeuge mit Durchsuchen, Kompression mit Sprach-Auswahl, TMDb,
|
||||||
// Benachrichtigung, Automatik und Roh-Regel. Dazu der Poster-Hintergrund.
|
// Benachrichtigung, Automatik und Roh-Regel. Dazu der Poster-Hintergrund.
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { DEFAULT_HB_PRESET, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
import { DEFAULT_HB_PRESET, empfehlungText, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
||||||
import { Badge, FELD_KLASSE, Feld, KNOPF_GOLD, KNOPF_LEISE, PfadFeld, Schalter, gbKurz } from '../bausteine'
|
import { Badge, FELD_KLASSE, Feld, KNOPF_GOLD, KNOPF_LEISE, PfadFeld, Schalter, gbKurz } from '../bausteine'
|
||||||
import { SprachAuswahl } from '../einstellungen/SprachAuswahl'
|
import { SprachAuswahl } from '../einstellungen/SprachAuswahl'
|
||||||
import { benachrichtigungTesten, einstellungSetzen, posterLaden, speicherLaden, type Verbindung } from '../kernverbindung'
|
import { benachrichtigungTesten, einstellungSetzen, posterLaden, speicherLaden, type Verbindung } from '../kernverbindung'
|
||||||
@@ -167,7 +167,7 @@ export function ErstEinrichtung({ verbindung }: { verbindung: Verbindung }) {
|
|||||||
{EMPFEHLUNGEN.map(([name, typ]) => (
|
{EMPFEHLUNGEN.map(([name, typ]) => (
|
||||||
<div key={typ} className="rounded-lg border border-tinte/8 bg-buehne/60 p-2">
|
<div key={typ} className="rounded-lg border border-tinte/8 bg-buehne/60 p-2">
|
||||||
<p className="font-mono text-[10px] tracking-wider text-nebel">{name}</p>
|
<p className="font-mono text-[10px] tracking-wider text-nebel">{name}</p>
|
||||||
<p className="mt-0.5 font-mono text-xs text-gold-hell">{presetEmpfehlung(typ, backends, presets) || DEFAULT_HB_PRESET}</p>
|
<p className="mt-0.5 font-mono text-xs text-gold-hell">{empfehlungText(typ, presetEmpfehlung(typ, backends, presets) || DEFAULT_HB_PRESET)}</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// einstellungSetzen in die EINE Quelle (§ 6.7).
|
// einstellungSetzen in die EINE Quelle (§ 6.7).
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import type { GeraeteInfo, HauptStatus, VorgangEintrag } from '../../gemeinsam/nachrichten'
|
import type { GeraeteInfo, HauptStatus, VorgangEintrag } from '../../gemeinsam/nachrichten'
|
||||||
import { presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
import { empfehlungText, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
FELD_KLASSE,
|
FELD_KLASSE,
|
||||||
@@ -127,7 +127,7 @@ export function KompressionKarte({ verbindung }: { verbindung: Verbindung }) {
|
|||||||
const werte = verbindung.einstellungen
|
const werte = verbindung.einstellungen
|
||||||
const presets = verbindung.werkzeuge?.presets ?? []
|
const presets = verbindung.werkzeuge?.presets ?? []
|
||||||
const backends = verbindung.werkzeuge?.backends ?? []
|
const backends = verbindung.werkzeuge?.backends ?? []
|
||||||
const empfehlungFuer = (typ: string): string => presetEmpfehlung(typ, backends, presets)
|
const empfehlungFuer = (typ: string): string => empfehlungText(typ, presetEmpfehlung(typ, backends, presets))
|
||||||
return (
|
return (
|
||||||
<SektionsKarte
|
<SektionsKarte
|
||||||
id="kompression"
|
id="kompression"
|
||||||
@@ -153,7 +153,8 @@ export function KompressionKarte({ verbindung }: { verbindung: Verbindung }) {
|
|||||||
</div>
|
</div>
|
||||||
<p className="text-[11px] leading-4 text-nebel">
|
<p className="text-[11px] leading-4 text-nebel">
|
||||||
„automatisch" nimmt die Empfehlung aus der gemessenen Liste deines HandBrake — auf dieser Maschine die Grafikkarte, wo es sie gibt. Eine eigene Wahl
|
„automatisch" nimmt die Empfehlung aus der gemessenen Liste deines HandBrake — auf dieser Maschine die Grafikkarte, wo es sie gibt. Eine eigene Wahl
|
||||||
schlägt die Empfehlung immer.
|
schlägt die Empfehlung immer. 4K-UHD wird nur mit Grafikkarten-Encoder komprimiert (8 Bit); ohne bleibt es verlustfrei. Wer HDR in 10 Bit will, wählt
|
||||||
|
„AV1 VCN 2160p 4K" — die Abspielgeräte müssen dann AV1 können.
|
||||||
</p>
|
</p>
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
<SprachAuswahl
|
<SprachAuswahl
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import type {
|
|||||||
RipStatus,
|
RipStatus,
|
||||||
VorgangEintrag,
|
VorgangEintrag,
|
||||||
} from '../../gemeinsam/nachrichten'
|
} from '../../gemeinsam/nachrichten'
|
||||||
import { DEFAULT_HB_PRESET, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
import { DEFAULT_HB_PRESET, PRESET_KEINE, empfehlungText, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
IconDisc,
|
IconDisc,
|
||||||
@@ -58,9 +58,11 @@ export function presetAnzeige(typ: GeraeteInfo['typ'], verbindung: Verbindung):
|
|||||||
if (werte['transcodeEnabled'] === 'false') return 'ohne Kompression'
|
if (werte['transcodeEnabled'] === 'false') return 'ohne Kompression'
|
||||||
const schluessel = typ === 'dvd' ? 'transcodePresetDvd' : typ === 'uhd' ? 'transcodePresetUhd' : 'transcodePresetBluray'
|
const schluessel = typ === 'dvd' ? 'transcodePresetDvd' : typ === 'uhd' ? 'transcodePresetUhd' : 'transcodePresetBluray'
|
||||||
const eigene = werte[schluessel] ?? ''
|
const eigene = werte[schluessel] ?? ''
|
||||||
|
if (eigene === PRESET_KEINE) return 'ohne Kompression'
|
||||||
|
if (eigene.length > 0) return eigene
|
||||||
const empfehlung = presetEmpfehlung(typ, verbindung.werkzeuge?.backends ?? [], verbindung.werkzeuge?.presets ?? [])
|
const empfehlung = presetEmpfehlung(typ, verbindung.werkzeuge?.backends ?? [], verbindung.werkzeuge?.presets ?? [])
|
||||||
const gewaehlt = eigene.length > 0 ? eigene : empfehlung.length > 0 ? empfehlung : DEFAULT_HB_PRESET
|
if (empfehlung === PRESET_KEINE) return typ === 'uhd' ? 'verlustfrei (nur CPU-Encoder)' : 'ohne Kompression'
|
||||||
return gewaehlt === 'keine' ? 'ohne Kompression' : gewaehlt
|
return empfehlungText(typ, empfehlung.length > 0 ? empfehlung : DEFAULT_HB_PRESET)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LaufwerkKachel({
|
export function LaufwerkKachel({
|
||||||
|
|||||||
@@ -18,6 +18,11 @@
|
|||||||
|
|
||||||
export const DEFAULT_HB_PRESET = 'H.265 MKV 1080p30'
|
export const DEFAULT_HB_PRESET = 'H.265 MKV 1080p30'
|
||||||
|
|
||||||
|
// Reservierter Wert: „diesen Typ NICHT komprimieren". Kein echtes
|
||||||
|
// HandBrake-Preset heißt so (gegen alle 107 aus --preset-list geprüft).
|
||||||
|
// Lebt seit 5.7.0 hier, weil die Empfehlung ihn liefern kann.
|
||||||
|
export const PRESET_KEINE = 'keine'
|
||||||
|
|
||||||
const FAMILIEN: ReadonlyArray<readonly [backend: string, presetKennung: string]> = [
|
const FAMILIEN: ReadonlyArray<readonly [backend: string, presetKennung: string]> = [
|
||||||
['vce', 'VCN'],
|
['vce', 'VCN'],
|
||||||
['nvenc', 'NVENC'],
|
['nvenc', 'NVENC'],
|
||||||
@@ -25,7 +30,19 @@ const FAMILIEN: ReadonlyArray<readonly [backend: string, presetKennung: string]>
|
|||||||
]
|
]
|
||||||
|
|
||||||
/** Das empfohlene Preset für diesen Disc-Typ — aus den GEMESSENEN
|
/** Das empfohlene Preset für diesen Disc-Typ — aus den GEMESSENEN
|
||||||
* Backends und Preset-Namen. Fällt auf den CPU-Default zurück. */
|
* Backends und Preset-Namen. DVD und Blu-ray fallen auf den CPU-Default
|
||||||
|
* zurück.
|
||||||
|
*
|
||||||
|
* 4K-UHD seit 5.7.0 nach der Leistung (Commander-Entscheid 12.09.2026):
|
||||||
|
* Gibt es einen Grafikkarten-Encoder UND dessen 4K-Preset, wird damit
|
||||||
|
* komprimiert; gibt es nur CPU-Encoder, bleibt UHD verlustfrei
|
||||||
|
* (PRESET_KEINE) — ein 4K-Encode auf der CPU dauert Tage, und der alte
|
||||||
|
* CPU-Default „1080p30" rechnete 4K klein. Gemessen am 12.09.2026: Die
|
||||||
|
* 4K-Hardware-Presets („H.265 VCN 2160p 4K" usw.) sind 8-Bit; der
|
||||||
|
* 10-Bit-HEVC-Encoder der AMD-Karte (vce_h265_10bit) stürzt in HandBrake
|
||||||
|
* 1.11.2 beim Start ab, 10-Bit-AV1 („AV1 VCN 2160p 4K") läuft — wer HDR
|
||||||
|
* in 10 Bit will, wählt das AV1-Preset selbst (Abspielgeräte müssen AV1
|
||||||
|
* können). */
|
||||||
export function presetEmpfehlung(
|
export function presetEmpfehlung(
|
||||||
discTyp: string,
|
discTyp: string,
|
||||||
backends: readonly string[],
|
backends: readonly string[],
|
||||||
@@ -36,9 +53,19 @@ export function presetEmpfehlung(
|
|||||||
if (!backends.includes(backend)) continue
|
if (!backends.includes(backend)) continue
|
||||||
const treffer = presets.find((p) => p.startsWith('H.265 ') && p.includes(kennung) && p.includes(gewuenscht))
|
const treffer = presets.find((p) => p.startsWith('H.265 ') && p.includes(kennung) && p.includes(gewuenscht))
|
||||||
if (treffer !== undefined) return treffer
|
if (treffer !== undefined) return treffer
|
||||||
// 4K-Familie ohne 4K-Preset: das 1080p-Pendant ist besser als CPU.
|
// Eine 4K-Disc ohne 4K-Preset der Familie: nicht klein rechnen —
|
||||||
|
// verlustfrei lassen (unten). Bei DVD/Blu-ray ist das 1080p-Pendant
|
||||||
|
// besser als CPU.
|
||||||
|
if (discTyp === 'uhd') continue
|
||||||
const kleiner = presets.find((p) => p.startsWith('H.265 ') && p.includes(kennung))
|
const kleiner = presets.find((p) => p.startsWith('H.265 ') && p.includes(kennung))
|
||||||
if (kleiner !== undefined) return kleiner
|
if (kleiner !== undefined) return kleiner
|
||||||
}
|
}
|
||||||
return DEFAULT_HB_PRESET
|
return discTyp === 'uhd' ? PRESET_KEINE : DEFAULT_HB_PRESET
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Klartext zur Empfehlung fürs Fenster (5.7.0). */
|
||||||
|
export function empfehlungText(discTyp: string, empfehlung: string): string {
|
||||||
|
if (empfehlung === PRESET_KEINE) return discTyp === 'uhd' ? 'verlustfrei lassen — nur CPU-Encoder gemessen' : 'verlustfrei lassen'
|
||||||
|
if (discTyp === 'uhd' && /2160p/.test(empfehlung)) return `${empfehlung} (8 Bit)`
|
||||||
|
return empfehlung
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export async function komprimierenUndAblegen(
|
|||||||
): Promise<AblageErgebnis> {
|
): Promise<AblageErgebnis> {
|
||||||
const einstellungen = umgebung.einstellungen()
|
const einstellungen = umgebung.einstellungen()
|
||||||
const handbrake = umgebung.handbrake()
|
const handbrake = umgebung.handbrake()
|
||||||
const soll = komprimierenFuer(auftrag.discTyp, einstellungen)
|
const soll = komprimierenFuer(auftrag.discTyp, einstellungen, umgebung.presetEmpfehlung?.(auftrag.discTyp) ?? '')
|
||||||
const komprimiert = soll && handbrake.length > 0
|
const komprimiert = soll && handbrake.length > 0
|
||||||
const preset =
|
const preset =
|
||||||
auftrag.preset.length > 0
|
auftrag.preset.length > 0
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
// mit den Befunden vom 25.07.2026 begründet: EIN Preset für alles rechnete
|
// mit den Befunden vom 25.07.2026 begründet: EIN Preset für alles rechnete
|
||||||
// 4K auf 1080p herunter und DVDs hinauf.
|
// 4K auf 1080p herunter und DVDs hinauf.
|
||||||
|
|
||||||
import { DEFAULT_HB_PRESET, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
import { DEFAULT_HB_PRESET, PRESET_KEINE, presetEmpfehlung } from '../../gemeinsam/preset-empfehlung'
|
||||||
|
|
||||||
export { DEFAULT_HB_PRESET, presetEmpfehlung }
|
export { DEFAULT_HB_PRESET, PRESET_KEINE, presetEmpfehlung }
|
||||||
|
|
||||||
/** Einstellungs-Schlüssel je Disc-Typ. */
|
/** Einstellungs-Schlüssel je Disc-Typ. */
|
||||||
export const PRESET_SCHLUESSEL: Record<string, string> = {
|
export const PRESET_SCHLUESSEL: Record<string, string> = {
|
||||||
@@ -13,15 +13,12 @@ export const PRESET_SCHLUESSEL: Record<string, string> = {
|
|||||||
uhd: 'transcodePresetUhd',
|
uhd: 'transcodePresetUhd',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reservierter Wert: „diesen Typ NICHT komprimieren". Kein echtes
|
|
||||||
// HandBrake-Preset heißt so (gegen alle 90 aus --preset-list geprüft).
|
|
||||||
// Grund: 4K verlustfrei durchreichen, DVDs trotzdem schrumpfen.
|
|
||||||
export const PRESET_KEINE = 'keine'
|
|
||||||
|
|
||||||
export type Einstellungen = Record<string, string | boolean | undefined>
|
export type Einstellungen = Record<string, string | boolean | undefined>
|
||||||
|
|
||||||
/** Soll dieser Disc-Typ nach dem Rip komprimiert werden? (pur) */
|
/** Soll dieser Disc-Typ nach dem Rip komprimiert werden? (pur)
|
||||||
export function komprimierenFuer(discTyp: string, einstellungen: Einstellungen): boolean {
|
* 5.7.0: Steht der Typ auf „automatisch" und die gemessene Empfehlung
|
||||||
|
* sagt „keine" (4K ohne Grafikkarten-Encoder), bleibt es verlustfrei. */
|
||||||
|
export function komprimierenFuer(discTyp: string, einstellungen: Einstellungen, empfehlung = ''): boolean {
|
||||||
if (einstellungen['transcodeEnabled'] === false || einstellungen['transcodeEnabled'] === 'false') {
|
if (einstellungen['transcodeEnabled'] === false || einstellungen['transcodeEnabled'] === 'false') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -29,6 +26,7 @@ export function komprimierenFuer(discTyp: string, einstellungen: Einstellungen):
|
|||||||
if (schluessel.length > 0) {
|
if (schluessel.length > 0) {
|
||||||
const wert = String(einstellungen[schluessel] ?? '').trim()
|
const wert = String(einstellungen[schluessel] ?? '').trim()
|
||||||
if (wert === PRESET_KEINE) return false
|
if (wert === PRESET_KEINE) return false
|
||||||
|
if (wert.length === 0 && empfehlung === PRESET_KEINE) return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// 5.7.0 — 4K nach der Leistung (Commander-Entscheid 12.09.2026): Grafik-
|
||||||
|
// karten-Encoder plus 4K-Preset → komprimieren; nur CPU → verlustfrei.
|
||||||
|
// Preset-Namen und Encoder am mitgelieferten HandBrake 1.11.2 gemessen
|
||||||
|
// (--preset-list, --help; vce_h265_10bit stürzt ab, AV1 10-Bit läuft).
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { DEFAULT_HB_PRESET, PRESET_KEINE, empfehlungText, presetEmpfehlung } from '../src/gemeinsam/preset-empfehlung'
|
||||||
|
import { komprimierenFuer, presetFuer } from '../src/kern/komprimieren/presets'
|
||||||
|
|
||||||
|
const PRESETS = ['H.265 MKV 1080p30', 'H.265 MKV 2160p60 4K', 'AV1 VCN 2160p 4K', 'H.265 VCN 2160p 4K', 'H.265 VCN 1080p', 'H.265 NVENC 1080p']
|
||||||
|
const NUR_CPU = ['cpu-x264', 'cpu-x265', 'cpu-av1']
|
||||||
|
const AMD = [...NUR_CPU, 'vce', 'vce-av1']
|
||||||
|
const NVIDIA_OHNE_4K = [...NUR_CPU, 'nvenc']
|
||||||
|
|
||||||
|
describe('presetEmpfehlung für 4K-UHD', () => {
|
||||||
|
it('Grafikkarte mit 4K-Preset → das 4K-Preset (8 Bit im Klartext)', () => {
|
||||||
|
expect(presetEmpfehlung('uhd', AMD, PRESETS)).toBe('H.265 VCN 2160p 4K')
|
||||||
|
expect(empfehlungText('uhd', 'H.265 VCN 2160p 4K')).toBe('H.265 VCN 2160p 4K (8 Bit)')
|
||||||
|
})
|
||||||
|
it('nur CPU → verlustfrei lassen, nie das 1080p-CPU-Preset', () => {
|
||||||
|
expect(presetEmpfehlung('uhd', NUR_CPU, PRESETS)).toBe(PRESET_KEINE)
|
||||||
|
expect(empfehlungText('uhd', PRESET_KEINE)).toContain('verlustfrei')
|
||||||
|
})
|
||||||
|
it('Grafikkarte ohne 4K-Preset → verlustfrei, nicht klein rechnen', () => {
|
||||||
|
expect(presetEmpfehlung('uhd', NVIDIA_OHNE_4K, PRESETS)).toBe(PRESET_KEINE)
|
||||||
|
})
|
||||||
|
it('DVD und Blu-ray wie bisher: Grafikkarte, sonst CPU-Default', () => {
|
||||||
|
expect(presetEmpfehlung('bluray', AMD, PRESETS)).toBe('H.265 VCN 1080p')
|
||||||
|
expect(presetEmpfehlung('dvd', NUR_CPU, PRESETS)).toBe(DEFAULT_HB_PRESET)
|
||||||
|
expect(presetEmpfehlung('bluray', NVIDIA_OHNE_4K, PRESETS)).toBe('H.265 NVENC 1080p')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('komprimierenFuer mit Empfehlung', () => {
|
||||||
|
it('automatisch + Empfehlung „keine" → nicht komprimieren', () => {
|
||||||
|
expect(komprimierenFuer('uhd', {}, PRESET_KEINE)).toBe(false)
|
||||||
|
expect(komprimierenFuer('uhd', { transcodePresetUhd: '' }, PRESET_KEINE)).toBe(false)
|
||||||
|
})
|
||||||
|
it('eigene Wahl schlägt die Empfehlung — in beide Richtungen', () => {
|
||||||
|
expect(komprimierenFuer('uhd', { transcodePresetUhd: 'AV1 VCN 2160p 4K' }, PRESET_KEINE)).toBe(true)
|
||||||
|
expect(komprimierenFuer('uhd', { transcodePresetUhd: PRESET_KEINE }, 'H.265 VCN 2160p 4K')).toBe(false)
|
||||||
|
expect(presetFuer('uhd', { transcodePresetUhd: 'AV1 VCN 2160p 4K' }, PRESET_KEINE)).toBe('AV1 VCN 2160p 4K')
|
||||||
|
})
|
||||||
|
it('Blu-ray bleibt unberührt', () => {
|
||||||
|
expect(komprimierenFuer('bluray', {}, 'H.265 VCN 1080p')).toBe(true)
|
||||||
|
expect(komprimierenFuer('bluray', {}, '')).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user