126f32636S猫头猫import RNFS, { 2927dbe93S猫头猫 copyFile, 3927dbe93S猫头猫 exists, 4927dbe93S猫头猫 readDir, 5927dbe93S猫头猫 readFile, 6*42a9f3e6Smaotoumao stat, 7927dbe93S猫头猫 unlink, 8927dbe93S猫头猫 writeFile, 9927dbe93S猫头猫} from 'react-native-fs'; 10927dbe93S猫头猫import CryptoJs from 'crypto-js'; 11927dbe93S猫头猫import dayjs from 'dayjs'; 12927dbe93S猫头猫import axios from 'axios'; 13ef60be1cS猫头猫import bigInt from 'big-integer'; 14ef60be1cS猫头猫import qs from 'qs'; 1528ccceb7S猫头猫import * as webdav from 'webdav'; 16d4cd40d8S猫头猫import {InteractionManager, ToastAndroid} from 'react-native'; 17927dbe93S猫头猫import pathConst from '@/constants/pathConst'; 1825c1bd29S猫头猫import {compare, satisfies} from 'compare-versions'; 19927dbe93S猫头猫import DeviceInfo from 'react-native-device-info'; 20927dbe93S猫头猫import StateMapper from '@/utils/stateMapper'; 215589cdf3S猫头猫import MediaExtra from './mediaExtra'; 22927dbe93S猫头猫import {nanoid} from 'nanoid'; 23ea6d708fS猫头猫import {devLog, errorLog, trace} from '../utils/log'; 24cfa0fc07S猫头猫import { 250e4173cdS猫头猫 getInternalData, 260e4173cdS猫头猫 InternalDataType, 270e4173cdS猫头猫 isSameMediaItem, 280e4173cdS猫头猫 resetMediaItem, 290e4173cdS猫头猫} from '@/utils/mediaItem'; 307993f90eS猫头猫import { 317993f90eS猫头猫 CacheControl, 32e08d37a3S猫头猫 emptyFunction, 337993f90eS猫头猫 internalSerializeKey, 347993f90eS猫头猫 localPluginHash, 357993f90eS猫头猫 localPluginPlatform, 367993f90eS猫头猫} from '@/constants/commonConst'; 37927dbe93S猫头猫import delay from '@/utils/delay'; 384d9d3c4cS猫头猫import * as cheerio from 'cheerio'; 397d7e864fS猫头猫import CookieManager from '@react-native-cookies/cookies'; 407d7e864fS猫头猫import he from 'he'; 41ef714860S猫头猫import Network from './network'; 420e4173cdS猫头猫import LocalMusicSheet from './localMusicSheet'; 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猫头猫} 256*42a9f3e6Smaotoumao 257d5bfeb7eS猫头猫//#endregion 258927dbe93S猫头猫 259d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 260927dbe93S猫头猫/** 有缓存等信息 */ 261927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 262927dbe93S猫头猫 private plugin; 263*42a9f3e6Smaotoumao 264927dbe93S猫头猫 constructor(plugin: Plugin) { 265927dbe93S猫头猫 this.plugin = plugin; 266927dbe93S猫头猫 } 267*42a9f3e6Smaotoumao 268927dbe93S猫头猫 /** 搜索 */ 269927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 270927dbe93S猫头猫 query: string, 271927dbe93S猫头猫 page: number, 272927dbe93S猫头猫 type: T, 273927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 274927dbe93S猫头猫 if (!this.plugin.instance.search) { 275927dbe93S猫头猫 return { 276927dbe93S猫头猫 isEnd: true, 277927dbe93S猫头猫 data: [], 278927dbe93S猫头猫 }; 279927dbe93S猫头猫 } 280927dbe93S猫头猫 2814060c00aS猫头猫 const result = 2824060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 283927dbe93S猫头猫 if (Array.isArray(result.data)) { 284927dbe93S猫头猫 result.data.forEach(_ => { 285927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 286927dbe93S猫头猫 }); 287927dbe93S猫头猫 return { 288927dbe93S猫头猫 isEnd: result.isEnd ?? true, 289927dbe93S猫头猫 data: result.data, 290927dbe93S猫头猫 }; 291927dbe93S猫头猫 } 292927dbe93S猫头猫 return { 293927dbe93S猫头猫 isEnd: true, 294927dbe93S猫头猫 data: [], 295927dbe93S猫头猫 }; 296927dbe93S猫头猫 } 297927dbe93S猫头猫 298927dbe93S猫头猫 /** 获取真实源 */ 29920e6a092S猫头猫 async getMediaSource( 300927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 301abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 302927dbe93S猫头猫 retryCount = 1, 303dc160d50S猫头猫 notUpdateCache = false, 304192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 305927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 30643eb30bfS猫头猫 const mediaExtra = MediaExtra.get(musicItem); 307927dbe93S猫头猫 const localPath = 30843eb30bfS猫头猫 mediaExtra?.localPath || 30943eb30bfS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) || 3100e4173cdS猫头猫 getInternalData<string>( 3110e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 3120e4173cdS猫头猫 InternalDataType.LOCALPATH, 3130e4173cdS猫头猫 ); 314*42a9f3e6Smaotoumao if (localPath && (await exists(localPath))) { 3150e4173cdS猫头猫 trace('本地播放', localPath); 31643eb30bfS猫头猫 if (mediaExtra && mediaExtra.localPath !== localPath) { 31743eb30bfS猫头猫 // 修正一下本地数据 31843eb30bfS猫头猫 MediaExtra.update(musicItem, { 31943eb30bfS猫头猫 localPath, 32043eb30bfS猫头猫 }); 32143eb30bfS猫头猫 } 322927dbe93S猫头猫 return { 32311908939S猫头猫 url: addFileScheme(localPath), 324927dbe93S猫头猫 }; 32543eb30bfS猫头猫 } else if (mediaExtra?.localPath) { 32643eb30bfS猫头猫 MediaExtra.update(musicItem, { 32743eb30bfS猫头猫 localPath: undefined, 32843eb30bfS猫头猫 }); 329927dbe93S猫头猫 } 330a84a85c5S猫头猫 3317993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 332f5935920S猫头猫 throw new Error('本地音乐不存在'); 333f5935920S猫头猫 } 334927dbe93S猫头猫 // 2. 缓存播放 33543eb30bfS猫头猫 const mediaCache = MediaCache.getMediaCache( 33643eb30bfS猫头猫 musicItem, 33743eb30bfS猫头猫 ) as IMusic.IMusicItem | null; 338985f8e75S猫头猫 const pluginCacheControl = 339985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 340cfa0fc07S猫头猫 if ( 341cfa0fc07S猫头猫 mediaCache && 34243eb30bfS猫头猫 mediaCache?.source?.[quality]?.url && 34348f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 34448f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 345ef714860S猫头猫 Network.isOffline())) 346cfa0fc07S猫头猫 ) { 3475276aef9S猫头猫 trace('播放', '缓存播放'); 34843eb30bfS猫头猫 const qualityInfo = mediaCache.source[quality]; 349927dbe93S猫头猫 return { 35043eb30bfS猫头猫 url: qualityInfo!.url, 351927dbe93S猫头猫 headers: mediaCache.headers, 3524060c00aS猫头猫 userAgent: 3534060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 354927dbe93S猫头猫 }; 355927dbe93S猫头猫 } 356927dbe93S猫头猫 // 3. 插件解析 35720e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 358a7b42a4cS猫头猫 const {url, auth} = formatAuthUrl( 359a7b42a4cS猫头猫 musicItem?.qualities?.[quality]?.url ?? musicItem.url, 360a7b42a4cS猫头猫 ); 361a7b42a4cS猫头猫 return { 362a7b42a4cS猫头猫 url: url, 363a7b42a4cS猫头猫 headers: auth 364a7b42a4cS猫头猫 ? { 365a7b42a4cS猫头猫 Authorization: auth, 366a7b42a4cS猫头猫 } 367a7b42a4cS猫头猫 : undefined, 368a7b42a4cS猫头猫 }; 369927dbe93S猫头猫 } 370927dbe93S猫头猫 try { 371abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 372abaede57S猫头猫 musicItem, 373abaede57S猫头猫 quality, 374abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 375927dbe93S猫头猫 if (!url) { 376a28eac61S猫头猫 throw new Error('NOT RETRY'); 377927dbe93S猫头猫 } 3785276aef9S猫头猫 trace('播放', '插件播放'); 379927dbe93S猫头猫 const result = { 380927dbe93S猫头猫 url, 381927dbe93S猫头猫 headers, 382927dbe93S猫头猫 userAgent: headers?.['user-agent'], 383cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 384a7b42a4cS猫头猫 const authFormattedResult = formatAuthUrl(result.url!); 385a7b42a4cS猫头猫 if (authFormattedResult.auth) { 386a7b42a4cS猫头猫 result.url = authFormattedResult.url; 387a7b42a4cS猫头猫 result.headers = { 388a7b42a4cS猫头猫 ...(result.headers ?? {}), 389a7b42a4cS猫头猫 Authorization: authFormattedResult.auth, 390a7b42a4cS猫头猫 }; 391a7b42a4cS猫头猫 } 392927dbe93S猫头猫 393dc160d50S猫头猫 if ( 394dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 395dc160d50S猫头猫 !notUpdateCache 396dc160d50S猫头猫 ) { 39743eb30bfS猫头猫 // 更新缓存 39843eb30bfS猫头猫 const cacheSource = { 39943eb30bfS猫头猫 headers: result.headers, 40043eb30bfS猫头猫 userAgent: result.userAgent, 40143eb30bfS猫头猫 url, 40243eb30bfS猫头猫 }; 40343eb30bfS猫头猫 let realMusicItem = { 40443eb30bfS猫头猫 ...musicItem, 40543eb30bfS猫头猫 ...(mediaCache || {}), 40643eb30bfS猫头猫 }; 40743eb30bfS猫头猫 realMusicItem.source = { 40843eb30bfS猫头猫 ...(realMusicItem.source || {}), 40943eb30bfS猫头猫 [quality]: cacheSource, 41043eb30bfS猫头猫 }; 41143eb30bfS猫头猫 41243eb30bfS猫头猫 MediaCache.setMediaCache(realMusicItem); 413752ffc5aS猫头猫 } 414927dbe93S猫头猫 return result; 415927dbe93S猫头猫 } catch (e: any) { 416a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 417927dbe93S猫头猫 await delay(150); 418abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 419927dbe93S猫头猫 } 420927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 421ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 422192ae2b0S猫头猫 return null; 423927dbe93S猫头猫 } 424927dbe93S猫头猫 } 425927dbe93S猫头猫 426927dbe93S猫头猫 /** 获取音乐详情 */ 427927dbe93S猫头猫 async getMusicInfo( 428927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 42974d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 430927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 431d704daedS猫头猫 return null; 432927dbe93S猫头猫 } 43374d0cf81S猫头猫 try { 434927dbe93S猫头猫 return ( 435927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 4367993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 437d704daedS猫头猫 ) ?? null 438927dbe93S猫头猫 ); 439ea6d708fS猫头猫 } catch (e: any) { 440ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 441d704daedS猫头猫 return null; 44274d0cf81S猫头猫 } 443927dbe93S猫头猫 } 444927dbe93S猫头猫 4457e883dbbS猫头猫 /** 4467e883dbbS猫头猫 * 4477e883dbbS猫头猫 * getLyric(musicItem) => { 4487e883dbbS猫头猫 * lyric: string; 4497e883dbbS猫头猫 * trans: string; 4507e883dbbS猫头猫 * } 4517e883dbbS猫头猫 * 4527e883dbbS猫头猫 */ 453927dbe93S猫头猫 /** 获取歌词 */ 454927dbe93S猫头猫 async getLyric( 45543eb30bfS猫头猫 originalMusicItem: IMusic.IMusicItemBase, 456927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 45743eb30bfS猫头猫 // 1.额外存储的meta信息(关联歌词) 4585589cdf3S猫头猫 const meta = MediaExtra.get(originalMusicItem); 45943eb30bfS猫头猫 let musicItem: IMusic.IMusicItem; 460927dbe93S猫头猫 if (meta && meta.associatedLrc) { 46143eb30bfS猫头猫 musicItem = meta.associatedLrc as IMusic.IMusicItem; 46243eb30bfS猫头猫 } else { 46343eb30bfS猫头猫 musicItem = originalMusicItem as IMusic.IMusicItem; 464927dbe93S猫头猫 } 46543eb30bfS猫头猫 46643eb30bfS猫头猫 const musicItemCache = MediaCache.getMediaCache( 4677a91f04fS猫头猫 musicItem, 46843eb30bfS猫头猫 ) as IMusic.IMusicItemCache | null; 46943eb30bfS猫头猫 47043eb30bfS猫头猫 /** 原始歌词文本 */ 4717e883dbbS猫头猫 let rawLrc: string | null = musicItem.rawLrc || null; 4727e883dbbS猫头猫 let translation: string | null = null; 47343eb30bfS猫头猫 47426f32636S猫头猫 // 2. 本地手动设置的歌词 47526f32636S猫头猫 const platformHash = CryptoJs.MD5(musicItem.platform).toString( 47626f32636S猫头猫 CryptoJs.enc.Hex, 47726f32636S猫头猫 ); 47826f32636S猫头猫 const idHash = CryptoJs.MD5(musicItem.id).toString(CryptoJs.enc.Hex); 47926f32636S猫头猫 if ( 48026f32636S猫头猫 await RNFS.exists( 48126f32636S猫头猫 pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc', 48226f32636S猫头猫 ) 48326f32636S猫头猫 ) { 48426f32636S猫头猫 rawLrc = await RNFS.readFile( 48526f32636S猫头猫 pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc', 48626f32636S猫头猫 'utf8', 48726f32636S猫头猫 ); 48826f32636S猫头猫 48926f32636S猫头猫 if ( 49026f32636S猫头猫 await RNFS.exists( 49126f32636S猫头猫 pathConst.localLrcPath + 49226f32636S猫头猫 platformHash + 49326f32636S猫头猫 '/' + 49426f32636S猫头猫 idHash + 49526f32636S猫头猫 '.tran.lrc', 49626f32636S猫头猫 ) 49726f32636S猫头猫 ) { 49826f32636S猫头猫 translation = 49926f32636S猫头猫 (await RNFS.readFile( 50026f32636S猫头猫 pathConst.localLrcPath + 50126f32636S猫头猫 platformHash + 50226f32636S猫头猫 '/' + 50326f32636S猫头猫 idHash + 50426f32636S猫头猫 '.tran.lrc', 50526f32636S猫头猫 'utf8', 50626f32636S猫头猫 )) || null; 50726f32636S猫头猫 } 50826f32636S猫头猫 50926f32636S猫头猫 return { 51026f32636S猫头猫 rawLrc, 51126f32636S猫头猫 translation: translation || undefined, // TODO: 这里写的不好 51226f32636S猫头猫 }; 51326f32636S猫头猫 } 51426f32636S猫头猫 51543eb30bfS猫头猫 // 2. 缓存歌词 / 对象上本身的歌词 51643eb30bfS猫头猫 if (musicItemCache?.lyric) { 51743eb30bfS猫头猫 // 缓存的远程结果 51843eb30bfS猫头猫 let cacheLyric: ILyric.ILyricSource | null = 51943eb30bfS猫头猫 musicItemCache.lyric || null; 52043eb30bfS猫头猫 // 缓存的本地结果 52143eb30bfS猫头猫 let localLyric: ILyric.ILyricSource | null = 52243eb30bfS猫头猫 musicItemCache.$localLyric || null; 52343eb30bfS猫头猫 5247e883dbbS猫头猫 // 优先用缓存的结果 5257e883dbbS猫头猫 if (cacheLyric.rawLrc || cacheLyric.translation) { 52643eb30bfS猫头猫 return { 5277e883dbbS猫头猫 rawLrc: cacheLyric.rawLrc, 5287e883dbbS猫头猫 translation: cacheLyric.translation, 52943eb30bfS猫头猫 }; 53043eb30bfS猫头猫 } 53143eb30bfS猫头猫 5327e883dbbS猫头猫 // 本地其实是缓存的路径 5337e883dbbS猫头猫 if (localLyric) { 5347e883dbbS猫头猫 let needRefetch = false; 5357e883dbbS猫头猫 if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) { 5367e883dbbS猫头猫 rawLrc = await readFile(localLyric.rawLrc, 'utf8'); 5377e883dbbS猫头猫 } else if (localLyric.rawLrc) { 5387e883dbbS猫头猫 needRefetch = true; 5397e883dbbS猫头猫 } 5407e883dbbS猫头猫 if ( 5417e883dbbS猫头猫 localLyric.translation && 5427e883dbbS猫头猫 (await exists(localLyric.translation)) 5437e883dbbS猫头猫 ) { 5447e883dbbS猫头猫 translation = await readFile( 5457e883dbbS猫头猫 localLyric.translation, 5467e883dbbS猫头猫 'utf8', 5477e883dbbS猫头猫 ); 5487e883dbbS猫头猫 } else if (localLyric.translation) { 5497e883dbbS猫头猫 needRefetch = true; 55043eb30bfS猫头猫 } 55143eb30bfS猫头猫 5527e883dbbS猫头猫 if (!needRefetch && (rawLrc || translation)) { 55343eb30bfS猫头猫 return { 5547e883dbbS猫头猫 rawLrc: rawLrc || undefined, 5557e883dbbS猫头猫 translation: translation || undefined, 55643eb30bfS猫头猫 }; 55743eb30bfS猫头猫 } 55843eb30bfS猫头猫 } 55943eb30bfS猫头猫 } 56043eb30bfS猫头猫 56143eb30bfS猫头猫 // 3. 无缓存歌词/无自带歌词/无本地歌词 56243eb30bfS猫头猫 let lrcSource: ILyric.ILyricSource | null; 56343eb30bfS猫头猫 if (isSameMediaItem(originalMusicItem, musicItem)) { 56443eb30bfS猫头猫 lrcSource = 56543eb30bfS猫头猫 (await this.plugin.instance 5667e883dbbS猫头猫 ?.getLyric?.(resetMediaItem(musicItem, undefined, true)) 56743eb30bfS猫头猫 ?.catch(() => null)) || null; 56843eb30bfS猫头猫 } else { 56943eb30bfS猫头猫 lrcSource = 57043eb30bfS猫头猫 (await PluginManager.getByMedia(musicItem) 57143eb30bfS猫头猫 ?.instance?.getLyric?.( 57243eb30bfS猫头猫 resetMediaItem(musicItem, undefined, true), 57343eb30bfS猫头猫 ) 57443eb30bfS猫头猫 ?.catch(() => null)) || null; 57543eb30bfS猫头猫 } 57643eb30bfS猫头猫 57743eb30bfS猫头猫 if (lrcSource) { 5787e883dbbS猫头猫 rawLrc = lrcSource?.rawLrc || rawLrc; 5797e883dbbS猫头猫 translation = lrcSource?.translation || null; 58043eb30bfS猫头猫 5817e883dbbS猫头猫 const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc; 58243eb30bfS猫头猫 5837e883dbbS猫头猫 // 本地的文件名 5847e883dbbS猫头猫 let filename: string | undefined = `${ 5857e883dbbS猫头猫 pathConst.lrcCachePath 5867e883dbbS猫头猫 }${nanoid()}.lrc`; 5877e883dbbS猫头猫 let filenameTrans: string | undefined = `${ 5887e883dbbS猫头猫 pathConst.lrcCachePath 5897e883dbbS猫头猫 }${nanoid()}.lrc`; 59043eb30bfS猫头猫 5917e883dbbS猫头猫 // 旧版本兼容 5927e883dbbS猫头猫 if (!(rawLrc || translation)) { 5937e883dbbS猫头猫 if (deprecatedLrcUrl) { 59443eb30bfS猫头猫 rawLrc = ( 5957e883dbbS猫头猫 await axios 5967e883dbbS猫头猫 .get(deprecatedLrcUrl, {timeout: 3000}) 5977e883dbbS猫头猫 .catch(() => null) 59843eb30bfS猫头猫 )?.data; 5997e883dbbS猫头猫 } else if (musicItem.rawLrc) { 6007e883dbbS猫头猫 rawLrc = musicItem.rawLrc; 6017e883dbbS猫头猫 } 60243eb30bfS猫头猫 } 60343eb30bfS猫头猫 60443eb30bfS猫头猫 if (rawLrc) { 60543eb30bfS猫头猫 await writeFile(filename, rawLrc, 'utf8'); 60643eb30bfS猫头猫 } else { 6077e883dbbS猫头猫 filename = undefined; 6087e883dbbS猫头猫 } 6097e883dbbS猫头猫 if (translation) { 6107e883dbbS猫头猫 await writeFile(filenameTrans, translation, 'utf8'); 6117e883dbbS猫头猫 } else { 6127e883dbbS猫头猫 filenameTrans = undefined; 6137a91f04fS猫头猫 } 6147a91f04fS猫头猫 6157e883dbbS猫头猫 if (rawLrc || translation) { 6167e883dbbS猫头猫 MediaCache.setMediaCache( 6177e883dbbS猫头猫 produce(musicItemCache || musicItem, draft => { 6187e883dbbS猫头猫 musicItemCache?.$localLyric?.rawLrc; 6197e883dbbS猫头猫 objectPath.set(draft, '$localLyric.rawLrc', filename); 62043eb30bfS猫头猫 objectPath.set( 62143eb30bfS猫头猫 draft, 6227e883dbbS猫头猫 '$localLyric.translation', 6237e883dbbS猫头猫 filenameTrans, 62443eb30bfS猫头猫 ); 62543eb30bfS猫头猫 return draft; 62643eb30bfS猫头猫 }), 62743eb30bfS猫头猫 ); 628927dbe93S猫头猫 return { 6297e883dbbS猫头猫 rawLrc: rawLrc || undefined, 6307e883dbbS猫头猫 translation: translation || undefined, 631927dbe93S猫头猫 }; 632927dbe93S猫头猫 } 633927dbe93S猫头猫 } 63443eb30bfS猫头猫 6353a6f67b1S猫头猫 // 6. 如果是本地文件 63643eb30bfS猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem); 63743eb30bfS猫头猫 if ( 63843eb30bfS猫头猫 originalMusicItem.platform !== localPluginPlatform && 63943eb30bfS猫头猫 isDownloaded 64043eb30bfS猫头猫 ) { 6417e883dbbS猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 64243eb30bfS猫头猫 64343eb30bfS猫头猫 console.log('本地文件歌词'); 64443eb30bfS猫头猫 6453a6f67b1S猫头猫 if (res) { 6463a6f67b1S猫头猫 return res; 6473a6f67b1S猫头猫 } 6483a6f67b1S猫头猫 } 649ea6d708fS猫头猫 devLog('warn', '无歌词'); 650927dbe93S猫头猫 651927dbe93S猫头猫 return null; 652927dbe93S猫头猫 } 653927dbe93S猫头猫 654927dbe93S猫头猫 /** 获取歌词文本 */ 655927dbe93S猫头猫 async getLyricText( 656927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 657927dbe93S猫头猫 ): Promise<string | undefined> { 6587e883dbbS猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 659927dbe93S猫头猫 } 660927dbe93S猫头猫 661927dbe93S猫头猫 /** 获取专辑信息 */ 662927dbe93S猫头猫 async getAlbumInfo( 663927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 664f9afcc0dS猫头猫 page: number = 1, 665f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 666927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 667f9afcc0dS猫头猫 return { 668f9afcc0dS猫头猫 albumItem, 669f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 670f2a4767cS猫头猫 resetMediaItem, 671f2a4767cS猫头猫 this.plugin.name, 672f2a4767cS猫头猫 true, 673f2a4767cS猫头猫 ), 674f9afcc0dS猫头猫 isEnd: true, 675f9afcc0dS猫头猫 }; 676927dbe93S猫头猫 } 677927dbe93S猫头猫 try { 678927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 679927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 680f9afcc0dS猫头猫 page, 681927dbe93S猫头猫 ); 6825276aef9S猫头猫 if (!result) { 6835276aef9S猫头猫 throw new Error(); 6845276aef9S猫头猫 } 685927dbe93S猫头猫 result?.musicList?.forEach(_ => { 686927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 68796744680S猫头猫 _.album = albumItem.title; 688927dbe93S猫头猫 }); 6895276aef9S猫头猫 690f9afcc0dS猫头猫 if (page <= 1) { 691f9afcc0dS猫头猫 // 合并信息 692f9afcc0dS猫头猫 return { 693f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 694f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 695f9afcc0dS猫头猫 musicList: result.musicList, 696f9afcc0dS猫头猫 }; 697f9afcc0dS猫头猫 } else { 698f9afcc0dS猫头猫 return { 699f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 700f9afcc0dS猫头猫 musicList: result.musicList, 701f9afcc0dS猫头猫 }; 702f9afcc0dS猫头猫 } 7034394410dS猫头猫 } catch (e: any) { 7044394410dS猫头猫 trace('获取专辑信息失败', e?.message); 705ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 706ea6d708fS猫头猫 707f9afcc0dS猫头猫 return null; 708927dbe93S猫头猫 } 709927dbe93S猫头猫 } 710927dbe93S猫头猫 7115830c002S猫头猫 /** 获取歌单信息 */ 7125830c002S猫头猫 async getMusicSheetInfo( 7135830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 7145830c002S猫头猫 page: number = 1, 7155830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 7165281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 7175830c002S猫头猫 return { 7185830c002S猫头猫 sheetItem, 7195830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 7205830c002S猫头猫 isEnd: true, 7215830c002S猫头猫 }; 7225830c002S猫头猫 } 7235830c002S猫头猫 try { 7245830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 7255830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 7265830c002S猫头猫 page, 7275830c002S猫头猫 ); 7285830c002S猫头猫 if (!result) { 7295830c002S猫头猫 throw new Error(); 7305830c002S猫头猫 } 7315830c002S猫头猫 result?.musicList?.forEach(_ => { 7325830c002S猫头猫 resetMediaItem(_, this.plugin.name); 7335830c002S猫头猫 }); 7345830c002S猫头猫 7355830c002S猫头猫 if (page <= 1) { 7365830c002S猫头猫 // 合并信息 7375830c002S猫头猫 return { 7385830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 7395830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7405830c002S猫头猫 musicList: result.musicList, 7415830c002S猫头猫 }; 7425830c002S猫头猫 } else { 7435830c002S猫头猫 return { 7445830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 7455830c002S猫头猫 musicList: result.musicList, 7465830c002S猫头猫 }; 7475830c002S猫头猫 } 7485830c002S猫头猫 } catch (e: any) { 7495830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 7505830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 7515830c002S猫头猫 7525830c002S猫头猫 return null; 7535830c002S猫头猫 } 7545830c002S猫头猫 } 7555830c002S猫头猫 756927dbe93S猫头猫 /** 查询作者信息 */ 757efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 758927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 759927dbe93S猫头猫 page: number, 760927dbe93S猫头猫 type: T, 761927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 762efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 763927dbe93S猫头猫 return { 764927dbe93S猫头猫 isEnd: true, 765927dbe93S猫头猫 data: [], 766927dbe93S猫头猫 }; 767927dbe93S猫头猫 } 768927dbe93S猫头猫 try { 769efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 770927dbe93S猫头猫 artistItem, 771927dbe93S猫头猫 page, 772927dbe93S猫头猫 type, 773927dbe93S猫头猫 ); 774927dbe93S猫头猫 if (!result.data) { 775927dbe93S猫头猫 return { 776927dbe93S猫头猫 isEnd: true, 777927dbe93S猫头猫 data: [], 778927dbe93S猫头猫 }; 779927dbe93S猫头猫 } 780927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 781927dbe93S猫头猫 return { 782927dbe93S猫头猫 isEnd: result.isEnd ?? true, 783927dbe93S猫头猫 data: result.data, 784927dbe93S猫头猫 }; 7854394410dS猫头猫 } catch (e: any) { 7864394410dS猫头猫 trace('查询作者信息失败', e?.message); 787ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 788ea6d708fS猫头猫 789927dbe93S猫头猫 throw e; 790927dbe93S猫头猫 } 791927dbe93S猫头猫 } 79208380090S猫头猫 79308380090S猫头猫 /** 导入歌单 */ 79408380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 79508380090S猫头猫 try { 79608380090S猫头猫 const result = 79708380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 79808380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 79908380090S猫头猫 return result; 800ea6d708fS猫头猫 } catch (e: any) { 8010e4173cdS猫头猫 console.log(e); 802ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 803ea6d708fS猫头猫 80408380090S猫头猫 return []; 80508380090S猫头猫 } 80608380090S猫头猫 } 807*42a9f3e6Smaotoumao 8084d9d3c4cS猫头猫 /** 导入单曲 */ 8094d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 8104d9d3c4cS猫头猫 try { 8114d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 8124d9d3c4cS猫头猫 urlLike, 8134d9d3c4cS猫头猫 ); 8144d9d3c4cS猫头猫 if (!result) { 8154d9d3c4cS猫头猫 throw new Error(); 8164d9d3c4cS猫头猫 } 8174d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 8184d9d3c4cS猫头猫 return result; 819ea6d708fS猫头猫 } catch (e: any) { 820ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 821ea6d708fS猫头猫 8224d9d3c4cS猫头猫 return null; 8234d9d3c4cS猫头猫 } 8244d9d3c4cS猫头猫 } 825*42a9f3e6Smaotoumao 826d52aa40eS猫头猫 /** 获取榜单 */ 82792b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 828d52aa40eS猫头猫 try { 829d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 830d52aa40eS猫头猫 if (!result) { 831d52aa40eS猫头猫 throw new Error(); 832d52aa40eS猫头猫 } 833d52aa40eS猫头猫 return result; 834d52aa40eS猫头猫 } catch (e: any) { 835d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 836d52aa40eS猫头猫 return []; 837d52aa40eS猫头猫 } 838d52aa40eS猫头猫 } 839*42a9f3e6Smaotoumao 840d52aa40eS猫头猫 /** 获取榜单详情 */ 841d52aa40eS猫头猫 async getTopListDetail( 84292b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 843956ee1b7S猫头猫 page: number, 844956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 845d52aa40eS猫头猫 try { 846d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 847d52aa40eS猫头猫 topListItem, 848956ee1b7S猫头猫 page, 849d52aa40eS猫头猫 ); 850d52aa40eS猫头猫 if (!result) { 851d52aa40eS猫头猫 throw new Error(); 852d52aa40eS猫头猫 } 853d384662fS猫头猫 if (result.musicList) { 854d384662fS猫头猫 result.musicList.forEach(_ => 855d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 856d384662fS猫头猫 ); 857d384662fS猫头猫 } 858956ee1b7S猫头猫 if (result.isEnd !== false) { 859956ee1b7S猫头猫 result.isEnd = true; 860956ee1b7S猫头猫 } 861d52aa40eS猫头猫 return result; 862d52aa40eS猫头猫 } catch (e: any) { 863d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 864d52aa40eS猫头猫 return { 865956ee1b7S猫头猫 isEnd: true, 866956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 867d52aa40eS猫头猫 musicList: [], 868d52aa40eS猫头猫 }; 869d52aa40eS猫头猫 } 870d52aa40eS猫头猫 } 871ceb900cdS猫头猫 8725830c002S猫头猫 /** 获取推荐歌单的tag */ 873ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 874ceb900cdS猫头猫 try { 875ceb900cdS猫头猫 const result = 876ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 877ceb900cdS猫头猫 if (!result) { 878ceb900cdS猫头猫 throw new Error(); 879ceb900cdS猫头猫 } 880ceb900cdS猫头猫 return result; 881ceb900cdS猫头猫 } catch (e: any) { 882ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 883ceb900cdS猫头猫 return { 884ceb900cdS猫头猫 data: [], 885ceb900cdS猫头猫 }; 886ceb900cdS猫头猫 } 887ceb900cdS猫头猫 } 888*42a9f3e6Smaotoumao 8895830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 890ceb900cdS猫头猫 async getRecommendSheetsByTag( 891ceb900cdS猫头猫 tagItem: ICommon.IUnique, 892ceb900cdS猫头猫 page?: number, 893ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 894ceb900cdS猫头猫 try { 895ceb900cdS猫头猫 const result = 896ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 897ceb900cdS猫头猫 tagItem, 898ceb900cdS猫头猫 page ?? 1, 899ceb900cdS猫头猫 ); 900ceb900cdS猫头猫 if (!result) { 901ceb900cdS猫头猫 throw new Error(); 902ceb900cdS猫头猫 } 903ceb900cdS猫头猫 if (result.isEnd !== false) { 904ceb900cdS猫头猫 result.isEnd = true; 905ceb900cdS猫头猫 } 906ceb900cdS猫头猫 if (!result.data) { 907ceb900cdS猫头猫 result.data = []; 908ceb900cdS猫头猫 } 909ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 910ceb900cdS猫头猫 911ceb900cdS猫头猫 return result; 912ceb900cdS猫头猫 } catch (e: any) { 913ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 914ceb900cdS猫头猫 return { 915ceb900cdS猫头猫 isEnd: true, 916ceb900cdS猫头猫 data: [], 917ceb900cdS猫头猫 }; 918ceb900cdS猫头猫 } 919ceb900cdS猫头猫 } 920da2a2959S猫头猫 921da2a2959S猫头猫 async getMusicComments( 922da2a2959S猫头猫 musicItem: IMusic.IMusicItem, 923da2a2959S猫头猫 ): Promise<ICommon.PaginationResponse<IMedia.IComment>> { 924da2a2959S猫头猫 const result = await this.plugin.instance?.getMusicComments?.( 925da2a2959S猫头猫 musicItem, 926da2a2959S猫头猫 ); 927da2a2959S猫头猫 if (!result) { 928da2a2959S猫头猫 throw new Error(); 929da2a2959S猫头猫 } 930da2a2959S猫头猫 if (result.isEnd !== false) { 931da2a2959S猫头猫 result.isEnd = true; 932da2a2959S猫头猫 } 933da2a2959S猫头猫 if (!result.data) { 934da2a2959S猫头猫 result.data = []; 935da2a2959S猫头猫 } 936da2a2959S猫头猫 937da2a2959S猫头猫 return result; 938da2a2959S猫头猫 } 9397fb90110S猫头猫 9407fb90110S猫头猫 async migrateFromOtherPlugin( 9417fb90110S猫头猫 mediaItem: ICommon.IMediaBase, 9427fb90110S猫头猫 fromPlatform: string, 9437fb90110S猫头猫 ): Promise<{isOk: boolean; data?: ICommon.IMediaBase}> { 9447fb90110S猫头猫 try { 9457fb90110S猫头猫 const result = await this.plugin.instance?.migrateFromOtherPlugin( 9467fb90110S猫头猫 mediaItem, 9477fb90110S猫头猫 fromPlatform, 9487fb90110S猫头猫 ); 9497fb90110S猫头猫 9507fb90110S猫头猫 if ( 9517fb90110S猫头猫 result.isOk && 9527fb90110S猫头猫 result.data?.id && 9537fb90110S猫头猫 result.data?.platform === this.plugin.platform 9547fb90110S猫头猫 ) { 9557fb90110S猫头猫 return { 9567fb90110S猫头猫 isOk: result.isOk, 9577fb90110S猫头猫 data: result.data, 9587fb90110S猫头猫 }; 9597fb90110S猫头猫 } 9607fb90110S猫头猫 return { 9617fb90110S猫头猫 isOk: false, 9627fb90110S猫头猫 }; 9637fb90110S猫头猫 } catch { 9647fb90110S猫头猫 return { 9657fb90110S猫头猫 isOk: false, 9667fb90110S猫头猫 }; 9677fb90110S猫头猫 } 9687fb90110S猫头猫 } 969927dbe93S猫头猫} 970d5bfeb7eS猫头猫//#endregion 9711a5528a0S猫头猫 972927dbe93S猫头猫let plugins: Array<Plugin> = []; 973927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 97474d0cf81S猫头猫 975d5bfeb7eS猫头猫//#region 本地音乐插件 97674d0cf81S猫头猫/** 本地插件 */ 97774d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 9780e4173cdS猫头猫 return { 979d5bfeb7eS猫头猫 platform: localPluginPlatform, 98074d0cf81S猫头猫 _path: '', 98174d0cf81S猫头猫 async getMusicInfo(musicBase) { 98274d0cf81S猫头猫 const localPath = getInternalData<string>( 98374d0cf81S猫头猫 musicBase, 98474d0cf81S猫头猫 InternalDataType.LOCALPATH, 9850e4173cdS猫头猫 ); 98674d0cf81S猫头猫 if (localPath) { 98774d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 98874d0cf81S猫头猫 return { 98974d0cf81S猫头猫 artwork: coverImg, 99074d0cf81S猫头猫 }; 99174d0cf81S猫头猫 } 99274d0cf81S猫头猫 return null; 99374d0cf81S猫头猫 }, 9947993f90eS猫头猫 async getLyric(musicBase) { 9957993f90eS猫头猫 const localPath = getInternalData<string>( 9967993f90eS猫头猫 musicBase, 9977993f90eS猫头猫 InternalDataType.LOCALPATH, 9987993f90eS猫头猫 ); 9993a6f67b1S猫头猫 let rawLrc: string | null = null; 10007993f90eS猫头猫 if (localPath) { 10013a6f67b1S猫头猫 // 读取内嵌歌词 10023a6f67b1S猫头猫 try { 10033a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 10043a6f67b1S猫头猫 } catch (e) { 1005a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 10067993f90eS猫头猫 } 10073a6f67b1S猫头猫 if (!rawLrc) { 10083a6f67b1S猫头猫 // 读取配置歌词 10093a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 10103a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 10113a6f67b1S猫头猫 10123a6f67b1S猫头猫 try { 10133a6f67b1S猫头猫 if (await exists(lrcPath)) { 10143a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 10153a6f67b1S猫头猫 } 10163a6f67b1S猫头猫 } catch {} 10173a6f67b1S猫头猫 } 10183a6f67b1S猫头猫 } 10193a6f67b1S猫头猫 10203a6f67b1S猫头猫 return rawLrc 10213a6f67b1S猫头猫 ? { 10223a6f67b1S猫头猫 rawLrc, 10233a6f67b1S猫头猫 } 10243a6f67b1S猫头猫 : null; 10257993f90eS猫头猫 }, 1026a84a85c5S猫头猫 async importMusicItem(urlLike) { 1027a84a85c5S猫头猫 let meta: any = {}; 1028*42a9f3e6Smaotoumao let id: string; 1029*42a9f3e6Smaotoumao 1030a84a85c5S猫头猫 try { 1031a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 1032*42a9f3e6Smaotoumao const fileStat = await stat(urlLike); 1033*42a9f3e6Smaotoumao id = 1034*42a9f3e6Smaotoumao CryptoJs.MD5(fileStat.originalFilepath).toString( 1035*42a9f3e6Smaotoumao CryptoJs.enc.Hex, 1036*42a9f3e6Smaotoumao ) || nanoid(); 1037*42a9f3e6Smaotoumao } catch { 10385353b473S猫头猫 id = nanoid(); 10395353b473S猫头猫 } 1040*42a9f3e6Smaotoumao 1041a84a85c5S猫头猫 return { 1042a84a85c5S猫头猫 id: id, 1043a84a85c5S猫头猫 platform: '本地', 1044a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 1045a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 10465353b473S猫头猫 duration: parseInt(meta?.duration ?? '0', 10) / 1000, 1047a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 1048a84a85c5S猫头猫 artwork: '', 1049a84a85c5S猫头猫 [internalSerializeKey]: { 1050a84a85c5S猫头猫 localPath: urlLike, 1051a84a85c5S猫头猫 }, 1052a84a85c5S猫头猫 }; 1053a84a85c5S猫头猫 }, 105411908939S猫头猫 async getMediaSource(musicItem, quality) { 105511908939S猫头猫 if (quality === 'standard') { 105611908939S猫头猫 return { 105711908939S猫头猫 url: addFileScheme(musicItem.$?.localPath || musicItem.url), 105811908939S猫头猫 }; 105911908939S猫头猫 } 106011908939S猫头猫 return null; 106111908939S猫头猫 }, 106274d0cf81S猫头猫 }; 106374d0cf81S猫头猫}, ''); 10647993f90eS猫头猫localFilePlugin.hash = localPluginHash; 1065927dbe93S猫头猫 1066d5bfeb7eS猫头猫//#endregion 1067d5bfeb7eS猫头猫 1068927dbe93S猫头猫async function setup() { 1069927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 1070927dbe93S猫头猫 try { 1071927dbe93S猫头猫 // 加载插件 1072927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 1073927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 1074927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 10751e263108S猫头猫 trace('初始化插件', _pluginUrl); 10761e263108S猫头猫 if ( 10771e263108S猫头猫 _pluginUrl.isFile() && 10781e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 10791e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 10801e263108S猫头猫 ) { 1081927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 1082927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 10834060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 10844060c00aS猫头猫 p => p.hash === plugin.hash, 10854060c00aS猫头猫 ); 1086927dbe93S猫头猫 if (_pluginIndex !== -1) { 1087927dbe93S猫头猫 // 重复插件,直接忽略 10880c266394S猫头猫 continue; 1089927dbe93S猫头猫 } 1090927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 1091927dbe93S猫头猫 } 1092927dbe93S猫头猫 } 1093927dbe93S猫头猫 1094927dbe93S猫头猫 plugins = _plugins; 1095e08d37a3S猫头猫 /** 初始化meta信息 */ 1096c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 1097c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 1098c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 1099c2b3a262S猫头猫 for (let plugin of plugins) { 1100c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 1101c2b3a262S猫头猫 plugin.state = 'disabled'; 1102c2b3a262S猫头猫 } 1103c2b3a262S猫头猫 } 1104c2b3a262S猫头猫 pluginStateMapper.notify(); 1105927dbe93S猫头猫 } catch (e: any) { 11064060c00aS猫头猫 ToastAndroid.show( 11074060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 11084060c00aS猫头猫 ToastAndroid.LONG, 11094060c00aS猫头猫 ); 11101a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 1111927dbe93S猫头猫 throw e; 1112927dbe93S猫头猫 } 1113927dbe93S猫头猫} 1114927dbe93S猫头猫 1115e36e2599S猫头猫interface IInstallPluginConfig { 1116e36e2599S猫头猫 notCheckVersion?: boolean; 1117e36e2599S猫头猫} 1118e36e2599S猫头猫 1119740e3947S猫头猫async function installPluginFromRawCode( 1120740e3947S猫头猫 funcCode: string, 1121740e3947S猫头猫 config?: IInstallPluginConfig, 1122740e3947S猫头猫) { 1123740e3947S猫头猫 if (funcCode) { 1124740e3947S猫头猫 const plugin = new Plugin(funcCode, ''); 1125740e3947S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1126740e3947S猫头猫 if (_pluginIndex !== -1) { 1127740e3947S猫头猫 // 静默忽略 1128740e3947S猫头猫 return plugin; 1129740e3947S猫头猫 } 1130740e3947S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1131740e3947S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1132740e3947S猫头猫 if ( 1133740e3947S猫头猫 compare( 1134740e3947S猫头猫 oldVersionPlugin.instance.version ?? '', 1135740e3947S猫头猫 plugin.instance.version ?? '', 1136740e3947S猫头猫 '>', 1137740e3947S猫头猫 ) 1138740e3947S猫头猫 ) { 1139740e3947S猫头猫 throw new Error('已安装更新版本的插件'); 1140740e3947S猫头猫 } 1141740e3947S猫头猫 } 1142740e3947S猫头猫 1143740e3947S猫头猫 if (plugin.hash !== '') { 1144740e3947S猫头猫 const fn = nanoid(); 1145740e3947S猫头猫 if (oldVersionPlugin) { 1146740e3947S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1147740e3947S猫头猫 try { 1148740e3947S猫头猫 await unlink(oldVersionPlugin.path); 1149740e3947S猫头猫 } catch {} 1150740e3947S猫头猫 } 1151740e3947S猫头猫 const pluginPath = `${pathConst.pluginPath}${fn}.js`; 1152740e3947S猫头猫 await writeFile(pluginPath, funcCode, 'utf8'); 1153740e3947S猫头猫 plugin.path = pluginPath; 1154740e3947S猫头猫 plugins = plugins.concat(plugin); 1155740e3947S猫头猫 pluginStateMapper.notify(); 1156740e3947S猫头猫 return plugin; 1157740e3947S猫头猫 } 1158740e3947S猫头猫 throw new Error('插件无法解析!'); 1159740e3947S猫头猫 } 1160740e3947S猫头猫} 1161740e3947S猫头猫 1162927dbe93S猫头猫// 安装插件 1163e36e2599S猫头猫async function installPlugin( 1164e36e2599S猫头猫 pluginPath: string, 1165e36e2599S猫头猫 config?: IInstallPluginConfig, 1166e36e2599S猫头猫) { 116722c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 1168927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 1169e36e2599S猫头猫 1170e36e2599S猫头猫 if (funcCode) { 1171927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 1172927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 1173927dbe93S猫头猫 if (_pluginIndex !== -1) { 1174e36e2599S猫头猫 // 静默忽略 1175e36e2599S猫头猫 return plugin; 1176927dbe93S猫头猫 } 1177e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1178e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 1179e36e2599S猫头猫 if ( 1180e36e2599S猫头猫 compare( 1181e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 1182e36e2599S猫头猫 plugin.instance.version ?? '', 1183e36e2599S猫头猫 '>', 1184e36e2599S猫头猫 ) 1185e36e2599S猫头猫 ) { 1186e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 1187e36e2599S猫头猫 } 1188e36e2599S猫头猫 } 1189e36e2599S猫头猫 1190927dbe93S猫头猫 if (plugin.hash !== '') { 1191927dbe93S猫头猫 const fn = nanoid(); 1192e36e2599S猫头猫 if (oldVersionPlugin) { 1193e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 1194e36e2599S猫头猫 try { 1195e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 1196e36e2599S猫头猫 } catch {} 1197e36e2599S猫头猫 } 1198927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 1199927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 1200927dbe93S猫头猫 plugin.path = _pluginPath; 1201927dbe93S猫头猫 plugins = plugins.concat(plugin); 1202927dbe93S猫头猫 pluginStateMapper.notify(); 1203a84a85c5S猫头猫 return plugin; 1204927dbe93S猫头猫 } 1205e36e2599S猫头猫 throw new Error('插件无法解析!'); 1206927dbe93S猫头猫 } 1207e36e2599S猫头猫 throw new Error('插件无法识别!'); 1208c2b3a262S猫头猫} 1209c2b3a262S猫头猫 1210ee781061S猫头猫const reqHeaders = { 1211ee781061S猫头猫 'Cache-Control': 'no-cache', 1212ee781061S猫头猫 Pragma: 'no-cache', 1213ee781061S猫头猫 Expires: '0', 1214ee781061S猫头猫}; 1215ee781061S猫头猫 1216c2b3a262S猫头猫async function installPluginFromUrl( 1217c2b3a262S猫头猫 url: string, 1218c2b3a262S猫头猫 config?: IInstallPluginConfig, 1219c2b3a262S猫头猫) { 122058992c6bS猫头猫 try { 1221ee781061S猫头猫 const funcCode = ( 1222ee781061S猫头猫 await axios.get(url, { 1223ee781061S猫头猫 headers: reqHeaders, 1224ee781061S猫头猫 }) 1225ee781061S猫头猫 ).data; 122658992c6bS猫头猫 if (funcCode) { 122758992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 122858992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 122958992c6bS猫头猫 if (_pluginIndex !== -1) { 12308b7ddca8S猫头猫 // 静默忽略 12318b7ddca8S猫头猫 return; 123258992c6bS猫头猫 } 123325c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 1234c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 123525c1bd29S猫头猫 if ( 123625c1bd29S猫头猫 compare( 123725c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 123825c1bd29S猫头猫 plugin.instance.version ?? '', 123925c1bd29S猫头猫 '>', 124025c1bd29S猫头猫 ) 124125c1bd29S猫头猫 ) { 124225c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 124325c1bd29S猫头猫 } 124425c1bd29S猫头猫 } 124525c1bd29S猫头猫 124658992c6bS猫头猫 if (plugin.hash !== '') { 124758992c6bS猫头猫 const fn = nanoid(); 124858992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 124958992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 125058992c6bS猫头猫 plugin.path = _pluginPath; 125158992c6bS猫头猫 plugins = plugins.concat(plugin); 125225c1bd29S猫头猫 if (oldVersionPlugin) { 125325c1bd29S猫头猫 plugins = plugins.filter( 125425c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 125525c1bd29S猫头猫 ); 125625c1bd29S猫头猫 try { 125725c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 125825c1bd29S猫头猫 } catch {} 125925c1bd29S猫头猫 } 126058992c6bS猫头猫 pluginStateMapper.notify(); 126158992c6bS猫头猫 return; 126258992c6bS猫头猫 } 126374acbfc0S猫头猫 throw new Error('插件无法解析!'); 126458992c6bS猫头猫 } 126525c1bd29S猫头猫 } catch (e: any) { 1266ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 126758992c6bS猫头猫 errorLog('URL安装插件失败', e); 126825c1bd29S猫头猫 throw new Error(e?.message ?? ''); 126958992c6bS猫头猫 } 127058992c6bS猫头猫} 127158992c6bS猫头猫 1272927dbe93S猫头猫/** 卸载插件 */ 1273927dbe93S猫头猫async function uninstallPlugin(hash: string) { 1274927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 1275927dbe93S猫头猫 if (targetIndex !== -1) { 1276927dbe93S猫头猫 try { 127724e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1278927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1279927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1280927dbe93S猫头猫 pluginStateMapper.notify(); 128143eb30bfS猫头猫 // 防止其他重名 128224e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 12835589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 128424e5e74aS猫头猫 } 1285927dbe93S猫头猫 } catch {} 1286927dbe93S猫头猫 } 1287927dbe93S猫头猫} 1288927dbe93S猫头猫 128908882a77S猫头猫async function uninstallAllPlugins() { 129008882a77S猫头猫 await Promise.all( 129108882a77S猫头猫 plugins.map(async plugin => { 129208882a77S猫头猫 try { 129308882a77S猫头猫 const pluginName = plugin.name; 129408882a77S猫头猫 await unlink(plugin.path); 12955589cdf3S猫头猫 MediaExtra.removeAll(pluginName); 129608882a77S猫头猫 } catch (e) {} 129708882a77S猫头猫 }), 129808882a77S猫头猫 ); 129908882a77S猫头猫 plugins = []; 130008882a77S猫头猫 pluginStateMapper.notify(); 1301e08d37a3S猫头猫 1302e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1303e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1304e08d37a3S猫头猫 .then(fns => { 1305e08d37a3S猫头猫 fns.forEach(fn => { 1306e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1307e08d37a3S猫头猫 }); 1308e08d37a3S猫头猫 }) 1309e08d37a3S猫头猫 .catch(emptyFunction); 131008882a77S猫头猫} 131108882a77S猫头猫 131225c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 131325c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 131425c1bd29S猫头猫 if (!updateUrl) { 131525c1bd29S猫头猫 throw new Error('没有更新源'); 131625c1bd29S猫头猫 } 131725c1bd29S猫头猫 try { 131825c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 131925c1bd29S猫头猫 } catch (e: any) { 132025c1bd29S猫头猫 if (e.message === '插件已安装') { 132125c1bd29S猫头猫 throw new Error('当前已是最新版本'); 132225c1bd29S猫头猫 } else { 132325c1bd29S猫头猫 throw e; 132425c1bd29S猫头猫 } 132525c1bd29S猫头猫 } 132625c1bd29S猫头猫} 132725c1bd29S猫头猫 1328927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 13292c595535S猫头猫 return getByName(mediaItem?.platform); 1330927dbe93S猫头猫} 1331927dbe93S猫头猫 1332927dbe93S猫头猫function getByHash(hash: string) { 13337993f90eS猫头猫 return hash === localPluginHash 13347993f90eS猫头猫 ? localFilePlugin 13357993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1336927dbe93S猫头猫} 1337927dbe93S猫头猫 1338927dbe93S猫头猫function getByName(name: string) { 13397993f90eS猫头猫 return name === localPluginPlatform 13400e4173cdS猫头猫 ? localFilePlugin 13410e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1342927dbe93S猫头猫} 1343927dbe93S猫头猫 1344927dbe93S猫头猫function getValidPlugins() { 1345927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1346927dbe93S猫头猫} 1347927dbe93S猫头猫 13482b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 13492b80a429S猫头猫 return plugins.filter( 13502b80a429S猫头猫 _ => 13512b80a429S猫头猫 _.state === 'enabled' && 13522b80a429S猫头猫 _.instance.search && 135339ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 135439ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 13552b80a429S猫头猫 : true), 13562b80a429S猫头猫 ); 1357efb9da24S猫头猫} 1358efb9da24S猫头猫 13592b80a429S猫头猫function getSortedSearchablePlugins( 13602b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 13612b80a429S猫头猫) { 13622b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1363e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1364e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1365e08d37a3S猫头猫 0 1366e08d37a3S猫头猫 ? -1 1367e08d37a3S猫头猫 : 1, 1368e08d37a3S猫头猫 ); 1369e08d37a3S猫头猫} 1370e08d37a3S猫头猫 137115feccc1S猫头猫function getTopListsablePlugins() { 137215feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 137315feccc1S猫头猫} 137415feccc1S猫头猫 137515feccc1S猫头猫function getSortedTopListsablePlugins() { 137615feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 137715feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 137815feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 137915feccc1S猫头猫 0 138015feccc1S猫头猫 ? -1 138115feccc1S猫头猫 : 1, 138215feccc1S猫头猫 ); 138315feccc1S猫头猫} 138415feccc1S猫头猫 1385ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1386ceb900cdS猫头猫 return plugins.filter( 1387ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1388ceb900cdS猫头猫 ); 1389ceb900cdS猫头猫} 1390ceb900cdS猫头猫 1391ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1392ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1393ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1394ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1395ceb900cdS猫头猫 0 1396ceb900cdS猫头猫 ? -1 1397ceb900cdS猫头猫 : 1, 1398ceb900cdS猫头猫 ); 1399ceb900cdS猫头猫} 1400ceb900cdS猫头猫 1401e08d37a3S猫头猫function useSortedPlugins() { 1402e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1403e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1404e08d37a3S猫头猫 140534588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 140634588741S猫头猫 [..._plugins].sort((a, b) => 1407e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1408e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1409e08d37a3S猫头猫 0 1410e08d37a3S猫头猫 ? -1 1411e08d37a3S猫头猫 : 1, 141234588741S猫头猫 ), 1413e08d37a3S猫头猫 ); 141434588741S猫头猫 141534588741S猫头猫 useEffect(() => { 1416d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 141734588741S猫头猫 setSortedPlugins( 141834588741S猫头猫 [..._plugins].sort((a, b) => 141934588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 142034588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 142134588741S猫头猫 0 142234588741S猫头猫 ? -1 142334588741S猫头猫 : 1, 142434588741S猫头猫 ), 142534588741S猫头猫 ); 1426d4cd40d8S猫头猫 }); 142734588741S猫头猫 }, [_plugins, _pluginMetaAll]); 142834588741S猫头猫 142934588741S猫头猫 return sortedPlugins; 1430e08d37a3S猫头猫} 1431e08d37a3S猫头猫 1432c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1433c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1434c2b3a262S猫头猫 if (target) { 1435c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1436c2b3a262S猫头猫 plugins = [...plugins]; 1437c2b3a262S猫头猫 pluginStateMapper.notify(); 1438c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1439c2b3a262S猫头猫 } 1440c2b3a262S猫头猫} 1441c2b3a262S猫头猫 1442927dbe93S猫头猫const PluginManager = { 1443927dbe93S猫头猫 setup, 1444927dbe93S猫头猫 installPlugin, 1445740e3947S猫头猫 installPluginFromRawCode, 144658992c6bS猫头猫 installPluginFromUrl, 144725c1bd29S猫头猫 updatePlugin, 1448927dbe93S猫头猫 uninstallPlugin, 1449927dbe93S猫头猫 getByMedia, 1450927dbe93S猫头猫 getByHash, 1451927dbe93S猫头猫 getByName, 1452927dbe93S猫头猫 getValidPlugins, 1453efb9da24S猫头猫 getSearchablePlugins, 1454e08d37a3S猫头猫 getSortedSearchablePlugins, 145515feccc1S猫头猫 getTopListsablePlugins, 1456ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 145715feccc1S猫头猫 getSortedTopListsablePlugins, 14585276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1459e08d37a3S猫头猫 useSortedPlugins, 146008882a77S猫头猫 uninstallAllPlugins, 1461c2b3a262S猫头猫 setPluginEnabled, 14625276aef9S猫头猫}; 1463927dbe93S猫头猫 1464927dbe93S猫头猫export default PluginManager; 1465