LibreTime 4.5.0 REST API 使用详解

LibreTime 4.5.0 REST API 使用详解:v2 全局约定(Basic/会话/legacy api_key 三种认证实测边界)、files/shows/show-instances/schedule/playlists/webstreams/users/preferences/playout-history 九大域逐条 curl 实测、创建排期全链路 201 回显、无服务端分页约定与错误格式、常见问题。

LibreTimeLibreTime2026/10/29

LibreTime 4.5.0 REST API 使用详解

目标:讲清 LibreTime 4.5.0 的 REST API 体系——三种认证方式的实测边界、九个核心资源域的端点/参数/请求/响应、从上传到排期开播的全链路调用实录,帮助运维与集成方用 API 完成排单、流管理与账号管理的自动化。全部请求与响应均在 4.5.0 实测环境 curl 实拍,无转述示例。

一、环境信息

  • 操作系统:Ubuntu 22.04 x86_64(4C/8G)
  • LibreTime:4.5.0(官方 Docker 编排全栈,入口 nginx http://127.0.0.1:8142)
  • API 版本:v2(实测 {"api_version":"2.0.0"})
  • 规范文件:官方 OpenAPI 3.0 规范(4.5.0 随仓库发布,共 74 个路径、38 个资源域 tag)
  • 客户端工具:curl 7.81 + python3(json 解析)
  • 测试账号:Web 管理员 admin / q1w2e3r4(role=A)
  • 内部 API Key:部署时 openssl rand -hex 32 生成(本环境为 867b550987df8c6ff65140584d9cd6e93a18e386e8ed69b5e4c00dc858e01b73,读者以自己的为准)
  • 实测日期:2026-10-29

二、前置条件

  • LibreTime 4.5.0 全栈已部署并完成数据库迁移(见《LibreTime 4.5.0 Docker Compose 部署指南》)。
  • 已知三类凭据:管理员账号口令(admin/q1w2e3r4)、config.yml 中的 general.api_key、Web 登录会话 cookie(PHPSESSID/sessionid)。
  • 确认入口可达:
curl -s -o /dev/null -w 'login page: %{http_code}\n' http://127.0.0.1:8142/login
curl -s http://127.0.0.1:8142/api/v2/version

实测回显:

login page: 200
{"api_version":"2.0.0"}

三、API 总览

3.1 协议与入口

  • 全部走 HTTP(生产建议反代加 TLS 后对外),统一入口为 nginx(本文 8142)。
  • 两套 API 共存:
    • v2 API(Python/Django 实现):/api/v2/*,REST 风格,OpenAPI 3.0 规范覆盖,本文主体。
    • legacy REST(PHP 实现):/rest/*,仅少数端点(如媒体上传 POST /rest/media),认证方式特殊(见 4.3)。

3.2 资源域清单

4.5.0 的 38 个资源域(OpenAPI tags 实查)按用途分组:

分组域
媒体资产files、libraries、third-party-track-references
排期播出shows、show-days、show-instances、show-rebroadcasts、show-hosts、schedule、playlists、playlist-contents、smart-blocks、smart-block-contents、smart-block-criteria、webstreams、webstream-metadata
播客podcasts、podcast-episodes、imported-podcasts、station-podcasts
播出记录与统计playout-history、playout-history-metadata、playout-history-templates、playout-history-template-fields、listener-counts、live-logs、timestamps
流配置与状态stream(/api/v2/stream/preferences)、mount-names
系统管理users、user-tokens、preferences、login-attempts、service-registers、celery-tasks、schema、info、version

如何选:排单自动化主用 shows/show-instances/schedule;媒体接入用 files;对接播放器/统计用 playout-history 与 listener-counts;配置巡检用 preferences 与 stream/preferences。

3.3 认证方式(实测边界)

认证方式形态适用范围(实测)
HTTP Basiccurl -u 'admin:q1w2e3r4'/api/v2/* 全部端点
会话 Cookie登录后携带 sessionidWeb 界面 + /api/v2/*
X-API-Key 头-H 'X-API-Key: <key>'仅少数 legacy 兼容端点(实测 /api/v2/version、/api/v2/info、/api/v2/stream/preferences 可用;/api/v2/files 等资源端点不认,返回 401)
legacy Basic(api_key:)curl -u '<API_KEY>:'/rest/*(用户名=api_key,密码留空)

⚠️ 实测踩坑:用 X-API-Key 调 /api/v2/files 会得到 {"detail":"Authentication credentials were not provided."}(api 服务日志 WARNING Forbidden: /api/v2/files)。资源端点一律用 Basic 或会话认证,X-API-Key 只在部分端点碰巧兼容,不要当通用凭证。

四、认证与首次调用

4.1 匿名端点:version 与 info

/api/v2/version 与 /api/v2/info 免认证(规范中 security 含空对象),可直接探测服务健康:

curl -s 'http://127.0.0.1:8142/api/v2/version'; echo
curl -s 'http://127.0.0.1:8142/api/v2/info'; echo

实测回显:

{"api_version":"2.0.0"}
{"station_name":"LibreTime"}

4.2 Basic 认证调用资源端点

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/users'

实测回显(节选):

[{"id":1,"role":"A","username":"admin","email":null,"first_name":"","last_name":"","login_attempts":0,"last_login":null,...}]

4.3 认证失败与 legacy 认证实录

无凭证调用受保护端点返回 401 与统一错误体:

curl -s 'http://127.0.0.1:8142/api/v2/shows' -w ' [%{http_code}]'

实测回显:

{"detail":"Authentication credentials were not provided."} [401]

legacy 上传端点用 api_key 做 Basic 用户名(密码留空),实测:

curl -s -u '867b550987df8c6ff65140584d9cd6e93a18e386e8ed69b5e4c00dc858e01b73:' \
  -X POST 'http://127.0.0.1:8142/rest/media' \
  -F 'file=@test-track.mp3;type=audio/mpeg' -o /tmp/up.json -w '%{http_code}\n'

实测回显:

201

查看响应(id 由服务端生成,import_status=1 表示分析中):

python3 -c "import json; d=json.load(open('/tmp/up.json')); print('file id:', d.get('id'), '| import_status:', d.get('import_status'), '| md5:', d.get('md5'))"

实测回显:

file id: 1 | import_status: 1 | md5: a30d22e4f630a36fc54d53f8963eabc1

五、各 API 域详解

5.1 files:媒体文件域

端点:/api/v2/files(GET 列表、POST 元数据创建)、/api/v2/files/{id}(GET/PUT/PATCH/DELETE)、/api/v2/files/{id}/download。

查询单文件元数据(等分析完成后 import_status=0):

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/files/1' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print({k:d[k] for k in ('id','filepath','size','mime','length','bit_rate','import_status')})"

实测回显:

{'id': 1, 'filepath': 'imported/1/test-track.mp3', 'size': 4321533, 'mime': 'audio/mp3', 'length': '00:03:00.035918', 'bit_rate': 192000, 'import_status': 0}

⚠️ 注意:POST /api/v2/files 是元数据创建端点(要求 size/mime/accessed/name 等字段),不收文件本体;文件上传固定走 4.3 的 legacy POST /rest/media。实测错误形态:

{"size":["This field is required."],"mime":["This field is required."],"accessed":["This field is required."],"name":["This field is required."]}

查询不存在的 id 返回 404:

{"detail":"No File matches the given query."} [404]

5.2 shows:节目域

端点:/api/v2/shows(GET/POST)、/api/v2/shows/{id}(GET/PUT/PATCH/DELETE)。

创建节目——布尔字段全部必填(实测省略即 400):

curl -s -u 'admin:q1w2e3r4' -X POST 'http://127.0.0.1:8142/api/v2/shows' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Test Show","description":"forensic test show","genre":"various","linked":false,"linkable":false,"auto_playlist_enabled":false,"auto_playlist_repeat":false,"override_intro_playlist":false,"override_outro_playlist":false,"live_enabled":false}' \
  -w '\n[%{http_code}]\n'

实测回显(节选):

{"id":1,"name":"Test Show","description":"forensic test show","genre":"various",...,"live_enabled":false,...}
[201]

漏字段的错误形态(实测省略布尔字段时):

{"linked":["This field is required."],"linkable":["This field is required."],"auto_playlist_enabled":["This field is required."],...}

查询单节目:

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/shows/1' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print({k:d[k] for k in ('id','name','genre','live_enabled')})"

实测回显:

{'id': 1, 'name': 'Test Show', 'genre': 'various', 'live_enabled': False}

5.3 show-instances:期次域

端点:/api/v2/show-instances(GET/POST)、/api/v2/show-instances/{id}(GET/PUT/PATCH/DELETE)。时间统一 UTC ISO8601(Z 后缀),除 show/starts_at/ends_at 外还需 created_at、auto_playlist_built、record_enabled(实测缺一即 400):

curl -s -u 'admin:q1w2e3r4' -X POST 'http://127.0.0.1:8142/api/v2/show-instances' \
  -H 'Content-Type: application/json' \
  -d '{"show":1,"starts_at":"2026-10-29T10:26:00Z","ends_at":"2026-10-29T10:46:00Z","record_enabled":0,"modified":true,"created_at":"2026-10-29T10:24:00Z","auto_playlist_built":false}' \
  -w '\n[%{http_code}]\n'

实测回显:

{"id":1,"show":1,"starts_at":"2026-10-29T10:26:00Z","ends_at":"2026-10-29T10:46:00Z",...}
[201]

缺字段错误形态(实测首次提交):

{"created_at":["This field is required."],"auto_playlist_built":["This field is required."]}

5.4 schedule:排期域(排单核心)

端点:/api/v2/schedule(GET/POST)、/api/v2/schedule/{id}(GET/PUT/PATCH/DELETE)。把文件排进期次(file=1 为 3 分钟曲目,排 10:26:00Z 开播):

curl -s -u 'admin:q1w2e3r4' -X POST 'http://127.0.0.1:8142/api/v2/schedule' \
  -H 'Content-Type: application/json' \
  -d '{"instance":1,"file":1,"starts_at":"2026-10-29T10:26:00Z","ends_at":"2026-10-29T10:29:00Z","length":"00:03:00.035918","position":0,"fade_in":"00:00:00","fade_out":"00:00:00","cue_in":"00:00:00","cue_out":"00:03:00.035918","broadcasted":0,"played":false}' \
  -w '\n[%{http_code}]\n'

实测回显:

{"id":1,"starts_at":"2026-10-29T10:26:00Z","ends_at":"2026-10-29T10:29:00Z","length":"00:03:00.035918","fade_in":"00:00:00","fade_out":"00:00:00","cue_in":"00:00:00","cue_out":"00:03:00.035918","position":0,"position_status":1,"broadcasted":0,"played":false,"instance":1,"file":1,"stream":null}
[201]

到点播出后核对该条排期——broadcasted 置 1、played 置 true(播出闭环的 API 证据):

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/schedule/1' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print({k:d[k] for k in ('id','starts_at','broadcasted','played')})"

实测回显:

{'id': 1, 'starts_at': '2026-10-29T10:26:00Z', 'broadcasted': 1, 'played': True}

stream 字段:排外部网络流时填 webstream 的 id(与 file 二选一),即 webstreams 域的排期用法。

5.5 playlists:播放列表域

端点:/api/v2/playlists(GET/POST)、/api/v2/playlists/{id}(GET/PUT/PATCH/DELETE)、/api/v2/playlist-contents(列表项管理)。列表查询:

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/playlists' -w '\n[%{http_code}]\n'

实测回显(空库初始状态):

[]
[200]

播放列表可挂在节目上作为自动填充(shows 的 auto_playlist 字段引用 playlist id),smart-blocks 域则提供按条件(流派/时长)动态生成的智能块,与 playlists 同构(blocks→block-contents 两级端点)。

5.6 webstreams:外部流域

端点:/api/v2/webstreams(GET/POST)、/api/v2/webstreams/{id}(GET/PUT/PATCH/DELETE)。用于把远端网络流登记为可排期资产(如转播友台),登记后经 schedule 的 stream 字段排入时间线。列表查询实测:

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/webstreams' -w '\n[%{http_code}]\n'

实测回显:

[]
[200]

5.7 users:用户域

端点:/api/v2/users(GET/POST)、/api/v2/users/{id}(GET/PUT/PATCH/DELETE)。PATCH 修改资料实测:

curl -s -u 'admin:q1w2e3r4' -X PATCH 'http://127.0.0.1:8142/api/v2/users/1' \
  -H 'Content-Type: application/json' -d '{"first_name":"Radio"}' -w '\n[%{http_code}]\n'

实测回显:

{"id":1,"role":"A","username":"admin","email":null,"first_name":"Radio","last_name":"","login_attempts":0,...}
[200]

⚠️ 注意:API 修改密码需同时满足 Django 密码策略(实测 q1w2e3r4 会被判 This password is too common 拒绝);部署期改密可用容器内 libretime-api shell -c 方式(见部署指南 4.7 节)。role 取值:A=admin、M=program manager、H=host、G=guest。

5.8 preferences:系统偏好域

端点:/api/v2/preferences(GET 全量、PUT 整体)、/api/v2/preferences/{key}(GET 单项)。实测全量 21 项:

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/preferences' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d),'项'); [print(k,'=',v) for k,v in list(d.items())[5:10]]"

实测回显(节选):

21 项
default_fade_in = 0.500
default_fade_out = 0.500
default_intro_playlist = 0
default_outro_playlist = 0
default_station_name = LibreTime

5.9 stream 与 mount-names:流配置/状态域

/api/v2/stream/preferences 返回流输入/输出的运行时摘要:

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/stream/preferences'

实测回显:

{"input_fade_transition":0.0,"message_format":0,"message_offline":"LibreTime - offline","replay_gain_enabled":true,"replay_gain_offset":0.0}

/api/v2/mount-names 返回当前输出挂载点清单:

curl -s -u 'admin:q1w2e3r4' 'http://127.0.0.1:8142/api/v2/mount-names'

实测回显:

[{"id":1,"mount_name":"main"}]

5.10 playout-history:播出历史域

端点:/api/v2/playout-history?starts_at=<UTC>&ends_at=<UTC>(GET)。这是 v2 列表端点中少数服务端过滤参数真实生效的域(实测不同时间窗返回不同结果):

curl -s -u 'admin:q1w2e3r4' \
  'http://127.0.0.1:8142/api/v2/playout-history?starts_at=2026-10-29T00:00:00Z&ends_at=2026-10-30T00:00:00Z'

实测回显:

[{"id":1,"starts":"2026-10-29T10:26:00Z","ends":"2026-10-29T10:29:00Z","file":1,"instance":1}]

配套的 listener-counts 域按流挂载点记录听众数时序(需 icecast admin_password 配置后才采集)。

六、分页/过滤/排序通用约定

实测结论(对全部 /api/v2/* 列表端点验证):

  • 无服务端分页:传 page=2 等参数被忽略,恒返回全量数组(实测 /api/v2/files?page=2 仍返回完整列表)。媒体库大时建议自行缓存或按 starts_at/ends_at(playout-history 专用)切窗。
  • 列表无服务端过滤:/api/v2/schedule?instance=2 实测返回全部 2 条(含 instance=1),过滤参数被忽略;需客户端拿到后自行筛选。例外是 playout-history 的时间窗参数(5.10,规范内定义、实测生效)。
  • 排序:列表默认按 id 升序返回;无排序参数。
  • 错误格式统一:401 返回 {"detail":"Authentication credentials were not provided."};404 返回 {"detail":"No <Resource> matches the given query."};400 返回字段级字典 {"<field>":["This field is required."]}。
  • 时间格式:一律 UTC ISO8601 带 Z(2026-10-29T10:26:00Z);时长格式 HH:MM:SS.ffffff(00:03:00.035918)。

七、常见问题

Q1:X-API-Key 为什么在部分端点 401? v2 资源端点(files/shows/schedule 等)只实现了 Basic 与会话认证;X-API-Key 是 legacy 兼容层,仅 version/info/stream 等端点接受。统一用 Basic(admin 账号)最稳。

Q2:POST /rest/media 报 Incorrect API key? legacy REST 的 Basic 用户名必须是 config.yml 的 general.api_key 且密码留空(-u '<API_KEY>:');用 admin 账号或 X-API-Key 头都不行。另外路径是 /rest/media,不是 /rest/media/upload(后者把 upload 当 id 查库报 400)。

Q3:POST 创建一直 400? 4.5.0 的序列化器把大量布尔/审计字段设为必填:shows 要 6 个布尔全给,show-instances 要 created_at 与 auto_playlist_built。按 5.2/5.3 的实测最小体照抄,缺啥按 400 返回的字段字典补。

Q4:排了单没有播出? 检查三点:排期时间是否 UTC(北京时间要 -8 换算);playout/liquidsoap 是否运行(部署期启动顺序故障见部署指南 11.2);GET /api/v2/schedule/{id} 看 broadcasted/played 是否翻转,未翻转则查 docker logs libretime-450-stack-playout-1。

Q5:大列表怎么处理? 无服务端分页(第六章),一次全量。媒体库上万首时在网关层缓存或改用 schema 域了解字段后按 id 区间自行实现增量拉取。

八、参考链接