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