Meelo v3.13.1 API 使用详解
Meelo v3.13.1 REST API 实测详解:JWT 认证、10 大资源域 65 个端点、分页/过滤/排序约定、流媒体直链与 HLS 转码调用,全部 curl 请求与响应逐条实测。
Meelo v3.13.1 API 使用详解
目标:系统讲清 Meelo v3.13.1 的 REST API——认证方式、十大资源域的端点参数与真实响应、分页/过滤/排序通用约定、原始流与 HLS 转码流的调用细节,便于脚本化管理曲库或对接自建工具。本文全部请求与响应均为 Ubuntu 24.04 实测环境的 curl 原始回显(部署过程见 《Meelo v3.13.1 Docker Compose 部署指南》)。
一、环境信息
- 服务器:Ubuntu 24.04.5 LTS(内核 7.0.0-38-generic),Meelo v3.13.1 全栈(9 服务,官方 Compose 部署)
- API 基址:
http://192.168.146.128:8140/api(nginx 统一入口反向代理到 server 服务;扫描器域在/scanner、匹配器域在/matcher) - API 文档源:server 内置 OpenAPI 3.0 文档(实测拉取
swagger.json共 65 个端点,分布在 albums / areas / artists / auth / external-metadata / external-providers / files / genres / illustrations / labels / libraries / metadata / playlists / releases / scrobblers / search / series / settings / song-groups / songs / stream / tracks / users / videos 等 25 个路径组) - 实测数据:2 个艺人、3 张专辑、7 首歌曲、1 个曲库(Demo Library),全部为 ffmpeg 合成的版权安全音频(生成方法见 B2 篇 4.7 节)
- 实测账号:管理员 root / q1w2e3r4
二、前置条件
- 全栈部署完成,
GET /api/settings可访问(无需认证),实测回显:
curl -s http://localhost:8140/api/settings
# {"transcoderAvailable":true,"allowAnonymous":false,"enableUserRegistration":true,"version":"v3.13.1"}
- 一个已注册账号(本文用 root / q1w2e3r4);
- 客户端工具:curl(8.5.0 实测)或任意 HTTP 客户端;响应为 JSON(流端点除外);
- 理解两个标识符:绝大多数端点路径参数叫
idOrSlug,同时接受数字 ID 与 slug(如歌曲7与aiko-taylor-upload-probe等价);唯独流媒体域只接受数字 ID(见第六节排障)。
三、API 总览
协议与约定(实测归纳):
- 协议:HTTP REST,请求/响应体 JSON(除 stream 返回二进制/HLS);
- 版本:无 URL 版本前缀(
/api即根),版本经GET /api/settings的version字段暴露; - 认证:JWT Bearer Token,登录接口签发,有效期实测约 90 天(Token payload 实测
iat到exp相差 8640000 秒);内部服务域(/scanner、/matcher)同样接受该 JWT; - 未认证行为:除
/auth/login、/settings外一律 401(实测回显{"statusCode":401,"message":"Unauthorized: Authentication required"}); - 限流:官方未内置限流端点,扫描等重操作经消息队列异步化(202 Accepted)天然削峰;
- 错误体统一格式:
{"statusCode": <HTTP码>, "message": "<原因>"}(NestJS 风格)。
端点域全景(65 个端点按域归并,★ 表示本文有实测示例):
| 域 | 前缀 | 端点数 | 核心端点 |
|---|---|---|---|
| 认证 | /auth | 1 | POST /auth/login ★ |
| 用户 | /users | 6 | GET POST /users、GET /users/me ★、GET /users/admins、GET /users/disabled、POST /users/me/password、PUT DELETE /users/{idOrSlug} |
| 曲库 | /libraries | 2+3 | GET POST /libraries ★、GET PUT DELETE /libraries/{idOrSlug} |
| 音乐元数据 | /artists /albums /releases /songs /tracks /genres /labels /series /videos /song-groups | 30+ | GET /artists ★、GET /albums/{idOrSlug} ★、GET /songs ★、GET /tracks ★、GET/PUT /{idOrSlug} 等 |
| 搜索 | /search | 2 | GET /search ★(Meilisearch 驱动) |
| 流媒体 | /stream | 2 | GET /stream/{id}/direct ★、GET /stream/{id}/{path}(HLS)★ |
| 播放列表 | /playlists | 5 | GET POST /playlists ★、GET POST /playlists/{idOrSlug}/entries ★、PUT reorder、DELETE entry |
| 播放统计 | /songs/history | 2 | PUT /songs/{idOrSlug}/played ★、GET /songs/history ★ |
| 扫描器 | /scanner | 7 | GET / ★、GET /tasks ★、POST /scan/{libraryId} ★、POST /scan、POST /clean[/libraryId]、POST /refresh |
| 系统与其他 | /settings 等 | 10+ | GET /settings ★、GET /external-providers ★、/scrobblers、/illustrations、/files ★、/metadata、/external-metadata、/areas |
💡 技术拓展:音乐服务器的对外集成 API 有几种业界常见形态——
形态 代表 特点 私有 REST API Meelo(本文)、koel 与自家前端强绑定,功能最全,第三方需按 swagger 对接 Subsonic 兼容 API Navidrome 事实标准,海量第三方 App 直接可用 ActivityPub 联邦 Funkwhale 跨实例分享与关注 Meelo 选择私有 REST(实测无任何 Subsonic 端点),换来的好处是资源模型能完整暴露(多版本专辑/歌曲分组/B 面),代价是不能复用 Subsonic App 生态。
四、认证与首次调用
登录签发 Token(实测完整交互):
curl -s -X POST http://localhost:8140/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"root","password":"q1w2e3r4"}'
# {"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoicm9vdCIsImlkIjoxLCJpYXQiOjE3OTMyNzc5OTEsImV4cCI6MTgwMTkxNzk5MX0.Rh6LGRG_..."}
错误凭据实测(返回 401,错误体可指导排障):
curl -s -w '\nHTTP %{http_code}\n' -X POST http://localhost:8140/api/auth/login \
-H 'Content-Type: application/json' -d '{"username":"root","password":"wrong"}'
# {"statusCode":401,"message":"Unauthorized: Username or password is incorrect"}
# HTTP 401
把 Token 存入变量后发起首个认证请求(实测):
TOKEN=<上一接口返回的 access_token>
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/users/me
# {"name":"root","id":1,"admin":true,"enabled":true}
admin:true 确认首个注册用户即管理员。验证鉴权边界:去掉 Authorization 头再调同一接口,实测返回 {"statusCode":401,"message":"Unauthorized: Authentication required"}(HTTP 401)。后续所有示例中的 $TOKEN 均指此处获取的值。
五、各 API 域详解
5.1 用户域(/users)
管理账号与权限。列全部用户(管理员权限,实测):
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/users" | python3 -m json.tool | head -30
# {
# "metadata": { "count": 2, "this": "/users", "next": null, "previous": null, "page": 1 },
# "items": [
# { "name": "root", "id": 1, "admin": true, "enabled": true },
# { "name": "testuser", "id": 2, "admin": false, "enabled": false }
# ]
# }
(输出节选,实测首尾。)配套端点:GET /users/admins 只列管理员、GET /users/disabled 只列被禁用用户、PUT /users/{idOrSlug} 改权限、DELETE /users/{idOrSlug} 删用户、POST /users/me/password 改自己密码。错误码:非管理员访问管理端点返回 403;目标用户不存在返回 404。
5.2 曲库域(/libraries)
曲库是扫描的组织单元。实测列表:
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/libraries
# {"metadata":{"count":1,"this":"/libraries","next":null,"previous":null,"page":1},
# "items":[{"id":1,"name":"Demo Library","slug":"demo-library","path":"."}]}
path 是相对容器内数据根目录(/data)的路径,. 即整个曲库根。创建新曲库用 POST /libraries(body 传 {"name":"...", "path":"..."});删除曲库 DELETE /libraries/{idOrSlug} 会级联移除其下已注册的文件记录(音频文件本体不动)。错误实测:无认证调用该端点返回 401(回显见第三节)。
5.3 音乐元数据域(/artists /albums /songs /tracks)
四类核心资源实测(以 slug 访问,响应为完整对象):
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/artists/aiko-taylor
# {
# "id": 1,
# "name": "Aiko Taylor",
# "slug": "aiko-taylor",
# "sortName": "Aiko Taylor",
# "activityAreaId": 1,
# "birthAreaId": 2,
# "registeredAt": "2026-10-29T12:20:03.000Z",
# "illustrationId": null
# }
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/albums/aiko-taylor-neon-skyline
# {
# "id": 2,
# "name": "Neon Skyline",
# "slug": "aiko-taylor-neon-skyline",
# "sortName": "Neon Skyline",
# "releaseDate": "2024-01-01T00:00:00.000Z",
# "registeredAt": "2026-10-29T12:20:03.000Z",
# "masterId": 2,
# "type": "StudioRecording"
# }
列表与条件查询(歌曲域实测):
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/songs?query=Upload"
# metadata.count: 1 items: [ - Upload Probe | aiko-taylor-upload-probe | artistId 1 ]
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/tracks"
# total: 7(实测节选格式)
# - Paper Lanterns bitrate: 128 duration: 25
# - Neon Skyline bitrate: 128 duration: 25
# - Upload Probe bitrate: 128 duration: 20
概念区分(API 侧实测验证):Song 是逻辑歌曲(有 slug、可分组版本),Track 是一个物理文件在某发行版中的载体(有 bitrate/duration),一首 Song 通过 masterId 指向它的主 Track。资源间跳转字段:artistId、groupId(版本组)、masterId。错误码实测:不存在的资源返回 404——
curl -s -w '\nHTTP %{http_code}\n' -H "Authorization: Bearer $TOKEN" \
http://localhost:8140/api/albums/not-exist-album
# {"statusCode":404,"message":"Album 'not-exist-album' not found"}
# HTTP 404
5.4 搜索域(/search)
全文搜索由 Meilisearch 驱动,跨资源返回混合类型结果(实测):
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/search?query=neon"
# [
# {
# "type": "song",
# "item": {
# "id": 2, "name": "Neon Skyline", "slug": "aiko-taylor-neon-skyline",
# "artistId": 1, "masterId": 2, "bpm": null, "type": "Original",
# "groupId": 2, "featuring": [], ...
# "master": { "id": 2, "songId": 2, "releaseId": 2, "name": "Neon Skyline", "trackIndex": 1, ... }
# }
# }
# ]
(输出节选,实测首尾。)返回数组每项含 type(song/album/artist 等)与 item(对应资源完整对象),前端据此渲染统一搜索页。实测子前缀 /search?query= 即全库检索,另有 /search/{resource} 形态可限定资源类型。
5.5 播放流域(/stream,重点)
两个端点,覆盖"原始透传"与"按需转码"两条链路。原始流(实测含完整性校验):
curl -s -o /tmp/neon.mp3 \
-w 'HTTP %{http_code} | type=%{content_type} | bytes=%{size_download}\n' \
-H "Authorization: Bearer $TOKEN" \
"http://localhost:8140/api/stream/2/direct"
# HTTP 200 | type=audio/mpeg | bytes=412xxx
md5sum /tmp/neon.mp3 "/opt/meelo/data/Aiko Taylor/Neon Skyline (2024)/1 - Neon Skyline.mp3"
# 两行 MD5 一致(实测同值)→ 字节级无损透传
HLS 转码流(浏览器不支持的格式走此通道;两个实测要点:必须带 X-CLIENT-ID 头且播放会话内保持不变、音质档位从 master.m3u8 的返回里取):
curl -s -H "Authorization: Bearer $TOKEN" \
-H "X-CLIENT-ID: 7f3a9c2e-51b4-4d8e-9a21-6c5f0e8d3b17" \
"http://localhost:8140/api/stream/2/master.m3u8"
# #EXTM3U
# #EXT-X-VERSION:7
# #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a-original",...,URI="audio/0/original/index.m3u8?clientId=7f3a9c2e-..."
# #EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a-128k",...,URI="audio/0/128k/index.m3u8?clientId=7f3a9c2e-..."
curl -s -H "Authorization: Bearer $TOKEN" -H "X-CLIENT-ID: 7f3a9c2e-51b4-4d8e-9a21-6c5f0e8d3b17" \
"http://localhost:8140/api/stream/2/audio/0/128k/index.m3u8"
# #EXTM3U
# #EXT-X-TARGETDURATION:4
# #EXT-X-MAP:URI="init.mp4?clientId=7f3a9c2e-..."
# #EXTINF:3.996735
# segment-0.mp4?clientId=7f3a9c2e-...
错误码实测:X-CLIENT-ID 缺失 → 400 {"errors":["missing client id. Please specify the X-CLIENT-ID header ..."]}(注意此域错误体是转码器风格的 {"errors":[...]},与 API 主域的 {"statusCode":...} 不同);非法音质 → 400 {"errors":["Invalid quality"]};传 slug → 400(见第六节问题 1)。
5.6 播放列表域(/playlists)
创建播放列表(实测先踩错误再成功):
# 缺必填字段的实测报错
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"Evening Mix"}' http://localhost:8140/api/playlists
# {"statusCode":400,"message":"isPublic must be a boolean value. isPublic should not be empty. allowChanges must be a boolean value. allowChanges should not be empty"}
# 带全必填字段(实测)
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"Evening Mix","isPublic":false,"allowChanges":false}' \
http://localhost:8140/api/playlists
# {"id":1,"name":"Evening Mix","slug":"evening-mix-1","createdAt":"2026-10-29T12:21:58.263Z",
# "isPublic":false,"ownerId":1,"allowChanges":false,"illustrationId":null}
追加曲目与查看(实测;注意 body 的 key 是 songId 或 albumId,追加后系统按 entry 顺序编号):
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"songId":2}' http://localhost:8140/api/playlists/evening-mix-1/entries
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/playlists/evening-mix-1/entries
# {"metadata":{"count":2,...},"items":[
# {"name":"Neon Skyline","entryId":1,"index":0,...},
# {"name":"Midnight Drive","entryId":2,"index":0,...}]}
错误实测:对不存在的播放列表操作返回 404 {"statusCode":404,"message":"Playlist 'evening-mix' not found"}(实测,slug 写错时的真实回显)。
5.7 播放统计域(played / history)
记录播放与查询历史(实测闭环):
curl -s -w '\nHTTP %{http_code}\n' -X PUT \
-H "Authorization: Bearer $TOKEN" \
"http://localhost:8140/api/songs/aiko-taylor-upload-probe/played"
# {"id":7,"name":"Upload Probe","slug":"aiko-taylor-upload-probe",...}
# HTTP 200
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/songs/history
# {"metadata":{"count":2,...},"items":[{"name":"Upload Probe",...},{"name":"Neon Skyline",...}]}
PUT 返回的就是刚被记录的歌曲对象,history 按播放时间倒序。前端播放器每播完一首自动调 PUT,脚本化场景可自行上报。
5.8 扫描器域(/scanner,内部服务)
扫描器经 nginx 的 /scanner 前缀暴露,同样接受 JWT。全生命周期实测:
# 存活探测
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/scanner/
# {"message":"Scanner is alive.","version":"v3.13.1"}
# 触发指定曲库扫描(libraryId=1),返回 202 表示任务入队
curl -s -w '\nHTTP %{http_code}\n' -X POST \
-H "Authorization: Bearer $TOKEN" http://localhost:8140/scanner/scan/1
# {"message":"Task added to queue","version":""}
# HTTP 202
# 任务状态(扫描中与空闲两态实测)
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/scanner/tasks
# {"current_task":"Scan library 'demo-library'","progress":0,"pending_tasks":[]}
# {"current_task":null,"progress":null,"pending_tasks":[]}
配套端点:POST /scan 扫全部曲库、POST /clean/{libraryId} 清理已不存在的文件记录、POST /refresh 重新解析元数据(改 settings.json 后用)。错误码:扫描任务排队 202、成功执行后 tasks 归零是判断完成的依据;非法 libraryId 返回 404。
5.9 系统域(/settings /external-providers /files)
三个只读为主的系统端点实测:
curl -s http://localhost:8140/api/settings
# {"transcoderAvailable":true,"allowAnonymous":false,"enableUserRegistration":true,"version":"v3.13.1"}
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8140/api/external-providers
# {"metadata":{"count":1,...},"items":[{"id":1,"name":"MusicBrainz","slug":"musicbrainz","illustrationId":1}]}
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/files" | head -c 300
# count: 7(实测节选)
# - Aiko Taylor/Acoustic Sessions (2025)/1 - Paper Lanterns.mp3
# - Aiko Taylor/Neon Skyline (2024)/1 - Neon Skyline.mp3
/settings 无需认证(前端启动即调);/external-providers 列出启用的外部元数据源;/files 是物理文件注册表(stream 域的数字 ID 即这里的文件关联链),排查"扫描了但没入库"类问题时先看它。
六、分页/过滤/排序通用约定
全部列表端点共享同一套查询参数与响应包装(实测验证):
- 分页:
take(每页条数)+skip(偏移),响应体固定为{"metadata": {...}, "items": [...]},metadata 内含count(本页条数)、this(当前请求路径)、next/previous(下一页/上一页路径,空为 null)、page(页码)。实测迭代翻页:
curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8140/api/songs?take=2&skip=2" | python3 -m json.tool | head -5
# {
# "metadata": { "count": 2, "this": "/songs?take=2&skip=2",
# "next": "/songs?take=2&skip=4", "previous": "/songs?take=2", "page": 2 },
# "items": [ "Metro Lines", "Rain on Tin" ... ]
# }
- 过滤:
query关键字(如/api/songs?query=Upload,实测命中 1 条); - 排序:
sortBy字段名(实测sortBy=name返回按名称字典序的 7 首歌:Metro Lines / Midnight Drive / Neon Skyline / Paper Lanterns / Rain on Tin / Rush Hour / Upload Probe); - 标识符:
idOrSlug双态,但批量操作与关联接口建议统一用数字 ID。
七、常见问题
Q1:stream 端点用 slug 报 400?
实测回显 {"statusCode":400,"message":"Identifier: expected a number, got aiko-taylor-upload-probe"}。stream 域只认数字 ID(File 关联链主键),先用 GET /api/songs/{slug} 取 id/masterId 再拼流地址。这是 65 个端点中唯一不接受 slug 的域。
Q2:HLS 请求返回 400 提示 client id / Invalid quality?
转码器按会话缓存分片,必须带 X-CLIENT-ID(UUID 形式,同一播放器实例保持不变);音质档位不是自定义的,照抄 master.m3u8 返回的 URI(实测合法值 original 与 128k)。两处错误体均为 {"errors":[...]} 转码器风格。
Q3:POST /playlists 返回 400 一长串 must be a boolean?
isPublic 与 allowChanges 是必填布尔,缺省即 400(实测回显见 5.6)。按 {"name":"...","isPublic":false,"allowChanges":false} 最小集提交即可。
Q4:扫描 POST 返回 202 但没有新歌?
202 只表示任务入队。用 GET /scanner/tasks 确认执行状态,再用 docker logs 之外的 API 侧证据核对:GET /api/files 看物理文件是否注册、GET /api/songs?query=曲名 看逻辑歌曲是否生成。实测中首扫失败(文件未写完)时扫描器会因文件系统事件自动补扫成功,等几秒再查即可;持续失败查 ID3 标签必要字段(artist 与 album artist 至少一个)。
Q5:Token 过期报什么?
过期/伪造的 JWT 统一 401 {"statusCode":401,"message":"Unauthorized: Authentication required"},与未带 Token 同形。重新 POST /auth/login 换新 Token(实测有效期约 90 天,长脚本建议放登录重试逻辑)。
Q6:想看全部端点清单去哪?
server 内置 OpenAPI 文档(65 端点),实测从服务端拉取 swagger.json 后可用任意 OpenAPI 工具渲染;扫描器子服务另有独立 swagger(/scanner/swagger 前缀)。版本升级后端点可能增减,以对应版本 swagger 为准。
八、参考链接
- 内置 OpenAPI 描述(v3.13.1 源文件):https://raw.githubusercontent.com/Arthi-chaud/Meelo/v3.13.1/server/src/main/api-spec.json
- 流端点与转码器(Kyoo Transcoder)关系说明:https://github.com/zoriya/Kyoo
- 官方 Wiki(曲库组织与概念):https://github.com/Arthi-chaud/Meelo/wiki
- 本系列文档:《Meelo v3.13.1 介绍与选型指南》、《Meelo v3.13.1 Docker Compose 部署指南》、《Meelo v3.13.1 配置参数详解》