xref: /MusicFree/src/core/pluginManager.ts (revision ab012f7f9483042c2f0de470231e96a189319868)
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';
5133d52955Smaotoumaoimport notImplementedFunction from '@/utils/notImplementedFunction.ts';
52*ab012f7fSmaotoumaoimport deviceInfoModule from "react-native-device-info";
53927dbe93S猫头猫
5461aca335S猫头猫axios.defaults.timeout = 2000;
55927dbe93S猫头猫
56927dbe93S猫头猫const sha256 = CryptoJs.SHA256;
57927dbe93S猫头猫
58cfa0fc07S猫头猫export enum PluginStateCode {
59927dbe93S猫头猫    /** 版本不匹配 */
60927dbe93S猫头猫    VersionNotMatch = 'VERSION NOT MATCH',
61927dbe93S猫头猫    /** 无法解析 */
62927dbe93S猫头猫    CannotParse = 'CANNOT PARSE',
63927dbe93S猫头猫}
64927dbe93S猫头猫
6533d52955Smaotoumaoconst deprecatedCookieManager = {
6633d52955Smaotoumao    get: notImplementedFunction,
6733d52955Smaotoumao    set: notImplementedFunction,
6833d52955Smaotoumao    flush: notImplementedFunction,
6933d52955Smaotoumao};
7033d52955Smaotoumao
719c34d637S猫头猫const packages: Record<string, any> = {
729c34d637S猫头猫    cheerio,
739c34d637S猫头猫    'crypto-js': CryptoJs,
749c34d637S猫头猫    axios,
759c34d637S猫头猫    dayjs,
769c34d637S猫头猫    'big-integer': bigInt,
779c34d637S猫头猫    qs,
789c34d637S猫头猫    he,
7933d52955Smaotoumao    '@react-native-cookies/cookies': deprecatedCookieManager,
8028ccceb7S猫头猫    webdav,
819c34d637S猫头猫};
829c34d637S猫头猫
83b43683eaS猫头猫const _require = (packageName: string) => {
84b43683eaS猫头猫    let pkg = packages[packageName];
85b43683eaS猫头猫    pkg.default = pkg;
86b43683eaS猫头猫    return pkg;
87b43683eaS猫头猫};
889c34d637S猫头猫
8953f8cd8eS猫头猫const _consoleBind = function (
9053f8cd8eS猫头猫    method: 'log' | 'error' | 'info' | 'warn',
9153f8cd8eS猫头猫    ...args: any
9253f8cd8eS猫头猫) {
9353f8cd8eS猫头猫    const fn = console[method];
9453f8cd8eS猫头猫    if (fn) {
9553f8cd8eS猫头猫        fn(...args);
9653f8cd8eS猫头猫        devLog(method, ...args);
9753f8cd8eS猫头猫    }
9853f8cd8eS猫头猫};
9953f8cd8eS猫头猫
10053f8cd8eS猫头猫const _console = {
10153f8cd8eS猫头猫    log: _consoleBind.bind(null, 'log'),
10253f8cd8eS猫头猫    warn: _consoleBind.bind(null, 'warn'),
10353f8cd8eS猫头猫    info: _consoleBind.bind(null, 'info'),
10453f8cd8eS猫头猫    error: _consoleBind.bind(null, 'error'),
10553f8cd8eS猫头猫};
10653f8cd8eS猫头猫
107*ab012f7fSmaotoumaoconst appVersion = deviceInfoModule.getVersion();
108*ab012f7fSmaotoumao
109a7b42a4cS猫头猫function formatAuthUrl(url: string) {
110a7b42a4cS猫头猫    const urlObj = new URL(url);
111a7b42a4cS猫头猫
112a7b42a4cS猫头猫    try {
113a7b42a4cS猫头猫        if (urlObj.username && urlObj.password) {
114a7b42a4cS猫头猫            const auth = `Basic ${Base64.btoa(
115a7b42a4cS猫头猫                `${decodeURIComponent(urlObj.username)}:${decodeURIComponent(
116a7b42a4cS猫头猫                    urlObj.password,
117a7b42a4cS猫头猫                )}`,
118a7b42a4cS猫头猫            )}`;
119a7b42a4cS猫头猫            urlObj.username = '';
120a7b42a4cS猫头猫            urlObj.password = '';
121a7b42a4cS猫头猫
122a7b42a4cS猫头猫            return {
123a7b42a4cS猫头猫                url: urlObj.toString(),
124a7b42a4cS猫头猫                auth,
125a7b42a4cS猫头猫            };
126a7b42a4cS猫头猫        }
127a7b42a4cS猫头猫    } catch (e) {
128a7b42a4cS猫头猫        return {
129a7b42a4cS猫头猫            url,
130a7b42a4cS猫头猫        };
131a7b42a4cS猫头猫    }
132a7b42a4cS猫头猫    return {
133a7b42a4cS猫头猫        url,
134a7b42a4cS猫头猫    };
135a7b42a4cS猫头猫}
136a7b42a4cS猫头猫
137d5bfeb7eS猫头猫//#region 插件类
138927dbe93S猫头猫export class Plugin {
139927dbe93S猫头猫    /** 插件名 */
140927dbe93S猫头猫    public name: string;
141927dbe93S猫头猫    /** 插件的hash,作为唯一id */
142927dbe93S猫头猫    public hash: string;
143927dbe93S猫头猫    /** 插件状态:激活、关闭、错误 */
144927dbe93S猫头猫    public state: 'enabled' | 'disabled' | 'error';
145927dbe93S猫头猫    /** 插件状态信息 */
146927dbe93S猫头猫    public stateCode?: PluginStateCode;
147927dbe93S猫头猫    /** 插件的实例 */
148927dbe93S猫头猫    public instance: IPlugin.IPluginInstance;
149927dbe93S猫头猫    /** 插件路径 */
150927dbe93S猫头猫    public path: string;
151927dbe93S猫头猫    /** 插件方法 */
152927dbe93S猫头猫    public methods: PluginMethods;
153927dbe93S猫头猫
15474d0cf81S猫头猫    constructor(
15574d0cf81S猫头猫        funcCode: string | (() => IPlugin.IPluginInstance),
15674d0cf81S猫头猫        pluginPath: string,
15774d0cf81S猫头猫    ) {
158927dbe93S猫头猫        this.state = 'enabled';
159927dbe93S猫头猫        let _instance: IPlugin.IPluginInstance;
1603b3d6357S猫头猫        const _module: any = {exports: {}};
161927dbe93S猫头猫        try {
16274d0cf81S猫头猫            if (typeof funcCode === 'string') {
163e0caf342S猫头猫                // 插件的环境变量
164e0caf342S猫头猫                const env = {
165e0caf342S猫头猫                    getUserVariables: () => {
166e0caf342S猫头猫                        return (
167e0caf342S猫头猫                            PluginMeta.getPluginMeta(this)?.userVariables ?? {}
168e0caf342S猫头猫                        );
169e0caf342S猫头猫                    },
170*ab012f7fSmaotoumao                    appVersion,
171e3fa9b3cS猫头猫                    os: 'android',
172*ab012f7fSmaotoumao                    lang: 'zh-CN'
173e0caf342S猫头猫                };
174*ab012f7fSmaotoumao                const _process = {
175*ab012f7fSmaotoumao                    platform: 'android',
176*ab012f7fSmaotoumao                    version: appVersion,
177*ab012f7fSmaotoumao                    env,
178*ab012f7fSmaotoumao                }
179e0caf342S猫头猫
1804060c00aS猫头猫                // eslint-disable-next-line no-new-func
181927dbe93S猫头猫                _instance = Function(`
182927dbe93S猫头猫                    'use strict';
183*ab012f7fSmaotoumao                    return function(require, __musicfree_require, module, exports, console, env, URL, process) {
1849c34d637S猫头猫                        ${funcCode}
185927dbe93S猫头猫                    }
186e0caf342S猫头猫                `)()(
187e0caf342S猫头猫                    _require,
188e0caf342S猫头猫                    _require,
189e0caf342S猫头猫                    _module,
190e0caf342S猫头猫                    _module.exports,
191e0caf342S猫头猫                    _console,
192e0caf342S猫头猫                    env,
19391eb8fa8S猫头猫                    URL,
194*ab012f7fSmaotoumao                    _process
195e0caf342S猫头猫                );
1963b3d6357S猫头猫                if (_module.exports.default) {
1973b3d6357S猫头猫                    _instance = _module.exports
1983b3d6357S猫头猫                        .default as IPlugin.IPluginInstance;
1993b3d6357S猫头猫                } else {
2009c34d637S猫头猫                    _instance = _module.exports as IPlugin.IPluginInstance;
2013b3d6357S猫头猫                }
20274d0cf81S猫头猫            } else {
20374d0cf81S猫头猫                _instance = funcCode();
20474d0cf81S猫头猫            }
205c2b3a262S猫头猫            // 插件初始化后的一些操作
20695297592S猫头猫            if (Array.isArray(_instance.userVariables)) {
20795297592S猫头猫                _instance.userVariables = _instance.userVariables.filter(
20895297592S猫头猫                    it => it?.key,
20995297592S猫头猫                );
21095297592S猫头猫            }
211927dbe93S猫头猫            this.checkValid(_instance);
212927dbe93S猫头猫        } catch (e: any) {
213b43683eaS猫头猫            console.log(e);
214927dbe93S猫头猫            this.state = 'error';
215927dbe93S猫头猫            this.stateCode = PluginStateCode.CannotParse;
216927dbe93S猫头猫            if (e?.stateCode) {
217927dbe93S猫头猫                this.stateCode = e.stateCode;
218927dbe93S猫头猫            }
219927dbe93S猫头猫            errorLog(`${pluginPath}插件无法解析 `, {
220927dbe93S猫头猫                stateCode: this.stateCode,
221927dbe93S猫头猫                message: e?.message,
222927dbe93S猫头猫                stack: e?.stack,
223927dbe93S猫头猫            });
224927dbe93S猫头猫            _instance = e?.instance ?? {
225927dbe93S猫头猫                _path: '',
226927dbe93S猫头猫                platform: '',
227927dbe93S猫头猫                appVersion: '',
22820e6a092S猫头猫                async getMediaSource() {
229927dbe93S猫头猫                    return null;
230927dbe93S猫头猫                },
231927dbe93S猫头猫                async search() {
232927dbe93S猫头猫                    return {};
233927dbe93S猫头猫                },
234927dbe93S猫头猫                async getAlbumInfo() {
235927dbe93S猫头猫                    return null;
236927dbe93S猫头猫                },
237927dbe93S猫头猫            };
238927dbe93S猫头猫        }
239927dbe93S猫头猫        this.instance = _instance;
240927dbe93S猫头猫        this.path = pluginPath;
241927dbe93S猫头猫        this.name = _instance.platform;
242ab8941d9S猫头猫        if (
243ab8941d9S猫头猫            this.instance.platform === '' ||
244ab8941d9S猫头猫            this.instance.platform === undefined
245ab8941d9S猫头猫        ) {
246927dbe93S猫头猫            this.hash = '';
247927dbe93S猫头猫        } else {
24874d0cf81S猫头猫            if (typeof funcCode === 'string') {
249927dbe93S猫头猫                this.hash = sha256(funcCode).toString();
25074d0cf81S猫头猫            } else {
25174d0cf81S猫头猫                this.hash = sha256(funcCode.toString()).toString();
25274d0cf81S猫头猫            }
253927dbe93S猫头猫        }
254927dbe93S猫头猫
255927dbe93S猫头猫        // 放在最后
256927dbe93S猫头猫        this.methods = new PluginMethods(this);
257927dbe93S猫头猫    }
258927dbe93S猫头猫
259927dbe93S猫头猫    private checkValid(_instance: IPlugin.IPluginInstance) {
260927dbe93S猫头猫        /** 版本号校验 */
261927dbe93S猫头猫        if (
262927dbe93S猫头猫            _instance.appVersion &&
263927dbe93S猫头猫            !satisfies(DeviceInfo.getVersion(), _instance.appVersion)
264927dbe93S猫头猫        ) {
265927dbe93S猫头猫            throw {
266927dbe93S猫头猫                instance: _instance,
267927dbe93S猫头猫                stateCode: PluginStateCode.VersionNotMatch,
268927dbe93S猫头猫            };
269927dbe93S猫头猫        }
270927dbe93S猫头猫        return true;
271927dbe93S猫头猫    }
272927dbe93S猫头猫}
27342a9f3e6Smaotoumao
274d5bfeb7eS猫头猫//#endregion
275927dbe93S猫头猫
276d5bfeb7eS猫头猫//#region 基于插件类封装的方法,供给APP侧直接调用
277927dbe93S猫头猫/** 有缓存等信息 */
278927dbe93S猫头猫class PluginMethods implements IPlugin.IPluginInstanceMethods {
279927dbe93S猫头猫    private plugin;
28042a9f3e6Smaotoumao
281927dbe93S猫头猫    constructor(plugin: Plugin) {
282927dbe93S猫头猫        this.plugin = plugin;
283927dbe93S猫头猫    }
28442a9f3e6Smaotoumao
285927dbe93S猫头猫    /** 搜索 */
286927dbe93S猫头猫    async search<T extends ICommon.SupportMediaType>(
287927dbe93S猫头猫        query: string,
288927dbe93S猫头猫        page: number,
289927dbe93S猫头猫        type: T,
290927dbe93S猫头猫    ): Promise<IPlugin.ISearchResult<T>> {
291927dbe93S猫头猫        if (!this.plugin.instance.search) {
292927dbe93S猫头猫            return {
293927dbe93S猫头猫                isEnd: true,
294927dbe93S猫头猫                data: [],
295927dbe93S猫头猫            };
296927dbe93S猫头猫        }
297927dbe93S猫头猫
2984060c00aS猫头猫        const result =
2994060c00aS猫头猫            (await this.plugin.instance.search(query, page, type)) ?? {};
300927dbe93S猫头猫        if (Array.isArray(result.data)) {
301927dbe93S猫头猫            result.data.forEach(_ => {
302927dbe93S猫头猫                resetMediaItem(_, this.plugin.name);
303927dbe93S猫头猫            });
304927dbe93S猫头猫            return {
305927dbe93S猫头猫                isEnd: result.isEnd ?? true,
306927dbe93S猫头猫                data: result.data,
307927dbe93S猫头猫            };
308927dbe93S猫头猫        }
309927dbe93S猫头猫        return {
310927dbe93S猫头猫            isEnd: true,
311927dbe93S猫头猫            data: [],
312927dbe93S猫头猫        };
313927dbe93S猫头猫    }
314927dbe93S猫头猫
315927dbe93S猫头猫    /** 获取真实源 */
31620e6a092S猫头猫    async getMediaSource(
317927dbe93S猫头猫        musicItem: IMusic.IMusicItemBase,
318abaede57S猫头猫        quality: IMusic.IQualityKey = 'standard',
319927dbe93S猫头猫        retryCount = 1,
320dc160d50S猫头猫        notUpdateCache = false,
321192ae2b0S猫头猫    ): Promise<IPlugin.IMediaSourceResult | null> {
322927dbe93S猫头猫        // 1. 本地搜索 其实直接读mediameta就好了
32343eb30bfS猫头猫        const mediaExtra = MediaExtra.get(musicItem);
324927dbe93S猫头猫        const localPath =
32543eb30bfS猫头猫            mediaExtra?.localPath ||
32643eb30bfS猫头猫            getInternalData<string>(musicItem, InternalDataType.LOCALPATH) ||
3270e4173cdS猫头猫            getInternalData<string>(
3280e4173cdS猫头猫                LocalMusicSheet.isLocalMusic(musicItem),
3290e4173cdS猫头猫                InternalDataType.LOCALPATH,
3300e4173cdS猫头猫            );
33142a9f3e6Smaotoumao        if (localPath && (await exists(localPath))) {
3320e4173cdS猫头猫            trace('本地播放', localPath);
33343eb30bfS猫头猫            if (mediaExtra && mediaExtra.localPath !== localPath) {
33443eb30bfS猫头猫                // 修正一下本地数据
33543eb30bfS猫头猫                MediaExtra.update(musicItem, {
33643eb30bfS猫头猫                    localPath,
33743eb30bfS猫头猫                });
33843eb30bfS猫头猫            }
339927dbe93S猫头猫            return {
34011908939S猫头猫                url: addFileScheme(localPath),
341927dbe93S猫头猫            };
34243eb30bfS猫头猫        } else if (mediaExtra?.localPath) {
34343eb30bfS猫头猫            MediaExtra.update(musicItem, {
34443eb30bfS猫头猫                localPath: undefined,
34543eb30bfS猫头猫            });
346927dbe93S猫头猫        }
347a84a85c5S猫头猫
3487993f90eS猫头猫        if (musicItem.platform === localPluginPlatform) {
349f5935920S猫头猫            throw new Error('本地音乐不存在');
350f5935920S猫头猫        }
351927dbe93S猫头猫        // 2. 缓存播放
35243eb30bfS猫头猫        const mediaCache = MediaCache.getMediaCache(
35343eb30bfS猫头猫            musicItem,
35443eb30bfS猫头猫        ) as IMusic.IMusicItem | null;
355985f8e75S猫头猫        const pluginCacheControl =
356985f8e75S猫头猫            this.plugin.instance.cacheControl ?? 'no-cache';
357cfa0fc07S猫头猫        if (
358cfa0fc07S猫头猫            mediaCache &&
35943eb30bfS猫头猫            mediaCache?.source?.[quality]?.url &&
36048f4b873S猫头猫            (pluginCacheControl === CacheControl.Cache ||
36148f4b873S猫头猫                (pluginCacheControl === CacheControl.NoCache &&
362ef714860S猫头猫                    Network.isOffline()))
363cfa0fc07S猫头猫        ) {
3645276aef9S猫头猫            trace('播放', '缓存播放');
36543eb30bfS猫头猫            const qualityInfo = mediaCache.source[quality];
366927dbe93S猫头猫            return {
36743eb30bfS猫头猫                url: qualityInfo!.url,
368927dbe93S猫头猫                headers: mediaCache.headers,
3694060c00aS猫头猫                userAgent:
3704060c00aS猫头猫                    mediaCache.userAgent ?? mediaCache.headers?.['user-agent'],
371927dbe93S猫头猫            };
372927dbe93S猫头猫        }
373927dbe93S猫头猫        // 3. 插件解析
37420e6a092S猫头猫        if (!this.plugin.instance.getMediaSource) {
375a7b42a4cS猫头猫            const {url, auth} = formatAuthUrl(
376a7b42a4cS猫头猫                musicItem?.qualities?.[quality]?.url ?? musicItem.url,
377a7b42a4cS猫头猫            );
378a7b42a4cS猫头猫            return {
379a7b42a4cS猫头猫                url: url,
380a7b42a4cS猫头猫                headers: auth
381a7b42a4cS猫头猫                    ? {
382a7b42a4cS猫头猫                          Authorization: auth,
383a7b42a4cS猫头猫                      }
384a7b42a4cS猫头猫                    : undefined,
385a7b42a4cS猫头猫            };
386927dbe93S猫头猫        }
387927dbe93S猫头猫        try {
388abaede57S猫头猫            const {url, headers} = (await this.plugin.instance.getMediaSource(
389abaede57S猫头猫                musicItem,
390abaede57S猫头猫                quality,
391abaede57S猫头猫            )) ?? {url: musicItem?.qualities?.[quality]?.url};
392927dbe93S猫头猫            if (!url) {
393a28eac61S猫头猫                throw new Error('NOT RETRY');
394927dbe93S猫头猫            }
3955276aef9S猫头猫            trace('播放', '插件播放');
396927dbe93S猫头猫            const result = {
397927dbe93S猫头猫                url,
398927dbe93S猫头猫                headers,
399927dbe93S猫头猫                userAgent: headers?.['user-agent'],
400cfa0fc07S猫头猫            } as IPlugin.IMediaSourceResult;
401a7b42a4cS猫头猫            const authFormattedResult = formatAuthUrl(result.url!);
402a7b42a4cS猫头猫            if (authFormattedResult.auth) {
403a7b42a4cS猫头猫                result.url = authFormattedResult.url;
404a7b42a4cS猫头猫                result.headers = {
405a7b42a4cS猫头猫                    ...(result.headers ?? {}),
406a7b42a4cS猫头猫                    Authorization: authFormattedResult.auth,
407a7b42a4cS猫头猫                };
408a7b42a4cS猫头猫            }
409927dbe93S猫头猫
410dc160d50S猫头猫            if (
411dc160d50S猫头猫                pluginCacheControl !== CacheControl.NoStore &&
412dc160d50S猫头猫                !notUpdateCache
413dc160d50S猫头猫            ) {
41443eb30bfS猫头猫                // 更新缓存
41543eb30bfS猫头猫                const cacheSource = {
41643eb30bfS猫头猫                    headers: result.headers,
41743eb30bfS猫头猫                    userAgent: result.userAgent,
41843eb30bfS猫头猫                    url,
41943eb30bfS猫头猫                };
42043eb30bfS猫头猫                let realMusicItem = {
42143eb30bfS猫头猫                    ...musicItem,
42243eb30bfS猫头猫                    ...(mediaCache || {}),
42343eb30bfS猫头猫                };
42443eb30bfS猫头猫                realMusicItem.source = {
42543eb30bfS猫头猫                    ...(realMusicItem.source || {}),
42643eb30bfS猫头猫                    [quality]: cacheSource,
42743eb30bfS猫头猫                };
42843eb30bfS猫头猫
42943eb30bfS猫头猫                MediaCache.setMediaCache(realMusicItem);
430752ffc5aS猫头猫            }
431927dbe93S猫头猫            return result;
432927dbe93S猫头猫        } catch (e: any) {
433a28eac61S猫头猫            if (retryCount > 0 && e?.message !== 'NOT RETRY') {
434927dbe93S猫头猫                await delay(150);
435abaede57S猫头猫                return this.getMediaSource(musicItem, quality, --retryCount);
436927dbe93S猫头猫            }
437927dbe93S猫头猫            errorLog('获取真实源失败', e?.message);
438ea6d708fS猫头猫            devLog('error', '获取真实源失败', e, e?.message);
439192ae2b0S猫头猫            return null;
440927dbe93S猫头猫        }
441927dbe93S猫头猫    }
442927dbe93S猫头猫
443927dbe93S猫头猫    /** 获取音乐详情 */
444927dbe93S猫头猫    async getMusicInfo(
445927dbe93S猫头猫        musicItem: ICommon.IMediaBase,
44674d0cf81S猫头猫    ): Promise<Partial<IMusic.IMusicItem> | null> {
447927dbe93S猫头猫        if (!this.plugin.instance.getMusicInfo) {
448d704daedS猫头猫            return null;
449927dbe93S猫头猫        }
45074d0cf81S猫头猫        try {
451927dbe93S猫头猫            return (
452927dbe93S猫头猫                this.plugin.instance.getMusicInfo(
4537993f90eS猫头猫                    resetMediaItem(musicItem, undefined, true),
454d704daedS猫头猫                ) ?? null
455927dbe93S猫头猫            );
456ea6d708fS猫头猫        } catch (e: any) {
457ea6d708fS猫头猫            devLog('error', '获取音乐详情失败', e, e?.message);
458d704daedS猫头猫            return null;
45974d0cf81S猫头猫        }
460927dbe93S猫头猫    }
461927dbe93S猫头猫
4627e883dbbS猫头猫    /**
4637e883dbbS猫头猫     *
4647e883dbbS猫头猫     * getLyric(musicItem) => {
4657e883dbbS猫头猫     *      lyric: string;
4667e883dbbS猫头猫     *      trans: string;
4677e883dbbS猫头猫     * }
4687e883dbbS猫头猫     *
4697e883dbbS猫头猫     */
470927dbe93S猫头猫    /** 获取歌词 */
471927dbe93S猫头猫    async getLyric(
47243eb30bfS猫头猫        originalMusicItem: IMusic.IMusicItemBase,
473927dbe93S猫头猫    ): Promise<ILyric.ILyricSource | null> {
47443eb30bfS猫头猫        // 1.额外存储的meta信息(关联歌词)
4755589cdf3S猫头猫        const meta = MediaExtra.get(originalMusicItem);
47643eb30bfS猫头猫        let musicItem: IMusic.IMusicItem;
477927dbe93S猫头猫        if (meta && meta.associatedLrc) {
47843eb30bfS猫头猫            musicItem = meta.associatedLrc as IMusic.IMusicItem;
47943eb30bfS猫头猫        } else {
48043eb30bfS猫头猫            musicItem = originalMusicItem as IMusic.IMusicItem;
481927dbe93S猫头猫        }
48243eb30bfS猫头猫
48343eb30bfS猫头猫        const musicItemCache = MediaCache.getMediaCache(
4847a91f04fS猫头猫            musicItem,
48543eb30bfS猫头猫        ) as IMusic.IMusicItemCache | null;
48643eb30bfS猫头猫
48743eb30bfS猫头猫        /** 原始歌词文本 */
4887e883dbbS猫头猫        let rawLrc: string | null = musicItem.rawLrc || null;
4897e883dbbS猫头猫        let translation: string | null = null;
49043eb30bfS猫头猫
49126f32636S猫头猫        // 2. 本地手动设置的歌词
49226f32636S猫头猫        const platformHash = CryptoJs.MD5(musicItem.platform).toString(
49326f32636S猫头猫            CryptoJs.enc.Hex,
49426f32636S猫头猫        );
49526f32636S猫头猫        const idHash = CryptoJs.MD5(musicItem.id).toString(CryptoJs.enc.Hex);
49626f32636S猫头猫        if (
49726f32636S猫头猫            await RNFS.exists(
49826f32636S猫头猫                pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc',
49926f32636S猫头猫            )
50026f32636S猫头猫        ) {
50126f32636S猫头猫            rawLrc = await RNFS.readFile(
50226f32636S猫头猫                pathConst.localLrcPath + platformHash + '/' + idHash + '.lrc',
50326f32636S猫头猫                'utf8',
50426f32636S猫头猫            );
50526f32636S猫头猫
50626f32636S猫头猫            if (
50726f32636S猫头猫                await RNFS.exists(
50826f32636S猫头猫                    pathConst.localLrcPath +
50926f32636S猫头猫                        platformHash +
51026f32636S猫头猫                        '/' +
51126f32636S猫头猫                        idHash +
51226f32636S猫头猫                        '.tran.lrc',
51326f32636S猫头猫                )
51426f32636S猫头猫            ) {
51526f32636S猫头猫                translation =
51626f32636S猫头猫                    (await RNFS.readFile(
51726f32636S猫头猫                        pathConst.localLrcPath +
51826f32636S猫头猫                            platformHash +
51926f32636S猫头猫                            '/' +
52026f32636S猫头猫                            idHash +
52126f32636S猫头猫                            '.tran.lrc',
52226f32636S猫头猫                        'utf8',
52326f32636S猫头猫                    )) || null;
52426f32636S猫头猫            }
52526f32636S猫头猫
52626f32636S猫头猫            return {
52726f32636S猫头猫                rawLrc,
52826f32636S猫头猫                translation: translation || undefined, // TODO: 这里写的不好
52926f32636S猫头猫            };
53026f32636S猫头猫        }
53126f32636S猫头猫
53243eb30bfS猫头猫        // 2. 缓存歌词 / 对象上本身的歌词
53343eb30bfS猫头猫        if (musicItemCache?.lyric) {
53443eb30bfS猫头猫            // 缓存的远程结果
53543eb30bfS猫头猫            let cacheLyric: ILyric.ILyricSource | null =
53643eb30bfS猫头猫                musicItemCache.lyric || null;
53743eb30bfS猫头猫            // 缓存的本地结果
53843eb30bfS猫头猫            let localLyric: ILyric.ILyricSource | null =
53943eb30bfS猫头猫                musicItemCache.$localLyric || null;
54043eb30bfS猫头猫
5417e883dbbS猫头猫            // 优先用缓存的结果
5427e883dbbS猫头猫            if (cacheLyric.rawLrc || cacheLyric.translation) {
54343eb30bfS猫头猫                return {
5447e883dbbS猫头猫                    rawLrc: cacheLyric.rawLrc,
5457e883dbbS猫头猫                    translation: cacheLyric.translation,
54643eb30bfS猫头猫                };
54743eb30bfS猫头猫            }
54843eb30bfS猫头猫
5497e883dbbS猫头猫            // 本地其实是缓存的路径
5507e883dbbS猫头猫            if (localLyric) {
5517e883dbbS猫头猫                let needRefetch = false;
5527e883dbbS猫头猫                if (localLyric.rawLrc && (await exists(localLyric.rawLrc))) {
5537e883dbbS猫头猫                    rawLrc = await readFile(localLyric.rawLrc, 'utf8');
5547e883dbbS猫头猫                } else if (localLyric.rawLrc) {
5557e883dbbS猫头猫                    needRefetch = true;
5567e883dbbS猫头猫                }
5577e883dbbS猫头猫                if (
5587e883dbbS猫头猫                    localLyric.translation &&
5597e883dbbS猫头猫                    (await exists(localLyric.translation))
5607e883dbbS猫头猫                ) {
5617e883dbbS猫头猫                    translation = await readFile(
5627e883dbbS猫头猫                        localLyric.translation,
5637e883dbbS猫头猫                        'utf8',
5647e883dbbS猫头猫                    );
5657e883dbbS猫头猫                } else if (localLyric.translation) {
5667e883dbbS猫头猫                    needRefetch = true;
56743eb30bfS猫头猫                }
56843eb30bfS猫头猫
5697e883dbbS猫头猫                if (!needRefetch && (rawLrc || translation)) {
57043eb30bfS猫头猫                    return {
5717e883dbbS猫头猫                        rawLrc: rawLrc || undefined,
5727e883dbbS猫头猫                        translation: translation || undefined,
57343eb30bfS猫头猫                    };
57443eb30bfS猫头猫                }
57543eb30bfS猫头猫            }
57643eb30bfS猫头猫        }
57743eb30bfS猫头猫
57843eb30bfS猫头猫        // 3. 无缓存歌词/无自带歌词/无本地歌词
57943eb30bfS猫头猫        let lrcSource: ILyric.ILyricSource | null;
58043eb30bfS猫头猫        if (isSameMediaItem(originalMusicItem, musicItem)) {
58143eb30bfS猫头猫            lrcSource =
58243eb30bfS猫头猫                (await this.plugin.instance
5837e883dbbS猫头猫                    ?.getLyric?.(resetMediaItem(musicItem, undefined, true))
58443eb30bfS猫头猫                    ?.catch(() => null)) || null;
58543eb30bfS猫头猫        } else {
58643eb30bfS猫头猫            lrcSource =
58743eb30bfS猫头猫                (await PluginManager.getByMedia(musicItem)
58843eb30bfS猫头猫                    ?.instance?.getLyric?.(
58943eb30bfS猫头猫                        resetMediaItem(musicItem, undefined, true),
59043eb30bfS猫头猫                    )
59143eb30bfS猫头猫                    ?.catch(() => null)) || null;
59243eb30bfS猫头猫        }
59343eb30bfS猫头猫
59443eb30bfS猫头猫        if (lrcSource) {
5957e883dbbS猫头猫            rawLrc = lrcSource?.rawLrc || rawLrc;
5967e883dbbS猫头猫            translation = lrcSource?.translation || null;
59743eb30bfS猫头猫
5987e883dbbS猫头猫            const deprecatedLrcUrl = lrcSource?.lrc || musicItem.lrc;
59943eb30bfS猫头猫
6007e883dbbS猫头猫            // 本地的文件名
6017e883dbbS猫头猫            let filename: string | undefined = `${
6027e883dbbS猫头猫                pathConst.lrcCachePath
6037e883dbbS猫头猫            }${nanoid()}.lrc`;
6047e883dbbS猫头猫            let filenameTrans: string | undefined = `${
6057e883dbbS猫头猫                pathConst.lrcCachePath
6067e883dbbS猫头猫            }${nanoid()}.lrc`;
60743eb30bfS猫头猫
6087e883dbbS猫头猫            // 旧版本兼容
6097e883dbbS猫头猫            if (!(rawLrc || translation)) {
6107e883dbbS猫头猫                if (deprecatedLrcUrl) {
61143eb30bfS猫头猫                    rawLrc = (
6127e883dbbS猫头猫                        await axios
6137e883dbbS猫头猫                            .get(deprecatedLrcUrl, {timeout: 3000})
6147e883dbbS猫头猫                            .catch(() => null)
61543eb30bfS猫头猫                    )?.data;
6167e883dbbS猫头猫                } else if (musicItem.rawLrc) {
6177e883dbbS猫头猫                    rawLrc = musicItem.rawLrc;
6187e883dbbS猫头猫                }
61943eb30bfS猫头猫            }
62043eb30bfS猫头猫
62143eb30bfS猫头猫            if (rawLrc) {
62243eb30bfS猫头猫                await writeFile(filename, rawLrc, 'utf8');
62343eb30bfS猫头猫            } else {
6247e883dbbS猫头猫                filename = undefined;
6257e883dbbS猫头猫            }
6267e883dbbS猫头猫            if (translation) {
6277e883dbbS猫头猫                await writeFile(filenameTrans, translation, 'utf8');
6287e883dbbS猫头猫            } else {
6297e883dbbS猫头猫                filenameTrans = undefined;
6307a91f04fS猫头猫            }
6317a91f04fS猫头猫
6327e883dbbS猫头猫            if (rawLrc || translation) {
6337e883dbbS猫头猫                MediaCache.setMediaCache(
6347e883dbbS猫头猫                    produce(musicItemCache || musicItem, draft => {
6357e883dbbS猫头猫                        musicItemCache?.$localLyric?.rawLrc;
6367e883dbbS猫头猫                        objectPath.set(draft, '$localLyric.rawLrc', filename);
63743eb30bfS猫头猫                        objectPath.set(
63843eb30bfS猫头猫                            draft,
6397e883dbbS猫头猫                            '$localLyric.translation',
6407e883dbbS猫头猫                            filenameTrans,
64143eb30bfS猫头猫                        );
64243eb30bfS猫头猫                        return draft;
64343eb30bfS猫头猫                    }),
64443eb30bfS猫头猫                );
645927dbe93S猫头猫                return {
6467e883dbbS猫头猫                    rawLrc: rawLrc || undefined,
6477e883dbbS猫头猫                    translation: translation || undefined,
648927dbe93S猫头猫                };
649927dbe93S猫头猫            }
650927dbe93S猫头猫        }
65143eb30bfS猫头猫
6523a6f67b1S猫头猫        // 6. 如果是本地文件
65343eb30bfS猫头猫        const isDownloaded = LocalMusicSheet.isLocalMusic(originalMusicItem);
65443eb30bfS猫头猫        if (
65543eb30bfS猫头猫            originalMusicItem.platform !== localPluginPlatform &&
65643eb30bfS猫头猫            isDownloaded
65743eb30bfS猫头猫        ) {
6587e883dbbS猫头猫            const res = await localFilePlugin.instance!.getLyric!(isDownloaded);
65943eb30bfS猫头猫
66043eb30bfS猫头猫            console.log('本地文件歌词');
66143eb30bfS猫头猫
6623a6f67b1S猫头猫            if (res) {
6633a6f67b1S猫头猫                return res;
6643a6f67b1S猫头猫            }
6653a6f67b1S猫头猫        }
666ea6d708fS猫头猫        devLog('warn', '无歌词');
667927dbe93S猫头猫
668927dbe93S猫头猫        return null;
669927dbe93S猫头猫    }
670927dbe93S猫头猫
671927dbe93S猫头猫    /** 获取歌词文本 */
672927dbe93S猫头猫    async getLyricText(
673927dbe93S猫头猫        musicItem: IMusic.IMusicItem,
674927dbe93S猫头猫    ): Promise<string | undefined> {
6757e883dbbS猫头猫        return (await this.getLyric(musicItem))?.rawLrc;
676927dbe93S猫头猫    }
677927dbe93S猫头猫
678927dbe93S猫头猫    /** 获取专辑信息 */
679927dbe93S猫头猫    async getAlbumInfo(
680927dbe93S猫头猫        albumItem: IAlbum.IAlbumItemBase,
681f9afcc0dS猫头猫        page: number = 1,
682f9afcc0dS猫头猫    ): Promise<IPlugin.IAlbumInfoResult | null> {
683927dbe93S猫头猫        if (!this.plugin.instance.getAlbumInfo) {
684f9afcc0dS猫头猫            return {
685f9afcc0dS猫头猫                albumItem,
686f2a4767cS猫头猫                musicList: (albumItem?.musicList ?? []).map(
687f2a4767cS猫头猫                    resetMediaItem,
688f2a4767cS猫头猫                    this.plugin.name,
689f2a4767cS猫头猫                    true,
690f2a4767cS猫头猫                ),
691f9afcc0dS猫头猫                isEnd: true,
692f9afcc0dS猫头猫            };
693927dbe93S猫头猫        }
694927dbe93S猫头猫        try {
695927dbe93S猫头猫            const result = await this.plugin.instance.getAlbumInfo(
696927dbe93S猫头猫                resetMediaItem(albumItem, undefined, true),
697f9afcc0dS猫头猫                page,
698927dbe93S猫头猫            );
6995276aef9S猫头猫            if (!result) {
7005276aef9S猫头猫                throw new Error();
7015276aef9S猫头猫            }
702927dbe93S猫头猫            result?.musicList?.forEach(_ => {
703927dbe93S猫头猫                resetMediaItem(_, this.plugin.name);
70496744680S猫头猫                _.album = albumItem.title;
705927dbe93S猫头猫            });
7065276aef9S猫头猫
707f9afcc0dS猫头猫            if (page <= 1) {
708f9afcc0dS猫头猫                // 合并信息
709f9afcc0dS猫头猫                return {
710f9afcc0dS猫头猫                    albumItem: {...albumItem, ...(result?.albumItem ?? {})},
711f9afcc0dS猫头猫                    isEnd: result.isEnd === false ? false : true,
712f9afcc0dS猫头猫                    musicList: result.musicList,
713f9afcc0dS猫头猫                };
714f9afcc0dS猫头猫            } else {
715f9afcc0dS猫头猫                return {
716f9afcc0dS猫头猫                    isEnd: result.isEnd === false ? false : true,
717f9afcc0dS猫头猫                    musicList: result.musicList,
718f9afcc0dS猫头猫                };
719f9afcc0dS猫头猫            }
7204394410dS猫头猫        } catch (e: any) {
7214394410dS猫头猫            trace('获取专辑信息失败', e?.message);
722ea6d708fS猫头猫            devLog('error', '获取专辑信息失败', e, e?.message);
723ea6d708fS猫头猫
724f9afcc0dS猫头猫            return null;
725927dbe93S猫头猫        }
726927dbe93S猫头猫    }
727927dbe93S猫头猫
7285830c002S猫头猫    /** 获取歌单信息 */
7295830c002S猫头猫    async getMusicSheetInfo(
7305830c002S猫头猫        sheetItem: IMusic.IMusicSheetItem,
7315830c002S猫头猫        page: number = 1,
7325830c002S猫头猫    ): Promise<IPlugin.ISheetInfoResult | null> {
7335281926bS猫头猫        if (!this.plugin.instance.getMusicSheetInfo) {
7345830c002S猫头猫            return {
7355830c002S猫头猫                sheetItem,
7365830c002S猫头猫                musicList: sheetItem?.musicList ?? [],
7375830c002S猫头猫                isEnd: true,
7385830c002S猫头猫            };
7395830c002S猫头猫        }
7405830c002S猫头猫        try {
7415830c002S猫头猫            const result = await this.plugin.instance?.getMusicSheetInfo?.(
7425830c002S猫头猫                resetMediaItem(sheetItem, undefined, true),
7435830c002S猫头猫                page,
7445830c002S猫头猫            );
7455830c002S猫头猫            if (!result) {
7465830c002S猫头猫                throw new Error();
7475830c002S猫头猫            }
7485830c002S猫头猫            result?.musicList?.forEach(_ => {
7495830c002S猫头猫                resetMediaItem(_, this.plugin.name);
7505830c002S猫头猫            });
7515830c002S猫头猫
7525830c002S猫头猫            if (page <= 1) {
7535830c002S猫头猫                // 合并信息
7545830c002S猫头猫                return {
7555830c002S猫头猫                    sheetItem: {...sheetItem, ...(result?.sheetItem ?? {})},
7565830c002S猫头猫                    isEnd: result.isEnd === false ? false : true,
7575830c002S猫头猫                    musicList: result.musicList,
7585830c002S猫头猫                };
7595830c002S猫头猫            } else {
7605830c002S猫头猫                return {
7615830c002S猫头猫                    isEnd: result.isEnd === false ? false : true,
7625830c002S猫头猫                    musicList: result.musicList,
7635830c002S猫头猫                };
7645830c002S猫头猫            }
7655830c002S猫头猫        } catch (e: any) {
7665830c002S猫头猫            trace('获取歌单信息失败', e, e?.message);
7675830c002S猫头猫            devLog('error', '获取歌单信息失败', e, e?.message);
7685830c002S猫头猫
7695830c002S猫头猫            return null;
7705830c002S猫头猫        }
7715830c002S猫头猫    }
7725830c002S猫头猫
773927dbe93S猫头猫    /** 查询作者信息 */
774efb9da24S猫头猫    async getArtistWorks<T extends IArtist.ArtistMediaType>(
775927dbe93S猫头猫        artistItem: IArtist.IArtistItem,
776927dbe93S猫头猫        page: number,
777927dbe93S猫头猫        type: T,
778927dbe93S猫头猫    ): Promise<IPlugin.ISearchResult<T>> {
779efb9da24S猫头猫        if (!this.plugin.instance.getArtistWorks) {
780927dbe93S猫头猫            return {
781927dbe93S猫头猫                isEnd: true,
782927dbe93S猫头猫                data: [],
783927dbe93S猫头猫            };
784927dbe93S猫头猫        }
785927dbe93S猫头猫        try {
786efb9da24S猫头猫            const result = await this.plugin.instance.getArtistWorks(
787927dbe93S猫头猫                artistItem,
788927dbe93S猫头猫                page,
789927dbe93S猫头猫                type,
790927dbe93S猫头猫            );
791927dbe93S猫头猫            if (!result.data) {
792927dbe93S猫头猫                return {
793927dbe93S猫头猫                    isEnd: true,
794927dbe93S猫头猫                    data: [],
795927dbe93S猫头猫                };
796927dbe93S猫头猫            }
797927dbe93S猫头猫            result.data?.forEach(_ => resetMediaItem(_, this.plugin.name));
798927dbe93S猫头猫            return {
799927dbe93S猫头猫                isEnd: result.isEnd ?? true,
800927dbe93S猫头猫                data: result.data,
801927dbe93S猫头猫            };
8024394410dS猫头猫        } catch (e: any) {
8034394410dS猫头猫            trace('查询作者信息失败', e?.message);
804ea6d708fS猫头猫            devLog('error', '查询作者信息失败', e, e?.message);
805ea6d708fS猫头猫
806927dbe93S猫头猫            throw e;
807927dbe93S猫头猫        }
808927dbe93S猫头猫    }
80908380090S猫头猫
81008380090S猫头猫    /** 导入歌单 */
81108380090S猫头猫    async importMusicSheet(urlLike: string): Promise<IMusic.IMusicItem[]> {
81208380090S猫头猫        try {
81308380090S猫头猫            const result =
81408380090S猫头猫                (await this.plugin.instance?.importMusicSheet?.(urlLike)) ?? [];
81508380090S猫头猫            result.forEach(_ => resetMediaItem(_, this.plugin.name));
81608380090S猫头猫            return result;
817ea6d708fS猫头猫        } catch (e: any) {
8180e4173cdS猫头猫            console.log(e);
819ea6d708fS猫头猫            devLog('error', '导入歌单失败', e, e?.message);
820ea6d708fS猫头猫
82108380090S猫头猫            return [];
82208380090S猫头猫        }
82308380090S猫头猫    }
82442a9f3e6Smaotoumao
8254d9d3c4cS猫头猫    /** 导入单曲 */
8264d9d3c4cS猫头猫    async importMusicItem(urlLike: string): Promise<IMusic.IMusicItem | null> {
8274d9d3c4cS猫头猫        try {
8284d9d3c4cS猫头猫            const result = await this.plugin.instance?.importMusicItem?.(
8294d9d3c4cS猫头猫                urlLike,
8304d9d3c4cS猫头猫            );
8314d9d3c4cS猫头猫            if (!result) {
8324d9d3c4cS猫头猫                throw new Error();
8334d9d3c4cS猫头猫            }
8344d9d3c4cS猫头猫            resetMediaItem(result, this.plugin.name);
8354d9d3c4cS猫头猫            return result;
836ea6d708fS猫头猫        } catch (e: any) {
837ea6d708fS猫头猫            devLog('error', '导入单曲失败', e, e?.message);
838ea6d708fS猫头猫
8394d9d3c4cS猫头猫            return null;
8404d9d3c4cS猫头猫        }
8414d9d3c4cS猫头猫    }
84242a9f3e6Smaotoumao
843d52aa40eS猫头猫    /** 获取榜单 */
84492b6c95aS猫头猫    async getTopLists(): Promise<IMusic.IMusicSheetGroupItem[]> {
845d52aa40eS猫头猫        try {
846d52aa40eS猫头猫            const result = await this.plugin.instance?.getTopLists?.();
847d52aa40eS猫头猫            if (!result) {
848d52aa40eS猫头猫                throw new Error();
849d52aa40eS猫头猫            }
850d52aa40eS猫头猫            return result;
851d52aa40eS猫头猫        } catch (e: any) {
852d52aa40eS猫头猫            devLog('error', '获取榜单失败', e, e?.message);
853d52aa40eS猫头猫            return [];
854d52aa40eS猫头猫        }
855d52aa40eS猫头猫    }
85642a9f3e6Smaotoumao
857d52aa40eS猫头猫    /** 获取榜单详情 */
858d52aa40eS猫头猫    async getTopListDetail(
85992b6c95aS猫头猫        topListItem: IMusic.IMusicSheetItemBase,
860956ee1b7S猫头猫        page: number,
861956ee1b7S猫头猫    ): Promise<IPlugin.ITopListInfoResult> {
862d52aa40eS猫头猫        try {
863d52aa40eS猫头猫            const result = await this.plugin.instance?.getTopListDetail?.(
864d52aa40eS猫头猫                topListItem,
865956ee1b7S猫头猫                page,
866d52aa40eS猫头猫            );
867d52aa40eS猫头猫            if (!result) {
868d52aa40eS猫头猫                throw new Error();
869d52aa40eS猫头猫            }
870d384662fS猫头猫            if (result.musicList) {
871d384662fS猫头猫                result.musicList.forEach(_ =>
872d384662fS猫头猫                    resetMediaItem(_, this.plugin.name),
873d384662fS猫头猫                );
874d384662fS猫头猫            }
875956ee1b7S猫头猫            if (result.isEnd !== false) {
876956ee1b7S猫头猫                result.isEnd = true;
877956ee1b7S猫头猫            }
878d52aa40eS猫头猫            return result;
879d52aa40eS猫头猫        } catch (e: any) {
880d52aa40eS猫头猫            devLog('error', '获取榜单详情失败', e, e?.message);
881d52aa40eS猫头猫            return {
882956ee1b7S猫头猫                isEnd: true,
883956ee1b7S猫头猫                topListItem: topListItem as IMusic.IMusicSheetItem,
884d52aa40eS猫头猫                musicList: [],
885d52aa40eS猫头猫            };
886d52aa40eS猫头猫        }
887d52aa40eS猫头猫    }
888ceb900cdS猫头猫
8895830c002S猫头猫    /** 获取推荐歌单的tag */
890ceb900cdS猫头猫    async getRecommendSheetTags(): Promise<IPlugin.IGetRecommendSheetTagsResult> {
891ceb900cdS猫头猫        try {
892ceb900cdS猫头猫            const result =
893ceb900cdS猫头猫                await this.plugin.instance?.getRecommendSheetTags?.();
894ceb900cdS猫头猫            if (!result) {
895ceb900cdS猫头猫                throw new Error();
896ceb900cdS猫头猫            }
897ceb900cdS猫头猫            return result;
898ceb900cdS猫头猫        } catch (e: any) {
899ceb900cdS猫头猫            devLog('error', '获取推荐歌单失败', e, e?.message);
900ceb900cdS猫头猫            return {
901ceb900cdS猫头猫                data: [],
902ceb900cdS猫头猫            };
903ceb900cdS猫头猫        }
904ceb900cdS猫头猫    }
90542a9f3e6Smaotoumao
9065830c002S猫头猫    /** 获取某个tag的推荐歌单 */
907ceb900cdS猫头猫    async getRecommendSheetsByTag(
908ceb900cdS猫头猫        tagItem: ICommon.IUnique,
909ceb900cdS猫头猫        page?: number,
910ceb900cdS猫头猫    ): Promise<ICommon.PaginationResponse<IMusic.IMusicSheetItemBase>> {
911ceb900cdS猫头猫        try {
912ceb900cdS猫头猫            const result =
913ceb900cdS猫头猫                await this.plugin.instance?.getRecommendSheetsByTag?.(
914ceb900cdS猫头猫                    tagItem,
915ceb900cdS猫头猫                    page ?? 1,
916ceb900cdS猫头猫                );
917ceb900cdS猫头猫            if (!result) {
918ceb900cdS猫头猫                throw new Error();
919ceb900cdS猫头猫            }
920ceb900cdS猫头猫            if (result.isEnd !== false) {
921ceb900cdS猫头猫                result.isEnd = true;
922ceb900cdS猫头猫            }
923ceb900cdS猫头猫            if (!result.data) {
924ceb900cdS猫头猫                result.data = [];
925ceb900cdS猫头猫            }
926ceb900cdS猫头猫            result.data.forEach(item => resetMediaItem(item, this.plugin.name));
927ceb900cdS猫头猫
928ceb900cdS猫头猫            return result;
929ceb900cdS猫头猫        } catch (e: any) {
930ceb900cdS猫头猫            devLog('error', '获取推荐歌单详情失败', e, e?.message);
931ceb900cdS猫头猫            return {
932ceb900cdS猫头猫                isEnd: true,
933ceb900cdS猫头猫                data: [],
934ceb900cdS猫头猫            };
935ceb900cdS猫头猫        }
936ceb900cdS猫头猫    }
937da2a2959S猫头猫
938da2a2959S猫头猫    async getMusicComments(
939da2a2959S猫头猫        musicItem: IMusic.IMusicItem,
940da2a2959S猫头猫    ): Promise<ICommon.PaginationResponse<IMedia.IComment>> {
941da2a2959S猫头猫        const result = await this.plugin.instance?.getMusicComments?.(
942da2a2959S猫头猫            musicItem,
943da2a2959S猫头猫        );
944da2a2959S猫头猫        if (!result) {
945da2a2959S猫头猫            throw new Error();
946da2a2959S猫头猫        }
947da2a2959S猫头猫        if (result.isEnd !== false) {
948da2a2959S猫头猫            result.isEnd = true;
949da2a2959S猫头猫        }
950da2a2959S猫头猫        if (!result.data) {
951da2a2959S猫头猫            result.data = [];
952da2a2959S猫头猫        }
953da2a2959S猫头猫
954da2a2959S猫头猫        return result;
955da2a2959S猫头猫    }
9567fb90110S猫头猫
9577fb90110S猫头猫    async migrateFromOtherPlugin(
9587fb90110S猫头猫        mediaItem: ICommon.IMediaBase,
9597fb90110S猫头猫        fromPlatform: string,
9607fb90110S猫头猫    ): Promise<{isOk: boolean; data?: ICommon.IMediaBase}> {
9617fb90110S猫头猫        try {
9627fb90110S猫头猫            const result = await this.plugin.instance?.migrateFromOtherPlugin(
9637fb90110S猫头猫                mediaItem,
9647fb90110S猫头猫                fromPlatform,
9657fb90110S猫头猫            );
9667fb90110S猫头猫
9677fb90110S猫头猫            if (
9687fb90110S猫头猫                result.isOk &&
9697fb90110S猫头猫                result.data?.id &&
9707fb90110S猫头猫                result.data?.platform === this.plugin.platform
9717fb90110S猫头猫            ) {
9727fb90110S猫头猫                return {
9737fb90110S猫头猫                    isOk: result.isOk,
9747fb90110S猫头猫                    data: result.data,
9757fb90110S猫头猫                };
9767fb90110S猫头猫            }
9777fb90110S猫头猫            return {
9787fb90110S猫头猫                isOk: false,
9797fb90110S猫头猫            };
9807fb90110S猫头猫        } catch {
9817fb90110S猫头猫            return {
9827fb90110S猫头猫                isOk: false,
9837fb90110S猫头猫            };
9847fb90110S猫头猫        }
9857fb90110S猫头猫    }
986927dbe93S猫头猫}
98733d52955Smaotoumao
988d5bfeb7eS猫头猫//#endregion
9891a5528a0S猫头猫
990927dbe93S猫头猫let plugins: Array<Plugin> = [];
991927dbe93S猫头猫const pluginStateMapper = new StateMapper(() => plugins);
99274d0cf81S猫头猫
993d5bfeb7eS猫头猫//#region 本地音乐插件
99474d0cf81S猫头猫/** 本地插件 */
99574d0cf81S猫头猫const localFilePlugin = new Plugin(function () {
9960e4173cdS猫头猫    return {
997d5bfeb7eS猫头猫        platform: localPluginPlatform,
99874d0cf81S猫头猫        _path: '',
99974d0cf81S猫头猫        async getMusicInfo(musicBase) {
100074d0cf81S猫头猫            const localPath = getInternalData<string>(
100174d0cf81S猫头猫                musicBase,
100274d0cf81S猫头猫                InternalDataType.LOCALPATH,
10030e4173cdS猫头猫            );
100474d0cf81S猫头猫            if (localPath) {
100574d0cf81S猫头猫                const coverImg = await Mp3Util.getMediaCoverImg(localPath);
100674d0cf81S猫头猫                return {
100774d0cf81S猫头猫                    artwork: coverImg,
100874d0cf81S猫头猫                };
100974d0cf81S猫头猫            }
101074d0cf81S猫头猫            return null;
101174d0cf81S猫头猫        },
10127993f90eS猫头猫        async getLyric(musicBase) {
10137993f90eS猫头猫            const localPath = getInternalData<string>(
10147993f90eS猫头猫                musicBase,
10157993f90eS猫头猫                InternalDataType.LOCALPATH,
10167993f90eS猫头猫            );
10173a6f67b1S猫头猫            let rawLrc: string | null = null;
10187993f90eS猫头猫            if (localPath) {
10193a6f67b1S猫头猫                // 读取内嵌歌词
10203a6f67b1S猫头猫                try {
10213a6f67b1S猫头猫                    rawLrc = await Mp3Util.getLyric(localPath);
10223a6f67b1S猫头猫                } catch (e) {
1023a84a85c5S猫头猫                    console.log('读取内嵌歌词失败', e);
10247993f90eS猫头猫                }
10253a6f67b1S猫头猫                if (!rawLrc) {
10263a6f67b1S猫头猫                    // 读取配置歌词
10273a6f67b1S猫头猫                    const lastDot = localPath.lastIndexOf('.');
10283a6f67b1S猫头猫                    const lrcPath = localPath.slice(0, lastDot) + '.lrc';
10293a6f67b1S猫头猫
10303a6f67b1S猫头猫                    try {
10313a6f67b1S猫头猫                        if (await exists(lrcPath)) {
10323a6f67b1S猫头猫                            rawLrc = await readFile(lrcPath, 'utf8');
10333a6f67b1S猫头猫                        }
10343a6f67b1S猫头猫                    } catch {}
10353a6f67b1S猫头猫                }
10363a6f67b1S猫头猫            }
10373a6f67b1S猫头猫
10383a6f67b1S猫头猫            return rawLrc
10393a6f67b1S猫头猫                ? {
10403a6f67b1S猫头猫                      rawLrc,
10413a6f67b1S猫头猫                  }
10423a6f67b1S猫头猫                : null;
10437993f90eS猫头猫        },
1044a84a85c5S猫头猫        async importMusicItem(urlLike) {
1045a84a85c5S猫头猫            let meta: any = {};
104642a9f3e6Smaotoumao            let id: string;
104742a9f3e6Smaotoumao
1048a84a85c5S猫头猫            try {
1049a84a85c5S猫头猫                meta = await Mp3Util.getBasicMeta(urlLike);
105042a9f3e6Smaotoumao                const fileStat = await stat(urlLike);
105142a9f3e6Smaotoumao                id =
105242a9f3e6Smaotoumao                    CryptoJs.MD5(fileStat.originalFilepath).toString(
105342a9f3e6Smaotoumao                        CryptoJs.enc.Hex,
105442a9f3e6Smaotoumao                    ) || nanoid();
105542a9f3e6Smaotoumao            } catch {
10565353b473S猫头猫                id = nanoid();
10575353b473S猫头猫            }
105842a9f3e6Smaotoumao
1059a84a85c5S猫头猫            return {
1060a84a85c5S猫头猫                id: id,
1061a84a85c5S猫头猫                platform: '本地',
1062a84a85c5S猫头猫                title: meta?.title ?? getFileName(urlLike),
1063a84a85c5S猫头猫                artist: meta?.artist ?? '未知歌手',
10645353b473S猫头猫                duration: parseInt(meta?.duration ?? '0', 10) / 1000,
1065a84a85c5S猫头猫                album: meta?.album ?? '未知专辑',
1066a84a85c5S猫头猫                artwork: '',
1067a84a85c5S猫头猫                [internalSerializeKey]: {
1068a84a85c5S猫头猫                    localPath: urlLike,
1069a84a85c5S猫头猫                },
1070a84a85c5S猫头猫            };
1071a84a85c5S猫头猫        },
107211908939S猫头猫        async getMediaSource(musicItem, quality) {
107311908939S猫头猫            if (quality === 'standard') {
107411908939S猫头猫                return {
107511908939S猫头猫                    url: addFileScheme(musicItem.$?.localPath || musicItem.url),
107611908939S猫头猫                };
107711908939S猫头猫            }
107811908939S猫头猫            return null;
107911908939S猫头猫        },
108074d0cf81S猫头猫    };
108174d0cf81S猫头猫}, '');
10827993f90eS猫头猫localFilePlugin.hash = localPluginHash;
1083927dbe93S猫头猫
1084d5bfeb7eS猫头猫//#endregion
1085d5bfeb7eS猫头猫
1086927dbe93S猫头猫async function setup() {
1087927dbe93S猫头猫    const _plugins: Array<Plugin> = [];
1088927dbe93S猫头猫    try {
1089927dbe93S猫头猫        // 加载插件
1090927dbe93S猫头猫        const pluginsPaths = await readDir(pathConst.pluginPath);
1091927dbe93S猫头猫        for (let i = 0; i < pluginsPaths.length; ++i) {
1092927dbe93S猫头猫            const _pluginUrl = pluginsPaths[i];
10931e263108S猫头猫            trace('初始化插件', _pluginUrl);
10941e263108S猫头猫            if (
10951e263108S猫头猫                _pluginUrl.isFile() &&
10961e263108S猫头猫                (_pluginUrl.name?.endsWith?.('.js') ||
10971e263108S猫头猫                    _pluginUrl.path?.endsWith?.('.js'))
10981e263108S猫头猫            ) {
1099927dbe93S猫头猫                const funcCode = await readFile(_pluginUrl.path, 'utf8');
1100927dbe93S猫头猫                const plugin = new Plugin(funcCode, _pluginUrl.path);
11014060c00aS猫头猫                const _pluginIndex = _plugins.findIndex(
11024060c00aS猫头猫                    p => p.hash === plugin.hash,
11034060c00aS猫头猫                );
1104927dbe93S猫头猫                if (_pluginIndex !== -1) {
1105927dbe93S猫头猫                    // 重复插件,直接忽略
11060c266394S猫头猫                    continue;
1107927dbe93S猫头猫                }
1108927dbe93S猫头猫                plugin.hash !== '' && _plugins.push(plugin);
1109927dbe93S猫头猫            }
1110927dbe93S猫头猫        }
1111927dbe93S猫头猫
1112927dbe93S猫头猫        plugins = _plugins;
1113e08d37a3S猫头猫        /** 初始化meta信息 */
1114c2b3a262S猫头猫        await PluginMeta.setupMeta(plugins.map(_ => _.name));
1115c2b3a262S猫头猫        /** 查看一下是否有禁用的标记 */
1116c2b3a262S猫头猫        const allMeta = PluginMeta.getPluginMetaAll() ?? {};
1117c2b3a262S猫头猫        for (let plugin of plugins) {
1118c2b3a262S猫头猫            if (allMeta[plugin.name]?.enabled === false) {
1119c2b3a262S猫头猫                plugin.state = 'disabled';
1120c2b3a262S猫头猫            }
1121c2b3a262S猫头猫        }
1122c2b3a262S猫头猫        pluginStateMapper.notify();
1123927dbe93S猫头猫    } catch (e: any) {
11244060c00aS猫头猫        ToastAndroid.show(
11254060c00aS猫头猫            `插件初始化失败:${e?.message ?? e}`,
11264060c00aS猫头猫            ToastAndroid.LONG,
11274060c00aS猫头猫        );
11281a5528a0S猫头猫        errorLog('插件初始化失败', e?.message);
1129927dbe93S猫头猫        throw e;
1130927dbe93S猫头猫    }
1131927dbe93S猫头猫}
1132927dbe93S猫头猫
1133e36e2599S猫头猫interface IInstallPluginConfig {
1134e36e2599S猫头猫    notCheckVersion?: boolean;
1135e36e2599S猫头猫}
1136e36e2599S猫头猫
1137740e3947S猫头猫async function installPluginFromRawCode(
1138740e3947S猫头猫    funcCode: string,
1139740e3947S猫头猫    config?: IInstallPluginConfig,
1140740e3947S猫头猫) {
1141740e3947S猫头猫    if (funcCode) {
1142740e3947S猫头猫        const plugin = new Plugin(funcCode, '');
1143740e3947S猫头猫        const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
1144740e3947S猫头猫        if (_pluginIndex !== -1) {
1145740e3947S猫头猫            // 静默忽略
1146740e3947S猫头猫            return plugin;
1147740e3947S猫头猫        }
1148740e3947S猫头猫        const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
1149740e3947S猫头猫        if (oldVersionPlugin && !config?.notCheckVersion) {
1150740e3947S猫头猫            if (
1151740e3947S猫头猫                compare(
1152740e3947S猫头猫                    oldVersionPlugin.instance.version ?? '',
1153740e3947S猫头猫                    plugin.instance.version ?? '',
1154740e3947S猫头猫                    '>',
1155740e3947S猫头猫                )
1156740e3947S猫头猫            ) {
1157740e3947S猫头猫                throw new Error('已安装更新版本的插件');
1158740e3947S猫头猫            }
1159740e3947S猫头猫        }
1160740e3947S猫头猫
1161740e3947S猫头猫        if (plugin.hash !== '') {
1162740e3947S猫头猫            const fn = nanoid();
1163740e3947S猫头猫            if (oldVersionPlugin) {
1164740e3947S猫头猫                plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash);
1165740e3947S猫头猫                try {
1166740e3947S猫头猫                    await unlink(oldVersionPlugin.path);
1167740e3947S猫头猫                } catch {}
1168740e3947S猫头猫            }
1169740e3947S猫头猫            const pluginPath = `${pathConst.pluginPath}${fn}.js`;
1170740e3947S猫头猫            await writeFile(pluginPath, funcCode, 'utf8');
1171740e3947S猫头猫            plugin.path = pluginPath;
1172740e3947S猫头猫            plugins = plugins.concat(plugin);
1173740e3947S猫头猫            pluginStateMapper.notify();
1174740e3947S猫头猫            return plugin;
1175740e3947S猫头猫        }
1176740e3947S猫头猫        throw new Error('插件无法解析!');
1177740e3947S猫头猫    }
1178740e3947S猫头猫}
1179740e3947S猫头猫
1180927dbe93S猫头猫// 安装插件
1181e36e2599S猫头猫async function installPlugin(
1182e36e2599S猫头猫    pluginPath: string,
1183e36e2599S猫头猫    config?: IInstallPluginConfig,
1184e36e2599S猫头猫) {
118522c09412S猫头猫    // if (pluginPath.endsWith('.js')) {
1186927dbe93S猫头猫    const funcCode = await readFile(pluginPath, 'utf8');
1187e36e2599S猫头猫
1188e36e2599S猫头猫    if (funcCode) {
1189927dbe93S猫头猫        const plugin = new Plugin(funcCode, pluginPath);
1190927dbe93S猫头猫        const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
1191927dbe93S猫头猫        if (_pluginIndex !== -1) {
1192e36e2599S猫头猫            // 静默忽略
1193e36e2599S猫头猫            return plugin;
1194927dbe93S猫头猫        }
1195e36e2599S猫头猫        const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
1196e36e2599S猫头猫        if (oldVersionPlugin && !config?.notCheckVersion) {
1197e36e2599S猫头猫            if (
1198e36e2599S猫头猫                compare(
1199e36e2599S猫头猫                    oldVersionPlugin.instance.version ?? '',
1200e36e2599S猫头猫                    plugin.instance.version ?? '',
1201e36e2599S猫头猫                    '>',
1202e36e2599S猫头猫                )
1203e36e2599S猫头猫            ) {
1204e36e2599S猫头猫                throw new Error('已安装更新版本的插件');
1205e36e2599S猫头猫            }
1206e36e2599S猫头猫        }
1207e36e2599S猫头猫
1208927dbe93S猫头猫        if (plugin.hash !== '') {
1209927dbe93S猫头猫            const fn = nanoid();
1210e36e2599S猫头猫            if (oldVersionPlugin) {
1211e36e2599S猫头猫                plugins = plugins.filter(_ => _.hash !== oldVersionPlugin.hash);
1212e36e2599S猫头猫                try {
1213e36e2599S猫头猫                    await unlink(oldVersionPlugin.path);
1214e36e2599S猫头猫                } catch {}
1215e36e2599S猫头猫            }
1216927dbe93S猫头猫            const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
1217927dbe93S猫头猫            await copyFile(pluginPath, _pluginPath);
1218927dbe93S猫头猫            plugin.path = _pluginPath;
1219927dbe93S猫头猫            plugins = plugins.concat(plugin);
1220927dbe93S猫头猫            pluginStateMapper.notify();
1221a84a85c5S猫头猫            return plugin;
1222927dbe93S猫头猫        }
1223e36e2599S猫头猫        throw new Error('插件无法解析!');
1224927dbe93S猫头猫    }
1225e36e2599S猫头猫    throw new Error('插件无法识别!');
1226c2b3a262S猫头猫}
1227c2b3a262S猫头猫
1228ee781061S猫头猫const reqHeaders = {
1229ee781061S猫头猫    'Cache-Control': 'no-cache',
1230ee781061S猫头猫    Pragma: 'no-cache',
1231ee781061S猫头猫    Expires: '0',
1232ee781061S猫头猫};
1233ee781061S猫头猫
1234c2b3a262S猫头猫async function installPluginFromUrl(
1235c2b3a262S猫头猫    url: string,
1236c2b3a262S猫头猫    config?: IInstallPluginConfig,
1237c2b3a262S猫头猫) {
123858992c6bS猫头猫    try {
1239ee781061S猫头猫        const funcCode = (
1240ee781061S猫头猫            await axios.get(url, {
1241ee781061S猫头猫                headers: reqHeaders,
1242ee781061S猫头猫            })
1243ee781061S猫头猫        ).data;
124458992c6bS猫头猫        if (funcCode) {
124558992c6bS猫头猫            const plugin = new Plugin(funcCode, '');
124658992c6bS猫头猫            const _pluginIndex = plugins.findIndex(p => p.hash === plugin.hash);
124758992c6bS猫头猫            if (_pluginIndex !== -1) {
12488b7ddca8S猫头猫                // 静默忽略
12498b7ddca8S猫头猫                return;
125058992c6bS猫头猫            }
125125c1bd29S猫头猫            const oldVersionPlugin = plugins.find(p => p.name === plugin.name);
1252c2b3a262S猫头猫            if (oldVersionPlugin && !config?.notCheckVersion) {
125325c1bd29S猫头猫                if (
125425c1bd29S猫头猫                    compare(
125525c1bd29S猫头猫                        oldVersionPlugin.instance.version ?? '',
125625c1bd29S猫头猫                        plugin.instance.version ?? '',
125725c1bd29S猫头猫                        '>',
125825c1bd29S猫头猫                    )
125925c1bd29S猫头猫                ) {
126025c1bd29S猫头猫                    throw new Error('已安装更新版本的插件');
126125c1bd29S猫头猫                }
126225c1bd29S猫头猫            }
126325c1bd29S猫头猫
126458992c6bS猫头猫            if (plugin.hash !== '') {
126558992c6bS猫头猫                const fn = nanoid();
126658992c6bS猫头猫                const _pluginPath = `${pathConst.pluginPath}${fn}.js`;
126758992c6bS猫头猫                await writeFile(_pluginPath, funcCode, 'utf8');
126858992c6bS猫头猫                plugin.path = _pluginPath;
126958992c6bS猫头猫                plugins = plugins.concat(plugin);
127025c1bd29S猫头猫                if (oldVersionPlugin) {
127125c1bd29S猫头猫                    plugins = plugins.filter(
127225c1bd29S猫头猫                        _ => _.hash !== oldVersionPlugin.hash,
127325c1bd29S猫头猫                    );
127425c1bd29S猫头猫                    try {
127525c1bd29S猫头猫                        await unlink(oldVersionPlugin.path);
127625c1bd29S猫头猫                    } catch {}
127725c1bd29S猫头猫                }
127858992c6bS猫头猫                pluginStateMapper.notify();
127958992c6bS猫头猫                return;
128058992c6bS猫头猫            }
128174acbfc0S猫头猫            throw new Error('插件无法解析!');
128258992c6bS猫头猫        }
128325c1bd29S猫头猫    } catch (e: any) {
1284ea6d708fS猫头猫        devLog('error', 'URL安装插件失败', e, e?.message);
128558992c6bS猫头猫        errorLog('URL安装插件失败', e);
128625c1bd29S猫头猫        throw new Error(e?.message ?? '');
128758992c6bS猫头猫    }
128858992c6bS猫头猫}
128958992c6bS猫头猫
1290927dbe93S猫头猫/** 卸载插件 */
1291927dbe93S猫头猫async function uninstallPlugin(hash: string) {
1292927dbe93S猫头猫    const targetIndex = plugins.findIndex(_ => _.hash === hash);
1293927dbe93S猫头猫    if (targetIndex !== -1) {
1294927dbe93S猫头猫        try {
129524e5e74aS猫头猫            const pluginName = plugins[targetIndex].name;
1296927dbe93S猫头猫            await unlink(plugins[targetIndex].path);
1297927dbe93S猫头猫            plugins = plugins.filter(_ => _.hash !== hash);
1298927dbe93S猫头猫            pluginStateMapper.notify();
129943eb30bfS猫头猫            // 防止其他重名
130024e5e74aS猫头猫            if (plugins.every(_ => _.name !== pluginName)) {
13015589cdf3S猫头猫                MediaExtra.removeAll(pluginName);
130224e5e74aS猫头猫            }
1303927dbe93S猫头猫        } catch {}
1304927dbe93S猫头猫    }
1305927dbe93S猫头猫}
1306927dbe93S猫头猫
130708882a77S猫头猫async function uninstallAllPlugins() {
130808882a77S猫头猫    await Promise.all(
130908882a77S猫头猫        plugins.map(async plugin => {
131008882a77S猫头猫            try {
131108882a77S猫头猫                const pluginName = plugin.name;
131208882a77S猫头猫                await unlink(plugin.path);
13135589cdf3S猫头猫                MediaExtra.removeAll(pluginName);
131408882a77S猫头猫            } catch (e) {}
131508882a77S猫头猫        }),
131608882a77S猫头猫    );
131708882a77S猫头猫    plugins = [];
131808882a77S猫头猫    pluginStateMapper.notify();
1319e08d37a3S猫头猫
1320e08d37a3S猫头猫    /** 清除空余文件,异步做就可以了 */
1321e08d37a3S猫头猫    readDir(pathConst.pluginPath)
1322e08d37a3S猫头猫        .then(fns => {
1323e08d37a3S猫头猫            fns.forEach(fn => {
1324e08d37a3S猫头猫                unlink(fn.path).catch(emptyFunction);
1325e08d37a3S猫头猫            });
1326e08d37a3S猫头猫        })
1327e08d37a3S猫头猫        .catch(emptyFunction);
132808882a77S猫头猫}
132908882a77S猫头猫
133025c1bd29S猫头猫async function updatePlugin(plugin: Plugin) {
133125c1bd29S猫头猫    const updateUrl = plugin.instance.srcUrl;
133225c1bd29S猫头猫    if (!updateUrl) {
133325c1bd29S猫头猫        throw new Error('没有更新源');
133425c1bd29S猫头猫    }
133525c1bd29S猫头猫    try {
133625c1bd29S猫头猫        await installPluginFromUrl(updateUrl);
133725c1bd29S猫头猫    } catch (e: any) {
133825c1bd29S猫头猫        if (e.message === '插件已安装') {
133925c1bd29S猫头猫            throw new Error('当前已是最新版本');
134025c1bd29S猫头猫        } else {
134125c1bd29S猫头猫            throw e;
134225c1bd29S猫头猫        }
134325c1bd29S猫头猫    }
134425c1bd29S猫头猫}
134525c1bd29S猫头猫
1346927dbe93S猫头猫function getByMedia(mediaItem: ICommon.IMediaBase) {
13472c595535S猫头猫    return getByName(mediaItem?.platform);
1348927dbe93S猫头猫}
1349927dbe93S猫头猫
1350927dbe93S猫头猫function getByHash(hash: string) {
13517993f90eS猫头猫    return hash === localPluginHash
13527993f90eS猫头猫        ? localFilePlugin
13537993f90eS猫头猫        : plugins.find(_ => _.hash === hash);
1354927dbe93S猫头猫}
1355927dbe93S猫头猫
1356927dbe93S猫头猫function getByName(name: string) {
13577993f90eS猫头猫    return name === localPluginPlatform
13580e4173cdS猫头猫        ? localFilePlugin
13590e4173cdS猫头猫        : plugins.find(_ => _.name === name);
1360927dbe93S猫头猫}
1361927dbe93S猫头猫
1362927dbe93S猫头猫function getValidPlugins() {
1363927dbe93S猫头猫    return plugins.filter(_ => _.state === 'enabled');
1364927dbe93S猫头猫}
1365927dbe93S猫头猫
13662b80a429S猫头猫function getSearchablePlugins(supportedSearchType?: ICommon.SupportMediaType) {
13672b80a429S猫头猫    return plugins.filter(
13682b80a429S猫头猫        _ =>
13692b80a429S猫头猫            _.state === 'enabled' &&
13702b80a429S猫头猫            _.instance.search &&
137139ac60f7S猫头猫            (supportedSearchType && _.instance.supportedSearchType
137239ac60f7S猫头猫                ? _.instance.supportedSearchType.includes(supportedSearchType)
13732b80a429S猫头猫                : true),
13742b80a429S猫头猫    );
1375efb9da24S猫头猫}
1376efb9da24S猫头猫
13772b80a429S猫头猫function getSortedSearchablePlugins(
13782b80a429S猫头猫    supportedSearchType?: ICommon.SupportMediaType,
13792b80a429S猫头猫) {
13802b80a429S猫头猫    return getSearchablePlugins(supportedSearchType).sort((a, b) =>
1381e08d37a3S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
1382e08d37a3S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
1383e08d37a3S猫头猫        0
1384e08d37a3S猫头猫            ? -1
1385e08d37a3S猫头猫            : 1,
1386e08d37a3S猫头猫    );
1387e08d37a3S猫头猫}
1388e08d37a3S猫头猫
138915feccc1S猫头猫function getTopListsablePlugins() {
139015feccc1S猫头猫    return plugins.filter(_ => _.state === 'enabled' && _.instance.getTopLists);
139115feccc1S猫头猫}
139215feccc1S猫头猫
139315feccc1S猫头猫function getSortedTopListsablePlugins() {
139415feccc1S猫头猫    return getTopListsablePlugins().sort((a, b) =>
139515feccc1S猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
139615feccc1S猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
139715feccc1S猫头猫        0
139815feccc1S猫头猫            ? -1
139915feccc1S猫头猫            : 1,
140015feccc1S猫头猫    );
140115feccc1S猫头猫}
140215feccc1S猫头猫
1403ceb900cdS猫头猫function getRecommendSheetablePlugins() {
1404ceb900cdS猫头猫    return plugins.filter(
1405ceb900cdS猫头猫        _ => _.state === 'enabled' && _.instance.getRecommendSheetsByTag,
1406ceb900cdS猫头猫    );
1407ceb900cdS猫头猫}
1408ceb900cdS猫头猫
1409ceb900cdS猫头猫function getSortedRecommendSheetablePlugins() {
1410ceb900cdS猫头猫    return getRecommendSheetablePlugins().sort((a, b) =>
1411ceb900cdS猫头猫        (PluginMeta.getPluginMeta(a).order ?? Infinity) -
1412ceb900cdS猫头猫            (PluginMeta.getPluginMeta(b).order ?? Infinity) <
1413ceb900cdS猫头猫        0
1414ceb900cdS猫头猫            ? -1
1415ceb900cdS猫头猫            : 1,
1416ceb900cdS猫头猫    );
1417ceb900cdS猫头猫}
1418ceb900cdS猫头猫
1419e08d37a3S猫头猫function useSortedPlugins() {
1420e08d37a3S猫头猫    const _plugins = pluginStateMapper.useMappedState();
1421e08d37a3S猫头猫    const _pluginMetaAll = PluginMeta.usePluginMetaAll();
1422e08d37a3S猫头猫
142334588741S猫头猫    const [sortedPlugins, setSortedPlugins] = useState(
142434588741S猫头猫        [..._plugins].sort((a, b) =>
1425e08d37a3S猫头猫            (_pluginMetaAll[a.name]?.order ?? Infinity) -
1426e08d37a3S猫头猫                (_pluginMetaAll[b.name]?.order ?? Infinity) <
1427e08d37a3S猫头猫            0
1428e08d37a3S猫头猫                ? -1
1429e08d37a3S猫头猫                : 1,
143034588741S猫头猫        ),
1431e08d37a3S猫头猫    );
143234588741S猫头猫
143334588741S猫头猫    useEffect(() => {
1434d4cd40d8S猫头猫        InteractionManager.runAfterInteractions(() => {
143534588741S猫头猫            setSortedPlugins(
143634588741S猫头猫                [..._plugins].sort((a, b) =>
143734588741S猫头猫                    (_pluginMetaAll[a.name]?.order ?? Infinity) -
143834588741S猫头猫                        (_pluginMetaAll[b.name]?.order ?? Infinity) <
143934588741S猫头猫                    0
144034588741S猫头猫                        ? -1
144134588741S猫头猫                        : 1,
144234588741S猫头猫                ),
144334588741S猫头猫            );
1444d4cd40d8S猫头猫        });
144534588741S猫头猫    }, [_plugins, _pluginMetaAll]);
144634588741S猫头猫
144734588741S猫头猫    return sortedPlugins;
1448e08d37a3S猫头猫}
1449e08d37a3S猫头猫
1450c2b3a262S猫头猫async function setPluginEnabled(plugin: Plugin, enabled?: boolean) {
1451c2b3a262S猫头猫    const target = plugins.find(it => it.hash === plugin.hash);
1452c2b3a262S猫头猫    if (target) {
1453c2b3a262S猫头猫        target.state = enabled ? 'enabled' : 'disabled';
1454c2b3a262S猫头猫        plugins = [...plugins];
1455c2b3a262S猫头猫        pluginStateMapper.notify();
1456c2b3a262S猫头猫        PluginMeta.setPluginMetaProp(plugin, 'enabled', enabled);
1457c2b3a262S猫头猫    }
1458c2b3a262S猫头猫}
1459c2b3a262S猫头猫
1460927dbe93S猫头猫const PluginManager = {
1461927dbe93S猫头猫    setup,
1462927dbe93S猫头猫    installPlugin,
1463740e3947S猫头猫    installPluginFromRawCode,
146458992c6bS猫头猫    installPluginFromUrl,
146525c1bd29S猫头猫    updatePlugin,
1466927dbe93S猫头猫    uninstallPlugin,
1467927dbe93S猫头猫    getByMedia,
1468927dbe93S猫头猫    getByHash,
1469927dbe93S猫头猫    getByName,
1470927dbe93S猫头猫    getValidPlugins,
1471efb9da24S猫头猫    getSearchablePlugins,
1472e08d37a3S猫头猫    getSortedSearchablePlugins,
147315feccc1S猫头猫    getTopListsablePlugins,
1474ceb900cdS猫头猫    getSortedRecommendSheetablePlugins,
147515feccc1S猫头猫    getSortedTopListsablePlugins,
14765276aef9S猫头猫    usePlugins: pluginStateMapper.useMappedState,
1477e08d37a3S猫头猫    useSortedPlugins,
147808882a77S猫头猫    uninstallAllPlugins,
1479c2b3a262S猫头猫    setPluginEnabled,
14805276aef9S猫头猫};
1481927dbe93S猫头猫
1482927dbe93S猫头猫export default PluginManager;
1483