14060c00aS猫头猫import { 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'; 20*5589cdf3S猫头猫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'; 420e4173cdS猫头猫import {FileSystem} from 'react-native-file-access'; 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'; 50*5589cdf3S猫头猫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猫头猫 ); 311a84a85c5S猫头猫 if ( 312a84a85c5S猫头猫 localPath && 313a84a85c5S猫头猫 (localPath.startsWith('content://') || 314a84a85c5S猫头猫 (await FileSystem.exists(localPath))) 315a84a85c5S猫头猫 ) { 3160e4173cdS猫头猫 trace('本地播放', localPath); 31743eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 31843eb30bfS猫头猫 // 修正一下本地数据 31943eb30bfS猫头猫 MediaExtra.update(musicItem, { 32043eb30bfS猫头猫 localPath, 32143eb30bfS猫头猫 }); 32243eb30bfS猫头猫 } 323927dbe93S猫头猫 return { 32411908939S猫头猫 url: addFileScheme(localPath), 325927dbe93S猫头猫 }; 32643eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 32743eb30bfS猫头猫 MediaExtra.update(musicItem, { 32843eb30bfS猫头猫 localPath: undefined, 32943eb30bfS猫头猫 }); 330927dbe93S猫头猫 } 331a84a85c5S猫头猫 3327993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 333f5935920S猫头猫 throw new Error('本地音乐不存在'); 334f5935920S猫头猫 } 335927dbe93S猫头猫 // 2. 缓存播放 33643eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 33743eb30bfS猫头猫 musicItem, 33843eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 339985f8e75S猫头猫 const pluginCacheControl = 340985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 341cfa0fc07S猫头猫 if ( 342cfa0fc07S猫头猫 mediaCache && 34343eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34448f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34548f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 346ef714860S猫头猫 Network.isOffline())) 347cfa0fc07S猫头猫 ) { 3485276aef9S猫头猫 trace('播放', '缓存播放'); 34943eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 350927dbe93S猫头猫 return { 35143eb30bfS猫头猫 url: qualityInfo!.url, 352927dbe93S猫头猫 headers: mediaCache.headers, 3534060c00aS猫头猫 userAgent: 3544060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 355927dbe93S猫头猫 }; 356927dbe93S猫头猫 } 357927dbe93S猫头猫 // 3. 插件解析 35820e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 359a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 360a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 361a7b42a4cS猫头猫 ); 362a7b42a4cS猫头猫 return { 363a7b42a4cS猫头猫 url: url, 364a7b42a4cS猫头猫 headers: auth 365a7b42a4cS猫头猫 ? { 366a7b42a4cS猫头猫 Authorization: auth, 367a7b42a4cS猫头猫 } 368a7b42a4cS猫头猫 : undefined, 369a7b42a4cS猫头猫 }; 370927dbe93S猫头猫 } 371927dbe93S猫头猫 try { 372abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 373abaede57S猫头猫 musicItem, 374abaede57S猫头猫 quality, 375abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 376927dbe93S猫头猫 if (!url) { 377a28eac61S猫头猫 throw new Error('NOT RETRY'); 378927dbe93S猫头猫 } 3795276aef9S猫头猫 trace('播放', '插件播放'); 380927dbe93S猫头猫 const result = { 381927dbe93S猫头猫 url, 382927dbe93S猫头猫 headers, 383927dbe93S猫头猫 userAgent: headers?.['user-agent'], 384cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 385a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 386a7b42a4cS猫头猫 if (authFormattedResult.auth) { 387a7b42a4cS猫头猫 result.url = authFormattedResult.url; 388a7b42a4cS猫头猫 result.headers = { 389a7b42a4cS猫头猫 ...(result.headers ?? {}), 390a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 391a7b42a4cS猫头猫 }; 392a7b42a4cS猫头猫 } 393927dbe93S猫头猫 394dc160d50S猫头猫 if ( 395dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 396dc160d50S猫头猫 !notUpdateCache 397dc160d50S猫头猫 ) { 39843eb30bfS猫头猫 // 更新缓存 39943eb30bfS猫头猫 const cacheSource = { 40043eb30bfS猫头猫 headers: result.headers, 40143eb30bfS猫头猫 userAgent: result.userAgent, 40243eb30bfS猫头猫 url, 40343eb30bfS猫头猫 }; 40443eb30bfS猫头猫 let realMusicItem = { 40543eb30bfS猫头猫 ...musicItem, 40643eb30bfS猫头猫 ...(mediaCache || {}), 40743eb30bfS猫头猫 }; 40843eb30bfS猫头猫 realMusicItem.source = { 40943eb30bfS猫头猫 ...(realMusicItem.source || {}), 41043eb30bfS猫头猫 [quality]: cacheSource, 41143eb30bfS猫头猫 }; 41243eb30bfS猫头猫 41343eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 414752ffc5aS猫头猫 } 415927dbe93S猫头猫 return result; 416927dbe93S猫头猫 } catch (e: any) { 417a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 418927dbe93S猫头猫 await delay(150); 419abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 420927dbe93S猫头猫 } 421927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 422ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 423192ae2b0S猫头猫 return null; 424927dbe93S猫头猫 } 425927dbe93S猫头猫 } 426927dbe93S猫头猫 427927dbe93S猫头猫 /** 获取音乐详情 */ 428927dbe93S猫头猫 async getMusicInfo( 429927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 43074d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 431927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 432d704daedS猫头猫 return null; 433927dbe93S猫头猫 } 43474d0cf81S猫头猫 try { 435927dbe93S猫头猫 return ( 436927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4377993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 438d704daedS猫头猫 ) ?? null 439927dbe93S猫头猫 ); 440ea6d708fS猫头猫 } catch (e: any) { 441ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 442d704daedS猫头猫 return null; 44374d0cf81S猫头猫 } 444927dbe93S猫头猫 } 445927dbe93S猫头猫 4467e883dbbS猫头猫 /** 4477e883dbbS猫头猫 * 4487e883dbbS猫头猫 * getLyric(musicItem) => { 4497e883dbbS猫头猫 * lyric: string; 4507e883dbbS猫头猫 * trans: string; 4517e883dbbS猫头猫 * } 4527e883dbbS猫头猫 * 4537e883dbbS猫头猫 */ 454927dbe93S猫头猫 /** 获取歌词 */ 455927dbe93S猫头猫 async getLyric( 45643eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 457927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 45843eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 459*5589cdf3S猫头猫 const meta = MediaExtra.get(originalMusicItem); 46043eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 461927dbe93S猫头猫 if (meta && meta.associatedLrc) { 46243eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 46343eb30bfS猫头猫 } else { 46443eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 465927dbe93S猫头猫 } 46643eb30bfS猫头猫 46743eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4687a91f04fS猫头猫 musicItem, 46943eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 47043eb30bfS猫头猫 47143eb30bfS猫头猫 /** 原始歌词文本 */ 4727e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4737e883dbbS猫头猫 let translation: string | null = null; 47443eb30bfS猫头猫 47543eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 47643eb30bfS猫头猫 if (musicItemCache?.lyric) { 47743eb30bfS猫头猫 // 缓存的远程结果 47843eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 47943eb30bfS猫头猫 musicItemCache.lyric || null; 48043eb30bfS猫头猫 // 缓存的本地结果 48143eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 48243eb30bfS猫头猫 musicItemCache.$localLyric || null; 48343eb30bfS猫头猫 4847e883dbbS猫头猫 // 优先用缓存的结果 4857e883dbbS猫头猫 if (cacheLyric.rawLrc || cacheLyric.translation) { 48643eb30bfS猫头猫 return { 4877e883dbbS猫头猫 rawLrc: cacheLyric.rawLrc, 4887e883dbbS猫头猫 translation: cacheLyric.translation, 48943eb30bfS猫头猫 }; 49043eb30bfS猫头猫 } 49143eb30bfS猫头猫 4927e883dbbS猫头猫 // 本地其实是缓存的路径 4937e883dbbS猫头猫 if (localLyric) { 4947e883dbbS猫头猫 let needRefetch = false; 4957e883dbbS猫头猫 if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) { 4967e883dbbS猫头猫 rawLrc = await readFile(localLyric.rawLrc, 'utf8'); 4977e883dbbS猫头猫 } else if (localLyric.rawLrc) { 4987e883dbbS猫头猫 needRefetch = true; 4997e883dbbS猫头猫 } 5007e883dbbS猫头猫 if ( 5017e883dbbS猫头猫 localLyric.translation && 5027e883dbbS猫头猫 (await exists(localLyric.translation)) 5037e883dbbS猫头猫 ) { 5047e883dbbS猫头猫 translation = await readFile( 5057e883dbbS猫头猫 localLyric.translation, 5067e883dbbS猫头猫 'utf8', 5077e883dbbS猫头猫 ); 5087e883dbbS猫头猫 } else if (localLyric.translation) { 5097e883dbbS猫头猫 needRefetch = true; 51043eb30bfS猫头猫 } 51143eb30bfS猫头猫 5127e883dbbS猫头猫 if (!needRefetch && (rawLrc || translation)) { 51343eb30bfS猫头猫 return { 5147e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5157e883dbbS猫头猫 translation: translation || undefined, 51643eb30bfS猫头猫 }; 51743eb30bfS猫头猫 } 51843eb30bfS猫头猫 } 51943eb30bfS猫头猫 } 52043eb30bfS猫头猫 52143eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 52243eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 52343eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 52443eb30bfS猫头猫 lrcSource = 52543eb30bfS猫头猫 (await this.plugin.instance 5267e883dbbS猫头猫 ?.getLyric?.(resetMediaItem(musicItem, undefined, true)) 52743eb30bfS猫头猫 ?.catch(() => null)) || null; 52843eb30bfS猫头猫 } else { 52943eb30bfS猫头猫 lrcSource = 53043eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 53143eb30bfS猫头猫 ?.instance?.getLyric?.( 53243eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 53343eb30bfS猫头猫 ) 53443eb30bfS猫头猫 ?.catch(() => null)) || null; 53543eb30bfS猫头猫 } 53643eb30bfS猫头猫 53743eb30bfS猫头猫 if (lrcSource) { 5387e883dbbS猫头猫 rawLrc = lrcSource?.rawLrc || rawLrc; 5397e883dbbS猫头猫 translation = lrcSource?.translation || null; 54043eb30bfS猫头猫 5417e883dbbS猫头猫 const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc; 54243eb30bfS猫头猫 5437e883dbbS猫头猫 // 本地的文件名 5447e883dbbS猫头猫 let filename: string | undefined = `${ 5457e883dbbS猫头猫 pathConst.lrcCachePath 5467e883dbbS猫头猫 }${nanoid()}.lrc`; 5477e883dbbS猫头猫 let filenameTrans: string | undefined = `${ 5487e883dbbS猫头猫 pathConst.lrcCachePath 5497e883dbbS猫头猫 }${nanoid()}.lrc`; 55043eb30bfS猫头猫 5517e883dbbS猫头猫 // 旧版本兼容 5527e883dbbS猫头猫 if (!(rawLrc || translation)) { 5537e883dbbS猫头猫 if (deprecatedLrcUrl) { 55443eb30bfS猫头猫 rawLrc = ( 5557e883dbbS猫头猫 await axios 5567e883dbbS猫头猫 .get(deprecatedLrcUrl, {timeout: 3000}) 5577e883dbbS猫头猫 .catch(() => null) 55843eb30bfS猫头猫 )?.data; 5597e883dbbS猫头猫 } else if (musicItem.rawLrc) { 5607e883dbbS猫头猫 rawLrc = musicItem.rawLrc; 5617e883dbbS猫头猫 } 56243eb30bfS猫头猫 } 56343eb30bfS猫头猫 56443eb30bfS猫头猫 if (rawLrc) { 56543eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 56643eb30bfS猫头猫 } else { 5677e883dbbS猫头猫 filename = undefined; 5687e883dbbS猫头猫 } 5697e883dbbS猫头猫 if (translation) { 5707e883dbbS猫头猫 await writeFile(filenameTrans, translation, 'utf8'); 5717e883dbbS猫头猫 } else { 5727e883dbbS猫头猫 filenameTrans = undefined; 5737a91f04fS猫头猫 } 5747a91f04fS猫头猫 5757e883dbbS猫头猫 if (rawLrc || translation) { 5767e883dbbS猫头猫 MediaCache.setMediaCache( 5777e883dbbS猫头猫 produce(musicItemCache || musicItem, draft => { 5787e883dbbS猫头猫 musicItemCache?.$localLyric?.rawLrc; 5797e883dbbS猫头猫 objectPath.set(draft, '$localLyric.rawLrc', filename); 58043eb30bfS猫头猫 objectPath.set( 58143eb30bfS猫头猫 draft, 5827e883dbbS猫头猫 '$localLyric.translation', 5837e883dbbS猫头猫 filenameTrans, 58443eb30bfS猫头猫 ); 58543eb30bfS猫头猫 return draft; 58643eb30bfS猫头猫 }), 58743eb30bfS猫头猫 ); 588927dbe93S猫头猫 return { 5897e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5907e883dbbS猫头猫 translation: translation || undefined, 591927dbe93S猫头猫 }; 592927dbe93S猫头猫 } 593927dbe93S猫头猫 } 59443eb30bfS猫头猫 5953a6f67b1S猫头猫 // 6. 如果是本地文件 59643eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 59743eb30bfS猫头猫 if ( 59843eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 59943eb30bfS猫头猫 isDownloaded 60043eb30bfS猫头猫 ) { 6017e883dbbS猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 60243eb30bfS猫头猫 60343eb30bfS猫头猫 console.log('本地文件歌词'); 60443eb30bfS猫头猫 6053a6f67b1S猫头猫 if (res) { 6063a6f67b1S猫头猫 return res; 6073a6f67b1S猫头猫 } 6083a6f67b1S猫头猫 } 609ea6d708fS猫头猫 devLog('warn', '无歌词'); 610927dbe93S猫头猫 611927dbe93S猫头猫 return null; 612927dbe93S猫头猫 } 613927dbe93S猫头猫 614927dbe93S猫头猫 /** 获取歌词文本 */ 615927dbe93S猫头猫 async getLyricText( 616927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 617927dbe93S猫头猫 ): Promise<string | undefined> { 6187e883dbbS猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 619927dbe93S猫头猫 } 620927dbe93S猫头猫 621927dbe93S猫头猫 /** 获取专辑信息 */ 622927dbe93S猫头猫 async getAlbumInfo( 623927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 624f9afcc0dS猫头猫 page: number = 1, 625f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 626927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 627f9afcc0dS猫头猫 return { 628f9afcc0dS猫头猫 albumItem, 629f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 630f2a4767cS猫头猫 resetMediaItem, 631f2a4767cS猫头猫 this.plugin.name, 632f2a4767cS猫头猫 true, 633f2a4767cS猫头猫 ), 634f9afcc0dS猫头猫 isEnd: true, 635f9afcc0dS猫头猫 }; 636927dbe93S猫头猫 } 637927dbe93S猫头猫 try { 638927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 639927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 640f9afcc0dS猫头猫 page, 641927dbe93S猫头猫 ); 6425276aef9S猫头猫 if (!result) { 6435276aef9S猫头猫 throw new Error(); 6445276aef9S猫头猫 } 645927dbe93S猫头猫 result?.musicList?.forEach(_ => { 646927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 64796744680S猫头猫 _.album = albumItem.title; 648927dbe93S猫头猫 }); 6495276aef9S猫头猫 650f9afcc0dS猫头猫 if (page <= 1) { 651f9afcc0dS猫头猫 // 合并信息 652f9afcc0dS猫头猫 return { 653f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 654f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 655f9afcc0dS猫头猫 musicList: result.musicList, 656f9afcc0dS猫头猫 }; 657f9afcc0dS猫头猫 } else { 658f9afcc0dS猫头猫 return { 659f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 660f9afcc0dS猫头猫 musicList: result.musicList, 661f9afcc0dS猫头猫 }; 662f9afcc0dS猫头猫 } 6634394410dS猫头猫 } catch (e: any) { 6644394410dS猫头猫 trace('获取专辑信息失败', e?.message); 665ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 666ea6d708fS猫头猫 667f9afcc0dS猫头猫 return null; 668927dbe93S猫头猫 } 669927dbe93S猫头猫 } 670927dbe93S猫头猫 6715830c002S猫头猫 /** 获取歌单信息 */ 6725830c002S猫头猫 async getMusicSheetInfo( 6735830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 6745830c002S猫头猫 page: number = 1, 6755830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 6765281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 6775830c002S猫头猫 return { 6785830c002S猫头猫 sheetItem, 6795830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 6805830c002S猫头猫 isEnd: true, 6815830c002S猫头猫 }; 6825830c002S猫头猫 } 6835830c002S猫头猫 try { 6845830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 6855830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 6865830c002S猫头猫 page, 6875830c002S猫头猫 ); 6885830c002S猫头猫 if (!result) { 6895830c002S猫头猫 throw new Error(); 6905830c002S猫头猫 } 6915830c002S猫头猫 result?.musicList?.forEach(_ => { 6925830c002S猫头猫 resetMediaItem(_, this.plugin.name); 6935830c002S猫头猫 }); 6945830c002S猫头猫 6955830c002S猫头猫 if (page <= 1) { 6965830c002S猫头猫 // 合并信息 6975830c002S猫头猫 return { 6985830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 6995830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7005830c002S猫头猫 musicList: result.musicList, 7015830c002S猫头猫 }; 7025830c002S猫头猫 } else { 7035830c002S猫头猫 return { 7045830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7055830c002S猫头猫 musicList: result.musicList, 7065830c002S猫头猫 }; 7075830c002S猫头猫 } 7085830c002S猫头猫 } catch (e: any) { 7095830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7105830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7115830c002S猫头猫 7125830c002S猫头猫 return null; 7135830c002S猫头猫 } 7145830c002S猫头猫 } 7155830c002S猫头猫 716927dbe93S猫头猫 /** 查询作者信息 */ 717efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 718927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 719927dbe93S猫头猫 page: number, 720927dbe93S猫头猫 type: T, 721927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 722efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 723927dbe93S猫头猫 return { 724927dbe93S猫头猫 isEnd: true, 725927dbe93S猫头猫 data: [], 726927dbe93S猫头猫 }; 727927dbe93S猫头猫 } 728927dbe93S猫头猫 try { 729efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 730927dbe93S猫头猫 artistItem, 731927dbe93S猫头猫 page, 732927dbe93S猫头猫 type, 733927dbe93S猫头猫 ); 734927dbe93S猫头猫 if (!result.data) { 735927dbe93S猫头猫 return { 736927dbe93S猫头猫 isEnd: true, 737927dbe93S猫头猫 data: [], 738927dbe93S猫头猫 }; 739927dbe93S猫头猫 } 740927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 741927dbe93S猫头猫 return { 742927dbe93S猫头猫 isEnd: result.isEnd ?? true, 743927dbe93S猫头猫 data: result.data, 744927dbe93S猫头猫 }; 7454394410dS猫头猫 } catch (e: any) { 7464394410dS猫头猫 trace('查询作者信息失败', e?.message); 747ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 748ea6d708fS猫头猫 749927dbe93S猫头猫 throw e; 750927dbe93S猫头猫 } 751927dbe93S猫头猫 } 75208380090S猫头猫 75308380090S猫头猫 /** 导入歌单 */ 75408380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 75508380090S猫头猫 try { 75608380090S猫头猫 const result = 75708380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 75808380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 75908380090S猫头猫 return result; 760ea6d708fS猫头猫 } catch (e: any) { 7610e4173cdS猫头猫 console.log(e); 762ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 763ea6d708fS猫头猫 76408380090S猫头猫 return []; 76508380090S猫头猫 } 76608380090S猫头猫 } 7674d9d3c4cS猫头猫 /** 导入单曲 */ 7684d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 7694d9d3c4cS猫头猫 try { 7704d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 7714d9d3c4cS猫头猫 urlLike, 7724d9d3c4cS猫头猫 ); 7734d9d3c4cS猫头猫 if (!result) { 7744d9d3c4cS猫头猫 throw new Error(); 7754d9d3c4cS猫头猫 } 7764d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 7774d9d3c4cS猫头猫 return result; 778ea6d708fS猫头猫 } catch (e: any) { 779ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 780ea6d708fS猫头猫 7814d9d3c4cS猫头猫 return null; 7824d9d3c4cS猫头猫 } 7834d9d3c4cS猫头猫 } 784d52aa40eS猫头猫 /** 获取榜单 */ 78592b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 786d52aa40eS猫头猫 try { 787d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 788d52aa40eS猫头猫 if (!result) { 789d52aa40eS猫头猫 throw new Error(); 790d52aa40eS猫头猫 } 791d52aa40eS猫头猫 return result; 792d52aa40eS猫头猫 } catch (e: any) { 793d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 794d52aa40eS猫头猫 return []; 795d52aa40eS猫头猫 } 796d52aa40eS猫头猫 } 797d52aa40eS猫头猫 /** 获取榜单详情 */ 798d52aa40eS猫头猫 async getTopListDetail( 79992b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 800956ee1b7S猫头猫 page: number, 801956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 802d52aa40eS猫头猫 try { 803d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 804d52aa40eS猫头猫 topListItem, 805956ee1b7S猫头猫 page, 806d52aa40eS猫头猫 ); 807d52aa40eS猫头猫 if (!result) { 808d52aa40eS猫头猫 throw new Error(); 809d52aa40eS猫头猫 } 810d384662fS猫头猫 if (result.musicList) { 811d384662fS猫头猫 result.musicList.forEach(_ => 812d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 813d384662fS猫头猫 ); 814d384662fS猫头猫 } 815956ee1b7S猫头猫 if (result.isEnd !== false) { 816956ee1b7S猫头猫 result.isEnd = true; 817956ee1b7S猫头猫 } 818d52aa40eS猫头猫 return result; 819d52aa40eS猫头猫 } catch (e: any) { 820d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 821d52aa40eS猫头猫 return { 822956ee1b7S猫头猫 isEnd: true, 823956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 824d52aa40eS猫头猫 musicList: [], 825d52aa40eS猫头猫 }; 826d52aa40eS猫头猫 } 827d52aa40eS猫头猫 } 828ceb900cdS猫头猫 8295830c002S猫头猫 /** 获取推荐歌单的tag */ 830ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 831ceb900cdS猫头猫 try { 832ceb900cdS猫头猫 const result = 833ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 834ceb900cdS猫头猫 if (!result) { 835ceb900cdS猫头猫 throw new Error(); 836ceb900cdS猫头猫 } 837ceb900cdS猫头猫 return result; 838ceb900cdS猫头猫 } catch (e: any) { 839ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 840ceb900cdS猫头猫 return { 841ceb900cdS猫头猫 data: [], 842ceb900cdS猫头猫 }; 843ceb900cdS猫头猫 } 844ceb900cdS猫头猫 } 8455830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 846ceb900cdS猫头猫 async getRecommendSheetsByTag( 847ceb900cdS猫头猫 tagItem: ICommon.IUnique, 848ceb900cdS猫头猫 page?: number, 849ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 850ceb900cdS猫头猫 try { 851ceb900cdS猫头猫 const result = 852ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 853ceb900cdS猫头猫 tagItem, 854ceb900cdS猫头猫 page ?? 1, 855ceb900cdS猫头猫 ); 856ceb900cdS猫头猫 if (!result) { 857ceb900cdS猫头猫 throw new Error(); 858ceb900cdS猫头猫 } 859ceb900cdS猫头猫 if (result.isEnd !== false) { 860ceb900cdS猫头猫 result.isEnd = true; 861ceb900cdS猫头猫 } 862ceb900cdS猫头猫 if (!result.data) { 863ceb900cdS猫头猫 result.data = []; 864ceb900cdS猫头猫 } 865ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 866ceb900cdS猫头猫 867ceb900cdS猫头猫 return result; 868ceb900cdS猫头猫 } catch (e: any) { 869ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 870ceb900cdS猫头猫 return { 871ceb900cdS猫头猫 isEnd: true, 872ceb900cdS猫头猫 data: [], 873ceb900cdS猫头猫 }; 874ceb900cdS猫头猫 } 875ceb900cdS猫头猫 } 876927dbe93S猫头猫} 877d5bfeb7eS猫头猫//#endregion 8781a5528a0S猫头猫 879927dbe93S猫头猫let plugins: Array<Plugin> = []; 880927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 88174d0cf81S猫头猫 882d5bfeb7eS猫头猫//#region 本地音乐插件 88374d0cf81S猫头猫/** 本地插件 */ 88474d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 8850e4173cdS猫头猫 return { 886d5bfeb7eS猫头猫 platform: localPluginPlatform, 88774d0cf81S猫头猫 _path: '', 88874d0cf81S猫头猫 async getMusicInfo(musicBase) { 88974d0cf81S猫头猫 const localPath = getInternalData<string>( 89074d0cf81S猫头猫 musicBase, 89174d0cf81S猫头猫 InternalDataType.LOCALPATH, 8920e4173cdS猫头猫 ); 89374d0cf81S猫头猫 if (localPath) { 89474d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 89574d0cf81S猫头猫 return { 89674d0cf81S猫头猫 artwork: coverImg, 89774d0cf81S猫头猫 }; 89874d0cf81S猫头猫 } 89974d0cf81S猫头猫 return null; 90074d0cf81S猫头猫 }, 9017993f90eS猫头猫 async getLyric(musicBase) { 9027993f90eS猫头猫 const localPath = getInternalData<string>( 9037993f90eS猫头猫 musicBase, 9047993f90eS猫头猫 InternalDataType.LOCALPATH, 9057993f90eS猫头猫 ); 9063a6f67b1S猫头猫 let rawLrc: string | null = null; 9077993f90eS猫头猫 if (localPath) { 9083a6f67b1S猫头猫 // 读取内嵌歌词 9093a6f67b1S猫头猫 try { 9103a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 9113a6f67b1S猫头猫 } catch (e) { 912a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 9137993f90eS猫头猫 } 9143a6f67b1S猫头猫 if (!rawLrc) { 9153a6f67b1S猫头猫 // 读取配置歌词 9163a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 9173a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 9183a6f67b1S猫头猫 9193a6f67b1S猫头猫 try { 9203a6f67b1S猫头猫 if (await exists(lrcPath)) { 9213a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 9223a6f67b1S猫头猫 } 9233a6f67b1S猫头猫 } catch {} 9243a6f67b1S猫头猫 } 9253a6f67b1S猫头猫 } 9263a6f67b1S猫头猫 9273a6f67b1S猫头猫 return rawLrc 9283a6f67b1S猫头猫 ? { 9293a6f67b1S猫头猫 rawLrc, 9303a6f67b1S猫头猫 } 9313a6f67b1S猫头猫 : null; 9327993f90eS猫头猫 }, 933a84a85c5S猫头猫 async importMusicItem(urlLike) { 934a84a85c5S猫头猫 let meta: any = {}; 935a84a85c5S猫头猫 try { 936a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 937a84a85c5S猫头猫 } catch {} 938a84a85c5S猫头猫 const id = await FileSystem.hash(urlLike, 'MD5'); 939a84a85c5S猫头猫 return { 940a84a85c5S猫头猫 id: id, 941a84a85c5S猫头猫 platform: '本地', 942a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 943a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 944a84a85c5S猫头猫 duration: parseInt(meta?.duration ?? '0') / 1000, 945a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 946a84a85c5S猫头猫 artwork: '', 947a84a85c5S猫头猫 [internalSerializeKey]: { 948a84a85c5S猫头猫 localPath: urlLike, 949a84a85c5S猫头猫 }, 950a84a85c5S猫头猫 }; 951a84a85c5S猫头猫 }, 95211908939S猫头猫 async getMediaSource(musicItem, quality) { 95311908939S猫头猫 if (quality === 'standard') { 95411908939S猫头猫 return { 95511908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 95611908939S猫头猫 }; 95711908939S猫头猫 } 95811908939S猫头猫 return null; 95911908939S猫头猫 }, 96074d0cf81S猫头猫 }; 96174d0cf81S猫头猫}, ''); 9627993f90eS猫头猫localFilePlugin.hash = localPluginHash; 963927dbe93S猫头猫 964d5bfeb7eS猫头猫//#endregion 965d5bfeb7eS猫头猫 966927dbe93S猫头猫async function setup() { 967927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 968927dbe93S猫头猫 try { 969927dbe93S猫头猫 // 加载插件 970927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 971927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 972927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 9731e263108S猫头猫 trace('初始化插件', _pluginUrl); 9741e263108S猫头猫 if ( 9751e263108S猫头猫 _pluginUrl.isFile() && 9761e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 9771e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 9781e263108S猫头猫 ) { 979927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 980927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 9814060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 9824060c00aS猫头猫 p => p.hash === plugin.hash, 9834060c00aS猫头猫 ); 984927dbe93S猫头猫 if (_pluginIndex !== -1) { 985927dbe93S猫头猫 // 重复插件,直接忽略 9860c266394S猫头猫 continue; 987927dbe93S猫头猫 } 988927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 989927dbe93S猫头猫 } 990927dbe93S猫头猫 } 991927dbe93S猫头猫 992927dbe93S猫头猫 plugins = _plugins; 993e08d37a3S猫头猫 /** 初始化meta信息 */ 994c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 995c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 996c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 997c2b3a262S猫头猫 for (let plugin of plugins) { 998c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 999c2b3a262S猫头猫 plugin.state = 'disabled'; 1000c2b3a262S猫头猫 } 1001c2b3a262S猫头猫 } 1002c2b3a262S猫头猫 pluginStateMapper.notify(); 1003927dbe93S猫头猫 } catch (e: any) { 10044060c00aS猫头猫 ToastAndroid.show( 10054060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 10064060c00aS猫头猫 ToastAndroid.LONG, 10074060c00aS猫头猫 ); 10081a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1009927dbe93S猫头猫 throw e; 1010927dbe93S猫头猫 } 1011927dbe93S猫头猫} 1012927dbe93S猫头猫 1013e36e2599S猫头猫interface IInstallPluginConfig { 1014e36e2599S猫头猫 notCheckVersion?: boolean; 1015e36e2599S猫头猫} 1016e36e2599S猫头猫 1017927dbe93S猫头猫// 安装插件 1018e36e2599S猫头猫async function installPlugin( 1019e36e2599S猫头猫 pluginPath: string, 1020e36e2599S猫头猫 config?: IInstallPluginConfig, 1021e36e2599S猫头猫) { 102222c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1023927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1024e36e2599S猫头猫 1025e36e2599S猫头猫 if (funcCode) { 1026927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1027927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1028927dbe93S猫头猫 if (_pluginIndex !== -1) { 1029e36e2599S猫头猫 // 静默忽略 1030e36e2599S猫头猫 return plugin; 1031927dbe93S猫头猫 } 1032e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1033e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1034e36e2599S猫头猫 if ( 1035e36e2599S猫头猫 compare( 1036e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1037e36e2599S猫头猫 plugin.instance.version ?? '', 1038e36e2599S猫头猫 '>', 1039e36e2599S猫头猫 ) 1040e36e2599S猫头猫 ) { 1041e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1042e36e2599S猫头猫 } 1043e36e2599S猫头猫 } 1044e36e2599S猫头猫 1045927dbe93S猫头猫 if (plugin.hash !== '') { 1046927dbe93S猫头猫 const fn = nanoid(); 1047e36e2599S猫头猫 if (oldVersionPlugin) { 1048e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1049e36e2599S猫头猫 try { 1050e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1051e36e2599S猫头猫 } catch {} 1052e36e2599S猫头猫 } 1053927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1054927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1055927dbe93S猫头猫 plugin.path = _pluginPath; 1056927dbe93S猫头猫 plugins = plugins.concat(plugin); 1057927dbe93S猫头猫 pluginStateMapper.notify(); 1058a84a85c5S猫头猫 return plugin; 1059927dbe93S猫头猫 } 1060e36e2599S猫头猫 throw new Error('插件无法解析!'); 1061927dbe93S猫头猫 } 1062e36e2599S猫头猫 throw new Error('插件无法识别!'); 1063c2b3a262S猫头猫} 1064c2b3a262S猫头猫 1065ee781061S猫头猫const reqHeaders = { 1066ee781061S猫头猫 'Cache-Control': 'no-cache', 1067ee781061S猫头猫 Pragma: 'no-cache', 1068ee781061S猫头猫 Expires: '0', 1069ee781061S猫头猫}; 1070ee781061S猫头猫 1071c2b3a262S猫头猫async function installPluginFromUrl( 1072c2b3a262S猫头猫 url: string, 1073c2b3a262S猫头猫 config?: IInstallPluginConfig, 1074c2b3a262S猫头猫) { 107558992c6bS猫头猫 try { 1076ee781061S猫头猫 const funcCode = ( 1077ee781061S猫头猫 await axios.get(url, { 1078ee781061S猫头猫 headers: reqHeaders, 1079ee781061S猫头猫 }) 1080ee781061S猫头猫 ).data; 108158992c6bS猫头猫 if (funcCode) { 108258992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 108358992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 108458992c6bS猫头猫 if (_pluginIndex !== -1) { 10858b7ddca8S猫头猫 // 静默忽略 10868b7ddca8S猫头猫 return; 108758992c6bS猫头猫 } 108825c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1089c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 109025c1bd29S猫头猫 if ( 109125c1bd29S猫头猫 compare( 109225c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 109325c1bd29S猫头猫 plugin.instance.version ?? '', 109425c1bd29S猫头猫 '>', 109525c1bd29S猫头猫 ) 109625c1bd29S猫头猫 ) { 109725c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 109825c1bd29S猫头猫 } 109925c1bd29S猫头猫 } 110025c1bd29S猫头猫 110158992c6bS猫头猫 if (plugin.hash !== '') { 110258992c6bS猫头猫 const fn = nanoid(); 110358992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 110458992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 110558992c6bS猫头猫 plugin.path = _pluginPath; 110658992c6bS猫头猫 plugins = plugins.concat(plugin); 110725c1bd29S猫头猫 if (oldVersionPlugin) { 110825c1bd29S猫头猫 plugins = plugins.filter( 110925c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 111025c1bd29S猫头猫 ); 111125c1bd29S猫头猫 try { 111225c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 111325c1bd29S猫头猫 } catch {} 111425c1bd29S猫头猫 } 111558992c6bS猫头猫 pluginStateMapper.notify(); 111658992c6bS猫头猫 return; 111758992c6bS猫头猫 } 111874acbfc0S猫头猫 throw new Error('插件无法解析!'); 111958992c6bS猫头猫 } 112025c1bd29S猫头猫 } catch (e: any) { 1121ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 112258992c6bS猫头猫 errorLog('URL安装插件失败', e); 112325c1bd29S猫头猫 throw new Error(e?.message ?? ''); 112458992c6bS猫头猫 } 112558992c6bS猫头猫} 112658992c6bS猫头猫 1127927dbe93S猫头猫/** 卸载插件 */ 1128927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1129927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1130927dbe93S猫头猫 if (targetIndex !== -1) { 1131927dbe93S猫头猫 try { 113224e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1133927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1134927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1135927dbe93S猫头猫 pluginStateMapper.notify(); 113643eb30bfS猫头猫 // 防止其他重名 113724e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 1138*5589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 113924e5e74aS猫头猫 } 1140927dbe93S猫头猫 } catch {} 1141927dbe93S猫头猫 } 1142927dbe93S猫头猫} 1143927dbe93S猫头猫 114408882a77S猫头猫async function uninstallAllPlugins() { 114508882a77S猫头猫 await Promise.all( 114608882a77S猫头猫 plugins.map(async plugin => { 114708882a77S猫头猫 try { 114808882a77S猫头猫 const pluginName = plugin.name; 114908882a77S猫头猫 await unlink(plugin.path); 1150*5589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 115108882a77S猫头猫 } catch (e) {} 115208882a77S猫头猫 }), 115308882a77S猫头猫 ); 115408882a77S猫头猫 plugins = []; 115508882a77S猫头猫 pluginStateMapper.notify(); 1156e08d37a3S猫头猫 1157e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1158e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1159e08d37a3S猫头猫 .then(fns => { 1160e08d37a3S猫头猫 fns.forEach(fn => { 1161e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1162e08d37a3S猫头猫 }); 1163e08d37a3S猫头猫 }) 1164e08d37a3S猫头猫 .catch(emptyFunction); 116508882a77S猫头猫} 116608882a77S猫头猫 116725c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 116825c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 116925c1bd29S猫头猫 if (!updateUrl) { 117025c1bd29S猫头猫 throw new Error('没有更新源'); 117125c1bd29S猫头猫 } 117225c1bd29S猫头猫 try { 117325c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 117425c1bd29S猫头猫 } catch (e: any) { 117525c1bd29S猫头猫 if (e.message === '插件已安装') { 117625c1bd29S猫头猫 throw new Error('当前已是最新版本'); 117725c1bd29S猫头猫 } else { 117825c1bd29S猫头猫 throw e; 117925c1bd29S猫头猫 } 118025c1bd29S猫头猫 } 118125c1bd29S猫头猫} 118225c1bd29S猫头猫 1183927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 11842c595535S猫头猫 return getByName(mediaItem?.platform); 1185927dbe93S猫头猫} 1186927dbe93S猫头猫 1187927dbe93S猫头猫function getByHash(hash: string) { 11887993f90eS猫头猫 return hash === localPluginHash 11897993f90eS猫头猫 ? localFilePlugin 11907993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1191927dbe93S猫头猫} 1192927dbe93S猫头猫 1193927dbe93S猫头猫function getByName(name: string) { 11947993f90eS猫头猫 return name === localPluginPlatform 11950e4173cdS猫头猫 ? localFilePlugin 11960e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1197927dbe93S猫头猫} 1198927dbe93S猫头猫 1199927dbe93S猫头猫function getValidPlugins() { 1200927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1201927dbe93S猫头猫} 1202927dbe93S猫头猫 12032b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 12042b80a429S猫头猫 return plugins.filter( 12052b80a429S猫头猫 _ => 12062b80a429S猫头猫 _.state === 'enabled' && 12072b80a429S猫头猫 _.instance.search && 120839ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 120939ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 12102b80a429S猫头猫 : true), 12112b80a429S猫头猫 ); 1212efb9da24S猫头猫} 1213efb9da24S猫头猫 12142b80a429S猫头猫function getSortedSearchablePlugins( 12152b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 12162b80a429S猫头猫) { 12172b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1218e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1219e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1220e08d37a3S猫头猫 0 1221e08d37a3S猫头猫 ? -1 1222e08d37a3S猫头猫 : 1, 1223e08d37a3S猫头猫 ); 1224e08d37a3S猫头猫} 1225e08d37a3S猫头猫 122615feccc1S猫头猫function getTopListsablePlugins() { 122715feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 122815feccc1S猫头猫} 122915feccc1S猫头猫 123015feccc1S猫头猫function getSortedTopListsablePlugins() { 123115feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 123215feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 123315feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 123415feccc1S猫头猫 0 123515feccc1S猫头猫 ? -1 123615feccc1S猫头猫 : 1, 123715feccc1S猫头猫 ); 123815feccc1S猫头猫} 123915feccc1S猫头猫 1240ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1241ceb900cdS猫头猫 return plugins.filter( 1242ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1243ceb900cdS猫头猫 ); 1244ceb900cdS猫头猫} 1245ceb900cdS猫头猫 1246ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1247ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1248ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1249ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1250ceb900cdS猫头猫 0 1251ceb900cdS猫头猫 ? -1 1252ceb900cdS猫头猫 : 1, 1253ceb900cdS猫头猫 ); 1254ceb900cdS猫头猫} 1255ceb900cdS猫头猫 1256e08d37a3S猫头猫function useSortedPlugins() { 1257e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1258e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1259e08d37a3S猫头猫 126034588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 126134588741S猫头猫 [..._plugins].sort((a, b) => 1262e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1263e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1264e08d37a3S猫头猫 0 1265e08d37a3S猫头猫 ? -1 1266e08d37a3S猫头猫 : 1, 126734588741S猫头猫 ), 1268e08d37a3S猫头猫 ); 126934588741S猫头猫 127034588741S猫头猫 useEffect(() => { 1271d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 127234588741S猫头猫 setSortedPlugins( 127334588741S猫头猫 [..._plugins].sort((a, b) => 127434588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 127534588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 127634588741S猫头猫 0 127734588741S猫头猫 ? -1 127834588741S猫头猫 : 1, 127934588741S猫头猫 ), 128034588741S猫头猫 ); 1281d4cd40d8S猫头猫 }); 128234588741S猫头猫 }, [_plugins, _pluginMetaAll]); 128334588741S猫头猫 128434588741S猫头猫 return sortedPlugins; 1285e08d37a3S猫头猫} 1286e08d37a3S猫头猫 1287c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1288c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1289c2b3a262S猫头猫 if (target) { 1290c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1291c2b3a262S猫头猫 plugins = [...plugins]; 1292c2b3a262S猫头猫 pluginStateMapper.notify(); 1293c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1294c2b3a262S猫头猫 } 1295c2b3a262S猫头猫} 1296c2b3a262S猫头猫 1297927dbe93S猫头猫const PluginManager = { 1298927dbe93S猫头猫 setup, 1299927dbe93S猫头猫 installPlugin, 130058992c6bS猫头猫 installPluginFromUrl, 130125c1bd29S猫头猫 updatePlugin, 1302927dbe93S猫头猫 uninstallPlugin, 1303927dbe93S猫头猫 getByMedia, 1304927dbe93S猫头猫 getByHash, 1305927dbe93S猫头猫 getByName, 1306927dbe93S猫头猫 getValidPlugins, 1307efb9da24S猫头猫 getSearchablePlugins, 1308e08d37a3S猫头猫 getSortedSearchablePlugins, 130915feccc1S猫头猫 getTopListsablePlugins, 1310ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 131115feccc1S猫头猫 getSortedTopListsablePlugins, 13125276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1313e08d37a3S猫头猫 useSortedPlugins, 131408882a77S猫头猫 uninstallAllPlugins, 1315c2b3a262S猫头猫 setPluginEnabled, 13165276aef9S猫头猫}; 1317927dbe93S猫头猫 1318927dbe93S猫头猫export default PluginManager; 1319