12 KiB
Music_Server 多类型搜索与歌手详情设计
日期:2026-04-23
状态:已确认设计,待实现计划
范围:catalog-sync、Music_Server、MusicFree
1. 背景
当前 Music_Server 面向 MusicFree 插件只支持单曲搜索,链路上存在三个直接影响使用的问题:
- 搜索页只能返回
单曲,无法返回歌手和歌单。 - 即使补出歌手搜索结果,当前 MusicFree 的歌手详情页默认固定为
单曲 / 专辑两个 tab,而本次目标是不再展示专辑,只展示该歌手的全部歌曲。 - 当前已有榜单详情能力,但如果把榜单混入“歌单搜索”结果,插件详情入口还不能自动识别并转发到榜单详情接口。
当前三端职责边界保持不变:
catalog-sync负责采集、同步、下载和维护源库catalogsync.db。Music_Server负责把源库导出成只读快照,并对 MusicFree 插件暴露/mf/v1/*接口。MusicFree负责插件调用、结果展示和详情页交互,不直接访问源站接口或服务端数据库。
本轮需要解决的是:
Music_Server支持单曲 / 歌手 / 歌单三类搜索。歌手搜索结果按平台分别显示,不做跨平台合并。- 点进歌手后只展示该歌手的全部可播歌曲,不再展示专辑。
歌单搜索同时返回普通歌单和现有榜单,并且点进去后能正常打开对应详情。
2. 目标与非目标
2.1 目标
Music_Server暴露 MusicFree 兼容的三类搜索接口。Music_Server导出稳定的歌手只读模型,避免运行时从songs.singers临时反推。Music_Server只返回至少包含 1 首可播歌曲的歌手和歌单结果。Music_Server歌手详情只返回该歌手的可播歌曲列表,并支持分页。Music_Server歌单搜索同时覆盖普通歌单和榜单。Music_Server插件同时支持music、artist、sheet三类搜索。MusicFree对Music_Server歌手详情仅展示单曲列表,不展示专辑 tab。
2.2 非目标
- 不做跨平台歌手身份合并。
- 不做歌手专辑详情能力。
- 不改动其它插件的歌手详情行为。
- 不新增新的播放解析策略。
- 不改动 MusicFree 全局搜索架构,只做与
Music_Server插件兼容所需的最小改动。
3. 方案选择
评估过三个方向:
- 运行时从
catalog_tracks.singers现算歌手搜索和详情。 - 在导出阶段补歌手读模型,再由服务端和插件接入。
- 服务端只提供少量接口,更多聚合逻辑放到插件端处理。
本轮选择方案 2,原因如下:
catalog-sync源库已经有artists和artist_songs表,可以稳定表达“平台内歌手”和“歌手作品关系”。- 歌手详情需要分页、排序和稳定 id,用导出读模型比字符串现算更可靠。
- 榜单混入歌单搜索后,插件只需识别 id 类型并分发详情请求,不需要自己做复杂聚合。
4. 核心设计
4.1 读模型扩展
在 catalog_read.db 中新增两张表:
catalog_artists
artist_id integer primary keyartist_key text not null uniqueplatform text not nullremote_artist_id textname text not nullnormalized_name text not nullavatar_url textdescription textplayable_song_count integer not null
用途:
- 作为歌手搜索结果和歌手详情头部的主表。
artist_id直接沿用源库artists.id,避免重新映射。artist_key沿用源库platform + remote_artist_id/normalized_name的稳定语义。
catalog_artist_tracks
artist_id integer not nullsong_id integer not nullposition integer not null
用途:
- 存储歌手与歌曲的展开关系。
- 用于歌手详情分页和稳定排序。
排序约定:
position由导出时确定,默认按歌曲名升序、song_id升序生成稳定序号。
4.2 导出策略
Music_Server/scripts/export_catalog_read.py 在现有导出流程基础上新增歌手导出:
- 从源库
artists读取歌手主体。 - 通过
artist_songs关联到songs。 - 通过
file_assets + file_locations(status='active')过滤出当前可播歌曲。 - 仅保留
playable_song_count > 0的歌手进入catalog_artists。 - 将这些可播歌曲关系写入
catalog_artist_tracks。
补充规则:
- 优先使用源库
artists关系,不从songs.singers反推歌手。 - 如果歌手缺少头像或简介,允许写空。
- 如果歌手没有可播歌曲,不进入只读库,这样搜索和详情天然只面向可用结果。
4.3 服务端接口
在现有 /mf/v1 之下新增四个接口:
GET /mf/v1/search/artists
参数:
qpagepage_size
返回 MusicFree 兼容形状:
isEnddata
单个结果字段:
id:catalogsync:artist:{artist_id}platform: 平台名,例如netease/qq/kuwonameavatarworksNum: 可播歌曲数descriptionsupportedArtistTabs:["music"]
GET /mf/v1/artists/{artist_id}
返回歌手详情头部字段,至少包含:
idplatformnameavatarworksNumdescriptionsupportedArtistTabs
GET /mf/v1/artists/{artist_id}/tracks
参数:
pagepage_size
返回:
isEndmusicList
歌曲仍复用当前单曲对象结构和播放链路。
GET /mf/v1/search/sheets
参数:
qpagepage_size
语义:
- 同时搜索
catalog_playlists和catalog_toplists - 将两类结果统一映射成 MusicFree 的 sheet 形状
单个结果字段:
id- 普通歌单:
catalogsync:playlist:{playlist_id} - 榜单:
catalogsync:toplist:{toplist_id}
- 普通歌单:
platformtitlecoverImgdescriptionworksNumplayableSongCountplayCount
4.4 查询语义与排序
单曲搜索
保持现有语义:
- 只返回有
active文件位置的歌曲。 - 排序为:
- 歌名精确匹配
- 歌名前缀匹配
- 歌名模糊匹配
- 歌手名模糊匹配
lower(name)升序song_id升序
歌手搜索
只搜索 catalog_artists,并保持平台内独立:
- 不做跨平台合并。
- 只返回
playable_song_count > 0的歌手。 - 排序为:
- 歌手名精确匹配
- 歌手名前缀匹配
- 歌手名模糊匹配
playable_song_count desclower(name) ascartist_id asc
歌单搜索
同时搜索 catalog_playlists.name 和 catalog_toplists.name:
- 只返回
playable_song_count > 0的结果。 - 结果合并后按以下规则排序:
- 标题精确匹配
- 标题前缀匹配
- 标题模糊匹配
play_count desc- 类型稳定顺序:普通歌单优先于榜单
- 稳定 id 升序
4.5 插件适配
Music_Server 的两个插件资产都要同步修改:
src/music_server/plugin_assets/music_server.jssrc/music_server/plugin_assets/music_server_lan.js
搜索能力
supportedSearchType改为["music", "artist", "sheet"]search(query, page, type)改为按类型分发:music->/mf/v1/search/songsartist->/mf/v1/search/artistssheet->/mf/v1/search/sheets
歌手映射
新增 mapArtistItem(...),负责把服务端歌手结果映射到 MusicFree 形状:
idnameavatarworksNumplatformdescriptionsupportedArtistTabs
歌手详情
新增 getArtistWorks(artistItem, page, type):
- 当
type === "music"时,请求/mf/v1/artists/{artist_id}/tracks - 其余类型返回空列表
歌单详情入口识别榜单
增强 getMusicSheetInfo(sheetItem, page):
- 若 id 前缀是
catalogsync:playlist:,继续走/mf/v1/playlists/* - 若 id 前缀是
catalogsync:toplist:,自动走/mf/v1/toplists/*
这样榜单即使从“歌单搜索”结果页点入,也能正常打开详情。
4.6 MusicFree 最小兼容改动
当前 MusicFree 的歌手详情页固定渲染 music 和 album 两个 tab。
本轮只对歌手详情页做最小改动:
- 如果
artistItem.supportedArtistTabs存在,则使用该数组作为当前歌手详情页 tab 集合。 - 否则继续回退到默认
["music", "album"]。 - 当 tab 只有一个时,不渲染 tab 栏,直接展示对应列表。
预期效果:
Music_Server歌手详情页直接显示“全部歌曲”。- 其它插件继续维持原本的“单曲 / 专辑”体验,不受影响。
5. 数据流
目标数据流如下:
catalog-sync持续维护源库中的artists、artist_songs、songs、playlist_songs、文件位置等数据。Music_Server执行导出脚本,把源库转换成包含歌手读模型的catalog_read.db。Music_Server通过CatalogReader对三类搜索与详情提供只读查询。Music_Server插件按music / artist / sheet三类请求分发到对应接口。- MusicFree 搜索页展示三类结果。
- 用户点击歌手结果后,插件只拉取该歌手的歌曲列表;点击歌单结果时,普通歌单和榜单都能按各自详情接口打开。
6. 错误处理与兼容性
6.1 服务端
- 空查询直接返回空列表。
- 不存在的歌手、歌单、榜单返回
404。 - 非法 id 返回
400或404,与现有公开 id 处理风格保持一致。 - 歌手详情和歌曲列表都只面向只读库中已导出的歌手 id,不做运行时兜底推断。
6.2 插件
artist类型的非music详情请求直接返回空结果,不抛异常。- 歌单详情里若识别到榜单 id,则自动转发到榜单详情,不暴露给上层页面额外判断。
- 若服务端返回空列表,插件返回
isEnd: true的空结果,保证 MusicFree 页面可正常结束加载。
6.3 客户端
- 对不带
supportedArtistTabs的旧插件保持原行为。 - 对
Music_Server插件,只有一个 tab 时隐藏 tab 栏,不影响列表分页和批量操作。
7. 测试策略
7.1 Music_Server
需要覆盖以下测试层次:
-
导出脚本测试
- 验证
artists/artist_songs被正确导出到catalog_artists/catalog_artist_tracks - 验证没有可播歌曲的歌手不会被导出
- 验证歌单搜索联合结果可覆盖普通歌单和榜单
- 验证
-
CatalogReader测试search_artists(...)get_artist(...)list_artist_tracks(...)search_sheets(...)- 排序、分页、空查询和边界条件
-
路由测试
/mf/v1/search/artists/mf/v1/artists/{artist_id}/mf/v1/artists/{artist_id}/tracks/mf/v1/search/sheets- token 鉴权兼容现有行为
7.2 插件与客户端
至少覆盖以下验证:
-
插件搜索分发
musicartistsheet
-
插件详情分发
- 歌手详情只拉歌曲
- 搜索结果中的榜单通过
getMusicSheetInfo(...)正确落到榜单详情接口
-
MusicFree 页面行为
Music_Server歌手详情只显示歌曲列表- 其它插件歌手详情仍保留
单曲 / 专辑
8. 实施范围
预计涉及以下文件:
Music_Server
scripts/export_catalog_read.pysrc/music_server/services/catalog_reader.pysrc/music_server/routes/mf_catalog.pysrc/music_server/plugin_assets/music_server.jssrc/music_server/plugin_assets/music_server_lan.jstests/test_export_catalog_read.pytests/test_catalog_reader.pytests/test_mf_catalog_routes.pytests/test_plugin_routes.py(如需校验插件导出字段)
MusicFree
src/pages/artistDetail/components/body.tsx- 可能附带
src/pages/artistDetail/store/atoms.ts - 可能附带
src/pages/artistDetail/components/resultList.tsx
原则:
- 只为支持单 tab 歌手详情做最小必要改动。
- 不改动全局搜索框架和其它插件约定。
9. 验收标准
满足以下条件即视为完成:
- MusicFree 中
Music_Server插件搜索页可切换并返回单曲 / 歌手 / 歌单三类结果。 歌手结果按平台分别显示。- 点进歌手后直接看到该歌手的全部可播歌曲,并可正常分页与播放。
- 歌手详情页不再展示专辑 tab。
歌单搜索可同时返回普通歌单和榜单。- 从搜索结果点进榜单时,详情页和歌曲播放都正常。
- 其它插件的歌手详情行为不发生变化。