xref: /MusicFree/src/entry/bootstrap.ts (revision 9677305be11b30a8953a6c14fd24375953a2309d)
1import MusicSheet from '@/core/musicSheet';
2import {check, PERMISSIONS, request} from 'react-native-permissions';
3import RNTrackPlayer, {
4    AppKilledPlaybackBehavior,
5    Capability,
6} from 'react-native-track-player';
7import 'react-native-get-random-values';
8import Config from '@/core/config';
9import RNBootSplash from 'react-native-bootsplash';
10import pathConst from '@/constants/pathConst';
11import {checkAndCreateDir} from '@/utils/fileUtils';
12import {errorLog, trace} from '@/utils/log';
13import MediaMeta from '@/core/mediaMeta.old';
14import Cache from '@/core/cache.old';
15import PluginManager from '@/core/pluginManager';
16import Network from '@/core/network';
17import {ImgAsset} from '@/constants/assetsConst';
18import LocalMusicSheet from '@/core/localMusicSheet';
19import {Linking} from 'react-native';
20import Theme from '@/core/theme';
21import LyricManager from '@/core/lyricManager';
22import Toast from '@/utils/toast';
23import {localPluginHash, supportLocalMediaType} from '@/constants/commonConst';
24import TrackPlayer from '@/core/trackPlayer';
25import musicHistory from '@/core/musicHistory';
26import PersistStatus from '@/core/persistStatus';
27import {perfLogger} from '@/utils/perfLogger';
28
29/** app加载前执行
30 * 1. 检查权限
31 * 2. 数据初始化
32 * 3.
33 */
34
35async function _bootstrap() {
36    const logger = perfLogger();
37    // 1. 检查权限
38    const [readStoragePermission, writeStoragePermission] = await Promise.all([
39        check(PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE),
40        check(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE),
41    ]);
42    if (
43        !(
44            readStoragePermission === 'granted' &&
45            writeStoragePermission === 'granted'
46        )
47    ) {
48        await request(PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE);
49        await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
50    }
51    logger.mark('权限检查完成');
52
53    // 2. 数据初始化
54    /** 初始化路径 */
55    await setupFolder();
56    trace('文件夹初始化完成');
57    logger.mark('文件夹初始化完成');
58
59    // 加载配置
60    await Promise.all([
61        Config.setup().then(() => {
62            logger.mark('Config');
63        }),
64        MediaMeta.setup().then(() => {
65            logger.mark('MediaMeta');
66        }),
67        MusicSheet.setup().then(() => {
68            logger.mark('MusicSheet');
69        }),
70        musicHistory.setupMusicHistory().then(() => {
71            logger.mark('musicHistory');
72        }),
73    ]);
74    trace('配置初始化完成');
75    logger.mark('配置初始化完成');
76
77    // 加载插件
78    try {
79        await RNTrackPlayer.setupPlayer({
80            maxCacheSize:
81                Config.get('setting.basic.maxCacheSize') ?? 1024 * 1024 * 512,
82        });
83    } catch (e: any) {
84        if (
85            e?.message !==
86            'The player has already been initialized via setupPlayer.'
87        ) {
88            throw e;
89        }
90    }
91    logger.mark('加载播放器');
92
93    const capabilities = Config.get('setting.basic.showExitOnNotification')
94        ? [
95              Capability.Play,
96              Capability.Pause,
97              Capability.SkipToNext,
98              Capability.SkipToPrevious,
99              Capability.Stop,
100          ]
101        : [
102              Capability.Play,
103              Capability.Pause,
104              Capability.SkipToNext,
105              Capability.SkipToPrevious,
106          ];
107    await RNTrackPlayer.updateOptions({
108        icon: ImgAsset.logoTransparent,
109        progressUpdateEventInterval: 1,
110        android: {
111            alwaysPauseOnInterruption: true,
112            appKilledPlaybackBehavior:
113                AppKilledPlaybackBehavior.ContinuePlayback,
114        },
115        capabilities: capabilities,
116        compactCapabilities: capabilities,
117        notificationCapabilities: [...capabilities, Capability.SeekTo],
118    });
119    logger.mark('播放器初始化完成');
120    trace('播放器初始化完成');
121    await Cache.setup();
122    logger.mark('缓存初始化完成');
123
124    trace('缓存初始化完成');
125    await PluginManager.setup();
126    logger.mark('插件初始化完成');
127
128    trace('插件初始化完成');
129    await TrackPlayer.setupTrackPlayer();
130    trace('播放列表初始化完成');
131    logger.mark('播放列表初始化完成');
132
133    await LocalMusicSheet.setup();
134    trace('本地音乐初始化完成');
135    logger.mark('本地音乐初始化完成');
136
137    Theme.setup();
138    trace('主题初始化完成');
139    logger.mark('主题初始化完成');
140
141    await LyricManager.setup();
142
143    logger.mark('歌词初始化完成');
144
145    extraMakeup();
146    ErrorUtils.setGlobalHandler(error => {
147        errorLog('未捕获的错误', error);
148    });
149}
150
151/** 初始化 */
152async function setupFolder() {
153    await Promise.all([
154        checkAndCreateDir(pathConst.dataPath),
155        checkAndCreateDir(pathConst.logPath),
156        checkAndCreateDir(pathConst.cachePath),
157        checkAndCreateDir(pathConst.pluginPath),
158        checkAndCreateDir(pathConst.lrcCachePath),
159        checkAndCreateDir(pathConst.downloadPath).then(() => {
160            checkAndCreateDir(pathConst.downloadMusicPath);
161        }),
162    ]);
163}
164
165export default async function () {
166    try {
167        await _bootstrap();
168    } catch (e) {
169        errorLog('初始化出错', e);
170    }
171    // 隐藏开屏动画
172    console.log('HIDE');
173    RNBootSplash.hide({fade: true});
174}
175
176/** 不需要阻塞的 */
177async function extraMakeup() {
178    // 自动更新
179    try {
180        // 初始化网络状态
181        Network.setup();
182
183        if (Config.get('setting.basic.autoUpdatePlugin')) {
184            const lastUpdated = PersistStatus.get('app.pluginUpdateTime') || 0;
185            const now = Date.now();
186            if (Math.abs(now - lastUpdated) > 86400000) {
187                PersistStatus.set('app.pluginUpdateTime', now);
188                const plugins = PluginManager.getValidPlugins();
189                for (let i = 0; i < plugins.length; ++i) {
190                    const srcUrl = plugins[i].instance.srcUrl;
191                    if (srcUrl) {
192                        await PluginManager.installPluginFromUrl(srcUrl);
193                    }
194                }
195            }
196        }
197    } catch {}
198
199    async function handleLinkingUrl(url: string) {
200        // 插件
201        try {
202            if (url.startsWith('musicfree://install/')) {
203                const plugins = url
204                    .slice(20)
205                    .split(',')
206                    .map(decodeURIComponent);
207                await Promise.all(
208                    plugins.map(it =>
209                        PluginManager.installPluginFromUrl(it).catch(() => {}),
210                    ),
211                );
212                Toast.success('安装成功~');
213            } else if (url.endsWith('.js')) {
214                PluginManager.installPlugin(url, {
215                    notCheckVersion: Config.get(
216                        'setting.basic.notCheckPluginVersion',
217                    ),
218                })
219                    .then(res => {
220                        Toast.success(`插件「${res.name}」安装成功~`);
221                    })
222                    .catch(e => {
223                        console.log(e);
224                        Toast.warn(e?.message ?? '无法识别此插件');
225                    });
226            } else if (supportLocalMediaType.some(it => url.endsWith(it))) {
227                // 本地播放
228                const musicItem = await PluginManager.getByHash(
229                    localPluginHash,
230                )?.instance?.importMusicItem?.(url);
231                console.log(musicItem);
232                if (musicItem) {
233                    TrackPlayer.play(musicItem);
234                }
235            }
236        } catch {}
237    }
238
239    // 开启监听
240    Linking.addEventListener('url', data => {
241        if (data.url) {
242            handleLinkingUrl(data.url);
243        }
244    });
245    const initUrl = await Linking.getInitialURL();
246    if (initUrl) {
247        handleLinkingUrl(initUrl);
248    }
249
250    if (Config.get('setting.basic.autoPlayWhenAppStart')) {
251        TrackPlayer.play();
252    }
253}
254