fix: improve title and episode parsing regex for sub-episodes, seasons, and clean formatting

main
I Luk Kim 3 weeks ago
parent 0c1013d94f
commit 224f28042f

@ -7,22 +7,52 @@ export interface ParsedTitle {
export function parseTitleAndEpisode(rawTitle: string | null): ParsedTitle { export function parseTitleAndEpisode(rawTitle: string | null): ParsedTitle {
if (!rawTitle) return { title: '', episode: null, fullCleanTitle: '' }; if (!rawTitle) return { title: '', episode: null, fullCleanTitle: '' };
// Remove site name suffixes like "- 마나토키 330", "- 뉴토끼", "- newtoki" let clean = rawTitle.trim();
let clean = rawTitle
.replace(/\s*[-|_]\s*(마나토키|뉴토끼|newtoki|manatoki|웹툰|만화).*$/i, '') // 1. Remove site name / brand suffixes & domain noise
// Matches: "- 마나토키 330", "> 뉴토끼 340", ":: manatoki 100", "| 뉴토끼", " - 웹툰", " - 만화", etc.
clean = clean
.replace(/\s*[-|_|>|:|=|~|/|\\]+\s*(마나토키|뉴토끼|newtoki|manatoki|웹툰|만화|toki|토끼)\s*\d*\s*$/i, '')
.replace(/\s*[-|_|>|:|=|~|/|\\]+\s*(https?:\/\/)?[\w.-]+\.(com|org|net|xyz|me|info|live)\s*$/i, '')
.replace(/\s*[\(\[]\s*(마나토키|뉴토끼|newtoki|manatoki|웹툰|만화)\s*\d*\s*[\)\]]\s*$/i, '')
.replace(/\s+(마나토키|뉴토끼|newtoki|manatoki)\s*\d+\s*$/i, '')
.trim(); .trim();
// 1. Match explicit episode format like "150화", "150.5화", "EP.15" // 2. Main Episode Pattern:
const epMatch = clean.match(/(\d+(?:\.\d+)?\s*화|ep\.?\s*\d+)/i); // Group 1 (optional season/part): (시즌\s*\d+|[1-9]기|[1-9]부|[Pp]art\s*\d+)
// Group 2 (episode number with optional sub-part or comma): (\d{1,4}(?:,\d{3})*(?:[._-]\d+)?)
// Group 3 (unit): (화|회|화차|강|話)
const mainEpRegex = /(?:((?:시즌\s*\d+|[1-9]기|[1-9]부|[Pp]art\s*\d+))\s+)?(?:제\s*)?(\d{1,4}(?:,\d{3})*(?:[._-]\d+)?)\s*(화|회|화차|강|話)/i;
const epMatch = clean.match(mainEpRegex);
if (epMatch) { if (epMatch) {
let episode = epMatch[1].replace(/\s+/g, ''); const season = epMatch[1] ? epMatch[1].trim() + ' ' : '';
if (!episode.endsWith('화') && !episode.toLowerCase().startsWith('ep')) { const num = epMatch[2].replace(/\s+/g, '');
episode += '화'; const unit = epMatch[3] || '화';
const fullEpString = epMatch[0]; // e.g. "2기 150화" or "150-1화"
const episode = `${season}${num}${unit}`;
// Remove the episode portion from clean to extract remaining title
let title = clean.replace(fullEpString, ' ');
title = cleanUpTitleString(title);
return {
title: title || clean,
episode,
fullCleanTitle: clean,
};
} }
// Remove the matched episode part from the title
let title = clean.replace(epMatch[0], '').trim(); // 3. "EP.150", "Ep 150", "#150", "No.150" pattern
// Clean up trailing dashes/colons/dots left over const epPrefixRegex = /(?:ep\.?|#|no\.?)\s*(\d+(?:[.-_]\d+)?)/i;
title = title.replace(/[-|_:]\s*$/, '').trim(); const prefixMatch = clean.match(epPrefixRegex);
if (prefixMatch) {
const episode = `EP.${prefixMatch[1]}`;
let title = clean.replace(prefixMatch[0], ' ');
title = cleanUpTitleString(title);
return { return {
title: title || clean, title: title || clean,
@ -31,12 +61,17 @@ export function parseTitleAndEpisode(rawTitle: string | null): ParsedTitle {
}; };
} }
// 2. Match trailing numbers like "제목 150" // 4. Trailing number without '화' (e.g. "원피스 1100", "원피스 1100-1")
const numMatch = clean.match(/(\d+(?:\.\d+)?)\s*$/); const trailingNumRegex = /(?:((?:시즌\s*\d+|[1-9]기|[1-9]부|[Pp]art\s*\d+))\s+)?(\d{1,4}(?:[.-_]\d+)?)\s*$/i;
if (numMatch) { const trailingMatch = clean.match(trailingNumRegex);
const episode = `${numMatch[1]}`;
let title = clean.slice(0, numMatch.index).trim(); if (trailingMatch) {
title = title.replace(/[-|_:]\s*$/, '').trim(); const season = trailingMatch[1] ? trailingMatch[1].trim() + ' ' : '';
const num = trailingMatch[2];
const episode = `${season}${num}`;
let title = clean.slice(0, trailingMatch.index).trim();
title = cleanUpTitleString(title);
return { return {
title: title || clean, title: title || clean,
@ -47,3 +82,12 @@ export function parseTitleAndEpisode(rawTitle: string | null): ParsedTitle {
return { title: clean, episode: null, fullCleanTitle: clean }; return { title: clean, episode: null, fullCleanTitle: clean };
} }
function cleanUpTitleString(str: string): string {
let s = str.replace(/\s+/g, ' ').trim();
s = s
.replace(/^[\s\-_|:;=><~,/\\\\]+/, '')
.replace(/[\s\-_|:;=><~,/\\\\]+$/, '')
.trim();
return s;
}

Loading…
Cancel
Save