xref: /MusicFree/src/core/pluginManager.ts (revision 92b6c95a091918b40c827f237dd20dd8319bc16c)
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';
46927dbe93S猫头猫
4761aca335S猫头猫axios.defaults.timeout = 2000;
48927dbe93S猫头猫
49927dbe93S猫头猫const sha256 = CryptoJs.SHA256;
50927dbe93S猫头猫
51cfa0fc07S猫头猫export enum PluginStateCode {
52927dbe93S猫头猫    /** 版本不匹配 */
53927dbe93S猫头猫    VersionNotMatch = 'VERSION NOT MATCH',
54927dbe93S猫头猫    /** 无法解析 */
55927dbe93S猫头猫    CannotParse = 'CANNOT PARSE',
56927dbe93S猫头猫}
57927dbe93S猫头猫
589c34d637S猫头猫const packages: Record<string, any> = {
599c34d637S猫头猫    cheerio,
609c34d637S猫头猫    'crypto-js': CryptoJs,
619c34d637S猫头猫    axios,
629c34d637S猫头猫    dayjs,
639c34d637S猫头猫    'big-integer': bigInt,
649c34d637S猫头猫    qs,
659c34d637S猫头猫    he,
663b3d6357S猫头猫    '@react-native-cookies/cookies': CookieManager,
679c34d637S猫头猫};
689c34d637S猫头猫
69b43683eaS猫头猫const _require = (packageName: string) => {
70b43683eaS猫头猫    let pkg = packages[packageName];
71b43683eaS猫头猫    pkg.default = pkg;
72b43683eaS猫头猫    return pkg;
73b43683eaS猫头猫};
749c34d637S猫头猫
7553f8cd8eS猫头猫const _consoleBind = function (
7653f8cd8eS猫头猫    method: 'log' | 'error' | 'info' | 'warn',
7753f8cd8eS猫头猫    ...args: any
7853f8cd8eS猫头猫) {
7953f8cd8eS猫头猫    const fn = console[method];
8053f8cd8eS猫头猫    if (fn) {
8153f8cd8eS猫头猫        fn(...args);
8253f8cd8eS猫头猫        devLog(method, ...args);
8353f8cd8eS猫头猫    }
8453f8cd8eS猫头猫};
8553f8cd8eS猫头猫
8653f8cd8eS猫头猫const _console = {
8753f8cd8eS猫头猫    log: _consoleBind.bind(null, 'log'),
8853f8cd8eS猫头猫    warn: _consoleBind.bind(null, 'warn'),
8953f8cd8eS猫头猫    info: _consoleBind.bind(null, 'info'),
9053f8cd8eS猫头猫    error: _consoleBind.bind(null, 'error'),
9153f8cd8eS猫头猫};
9253f8cd8eS猫头猫
93d5bfeb7eS猫头猫//#region 插件类
94927dbe93S猫头猫export class Plugin {
95927dbe93S猫头猫    /** 插件名 */
96927dbe93S猫头猫    public name: string;
97927dbe93S猫头猫    /** 插件的hash,作为唯一id */
98927dbe93S猫头猫    public hash: string;
99927dbe93S猫头猫    /** 插件状态:激活、关闭、错误 */
100927dbe93S猫头猫    public state: 'enabled' | 'disabled' | 'error';
101927dbe93S猫头猫    /** 插件支持的搜索类型 */
102927dbe93S猫头猫    public supportedSearchType?: string;
103927dbe93S猫头猫    /** 插件状态信息 */
104927dbe93S猫头猫    public stateCode?: PluginStateCode;
105927dbe93S猫头猫    /** 插件的实例 */
106927dbe93S猫头猫    public instance: IPlugin.IPluginInstance;
107927dbe93S猫头猫    /** 插件路径 */
108927dbe93S猫头猫    public path: string;
109927dbe93S猫头猫    /** 插件方法 */
110927dbe93S猫头猫    public methods: PluginMethods;
111e1f817d7S猫头猫    /** TODO 用户输入 */
112d5bfeb7eS猫头猫    public userEnv?: Record<string, string>;
113927dbe93S猫头猫
11474d0cf81S猫头猫    constructor(
11574d0cf81S猫头猫        funcCode: string | (() => IPlugin.IPluginInstance),
11674d0cf81S猫头猫        pluginPath: string,
11774d0cf81S猫头猫    ) {
118927dbe93S猫头猫        this.state = 'enabled';
119927dbe93S猫头猫        let _instance: IPlugin.IPluginInstance;
1203b3d6357S猫头猫        const _module: any = {exports: {}};
121927dbe93S猫头猫        try {
12274d0cf81S猫头猫            if (typeof funcCode === 'string') {
1234060c00aS猫头猫                // eslint-disable-next-line no-new-func
124927dbe93S猫头猫                _instance = Function(`
125927dbe93S猫头猫                    'use strict';
12653f8cd8eS猫头猫                    return function(require, __musicfree_require, module, exports, console) {
1279c34d637S猫头猫                        ${funcCode}
128927dbe93S猫头猫                    }
12953f8cd8eS猫头猫                `)()(_require, _require, _module, _module.exports, _console);
1303b3d6357S猫头猫                if (_module.exports.default) {
1313b3d6357S猫头猫                    _instance = _module.exports
1323b3d6357S猫头猫                        .default as IPlugin.IPluginInstance;
1333b3d6357S猫头猫                } else {
1349c34d637S猫头猫                    _instance = _module.exports as IPlugin.IPluginInstance;
1353b3d6357S猫头猫                }
13674d0cf81S猫头猫            } else {
13774d0cf81S猫头猫                _instance = funcCode();
13874d0cf81S猫头猫            }
139927dbe93S猫头猫            this.checkValid(_instance);
140927dbe93S猫头猫        } catch (e: any) {
141b43683eaS猫头猫            console.log(e);
142927dbe93S猫头猫            this.state = 'error';
143927dbe93S猫头猫            this.stateCode = PluginStateCode.CannotParse;
144927dbe93S猫头猫            if (e?.stateCode) {
145927dbe93S猫头猫                this.stateCode = e.stateCode;
146927dbe93S猫头猫            }
147927dbe93S猫头猫            errorLog(`${pluginPath}插件无法解析 `, {
148927dbe93S猫头猫                stateCode: this.stateCode,
149927dbe93S猫头猫                message: e?.message,
150927dbe93S猫头猫                stack: e?.stack,
151927dbe93S猫头猫            });
152927dbe93S猫头猫            _instance = e?.instance ?? {
153927dbe93S猫头猫                _path: '',
154927dbe93S猫头猫                platform: '',
155927dbe93S猫头猫                appVersion: '',
15620e6a092S猫头猫                async getMediaSource() {
157927dbe93S猫头猫                    return null;
158927dbe93S猫头猫                },
159927dbe93S猫头猫                async search() {
160927dbe93S猫头猫                    return {};
161927dbe93S猫头猫                },
162927dbe93S猫头猫                async getAlbumInfo() {
163927dbe93S猫头猫                    return null;
164927dbe93S猫头猫                },
165927dbe93S猫头猫            };
166927dbe93S猫头猫        }
167927dbe93S猫头猫        this.instance = _instance;
168927dbe93S猫头猫        this.path = pluginPath;
169927dbe93S猫头猫        this.name = _instance.platform;
170ab8941d9S猫头猫        if (
171ab8941d9S猫头猫            this.instance.platform === '' ||
172ab8941d9S猫头猫            this.instance.platform === undefined
173ab8941d9S猫头猫        ) {
174927dbe93S猫头猫            this.hash = '';
175927dbe93S猫头猫        } else {
17674d0cf81S猫头猫            if (typeof funcCode === 'string') {
177927dbe93S猫头猫                this.hash = sha256(funcCode).toString();
17874d0cf81S猫头猫            } else {
17974d0cf81S猫头猫                this.hash = sha256(funcCode.toString()).toString();
18074d0cf81S猫头猫            }
181927dbe93S猫头猫        }
182927dbe93S猫头猫
183927dbe93S猫头猫        // 放在最后
184927dbe93S猫头猫        this.methods = new PluginMethods(this);
185927dbe93S猫头猫    }
186927dbe93S猫头猫
187927dbe93S猫头猫    private checkValid(_instance: IPlugin.IPluginInstance) {
188927dbe93S猫头猫        /** 版本号校验 */
189927dbe93S猫头猫        if (
190927dbe93S猫头猫            _instance.appVersion &&
191927dbe93S猫头猫            !satisfies(DeviceInfo.getVersion(), _instance.appVersion)
192927dbe93S猫头猫        ) {
193927dbe93S猫头猫            throw {
194927dbe93S猫头猫                instance: _instance,
195927dbe93S猫头猫                stateCode: PluginStateCode.VersionNotMatch,
196927dbe93S猫头猫            };
197927dbe93S猫头猫        }
198927dbe93S猫头猫        return true;
199927dbe93S猫头猫    }
200927dbe93S猫头猫}
201d5bfeb7eS猫头猫//#endregion
202927dbe93S猫头猫
203d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用
204927dbe93S猫头猫/** 有缓存等信息 */
205927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods {
206927dbe93S猫头猫    private plugin;
207927dbe93S猫头猫    constructor(plugin: Plugin) {
208927dbe93S猫头猫        this.plugin = plugin;
209927dbe93S猫头猫    }
210927dbe93S猫头猫    /** 搜索 */
211927dbe93S猫头猫    async search<T extends ICommon.SupportMediaType>(
212927dbe93S猫头猫        query: string,
213927dbe93S猫头猫        page: number,
214927dbe93S猫头猫        type: T,
215927dbe93S猫头猫    ): Promise<IPlugin.ISearchResult<T>> {
216927dbe93S猫头猫        if (!this.plugin.instance.search) {
217927dbe93S猫头猫            return {
218927dbe93S猫头猫                isEnd: true,
219927dbe93S猫头猫                data: [],
220927dbe93S猫头猫            };
221927dbe93S猫头猫        }
222927dbe93S猫头猫
2234060c00aS猫头猫        const result =
2244060c00aS猫头猫            (await this.plugin.instance.search(query, page, type)) ?? {};
225927dbe93S猫头猫        if (Array.isArray(result.data)) {
226927dbe93S猫头猫            result.data.forEach(_ => {
227927dbe93S猫头猫                resetMediaItem(_, this.plugin.name);
228927dbe93S猫头猫            });
229927dbe93S猫头猫            return {
230927dbe93S猫头猫                isEnd: result.isEnd ?? true,
231927dbe93S猫头猫                data: result.data,
232927dbe93S猫头猫            };
233927dbe93S猫头猫        }
234927dbe93S猫头猫        return {
235927dbe93S猫头猫            isEnd: true,
236927dbe93S猫头猫            data: [],
237927dbe93S猫头猫        };
238927dbe93S猫头猫    }
239927dbe93S猫头猫
240927dbe93S猫头猫    /** 获取真实源 */
24120e6a092S猫头猫    async getMediaSource(
242927dbe93S猫头猫        musicItem: IMusic.IMusicItemBase,
243abaede57S猫头猫        quality: IMusic.IQualityKey = 'standard',
244927dbe93S猫头猫        retryCount = 1,
245dc160d50S猫头猫        notUpdateCache = false,
246192ae2b0S猫头猫    ): Promise<IPlugin.IMediaSourceResult | null> {
247927dbe93S猫头猫        // 1. 本地搜索 其实直接读mediameta就好了
248927dbe93S猫头猫        const localPath =
2490e4173cdS猫头猫            getInternalData<string>(musicItem, InternalDataType.LOCALPATH) ??
2500e4173cdS猫头猫            getInternalData<string>(
2510e4173cdS猫头猫                LocalMusicSheet.isLocalMusic(musicItem),
2520e4173cdS猫头猫                InternalDataType.LOCALPATH,
2530e4173cdS猫头猫            );
2540e4173cdS猫头猫        if (localPath && (await FileSystem.exists(localPath))) {
2550e4173cdS猫头猫            trace('本地播放', localPath);
256927dbe93S猫头猫            return {
257927dbe93S猫头猫                url: localPath,
258927dbe93S猫头猫            };
259927dbe93S猫头猫        }
2607993f90eS猫头猫        if (musicItem.platform === localPluginPlatform) {
261f5935920S猫头猫            throw new Error('本地音乐不存在');
262f5935920S猫头猫        }
263927dbe93S猫头猫        // 2. 缓存播放
264927dbe93S猫头猫        const mediaCache = Cache.get(musicItem);
265985f8e75S猫头猫        const pluginCacheControl =
266985f8e75S猫头猫            this.plugin.instance.cacheControl ?? 'no-cache';
267cfa0fc07S猫头猫        if (
268cfa0fc07S猫头猫            mediaCache &&
269abaede57S猫头猫            mediaCache?.qualities?.[quality]?.url &&
27048f4b873S猫头猫            (pluginCacheControl === CacheControl.Cache ||
27148f4b873S猫头猫                (pluginCacheControl === CacheControl.NoCache &&
272ef714860S猫头猫                    Network.isOffline()))
273cfa0fc07S猫头猫        ) {
2745276aef9S猫头猫            trace('播放', '缓存播放');
275abaede57S猫头猫            const qualityInfo = mediaCache.qualities[quality];
276927dbe93S猫头猫            return {
277abaede57S猫头猫                url: qualityInfo.url,
278927dbe93S猫头猫                headers: mediaCache.headers,
2794060c00aS猫头猫                userAgent:
2804060c00aS猫头猫                    mediaCache.userAgent ?? mediaCache.headers?.['user-agent'],
281927dbe93S猫头猫            };
282927dbe93S猫头猫        }
283927dbe93S猫头猫        // 3. 插件解析
28420e6a092S猫头猫        if (!this.plugin.instance.getMediaSource) {
285abaede57S猫头猫            return {url: musicItem?.qualities?.[quality]?.url ?? musicItem.url};
286927dbe93S猫头猫        }
287927dbe93S猫头猫        try {
288abaede57S猫头猫            const {url, headers} = (await this.plugin.instance.getMediaSource(
289abaede57S猫头猫                musicItem,
290abaede57S猫头猫                quality,
291abaede57S猫头猫            )) ?? {url: musicItem?.qualities?.[quality]?.url};
292927dbe93S猫头猫            if (!url) {
293a28eac61S猫头猫                throw new Error('NOT RETRY');
294927dbe93S猫头猫            }
2955276aef9S猫头猫            trace('播放', '插件播放');
296927dbe93S猫头猫            const result = {
297927dbe93S猫头猫                url,
298927dbe93S猫头猫                headers,
299927dbe93S猫头猫                userAgent: headers?.['user-agent'],
300cfa0fc07S猫头猫            } as IPlugin.IMediaSourceResult;
301927dbe93S猫头猫
302dc160d50S猫头猫            if (
303dc160d50S猫头猫                pluginCacheControl !== CacheControl.NoStore &&
304dc160d50S猫头猫                !notUpdateCache
305dc160d50S猫头猫            ) {
306abaede57S猫头猫                Cache.update(musicItem, [
307abaede57S猫头猫                    ['headers', result.headers],
308abaede57S猫头猫                    ['userAgent', result.userAgent],
309abaede57S猫头猫                    [`qualities.${quality}.url`, url],
310abaede57S猫头猫                ]);
311752ffc5aS猫头猫            }
312cfa0fc07S猫头猫
313927dbe93S猫头猫            return result;
314927dbe93S猫头猫        } catch (e: any) {
315a28eac61S猫头猫            if (retryCount > 0 && e?.message !== 'NOT RETRY') {
316927dbe93S猫头猫                await delay(150);
317abaede57S猫头猫                return this.getMediaSource(musicItem, quality, --retryCount);
318927dbe93S猫头猫            }
319927dbe93S猫头猫            errorLog('获取真实源失败', e?.message);
320ea6d708fS猫头猫            devLog('error', '获取真实源失败', e, e?.message);
321192ae2b0S猫头猫            return null;
322927dbe93S猫头猫        }
323927dbe93S猫头猫    }
324927dbe93S猫头猫
325927dbe93S猫头猫    /** 获取音乐详情 */
326927dbe93S猫头猫    async getMusicInfo(
327927dbe93S猫头猫        musicItem: ICommon.IMediaBase,
32874d0cf81S猫头猫    ): Promise<Partial<IMusic.IMusicItem> | null> {
329927dbe93S猫头猫        if (!this.plugin.instance.getMusicInfo) {
330d704daedS猫头猫            return null;
331927dbe93S猫头猫        }
33274d0cf81S猫头猫        try {
333927dbe93S猫头猫            return (
334927dbe93S猫头猫                this.plugin.instance.getMusicInfo(
3357993f90eS猫头猫                    resetMediaItem(musicItem, undefined, true),
336d704daedS猫头猫                ) ?? null
337927dbe93S猫头猫            );
338ea6d708fS猫头猫        } catch (e: any) {
339ea6d708fS猫头猫            devLog('error', '获取音乐详情失败', e, e?.message);
340d704daedS猫头猫            return null;
34174d0cf81S猫头猫        }
342927dbe93S猫头猫    }
343927dbe93S猫头猫
344927dbe93S猫头猫    /** 获取歌词 */
345927dbe93S猫头猫    async getLyric(
346927dbe93S猫头猫        musicItem: IMusic.IMusicItemBase,
347927dbe93S猫头猫        from?: IMusic.IMusicItemBase,
348927dbe93S猫头猫    ): Promise<ILyric.ILyricSource | null> {
349927dbe93S猫头猫        // 1.额外存储的meta信息
350927dbe93S猫头猫        const meta = MediaMeta.get(musicItem);
351927dbe93S猫头猫        if (meta && meta.associatedLrc) {
352927dbe93S猫头猫            // 有关联歌词
353927dbe93S猫头猫            if (
354927dbe93S猫头猫                isSameMediaItem(musicItem, from) ||
355927dbe93S猫头猫                isSameMediaItem(meta.associatedLrc, musicItem)
356927dbe93S猫头猫            ) {
357927dbe93S猫头猫                // 形成环路,断开当前的环
358927dbe93S猫头猫                await MediaMeta.update(musicItem, {
359927dbe93S猫头猫                    associatedLrc: undefined,
360927dbe93S猫头猫                });
361927dbe93S猫头猫                // 无歌词
362927dbe93S猫头猫                return null;
363927dbe93S猫头猫            }
364927dbe93S猫头猫            // 获取关联歌词
3657a91f04fS猫头猫            const associatedMeta = MediaMeta.get(meta.associatedLrc) ?? {};
3664060c00aS猫头猫            const result = await this.getLyric(
3677a91f04fS猫头猫                {...meta.associatedLrc, ...associatedMeta},
3684060c00aS猫头猫                from ?? musicItem,
3694060c00aS猫头猫            );
370927dbe93S猫头猫            if (result) {
371927dbe93S猫头猫                // 如果有关联歌词,就返回关联歌词,深度优先
372927dbe93S猫头猫                return result;
373927dbe93S猫头猫            }
374927dbe93S猫头猫        }
375927dbe93S猫头猫        const cache = Cache.get(musicItem);
376927dbe93S猫头猫        let rawLrc = meta?.rawLrc || musicItem.rawLrc || cache?.rawLrc;
377927dbe93S猫头猫        let lrcUrl = meta?.lrc || musicItem.lrc || cache?.lrc;
378927dbe93S猫头猫        // 如果存在文本
379927dbe93S猫头猫        if (rawLrc) {
380927dbe93S猫头猫            return {
381927dbe93S猫头猫                rawLrc,
382927dbe93S猫头猫                lrc: lrcUrl,
383927dbe93S猫头猫            };
384927dbe93S猫头猫        }
385927dbe93S猫头猫        // 2.本地缓存
386927dbe93S猫头猫        const localLrc =
3870e4173cdS猫头猫            meta?.[internalSerializeKey]?.local?.localLrc ||
3880e4173cdS猫头猫            cache?.[internalSerializeKey]?.local?.localLrc;
389927dbe93S猫头猫        if (localLrc && (await exists(localLrc))) {
390927dbe93S猫头猫            rawLrc = await readFile(localLrc, 'utf8');
391927dbe93S猫头猫            return {
392927dbe93S猫头猫                rawLrc,
393927dbe93S猫头猫                lrc: lrcUrl,
394927dbe93S猫头猫            };
395927dbe93S猫头猫        }
396927dbe93S猫头猫        // 3.优先使用url
397927dbe93S猫头猫        if (lrcUrl) {
398927dbe93S猫头猫            try {
399927dbe93S猫头猫                // 需要超时时间 axios timeout 但是没生效
40061aca335S猫头猫                rawLrc = (await axios.get(lrcUrl, {timeout: 2000})).data;
401927dbe93S猫头猫                return {
402927dbe93S猫头猫                    rawLrc,
403927dbe93S猫头猫                    lrc: lrcUrl,
404927dbe93S猫头猫                };
405927dbe93S猫头猫            } catch {
406927dbe93S猫头猫                lrcUrl = undefined;
407927dbe93S猫头猫            }
408927dbe93S猫头猫        }
409927dbe93S猫头猫        // 4. 如果地址失效
410927dbe93S猫头猫        if (!lrcUrl) {
411927dbe93S猫头猫            // 插件获得url
412927dbe93S猫头猫            try {
4137a91f04fS猫头猫                let lrcSource;
4147a91f04fS猫头猫                if (from) {
4157a91f04fS猫头猫                    lrcSource = await PluginManager.getByMedia(
4167a91f04fS猫头猫                        musicItem,
4177a91f04fS猫头猫                    )?.instance?.getLyric?.(
418927dbe93S猫头猫                        resetMediaItem(musicItem, undefined, true),
419927dbe93S猫头猫                    );
4207a91f04fS猫头猫                } else {
4217a91f04fS猫头猫                    lrcSource = await this.plugin.instance?.getLyric?.(
4227a91f04fS猫头猫                        resetMediaItem(musicItem, undefined, true),
4237a91f04fS猫头猫                    );
4247a91f04fS猫头猫                }
4257a91f04fS猫头猫
426927dbe93S猫头猫                rawLrc = lrcSource?.rawLrc;
427927dbe93S猫头猫                lrcUrl = lrcSource?.lrc;
428927dbe93S猫头猫            } catch (e: any) {
429927dbe93S猫头猫                trace('插件获取歌词失败', e?.message, 'error');
430ea6d708fS猫头猫                devLog('error', '插件获取歌词失败', e, e?.message);
431927dbe93S猫头猫            }
432927dbe93S猫头猫        }
433927dbe93S猫头猫        // 5. 最后一次请求
434927dbe93S猫头猫        if (rawLrc || lrcUrl) {
435927dbe93S猫头猫            const filename = `${pathConst.lrcCachePath}${nanoid()}.lrc`;
436927dbe93S猫头猫            if (lrcUrl) {
437927dbe93S猫头猫                try {
43861aca335S猫头猫                    rawLrc = (await axios.get(lrcUrl, {timeout: 2000})).data;
439927dbe93S猫头猫                } catch {}
440927dbe93S猫头猫            }
441927dbe93S猫头猫            if (rawLrc) {
442927dbe93S猫头猫                await writeFile(filename, rawLrc, 'utf8');
443927dbe93S猫头猫                // 写入缓存
444927dbe93S猫头猫                Cache.update(musicItem, [
4450e4173cdS猫头猫                    [`${internalSerializeKey}.local.localLrc`, filename],
446927dbe93S猫头猫                ]);
447927dbe93S猫头猫                // 如果有meta
448927dbe93S猫头猫                if (meta) {
449927dbe93S猫头猫                    MediaMeta.update(musicItem, [
4500e4173cdS猫头猫                        [`${internalSerializeKey}.local.localLrc`, filename],
451927dbe93S猫头猫                    ]);
452927dbe93S猫头猫                }
453927dbe93S猫头猫                return {
454927dbe93S猫头猫                    rawLrc,
455927dbe93S猫头猫                    lrc: lrcUrl,
456927dbe93S猫头猫                };
457927dbe93S猫头猫            }
458927dbe93S猫头猫        }
4593a6f67b1S猫头猫        // 6. 如果是本地文件
4603a6f67b1S猫头猫        const isDownloaded = LocalMusicSheet.isLocalMusic(musicItem);
4613a6f67b1S猫头猫        if (musicItem.platform !== localPluginPlatform && isDownloaded) {
4623a6f67b1S猫头猫            const res = await localFilePlugin.instance!.getLyric!(isDownloaded);
4633a6f67b1S猫头猫            if (res) {
4643a6f67b1S猫头猫                return res;
4653a6f67b1S猫头猫            }
4663a6f67b1S猫头猫        }
467ea6d708fS猫头猫        devLog('warn', '无歌词');
468927dbe93S猫头猫
469927dbe93S猫头猫        return null;
470927dbe93S猫头猫    }
471927dbe93S猫头猫
472927dbe93S猫头猫    /** 获取歌词文本 */
473927dbe93S猫头猫    async getLyricText(
474927dbe93S猫头猫        musicItem: IMusic.IMusicItem,
475927dbe93S猫头猫    ): Promise<string | undefined> {
476927dbe93S猫头猫        return (await this.getLyric(musicItem))?.rawLrc;
477927dbe93S猫头猫    }
478927dbe93S猫头猫
479927dbe93S猫头猫    /** 获取专辑信息 */
480927dbe93S猫头猫    async getAlbumInfo(
481927dbe93S猫头猫        albumItem: IAlbum.IAlbumItemBase,
482f9afcc0dS猫头猫        page: number = 1,
483f9afcc0dS猫头猫    ): Promise<IPlugin.IAlbumInfoResult | null> {
484927dbe93S猫头猫        if (!this.plugin.instance.getAlbumInfo) {
485f9afcc0dS猫头猫            return {
486f9afcc0dS猫头猫                albumItem,
487f9afcc0dS猫头猫                musicList: albumItem?.musicList ?? [],
488f9afcc0dS猫头猫                isEnd: true,
489f9afcc0dS猫头猫            };
490927dbe93S猫头猫        }
491927dbe93S猫头猫        try {
492927dbe93S猫头猫            const result = await this.plugin.instance.getAlbumInfo(
493927dbe93S猫头猫                resetMediaItem(albumItem, undefined, true),
494f9afcc0dS猫头猫                page,
495927dbe93S猫头猫            );
4965276aef9S猫头猫            if (!result) {
4975276aef9S猫头猫                throw new Error();
4985276aef9S猫头猫            }
499927dbe93S猫头猫            result?.musicList?.forEach(_ => {
500927dbe93S猫头猫                resetMediaItem(_, this.plugin.name);
50196744680S猫头猫                _.album = albumItem.title;
502927dbe93S猫头猫            });
5035276aef9S猫头猫
504f9afcc0dS猫头猫            if (page <= 1) {
505f9afcc0dS猫头猫                // 合并信息
506f9afcc0dS猫头猫                return {
507f9afcc0dS猫头猫                    albumItem: {...albumItem, ...(result?.albumItem ?? {})},
508f9afcc0dS猫头猫                    isEnd: result.isEnd === false ? false : true,
509f9afcc0dS猫头猫                    musicList: result.musicList,
510f9afcc0dS猫头猫                };
511f9afcc0dS猫头猫            } else {
512f9afcc0dS猫头猫                return {
513f9afcc0dS猫头猫                    isEnd: result.isEnd === false ? false : true,
514f9afcc0dS猫头猫                    musicList: result.musicList,
515f9afcc0dS猫头猫                };
516f9afcc0dS猫头猫            }
5174394410dS猫头猫        } catch (e: any) {
5184394410dS猫头猫            trace('获取专辑信息失败', e?.message);
519ea6d708fS猫头猫            devLog('error', '获取专辑信息失败', e, e?.message);
520ea6d708fS猫头猫
521f9afcc0dS猫头猫            return null;
522927dbe93S猫头猫        }
523927dbe93S猫头猫    }
524927dbe93S猫头猫
525927dbe93S猫头猫    /** 查询作者信息 */
526efb9da24S猫头猫    async getArtistWorks<T extends IArtist.ArtistMediaType>(
527927dbe93S猫头猫        artistItem: IArtist.IArtistItem,
528927dbe93S猫头猫        page: number,
529927dbe93S猫头猫        type: T,
530927dbe93S猫头猫    ): Promise<IPlugin.ISearchResult<T>> {
531efb9da24S猫头猫        if (!this.plugin.instance.getArtistWorks) {
532927dbe93S猫头猫            return {
533927dbe93S猫头猫                isEnd: true,
534927dbe93S猫头猫                data: [],
535927dbe93S猫头猫            };
536927dbe93S猫头猫        }
537927dbe93S猫头猫        try {
538efb9da24S猫头猫            const result = await this.plugin.instance.getArtistWorks(
539927dbe93S猫头猫                artistItem,
540927dbe93S猫头猫                page,
541927dbe93S猫头猫                type,
542927dbe93S猫头猫            );
543927dbe93S猫头猫            if (!result.data) {
544927dbe93S猫头猫                return {
545927dbe93S猫头猫                    isEnd: true,
546927dbe93S猫头猫                    data: [],
547927dbe93S猫头猫                };
548927dbe93S猫头猫            }
549927dbe93S猫头猫            result.data?.forEach(_ => resetMediaItem(_, this.plugin.name));
550927dbe93S猫头猫            return {
551927dbe93S猫头猫                isEnd: result.isEnd ?? true,
552927dbe93S猫头猫                data: result.data,
553927dbe93S猫头猫            };
5544394410dS猫头猫        } catch (e: any) {
5554394410dS猫头猫            trace('查询作者信息失败', e?.message);
556ea6d708fS猫头猫            devLog('error', '查询作者信息失败', e, e?.message);
557ea6d708fS猫头猫
558927dbe93S猫头猫            throw e;
559927dbe93S猫头猫        }
560927dbe93S猫头猫    }
56108380090S猫头猫
56208380090S猫头猫    /** 导入歌单 */
56308380090S猫头猫    async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> {
56408380090S猫头猫        try {
56508380090S猫头猫            const result =
56608380090S猫头猫                (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? [];
56708380090S猫头猫            result.forEach(_ => resetMediaItem(_, this.plugin.name));
56808380090S猫头猫            return result;
569ea6d708fS猫头猫        } catch (e: any) {
5700e4173cdS猫头猫            console.log(e);
571ea6d708fS猫头猫            devLog('error', '导入歌单失败', e, e?.message);
572ea6d708fS猫头猫
57308380090S猫头猫            return [];
57408380090S猫头猫        }
57508380090S猫头猫    }
5764d9d3c4cS猫头猫    /** 导入单曲 */
5774d9d3c4cS猫头猫    async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> {
5784d9d3c4cS猫头猫        try {
5794d9d3c4cS猫头猫            const result = await this.plugin.instance?.importMusicItem?.(
5804d9d3c4cS猫头猫                urlLike,
5814d9d3c4cS猫头猫            );
5824d9d3c4cS猫头猫            if (!result) {
5834d9d3c4cS猫头猫                throw new Error();
5844d9d3c4cS猫头猫            }
5854d9d3c4cS猫头猫            resetMediaItem(result, this.plugin.name);
5864d9d3c4cS猫头猫            return result;
587ea6d708fS猫头猫        } catch (e: any) {
588ea6d708fS猫头猫            devLog('error', '导入单曲失败', e, e?.message);
589ea6d708fS猫头猫
5904d9d3c4cS猫头猫            return null;
5914d9d3c4cS猫头猫        }
5924d9d3c4cS猫头猫    }
593d52aa40eS猫头猫    /** 获取榜单 */
594*92b6c95aS猫头猫    async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> {
595d52aa40eS猫头猫        try {
596d52aa40eS猫头猫            const result = await this.plugin.instance?.getTopLists?.();
597d52aa40eS猫头猫            if (!result) {
598d52aa40eS猫头猫                throw new Error();
599d52aa40eS猫头猫            }
600d52aa40eS猫头猫            return result;
601d52aa40eS猫头猫        } catch (e: any) {
602d52aa40eS猫头猫            devLog('error', '获取榜单失败', e, e?.message);
603d52aa40eS猫头猫            return [];
604d52aa40eS猫头猫        }
605d52aa40eS猫头猫    }
606d52aa40eS猫头猫    /** 获取榜单详情 */
607d52aa40eS猫头猫    async getTopListDetail(
608*92b6c95aS猫头猫        topListItem: IMusic.IMusicSheetItemBase,
609*92b6c95aS猫头猫    ): Promise<ICommon.WithMusicList<IMusic.IMusicSheetItemBase>> {
610d52aa40eS猫头猫        try {
611d52aa40eS猫头猫            const result = await this.plugin.instance?.getTopListDetail?.(
612d52aa40eS猫头猫                topListItem,
613d52aa40eS猫头猫            );
614d52aa40eS猫头猫            if (!result) {
615d52aa40eS猫头猫                throw new Error();
616d52aa40eS猫头猫            }
617d384662fS猫头猫            if (result.musicList) {
618d384662fS猫头猫                result.musicList.forEach(_ =>
619d384662fS猫头猫                    resetMediaItem(_, this.plugin.name),
620d384662fS猫头猫                );
621d384662fS猫头猫            }
622d52aa40eS猫头猫            return result;
623d52aa40eS猫头猫        } catch (e: any) {
624d52aa40eS猫头猫            devLog('error', '获取榜单详情失败', e, e?.message);
625d52aa40eS猫头猫            return {
626d52aa40eS猫头猫                ...topListItem,
627d52aa40eS猫头猫                musicList: [],
628d52aa40eS猫头猫            };
629d52aa40eS猫头猫        }
630d52aa40eS猫头猫    }
631927dbe93S猫头猫}
632d5bfeb7eS猫头猫//#endregion
6331a5528a0S猫头猫
634927dbe93S猫头猫let plugins: Array<Plugin> = [];
635927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins);
63674d0cf81S猫头猫
637d5bfeb7eS猫头猫//#region 本地音乐插件
63874d0cf81S猫头猫/** 本地插件 */
63974d0cf81S猫头猫const localFilePlugin = new Plugin(function () {
6400e4173cdS猫头猫    return {
641d5bfeb7eS猫头猫        platform: localPluginPlatform,
64274d0cf81S猫头猫        _path: '',
64374d0cf81S猫头猫        async getMusicInfo(musicBase) {
64474d0cf81S猫头猫            const localPath = getInternalData<string>(
64574d0cf81S猫头猫                musicBase,
64674d0cf81S猫头猫                InternalDataType.LOCALPATH,
6470e4173cdS猫头猫            );
64874d0cf81S猫头猫            if (localPath) {
64974d0cf81S猫头猫                const coverImg = await Mp3Util.getMediaCoverImg(localPath);
65074d0cf81S猫头猫                return {
65174d0cf81S猫头猫                    artwork: coverImg,
65274d0cf81S猫头猫                };
65374d0cf81S猫头猫            }
65474d0cf81S猫头猫            return null;
65574d0cf81S猫头猫        },
6567993f90eS猫头猫        async getLyric(musicBase) {
6577993f90eS猫头猫            const localPath = getInternalData<string>(
6587993f90eS猫头猫                musicBase,
6597993f90eS猫头猫                InternalDataType.LOCALPATH,
6607993f90eS猫头猫            );
6613a6f67b1S猫头猫            let rawLrc: string | null = null;
6627993f90eS猫头猫            if (localPath) {
6633a6f67b1S猫头猫                // 读取内嵌歌词
6643a6f67b1S猫头猫                try {
6653a6f67b1S猫头猫                    rawLrc = await Mp3Util.getLyric(localPath);
6663a6f67b1S猫头猫                } catch (e) {
6673a6f67b1S猫头猫                    console.log('e', e);
6687993f90eS猫头猫                }
6693a6f67b1S猫头猫                if (!rawLrc) {
6703a6f67b1S猫头猫                    // 读取配置歌词
6713a6f67b1S猫头猫                    const lastDot = localPath.lastIndexOf('.');
6723a6f67b1S猫头猫                    const lrcPath = localPath.slice(0, lastDot) + '.lrc';
6733a6f67b1S猫头猫
6743a6f67b1S猫头猫                    try {
6753a6f67b1S猫头猫                        if (await exists(lrcPath)) {
6763a6f67b1S猫头猫                            rawLrc = await readFile(lrcPath, 'utf8');
6773a6f67b1S猫头猫                        }
6783a6f67b1S猫头猫                    } catch {}
6793a6f67b1S猫头猫                }
6803a6f67b1S猫头猫            }
6813a6f67b1S猫头猫
6823a6f67b1S猫头猫            return rawLrc
6833a6f67b1S猫头猫                ? {
6843a6f67b1S猫头猫                      rawLrc,
6853a6f67b1S猫头猫                  }
6863a6f67b1S猫头猫                : null;
6877993f90eS猫头猫        },
68874d0cf81S猫头猫    };
68974d0cf81S猫头猫}, '');
6907993f90eS猫头猫localFilePlugin.hash = localPluginHash;
691927dbe93S猫头猫
692d5bfeb7eS猫头猫//#endregion
693d5bfeb7eS猫头猫
694927dbe93S猫头猫async function setup() {
695927dbe93S猫头猫    const _plugins: Array<Plugin> = [];
696927dbe93S猫头猫    try {
697927dbe93S猫头猫        // 加载插件
698927dbe93S猫头猫        const pluginsPaths = await readDir(pathConst.pluginPath);
699927dbe93S猫头猫        for (let i = 0; i < pluginsPaths.length; ++i) {
700927dbe93S猫头猫            const _pluginUrl = pluginsPaths[i];
7011e263108S猫头猫            trace('初始化插件', _pluginUrl);
7021e263108S猫头猫            if (
7031e263108S猫头猫                _pluginUrl.isFile() &&
7041e263108S猫头猫                (_pluginUrl.name?.endsWith?.('.js') ||
7051e263108S猫头猫                    _pluginUrl.path?.endsWith?.('.js'))
7061e263108S猫头猫            ) {
707927dbe93S猫头猫                const funcCode = await readFile(_pluginUrl.path, 'utf8');
708927dbe93S猫头猫                const plugin = new Plugin(funcCode, _pluginUrl.path);
7094060c00aS猫头猫                const _pluginIndex = _plugins.findIndex(
7104060c00aS猫头猫                    p => p.hash === plugin.hash,
7114060c00aS猫头猫                );
712927dbe93S猫头猫                if (_pluginIndex !== -1) {
713927dbe93S猫头猫                    // 重复插件,直接忽略
714927dbe93S猫头猫                    return;
715927dbe93S猫头猫                }
716927dbe93S猫头猫                plugin.hash !== '' && _plugins.push(plugin);
717927dbe93S猫头猫            }
718927dbe93S猫头猫        }
719927dbe93S猫头猫
720927dbe93S猫头猫        plugins = _plugins;
721927dbe93S猫头猫        pluginStateMapper.notify();
722e08d37a3S猫头猫        /** 初始化meta信息 */
723e08d37a3S猫头猫        PluginMeta.setupMeta(plugins.map(_ => _.name));
724927dbe93S猫头猫    } catch (e: any) {
7254060c00aS猫头猫        ToastAndroid.show(
7264060c00aS猫头猫            `插件初始化失败:${e?.message ?? e}`,
7274060c00aS猫头猫            ToastAndroid.LONG,
7284060c00aS猫头猫        );
7291a5528a0S猫头猫        errorLog('插件初始化失败', e?.message);
730927dbe93S猫头猫        throw e;
731927dbe93S猫头猫    }
732927dbe93S猫头猫}
733927dbe93S猫头猫
734927dbe93S猫头猫// 安装插件
735927dbe93S猫头猫async function installPlugin(pluginPath: string) {
73622c09412S猫头猫    // if (pluginPath.endsWith('.js')) {
737927dbe93S猫头猫    const funcCode = await readFile(pluginPath, 'utf8');
738927dbe93S猫头猫    const plugin = new Plugin(funcCode, pluginPath);
739927dbe93S猫头猫    const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
740927dbe93S猫头猫    if (_pluginIndex !== -1) {
7414d9d3c4cS猫头猫        throw new Error('插件已安装');
742927dbe93S猫头猫    }
743927dbe93S猫头猫    if (plugin.hash !== '') {
744927dbe93S猫头猫        const fn = nanoid();
745927dbe93S猫头猫        const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
746927dbe93S猫头猫        await copyFile(pluginPath, _pluginPath);
747927dbe93S猫头猫        plugin.path = _pluginPath;
748927dbe93S猫头猫        plugins = plugins.concat(plugin);
749927dbe93S猫头猫        pluginStateMapper.notify();
7504d9d3c4cS猫头猫        return;
751927dbe93S猫头猫    }
7524d9d3c4cS猫头猫    throw new Error('插件无法解析');
75322c09412S猫头猫    // }
75422c09412S猫头猫    // throw new Error('插件不存在');
755927dbe93S猫头猫}
756927dbe93S猫头猫
75758992c6bS猫头猫async function installPluginFromUrl(url: string) {
75858992c6bS猫头猫    try {
75958992c6bS猫头猫        const funcCode = (await axios.get(url)).data;
76058992c6bS猫头猫        if (funcCode) {
76158992c6bS猫头猫            const plugin = new Plugin(funcCode, '');
76258992c6bS猫头猫            const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
76358992c6bS猫头猫            if (_pluginIndex !== -1) {
7648b7ddca8S猫头猫                // 静默忽略
7658b7ddca8S猫头猫                return;
76658992c6bS猫头猫            }
76725c1bd29S猫头猫            const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
76825c1bd29S猫头猫            if (oldVersionPlugin) {
76925c1bd29S猫头猫                if (
77025c1bd29S猫头猫                    compare(
77125c1bd29S猫头猫                        oldVersionPlugin.instance.version ?? '',
77225c1bd29S猫头猫                        plugin.instance.version ?? '',
77325c1bd29S猫头猫                        '>',
77425c1bd29S猫头猫                    )
77525c1bd29S猫头猫                ) {
77625c1bd29S猫头猫                    throw new Error('已安装更新版本的插件');
77725c1bd29S猫头猫                }
77825c1bd29S猫头猫            }
77925c1bd29S猫头猫
78058992c6bS猫头猫            if (plugin.hash !== '') {
78158992c6bS猫头猫                const fn = nanoid();
78258992c6bS猫头猫                const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
78358992c6bS猫头猫                await writeFile(_pluginPath, funcCode, 'utf8');
78458992c6bS猫头猫                plugin.path = _pluginPath;
78558992c6bS猫头猫                plugins = plugins.concat(plugin);
78625c1bd29S猫头猫                if (oldVersionPlugin) {
78725c1bd29S猫头猫                    plugins = plugins.filter(
78825c1bd29S猫头猫                        _ => _.hash !== oldVersionPlugin.hash,
78925c1bd29S猫头猫                    );
79025c1bd29S猫头猫                    try {
79125c1bd29S猫头猫                        await unlink(oldVersionPlugin.path);
79225c1bd29S猫头猫                    } catch {}
79325c1bd29S猫头猫                }
79458992c6bS猫头猫                pluginStateMapper.notify();
79558992c6bS猫头猫                return;
79658992c6bS猫头猫            }
79774acbfc0S猫头猫            throw new Error('插件无法解析!');
79858992c6bS猫头猫        }
79925c1bd29S猫头猫    } catch (e: any) {
800ea6d708fS猫头猫        devLog('error', 'URL安装插件失败', e, e?.message);
80158992c6bS猫头猫        errorLog('URL安装插件失败', e);
80225c1bd29S猫头猫        throw new Error(e?.message ?? '');
80358992c6bS猫头猫    }
80458992c6bS猫头猫}
80558992c6bS猫头猫
806927dbe93S猫头猫/** 卸载插件 */
807927dbe93S猫头猫async function uninstallPlugin(hash: string) {
808927dbe93S猫头猫    const targetIndex = plugins.findIndex(_ => _.hash === hash);
809927dbe93S猫头猫    if (targetIndex !== -1) {
810927dbe93S猫头猫        try {
81124e5e74aS猫头猫            const pluginName = plugins[targetIndex].name;
812927dbe93S猫头猫            await unlink(plugins[targetIndex].path);
813927dbe93S猫头猫            plugins = plugins.filter(_ => _.hash !== hash);
814927dbe93S猫头猫            pluginStateMapper.notify();
81524e5e74aS猫头猫            if (plugins.every(_ => _.name !== pluginName)) {
81624e5e74aS猫头猫                await MediaMeta.removePlugin(pluginName);
81724e5e74aS猫头猫            }
818927dbe93S猫头猫        } catch {}
819927dbe93S猫头猫    }
820927dbe93S猫头猫}
821927dbe93S猫头猫
82208882a77S猫头猫async function uninstallAllPlugins() {
82308882a77S猫头猫    await Promise.all(
82408882a77S猫头猫        plugins.map(async plugin => {
82508882a77S猫头猫            try {
82608882a77S猫头猫                const pluginName = plugin.name;
82708882a77S猫头猫                await unlink(plugin.path);
82808882a77S猫头猫                await MediaMeta.removePlugin(pluginName);
82908882a77S猫头猫            } catch (e) {}
83008882a77S猫头猫        }),
83108882a77S猫头猫    );
83208882a77S猫头猫    plugins = [];
83308882a77S猫头猫    pluginStateMapper.notify();
834e08d37a3S猫头猫
835e08d37a3S猫头猫    /** 清除空余文件,异步做就可以了 */
836e08d37a3S猫头猫    readDir(pathConst.pluginPath)
837e08d37a3S猫头猫        .then(fns => {
838e08d37a3S猫头猫            fns.forEach(fn => {
839e08d37a3S猫头猫                unlink(fn.path).catch(emptyFunction);
840e08d37a3S猫头猫            });
841e08d37a3S猫头猫        })
842e08d37a3S猫头猫        .catch(emptyFunction);
84308882a77S猫头猫}
84408882a77S猫头猫
84525c1bd29S猫头猫async function updatePlugin(plugin: Plugin) {
84625c1bd29S猫头猫    const updateUrl = plugin.instance.srcUrl;
84725c1bd29S猫头猫    if (!updateUrl) {
84825c1bd29S猫头猫        throw new Error('没有更新源');
84925c1bd29S猫头猫    }
85025c1bd29S猫头猫    try {
85125c1bd29S猫头猫        await installPluginFromUrl(updateUrl);
85225c1bd29S猫头猫    } catch (e: any) {
85325c1bd29S猫头猫        if (e.message === '插件已安装') {
85425c1bd29S猫头猫            throw new Error('当前已是最新版本');
85525c1bd29S猫头猫        } else {
85625c1bd29S猫头猫            throw e;
85725c1bd29S猫头猫        }
85825c1bd29S猫头猫    }
85925c1bd29S猫头猫}
86025c1bd29S猫头猫
861927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) {
8622c595535S猫头猫    return getByName(mediaItem?.platform);
863927dbe93S猫头猫}
864927dbe93S猫头猫
865927dbe93S猫头猫function getByHash(hash: string) {
8667993f90eS猫头猫    return hash === localPluginHash
8677993f90eS猫头猫        ? localFilePlugin
8687993f90eS猫头猫        : plugins.find(_ => _.hash === hash);
869927dbe93S猫头猫}
870927dbe93S猫头猫
871927dbe93S猫头猫function getByName(name: string) {
8727993f90eS猫头猫    return name === localPluginPlatform
8730e4173cdS猫头猫        ? localFilePlugin
8740e4173cdS猫头猫        : plugins.find(_ => _.name === name);
875927dbe93S猫头猫}
876927dbe93S猫头猫
877927dbe93S猫头猫function getValidPlugins() {
878927dbe93S猫头猫    return plugins.filter(_ => _.state === 'enabled');
879927dbe93S猫头猫}
880927dbe93S猫头猫
881efb9da24S猫头猫function getSearchablePlugins() {
882efb9da24S猫头猫    return plugins.filter(_ => _.state === 'enabled' && _.instance.search);
883efb9da24S猫头猫}
884efb9da24S猫头猫
885e08d37a3S猫头猫function getSortedSearchablePlugins() {
886e08d37a3S猫头猫    return getSearchablePlugins().sort((a, b) =>
887e08d37a3S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
888e08d37a3S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
889e08d37a3S猫头猫        0
890e08d37a3S猫头猫            ? -1
891e08d37a3S猫头猫            : 1,
892e08d37a3S猫头猫    );
893e08d37a3S猫头猫}
894e08d37a3S猫头猫
89515feccc1S猫头猫function getTopListsablePlugins() {
89615feccc1S猫头猫    return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists);
89715feccc1S猫头猫}
89815feccc1S猫头猫
89915feccc1S猫头猫function getSortedTopListsablePlugins() {
90015feccc1S猫头猫    return getTopListsablePlugins().sort((a, b) =>
90115feccc1S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
90215feccc1S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
90315feccc1S猫头猫        0
90415feccc1S猫头猫            ? -1
90515feccc1S猫头猫            : 1,
90615feccc1S猫头猫    );
90715feccc1S猫头猫}
90815feccc1S猫头猫
909e08d37a3S猫头猫function useSortedPlugins() {
910e08d37a3S猫头猫    const _plugins = pluginStateMapper.useMappedState();
911e08d37a3S猫头猫    const _pluginMetaAll = PluginMeta.usePluginMetaAll();
912e08d37a3S猫头猫
91334588741S猫头猫    const [sortedPlugins, setSortedPlugins] = useState(
91434588741S猫头猫        [..._plugins].sort((a, b) =>
915e08d37a3S猫头猫            (_pluginMetaAll[a.name]?.order ?? Infinity) -
916e08d37a3S猫头猫                (_pluginMetaAll[b.name]?.order ?? Infinity) <
917e08d37a3S猫头猫            0
918e08d37a3S猫头猫                ? -1
919e08d37a3S猫头猫                : 1,
92034588741S猫头猫        ),
921e08d37a3S猫头猫    );
92234588741S猫头猫
92334588741S猫头猫    useEffect(() => {
924d4cd40d8S猫头猫        InteractionManager.runAfterInteractions(() => {
92534588741S猫头猫            setSortedPlugins(
92634588741S猫头猫                [..._plugins].sort((a, b) =>
92734588741S猫头猫                    (_pluginMetaAll[a.name]?.order ?? Infinity) -
92834588741S猫头猫                        (_pluginMetaAll[b.name]?.order ?? Infinity) <
92934588741S猫头猫                    0
93034588741S猫头猫                        ? -1
93134588741S猫头猫                        : 1,
93234588741S猫头猫                ),
93334588741S猫头猫            );
934d4cd40d8S猫头猫        });
93534588741S猫头猫    }, [_plugins, _pluginMetaAll]);
93634588741S猫头猫
93734588741S猫头猫    return sortedPlugins;
938e08d37a3S猫头猫}
939e08d37a3S猫头猫
940927dbe93S猫头猫const PluginManager = {
941927dbe93S猫头猫    setup,
942927dbe93S猫头猫    installPlugin,
94358992c6bS猫头猫    installPluginFromUrl,
94425c1bd29S猫头猫    updatePlugin,
945927dbe93S猫头猫    uninstallPlugin,
946927dbe93S猫头猫    getByMedia,
947927dbe93S猫头猫    getByHash,
948927dbe93S猫头猫    getByName,
949927dbe93S猫头猫    getValidPlugins,
950efb9da24S猫头猫    getSearchablePlugins,
951e08d37a3S猫头猫    getSortedSearchablePlugins,
95215feccc1S猫头猫    getTopListsablePlugins,
95315feccc1S猫头猫    getSortedTopListsablePlugins,
9545276aef9S猫头猫    usePlugins: pluginStateMapper.useMappedState,
955e08d37a3S猫头猫    useSortedPlugins,
95608882a77S猫头猫    uninstallAllPlugins,
9575276aef9S猫头猫};
958927dbe93S猫头猫
959927dbe93S猫头猫export default PluginManager;
960