Die sechs Punkte des Commanders vom 02.09.2026 abends und seine Entscheide:
* Restzeit der Kompression aus HandBrakes eigener ETA-Klammer (Formatstring
aus dem Binary 1.11.2 gelesen), KompressionStand.restS.
* Kachel zeigt nach dem Rip nur noch eine Zeile — Balken, Prozent, Ziel in
der Kompressions-Karte und im Verlauf ("nur in der Kompressions-Karte").
* Kopfzeile bleibt beim Scrollen stehen (sticky), Sektionswahl darunter.
* Untertitel-Vorwahl je Extra ("NUR für Extras, IMMER OPTIONAL"): welche
Untertitel-Spur beim Abspielen an ist — weiche Spur, nie eingebrannt;
RipAuftragTitel/RohDatei.untertitelVorwahl, sprachwahl.
* Mehrere Filme je Disc: "anderer Film …" je Titel mit TMDb-Suche;
filme.ts filmGruppen trennt am Rip-Ende in eigene Roh-Ordner, Vorgänge
und Kompressions-Aufträge mit voller Zuordnung (erkennung.zuordnungAus).
* Roh-Ordner und Dateien tragen den Filmnamen (filme.ts rohOrdnerName,
ablage/dateinamen.ts: Akira (1988).mkv, - Fassung n, - Extra nn).
* Hell-Modus: Einstellung design (dunkel/hell/system), data-design auf
<html>, Token --color-tinte statt white/-Alpha-Klassen.
* Vorgangs-Leiste (Commander: "Phase 1–4 plus Komprimierung — zu einem
machen"): gemeinsam/vorgangsleiste.ts + uebersicht/VorgangsLeiste.tsx,
vier Stationen (Rettung als Zwischenstation), Segment-Balken in der
aktiven Station; Kachel, Kompressions-Karte, Verlauf. PHASE-x/4-Texte weg.
* Automatik-Kasten leert sich, sobald ein Rip startet.
Beweise: 446 Tests (neu: filme, dateinamen, vorgangsleiste; erweitert:
auftrag, handbrake, automatik, ablegen), drei Typprüfungen, Klick-Beweis
33 Schritte inkl. Vorwahl, anderer Film, Vorgangs-Leiste, Restzeit,
Hell-Modus (Bilder beweise/klick/01…11).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
// Die Sprach-Auswahl (5.5.0, Commander: „bei Tonspuren und Untertiteln
|
||
// können Dropdowns hin") — gewählte Sprachen als Chips in Wunschreihenfolge,
|
||
// dazu ein Dropdown zum Hinzufügen. Die erste Tonsprache ist die
|
||
// Wunschsprache (auftrag.ts sprachwahl). Leer heißt „alle".
|
||
import { SPRACHEN, sprachName, sprachlisteAus, sprachlisteText } from '../../gemeinsam/sprachen'
|
||
|
||
export function SprachAuswahl({
|
||
name,
|
||
hinweis,
|
||
wert,
|
||
aufAendern,
|
||
}: {
|
||
name: string
|
||
hinweis: string
|
||
wert: string
|
||
aufAendern: (wert: string) => void
|
||
}) {
|
||
const gewaehlt = sprachlisteAus(wert)
|
||
const frei = SPRACHEN.filter((s) => !gewaehlt.includes(s.code))
|
||
const setzen = (codes: string[]): void => aufAendern(sprachlisteText(codes))
|
||
const verschieben = (code: string, richtung: -1 | 1): void => {
|
||
const i = gewaehlt.indexOf(code)
|
||
const j = i + richtung
|
||
if (i < 0 || j < 0 || j >= gewaehlt.length) return
|
||
const neu = [...gewaehlt]
|
||
;[neu[i], neu[j]] = [neu[j], neu[i]]
|
||
setzen(neu)
|
||
}
|
||
return (
|
||
<div>
|
||
<span className="text-xs font-semibold text-dunst">{name}</span>
|
||
<span className="ml-2 text-xs text-nebel">{hinweis}</span>
|
||
<div className="mt-1 flex min-h-[38px] flex-wrap items-center gap-1.5 rounded-lg border border-tinte/12 bg-samt px-2 py-1.5">
|
||
{gewaehlt.length === 0 && <span className="font-mono text-[11px] text-nebel">alle Sprachen der Disc</span>}
|
||
{gewaehlt.map((code, i) => (
|
||
<span key={code} className="inline-flex items-center gap-1 rounded-md border border-gold/40 bg-gold/12 px-2 py-0.5 text-[11px] text-gold-hell">
|
||
<span className="font-mono text-[10px] text-gold/70">{i + 1}.</span>
|
||
<span>{sprachName(code)}</span>
|
||
<span className="font-mono text-[10px] text-nebel">{code}</span>
|
||
<button type="button" onClick={() => verschieben(code, -1)} disabled={i === 0} className="ml-1 text-nebel hover:text-schnee disabled:opacity-30" title="nach vorn">
|
||
‹
|
||
</button>
|
||
<button type="button" onClick={() => verschieben(code, 1)} disabled={i === gewaehlt.length - 1} className="text-nebel hover:text-schnee disabled:opacity-30" title="nach hinten">
|
||
›
|
||
</button>
|
||
<button type="button" onClick={() => setzen(gewaehlt.filter((c) => c !== code))} className="ml-0.5 text-nebel hover:text-rot" title="entfernen">
|
||
×
|
||
</button>
|
||
</span>
|
||
))}
|
||
<select
|
||
value=""
|
||
onChange={(e) => {
|
||
if (e.target.value.length > 0) setzen([...gewaehlt, e.target.value])
|
||
}}
|
||
className="ml-auto rounded-md border border-tinte/12 bg-buehne px-2 py-1 font-mono text-[11px] text-dunst"
|
||
>
|
||
<option value="">+ Sprache …</option>
|
||
{frei.map((s) => (
|
||
<option key={s.code} value={s.code}>
|
||
{s.name} ({s.code})
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|