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'; 14*28ccceb7S猫头猫import * as webdav from 'webdav'; 15d4cd40d8S猫头猫import {InteractionManager, ToastAndroid} from 'react-native'; 16927dbe93S猫头猫import pathConst from '@/constants/pathConst'; 1725c1bd29S猫头猫import {compare, satisfies} from 'compare-versions'; 18927dbe93S猫头猫import DeviceInfo from 'react-native-device-info'; 19927dbe93S猫头猫import StateMapper from '@/utils/stateMapper'; 20927dbe93S猫头猫import MediaMeta from './mediaMeta'; 21927dbe93S猫头猫import {nanoid} from 'nanoid'; 22ea6d708fS猫头猫import {devLog, errorLog, trace} from '../utils/log'; 23927dbe93S猫头猫import Cache from './cache'; 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'; 430e4173cdS猫头猫import {FileSystem} from 'react-native-file-access'; 4474d0cf81S猫头猫import Mp3Util from '@/native/mp3Util'; 45e08d37a3S猫头猫import {PluginMeta} from './pluginMeta'; 4634588741S猫头猫import {useEffect, useState} from 'react'; 47a84a85c5S猫头猫import {getFileName} from '@/utils/fileUtils'; 48927dbe93S猫头猫 4961aca335S猫头猫axios.defaults.timeout = 2000; 50927dbe93S猫头猫 51927dbe93S猫头猫const sha256 = CryptoJs.SHA256; 52927dbe93S猫头猫 53cfa0fc07S猫头猫export enum PluginStateCode { 54927dbe93S猫头猫 /** 版本不匹配 */ 55927dbe93S猫头猫 VersionNotMatch = 'VERSION NOT MATCH', 56927dbe93S猫头猫 /** 无法解析 */ 57927dbe93S猫头猫 CannotParse = 'CANNOT PARSE', 58927dbe93S猫头猫} 59927dbe93S猫头猫 609c34d637S猫头猫const packages: Record<string, any> = { 619c34d637S猫头猫 cheerio, 629c34d637S猫头猫 'crypto-js': CryptoJs, 639c34d637S猫头猫 axios, 649c34d637S猫头猫 dayjs, 659c34d637S猫头猫 'big-integer': bigInt, 669c34d637S猫头猫 qs, 679c34d637S猫头猫 he, 683b3d6357S猫头猫 '@react-native-cookies/cookies': CookieManager, 69*28ccceb7S猫头猫 webdav, 709c34d637S猫头猫}; 719c34d637S猫头猫 72b43683eaS猫头猫const _require = (packageName: string) => { 73b43683eaS猫头猫 let pkg = packages[packageName]; 74b43683eaS猫头猫 pkg.default = pkg; 75b43683eaS猫头猫 return pkg; 76b43683eaS猫头猫}; 779c34d637S猫头猫 7853f8cd8eS猫头猫const _consoleBind = function ( 7953f8cd8eS猫头猫 method: 'log' | 'error' | 'info' | 'warn', 8053f8cd8eS猫头猫 ...args: any 8153f8cd8eS猫头猫) { 8253f8cd8eS猫头猫 const fn = console[method]; 8353f8cd8eS猫头猫 if (fn) { 8453f8cd8eS猫头猫 fn(...args); 8553f8cd8eS猫头猫 devLog(method, ...args); 8653f8cd8eS猫头猫 } 8753f8cd8eS猫头猫}; 8853f8cd8eS猫头猫 8953f8cd8eS猫头猫const _console = { 9053f8cd8eS猫头猫 log: _consoleBind.bind(null, 'log'), 9153f8cd8eS猫头猫 warn: _consoleBind.bind(null, 'warn'), 9253f8cd8eS猫头猫 info: _consoleBind.bind(null, 'info'), 9353f8cd8eS猫头猫 error: _consoleBind.bind(null, 'error'), 9453f8cd8eS猫头猫}; 9553f8cd8eS猫头猫 96d5bfeb7eS猫头猫//#region 插件类 97927dbe93S猫头猫export class Plugin { 98927dbe93S猫头猫 /** 插件名 */ 99927dbe93S猫头猫 public name: string; 100927dbe93S猫头猫 /** 插件的hash,作为唯一id */ 101927dbe93S猫头猫 public hash: string; 102927dbe93S猫头猫 /** 插件状态:激活、关闭、错误 */ 103927dbe93S猫头猫 public state: 'enabled' | 'disabled' | 'error'; 104927dbe93S猫头猫 /** 插件状态信息 */ 105927dbe93S猫头猫 public stateCode?: PluginStateCode; 106927dbe93S猫头猫 /** 插件的实例 */ 107927dbe93S猫头猫 public instance: IPlugin.IPluginInstance; 108927dbe93S猫头猫 /** 插件路径 */ 109927dbe93S猫头猫 public path: string; 110927dbe93S猫头猫 /** 插件方法 */ 111927dbe93S猫头猫 public methods: PluginMethods; 112927dbe93S猫头猫 11374d0cf81S猫头猫 constructor( 11474d0cf81S猫头猫 funcCode: string | (() => IPlugin.IPluginInstance), 11574d0cf81S猫头猫 pluginPath: string, 11674d0cf81S猫头猫 ) { 117927dbe93S猫头猫 this.state = 'enabled'; 118927dbe93S猫头猫 let _instance: IPlugin.IPluginInstance; 1193b3d6357S猫头猫 const _module: any = {exports: {}}; 120927dbe93S猫头猫 try { 12174d0cf81S猫头猫 if (typeof funcCode === 'string') { 122e0caf342S猫头猫 // 插件的环境变量 123e0caf342S猫头猫 const env = { 124e0caf342S猫头猫 getUserVariables: () => { 125e0caf342S猫头猫 return ( 126e0caf342S猫头猫 PluginMeta.getPluginMeta(this)?.userVariables ?? {} 127e0caf342S猫头猫 ); 128e0caf342S猫头猫 }, 129e3fa9b3cS猫头猫 os: 'android', 130e0caf342S猫头猫 }; 131e0caf342S猫头猫 1324060c00aS猫头猫 // eslint-disable-next-line no-new-func 133927dbe93S猫头猫 _instance = Function(` 134927dbe93S猫头猫 'use strict'; 135e0caf342S猫头猫 return function(require, __musicfree_require, module, exports, console, env) { 1369c34d637S猫头猫 ${funcCode} 137927dbe93S猫头猫 } 138e0caf342S猫头猫 `)()( 139e0caf342S猫头猫 _require, 140e0caf342S猫头猫 _require, 141e0caf342S猫头猫 _module, 142e0caf342S猫头猫 _module.exports, 143e0caf342S猫头猫 _console, 144e0caf342S猫头猫 env, 145e0caf342S猫头猫 ); 1463b3d6357S猫头猫 if (_module.exports.default) { 1473b3d6357S猫头猫 _instance = _module.exports 1483b3d6357S猫头猫 .default as IPlugin.IPluginInstance; 1493b3d6357S猫头猫 } else { 1509c34d637S猫头猫 _instance = _module.exports as IPlugin.IPluginInstance; 1513b3d6357S猫头猫 } 15274d0cf81S猫头猫 } else { 15374d0cf81S猫头猫 _instance = funcCode(); 15474d0cf81S猫头猫 } 155c2b3a262S猫头猫 // 插件初始化后的一些操作 15695297592S猫头猫 if (Array.isArray(_instance.userVariables)) { 15795297592S猫头猫 _instance.userVariables = _instance.userVariables.filter( 15895297592S猫头猫 it => it?.key, 15995297592S猫头猫 ); 16095297592S猫头猫 } 161927dbe93S猫头猫 this.checkValid(_instance); 162927dbe93S猫头猫 } catch (e: any) { 163b43683eaS猫头猫 console.log(e); 164927dbe93S猫头猫 this.state = 'error'; 165927dbe93S猫头猫 this.stateCode = PluginStateCode.CannotParse; 166927dbe93S猫头猫 if (e?.stateCode) { 167927dbe93S猫头猫 this.stateCode = e.stateCode; 168927dbe93S猫头猫 } 169927dbe93S猫头猫 errorLog(`${pluginPath}插件无法解析 `, { 170927dbe93S猫头猫 stateCode: this.stateCode, 171927dbe93S猫头猫 message: e?.message, 172927dbe93S猫头猫 stack: e?.stack, 173927dbe93S猫头猫 }); 174927dbe93S猫头猫 _instance = e?.instance ?? { 175927dbe93S猫头猫 _path: '', 176927dbe93S猫头猫 platform: '', 177927dbe93S猫头猫 appVersion: '', 17820e6a092S猫头猫 async getMediaSource() { 179927dbe93S猫头猫 return null; 180927dbe93S猫头猫 }, 181927dbe93S猫头猫 async search() { 182927dbe93S猫头猫 return {}; 183927dbe93S猫头猫 }, 184927dbe93S猫头猫 async getAlbumInfo() { 185927dbe93S猫头猫 return null; 186927dbe93S猫头猫 }, 187927dbe93S猫头猫 }; 188927dbe93S猫头猫 } 189927dbe93S猫头猫 this.instance = _instance; 190927dbe93S猫头猫 this.path = pluginPath; 191927dbe93S猫头猫 this.name = _instance.platform; 192ab8941d9S猫头猫 if ( 193ab8941d9S猫头猫 this.instance.platform === '' || 194ab8941d9S猫头猫 this.instance.platform === undefined 195ab8941d9S猫头猫 ) { 196927dbe93S猫头猫 this.hash = ''; 197927dbe93S猫头猫 } else { 19874d0cf81S猫头猫 if (typeof funcCode === 'string') { 199927dbe93S猫头猫 this.hash = sha256(funcCode).toString(); 20074d0cf81S猫头猫 } else { 20174d0cf81S猫头猫 this.hash = sha256(funcCode.toString()).toString(); 20274d0cf81S猫头猫 } 203927dbe93S猫头猫 } 204927dbe93S猫头猫 205927dbe93S猫头猫 // 放在最后 206927dbe93S猫头猫 this.methods = new PluginMethods(this); 207927dbe93S猫头猫 } 208927dbe93S猫头猫 209927dbe93S猫头猫 private checkValid(_instance: IPlugin.IPluginInstance) { 210927dbe93S猫头猫 /** 版本号校验 */ 211927dbe93S猫头猫 if ( 212927dbe93S猫头猫 _instance.appVersion && 213927dbe93S猫头猫 !satisfies(DeviceInfo.getVersion(), _instance.appVersion) 214927dbe93S猫头猫 ) { 215927dbe93S猫头猫 throw { 216927dbe93S猫头猫 instance: _instance, 217927dbe93S猫头猫 stateCode: PluginStateCode.VersionNotMatch, 218927dbe93S猫头猫 }; 219927dbe93S猫头猫 } 220927dbe93S猫头猫 return true; 221927dbe93S猫头猫 } 222927dbe93S猫头猫} 223d5bfeb7eS猫头猫//#endregion 224927dbe93S猫头猫 225d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用 226927dbe93S猫头猫/** 有缓存等信息 */ 227927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods { 228927dbe93S猫头猫 private plugin; 229927dbe93S猫头猫 constructor(plugin: Plugin) { 230927dbe93S猫头猫 this.plugin = plugin; 231927dbe93S猫头猫 } 232927dbe93S猫头猫 /** 搜索 */ 233927dbe93S猫头猫 async search<T extends ICommon.SupportMediaType>( 234927dbe93S猫头猫 query: string, 235927dbe93S猫头猫 page: number, 236927dbe93S猫头猫 type: T, 237927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 238927dbe93S猫头猫 if (!this.plugin.instance.search) { 239927dbe93S猫头猫 return { 240927dbe93S猫头猫 isEnd: true, 241927dbe93S猫头猫 data: [], 242927dbe93S猫头猫 }; 243927dbe93S猫头猫 } 244927dbe93S猫头猫 2454060c00aS猫头猫 const result = 2464060c00aS猫头猫 (await this.plugin.instance.search(query, page, type)) ?? {}; 247927dbe93S猫头猫 if (Array.isArray(result.data)) { 248927dbe93S猫头猫 result.data.forEach(_ => { 249927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 250927dbe93S猫头猫 }); 251927dbe93S猫头猫 return { 252927dbe93S猫头猫 isEnd: result.isEnd ?? true, 253927dbe93S猫头猫 data: result.data, 254927dbe93S猫头猫 }; 255927dbe93S猫头猫 } 256927dbe93S猫头猫 return { 257927dbe93S猫头猫 isEnd: true, 258927dbe93S猫头猫 data: [], 259927dbe93S猫头猫 }; 260927dbe93S猫头猫 } 261927dbe93S猫头猫 262927dbe93S猫头猫 /** 获取真实源 */ 26320e6a092S猫头猫 async getMediaSource( 264927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 265abaede57S猫头猫 quality: IMusic.IQualityKey = 'standard', 266927dbe93S猫头猫 retryCount = 1, 267dc160d50S猫头猫 notUpdateCache = false, 268192ae2b0S猫头猫 ): Promise<IPlugin.IMediaSourceResult | null> { 269927dbe93S猫头猫 // 1. 本地搜索 其实直接读mediameta就好了 270927dbe93S猫头猫 const localPath = 2710e4173cdS猫头猫 getInternalData<string>(musicItem, InternalDataType.LOCALPATH) ?? 2720e4173cdS猫头猫 getInternalData<string>( 2730e4173cdS猫头猫 LocalMusicSheet.isLocalMusic(musicItem), 2740e4173cdS猫头猫 InternalDataType.LOCALPATH, 2750e4173cdS猫头猫 ); 276a84a85c5S猫头猫 if ( 277a84a85c5S猫头猫 localPath && 278a84a85c5S猫头猫 (localPath.startsWith('content://') || 279a84a85c5S猫头猫 (await FileSystem.exists(localPath))) 280a84a85c5S猫头猫 ) { 2810e4173cdS猫头猫 trace('本地播放', localPath); 282927dbe93S猫头猫 return { 283927dbe93S猫头猫 url: localPath, 284927dbe93S猫头猫 }; 285927dbe93S猫头猫 } 286a84a85c5S猫头猫 console.log('BFFF2'); 287a84a85c5S猫头猫 2887993f90eS猫头猫 if (musicItem.platform === localPluginPlatform) { 289f5935920S猫头猫 throw new Error('本地音乐不存在'); 290f5935920S猫头猫 } 291927dbe93S猫头猫 // 2. 缓存播放 292927dbe93S猫头猫 const mediaCache = Cache.get(musicItem); 293985f8e75S猫头猫 const pluginCacheControl = 294985f8e75S猫头猫 this.plugin.instance.cacheControl ?? 'no-cache'; 295cfa0fc07S猫头猫 if ( 296cfa0fc07S猫头猫 mediaCache && 297abaede57S猫头猫 mediaCache?.qualities?.[quality]?.url && 29848f4b873S猫头猫 (pluginCacheControl === CacheControl.Cache || 29948f4b873S猫头猫 (pluginCacheControl === CacheControl.NoCache && 300ef714860S猫头猫 Network.isOffline())) 301cfa0fc07S猫头猫 ) { 3025276aef9S猫头猫 trace('播放', '缓存播放'); 303abaede57S猫头猫 const qualityInfo = mediaCache.qualities[quality]; 304927dbe93S猫头猫 return { 305abaede57S猫头猫 url: qualityInfo.url, 306927dbe93S猫头猫 headers: mediaCache.headers, 3074060c00aS猫头猫 userAgent: 3084060c00aS猫头猫 mediaCache.userAgent ?? mediaCache.headers?.['user-agent'], 309927dbe93S猫头猫 }; 310927dbe93S猫头猫 } 311927dbe93S猫头猫 // 3. 插件解析 31220e6a092S猫头猫 if (!this.plugin.instance.getMediaSource) { 313abaede57S猫头猫 return {url: musicItem?.qualities?.[quality]?.url ?? musicItem.url}; 314927dbe93S猫头猫 } 315927dbe93S猫头猫 try { 316abaede57S猫头猫 const {url, headers} = (await this.plugin.instance.getMediaSource( 317abaede57S猫头猫 musicItem, 318abaede57S猫头猫 quality, 319abaede57S猫头猫 )) ?? {url: musicItem?.qualities?.[quality]?.url}; 320927dbe93S猫头猫 if (!url) { 321a28eac61S猫头猫 throw new Error('NOT RETRY'); 322927dbe93S猫头猫 } 3235276aef9S猫头猫 trace('播放', '插件播放'); 324927dbe93S猫头猫 const result = { 325927dbe93S猫头猫 url, 326927dbe93S猫头猫 headers, 327927dbe93S猫头猫 userAgent: headers?.['user-agent'], 328cfa0fc07S猫头猫 } as IPlugin.IMediaSourceResult; 329927dbe93S猫头猫 330dc160d50S猫头猫 if ( 331dc160d50S猫头猫 pluginCacheControl !== CacheControl.NoStore && 332dc160d50S猫头猫 !notUpdateCache 333dc160d50S猫头猫 ) { 334abaede57S猫头猫 Cache.update(musicItem, [ 335abaede57S猫头猫 ['headers', result.headers], 336abaede57S猫头猫 ['userAgent', result.userAgent], 337abaede57S猫头猫 [`qualities.${quality}.url`, url], 338abaede57S猫头猫 ]); 339752ffc5aS猫头猫 } 340cfa0fc07S猫头猫 341927dbe93S猫头猫 return result; 342927dbe93S猫头猫 } catch (e: any) { 343a28eac61S猫头猫 if (retryCount > 0 && e?.message !== 'NOT RETRY') { 344927dbe93S猫头猫 await delay(150); 345abaede57S猫头猫 return this.getMediaSource(musicItem, quality, --retryCount); 346927dbe93S猫头猫 } 347927dbe93S猫头猫 errorLog('获取真实源失败', e?.message); 348ea6d708fS猫头猫 devLog('error', '获取真实源失败', e, e?.message); 349192ae2b0S猫头猫 return null; 350927dbe93S猫头猫 } 351927dbe93S猫头猫 } 352927dbe93S猫头猫 353927dbe93S猫头猫 /** 获取音乐详情 */ 354927dbe93S猫头猫 async getMusicInfo( 355927dbe93S猫头猫 musicItem: ICommon.IMediaBase, 35674d0cf81S猫头猫 ): Promise<Partial<IMusic.IMusicItem> | null> { 357927dbe93S猫头猫 if (!this.plugin.instance.getMusicInfo) { 358d704daedS猫头猫 return null; 359927dbe93S猫头猫 } 36074d0cf81S猫头猫 try { 361927dbe93S猫头猫 return ( 362927dbe93S猫头猫 this.plugin.instance.getMusicInfo( 3637993f90eS猫头猫 resetMediaItem(musicItem, undefined, true), 364d704daedS猫头猫 ) ?? null 365927dbe93S猫头猫 ); 366ea6d708fS猫头猫 } catch (e: any) { 367ea6d708fS猫头猫 devLog('error', '获取音乐详情失败', e, e?.message); 368d704daedS猫头猫 return null; 36974d0cf81S猫头猫 } 370927dbe93S猫头猫 } 371927dbe93S猫头猫 372927dbe93S猫头猫 /** 获取歌词 */ 373927dbe93S猫头猫 async getLyric( 374927dbe93S猫头猫 musicItem: IMusic.IMusicItemBase, 375927dbe93S猫头猫 from?: IMusic.IMusicItemBase, 376927dbe93S猫头猫 ): Promise<ILyric.ILyricSource | null> { 377927dbe93S猫头猫 // 1.额外存储的meta信息 378927dbe93S猫头猫 const meta = MediaMeta.get(musicItem); 379927dbe93S猫头猫 if (meta && meta.associatedLrc) { 380927dbe93S猫头猫 // 有关联歌词 381927dbe93S猫头猫 if ( 382927dbe93S猫头猫 isSameMediaItem(musicItem, from) || 383927dbe93S猫头猫 isSameMediaItem(meta.associatedLrc, musicItem) 384927dbe93S猫头猫 ) { 385927dbe93S猫头猫 // 形成环路,断开当前的环 386927dbe93S猫头猫 await MediaMeta.update(musicItem, { 387927dbe93S猫头猫 associatedLrc: undefined, 388927dbe93S猫头猫 }); 389927dbe93S猫头猫 // 无歌词 390927dbe93S猫头猫 return null; 391927dbe93S猫头猫 } 392927dbe93S猫头猫 // 获取关联歌词 3937a91f04fS猫头猫 const associatedMeta = MediaMeta.get(meta.associatedLrc) ?? {}; 3944060c00aS猫头猫 const result = await this.getLyric( 3957a91f04fS猫头猫 {...meta.associatedLrc, ...associatedMeta}, 3964060c00aS猫头猫 from ?? musicItem, 3974060c00aS猫头猫 ); 398927dbe93S猫头猫 if (result) { 399927dbe93S猫头猫 // 如果有关联歌词,就返回关联歌词,深度优先 400927dbe93S猫头猫 return result; 401927dbe93S猫头猫 } 402927dbe93S猫头猫 } 403927dbe93S猫头猫 const cache = Cache.get(musicItem); 404927dbe93S猫头猫 let rawLrc = meta?.rawLrc || musicItem.rawLrc || cache?.rawLrc; 405927dbe93S猫头猫 let lrcUrl = meta?.lrc || musicItem.lrc || cache?.lrc; 406927dbe93S猫头猫 // 如果存在文本 407927dbe93S猫头猫 if (rawLrc) { 408927dbe93S猫头猫 return { 409927dbe93S猫头猫 rawLrc, 410927dbe93S猫头猫 lrc: lrcUrl, 411927dbe93S猫头猫 }; 412927dbe93S猫头猫 } 413927dbe93S猫头猫 // 2.本地缓存 414927dbe93S猫头猫 const localLrc = 4150e4173cdS猫头猫 meta?.[internalSerializeKey]?.local?.localLrc || 4160e4173cdS猫头猫 cache?.[internalSerializeKey]?.local?.localLrc; 417927dbe93S猫头猫 if (localLrc && (await exists(localLrc))) { 418927dbe93S猫头猫 rawLrc = await readFile(localLrc, 'utf8'); 419927dbe93S猫头猫 return { 420927dbe93S猫头猫 rawLrc, 421927dbe93S猫头猫 lrc: lrcUrl, 422927dbe93S猫头猫 }; 423927dbe93S猫头猫 } 424927dbe93S猫头猫 // 3.优先使用url 425927dbe93S猫头猫 if (lrcUrl) { 426927dbe93S猫头猫 try { 427927dbe93S猫头猫 // 需要超时时间 axios timeout 但是没生效 42861aca335S猫头猫 rawLrc = (await axios.get(lrcUrl, {timeout: 2000})).data; 429927dbe93S猫头猫 return { 430927dbe93S猫头猫 rawLrc, 431927dbe93S猫头猫 lrc: lrcUrl, 432927dbe93S猫头猫 }; 433927dbe93S猫头猫 } catch { 434927dbe93S猫头猫 lrcUrl = undefined; 435927dbe93S猫头猫 } 436927dbe93S猫头猫 } 437927dbe93S猫头猫 // 4. 如果地址失效 438927dbe93S猫头猫 if (!lrcUrl) { 439927dbe93S猫头猫 // 插件获得url 440927dbe93S猫头猫 try { 4417a91f04fS猫头猫 let lrcSource; 4427a91f04fS猫头猫 if (from) { 4437a91f04fS猫头猫 lrcSource = await PluginManager.getByMedia( 4447a91f04fS猫头猫 musicItem, 4457a91f04fS猫头猫 )?.instance?.getLyric?.( 446927dbe93S猫头猫 resetMediaItem(musicItem, undefined, true), 447927dbe93S猫头猫 ); 4487a91f04fS猫头猫 } else { 4497a91f04fS猫头猫 lrcSource = await this.plugin.instance?.getLyric?.( 4507a91f04fS猫头猫 resetMediaItem(musicItem, undefined, true), 4517a91f04fS猫头猫 ); 4527a91f04fS猫头猫 } 4537a91f04fS猫头猫 454927dbe93S猫头猫 rawLrc = lrcSource?.rawLrc; 455927dbe93S猫头猫 lrcUrl = lrcSource?.lrc; 456927dbe93S猫头猫 } catch (e: any) { 457927dbe93S猫头猫 trace('插件获取歌词失败', e?.message, 'error'); 458ea6d708fS猫头猫 devLog('error', '插件获取歌词失败', e, e?.message); 459927dbe93S猫头猫 } 460927dbe93S猫头猫 } 461927dbe93S猫头猫 // 5. 最后一次请求 462927dbe93S猫头猫 if (rawLrc || lrcUrl) { 463927dbe93S猫头猫 const filename = `${pathConst.lrcCachePath}${nanoid()}.lrc`; 464927dbe93S猫头猫 if (lrcUrl) { 465927dbe93S猫头猫 try { 46661aca335S猫头猫 rawLrc = (await axios.get(lrcUrl, {timeout: 2000})).data; 467927dbe93S猫头猫 } catch {} 468927dbe93S猫头猫 } 469927dbe93S猫头猫 if (rawLrc) { 470927dbe93S猫头猫 await writeFile(filename, rawLrc, 'utf8'); 471927dbe93S猫头猫 // 写入缓存 472927dbe93S猫头猫 Cache.update(musicItem, [ 4730e4173cdS猫头猫 [`${internalSerializeKey}.local.localLrc`, filename], 474927dbe93S猫头猫 ]); 475927dbe93S猫头猫 // 如果有meta 476927dbe93S猫头猫 if (meta) { 477927dbe93S猫头猫 MediaMeta.update(musicItem, [ 4780e4173cdS猫头猫 [`${internalSerializeKey}.local.localLrc`, filename], 479927dbe93S猫头猫 ]); 480927dbe93S猫头猫 } 481927dbe93S猫头猫 return { 482927dbe93S猫头猫 rawLrc, 483927dbe93S猫头猫 lrc: lrcUrl, 484927dbe93S猫头猫 }; 485927dbe93S猫头猫 } 486927dbe93S猫头猫 } 4873a6f67b1S猫头猫 // 6. 如果是本地文件 4883a6f67b1S猫头猫 const isDownloaded = LocalMusicSheet.isLocalMusic(musicItem); 4893a6f67b1S猫头猫 if (musicItem.platform !== localPluginPlatform && isDownloaded) { 4903a6f67b1S猫头猫 const res = await localFilePlugin.instance!.getLyric!(isDownloaded); 4913a6f67b1S猫头猫 if (res) { 4923a6f67b1S猫头猫 return res; 4933a6f67b1S猫头猫 } 4943a6f67b1S猫头猫 } 495ea6d708fS猫头猫 devLog('warn', '无歌词'); 496927dbe93S猫头猫 497927dbe93S猫头猫 return null; 498927dbe93S猫头猫 } 499927dbe93S猫头猫 500927dbe93S猫头猫 /** 获取歌词文本 */ 501927dbe93S猫头猫 async getLyricText( 502927dbe93S猫头猫 musicItem: IMusic.IMusicItem, 503927dbe93S猫头猫 ): Promise<string | undefined> { 504927dbe93S猫头猫 return (await this.getLyric(musicItem))?.rawLrc; 505927dbe93S猫头猫 } 506927dbe93S猫头猫 507927dbe93S猫头猫 /** 获取专辑信息 */ 508927dbe93S猫头猫 async getAlbumInfo( 509927dbe93S猫头猫 albumItem: IAlbum.IAlbumItemBase, 510f9afcc0dS猫头猫 page: number = 1, 511f9afcc0dS猫头猫 ): Promise<IPlugin.IAlbumInfoResult | null> { 512927dbe93S猫头猫 if (!this.plugin.instance.getAlbumInfo) { 513f9afcc0dS猫头猫 return { 514f9afcc0dS猫头猫 albumItem, 515f2a4767cS猫头猫 musicList: (albumItem?.musicList ?? []).map( 516f2a4767cS猫头猫 resetMediaItem, 517f2a4767cS猫头猫 this.plugin.name, 518f2a4767cS猫头猫 true, 519f2a4767cS猫头猫 ), 520f9afcc0dS猫头猫 isEnd: true, 521f9afcc0dS猫头猫 }; 522927dbe93S猫头猫 } 523927dbe93S猫头猫 try { 524927dbe93S猫头猫 const result = await this.plugin.instance.getAlbumInfo( 525927dbe93S猫头猫 resetMediaItem(albumItem, undefined, true), 526f9afcc0dS猫头猫 page, 527927dbe93S猫头猫 ); 5285276aef9S猫头猫 if (!result) { 5295276aef9S猫头猫 throw new Error(); 5305276aef9S猫头猫 } 531927dbe93S猫头猫 result?.musicList?.forEach(_ => { 532927dbe93S猫头猫 resetMediaItem(_, this.plugin.name); 53396744680S猫头猫 _.album = albumItem.title; 534927dbe93S猫头猫 }); 5355276aef9S猫头猫 536f9afcc0dS猫头猫 if (page <= 1) { 537f9afcc0dS猫头猫 // 合并信息 538f9afcc0dS猫头猫 return { 539f9afcc0dS猫头猫 albumItem: {...albumItem, ...(result?.albumItem ?? {})}, 540f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 541f9afcc0dS猫头猫 musicList: result.musicList, 542f9afcc0dS猫头猫 }; 543f9afcc0dS猫头猫 } else { 544f9afcc0dS猫头猫 return { 545f9afcc0dS猫头猫 isEnd: result.isEnd === false ? false : true, 546f9afcc0dS猫头猫 musicList: result.musicList, 547f9afcc0dS猫头猫 }; 548f9afcc0dS猫头猫 } 5494394410dS猫头猫 } catch (e: any) { 5504394410dS猫头猫 trace('获取专辑信息失败', e?.message); 551ea6d708fS猫头猫 devLog('error', '获取专辑信息失败', e, e?.message); 552ea6d708fS猫头猫 553f9afcc0dS猫头猫 return null; 554927dbe93S猫头猫 } 555927dbe93S猫头猫 } 556927dbe93S猫头猫 5575830c002S猫头猫 /** 获取歌单信息 */ 5585830c002S猫头猫 async getMusicSheetInfo( 5595830c002S猫头猫 sheetItem: IMusic.IMusicSheetItem, 5605830c002S猫头猫 page: number = 1, 5615830c002S猫头猫 ): Promise<IPlugin.ISheetInfoResult | null> { 5625281926bS猫头猫 if (!this.plugin.instance.getMusicSheetInfo) { 5635830c002S猫头猫 return { 5645830c002S猫头猫 sheetItem, 5655830c002S猫头猫 musicList: sheetItem?.musicList ?? [], 5665830c002S猫头猫 isEnd: true, 5675830c002S猫头猫 }; 5685830c002S猫头猫 } 5695830c002S猫头猫 try { 5705830c002S猫头猫 const result = await this.plugin.instance?.getMusicSheetInfo?.( 5715830c002S猫头猫 resetMediaItem(sheetItem, undefined, true), 5725830c002S猫头猫 page, 5735830c002S猫头猫 ); 5745830c002S猫头猫 if (!result) { 5755830c002S猫头猫 throw new Error(); 5765830c002S猫头猫 } 5775830c002S猫头猫 result?.musicList?.forEach(_ => { 5785830c002S猫头猫 resetMediaItem(_, this.plugin.name); 5795830c002S猫头猫 }); 5805830c002S猫头猫 5815830c002S猫头猫 if (page <= 1) { 5825830c002S猫头猫 // 合并信息 5835830c002S猫头猫 return { 5845830c002S猫头猫 sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})}, 5855830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 5865830c002S猫头猫 musicList: result.musicList, 5875830c002S猫头猫 }; 5885830c002S猫头猫 } else { 5895830c002S猫头猫 return { 5905830c002S猫头猫 isEnd: result.isEnd === false ? false : true, 5915830c002S猫头猫 musicList: result.musicList, 5925830c002S猫头猫 }; 5935830c002S猫头猫 } 5945830c002S猫头猫 } catch (e: any) { 5955830c002S猫头猫 trace('获取歌单信息失败', e, e?.message); 5965830c002S猫头猫 devLog('error', '获取歌单信息失败', e, e?.message); 5975830c002S猫头猫 5985830c002S猫头猫 return null; 5995830c002S猫头猫 } 6005830c002S猫头猫 } 6015830c002S猫头猫 602927dbe93S猫头猫 /** 查询作者信息 */ 603efb9da24S猫头猫 async getArtistWorks<T extends IArtist.ArtistMediaType>( 604927dbe93S猫头猫 artistItem: IArtist.IArtistItem, 605927dbe93S猫头猫 page: number, 606927dbe93S猫头猫 type: T, 607927dbe93S猫头猫 ): Promise<IPlugin.ISearchResult<T>> { 608efb9da24S猫头猫 if (!this.plugin.instance.getArtistWorks) { 609927dbe93S猫头猫 return { 610927dbe93S猫头猫 isEnd: true, 611927dbe93S猫头猫 data: [], 612927dbe93S猫头猫 }; 613927dbe93S猫头猫 } 614927dbe93S猫头猫 try { 615efb9da24S猫头猫 const result = await this.plugin.instance.getArtistWorks( 616927dbe93S猫头猫 artistItem, 617927dbe93S猫头猫 page, 618927dbe93S猫头猫 type, 619927dbe93S猫头猫 ); 620927dbe93S猫头猫 if (!result.data) { 621927dbe93S猫头猫 return { 622927dbe93S猫头猫 isEnd: true, 623927dbe93S猫头猫 data: [], 624927dbe93S猫头猫 }; 625927dbe93S猫头猫 } 626927dbe93S猫头猫 result.data?.forEach(_ => resetMediaItem(_, this.plugin.name)); 627927dbe93S猫头猫 return { 628927dbe93S猫头猫 isEnd: result.isEnd ?? true, 629927dbe93S猫头猫 data: result.data, 630927dbe93S猫头猫 }; 6314394410dS猫头猫 } catch (e: any) { 6324394410dS猫头猫 trace('查询作者信息失败', e?.message); 633ea6d708fS猫头猫 devLog('error', '查询作者信息失败', e, e?.message); 634ea6d708fS猫头猫 635927dbe93S猫头猫 throw e; 636927dbe93S猫头猫 } 637927dbe93S猫头猫 } 63808380090S猫头猫 63908380090S猫头猫 /** 导入歌单 */ 64008380090S猫头猫 async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> { 64108380090S猫头猫 try { 64208380090S猫头猫 const result = 64308380090S猫头猫 (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? []; 64408380090S猫头猫 result.forEach(_ => resetMediaItem(_, this.plugin.name)); 64508380090S猫头猫 return result; 646ea6d708fS猫头猫 } catch (e: any) { 6470e4173cdS猫头猫 console.log(e); 648ea6d708fS猫头猫 devLog('error', '导入歌单失败', e, e?.message); 649ea6d708fS猫头猫 65008380090S猫头猫 return []; 65108380090S猫头猫 } 65208380090S猫头猫 } 6534d9d3c4cS猫头猫 /** 导入单曲 */ 6544d9d3c4cS猫头猫 async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> { 6554d9d3c4cS猫头猫 try { 6564d9d3c4cS猫头猫 const result = await this.plugin.instance?.importMusicItem?.( 6574d9d3c4cS猫头猫 urlLike, 6584d9d3c4cS猫头猫 ); 6594d9d3c4cS猫头猫 if (!result) { 6604d9d3c4cS猫头猫 throw new Error(); 6614d9d3c4cS猫头猫 } 6624d9d3c4cS猫头猫 resetMediaItem(result, this.plugin.name); 6634d9d3c4cS猫头猫 return result; 664ea6d708fS猫头猫 } catch (e: any) { 665ea6d708fS猫头猫 devLog('error', '导入单曲失败', e, e?.message); 666ea6d708fS猫头猫 6674d9d3c4cS猫头猫 return null; 6684d9d3c4cS猫头猫 } 6694d9d3c4cS猫头猫 } 670d52aa40eS猫头猫 /** 获取榜单 */ 67192b6c95aS猫头猫 async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> { 672d52aa40eS猫头猫 try { 673d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopLists?.(); 674d52aa40eS猫头猫 if (!result) { 675d52aa40eS猫头猫 throw new Error(); 676d52aa40eS猫头猫 } 677d52aa40eS猫头猫 return result; 678d52aa40eS猫头猫 } catch (e: any) { 679d52aa40eS猫头猫 devLog('error', '获取榜单失败', e, e?.message); 680d52aa40eS猫头猫 return []; 681d52aa40eS猫头猫 } 682d52aa40eS猫头猫 } 683d52aa40eS猫头猫 /** 获取榜单详情 */ 684d52aa40eS猫头猫 async getTopListDetail( 68592b6c95aS猫头猫 topListItem: IMusic.IMusicSheetItemBase, 686956ee1b7S猫头猫 page: number, 687956ee1b7S猫头猫 ): Promise<IPlugin.ITopListInfoResult> { 688d52aa40eS猫头猫 try { 689d52aa40eS猫头猫 const result = await this.plugin.instance?.getTopListDetail?.( 690d52aa40eS猫头猫 topListItem, 691956ee1b7S猫头猫 page, 692d52aa40eS猫头猫 ); 693d52aa40eS猫头猫 if (!result) { 694d52aa40eS猫头猫 throw new Error(); 695d52aa40eS猫头猫 } 696d384662fS猫头猫 if (result.musicList) { 697d384662fS猫头猫 result.musicList.forEach(_ => 698d384662fS猫头猫 resetMediaItem(_, this.plugin.name), 699d384662fS猫头猫 ); 700d384662fS猫头猫 } 701956ee1b7S猫头猫 if (result.isEnd !== false) { 702956ee1b7S猫头猫 result.isEnd = true; 703956ee1b7S猫头猫 } 704d52aa40eS猫头猫 return result; 705d52aa40eS猫头猫 } catch (e: any) { 706d52aa40eS猫头猫 devLog('error', '获取榜单详情失败', e, e?.message); 707d52aa40eS猫头猫 return { 708956ee1b7S猫头猫 isEnd: true, 709956ee1b7S猫头猫 topListItem: topListItem as IMusic.IMusicSheetItem, 710d52aa40eS猫头猫 musicList: [], 711d52aa40eS猫头猫 }; 712d52aa40eS猫头猫 } 713d52aa40eS猫头猫 } 714ceb900cdS猫头猫 7155830c002S猫头猫 /** 获取推荐歌单的tag */ 716ceb900cdS猫头猫 async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> { 717ceb900cdS猫头猫 try { 718ceb900cdS猫头猫 const result = 719ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetTags?.(); 720ceb900cdS猫头猫 if (!result) { 721ceb900cdS猫头猫 throw new Error(); 722ceb900cdS猫头猫 } 723ceb900cdS猫头猫 return result; 724ceb900cdS猫头猫 } catch (e: any) { 725ceb900cdS猫头猫 devLog('error', '获取推荐歌单失败', e, e?.message); 726ceb900cdS猫头猫 return { 727ceb900cdS猫头猫 data: [], 728ceb900cdS猫头猫 }; 729ceb900cdS猫头猫 } 730ceb900cdS猫头猫 } 7315830c002S猫头猫 /** 获取某个tag的推荐歌单 */ 732ceb900cdS猫头猫 async getRecommendSheetsByTag( 733ceb900cdS猫头猫 tagItem: ICommon.IUnique, 734ceb900cdS猫头猫 page?: number, 735ceb900cdS猫头猫 ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> { 736ceb900cdS猫头猫 try { 737ceb900cdS猫头猫 const result = 738ceb900cdS猫头猫 await this.plugin.instance?.getRecommendSheetsByTag?.( 739ceb900cdS猫头猫 tagItem, 740ceb900cdS猫头猫 page ?? 1, 741ceb900cdS猫头猫 ); 742ceb900cdS猫头猫 if (!result) { 743ceb900cdS猫头猫 throw new Error(); 744ceb900cdS猫头猫 } 745ceb900cdS猫头猫 if (result.isEnd !== false) { 746ceb900cdS猫头猫 result.isEnd = true; 747ceb900cdS猫头猫 } 748ceb900cdS猫头猫 if (!result.data) { 749ceb900cdS猫头猫 result.data = []; 750ceb900cdS猫头猫 } 751ceb900cdS猫头猫 result.data.forEach(item => resetMediaItem(item, this.plugin.name)); 752ceb900cdS猫头猫 753ceb900cdS猫头猫 return result; 754ceb900cdS猫头猫 } catch (e: any) { 755ceb900cdS猫头猫 devLog('error', '获取推荐歌单详情失败', e, e?.message); 756ceb900cdS猫头猫 return { 757ceb900cdS猫头猫 isEnd: true, 758ceb900cdS猫头猫 data: [], 759ceb900cdS猫头猫 }; 760ceb900cdS猫头猫 } 761ceb900cdS猫头猫 } 762927dbe93S猫头猫} 763d5bfeb7eS猫头猫//#endregion 7641a5528a0S猫头猫 765927dbe93S猫头猫let plugins: Array<Plugin> = []; 766927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins); 76774d0cf81S猫头猫 768d5bfeb7eS猫头猫//#region 本地音乐插件 76974d0cf81S猫头猫/** 本地插件 */ 77074d0cf81S猫头猫const localFilePlugin = new Plugin(function () { 7710e4173cdS猫头猫 return { 772d5bfeb7eS猫头猫 platform: localPluginPlatform, 77374d0cf81S猫头猫 _path: '', 77474d0cf81S猫头猫 async getMusicInfo(musicBase) { 77574d0cf81S猫头猫 const localPath = getInternalData<string>( 77674d0cf81S猫头猫 musicBase, 77774d0cf81S猫头猫 InternalDataType.LOCALPATH, 7780e4173cdS猫头猫 ); 77974d0cf81S猫头猫 if (localPath) { 78074d0cf81S猫头猫 const coverImg = await Mp3Util.getMediaCoverImg(localPath); 78174d0cf81S猫头猫 return { 78274d0cf81S猫头猫 artwork: coverImg, 78374d0cf81S猫头猫 }; 78474d0cf81S猫头猫 } 78574d0cf81S猫头猫 return null; 78674d0cf81S猫头猫 }, 7877993f90eS猫头猫 async getLyric(musicBase) { 7887993f90eS猫头猫 const localPath = getInternalData<string>( 7897993f90eS猫头猫 musicBase, 7907993f90eS猫头猫 InternalDataType.LOCALPATH, 7917993f90eS猫头猫 ); 7923a6f67b1S猫头猫 let rawLrc: string | null = null; 7937993f90eS猫头猫 if (localPath) { 7943a6f67b1S猫头猫 // 读取内嵌歌词 7953a6f67b1S猫头猫 try { 7963a6f67b1S猫头猫 rawLrc = await Mp3Util.getLyric(localPath); 7973a6f67b1S猫头猫 } catch (e) { 798a84a85c5S猫头猫 console.log('读取内嵌歌词失败', e); 7997993f90eS猫头猫 } 8003a6f67b1S猫头猫 if (!rawLrc) { 8013a6f67b1S猫头猫 // 读取配置歌词 8023a6f67b1S猫头猫 const lastDot = localPath.lastIndexOf('.'); 8033a6f67b1S猫头猫 const lrcPath = localPath.slice(0, lastDot) + '.lrc'; 8043a6f67b1S猫头猫 8053a6f67b1S猫头猫 try { 8063a6f67b1S猫头猫 if (await exists(lrcPath)) { 8073a6f67b1S猫头猫 rawLrc = await readFile(lrcPath, 'utf8'); 8083a6f67b1S猫头猫 } 8093a6f67b1S猫头猫 } catch {} 8103a6f67b1S猫头猫 } 8113a6f67b1S猫头猫 } 8123a6f67b1S猫头猫 8133a6f67b1S猫头猫 return rawLrc 8143a6f67b1S猫头猫 ? { 8153a6f67b1S猫头猫 rawLrc, 8163a6f67b1S猫头猫 } 8173a6f67b1S猫头猫 : null; 8187993f90eS猫头猫 }, 819a84a85c5S猫头猫 async importMusicItem(urlLike) { 820a84a85c5S猫头猫 let meta: any = {}; 821a84a85c5S猫头猫 try { 822a84a85c5S猫头猫 meta = await Mp3Util.getBasicMeta(urlLike); 823a84a85c5S猫头猫 } catch {} 824a84a85c5S猫头猫 const id = await FileSystem.hash(urlLike, 'MD5'); 825a84a85c5S猫头猫 return { 826a84a85c5S猫头猫 id: id, 827a84a85c5S猫头猫 platform: '本地', 828a84a85c5S猫头猫 title: meta?.title ?? getFileName(urlLike), 829a84a85c5S猫头猫 artist: meta?.artist ?? '未知歌手', 830a84a85c5S猫头猫 duration: parseInt(meta?.duration ?? '0') / 1000, 831a84a85c5S猫头猫 album: meta?.album ?? '未知专辑', 832a84a85c5S猫头猫 artwork: '', 833a84a85c5S猫头猫 [internalSerializeKey]: { 834a84a85c5S猫头猫 localPath: urlLike, 835a84a85c5S猫头猫 }, 836a84a85c5S猫头猫 }; 837a84a85c5S猫头猫 }, 83874d0cf81S猫头猫 }; 83974d0cf81S猫头猫}, ''); 8407993f90eS猫头猫localFilePlugin.hash = localPluginHash; 841927dbe93S猫头猫 842d5bfeb7eS猫头猫//#endregion 843d5bfeb7eS猫头猫 844927dbe93S猫头猫async function setup() { 845927dbe93S猫头猫 const _plugins: Array<Plugin> = []; 846927dbe93S猫头猫 try { 847927dbe93S猫头猫 // 加载插件 848927dbe93S猫头猫 const pluginsPaths = await readDir(pathConst.pluginPath); 849927dbe93S猫头猫 for (let i = 0; i < pluginsPaths.length; ++i) { 850927dbe93S猫头猫 const _pluginUrl = pluginsPaths[i]; 8511e263108S猫头猫 trace('初始化插件', _pluginUrl); 8521e263108S猫头猫 if ( 8531e263108S猫头猫 _pluginUrl.isFile() && 8541e263108S猫头猫 (_pluginUrl.name?.endsWith?.('.js') || 8551e263108S猫头猫 _pluginUrl.path?.endsWith?.('.js')) 8561e263108S猫头猫 ) { 857927dbe93S猫头猫 const funcCode = await readFile(_pluginUrl.path, 'utf8'); 858927dbe93S猫头猫 const plugin = new Plugin(funcCode, _pluginUrl.path); 8594060c00aS猫头猫 const _pluginIndex = _plugins.findIndex( 8604060c00aS猫头猫 p => p.hash === plugin.hash, 8614060c00aS猫头猫 ); 862927dbe93S猫头猫 if (_pluginIndex !== -1) { 863927dbe93S猫头猫 // 重复插件,直接忽略 8640c266394S猫头猫 continue; 865927dbe93S猫头猫 } 866927dbe93S猫头猫 plugin.hash !== '' && _plugins.push(plugin); 867927dbe93S猫头猫 } 868927dbe93S猫头猫 } 869927dbe93S猫头猫 870927dbe93S猫头猫 plugins = _plugins; 871e08d37a3S猫头猫 /** 初始化meta信息 */ 872c2b3a262S猫头猫 await PluginMeta.setupMeta(plugins.map(_ => _.name)); 873c2b3a262S猫头猫 /** 查看一下是否有禁用的标记 */ 874c2b3a262S猫头猫 const allMeta = PluginMeta.getPluginMetaAll() ?? {}; 875c2b3a262S猫头猫 for (let plugin of plugins) { 876c2b3a262S猫头猫 if (allMeta[plugin.name]?.enabled === false) { 877c2b3a262S猫头猫 plugin.state = 'disabled'; 878c2b3a262S猫头猫 } 879c2b3a262S猫头猫 } 880c2b3a262S猫头猫 pluginStateMapper.notify(); 881927dbe93S猫头猫 } catch (e: any) { 8824060c00aS猫头猫 ToastAndroid.show( 8834060c00aS猫头猫 `插件初始化失败:${e?.message ?? e}`, 8844060c00aS猫头猫 ToastAndroid.LONG, 8854060c00aS猫头猫 ); 8861a5528a0S猫头猫 errorLog('插件初始化失败', e?.message); 887927dbe93S猫头猫 throw e; 888927dbe93S猫头猫 } 889927dbe93S猫头猫} 890927dbe93S猫头猫 891e36e2599S猫头猫interface IInstallPluginConfig { 892e36e2599S猫头猫 notCheckVersion?: boolean; 893e36e2599S猫头猫} 894e36e2599S猫头猫 895927dbe93S猫头猫// 安装插件 896e36e2599S猫头猫async function installPlugin( 897e36e2599S猫头猫 pluginPath: string, 898e36e2599S猫头猫 config?: IInstallPluginConfig, 899e36e2599S猫头猫) { 90022c09412S猫头猫 // if (pluginPath.endsWith('.js')) { 901927dbe93S猫头猫 const funcCode = await readFile(pluginPath, 'utf8'); 902e36e2599S猫头猫 903e36e2599S猫头猫 if (funcCode) { 904927dbe93S猫头猫 const plugin = new Plugin(funcCode, pluginPath); 905927dbe93S猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 906927dbe93S猫头猫 if (_pluginIndex !== -1) { 907e36e2599S猫头猫 // 静默忽略 908e36e2599S猫头猫 return plugin; 909927dbe93S猫头猫 } 910e36e2599S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 911e36e2599S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 912e36e2599S猫头猫 if ( 913e36e2599S猫头猫 compare( 914e36e2599S猫头猫 oldVersionPlugin.instance.version ?? '', 915e36e2599S猫头猫 plugin.instance.version ?? '', 916e36e2599S猫头猫 '>', 917e36e2599S猫头猫 ) 918e36e2599S猫头猫 ) { 919e36e2599S猫头猫 throw new Error('已安装更新版本的插件'); 920e36e2599S猫头猫 } 921e36e2599S猫头猫 } 922e36e2599S猫头猫 923927dbe93S猫头猫 if (plugin.hash !== '') { 924927dbe93S猫头猫 const fn = nanoid(); 925e36e2599S猫头猫 if (oldVersionPlugin) { 926e36e2599S猫头猫 plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash); 927e36e2599S猫头猫 try { 928e36e2599S猫头猫 await unlink(oldVersionPlugin.path); 929e36e2599S猫头猫 } catch {} 930e36e2599S猫头猫 } 931927dbe93S猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 932927dbe93S猫头猫 await copyFile(pluginPath, _pluginPath); 933927dbe93S猫头猫 plugin.path = _pluginPath; 934927dbe93S猫头猫 plugins = plugins.concat(plugin); 935927dbe93S猫头猫 pluginStateMapper.notify(); 936a84a85c5S猫头猫 return plugin; 937927dbe93S猫头猫 } 938e36e2599S猫头猫 throw new Error('插件无法解析!'); 939927dbe93S猫头猫 } 940e36e2599S猫头猫 throw new Error('插件无法识别!'); 941c2b3a262S猫头猫} 942c2b3a262S猫头猫 943c2b3a262S猫头猫async function installPluginFromUrl( 944c2b3a262S猫头猫 url: string, 945c2b3a262S猫头猫 config?: IInstallPluginConfig, 946c2b3a262S猫头猫) { 94758992c6bS猫头猫 try { 94858992c6bS猫头猫 const funcCode = (await axios.get(url)).data; 94958992c6bS猫头猫 if (funcCode) { 95058992c6bS猫头猫 const plugin = new Plugin(funcCode, ''); 95158992c6bS猫头猫 const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash); 95258992c6bS猫头猫 if (_pluginIndex !== -1) { 9538b7ddca8S猫头猫 // 静默忽略 9548b7ddca8S猫头猫 return; 95558992c6bS猫头猫 } 95625c1bd29S猫头猫 const oldVersionPlugin = plugins.find(p => p.name === plugin.name); 957c2b3a262S猫头猫 if (oldVersionPlugin && !config?.notCheckVersion) { 95825c1bd29S猫头猫 if ( 95925c1bd29S猫头猫 compare( 96025c1bd29S猫头猫 oldVersionPlugin.instance.version ?? '', 96125c1bd29S猫头猫 plugin.instance.version ?? '', 96225c1bd29S猫头猫 '>', 96325c1bd29S猫头猫 ) 96425c1bd29S猫头猫 ) { 96525c1bd29S猫头猫 throw new Error('已安装更新版本的插件'); 96625c1bd29S猫头猫 } 96725c1bd29S猫头猫 } 96825c1bd29S猫头猫 96958992c6bS猫头猫 if (plugin.hash !== '') { 97058992c6bS猫头猫 const fn = nanoid(); 97158992c6bS猫头猫 const _pluginPath = `${pathConst.pluginPath}${fn}.js`; 97258992c6bS猫头猫 await writeFile(_pluginPath, funcCode, 'utf8'); 97358992c6bS猫头猫 plugin.path = _pluginPath; 97458992c6bS猫头猫 plugins = plugins.concat(plugin); 97525c1bd29S猫头猫 if (oldVersionPlugin) { 97625c1bd29S猫头猫 plugins = plugins.filter( 97725c1bd29S猫头猫 _ => _.hash !== oldVersionPlugin.hash, 97825c1bd29S猫头猫 ); 97925c1bd29S猫头猫 try { 98025c1bd29S猫头猫 await unlink(oldVersionPlugin.path); 98125c1bd29S猫头猫 } catch {} 98225c1bd29S猫头猫 } 98358992c6bS猫头猫 pluginStateMapper.notify(); 98458992c6bS猫头猫 return; 98558992c6bS猫头猫 } 98674acbfc0S猫头猫 throw new Error('插件无法解析!'); 98758992c6bS猫头猫 } 98825c1bd29S猫头猫 } catch (e: any) { 989ea6d708fS猫头猫 devLog('error', 'URL安装插件失败', e, e?.message); 99058992c6bS猫头猫 errorLog('URL安装插件失败', e); 99125c1bd29S猫头猫 throw new Error(e?.message ?? ''); 99258992c6bS猫头猫 } 99358992c6bS猫头猫} 99458992c6bS猫头猫 995927dbe93S猫头猫/** 卸载插件 */ 996927dbe93S猫头猫async function uninstallPlugin(hash: string) { 997927dbe93S猫头猫 const targetIndex = plugins.findIndex(_ => _.hash === hash); 998927dbe93S猫头猫 if (targetIndex !== -1) { 999927dbe93S猫头猫 try { 100024e5e74aS猫头猫 const pluginName = plugins[targetIndex].name; 1001927dbe93S猫头猫 await unlink(plugins[targetIndex].path); 1002927dbe93S猫头猫 plugins = plugins.filter(_ => _.hash !== hash); 1003927dbe93S猫头猫 pluginStateMapper.notify(); 100424e5e74aS猫头猫 if (plugins.every(_ => _.name !== pluginName)) { 100524e5e74aS猫头猫 await MediaMeta.removePlugin(pluginName); 100624e5e74aS猫头猫 } 1007927dbe93S猫头猫 } catch {} 1008927dbe93S猫头猫 } 1009927dbe93S猫头猫} 1010927dbe93S猫头猫 101108882a77S猫头猫async function uninstallAllPlugins() { 101208882a77S猫头猫 await Promise.all( 101308882a77S猫头猫 plugins.map(async plugin => { 101408882a77S猫头猫 try { 101508882a77S猫头猫 const pluginName = plugin.name; 101608882a77S猫头猫 await unlink(plugin.path); 101708882a77S猫头猫 await MediaMeta.removePlugin(pluginName); 101808882a77S猫头猫 } catch (e) {} 101908882a77S猫头猫 }), 102008882a77S猫头猫 ); 102108882a77S猫头猫 plugins = []; 102208882a77S猫头猫 pluginStateMapper.notify(); 1023e08d37a3S猫头猫 1024e08d37a3S猫头猫 /** 清除空余文件,异步做就可以了 */ 1025e08d37a3S猫头猫 readDir(pathConst.pluginPath) 1026e08d37a3S猫头猫 .then(fns => { 1027e08d37a3S猫头猫 fns.forEach(fn => { 1028e08d37a3S猫头猫 unlink(fn.path).catch(emptyFunction); 1029e08d37a3S猫头猫 }); 1030e08d37a3S猫头猫 }) 1031e08d37a3S猫头猫 .catch(emptyFunction); 103208882a77S猫头猫} 103308882a77S猫头猫 103425c1bd29S猫头猫async function updatePlugin(plugin: Plugin) { 103525c1bd29S猫头猫 const updateUrl = plugin.instance.srcUrl; 103625c1bd29S猫头猫 if (!updateUrl) { 103725c1bd29S猫头猫 throw new Error('没有更新源'); 103825c1bd29S猫头猫 } 103925c1bd29S猫头猫 try { 104025c1bd29S猫头猫 await installPluginFromUrl(updateUrl); 104125c1bd29S猫头猫 } catch (e: any) { 104225c1bd29S猫头猫 if (e.message === '插件已安装') { 104325c1bd29S猫头猫 throw new Error('当前已是最新版本'); 104425c1bd29S猫头猫 } else { 104525c1bd29S猫头猫 throw e; 104625c1bd29S猫头猫 } 104725c1bd29S猫头猫 } 104825c1bd29S猫头猫} 104925c1bd29S猫头猫 1050927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) { 10512c595535S猫头猫 return getByName(mediaItem?.platform); 1052927dbe93S猫头猫} 1053927dbe93S猫头猫 1054927dbe93S猫头猫function getByHash(hash: string) { 10557993f90eS猫头猫 return hash === localPluginHash 10567993f90eS猫头猫 ? localFilePlugin 10577993f90eS猫头猫 : plugins.find(_ => _.hash === hash); 1058927dbe93S猫头猫} 1059927dbe93S猫头猫 1060927dbe93S猫头猫function getByName(name: string) { 10617993f90eS猫头猫 return name === localPluginPlatform 10620e4173cdS猫头猫 ? localFilePlugin 10630e4173cdS猫头猫 : plugins.find(_ => _.name === name); 1064927dbe93S猫头猫} 1065927dbe93S猫头猫 1066927dbe93S猫头猫function getValidPlugins() { 1067927dbe93S猫头猫 return plugins.filter(_ => _.state === 'enabled'); 1068927dbe93S猫头猫} 1069927dbe93S猫头猫 10702b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) { 10712b80a429S猫头猫 return plugins.filter( 10722b80a429S猫头猫 _ => 10732b80a429S猫头猫 _.state === 'enabled' && 10742b80a429S猫头猫 _.instance.search && 107539ac60f7S猫头猫 (supportedSearchType && _.instance.supportedSearchType 107639ac60f7S猫头猫 ? _.instance.supportedSearchType.includes(supportedSearchType) 10772b80a429S猫头猫 : true), 10782b80a429S猫头猫 ); 1079efb9da24S猫头猫} 1080efb9da24S猫头猫 10812b80a429S猫头猫function getSortedSearchablePlugins( 10822b80a429S猫头猫 supportedSearchType?: ICommon.SupportMediaType, 10832b80a429S猫头猫) { 10842b80a429S猫头猫 return getSearchablePlugins(supportedSearchType).sort((a, b) => 1085e08d37a3S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1086e08d37a3S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1087e08d37a3S猫头猫 0 1088e08d37a3S猫头猫 ? -1 1089e08d37a3S猫头猫 : 1, 1090e08d37a3S猫头猫 ); 1091e08d37a3S猫头猫} 1092e08d37a3S猫头猫 109315feccc1S猫头猫function getTopListsablePlugins() { 109415feccc1S猫头猫 return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists); 109515feccc1S猫头猫} 109615feccc1S猫头猫 109715feccc1S猫头猫function getSortedTopListsablePlugins() { 109815feccc1S猫头猫 return getTopListsablePlugins().sort((a, b) => 109915feccc1S猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 110015feccc1S猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 110115feccc1S猫头猫 0 110215feccc1S猫头猫 ? -1 110315feccc1S猫头猫 : 1, 110415feccc1S猫头猫 ); 110515feccc1S猫头猫} 110615feccc1S猫头猫 1107ceb900cdS猫头猫function getRecommendSheetablePlugins() { 1108ceb900cdS猫头猫 return plugins.filter( 1109ceb900cdS猫头猫 _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag, 1110ceb900cdS猫头猫 ); 1111ceb900cdS猫头猫} 1112ceb900cdS猫头猫 1113ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() { 1114ceb900cdS猫头猫 return getRecommendSheetablePlugins().sort((a, b) => 1115ceb900cdS猫头猫 (PluginMeta.getPluginMeta(a).order ?? Infinity) - 1116ceb900cdS猫头猫 (PluginMeta.getPluginMeta(b).order ?? Infinity) < 1117ceb900cdS猫头猫 0 1118ceb900cdS猫头猫 ? -1 1119ceb900cdS猫头猫 : 1, 1120ceb900cdS猫头猫 ); 1121ceb900cdS猫头猫} 1122ceb900cdS猫头猫 1123e08d37a3S猫头猫function useSortedPlugins() { 1124e08d37a3S猫头猫 const _plugins = pluginStateMapper.useMappedState(); 1125e08d37a3S猫头猫 const _pluginMetaAll = PluginMeta.usePluginMetaAll(); 1126e08d37a3S猫头猫 112734588741S猫头猫 const [sortedPlugins, setSortedPlugins] = useState( 112834588741S猫头猫 [..._plugins].sort((a, b) => 1129e08d37a3S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 1130e08d37a3S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 1131e08d37a3S猫头猫 0 1132e08d37a3S猫头猫 ? -1 1133e08d37a3S猫头猫 : 1, 113434588741S猫头猫 ), 1135e08d37a3S猫头猫 ); 113634588741S猫头猫 113734588741S猫头猫 useEffect(() => { 1138d4cd40d8S猫头猫 InteractionManager.runAfterInteractions(() => { 113934588741S猫头猫 setSortedPlugins( 114034588741S猫头猫 [..._plugins].sort((a, b) => 114134588741S猫头猫 (_pluginMetaAll[a.name]?.order ?? Infinity) - 114234588741S猫头猫 (_pluginMetaAll[b.name]?.order ?? Infinity) < 114334588741S猫头猫 0 114434588741S猫头猫 ? -1 114534588741S猫头猫 : 1, 114634588741S猫头猫 ), 114734588741S猫头猫 ); 1148d4cd40d8S猫头猫 }); 114934588741S猫头猫 }, [_plugins, _pluginMetaAll]); 115034588741S猫头猫 115134588741S猫头猫 return sortedPlugins; 1152e08d37a3S猫头猫} 1153e08d37a3S猫头猫 1154c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) { 1155c2b3a262S猫头猫 const target = plugins.find(it => it.hash === plugin.hash); 1156c2b3a262S猫头猫 if (target) { 1157c2b3a262S猫头猫 target.state = enabled ? 'enabled' : 'disabled'; 1158c2b3a262S猫头猫 plugins = [...plugins]; 1159c2b3a262S猫头猫 pluginStateMapper.notify(); 1160c2b3a262S猫头猫 PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled); 1161c2b3a262S猫头猫 } 1162c2b3a262S猫头猫} 1163c2b3a262S猫头猫 1164927dbe93S猫头猫const PluginManager = { 1165927dbe93S猫头猫 setup, 1166927dbe93S猫头猫 installPlugin, 116758992c6bS猫头猫 installPluginFromUrl, 116825c1bd29S猫头猫 updatePlugin, 1169927dbe93S猫头猫 uninstallPlugin, 1170927dbe93S猫头猫 getByMedia, 1171927dbe93S猫头猫 getByHash, 1172927dbe93S猫头猫 getByName, 1173927dbe93S猫头猫 getValidPlugins, 1174efb9da24S猫头猫 getSearchablePlugins, 1175e08d37a3S猫头猫 getSortedSearchablePlugins, 117615feccc1S猫头猫 getTopListsablePlugins, 1177ceb900cdS猫头猫 getSortedRecommendSheetablePlugins, 117815feccc1S猫头猫 getSortedTopListsablePlugins, 11795276aef9S猫头猫 usePlugins: pluginStateMapper.useMappedState, 1180e08d37a3S猫头猫 useSortedPlugins, 118108882a77S猫头猫 uninstallAllPlugins, 1182c2b3a262S猫头猫 setPluginEnabled, 11835276aef9S猫头猫}; 1184927dbe93S猫头猫 1185927dbe93S猫头猫export default PluginManager; 1186