歌曲管理
接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
PUT | /api/v1/songs/{id}/tags | 写入歌曲标签 |
POST | /api/v1/songs/organize | 批量整理歌曲文件 |
POST | /api/v1/songs/organize/preview | 预览批量整理歌曲文件 |
PUT | /settings/remote-title-source | 更新网络歌曲标题来源配置 |
GET | /settings/remote-title-source | 获取网络歌曲标题来源配置 |
GET | /songs | 获取歌曲列表 |
PUT | /songs/{id} | 更新歌曲信息 |
DELETE | /songs/{id} | 删除歌曲 |
GET | /songs/{id} | 获取单个歌曲详情 |
POST | /songs/{id}/activate | 标记当前活跃歌曲 |
GET | /songs/{id}/audio-tracks | 获取歌曲音频流列表 |
GET | /songs/{id}/cover | 获取歌曲封面图片 |
GET | /songs/{id}/lyric | 获取歌曲歌词 |
PUT | /songs/{id}/lyrics | 更新歌曲歌词 |
GET | /songs/{id}/play | 流式播放歌曲 |
GET | /songs/{id}/play.m3u8 | 流式播放歌曲 |
POST | /songs/{id}/played | 通知歌曲播放事件 |
GET | /songs/{id}/video-hls/{path} | 获取视频 HLS 子资源 |
GET | /songs/{id}/video-hls/playlist.m3u8 | 获取视频 HLS 播放列表 |
POST | /songs/batch-delete | 批量删除歌曲 |
POST | /songs/clean | 清理无效的本地歌曲 |
GET | /songs/duplicates | 获取重复歌曲组 |
GET | /songs/facets | 曲库标签分类聚合 |
GET | /songs/ids | 获取匹配歌曲的 ID 列表 |
POST | /songs/radio | 批量添加电台/广播 |
POST | /songs/refresh-metadata | 刷新远程歌曲元数据 |
POST | /songs/refresh-metadata/cancel | 取消元数据刷新 |
GET | /songs/refresh-metadata/progress | 获取元数据刷新进度 |
POST | /songs/remote | 批量添加网络歌曲 |
PUT /api/v1/songs/{id}/tags
写入歌曲标签
将元数据写入数据库和本地音频文件标签(仅本地歌曲)。cover_data(base64) 优先于 cover_url。非空字段覆盖,空值保留原值。设置 clear_cover=true 可显式清空封面。rename_file=true 时按新标题重命名本地音频文件(保留原目录与扩展名,仅本地非 CUE 歌曲生效);标题清理后为空或目标文件名已存在时返回 400,与原文件同名则不移动仅写库。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲ID |
请求体
标签数据
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
album | string | 否 | |
artist | string | 否 | |
clear_cover | boolean | 否 | |
cover_data | string | 否 | |
cover_url | string | 否 | |
genre | string | 否 | |
language | string | 否 | |
lyrics | string | 否 | |
rename_file | boolean | 否 | RenameFile 为 true 时按新标题重命名本地音频文件(保留原目录与扩展名),仅对本地非 CUE 歌曲生效。 |
style | string | 否 | |
title | string | 否 | |
track | string | 否 | |
year | integer | 否 |
响应
200 - 写入结果
类型: object
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file_write | string | 否 | |
song | Song | 否 |
400 - 请求错误
类型: map[string]string
404 - 歌曲不存在
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
POST /api/v1/songs/organize
批量整理歌曲文件
批量移动/重命名本地歌曲文件到指定目录结构。target_path 为相对于 music_path 的路径(含目录和文件名),扩展名必须与原文件一致。CUE 拆分歌曲会被跳过(status=skip);目标文件已存在时拒绝覆盖(status=error)。music_path 由服务端自取。
需要认证
此接口需要 Bearer Token 认证
请求体
整理项目列表
类型: OrganizeItem[]
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 否 | |
target_path | string | 否 |
数组元素结构:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 否 | |
target_path | string | 否 |
响应
200 - 整理结果
类型: OrganizeResult[]
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | string | 否 | |
file_path | string | 否 | |
id | integer | 否 | |
status | string | 否 |
数组元素结构:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | string | 否 | |
file_path | string | 否 | |
id | integer | 否 | |
status | string | 否 |
400 - 请求错误
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
POST /api/v1/songs/organize/preview
预览批量整理歌曲文件
dry-run 预览目录整理变更,返回每项 old_path→new_path 与状态(ok/conflict/skip/error),不移动任何文件、不改数据库。target_path 为相对 music_path 的路径。CUE 歌曲 skip;目标已存在或批内撞名 conflict。music_path 由服务端自取。
需要认证
此接口需要 Bearer Token 认证
请求体
整理项目列表
类型: OrganizeItem[]
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 否 | |
target_path | string | 否 |
数组元素结构:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 否 | |
target_path | string | 否 |
响应
200 - 预览结果
类型: OrganizePreviewResult[]
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | string | 否 | |
id | integer | 否 | |
new_path | string | 否 | |
old_path | string | 否 | |
status | string | 否 |
数组元素结构:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
error | string | 否 | |
id | integer | 否 | |
new_path | string | 否 | |
old_path | string | 否 | |
status | string | 否 |
400 - 请求错误
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
PUT /settings/remote-title-source
更新网络歌曲标题来源配置
tag:元数据刷新时用音频标签覆盖标题;filename(默认):保持文件名作为标题,不覆盖。
需要认证
此接口需要 Bearer Token 认证
请求体
标题来源配置
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title_source | string | 否 | 可选值: tag, filename 示例: "filename" |
响应
200 - 返回 title_source 字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title_source | string | 否 | 可选值: tag, filename 示例: "filename" |
400 - 请求格式错误或参数无效
类型: map[string]string
500 - 保存配置失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
GET /settings/remote-title-source
获取网络歌曲标题来源配置
tag:元数据刷新时用音频标签覆盖标题;filename(默认):保持文件名作为标题,不覆盖。
需要认证
此接口需要 Bearer Token 认证
响应
200 - 返回 title_source 字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
title_source | string | 否 | 可选值: tag, filename 示例: "filename" |
内容类型
- 响应:
application/json
GET /songs
获取歌曲列表
获取歌曲列表,支持按类型过滤、关键词搜索和分页。默认排除隐藏歌单里的歌,传 exclude_playlist_labels=none 显示全部
需要认证
此接口需要 Bearer Token 认证
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 否 | 歌曲类型 可选值: local, remote, radio |
keyword | string | 否 | 搜索关键词 |
path_prefix | string | 否 | 按 file_path 前缀过滤(如 music/Pop) |
genre | string | 否 | 按流派精确过滤 |
artist | string | 否 | 按歌手精确过滤 |
album | string | 否 | 按专辑精确过滤 |
language | string | 否 | 按语种精确过滤 |
style | string | 否 | 按风格精确过滤 |
year | integer | 否 | 按发行年份精确过滤 |
decade | integer | 否 | 按年代过滤(起始年,如 1990 匹配 1990-1999) |
exclude_playlist_labels | string | 否 | 排除属于这些 label 歌单的歌曲(逗号分隔), 默认 hidden; 传 none 显示全部 默认: "hidden" |
limit | integer | 否 | 每页数量 默认: 20 |
offset | integer | 否 | 偏移量 默认: 0 |
sort | string | 否 | 排序字段,缺省 added_at 可选值: id, title, artist, album, duration, added_at, updated_at, file_modified_at, year, genre |
order | string | 否 | 排序方向,缺省 desc 可选值: asc, desc |
响应
200 - 成功返回歌曲列表
类型: map[string]any
500 - 服务器错误
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
PUT /songs/{id}
更新歌曲信息
更新歌曲信息(仅支持网络歌曲和电台)
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲ID |
请求体
歌曲信息
类型: object
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
album | string | 否 | |
artist | string | 否 | |
cover_url | string | 否 | |
is_live | boolean | 否 | |
is_video | boolean | 否 | |
title | string | 否 | |
url | string | 否 |
响应
200 - 更新成功
类型: Song
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
added_at | string | 否 | 添加时间 示例: "2024-01-01T12:00:00Z" |
album | string | 否 | 专辑名称 示例: "十一月的萧邦" |
artist | string | 否 | 艺术家/歌手 示例: "周杰伦" |
bit_rate | integer | 否 | 比特率(kbps) 示例: 320 |
cover_url | string | 否 | 封面图片URL 示例: "https://example.com/cover.jpg" |
cue_source_path | string | 否 | CUE 来源路径(非空表示 CUE 拆分歌曲) |
cue_track_index | integer | 否 | CUE track 序号 (1-99) |
dedup_key | string | 否 | 去重 key(由插件定义,典型形态 "<platform>:<platform_id>");与 PluginEntryPath 组成 UNIQUE |
duration | number | 否 | 播放时长(秒) 示例: 253.5 |
file_modified_at | string | 否 | 文件修改时间(mtime,本地歌曲扫描时记录;未知为 nil) |
file_path | string | 否 | 本地文件路径 示例: "/music/周杰伦/夜曲.mp3" |
file_size | integer | 否 | 文件大小(字节) 示例: 10485760 |
fingerprint | string | 否 | 音频指纹(Chromaprint) |
fingerprint_duration | number | 否 | 指纹对应音频时长 |
format | string | 否 | 音频格式 示例: "mp3" |
genre | string | 否 | 流派 示例: "Pop" |
id | integer | 否 | 歌曲ID 示例: 1 |
is_live | boolean | 否 | 是否为直播流 示例: false |
is_video | boolean | 否 | 是否含真实视频轨(扫描时 ffprobe 探测,排除封面);客户端据此渲染画面/选择投屏 mime 示例: false |
isrc | string | 否 | ISRC(国际标准录音编码) |
language | string | 否 | 语种 示例: "国语" |
lyric_remote_url | string | 否 | lyric_source=url 时的原始 URL(运行时由 LyricFetcher 拉取) |
lyric_url | string | 否 | 歌词端点 URL(客户端唯一可见字段,指向 /api/v1/songs/{id}/lyric) |
plugin_entry_path | string | 否 | 音源插件 entryPath(网络歌曲) 示例: "my-source" |
sample_rate | integer | 否 | 采样率(Hz) 示例: 44100 |
source_cover_url | string | 否 | 原始封面 URL(仅 JSON 输出,CoverURL 非空时保留原始值供编辑使用) |
source_data | string | 否 | 音源元数据 JSON(给插件 music/url 接口用,opaque) |
source_url | string | 否 | 原始音源 URL(仅 JSON 输出,radio/remote 类型返回原始流地址供编辑使用) |
style | string | 否 | 风格 示例: "抒情" |
title | string | 否 | 标题 示例: "夜曲" |
track | string | 否 | 音轨号,可为 "3" 或 "3/12"(轨号/总数) 示例: "3/12" |
type | string | 否 | 歌曲类型:local/remote/radio 可选值: local, remote, radio 示例: "local" |
updated_at | string | 否 | 最后更新时间 示例: "2024-01-01T12:00:00Z" |
url | string | 否 | 网络地址 示例: "https://example.com/song.mp3" |
year | integer | 否 | 发行年份 示例: 2005 |
400 - 请求数据错误
类型: map[string]string
404 - 歌曲不存在
类型: map[string]string
500 - 更新失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
DELETE /songs/{id}
删除歌曲
根据歌曲ID删除歌曲。设置 delete_files=true 时同步删除本地音频文件
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲ID |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
delete_files | boolean | 否 | 是否同时删除本地音频文件 |
响应
200 - 删除成功
类型: map[string]string
400 - 无效的歌曲ID
类型: map[string]string
500 - 删除失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
GET /songs/{id}
获取单个歌曲详情
根据歌曲ID获取详细信息
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲ID |
响应
200 - 成功返回歌曲详情
类型: Song
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
added_at | string | 否 | 添加时间 示例: "2024-01-01T12:00:00Z" |
album | string | 否 | 专辑名称 示例: "十一月的萧邦" |
artist | string | 否 | 艺术家/歌手 示例: "周杰伦" |
bit_rate | integer | 否 | 比特率(kbps) 示例: 320 |
cover_url | string | 否 | 封面图片URL 示例: "https://example.com/cover.jpg" |
cue_source_path | string | 否 | CUE 来源路径(非空表示 CUE 拆分歌曲) |
cue_track_index | integer | 否 | CUE track 序号 (1-99) |
dedup_key | string | 否 | 去重 key(由插件定义,典型形态 "<platform>:<platform_id>");与 PluginEntryPath 组成 UNIQUE |
duration | number | 否 | 播放时长(秒) 示例: 253.5 |
file_modified_at | string | 否 | 文件修改时间(mtime,本地歌曲扫描时记录;未知为 nil) |
file_path | string | 否 | 本地文件路径 示例: "/music/周杰伦/夜曲.mp3" |
file_size | integer | 否 | 文件大小(字节) 示例: 10485760 |
fingerprint | string | 否 | 音频指纹(Chromaprint) |
fingerprint_duration | number | 否 | 指纹对应音频时长 |
format | string | 否 | 音频格式 示例: "mp3" |
genre | string | 否 | 流派 示例: "Pop" |
id | integer | 否 | 歌曲ID 示例: 1 |
is_live | boolean | 否 | 是否为直播流 示例: false |
is_video | boolean | 否 | 是否含真实视频轨(扫描时 ffprobe 探测,排除封面);客户端据此渲染画面/选择投屏 mime 示例: false |
isrc | string | 否 | ISRC(国际标准录音编码) |
language | string | 否 | 语种 示例: "国语" |
lyric_remote_url | string | 否 | lyric_source=url 时的原始 URL(运行时由 LyricFetcher 拉取) |
lyric_url | string | 否 | 歌词端点 URL(客户端唯一可见字段,指向 /api/v1/songs/{id}/lyric) |
plugin_entry_path | string | 否 | 音源插件 entryPath(网络歌曲) 示例: "my-source" |
sample_rate | integer | 否 | 采样率(Hz) 示例: 44100 |
source_cover_url | string | 否 | 原始封面 URL(仅 JSON 输出,CoverURL 非空时保留原始值供编辑使用) |
source_data | string | 否 | 音源元数据 JSON(给插件 music/url 接口用,opaque) |
source_url | string | 否 | 原始音源 URL(仅 JSON 输出,radio/remote 类型返回原始流地址供编辑使用) |
style | string | 否 | 风格 示例: "抒情" |
title | string | 否 | 标题 示例: "夜曲" |
track | string | 否 | 音轨号,可为 "3" 或 "3/12"(轨号/总数) 示例: "3/12" |
type | string | 否 | 歌曲类型:local/remote/radio 可选值: local, remote, radio 示例: "local" |
updated_at | string | 否 | 最后更新时间 示例: "2024-01-01T12:00:00Z" |
url | string | 否 | 网络地址 示例: "https://example.com/song.mp3" |
year | integer | 否 | 发行年份 示例: 2005 |
400 - 无效的歌曲ID
类型: map[string]string
404 - 歌曲不存在
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
POST /songs/{id}/activate
标记当前活跃歌曲
客户端切歌前调用,让后端 cancel 同一会话下其他歌曲的进行中工作(prefetch/transcode/reassign)。其他客户端会话不受影响。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
响应
204 - 无内容
400 - 无效的 song_id
类型: map[string]string
内容类型
- 响应:
application/json
GET /songs/{id}/audio-tracks
获取歌曲音频流列表
用 ffprobe 探测该歌曲文件的音频流,返回每条流的 audio-relative index(对应 ffmpeg -map 0🅰️N)、title、language、codec、default。主要用于 Web 端双音轨(原唱/伴奏 mka)切换:前端据 tracks 数量决定是否显示切轨入口,并用 index 调 /songs/{id}/play?track=N 抽轨播放。仅本地歌曲(或已落地缓存的网络歌曲)有文件可探测;无可探测文件或音频流 < 2 条时也正常返回(前端据此不显示切轨)。运行时按需探测,不落库。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
响应
200 - 音频流列表
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
tracks | AudioTrackInfo[] | 否 |
400 - 无效的歌曲 ID
类型: map[string]string
404 - 歌曲不存在
类型: map[string]string
内容类型
- 响应:
application/json
GET /songs/{id}/cover
获取歌曲封面图片
根据歌曲 ID 获取封面图片。优先使用本地封面文件(CoverPath),其次代理 CoverURL。CoverURL 支持以 "/" 开头的相对路径,服务端自动经 InternalURLResolver 解析为内部 URL(含 access_token),用于插件歌曲封面代理。可选 query 参数 w:把本地封面等比缩放到该宽度(物理像素,绝不放大、上限 1024)后以 JPEG 返回,用于 Web 端降低 GPU 纹理体积(songloft-org/songloft#309);缺省或非法时返回原图。缩略仅作用于本地封面,远程代理封面忽略 w。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
w | integer | 否 | 本地封面缩略目标宽度(物理像素,绝不放大,上限 1024) |
响应
200 - 封面图片
类型: file
400 - 无效的歌曲 ID
类型: map[string]string
404 - 歌曲或封面不存在
类型: map[string]string
500 - 服务器错误
类型: map[string]string
内容类型
- 响应:
image/jpeg
GET /songs/{id}/lyric
获取歌曲歌词
根据 song.ID 返回 LyricPayload JSON,含 lyric/tlyric/rlyric/lxlyric。传 refresh=1 时强制重新抓取:跳过库中自动获取的旧歌词(空/scraped/cached)重跑歌词搜索插件,响应挂 no-store 不缓存;file/embedded/manual 等权威歌词不被覆盖。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
refresh | boolean | 否 | 为 true 时绕过缓存强制重新抓取歌词(重跑歌词搜索插件,不覆盖 file/embedded/manual 歌词) |
响应
200 - LyricPayload
类型: map[string]any
404 - 歌曲或歌词不存在
类型: string
502 - 歌词获取失败
类型: string
内容类型
- 响应:
application/json
PUT /songs/{id}/lyrics
更新歌曲歌词
更新指定歌曲的歌词内容和来源。url 来源传 lyric_remote_url,其它来源传 lyric/tlyric/rlyric/lxlyric 四字段。响应里的 file_write_status 表示是否把元数据回写到本地音频文件:written=已写入,unchanged=未变更(非本地歌曲/无文件路径/不支持的扩展名/url 来源),skipped=标签已一致无需写入,failed=尝试写入但失败(DB 已成功)。lyric_source=manual 用于标记用户手动调整,scanner 重扫时不会覆盖
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
请求体
歌词信息
类型: object
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
lxlyric | string | 否 | |
lyric | string | 否 | |
lyric_remote_url | string | 否 | |
lyric_source | string | 否 | |
rlyric | string | 否 | |
tlyric | string | 否 |
响应
200 - 更新成功
类型: object
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file_write_status | string | 否 | |
message | string | 否 |
400 - 请求数据错误
类型: map[string]string
404 - 歌曲不存在
类型: map[string]string
500 - 更新失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
GET /songs/{id}/play
流式播放歌曲
按 song.ID 流式返回音频。内部根据 song.type 分发到本地文件 / 缓存下载 / 直链下载 / 电台 302。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
format | string | 否 | 目标转码格式(如 mp3、ogg),用于平台兼容性转码 |
quality | string | 否 | 目标音质码率(128/192/320),不传或不合法值表示原始音质。指定后默认转码为 mp3(除非同时指定了 format) |
track | integer | 否 | 抽取指定音频流播放(audio-relative 0-based,对应 ffmpeg -map 0🅰️N)。用于 Web 端双音轨(原唱/伴奏 mka)切轨:后端抽出单条音轨,AAC 编码时无损 remux 成 m4a、否则转 mp3。缺省/负数=不抽轨;与 media=video 互斥 |
prefetch | string | 否 | 传 1 时异步预热缓存/转码,立即返回 202 |
media | string | 否 | 传 video 时按视频播放:直出原容器(忽略 format/quality 转码,避免 -vn 丢画面),并按容器真实类型返回 Content-Type(如 video/mp4)。用于应用内视频画面渲染与 DLNA 视频投屏 |
hls | string | 否 | 仅电台(HLS)有效。传 direct 时强制 302 直连源站、绕过本机 HLS 反代(即使 /settings/hls-proxy 已开)。原生 player 无 CORS 限制,直连可避免直播切片经反代往返后过期(404);浏览器不传此参数以继续走反代解决 CORS |
radio_transcode | string | 否 | 仅电台有效。传目标格式(如 mp3)时,服务端用 ffmpeg 把电台流实时转码为该格式(HLS 与裸流均适用)。用于只支持 MP3、无法解码 AAC/HE-AAC 或不支持 HLS 的音箱。缺 ffmpeg 或坏源时优雅降级为原样代理/302。与 format 分离:电台侧忽略 format,只认此参数 |
响应
200 - 音频文件
类型: file
202 - 预拉取已触发
类型: string
302 - 电台流重定向
类型: string
404 - 歌曲不存在
类型: string
502 - 音源不可用
类型: string
内容类型
- 响应:
application/octet-stream
GET /songs/{id}/play.m3u8
流式播放歌曲
按 song.ID 流式返回音频。内部根据 song.type 分发到本地文件 / 缓存下载 / 直链下载 / 电台 302。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
format | string | 否 | 目标转码格式(如 mp3、ogg),用于平台兼容性转码 |
quality | string | 否 | 目标音质码率(128/192/320),不传或不合法值表示原始音质。指定后默认转码为 mp3(除非同时指定了 format) |
track | integer | 否 | 抽取指定音频流播放(audio-relative 0-based,对应 ffmpeg -map 0🅰️N)。用于 Web 端双音轨(原唱/伴奏 mka)切轨:后端抽出单条音轨,AAC 编码时无损 remux 成 m4a、否则转 mp3。缺省/负数=不抽轨;与 media=video 互斥 |
prefetch | string | 否 | 传 1 时异步预热缓存/转码,立即返回 202 |
media | string | 否 | 传 video 时按视频播放:直出原容器(忽略 format/quality 转码,避免 -vn 丢画面),并按容器真实类型返回 Content-Type(如 video/mp4)。用于应用内视频画面渲染与 DLNA 视频投屏 |
hls | string | 否 | 仅电台(HLS)有效。传 direct 时强制 302 直连源站、绕过本机 HLS 反代(即使 /settings/hls-proxy 已开)。原生 player 无 CORS 限制,直连可避免直播切片经反代往返后过期(404);浏览器不传此参数以继续走反代解决 CORS |
radio_transcode | string | 否 | 仅电台有效。传目标格式(如 mp3)时,服务端用 ffmpeg 把电台流实时转码为该格式(HLS 与裸流均适用)。用于只支持 MP3、无法解码 AAC/HE-AAC 或不支持 HLS 的音箱。缺 ffmpeg 或坏源时优雅降级为原样代理/302。与 format 分离:电台侧忽略 format,只认此参数 |
响应
200 - 音频文件
类型: file
202 - 预拉取已触发
类型: string
302 - 电台流重定向
类型: string
404 - 歌曲不存在
类型: string
502 - 音源不可用
类型: string
内容类型
- 响应:
application/octet-stream
POST /songs/{id}/played
通知歌曲播放事件
客户端在歌曲开始播放、播放完成或被跳过时调用此端点,后端将事件广播给已订阅播放事件的 JS 插件(通过 songloft.events.onPlayEvent 注册)。source 参数标识调用来源,如 songloft-player(官方客户端)、miot(小爱音箱插件)等。type 参数标识事件类型:play(开始播放)、finish(播放完成)、skip(用户跳过)。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
source | string | 否 | 调用来源标识,如 songloft-player、miot |
type | string | 否 | 事件类型:play、finish、skip,默认 finish 可选值: play, finish, skip |
响应
204 - 无内容
400 - 无效的歌曲 ID 或事件类型
类型: ErrorResponse
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
detail | string | 否 | 详细错误信息(可选) 示例: "详细错误信息" |
error | string | 否 | 错误信息 示例: "操作失败" |
404 - 歌曲不存在
类型: ErrorResponse
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
detail | string | 否 | 详细错误信息(可选) 示例: "详细错误信息" |
error | string | 否 | 错误信息 示例: "操作失败" |
内容类型
- 响应:
application/json
GET /songs/{id}/video-hls/{path}
获取视频 HLS 子资源
返回视频 HLS 转码生成的子播放列表或 .ts 切片文件。由 hls.js 根据 master playlist 自动请求。支持多音轨场景下的子目录结构(stream_0/playlist.m3u8, stream_0/0001.ts 等)。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
path | string | 是 | 子资源路径(如 stream_0/playlist.m3u8 或 stream_0/0001.ts) |
响应
200 - HLS 子资源
类型: file
400 - 无效请求
类型: map[string]string
404 - 资源不存在
类型: map[string]string
GET /songs/{id}/video-hls/playlist.m3u8
获取视频 HLS 播放列表
对浏览器不原生支持的视频格式(mpg/flv/wmv/rmvb/avi/mkv 等)实时转码为 HLS(H.264+AAC),返回 master.m3u8 播放列表。多音轨文件会生成 HLS 多音频 rendition(hls.js 原生支持切换)。首次请求会启动转码(转完再播);后续请求命中缓存。需要 ffmpeg。
需要认证
此接口需要 Bearer Token 认证
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 歌曲 ID |
响应
200 - HLS 播放列表内容
类型: string
400 - 无效的歌曲 ID
类型: map[string]string
404 - 歌曲不存在
类型: map[string]string
503 - ffmpeg 不可用或转码失败
类型: map[string]string
内容类型
- 响应:
application/vnd.apple.mpegurl
POST /songs/batch-delete
批量删除歌曲
根据歌曲 ID 列表批量删除歌曲。设置 delete_files=true 时同步删除本地音频文件(用于去重等场景)
需要认证
此接口需要 Bearer Token 认证
请求体
批量删除请求
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
delete_files | boolean | 否 | 是否同步删除本地音频文件 示例: false |
ids | integer[] | 否 | 要删除的歌曲 ID 列表 示例: [1] |
响应
200 - 删除成功
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
deleted | integer | 否 | 实际删除的数量 示例: 3 |
400 - 请求数据错误
类型: map[string]string
500 - 删除失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
POST /songs/clean
清理无效的本地歌曲
清理本地歌曲中文件已不存在或位于排除目录中的记录,同时删除关联的封面文件
需要认证
此接口需要 Bearer Token 认证
响应
200 - 清理成功
类型: map[string]any
500 - 清理失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
GET /songs/duplicates
获取重复歌曲组
通过音频指纹查询本地歌曲中内容相同的重复组
需要认证
此接口需要 Bearer Token 认证
响应
200 - 重复歌曲组列表
类型: map[string]any
内容类型
- 响应:
application/json
GET /songs/facets
曲库标签分类聚合
按指定维度聚合曲库,返回该维度下非空取值、各自的歌曲数量及一首代表歌曲的封面 URL,用于「分类浏览」的卡片网格。 支持维度:genre(流派)/artist(歌手)/album(专辑)/language(语种)/style(风格)/year(年份)/decade(年代)。 year/decade 的 value 为数字字符串(年代如 "1990" 表示 1990-1999)。取到某取值后可用 /songs?<field>=<value> 拉取该分类下歌曲。 支持 keyword 模糊搜索取值、limit/offset 分页、sort(count|name)/order 排序;返回 total 为该维度去重取值总数。
需要认证
此接口需要 Bearer Token 认证
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
field | string | 是 | 聚合维度 可选值: genre, artist, album, language, style, year, decade |
keyword | string | 否 | 对取值模糊搜索 |
limit | integer | 否 | 分页大小,缺省 20,上限 100000 |
offset | integer | 否 | 分页偏移,缺省 0 |
sort | string | 否 | 排序维度,缺省 count 可选值: count, name |
order | string | 否 | 排序方向;count 缺省 desc,name 缺省 asc 可选值: asc, desc |
响应
200 - 成功返回聚合结果 {field, facets:[{value,count,cover_url}], total, limit, offset}
类型: map[string]any
400 - 缺少或不支持的 field
类型: map[string]string
500 - 服务器错误
类型: map[string]string
内容类型
- 响应:
application/json
GET /songs/ids
获取匹配歌曲的 ID 列表
与 /songs 共享过滤条件,仅返回 ID。用于「全选当前筛选范围」场景。
需要认证
此接口需要 Bearer Token 认证
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 否 | 歌曲类型 |
keyword | string | 否 | 搜索关键词 |
path_prefix | string | 否 | 按 file_path 前缀过滤 |
genre | string | 否 | 按流派精确过滤 |
artist | string | 否 | 按歌手精确过滤 |
album | string | 否 | 按专辑精确过滤 |
language | string | 否 | 按语种精确过滤 |
style | string | 否 | 按风格精确过滤 |
year | integer | 否 | 按发行年份精确过滤 |
decade | integer | 否 | 按年代过滤(起始年,如 1990 匹配 1990-1999) |
exclude_playlist_labels | string | 否 | 排除属于这些 label 歌单的歌曲(逗号分隔), 默认 hidden; 传 none 显示全部 默认: "hidden" |
sort | string | 否 | 排序字段,缺省 added_at 可选值: id, title, artist, album, duration, added_at, updated_at, file_modified_at, year, genre |
order | string | 否 | 排序方向,缺省 desc 可选值: asc, desc |
响应
200 - 成功返回 ID 列表
类型: map[string]any
500 - 服务器错误
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
POST /songs/radio
批量添加电台/广播
批量添加电台/广播到数据库
需要认证
此接口需要 Bearer Token 认证
请求体
电台/广播列表
类型: object[]
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
cover_url | string | 否 | |
is_video | boolean | 否 | |
title | string | 否 | |
url | string | 否 |
响应
201 - 添加成功
类型: object
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
count | integer | 否 | |
songs | Song[] | 否 |
400 - 请求数据错误
类型: map[string]string
500 - 添加失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
POST /songs/refresh-metadata
刷新远程歌曲元数据
对所有元数据缺失的远程歌曲,通过 ffprobe 探测时长、比特率、采样率、格式及标签并回填。已在运行时返回 409。
需要认证
此接口需要 Bearer Token 认证
响应
202 - 已启动
类型: map[string]string
409 - 已在运行
类型: map[string]string
500 - 启动失败
类型: map[string]string
内容类型
- 响应:
application/json
POST /songs/refresh-metadata/cancel
取消元数据刷新
取消正在执行的远程歌曲元数据刷新任务
需要认证
此接口需要 Bearer Token 认证
响应
204 - 已取消
内容类型
- 响应:
application/json
GET /songs/refresh-metadata/progress
获取元数据刷新进度
轮询远程歌曲元数据刷新的执行状态和进度
需要认证
此接口需要 Bearer Token 认证
响应
200 - 进度信息
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
failed | integer | 否 | |
processed | integer | 否 | |
status | string | 否 | |
total | integer | 否 |
内容类型
- 响应:
application/json
POST /songs/remote
批量添加网络歌曲
批量添加网络歌曲到数据库。cover_url 支持以 "/" 开头的相对路径(插件场景下由服务端自动解析为内部 URL,与歌词 lyric_remote_url 的解析机制一致)。lyric_remote_url 为歌词远程 URL 直传字段,提供时优先于 lyric + lyric_source=url 的间接方式。副作用:插入成功后,对缺失技术元数据(duration/bitrate/samplerate/format)的歌曲异步探测补齐(限并发后台执行,不阻塞响应),确保 WebDAV 等无法自带时长的音源在首次播放前就落库 duration,供音箱等仅依赖服务端时长的消费端自动切歌。
需要认证
此接口需要 Bearer Token 认证
请求体
网络歌曲列表
类型: object[]
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
album | string | 否 | |
artist | string | 否 | |
cover_url | string | 否 | |
dedup_key | string | 否 | |
duration | number | 否 | |
is_video | boolean | 否 | |
lyric | string | 否 | |
lyric_remote_url | string | 否 | |
lyric_source | string | 否 | |
plugin_entry_path | string | 否 | |
source_data | string | 否 | |
title | string | 否 | |
url | string | 否 |
响应
201 - 添加成功
类型: object
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
count | integer | 否 | |
songs | Song[] | 否 |
400 - 请求数据错误
类型: map[string]string
500 - 添加失败
类型: map[string]string
内容类型
- 请求:
application/json - 响应:
application/json
