装好 LX Music,界面干净,搜歌有结果,点播放却弹「换源失败」。它自己不存一首歌,音频全凭一段你亲手导进去的 JavaScript 脚本返回。这篇讲清它为什么长这样:源怎么接、服务怎么搭、桌面和安卓怎么同步,以及这套设计要你付什么账。
下载、安装、打开。第一眼挺讨喜:左边是搜索、歌单、排行榜、我的列表,中间是列表,底下一条播放栏。深色主题,图标克制,没有开屏广告,没有登录弹窗,也没有「开通会员」的角标。
搜一首歌,结果出来了,歌手、专辑、时长都对。点播放。
「换源失败,请尝试手动换源」。
换一首,一样。再换一首,还是一样。
第一次遇到这行字的人,多半会往三个方向猜:装错了包、网络抽风、软件坏了。三个都不对。它一点没坏,这就是它正常的样子。
作者对这件事的态度写在文档里:软件内的默认设置和界面操作不以新手友好为目标,装好之后建议先按自己的习惯把设置翻一遍,顺便读一遍播放列表机制和快捷键。这话说得很直,也很少见——多数软件会把「新手引导」当作必须做的功课,它反过来主动抬高门槛。
抬高门槛的意思不是为难人,是筛人。一个需要你自己接源的播放器,用户里注定有一部分会在第一步就走掉,剩下的那部分人不需要被哄。所以它没有新手向导,没有一键配置,没有「猜你喜欢」,也没有任何东西在你看不见的地方替你决定。
一、装完是个空壳,这件事是设计
LX Music 的中文名叫洛雪音乐助手,是一个开源的音乐播放器。桌面版用 Electron 加 Vue 3 写,跑在 Windows、macOS、Linux 上;安卓版是另一个独立项目,界面重画过,逻辑重写过。两端都免费,都没有账号体系。
但它自己不含任何音频。
播放按钮按下去之后,程序做的事只有一件:把这首歌的歌名、歌手、歌曲 ID 交给「源」,等对方返回一个链接,然后拿这个链接去解码播放。源没接上,就没有链接;没有链接,就没有声音。
所以它的使用顺序和别人是反的。别家是「装好就能听,想听更多就付费」,它是「装好是个壳,先把源接上,才有得听」。
这个顺序会让相当一部分人第一次就放弃。我见过最典型的抱怨是「搜得到为什么放不了」——因为搜索和播放在这套架构里是两条完全不同的链路:搜索走的是平台公开的检索接口,只要联网就有结果;播放走的是源脚本,没装源就没有第三步。
也正是这个顺序,让它在一个人挤人的赛道里活了下来。它把「音频从哪来」这件事,从软件内部搬到了软件外部。
搬出去之后,很多东西跟着变了。它不需要版权授权,所以敢免费、敢无广告、敢不注册;它不需要为曲库付钱,所以没有动力往你界面上塞任何东西。界面冷淡到有点孤僻,不是因为作者有洁癖,是这个结构里本来就没有需要推销的货。
软件早年内置过几个平台的源,后来因为版权投诉,把所有内置源都拿掉了。这件事的结果比过程重要:它从「一个能听歌的软件」变成了「一个能被接上源的播放器」。 听什么,交给你。
顺带说一句它没变的部分:本地文件不需要源也能放。你把硬盘上攒的无损拖进「我的列表」,它照放不误,歌词封面自己配。只有当你加的本地文件坏了、被删了,它才会拿歌名去搜一首在线的顶上——这时候才轮到源出场。
所以「免费」这个词得说清楚。软件本身确实不收钱,也没有任何地方能让你付钱,它连捐赠都不收。但它把成本换了一种形式收:你要花时间接源、维护源、琢磨为什么不出声。这笔时间账,第一次用的人往往算漏了。嫌麻烦的人觉得它难用,愿意折腾的人觉得它省心,分歧就在这儿——两边算的根本不是同一本账。

