Files
MangaCircus/scraper/lib/names.js
T

51 lines
1.6 KiB
JavaScript

'use strict';
const path = require('path');
const { isSpecialKey } = require('../../chapter-identity');
const RESERVED = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
function cleanPart(value, max = 120) {
let result = String(value || '')
.normalize('NFC')
.replace(/[<>:"/\\|?*\x00-\x1f]/g, '')
.replace(/[\[\]]/g, '')
.replace(/\s+/g, ' ')
.trim()
.replace(/[ .]+$/g, '');
if (!result) result = 'Untitled';
if (RESERVED.test(result)) result += '-manga';
return result.slice(0, max).replace(/[ .]+$/g, '');
}
function chapterKey(value) {
const raw = String(value).trim();
if (isSpecialKey(raw)) return raw.toLowerCase();
if (/^\d+(?:\.\d+)?$/.test(raw)) {
const [integer, decimal = ''] = raw.split('.');
const normalized = decimal.replace(/0+$/g, '');
return `c${integer.padStart(4, '0')}${normalized ? `.${normalized}` : ''}`;
}
const extra = raw.match(/^x(\d+)$/i);
if (extra) return `x${extra[1].padStart(2, '0')}`;
throw new Error(`Unsupported chapter number: ${value}`);
}
function chapterFolder(chapter) {
const parts = [chapterKey(chapter.number)];
if (chapter.title) parts.push(cleanPart(chapter.title));
parts.push(`[${cleanPart(chapter.source.name, 50)}]`);
return parts.join(' ');
}
function assertInside(parent, child) {
const root = path.resolve(parent);
const target = path.resolve(child);
if (target !== root && !target.startsWith(root + path.sep)) {
throw new Error(`Path escapes library root: ${target}`);
}
return target;
}
module.exports = { cleanPart, chapterKey, chapterFolder, assertInside };