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'; 425353b473S猫头猫import {getInfoAsync} from 'expo-file-system'; 4374d0cf81S猫头猫import Mp3Util from '@/native/mp3Util'; 44e08d37a3S猫头猫import {PluginMeta} from './pluginMeta'; 4534588741S猫头猫import {useEffect, useState} from 'react'; 4611908939S猫头猫import {addFileScheme, getFileName} from '@/utils/fileUtils'; 47a7b42a4cS猫头猫import {URL} from 'react-native-url-polyfill'; 48a7b42a4cS猫头猫import Base64 from '@/utils/base64'; 4943eb30bfS猫头猫import MediaCache from './mediaCache'; 505589cdf3S猫头猫import {produce} from 'immer'; 5143eb30bfS猫头猫import objectPath from 'object-path'; 52927dbe93S猫头猫 5361aca335S猫头猫axios.defaults.timeout = 2000; 54927dbe93S猫头猫 55927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 56927dbe93S猫头猫 57cfa0fc07S猫头猫export enum PluginStateCode { 58927dbe93S猫头猫 /** 版本不匹配 */ 59927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 60927dbe93S猫头猫 /** 无法解析 */ 61927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 62927dbe93S猫头猫} 63927dbe93S猫头猫 649c34d637S猫头猫const packages: Record<string, any> = { 659c34d637S猫头猫 cheerio, 669c34d637S猫头猫 'crypto-js': CryptoJs, 679c34d637S猫头猫 axios, 689c34d637S猫头猫 dayjs, 699c34d637S猫头猫 'big-integer': bigInt, 709c34d637S猫头猫 qs, 719c34d637S猫头猫 he, 723b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 7328ccceb7S猫头猫 webdav, 749c34d637S猫头猫}; 759c34d637S猫头猫 76b43683eaS猫头猫const _require = (packageName: string) => { 77b43683eaS猫头猫 let pkg = packages[packageName]; 78b43683eaS猫头猫 pkg.default = pkg; 79b43683eaS猫头猫 return pkg; 80b43683eaS猫头猫}; 819c34d637S猫头猫 8253f8cd8eS猫头猫const _consoleBind = function ( 8353f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 8453f8cd8eS猫头猫 ...args: any 8553f8cd8eS猫头猫) { 8653f8cd8eS猫头猫 const fn = console[method]; 8753f8cd8eS猫头猫 if (fn) { 8853f8cd8eS猫头猫 fn(...args); 8953f8cd8eS猫头猫 devLog(method, ...args); 9053f8cd8eS猫头猫 } 9153f8cd8eS猫头猫}; 9253f8cd8eS猫头猫 9353f8cd8eS猫头猫const _console = { 9453f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9553f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9653f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9753f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9853f8cd8eS猫头猫}; 9953f8cd8eS猫头猫 100a7b42a4cS猫头猫function formatAuthUrl(url: string) { 101a7b42a4cS猫头猫 const urlObj = new URL(url); 102a7b42a4cS猫头猫 103a7b42a4cS猫头猫 try { 104a7b42a4cS猫头猫 if (urlObj.username && urlObj.password) { 105a7b42a4cS猫头猫 const auth = `Basic ${Base64.btoa( 106a7b42a4cS猫头猫 `${decodeURIComponent(urlObj.username)}:${decodeURIComponent( 107a7b42a4cS猫头猫 urlObj.password, 108a7b42a4cS猫头猫 )}`, 109a7b42a4cS猫头猫 )}`; 110a7b42a4cS猫头猫 urlObj.username = ''; 111a7b42a4cS猫头猫 urlObj.password = ''; 112a7b42a4cS猫头猫 113a7b42a4cS猫头猫 return { 114a7b42a4cS猫头猫 url: urlObj.toString(), 115a7b42a4cS猫头猫 auth, 116a7b42a4cS猫头猫 }; 117a7b42a4cS猫头猫 } 118a7b42a4cS猫头猫 } catch (e) { 119a7b42a4cS猫头猫 return { 120a7b42a4cS猫头猫 url, 121a7b42a4cS猫头猫 }; 122a7b42a4cS猫头猫 } 123a7b42a4cS猫头猫 return { 124a7b42a4cS猫头猫 url, 125a7b42a4cS猫头猫 }; 126a7b42a4cS猫头猫} 127a7b42a4cS猫头猫 128d5bfeb7eS猫头猫//#region 插件类 129927dbe93S猫头猫export class Plugin { 130927dbe93S猫头猫 /** 插件名 */ 131927dbe93S猫头猫 public name: string; 132927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 133927dbe93S猫头猫 public hash: string; 134927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 135927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 136927dbe93S猫头猫 /** 插件状态信息 */ 137927dbe93S猫头猫 public stateCode?: PluginStateCode; 138927dbe93S猫头猫 /** 插件的实例 */ 139927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 140927dbe93S猫头猫 /** 插件路径 */ 141927dbe93S猫头猫 public path: string; 142927dbe93S猫头猫 /** 插件方法 */ 143927dbe93S猫头猫 public methods: PluginMethods; 144927dbe93S猫头猫 14574d0cf81S猫头猫 constructor( 14674d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 14774d0cf81S猫头猫 pluginPath: string, 14874d0cf81S猫头猫 ) { 149927dbe93S猫头猫 this.state = 'enabled'; 150927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1513b3d6357S猫头猫 const _module: any = {exports: {}}; 152927dbe93S猫头猫 try { 15374d0cf81S猫头猫 if (typeof funcCode === 'string') { 154e0caf342S猫头猫 // 插件的环境变量 155e0caf342S猫头猫 const env = { 156e0caf342S猫头猫 getUserVariables: () => { 157e0caf342S猫头猫 return ( 158e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 159e0caf342S猫头猫 ); 160e0caf342S猫头猫 }, 161e3fa9b3cS猫头猫 os: 'android', 162e0caf342S猫头猫 }; 163e0caf342S猫头猫 1644060c00aS猫头猫 // eslint-disable-next-line no-new-func 165927dbe93S猫头猫 _instance = Function(` 166927dbe93S猫头猫 'use strict'; 16791eb8fa8S猫头猫 return function(require, __musicfree_require, module, exports, console, env, URL) { 1689c34d637S猫头猫 ${funcCode} 169927dbe93S猫头猫 } 170e0caf342S猫头猫 `)()( 171e0caf342S猫头猫 _require, 172e0caf342S猫头猫 _require, 173e0caf342S猫头猫 _module, 174e0caf342S猫头猫 _module.exports, 175e0caf342S猫头猫 _console, 176e0caf342S猫头猫 env, 17791eb8fa8S猫头猫 URL, 178e0caf342S猫头猫 ); 1793b3d6357S猫头猫 if (_module.exports.default) { 1803b3d6357S猫头猫 _instance = _module.exports 1813b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1823b3d6357S猫头猫 } else { 1839c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1843b3d6357S猫头猫 } 18574d0cf81S猫头猫 } else { 18674d0cf81S猫头猫 _instance = funcCode(); 18774d0cf81S猫头猫 } 188c2b3a262S猫头猫 // 插件初始化后的一些操作 18995297592S猫头猫 if (Array.isArray(_instance.userVariables)) { 19095297592S猫头猫 _instance.userVariables = _instance.userVariables.filter( 19195297592S猫头猫 it => it?.key, 19295297592S猫头猫 ); 19395297592S猫头猫 } 194927dbe93S猫头猫 this.checkValid(_instance); 195927dbe93S猫头猫 } catch (e: any) { 196b43683eaS猫头猫 console.log(e); 197927dbe93S猫头猫 this.state = 'error'; 198927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 199927dbe93S猫头猫 if (e?.stateCode) { 200927dbe93S猫头猫 this.stateCode = e.stateCode; 201927dbe93S猫头猫 } 202927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 203927dbe93S猫头猫 stateCode: this.stateCode, 204927dbe93S猫头猫 message: e?.message, 205927dbe93S猫头猫 stack: e?.stack, 206927dbe93S猫头猫 }); 207927dbe93S猫头猫 _instance = e?.instance ?? { 208927dbe93S猫头猫 _path: '', 209927dbe93S猫头猫 platform: '', 210927dbe93S猫头猫 appVersion: '', 21120e6a092S猫头猫 async getMediaSource() { 212927dbe93S猫头猫 return null; 213927dbe93S猫头猫 }, 214927dbe93S猫头猫 async search() { 215927dbe93S猫头猫 return {}; 216927dbe93S猫头猫 }, 217927dbe93S猫头猫 async getAlbumInfo() { 218927dbe93S猫头猫 return null; 219927dbe93S猫头猫 }, 220927dbe93S猫头猫 }; 221927dbe93S猫头猫 } 222927dbe93S猫头猫 this.instance = _instance; 223927dbe93S猫头猫 this.path = pluginPath; 224927dbe93S猫头猫 this.name = _instance.platform; 225ab8941d9S猫头猫 if ( 226ab8941d9S猫头猫 this.instance.platform === '' || 227ab8941d9S猫头猫 this.instance.platform === undefined 228ab8941d9S猫头猫 ) { 229927dbe93S猫头猫 this.hash = ''; 230927dbe93S猫头猫 } else { 23174d0cf81S猫头猫 if (typeof funcCode === 'string') { 232927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 23374d0cf81S猫头猫 } else { 23474d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 23574d0cf81S猫头猫 } 236927dbe93S猫头猫 } 237927dbe93S猫头猫 238927dbe93S猫头猫 // 放在最后 239927dbe93S猫头猫 this.methods = new PluginMethods(this); 240927dbe93S猫头猫 } 241927dbe93S猫头猫 242927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 243927dbe93S猫头猫 /** 版本号校验 */ 244927dbe93S猫头猫 if ( 245927dbe93S猫头猫 _instance.appVersion && 246927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 247927dbe93S猫头猫 ) { 248927dbe93S猫头猫 throw { 249927dbe93S猫头猫 instance: _instance, 250927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 251927dbe93S猫头猫 }; 252927dbe93S猫头猫 } 253927dbe93S猫头猫 return true; 254927dbe93S猫头猫 } 255927dbe93S猫头猫} 256d5bfeb7eS猫头猫//#endregion 257927dbe93S猫头猫 258d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 259927dbe93S猫头猫/** 有缓存等信息 */ 260927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 261927dbe93S猫头猫 private plugin; 262927dbe93S猫头猫 constructor(plugin: Plugin) { 263927dbe93S猫头猫 this.plugin = plugin; 264927dbe93S猫头猫 } 265927dbe93S猫头猫 /** 搜索 */ 266927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 267927dbe93S猫头猫 query: string, 268927dbe93S猫头猫 page: number, 269927dbe93S猫头猫 type: T, 270927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 271927dbe93S猫头猫 if (!this.plugin.instance.search) { 272927dbe93S猫头猫 return { 273927dbe93S猫头猫 isEnd: true, 274927dbe93S猫头猫 data: [], 275927dbe93S猫头猫 }; 276927dbe93S猫头猫 } 277927dbe93S猫头猫 2784060c00aS猫头猫 const result = 2794060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 280927dbe93S猫头猫 if (Array.isArray(result.data)) { 281927dbe93S猫头猫 result.data.forEach(_ => { 282927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 283927dbe93S猫头猫 }); 284927dbe93S猫头猫 return { 285927dbe93S猫头猫 isEnd: result.isEnd ?? true, 286927dbe93S猫头猫 data: result.data, 287927dbe93S猫头猫 }; 288927dbe93S猫头猫 } 289927dbe93S猫头猫 return { 290927dbe93S猫头猫 isEnd: true, 291927dbe93S猫头猫 data: [], 292927dbe93S猫头猫 }; 293927dbe93S猫头猫 } 294927dbe93S猫头猫 295927dbe93S猫头猫 /** 获取真实源 */ 29620e6a092S猫头猫 async getMediaSource( 297927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 298abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 299927dbe93S猫头猫 retryCount = 1, 300dc160d50S猫头猫 notUpdateCache = false, 301192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 302927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 30343eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 304927dbe93S猫头猫 const localPath = 30543eb30bfS猫头猫 mediaExtra?.localPath || 30643eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3070e4173cdS猫头猫 getInternalData<string>( 3080e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3090e4173cdS猫头猫 InternalDataType.LOCALPATH, 3100e4173cdS猫头猫 ); 3115353b473S猫头猫 if (localPath && (await getInfoAsync(localPath)).exists) { 3120e4173cdS猫头猫 trace('本地播放', localPath); 31343eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 31443eb30bfS猫头猫 // 修正一下本地数据 31543eb30bfS猫头猫 MediaExtra.update(musicItem, { 31643eb30bfS猫头猫 localPath, 31743eb30bfS猫头猫 }); 31843eb30bfS猫头猫 } 319927dbe93S猫头猫 return { 32011908939S猫头猫 url: addFileScheme(localPath), 321927dbe93S猫头猫 }; 32243eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 32343eb30bfS猫头猫 MediaExtra.update(musicItem, { 32443eb30bfS猫头猫 localPath: undefined, 32543eb30bfS猫头猫 }); 326927dbe93S猫头猫 } 327a84a85c5S猫头猫 3287993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 329f5935920S猫头猫 throw new Error('本地音乐不存在'); 330f5935920S猫头猫 } 331927dbe93S猫头猫 // 2. 缓存播放 33243eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 33343eb30bfS猫头猫 musicItem, 33443eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 335985f8e75S猫头猫 const pluginCacheControl = 336985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 337cfa0fc07S猫头猫 if ( 338cfa0fc07S猫头猫 mediaCache && 33943eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34048f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34148f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 342ef714860S猫头猫 Network.isOffline())) 343cfa0fc07S猫头猫 ) { 3445276aef9S猫头猫 trace('播放', '缓存播放'); 34543eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 346927dbe93S猫头猫 return { 34743eb30bfS猫头猫 url: qualityInfo!.url, 348927dbe93S猫头猫 headers: mediaCache.headers, 3494060c00aS猫头猫 userAgent: 3504060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 351927dbe93S猫头猫 }; 352927dbe93S猫头猫 } 353927dbe93S猫头猫 // 3. 插件解析 35420e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 355a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 356a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 357a7b42a4cS猫头猫 ); 358a7b42a4cS猫头猫 return { 359a7b42a4cS猫头猫 url: url, 360a7b42a4cS猫头猫 headers: auth 361a7b42a4cS猫头猫 ? { 362a7b42a4cS猫头猫 Authorization: auth, 363a7b42a4cS猫头猫 } 364a7b42a4cS猫头猫 : undefined, 365a7b42a4cS猫头猫 }; 366927dbe93S猫头猫 } 367927dbe93S猫头猫 try { 368abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 369abaede57S猫头猫 musicItem, 370abaede57S猫头猫 quality, 371abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 372927dbe93S猫头猫 if (!url) { 373a28eac61S猫头猫 throw new Error('NOT RETRY'); 374927dbe93S猫头猫 } 3755276aef9S猫头猫 trace('播放', '插件播放'); 376927dbe93S猫头猫 const result = { 377927dbe93S猫头猫 url, 378927dbe93S猫头猫 headers, 379927dbe93S猫头猫 userAgent: headers?.['user-agent'], 380cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 381a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 382a7b42a4cS猫头猫 if (authFormattedResult.auth) { 383a7b42a4cS猫头猫 result.url = authFormattedResult.url; 384a7b42a4cS猫头猫 result.headers = { 385a7b42a4cS猫头猫 ...(result.headers ?? {}), 386a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 387a7b42a4cS猫头猫 }; 388a7b42a4cS猫头猫 } 389927dbe93S猫头猫 390dc160d50S猫头猫 if ( 391dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 392dc160d50S猫头猫 !notUpdateCache 393dc160d50S猫头猫 ) { 39443eb30bfS猫头猫 // 更新缓存 39543eb30bfS猫头猫 const cacheSource = { 39643eb30bfS猫头猫 headers: result.headers, 39743eb30bfS猫头猫 userAgent: result.userAgent, 39843eb30bfS猫头猫 url, 39943eb30bfS猫头猫 }; 40043eb30bfS猫头猫 let realMusicItem = { 40143eb30bfS猫头猫 ...musicItem, 40243eb30bfS猫头猫 ...(mediaCache || {}), 40343eb30bfS猫头猫 }; 40443eb30bfS猫头猫 realMusicItem.source = { 40543eb30bfS猫头猫 ...(realMusicItem.source || {}), 40643eb30bfS猫头猫 [quality]: cacheSource, 40743eb30bfS猫头猫 }; 40843eb30bfS猫头猫 40943eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 410752ffc5aS猫头猫 } 411927dbe93S猫头猫 return result; 412927dbe93S猫头猫 } catch (e: any) { 413a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 414927dbe93S猫头猫 await delay(150); 415abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 416927dbe93S猫头猫 } 417927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 418ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 419192ae2b0S猫头猫 return null; 420927dbe93S猫头猫 } 421927dbe93S猫头猫 } 422927dbe93S猫头猫 423927dbe93S猫头猫 /** 获取音乐详情 */ 424927dbe93S猫头猫 async getMusicInfo( 425927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 42674d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 427927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 428d704daedS猫头猫 return null; 429927dbe93S猫头猫 } 43074d0cf81S猫头猫 try { 431927dbe93S猫头猫 return ( 432927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4337993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 434d704daedS猫头猫 ) ?? null 435927dbe93S猫头猫 ); 436ea6d708fS猫头猫 } catch (e: any) { 437ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 438d704daedS猫头猫 return null; 43974d0cf81S猫头猫 } 440927dbe93S猫头猫 } 441927dbe93S猫头猫 4427e883dbbS猫头猫 /** 4437e883dbbS猫头猫 * 4447e883dbbS猫头猫 * getLyric(musicItem) => { 4457e883dbbS猫头猫 * lyric: string; 4467e883dbbS猫头猫 * trans: string; 4477e883dbbS猫头猫 * } 4487e883dbbS猫头猫 * 4497e883dbbS猫头猫 */ 450927dbe93S猫头猫 /** 获取歌词 */ 451927dbe93S猫头猫 async getLyric( 45243eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 453927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 45443eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 4555589cdf3S猫头猫 const meta = MediaExtra.get(originalMusicItem); 45643eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 457927dbe93S猫头猫 if (meta && meta.associatedLrc) { 45843eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 45943eb30bfS猫头猫 } else { 46043eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 461927dbe93S猫头猫 } 46243eb30bfS猫头猫 46343eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4647a91f04fS猫头猫 musicItem, 46543eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 46643eb30bfS猫头猫 46743eb30bfS猫头猫 /** 原始歌词文本 */ 4687e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4697e883dbbS猫头猫 let translation: string | null = null; 47043eb30bfS猫头猫 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猫头猫 } 872*da2a2959S猫头猫 873*da2a2959S猫头猫 async getMusicComments( 874*da2a2959S猫头猫 musicItem: IMusic.IMusicItem, 875*da2a2959S猫头猫 ): Promise<ICommon.PaginationResponse<IMedia.IComment>> { 876*da2a2959S猫头猫 const result = await this.plugin.instance?.getMusicComments?.( 877*da2a2959S猫头猫 musicItem, 878*da2a2959S猫头猫 ); 879*da2a2959S猫头猫 if (!result) { 880*da2a2959S猫头猫 throw new Error(); 881*da2a2959S猫头猫 } 882*da2a2959S猫头猫 if (result.isEnd !== false) { 883*da2a2959S猫头猫 result.isEnd = true; 884*da2a2959S猫头猫 } 885*da2a2959S猫头猫 if (!result.data) { 886*da2a2959S猫头猫 result.data = []; 887*da2a2959S猫头猫 } 888*da2a2959S猫头猫 889*da2a2959S猫头猫 return result; 890*da2a2959S猫头猫 } 891927dbe93S猫头猫} 892d5bfeb7eS猫头猫//#endregion 8931a5528a0S猫头猫 894927dbe93S猫头猫let plugins: Array<Plugin> = []; 895927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 89674d0cf81S猫头猫 897d5bfeb7eS猫头猫//#region 本地音乐插件 89874d0cf81S猫头猫/** 本地插件 */ 89974d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 9000e4173cdS猫头猫 return { 901d5bfeb7eS猫头猫 platform: localPluginPlatform, 90274d0cf81S猫头猫 _path: '', 90374d0cf81S猫头猫 async getMusicInfo(musicBase) { 90474d0cf81S猫头猫 const localPath = getInternalData<string>( 90574d0cf81S猫头猫 musicBase, 90674d0cf81S猫头猫 InternalDataType.LOCALPATH, 9070e4173cdS猫头猫 ); 90874d0cf81S猫头猫 if (localPath) { 90974d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 91074d0cf81S猫头猫 return { 91174d0cf81S猫头猫 artwork: coverImg, 91274d0cf81S猫头猫 }; 91374d0cf81S猫头猫 } 91474d0cf81S猫头猫 return null; 91574d0cf81S猫头猫 }, 9167993f90eS猫头猫 async getLyric(musicBase) { 9177993f90eS猫头猫 const localPath = getInternalData<string>( 9187993f90eS猫头猫 musicBase, 9197993f90eS猫头猫 InternalDataType.LOCALPATH, 9207993f90eS猫头猫 ); 9213a6f67b1S猫头猫 let rawLrc: string | null = null; 9227993f90eS猫头猫 if (localPath) { 9233a6f67b1S猫头猫 // 读取内嵌歌词 9243a6f67b1S猫头猫 try { 9253a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 9263a6f67b1S猫头猫 } catch (e) { 927a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 9287993f90eS猫头猫 } 9293a6f67b1S猫头猫 if (!rawLrc) { 9303a6f67b1S猫头猫 // 读取配置歌词 9313a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 9323a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 9333a6f67b1S猫头猫 9343a6f67b1S猫头猫 try { 9353a6f67b1S猫头猫 if (await exists(lrcPath)) { 9363a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 9373a6f67b1S猫头猫 } 9383a6f67b1S猫头猫 } catch {} 9393a6f67b1S猫头猫 } 9403a6f67b1S猫头猫 } 9413a6f67b1S猫头猫 9423a6f67b1S猫头猫 return rawLrc 9433a6f67b1S猫头猫 ? { 9443a6f67b1S猫头猫 rawLrc, 9453a6f67b1S猫头猫 } 9463a6f67b1S猫头猫 : null; 9477993f90eS猫头猫 }, 948a84a85c5S猫头猫 async importMusicItem(urlLike) { 949a84a85c5S猫头猫 let meta: any = {}; 950a84a85c5S猫头猫 try { 951a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 952a84a85c5S猫头猫 } catch {} 9535353b473S猫头猫 const stat = await getInfoAsync(urlLike, { 9545353b473S猫头猫 md5: true, 9555353b473S猫头猫 }); 9565353b473S猫头猫 let id: string; 9575353b473S猫头猫 if (stat.exists) { 9585353b473S猫头猫 id = stat.md5 || nanoid(); 9595353b473S猫头猫 } else { 9605353b473S猫头猫 id = nanoid(); 9615353b473S猫头猫 } 962a84a85c5S猫头猫 return { 963a84a85c5S猫头猫 id: id, 964a84a85c5S猫头猫 platform: '本地', 965a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 966a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 9675353b473S猫头猫 duration: parseInt(meta?.duration ?? '0', 10) / 1000, 968a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 969a84a85c5S猫头猫 artwork: '', 970a84a85c5S猫头猫 [internalSerializeKey]: { 971a84a85c5S猫头猫 localPath: urlLike, 972a84a85c5S猫头猫 }, 973a84a85c5S猫头猫 }; 974a84a85c5S猫头猫 }, 97511908939S猫头猫 async getMediaSource(musicItem, quality) { 97611908939S猫头猫 if (quality === 'standard') { 97711908939S猫头猫 return { 97811908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 97911908939S猫头猫 }; 98011908939S猫头猫 } 98111908939S猫头猫 return null; 98211908939S猫头猫 }, 98374d0cf81S猫头猫 }; 98474d0cf81S猫头猫}, ''); 9857993f90eS猫头猫localFilePlugin.hash = localPluginHash; 986927dbe93S猫头猫 987d5bfeb7eS猫头猫//#endregion 988d5bfeb7eS猫头猫 989927dbe93S猫头猫async function setup() { 990927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 991927dbe93S猫头猫 try { 992927dbe93S猫头猫 // 加载插件 993927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 994927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 995927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 9961e263108S猫头猫 trace('初始化插件', _pluginUrl); 9971e263108S猫头猫 if ( 9981e263108S猫头猫 _pluginUrl.isFile() && 9991e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 10001e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 10011e263108S猫头猫 ) { 1002927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 1003927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 10044060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 10054060c00aS猫头猫 p => p.hash === plugin.hash, 10064060c00aS猫头猫 ); 1007927dbe93S猫头猫 if (_pluginIndex !== -1) { 1008927dbe93S猫头猫 // 重复插件,直接忽略 10090c266394S猫头猫 continue; 1010927dbe93S猫头猫 } 1011927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 1012927dbe93S猫头猫 } 1013927dbe93S猫头猫 } 1014927dbe93S猫头猫 1015927dbe93S猫头猫 plugins = _plugins; 1016e08d37a3S猫头猫 /** 初始化meta信息 */ 1017c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 1018c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 1019c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 1020c2b3a262S猫头猫 for (let plugin of plugins) { 1021c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1022c2b3a262S猫头猫 plugin.state = 'disabled'; 1023c2b3a262S猫头猫 } 1024c2b3a262S猫头猫 } 1025c2b3a262S猫头猫 pluginStateMapper.notify(); 1026927dbe93S猫头猫 } catch (e: any) { 10274060c00aS猫头猫 ToastAndroid.show( 10284060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 10294060c00aS猫头猫 ToastAndroid.LONG, 10304060c00aS猫头猫 ); 10311a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1032927dbe93S猫头猫 throw e; 1033927dbe93S猫头猫 } 1034927dbe93S猫头猫} 1035927dbe93S猫头猫 1036e36e2599S猫头猫interface IInstallPluginConfig { 1037e36e2599S猫头猫 notCheckVersion?: boolean; 1038e36e2599S猫头猫} 1039e36e2599S猫头猫 1040740e3947S猫头猫async function installPluginFromRawCode( 1041740e3947S猫头猫 funcCode: string, 1042740e3947S猫头猫 config?: IInstallPluginConfig, 1043740e3947S猫头猫) { 1044740e3947S猫头猫 if (funcCode) { 1045740e3947S猫头猫 const plugin = new Plugin(funcCode, ''); 1046740e3947S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1047740e3947S猫头猫 if (_pluginIndex !== -1) { 1048740e3947S猫头猫 // 静默忽略 1049740e3947S猫头猫 return plugin; 1050740e3947S猫头猫 } 1051740e3947S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1052740e3947S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1053740e3947S猫头猫 if ( 1054740e3947S猫头猫 compare( 1055740e3947S猫头猫 oldVersionPlugin.instance.version ?? '', 1056740e3947S猫头猫 plugin.instance.version ?? '', 1057740e3947S猫头猫 '>', 1058740e3947S猫头猫 ) 1059740e3947S猫头猫 ) { 1060740e3947S猫头猫 throw new Error('已安装更新版本的插件'); 1061740e3947S猫头猫 } 1062740e3947S猫头猫 } 1063740e3947S猫头猫 1064740e3947S猫头猫 if (plugin.hash !== '') { 1065740e3947S猫头猫 const fn = nanoid(); 1066740e3947S猫头猫 if (oldVersionPlugin) { 1067740e3947S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1068740e3947S猫头猫 try { 1069740e3947S猫头猫 await unlink(oldVersionPlugin.path); 1070740e3947S猫头猫 } catch {} 1071740e3947S猫头猫 } 1072740e3947S猫头猫 const pluginPath = `${pathConst.pluginPath}${fn}.js`; 1073740e3947S猫头猫 await writeFile(pluginPath, funcCode, 'utf8'); 1074740e3947S猫头猫 plugin.path = pluginPath; 1075740e3947S猫头猫 plugins = plugins.concat(plugin); 1076740e3947S猫头猫 pluginStateMapper.notify(); 1077740e3947S猫头猫 return plugin; 1078740e3947S猫头猫 } 1079740e3947S猫头猫 throw new Error('插件无法解析!'); 1080740e3947S猫头猫 } 1081740e3947S猫头猫} 1082740e3947S猫头猫 1083927dbe93S猫头猫// 安装插件 1084e36e2599S猫头猫async function installPlugin( 1085e36e2599S猫头猫 pluginPath: string, 1086e36e2599S猫头猫 config?: IInstallPluginConfig, 1087e36e2599S猫头猫) { 108822c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1089927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1090e36e2599S猫头猫 1091e36e2599S猫头猫 if (funcCode) { 1092927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1093927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1094927dbe93S猫头猫 if (_pluginIndex !== -1) { 1095e36e2599S猫头猫 // 静默忽略 1096e36e2599S猫头猫 return plugin; 1097927dbe93S猫头猫 } 1098e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1099e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1100e36e2599S猫头猫 if ( 1101e36e2599S猫头猫 compare( 1102e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1103e36e2599S猫头猫 plugin.instance.version ?? '', 1104e36e2599S猫头猫 '>', 1105e36e2599S猫头猫 ) 1106e36e2599S猫头猫 ) { 1107e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1108e36e2599S猫头猫 } 1109e36e2599S猫头猫 } 1110e36e2599S猫头猫 1111927dbe93S猫头猫 if (plugin.hash !== '') { 1112927dbe93S猫头猫 const fn = nanoid(); 1113e36e2599S猫头猫 if (oldVersionPlugin) { 1114e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1115e36e2599S猫头猫 try { 1116e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1117e36e2599S猫头猫 } catch {} 1118e36e2599S猫头猫 } 1119927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1120927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1121927dbe93S猫头猫 plugin.path = _pluginPath; 1122927dbe93S猫头猫 plugins = plugins.concat(plugin); 1123927dbe93S猫头猫 pluginStateMapper.notify(); 1124a84a85c5S猫头猫 return plugin; 1125927dbe93S猫头猫 } 1126e36e2599S猫头猫 throw new Error('插件无法解析!'); 1127927dbe93S猫头猫 } 1128e36e2599S猫头猫 throw new Error('插件无法识别!'); 1129c2b3a262S猫头猫} 1130c2b3a262S猫头猫 1131ee781061S猫头猫const reqHeaders = { 1132ee781061S猫头猫 'Cache-Control': 'no-cache', 1133ee781061S猫头猫 Pragma: 'no-cache', 1134ee781061S猫头猫 Expires: '0', 1135ee781061S猫头猫}; 1136ee781061S猫头猫 1137c2b3a262S猫头猫async function installPluginFromUrl( 1138c2b3a262S猫头猫 url: string, 1139c2b3a262S猫头猫 config?: IInstallPluginConfig, 1140c2b3a262S猫头猫) { 114158992c6bS猫头猫 try { 1142ee781061S猫头猫 const funcCode = ( 1143ee781061S猫头猫 await axios.get(url, { 1144ee781061S猫头猫 headers: reqHeaders, 1145ee781061S猫头猫 }) 1146ee781061S猫头猫 ).data; 114758992c6bS猫头猫 if (funcCode) { 114858992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 114958992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 115058992c6bS猫头猫 if (_pluginIndex !== -1) { 11518b7ddca8S猫头猫 // 静默忽略 11528b7ddca8S猫头猫 return; 115358992c6bS猫头猫 } 115425c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1155c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 115625c1bd29S猫头猫 if ( 115725c1bd29S猫头猫 compare( 115825c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 115925c1bd29S猫头猫 plugin.instance.version ?? '', 116025c1bd29S猫头猫 '>', 116125c1bd29S猫头猫 ) 116225c1bd29S猫头猫 ) { 116325c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 116425c1bd29S猫头猫 } 116525c1bd29S猫头猫 } 116625c1bd29S猫头猫 116758992c6bS猫头猫 if (plugin.hash !== '') { 116858992c6bS猫头猫 const fn = nanoid(); 116958992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 117058992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 117158992c6bS猫头猫 plugin.path = _pluginPath; 117258992c6bS猫头猫 plugins = plugins.concat(plugin); 117325c1bd29S猫头猫 if (oldVersionPlugin) { 117425c1bd29S猫头猫 plugins = plugins.filter( 117525c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 117625c1bd29S猫头猫 ); 117725c1bd29S猫头猫 try { 117825c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 117925c1bd29S猫头猫 } catch {} 118025c1bd29S猫头猫 } 118158992c6bS猫头猫 pluginStateMapper.notify(); 118258992c6bS猫头猫 return; 118358992c6bS猫头猫 } 118474acbfc0S猫头猫 throw new Error('插件无法解析!'); 118558992c6bS猫头猫 } 118625c1bd29S猫头猫 } catch (e: any) { 1187ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 118858992c6bS猫头猫 errorLog('URL安装插件失败', e); 118925c1bd29S猫头猫 throw new Error(e?.message ?? ''); 119058992c6bS猫头猫 } 119158992c6bS猫头猫} 119258992c6bS猫头猫 1193927dbe93S猫头猫/** 卸载插件 */ 1194927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1195927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1196927dbe93S猫头猫 if (targetIndex !== -1) { 1197927dbe93S猫头猫 try { 119824e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1199927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1200927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1201927dbe93S猫头猫 pluginStateMapper.notify(); 120243eb30bfS猫头猫 // 防止其他重名 120324e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 12045589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 120524e5e74aS猫头猫 } 1206927dbe93S猫头猫 } catch {} 1207927dbe93S猫头猫 } 1208927dbe93S猫头猫} 1209927dbe93S猫头猫 121008882a77S猫头猫async function uninstallAllPlugins() { 121108882a77S猫头猫 await Promise.all( 121208882a77S猫头猫 plugins.map(async plugin => { 121308882a77S猫头猫 try { 121408882a77S猫头猫 const pluginName = plugin.name; 121508882a77S猫头猫 await unlink(plugin.path); 12165589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 121708882a77S猫头猫 } catch (e) {} 121808882a77S猫头猫 }), 121908882a77S猫头猫 ); 122008882a77S猫头猫 plugins = []; 122108882a77S猫头猫 pluginStateMapper.notify(); 1222e08d37a3S猫头猫 1223e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1224e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1225e08d37a3S猫头猫 .then(fns => { 1226e08d37a3S猫头猫 fns.forEach(fn => { 1227e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1228e08d37a3S猫头猫 }); 1229e08d37a3S猫头猫 }) 1230e08d37a3S猫头猫 .catch(emptyFunction); 123108882a77S猫头猫} 123208882a77S猫头猫 123325c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 123425c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 123525c1bd29S猫头猫 if (!updateUrl) { 123625c1bd29S猫头猫 throw new Error('没有更新源'); 123725c1bd29S猫头猫 } 123825c1bd29S猫头猫 try { 123925c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 124025c1bd29S猫头猫 } catch (e: any) { 124125c1bd29S猫头猫 if (e.message === '插件已安装') { 124225c1bd29S猫头猫 throw new Error('当前已是最新版本'); 124325c1bd29S猫头猫 } else { 124425c1bd29S猫头猫 throw e; 124525c1bd29S猫头猫 } 124625c1bd29S猫头猫 } 124725c1bd29S猫头猫} 124825c1bd29S猫头猫 1249927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 12502c595535S猫头猫 return getByName(mediaItem?.platform); 1251927dbe93S猫头猫} 1252927dbe93S猫头猫 1253927dbe93S猫头猫function getByHash(hash: string) { 12547993f90eS猫头猫 return hash === localPluginHash 12557993f90eS猫头猫 ? localFilePlugin 12567993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1257927dbe93S猫头猫} 1258927dbe93S猫头猫 1259927dbe93S猫头猫function getByName(name: string) { 12607993f90eS猫头猫 return name === localPluginPlatform 12610e4173cdS猫头猫 ? localFilePlugin 12620e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1263927dbe93S猫头猫} 1264927dbe93S猫头猫 1265927dbe93S猫头猫function getValidPlugins() { 1266927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1267927dbe93S猫头猫} 1268927dbe93S猫头猫 12692b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 12702b80a429S猫头猫 return plugins.filter( 12712b80a429S猫头猫 _ => 12722b80a429S猫头猫 _.state === 'enabled' && 12732b80a429S猫头猫 _.instance.search && 127439ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 127539ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 12762b80a429S猫头猫 : true), 12772b80a429S猫头猫 ); 1278efb9da24S猫头猫} 1279efb9da24S猫头猫 12802b80a429S猫头猫function getSortedSearchablePlugins( 12812b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 12822b80a429S猫头猫) { 12832b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1284e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1285e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1286e08d37a3S猫头猫 0 1287e08d37a3S猫头猫 ? -1 1288e08d37a3S猫头猫 : 1, 1289e08d37a3S猫头猫 ); 1290e08d37a3S猫头猫} 1291e08d37a3S猫头猫 129215feccc1S猫头猫function getTopListsablePlugins() { 129315feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 129415feccc1S猫头猫} 129515feccc1S猫头猫 129615feccc1S猫头猫function getSortedTopListsablePlugins() { 129715feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 129815feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 129915feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 130015feccc1S猫头猫 0 130115feccc1S猫头猫 ? -1 130215feccc1S猫头猫 : 1, 130315feccc1S猫头猫 ); 130415feccc1S猫头猫} 130515feccc1S猫头猫 1306ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1307ceb900cdS猫头猫 return plugins.filter( 1308ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1309ceb900cdS猫头猫 ); 1310ceb900cdS猫头猫} 1311ceb900cdS猫头猫 1312ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1313ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1314ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1315ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1316ceb900cdS猫头猫 0 1317ceb900cdS猫头猫 ? -1 1318ceb900cdS猫头猫 : 1, 1319ceb900cdS猫头猫 ); 1320ceb900cdS猫头猫} 1321ceb900cdS猫头猫 1322e08d37a3S猫头猫function useSortedPlugins() { 1323e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1324e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1325e08d37a3S猫头猫 132634588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 132734588741S猫头猫 [..._plugins].sort((a, b) => 1328e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1329e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1330e08d37a3S猫头猫 0 1331e08d37a3S猫头猫 ? -1 1332e08d37a3S猫头猫 : 1, 133334588741S猫头猫 ), 1334e08d37a3S猫头猫 ); 133534588741S猫头猫 133634588741S猫头猫 useEffect(() => { 1337d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 133834588741S猫头猫 setSortedPlugins( 133934588741S猫头猫 [..._plugins].sort((a, b) => 134034588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 134134588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 134234588741S猫头猫 0 134334588741S猫头猫 ? -1 134434588741S猫头猫 : 1, 134534588741S猫头猫 ), 134634588741S猫头猫 ); 1347d4cd40d8S猫头猫 }); 134834588741S猫头猫 }, [_plugins, _pluginMetaAll]); 134934588741S猫头猫 135034588741S猫头猫 return sortedPlugins; 1351e08d37a3S猫头猫} 1352e08d37a3S猫头猫 1353c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1354c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1355c2b3a262S猫头猫 if (target) { 1356c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1357c2b3a262S猫头猫 plugins = [...plugins]; 1358c2b3a262S猫头猫 pluginStateMapper.notify(); 1359c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1360c2b3a262S猫头猫 } 1361c2b3a262S猫头猫} 1362c2b3a262S猫头猫 1363927dbe93S猫头猫const PluginManager = { 1364927dbe93S猫头猫 setup, 1365927dbe93S猫头猫 installPlugin, 1366740e3947S猫头猫 installPluginFromRawCode, 136758992c6bS猫头猫 installPluginFromUrl, 136825c1bd29S猫头猫 updatePlugin, 1369927dbe93S猫头猫 uninstallPlugin, 1370927dbe93S猫头猫 getByMedia, 1371927dbe93S猫头猫 getByHash, 1372927dbe93S猫头猫 getByName, 1373927dbe93S猫头猫 getValidPlugins, 1374efb9da24S猫头猫 getSearchablePlugins, 1375e08d37a3S猫头猫 getSortedSearchablePlugins, 137615feccc1S猫头猫 getTopListsablePlugins, 1377ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 137815feccc1S猫头猫 getSortedTopListsablePlugins, 13795276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1380e08d37a3S猫头猫 useSortedPlugins, 138108882a77S猫头猫 uninstallAllPlugins, 1382c2b3a262S猫头猫 setPluginEnabled, 13835276aef9S猫头猫}; 1384927dbe93S猫头猫 1385927dbe93S猫头猫export default PluginManager; 1386