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'; 205589cdf3S猫头猫import MediaExtra from './mediaExtra'; 21927dbe93S猫头猫import {nanoid} from 'nanoid'; 22ea6d708fS猫头猫import {devLog, errorLog, trace} from '../utils/log'; 23cfa0fc07S猫头猫import { 240e4173cdS猫头猫 getInternalData, 250e4173cdS猫头猫 InternalDataType, 260e4173cdS猫头猫 isSameMediaItem, 270e4173cdS猫头猫 resetMediaItem, 280e4173cdS猫头猫} from '@/utils/mediaItem'; 297993f90eS猫头猫import { 307993f90eS猫头猫 CacheControl, 31e08d37a3S猫头猫 emptyFunction, 327993f90eS猫头猫 internalSerializeKey, 337993f90eS猫头猫 localPluginHash, 347993f90eS猫头猫 localPluginPlatform, 357993f90eS猫头猫} from '@/constants/commonConst'; 36927dbe93S猫头猫import delay from '@/utils/delay'; 374d9d3c4cS猫头猫import * as cheerio from 'cheerio'; 387d7e864fS猫头猫import CookieManager from '@react-native-cookies/cookies'; 397d7e864fS猫头猫import he from 'he'; 40ef714860S猫头猫import Network from './network'; 410e4173cdS猫头猫import LocalMusicSheet from './localMusicSheet'; 42*5353b473S猫头猫import {getInfoAsync} from 'expo-file-system'; 4374d0cf81S猫头猫import Mp3Util from '@/native/mp3Util'; 44e08d37a3S猫头猫import {PluginMeta} from './pluginMeta'; 4534588741S猫头猫import {useEffect, useState} from 'react'; 4611908939S猫头猫import {addFileScheme, getFileName} from '@/utils/fileUtils'; 47a7b42a4cS猫头猫import {URL} from 'react-native-url-polyfill'; 48a7b42a4cS猫头猫import Base64 from '@/utils/base64'; 4943eb30bfS猫头猫import MediaCache from './mediaCache'; 505589cdf3S猫头猫import {produce} from 'immer'; 5143eb30bfS猫头猫import objectPath from 'object-path'; 52927dbe93S猫头猫 5361aca335S猫头猫axios.defaults.timeout = 2000; 54927dbe93S猫头猫 55927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 56927dbe93S猫头猫 57cfa0fc07S猫头猫export enum PluginStateCode { 58927dbe93S猫头猫 /** 版本不匹配 */ 59927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 60927dbe93S猫头猫 /** 无法解析 */ 61927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 62927dbe93S猫头猫} 63927dbe93S猫头猫 649c34d637S猫头猫const packages: Record<string, any> = { 659c34d637S猫头猫 cheerio, 669c34d637S猫头猫 'crypto-js': CryptoJs, 679c34d637S猫头猫 axios, 689c34d637S猫头猫 dayjs, 699c34d637S猫头猫 'big-integer': bigInt, 709c34d637S猫头猫 qs, 719c34d637S猫头猫 he, 723b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 7328ccceb7S猫头猫 webdav, 749c34d637S猫头猫}; 759c34d637S猫头猫 76b43683eaS猫头猫const _require = (packageName: string) => { 77b43683eaS猫头猫 let pkg = packages[packageName]; 78b43683eaS猫头猫 pkg.default = pkg; 79b43683eaS猫头猫 return pkg; 80b43683eaS猫头猫}; 819c34d637S猫头猫 8253f8cd8eS猫头猫const _consoleBind = function ( 8353f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 8453f8cd8eS猫头猫 ...args: any 8553f8cd8eS猫头猫) { 8653f8cd8eS猫头猫 const fn = console[method]; 8753f8cd8eS猫头猫 if (fn) { 8853f8cd8eS猫头猫 fn(...args); 8953f8cd8eS猫头猫 devLog(method, ...args); 9053f8cd8eS猫头猫 } 9153f8cd8eS猫头猫}; 9253f8cd8eS猫头猫 9353f8cd8eS猫头猫const _console = { 9453f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9553f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9653f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9753f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9853f8cd8eS猫头猫}; 9953f8cd8eS猫头猫 100a7b42a4cS猫头猫function formatAuthUrl(url: string) { 101a7b42a4cS猫头猫 const urlObj = new URL(url); 102a7b42a4cS猫头猫 103a7b42a4cS猫头猫 try { 104a7b42a4cS猫头猫 if (urlObj.username && urlObj.password) { 105a7b42a4cS猫头猫 const auth = `Basic ${Base64.btoa( 106a7b42a4cS猫头猫 `${decodeURIComponent(urlObj.username)}:${decodeURIComponent( 107a7b42a4cS猫头猫 urlObj.password, 108a7b42a4cS猫头猫 )}`, 109a7b42a4cS猫头猫 )}`; 110a7b42a4cS猫头猫 urlObj.username = ''; 111a7b42a4cS猫头猫 urlObj.password = ''; 112a7b42a4cS猫头猫 113a7b42a4cS猫头猫 return { 114a7b42a4cS猫头猫 url: urlObj.toString(), 115a7b42a4cS猫头猫 auth, 116a7b42a4cS猫头猫 }; 117a7b42a4cS猫头猫 } 118a7b42a4cS猫头猫 } catch (e) { 119a7b42a4cS猫头猫 return { 120a7b42a4cS猫头猫 url, 121a7b42a4cS猫头猫 }; 122a7b42a4cS猫头猫 } 123a7b42a4cS猫头猫 return { 124a7b42a4cS猫头猫 url, 125a7b42a4cS猫头猫 }; 126a7b42a4cS猫头猫} 127a7b42a4cS猫头猫 128d5bfeb7eS猫头猫//#region 插件类 129927dbe93S猫头猫export class Plugin { 130927dbe93S猫头猫 /** 插件名 */ 131927dbe93S猫头猫 public name: string; 132927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 133927dbe93S猫头猫 public hash: string; 134927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 135927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 136927dbe93S猫头猫 /** 插件状态信息 */ 137927dbe93S猫头猫 public stateCode?: PluginStateCode; 138927dbe93S猫头猫 /** 插件的实例 */ 139927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 140927dbe93S猫头猫 /** 插件路径 */ 141927dbe93S猫头猫 public path: string; 142927dbe93S猫头猫 /** 插件方法 */ 143927dbe93S猫头猫 public methods: PluginMethods; 144927dbe93S猫头猫 14574d0cf81S猫头猫 constructor( 14674d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 14774d0cf81S猫头猫 pluginPath: string, 14874d0cf81S猫头猫 ) { 149927dbe93S猫头猫 this.state = 'enabled'; 150927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1513b3d6357S猫头猫 const _module: any = {exports: {}}; 152927dbe93S猫头猫 try { 15374d0cf81S猫头猫 if (typeof funcCode === 'string') { 154e0caf342S猫头猫 // 插件的环境变量 155e0caf342S猫头猫 const env = { 156e0caf342S猫头猫 getUserVariables: () => { 157e0caf342S猫头猫 return ( 158e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 159e0caf342S猫头猫 ); 160e0caf342S猫头猫 }, 161e3fa9b3cS猫头猫 os: 'android', 162e0caf342S猫头猫 }; 163e0caf342S猫头猫 1644060c00aS猫头猫 // eslint-disable-next-line no-new-func 165927dbe93S猫头猫 _instance = Function(` 166927dbe93S猫头猫 'use strict'; 16791eb8fa8S猫头猫 return function(require, __musicfree_require, module, exports, console, env, URL) { 1689c34d637S猫头猫 ${funcCode} 169927dbe93S猫头猫 } 170e0caf342S猫头猫 `)()( 171e0caf342S猫头猫 _require, 172e0caf342S猫头猫 _require, 173e0caf342S猫头猫 _module, 174e0caf342S猫头猫 _module.exports, 175e0caf342S猫头猫 _console, 176e0caf342S猫头猫 env, 17791eb8fa8S猫头猫 URL, 178e0caf342S猫头猫 ); 1793b3d6357S猫头猫 if (_module.exports.default) { 1803b3d6357S猫头猫 _instance = _module.exports 1813b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1823b3d6357S猫头猫 } else { 1839c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1843b3d6357S猫头猫 } 18574d0cf81S猫头猫 } else { 18674d0cf81S猫头猫 _instance = funcCode(); 18774d0cf81S猫头猫 } 188c2b3a262S猫头猫 // 插件初始化后的一些操作 18995297592S猫头猫 if (Array.isArray(_instance.userVariables)) { 19095297592S猫头猫 _instance.userVariables = _instance.userVariables.filter( 19195297592S猫头猫 it => it?.key, 19295297592S猫头猫 ); 19395297592S猫头猫 } 194927dbe93S猫头猫 this.checkValid(_instance); 195927dbe93S猫头猫 } catch (e: any) { 196b43683eaS猫头猫 console.log(e); 197927dbe93S猫头猫 this.state = 'error'; 198927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 199927dbe93S猫头猫 if (e?.stateCode) { 200927dbe93S猫头猫 this.stateCode = e.stateCode; 201927dbe93S猫头猫 } 202927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 203927dbe93S猫头猫 stateCode: this.stateCode, 204927dbe93S猫头猫 message: e?.message, 205927dbe93S猫头猫 stack: e?.stack, 206927dbe93S猫头猫 }); 207927dbe93S猫头猫 _instance = e?.instance ?? { 208927dbe93S猫头猫 _path: '', 209927dbe93S猫头猫 platform: '', 210927dbe93S猫头猫 appVersion: '', 21120e6a092S猫头猫 async getMediaSource() { 212927dbe93S猫头猫 return null; 213927dbe93S猫头猫 }, 214927dbe93S猫头猫 async search() { 215927dbe93S猫头猫 return {}; 216927dbe93S猫头猫 }, 217927dbe93S猫头猫 async getAlbumInfo() { 218927dbe93S猫头猫 return null; 219927dbe93S猫头猫 }, 220927dbe93S猫头猫 }; 221927dbe93S猫头猫 } 222927dbe93S猫头猫 this.instance = _instance; 223927dbe93S猫头猫 this.path = pluginPath; 224927dbe93S猫头猫 this.name = _instance.platform; 225ab8941d9S猫头猫 if ( 226ab8941d9S猫头猫 this.instance.platform === '' || 227ab8941d9S猫头猫 this.instance.platform === undefined 228ab8941d9S猫头猫 ) { 229927dbe93S猫头猫 this.hash = ''; 230927dbe93S猫头猫 } else { 23174d0cf81S猫头猫 if (typeof funcCode === 'string') { 232927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 23374d0cf81S猫头猫 } else { 23474d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 23574d0cf81S猫头猫 } 236927dbe93S猫头猫 } 237927dbe93S猫头猫 238927dbe93S猫头猫 // 放在最后 239927dbe93S猫头猫 this.methods = new PluginMethods(this); 240927dbe93S猫头猫 } 241927dbe93S猫头猫 242927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 243927dbe93S猫头猫 /** 版本号校验 */ 244927dbe93S猫头猫 if ( 245927dbe93S猫头猫 _instance.appVersion && 246927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 247927dbe93S猫头猫 ) { 248927dbe93S猫头猫 throw { 249927dbe93S猫头猫 instance: _instance, 250927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 251927dbe93S猫头猫 }; 252927dbe93S猫头猫 } 253927dbe93S猫头猫 return true; 254927dbe93S猫头猫 } 255927dbe93S猫头猫} 256d5bfeb7eS猫头猫//#endregion 257927dbe93S猫头猫 258d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 259927dbe93S猫头猫/** 有缓存等信息 */ 260927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 261927dbe93S猫头猫 private plugin; 262927dbe93S猫头猫 constructor(plugin: Plugin) { 263927dbe93S猫头猫 this.plugin = plugin; 264927dbe93S猫头猫 } 265927dbe93S猫头猫 /** 搜索 */ 266927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 267927dbe93S猫头猫 query: string, 268927dbe93S猫头猫 page: number, 269927dbe93S猫头猫 type: T, 270927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 271927dbe93S猫头猫 if (!this.plugin.instance.search) { 272927dbe93S猫头猫 return { 273927dbe93S猫头猫 isEnd: true, 274927dbe93S猫头猫 data: [], 275927dbe93S猫头猫 }; 276927dbe93S猫头猫 } 277927dbe93S猫头猫 2784060c00aS猫头猫 const result = 2794060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 280927dbe93S猫头猫 if (Array.isArray(result.data)) { 281927dbe93S猫头猫 result.data.forEach(_ => { 282927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 283927dbe93S猫头猫 }); 284927dbe93S猫头猫 return { 285927dbe93S猫头猫 isEnd: result.isEnd ?? true, 286927dbe93S猫头猫 data: result.data, 287927dbe93S猫头猫 }; 288927dbe93S猫头猫 } 289927dbe93S猫头猫 return { 290927dbe93S猫头猫 isEnd: true, 291927dbe93S猫头猫 data: [], 292927dbe93S猫头猫 }; 293927dbe93S猫头猫 } 294927dbe93S猫头猫 295927dbe93S猫头猫 /** 获取真实源 */ 29620e6a092S猫头猫 async getMediaSource( 297927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 298abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 299927dbe93S猫头猫 retryCount = 1, 300dc160d50S猫头猫 notUpdateCache = false, 301192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 302927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 30343eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 304927dbe93S猫头猫 const localPath = 30543eb30bfS猫头猫 mediaExtra?.localPath || 30643eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3070e4173cdS猫头猫 getInternalData<string>( 3080e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3090e4173cdS猫头猫 InternalDataType.LOCALPATH, 3100e4173cdS猫头猫 ); 311*5353b473S猫头猫 if (localPath && (await getInfoAsync(localPath)).exists) { 3120e4173cdS猫头猫 trace('本地播放', localPath); 31343eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 31443eb30bfS猫头猫 // 修正一下本地数据 31543eb30bfS猫头猫 MediaExtra.update(musicItem, { 31643eb30bfS猫头猫 localPath, 31743eb30bfS猫头猫 }); 31843eb30bfS猫头猫 } 319927dbe93S猫头猫 return { 32011908939S猫头猫 url: addFileScheme(localPath), 321927dbe93S猫头猫 }; 32243eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 32343eb30bfS猫头猫 MediaExtra.update(musicItem, { 32443eb30bfS猫头猫 localPath: undefined, 32543eb30bfS猫头猫 }); 326927dbe93S猫头猫 } 327a84a85c5S猫头猫 3287993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 329f5935920S猫头猫 throw new Error('本地音乐不存在'); 330f5935920S猫头猫 } 331927dbe93S猫头猫 // 2. 缓存播放 33243eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 33343eb30bfS猫头猫 musicItem, 33443eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 335985f8e75S猫头猫 const pluginCacheControl = 336985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 337cfa0fc07S猫头猫 if ( 338cfa0fc07S猫头猫 mediaCache && 33943eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34048f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34148f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 342ef714860S猫头猫 Network.isOffline())) 343cfa0fc07S猫头猫 ) { 3445276aef9S猫头猫 trace('播放', '缓存播放'); 34543eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 346927dbe93S猫头猫 return { 34743eb30bfS猫头猫 url: qualityInfo!.url, 348927dbe93S猫头猫 headers: mediaCache.headers, 3494060c00aS猫头猫 userAgent: 3504060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 351927dbe93S猫头猫 }; 352927dbe93S猫头猫 } 353927dbe93S猫头猫 // 3. 插件解析 35420e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 355a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 356a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 357a7b42a4cS猫头猫 ); 358a7b42a4cS猫头猫 return { 359a7b42a4cS猫头猫 url: url, 360a7b42a4cS猫头猫 headers: auth 361a7b42a4cS猫头猫 ? { 362a7b42a4cS猫头猫 Authorization: auth, 363a7b42a4cS猫头猫 } 364a7b42a4cS猫头猫 : undefined, 365a7b42a4cS猫头猫 }; 366927dbe93S猫头猫 } 367927dbe93S猫头猫 try { 368abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 369abaede57S猫头猫 musicItem, 370abaede57S猫头猫 quality, 371abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 372927dbe93S猫头猫 if (!url) { 373a28eac61S猫头猫 throw new Error('NOT RETRY'); 374927dbe93S猫头猫 } 3755276aef9S猫头猫 trace('播放', '插件播放'); 376927dbe93S猫头猫 const result = { 377927dbe93S猫头猫 url, 378927dbe93S猫头猫 headers, 379927dbe93S猫头猫 userAgent: headers?.['user-agent'], 380cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 381a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 382a7b42a4cS猫头猫 if (authFormattedResult.auth) { 383a7b42a4cS猫头猫 result.url = authFormattedResult.url; 384a7b42a4cS猫头猫 result.headers = { 385a7b42a4cS猫头猫 ...(result.headers ?? {}), 386a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 387a7b42a4cS猫头猫 }; 388a7b42a4cS猫头猫 } 389927dbe93S猫头猫 390dc160d50S猫头猫 if ( 391dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 392dc160d50S猫头猫 !notUpdateCache 393dc160d50S猫头猫 ) { 39443eb30bfS猫头猫 // 更新缓存 39543eb30bfS猫头猫 const cacheSource = { 39643eb30bfS猫头猫 headers: result.headers, 39743eb30bfS猫头猫 userAgent: result.userAgent, 39843eb30bfS猫头猫 url, 39943eb30bfS猫头猫 }; 40043eb30bfS猫头猫 let realMusicItem = { 40143eb30bfS猫头猫 ...musicItem, 40243eb30bfS猫头猫 ...(mediaCache || {}), 40343eb30bfS猫头猫 }; 40443eb30bfS猫头猫 realMusicItem.source = { 40543eb30bfS猫头猫 ...(realMusicItem.source || {}), 40643eb30bfS猫头猫 [quality]: cacheSource, 40743eb30bfS猫头猫 }; 40843eb30bfS猫头猫 40943eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 410752ffc5aS猫头猫 } 411927dbe93S猫头猫 return result; 412927dbe93S猫头猫 } catch (e: any) { 413a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 414927dbe93S猫头猫 await delay(150); 415abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 416927dbe93S猫头猫 } 417927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 418ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 419192ae2b0S猫头猫 return null; 420927dbe93S猫头猫 } 421927dbe93S猫头猫 } 422927dbe93S猫头猫 423927dbe93S猫头猫 /** 获取音乐详情 */ 424927dbe93S猫头猫 async getMusicInfo( 425927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 42674d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 427927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 428d704daedS猫头猫 return null; 429927dbe93S猫头猫 } 43074d0cf81S猫头猫 try { 431927dbe93S猫头猫 return ( 432927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4337993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 434d704daedS猫头猫 ) ?? null 435927dbe93S猫头猫 ); 436ea6d708fS猫头猫 } catch (e: any) { 437ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 438d704daedS猫头猫 return null; 43974d0cf81S猫头猫 } 440927dbe93S猫头猫 } 441927dbe93S猫头猫 4427e883dbbS猫头猫 /** 4437e883dbbS猫头猫 * 4447e883dbbS猫头猫 * getLyric(musicItem) => { 4457e883dbbS猫头猫 * lyric: string; 4467e883dbbS猫头猫 * trans: string; 4477e883dbbS猫头猫 * } 4487e883dbbS猫头猫 * 4497e883dbbS猫头猫 */ 450927dbe93S猫头猫 /** 获取歌词 */ 451927dbe93S猫头猫 async getLyric( 45243eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 453927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 45443eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 4555589cdf3S猫头猫 const meta = MediaExtra.get(originalMusicItem); 45643eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 457927dbe93S猫头猫 if (meta && meta.associatedLrc) { 45843eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 45943eb30bfS猫头猫 } else { 46043eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 461927dbe93S猫头猫 } 46243eb30bfS猫头猫 46343eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4647a91f04fS猫头猫 musicItem, 46543eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 46643eb30bfS猫头猫 46743eb30bfS猫头猫 /** 原始歌词文本 */ 4687e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4697e883dbbS猫头猫 let translation: string | null = null; 47043eb30bfS猫头猫 47143eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 47243eb30bfS猫头猫 if (musicItemCache?.lyric) { 47343eb30bfS猫头猫 // 缓存的远程结果 47443eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 47543eb30bfS猫头猫 musicItemCache.lyric || null; 47643eb30bfS猫头猫 // 缓存的本地结果 47743eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 47843eb30bfS猫头猫 musicItemCache.$localLyric || null; 47943eb30bfS猫头猫 4807e883dbbS猫头猫 // 优先用缓存的结果 4817e883dbbS猫头猫 if (cacheLyric.rawLrc || cacheLyric.translation) { 48243eb30bfS猫头猫 return { 4837e883dbbS猫头猫 rawLrc: cacheLyric.rawLrc, 4847e883dbbS猫头猫 translation: cacheLyric.translation, 48543eb30bfS猫头猫 }; 48643eb30bfS猫头猫 } 48743eb30bfS猫头猫 4887e883dbbS猫头猫 // 本地其实是缓存的路径 4897e883dbbS猫头猫 if (localLyric) { 4907e883dbbS猫头猫 let needRefetch = false; 4917e883dbbS猫头猫 if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) { 4927e883dbbS猫头猫 rawLrc = await readFile(localLyric.rawLrc, 'utf8'); 4937e883dbbS猫头猫 } else if (localLyric.rawLrc) { 4947e883dbbS猫头猫 needRefetch = true; 4957e883dbbS猫头猫 } 4967e883dbbS猫头猫 if ( 4977e883dbbS猫头猫 localLyric.translation && 4987e883dbbS猫头猫 (await exists(localLyric.translation)) 4997e883dbbS猫头猫 ) { 5007e883dbbS猫头猫 translation = await readFile( 5017e883dbbS猫头猫 localLyric.translation, 5027e883dbbS猫头猫 'utf8', 5037e883dbbS猫头猫 ); 5047e883dbbS猫头猫 } else if (localLyric.translation) { 5057e883dbbS猫头猫 needRefetch = true; 50643eb30bfS猫头猫 } 50743eb30bfS猫头猫 5087e883dbbS猫头猫 if (!needRefetch && (rawLrc || translation)) { 50943eb30bfS猫头猫 return { 5107e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5117e883dbbS猫头猫 translation: translation || undefined, 51243eb30bfS猫头猫 }; 51343eb30bfS猫头猫 } 51443eb30bfS猫头猫 } 51543eb30bfS猫头猫 } 51643eb30bfS猫头猫 51743eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 51843eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 51943eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 52043eb30bfS猫头猫 lrcSource = 52143eb30bfS猫头猫 (await this.plugin.instance 5227e883dbbS猫头猫 ?.getLyric?.(resetMediaItem(musicItem, undefined, true)) 52343eb30bfS猫头猫 ?.catch(() => null)) || null; 52443eb30bfS猫头猫 } else { 52543eb30bfS猫头猫 lrcSource = 52643eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 52743eb30bfS猫头猫 ?.instance?.getLyric?.( 52843eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 52943eb30bfS猫头猫 ) 53043eb30bfS猫头猫 ?.catch(() => null)) || null; 53143eb30bfS猫头猫 } 53243eb30bfS猫头猫 53343eb30bfS猫头猫 if (lrcSource) { 5347e883dbbS猫头猫 rawLrc = lrcSource?.rawLrc || rawLrc; 5357e883dbbS猫头猫 translation = lrcSource?.translation || null; 53643eb30bfS猫头猫 5377e883dbbS猫头猫 const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc; 53843eb30bfS猫头猫 5397e883dbbS猫头猫 // 本地的文件名 5407e883dbbS猫头猫 let filename: string | undefined = `${ 5417e883dbbS猫头猫 pathConst.lrcCachePath 5427e883dbbS猫头猫 }${nanoid()}.lrc`; 5437e883dbbS猫头猫 let filenameTrans: string | undefined = `${ 5447e883dbbS猫头猫 pathConst.lrcCachePath 5457e883dbbS猫头猫 }${nanoid()}.lrc`; 54643eb30bfS猫头猫 5477e883dbbS猫头猫 // 旧版本兼容 5487e883dbbS猫头猫 if (!(rawLrc || translation)) { 5497e883dbbS猫头猫 if (deprecatedLrcUrl) { 55043eb30bfS猫头猫 rawLrc = ( 5517e883dbbS猫头猫 await axios 5527e883dbbS猫头猫 .get(deprecatedLrcUrl, {timeout: 3000}) 5537e883dbbS猫头猫 .catch(() => null) 55443eb30bfS猫头猫 )?.data; 5557e883dbbS猫头猫 } else if (musicItem.rawLrc) { 5567e883dbbS猫头猫 rawLrc = musicItem.rawLrc; 5577e883dbbS猫头猫 } 55843eb30bfS猫头猫 } 55943eb30bfS猫头猫 56043eb30bfS猫头猫 if (rawLrc) { 56143eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 56243eb30bfS猫头猫 } else { 5637e883dbbS猫头猫 filename = undefined; 5647e883dbbS猫头猫 } 5657e883dbbS猫头猫 if (translation) { 5667e883dbbS猫头猫 await writeFile(filenameTrans, translation, 'utf8'); 5677e883dbbS猫头猫 } else { 5687e883dbbS猫头猫 filenameTrans = undefined; 5697a91f04fS猫头猫 } 5707a91f04fS猫头猫 5717e883dbbS猫头猫 if (rawLrc || translation) { 5727e883dbbS猫头猫 MediaCache.setMediaCache( 5737e883dbbS猫头猫 produce(musicItemCache || musicItem, draft => { 5747e883dbbS猫头猫 musicItemCache?.$localLyric?.rawLrc; 5757e883dbbS猫头猫 objectPath.set(draft, '$localLyric.rawLrc', filename); 57643eb30bfS猫头猫 objectPath.set( 57743eb30bfS猫头猫 draft, 5787e883dbbS猫头猫 '$localLyric.translation', 5797e883dbbS猫头猫 filenameTrans, 58043eb30bfS猫头猫 ); 58143eb30bfS猫头猫 return draft; 58243eb30bfS猫头猫 }), 58343eb30bfS猫头猫 ); 584927dbe93S猫头猫 return { 5857e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5867e883dbbS猫头猫 translation: translation || undefined, 587927dbe93S猫头猫 }; 588927dbe93S猫头猫 } 589927dbe93S猫头猫 } 59043eb30bfS猫头猫 5913a6f67b1S猫头猫 // 6. 如果是本地文件 59243eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 59343eb30bfS猫头猫 if ( 59443eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 59543eb30bfS猫头猫 isDownloaded 59643eb30bfS猫头猫 ) { 5977e883dbbS猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 59843eb30bfS猫头猫 59943eb30bfS猫头猫 console.log('本地文件歌词'); 60043eb30bfS猫头猫 6013a6f67b1S猫头猫 if (res) { 6023a6f67b1S猫头猫 return res; 6033a6f67b1S猫头猫 } 6043a6f67b1S猫头猫 } 605ea6d708fS猫头猫 devLog('warn', '无歌词'); 606927dbe93S猫头猫 607927dbe93S猫头猫 return null; 608927dbe93S猫头猫 } 609927dbe93S猫头猫 610927dbe93S猫头猫 /** 获取歌词文本 */ 611927dbe93S猫头猫 async getLyricText( 612927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 613927dbe93S猫头猫 ): Promise<string | undefined> { 6147e883dbbS猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 615927dbe93S猫头猫 } 616927dbe93S猫头猫 617927dbe93S猫头猫 /** 获取专辑信息 */ 618927dbe93S猫头猫 async getAlbumInfo( 619927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 620f9afcc0dS猫头猫 page: number = 1, 621f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 622927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 623f9afcc0dS猫头猫 return { 624f9afcc0dS猫头猫 albumItem, 625f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 626f2a4767cS猫头猫 resetMediaItem, 627f2a4767cS猫头猫 this.plugin.name, 628f2a4767cS猫头猫 true, 629f2a4767cS猫头猫 ), 630f9afcc0dS猫头猫 isEnd: true, 631f9afcc0dS猫头猫 }; 632927dbe93S猫头猫 } 633927dbe93S猫头猫 try { 634927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 635927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 636f9afcc0dS猫头猫 page, 637927dbe93S猫头猫 ); 6385276aef9S猫头猫 if (!result) { 6395276aef9S猫头猫 throw new Error(); 6405276aef9S猫头猫 } 641927dbe93S猫头猫 result?.musicList?.forEach(_ => { 642927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 64396744680S猫头猫 _.album = albumItem.title; 644927dbe93S猫头猫 }); 6455276aef9S猫头猫 646f9afcc0dS猫头猫 if (page <= 1) { 647f9afcc0dS猫头猫 // 合并信息 648f9afcc0dS猫头猫 return { 649f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 650f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 651f9afcc0dS猫头猫 musicList: result.musicList, 652f9afcc0dS猫头猫 }; 653f9afcc0dS猫头猫 } else { 654f9afcc0dS猫头猫 return { 655f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 656f9afcc0dS猫头猫 musicList: result.musicList, 657f9afcc0dS猫头猫 }; 658f9afcc0dS猫头猫 } 6594394410dS猫头猫 } catch (e: any) { 6604394410dS猫头猫 trace('获取专辑信息失败', e?.message); 661ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 662ea6d708fS猫头猫 663f9afcc0dS猫头猫 return null; 664927dbe93S猫头猫 } 665927dbe93S猫头猫 } 666927dbe93S猫头猫 6675830c002S猫头猫 /** 获取歌单信息 */ 6685830c002S猫头猫 async getMusicSheetInfo( 6695830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 6705830c002S猫头猫 page: number = 1, 6715830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 6725281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 6735830c002S猫头猫 return { 6745830c002S猫头猫 sheetItem, 6755830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 6765830c002S猫头猫 isEnd: true, 6775830c002S猫头猫 }; 6785830c002S猫头猫 } 6795830c002S猫头猫 try { 6805830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 6815830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 6825830c002S猫头猫 page, 6835830c002S猫头猫 ); 6845830c002S猫头猫 if (!result) { 6855830c002S猫头猫 throw new Error(); 6865830c002S猫头猫 } 6875830c002S猫头猫 result?.musicList?.forEach(_ => { 6885830c002S猫头猫 resetMediaItem(_, this.plugin.name); 6895830c002S猫头猫 }); 6905830c002S猫头猫 6915830c002S猫头猫 if (page <= 1) { 6925830c002S猫头猫 // 合并信息 6935830c002S猫头猫 return { 6945830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 6955830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 6965830c002S猫头猫 musicList: result.musicList, 6975830c002S猫头猫 }; 6985830c002S猫头猫 } else { 6995830c002S猫头猫 return { 7005830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7015830c002S猫头猫 musicList: result.musicList, 7025830c002S猫头猫 }; 7035830c002S猫头猫 } 7045830c002S猫头猫 } catch (e: any) { 7055830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7065830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7075830c002S猫头猫 7085830c002S猫头猫 return null; 7095830c002S猫头猫 } 7105830c002S猫头猫 } 7115830c002S猫头猫 712927dbe93S猫头猫 /** 查询作者信息 */ 713efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 714927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 715927dbe93S猫头猫 page: number, 716927dbe93S猫头猫 type: T, 717927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 718efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 719927dbe93S猫头猫 return { 720927dbe93S猫头猫 isEnd: true, 721927dbe93S猫头猫 data: [], 722927dbe93S猫头猫 }; 723927dbe93S猫头猫 } 724927dbe93S猫头猫 try { 725efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 726927dbe93S猫头猫 artistItem, 727927dbe93S猫头猫 page, 728927dbe93S猫头猫 type, 729927dbe93S猫头猫 ); 730927dbe93S猫头猫 if (!result.data) { 731927dbe93S猫头猫 return { 732927dbe93S猫头猫 isEnd: true, 733927dbe93S猫头猫 data: [], 734927dbe93S猫头猫 }; 735927dbe93S猫头猫 } 736927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 737927dbe93S猫头猫 return { 738927dbe93S猫头猫 isEnd: result.isEnd ?? true, 739927dbe93S猫头猫 data: result.data, 740927dbe93S猫头猫 }; 7414394410dS猫头猫 } catch (e: any) { 7424394410dS猫头猫 trace('查询作者信息失败', e?.message); 743ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 744ea6d708fS猫头猫 745927dbe93S猫头猫 throw e; 746927dbe93S猫头猫 } 747927dbe93S猫头猫 } 74808380090S猫头猫 74908380090S猫头猫 /** 导入歌单 */ 75008380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 75108380090S猫头猫 try { 75208380090S猫头猫 const result = 75308380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 75408380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 75508380090S猫头猫 return result; 756ea6d708fS猫头猫 } catch (e: any) { 7570e4173cdS猫头猫 console.log(e); 758ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 759ea6d708fS猫头猫 76008380090S猫头猫 return []; 76108380090S猫头猫 } 76208380090S猫头猫 } 7634d9d3c4cS猫头猫 /** 导入单曲 */ 7644d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 7654d9d3c4cS猫头猫 try { 7664d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 7674d9d3c4cS猫头猫 urlLike, 7684d9d3c4cS猫头猫 ); 7694d9d3c4cS猫头猫 if (!result) { 7704d9d3c4cS猫头猫 throw new Error(); 7714d9d3c4cS猫头猫 } 7724d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 7734d9d3c4cS猫头猫 return result; 774ea6d708fS猫头猫 } catch (e: any) { 775ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 776ea6d708fS猫头猫 7774d9d3c4cS猫头猫 return null; 7784d9d3c4cS猫头猫 } 7794d9d3c4cS猫头猫 } 780d52aa40eS猫头猫 /** 获取榜单 */ 78192b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 782d52aa40eS猫头猫 try { 783d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 784d52aa40eS猫头猫 if (!result) { 785d52aa40eS猫头猫 throw new Error(); 786d52aa40eS猫头猫 } 787d52aa40eS猫头猫 return result; 788d52aa40eS猫头猫 } catch (e: any) { 789d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 790d52aa40eS猫头猫 return []; 791d52aa40eS猫头猫 } 792d52aa40eS猫头猫 } 793d52aa40eS猫头猫 /** 获取榜单详情 */ 794d52aa40eS猫头猫 async getTopListDetail( 79592b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 796956ee1b7S猫头猫 page: number, 797956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 798d52aa40eS猫头猫 try { 799d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 800d52aa40eS猫头猫 topListItem, 801956ee1b7S猫头猫 page, 802d52aa40eS猫头猫 ); 803d52aa40eS猫头猫 if (!result) { 804d52aa40eS猫头猫 throw new Error(); 805d52aa40eS猫头猫 } 806d384662fS猫头猫 if (result.musicList) { 807d384662fS猫头猫 result.musicList.forEach(_ => 808d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 809d384662fS猫头猫 ); 810d384662fS猫头猫 } 811956ee1b7S猫头猫 if (result.isEnd !== false) { 812956ee1b7S猫头猫 result.isEnd = true; 813956ee1b7S猫头猫 } 814d52aa40eS猫头猫 return result; 815d52aa40eS猫头猫 } catch (e: any) { 816d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 817d52aa40eS猫头猫 return { 818956ee1b7S猫头猫 isEnd: true, 819956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 820d52aa40eS猫头猫 musicList: [], 821d52aa40eS猫头猫 }; 822d52aa40eS猫头猫 } 823d52aa40eS猫头猫 } 824ceb900cdS猫头猫 8255830c002S猫头猫 /** 获取推荐歌单的tag */ 826ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 827ceb900cdS猫头猫 try { 828ceb900cdS猫头猫 const result = 829ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 830ceb900cdS猫头猫 if (!result) { 831ceb900cdS猫头猫 throw new Error(); 832ceb900cdS猫头猫 } 833ceb900cdS猫头猫 return result; 834ceb900cdS猫头猫 } catch (e: any) { 835ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 836ceb900cdS猫头猫 return { 837ceb900cdS猫头猫 data: [], 838ceb900cdS猫头猫 }; 839ceb900cdS猫头猫 } 840ceb900cdS猫头猫 } 8415830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 842ceb900cdS猫头猫 async getRecommendSheetsByTag( 843ceb900cdS猫头猫 tagItem: ICommon.IUnique, 844ceb900cdS猫头猫 page?: number, 845ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 846ceb900cdS猫头猫 try { 847ceb900cdS猫头猫 const result = 848ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 849ceb900cdS猫头猫 tagItem, 850ceb900cdS猫头猫 page ?? 1, 851ceb900cdS猫头猫 ); 852ceb900cdS猫头猫 if (!result) { 853ceb900cdS猫头猫 throw new Error(); 854ceb900cdS猫头猫 } 855ceb900cdS猫头猫 if (result.isEnd !== false) { 856ceb900cdS猫头猫 result.isEnd = true; 857ceb900cdS猫头猫 } 858ceb900cdS猫头猫 if (!result.data) { 859ceb900cdS猫头猫 result.data = []; 860ceb900cdS猫头猫 } 861ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 862ceb900cdS猫头猫 863ceb900cdS猫头猫 return result; 864ceb900cdS猫头猫 } catch (e: any) { 865ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 866ceb900cdS猫头猫 return { 867ceb900cdS猫头猫 isEnd: true, 868ceb900cdS猫头猫 data: [], 869ceb900cdS猫头猫 }; 870ceb900cdS猫头猫 } 871ceb900cdS猫头猫 } 872927dbe93S猫头猫} 873d5bfeb7eS猫头猫//#endregion 8741a5528a0S猫头猫 875927dbe93S猫头猫let plugins: Array<Plugin> = []; 876927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 87774d0cf81S猫头猫 878d5bfeb7eS猫头猫//#region 本地音乐插件 87974d0cf81S猫头猫/** 本地插件 */ 88074d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 8810e4173cdS猫头猫 return { 882d5bfeb7eS猫头猫 platform: localPluginPlatform, 88374d0cf81S猫头猫 _path: '', 88474d0cf81S猫头猫 async getMusicInfo(musicBase) { 88574d0cf81S猫头猫 const localPath = getInternalData<string>( 88674d0cf81S猫头猫 musicBase, 88774d0cf81S猫头猫 InternalDataType.LOCALPATH, 8880e4173cdS猫头猫 ); 88974d0cf81S猫头猫 if (localPath) { 89074d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 89174d0cf81S猫头猫 return { 89274d0cf81S猫头猫 artwork: coverImg, 89374d0cf81S猫头猫 }; 89474d0cf81S猫头猫 } 89574d0cf81S猫头猫 return null; 89674d0cf81S猫头猫 }, 8977993f90eS猫头猫 async getLyric(musicBase) { 8987993f90eS猫头猫 const localPath = getInternalData<string>( 8997993f90eS猫头猫 musicBase, 9007993f90eS猫头猫 InternalDataType.LOCALPATH, 9017993f90eS猫头猫 ); 9023a6f67b1S猫头猫 let rawLrc: string | null = null; 9037993f90eS猫头猫 if (localPath) { 9043a6f67b1S猫头猫 // 读取内嵌歌词 9053a6f67b1S猫头猫 try { 9063a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 9073a6f67b1S猫头猫 } catch (e) { 908a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 9097993f90eS猫头猫 } 9103a6f67b1S猫头猫 if (!rawLrc) { 9113a6f67b1S猫头猫 // 读取配置歌词 9123a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 9133a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 9143a6f67b1S猫头猫 9153a6f67b1S猫头猫 try { 9163a6f67b1S猫头猫 if (await exists(lrcPath)) { 9173a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 9183a6f67b1S猫头猫 } 9193a6f67b1S猫头猫 } catch {} 9203a6f67b1S猫头猫 } 9213a6f67b1S猫头猫 } 9223a6f67b1S猫头猫 9233a6f67b1S猫头猫 return rawLrc 9243a6f67b1S猫头猫 ? { 9253a6f67b1S猫头猫 rawLrc, 9263a6f67b1S猫头猫 } 9273a6f67b1S猫头猫 : null; 9287993f90eS猫头猫 }, 929a84a85c5S猫头猫 async importMusicItem(urlLike) { 930a84a85c5S猫头猫 let meta: any = {}; 931a84a85c5S猫头猫 try { 932a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 933a84a85c5S猫头猫 } catch {} 934*5353b473S猫头猫 const stat = await getInfoAsync(urlLike, { 935*5353b473S猫头猫 md5: true, 936*5353b473S猫头猫 }); 937*5353b473S猫头猫 let id: string; 938*5353b473S猫头猫 if (stat.exists) { 939*5353b473S猫头猫 id = stat.md5 || nanoid(); 940*5353b473S猫头猫 } else { 941*5353b473S猫头猫 id = nanoid(); 942*5353b473S猫头猫 } 943a84a85c5S猫头猫 return { 944a84a85c5S猫头猫 id: id, 945a84a85c5S猫头猫 platform: '本地', 946a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 947a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 948*5353b473S猫头猫 duration: parseInt(meta?.duration ?? '0', 10) / 1000, 949a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 950a84a85c5S猫头猫 artwork: '', 951a84a85c5S猫头猫 [internalSerializeKey]: { 952a84a85c5S猫头猫 localPath: urlLike, 953a84a85c5S猫头猫 }, 954a84a85c5S猫头猫 }; 955a84a85c5S猫头猫 }, 95611908939S猫头猫 async getMediaSource(musicItem, quality) { 95711908939S猫头猫 if (quality === 'standard') { 95811908939S猫头猫 return { 95911908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 96011908939S猫头猫 }; 96111908939S猫头猫 } 96211908939S猫头猫 return null; 96311908939S猫头猫 }, 96474d0cf81S猫头猫 }; 96574d0cf81S猫头猫}, ''); 9667993f90eS猫头猫localFilePlugin.hash = localPluginHash; 967927dbe93S猫头猫 968d5bfeb7eS猫头猫//#endregion 969d5bfeb7eS猫头猫 970927dbe93S猫头猫async function setup() { 971927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 972927dbe93S猫头猫 try { 973927dbe93S猫头猫 // 加载插件 974927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 975927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 976927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 9771e263108S猫头猫 trace('初始化插件', _pluginUrl); 9781e263108S猫头猫 if ( 9791e263108S猫头猫 _pluginUrl.isFile() && 9801e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 9811e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 9821e263108S猫头猫 ) { 983927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 984927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 9854060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 9864060c00aS猫头猫 p => p.hash === plugin.hash, 9874060c00aS猫头猫 ); 988927dbe93S猫头猫 if (_pluginIndex !== -1) { 989927dbe93S猫头猫 // 重复插件,直接忽略 9900c266394S猫头猫 continue; 991927dbe93S猫头猫 } 992927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 993927dbe93S猫头猫 } 994927dbe93S猫头猫 } 995927dbe93S猫头猫 996927dbe93S猫头猫 plugins = _plugins; 997e08d37a3S猫头猫 /** 初始化meta信息 */ 998c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 999c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 1000c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 1001c2b3a262S猫头猫 for (let plugin of plugins) { 1002c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1003c2b3a262S猫头猫 plugin.state = 'disabled'; 1004c2b3a262S猫头猫 } 1005c2b3a262S猫头猫 } 1006c2b3a262S猫头猫 pluginStateMapper.notify(); 1007927dbe93S猫头猫 } catch (e: any) { 10084060c00aS猫头猫 ToastAndroid.show( 10094060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 10104060c00aS猫头猫 ToastAndroid.LONG, 10114060c00aS猫头猫 ); 10121a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1013927dbe93S猫头猫 throw e; 1014927dbe93S猫头猫 } 1015927dbe93S猫头猫} 1016927dbe93S猫头猫 1017e36e2599S猫头猫interface IInstallPluginConfig { 1018e36e2599S猫头猫 notCheckVersion?: boolean; 1019e36e2599S猫头猫} 1020e36e2599S猫头猫 1021927dbe93S猫头猫// 安装插件 1022e36e2599S猫头猫async function installPlugin( 1023e36e2599S猫头猫 pluginPath: string, 1024e36e2599S猫头猫 config?: IInstallPluginConfig, 1025e36e2599S猫头猫) { 102622c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1027927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1028e36e2599S猫头猫 1029e36e2599S猫头猫 if (funcCode) { 1030927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1031927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1032927dbe93S猫头猫 if (_pluginIndex !== -1) { 1033e36e2599S猫头猫 // 静默忽略 1034e36e2599S猫头猫 return plugin; 1035927dbe93S猫头猫 } 1036e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1037e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1038e36e2599S猫头猫 if ( 1039e36e2599S猫头猫 compare( 1040e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1041e36e2599S猫头猫 plugin.instance.version ?? '', 1042e36e2599S猫头猫 '>', 1043e36e2599S猫头猫 ) 1044e36e2599S猫头猫 ) { 1045e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1046e36e2599S猫头猫 } 1047e36e2599S猫头猫 } 1048e36e2599S猫头猫 1049927dbe93S猫头猫 if (plugin.hash !== '') { 1050927dbe93S猫头猫 const fn = nanoid(); 1051e36e2599S猫头猫 if (oldVersionPlugin) { 1052e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1053e36e2599S猫头猫 try { 1054e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1055e36e2599S猫头猫 } catch {} 1056e36e2599S猫头猫 } 1057927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1058927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1059927dbe93S猫头猫 plugin.path = _pluginPath; 1060927dbe93S猫头猫 plugins = plugins.concat(plugin); 1061927dbe93S猫头猫 pluginStateMapper.notify(); 1062a84a85c5S猫头猫 return plugin; 1063927dbe93S猫头猫 } 1064e36e2599S猫头猫 throw new Error('插件无法解析!'); 1065927dbe93S猫头猫 } 1066e36e2599S猫头猫 throw new Error('插件无法识别!'); 1067c2b3a262S猫头猫} 1068c2b3a262S猫头猫 1069ee781061S猫头猫const reqHeaders = { 1070ee781061S猫头猫 'Cache-Control': 'no-cache', 1071ee781061S猫头猫 Pragma: 'no-cache', 1072ee781061S猫头猫 Expires: '0', 1073ee781061S猫头猫}; 1074ee781061S猫头猫 1075c2b3a262S猫头猫async function installPluginFromUrl( 1076c2b3a262S猫头猫 url: string, 1077c2b3a262S猫头猫 config?: IInstallPluginConfig, 1078c2b3a262S猫头猫) { 107958992c6bS猫头猫 try { 1080ee781061S猫头猫 const funcCode = ( 1081ee781061S猫头猫 await axios.get(url, { 1082ee781061S猫头猫 headers: reqHeaders, 1083ee781061S猫头猫 }) 1084ee781061S猫头猫 ).data; 108558992c6bS猫头猫 if (funcCode) { 108658992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 108758992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 108858992c6bS猫头猫 if (_pluginIndex !== -1) { 10898b7ddca8S猫头猫 // 静默忽略 10908b7ddca8S猫头猫 return; 109158992c6bS猫头猫 } 109225c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1093c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 109425c1bd29S猫头猫 if ( 109525c1bd29S猫头猫 compare( 109625c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 109725c1bd29S猫头猫 plugin.instance.version ?? '', 109825c1bd29S猫头猫 '>', 109925c1bd29S猫头猫 ) 110025c1bd29S猫头猫 ) { 110125c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 110225c1bd29S猫头猫 } 110325c1bd29S猫头猫 } 110425c1bd29S猫头猫 110558992c6bS猫头猫 if (plugin.hash !== '') { 110658992c6bS猫头猫 const fn = nanoid(); 110758992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 110858992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 110958992c6bS猫头猫 plugin.path = _pluginPath; 111058992c6bS猫头猫 plugins = plugins.concat(plugin); 111125c1bd29S猫头猫 if (oldVersionPlugin) { 111225c1bd29S猫头猫 plugins = plugins.filter( 111325c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 111425c1bd29S猫头猫 ); 111525c1bd29S猫头猫 try { 111625c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 111725c1bd29S猫头猫 } catch {} 111825c1bd29S猫头猫 } 111958992c6bS猫头猫 pluginStateMapper.notify(); 112058992c6bS猫头猫 return; 112158992c6bS猫头猫 } 112274acbfc0S猫头猫 throw new Error('插件无法解析!'); 112358992c6bS猫头猫 } 112425c1bd29S猫头猫 } catch (e: any) { 1125ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 112658992c6bS猫头猫 errorLog('URL安装插件失败', e); 112725c1bd29S猫头猫 throw new Error(e?.message ?? ''); 112858992c6bS猫头猫 } 112958992c6bS猫头猫} 113058992c6bS猫头猫 1131927dbe93S猫头猫/** 卸载插件 */ 1132927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1133927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1134927dbe93S猫头猫 if (targetIndex !== -1) { 1135927dbe93S猫头猫 try { 113624e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1137927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1138927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1139927dbe93S猫头猫 pluginStateMapper.notify(); 114043eb30bfS猫头猫 // 防止其他重名 114124e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 11425589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 114324e5e74aS猫头猫 } 1144927dbe93S猫头猫 } catch {} 1145927dbe93S猫头猫 } 1146927dbe93S猫头猫} 1147927dbe93S猫头猫 114808882a77S猫头猫async function uninstallAllPlugins() { 114908882a77S猫头猫 await Promise.all( 115008882a77S猫头猫 plugins.map(async plugin => { 115108882a77S猫头猫 try { 115208882a77S猫头猫 const pluginName = plugin.name; 115308882a77S猫头猫 await unlink(plugin.path); 11545589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 115508882a77S猫头猫 } catch (e) {} 115608882a77S猫头猫 }), 115708882a77S猫头猫 ); 115808882a77S猫头猫 plugins = []; 115908882a77S猫头猫 pluginStateMapper.notify(); 1160e08d37a3S猫头猫 1161e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1162e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1163e08d37a3S猫头猫 .then(fns => { 1164e08d37a3S猫头猫 fns.forEach(fn => { 1165e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1166e08d37a3S猫头猫 }); 1167e08d37a3S猫头猫 }) 1168e08d37a3S猫头猫 .catch(emptyFunction); 116908882a77S猫头猫} 117008882a77S猫头猫 117125c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 117225c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 117325c1bd29S猫头猫 if (!updateUrl) { 117425c1bd29S猫头猫 throw new Error('没有更新源'); 117525c1bd29S猫头猫 } 117625c1bd29S猫头猫 try { 117725c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 117825c1bd29S猫头猫 } catch (e: any) { 117925c1bd29S猫头猫 if (e.message === '插件已安装') { 118025c1bd29S猫头猫 throw new Error('当前已是最新版本'); 118125c1bd29S猫头猫 } else { 118225c1bd29S猫头猫 throw e; 118325c1bd29S猫头猫 } 118425c1bd29S猫头猫 } 118525c1bd29S猫头猫} 118625c1bd29S猫头猫 1187927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 11882c595535S猫头猫 return getByName(mediaItem?.platform); 1189927dbe93S猫头猫} 1190927dbe93S猫头猫 1191927dbe93S猫头猫function getByHash(hash: string) { 11927993f90eS猫头猫 return hash === localPluginHash 11937993f90eS猫头猫 ? localFilePlugin 11947993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1195927dbe93S猫头猫} 1196927dbe93S猫头猫 1197927dbe93S猫头猫function getByName(name: string) { 11987993f90eS猫头猫 return name === localPluginPlatform 11990e4173cdS猫头猫 ? localFilePlugin 12000e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1201927dbe93S猫头猫} 1202927dbe93S猫头猫 1203927dbe93S猫头猫function getValidPlugins() { 1204927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1205927dbe93S猫头猫} 1206927dbe93S猫头猫 12072b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 12082b80a429S猫头猫 return plugins.filter( 12092b80a429S猫头猫 _ => 12102b80a429S猫头猫 _.state === 'enabled' && 12112b80a429S猫头猫 _.instance.search && 121239ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 121339ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 12142b80a429S猫头猫 : true), 12152b80a429S猫头猫 ); 1216efb9da24S猫头猫} 1217efb9da24S猫头猫 12182b80a429S猫头猫function getSortedSearchablePlugins( 12192b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 12202b80a429S猫头猫) { 12212b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1222e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1223e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1224e08d37a3S猫头猫 0 1225e08d37a3S猫头猫 ? -1 1226e08d37a3S猫头猫 : 1, 1227e08d37a3S猫头猫 ); 1228e08d37a3S猫头猫} 1229e08d37a3S猫头猫 123015feccc1S猫头猫function getTopListsablePlugins() { 123115feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 123215feccc1S猫头猫} 123315feccc1S猫头猫 123415feccc1S猫头猫function getSortedTopListsablePlugins() { 123515feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 123615feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 123715feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 123815feccc1S猫头猫 0 123915feccc1S猫头猫 ? -1 124015feccc1S猫头猫 : 1, 124115feccc1S猫头猫 ); 124215feccc1S猫头猫} 124315feccc1S猫头猫 1244ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1245ceb900cdS猫头猫 return plugins.filter( 1246ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1247ceb900cdS猫头猫 ); 1248ceb900cdS猫头猫} 1249ceb900cdS猫头猫 1250ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1251ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1252ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1253ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1254ceb900cdS猫头猫 0 1255ceb900cdS猫头猫 ? -1 1256ceb900cdS猫头猫 : 1, 1257ceb900cdS猫头猫 ); 1258ceb900cdS猫头猫} 1259ceb900cdS猫头猫 1260e08d37a3S猫头猫function useSortedPlugins() { 1261e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1262e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1263e08d37a3S猫头猫 126434588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 126534588741S猫头猫 [..._plugins].sort((a, b) => 1266e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1267e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1268e08d37a3S猫头猫 0 1269e08d37a3S猫头猫 ? -1 1270e08d37a3S猫头猫 : 1, 127134588741S猫头猫 ), 1272e08d37a3S猫头猫 ); 127334588741S猫头猫 127434588741S猫头猫 useEffect(() => { 1275d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 127634588741S猫头猫 setSortedPlugins( 127734588741S猫头猫 [..._plugins].sort((a, b) => 127834588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 127934588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 128034588741S猫头猫 0 128134588741S猫头猫 ? -1 128234588741S猫头猫 : 1, 128334588741S猫头猫 ), 128434588741S猫头猫 ); 1285d4cd40d8S猫头猫 }); 128634588741S猫头猫 }, [_plugins, _pluginMetaAll]); 128734588741S猫头猫 128834588741S猫头猫 return sortedPlugins; 1289e08d37a3S猫头猫} 1290e08d37a3S猫头猫 1291c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1292c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1293c2b3a262S猫头猫 if (target) { 1294c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1295c2b3a262S猫头猫 plugins = [...plugins]; 1296c2b3a262S猫头猫 pluginStateMapper.notify(); 1297c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1298c2b3a262S猫头猫 } 1299c2b3a262S猫头猫} 1300c2b3a262S猫头猫 1301927dbe93S猫头猫const PluginManager = { 1302927dbe93S猫头猫 setup, 1303927dbe93S猫头猫 installPlugin, 130458992c6bS猫头猫 installPluginFromUrl, 130525c1bd29S猫头猫 updatePlugin, 1306927dbe93S猫头猫 uninstallPlugin, 1307927dbe93S猫头猫 getByMedia, 1308927dbe93S猫头猫 getByHash, 1309927dbe93S猫头猫 getByName, 1310927dbe93S猫头猫 getValidPlugins, 1311efb9da24S猫头猫 getSearchablePlugins, 1312e08d37a3S猫头猫 getSortedSearchablePlugins, 131315feccc1S猫头猫 getTopListsablePlugins, 1314ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 131515feccc1S猫头猫 getSortedTopListsablePlugins, 13165276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1317e08d37a3S猫头猫 useSortedPlugins, 131808882a77S猫头猫 uninstallAllPlugins, 1319c2b3a262S猫头猫 setPluginEnabled, 13205276aef9S猫头猫}; 1321927dbe93S猫头猫 1322927dbe93S猫头猫export default PluginManager; 1323