二、一个音乐 App,其实是三样东西
要看懂它为什么长成这样,得先把「音乐 App」这个词拆开。
一个能听歌的软件,里面其实装着三样东西:曲库,音频文件本身;检索,歌名、歌手、专辑、歌单、排行榜这些信息;播放器,解码、列表、歌词、音效、界面这一整套。
平时说的「我买了会员」,买的只是曲库的使用权。检索信息大部分是公开的,未登录状态下去平台自己的服务里问,一样能拿到结果。播放器更不值钱,它是一段可以被重写的代码。
三样捆在一起卖,是商业模式的选择,不是技术上的必然。拆开看,每一件都能独立存在:曲库可以只是硬盘上的一堆文件,检索可以只是几个 HTTP 接口,播放器可以只是一个能解码、能管列表、能滚歌词的壳。
LX Music 做后两件,把第一件留空。这就是它的全部设计。
先看检索这一层。 它要面对的第一个麻烦是各家字段不统一。同一个概念,在这家叫 songmid,在那家叫 hash,换一家又变成一串数字 id;歌手字段有的给数组,有的给一个用斜杠拼起来的字符串;封面有的直接给图,有的给一个需要再拼前缀的相对路径。
于是软件里得有一层归一化:把六路来源的返回结果,塞进同一个「音乐信息」结构里——统一的 ID、统一的歌名、统一的歌手、统一的专辑、统一的时长、统一的音质标签。这层做得干净,界面上就看不出来源差异;做得不干净,就会出现同名歌曲混乱、封面错位、时长显示 00:00 这种小毛病。
再看播放器这一层。 它拿到的不是一个文件,是一个链接。这就决定了它必须做流式播放:能边下边播,而不是等整首下完再响。切歌的时候要做预取,提前把下一首的地址取好,否则每次切歌都要等一两秒握手。解码要能吃 mp3、aac、flac 这些常见格式,flac24bit 这种高码率还得看设备撑不撑。
列表逻辑也在这层。它默认的规矩是「听到即入库」:你在搜索、歌单、排行榜里点一首歌,它会先把这首歌加进「试听列表」,再播放。试听列表是临时的、会被新歌挤掉的,收藏是长期的、要你手动加的。两个列表分开,是为了让你随手试听的行为不污染收藏。
还有个反直觉的设计叫「稍后播放」。加进去的歌进入一个优先级最高的队列,点「下一曲」会按序消耗它,但回不到队列里的历史——「上一曲」找不回来。而且只要你切歌不是通过上一曲、下一曲,而是直接双击了别的列表里的歌,这个队列会被直接清空。它的定位是「临时插队」,不是「第二个播放列表」,用之前得知道这条。
歌词也在这层处理,而且是四份:原文、翻译、罗马音、逐字。逐字那份格式很特别,[00:00.000]<0,36>测<36,36>试,方括号里是整句起点,尖括号里是每个字的起止毫秒。有了它才能做卡拉 OK 那种一个字一个字点亮的效果,普通 LRC 只能整句高亮。
音效同样是这层的东西。多段均衡器、卷积混响(自己选一份 wav 脉冲响应文件当空间)、声像扩展、变调。默认全关,因为这些处理都吃性能,也会改变原曲的味道。
拆到这儿就能看出区别了:曲库型播放器的产品经理在谈版权,接口型播放器的作者在谈解码和列表。 前者卖的是内容,后者磨的是工具。
对比一下更清楚:
| 维度 |
曲库型播放器 |
接口型播放器 |
| 听歌的前提 |
会员有效 |
源能返回地址 |
| 装完的状态 |
能听 |
壳 |
| 失效的表现 |
会员到期 |
换源失败 |
| 谁在维护曲库 |
平台 |
你自己 |
第四行是这张表里最硬的一行。「谁在维护」决定了你在这件事里到底是用户,还是半个运维。

