1/** 2 * 管理当前歌曲的歌词 3 */ 4 5import {isSameMediaItem} from '@/utils/mediaItem'; 6import PluginManager from './pluginManager'; 7import LyricParser from '@/utils/lrcParser'; 8import {GlobalState} from '@/utils/stateMapper'; 9import {EDeviceEvents} from '@/constants/commonConst'; 10import {DeviceEventEmitter} from 'react-native'; 11import Config from './config'; 12import LyricUtil from '@/native/lyricUtil'; 13import TrackPlayer from './trackPlayer'; 14import MediaExtra from './mediaExtra'; 15 16const lyricStateStore = new GlobalState<{ 17 loading: boolean; 18 lyricParser?: LyricParser; 19 lyrics: ILyric.IParsedLrc; 20 translationLyrics?: ILyric.IParsedLrc; 21 meta?: Record<string, string>; 22 hasTranslation: boolean; 23}>({ 24 loading: true, 25 lyrics: [], 26 hasTranslation: false, 27}); 28 29const currentLyricStore = new GlobalState<ILyric.IParsedLrcItem | null>(null); 30const loadingState = { 31 loading: true, 32 lyrics: [], 33 hasTranslation: false, 34}; 35 36function setLyricLoading() { 37 lyricStateStore.setValue(loadingState); 38} 39 40// 重新获取歌词 41async function refreshLyric(fromStart?: boolean, forceRequest = false) { 42 const musicItem = TrackPlayer.getCurrentMusic(); 43 try { 44 if (!musicItem) { 45 lyricStateStore.setValue({ 46 loading: false, 47 lyrics: [], 48 hasTranslation: false, 49 }); 50 51 currentLyricStore.setValue({ 52 lrc: 'MusicFree', 53 time: 0, 54 }); 55 56 return; 57 } 58 59 const currentParserMusicItem = lyricStateStore 60 .getValue() 61 ?.lyricParser?.getCurrentMusicItem(); 62 63 let lrcSource: ILyric.ILyricSource | null | undefined; 64 if ( 65 forceRequest || 66 !isSameMediaItem(currentParserMusicItem, musicItem) 67 ) { 68 lyricStateStore.setValue(loadingState); 69 currentLyricStore.setValue(null); 70 71 lrcSource = await PluginManager.getByMedia( 72 musicItem, 73 )?.methods?.getLyric(musicItem); 74 } else { 75 lrcSource = lyricStateStore.getValue().lyricParser!.lrcSource; 76 } 77 78 if (!lrcSource && Config.get('setting.lyric.autoSearchLyric')) { 79 const keyword = musicItem.alias || musicItem.title; 80 const plugins = PluginManager.getSearchablePlugins('lyric'); 81 82 for (let plugin of plugins) { 83 const realtimeMusicItem = TrackPlayer.getCurrentMusic(); 84 if (!isSameMediaItem(musicItem, realtimeMusicItem)) { 85 return; 86 } 87 const results = await plugin.methods 88 .search(keyword, 1, 'lyric') 89 .catch(() => null); 90 if (results?.data[0]) { 91 lrcSource = await plugin.methods 92 .getLyric(results.data[0]) 93 .catch(() => null); 94 if (lrcSource) { 95 break; 96 } 97 } 98 } 99 } 100 101 const realtimeMusicItem = TrackPlayer.getCurrentMusic(); 102 if (isSameMediaItem(musicItem, realtimeMusicItem)) { 103 if (lrcSource) { 104 const mediaExtra = MediaExtra.get(musicItem); 105 const parser = new LyricParser(lrcSource, musicItem, { 106 offset: (mediaExtra?.lyricOffset || 0) * -1, 107 }); 108 109 lyricStateStore.setValue({ 110 loading: false, 111 lyricParser: parser, 112 lyrics: parser.getLyric(), 113 translationLyrics: lrcSource.translation 114 ? parser.getTranslationLyric() 115 : undefined, 116 meta: parser.getMeta(), 117 hasTranslation: !!lrcSource.translation, 118 }); 119 // 更新当前状态的歌词 120 const currentLyric = fromStart 121 ? parser.getLyric()[0] 122 : parser.getPosition( 123 (await TrackPlayer.getProgress()).position, 124 ).lrc; 125 currentLyricStore.setValue(currentLyric || null); 126 } else { 127 // 没有歌词 128 lyricStateStore.setValue({ 129 loading: false, 130 lyrics: [], 131 hasTranslation: false, 132 }); 133 } 134 } 135 } catch (e) { 136 console.log(e, 'LRC'); 137 const realtimeMusicItem = TrackPlayer.getCurrentMusic(); 138 if (isSameMediaItem(musicItem, realtimeMusicItem)) { 139 // 异常情况 140 lyricStateStore.setValue({ 141 loading: false, 142 lyrics: [], 143 hasTranslation: false, 144 }); 145 } 146 } 147} 148 149// 获取歌词 150async function setup() { 151 DeviceEventEmitter.addListener(EDeviceEvents.REFRESH_LYRIC, refreshLyric); 152 153 if (Config.get('setting.lyric.showStatusBarLyric')) { 154 LyricUtil.showStatusBarLyric( 155 'MusicFree', 156 Config.get('setting.lyric') ?? {}, 157 ); 158 } 159 160 refreshLyric(); 161} 162 163const LyricManager = { 164 setup, 165 useLyricState: lyricStateStore.useValue, 166 getLyricState: lyricStateStore.getValue, 167 useCurrentLyric: currentLyricStore.useValue, 168 getCurrentLyric: currentLyricStore.getValue, 169 setCurrentLyric: currentLyricStore.setValue, 170 refreshLyric, 171 setLyricLoading, 172}; 173 174export default LyricManager; 175