xref: /MusicFree/src/entry/bootstrap.ts (revision 9c72192f09e3e71b70c1fb446447ba438c4fffbd)
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                    .then(res => {
165                        Toast.success(`插件「${res.name}」安装成功~`);
166                    })
167                    .catch(e => {
168                        console.log(e);
169                        Toast.warn(e?.message ?? '无法识别此插件');
170                    });
171            } else if (supportLocalMediaType.some(it => url.endsWith(it))) {
172                // 本地播放
173                const musicItem = await PluginManager.getByHash(
174                    localPluginHash,
175                )?.instance?.importMusicItem?.(url);
176                console.log(musicItem);
177                if (musicItem) {
178                    MusicQueue.play(musicItem);
179                }
180            }
181        } catch {}
182    }
183
184    // 开启监听
185    Linking.addEventListener('url', data => {
186        if (data.url) {
187            handleLinkingUrl(data.url);
188        }
189    });
190    const initUrl = await Linking.getInitialURL();
191    if (initUrl) {
192        handleLinkingUrl(initUrl);
193    }
194
195    if (Config.get('setting.basic.autoPlayWhenAppStart')) {
196        MusicQueue.play();
197    }
198}
199