三、自定义源,是一个跑在沙箱里的脚本
「源」不是配置文件,不是订阅链接,也不是一个填进去的网址。它是一个 .js 文件。
用 JavaScript 写,ES6+ 语法,UTF-8 编码。导入之后,这段代码会在 LX Music 里跑起来。它拿不到你的文件系统,也调不动 Electron 的能力,它被允许做的事情只有三件:发请求、返回地址、报告自己支持什么。
这个边界值得留意。它决定了源脚本能干什么,也决定了源脚本干不了什么——它不能读你的本地文件,不能往你的硬盘写东西,不能偷偷起一个后台服务。它能做的就是把一个 HTTP 请求发出去,把回来的东西整理一下交回去。
整个交互是一套事件收发。脚本启动时会拿到一个全局对象 globalThis.lx,里面装着:
| 成员 |
作用 |
EVENT_NAMES |
事件名常量:inited、request、updateAlert |
on(name, handler) |
注册回调,等应用来找 |
send(name, data) |
主动向应用发事件 |
request(url, opts, cb) |
发 HTTP 请求,不受跨域限制 |
utils |
buffer、crypto、zlib 三组工具 |
那个「不受跨域限制」是个关键让步。浏览器里发请求会被同源策略拦,源脚本没有这个烦恼,因为它的请求实际上由主进程代发。没有这条,绝大多数平台接口都对接不了。
两个事件构成一次握手。
inited 是脚本对应用说的第一句话,也是一份能力声明:我支持哪些源、每个源支持哪几档音质、要不要打开开发者工具。它必须在脚本跑完之后主动发出去。发出去之前只要抛过一次错,这个脚本就算初始化失败,直接作废——没有重试,没有降级,你在设置里只会看到一个永远转圈的「初始化中」。
request 是应用反过来找脚本:我要这一首,给我地址。回调必须返回 Promise,返回别的都不认。
还有一个 updateAlert 事件,是给源作者用的:脚本发现自己的接口变了,可以主动弹一个更新提示,带上更新日志和更新地址。一次运行只能调一次,日志长度有上限,超出的部分会被丢掉。这是这套体系里少见的「作者与用户之间的广播通道」——毕竟软件本体不维护源,源过期了只能靠源自己喊一声。
一个最小的骨架长这样:
/**
* @name 示例音乐源
* @description 只演示结构,不含任何真实接口
* @version 1.0.0
* @author 尧哥
*/
const { EVENT_NAMES, request, on, send } = globalThis.lx
// 声明这个源支持哪几档音质
const qualitys = {
kw: { '128k': '128', '320k': '320', flac: 'flac', flac24bit: 'flac24bit' },
}
// 应用来要地址
on(EVENT_NAMES.request, ({ source, action, info }) => {
if (action !== 'musicUrl') return Promise.reject(new Error('unsupported'))
return new Promise((resolve, reject) => {
request('https://example.com/api', { method: 'get' }, (err, resp) => {
if (err) return reject(err)
resolve(resp.body.url)
})
})
})
// 报告自己已就绪
send(EVENT_NAMES.inited, {
openDevTools: false,
sources: {
kw: {
name: '酷我音乐',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k', 'flac', 'flac24bit'],
},
},
})
三十行,一个能跑的空壳。文件开头那段注释不是随便写的,@name 会变成源列表里显示的名字,@description 是名字下面那行小字,导入之后你在设置里看到的就是它们。
真实可用的源,是在这个骨架上不断填请求、签名、解密、兜错。
源用 6 个键区分:kw 是酷我,kg 是酷狗,tx 是 QQ 音乐,wy 是网易云,mg 是咪咕,local 是本地音乐。前五个只支持 musicUrl 一个动作;local 多两个,lyric 和 pic——本地文件没有平台能问,歌词和封面得自己想办法,可以读同目录下的同名文件,也可以去网上找。
音质四档:128k、320k、flac、flac24bit。这里有个容易被忽略的点:界面上能选哪几档,不是软件定的,是脚本在 inited 里声明的。 脚本只报 128k,你的设置里就只有 128k 一项。脚本报了 flac,实际返回的却是 128k 的地址,软件也照播不误——协议里写得很直白,它只负责转发,不校验对方给的对不对。
看完这些成员,utils 那部分是全场最值得琢磨的。里面给了 md5、aesEncrypt、rsaEncrypt、randomBytes,给了 inflate 和 deflate,还给了 buffer.from 和 bufToString。
一个「取个播放地址」的接口,为什么要配 MD5、AES、RSA 和解压?
因为真实的平台接口不裸奔。参数要签名,响应被压缩,个别字段加过密。作者把这套工具做进宿主环境,等于用代码承认了一件事:写源的本质,是去对接一个并不打算配合你的接口。 这不是给新手准备的玩具,这是给能看懂网络请求的人准备的工具箱。


