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