/** * 续作 / 章节 / Part 序号(ID-2):`Chapter III`、`Part Two`、`Vol. 2`、`第3章`、`第三章`、`Ⅲ`。 * 只认显式关键词或罗马数字符号,不把片名末尾的普通数字当序号(`Blade Runner 2049`、`Apollo 13`)。 * 季号不在这里(季由解析器单独给出)。 */ import { cnToInt } from "../release-parser/cn-number.ts"; const WORD_NUM: Record = { one: 1, two: 2, three: 3, four: 4, five: 5, six: 6, seven: 7, eight: 8, nine: 9, ten: 10, first: 1, second: 2, third: 3, fourth: 4, final: -1, }; const ROMAN: Record = { i: 1, ii: 2, iii: 3, iv: 4, v: 5, vi: 6, vii: 7, viii: 8, ix: 9, x: 10 }; const ROMAN_CHAR: Record = { Ⅰ: 1, Ⅱ: 2, Ⅲ: 3, Ⅳ: 4, Ⅴ: 5, Ⅵ: 6, Ⅶ: 7, Ⅷ: 8, Ⅸ: 9, Ⅹ: 10, }; function toNum(tok: string): number | undefined { const t = tok.toLowerCase(); if (/^\d{1,2}$/.test(t)) return Number(t); if (t in ROMAN) return ROMAN[t]; if (t in WORD_NUM) return WORD_NUM[t]! > 0 ? WORD_NUM[t] : undefined; return undefined; } export function sequelNumbers(title: string): number[] { if (!title) return []; const out = new Set(); const s = title.replace(/[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ]/g, (c) => ` ${ROMAN_CHAR[c]} `); for (const m of s.matchAll(/\b(?:chapter|part|vol(?:ume)?)\b\.?\s*[::-]?\s*([0-9]{1,2}|[ivx]{1,4}|[a-z]+)\b/giu)) { const n = toNum(m[1]!); if (n !== undefined) out.add(n); } for (const m of s.matchAll(/第\s*([0-9]{1,2}|[一二三四五六七八九十]{1,3})\s*章/gu)) { const n = /^\d+$/.test(m[1]!) ? Number(m[1]) : cnToInt(m[1]!); if (n !== undefined) out.add(n); } return [...out]; }