四、自己写源,会撞上什么
如果你打算自己动手,先把预期调对:这不是写完就能一直用的事,这是一件需要跟着接口不断修补的事。
第一道坎是签名。平台接口通常要求带上时间戳和签名参数,签名算法可能是参数排序拼串后做一次 MD5,也可能是对某个关键字段做 AES 加密。utils 里那几个方法就是为这一步准备的——md5 算摘要,aesEncrypt 造加密串,randomBytes 生成随机串防重放。
第二道坎是响应格式。返回的可能是嵌套 JSON,也可能是被压缩过的二进制;可能直接给地址,也可能给一个还得再请求一次的中间 ID。inflate 和 bufToString 是给这类情况兜底的,buffer.from 则用来处理 base64 和二进制之间的转换。
第三道坎是风控。同一个接口,请求太密会被限;请求头缺字段会被拒;换了 IP 之后行为不一致也会被拦。这些都没有文档,只能靠试。更麻烦的是它不对称:你花一晚上调通的东西,对方改一个参数名就全废,而你收到的是一个和「网络不通」看起来一样的报错。
写不出地址时,LX Music 给出的报错只有三句,得会分辨:
- 换源失败,请尝试手动换源:所有勾选的源都没返回地址。要么脚本初始化就没成功,要么它依赖的接口整体挂了。
- 请求异常,可以多试几次:请求根本没走通。网络、系统代理、对方风控,都可能有份。
- 音频加载出错,5 秒后切换下一首:地址拿到了,但那个地址在你这儿打不开。过期、防盗链、地域限制是常见原因。Windows 上如果开着系统代理,也会固定出现这句,把「Internet 选项」里的代理勾掉就好。
源在设置里其实只有三种状态:名字后面转圈的「初始化中」、打勾的正常状态、以及勾着但一直失败。三种状态对应三条完全不同的排查路径,混着查是最浪费时间的。
排查顺序也有讲究。先看源状态:如果名字后面一直显示「初始化中」,问题在脚本自己身上,往下就不用查了,翻代码比改网络快——多半是它在 inited 之前抛了错,或者依赖的接口第一次请求就失败。如果源显示正常但全都不出声,去手动换一次源,能出声说明是某一个源的问题,不能出声说明是网络或者脚本整体失效。最后才去查网络代理——这一步排在最末,是因为它最容易被误判,也最难被证伪。
还有一件常被忽略的事:源可以同时装好几个。 设置里勾选多个源,一个返回失败会往下试另一个;也可以在播放失败时手动换。这解释了为什么有些人的源列表里塞了七八个文件——不是因为贪心,是因为没有哪个能长久稳定,多备几个才能扛住下一次失效。
桌面版留了一条调试通道:inited 里把 openDevTools 打开,脚本能获得一个开发者工具窗口,console.log 直接可见。安卓版没有这个选项,出问题只能靠猜——这也是我建议先在桌面端把源调通、再往手机上搬的原因。
顺带一个常被踩的坑:歌单不能跨源打开。你从这家平台复制的歌单链接,必须在「打开歌单」的对话框里把歌单源切到对应平台,否则它会以为什么都没找到。歌单的检索和歌曲的播放是两套接口,源只管后者。

