MusicFree:一个空壳播放器,27.7k 星靠插件活着

3次阅读
没有评论

你手机里那个音乐 App,装着几十万首歌,但没有一首是它自己的。MusicFree 反过来——它一个音源都不带,27.7k 星却全靠一套插件协议撑起来。装上插件,它能搜能放;卸掉插件,它就只是个本地播放器。

一个「什么都不会」的播放器

先讲个反常识的事。

市面上绝大多数音乐 App,本质是「音源 + 播放器」打包卖给你。音源是它的命根子,播放器只是外壳。你把外壳做得再漂亮,没有音源照样是废铁。

MusicFree 把这两样东西拆开了。它只做播放器,一个音源都不带。

你第一次打开它,界面干干净净,能放手机里的本地音乐,能建歌单,能看歌词——但它搜不到任何一首网上的歌。因为搜索这个动作,它自己不会,得靠插件教它。

这就好比买了个音响,插上电没声音。你得自己接音源:接黑胶、接 CD、接蓝牙、接网络电台,接什么响什么。音响只负责把声音放好,接什么由你决定。

这个设计听着像缺胳膊少腿,但它是 MusicFree 全部价值所在。GitHub 上 27.7k 星,2.0k fork,277 个 open issue——一个「什么都不会」的播放器,凭什么?

MusicFree:一个空壳播放器,27.7k 星靠插件活着

音源可以换,播放器的职责不变——这是 MusicFree 全部设计的起点

为什么有人要做这么个东西

故事要从一个人说起。

MusicFree 的作者是「猫头猫」,公众号叫「一只猫头猫」,B 站叫「不想睡觉猫头猫」。他在 README 里写得很坦诚:这是个业余项目,维护时间有限,每天大概半小时。

一个人,每天半小时,做了一个 27.7k 星的项目。

他为什么要做?README 里没直接说,但从项目的几个设定能反推出动机:

第一,他不想做音源。 做音源意味着什么?意味着要跟版权打交道、要跟平台斗智斗勇、要不断修接口。这是个体开发者扛不住的活。

第二,他又想让播放器能用。 只做本地播放器,那市面上已经有一堆了,何必再造一个。

第三,他找到了那个中间点:把音源这件事交给插件,自己只管播放器。

于是有了这套架构——软件负责「怎么放」,插件负责「放什么」。 两件事彻底解耦,各归各管。

这个思路不新鲜,浏览器和网页、编辑器和语法高亮插件,都是这么干的。但放在音乐播放器上,它解决了一个很具体的困境:想听歌的人很多,愿意做音源的人很少,而愿意做一个好播放器的人,也不多。 把这三拨人用一套协议连起来,事情就成了。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

作者每天维护时间约半小时,这个项目是业余时间做出来的

它到底长什么样

说了半天「空壳」,得让你看见壳子里有什么。

MusicFree 的界面分三块:左侧是侧边栏(我的音乐、歌单、插件设置),中间是主区域(列表、详情页),底部是播放条。这个布局你在任何一个音乐 App 里都见过,不新鲜。

真正值得注意的是它没有什么:

  • 没有开屏广告
  • 没有首页推荐位
  • 没有 VIP 弹窗
  • 没有「猜你喜欢」这种要你登录才给看的东西
  • 没有账号体系(你的数据不上传,也不需要登录)

它干净得有点不适应。 第一次打开你会下意识找「搜索框在哪」,找到之后输入歌名,回车——没反应。因为还没装插件。

这就是它最诚实的时刻:它不假装自己能搜歌。

界面上能自定义的东西倒是不少。明暗两套主题是标配,还能自己设背景图。桌面版更进一步,支持主题包——一个文件夹里放 index.css 和 config.json,你能把整个界面的配色、圆角、分割线、悬浮效果全改掉,甚至用 iframes 字段塞一个 HTML 页面当背景,做出 CSS 做不到的动态效果。

官方给了几个例子:暗黑模式、背景图片、fliqlo(那个翻页时钟)、樱花、雨季。听着像玩具,但这恰恰是「自己造的东西」才有的自由度。

技术栈上,安卓版是 React Native + TypeScript,桌面版是 Electron + React + TypeScript。两端不共用代码,但共用一套插件协议——这一点后面会细讲,它是这个项目最聪明的地方。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

没有账号体系,也没有埋点上报,所有数据留在本地

插件到底是个什么东西

这是全文最核心的一节。理解了插件,你就理解了 MusicFree。

插件的本质,是一个导出特定数据结构的 CommonJS 模块。

翻译成人话:一个 .js 文件,里面 module.exports 出一个对象,这个对象上有几个约定的函数。软件在需要的时候调用这些函数,插件返回软件要的数据。

最小的插件长这样:

