xref: /MusicFree/src/core/pluginManager.ts (revision 33d529550b6205d83b1bee02bb839fb71b46cd36)
126f32636S猫头猫import RNFS, {
2927dbe93S猫头猫    copyFile,
3927dbe93S猫头猫    exists,
4927dbe93S猫头猫    readDir,
5927dbe93S猫头猫    readFile,
642a9f3e6Smaotoumao    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 he from 'he';
40ef714860S猫头猫import Network from './network';
410e4173cdS猫头猫import LocalMusicSheet from './localMusicSheet';
4274d0cf81S猫头猫import Mp3Util from '@/native/mp3Util';
43e08d37a3S猫头猫import {PluginMeta} from './pluginMeta';
4434588741S猫头猫import {useEffect, useState} from 'react';
4511908939S猫头猫import {addFileScheme, getFileName} from '@/utils/fileUtils';
46a7b42a4cS猫头猫import {URL} from 'react-native-url-polyfill';
47a7b42a4cS猫头猫import Base64 from '@/utils/base64';
4843eb30bfS猫头猫import MediaCache from './mediaCache';
495589cdf3S猫头猫import {produce} from 'immer';
5043eb30bfS猫头猫import objectPath from 'object-path';
51*33d52955Smaotoumaoimport notImplementedFunction from '@/utils/notImplementedFunction.ts';
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猫头猫
64*33d52955Smaotoumaoconst deprecatedCookieManager = {
65*33d52955Smaotoumao    get: notImplementedFunction,
66*33d52955Smaotoumao    set: notImplementedFunction,
67*33d52955Smaotoumao    flush: notImplementedFunction,
68*33d52955Smaotoumao};
69*33d52955Smaotoumao
709c34d637S猫头猫const packages: Record<string, any> = {
719c34d637S猫头猫    cheerio,
729c34d637S猫头猫    'crypto-js': CryptoJs,
739c34d637S猫头猫    axios,
749c34d637S猫头猫    dayjs,
759c34d637S猫头猫    'big-integer': bigInt,
769c34d637S猫头猫    qs,
779c34d637S猫头猫    he,
78*33d52955Smaotoumao    '@react-native-cookies/cookies': deprecatedCookieManager,
7928ccceb7S猫头猫    webdav,
809c34d637S猫头猫};
819c34d637S猫头猫
82b43683eaS猫头猫const _require = (packageName: string) => {
83b43683eaS猫头猫    let pkg = packages[packageName];
84b43683eaS猫头猫    pkg.default = pkg;
85b43683eaS猫头猫    return pkg;
86b43683eaS猫头猫};
879c34d637S猫头猫
8853f8cd8eS猫头猫const _consoleBind = function (
8953f8cd8eS猫头猫    method: 'log' | 'error' | 'info' | 'warn',
9053f8cd8eS猫头猫    ...args: any
9153f8cd8eS猫头猫) {
9253f8cd8eS猫头猫    const fn = console[method];
9353f8cd8eS猫头猫    if (fn) {
9453f8cd8eS猫头猫        fn(...args);
9553f8cd8eS猫头猫        devLog(method, ...args);
9653f8cd8eS猫头猫    }
9753f8cd8eS猫头猫};
9853f8cd8eS猫头猫
9953f8cd8eS猫头猫const _console = {
10053f8cd8eS猫头猫    log: _consoleBind.bind(null, 'log'),
10153f8cd8eS猫头猫    warn: _consoleBind.bind(null, 'warn'),
10253f8cd8eS猫头猫    info: _consoleBind.bind(null, 'info'),
10353f8cd8eS猫头猫    error: _consoleBind.bind(null, 'error'),
10453f8cd8eS猫头猫};
10553f8cd8eS猫头猫
106a7b42a4cS猫头猫function formatAuthUrl(url: string) {
107a7b42a4cS猫头猫    const urlObj = new URL(url);
108a7b42a4cS猫头猫
109a7b42a4cS猫头猫    try {
110a7b42a4cS猫头猫        if (urlObj.username && urlObj.password) {
111a7b42a4cS猫头猫            const auth = `Basic ${Base64.btoa(
112a7b42a4cS猫头猫                `${decodeURIComponent(urlObj.username)}:${decodeURIComponent(
113a7b42a4cS猫头猫                    urlObj.password,
114a7b42a4cS猫头猫                )}`,
115a7b42a4cS猫头猫            )}`;
116a7b42a4cS猫头猫            urlObj.username = '';
117a7b42a4cS猫头猫            urlObj.password = '';
118a7b42a4cS猫头猫
119a7b42a4cS猫头猫            return {
120a7b42a4cS猫头猫                url: urlObj.toString(),
121a7b42a4cS猫头猫                auth,
122a7b42a4cS猫头猫            };
123a7b42a4cS猫头猫        }
124a7b42a4cS猫头猫    } catch (e) {
125a7b42a4cS猫头猫        return {
126a7b42a4cS猫头猫            url,
127a7b42a4cS猫头猫        };
128a7b42a4cS猫头猫    }
129a7b42a4cS猫头猫    return {
130a7b42a4cS猫头猫        url,
131a7b42a4cS猫头猫    };
132a7b42a4cS猫头猫}
133a7b42a4cS猫头猫
134d5bfeb7eS猫头猫//#region 插件类
135927dbe93S猫头猫export class Plugin {
136927dbe93S猫头猫    /** 插件名 */
137927dbe93S猫头猫    public name: string;
138927dbe93S猫头猫    /** 插件的hash,作为唯一id */
139927dbe93S猫头猫    public hash: string;
140927dbe93S猫头猫    /** 插件状态:激活、关闭、错误 */
141927dbe93S猫头猫    public state: 'enabled' | 'disabled' | 'error';
142927dbe93S猫头猫    /** 插件状态信息 */
143927dbe93S猫头猫    public stateCode?: PluginStateCode;
144927dbe93S猫头猫    /** 插件的实例 */
145927dbe93S猫头猫    public instance: IPlugin.IPluginInstance;
146927dbe93S猫头猫    /** 插件路径 */
147927dbe93S猫头猫    public path: string;
148927dbe93S猫头猫    /** 插件方法 */
149927dbe93S猫头猫    public methods: PluginMethods;
150927dbe93S猫头猫
15174d0cf81S猫头猫    constructor(
15274d0cf81S猫头猫        funcCode: string | (() => IPlugin.IPluginInstance),
15374d0cf81S猫头猫        pluginPath: string,
15474d0cf81S猫头猫    ) {
155927dbe93S猫头猫        this.state = 'enabled';
156927dbe93S猫头猫        let _instance: IPlugin.IPluginInstance;
1573b3d6357S猫头猫        const _module: any = {exports: {}};
158927dbe93S猫头猫        try {
15974d0cf81S猫头猫            if (typeof funcCode === 'string') {
160e0caf342S猫头猫                // 插件的环境变量
161e0caf342S猫头猫                const env = {
162e0caf342S猫头猫                    getUserVariables: () => {
163e0caf342S猫头猫                        return (
164e0caf342S猫头猫                            PluginMeta.getPluginMeta(this)?.userVariables ?? {}
165e0caf342S猫头猫                        );
166e0caf342S猫头猫                    },
167e3fa9b3cS猫头猫                    os: 'android',
168e0caf342S猫头猫                };
169e0caf342S猫头猫
1704060c00aS猫头猫                // eslint-disable-next-line no-new-func
171927dbe93S猫头猫                _instance = Function(`
172927dbe93S猫头猫                    'use strict';
17391eb8fa8S猫头猫                    return function(require, __musicfree_require, module, exports, console, env, URL) {
1749c34d637S猫头猫                        ${funcCode}
175927dbe93S猫头猫                    }
176e0caf342S猫头猫                `)()(
177e0caf342S猫头猫                    _require,
178e0caf342S猫头猫                    _require,
179e0caf342S猫头猫                    _module,
180e0caf342S猫头猫                    _module.exports,
181e0caf342S猫头猫                    _console,
182e0caf342S猫头猫                    env,
18391eb8fa8S猫头猫                    URL,
184e0caf342S猫头猫                );
1853b3d6357S猫头猫                if (_module.exports.default) {
1863b3d6357S猫头猫                    _instance = _module.exports
1873b3d6357S猫头猫                        .default as IPlugin.IPluginInstance;
1883b3d6357S猫头猫                } else {
1899c34d637S猫头猫                    _instance = _module.exports as IPlugin.IPluginInstance;
1903b3d6357S猫头猫                }
19174d0cf81S猫头猫            } else {
19274d0cf81S猫头猫                _instance = funcCode();
19374d0cf81S猫头猫            }
194c2b3a262S猫头猫            // 插件初始化后的一些操作
19595297592S猫头猫            if (Array.isArray(_instance.userVariables)) {
19695297592S猫头猫                _instance.userVariables = _instance.userVariables.filter(
19795297592S猫头猫                    it => it?.key,
19895297592S猫头猫                );
19995297592S猫头猫            }
200927dbe93S猫头猫            this.checkValid(_instance);
201927dbe93S猫头猫        } catch (e: any) {
202b43683eaS猫头猫            console.log(e);
203927dbe93S猫头猫            this.state = 'error';
204927dbe93S猫头猫            this.stateCode = PluginStateCode.CannotParse;
205927dbe93S猫头猫            if (e?.stateCode) {
206927dbe93S猫头猫                this.stateCode = e.stateCode;
207927dbe93S猫头猫            }
208927dbe93S猫头猫            errorLog(`${pluginPath}插件无法解析 `, {
209927dbe93S猫头猫                stateCode: this.stateCode,
210927dbe93S猫头猫                message: e?.message,
211927dbe93S猫头猫                stack: e?.stack,
212927dbe93S猫头猫            });
213927dbe93S猫头猫            _instance = e?.instance ?? {
214927dbe93S猫头猫                _path: '',
215927dbe93S猫头猫                platform: '',
216927dbe93S猫头猫                appVersion: '',
21720e6a092S猫头猫                async getMediaSource() {
218927dbe93S猫头猫                    return null;
219927dbe93S猫头猫                },
220927dbe93S猫头猫                async search() {
221927dbe93S猫头猫                    return {};
222927dbe93S猫头猫                },
223927dbe93S猫头猫                async getAlbumInfo() {
224927dbe93S猫头猫                    return null;
225927dbe93S猫头猫                },
226927dbe93S猫头猫            };
227927dbe93S猫头猫        }
228927dbe93S猫头猫        this.instance = _instance;
229927dbe93S猫头猫        this.path = pluginPath;
230927dbe93S猫头猫        this.name = _instance.platform;
231ab8941d9S猫头猫        if (
232ab8941d9S猫头猫            this.instance.platform === '' ||
233ab8941d9S猫头猫            this.instance.platform === undefined
234ab8941d9S猫头猫        ) {
235927dbe93S猫头猫            this.hash = '';
236927dbe93S猫头猫        } else {
23774d0cf81S猫头猫            if (typeof funcCode === 'string') {
238927dbe93S猫头猫                this.hash = sha256(funcCode).toString();
23974d0cf81S猫头猫            } else {
24074d0cf81S猫头猫                this.hash = sha256(funcCode.toString()).toString();
24174d0cf81S猫头猫            }
242927dbe93S猫头猫        }
243927dbe93S猫头猫
244927dbe93S猫头猫        // 放在最后
245927dbe93S猫头猫        this.methods = new PluginMethods(this);
246927dbe93S猫头猫    }
247927dbe93S猫头猫
248927dbe93S猫头猫    private checkValid(_instance: IPlugin.IPluginInstance) {
249927dbe93S猫头猫        /** 版本号校验 */
250927dbe93S猫头猫        if (
251927dbe93S猫头猫            _instance.appVersion &&
252927dbe93S猫头猫            !satisfies(DeviceInfo.getVersion(), _instance.appVersion)
253927dbe93S猫头猫        ) {
254927dbe93S猫头猫            throw {
255927dbe93S猫头猫                instance: _instance,
256927dbe93S猫头猫                stateCode: PluginStateCode.VersionNotMatch,
257927dbe93S猫头猫            };
258927dbe93S猫头猫        }
259927dbe93S猫头猫        return true;
260927dbe93S猫头猫    }
261927dbe93S猫头猫}
26242a9f3e6Smaotoumao
263d5bfeb7eS猫头猫//#endregion
264927dbe93S猫头猫
265d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用
266927dbe93S猫头猫/** 有缓存等信息 */
267927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods {
268927dbe93S猫头猫    private plugin;
26942a9f3e6Smaotoumao
270927dbe93S猫头猫    constructor(plugin: Plugin) {
271927dbe93S猫头猫        this.plugin = plugin;
272927dbe93S猫头猫    }
27342a9f3e6Smaotoumao
274927dbe93S猫头猫    /** 搜索 */
275927dbe93S猫头猫    async search<T extends ICommon.SupportMediaType>(
276927dbe93S猫头猫        query: string,
277927dbe93S猫头猫        page: number,
278927dbe93S猫头猫        type: T,
279927dbe93S猫头猫    ): Promise<IPlugin.ISearchResult<T>> {
280927dbe93S猫头猫        if (!this.plugin.instance.search) {
281927dbe93S猫头猫            return {
282927dbe93S猫头猫                isEnd: true,
283927dbe93S猫头猫                data: [],
284927dbe93S猫头猫            };
285927dbe93S猫头猫        }
286927dbe93S猫头猫
2874060c00aS猫头猫        const result =
2884060c00aS猫头猫            (await this.plugin.instance.search(query, page, type)) ?? {};
289927dbe93S猫头猫        if (Array.isArray(result.data)) {
290927dbe93S猫头猫            result.data.forEach(_ => {
291927dbe93S猫头猫                resetMediaItem(_, this.plugin.name);
292927dbe93S猫头猫            });
293927dbe93S猫头猫            return {
294927dbe93S猫头猫                isEnd: result.isEnd ?? true,
295927dbe93S猫头猫                data: result.data,
296927dbe93S猫头猫            };
297927dbe93S猫头猫        }
298927dbe93S猫头猫        return {
299927dbe93S猫头猫            isEnd: true,
300927dbe93S猫头猫            data: [],
301927dbe93S猫头猫        };
302927dbe93S猫头猫    }
303927dbe93S猫头猫
304927dbe93S猫头猫    /** 获取真实源 */
30520e6a092S猫头猫    async getMediaSource(
306927dbe93S猫头猫        musicItem: IMusic.IMusicItemBase,
307abaede57S猫头猫        quality: IMusic.IQualityKey = 'standard',
308927dbe93S猫头猫        retryCount = 1,
309dc160d50S猫头猫        notUpdateCache = false,
310192ae2b0S猫头猫    ): Promise<IPlugin.IMediaSourceResult | null> {
311927dbe93S猫头猫        // 1. 本地搜索 其实直接读mediameta就好了
31243eb30bfS猫头猫        const mediaExtra = MediaExtra.get(musicItem);
313927dbe93S猫头猫        const localPath =
31443eb30bfS猫头猫            mediaExtra?.localPath ||
31543eb30bfS猫头猫            getInternalData<string>(musicItem, InternalDataType.LOCALPATH) ||
3160e4173cdS猫头猫            getInternalData<string>(
3170e4173cdS猫头猫                LocalMusicSheet.isLocalMusic(musicItem),
3180e4173cdS猫头猫                InternalDataType.LOCALPATH,
3190e4173cdS猫头猫            );
32042a9f3e6Smaotoumao        if (localPath && (await exists(localPath))) {
3210e4173cdS猫头猫            trace('本地播放', localPath);
32243eb30bfS猫头猫            if (mediaExtra && mediaExtra.localPath !== localPath) {
32343eb30bfS猫头猫                // 修正一下本地数据
32443eb30bfS猫头猫                MediaExtra.update(musicItem, {
32543eb30bfS猫头猫                    localPath,
32643eb30bfS猫头猫                });
32743eb30bfS猫头猫            }
328927dbe93S猫头猫            return {
32911908939S猫头猫                url: addFileScheme(localPath),
330927dbe93S猫头猫            };
33143eb30bfS猫头猫        } else if (mediaExtra?.localPath) {
33243eb30bfS猫头猫            MediaExtra.update(musicItem, {
33343eb30bfS猫头猫                localPath: undefined,
33443eb30bfS猫头猫            });
335927dbe93S猫头猫        }
336a84a85c5S猫头猫
3377993f90eS猫头猫        if (musicItem.platform === localPluginPlatform) {
338f5935920S猫头猫            throw new Error('本地音乐不存在');
339f5935920S猫头猫        }
340927dbe93S猫头猫        // 2. 缓存播放
34143eb30bfS猫头猫        const mediaCache = MediaCache.getMediaCache(
34243eb30bfS猫头猫            musicItem,
34343eb30bfS猫头猫        ) as IMusic.IMusicItem | null;
344985f8e75S猫头猫        const pluginCacheControl =
345985f8e75S猫头猫            this.plugin.instance.cacheControl ?? 'no-cache';
346cfa0fc07S猫头猫        if (
347cfa0fc07S猫头猫            mediaCache &&
34843eb30bfS猫头猫            mediaCache?.source?.[quality]?.url &&
34948f4b873S猫头猫            (pluginCacheControl === CacheControl.Cache ||
35048f4b873S猫头猫                (pluginCacheControl === CacheControl.NoCache &&
351ef714860S猫头猫                    Network.isOffline()))
352cfa0fc07S猫头猫        ) {
3535276aef9S猫头猫            trace('播放', '缓存播放');
35443eb30bfS猫头猫            const qualityInfo = mediaCache.source[quality];
355927dbe93S猫头猫            return {
35643eb30bfS猫头猫                url: qualityInfo!.url,
357927dbe93S猫头猫                headers: mediaCache.headers,
3584060c00aS猫头猫                userAgent:
3594060c00aS猫头猫                    mediaCache.userAgent ?? mediaCache.headers?.['user-agent'],
360927dbe93S猫头猫            };
361927dbe93S猫头猫        }
362927dbe93S猫头猫        // 3. 插件解析
36320e6a092S猫头猫        if (!this.plugin.instance.getMediaSource) {
364a7b42a4cS猫头猫            const {url, auth} = formatAuthUrl(
365a7b42a4cS猫头猫                musicItem?.qualities?.[quality]?.url ?? musicItem.url,
366a7b42a4cS猫头猫            );
367a7b42a4cS猫头猫            return {
368a7b42a4cS猫头猫                url: url,
369a7b42a4cS猫头猫                headers: auth
370a7b42a4cS猫头猫                    ? {
371a7b42a4cS猫头猫                          Authorization: auth,
372a7b42a4cS猫头猫                      }
373a7b42a4cS猫头猫                    : undefined,
374a7b42a4cS猫头猫            };
375927dbe93S猫头猫        }
376927dbe93S猫头猫        try {
377abaede57S猫头猫            const {url, headers} = (await this.plugin.instance.getMediaSource(
378abaede57S猫头猫                musicItem,
379abaede57S猫头猫                quality,
380abaede57S猫头猫            )) ?? {url: musicItem?.qualities?.[quality]?.url};
381927dbe93S猫头猫            if (!url) {
382a28eac61S猫头猫                throw new Error('NOT RETRY');
383927dbe93S猫头猫            }
3845276aef9S猫头猫            trace('播放', '插件播放');
385927dbe93S猫头猫            const result = {
386927dbe93S猫头猫                url,
387927dbe93S猫头猫                headers,
388927dbe93S猫头猫                userAgent: headers?.['user-agent'],
389cfa0fc07S猫头猫            } as IPlugin.IMediaSourceResult;
390a7b42a4cS猫头猫            const authFormattedResult = formatAuthUrl(result.url!);
391a7b42a4cS猫头猫            if (authFormattedResult.auth) {
392a7b42a4cS猫头猫                result.url = authFormattedResult.url;
393a7b42a4cS猫头猫                result.headers = {
394a7b42a4cS猫头猫                    ...(result.headers ?? {}),
395a7b42a4cS猫头猫                    Authorization: authFormattedResult.auth,
396a7b42a4cS猫头猫                };
397a7b42a4cS猫头猫            }
398927dbe93S猫头猫
399dc160d50S猫头猫            if (
400dc160d50S猫头猫                pluginCacheControl !== CacheControl.NoStore &&
401dc160d50S猫头猫                !notUpdateCache
402dc160d50S猫头猫            ) {
40343eb30bfS猫头猫                // 更新缓存
40443eb30bfS猫头猫                const cacheSource = {
40543eb30bfS猫头猫                    headers: result.headers,
40643eb30bfS猫头猫                    userAgent: result.userAgent,
40743eb30bfS猫头猫                    url,
40843eb30bfS猫头猫                };
40943eb30bfS猫头猫                let realMusicItem = {
41043eb30bfS猫头猫                    ...musicItem,
41143eb30bfS猫头猫                    ...(mediaCache || {}),
41243eb30bfS猫头猫                };
41343eb30bfS猫头猫                realMusicItem.source = {
41443eb30bfS猫头猫                    ...(realMusicItem.source || {}),
41543eb30bfS猫头猫                    [quality]: cacheSource,
41643eb30bfS猫头猫                };
41743eb30bfS猫头猫
41843eb30bfS猫头猫                MediaCache.setMediaCache(realMusicItem);
419752ffc5aS猫头猫            }
420927dbe93S猫头猫            return result;
421927dbe93S猫头猫        } catch (e: any) {
422a28eac61S猫头猫            if (retryCount > 0 && e?.message !== 'NOT RETRY') {
423927dbe93S猫头猫                await delay(150);
424abaede57S猫头猫                return this.getMediaSource(musicItem, quality, --retryCount);
425927dbe93S猫头猫            }
426927dbe93S猫头猫            errorLog('获取真实源失败', e?.message);
427ea6d708fS猫头猫            devLog('error', '获取真实源失败', e, e?.message);
428192ae2b0S猫头猫            return null;
429927dbe93S猫头猫        }
430927dbe93S猫头猫    }
431927dbe93S猫头猫
432927dbe93S猫头猫    /** 获取音乐详情 */
433927dbe93S猫头猫    async getMusicInfo(
434927dbe93S猫头猫        musicItem: ICommon.IMediaBase,
43574d0cf81S猫头猫    ): Promise<Partial<IMusic.IMusicItem> | null> {
436927dbe93S猫头猫        if (!this.plugin.instance.getMusicInfo) {
437d704daedS猫头猫            return null;
438927dbe93S猫头猫        }
43974d0cf81S猫头猫        try {
440927dbe93S猫头猫            return (
441927dbe93S猫头猫                this.plugin.instance.getMusicInfo(
4427993f90eS猫头猫                    resetMediaItem(musicItem, undefined, true),
443d704daedS猫头猫                ) ?? null
444927dbe93S猫头猫            );
445ea6d708fS猫头猫        } catch (e: any) {
446ea6d708fS猫头猫            devLog('error', '获取音乐详情失败', e, e?.message);
447d704daedS猫头猫            return null;
44874d0cf81S猫头猫        }
449927dbe93S猫头猫    }
450927dbe93S猫头猫
4517e883dbbS猫头猫    /**
4527e883dbbS猫头猫     *
4537e883dbbS猫头猫     * getLyric(musicItem) => {
4547e883dbbS猫头猫     *      lyric: string;
4557e883dbbS猫头猫     *      trans: string;
4567e883dbbS猫头猫     * }
4577e883dbbS猫头猫     *
4587e883dbbS猫头猫     */
459927dbe93S猫头猫    /** 获取歌词 */
460927dbe93S猫头猫    async getLyric(
46143eb30bfS猫头猫        originalMusicItem: IMusic.IMusicItemBase,
462927dbe93S猫头猫    ): Promise<ILyric.ILyricSource | null> {
46343eb30bfS猫头猫        // 1.额外存储的meta信息(关联歌词)
4645589cdf3S猫头猫        const meta = MediaExtra.get(originalMusicItem);
46543eb30bfS猫头猫        let musicItem: IMusic.IMusicItem;
466927dbe93S猫头猫        if (meta && meta.associatedLrc) {
46743eb30bfS猫头猫            musicItem = meta.associatedLrc as IMusic.IMusicItem;
46843eb30bfS猫头猫        } else {
46943eb30bfS猫头猫            musicItem = originalMusicItem as IMusic.IMusicItem;
470927dbe93S猫头猫        }
47143eb30bfS猫头猫
47243eb30bfS猫头猫        const musicItemCache = MediaCache.getMediaCache(
4737a91f04fS猫头猫            musicItem,
47443eb30bfS猫头猫        ) as IMusic.IMusicItemCache | null;
47543eb30bfS猫头猫
47643eb30bfS猫头猫        /** 原始歌词文本 */
4777e883dbbS猫头猫        let rawLrc: string | null = musicItem.rawLrc || null;
4787e883dbbS猫头猫        let translation: string | null = null;
47943eb30bfS猫头猫
48026f32636S猫头猫        // 2. 本地手动设置的歌词
48126f32636S猫头猫        const platformHash = CryptoJs.MD5(musicItem.platform).toString(
48226f32636S猫头猫            CryptoJs.enc.Hex,
48326f32636S猫头猫        );
48426f32636S猫头猫        const idHash = CryptoJs.MD5(musicItem.id).toString(CryptoJs.enc.Hex);
48526f32636S猫头猫        if (
48626f32636S猫头猫            await RNFS.exists(
48726f32636S猫头猫                pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc',
48826f32636S猫头猫            )
48926f32636S猫头猫        ) {
49026f32636S猫头猫            rawLrc = await RNFS.readFile(
49126f32636S猫头猫                pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc',
49226f32636S猫头猫                'utf8',
49326f32636S猫头猫            );
49426f32636S猫头猫
49526f32636S猫头猫            if (
49626f32636S猫头猫                await RNFS.exists(
49726f32636S猫头猫                    pathConst.localLrcPath +
49826f32636S猫头猫                        platformHash +
49926f32636S猫头猫                        '/' +
50026f32636S猫头猫                        idHash +
50126f32636S猫头猫                        '.tran.lrc',
50226f32636S猫头猫                )
50326f32636S猫头猫            ) {
50426f32636S猫头猫                translation =
50526f32636S猫头猫                    (await RNFS.readFile(
50626f32636S猫头猫                        pathConst.localLrcPath +
50726f32636S猫头猫                            platformHash +
50826f32636S猫头猫                            '/' +
50926f32636S猫头猫                            idHash +
51026f32636S猫头猫                            '.tran.lrc',
51126f32636S猫头猫                        'utf8',
51226f32636S猫头猫                    )) || null;
51326f32636S猫头猫            }
51426f32636S猫头猫
51526f32636S猫头猫            return {
51626f32636S猫头猫                rawLrc,
51726f32636S猫头猫                translation: translation || undefined, // TODO: 这里写的不好
51826f32636S猫头猫            };
51926f32636S猫头猫        }
52026f32636S猫头猫
52143eb30bfS猫头猫        // 2. 缓存歌词 / 对象上本身的歌词
52243eb30bfS猫头猫        if (musicItemCache?.lyric) {
52343eb30bfS猫头猫            // 缓存的远程结果
52443eb30bfS猫头猫            let cacheLyric: ILyric.ILyricSource | null =
52543eb30bfS猫头猫                musicItemCache.lyric || null;
52643eb30bfS猫头猫            // 缓存的本地结果
52743eb30bfS猫头猫            let localLyric: ILyric.ILyricSource | null =
52843eb30bfS猫头猫                musicItemCache.$localLyric || null;
52943eb30bfS猫头猫
5307e883dbbS猫头猫            // 优先用缓存的结果
5317e883dbbS猫头猫            if (cacheLyric.rawLrc || cacheLyric.translation) {
53243eb30bfS猫头猫                return {
5337e883dbbS猫头猫                    rawLrc: cacheLyric.rawLrc,
5347e883dbbS猫头猫                    translation: cacheLyric.translation,
53543eb30bfS猫头猫                };
53643eb30bfS猫头猫            }
53743eb30bfS猫头猫
5387e883dbbS猫头猫            // 本地其实是缓存的路径
5397e883dbbS猫头猫            if (localLyric) {
5407e883dbbS猫头猫                let needRefetch = false;
5417e883dbbS猫头猫                if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) {
5427e883dbbS猫头猫                    rawLrc = await readFile(localLyric.rawLrc, 'utf8');
5437e883dbbS猫头猫                } else if (localLyric.rawLrc) {
5447e883dbbS猫头猫                    needRefetch = true;
5457e883dbbS猫头猫                }
5467e883dbbS猫头猫                if (
5477e883dbbS猫头猫                    localLyric.translation &&
5487e883dbbS猫头猫                    (await exists(localLyric.translation))
5497e883dbbS猫头猫                ) {
5507e883dbbS猫头猫                    translation = await readFile(
5517e883dbbS猫头猫                        localLyric.translation,
5527e883dbbS猫头猫                        'utf8',
5537e883dbbS猫头猫                    );
5547e883dbbS猫头猫                } else if (localLyric.translation) {
5557e883dbbS猫头猫                    needRefetch = true;
55643eb30bfS猫头猫                }
55743eb30bfS猫头猫
5587e883dbbS猫头猫                if (!needRefetch && (rawLrc || translation)) {
55943eb30bfS猫头猫                    return {
5607e883dbbS猫头猫                        rawLrc: rawLrc || undefined,
5617e883dbbS猫头猫                        translation: translation || undefined,
56243eb30bfS猫头猫                    };
56343eb30bfS猫头猫                }
56443eb30bfS猫头猫            }
56543eb30bfS猫头猫        }
56643eb30bfS猫头猫
56743eb30bfS猫头猫        // 3. 无缓存歌词/无自带歌词/无本地歌词
56843eb30bfS猫头猫        let lrcSource: ILyric.ILyricSource | null;
56943eb30bfS猫头猫        if (isSameMediaItem(originalMusicItem, musicItem)) {
57043eb30bfS猫头猫            lrcSource =
57143eb30bfS猫头猫                (await this.plugin.instance
5727e883dbbS猫头猫                    ?.getLyric?.(resetMediaItem(musicItem, undefined, true))
57343eb30bfS猫头猫                    ?.catch(() => null)) || null;
57443eb30bfS猫头猫        } else {
57543eb30bfS猫头猫            lrcSource =
57643eb30bfS猫头猫                (await PluginManager.getByMedia(musicItem)
57743eb30bfS猫头猫                    ?.instance?.getLyric?.(
57843eb30bfS猫头猫                        resetMediaItem(musicItem, undefined, true),
57943eb30bfS猫头猫                    )
58043eb30bfS猫头猫                    ?.catch(() => null)) || null;
58143eb30bfS猫头猫        }
58243eb30bfS猫头猫
58343eb30bfS猫头猫        if (lrcSource) {
5847e883dbbS猫头猫            rawLrc = lrcSource?.rawLrc || rawLrc;
5857e883dbbS猫头猫            translation = lrcSource?.translation || null;
58643eb30bfS猫头猫
5877e883dbbS猫头猫            const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc;
58843eb30bfS猫头猫
5897e883dbbS猫头猫            // 本地的文件名
5907e883dbbS猫头猫            let filename: string | undefined = `${
5917e883dbbS猫头猫                pathConst.lrcCachePath
5927e883dbbS猫头猫            }${nanoid()}.lrc`;
5937e883dbbS猫头猫            let filenameTrans: string | undefined = `${
5947e883dbbS猫头猫                pathConst.lrcCachePath
5957e883dbbS猫头猫            }${nanoid()}.lrc`;
59643eb30bfS猫头猫
5977e883dbbS猫头猫            // 旧版本兼容
5987e883dbbS猫头猫            if (!(rawLrc || translation)) {
5997e883dbbS猫头猫                if (deprecatedLrcUrl) {
60043eb30bfS猫头猫                    rawLrc = (
6017e883dbbS猫头猫                        await axios
6027e883dbbS猫头猫                            .get(deprecatedLrcUrl, {timeout: 3000})
6037e883dbbS猫头猫                            .catch(() => null)
60443eb30bfS猫头猫                    )?.data;
6057e883dbbS猫头猫                } else if (musicItem.rawLrc) {
6067e883dbbS猫头猫                    rawLrc = musicItem.rawLrc;
6077e883dbbS猫头猫                }
60843eb30bfS猫头猫            }
60943eb30bfS猫头猫
61043eb30bfS猫头猫            if (rawLrc) {
61143eb30bfS猫头猫                await writeFile(filename, rawLrc, 'utf8');
61243eb30bfS猫头猫            } else {
6137e883dbbS猫头猫                filename = undefined;
6147e883dbbS猫头猫            }
6157e883dbbS猫头猫            if (translation) {
6167e883dbbS猫头猫                await writeFile(filenameTrans, translation, 'utf8');
6177e883dbbS猫头猫            } else {
6187e883dbbS猫头猫                filenameTrans = undefined;
6197a91f04fS猫头猫            }
6207a91f04fS猫头猫
6217e883dbbS猫头猫            if (rawLrc || translation) {
6227e883dbbS猫头猫                MediaCache.setMediaCache(
6237e883dbbS猫头猫                    produce(musicItemCache || musicItem, draft => {
6247e883dbbS猫头猫                        musicItemCache?.$localLyric?.rawLrc;
6257e883dbbS猫头猫                        objectPath.set(draft, '$localLyric.rawLrc', filename);
62643eb30bfS猫头猫                        objectPath.set(
62743eb30bfS猫头猫                            draft,
6287e883dbbS猫头猫                            '$localLyric.translation',
6297e883dbbS猫头猫                            filenameTrans,
63043eb30bfS猫头猫                        );
63143eb30bfS猫头猫                        return draft;
63243eb30bfS猫头猫                    }),
63343eb30bfS猫头猫                );
634927dbe93S猫头猫                return {
6357e883dbbS猫头猫                    rawLrc: rawLrc || undefined,
6367e883dbbS猫头猫                    translation: translation || undefined,
637927dbe93S猫头猫                };
638927dbe93S猫头猫            }
639927dbe93S猫头猫        }
64043eb30bfS猫头猫
6413a6f67b1S猫头猫        // 6. 如果是本地文件
64243eb30bfS猫头猫        const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem);
64343eb30bfS猫头猫        if (
64443eb30bfS猫头猫            originalMusicItem.platform !== localPluginPlatform &&
64543eb30bfS猫头猫            isDownloaded
64643eb30bfS猫头猫        ) {
6477e883dbbS猫头猫            const res = await localFilePlugin.instance!.getLyric!(isDownloaded);
64843eb30bfS猫头猫
64943eb30bfS猫头猫            console.log('本地文件歌词');
65043eb30bfS猫头猫
6513a6f67b1S猫头猫            if (res) {
6523a6f67b1S猫头猫                return res;
6533a6f67b1S猫头猫            }
6543a6f67b1S猫头猫        }
655ea6d708fS猫头猫        devLog('warn', '无歌词');
656927dbe93S猫头猫
657927dbe93S猫头猫        return null;
658927dbe93S猫头猫    }
659927dbe93S猫头猫
660927dbe93S猫头猫    /** 获取歌词文本 */
661927dbe93S猫头猫    async getLyricText(
662927dbe93S猫头猫        musicItem: IMusic.IMusicItem,
663927dbe93S猫头猫    ): Promise<string | undefined> {
6647e883dbbS猫头猫        return (await this.getLyric(musicItem))?.rawLrc;
665927dbe93S猫头猫    }
666927dbe93S猫头猫
667927dbe93S猫头猫    /** 获取专辑信息 */
668927dbe93S猫头猫    async getAlbumInfo(
669927dbe93S猫头猫        albumItem: IAlbum.IAlbumItemBase,
670f9afcc0dS猫头猫        page: number = 1,
671f9afcc0dS猫头猫    ): Promise<IPlugin.IAlbumInfoResult | null> {
672927dbe93S猫头猫        if (!this.plugin.instance.getAlbumInfo) {
673f9afcc0dS猫头猫            return {
674f9afcc0dS猫头猫                albumItem,
675f2a4767cS猫头猫                musicList: (albumItem?.musicList ?? []).map(
676f2a4767cS猫头猫                    resetMediaItem,
677f2a4767cS猫头猫                    this.plugin.name,
678f2a4767cS猫头猫                    true,
679f2a4767cS猫头猫                ),
680f9afcc0dS猫头猫                isEnd: true,
681f9afcc0dS猫头猫            };
682927dbe93S猫头猫        }
683927dbe93S猫头猫        try {
684927dbe93S猫头猫            const result = await this.plugin.instance.getAlbumInfo(
685927dbe93S猫头猫                resetMediaItem(albumItem, undefined, true),
686f9afcc0dS猫头猫                page,
687927dbe93S猫头猫            );
6885276aef9S猫头猫            if (!result) {
6895276aef9S猫头猫                throw new Error();
6905276aef9S猫头猫            }
691927dbe93S猫头猫            result?.musicList?.forEach(_ => {
692927dbe93S猫头猫                resetMediaItem(_, this.plugin.name);
69396744680S猫头猫                _.album = albumItem.title;
694927dbe93S猫头猫            });
6955276aef9S猫头猫
696f9afcc0dS猫头猫            if (page <= 1) {
697f9afcc0dS猫头猫                // 合并信息
698f9afcc0dS猫头猫                return {
699f9afcc0dS猫头猫                    albumItem: {...albumItem, ...(result?.albumItem ?? {})},
700f9afcc0dS猫头猫                    isEnd: result.isEnd === false ? false : true,
701f9afcc0dS猫头猫                    musicList: result.musicList,
702f9afcc0dS猫头猫                };
703f9afcc0dS猫头猫            } else {
704f9afcc0dS猫头猫                return {
705f9afcc0dS猫头猫                    isEnd: result.isEnd === false ? false : true,
706f9afcc0dS猫头猫                    musicList: result.musicList,
707f9afcc0dS猫头猫                };
708f9afcc0dS猫头猫            }
7094394410dS猫头猫        } catch (e: any) {
7104394410dS猫头猫            trace('获取专辑信息失败', e?.message);
711ea6d708fS猫头猫            devLog('error', '获取专辑信息失败', e, e?.message);
712ea6d708fS猫头猫
713f9afcc0dS猫头猫            return null;
714927dbe93S猫头猫        }
715927dbe93S猫头猫    }
716927dbe93S猫头猫
7175830c002S猫头猫    /** 获取歌单信息 */
7185830c002S猫头猫    async getMusicSheetInfo(
7195830c002S猫头猫        sheetItem: IMusic.IMusicSheetItem,
7205830c002S猫头猫        page: number = 1,
7215830c002S猫头猫    ): Promise<IPlugin.ISheetInfoResult | null> {
7225281926bS猫头猫        if (!this.plugin.instance.getMusicSheetInfo) {
7235830c002S猫头猫            return {
7245830c002S猫头猫                sheetItem,
7255830c002S猫头猫                musicList: sheetItem?.musicList ?? [],
7265830c002S猫头猫                isEnd: true,
7275830c002S猫头猫            };
7285830c002S猫头猫        }
7295830c002S猫头猫        try {
7305830c002S猫头猫            const result = await this.plugin.instance?.getMusicSheetInfo?.(
7315830c002S猫头猫                resetMediaItem(sheetItem, undefined, true),
7325830c002S猫头猫                page,
7335830c002S猫头猫            );
7345830c002S猫头猫            if (!result) {
7355830c002S猫头猫                throw new Error();
7365830c002S猫头猫            }
7375830c002S猫头猫            result?.musicList?.forEach(_ => {
7385830c002S猫头猫                resetMediaItem(_, this.plugin.name);
7395830c002S猫头猫            });
7405830c002S猫头猫
7415830c002S猫头猫            if (page <= 1) {
7425830c002S猫头猫                // 合并信息
7435830c002S猫头猫                return {
7445830c002S猫头猫                    sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})},
7455830c002S猫头猫                    isEnd: result.isEnd === false ? false : true,
7465830c002S猫头猫                    musicList: result.musicList,
7475830c002S猫头猫                };
7485830c002S猫头猫            } else {
7495830c002S猫头猫                return {
7505830c002S猫头猫                    isEnd: result.isEnd === false ? false : true,
7515830c002S猫头猫                    musicList: result.musicList,
7525830c002S猫头猫                };
7535830c002S猫头猫            }
7545830c002S猫头猫        } catch (e: any) {
7555830c002S猫头猫            trace('获取歌单信息失败', e, e?.message);
7565830c002S猫头猫            devLog('error', '获取歌单信息失败', e, e?.message);
7575830c002S猫头猫
7585830c002S猫头猫            return null;
7595830c002S猫头猫        }
7605830c002S猫头猫    }
7615830c002S猫头猫
762927dbe93S猫头猫    /** 查询作者信息 */
763efb9da24S猫头猫    async getArtistWorks<T extends IArtist.ArtistMediaType>(
764927dbe93S猫头猫        artistItem: IArtist.IArtistItem,
765927dbe93S猫头猫        page: number,
766927dbe93S猫头猫        type: T,
767927dbe93S猫头猫    ): Promise<IPlugin.ISearchResult<T>> {
768efb9da24S猫头猫        if (!this.plugin.instance.getArtistWorks) {
769927dbe93S猫头猫            return {
770927dbe93S猫头猫                isEnd: true,
771927dbe93S猫头猫                data: [],
772927dbe93S猫头猫            };
773927dbe93S猫头猫        }
774927dbe93S猫头猫        try {
775efb9da24S猫头猫            const result = await this.plugin.instance.getArtistWorks(
776927dbe93S猫头猫                artistItem,
777927dbe93S猫头猫                page,
778927dbe93S猫头猫                type,
779927dbe93S猫头猫            );
780927dbe93S猫头猫            if (!result.data) {
781927dbe93S猫头猫                return {
782927dbe93S猫头猫                    isEnd: true,
783927dbe93S猫头猫                    data: [],
784927dbe93S猫头猫                };
785927dbe93S猫头猫            }
786927dbe93S猫头猫            result.data?.forEach(_ => resetMediaItem(_, this.plugin.name));
787927dbe93S猫头猫            return {
788927dbe93S猫头猫                isEnd: result.isEnd ?? true,
789927dbe93S猫头猫                data: result.data,
790927dbe93S猫头猫            };
7914394410dS猫头猫        } catch (e: any) {
7924394410dS猫头猫            trace('查询作者信息失败', e?.message);
793ea6d708fS猫头猫            devLog('error', '查询作者信息失败', e, e?.message);
794ea6d708fS猫头猫
795927dbe93S猫头猫            throw e;
796927dbe93S猫头猫        }
797927dbe93S猫头猫    }
79808380090S猫头猫
79908380090S猫头猫    /** 导入歌单 */
80008380090S猫头猫    async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> {
80108380090S猫头猫        try {
80208380090S猫头猫            const result =
80308380090S猫头猫                (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? [];
80408380090S猫头猫            result.forEach(_ => resetMediaItem(_, this.plugin.name));
80508380090S猫头猫            return result;
806ea6d708fS猫头猫        } catch (e: any) {
8070e4173cdS猫头猫            console.log(e);
808ea6d708fS猫头猫            devLog('error', '导入歌单失败', e, e?.message);
809ea6d708fS猫头猫
81008380090S猫头猫            return [];
81108380090S猫头猫        }
81208380090S猫头猫    }
81342a9f3e6Smaotoumao
8144d9d3c4cS猫头猫    /** 导入单曲 */
8154d9d3c4cS猫头猫    async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> {
8164d9d3c4cS猫头猫        try {
8174d9d3c4cS猫头猫            const result = await this.plugin.instance?.importMusicItem?.(
8184d9d3c4cS猫头猫                urlLike,
8194d9d3c4cS猫头猫            );
8204d9d3c4cS猫头猫            if (!result) {
8214d9d3c4cS猫头猫                throw new Error();
8224d9d3c4cS猫头猫            }
8234d9d3c4cS猫头猫            resetMediaItem(result, this.plugin.name);
8244d9d3c4cS猫头猫            return result;
825ea6d708fS猫头猫        } catch (e: any) {
826ea6d708fS猫头猫            devLog('error', '导入单曲失败', e, e?.message);
827ea6d708fS猫头猫
8284d9d3c4cS猫头猫            return null;
8294d9d3c4cS猫头猫        }
8304d9d3c4cS猫头猫    }
83142a9f3e6Smaotoumao
832d52aa40eS猫头猫    /** 获取榜单 */
83392b6c95aS猫头猫    async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> {
834d52aa40eS猫头猫        try {
835d52aa40eS猫头猫            const result = await this.plugin.instance?.getTopLists?.();
836d52aa40eS猫头猫            if (!result) {
837d52aa40eS猫头猫                throw new Error();
838d52aa40eS猫头猫            }
839d52aa40eS猫头猫            return result;
840d52aa40eS猫头猫        } catch (e: any) {
841d52aa40eS猫头猫            devLog('error', '获取榜单失败', e, e?.message);
842d52aa40eS猫头猫            return [];
843d52aa40eS猫头猫        }
844d52aa40eS猫头猫    }
84542a9f3e6Smaotoumao
846d52aa40eS猫头猫    /** 获取榜单详情 */
847d52aa40eS猫头猫    async getTopListDetail(
84892b6c95aS猫头猫        topListItem: IMusic.IMusicSheetItemBase,
849956ee1b7S猫头猫        page: number,
850956ee1b7S猫头猫    ): Promise<IPlugin.ITopListInfoResult> {
851d52aa40eS猫头猫        try {
852d52aa40eS猫头猫            const result = await this.plugin.instance?.getTopListDetail?.(
853d52aa40eS猫头猫                topListItem,
854956ee1b7S猫头猫                page,
855d52aa40eS猫头猫            );
856d52aa40eS猫头猫            if (!result) {
857d52aa40eS猫头猫                throw new Error();
858d52aa40eS猫头猫            }
859d384662fS猫头猫            if (result.musicList) {
860d384662fS猫头猫                result.musicList.forEach(_ =>
861d384662fS猫头猫                    resetMediaItem(_, this.plugin.name),
862d384662fS猫头猫                );
863d384662fS猫头猫            }
864956ee1b7S猫头猫            if (result.isEnd !== false) {
865956ee1b7S猫头猫                result.isEnd = true;
866956ee1b7S猫头猫            }
867d52aa40eS猫头猫            return result;
868d52aa40eS猫头猫        } catch (e: any) {
869d52aa40eS猫头猫            devLog('error', '获取榜单详情失败', e, e?.message);
870d52aa40eS猫头猫            return {
871956ee1b7S猫头猫                isEnd: true,
872956ee1b7S猫头猫                topListItem: topListItem as IMusic.IMusicSheetItem,
873d52aa40eS猫头猫                musicList: [],
874d52aa40eS猫头猫            };
875d52aa40eS猫头猫        }
876d52aa40eS猫头猫    }
877ceb900cdS猫头猫
8785830c002S猫头猫    /** 获取推荐歌单的tag */
879ceb900cdS猫头猫    async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> {
880ceb900cdS猫头猫        try {
881ceb900cdS猫头猫            const result =
882ceb900cdS猫头猫                await this.plugin.instance?.getRecommendSheetTags?.();
883ceb900cdS猫头猫            if (!result) {
884ceb900cdS猫头猫                throw new Error();
885ceb900cdS猫头猫            }
886ceb900cdS猫头猫            return result;
887ceb900cdS猫头猫        } catch (e: any) {
888ceb900cdS猫头猫            devLog('error', '获取推荐歌单失败', e, e?.message);
889ceb900cdS猫头猫            return {
890ceb900cdS猫头猫                data: [],
891ceb900cdS猫头猫            };
892ceb900cdS猫头猫        }
893ceb900cdS猫头猫    }
89442a9f3e6Smaotoumao
8955830c002S猫头猫    /** 获取某个tag的推荐歌单 */
896ceb900cdS猫头猫    async getRecommendSheetsByTag(
897ceb900cdS猫头猫        tagItem: ICommon.IUnique,
898ceb900cdS猫头猫        page?: number,
899ceb900cdS猫头猫    ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> {
900ceb900cdS猫头猫        try {
901ceb900cdS猫头猫            const result =
902ceb900cdS猫头猫                await this.plugin.instance?.getRecommendSheetsByTag?.(
903ceb900cdS猫头猫                    tagItem,
904ceb900cdS猫头猫                    page ?? 1,
905ceb900cdS猫头猫                );
906ceb900cdS猫头猫            if (!result) {
907ceb900cdS猫头猫                throw new Error();
908ceb900cdS猫头猫            }
909ceb900cdS猫头猫            if (result.isEnd !== false) {
910ceb900cdS猫头猫                result.isEnd = true;
911ceb900cdS猫头猫            }
912ceb900cdS猫头猫            if (!result.data) {
913ceb900cdS猫头猫                result.data = [];
914ceb900cdS猫头猫            }
915ceb900cdS猫头猫            result.data.forEach(item => resetMediaItem(item, this.plugin.name));
916ceb900cdS猫头猫
917ceb900cdS猫头猫            return result;
918ceb900cdS猫头猫        } catch (e: any) {
919ceb900cdS猫头猫            devLog('error', '获取推荐歌单详情失败', e, e?.message);
920ceb900cdS猫头猫            return {
921ceb900cdS猫头猫                isEnd: true,
922ceb900cdS猫头猫                data: [],
923ceb900cdS猫头猫            };
924ceb900cdS猫头猫        }
925ceb900cdS猫头猫    }
926da2a2959S猫头猫
927da2a2959S猫头猫    async getMusicComments(
928da2a2959S猫头猫        musicItem: IMusic.IMusicItem,
929da2a2959S猫头猫    ): Promise<ICommon.PaginationResponse<IMedia.IComment>> {
930da2a2959S猫头猫        const result = await this.plugin.instance?.getMusicComments?.(
931da2a2959S猫头猫            musicItem,
932da2a2959S猫头猫        );
933da2a2959S猫头猫        if (!result) {
934da2a2959S猫头猫            throw new Error();
935da2a2959S猫头猫        }
936da2a2959S猫头猫        if (result.isEnd !== false) {
937da2a2959S猫头猫            result.isEnd = true;
938da2a2959S猫头猫        }
939da2a2959S猫头猫        if (!result.data) {
940da2a2959S猫头猫            result.data = [];
941da2a2959S猫头猫        }
942da2a2959S猫头猫
943da2a2959S猫头猫        return result;
944da2a2959S猫头猫    }
9457fb90110S猫头猫
9467fb90110S猫头猫    async migrateFromOtherPlugin(
9477fb90110S猫头猫        mediaItem: ICommon.IMediaBase,
9487fb90110S猫头猫        fromPlatform: string,
9497fb90110S猫头猫    ): Promise<{isOk: boolean; data?: ICommon.IMediaBase}> {
9507fb90110S猫头猫        try {
9517fb90110S猫头猫            const result = await this.plugin.instance?.migrateFromOtherPlugin(
9527fb90110S猫头猫                mediaItem,
9537fb90110S猫头猫                fromPlatform,
9547fb90110S猫头猫            );
9557fb90110S猫头猫
9567fb90110S猫头猫            if (
9577fb90110S猫头猫                result.isOk &&
9587fb90110S猫头猫                result.data?.id &&
9597fb90110S猫头猫                result.data?.platform === this.plugin.platform
9607fb90110S猫头猫            ) {
9617fb90110S猫头猫                return {
9627fb90110S猫头猫                    isOk: result.isOk,
9637fb90110S猫头猫                    data: result.data,
9647fb90110S猫头猫                };
9657fb90110S猫头猫            }
9667fb90110S猫头猫            return {
9677fb90110S猫头猫                isOk: false,
9687fb90110S猫头猫            };
9697fb90110S猫头猫        } catch {
9707fb90110S猫头猫            return {
9717fb90110S猫头猫                isOk: false,
9727fb90110S猫头猫            };
9737fb90110S猫头猫        }
9747fb90110S猫头猫    }
975927dbe93S猫头猫}
976*33d52955Smaotoumao
977d5bfeb7eS猫头猫//#endregion
9781a5528a0S猫头猫
979927dbe93S猫头猫let plugins: Array<Plugin> = [];
980927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins);
98174d0cf81S猫头猫
982d5bfeb7eS猫头猫//#region 本地音乐插件
98374d0cf81S猫头猫/** 本地插件 */
98474d0cf81S猫头猫const localFilePlugin = new Plugin(function () {
9850e4173cdS猫头猫    return {
986d5bfeb7eS猫头猫        platform: localPluginPlatform,
98774d0cf81S猫头猫        _path: '',
98874d0cf81S猫头猫        async getMusicInfo(musicBase) {
98974d0cf81S猫头猫            const localPath = getInternalData<string>(
99074d0cf81S猫头猫                musicBase,
99174d0cf81S猫头猫                InternalDataType.LOCALPATH,
9920e4173cdS猫头猫            );
99374d0cf81S猫头猫            if (localPath) {
99474d0cf81S猫头猫                const coverImg = await Mp3Util.getMediaCoverImg(localPath);
99574d0cf81S猫头猫                return {
99674d0cf81S猫头猫                    artwork: coverImg,
99774d0cf81S猫头猫                };
99874d0cf81S猫头猫            }
99974d0cf81S猫头猫            return null;
100074d0cf81S猫头猫        },
10017993f90eS猫头猫        async getLyric(musicBase) {
10027993f90eS猫头猫            const localPath = getInternalData<string>(
10037993f90eS猫头猫                musicBase,
10047993f90eS猫头猫                InternalDataType.LOCALPATH,
10057993f90eS猫头猫            );
10063a6f67b1S猫头猫            let rawLrc: string | null = null;
10077993f90eS猫头猫            if (localPath) {
10083a6f67b1S猫头猫                // 读取内嵌歌词
10093a6f67b1S猫头猫                try {
10103a6f67b1S猫头猫                    rawLrc = await Mp3Util.getLyric(localPath);
10113a6f67b1S猫头猫                } catch (e) {
1012a84a85c5S猫头猫                    console.log('读取内嵌歌词失败', e);
10137993f90eS猫头猫                }
10143a6f67b1S猫头猫                if (!rawLrc) {
10153a6f67b1S猫头猫                    // 读取配置歌词
10163a6f67b1S猫头猫                    const lastDot = localPath.lastIndexOf('.');
10173a6f67b1S猫头猫                    const lrcPath = localPath.slice(0, lastDot) + '.lrc';
10183a6f67b1S猫头猫
10193a6f67b1S猫头猫                    try {
10203a6f67b1S猫头猫                        if (await exists(lrcPath)) {
10213a6f67b1S猫头猫                            rawLrc = await readFile(lrcPath, 'utf8');
10223a6f67b1S猫头猫                        }
10233a6f67b1S猫头猫                    } catch {}
10243a6f67b1S猫头猫                }
10253a6f67b1S猫头猫            }
10263a6f67b1S猫头猫
10273a6f67b1S猫头猫            return rawLrc
10283a6f67b1S猫头猫                ? {
10293a6f67b1S猫头猫                      rawLrc,
10303a6f67b1S猫头猫                  }
10313a6f67b1S猫头猫                : null;
10327993f90eS猫头猫        },
1033a84a85c5S猫头猫        async importMusicItem(urlLike) {
1034a84a85c5S猫头猫            let meta: any = {};
103542a9f3e6Smaotoumao            let id: string;
103642a9f3e6Smaotoumao
1037a84a85c5S猫头猫            try {
1038a84a85c5S猫头猫                meta = await Mp3Util.getBasicMeta(urlLike);
103942a9f3e6Smaotoumao                const fileStat = await stat(urlLike);
104042a9f3e6Smaotoumao                id =
104142a9f3e6Smaotoumao                    CryptoJs.MD5(fileStat.originalFilepath).toString(
104242a9f3e6Smaotoumao                        CryptoJs.enc.Hex,
104342a9f3e6Smaotoumao                    ) || nanoid();
104442a9f3e6Smaotoumao            } catch {
10455353b473S猫头猫                id = nanoid();
10465353b473S猫头猫            }
104742a9f3e6Smaotoumao
1048a84a85c5S猫头猫            return {
1049a84a85c5S猫头猫                id: id,
1050a84a85c5S猫头猫                platform: '本地',
1051a84a85c5S猫头猫                title: meta?.title ?? getFileName(urlLike),
1052a84a85c5S猫头猫                artist: meta?.artist ?? '未知歌手',
10535353b473S猫头猫                duration: parseInt(meta?.duration ?? '0', 10) / 1000,
1054a84a85c5S猫头猫                album: meta?.album ?? '未知专辑',
1055a84a85c5S猫头猫                artwork: '',
1056a84a85c5S猫头猫                [internalSerializeKey]: {
1057a84a85c5S猫头猫                    localPath: urlLike,
1058a84a85c5S猫头猫                },
1059a84a85c5S猫头猫            };
1060a84a85c5S猫头猫        },
106111908939S猫头猫        async getMediaSource(musicItem, quality) {
106211908939S猫头猫            if (quality === 'standard') {
106311908939S猫头猫                return {
106411908939S猫头猫                    url: addFileScheme(musicItem.$?.localPath || musicItem.url),
106511908939S猫头猫                };
106611908939S猫头猫            }
106711908939S猫头猫            return null;
106811908939S猫头猫        },
106974d0cf81S猫头猫    };
107074d0cf81S猫头猫}, '');
10717993f90eS猫头猫localFilePlugin.hash = localPluginHash;
1072927dbe93S猫头猫
1073d5bfeb7eS猫头猫//#endregion
1074d5bfeb7eS猫头猫
1075927dbe93S猫头猫async function setup() {
1076927dbe93S猫头猫    const _plugins: Array<Plugin> = [];
1077927dbe93S猫头猫    try {
1078927dbe93S猫头猫        // 加载插件
1079927dbe93S猫头猫        const pluginsPaths = await readDir(pathConst.pluginPath);
1080927dbe93S猫头猫        for (let i = 0; i < pluginsPaths.length; ++i) {
1081927dbe93S猫头猫            const _pluginUrl = pluginsPaths[i];
10821e263108S猫头猫            trace('初始化插件', _pluginUrl);
10831e263108S猫头猫            if (
10841e263108S猫头猫                _pluginUrl.isFile() &&
10851e263108S猫头猫                (_pluginUrl.name?.endsWith?.('.js') ||
10861e263108S猫头猫                    _pluginUrl.path?.endsWith?.('.js'))
10871e263108S猫头猫            ) {
1088927dbe93S猫头猫                const funcCode = await readFile(_pluginUrl.path, 'utf8');
1089927dbe93S猫头猫                const plugin = new Plugin(funcCode, _pluginUrl.path);
10904060c00aS猫头猫                const _pluginIndex = _plugins.findIndex(
10914060c00aS猫头猫                    p => p.hash === plugin.hash,
10924060c00aS猫头猫                );
1093927dbe93S猫头猫                if (_pluginIndex !== -1) {
1094927dbe93S猫头猫                    // 重复插件,直接忽略
10950c266394S猫头猫                    continue;
1096927dbe93S猫头猫                }
1097927dbe93S猫头猫                plugin.hash !== '' && _plugins.push(plugin);
1098927dbe93S猫头猫            }
1099927dbe93S猫头猫        }
1100927dbe93S猫头猫
1101927dbe93S猫头猫        plugins = _plugins;
1102e08d37a3S猫头猫        /** 初始化meta信息 */
1103c2b3a262S猫头猫        await PluginMeta.setupMeta(plugins.map(_ => _.name));
1104c2b3a262S猫头猫        /** 查看一下是否有禁用的标记 */
1105c2b3a262S猫头猫        const allMeta = PluginMeta.getPluginMetaAll() ?? {};
1106c2b3a262S猫头猫        for (let plugin of plugins) {
1107c2b3a262S猫头猫            if (allMeta[plugin.name]?.enabled === false) {
1108c2b3a262S猫头猫                plugin.state = 'disabled';
1109c2b3a262S猫头猫            }
1110c2b3a262S猫头猫        }
1111c2b3a262S猫头猫        pluginStateMapper.notify();
1112927dbe93S猫头猫    } catch (e: any) {
11134060c00aS猫头猫        ToastAndroid.show(
11144060c00aS猫头猫            `插件初始化失败:${e?.message ?? e}`,
11154060c00aS猫头猫            ToastAndroid.LONG,
11164060c00aS猫头猫        );
11171a5528a0S猫头猫        errorLog('插件初始化失败', e?.message);
1118927dbe93S猫头猫        throw e;
1119927dbe93S猫头猫    }
1120927dbe93S猫头猫}
1121927dbe93S猫头猫
1122e36e2599S猫头猫interface IInstallPluginConfig {
1123e36e2599S猫头猫    notCheckVersion?: boolean;
1124e36e2599S猫头猫}
1125e36e2599S猫头猫
1126740e3947S猫头猫async function installPluginFromRawCode(
1127740e3947S猫头猫    funcCode: string,
1128740e3947S猫头猫    config?: IInstallPluginConfig,
1129740e3947S猫头猫) {
1130740e3947S猫头猫    if (funcCode) {
1131740e3947S猫头猫        const plugin = new Plugin(funcCode, '');
1132740e3947S猫头猫        const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
1133740e3947S猫头猫        if (_pluginIndex !== -1) {
1134740e3947S猫头猫            // 静默忽略
1135740e3947S猫头猫            return plugin;
1136740e3947S猫头猫        }
1137740e3947S猫头猫        const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
1138740e3947S猫头猫        if (oldVersionPlugin && !config?.notCheckVersion) {
1139740e3947S猫头猫            if (
1140740e3947S猫头猫                compare(
1141740e3947S猫头猫                    oldVersionPlugin.instance.version ?? '',
1142740e3947S猫头猫                    plugin.instance.version ?? '',
1143740e3947S猫头猫                    '>',
1144740e3947S猫头猫                )
1145740e3947S猫头猫            ) {
1146740e3947S猫头猫                throw new Error('已安装更新版本的插件');
1147740e3947S猫头猫            }
1148740e3947S猫头猫        }
1149740e3947S猫头猫
1150740e3947S猫头猫        if (plugin.hash !== '') {
1151740e3947S猫头猫            const fn = nanoid();
1152740e3947S猫头猫            if (oldVersionPlugin) {
1153740e3947S猫头猫                plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash);
1154740e3947S猫头猫                try {
1155740e3947S猫头猫                    await unlink(oldVersionPlugin.path);
1156740e3947S猫头猫                } catch {}
1157740e3947S猫头猫            }
1158740e3947S猫头猫            const pluginPath = `${pathConst.pluginPath}${fn}.js`;
1159740e3947S猫头猫            await writeFile(pluginPath, funcCode, 'utf8');
1160740e3947S猫头猫            plugin.path = pluginPath;
1161740e3947S猫头猫            plugins = plugins.concat(plugin);
1162740e3947S猫头猫            pluginStateMapper.notify();
1163740e3947S猫头猫            return plugin;
1164740e3947S猫头猫        }
1165740e3947S猫头猫        throw new Error('插件无法解析!');
1166740e3947S猫头猫    }
1167740e3947S猫头猫}
1168740e3947S猫头猫
1169927dbe93S猫头猫// 安装插件
1170e36e2599S猫头猫async function installPlugin(
1171e36e2599S猫头猫    pluginPath: string,
1172e36e2599S猫头猫    config?: IInstallPluginConfig,
1173e36e2599S猫头猫) {
117422c09412S猫头猫    // if (pluginPath.endsWith('.js')) {
1175927dbe93S猫头猫    const funcCode = await readFile(pluginPath, 'utf8');
1176e36e2599S猫头猫
1177e36e2599S猫头猫    if (funcCode) {
1178927dbe93S猫头猫        const plugin = new Plugin(funcCode, pluginPath);
1179927dbe93S猫头猫        const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
1180927dbe93S猫头猫        if (_pluginIndex !== -1) {
1181e36e2599S猫头猫            // 静默忽略
1182e36e2599S猫头猫            return plugin;
1183927dbe93S猫头猫        }
1184e36e2599S猫头猫        const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
1185e36e2599S猫头猫        if (oldVersionPlugin && !config?.notCheckVersion) {
1186e36e2599S猫头猫            if (
1187e36e2599S猫头猫                compare(
1188e36e2599S猫头猫                    oldVersionPlugin.instance.version ?? '',
1189e36e2599S猫头猫                    plugin.instance.version ?? '',
1190e36e2599S猫头猫                    '>',
1191e36e2599S猫头猫                )
1192e36e2599S猫头猫            ) {
1193e36e2599S猫头猫                throw new Error('已安装更新版本的插件');
1194e36e2599S猫头猫            }
1195e36e2599S猫头猫        }
1196e36e2599S猫头猫
1197927dbe93S猫头猫        if (plugin.hash !== '') {
1198927dbe93S猫头猫            const fn = nanoid();
1199e36e2599S猫头猫            if (oldVersionPlugin) {
1200e36e2599S猫头猫                plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash);
1201e36e2599S猫头猫                try {
1202e36e2599S猫头猫                    await unlink(oldVersionPlugin.path);
1203e36e2599S猫头猫                } catch {}
1204e36e2599S猫头猫            }
1205927dbe93S猫头猫            const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
1206927dbe93S猫头猫            await copyFile(pluginPath, _pluginPath);
1207927dbe93S猫头猫            plugin.path = _pluginPath;
1208927dbe93S猫头猫            plugins = plugins.concat(plugin);
1209927dbe93S猫头猫            pluginStateMapper.notify();
1210a84a85c5S猫头猫            return plugin;
1211927dbe93S猫头猫        }
1212e36e2599S猫头猫        throw new Error('插件无法解析!');
1213927dbe93S猫头猫    }
1214e36e2599S猫头猫    throw new Error('插件无法识别!');
1215c2b3a262S猫头猫}
1216c2b3a262S猫头猫
1217ee781061S猫头猫const reqHeaders = {
1218ee781061S猫头猫    'Cache-Control': 'no-cache',
1219ee781061S猫头猫    Pragma: 'no-cache',
1220ee781061S猫头猫    Expires: '0',
1221ee781061S猫头猫};
1222ee781061S猫头猫
1223c2b3a262S猫头猫async function installPluginFromUrl(
1224c2b3a262S猫头猫    url: string,
1225c2b3a262S猫头猫    config?: IInstallPluginConfig,
1226c2b3a262S猫头猫) {
122758992c6bS猫头猫    try {
1228ee781061S猫头猫        const funcCode = (
1229ee781061S猫头猫            await axios.get(url, {
1230ee781061S猫头猫                headers: reqHeaders,
1231ee781061S猫头猫            })
1232ee781061S猫头猫        ).data;
123358992c6bS猫头猫        if (funcCode) {
123458992c6bS猫头猫            const plugin = new Plugin(funcCode, '');
123558992c6bS猫头猫            const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
123658992c6bS猫头猫            if (_pluginIndex !== -1) {
12378b7ddca8S猫头猫                // 静默忽略
12388b7ddca8S猫头猫                return;
123958992c6bS猫头猫            }
124025c1bd29S猫头猫            const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
1241c2b3a262S猫头猫            if (oldVersionPlugin && !config?.notCheckVersion) {
124225c1bd29S猫头猫                if (
124325c1bd29S猫头猫                    compare(
124425c1bd29S猫头猫                        oldVersionPlugin.instance.version ?? '',
124525c1bd29S猫头猫                        plugin.instance.version ?? '',
124625c1bd29S猫头猫                        '>',
124725c1bd29S猫头猫                    )
124825c1bd29S猫头猫                ) {
124925c1bd29S猫头猫                    throw new Error('已安装更新版本的插件');
125025c1bd29S猫头猫                }
125125c1bd29S猫头猫            }
125225c1bd29S猫头猫
125358992c6bS猫头猫            if (plugin.hash !== '') {
125458992c6bS猫头猫                const fn = nanoid();
125558992c6bS猫头猫                const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
125658992c6bS猫头猫                await writeFile(_pluginPath, funcCode, 'utf8');
125758992c6bS猫头猫                plugin.path = _pluginPath;
125858992c6bS猫头猫                plugins = plugins.concat(plugin);
125925c1bd29S猫头猫                if (oldVersionPlugin) {
126025c1bd29S猫头猫                    plugins = plugins.filter(
126125c1bd29S猫头猫                        _ => _.hash !== oldVersionPlugin.hash,
126225c1bd29S猫头猫                    );
126325c1bd29S猫头猫                    try {
126425c1bd29S猫头猫                        await unlink(oldVersionPlugin.path);
126525c1bd29S猫头猫                    } catch {}
126625c1bd29S猫头猫                }
126758992c6bS猫头猫                pluginStateMapper.notify();
126858992c6bS猫头猫                return;
126958992c6bS猫头猫            }
127074acbfc0S猫头猫            throw new Error('插件无法解析!');
127158992c6bS猫头猫        }
127225c1bd29S猫头猫    } catch (e: any) {
1273ea6d708fS猫头猫        devLog('error', 'URL安装插件失败', e, e?.message);
127458992c6bS猫头猫        errorLog('URL安装插件失败', e);
127525c1bd29S猫头猫        throw new Error(e?.message ?? '');
127658992c6bS猫头猫    }
127758992c6bS猫头猫}
127858992c6bS猫头猫
1279927dbe93S猫头猫/** 卸载插件 */
1280927dbe93S猫头猫async function uninstallPlugin(hash: string) {
1281927dbe93S猫头猫    const targetIndex = plugins.findIndex(_ => _.hash === hash);
1282927dbe93S猫头猫    if (targetIndex !== -1) {
1283927dbe93S猫头猫        try {
128424e5e74aS猫头猫            const pluginName = plugins[targetIndex].name;
1285927dbe93S猫头猫            await unlink(plugins[targetIndex].path);
1286927dbe93S猫头猫            plugins = plugins.filter(_ => _.hash !== hash);
1287927dbe93S猫头猫            pluginStateMapper.notify();
128843eb30bfS猫头猫            // 防止其他重名
128924e5e74aS猫头猫            if (plugins.every(_ => _.name !== pluginName)) {
12905589cdf3S猫头猫                MediaExtra.removeAll(pluginName);
129124e5e74aS猫头猫            }
1292927dbe93S猫头猫        } catch {}
1293927dbe93S猫头猫    }
1294927dbe93S猫头猫}
1295927dbe93S猫头猫
129608882a77S猫头猫async function uninstallAllPlugins() {
129708882a77S猫头猫    await Promise.all(
129808882a77S猫头猫        plugins.map(async plugin => {
129908882a77S猫头猫            try {
130008882a77S猫头猫                const pluginName = plugin.name;
130108882a77S猫头猫                await unlink(plugin.path);
13025589cdf3S猫头猫                MediaExtra.removeAll(pluginName);
130308882a77S猫头猫            } catch (e) {}
130408882a77S猫头猫        }),
130508882a77S猫头猫    );
130608882a77S猫头猫    plugins = [];
130708882a77S猫头猫    pluginStateMapper.notify();
1308e08d37a3S猫头猫
1309e08d37a3S猫头猫    /** 清除空余文件,异步做就可以了 */
1310e08d37a3S猫头猫    readDir(pathConst.pluginPath)
1311e08d37a3S猫头猫        .then(fns => {
1312e08d37a3S猫头猫            fns.forEach(fn => {
1313e08d37a3S猫头猫                unlink(fn.path).catch(emptyFunction);
1314e08d37a3S猫头猫            });
1315e08d37a3S猫头猫        })
1316e08d37a3S猫头猫        .catch(emptyFunction);
131708882a77S猫头猫}
131808882a77S猫头猫
131925c1bd29S猫头猫async function updatePlugin(plugin: Plugin) {
132025c1bd29S猫头猫    const updateUrl = plugin.instance.srcUrl;
132125c1bd29S猫头猫    if (!updateUrl) {
132225c1bd29S猫头猫        throw new Error('没有更新源');
132325c1bd29S猫头猫    }
132425c1bd29S猫头猫    try {
132525c1bd29S猫头猫        await installPluginFromUrl(updateUrl);
132625c1bd29S猫头猫    } catch (e: any) {
132725c1bd29S猫头猫        if (e.message === '插件已安装') {
132825c1bd29S猫头猫            throw new Error('当前已是最新版本');
132925c1bd29S猫头猫        } else {
133025c1bd29S猫头猫            throw e;
133125c1bd29S猫头猫        }
133225c1bd29S猫头猫    }
133325c1bd29S猫头猫}
133425c1bd29S猫头猫
1335927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) {
13362c595535S猫头猫    return getByName(mediaItem?.platform);
1337927dbe93S猫头猫}
1338927dbe93S猫头猫
1339927dbe93S猫头猫function getByHash(hash: string) {
13407993f90eS猫头猫    return hash === localPluginHash
13417993f90eS猫头猫        ? localFilePlugin
13427993f90eS猫头猫        : plugins.find(_ => _.hash === hash);
1343927dbe93S猫头猫}
1344927dbe93S猫头猫
1345927dbe93S猫头猫function getByName(name: string) {
13467993f90eS猫头猫    return name === localPluginPlatform
13470e4173cdS猫头猫        ? localFilePlugin
13480e4173cdS猫头猫        : plugins.find(_ => _.name === name);
1349927dbe93S猫头猫}
1350927dbe93S猫头猫
1351927dbe93S猫头猫function getValidPlugins() {
1352927dbe93S猫头猫    return plugins.filter(_ => _.state === 'enabled');
1353927dbe93S猫头猫}
1354927dbe93S猫头猫
13552b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) {
13562b80a429S猫头猫    return plugins.filter(
13572b80a429S猫头猫        _ =>
13582b80a429S猫头猫            _.state === 'enabled' &&
13592b80a429S猫头猫            _.instance.search &&
136039ac60f7S猫头猫            (supportedSearchType && _.instance.supportedSearchType
136139ac60f7S猫头猫                ? _.instance.supportedSearchType.includes(supportedSearchType)
13622b80a429S猫头猫                : true),
13632b80a429S猫头猫    );
1364efb9da24S猫头猫}
1365efb9da24S猫头猫
13662b80a429S猫头猫function getSortedSearchablePlugins(
13672b80a429S猫头猫    supportedSearchType?: ICommon.SupportMediaType,
13682b80a429S猫头猫) {
13692b80a429S猫头猫    return getSearchablePlugins(supportedSearchType).sort((a, b) =>
1370e08d37a3S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
1371e08d37a3S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
1372e08d37a3S猫头猫        0
1373e08d37a3S猫头猫            ? -1
1374e08d37a3S猫头猫            : 1,
1375e08d37a3S猫头猫    );
1376e08d37a3S猫头猫}
1377e08d37a3S猫头猫
137815feccc1S猫头猫function getTopListsablePlugins() {
137915feccc1S猫头猫    return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists);
138015feccc1S猫头猫}
138115feccc1S猫头猫
138215feccc1S猫头猫function getSortedTopListsablePlugins() {
138315feccc1S猫头猫    return getTopListsablePlugins().sort((a, b) =>
138415feccc1S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
138515feccc1S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
138615feccc1S猫头猫        0
138715feccc1S猫头猫            ? -1
138815feccc1S猫头猫            : 1,
138915feccc1S猫头猫    );
139015feccc1S猫头猫}
139115feccc1S猫头猫
1392ceb900cdS猫头猫function getRecommendSheetablePlugins() {
1393ceb900cdS猫头猫    return plugins.filter(
1394ceb900cdS猫头猫        _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag,
1395ceb900cdS猫头猫    );
1396ceb900cdS猫头猫}
1397ceb900cdS猫头猫
1398ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() {
1399ceb900cdS猫头猫    return getRecommendSheetablePlugins().sort((a, b) =>
1400ceb900cdS猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
1401ceb900cdS猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
1402ceb900cdS猫头猫        0
1403ceb900cdS猫头猫            ? -1
1404ceb900cdS猫头猫            : 1,
1405ceb900cdS猫头猫    );
1406ceb900cdS猫头猫}
1407ceb900cdS猫头猫
1408e08d37a3S猫头猫function useSortedPlugins() {
1409e08d37a3S猫头猫    const _plugins = pluginStateMapper.useMappedState();
1410e08d37a3S猫头猫    const _pluginMetaAll = PluginMeta.usePluginMetaAll();
1411e08d37a3S猫头猫
141234588741S猫头猫    const [sortedPlugins, setSortedPlugins] = useState(
141334588741S猫头猫        [..._plugins].sort((a, b) =>
1414e08d37a3S猫头猫            (_pluginMetaAll[a.name]?.order ?? Infinity) -
1415e08d37a3S猫头猫                (_pluginMetaAll[b.name]?.order ?? Infinity) <
1416e08d37a3S猫头猫            0
1417e08d37a3S猫头猫                ? -1
1418e08d37a3S猫头猫                : 1,
141934588741S猫头猫        ),
1420e08d37a3S猫头猫    );
142134588741S猫头猫
142234588741S猫头猫    useEffect(() => {
1423d4cd40d8S猫头猫        InteractionManager.runAfterInteractions(() => {
142434588741S猫头猫            setSortedPlugins(
142534588741S猫头猫                [..._plugins].sort((a, b) =>
142634588741S猫头猫                    (_pluginMetaAll[a.name]?.order ?? Infinity) -
142734588741S猫头猫                        (_pluginMetaAll[b.name]?.order ?? Infinity) <
142834588741S猫头猫                    0
142934588741S猫头猫                        ? -1
143034588741S猫头猫                        : 1,
143134588741S猫头猫                ),
143234588741S猫头猫            );
1433d4cd40d8S猫头猫        });
143434588741S猫头猫    }, [_plugins, _pluginMetaAll]);
143534588741S猫头猫
143634588741S猫头猫    return sortedPlugins;
1437e08d37a3S猫头猫}
1438e08d37a3S猫头猫
1439c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) {
1440c2b3a262S猫头猫    const target = plugins.find(it => it.hash === plugin.hash);
1441c2b3a262S猫头猫    if (target) {
1442c2b3a262S猫头猫        target.state = enabled ? 'enabled' : 'disabled';
1443c2b3a262S猫头猫        plugins = [...plugins];
1444c2b3a262S猫头猫        pluginStateMapper.notify();
1445c2b3a262S猫头猫        PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled);
1446c2b3a262S猫头猫    }
1447c2b3a262S猫头猫}
1448c2b3a262S猫头猫
1449927dbe93S猫头猫const PluginManager = {
1450927dbe93S猫头猫    setup,
1451927dbe93S猫头猫    installPlugin,
1452740e3947S猫头猫    installPluginFromRawCode,
145358992c6bS猫头猫    installPluginFromUrl,
145425c1bd29S猫头猫    updatePlugin,
1455927dbe93S猫头猫    uninstallPlugin,
1456927dbe93S猫头猫    getByMedia,
1457927dbe93S猫头猫    getByHash,
1458927dbe93S猫头猫    getByName,
1459927dbe93S猫头猫    getValidPlugins,
1460efb9da24S猫头猫    getSearchablePlugins,
1461e08d37a3S猫头猫    getSortedSearchablePlugins,
146215feccc1S猫头猫    getTopListsablePlugins,
1463ceb900cdS猫头猫    getSortedRecommendSheetablePlugins,
146415feccc1S猫头猫    getSortedTopListsablePlugins,
14655276aef9S猫头猫    usePlugins: pluginStateMapper.useMappedState,
1466e08d37a3S猫头猫    useSortedPlugins,
146708882a77S猫头猫    uninstallAllPlugins,
1468c2b3a262S猫头猫    setPluginEnabled,
14695276aef9S猫头猫};
1470927dbe93S猫头猫
1471927dbe93S猫头猫export default PluginManager;
1472