UNPKG

native-lyrics-tools

Version:

A JavaScript library for parsing and generating various lyric formats.

115 lines (105 loc) 3.14 kB
// LyricWord and LyricLine should be compatible with your LRC and QRC modules class LyricWord { constructor(word, start_time, end_time) { this.word = word; this.start_time = start_time; this.end_time = end_time; } } class LyricLine { constructor(words = []) { this.words = words; } } // Parses a [mm:ss.xx] timestamp (e.g. [00:10.97]) to milliseconds function parseTimestamp(str) { const match = str.match(/^\[(\d+):(\d+)[.:](\d{1,3})]/); if (!match) throw new Error(`Invalid timestamp: ${str}`); const min = parseInt(match[1], 10); const sec = parseInt(match[2], 10); let ms = match[3].padEnd(3, '0'); ms = parseInt(ms, 10); return { time: min * 60000 + sec * 1000 + ms, consumed: match[0].length }; } // Parse a single ESLyric line (e.g. [00:10.82]Test[00:10.97] Word[00:12.62]) function parseEslrcLine(line) { let src = line.trim(); // Initial timestamp const first = parseTimestamp(src); let pos = first.consumed; let start_time = first.time; const words = []; // parse word[timestamp] pairs while (pos < src.length) { // Get text up to next [ const nextBracket = src.indexOf('[', pos); if (nextBracket === -1) break; const word = src.slice(pos, nextBracket).trim(); const t = parseTimestamp(src.slice(nextBracket)); const end_time = t.time; if (word) { words.push(new LyricWord(word, start_time, end_time)); } start_time = end_time; pos = nextBracket + t.consumed; } return new LyricLine(words); } // Parse full ESLRC string function parseESLRC(src) { const lines = src.split(/\r?\n/); const result = []; for (const line of lines) { const trimmed = line.trim(); if (!trimmed) continue; try { const lyricLine = parseEslrcLine(trimmed); if (lyricLine.words.length > 0) { result.push(lyricLine); } } catch (e) { // ignore malformed lines } } processLyrics(result); return result; } // In-place adjust end_time for lines/words if needed (for compatibility) function processLyrics(lines) { // For ESLRC, word end_times are explicit; you may want to set line end_times as the last word end_time for (const line of lines) { if (line.words.length) { line.end_time = line.words[line.words.length - 1].end_time; } } } // Write LRC-style timestamp function writeTimestamp(ms) { const min = Math.floor(ms / 60000); const sec = Math.floor(ms / 1000) % 60; const ms3 = String(ms % 1000).padStart(3, '0'); return `[${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}.${ms3}]`; } // Stringify ESLRC function stringifyESLRC(lines) { let result = ''; for (const line of lines) { if (!line.words.length) continue; result += writeTimestamp(line.words[0].start_time); for (const word of line.words) { result += word.word + writeTimestamp(word.end_time); } result += '\n'; } return result; } // ES module exports export { LyricWord, LyricLine, parseESLRC, stringifyESLRC };