import { computed, ref } from 'vue' // 歌词行接口 export interface ParsedLyricLine { time: number // 时间戳(毫秒) content: string // 歌词内容 words?: Array<{ // 逐字歌词(可选) time: number duration: number content: string }> } // 歌词数据接口 export interface LyricData { lrc: string // 普通歌词 trans?: string // 翻译歌词 yrc?: string // 逐字歌词 } export const useLyrics = () => { // 响应式状态 const currentLyrics = ref([]) const translationLyrics = ref([]) const wordByWordLyrics = ref([]) const currentLyricIndex = ref(0) const currentTime = ref(0) const isLoading = ref(false) const error = ref(null) // 歌词显示设置 const showTranslation = ref(false) const showWordByWord = ref(false) // 解析LRC格式歌词 const parseLrcLyrics = (lrcText: string): ParsedLyricLine[] => { if (!lrcText) return [] const lines = lrcText.split('\n') const lyrics: ParsedLyricLine[] = [] for (const line of lines) { // 匹配时间标签 [mm:ss.xx] 或 [mm:ss:xx] const timeMatch = line.match(/\[(\d{2}):(\d{2})[.:](\d{2,3})\](.*)/) if (timeMatch) { const minutes = parseInt(timeMatch[1]) const seconds = parseInt(timeMatch[2]) const milliseconds = parseInt(timeMatch[3].padEnd(3, '0')) const content = timeMatch[4].trim() const time = minutes * 60 * 1000 + seconds * 1000 + milliseconds if (content) { // 只添加有内容的歌词行 lyrics.push({ time, content }) } } } return lyrics.sort((a, b) => a.time - b.time) } // 解析网易云逐字歌词 const parseYrcLyrics = (yrcText: string): ParsedLyricLine[] => { if (!yrcText) return [] const lines = yrcText.split('\n') const lyrics: ParsedLyricLine[] = [] for (const line of lines) { try { // 网易云逐字歌词格式:{"t":12420,"c":[{"tx":"ya ","t":290},{"tx":"la ","t":380}]} if (line.startsWith('{')) { const data = JSON.parse(line) if (typeof data.t === 'number' && Array.isArray(data.c)) { const start = data.t let accTime = start const words = data.c.map((word: any) => { const d = typeof word.t === 'number' ? word.t : 0 // t 为毫秒时长 const wContent = typeof word.tx === 'string' ? word.tx : '' const wTime = accTime accTime += d return { time: wTime, duration: d, content: wContent } }) const content = data.c .map((word: any) => (typeof word.tx === 'string' ? word.tx : '')) .join('') lyrics.push({ time: start, content, words }) } } // 方括号 + 括号 的逐字格式 else if (line.includes('[')) { // 示例 A(绝对时间在括号前):[12420,3470](12420,290,0)ya (12710,380,0)la // 示例 B(相对时间在括号后):[0,4690]嘘(0,293)つ(293,293)き(586,293) const timeMatch = line.match(/\[(\d+),(\d+)\](.*)/) if (!timeMatch) continue const startTime = parseInt(timeMatch[1]) const duration = parseInt(timeMatch[2]) const wordsText = timeMatch[3] const words: Array<{ time: number; duration: number; content: string }> = [] // 解析示例 B:字(相对或绝对时间, 时长) —— 单位均为毫秒 for (const wordMatch of wordsText.matchAll(/([^()]+)\((\d+),(\d+)\)/g)) { const content = wordMatch[1] const t = parseInt(wordMatch[2]) const durMs = parseInt(wordMatch[3]) // 如果时间小于行起始时间,视为相对时间;否则视为绝对时间 const absTime = startTime > 0 && t < startTime ? startTime + t : t words.push({ time: absTime, duration: durMs, content }) } // 若上面未匹配到,则解析示例 A: (绝对时间, 时长, 0)字 —— 单位为毫秒 if (words.length === 0) { for (const wordMatch of wordsText.matchAll(/\((\d+),(\d+),\d+\)([^()]*)/g)) { const absTime = parseInt(wordMatch[1]) const durMs = parseInt(wordMatch[2]) const content = wordMatch[3] words.push({ time: absTime, duration: durMs, content }) } } const content = words.map((w) => w.content).join('') if (content.length > 0) { lyrics.push({ time: startTime, content, words }) } } } catch (e) { console.warn('解析逐字歌词行失败:', line, e) } } return lyrics.sort((a, b) => a.time - b.time) } // 统一调度后,移除旧的备用源歌词获取逻辑 // 统一调度后,移除旧的网易云逐字歌词解析函数(现统一用 parseYrcLyrics) // 获取歌词(统一调度:先备用源,再vkeys) const fetchLyrics = async (platform: string, musicId: string): Promise => { if (!platform || !musicId) { console.error('fetchLyrics 参数错误:', { platform, musicId }) error.value = '缺少必要参数' currentLyrics.value = [] translationLyrics.value = [] wordByWordLyrics.value = [] currentLyricIndex.value = 0 notifyHarmonyOSLyricsUpdate('') return } isLoading.value = true error.value = null currentLyrics.value = [] translationLyrics.value = [] wordByWordLyrics.value = [] currentLyricIndex.value = 0 notifyHarmonyOSLyricsUpdate('') try { const { getLyrics } = useMusicSources() const result = await getLyrics(platform as 'netease' | 'tencent', musicId) if (result.success && result.data) { const lyricData = result.data // 优先使用逐字歌词 if (lyricData.yrc) { wordByWordLyrics.value = parseYrcLyrics(lyricData.yrc) currentLyrics.value = wordByWordLyrics.value } else if (lyricData.lrc) { currentLyrics.value = parseLrcLyrics(lyricData.lrc) } else { currentLyrics.value = [] } // 翻译歌词 if (lyricData.trans) { translationLyrics.value = parseLrcLyrics(lyricData.trans) showTranslation.value = true } else { translationLyrics.value = [] showTranslation.value = false } const harmonyLyrics = getFormattedLyricsForHarmonyOS() notifyHarmonyOSLyricsUpdate(harmonyLyrics) error.value = null isLoading.value = false return } else { throw new Error(result.error || '未获取到歌词') } } catch (e: any) { console.error('获取歌词失败:', e?.message || e) error.value = e?.message || '获取歌词失败' currentLyrics.value = [] translationLyrics.value = [] wordByWordLyrics.value = [] currentLyricIndex.value = 0 notifyHarmonyOSLyricsUpdate('') } finally { isLoading.value = false } } // 通知鸿蒙侧歌词更新的辅助函数 const notifyHarmonyOSLyricsUpdate = (lyrics: string) => { if (typeof window !== 'undefined' && window.HarmonyOS && window.HarmonyOS.postMessage) { try { const message = { method: 'updateLyrics', parameters: { lyrics: lyrics } } window.HarmonyOS.postMessage(JSON.stringify(message)) // 通知鸿蒙侧更新歌词 } catch (error) { console.error('通知鸿蒙侧更新歌词失败:', error) } } else { console.warn('鸿蒙侧消息接口不可用,无法更新歌词') } } // 根据当前时间更新歌词索引 const updateCurrentLyricIndex = (time: number): void => { currentTime.value = time const lyrics = currentLyrics.value if (lyrics.length === 0) { // 没有歌词数据 return } // 找到当前时间对应的歌词行 for (let i = 0; i < lyrics.length; i++) { if (time >= lyrics[i].time && (i === lyrics.length - 1 || time < lyrics[i + 1].time)) { if (currentLyricIndex.value !== i) { // 歌词索引更新 currentLyricIndex.value = i } return } } // 未找到匹配的歌词行 } // 获取当前歌词内容 const currentLyricContent = computed(() => { const lyrics = currentLyrics.value if (lyrics.length === 0 || currentLyricIndex.value >= lyrics.length) { return '' } return lyrics[currentLyricIndex.value].content }) // 获取当前翻译歌词 const currentTranslationContent = computed(() => { if (!showTranslation.value || translationLyrics.value.length === 0) { return '' } const currentTime = currentLyrics.value[currentLyricIndex.value]?.time if (!currentTime) return '' // 找到对应时间的翻译歌词 for (let i = 0; i < translationLyrics.value.length; i++) { if ( currentTime >= translationLyrics.value[i].time && (i === translationLyrics.value.length - 1 || currentTime < translationLyrics.value[i + 1].time) ) { return translationLyrics.value[i].content } } return '' }) // 获取格式化的LRC歌词文本 const getFormattedLrcText = (): string => { if (currentLyrics.value.length === 0) { return '[00:00.00]暂无歌词' } return currentLyrics.value .map((line) => { const minutes = Math.floor(line.time / 60000) const seconds = Math.floor((line.time % 60000) / 1000) const milliseconds = Math.floor((line.time % 1000) / 10) const timeStr = `[${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}]` return `${timeStr}${line.content}` }) .join('\r\n') } // 获取格式化的歌词用于鸿蒙侧,符合AVMetadata.lyric要求 const getFormattedLyricsForHarmonyOS = (): string => { if (currentLyrics.value.length === 0) { return '[00:00.00]暂无歌词' } // 按照鸿蒙侧要求格式化歌词:[mm:ss.xx]歌词内容\r\n let formattedLyrics = currentLyrics.value .map((line) => { const minutes = Math.floor(line.time / 60000) const seconds = Math.floor((line.time % 60000) / 1000) const milliseconds = Math.floor((line.time % 1000) / 10) // 格式:[mm:ss.xx]歌词内容 const timeStr = `[${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}]` return `${timeStr}${line.content}` }) .join('\r\n') // 鸿蒙侧字符串长度限制:<40960字节 const maxBytes = 40960 const encoder = new TextEncoder() // 检查字符串长度是否超过限制 if (encoder.encode(formattedLyrics).length > maxBytes) { console.warn('歌词长度超过鸿蒙侧限制,正在截断...') // 逐行添加歌词,直到接近字节限制 let truncatedLyrics = '' for (const line of currentLyrics.value) { const minutes = Math.floor(line.time / 60000) const seconds = Math.floor((line.time % 60000) / 1000) const milliseconds = Math.floor((line.time % 1000) / 10) const timeStr = `[${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(2, '0')}]` const lineText = `${timeStr}${line.content}\r\n` // 检查添加这一行后是否会超过限制 const testLyrics = truncatedLyrics + lineText if (encoder.encode(testLyrics).length > maxBytes - 100) { // 留100字节缓冲 break } truncatedLyrics += lineText } formattedLyrics = truncatedLyrics.trim() } // 歌词格式化完成 return formattedLyrics } // 清空歌词 const clearLyrics = (): void => { currentLyrics.value = [] translationLyrics.value = [] wordByWordLyrics.value = [] currentLyricIndex.value = 0 currentTime.value = 0 error.value = null } // 跳转到指定歌词行 const seekToLyricLine = (index: number): number => { if (index >= 0 && index < currentLyrics.value.length) { return currentLyrics.value[index].time / 1000 // 返回秒数 } return 0 } return { // 状态 currentLyrics: readonly(currentLyrics), translationLyrics: readonly(translationLyrics), wordByWordLyrics: readonly(wordByWordLyrics), currentLyricIndex: readonly(currentLyricIndex), currentTime: readonly(currentTime), isLoading: readonly(isLoading), error: readonly(error), // 设置 showTranslation, showWordByWord, // 计算属性 currentLyricContent, currentTranslationContent, // 方法 fetchLyrics, updateCurrentLyricIndex, getFormattedLrcText, getFormattedLyricsForHarmonyOS, // 保持向后兼容 notifyHarmonyOSLyricsUpdate, // 鸿蒙侧歌词更新通知 clearLyrics, seekToLyricLine, parseLrcLyrics, parseYrcLyrics } }