五、桌面和安卓,不是同一份代码
桌面版和安卓版看着像同一个软件的两套皮肤,其实是两个仓库、两套实现。
桌面版基于 Electron 30 以上加 Vue 3,覆盖 Windows 7 及以上、macOS、Linux。安卓版是独立的移动端项目,跟桌面版共享的只有「源脚本」这套协议和同步协议,界面和内部逻辑各写各的。
数据存在哪,是个实用信息:
| 系统 |
默认数据目录 |
| Windows |
%APPDATA%/lx-music-desktop |
| macOS |
~/Library/Application Support/lx-music-desktop |
| Linux |
~/.config/lx-music-desktop |
Windows 上有个小机关:程序目录里如果存在一个叫 portable 的文件夹,软件会自动改用它存数据。等于自带绿色版——整套拷进 U 盘,换台机器插上就能接着用,歌单、设置、下载记录都在里面。换机迁移也可以直接拷这个目录,比找导出按钮省事。
安装本身有两个坑,都跟权限有关。一是别选「为使用这台电脑的任何人安装」,选「仅为我安装」,装到当前用户目录下,否则容易在写配置时被拦。二是别在同一个系统里装好几份,残留的旧文件会把数据目录和注册表搅在一起,表现出来就是装完打不开、或者打开的是旧界面。
两端各自还有几样只有自己才有的东西。桌面版有独立的桌面歌词窗口,能横排也能竖排,可以锁位、调透明度、分别设置已唱和未唱的颜色,暂停或主窗口全屏时可以自动隐藏——写东西的时候把歌词贴在屏幕角落,比来回切窗口舒服。快捷键也分两档:窗口聚焦时才生效的本地键,和系统级的全局键,全局那档默认是关的,需要自己去设置里打开。
安卓版拿手的是移动场景:通知栏控制、定时关闭、后台播放。这些在桌面版上没有,也没必要有。所以这两端不是「谁替代谁」,是两个界面各自贴着自己的使用场景重画了一遍,共同点只剩数据格式和源协议。
两端真正的差异在源脚本的运行环境上:
| 差异 |
桌面版 |
安卓版 |
| 调试开关 |
inited 可开开发者工具 |
无此选项 |
| 工具方法 |
全部可用 |
部分不可用 |
| 宿主 API |
完整 |
极少 |
| 内置对象 |
未冻结 |
只能扩展,不能覆盖 |
最后一行值得多说两句。安卓版把预加载脚本和源脚本放在同一个环境里跑,为了不让源脚本碰到预加载脚本的东西,除了三个 toString,其余的 JavaScript 内置属性全部冻结。结果是 Array.prototype.push = ... 这种覆盖写法直接无效,但 Array.prototype.myPush = ... 这种扩展写法是允许的。
这条限制带来的实际后果是:桌面版能跑的源,拷到安卓上大概率跑不起来。 得找专门标注了移动端的,或者自己改。导入方式也不一样,安卓受存储权限限制,得在设置里走「本地导入」挑文件,没法像桌面那样把文件拖进去。
两端要真正连成一套,靠的不是账号,是同步服务——下一节说。

