xref: /MusicFree/src/entry/bootstrap.ts (revision a84a85c5181b457562342bf0bf1cf44d23df849b)
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, StatusBar} 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    StatusBar.setBackgroundColor('transparent');
108    StatusBar.setTranslucent(true);
109
110    extraMakeup();
111    ErrorUtils.setGlobalHandler(error => {
112        errorLog('未捕获的错误', error);
113    });
114}
115
116/** 初始化 */
117async function setupFolder() {
118    await Promise.all([
119        checkAndCreateDir(pathConst.dataPath),
120        checkAndCreateDir(pathConst.logPath),
121        checkAndCreateDir(pathConst.cachePath),
122        checkAndCreateDir(pathConst.pluginPath),
123        checkAndCreateDir(pathConst.lrcCachePath),
124        checkAndCreateDir(pathConst.downloadPath).then(() => {
125            checkAndCreateDir(pathConst.downloadMusicPath);
126        }),
127    ]);
128}
129
130export default async function () {
131    try {
132        await _bootstrap();
133    } catch (e) {
134        errorLog('初始化出错', e);
135    }
136    // 隐藏开屏动画
137    console.log('HIDE');
138    RNBootSplash.hide({fade: true});
139}
140
141/** 不需要阻塞的 */
142async function extraMakeup() {
143    // 自动更新
144    try {
145        if (Config.get('setting.basic.autoUpdatePlugin')) {
146            const lastUpdated =
147                (await getStorage('pluginLastupdatedTime')) || 0;
148            const now = Date.now();
149            if (Math.abs(now - lastUpdated) > 86400000) {
150                setStorage('pluginLastupdatedTime', now);
151                const plugins = PluginManager.getValidPlugins();
152                for (let i = 0; i < plugins.length; ++i) {
153                    const srcUrl = plugins[i].instance.srcUrl;
154                    if (srcUrl) {
155                        await PluginManager.installPluginFromUrl(srcUrl);
156                    }
157                }
158            }
159        }
160    } catch {}
161
162    async function handleLinkingUrl(url: string) {
163        // 插件
164        try {
165            if (url.endsWith('.js')) {
166                PluginManager.installPlugin(url)
167                    .then(res => {
168                        Toast.success(`插件「${res.name}」安装成功~`);
169                    })
170                    .catch(e => {
171                        console.log(e);
172                        Toast.warn(e?.message ?? '无法识别此插件');
173                    });
174            } else if (supportLocalMediaType.some(it => url.endsWith(it))) {
175                // 本地播放
176                const musicItem = await PluginManager.getByHash(
177                    localPluginHash,
178                )?.instance?.importMusicItem?.(url);
179                console.log(musicItem);
180                if (musicItem) {
181                    MusicQueue.play(musicItem);
182                }
183            }
184        } catch {}
185    }
186
187    // 开启监听
188    Linking.addEventListener('url', data => {
189        if (data.url) {
190            handleLinkingUrl(data.url);
191        }
192    });
193    const initUrl = await Linking.getInitialURL();
194    if (initUrl) {
195        handleLinkingUrl(initUrl);
196    }
197}
198