feat(v5): 5.6.0 — Vorgangs-Leiste, mehrere Filme je Disc, Filmnamen für Ordner und Dateien, Untertitel-Vorwahl je Extra, Restzeit, Hell-Modus
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>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
76ed3b337a
commit
f5dab5d1c1
@@ -24,7 +24,6 @@ import {
|
||||
InfoKachel,
|
||||
LAUFENDE_PHASEN,
|
||||
PHASEN_MONO,
|
||||
SegmentBalken,
|
||||
TYP_NAMEN,
|
||||
backdropUrl,
|
||||
discHerkunft,
|
||||
@@ -46,6 +45,8 @@ import {
|
||||
ripUeberspringen,
|
||||
type Verbindung,
|
||||
} from '../kernverbindung'
|
||||
import { leisteAusRip } from '../../gemeinsam/vorgangsleiste'
|
||||
import { VorgangsLeiste } from './VorgangsLeiste'
|
||||
|
||||
/** Welches Preset diese Disc bekäme — dieselbe Kette wie im Kern
|
||||
* (eigene Wahl → Empfehlung → Default). */
|
||||
@@ -113,10 +114,10 @@ export function LaufwerkKachel({
|
||||
? 'text-warn border-warn/45 bg-warn/10'
|
||||
: bericht.urteil === 'hardware'
|
||||
? 'text-rot border-rot/45 bg-rot/10'
|
||||
: 'text-dunst border-white/12 bg-samt/60'
|
||||
: 'text-dunst border-tinte/12 bg-samt/60'
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-2xl border border-white/10 bg-karte/50">
|
||||
<div className="relative overflow-hidden rounded-2xl border border-tinte/10 bg-karte/50">
|
||||
{backdrop.length > 0 && (
|
||||
<>
|
||||
<img src={backdrop} alt="" className="absolute inset-0 h-full w-full object-cover" />
|
||||
@@ -135,7 +136,7 @@ export function LaufwerkKachel({
|
||||
: discDa
|
||||
? 'bg-gruen'
|
||||
: geraet.status === 'empty'
|
||||
? 'bg-white/25'
|
||||
? 'bg-tinte/25'
|
||||
: 'bg-rot'
|
||||
}`}
|
||||
/>
|
||||
@@ -166,7 +167,7 @@ export function LaufwerkKachel({
|
||||
<button
|
||||
onClick={() => auswerfen(geraet.id)}
|
||||
disabled={!discDa || ripLaeuft}
|
||||
className="inline-flex items-center gap-2 rounded-xl border border-white/15 bg-gradient-to-b from-karte/80 to-samt/80 px-4 py-1.5 text-xs text-dunst shadow-[inset_0_1px_0_oklch(1_0_0/0.07)] hover:text-schnee disabled:cursor-not-allowed disabled:opacity-35"
|
||||
className="inline-flex items-center gap-2 rounded-xl border border-tinte/15 bg-gradient-to-b from-karte/80 to-samt/80 px-4 py-1.5 text-xs text-dunst shadow-[inset_0_1px_0_oklch(1_0_0/0.07)] hover:text-schnee disabled:cursor-not-allowed disabled:opacity-35"
|
||||
title="Entriegeln, auswerfen, nachsehen — in dieser Reihenfolge"
|
||||
>
|
||||
<IconEject />
|
||||
@@ -189,7 +190,7 @@ export function LaufwerkKachel({
|
||||
{poster.length > 0 ? (
|
||||
<img src={poster} alt="" className="h-44 w-[118px] flex-none rounded-xl object-cover shadow-2xl shadow-black/60 ring-1 ring-gold/35" />
|
||||
) : (
|
||||
<div className="flex h-44 w-[118px] flex-none items-center justify-center rounded-xl bg-gradient-to-br from-karte to-samt ring-1 ring-white/10">
|
||||
<div className="flex h-44 w-[118px] flex-none items-center justify-center rounded-xl bg-gradient-to-br from-karte to-samt ring-1 ring-tinte/10">
|
||||
<IconDisc groesse={40} farbe="oklch(0.5 0.05 75)" />
|
||||
</div>
|
||||
)}
|
||||
@@ -244,7 +245,7 @@ export function LaufwerkKachel({
|
||||
{d.bildPfad.length > 0 ? (
|
||||
<img src={profilUrl(d.bildPfad)} alt="" className="h-9 w-9 rounded-full object-cover ring-1 ring-gold/30" />
|
||||
) : (
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-full bg-samt font-mono text-[11px] text-nebel ring-1 ring-white/10">
|
||||
<span className="flex h-9 w-9 items-center justify-center rounded-full bg-samt font-mono text-[11px] text-nebel ring-1 ring-tinte/10">
|
||||
{d.name
|
||||
.split(' ')
|
||||
.map((w) => w[0] ?? '')
|
||||
@@ -266,7 +267,7 @@ export function LaufwerkKachel({
|
||||
<button
|
||||
onClick={aufAendern}
|
||||
disabled={ripLaeuft}
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-white/15 bg-samt/60 px-3 py-1 text-xs text-dunst hover:text-schnee disabled:opacity-35"
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-tinte/15 bg-samt/60 px-3 py-1 text-xs text-dunst hover:text-schnee disabled:opacity-35"
|
||||
title="Falsch erkannt? Selbst suchen und wählen — die Ablage folgt deiner Wahl."
|
||||
>
|
||||
<IconStift />
|
||||
@@ -277,7 +278,7 @@ export function LaufwerkKachel({
|
||||
setBerichtOffen(true)
|
||||
laufwerkPruefen(geraet.id)
|
||||
}}
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-white/15 bg-samt/60 px-3 py-1 text-xs text-dunst hover:text-schnee"
|
||||
className="inline-flex items-center gap-2 rounded-lg border border-tinte/15 bg-samt/60 px-3 py-1 text-xs text-dunst hover:text-schnee"
|
||||
title="Liest die cdrom-Ereignisse aus dem Windows-Protokoll: Disc oder Hardware?"
|
||||
>
|
||||
Laufwerk prüfen
|
||||
@@ -317,7 +318,7 @@ export function LaufwerkKachel({
|
||||
{/* Automatik (5.5.0): Countdown mit Stopp und Jetzt — oder der
|
||||
Grund, warum Rippy wartet. */}
|
||||
{discDa && !ripLaeuft && automatik !== undefined && automatik.text.length > 0 && (
|
||||
<div className={`mt-4 flex flex-wrap items-center gap-3 rounded-xl border p-3 ${automatik.aktiv ? 'border-gold/45 bg-gold/10' : 'border-white/10 bg-samt/60'}`}>
|
||||
<div className={`mt-4 flex flex-wrap items-center gap-3 rounded-xl border p-3 ${automatik.aktiv ? 'border-gold/45 bg-gold/10' : 'border-tinte/10 bg-samt/60'}`}>
|
||||
{automatik.aktiv && (
|
||||
<span className="font-mono text-[28px] font-bold leading-none text-gold-hell">
|
||||
{automatik.sekunden}
|
||||
@@ -330,7 +331,7 @@ export function LaufwerkKachel({
|
||||
<button onClick={() => automatikJetzt(geraet.id)} className="rounded-lg border border-gold/40 bg-gold/15 px-3 py-1 text-[11px] font-semibold text-gold-hell hover:bg-gold/25">
|
||||
Jetzt
|
||||
</button>
|
||||
<button onClick={() => automatikStopp(geraet.id)} className="rounded-lg border border-white/15 px-3 py-1 text-[11px] text-dunst hover:text-schnee">
|
||||
<button onClick={() => automatikStopp(geraet.id)} className="rounded-lg border border-tinte/15 px-3 py-1 text-[11px] text-dunst hover:text-schnee">
|
||||
Stopp
|
||||
</button>
|
||||
</div>
|
||||
@@ -366,7 +367,7 @@ export function LaufwerkKachel({
|
||||
)}
|
||||
|
||||
{rip !== undefined && (
|
||||
<div className="mt-4 rounded-xl border border-white/10 bg-samt/80 p-4 backdrop-blur">
|
||||
<div className="mt-4 rounded-xl border border-tinte/10 bg-samt/80 p-4 backdrop-blur">
|
||||
<div className="flex items-end justify-between gap-5">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mb-2 flex items-baseline justify-between gap-3">
|
||||
@@ -380,7 +381,9 @@ export function LaufwerkKachel({
|
||||
</span>
|
||||
{ripLaeuft && <span className="font-mono text-[10.5px] text-nebel">{restzeitText(rip.prozent, ripStartMs)}</span>}
|
||||
</div>
|
||||
{ripLaeuft && <SegmentBalken prozent={rip.prozent} />}
|
||||
{/* 5.6.0: die Vorgangs-Leiste — der ganze Weg, hier mit den
|
||||
Details der Laufwerks-Stationen; nach dem Rip nur der Stand. */}
|
||||
<VorgangsLeiste stand={leisteAusRip(rip, vorgang, wartePlatz)} />
|
||||
{ripLaeuft && (rip.titelGesamt ?? 0) > 1 && (
|
||||
<p className="mt-1.5 font-mono text-[10.5px] text-nebel">
|
||||
Titel {rip.titelNr} von {rip.titelGesamt}
|
||||
@@ -425,35 +428,22 @@ export function LaufwerkKachel({
|
||||
</p>
|
||||
))}
|
||||
|
||||
{/* Nach dem Rip (5.4.0): Wo steht die Kompression? Das
|
||||
Laufwerk ist schon frei — die Arbeit läuft im Hintergrund. */}
|
||||
{/* Nach dem Rip (5.6.0, Commander: „Nur in der Kompressions-Karte"):
|
||||
hier nur noch EINE Zeile, wo es weitergeht — Balken, Prozent,
|
||||
Restzeit und Ziel stehen in der Kompressions-Karte, danach im
|
||||
Verlauf. Fehler bleiben sichtbar (R4). */}
|
||||
{rip.phase === 'fertig' && vorgang !== undefined && (
|
||||
<div className="mt-3 rounded-lg border border-white/8 bg-buehne/60 p-2.5">
|
||||
{kompression !== null ? (
|
||||
<>
|
||||
<p className="font-mono text-[10.5px] tracking-[0.12em] text-gold-hell">
|
||||
KOMPRESSION LÄUFT{kompression.prozent >= 0 ? ` — ${kompression.prozent} %` : ''}
|
||||
</p>
|
||||
<div className="mt-1.5">
|
||||
<SegmentBalken prozent={kompression.prozent} />
|
||||
</div>
|
||||
<p className="mt-1.5 truncate font-mono text-[11px] text-nebel" title={kompression.text}>
|
||||
> {kompression.text}
|
||||
</p>
|
||||
</>
|
||||
) : wartePlatz > 0 ? (
|
||||
<p className="font-mono text-[11px] text-dunst">Kompression wartet — Platz {wartePlatz} in der Warteschlange.</p>
|
||||
) : vorgang.phase === 'fertig' ? (
|
||||
<p className="break-all font-mono text-[11px] text-gruen">
|
||||
✓ {vorgang.komprimiert ? 'Komprimiert und abgelegt' : 'Verlustfrei abgelegt'}: {vorgang.zielOrdner}
|
||||
{vorgang.fertigBytes > 0 && ` (${gb(vorgang.fertigBytes)})`}
|
||||
</p>
|
||||
) : vorgang.phase === 'fehler' || vorgang.phase === 'abgebrochen' ? (
|
||||
<p className="break-all font-mono text-[11px] text-warn">⚠ {vorgang.fehler.length > 0 ? vorgang.fehler : `Kompression ${vorgang.phase}`}</p>
|
||||
) : (
|
||||
<p className="font-mono text-[11px] text-dunst">Kompression: {vorgang.phase}</p>
|
||||
)}
|
||||
</div>
|
||||
<p className={`mt-2 font-mono text-[11px] ${vorgang.phase === 'fehler' || vorgang.phase === 'abgebrochen' ? 'text-warn' : 'text-nebel'}`}>
|
||||
{kompression !== null
|
||||
? 'Kompression läuft — Stand in der Kompressions-Karte unten.'
|
||||
: wartePlatz > 0
|
||||
? `Kompression wartet — Platz ${wartePlatz} in der Warteschlange (Karte unten).`
|
||||
: vorgang.phase === 'fertig'
|
||||
? 'Kompression fertig — das Ergebnis steht im Verlauf.'
|
||||
: vorgang.phase === 'fehler' || vorgang.phase === 'abgebrochen'
|
||||
? `⚠ ${vorgang.fehler.length > 0 ? vorgang.fehler : `Kompression ${vorgang.phase}`}`
|
||||
: `Kompression: ${vorgang.phase}`}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{ripLaeuft && rip.prozent >= 0 && (
|
||||
|
||||
Reference in New Issue
Block a user