文档类型:API 参考 关联软件:FoxPlayer 更新于 2026-10-19

集成与 API

FoxPlayer 对外集成参考:四个通道、认证方式、HTTP 接口、指令表、推送事件、错误码与可运行示例。

把 IFU 接进现有系统

1. 集成方式总览

方式 传输 默认监听 适用 特点
HTTP TCP 0.0.0.0:58000 管理后台、第三方控制、素材上传、状态查询 REST + JWT;可选 HTTPS
TCP TCP 0.0.0.0:58001 中控长连接 每行一条 JSON(JSON Line);最多 50 客户端
UDP UDP 0.0.0.0:58002 局域网广播/轻量控制 单数据报一条 JSON;无连接、不保证到达
WebSocket TCP 0.0.0.0:58003 需要实时状态的监控端 订阅-推送;最多 100 会话

四个通道共用同一套指令契约:同样的 cmd 与 params,在任一通道上的执行结果一致。区别只在传输、鉴权方式与响应回法。

事件上报是另一条链路:程序可以主动把 progress 等事件推到指定的 HTTP/TCP/UDP 目标,由 progress_http_enabled、progress_tcp_enabled、progress_udp_enabled 控制,默认关闭。

2. HTTP 接口清单

2.1 公开接口(无需 JWT)

方法 路径 说明
GET /api/pub/playlist 已启用节目单(enabled=1),按 sort_order 升序;失败返回 500 而不是空数组
GET /api/pub/status 播放状态 + 监视页布局字段
POST /api/pub/control 下发播放指令;配置 control_secret 后需 token 或 JWT
GET /api/auth/machine-code 获取机器码(用于申请授权)
GET /media/{filename} 静态媒体文件,支持 HTTP Range

2.2 认证接口

方法 路径 鉴权 说明
POST /api/auth/login 否 登录,返回 JWT;同 IP 10 分钟失败 5 次锁定
POST /api/auth/logout Bearer(可选) 令牌版本自增,旧 token 立即失效;幂等
PUT /api/auth/password Bearer 修改密码,body 字段 old / new
GET /api/auth/license Bearer 授权状态、机器码、到期日、剩余天数
POST /api/auth/license Bearer 激活,body 字段 licenseKey
DELETE /api/auth/license Bearer 清除授权文件

2.3 管理与业务接口(需 Bearer)

方法 路径 说明
GET /api/playlist 全部节目单项(含停用项),LEFT JOIN 素材信息
POST /api/playlist 新增节目单项,成功返回 201 与 id
PUT /api/playlist/{id} 更新节目单项
DELETE /api/playlist/{id} 删除节目单项(只删编排,不删素材)
PUT /api/playlist/batch 批量替换,body 为 {"items":[...]},先清空再事务写入
PUT /api/playlist/order 重排,body 为 {"ids":[3,1,2]}
POST /api/player/control 下发播放指令;body id 非空时校验 device_id
GET /api/player/status 播放器状态
GET /api/files 素材列表(过滤物理文件已不存在的记录)
DELETE /api/files/{filename} 删除素材:停播 → 软禁用节目单 → 删记录 → 删文件
POST /api/upload 多文件上传,multipart/form-data
GET /api/config 全部配置 + platform + capabilities
PUT /api/config 批量写配置,广播 config_changed
GET /api/logs 操作日志,支持 channel / cmd / result 筛选,最多 500 条
DELETE /api/logs 清空操作日志
GET /api/network/info IP / 掩码 / 网关 / DNS
POST /api/system/shutdown 系统关机(Android 返回 ignored)
POST /api/system/restart 系统重启(Android 返回 ignored)
POST /api/reload 重新加载节目单,不中断当前播放
POST /api/transcode/start 提交转码,body {"filename":"..."}
POST /api/transcode/cancel 取消转码,body {"filename":"..."}
GET /api/transcode/status busy / queue_size / current_file / current_progress

除上述接口外,其余非 /api/、非 /media/ 的路径由 SPA fallback 交给内置 Web 后台;未知 /api/ 路径返回 404 {}。

3. 认证

3.1 管理端 JWT

POST /api/auth/login
Content-Type: application/json

{"username":"admin","password":"admin123"}

成功:

{"status":"ok","token":"eyJhbGciOiJIUzI1NiIs..."}

之后请求携带:

Authorization: Bearer 

规则:

  • 算法 HS256,有效期 24 小时
  • payload 含 user 与 ver(令牌版本号)
  • 登出或改密会使 ver 自增,所有旧 token 立即失效
  • 密钥首次启动随机生成并持久化,不硬编码
  • 密码使用 PBKDF2-HMAC-SHA256(10 万次迭代 + 随机盐)存储

