150245d53Smaotoumaoimport RNFS, { copyFile, exists, readDir, readFile, stat, unlink, writeFile } from "react-native-fs"; 250245d53Smaotoumaoimport CryptoJs from "crypto-js"; 350245d53Smaotoumaoimport dayjs from "dayjs"; 450245d53Smaotoumaoimport axios from "axios"; 550245d53Smaotoumaoimport bigInt from "big-integer"; 650245d53Smaotoumaoimport qs from "qs"; 750245d53Smaotoumaoimport * as webdav from "webdav"; 850245d53Smaotoumaoimport { InteractionManager, ToastAndroid } from "react-native"; 950245d53Smaotoumaoimport pathConst from "@/constants/pathConst"; 1050245d53Smaotoumaoimport { compare, satisfies } from "compare-versions"; 1150245d53Smaotoumaoimport DeviceInfo from "react-native-device-info"; 1250245d53Smaotoumaoimport deviceInfoModule from "react-native-device-info"; 1350245d53Smaotoumaoimport StateMapper from "@/utils/stateMapper"; 1450245d53Smaotoumaoimport MediaExtra from "./mediaExtra"; 1550245d53Smaotoumaoimport { nanoid } from "nanoid"; 1650245d53Smaotoumaoimport { devLog, errorLog, trace } from "../utils/log"; 1750245d53Smaotoumaoimport { getInternalData, InternalDataType, isSameMediaItem, resetMediaItem } from "@/utils/mediaItem"; 187993f90eS猫头猫import { 197993f90eS猫头猫 CacheControl, 20e08d37a3S猫头猫 emptyFunction, 217993f90eS猫头猫 internalSerializeKey, 227993f90eS猫头猫 localPluginHash, 2350245d53Smaotoumao localPluginPlatform 2450245d53Smaotoumao} from "@/constants/commonConst"; 2550245d53Smaotoumaoimport delay from "@/utils/delay"; 2650245d53Smaotoumaoimport * as cheerio from "cheerio"; 2750245d53Smaotoumaoimport he from "he"; 2850245d53Smaotoumaoimport Network from "./network"; 2950245d53Smaotoumaoimport LocalMusicSheet from "./localMusicSheet"; 3050245d53Smaotoumaoimport Mp3Util from "@/native/mp3Util"; 3150245d53Smaotoumaoimport { PluginMeta } from "./pluginMeta"; 3250245d53Smaotoumaoimport { useEffect, useState } from "react"; 3350245d53Smaotoumaoimport { addFileScheme, getFileName } from "@/utils/fileUtils"; 3450245d53Smaotoumaoimport { URL } from "react-native-url-polyfill"; 3550245d53Smaotoumaoimport Base64 from "@/utils/base64"; 3650245d53Smaotoumaoimport MediaCache from "./mediaCache"; 3750245d53Smaotoumaoimport { produce } from "immer"; 3850245d53Smaotoumaoimport objectPath from "object-path"; 3950245d53Smaotoumaoimport notImplementedFunction from "@/utils/notImplementedFunction.ts"; 4050245d53Smaotoumaoimport { readAsStringAsync } from "expo-file-system"; 41927dbe93S猫头猫 4261aca335S猫头猫axios.defaults.timeout = 2000; 43927dbe93S猫头猫 44927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 45927dbe93S猫头猫 46cfa0fc07S猫头猫export enum PluginStateCode { 47927dbe93S猫头猫 /** 版本不匹配 */ 48927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 49927dbe93S猫头猫 /** 无法解析 */ 50927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 51927dbe93S猫头猫} 52927dbe93S猫头猫 5333d52955Smaotoumaoconst deprecatedCookieManager = { 5433d52955Smaotoumao get: notImplementedFunction, 5533d52955Smaotoumao set: notImplementedFunction, 5633d52955Smaotoumao flush: notImplementedFunction, 5733d52955Smaotoumao}; 5833d52955Smaotoumao 599c34d637S猫头猫const packages: Record<string, any> = { 609c34d637S猫头猫 cheerio, 619c34d637S猫头猫 'crypto-js': CryptoJs, 629c34d637S猫头猫 axios, 639c34d637S猫头猫 dayjs, 649c34d637S猫头猫 'big-integer': bigInt, 659c34d637S猫头猫 qs, 669c34d637S猫头猫 he, 6733d52955Smaotoumao '@react-native-cookies/cookies': deprecatedCookieManager, 6828ccceb7S猫头猫 webdav, 699c34d637S猫头猫}; 709c34d637S猫头猫 71b43683eaS猫头猫const _require = (packageName: string) => { 72b43683eaS猫头猫 let pkg = packages[packageName]; 73b43683eaS猫头猫 pkg.default = pkg; 74b43683eaS猫头猫 return pkg; 75b43683eaS猫头猫}; 769c34d637S猫头猫 7753f8cd8eS猫头猫const _consoleBind = function ( 7853f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 7953f8cd8eS猫头猫 ...args: any 8053f8cd8eS猫头猫) { 8153f8cd8eS猫头猫 const fn = console[method]; 8253f8cd8eS猫头猫 if (fn) { 8353f8cd8eS猫头猫 fn(...args); 8453f8cd8eS猫头猫 devLog(method, ...args); 8553f8cd8eS猫头猫 } 8653f8cd8eS猫头猫}; 8753f8cd8eS猫头猫 8853f8cd8eS猫头猫const _console = { 8953f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9053f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9153f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9253f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9353f8cd8eS猫头猫}; 9453f8cd8eS猫头猫 95ab012f7fSmaotoumaoconst appVersion = deviceInfoModule.getVersion(); 96ab012f7fSmaotoumao 97a7b42a4cS猫头猫function formatAuthUrl(url: string) { 98a7b42a4cS猫头猫 const urlObj = new URL(url); 99a7b42a4cS猫头猫 100a7b42a4cS猫头猫 try { 101a7b42a4cS猫头猫 if (urlObj.username && urlObj.password) { 102a7b42a4cS猫头猫 const auth = `Basic ${Base64.btoa( 103a7b42a4cS猫头猫 `${decodeURIComponent(urlObj.username)}:${decodeURIComponent( 104a7b42a4cS猫头猫 urlObj.password, 105a7b42a4cS猫头猫 )}`, 106a7b42a4cS猫头猫 )}`; 107a7b42a4cS猫头猫 urlObj.username = ''; 108a7b42a4cS猫头猫 urlObj.password = ''; 109a7b42a4cS猫头猫 110a7b42a4cS猫头猫 return { 111a7b42a4cS猫头猫 url: urlObj.toString(), 112a7b42a4cS猫头猫 auth, 113a7b42a4cS猫头猫 }; 114a7b42a4cS猫头猫 } 115a7b42a4cS猫头猫 } catch (e) { 116a7b42a4cS猫头猫 return { 117a7b42a4cS猫头猫 url, 118a7b42a4cS猫头猫 }; 119a7b42a4cS猫头猫 } 120a7b42a4cS猫头猫 return { 121a7b42a4cS猫头猫 url, 122a7b42a4cS猫头猫 }; 123a7b42a4cS猫头猫} 124a7b42a4cS猫头猫 125d5bfeb7eS猫头猫//#region 插件类 126927dbe93S猫头猫export class Plugin { 127927dbe93S猫头猫 /** 插件名 */ 128927dbe93S猫头猫 public name: string; 129927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 130927dbe93S猫头猫 public hash: string; 131927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 132927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 133927dbe93S猫头猫 /** 插件状态信息 */ 134927dbe93S猫头猫 public stateCode?: PluginStateCode; 135927dbe93S猫头猫 /** 插件的实例 */ 136927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 137927dbe93S猫头猫 /** 插件路径 */ 138927dbe93S猫头猫 public path: string; 139927dbe93S猫头猫 /** 插件方法 */ 140927dbe93S猫头猫 public methods: PluginMethods; 141927dbe93S猫头猫 14274d0cf81S猫头猫 constructor( 14374d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 14474d0cf81S猫头猫 pluginPath: string, 14574d0cf81S猫头猫 ) { 146927dbe93S猫头猫 this.state = 'enabled'; 147927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1483b3d6357S猫头猫 const _module: any = {exports: {}}; 149927dbe93S猫头猫 try { 15074d0cf81S猫头猫 if (typeof funcCode === 'string') { 151e0caf342S猫头猫 // 插件的环境变量 152e0caf342S猫头猫 const env = { 153e0caf342S猫头猫 getUserVariables: () => { 154e0caf342S猫头猫 return ( 155e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 156e0caf342S猫头猫 ); 157e0caf342S猫头猫 }, 158ab012f7fSmaotoumao appVersion, 159e3fa9b3cS猫头猫 os: 'android', 160ab012f7fSmaotoumao lang: 'zh-CN' 161e0caf342S猫头猫 }; 162ab012f7fSmaotoumao const _process = { 163ab012f7fSmaotoumao platform: 'android', 164ab012f7fSmaotoumao version: appVersion, 165ab012f7fSmaotoumao env, 166ab012f7fSmaotoumao } 167e0caf342S猫头猫 1684060c00aS猫头猫 // eslint-disable-next-line no-new-func 169927dbe93S猫头猫 _instance = Function(` 170927dbe93S猫头猫 'use strict'; 171ab012f7fSmaotoumao return function(require, __musicfree_require, module, exports, console, env, URL, process) { 1729c34d637S猫头猫 ${funcCode} 173927dbe93S猫头猫 } 174e0caf342S猫头猫 `)()( 175e0caf342S猫头猫 _require, 176e0caf342S猫头猫 _require, 177e0caf342S猫头猫 _module, 178e0caf342S猫头猫 _module.exports, 179e0caf342S猫头猫 _console, 180e0caf342S猫头猫 env, 18191eb8fa8S猫头猫 URL, 182ab012f7fSmaotoumao _process 183e0caf342S猫头猫 ); 1843b3d6357S猫头猫 if (_module.exports.default) { 1853b3d6357S猫头猫 _instance = _module.exports 1863b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1873b3d6357S猫头猫 } else { 1889c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1893b3d6357S猫头猫 } 19074d0cf81S猫头猫 } else { 19174d0cf81S猫头猫 _instance = funcCode(); 19274d0cf81S猫头猫 } 193c2b3a262S猫头猫 // 插件初始化后的一些操作 19495297592S猫头猫 if (Array.isArray(_instance.userVariables)) { 19595297592S猫头猫 _instance.userVariables = _instance.userVariables.filter( 19695297592S猫头猫 it => it?.key, 19795297592S猫头猫 ); 19895297592S猫头猫 } 199927dbe93S猫头猫 this.checkValid(_instance); 200927dbe93S猫头猫 } catch (e: any) { 201b43683eaS猫头猫 console.log(e); 202927dbe93S猫头猫 this.state = 'error'; 203927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 204927dbe93S猫头猫 if (e?.stateCode) { 205927dbe93S猫头猫 this.stateCode = e.stateCode; 206927dbe93S猫头猫 } 207927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 208927dbe93S猫头猫 stateCode: this.stateCode, 209927dbe93S猫头猫 message: e?.message, 210927dbe93S猫头猫 stack: e?.stack, 211927dbe93S猫头猫 }); 212927dbe93S猫头猫 _instance = e?.instance ?? { 213927dbe93S猫头猫 _path: '', 214927dbe93S猫头猫 platform: '', 215927dbe93S猫头猫 appVersion: '', 21620e6a092S猫头猫 async getMediaSource() { 217927dbe93S猫头猫 return null; 218927dbe93S猫头猫 }, 219927dbe93S猫头猫 async search() { 220927dbe93S猫头猫 return {}; 221927dbe93S猫头猫 }, 222927dbe93S猫头猫 async getAlbumInfo() { 223927dbe93S猫头猫 return null; 224927dbe93S猫头猫 }, 225927dbe93S猫头猫 }; 226927dbe93S猫头猫 } 227927dbe93S猫头猫 this.instance = _instance; 228927dbe93S猫头猫 this.path = pluginPath; 229927dbe93S猫头猫 this.name = _instance.platform; 230ab8941d9S猫头猫 if ( 231ab8941d9S猫头猫 this.instance.platform === '' || 232ab8941d9S猫头猫 this.instance.platform === undefined 233ab8941d9S猫头猫 ) { 234927dbe93S猫头猫 this.hash = ''; 235927dbe93S猫头猫 } else { 23674d0cf81S猫头猫 if (typeof funcCode === 'string') { 237927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 23874d0cf81S猫头猫 } else { 23974d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 24074d0cf81S猫头猫 } 241927dbe93S猫头猫 } 242927dbe93S猫头猫 243927dbe93S猫头猫 // 放在最后 244927dbe93S猫头猫 this.methods = new PluginMethods(this); 245927dbe93S猫头猫 } 246927dbe93S猫头猫 247927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 248927dbe93S猫头猫 /** 版本号校验 */ 249927dbe93S猫头猫 if ( 250927dbe93S猫头猫 _instance.appVersion && 251927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 252927dbe93S猫头猫 ) { 253927dbe93S猫头猫 throw { 254927dbe93S猫头猫 instance: _instance, 255927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 256927dbe93S猫头猫 }; 257927dbe93S猫头猫 } 258927dbe93S猫头猫 return true; 259927dbe93S猫头猫 } 260927dbe93S猫头猫} 26142a9f3e6Smaotoumao 262d5bfeb7eS猫头猫//#endregion 263927dbe93S猫头猫 264d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 265927dbe93S猫头猫/** 有缓存等信息 */ 266927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 267927dbe93S猫头猫 private plugin; 26842a9f3e6Smaotoumao 269927dbe93S猫头猫 constructor(plugin: Plugin) { 270927dbe93S猫头猫 this.plugin = plugin; 271927dbe93S猫头猫 } 27242a9f3e6Smaotoumao 273927dbe93S猫头猫 /** 搜索 */ 274927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 275927dbe93S猫头猫 query: string, 276927dbe93S猫头猫 page: number, 277927dbe93S猫头猫 type: T, 278927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 279927dbe93S猫头猫 if (!this.plugin.instance.search) { 280927dbe93S猫头猫 return { 281927dbe93S猫头猫 isEnd: true, 282927dbe93S猫头猫 data: [], 283927dbe93S猫头猫 }; 284927dbe93S猫头猫 } 285927dbe93S猫头猫 2864060c00aS猫头猫 const result = 2874060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 288927dbe93S猫头猫 if (Array.isArray(result.data)) { 289927dbe93S猫头猫 result.data.forEach(_ => { 290927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 291927dbe93S猫头猫 }); 292927dbe93S猫头猫 return { 293927dbe93S猫头猫 isEnd: result.isEnd ?? true, 294927dbe93S猫头猫 data: result.data, 295927dbe93S猫头猫 }; 296927dbe93S猫头猫 } 297927dbe93S猫头猫 return { 298927dbe93S猫头猫 isEnd: true, 299927dbe93S猫头猫 data: [], 300927dbe93S猫头猫 }; 301927dbe93S猫头猫 } 302927dbe93S猫头猫 303927dbe93S猫头猫 /** 获取真实源 */ 30420e6a092S猫头猫 async getMediaSource( 305927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 306abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 307927dbe93S猫头猫 retryCount = 1, 308dc160d50S猫头猫 notUpdateCache = false, 309192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 310927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 31143eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 312927dbe93S猫头猫 const localPath = 31343eb30bfS猫头猫 mediaExtra?.localPath || 31443eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3150e4173cdS猫头猫 getInternalData<string>( 3160e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3170e4173cdS猫头猫 InternalDataType.LOCALPATH, 3180e4173cdS猫头猫 ); 31942a9f3e6Smaotoumao if (localPath && (await exists(localPath))) { 3200e4173cdS猫头猫 trace('本地播放', localPath); 32143eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 32243eb30bfS猫头猫 // 修正一下本地数据 32343eb30bfS猫头猫 MediaExtra.update(musicItem, { 32443eb30bfS猫头猫 localPath, 32543eb30bfS猫头猫 }); 32643eb30bfS猫头猫 } 327927dbe93S猫头猫 return { 32811908939S猫头猫 url: addFileScheme(localPath), 329927dbe93S猫头猫 }; 33043eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 33143eb30bfS猫头猫 MediaExtra.update(musicItem, { 33243eb30bfS猫头猫 localPath: undefined, 33343eb30bfS猫头猫 }); 334927dbe93S猫头猫 } 335a84a85c5S猫头猫 3367993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 337f5935920S猫头猫 throw new Error('本地音乐不存在'); 338f5935920S猫头猫 } 339927dbe93S猫头猫 // 2. 缓存播放 34043eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 34143eb30bfS猫头猫 musicItem, 34243eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 343985f8e75S猫头猫 const pluginCacheControl = 344985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 345cfa0fc07S猫头猫 if ( 346cfa0fc07S猫头猫 mediaCache && 34743eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34848f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34948f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 350ef714860S猫头猫 Network.isOffline())) 351cfa0fc07S猫头猫 ) { 3525276aef9S猫头猫 trace('播放', '缓存播放'); 35343eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 354927dbe93S猫头猫 return { 35543eb30bfS猫头猫 url: qualityInfo!.url, 356927dbe93S猫头猫 headers: mediaCache.headers, 3574060c00aS猫头猫 userAgent: 3584060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 359927dbe93S猫头猫 }; 360927dbe93S猫头猫 } 361927dbe93S猫头猫 // 3. 插件解析 36220e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 363a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 364a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 365a7b42a4cS猫头猫 ); 366a7b42a4cS猫头猫 return { 367a7b42a4cS猫头猫 url: url, 368a7b42a4cS猫头猫 headers: auth 369a7b42a4cS猫头猫 ? { 370a7b42a4cS猫头猫 Authorization: auth, 371a7b42a4cS猫头猫 } 372a7b42a4cS猫头猫 : undefined, 373a7b42a4cS猫头猫 }; 374927dbe93S猫头猫 } 375927dbe93S猫头猫 try { 376abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 377abaede57S猫头猫 musicItem, 378abaede57S猫头猫 quality, 379abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 380927dbe93S猫头猫 if (!url) { 381a28eac61S猫头猫 throw new Error('NOT RETRY'); 382927dbe93S猫头猫 } 3835276aef9S猫头猫 trace('播放', '插件播放'); 384927dbe93S猫头猫 const result = { 385927dbe93S猫头猫 url, 386927dbe93S猫头猫 headers, 387927dbe93S猫头猫 userAgent: headers?.['user-agent'], 388cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 389a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 390a7b42a4cS猫头猫 if (authFormattedResult.auth) { 391a7b42a4cS猫头猫 result.url = authFormattedResult.url; 392a7b42a4cS猫头猫 result.headers = { 393a7b42a4cS猫头猫 ...(result.headers ?? {}), 394a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 395a7b42a4cS猫头猫 }; 396a7b42a4cS猫头猫 } 397927dbe93S猫头猫 398dc160d50S猫头猫 if ( 399dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 400dc160d50S猫头猫 !notUpdateCache 401dc160d50S猫头猫 ) { 40243eb30bfS猫头猫 // 更新缓存 40343eb30bfS猫头猫 const cacheSource = { 40443eb30bfS猫头猫 headers: result.headers, 40543eb30bfS猫头猫 userAgent: result.userAgent, 40643eb30bfS猫头猫 url, 40743eb30bfS猫头猫 }; 40843eb30bfS猫头猫 let realMusicItem = { 40943eb30bfS猫头猫 ...musicItem, 41043eb30bfS猫头猫 ...(mediaCache || {}), 41143eb30bfS猫头猫 }; 41243eb30bfS猫头猫 realMusicItem.source = { 41343eb30bfS猫头猫 ...(realMusicItem.source || {}), 41443eb30bfS猫头猫 [quality]: cacheSource, 41543eb30bfS猫头猫 }; 41643eb30bfS猫头猫 41743eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 418752ffc5aS猫头猫 } 419927dbe93S猫头猫 return result; 420927dbe93S猫头猫 } catch (e: any) { 421a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 422927dbe93S猫头猫 await delay(150); 423abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 424927dbe93S猫头猫 } 425927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 426ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 427192ae2b0S猫头猫 return null; 428927dbe93S猫头猫 } 429927dbe93S猫头猫 } 430927dbe93S猫头猫 431927dbe93S猫头猫 /** 获取音乐详情 */ 432927dbe93S猫头猫 async getMusicInfo( 433927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 43474d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 435927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 436d704daedS猫头猫 return null; 437927dbe93S猫头猫 } 43874d0cf81S猫头猫 try { 439927dbe93S猫头猫 return ( 440927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4417993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 442d704daedS猫头猫 ) ?? null 443927dbe93S猫头猫 ); 444ea6d708fS猫头猫 } catch (e: any) { 445ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 446d704daedS猫头猫 return null; 44774d0cf81S猫头猫 } 448927dbe93S猫头猫 } 449927dbe93S猫头猫 4507e883dbbS猫头猫 /** 4517e883dbbS猫头猫 * 4527e883dbbS猫头猫 * getLyric(musicItem) => { 4537e883dbbS猫头猫 * lyric: string; 4547e883dbbS猫头猫 * trans: string; 4557e883dbbS猫头猫 * } 4567e883dbbS猫头猫 * 4577e883dbbS猫头猫 */ 458927dbe93S猫头猫 /** 获取歌词 */ 459927dbe93S猫头猫 async getLyric( 46043eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 461927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 46243eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 4635589cdf3S猫头猫 const meta = MediaExtra.get(originalMusicItem); 46443eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 465927dbe93S猫头猫 if (meta && meta.associatedLrc) { 46643eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 46743eb30bfS猫头猫 } else { 46843eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 469927dbe93S猫头猫 } 47043eb30bfS猫头猫 47143eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4727a91f04fS猫头猫 musicItem, 47343eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 47443eb30bfS猫头猫 47543eb30bfS猫头猫 /** 原始歌词文本 */ 4767e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4777e883dbbS猫头猫 let translation: string | null = null; 47843eb30bfS猫头猫 47926f32636S猫头猫 // 2. 本地手动设置的歌词 48026f32636S猫头猫 const platformHash = CryptoJs.MD5(musicItem.platform).toString( 48126f32636S猫头猫 CryptoJs.enc.Hex, 48226f32636S猫头猫 ); 48326f32636S猫头猫 const idHash = CryptoJs.MD5(musicItem.id).toString(CryptoJs.enc.Hex); 48426f32636S猫头猫 if ( 48526f32636S猫头猫 await RNFS.exists( 48626f32636S猫头猫 pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc', 48726f32636S猫头猫 ) 48826f32636S猫头猫 ) { 48926f32636S猫头猫 rawLrc = await RNFS.readFile( 49026f32636S猫头猫 pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc', 49126f32636S猫头猫 'utf8', 49226f32636S猫头猫 ); 49326f32636S猫头猫 49426f32636S猫头猫 if ( 49526f32636S猫头猫 await RNFS.exists( 49626f32636S猫头猫 pathConst.localLrcPath + 49726f32636S猫头猫 platformHash + 49826f32636S猫头猫 '/' + 49926f32636S猫头猫 idHash + 50026f32636S猫头猫 '.tran.lrc', 50126f32636S猫头猫 ) 50226f32636S猫头猫 ) { 50326f32636S猫头猫 translation = 50426f32636S猫头猫 (await RNFS.readFile( 50526f32636S猫头猫 pathConst.localLrcPath + 50626f32636S猫头猫 platformHash + 50726f32636S猫头猫 '/' + 50826f32636S猫头猫 idHash + 50926f32636S猫头猫 '.tran.lrc', 51026f32636S猫头猫 'utf8', 51126f32636S猫头猫 )) || null; 51226f32636S猫头猫 } 51326f32636S猫头猫 51426f32636S猫头猫 return { 51526f32636S猫头猫 rawLrc, 51626f32636S猫头猫 translation: translation || undefined, // TODO: 这里写的不好 51726f32636S猫头猫 }; 51826f32636S猫头猫 } 51926f32636S猫头猫 52043eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 52143eb30bfS猫头猫 if (musicItemCache?.lyric) { 52243eb30bfS猫头猫 // 缓存的远程结果 52343eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 52443eb30bfS猫头猫 musicItemCache.lyric || null; 52543eb30bfS猫头猫 // 缓存的本地结果 52643eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 52743eb30bfS猫头猫 musicItemCache.$localLyric || null; 52843eb30bfS猫头猫 5297e883dbbS猫头猫 // 优先用缓存的结果 5307e883dbbS猫头猫 if (cacheLyric.rawLrc || cacheLyric.translation) { 53143eb30bfS猫头猫 return { 5327e883dbbS猫头猫 rawLrc: cacheLyric.rawLrc, 5337e883dbbS猫头猫 translation: cacheLyric.translation, 53443eb30bfS猫头猫 }; 53543eb30bfS猫头猫 } 53643eb30bfS猫头猫 5377e883dbbS猫头猫 // 本地其实是缓存的路径 5387e883dbbS猫头猫 if (localLyric) { 5397e883dbbS猫头猫 let needRefetch = false; 5407e883dbbS猫头猫 if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) { 5417e883dbbS猫头猫 rawLrc = await readFile(localLyric.rawLrc, 'utf8'); 5427e883dbbS猫头猫 } else if (localLyric.rawLrc) { 5437e883dbbS猫头猫 needRefetch = true; 5447e883dbbS猫头猫 } 5457e883dbbS猫头猫 if ( 5467e883dbbS猫头猫 localLyric.translation && 5477e883dbbS猫头猫 (await exists(localLyric.translation)) 5487e883dbbS猫头猫 ) { 5497e883dbbS猫头猫 translation = await readFile( 5507e883dbbS猫头猫 localLyric.translation, 5517e883dbbS猫头猫 'utf8', 5527e883dbbS猫头猫 ); 5537e883dbbS猫头猫 } else if (localLyric.translation) { 5547e883dbbS猫头猫 needRefetch = true; 55543eb30bfS猫头猫 } 55643eb30bfS猫头猫 5577e883dbbS猫头猫 if (!needRefetch && (rawLrc || translation)) { 55843eb30bfS猫头猫 return { 5597e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5607e883dbbS猫头猫 translation: translation || undefined, 56143eb30bfS猫头猫 }; 56243eb30bfS猫头猫 } 56343eb30bfS猫头猫 } 56443eb30bfS猫头猫 } 56543eb30bfS猫头猫 56643eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 56743eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 56843eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 56943eb30bfS猫头猫 lrcSource = 57043eb30bfS猫头猫 (await this.plugin.instance 5717e883dbbS猫头猫 ?.getLyric?.(resetMediaItem(musicItem, undefined, true)) 57243eb30bfS猫头猫 ?.catch(() => null)) || null; 57343eb30bfS猫头猫 } else { 57443eb30bfS猫头猫 lrcSource = 57543eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 57643eb30bfS猫头猫 ?.instance?.getLyric?.( 57743eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 57843eb30bfS猫头猫 ) 57943eb30bfS猫头猫 ?.catch(() => null)) || null; 58043eb30bfS猫头猫 } 58143eb30bfS猫头猫 58243eb30bfS猫头猫 if (lrcSource) { 5837e883dbbS猫头猫 rawLrc = lrcSource?.rawLrc || rawLrc; 5847e883dbbS猫头猫 translation = lrcSource?.translation || null; 58543eb30bfS猫头猫 5867e883dbbS猫头猫 const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc; 58743eb30bfS猫头猫 5887e883dbbS猫头猫 // 本地的文件名 5897e883dbbS猫头猫 let filename: string | undefined = `${ 5907e883dbbS猫头猫 pathConst.lrcCachePath 5917e883dbbS猫头猫 }${nanoid()}.lrc`; 5927e883dbbS猫头猫 let filenameTrans: string | undefined = `${ 5937e883dbbS猫头猫 pathConst.lrcCachePath 5947e883dbbS猫头猫 }${nanoid()}.lrc`; 59543eb30bfS猫头猫 5967e883dbbS猫头猫 // 旧版本兼容 5977e883dbbS猫头猫 if (!(rawLrc || translation)) { 5987e883dbbS猫头猫 if (deprecatedLrcUrl) { 59943eb30bfS猫头猫 rawLrc = ( 6007e883dbbS猫头猫 await axios 6017e883dbbS猫头猫 .get(deprecatedLrcUrl, {timeout: 3000}) 6027e883dbbS猫头猫 .catch(() => null) 60343eb30bfS猫头猫 )?.data; 6047e883dbbS猫头猫 } else if (musicItem.rawLrc) { 6057e883dbbS猫头猫 rawLrc = musicItem.rawLrc; 6067e883dbbS猫头猫 } 60743eb30bfS猫头猫 } 60843eb30bfS猫头猫 60943eb30bfS猫头猫 if (rawLrc) { 61043eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 61143eb30bfS猫头猫 } else { 6127e883dbbS猫头猫 filename = undefined; 6137e883dbbS猫头猫 } 6147e883dbbS猫头猫 if (translation) { 6157e883dbbS猫头猫 await writeFile(filenameTrans, translation, 'utf8'); 6167e883dbbS猫头猫 } else { 6177e883dbbS猫头猫 filenameTrans = undefined; 6187a91f04fS猫头猫 } 6197a91f04fS猫头猫 6207e883dbbS猫头猫 if (rawLrc || translation) { 6217e883dbbS猫头猫 MediaCache.setMediaCache( 6227e883dbbS猫头猫 produce(musicItemCache || musicItem, draft => { 6237e883dbbS猫头猫 musicItemCache?.$localLyric?.rawLrc; 6247e883dbbS猫头猫 objectPath.set(draft, '$localLyric.rawLrc', filename); 62543eb30bfS猫头猫 objectPath.set( 62643eb30bfS猫头猫 draft, 6277e883dbbS猫头猫 '$localLyric.translation', 6287e883dbbS猫头猫 filenameTrans, 62943eb30bfS猫头猫 ); 63043eb30bfS猫头猫 return draft; 63143eb30bfS猫头猫 }), 63243eb30bfS猫头猫 ); 633927dbe93S猫头猫 return { 6347e883dbbS猫头猫 rawLrc: rawLrc || undefined, 6357e883dbbS猫头猫 translation: translation || undefined, 636927dbe93S猫头猫 }; 637927dbe93S猫头猫 } 638927dbe93S猫头猫 } 63943eb30bfS猫头猫 6403a6f67b1S猫头猫 // 6. 如果是本地文件 64143eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 64243eb30bfS猫头猫 if ( 64343eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 64443eb30bfS猫头猫 isDownloaded 64543eb30bfS猫头猫 ) { 6467e883dbbS猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 64743eb30bfS猫头猫 64843eb30bfS猫头猫 console.log('本地文件歌词'); 64943eb30bfS猫头猫 6503a6f67b1S猫头猫 if (res) { 6513a6f67b1S猫头猫 return res; 6523a6f67b1S猫头猫 } 6533a6f67b1S猫头猫 } 654ea6d708fS猫头猫 devLog('warn', '无歌词'); 655927dbe93S猫头猫 656927dbe93S猫头猫 return null; 657927dbe93S猫头猫 } 658927dbe93S猫头猫 659927dbe93S猫头猫 /** 获取歌词文本 */ 660927dbe93S猫头猫 async getLyricText( 661927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 662927dbe93S猫头猫 ): Promise<string | undefined> { 6637e883dbbS猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 664927dbe93S猫头猫 } 665927dbe93S猫头猫 666927dbe93S猫头猫 /** 获取专辑信息 */ 667927dbe93S猫头猫 async getAlbumInfo( 668927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 669f9afcc0dS猫头猫 page: number = 1, 670f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 671927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 672f9afcc0dS猫头猫 return { 673f9afcc0dS猫头猫 albumItem, 674f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 675f2a4767cS猫头猫 resetMediaItem, 676f2a4767cS猫头猫 this.plugin.name, 677f2a4767cS猫头猫 true, 678f2a4767cS猫头猫 ), 679f9afcc0dS猫头猫 isEnd: true, 680f9afcc0dS猫头猫 }; 681927dbe93S猫头猫 } 682927dbe93S猫头猫 try { 683927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 684927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 685f9afcc0dS猫头猫 page, 686927dbe93S猫头猫 ); 6875276aef9S猫头猫 if (!result) { 6885276aef9S猫头猫 throw new Error(); 6895276aef9S猫头猫 } 690927dbe93S猫头猫 result?.musicList?.forEach(_ => { 691927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 69296744680S猫头猫 _.album = albumItem.title; 693927dbe93S猫头猫 }); 6945276aef9S猫头猫 695f9afcc0dS猫头猫 if (page <= 1) { 696f9afcc0dS猫头猫 // 合并信息 697f9afcc0dS猫头猫 return { 698f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 699f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 700f9afcc0dS猫头猫 musicList: result.musicList, 701f9afcc0dS猫头猫 }; 702f9afcc0dS猫头猫 } else { 703f9afcc0dS猫头猫 return { 704f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 705f9afcc0dS猫头猫 musicList: result.musicList, 706f9afcc0dS猫头猫 }; 707f9afcc0dS猫头猫 } 7084394410dS猫头猫 } catch (e: any) { 7094394410dS猫头猫 trace('获取专辑信息失败', e?.message); 710ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 711ea6d708fS猫头猫 712f9afcc0dS猫头猫 return null; 713927dbe93S猫头猫 } 714927dbe93S猫头猫 } 715927dbe93S猫头猫 7165830c002S猫头猫 /** 获取歌单信息 */ 7175830c002S猫头猫 async getMusicSheetInfo( 7185830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 7195830c002S猫头猫 page: number = 1, 7205830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 7215281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 7225830c002S猫头猫 return { 7235830c002S猫头猫 sheetItem, 7245830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 7255830c002S猫头猫 isEnd: true, 7265830c002S猫头猫 }; 7275830c002S猫头猫 } 7285830c002S猫头猫 try { 7295830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 7305830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 7315830c002S猫头猫 page, 7325830c002S猫头猫 ); 7335830c002S猫头猫 if (!result) { 7345830c002S猫头猫 throw new Error(); 7355830c002S猫头猫 } 7365830c002S猫头猫 result?.musicList?.forEach(_ => { 7375830c002S猫头猫 resetMediaItem(_, this.plugin.name); 7385830c002S猫头猫 }); 7395830c002S猫头猫 7405830c002S猫头猫 if (page <= 1) { 7415830c002S猫头猫 // 合并信息 7425830c002S猫头猫 return { 7435830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 7445830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7455830c002S猫头猫 musicList: result.musicList, 7465830c002S猫头猫 }; 7475830c002S猫头猫 } else { 7485830c002S猫头猫 return { 7495830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7505830c002S猫头猫 musicList: result.musicList, 7515830c002S猫头猫 }; 7525830c002S猫头猫 } 7535830c002S猫头猫 } catch (e: any) { 7545830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7555830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7565830c002S猫头猫 7575830c002S猫头猫 return null; 7585830c002S猫头猫 } 7595830c002S猫头猫 } 7605830c002S猫头猫 761927dbe93S猫头猫 /** 查询作者信息 */ 762efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 763927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 764927dbe93S猫头猫 page: number, 765927dbe93S猫头猫 type: T, 766927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 767efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 768927dbe93S猫头猫 return { 769927dbe93S猫头猫 isEnd: true, 770927dbe93S猫头猫 data: [], 771927dbe93S猫头猫 }; 772927dbe93S猫头猫 } 773927dbe93S猫头猫 try { 774efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 775927dbe93S猫头猫 artistItem, 776927dbe93S猫头猫 page, 777927dbe93S猫头猫 type, 778927dbe93S猫头猫 ); 779927dbe93S猫头猫 if (!result.data) { 780927dbe93S猫头猫 return { 781927dbe93S猫头猫 isEnd: true, 782927dbe93S猫头猫 data: [], 783927dbe93S猫头猫 }; 784927dbe93S猫头猫 } 785927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 786927dbe93S猫头猫 return { 787927dbe93S猫头猫 isEnd: result.isEnd ?? true, 788927dbe93S猫头猫 data: result.data, 789927dbe93S猫头猫 }; 7904394410dS猫头猫 } catch (e: any) { 7914394410dS猫头猫 trace('查询作者信息失败', e?.message); 792ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 793ea6d708fS猫头猫 794927dbe93S猫头猫 throw e; 795927dbe93S猫头猫 } 796927dbe93S猫头猫 } 79708380090S猫头猫 79808380090S猫头猫 /** 导入歌单 */ 79908380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 80008380090S猫头猫 try { 80108380090S猫头猫 const result = 80208380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 80308380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 80408380090S猫头猫 return result; 805ea6d708fS猫头猫 } catch (e: any) { 8060e4173cdS猫头猫 console.log(e); 807ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 808ea6d708fS猫头猫 80908380090S猫头猫 return []; 81008380090S猫头猫 } 81108380090S猫头猫 } 81242a9f3e6Smaotoumao 8134d9d3c4cS猫头猫 /** 导入单曲 */ 8144d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 8154d9d3c4cS猫头猫 try { 8164d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 8174d9d3c4cS猫头猫 urlLike, 8184d9d3c4cS猫头猫 ); 8194d9d3c4cS猫头猫 if (!result) { 8204d9d3c4cS猫头猫 throw new Error(); 8214d9d3c4cS猫头猫 } 8224d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 8234d9d3c4cS猫头猫 return result; 824ea6d708fS猫头猫 } catch (e: any) { 825ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 826ea6d708fS猫头猫 8274d9d3c4cS猫头猫 return null; 8284d9d3c4cS猫头猫 } 8294d9d3c4cS猫头猫 } 83042a9f3e6Smaotoumao 831d52aa40eS猫头猫 /** 获取榜单 */ 83292b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 833d52aa40eS猫头猫 try { 834d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 835d52aa40eS猫头猫 if (!result) { 836d52aa40eS猫头猫 throw new Error(); 837d52aa40eS猫头猫 } 838d52aa40eS猫头猫 return result; 839d52aa40eS猫头猫 } catch (e: any) { 840d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 841d52aa40eS猫头猫 return []; 842d52aa40eS猫头猫 } 843d52aa40eS猫头猫 } 84442a9f3e6Smaotoumao 845d52aa40eS猫头猫 /** 获取榜单详情 */ 846d52aa40eS猫头猫 async getTopListDetail( 84792b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 848956ee1b7S猫头猫 page: number, 849956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 850d52aa40eS猫头猫 try { 851d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 852d52aa40eS猫头猫 topListItem, 853956ee1b7S猫头猫 page, 854d52aa40eS猫头猫 ); 855d52aa40eS猫头猫 if (!result) { 856d52aa40eS猫头猫 throw new Error(); 857d52aa40eS猫头猫 } 858d384662fS猫头猫 if (result.musicList) { 859d384662fS猫头猫 result.musicList.forEach(_ => 860d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 861d384662fS猫头猫 ); 862d384662fS猫头猫 } 863956ee1b7S猫头猫 if (result.isEnd !== false) { 864956ee1b7S猫头猫 result.isEnd = true; 865956ee1b7S猫头猫 } 866d52aa40eS猫头猫 return result; 867d52aa40eS猫头猫 } catch (e: any) { 868d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 869d52aa40eS猫头猫 return { 870956ee1b7S猫头猫 isEnd: true, 871956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 872d52aa40eS猫头猫 musicList: [], 873d52aa40eS猫头猫 }; 874d52aa40eS猫头猫 } 875d52aa40eS猫头猫 } 876ceb900cdS猫头猫 8775830c002S猫头猫 /** 获取推荐歌单的tag */ 878ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 879ceb900cdS猫头猫 try { 880ceb900cdS猫头猫 const result = 881ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 882ceb900cdS猫头猫 if (!result) { 883ceb900cdS猫头猫 throw new Error(); 884ceb900cdS猫头猫 } 885ceb900cdS猫头猫 return result; 886ceb900cdS猫头猫 } catch (e: any) { 887ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 888ceb900cdS猫头猫 return { 889ceb900cdS猫头猫 data: [], 890ceb900cdS猫头猫 }; 891ceb900cdS猫头猫 } 892ceb900cdS猫头猫 } 89342a9f3e6Smaotoumao 8945830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 895ceb900cdS猫头猫 async getRecommendSheetsByTag( 896ceb900cdS猫头猫 tagItem: ICommon.IUnique, 897ceb900cdS猫头猫 page?: number, 898ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 899ceb900cdS猫头猫 try { 900ceb900cdS猫头猫 const result = 901ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 902ceb900cdS猫头猫 tagItem, 903ceb900cdS猫头猫 page ?? 1, 904ceb900cdS猫头猫 ); 905ceb900cdS猫头猫 if (!result) { 906ceb900cdS猫头猫 throw new Error(); 907ceb900cdS猫头猫 } 908ceb900cdS猫头猫 if (result.isEnd !== false) { 909ceb900cdS猫头猫 result.isEnd = true; 910ceb900cdS猫头猫 } 911ceb900cdS猫头猫 if (!result.data) { 912ceb900cdS猫头猫 result.data = []; 913ceb900cdS猫头猫 } 914ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 915ceb900cdS猫头猫 916ceb900cdS猫头猫 return result; 917ceb900cdS猫头猫 } catch (e: any) { 918ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 919ceb900cdS猫头猫 return { 920ceb900cdS猫头猫 isEnd: true, 921ceb900cdS猫头猫 data: [], 922ceb900cdS猫头猫 }; 923ceb900cdS猫头猫 } 924ceb900cdS猫头猫 } 925da2a2959S猫头猫 926da2a2959S猫头猫 async getMusicComments( 927da2a2959S猫头猫 musicItem: IMusic.IMusicItem, 928da2a2959S猫头猫 ): Promise<ICommon.PaginationResponse<IMedia.IComment>> { 929da2a2959S猫头猫 const result = await this.plugin.instance?.getMusicComments?.( 930da2a2959S猫头猫 musicItem, 931da2a2959S猫头猫 ); 932da2a2959S猫头猫 if (!result) { 933da2a2959S猫头猫 throw new Error(); 934da2a2959S猫头猫 } 935da2a2959S猫头猫 if (result.isEnd !== false) { 936da2a2959S猫头猫 result.isEnd = true; 937da2a2959S猫头猫 } 938da2a2959S猫头猫 if (!result.data) { 939da2a2959S猫头猫 result.data = []; 940da2a2959S猫头猫 } 941da2a2959S猫头猫 942da2a2959S猫头猫 return result; 943da2a2959S猫头猫 } 9447fb90110S猫头猫 9457fb90110S猫头猫 async migrateFromOtherPlugin( 9467fb90110S猫头猫 mediaItem: ICommon.IMediaBase, 9477fb90110S猫头猫 fromPlatform: string, 9487fb90110S猫头猫 ): Promise<{isOk: boolean; data?: ICommon.IMediaBase}> { 9497fb90110S猫头猫 try { 9507fb90110S猫头猫 const result = await this.plugin.instance?.migrateFromOtherPlugin( 9517fb90110S猫头猫 mediaItem, 9527fb90110S猫头猫 fromPlatform, 9537fb90110S猫头猫 ); 9547fb90110S猫头猫 9557fb90110S猫头猫 if ( 9567fb90110S猫头猫 result.isOk && 9577fb90110S猫头猫 result.data?.id && 9587fb90110S猫头猫 result.data?.platform === this.plugin.platform 9597fb90110S猫头猫 ) { 9607fb90110S猫头猫 return { 9617fb90110S猫头猫 isOk: result.isOk, 9627fb90110S猫头猫 data: result.data, 9637fb90110S猫头猫 }; 9647fb90110S猫头猫 } 9657fb90110S猫头猫 return { 9667fb90110S猫头猫 isOk: false, 9677fb90110S猫头猫 }; 9687fb90110S猫头猫 } catch { 9697fb90110S猫头猫 return { 9707fb90110S猫头猫 isOk: false, 9717fb90110S猫头猫 }; 9727fb90110S猫头猫 } 9737fb90110S猫头猫 } 974927dbe93S猫头猫} 97533d52955Smaotoumao 976d5bfeb7eS猫头猫//#endregion 9771a5528a0S猫头猫 978927dbe93S猫头猫let plugins: Array<Plugin> = []; 979927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 98074d0cf81S猫头猫 981d5bfeb7eS猫头猫//#region 本地音乐插件 98274d0cf81S猫头猫/** 本地插件 */ 98374d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 9840e4173cdS猫头猫 return { 985d5bfeb7eS猫头猫 platform: localPluginPlatform, 98674d0cf81S猫头猫 _path: '', 98774d0cf81S猫头猫 async getMusicInfo(musicBase) { 98874d0cf81S猫头猫 const localPath = getInternalData<string>( 98974d0cf81S猫头猫 musicBase, 99074d0cf81S猫头猫 InternalDataType.LOCALPATH, 9910e4173cdS猫头猫 ); 99274d0cf81S猫头猫 if (localPath) { 99374d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 99474d0cf81S猫头猫 return { 99574d0cf81S猫头猫 artwork: coverImg, 99674d0cf81S猫头猫 }; 99774d0cf81S猫头猫 } 99874d0cf81S猫头猫 return null; 99974d0cf81S猫头猫 }, 10007993f90eS猫头猫 async getLyric(musicBase) { 10017993f90eS猫头猫 const localPath = getInternalData<string>( 10027993f90eS猫头猫 musicBase, 10037993f90eS猫头猫 InternalDataType.LOCALPATH, 10047993f90eS猫头猫 ); 10053a6f67b1S猫头猫 let rawLrc: string | null = null; 10067993f90eS猫头猫 if (localPath) { 10073a6f67b1S猫头猫 // 读取内嵌歌词 10083a6f67b1S猫头猫 try { 10093a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 10103a6f67b1S猫头猫 } catch (e) { 1011a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 10127993f90eS猫头猫 } 10133a6f67b1S猫头猫 if (!rawLrc) { 10143a6f67b1S猫头猫 // 读取配置歌词 10153a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 10163a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 10173a6f67b1S猫头猫 10183a6f67b1S猫头猫 try { 10193a6f67b1S猫头猫 if (await exists(lrcPath)) { 10203a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 10213a6f67b1S猫头猫 } 10223a6f67b1S猫头猫 } catch {} 10233a6f67b1S猫头猫 } 10243a6f67b1S猫头猫 } 10253a6f67b1S猫头猫 10263a6f67b1S猫头猫 return rawLrc 10273a6f67b1S猫头猫 ? { 10283a6f67b1S猫头猫 rawLrc, 10293a6f67b1S猫头猫 } 10303a6f67b1S猫头猫 : null; 10317993f90eS猫头猫 }, 1032a84a85c5S猫头猫 async importMusicItem(urlLike) { 1033a84a85c5S猫头猫 let meta: any = {}; 103442a9f3e6Smaotoumao let id: string; 103542a9f3e6Smaotoumao 1036a84a85c5S猫头猫 try { 1037a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 103842a9f3e6Smaotoumao const fileStat = await stat(urlLike); 103942a9f3e6Smaotoumao id = 104042a9f3e6Smaotoumao CryptoJs.MD5(fileStat.originalFilepath).toString( 104142a9f3e6Smaotoumao CryptoJs.enc.Hex, 104242a9f3e6Smaotoumao ) || nanoid(); 104342a9f3e6Smaotoumao } catch { 10445353b473S猫头猫 id = nanoid(); 10455353b473S猫头猫 } 104642a9f3e6Smaotoumao 1047a84a85c5S猫头猫 return { 1048a84a85c5S猫头猫 id: id, 1049a84a85c5S猫头猫 platform: '本地', 1050a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 1051a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 10525353b473S猫头猫 duration: parseInt(meta?.duration ?? '0', 10) / 1000, 1053a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 1054a84a85c5S猫头猫 artwork: '', 1055a84a85c5S猫头猫 [internalSerializeKey]: { 1056a84a85c5S猫头猫 localPath: urlLike, 1057a84a85c5S猫头猫 }, 1058a84a85c5S猫头猫 }; 1059a84a85c5S猫头猫 }, 106011908939S猫头猫 async getMediaSource(musicItem, quality) { 106111908939S猫头猫 if (quality === 'standard') { 106211908939S猫头猫 return { 106311908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 106411908939S猫头猫 }; 106511908939S猫头猫 } 106611908939S猫头猫 return null; 106711908939S猫头猫 }, 106874d0cf81S猫头猫 }; 106974d0cf81S猫头猫}, ''); 10707993f90eS猫头猫localFilePlugin.hash = localPluginHash; 1071927dbe93S猫头猫 1072d5bfeb7eS猫头猫//#endregion 1073d5bfeb7eS猫头猫 1074927dbe93S猫头猫async function setup() { 1075927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 1076927dbe93S猫头猫 try { 1077927dbe93S猫头猫 // 加载插件 1078927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 1079927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 1080927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 10811e263108S猫头猫 trace('初始化插件', _pluginUrl); 10821e263108S猫头猫 if ( 10831e263108S猫头猫 _pluginUrl.isFile() && 10841e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 10851e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 10861e263108S猫头猫 ) { 1087927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 1088927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 10894060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 10904060c00aS猫头猫 p => p.hash === plugin.hash, 10914060c00aS猫头猫 ); 1092927dbe93S猫头猫 if (_pluginIndex !== -1) { 1093927dbe93S猫头猫 // 重复插件,直接忽略 10940c266394S猫头猫 continue; 1095927dbe93S猫头猫 } 1096927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 1097927dbe93S猫头猫 } 1098927dbe93S猫头猫 } 1099927dbe93S猫头猫 1100927dbe93S猫头猫 plugins = _plugins; 1101e08d37a3S猫头猫 /** 初始化meta信息 */ 1102c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 1103c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 1104c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 1105c2b3a262S猫头猫 for (let plugin of plugins) { 1106c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1107c2b3a262S猫头猫 plugin.state = 'disabled'; 1108c2b3a262S猫头猫 } 1109c2b3a262S猫头猫 } 1110c2b3a262S猫头猫 pluginStateMapper.notify(); 1111927dbe93S猫头猫 } catch (e: any) { 11124060c00aS猫头猫 ToastAndroid.show( 11134060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 11144060c00aS猫头猫 ToastAndroid.LONG, 11154060c00aS猫头猫 ); 11161a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1117927dbe93S猫头猫 throw e; 1118927dbe93S猫头猫 } 1119927dbe93S猫头猫} 1120927dbe93S猫头猫 1121e36e2599S猫头猫interface IInstallPluginConfig { 1122e36e2599S猫头猫 notCheckVersion?: boolean; 1123e36e2599S猫头猫} 1124e36e2599S猫头猫 1125*34bfbe58Smaotoumaoexport interface IInstallPluginResult { 1126*34bfbe58Smaotoumao success: boolean; 1127*34bfbe58Smaotoumao message?: string; 1128*34bfbe58Smaotoumao pluginName?: string; 1129*34bfbe58Smaotoumao pluginHash?: string; 1130*34bfbe58Smaotoumao pluginUrl?: string; 1131*34bfbe58Smaotoumao} 1132*34bfbe58Smaotoumao 113350245d53Smaotoumao// 从本地文件安装插件 113450245d53Smaotoumaoasync function installPluginFromLocalFile( 1135e36e2599S猫头猫 pluginPath: string, 113650245d53Smaotoumao config?: IInstallPluginConfig & { 113750245d53Smaotoumao useExpoFs?: boolean 113850245d53Smaotoumao }, 1139*34bfbe58Smaotoumao): Promise<IInstallPluginResult> { 114050245d53Smaotoumao let funcCode: string; 114150245d53Smaotoumao if (config?.useExpoFs) { 114250245d53Smaotoumao funcCode = await readAsStringAsync(pluginPath); 114350245d53Smaotoumao } else { 114450245d53Smaotoumao funcCode = await readFile(pluginPath, 'utf8'); 114550245d53Smaotoumao } 1146e36e2599S猫头猫 1147e36e2599S猫头猫 if (funcCode) { 1148927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1149927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1150927dbe93S猫头猫 if (_pluginIndex !== -1) { 1151e36e2599S猫头猫 // 静默忽略 1152*34bfbe58Smaotoumao return { 1153*34bfbe58Smaotoumao success: true, 1154*34bfbe58Smaotoumao message: '插件已安装', 1155*34bfbe58Smaotoumao pluginName: plugin.name, 1156*34bfbe58Smaotoumao pluginHash: plugin.hash, 1157*34bfbe58Smaotoumao }; 1158927dbe93S猫头猫 } 1159e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1160e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1161e36e2599S猫头猫 if ( 1162e36e2599S猫头猫 compare( 1163e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1164e36e2599S猫头猫 plugin.instance.version ?? '', 1165e36e2599S猫头猫 '>', 1166e36e2599S猫头猫 ) 1167e36e2599S猫头猫 ) { 1168*34bfbe58Smaotoumao return { 1169*34bfbe58Smaotoumao success: false, 1170*34bfbe58Smaotoumao message: '已安装更新版本的插件', 1171*34bfbe58Smaotoumao pluginName: plugin.name, 1172*34bfbe58Smaotoumao pluginHash: plugin.hash, 1173*34bfbe58Smaotoumao }; 1174e36e2599S猫头猫 } 1175e36e2599S猫头猫 } 1176e36e2599S猫头猫 1177927dbe93S猫头猫 if (plugin.hash !== '') { 1178927dbe93S猫头猫 const fn = nanoid(); 1179e36e2599S猫头猫 if (oldVersionPlugin) { 1180e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1181e36e2599S猫头猫 try { 1182e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1183e36e2599S猫头猫 } catch {} 1184e36e2599S猫头猫 } 1185927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1186927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1187927dbe93S猫头猫 plugin.path = _pluginPath; 1188927dbe93S猫头猫 plugins = plugins.concat(plugin); 1189927dbe93S猫头猫 pluginStateMapper.notify(); 1190*34bfbe58Smaotoumao return { 1191*34bfbe58Smaotoumao success: true, 1192*34bfbe58Smaotoumao pluginName: plugin.name, 1193*34bfbe58Smaotoumao pluginHash: plugin.hash, 1194*34bfbe58Smaotoumao }; 1195927dbe93S猫头猫 } 1196*34bfbe58Smaotoumao return { 1197*34bfbe58Smaotoumao success: false, 1198*34bfbe58Smaotoumao message: '插件无法解析', 1199927dbe93S猫头猫 } 1200*34bfbe58Smaotoumao } 1201*34bfbe58Smaotoumao return { 1202*34bfbe58Smaotoumao success: false, 1203*34bfbe58Smaotoumao message: '插件无法识别', 1204*34bfbe58Smaotoumao }; 1205c2b3a262S猫头猫} 1206c2b3a262S猫头猫 1207ee781061S猫头猫 1208c2b3a262S猫头猫async function installPluginFromUrl( 1209c2b3a262S猫头猫 url: string, 1210c2b3a262S猫头猫 config?: IInstallPluginConfig, 1211*34bfbe58Smaotoumao) : Promise<IInstallPluginResult> { 121258992c6bS猫头猫 try { 1213ee781061S猫头猫 const funcCode = ( 1214ee781061S猫头猫 await axios.get(url, { 1215*34bfbe58Smaotoumao headers: { 1216*34bfbe58Smaotoumao 'Cache-Control': 'no-cache', 1217*34bfbe58Smaotoumao Pragma: 'no-cache', 1218*34bfbe58Smaotoumao Expires: '0', 1219*34bfbe58Smaotoumao }, 1220ee781061S猫头猫 }) 1221ee781061S猫头猫 ).data; 122258992c6bS猫头猫 if (funcCode) { 122358992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 122458992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 122558992c6bS猫头猫 if (_pluginIndex !== -1) { 12268b7ddca8S猫头猫 // 静默忽略 1227*34bfbe58Smaotoumao return { 1228*34bfbe58Smaotoumao success: true, 1229*34bfbe58Smaotoumao message: '插件已安装', 1230*34bfbe58Smaotoumao pluginName: plugin.name, 1231*34bfbe58Smaotoumao pluginHash: plugin.hash, 1232*34bfbe58Smaotoumao pluginUrl: url, 1233*34bfbe58Smaotoumao }; 123458992c6bS猫头猫 } 123525c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1236c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 123725c1bd29S猫头猫 if ( 123825c1bd29S猫头猫 compare( 123925c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 124025c1bd29S猫头猫 plugin.instance.version ?? '', 124125c1bd29S猫头猫 '>', 124225c1bd29S猫头猫 ) 124325c1bd29S猫头猫 ) { 1244*34bfbe58Smaotoumao return { 1245*34bfbe58Smaotoumao success: false, 1246*34bfbe58Smaotoumao message: '已安装更新版本的插件', 1247*34bfbe58Smaotoumao pluginName: plugin.name, 1248*34bfbe58Smaotoumao pluginHash: plugin.hash, 1249*34bfbe58Smaotoumao pluginUrl: url, 1250*34bfbe58Smaotoumao }; 125125c1bd29S猫头猫 } 125225c1bd29S猫头猫 } 125325c1bd29S猫头猫 125458992c6bS猫头猫 if (plugin.hash !== '') { 125558992c6bS猫头猫 const fn = nanoid(); 125658992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 125758992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 125858992c6bS猫头猫 plugin.path = _pluginPath; 125958992c6bS猫头猫 plugins = plugins.concat(plugin); 126025c1bd29S猫头猫 if (oldVersionPlugin) { 126125c1bd29S猫头猫 plugins = plugins.filter( 126225c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 126325c1bd29S猫头猫 ); 126425c1bd29S猫头猫 try { 126525c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 126625c1bd29S猫头猫 } catch {} 126725c1bd29S猫头猫 } 126858992c6bS猫头猫 pluginStateMapper.notify(); 1269*34bfbe58Smaotoumao return { 1270*34bfbe58Smaotoumao success: true, 1271*34bfbe58Smaotoumao pluginName: plugin.name, 1272*34bfbe58Smaotoumao pluginHash: plugin.hash, 1273*34bfbe58Smaotoumao pluginUrl: url, 127458992c6bS猫头猫 } 1275*34bfbe58Smaotoumao } 1276*34bfbe58Smaotoumao return { 1277*34bfbe58Smaotoumao success: false, 1278*34bfbe58Smaotoumao message: '插件无法解析', 1279*34bfbe58Smaotoumao pluginUrl: url, 1280*34bfbe58Smaotoumao } 1281*34bfbe58Smaotoumao } else { 1282*34bfbe58Smaotoumao return { 1283*34bfbe58Smaotoumao success: false, 1284*34bfbe58Smaotoumao message: '插件无法识别', 1285*34bfbe58Smaotoumao pluginUrl: url, 1286*34bfbe58Smaotoumao } 128758992c6bS猫头猫 } 128825c1bd29S猫头猫 } catch (e: any) { 1289ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 129058992c6bS猫头猫 errorLog('URL安装插件失败', e); 1291*34bfbe58Smaotoumao 1292*34bfbe58Smaotoumao if (e?.response?.statusCode === 404) { 1293*34bfbe58Smaotoumao return { 1294*34bfbe58Smaotoumao success: false, 1295*34bfbe58Smaotoumao message: '插件不存在,请联系插件作者', 1296*34bfbe58Smaotoumao pluginUrl: url, 1297*34bfbe58Smaotoumao } 1298*34bfbe58Smaotoumao } else { 1299*34bfbe58Smaotoumao return { 1300*34bfbe58Smaotoumao success: false, 1301*34bfbe58Smaotoumao message: e?.message ?? '', 1302*34bfbe58Smaotoumao pluginUrl: url, 1303*34bfbe58Smaotoumao } 1304*34bfbe58Smaotoumao } 130558992c6bS猫头猫 } 130658992c6bS猫头猫} 130758992c6bS猫头猫 1308927dbe93S猫头猫/** 卸载插件 */ 1309927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1310927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1311927dbe93S猫头猫 if (targetIndex !== -1) { 1312927dbe93S猫头猫 try { 131324e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1314927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1315927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1316927dbe93S猫头猫 pluginStateMapper.notify(); 131743eb30bfS猫头猫 // 防止其他重名 131824e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 13195589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 132024e5e74aS猫头猫 } 1321927dbe93S猫头猫 } catch {} 1322927dbe93S猫头猫 } 1323927dbe93S猫头猫} 1324927dbe93S猫头猫 132508882a77S猫头猫async function uninstallAllPlugins() { 132608882a77S猫头猫 await Promise.all( 132708882a77S猫头猫 plugins.map(async plugin => { 132808882a77S猫头猫 try { 132908882a77S猫头猫 const pluginName = plugin.name; 133008882a77S猫头猫 await unlink(plugin.path); 13315589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 133208882a77S猫头猫 } catch (e) {} 133308882a77S猫头猫 }), 133408882a77S猫头猫 ); 133508882a77S猫头猫 plugins = []; 133608882a77S猫头猫 pluginStateMapper.notify(); 1337e08d37a3S猫头猫 1338e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1339e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1340e08d37a3S猫头猫 .then(fns => { 1341e08d37a3S猫头猫 fns.forEach(fn => { 1342e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1343e08d37a3S猫头猫 }); 1344e08d37a3S猫头猫 }) 1345e08d37a3S猫头猫 .catch(emptyFunction); 134608882a77S猫头猫} 134708882a77S猫头猫 134825c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 134925c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 135025c1bd29S猫头猫 if (!updateUrl) { 135125c1bd29S猫头猫 throw new Error('没有更新源'); 135225c1bd29S猫头猫 } 135325c1bd29S猫头猫 try { 135425c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 135525c1bd29S猫头猫 } catch (e: any) { 135625c1bd29S猫头猫 if (e.message === '插件已安装') { 135725c1bd29S猫头猫 throw new Error('当前已是最新版本'); 135825c1bd29S猫头猫 } else { 135925c1bd29S猫头猫 throw e; 136025c1bd29S猫头猫 } 136125c1bd29S猫头猫 } 136225c1bd29S猫头猫} 136325c1bd29S猫头猫 1364927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 13652c595535S猫头猫 return getByName(mediaItem?.platform); 1366927dbe93S猫头猫} 1367927dbe93S猫头猫 1368927dbe93S猫头猫function getByHash(hash: string) { 13697993f90eS猫头猫 return hash === localPluginHash 13707993f90eS猫头猫 ? localFilePlugin 13717993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1372927dbe93S猫头猫} 1373927dbe93S猫头猫 1374927dbe93S猫头猫function getByName(name: string) { 13757993f90eS猫头猫 return name === localPluginPlatform 13760e4173cdS猫头猫 ? localFilePlugin 13770e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1378927dbe93S猫头猫} 1379927dbe93S猫头猫 1380927dbe93S猫头猫function getValidPlugins() { 1381927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1382927dbe93S猫头猫} 1383927dbe93S猫头猫 13842b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 13852b80a429S猫头猫 return plugins.filter( 13862b80a429S猫头猫 _ => 13872b80a429S猫头猫 _.state === 'enabled' && 13882b80a429S猫头猫 _.instance.search && 138939ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 139039ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 13912b80a429S猫头猫 : true), 13922b80a429S猫头猫 ); 1393efb9da24S猫头猫} 1394efb9da24S猫头猫 13952b80a429S猫头猫function getSortedSearchablePlugins( 13962b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 13972b80a429S猫头猫) { 13982b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1399e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1400e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1401e08d37a3S猫头猫 0 1402e08d37a3S猫头猫 ? -1 1403e08d37a3S猫头猫 : 1, 1404e08d37a3S猫头猫 ); 1405e08d37a3S猫头猫} 1406e08d37a3S猫头猫 140715feccc1S猫头猫function getTopListsablePlugins() { 140815feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 140915feccc1S猫头猫} 141015feccc1S猫头猫 141115feccc1S猫头猫function getSortedTopListsablePlugins() { 141215feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 141315feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 141415feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 141515feccc1S猫头猫 0 141615feccc1S猫头猫 ? -1 141715feccc1S猫头猫 : 1, 141815feccc1S猫头猫 ); 141915feccc1S猫头猫} 142015feccc1S猫头猫 1421ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1422ceb900cdS猫头猫 return plugins.filter( 1423ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1424ceb900cdS猫头猫 ); 1425ceb900cdS猫头猫} 1426ceb900cdS猫头猫 1427ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1428ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1429ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1430ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1431ceb900cdS猫头猫 0 1432ceb900cdS猫头猫 ? -1 1433ceb900cdS猫头猫 : 1, 1434ceb900cdS猫头猫 ); 1435ceb900cdS猫头猫} 1436ceb900cdS猫头猫 1437e08d37a3S猫头猫function useSortedPlugins() { 1438e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1439e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1440e08d37a3S猫头猫 144134588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 144234588741S猫头猫 [..._plugins].sort((a, b) => 1443e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1444e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1445e08d37a3S猫头猫 0 1446e08d37a3S猫头猫 ? -1 1447e08d37a3S猫头猫 : 1, 144834588741S猫头猫 ), 1449e08d37a3S猫头猫 ); 145034588741S猫头猫 145134588741S猫头猫 useEffect(() => { 1452d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 145334588741S猫头猫 setSortedPlugins( 145434588741S猫头猫 [..._plugins].sort((a, b) => 145534588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 145634588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 145734588741S猫头猫 0 145834588741S猫头猫 ? -1 145934588741S猫头猫 : 1, 146034588741S猫头猫 ), 146134588741S猫头猫 ); 1462d4cd40d8S猫头猫 }); 146334588741S猫头猫 }, [_plugins, _pluginMetaAll]); 146434588741S猫头猫 146534588741S猫头猫 return sortedPlugins; 1466e08d37a3S猫头猫} 1467e08d37a3S猫头猫 1468c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1469c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1470c2b3a262S猫头猫 if (target) { 1471c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1472c2b3a262S猫头猫 plugins = [...plugins]; 1473c2b3a262S猫头猫 pluginStateMapper.notify(); 1474c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1475c2b3a262S猫头猫 } 1476c2b3a262S猫头猫} 1477c2b3a262S猫头猫 1478927dbe93S猫头猫const PluginManager = { 1479927dbe93S猫头猫 setup, 148050245d53Smaotoumao installPluginFromLocalFile, 148158992c6bS猫头猫 installPluginFromUrl, 148225c1bd29S猫头猫 updatePlugin, 1483927dbe93S猫头猫 uninstallPlugin, 1484927dbe93S猫头猫 getByMedia, 1485927dbe93S猫头猫 getByHash, 1486927dbe93S猫头猫 getByName, 1487927dbe93S猫头猫 getValidPlugins, 1488efb9da24S猫头猫 getSearchablePlugins, 1489e08d37a3S猫头猫 getSortedSearchablePlugins, 149015feccc1S猫头猫 getTopListsablePlugins, 1491ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 149215feccc1S猫头猫 getSortedTopListsablePlugins, 14935276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1494e08d37a3S猫头猫 useSortedPlugins, 149508882a77S猫头猫 uninstallAllPlugins, 1496c2b3a262S猫头猫 setPluginEnabled, 14975276aef9S猫头猫}; 1498927dbe93S猫头猫 1499927dbe93S猫头猫export default PluginManager; 1500