126f32636S猫头猫import RNFS, { 2927dbe93S猫头猫 copyFile, 3927dbe93S猫头猫 exists, 4927dbe93S猫头猫 readDir, 5927dbe93S猫头猫 readFile, 6927dbe93S猫头猫 unlink, 7927dbe93S猫头猫 writeFile, 8927dbe93S猫头猫} from 'react-native-fs'; 9927dbe93S猫头猫import CryptoJs from 'crypto-js'; 10927dbe93S猫头猫import dayjs from 'dayjs'; 11927dbe93S猫头猫import axios from 'axios'; 12ef60be1cS猫头猫import bigInt from 'big-integer'; 13ef60be1cS猫头猫import qs from 'qs'; 1428ccceb7S猫头猫import * as webdav from 'webdav'; 15d4cd40d8S猫头猫import {InteractionManager, ToastAndroid} from 'react-native'; 16927dbe93S猫头猫import pathConst from '@/constants/pathConst'; 1725c1bd29S猫头猫import {compare, satisfies} from 'compare-versions'; 18927dbe93S猫头猫import DeviceInfo from 'react-native-device-info'; 19927dbe93S猫头猫import StateMapper from '@/utils/stateMapper'; 205589cdf3S猫头猫import MediaExtra from './mediaExtra'; 21927dbe93S猫头猫import {nanoid} from 'nanoid'; 22ea6d708fS猫头猫import {devLog, errorLog, trace} from '../utils/log'; 23cfa0fc07S猫头猫import { 240e4173cdS猫头猫 getInternalData, 250e4173cdS猫头猫 InternalDataType, 260e4173cdS猫头猫 isSameMediaItem, 270e4173cdS猫头猫 resetMediaItem, 280e4173cdS猫头猫} from '@/utils/mediaItem'; 297993f90eS猫头猫import { 307993f90eS猫头猫 CacheControl, 31e08d37a3S猫头猫 emptyFunction, 327993f90eS猫头猫 internalSerializeKey, 337993f90eS猫头猫 localPluginHash, 347993f90eS猫头猫 localPluginPlatform, 357993f90eS猫头猫} from '@/constants/commonConst'; 36927dbe93S猫头猫import delay from '@/utils/delay'; 374d9d3c4cS猫头猫import * as cheerio from 'cheerio'; 387d7e864fS猫头猫import CookieManager from '@react-native-cookies/cookies'; 397d7e864fS猫头猫import he from 'he'; 40ef714860S猫头猫import Network from './network'; 410e4173cdS猫头猫import LocalMusicSheet from './localMusicSheet'; 425353b473S猫头猫import {getInfoAsync} from 'expo-file-system'; 4374d0cf81S猫头猫import Mp3Util from '@/native/mp3Util'; 44e08d37a3S猫头猫import {PluginMeta} from './pluginMeta'; 4534588741S猫头猫import {useEffect, useState} from 'react'; 4611908939S猫头猫import {addFileScheme, getFileName} from '@/utils/fileUtils'; 47a7b42a4cS猫头猫import {URL} from 'react-native-url-polyfill'; 48a7b42a4cS猫头猫import Base64 from '@/utils/base64'; 4943eb30bfS猫头猫import MediaCache from './mediaCache'; 505589cdf3S猫头猫import {produce} from 'immer'; 5143eb30bfS猫头猫import objectPath from 'object-path'; 52927dbe93S猫头猫 5361aca335S猫头猫axios.defaults.timeout = 2000; 54927dbe93S猫头猫 55927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 56927dbe93S猫头猫 57cfa0fc07S猫头猫export enum PluginStateCode { 58927dbe93S猫头猫 /** 版本不匹配 */ 59927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 60927dbe93S猫头猫 /** 无法解析 */ 61927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 62927dbe93S猫头猫} 63927dbe93S猫头猫 649c34d637S猫头猫const packages: Record<string, any> = { 659c34d637S猫头猫 cheerio, 669c34d637S猫头猫 'crypto-js': CryptoJs, 679c34d637S猫头猫 axios, 689c34d637S猫头猫 dayjs, 699c34d637S猫头猫 'big-integer': bigInt, 709c34d637S猫头猫 qs, 719c34d637S猫头猫 he, 723b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 7328ccceb7S猫头猫 webdav, 749c34d637S猫头猫}; 759c34d637S猫头猫 76b43683eaS猫头猫const _require = (packageName: string) => { 77b43683eaS猫头猫 let pkg = packages[packageName]; 78b43683eaS猫头猫 pkg.default = pkg; 79b43683eaS猫头猫 return pkg; 80b43683eaS猫头猫}; 819c34d637S猫头猫 8253f8cd8eS猫头猫const _consoleBind = function ( 8353f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 8453f8cd8eS猫头猫 ...args: any 8553f8cd8eS猫头猫) { 8653f8cd8eS猫头猫 const fn = console[method]; 8753f8cd8eS猫头猫 if (fn) { 8853f8cd8eS猫头猫 fn(...args); 8953f8cd8eS猫头猫 devLog(method, ...args); 9053f8cd8eS猫头猫 } 9153f8cd8eS猫头猫}; 9253f8cd8eS猫头猫 9353f8cd8eS猫头猫const _console = { 9453f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9553f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9653f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9753f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9853f8cd8eS猫头猫}; 9953f8cd8eS猫头猫 100a7b42a4cS猫头猫function formatAuthUrl(url: string) { 101a7b42a4cS猫头猫 const urlObj = new URL(url); 102a7b42a4cS猫头猫 103a7b42a4cS猫头猫 try { 104a7b42a4cS猫头猫 if (urlObj.username && urlObj.password) { 105a7b42a4cS猫头猫 const auth = `Basic ${Base64.btoa( 106a7b42a4cS猫头猫 `${decodeURIComponent(urlObj.username)}:${decodeURIComponent( 107a7b42a4cS猫头猫 urlObj.password, 108a7b42a4cS猫头猫 )}`, 109a7b42a4cS猫头猫 )}`; 110a7b42a4cS猫头猫 urlObj.username = ''; 111a7b42a4cS猫头猫 urlObj.password = ''; 112a7b42a4cS猫头猫 113a7b42a4cS猫头猫 return { 114a7b42a4cS猫头猫 url: urlObj.toString(), 115a7b42a4cS猫头猫 auth, 116a7b42a4cS猫头猫 }; 117a7b42a4cS猫头猫 } 118a7b42a4cS猫头猫 } catch (e) { 119a7b42a4cS猫头猫 return { 120a7b42a4cS猫头猫 url, 121a7b42a4cS猫头猫 }; 122a7b42a4cS猫头猫 } 123a7b42a4cS猫头猫 return { 124a7b42a4cS猫头猫 url, 125a7b42a4cS猫头猫 }; 126a7b42a4cS猫头猫} 127a7b42a4cS猫头猫 128d5bfeb7eS猫头猫//#region 插件类 129927dbe93S猫头猫export class Plugin { 130927dbe93S猫头猫 /** 插件名 */ 131927dbe93S猫头猫 public name: string; 132927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 133927dbe93S猫头猫 public hash: string; 134927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 135927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 136927dbe93S猫头猫 /** 插件状态信息 */ 137927dbe93S猫头猫 public stateCode?: PluginStateCode; 138927dbe93S猫头猫 /** 插件的实例 */ 139927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 140927dbe93S猫头猫 /** 插件路径 */ 141927dbe93S猫头猫 public path: string; 142927dbe93S猫头猫 /** 插件方法 */ 143927dbe93S猫头猫 public methods: PluginMethods; 144927dbe93S猫头猫 14574d0cf81S猫头猫 constructor( 14674d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 14774d0cf81S猫头猫 pluginPath: string, 14874d0cf81S猫头猫 ) { 149927dbe93S猫头猫 this.state = 'enabled'; 150927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1513b3d6357S猫头猫 const _module: any = {exports: {}}; 152927dbe93S猫头猫 try { 15374d0cf81S猫头猫 if (typeof funcCode === 'string') { 154e0caf342S猫头猫 // 插件的环境变量 155e0caf342S猫头猫 const env = { 156e0caf342S猫头猫 getUserVariables: () => { 157e0caf342S猫头猫 return ( 158e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 159e0caf342S猫头猫 ); 160e0caf342S猫头猫 }, 161e3fa9b3cS猫头猫 os: 'android', 162e0caf342S猫头猫 }; 163e0caf342S猫头猫 1644060c00aS猫头猫 // eslint-disable-next-line no-new-func 165927dbe93S猫头猫 _instance = Function(` 166927dbe93S猫头猫 'use strict'; 16791eb8fa8S猫头猫 return function(require, __musicfree_require, module, exports, console, env, URL) { 1689c34d637S猫头猫 ${funcCode} 169927dbe93S猫头猫 } 170e0caf342S猫头猫 `)()( 171e0caf342S猫头猫 _require, 172e0caf342S猫头猫 _require, 173e0caf342S猫头猫 _module, 174e0caf342S猫头猫 _module.exports, 175e0caf342S猫头猫 _console, 176e0caf342S猫头猫 env, 17791eb8fa8S猫头猫 URL, 178e0caf342S猫头猫 ); 1793b3d6357S猫头猫 if (_module.exports.default) { 1803b3d6357S猫头猫 _instance = _module.exports 1813b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1823b3d6357S猫头猫 } else { 1839c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1843b3d6357S猫头猫 } 18574d0cf81S猫头猫 } else { 18674d0cf81S猫头猫 _instance = funcCode(); 18774d0cf81S猫头猫 } 188c2b3a262S猫头猫 // 插件初始化后的一些操作 18995297592S猫头猫 if (Array.isArray(_instance.userVariables)) { 19095297592S猫头猫 _instance.userVariables = _instance.userVariables.filter( 19195297592S猫头猫 it => it?.key, 19295297592S猫头猫 ); 19395297592S猫头猫 } 194927dbe93S猫头猫 this.checkValid(_instance); 195927dbe93S猫头猫 } catch (e: any) { 196b43683eaS猫头猫 console.log(e); 197927dbe93S猫头猫 this.state = 'error'; 198927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 199927dbe93S猫头猫 if (e?.stateCode) { 200927dbe93S猫头猫 this.stateCode = e.stateCode; 201927dbe93S猫头猫 } 202927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 203927dbe93S猫头猫 stateCode: this.stateCode, 204927dbe93S猫头猫 message: e?.message, 205927dbe93S猫头猫 stack: e?.stack, 206927dbe93S猫头猫 }); 207927dbe93S猫头猫 _instance = e?.instance ?? { 208927dbe93S猫头猫 _path: '', 209927dbe93S猫头猫 platform: '', 210927dbe93S猫头猫 appVersion: '', 21120e6a092S猫头猫 async getMediaSource() { 212927dbe93S猫头猫 return null; 213927dbe93S猫头猫 }, 214927dbe93S猫头猫 async search() { 215927dbe93S猫头猫 return {}; 216927dbe93S猫头猫 }, 217927dbe93S猫头猫 async getAlbumInfo() { 218927dbe93S猫头猫 return null; 219927dbe93S猫头猫 }, 220927dbe93S猫头猫 }; 221927dbe93S猫头猫 } 222927dbe93S猫头猫 this.instance = _instance; 223927dbe93S猫头猫 this.path = pluginPath; 224927dbe93S猫头猫 this.name = _instance.platform; 225ab8941d9S猫头猫 if ( 226ab8941d9S猫头猫 this.instance.platform === '' || 227ab8941d9S猫头猫 this.instance.platform === undefined 228ab8941d9S猫头猫 ) { 229927dbe93S猫头猫 this.hash = ''; 230927dbe93S猫头猫 } else { 23174d0cf81S猫头猫 if (typeof funcCode === 'string') { 232927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 23374d0cf81S猫头猫 } else { 23474d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 23574d0cf81S猫头猫 } 236927dbe93S猫头猫 } 237927dbe93S猫头猫 238927dbe93S猫头猫 // 放在最后 239927dbe93S猫头猫 this.methods = new PluginMethods(this); 240927dbe93S猫头猫 } 241927dbe93S猫头猫 242927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 243927dbe93S猫头猫 /** 版本号校验 */ 244927dbe93S猫头猫 if ( 245927dbe93S猫头猫 _instance.appVersion && 246927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 247927dbe93S猫头猫 ) { 248927dbe93S猫头猫 throw { 249927dbe93S猫头猫 instance: _instance, 250927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 251927dbe93S猫头猫 }; 252927dbe93S猫头猫 } 253927dbe93S猫头猫 return true; 254927dbe93S猫头猫 } 255927dbe93S猫头猫} 256d5bfeb7eS猫头猫//#endregion 257927dbe93S猫头猫 258d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 259927dbe93S猫头猫/** 有缓存等信息 */ 260927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 261927dbe93S猫头猫 private plugin; 262927dbe93S猫头猫 constructor(plugin: Plugin) { 263927dbe93S猫头猫 this.plugin = plugin; 264927dbe93S猫头猫 } 265927dbe93S猫头猫 /** 搜索 */ 266927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 267927dbe93S猫头猫 query: string, 268927dbe93S猫头猫 page: number, 269927dbe93S猫头猫 type: T, 270927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 271927dbe93S猫头猫 if (!this.plugin.instance.search) { 272927dbe93S猫头猫 return { 273927dbe93S猫头猫 isEnd: true, 274927dbe93S猫头猫 data: [], 275927dbe93S猫头猫 }; 276927dbe93S猫头猫 } 277927dbe93S猫头猫 2784060c00aS猫头猫 const result = 2794060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 280927dbe93S猫头猫 if (Array.isArray(result.data)) { 281927dbe93S猫头猫 result.data.forEach(_ => { 282927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 283927dbe93S猫头猫 }); 284927dbe93S猫头猫 return { 285927dbe93S猫头猫 isEnd: result.isEnd ?? true, 286927dbe93S猫头猫 data: result.data, 287927dbe93S猫头猫 }; 288927dbe93S猫头猫 } 289927dbe93S猫头猫 return { 290927dbe93S猫头猫 isEnd: true, 291927dbe93S猫头猫 data: [], 292927dbe93S猫头猫 }; 293927dbe93S猫头猫 } 294927dbe93S猫头猫 295927dbe93S猫头猫 /** 获取真实源 */ 29620e6a092S猫头猫 async getMediaSource( 297927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 298abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 299927dbe93S猫头猫 retryCount = 1, 300dc160d50S猫头猫 notUpdateCache = false, 301192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 302927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 30343eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 304927dbe93S猫头猫 const localPath = 30543eb30bfS猫头猫 mediaExtra?.localPath || 30643eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3070e4173cdS猫头猫 getInternalData<string>( 3080e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3090e4173cdS猫头猫 InternalDataType.LOCALPATH, 3100e4173cdS猫头猫 ); 3115353b473S猫头猫 if (localPath && (await getInfoAsync(localPath)).exists) { 3120e4173cdS猫头猫 trace('本地播放', localPath); 31343eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 31443eb30bfS猫头猫 // 修正一下本地数据 31543eb30bfS猫头猫 MediaExtra.update(musicItem, { 31643eb30bfS猫头猫 localPath, 31743eb30bfS猫头猫 }); 31843eb30bfS猫头猫 } 319927dbe93S猫头猫 return { 32011908939S猫头猫 url: addFileScheme(localPath), 321927dbe93S猫头猫 }; 32243eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 32343eb30bfS猫头猫 MediaExtra.update(musicItem, { 32443eb30bfS猫头猫 localPath: undefined, 32543eb30bfS猫头猫 }); 326927dbe93S猫头猫 } 327a84a85c5S猫头猫 3287993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 329f5935920S猫头猫 throw new Error('本地音乐不存在'); 330f5935920S猫头猫 } 331927dbe93S猫头猫 // 2. 缓存播放 33243eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 33343eb30bfS猫头猫 musicItem, 33443eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 335985f8e75S猫头猫 const pluginCacheControl = 336985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 337cfa0fc07S猫头猫 if ( 338cfa0fc07S猫头猫 mediaCache && 33943eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34048f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34148f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 342ef714860S猫头猫 Network.isOffline())) 343cfa0fc07S猫头猫 ) { 3445276aef9S猫头猫 trace('播放', '缓存播放'); 34543eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 346927dbe93S猫头猫 return { 34743eb30bfS猫头猫 url: qualityInfo!.url, 348927dbe93S猫头猫 headers: mediaCache.headers, 3494060c00aS猫头猫 userAgent: 3504060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 351927dbe93S猫头猫 }; 352927dbe93S猫头猫 } 353927dbe93S猫头猫 // 3. 插件解析 35420e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 355a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 356a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 357a7b42a4cS猫头猫 ); 358a7b42a4cS猫头猫 return { 359a7b42a4cS猫头猫 url: url, 360a7b42a4cS猫头猫 headers: auth 361a7b42a4cS猫头猫 ? { 362a7b42a4cS猫头猫 Authorization: auth, 363a7b42a4cS猫头猫 } 364a7b42a4cS猫头猫 : undefined, 365a7b42a4cS猫头猫 }; 366927dbe93S猫头猫 } 367927dbe93S猫头猫 try { 368abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 369abaede57S猫头猫 musicItem, 370abaede57S猫头猫 quality, 371abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 372927dbe93S猫头猫 if (!url) { 373a28eac61S猫头猫 throw new Error('NOT RETRY'); 374927dbe93S猫头猫 } 3755276aef9S猫头猫 trace('播放', '插件播放'); 376927dbe93S猫头猫 const result = { 377927dbe93S猫头猫 url, 378927dbe93S猫头猫 headers, 379927dbe93S猫头猫 userAgent: headers?.['user-agent'], 380cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 381a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 382a7b42a4cS猫头猫 if (authFormattedResult.auth) { 383a7b42a4cS猫头猫 result.url = authFormattedResult.url; 384a7b42a4cS猫头猫 result.headers = { 385a7b42a4cS猫头猫 ...(result.headers ?? {}), 386a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 387a7b42a4cS猫头猫 }; 388a7b42a4cS猫头猫 } 389927dbe93S猫头猫 390dc160d50S猫头猫 if ( 391dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 392dc160d50S猫头猫 !notUpdateCache 393dc160d50S猫头猫 ) { 39443eb30bfS猫头猫 // 更新缓存 39543eb30bfS猫头猫 const cacheSource = { 39643eb30bfS猫头猫 headers: result.headers, 39743eb30bfS猫头猫 userAgent: result.userAgent, 39843eb30bfS猫头猫 url, 39943eb30bfS猫头猫 }; 40043eb30bfS猫头猫 let realMusicItem = { 40143eb30bfS猫头猫 ...musicItem, 40243eb30bfS猫头猫 ...(mediaCache || {}), 40343eb30bfS猫头猫 }; 40443eb30bfS猫头猫 realMusicItem.source = { 40543eb30bfS猫头猫 ...(realMusicItem.source || {}), 40643eb30bfS猫头猫 [quality]: cacheSource, 40743eb30bfS猫头猫 }; 40843eb30bfS猫头猫 40943eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 410752ffc5aS猫头猫 } 411927dbe93S猫头猫 return result; 412927dbe93S猫头猫 } catch (e: any) { 413a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 414927dbe93S猫头猫 await delay(150); 415abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 416927dbe93S猫头猫 } 417927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 418ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 419192ae2b0S猫头猫 return null; 420927dbe93S猫头猫 } 421927dbe93S猫头猫 } 422927dbe93S猫头猫 423927dbe93S猫头猫 /** 获取音乐详情 */ 424927dbe93S猫头猫 async getMusicInfo( 425927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 42674d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 427927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 428d704daedS猫头猫 return null; 429927dbe93S猫头猫 } 43074d0cf81S猫头猫 try { 431927dbe93S猫头猫 return ( 432927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4337993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 434d704daedS猫头猫 ) ?? null 435927dbe93S猫头猫 ); 436ea6d708fS猫头猫 } catch (e: any) { 437ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 438d704daedS猫头猫 return null; 43974d0cf81S猫头猫 } 440927dbe93S猫头猫 } 441927dbe93S猫头猫 4427e883dbbS猫头猫 /** 4437e883dbbS猫头猫 * 4447e883dbbS猫头猫 * getLyric(musicItem) => { 4457e883dbbS猫头猫 * lyric: string; 4467e883dbbS猫头猫 * trans: string; 4477e883dbbS猫头猫 * } 4487e883dbbS猫头猫 * 4497e883dbbS猫头猫 */ 450927dbe93S猫头猫 /** 获取歌词 */ 451927dbe93S猫头猫 async getLyric( 45243eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 453927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 45443eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 4555589cdf3S猫头猫 const meta = MediaExtra.get(originalMusicItem); 45643eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 457927dbe93S猫头猫 if (meta && meta.associatedLrc) { 45843eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 45943eb30bfS猫头猫 } else { 46043eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 461927dbe93S猫头猫 } 46243eb30bfS猫头猫 46343eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4647a91f04fS猫头猫 musicItem, 46543eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 46643eb30bfS猫头猫 46743eb30bfS猫头猫 /** 原始歌词文本 */ 4687e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4697e883dbbS猫头猫 let translation: string | null = null; 47043eb30bfS猫头猫 47126f32636S猫头猫 // 2. 本地手动设置的歌词 47226f32636S猫头猫 const platformHash = CryptoJs.MD5(musicItem.platform).toString( 47326f32636S猫头猫 CryptoJs.enc.Hex, 47426f32636S猫头猫 ); 47526f32636S猫头猫 const idHash = CryptoJs.MD5(musicItem.id).toString(CryptoJs.enc.Hex); 47626f32636S猫头猫 if ( 47726f32636S猫头猫 await RNFS.exists( 47826f32636S猫头猫 pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc', 47926f32636S猫头猫 ) 48026f32636S猫头猫 ) { 48126f32636S猫头猫 rawLrc = await RNFS.readFile( 48226f32636S猫头猫 pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc', 48326f32636S猫头猫 'utf8', 48426f32636S猫头猫 ); 48526f32636S猫头猫 48626f32636S猫头猫 if ( 48726f32636S猫头猫 await RNFS.exists( 48826f32636S猫头猫 pathConst.localLrcPath + 48926f32636S猫头猫 platformHash + 49026f32636S猫头猫 '/' + 49126f32636S猫头猫 idHash + 49226f32636S猫头猫 '.tran.lrc', 49326f32636S猫头猫 ) 49426f32636S猫头猫 ) { 49526f32636S猫头猫 translation = 49626f32636S猫头猫 (await RNFS.readFile( 49726f32636S猫头猫 pathConst.localLrcPath + 49826f32636S猫头猫 platformHash + 49926f32636S猫头猫 '/' + 50026f32636S猫头猫 idHash + 50126f32636S猫头猫 '.tran.lrc', 50226f32636S猫头猫 'utf8', 50326f32636S猫头猫 )) || null; 50426f32636S猫头猫 } 50526f32636S猫头猫 50626f32636S猫头猫 return { 50726f32636S猫头猫 rawLrc, 50826f32636S猫头猫 translation: translation || undefined, // TODO: 这里写的不好 50926f32636S猫头猫 }; 51026f32636S猫头猫 } 51126f32636S猫头猫 51243eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 51343eb30bfS猫头猫 if (musicItemCache?.lyric) { 51443eb30bfS猫头猫 // 缓存的远程结果 51543eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 51643eb30bfS猫头猫 musicItemCache.lyric || null; 51743eb30bfS猫头猫 // 缓存的本地结果 51843eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 51943eb30bfS猫头猫 musicItemCache.$localLyric || null; 52043eb30bfS猫头猫 5217e883dbbS猫头猫 // 优先用缓存的结果 5227e883dbbS猫头猫 if (cacheLyric.rawLrc || cacheLyric.translation) { 52343eb30bfS猫头猫 return { 5247e883dbbS猫头猫 rawLrc: cacheLyric.rawLrc, 5257e883dbbS猫头猫 translation: cacheLyric.translation, 52643eb30bfS猫头猫 }; 52743eb30bfS猫头猫 } 52843eb30bfS猫头猫 5297e883dbbS猫头猫 // 本地其实是缓存的路径 5307e883dbbS猫头猫 if (localLyric) { 5317e883dbbS猫头猫 let needRefetch = false; 5327e883dbbS猫头猫 if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) { 5337e883dbbS猫头猫 rawLrc = await readFile(localLyric.rawLrc, 'utf8'); 5347e883dbbS猫头猫 } else if (localLyric.rawLrc) { 5357e883dbbS猫头猫 needRefetch = true; 5367e883dbbS猫头猫 } 5377e883dbbS猫头猫 if ( 5387e883dbbS猫头猫 localLyric.translation && 5397e883dbbS猫头猫 (await exists(localLyric.translation)) 5407e883dbbS猫头猫 ) { 5417e883dbbS猫头猫 translation = await readFile( 5427e883dbbS猫头猫 localLyric.translation, 5437e883dbbS猫头猫 'utf8', 5447e883dbbS猫头猫 ); 5457e883dbbS猫头猫 } else if (localLyric.translation) { 5467e883dbbS猫头猫 needRefetch = true; 54743eb30bfS猫头猫 } 54843eb30bfS猫头猫 5497e883dbbS猫头猫 if (!needRefetch && (rawLrc || translation)) { 55043eb30bfS猫头猫 return { 5517e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5527e883dbbS猫头猫 translation: translation || undefined, 55343eb30bfS猫头猫 }; 55443eb30bfS猫头猫 } 55543eb30bfS猫头猫 } 55643eb30bfS猫头猫 } 55743eb30bfS猫头猫 55843eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 55943eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 56043eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 56143eb30bfS猫头猫 lrcSource = 56243eb30bfS猫头猫 (await this.plugin.instance 5637e883dbbS猫头猫 ?.getLyric?.(resetMediaItem(musicItem, undefined, true)) 56443eb30bfS猫头猫 ?.catch(() => null)) || null; 56543eb30bfS猫头猫 } else { 56643eb30bfS猫头猫 lrcSource = 56743eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 56843eb30bfS猫头猫 ?.instance?.getLyric?.( 56943eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 57043eb30bfS猫头猫 ) 57143eb30bfS猫头猫 ?.catch(() => null)) || null; 57243eb30bfS猫头猫 } 57343eb30bfS猫头猫 57443eb30bfS猫头猫 if (lrcSource) { 5757e883dbbS猫头猫 rawLrc = lrcSource?.rawLrc || rawLrc; 5767e883dbbS猫头猫 translation = lrcSource?.translation || null; 57743eb30bfS猫头猫 5787e883dbbS猫头猫 const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc; 57943eb30bfS猫头猫 5807e883dbbS猫头猫 // 本地的文件名 5817e883dbbS猫头猫 let filename: string | undefined = `${ 5827e883dbbS猫头猫 pathConst.lrcCachePath 5837e883dbbS猫头猫 }${nanoid()}.lrc`; 5847e883dbbS猫头猫 let filenameTrans: string | undefined = `${ 5857e883dbbS猫头猫 pathConst.lrcCachePath 5867e883dbbS猫头猫 }${nanoid()}.lrc`; 58743eb30bfS猫头猫 5887e883dbbS猫头猫 // 旧版本兼容 5897e883dbbS猫头猫 if (!(rawLrc || translation)) { 5907e883dbbS猫头猫 if (deprecatedLrcUrl) { 59143eb30bfS猫头猫 rawLrc = ( 5927e883dbbS猫头猫 await axios 5937e883dbbS猫头猫 .get(deprecatedLrcUrl, {timeout: 3000}) 5947e883dbbS猫头猫 .catch(() => null) 59543eb30bfS猫头猫 )?.data; 5967e883dbbS猫头猫 } else if (musicItem.rawLrc) { 5977e883dbbS猫头猫 rawLrc = musicItem.rawLrc; 5987e883dbbS猫头猫 } 59943eb30bfS猫头猫 } 60043eb30bfS猫头猫 60143eb30bfS猫头猫 if (rawLrc) { 60243eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 60343eb30bfS猫头猫 } else { 6047e883dbbS猫头猫 filename = undefined; 6057e883dbbS猫头猫 } 6067e883dbbS猫头猫 if (translation) { 6077e883dbbS猫头猫 await writeFile(filenameTrans, translation, 'utf8'); 6087e883dbbS猫头猫 } else { 6097e883dbbS猫头猫 filenameTrans = undefined; 6107a91f04fS猫头猫 } 6117a91f04fS猫头猫 6127e883dbbS猫头猫 if (rawLrc || translation) { 6137e883dbbS猫头猫 MediaCache.setMediaCache( 6147e883dbbS猫头猫 produce(musicItemCache || musicItem, draft => { 6157e883dbbS猫头猫 musicItemCache?.$localLyric?.rawLrc; 6167e883dbbS猫头猫 objectPath.set(draft, '$localLyric.rawLrc', filename); 61743eb30bfS猫头猫 objectPath.set( 61843eb30bfS猫头猫 draft, 6197e883dbbS猫头猫 '$localLyric.translation', 6207e883dbbS猫头猫 filenameTrans, 62143eb30bfS猫头猫 ); 62243eb30bfS猫头猫 return draft; 62343eb30bfS猫头猫 }), 62443eb30bfS猫头猫 ); 625927dbe93S猫头猫 return { 6267e883dbbS猫头猫 rawLrc: rawLrc || undefined, 6277e883dbbS猫头猫 translation: translation || undefined, 628927dbe93S猫头猫 }; 629927dbe93S猫头猫 } 630927dbe93S猫头猫 } 63143eb30bfS猫头猫 6323a6f67b1S猫头猫 // 6. 如果是本地文件 63343eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 63443eb30bfS猫头猫 if ( 63543eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 63643eb30bfS猫头猫 isDownloaded 63743eb30bfS猫头猫 ) { 6387e883dbbS猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 63943eb30bfS猫头猫 64043eb30bfS猫头猫 console.log('本地文件歌词'); 64143eb30bfS猫头猫 6423a6f67b1S猫头猫 if (res) { 6433a6f67b1S猫头猫 return res; 6443a6f67b1S猫头猫 } 6453a6f67b1S猫头猫 } 646ea6d708fS猫头猫 devLog('warn', '无歌词'); 647927dbe93S猫头猫 648927dbe93S猫头猫 return null; 649927dbe93S猫头猫 } 650927dbe93S猫头猫 651927dbe93S猫头猫 /** 获取歌词文本 */ 652927dbe93S猫头猫 async getLyricText( 653927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 654927dbe93S猫头猫 ): Promise<string | undefined> { 6557e883dbbS猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 656927dbe93S猫头猫 } 657927dbe93S猫头猫 658927dbe93S猫头猫 /** 获取专辑信息 */ 659927dbe93S猫头猫 async getAlbumInfo( 660927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 661f9afcc0dS猫头猫 page: number = 1, 662f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 663927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 664f9afcc0dS猫头猫 return { 665f9afcc0dS猫头猫 albumItem, 666f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 667f2a4767cS猫头猫 resetMediaItem, 668f2a4767cS猫头猫 this.plugin.name, 669f2a4767cS猫头猫 true, 670f2a4767cS猫头猫 ), 671f9afcc0dS猫头猫 isEnd: true, 672f9afcc0dS猫头猫 }; 673927dbe93S猫头猫 } 674927dbe93S猫头猫 try { 675927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 676927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 677f9afcc0dS猫头猫 page, 678927dbe93S猫头猫 ); 6795276aef9S猫头猫 if (!result) { 6805276aef9S猫头猫 throw new Error(); 6815276aef9S猫头猫 } 682927dbe93S猫头猫 result?.musicList?.forEach(_ => { 683927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 68496744680S猫头猫 _.album = albumItem.title; 685927dbe93S猫头猫 }); 6865276aef9S猫头猫 687f9afcc0dS猫头猫 if (page <= 1) { 688f9afcc0dS猫头猫 // 合并信息 689f9afcc0dS猫头猫 return { 690f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 691f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 692f9afcc0dS猫头猫 musicList: result.musicList, 693f9afcc0dS猫头猫 }; 694f9afcc0dS猫头猫 } else { 695f9afcc0dS猫头猫 return { 696f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 697f9afcc0dS猫头猫 musicList: result.musicList, 698f9afcc0dS猫头猫 }; 699f9afcc0dS猫头猫 } 7004394410dS猫头猫 } catch (e: any) { 7014394410dS猫头猫 trace('获取专辑信息失败', e?.message); 702ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 703ea6d708fS猫头猫 704f9afcc0dS猫头猫 return null; 705927dbe93S猫头猫 } 706927dbe93S猫头猫 } 707927dbe93S猫头猫 7085830c002S猫头猫 /** 获取歌单信息 */ 7095830c002S猫头猫 async getMusicSheetInfo( 7105830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 7115830c002S猫头猫 page: number = 1, 7125830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 7135281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 7145830c002S猫头猫 return { 7155830c002S猫头猫 sheetItem, 7165830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 7175830c002S猫头猫 isEnd: true, 7185830c002S猫头猫 }; 7195830c002S猫头猫 } 7205830c002S猫头猫 try { 7215830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 7225830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 7235830c002S猫头猫 page, 7245830c002S猫头猫 ); 7255830c002S猫头猫 if (!result) { 7265830c002S猫头猫 throw new Error(); 7275830c002S猫头猫 } 7285830c002S猫头猫 result?.musicList?.forEach(_ => { 7295830c002S猫头猫 resetMediaItem(_, this.plugin.name); 7305830c002S猫头猫 }); 7315830c002S猫头猫 7325830c002S猫头猫 if (page <= 1) { 7335830c002S猫头猫 // 合并信息 7345830c002S猫头猫 return { 7355830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 7365830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7375830c002S猫头猫 musicList: result.musicList, 7385830c002S猫头猫 }; 7395830c002S猫头猫 } else { 7405830c002S猫头猫 return { 7415830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7425830c002S猫头猫 musicList: result.musicList, 7435830c002S猫头猫 }; 7445830c002S猫头猫 } 7455830c002S猫头猫 } catch (e: any) { 7465830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7475830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7485830c002S猫头猫 7495830c002S猫头猫 return null; 7505830c002S猫头猫 } 7515830c002S猫头猫 } 7525830c002S猫头猫 753927dbe93S猫头猫 /** 查询作者信息 */ 754efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 755927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 756927dbe93S猫头猫 page: number, 757927dbe93S猫头猫 type: T, 758927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 759efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 760927dbe93S猫头猫 return { 761927dbe93S猫头猫 isEnd: true, 762927dbe93S猫头猫 data: [], 763927dbe93S猫头猫 }; 764927dbe93S猫头猫 } 765927dbe93S猫头猫 try { 766efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 767927dbe93S猫头猫 artistItem, 768927dbe93S猫头猫 page, 769927dbe93S猫头猫 type, 770927dbe93S猫头猫 ); 771927dbe93S猫头猫 if (!result.data) { 772927dbe93S猫头猫 return { 773927dbe93S猫头猫 isEnd: true, 774927dbe93S猫头猫 data: [], 775927dbe93S猫头猫 }; 776927dbe93S猫头猫 } 777927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 778927dbe93S猫头猫 return { 779927dbe93S猫头猫 isEnd: result.isEnd ?? true, 780927dbe93S猫头猫 data: result.data, 781927dbe93S猫头猫 }; 7824394410dS猫头猫 } catch (e: any) { 7834394410dS猫头猫 trace('查询作者信息失败', e?.message); 784ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 785ea6d708fS猫头猫 786927dbe93S猫头猫 throw e; 787927dbe93S猫头猫 } 788927dbe93S猫头猫 } 78908380090S猫头猫 79008380090S猫头猫 /** 导入歌单 */ 79108380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 79208380090S猫头猫 try { 79308380090S猫头猫 const result = 79408380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 79508380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 79608380090S猫头猫 return result; 797ea6d708fS猫头猫 } catch (e: any) { 7980e4173cdS猫头猫 console.log(e); 799ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 800ea6d708fS猫头猫 80108380090S猫头猫 return []; 80208380090S猫头猫 } 80308380090S猫头猫 } 8044d9d3c4cS猫头猫 /** 导入单曲 */ 8054d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 8064d9d3c4cS猫头猫 try { 8074d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 8084d9d3c4cS猫头猫 urlLike, 8094d9d3c4cS猫头猫 ); 8104d9d3c4cS猫头猫 if (!result) { 8114d9d3c4cS猫头猫 throw new Error(); 8124d9d3c4cS猫头猫 } 8134d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 8144d9d3c4cS猫头猫 return result; 815ea6d708fS猫头猫 } catch (e: any) { 816ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 817ea6d708fS猫头猫 8184d9d3c4cS猫头猫 return null; 8194d9d3c4cS猫头猫 } 8204d9d3c4cS猫头猫 } 821d52aa40eS猫头猫 /** 获取榜单 */ 82292b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 823d52aa40eS猫头猫 try { 824d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 825d52aa40eS猫头猫 if (!result) { 826d52aa40eS猫头猫 throw new Error(); 827d52aa40eS猫头猫 } 828d52aa40eS猫头猫 return result; 829d52aa40eS猫头猫 } catch (e: any) { 830d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 831d52aa40eS猫头猫 return []; 832d52aa40eS猫头猫 } 833d52aa40eS猫头猫 } 834d52aa40eS猫头猫 /** 获取榜单详情 */ 835d52aa40eS猫头猫 async getTopListDetail( 83692b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 837956ee1b7S猫头猫 page: number, 838956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 839d52aa40eS猫头猫 try { 840d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 841d52aa40eS猫头猫 topListItem, 842956ee1b7S猫头猫 page, 843d52aa40eS猫头猫 ); 844d52aa40eS猫头猫 if (!result) { 845d52aa40eS猫头猫 throw new Error(); 846d52aa40eS猫头猫 } 847d384662fS猫头猫 if (result.musicList) { 848d384662fS猫头猫 result.musicList.forEach(_ => 849d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 850d384662fS猫头猫 ); 851d384662fS猫头猫 } 852956ee1b7S猫头猫 if (result.isEnd !== false) { 853956ee1b7S猫头猫 result.isEnd = true; 854956ee1b7S猫头猫 } 855d52aa40eS猫头猫 return result; 856d52aa40eS猫头猫 } catch (e: any) { 857d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 858d52aa40eS猫头猫 return { 859956ee1b7S猫头猫 isEnd: true, 860956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 861d52aa40eS猫头猫 musicList: [], 862d52aa40eS猫头猫 }; 863d52aa40eS猫头猫 } 864d52aa40eS猫头猫 } 865ceb900cdS猫头猫 8665830c002S猫头猫 /** 获取推荐歌单的tag */ 867ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 868ceb900cdS猫头猫 try { 869ceb900cdS猫头猫 const result = 870ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 871ceb900cdS猫头猫 if (!result) { 872ceb900cdS猫头猫 throw new Error(); 873ceb900cdS猫头猫 } 874ceb900cdS猫头猫 return result; 875ceb900cdS猫头猫 } catch (e: any) { 876ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 877ceb900cdS猫头猫 return { 878ceb900cdS猫头猫 data: [], 879ceb900cdS猫头猫 }; 880ceb900cdS猫头猫 } 881ceb900cdS猫头猫 } 8825830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 883ceb900cdS猫头猫 async getRecommendSheetsByTag( 884ceb900cdS猫头猫 tagItem: ICommon.IUnique, 885ceb900cdS猫头猫 page?: number, 886ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 887ceb900cdS猫头猫 try { 888ceb900cdS猫头猫 const result = 889ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 890ceb900cdS猫头猫 tagItem, 891ceb900cdS猫头猫 page ?? 1, 892ceb900cdS猫头猫 ); 893ceb900cdS猫头猫 if (!result) { 894ceb900cdS猫头猫 throw new Error(); 895ceb900cdS猫头猫 } 896ceb900cdS猫头猫 if (result.isEnd !== false) { 897ceb900cdS猫头猫 result.isEnd = true; 898ceb900cdS猫头猫 } 899ceb900cdS猫头猫 if (!result.data) { 900ceb900cdS猫头猫 result.data = []; 901ceb900cdS猫头猫 } 902ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 903ceb900cdS猫头猫 904ceb900cdS猫头猫 return result; 905ceb900cdS猫头猫 } catch (e: any) { 906ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 907ceb900cdS猫头猫 return { 908ceb900cdS猫头猫 isEnd: true, 909ceb900cdS猫头猫 data: [], 910ceb900cdS猫头猫 }; 911ceb900cdS猫头猫 } 912ceb900cdS猫头猫 } 913da2a2959S猫头猫 914da2a2959S猫头猫 async getMusicComments( 915da2a2959S猫头猫 musicItem: IMusic.IMusicItem, 916da2a2959S猫头猫 ): Promise<ICommon.PaginationResponse<IMedia.IComment>> { 917da2a2959S猫头猫 const result = await this.plugin.instance?.getMusicComments?.( 918da2a2959S猫头猫 musicItem, 919da2a2959S猫头猫 ); 920da2a2959S猫头猫 if (!result) { 921da2a2959S猫头猫 throw new Error(); 922da2a2959S猫头猫 } 923da2a2959S猫头猫 if (result.isEnd !== false) { 924da2a2959S猫头猫 result.isEnd = true; 925da2a2959S猫头猫 } 926da2a2959S猫头猫 if (!result.data) { 927da2a2959S猫头猫 result.data = []; 928da2a2959S猫头猫 } 929da2a2959S猫头猫 930da2a2959S猫头猫 return result; 931da2a2959S猫头猫 } 932*7fb90110S猫头猫 933*7fb90110S猫头猫 async migrateFromOtherPlugin( 934*7fb90110S猫头猫 mediaItem: ICommon.IMediaBase, 935*7fb90110S猫头猫 fromPlatform: string, 936*7fb90110S猫头猫 ): Promise<{isOk: boolean; data?: ICommon.IMediaBase}> { 937*7fb90110S猫头猫 try { 938*7fb90110S猫头猫 const result = await this.plugin.instance?.migrateFromOtherPlugin( 939*7fb90110S猫头猫 mediaItem, 940*7fb90110S猫头猫 fromPlatform, 941*7fb90110S猫头猫 ); 942*7fb90110S猫头猫 943*7fb90110S猫头猫 if ( 944*7fb90110S猫头猫 result.isOk && 945*7fb90110S猫头猫 result.data?.id && 946*7fb90110S猫头猫 result.data?.platform === this.plugin.platform 947*7fb90110S猫头猫 ) { 948*7fb90110S猫头猫 return { 949*7fb90110S猫头猫 isOk: result.isOk, 950*7fb90110S猫头猫 data: result.data, 951*7fb90110S猫头猫 }; 952*7fb90110S猫头猫 } 953*7fb90110S猫头猫 return { 954*7fb90110S猫头猫 isOk: false, 955*7fb90110S猫头猫 }; 956*7fb90110S猫头猫 } catch { 957*7fb90110S猫头猫 return { 958*7fb90110S猫头猫 isOk: false, 959*7fb90110S猫头猫 }; 960*7fb90110S猫头猫 } 961*7fb90110S猫头猫 } 962927dbe93S猫头猫} 963d5bfeb7eS猫头猫//#endregion 9641a5528a0S猫头猫 965927dbe93S猫头猫let plugins: Array<Plugin> = []; 966927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 96774d0cf81S猫头猫 968d5bfeb7eS猫头猫//#region 本地音乐插件 96974d0cf81S猫头猫/** 本地插件 */ 97074d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 9710e4173cdS猫头猫 return { 972d5bfeb7eS猫头猫 platform: localPluginPlatform, 97374d0cf81S猫头猫 _path: '', 97474d0cf81S猫头猫 async getMusicInfo(musicBase) { 97574d0cf81S猫头猫 const localPath = getInternalData<string>( 97674d0cf81S猫头猫 musicBase, 97774d0cf81S猫头猫 InternalDataType.LOCALPATH, 9780e4173cdS猫头猫 ); 97974d0cf81S猫头猫 if (localPath) { 98074d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 98174d0cf81S猫头猫 return { 98274d0cf81S猫头猫 artwork: coverImg, 98374d0cf81S猫头猫 }; 98474d0cf81S猫头猫 } 98574d0cf81S猫头猫 return null; 98674d0cf81S猫头猫 }, 9877993f90eS猫头猫 async getLyric(musicBase) { 9887993f90eS猫头猫 const localPath = getInternalData<string>( 9897993f90eS猫头猫 musicBase, 9907993f90eS猫头猫 InternalDataType.LOCALPATH, 9917993f90eS猫头猫 ); 9923a6f67b1S猫头猫 let rawLrc: string | null = null; 9937993f90eS猫头猫 if (localPath) { 9943a6f67b1S猫头猫 // 读取内嵌歌词 9953a6f67b1S猫头猫 try { 9963a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 9973a6f67b1S猫头猫 } catch (e) { 998a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 9997993f90eS猫头猫 } 10003a6f67b1S猫头猫 if (!rawLrc) { 10013a6f67b1S猫头猫 // 读取配置歌词 10023a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 10033a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 10043a6f67b1S猫头猫 10053a6f67b1S猫头猫 try { 10063a6f67b1S猫头猫 if (await exists(lrcPath)) { 10073a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 10083a6f67b1S猫头猫 } 10093a6f67b1S猫头猫 } catch {} 10103a6f67b1S猫头猫 } 10113a6f67b1S猫头猫 } 10123a6f67b1S猫头猫 10133a6f67b1S猫头猫 return rawLrc 10143a6f67b1S猫头猫 ? { 10153a6f67b1S猫头猫 rawLrc, 10163a6f67b1S猫头猫 } 10173a6f67b1S猫头猫 : null; 10187993f90eS猫头猫 }, 1019a84a85c5S猫头猫 async importMusicItem(urlLike) { 1020a84a85c5S猫头猫 let meta: any = {}; 1021a84a85c5S猫头猫 try { 1022a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 1023a84a85c5S猫头猫 } catch {} 10245353b473S猫头猫 const stat = await getInfoAsync(urlLike, { 10255353b473S猫头猫 md5: true, 10265353b473S猫头猫 }); 10275353b473S猫头猫 let id: string; 10285353b473S猫头猫 if (stat.exists) { 10295353b473S猫头猫 id = stat.md5 || nanoid(); 10305353b473S猫头猫 } else { 10315353b473S猫头猫 id = nanoid(); 10325353b473S猫头猫 } 1033a84a85c5S猫头猫 return { 1034a84a85c5S猫头猫 id: id, 1035a84a85c5S猫头猫 platform: '本地', 1036a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 1037a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 10385353b473S猫头猫 duration: parseInt(meta?.duration ?? '0', 10) / 1000, 1039a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 1040a84a85c5S猫头猫 artwork: '', 1041a84a85c5S猫头猫 [internalSerializeKey]: { 1042a84a85c5S猫头猫 localPath: urlLike, 1043a84a85c5S猫头猫 }, 1044a84a85c5S猫头猫 }; 1045a84a85c5S猫头猫 }, 104611908939S猫头猫 async getMediaSource(musicItem, quality) { 104711908939S猫头猫 if (quality === 'standard') { 104811908939S猫头猫 return { 104911908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 105011908939S猫头猫 }; 105111908939S猫头猫 } 105211908939S猫头猫 return null; 105311908939S猫头猫 }, 105474d0cf81S猫头猫 }; 105574d0cf81S猫头猫}, ''); 10567993f90eS猫头猫localFilePlugin.hash = localPluginHash; 1057927dbe93S猫头猫 1058d5bfeb7eS猫头猫//#endregion 1059d5bfeb7eS猫头猫 1060927dbe93S猫头猫async function setup() { 1061927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 1062927dbe93S猫头猫 try { 1063927dbe93S猫头猫 // 加载插件 1064927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 1065927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 1066927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 10671e263108S猫头猫 trace('初始化插件', _pluginUrl); 10681e263108S猫头猫 if ( 10691e263108S猫头猫 _pluginUrl.isFile() && 10701e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 10711e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 10721e263108S猫头猫 ) { 1073927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 1074927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 10754060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 10764060c00aS猫头猫 p => p.hash === plugin.hash, 10774060c00aS猫头猫 ); 1078927dbe93S猫头猫 if (_pluginIndex !== -1) { 1079927dbe93S猫头猫 // 重复插件,直接忽略 10800c266394S猫头猫 continue; 1081927dbe93S猫头猫 } 1082927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 1083927dbe93S猫头猫 } 1084927dbe93S猫头猫 } 1085927dbe93S猫头猫 1086927dbe93S猫头猫 plugins = _plugins; 1087e08d37a3S猫头猫 /** 初始化meta信息 */ 1088c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 1089c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 1090c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 1091c2b3a262S猫头猫 for (let plugin of plugins) { 1092c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1093c2b3a262S猫头猫 plugin.state = 'disabled'; 1094c2b3a262S猫头猫 } 1095c2b3a262S猫头猫 } 1096c2b3a262S猫头猫 pluginStateMapper.notify(); 1097927dbe93S猫头猫 } catch (e: any) { 10984060c00aS猫头猫 ToastAndroid.show( 10994060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 11004060c00aS猫头猫 ToastAndroid.LONG, 11014060c00aS猫头猫 ); 11021a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1103927dbe93S猫头猫 throw e; 1104927dbe93S猫头猫 } 1105927dbe93S猫头猫} 1106927dbe93S猫头猫 1107e36e2599S猫头猫interface IInstallPluginConfig { 1108e36e2599S猫头猫 notCheckVersion?: boolean; 1109e36e2599S猫头猫} 1110e36e2599S猫头猫 1111740e3947S猫头猫async function installPluginFromRawCode( 1112740e3947S猫头猫 funcCode: string, 1113740e3947S猫头猫 config?: IInstallPluginConfig, 1114740e3947S猫头猫) { 1115740e3947S猫头猫 if (funcCode) { 1116740e3947S猫头猫 const plugin = new Plugin(funcCode, ''); 1117740e3947S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1118740e3947S猫头猫 if (_pluginIndex !== -1) { 1119740e3947S猫头猫 // 静默忽略 1120740e3947S猫头猫 return plugin; 1121740e3947S猫头猫 } 1122740e3947S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1123740e3947S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1124740e3947S猫头猫 if ( 1125740e3947S猫头猫 compare( 1126740e3947S猫头猫 oldVersionPlugin.instance.version ?? '', 1127740e3947S猫头猫 plugin.instance.version ?? '', 1128740e3947S猫头猫 '>', 1129740e3947S猫头猫 ) 1130740e3947S猫头猫 ) { 1131740e3947S猫头猫 throw new Error('已安装更新版本的插件'); 1132740e3947S猫头猫 } 1133740e3947S猫头猫 } 1134740e3947S猫头猫 1135740e3947S猫头猫 if (plugin.hash !== '') { 1136740e3947S猫头猫 const fn = nanoid(); 1137740e3947S猫头猫 if (oldVersionPlugin) { 1138740e3947S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1139740e3947S猫头猫 try { 1140740e3947S猫头猫 await unlink(oldVersionPlugin.path); 1141740e3947S猫头猫 } catch {} 1142740e3947S猫头猫 } 1143740e3947S猫头猫 const pluginPath = `${pathConst.pluginPath}${fn}.js`; 1144740e3947S猫头猫 await writeFile(pluginPath, funcCode, 'utf8'); 1145740e3947S猫头猫 plugin.path = pluginPath; 1146740e3947S猫头猫 plugins = plugins.concat(plugin); 1147740e3947S猫头猫 pluginStateMapper.notify(); 1148740e3947S猫头猫 return plugin; 1149740e3947S猫头猫 } 1150740e3947S猫头猫 throw new Error('插件无法解析!'); 1151740e3947S猫头猫 } 1152740e3947S猫头猫} 1153740e3947S猫头猫 1154927dbe93S猫头猫// 安装插件 1155e36e2599S猫头猫async function installPlugin( 1156e36e2599S猫头猫 pluginPath: string, 1157e36e2599S猫头猫 config?: IInstallPluginConfig, 1158e36e2599S猫头猫) { 115922c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1160927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1161e36e2599S猫头猫 1162e36e2599S猫头猫 if (funcCode) { 1163927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1164927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1165927dbe93S猫头猫 if (_pluginIndex !== -1) { 1166e36e2599S猫头猫 // 静默忽略 1167e36e2599S猫头猫 return plugin; 1168927dbe93S猫头猫 } 1169e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1170e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1171e36e2599S猫头猫 if ( 1172e36e2599S猫头猫 compare( 1173e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1174e36e2599S猫头猫 plugin.instance.version ?? '', 1175e36e2599S猫头猫 '>', 1176e36e2599S猫头猫 ) 1177e36e2599S猫头猫 ) { 1178e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1179e36e2599S猫头猫 } 1180e36e2599S猫头猫 } 1181e36e2599S猫头猫 1182927dbe93S猫头猫 if (plugin.hash !== '') { 1183927dbe93S猫头猫 const fn = nanoid(); 1184e36e2599S猫头猫 if (oldVersionPlugin) { 1185e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1186e36e2599S猫头猫 try { 1187e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1188e36e2599S猫头猫 } catch {} 1189e36e2599S猫头猫 } 1190927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1191927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1192927dbe93S猫头猫 plugin.path = _pluginPath; 1193927dbe93S猫头猫 plugins = plugins.concat(plugin); 1194927dbe93S猫头猫 pluginStateMapper.notify(); 1195a84a85c5S猫头猫 return plugin; 1196927dbe93S猫头猫 } 1197e36e2599S猫头猫 throw new Error('插件无法解析!'); 1198927dbe93S猫头猫 } 1199e36e2599S猫头猫 throw new Error('插件无法识别!'); 1200c2b3a262S猫头猫} 1201c2b3a262S猫头猫 1202ee781061S猫头猫const reqHeaders = { 1203ee781061S猫头猫 'Cache-Control': 'no-cache', 1204ee781061S猫头猫 Pragma: 'no-cache', 1205ee781061S猫头猫 Expires: '0', 1206ee781061S猫头猫}; 1207ee781061S猫头猫 1208c2b3a262S猫头猫async function installPluginFromUrl( 1209c2b3a262S猫头猫 url: string, 1210c2b3a262S猫头猫 config?: IInstallPluginConfig, 1211c2b3a262S猫头猫) { 121258992c6bS猫头猫 try { 1213ee781061S猫头猫 const funcCode = ( 1214ee781061S猫头猫 await axios.get(url, { 1215ee781061S猫头猫 headers: reqHeaders, 1216ee781061S猫头猫 }) 1217ee781061S猫头猫 ).data; 121858992c6bS猫头猫 if (funcCode) { 121958992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 122058992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 122158992c6bS猫头猫 if (_pluginIndex !== -1) { 12228b7ddca8S猫头猫 // 静默忽略 12238b7ddca8S猫头猫 return; 122458992c6bS猫头猫 } 122525c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1226c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 122725c1bd29S猫头猫 if ( 122825c1bd29S猫头猫 compare( 122925c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 123025c1bd29S猫头猫 plugin.instance.version ?? '', 123125c1bd29S猫头猫 '>', 123225c1bd29S猫头猫 ) 123325c1bd29S猫头猫 ) { 123425c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 123525c1bd29S猫头猫 } 123625c1bd29S猫头猫 } 123725c1bd29S猫头猫 123858992c6bS猫头猫 if (plugin.hash !== '') { 123958992c6bS猫头猫 const fn = nanoid(); 124058992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 124158992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 124258992c6bS猫头猫 plugin.path = _pluginPath; 124358992c6bS猫头猫 plugins = plugins.concat(plugin); 124425c1bd29S猫头猫 if (oldVersionPlugin) { 124525c1bd29S猫头猫 plugins = plugins.filter( 124625c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 124725c1bd29S猫头猫 ); 124825c1bd29S猫头猫 try { 124925c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 125025c1bd29S猫头猫 } catch {} 125125c1bd29S猫头猫 } 125258992c6bS猫头猫 pluginStateMapper.notify(); 125358992c6bS猫头猫 return; 125458992c6bS猫头猫 } 125574acbfc0S猫头猫 throw new Error('插件无法解析!'); 125658992c6bS猫头猫 } 125725c1bd29S猫头猫 } catch (e: any) { 1258ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 125958992c6bS猫头猫 errorLog('URL安装插件失败', e); 126025c1bd29S猫头猫 throw new Error(e?.message ?? ''); 126158992c6bS猫头猫 } 126258992c6bS猫头猫} 126358992c6bS猫头猫 1264927dbe93S猫头猫/** 卸载插件 */ 1265927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1266927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1267927dbe93S猫头猫 if (targetIndex !== -1) { 1268927dbe93S猫头猫 try { 126924e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1270927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1271927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1272927dbe93S猫头猫 pluginStateMapper.notify(); 127343eb30bfS猫头猫 // 防止其他重名 127424e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 12755589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 127624e5e74aS猫头猫 } 1277927dbe93S猫头猫 } catch {} 1278927dbe93S猫头猫 } 1279927dbe93S猫头猫} 1280927dbe93S猫头猫 128108882a77S猫头猫async function uninstallAllPlugins() { 128208882a77S猫头猫 await Promise.all( 128308882a77S猫头猫 plugins.map(async plugin => { 128408882a77S猫头猫 try { 128508882a77S猫头猫 const pluginName = plugin.name; 128608882a77S猫头猫 await unlink(plugin.path); 12875589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 128808882a77S猫头猫 } catch (e) {} 128908882a77S猫头猫 }), 129008882a77S猫头猫 ); 129108882a77S猫头猫 plugins = []; 129208882a77S猫头猫 pluginStateMapper.notify(); 1293e08d37a3S猫头猫 1294e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1295e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1296e08d37a3S猫头猫 .then(fns => { 1297e08d37a3S猫头猫 fns.forEach(fn => { 1298e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1299e08d37a3S猫头猫 }); 1300e08d37a3S猫头猫 }) 1301e08d37a3S猫头猫 .catch(emptyFunction); 130208882a77S猫头猫} 130308882a77S猫头猫 130425c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 130525c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 130625c1bd29S猫头猫 if (!updateUrl) { 130725c1bd29S猫头猫 throw new Error('没有更新源'); 130825c1bd29S猫头猫 } 130925c1bd29S猫头猫 try { 131025c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 131125c1bd29S猫头猫 } catch (e: any) { 131225c1bd29S猫头猫 if (e.message === '插件已安装') { 131325c1bd29S猫头猫 throw new Error('当前已是最新版本'); 131425c1bd29S猫头猫 } else { 131525c1bd29S猫头猫 throw e; 131625c1bd29S猫头猫 } 131725c1bd29S猫头猫 } 131825c1bd29S猫头猫} 131925c1bd29S猫头猫 1320927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 13212c595535S猫头猫 return getByName(mediaItem?.platform); 1322927dbe93S猫头猫} 1323927dbe93S猫头猫 1324927dbe93S猫头猫function getByHash(hash: string) { 13257993f90eS猫头猫 return hash === localPluginHash 13267993f90eS猫头猫 ? localFilePlugin 13277993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1328927dbe93S猫头猫} 1329927dbe93S猫头猫 1330927dbe93S猫头猫function getByName(name: string) { 13317993f90eS猫头猫 return name === localPluginPlatform 13320e4173cdS猫头猫 ? localFilePlugin 13330e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1334927dbe93S猫头猫} 1335927dbe93S猫头猫 1336927dbe93S猫头猫function getValidPlugins() { 1337927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1338927dbe93S猫头猫} 1339927dbe93S猫头猫 13402b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 13412b80a429S猫头猫 return plugins.filter( 13422b80a429S猫头猫 _ => 13432b80a429S猫头猫 _.state === 'enabled' && 13442b80a429S猫头猫 _.instance.search && 134539ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 134639ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 13472b80a429S猫头猫 : true), 13482b80a429S猫头猫 ); 1349efb9da24S猫头猫} 1350efb9da24S猫头猫 13512b80a429S猫头猫function getSortedSearchablePlugins( 13522b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 13532b80a429S猫头猫) { 13542b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1355e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1356e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1357e08d37a3S猫头猫 0 1358e08d37a3S猫头猫 ? -1 1359e08d37a3S猫头猫 : 1, 1360e08d37a3S猫头猫 ); 1361e08d37a3S猫头猫} 1362e08d37a3S猫头猫 136315feccc1S猫头猫function getTopListsablePlugins() { 136415feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 136515feccc1S猫头猫} 136615feccc1S猫头猫 136715feccc1S猫头猫function getSortedTopListsablePlugins() { 136815feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 136915feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 137015feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 137115feccc1S猫头猫 0 137215feccc1S猫头猫 ? -1 137315feccc1S猫头猫 : 1, 137415feccc1S猫头猫 ); 137515feccc1S猫头猫} 137615feccc1S猫头猫 1377ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1378ceb900cdS猫头猫 return plugins.filter( 1379ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1380ceb900cdS猫头猫 ); 1381ceb900cdS猫头猫} 1382ceb900cdS猫头猫 1383ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1384ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1385ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1386ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1387ceb900cdS猫头猫 0 1388ceb900cdS猫头猫 ? -1 1389ceb900cdS猫头猫 : 1, 1390ceb900cdS猫头猫 ); 1391ceb900cdS猫头猫} 1392ceb900cdS猫头猫 1393e08d37a3S猫头猫function useSortedPlugins() { 1394e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1395e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1396e08d37a3S猫头猫 139734588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 139834588741S猫头猫 [..._plugins].sort((a, b) => 1399e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1400e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1401e08d37a3S猫头猫 0 1402e08d37a3S猫头猫 ? -1 1403e08d37a3S猫头猫 : 1, 140434588741S猫头猫 ), 1405e08d37a3S猫头猫 ); 140634588741S猫头猫 140734588741S猫头猫 useEffect(() => { 1408d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 140934588741S猫头猫 setSortedPlugins( 141034588741S猫头猫 [..._plugins].sort((a, b) => 141134588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 141234588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 141334588741S猫头猫 0 141434588741S猫头猫 ? -1 141534588741S猫头猫 : 1, 141634588741S猫头猫 ), 141734588741S猫头猫 ); 1418d4cd40d8S猫头猫 }); 141934588741S猫头猫 }, [_plugins, _pluginMetaAll]); 142034588741S猫头猫 142134588741S猫头猫 return sortedPlugins; 1422e08d37a3S猫头猫} 1423e08d37a3S猫头猫 1424c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1425c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1426c2b3a262S猫头猫 if (target) { 1427c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1428c2b3a262S猫头猫 plugins = [...plugins]; 1429c2b3a262S猫头猫 pluginStateMapper.notify(); 1430c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1431c2b3a262S猫头猫 } 1432c2b3a262S猫头猫} 1433c2b3a262S猫头猫 1434927dbe93S猫头猫const PluginManager = { 1435927dbe93S猫头猫 setup, 1436927dbe93S猫头猫 installPlugin, 1437740e3947S猫头猫 installPluginFromRawCode, 143858992c6bS猫头猫 installPluginFromUrl, 143925c1bd29S猫头猫 updatePlugin, 1440927dbe93S猫头猫 uninstallPlugin, 1441927dbe93S猫头猫 getByMedia, 1442927dbe93S猫头猫 getByHash, 1443927dbe93S猫头猫 getByName, 1444927dbe93S猫头猫 getValidPlugins, 1445efb9da24S猫头猫 getSearchablePlugins, 1446e08d37a3S猫头猫 getSortedSearchablePlugins, 144715feccc1S猫头猫 getTopListsablePlugins, 1448ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 144915feccc1S猫头猫 getSortedTopListsablePlugins, 14505276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1451e08d37a3S猫头猫 useSortedPlugins, 145208882a77S猫头猫 uninstallAllPlugins, 1453c2b3a262S猫头猫 setPluginEnabled, 14545276aef9S猫头猫}; 1455927dbe93S猫头猫 1456927dbe93S猫头猫export default PluginManager; 1457