/** * Сопоставление файла с шоу по названию: имя релиза (напр. «The.Simpsons.S33E01.WEBDL…») содержит * оригинальное (или отображаемое) название шоу. Нормализуем обе строки до слов через пробел и ищем * название как цельную последовательность слов; при нескольких совпадениях берём самое длинное * («Star Trek Discovery» важнее «Star Trek»). */ type ShowNameRef = { id: string; name: string; originalName?: string | null } /** Приводит строку к «словам через пробел»: буквы/цифры сохраняем, всё прочее — разделитель. */ function normalize(value: string): string { return value .toLowerCase() .replace(/[^\p{L}\p{N}]+/gu, ' ') .trim() .replace(/\s+/g, ' ') } /** Минимальная длина названия-кандидата (в символах после нормализации) — отсекает шум. */ const MIN_CANDIDATE_LENGTH = 2 /** * Возвращает id наиболее подходящего шоу для имени файла или undefined, если совпадений нет. * Совпадением считается вхождение названия шоу как цельной последовательности слов в имя файла. */ export function matchShowByName( fileName: string, shows: readonly ShowNameRef[], ): string | undefined { const haystack = ` ${normalize(fileName)} ` let best: { id: string; length: number } | undefined for (const show of shows) { for (const candidate of [show.originalName, show.name]) { if (!candidate) continue const norm = normalize(candidate) if (norm.length < MIN_CANDIDATE_LENGTH) continue if (haystack.includes(` ${norm} `) && norm.length > (best?.length ?? 0)) { best = { id: show.id, length: norm.length } } } } return best?.id }