三件事: 1) 主播列表的录制档位 - 一律是下拉,不再有手填输入框。第一项「自动 · 第二高」(值 0)= 按这次读到的 档位排序自上往下取第二高(只有一档就用它,读不到档位表才退回最高档); 后面依次列出实际档位。选最高/最低/中间某一档直接点它,手动选过之后 「自动」仍留在列表里,随时能改回去。 - 语义变化:0 不再被写死成某个具体码率。原来记录档位时会把「没选过」的主播 按第二档定下来(写进 config.json),现在只在取流时解析,所以主播换了档位表 也能跟着走;app 启动时的补齐逻辑(applyCachedDefaultQualities)随之去掉。 - 解析规则统一到 resolveQualityBitrate:0 = 第二高,>0 = 不超过它的最高档, 都比它高时退回最低档;「自动」项会把解析到的档位名带出来 (自动 · 第二高(当前 = 蓝光 4M))。/api/status 和档位接口都多了 qualityBitrate。 - 点「档位」按钮时先给一句「正在读取码率档位…」:主播没开播时服务端要重试三次 才放弃,约 5 秒,之前这段时间界面没有任何反应。 2) 手动合并完成也推送(这就是「MeoW 只在测试时成功」的原因) - 原来只有自动合并调 sendNotify,手动合并从头到尾没推过,所以事件开着、渠道也通 却一直收不到。现在手动合并完成后同样按 mergeDone 事件推送两个渠道。 - 手动合并没有 uid,会拿成片名(再退到视频名)去对已有主播名,只有一个能对上时 才在文案里带上主播名,对不上就用「手动合并完成」。 3) 头像缓存挪到程序所在目录 - 原来存在「录播目录/avatar_cache」,换一次保存目录(甚至换盘)就要把同一批观众 的头像重新下一遍(一个头像可能 0.5MB)。现在统一放程序目录下的 avatar_cache。 - 旧位置作为只读回退:命中就搬过来(同盘改名,跨盘退回复制),不重复下载; 没被用到的旧文件留在原地,用到哪个搬哪个。 验证: - 新增 avatar_test.go 两个用例(缓存路径、旧位置搬家)和 notify_test.go 的 TestManualMergeSendsMergeDoneNotify(真跑一次合并,断言 webhook 与 MeoW 各收到 一条 mergeDone,且从文件名认出主播);qualities_test.go 改成新语义,新增 resolveQualityBitrate(7 种档位/码率组合)和 guessUIDByFileName。 - 端到端:把旧位置的头像缓存放在录播目录、头像地址指向连不通的端口,合并仍然成功 且画面里有那张头像(纯红像素 400 个),证明是搬来的而不是重新下载的;文件也从 录播目录搬到了程序目录。 - 浏览器:verify_streamer_br.py 重写成档位下拉的 20 项断言,verify_qualities.py 更新为新语义,两个都全过;verify_settings.py / verify_pick.py / verify_danmu_match.py 无回归;go test ./...、gofmt、go vet 干净。
137 lines
11 KiB
Markdown
137 lines
11 KiB
Markdown
# 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=` 里也都能通过。用脚本调这套接口时不用管编码,原样填就行。
|
||
|
||
`GET /api/streamer/qualities?uid=<uid>` 去直播间接一次该主播的**码率档位**(返回`{"uid":…,"qualities":[{"name":"蓝光 4M","bitrate":4000},…],"bitrate":选定的档位码率,"qualityBitrate":实际会用到的档位码率,"qualityName":实际会用到的档位名}`,`qualities`按码率从高到低)。读到的档位会缓存进`config.json`(没开播时页面靠它显示下拉),**但不会改动用户选的档位**:`bitrate`为 0 表示`自动`(按档位排序取第二高),只在取流时解析,所以主播换了档位表也能跟着走。同一个主播 10 秒内只让读一次,太频繁返回 502 并给中文原因(没开播时也会返回 502,且没开播时服务端本来就要重试三次,页面上会先给一句`正在读取码率档位…`)。`GET /api/status`里每个主播的`qualities`是这份缓存,`qualityBitrate`/`qualityName`是根据当前选择解析出来的结果(`bitrate`为 0 时就是第二高的那一档)。
|
||
|
||
跟手动合并有关的是下面三个:
|
||
`GET /api/merge/danmu?video=<视频文件名或路径>` 按视频名找同目录下的同名弹幕文件,给页面「选好视频自动匹配弹幕」用。依次试`.jsonl`、`.ass`、`.nick.ass`,同目录没有就拿文件名去录播目录再找一次;返回`{"danmu":"…"}`,找不到时是空字符串。值里录播目录/成片目录下的文件只写文件名,别处的写绝对路径,和`浏览`弹窗的填法一致。
|
||
|
||
`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。**合并完成也会触发一次推送**(和自动合并同一个`mergeDone`事件):手动合并没有 uid,会拿成片或视频的文件名去对已有的主播名,只有一个能对上时才在文案里带上主播名,对不上就用`手动合并完成`。
|
||
|
||
合并状态通过`GET /api/status`查看:`mergeBusy`(是否在跑)、`mergeOutName`(当前成片名)、`mergeErr`(失败原因)、`mergeDoneAt`(上次完成时间)。合并期间成片会出现在`GET /api/files`里且`inUse`为 true,此时`POST /api/file/remove`会返回 409 拒绝删除。
|
||
|
||
`POST /api/config`里的`danmuFontSize`控制合并时弹幕文字的字号(像素),头像按同一比例缩放,填 0 表示沿用弹幕文件里记的字号。渲染前会按最终字号重排弹道:从上游原版`.ass`还原出来的弹幕只按内容宽度占位,而渲染用的是「昵称(uid):内容 + 头像」的完整宽度,占位时间更长,照搬原弹道会追尾叠字;重排按完整宽度重新分配,同一条弹道上不会再有两条弹幕叠在一起。
|
||
|
||
`mergeBitrate`填 0(默认)时按「源片码率 + 500 Kbps」自动计算:先探测视频流的码率,加 500 留给烧进去的弹幕;探测不到就退回质量模式。填大于 0 的值则按该码率固定输出。编码器优先硬件 HEVC(`hevc_qsv`,同画质比 H.264 省约三成体积,mp4 里打`hvc1`标记),不支持时回退`h264_qsv`、`libx264`,日志里会打印实际用的那一个。
|
||
|
||
`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`会带上——页面据此退到上一级,不会把用户卡在一个打不开的路径里(比如把文件路径当目录展开时)。
|
||
|
||
这个接口能看到整台机器的目录,但权限和其它管理接口一样,都是拿到口令即全部权限,没有放宽。
|