import { FetchLyricResponse } from '@/shared/api/Track' export function lyricParser(lrc: FetchLyricResponse) { return { lyric: parseLyric(lrc?.lrc?.lyric || ''), tlyric: parseLyric(lrc?.tlyric?.lyric || ''), lyricuser: lrc.lyricUser, transuser: lrc.transUser, } } /** * @see {@link https://regexr.com/6e52n} */ const extractLrcRegex = /^(?(?:\[.+?\])+)(?!\[)(?.+)$/gm const extractTimestampRegex = /\[(?\d+):(?\d+)(?:\.|:)*(?\d+)*\]/g interface ParsedLyric { time: number rawTime: string content: string } function parseLyric(lrc: string): ParsedLyric[] { // A sorted list of parsed lyric and its timestamp. const parsedLyrics: ParsedLyric[] = [] // Find the appropriate index to push our parsed lyric. const binarySearch = (lyric: ParsedLyric) => { const time = lyric.time let low = 0 let high = parsedLyrics.length - 1 while (low <= high) { const mid = Math.floor((low + high) / 2) const midTime = parsedLyrics[mid].time if (midTime === time) { return mid } else if (midTime < time) { low = mid + 1 } else { high = mid - 1 } } return low } function trimContent(content: string): string { const t = content.trim() return t.length < 1 ? content : t } for (const line of lrc.trim().matchAll(extractLrcRegex)) { const { lyricTimestamps, content } = line.groups as { lyricTimestamps: string content: string } if (content === '纯音乐,请欣赏') continue if ( content.match( // https://regexr.com/6j8pf /.*(?作曲|作词|编曲|制作|Producers|Producer|Produced|贝斯|工程师|吉他|合成器|助理|编程|制作|和声|母带|人声|鼓|混音|中提琴|编写|Talkbox|钢琴|出版|录音|发行|出品|键盘|弦乐|设计|监制|原曲|演唱|声明|版权|封面|插画|统筹|企划|填词|原唱|后期|和音|琵琶).*[::]\s*(?.*)/ ) ) { continue } for (const timestamp of lyricTimestamps.matchAll(extractTimestampRegex)) { const { min, sec, ms } = timestamp.groups as { min: string sec: string ms: string } const rawTime = timestamp[0] const time = Number(min) * 60 + Number(sec) + Number(ms ?? 0) * 0.001 const parsedLyric = { rawTime, time, content: trimContent(content) } parsedLyrics.splice(binarySearch(parsedLyric), 0, parsedLyric) } } return parsedLyrics }