外观
音源插件使用教程
拼好歌不提供任何音源插件,也不分发、不推荐具体插件。它只提供运行插件的框架:你把自己准备的 JavaScript 脚本导入客户端,客户端在需要播放时向脚本索取一个可播放的音频地址。
目前只支持一种插件格式:洛雪音乐(lx-music)的音源脚本生态。 这是「遵循同一套脚本协议」,并非「兼容多种格式」—— 拼好歌的插件层按洛雪的 globalThis.lx 协议实现(协议版本 2.0.0,运行环境标识 mobile),其他格式的插件(各平台自研的插件包等)不能直接使用。
这一页讲三件事:支持什么样的脚本、脚本在哪里执行、以及怎么自己写一个。
支持什么样的脚本
拼好歌兼容洛雪音乐音源脚本协议(globalThis.lx,协议版本 2.0.0),与 lx-music 移动版的脚本机制一致。具体支持范围:
| 项目 | 支持情况 |
|---|---|
| 插件生态 | 仅洛雪音乐音源脚本(lx-music),不兼容其他格式 |
| 脚本语言 | JavaScript(ES6+),UTF-8 编码 |
| 音乐源 | 六个:kw kg tx wy mg local |
| 音质 | 128k 320k flac flac24bit |
| 操作 | 非 local 源只有 musicUrl;local 源另有 lyric 与 pic |
| 事件 | inited(初始化上报)、request(应用索取数据) |
| 网络请求 | 只能走 lx.request,它不受跨域限制 |
| 宿主 API | 只有 setTimeout / clearTimeout / setInterval / clearInterval 与部分 Buffer;没有 fetch、XMLHttpRequest、document、window、require |
这些脚本跑不起来
- 依赖浏览器或 Node.js 宿主 API(
fetch、XMLHttpRequest、fs、require、npm 模块)的脚本; - 部分经过混淆的脚本 —— 这类脚本通常在初始化阶段就抛错,客户端会判定为不可用;
- 需要
lx.utils.zlib的脚本可能失败:解压依赖运行时是否带pako,没带时inflate/deflate会直接 reject。
脚本在哪里执行
| 平台 | 可用的执行位置 |
|---|---|
| Android | 本机运行 / 专属服务器 |
| Windows / macOS / Linux | 本机运行 / 专属服务器 |
| iOS / 华为纯血鸿蒙 | 只能专属服务器 |
「本机运行」是把脚本放进客户端内置的 QuickJS 沙箱执行;「专属服务器」是把脚本放到你自己部署的服务端执行。两者的脚本写法完全一样,区别只在执行位置。详见平台与运行模式。
写一个自己的插件
第 1 步:写好头部注释
文件开头必须有注释块,客户端从里面读名称、版本、作者等信息,缺省时留空:
js
/**
* @name 我的音源
* @description 一句话说明这个源做什么
* @version 1.0.0
* @author 你的名字
* @homepage https://example.com
*/第 2 步:注册 request、发送 inited
脚本与客户端通过事件交互:注册 request 接收请求,初始化完成后发送 inited 告诉客户端支持哪些源。
js
const { EVENT_NAMES, request, on, send } = globalThis.lx
// 把 lx.request 包成 Promise 便于链式书写
const httpGet = (url, options = {}) =>
new Promise((resolve, reject) => {
request(url, options, (err, resp) => {
if (err) return reject(err)
resolve(resp.body)
})
})
on(EVENT_NAMES.request, ({ source, action, info }) => {
if (action !== 'musicUrl') return Promise.reject(new Error('unsupported action'))
const { musicInfo, type } = info
return httpGet(`https://api.example.com/url?mid=${musicInfo.songmid}&quality=${type}`)
.then(body => JSON.parse(body).url) // 必须返回 http(s) 地址字符串
})
send(EVENT_NAMES.inited, {
sources: {
kw: {
name: '示例源',
type: 'music',
actions: ['musicUrl'],
qualitys: ['128k', '320k']
}
}
})四条硬规则:
on(request, handler)的回调必须返回 Promise;musicUrl的 Promise 结果必须是能直接播放的 http(s) 地址字符串;send(inited, ...)必须在初始化阶段调用一次,在这之前抛任何错都算初始化失败;inited里声明的qualitys决定客户端能选哪些音质,没声明的音质不会去请求。
第 3 步:按音乐信息取地址
info.musicInfo 是歌曲对象,常见字段:
| 字段 | 说明 |
|---|---|
songmid / id / hash | 各源的主键,不同源用不同名字 |
name / singer | 歌名与歌手,适合做搜索兜底 |
interval | 时长 |
albumName | 专辑名 |
source | 来源源标识 |
meta.songId | 部分源把主键放在这里 |
取不到主键时可以退回用 name + singer 搜索。客户端换源时会带上目标源的参数,所以脚本只需要处理自己声明过的 source。
API 参考
globalThis.lx
| 成员 | 说明 |
|---|---|
version | 协议版本,当前 2.0.0 |
env | 运行环境,固定为 mobile |
currentScriptInfo | 当前脚本信息:name / description / version / author / homepage / rawScript |
EVENT_NAMES | { request, inited, updateAlert } |
on(event, handler) | 注册事件。request 的回调必须返回 Promise |
send(event, data) | 发送事件。inited 必须发一次 |
request(url, options, callback) | 发 HTTP 请求,返回一个可取消这次请求的函数 |
utils | 工具方法,见下表 |
lx.request 的 options 支持 method、headers、body、form、formData、binary、timeout(毫秒,上限 60 秒)。 回调签名是 callback(err, resp, body),resp 含 statusCode、statusMessage、headers、bytes、raw、body。
lx.utils
| 方法 | 说明 |
|---|---|
crypto.md5(str) | MD5 |
crypto.aesEncrypt(buffer, mode, key, iv) | AES 加密,仅 aes-128-cbc / aes-128-ecb |
crypto.rsaEncrypt(buffer, key) | RSA 公钥加密(NoPadding) |
crypto.randomBytes(size) | 随机字节 |
buffer.from(input, encoding) | 支持 base64 / hex / utf8 |
buffer.bufToString(buf, format) | 上一条的反向操作 |
zlib.inflate(buf) / zlib.deflate(buf) | 依赖 pako,未加载时不可用 |
宿主环境
- 定时器:
setTimeout/clearTimeout/setInterval/clearInterval; Buffer:from/alloc/allocUnsafe/concat/isBuffer(部分实现);- 内置属性被锁定:可以新增扩展(如
Array.prototype.myPush = ...),但改写已有方法无效。
兼容保留成员
lx.getConsole()、lx.createMainWindow()、lx.getSystemFonts()、lx.proxy 为兼容旧脚本保留,没有实际作用。 updateAlert 事件可以发送,但客户端当前不会弹出更新提示。
调试
- 打开设置 → 开发者选项 → 调试日志,客户端会把脚本里的
console.log收进调试面板; - 先用最小脚本验证
inited能通过,再逐步加业务逻辑; - 请求失败时把
err打出来,lx.request的错误里带状态码; - 播放失败时客户端会自动换源,注意区分「这个插件拿不到地址」和「地址拿到了但放不了」。
常见失败原因
| 现象 | 通常的原因 |
|---|---|
| 导入后显示不可用 | 初始化阶段抛错:语法错误、没发 inited、声明了不支持的源或音质 |
| 能搜到歌,播放失败 | musicUrl 返回的不是 http(s) 地址,或该地址已经失效 |
| 部分歌能放、部分不能 | 该源对某些歌曲没有对应音质,脚本里需要做降级 |
| 换源总是失败 | 脚本没有实现目标 source,或上游接口已变更 |
| 播放几秒后中断 | 拿到的是短时效 URL,需要重新请求而不是复用 |
合规与责任
- 项目不提供、不分发、不推荐任何插件,也不提供插件定制服务;
- 项目不提供任何音频内容,也不提供绕过平台授权或付费机制的方法;本页只说明脚本怎么写,不涉及从哪里获取内容;
- 插件由你自行编写或获取,来源与授权是否合法由你自行确认,本项目不对第三方插件的内容与行为负责;
- 请仅用于技术学习与交流,尊重版权、支持正版;因使用插件产生的版权数据请在 24 小时内清除。
参考与致谢
- 洛雪音乐(lx-music,以 Apache-2.0 许可开源)的音源脚本协议说明:https://lxmusic.toside.cn/mobile/custom-source
- 拼好歌的插件层遵循该脚本协议实现,插件沙箱与播放器的部分设计也参考了这个项目;拼好歌代码为独立实现,同样以 Apache-2.0 发布,与该项目无隶属或合作关系。
- 本页的 API 范围按拼好歌实际实现整理(
utils/localRunner与服务端沙箱共用同一份协议层),与上游文档存在个别差异,以上表为准。
