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'; 14d4cd40d8S猫头猫import {InteractionManager, ToastAndroid} from 'react-native'; 15927dbe93S猫头猫import pathConst from '@/constants/pathConst'; 1625c1bd29S猫头猫import {compare, satisfies} from 'compare-versions'; 17927dbe93S猫头猫import DeviceInfo from 'react-native-device-info'; 18927dbe93S猫头猫import StateMapper from '@/utils/stateMapper'; 19927dbe93S猫头猫import MediaMeta from './mediaMeta'; 20927dbe93S猫头猫import {nanoid} from 'nanoid'; 21ea6d708fS猫头猫import {devLog, errorLog, trace} from '../utils/log'; 22927dbe93S猫头猫import Cache from './cache'; 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'; 46*a84a85c5S猫头猫import {getFileName} from '@/utils/fileUtils'; 47927dbe93S猫头猫 4861aca335S猫头猫axios.defaults.timeout = 2000; 49927dbe93S猫头猫 50927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 51927dbe93S猫头猫 52cfa0fc07S猫头猫export enum PluginStateCode { 53927dbe93S猫头猫 /** 版本不匹配 */ 54927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 55927dbe93S猫头猫 /** 无法解析 */ 56927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 57927dbe93S猫头猫} 58927dbe93S猫头猫 599c34d637S猫头猫const packages: Record<string, any> = { 609c34d637S猫头猫 cheerio, 619c34d637S猫头猫 'crypto-js': CryptoJs, 629c34d637S猫头猫 axios, 639c34d637S猫头猫 dayjs, 649c34d637S猫头猫 'big-integer': bigInt, 659c34d637S猫头猫 qs, 669c34d637S猫头猫 he, 673b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 689c34d637S猫头猫}; 699c34d637S猫头猫 70b43683eaS猫头猫const _require = (packageName: string) => { 71b43683eaS猫头猫 let pkg = packages[packageName]; 72b43683eaS猫头猫 pkg.default = pkg; 73b43683eaS猫头猫 return pkg; 74b43683eaS猫头猫}; 759c34d637S猫头猫 7653f8cd8eS猫头猫const _consoleBind = function ( 7753f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 7853f8cd8eS猫头猫 ...args: any 7953f8cd8eS猫头猫) { 8053f8cd8eS猫头猫 const fn = console[method]; 8153f8cd8eS猫头猫 if (fn) { 8253f8cd8eS猫头猫 fn(...args); 8353f8cd8eS猫头猫 devLog(method, ...args); 8453f8cd8eS猫头猫 } 8553f8cd8eS猫头猫}; 8653f8cd8eS猫头猫 8753f8cd8eS猫头猫const _console = { 8853f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 8953f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9053f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9153f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9253f8cd8eS猫头猫}; 9353f8cd8eS猫头猫 94d5bfeb7eS猫头猫//#region 插件类 95927dbe93S猫头猫export class Plugin { 96927dbe93S猫头猫 /** 插件名 */ 97927dbe93S猫头猫 public name: string; 98927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 99927dbe93S猫头猫 public hash: string; 100927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 101927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 102927dbe93S猫头猫 /** 插件状态信息 */ 103927dbe93S猫头猫 public stateCode?: PluginStateCode; 104927dbe93S猫头猫 /** 插件的实例 */ 105927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 106927dbe93S猫头猫 /** 插件路径 */ 107927dbe93S猫头猫 public path: string; 108927dbe93S猫头猫 /** 插件方法 */ 109927dbe93S猫头猫 public methods: PluginMethods; 110927dbe93S猫头猫 11174d0cf81S猫头猫 constructor( 11274d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 11374d0cf81S猫头猫 pluginPath: string, 11474d0cf81S猫头猫 ) { 115927dbe93S猫头猫 this.state = 'enabled'; 116927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1173b3d6357S猫头猫 const _module: any = {exports: {}}; 118927dbe93S猫头猫 try { 11974d0cf81S猫头猫 if (typeof funcCode === 'string') { 120e0caf342S猫头猫 // 插件的环境变量 121e0caf342S猫头猫 const env = { 122e0caf342S猫头猫 getUserVariables: () => { 123e0caf342S猫头猫 return ( 124e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 125e0caf342S猫头猫 ); 126e0caf342S猫头猫 }, 127e0caf342S猫头猫 }; 128e0caf342S猫头猫 1294060c00aS猫头猫 // eslint-disable-next-line no-new-func 130927dbe93S猫头猫 _instance = Function(` 131927dbe93S猫头猫 'use strict'; 132e0caf342S猫头猫 return function(require, __musicfree_require, module, exports, console, env) { 1339c34d637S猫头猫 ${funcCode} 134927dbe93S猫头猫 } 135e0caf342S猫头猫 `)()( 136e0caf342S猫头猫 _require, 137e0caf342S猫头猫 _require, 138e0caf342S猫头猫 _module, 139e0caf342S猫头猫 _module.exports, 140e0caf342S猫头猫 _console, 141e0caf342S猫头猫 env, 142e0caf342S猫头猫 ); 1433b3d6357S猫头猫 if (_module.exports.default) { 1443b3d6357S猫头猫 _instance = _module.exports 1453b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1463b3d6357S猫头猫 } else { 1479c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1483b3d6357S猫头猫 } 14974d0cf81S猫头猫 } else { 15074d0cf81S猫头猫 _instance = funcCode(); 15174d0cf81S猫头猫 } 152927dbe93S猫头猫 this.checkValid(_instance); 153927dbe93S猫头猫 } catch (e: any) { 154b43683eaS猫头猫 console.log(e); 155927dbe93S猫头猫 this.state = 'error'; 156927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 157927dbe93S猫头猫 if (e?.stateCode) { 158927dbe93S猫头猫 this.stateCode = e.stateCode; 159927dbe93S猫头猫 } 160927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 161927dbe93S猫头猫 stateCode: this.stateCode, 162927dbe93S猫头猫 message: e?.message, 163927dbe93S猫头猫 stack: e?.stack, 164927dbe93S猫头猫 }); 165927dbe93S猫头猫 _instance = e?.instance ?? { 166927dbe93S猫头猫 _path: '', 167927dbe93S猫头猫 platform: '', 168927dbe93S猫头猫 appVersion: '', 16920e6a092S猫头猫 async getMediaSource() { 170927dbe93S猫头猫 return null; 171927dbe93S猫头猫 }, 172927dbe93S猫头猫 async search() { 173927dbe93S猫头猫 return {}; 174927dbe93S猫头猫 }, 175927dbe93S猫头猫 async getAlbumInfo() { 176927dbe93S猫头猫 return null; 177927dbe93S猫头猫 }, 178927dbe93S猫头猫 }; 179927dbe93S猫头猫 } 180927dbe93S猫头猫 this.instance = _instance; 181927dbe93S猫头猫 this.path = pluginPath; 182927dbe93S猫头猫 this.name = _instance.platform; 183ab8941d9S猫头猫 if ( 184ab8941d9S猫头猫 this.instance.platform === '' || 185ab8941d9S猫头猫 this.instance.platform === undefined 186ab8941d9S猫头猫 ) { 187927dbe93S猫头猫 this.hash = ''; 188927dbe93S猫头猫 } else { 18974d0cf81S猫头猫 if (typeof funcCode === 'string') { 190927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 19174d0cf81S猫头猫 } else { 19274d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 19374d0cf81S猫头猫 } 194927dbe93S猫头猫 } 195927dbe93S猫头猫 196927dbe93S猫头猫 // 放在最后 197927dbe93S猫头猫 this.methods = new PluginMethods(this); 198927dbe93S猫头猫 } 199927dbe93S猫头猫 200927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 201927dbe93S猫头猫 /** 版本号校验 */ 202927dbe93S猫头猫 if ( 203927dbe93S猫头猫 _instance.appVersion && 204927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 205927dbe93S猫头猫 ) { 206927dbe93S猫头猫 throw { 207927dbe93S猫头猫 instance: _instance, 208927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 209927dbe93S猫头猫 }; 210927dbe93S猫头猫 } 211927dbe93S猫头猫 return true; 212927dbe93S猫头猫 } 213927dbe93S猫头猫} 214d5bfeb7eS猫头猫//#endregion 215927dbe93S猫头猫 216d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 217927dbe93S猫头猫/** 有缓存等信息 */ 218927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 219927dbe93S猫头猫 private plugin; 220927dbe93S猫头猫 constructor(plugin: Plugin) { 221927dbe93S猫头猫 this.plugin = plugin; 222927dbe93S猫头猫 } 223927dbe93S猫头猫 /** 搜索 */ 224927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 225927dbe93S猫头猫 query: string, 226927dbe93S猫头猫 page: number, 227927dbe93S猫头猫 type: T, 228927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 229927dbe93S猫头猫 if (!this.plugin.instance.search) { 230927dbe93S猫头猫 return { 231927dbe93S猫头猫 isEnd: true, 232927dbe93S猫头猫 data: [], 233927dbe93S猫头猫 }; 234927dbe93S猫头猫 } 235927dbe93S猫头猫 2364060c00aS猫头猫 const result = 2374060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 238927dbe93S猫头猫 if (Array.isArray(result.data)) { 239927dbe93S猫头猫 result.data.forEach(_ => { 240927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 241927dbe93S猫头猫 }); 242927dbe93S猫头猫 return { 243927dbe93S猫头猫 isEnd: result.isEnd ?? true, 244927dbe93S猫头猫 data: result.data, 245927dbe93S猫头猫 }; 246927dbe93S猫头猫 } 247927dbe93S猫头猫 return { 248927dbe93S猫头猫 isEnd: true, 249927dbe93S猫头猫 data: [], 250927dbe93S猫头猫 }; 251927dbe93S猫头猫 } 252927dbe93S猫头猫 253927dbe93S猫头猫 /** 获取真实源 */ 25420e6a092S猫头猫 async getMediaSource( 255927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 256abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 257927dbe93S猫头猫 retryCount = 1, 258dc160d50S猫头猫 notUpdateCache = false, 259192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 260927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 261927dbe93S猫头猫 const localPath = 2620e4173cdS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) ?? 2630e4173cdS猫头猫 getInternalData<string>( 2640e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 2650e4173cdS猫头猫 InternalDataType.LOCALPATH, 2660e4173cdS猫头猫 ); 267*a84a85c5S猫头猫 if ( 268*a84a85c5S猫头猫 localPath && 269*a84a85c5S猫头猫 (localPath.startsWith('content://') || 270*a84a85c5S猫头猫 (await FileSystem.exists(localPath))) 271*a84a85c5S猫头猫 ) { 2720e4173cdS猫头猫 trace('本地播放', localPath); 273927dbe93S猫头猫 return { 274927dbe93S猫头猫 url: localPath, 275927dbe93S猫头猫 }; 276927dbe93S猫头猫 } 277*a84a85c5S猫头猫 console.log('BFFF2'); 278*a84a85c5S猫头猫 2797993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 280f5935920S猫头猫 throw new Error('本地音乐不存在'); 281f5935920S猫头猫 } 282927dbe93S猫头猫 // 2. 缓存播放 283927dbe93S猫头猫 const mediaCache = Cache.get(musicItem); 284985f8e75S猫头猫 const pluginCacheControl = 285985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 286cfa0fc07S猫头猫 if ( 287cfa0fc07S猫头猫 mediaCache && 288abaede57S猫头猫 mediaCache?.qualities?.[quality]?.url && 28948f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 29048f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 291ef714860S猫头猫 Network.isOffline())) 292cfa0fc07S猫头猫 ) { 2935276aef9S猫头猫 trace('播放', '缓存播放'); 294abaede57S猫头猫 const qualityInfo = mediaCache.qualities[quality]; 295927dbe93S猫头猫 return { 296abaede57S猫头猫 url: qualityInfo.url, 297927dbe93S猫头猫 headers: mediaCache.headers, 2984060c00aS猫头猫 userAgent: 2994060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 300927dbe93S猫头猫 }; 301927dbe93S猫头猫 } 302927dbe93S猫头猫 // 3. 插件解析 30320e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 304abaede57S猫头猫 return {url: musicItem?.qualities?.[quality]?.url ?? musicItem.url}; 305927dbe93S猫头猫 } 306927dbe93S猫头猫 try { 307abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 308abaede57S猫头猫 musicItem, 309abaede57S猫头猫 quality, 310abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 311927dbe93S猫头猫 if (!url) { 312a28eac61S猫头猫 throw new Error('NOT RETRY'); 313927dbe93S猫头猫 } 3145276aef9S猫头猫 trace('播放', '插件播放'); 315927dbe93S猫头猫 const result = { 316927dbe93S猫头猫 url, 317927dbe93S猫头猫 headers, 318927dbe93S猫头猫 userAgent: headers?.['user-agent'], 319cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 320927dbe93S猫头猫 321dc160d50S猫头猫 if ( 322dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 323dc160d50S猫头猫 !notUpdateCache 324dc160d50S猫头猫 ) { 325abaede57S猫头猫 Cache.update(musicItem, [ 326abaede57S猫头猫 ['headers', result.headers], 327abaede57S猫头猫 ['userAgent', result.userAgent], 328abaede57S猫头猫 [`qualities.${quality}.url`, url], 329abaede57S猫头猫 ]); 330752ffc5aS猫头猫 } 331cfa0fc07S猫头猫 332927dbe93S猫头猫 return result; 333927dbe93S猫头猫 } catch (e: any) { 334a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 335927dbe93S猫头猫 await delay(150); 336abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 337927dbe93S猫头猫 } 338927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 339ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 340192ae2b0S猫头猫 return null; 341927dbe93S猫头猫 } 342927dbe93S猫头猫 } 343927dbe93S猫头猫 344927dbe93S猫头猫 /** 获取音乐详情 */ 345927dbe93S猫头猫 async getMusicInfo( 346927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 34774d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 348927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 349d704daedS猫头猫 return null; 350927dbe93S猫头猫 } 35174d0cf81S猫头猫 try { 352927dbe93S猫头猫 return ( 353927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 3547993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 355d704daedS猫头猫 ) ?? null 356927dbe93S猫头猫 ); 357ea6d708fS猫头猫 } catch (e: any) { 358ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 359d704daedS猫头猫 return null; 36074d0cf81S猫头猫 } 361927dbe93S猫头猫 } 362927dbe93S猫头猫 363927dbe93S猫头猫 /** 获取歌词 */ 364927dbe93S猫头猫 async getLyric( 365927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 366927dbe93S猫头猫 from?: IMusic.IMusicItemBase, 367927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 368927dbe93S猫头猫 // 1.额外存储的meta信息 369927dbe93S猫头猫 const meta = MediaMeta.get(musicItem); 370927dbe93S猫头猫 if (meta && meta.associatedLrc) { 371927dbe93S猫头猫 // 有关联歌词 372927dbe93S猫头猫 if ( 373927dbe93S猫头猫 isSameMediaItem(musicItem, from) || 374927dbe93S猫头猫 isSameMediaItem(meta.associatedLrc, musicItem) 375927dbe93S猫头猫 ) { 376927dbe93S猫头猫 // 形成环路,断开当前的环 377927dbe93S猫头猫 await MediaMeta.update(musicItem, { 378927dbe93S猫头猫 associatedLrc: undefined, 379927dbe93S猫头猫 }); 380927dbe93S猫头猫 // 无歌词 381927dbe93S猫头猫 return null; 382927dbe93S猫头猫 } 383927dbe93S猫头猫 // 获取关联歌词 3847a91f04fS猫头猫 const associatedMeta = MediaMeta.get(meta.associatedLrc) ?? {}; 3854060c00aS猫头猫 const result = await this.getLyric( 3867a91f04fS猫头猫 {...meta.associatedLrc, ...associatedMeta}, 3874060c00aS猫头猫 from ?? musicItem, 3884060c00aS猫头猫 ); 389927dbe93S猫头猫 if (result) { 390927dbe93S猫头猫 // 如果有关联歌词,就返回关联歌词,深度优先 391927dbe93S猫头猫 return result; 392927dbe93S猫头猫 } 393927dbe93S猫头猫 } 394927dbe93S猫头猫 const cache = Cache.get(musicItem); 395927dbe93S猫头猫 let rawLrc = meta?.rawLrc || musicItem.rawLrc || cache?.rawLrc; 396927dbe93S猫头猫 let lrcUrl = meta?.lrc || musicItem.lrc || cache?.lrc; 397927dbe93S猫头猫 // 如果存在文本 398927dbe93S猫头猫 if (rawLrc) { 399927dbe93S猫头猫 return { 400927dbe93S猫头猫 rawLrc, 401927dbe93S猫头猫 lrc: lrcUrl, 402927dbe93S猫头猫 }; 403927dbe93S猫头猫 } 404927dbe93S猫头猫 // 2.本地缓存 405927dbe93S猫头猫 const localLrc = 4060e4173cdS猫头猫 meta?.[internalSerializeKey]?.local?.localLrc || 4070e4173cdS猫头猫 cache?.[internalSerializeKey]?.local?.localLrc; 408927dbe93S猫头猫 if (localLrc && (await exists(localLrc))) { 409927dbe93S猫头猫 rawLrc = await readFile(localLrc, 'utf8'); 410927dbe93S猫头猫 return { 411927dbe93S猫头猫 rawLrc, 412927dbe93S猫头猫 lrc: lrcUrl, 413927dbe93S猫头猫 }; 414927dbe93S猫头猫 } 415927dbe93S猫头猫 // 3.优先使用url 416927dbe93S猫头猫 if (lrcUrl) { 417927dbe93S猫头猫 try { 418927dbe93S猫头猫 // 需要超时时间 axios timeout 但是没生效 41961aca335S猫头猫 rawLrc = (await axios.get(lrcUrl, {timeout: 2000})).data; 420927dbe93S猫头猫 return { 421927dbe93S猫头猫 rawLrc, 422927dbe93S猫头猫 lrc: lrcUrl, 423927dbe93S猫头猫 }; 424927dbe93S猫头猫 } catch { 425927dbe93S猫头猫 lrcUrl = undefined; 426927dbe93S猫头猫 } 427927dbe93S猫头猫 } 428927dbe93S猫头猫 // 4. 如果地址失效 429927dbe93S猫头猫 if (!lrcUrl) { 430927dbe93S猫头猫 // 插件获得url 431927dbe93S猫头猫 try { 4327a91f04fS猫头猫 let lrcSource; 4337a91f04fS猫头猫 if (from) { 4347a91f04fS猫头猫 lrcSource = await PluginManager.getByMedia( 4357a91f04fS猫头猫 musicItem, 4367a91f04fS猫头猫 )?.instance?.getLyric?.( 437927dbe93S猫头猫 resetMediaItem(musicItem, undefined, true), 438927dbe93S猫头猫 ); 4397a91f04fS猫头猫 } else { 4407a91f04fS猫头猫 lrcSource = await this.plugin.instance?.getLyric?.( 4417a91f04fS猫头猫 resetMediaItem(musicItem, undefined, true), 4427a91f04fS猫头猫 ); 4437a91f04fS猫头猫 } 4447a91f04fS猫头猫 445927dbe93S猫头猫 rawLrc = lrcSource?.rawLrc; 446927dbe93S猫头猫 lrcUrl = lrcSource?.lrc; 447927dbe93S猫头猫 } catch (e: any) { 448927dbe93S猫头猫 trace('插件获取歌词失败', e?.message, 'error'); 449ea6d708fS猫头猫 devLog('error', '插件获取歌词失败', e, e?.message); 450927dbe93S猫头猫 } 451927dbe93S猫头猫 } 452927dbe93S猫头猫 // 5. 最后一次请求 453927dbe93S猫头猫 if (rawLrc || lrcUrl) { 454927dbe93S猫头猫 const filename = `${pathConst.lrcCachePath}${nanoid()}.lrc`; 455927dbe93S猫头猫 if (lrcUrl) { 456927dbe93S猫头猫 try { 45761aca335S猫头猫 rawLrc = (await axios.get(lrcUrl, {timeout: 2000})).data; 458927dbe93S猫头猫 } catch {} 459927dbe93S猫头猫 } 460927dbe93S猫头猫 if (rawLrc) { 461927dbe93S猫头猫 await writeFile(filename, rawLrc, 'utf8'); 462927dbe93S猫头猫 // 写入缓存 463927dbe93S猫头猫 Cache.update(musicItem, [ 4640e4173cdS猫头猫 [`${internalSerializeKey}.local.localLrc`, filename], 465927dbe93S猫头猫 ]); 466927dbe93S猫头猫 // 如果有meta 467927dbe93S猫头猫 if (meta) { 468927dbe93S猫头猫 MediaMeta.update(musicItem, [ 4690e4173cdS猫头猫 [`${internalSerializeKey}.local.localLrc`, filename], 470927dbe93S猫头猫 ]); 471927dbe93S猫头猫 } 472927dbe93S猫头猫 return { 473927dbe93S猫头猫 rawLrc, 474927dbe93S猫头猫 lrc: lrcUrl, 475927dbe93S猫头猫 }; 476927dbe93S猫头猫 } 477927dbe93S猫头猫 } 4783a6f67b1S猫头猫 // 6. 如果是本地文件 4793a6f67b1S猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(musicItem); 4803a6f67b1S猫头猫 if (musicItem.platform !== localPluginPlatform && isDownloaded) { 4813a6f67b1S猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 4823a6f67b1S猫头猫 if (res) { 4833a6f67b1S猫头猫 return res; 4843a6f67b1S猫头猫 } 4853a6f67b1S猫头猫 } 486ea6d708fS猫头猫 devLog('warn', '无歌词'); 487927dbe93S猫头猫 488927dbe93S猫头猫 return null; 489927dbe93S猫头猫 } 490927dbe93S猫头猫 491927dbe93S猫头猫 /** 获取歌词文本 */ 492927dbe93S猫头猫 async getLyricText( 493927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 494927dbe93S猫头猫 ): Promise<string | undefined> { 495927dbe93S猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 496927dbe93S猫头猫 } 497927dbe93S猫头猫 498927dbe93S猫头猫 /** 获取专辑信息 */ 499927dbe93S猫头猫 async getAlbumInfo( 500927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 501f9afcc0dS猫头猫 page: number = 1, 502f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 503927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 504f9afcc0dS猫头猫 return { 505f9afcc0dS猫头猫 albumItem, 506f9afcc0dS猫头猫 musicList: albumItem?.musicList ?? [], 507f9afcc0dS猫头猫 isEnd: true, 508f9afcc0dS猫头猫 }; 509927dbe93S猫头猫 } 510927dbe93S猫头猫 try { 511927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 512927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 513f9afcc0dS猫头猫 page, 514927dbe93S猫头猫 ); 5155276aef9S猫头猫 if (!result) { 5165276aef9S猫头猫 throw new Error(); 5175276aef9S猫头猫 } 518927dbe93S猫头猫 result?.musicList?.forEach(_ => { 519927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 52096744680S猫头猫 _.album = albumItem.title; 521927dbe93S猫头猫 }); 5225276aef9S猫头猫 523f9afcc0dS猫头猫 if (page <= 1) { 524f9afcc0dS猫头猫 // 合并信息 525f9afcc0dS猫头猫 return { 526f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 527f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 528f9afcc0dS猫头猫 musicList: result.musicList, 529f9afcc0dS猫头猫 }; 530f9afcc0dS猫头猫 } else { 531f9afcc0dS猫头猫 return { 532f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 533f9afcc0dS猫头猫 musicList: result.musicList, 534f9afcc0dS猫头猫 }; 535f9afcc0dS猫头猫 } 5364394410dS猫头猫 } catch (e: any) { 5374394410dS猫头猫 trace('获取专辑信息失败', e?.message); 538ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 539ea6d708fS猫头猫 540f9afcc0dS猫头猫 return null; 541927dbe93S猫头猫 } 542927dbe93S猫头猫 } 543927dbe93S猫头猫 5445830c002S猫头猫 /** 获取歌单信息 */ 5455830c002S猫头猫 async getMusicSheetInfo( 5465830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 5475830c002S猫头猫 page: number = 1, 5485830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 5495281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 5505830c002S猫头猫 return { 5515830c002S猫头猫 sheetItem, 5525830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 5535830c002S猫头猫 isEnd: true, 5545830c002S猫头猫 }; 5555830c002S猫头猫 } 5565830c002S猫头猫 try { 5575830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 5585830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 5595830c002S猫头猫 page, 5605830c002S猫头猫 ); 5615830c002S猫头猫 if (!result) { 5625830c002S猫头猫 throw new Error(); 5635830c002S猫头猫 } 5645830c002S猫头猫 result?.musicList?.forEach(_ => { 5655830c002S猫头猫 resetMediaItem(_, this.plugin.name); 5665830c002S猫头猫 }); 5675830c002S猫头猫 5685830c002S猫头猫 if (page <= 1) { 5695830c002S猫头猫 // 合并信息 5705830c002S猫头猫 return { 5715830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 5725830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 5735830c002S猫头猫 musicList: result.musicList, 5745830c002S猫头猫 }; 5755830c002S猫头猫 } else { 5765830c002S猫头猫 return { 5775830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 5785830c002S猫头猫 musicList: result.musicList, 5795830c002S猫头猫 }; 5805830c002S猫头猫 } 5815830c002S猫头猫 } catch (e: any) { 5825830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 5835830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 5845830c002S猫头猫 5855830c002S猫头猫 return null; 5865830c002S猫头猫 } 5875830c002S猫头猫 } 5885830c002S猫头猫 589927dbe93S猫头猫 /** 查询作者信息 */ 590efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 591927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 592927dbe93S猫头猫 page: number, 593927dbe93S猫头猫 type: T, 594927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 595efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 596927dbe93S猫头猫 return { 597927dbe93S猫头猫 isEnd: true, 598927dbe93S猫头猫 data: [], 599927dbe93S猫头猫 }; 600927dbe93S猫头猫 } 601927dbe93S猫头猫 try { 602efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 603927dbe93S猫头猫 artistItem, 604927dbe93S猫头猫 page, 605927dbe93S猫头猫 type, 606927dbe93S猫头猫 ); 607927dbe93S猫头猫 if (!result.data) { 608927dbe93S猫头猫 return { 609927dbe93S猫头猫 isEnd: true, 610927dbe93S猫头猫 data: [], 611927dbe93S猫头猫 }; 612927dbe93S猫头猫 } 613927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 614927dbe93S猫头猫 return { 615927dbe93S猫头猫 isEnd: result.isEnd ?? true, 616927dbe93S猫头猫 data: result.data, 617927dbe93S猫头猫 }; 6184394410dS猫头猫 } catch (e: any) { 6194394410dS猫头猫 trace('查询作者信息失败', e?.message); 620ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 621ea6d708fS猫头猫 622927dbe93S猫头猫 throw e; 623927dbe93S猫头猫 } 624927dbe93S猫头猫 } 62508380090S猫头猫 62608380090S猫头猫 /** 导入歌单 */ 62708380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 62808380090S猫头猫 try { 62908380090S猫头猫 const result = 63008380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 63108380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 63208380090S猫头猫 return result; 633ea6d708fS猫头猫 } catch (e: any) { 6340e4173cdS猫头猫 console.log(e); 635ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 636ea6d708fS猫头猫 63708380090S猫头猫 return []; 63808380090S猫头猫 } 63908380090S猫头猫 } 6404d9d3c4cS猫头猫 /** 导入单曲 */ 6414d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 6424d9d3c4cS猫头猫 try { 6434d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 6444d9d3c4cS猫头猫 urlLike, 6454d9d3c4cS猫头猫 ); 6464d9d3c4cS猫头猫 if (!result) { 6474d9d3c4cS猫头猫 throw new Error(); 6484d9d3c4cS猫头猫 } 6494d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 6504d9d3c4cS猫头猫 return result; 651ea6d708fS猫头猫 } catch (e: any) { 652ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 653ea6d708fS猫头猫 6544d9d3c4cS猫头猫 return null; 6554d9d3c4cS猫头猫 } 6564d9d3c4cS猫头猫 } 657d52aa40eS猫头猫 /** 获取榜单 */ 65892b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 659d52aa40eS猫头猫 try { 660d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 661d52aa40eS猫头猫 if (!result) { 662d52aa40eS猫头猫 throw new Error(); 663d52aa40eS猫头猫 } 664d52aa40eS猫头猫 return result; 665d52aa40eS猫头猫 } catch (e: any) { 666d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 667d52aa40eS猫头猫 return []; 668d52aa40eS猫头猫 } 669d52aa40eS猫头猫 } 670d52aa40eS猫头猫 /** 获取榜单详情 */ 671d52aa40eS猫头猫 async getTopListDetail( 67292b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 67392b6c95aS猫头猫 ): Promise<ICommon.WithMusicList<IMusic.IMusicSheetItemBase>> { 674d52aa40eS猫头猫 try { 675d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 676d52aa40eS猫头猫 topListItem, 677d52aa40eS猫头猫 ); 678d52aa40eS猫头猫 if (!result) { 679d52aa40eS猫头猫 throw new Error(); 680d52aa40eS猫头猫 } 681d384662fS猫头猫 if (result.musicList) { 682d384662fS猫头猫 result.musicList.forEach(_ => 683d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 684d384662fS猫头猫 ); 685d384662fS猫头猫 } 686d52aa40eS猫头猫 return result; 687d52aa40eS猫头猫 } catch (e: any) { 688d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 689d52aa40eS猫头猫 return { 690d52aa40eS猫头猫 ...topListItem, 691d52aa40eS猫头猫 musicList: [], 692d52aa40eS猫头猫 }; 693d52aa40eS猫头猫 } 694d52aa40eS猫头猫 } 695ceb900cdS猫头猫 6965830c002S猫头猫 /** 获取推荐歌单的tag */ 697ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 698ceb900cdS猫头猫 try { 699ceb900cdS猫头猫 const result = 700ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 701ceb900cdS猫头猫 if (!result) { 702ceb900cdS猫头猫 throw new Error(); 703ceb900cdS猫头猫 } 704ceb900cdS猫头猫 return result; 705ceb900cdS猫头猫 } catch (e: any) { 706ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 707ceb900cdS猫头猫 return { 708ceb900cdS猫头猫 data: [], 709ceb900cdS猫头猫 }; 710ceb900cdS猫头猫 } 711ceb900cdS猫头猫 } 7125830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 713ceb900cdS猫头猫 async getRecommendSheetsByTag( 714ceb900cdS猫头猫 tagItem: ICommon.IUnique, 715ceb900cdS猫头猫 page?: number, 716ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 717ceb900cdS猫头猫 try { 718ceb900cdS猫头猫 const result = 719ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 720ceb900cdS猫头猫 tagItem, 721ceb900cdS猫头猫 page ?? 1, 722ceb900cdS猫头猫 ); 723ceb900cdS猫头猫 if (!result) { 724ceb900cdS猫头猫 throw new Error(); 725ceb900cdS猫头猫 } 726ceb900cdS猫头猫 if (result.isEnd !== false) { 727ceb900cdS猫头猫 result.isEnd = true; 728ceb900cdS猫头猫 } 729ceb900cdS猫头猫 if (!result.data) { 730ceb900cdS猫头猫 result.data = []; 731ceb900cdS猫头猫 } 732ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 733ceb900cdS猫头猫 734ceb900cdS猫头猫 return result; 735ceb900cdS猫头猫 } catch (e: any) { 736ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 737ceb900cdS猫头猫 return { 738ceb900cdS猫头猫 isEnd: true, 739ceb900cdS猫头猫 data: [], 740ceb900cdS猫头猫 }; 741ceb900cdS猫头猫 } 742ceb900cdS猫头猫 } 743927dbe93S猫头猫} 744d5bfeb7eS猫头猫//#endregion 7451a5528a0S猫头猫 746927dbe93S猫头猫let plugins: Array<Plugin> = []; 747927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 74874d0cf81S猫头猫 749d5bfeb7eS猫头猫//#region 本地音乐插件 75074d0cf81S猫头猫/** 本地插件 */ 75174d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 7520e4173cdS猫头猫 return { 753d5bfeb7eS猫头猫 platform: localPluginPlatform, 75474d0cf81S猫头猫 _path: '', 75574d0cf81S猫头猫 async getMusicInfo(musicBase) { 75674d0cf81S猫头猫 const localPath = getInternalData<string>( 75774d0cf81S猫头猫 musicBase, 75874d0cf81S猫头猫 InternalDataType.LOCALPATH, 7590e4173cdS猫头猫 ); 76074d0cf81S猫头猫 if (localPath) { 76174d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 76274d0cf81S猫头猫 return { 76374d0cf81S猫头猫 artwork: coverImg, 76474d0cf81S猫头猫 }; 76574d0cf81S猫头猫 } 76674d0cf81S猫头猫 return null; 76774d0cf81S猫头猫 }, 7687993f90eS猫头猫 async getLyric(musicBase) { 7697993f90eS猫头猫 const localPath = getInternalData<string>( 7707993f90eS猫头猫 musicBase, 7717993f90eS猫头猫 InternalDataType.LOCALPATH, 7727993f90eS猫头猫 ); 7733a6f67b1S猫头猫 let rawLrc: string | null = null; 7747993f90eS猫头猫 if (localPath) { 7753a6f67b1S猫头猫 // 读取内嵌歌词 7763a6f67b1S猫头猫 try { 7773a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 7783a6f67b1S猫头猫 } catch (e) { 779*a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 7807993f90eS猫头猫 } 7813a6f67b1S猫头猫 if (!rawLrc) { 7823a6f67b1S猫头猫 // 读取配置歌词 7833a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 7843a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 7853a6f67b1S猫头猫 7863a6f67b1S猫头猫 try { 7873a6f67b1S猫头猫 if (await exists(lrcPath)) { 7883a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 7893a6f67b1S猫头猫 } 7903a6f67b1S猫头猫 } catch {} 7913a6f67b1S猫头猫 } 7923a6f67b1S猫头猫 } 7933a6f67b1S猫头猫 7943a6f67b1S猫头猫 return rawLrc 7953a6f67b1S猫头猫 ? { 7963a6f67b1S猫头猫 rawLrc, 7973a6f67b1S猫头猫 } 7983a6f67b1S猫头猫 : null; 7997993f90eS猫头猫 }, 800*a84a85c5S猫头猫 async importMusicItem(urlLike) { 801*a84a85c5S猫头猫 let meta: any = {}; 802*a84a85c5S猫头猫 try { 803*a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 804*a84a85c5S猫头猫 } catch {} 805*a84a85c5S猫头猫 const id = await FileSystem.hash(urlLike, 'MD5'); 806*a84a85c5S猫头猫 return { 807*a84a85c5S猫头猫 id: id, 808*a84a85c5S猫头猫 platform: '本地', 809*a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 810*a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 811*a84a85c5S猫头猫 duration: parseInt(meta?.duration ?? '0') / 1000, 812*a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 813*a84a85c5S猫头猫 artwork: '', 814*a84a85c5S猫头猫 [internalSerializeKey]: { 815*a84a85c5S猫头猫 localPath: urlLike, 816*a84a85c5S猫头猫 }, 817*a84a85c5S猫头猫 }; 818*a84a85c5S猫头猫 }, 81974d0cf81S猫头猫 }; 82074d0cf81S猫头猫}, ''); 8217993f90eS猫头猫localFilePlugin.hash = localPluginHash; 822927dbe93S猫头猫 823d5bfeb7eS猫头猫//#endregion 824d5bfeb7eS猫头猫 825927dbe93S猫头猫async function setup() { 826927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 827927dbe93S猫头猫 try { 828927dbe93S猫头猫 // 加载插件 829927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 830927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 831927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 8321e263108S猫头猫 trace('初始化插件', _pluginUrl); 8331e263108S猫头猫 if ( 8341e263108S猫头猫 _pluginUrl.isFile() && 8351e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 8361e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 8371e263108S猫头猫 ) { 838927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 839927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 8404060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 8414060c00aS猫头猫 p => p.hash === plugin.hash, 8424060c00aS猫头猫 ); 843927dbe93S猫头猫 if (_pluginIndex !== -1) { 844927dbe93S猫头猫 // 重复插件,直接忽略 8450c266394S猫头猫 continue; 846927dbe93S猫头猫 } 847927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 848927dbe93S猫头猫 } 849927dbe93S猫头猫 } 850927dbe93S猫头猫 851927dbe93S猫头猫 plugins = _plugins; 852927dbe93S猫头猫 pluginStateMapper.notify(); 853e08d37a3S猫头猫 /** 初始化meta信息 */ 854e08d37a3S猫头猫 PluginMeta.setupMeta(plugins.map(_ => _.name)); 855927dbe93S猫头猫 } catch (e: any) { 8564060c00aS猫头猫 ToastAndroid.show( 8574060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 8584060c00aS猫头猫 ToastAndroid.LONG, 8594060c00aS猫头猫 ); 8601a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 861927dbe93S猫头猫 throw e; 862927dbe93S猫头猫 } 863927dbe93S猫头猫} 864927dbe93S猫头猫 865927dbe93S猫头猫// 安装插件 866927dbe93S猫头猫async function installPlugin(pluginPath: string) { 86722c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 868927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 869927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 870927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 871927dbe93S猫头猫 if (_pluginIndex !== -1) { 8724d9d3c4cS猫头猫 throw new Error('插件已安装'); 873927dbe93S猫头猫 } 874927dbe93S猫头猫 if (plugin.hash !== '') { 875927dbe93S猫头猫 const fn = nanoid(); 876927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 877927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 878927dbe93S猫头猫 plugin.path = _pluginPath; 879927dbe93S猫头猫 plugins = plugins.concat(plugin); 880927dbe93S猫头猫 pluginStateMapper.notify(); 881*a84a85c5S猫头猫 return plugin; 882927dbe93S猫头猫 } 8834d9d3c4cS猫头猫 throw new Error('插件无法解析'); 88422c09412S猫头猫 // } 88522c09412S猫头猫 // throw new Error('插件不存在'); 886927dbe93S猫头猫} 887927dbe93S猫头猫 88858992c6bS猫头猫async function installPluginFromUrl(url: string) { 88958992c6bS猫头猫 try { 89058992c6bS猫头猫 const funcCode = (await axios.get(url)).data; 89158992c6bS猫头猫 if (funcCode) { 89258992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 89358992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 89458992c6bS猫头猫 if (_pluginIndex !== -1) { 8958b7ddca8S猫头猫 // 静默忽略 8968b7ddca8S猫头猫 return; 89758992c6bS猫头猫 } 89825c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 89925c1bd29S猫头猫 if (oldVersionPlugin) { 90025c1bd29S猫头猫 if ( 90125c1bd29S猫头猫 compare( 90225c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 90325c1bd29S猫头猫 plugin.instance.version ?? '', 90425c1bd29S猫头猫 '>', 90525c1bd29S猫头猫 ) 90625c1bd29S猫头猫 ) { 90725c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 90825c1bd29S猫头猫 } 90925c1bd29S猫头猫 } 91025c1bd29S猫头猫 91158992c6bS猫头猫 if (plugin.hash !== '') { 91258992c6bS猫头猫 const fn = nanoid(); 91358992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 91458992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 91558992c6bS猫头猫 plugin.path = _pluginPath; 91658992c6bS猫头猫 plugins = plugins.concat(plugin); 91725c1bd29S猫头猫 if (oldVersionPlugin) { 91825c1bd29S猫头猫 plugins = plugins.filter( 91925c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 92025c1bd29S猫头猫 ); 92125c1bd29S猫头猫 try { 92225c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 92325c1bd29S猫头猫 } catch {} 92425c1bd29S猫头猫 } 92558992c6bS猫头猫 pluginStateMapper.notify(); 92658992c6bS猫头猫 return; 92758992c6bS猫头猫 } 92874acbfc0S猫头猫 throw new Error('插件无法解析!'); 92958992c6bS猫头猫 } 93025c1bd29S猫头猫 } catch (e: any) { 931ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 93258992c6bS猫头猫 errorLog('URL安装插件失败', e); 93325c1bd29S猫头猫 throw new Error(e?.message ?? ''); 93458992c6bS猫头猫 } 93558992c6bS猫头猫} 93658992c6bS猫头猫 937927dbe93S猫头猫/** 卸载插件 */ 938927dbe93S猫头猫async function uninstallPlugin(hash: string) { 939927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 940927dbe93S猫头猫 if (targetIndex !== -1) { 941927dbe93S猫头猫 try { 94224e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 943927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 944927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 945927dbe93S猫头猫 pluginStateMapper.notify(); 94624e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 94724e5e74aS猫头猫 await MediaMeta.removePlugin(pluginName); 94824e5e74aS猫头猫 } 949927dbe93S猫头猫 } catch {} 950927dbe93S猫头猫 } 951927dbe93S猫头猫} 952927dbe93S猫头猫 95308882a77S猫头猫async function uninstallAllPlugins() { 95408882a77S猫头猫 await Promise.all( 95508882a77S猫头猫 plugins.map(async plugin => { 95608882a77S猫头猫 try { 95708882a77S猫头猫 const pluginName = plugin.name; 95808882a77S猫头猫 await unlink(plugin.path); 95908882a77S猫头猫 await MediaMeta.removePlugin(pluginName); 96008882a77S猫头猫 } catch (e) {} 96108882a77S猫头猫 }), 96208882a77S猫头猫 ); 96308882a77S猫头猫 plugins = []; 96408882a77S猫头猫 pluginStateMapper.notify(); 965e08d37a3S猫头猫 966e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 967e08d37a3S猫头猫 readDir(pathConst.pluginPath) 968e08d37a3S猫头猫 .then(fns => { 969e08d37a3S猫头猫 fns.forEach(fn => { 970e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 971e08d37a3S猫头猫 }); 972e08d37a3S猫头猫 }) 973e08d37a3S猫头猫 .catch(emptyFunction); 97408882a77S猫头猫} 97508882a77S猫头猫 97625c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 97725c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 97825c1bd29S猫头猫 if (!updateUrl) { 97925c1bd29S猫头猫 throw new Error('没有更新源'); 98025c1bd29S猫头猫 } 98125c1bd29S猫头猫 try { 98225c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 98325c1bd29S猫头猫 } catch (e: any) { 98425c1bd29S猫头猫 if (e.message === '插件已安装') { 98525c1bd29S猫头猫 throw new Error('当前已是最新版本'); 98625c1bd29S猫头猫 } else { 98725c1bd29S猫头猫 throw e; 98825c1bd29S猫头猫 } 98925c1bd29S猫头猫 } 99025c1bd29S猫头猫} 99125c1bd29S猫头猫 992927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 9932c595535S猫头猫 return getByName(mediaItem?.platform); 994927dbe93S猫头猫} 995927dbe93S猫头猫 996927dbe93S猫头猫function getByHash(hash: string) { 9977993f90eS猫头猫 return hash === localPluginHash 9987993f90eS猫头猫 ? localFilePlugin 9997993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1000927dbe93S猫头猫} 1001927dbe93S猫头猫 1002927dbe93S猫头猫function getByName(name: string) { 10037993f90eS猫头猫 return name === localPluginPlatform 10040e4173cdS猫头猫 ? localFilePlugin 10050e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1006927dbe93S猫头猫} 1007927dbe93S猫头猫 1008927dbe93S猫头猫function getValidPlugins() { 1009927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1010927dbe93S猫头猫} 1011927dbe93S猫头猫 10122b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 10132b80a429S猫头猫 return plugins.filter( 10142b80a429S猫头猫 _ => 10152b80a429S猫头猫 _.state === 'enabled' && 10162b80a429S猫头猫 _.instance.search && 101739ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 101839ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 10192b80a429S猫头猫 : true), 10202b80a429S猫头猫 ); 1021efb9da24S猫头猫} 1022efb9da24S猫头猫 10232b80a429S猫头猫function getSortedSearchablePlugins( 10242b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 10252b80a429S猫头猫) { 10262b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1027e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1028e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1029e08d37a3S猫头猫 0 1030e08d37a3S猫头猫 ? -1 1031e08d37a3S猫头猫 : 1, 1032e08d37a3S猫头猫 ); 1033e08d37a3S猫头猫} 1034e08d37a3S猫头猫 103515feccc1S猫头猫function getTopListsablePlugins() { 103615feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 103715feccc1S猫头猫} 103815feccc1S猫头猫 103915feccc1S猫头猫function getSortedTopListsablePlugins() { 104015feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 104115feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 104215feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 104315feccc1S猫头猫 0 104415feccc1S猫头猫 ? -1 104515feccc1S猫头猫 : 1, 104615feccc1S猫头猫 ); 104715feccc1S猫头猫} 104815feccc1S猫头猫 1049ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1050ceb900cdS猫头猫 return plugins.filter( 1051ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1052ceb900cdS猫头猫 ); 1053ceb900cdS猫头猫} 1054ceb900cdS猫头猫 1055ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1056ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1057ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1058ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1059ceb900cdS猫头猫 0 1060ceb900cdS猫头猫 ? -1 1061ceb900cdS猫头猫 : 1, 1062ceb900cdS猫头猫 ); 1063ceb900cdS猫头猫} 1064ceb900cdS猫头猫 1065e08d37a3S猫头猫function useSortedPlugins() { 1066e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1067e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1068e08d37a3S猫头猫 106934588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 107034588741S猫头猫 [..._plugins].sort((a, b) => 1071e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1072e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1073e08d37a3S猫头猫 0 1074e08d37a3S猫头猫 ? -1 1075e08d37a3S猫头猫 : 1, 107634588741S猫头猫 ), 1077e08d37a3S猫头猫 ); 107834588741S猫头猫 107934588741S猫头猫 useEffect(() => { 1080d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 108134588741S猫头猫 setSortedPlugins( 108234588741S猫头猫 [..._plugins].sort((a, b) => 108334588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 108434588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 108534588741S猫头猫 0 108634588741S猫头猫 ? -1 108734588741S猫头猫 : 1, 108834588741S猫头猫 ), 108934588741S猫头猫 ); 1090d4cd40d8S猫头猫 }); 109134588741S猫头猫 }, [_plugins, _pluginMetaAll]); 109234588741S猫头猫 109334588741S猫头猫 return sortedPlugins; 1094e08d37a3S猫头猫} 1095e08d37a3S猫头猫 1096927dbe93S猫头猫const PluginManager = { 1097927dbe93S猫头猫 setup, 1098927dbe93S猫头猫 installPlugin, 109958992c6bS猫头猫 installPluginFromUrl, 110025c1bd29S猫头猫 updatePlugin, 1101927dbe93S猫头猫 uninstallPlugin, 1102927dbe93S猫头猫 getByMedia, 1103927dbe93S猫头猫 getByHash, 1104927dbe93S猫头猫 getByName, 1105927dbe93S猫头猫 getValidPlugins, 1106efb9da24S猫头猫 getSearchablePlugins, 1107e08d37a3S猫头猫 getSortedSearchablePlugins, 110815feccc1S猫头猫 getTopListsablePlugins, 1109ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 111015feccc1S猫头猫 getSortedTopListsablePlugins, 11115276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1112e08d37a3S猫头猫 useSortedPlugins, 111308882a77S猫头猫 uninstallAllPlugins, 11145276aef9S猫头猫}; 1115927dbe93S猫头猫 1116927dbe93S猫头猫export default PluginManager; 1117