@music-lyric-player/base 0.8.0 → 0.10.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/README.md CHANGED
@@ -0,0 +1,59 @@
1
+ # @music-lyric-player/base
2
+
3
+ > Headless timing and state engine for `music-lyric-player`.
4
+
5
+ Owns playback time, active-line tracking, and event emission. Pure logic with **zero DOM dependencies** — pair it with [`@music-lyric-player/dom`](https://github.com/music-lyric/music-lyric-player-web/tree/main/packages/dom) for the default renderer, or plug in your own.
6
+
7
+ Part of [music-lyric-player-web](https://github.com/music-lyric/music-lyric-player-web).
8
+
9
+ ## Install
10
+
11
+ ```shell
12
+ npm install @music-lyric-player/base music-lyric-kit
13
+ ```
14
+
15
+ ## Events
16
+
17
+ | Event | Payload | Fires when |
18
+ | ------------- | -------------------------------------- | -------------------------- |
19
+ | `play` | `(currentTime: number)` | Playback starts or resumes |
20
+ | `pause` | `(currentTime: number)` | Playback pauses |
21
+ | `lyricUpdate` | `(info: Info)` | A new lyric is loaded |
22
+ | `linesUpdate` | `(lines, indexes, firstIndex, isSeek)` | Active lines change |
23
+
24
+ ## Usage
25
+
26
+ ```js
27
+ import { BaseLyricPlayer } from '@music-lyric-player/base'
28
+ import { ParserPipeline } from 'music-lyric-kit'
29
+
30
+ const base = new BaseLyricPlayer()
31
+
32
+ base.event.add('play', (time) => {})
33
+ base.event.add('pause', (time) => {})
34
+ base.event.add('lyricUpdate', (info) => {})
35
+ base.event.add('linesUpdate', (lines, indexes, firstIndex, isSeek) => {})
36
+
37
+ const { result } = new ParserPipeline({
38
+ content: { original: '[00:01.114]Hello world' },
39
+ format: 'lrc',
40
+ })
41
+ .parse()
42
+ .pureClean()
43
+ .interludeInsert()
44
+ .spaceInsert()
45
+ .final()
46
+
47
+ base.updateLyric(result)
48
+ base.play(0)
49
+ ```
50
+
51
+ ## Options
52
+
53
+ | Field | Type | Default | Description |
54
+ | -------- | ------------------------ | --------- | ------------------------------------------------------------------------------------ |
55
+ | `driver` | `'animation' \| 'timer'` | `'timer'` | Tick source. `animation` uses `requestAnimationFrame`; `timer` uses `setTimeout(16)` |
56
+
57
+ ```js
58
+ base.config.update({ driver: 'animation' })
59
+ ```
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require("lodash-es");const r=require("@music-lyric-player/utils"),d={driver:"timer"},u="0.5.1";class l{get version(){return u}type="Incorrect";metas=[];lines=[];agents=[];toJSON(){return{version:this.version,type:this.type,metas:this.metas,lines:this.lines,agents:this.agents}}}class o{config=new r.ConfigManager(d);event=new r.Event;state;active;time;info;constructor(){this.state={playing:!1,frameId:null,timerId:null,scanIndex:0},this.active={lines:[],index:[]},this.time={start:0,seek:0},this.info=new l}handleGetCurrentTime(){return this.state.playing?this.time.seek+(performance.now()-this.time.start):this.time.seek}handleGetLineTime(e){if(e<0||e>=this.info.lines.length)return 0;if(e===this.info.lines.length-1)return 1/0;const s=this.info.lines[e],n=this.info.lines[e+1];return Math.max(s.time.end,n.time.start)}handleGetActiveIndex(){return this.active.index.length>0?this.active.index[0]:-1}handleSyncTime(e){const s=[],n=[];let i=this.info.lines.length;for(let t=0;t<this.info.lines.length;t++){const a=this.info.lines[t];if(a.time.start>e){i=t;break}this.handleGetLineTime(t)>e&&(s.push(a),n.push(t))}this.state.scanIndex=i,this.active.lines=s,this.active.index=n,this.event.emit("linesUpdate",[...this.active.lines],[...this.active.index],this.handleGetActiveIndex(),!0)}handleUpdateActiveLines(e){let s=!1;const n=[],i=[];for(let t=0;t<this.active.lines.length;t++){const a=this.active.lines[t],h=this.active.index[t];e>=this.handleGetLineTime(h)?s=!0:(n.push(a),i.push(h))}for(;this.state.scanIndex<this.info.lines.length;){const t=this.info.lines[this.state.scanIndex];if(e>=t.time.start)e<this.handleGetLineTime(this.state.scanIndex)&&(n.push(t),i.push(this.state.scanIndex),s=!0),this.state.scanIndex++;else break}s&&(this.active.lines=n,this.active.index=i,this.event.emit("linesUpdate",[...this.active.lines],[...this.active.index],this.handleGetActiveIndex(),!1))}onTick=()=>{if(!this.state.playing)return;const e=this.handleGetCurrentTime();switch(this.handleUpdateActiveLines(e),this.config.current.driver){case"animation":this.state.frameId=window.requestAnimationFrame(this.onTick);break;case"timer":this.state.timerId=window.setTimeout(this.onTick,16);break}};updateLyric(e){e&&(this.pause(),this.info=e,this.active.lines=[],this.active.index=[],this.state.scanIndex=0,this.time.seek=0,this.event.emit("lyricUpdate",e),this.event.emit("linesUpdate",[],[],-1,!1))}play(e){this.pause(),typeof e=="number"&&!Number.isNaN(e)&&(this.time.seek=e,this.handleSyncTime(e)),this.time.start=performance.now(),this.state.playing=!0,this.onTick(),this.event.emit("play",this.handleGetCurrentTime())}pause(){this.state.playing&&(this.time.seek=this.handleGetCurrentTime(),this.state.playing=!1),this.state.frameId!==null&&(cancelAnimationFrame(this.state.frameId),this.state.frameId=null),this.state.timerId!==null&&(clearTimeout(this.state.timerId),this.state.timerId=null),this.event.emit("pause",this.handleGetCurrentTime())}dispose(){this.pause(),this.event.clear(),this.active.lines=[],this.active.index=[],this.info=new l}matchLinesWithTime(e){const s=[],n=[];for(let i=0;i<this.info.lines.length;i++){const t=this.info.lines[i];if(t.time.start>e)break;this.handleGetLineTime(i)>e&&(s.push(t),n.push(i))}return{lines:s,index:n}}get currentPlaying(){return this.state.playing}get currentLines(){return[...this.active.lines]}get currentIndex(){return[...this.active.index]}get currentActive(){return this.handleGetActiveIndex()}get currentInfo(){return this.info}get currentTime(){return this.handleGetCurrentTime()}}exports.BaseLyricPlayer=o;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("@music-lyric-player/utils");require("lodash-es");const o=l.freezeObjectDeep({driver:"animation",bridgeActive:!0}),f=Object.freeze(Object.defineProperty({__proto__:null,DEFAULT:o},Symbol.toStringTag,{value:"Module"})),m="0.5.1";class d{get version(){return m}type="Incorrect";metas=[];lines=[];agents=[];toJSON(){return{version:this.version,type:this.type,metas:this.metas,lines:this.lines,agents:this.agents}}}class g{config=new l.ConfigManager(o);event=new l.Event;state;active;time;info;constructor(){this.state={playing:!1,frameId:null,timerId:null,scanIndex:0},this.active={lines:[],index:[]},this.time={start:0,seek:0},this.info=new d}handleGetCurrentTime(){return this.state.playing?this.time.seek+(performance.now()-this.time.start):this.time.seek}handleGetLineTime(e){if(e<0||e>=this.info.lines.length)return 0;if(e===this.info.lines.length-1)return 1/0;const t=this.info.lines[e],s=this.info.lines[e+1];return Math.max(t.time.end,s.time.start)}handleGetActiveIndex(){return this.active.index.length>0?this.active.index[0]:-1}handleBridgeActive(e,t){if(!this.config.current.bridgeActive||t.length<2)return{lines:e,index:t};const s=t[0],n=t[t.length-1];if(n-s+1===t.length)return{lines:e,index:t};const i=new Map;for(let a=0;a<t.length;a++)i.set(t[a],e[a]);const r=[],h=[];for(let a=s;a<=n;a++){const c=i.get(a)??this.info.lines[a];c&&(r.push(c),h.push(a))}return{lines:r,index:h}}handleEmitLinesUpdate(e){const t=this.handleBridgeActive(this.active.lines,this.active.index);this.event.emit("linesUpdate",t.lines,t.index,this.handleGetActiveIndex(),e)}handleSyncTime(e){const t=[],s=[];let n=this.info.lines.length;for(let i=0;i<this.info.lines.length;i++){const r=this.info.lines[i];if(r.time.start>e){n=i;break}this.handleGetLineTime(i)>e&&(t.push(r),s.push(i))}this.state.scanIndex=n,this.active.lines=t,this.active.index=s,this.handleEmitLinesUpdate(!0)}handleUpdateActiveLines(e){let t=!1;const s=[],n=[];for(let i=0;i<this.active.lines.length;i++){const r=this.active.lines[i],h=this.active.index[i];e>=this.handleGetLineTime(h)?t=!0:(s.push(r),n.push(h))}for(;this.state.scanIndex<this.info.lines.length;){const i=this.info.lines[this.state.scanIndex];if(e>=i.time.start)e<this.handleGetLineTime(this.state.scanIndex)&&(s.push(i),n.push(this.state.scanIndex),t=!0),this.state.scanIndex++;else break}t&&(this.active.lines=s,this.active.index=n,this.handleEmitLinesUpdate(!1))}onTick=()=>{if(!this.state.playing)return;const e=this.handleGetCurrentTime();switch(this.handleUpdateActiveLines(e),this.config.current.driver){case"animation":this.state.frameId=window.requestAnimationFrame(this.onTick);break;case"timer":this.state.timerId=window.setTimeout(this.onTick,16);break}};updateLyric(e){e&&(this.pause(),this.info=e,this.active.lines=[],this.active.index=[],this.state.scanIndex=0,this.time.seek=0,this.event.emit("lyricUpdate",e),this.event.emit("linesUpdate",[],[],-1,!1))}play(e){this.pause(),typeof e=="number"&&!Number.isNaN(e)&&(this.time.seek=e,this.handleSyncTime(e)),this.time.start=performance.now(),this.state.playing=!0,this.onTick(),this.event.emit("play",this.handleGetCurrentTime())}pause(){this.state.playing&&(this.time.seek=this.handleGetCurrentTime(),this.state.playing=!1),this.state.frameId!==null&&(cancelAnimationFrame(this.state.frameId),this.state.frameId=null),this.state.timerId!==null&&(clearTimeout(this.state.timerId),this.state.timerId=null),this.event.emit("pause",this.handleGetCurrentTime())}dispose(){this.pause(),this.event.clear(),this.active.lines=[],this.active.index=[],this.info=new d}matchLinesWithTime(e){const t=[],s=[];for(let n=0;n<this.info.lines.length;n++){const i=this.info.lines[n];if(i.time.start>e)break;this.handleGetLineTime(n)>e&&(t.push(i),s.push(n))}return this.handleBridgeActive(t,s)}get currentPlaying(){return this.state.playing}get currentLines(){return this.handleBridgeActive(this.active.lines,this.active.index).lines}get currentIndex(){return this.handleBridgeActive(this.active.lines,this.active.index).index}get currentActive(){return this.handleGetActiveIndex()}get currentInfo(){return this.info}get currentTime(){return this.handleGetCurrentTime()}}exports.BaseLyricPlayer=g;exports.BaseLyricPlayerConfig=f;
2
2
  //# sourceMappingURL=index.comm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.comm.js","sources":["../src/options.ts","../../../node_modules/.pnpm/@music-lyric-kit+lyric@0.4.0/node_modules/@music-lyric-kit/lyric/dist/index.ecma.js","../src/index.ts"],"sourcesContent":["export interface Options {\n /**\n * Driver type for playback scheduling.\n * - `animation`: use requestAnimationFrame\n * - `timer`: use setTimeout\n * @default animation\n */\n driver: 'timer' | 'animation'\n}\n\nexport const DEFAULT_OPTIONS: Options = {\n driver: 'timer',\n}\n","import { createRandomHex as r } from \"@music-lyric-kit/utils\";\nclass s {\n start = 0;\n end = 0;\n get duration() {\n return this.end - this.start;\n }\n toJSON() {\n return {\n start: this.start,\n end: this.end,\n duration: this.duration\n };\n }\n}\nvar o = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Space = \"Space\", t))(o || {});\nclass i {\n stress = !1;\n toJSON() {\n return {\n stress: this.stress\n };\n }\n}\nclass p {\n get type() {\n return \"Normal\";\n }\n time = new s();\n content = \"\";\n extended = [];\n config = new i();\n toJSON() {\n return {\n type: this.type,\n time: this.time,\n content: this.content,\n extended: this.extended,\n config: this.config\n };\n }\n}\nclass N {\n get type() {\n return \"Space\";\n }\n count = 1;\n toJSON() {\n return {\n type: this.type,\n count: this.count\n };\n }\n}\nvar a = /* @__PURE__ */ ((t) => (t.UnKnown = \"UnKnown\", t.Translate = \"Translate\", t.Roman = \"Roman\", t))(a || {});\nclass S {\n type = \"UnKnown\";\n content = \"\";\n toJSON() {\n return {\n type: this.type,\n content: this.content\n };\n }\n}\nvar l = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Interlude = \"Interlude\", t))(l || {});\nclass x {\n get type() {\n return \"Interlude\";\n }\n id = r(4).toUpperCase();\n time = new s();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time\n };\n }\n}\nclass c {\n words = [];\n extended = [];\n get original() {\n return this.words.map((n) => n.type === o.Normal ? n.content : new Array(n.count).fill(\" \").join(\"\")).join(\"\");\n }\n toJSON() {\n return {\n words: this.words,\n extended: this.extended,\n original: this.original\n };\n }\n}\nclass y {\n get type() {\n return \"Normal\";\n }\n id = r(4).toUpperCase();\n time = new s();\n content = new c();\n agent;\n background;\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time,\n content: this.content,\n agent: this.agent,\n background: this.background\n };\n }\n}\nvar u = /* @__PURE__ */ ((t) => (t.Offset = \"Offset\", t.Duration = \"Duration\", t.Title = \"Title\", t.Singer = \"Singer\", t.Album = \"Album\", t.Creator = \"Creator\", t.UnKnown = \"UnKnown\", t))(u || {});\nclass e {\n id = r(4).toUpperCase();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n content: this.content\n };\n }\n}\nclass f extends e {\n get type() {\n return \"Offset\";\n }\n content = 0;\n}\nclass w extends e {\n get type() {\n return \"Duration\";\n }\n content = 0;\n}\nclass O extends e {\n get type() {\n return \"Title\";\n }\n content = \"\";\n}\nclass b extends e {\n get type() {\n return \"Singer\";\n }\n content = \"\";\n}\nclass J extends e {\n get type() {\n return \"Album\";\n }\n content = \"\";\n}\nclass U extends e {\n get type() {\n return \"Creator\";\n }\n content = {\n role: \"\",\n name: []\n };\n}\nclass C extends e {\n get type() {\n return \"UnKnown\";\n }\n content = \"\";\n}\nclass I {\n id = \"\";\n name = \"\";\n count = 0;\n}\nclass d {\n // index in global\n global = 0;\n // index in block\n block = 0;\n toJSON() {\n return {\n global: this.global,\n block: this.block\n };\n }\n}\nclass v {\n id = \"\";\n index = new d();\n toJSON() {\n return {\n id: this.id,\n index: this.index\n };\n }\n}\nconst m = \"0.5.1\";\nvar g = /* @__PURE__ */ ((t) => (t.Incorrect = \"Incorrect\", t.Normal = \"Normal\", t.Syllable = \"Syllable\", t.Pure = \"Pure\", t.Empty = \"Empty\", t.NoTime = \"NoTime\", t))(g || {});\nclass A {\n get version() {\n return m;\n }\n type = \"Incorrect\";\n metas = [];\n lines = [];\n agents = [];\n toJSON() {\n return {\n version: this.version,\n type: this.type,\n metas: this.metas,\n lines: this.lines,\n agents: this.agents\n };\n }\n}\nexport {\n I as Agent,\n v as AgentLine,\n d as AgentLineIndex,\n S as Extended,\n a as ExtendedType,\n A as Info,\n x as LineInterlude,\n y as LineNormal,\n c as LineNormalContent,\n l as LineType,\n J as MetaAlbum,\n U as MetaCreator,\n w as MetaDuration,\n f as MetaOffset,\n b as MetaSinger,\n O as MetaTitle,\n u as MetaType,\n C as MetaUnKnown,\n s as Time,\n g as Type,\n m as Version,\n p as WordNormal,\n N as WordSpace,\n o as WordType\n};\n//# sourceMappingURL=index.ecma.js.map\n","import type { Line } from '@music-lyric-kit/lyric'\nimport type { DeepPartial } from '@music-lyric-player/utils'\nimport type { Options } from './options'\n\nimport { DEFAULT_OPTIONS } from './options'\n\nimport { Info } from '@music-lyric-kit/lyric'\nimport { ConfigManager, Event } from '@music-lyric-player/utils'\n\nexport interface BaseLyricPlayerEventMap {\n /**\n * When the player starts or resumes playback.\n * @param currentTime The current playback time.\n */\n play: (currentTime: number) => void\n\n /**\n * When the player pauses playback.\n * @param currentTime The current playback time.\n */\n pause: (currentTime: number) => void\n\n /**\n * When the entire lyric information is updated (e.g., loading a new lyric/song).\n * @param info The newly loaded lyric information object.\n */\n lyricUpdate: (info: Info) => void\n\n /**\n * When the currently active lyric lines change during playback.\n * @param lines An array of the currently active lyric lines.\n * @param indexs An array of the currently active lyric lines' indexes.\n * @param firstIndex The index of the first currently active lyric line (-1 if none).\n * @param isSeek Is seek.\n */\n linesUpdate: (lines: Line[], indexs: number[], index: number, isSeek: boolean) => void\n}\n\nexport class BaseLyricPlayer {\n readonly config = new ConfigManager<Options, DeepPartial<Options>>(DEFAULT_OPTIONS)\n\n readonly event: Event<BaseLyricPlayerEventMap> = new Event()\n\n private state: {\n playing: boolean\n frameId: number | null\n timerId: number | null\n scanIndex: number\n }\n private active: {\n lines: Line[]\n index: number[]\n }\n private time: {\n start: number\n seek: number\n }\n private info: Info\n\n constructor() {\n this.state = {\n playing: false,\n frameId: null,\n timerId: null,\n scanIndex: 0,\n }\n this.active = {\n lines: [],\n index: [],\n }\n this.time = {\n start: 0,\n seek: 0,\n }\n this.info = new Info()\n }\n\n private handleGetCurrentTime() {\n if (!this.state.playing) {\n return this.time.seek\n }\n return this.time.seek + (performance.now() - this.time.start)\n }\n\n private handleGetLineTime(index: number): number {\n if (index < 0 || index >= this.info.lines.length) {\n return 0\n }\n\n if (index === this.info.lines.length - 1) {\n return Infinity\n }\n\n const line = this.info.lines[index]\n const nextLine = this.info.lines[index + 1]\n return Math.max(line.time.end, nextLine.time.start)\n }\n\n private handleGetActiveIndex() {\n return this.active.index.length > 0 ? this.active.index[0] : -1\n }\n\n private handleSyncTime(time: number) {\n const lines: Line[] = []\n const index: number[] = []\n\n let firstIndex = this.info.lines.length\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n firstIndex = i\n break\n }\n\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n\n this.state.scanIndex = firstIndex\n\n this.active.lines = lines\n this.active.index = index\n\n this.event.emit('linesUpdate', [...this.active.lines], [...this.active.index], this.handleGetActiveIndex(), true)\n }\n\n private handleUpdateActiveLines(now: number) {\n let hasChanged = false\n\n const newActiveLines: Line[] = []\n const newActiveIndex: number[] = []\n\n for (let i = 0; i < this.active.lines.length; i++) {\n const line = this.active.lines[i]\n const infoIndex = this.active.index[i]\n\n if (now >= this.handleGetLineTime(infoIndex)) {\n hasChanged = true\n } else {\n newActiveLines.push(line)\n newActiveIndex.push(infoIndex)\n }\n }\n\n while (this.state.scanIndex < this.info.lines.length) {\n const nextLine = this.info.lines[this.state.scanIndex]\n if (now >= nextLine.time.start) {\n if (now < this.handleGetLineTime(this.state.scanIndex)) {\n newActiveLines.push(nextLine)\n newActiveIndex.push(this.state.scanIndex)\n hasChanged = true\n }\n this.state.scanIndex++\n } else {\n break\n }\n }\n\n if (!hasChanged) {\n return\n }\n\n this.active.lines = newActiveLines\n this.active.index = newActiveIndex\n this.event.emit('linesUpdate', [...this.active.lines], [...this.active.index], this.handleGetActiveIndex(), false)\n }\n\n private onTick = () => {\n if (!this.state.playing) {\n return\n }\n\n const now = this.handleGetCurrentTime()\n this.handleUpdateActiveLines(now)\n\n switch (this.config.current.driver) {\n case 'animation':\n this.state.frameId = window.requestAnimationFrame(this.onTick)\n break\n case 'timer':\n this.state.timerId = window.setTimeout(this.onTick, 16)\n break\n }\n }\n\n updateLyric(info: Info) {\n if (!info) {\n return\n }\n\n this.pause()\n this.info = info\n\n this.active.lines = []\n this.active.index = []\n\n this.state.scanIndex = 0\n this.time.seek = 0\n\n this.event.emit('lyricUpdate', info)\n this.event.emit('linesUpdate', [], [], -1, false)\n }\n\n /**\n * Start playback\n * @param time Optional time in ms to seek to before starting playback. If not provided, playback will start from the current position.\n */\n play(time?: number) {\n this.pause()\n\n if (typeof time === 'number' && !Number.isNaN(time)) {\n this.time.seek = time\n this.handleSyncTime(time)\n }\n\n this.time.start = performance.now()\n this.state.playing = true\n this.onTick()\n\n this.event.emit('play', this.handleGetCurrentTime())\n }\n\n /**\n * Pause playback\n */\n pause() {\n if (this.state.playing) {\n this.time.seek = this.handleGetCurrentTime()\n this.state.playing = false\n }\n if (this.state.frameId !== null) {\n cancelAnimationFrame(this.state.frameId)\n this.state.frameId = null\n }\n if (this.state.timerId !== null) {\n clearTimeout(this.state.timerId)\n this.state.timerId = null\n }\n\n this.event.emit('pause', this.handleGetCurrentTime())\n }\n\n /**\n * Stop playback\n */\n dispose() {\n this.pause()\n this.event.clear()\n\n this.active.lines = []\n this.active.index = []\n\n this.info = new Info()\n }\n\n /**\n * Find all active lines at the given time (ms). Does not mutate internal state.\n * @param time time in ms to find active lines for.\n */\n matchLinesWithTime(time: number): { lines: Line[]; index: number[] } {\n const lines: Line[] = []\n const index: number[] = []\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n break\n }\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n return { lines, index }\n }\n\n /**\n * Whether the player is currently playing.\n */\n get currentPlaying() {\n return this.state.playing\n }\n\n /**\n * Current active lines.\n */\n get currentLines() {\n return [...this.active.lines]\n }\n\n /**\n * Indices of currently active lines.\n */\n get currentIndex() {\n return [...this.active.index]\n }\n\n /**\n * The index of the primary active line, or -1 if none.\n */\n get currentActive() {\n return this.handleGetActiveIndex()\n }\n\n /**\n * The current lyric info object.\n */\n get currentInfo() {\n return this.info\n }\n\n /**\n * The current playback time in ms.\n */\n get currentTime() {\n return this.handleGetCurrentTime()\n }\n}\n"],"names":["DEFAULT_OPTIONS","m","A","BaseLyricPlayer","ConfigManager","Event","Info","index","line","nextLine","time","lines","firstIndex","i","now","hasChanged","newActiveLines","newActiveIndex","infoIndex","info"],"mappings":"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","x_google_ignoreList":[1]}
1
+ {"version":3,"file":"index.comm.js","sources":["../src/config/root.ts","../../../node_modules/.pnpm/@music-lyric-kit+lyric@0.4.0/node_modules/@music-lyric-kit/lyric/dist/index.ecma.js","../src/core.ts"],"sourcesContent":["import type { ConfigManager, DeepRequired, NestedKeys } from '@music-lyric-player/utils'\n\nimport { freezeObjectDeep } from '@music-lyric-player/utils'\n\nexport interface Root {\n /**\n * Driver type for playback scheduling.\n * - `animation`: use requestAnimationFrame\n * - `timer`: use setTimeout\n *\n * @default 'animation'\n */\n driver?: 'timer' | 'animation'\n /**\n * Whether to bridge gaps between simultaneously active lines.\n *\n * Active lines may not be consecutive — a line in the middle could have already ended (`played`) while its neighbors are still active, producing a `[active, played, active]` pattern.\n *\n * Enabling this promotes the sandwiched lines back to active so the visual reads as one continuous `[active, active, active]` block.\n *\n * @default true\n */\n bridgeActive?: boolean\n}\n\n/**\n * Fully‑resolved config in which every field is required, produced by deep‑requiring the user‑facing {@link Config}.\n */\nexport type RootRequired = DeepRequired<Root>\n\n/**\n * All config keys\n */\nexport type RootKeys = NestedKeys<Root>\n\n/**\n * Set of config key paths that changed since the previous value, as emitted by `ConfigManager.event['update']`.\n */\nexport type RootKeySet = Set<RootKeys>\n\n/**\n * Runtime config manager bound to this module's {@link Config} shape.\n */\nexport type RootManager = ConfigManager<RootRequired, Root>\n\nexport const DEFAULT: Root = freezeObjectDeep({\n driver: 'animation',\n bridgeActive: true,\n})\n","import { createRandomHex as r } from \"@music-lyric-kit/utils\";\nclass s {\n start = 0;\n end = 0;\n get duration() {\n return this.end - this.start;\n }\n toJSON() {\n return {\n start: this.start,\n end: this.end,\n duration: this.duration\n };\n }\n}\nvar o = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Space = \"Space\", t))(o || {});\nclass i {\n stress = !1;\n toJSON() {\n return {\n stress: this.stress\n };\n }\n}\nclass p {\n get type() {\n return \"Normal\";\n }\n time = new s();\n content = \"\";\n extended = [];\n config = new i();\n toJSON() {\n return {\n type: this.type,\n time: this.time,\n content: this.content,\n extended: this.extended,\n config: this.config\n };\n }\n}\nclass N {\n get type() {\n return \"Space\";\n }\n count = 1;\n toJSON() {\n return {\n type: this.type,\n count: this.count\n };\n }\n}\nvar a = /* @__PURE__ */ ((t) => (t.UnKnown = \"UnKnown\", t.Translate = \"Translate\", t.Roman = \"Roman\", t))(a || {});\nclass S {\n type = \"UnKnown\";\n content = \"\";\n toJSON() {\n return {\n type: this.type,\n content: this.content\n };\n }\n}\nvar l = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Interlude = \"Interlude\", t))(l || {});\nclass x {\n get type() {\n return \"Interlude\";\n }\n id = r(4).toUpperCase();\n time = new s();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time\n };\n }\n}\nclass c {\n words = [];\n extended = [];\n get original() {\n return this.words.map((n) => n.type === o.Normal ? n.content : new Array(n.count).fill(\" \").join(\"\")).join(\"\");\n }\n toJSON() {\n return {\n words: this.words,\n extended: this.extended,\n original: this.original\n };\n }\n}\nclass y {\n get type() {\n return \"Normal\";\n }\n id = r(4).toUpperCase();\n time = new s();\n content = new c();\n agent;\n background;\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time,\n content: this.content,\n agent: this.agent,\n background: this.background\n };\n }\n}\nvar u = /* @__PURE__ */ ((t) => (t.Offset = \"Offset\", t.Duration = \"Duration\", t.Title = \"Title\", t.Singer = \"Singer\", t.Album = \"Album\", t.Creator = \"Creator\", t.UnKnown = \"UnKnown\", t))(u || {});\nclass e {\n id = r(4).toUpperCase();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n content: this.content\n };\n }\n}\nclass f extends e {\n get type() {\n return \"Offset\";\n }\n content = 0;\n}\nclass w extends e {\n get type() {\n return \"Duration\";\n }\n content = 0;\n}\nclass O extends e {\n get type() {\n return \"Title\";\n }\n content = \"\";\n}\nclass b extends e {\n get type() {\n return \"Singer\";\n }\n content = \"\";\n}\nclass J extends e {\n get type() {\n return \"Album\";\n }\n content = \"\";\n}\nclass U extends e {\n get type() {\n return \"Creator\";\n }\n content = {\n role: \"\",\n name: []\n };\n}\nclass C extends e {\n get type() {\n return \"UnKnown\";\n }\n content = \"\";\n}\nclass I {\n id = \"\";\n name = \"\";\n count = 0;\n}\nclass d {\n // index in global\n global = 0;\n // index in block\n block = 0;\n toJSON() {\n return {\n global: this.global,\n block: this.block\n };\n }\n}\nclass v {\n id = \"\";\n index = new d();\n toJSON() {\n return {\n id: this.id,\n index: this.index\n };\n }\n}\nconst m = \"0.5.1\";\nvar g = /* @__PURE__ */ ((t) => (t.Incorrect = \"Incorrect\", t.Normal = \"Normal\", t.Syllable = \"Syllable\", t.Pure = \"Pure\", t.Empty = \"Empty\", t.NoTime = \"NoTime\", t))(g || {});\nclass A {\n get version() {\n return m;\n }\n type = \"Incorrect\";\n metas = [];\n lines = [];\n agents = [];\n toJSON() {\n return {\n version: this.version,\n type: this.type,\n metas: this.metas,\n lines: this.lines,\n agents: this.agents\n };\n }\n}\nexport {\n I as Agent,\n v as AgentLine,\n d as AgentLineIndex,\n S as Extended,\n a as ExtendedType,\n A as Info,\n x as LineInterlude,\n y as LineNormal,\n c as LineNormalContent,\n l as LineType,\n J as MetaAlbum,\n U as MetaCreator,\n w as MetaDuration,\n f as MetaOffset,\n b as MetaSinger,\n O as MetaTitle,\n u as MetaType,\n C as MetaUnKnown,\n s as Time,\n g as Type,\n m as Version,\n p as WordNormal,\n N as WordSpace,\n o as WordType\n};\n//# sourceMappingURL=index.ecma.js.map\n","import type { Line } from '@music-lyric-kit/lyric'\nimport type { BaseLyricPlayerEventMap } from './interface'\n\nimport { Info } from '@music-lyric-kit/lyric'\nimport { ConfigManager, Event } from '@music-lyric-player/utils'\nimport { BaseLyricPlayerConfig } from './config'\n\nexport class BaseLyricPlayer {\n readonly config: BaseLyricPlayerConfig.RootManager = new ConfigManager(BaseLyricPlayerConfig.DEFAULT as BaseLyricPlayerConfig.RootRequired)\n\n readonly event: Event<BaseLyricPlayerEventMap> = new Event()\n\n private state: {\n playing: boolean\n frameId: number | null\n timerId: number | null\n scanIndex: number\n }\n private active: {\n lines: Line[]\n index: number[]\n }\n private time: {\n start: number\n seek: number\n }\n private info: Info\n\n constructor() {\n this.state = {\n playing: false,\n frameId: null,\n timerId: null,\n scanIndex: 0,\n }\n this.active = {\n lines: [],\n index: [],\n }\n this.time = {\n start: 0,\n seek: 0,\n }\n this.info = new Info()\n }\n\n private handleGetCurrentTime() {\n if (!this.state.playing) {\n return this.time.seek\n }\n return this.time.seek + (performance.now() - this.time.start)\n }\n\n private handleGetLineTime(index: number): number {\n if (index < 0 || index >= this.info.lines.length) {\n return 0\n }\n\n if (index === this.info.lines.length - 1) {\n return Infinity\n }\n\n const line = this.info.lines[index]\n const nextLine = this.info.lines[index + 1]\n return Math.max(line.time.end, nextLine.time.start)\n }\n\n private handleGetActiveIndex() {\n return this.active.index.length > 0 ? this.active.index[0] : -1\n }\n\n private handleBridgeActive(lines: Line[], index: number[]): { lines: Line[]; index: number[] } {\n if (!this.config.current.bridgeActive || index.length < 2) {\n return { lines, index }\n }\n const min = index[0]\n const max = index[index.length - 1]\n if (max - min + 1 === index.length) {\n return { lines, index }\n }\n const existing = new Map<number, Line>()\n for (let i = 0; i < index.length; i++) {\n existing.set(index[i], lines[i])\n }\n const bridgedLines: Line[] = []\n const bridgedIndex: number[] = []\n for (let i = min; i <= max; i++) {\n const line = existing.get(i) ?? this.info.lines[i]\n if (!line) continue\n bridgedLines.push(line)\n bridgedIndex.push(i)\n }\n return { lines: bridgedLines, index: bridgedIndex }\n }\n\n private handleEmitLinesUpdate(isSeek: boolean) {\n const bridged = this.handleBridgeActive(this.active.lines, this.active.index)\n this.event.emit('linesUpdate', bridged.lines, bridged.index, this.handleGetActiveIndex(), isSeek)\n }\n\n private handleSyncTime(time: number) {\n const lines: Line[] = []\n const index: number[] = []\n\n let firstIndex = this.info.lines.length\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n firstIndex = i\n break\n }\n\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n\n this.state.scanIndex = firstIndex\n\n this.active.lines = lines\n this.active.index = index\n\n this.handleEmitLinesUpdate(true)\n }\n\n private handleUpdateActiveLines(now: number) {\n let hasChanged = false\n\n const newActiveLines: Line[] = []\n const newActiveIndex: number[] = []\n\n for (let i = 0; i < this.active.lines.length; i++) {\n const line = this.active.lines[i]\n const infoIndex = this.active.index[i]\n\n if (now >= this.handleGetLineTime(infoIndex)) {\n hasChanged = true\n } else {\n newActiveLines.push(line)\n newActiveIndex.push(infoIndex)\n }\n }\n\n while (this.state.scanIndex < this.info.lines.length) {\n const nextLine = this.info.lines[this.state.scanIndex]\n if (now >= nextLine.time.start) {\n if (now < this.handleGetLineTime(this.state.scanIndex)) {\n newActiveLines.push(nextLine)\n newActiveIndex.push(this.state.scanIndex)\n hasChanged = true\n }\n this.state.scanIndex++\n } else {\n break\n }\n }\n\n if (!hasChanged) {\n return\n }\n\n this.active.lines = newActiveLines\n this.active.index = newActiveIndex\n this.handleEmitLinesUpdate(false)\n }\n\n private onTick = () => {\n if (!this.state.playing) {\n return\n }\n\n const now = this.handleGetCurrentTime()\n this.handleUpdateActiveLines(now)\n\n switch (this.config.current.driver) {\n case 'animation':\n this.state.frameId = window.requestAnimationFrame(this.onTick)\n break\n case 'timer':\n this.state.timerId = window.setTimeout(this.onTick, 16)\n break\n }\n }\n\n updateLyric(info: Info) {\n if (!info) {\n return\n }\n\n this.pause()\n this.info = info\n\n this.active.lines = []\n this.active.index = []\n\n this.state.scanIndex = 0\n this.time.seek = 0\n\n this.event.emit('lyricUpdate', info)\n this.event.emit('linesUpdate', [], [], -1, false)\n }\n\n /**\n * Start playback\n * @param time Optional time in ms to seek to before starting playback. If not provided, playback will start from the current position.\n */\n play(time?: number) {\n this.pause()\n\n if (typeof time === 'number' && !Number.isNaN(time)) {\n this.time.seek = time\n this.handleSyncTime(time)\n }\n\n this.time.start = performance.now()\n this.state.playing = true\n this.onTick()\n\n this.event.emit('play', this.handleGetCurrentTime())\n }\n\n /**\n * Pause playback\n */\n pause() {\n if (this.state.playing) {\n this.time.seek = this.handleGetCurrentTime()\n this.state.playing = false\n }\n if (this.state.frameId !== null) {\n cancelAnimationFrame(this.state.frameId)\n this.state.frameId = null\n }\n if (this.state.timerId !== null) {\n clearTimeout(this.state.timerId)\n this.state.timerId = null\n }\n\n this.event.emit('pause', this.handleGetCurrentTime())\n }\n\n /**\n * Stop playback\n */\n dispose() {\n this.pause()\n this.event.clear()\n\n this.active.lines = []\n this.active.index = []\n\n this.info = new Info()\n }\n\n /**\n * Find all active lines at the given time (ms). Does not mutate internal state.\n * @param time time in ms to find active lines for.\n */\n matchLinesWithTime(time: number): { lines: Line[]; index: number[] } {\n const lines: Line[] = []\n const index: number[] = []\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n break\n }\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n return this.handleBridgeActive(lines, index)\n }\n\n /**\n * Whether the player is currently playing.\n */\n get currentPlaying() {\n return this.state.playing\n }\n\n /**\n * Current active lines.\n */\n get currentLines() {\n return this.handleBridgeActive(this.active.lines, this.active.index).lines\n }\n\n /**\n * Indices of currently active lines.\n */\n get currentIndex() {\n return this.handleBridgeActive(this.active.lines, this.active.index).index\n }\n\n /**\n * The index of the primary active line, or -1 if none.\n */\n get currentActive() {\n return this.handleGetActiveIndex()\n }\n\n /**\n * The current lyric info object.\n */\n get currentInfo() {\n return this.info\n }\n\n /**\n * The current playback time in ms.\n */\n get currentTime() {\n return this.handleGetCurrentTime()\n }\n}\n"],"names":["DEFAULT","freezeObjectDeep","A","BaseLyricPlayer","ConfigManager","BaseLyricPlayerConfig.DEFAULT","Event","Info","index","line","nextLine","lines","min","max","existing","i","bridgedLines","bridgedIndex","isSeek","bridged","time","firstIndex","now","hasChanged","newActiveLines","newActiveIndex","infoIndex","info"],"mappings":"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","x_google_ignoreList":[1]}
@@ -1,12 +1,12 @@
1
1
  import { ConfigManager } from '@music-lyric-player/utils';
2
+ import { DeepRequired } from '@music-lyric-player/utils';
2
3
  import { Event as Event_2 } from '@music-lyric-player/utils';
3
4
  import { Info } from '@music-lyric-kit/lyric';
4
5
  import { Line } from '@music-lyric-kit/lyric';
6
+ import { NestedKeys } from '@music-lyric-player/utils';
5
7
 
6
8
  export declare class BaseLyricPlayer {
7
- readonly config: ConfigManager<Options, {
8
- driver?: "timer" | "animation" | undefined;
9
- }, "driver">;
9
+ readonly config: BaseLyricPlayerConfig.RootManager;
10
10
  readonly event: Event_2<BaseLyricPlayerEventMap>;
11
11
  private state;
12
12
  private active;
@@ -16,6 +16,8 @@ export declare class BaseLyricPlayer {
16
16
  private handleGetCurrentTime;
17
17
  private handleGetLineTime;
18
18
  private handleGetActiveIndex;
19
+ private handleBridgeActive;
20
+ private handleEmitLinesUpdate;
19
21
  private handleSyncTime;
20
22
  private handleUpdateActiveLines;
21
23
  private onTick;
@@ -67,6 +69,17 @@ export declare class BaseLyricPlayer {
67
69
  get currentTime(): number;
68
70
  }
69
71
 
72
+ export declare namespace BaseLyricPlayerConfig {
73
+ export {
74
+ Root,
75
+ RootRequired,
76
+ RootKeys,
77
+ RootKeySet,
78
+ RootManager,
79
+ DEFAULT
80
+ }
81
+ }
82
+
70
83
  export declare interface BaseLyricPlayerEventMap {
71
84
  /**
72
85
  * When the player starts or resumes playback.
@@ -93,14 +106,47 @@ export declare interface BaseLyricPlayerEventMap {
93
106
  linesUpdate: (lines: Line[], indexs: number[], index: number, isSeek: boolean) => void;
94
107
  }
95
108
 
96
- declare interface Options {
109
+ declare const DEFAULT: Root;
110
+
111
+ declare interface Root {
97
112
  /**
98
113
  * Driver type for playback scheduling.
99
114
  * - `animation`: use requestAnimationFrame
100
115
  * - `timer`: use setTimeout
101
- * @default animation
116
+ *
117
+ * @default 'animation'
118
+ */
119
+ driver?: 'timer' | 'animation';
120
+ /**
121
+ * Whether to bridge gaps between simultaneously active lines.
122
+ *
123
+ * Active lines may not be consecutive — a line in the middle could have already ended (`played`) while its neighbors are still active, producing a `[active, played, active]` pattern.
124
+ *
125
+ * Enabling this promotes the sandwiched lines back to active so the visual reads as one continuous `[active, active, active]` block.
126
+ *
127
+ * @default true
102
128
  */
103
- driver: 'timer' | 'animation';
129
+ bridgeActive?: boolean;
104
130
  }
105
131
 
132
+ /**
133
+ * All config keys
134
+ */
135
+ declare type RootKeys = NestedKeys<Root>;
136
+
137
+ /**
138
+ * Set of config key paths that changed since the previous value, as emitted by `ConfigManager.event['update']`.
139
+ */
140
+ declare type RootKeySet = Set<RootKeys>;
141
+
142
+ /**
143
+ * Runtime config manager bound to this module's {@link Config} shape.
144
+ */
145
+ declare type RootManager = ConfigManager<RootRequired, Root>;
146
+
147
+ /**
148
+ * Fully‑resolved config in which every field is required, produced by deep‑requiring the user‑facing {@link Config}.
149
+ */
150
+ declare type RootRequired = DeepRequired<Root>;
151
+
106
152
  export { }
@@ -1,11 +1,15 @@
1
+ import { freezeObjectDeep as u, ConfigManager as m, Event as f } from "@music-lyric-player/utils";
1
2
  import "lodash-es";
2
- import { ConfigManager as c, Event as d } from "@music-lyric-player/utils";
3
- const o = {
4
- driver: "timer"
5
- }, m = "0.5.1";
6
- class r {
3
+ const o = u({
4
+ driver: "animation",
5
+ bridgeActive: !0
6
+ }), I = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
7
+ __proto__: null,
8
+ DEFAULT: o
9
+ }, Symbol.toStringTag, { value: "Module" })), g = "0.5.1";
10
+ class c {
7
11
  get version() {
8
- return m;
12
+ return g;
9
13
  }
10
14
  type = "Incorrect";
11
15
  metas = [];
@@ -21,9 +25,9 @@ class r {
21
25
  };
22
26
  }
23
27
  }
24
- class v {
25
- config = new c(o);
26
- event = new d();
28
+ class x {
29
+ config = new m(o);
30
+ event = new f();
27
31
  state;
28
32
  active;
29
33
  time;
@@ -40,7 +44,7 @@ class v {
40
44
  }, this.time = {
41
45
  start: 0,
42
46
  seek: 0
43
- }, this.info = new r();
47
+ }, this.info = new c();
44
48
  }
45
49
  handleGetCurrentTime() {
46
50
  return this.state.playing ? this.time.seek + (performance.now() - this.time.start) : this.time.seek;
@@ -50,40 +54,60 @@ class v {
50
54
  return 0;
51
55
  if (e === this.info.lines.length - 1)
52
56
  return 1 / 0;
53
- const s = this.info.lines[e], n = this.info.lines[e + 1];
54
- return Math.max(s.time.end, n.time.start);
57
+ const t = this.info.lines[e], s = this.info.lines[e + 1];
58
+ return Math.max(t.time.end, s.time.start);
55
59
  }
56
60
  handleGetActiveIndex() {
57
61
  return this.active.index.length > 0 ? this.active.index[0] : -1;
58
62
  }
63
+ handleBridgeActive(e, t) {
64
+ if (!this.config.current.bridgeActive || t.length < 2)
65
+ return { lines: e, index: t };
66
+ const s = t[0], n = t[t.length - 1];
67
+ if (n - s + 1 === t.length)
68
+ return { lines: e, index: t };
69
+ const i = /* @__PURE__ */ new Map();
70
+ for (let a = 0; a < t.length; a++)
71
+ i.set(t[a], e[a]);
72
+ const h = [], r = [];
73
+ for (let a = s; a <= n; a++) {
74
+ const l = i.get(a) ?? this.info.lines[a];
75
+ l && (h.push(l), r.push(a));
76
+ }
77
+ return { lines: h, index: r };
78
+ }
79
+ handleEmitLinesUpdate(e) {
80
+ const t = this.handleBridgeActive(this.active.lines, this.active.index);
81
+ this.event.emit("linesUpdate", t.lines, t.index, this.handleGetActiveIndex(), e);
82
+ }
59
83
  handleSyncTime(e) {
60
- const s = [], n = [];
61
- let i = this.info.lines.length;
62
- for (let t = 0; t < this.info.lines.length; t++) {
63
- const a = this.info.lines[t];
64
- if (a.time.start > e) {
65
- i = t;
84
+ const t = [], s = [];
85
+ let n = this.info.lines.length;
86
+ for (let i = 0; i < this.info.lines.length; i++) {
87
+ const h = this.info.lines[i];
88
+ if (h.time.start > e) {
89
+ n = i;
66
90
  break;
67
91
  }
68
- this.handleGetLineTime(t) > e && (s.push(a), n.push(t));
92
+ this.handleGetLineTime(i) > e && (t.push(h), s.push(i));
69
93
  }
70
- this.state.scanIndex = i, this.active.lines = s, this.active.index = n, this.event.emit("linesUpdate", [...this.active.lines], [...this.active.index], this.handleGetActiveIndex(), !0);
94
+ this.state.scanIndex = n, this.active.lines = t, this.active.index = s, this.handleEmitLinesUpdate(!0);
71
95
  }
72
96
  handleUpdateActiveLines(e) {
73
- let s = !1;
74
- const n = [], i = [];
75
- for (let t = 0; t < this.active.lines.length; t++) {
76
- const a = this.active.lines[t], h = this.active.index[t];
77
- e >= this.handleGetLineTime(h) ? s = !0 : (n.push(a), i.push(h));
97
+ let t = !1;
98
+ const s = [], n = [];
99
+ for (let i = 0; i < this.active.lines.length; i++) {
100
+ const h = this.active.lines[i], r = this.active.index[i];
101
+ e >= this.handleGetLineTime(r) ? t = !0 : (s.push(h), n.push(r));
78
102
  }
79
103
  for (; this.state.scanIndex < this.info.lines.length; ) {
80
- const t = this.info.lines[this.state.scanIndex];
81
- if (e >= t.time.start)
82
- e < this.handleGetLineTime(this.state.scanIndex) && (n.push(t), i.push(this.state.scanIndex), s = !0), this.state.scanIndex++;
104
+ const i = this.info.lines[this.state.scanIndex];
105
+ if (e >= i.time.start)
106
+ e < this.handleGetLineTime(this.state.scanIndex) && (s.push(i), n.push(this.state.scanIndex), t = !0), this.state.scanIndex++;
83
107
  else
84
108
  break;
85
109
  }
86
- s && (this.active.lines = n, this.active.index = i, this.event.emit("linesUpdate", [...this.active.lines], [...this.active.index], this.handleGetActiveIndex(), !1));
110
+ t && (this.active.lines = s, this.active.index = n, this.handleEmitLinesUpdate(!1));
87
111
  }
88
112
  onTick = () => {
89
113
  if (!this.state.playing)
@@ -118,21 +142,21 @@ class v {
118
142
  * Stop playback
119
143
  */
120
144
  dispose() {
121
- this.pause(), this.event.clear(), this.active.lines = [], this.active.index = [], this.info = new r();
145
+ this.pause(), this.event.clear(), this.active.lines = [], this.active.index = [], this.info = new c();
122
146
  }
123
147
  /**
124
148
  * Find all active lines at the given time (ms). Does not mutate internal state.
125
149
  * @param time time in ms to find active lines for.
126
150
  */
127
151
  matchLinesWithTime(e) {
128
- const s = [], n = [];
129
- for (let i = 0; i < this.info.lines.length; i++) {
130
- const t = this.info.lines[i];
131
- if (t.time.start > e)
152
+ const t = [], s = [];
153
+ for (let n = 0; n < this.info.lines.length; n++) {
154
+ const i = this.info.lines[n];
155
+ if (i.time.start > e)
132
156
  break;
133
- this.handleGetLineTime(i) > e && (s.push(t), n.push(i));
157
+ this.handleGetLineTime(n) > e && (t.push(i), s.push(n));
134
158
  }
135
- return { lines: s, index: n };
159
+ return this.handleBridgeActive(t, s);
136
160
  }
137
161
  /**
138
162
  * Whether the player is currently playing.
@@ -144,13 +168,13 @@ class v {
144
168
  * Current active lines.
145
169
  */
146
170
  get currentLines() {
147
- return [...this.active.lines];
171
+ return this.handleBridgeActive(this.active.lines, this.active.index).lines;
148
172
  }
149
173
  /**
150
174
  * Indices of currently active lines.
151
175
  */
152
176
  get currentIndex() {
153
- return [...this.active.index];
177
+ return this.handleBridgeActive(this.active.lines, this.active.index).index;
154
178
  }
155
179
  /**
156
180
  * The index of the primary active line, or -1 if none.
@@ -172,6 +196,7 @@ class v {
172
196
  }
173
197
  }
174
198
  export {
175
- v as BaseLyricPlayer
199
+ x as BaseLyricPlayer,
200
+ I as BaseLyricPlayerConfig
176
201
  };
177
202
  //# sourceMappingURL=index.ecma.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.ecma.js","sources":["../src/options.ts","../../../node_modules/.pnpm/@music-lyric-kit+lyric@0.4.0/node_modules/@music-lyric-kit/lyric/dist/index.ecma.js","../src/index.ts"],"sourcesContent":["export interface Options {\n /**\n * Driver type for playback scheduling.\n * - `animation`: use requestAnimationFrame\n * - `timer`: use setTimeout\n * @default animation\n */\n driver: 'timer' | 'animation'\n}\n\nexport const DEFAULT_OPTIONS: Options = {\n driver: 'timer',\n}\n","import { createRandomHex as r } from \"@music-lyric-kit/utils\";\nclass s {\n start = 0;\n end = 0;\n get duration() {\n return this.end - this.start;\n }\n toJSON() {\n return {\n start: this.start,\n end: this.end,\n duration: this.duration\n };\n }\n}\nvar o = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Space = \"Space\", t))(o || {});\nclass i {\n stress = !1;\n toJSON() {\n return {\n stress: this.stress\n };\n }\n}\nclass p {\n get type() {\n return \"Normal\";\n }\n time = new s();\n content = \"\";\n extended = [];\n config = new i();\n toJSON() {\n return {\n type: this.type,\n time: this.time,\n content: this.content,\n extended: this.extended,\n config: this.config\n };\n }\n}\nclass N {\n get type() {\n return \"Space\";\n }\n count = 1;\n toJSON() {\n return {\n type: this.type,\n count: this.count\n };\n }\n}\nvar a = /* @__PURE__ */ ((t) => (t.UnKnown = \"UnKnown\", t.Translate = \"Translate\", t.Roman = \"Roman\", t))(a || {});\nclass S {\n type = \"UnKnown\";\n content = \"\";\n toJSON() {\n return {\n type: this.type,\n content: this.content\n };\n }\n}\nvar l = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Interlude = \"Interlude\", t))(l || {});\nclass x {\n get type() {\n return \"Interlude\";\n }\n id = r(4).toUpperCase();\n time = new s();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time\n };\n }\n}\nclass c {\n words = [];\n extended = [];\n get original() {\n return this.words.map((n) => n.type === o.Normal ? n.content : new Array(n.count).fill(\" \").join(\"\")).join(\"\");\n }\n toJSON() {\n return {\n words: this.words,\n extended: this.extended,\n original: this.original\n };\n }\n}\nclass y {\n get type() {\n return \"Normal\";\n }\n id = r(4).toUpperCase();\n time = new s();\n content = new c();\n agent;\n background;\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time,\n content: this.content,\n agent: this.agent,\n background: this.background\n };\n }\n}\nvar u = /* @__PURE__ */ ((t) => (t.Offset = \"Offset\", t.Duration = \"Duration\", t.Title = \"Title\", t.Singer = \"Singer\", t.Album = \"Album\", t.Creator = \"Creator\", t.UnKnown = \"UnKnown\", t))(u || {});\nclass e {\n id = r(4).toUpperCase();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n content: this.content\n };\n }\n}\nclass f extends e {\n get type() {\n return \"Offset\";\n }\n content = 0;\n}\nclass w extends e {\n get type() {\n return \"Duration\";\n }\n content = 0;\n}\nclass O extends e {\n get type() {\n return \"Title\";\n }\n content = \"\";\n}\nclass b extends e {\n get type() {\n return \"Singer\";\n }\n content = \"\";\n}\nclass J extends e {\n get type() {\n return \"Album\";\n }\n content = \"\";\n}\nclass U extends e {\n get type() {\n return \"Creator\";\n }\n content = {\n role: \"\",\n name: []\n };\n}\nclass C extends e {\n get type() {\n return \"UnKnown\";\n }\n content = \"\";\n}\nclass I {\n id = \"\";\n name = \"\";\n count = 0;\n}\nclass d {\n // index in global\n global = 0;\n // index in block\n block = 0;\n toJSON() {\n return {\n global: this.global,\n block: this.block\n };\n }\n}\nclass v {\n id = \"\";\n index = new d();\n toJSON() {\n return {\n id: this.id,\n index: this.index\n };\n }\n}\nconst m = \"0.5.1\";\nvar g = /* @__PURE__ */ ((t) => (t.Incorrect = \"Incorrect\", t.Normal = \"Normal\", t.Syllable = \"Syllable\", t.Pure = \"Pure\", t.Empty = \"Empty\", t.NoTime = \"NoTime\", t))(g || {});\nclass A {\n get version() {\n return m;\n }\n type = \"Incorrect\";\n metas = [];\n lines = [];\n agents = [];\n toJSON() {\n return {\n version: this.version,\n type: this.type,\n metas: this.metas,\n lines: this.lines,\n agents: this.agents\n };\n }\n}\nexport {\n I as Agent,\n v as AgentLine,\n d as AgentLineIndex,\n S as Extended,\n a as ExtendedType,\n A as Info,\n x as LineInterlude,\n y as LineNormal,\n c as LineNormalContent,\n l as LineType,\n J as MetaAlbum,\n U as MetaCreator,\n w as MetaDuration,\n f as MetaOffset,\n b as MetaSinger,\n O as MetaTitle,\n u as MetaType,\n C as MetaUnKnown,\n s as Time,\n g as Type,\n m as Version,\n p as WordNormal,\n N as WordSpace,\n o as WordType\n};\n//# sourceMappingURL=index.ecma.js.map\n","import type { Line } from '@music-lyric-kit/lyric'\nimport type { DeepPartial } from '@music-lyric-player/utils'\nimport type { Options } from './options'\n\nimport { DEFAULT_OPTIONS } from './options'\n\nimport { Info } from '@music-lyric-kit/lyric'\nimport { ConfigManager, Event } from '@music-lyric-player/utils'\n\nexport interface BaseLyricPlayerEventMap {\n /**\n * When the player starts or resumes playback.\n * @param currentTime The current playback time.\n */\n play: (currentTime: number) => void\n\n /**\n * When the player pauses playback.\n * @param currentTime The current playback time.\n */\n pause: (currentTime: number) => void\n\n /**\n * When the entire lyric information is updated (e.g., loading a new lyric/song).\n * @param info The newly loaded lyric information object.\n */\n lyricUpdate: (info: Info) => void\n\n /**\n * When the currently active lyric lines change during playback.\n * @param lines An array of the currently active lyric lines.\n * @param indexs An array of the currently active lyric lines' indexes.\n * @param firstIndex The index of the first currently active lyric line (-1 if none).\n * @param isSeek Is seek.\n */\n linesUpdate: (lines: Line[], indexs: number[], index: number, isSeek: boolean) => void\n}\n\nexport class BaseLyricPlayer {\n readonly config = new ConfigManager<Options, DeepPartial<Options>>(DEFAULT_OPTIONS)\n\n readonly event: Event<BaseLyricPlayerEventMap> = new Event()\n\n private state: {\n playing: boolean\n frameId: number | null\n timerId: number | null\n scanIndex: number\n }\n private active: {\n lines: Line[]\n index: number[]\n }\n private time: {\n start: number\n seek: number\n }\n private info: Info\n\n constructor() {\n this.state = {\n playing: false,\n frameId: null,\n timerId: null,\n scanIndex: 0,\n }\n this.active = {\n lines: [],\n index: [],\n }\n this.time = {\n start: 0,\n seek: 0,\n }\n this.info = new Info()\n }\n\n private handleGetCurrentTime() {\n if (!this.state.playing) {\n return this.time.seek\n }\n return this.time.seek + (performance.now() - this.time.start)\n }\n\n private handleGetLineTime(index: number): number {\n if (index < 0 || index >= this.info.lines.length) {\n return 0\n }\n\n if (index === this.info.lines.length - 1) {\n return Infinity\n }\n\n const line = this.info.lines[index]\n const nextLine = this.info.lines[index + 1]\n return Math.max(line.time.end, nextLine.time.start)\n }\n\n private handleGetActiveIndex() {\n return this.active.index.length > 0 ? this.active.index[0] : -1\n }\n\n private handleSyncTime(time: number) {\n const lines: Line[] = []\n const index: number[] = []\n\n let firstIndex = this.info.lines.length\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n firstIndex = i\n break\n }\n\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n\n this.state.scanIndex = firstIndex\n\n this.active.lines = lines\n this.active.index = index\n\n this.event.emit('linesUpdate', [...this.active.lines], [...this.active.index], this.handleGetActiveIndex(), true)\n }\n\n private handleUpdateActiveLines(now: number) {\n let hasChanged = false\n\n const newActiveLines: Line[] = []\n const newActiveIndex: number[] = []\n\n for (let i = 0; i < this.active.lines.length; i++) {\n const line = this.active.lines[i]\n const infoIndex = this.active.index[i]\n\n if (now >= this.handleGetLineTime(infoIndex)) {\n hasChanged = true\n } else {\n newActiveLines.push(line)\n newActiveIndex.push(infoIndex)\n }\n }\n\n while (this.state.scanIndex < this.info.lines.length) {\n const nextLine = this.info.lines[this.state.scanIndex]\n if (now >= nextLine.time.start) {\n if (now < this.handleGetLineTime(this.state.scanIndex)) {\n newActiveLines.push(nextLine)\n newActiveIndex.push(this.state.scanIndex)\n hasChanged = true\n }\n this.state.scanIndex++\n } else {\n break\n }\n }\n\n if (!hasChanged) {\n return\n }\n\n this.active.lines = newActiveLines\n this.active.index = newActiveIndex\n this.event.emit('linesUpdate', [...this.active.lines], [...this.active.index], this.handleGetActiveIndex(), false)\n }\n\n private onTick = () => {\n if (!this.state.playing) {\n return\n }\n\n const now = this.handleGetCurrentTime()\n this.handleUpdateActiveLines(now)\n\n switch (this.config.current.driver) {\n case 'animation':\n this.state.frameId = window.requestAnimationFrame(this.onTick)\n break\n case 'timer':\n this.state.timerId = window.setTimeout(this.onTick, 16)\n break\n }\n }\n\n updateLyric(info: Info) {\n if (!info) {\n return\n }\n\n this.pause()\n this.info = info\n\n this.active.lines = []\n this.active.index = []\n\n this.state.scanIndex = 0\n this.time.seek = 0\n\n this.event.emit('lyricUpdate', info)\n this.event.emit('linesUpdate', [], [], -1, false)\n }\n\n /**\n * Start playback\n * @param time Optional time in ms to seek to before starting playback. If not provided, playback will start from the current position.\n */\n play(time?: number) {\n this.pause()\n\n if (typeof time === 'number' && !Number.isNaN(time)) {\n this.time.seek = time\n this.handleSyncTime(time)\n }\n\n this.time.start = performance.now()\n this.state.playing = true\n this.onTick()\n\n this.event.emit('play', this.handleGetCurrentTime())\n }\n\n /**\n * Pause playback\n */\n pause() {\n if (this.state.playing) {\n this.time.seek = this.handleGetCurrentTime()\n this.state.playing = false\n }\n if (this.state.frameId !== null) {\n cancelAnimationFrame(this.state.frameId)\n this.state.frameId = null\n }\n if (this.state.timerId !== null) {\n clearTimeout(this.state.timerId)\n this.state.timerId = null\n }\n\n this.event.emit('pause', this.handleGetCurrentTime())\n }\n\n /**\n * Stop playback\n */\n dispose() {\n this.pause()\n this.event.clear()\n\n this.active.lines = []\n this.active.index = []\n\n this.info = new Info()\n }\n\n /**\n * Find all active lines at the given time (ms). Does not mutate internal state.\n * @param time time in ms to find active lines for.\n */\n matchLinesWithTime(time: number): { lines: Line[]; index: number[] } {\n const lines: Line[] = []\n const index: number[] = []\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n break\n }\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n return { lines, index }\n }\n\n /**\n * Whether the player is currently playing.\n */\n get currentPlaying() {\n return this.state.playing\n }\n\n /**\n * Current active lines.\n */\n get currentLines() {\n return [...this.active.lines]\n }\n\n /**\n * Indices of currently active lines.\n */\n get currentIndex() {\n return [...this.active.index]\n }\n\n /**\n * The index of the primary active line, or -1 if none.\n */\n get currentActive() {\n return this.handleGetActiveIndex()\n }\n\n /**\n * The current lyric info object.\n */\n get currentInfo() {\n return this.info\n }\n\n /**\n * The current playback time in ms.\n */\n get currentTime() {\n return this.handleGetCurrentTime()\n }\n}\n"],"names":["DEFAULT_OPTIONS","A","BaseLyricPlayer","ConfigManager","Event","Info","index","line","nextLine","time","lines","firstIndex","i","now","hasChanged","newActiveLines","newActiveIndex","infoIndex","info"],"mappings":"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","x_google_ignoreList":[1]}
1
+ {"version":3,"file":"index.ecma.js","sources":["../src/config/root.ts","../../../node_modules/.pnpm/@music-lyric-kit+lyric@0.4.0/node_modules/@music-lyric-kit/lyric/dist/index.ecma.js","../src/core.ts"],"sourcesContent":["import type { ConfigManager, DeepRequired, NestedKeys } from '@music-lyric-player/utils'\n\nimport { freezeObjectDeep } from '@music-lyric-player/utils'\n\nexport interface Root {\n /**\n * Driver type for playback scheduling.\n * - `animation`: use requestAnimationFrame\n * - `timer`: use setTimeout\n *\n * @default 'animation'\n */\n driver?: 'timer' | 'animation'\n /**\n * Whether to bridge gaps between simultaneously active lines.\n *\n * Active lines may not be consecutive — a line in the middle could have already ended (`played`) while its neighbors are still active, producing a `[active, played, active]` pattern.\n *\n * Enabling this promotes the sandwiched lines back to active so the visual reads as one continuous `[active, active, active]` block.\n *\n * @default true\n */\n bridgeActive?: boolean\n}\n\n/**\n * Fully‑resolved config in which every field is required, produced by deep‑requiring the user‑facing {@link Config}.\n */\nexport type RootRequired = DeepRequired<Root>\n\n/**\n * All config keys\n */\nexport type RootKeys = NestedKeys<Root>\n\n/**\n * Set of config key paths that changed since the previous value, as emitted by `ConfigManager.event['update']`.\n */\nexport type RootKeySet = Set<RootKeys>\n\n/**\n * Runtime config manager bound to this module's {@link Config} shape.\n */\nexport type RootManager = ConfigManager<RootRequired, Root>\n\nexport const DEFAULT: Root = freezeObjectDeep({\n driver: 'animation',\n bridgeActive: true,\n})\n","import { createRandomHex as r } from \"@music-lyric-kit/utils\";\nclass s {\n start = 0;\n end = 0;\n get duration() {\n return this.end - this.start;\n }\n toJSON() {\n return {\n start: this.start,\n end: this.end,\n duration: this.duration\n };\n }\n}\nvar o = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Space = \"Space\", t))(o || {});\nclass i {\n stress = !1;\n toJSON() {\n return {\n stress: this.stress\n };\n }\n}\nclass p {\n get type() {\n return \"Normal\";\n }\n time = new s();\n content = \"\";\n extended = [];\n config = new i();\n toJSON() {\n return {\n type: this.type,\n time: this.time,\n content: this.content,\n extended: this.extended,\n config: this.config\n };\n }\n}\nclass N {\n get type() {\n return \"Space\";\n }\n count = 1;\n toJSON() {\n return {\n type: this.type,\n count: this.count\n };\n }\n}\nvar a = /* @__PURE__ */ ((t) => (t.UnKnown = \"UnKnown\", t.Translate = \"Translate\", t.Roman = \"Roman\", t))(a || {});\nclass S {\n type = \"UnKnown\";\n content = \"\";\n toJSON() {\n return {\n type: this.type,\n content: this.content\n };\n }\n}\nvar l = /* @__PURE__ */ ((t) => (t.Normal = \"Normal\", t.Interlude = \"Interlude\", t))(l || {});\nclass x {\n get type() {\n return \"Interlude\";\n }\n id = r(4).toUpperCase();\n time = new s();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time\n };\n }\n}\nclass c {\n words = [];\n extended = [];\n get original() {\n return this.words.map((n) => n.type === o.Normal ? n.content : new Array(n.count).fill(\" \").join(\"\")).join(\"\");\n }\n toJSON() {\n return {\n words: this.words,\n extended: this.extended,\n original: this.original\n };\n }\n}\nclass y {\n get type() {\n return \"Normal\";\n }\n id = r(4).toUpperCase();\n time = new s();\n content = new c();\n agent;\n background;\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n time: this.time,\n content: this.content,\n agent: this.agent,\n background: this.background\n };\n }\n}\nvar u = /* @__PURE__ */ ((t) => (t.Offset = \"Offset\", t.Duration = \"Duration\", t.Title = \"Title\", t.Singer = \"Singer\", t.Album = \"Album\", t.Creator = \"Creator\", t.UnKnown = \"UnKnown\", t))(u || {});\nclass e {\n id = r(4).toUpperCase();\n toJSON() {\n return {\n id: this.id,\n type: this.type,\n content: this.content\n };\n }\n}\nclass f extends e {\n get type() {\n return \"Offset\";\n }\n content = 0;\n}\nclass w extends e {\n get type() {\n return \"Duration\";\n }\n content = 0;\n}\nclass O extends e {\n get type() {\n return \"Title\";\n }\n content = \"\";\n}\nclass b extends e {\n get type() {\n return \"Singer\";\n }\n content = \"\";\n}\nclass J extends e {\n get type() {\n return \"Album\";\n }\n content = \"\";\n}\nclass U extends e {\n get type() {\n return \"Creator\";\n }\n content = {\n role: \"\",\n name: []\n };\n}\nclass C extends e {\n get type() {\n return \"UnKnown\";\n }\n content = \"\";\n}\nclass I {\n id = \"\";\n name = \"\";\n count = 0;\n}\nclass d {\n // index in global\n global = 0;\n // index in block\n block = 0;\n toJSON() {\n return {\n global: this.global,\n block: this.block\n };\n }\n}\nclass v {\n id = \"\";\n index = new d();\n toJSON() {\n return {\n id: this.id,\n index: this.index\n };\n }\n}\nconst m = \"0.5.1\";\nvar g = /* @__PURE__ */ ((t) => (t.Incorrect = \"Incorrect\", t.Normal = \"Normal\", t.Syllable = \"Syllable\", t.Pure = \"Pure\", t.Empty = \"Empty\", t.NoTime = \"NoTime\", t))(g || {});\nclass A {\n get version() {\n return m;\n }\n type = \"Incorrect\";\n metas = [];\n lines = [];\n agents = [];\n toJSON() {\n return {\n version: this.version,\n type: this.type,\n metas: this.metas,\n lines: this.lines,\n agents: this.agents\n };\n }\n}\nexport {\n I as Agent,\n v as AgentLine,\n d as AgentLineIndex,\n S as Extended,\n a as ExtendedType,\n A as Info,\n x as LineInterlude,\n y as LineNormal,\n c as LineNormalContent,\n l as LineType,\n J as MetaAlbum,\n U as MetaCreator,\n w as MetaDuration,\n f as MetaOffset,\n b as MetaSinger,\n O as MetaTitle,\n u as MetaType,\n C as MetaUnKnown,\n s as Time,\n g as Type,\n m as Version,\n p as WordNormal,\n N as WordSpace,\n o as WordType\n};\n//# sourceMappingURL=index.ecma.js.map\n","import type { Line } from '@music-lyric-kit/lyric'\nimport type { BaseLyricPlayerEventMap } from './interface'\n\nimport { Info } from '@music-lyric-kit/lyric'\nimport { ConfigManager, Event } from '@music-lyric-player/utils'\nimport { BaseLyricPlayerConfig } from './config'\n\nexport class BaseLyricPlayer {\n readonly config: BaseLyricPlayerConfig.RootManager = new ConfigManager(BaseLyricPlayerConfig.DEFAULT as BaseLyricPlayerConfig.RootRequired)\n\n readonly event: Event<BaseLyricPlayerEventMap> = new Event()\n\n private state: {\n playing: boolean\n frameId: number | null\n timerId: number | null\n scanIndex: number\n }\n private active: {\n lines: Line[]\n index: number[]\n }\n private time: {\n start: number\n seek: number\n }\n private info: Info\n\n constructor() {\n this.state = {\n playing: false,\n frameId: null,\n timerId: null,\n scanIndex: 0,\n }\n this.active = {\n lines: [],\n index: [],\n }\n this.time = {\n start: 0,\n seek: 0,\n }\n this.info = new Info()\n }\n\n private handleGetCurrentTime() {\n if (!this.state.playing) {\n return this.time.seek\n }\n return this.time.seek + (performance.now() - this.time.start)\n }\n\n private handleGetLineTime(index: number): number {\n if (index < 0 || index >= this.info.lines.length) {\n return 0\n }\n\n if (index === this.info.lines.length - 1) {\n return Infinity\n }\n\n const line = this.info.lines[index]\n const nextLine = this.info.lines[index + 1]\n return Math.max(line.time.end, nextLine.time.start)\n }\n\n private handleGetActiveIndex() {\n return this.active.index.length > 0 ? this.active.index[0] : -1\n }\n\n private handleBridgeActive(lines: Line[], index: number[]): { lines: Line[]; index: number[] } {\n if (!this.config.current.bridgeActive || index.length < 2) {\n return { lines, index }\n }\n const min = index[0]\n const max = index[index.length - 1]\n if (max - min + 1 === index.length) {\n return { lines, index }\n }\n const existing = new Map<number, Line>()\n for (let i = 0; i < index.length; i++) {\n existing.set(index[i], lines[i])\n }\n const bridgedLines: Line[] = []\n const bridgedIndex: number[] = []\n for (let i = min; i <= max; i++) {\n const line = existing.get(i) ?? this.info.lines[i]\n if (!line) continue\n bridgedLines.push(line)\n bridgedIndex.push(i)\n }\n return { lines: bridgedLines, index: bridgedIndex }\n }\n\n private handleEmitLinesUpdate(isSeek: boolean) {\n const bridged = this.handleBridgeActive(this.active.lines, this.active.index)\n this.event.emit('linesUpdate', bridged.lines, bridged.index, this.handleGetActiveIndex(), isSeek)\n }\n\n private handleSyncTime(time: number) {\n const lines: Line[] = []\n const index: number[] = []\n\n let firstIndex = this.info.lines.length\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n firstIndex = i\n break\n }\n\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n\n this.state.scanIndex = firstIndex\n\n this.active.lines = lines\n this.active.index = index\n\n this.handleEmitLinesUpdate(true)\n }\n\n private handleUpdateActiveLines(now: number) {\n let hasChanged = false\n\n const newActiveLines: Line[] = []\n const newActiveIndex: number[] = []\n\n for (let i = 0; i < this.active.lines.length; i++) {\n const line = this.active.lines[i]\n const infoIndex = this.active.index[i]\n\n if (now >= this.handleGetLineTime(infoIndex)) {\n hasChanged = true\n } else {\n newActiveLines.push(line)\n newActiveIndex.push(infoIndex)\n }\n }\n\n while (this.state.scanIndex < this.info.lines.length) {\n const nextLine = this.info.lines[this.state.scanIndex]\n if (now >= nextLine.time.start) {\n if (now < this.handleGetLineTime(this.state.scanIndex)) {\n newActiveLines.push(nextLine)\n newActiveIndex.push(this.state.scanIndex)\n hasChanged = true\n }\n this.state.scanIndex++\n } else {\n break\n }\n }\n\n if (!hasChanged) {\n return\n }\n\n this.active.lines = newActiveLines\n this.active.index = newActiveIndex\n this.handleEmitLinesUpdate(false)\n }\n\n private onTick = () => {\n if (!this.state.playing) {\n return\n }\n\n const now = this.handleGetCurrentTime()\n this.handleUpdateActiveLines(now)\n\n switch (this.config.current.driver) {\n case 'animation':\n this.state.frameId = window.requestAnimationFrame(this.onTick)\n break\n case 'timer':\n this.state.timerId = window.setTimeout(this.onTick, 16)\n break\n }\n }\n\n updateLyric(info: Info) {\n if (!info) {\n return\n }\n\n this.pause()\n this.info = info\n\n this.active.lines = []\n this.active.index = []\n\n this.state.scanIndex = 0\n this.time.seek = 0\n\n this.event.emit('lyricUpdate', info)\n this.event.emit('linesUpdate', [], [], -1, false)\n }\n\n /**\n * Start playback\n * @param time Optional time in ms to seek to before starting playback. If not provided, playback will start from the current position.\n */\n play(time?: number) {\n this.pause()\n\n if (typeof time === 'number' && !Number.isNaN(time)) {\n this.time.seek = time\n this.handleSyncTime(time)\n }\n\n this.time.start = performance.now()\n this.state.playing = true\n this.onTick()\n\n this.event.emit('play', this.handleGetCurrentTime())\n }\n\n /**\n * Pause playback\n */\n pause() {\n if (this.state.playing) {\n this.time.seek = this.handleGetCurrentTime()\n this.state.playing = false\n }\n if (this.state.frameId !== null) {\n cancelAnimationFrame(this.state.frameId)\n this.state.frameId = null\n }\n if (this.state.timerId !== null) {\n clearTimeout(this.state.timerId)\n this.state.timerId = null\n }\n\n this.event.emit('pause', this.handleGetCurrentTime())\n }\n\n /**\n * Stop playback\n */\n dispose() {\n this.pause()\n this.event.clear()\n\n this.active.lines = []\n this.active.index = []\n\n this.info = new Info()\n }\n\n /**\n * Find all active lines at the given time (ms). Does not mutate internal state.\n * @param time time in ms to find active lines for.\n */\n matchLinesWithTime(time: number): { lines: Line[]; index: number[] } {\n const lines: Line[] = []\n const index: number[] = []\n for (let i = 0; i < this.info.lines.length; i++) {\n const line = this.info.lines[i]\n if (line.time.start > time) {\n break\n }\n if (this.handleGetLineTime(i) > time) {\n lines.push(line)\n index.push(i)\n }\n }\n return this.handleBridgeActive(lines, index)\n }\n\n /**\n * Whether the player is currently playing.\n */\n get currentPlaying() {\n return this.state.playing\n }\n\n /**\n * Current active lines.\n */\n get currentLines() {\n return this.handleBridgeActive(this.active.lines, this.active.index).lines\n }\n\n /**\n * Indices of currently active lines.\n */\n get currentIndex() {\n return this.handleBridgeActive(this.active.lines, this.active.index).index\n }\n\n /**\n * The index of the primary active line, or -1 if none.\n */\n get currentActive() {\n return this.handleGetActiveIndex()\n }\n\n /**\n * The current lyric info object.\n */\n get currentInfo() {\n return this.info\n }\n\n /**\n * The current playback time in ms.\n */\n get currentTime() {\n return this.handleGetCurrentTime()\n }\n}\n"],"names":["DEFAULT","freezeObjectDeep","m","A","BaseLyricPlayer","ConfigManager","BaseLyricPlayerConfig.DEFAULT","Event","Info","index","line","nextLine","lines","min","max","existing","i","bridgedLines","bridgedIndex","isSeek","bridged","time","firstIndex","now","hasChanged","newActiveLines","newActiveIndex","infoIndex","info"],"mappings":"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","x_google_ignoreList":[1]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@music-lyric-player/base",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "author": "folltoshe",
5
5
  "type": "module",
6
6
  "description": "Music Lyric Player - Base Player",
@@ -32,7 +32,7 @@
32
32
  "@music-lyric-kit/lyric": "^0.4.0"
33
33
  },
34
34
  "dependencies": {
35
- "@music-lyric-player/utils": "^0.8.0"
35
+ "@music-lyric-player/utils": "^0.10.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "vite build"