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