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