Files
acfunlive-remake/doc/webapi.md
T
tamakyi 9c94a72c4d 合并前重排弹道并支持自定义弹幕字号
用户反馈两件事:弹幕想再小些;某场合并的 1:19:25 处弹幕糊在一起(把成片抽帧
出来一看,是同一弹道上两条弹幕追尾叠字)。

重叠的根因:弹幕从上游原版的 .ass 还原成 jsonl 时,lane 是按「只有内容」的
宽度反推的(原格式的 x/y 就是这么摆的),而渲染用的是「昵称(uid):内容 + 头像」
的完整宽度 —— 后者占位时间长得多,照搬原弹道必然追尾。用用户那份 3011 条的真实
jsonl 验过:按渲染公式统计,重排前 960 条(31.9%)追尾,重排后 0 条。

- 新增 relayoutLanes:渲染前按最终字号统一重排弹道并重算宽度,公式和录制时的
  placeLane 完全一致,所以录制下来的 jsonl 重排后弹道不变(已加用例覆盖)。
- 空弹道用 math.MinInt64 占位:placeLane 的条件是「本条出现时间晚于上一条的让位
  时刻」,零值下 0 > 0 不成立,第一条弹幕(还原出来的时间可能正好是 0)会占不上
  弹道、占用状态丢失,后面又跟着乱排。录制的弹幕时间总大于 0,所以原逻辑没暴露。
- 弹道全满(100 条都占着)时退到最早让出来的那条:录制时是直接不写这条,合并时
  丢一条等于凭空少一句弹幕,压一点也比丢了强。
- 新增 danmuFontSize 设置(合并分区,0 = 跟随弹幕文件):覆盖渲染字号,头像按
  同一比例缩放;改了字号会在重排时一并生效,不会因为宽度变了又追尾。

验证:layout_test.go 5 个用例;用新 ffmpeg(用户已自行升级到 n9.0.2)在隔离实例上
跑了真合并 —— 字号 95 以上的对照(60px → 42px:抽帧量到的弹幕墨迹高度 48px → 36px)、
按 1:19:00 裁出的 130 秒片段合并前后追尾 25 条 → 0 条;重跑 verify_settings.py
(新增第 8 节覆盖字号字段)、verify_pick.py、verify_streamer_br.py 无回归;
go test ./...、gofmt、go vet 干净。
2026-09-25 18:18:30 +08:00

