Commit Graph
4 Commits
Author SHA1 Message Date
tamakyi a24425e16d 修第三批 P2:除零、停止/暂停的响应与状态一致性、临时文件、带 BOM 的 ASS
一、弹幕滚动时间不再整数除零
   write() 里拿 (PlayResX+length) 做除数,PlayResX 被配成 0 又收到一条空内容弹幕时
   分母正好是 0,直接 panic 终止整场录制。抽成 scrollMarks 并给分母为 0 的兜底
   (退回半个停留时长,至少让它正常显示出来)。

二、点停止不再要等整个块渲完
   jobAttach 原来只看 paused:停止请求若落在 jobGate 之后、ffmpeg 启动过程中,
   job.proc 还没登记,jobStop 拿不到进程可杀,用户得等整个块渲完(大块要几分钟)。
   现在 attach 时发现任务已请求停止就地把它杀掉,上层拿到 errJobStopped,
   卡片显示的是「已停止」而不是「失败」。

三、暂停没挂住就回滚状态
   jobPause 先把 job.paused 置 true 再调 suspendProcess,挂起失败时状态已经改了:
   界面显示「已暂停」而 ffmpeg 还在跑。失败时回滚(只在 proc 仍是同一个进程时回滚)。
   jobResume 同理:恢复不成放回「已暂停」,免得显示在跑却停着。

四、.ass 还原的临时 jsonl 用完就删
   还原结果写不进录播目录时会退到系统临时目录的固定名文件,原来从不清理,
   每失败一次留一份。新增 danmuSource.tempJsonl 标记 + dropTempDanmu 收尾删除;
   只删标记过的临时文件 —— 正常情况下那个路径是用户录播目录里的同名 jsonl,
   删了就是丢数据。

五、带 BOM 的 ASS 能整份解析
   带 UTF-8 BOM 时首行是 BOM+[Script Info],一个节都进不去,最后只报
   「可能不是弹幕文件」,跟真实原因完全对不上。解析时剥掉 BOM。

六、头像下载期间暂停/停止不用再干等
   单次超时 30 秒 × 3 次重试,最坏 96 秒才响应暂停/停止。单次超时收到 10 秒
   (头像只有几百 KB),并且每次重试前过闸门:暂停在这里等、停止直接放弃,
   不再把剩下的重试跑完;停止也不再打「头像不可用」的告警。

验证:
- gofmt -l 空;go vet ./... 空;go build 与 GOOS=linux go build 通过
- go test ./... 全绿(10.9s);go test -race ./... 全绿(14.5s)
- 新增 p2_fixes_test.go 8 个用例;本轮 6 项加上一批 6 项共 12 项反向验证全部通过
  (每项临时退回源码 → 对应测试确实失败 → 恢复)
- 51897 隔离实例上完整复测:e2e.py 37 项全过、smoke_extra.py 33 项全过
2026-09-26 10:07:30 +08:00
tamakyi 9b9b3fa2cd 弹幕写盘出错不再被静默吞掉
审查发现:录制时每写一条弹幕都调用 os.File.Write / fmt.Fprintf,
但返回值一个都没接。磁盘写满、外接盘或网络盘掉线时,写入会失败而程序
毫无察觉 —— 照样打印「录制完成」、照样自动合并,最后拿到的成片缺了一大截
弹幕,界面上看不出是「缺了」还是「本来就没弹幕」。

1. 写入器记住写盘错误
   danmuWriter 增加一个 err 字段:每条弹幕的 ass / nick.ass / jsonl 写入,
   以及文件头的写入,都检查返回值。只保留第一个错误 —— 磁盘满了之后
   每写一条都会失败,全记下来只会把日志刷爆,而第一个就足以说明原因。

2. close() 负责把错误交出来
   三个文件的 Close 错误也要记:os.File 在部分系统上(NFS、某些网络盘)
   是到 Close 才报出「写不进去」,只盯每次 Write 的返回值会漏掉这种情况。
   getDanmu 里拿到错误就明确告警,并把这条记录标记成「弹幕文件写坏了」。