六、可自建的不只是源
标题里说「可自建服务」,指的不止是源。这套软件把自己拆成了好几个能被你接管的部件。
第一个是同步服务。 它解决的是「桌面收藏的歌,手机上怎么看到」。桌面版里有个「数据同步」设置,能做两种角色:服务端模式,让本机在局域网里给别的设备提供同步;客户端模式,去连另一个服务端。安卓版只有客户端模式。
局域网这套操作很朴素:桌面端选服务端模式、启用,它会显示一个地址和一个端口(默认 23332);手机端把这个地址填进「同步服务地址」,注意填完要按一下回车让它生效,再点「启用同步」。首次同步时如果两边列表都不空,桌面端会弹一个窗口让你选合并方式,之后就不用了,会自动按两边内容合并。
连不上时有个很干脆的验证方法:手机浏览器打开 http://桌面地址:23332/hello,能返回一行 Hello~ 就说明网络通了,问题在别处。通不了再看三件事——两边是不是连在同一个路由器下、电脑防火墙有没有拦这个端口、路由器是不是开了 AP 隔离(公共网络里常见)。
有服务器的话,官方还放出了一个独立的同步服务项目,可以部署上去,长期用、跨网络用。配上域名和 SSL 之后,客户端填 https://你的域名 就行。要注意这个功能官方标了实验性,而且传输是明文的——局域网里凑合,公网上务必套一层加密,别裸着暴露端口。
第二个是开放 API。 设置里打开之后,本机会起一个 HTTP 服务,默认只监听 127.0.0.1,端口 23330。它把播放器变成了一个可以被程序调用的对象:
| 接口 |
用途 |
/status |
当前状态:歌名、歌手、专辑、进度、时长、播放速率 |
/lyric |
当前 LRC 歌词全文 |
/lyric-all |
原文、翻译、罗马音、逐字歌词四份一起给 |
/play、/pause、/skip-next、/skip-prev |
播放控制 |
/seek、/volume、/mute |
进度、音量、静音 |
/collect、/uncollect |
收藏与取消收藏 |
/subscribe-player-status |
SSE 长连接,状态一变就推 |
/status 支持 filter 参数只取你要的字段,别每次都拉全量。要实时状态就别轮询,官方明确建议走 SSE:
# 只拿三个字段
curl "http://127.0.0.1:23330/status?filter=status,name,progress"
# 状态一变就推过来,长连接不关
curl -N "http://127.0.0.1:23330/subscribe-player-status?filter=status,name,progress"
第三个是 Scheme URL。 以 lxmusic:// 开头的协议链接,在浏览器里点一下就能唤起软件,可以直接搜索、开歌单、播放指定歌曲。作者还配了一个油猴脚本,装上之后网页上的歌能直接丢给桌面版放。
还有一个更土但更好用的东西:启动参数。给桌面快捷方式加一个搜索参数,双击就能直接搜某首歌,不用先打开软件再敲键盘。配合 Scheme URL 一起用,等于把「想起一首歌到听见」这段路压到了一步。
这几样凑齐,它的身份就变了。它不只是一个你点着用的播放器,还是一个可以被编排的音频节点。
想象几个具体用法:直播的时候,脚本订阅 /status,把当前播放的歌曲名推到屏幕上;写稿卡住了,用一个全局快捷键切下一首,不用切窗口;家里那套自动化,语音说一句「放点安静的」,由它去执行;甚至可以在自己的笔记软件里做一个按钮,点一下就把当前在放的歌记进日志。
这些事在任何一家平台的客户端上都做不到,因为它们不会把控制权交出来。它们的接口是给自家 App 用的,你的脚本进不去。
最后提醒一句边界:开放 API 默认只绑本机。别为了远程访问把它绑到 0.0.0.0、再往路由器上做端口映射——那等于把你电脑上的播放控制权随手放到了公网上,而它没有鉴权。

七、它的代价,写在协议里
讲到这儿,该把不好听的部分摆出来了。
先说法律边界。 它的许可协议是 Apache 2.0 加一份补充条款,补充部分有四条绕不开:
- 使用过程中产生的版权数据,务必在 24 小时内清除;
- 项目只用于技术可行性的探索与研究,不接商业合作,也不收捐赠;
- 禁止在违反当地法律法规的情况下使用;
- 协议原文里写着「音乐平台不易,请尊重版权,支持正版」。
这四条不是摆设,是作者给自己的定位。他做的从来不是正版替代品,而是一个研究用的播放器壳。这篇文章也不会提供任何具体的源地址——那种东西今天能用、下周就废,写进来只会让文章比软件先过期,还容易把读者带到不该去的地方。
再说技术边界。
源会失效,而且会经常失效。这不是 bug,是这套架构的固有成本:接口在产品侧随时可能改,改了你这边就断。你得接受「今天能听,明天可能要折腾半小时」这个节奏。也正是这个节奏,让它的用户群天然分层——愿意动手的人留下来了,只想听歌的人三天就卸了。
返回的音画不匹配,它管不了。协议里写得很清楚:它只是把歌名歌手交出去,对方返回什么链接,它就当那是这首歌。所以偶尔会出现播出来是另一首歌、或者干脆是一段静音的情况。这是设计上的已知缺陷,不是偶发故障,你也没法靠重装解决。
检索和播放的粒度也不一致。搜索能同时看到六个平台的结果,点下去却只能由当前勾选的源去取;歌单要对应平台的源才能打开;同一个歌单里混着不同平台的歌,换源的时候会出现「这首能放、那首不能放」。
下载和缓存这类边角也常有小事故。最常见的是下载报 ENOENT: no such file or directory——它想往你设置的目录里写文件,但那个目录没有写权限,或者盘符已经拔掉了。换个目录就好,不用重装。安卓上还有一类怪现象:每首歌放到一半自动切下一首,官方给的办法是去设置里关掉「音频卸载」,然后完全重启 App。这些都不是设计缺陷,是宿主环境的差异没抹平。
平台覆盖也有明确的缺口:目前没有 iOS 版,也没有 HarmonyOS NEXT 版的计划。苹果手机用户和纯血鸿蒙用户,这条路走不通。
还有一条要提前知道的信息:作者的开发精力已经主要转向新项目 Any Listen,那个项目实现了 LX Music 的大部分功能,还多了 WebDAV 相关的能力。LX Music 现在仍然可以正常下载和使用,但如果你打算长期依赖它,心里得有个数——别把它当成一个会一直往前跑的东西,也别把全部收藏只存在它一家手里。同步服务那套自建方案,某种程度上就是为这种情况准备的:数据在你自己的机器上,换个客户端还能接着用。

