MusicFree插件化架构拆解:多平台音源如何统一接入一个播放器
【免费下载链接】MusicFree插件化、定制化、无广告的免费音乐播放器项目地址: https://gitcode.com/GitHub_Trending/mu/MusicFree
MusicFree 是一个插件化的免费音乐播放器,本体不内置任何音源:搜索、播放、歌单导入、歌词获取全部由外部插件完成。本文基于仓库代码,拆解主应用如何把各插件的异构输出收敛为统一的数据结构。
设计起点:IMusicItem 数据契约
无论歌曲来自哪个平台,进入应用后的形态都是 src/types/music.d.ts 中定义的IMusicItem,插件与播放器之间的一切数据交换都围绕这个接口展开:
export interface IMusicItem { id: string; platform: string; artist: string; title: string; duration: number; // 时长,单位秒 album: string; artwork: string; source?: Partial<Record<IQualityKey, IMediaSource>>; lyric?: ILyric.ILyricSource; }platform字段标记歌曲归属,后续主应用靠它反查对应插件。音质键IQualityKey只有low / standard / high / super四档,各平台的原始音质标识都要在插件内部映射到这四档。时长统一为秒,封面统一为 URL。
插件加载层:沙箱执行与依赖白名单
插件本质是一个满足插件协议的 CommonJS 模块。加载逻辑在 src/core/pluginManager/plugin.ts:主应用通过Function构造器在受限环境中运行插件代码,插件拿到的require是_require白名单实现,只提供 axios、cheerio、dayjs、qs、webdav 等少量包。插件以代码的 SHA256 作为唯一 hash,用于去重和卸载;安装支持本地.js文件与 URL 两种方式,插件声明的appVersion与当前版本不匹配时会被拒绝加载。用户侧的启用、排序、用户变量、替代插件等状态由 src/core/pluginManager/meta.ts 持久化到 MMKV。
转换层:包装器如何统一各平台输出
所有插件方法都被PluginMethodsWrapper包了一层:插件只需返回原始数据,主应用在结果上逐项调用resetMediaItem(src/utils/mediaUtils.ts)补全platform字段并清理内部序列化字段,保证列表中的每一项都符合契约:
mediaItem.platform = platform ?? mediaItem.platform; mediaItem[internalSerializeKey] = undefined;插件未实现的方法一律返回兜底空结果而不是抛错,单个插件异常不会阻塞其他插件。
音源获取的四级优先链
getMediaSource(按音质等级获取可播放地址)的执行顺序是:本地文件存在则直接返回file://路径;否则查MediaCache缓存;再否则调用插件的getMediaSource解析真实地址,失败自动重试一次;最后回退到item.qualities或item.url。缓存行为受插件cacheControl字段控制,声明no-cache的插件只在离线时回落到缓存兜底。
歌词的多级来源
getLyric按优先级依次尝试:用户手动关联的歌词、手动上传的本地.lrc、MediaCache缓存、插件getLyric、旧版lrc字段下载;本地音乐则读 MP3 内嵌歌词或同名.lrc文件。拿到的歌词写入本地.lrc缓存文件并记入MediaCache,缓存上限 800 条,超限时先清理一半条目及其歌词文件。
实际效果与收益
对插件开发者,只需实现协议函数并处理接口输入输出,分页、重试、缓存、歌词回退全部由主应用统一完成;对使用者,可以安装、排序、禁用插件,还能给某个平台设置替代音源插件;对维护者,新增音源平台只需新插件,主应用代码不用改。播放器与音源解耦后,第三方插件的故障不会波及主应用,这也是该项目"小而美"定位的工程基础。
【免费下载链接】MusicFree插件化、定制化、无广告的免费音乐播放器项目地址: https://gitcode.com/GitHub_Trending/mu/MusicFree
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考