tamakyi
|
6f3c8096b4
|
修分块渲染的弹幕轨迹:跨块续画 + 文字头像同源,并补失败推送与输出帧率设置
1. 密集弹幕时头像偏离轨迹、弹幕突然加速、之后一段时间屏幕空白
分块渲染的固有问题,三个毛病叠在一起:
- writeChunkAss 把 \move 的起止时间截断到块边界(End=min(t0+10, chunk.end)),
但 x2 写死成 -Width —— 等于把 10 秒的路挤进可见的那零点几秒里,文字直接飞过去。
而头像 overlay 的 x 一直是「固定 /10 秒 + 真实 t0」,于是同一时刻文字已经走到
左边、头像还在半路,看起来就是「头像掉队(移动变慢)」。
- 块尾还会被单块上限收紧成「下一条弹幕出现的时间」,最极端时窗口只剩 0.01 秒。
- 每块只画「本块新出现」的弹幕,跨块的续画没有:块头屏幕上只剩刚出现的几条,
要 10 秒才重新填满 —— 就是「弹幕突然加速,然后一段时间屏幕上什么都没有」。
改法:文字层改成按绝对时间算轨迹(danmuTextX / danmuAvatarX / avatarXExpr 同源),
任何一块里的文字速度都等于真实速度;分块器改成「续画(起播时间在 start-10 ~ start)
+ 本块新出现的」,块尾取「下一条弹幕的起播时间」,这样每条弹幕只在唯一一块里算
新出现,显示期间跨过的后面几块把它接着画,一帧都不缺。
代价是每块要多画一倍多的弹幕,所以顺手把头像一个「绝对路径 + 每人一行 scale」改成
「预缩放 + 短相对路径」(prepareAvatarInputs / scaleAvatarFile),每条从实测 241
字符降到约 130,单块上限才能从 120 抬到 200(200 条 ≈ 26600,仍低于自检阈值 30000
和 Windows 的 32767)。
2. 合并失败没有任何通知
notify.go 新增 mergeFail 事件与 notifyMergeFailure(),接在 autoMerge() 和手动合并的
失败分支上。正常结束、文件里没有弹幕、以及用户自己点的停止都不算失败,不推 ——
最后一种推「合并失败」会误导。配置项 notifyMergeFail 默认开,且和 notifyEnabled 一样
在 Unmarshal 之前预置 true,免得老设置文件升级后被静默关掉。
3. 低帧率录播里的弹幕一格一格跳
新增设置项 mergeFps,默认 0 = 跟随源片(什么都不做)。只在目标高于源片帧率时才加
fps 滤镜,而且必须加在字幕/头像之前 —— 加在之后只是把已经画好的几帧复制一遍,
看起来还是卡。上限 240。
4. 顺带
- -danmuburn 命令行模式不走 initialize(),exeDir 从来没被赋值:头像缓存会落到
「当前工作目录/avatar_cache」,旧版还会把这个相对路径写进滤镜,而 ffmpeg 的工作
目录是弹幕目录,报「打不开 avatar_cache\xxx.png」整块失败。
- cleanTempFiles 见到目录就 continue,预缩放头像那个临时文件夹永远清不掉。
- command_len_test 不再用会过期的「每条 241 字符」估算,改成真跑一遍最坏情况;
探针的头像路径也改成真实的短相对路径(原来量的是早已不存在的长绝对路径)。
- 新增 TestWebuiSettingFieldsMatchInputs:把页面里 JS 的 settingFields/numberFields
抠出来,跟所有 input/select/textarea 的 id 比对,漏登记的设置项会被抓住。
验证
- gofmt / go vet / go test(174 个)/ go test -race / staticcheck / GOOS=linux go build 全过。
- 反向验证 32 项全过:把每个修复临时退回,对应测试确实失败。
- 真实片段实测(2023AcFun 晚会 120 秒高峰段,689 条弹幕,10 秒窗口内最多同时 140 条):
旧 6 块 / 绘制 689 次 / 54.1% 的弹幕文字被加速(中位 2.12x,最差 500x)/
54.2% 有轨迹缺口(中位缺 5.29 秒)/ 78.3s;
新 5 块 / 绘制 984 次 / 加速 0% / 缺口 0% / 98.8s(+26%)。
对照帧里旧版在块尾前 0.1 秒屏幕已经全空,块头后 0.4 秒仍然全空;新版一直是满屏。
- 输出帧率:15fps 源配 mergeFps=60,成片 60fps,1 秒内 60 帧哈希互不相同(不是复制帧);
默认设置下成片仍是 15fps。
|
2026-09-26 16:34:47 +08:00 |
|
tamakyi
|
c55fe3539d
|
口令失败记录加上硬上限,并且不再傻等到满 delay
审查发现这张表的容量可以被外部放大:IP 来自 r.RemoteAddr,是请求方能左右的输入。
1. 原来为什么会被写满
清理逻辑只在 len > 1000 时才触发一次,而且只删「10 分钟没动静」的。
换着来源持续撞口令时,每条记录的 last 都是刚刚更新的,于是「虽然超了
1000 条但一条都不符合删除条件」,表现就是表不停往上涨。
实测:3000 个不同来源各失败一次之后,表里 3000 条,一条没删。
2. 现在怎么收敛
- 加硬上限 authFailMaxEntries(1 万条)。到上限立刻扫一次,
只靠「每 256 条扫一次」的话两个周期之间涌进来的会把表顶破。
- 扫表先删过期项,还多就按「最久没动静」排序淘汰。
- 淘汰时留一成余量(淘汰到九成),这样满表之后还能再收一千来个
新来源才需要排下一次,不会变成「每来一个就排一遍全表」。
- 同一来源的失败次数封顶。延迟早就封顶了(2 秒),数字继续涨没有意义。
3. 延迟等待改成可以被断开
原来在 handler 里直接 time.Sleep(最多 2 秒)。并发撞口令时每个请求各占一个
goroutine 干等两秒,很容易把服务拖住。现在用 timer + r.Context():
客户端一断开立刻返回,不再占着连接。
已验证:
- 新增 5 个用例:
TestAuthFailTableHasHardCap(2 万个新来源撞完,表必须被压在 1 万以内)、
TestAuthFailExpiredEntriesGetSwept(过期项扫干净)、
TestAuthFailSweepsPeriodically(定期扫是自动的,不用调用方记着)、
TestAuthFailCountCapped(同来源次数封顶、延迟封顶)、
TestAwaitAuthDelayStopsOnCancel(上游取消后立刻返回,不能傻等满 2 秒)。
- 反向验证:把淘汰目标临时改成 authFailMaxEntries*1000(等于没上限),
TestAuthFailTableHasHardCap 立刻报「应压在 10000 以内,实际 20000」。
- gofmt / go vet / go build / GOOS=linux go build / go test / go test -race 全过。
|
2026-09-26 03:00:37 +08:00 |
|
tamakyi
|
a2f27974b2
|
web UI 按用途分成六个分区,整体改版并跟随系统深浅色
需求是「界面比较杂乱,做一下分类,UI 也现代化一些」。原来所有设置挤在一张
「设置」卡片里,从保存目录一路排到管理口令,加上通知推送的两个渠道,一屏
拉不到底,找东西全靠翻。
1) 分成六个分区,顶部一条导航切换
- 监控:添加主播 + 主播列表 + 运行日志(默认打开的就是这里)
- 录制:保存目录、录播/弹幕和成片子文件夹、录播格式、默认码率上限、ffmpeg 路径
- 合并:自动合并的三个开关、成片输出目录和合成码率、手动合并
- 通知:推送哪些事件、QQ 机器人渠道、MeoW 渠道
- 文件:磁盘容量 + 文件列表
- 设置:外网访问、管理口令
- 分区只靠 class 切显示,所有元素始终在 DOM 里,所以原来按 id 取值的
那一大堆逻辑一行都不用改;「监控」是默认页,因为看录制状态是最高频的操作
2) 地址栏带上 #分区名
- 用 replaceState 写,不往历史里塞记录,但刷新、收藏、把链接发给别人
都能落到同一个分区;也顺便记住了上次看的是哪一页(localStorage 兜底)
3) 大改样式(都在同一个模板里,没有引入任何外部依赖)
- 颜色、圆角、阴影收进 CSS 变量,卡片改成柔和阴影 + 大圆角,输入框加了
hover 和 focus 光晕,按钮分主次两级
- 勾选框改成开关样式(隐藏原生 input + 兄弟节点画轨道,所以 el.checked
和 :focus 判断照旧能用);主播列表的「弹幕/录播」改成小圆片
- 把原来那个行内的小 #msg 文案换成右下角提示条:成功 2.6 秒、失败 6 秒
自动消失,最多同时留 4 条,报错信息不会再一闪而过
- 顶栏常驻状态摘要(录制中 N / 直播中 N / 空闲),「保存设置」也挪到顶栏,
一直可见——文本框可能在别的分区里,按钮跟着分区走会找不到
- 跟随系统深浅色(prefers-color-scheme),配套改了 color-scheme 元信息;
日志面板在两种模式下都是深色终端风格
- 手机上标题居中、右侧按钮收成两个字(「保存」),分区导航横向滚动;
顶栏用 1fr auto 1fr 的网格,两侧等宽才是真的居中
- 文件列表里的文件名原来被 max-width:200px 截成「2026-09-24 20-00-00 Xsp…」,
文件名本来就是用来认文件的,改成不截断、允许换行
4) 顺手修的两个真问题
- 口令里有中文时,页面彻底用不了:HTTP 请求头只接受 ISO-8859-1,浏览器
遇到非 ASCII 的 X-Auth-Token 会直接抛 TypeError
(Failed to read the 'headers' property…),请求根本发不出去,页面只显示
「无法连接到服务器」,用户既进不去也没法在页面上把口令改回来。
现在页面统一把口令 encodeURIComponent 之后再放进头里,服务端
authTokenCandidates() 把原样和解码后的都作为候选逐个定长比较
(不提前返回,避免响应时间泄露是哪一种对上了),所以中文口令能用,
curl / 脚本按文档原样发中文口令也照样能过
- 删「正在写入」的文件返回 500 而不是 409,见上一个提交
5) 文档
- README 的「使用方法」补分区表、保存规则(开关立即生效 / 文本框点保存)、
顶栏状态和深浅色说明;各处「设置区」改成对应的分区名
- doc/webapi.md 补口令编码那段,说明脚本侧按原样填即可
已验证:新增 webui_test.go 5 个用例(ASCII 口令、含 % 的口令、中文口令的
编码与原样两种写法、Bearer 与 ?token= 三个来源、以及错口令/空口令/口令未配
一律拒绝、候选顺序与非 Bearer 的 Authorization 不算数),全量 39 个用例通过。
真机用 Edge 无头浏览器跑了两轮共 40 项断言,覆盖:自动登录、六个分区逐一点开
(只显示该分区、页签高亮、hash 跟着变)、六个分区 + 深色 + 三个移动端截图、
开关点了立刻落盘并能点回来、顶栏保存写到配置文件、在别的分区保存不会把隐藏
表单的值清空、刷新后仍在原分区、MeoW 红字提示的显示与隐藏、合并中按钮禁用
并改文案、口令弹窗的失败与重试,桌面/深色/移动端三处控制台零报错。
中文口令另做了端到端验证:配置里放中文口令能直接进、换口令后接口继续可用、
重开页面用新口令能进、旧口令被拒;把客户端编码去掉后同一个测试立刻失败,
确认这个修复是必需的而不是想当然。Windows/Linux 双平台构建、go vet、
gofmt 全干净。51880 的上游原版全程未动。
|
2026-09-25 14:19:44 +08:00 |
|