3. 写坏的本场跳过自动合并
   光打一条日志不够 —— 照样会合成一个缺弹幕的成片。liveInfo 增加
   danmuWriteFailed,finishPart 据此跳过合并,录播和弹幕文件都留着,
   用户检查磁盘后还能手动合并。

已验证:
- 新增 4 个用例:
  TestDanmuWriteErrorSurfacedOnClose(写入失败后 failed() 为真、
  close() 必须把错误交出来、错误里能看出是哪个文件)、
  TestDanmuWriteKeepsOnlyFirstError(只记第一个)、
  TestDanmuWriteNoFalseAlarm(正常录制不能误判,这是加检查最容易踩的反向坑)、
  TestFinishPartSkipsMergeWhenDanmuBroken(先跑一个「条件都满足应当合并」
  的对照组确认测试本身有效,再验证标了写坏之后不合并)。
- 反向验证:把 jsonl 的两处 Write 错误改回丢弃,
  TestDanmuWriteErrorSurfacedOnClose 立刻报「failed() 应为 true」。
- 测试踩到的两个坑(都记进注释):替换 w.jsonl 前要先关掉原句柄否则
  Windows 上临时目录删不掉;测试里没跑 initialize(),lInfoMap.info 是 nil,
  直接赋值会 panic。
- gofmt / go vet / go build / GOOS=linux go build / go test / go test -race 全过,
  全量测试连跑两次稳定。
2026-09-26 03:14:07 +08:00
tamakyi cbe5dfa982 弹幕字号按画面短边自动定,竖屏成片不再字大到铺满整行
1. 字号基准从「画面高度」改成「画面短边」
   以前合并时只用高度比换算字号(字号 × 视频高 ÷ 弹幕文件高)。横屏弹幕文件
   (1920x1080、60 号)配竖屏视频(720x1280)时会被放大到 71 号,而画面只有
   720 宽,一条 10 字弹幕就铺满整行。横竖屏互换时高度不是约束文字宽度的维度,
   短边才是:横屏取高、竖屏取宽。

2. 「弹幕字号」填 0 的含义从「跟随弹幕文件」改成「自动按画面分辨率」
   取画布短边的 5.56%(60/1080)。于是 1920x1080 → 60、1280x720 → 40、
   960x540 → 30,横屏行为与以前完全一致;720x1280 → 40(以前 60)、
   640x1280 → 36。填大于 0 仍是精确像素覆盖。

3. 录制端竖屏档位预设 720x1280 的字号从 60 改成 40
   原来横屏三档都是短边的 5.56%,只有竖屏这档是 8.33%,比横屏大 50%。
   改完四档口径一致,新录的弹幕文件和成片也对得上。

4. 坐标系和字号拆开
   unifyDanmuSpace 只负责把 PlayRes 统一到视频分辨率,字号统一交给
   applyFontSize 按最终画布算,避免两处各改一次。

5. .gitignore 加上 /avatar_cache
   头像缓存挪到程序目录后,程序正好跑在仓库里,缓存目录会被 git 看见,
   git add . 会把那 336 个 PNG 一起提交进去。

已验证:
- 新增 TestShortSide / TestAutoFontSize / TestSubConfigsMatchAutoFontSize /
  TestApplyFontSize;TestUnifyDanmuSpace 改成只校验 PlayRes,并且新增断言
  确认它不再动字号。
- 反向验证:把短边规则临时退回「按高度」,TestAutoFontSize 报
  「720x1280 应为 40,实际 71」「1080x1920 应为 60,实际 107」,
  TestApplyFontSize、TestSubConfigsMatchAutoFontSize 同步失败。
- 端到端:造 720x1280 竖屏视频 + 1920x1080/60 号的 jsonl,字号填 0 真跑一次合并,
  日志打出「竖屏画布 720x1280,弹幕字号自动取 40」;同一输入再用旧规则跑一次对照,
  抽同一时刻的帧量墨迹高度:改前 46px、改后 25px(1.84 倍)。
- gofmt / go vet / go build / GOOS=linux go build / go test 全过。
2026-09-26 00:59:17 +08:00
tamakyi ed4e47e305 init 2026-09-25 13:16:54 +08:00