import { ref, watch } from 'vue' import { useAudioPlayer } from './useAudioPlayer' import { useMusicSources } from './useMusicSources' import { useLyricSettings } from './useLyricSettings' import { parseSmartLrc, alignLyrics, cleanTTMLTranslations, parseQRCLyric, type LrcFormat } from '~/utils/lyric/lyricParser' import { formatLyric } from '~/utils/lyric/lyricFormat' import type { LyricLine } from '@applemusic-like-lyrics/lyric' import { parseTTML, parseYrc } from '@applemusic-like-lyrics/lyric' export const useLyricManager = () => { const audioPlayer = useAudioPlayer() const { getLyrics } = useMusicSources() const settings = useLyricSettings() // 状态 const lyrics = ref([]) const loading = ref(false) const error = ref(null) const currentTrackId = ref(null) const hasTranslation = ref(false) const hasRoma = ref(false) const lyricFormat = ref('line') /** * 清空歌词 */ const clearLyrics = () => { lyrics.value = [] error.value = null hasTranslation.value = false hasRoma.value = false currentTrackId.value = null } /** * 获取并解析歌词 */ const fetchLyric = async (track: any) => { if (!track || !track.id) return // 如果已经在加载同一首歌,跳过 if (loading.value && currentTrackId.value === track.id?.toString()) return // 如果是同一首歌且已有歌词,不重新加载 if (currentTrackId.value === track.id?.toString() && lyrics.value.length > 0) return currentTrackId.value = track.id?.toString() loading.value = true try { // 确定平台 const platform = track.musicPlatform || 'netease' const musicId = track.musicId || track.id console.log(`[LyricManager] 开始获取歌词: ${track.title} (${platform})`) // 1. 获取歌词数据 const result = await getLyrics(platform, musicId) if (!result.success || !result.data) { throw new Error(result.error || '未找到歌词') } const { lrc, trans, yrc, ttml } = result.data console.log( `[LyricManager] 获取结果: LRC=${!!lrc}, TRANS=${!!trans}, YRC=${!!yrc}, TTML=${!!ttml}` ) // 2. 解析歌词 let parsedLyrics: LyricLine[] = [] let format: LrcFormat | 'ttml' | 'qrc' = 'line' // 优先处理 TTML (如果启用且存在) if (settings.enableOnlineTTMLLyric.value && ttml) { try { const cleaned = cleanTTMLTranslations(ttml) const parsed = parseTTML(cleaned) const lines = (parsed as any)?.lines || [] if (lines.length > 0) { parsedLyrics = lines format = 'ttml' console.log('[LyricManager] 使用 TTML 格式') } } catch (e) { console.warn('[LyricManager] TTML解析失败,回退到其他格式', e) } } // 如果没有 TTML 或解析失败,尝试 YRC if (parsedLyrics.length === 0 && yrc) { // 检测 QRC (QQ音乐 XML格式) if (yrc.trim().startsWith('<') || yrc.includes('LyricContent="')) { try { const qrcLines = parseQRCLyric(yrc, trans, undefined) // QQ 音乐 QRC 可能带有翻译 if (qrcLines.length > 0) { parsedLyrics = qrcLines format = 'qrc' console.log(`[LyricManager] 使用 QRC 格式`) } } catch (e) { console.warn('[LyricManager] QRC解析失败', e) } } if (parsedLyrics.length === 0) { try { // 尝试使用库的 parseYrc (Netease JSON) const lines = parseYrc(yrc) if (lines && lines.length > 0) { // 检查解析质量:如果所有行的 words 都为空,说明解析失败 const validLines = lines.filter((l) => l.words && l.words.length > 0) if (validLines.length > 0) { parsedLyrics = lines format = 'word-by-word' console.log(`[LyricManager] 使用 YRC 格式 (Library)`) } else { // 解析结果无效,回退 console.warn('[LyricManager] YRC (Library) 解析结果无效(无words),尝试回退') parsedLyrics = [] // 触发后续逻辑 } } else { const { lines, format: detectedFormat } = parseSmartLrc(yrc) if (lines.length > 0) { parsedLyrics = lines format = detectedFormat console.log(`[LyricManager] 使用 YRC 格式 (Smart: ${detectedFormat})`) } } } catch (e) { // 回退到 parseSmartLrc const { lines, format: detectedFormat } = parseSmartLrc(yrc) if (lines.length > 0) { parsedLyrics = lines format = detectedFormat console.log(`[LyricManager] 使用 YRC 格式 (Fallback: ${detectedFormat})`) } } } } // 如果还是没有,使用 LRC if (parsedLyrics.length === 0 && lrc) { const { lines, format: detectedFormat } = parseSmartLrc(lrc) parsedLyrics = lines format = detectedFormat console.log(`[LyricManager] 使用 LRC 格式 (${detectedFormat})`) } // 3. 对齐翻译和罗马音 if (parsedLyrics.length > 0) { // 如果有翻译 if (trans) { const { lines: transLines } = parseSmartLrc(trans) if (transLines.length > 0) { parsedLyrics = alignLyrics(parsedLyrics, transLines, 'translatedLyric') hasTranslation.value = true console.log('[LyricManager] 已对齐翻译') } } } else { // 没有任何歌词 error.value = '暂无歌词' } // 4. 格式化 (应用配置,如括号替换) if (parsedLyrics.length > 0) { // 检查当前播放的歌曲 ID 是否仍然是请求的歌曲 ID if (currentTrackId.value !== track.id?.toString()) { console.log('[LyricManager] 歌曲已切换,丢弃过期的歌词响应') return } // 提取元数据用于清洗 const metadata = { title: track.title, artists: track.artist ? [track.artist] : undefined } lyrics.value = formatLyric(parsedLyrics, settings, metadata) lyricFormat.value = format error.value = null // 调试 console.log('[LyricManager] 解析完成,首行:', lyrics.value[0]) console.log('[LyricManager] 解析完成,总行数:', lyrics.value.length) } } catch (e: any) { console.error('[LyricManager] 获取歌词失败:', e) error.value = e.message || '获取歌词失败' lyrics.value = [] } finally { loading.value = false } } // 监听当前播放歌曲变化 watch( () => audioPlayer.getCurrentSong().value, (newTrack) => { if (newTrack) { fetchLyric(newTrack) } else { clearLyrics() } }, { immediate: true } ) return { lyrics, loading, error, currentTrackId, hasTranslation, hasRoma, lyricFormat, fetchLyric, clearLyrics } }