9.9 KiB
MusicFree Pure Music_Server Plugin Design
日期:2026-04-19
状态:已确认设计,待实现计划
范围:Music_Server 榜单详情接口补齐、MusicFree 纯 Music_Server 插件、旧插件兼容壳
1. 背景
当前 Music_Server 已经提供这些面向 MusicFree 的能力:
GET /mf/v1/recommend/tagsGET /mf/v1/recommend/sheetsGET /mf/v1/playlists/{id}GET /mf/v1/playlists/{id}/tracksGET /mf/v1/toplistsGET /mf/v1/search/songsPOST /mf/v1/media/resolve
同时,当前 MusicFree 使用的 netease_17000.js 仍然是旧的网易 relay 风格插件,内部直接调用旧服务端接口,并带有与本轮目标无关的能力:
album搜索与详情artist搜索与作品lyricimportMusicSheet- 旧的网易与 relay 回退逻辑
本轮要把插件收敛成“纯 Music_Server 插件”,并把服务端缺失的榜单详情链路补齐,使 MusicFree 可以完整走“搜歌、看歌单、看榜单、播放”这一条自有服务链路。
2. 目标
本轮目标:
Music_Server补齐榜单详情与榜单歌曲分页接口MusicFree新增正式插件music_server.js- 旧插件
netease_17000.js保留为兼容壳,转发到新插件 - 插件只依赖
Music_Server,不再直连旧网易 relay 或其他平台 - 插件能力只保留当前服务端已支持的:
music搜索- 推荐歌单
- 歌单详情
- 榜单列表与榜单详情
- 播放解析
非目标:
- 不做
album搜索或专辑详情 - 不做
artist搜索或歌手作品页 - 不做
lyric - 不做
importMusicSheet - 不做插件侧多平台回退
- 不做插件直连网易、QQ、酷我等外部平台
3. 总体设计
采用“两端各补一小段”的方式完成:
-
Music_Server- 保持现有
/mf/v1/*结构不变 - 新增榜单详情与榜单歌曲接口
- 返回结构与歌单详情链路保持一致
- 保持现有
-
MusicFree插件- 新增正式插件文件
music_server.js - 旧文件
netease_17000.js退化为兼容壳 - 所有方法仅调用
Music_Server - 仅负责请求与字段映射,不承担内容回源逻辑
- 新增正式插件文件
这样做的原则是:
- 服务端负责内容真相与业务契约
- 插件只负责协议适配
- 旧入口兼容,新入口语义清晰
4. Music_Server 设计
4.1 数据模型现状
catalog_read.db 现有导出脚本 export_catalog_read.py 已经包含:
catalog_toplistscatalog_toplist_tracks
因此本轮不需要改读模型导出结构,只需要把现有数据通过读取层和路由层暴露出来。
4.2 读取层补齐
在 catalog_reader.py 中新增两个能力:
get_toplist(toplist_id: str) -> ToplistRow | Nonelist_toplist_tracks(toplist_id: str, page: int, page_size: int) -> list[PlaylistTrackRow]
约束:
get_toplist()从catalog_toplists取单条list_toplist_tracks()从catalog_toplist_tracks联结catalog_tracks- 排序按
position asc - 返回的歌曲对象字段与
list_playlist_tracks()保持一致:song_idnamesingersalbumcover_urlduration_ms
4.3 路由层补齐
在 mf_catalog.py 中新增:
GET /mf/v1/toplists/{toplist_id}GET /mf/v1/toplists/{toplist_id}/tracks?page=&page_size=
返回约束:
-
/mf/v1/toplists/{toplist_id}- 返回对象 shape 与
/mf/v1/playlists/{playlist_id}一致 id使用catalogsync:toplist:{toplist_id}platform固定返回catalogsync
- 返回对象 shape 与
-
/mf/v1/toplists/{toplist_id}/tracks- 返回对象 shape 与
/mf/v1/playlists/{playlist_id}/tracks一致 - 返回:
isEndmusicList
- 返回对象 shape 与
错误语义:
- 找不到榜单:
404 - 缺少或错误 Bearer Token:
401 - 空榜单:
200且musicList: []
4.4 与现有接口的关系
本轮不改这些接口的既有行为:
GET /mf/v1/recommend/tagsGET /mf/v1/recommend/sheetsGET /mf/v1/playlists/{id}GET /mf/v1/playlists/{id}/tracksGET /mf/v1/toplistsGET /mf/v1/search/songsPOST /mf/v1/media/resolve
也不改导出脚本中的歌单、歌曲、文件位置逻辑。
5. MusicFree 插件设计
5.1 文件布局
插件文件放在 D:\source\MusicFree\keep-alive-master\Music_Free。
本轮交付:
处理方式:
music_server.js作为正式插件文件netease_17000.js只保留一层兼容壳:module.exports = require("./music_server")
这样既保留旧入口,又让正式插件名称与实际能力一致。
5.2 插件元数据
建议插件元数据固定为:
platform: "Music_Server"version: 本轮实现版本author: 保留现有项目惯例supportedSearchType: ["music"]
userVariables 只保留:
baseUrlaccessToken
规则:
baseUrl指向Music_Server部署地址accessToken指向Music_ServerBearer Token- 插件不内置 NAS 地址、旧 relay 地址或其他平台地址
5.3 请求层规则
插件请求层保持极薄:
- 所有 JSON 业务请求带:
Authorization: Bearer <accessToken>
baseUrl统一去掉尾部斜杠- 不在插件内保存本地数据库或缓存目录
- 不做多平台搜索与回退
media/resolve 特殊规则:
- 若
stream.url是相对路径,例如/mf/v1/media/stream/...- 插件自动拼接为
${baseUrl}${url}
- 插件自动拼接为
- 若
stream.url已是绝对地址- 直接原样使用
5.4 插件能力映射
插件仅实现并暴露这些方法:
search(query, page, "music")- 对应
GET /mf/v1/search/songs
- 对应
getRecommendSheetTags()- 对应
GET /mf/v1/recommend/tags
- 对应
getRecommendSheetsByTag(tag, page)- 对应
GET /mf/v1/recommend/sheets
- 对应
getMusicSheetInfo(sheetItem, page)- 对应:
GET /mf/v1/playlists/{id}GET /mf/v1/playlists/{id}/tracks
- 对应:
getTopLists()- 对应
GET /mf/v1/toplists
- 对应
getTopListDetail(topListItem, page)- 对应:
GET /mf/v1/toplists/{id}GET /mf/v1/toplists/{id}/tracks
- 对应:
getMediaSource(musicItem, quality)- 对应
POST /mf/v1/media/resolve
- 对应
不再实现:
albumartistlyricimportMusicSheet
5.5 稳定 ID 规则
插件与服务端之间继续使用稳定 public id:
- 歌单:
catalogsync:playlist:{playlist_id} - 榜单:
catalogsync:toplist:{toplist_id} - 歌曲:
catalogsync:song:{song_id}
插件只负责从 public id 解析出最后一段真实 id,然后发给服务端路由。
6. 字段映射
6.1 歌单与榜单对象
服务端返回的歌单、榜单对象直接映射成 MusicFree 的 sheetItem / topListItem 风格对象,核心字段统一为:
idplatformtitlecoverImgdescriptionworksNumplayCount
6.2 歌曲对象
服务端返回的 musicItem 直接映射,核心字段:
idplatformtitleartistalbumartworkduration
6.3 播放源对象
getMediaSource() 返回:
urlheadersquality
其中:
headers来自服务端stream.headersquality优先取服务端selected_source.quality
7. 错误处理
7.1 插件配置错误
若 baseUrl 或 accessToken 为空:
- 插件直接抛出明确错误
- 不做隐式默认值兜底
- 不静默退回旧服务
7.2 浏览型接口错误
对于这些接口:
- 搜歌
- 推荐歌单
- 榜单列表
如果请求失败:
- 插件返回空结果
- 同时保留清晰错误信息,方便调试
7.3 详情接口错误
对于:
getMusicSheetInfogetTopListDetail
若详情页或 tracks 请求失败:
- 不伪造旧平台数据
- 返回空
musicList - 第 1 页如果头信息获取失败,不补假数据
7.4 播放解析错误
若 /mf/v1/media/resolve 失败或无可播放地址:
getMediaSource()返回null- 不回退到旧网易、QQ、酷我逻辑
8. 测试设计
8.1 Music_Server 测试
在 Music_Server 仓库补这些测试:
CatalogReaderget_toplist()list_toplist_tracks()
mf_catalog路由GET /mf/v1/toplists/{id}GET /mf/v1/toplists/{id}/tracks
重点覆盖:
- 正常返回 shape
- 第 1 页与末页
isEnd 404401
8.2 MusicFree 插件测试
在 MusicFree 仓库新增独立测试,测试目标是 music_server.js,不再复用旧 netease_17000.test.js 的网易 fallback 语义。
重点覆盖:
userVariables只有baseUrl与accessTokensupportedSearchType只有music- 搜歌走
/mf/v1/search/songs - 歌单详情走
/mf/v1/playlists/* - 榜单详情走
/mf/v1/toplists/* getMediaSource()正确处理相对流地址与绝对流地址netease_17000.js兼容壳导出与music_server.js相同插件对象
9. 验收标准
联调通过标准:
MusicFree能搜歌- 能看推荐歌单
- 能点进歌单并加载歌曲列表
- 能看榜单列表
- 能点进榜单并加载歌曲列表
- 能播放
Music_Server返回的流地址 - 插件逻辑不再依赖旧网易 relay 搜索或播放路径
10. 实施边界
代码边界保持如下:
Music_Server服务端代码只改D:\source\musicdl-catalog-sync-worktrees\Music_ServerMusicFree插件代码只改D:\source\MusicFree\keep-alive-master\Music_Free- 不回到
D:\source\musicdl老仓库实现服务逻辑
本 spec 只覆盖“纯 Music_Server 插件 + 服务端榜单详情补齐”这一小段工作,后续若要继续扩充 album、artist、lyric,需要新 spec 或新实现计划单独推进。