Wristhyme开发文档
示例网站公网音乐 API 已关闭。接口示例供阅读,自建部署后使用。
本页目录

公开音乐 API

文档首页 · 数据模型及通用错误

本文件全部接口为 GET。除音频接口外均返回 JSON,不要求管理员登录。全局设备/IP封禁和访问策略可能使任意 /api/* 返回 403/503;单接口下方只列主要分支,不重复全部通用错误。

1. 健康与目录

1.1 GET /api/health

无查询参数。

{"status":"ok","app":"Wristhyme","version":"2.1.1","access":"public"}

200 只表示该网关处理了健康请求,不验证上游搜索、插件、音频或完整播放。version 示例来自网关快照,不是当前 Android 版本。启用访问策略后健康接口仍可能被封禁/策略错误拦截。

1.2 GET /api/sources

无查询参数。200:

{
  "sources":[
    {"id":"qq","name":"QQ音乐","backend":"qq","plugin":""},
    {"id":"netease","name":"网易云音乐","backend":"netease","plugin":""}
  ],
  "catalog_revision":1790812800,
  "default_plugin":"0123456789abcdef"
}
  • 只返回启用目录项,且自定义别名的基础平台也必须启用。
  • 关联 plugin 禁用时返回空;关联插件只是推荐信息,不代表客户端必须或已经切换。
  • default_plugin 是当前有效默认,空字符串表示没有有效默认。客户端使用稳定别名进行解析,避免保存一个随后变化的真实默认 ID。
  • catalog_revision 是策略投影的 Unix 秒时间戳,不是严格递增事务序号;同秒修改可能相同,不能作为唯一变更检测依据。
  • 基础七平台有特定展示顺序;不要要求响应永远恰好七条。
  • 无启用项时可以正常返回 sources: [];客户端不得硬编码补回禁用项。

2. 通用搜索与歌单

2.1 GET /api/search

查询参数 类型 必需 默认/范围
source string 是 /api/sources 返回的 ID;可为自定义别名
q string 是 空白折叠后1..100字符,不能以http:/https:开头
page integer字符串 否 网易云默认1,范围1..20;其他平台当前通用接口忽略此字段
refresh string 否 仅 1 绕过搜索缓存;其他值按非刷新处理

网易云每次最多20首,其他普通平台最多60首;哔哩哔哩最多20个视频首分P结果。自定义 source 先映射到 backend,响应歌曲和顶层 source 保持真实平台。

普通平台 200:

{
  "source":"qq",
  "songs":[
    {"key":"11111111111111111111111111111111","ref":"<SIGNED_REF>","name":"示例歌曲","artist":"示例歌手","album":"示例专辑","duration":"240","source":"qq","original":false,"cover":""}
  ],
  "note":"",
  "query":"示例歌曲",
  "state":"ok"
}

网易云 200 在上述结构上增加:

{
  "page":1,
  "page_size":20,
  "total":120,
  "has_more":true,
  "search_adapter":"netease-signals-221",
  "ranking_scope":"current_page",
  "ranking_note":"原版和热度缺乏已验证字段时为未知;版本仅依标题/别名线索,平台次序只作稳定兜底。",
  "dedup":{
    "duplicate_ids_removed":0,
    "duplicate_reissues_collapsed":1,
    "policy":"same-title-artist-version-duration-span-1000ms",
    "scope":"current-page",
    "raw_count":20,
    "display_count":19
  }
}

示例是扩展字段片段,完整响应仍包含 songs/source/query/state/note。total 可为 null;去重后歌曲数可以少于 page_size。分页依据上游总量,而非返回列表长度;第20页可能仍报告上游有更多,但本接口不能继续请求第21页。

空结果为200、songs:[]、state:empty,可能附平台限制提示。平台搜索错误不能视为空结果,网易云或哔哩哔哩显式错误分支为502、state:error、songs:[]、error。

主要错误:400参数不合法;403禁用;429繁忙;502上游失败。非空搜索缓存90秒。没有通用跨源聚合搜索参数,也没有可自定义 page_size 的公开合同。

2.2 GET /api/playlist_search

参数 类型 必需 默认/范围
source string 否 默认 netease;可用目录平台/别名
q string 是 空白折叠后1..100字符,不接受http:/https:开头

200:{playlists: Playlist[], source: string},最多20条。没有公开分页、刷新、排序或个性化参数;某个平台有适配器不等于它一定支持歌单搜索。空数组表示当前未读取到歌单。

主要错误:400参数无效;403禁用;429繁忙;502上游失败。

2.3 GET /api/playlist

两种输入方式:source + id,或者 link。非空 link 存在时,解析后的平台和ID覆盖 source/id。

参数 类型 必需 默认/范围
source string source/id模式需要 启用平台/目录别名
id string source/id模式需要 [A-Za-z0-9_-]{1,180}
link string 否 下述受支持完整URL,不支持短链
page integer字符串 否 默认1,转换整数后钳制到1..100;非整数返回400

200:

{"songs":[],"source":"netease","id":"123456789","page":1,"has_more":false,"note":"该歌单无可读取曲目,可能为空、私密或平台受限"}

每页最多100首;不会连续抓取全歌单。has_more 优先取上游页数;没有页数标记时按本页是否达到100首估算,所以不是绝对准确的全量计数。

完整链接合同:

平台 允许主机 ID提取形式
网易云 music.163.com、y.music.163.com 路径含playlist,query或fragment query中数字id;如 https://music.163.com/#/playlist?id=123456789
QQ y.qq.com、i.y.qq.com /playlist/数字、/taoge/数字,或disstid/id数字查询参数
酷狗 www.kugou.com、m.kugou.com /songlist/gcid_字母数字、/special/数字、/special/single/数字,或specialid数字参数

链接可用HTTP/HTTPS,但禁止用户名/密码及80/443以外端口;服务器只提取ID,不抓取任意用户链接、不展开跳转。整个链接必须 URL 编码,尤其是 #、? 和 &。

主要错误:400链接/ID/分页无效;403平台禁用;429繁忙;502上游失败。

3. 轻量列表

这组路径名带 watch,但也可供手机等客户端调用。固定每页10项,无自动后台预取;共用2并发槽和300秒缓存。

3.1 GET /api/watch/search

参数 必需 说明
source 是 启用平台/目录别名
q 是 与通用搜索相同的1..100字符约束
page 否 默认1,必须1..100,越界返回400
refresh 否 1 刷新请求页;首页刷新会清理相应搜索页缓存

200包含 songs: Song[]、source、page、page_size:10、has_more、query。

网易云还可包含 total/dedup/search_adapter/ranking_scope/ranking_note/note;使用独立10首的上游分页,不能混用通用20首分页的page偏移。网易云第20页强制has_more=false,第21..100页正常返回空和has_more=false。

哔哩哔哩从有界的最多20条快照切页,并非继续搜索后续平台页。其他平台依上游分页标记;若上游当前页与请求页不符,返回空页且has_more=false,防止无限重复加载。

主要错误:400参数无效;部分目录ValueError也被该模块包装为400;403禁用;429加载繁忙;502其他上游异常。因此轻量网易云失败的HTTP包装与通用搜索不完全一致。

3.2 GET /api/watch/recommend

参数 必需 说明
source 是 只支持基础平台QQ/网易云,别名映射后同样适用
page 否 默认1,范围1..100
refresh 否 仅page=1且refresh=1重新获取推荐快照

200:{playlists: Playlist[], source, page, page_size:10, has_more, note}。

服务器读取最多100个去重的公开推荐歌单,客户端分页。不是登录账号个性化日推;没有账号、历史听歌、推荐模型参数。平台没有可读推荐时当前模块抛ValueError并返回400,不是必然200空数组。

主要错误:400不支持平台/页码/推荐不可读;403禁用;429繁忙;502其他上游失败。

3.3 GET /api/watch/playlist

参数 必需 说明
source 是 只支持QQ/网易云
id 是 [A-Za-z0-9_-]{1,180}
page 否 默认1,范围1..100
refresh 否 1刷新当前页,首页刷新同时清理同歌单其他页缓存

200:{songs: Song[], has_more, source, page, page_size:10}。没有 link输入合同,响应也不保证包含id/note。过滤基础平台不符的歌曲;发现分页标记错位时返回空列表。

主要错误:400参数无效;403禁用;429繁忙;502上游失败。

4. 热歌榜

GET /api/charts/hot

参数 必需 说明
source 是 qq或netease;音源别名经公共策略映射后可用
page 否 默认1,1..100;不钳制,越界400
refresh 否 默认0,只接受0/1;仅首页的1才清理该平台热榜缓存

200:

{"songs":[],"total":0,"has_more":false,"period":"2026-10-01","source":"qq","title":"QQ音乐热歌榜","chart_id":"26","page":1,"page_size":10,"note":"官方公开热歌榜 · 2026-10-01"}

此为结构示例;真实有效榜单首页通常有歌曲。QQ固定榜单26,网易云固定3778678,每页10首。period是平台更新标识/日期,不应自行当作完整精确时间戳;缺失时空字符串。

QQ按平台offset拉取并校验rank与页码;网易云最多读取1000个有效去重条目后本地切页。total 在QQ来自平台截断到0..1000,在网易云是已读有效行数。某页无数据不一定是错误;首页/应有数据的位置返回异常内容则502。

每平台独立加载锁,第二个并发请求可能429。缓存300秒;没有任意榜单ID、topid、page_size、登录个性化或媒体下载参数。禁用检查在慢请求前后都执行。

主要错误:400不支持平台/页码/refresh;403禁用;429榜单加载中;502平台内容异常/超时。

5. 恢复与原平台音频

5.1 GET /api/restore

查询参数 ref: string 必需,长度最多12000字符。200直接返回一个 Song,不包在song字段中。

400:引用为空/损坏/签名不匹配、来源无效或其平台已禁用。虽然文案可能写“签名过期”,ref没有内置TTL。签名密钥轮换会使旧ref失效;访问策略异常还可能被外层包装为其他错误。

5.2 GET /api/inspect

查询参数 key: string 必需。检查原平台,不检测后台默认插件音频。

200可能为:

{"valid":true,"size":"8.00 MB","bitrate":"267 kbps","format":"MP3","quality_note":"格式由上游地址标识;码率为估算值,不代表无损认证"}

上游早期失败可能只给valid=false,网关仍补format/quality_note;size和bitrate不保证存在。size是人类可读字符串,不是字节整数。format为FLAC/MP3/M4A/AAC/OGG/WAV/未知,根据地址后缀推断;url字段被移除。估算码率与文件扩展名不能证明无损或授权。

主要错误:410key不可用;429繁忙;502上游失败。valid=false可正常200。

5.3 GET /api/media

参数 必需 说明
key 是 服务器已知歌曲
download 否 仅1增加附件响应头;否则普通音频流

请求可带单一Range。成功为200/206二进制音频,常见MIME:audio/mpeg、audio/flac、audio/mp4、audio/aac、audio/ogg、audio/wav。实际类型结合字节验证,不能仅依据平台名或Content-Type。

可能透传 Content-Range、Accept-Ranges、ETag、Last-Modified、Content-Length,以真实上游存在为条件。没有保证一定提供长度、范围或校验标记。

download=1 的附件名由标题和歌手清理后组成,头格式包含ASCII后备名及UTF-8 filename*;不会创建服务器下载任务、写服务器音乐库,也不切换后台默认插件。

Range与续传:

  • 只接受正则 bytes=\d*-\d*:典型 bytes=0-、bytes=1024-2047、bytes=-1024;多段范围拒绝。
  • 这是格式检查,不代表上游支持每种范围;非音频、续传资源变化也会失败。
  • 请求了Range但响应为200时表示不能把body直接追加到旧文件,应按下载器策略从头处理。
  • 上游416可能被包装为外层502 + upstream_status=416;本地格式检查416与上游不支持要区分。
  • 仅Range被网关显式转发;不要依靠发送If-Range来保证服务端条件续传。客户端自行核对响应范围、ETag、长度和已保存资源身份。

主要错误:410key不可用;416本地Range不支持;429繁忙;502平台拒绝/非有效音频/资源变化;504连接失败或超时。

5.4 GET /api/playback_status

查询参数 key 必需。200示例:

{"state":"streaming","content_type":"audio/mpeg","source":"qq"}

其他状态为 {state:"unknown"} 或 {state:"error",error,source,upstream_status?}。状态有效观察窗口180秒,最多1200条进程内记录。

该状态只反映原平台 /api/media 部分取流分支,不代表客户端解码、已播放时长或完整下载。插件媒体不更新此表,部分媒体验证失败也不会写error,所以unknown/streaming不能作为最终播放验收。

主要错误:410key不可用;全局403/503。该接口不走普通六槽慢请求限流。

6. 歌词与快照

6.1 GET /api/lyric

查询参数 key 必需。200:

{"lyric":"[00:00.00]示例歌词","source":"netease","key":"11111111111111111111111111111111","lyric_adapter":"square-karaoke-1"}

上游请求format=lrc,读取最多128KiB并规范化换行/部分方括号逐字时间到增强LRC。适配不会创造缺失的逐字时间;相邻行首时间戳表示重复行,不应误当逐字标签。

此接口不是结构化逐字数组或翻译专用API;普通LRC、增强LRC和客户端支持的其他解码能力应区分。空字符串允许200,不能据此生成歌词。旧部署可能没有lyric_adapter字段;上游格式质量也不由适配标识保证。

主要错误:410key不可用;429繁忙;502上游失败。该路径调用原平台歌词,不自动跟随后台默认插件。

6.2 GET /api/snapshot

查询参数 key 必需。用于下载时取得曲目信息和歌词,不下载音频、不保存服务器文件。

200:

{"name":"示例歌曲","artist":"示例歌手","album":"示例专辑","duration":"240","source":"qq","key":"11111111111111111111111111111111","captured_at":1790812800000,"lyric":"","lyric_state":"unavailable","lyric_note":"未保存歌词:下载时平台未提供"}

captured_at为Unix毫秒。lyric_state为saved/unavailable;正常取得可接受歌词时saved,其余通过lyric_note说明超限、不可用、失败或超时。歌词抓取失败通常仍返回200及歌曲信息,不能把unavailable当作整个快照请求失败。

最多128KiB歌词,快照会检测多1字节的超限,并排除常见HTML/JSON错误内容。返回不包含ref、cover或search_signals;需要长期恢复引用时另存原Song.ref。

主要错误:410key不可用;429繁忙;外层502异常。快照的原平台歌词不保证等于插件播放来源歌词。

7. LX插件

支持平台与默认别名见 协议。第三方可能获得服务器IP及当前在线歌曲元数据;不会因为此接口取得设备本地整个音乐库。consent=1是接口请求合同,不由管理员会话代替。

7.1 GET /api/plugins

无查询参数。200:

{
  "plugins":[
    {"id":"0000000000000000","name":"跟随后台默认 · 示例插件","version":"1.0.0","sha256":"<SHA256>","imported":true,"status":"imported","detail":"已导入服务器,尚未运行检测","sources":{},"is_default":true,"target_plugin":"0123456789abcdef"},
    {"id":"0123456789abcdef","name":"示例插件","version":"1.0.0","sha256":"<SHA256>","imported":true,"status":"imported","detail":"已导入服务器,尚未运行检测","sources":{}}
  ],
  "runtime":"quickjs-isolated-process",
  "notice":"启用后第三方将获得服务器IP和当前歌曲信息;不读取或上传设备本地音乐库。",
  "default_plugin":"0123456789abcdef"
}

只显示当前启用项;默认有效时首项可能为别名,复制真实插件元数据并增加is_default/target_plugin。别名不满足“ID等于SHA256前16位”,客户端应明确识别。

状态包括imported/initialized/failed;checked_at可能存在,单位Unix秒;网关最后取流结果可能覆盖status/detail。sources来自插件初始化声明,未检测时可为空。声明样例:

{"wy":{"actions":["musicUrl","lyric"],"qualitys":["128k","320k","flac"]}}

注意字段拼写是qualitys,不是qualities。声明支持、初始化通过和每首歌实际可播放互不等价。

主要错误:503运行时/策略不可用,可能附plugins空数组。无需consent即可读目录,该接口不占插件操作双槽。

7.2 GET /api/plugin_probe

参数 必需 说明
plugin 否 真实16位小写hex ID,或default/稳定别名;缺省走后台默认
consent 是 必须为字符串1

200示例:{ok:true,state:"initialized",sources:{},detail:"初始化通过;不代表每首歌曲可播放"}。422示例:{ok:false,error:"ValueError",detail:"插件执行失败,可能是接口离线、授权限制或不兼容的 LX 扩展"}。

会清除网关该插件最后取流状态,再触发脚本初始化;不需要歌曲key,不验证某首音频。可产生第三方网络请求,不应后台自动连续运行。

主要错误:400缺consent/ID无效;403禁用;429频率/并发;422执行失败;503无默认;502运行时通信失败。

7.3 GET /api/plugin_resolve

参数 必需 说明
key 是 服务器已知歌曲;仅五个LX映射平台
plugin 否 同probe;推荐新受管客户端使用0000000000000000
consent 是 1
quality 否 默认128k,必须在该插件该平台的qualitys中;无统一固定音质枚举

200:

{"url":"/api/plugin_media?ticket=<TICKET>","plugin":"0123456789abcdef","state":"resolved","expires_in":3600}

url为本站相对取流路径,plugin为映射后的真实插件ID。服务器只将已缓存歌曲元数据交给插件;拒绝直接上传任意musicInfo或audioURL。返回resolved只表示获取并校验了地址,不是已拉到有效音频。

票据3600秒、512条有界缓存;无需一次性消费,可重复请求或Range,但可能因淘汰/重启提前失效。票据没有在当前实现中绑定客户端IP或设备ID,拥有者可尝试访问,应视为临时访问凭据,不传播/记录完整URL。平台/插件禁用会使已有票据取流403。

主要错误:400consent/ID/不支持平台;403禁用;410key不可用;422插件失败/不声明音质;429频率/并发;503无默认;502异常/非法地址。

7.4 GET /api/plugin_media

查询参数 ticket 必需。无需consent、plugin或key,实际平台和插件从票据读取;仍执行全局封禁和票据内音源/插件启用检查。

成功200/206音频,Range合同、内容验证与原平台媒体相似;可能透传Content-Range/Accept-Ranges/ETag/Last-Modified/Content-Length。该接口没有download=1附件命名合同,客户端下载应自行保存已验证的二进制数据。

主要错误:410票据过期/未知;403插件或音源禁用;416Range不合法;429插件双槽繁忙;502第三方拒绝/非有效音频/续传资源变化/连接异常。该路径不调用原平台playback_status状态表,但会影响plugins的最近取流status/detail。

7.5 GET /api/plugin_lyric

参数key/plugin/consent与resolve相同;quality也可传递给运行时,默认128k,但歌词动作没有musicUrl的同一音质声明检查。

200仅为 {lyric: string}。插件value为对象时取其lyric,为字符串时直接使用,其他类型变为空字符串。该网关分支不保证与 /api/lyric 一样包含source/key/lyric_adapter或相同规范化流程。

主要错误与resolve相同,除音乐URL及票据签发相关分支。客户端仍须有自己的大小和格式限制。

8. 非API发布文件

GET路径 内容
/downloads/latest-phone.json 手机更新feed
/downloads/latest-watch.json 手表更新feed
/downloads/latest.json 旧双模式feed;不能代替当前两端独立feed
/downloads/Wristhyme-Phone-<version>.apk 手机安装包
/downloads/Wristhyme-<version>.apk 手表安装包
上述文件 + .sha256 SHA256文本
/downloads/*-source.zip 对应源码包,以实际发行文件名为准

两端feed主要字段:versionCode integer、versionName string、applicationId string、minSdk integer、apkUrl string、sha256 string、notes string;新发布可附 signerSha256。这些路径由静态文件服务处理,错误未必为JSON。

2026-09-30发行记录中两端为2.2.2/222;实际更新仍读各自feed。手机包名 cn.tolike.wristhyme.phone,手表 cn.tolike.wristhyme。下载前核对包名/版本/平台兼容性,下载后核对SHA256与签名,不仅依靠健康接口version或网页文案。