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