UNPKG

paroles

Version:

A library for parsing, making, modifying and playing LRC format lyrics

288 lines (287 loc) 8.71 kB
var C = Object.defineProperty; var I = (r, e, t) => e in r ? C(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t; var h = (r, e, t) => (I(r, typeof e != "symbol" ? e + "" : e, t), t); const g = ` `, A = `\r `; function S(r) { const e = Array.from(r.matchAll(/\n/g)).length; return e === 0 ? g : Array.from(r.matchAll(/\r\n/g)).length === e ? A : g; } const y = (r, e) => { const [t, i] = r.toString().split("."); return t.length < e ? new Array(e - t.length).fill("0").join("") + t + (i ? "." + i : "") : t + (i ? "." + i : ""); }, $ = /^\[[^:]+:[^\]]*\]/, x = /\[(\d+):(\d+)(?:.(\d+))?\]/g, p = /\[([a-z]+):([^\]]*)\]/, E = { resolveConflict: "merge" }, j = { eol: g, resolveConflict: "merge" }; class a { constructor(e, t) { h(this, "_offset"); h(this, "_option"); h(this, "eol"); h(this, "info"); h(this, "lines"); if (this._option = { ...E, ...t || {} }, typeof e == "string") { this.eol = S(e); const i = a.parse(e, { eol: this.eol, resolveConflict: this._option.resolveConflict }); this.info = i.info, this.lines = i.lines, this._offset = i.info.offset || 0; } else if (typeof e > "u") this.eol = g, this.info = {}, this.lines = [], this._offset = 0; else { const i = e.clone(); this.eol = i.eol, this.info = i.info, this.lines = i.lines, this._offset = i.info.offset || 0, this._option = i._option; } } clone() { return new a(this.toString()); } toString() { return a.stringify(this); } at(e) { return this.lines.at(e); } getIndexByTime(e) { const t = this.lines.findIndex((i) => i.time - this._offset > e); return t <= 0 ? t : t - 1; } atTime(e) { const t = this.getIndexByTime(e); return this.lines.at(t); } setOffset(e) { this._offset = e; } merge(e, t) { const i = new a(e); t != null && t.resolveInfo ? this.info = t.resolveInfo(this.info, i.info) : t != null && t.override ? this.info = { ...this.info, ...i.info } : this.info = { ...i.info, ...this.info }; for (const s of i.lines) { let n = this.getIndexByTime(s.time); if (n = n === -1 ? this.lines.length - 1 : n, this.lines[n].time === s.time) if (t != null && t.resolveConflict) this.lines[n] = { time: s.time, text: t.resolveConflict(this.lines[n].text, s.text) }; else if (t != null && t.override) this.lines[n] = { time: s.time, text: s.text }; else continue; else n === this.lines.length - 1 ? this.lines.push(s) : n === 0 && s.time < this.lines[n].time ? this.lines.unshift(s) : this.lines.splice(n + 1, 0, s); } return this; } insert(e) { const t = Array.isArray(e) ? e : [e]; for (const i of t) { let s = this.getIndexByTime(i.time); s = s === -1 ? this.lines.length - 1 : s, this.lines[s].time === i.time ? this.lines[s] = i : s === this.lines.length - 1 ? this.lines.push(i) : s === 0 && i.time < this.lines[s].time ? this.lines.unshift(i) : this.lines.splice(s + 1, 0, i); } return this; } remove(e) { if (typeof e == "string") this.lines = this.lines.filter((t) => t.text !== e); else if (e instanceof RegExp) this.lines = this.lines.filter((t) => !e.test(t.text)); else { let t = this.getIndexByTime(e.time); t = t === -1 ? this.lines.length - 1 : t, this.lines[t].time === e.time && this.lines[t].text === e.text ? this.lines.splice(t, 1) : console.error("lyrics line not existed"); } return this; } replace(e, t) { if ((typeof e == "string" || e instanceof RegExp) && typeof t == "string") { const i = this.lines.filter((s) => typeof e == "string" ? s.text === e : e.test(s.text)); i.length && i.forEach((s) => { typeof e == "string" ? s.text = t : s.text = s.text.replace(e, t); }); } else if (d(e) && d(t)) { const i = []; this.lines.forEach((s, n) => { s.text === e.text && s.time === e.time && i.push(n); }), i.forEach((s) => { this.lines[s] = t; }); } return this; } setInfo(e) { return this.info = { ...this.info, ...e }, this; } static stringify(e) { const t = []; for (const [n, f] of Object.entries(e.info)) { const c = B(n); t.push(`[${c}:${f}]`); } const i = {}; for (const n of e.lines) i[n.text] ? i[n.text].push(n.time) : i[n.text] = [n.time]; const s = Object.entries(i).map(([n, f]) => ({ text: n, time: f })).sort((n, f) => n.time[0] - f.time[0]).map((n) => `${n.time.map((c) => `[${O(c)}]`).join("")}${n.text}`); return t.push(...s), t.join(e.eol); } static parse(e, t) { const i = { ...j, ...t || {} }, s = e.split(i.eol).map((c) => c.trim()).filter((c) => $.test(c)), n = {}, f = []; for (const c of s) if (x.test(c)) { const o = c.replace(x, "").trim(); Array.from(c.matchAll(x)).forEach((u) => { const m = +u[1], v = +u[2], b = (u[3] ? +`0.${u[3]}` : 0).toFixed(2), T = (m * 60 + v).toString(), _ = b.slice(1); f.push({ time: Number(T + _), text: o }); }); } else if (p.test(c)) { const o = c.match(p) || ["", ""], l = z(o[1].trim()), u = o[2].trim(); if (l) if (l !== "offset") n[l] = u || ""; else { const m = +u; n[l] = isNaN(m) ? 0 : +(m / 1e3).toFixed(2); } } return f.sort(function(c, o) { return c.time - o.time; }), f.forEach((c, o, l) => { !l[o + 1] || c.time !== l[o + 1].time || (i.resolveConflict === "merge" ? l[o].text = `${l[o].text}${i.eol}${l[o + 1].text}` : i.resolveConflict === "overwrite" ? l[o].text = l[o + 1].text : typeof i.resolveConflict == "function" && (l[o].text = i.resolveConflict(l[o].text, l[o + 1].text)), l.splice(o + 1, 1)); }), { info: n, lines: f }; } } function z(r) { switch (r) { case "al": return "album"; case "ar": return "artist"; case "au": return "author"; case "ti": return "title"; case "by": return "creator"; case "offset": return "offset"; case "length": return "length"; case "re": return "editor"; case "ve": return "version"; default: return; } } function B(r) { switch (r) { case "album": return "al"; case "artist": return "ar"; case "author": return "au"; case "title": return "ti"; case "creator": return "by"; case "offset": return "offset"; case "length": return "length"; case "editor": return "re"; case "version": return "ve"; default: return ""; } } function O(r) { const e = y(Math.floor(r / 60), 2), t = y((r % 60).toFixed(2), 2); return `${e}:${t}`; } function d(r) { return !!r && typeof r == "object" && "text" in r && "time" in r; } class F { constructor(e) { h(this, "lyrics"); h(this, "currentTime"); h(this, "_currentLine"); h(this, "_subscriptions"); this.lyrics = e, this.currentTime = 0, this._subscriptions = { linechange: [], lyricschange: [] }; } updateTime(e) { this.currentTime = e, this._currentLine !== this.getCurrentLine() && (this._currentLine = this.getCurrentLine(), this._subscriptions.linechange.forEach((t) => { const i = this.getCurrentIndex(), s = this.lyrics.lines.at(i); t( (s == null ? void 0 : s.text) || "", i === -1 ? this.lyrics.lines.length - 1 : i ); })); } getCurrentLine() { var e; return ((e = this.lyrics.atTime(this.currentTime)) == null ? void 0 : e.text) || ""; } getCurrentIndex() { return this.lyrics.getIndexByTime(this.currentTime); } on(...[e, t]) { e === "linechange" ? this._subscriptions.linechange.push(t) : e === "lyricschange" && this._subscriptions.lyricschange.push(t); } off(e, t) { if (!e) { this._subscriptions = { linechange: [], lyricschange: [] }; return; } const i = this._subscriptions[e].findIndex((s) => s === t); i > -1 ? this._subscriptions.linechange.splice(i, 1) : t ? console.error("LyricsPlayer.off(): handler not correct.") : this._subscriptions.linechange = []; } rewind(e) { this.currentTime = 0, this._currentLine = void 0, e && (this.lyrics = e, this._subscriptions.lyricschange.forEach((t) => t())); } } export { a as Lyrics, F as LyricsPlayer };