131 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# acfunlive web API
web API 默认本地端口为 51880
`http://localhost:51880/listlive` 列出正在直播的主播
`http://localhost:51880/listrecord` 列出正在下载的直播视频
`http://localhost:51880/listdanmu` 列出正在下载的直播弹幕
`http://localhost:51880/liststreamer` 列出设置了开播提醒或自动下载直播的主播
`http://localhost:51880/startmirai` 利用 Mirai 发送直播通知到指定 QQ 或 QQ 群
`http://localhost:51880/addnotifyon/23682490` 通知 uid 为 23682490 的主播的开播
`http://localhost:51880/delnotifyon/23682490` 取消通知 uid 为 23682490 的主播的开播
`http://localhost:51880/addnotifyoff/23682490` 通知 uid 为 23682490 的主播的下播
`http://localhost:51880/delnotifyoff/23682490` 取消通知 uid 为 23682490 的主播的下播
`http://localhost:51880/addnotifyrecord/23682490` 通知 uid 为 23682490 的主播的直播视频下载
`http://localhost:51880/delnotifyrecord/23682490` 取消通知 uid 为 23682490 的主播的直播视频下载
`http://localhost:51880/addnotifydanmu/23682490` 通知 uid 为 23682490 的主播的直播弹幕下载
`http://localhost:51880/delnotifydanmu/23682490` 取消通知 uid 为 23682490 的主播的直播弹幕下载
`http://localhost:51880/addrecord/23682490` uid 为 23682490 的主播直播时自动下载其直播视频
`http://localhost:51880/delrecord/23682490` 取消自动下载 uid 为 23682490 的主播的直播视频
`http://localhost:51880/adddanmu/23682490` uid 为 23682490 的主播直播时自动下载其直播弹幕
`http://localhost:51880/deldanmu/23682490` 取消自动下载 uid 为 23682490 的主播的直播弹幕
`http://localhost:51880/addkeeponline/23682490` uid 为 23682490 的主播直播时在其直播间里挂机
`http://localhost:51880/delkeeponline/23682490` 取消设置在 uid 为 23682490 的主播直播时在其直播间里挂机
`http://localhost:51880/delconfig/23682490` 删除 uid 为 23682490 的主播的所有设置
`http://localhost:51880/getdlurl/23682490` 查看 uid 为 23682490 的主播是否在直播,并输出其直播源
`http://localhost:51880/addqq/23682490/12345` 将 uid 为 23682490 的主播的开播提醒发送到 QQ12345,需要 QQ 机器人已经添加该 QQ 为好友
`http://localhost:51880/delqq/23682490/12345` 取消将 uid 为 23682490 的主播的开播提醒发送到 QQ12345
`http://localhost:51880/cancelqq/23682490` 取消将 uid 为 23682490 的主播的开播提醒发送到任何 QQ
`http://localhost:51880/addqqgroup/23682490/12345` 将 uid 为 23682490 的主播的开播提醒发送到 QQ 群 12345,需要 QQ 机器人已经加入该 QQ 群,最好是管理员,会@全体成员
`http://localhost:51880/delqqgroup/23682490/12345` 取消将 uid 为 23682490 的主播的开播提醒发送到 QQ 群 12345
`http://localhost:51880/cancelqqgroup/23682490` 取消将 uid 为 23682490 的主播的开播提醒发送到任何 QQ 群
`http://localhost:51880/startrecord/23682490` 临时下载 uid 为 23682490 的主播的直播视频
`http://localhost:51880/stoprecord/23682490` 取消下载 uid 为 23682490 的主播的直播视频
`http://localhost:51880/startdanmu/23682490` 临时下载 uid 为 23682490 的主播的直播弹幕
`http://localhost:51880/stopdanmu/23682490` 取消下载 uid 为 23682490 的主播的直播弹幕
`http://localhost:51880/startrecdan/23682490` 临时下载 uid 为 23682490 的主播的直播视频和弹幕
`http://localhost:51880/stoprecdan/23682490` 取消下载 uid 为 23682490 的主播的直播视频和弹幕
`http://localhost:51880/log` 查看 log
`http://localhost:51880/quit` 退出 acfunlive 运行
`http://localhost:51880/help` 显示帮助信息
## 新版 web UI 的管理接口
web UI(`-webui`,端口看`webPort`)用的是另一套`/api/`接口,全部需要带管理口令:请求头`X-Auth-Token: <口令>`,或者在 URL 上带`?token=<口令>`(下载链接用后者,因为浏览器跳转带不了请求头)。
口令支持任意字符,包括中文——不过 HTTP 请求头只接受 ISO-8859-1,浏览器遇到非 ASCII 的口令会直接拒绝发请求(报`String contains non ISO-8859-1 code point`),所以**页面发给服务端的`X-Auth-Token`是百分号编码过的口令**(`encodeURIComponent`)。服务端两种写法都认:先拿原样比,再拿解码后的比,所以手写 curl 或者脚本按原样把中文口令放进请求头、`Authorization: Bearer`、`?token=` 里也都能通过。用脚本调这套接口时不用管编码,原样填就行。
跟手动合并有关的是下面两个:
`POST /api/merge/info` 预览一次手动合并的计划,不落盘。
```
{
"videos": ["2026-09-25 01-00-00 主播 标题.mp4", "D:/别处/随手放的视频.mp4"],
"danmu": "2026-09-25 01-00-00 主播 标题.ass",
"offset": 3 // 可选,不填按文件名里的录制时间推算
}
```
返回分段列表(名字、体积、时长、是否正在写入)、总时长、弹幕条数、弹幕时间范围、要用的弹幕文件和来源(`jsonl 弹幕数据` / `同名的 jsonl 弹幕数据` / `由 xxx.ass 还原`)、推算出的偏移和原因、最终输出文件名、以及提示(`warnings`)。参数不合法一律 400,并在响应体里给中文原因。
`POST /api/merge` 真正开始合并,请求体和上面一样,另外可以带`outName`指定输出文件名(不合法会被清理成安全的文件名,重名自动加后缀)。成功后立刻返回`{"outName":"…"}`,合并在后台跑。已经有一个手动合并任务在跑时返回 409。
合并状态通过`GET /api/status`查看:`mergeBusy`(是否在跑)、`mergeOutName`(当前成片名)、`mergeErr`(失败原因)、`mergeDoneAt`(上次完成时间)。合并期间成片会出现在`GET /api/files`里且`inUse`为 true,此时`POST /api/file/remove`会返回 409 拒绝删除。
`POST /api/config`里的`danmuFontSize`控制合并时弹幕文字的字号(像素),头像按同一比例缩放,填 0 表示沿用弹幕文件里记的字号。渲染前会按最终字号重排弹道:从上游原版`.ass`还原出来的弹幕只按内容宽度占位,而渲染用的是「昵称(uid):内容 + 头像」的完整宽度,占位时间更长,照搬原弹道会追尾叠字;重排按完整宽度重新分配,同一条弹道上不会再有两条弹幕叠在一起。
`POST /api/notify/test` 测试某个推送渠道能不能连通。用传进来的值直接测,留空的项自动用已保存的配置补齐,所以不用先保存设置。测试不受渠道开关和事件勾选影响。
```
// QQ 机器人(webhook)渠道,channel 不填就按这个
{ "channel": "qq", "url": "http://127.0.0.1:8080/acfunlive/notify", "token": "可选", "group": "可选" }
// MeoW 渠道
{ "channel": "meow", "nickname": "你的昵称", "msgType": "text", "imgUrl": "可选", "htmlHeight": 200 }
```
成功后返回`{"ok":true,"detail":"webhook 返回 HTTP 200,用时 0.31 秒。…"}`,`detail`就是页面上显示的那句结论。失败时返回 502 并在响应体里给中文原因(如`MeoW 返回 status 404(昵称未注册)`),参数不合法返回 400。
MeoW 的`status`和封禁规则会反映在`GET /api/status`的`meowBlocked`字段里:昵称没填、格式不对、或已被 MeoW 判过「未注册」时,这里会给出中文原因,非空就说明 MeoW 渠道当前推不出去。注意 MeoW 一天内累计 3 次推给不存在的昵称会封 IP,所以被判过不存在的昵称会被程序记住并停止重试,改昵称或重启程序才恢复。
`GET /api/browse` 给页面上的`浏览`按钮列目录用。参数:`path`(要展开的目录,不填就用程序当前的工作目录)、`kind=file`(连文件一起列,挑文件时用)、`hidden=1`(连以点开头的隐藏项一起列)。页面上的`浏览`有单选(保存目录、ffmpeg 路径、弹幕文件)和多选(手动合并的视频文件)两种用法,接口每次只负责列一层,多选的勾选状态由页面自己保管,所以可以换着目录勾、跨文件夹攒;填回框里时,录播目录下的文件写文件名、别的文件夹里的写绝对路径。
```
{
"path": "D:\\record", // 当前展开的目录,已转成绝对路径
"parent": "D:\\", // 上级目录,已经到根时是空串
"roots": [{"name": "C:", "path": "C:\\", "dir": true, "size": 0}],
"entries": [{"name": "2026-09-25.mp4", "path": "D:\\record\\2026-09-25.mp4", "dir": false, "size": 123}],
"truncated": false, // 条目太多时只返回前 2000 项
"error": "" // 打不开时的中文原因
}
```
文件夹排在文件前面,同类按名字排(不区分大小写)。挑文件时文件夹照样会列出来,不然进不去子目录。`error`非空时仍是 HTTP 200,`entries`是空的,但`parent`会带上——页面据此退到上一级,不会把用户卡在一个打不开的路径里(比如把文件路径当目录展开时)。
这个接口能看到整台机器的目录,但权限和其它管理接口一样,都是拿到口令即全部权限,没有放宽。