@gondola-lyrics/kit 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/formats.d.ts +224 -6
- package/dist/formats.js +952 -169
- package/dist/formats.js.map +1 -1
- package/dist/generator.d.ts +1 -1
- package/dist/index.d.ts +92 -4
- package/dist/index.js +2 -2
- package/dist/parser.d.ts +92 -4
- package/dist/parser.js +2 -2
- package/dist/{language.js → rules.js} +51 -2
- package/dist/rules.js.map +1 -0
- package/dist/transforms.d.ts +10 -2
- package/dist/transforms.js +47 -87
- package/dist/transforms.js.map +1 -1
- package/package.json +1 -1
- package/dist/language.js.map +0 -1
package/dist/transforms.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transforms.js","names":["$schema","version","language","normalization","groups","$schema","version","language","normalization","groups","zhHansRules","enRules","$schema","version","language","normalization","groups","zhHansRules","enRules"],"sources":["../src/utils/rules.ts","../src/transforms/agent/extract.ts","../src/transforms/background/utils.ts","../src/transforms/background/brackets.ts","../src/transforms/background/extract.ts","../rules/credits/en.json","../rules/credits/zh-hans.json","../src/transforms/credit/rules.ts","../src/transforms/credit/extract.ts","../src/transforms/emphasis/index.ts","../rules/filters/en.json","../rules/filters/zh-hans.json","../src/transforms/filter/rules.ts","../src/transforms/filter/remove.ts","../src/transforms/instrumental/index.ts","../src/transforms/space/constants.ts","../src/transforms/space/rules.ts","../src/transforms/space/insert.ts"],"sourcesContent":["import { findScriptRunEnd } from './language'\n\nconst RULE_WHITESPACE = /[\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000\\uFEFF]/g\nconst RULE_EDGE_WHITESPACE =\n /^[\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000\\uFEFF]+|[\\u0009-\\u000D\\u0020\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000\\uFEFF]+$/g\n\n/**\n * Uses the shared whitespace set and ASCII case folding to keep matching consistent across language kits.\n */\nexport const toRuleMatchKey = (text: string): string => {\n return text.replace(RULE_WHITESPACE, '').replace(/[A-Z]+/g, (run) => run.toLowerCase())\n}\n\nconst RULE_WHITESPACE_CHARACTER = new RegExp(RULE_WHITESPACE.source)\n\n/**\n * Finds the offsets in toRuleMatchKey(text) where the source allows a cut.\n * A cut sits before the first kept character of a script run and after configured whitespace, so neutral punctuation never splits aliases such as A&R or Co-Producer.\n *\n * @param text A source label with its original spelling.\n *\n * @returns Ascending offsets from 0 to the key length, or undefined when the source has no inner cut.\n */\nexport const findRuleMatchCuts = (text: string): readonly number[] | undefined => {\n // Most labels are a single script run without inner whitespace, so they skip the character walk.\n if (!RULE_WHITESPACE_CHARACTER.test(text) && findScriptRunEnd(text) === text.length) {\n return undefined\n }\n\n const cuts: number[] = [0]\n let length = 0\n let pendingCut = false\n let runStart = 0\n while (runStart < text.length) {\n const runEnd = findScriptRunEnd(text, runStart)\n if (runStart > 0) {\n pendingCut = true\n }\n for (const character of text.slice(runStart, runEnd)) {\n if (RULE_WHITESPACE_CHARACTER.test(character)) {\n pendingCut = true\n continue\n }\n if (pendingCut) {\n if (length > 0) {\n cuts.push(length)\n }\n pendingCut = false\n }\n length += character.length\n }\n runStart = runEnd\n }\n if (cuts.length === 1) {\n return undefined\n }\n cuts.push(length)\n return cuts\n}\n\n/**\n * Preserves internal whitespace so source labels and credited names retain their spelling.\n */\nexport const trimRuleWhitespace = (text: string): string => text.replace(RULE_EDGE_WHITESPACE, '')\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport {\n AgentType,\n LineType,\n Timing,\n deriveLanguageUsages,\n getContentText,\n isWordLevelLine,\n makeAgent,\n makeLineContentText,\n makeLineContentWords,\n makeText,\n} from '@gondola-lyrics/model/runtime'\n\nexport interface ExtractAgentsOptions {\n /**\n * Maps complete, trimmed prefixes to existing agent ids, preserving array order for joint performers.\n * Matching is case-sensitive and preserves internal whitespace; an empty object disables automatic inference.\n * Omitting this option enables legacy name inference; explicit bindings override its clock exclusion.\n */\n bindings?: Readonly<Record<string, string | readonly string[]>>\n /**\n * Removes matched prefixes and drops lines left with no sung text, including their attached content.\n *\n * @default true\n */\n removePrefixes?: boolean\n}\n\nconst hashName = (name: string): string => {\n let hash = 0\n for (let index = 0; index < name.length; index += 1) {\n hash = ((hash << 5) - hash + name.charCodeAt(index)) | 0\n }\n return (hash >>> 0).toString(16).padStart(8, '0').toUpperCase()\n}\n\nconst haveSameAgents = (first: readonly string[], second: readonly string[]): boolean => {\n return first.length === second.length && first.every((id, index) => id === second[index])\n}\n\nconst prepareBindings = (\n bindings: ExtractAgentsOptions['bindings'],\n agents: readonly runtime.Agent[],\n): Map<string, readonly string[]> | undefined => {\n if (!bindings) {\n return undefined\n }\n const knownIds = new Set(agents.map((agent) => agent.id))\n return new Map(\n Object.entries(bindings).map(([prefix, target]) => {\n const ids = [...new Set(typeof target === 'string' ? [target] : target)]\n const missingId = ids.find((id) => !knownIds.has(id))\n if (missingId !== undefined) {\n throw new TypeError(`binding for ${JSON.stringify(prefix)} references unknown agent id ${JSON.stringify(missingId)}`)\n }\n return [prefix.trim(), ids] as const\n }),\n )\n}\n\n/**\n * Retains the legacy clock exclusion, including its accepted hour and minute bounds.\n */\nconst isClockTimeColon = (before: string, after: string): boolean => {\n const hour = /(\\d+)$/.exec(before)?.[1]\n const minute = /^(\\d+)/.exec(after)?.[1]\n if (!hour || !minute) {\n return false\n }\n return hour.length <= 2 && Number(hour) <= 24 && minute.length <= 2 && Number(minute) <= 60\n}\n\n/**\n * Cuts only source text, so retained word ranges and annotations do not need fabricated character timings.\n */\nconst removePrefix = (content: runtime.LineContent | undefined, length: number): runtime.LineContent | undefined => {\n if (!content) {\n return undefined\n }\n if (content.text !== undefined) {\n const text = content.text.slice(length).trim()\n return text ? makeLineContentText(text) : undefined\n }\n\n const words: runtime.Word[] = []\n let remaining = length\n for (const word of content.words) {\n if (remaining >= word.text.length) {\n remaining -= word.text.length\n continue\n }\n if (remaining > 0) {\n const text = word.text.slice(remaining).trim()\n remaining = 0\n if (text) {\n words.push({ ...word, text })\n }\n } else {\n words.push(word)\n }\n }\n const hasText = words.some((word) => !!word.text.trim())\n return hasText ? makeLineContentWords(words) : undefined\n}\n\n/**\n * Extracts colon-prefixed performers and carries them across nonempty normal lines, replacing their existing agent lists.\n * Missing bindings enables legacy name inference; supplied bindings accept exact prefixes and reset propagation on unmatched prefixes.\n * Removing prefixes preserves retained line and word times, but drops prefix-only lines with their attached content.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n *\n * @throws {TypeError} If bindings reference an agent id absent from lyric.agents.\n */\nexport const extractAgents = (lyric: runtime.Lyric, options: ExtractAgentsOptions = {}): runtime.Lyric => {\n const bindings = prepareBindings(options.bindings, lyric.agents)\n if (bindings?.size === 0 || !lyric.lines.length) {\n return lyric\n }\n\n const removePrefixes = options.removePrefixes ?? true\n const usedIds = new Set(lyric.agents.map((agent) => agent.id))\n const names = new Map<string, string | null>()\n for (const agent of lyric.agents) {\n if (!agent.id) {\n continue\n }\n for (const item of agent.names) {\n const name = item.text.trim()\n if (!name) {\n continue\n }\n if (!names.has(name)) {\n names.set(name, agent.id)\n } else if (names.get(name) !== agent.id) {\n names.set(name, null)\n }\n }\n }\n\n let agents = lyric.agents\n const resolveName = (name: string): readonly string[] => {\n const existing = names.get(name)\n if (existing) {\n return [existing]\n }\n const base = hashName(name)\n let id = base\n for (let suffix = 1; usedIds.has(id); suffix += 1) {\n id = `${base}-${suffix}`\n }\n const agent = makeAgent({ id, type: AgentType.PERSON, names: [makeText({ text: name })] })\n if (agents === lyric.agents) {\n agents = [...agents]\n }\n agents.push(agent)\n usedIds.add(id)\n names.set(name, id)\n return [id]\n }\n\n const originalLines = lyric.lines\n let lines: runtime.Line[] | undefined\n let current: readonly string[] | undefined\n let contentChanged = false\n for (const [index, line] of originalLines.entries()) {\n const text = line.type === LineType.NORMAL ? getContentText(line.content) : ''\n if (line.type !== LineType.NORMAL || !text.trim()) {\n current = undefined\n lines?.push(line)\n continue\n }\n\n const colon = text.search(/[::]/u)\n let matched = false\n if (colon !== -1) {\n const name = text.slice(0, colon).trim()\n if (bindings) {\n const target = bindings.get(name)\n if (!target) {\n current = undefined\n lines?.push(line)\n continue\n }\n current = target\n matched = true\n } else if (!isClockTimeColon(text.slice(0, colon), text.slice(colon + 1))) {\n if (!name) {\n current = undefined\n lines?.push(line)\n continue\n }\n current = resolveName(name)\n matched = true\n }\n }\n\n let content = line.content\n if (matched && removePrefixes) {\n content = removePrefix(content, colon + 1)\n contentChanged = true\n if (!content) {\n lines ??= originalLines.slice(0, index)\n continue\n }\n }\n\n const nextAgents = current && !haveSameAgents(line.agents, current) ? [...current] : line.agents\n if (content !== line.content || nextAgents !== line.agents) {\n lines ??= originalLines.slice(0, index)\n lines.push({ ...line, content, agents: nextAgents })\n } else {\n lines?.push(line)\n }\n }\n\n if (!lines && agents === lyric.agents) {\n return lyric\n }\n\n const remainingLines = lines ?? originalLines\n let timing = lyric.timing\n if (contentChanged && timing === Timing.WORD) {\n const hasWordLevelContent = remainingLines.some((line) => {\n return isWordLevelLine(line) || line.backgrounds.some(isWordLevelLine)\n })\n if (!hasWordLevelContent) {\n timing = Timing.LINE\n }\n }\n return { ...lyric, agents, lines: remainingLines, timing, languages: contentChanged ? deriveLanguageUsages(remainingLines) : lyric.languages }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { WordType, makeTimeRange } from '@gondola-lyrics/model/runtime'\n\nconst OPEN_BRACKETS = new Set(['(', '('])\nconst CLOSE_BRACKETS = new Set([')', ')'])\n\nexport const isOpenBracket = (char: string) => {\n return OPEN_BRACKETS.has(char)\n}\n\nexport const isCloseBracket = (char: string) => {\n return CLOSE_BRACKETS.has(char)\n}\n\nexport const isNormalWord = (word: runtime.Word) => {\n return word.type === WordType.NORMAL\n}\n\n/**\n * Content carries plain text or words, never both, so exactly one of the two is read.\n */\nexport const readLineText = (line: runtime.Line) => {\n return line.content?.text ?? (line.content?.words ?? []).map((word) => word.text).join('')\n}\n\nexport const readTimeRange = (line: runtime.Line) => {\n return line.time ? makeTimeRange({ start: line.time.start, end: line.time.end }) : undefined\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { WordType, makeLineContentText, makeLineContentWords } from '@gondola-lyrics/model/runtime'\n\nimport { mapShared } from '@root/utils'\n\nimport { isCloseBracket, isNormalWord, isOpenBracket } from './utils'\n\n/**\n * Removes one leading bracket from the first sung word and one trailing bracket from the last, so an inner pair stays as written.\n *\n * Space words at the same edge go with the bracket, and a word left with no text goes too, since a sung word must carry something.\n */\nconst stripWordEdges = (words: runtime.Word[]): runtime.Word[] => {\n const firstIndex = words.findIndex(isNormalWord)\n const first = words[firstIndex]\n const last = words[words.findLastIndex(isNormalWord)]\n const stripStart = first ? isOpenBracket(first.text.charAt(0)) : false\n const stripEnd = last ? isCloseBracket(last.text.charAt(last.text.length - 1)) : false\n if (!stripStart && !stripEnd) {\n return words\n }\n\n const result = [...words]\n if (stripStart && first) {\n const text = first.text.slice(1)\n if (text) {\n result[firstIndex] = { ...first, text }\n } else {\n result.splice(firstIndex, 1)\n }\n while (result[0]?.type === WordType.SPACE) {\n result.shift()\n }\n }\n\n // The end is resolved again, since stripping the start may have removed the word it referred to.\n const endIndex = result.findLastIndex(isNormalWord)\n const end = result[endIndex]\n if (stripEnd && end) {\n const text = end.text.slice(0, -1)\n if (text) {\n result[endIndex] = { ...end, text }\n } else {\n result.splice(endIndex, 1)\n }\n while (result.at(-1)?.type === WordType.SPACE) {\n result.pop()\n }\n }\n\n return result\n}\n\n/**\n * Plain text is read literally, so a source that padded it with spaces keeps them; only word content\n * treats space words as the structural separators they are.\n */\nconst stripContentBrackets = (content: runtime.LineContent | undefined): runtime.LineContent | undefined => {\n if (!content) {\n return content\n }\n\n if (!content.words.length) {\n const text = content.text\n if (!text) {\n return content\n }\n const start = isOpenBracket(text.charAt(0)) ? 1 : 0\n const end = isCloseBracket(text.charAt(text.length - 1)) ? -1 : 0\n if (!start && !end) {\n return content\n }\n const stripped = text.slice(start, end === 0 ? undefined : end)\n return stripped ? makeLineContentText(stripped) : undefined\n }\n\n const words = stripWordEdges(content.words)\n if (words === content.words) {\n return content\n }\n return words.length ? makeLineContentWords(words) : undefined\n}\n\nconst stripLineBackgrounds = (line: runtime.Line): runtime.Line => {\n if (!line.backgrounds.length) {\n return line\n }\n\n let changed = false\n const kept: runtime.LineBackground[] = []\n for (const background of line.backgrounds) {\n const content = stripContentBrackets(background.content)\n if (content === background.content) {\n kept.push(background)\n continue\n }\n changed = true\n if (content) {\n kept.push({ ...background, content })\n }\n }\n return changed ? { ...line, backgrounds: kept } : line\n}\n\n/**\n * Removes the brackets some sources wrap around background vocals.\n *\n * One layer is removed from each edge of every background line, leaving a nested pair as `(x)`.\n * A line left with no content is dropped, since a background line must carry something.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const stripBackgroundBrackets = (lyric: runtime.Lyric): runtime.Lyric => {\n const lines = mapShared(lyric.lines, stripLineBackgrounds)\n return lines === lyric.lines ? lyric : { ...lyric, lines }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport {\n LineType,\n Timing,\n deriveLanguageUsages,\n makeLineAnnotation,\n makeLineAnnotationRoman,\n makeLineAnnotationTranslation,\n makeLineBackground,\n makeLineContentText,\n makeLineContentWords,\n makeTimeRange,\n} from '@gondola-lyrics/model/runtime'\n\nimport { isCloseBracket, isNormalWord, isOpenBracket, readLineText, readTimeRange } from './utils'\n\nexport interface ExtractBackgroundLinesOptions {\n /**\n * Turn a line holding nothing but brackets into a background line of the line before it.\n *\n * @default true\n */\n fullLine?: boolean\n /**\n * Pull the bracketed runs inside a line out into background lines of that same line.\n *\n * @default true\n */\n inLine?: boolean\n /**\n * Join a bracket opened on one line and closed on a later one, attaching it to the line before them.\n *\n * @default true\n */\n crossLine?: boolean\n}\n\ninterface TextRun {\n text: string\n start: number\n end: number\n}\n\n/**\n * An unclosed bracket never became a run, so its text returns to the line it came from.\n * An empty pair yields no run, since a background line must carry text.\n */\nconst splitText = (text: string) => {\n const chars = [...text]\n let main = ''\n let current = ''\n let opened = ''\n let openedAt = 0\n let depth = 0\n const runs: TextRun[] = []\n\n for (const [index, char] of chars.entries()) {\n if (isOpenBracket(char)) {\n if (depth) {\n current += char\n } else {\n opened = char\n openedAt = index\n }\n depth += 1\n } else if (isCloseBracket(char)) {\n if (depth === 0) {\n main += char\n continue\n }\n depth -= 1\n if (depth) {\n current += char\n } else {\n runs.push({ text: current, start: openedAt + 1, end: index })\n current = ''\n }\n } else if (depth) {\n current += char\n } else {\n main += char\n }\n }\n\n if (depth) {\n main += opened + current\n }\n // A removed run leaves the separators that framed it behind, so they collapse rather than double.\n return {\n main: main.trim().replaceAll(/\\s{2,}/gu, ' '),\n runs: runs.filter((run) => {\n return !!run.text.trim()\n }),\n length: chars.length,\n }\n}\n/**\n * A word straddling a bracket is cut into pieces sharing its time, since the source gave no finer boundary.\n * An unclosed bracket never became a run, so its text returns to the line it came from.\n * An empty pair yields no run, since a background line must carry text.\n */\nconst splitWords = (words: runtime.Word[]) => {\n const main: runtime.Word[] = []\n const runs: runtime.Word[][] = []\n let current: runtime.Word[] = []\n let opened: runtime.Word | undefined\n let depth = 0\n\n for (const word of words) {\n if (!isNormalWord(word)) {\n const target = depth ? current : main\n target.push(word)\n continue\n }\n\n let buffer = ''\n for (const char of word.text) {\n if (isOpenBracket(char)) {\n if (depth) {\n buffer += char\n } else {\n if (buffer) {\n main.push({ ...word, text: buffer })\n buffer = ''\n }\n opened = { ...word, text: char }\n }\n depth += 1\n } else if (isCloseBracket(char)) {\n if (depth === 0) {\n buffer += char\n continue\n }\n depth -= 1\n if (depth) {\n buffer += char\n } else {\n if (buffer) {\n current.push({ ...word, text: buffer })\n buffer = ''\n }\n if (current.length) {\n runs.push(current)\n current = []\n }\n }\n } else {\n buffer += char\n }\n }\n\n if (buffer) {\n const target = depth ? current : main\n target.push({ ...word, text: buffer })\n }\n }\n\n if (depth) {\n if (opened) {\n main.push(opened)\n }\n main.push(...current)\n }\n return {\n main,\n runs: runs.filter((run) => {\n return run.some((word) => {\n return !!word.text.trim()\n })\n }),\n }\n}\n\nconst mapSpan = (range: runtime.TimeRange, length: number, run: TextRun) => {\n if (range.end === undefined || !length) {\n return makeTimeRange({ start: range.start, end: range.end })\n }\n const span = range.end - range.start\n const start = range.start + Math.round((span * run.start) / length)\n const end = range.start + Math.round((span * run.end) / length)\n return makeTimeRange({ start, end: Math.max(start, end) })\n}\n\n/**\n * Falls back to the line's own range when no word carries a time, so a run still gets a range.\n */\nconst readWordRange = (words: runtime.Word[], fallback: runtime.TimeRange) => {\n const timed = words.filter((word) => {\n return isNormalWord(word) && !!word.time\n })\n const first = timed[0]?.time\n const last = timed.at(-1)?.time\n if (!first || !last) {\n return fallback\n }\n return makeTimeRange({ start: first.start, end: last.end })\n}\n\n/**\n * A background may start before its line but not finish after it, so the line grows to the furthest stated end.\n */\nconst attach = (line: runtime.Line, backgrounds: runtime.LineBackground[]) => {\n const merged = [...line.backgrounds, ...backgrounds]\n merged.sort((left, right) => {\n const leftStart = left.time?.start ?? 0\n const rightStart = right.time?.start ?? 0\n return leftStart === rightStart ? (left.time?.end ?? 0) - (right.time?.end ?? 0) : leftStart - rightStart\n })\n\n let end = line.time?.end\n for (const background of merged) {\n const backgroundEnd = background.time?.end\n if (backgroundEnd !== undefined && (end === undefined || backgroundEnd > end)) {\n end = backgroundEnd\n }\n }\n if (!line.time || end === line.time.end) {\n return { ...line, backgrounds: merged }\n }\n return { ...line, time: makeTimeRange({ start: line.time.start, end }), backgrounds: merged }\n}\n\n/**\n * A closed line cannot hold an open background, so it stops where the line does.\n * On the last line the timeline itself ends unknown, so an open background leaves the line open too.\n */\nconst boundOpenBackgrounds = (lines: runtime.Line[]) => {\n const last = lines.length - 1\n return lines.map((line, index) => {\n const open = line.backgrounds.filter((background) => {\n return !!background.time && background.time.end === undefined\n })\n if (!open.length) {\n return line\n }\n if (index === last) {\n return line.time ? { ...line, time: makeTimeRange({ start: line.time.start }) } : line\n }\n if (!line.time || line.time.end === undefined) {\n return line\n }\n const end = line.time.end\n return {\n ...line,\n backgrounds: line.backgrounds.map((background) => {\n const time = background.time\n if (!time || time.end !== undefined) {\n return background\n }\n return { ...background, time: makeTimeRange({ start: time.start, end: Math.max(time.start, end) }) }\n }),\n }\n })\n}\n\n/**\n * Words are kept only where the caller says the lyric is word-timed, since the other timings forbid them.\n */\nconst makeBackground = (line: runtime.Line, words: runtime.Word[], text: string, time: runtime.TimeRange | undefined, wordLevel: boolean) => {\n return makeLineBackground({\n time,\n agents: [...line.agents],\n languages: [...line.languages],\n content: wordLevel && words.length ? makeLineContentWords(words) : makeLineContentText(text),\n })\n}\n\nconst readBackgrounds = (line: runtime.Line, wordLevel: boolean) => {\n const content = line.content\n if (!content) {\n return []\n }\n\n const range = line.time\n if (wordLevel && content.words.length) {\n return splitWords(content.words).runs.map((words) => {\n const time = range ? readWordRange(words, range) : undefined\n return makeBackground(line, words, words.map((word) => word.text).join(''), time, wordLevel)\n })\n }\n\n const text = content.text ?? content.words.map((word) => word.text).join('')\n if (!text) {\n return []\n }\n const { runs, length } = splitText(text)\n return runs.map((run) => {\n return makeBackground(line, [], run.text, range ? mapSpan(range, length, run) : undefined, false)\n })\n}\n\n/**\n * An entry without brackets stays on the line, since only the bracketed part describes a background.\n * The line's annotation is rebuilt rather than edited, so the input lyric stays untouched.\n */\nconst splitAnnotation = (annotation: runtime.LineAnnotation | undefined, backgrounds: runtime.LineBackground[]) => {\n if (!annotation || !backgrounds.length) {\n return annotation\n }\n\n const move = <Item extends runtime.LineAnnotationRoman | runtime.LineAnnotationTranslation>(\n items: Item[],\n rebuild: (item: Item, text: string) => Item,\n push: (background: runtime.LineBackground, text: string, language: string | undefined) => void,\n ) => {\n return items.map((item) => {\n const { main, runs } = splitText(item.text)\n if (!runs.length) {\n return item\n }\n for (const [index, run] of runs.entries()) {\n const background = backgrounds[index]\n if (background) {\n push(background, run.text, item.language)\n }\n }\n return rebuild(item, main)\n })\n }\n\n const romans = move(\n annotation.romans,\n (item, text) => makeLineAnnotationRoman({ ...item, text }),\n (background, text, language) => {\n background.annotation ??= makeLineAnnotation()\n background.annotation.romans.push(makeLineAnnotationRoman({ text, language }))\n },\n )\n const translations = move(\n annotation.translations,\n (item, text) => makeLineAnnotationTranslation({ ...item, text }),\n (background, text, language) => {\n background.annotation ??= makeLineAnnotation()\n background.annotation.translations.push(makeLineAnnotationTranslation({ text, language }))\n },\n )\n return makeLineAnnotation({ ...annotation, romans, translations })\n}\n\n/**\n * Words survive only where the lyric is word-timed, since the other timings forbid them.\n */\nconst toBackground = (line: runtime.Line, time: runtime.TimeRange | undefined, wordLevel: boolean) => {\n const content = line.content\n if (!content) {\n return undefined\n }\n const text = content.text ?? content.words.map((word) => word.text).join('')\n if (!text) {\n return undefined\n }\n return makeLineBackground({\n time,\n agents: [...line.agents],\n languages: [...line.languages],\n content: wordLevel && content.words.length ? content : makeLineContentText(text),\n annotation: line.annotation,\n })\n}\n\nconst isBracketOnlyLine = (line: runtime.Line | undefined) => {\n if (!line || line.type !== LineType.NORMAL) {\n return false\n }\n const { main, runs } = splitText(readLineText(line))\n return !!runs.length && !main\n}\n\n/**\n * A line that held nothing but brackets has no text of its own to sing, so it cannot be the one a background accompanies.\n */\nconst findParentIndex = (lines: runtime.Line[]) => {\n for (let index = lines.length - 1; index >= 0; index--) {\n const line = lines[index]\n if (line?.type === LineType.NORMAL && !isBracketOnlyLine(line)) {\n return index\n }\n }\n return -1\n}\n\n/**\n * Both source lines go, since neither holds anything left to sing, and the joined range spans both.\n */\nconst joinCrossLine = (lines: runtime.Line[], wordLevel: boolean) => {\n const result: runtime.Line[] = []\n let changed = false\n let skip = false\n\n for (const [index, line] of lines.entries()) {\n if (skip) {\n skip = false\n continue\n }\n\n const next = lines[index + 1]\n const parentIndex = findParentIndex(result)\n if (line.type === LineType.NORMAL && next?.type === LineType.NORMAL && parentIndex !== -1) {\n const opened = splitText(readLineText(line))\n const closed = splitText(readLineText(next))\n const opens = !opened.runs.length && isOpenBracket(opened.main.charAt(0))\n const closes = !closed.runs.length && isCloseBracket(closed.main.charAt(closed.main.length - 1))\n if (opens && closes) {\n const start = line.time?.start\n const end = next.time?.end ?? next.time?.start\n const background = toBackground(\n { ...line, content: makeLineContentText(opened.main.slice(1) + closed.main.slice(0, -1)) },\n start === undefined ? undefined : makeTimeRange({ start, end }),\n wordLevel,\n )\n if (background) {\n result[parentIndex] = attach(result[parentIndex]!, [background])\n changed = true\n skip = true\n continue\n }\n }\n }\n result.push(line)\n }\n\n return { lines: result, changed }\n}\n\nconst stripRuns = (content: runtime.LineContent): runtime.LineContent | undefined => {\n if (content.words.length) {\n const { main } = splitWords(content.words)\n return main.length ? makeLineContentWords(main) : undefined\n }\n const { main } = splitText(content.text ?? '')\n return main ? makeLineContentText(main) : undefined\n}\n\n/**\n * Pulls bracketed background vocals out of the sung lines and attaches them to the line they accompany.\n *\n * A line holding nothing but brackets becomes a background of the line before it and is removed.\n * A bracket opened on one line and closed on a later one joins the line before the opening, and both source lines go.\n * Text outside brackets stays on its line, and a line left with none keeps its place.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const extractBackgroundLines = (lyric: runtime.Lyric, options: ExtractBackgroundLinesOptions = {}): runtime.Lyric => {\n const fullLine = options.fullLine ?? true\n const inLine = options.inLine ?? true\n const crossLine = options.crossLine ?? true\n if (!lyric.lines.length) {\n return lyric\n }\n\n const wordLevel = lyric.timing === Timing.WORD\n const joined = crossLine ? joinCrossLine(lyric.lines, wordLevel) : { lines: lyric.lines, changed: false }\n const result: runtime.Line[] = []\n let changed = joined.changed\n\n for (const line of joined.lines) {\n if (line.type !== LineType.NORMAL || !line.content) {\n result.push(line)\n continue\n }\n\n const parentIndex = findParentIndex(result)\n\n if (fullLine && parentIndex !== -1) {\n const { main, runs } = splitText(readLineText(line))\n if (runs.length && !main) {\n // Every run becomes a background of its own, so a line of two pairs keeps both.\n const backgrounds = runs.map((run) => {\n return makeBackground(line, [], run.text, readTimeRange(line), false)\n })\n result[parentIndex] = attach(result[parentIndex]!, backgrounds)\n changed = true\n continue\n }\n }\n\n const backgrounds = inLine ? readBackgrounds(line, wordLevel) : []\n if (!backgrounds.length) {\n result.push(line)\n continue\n }\n\n const nextContent = stripRuns(line.content)\n\n // A line left with nothing to sing only leaves when fullLine owns that case, so inLine keeps it here.\n if (!nextContent) {\n result.push(line)\n continue\n }\n\n const extracted: runtime.Line = {\n ...line,\n content: nextContent,\n annotation: splitAnnotation(line.annotation, backgrounds),\n }\n result.push(attach(extracted, backgrounds))\n changed = true\n }\n\n if (!changed) {\n return lyric\n }\n const lines = boundOpenBackgrounds(result)\n return { ...lyric, lines, languages: deriveLanguageUsages(lines) }\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"en\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"lyricist\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\"],\n \"words\": [\"Lyrics\", \"Lyricsby\", \"Lyricist\", \"Words\", \"Wordsby\", \"Lyric\", \"Lyr\", \"Lyricists\", \"Lyricwriting\"]\n },\n {\n \"id\": \"composer\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\"],\n \"words\": [\n \"Music\",\n \"Musicby\",\n \"Composer\",\n \"Composedby\",\n \"Compose\",\n \"Composition\",\n \"Melody\",\n \"Melodyby\",\n \"Comp\",\n \"Composers\",\n \"Score\",\n \"Scoredby\",\n \"Composed\"\n ]\n },\n {\n \"id\": \"arranger\",\n \"kind\": \"role\",\n \"roles\": [\"ARRANGER\"],\n \"words\": [\n \"Arrangement\",\n \"Arranger\",\n \"Arrangedby\",\n \"Arranged\",\n \"Arrange\",\n \"Arr\",\n \"Orchestration\",\n \"StringArrangement\",\n \"VocalArrangement\",\n \"ChoirArrangement\",\n \"Orchestrator\",\n \"Orchestratedby\",\n \"Orch\",\n \"AdditionalArrangement\",\n \"BGVsArranger\",\n \"BVArranger\",\n \"BackgroundVocalArrangement\",\n \"BackgroundVocalsArrangement\",\n \"BackingVocalsArranger\",\n \"BackingVocalArrangement\",\n \"BackingVocalsArrangement\",\n \"BrassArrangement\",\n \"HornArrangement\",\n \"OrchestralArrangement\",\n \"RhythmArrangement\",\n \"StringArrangedby\",\n \"StringArranger\",\n \"VocalArrangedby\",\n \"VocalArranger\"\n ]\n },\n {\n \"id\": \"producer\",\n \"kind\": \"role\",\n \"roles\": [\"PRODUCER\"],\n \"words\": [\n \"Producer\",\n \"Producedby\",\n \"Produced\",\n \"Production\",\n \"Prod\",\n \"Prodby\",\n \"Producers\",\n \"Produce\",\n \"CoProducer\",\n \"Co-Producer\",\n \"ExecutiveProducer\",\n \"AdditionalProducer\",\n \"AdditionalProduction\",\n \"VocalProducer\",\n \"MusicProducer\",\n \"AssociateProducer\",\n \"Co-Produced\",\n \"Co-Producedby\",\n \"Co-Production\",\n \"Co-Productionby\",\n \"CoProd\",\n \"Coproduction\",\n \"CreativeProducer\",\n \"ExProd\",\n \"ExecProducer\",\n \"ExecutiveProducers\",\n \"Productionby\",\n \"VocalProduction\",\n \"MusicSupervisor\",\n \"Supervisor\",\n \"Presentedby\",\n \"Presenter\"\n ]\n },\n {\n \"id\": \"performer\",\n \"kind\": \"role\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"Vocals\",\n \"Vocal\",\n \"Vocalist\",\n \"Singer\",\n \"Voice\",\n \"Performer\",\n \"Featuring\",\n \"Feat\",\n \"Ft\",\n \"Artist\",\n \"Vocalsby\",\n \"Sungby\",\n \"Performedby\",\n \"Performed\",\n \"LeadVocal\",\n \"LeadVocals\",\n \"BackgroundVocals\",\n \"BackingVocals\",\n \"Harmony\",\n \"Choir\",\n \"Band\",\n \"Rap\",\n \"Rapper\",\n \"Soloist\",\n \"Musician\",\n \"Player\",\n \"GuestVocals\",\n \"FeaturedArtist\",\n \"Narrator\",\n \"Guitarist\",\n \"Bassist\",\n \"Pianist\",\n \"Drummer\",\n \"Keyboardist\",\n \"Violinist\",\n \"Cellist\",\n \"Accompaniment\",\n \"Accompanist\",\n \"Accordionist\",\n \"Ad-lib\",\n \"Ad-libs\",\n \"AdditionalVocals\",\n \"Adlib\",\n \"Adlibs\",\n \"Allvocals\",\n \"Allvocalsby\",\n \"BGV\",\n \"BGVs\",\n \"BGs\",\n \"BV\",\n \"BackgroundVocal\",\n \"BackingVocal\",\n \"Banjoist\",\n \"Beatbox\",\n \"Beatboxer\",\n \"Bvox\",\n \"CV\",\n \"ChildrenVoice\",\n \"Clarinetist\",\n \"Contrabassist\",\n \"CoverSong\",\n \"Dialogue\",\n \"Ensemble\",\n \"FemaleVoice\",\n \"Flutist\",\n \"GuestArtist\",\n \"GuestSinger\",\n \"Harmonies\",\n \"Harpist\",\n \"Hornist\",\n \"HumanBeatbox\",\n \"HumanVoice\",\n \"LeadSinger\",\n \"LeadVocalist\",\n \"LiveBand\",\n \"MainVocal\",\n \"MainVocals\",\n \"MaleVoice\",\n \"Mandolinist\",\n \"Narration\",\n \"Orchestra\",\n \"Organist\",\n \"OriginalSinger\",\n \"Percussionist\",\n \"Recitation\",\n \"Saxophonist\",\n \"SessionMusician\",\n \"SessionPlayer\",\n \"Singers\",\n \"Singing\",\n \"Sitarist\",\n \"Trombonist\",\n \"Trumpeter\",\n \"Tubist\",\n \"Ukuleleist\",\n \"Violist\",\n \"VocalPerformance\",\n \"Vocalists\",\n \"VoiceActor\",\n \"VoiceActress\",\n \"Vox\",\n \"Chorus\",\n \"Solo\"\n ]\n },\n {\n \"id\": \"instruments\",\n \"kind\": \"instrument\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"Guitar\",\n \"Piano\",\n \"Bass\",\n \"Drums\",\n \"Keyboard\",\n \"Strings\",\n \"Synth\",\n \"Violin\",\n \"Cello\",\n \"Saxophone\",\n \"Trumpet\",\n \"Flute\",\n \"Clarinet\",\n \"Trombone\",\n \"Horn\",\n \"Organ\",\n \"Percussion\",\n \"Ukulele\",\n \"Banjo\",\n \"Mandolin\",\n \"Harp\",\n \"Viola\",\n \"AcousticGuitar\",\n \"ElectricGuitar\",\n \"BassGuitar\",\n \"ElectricBass\",\n \"Keyboards\",\n \"Keys\",\n \"Synthesizer\",\n \"Perc\",\n \"12-StringGuitar\",\n \"A.Gtr\",\n \"A.Sax\",\n \"Accordion\",\n \"AcousticBass\",\n \"AcousticElectricGuitar\",\n \"Allinstrumental\",\n \"Allinstrumentalby\",\n \"Allinstruments\",\n \"AltoSax\",\n \"AltoSaxophone\",\n \"AppalachianDulcimer\",\n \"Autoharp\",\n \"B.Cl\",\n \"B.Sax\",\n \"B3\",\n \"BANDONEÓN\",\n \"BODHRÁN\",\n \"Bagpipe\",\n \"Bagpipes\",\n \"Balalaika\",\n \"Bandoneon\",\n \"Bandoneón\",\n \"Banhu\",\n \"Bansuri\",\n \"BaritoneSax\",\n \"BaritoneSaxophone\",\n \"BassClarinet\",\n \"BassSynth\",\n \"Bassoon\",\n \"Bawu\",\n \"Bodhran\",\n \"Bodhrán\",\n \"Bongo\",\n \"Bouzouki\",\n \"Brass\",\n \"Bsn\",\n \"Bss\",\n \"CAJÓN\",\n \"Cajon\",\n \"Cajón\",\n \"Castanets\",\n \"Cb\",\n \"Celesta\",\n \"Charango\",\n \"Chimes\",\n \"Cl\",\n \"ClassicalGuitar\",\n \"Clav\",\n \"Claves\",\n \"Clavinet\",\n \"Cnt\",\n \"Concertina\",\n \"Conga\",\n \"Contrabass\",\n \"Cornet\",\n \"Cowbell\",\n \"Cymbal\",\n \"Cymbals\",\n \"DJ\",\n \"Daegeum\",\n \"Darbuka\",\n \"Daruan\",\n \"Didgeridoo\",\n \"Dizi\",\n \"Djembe\",\n \"Dobro\",\n \"DoubleBass\",\n \"Doumbek\",\n \"Dr\",\n \"Drum\",\n \"DrumMachine\",\n \"DrumPad\",\n \"DrumSet\",\n \"Duduk\",\n \"Dulcimer\",\n \"E.Bss\",\n \"E.Gtr\",\n \"E.Hn\",\n \"EP\",\n \"ElectricPiano\",\n \"ElectronicKeyboard\",\n \"EnglishHorn\",\n \"Erhu\",\n \"Euph\",\n \"Euphonium\",\n \"FingerDrumming\",\n \"Fingerpicking\",\n \"FingerstyleGuitar\",\n \"Fl\",\n \"Flageolet\",\n \"Flt\",\n \"Flugelhorn\",\n \"FrameDrum\",\n \"FrenchHorn\",\n \"Gaohu\",\n \"Gayageum\",\n \"Glockenspiel\",\n \"Gong\",\n \"Gtr\",\n \"Guiro\",\n \"Guitars\",\n \"Guqin\",\n \"Guzheng\",\n \"GÜIRO\",\n \"Güiro\",\n \"Haegeum\",\n \"HammeredDulcimer\",\n \"Hammond\",\n \"HammondOrgan\",\n \"HandDrum\",\n \"Handpan\",\n \"HangDrum\",\n \"Harmonica\",\n \"Harmonium\",\n \"Harpsichord\",\n \"Hn\",\n \"HornSection\",\n \"Horns\",\n \"Hpschd\",\n \"Hrp\",\n \"Hulusi\",\n \"Hurdy-Gurdy\",\n \"HurdyGurdy\",\n \"Janggu\",\n \"Jinghu\",\n \"Kalimba\",\n \"Keytar\",\n \"Konghou\",\n \"Kora\",\n \"Koto\",\n \"Kybd\",\n \"LapSteel\",\n \"LapSteelGuitar\",\n \"LeadGuitar\",\n \"Liuqin\",\n \"Lute\",\n \"MPC\",\n \"Marimba\",\n \"Matouqin\",\n \"Mbira\",\n \"Mellotron\",\n \"Melodica\",\n \"MorinKhuur\",\n \"MusicalInstruments\",\n \"Nyckelharpa\",\n \"NylonGuitar\",\n \"Ob\",\n \"Oboe\",\n \"Ocarina\",\n \"Org\",\n \"Oud\",\n \"PanFlute\",\n \"Panpipes\",\n \"PedalSteel\",\n \"PedalSteelGuitar\",\n \"PennyWhistle\",\n \"Picc\",\n \"Piccolo\",\n \"Pipa\",\n \"Pno\",\n \"Rebab\",\n \"Recorder\",\n \"Resonator\",\n \"ResonatorGuitar\",\n \"Rhodes\",\n \"RhythmGuitar\",\n \"Ruan\",\n \"S.Sax\",\n \"Sampler\",\n \"Sanxian\",\n \"Sax\",\n \"Scratching\",\n \"Sequencer\",\n \"Shaker\",\n \"Shakuhachi\",\n \"Shamisen\",\n \"Sheng\",\n \"Sitar\",\n \"SlideGuitar\",\n \"Snare\",\n \"SnareDrum\",\n \"SopranoSax\",\n \"SteelDrum\",\n \"SteelGuitar\",\n \"SteelPan\",\n \"Str\",\n \"String\",\n \"StringQuartet\",\n \"StringSection\",\n \"SubBass\",\n \"Suona\",\n \"SynthBass\",\n \"Synthesizers\",\n \"Synths\",\n \"T.Sax\",\n \"Tabla\",\n \"Taiko\",\n \"TalkingDrum\",\n \"Tamb\",\n \"Tambourine\",\n \"Tba\",\n \"Tbn\",\n \"TenorSax\",\n \"TenorSaxophone\",\n \"Theremin\",\n \"Timbales\",\n \"Timp\",\n \"Timpani\",\n \"TinWhistle\",\n \"Tpt\",\n \"Triangle\",\n \"Tuba\",\n \"TubularBells\",\n \"Turntable\",\n \"Turntables\",\n \"Uke\",\n \"UprightBass\",\n \"Vc\",\n \"Vib\",\n \"Vibes\",\n \"Vibraphone\",\n \"Vla\",\n \"Vln\",\n \"Vocoder\",\n \"Whistle\",\n \"Winds\",\n \"Woodblock\",\n \"Woodwinds\",\n \"Wurlitzer\",\n \"Xiao\",\n \"Xiaoruan\",\n \"Xun\",\n \"Xylo\",\n \"Xylophone\",\n \"Yangqin\",\n \"Yueqin\",\n \"Zhonghu\",\n \"Zither\"\n ]\n },\n {\n \"id\": \"mixer\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\"],\n \"words\": [\n \"Mix\",\n \"Mixedby\",\n \"Mixing\",\n \"Mixer\",\n \"MixingEngineer\",\n \"MixEngineer\",\n \"Remix\",\n \"Remixedby\",\n \"Remixer\",\n \"Mixdown\",\n \"Mixdownby\",\n \"Mixed\",\n \"Remixby\"\n ]\n },\n {\n \"id\": \"mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MASTERING\"],\n \"words\": [\"Mastering\", \"Masteredby\", \"Master\", \"MasteringEngineer\", \"MasterEngineer\", \"Mst\", \"Mastered\", \"MasteringEngineerby\", \"Masteringby\"]\n },\n {\n \"id\": \"lyrics-and-composition\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\"],\n \"words\": [\n \"Lyrics&Music\",\n \"Music&Lyrics\",\n \"Words&Music\",\n \"Lyrics&Musicby\",\n \"Music&Lyricsby\",\n \"Words&Musicby\",\n \"Composed&Writtenby\",\n \"ComposedandWrittenby\",\n \"Composer&Lyricist\",\n \"Composer/Lyricist\",\n \"Lyricist&Composer\",\n \"Lyricist/Composer\",\n \"Lyrics&Melody\",\n \"Lyrics/Music\",\n \"LyricsandMusic\",\n \"LyricsandMusicby\",\n \"Melody&Lyrics\",\n \"Melody&Words\",\n \"Music&Words\",\n \"Music/Lyrics\",\n \"MusicandLyrics\",\n \"MusicandLyricsby\",\n \"Words&Melody\",\n \"Words/Music\",\n \"WordsandMusic\",\n \"WordsandMusicby\",\n \"Written&Composedby\",\n \"WrittenandComposedby\"\n ]\n },\n {\n \"id\": \"composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\", \"ARRANGER\"],\n \"words\": [\n \"Composer&Arranger\",\n \"Composer/Arranger\",\n \"Composed&Arrangedby\",\n \"Music&Arrangement\",\n \"Composed&Arranged\",\n \"ComposedandArrangedby\",\n \"ComposerandArranger\",\n \"Composition&Arrangement\",\n \"MusicandArrangement\"\n ]\n },\n {\n \"id\": \"mixing-and-mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\", \"MASTERING\"],\n \"words\": [\n \"Mix&Master\",\n \"Mix/Master\",\n \"Mixed&Masteredby\",\n \"Mixing&Mastering\",\n \"Mixing/Mastering\",\n \"Mix&Mastering\",\n \"MixandMaster\",\n \"MixandMastering\",\n \"MixedandMasteredby\",\n \"Mixing&Master\",\n \"MixingandMaster\",\n \"MixingandMastering\"\n ]\n },\n {\n \"id\": \"lyrics-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"ARRANGER\"],\n \"words\": [\"Lyrics&Arrangement\", \"LyricsArrangement\", \"LyricsandArrangement\", \"Words&Arrangement\", \"WordsArrangement\"]\n },\n {\n \"id\": \"lyrics-composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"ARRANGER\"],\n \"words\": [\n \"ComposedWritten&Arrangedby\",\n \"ComposedWrittenandArrangedby\",\n \"LyricsMusic&Arrangement\",\n \"LyricsMusicArrangement\",\n \"LyricsMusicandArrangement\",\n \"MusicLyrics&Arrangement\",\n \"WordsMusic&Arrangement\",\n \"WrittenComposed&Arrangedby\",\n \"WrittenComposedandArrangedby\"\n ]\n },\n {\n \"id\": \"singer-songwriter\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"PERFORMER\"],\n \"words\": [\"Singer-Songwriter\", \"SingerSongwriter\", \"SingerSongwriterby\"]\n },\n {\n \"id\": \"other\",\n \"kind\": \"role\",\n \"roles\": [\"OTHER\"],\n \"words\": [\n \"Engineer\",\n \"RecordingEngineer\",\n \"SoundEngineer\",\n \"MixAssistant\",\n \"MixingAssistant\",\n \"MasteringAssistant\",\n \"A&R\",\n \"ArtDirection\",\n \"ArtDirector\",\n \"Design\",\n \"Designer\",\n \"Photographer\",\n \"Photography\",\n \"Photo\",\n \"Director\",\n \"Video\",\n \"Manager\",\n \"Management\",\n \"Coordinator\",\n \"Consultant\",\n \"Translator\",\n \"Translation\",\n \"Transcription\",\n \"Editor\",\n \"Edit\",\n \"Programmer\",\n \"Programming\",\n \"Conductor\",\n \"Choreographer\",\n \"A.Eng\",\n \"Acknowledgments\",\n \"AdditionalEngineering\",\n \"Adjustment\",\n \"AlbumArt\",\n \"Animatedby\",\n \"Animation\",\n \"Animator\",\n \"Animatorby\",\n \"ArtConsultant\",\n \"ArtistManagement\",\n \"Artistassistant\",\n \"ArtistsandRepertoire\",\n \"Artwork\",\n \"AssetManagement\",\n \"Assistance\",\n \"Assistant\",\n \"AssistantEngineer\",\n \"AssistantMixEngineer\",\n \"AsstEng\",\n \"AudioEditing\",\n \"AudioEditor\",\n \"AudioEngineer\",\n \"Auto-Tune\",\n \"BTS\",\n \"Beatby\",\n \"Beatmaker\",\n \"Beats\",\n \"Behind-the-Scenes\",\n \"Behind-theScenes\",\n \"Behindthe-Scenes\",\n \"BehindtheScenes\",\n \"CG\",\n \"COO\",\n \"ChiefOperatingOfficer\",\n \"Choreography\",\n \"Clothing\",\n \"ComputerEngineer\",\n \"Cond\",\n \"Conductedby\",\n \"Contractor\",\n \"Copyist\",\n \"Copywriter\",\n \"Copywriting\",\n \"Costume\",\n \"CoverArt\",\n \"CoverDesign\",\n \"CreativeDirection\",\n \"CreativeDirector\",\n \"Designedby\",\n \"Digitaledit\",\n \"Digitalediting\",\n \"Directedby\",\n \"Distributedby\",\n \"DolbyAtmos\",\n \"Drawing\",\n \"DrumProgramming\",\n \"DrumTech\",\n \"Editedby\",\n \"Eng\",\n \"Engineeredby\",\n \"FOHEngineer\",\n \"Fashion\",\n \"Film\",\n \"Filmedby\",\n \"FineArts\",\n \"Flatdesign\",\n \"Foley\",\n \"FoleyArtist\",\n \"FrontofHouse\",\n \"FrontofHouseEngineer\",\n \"GraphicDesign\",\n \"Graphics\",\n \"Graphicvisual\",\n \"GuitarTech\",\n \"Hair\",\n \"Hair-Stylist\",\n \"HairStylist\",\n \"Illustratedby\",\n \"Illustration\",\n \"Illustrator\",\n \"Inscription\",\n \"InstrumentTech\",\n \"Librarian\",\n \"LightTech\",\n \"LightingDesigner\",\n \"LiveSoundEngineer\",\n \"Loops\",\n \"LyricVideo\",\n \"MD\",\n \"MIDI\",\n \"MIDIProgramming\",\n \"MV\",\n \"MVDirector\",\n \"MainCreators\",\n \"Make-up\",\n \"Makeup\",\n \"MakeupArtist\",\n \"Making\",\n \"Making-of\",\n \"Makingof\",\n \"Marketing\",\n \"Marketing&promotion\",\n \"MonitorEngineer\",\n \"MusicConcept\",\n \"MusicDirector\",\n \"MusicVideo\",\n \"MusicVideoDirector\",\n \"Musicproductionassistant\",\n \"OP\",\n \"PGM\",\n \"PRpromotion\",\n \"PV\",\n \"Photoby\",\n \"PitchCorrection\",\n \"PitchEditing\",\n \"Planner\",\n \"Planning\",\n \"Post-production\",\n \"PostMastering\",\n \"PostProd\",\n \"Pre-production\",\n \"Printvisual\",\n \"Productionassistant\",\n \"Prog\",\n \"Program\",\n \"Programmedby\",\n \"ProjectCoordinator\",\n \"ProjectPlanning\",\n \"Promo-Material\",\n \"PromoMaterial\",\n \"Promotion\",\n \"Proofreading\",\n \"Publicity\",\n \"Publishedby\",\n \"Publisher\",\n \"Rec\",\n \"Recordedby\",\n \"Recording\",\n \"RecordingAssistant\",\n \"Recordist\",\n \"SFX\",\n \"SFXDesigner\",\n \"SP\",\n \"Samples\",\n \"Sampling\",\n \"Scoring\",\n \"Short-Form\",\n \"Short-Video\",\n \"ShortForm\",\n \"ShortVideo\",\n \"Songby\",\n \"Songwriter\",\n \"Songwriting\",\n \"SoundDes\",\n \"SoundDesign\",\n \"SoundDesigner\",\n \"SoundEffects\",\n \"SoundEffectsDesigner\",\n \"SoundMixer\",\n \"SpecialThanks\",\n \"SpecialThanksto\",\n \"StageManager\",\n \"Styling\",\n \"Stylist\",\n \"Sub-publisher\",\n \"Subpublisher\",\n \"Subtitles\",\n \"Supervisedby\",\n \"Tech\",\n \"Thanks\",\n \"Thanksto\",\n \"Timing\",\n \"Topliner\",\n \"TrackingEng\",\n \"TrackingEngineer\",\n \"Training\",\n \"Trans\",\n \"Transcriber\",\n \"Translatedby\",\n \"Tunedby\",\n \"Tuning\",\n \"Videoby\",\n \"Visual\",\n \"VisualDesign\",\n \"VocalCoach\",\n \"VocalDirectedby\",\n \"VocalDirection\",\n \"VocalDirector\",\n \"VocalEdit\",\n \"VocalEditing\",\n \"VocalEditor\",\n \"VocalTuning\",\n \"Wardrobe\",\n \"Writer\",\n \"Written\",\n \"Writtenby\",\n \"Art\",\n \"Image\",\n \"Material\",\n \"Marketing-Material\",\n \"MarketingMaterial\",\n \"Copyrightmanagement\",\n \"Copyrightoperation\",\n \"StudioManager\",\n \"Beat\",\n \"Cut\",\n \"Guest\",\n \"Stage\"\n ]\n }\n ]\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"zh-hans\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"lyricist\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\"],\n \"words\": [\n \"作词\",\n \"填词\",\n \"词\",\n \"歌词\",\n \"作词人\",\n \"词作者\",\n \"作词者\",\n \"填词人\",\n \"词作\",\n \"中文词\",\n \"英文词\",\n \"粤语词\",\n \"韩文词\",\n \"日文词\",\n \"RAP词\"\n ]\n },\n {\n \"id\": \"composer\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\"],\n \"words\": [\"作曲\", \"曲\", \"谱曲\", \"作曲人\", \"曲作者\", \"作曲者\"]\n },\n {\n \"id\": \"arranger\",\n \"kind\": \"role\",\n \"roles\": [\"ARRANGER\"],\n \"words\": [\n \"编曲\",\n \"配器\",\n \"和声编写\",\n \"弦乐编写\",\n \"管乐编写\",\n \"节奏编写\",\n \"合唱编写\",\n \"和声编排\",\n \"弦乐编排\",\n \"管弦乐编排\",\n \"编曲人\",\n \"编曲者\",\n \"编曲师\",\n \"和声编配\",\n \"弦乐编配\",\n \"和音编写\"\n ]\n },\n {\n \"id\": \"producer\",\n \"kind\": \"role\",\n \"roles\": [\"PRODUCER\"],\n \"words\": [\n \"制作人\",\n \"制作\",\n \"监制\",\n \"出品人\",\n \"出品\",\n \"音乐制作人\",\n \"执行制作人\",\n \"制作总监\",\n \"总制作人\",\n \"人声制作\",\n \"人声制作人\",\n \"配唱制作人\",\n \"共同制作\",\n \"共同制作人\",\n \"协力制作人\",\n \"制作协力\",\n \"单曲制作\",\n \"创意制作人\",\n \"出品方\",\n \"联合出品\",\n \"联合出品方\",\n \"执制\",\n \"执行监制\",\n \"总监制\",\n \"音乐监制\"\n ]\n },\n {\n \"id\": \"performer\",\n \"kind\": \"role\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"演唱\",\n \"主唱\",\n \"歌手\",\n \"唱\",\n \"和声\",\n \"和音\",\n \"伴唱\",\n \"合唱\",\n \"原唱\",\n \"翻唱\",\n \"配唱\",\n \"女声\",\n \"男声\",\n \"童声\",\n \"人声\",\n \"合声\",\n \"合音\",\n \"领唱\",\n \"独奏\",\n \"演奏\",\n \"演奏者\",\n \"说唱\",\n \"配音\",\n \"旁白\",\n \"念白\",\n \"对白\",\n \"口技\",\n \"人声打击\",\n \"乐队\",\n \"乐团\",\n \"交响乐团\",\n \"管弦乐团\",\n \"弦乐团\",\n \"现场乐队\",\n \"乐手\",\n \"乐师\",\n \"伴奏\",\n \"演唱者\",\n \"演唱人\",\n \"和声者\",\n \"伴唱者\",\n \"领唱者\",\n \"吉他手\",\n \"贝斯手\",\n \"鼓手\",\n \"键盘手\",\n \"钢琴家\",\n \"小提琴手\",\n \"大提琴手\",\n \"中提琴手\",\n \"长笛手\",\n \"萨克斯手\",\n \"小号手\",\n \"长号手\",\n \"圆号手\",\n \"打击乐手\",\n \"单簧管手\",\n \"口琴手\",\n \"合唱团\",\n \"手风琴手\",\n \"竖琴手\",\n \"艺人\"\n ]\n },\n {\n \"id\": \"instruments\",\n \"kind\": \"instrument\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"吉他\",\n \"钢琴\",\n \"贝斯\",\n \"贝司\",\n \"鼓\",\n \"键盘\",\n \"弦乐\",\n \"小提琴\",\n \"大提琴\",\n \"中提琴\",\n \"长笛\",\n \"萨克斯\",\n \"小号\",\n \"长号\",\n \"圆号\",\n \"单簧管\",\n \"双簧管\",\n \"手风琴\",\n \"口琴\",\n \"二胡\",\n \"古筝\",\n \"琵琶\",\n \"架子鼓\",\n \"电子琴\",\n \"合成器\",\n \"打击乐\",\n \"打击乐器\",\n \"管乐\",\n \"木管\",\n \"铜管\",\n \"三味线\",\n \"三弦\",\n \"三角铁\",\n \"中国笛\",\n \"中提\",\n \"中胡\",\n \"中阮\",\n \"乌德琴\",\n \"乐器\",\n \"乐器独奏\",\n \"京胡\",\n \"伽倻琴\",\n \"低音单簧管\",\n \"低音吉他\",\n \"低音提琴\",\n \"克拉维奈\",\n \"全部乐器\",\n \"六角手风琴\",\n \"军鼓\",\n \"刮壶\",\n \"十二弦吉他\",\n \"卡洪\",\n \"口风琴\",\n \"古琴\",\n \"合成贝斯\",\n \"哈蒙德风琴\",\n \"响板\",\n \"哨笛\",\n \"唢呐\",\n \"埙\",\n \"塔布拉\",\n \"声码器\",\n \"大号\",\n \"大提\",\n \"大笒\",\n \"大管\",\n \"大键琴\",\n \"大阮\",\n \"太鼓\",\n \"奚琴\",\n \"定音鼓\",\n \"富鲁格\",\n \"富鲁格号\",\n \"小提\",\n \"小阮\",\n \"小鼓\",\n \"尤克里里\",\n \"尺八\",\n \"巴乌\",\n \"巴拉莱卡\",\n \"巴松\",\n \"布祖基\",\n \"序列器\",\n \"康加鼓\",\n \"弦\",\n \"德西马\",\n \"恰朗戈\",\n \"所有乐器\",\n \"所有乐器演奏\",\n \"手摇琴\",\n \"手碟\",\n \"手鼓\",\n \"打击垫\",\n \"扬琴\",\n \"拇指琴\",\n \"拉巴卜\",\n \"指弹吉他\",\n \"排笛\",\n \"排箫\",\n \"提琴\",\n \"曼陀林\",\n \"月琴\",\n \"木吉他\",\n \"木棒\",\n \"木琴\",\n \"木箱鼓\",\n \"木鱼\",\n \"杜杜克\",\n \"板胡\",\n \"柳琴\",\n \"框鼓\",\n \"梅洛特隆\",\n \"次中音号\",\n \"沙锤\",\n \"波德兰鼓\",\n \"洞萧\",\n \"滑棒吉他\",\n \"牛铃\",\n \"特雷门琴\",\n \"玉琴\",\n \"班卓琴\",\n \"班多钮\",\n \"班苏里笛\",\n \"瑶琴\",\n \"电吉他\",\n \"电子打击垫\",\n \"电子鼓\",\n \"电贝司\",\n \"电贝斯\",\n \"电钢琴\",\n \"短号\",\n \"短笛\",\n \"竖琴\",\n \"竖笛\",\n \"竹笛\",\n \"笙\",\n \"笛子\",\n \"笛萧\",\n \"筝\",\n \"箜篌\",\n \"管钟\",\n \"箫\",\n \"箱琴\",\n \"簧风琴\",\n \"编钟\",\n \"肩背键盘\",\n \"自动竖琴\",\n \"英国管\",\n \"葫芦丝\",\n \"蒂姆巴尔鼓\",\n \"西塔琴\",\n \"说话鼓\",\n \"谐振吉他\",\n \"踏板钢弦吉他\",\n \"转盘\",\n \"达布卡\",\n \"迪吉里杜管\",\n \"邦戈鼓\",\n \"采样器\",\n \"金贝鼓\",\n \"钟琴\",\n \"钢片琴\",\n \"钢鼓\",\n \"钹\",\n \"铃鼓\",\n \"锡笛\",\n \"锣\",\n \"键提琴\",\n \"镲\",\n \"长鼓\",\n \"阮\",\n \"陶笛\",\n \"非洲鼓\",\n \"颤音琴\",\n \"风琴\",\n \"风笛\",\n \"马头琴\",\n \"马林巴\",\n \"高胡\",\n \"鲁特琴\",\n \"齐特琴\"\n ]\n },\n {\n \"id\": \"mixer\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\"],\n \"words\": [\"混音\", \"混音师\", \"混\", \"混音工程师\", \"缩混\", \"贴混\", \"混音工程\", \"重混音师\"]\n },\n {\n \"id\": \"mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MASTERING\"],\n \"words\": [\"母带\", \"母带工程师\", \"母带处理\", \"母带制作\", \"母带后期\", \"母带工程\", \"母带师\", \"母带制作人\", \"母带处理制作人\"]\n },\n {\n \"id\": \"lyrics-and-composition\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\"],\n \"words\": [\n \"词曲\",\n \"作词/作曲\",\n \"作词作曲\",\n \"词/曲\",\n \"词&曲\",\n \"词曲作者\",\n \"词曲创作\",\n \"作曲/作词\",\n \"作曲、作词\",\n \"作词+作曲\",\n \"作词,作曲\",\n \"作词·作曲\",\n \"作词、作曲\",\n \"作词,作曲\",\n \"词/曲作\",\n \"词、曲\",\n \"词,曲\"\n ]\n },\n {\n \"id\": \"composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\", \"ARRANGER\"],\n \"words\": [\"作编曲\", \"作曲/编曲\", \"作曲编曲\", \"曲/编\", \"曲编\", \"作曲、编曲\", \"作曲,编曲\"]\n },\n {\n \"id\": \"mixing-and-mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\", \"MASTERING\"],\n \"words\": [\"混音/母带\", \"混音母带\", \"混音&母带\", \"混音与母带\", \"混音及母带\", \"母带/混音\", \"混音、母带\", \"混音,母带\"]\n },\n {\n \"id\": \"lyrics-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"ARRANGER\"],\n \"words\": [\"作词/编曲\", \"作词、编曲\", \"作词编曲\", \"词/编\", \"词编\"]\n },\n {\n \"id\": \"lyrics-composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"ARRANGER\"],\n \"words\": [\"作词/作曲/编曲\", \"作词、作曲、编曲\", \"作词作曲编曲\", \"词曲编\", \"词曲编曲\"]\n },\n {\n \"id\": \"singer-songwriter\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"PERFORMER\"],\n \"words\": [\"唱作\", \"唱作人\", \"唱作歌手\"]\n },\n {\n \"id\": \"other\",\n \"kind\": \"role\",\n \"roles\": [\"OTHER\"],\n \"words\": [\n \"录音师\",\n \"录音工程师\",\n \"录音\",\n \"录制\",\n \"工程师\",\n \"混音助理\",\n \"母带助理\",\n \"制作助理\",\n \"策划\",\n \"宣发\",\n \"摄影师\",\n \"摄像\",\n \"设计师\",\n \"美术\",\n \"美工\",\n \"视觉设计\",\n \"封面设计\",\n \"插画\",\n \"插画师\",\n \"曲绘\",\n \"校对\",\n \"打轴\",\n \"时轴\",\n \"时间轴\",\n \"修音\",\n \"修音师\",\n \"调音\",\n \"调音师\",\n \"拟音\",\n \"音效\",\n \"音响\",\n \"指挥\",\n \"经纪\",\n \"顾问\",\n \"艺术指导\",\n \"艺术总监\",\n \"音乐总监\",\n \"创意总监\",\n \"舞台监督\",\n \"灯光师\",\n \"特别感谢\",\n \"特别鸣谢\",\n \"感谢\",\n \"鸣谢\",\n \"致谢\",\n \"短视频\",\n \"MIDI编程\",\n \"OP\",\n \"PGM\",\n \"Program\",\n \"SP\",\n \"主创\",\n \"乐器技师\",\n \"乐谱\",\n \"乐谱管理员\",\n \"人声指导\",\n \"人声编辑\",\n \"企划宣传\",\n \"企宣\",\n \"分轨\",\n \"分镜\",\n \"制作统筹\",\n \"前台工程师\",\n \"剪辑\",\n \"剪辑师\",\n \"动画师\",\n \"助工\",\n \"助理\",\n \"助理工程师\",\n \"化妆\",\n \"协力\",\n \"吉他技师\",\n \"后期制作\",\n \"图绘\",\n \"声工\",\n \"声设\",\n \"声音设计师\",\n \"妆发\",\n \"妆造\",\n \"客串\",\n \"宣推\",\n \"平面\",\n \"平面视觉\",\n \"平面设计\",\n \"录音助理\",\n \"录音室经理\",\n \"扒谱\",\n \"扒谱员\",\n \"承办人\",\n \"技术支持\",\n \"抄谱\",\n \"抄谱员\",\n \"拟音师\",\n \"数字剪辑\",\n \"数字编辑\",\n \"文案策划\",\n \"映像\",\n \"服装\",\n \"杜比全景声\",\n \"歌词翻译\",\n \"灯光\",\n \"特约嘉宾\",\n \"特邀\",\n \"电脑\",\n \"电脑工程师\",\n \"监听\",\n \"监听工程师\",\n \"策监\",\n \"筹划\",\n \"编程\",\n \"编程播放\",\n \"编调\",\n \"编辑\",\n \"美术指导\",\n \"舞台总监\",\n \"艺人与产品\",\n \"艺人助理\",\n \"艺人统筹\",\n \"艺术顾问\",\n \"节拍\",\n \"节拍制作\",\n \"节拍制作人\",\n \"营销\",\n \"视觉总监\",\n \"调教\",\n \"调校\",\n \"谱务\",\n \"资产管理\",\n \"跟踪工程师\",\n \"辅助监督\",\n \"造型\",\n \"造型师\",\n \"采样\",\n \"音乐制作助理\",\n \"音乐统筹\",\n \"音乐视频导演\",\n \"音响工程师\",\n \"音序\",\n \"音控师\",\n \"音效设计\",\n \"音频编辑\",\n \"项目策划\",\n \"项目统筹\",\n \"预制作\",\n \"题字\",\n \"首席运营官\",\n \"鼓技师\",\n \"封面\",\n \"海报\",\n \"原版权\",\n \"原始版权\",\n \"次级版权\",\n \"版权运营\",\n \"业务\",\n \"企划\",\n \"宣传\",\n \"推广\",\n \"发行\",\n \"统筹\",\n \"设计\",\n \"摄影\",\n \"导演\",\n \"翻译\",\n \"后期\",\n \"工程\",\n \"声音\",\n \"支持\",\n \"艺术\",\n \"指导\",\n \"监督\",\n \"视觉\",\n \"舞台\",\n \"物料\",\n \"台词\",\n \"字幕\",\n \"文案\",\n \"脚本\",\n \"花絮\",\n \"视频\",\n \"作\",\n \"封\",\n \"监\",\n \"编\",\n \"译\",\n \"谱\"\n ]\n }\n ]\n}\n","import { CreditRole } from '@gondola-lyrics/model/runtime'\n\nimport enRules from '@rules/credits/en.json'\nimport zhHansRules from '@rules/credits/zh-hans.json'\nimport { findRuleMatchCuts, toRuleMatchKey } from '@root/utils'\n\nconst MATCH_CACHE_LIMIT = 256\nconst MAX_CACHED_LABEL_LENGTH = 256\n\nconst CREDIT_ROLES = new Map<string, CreditRole>([\n ['OTHER', CreditRole.OTHER],\n ['LYRICIST', CreditRole.LYRICIST],\n ['COMPOSER', CreditRole.COMPOSER],\n ['ARRANGER', CreditRole.ARRANGER],\n ['PRODUCER', CreditRole.PRODUCER],\n ['PERFORMER', CreditRole.PERFORMER],\n ['MIXER', CreditRole.MIXER],\n ['MASTERING', CreditRole.MASTERING],\n])\n\nconst creditRules = new Map<string, readonly CreditRole[]>()\nconst matchCache = new Map<string, readonly CreditRole[] | null>()\n\nfor (const ruleSet of [zhHansRules, enRules]) {\n for (const group of ruleSet.groups) {\n const roles = group.roles.map((name) => {\n const role = CREDIT_ROLES.get(name)\n if (role === undefined) {\n throw new TypeError('Unsupported credit role: ' + name)\n }\n return role\n })\n\n for (const word of group.words) {\n const key = toRuleMatchKey(word)\n if (!creditRules.has(key)) {\n creditRules.set(key, roles)\n }\n }\n }\n}\n\nconst haveSameRoles = (first: readonly CreditRole[], second: readonly CreditRole[]): boolean => {\n if (first === second) {\n return true\n }\n if (first.length !== second.length) {\n return false\n }\n if (first.length === 1) {\n return first[0] === second[0]\n }\n return first.every((role) => second.includes(role))\n}\n\nconst findCoveringRoles = (label: string, key: string): readonly CreditRole[] | undefined => {\n // Cuts come from the source label because the key no longer carries its whitespace.\n const cuts = findRuleMatchCuts(label)\n if (!cuts) {\n return undefined\n }\n\n const last = cuts.length - 1\n let bestCount = last + 1\n let bestRoles: readonly CreditRole[] | undefined\n // Parts are tried longest first so the fewest-part covering appears early; false means two equally short coverings disagree.\n const search = (from: number, count: number, roles: readonly CreditRole[] | undefined): boolean => {\n if (from === last) {\n if (count < bestCount) {\n bestCount = count\n bestRoles = roles\n return true\n }\n return count > bestCount || haveSameRoles(roles!, bestRoles!)\n }\n if (count + 1 > bestCount) {\n return true\n }\n for (let to = last; to > from; to -= 1) {\n const matched = creditRules.get(key.slice(cuts[from]!, cuts[to]!))\n if (!matched || (roles && !haveSameRoles(roles, matched))) {\n continue\n }\n if (!search(to, count + 1, roles ?? matched)) {\n return false\n }\n }\n return true\n }\n return search(0, 0, undefined) ? bestRoles : undefined\n}\n\n/**\n * Prefers the complete alias, then accepts adjacent parts that each resolve to the same roles.\n * Parts are cut only where the source has whitespace or a script boundary, and the covering with the fewest parts wins.\n * Unknown parts, role conflicts, and equally short coverings that disagree stay unmatched so the source remains available to later processing.\n */\nexport const matchCreditLabel = (label: string): readonly CreditRole[] | undefined => {\n const cached = matchCache.get(label)\n if (cached !== undefined) {\n return cached ?? undefined\n }\n\n const key = toRuleMatchKey(label)\n const roles = creditRules.get(key) ?? findCoveringRoles(label, key)\n\n // Cache source spellings to avoid renormalizing recurring headers without retaining arbitrarily long labels.\n if (label.length <= MAX_CACHED_LABEL_LENGTH) {\n if (matchCache.size === MATCH_CACHE_LIMIT) {\n const oldest = matchCache.keys().next().value\n if (oldest !== undefined) {\n matchCache.delete(oldest)\n }\n }\n matchCache.set(label, roles ?? null)\n }\n return roles\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport {\n LineType,\n Timing,\n deriveLanguageUsages,\n getContentText,\n isWordLevelLine,\n makeMeta,\n makeMetaCredit,\n makeText,\n} from '@gondola-lyrics/model/runtime'\n\nimport { trimRuleWhitespace } from '@root/utils'\n\nimport { matchCreditLabel } from './rules'\n\nexport const DEFAULT_CREDIT_NAME_SEPARATORS = ['+', ':', '/'] as const\n\nexport interface ExtractCreditsOptions {\n /**\n * Removes extracted source lines unless they carry background vocals, line annotations, or word translations or romanizations.\n * Ruby annotations alone do not prevent removal.\n *\n * Refreshes language usages and falls back to line timing when no word-level content remains.\n *\n * @default false\n */\n removeSourceLines?: boolean\n /**\n * Splits names at literal separators and ignores empty strings; an empty list preserves the complete source name.\n * Longer separators take precedence so overlapping delimiters remain predictable.\n *\n * @default DEFAULT_CREDIT_NAME_SEPARATORS\n */\n nameSeparators?: readonly string[]\n}\n\ninterface CreditEntry {\n index: number\n names: Set<string>\n}\n\ntype CreditIndex = Map<runtime.MetaCredit['raw'], Map<runtime.MetaCredit['role'], CreditEntry>>\n\nconst createCreditIndex = (credits: readonly runtime.MetaCredit[]): CreditIndex => {\n const creditIndex: CreditIndex = new Map()\n for (let index = 0; index < credits.length; index += 1) {\n const credit = credits[index]!\n let entries = creditIndex.get(credit.raw)\n if (!entries) {\n entries = new Map()\n creditIndex.set(credit.raw, entries)\n }\n let entry = entries.get(credit.role)\n if (!entry) {\n entry = { index, names: new Set() }\n entries.set(credit.role, entry)\n }\n for (const name of credit.names) {\n entry.names.add(name.text)\n }\n }\n return creditIndex\n}\n\nconst prepareCreditSeparators = (selected: readonly string[] | undefined): readonly string[] => {\n if (!selected || selected === DEFAULT_CREDIT_NAME_SEPARATORS) {\n return DEFAULT_CREDIT_NAME_SEPARATORS\n }\n\n const useDefaultSeparators =\n selected.length === DEFAULT_CREDIT_NAME_SEPARATORS.length &&\n selected.every((separator, index) => separator === DEFAULT_CREDIT_NAME_SEPARATORS[index])\n if (useDefaultSeparators) {\n return DEFAULT_CREDIT_NAME_SEPARATORS\n }\n\n const separators = [...new Set(selected)].filter(Boolean).sort((first, second) => second.length - first.length)\n return separators\n}\n\nconst readCreditNames = (text: string, separators: readonly string[]): string[] => {\n const hasSeparator = separators.some((separator) => text.includes(separator))\n if (!hasSeparator) {\n const name = trimRuleWhitespace(text)\n return name ? [name] : []\n }\n\n let names = [text]\n for (const separator of separators) {\n const hasNameSeparator = names.some((name) => name.includes(separator))\n if (hasNameSeparator) {\n names = names.flatMap((name) => name.split(separator))\n }\n }\n\n const unique = new Set<string>()\n for (const name of names) {\n const trimmed = trimRuleWhitespace(name)\n if (trimmed) {\n unique.add(trimmed)\n }\n }\n return [...unique]\n}\n\nconst canRemoveSourceLine = (line: runtime.Line): boolean => {\n if (line.backgrounds.length || line.annotation) {\n return false\n }\n\n // Credit extraction does not carry translations or romanizations into metadata.\n // Ruby alone must not keep extracted credits in the lyric stream.\n return !line.content?.words.some(({ annotation }) => {\n if (!annotation) {\n return false\n }\n return !!(annotation.translations.length || annotation.romans.length)\n })\n}\n\nconst hasCreditColon = (word: runtime.Word): boolean => {\n return word.text.includes(':') || word.text.includes(':')\n}\n\n/**\n * Preserves source labels and name spelling while matching normalized credit labels in normal lead lines.\n * Merges names by role and original label without inferring their language.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const extractCredits = (lyric: runtime.Lyric, options: ExtractCreditsOptions = {}): runtime.Lyric => {\n const separators = prepareCreditSeparators(options.nameSeparators)\n\n const originalCredits = lyric.meta?.credits ?? []\n let credits = originalCredits\n let creditIndex: CreditIndex | undefined\n\n const originalLines = lyric.lines\n let remainingLines: runtime.Line[] | undefined\n for (let index = 0; index < originalLines.length; index += 1) {\n const line = originalLines[index]!\n if (line.type !== LineType.NORMAL) {\n remainingLines?.push(line)\n continue\n }\n\n const content = line.content\n // Avoid joining word-timed lyric content when no word can contain a credit delimiter.\n const hasCandidateContent = content?.text !== undefined || (content?.words.some(hasCreditColon) ?? false)\n if (!hasCandidateContent) {\n remainingLines?.push(line)\n continue\n }\n const text = getContentText(content)\n const colon = text.search(/[::]/)\n if (colon === -1) {\n remainingLines?.push(line)\n continue\n }\n const raw = trimRuleWhitespace(text.slice(0, colon))\n const roles = matchCreditLabel(raw)\n const names = roles ? readCreditNames(text.slice(colon + 1), separators) : []\n if (!roles || !names.length) {\n remainingLines?.push(line)\n continue\n }\n\n creditIndex ??= createCreditIndex(originalCredits)\n let entries = creditIndex.get(raw)\n if (!entries) {\n entries = new Map()\n creditIndex.set(raw, entries)\n }\n for (const role of roles) {\n let entry = entries.get(role)\n const additions = names.filter((name) => !entry?.names.has(name))\n if (!additions.length) {\n continue\n }\n if (credits === originalCredits) {\n credits = [...originalCredits]\n }\n const nameNodes = additions.map((name) => makeText({ text: name }))\n if (!entry) {\n entry = { index: credits.length, names: new Set() }\n entries.set(role, entry)\n credits.push(makeMetaCredit({ role, raw, names: nameNodes }))\n } else {\n const credit = credits[entry.index]!\n credits[entry.index] = { ...credit, names: [...credit.names, ...nameNodes] }\n }\n for (const name of additions) {\n entry.names.add(name)\n }\n }\n\n if (options.removeSourceLines && canRemoveSourceLine(line)) {\n remainingLines ??= originalLines.slice(0, index)\n } else {\n remainingLines?.push(line)\n }\n }\n\n if (credits === originalCredits && !remainingLines) {\n return lyric\n }\n\n const removed = !!remainingLines\n const lines = remainingLines ?? originalLines\n\n let timing = lyric.timing\n if (removed && timing === Timing.WORD) {\n const hasWordLevelContent = lines.some((line) => {\n return isWordLevelLine(line) || line.backgrounds.some(isWordLevelLine)\n })\n if (!hasWordLevelContent) {\n timing = Timing.LINE\n }\n }\n\n const meta = credits === originalCredits ? lyric.meta : lyric.meta ? { ...lyric.meta, credits } : makeMeta({ credits })\n return { ...lyric, meta, lines, timing, languages: removed ? deriveLanguageUsages(lines) : lyric.languages }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { LineType, WordType, makeWordEmphasis, validateTimeRange } from '@gondola-lyrics/model/runtime'\n\nimport { mapShared } from '@root/utils'\n\nexport interface DeriveWordEmphasisOptions {\n /**\n * Word duration must strictly exceed this threshold, expressed as a non-negative safe integer in milliseconds.\n *\n * @default 3000\n */\n durationThreshold?: number\n}\n\nconst deriveContentEmphasis = (content: runtime.LineContent | undefined, durationThreshold: number): runtime.LineContent | undefined => {\n if (!content) {\n return content\n }\n\n const words = mapShared(content.words, (word) => {\n if (word.type !== WordType.NORMAL || word.emphasis?.derived === false) {\n return word\n }\n\n const time = word.time\n const emphasized = !!time && time.end !== undefined && !validateTimeRange(time).length && time.end - time.start > durationThreshold\n\n if (emphasized) {\n if (word.emphasis?.value) {\n return word\n }\n return { ...word, emphasis: makeWordEmphasis({ value: true, derived: true }) }\n }\n if (!word.emphasis) {\n return word\n }\n return { ...word, emphasis: undefined }\n })\n\n return words === content.words ? content : { ...content, words }\n}\n\n/**\n * Derives emphasis for normal words in lead and background vocals using complete, valid word ranges.\n * The stored word duration must strictly exceed durationThreshold; metadata offset and duration have no effect.\n *\n * Source emphasis, including an explicit false, is preserved; derived emphasis is cleared when a word no longer qualifies.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n *\n * @throws {RangeError} If durationThreshold is not a non-negative safe integer in milliseconds.\n */\nexport const deriveWordEmphasis = (lyric: runtime.Lyric, options: DeriveWordEmphasisOptions = {}): runtime.Lyric => {\n const durationThreshold = options.durationThreshold ?? 3000\n if (!Number.isSafeInteger(durationThreshold) || durationThreshold < 0) {\n throw new RangeError('durationThreshold must be a non-negative safe integer in milliseconds')\n }\n\n const lines = mapShared(lyric.lines, (line) => {\n if (line.type !== LineType.NORMAL) {\n return line\n }\n\n const content = deriveContentEmphasis(line.content, durationThreshold)\n const backgrounds = mapShared(line.backgrounds, (background) => {\n const content = deriveContentEmphasis(background.content, durationThreshold)\n return content === background.content ? background : { ...background, content }\n })\n\n return content === line.content && backgrounds === line.backgrounds ? line : { ...line, content, backgrounds }\n })\n\n return lines === lyric.lines ? lyric : { ...lyric, lines }\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"en\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"copyright-notice\",\n \"category\": \"copyright\",\n \"match\": \"contains\",\n \"words\": [\n \"Allrightsreserved\",\n \"Allrightsofthe\",\n \"Nopartofthis\",\n \"Unauthorizedreproduction\",\n \"Unauthorizedduplication\",\n \"Unauthorizedcopying\",\n \"Unauthorizeduse\",\n \"Underexclusivelicense\",\n \"Allrightsreservedby\",\n \"Anyunauthorizeduse\",\n \"Usedbypermission\"\n ]\n },\n {\n \"id\": \"copyright-label\",\n \"category\": \"copyright\",\n \"match\": \"whole\",\n \"words\": [\"Copyright\", \"License\"]\n },\n {\n \"id\": \"adaptation-label\",\n \"category\": \"adaptation\",\n \"match\": \"whole\",\n \"words\": [\"Cover\", \"Remake\", \"Tribute\", \"Adaptation\"]\n },\n {\n \"id\": \"disclaimer-notice\",\n \"category\": \"disclaimer\",\n \"match\": \"contains\",\n \"words\": [\"Foreducationalpurposes\", \"Fornon-commercial\", \"Fornoncommercial\", \"Fornonprofitable\", \"Forpersonaluse\", \"Forpromotionalpurposes\"]\n },\n {\n \"id\": \"organization-label\",\n \"category\": \"organization\",\n \"match\": \"contains\",\n \"words\": [\"ManufacturingCompany\", \"RecordCompany\", \"RecordLabel\", \"Distribution\"]\n },\n {\n \"id\": \"studio-label\",\n \"category\": \"studio\",\n \"match\": \"contains\",\n \"words\": [\"RecordingStudio\", \"MasteringStudio\", \"Mixingstudio\", \"Mixstudio\", \"StudioPersonnel\"]\n }\n ]\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"zh-hans\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"copyright-notice\",\n \"category\": \"copyright\",\n \"match\": \"contains\",\n \"words\": [\n \"版权所有\",\n \"保留所有权利\",\n \"侵权必究\",\n \"翻版必究\",\n \"违者必究\",\n \"未经授权\",\n \"未经许可\",\n \"版权归\",\n \"版权属于\",\n \"不得转载\",\n \"严禁转载\",\n \"盗版必究\",\n \"禁止转载\"\n ]\n },\n {\n \"id\": \"copyright-label\",\n \"category\": \"copyright\",\n \"match\": \"whole\",\n \"words\": [\"版权\", \"授权\"]\n },\n {\n \"id\": \"adaptation-label\",\n \"category\": \"adaptation\",\n \"match\": \"whole\",\n \"words\": [\"改编\", \"翻唱\", \"致敬\"]\n },\n {\n \"id\": \"disclaimer-notice\",\n \"category\": \"disclaimer\",\n \"match\": \"contains\",\n \"words\": [\"仅供学习\", \"仅供个人\", \"仅供参考\", \"仅供交流\", \"禁止商业用途\", \"非商业用途\"]\n },\n {\n \"id\": \"organization-label\",\n \"category\": \"organization\",\n \"match\": \"contains\",\n \"words\": [\"唱片公司\", \"制作公司\", \"发行公司\", \"承办单位\", \"次级发行方\", \"厂牌\"]\n },\n {\n \"id\": \"studio-label\",\n \"category\": \"studio\",\n \"match\": \"contains\",\n \"words\": [\"录音室\", \"录音棚\", \"混音室\", \"混音工作室\", \"混音录音室\", \"母带录音室\", \"母带处理录音室\"]\n }\n ]\n}\n","import enRules from '@rules/filters/en.json'\nimport zhHansRules from '@rules/filters/zh-hans.json'\nimport { toRuleMatchKey } from '@root/utils'\n\nexport interface NonLyricGroup {\n readonly category: string\n readonly whole: boolean\n readonly words: readonly string[]\n}\n\nconst nonLyricGroups: readonly NonLyricGroup[] = [zhHansRules, enRules].flatMap((ruleSet) => {\n return ruleSet.groups.map((group) => {\n if (group.match !== 'contains' && group.match !== 'whole') {\n throw new TypeError('Unsupported filter match: ' + group.match)\n }\n return { category: group.category, whole: group.match === 'whole', words: group.words.map(toRuleMatchKey) }\n })\n})\n\n/**\n * Categories the bundled filter rules identify, in rule order.\n */\nexport const NON_LYRIC_CATEGORIES: readonly string[] = [...new Set(nonLyricGroups.map((group) => group.category))]\n\n/**\n * Keeps rule order so earlier groups still take precedence within a selection; undefined selects every group.\n */\nexport const selectNonLyricGroups = (categories: readonly string[] | undefined): readonly NonLyricGroup[] => {\n if (!categories) {\n return nonLyricGroups\n }\n return nonLyricGroups.filter((group) => categories.includes(group.category))\n}\n\n/**\n * Returns the category of the first group that identifies the normalized text.\n * whole needs the complete text to equal a word, while contains accepts a word anywhere because the rules carry no word boundaries.\n */\nexport const matchNonLyricCategory = (text: string, groups: readonly NonLyricGroup[]): string | undefined => {\n const key = toRuleMatchKey(text)\n if (!key) {\n return undefined\n }\n for (const group of groups) {\n const matched = group.whole ? group.words.includes(key) : group.words.some((word) => key.includes(word))\n if (matched) {\n return group.category\n }\n }\n return undefined\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { LineType, Timing, deriveLanguageUsages, getContentText, isWordLevelLine } from '@gondola-lyrics/model/runtime'\n\nimport { matchNonLyricCategory, selectNonLyricGroups } from './rules'\n\nexport interface RemoveNonLyricLinesOptions {\n /**\n * Removes lines identified under these categories; an empty list skips the shared rules.\n * First-line music information is selected independently.\n *\n * @default NON_LYRIC_CATEGORIES\n */\n categories?: readonly string[]\n /**\n * Enables first-line music-information matching using these known titles and artists.\n * Omitting this option, or leaving either list empty, skips the check; lyric metadata is not inferred.\n */\n musicInfo?: {\n titles?: readonly string[]\n artists?: readonly string[]\n }\n}\n\nconst toMusicInfoMatchText = (text: string): string => {\n return text\n .normalize('NFC')\n .trim()\n .replace(/\\s+/gu, ' ')\n .replace(/[A-Z]+/g, (run) => run.toLowerCase())\n}\n\nconst findFirstMusicInfoLine = (lyric: runtime.Lyric, musicInfo: NonNullable<RemoveNonLyricLinesOptions['musicInfo']>) => {\n const titles = (musicInfo.titles ?? []).map(toMusicInfoMatchText).filter(Boolean)\n const artists = (musicInfo.artists ?? []).map(toMusicInfoMatchText).filter(Boolean)\n if (!titles.length || !artists.length) {\n return -1\n }\n\n const index = lyric.lines.findIndex((line) => {\n return line.type === LineType.NORMAL && !!getContentText(line.content).trim()\n })\n if (index === -1) {\n return -1\n }\n\n const parts = getContentText(lyric.lines[index]!.content).trim().split(' - ')\n if (parts.length !== 2) {\n return -1\n }\n const left = toMusicInfoMatchText(parts[0]!)\n const right = toMusicInfoMatchText(parts[1]!)\n if (!left || !right) {\n return -1\n }\n\n const titleOnLeft = titles.some((title) => left.includes(title))\n const titleOnRight = titles.some((title) => right.includes(title))\n const artistOnLeft = artists.some((artist) => left.includes(artist))\n const artistOnRight = artists.some((artist) => right.includes(artist))\n return (titleOnLeft && artistOnRight) || (artistOnLeft && titleOnRight) ? index : -1\n}\n\n/**\n * Removes normal lead lines matched by the shared notice rules and, when musicInfo is provided, first-line song information.\n * The first non-empty normal line must have one literal \" - \" separator and a known title and artist on opposite sides; either order and extra text such as bracketed aliases are accepted.\n * Drops attached annotations and backgrounds, refreshes language usages, and falls back to line timing when no word-level content remains.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const removeNonLyricLines = (lyric: runtime.Lyric, options: RemoveNonLyricLinesOptions = {}): runtime.Lyric => {\n const groups = selectNonLyricGroups(options.categories)\n const musicInfoIndex = options.musicInfo ? findFirstMusicInfoLine(lyric, options.musicInfo) : -1\n if (!groups.length && musicInfoIndex === -1) {\n return lyric\n }\n\n const lines = lyric.lines.filter((line, index) => {\n if (index === musicInfoIndex) {\n return false\n }\n return line.type !== LineType.NORMAL || matchNonLyricCategory(getContentText(line.content), groups) === undefined\n })\n\n if (lines.length === lyric.lines.length) {\n return lyric\n }\n\n let timing = lyric.timing\n if (timing === Timing.WORD) {\n const hasWordLevelContent = lines.some((line) => {\n return isWordLevelLine(line) || line.backgrounds.some(isWordLevelLine)\n })\n if (!hasWordLevelContent) {\n timing = Timing.LINE\n }\n }\n return { ...lyric, lines, timing, languages: deriveLanguageUsages(lines) }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { LineType, Timing, makeLineInstrumental, makeTimeRange, validateTimeRange } from '@gondola-lyrics/model/runtime'\n\nexport interface InsertInstrumentalLinesOptions {\n /**\n * Minimum gap duration in milliseconds, including an outro.\n *\n * @default 10000\n */\n minDuration?: number\n /**\n * Minimum gap duration before the first vocal in milliseconds.\n *\n * @default 5000\n */\n minIntroDuration?: number\n /**\n * Outro boundary in milliseconds, measured on the same timeline as line ranges.\n * An omitted boundary leaves the outro unknown.\n */\n endTime?: number\n}\n\nconst MAX_TIME = 2 ** 31 - 1\n\nconst readMinimum = (value: number | undefined, fallback: number, name: string): number => {\n const minimum = value ?? fallback\n if (!Number.isSafeInteger(minimum) || minimum < 0) {\n throw new RangeError(`${name} must be a non-negative safe integer in milliseconds`)\n }\n return minimum\n}\n\nconst readEndTime = (value: number | undefined): number | undefined => {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > MAX_TIME)) {\n throw new RangeError(`endTime must be a non-negative integer in milliseconds no greater than ${MAX_TIME}`)\n }\n return value\n}\n\nconst compareTimeRanges = (left: runtime.TimeRange | undefined, right: runtime.TimeRange | undefined): number => {\n const leftStart = left?.start ?? Infinity\n const rightStart = right?.start ?? Infinity\n if (leftStart !== rightStart) {\n return leftStart - rightStart\n }\n\n const leftEnd = left?.end ?? Infinity\n const rightEnd = right?.end ?? Infinity\n return leftEnd === rightEnd ? 0 : leftEnd - rightEnd\n}\n\nconst readOccupiedTimeline = (lyric: runtime.Lyric) => {\n const ranges: runtime.TimeRange[] = []\n let firstSungStart = Infinity\n\n const append = (time: runtime.TimeRange | undefined, sung: boolean): boolean => {\n if (!time || validateTimeRange(time).length) {\n return false\n }\n\n // An empty half-open range cannot divide an uncovered gap.\n if (time.end === time.start) {\n return true\n }\n ranges.push(time)\n if (sung) {\n firstSungStart = Math.min(firstSungStart, time.start)\n }\n return true\n }\n\n for (const line of lyric.lines) {\n if (!append(line.time, line.type === LineType.NORMAL)) {\n return undefined\n }\n for (const background of line.backgrounds) {\n if (!append(background.time, true)) {\n return undefined\n }\n }\n }\n\n ranges.sort(compareTimeRanges)\n return { ranges, firstSungStart }\n}\n\nconst compareLineTimes = (left: runtime.Line, right: runtime.Line): number => {\n return compareTimeRanges(left.time, right.time)\n}\n\nconst mergeLines = (lines: runtime.Line[], inserted: runtime.Line[]): runtime.Line[] => {\n let source = lines\n for (const [index, line] of lines.entries()) {\n const previous = lines[index - 1]\n if (previous && compareLineTimes(previous, line) > 0) {\n source = [...lines].sort(compareLineTimes)\n break\n }\n }\n\n const merged: runtime.Line[] = []\n let nextIndex = 0\n for (const line of source) {\n let next = inserted[nextIndex]\n while (next && compareLineTimes(next, line) < 0) {\n merged.push(next)\n nextIndex += 1\n next = inserted[nextIndex]\n }\n merged.push(line)\n }\n merged.push(...inserted.slice(nextIndex))\n return merged\n}\n\n/**\n * Inserts derived instrumental lines into uncovered gaps on the stored timeline, starting at zero.\n * Existing lines and background vocals occupy their full ranges; an unknown end blocks later gaps.\n *\n * An outro requires an explicit endTime; metadata duration and offset do not affect insertion.\n *\n * @returns The input when unchanged, or a new lyric and lines array sharing existing nodes.\n */\nexport const insertInstrumentalLines = (lyric: runtime.Lyric, options: InsertInstrumentalLinesOptions = {}): runtime.Lyric => {\n const minDuration = readMinimum(options.minDuration, 10000, 'minDuration')\n const minIntroDuration = readMinimum(options.minIntroDuration, 5000, 'minIntroDuration')\n const endTime = readEndTime(options.endTime)\n if ((lyric.timing !== Timing.LINE && lyric.timing !== Timing.WORD) || !lyric.lines.length) {\n return lyric\n }\n\n const occupied = readOccupiedTimeline(lyric)\n if (!occupied || occupied.firstSungStart === Infinity) {\n return lyric\n }\n\n const inserted: runtime.Line[] = []\n let coveredEnd = 0\n\n const appendGap = (end: number, minimum: number): void => {\n const duration = end - coveredEnd\n if (duration > 0 && duration >= minimum) {\n inserted.push(makeLineInstrumental({ time: makeTimeRange({ start: coveredEnd, end }) }))\n }\n }\n\n for (const range of occupied.ranges) {\n appendGap(range.start, range.start <= occupied.firstSungStart ? minIntroDuration : minDuration)\n // An unknown end gives no evidence of a later gap, so it occupies the rest of the timeline.\n coveredEnd = Math.max(coveredEnd, range.end ?? Infinity)\n if (coveredEnd === Infinity) {\n break\n }\n }\n\n if (endTime !== undefined) {\n appendGap(endTime, minDuration)\n }\n\n if (!inserted.length) {\n return lyric\n }\n return { ...lyric, lines: mergeLines(lyric.lines, inserted) }\n}\n","/**\n * Groups of characters a source may write without a space where one belongs.\n */\nexport const InsertSpacesRules = {\n /**\n * A CJK character touching a Latin letter, as in `ABC中文`.\n */\n CJK_ENGLISH: 'cjkEnglish',\n /**\n * A CJK character touching a digit, as in `2026年`.\n */\n CJK_NUMBER: 'cjkNumber',\n /**\n * A comma, semicolon, exclamation mark, question mark or colon touching a character, as in `a,b`; a clock time or thousands separator such as `10:30` stays as written.\n */\n PUNCTUATION: 'punctuation',\n /**\n * A quotation mark touching a neighbouring character.\n */\n QUOTE: 'quote',\n /**\n * A bracket touching the character outside it.\n */\n BRACKET_OUTSIDE: 'bracketOutside',\n /**\n * A plus, equals or ampersand touching a character, as in `a+b`; a compound operator such as `a+=b` stays as written.\n */\n MATH_OPERATOR: 'mathOperator',\n /**\n * A hyphen touching a CJK character, as in `中-文`; a compound word such as `well-known` stays as written.\n */\n HYPHEN: 'hyphen',\n /**\n * A slash touching a character, as in `and/or`; a fraction such as `1/2` stays as written.\n */\n SLASH: 'slash',\n /**\n * Runs of two or more spaces, collapsed to one.\n */\n COMPRESS_SPACES: 'compressSpaces',\n /**\n * Spaces just inside a bracket or quotation mark, removed.\n */\n TRIM_INSIDE_SYMBOLS: 'trimInsideSymbols',\n} as const\n\nexport type InsertSpacesRule = (typeof InsertSpacesRules)[keyof typeof InsertSpacesRules]\n\nexport const INSERT_SPACES_RULES = Object.values(InsertSpacesRules) as InsertSpacesRule[]\n","import type { InsertSpacesRule } from './constants'\n\nimport { INSERT_SPACES_RULES, InsertSpacesRules } from './constants'\n\n// Kana, CJK extensions, unified ideographs and compatibility ideographs.\nconst CJK_RANGE = '\\u3040-\\u30ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff'\nconst LATIN_RANGE = 'A-Za-z'\nconst DIGIT_RANGE = '0-9'\nconst SYMBOL_RANGE = '!@#$%^&+\\\\-=/|<>'\nconst WORD_RANGE = `${LATIN_RANGE}${DIGIT_RANGE}${CJK_RANGE}`\nconst ALL_RANGE = `${WORD_RANGE}${SYMBOL_RANGE}`\n\nconst CJK_LATIN = new RegExp(`([${CJK_RANGE}])([${LATIN_RANGE}])`, 'gu')\nconst LATIN_CJK = new RegExp(`([${LATIN_RANGE}])([${CJK_RANGE}])`, 'gu')\nconst CJK_DIGIT = new RegExp(`([${CJK_RANGE}])([${DIGIT_RANGE}])`, 'gu')\nconst DIGIT_CJK = new RegExp(`([${DIGIT_RANGE}])([${CJK_RANGE}])`, 'gu')\n\nconst PUNCTUATION = new RegExp(`([${ALL_RANGE}])([!;,\\\\?:])(?=[${ALL_RANGE}])`, 'gu')\nconst QUOTE_BEFORE = new RegExp(`([${CJK_RANGE}${LATIN_RANGE}${DIGIT_RANGE}])([\"\\`“‘])`, 'gu')\nconst QUOTE_AFTER = new RegExp(`([\"”’])([${CJK_RANGE}${LATIN_RANGE}${DIGIT_RANGE}])`, 'gu')\nconst BRACKET_BEFORE = new RegExp(`([${WORD_RANGE}])([\\\\[({<])`, 'gu')\nconst BRACKET_AFTER = new RegExp(`([\\\\])}>])([${WORD_RANGE}])`, 'gu')\nconst MATH_OPERATOR = new RegExp(`([${ALL_RANGE}])([+=&])(?=[${ALL_RANGE}])`, 'gu')\nconst HYPHEN = new RegExp(`(?<=[${WORD_RANGE}])-(?=[${WORD_RANGE}])`, 'gu')\nconst SLASH = new RegExp(`([${WORD_RANGE}])(/)(?=[${WORD_RANGE}])`, 'gu')\nconst TRIM_INSIDE_SYMBOLS = new RegExp('([<[{\"“‘(])\\\\s*([^<>[\\\\]{}()\"“‘”’]*?)\\\\s*([>\\\\]}\"”’)])', 'gu')\nconst REPEATED_SPACES = / {2,}/gu\n\n// A run of two or more marks belongs together: `a--b`, `a//b`, `a...b`, `a::b` and `a+=b` stay as written.\nconst REPEATED_MARKS = /[!;,\\\\?:+\\-=/&]{2,}/gu\n\n// A control character stands in for a held run, since lyric text never carries one and no rule's character class covers it.\nconst PLACEHOLDER = /\\u0001(\\d+)\\u0001/gu\n\nconst isDigit = (char: string | undefined) => {\n return !!char && char >= '0' && char <= '9'\n}\n\nconst isLatin = (char: string | undefined) => {\n return !!char && ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z'))\n}\n\n/**\n * A comma or colon between two digits belongs to one number or clock time, so it stays tight.\n */\nconst applyPunctuation = (text: string) => {\n return text.replace(PUNCTUATION, (match, before: string, mark: string, offset: number, source: string) => {\n if ((mark === ',' || mark === ':') && isDigit(before) && isDigit(source[offset + match.length])) {\n return match\n }\n return `${before}${mark} `\n })\n}\n\n/**\n * A hyphen between two digits is a date, range or phone number, and one between two Latin letters joins a compound word.\n *\n * An edge hyphen is left alone, since it reads as a sign or a list marker rather than a dash.\n */\nconst applyHyphen = (text: string) => {\n return text.replace(HYPHEN, (match, offset: number, source: string) => {\n const left = source[offset - 1]\n const right = source[offset + 1]\n return (isDigit(left) && isDigit(right)) || (isLatin(left) && isLatin(right)) ? match : ' - '\n })\n}\n\n/**\n * A slash between two digits is a fraction or date, so it stays tight.\n */\nconst applySlash = (text: string) => {\n return text.replace(SLASH, (match, before: string, slash: string, offset: number, source: string) => {\n return isDigit(before) && isDigit(source[offset + match.length]) ? match : `${before} ${slash} `\n })\n}\n\n/**\n * Trims the spaces just inside a bracket or quotation mark, without touching a nested pair inside it.\n */\nconst applyTrimInsideSymbols = (text: string) => {\n return text.replace(TRIM_INSIDE_SYMBOLS, (match, left: string, inner: string, right: string) => {\n return `${left}${inner.trim()}${right}`\n })\n}\n\nconst APPLIERS: Record<InsertSpacesRule, (text: string) => string> = {\n [InsertSpacesRules.CJK_ENGLISH]: (text) => {\n return text.replace(CJK_LATIN, '$1 $2').replace(LATIN_CJK, '$1 $2')\n },\n [InsertSpacesRules.CJK_NUMBER]: (text) => {\n return text.replace(CJK_DIGIT, '$1 $2').replace(DIGIT_CJK, '$1 $2')\n },\n [InsertSpacesRules.PUNCTUATION]: applyPunctuation,\n [InsertSpacesRules.QUOTE]: (text) => {\n return text.replace(QUOTE_BEFORE, '$1 $2').replace(QUOTE_AFTER, '$1 $2')\n },\n [InsertSpacesRules.BRACKET_OUTSIDE]: (text) => {\n return text.replace(BRACKET_BEFORE, '$1 $2').replace(BRACKET_AFTER, '$1 $2')\n },\n [InsertSpacesRules.MATH_OPERATOR]: (text) => {\n return text.replace(MATH_OPERATOR, '$1 $2 ')\n },\n [InsertSpacesRules.HYPHEN]: applyHyphen,\n [InsertSpacesRules.SLASH]: applySlash,\n [InsertSpacesRules.COMPRESS_SPACES]: (text) => {\n return text.replace(REPEATED_SPACES, ' ')\n },\n [InsertSpacesRules.TRIM_INSIDE_SYMBOLS]: applyTrimInsideSymbols,\n}\n\n// Order matters: separating marks first, then tidying the spaces they may have doubled.\nconst APPLY_ORDER: InsertSpacesRule[] = [\n InsertSpacesRules.CJK_ENGLISH,\n InsertSpacesRules.CJK_NUMBER,\n InsertSpacesRules.PUNCTUATION,\n InsertSpacesRules.QUOTE,\n InsertSpacesRules.BRACKET_OUTSIDE,\n InsertSpacesRules.MATH_OPERATOR,\n InsertSpacesRules.HYPHEN,\n InsertSpacesRules.SLASH,\n InsertSpacesRules.TRIM_INSIDE_SYMBOLS,\n InsertSpacesRules.COMPRESS_SPACES,\n]\n\n/**\n * Falls back to every rule only when no list was given, so an explicit empty list means \"apply nothing\".\n */\nexport const resolveRules = (rules: InsertSpacesRule[] | undefined) => {\n return new Set(rules ?? INSERT_SPACES_RULES)\n}\n\n/**\n * Applies the selected rules to one text, returning it trimmed.\n *\n * Repeated marks are held aside first, so a rule that widens a single mark never splits a run that belongs together.\n */\nexport const insertSpaces = (text: string, rules: Set<InsertSpacesRule>) => {\n if (!text.trim()) {\n return text\n }\n\n const held: string[] = []\n const guarded = text.replace(REPEATED_MARKS, (match) => {\n held.push(match)\n return `\\u0001${held.length - 1}\\u0001`\n })\n\n let result = guarded\n for (const rule of APPLY_ORDER) {\n if (rules.has(rule)) {\n result = APPLIERS[rule](result)\n }\n }\n return result\n .replace(PLACEHOLDER, (match, index: string) => {\n return held[Number(index)] ?? match\n })\n .trim()\n}\n","import type { runtime } from '@gondola-lyrics/model'\nimport type { InsertSpacesRule } from './constants'\n\nimport {\n LineType,\n makeLineAnnotationRoman,\n makeLineAnnotationTranslation,\n makeLineContentText,\n makeLineContentWords,\n makeWordSpace,\n} from '@gondola-lyrics/model/runtime'\n\nimport { mapShared } from '@root/utils'\n\nimport { isNormalWord } from '../background/utils'\nimport { insertSpaces, resolveRules } from './rules'\n\nexport interface InsertSpacesOptions {\n /**\n * Rules to apply; omitting the list applies all of them.\n */\n rules?: InsertSpacesRule[]\n /**\n * Rewrite the translation and romanization tracks too.\n *\n * @default true\n */\n annotations?: boolean\n}\n\n/**\n * Maps the rewritten text back onto the original words.\n *\n * Every non-space character of the original survives in order, so walking both strings keeps each word's time,\n * language and annotation while the inserted spaces become their own words.\n * A word whose characters stay adjacent in the rewrite is kept whole, so only a word the space actually cuts is split.\n */\nconst alignWords = (words: runtime.Word[], text: string) => {\n const source = words.filter(isNormalWord)\n const result: runtime.Word[] = []\n let cursor = 0\n\n const takeSpaces = () => {\n let spaces = ''\n while (text[cursor] === ' ') {\n spaces += ' '\n cursor += 1\n }\n return spaces\n }\n\n for (const word of source) {\n const spaces = takeSpaces()\n if (spaces) {\n result.push(makeWordSpace({ text: spaces }))\n }\n\n // A word the rewrite left intact keeps its identity, including any annotation and emphasis.\n if (text.startsWith(word.text, cursor)) {\n result.push(word)\n cursor += word.text.length\n continue\n }\n\n // The rewrite split this word, so each piece carries the slice it stands for.\n let remaining = word.text\n while (remaining) {\n let taken = 0\n while (taken < remaining.length && text[cursor + taken] === remaining[taken]) {\n taken += 1\n }\n if (taken === 0) {\n return undefined\n }\n result.push({ ...word, text: remaining.slice(0, taken) })\n cursor += taken\n remaining = remaining.slice(taken)\n if (!remaining) {\n break\n }\n const split = takeSpaces()\n if (!split) {\n // Nothing separates the pieces, so the rewrite changed more than spacing.\n return undefined\n }\n result.push(makeWordSpace({ text: split }))\n }\n }\n\n const tail = takeSpaces()\n if (tail) {\n result.push(makeWordSpace({ text: tail }))\n }\n\n // A leftover character means the rewrite changed something other than spacing, so the caller keeps the original words.\n if (cursor !== text.length) {\n return undefined\n }\n return result\n}\n\n/**\n * Applies the rules to a word-level content, keeping it unchanged when the alignment fails.\n */\nconst applyToWords = (content: runtime.LineContent, rules: Set<InsertSpacesRule>) => {\n const text = content.words.map((word) => word.text).join('')\n const rewritten = insertSpaces(text, rules)\n if (rewritten === text) {\n return content\n }\n\n const words = alignWords(content.words, rewritten)\n if (!words) {\n return content\n }\n return words.length ? makeLineContentWords(words) : undefined\n}\n\n/**\n * Applies the rules to a plain-text content, dropping a line the rules would leave empty.\n */\nconst applyToText = (content: runtime.LineContent, rules: Set<InsertSpacesRule>) => {\n const text = content.text ?? ''\n const rewritten = insertSpaces(text, rules)\n if (rewritten === text) {\n return content\n }\n return rewritten ? makeLineContentText(rewritten) : undefined\n}\n\n/**\n * Applies the rules to one content by the shape it carries.\n */\nconst applyToContent = (content: runtime.LineContent | undefined, rules: Set<InsertSpacesRule>) => {\n if (!content) {\n return content\n }\n return content.words.length ? applyToWords(content, rules) : applyToText(content, rules)\n}\n\n/**\n * Applies the rules to the line annotation, keeping the entries they leave untouched.\n */\nconst applyToAnnotation = (annotation: runtime.LineAnnotation | undefined, rules: Set<InsertSpacesRule>) => {\n if (!annotation) {\n return annotation\n }\n\n let changed = false\n const romans = annotation.romans.map((item) => {\n const text = insertSpaces(item.text, rules)\n if (text === item.text) {\n return item\n }\n changed = true\n return makeLineAnnotationRoman({ ...item, text })\n })\n const translations = annotation.translations.map((item) => {\n const text = insertSpaces(item.text, rules)\n if (text === item.text) {\n return item\n }\n changed = true\n return makeLineAnnotationTranslation({ ...item, text })\n })\n return changed ? { ...annotation, romans, translations } : annotation\n}\n\n/**\n * Inserts the spaces a source left out, so CJK text set against Latin letters, digits or punctuation reads evenly.\n *\n * A word-level line keeps every word's time and annotation: an inserted space becomes its own space word,\n * and a word the rules split yields one word per character, all sharing the original range.\n * A plain-text line and the annotation tracks are rewritten as whole strings.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const insertSpacesInLines = (lyric: runtime.Lyric, options: InsertSpacesOptions = {}): runtime.Lyric => {\n const rules = resolveRules(options.rules)\n const annotations = options.annotations ?? true\n\n const applyToBackground = (background: runtime.LineBackground): runtime.LineBackground => {\n const content = applyToContent(background.content, rules)\n const annotation = annotations ? applyToAnnotation(background.annotation, rules) : background.annotation\n if (content === background.content && annotation === background.annotation) {\n return background\n }\n return { ...background, content, annotation }\n }\n\n const lines = mapShared(lyric.lines, (line) => {\n if (line.type !== LineType.NORMAL) {\n return line\n }\n\n const content = applyToContent(line.content, rules)\n const annotation = annotations ? applyToAnnotation(line.annotation, rules) : line.annotation\n const backgrounds = mapShared(line.backgrounds, applyToBackground)\n if (content === line.content && annotation === line.annotation && backgrounds === line.backgrounds) {\n return line\n }\n return { ...line, content, annotation, backgrounds }\n })\n\n return lines === lyric.lines ? lyric : { ...lyric, lines }\n}\n\nexport type { InsertSpacesRule } from './constants'\n\nexport { InsertSpacesRules } from './constants'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transforms.js","names":["$schema","version","language","normalization","groups","$schema","version","language","normalization","groups","zhHansRules","enRules","$schema","version","language","normalization","groups","zhHansRules","enRules"],"sources":["../src/transforms/agent/extract.ts","../src/transforms/background/utils.ts","../src/transforms/background/brackets.ts","../src/transforms/background/extract.ts","../rules/credits/en.json","../rules/credits/zh-hans.json","../src/transforms/credit/rules.ts","../src/transforms/credit/extract.ts","../src/transforms/emphasis/index.ts","../rules/filters/en.json","../rules/filters/zh-hans.json","../src/transforms/filter/rules.ts","../src/transforms/filter/remove.ts","../src/transforms/instrumental/index.ts","../src/transforms/space/constants.ts","../src/transforms/space/rules.ts","../src/transforms/space/insert.ts"],"sourcesContent":["import type { runtime } from '@gondola-lyrics/model'\n\nimport {\n AgentType,\n LineType,\n Timing,\n deriveLanguageUsages,\n getContentText,\n isWordLevelLine,\n makeAgent,\n makeLineContentText,\n makeLineContentWords,\n makeText,\n} from '@gondola-lyrics/model/runtime'\n\nexport interface ExtractAgentsOptions {\n /**\n * Maps complete, trimmed prefixes to existing agent ids, preserving array order for joint performers.\n * Matching is case-sensitive and preserves internal whitespace; an empty object disables automatic inference.\n * Omitting this option enables legacy name inference; explicit bindings override its clock exclusion.\n */\n bindings?: Readonly<Record<string, string | readonly string[]>>\n /**\n * Removes matched prefixes and drops lines left with no sung text, including their attached content.\n *\n * @default true\n */\n removePrefixes?: boolean\n}\n\nconst hashName = (name: string): string => {\n let hash = 0\n for (let index = 0; index < name.length; index += 1) {\n hash = ((hash << 5) - hash + name.charCodeAt(index)) | 0\n }\n return (hash >>> 0).toString(16).padStart(8, '0').toUpperCase()\n}\n\nconst haveSameAgents = (first: readonly string[], second: readonly string[]): boolean => {\n return first.length === second.length && first.every((id, index) => id === second[index])\n}\n\nconst prepareBindings = (\n bindings: ExtractAgentsOptions['bindings'],\n agents: readonly runtime.Agent[],\n): Map<string, readonly string[]> | undefined => {\n if (!bindings) {\n return undefined\n }\n const knownIds = new Set(agents.map((agent) => agent.id))\n return new Map(\n Object.entries(bindings).map(([prefix, target]) => {\n const ids = [...new Set(typeof target === 'string' ? [target] : target)]\n const missingId = ids.find((id) => !knownIds.has(id))\n if (missingId !== undefined) {\n throw new TypeError(`binding for ${JSON.stringify(prefix)} references unknown agent id ${JSON.stringify(missingId)}`)\n }\n return [prefix.trim(), ids] as const\n }),\n )\n}\n\n/**\n * Retains the legacy clock exclusion, including its accepted hour and minute bounds.\n */\nconst isClockTimeColon = (before: string, after: string): boolean => {\n const hour = /(\\d+)$/.exec(before)?.[1]\n const minute = /^(\\d+)/.exec(after)?.[1]\n if (!hour || !minute) {\n return false\n }\n return hour.length <= 2 && Number(hour) <= 24 && minute.length <= 2 && Number(minute) <= 60\n}\n\n/**\n * Cuts only source text, so retained word ranges and annotations do not need fabricated character timings.\n */\nconst removePrefix = (content: runtime.LineContent | undefined, length: number): runtime.LineContent | undefined => {\n if (!content) {\n return undefined\n }\n if (content.text !== undefined) {\n const text = content.text.slice(length).trim()\n return text ? makeLineContentText(text) : undefined\n }\n\n const words: runtime.Word[] = []\n let remaining = length\n for (const word of content.words) {\n if (remaining >= word.text.length) {\n remaining -= word.text.length\n continue\n }\n if (remaining > 0) {\n const text = word.text.slice(remaining).trim()\n remaining = 0\n if (text) {\n words.push({ ...word, text })\n }\n } else {\n words.push(word)\n }\n }\n const hasText = words.some((word) => !!word.text.trim())\n return hasText ? makeLineContentWords(words) : undefined\n}\n\n/**\n * Extracts colon-prefixed performers and carries them across nonempty normal lines, replacing their existing agent lists.\n * Missing bindings enables legacy name inference; supplied bindings accept exact prefixes and reset propagation on unmatched prefixes.\n * Removing prefixes preserves retained line and word times, but drops prefix-only lines with their attached content.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n *\n * @throws {TypeError} If bindings reference an agent id absent from lyric.agents.\n */\nexport const extractAgents = (lyric: runtime.Lyric, options: ExtractAgentsOptions = {}): runtime.Lyric => {\n const bindings = prepareBindings(options.bindings, lyric.agents)\n if (bindings?.size === 0 || !lyric.lines.length) {\n return lyric\n }\n\n const removePrefixes = options.removePrefixes ?? true\n const usedIds = new Set(lyric.agents.map((agent) => agent.id))\n const names = new Map<string, string | null>()\n for (const agent of lyric.agents) {\n if (!agent.id) {\n continue\n }\n for (const item of agent.names) {\n const name = item.text.trim()\n if (!name) {\n continue\n }\n if (!names.has(name)) {\n names.set(name, agent.id)\n } else if (names.get(name) !== agent.id) {\n names.set(name, null)\n }\n }\n }\n\n let agents = lyric.agents\n const resolveName = (name: string): readonly string[] => {\n const existing = names.get(name)\n if (existing) {\n return [existing]\n }\n const base = hashName(name)\n let id = base\n for (let suffix = 1; usedIds.has(id); suffix += 1) {\n id = `${base}-${suffix}`\n }\n const agent = makeAgent({ id, type: AgentType.PERSON, names: [makeText({ text: name })] })\n if (agents === lyric.agents) {\n agents = [...agents]\n }\n agents.push(agent)\n usedIds.add(id)\n names.set(name, id)\n return [id]\n }\n\n const originalLines = lyric.lines\n let lines: runtime.Line[] | undefined\n let current: readonly string[] | undefined\n let contentChanged = false\n for (const [index, line] of originalLines.entries()) {\n const text = line.type === LineType.NORMAL ? getContentText(line.content) : ''\n if (line.type !== LineType.NORMAL || !text.trim()) {\n current = undefined\n lines?.push(line)\n continue\n }\n\n const colon = text.search(/[::]/u)\n let matched = false\n if (colon !== -1) {\n const name = text.slice(0, colon).trim()\n if (bindings) {\n const target = bindings.get(name)\n if (!target) {\n current = undefined\n lines?.push(line)\n continue\n }\n current = target\n matched = true\n } else if (!isClockTimeColon(text.slice(0, colon), text.slice(colon + 1))) {\n if (!name) {\n current = undefined\n lines?.push(line)\n continue\n }\n current = resolveName(name)\n matched = true\n }\n }\n\n let content = line.content\n if (matched && removePrefixes) {\n content = removePrefix(content, colon + 1)\n contentChanged = true\n if (!content) {\n lines ??= originalLines.slice(0, index)\n continue\n }\n }\n\n const nextAgents = current && !haveSameAgents(line.agents, current) ? [...current] : line.agents\n if (content !== line.content || nextAgents !== line.agents) {\n lines ??= originalLines.slice(0, index)\n lines.push({ ...line, content, agents: nextAgents })\n } else {\n lines?.push(line)\n }\n }\n\n if (!lines && agents === lyric.agents) {\n return lyric\n }\n\n const remainingLines = lines ?? originalLines\n let timing = lyric.timing\n if (contentChanged && timing === Timing.WORD) {\n const hasWordLevelContent = remainingLines.some((line) => {\n return isWordLevelLine(line) || line.backgrounds.some(isWordLevelLine)\n })\n if (!hasWordLevelContent) {\n timing = Timing.LINE\n }\n }\n return { ...lyric, agents, lines: remainingLines, timing, languages: contentChanged ? deriveLanguageUsages(remainingLines) : lyric.languages }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { WordType, makeTimeRange } from '@gondola-lyrics/model/runtime'\n\nconst OPEN_BRACKETS = new Set(['(', '('])\nconst CLOSE_BRACKETS = new Set([')', ')'])\n\nexport const isOpenBracket = (char: string) => {\n return OPEN_BRACKETS.has(char)\n}\n\nexport const isCloseBracket = (char: string) => {\n return CLOSE_BRACKETS.has(char)\n}\n\nexport const isNormalWord = (word: runtime.Word) => {\n return word.type === WordType.NORMAL\n}\n\n/**\n * Content carries plain text or words, never both, so exactly one of the two is read.\n */\nexport const readLineText = (line: runtime.Line) => {\n return line.content?.text ?? (line.content?.words ?? []).map((word) => word.text).join('')\n}\n\nexport const readTimeRange = (line: runtime.Line) => {\n return line.time ? makeTimeRange({ start: line.time.start, end: line.time.end }) : undefined\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { WordType, makeLineContentText, makeLineContentWords } from '@gondola-lyrics/model/runtime'\n\nimport { mapShared } from '@root/utils'\n\nimport { isCloseBracket, isNormalWord, isOpenBracket } from './utils'\n\n/**\n * Removes one leading bracket from the first sung word and one trailing bracket from the last, so an inner pair stays as written.\n *\n * Space words at the same edge go with the bracket, and a word left with no text goes too, since a sung word must carry something.\n */\nconst stripWordEdges = (words: runtime.Word[]): runtime.Word[] => {\n const firstIndex = words.findIndex(isNormalWord)\n const first = words[firstIndex]\n const last = words[words.findLastIndex(isNormalWord)]\n const stripStart = first ? isOpenBracket(first.text.charAt(0)) : false\n const stripEnd = last ? isCloseBracket(last.text.charAt(last.text.length - 1)) : false\n if (!stripStart && !stripEnd) {\n return words\n }\n\n const result = [...words]\n if (stripStart && first) {\n const text = first.text.slice(1)\n if (text) {\n result[firstIndex] = { ...first, text }\n } else {\n result.splice(firstIndex, 1)\n }\n while (result[0]?.type === WordType.SPACE) {\n result.shift()\n }\n }\n\n // The end is resolved again, since stripping the start may have removed the word it referred to.\n const endIndex = result.findLastIndex(isNormalWord)\n const end = result[endIndex]\n if (stripEnd && end) {\n const text = end.text.slice(0, -1)\n if (text) {\n result[endIndex] = { ...end, text }\n } else {\n result.splice(endIndex, 1)\n }\n while (result.at(-1)?.type === WordType.SPACE) {\n result.pop()\n }\n }\n\n return result\n}\n\n/**\n * Plain text is read literally, so a source that padded it with spaces keeps them; only word content\n * treats space words as the structural separators they are.\n */\nconst stripContentBrackets = (content: runtime.LineContent | undefined): runtime.LineContent | undefined => {\n if (!content) {\n return content\n }\n\n if (!content.words.length) {\n const text = content.text\n if (!text) {\n return content\n }\n const start = isOpenBracket(text.charAt(0)) ? 1 : 0\n const end = isCloseBracket(text.charAt(text.length - 1)) ? -1 : 0\n if (!start && !end) {\n return content\n }\n const stripped = text.slice(start, end === 0 ? undefined : end)\n return stripped ? makeLineContentText(stripped) : undefined\n }\n\n const words = stripWordEdges(content.words)\n if (words === content.words) {\n return content\n }\n return words.length ? makeLineContentWords(words) : undefined\n}\n\nconst stripLineBackgrounds = (line: runtime.Line): runtime.Line => {\n if (!line.backgrounds.length) {\n return line\n }\n\n let changed = false\n const kept: runtime.LineBackground[] = []\n for (const background of line.backgrounds) {\n const content = stripContentBrackets(background.content)\n if (content === background.content) {\n kept.push(background)\n continue\n }\n changed = true\n if (content) {\n kept.push({ ...background, content })\n }\n }\n return changed ? { ...line, backgrounds: kept } : line\n}\n\n/**\n * Removes the brackets some sources wrap around background vocals.\n *\n * One layer is removed from each edge of every background line, leaving a nested pair as `(x)`.\n * A line left with no content is dropped, since a background line must carry something.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const stripBackgroundBrackets = (lyric: runtime.Lyric): runtime.Lyric => {\n const lines = mapShared(lyric.lines, stripLineBackgrounds)\n return lines === lyric.lines ? lyric : { ...lyric, lines }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport {\n LineType,\n Timing,\n deriveLanguageUsages,\n makeLineAnnotation,\n makeLineAnnotationRoman,\n makeLineAnnotationTranslation,\n makeLineBackground,\n makeLineContentText,\n makeLineContentWords,\n makeTimeRange,\n} from '@gondola-lyrics/model/runtime'\n\nimport { isCloseBracket, isNormalWord, isOpenBracket, readLineText, readTimeRange } from './utils'\n\nexport interface ExtractBackgroundLinesOptions {\n /**\n * Turn a line holding nothing but brackets into a background line of the line before it.\n *\n * @default true\n */\n fullLine?: boolean\n /**\n * Pull the bracketed runs inside a line out into background lines of that same line.\n *\n * @default true\n */\n inLine?: boolean\n /**\n * Join a bracket opened on one line and closed on a later one, attaching it to the line before them.\n *\n * @default true\n */\n crossLine?: boolean\n}\n\ninterface TextRun {\n text: string\n start: number\n end: number\n}\n\n/**\n * An unclosed bracket never became a run, so its text returns to the line it came from.\n * An empty pair yields no run, since a background line must carry text.\n */\nconst splitText = (text: string) => {\n const chars = [...text]\n let main = ''\n let current = ''\n let opened = ''\n let openedAt = 0\n let depth = 0\n const runs: TextRun[] = []\n\n for (const [index, char] of chars.entries()) {\n if (isOpenBracket(char)) {\n if (depth) {\n current += char\n } else {\n opened = char\n openedAt = index\n }\n depth += 1\n } else if (isCloseBracket(char)) {\n if (depth === 0) {\n main += char\n continue\n }\n depth -= 1\n if (depth) {\n current += char\n } else {\n runs.push({ text: current, start: openedAt + 1, end: index })\n current = ''\n }\n } else if (depth) {\n current += char\n } else {\n main += char\n }\n }\n\n if (depth) {\n main += opened + current\n }\n // A removed run leaves the separators that framed it behind, so they collapse rather than double.\n return {\n main: main.trim().replaceAll(/\\s{2,}/gu, ' '),\n runs: runs.filter((run) => {\n return !!run.text.trim()\n }),\n length: chars.length,\n }\n}\n/**\n * A word straddling a bracket is cut into pieces sharing its time, since the source gave no finer boundary.\n * An unclosed bracket never became a run, so its text returns to the line it came from.\n * An empty pair yields no run, since a background line must carry text.\n */\nconst splitWords = (words: runtime.Word[]) => {\n const main: runtime.Word[] = []\n const runs: runtime.Word[][] = []\n let current: runtime.Word[] = []\n let opened: runtime.Word | undefined\n let depth = 0\n\n for (const word of words) {\n if (!isNormalWord(word)) {\n const target = depth ? current : main\n target.push(word)\n continue\n }\n\n let buffer = ''\n for (const char of word.text) {\n if (isOpenBracket(char)) {\n if (depth) {\n buffer += char\n } else {\n if (buffer) {\n main.push({ ...word, text: buffer })\n buffer = ''\n }\n opened = { ...word, text: char }\n }\n depth += 1\n } else if (isCloseBracket(char)) {\n if (depth === 0) {\n buffer += char\n continue\n }\n depth -= 1\n if (depth) {\n buffer += char\n } else {\n if (buffer) {\n current.push({ ...word, text: buffer })\n buffer = ''\n }\n if (current.length) {\n runs.push(current)\n current = []\n }\n }\n } else {\n buffer += char\n }\n }\n\n if (buffer) {\n const target = depth ? current : main\n target.push({ ...word, text: buffer })\n }\n }\n\n if (depth) {\n if (opened) {\n main.push(opened)\n }\n main.push(...current)\n }\n return {\n main,\n runs: runs.filter((run) => {\n return run.some((word) => {\n return !!word.text.trim()\n })\n }),\n }\n}\n\nconst mapSpan = (range: runtime.TimeRange, length: number, run: TextRun) => {\n if (range.end === undefined || !length) {\n return makeTimeRange({ start: range.start, end: range.end })\n }\n const span = range.end - range.start\n const start = range.start + Math.round((span * run.start) / length)\n const end = range.start + Math.round((span * run.end) / length)\n return makeTimeRange({ start, end: Math.max(start, end) })\n}\n\n/**\n * Falls back to the line's own range when no word carries a time, so a run still gets a range.\n */\nconst readWordRange = (words: runtime.Word[], fallback: runtime.TimeRange) => {\n const timed = words.filter((word) => {\n return isNormalWord(word) && !!word.time\n })\n const first = timed[0]?.time\n const last = timed.at(-1)?.time\n if (!first || !last) {\n return fallback\n }\n return makeTimeRange({ start: first.start, end: last.end })\n}\n\n/**\n * A background may start before its line but not finish after it, so the line grows to the furthest stated end.\n */\nconst attach = (line: runtime.Line, backgrounds: runtime.LineBackground[]) => {\n const merged = [...line.backgrounds, ...backgrounds]\n merged.sort((left, right) => {\n const leftStart = left.time?.start ?? 0\n const rightStart = right.time?.start ?? 0\n return leftStart === rightStart ? (left.time?.end ?? 0) - (right.time?.end ?? 0) : leftStart - rightStart\n })\n\n let end = line.time?.end\n for (const background of merged) {\n const backgroundEnd = background.time?.end\n if (backgroundEnd !== undefined && (end === undefined || backgroundEnd > end)) {\n end = backgroundEnd\n }\n }\n if (!line.time || end === line.time.end) {\n return { ...line, backgrounds: merged }\n }\n return { ...line, time: makeTimeRange({ start: line.time.start, end }), backgrounds: merged }\n}\n\n/**\n * A closed line cannot hold an open background, so it stops where the line does.\n * On the last line the timeline itself ends unknown, so an open background leaves the line open too.\n */\nconst boundOpenBackgrounds = (lines: runtime.Line[]) => {\n const last = lines.length - 1\n return lines.map((line, index) => {\n const open = line.backgrounds.filter((background) => {\n return !!background.time && background.time.end === undefined\n })\n if (!open.length) {\n return line\n }\n if (index === last) {\n return line.time ? { ...line, time: makeTimeRange({ start: line.time.start }) } : line\n }\n if (!line.time || line.time.end === undefined) {\n return line\n }\n const end = line.time.end\n return {\n ...line,\n backgrounds: line.backgrounds.map((background) => {\n const time = background.time\n if (!time || time.end !== undefined) {\n return background\n }\n return { ...background, time: makeTimeRange({ start: time.start, end: Math.max(time.start, end) }) }\n }),\n }\n })\n}\n\n/**\n * Words are kept only where the caller says the lyric is word-timed, since the other timings forbid them.\n */\nconst makeBackground = (line: runtime.Line, words: runtime.Word[], text: string, time: runtime.TimeRange | undefined, wordLevel: boolean) => {\n return makeLineBackground({\n time,\n agents: [...line.agents],\n languages: [...line.languages],\n content: wordLevel && words.length ? makeLineContentWords(words) : makeLineContentText(text),\n })\n}\n\nconst readBackgrounds = (line: runtime.Line, wordLevel: boolean) => {\n const content = line.content\n if (!content) {\n return []\n }\n\n const range = line.time\n if (wordLevel && content.words.length) {\n return splitWords(content.words).runs.map((words) => {\n const time = range ? readWordRange(words, range) : undefined\n return makeBackground(line, words, words.map((word) => word.text).join(''), time, wordLevel)\n })\n }\n\n const text = content.text ?? content.words.map((word) => word.text).join('')\n if (!text) {\n return []\n }\n const { runs, length } = splitText(text)\n return runs.map((run) => {\n return makeBackground(line, [], run.text, range ? mapSpan(range, length, run) : undefined, false)\n })\n}\n\n/**\n * An entry without brackets stays on the line, since only the bracketed part describes a background.\n * The line's annotation is rebuilt rather than edited, so the input lyric stays untouched.\n */\nconst splitAnnotation = (annotation: runtime.LineAnnotation | undefined, backgrounds: runtime.LineBackground[]) => {\n if (!annotation || !backgrounds.length) {\n return annotation\n }\n\n const move = <Item extends runtime.LineAnnotationRoman | runtime.LineAnnotationTranslation>(\n items: Item[],\n rebuild: (item: Item, text: string) => Item,\n push: (background: runtime.LineBackground, text: string, language: string | undefined) => void,\n ) => {\n return items.map((item) => {\n const { main, runs } = splitText(item.text)\n if (!runs.length) {\n return item\n }\n for (const [index, run] of runs.entries()) {\n const background = backgrounds[index]\n if (background) {\n push(background, run.text, item.language)\n }\n }\n return rebuild(item, main)\n })\n }\n\n const romans = move(\n annotation.romans,\n (item, text) => makeLineAnnotationRoman({ ...item, text }),\n (background, text, language) => {\n background.annotation ??= makeLineAnnotation()\n background.annotation.romans.push(makeLineAnnotationRoman({ text, language }))\n },\n )\n const translations = move(\n annotation.translations,\n (item, text) => makeLineAnnotationTranslation({ ...item, text }),\n (background, text, language) => {\n background.annotation ??= makeLineAnnotation()\n background.annotation.translations.push(makeLineAnnotationTranslation({ text, language }))\n },\n )\n return makeLineAnnotation({ ...annotation, romans, translations })\n}\n\n/**\n * Words survive only where the lyric is word-timed, since the other timings forbid them.\n */\nconst toBackground = (line: runtime.Line, time: runtime.TimeRange | undefined, wordLevel: boolean) => {\n const content = line.content\n if (!content) {\n return undefined\n }\n const text = content.text ?? content.words.map((word) => word.text).join('')\n if (!text) {\n return undefined\n }\n return makeLineBackground({\n time,\n agents: [...line.agents],\n languages: [...line.languages],\n content: wordLevel && content.words.length ? content : makeLineContentText(text),\n annotation: line.annotation,\n })\n}\n\nconst isBracketOnlyLine = (line: runtime.Line | undefined) => {\n if (!line || line.type !== LineType.NORMAL) {\n return false\n }\n const { main, runs } = splitText(readLineText(line))\n return !!runs.length && !main\n}\n\n/**\n * A line that held nothing but brackets has no text of its own to sing, so it cannot be the one a background accompanies.\n */\nconst findParentIndex = (lines: runtime.Line[]) => {\n for (let index = lines.length - 1; index >= 0; index--) {\n const line = lines[index]\n if (line?.type === LineType.NORMAL && !isBracketOnlyLine(line)) {\n return index\n }\n }\n return -1\n}\n\n/**\n * Both source lines go, since neither holds anything left to sing, and the joined range spans both.\n */\nconst joinCrossLine = (lines: runtime.Line[], wordLevel: boolean) => {\n const result: runtime.Line[] = []\n let changed = false\n let skip = false\n\n for (const [index, line] of lines.entries()) {\n if (skip) {\n skip = false\n continue\n }\n\n const next = lines[index + 1]\n const parentIndex = findParentIndex(result)\n if (line.type === LineType.NORMAL && next?.type === LineType.NORMAL && parentIndex !== -1) {\n const opened = splitText(readLineText(line))\n const closed = splitText(readLineText(next))\n const opens = !opened.runs.length && isOpenBracket(opened.main.charAt(0))\n const closes = !closed.runs.length && isCloseBracket(closed.main.charAt(closed.main.length - 1))\n if (opens && closes) {\n const start = line.time?.start\n const end = next.time?.end ?? next.time?.start\n const background = toBackground(\n { ...line, content: makeLineContentText(opened.main.slice(1) + closed.main.slice(0, -1)) },\n start === undefined ? undefined : makeTimeRange({ start, end }),\n wordLevel,\n )\n if (background) {\n result[parentIndex] = attach(result[parentIndex]!, [background])\n changed = true\n skip = true\n continue\n }\n }\n }\n result.push(line)\n }\n\n return { lines: result, changed }\n}\n\nconst stripRuns = (content: runtime.LineContent): runtime.LineContent | undefined => {\n if (content.words.length) {\n const { main } = splitWords(content.words)\n return main.length ? makeLineContentWords(main) : undefined\n }\n const { main } = splitText(content.text ?? '')\n return main ? makeLineContentText(main) : undefined\n}\n\n/**\n * Pulls bracketed background vocals out of the sung lines and attaches them to the line they accompany.\n *\n * A line holding nothing but brackets becomes a background of the line before it and is removed.\n * A bracket opened on one line and closed on a later one joins the line before the opening, and both source lines go.\n * Text outside brackets stays on its line, and a line left with none keeps its place.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const extractBackgroundLines = (lyric: runtime.Lyric, options: ExtractBackgroundLinesOptions = {}): runtime.Lyric => {\n const fullLine = options.fullLine ?? true\n const inLine = options.inLine ?? true\n const crossLine = options.crossLine ?? true\n if (!lyric.lines.length) {\n return lyric\n }\n\n const wordLevel = lyric.timing === Timing.WORD\n const joined = crossLine ? joinCrossLine(lyric.lines, wordLevel) : { lines: lyric.lines, changed: false }\n const result: runtime.Line[] = []\n let changed = joined.changed\n\n for (const line of joined.lines) {\n if (line.type !== LineType.NORMAL || !line.content) {\n result.push(line)\n continue\n }\n\n const parentIndex = findParentIndex(result)\n\n if (fullLine && parentIndex !== -1) {\n const { main, runs } = splitText(readLineText(line))\n if (runs.length && !main) {\n // Every run becomes a background of its own, so a line of two pairs keeps both.\n const backgrounds = runs.map((run) => {\n return makeBackground(line, [], run.text, readTimeRange(line), false)\n })\n result[parentIndex] = attach(result[parentIndex]!, backgrounds)\n changed = true\n continue\n }\n }\n\n const backgrounds = inLine ? readBackgrounds(line, wordLevel) : []\n if (!backgrounds.length) {\n result.push(line)\n continue\n }\n\n const nextContent = stripRuns(line.content)\n\n // A line left with nothing to sing only leaves when fullLine owns that case, so inLine keeps it here.\n if (!nextContent) {\n result.push(line)\n continue\n }\n\n const extracted: runtime.Line = {\n ...line,\n content: nextContent,\n annotation: splitAnnotation(line.annotation, backgrounds),\n }\n result.push(attach(extracted, backgrounds))\n changed = true\n }\n\n if (!changed) {\n return lyric\n }\n const lines = boundOpenBackgrounds(result)\n return { ...lyric, lines, languages: deriveLanguageUsages(lines) }\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"en\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"lyricist\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\"],\n \"words\": [\"Lyrics\", \"Lyricsby\", \"Lyricist\", \"Words\", \"Wordsby\", \"Lyric\", \"Lyr\", \"Lyricists\", \"Lyricwriting\"]\n },\n {\n \"id\": \"composer\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\"],\n \"words\": [\n \"Music\",\n \"Musicby\",\n \"Composer\",\n \"Composedby\",\n \"Compose\",\n \"Composition\",\n \"Melody\",\n \"Melodyby\",\n \"Comp\",\n \"Composers\",\n \"Score\",\n \"Scoredby\",\n \"Composed\"\n ]\n },\n {\n \"id\": \"arranger\",\n \"kind\": \"role\",\n \"roles\": [\"ARRANGER\"],\n \"words\": [\n \"Arrangement\",\n \"Arranger\",\n \"Arrangedby\",\n \"Arranged\",\n \"Arrange\",\n \"Arr\",\n \"Orchestration\",\n \"StringArrangement\",\n \"VocalArrangement\",\n \"ChoirArrangement\",\n \"Orchestrator\",\n \"Orchestratedby\",\n \"Orch\",\n \"AdditionalArrangement\",\n \"BGVsArranger\",\n \"BVArranger\",\n \"BackgroundVocalArrangement\",\n \"BackgroundVocalsArrangement\",\n \"BackingVocalsArranger\",\n \"BackingVocalArrangement\",\n \"BackingVocalsArrangement\",\n \"BrassArrangement\",\n \"HornArrangement\",\n \"OrchestralArrangement\",\n \"RhythmArrangement\",\n \"StringArrangedby\",\n \"StringArranger\",\n \"VocalArrangedby\",\n \"VocalArranger\"\n ]\n },\n {\n \"id\": \"producer\",\n \"kind\": \"role\",\n \"roles\": [\"PRODUCER\"],\n \"words\": [\n \"Producer\",\n \"Producedby\",\n \"Produced\",\n \"Production\",\n \"Prod\",\n \"Prodby\",\n \"Producers\",\n \"Produce\",\n \"CoProducer\",\n \"Co-Producer\",\n \"ExecutiveProducer\",\n \"AdditionalProducer\",\n \"AdditionalProduction\",\n \"VocalProducer\",\n \"MusicProducer\",\n \"AssociateProducer\",\n \"Co-Produced\",\n \"Co-Producedby\",\n \"Co-Production\",\n \"Co-Productionby\",\n \"CoProd\",\n \"Coproduction\",\n \"CreativeProducer\",\n \"ExProd\",\n \"ExecProducer\",\n \"ExecutiveProducers\",\n \"Productionby\",\n \"VocalProduction\",\n \"MusicSupervisor\",\n \"Supervisor\",\n \"Presentedby\",\n \"Presenter\"\n ]\n },\n {\n \"id\": \"performer\",\n \"kind\": \"role\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"Vocals\",\n \"Vocal\",\n \"Vocalist\",\n \"Singer\",\n \"Voice\",\n \"Performer\",\n \"Featuring\",\n \"Feat\",\n \"Ft\",\n \"Artist\",\n \"Vocalsby\",\n \"Sungby\",\n \"Performedby\",\n \"Performed\",\n \"LeadVocal\",\n \"LeadVocals\",\n \"BackgroundVocals\",\n \"BackingVocals\",\n \"Harmony\",\n \"Choir\",\n \"Band\",\n \"Rap\",\n \"Rapper\",\n \"Soloist\",\n \"Musician\",\n \"Player\",\n \"GuestVocals\",\n \"FeaturedArtist\",\n \"Narrator\",\n \"Guitarist\",\n \"Bassist\",\n \"Pianist\",\n \"Drummer\",\n \"Keyboardist\",\n \"Violinist\",\n \"Cellist\",\n \"Accompaniment\",\n \"Accompanist\",\n \"Accordionist\",\n \"Ad-lib\",\n \"Ad-libs\",\n \"AdditionalVocals\",\n \"Adlib\",\n \"Adlibs\",\n \"Allvocals\",\n \"Allvocalsby\",\n \"BGV\",\n \"BGVs\",\n \"BGs\",\n \"BV\",\n \"BackgroundVocal\",\n \"BackingVocal\",\n \"Banjoist\",\n \"Beatbox\",\n \"Beatboxer\",\n \"Bvox\",\n \"CV\",\n \"ChildrenVoice\",\n \"Clarinetist\",\n \"Contrabassist\",\n \"CoverSong\",\n \"Dialogue\",\n \"Ensemble\",\n \"FemaleVoice\",\n \"Flutist\",\n \"GuestArtist\",\n \"GuestSinger\",\n \"Harmonies\",\n \"Harpist\",\n \"Hornist\",\n \"HumanBeatbox\",\n \"HumanVoice\",\n \"LeadSinger\",\n \"LeadVocalist\",\n \"LiveBand\",\n \"MainVocal\",\n \"MainVocals\",\n \"MaleVoice\",\n \"Mandolinist\",\n \"Narration\",\n \"Orchestra\",\n \"Organist\",\n \"OriginalSinger\",\n \"Percussionist\",\n \"Recitation\",\n \"Saxophonist\",\n \"SessionMusician\",\n \"SessionPlayer\",\n \"Singers\",\n \"Singing\",\n \"Sitarist\",\n \"Trombonist\",\n \"Trumpeter\",\n \"Tubist\",\n \"Ukuleleist\",\n \"Violist\",\n \"VocalPerformance\",\n \"Vocalists\",\n \"VoiceActor\",\n \"VoiceActress\",\n \"Vox\",\n \"Chorus\",\n \"Solo\"\n ]\n },\n {\n \"id\": \"instruments\",\n \"kind\": \"instrument\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"Guitar\",\n \"Piano\",\n \"Bass\",\n \"Drums\",\n \"Keyboard\",\n \"Strings\",\n \"Synth\",\n \"Violin\",\n \"Cello\",\n \"Saxophone\",\n \"Trumpet\",\n \"Flute\",\n \"Clarinet\",\n \"Trombone\",\n \"Horn\",\n \"Organ\",\n \"Percussion\",\n \"Ukulele\",\n \"Banjo\",\n \"Mandolin\",\n \"Harp\",\n \"Viola\",\n \"AcousticGuitar\",\n \"ElectricGuitar\",\n \"BassGuitar\",\n \"ElectricBass\",\n \"Keyboards\",\n \"Keys\",\n \"Synthesizer\",\n \"Perc\",\n \"12-StringGuitar\",\n \"A.Gtr\",\n \"A.Sax\",\n \"Accordion\",\n \"AcousticBass\",\n \"AcousticElectricGuitar\",\n \"Allinstrumental\",\n \"Allinstrumentalby\",\n \"Allinstruments\",\n \"AltoSax\",\n \"AltoSaxophone\",\n \"AppalachianDulcimer\",\n \"Autoharp\",\n \"B.Cl\",\n \"B.Sax\",\n \"B3\",\n \"BANDONEÓN\",\n \"BODHRÁN\",\n \"Bagpipe\",\n \"Bagpipes\",\n \"Balalaika\",\n \"Bandoneon\",\n \"Bandoneón\",\n \"Banhu\",\n \"Bansuri\",\n \"BaritoneSax\",\n \"BaritoneSaxophone\",\n \"BassClarinet\",\n \"BassSynth\",\n \"Bassoon\",\n \"Bawu\",\n \"Bodhran\",\n \"Bodhrán\",\n \"Bongo\",\n \"Bouzouki\",\n \"Brass\",\n \"Bsn\",\n \"Bss\",\n \"CAJÓN\",\n \"Cajon\",\n \"Cajón\",\n \"Castanets\",\n \"Cb\",\n \"Celesta\",\n \"Charango\",\n \"Chimes\",\n \"Cl\",\n \"ClassicalGuitar\",\n \"Clav\",\n \"Claves\",\n \"Clavinet\",\n \"Cnt\",\n \"Concertina\",\n \"Conga\",\n \"Contrabass\",\n \"Cornet\",\n \"Cowbell\",\n \"Cymbal\",\n \"Cymbals\",\n \"DJ\",\n \"Daegeum\",\n \"Darbuka\",\n \"Daruan\",\n \"Didgeridoo\",\n \"Dizi\",\n \"Djembe\",\n \"Dobro\",\n \"DoubleBass\",\n \"Doumbek\",\n \"Dr\",\n \"Drum\",\n \"DrumMachine\",\n \"DrumPad\",\n \"DrumSet\",\n \"Duduk\",\n \"Dulcimer\",\n \"E.Bss\",\n \"E.Gtr\",\n \"E.Hn\",\n \"EP\",\n \"ElectricPiano\",\n \"ElectronicKeyboard\",\n \"EnglishHorn\",\n \"Erhu\",\n \"Euph\",\n \"Euphonium\",\n \"FingerDrumming\",\n \"Fingerpicking\",\n \"FingerstyleGuitar\",\n \"Fl\",\n \"Flageolet\",\n \"Flt\",\n \"Flugelhorn\",\n \"FrameDrum\",\n \"FrenchHorn\",\n \"Gaohu\",\n \"Gayageum\",\n \"Glockenspiel\",\n \"Gong\",\n \"Gtr\",\n \"Guiro\",\n \"Guitars\",\n \"Guqin\",\n \"Guzheng\",\n \"GÜIRO\",\n \"Güiro\",\n \"Haegeum\",\n \"HammeredDulcimer\",\n \"Hammond\",\n \"HammondOrgan\",\n \"HandDrum\",\n \"Handpan\",\n \"HangDrum\",\n \"Harmonica\",\n \"Harmonium\",\n \"Harpsichord\",\n \"Hn\",\n \"HornSection\",\n \"Horns\",\n \"Hpschd\",\n \"Hrp\",\n \"Hulusi\",\n \"Hurdy-Gurdy\",\n \"HurdyGurdy\",\n \"Janggu\",\n \"Jinghu\",\n \"Kalimba\",\n \"Keytar\",\n \"Konghou\",\n \"Kora\",\n \"Koto\",\n \"Kybd\",\n \"LapSteel\",\n \"LapSteelGuitar\",\n \"LeadGuitar\",\n \"Liuqin\",\n \"Lute\",\n \"MPC\",\n \"Marimba\",\n \"Matouqin\",\n \"Mbira\",\n \"Mellotron\",\n \"Melodica\",\n \"MorinKhuur\",\n \"MusicalInstruments\",\n \"Nyckelharpa\",\n \"NylonGuitar\",\n \"Ob\",\n \"Oboe\",\n \"Ocarina\",\n \"Org\",\n \"Oud\",\n \"PanFlute\",\n \"Panpipes\",\n \"PedalSteel\",\n \"PedalSteelGuitar\",\n \"PennyWhistle\",\n \"Picc\",\n \"Piccolo\",\n \"Pipa\",\n \"Pno\",\n \"Rebab\",\n \"Recorder\",\n \"Resonator\",\n \"ResonatorGuitar\",\n \"Rhodes\",\n \"RhythmGuitar\",\n \"Ruan\",\n \"S.Sax\",\n \"Sampler\",\n \"Sanxian\",\n \"Sax\",\n \"Scratching\",\n \"Sequencer\",\n \"Shaker\",\n \"Shakuhachi\",\n \"Shamisen\",\n \"Sheng\",\n \"Sitar\",\n \"SlideGuitar\",\n \"Snare\",\n \"SnareDrum\",\n \"SopranoSax\",\n \"SteelDrum\",\n \"SteelGuitar\",\n \"SteelPan\",\n \"Str\",\n \"String\",\n \"StringQuartet\",\n \"StringSection\",\n \"SubBass\",\n \"Suona\",\n \"SynthBass\",\n \"Synthesizers\",\n \"Synths\",\n \"T.Sax\",\n \"Tabla\",\n \"Taiko\",\n \"TalkingDrum\",\n \"Tamb\",\n \"Tambourine\",\n \"Tba\",\n \"Tbn\",\n \"TenorSax\",\n \"TenorSaxophone\",\n \"Theremin\",\n \"Timbales\",\n \"Timp\",\n \"Timpani\",\n \"TinWhistle\",\n \"Tpt\",\n \"Triangle\",\n \"Tuba\",\n \"TubularBells\",\n \"Turntable\",\n \"Turntables\",\n \"Uke\",\n \"UprightBass\",\n \"Vc\",\n \"Vib\",\n \"Vibes\",\n \"Vibraphone\",\n \"Vla\",\n \"Vln\",\n \"Vocoder\",\n \"Whistle\",\n \"Winds\",\n \"Woodblock\",\n \"Woodwinds\",\n \"Wurlitzer\",\n \"Xiao\",\n \"Xiaoruan\",\n \"Xun\",\n \"Xylo\",\n \"Xylophone\",\n \"Yangqin\",\n \"Yueqin\",\n \"Zhonghu\",\n \"Zither\"\n ]\n },\n {\n \"id\": \"mixer\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\"],\n \"words\": [\n \"Mix\",\n \"Mixedby\",\n \"Mixing\",\n \"Mixer\",\n \"MixingEngineer\",\n \"MixEngineer\",\n \"Remix\",\n \"Remixedby\",\n \"Remixer\",\n \"Mixdown\",\n \"Mixdownby\",\n \"Mixed\",\n \"Remixby\"\n ]\n },\n {\n \"id\": \"mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MASTERING\"],\n \"words\": [\"Mastering\", \"Masteredby\", \"Master\", \"MasteringEngineer\", \"MasterEngineer\", \"Mst\", \"Mastered\", \"MasteringEngineerby\", \"Masteringby\"]\n },\n {\n \"id\": \"lyrics-and-composition\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\"],\n \"words\": [\n \"Lyrics&Music\",\n \"Music&Lyrics\",\n \"Words&Music\",\n \"Lyrics&Musicby\",\n \"Music&Lyricsby\",\n \"Words&Musicby\",\n \"Composed&Writtenby\",\n \"ComposedandWrittenby\",\n \"Composer&Lyricist\",\n \"Composer/Lyricist\",\n \"Lyricist&Composer\",\n \"Lyricist/Composer\",\n \"Lyrics&Melody\",\n \"Lyrics/Music\",\n \"LyricsandMusic\",\n \"LyricsandMusicby\",\n \"Melody&Lyrics\",\n \"Melody&Words\",\n \"Music&Words\",\n \"Music/Lyrics\",\n \"MusicandLyrics\",\n \"MusicandLyricsby\",\n \"Words&Melody\",\n \"Words/Music\",\n \"WordsandMusic\",\n \"WordsandMusicby\",\n \"Written&Composedby\",\n \"WrittenandComposedby\"\n ]\n },\n {\n \"id\": \"composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\", \"ARRANGER\"],\n \"words\": [\n \"Composer&Arranger\",\n \"Composer/Arranger\",\n \"Composed&Arrangedby\",\n \"Music&Arrangement\",\n \"Composed&Arranged\",\n \"ComposedandArrangedby\",\n \"ComposerandArranger\",\n \"Composition&Arrangement\",\n \"MusicandArrangement\"\n ]\n },\n {\n \"id\": \"mixing-and-mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\", \"MASTERING\"],\n \"words\": [\n \"Mix&Master\",\n \"Mix/Master\",\n \"Mixed&Masteredby\",\n \"Mixing&Mastering\",\n \"Mixing/Mastering\",\n \"Mix&Mastering\",\n \"MixandMaster\",\n \"MixandMastering\",\n \"MixedandMasteredby\",\n \"Mixing&Master\",\n \"MixingandMaster\",\n \"MixingandMastering\"\n ]\n },\n {\n \"id\": \"lyrics-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"ARRANGER\"],\n \"words\": [\"Lyrics&Arrangement\", \"LyricsArrangement\", \"LyricsandArrangement\", \"Words&Arrangement\", \"WordsArrangement\"]\n },\n {\n \"id\": \"lyrics-composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"ARRANGER\"],\n \"words\": [\n \"ComposedWritten&Arrangedby\",\n \"ComposedWrittenandArrangedby\",\n \"LyricsMusic&Arrangement\",\n \"LyricsMusicArrangement\",\n \"LyricsMusicandArrangement\",\n \"MusicLyrics&Arrangement\",\n \"WordsMusic&Arrangement\",\n \"WrittenComposed&Arrangedby\",\n \"WrittenComposedandArrangedby\"\n ]\n },\n {\n \"id\": \"singer-songwriter\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"PERFORMER\"],\n \"words\": [\"Singer-Songwriter\", \"SingerSongwriter\", \"SingerSongwriterby\"]\n },\n {\n \"id\": \"other\",\n \"kind\": \"role\",\n \"roles\": [\"OTHER\"],\n \"words\": [\n \"Engineer\",\n \"RecordingEngineer\",\n \"SoundEngineer\",\n \"MixAssistant\",\n \"MixingAssistant\",\n \"MasteringAssistant\",\n \"A&R\",\n \"ArtDirection\",\n \"ArtDirector\",\n \"Design\",\n \"Designer\",\n \"Photographer\",\n \"Photography\",\n \"Photo\",\n \"Director\",\n \"Video\",\n \"Manager\",\n \"Management\",\n \"Coordinator\",\n \"Consultant\",\n \"Translator\",\n \"Translation\",\n \"Transcription\",\n \"Editor\",\n \"Edit\",\n \"Programmer\",\n \"Programming\",\n \"Conductor\",\n \"Choreographer\",\n \"A.Eng\",\n \"Acknowledgments\",\n \"AdditionalEngineering\",\n \"Adjustment\",\n \"AlbumArt\",\n \"Animatedby\",\n \"Animation\",\n \"Animator\",\n \"Animatorby\",\n \"ArtConsultant\",\n \"ArtistManagement\",\n \"Artistassistant\",\n \"ArtistsandRepertoire\",\n \"Artwork\",\n \"AssetManagement\",\n \"Assistance\",\n \"Assistant\",\n \"AssistantEngineer\",\n \"AssistantMixEngineer\",\n \"AsstEng\",\n \"AudioEditing\",\n \"AudioEditor\",\n \"AudioEngineer\",\n \"Auto-Tune\",\n \"BTS\",\n \"Beatby\",\n \"Beatmaker\",\n \"Beats\",\n \"Behind-the-Scenes\",\n \"Behind-theScenes\",\n \"Behindthe-Scenes\",\n \"BehindtheScenes\",\n \"CG\",\n \"COO\",\n \"ChiefOperatingOfficer\",\n \"Choreography\",\n \"Clothing\",\n \"ComputerEngineer\",\n \"Cond\",\n \"Conductedby\",\n \"Contractor\",\n \"Copyist\",\n \"Copywriter\",\n \"Copywriting\",\n \"Costume\",\n \"CoverArt\",\n \"CoverDesign\",\n \"CreativeDirection\",\n \"CreativeDirector\",\n \"Designedby\",\n \"Digitaledit\",\n \"Digitalediting\",\n \"Directedby\",\n \"Distributedby\",\n \"DolbyAtmos\",\n \"Drawing\",\n \"DrumProgramming\",\n \"DrumTech\",\n \"Editedby\",\n \"Eng\",\n \"Engineeredby\",\n \"FOHEngineer\",\n \"Fashion\",\n \"Film\",\n \"Filmedby\",\n \"FineArts\",\n \"Flatdesign\",\n \"Foley\",\n \"FoleyArtist\",\n \"FrontofHouse\",\n \"FrontofHouseEngineer\",\n \"GraphicDesign\",\n \"Graphics\",\n \"Graphicvisual\",\n \"GuitarTech\",\n \"Hair\",\n \"Hair-Stylist\",\n \"HairStylist\",\n \"Illustratedby\",\n \"Illustration\",\n \"Illustrator\",\n \"Inscription\",\n \"InstrumentTech\",\n \"Librarian\",\n \"LightTech\",\n \"LightingDesigner\",\n \"LiveSoundEngineer\",\n \"Loops\",\n \"LyricVideo\",\n \"MD\",\n \"MIDI\",\n \"MIDIProgramming\",\n \"MV\",\n \"MVDirector\",\n \"MainCreators\",\n \"Make-up\",\n \"Makeup\",\n \"MakeupArtist\",\n \"Making\",\n \"Making-of\",\n \"Makingof\",\n \"Marketing\",\n \"Marketing&promotion\",\n \"MonitorEngineer\",\n \"MusicConcept\",\n \"MusicDirector\",\n \"MusicVideo\",\n \"MusicVideoDirector\",\n \"Musicproductionassistant\",\n \"OP\",\n \"PGM\",\n \"PRpromotion\",\n \"PV\",\n \"Photoby\",\n \"PitchCorrection\",\n \"PitchEditing\",\n \"Planner\",\n \"Planning\",\n \"Post-production\",\n \"PostMastering\",\n \"PostProd\",\n \"Pre-production\",\n \"Printvisual\",\n \"Productionassistant\",\n \"Prog\",\n \"Program\",\n \"Programmedby\",\n \"ProjectCoordinator\",\n \"ProjectPlanning\",\n \"Promo-Material\",\n \"PromoMaterial\",\n \"Promotion\",\n \"Proofreading\",\n \"Publicity\",\n \"Publishedby\",\n \"Publisher\",\n \"Rec\",\n \"Recordedby\",\n \"Recording\",\n \"RecordingAssistant\",\n \"Recordist\",\n \"SFX\",\n \"SFXDesigner\",\n \"SP\",\n \"Samples\",\n \"Sampling\",\n \"Scoring\",\n \"Short-Form\",\n \"Short-Video\",\n \"ShortForm\",\n \"ShortVideo\",\n \"Songby\",\n \"Songwriter\",\n \"Songwriting\",\n \"SoundDes\",\n \"SoundDesign\",\n \"SoundDesigner\",\n \"SoundEffects\",\n \"SoundEffectsDesigner\",\n \"SoundMixer\",\n \"SpecialThanks\",\n \"SpecialThanksto\",\n \"StageManager\",\n \"Styling\",\n \"Stylist\",\n \"Sub-publisher\",\n \"Subpublisher\",\n \"Subtitles\",\n \"Supervisedby\",\n \"Tech\",\n \"Thanks\",\n \"Thanksto\",\n \"Timing\",\n \"Topliner\",\n \"TrackingEng\",\n \"TrackingEngineer\",\n \"Training\",\n \"Trans\",\n \"Transcriber\",\n \"Translatedby\",\n \"Tunedby\",\n \"Tuning\",\n \"Videoby\",\n \"Visual\",\n \"VisualDesign\",\n \"VocalCoach\",\n \"VocalDirectedby\",\n \"VocalDirection\",\n \"VocalDirector\",\n \"VocalEdit\",\n \"VocalEditing\",\n \"VocalEditor\",\n \"VocalTuning\",\n \"Wardrobe\",\n \"Writer\",\n \"Written\",\n \"Writtenby\",\n \"Art\",\n \"Image\",\n \"Material\",\n \"Marketing-Material\",\n \"MarketingMaterial\",\n \"Copyrightmanagement\",\n \"Copyrightoperation\",\n \"StudioManager\",\n \"Beat\",\n \"Cut\",\n \"Guest\",\n \"Stage\"\n ]\n }\n ]\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"zh-hans\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"lyricist\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\"],\n \"words\": [\n \"作词\",\n \"填词\",\n \"词\",\n \"歌词\",\n \"作词人\",\n \"词作者\",\n \"作词者\",\n \"填词人\",\n \"词作\",\n \"中文词\",\n \"英文词\",\n \"粤语词\",\n \"韩文词\",\n \"日文词\",\n \"RAP词\"\n ]\n },\n {\n \"id\": \"composer\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\"],\n \"words\": [\"作曲\", \"曲\", \"谱曲\", \"作曲人\", \"曲作者\", \"作曲者\"]\n },\n {\n \"id\": \"arranger\",\n \"kind\": \"role\",\n \"roles\": [\"ARRANGER\"],\n \"words\": [\n \"编曲\",\n \"配器\",\n \"和声编写\",\n \"弦乐编写\",\n \"管乐编写\",\n \"节奏编写\",\n \"合唱编写\",\n \"和声编排\",\n \"弦乐编排\",\n \"管弦乐编排\",\n \"编曲人\",\n \"编曲者\",\n \"编曲师\",\n \"和声编配\",\n \"弦乐编配\",\n \"和音编写\"\n ]\n },\n {\n \"id\": \"producer\",\n \"kind\": \"role\",\n \"roles\": [\"PRODUCER\"],\n \"words\": [\n \"制作人\",\n \"制作\",\n \"监制\",\n \"出品人\",\n \"出品\",\n \"音乐制作人\",\n \"执行制作人\",\n \"制作总监\",\n \"总制作人\",\n \"人声制作\",\n \"人声制作人\",\n \"配唱制作人\",\n \"共同制作\",\n \"共同制作人\",\n \"协力制作人\",\n \"制作协力\",\n \"单曲制作\",\n \"创意制作人\",\n \"出品方\",\n \"联合出品\",\n \"联合出品方\",\n \"执制\",\n \"执行监制\",\n \"总监制\",\n \"音乐监制\"\n ]\n },\n {\n \"id\": \"performer\",\n \"kind\": \"role\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"演唱\",\n \"主唱\",\n \"歌手\",\n \"唱\",\n \"和声\",\n \"和音\",\n \"伴唱\",\n \"合唱\",\n \"原唱\",\n \"翻唱\",\n \"配唱\",\n \"女声\",\n \"男声\",\n \"童声\",\n \"人声\",\n \"合声\",\n \"合音\",\n \"领唱\",\n \"独奏\",\n \"演奏\",\n \"演奏者\",\n \"说唱\",\n \"配音\",\n \"旁白\",\n \"念白\",\n \"对白\",\n \"口技\",\n \"人声打击\",\n \"乐队\",\n \"乐团\",\n \"交响乐团\",\n \"管弦乐团\",\n \"弦乐团\",\n \"现场乐队\",\n \"乐手\",\n \"乐师\",\n \"伴奏\",\n \"演唱者\",\n \"演唱人\",\n \"和声者\",\n \"伴唱者\",\n \"领唱者\",\n \"吉他手\",\n \"贝斯手\",\n \"鼓手\",\n \"键盘手\",\n \"钢琴家\",\n \"小提琴手\",\n \"大提琴手\",\n \"中提琴手\",\n \"长笛手\",\n \"萨克斯手\",\n \"小号手\",\n \"长号手\",\n \"圆号手\",\n \"打击乐手\",\n \"单簧管手\",\n \"口琴手\",\n \"合唱团\",\n \"手风琴手\",\n \"竖琴手\",\n \"艺人\"\n ]\n },\n {\n \"id\": \"instruments\",\n \"kind\": \"instrument\",\n \"roles\": [\"PERFORMER\"],\n \"words\": [\n \"吉他\",\n \"钢琴\",\n \"贝斯\",\n \"贝司\",\n \"鼓\",\n \"键盘\",\n \"弦乐\",\n \"小提琴\",\n \"大提琴\",\n \"中提琴\",\n \"长笛\",\n \"萨克斯\",\n \"小号\",\n \"长号\",\n \"圆号\",\n \"单簧管\",\n \"双簧管\",\n \"手风琴\",\n \"口琴\",\n \"二胡\",\n \"古筝\",\n \"琵琶\",\n \"架子鼓\",\n \"电子琴\",\n \"合成器\",\n \"打击乐\",\n \"打击乐器\",\n \"管乐\",\n \"木管\",\n \"铜管\",\n \"三味线\",\n \"三弦\",\n \"三角铁\",\n \"中国笛\",\n \"中提\",\n \"中胡\",\n \"中阮\",\n \"乌德琴\",\n \"乐器\",\n \"乐器独奏\",\n \"京胡\",\n \"伽倻琴\",\n \"低音单簧管\",\n \"低音吉他\",\n \"低音提琴\",\n \"克拉维奈\",\n \"全部乐器\",\n \"六角手风琴\",\n \"军鼓\",\n \"刮壶\",\n \"十二弦吉他\",\n \"卡洪\",\n \"口风琴\",\n \"古琴\",\n \"合成贝斯\",\n \"哈蒙德风琴\",\n \"响板\",\n \"哨笛\",\n \"唢呐\",\n \"埙\",\n \"塔布拉\",\n \"声码器\",\n \"大号\",\n \"大提\",\n \"大笒\",\n \"大管\",\n \"大键琴\",\n \"大阮\",\n \"太鼓\",\n \"奚琴\",\n \"定音鼓\",\n \"富鲁格\",\n \"富鲁格号\",\n \"小提\",\n \"小阮\",\n \"小鼓\",\n \"尤克里里\",\n \"尺八\",\n \"巴乌\",\n \"巴拉莱卡\",\n \"巴松\",\n \"布祖基\",\n \"序列器\",\n \"康加鼓\",\n \"弦\",\n \"德西马\",\n \"恰朗戈\",\n \"所有乐器\",\n \"所有乐器演奏\",\n \"手摇琴\",\n \"手碟\",\n \"手鼓\",\n \"打击垫\",\n \"扬琴\",\n \"拇指琴\",\n \"拉巴卜\",\n \"指弹吉他\",\n \"排笛\",\n \"排箫\",\n \"提琴\",\n \"曼陀林\",\n \"月琴\",\n \"木吉他\",\n \"木棒\",\n \"木琴\",\n \"木箱鼓\",\n \"木鱼\",\n \"杜杜克\",\n \"板胡\",\n \"柳琴\",\n \"框鼓\",\n \"梅洛特隆\",\n \"次中音号\",\n \"沙锤\",\n \"波德兰鼓\",\n \"洞萧\",\n \"滑棒吉他\",\n \"牛铃\",\n \"特雷门琴\",\n \"玉琴\",\n \"班卓琴\",\n \"班多钮\",\n \"班苏里笛\",\n \"瑶琴\",\n \"电吉他\",\n \"电子打击垫\",\n \"电子鼓\",\n \"电贝司\",\n \"电贝斯\",\n \"电钢琴\",\n \"短号\",\n \"短笛\",\n \"竖琴\",\n \"竖笛\",\n \"竹笛\",\n \"笙\",\n \"笛子\",\n \"笛萧\",\n \"筝\",\n \"箜篌\",\n \"管钟\",\n \"箫\",\n \"箱琴\",\n \"簧风琴\",\n \"编钟\",\n \"肩背键盘\",\n \"自动竖琴\",\n \"英国管\",\n \"葫芦丝\",\n \"蒂姆巴尔鼓\",\n \"西塔琴\",\n \"说话鼓\",\n \"谐振吉他\",\n \"踏板钢弦吉他\",\n \"转盘\",\n \"达布卡\",\n \"迪吉里杜管\",\n \"邦戈鼓\",\n \"采样器\",\n \"金贝鼓\",\n \"钟琴\",\n \"钢片琴\",\n \"钢鼓\",\n \"钹\",\n \"铃鼓\",\n \"锡笛\",\n \"锣\",\n \"键提琴\",\n \"镲\",\n \"长鼓\",\n \"阮\",\n \"陶笛\",\n \"非洲鼓\",\n \"颤音琴\",\n \"风琴\",\n \"风笛\",\n \"马头琴\",\n \"马林巴\",\n \"高胡\",\n \"鲁特琴\",\n \"齐特琴\"\n ]\n },\n {\n \"id\": \"mixer\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\"],\n \"words\": [\"混音\", \"混音师\", \"混\", \"混音工程师\", \"缩混\", \"贴混\", \"混音工程\", \"重混音师\"]\n },\n {\n \"id\": \"mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MASTERING\"],\n \"words\": [\"母带\", \"母带工程师\", \"母带处理\", \"母带制作\", \"母带后期\", \"母带工程\", \"母带师\", \"母带制作人\", \"母带处理制作人\"]\n },\n {\n \"id\": \"lyrics-and-composition\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\"],\n \"words\": [\n \"词曲\",\n \"作词/作曲\",\n \"作词作曲\",\n \"词/曲\",\n \"词&曲\",\n \"词曲作者\",\n \"词曲创作\",\n \"作曲/作词\",\n \"作曲、作词\",\n \"作词+作曲\",\n \"作词,作曲\",\n \"作词·作曲\",\n \"作词、作曲\",\n \"作词,作曲\",\n \"词/曲作\",\n \"词、曲\",\n \"词,曲\"\n ]\n },\n {\n \"id\": \"composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"COMPOSER\", \"ARRANGER\"],\n \"words\": [\"作编曲\", \"作曲/编曲\", \"作曲编曲\", \"曲/编\", \"曲编\", \"作曲、编曲\", \"作曲,编曲\"]\n },\n {\n \"id\": \"mixing-and-mastering\",\n \"kind\": \"role\",\n \"roles\": [\"MIXER\", \"MASTERING\"],\n \"words\": [\"混音/母带\", \"混音母带\", \"混音&母带\", \"混音与母带\", \"混音及母带\", \"母带/混音\", \"混音、母带\", \"混音,母带\"]\n },\n {\n \"id\": \"lyrics-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"ARRANGER\"],\n \"words\": [\"作词/编曲\", \"作词、编曲\", \"作词编曲\", \"词/编\", \"词编\"]\n },\n {\n \"id\": \"lyrics-composition-and-arrangement\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"ARRANGER\"],\n \"words\": [\"作词/作曲/编曲\", \"作词、作曲、编曲\", \"作词作曲编曲\", \"词曲编\", \"词曲编曲\"]\n },\n {\n \"id\": \"singer-songwriter\",\n \"kind\": \"role\",\n \"roles\": [\"LYRICIST\", \"COMPOSER\", \"PERFORMER\"],\n \"words\": [\"唱作\", \"唱作人\", \"唱作歌手\"]\n },\n {\n \"id\": \"other\",\n \"kind\": \"role\",\n \"roles\": [\"OTHER\"],\n \"words\": [\n \"录音师\",\n \"录音工程师\",\n \"录音\",\n \"录制\",\n \"工程师\",\n \"混音助理\",\n \"母带助理\",\n \"制作助理\",\n \"策划\",\n \"宣发\",\n \"摄影师\",\n \"摄像\",\n \"设计师\",\n \"美术\",\n \"美工\",\n \"视觉设计\",\n \"封面设计\",\n \"插画\",\n \"插画师\",\n \"曲绘\",\n \"校对\",\n \"打轴\",\n \"时轴\",\n \"时间轴\",\n \"修音\",\n \"修音师\",\n \"调音\",\n \"调音师\",\n \"拟音\",\n \"音效\",\n \"音响\",\n \"指挥\",\n \"经纪\",\n \"顾问\",\n \"艺术指导\",\n \"艺术总监\",\n \"音乐总监\",\n \"创意总监\",\n \"舞台监督\",\n \"灯光师\",\n \"特别感谢\",\n \"特别鸣谢\",\n \"感谢\",\n \"鸣谢\",\n \"致谢\",\n \"短视频\",\n \"MIDI编程\",\n \"OP\",\n \"PGM\",\n \"Program\",\n \"SP\",\n \"主创\",\n \"乐器技师\",\n \"乐谱\",\n \"乐谱管理员\",\n \"人声指导\",\n \"人声编辑\",\n \"企划宣传\",\n \"企宣\",\n \"分轨\",\n \"分镜\",\n \"制作统筹\",\n \"前台工程师\",\n \"剪辑\",\n \"剪辑师\",\n \"动画师\",\n \"助工\",\n \"助理\",\n \"助理工程师\",\n \"化妆\",\n \"协力\",\n \"吉他技师\",\n \"后期制作\",\n \"图绘\",\n \"声工\",\n \"声设\",\n \"声音设计师\",\n \"妆发\",\n \"妆造\",\n \"客串\",\n \"宣推\",\n \"平面\",\n \"平面视觉\",\n \"平面设计\",\n \"录音助理\",\n \"录音室经理\",\n \"扒谱\",\n \"扒谱员\",\n \"承办人\",\n \"技术支持\",\n \"抄谱\",\n \"抄谱员\",\n \"拟音师\",\n \"数字剪辑\",\n \"数字编辑\",\n \"文案策划\",\n \"映像\",\n \"服装\",\n \"杜比全景声\",\n \"歌词翻译\",\n \"灯光\",\n \"特约嘉宾\",\n \"特邀\",\n \"电脑\",\n \"电脑工程师\",\n \"监听\",\n \"监听工程师\",\n \"策监\",\n \"筹划\",\n \"编程\",\n \"编程播放\",\n \"编调\",\n \"编辑\",\n \"美术指导\",\n \"舞台总监\",\n \"艺人与产品\",\n \"艺人助理\",\n \"艺人统筹\",\n \"艺术顾问\",\n \"节拍\",\n \"节拍制作\",\n \"节拍制作人\",\n \"营销\",\n \"视觉总监\",\n \"调教\",\n \"调校\",\n \"谱务\",\n \"资产管理\",\n \"跟踪工程师\",\n \"辅助监督\",\n \"造型\",\n \"造型师\",\n \"采样\",\n \"音乐制作助理\",\n \"音乐统筹\",\n \"音乐视频导演\",\n \"音响工程师\",\n \"音序\",\n \"音控师\",\n \"音效设计\",\n \"音频编辑\",\n \"项目策划\",\n \"项目统筹\",\n \"预制作\",\n \"题字\",\n \"首席运营官\",\n \"鼓技师\",\n \"封面\",\n \"海报\",\n \"原版权\",\n \"原始版权\",\n \"次级版权\",\n \"版权运营\",\n \"业务\",\n \"企划\",\n \"宣传\",\n \"推广\",\n \"发行\",\n \"统筹\",\n \"设计\",\n \"摄影\",\n \"导演\",\n \"翻译\",\n \"后期\",\n \"工程\",\n \"声音\",\n \"支持\",\n \"艺术\",\n \"指导\",\n \"监督\",\n \"视觉\",\n \"舞台\",\n \"物料\",\n \"台词\",\n \"字幕\",\n \"文案\",\n \"脚本\",\n \"花絮\",\n \"视频\",\n \"作\",\n \"封\",\n \"监\",\n \"编\",\n \"译\",\n \"谱\"\n ]\n }\n ]\n}\n","import { CreditRole } from '@gondola-lyrics/model/runtime'\n\nimport enRules from '@rules/credits/en.json'\nimport zhHansRules from '@rules/credits/zh-hans.json'\n\nimport { findRuleMatchCuts, toRuleMatchKey } from '@root/utils'\n\nconst MATCH_CACHE_LIMIT = 256\nconst MAX_CACHED_LABEL_LENGTH = 256\n\nconst CREDIT_ROLES = new Map<string, CreditRole>([\n ['OTHER', CreditRole.OTHER],\n ['LYRICIST', CreditRole.LYRICIST],\n ['COMPOSER', CreditRole.COMPOSER],\n ['ARRANGER', CreditRole.ARRANGER],\n ['PRODUCER', CreditRole.PRODUCER],\n ['PERFORMER', CreditRole.PERFORMER],\n ['MIXER', CreditRole.MIXER],\n ['MASTERING', CreditRole.MASTERING],\n])\n\nconst creditRules = new Map<string, readonly CreditRole[]>()\nconst matchCache = new Map<string, readonly CreditRole[] | null>()\n\nfor (const ruleSet of [zhHansRules, enRules]) {\n for (const group of ruleSet.groups) {\n const roles = group.roles.map((name) => {\n const role = CREDIT_ROLES.get(name)\n if (role === undefined) {\n throw new TypeError('Unsupported credit role: ' + name)\n }\n return role\n })\n\n for (const word of group.words) {\n const key = toRuleMatchKey(word)\n if (!creditRules.has(key)) {\n creditRules.set(key, roles)\n }\n }\n }\n}\n\nconst haveSameRoles = (first: readonly CreditRole[], second: readonly CreditRole[]): boolean => {\n if (first === second) {\n return true\n }\n if (first.length !== second.length) {\n return false\n }\n if (first.length === 1) {\n return first[0] === second[0]\n }\n return first.every((role) => second.includes(role))\n}\n\nconst findCoveringRoles = (label: string, key: string): readonly CreditRole[] | undefined => {\n // Cuts come from the source label because the key no longer carries its whitespace.\n const cuts = findRuleMatchCuts(label)\n if (!cuts) {\n return undefined\n }\n\n const last = cuts.length - 1\n let bestCount = last + 1\n let bestRoles: readonly CreditRole[] | undefined\n // Parts are tried longest first so the fewest-part covering appears early; false means two equally short coverings disagree.\n const search = (from: number, count: number, roles: readonly CreditRole[] | undefined): boolean => {\n if (from === last) {\n if (count < bestCount) {\n bestCount = count\n bestRoles = roles\n return true\n }\n return count > bestCount || haveSameRoles(roles!, bestRoles!)\n }\n if (count + 1 > bestCount) {\n return true\n }\n for (let to = last; to > from; to -= 1) {\n const matched = creditRules.get(key.slice(cuts[from]!, cuts[to]!))\n if (!matched || (roles && !haveSameRoles(roles, matched))) {\n continue\n }\n if (!search(to, count + 1, roles ?? matched)) {\n return false\n }\n }\n return true\n }\n return search(0, 0, undefined) ? bestRoles : undefined\n}\n\n/**\n * Prefers the complete alias, then accepts adjacent parts that each resolve to the same roles.\n * Parts are cut only where the source has whitespace or a script boundary, and the covering with the fewest parts wins.\n * Unknown parts, role conflicts, and equally short coverings that disagree stay unmatched so the source remains available to later processing.\n */\nexport const matchCreditLabel = (label: string): readonly CreditRole[] | undefined => {\n const cached = matchCache.get(label)\n if (cached !== undefined) {\n return cached ?? undefined\n }\n\n const key = toRuleMatchKey(label)\n const roles = creditRules.get(key) ?? findCoveringRoles(label, key)\n\n // Cache source spellings to avoid renormalizing recurring headers without retaining arbitrarily long labels.\n if (label.length <= MAX_CACHED_LABEL_LENGTH) {\n if (matchCache.size === MATCH_CACHE_LIMIT) {\n const oldest = matchCache.keys().next().value\n if (oldest !== undefined) {\n matchCache.delete(oldest)\n }\n }\n matchCache.set(label, roles ?? null)\n }\n return roles\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport {\n LineType,\n Timing,\n deriveLanguageUsages,\n getContentText,\n isWordLevelLine,\n makeMeta,\n makeMetaCredit,\n makeText,\n} from '@gondola-lyrics/model/runtime'\n\nimport { trimRuleWhitespace } from '@root/utils'\n\nimport { matchCreditLabel } from './rules'\n\nexport const DEFAULT_CREDIT_NAME_SEPARATORS = ['+', ':', '/'] as const\n\nexport interface ExtractCreditsOptions {\n /**\n * Matches existing unknown metadata keys as credit labels and consumes fully readable matches.\n * Values use nameSeparators; unrecognized keys and values with no readable names remain unchanged.\n *\n * @default false\n */\n matchUnknowns?: boolean\n /**\n * Removes successfully extracted source lines together with their backgrounds and annotations.\n * Extracted roles and names remain in metadata; attached content is not moved to other lines.\n *\n * Refreshes language usages and falls back to line timing when no word-level content remains.\n *\n * @default false\n */\n removeSourceLines?: boolean\n /**\n * Splits names at literal separators and ignores empty strings; an empty list preserves the complete source name.\n * Longer separators take precedence so overlapping delimiters remain predictable.\n *\n * @default DEFAULT_CREDIT_NAME_SEPARATORS\n */\n nameSeparators?: readonly string[]\n}\n\ninterface CreditEntry {\n index: number\n names: Set<string>\n}\n\ntype CreditIndex = Map<runtime.MetaCredit['raw'], Map<runtime.MetaCredit['role'], CreditEntry>>\n\nconst createCreditIndex = (credits: readonly runtime.MetaCredit[]): CreditIndex => {\n const creditIndex: CreditIndex = new Map()\n for (let index = 0; index < credits.length; index += 1) {\n const credit = credits[index]!\n let entries = creditIndex.get(credit.raw)\n if (!entries) {\n entries = new Map()\n creditIndex.set(credit.raw, entries)\n }\n let entry = entries.get(credit.role)\n if (!entry) {\n entry = { index, names: new Set() }\n entries.set(credit.role, entry)\n }\n for (const name of credit.names) {\n entry.names.add(name.text)\n }\n }\n return creditIndex\n}\n\nconst prepareCreditSeparators = (selected: readonly string[] | undefined): readonly string[] => {\n if (!selected || selected === DEFAULT_CREDIT_NAME_SEPARATORS) {\n return DEFAULT_CREDIT_NAME_SEPARATORS\n }\n\n const useDefaultSeparators =\n selected.length === DEFAULT_CREDIT_NAME_SEPARATORS.length &&\n selected.every((separator, index) => separator === DEFAULT_CREDIT_NAME_SEPARATORS[index])\n if (useDefaultSeparators) {\n return DEFAULT_CREDIT_NAME_SEPARATORS\n }\n\n const separators = [...new Set(selected)].filter(Boolean).sort((first, second) => second.length - first.length)\n return separators\n}\n\nconst readCreditNames = (text: string, separators: readonly string[]): string[] => {\n const hasSeparator = separators.some((separator) => text.includes(separator))\n if (!hasSeparator) {\n const name = trimRuleWhitespace(text)\n return name ? [name] : []\n }\n\n let names = [text]\n for (const separator of separators) {\n const hasNameSeparator = names.some((name) => name.includes(separator))\n if (hasNameSeparator) {\n names = names.flatMap((name) => name.split(separator))\n }\n }\n\n const unique = new Set<string>()\n for (const name of names) {\n const trimmed = trimRuleWhitespace(name)\n if (trimmed) {\n unique.add(trimmed)\n }\n }\n return [...unique]\n}\n\nconst hasCreditColon = (word: runtime.Word): boolean => {\n return word.text.includes(':') || word.text.includes(':')\n}\n\n/**\n * Preserves source labels and name spelling while matching normalized credit labels in normal lead lines.\n * Merges names by role and original label without inferring their language.\n * Unknown metadata is processed only when matchUnknowns is enabled; consumed entries leave the unknown list.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const extractCredits = (lyric: runtime.Lyric, options: ExtractCreditsOptions = {}): runtime.Lyric => {\n const separators = prepareCreditSeparators(options.nameSeparators)\n\n const originalCredits = lyric.meta?.credits ?? []\n let credits = originalCredits\n let creditIndex: CreditIndex | undefined\n\n const appendCredit = (raw: string, names: string[]): boolean => {\n const roles = matchCreditLabel(raw)\n if (!roles || !names.length) {\n return false\n }\n creditIndex ??= createCreditIndex(originalCredits)\n let entries = creditIndex.get(raw)\n if (!entries) {\n entries = new Map()\n creditIndex.set(raw, entries)\n }\n for (const role of roles) {\n let entry = entries.get(role)\n const additions = names.filter((name) => !entry?.names.has(name))\n if (!additions.length) {\n continue\n }\n if (credits === originalCredits) {\n credits = [...originalCredits]\n }\n const nameNodes = additions.map((name) => makeText({ text: name }))\n if (!entry) {\n entry = { index: credits.length, names: new Set() }\n entries.set(role, entry)\n credits.push(makeMetaCredit({ role, raw, names: nameNodes }))\n } else {\n const credit = credits[entry.index]!\n credits[entry.index] = { ...credit, names: [...credit.names, ...nameNodes] }\n }\n for (const name of additions) {\n entry.names.add(name)\n }\n }\n return true\n }\n\n const originalUnknowns = lyric.meta?.unknowns ?? []\n let unknowns = originalUnknowns\n if (options.matchUnknowns) {\n for (const [index, entry] of originalUnknowns.entries()) {\n const raw = trimRuleWhitespace(entry.key)\n const names = matchCreditLabel(raw) ? readCreditNames(entry.value, separators) : []\n if (appendCredit(raw, names)) {\n if (unknowns === originalUnknowns) {\n unknowns = originalUnknowns.slice(0, index)\n }\n } else if (unknowns !== originalUnknowns) {\n unknowns.push(entry)\n }\n }\n }\n\n const originalLines = lyric.lines\n let remainingLines: runtime.Line[] | undefined\n for (let index = 0; index < originalLines.length; index += 1) {\n const line = originalLines[index]!\n if (line.type !== LineType.NORMAL) {\n remainingLines?.push(line)\n continue\n }\n\n const content = line.content\n // Avoid joining word-timed lyric content when no word can contain a credit delimiter.\n const hasCandidateContent = content?.text !== undefined || (content?.words.some(hasCreditColon) ?? false)\n if (!hasCandidateContent) {\n remainingLines?.push(line)\n continue\n }\n const text = getContentText(content)\n const colon = text.search(/[::]/)\n if (colon === -1) {\n remainingLines?.push(line)\n continue\n }\n const raw = trimRuleWhitespace(text.slice(0, colon))\n const names = matchCreditLabel(raw) ? readCreditNames(text.slice(colon + 1), separators) : []\n if (!appendCredit(raw, names)) {\n remainingLines?.push(line)\n continue\n }\n\n if (options.removeSourceLines) {\n remainingLines ??= originalLines.slice(0, index)\n } else {\n remainingLines?.push(line)\n }\n }\n\n if (credits === originalCredits && unknowns === originalUnknowns && !remainingLines) {\n return lyric\n }\n\n const removed = !!remainingLines\n const lines = remainingLines ?? originalLines\n\n let timing = lyric.timing\n if (removed && timing === Timing.WORD) {\n const hasWordLevelContent = lines.some((line) => {\n return isWordLevelLine(line) || line.backgrounds.some(isWordLevelLine)\n })\n if (!hasWordLevelContent) {\n timing = Timing.LINE\n }\n }\n\n const meta =\n credits === originalCredits && unknowns === originalUnknowns\n ? lyric.meta\n : lyric.meta\n ? { ...lyric.meta, credits, unknowns }\n : makeMeta({ credits, unknowns })\n return { ...lyric, meta, lines, timing, languages: removed ? deriveLanguageUsages(lines) : lyric.languages }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { LineType, WordType, makeWordEmphasis, validateTimeRange } from '@gondola-lyrics/model/runtime'\n\nimport { mapShared } from '@root/utils'\n\nexport interface DeriveWordEmphasisOptions {\n /**\n * Word duration must strictly exceed this threshold, expressed as a non-negative safe integer in milliseconds.\n *\n * @default 3000\n */\n durationThreshold?: number\n}\n\nconst deriveContentEmphasis = (content: runtime.LineContent | undefined, durationThreshold: number): runtime.LineContent | undefined => {\n if (!content) {\n return content\n }\n\n const words = mapShared(content.words, (word) => {\n if (word.type !== WordType.NORMAL || word.emphasis?.derived === false) {\n return word\n }\n\n const time = word.time\n const emphasized = !!time && time.end !== undefined && !validateTimeRange(time).length && time.end - time.start > durationThreshold\n\n if (emphasized) {\n if (word.emphasis?.value) {\n return word\n }\n return { ...word, emphasis: makeWordEmphasis({ value: true, derived: true }) }\n }\n if (!word.emphasis) {\n return word\n }\n return { ...word, emphasis: undefined }\n })\n\n return words === content.words ? content : { ...content, words }\n}\n\n/**\n * Derives emphasis for normal words in lead and background vocals using complete, valid word ranges.\n * The stored word duration must strictly exceed durationThreshold; metadata offset and duration have no effect.\n *\n * Source emphasis, including an explicit false, is preserved; derived emphasis is cleared when a word no longer qualifies.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n *\n * @throws {RangeError} If durationThreshold is not a non-negative safe integer in milliseconds.\n */\nexport const deriveWordEmphasis = (lyric: runtime.Lyric, options: DeriveWordEmphasisOptions = {}): runtime.Lyric => {\n const durationThreshold = options.durationThreshold ?? 3000\n if (!Number.isSafeInteger(durationThreshold) || durationThreshold < 0) {\n throw new RangeError('durationThreshold must be a non-negative safe integer in milliseconds')\n }\n\n const lines = mapShared(lyric.lines, (line) => {\n if (line.type !== LineType.NORMAL) {\n return line\n }\n\n const content = deriveContentEmphasis(line.content, durationThreshold)\n const backgrounds = mapShared(line.backgrounds, (background) => {\n const content = deriveContentEmphasis(background.content, durationThreshold)\n return content === background.content ? background : { ...background, content }\n })\n\n return content === line.content && backgrounds === line.backgrounds ? line : { ...line, content, backgrounds }\n })\n\n return lines === lyric.lines ? lyric : { ...lyric, lines }\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"en\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"copyright-notice\",\n \"category\": \"copyright\",\n \"match\": \"contains\",\n \"words\": [\n \"Allrightsreserved\",\n \"Allrightsofthe\",\n \"Nopartofthis\",\n \"Unauthorizedreproduction\",\n \"Unauthorizedduplication\",\n \"Unauthorizedcopying\",\n \"Unauthorizeduse\",\n \"Underexclusivelicense\",\n \"Allrightsreservedby\",\n \"Anyunauthorizeduse\",\n \"Usedbypermission\"\n ]\n },\n {\n \"id\": \"copyright-label\",\n \"category\": \"copyright\",\n \"match\": \"whole\",\n \"words\": [\"Copyright\", \"License\"]\n },\n {\n \"id\": \"adaptation-label\",\n \"category\": \"adaptation\",\n \"match\": \"whole\",\n \"words\": [\"Cover\", \"Remake\", \"Tribute\", \"Adaptation\"]\n },\n {\n \"id\": \"disclaimer-notice\",\n \"category\": \"disclaimer\",\n \"match\": \"contains\",\n \"words\": [\"Foreducationalpurposes\", \"Fornon-commercial\", \"Fornoncommercial\", \"Fornonprofitable\", \"Forpersonaluse\", \"Forpromotionalpurposes\"]\n },\n {\n \"id\": \"organization-label\",\n \"category\": \"organization\",\n \"match\": \"contains\",\n \"words\": [\"ManufacturingCompany\", \"RecordCompany\", \"RecordLabel\", \"Distribution\"]\n },\n {\n \"id\": \"studio-label\",\n \"category\": \"studio\",\n \"match\": \"contains\",\n \"words\": [\"RecordingStudio\", \"MasteringStudio\", \"Mixingstudio\", \"Mixstudio\", \"StudioPersonnel\"]\n }\n ]\n}\n","{\n \"$schema\": \"../schema.json\",\n \"version\": 1,\n \"language\": \"zh-hans\",\n \"normalization\": {\n \"whitespace\": \"remove\",\n \"case\": \"asciiLower\"\n },\n \"groups\": [\n {\n \"id\": \"copyright-notice\",\n \"category\": \"copyright\",\n \"match\": \"contains\",\n \"words\": [\n \"版权所有\",\n \"保留所有权利\",\n \"侵权必究\",\n \"翻版必究\",\n \"违者必究\",\n \"未经授权\",\n \"未经许可\",\n \"版权归\",\n \"版权属于\",\n \"不得转载\",\n \"严禁转载\",\n \"盗版必究\",\n \"禁止转载\"\n ]\n },\n {\n \"id\": \"copyright-label\",\n \"category\": \"copyright\",\n \"match\": \"whole\",\n \"words\": [\"版权\", \"授权\"]\n },\n {\n \"id\": \"adaptation-label\",\n \"category\": \"adaptation\",\n \"match\": \"whole\",\n \"words\": [\"改编\", \"翻唱\", \"致敬\"]\n },\n {\n \"id\": \"disclaimer-notice\",\n \"category\": \"disclaimer\",\n \"match\": \"contains\",\n \"words\": [\"仅供学习\", \"仅供个人\", \"仅供参考\", \"仅供交流\", \"禁止商业用途\", \"非商业用途\"]\n },\n {\n \"id\": \"organization-label\",\n \"category\": \"organization\",\n \"match\": \"contains\",\n \"words\": [\"唱片公司\", \"制作公司\", \"发行公司\", \"承办单位\", \"次级发行方\", \"厂牌\"]\n },\n {\n \"id\": \"studio-label\",\n \"category\": \"studio\",\n \"match\": \"contains\",\n \"words\": [\"录音室\", \"录音棚\", \"混音室\", \"混音工作室\", \"混音录音室\", \"母带录音室\", \"母带处理录音室\"]\n }\n ]\n}\n","import enRules from '@rules/filters/en.json'\nimport zhHansRules from '@rules/filters/zh-hans.json'\nimport { toRuleMatchKey } from '@root/utils'\n\nexport interface NonLyricGroup {\n readonly category: string\n readonly whole: boolean\n readonly words: readonly string[]\n}\n\nconst nonLyricGroups: readonly NonLyricGroup[] = [zhHansRules, enRules].flatMap((ruleSet) => {\n return ruleSet.groups.map((group) => {\n if (group.match !== 'contains' && group.match !== 'whole') {\n throw new TypeError('Unsupported filter match: ' + group.match)\n }\n return { category: group.category, whole: group.match === 'whole', words: group.words.map(toRuleMatchKey) }\n })\n})\n\n/**\n * Categories the bundled filter rules identify, in rule order.\n */\nexport const NON_LYRIC_CATEGORIES: readonly string[] = [...new Set(nonLyricGroups.map((group) => group.category))]\n\n/**\n * Keeps rule order so earlier groups still take precedence within a selection; undefined selects every group.\n */\nexport const selectNonLyricGroups = (categories: readonly string[] | undefined): readonly NonLyricGroup[] => {\n if (!categories) {\n return nonLyricGroups\n }\n return nonLyricGroups.filter((group) => categories.includes(group.category))\n}\n\n/**\n * Returns the category of the first group that identifies the normalized text.\n * whole needs the complete text to equal a word, while contains accepts a word anywhere because the rules carry no word boundaries.\n */\nexport const matchNonLyricCategory = (text: string, groups: readonly NonLyricGroup[]): string | undefined => {\n const key = toRuleMatchKey(text)\n if (!key) {\n return undefined\n }\n for (const group of groups) {\n const matched = group.whole ? group.words.includes(key) : group.words.some((word) => key.includes(word))\n if (matched) {\n return group.category\n }\n }\n return undefined\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { LineType, Timing, deriveLanguageUsages, getContentText, isWordLevelLine } from '@gondola-lyrics/model/runtime'\n\nimport { matchNonLyricCategory, selectNonLyricGroups } from './rules'\n\nexport interface RemoveNonLyricLinesOptions {\n /**\n * Removes lines identified under these categories; an empty list skips the shared rules.\n * First-line music information is selected independently.\n *\n * @default NON_LYRIC_CATEGORIES\n */\n categories?: readonly string[]\n /**\n * Enables first-line music-information matching using these known titles and artists.\n * Omitting this option, or leaving either list empty, skips the check; lyric metadata is not inferred.\n */\n musicInfo?: {\n titles?: readonly string[]\n artists?: readonly string[]\n }\n}\n\nconst toMusicInfoMatchText = (text: string): string => {\n return text\n .normalize('NFC')\n .trim()\n .replace(/\\s+/gu, ' ')\n .replace(/[A-Z]+/g, (run) => run.toLowerCase())\n}\n\nconst findFirstMusicInfoLine = (lyric: runtime.Lyric, musicInfo: NonNullable<RemoveNonLyricLinesOptions['musicInfo']>) => {\n const titles = (musicInfo.titles ?? []).map(toMusicInfoMatchText).filter(Boolean)\n const artists = (musicInfo.artists ?? []).map(toMusicInfoMatchText).filter(Boolean)\n if (!titles.length || !artists.length) {\n return -1\n }\n\n const index = lyric.lines.findIndex((line) => {\n return line.type === LineType.NORMAL && !!getContentText(line.content).trim()\n })\n if (index === -1) {\n return -1\n }\n\n const parts = getContentText(lyric.lines[index]!.content).trim().split(' - ')\n if (parts.length !== 2) {\n return -1\n }\n const left = toMusicInfoMatchText(parts[0]!)\n const right = toMusicInfoMatchText(parts[1]!)\n if (!left || !right) {\n return -1\n }\n\n const titleOnLeft = titles.some((title) => left.includes(title))\n const titleOnRight = titles.some((title) => right.includes(title))\n const artistOnLeft = artists.some((artist) => left.includes(artist))\n const artistOnRight = artists.some((artist) => right.includes(artist))\n return (titleOnLeft && artistOnRight) || (artistOnLeft && titleOnRight) ? index : -1\n}\n\n/**\n * Removes normal lead lines matched by the shared notice rules and, when musicInfo is provided, first-line song information.\n * The first non-empty normal line must have one literal \" - \" separator and a known title and artist on opposite sides; either order and extra text such as bracketed aliases are accepted.\n * Drops attached annotations and backgrounds, refreshes language usages, and falls back to line timing when no word-level content remains.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const removeNonLyricLines = (lyric: runtime.Lyric, options: RemoveNonLyricLinesOptions = {}): runtime.Lyric => {\n const groups = selectNonLyricGroups(options.categories)\n const musicInfoIndex = options.musicInfo ? findFirstMusicInfoLine(lyric, options.musicInfo) : -1\n if (!groups.length && musicInfoIndex === -1) {\n return lyric\n }\n\n const lines = lyric.lines.filter((line, index) => {\n if (index === musicInfoIndex) {\n return false\n }\n return line.type !== LineType.NORMAL || matchNonLyricCategory(getContentText(line.content), groups) === undefined\n })\n\n if (lines.length === lyric.lines.length) {\n return lyric\n }\n\n let timing = lyric.timing\n if (timing === Timing.WORD) {\n const hasWordLevelContent = lines.some((line) => {\n return isWordLevelLine(line) || line.backgrounds.some(isWordLevelLine)\n })\n if (!hasWordLevelContent) {\n timing = Timing.LINE\n }\n }\n return { ...lyric, lines, timing, languages: deriveLanguageUsages(lines) }\n}\n","import type { runtime } from '@gondola-lyrics/model'\n\nimport { LineType, Timing, makeLineInstrumental, makeTimeRange, validateTimeRange } from '@gondola-lyrics/model/runtime'\n\nexport interface InsertInstrumentalLinesOptions {\n /**\n * Minimum gap duration in milliseconds, including an outro.\n *\n * @default 10000\n */\n minDuration?: number\n /**\n * Minimum gap duration before the first vocal in milliseconds.\n *\n * @default 5000\n */\n minIntroDuration?: number\n /**\n * Outro boundary in milliseconds, measured on the same timeline as line ranges.\n * An omitted boundary leaves the outro unknown.\n */\n endTime?: number\n}\n\nconst MAX_TIME = 2 ** 31 - 1\n\nconst readMinimum = (value: number | undefined, fallback: number, name: string): number => {\n const minimum = value ?? fallback\n if (!Number.isSafeInteger(minimum) || minimum < 0) {\n throw new RangeError(`${name} must be a non-negative safe integer in milliseconds`)\n }\n return minimum\n}\n\nconst readEndTime = (value: number | undefined): number | undefined => {\n if (value !== undefined && (!Number.isInteger(value) || value < 0 || value > MAX_TIME)) {\n throw new RangeError(`endTime must be a non-negative integer in milliseconds no greater than ${MAX_TIME}`)\n }\n return value\n}\n\nconst compareTimeRanges = (left: runtime.TimeRange | undefined, right: runtime.TimeRange | undefined): number => {\n const leftStart = left?.start ?? Infinity\n const rightStart = right?.start ?? Infinity\n if (leftStart !== rightStart) {\n return leftStart - rightStart\n }\n\n const leftEnd = left?.end ?? Infinity\n const rightEnd = right?.end ?? Infinity\n return leftEnd === rightEnd ? 0 : leftEnd - rightEnd\n}\n\nconst readOccupiedTimeline = (lyric: runtime.Lyric) => {\n const ranges: runtime.TimeRange[] = []\n let firstSungStart = Infinity\n\n const append = (time: runtime.TimeRange | undefined, sung: boolean): boolean => {\n if (!time || validateTimeRange(time).length) {\n return false\n }\n\n // An empty half-open range cannot divide an uncovered gap.\n if (time.end === time.start) {\n return true\n }\n ranges.push(time)\n if (sung) {\n firstSungStart = Math.min(firstSungStart, time.start)\n }\n return true\n }\n\n for (const line of lyric.lines) {\n if (!append(line.time, line.type === LineType.NORMAL)) {\n return undefined\n }\n for (const background of line.backgrounds) {\n if (!append(background.time, true)) {\n return undefined\n }\n }\n }\n\n ranges.sort(compareTimeRanges)\n return { ranges, firstSungStart }\n}\n\nconst compareLineTimes = (left: runtime.Line, right: runtime.Line): number => {\n return compareTimeRanges(left.time, right.time)\n}\n\nconst mergeLines = (lines: runtime.Line[], inserted: runtime.Line[]): runtime.Line[] => {\n let source = lines\n for (const [index, line] of lines.entries()) {\n const previous = lines[index - 1]\n if (previous && compareLineTimes(previous, line) > 0) {\n source = [...lines].sort(compareLineTimes)\n break\n }\n }\n\n const merged: runtime.Line[] = []\n let nextIndex = 0\n for (const line of source) {\n let next = inserted[nextIndex]\n while (next && compareLineTimes(next, line) < 0) {\n merged.push(next)\n nextIndex += 1\n next = inserted[nextIndex]\n }\n merged.push(line)\n }\n merged.push(...inserted.slice(nextIndex))\n return merged\n}\n\n/**\n * Inserts derived instrumental lines into uncovered gaps on the stored timeline, starting at zero.\n * Existing lines and background vocals occupy their full ranges; an unknown end blocks later gaps.\n *\n * An outro requires an explicit endTime; metadata duration and offset do not affect insertion.\n *\n * @returns The input when unchanged, or a new lyric and lines array sharing existing nodes.\n */\nexport const insertInstrumentalLines = (lyric: runtime.Lyric, options: InsertInstrumentalLinesOptions = {}): runtime.Lyric => {\n const minDuration = readMinimum(options.minDuration, 10000, 'minDuration')\n const minIntroDuration = readMinimum(options.minIntroDuration, 5000, 'minIntroDuration')\n const endTime = readEndTime(options.endTime)\n if ((lyric.timing !== Timing.LINE && lyric.timing !== Timing.WORD) || !lyric.lines.length) {\n return lyric\n }\n\n const occupied = readOccupiedTimeline(lyric)\n if (!occupied || occupied.firstSungStart === Infinity) {\n return lyric\n }\n\n const inserted: runtime.Line[] = []\n let coveredEnd = 0\n\n const appendGap = (end: number, minimum: number): void => {\n const duration = end - coveredEnd\n if (duration > 0 && duration >= minimum) {\n inserted.push(makeLineInstrumental({ time: makeTimeRange({ start: coveredEnd, end }) }))\n }\n }\n\n for (const range of occupied.ranges) {\n appendGap(range.start, range.start <= occupied.firstSungStart ? minIntroDuration : minDuration)\n // An unknown end gives no evidence of a later gap, so it occupies the rest of the timeline.\n coveredEnd = Math.max(coveredEnd, range.end ?? Infinity)\n if (coveredEnd === Infinity) {\n break\n }\n }\n\n if (endTime !== undefined) {\n appendGap(endTime, minDuration)\n }\n\n if (!inserted.length) {\n return lyric\n }\n return { ...lyric, lines: mergeLines(lyric.lines, inserted) }\n}\n","/**\n * Groups of characters a source may write without a space where one belongs.\n */\nexport const InsertSpacesRules = {\n /**\n * A CJK character touching a Latin letter, as in `ABC中文`.\n */\n CJK_ENGLISH: 'cjkEnglish',\n /**\n * A CJK character touching a digit, as in `2026年`.\n */\n CJK_NUMBER: 'cjkNumber',\n /**\n * A comma, semicolon, exclamation mark, question mark or colon touching a character, as in `a,b`; a clock time or thousands separator such as `10:30` stays as written.\n */\n PUNCTUATION: 'punctuation',\n /**\n * A quotation mark touching a neighbouring character.\n */\n QUOTE: 'quote',\n /**\n * A bracket touching the character outside it.\n */\n BRACKET_OUTSIDE: 'bracketOutside',\n /**\n * A plus, equals or ampersand touching a character, as in `a+b`; a compound operator such as `a+=b` stays as written.\n */\n MATH_OPERATOR: 'mathOperator',\n /**\n * A hyphen touching a CJK character, as in `中-文`; a compound word such as `well-known` stays as written.\n */\n HYPHEN: 'hyphen',\n /**\n * A slash touching a character, as in `and/or`; a fraction such as `1/2` stays as written.\n */\n SLASH: 'slash',\n /**\n * Runs of two or more spaces, collapsed to one.\n */\n COMPRESS_SPACES: 'compressSpaces',\n /**\n * Spaces just inside a bracket or quotation mark, removed.\n */\n TRIM_INSIDE_SYMBOLS: 'trimInsideSymbols',\n} as const\n\nexport type InsertSpacesRule = (typeof InsertSpacesRules)[keyof typeof InsertSpacesRules]\n\nexport const INSERT_SPACES_RULES = Object.values(InsertSpacesRules) as InsertSpacesRule[]\n","import type { InsertSpacesRule } from './constants'\n\nimport { INSERT_SPACES_RULES, InsertSpacesRules } from './constants'\n\n// Kana, CJK extensions, unified ideographs and compatibility ideographs.\nconst CJK_RANGE = '\\u3040-\\u30ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff'\nconst LATIN_RANGE = 'A-Za-z'\nconst DIGIT_RANGE = '0-9'\nconst SYMBOL_RANGE = '!@#$%^&+\\\\-=/|<>'\nconst WORD_RANGE = `${LATIN_RANGE}${DIGIT_RANGE}${CJK_RANGE}`\nconst ALL_RANGE = `${WORD_RANGE}${SYMBOL_RANGE}`\n\nconst CJK_LATIN = new RegExp(`([${CJK_RANGE}])([${LATIN_RANGE}])`, 'gu')\nconst LATIN_CJK = new RegExp(`([${LATIN_RANGE}])([${CJK_RANGE}])`, 'gu')\nconst CJK_DIGIT = new RegExp(`([${CJK_RANGE}])([${DIGIT_RANGE}])`, 'gu')\nconst DIGIT_CJK = new RegExp(`([${DIGIT_RANGE}])([${CJK_RANGE}])`, 'gu')\n\nconst PUNCTUATION = new RegExp(`([${ALL_RANGE}])([!;,\\\\?:])(?=[${ALL_RANGE}])`, 'gu')\nconst QUOTE_BEFORE = new RegExp(`([${CJK_RANGE}${LATIN_RANGE}${DIGIT_RANGE}])([\"\\`“‘])`, 'gu')\nconst QUOTE_AFTER = new RegExp(`([\"”’])([${CJK_RANGE}${LATIN_RANGE}${DIGIT_RANGE}])`, 'gu')\nconst BRACKET_BEFORE = new RegExp(`([${WORD_RANGE}])([\\\\[({<])`, 'gu')\nconst BRACKET_AFTER = new RegExp(`([\\\\])}>])([${WORD_RANGE}])`, 'gu')\nconst MATH_OPERATOR = new RegExp(`([${ALL_RANGE}])([+=&])(?=[${ALL_RANGE}])`, 'gu')\nconst HYPHEN = new RegExp(`(?<=[${WORD_RANGE}])-(?=[${WORD_RANGE}])`, 'gu')\nconst SLASH = new RegExp(`([${WORD_RANGE}])(/)(?=[${WORD_RANGE}])`, 'gu')\nconst TRIM_INSIDE_SYMBOLS = new RegExp('([<[{\"“‘(])\\\\s*([^<>[\\\\]{}()\"“‘”’]*?)\\\\s*([>\\\\]}\"”’)])', 'gu')\nconst REPEATED_SPACES = / {2,}/gu\n\n// A run of two or more marks belongs together: `a--b`, `a//b`, `a...b`, `a::b` and `a+=b` stay as written.\nconst REPEATED_MARKS = /[!;,\\\\?:+\\-=/&]{2,}/gu\n\n// A control character stands in for a held run, since lyric text never carries one and no rule's character class covers it.\nconst PLACEHOLDER = /\\u0001(\\d+)\\u0001/gu\n\nconst isDigit = (char: string | undefined) => {\n return !!char && char >= '0' && char <= '9'\n}\n\nconst isLatin = (char: string | undefined) => {\n return !!char && ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z'))\n}\n\n/**\n * A comma or colon between two digits belongs to one number or clock time, so it stays tight.\n */\nconst applyPunctuation = (text: string) => {\n return text.replace(PUNCTUATION, (match, before: string, mark: string, offset: number, source: string) => {\n if ((mark === ',' || mark === ':') && isDigit(before) && isDigit(source[offset + match.length])) {\n return match\n }\n return `${before}${mark} `\n })\n}\n\n/**\n * A hyphen between two digits is a date, range or phone number, and one between two Latin letters joins a compound word.\n *\n * An edge hyphen is left alone, since it reads as a sign or a list marker rather than a dash.\n */\nconst applyHyphen = (text: string) => {\n return text.replace(HYPHEN, (match, offset: number, source: string) => {\n const left = source[offset - 1]\n const right = source[offset + 1]\n return (isDigit(left) && isDigit(right)) || (isLatin(left) && isLatin(right)) ? match : ' - '\n })\n}\n\n/**\n * A slash between two digits is a fraction or date, so it stays tight.\n */\nconst applySlash = (text: string) => {\n return text.replace(SLASH, (match, before: string, slash: string, offset: number, source: string) => {\n return isDigit(before) && isDigit(source[offset + match.length]) ? match : `${before} ${slash} `\n })\n}\n\n/**\n * Trims the spaces just inside a bracket or quotation mark, without touching a nested pair inside it.\n */\nconst applyTrimInsideSymbols = (text: string) => {\n return text.replace(TRIM_INSIDE_SYMBOLS, (match, left: string, inner: string, right: string) => {\n return `${left}${inner.trim()}${right}`\n })\n}\n\nconst APPLIERS: Record<InsertSpacesRule, (text: string) => string> = {\n [InsertSpacesRules.CJK_ENGLISH]: (text) => {\n return text.replace(CJK_LATIN, '$1 $2').replace(LATIN_CJK, '$1 $2')\n },\n [InsertSpacesRules.CJK_NUMBER]: (text) => {\n return text.replace(CJK_DIGIT, '$1 $2').replace(DIGIT_CJK, '$1 $2')\n },\n [InsertSpacesRules.PUNCTUATION]: applyPunctuation,\n [InsertSpacesRules.QUOTE]: (text) => {\n return text.replace(QUOTE_BEFORE, '$1 $2').replace(QUOTE_AFTER, '$1 $2')\n },\n [InsertSpacesRules.BRACKET_OUTSIDE]: (text) => {\n return text.replace(BRACKET_BEFORE, '$1 $2').replace(BRACKET_AFTER, '$1 $2')\n },\n [InsertSpacesRules.MATH_OPERATOR]: (text) => {\n return text.replace(MATH_OPERATOR, '$1 $2 ')\n },\n [InsertSpacesRules.HYPHEN]: applyHyphen,\n [InsertSpacesRules.SLASH]: applySlash,\n [InsertSpacesRules.COMPRESS_SPACES]: (text) => {\n return text.replace(REPEATED_SPACES, ' ')\n },\n [InsertSpacesRules.TRIM_INSIDE_SYMBOLS]: applyTrimInsideSymbols,\n}\n\n// Order matters: separating marks first, then tidying the spaces they may have doubled.\nconst APPLY_ORDER: InsertSpacesRule[] = [\n InsertSpacesRules.CJK_ENGLISH,\n InsertSpacesRules.CJK_NUMBER,\n InsertSpacesRules.PUNCTUATION,\n InsertSpacesRules.QUOTE,\n InsertSpacesRules.BRACKET_OUTSIDE,\n InsertSpacesRules.MATH_OPERATOR,\n InsertSpacesRules.HYPHEN,\n InsertSpacesRules.SLASH,\n InsertSpacesRules.TRIM_INSIDE_SYMBOLS,\n InsertSpacesRules.COMPRESS_SPACES,\n]\n\n/**\n * Falls back to every rule only when no list was given, so an explicit empty list means \"apply nothing\".\n */\nexport const resolveRules = (rules: InsertSpacesRule[] | undefined) => {\n return new Set(rules ?? INSERT_SPACES_RULES)\n}\n\n/**\n * Applies the selected rules to one text, returning it trimmed.\n *\n * Repeated marks are held aside first, so a rule that widens a single mark never splits a run that belongs together.\n */\nexport const insertSpaces = (text: string, rules: Set<InsertSpacesRule>) => {\n if (!text.trim()) {\n return text\n }\n\n const held: string[] = []\n const guarded = text.replace(REPEATED_MARKS, (match) => {\n held.push(match)\n return `\\u0001${held.length - 1}\\u0001`\n })\n\n let result = guarded\n for (const rule of APPLY_ORDER) {\n if (rules.has(rule)) {\n result = APPLIERS[rule](result)\n }\n }\n return result\n .replace(PLACEHOLDER, (match, index: string) => {\n return held[Number(index)] ?? match\n })\n .trim()\n}\n","import type { runtime } from '@gondola-lyrics/model'\nimport type { InsertSpacesRule } from './constants'\n\nimport {\n LineType,\n makeLineAnnotationRoman,\n makeLineAnnotationTranslation,\n makeLineContentText,\n makeLineContentWords,\n makeWordSpace,\n} from '@gondola-lyrics/model/runtime'\n\nimport { mapShared } from '@root/utils'\n\nimport { isNormalWord } from '../background/utils'\nimport { insertSpaces, resolveRules } from './rules'\n\nexport interface InsertSpacesOptions {\n /**\n * Rules to apply; omitting the list applies all of them.\n */\n rules?: InsertSpacesRule[]\n /**\n * Rewrite the translation and romanization tracks too.\n *\n * @default true\n */\n annotations?: boolean\n}\n\n/**\n * Maps the rewritten text back onto the original words.\n *\n * Every non-space character of the original survives in order, so walking both strings keeps each word's time,\n * language and annotation while the inserted spaces become their own words.\n * A word whose characters stay adjacent in the rewrite is kept whole, so only a word the space actually cuts is split.\n */\nconst alignWords = (words: runtime.Word[], text: string) => {\n const source = words.filter(isNormalWord)\n const result: runtime.Word[] = []\n let cursor = 0\n\n const takeSpaces = () => {\n let spaces = ''\n while (text[cursor] === ' ') {\n spaces += ' '\n cursor += 1\n }\n return spaces\n }\n\n for (const word of source) {\n const spaces = takeSpaces()\n if (spaces) {\n result.push(makeWordSpace({ text: spaces }))\n }\n\n // A word the rewrite left intact keeps its identity, including any annotation and emphasis.\n if (text.startsWith(word.text, cursor)) {\n result.push(word)\n cursor += word.text.length\n continue\n }\n\n // The rewrite split this word, so each piece carries the slice it stands for.\n let remaining = word.text\n while (remaining) {\n let taken = 0\n while (taken < remaining.length && text[cursor + taken] === remaining[taken]) {\n taken += 1\n }\n if (taken === 0) {\n return undefined\n }\n result.push({ ...word, text: remaining.slice(0, taken) })\n cursor += taken\n remaining = remaining.slice(taken)\n if (!remaining) {\n break\n }\n const split = takeSpaces()\n if (!split) {\n // Nothing separates the pieces, so the rewrite changed more than spacing.\n return undefined\n }\n result.push(makeWordSpace({ text: split }))\n }\n }\n\n const tail = takeSpaces()\n if (tail) {\n result.push(makeWordSpace({ text: tail }))\n }\n\n // A leftover character means the rewrite changed something other than spacing, so the caller keeps the original words.\n if (cursor !== text.length) {\n return undefined\n }\n return result\n}\n\n/**\n * Applies the rules to a word-level content, keeping it unchanged when the alignment fails.\n */\nconst applyToWords = (content: runtime.LineContent, rules: Set<InsertSpacesRule>) => {\n const text = content.words.map((word) => word.text).join('')\n const rewritten = insertSpaces(text, rules)\n if (rewritten === text) {\n return content\n }\n\n const words = alignWords(content.words, rewritten)\n if (!words) {\n return content\n }\n return words.length ? makeLineContentWords(words) : undefined\n}\n\n/**\n * Applies the rules to a plain-text content, dropping a line the rules would leave empty.\n */\nconst applyToText = (content: runtime.LineContent, rules: Set<InsertSpacesRule>) => {\n const text = content.text ?? ''\n const rewritten = insertSpaces(text, rules)\n if (rewritten === text) {\n return content\n }\n return rewritten ? makeLineContentText(rewritten) : undefined\n}\n\n/**\n * Applies the rules to one content by the shape it carries.\n */\nconst applyToContent = (content: runtime.LineContent | undefined, rules: Set<InsertSpacesRule>) => {\n if (!content) {\n return content\n }\n return content.words.length ? applyToWords(content, rules) : applyToText(content, rules)\n}\n\n/**\n * Applies the rules to the line annotation, keeping the entries they leave untouched.\n */\nconst applyToAnnotation = (annotation: runtime.LineAnnotation | undefined, rules: Set<InsertSpacesRule>) => {\n if (!annotation) {\n return annotation\n }\n\n let changed = false\n const romans = annotation.romans.map((item) => {\n const text = insertSpaces(item.text, rules)\n if (text === item.text) {\n return item\n }\n changed = true\n return makeLineAnnotationRoman({ ...item, text })\n })\n const translations = annotation.translations.map((item) => {\n const text = insertSpaces(item.text, rules)\n if (text === item.text) {\n return item\n }\n changed = true\n return makeLineAnnotationTranslation({ ...item, text })\n })\n return changed ? { ...annotation, romans, translations } : annotation\n}\n\n/**\n * Inserts the spaces a source left out, so CJK text set against Latin letters, digits or punctuation reads evenly.\n *\n * A word-level line keeps every word's time and annotation: an inserted space becomes its own space word,\n * and a word the rules split yields one word per character, all sharing the original range.\n * A plain-text line and the annotation tracks are rewritten as whole strings.\n *\n * @returns The input when unchanged, or a new lyric sharing all unchanged nodes.\n */\nexport const insertSpacesInLines = (lyric: runtime.Lyric, options: InsertSpacesOptions = {}): runtime.Lyric => {\n const rules = resolveRules(options.rules)\n const annotations = options.annotations ?? true\n\n const applyToBackground = (background: runtime.LineBackground): runtime.LineBackground => {\n const content = applyToContent(background.content, rules)\n const annotation = annotations ? applyToAnnotation(background.annotation, rules) : background.annotation\n if (content === background.content && annotation === background.annotation) {\n return background\n }\n return { ...background, content, annotation }\n }\n\n const lines = mapShared(lyric.lines, (line) => {\n if (line.type !== LineType.NORMAL) {\n return line\n }\n\n const content = applyToContent(line.content, rules)\n const annotation = annotations ? applyToAnnotation(line.annotation, rules) : line.annotation\n const backgrounds = mapShared(line.backgrounds, applyToBackground)\n if (content === line.content && annotation === line.annotation && backgrounds === line.backgrounds) {\n return line\n }\n return { ...line, content, annotation, backgrounds }\n })\n\n return lines === lyric.lines ? lyric : { ...lyric, lines }\n}\n\nexport type { InsertSpacesRule } from './constants'\n\nexport { InsertSpacesRules } from './constants'\n"],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|