import { useEffect, useState } from 'react'
import Lyric, { type Lines } from 'lrc-file-parser'
export type Line = Lines[number]
type PlayHook = (line: number, text: string) => void
type SetLyricHook = (lines: Lines) => void
const lrcTools = {
isInited: false,
lrc: null as Lyric | null,
currentLineData: { line: 0, text: '' },
currentLines: [] as Lines,
playHooks: [] as PlayHook[],
setLyricHooks: [] as SetLyricHook[],
isPlay: false,
isShowTranslation: false,
isShowRoma: false,
lyricText: '',
translationText: '' as string | null | undefined,
romaText: '' as string | null | undefined,
init() {
if (this.isInited) return
this.isInited = true
this.lrc = new Lyric({
onPlay: this.onPlay.bind(this),
onSetLyric: this.onSetLyric.bind(this),
offset: 100,
})
},
onPlay(line: number, text: string) {
this.currentLineData.line = line
this.currentLineData.text = text
for (const hook of this.playHooks) hook(line, text)
},
onSetLyric(lines: Lines) {
this.currentLines = lines
this.currentLineData.line = 0
this.currentLineData.text = ''
for (const hook of this.playHooks) hook(-1, '')
for (const hook of this.setLyricHooks) hook(lines)
},
addPlayHook(hook: PlayHook) {
this.playHooks.push(hook)
hook(this.currentLineData.line, this.currentLineData.text)
},
removePlayHook(hook: PlayHook) {
this.playHooks.splice(this.playHooks.indexOf(hook), 1)
},
addSetLyricHook(hook: SetLyricHook) {
this.setLyricHooks.push(hook)
hook(this.currentLines)
},
removeSetLyricHook(hook: SetLyricHook) {
this.setLyricHooks.splice(this.setLyricHooks.indexOf(hook), 1)
},
setLyric() {
const extendedLyrics = [] as string[]
if (this.isShowTranslation && this.translationText) extendedLyrics.push(this.translationText)
if (this.isShowRoma && this.romaText) extendedLyrics.push(this.romaText)
this.lrc!.setLyric(this.lyricText, extendedLyrics)
},
}
export const init = async() => {
lrcTools.init()
}
export const setLyric = (lyric: string, translation?: string, romalrc?: string) => {
lrcTools.isPlay = false
lrcTools.lyricText = lyric
lrcTools.translationText = translation
lrcTools.romaText = romalrc
lrcTools.setLyric()
}
export const setPlaybackRate = (playbackRate: number) => {
lrcTools.lrc!.setPlaybackRate(playbackRate)
}
export const toggleTranslation = (isShow: boolean) => {
lrcTools.isShowTranslation = isShow
if (!lrcTools.lyricText) return
lrcTools.setLyric()
}
export const toggleRoma = (isShow: boolean) => {
lrcTools.isShowRoma = isShow
if (!lrcTools.lyricText) return
lrcTools.setLyric()
}
export const play = (time: number) => {
lrcTools.isPlay = true
lrcTools.lrc!.play(time)
}
export const pause = () => {
lrcTools.isPlay = false
lrcTools.lrc!.pause()
}
export const useLrcPlay = (autoUpdate = true) => {
const [lrcInfo, setLrcInfo] = useState(lrcTools.currentLineData)
useEffect(() => {
if (!autoUpdate) return
const setLrcCallback: SetLyricHook = () => {
setLrcInfo({ line: 0, text: '' })
}
const playCallback: PlayHook = (line, text) => {
setLrcInfo({ line, text })
}
lrcTools.addSetLyricHook(setLrcCallback)
lrcTools.addPlayHook(playCallback)
setLrcInfo(lrcTools.currentLineData)
return () => {
lrcTools.removeSetLyricHook(setLrcCallback)
lrcTools.removePlayHook(playCallback)
}
}, [autoUpdate])
return lrcInfo
}
export const useLrcSet = () => {
const [lines, setLines] = useState<Lines>(lrcTools.currentLines)
useEffect(() => {
const callback = (lines: Lines) => {
setLines(lines)
}
lrcTools.addSetLyricHook(callback)
return () => { lrcTools.removeSetLyricHook(callback) }
}, [])
return lines
}