Files
QZMusic_PC-pre/amll-local/packages/lyric/src/formats/eslrc.ts

110 lines
2.7 KiB
TypeScript
Raw Normal View History

/**
* @fileoverview ESLyric
*
*
*
* [00:10.82]Test[00:10.97] Word[00:12.62]
* [00:12.62]Next[00:13.20] line[00:14.10]
*/
import type { LyricLine, LyricWord } from "../types";
import {
clampTimestamp,
createLine,
createWord,
formatTime,
parseTime,
} from "../utils";
const TIME_REGEX = /^\[((?:\d+:)*\d+(?:\.\d+)?)\]/;
function parseTimestampPrefix(
src: string,
): { time: number; length: number } | null {
const match = src.match(TIME_REGEX);
if (!match) return null;
const [raw, timeStr] = match;
return { time: parseTime(timeStr), length: raw.length };
}
function parseEslrcLine(rawLine: string): LyricLine | null {
let src = rawLine.trim();
const first = parseTimestampPrefix(src);
if (!first) return null;
src = src.slice(first.length);
let startTime = first.time;
if (!src.trim()) return null;
const words: LyricWord[] = [];
while (src.trim().length > 0) {
const nextTimePos = src.indexOf("[");
if (nextTimePos <= 0) return null;
const word = src.slice(0, nextTimePos);
const nextTime = parseTimestampPrefix(src.slice(nextTimePos));
if (!nextTime) return null;
words.push(
createWord({
word,
startTime,
endTime: nextTime.time,
}),
);
src = src.slice(nextTimePos + nextTime.length);
startTime = nextTime.time;
}
return createLine({ words });
}
/**
* ESLyric
* @param eslrc
* @returns
*/
export function parseEslrc(eslrc: string): LyricLine[] {
const result: LyricLine[] = [];
for (const rawLine of eslrc.split(/\r?\n/)) {
const line = parseEslrcLine(rawLine);
if (line) result.push(line);
}
result.sort(
(a, b) =>
(a.words[0]?.startTime ?? Number.MAX_SAFE_INTEGER) -
(b.words[0]?.startTime ?? Number.MAX_SAFE_INTEGER),
);
for (const line of result) {
for (const word of line.words) {
word.startTime = clampTimestamp(word.startTime);
word.endTime = clampTimestamp(word.endTime);
}
line.startTime = clampTimestamp(line.words[0]?.startTime ?? 0);
line.endTime = clampTimestamp(
line.words[line.words.length - 1]?.endTime ?? 0,
);
}
return result;
}
/**
* ESLyric
* @param lines
* @returns ESLyric
*/
export function stringifyEslrc(lines: LyricLine[]): string {
return lines
.map((line) => {
if (!line.words.length) return "";
return `[${formatTime(clampTimestamp(line.words[0].startTime))}]${line.words
.map(
(word) => `${word.word}[${formatTime(clampTimestamp(word.endTime))}]`,
)
.join("")}`;
})
.filter(Boolean)
.join("\n");
}