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