// Einstellungen (§ 6.7): Das Fenster liest und schreibt NUR über den Kern; // erlaubt sind genau die Schlüssel hier. Dazu die Pfad-Regeln: Ablage // (Fertiges — beim Commander der Server) und seit 5.5.0 der Arbeitsordner // (Roh-Dateien und Kompression — lokal, schnell; Vorgabe: unter der Ablage, // also wie bisher). import { join } from 'node:path' import { AUFBEWAHRUNG_SCHLUESSEL } from '../ablage/rohaufraeumen' import { einstellung, type KernKontext } from '../kontext' export const ERLAUBTE_EINSTELLUNGEN: ReadonlySet = new Set([ 'ablage', 'arbeitsordner', 'transcodeEnabled', 'transcodePresetDvd', 'transcodePresetBluray', 'transcodePresetUhd', 'audioSprachen', 'untertitelSprachen', 'mediaServer', 'mediaServerUrl', 'mediaServerApiKey', 'tmdbApiKey', 'omdbApiKey', 'makemkvAppKey', 'updateUrl', 'setup.done', 'autoAuswurf', 'werkzeug.makemkv', 'werkzeug.handbrake', 'werkzeug.flac', AUFBEWAHRUNG_SCHLUESSEL, // 5.5.0 'automatik', 'automatikSekunden', 'benachrichtigungen', 'discordWebhook', 'telegramToken', 'telegramChatId', 'design', // 5.7.0: wie lange makemkvcon die Titel lesen darf (Kandidat 2 der Durchsicht) 'titelLesenMinuten', ]) /** Die Schlüssel, die Rip und Kompression lesen (Pipeline/Ablage). Das * frühere „Allgemeine Preset" (transcodePreset) ist seit 5.5.0 raus — * Commander-Wunsch: je Disc-Typ reicht, ein unsichtbarer Übersteuerer * verwirrt nur. */ const PIPELINE_EINSTELLUNGEN = [ 'transcodeEnabled', 'transcodePresetDvd', 'transcodePresetBluray', 'transcodePresetUhd', 'audioSprachen', 'untertitelSprachen', 'mediaServer', 'mediaServerUrl', 'mediaServerApiKey', ] as const export const AUTOMATIK_SEKUNDEN_VORGABE = 30 /** 5.7.0: Vorgabe 15 min (Commander-Entscheid 12.09.2026) — kopiergeschützte * DVDs und Discs mit vielen Playlisten brauchen im MakeMKV-GUI regelmäßig * länger als die alten fünf Minuten. */ export const TITEL_LESEN_MINUTEN_VORGABE = 15 export class EinstellungsDienst { constructor(private readonly kontext: KernKontext) {} lesen(schluessel: string, vorgabe = ''): string { return einstellung(this.kontext, schluessel, vorgabe) } /** Wurzel der Ablage — Fertiges, Musik, ISO. */ ablageOrdner(): string { const eingestellt = this.lesen('ablage') if (eingestellt.length > 0) return eingestellt return join(process.env['USERPROFILE'] ?? '.', 'Videos', 'Rippy') } /** Ein eigener Arbeitsordner — '' wenn keiner gesetzt ist. */ arbeitsordnerEigen(): string { return this.lesen('arbeitsordner').trim() } /** Wo gearbeitet wird: der eigene Arbeitsordner, sonst die Ablage. */ arbeitsordner(): string { const eigen = this.arbeitsordnerEigen() return eigen.length > 0 ? eigen : this.ablageOrdner() } /** Wo die Roh-Ordner liegen. */ rohWurzel(): string { return join(this.arbeitsordner(), 'roh') } /** Was Pipeline und Ablage brauchen. */ fuerPipeline(): Record { const werte: Record = {} for (const schluessel of PIPELINE_EINSTELLUNGEN) { const wert = this.kontext.db?.einstellungLesen(schluessel) if (wert !== null && wert !== undefined) werte[schluessel] = wert } return werte } automatikAn(): boolean { return this.lesen('automatik', 'an') !== 'aus' } /** Grenze fürs Titel-Lesen in Minuten (5–60, Vorgabe 15). */ titelLesenMinuten(): number { const n = Number(this.lesen('titelLesenMinuten', String(TITEL_LESEN_MINUTEN_VORGABE))) return Number.isInteger(n) && n >= 5 && n <= 60 ? n : TITEL_LESEN_MINUTEN_VORGABE } automatikSekunden(): number { const n = Number(this.lesen('automatikSekunden', String(AUTOMATIK_SEKUNDEN_VORGABE))) return Number.isInteger(n) && n >= 5 && n <= 600 ? n : AUTOMATIK_SEKUNDEN_VORGABE } /** Alle erlaubten Werte ans Fenster — AUCH ans Haupt (Update-Adresse, § 6.8). */ senden(): void { const werte: Record = { ablage: this.ablageOrdner() } for (const schluessel of ERLAUBTE_EINSTELLUNGEN) { const wert = this.kontext.db?.einstellungLesen(schluessel) if (wert !== null && wert !== undefined) werte[schluessel] = wert } this.kontext.anHauptUndFenster({ art: 'einstellungen', werte }) } /** Schreiben — nur erlaubte Schlüssel, nur mit Datenbank. Liefert, ob es klappte. */ setzen(schluessel: string, wert: string): boolean { if (!ERLAUBTE_EINSTELLUNGEN.has(schluessel)) { this.kontext.fehler(`Unbekannter Einstellungs-Schlüssel: ${schluessel}`) return false } if (this.kontext.db === null) { this.kontext.fehler('Einstellung nicht gespeichert — die Datenbank ist nicht da.') return false } this.kontext.db.einstellungSchreiben(schluessel, wert) this.kontext.protokoll.schreiben(`Einstellung ${schluessel} gesetzt${istGeheim(schluessel) ? '' : `: ${wert.slice(0, 120)}`}`) this.senden() return true } } function istGeheim(schluessel: string): boolean { return /key|token|webhook|passwort/i.test(schluessel) }