14060c00aS猫头猫import { 2927dbe93S猫头猫 copyFile, 3927dbe93S猫头猫 exists, 4927dbe93S猫头猫 readDir, 5927dbe93S猫头猫 readFile, 6927dbe93S猫头猫 unlink, 7927dbe93S猫头猫 writeFile, 8927dbe93S猫头猫} from 'react-native-fs'; 9927dbe93S猫头猫import CryptoJs from 'crypto-js'; 10927dbe93S猫头猫import dayjs from 'dayjs'; 11927dbe93S猫头猫import axios from 'axios'; 12ef60be1cS猫头猫import bigInt from 'big-integer'; 13ef60be1cS猫头猫import qs from 'qs'; 1428ccceb7S猫头猫import * as webdav from 'webdav'; 15d4cd40d8S猫头猫import {InteractionManager, ToastAndroid} from 'react-native'; 16927dbe93S猫头猫import pathConst from '@/constants/pathConst'; 1725c1bd29S猫头猫import {compare, satisfies} from 'compare-versions'; 18927dbe93S猫头猫import DeviceInfo from 'react-native-device-info'; 19927dbe93S猫头猫import StateMapper from '@/utils/stateMapper'; 20*43eb30bfS猫头猫import MediaMeta from './mediaExtra'; 21927dbe93S猫头猫import {nanoid} from 'nanoid'; 22ea6d708fS猫头猫import {devLog, errorLog, trace} from '../utils/log'; 23cfa0fc07S猫头猫import { 240e4173cdS猫头猫 getInternalData, 250e4173cdS猫头猫 InternalDataType, 260e4173cdS猫头猫 isSameMediaItem, 270e4173cdS猫头猫 resetMediaItem, 280e4173cdS猫头猫} from '@/utils/mediaItem'; 297993f90eS猫头猫import { 307993f90eS猫头猫 CacheControl, 31e08d37a3S猫头猫 emptyFunction, 327993f90eS猫头猫 internalSerializeKey, 337993f90eS猫头猫 localPluginHash, 347993f90eS猫头猫 localPluginPlatform, 357993f90eS猫头猫} from '@/constants/commonConst'; 36927dbe93S猫头猫import delay from '@/utils/delay'; 374d9d3c4cS猫头猫import * as cheerio from 'cheerio'; 387d7e864fS猫头猫import CookieManager from '@react-native-cookies/cookies'; 397d7e864fS猫头猫import he from 'he'; 40ef714860S猫头猫import Network from './network'; 410e4173cdS猫头猫import LocalMusicSheet from './localMusicSheet'; 420e4173cdS猫头猫import {FileSystem} from 'react-native-file-access'; 4374d0cf81S猫头猫import Mp3Util from '@/native/mp3Util'; 44e08d37a3S猫头猫import {PluginMeta} from './pluginMeta'; 4534588741S猫头猫import {useEffect, useState} from 'react'; 46a84a85c5S猫头猫import {getFileName} from '@/utils/fileUtils'; 47a7b42a4cS猫头猫import {URL} from 'react-native-url-polyfill'; 48a7b42a4cS猫头猫import Base64 from '@/utils/base64'; 49*43eb30bfS猫头猫import MediaCache from './mediaCache'; 50*43eb30bfS猫头猫import produce from 'immer'; 51*43eb30bfS猫头猫import MediaExtra from './mediaExtra'; 52*43eb30bfS猫头猫import objectPath from 'object-path'; 53927dbe93S猫头猫 5461aca335S猫头猫axios.defaults.timeout = 2000; 55927dbe93S猫头猫 56927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 57927dbe93S猫头猫 58cfa0fc07S猫头猫export enum PluginStateCode { 59927dbe93S猫头猫 /** 版本不匹配 */ 60927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 61927dbe93S猫头猫 /** 无法解析 */ 62927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 63927dbe93S猫头猫} 64927dbe93S猫头猫 659c34d637S猫头猫const packages: Record<string, any> = { 669c34d637S猫头猫 cheerio, 679c34d637S猫头猫 'crypto-js': CryptoJs, 689c34d637S猫头猫 axios, 699c34d637S猫头猫 dayjs, 709c34d637S猫头猫 'big-integer': bigInt, 719c34d637S猫头猫 qs, 729c34d637S猫头猫 he, 733b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 7428ccceb7S猫头猫 webdav, 759c34d637S猫头猫}; 769c34d637S猫头猫 77b43683eaS猫头猫const _require = (packageName: string) => { 78b43683eaS猫头猫 let pkg = packages[packageName]; 79b43683eaS猫头猫 pkg.default = pkg; 80b43683eaS猫头猫 return pkg; 81b43683eaS猫头猫}; 829c34d637S猫头猫 8353f8cd8eS猫头猫const _consoleBind = function ( 8453f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 8553f8cd8eS猫头猫 ...args: any 8653f8cd8eS猫头猫) { 8753f8cd8eS猫头猫 const fn = console[method]; 8853f8cd8eS猫头猫 if (fn) { 8953f8cd8eS猫头猫 fn(...args); 9053f8cd8eS猫头猫 devLog(method, ...args); 9153f8cd8eS猫头猫 } 9253f8cd8eS猫头猫}; 9353f8cd8eS猫头猫 9453f8cd8eS猫头猫const _console = { 9553f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9653f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9753f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9853f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9953f8cd8eS猫头猫}; 10053f8cd8eS猫头猫 101a7b42a4cS猫头猫function formatAuthUrl(url: string) { 102a7b42a4cS猫头猫 const urlObj = new URL(url); 103a7b42a4cS猫头猫 104a7b42a4cS猫头猫 try { 105a7b42a4cS猫头猫 if (urlObj.username && urlObj.password) { 106a7b42a4cS猫头猫 const auth = `Basic ${Base64.btoa( 107a7b42a4cS猫头猫 `${decodeURIComponent(urlObj.username)}:${decodeURIComponent( 108a7b42a4cS猫头猫 urlObj.password, 109a7b42a4cS猫头猫 )}`, 110a7b42a4cS猫头猫 )}`; 111a7b42a4cS猫头猫 urlObj.username = ''; 112a7b42a4cS猫头猫 urlObj.password = ''; 113a7b42a4cS猫头猫 114a7b42a4cS猫头猫 return { 115a7b42a4cS猫头猫 url: urlObj.toString(), 116a7b42a4cS猫头猫 auth, 117a7b42a4cS猫头猫 }; 118a7b42a4cS猫头猫 } 119a7b42a4cS猫头猫 } catch (e) { 120a7b42a4cS猫头猫 return { 121a7b42a4cS猫头猫 url, 122a7b42a4cS猫头猫 }; 123a7b42a4cS猫头猫 } 124a7b42a4cS猫头猫 return { 125a7b42a4cS猫头猫 url, 126a7b42a4cS猫头猫 }; 127a7b42a4cS猫头猫} 128a7b42a4cS猫头猫 129d5bfeb7eS猫头猫//#region 插件类 130927dbe93S猫头猫export class Plugin { 131927dbe93S猫头猫 /** 插件名 */ 132927dbe93S猫头猫 public name: string; 133927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 134927dbe93S猫头猫 public hash: string; 135927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 136927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 137927dbe93S猫头猫 /** 插件状态信息 */ 138927dbe93S猫头猫 public stateCode?: PluginStateCode; 139927dbe93S猫头猫 /** 插件的实例 */ 140927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 141927dbe93S猫头猫 /** 插件路径 */ 142927dbe93S猫头猫 public path: string; 143927dbe93S猫头猫 /** 插件方法 */ 144927dbe93S猫头猫 public methods: PluginMethods; 145927dbe93S猫头猫 14674d0cf81S猫头猫 constructor( 14774d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 14874d0cf81S猫头猫 pluginPath: string, 14974d0cf81S猫头猫 ) { 150927dbe93S猫头猫 this.state = 'enabled'; 151927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1523b3d6357S猫头猫 const _module: any = {exports: {}}; 153927dbe93S猫头猫 try { 15474d0cf81S猫头猫 if (typeof funcCode === 'string') { 155e0caf342S猫头猫 // 插件的环境变量 156e0caf342S猫头猫 const env = { 157e0caf342S猫头猫 getUserVariables: () => { 158e0caf342S猫头猫 return ( 159e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 160e0caf342S猫头猫 ); 161e0caf342S猫头猫 }, 162e3fa9b3cS猫头猫 os: 'android', 163e0caf342S猫头猫 }; 164e0caf342S猫头猫 1654060c00aS猫头猫 // eslint-disable-next-line no-new-func 166927dbe93S猫头猫 _instance = Function(` 167927dbe93S猫头猫 'use strict'; 168e0caf342S猫头猫 return function(require, __musicfree_require, module, exports, console, env) { 1699c34d637S猫头猫 ${funcCode} 170927dbe93S猫头猫 } 171e0caf342S猫头猫 `)()( 172e0caf342S猫头猫 _require, 173e0caf342S猫头猫 _require, 174e0caf342S猫头猫 _module, 175e0caf342S猫头猫 _module.exports, 176e0caf342S猫头猫 _console, 177e0caf342S猫头猫 env, 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就好了 303*43eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 304927dbe93S猫头猫 const localPath = 305*43eb30bfS猫头猫 mediaExtra?.localPath || 306*43eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3070e4173cdS猫头猫 getInternalData<string>( 3080e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3090e4173cdS猫头猫 InternalDataType.LOCALPATH, 3100e4173cdS猫头猫 ); 311a84a85c5S猫头猫 if ( 312a84a85c5S猫头猫 localPath && 313a84a85c5S猫头猫 (localPath.startsWith('content://') || 314a84a85c5S猫头猫 (await FileSystem.exists(localPath))) 315a84a85c5S猫头猫 ) { 3160e4173cdS猫头猫 trace('本地播放', localPath); 317*43eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 318*43eb30bfS猫头猫 // 修正一下本地数据 319*43eb30bfS猫头猫 MediaExtra.update(musicItem, { 320*43eb30bfS猫头猫 localPath, 321*43eb30bfS猫头猫 }); 322*43eb30bfS猫头猫 } 323927dbe93S猫头猫 return { 324927dbe93S猫头猫 url: localPath, 325927dbe93S猫头猫 }; 326*43eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 327*43eb30bfS猫头猫 MediaExtra.update(musicItem, { 328*43eb30bfS猫头猫 localPath: undefined, 329*43eb30bfS猫头猫 }); 330927dbe93S猫头猫 } 331a84a85c5S猫头猫 3327993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 333f5935920S猫头猫 throw new Error('本地音乐不存在'); 334f5935920S猫头猫 } 335927dbe93S猫头猫 // 2. 缓存播放 336*43eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 337*43eb30bfS猫头猫 musicItem, 338*43eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 339985f8e75S猫头猫 const pluginCacheControl = 340985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 341cfa0fc07S猫头猫 if ( 342cfa0fc07S猫头猫 mediaCache && 343*43eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34448f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34548f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 346ef714860S猫头猫 Network.isOffline())) 347cfa0fc07S猫头猫 ) { 3485276aef9S猫头猫 trace('播放', '缓存播放'); 349*43eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 350927dbe93S猫头猫 return { 351*43eb30bfS猫头猫 url: qualityInfo!.url, 352927dbe93S猫头猫 headers: mediaCache.headers, 3534060c00aS猫头猫 userAgent: 3544060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 355927dbe93S猫头猫 }; 356927dbe93S猫头猫 } 357927dbe93S猫头猫 // 3. 插件解析 35820e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 359a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 360a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 361a7b42a4cS猫头猫 ); 362a7b42a4cS猫头猫 return { 363a7b42a4cS猫头猫 url: url, 364a7b42a4cS猫头猫 headers: auth 365a7b42a4cS猫头猫 ? { 366a7b42a4cS猫头猫 Authorization: auth, 367a7b42a4cS猫头猫 } 368a7b42a4cS猫头猫 : undefined, 369a7b42a4cS猫头猫 }; 370927dbe93S猫头猫 } 371927dbe93S猫头猫 try { 372abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 373abaede57S猫头猫 musicItem, 374abaede57S猫头猫 quality, 375abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 376927dbe93S猫头猫 if (!url) { 377a28eac61S猫头猫 throw new Error('NOT RETRY'); 378927dbe93S猫头猫 } 3795276aef9S猫头猫 trace('播放', '插件播放'); 380927dbe93S猫头猫 const result = { 381927dbe93S猫头猫 url, 382927dbe93S猫头猫 headers, 383927dbe93S猫头猫 userAgent: headers?.['user-agent'], 384cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 385a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 386a7b42a4cS猫头猫 if (authFormattedResult.auth) { 387a7b42a4cS猫头猫 result.url = authFormattedResult.url; 388a7b42a4cS猫头猫 result.headers = { 389a7b42a4cS猫头猫 ...(result.headers ?? {}), 390a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 391a7b42a4cS猫头猫 }; 392a7b42a4cS猫头猫 } 393927dbe93S猫头猫 394dc160d50S猫头猫 if ( 395dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 396dc160d50S猫头猫 !notUpdateCache 397dc160d50S猫头猫 ) { 398*43eb30bfS猫头猫 // 更新缓存 399*43eb30bfS猫头猫 const cacheSource = { 400*43eb30bfS猫头猫 headers: result.headers, 401*43eb30bfS猫头猫 userAgent: result.userAgent, 402*43eb30bfS猫头猫 url, 403*43eb30bfS猫头猫 }; 404*43eb30bfS猫头猫 let realMusicItem = { 405*43eb30bfS猫头猫 ...musicItem, 406*43eb30bfS猫头猫 ...(mediaCache || {}), 407*43eb30bfS猫头猫 }; 408*43eb30bfS猫头猫 realMusicItem.source = { 409*43eb30bfS猫头猫 ...(realMusicItem.source || {}), 410*43eb30bfS猫头猫 [quality]: cacheSource, 411*43eb30bfS猫头猫 }; 412*43eb30bfS猫头猫 413*43eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 414752ffc5aS猫头猫 } 415927dbe93S猫头猫 return result; 416927dbe93S猫头猫 } catch (e: any) { 417a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 418927dbe93S猫头猫 await delay(150); 419abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 420927dbe93S猫头猫 } 421927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 422ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 423192ae2b0S猫头猫 return null; 424927dbe93S猫头猫 } 425927dbe93S猫头猫 } 426927dbe93S猫头猫 427927dbe93S猫头猫 /** 获取音乐详情 */ 428927dbe93S猫头猫 async getMusicInfo( 429927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 43074d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 431927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 432d704daedS猫头猫 return null; 433927dbe93S猫头猫 } 43474d0cf81S猫头猫 try { 435927dbe93S猫头猫 return ( 436927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4377993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 438d704daedS猫头猫 ) ?? null 439927dbe93S猫头猫 ); 440ea6d708fS猫头猫 } catch (e: any) { 441ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 442d704daedS猫头猫 return null; 44374d0cf81S猫头猫 } 444927dbe93S猫头猫 } 445927dbe93S猫头猫 446927dbe93S猫头猫 /** 获取歌词 */ 447927dbe93S猫头猫 async getLyric( 448*43eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 449*43eb30bfS猫头猫 lang: string, // 语言 450927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 451*43eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 452*43eb30bfS猫头猫 const meta = MediaMeta.get(originalMusicItem); 453*43eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 454927dbe93S猫头猫 if (meta && meta.associatedLrc) { 455*43eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 456*43eb30bfS猫头猫 } else { 457*43eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 458927dbe93S猫头猫 } 459*43eb30bfS猫头猫 460*43eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4617a91f04fS猫头猫 musicItem, 462*43eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 463*43eb30bfS猫头猫 464*43eb30bfS猫头猫 /** 原始歌词文本 */ 465*43eb30bfS猫头猫 let rawLrc: string | null = null; 466*43eb30bfS猫头猫 /** 歌词URL */ 467*43eb30bfS猫头猫 let lrcUrl: string | null = null; 468*43eb30bfS猫头猫 469*43eb30bfS猫头猫 // 本地的文件名 470*43eb30bfS猫头猫 const filename = `${pathConst.lrcCachePath}${nanoid()}.lrc`; 471*43eb30bfS猫头猫 472*43eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 473*43eb30bfS猫头猫 if (musicItemCache?.lyric) { 474*43eb30bfS猫头猫 let lyric: ILyric.ILyricSource | null = musicItem.lyric || null; 475*43eb30bfS猫头猫 // 缓存的远程结果 476*43eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 477*43eb30bfS猫头猫 musicItemCache.lyric || null; 478*43eb30bfS猫头猫 // 缓存的本地结果 479*43eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 480*43eb30bfS猫头猫 musicItemCache.$localLyric || null; 481*43eb30bfS猫头猫 482*43eb30bfS猫头猫 // 如果存在本地歌词,语言非默认 483*43eb30bfS猫头猫 if (lang && lang !== 'default') { 484*43eb30bfS猫头猫 cacheLyric = cacheLyric?.versions?.[lang] || null; 485*43eb30bfS猫头猫 localLyric = localLyric?.versions?.[lang] || null; 486*43eb30bfS猫头猫 lyric = lyric?.versions?.[lang] || null; 487*43eb30bfS猫头猫 } else { 488*43eb30bfS猫头猫 cacheLyric = { 489*43eb30bfS猫头猫 lrc: cacheLyric?.lrc || musicItemCache.lrc, 490*43eb30bfS猫头猫 rawLrc: cacheLyric?.rawLrc || musicItemCache.rawLrc, 491*43eb30bfS猫头猫 }; 492*43eb30bfS猫头猫 lyric = { 493*43eb30bfS猫头猫 lrc: lyric?.lrc || musicItem.lrc, 494*43eb30bfS猫头猫 rawLrc: lyric?.rawLrc ?? musicItem.rawLrc, 495*43eb30bfS猫头猫 }; 496*43eb30bfS猫头猫 } 497*43eb30bfS猫头猫 498*43eb30bfS猫头猫 // 返回本地歌词 499*43eb30bfS猫头猫 if (localLyric?.lrc && (await exists(localLyric.lrc))) { 500*43eb30bfS猫头猫 // TODO 清理缓存,不管也行 501*43eb30bfS猫头猫 rawLrc = await readFile(localLyric.lrc, 'utf8'); 502*43eb30bfS猫头猫 503*43eb30bfS猫头猫 console.log('本地缓存的歌词'); 504*43eb30bfS猫头猫 return { 505*43eb30bfS猫头猫 rawLrc, 506*43eb30bfS猫头猫 lang, 507*43eb30bfS猫头猫 versions: cacheLyric?.versions, 508*43eb30bfS猫头猫 }; 509*43eb30bfS猫头猫 } 510*43eb30bfS猫头猫 511*43eb30bfS猫头猫 // 缓存的搜索结果 512*43eb30bfS猫头猫 if (cacheLyric?.rawLrc || lyric?.rawLrc) { 513*43eb30bfS猫头猫 console.log('缓存结果的歌词1'); 514*43eb30bfS猫头猫 return { 515*43eb30bfS猫头猫 rawLrc: cacheLyric?.rawLrc || lyric?.rawLrc, 516*43eb30bfS猫头猫 lang, 517*43eb30bfS猫头猫 versions: cacheLyric?.versions || lyric?.versions, 518*43eb30bfS猫头猫 }; 519*43eb30bfS猫头猫 } 520*43eb30bfS猫头猫 521*43eb30bfS猫头猫 if (cacheLyric?.lrc || lyric?.lrc) { 522*43eb30bfS猫头猫 // 加载远程歌词 523*43eb30bfS猫头猫 rawLrc = ( 524*43eb30bfS猫头猫 await axios 525*43eb30bfS猫头猫 .get((cacheLyric?.lrc || lyric?.lrc)!, {timeout: 3000}) 526*43eb30bfS猫头猫 .catch(() => null) 527*43eb30bfS猫头猫 )?.data; 528*43eb30bfS猫头猫 529*43eb30bfS猫头猫 if (rawLrc) { 530*43eb30bfS猫头猫 // 写本地歌词缓存 531*43eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 532*43eb30bfS猫头猫 MediaCache.setMediaCache( 533*43eb30bfS猫头猫 produce(musicItemCache, draft => { 534*43eb30bfS猫头猫 if (!lang || lang === 'default') { 535*43eb30bfS猫头猫 objectPath.set( 536*43eb30bfS猫头猫 draft, 537*43eb30bfS猫头猫 '$localLyric.lrc', 538*43eb30bfS猫头猫 filename, 539927dbe93S猫头猫 ); 5407a91f04fS猫头猫 } else { 541*43eb30bfS猫头猫 objectPath.set( 542*43eb30bfS猫头猫 draft, 543*43eb30bfS猫头猫 `$localLyric.lrc.versions.${lang}.lrc`, 544*43eb30bfS猫头猫 filename, 545*43eb30bfS猫头猫 ); 546*43eb30bfS猫头猫 } 547*43eb30bfS猫头猫 return draft; 548*43eb30bfS猫头猫 }), 549*43eb30bfS猫头猫 ); 550*43eb30bfS猫头猫 551*43eb30bfS猫头猫 console.log('缓存结果的歌词2'); 552*43eb30bfS猫头猫 553*43eb30bfS猫头猫 return { 554*43eb30bfS猫头猫 rawLrc, 555*43eb30bfS猫头猫 lang, 556*43eb30bfS猫头猫 lrc: cacheLyric?.lrc || lyric?.lrc, 557*43eb30bfS猫头猫 versions: cacheLyric?.versions || lyric?.versions, 558*43eb30bfS猫头猫 }; 559*43eb30bfS猫头猫 } 560*43eb30bfS猫头猫 } 561*43eb30bfS猫头猫 } 562*43eb30bfS猫头猫 563*43eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 564*43eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 565*43eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 566*43eb30bfS猫头猫 lrcSource = 567*43eb30bfS猫头猫 (await this.plugin.instance 568*43eb30bfS猫头猫 ?.getLyric?.( 5697a91f04fS猫头猫 resetMediaItem(musicItem, undefined, true), 570*43eb30bfS猫头猫 lang, 571*43eb30bfS猫头猫 ) 572*43eb30bfS猫头猫 ?.catch(() => null)) || null; 573*43eb30bfS猫头猫 } else { 574*43eb30bfS猫头猫 lrcSource = 575*43eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 576*43eb30bfS猫头猫 ?.instance?.getLyric?.( 577*43eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 578*43eb30bfS猫头猫 lang, 579*43eb30bfS猫头猫 ) 580*43eb30bfS猫头猫 ?.catch(() => null)) || null; 581*43eb30bfS猫头猫 } 582*43eb30bfS猫头猫 583*43eb30bfS猫头猫 if (lrcSource) { 584*43eb30bfS猫头猫 rawLrc = lrcSource?.rawLrc || null; 585*43eb30bfS猫头猫 lrcUrl = lrcSource?.lrc || null; 586*43eb30bfS猫头猫 587*43eb30bfS猫头猫 console.log('远程歌词3'); 588*43eb30bfS猫头猫 589*43eb30bfS猫头猫 // 只返回了个versions 590*43eb30bfS猫头猫 if ( 591*43eb30bfS猫头猫 !rawLrc && 592*43eb30bfS猫头猫 !lrcUrl && 593*43eb30bfS猫头猫 lrcSource?.versions?.[lang || 'default'] 594*43eb30bfS猫头猫 ) { 595*43eb30bfS猫头猫 rawLrc = 596*43eb30bfS猫头猫 lrcSource?.versions?.[lang || 'default']?.rawLrc || null; 597*43eb30bfS猫头猫 lrcUrl = lrcSource?.versions?.[lang || 'default']?.lrc || null; 598*43eb30bfS猫头猫 } 599*43eb30bfS猫头猫 600*43eb30bfS猫头猫 if (!rawLrc && lrcUrl) { 601*43eb30bfS猫头猫 rawLrc = ( 602*43eb30bfS猫头猫 await axios.get(lrcUrl, {timeout: 3000}).catch(() => null) 603*43eb30bfS猫头猫 )?.data; 604*43eb30bfS猫头猫 } 605*43eb30bfS猫头猫 606*43eb30bfS猫头猫 if (rawLrc) { 607*43eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 608*43eb30bfS猫头猫 MediaCache.setMediaCache( 609*43eb30bfS猫头猫 produce(musicItemCache || musicItem, draft => { 610*43eb30bfS猫头猫 if (!lang || lang === 'default') { 611*43eb30bfS猫头猫 objectPath.set(draft, '$localLyric.lrc', filename); 612*43eb30bfS猫头猫 } else { 613*43eb30bfS猫头猫 objectPath.set( 614*43eb30bfS猫头猫 draft, 615*43eb30bfS猫头猫 `$localLyric.lrc.versions.${lang}.lrc`, 616*43eb30bfS猫头猫 filename, 6177a91f04fS猫头猫 ); 6187a91f04fS猫头猫 } 6197a91f04fS猫头猫 620*43eb30bfS猫头猫 objectPath.set( 621*43eb30bfS猫头猫 draft, 622*43eb30bfS猫头猫 `lyric.versions`, 623*43eb30bfS猫头猫 lrcSource?.versions || draft?.versions, 624*43eb30bfS猫头猫 ); 625*43eb30bfS猫头猫 return draft; 626*43eb30bfS猫头猫 }), 627*43eb30bfS猫头猫 ); 628*43eb30bfS猫头猫 629927dbe93S猫头猫 return { 630927dbe93S猫头猫 rawLrc, 631*43eb30bfS猫头猫 lrc: lrcUrl || undefined, 632*43eb30bfS猫头猫 lang, 633*43eb30bfS猫头猫 versions: lrcSource.versions, 634927dbe93S猫头猫 }; 635927dbe93S猫头猫 } 636927dbe93S猫头猫 } 637*43eb30bfS猫头猫 6383a6f67b1S猫头猫 // 6. 如果是本地文件 639*43eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 640*43eb30bfS猫头猫 if ( 641*43eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 642*43eb30bfS猫头猫 isDownloaded 643*43eb30bfS猫头猫 ) { 644*43eb30bfS猫头猫 const res = await localFilePlugin.instance!.getLyric!( 645*43eb30bfS猫头猫 isDownloaded, 646*43eb30bfS猫头猫 lang, 647*43eb30bfS猫头猫 ); 648*43eb30bfS猫头猫 649*43eb30bfS猫头猫 console.log('本地文件歌词'); 650*43eb30bfS猫头猫 6513a6f67b1S猫头猫 if (res) { 6523a6f67b1S猫头猫 return res; 6533a6f67b1S猫头猫 } 6543a6f67b1S猫头猫 } 655ea6d708fS猫头猫 devLog('warn', '无歌词'); 656927dbe93S猫头猫 657927dbe93S猫头猫 return null; 658927dbe93S猫头猫 } 659927dbe93S猫头猫 660927dbe93S猫头猫 /** 获取歌词文本 */ 661927dbe93S猫头猫 async getLyricText( 662927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 663*43eb30bfS猫头猫 lang = 'default', 664927dbe93S猫头猫 ): Promise<string | undefined> { 665*43eb30bfS猫头猫 return (await this.getLyric(musicItem, lang))?.rawLrc; 666927dbe93S猫头猫 } 667927dbe93S猫头猫 668927dbe93S猫头猫 /** 获取专辑信息 */ 669927dbe93S猫头猫 async getAlbumInfo( 670927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 671f9afcc0dS猫头猫 page: number = 1, 672f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 673927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 674f9afcc0dS猫头猫 return { 675f9afcc0dS猫头猫 albumItem, 676f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 677f2a4767cS猫头猫 resetMediaItem, 678f2a4767cS猫头猫 this.plugin.name, 679f2a4767cS猫头猫 true, 680f2a4767cS猫头猫 ), 681f9afcc0dS猫头猫 isEnd: true, 682f9afcc0dS猫头猫 }; 683927dbe93S猫头猫 } 684927dbe93S猫头猫 try { 685927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 686927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 687f9afcc0dS猫头猫 page, 688927dbe93S猫头猫 ); 6895276aef9S猫头猫 if (!result) { 6905276aef9S猫头猫 throw new Error(); 6915276aef9S猫头猫 } 692927dbe93S猫头猫 result?.musicList?.forEach(_ => { 693927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 69496744680S猫头猫 _.album = albumItem.title; 695927dbe93S猫头猫 }); 6965276aef9S猫头猫 697f9afcc0dS猫头猫 if (page <= 1) { 698f9afcc0dS猫头猫 // 合并信息 699f9afcc0dS猫头猫 return { 700f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 701f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 702f9afcc0dS猫头猫 musicList: result.musicList, 703f9afcc0dS猫头猫 }; 704f9afcc0dS猫头猫 } else { 705f9afcc0dS猫头猫 return { 706f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 707f9afcc0dS猫头猫 musicList: result.musicList, 708f9afcc0dS猫头猫 }; 709f9afcc0dS猫头猫 } 7104394410dS猫头猫 } catch (e: any) { 7114394410dS猫头猫 trace('获取专辑信息失败', e?.message); 712ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 713ea6d708fS猫头猫 714f9afcc0dS猫头猫 return null; 715927dbe93S猫头猫 } 716927dbe93S猫头猫 } 717927dbe93S猫头猫 7185830c002S猫头猫 /** 获取歌单信息 */ 7195830c002S猫头猫 async getMusicSheetInfo( 7205830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 7215830c002S猫头猫 page: number = 1, 7225830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 7235281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 7245830c002S猫头猫 return { 7255830c002S猫头猫 sheetItem, 7265830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 7275830c002S猫头猫 isEnd: true, 7285830c002S猫头猫 }; 7295830c002S猫头猫 } 7305830c002S猫头猫 try { 7315830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 7325830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 7335830c002S猫头猫 page, 7345830c002S猫头猫 ); 7355830c002S猫头猫 if (!result) { 7365830c002S猫头猫 throw new Error(); 7375830c002S猫头猫 } 7385830c002S猫头猫 result?.musicList?.forEach(_ => { 7395830c002S猫头猫 resetMediaItem(_, this.plugin.name); 7405830c002S猫头猫 }); 7415830c002S猫头猫 7425830c002S猫头猫 if (page <= 1) { 7435830c002S猫头猫 // 合并信息 7445830c002S猫头猫 return { 7455830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 7465830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7475830c002S猫头猫 musicList: result.musicList, 7485830c002S猫头猫 }; 7495830c002S猫头猫 } else { 7505830c002S猫头猫 return { 7515830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7525830c002S猫头猫 musicList: result.musicList, 7535830c002S猫头猫 }; 7545830c002S猫头猫 } 7555830c002S猫头猫 } catch (e: any) { 7565830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7575830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7585830c002S猫头猫 7595830c002S猫头猫 return null; 7605830c002S猫头猫 } 7615830c002S猫头猫 } 7625830c002S猫头猫 763927dbe93S猫头猫 /** 查询作者信息 */ 764efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 765927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 766927dbe93S猫头猫 page: number, 767927dbe93S猫头猫 type: T, 768927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 769efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 770927dbe93S猫头猫 return { 771927dbe93S猫头猫 isEnd: true, 772927dbe93S猫头猫 data: [], 773927dbe93S猫头猫 }; 774927dbe93S猫头猫 } 775927dbe93S猫头猫 try { 776efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 777927dbe93S猫头猫 artistItem, 778927dbe93S猫头猫 page, 779927dbe93S猫头猫 type, 780927dbe93S猫头猫 ); 781927dbe93S猫头猫 if (!result.data) { 782927dbe93S猫头猫 return { 783927dbe93S猫头猫 isEnd: true, 784927dbe93S猫头猫 data: [], 785927dbe93S猫头猫 }; 786927dbe93S猫头猫 } 787927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 788927dbe93S猫头猫 return { 789927dbe93S猫头猫 isEnd: result.isEnd ?? true, 790927dbe93S猫头猫 data: result.data, 791927dbe93S猫头猫 }; 7924394410dS猫头猫 } catch (e: any) { 7934394410dS猫头猫 trace('查询作者信息失败', e?.message); 794ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 795ea6d708fS猫头猫 796927dbe93S猫头猫 throw e; 797927dbe93S猫头猫 } 798927dbe93S猫头猫 } 79908380090S猫头猫 80008380090S猫头猫 /** 导入歌单 */ 80108380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 80208380090S猫头猫 try { 80308380090S猫头猫 const result = 80408380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 80508380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 80608380090S猫头猫 return result; 807ea6d708fS猫头猫 } catch (e: any) { 8080e4173cdS猫头猫 console.log(e); 809ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 810ea6d708fS猫头猫 81108380090S猫头猫 return []; 81208380090S猫头猫 } 81308380090S猫头猫 } 8144d9d3c4cS猫头猫 /** 导入单曲 */ 8154d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 8164d9d3c4cS猫头猫 try { 8174d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 8184d9d3c4cS猫头猫 urlLike, 8194d9d3c4cS猫头猫 ); 8204d9d3c4cS猫头猫 if (!result) { 8214d9d3c4cS猫头猫 throw new Error(); 8224d9d3c4cS猫头猫 } 8234d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 8244d9d3c4cS猫头猫 return result; 825ea6d708fS猫头猫 } catch (e: any) { 826ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 827ea6d708fS猫头猫 8284d9d3c4cS猫头猫 return null; 8294d9d3c4cS猫头猫 } 8304d9d3c4cS猫头猫 } 831d52aa40eS猫头猫 /** 获取榜单 */ 83292b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 833d52aa40eS猫头猫 try { 834d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 835d52aa40eS猫头猫 if (!result) { 836d52aa40eS猫头猫 throw new Error(); 837d52aa40eS猫头猫 } 838d52aa40eS猫头猫 return result; 839d52aa40eS猫头猫 } catch (e: any) { 840d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 841d52aa40eS猫头猫 return []; 842d52aa40eS猫头猫 } 843d52aa40eS猫头猫 } 844d52aa40eS猫头猫 /** 获取榜单详情 */ 845d52aa40eS猫头猫 async getTopListDetail( 84692b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 847956ee1b7S猫头猫 page: number, 848956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 849d52aa40eS猫头猫 try { 850d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 851d52aa40eS猫头猫 topListItem, 852956ee1b7S猫头猫 page, 853d52aa40eS猫头猫 ); 854d52aa40eS猫头猫 if (!result) { 855d52aa40eS猫头猫 throw new Error(); 856d52aa40eS猫头猫 } 857d384662fS猫头猫 if (result.musicList) { 858d384662fS猫头猫 result.musicList.forEach(_ => 859d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 860d384662fS猫头猫 ); 861d384662fS猫头猫 } 862956ee1b7S猫头猫 if (result.isEnd !== false) { 863956ee1b7S猫头猫 result.isEnd = true; 864956ee1b7S猫头猫 } 865d52aa40eS猫头猫 return result; 866d52aa40eS猫头猫 } catch (e: any) { 867d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 868d52aa40eS猫头猫 return { 869956ee1b7S猫头猫 isEnd: true, 870956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 871d52aa40eS猫头猫 musicList: [], 872d52aa40eS猫头猫 }; 873d52aa40eS猫头猫 } 874d52aa40eS猫头猫 } 875ceb900cdS猫头猫 8765830c002S猫头猫 /** 获取推荐歌单的tag */ 877ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 878ceb900cdS猫头猫 try { 879ceb900cdS猫头猫 const result = 880ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 881ceb900cdS猫头猫 if (!result) { 882ceb900cdS猫头猫 throw new Error(); 883ceb900cdS猫头猫 } 884ceb900cdS猫头猫 return result; 885ceb900cdS猫头猫 } catch (e: any) { 886ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 887ceb900cdS猫头猫 return { 888ceb900cdS猫头猫 data: [], 889ceb900cdS猫头猫 }; 890ceb900cdS猫头猫 } 891ceb900cdS猫头猫 } 8925830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 893ceb900cdS猫头猫 async getRecommendSheetsByTag( 894ceb900cdS猫头猫 tagItem: ICommon.IUnique, 895ceb900cdS猫头猫 page?: number, 896ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 897ceb900cdS猫头猫 try { 898ceb900cdS猫头猫 const result = 899ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 900ceb900cdS猫头猫 tagItem, 901ceb900cdS猫头猫 page ?? 1, 902ceb900cdS猫头猫 ); 903ceb900cdS猫头猫 if (!result) { 904ceb900cdS猫头猫 throw new Error(); 905ceb900cdS猫头猫 } 906ceb900cdS猫头猫 if (result.isEnd !== false) { 907ceb900cdS猫头猫 result.isEnd = true; 908ceb900cdS猫头猫 } 909ceb900cdS猫头猫 if (!result.data) { 910ceb900cdS猫头猫 result.data = []; 911ceb900cdS猫头猫 } 912ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 913ceb900cdS猫头猫 914ceb900cdS猫头猫 return result; 915ceb900cdS猫头猫 } catch (e: any) { 916ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 917ceb900cdS猫头猫 return { 918ceb900cdS猫头猫 isEnd: true, 919ceb900cdS猫头猫 data: [], 920ceb900cdS猫头猫 }; 921ceb900cdS猫头猫 } 922ceb900cdS猫头猫 } 923927dbe93S猫头猫} 924d5bfeb7eS猫头猫//#endregion 9251a5528a0S猫头猫 926927dbe93S猫头猫let plugins: Array<Plugin> = []; 927927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 92874d0cf81S猫头猫 929d5bfeb7eS猫头猫//#region 本地音乐插件 93074d0cf81S猫头猫/** 本地插件 */ 93174d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 9320e4173cdS猫头猫 return { 933d5bfeb7eS猫头猫 platform: localPluginPlatform, 93474d0cf81S猫头猫 _path: '', 93574d0cf81S猫头猫 async getMusicInfo(musicBase) { 93674d0cf81S猫头猫 const localPath = getInternalData<string>( 93774d0cf81S猫头猫 musicBase, 93874d0cf81S猫头猫 InternalDataType.LOCALPATH, 9390e4173cdS猫头猫 ); 94074d0cf81S猫头猫 if (localPath) { 94174d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 94274d0cf81S猫头猫 return { 94374d0cf81S猫头猫 artwork: coverImg, 94474d0cf81S猫头猫 }; 94574d0cf81S猫头猫 } 94674d0cf81S猫头猫 return null; 94774d0cf81S猫头猫 }, 9487993f90eS猫头猫 async getLyric(musicBase) { 9497993f90eS猫头猫 const localPath = getInternalData<string>( 9507993f90eS猫头猫 musicBase, 9517993f90eS猫头猫 InternalDataType.LOCALPATH, 9527993f90eS猫头猫 ); 9533a6f67b1S猫头猫 let rawLrc: string | null = null; 9547993f90eS猫头猫 if (localPath) { 9553a6f67b1S猫头猫 // 读取内嵌歌词 9563a6f67b1S猫头猫 try { 9573a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 9583a6f67b1S猫头猫 } catch (e) { 959a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 9607993f90eS猫头猫 } 9613a6f67b1S猫头猫 if (!rawLrc) { 9623a6f67b1S猫头猫 // 读取配置歌词 9633a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 9643a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 9653a6f67b1S猫头猫 9663a6f67b1S猫头猫 try { 9673a6f67b1S猫头猫 if (await exists(lrcPath)) { 9683a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 9693a6f67b1S猫头猫 } 9703a6f67b1S猫头猫 } catch {} 9713a6f67b1S猫头猫 } 9723a6f67b1S猫头猫 } 9733a6f67b1S猫头猫 9743a6f67b1S猫头猫 return rawLrc 9753a6f67b1S猫头猫 ? { 9763a6f67b1S猫头猫 rawLrc, 9773a6f67b1S猫头猫 } 9783a6f67b1S猫头猫 : null; 9797993f90eS猫头猫 }, 980a84a85c5S猫头猫 async importMusicItem(urlLike) { 981a84a85c5S猫头猫 let meta: any = {}; 982a84a85c5S猫头猫 try { 983a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 984a84a85c5S猫头猫 } catch {} 985a84a85c5S猫头猫 const id = await FileSystem.hash(urlLike, 'MD5'); 986a84a85c5S猫头猫 return { 987a84a85c5S猫头猫 id: id, 988a84a85c5S猫头猫 platform: '本地', 989a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 990a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 991a84a85c5S猫头猫 duration: parseInt(meta?.duration ?? '0') / 1000, 992a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 993a84a85c5S猫头猫 artwork: '', 994a84a85c5S猫头猫 [internalSerializeKey]: { 995a84a85c5S猫头猫 localPath: urlLike, 996a84a85c5S猫头猫 }, 997a84a85c5S猫头猫 }; 998a84a85c5S猫头猫 }, 99974d0cf81S猫头猫 }; 100074d0cf81S猫头猫}, ''); 10017993f90eS猫头猫localFilePlugin.hash = localPluginHash; 1002927dbe93S猫头猫 1003d5bfeb7eS猫头猫//#endregion 1004d5bfeb7eS猫头猫 1005927dbe93S猫头猫async function setup() { 1006927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 1007927dbe93S猫头猫 try { 1008927dbe93S猫头猫 // 加载插件 1009927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 1010927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 1011927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 10121e263108S猫头猫 trace('初始化插件', _pluginUrl); 10131e263108S猫头猫 if ( 10141e263108S猫头猫 _pluginUrl.isFile() && 10151e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 10161e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 10171e263108S猫头猫 ) { 1018927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 1019927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 10204060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 10214060c00aS猫头猫 p => p.hash === plugin.hash, 10224060c00aS猫头猫 ); 1023927dbe93S猫头猫 if (_pluginIndex !== -1) { 1024927dbe93S猫头猫 // 重复插件,直接忽略 10250c266394S猫头猫 continue; 1026927dbe93S猫头猫 } 1027927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 1028927dbe93S猫头猫 } 1029927dbe93S猫头猫 } 1030927dbe93S猫头猫 1031927dbe93S猫头猫 plugins = _plugins; 1032e08d37a3S猫头猫 /** 初始化meta信息 */ 1033c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 1034c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 1035c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 1036c2b3a262S猫头猫 for (let plugin of plugins) { 1037c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1038c2b3a262S猫头猫 plugin.state = 'disabled'; 1039c2b3a262S猫头猫 } 1040c2b3a262S猫头猫 } 1041c2b3a262S猫头猫 pluginStateMapper.notify(); 1042927dbe93S猫头猫 } catch (e: any) { 10434060c00aS猫头猫 ToastAndroid.show( 10444060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 10454060c00aS猫头猫 ToastAndroid.LONG, 10464060c00aS猫头猫 ); 10471a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1048927dbe93S猫头猫 throw e; 1049927dbe93S猫头猫 } 1050927dbe93S猫头猫} 1051927dbe93S猫头猫 1052e36e2599S猫头猫interface IInstallPluginConfig { 1053e36e2599S猫头猫 notCheckVersion?: boolean; 1054e36e2599S猫头猫} 1055e36e2599S猫头猫 1056927dbe93S猫头猫// 安装插件 1057e36e2599S猫头猫async function installPlugin( 1058e36e2599S猫头猫 pluginPath: string, 1059e36e2599S猫头猫 config?: IInstallPluginConfig, 1060e36e2599S猫头猫) { 106122c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1062927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1063e36e2599S猫头猫 1064e36e2599S猫头猫 if (funcCode) { 1065927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1066927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1067927dbe93S猫头猫 if (_pluginIndex !== -1) { 1068e36e2599S猫头猫 // 静默忽略 1069e36e2599S猫头猫 return plugin; 1070927dbe93S猫头猫 } 1071e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1072e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1073e36e2599S猫头猫 if ( 1074e36e2599S猫头猫 compare( 1075e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1076e36e2599S猫头猫 plugin.instance.version ?? '', 1077e36e2599S猫头猫 '>', 1078e36e2599S猫头猫 ) 1079e36e2599S猫头猫 ) { 1080e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1081e36e2599S猫头猫 } 1082e36e2599S猫头猫 } 1083e36e2599S猫头猫 1084927dbe93S猫头猫 if (plugin.hash !== '') { 1085927dbe93S猫头猫 const fn = nanoid(); 1086e36e2599S猫头猫 if (oldVersionPlugin) { 1087e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1088e36e2599S猫头猫 try { 1089e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1090e36e2599S猫头猫 } catch {} 1091e36e2599S猫头猫 } 1092927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1093927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1094927dbe93S猫头猫 plugin.path = _pluginPath; 1095927dbe93S猫头猫 plugins = plugins.concat(plugin); 1096927dbe93S猫头猫 pluginStateMapper.notify(); 1097a84a85c5S猫头猫 return plugin; 1098927dbe93S猫头猫 } 1099e36e2599S猫头猫 throw new Error('插件无法解析!'); 1100927dbe93S猫头猫 } 1101e36e2599S猫头猫 throw new Error('插件无法识别!'); 1102c2b3a262S猫头猫} 1103c2b3a262S猫头猫 1104c2b3a262S猫头猫async function installPluginFromUrl( 1105c2b3a262S猫头猫 url: string, 1106c2b3a262S猫头猫 config?: IInstallPluginConfig, 1107c2b3a262S猫头猫) { 110858992c6bS猫头猫 try { 110958992c6bS猫头猫 const funcCode = (await axios.get(url)).data; 111058992c6bS猫头猫 if (funcCode) { 111158992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 111258992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 111358992c6bS猫头猫 if (_pluginIndex !== -1) { 11148b7ddca8S猫头猫 // 静默忽略 11158b7ddca8S猫头猫 return; 111658992c6bS猫头猫 } 111725c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1118c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 111925c1bd29S猫头猫 if ( 112025c1bd29S猫头猫 compare( 112125c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 112225c1bd29S猫头猫 plugin.instance.version ?? '', 112325c1bd29S猫头猫 '>', 112425c1bd29S猫头猫 ) 112525c1bd29S猫头猫 ) { 112625c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 112725c1bd29S猫头猫 } 112825c1bd29S猫头猫 } 112925c1bd29S猫头猫 113058992c6bS猫头猫 if (plugin.hash !== '') { 113158992c6bS猫头猫 const fn = nanoid(); 113258992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 113358992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 113458992c6bS猫头猫 plugin.path = _pluginPath; 113558992c6bS猫头猫 plugins = plugins.concat(plugin); 113625c1bd29S猫头猫 if (oldVersionPlugin) { 113725c1bd29S猫头猫 plugins = plugins.filter( 113825c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 113925c1bd29S猫头猫 ); 114025c1bd29S猫头猫 try { 114125c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 114225c1bd29S猫头猫 } catch {} 114325c1bd29S猫头猫 } 114458992c6bS猫头猫 pluginStateMapper.notify(); 114558992c6bS猫头猫 return; 114658992c6bS猫头猫 } 114774acbfc0S猫头猫 throw new Error('插件无法解析!'); 114858992c6bS猫头猫 } 114925c1bd29S猫头猫 } catch (e: any) { 1150ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 115158992c6bS猫头猫 errorLog('URL安装插件失败', e); 115225c1bd29S猫头猫 throw new Error(e?.message ?? ''); 115358992c6bS猫头猫 } 115458992c6bS猫头猫} 115558992c6bS猫头猫 1156927dbe93S猫头猫/** 卸载插件 */ 1157927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1158927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1159927dbe93S猫头猫 if (targetIndex !== -1) { 1160927dbe93S猫头猫 try { 116124e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1162927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1163927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1164927dbe93S猫头猫 pluginStateMapper.notify(); 1165*43eb30bfS猫头猫 // 防止其他重名 116624e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 1167*43eb30bfS猫头猫 MediaMeta.removeAll(pluginName); 116824e5e74aS猫头猫 } 1169927dbe93S猫头猫 } catch {} 1170927dbe93S猫头猫 } 1171927dbe93S猫头猫} 1172927dbe93S猫头猫 117308882a77S猫头猫async function uninstallAllPlugins() { 117408882a77S猫头猫 await Promise.all( 117508882a77S猫头猫 plugins.map(async plugin => { 117608882a77S猫头猫 try { 117708882a77S猫头猫 const pluginName = plugin.name; 117808882a77S猫头猫 await unlink(plugin.path); 1179*43eb30bfS猫头猫 MediaMeta.removeAll(pluginName); 118008882a77S猫头猫 } catch (e) {} 118108882a77S猫头猫 }), 118208882a77S猫头猫 ); 118308882a77S猫头猫 plugins = []; 118408882a77S猫头猫 pluginStateMapper.notify(); 1185e08d37a3S猫头猫 1186e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1187e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1188e08d37a3S猫头猫 .then(fns => { 1189e08d37a3S猫头猫 fns.forEach(fn => { 1190e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1191e08d37a3S猫头猫 }); 1192e08d37a3S猫头猫 }) 1193e08d37a3S猫头猫 .catch(emptyFunction); 119408882a77S猫头猫} 119508882a77S猫头猫 119625c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 119725c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 119825c1bd29S猫头猫 if (!updateUrl) { 119925c1bd29S猫头猫 throw new Error('没有更新源'); 120025c1bd29S猫头猫 } 120125c1bd29S猫头猫 try { 120225c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 120325c1bd29S猫头猫 } catch (e: any) { 120425c1bd29S猫头猫 if (e.message === '插件已安装') { 120525c1bd29S猫头猫 throw new Error('当前已是最新版本'); 120625c1bd29S猫头猫 } else { 120725c1bd29S猫头猫 throw e; 120825c1bd29S猫头猫 } 120925c1bd29S猫头猫 } 121025c1bd29S猫头猫} 121125c1bd29S猫头猫 1212927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 12132c595535S猫头猫 return getByName(mediaItem?.platform); 1214927dbe93S猫头猫} 1215927dbe93S猫头猫 1216927dbe93S猫头猫function getByHash(hash: string) { 12177993f90eS猫头猫 return hash === localPluginHash 12187993f90eS猫头猫 ? localFilePlugin 12197993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1220927dbe93S猫头猫} 1221927dbe93S猫头猫 1222927dbe93S猫头猫function getByName(name: string) { 12237993f90eS猫头猫 return name === localPluginPlatform 12240e4173cdS猫头猫 ? localFilePlugin 12250e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1226927dbe93S猫头猫} 1227927dbe93S猫头猫 1228927dbe93S猫头猫function getValidPlugins() { 1229927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1230927dbe93S猫头猫} 1231927dbe93S猫头猫 12322b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 12332b80a429S猫头猫 return plugins.filter( 12342b80a429S猫头猫 _ => 12352b80a429S猫头猫 _.state === 'enabled' && 12362b80a429S猫头猫 _.instance.search && 123739ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 123839ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 12392b80a429S猫头猫 : true), 12402b80a429S猫头猫 ); 1241efb9da24S猫头猫} 1242efb9da24S猫头猫 12432b80a429S猫头猫function getSortedSearchablePlugins( 12442b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 12452b80a429S猫头猫) { 12462b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1247e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1248e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1249e08d37a3S猫头猫 0 1250e08d37a3S猫头猫 ? -1 1251e08d37a3S猫头猫 : 1, 1252e08d37a3S猫头猫 ); 1253e08d37a3S猫头猫} 1254e08d37a3S猫头猫 125515feccc1S猫头猫function getTopListsablePlugins() { 125615feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 125715feccc1S猫头猫} 125815feccc1S猫头猫 125915feccc1S猫头猫function getSortedTopListsablePlugins() { 126015feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 126115feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 126215feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 126315feccc1S猫头猫 0 126415feccc1S猫头猫 ? -1 126515feccc1S猫头猫 : 1, 126615feccc1S猫头猫 ); 126715feccc1S猫头猫} 126815feccc1S猫头猫 1269ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1270ceb900cdS猫头猫 return plugins.filter( 1271ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1272ceb900cdS猫头猫 ); 1273ceb900cdS猫头猫} 1274ceb900cdS猫头猫 1275ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1276ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1277ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1278ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1279ceb900cdS猫头猫 0 1280ceb900cdS猫头猫 ? -1 1281ceb900cdS猫头猫 : 1, 1282ceb900cdS猫头猫 ); 1283ceb900cdS猫头猫} 1284ceb900cdS猫头猫 1285e08d37a3S猫头猫function useSortedPlugins() { 1286e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1287e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1288e08d37a3S猫头猫 128934588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 129034588741S猫头猫 [..._plugins].sort((a, b) => 1291e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1292e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1293e08d37a3S猫头猫 0 1294e08d37a3S猫头猫 ? -1 1295e08d37a3S猫头猫 : 1, 129634588741S猫头猫 ), 1297e08d37a3S猫头猫 ); 129834588741S猫头猫 129934588741S猫头猫 useEffect(() => { 1300d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 130134588741S猫头猫 setSortedPlugins( 130234588741S猫头猫 [..._plugins].sort((a, b) => 130334588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 130434588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 130534588741S猫头猫 0 130634588741S猫头猫 ? -1 130734588741S猫头猫 : 1, 130834588741S猫头猫 ), 130934588741S猫头猫 ); 1310d4cd40d8S猫头猫 }); 131134588741S猫头猫 }, [_plugins, _pluginMetaAll]); 131234588741S猫头猫 131334588741S猫头猫 return sortedPlugins; 1314e08d37a3S猫头猫} 1315e08d37a3S猫头猫 1316c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1317c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1318c2b3a262S猫头猫 if (target) { 1319c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1320c2b3a262S猫头猫 plugins = [...plugins]; 1321c2b3a262S猫头猫 pluginStateMapper.notify(); 1322c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1323c2b3a262S猫头猫 } 1324c2b3a262S猫头猫} 1325c2b3a262S猫头猫 1326927dbe93S猫头猫const PluginManager = { 1327927dbe93S猫头猫 setup, 1328927dbe93S猫头猫 installPlugin, 132958992c6bS猫头猫 installPluginFromUrl, 133025c1bd29S猫头猫 updatePlugin, 1331927dbe93S猫头猫 uninstallPlugin, 1332927dbe93S猫头猫 getByMedia, 1333927dbe93S猫头猫 getByHash, 1334927dbe93S猫头猫 getByName, 1335927dbe93S猫头猫 getValidPlugins, 1336efb9da24S猫头猫 getSearchablePlugins, 1337e08d37a3S猫头猫 getSortedSearchablePlugins, 133815feccc1S猫头猫 getTopListsablePlugins, 1339ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 134015feccc1S猫头猫 getSortedTopListsablePlugins, 13415276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1342e08d37a3S猫头猫 useSortedPlugins, 134308882a77S猫头猫 uninstallAllPlugins, 1344c2b3a262S猫头猫 setPluginEnabled, 13455276aef9S猫头猫}; 1346927dbe93S猫头猫 1347927dbe93S猫头猫export default PluginManager; 1348