3.2 控制通道共享密钥

配置项 control_secret 为空时,TCP/UDP/WebSocket 与 /api/pub/control 默认开放(便于联调)。一旦填入非空值:

  • TCP / UDP / WebSocket:指令 JSON 顶层必须带 "token":"",常量时间比较
  • HTTP /api/pub/control:可用 query ?token=、body 顶层 token,或管理端 JWT 三者之一
  • 鉴权失败返回 {"status":"error","message":"unauthorized"};HTTP 公开控制接口对应 401

正式交付必须配置 control_secret,并同步给所有调用方。

3.3 授权码

  • 机器码:网卡名 + 主机名 + 操作系统与版本 + CPU 核数 的 SHA-256 前 32 位十六进制
  • 授权码:Base64(JSON payload).Base64(RSA-SHA256 签名)
  • payload:{"machineId":"<机器码>","expiry":<毫秒时间戳>}
  • 校验顺序:机器码匹配 → RSA-2048/SHA-256 验签 → 到期时间
  • 状态:有效 / 30 天内到期 / 已过期 / 未授权
  • 文件:数据目录 license.key;程序每 60 秒复查

4. 指令格式

4.1 请求

{
  "id": "",
  "cmd": "setplay",
  "params": {"file": "#1"},
  "token": ""
}
字段 类型 必选 说明
id string 否 指令标识。WS/TCP 中非空时只回给来源连接;为空则广播响应。HTTP /api/player/control 中它被当作设备 ID,非空且不等于本机 device_id 时返回 400
cmd string 是 指令名,使用小写(注册表按原样匹配)
params object 否 指令参数,缺省为空对象
token string 视配置 共享密钥;control_secret 非空时 TCP/UDP/WS 必填
timestamp number 否 毫秒时间戳;缺省由服务端补齐

4.2 响应

指令通道统一返回:

{"status":"ok","id":"","data":{...}}
{"status":"ignored","id":"","message":"已在播放状态"}
{"status":"error","id":"","message":"未知指令: xxx"}

HTTP 管理接口的写操作多为 202 Accepted:

{"status":"accepted"}

HTTP 业务查询直接返回数据对象(数组或对象),错误统一:

{"status":"error","message":"..."}

4.3 去重与延迟队列

  • setplay 对同一文件有 200 ms 防抖:窗口内重复切同一文件返回 ignored("操作过于频繁,请稍后重试")
  • 播放器处于 Preparing 时,setplay 进入最多 3 条的延迟队列;准备完成后只执行最先入队的一条,其余丢弃(避免连发导致连续跳项)
  • 其他指令不做全局去重

5. 指令表

指令 参数 行为 通道限制
setplay file 或 playlist_id;#N 表示第 N 项(1 起) 切到指定文件;不在节目单中则按外部文件路径兜底播放 四通道
play 无 / file / playlist_id 无参数:暂停→恢复、停止→从当前项/第 1 项开始、播放中→ignored;带文件:等价 setplay 四通道
pause 无 仅 playing 可暂停,否则 ignored 四通道
resume 无 仅 paused 可恢复,否则 ignored 四通道
replay 无 重播当前项;图片重置停留计时,视频 seek(0) 续播 四通道
seek mode:absolute/relative/progress;seconds 或 percent 绝对秒、相对秒、百分比(0–1);图片返回 error 四通道
volume level 0–100 设置音量并推送 volume 事件 四通道
next / previous 无 下一项 / 上一项 四通道
status 无 返回 state、position、duration、volume、current_file、media_type 四通道
playlist 无 返回 current_index、total、items 四通道
window_show / window_hide 无 显示 / 隐藏窗口 四通道
debug 无 切换调试面板 四通道
setConfig key、value 写配置;key 必须在默认配置白名单内 仅 HTTP
shutdown 无 系统关机(5 秒后) 仅 HTTP
restart 无 系统重启(5 秒后) 仅 HTTP
quit_app 无 退出应用 仅 HTTP
restart_app 无 重启应用 仅 HTTP

受控指令(shutdown/restart/quit_app/restart_app/setConfig)从 TCP/UDP/WebSocket 调用会返回:该指令仅允许 HTTP 管理通道调用。

