xref: /MusicFree/src/core/pluginManager.ts (revision ceb900cd812eb1b086ac040a306ddf325339e920)
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猫头猫    /** 获取榜单 */
59492b6c95aS猫头猫    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(
60892b6c95aS猫头猫        topListItem: IMusic.IMusicSheetItemBase,
60992b6c95aS猫头猫    ): 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猫头猫    }
631*ceb900cdS猫头猫
632*ceb900cdS猫头猫    /** 获取推荐歌单 */
633*ceb900cdS猫头猫    async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> {
634*ceb900cdS猫头猫        try {
635*ceb900cdS猫头猫            const result =
636*ceb900cdS猫头猫                await this.plugin.instance?.getRecommendSheetTags?.();
637*ceb900cdS猫头猫            if (!result) {
638*ceb900cdS猫头猫                throw new Error();
639*ceb900cdS猫头猫            }
640*ceb900cdS猫头猫            return result;
641*ceb900cdS猫头猫        } catch (e: any) {
642*ceb900cdS猫头猫            devLog('error', '获取推荐歌单失败', e, e?.message);
643*ceb900cdS猫头猫            return {
644*ceb900cdS猫头猫                data: [],
645*ceb900cdS猫头猫            };
646*ceb900cdS猫头猫        }
647*ceb900cdS猫头猫    }
648*ceb900cdS猫头猫    /** 获取歌单 */
649*ceb900cdS猫头猫    async getRecommendSheetsByTag(
650*ceb900cdS猫头猫        tagItem: ICommon.IUnique,
651*ceb900cdS猫头猫        page?: number,
652*ceb900cdS猫头猫    ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> {
653*ceb900cdS猫头猫        try {
654*ceb900cdS猫头猫            const result =
655*ceb900cdS猫头猫                await this.plugin.instance?.getRecommendSheetsByTag?.(
656*ceb900cdS猫头猫                    tagItem,
657*ceb900cdS猫头猫                    page ?? 1,
658*ceb900cdS猫头猫                );
659*ceb900cdS猫头猫            if (!result) {
660*ceb900cdS猫头猫                throw new Error();
661*ceb900cdS猫头猫            }
662*ceb900cdS猫头猫            if (result.isEnd !== false) {
663*ceb900cdS猫头猫                result.isEnd = true;
664*ceb900cdS猫头猫            }
665*ceb900cdS猫头猫            if (!result.data) {
666*ceb900cdS猫头猫                result.data = [];
667*ceb900cdS猫头猫            }
668*ceb900cdS猫头猫            result.data.forEach(item => resetMediaItem(item, this.plugin.name));
669*ceb900cdS猫头猫
670*ceb900cdS猫头猫            return result;
671*ceb900cdS猫头猫        } catch (e: any) {
672*ceb900cdS猫头猫            devLog('error', '获取推荐歌单详情失败', e, e?.message);
673*ceb900cdS猫头猫            return {
674*ceb900cdS猫头猫                isEnd: true,
675*ceb900cdS猫头猫                data: [],
676*ceb900cdS猫头猫            };
677*ceb900cdS猫头猫        }
678*ceb900cdS猫头猫    }
679927dbe93S猫头猫}
680d5bfeb7eS猫头猫//#endregion
6811a5528a0S猫头猫
682927dbe93S猫头猫let plugins: Array<Plugin> = [];
683927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins);
68474d0cf81S猫头猫
685d5bfeb7eS猫头猫//#region 本地音乐插件
68674d0cf81S猫头猫/** 本地插件 */
68774d0cf81S猫头猫const localFilePlugin = new Plugin(function () {
6880e4173cdS猫头猫    return {
689d5bfeb7eS猫头猫        platform: localPluginPlatform,
69074d0cf81S猫头猫        _path: '',
69174d0cf81S猫头猫        async getMusicInfo(musicBase) {
69274d0cf81S猫头猫            const localPath = getInternalData<string>(
69374d0cf81S猫头猫                musicBase,
69474d0cf81S猫头猫                InternalDataType.LOCALPATH,
6950e4173cdS猫头猫            );
69674d0cf81S猫头猫            if (localPath) {
69774d0cf81S猫头猫                const coverImg = await Mp3Util.getMediaCoverImg(localPath);
69874d0cf81S猫头猫                return {
69974d0cf81S猫头猫                    artwork: coverImg,
70074d0cf81S猫头猫                };
70174d0cf81S猫头猫            }
70274d0cf81S猫头猫            return null;
70374d0cf81S猫头猫        },
7047993f90eS猫头猫        async getLyric(musicBase) {
7057993f90eS猫头猫            const localPath = getInternalData<string>(
7067993f90eS猫头猫                musicBase,
7077993f90eS猫头猫                InternalDataType.LOCALPATH,
7087993f90eS猫头猫            );
7093a6f67b1S猫头猫            let rawLrc: string | null = null;
7107993f90eS猫头猫            if (localPath) {
7113a6f67b1S猫头猫                // 读取内嵌歌词
7123a6f67b1S猫头猫                try {
7133a6f67b1S猫头猫                    rawLrc = await Mp3Util.getLyric(localPath);
7143a6f67b1S猫头猫                } catch (e) {
7153a6f67b1S猫头猫                    console.log('e', e);
7167993f90eS猫头猫                }
7173a6f67b1S猫头猫                if (!rawLrc) {
7183a6f67b1S猫头猫                    // 读取配置歌词
7193a6f67b1S猫头猫                    const lastDot = localPath.lastIndexOf('.');
7203a6f67b1S猫头猫                    const lrcPath = localPath.slice(0, lastDot) + '.lrc';
7213a6f67b1S猫头猫
7223a6f67b1S猫头猫                    try {
7233a6f67b1S猫头猫                        if (await exists(lrcPath)) {
7243a6f67b1S猫头猫                            rawLrc = await readFile(lrcPath, 'utf8');
7253a6f67b1S猫头猫                        }
7263a6f67b1S猫头猫                    } catch {}
7273a6f67b1S猫头猫                }
7283a6f67b1S猫头猫            }
7293a6f67b1S猫头猫
7303a6f67b1S猫头猫            return rawLrc
7313a6f67b1S猫头猫                ? {
7323a6f67b1S猫头猫                      rawLrc,
7333a6f67b1S猫头猫                  }
7343a6f67b1S猫头猫                : null;
7357993f90eS猫头猫        },
73674d0cf81S猫头猫    };
73774d0cf81S猫头猫}, '');
7387993f90eS猫头猫localFilePlugin.hash = localPluginHash;
739927dbe93S猫头猫
740d5bfeb7eS猫头猫//#endregion
741d5bfeb7eS猫头猫
742927dbe93S猫头猫async function setup() {
743927dbe93S猫头猫    const _plugins: Array<Plugin> = [];
744927dbe93S猫头猫    try {
745927dbe93S猫头猫        // 加载插件
746927dbe93S猫头猫        const pluginsPaths = await readDir(pathConst.pluginPath);
747927dbe93S猫头猫        for (let i = 0; i < pluginsPaths.length; ++i) {
748927dbe93S猫头猫            const _pluginUrl = pluginsPaths[i];
7491e263108S猫头猫            trace('初始化插件', _pluginUrl);
7501e263108S猫头猫            if (
7511e263108S猫头猫                _pluginUrl.isFile() &&
7521e263108S猫头猫                (_pluginUrl.name?.endsWith?.('.js') ||
7531e263108S猫头猫                    _pluginUrl.path?.endsWith?.('.js'))
7541e263108S猫头猫            ) {
755927dbe93S猫头猫                const funcCode = await readFile(_pluginUrl.path, 'utf8');
756927dbe93S猫头猫                const plugin = new Plugin(funcCode, _pluginUrl.path);
7574060c00aS猫头猫                const _pluginIndex = _plugins.findIndex(
7584060c00aS猫头猫                    p => p.hash === plugin.hash,
7594060c00aS猫头猫                );
760927dbe93S猫头猫                if (_pluginIndex !== -1) {
761927dbe93S猫头猫                    // 重复插件,直接忽略
762927dbe93S猫头猫                    return;
763927dbe93S猫头猫                }
764927dbe93S猫头猫                plugin.hash !== '' && _plugins.push(plugin);
765927dbe93S猫头猫            }
766927dbe93S猫头猫        }
767927dbe93S猫头猫
768927dbe93S猫头猫        plugins = _plugins;
769927dbe93S猫头猫        pluginStateMapper.notify();
770e08d37a3S猫头猫        /** 初始化meta信息 */
771e08d37a3S猫头猫        PluginMeta.setupMeta(plugins.map(_ => _.name));
772927dbe93S猫头猫    } catch (e: any) {
7734060c00aS猫头猫        ToastAndroid.show(
7744060c00aS猫头猫            `插件初始化失败:${e?.message ?? e}`,
7754060c00aS猫头猫            ToastAndroid.LONG,
7764060c00aS猫头猫        );
7771a5528a0S猫头猫        errorLog('插件初始化失败', e?.message);
778927dbe93S猫头猫        throw e;
779927dbe93S猫头猫    }
780927dbe93S猫头猫}
781927dbe93S猫头猫
782927dbe93S猫头猫// 安装插件
783927dbe93S猫头猫async function installPlugin(pluginPath: string) {
78422c09412S猫头猫    // if (pluginPath.endsWith('.js')) {
785927dbe93S猫头猫    const funcCode = await readFile(pluginPath, 'utf8');
786927dbe93S猫头猫    const plugin = new Plugin(funcCode, pluginPath);
787927dbe93S猫头猫    const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
788927dbe93S猫头猫    if (_pluginIndex !== -1) {
7894d9d3c4cS猫头猫        throw new Error('插件已安装');
790927dbe93S猫头猫    }
791927dbe93S猫头猫    if (plugin.hash !== '') {
792927dbe93S猫头猫        const fn = nanoid();
793927dbe93S猫头猫        const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
794927dbe93S猫头猫        await copyFile(pluginPath, _pluginPath);
795927dbe93S猫头猫        plugin.path = _pluginPath;
796927dbe93S猫头猫        plugins = plugins.concat(plugin);
797927dbe93S猫头猫        pluginStateMapper.notify();
7984d9d3c4cS猫头猫        return;
799927dbe93S猫头猫    }
8004d9d3c4cS猫头猫    throw new Error('插件无法解析');
80122c09412S猫头猫    // }
80222c09412S猫头猫    // throw new Error('插件不存在');
803927dbe93S猫头猫}
804927dbe93S猫头猫
80558992c6bS猫头猫async function installPluginFromUrl(url: string) {
80658992c6bS猫头猫    try {
80758992c6bS猫头猫        const funcCode = (await axios.get(url)).data;
80858992c6bS猫头猫        if (funcCode) {
80958992c6bS猫头猫            const plugin = new Plugin(funcCode, '');
81058992c6bS猫头猫            const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
81158992c6bS猫头猫            if (_pluginIndex !== -1) {
8128b7ddca8S猫头猫                // 静默忽略
8138b7ddca8S猫头猫                return;
81458992c6bS猫头猫            }
81525c1bd29S猫头猫            const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
81625c1bd29S猫头猫            if (oldVersionPlugin) {
81725c1bd29S猫头猫                if (
81825c1bd29S猫头猫                    compare(
81925c1bd29S猫头猫                        oldVersionPlugin.instance.version ?? '',
82025c1bd29S猫头猫                        plugin.instance.version ?? '',
82125c1bd29S猫头猫                        '>',
82225c1bd29S猫头猫                    )
82325c1bd29S猫头猫                ) {
82425c1bd29S猫头猫                    throw new Error('已安装更新版本的插件');
82525c1bd29S猫头猫                }
82625c1bd29S猫头猫            }
82725c1bd29S猫头猫
82858992c6bS猫头猫            if (plugin.hash !== '') {
82958992c6bS猫头猫                const fn = nanoid();
83058992c6bS猫头猫                const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
83158992c6bS猫头猫                await writeFile(_pluginPath, funcCode, 'utf8');
83258992c6bS猫头猫                plugin.path = _pluginPath;
83358992c6bS猫头猫                plugins = plugins.concat(plugin);
83425c1bd29S猫头猫                if (oldVersionPlugin) {
83525c1bd29S猫头猫                    plugins = plugins.filter(
83625c1bd29S猫头猫                        _ => _.hash !== oldVersionPlugin.hash,
83725c1bd29S猫头猫                    );
83825c1bd29S猫头猫                    try {
83925c1bd29S猫头猫                        await unlink(oldVersionPlugin.path);
84025c1bd29S猫头猫                    } catch {}
84125c1bd29S猫头猫                }
84258992c6bS猫头猫                pluginStateMapper.notify();
84358992c6bS猫头猫                return;
84458992c6bS猫头猫            }
84574acbfc0S猫头猫            throw new Error('插件无法解析!');
84658992c6bS猫头猫        }
84725c1bd29S猫头猫    } catch (e: any) {
848ea6d708fS猫头猫        devLog('error', 'URL安装插件失败', e, e?.message);
84958992c6bS猫头猫        errorLog('URL安装插件失败', e);
85025c1bd29S猫头猫        throw new Error(e?.message ?? '');
85158992c6bS猫头猫    }
85258992c6bS猫头猫}
85358992c6bS猫头猫
854927dbe93S猫头猫/** 卸载插件 */
855927dbe93S猫头猫async function uninstallPlugin(hash: string) {
856927dbe93S猫头猫    const targetIndex = plugins.findIndex(_ => _.hash === hash);
857927dbe93S猫头猫    if (targetIndex !== -1) {
858927dbe93S猫头猫        try {
85924e5e74aS猫头猫            const pluginName = plugins[targetIndex].name;
860927dbe93S猫头猫            await unlink(plugins[targetIndex].path);
861927dbe93S猫头猫            plugins = plugins.filter(_ => _.hash !== hash);
862927dbe93S猫头猫            pluginStateMapper.notify();
86324e5e74aS猫头猫            if (plugins.every(_ => _.name !== pluginName)) {
86424e5e74aS猫头猫                await MediaMeta.removePlugin(pluginName);
86524e5e74aS猫头猫            }
866927dbe93S猫头猫        } catch {}
867927dbe93S猫头猫    }
868927dbe93S猫头猫}
869927dbe93S猫头猫
87008882a77S猫头猫async function uninstallAllPlugins() {
87108882a77S猫头猫    await Promise.all(
87208882a77S猫头猫        plugins.map(async plugin => {
87308882a77S猫头猫            try {
87408882a77S猫头猫                const pluginName = plugin.name;
87508882a77S猫头猫                await unlink(plugin.path);
87608882a77S猫头猫                await MediaMeta.removePlugin(pluginName);
87708882a77S猫头猫            } catch (e) {}
87808882a77S猫头猫        }),
87908882a77S猫头猫    );
88008882a77S猫头猫    plugins = [];
88108882a77S猫头猫    pluginStateMapper.notify();
882e08d37a3S猫头猫
883e08d37a3S猫头猫    /** 清除空余文件,异步做就可以了 */
884e08d37a3S猫头猫    readDir(pathConst.pluginPath)
885e08d37a3S猫头猫        .then(fns => {
886e08d37a3S猫头猫            fns.forEach(fn => {
887e08d37a3S猫头猫                unlink(fn.path).catch(emptyFunction);
888e08d37a3S猫头猫            });
889e08d37a3S猫头猫        })
890e08d37a3S猫头猫        .catch(emptyFunction);
89108882a77S猫头猫}
89208882a77S猫头猫
89325c1bd29S猫头猫async function updatePlugin(plugin: Plugin) {
89425c1bd29S猫头猫    const updateUrl = plugin.instance.srcUrl;
89525c1bd29S猫头猫    if (!updateUrl) {
89625c1bd29S猫头猫        throw new Error('没有更新源');
89725c1bd29S猫头猫    }
89825c1bd29S猫头猫    try {
89925c1bd29S猫头猫        await installPluginFromUrl(updateUrl);
90025c1bd29S猫头猫    } catch (e: any) {
90125c1bd29S猫头猫        if (e.message === '插件已安装') {
90225c1bd29S猫头猫            throw new Error('当前已是最新版本');
90325c1bd29S猫头猫        } else {
90425c1bd29S猫头猫            throw e;
90525c1bd29S猫头猫        }
90625c1bd29S猫头猫    }
90725c1bd29S猫头猫}
90825c1bd29S猫头猫
909927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) {
9102c595535S猫头猫    return getByName(mediaItem?.platform);
911927dbe93S猫头猫}
912927dbe93S猫头猫
913927dbe93S猫头猫function getByHash(hash: string) {
9147993f90eS猫头猫    return hash === localPluginHash
9157993f90eS猫头猫        ? localFilePlugin
9167993f90eS猫头猫        : plugins.find(_ => _.hash === hash);
917927dbe93S猫头猫}
918927dbe93S猫头猫
919927dbe93S猫头猫function getByName(name: string) {
9207993f90eS猫头猫    return name === localPluginPlatform
9210e4173cdS猫头猫        ? localFilePlugin
9220e4173cdS猫头猫        : plugins.find(_ => _.name === name);
923927dbe93S猫头猫}
924927dbe93S猫头猫
925927dbe93S猫头猫function getValidPlugins() {
926927dbe93S猫头猫    return plugins.filter(_ => _.state === 'enabled');
927927dbe93S猫头猫}
928927dbe93S猫头猫
929efb9da24S猫头猫function getSearchablePlugins() {
930efb9da24S猫头猫    return plugins.filter(_ => _.state === 'enabled' && _.instance.search);
931efb9da24S猫头猫}
932efb9da24S猫头猫
933e08d37a3S猫头猫function getSortedSearchablePlugins() {
934e08d37a3S猫头猫    return getSearchablePlugins().sort((a, b) =>
935e08d37a3S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
936e08d37a3S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
937e08d37a3S猫头猫        0
938e08d37a3S猫头猫            ? -1
939e08d37a3S猫头猫            : 1,
940e08d37a3S猫头猫    );
941e08d37a3S猫头猫}
942e08d37a3S猫头猫
94315feccc1S猫头猫function getTopListsablePlugins() {
94415feccc1S猫头猫    return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists);
94515feccc1S猫头猫}
94615feccc1S猫头猫
94715feccc1S猫头猫function getSortedTopListsablePlugins() {
94815feccc1S猫头猫    return getTopListsablePlugins().sort((a, b) =>
94915feccc1S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
95015feccc1S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
95115feccc1S猫头猫        0
95215feccc1S猫头猫            ? -1
95315feccc1S猫头猫            : 1,
95415feccc1S猫头猫    );
95515feccc1S猫头猫}
95615feccc1S猫头猫
957*ceb900cdS猫头猫function getRecommendSheetablePlugins() {
958*ceb900cdS猫头猫    return plugins.filter(
959*ceb900cdS猫头猫        _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag,
960*ceb900cdS猫头猫    );
961*ceb900cdS猫头猫}
962*ceb900cdS猫头猫
963*ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() {
964*ceb900cdS猫头猫    return getRecommendSheetablePlugins().sort((a, b) =>
965*ceb900cdS猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
966*ceb900cdS猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
967*ceb900cdS猫头猫        0
968*ceb900cdS猫头猫            ? -1
969*ceb900cdS猫头猫            : 1,
970*ceb900cdS猫头猫    );
971*ceb900cdS猫头猫}
972*ceb900cdS猫头猫
973e08d37a3S猫头猫function useSortedPlugins() {
974e08d37a3S猫头猫    const _plugins = pluginStateMapper.useMappedState();
975e08d37a3S猫头猫    const _pluginMetaAll = PluginMeta.usePluginMetaAll();
976e08d37a3S猫头猫
97734588741S猫头猫    const [sortedPlugins, setSortedPlugins] = useState(
97834588741S猫头猫        [..._plugins].sort((a, b) =>
979e08d37a3S猫头猫            (_pluginMetaAll[a.name]?.order ?? Infinity) -
980e08d37a3S猫头猫                (_pluginMetaAll[b.name]?.order ?? Infinity) <
981e08d37a3S猫头猫            0
982e08d37a3S猫头猫                ? -1
983e08d37a3S猫头猫                : 1,
98434588741S猫头猫        ),
985e08d37a3S猫头猫    );
98634588741S猫头猫
98734588741S猫头猫    useEffect(() => {
988d4cd40d8S猫头猫        InteractionManager.runAfterInteractions(() => {
98934588741S猫头猫            setSortedPlugins(
99034588741S猫头猫                [..._plugins].sort((a, b) =>
99134588741S猫头猫                    (_pluginMetaAll[a.name]?.order ?? Infinity) -
99234588741S猫头猫                        (_pluginMetaAll[b.name]?.order ?? Infinity) <
99334588741S猫头猫                    0
99434588741S猫头猫                        ? -1
99534588741S猫头猫                        : 1,
99634588741S猫头猫                ),
99734588741S猫头猫            );
998d4cd40d8S猫头猫        });
99934588741S猫头猫    }, [_plugins, _pluginMetaAll]);
100034588741S猫头猫
100134588741S猫头猫    return sortedPlugins;
1002e08d37a3S猫头猫}
1003e08d37a3S猫头猫
1004927dbe93S猫头猫const PluginManager = {
1005927dbe93S猫头猫    setup,
1006927dbe93S猫头猫    installPlugin,
100758992c6bS猫头猫    installPluginFromUrl,
100825c1bd29S猫头猫    updatePlugin,
1009927dbe93S猫头猫    uninstallPlugin,
1010927dbe93S猫头猫    getByMedia,
1011927dbe93S猫头猫    getByHash,
1012927dbe93S猫头猫    getByName,
1013927dbe93S猫头猫    getValidPlugins,
1014efb9da24S猫头猫    getSearchablePlugins,
1015e08d37a3S猫头猫    getSortedSearchablePlugins,
101615feccc1S猫头猫    getTopListsablePlugins,
1017*ceb900cdS猫头猫    getSortedRecommendSheetablePlugins,
101815feccc1S猫头猫    getSortedTopListsablePlugins,
10195276aef9S猫头猫    usePlugins: pluginStateMapper.useMappedState,
1020e08d37a3S猫头猫    useSortedPlugins,
102108882a77S猫头猫    uninstallAllPlugins,
10225276aef9S猫头猫};
1023927dbe93S猫头猫
1024927dbe93S猫头猫export default PluginManager;
1025