module.exports = {   platform: "我的插件",      // 插件名   version: "0.0.0",         // 版本号   getMediaSource: function (musicItem) {     // 给我一首歌的信息,我还你一个能播的 URL     return { url: "https://..." };   }, };

就这么点东西。没有 SDK,没有框架,没有构建流程强制要求——你甚至可以直接写一个 .js 文件丢进去。

那软件怎么知道什么时候调哪个函数?靠约定俗成的方法名:

你想让软件能做的事 插件里要实现的方法
搜歌 search
拿播放地址 getMediaSource
看专辑 getAlbumInfo
看歌手 getArtistWorks
导入歌单 importMusicSheet
拿歌词 getLyric

软件负责「什么时候调」,插件负责「怎么实现」。 这个分工干净得近乎粗暴,但极其有效。

更妙的是,分页、缓存、请求重试这些脏活,全都由软件干了。插件作者只需要处理「给什么入参、返回什么数据」这一件事。这是这套协议对开发者最友好的地方——你不需要写一个完整的 App,只需要写一个能回答问题的函数。

插件加载的机制也很朴素。软件启动时,去一个固定路径下扫 .js 文件:

  • 安卓:Android/data/fun.upup.musicfree/files/plugins
  • 桌面端:C:/Users/{用户名}/AppData/Roaming/MusicFree/musicfree-plugins

扫到就加载,加载完就成了这个 App 的一部分。没有沙箱,没有权限隔离,插件和软件跑在同一个 JS 上下文里。

这一点作者自己也在文档里标了红:插件有能力破坏软件本身的执行逻辑,所以你必须谨慎识别插件来源。 他解释了为什么这么设计——省掉序列化和反序列化的开销,性能好一点;而且项目初衷是技术探索,安全性考虑不多。

这段话很诚实,也是你必须知道的代价:插件生态的开放性,是用安全性换来的。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

分页、缓存、重试由软件承担,插件只需回答「给什么、返什么」

桌面版:同一套协议,换了个壳

安卓版之外,还有一个独立的桌面版仓库 MusicFreeDesktop,9.0k 星,877 fork,支持 Windows、macOS、Linux。

它不是安卓版的移植,是重新写的一个 App。

技术上,桌面版用 Electron + React + TypeScript,安卓版用 React Native + TypeScript。渲染层一个跑浏览器内核,一个跑原生桥接,代码没法共用。

但插件协议完全一样。

这是整件事最聪明的地方。作者没有为桌面端发明第二套插件标准,而是直接沿用安卓版那份协议——search、getMediaSource、getLyric 这些方法名、入参结构、返回格式,一模一样。

结果是什么?同一个插件,安卓能装,桌面也能装。 你写一次,两端通吃。

对插件作者来说,这是天大的好事。安卓生态已经攒了一批插件,桌面版一出生就继承了这个生态,不需要从零开始求人适配。

对用户来说,也省事。手机上装了什么插件,电脑上照着装一遍,界面不同但能搜到的歌是一样的。

桌面版多出来的东西

桌面版不是简单的「安卓版搬到大屏」。它多了一个安卓版没有的能力:主题包。

主题包是一个文件夹,必须包含两个文件:

index.css —— 覆盖界面样式。作者预留了一整套 CSS 变量,改这几个值就能换肤:

:root {   --primaryColor: #f17d34;      /* 主色调 */   --backgroundColor: #fdfdfd;   /* 背景色 */   --dividerColor: rgba(0,0,0,0.1);  /* 分割线 */   --listHoverColor: rgba(0,0,0,0.05); /* 列表悬浮 */   --textColor: #333333;         /* 主文本 */   --maskColor: rgba(51,51,51,0.2);  /* 遮罩层 */   --shadowColor: rgba(0,0,0,0.2);   /* 阴影 */   --successColor: #08A34C;      /* 成功 */   --dangerColor: #FC5F5F;       /* 危险 */   --infoColor: #0A95C8;         /* 通知 */ }

config.json —— 主题的元信息和高级配置:

{   "name": "主题名",   "preview": "#000000",   "description": "描述",   "iframes": {     "app": "http://...",      // 整个应用的背景     "header": "",             // 顶部区背景     "body": "",               // 侧边栏+主区背景     "side-bar": "",           // 侧边栏背景     "page": "",               // 主页面背景     "music-bar": ""           // 底部播放条背景   } }

iframes 这个字段是关键。它能让你把一个 HTML 页面当成背景塞进去,于是 CSS 做不到的效果——粒子、视频、Canvas 动画——都能上。官方给的「樱花」主题就是这么做的。

本地图片路径用 @/ 指代主题包目录,preview、iframes、以及 iframe 引用的 HTML 里的 @/ 都会被自动替换。这个细节说明作者是真的想让人做出好东西,不只是给个配置项敷衍。

桌面版的现实

说点不好听的。

桌面版没有一键安装包。 官方下载链接指向飞书云文档,不是 GitHub Releases。你要么去飞书拿,要么自己 clone 源码跑 npm install && npm start。

对一个 9k 星的项目来说,这个分发方式挺寒酸。原因也简单:Electron 打包三平台(Win/Mac/Linux)成本不低,作者每天只有半小时。

还有一点得说清楚:桌面版的插件机制和安卓版「略有差异」。文档原话是「基本原理相同」,但没细说差在哪。实际操作中,绝大多数插件能直接用,个别涉及平台特性的可能要调。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

两端渲染层不共用代码,但方法名、入参、返回格式完全一致

生态:插件从哪来,能干什么

软件是壳,插件才是肉。这一节讲清楚三件事:插件怎么装、插件从哪找、插件能干什么。

装插件的三条路

第一条:从网络装。 点左上角按钮 → 插件设置 → 右下角加号 → 从网络安装插件 → 粘贴 URL。URL 以 .json 或 .js 结尾。

官方给了个示例仓库地址:

https://raw.gitcode.com/maotoumao/MusicFreePlugins/raw/master/plugins.json

注意是 gitcode.com 不是 github.com——国内访问 GitHub 不稳,作者备了一份镜像。

第二条:从本地装。 同一个入口,选「从本地安装插件」,挑一个 .json 或 .js 文件。适合自己写的、或者别人私下传给你的插件。

第三条:订阅。 地址和第一条一样。区别是订阅之后,源里更新了插件,你点「更新订阅」就能批量更新,不用一个个重装。

安装时会发生什么

这块逻辑值得讲,因为它解释了你会遇到的一个坑。

安装插件时,软件先检查有没有同名插件——同名的判断标准是 platform 字段相同。如果有,就比较 version:

  • 要装的版本 高于 本地 → 装
  • 要装的版本 低于或等于 本地 → 安装失败

版本号是 1.2.3 这种格式,从后往前比。1.2.4 > 1.2.3,2.0.0 > 1.99.99。

所以你没法用「装旧版」来降级。 想退版本,必须先卸载本地的新版,再装旧的。这是很多人第一次更新插件时踩的坑——从别处拿到一个旧插件,装不上,以为是文件坏了。

插件能干什么

按官方文档,插件支持这些功能:

功能 方法 说明
搜音乐 search 按关键词搜歌
搜专辑 search 同一方法,换 type
搜歌手 search 同上
拿播放地址 getMediaSource 最核心的一个
看专辑详情 getAlbumInfo 曲目列表
看歌手作品 getArtistWorks 专辑 + 单曲
导入单曲 importMusicItem 把外部歌曲加进库
导入歌单 importMusicSheet 整张歌单搬过来
拿歌词 getLyric 支持 LRC

九个方法,覆盖了一个音乐播放器的全部核心动作。

这里有个设计上的巧思:search 一个方法同时承担搜歌、搜专辑、搜歌手,靠 type 参数区分。少一个方法,插件作者就少写一份模板代码。

歌词关联:一个奇怪但有用的功能

README 里提到一个别处没见过的功能:歌词关联。

你可以把 A 歌的歌词,关联到 B 歌上。关联之后,放 B 歌显示的是 B 的歌词,但……等等,这里得说清楚——官方描述是「关联 A 的歌词到 B,两首歌都显示 B 的歌词」。

这个功能解决什么?举个实际场景:同一首歌,专辑版和现场版是两个不同的曲目 ID,插件只给其中一个配了歌词。你把有歌词的那个关联到没歌词的那个,两首都能显示歌词了。

还能链式关联:A → B → C,那么 A、B、C 都显示 C 的歌词。

这是个很小众的需求,但作者做了。 这种细节往往比大功能更能说明项目的成色——它意味着作者自己真的在用这个软件。

示例插件里有什么

官方示例仓库 MusicFreePlugins 里有一批现成插件。README 特意声明:示例仓库只封装公开的网络接口,会过滤掉所有 VIP、试听、付费歌曲,永远不会提供破解功能的插件。

这句话很重要,它是这个项目划下的线:软件本身不提供任何音源,也不提供破解。 你能听到什么,取决于你装了什么插件,以及那个插件对接的是什么。

作者在 README 里反复强调这点,还专门列了个「耻辱榜」:

  • 小米、华为、vivo 等应用市场里的「MusicFree」,全是抄了名字和图标的广告软件
  • 「速悦音乐」是二次开发版,改了内置插件、动了 UI、加了引流,不符合开源协议,且拒绝沟通

一个 27.7k 星的项目,被冒名顶替、被套壳引流,作者只能在自己的 README 里挂个耻辱榜。 这大概是开源作者最无奈的时刻。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

同名判断看 platform 字段,版本比对从后往前逐段比

一个绕不开的问题:插件安全吗

上一节说了,插件和软件跑在同一个 JS 上下文里,没有沙箱。

这意味着什么?一个恶意插件,能做的事和软件本身一样多。

它能读你的歌单、能改界面、能发网络请求、能在你不知情的情况下把数据传出去。官方文档写得很直白:

插件内有可能会有些破坏软件本体执行的逻辑,因此插件的使用者需要谨慎识别插件的来源。

作者给了个很实在的建议:最好自己看一下插件代码,确认没有奇怪的网络请求。 插件是 JS 写的,就算你不懂编程,搜一下 fetch、XMLHttpRequest、http 这些关键词,看看请求发到哪,也能判断个大概。

为什么不做沙箱?作者也解释了:

第一,性能。 沙箱意味着要序列化、反序列化,插件和软件之间传数据得走一遍转换,有开销。

第二,初衷。 项目一开始是技术探索,没想那么多安全性。

第三,可判断性。 JS 是明文,看一眼就知道大概在干什么。

这个解释算不上完美,但够诚实。它把选择权交给了你:你要开放性,就得自己承担判断的责任。

对比一下更主流的做法。浏览器扩展有权限声明,装的时候会告诉你要读哪些网站的数据;VS Code 插件跑在独立进程里,崩了不影响主程序。MusicFree 两者都没有——它选择了最简单粗暴的方式:插件就是代码,代码就是权力。

这不是缺陷,是取舍。你享受「随便写个 js 就能扩展播放器」的便利,就得接受「随便一个 js 也能干坏事」的风险。

用它的正确姿势是:只用你自己写的,或者你信得过来源的插件。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

浏览器扩展有权限声明与进程隔离,MusicFree 插件两者都没有

上手:从零到听到第一首歌

说了这么多原理,该动手了。

第一步:装应用

安卓版去 GitHub Releases 下 APK。如果 GitHub 打不开,把地址里的 github 换成 gitcode 试试——这是作者留的备用路径。也可以在公众号「一只猫头猫」回复「Musicfree」拿。

鸿蒙(HarmonyOS)也在支持列表里,这是不少同类项目没覆盖的。

桌面版麻烦一点。官方下载指向飞书云文档,或者你自己 clone 源码:

git clone https://github.com/maotoumao/MusicFreeDesktop cd MusicFreeDesktop npm install npm start

注意 npm install 可能要跑几分钟,Electron 相关依赖体积不小。跑完之后 npm start 会拉起一个桌面窗口。

第二步:装插件

打开应用,点左上角按钮 → 插件设置 → 右下角加号。

想快点听到歌,就选「从网络安装插件」,粘这个地址:

https://raw.gitcode.com/maotoumao/MusicFreePlugins/raw/master/plugins.json

这是官方示例仓库,里面有几个公开接口封装的插件。装完回到主界面,搜索框就能用了。

想更稳妥,就自己写一个。 后面有一节讲怎么写,二十行代码能跑通。

第三步:搜歌、播放

搜索框输入歌名,回车。有结果就点进去播。

播放页能看歌词、能调播放模式、能加进歌单。这部分和任何音乐 App 一样,不需要学。

第四步(可选):换主题

只有桌面版支持。去 MusicFreeThemePacks 仓库下几个主题包,放进主题目录,在设置里切换。

想自己做,就建个文件夹,放 index.css 和 config.json,改几个 CSS 变量就是一个新主题。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

官方示例插件地址托管在 gitcode,是 GitHub 之外的一份镜像

自己写一个插件:二十行代码

这是本文最硬的一节。看完你能写出一个能跑的插件。

最小可运行插件

新建一个 demo.js:

module.exports = {   platform: "我的第一个插件",   version: "0.0.1",   author: "你的名字",   srcUrl: "",   cacheControl: "no-cache",    // 搜索:软件给你关键词,你返回歌曲列表   search: async function (query, page, type) {     // query: 关键词  page: 页码  type: 搜索类型     return {       isEnd: true,           // 是否没有更多了       data: [         {           id: "1",                       // 唯一 ID           title: "示例歌曲",              // 歌名           artist: "示例歌手",             // 歌手           album: "示例专辑",              // 专辑           artwork: "",                   // 封面图 URL           duration: 240,                 // 时长(秒)           platform: "我的第一个插件",      // 平台名,要和上面一致         },       ],     };   },    // 拿播放地址:软件给你一首歌,你返回能播的 URL   getMediaSource: async function (musicItem) {     return {       url: "https://example.com/song.mp3",       quality: "standard",     };   }, };

把它放进插件目录,或者用「从本地安装插件」装进去。搜索框输入任意词,你会看到「示例歌曲」——插件跑通了。

真实插件要做什么

上面那个是假的,返回的是写死的数据。真插件要做的是:

第一步,找到目标音源的接口。 打开浏览器开发者工具,在目标网站搜一首歌,看 Network 面板里哪个请求返回了歌曲列表。

第二步,在插件里复现这个请求。 用 fetch 发同样的请求,带上必要的 header,拿到 JSON。

第三步,把返回的数据映射成 MusicFree 要的格式。 字段名对不上没关系,你手动转一下:

search: async function (query, page) {   const res = await fetch(     `https://api.example.com/search?q=${encodeURIComponent(query)}&page=${page}`   );   const json = await res.json();    return {     isEnd: json.hasMore === false,     data: json.list.map((item) => ({       id: String(item.songId),       title: item.songName,       artist: item.singerName,       album: item.albumName,       artwork: item.coverUrl,       duration: Math.floor(item.duration / 1000),       platform: "我的第一个插件",     })),   }; }

第四步,实现 getMediaSource。 这一步通常最麻烦——很多平台的播放地址是动态生成的,要拿 token、要拼签名、要处理加密。这也是为什么现成插件更有价值:别人已经把那套麻烦逻辑趟过一遍了。

几个容易踩的坑

坑一:插件不能引原生依赖。 官方文档明确说,插件里可以引第三方库,但只能引纯 JavaScript 库。带原生模块的(比如某些加密库)用不了。想在插件里做 AES 解密?得找纯 JS 实现。

坑二:版本号降不了。 前面说过,装旧版必须先卸载新版。

坑三:改了代码要重装。 插件是启动时扫目录加载的,你直接改目录里的 js 文件,得重启应用才生效。用「从本地安装」覆盖更省事。

坑四:调试靠 node。 不用每次都装进 App 试。插件本质是个 CommonJS 模块,直接在 node 里 require 它,调函数看返回值对不对,快得多:

node -e "const p=require('./demo.js'); p.search('周杰伦',1).then(console.log)"

用 AI 写插件

官方文档里专门有一节「用 AI 开发 MusicFree 插件」。作者的态度很务实:这活儿适合交给 AI。

原因不难理解:插件开发的大部分工作是「看懂一个网页的接口,把数据映射成另一种格式」——这是典型的模式匹配任务,正是 AI 擅长的。

你可以把目标接口的返回样例、MusicFree 的插件协议一起丢给 AI,让它生成初版,再自己调。比从零手写快得多。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

插件只能引纯 JavaScript 库,带原生模块的依赖用不了

它有哪些坑和局限

好话说完了,说点实际的。

坑一:没插件就是个本地播放器

这是最根本的一条。你下载完打开,搜不了任何歌。 对不熟悉这套逻辑的人来说,第一反应是「这软件坏了」。

官方文档得单独写一节讲怎么装插件,本身就说明这个门槛真实存在。

坑二:插件质量参差不齐

插件是第三方写的,作者管不了。某个音源搜不到歌、播放失败、歌词不对——大概率是插件的问题,不是软件的问题。

但普通用户分不清这两者。搜索结果为空,第一反应是「这 App 不行」,然后卸载。

坑三:插件会失效

这是所有「对接第三方接口」的项目共同的宿命。平台改接口,插件就挂。 插件作者不维护了,那个音源就死了。

MusicFree 对此没什么好办法——它只能提供订阅更新机制,让插件作者能推新版本。至于作者还愿不愿意维护,软件管不了。

坑四:法律边界模糊

这条得说清楚,因为它关系到你会不会惹麻烦。

MusicFree 软件本身不提供任何音源,也不提供破解。 官方示例仓库明确声明:只封装公开接口,过滤掉所有 VIP、试听、付费歌曲。

但插件是开放的。别人写的插件对接什么,软件管不了。 你装了什么插件、听了什么歌、这些歌的版权状态如何——责任在你自己。

README 里的原话是:「请合法合理使用,如有侵犯版权的内容请及时删除。」

这不是免责声明式的甩锅,是这个架构的必然结果。 一个不内置音源的播放器,没法为用户装的插件负责。

坑五:更新不稳定

作者自己说了:业余项目,每天半小时,很长时间内都会是不稳定的测试版,更新频率不固定。

这意味着你可能遇到:某个版本有 bug、issue 提了没人回、想要的功能等很久。

27.7k 星、277 个 open issue、每天半小时——这个比例本身就在说明问题。

坑六:桌面版分发拉胯

前面提过,桌面版没有一键安装包,下载指向飞书文档。对一个 9k 星的项目,这个体验不合格。

坑七:没有 iOS 版

只支持安卓和鸿蒙。iOS 用户用不了——这不是作者不想做,是 iOS 的限制让这类应用很难上架。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

277 个 open issue 对每天半小时的维护者,意味着大部分不会被处理

技术选型:为什么是这套组合

一个业余项目,每天半小时,技术选型必然偏向「少写代码、多靠现成」。

React Native + TypeScript(安卓)

安卓版用 React Native,不是原生 Kotlin/Java。

原因很实际:作者是前端背景。 从 package.json、metro.config.js、babel.config.js 这些文件能看出来,这是个标准的前端工程结构。

选 React Native 意味着:写一套 React 代码,跑在安卓上。UI 组件用 RN 的,逻辑用 TS 写,跟写网页差别不大。

代价是性能和包体积。RN 的启动速度不如原生,APK 也更大。但对一个音乐播放器来说,这些不是瓶颈——播放是原生层干的,RN 只负责画界面。

Electron + React(桌面)

桌面版用 Electron,同样不是原生方案。

Electron 的逻辑是:打包一个 Chromium + Node.js,你的网页跑在里面。好处是三平台一套代码,坏处是每个安装包都带一个浏览器,体积动辄上百 MB。

对一个业余项目,这个取舍很合理:用体积换开发效率。 作者没精力为 Windows、macOS、Linux 各写一套原生界面。

两端不共用代码,但共用协议

这是整套架构最值得学的点。

技术上,RN 和 Electron 的渲染层完全不同,代码没法直接复用。如果强行抽象一层「跨平台 UI 框架」,复杂度会爆炸。

作者的选择是:界面各写各的,协议统一。

  • 安卓版:自己实现一套插件加载器和调用逻辑
  • 桌面版:再实现一套,但方法名、入参、返回格式完全照抄

结果就是前面说的:同一个插件,两端都能跑。

这个决策的聪明之处在于,它把「复用」放在了正确的层次上。复用的不是代码,是契约。 代码复用要求技术栈一致,契约复用只要求双方遵守同一份约定——后者的适用范围大得多。复用的不是代码,是契约。—— 猫头猫在 MusicFree 两端架构里的选择

放到更大的视角看,这也是很多成熟系统的做法:HTTP 协议让任何语言写的服务器和客户端能通信,SQL 让任何语言写的程序能查同一个数据库。协议比代码活得久。

数据全在本地

README 里有一句:所有数据存储在本地,软件不会收集任何个人信息。

没有账号体系,没有云端同步,没有埋点上报。你的歌单、播放记录、插件配置,全在手机或电脑的本地目录里。

好处是隐私干净。坏处是换设备得手动迁移,而且数据丢了就是丢了。

对一个不商业化的开源项目,这是最省事也最诚实的选择——不收集,就不用承担保管责任。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

协议比代码活得久——复用的是契约,不是实现

跟同类比,它站在哪

「插件化音乐播放器」不是 MusicFree 独创。把几个主要的放一起看,能看清它的位置。

Listen1

Listen1 是这个思路的先行者之一,支持网易云、QQ、虾米、酷狗、酷我、B 站等多个平台,有浏览器插件、桌面版、安卓版。

它和 MusicFree 最大的区别是:音源是内置的。

Listen1 自己维护各个平台的接口,你装完就能搜。听起来更方便,但代价是:接口一变就得跟着改,改不及时整个平台就废了。 而且内置音源意味着它自己要面对版权问题。

MusicFree 反过来——它不内置任何音源,把这件事彻底外包给插件作者。 软件永远不会因为某个平台改接口而失效,失效的只是那个插件。

YesPlayMusic

YesPlayMusic 是网易云音乐的第三方客户端,界面做得非常漂亮,支持桌面和网页。

但它的定位完全不同:只对接网易云一个平台。 不是通用播放器,是「更好看的网易云」。

它证明了一件事:把界面做好,本身就有价值。 但它也受制于单一平台——网易云改接口,它就得改。

其他

还有一些更小众的项目,比如各类「音乐聚合器」、各种基于 Web 的播放器。它们的共同问题是:要么内置音源维护不过来,要么插件生态没做起来。

MusicFree 的位置

把它放进这张图里,能看出它的独特性:

项目 音源来源 平台覆盖 插件生态
MusicFree 全靠插件 安卓 / 鸿蒙 / Win / Mac / Linux 有,协议开放
Listen1 内置多平台 浏览器 / 桌面 / 安卓 无
YesPlayMusic 内置网易云 桌面 / 网页 无

在几个主流选择里,MusicFree 是少数把「音源完全外置、协议对外开放」这两件事同时做到的项目之一。

这个选择的代价前面说过:没插件就不能用、插件质量参差、插件会失效。但它换来了一个别人没有的东西:软件本体不会死。

只要播放器还在、协议还在,插件生态就能自我更新。作者每天只有半小时,但他不需要去追每个平台的接口变化——那是插件作者的事。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

Listen1 与 YesPlayMusic 都内置音源,接口一变就要跟着改

生态观察:27.7k 星背后是什么

看一个开源项目,星数只是表面。真正值得看的是:谁在用、谁在贡献、项目在往哪走。

数字

  • 主仓库
    27.7k 星,2.0k fork,890 commits,277 open issues,12 open PR
  • 桌面版
    9.0k 星,877 fork,509 commits,225 open issues,9 open PR
  • 协议
    AGPL-3.0

两个仓库加起来 36.7k 星。在「音乐播放器」这个品类里,这算头部。

但看另一组数字:277 个 open issue,12 个 open PR。 对一个每天只有半小时维护时间的作者,这个积压量意味着——大部分 issue 不会被处理。

这不是批评,是现实。一个人扛 27.7k 星的项目,只能做取舍:修影响大多数人的 bug,剩下的放着。

谁在贡献

从 fork 数(2.0k)能看出来,不少人在改这个项目自用。

但 PR 只有 12 个开着——说明大部分人 fork 之后自己改,不提回来。 这在个人项目里很常见:改动太个性化、或者懒得走 PR 流程、或者觉得提了也不会合。

真正的生态不在主仓库,在插件仓库。MusicFreePlugins 是官方示例,但社区里还有大量第三方插件仓库,散落在 GitHub、Gitee、各种论坛和群里。

这些插件没有统一的分发渠道,也没有质量把关。 你得自己找、自己判断、自己承担风险。

项目在往哪走

从 README 和更新日志能看出几个方向:

一、修 bug 为主。 277 个 issue 里,大部分是「某功能不 work」「某处崩溃」。作者的精力主要花在这上面。

二、桌面版在补齐。 主题包、iframe 背景这些,都是桌面版独有能力,说明作者在给桌面端加差异化。

三、AI 辅助开发。 官方文档专门写了「用 AI 开发 MusicFree 插件」,这是个很新的信号——作者意识到插件生态的瓶颈在「写插件的人太少」,而 AI 能降低这个门槛。

这个判断挺准的。 一个插件化项目,软件做得再好,没插件就是废的。把写插件的成本降下来,比给软件加功能更重要。

被冒名的那部分

前面提过耻辱榜,这里再说两句,因为它反映了一个更大的问题。

README 里列了两类:

  • 应用市场里的同名 App
    小米、华为、vivo 等市场里的「MusicFree」,全是抄名字和图标的广告软件
  • 速悦音乐
    二次开发版,改了插件、动了 UI、加了引流,不符合 AGPL 协议,拒绝沟通

对一个普通用户,这意味着一件事:你在应用市场搜到的 MusicFree,大概率不是真的。

官方渠道只有两个:GitHub 仓库,和公众号「一只猫头猫」。除此之外任何渠道的同名应用,都要警惕。

这是开源项目在国内的普遍困境。 AGPL 协议要求衍生作品也开源,但执行成本极高——个人作者没有法务团队,只能在自己的 README 里挂个名单。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

两个仓库合计 36.7k 星,但 PR 长期只有个位数开着

它适合谁,不适合谁

写到这儿,该给个明确判断了。

适合你,如果

你愿意花十分钟折腾。 装应用、找插件、装插件——这套流程对熟悉技术的人是小事,对普通用户是门槛。

你有明确的听歌需求,且不在乎「一键即用」。 你想听某首歌,愿意为它找个能播的插件,而不是打开 App 就有推荐。

你在意隐私。 数据全在本地,不上传、不登录、不埋点。这在今天的音乐 App 里几乎绝迹。

你喜欢自己掌控界面。 桌面版的主题包能让你把界面改成任何样子。

你用安卓或鸿蒙。 iOS 用户用不了。

不适合你,如果

你想要「打开就能听」。 这类用户占大多数,MusicFree 不适合你们。去用主流音乐 App,付费买个省心。

你不能接受插件失效。 某个音源突然不能用了,这是常态,不是 bug。

你需要稳定更新和技术支持。 每天半小时的业余项目,给不了这个。

你在意版权合规的绝对安全。 插件是你自己装的,责任在你。想完全干净,就用正版服务。

一句话总结

MusicFree 不是给「想听歌的人」做的,是给「想自己决定听什么、怎么听」的人做的。

它把选择权交给你,也把责任交给你。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

判断标准只有一条:你愿不愿意为自己的选择承担判断成本

一个插件作者的真实处境

前面都在讲软件,这一节讲讲写插件的人。

为什么有人愿意写插件

写一个能用的插件,工作量大概是这样:找一个音源、摸清它的搜索接口和播放接口、处理签名和加密、映射字段、调试。顺利的话一个周末,不顺利的话要一两周。

写完之后,你得到什么?

没有钱。 插件通常不收费,收费会立刻招来版权麻烦。

没有名声。 插件散落在各种仓库和群里,没人统计下载量。

有一点便利。 你自己想听的歌能听了,顺便分享给别人。

所以大部分插件作者的动机很朴素:我自己要用。

这也解释了一个现象:为什么很多插件写着写着就不更新了。 作者自己换了听歌方式、或者不再听那个平台、或者单纯没时间了——插件就停在那儿,直到某天接口变了,彻底失效。

插件维护的成本在哪

不是写的时候,是改的时候。

平台改接口,插件要跟着改。改一次可能只是换个 URL、加个 header,也可能要重新分析整个加密逻辑。

问题在于,你不知道它什么时候会改。

可能三个月不动,也可能明天就变。这种「随时待命」的状态,对业余作者很不友好——你不能规划时间,只能被动响应。

于是形成了一个循环:插件失效 → 有人抱怨 → 作者修一次 → 过段时间又失效 → 作者不修了 → 那个音源死了。

MusicFree 对此能做的有限。订阅机制能让作者推更新,但推不推是作者的自由。

AI 能改变什么

官方文档里那节「用 AI 开发 MusicFree 插件」,可能是这个生态最实际的出路。

传统流程里,写插件的门槛在于:你得看得懂接口、写得出 JS、调得动调试。 这三样筛掉了大部分人。

AI 能压缩的正是这部分。你把接口返回的 JSON 贴给它、把 MusicFree 的协议贴给它,让它生成映射代码——你只需要判断结果对不对。

门槛从「会写代码」降到「能判断对错」。这个变化不小。

但它解决不了维护问题。 AI 能帮你写出第一版,不能帮你盯着接口什么时候变。

这个生态的真实规模

没有官方统计。从能看到的信息推测:

  • 官方示例仓库里有几个插件,覆盖主流平台
  • 社区里散落着一些第三方插件仓库
  • 各种群里流传着打包好的插件文件

规模不大,但够用。 对一个「自己用」的工具,这已经是合理状态——你不需要一百个音源,你需要的是那几个你想听的。

MusicFree:一个空壳播放器,27.7k 星靠插件活着

插件作者没有收益,动机基本是「我自己要用」

结语:把播放器做成一个插槽

回到最开始那个问题:一个「什么都不会」的播放器,凭什么 27.7k 星?

现在能回答了。

因为它把「播放器」这件事,还原成了它本来的样子——一个插槽。

你插上什么,它就播什么。插黑胶播黑胶,插 CD 播 CD,插网络电台播网络电台。播放器本身不生产内容,它只负责把内容放好。

这个道理听起来简单,但市面上大部分音乐 App 走的是相反的路:它们把插槽和内容焊死在一起。 你想听歌,必须用它的内容;你想用别的内容,必须换一个 App。

MusicFree 把这个焊点拆开了。

拆开之后,事情变得有意思:

  • 软件作者不用再追着平台接口跑
    ,他只管播放器好不好用
  • 插件作者可以只对接自己关心的音源
    ,不用写一整个 App
  • 用户可以自己决定装什么、不装什么
    ,也自己承担判断的责任

三方各干各的,用一份协议连起来。

这套逻辑不新鲜。浏览器和网页、编辑器和插件、操作系统和应用,都是这么运作的。但在一个被商业公司牢牢把持的领域里,有人把它重新做了一遍,而且做成了 27.7k 星。

作者在 README 里写:「一星一次交易,会一直免费。」

这句话有点浪漫。一个人每天半小时,维护一个 36.7k 星(含桌面版)的项目,被冒名、被套壳、被应用市场的假货淹没,还在文档里认真写「用 AI 开发插件」的教程。

他大概是真的喜欢这件事。

如果你也用安卓或鸿蒙,愿意花十分钟折腾一下——去 GitHub 下个 APK,装个插件,搜一首你很久没听的歌。

听到的那一刻,你会明白这个「空壳」的意义。


  1. MusicFree 主仓库:https://github.com/maotoumao/musicfree — 安卓 / 鸿蒙版源码,AGPL-3.0
  2. MusicFreeDesktop:https://github.com/maotoumao/MusicFreeDesktop — 桌面版源码,Electron + React
  3. 官方文档:https://musicfree.catcat.work — 使用指南、插件开发文档、常见问题
  4. 示例插件仓库:https://github.com/maotoumao/MusicFreePlugins — 公开接口封装的示例插件

本文转自:https://mp.weixin.qq.com/s/EHgcfolgSQ8bFFUN8wqHKw

正文完
 0