5.1 setplay 细节

  • {"file":"#3"}:节目单第 3 项
  • {"playlist_id":"#3"}:同上
  • {"file":"abc.mp4"}:按文件名在节目单中查找;找不到则按路径兜底播放
  • {"playlist_id":"1"} 且无 file:回退到节目单第 1 项
  • 序号越界:节目单序号无效: N
  • 节目单为空且无目标:无播放目标且节目单为空

5.2 seek 细节

{"cmd":"seek","params":{"mode":"absolute","seconds":30}}
{"cmd":"seek","params":{"mode":"relative","seconds":-10}}
{"cmd":"seek","params":{"mode":"progress","percent":0.5}}

图片类型返回:图片类型不支持seek操作。

6. HTTP 调用示例

6.1 公开控制(未配置 control_secret)

Invoke-RestMethod -Method Post -Uri 'http://192.168.1.50:58000/api/pub/control' `
  -ContentType 'application/json' `
  -Body '{"cmd":"setplay","params":{"file":"#2"}}'

6.2 公开控制(已配置 control_secret)

Invoke-RestMethod -Method Post -Uri 'http://192.168.1.50:58000/api/pub/control?token=你的密钥' `
  -ContentType 'application/json' `
  -Body '{"cmd":"volume","params":{"level":60}}'

6.3 登录后控制(推荐管理端使用)

$base = 'http://192.168.1.50:58000'
$login = Invoke-RestMethod -Method Post -Uri "$base/api/auth/login" `
  -ContentType 'application/json' `
  -Body '{"username":"admin","password":"你的密码"}'
$h = @{ Authorization = "Bearer $($login.token)" }

# 带设备校验的控制
Invoke-RestMethod -Method Post -Uri "$base/api/player/control" -Headers $h `
  -ContentType 'application/json' `
  -Body '{"id":"FOX-STORE-001","cmd":"next"}'

# 查询状态
Invoke-RestMethod -Uri "$base/api/player/status" -Headers $h

6.4 上传素材

$h = @{ Authorization = "Bearer $($login.token)" }
Invoke-RestMethod -Method Post -Uri "$base/api/upload" -Headers $h -Form @{
  files = Get-Item 'D:\media\promo.mp4'
}

字段名 files;扩展名白名单与大小上限见《使用手册》。上传接口必须带 JWT。

6.5 媒体文件与 Range

GET http://192.168.1.50:58000/media/3f2a....mp4
Range: bytes=0-1048575

返回 206 与 Content-Range;越界返回 416。未知扩展名强制 application/octet-stream + 附件下载。

7. TCP / UDP / WebSocket

7.1 TCP(58001)

  • 每行一条 JSON;分隔符支持 LF、CR、CRLF、NUL、$
  • 行首 ^ 自动剥离;推荐 ^{"cmd":"play"}$
  • 响应为一行 JSON + \n
  • id 非空只回来源连接;id 为空广播给所有客户端
  • 最多 50 客户端;单连接缓冲超过 1 MB 断开
$client = New-Object System.Net.Sockets.TcpClient('192.168.1.50', 58001)
$stream = $client.GetStream()
$writer = New-Object System.IO.StreamWriter($stream)
$writer.AutoFlush = $true
$writer.WriteLine('^{"cmd":"status"}$')

$reader = New-Object System.IO.StreamReader($stream)
$reader.ReadLine()
$client.Close()

7.2 UDP(58002)

  • 一个数据报一条 JSON;回复发给最后发送者
  • 无连接、不保证到达;源码当前未实现令牌桶限流,上层应自行控制发送频率
  • 单包建议不超过 64 KB
$udp = New-Object System.Net.Sockets.UdpClient
$bytes = [Text.Encoding]::UTF8.GetBytes('{"cmd":"status"}')
$udp.Send($bytes, $bytes.Length, '192.168.1.50', 58002) | Out-Null
$ep = New-Object System.Net.IPEndPoint([Net.IPAddress]::Any, 0)
[Text.Encoding]::UTF8.GetString($udp.Receive([ref]$ep))

7.3 WebSocket(58003)

连接:ws://192.168.1.50:58003

订阅:

{"type":"subscribe","events":["progress","player_changed","command","volume"]}

发送指令:

{"id":"c1","cmd":"setplay","params":{"file":"#1"}}

响应会回到该连接(因为 id 非空)。服务端每 30 秒发协议 Ping 与应用层 {"type":"ping"};客户端可回 {"type":"pong"}。二进制帧被忽略;最多 100 会话。

8. 推送事件

事件信封(WS pushEvent / 事件上报):

{
  "device_id": "FoxPlayer-001",
  "event": "progress",
  "ts": 1750000000,
  "data": { }
}