八、谁该装,谁别折腾
说到取舍,其实只有三类人真正需要它。
第一类,本地曲库为主的人。 你手上有一堆自己攒的无损文件,缺一个干净、不要会员、不偷偷上传的桌面播放器。LX Music 的 local 源就是为这个准备的:本地文件不用联网也能放,标签读不出来会退回文件名,歌词封面可以自己配。这条路它做得挺稳,而且跟版权无关,不会跟着接口一起失效。
第二类,愿意为「能改」付时间的人。 你会看网络请求,能读别人的脚本,出问题愿意翻日志而不是发帖问「为什么不能用」。这套东西对你来说不是软件,是一套工具,改起来比用起来有意思。
第三类,有服务器、想自己掌控数据的人。 你不接受歌单存在别人的云上,宁可自己部署一个同步服务。那它给的很对味:源是你的、同步服务是你的、控制接口只在本机,换客户端也不丢东西。
举个最平常的落地场景:一台不吃灰的旧笔记本接一块移动硬盘,专门装无损;LX Music 常驻在上面,开着服务端模式的同步;手机连同一个局域网填上地址,收藏双向合并;客厅那台小主机上写个脚本订阅 /status,把当前在放的歌名显示在屏幕上。整套东西加起来不花一分钱,也没有任何一个环节的数据经过别人的服务器。
这套方案的技术门槛其实很低,真正的门槛在心理上——你得接受「这件事由我负责」。源失效了是你的问题,同步连不上是你的问题,歌单丢了是你的问题。商业产品把这些都接过去了,代价是你要按月付钱,并且接受它的界面、它的推荐、它的开屏。两边都在做交换,只是换的东西不一样。
反过来说,三类人别折腾:
只想点开就听的人,别装。它需要你先做一步准备工作,这一步对不愿意动手的人是硬门槛,而且这一步没有官方答案。
需要车机、投屏、全家桶自动同步的人,别装。它的双端同步要你自己搭服务,跨设备体验跟商业产品差着量级。
不能接受「随时可能失效」的人,别装。付费买的就是稳定,这件事它给不了,也不打算给。

至于我自己,用法很朴素:桌面上常驻,本地文件为主,偶尔接个源听点买不到的现场版。它替代不了任何一个音乐会员,但它补上了会员给不了的那部分——一个不属于任何人、只属于我这台机器的播放器。
回头看,这套设计最聪明的地方,不在技术难度,在责任划分。它把「放什么」交出去,把「怎么放」攥在手里,于是它既不用为内容负责,也不必为内容妥协。界面干净、没有广告、不收一分钱,都是这个划分的自然结果。
代价是你要多干一点活。这笔交易划不划算,取决于你是想买一个结果,还是想留一个选择权——这也是云栈社区里常被拿出来聊的话题。
把曲库掏空之后,剩下的部分才看得出一个播放器的手艺。
—— 尧哥
参考
- LX Music 桌面版文档:
https://lxmusic.toside.cn/desktop/ — 技术栈、平台支持与文档总入口
- 自定义源脚本编写说明:
https://lxmusic.toside.cn/desktop/custom-source — 事件、动作、音质与工具方法的权威定义
- 开放 API 服务:
https://lxmusic.toside.cn/desktop/open-api — 本机接口、字段与 SSE 订阅
- 许可协议:
https://lxmusic.toside.cn/desktop/license — Apache 2.0 补充条款全文