src/main/infra/format-helpers.ts. main.ts adapter for getMergeGroupPhaseText injects config.language. 210 unit tests gruen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79 lines
2.9 KiB
TypeScript
79 lines
2.9 KiB
TypeScript
// Pure-Format-Helpers, extrahiert aus main.ts. Keine Globals, keine I/O.
|
|
|
|
const FILENAME_INVALID_RE = /[<>:"|?*\x00-\x1f]/g;
|
|
const FILENAME_PATH_SEP_RE = /[\\/]/g;
|
|
|
|
/**
|
|
* Entfernt Windows-Filesystem-verbotene Zeichen und Pfad-Separatoren aus einem
|
|
* Datei-Namen-Teilstring. Fallback wird zurueckgegeben, wenn nach Cleanup
|
|
* nichts uebrig bleibt.
|
|
*/
|
|
export function sanitizeFilenamePart(input: string, fallback = 'unnamed'): string {
|
|
const cleaned = (input || '')
|
|
.replace(FILENAME_INVALID_RE, '_')
|
|
.replace(FILENAME_PATH_SEP_RE, '_')
|
|
.trim();
|
|
return cleaned || fallback;
|
|
}
|
|
|
|
/**
|
|
* Twitch-Style Duration-Format: `1h2m3s`, `2m5s`, `42s`. Negative oder
|
|
* NaN-Inputs werden auf 0 geclamt.
|
|
*/
|
|
export function formatTwitchDurationFromSeconds(totalSeconds: number): string {
|
|
const seconds = Math.max(0, Math.floor(Number.isFinite(totalSeconds) ? totalSeconds : 0));
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
const s = seconds % 60;
|
|
|
|
if (h > 0) return `${h}h${m}m${s}s`;
|
|
if (m > 0) return `${m}m${s}s`;
|
|
return `${s}s`;
|
|
}
|
|
|
|
const DATE_TOKEN_RE = /yyyy|yy|MM|M|dd|d|HH|H|hh|h|mm|m|ss|s/g;
|
|
|
|
/**
|
|
* Date-Formatter mit Pattern-Tokens (yyyy, yy, MM, M, dd, d, HH, H, hh, h,
|
|
* mm, m, ss, s). Backslash-escapes (\T) lassen das Folgezeichen literal.
|
|
*/
|
|
export function formatDateWithPattern(date: Date, pattern: string): string {
|
|
const tokenMap: Record<string, string> = {
|
|
yyyy: date.getFullYear().toString(),
|
|
yy: date.getFullYear().toString().slice(-2),
|
|
MM: (date.getMonth() + 1).toString().padStart(2, '0'),
|
|
M: (date.getMonth() + 1).toString(),
|
|
dd: date.getDate().toString().padStart(2, '0'),
|
|
d: date.getDate().toString(),
|
|
HH: date.getHours().toString().padStart(2, '0'),
|
|
H: date.getHours().toString(),
|
|
hh: date.getHours().toString().padStart(2, '0'),
|
|
h: date.getHours().toString(),
|
|
mm: date.getMinutes().toString().padStart(2, '0'),
|
|
m: date.getMinutes().toString(),
|
|
ss: date.getSeconds().toString().padStart(2, '0'),
|
|
s: date.getSeconds().toString(),
|
|
};
|
|
|
|
return pattern
|
|
.replace(DATE_TOKEN_RE, token => tokenMap[token] ?? token)
|
|
.replace(/\\(.)/g, '$1');
|
|
}
|
|
|
|
export type MergeGroupLanguage = 'de' | 'en';
|
|
|
|
/**
|
|
* Label fuer den aktuellen Merge-Group-Phase-Status. Pure variant — Sprache
|
|
* wird vom Caller injiziert.
|
|
*/
|
|
export function getMergeGroupPhaseText(phase: string, language: MergeGroupLanguage | string): string {
|
|
const isEnglish = language === 'en';
|
|
switch (phase) {
|
|
case 'downloading': return isEnglish ? 'Downloading VOD' : 'VOD wird heruntergeladen';
|
|
case 'merging': return isEnglish ? 'Merging...' : 'Zusammenfugen...';
|
|
case 'splitting': return isEnglish ? 'Splitting Part' : 'Part wird erstellt';
|
|
case 'cleanup': return isEnglish ? 'Cleaning up...' : 'Aufraumen...';
|
|
default: return phase;
|
|
}
|
|
}
|