config_changed、files_changed 走广播通道,信封为:

{"event":"config_changed","data":{ },"ts":1750000000}
事件 触发 data 主要字段
progress 播放状态流(约 250 ms) position、duration、state、volume、current_file、media_url、media_url_transcoded、media_type
player_changed 当前文件变更 current_file、media_url、media_url_transcoded、state、duration、media_type
command 收到有效指令 cmd、params
volume volume 指令 volume
config_changed PUT /api/config 变更键值;monitor_* 变更时补全结构化字段
files_changed 上传完成 action、files、count

WS 的 progress / player_changed / command / volume 受 progress_ws_enabled 与 push_ws_events 白名单过滤(默认只开 progress,player_changed)。想收到 command / volume,需把它们加入 push_ws_events。config_changed / files_changed 不受该白名单限制。

8.1 外推目标(可选)

配置 作用
progress_http_enabled + progress_http_url 把事件 POST 到指定 URL
progress_tcp_enabled + progress_tcp_target_host/port 把事件以 JSON Line 推到指定 TCP 服务
progress_udp_enabled + progress_udp_target_host/port 把事件以数据报推到指定 UDP 地址
progress_interval_ms 推送间隔基准,默认 250
push_http_events / push_tcp_events / push_udp_events 各通道事件白名单,逗号分隔

9. 错误码与常见消息

9.1 HTTP 状态码

状态码 场景 响应示例
200 查询/更新成功 {"status":"ok"} 或数据对象
201 新增节目单项 {"status":"ok","id":12}
202 指令已接受、关机/重启已下发 {"status":"accepted"}
400 参数缺失、旧密码错误、授权码格式错、device_id 不匹配 {"status":"error","message":"旧密码错误"}
401 未带 token / token 无效 / 登录失败 / 控制密钥错误 {"status":"error","message":"认证令牌无效或已过期"}
404 节目单项不存在、未知 API 路径 {"status":"error","message":"项目不存在"}
415 上传扩展名不在白名单 {"status":"error","message":"不支持的文件类型: x.exe"}
416 Range 越界 Range Not Satisfiable
500 服务端异常 {"status":"error","message":"操作失败"}
503 节目单服务未初始化 {"status":"error","message":"节目单服务未初始化"}

9.2 指令层消息

消息 含义
未知指令: xxx cmd 不在注册表
操作过于频繁,请稍后重试 setplay 200 ms 去重
当前不在播放状态 / 当前不在暂停状态 pause / resume 状态不匹配
当前无播放内容 replay 时无当前项
图片类型不支持seek操作 对图片执行 seek
节目单序号无效: N #N 越界
无播放目标且节目单为空 setplay 无目标且节目单为空
该指令仅允许 HTTP 管理通道调用 受控指令走了非 HTTP 通道
unauthorized 共享密钥缺失或不匹配
非法配置键: xxx setConfig 的 key 不在白名单

10. 集成注意事项

  1. id 有两层含义:在 WS/TCP/UDP//api/pub/control 中是指令标识(用于定向回包);在 /api/player/control 中同时被当作设备 ID 校验。跨系统对接时建议统一使用空 id + 业务侧自建请求号。
  2. cmd 用小写:注册表按字符串精确匹配,SetPlay 不会命中 setplay。
  3. 系统指令只走 HTTP:不要让中控通过 UDP 关机;这是有意的安全边界。
  4. 控制密钥必须配:不配 control_secret 等于把播放控制开放给整个局域网。
  5. 重复切同一文件会被去重:200 ms 内的第二次 setplay 返回 ignored,不是故障。
  6. 状态查询优先用 /api/pub/status:字段最全(含 media_url_transcoded 与监视布局);/api/player/status 更精简。
  7. 上传要 JWT:/api/upload 已套认证,匿名上传会 401。
  8. 视频 seek 用 Range:浏览器/播放器拉 /media/ 时可能发 Range,服务端已支持 206/416。
  9. HTTPS 是可选:证书与私钥都配置才启用;加载失败会回退 HTTP 并记录告警,不会中断启动。
  10. 事件不是可靠消息:UDP 推送会丢包;关键状态以轮询 /api/pub/status 为准。

事实依据:http_server.dart、auth_routes.dart、pub_routes.dart、player_routes.dart、playlist_routes.dart、command_service.dart、websocket_server.dart、tcp_server.dart、udp_server.dart

文档解决不了的,直接问我们

把现场条件和使用场景说清楚,我们的工程师会给出明确结论,而不是只发一份资料链接。