From ed44d866dffe55935395d449307d2881374f3ba2 Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Mon, 16 Mar 2026 08:36:02 +0000 Subject: [PATCH] 0.10.2-7 Layout/Chars --- backend/src/services/cdRipService.js | 6 ++---- backend/src/utils/files.js | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/backend/src/services/cdRipService.js b/backend/src/services/cdRipService.js index e966030..aff7916 100644 --- a/backend/src/services/cdRipService.js +++ b/backend/src/services/cdRipService.js @@ -5,7 +5,7 @@ const { promisify } = require('util'); const logger = require('./logger').child('CD_RIP'); const { spawnTrackedProcess } = require('./processRunner'); const { parseCdParanoiaProgress } = require('../utils/progressParsers'); -const { ensureDir } = require('../utils/files'); +const { ensureDir, transliterateForFilename } = require('../utils/files'); const { errorToMeta } = require('../utils/errorMeta'); const execFileAsync = promisify(execFile); @@ -120,10 +120,8 @@ function buildOutputFilename(track, meta, format, outputTemplate = DEFAULT_CD_OU } function sanitizePathSegment(value, fallback = 'unknown') { - const raw = String(value == null ? '' : value) - .normalize('NFC') + const raw = transliterateForFilename(String(value == null ? '' : value)) .replace(/[\\/:*?"<>|]/g, '-') - // Keep umlauts/special letters, but filter heart symbols in filenames. .replace(/[♥❤♡❥❣❦❧]/gu, ' ') .replace(/\p{C}+/gu, ' ') .replace(/\s+/g, ' ') diff --git a/backend/src/utils/files.js b/backend/src/utils/files.js index f1705f4..c0bdcde 100644 --- a/backend/src/utils/files.js +++ b/backend/src/utils/files.js @@ -5,8 +5,29 @@ function ensureDir(dirPath) { fs.mkdirSync(dirPath, { recursive: true }); } +function transliterateForFilename(input) { + return String(input || '') + // German — must come before NFD to get ae/oe/ue instead of just a/o/u + .replace(/ä/g, 'ae').replace(/Ä/g, 'Ae') + .replace(/ö/g, 'oe').replace(/Ö/g, 'Oe') + .replace(/ü/g, 'ue').replace(/Ü/g, 'Ue') + .replace(/ß/g, 'ss') + // Danish / Norwegian / Swedish + .replace(/å/g, 'a').replace(/Å/g, 'A') + .replace(/æ/g, 'ae').replace(/Æ/g, 'Ae') + .replace(/ø/g, 'o').replace(/Ø/g, 'O') + // Icelandic + .replace(/ð/g, 'd').replace(/Ð/g, 'D') + .replace(/þ/g, 'th').replace(/Þ/g, 'Th') + // NFD decomposition + strip combining diacritical marks (handles é→e, ñ→n, ç→c, etc.) + .normalize('NFD') + .replace(/\p{M}+/gu, '') + // Drop any remaining non-ASCII characters + .replace(/[^\x00-\x7F]/g, ''); +} + function sanitizeFileName(input) { - return String(input || 'untitled') + return transliterateForFilename(String(input || 'untitled')) .replace(/[\\/:*?"<>|]/g, '_') .replace(/\s+/g, ' ') .trim() @@ -63,6 +84,7 @@ function findMediaFiles(dirPath, extensions = ['.mkv', '.mp4']) { module.exports = { ensureDir, + transliterateForFilename, sanitizeFileName, renderTemplate, findLargestMediaFile,