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
@@ -5,20 +5,25 @@
|
||||
// 5.4.0: Sprachen je Titel, Staffel/Folge von Hand, Zielpfad-Vorschau.
|
||||
// 5.5.0: Platz-Prüfung vor dem Rip und der Serien-Merker (Folge von Disc 1
|
||||
// zählt auf Disc 2 weiter).
|
||||
// 5.6.0 (Commander): Je Extra WELCHE Untertitel-Spur beim Abspielen
|
||||
// vorgewählt ist (weiche Spur, nie eingebrannt, immer optional) — und je
|
||||
// Titel „anderer Film …": Double Feature, Trilogie-Disc, Bonus-Film bekommen
|
||||
// eigenen Roh-Ordner, Vorgang und Zielordner.
|
||||
import { useState } from 'react'
|
||||
import { zielVorschau } from '../../gemeinsam/ablagepfad'
|
||||
import { sichererName, zielVorschau } from '../../gemeinsam/ablagepfad'
|
||||
import { platzUrteil } from '../../gemeinsam/platz'
|
||||
import type {
|
||||
DiscInfoStand,
|
||||
DiscTitelVorschlag,
|
||||
GeraeteInfo,
|
||||
MetaVorschlag,
|
||||
SpeicherStand,
|
||||
SprachEintrag,
|
||||
TitelRolle,
|
||||
TitelStand,
|
||||
} from '../../gemeinsam/nachrichten'
|
||||
import { DialogRahmen, KNOPF_GOLD, dauerText, gb, gbKurz, sprachenVorbelegt } from '../bausteine'
|
||||
import { ripStarten } from '../kernverbindung'
|
||||
import { DialogRahmen, IconDisc, KNOPF_GOLD, dauerText, gb, gbKurz, posterUrlKlein, sprachenVorbelegt } from '../bausteine'
|
||||
import { metadatenSuchen, ripStarten } from '../kernverbindung'
|
||||
|
||||
/** Sprach-Chips eines Titels — anklickbar, außerhalb des Auswahl-Labels
|
||||
* (ein Knopf IM Label würde die Checkbox mit umschalten). */
|
||||
@@ -45,7 +50,7 @@ function SprachChips({
|
||||
type="button"
|
||||
onClick={() => aufWechsel(s.lang)}
|
||||
className={`rounded-md border px-1.5 py-0.5 font-mono text-[10.5px] ${
|
||||
an ? 'border-gold/50 bg-gold/15 text-gold-hell' : 'border-white/12 text-nebel hover:text-dunst'
|
||||
an ? 'border-gold/50 bg-gold/15 text-gold-hell' : 'border-tinte/12 text-nebel hover:text-dunst'
|
||||
}`}
|
||||
title={`${s.sprache}${s.spuren > 1 ? ` (${s.spuren} Spuren)` : ''}`}
|
||||
>
|
||||
@@ -59,6 +64,13 @@ function SprachChips({
|
||||
)
|
||||
}
|
||||
|
||||
/** Was Rippys Regel für diesen Titel vorwählen würde (dieselbe Regel wie
|
||||
* kern/ablauf/auftrag.ts sprachwahl) — '' wenn nichts. */
|
||||
function regelVorwahl(audio: readonly string[], untertitel: readonly string[], wunsch: string): string {
|
||||
if (wunsch.length === 0 || audio.length === 0 || audio[0] === wunsch) return ''
|
||||
return untertitel.includes(wunsch) ? wunsch : ''
|
||||
}
|
||||
|
||||
export function TitelWahlDialog({
|
||||
id,
|
||||
typ,
|
||||
@@ -67,6 +79,7 @@ export function TitelWahlDialog({
|
||||
einstellungen,
|
||||
regel,
|
||||
speicher,
|
||||
vorschlaege,
|
||||
schliessen,
|
||||
}: {
|
||||
id: string
|
||||
@@ -78,6 +91,8 @@ export function TitelWahlDialog({
|
||||
* Roh-Material passiert. */
|
||||
regel: string
|
||||
speicher: SpeicherStand | null
|
||||
/** Treffer der Metadaten-Suche (für „anderer Film …"). */
|
||||
vorschlaege: MetaVorschlag[]
|
||||
schliessen: () => void
|
||||
}) {
|
||||
const [gewaehlt, setGewaehlt] = useState<Set<number> | null>(null)
|
||||
@@ -87,6 +102,12 @@ export function TitelWahlDialog({
|
||||
// Sprachen je Titel (5.4.0): Was hier nicht drinsteht, ist vorbelegt
|
||||
// (Einstellung, soweit der Titel sie hat — sonst alles, was er hat).
|
||||
const [sprachen, setSprachen] = useState<Record<number, { audio: string[]; untertitel: string[] }>>({})
|
||||
// 5.6.0: Untertitel-Vorwahl je Extra — fehlt: Rippys Regel; null: keine;
|
||||
// sonst die Sprache. Immer optional, nie eingebrannt.
|
||||
const [vorwahl, setVorwahl] = useState<Record<number, string | null>>({})
|
||||
// 5.6.0: Titel, die zu einem anderen Film gehören.
|
||||
const [filme, setFilme] = useState<Record<number, MetaVorschlag>>({})
|
||||
const [filmSuche, setFilmSuche] = useState<{ nr: number; text: string } | null>(null)
|
||||
// Staffel und erste Folge von Hand (5.4.0) — die Staffel kommt von der
|
||||
// Disc, die erste Folge vom Serien-Merker (5.5.0: die vorige Disc dieser
|
||||
// Serie) oder vom Nutzer („auf Disc 2 sind 3–4").
|
||||
@@ -115,20 +136,32 @@ export function TitelWahlDialog({
|
||||
else neu.add(nr)
|
||||
setGewaehlt(neu)
|
||||
}
|
||||
const wunschSprache = (einstellungen['audioSprachen'] ?? '').split(',')[0]?.trim().toLowerCase() ?? ''
|
||||
const alle = stand?.titel ?? []
|
||||
const gewaehlteTitel = alle.filter((t) => aktiv.has(t.nr))
|
||||
const gesamtBytes = gewaehlteTitel.reduce((s, t) => s + t.groesseBytes, 0)
|
||||
const alsSerie = gewaehlteTitel.some((t) => rolleVon(t) === 'folge')
|
||||
const hauptTitel = gewaehlteTitel.filter((t) => rolleVon(t) !== 'extra').length
|
||||
const extras = gewaehlteTitel.filter((t) => rolleVon(t) === 'extra').length
|
||||
const eigeneTitel = gewaehlteTitel.filter((t) => filme[t.nr] === undefined)
|
||||
const hauptTitel = eigeneTitel.filter((t) => rolleVon(t) !== 'extra').length
|
||||
const extras = eigeneTitel.filter((t) => rolleVon(t) === 'extra').length
|
||||
// Andere Filme (5.6.0): je Film die Zahl der Titel, für die Vorschau.
|
||||
const andereFilme = new Map<number, { film: MetaVorschlag; titel: number }>()
|
||||
for (const t of gewaehlteTitel) {
|
||||
const f = filme[t.nr]
|
||||
if (f === undefined) continue
|
||||
const e = andereFilme.get(f.tmdbId)
|
||||
if (e !== undefined) e.titel += 1
|
||||
else andereFilme.set(f.tmdbId, { film: f, titel: 1 })
|
||||
}
|
||||
const zahlOderNull = (text: string, mindestens: number): number | null => {
|
||||
const n = Number(text.trim())
|
||||
return text.trim().length > 0 && Number.isInteger(n) && n >= mindestens ? n : null
|
||||
}
|
||||
const staffelZahl = zahlOderNull(staffel, 0)
|
||||
const folgeZahl = zahlOderNull(ersteFolge, 1)
|
||||
const ablage = einstellungen['ablage'] ?? 'Videos\\Rippy'
|
||||
const vorschau = zielVorschau({
|
||||
ablage: einstellungen['ablage'] ?? 'Videos\\Rippy',
|
||||
ablage,
|
||||
alsSerie,
|
||||
titel: info?.titel ?? '',
|
||||
jahr: info?.jahr ?? null,
|
||||
@@ -141,6 +174,7 @@ export function TitelWahlDialog({
|
||||
// Platz-Prüfung (5.5.0): gegen den Arbeitsordner — dort landen Roh und Encode.
|
||||
const komprimiert = einstellungen['transcodeEnabled'] !== 'false'
|
||||
const platz = speicher !== null && speicher.arbeit.ok ? platzUrteil(speicher.arbeit.freiBytes, gesamtBytes, komprimiert) : { text: '', hart: false }
|
||||
const filmTreffer = vorschlaege.filter((v) => v.typ === 'movie')
|
||||
|
||||
return (
|
||||
<DialogRahmen schliessen={schliessen} breite="max-w-3xl">
|
||||
@@ -174,14 +208,14 @@ export function TitelWahlDialog({
|
||||
new Set(alle.filter((t) => !t.grund.startsWith('Sammeltitel') && !t.grund.startsWith('zu kurz')).map((t) => t.nr)),
|
||||
)
|
||||
}
|
||||
className="rounded-lg border border-white/15 px-2.5 py-1 text-[11px] text-dunst hover:text-schnee"
|
||||
className="rounded-lg border border-tinte/15 px-2.5 py-1 text-[11px] text-dunst hover:text-schnee"
|
||||
>
|
||||
Hauptinhalt + Extras
|
||||
</button>
|
||||
<button onClick={() => setGewaehlt(new Set(alle.map((t) => t.nr)))} className="rounded-lg border border-white/15 px-2.5 py-1 text-[11px] text-dunst hover:text-schnee">
|
||||
<button onClick={() => setGewaehlt(new Set(alle.map((t) => t.nr)))} className="rounded-lg border border-tinte/15 px-2.5 py-1 text-[11px] text-dunst hover:text-schnee">
|
||||
Alles
|
||||
</button>
|
||||
<button onClick={() => setGewaehlt(new Set())} className="rounded-lg border border-white/15 px-2.5 py-1 text-[11px] text-dunst hover:text-schnee">
|
||||
<button onClick={() => setGewaehlt(new Set())} className="rounded-lg border border-tinte/15 px-2.5 py-1 text-[11px] text-dunst hover:text-schnee">
|
||||
Nichts
|
||||
</button>
|
||||
</div>
|
||||
@@ -190,8 +224,12 @@ export function TitelWahlDialog({
|
||||
{alle.map((t) => {
|
||||
const an = aktiv.has(t.nr)
|
||||
const wahl = sprachenVon(t)
|
||||
const rolle = rolleVon(t)
|
||||
const film = filme[t.nr]
|
||||
const regelWahl = regelVorwahl(wahl.audio.length > 0 ? wahl.audio : t.sprachen.audio.map((s) => s.lang), wahl.untertitel, wunschSprache)
|
||||
const vorwahlWert = t.nr in vorwahl ? (vorwahl[t.nr] === null ? 'keine' : vorwahl[t.nr]!) : 'regel'
|
||||
return (
|
||||
<div key={t.nr} className={`rounded-lg border ${an ? 'border-gold/40 bg-gold/8' : 'border-white/8 bg-samt/40'}`}>
|
||||
<div key={t.nr} className={`rounded-lg border ${an ? 'border-gold/40 bg-gold/8' : 'border-tinte/8 bg-samt/40'}`}>
|
||||
<label className="flex cursor-pointer items-center gap-3 p-2">
|
||||
<input type="checkbox" checked={an} onChange={() => umschalten(t.nr)} className="accent-[oklch(0.83_0.11_75)]" />
|
||||
<span className="w-16 flex-none font-mono text-[11px] text-nebel">Titel {t.nr}</span>
|
||||
@@ -200,10 +238,10 @@ export function TitelWahlDialog({
|
||||
<span className="hidden w-20 flex-none font-mono text-[11px] text-nebel sm:inline">{t.kapitel} Kap.</span>
|
||||
{an ? (
|
||||
<select
|
||||
value={rolleVon(t)}
|
||||
value={rolle}
|
||||
onChange={(e) => setRollen({ ...rollen, [t.nr]: e.target.value as TitelRolle })}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex-none rounded-lg border border-white/15 bg-samt/70 px-2 py-0.5 text-[11px] text-dunst"
|
||||
className="flex-none rounded-lg border border-tinte/15 bg-samt/70 px-2 py-0.5 text-[11px] text-dunst"
|
||||
title="Bestimmt, WOHIN der Titel abgelegt wird"
|
||||
>
|
||||
<option value="hauptfilm">Hauptfilm</option>
|
||||
@@ -218,12 +256,126 @@ export function TitelWahlDialog({
|
||||
Titel gemessen hat — anhaken, was in die Datei soll.
|
||||
Der Commander-Fall: japanischer Ton, deutsche Untertitel. */}
|
||||
{an && (t.sprachen.audio.length > 0 || t.sprachen.untertitel.length > 0) && (
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-white/8 px-2 py-1.5 pl-9">
|
||||
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-tinte/8 px-2 py-1.5 pl-9">
|
||||
<SprachChips name="TON" vorhanden={t.sprachen.audio} gewaehlt={wahl.audio} aufWechsel={(lang) => sprachWechsel(t, 'audio', lang)} />
|
||||
<SprachChips name="UNTERTITEL" vorhanden={t.sprachen.untertitel} gewaehlt={wahl.untertitel} aufWechsel={(lang) => sprachWechsel(t, 'untertitel', lang)} />
|
||||
{wahl.audio.length === 0 && t.sprachen.audio.length > 0 && (
|
||||
<span className="font-mono text-[10.5px] text-warn">kein Ton gewählt — HandBrake nimmt die erste Spur</span>
|
||||
)}
|
||||
{/* 5.6.0: Vorwahl NUR bei Extras, IMMER optional — eine
|
||||
weiche Spur, die der Spieler von selbst einblendet;
|
||||
eingebrannt wird nie. */}
|
||||
{rolle === 'extra' && wahl.untertitel.length > 0 && (
|
||||
<label className="inline-flex items-center gap-1.5" title="Weiche Untertitel-Spur, die beim Abspielen schon an ist — nichts wird eingebrannt. Leer lassen ist immer erlaubt.">
|
||||
<span className="font-mono text-[10px] tracking-wider text-nebel">BEIM ABSPIELEN AN</span>
|
||||
<select
|
||||
value={vorwahlWert}
|
||||
onChange={(e) => {
|
||||
const w = e.target.value
|
||||
const neu = { ...vorwahl }
|
||||
if (w === 'regel') delete neu[t.nr]
|
||||
else neu[t.nr] = w === 'keine' ? null : w
|
||||
setVorwahl(neu)
|
||||
}}
|
||||
className="rounded-md border border-tinte/15 bg-samt/70 px-1.5 py-0.5 font-mono text-[10.5px] text-dunst"
|
||||
>
|
||||
<option value="regel">{regelWahl.length > 0 ? `Rippys Regel (${regelWahl})` : 'Rippys Regel (keine)'}</option>
|
||||
<option value="keine">keine</option>
|
||||
{wahl.untertitel.map((u) => (
|
||||
<option key={u} value={u}>
|
||||
{u}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* 5.6.0: „anderer Film …" — Double Feature, Trilogie-Disc,
|
||||
Bonus-Film: eigener Roh-Ordner, Vorgang und Zielordner. */}
|
||||
{an && rolle !== 'folge' && (
|
||||
<div className="flex flex-wrap items-center gap-2 border-t border-tinte/8 px-2 py-1.5 pl-9">
|
||||
{film !== undefined ? (
|
||||
<>
|
||||
<span className="inline-flex items-center gap-1.5 rounded-md border border-gold/40 bg-gold/12 px-2 py-0.5 text-[11px] text-gold-hell">
|
||||
→ eigener Film: {film.titel}
|
||||
{film.jahr !== null ? ` (${film.jahr})` : ''}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const neu = { ...filme }
|
||||
delete neu[t.nr]
|
||||
setFilme(neu)
|
||||
}}
|
||||
className="ml-1 text-nebel hover:text-rot"
|
||||
title="wieder zur Disc-Zuordnung"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setFilmSuche(filmSuche?.nr === t.nr ? null : { nr: t.nr, text: '' })}
|
||||
className="rounded-md border border-tinte/15 px-2 py-0.5 text-[11px] text-nebel hover:text-schnee"
|
||||
title="Dieser Titel ist ein anderer Film (Double Feature, Bonus-Film): eigener Ordner mit Poster und Infodatei"
|
||||
>
|
||||
anderer Film …
|
||||
</button>
|
||||
)}
|
||||
{filmSuche?.nr === t.nr && (
|
||||
<div className="w-full rounded-lg border border-tinte/10 bg-buehne/70 p-2">
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
value={filmSuche.text}
|
||||
autoFocus
|
||||
onChange={(e) => setFilmSuche({ nr: t.nr, text: e.target.value })}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') metadatenSuchen(id, filmSuche.text)
|
||||
}}
|
||||
placeholder="Filmtitel suchen …"
|
||||
className="flex-1 rounded-lg border border-tinte/12 bg-samt px-3 py-1.5 font-mono text-xs text-schnee placeholder:text-nebel focus:border-gold/60 focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => metadatenSuchen(id, filmSuche.text)}
|
||||
className="rounded-lg border border-gold/50 bg-gold/10 px-3 py-1.5 text-xs font-semibold text-gold-hell hover:bg-gold/20"
|
||||
>
|
||||
Suchen
|
||||
</button>
|
||||
</div>
|
||||
{filmTreffer.length === 0 ? (
|
||||
<p className="mt-2 font-mono text-[10.5px] text-nebel">Noch keine Treffer — Titel eingeben und suchen (braucht den TMDb-Schlüssel).</p>
|
||||
) : (
|
||||
<div className="mt-2 max-h-48 space-y-1 overflow-y-auto">
|
||||
{filmTreffer.map((v) => (
|
||||
<button
|
||||
key={v.tmdbId}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setFilme({ ...filme, [t.nr]: v })
|
||||
setFilmSuche(null)
|
||||
}}
|
||||
className="flex w-full items-center gap-2 rounded-lg border border-tinte/10 bg-karte/60 p-1.5 text-left hover:border-gold/50"
|
||||
>
|
||||
{v.posterPfad.length > 0 ? (
|
||||
<img src={posterUrlKlein(v.posterPfad)} alt="" className="h-10 w-7 rounded object-cover" />
|
||||
) : (
|
||||
<span className="flex h-10 w-7 items-center justify-center rounded bg-samt">
|
||||
<IconDisc groesse={14} farbe="oklch(0.55 0.015 80)" />
|
||||
</span>
|
||||
)}
|
||||
<span className="text-xs text-schnee">
|
||||
{v.titel}
|
||||
{v.jahr !== null ? ` (${v.jahr})` : ''}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -233,7 +385,7 @@ export function TitelWahlDialog({
|
||||
</div>
|
||||
|
||||
{alsSerie && (
|
||||
<div className="mt-3 flex flex-wrap items-end gap-3 rounded-xl border border-white/8 bg-samt/40 p-3">
|
||||
<div className="mt-3 flex flex-wrap items-end gap-3 rounded-xl border border-tinte/8 bg-samt/40 p-3">
|
||||
<label className="block">
|
||||
<span className="font-mono text-[10px] tracking-wider text-nebel">STAFFEL</span>
|
||||
<input
|
||||
@@ -242,7 +394,7 @@ export function TitelWahlDialog({
|
||||
value={staffel}
|
||||
onChange={(e) => setStaffel(e.target.value)}
|
||||
placeholder={info !== undefined && info.staffel !== null ? String(info.staffel) : '1'}
|
||||
className="mt-1 block w-20 rounded-lg border border-white/12 bg-buehne px-2 py-1 font-mono text-xs text-schnee"
|
||||
className="mt-1 block w-20 rounded-lg border border-tinte/12 bg-buehne px-2 py-1 font-mono text-xs text-schnee"
|
||||
/>
|
||||
</label>
|
||||
<label className="block">
|
||||
@@ -253,7 +405,7 @@ export function TitelWahlDialog({
|
||||
value={ersteFolge}
|
||||
onChange={(e) => setErsteFolge(e.target.value)}
|
||||
placeholder="automatisch"
|
||||
className="mt-1 block w-28 rounded-lg border border-white/12 bg-buehne px-2 py-1 font-mono text-xs text-schnee"
|
||||
className="mt-1 block w-28 rounded-lg border border-tinte/12 bg-buehne px-2 py-1 font-mono text-xs text-schnee"
|
||||
/>
|
||||
</label>
|
||||
<p className="min-w-[200px] flex-1 text-[11px] leading-4 text-nebel">
|
||||
@@ -266,11 +418,25 @@ export function TitelWahlDialog({
|
||||
)}
|
||||
|
||||
{/* Vorschau (5.4.0): Rolle, Serie, Staffel und Extras entscheiden
|
||||
einiges — zwei Zeilen Zielpfad ersparen Überraschungen. */}
|
||||
<div className="mt-3 rounded-xl border border-white/8 bg-buehne/70 p-3 font-mono text-[11px] leading-5">
|
||||
einiges — zwei Zeilen Zielpfad ersparen Überraschungen. 5.6.0:
|
||||
jeder weitere Film bekommt seine Zeile. */}
|
||||
<div className="mt-3 rounded-xl border border-tinte/8 bg-buehne/70 p-3 font-mono text-[11px] leading-5">
|
||||
<p className="text-[10px] tracking-wider text-nebel">LANDET IN</p>
|
||||
<p className="break-all text-gold-hell">{vorschau[0]}</p>
|
||||
<p className="text-dunst">{vorschau[1]}</p>
|
||||
{eigeneTitel.length > 0 && (
|
||||
<>
|
||||
<p className="break-all text-gold-hell">{vorschau[0]}</p>
|
||||
<p className="text-dunst">{vorschau[1]}</p>
|
||||
</>
|
||||
)}
|
||||
{[...andereFilme.values()].map(({ film, titel }) => (
|
||||
<p key={film.tmdbId} className="break-all text-gold-hell">
|
||||
{`${ablage.replace(/[\\/]+$/, '')}\\Filme\\${sichererName(film.titel, film.jahr) || film.titel}\\`}
|
||||
<span className="text-dunst">
|
||||
{' '}
|
||||
— {titel} {titel === 1 ? 'Titel' : 'Titel'}, eigener Vorgang mit Poster und Infodatei
|
||||
</span>
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Platz-Prüfung (5.5.0): 40 Minuten Lesen sollen nicht an einem
|
||||
@@ -297,7 +463,7 @@ export function TitelWahlDialog({
|
||||
)}
|
||||
|
||||
<div className="mt-5 flex justify-between gap-2">
|
||||
<button onClick={schliessen} className="rounded-lg border border-white/15 px-4 py-1.5 text-xs text-dunst hover:text-schnee">
|
||||
<button onClick={schliessen} className="rounded-lg border border-tinte/15 px-4 py-1.5 text-xs text-dunst hover:text-schnee">
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
@@ -306,13 +472,16 @@ export function TitelWahlDialog({
|
||||
id,
|
||||
gewaehlteTitel.map((t) => {
|
||||
const wahl = sprachenVon(t)
|
||||
const rolle = rolleVon(t)
|
||||
return {
|
||||
nr: t.nr,
|
||||
rolle: rolleVon(t),
|
||||
rolle,
|
||||
dauerS: t.dauerS,
|
||||
groesseBytes: t.groesseBytes,
|
||||
audioSprachen: wahl.audio,
|
||||
untertitelSprachen: wahl.untertitel,
|
||||
...(rolle === 'extra' && t.nr in vorwahl ? { untertitelVorwahl: vorwahl[t.nr] } : {}),
|
||||
...(filme[t.nr] !== undefined ? { film: filme[t.nr] } : {}),
|
||||
}
|
||||
}),
|
||||
{ staffel: alsSerie ? staffelZahl : null, ersteFolge: alsSerie ? folgeZahl : null },
|
||||
|
||||
Reference in New Issue
Block a user