// Ablage-Namen und die Zielpfad-Vorschau — PUR, ohne Dateisystem, damit // Kern UND Fenster dieselbe Regel benutzen (5.4.0: „Vorschau, welche Datei // landet wo" im Auswahl-Dialog). Die Kern-Seite (kern/ablage/struktur.ts) // baut auf diesen Namen ihre echten Ordner. // Zeichen, die in Ordnernamen auf SMB/NTFS/ext4 Ärger machen. // eslint-Hinweis unnötig: bewusst inklusive Steuerzeichen. // eslint-disable-next-line no-control-regex const VERBOTEN = /[<>:"/\\|?*\x00-\x1f]/g /** Titel → dateisystem-tauglicher Ordnername (pur). */ export function sichererName(titel: string, jahr?: number | string | null): string { let name = (titel ?? '').replace(VERBOTEN, '').trim().replace(/\.+$/, '') name = name.replace(/\s+/g, ' ').slice(0, 150).trim() if (name.length === 0) return '' // Kein doppeltes Jahr: OMDb-Titel tragen es teils schon im Namen. if (jahr !== undefined && jahr !== null && String(jahr).length > 0 && !name.endsWith(`(${jahr})`)) { name = `${name} (${jahr})` } return name } export function staffelOrdnerName(staffel: number): string { return `Season ${String(Math.trunc(staffel)).padStart(2, '0')}` } export function folgenDateiname(serie: string, staffel: number, folge: number): string { const s = String(Math.trunc(staffel)).padStart(2, '0') const e = String(Math.trunc(folge)).padStart(2, '0') return `${sichererName(serie) || 'Serie'} S${s}E${e}.mkv` } export interface VorschauEingabe { ablage: string alsSerie: boolean titel: string jahr: number | null staffel: number | null ersteFolge: number | null /** Zahl der gewählten Folgen (Serie) bzw. Hauptfilme (Film). */ hauptTitel: number extras: number /** Der Vorgangsname — gilt, wenn kein Titel erkannt ist. */ vorgangsName: string } /** Zwei Zeilen: Zielordner und was darin landet (pur). Windows-Trenner, * weil die Vorschau eine Windows-Ablage beschreibt. */ export function zielVorschau(e: VorschauEingabe): [string, string] { const wurzel = e.ablage.replace(/[\\/]+$/, '') const titel = e.titel.trim().length > 0 ? e.titel.trim() : e.vorgangsName if (e.alsSerie) { const staffel = e.staffel ?? 1 const ordner = `${wurzel}\\Serien\\${sichererName(titel) || 'Unbekannte Serie'}\\${staffelOrdnerName(staffel)}\\` const n = Math.max(1, e.hauptTitel) let inhalt: string if (e.ersteFolge !== null && e.ersteFolge > 0) { const erste = folgenDateiname(titel, staffel, e.ersteFolge) inhalt = n === 1 ? erste : `${erste} … ${folgenDateiname(titel, staffel, e.ersteFolge + n - 1)} (${n} Folgen, fortlaufend)` } else { inhalt = `${n} ${n === 1 ? 'Folge' : 'Folgen'} — Nummern aus den TMDb-Laufzeiten, sonst bleiben die MakeMKV-Namen` } if (e.extras > 0) inhalt += ` · extras\\ (${e.extras})` return [ordner, inhalt] } const ordner = `${wurzel}\\Filme\\${sichererName(titel, e.jahr) || e.vorgangsName}\\` const teile: string[] = [] if (e.hauptTitel > 0) teile.push(e.hauptTitel === 1 ? 'der Hauptfilm' : `${e.hauptTitel} Hauptfilme`) if (e.extras > 0) teile.push(`extras\\ (${e.extras} ${e.extras === 1 ? 'Zugabe' : 'Zugaben'})`) return [ordner, teile.length > 0 ? teile.join(' · ') : 'nichts gewählt'] }