xref: /MusicFree/src/core/lyricManager.ts (revision 095287552b9baf2f2ceeb9397c563c292a4f7934)
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    meta?: Record<string, string>;
21}>({
22    loading: true,
23    lyrics: [],
24});
25
26const currentLyricStore = new GlobalState<ILyric.IParsedLrcItem | null>(null);
27
28// 重新获取歌词
29async function refreshLyric(fromStart?: boolean, forceRequest = false) {
30    const musicItem = TrackPlayer.getCurrentMusic();
31    try {
32        if (!musicItem) {
33            lyricStateStore.setValue({
34                loading: false,
35                lyrics: [],
36            });
37
38            currentLyricStore.setValue({
39                lrc: 'MusicFree',
40                time: 0,
41            });
42
43            return;
44        }
45
46        const currentParserMusicItem = lyricStateStore
47            .getValue()
48            ?.lyricParser?.getCurrentMusicItem();
49
50        let rawLrc: string | undefined;
51        if (
52            forceRequest ||
53            !isSameMediaItem(currentParserMusicItem, musicItem)
54        ) {
55            lyricStateStore.setValue({
56                loading: true,
57                lyrics: [],
58            });
59            currentLyricStore.setValue(null);
60
61            rawLrc = await PluginManager.getByMedia(
62                musicItem,
63            )?.methods?.getLyricText(musicItem);
64        } else {
65            rawLrc = lyricStateStore.getValue().lyricParser!.raw;
66        }
67
68        const realtimeMusicItem = TrackPlayer.getCurrentMusic();
69        if (isSameMediaItem(musicItem, realtimeMusicItem)) {
70            if (rawLrc) {
71                const mediaExtra = MediaExtra.get(musicItem);
72                const parser = new LyricParser(rawLrc, musicItem, {
73                    offset: (mediaExtra?.lyricOffset || 0) * -1,
74                });
75                lyricStateStore.setValue({
76                    loading: false,
77                    lyricParser: parser,
78                    lyrics: parser.getLyric(),
79                    meta: parser.getMeta(),
80                });
81                // 更新当前状态的歌词
82                const currentLyric = fromStart
83                    ? parser.getLyric()[0]
84                    : parser.getPosition(
85                          (await TrackPlayer.getProgress()).position,
86                      ).lrc;
87                currentLyricStore.setValue(currentLyric || null);
88            } else {
89                // 没有歌词
90                lyricStateStore.setValue({
91                    loading: false,
92                    lyrics: [],
93                });
94            }
95        }
96    } catch {
97        const realtimeMusicItem = TrackPlayer.getCurrentMusic();
98        if (isSameMediaItem(musicItem, realtimeMusicItem)) {
99            // 异常情况
100            lyricStateStore.setValue({
101                loading: false,
102                lyrics: [],
103            });
104        }
105    }
106}
107
108// 获取歌词
109async function setup() {
110    DeviceEventEmitter.addListener(EDeviceEvents.REFRESH_LYRIC, refreshLyric);
111
112    if (Config.get('setting.lyric.showStatusBarLyric')) {
113        LyricUtil.showStatusBarLyric(
114            'MusicFree',
115            Config.get('setting.lyric') ?? {},
116        );
117    }
118
119    refreshLyric();
120}
121
122const LyricManager = {
123    setup,
124    useLyricState: lyricStateStore.useValue,
125    getLyricState: lyricStateStore.getValue,
126    useCurrentLyric: currentLyricStore.useValue,
127    getCurrentLyric: currentLyricStore.getValue,
128    setCurrentLyric: currentLyricStore.setValue,
129    refreshLyric,
130};
131
132export default LyricManager;
133