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'; 2043eb30bfS猫头猫import MediaMeta 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'; 5043eb30bfS猫头猫import produce from 'immer'; 5143eb30bfS猫头猫import MediaExtra from './mediaExtra'; 5243eb30bfS猫头猫import objectPath from 'object-path'; 53927dbe93S猫头猫 5461aca335S猫头猫axios.defaults.timeout = 2000; 55927dbe93S猫头猫 56927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 57927dbe93S猫头猫 58cfa0fc07S猫头猫export enum PluginStateCode { 59927dbe93S猫头猫 /** 版本不匹配 */ 60927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 61927dbe93S猫头猫 /** 无法解析 */ 62927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 63927dbe93S猫头猫} 64927dbe93S猫头猫 659c34d637S猫头猫const packages: Record<string, any> = { 669c34d637S猫头猫 cheerio, 679c34d637S猫头猫 'crypto-js': CryptoJs, 689c34d637S猫头猫 axios, 699c34d637S猫头猫 dayjs, 709c34d637S猫头猫 'big-integer': bigInt, 719c34d637S猫头猫 qs, 729c34d637S猫头猫 he, 733b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 7428ccceb7S猫头猫 webdav, 759c34d637S猫头猫}; 769c34d637S猫头猫 77b43683eaS猫头猫const _require = (packageName: string) => { 78b43683eaS猫头猫 let pkg = packages[packageName]; 79b43683eaS猫头猫 pkg.default = pkg; 80b43683eaS猫头猫 return pkg; 81b43683eaS猫头猫}; 829c34d637S猫头猫 8353f8cd8eS猫头猫const _consoleBind = function ( 8453f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 8553f8cd8eS猫头猫 ...args: any 8653f8cd8eS猫头猫) { 8753f8cd8eS猫头猫 const fn = console[method]; 8853f8cd8eS猫头猫 if (fn) { 8953f8cd8eS猫头猫 fn(...args); 9053f8cd8eS猫头猫 devLog(method, ...args); 9153f8cd8eS猫头猫 } 9253f8cd8eS猫头猫}; 9353f8cd8eS猫头猫 9453f8cd8eS猫头猫const _console = { 9553f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9653f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9753f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9853f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9953f8cd8eS猫头猫}; 10053f8cd8eS猫头猫 101a7b42a4cS猫头猫function formatAuthUrl(url: string) { 102a7b42a4cS猫头猫 const urlObj = new URL(url); 103a7b42a4cS猫头猫 104a7b42a4cS猫头猫 try { 105a7b42a4cS猫头猫 if (urlObj.username && urlObj.password) { 106a7b42a4cS猫头猫 const auth = `Basic ${Base64.btoa( 107a7b42a4cS猫头猫 `${decodeURIComponent(urlObj.username)}:${decodeURIComponent( 108a7b42a4cS猫头猫 urlObj.password, 109a7b42a4cS猫头猫 )}`, 110a7b42a4cS猫头猫 )}`; 111a7b42a4cS猫头猫 urlObj.username = ''; 112a7b42a4cS猫头猫 urlObj.password = ''; 113a7b42a4cS猫头猫 114a7b42a4cS猫头猫 return { 115a7b42a4cS猫头猫 url: urlObj.toString(), 116a7b42a4cS猫头猫 auth, 117a7b42a4cS猫头猫 }; 118a7b42a4cS猫头猫 } 119a7b42a4cS猫头猫 } catch (e) { 120a7b42a4cS猫头猫 return { 121a7b42a4cS猫头猫 url, 122a7b42a4cS猫头猫 }; 123a7b42a4cS猫头猫 } 124a7b42a4cS猫头猫 return { 125a7b42a4cS猫头猫 url, 126a7b42a4cS猫头猫 }; 127a7b42a4cS猫头猫} 128a7b42a4cS猫头猫 129d5bfeb7eS猫头猫//#region 插件类 130927dbe93S猫头猫export class Plugin { 131927dbe93S猫头猫 /** 插件名 */ 132927dbe93S猫头猫 public name: string; 133927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 134927dbe93S猫头猫 public hash: string; 135927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 136927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 137927dbe93S猫头猫 /** 插件状态信息 */ 138927dbe93S猫头猫 public stateCode?: PluginStateCode; 139927dbe93S猫头猫 /** 插件的实例 */ 140927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 141927dbe93S猫头猫 /** 插件路径 */ 142927dbe93S猫头猫 public path: string; 143927dbe93S猫头猫 /** 插件方法 */ 144927dbe93S猫头猫 public methods: PluginMethods; 145927dbe93S猫头猫 14674d0cf81S猫头猫 constructor( 14774d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 14874d0cf81S猫头猫 pluginPath: string, 14974d0cf81S猫头猫 ) { 150927dbe93S猫头猫 this.state = 'enabled'; 151927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1523b3d6357S猫头猫 const _module: any = {exports: {}}; 153927dbe93S猫头猫 try { 15474d0cf81S猫头猫 if (typeof funcCode === 'string') { 155e0caf342S猫头猫 // 插件的环境变量 156e0caf342S猫头猫 const env = { 157e0caf342S猫头猫 getUserVariables: () => { 158e0caf342S猫头猫 return ( 159e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 160e0caf342S猫头猫 ); 161e0caf342S猫头猫 }, 162e3fa9b3cS猫头猫 os: 'android', 163e0caf342S猫头猫 }; 164e0caf342S猫头猫 1654060c00aS猫头猫 // eslint-disable-next-line no-new-func 166927dbe93S猫头猫 _instance = Function(` 167927dbe93S猫头猫 'use strict'; 168*91eb8fa8S猫头猫 return function(require, __musicfree_require, module, exports, console, env, URL) { 1699c34d637S猫头猫 ${funcCode} 170927dbe93S猫头猫 } 171e0caf342S猫头猫 `)()( 172e0caf342S猫头猫 _require, 173e0caf342S猫头猫 _require, 174e0caf342S猫头猫 _module, 175e0caf342S猫头猫 _module.exports, 176e0caf342S猫头猫 _console, 177e0caf342S猫头猫 env, 178*91eb8fa8S猫头猫 URL, 179e0caf342S猫头猫 ); 1803b3d6357S猫头猫 if (_module.exports.default) { 1813b3d6357S猫头猫 _instance = _module.exports 1823b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1833b3d6357S猫头猫 } else { 1849c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1853b3d6357S猫头猫 } 18674d0cf81S猫头猫 } else { 18774d0cf81S猫头猫 _instance = funcCode(); 18874d0cf81S猫头猫 } 189c2b3a262S猫头猫 // 插件初始化后的一些操作 19095297592S猫头猫 if (Array.isArray(_instance.userVariables)) { 19195297592S猫头猫 _instance.userVariables = _instance.userVariables.filter( 19295297592S猫头猫 it => it?.key, 19395297592S猫头猫 ); 19495297592S猫头猫 } 195927dbe93S猫头猫 this.checkValid(_instance); 196927dbe93S猫头猫 } catch (e: any) { 197b43683eaS猫头猫 console.log(e); 198927dbe93S猫头猫 this.state = 'error'; 199927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 200927dbe93S猫头猫 if (e?.stateCode) { 201927dbe93S猫头猫 this.stateCode = e.stateCode; 202927dbe93S猫头猫 } 203927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 204927dbe93S猫头猫 stateCode: this.stateCode, 205927dbe93S猫头猫 message: e?.message, 206927dbe93S猫头猫 stack: e?.stack, 207927dbe93S猫头猫 }); 208927dbe93S猫头猫 _instance = e?.instance ?? { 209927dbe93S猫头猫 _path: '', 210927dbe93S猫头猫 platform: '', 211927dbe93S猫头猫 appVersion: '', 21220e6a092S猫头猫 async getMediaSource() { 213927dbe93S猫头猫 return null; 214927dbe93S猫头猫 }, 215927dbe93S猫头猫 async search() { 216927dbe93S猫头猫 return {}; 217927dbe93S猫头猫 }, 218927dbe93S猫头猫 async getAlbumInfo() { 219927dbe93S猫头猫 return null; 220927dbe93S猫头猫 }, 221927dbe93S猫头猫 }; 222927dbe93S猫头猫 } 223927dbe93S猫头猫 this.instance = _instance; 224927dbe93S猫头猫 this.path = pluginPath; 225927dbe93S猫头猫 this.name = _instance.platform; 226ab8941d9S猫头猫 if ( 227ab8941d9S猫头猫 this.instance.platform === '' || 228ab8941d9S猫头猫 this.instance.platform === undefined 229ab8941d9S猫头猫 ) { 230927dbe93S猫头猫 this.hash = ''; 231927dbe93S猫头猫 } else { 23274d0cf81S猫头猫 if (typeof funcCode === 'string') { 233927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 23474d0cf81S猫头猫 } else { 23574d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 23674d0cf81S猫头猫 } 237927dbe93S猫头猫 } 238927dbe93S猫头猫 239927dbe93S猫头猫 // 放在最后 240927dbe93S猫头猫 this.methods = new PluginMethods(this); 241927dbe93S猫头猫 } 242927dbe93S猫头猫 243927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 244927dbe93S猫头猫 /** 版本号校验 */ 245927dbe93S猫头猫 if ( 246927dbe93S猫头猫 _instance.appVersion && 247927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 248927dbe93S猫头猫 ) { 249927dbe93S猫头猫 throw { 250927dbe93S猫头猫 instance: _instance, 251927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 252927dbe93S猫头猫 }; 253927dbe93S猫头猫 } 254927dbe93S猫头猫 return true; 255927dbe93S猫头猫 } 256927dbe93S猫头猫} 257d5bfeb7eS猫头猫//#endregion 258927dbe93S猫头猫 259d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 260927dbe93S猫头猫/** 有缓存等信息 */ 261927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 262927dbe93S猫头猫 private plugin; 263927dbe93S猫头猫 constructor(plugin: Plugin) { 264927dbe93S猫头猫 this.plugin = plugin; 265927dbe93S猫头猫 } 266927dbe93S猫头猫 /** 搜索 */ 267927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 268927dbe93S猫头猫 query: string, 269927dbe93S猫头猫 page: number, 270927dbe93S猫头猫 type: T, 271927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 272927dbe93S猫头猫 if (!this.plugin.instance.search) { 273927dbe93S猫头猫 return { 274927dbe93S猫头猫 isEnd: true, 275927dbe93S猫头猫 data: [], 276927dbe93S猫头猫 }; 277927dbe93S猫头猫 } 278927dbe93S猫头猫 2794060c00aS猫头猫 const result = 2804060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 281927dbe93S猫头猫 if (Array.isArray(result.data)) { 282927dbe93S猫头猫 result.data.forEach(_ => { 283927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 284927dbe93S猫头猫 }); 285927dbe93S猫头猫 return { 286927dbe93S猫头猫 isEnd: result.isEnd ?? true, 287927dbe93S猫头猫 data: result.data, 288927dbe93S猫头猫 }; 289927dbe93S猫头猫 } 290927dbe93S猫头猫 return { 291927dbe93S猫头猫 isEnd: true, 292927dbe93S猫头猫 data: [], 293927dbe93S猫头猫 }; 294927dbe93S猫头猫 } 295927dbe93S猫头猫 296927dbe93S猫头猫 /** 获取真实源 */ 29720e6a092S猫头猫 async getMediaSource( 298927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 299abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 300927dbe93S猫头猫 retryCount = 1, 301dc160d50S猫头猫 notUpdateCache = false, 302192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 303927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 30443eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 305927dbe93S猫头猫 const localPath = 30643eb30bfS猫头猫 mediaExtra?.localPath || 30743eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3080e4173cdS猫头猫 getInternalData<string>( 3090e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3100e4173cdS猫头猫 InternalDataType.LOCALPATH, 3110e4173cdS猫头猫 ); 312a84a85c5S猫头猫 if ( 313a84a85c5S猫头猫 localPath && 314a84a85c5S猫头猫 (localPath.startsWith('content://') || 315a84a85c5S猫头猫 (await FileSystem.exists(localPath))) 316a84a85c5S猫头猫 ) { 3170e4173cdS猫头猫 trace('本地播放', localPath); 31843eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 31943eb30bfS猫头猫 // 修正一下本地数据 32043eb30bfS猫头猫 MediaExtra.update(musicItem, { 32143eb30bfS猫头猫 localPath, 32243eb30bfS猫头猫 }); 32343eb30bfS猫头猫 } 324927dbe93S猫头猫 return { 32511908939S猫头猫 url: addFileScheme(localPath), 326927dbe93S猫头猫 }; 32743eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 32843eb30bfS猫头猫 MediaExtra.update(musicItem, { 32943eb30bfS猫头猫 localPath: undefined, 33043eb30bfS猫头猫 }); 331927dbe93S猫头猫 } 332a84a85c5S猫头猫 3337993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 334f5935920S猫头猫 throw new Error('本地音乐不存在'); 335f5935920S猫头猫 } 336927dbe93S猫头猫 // 2. 缓存播放 33743eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 33843eb30bfS猫头猫 musicItem, 33943eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 340985f8e75S猫头猫 const pluginCacheControl = 341985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 342cfa0fc07S猫头猫 if ( 343cfa0fc07S猫头猫 mediaCache && 34443eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34548f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34648f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 347ef714860S猫头猫 Network.isOffline())) 348cfa0fc07S猫头猫 ) { 3495276aef9S猫头猫 trace('播放', '缓存播放'); 35043eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 351927dbe93S猫头猫 return { 35243eb30bfS猫头猫 url: qualityInfo!.url, 353927dbe93S猫头猫 headers: mediaCache.headers, 3544060c00aS猫头猫 userAgent: 3554060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 356927dbe93S猫头猫 }; 357927dbe93S猫头猫 } 358927dbe93S猫头猫 // 3. 插件解析 35920e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 360a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 361a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 362a7b42a4cS猫头猫 ); 363a7b42a4cS猫头猫 return { 364a7b42a4cS猫头猫 url: url, 365a7b42a4cS猫头猫 headers: auth 366a7b42a4cS猫头猫 ? { 367a7b42a4cS猫头猫 Authorization: auth, 368a7b42a4cS猫头猫 } 369a7b42a4cS猫头猫 : undefined, 370a7b42a4cS猫头猫 }; 371927dbe93S猫头猫 } 372927dbe93S猫头猫 try { 373abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 374abaede57S猫头猫 musicItem, 375abaede57S猫头猫 quality, 376abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 377927dbe93S猫头猫 if (!url) { 378a28eac61S猫头猫 throw new Error('NOT RETRY'); 379927dbe93S猫头猫 } 3805276aef9S猫头猫 trace('播放', '插件播放'); 381927dbe93S猫头猫 const result = { 382927dbe93S猫头猫 url, 383927dbe93S猫头猫 headers, 384927dbe93S猫头猫 userAgent: headers?.['user-agent'], 385cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 386a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 387a7b42a4cS猫头猫 if (authFormattedResult.auth) { 388a7b42a4cS猫头猫 result.url = authFormattedResult.url; 389a7b42a4cS猫头猫 result.headers = { 390a7b42a4cS猫头猫 ...(result.headers ?? {}), 391a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 392a7b42a4cS猫头猫 }; 393a7b42a4cS猫头猫 } 394927dbe93S猫头猫 395dc160d50S猫头猫 if ( 396dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 397dc160d50S猫头猫 !notUpdateCache 398dc160d50S猫头猫 ) { 39943eb30bfS猫头猫 // 更新缓存 40043eb30bfS猫头猫 const cacheSource = { 40143eb30bfS猫头猫 headers: result.headers, 40243eb30bfS猫头猫 userAgent: result.userAgent, 40343eb30bfS猫头猫 url, 40443eb30bfS猫头猫 }; 40543eb30bfS猫头猫 let realMusicItem = { 40643eb30bfS猫头猫 ...musicItem, 40743eb30bfS猫头猫 ...(mediaCache || {}), 40843eb30bfS猫头猫 }; 40943eb30bfS猫头猫 realMusicItem.source = { 41043eb30bfS猫头猫 ...(realMusicItem.source || {}), 41143eb30bfS猫头猫 [quality]: cacheSource, 41243eb30bfS猫头猫 }; 41343eb30bfS猫头猫 41443eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 415752ffc5aS猫头猫 } 416927dbe93S猫头猫 return result; 417927dbe93S猫头猫 } catch (e: any) { 418a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 419927dbe93S猫头猫 await delay(150); 420abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 421927dbe93S猫头猫 } 422927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 423ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 424192ae2b0S猫头猫 return null; 425927dbe93S猫头猫 } 426927dbe93S猫头猫 } 427927dbe93S猫头猫 428927dbe93S猫头猫 /** 获取音乐详情 */ 429927dbe93S猫头猫 async getMusicInfo( 430927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 43174d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 432927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 433d704daedS猫头猫 return null; 434927dbe93S猫头猫 } 43574d0cf81S猫头猫 try { 436927dbe93S猫头猫 return ( 437927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4387993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 439d704daedS猫头猫 ) ?? null 440927dbe93S猫头猫 ); 441ea6d708fS猫头猫 } catch (e: any) { 442ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 443d704daedS猫头猫 return null; 44474d0cf81S猫头猫 } 445927dbe93S猫头猫 } 446927dbe93S猫头猫 4477e883dbbS猫头猫 /** 4487e883dbbS猫头猫 * 4497e883dbbS猫头猫 * getLyric(musicItem) => { 4507e883dbbS猫头猫 * lyric: string; 4517e883dbbS猫头猫 * trans: string; 4527e883dbbS猫头猫 * } 4537e883dbbS猫头猫 * 4547e883dbbS猫头猫 */ 455927dbe93S猫头猫 /** 获取歌词 */ 456927dbe93S猫头猫 async getLyric( 45743eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 458927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 45943eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 46043eb30bfS猫头猫 const meta = MediaMeta.get(originalMusicItem); 46143eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 462927dbe93S猫头猫 if (meta && meta.associatedLrc) { 46343eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 46443eb30bfS猫头猫 } else { 46543eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 466927dbe93S猫头猫 } 46743eb30bfS猫头猫 46843eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4697a91f04fS猫头猫 musicItem, 47043eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 47143eb30bfS猫头猫 47243eb30bfS猫头猫 /** 原始歌词文本 */ 4737e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4747e883dbbS猫头猫 let translation: string | null = null; 47543eb30bfS猫头猫 47643eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 47743eb30bfS猫头猫 if (musicItemCache?.lyric) { 47843eb30bfS猫头猫 // 缓存的远程结果 47943eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 48043eb30bfS猫头猫 musicItemCache.lyric || null; 48143eb30bfS猫头猫 // 缓存的本地结果 48243eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 48343eb30bfS猫头猫 musicItemCache.$localLyric || null; 48443eb30bfS猫头猫 4857e883dbbS猫头猫 // 优先用缓存的结果 4867e883dbbS猫头猫 if (cacheLyric.rawLrc || cacheLyric.translation) { 48743eb30bfS猫头猫 return { 4887e883dbbS猫头猫 rawLrc: cacheLyric.rawLrc, 4897e883dbbS猫头猫 translation: cacheLyric.translation, 49043eb30bfS猫头猫 }; 49143eb30bfS猫头猫 } 49243eb30bfS猫头猫 4937e883dbbS猫头猫 // 本地其实是缓存的路径 4947e883dbbS猫头猫 if (localLyric) { 4957e883dbbS猫头猫 let needRefetch = false; 4967e883dbbS猫头猫 if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) { 4977e883dbbS猫头猫 rawLrc = await readFile(localLyric.rawLrc, 'utf8'); 4987e883dbbS猫头猫 } else if (localLyric.rawLrc) { 4997e883dbbS猫头猫 needRefetch = true; 5007e883dbbS猫头猫 } 5017e883dbbS猫头猫 if ( 5027e883dbbS猫头猫 localLyric.translation && 5037e883dbbS猫头猫 (await exists(localLyric.translation)) 5047e883dbbS猫头猫 ) { 5057e883dbbS猫头猫 translation = await readFile( 5067e883dbbS猫头猫 localLyric.translation, 5077e883dbbS猫头猫 'utf8', 5087e883dbbS猫头猫 ); 5097e883dbbS猫头猫 } else if (localLyric.translation) { 5107e883dbbS猫头猫 needRefetch = true; 51143eb30bfS猫头猫 } 51243eb30bfS猫头猫 5137e883dbbS猫头猫 if (!needRefetch && (rawLrc || translation)) { 51443eb30bfS猫头猫 return { 5157e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5167e883dbbS猫头猫 translation: translation || undefined, 51743eb30bfS猫头猫 }; 51843eb30bfS猫头猫 } 51943eb30bfS猫头猫 } 52043eb30bfS猫头猫 } 52143eb30bfS猫头猫 52243eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 52343eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 52443eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 52543eb30bfS猫头猫 lrcSource = 52643eb30bfS猫头猫 (await this.plugin.instance 5277e883dbbS猫头猫 ?.getLyric?.(resetMediaItem(musicItem, undefined, true)) 52843eb30bfS猫头猫 ?.catch(() => null)) || null; 52943eb30bfS猫头猫 } else { 53043eb30bfS猫头猫 lrcSource = 53143eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 53243eb30bfS猫头猫 ?.instance?.getLyric?.( 53343eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 53443eb30bfS猫头猫 ) 53543eb30bfS猫头猫 ?.catch(() => null)) || null; 53643eb30bfS猫头猫 } 53743eb30bfS猫头猫 53843eb30bfS猫头猫 if (lrcSource) { 5397e883dbbS猫头猫 rawLrc = lrcSource?.rawLrc || rawLrc; 5407e883dbbS猫头猫 translation = lrcSource?.translation || null; 54143eb30bfS猫头猫 5427e883dbbS猫头猫 const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc; 54343eb30bfS猫头猫 5447e883dbbS猫头猫 // 本地的文件名 5457e883dbbS猫头猫 let filename: string | undefined = `${ 5467e883dbbS猫头猫 pathConst.lrcCachePath 5477e883dbbS猫头猫 }${nanoid()}.lrc`; 5487e883dbbS猫头猫 let filenameTrans: string | undefined = `${ 5497e883dbbS猫头猫 pathConst.lrcCachePath 5507e883dbbS猫头猫 }${nanoid()}.lrc`; 55143eb30bfS猫头猫 5527e883dbbS猫头猫 // 旧版本兼容 5537e883dbbS猫头猫 if (!(rawLrc || translation)) { 5547e883dbbS猫头猫 if (deprecatedLrcUrl) { 55543eb30bfS猫头猫 rawLrc = ( 5567e883dbbS猫头猫 await axios 5577e883dbbS猫头猫 .get(deprecatedLrcUrl, {timeout: 3000}) 5587e883dbbS猫头猫 .catch(() => null) 55943eb30bfS猫头猫 )?.data; 5607e883dbbS猫头猫 } else if (musicItem.rawLrc) { 5617e883dbbS猫头猫 rawLrc = musicItem.rawLrc; 5627e883dbbS猫头猫 } 56343eb30bfS猫头猫 } 56443eb30bfS猫头猫 56543eb30bfS猫头猫 if (rawLrc) { 56643eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 56743eb30bfS猫头猫 } else { 5687e883dbbS猫头猫 filename = undefined; 5697e883dbbS猫头猫 } 5707e883dbbS猫头猫 if (translation) { 5717e883dbbS猫头猫 await writeFile(filenameTrans, translation, 'utf8'); 5727e883dbbS猫头猫 } else { 5737e883dbbS猫头猫 filenameTrans = undefined; 5747a91f04fS猫头猫 } 5757a91f04fS猫头猫 5767e883dbbS猫头猫 if (rawLrc || translation) { 5777e883dbbS猫头猫 MediaCache.setMediaCache( 5787e883dbbS猫头猫 produce(musicItemCache || musicItem, draft => { 5797e883dbbS猫头猫 musicItemCache?.$localLyric?.rawLrc; 5807e883dbbS猫头猫 objectPath.set(draft, '$localLyric.rawLrc', filename); 58143eb30bfS猫头猫 objectPath.set( 58243eb30bfS猫头猫 draft, 5837e883dbbS猫头猫 '$localLyric.translation', 5847e883dbbS猫头猫 filenameTrans, 58543eb30bfS猫头猫 ); 58643eb30bfS猫头猫 return draft; 58743eb30bfS猫头猫 }), 58843eb30bfS猫头猫 ); 589927dbe93S猫头猫 return { 5907e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5917e883dbbS猫头猫 translation: translation || undefined, 592927dbe93S猫头猫 }; 593927dbe93S猫头猫 } 594927dbe93S猫头猫 } 59543eb30bfS猫头猫 5963a6f67b1S猫头猫 // 6. 如果是本地文件 59743eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 59843eb30bfS猫头猫 if ( 59943eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 60043eb30bfS猫头猫 isDownloaded 60143eb30bfS猫头猫 ) { 6027e883dbbS猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 60343eb30bfS猫头猫 60443eb30bfS猫头猫 console.log('本地文件歌词'); 60543eb30bfS猫头猫 6063a6f67b1S猫头猫 if (res) { 6073a6f67b1S猫头猫 return res; 6083a6f67b1S猫头猫 } 6093a6f67b1S猫头猫 } 610ea6d708fS猫头猫 devLog('warn', '无歌词'); 611927dbe93S猫头猫 612927dbe93S猫头猫 return null; 613927dbe93S猫头猫 } 614927dbe93S猫头猫 615927dbe93S猫头猫 /** 获取歌词文本 */ 616927dbe93S猫头猫 async getLyricText( 617927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 618927dbe93S猫头猫 ): Promise<string | undefined> { 6197e883dbbS猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 620927dbe93S猫头猫 } 621927dbe93S猫头猫 622927dbe93S猫头猫 /** 获取专辑信息 */ 623927dbe93S猫头猫 async getAlbumInfo( 624927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 625f9afcc0dS猫头猫 page: number = 1, 626f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 627927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 628f9afcc0dS猫头猫 return { 629f9afcc0dS猫头猫 albumItem, 630f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 631f2a4767cS猫头猫 resetMediaItem, 632f2a4767cS猫头猫 this.plugin.name, 633f2a4767cS猫头猫 true, 634f2a4767cS猫头猫 ), 635f9afcc0dS猫头猫 isEnd: true, 636f9afcc0dS猫头猫 }; 637927dbe93S猫头猫 } 638927dbe93S猫头猫 try { 639927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 640927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 641f9afcc0dS猫头猫 page, 642927dbe93S猫头猫 ); 6435276aef9S猫头猫 if (!result) { 6445276aef9S猫头猫 throw new Error(); 6455276aef9S猫头猫 } 646927dbe93S猫头猫 result?.musicList?.forEach(_ => { 647927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 64896744680S猫头猫 _.album = albumItem.title; 649927dbe93S猫头猫 }); 6505276aef9S猫头猫 651f9afcc0dS猫头猫 if (page <= 1) { 652f9afcc0dS猫头猫 // 合并信息 653f9afcc0dS猫头猫 return { 654f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 655f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 656f9afcc0dS猫头猫 musicList: result.musicList, 657f9afcc0dS猫头猫 }; 658f9afcc0dS猫头猫 } else { 659f9afcc0dS猫头猫 return { 660f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 661f9afcc0dS猫头猫 musicList: result.musicList, 662f9afcc0dS猫头猫 }; 663f9afcc0dS猫头猫 } 6644394410dS猫头猫 } catch (e: any) { 6654394410dS猫头猫 trace('获取专辑信息失败', e?.message); 666ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 667ea6d708fS猫头猫 668f9afcc0dS猫头猫 return null; 669927dbe93S猫头猫 } 670927dbe93S猫头猫 } 671927dbe93S猫头猫 6725830c002S猫头猫 /** 获取歌单信息 */ 6735830c002S猫头猫 async getMusicSheetInfo( 6745830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 6755830c002S猫头猫 page: number = 1, 6765830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 6775281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 6785830c002S猫头猫 return { 6795830c002S猫头猫 sheetItem, 6805830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 6815830c002S猫头猫 isEnd: true, 6825830c002S猫头猫 }; 6835830c002S猫头猫 } 6845830c002S猫头猫 try { 6855830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 6865830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 6875830c002S猫头猫 page, 6885830c002S猫头猫 ); 6895830c002S猫头猫 if (!result) { 6905830c002S猫头猫 throw new Error(); 6915830c002S猫头猫 } 6925830c002S猫头猫 result?.musicList?.forEach(_ => { 6935830c002S猫头猫 resetMediaItem(_, this.plugin.name); 6945830c002S猫头猫 }); 6955830c002S猫头猫 6965830c002S猫头猫 if (page <= 1) { 6975830c002S猫头猫 // 合并信息 6985830c002S猫头猫 return { 6995830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 7005830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7015830c002S猫头猫 musicList: result.musicList, 7025830c002S猫头猫 }; 7035830c002S猫头猫 } else { 7045830c002S猫头猫 return { 7055830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7065830c002S猫头猫 musicList: result.musicList, 7075830c002S猫头猫 }; 7085830c002S猫头猫 } 7095830c002S猫头猫 } catch (e: any) { 7105830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7115830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7125830c002S猫头猫 7135830c002S猫头猫 return null; 7145830c002S猫头猫 } 7155830c002S猫头猫 } 7165830c002S猫头猫 717927dbe93S猫头猫 /** 查询作者信息 */ 718efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 719927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 720927dbe93S猫头猫 page: number, 721927dbe93S猫头猫 type: T, 722927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 723efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 724927dbe93S猫头猫 return { 725927dbe93S猫头猫 isEnd: true, 726927dbe93S猫头猫 data: [], 727927dbe93S猫头猫 }; 728927dbe93S猫头猫 } 729927dbe93S猫头猫 try { 730efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 731927dbe93S猫头猫 artistItem, 732927dbe93S猫头猫 page, 733927dbe93S猫头猫 type, 734927dbe93S猫头猫 ); 735927dbe93S猫头猫 if (!result.data) { 736927dbe93S猫头猫 return { 737927dbe93S猫头猫 isEnd: true, 738927dbe93S猫头猫 data: [], 739927dbe93S猫头猫 }; 740927dbe93S猫头猫 } 741927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 742927dbe93S猫头猫 return { 743927dbe93S猫头猫 isEnd: result.isEnd ?? true, 744927dbe93S猫头猫 data: result.data, 745927dbe93S猫头猫 }; 7464394410dS猫头猫 } catch (e: any) { 7474394410dS猫头猫 trace('查询作者信息失败', e?.message); 748ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 749ea6d708fS猫头猫 750927dbe93S猫头猫 throw e; 751927dbe93S猫头猫 } 752927dbe93S猫头猫 } 75308380090S猫头猫 75408380090S猫头猫 /** 导入歌单 */ 75508380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 75608380090S猫头猫 try { 75708380090S猫头猫 const result = 75808380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 75908380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 76008380090S猫头猫 return result; 761ea6d708fS猫头猫 } catch (e: any) { 7620e4173cdS猫头猫 console.log(e); 763ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 764ea6d708fS猫头猫 76508380090S猫头猫 return []; 76608380090S猫头猫 } 76708380090S猫头猫 } 7684d9d3c4cS猫头猫 /** 导入单曲 */ 7694d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 7704d9d3c4cS猫头猫 try { 7714d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 7724d9d3c4cS猫头猫 urlLike, 7734d9d3c4cS猫头猫 ); 7744d9d3c4cS猫头猫 if (!result) { 7754d9d3c4cS猫头猫 throw new Error(); 7764d9d3c4cS猫头猫 } 7774d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 7784d9d3c4cS猫头猫 return result; 779ea6d708fS猫头猫 } catch (e: any) { 780ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 781ea6d708fS猫头猫 7824d9d3c4cS猫头猫 return null; 7834d9d3c4cS猫头猫 } 7844d9d3c4cS猫头猫 } 785d52aa40eS猫头猫 /** 获取榜单 */ 78692b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 787d52aa40eS猫头猫 try { 788d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 789d52aa40eS猫头猫 if (!result) { 790d52aa40eS猫头猫 throw new Error(); 791d52aa40eS猫头猫 } 792d52aa40eS猫头猫 return result; 793d52aa40eS猫头猫 } catch (e: any) { 794d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 795d52aa40eS猫头猫 return []; 796d52aa40eS猫头猫 } 797d52aa40eS猫头猫 } 798d52aa40eS猫头猫 /** 获取榜单详情 */ 799d52aa40eS猫头猫 async getTopListDetail( 80092b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 801956ee1b7S猫头猫 page: number, 802956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 803d52aa40eS猫头猫 try { 804d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 805d52aa40eS猫头猫 topListItem, 806956ee1b7S猫头猫 page, 807d52aa40eS猫头猫 ); 808d52aa40eS猫头猫 if (!result) { 809d52aa40eS猫头猫 throw new Error(); 810d52aa40eS猫头猫 } 811d384662fS猫头猫 if (result.musicList) { 812d384662fS猫头猫 result.musicList.forEach(_ => 813d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 814d384662fS猫头猫 ); 815d384662fS猫头猫 } 816956ee1b7S猫头猫 if (result.isEnd !== false) { 817956ee1b7S猫头猫 result.isEnd = true; 818956ee1b7S猫头猫 } 819d52aa40eS猫头猫 return result; 820d52aa40eS猫头猫 } catch (e: any) { 821d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 822d52aa40eS猫头猫 return { 823956ee1b7S猫头猫 isEnd: true, 824956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 825d52aa40eS猫头猫 musicList: [], 826d52aa40eS猫头猫 }; 827d52aa40eS猫头猫 } 828d52aa40eS猫头猫 } 829ceb900cdS猫头猫 8305830c002S猫头猫 /** 获取推荐歌单的tag */ 831ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 832ceb900cdS猫头猫 try { 833ceb900cdS猫头猫 const result = 834ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 835ceb900cdS猫头猫 if (!result) { 836ceb900cdS猫头猫 throw new Error(); 837ceb900cdS猫头猫 } 838ceb900cdS猫头猫 return result; 839ceb900cdS猫头猫 } catch (e: any) { 840ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 841ceb900cdS猫头猫 return { 842ceb900cdS猫头猫 data: [], 843ceb900cdS猫头猫 }; 844ceb900cdS猫头猫 } 845ceb900cdS猫头猫 } 8465830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 847ceb900cdS猫头猫 async getRecommendSheetsByTag( 848ceb900cdS猫头猫 tagItem: ICommon.IUnique, 849ceb900cdS猫头猫 page?: number, 850ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 851ceb900cdS猫头猫 try { 852ceb900cdS猫头猫 const result = 853ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 854ceb900cdS猫头猫 tagItem, 855ceb900cdS猫头猫 page ?? 1, 856ceb900cdS猫头猫 ); 857ceb900cdS猫头猫 if (!result) { 858ceb900cdS猫头猫 throw new Error(); 859ceb900cdS猫头猫 } 860ceb900cdS猫头猫 if (result.isEnd !== false) { 861ceb900cdS猫头猫 result.isEnd = true; 862ceb900cdS猫头猫 } 863ceb900cdS猫头猫 if (!result.data) { 864ceb900cdS猫头猫 result.data = []; 865ceb900cdS猫头猫 } 866ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 867ceb900cdS猫头猫 868ceb900cdS猫头猫 return result; 869ceb900cdS猫头猫 } catch (e: any) { 870ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 871ceb900cdS猫头猫 return { 872ceb900cdS猫头猫 isEnd: true, 873ceb900cdS猫头猫 data: [], 874ceb900cdS猫头猫 }; 875ceb900cdS猫头猫 } 876ceb900cdS猫头猫 } 877927dbe93S猫头猫} 878d5bfeb7eS猫头猫//#endregion 8791a5528a0S猫头猫 880927dbe93S猫头猫let plugins: Array<Plugin> = []; 881927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 88274d0cf81S猫头猫 883d5bfeb7eS猫头猫//#region 本地音乐插件 88474d0cf81S猫头猫/** 本地插件 */ 88574d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 8860e4173cdS猫头猫 return { 887d5bfeb7eS猫头猫 platform: localPluginPlatform, 88874d0cf81S猫头猫 _path: '', 88974d0cf81S猫头猫 async getMusicInfo(musicBase) { 89074d0cf81S猫头猫 const localPath = getInternalData<string>( 89174d0cf81S猫头猫 musicBase, 89274d0cf81S猫头猫 InternalDataType.LOCALPATH, 8930e4173cdS猫头猫 ); 89474d0cf81S猫头猫 if (localPath) { 89574d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 89674d0cf81S猫头猫 return { 89774d0cf81S猫头猫 artwork: coverImg, 89874d0cf81S猫头猫 }; 89974d0cf81S猫头猫 } 90074d0cf81S猫头猫 return null; 90174d0cf81S猫头猫 }, 9027993f90eS猫头猫 async getLyric(musicBase) { 9037993f90eS猫头猫 const localPath = getInternalData<string>( 9047993f90eS猫头猫 musicBase, 9057993f90eS猫头猫 InternalDataType.LOCALPATH, 9067993f90eS猫头猫 ); 9073a6f67b1S猫头猫 let rawLrc: string | null = null; 9087993f90eS猫头猫 if (localPath) { 9093a6f67b1S猫头猫 // 读取内嵌歌词 9103a6f67b1S猫头猫 try { 9113a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 9123a6f67b1S猫头猫 } catch (e) { 913a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 9147993f90eS猫头猫 } 9153a6f67b1S猫头猫 if (!rawLrc) { 9163a6f67b1S猫头猫 // 读取配置歌词 9173a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 9183a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 9193a6f67b1S猫头猫 9203a6f67b1S猫头猫 try { 9213a6f67b1S猫头猫 if (await exists(lrcPath)) { 9223a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 9233a6f67b1S猫头猫 } 9243a6f67b1S猫头猫 } catch {} 9253a6f67b1S猫头猫 } 9263a6f67b1S猫头猫 } 9273a6f67b1S猫头猫 9283a6f67b1S猫头猫 return rawLrc 9293a6f67b1S猫头猫 ? { 9303a6f67b1S猫头猫 rawLrc, 9313a6f67b1S猫头猫 } 9323a6f67b1S猫头猫 : null; 9337993f90eS猫头猫 }, 934a84a85c5S猫头猫 async importMusicItem(urlLike) { 935a84a85c5S猫头猫 let meta: any = {}; 936a84a85c5S猫头猫 try { 937a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 938a84a85c5S猫头猫 } catch {} 939a84a85c5S猫头猫 const id = await FileSystem.hash(urlLike, 'MD5'); 940a84a85c5S猫头猫 return { 941a84a85c5S猫头猫 id: id, 942a84a85c5S猫头猫 platform: '本地', 943a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 944a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 945a84a85c5S猫头猫 duration: parseInt(meta?.duration ?? '0') / 1000, 946a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 947a84a85c5S猫头猫 artwork: '', 948a84a85c5S猫头猫 [internalSerializeKey]: { 949a84a85c5S猫头猫 localPath: urlLike, 950a84a85c5S猫头猫 }, 951a84a85c5S猫头猫 }; 952a84a85c5S猫头猫 }, 95311908939S猫头猫 async getMediaSource(musicItem, quality) { 95411908939S猫头猫 if (quality === 'standard') { 95511908939S猫头猫 return { 95611908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 95711908939S猫头猫 }; 95811908939S猫头猫 } 95911908939S猫头猫 return null; 96011908939S猫头猫 }, 96174d0cf81S猫头猫 }; 96274d0cf81S猫头猫}, ''); 9637993f90eS猫头猫localFilePlugin.hash = localPluginHash; 964927dbe93S猫头猫 965d5bfeb7eS猫头猫//#endregion 966d5bfeb7eS猫头猫 967927dbe93S猫头猫async function setup() { 968927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 969927dbe93S猫头猫 try { 970927dbe93S猫头猫 // 加载插件 971927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 972927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 973927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 9741e263108S猫头猫 trace('初始化插件', _pluginUrl); 9751e263108S猫头猫 if ( 9761e263108S猫头猫 _pluginUrl.isFile() && 9771e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 9781e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 9791e263108S猫头猫 ) { 980927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 981927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 9824060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 9834060c00aS猫头猫 p => p.hash === plugin.hash, 9844060c00aS猫头猫 ); 985927dbe93S猫头猫 if (_pluginIndex !== -1) { 986927dbe93S猫头猫 // 重复插件,直接忽略 9870c266394S猫头猫 continue; 988927dbe93S猫头猫 } 989927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 990927dbe93S猫头猫 } 991927dbe93S猫头猫 } 992927dbe93S猫头猫 993927dbe93S猫头猫 plugins = _plugins; 994e08d37a3S猫头猫 /** 初始化meta信息 */ 995c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 996c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 997c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 998c2b3a262S猫头猫 for (let plugin of plugins) { 999c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1000c2b3a262S猫头猫 plugin.state = 'disabled'; 1001c2b3a262S猫头猫 } 1002c2b3a262S猫头猫 } 1003c2b3a262S猫头猫 pluginStateMapper.notify(); 1004927dbe93S猫头猫 } catch (e: any) { 10054060c00aS猫头猫 ToastAndroid.show( 10064060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 10074060c00aS猫头猫 ToastAndroid.LONG, 10084060c00aS猫头猫 ); 10091a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1010927dbe93S猫头猫 throw e; 1011927dbe93S猫头猫 } 1012927dbe93S猫头猫} 1013927dbe93S猫头猫 1014e36e2599S猫头猫interface IInstallPluginConfig { 1015e36e2599S猫头猫 notCheckVersion?: boolean; 1016e36e2599S猫头猫} 1017e36e2599S猫头猫 1018927dbe93S猫头猫// 安装插件 1019e36e2599S猫头猫async function installPlugin( 1020e36e2599S猫头猫 pluginPath: string, 1021e36e2599S猫头猫 config?: IInstallPluginConfig, 1022e36e2599S猫头猫) { 102322c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1024927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1025e36e2599S猫头猫 1026e36e2599S猫头猫 if (funcCode) { 1027927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1028927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1029927dbe93S猫头猫 if (_pluginIndex !== -1) { 1030e36e2599S猫头猫 // 静默忽略 1031e36e2599S猫头猫 return plugin; 1032927dbe93S猫头猫 } 1033e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1034e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1035e36e2599S猫头猫 if ( 1036e36e2599S猫头猫 compare( 1037e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1038e36e2599S猫头猫 plugin.instance.version ?? '', 1039e36e2599S猫头猫 '>', 1040e36e2599S猫头猫 ) 1041e36e2599S猫头猫 ) { 1042e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1043e36e2599S猫头猫 } 1044e36e2599S猫头猫 } 1045e36e2599S猫头猫 1046927dbe93S猫头猫 if (plugin.hash !== '') { 1047927dbe93S猫头猫 const fn = nanoid(); 1048e36e2599S猫头猫 if (oldVersionPlugin) { 1049e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1050e36e2599S猫头猫 try { 1051e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1052e36e2599S猫头猫 } catch {} 1053e36e2599S猫头猫 } 1054927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1055927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1056927dbe93S猫头猫 plugin.path = _pluginPath; 1057927dbe93S猫头猫 plugins = plugins.concat(plugin); 1058927dbe93S猫头猫 pluginStateMapper.notify(); 1059a84a85c5S猫头猫 return plugin; 1060927dbe93S猫头猫 } 1061e36e2599S猫头猫 throw new Error('插件无法解析!'); 1062927dbe93S猫头猫 } 1063e36e2599S猫头猫 throw new Error('插件无法识别!'); 1064c2b3a262S猫头猫} 1065c2b3a262S猫头猫 1066ee781061S猫头猫const reqHeaders = { 1067ee781061S猫头猫 'Cache-Control': 'no-cache', 1068ee781061S猫头猫 Pragma: 'no-cache', 1069ee781061S猫头猫 Expires: '0', 1070ee781061S猫头猫}; 1071ee781061S猫头猫 1072c2b3a262S猫头猫async function installPluginFromUrl( 1073c2b3a262S猫头猫 url: string, 1074c2b3a262S猫头猫 config?: IInstallPluginConfig, 1075c2b3a262S猫头猫) { 107658992c6bS猫头猫 try { 1077ee781061S猫头猫 const funcCode = ( 1078ee781061S猫头猫 await axios.get(url, { 1079ee781061S猫头猫 headers: reqHeaders, 1080ee781061S猫头猫 }) 1081ee781061S猫头猫 ).data; 108258992c6bS猫头猫 if (funcCode) { 108358992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 108458992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 108558992c6bS猫头猫 if (_pluginIndex !== -1) { 10868b7ddca8S猫头猫 // 静默忽略 10878b7ddca8S猫头猫 return; 108858992c6bS猫头猫 } 108925c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1090c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 109125c1bd29S猫头猫 if ( 109225c1bd29S猫头猫 compare( 109325c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 109425c1bd29S猫头猫 plugin.instance.version ?? '', 109525c1bd29S猫头猫 '>', 109625c1bd29S猫头猫 ) 109725c1bd29S猫头猫 ) { 109825c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 109925c1bd29S猫头猫 } 110025c1bd29S猫头猫 } 110125c1bd29S猫头猫 110258992c6bS猫头猫 if (plugin.hash !== '') { 110358992c6bS猫头猫 const fn = nanoid(); 110458992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 110558992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 110658992c6bS猫头猫 plugin.path = _pluginPath; 110758992c6bS猫头猫 plugins = plugins.concat(plugin); 110825c1bd29S猫头猫 if (oldVersionPlugin) { 110925c1bd29S猫头猫 plugins = plugins.filter( 111025c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 111125c1bd29S猫头猫 ); 111225c1bd29S猫头猫 try { 111325c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 111425c1bd29S猫头猫 } catch {} 111525c1bd29S猫头猫 } 111658992c6bS猫头猫 pluginStateMapper.notify(); 111758992c6bS猫头猫 return; 111858992c6bS猫头猫 } 111974acbfc0S猫头猫 throw new Error('插件无法解析!'); 112058992c6bS猫头猫 } 112125c1bd29S猫头猫 } catch (e: any) { 1122ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 112358992c6bS猫头猫 errorLog('URL安装插件失败', e); 112425c1bd29S猫头猫 throw new Error(e?.message ?? ''); 112558992c6bS猫头猫 } 112658992c6bS猫头猫} 112758992c6bS猫头猫 1128927dbe93S猫头猫/** 卸载插件 */ 1129927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1130927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1131927dbe93S猫头猫 if (targetIndex !== -1) { 1132927dbe93S猫头猫 try { 113324e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1134927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1135927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1136927dbe93S猫头猫 pluginStateMapper.notify(); 113743eb30bfS猫头猫 // 防止其他重名 113824e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 113943eb30bfS猫头猫 MediaMeta.removeAll(pluginName); 114024e5e74aS猫头猫 } 1141927dbe93S猫头猫 } catch {} 1142927dbe93S猫头猫 } 1143927dbe93S猫头猫} 1144927dbe93S猫头猫 114508882a77S猫头猫async function uninstallAllPlugins() { 114608882a77S猫头猫 await Promise.all( 114708882a77S猫头猫 plugins.map(async plugin => { 114808882a77S猫头猫 try { 114908882a77S猫头猫 const pluginName = plugin.name; 115008882a77S猫头猫 await unlink(plugin.path); 115143eb30bfS猫头猫 MediaMeta.removeAll(pluginName); 115208882a77S猫头猫 } catch (e) {} 115308882a77S猫头猫 }), 115408882a77S猫头猫 ); 115508882a77S猫头猫 plugins = []; 115608882a77S猫头猫 pluginStateMapper.notify(); 1157e08d37a3S猫头猫 1158e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1159e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1160e08d37a3S猫头猫 .then(fns => { 1161e08d37a3S猫头猫 fns.forEach(fn => { 1162e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1163e08d37a3S猫头猫 }); 1164e08d37a3S猫头猫 }) 1165e08d37a3S猫头猫 .catch(emptyFunction); 116608882a77S猫头猫} 116708882a77S猫头猫 116825c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 116925c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 117025c1bd29S猫头猫 if (!updateUrl) { 117125c1bd29S猫头猫 throw new Error('没有更新源'); 117225c1bd29S猫头猫 } 117325c1bd29S猫头猫 try { 117425c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 117525c1bd29S猫头猫 } catch (e: any) { 117625c1bd29S猫头猫 if (e.message === '插件已安装') { 117725c1bd29S猫头猫 throw new Error('当前已是最新版本'); 117825c1bd29S猫头猫 } else { 117925c1bd29S猫头猫 throw e; 118025c1bd29S猫头猫 } 118125c1bd29S猫头猫 } 118225c1bd29S猫头猫} 118325c1bd29S猫头猫 1184927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 11852c595535S猫头猫 return getByName(mediaItem?.platform); 1186927dbe93S猫头猫} 1187927dbe93S猫头猫 1188927dbe93S猫头猫function getByHash(hash: string) { 11897993f90eS猫头猫 return hash === localPluginHash 11907993f90eS猫头猫 ? localFilePlugin 11917993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1192927dbe93S猫头猫} 1193927dbe93S猫头猫 1194927dbe93S猫头猫function getByName(name: string) { 11957993f90eS猫头猫 return name === localPluginPlatform 11960e4173cdS猫头猫 ? localFilePlugin 11970e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1198927dbe93S猫头猫} 1199927dbe93S猫头猫 1200927dbe93S猫头猫function getValidPlugins() { 1201927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1202927dbe93S猫头猫} 1203927dbe93S猫头猫 12042b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 12052b80a429S猫头猫 return plugins.filter( 12062b80a429S猫头猫 _ => 12072b80a429S猫头猫 _.state === 'enabled' && 12082b80a429S猫头猫 _.instance.search && 120939ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 121039ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 12112b80a429S猫头猫 : true), 12122b80a429S猫头猫 ); 1213efb9da24S猫头猫} 1214efb9da24S猫头猫 12152b80a429S猫头猫function getSortedSearchablePlugins( 12162b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 12172b80a429S猫头猫) { 12182b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1219e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1220e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1221e08d37a3S猫头猫 0 1222e08d37a3S猫头猫 ? -1 1223e08d37a3S猫头猫 : 1, 1224e08d37a3S猫头猫 ); 1225e08d37a3S猫头猫} 1226e08d37a3S猫头猫 122715feccc1S猫头猫function getTopListsablePlugins() { 122815feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 122915feccc1S猫头猫} 123015feccc1S猫头猫 123115feccc1S猫头猫function getSortedTopListsablePlugins() { 123215feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 123315feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 123415feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 123515feccc1S猫头猫 0 123615feccc1S猫头猫 ? -1 123715feccc1S猫头猫 : 1, 123815feccc1S猫头猫 ); 123915feccc1S猫头猫} 124015feccc1S猫头猫 1241ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1242ceb900cdS猫头猫 return plugins.filter( 1243ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1244ceb900cdS猫头猫 ); 1245ceb900cdS猫头猫} 1246ceb900cdS猫头猫 1247ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1248ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1249ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1250ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1251ceb900cdS猫头猫 0 1252ceb900cdS猫头猫 ? -1 1253ceb900cdS猫头猫 : 1, 1254ceb900cdS猫头猫 ); 1255ceb900cdS猫头猫} 1256ceb900cdS猫头猫 1257e08d37a3S猫头猫function useSortedPlugins() { 1258e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1259e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1260e08d37a3S猫头猫 126134588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 126234588741S猫头猫 [..._plugins].sort((a, b) => 1263e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1264e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1265e08d37a3S猫头猫 0 1266e08d37a3S猫头猫 ? -1 1267e08d37a3S猫头猫 : 1, 126834588741S猫头猫 ), 1269e08d37a3S猫头猫 ); 127034588741S猫头猫 127134588741S猫头猫 useEffect(() => { 1272d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 127334588741S猫头猫 setSortedPlugins( 127434588741S猫头猫 [..._plugins].sort((a, b) => 127534588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 127634588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 127734588741S猫头猫 0 127834588741S猫头猫 ? -1 127934588741S猫头猫 : 1, 128034588741S猫头猫 ), 128134588741S猫头猫 ); 1282d4cd40d8S猫头猫 }); 128334588741S猫头猫 }, [_plugins, _pluginMetaAll]); 128434588741S猫头猫 128534588741S猫头猫 return sortedPlugins; 1286e08d37a3S猫头猫} 1287e08d37a3S猫头猫 1288c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1289c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1290c2b3a262S猫头猫 if (target) { 1291c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1292c2b3a262S猫头猫 plugins = [...plugins]; 1293c2b3a262S猫头猫 pluginStateMapper.notify(); 1294c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1295c2b3a262S猫头猫 } 1296c2b3a262S猫头猫} 1297c2b3a262S猫头猫 1298927dbe93S猫头猫const PluginManager = { 1299927dbe93S猫头猫 setup, 1300927dbe93S猫头猫 installPlugin, 130158992c6bS猫头猫 installPluginFromUrl, 130225c1bd29S猫头猫 updatePlugin, 1303927dbe93S猫头猫 uninstallPlugin, 1304927dbe93S猫头猫 getByMedia, 1305927dbe93S猫头猫 getByHash, 1306927dbe93S猫头猫 getByName, 1307927dbe93S猫头猫 getValidPlugins, 1308efb9da24S猫头猫 getSearchablePlugins, 1309e08d37a3S猫头猫 getSortedSearchablePlugins, 131015feccc1S猫头猫 getTopListsablePlugins, 1311ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 131215feccc1S猫头猫 getSortedTopListsablePlugins, 13135276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1314e08d37a3S猫头猫 useSortedPlugins, 131508882a77S猫头猫 uninstallAllPlugins, 1316c2b3a262S猫头猫 setPluginEnabled, 13175276aef9S猫头猫}; 1318927dbe93S猫头猫 1319927dbe93S猫头猫export default PluginManager; 1320