tamakyi
|
8e82aa54c6
|
修第二批 P1:合并收尾、分块上限、确定性失败与 ffmpeg 路径归一
一、合并收尾只删真正进了成片的分段
prepareForMerge 中途有 4 处会回退成「只合并第一个分段」,而收尾仍按原分段列表
全删源视频 —— 后半场没进成片却照样被删掉,等于永久丢数据。现在 prepareForMerge
把「实际参与合并的源文件」一并返回(新增 used),收尾只删这些。
二、拼接中断不再在录播目录留几 GB 中间文件
concat 失败分支原来只有「被停止」那条路径删了 acfunlive_chunk_joined.*,
普通失败会把它留在录播目录,而且带着 chunkTempPrefix,界面文件列表里根本看不见。
三、把「被停止」和「拼接失败」分开
停止任务会杀掉 ffmpeg 让它非 0 退出,原来一律报成「拼接失败」,
卡片上显示的是失败而不是已停止;prepareForMerge 那里也统一改用 errors.Is 判断。
四、单块弹幕条数加硬上限(maxDanmuPerChunkHard = 200)
块尾会把「显示时间跨过块尾」的弹幕一并并入,弹幕密集时单块能膨胀到几百条。
一条弹幕在 -filter_complex 里约占 110 字节,几百条足以顶破 Windows 的
32767 字符命令行上限,表现同样是「合并失败且看不出原因」。到上限就把块尾
收到下一条弹幕出现之前,保证被挤到下一块的弹幕不会因为「整条早于块头」被丢弃。
五、确定性失败不再白重试三次
头像超出体积/尺寸上限这类失败重试多少次结果都一样,原来会白白把十几 MB 重新下三遍。
新增 noRetryError(runThrice 用 errors.As 识别)只标记这两个上限,网络类错误照旧重试。
六、ffmpeg 路径统一归一成绝对路径
PATH 里带「.」这类相对条目、或程序目录探测失败时,getFFmpeg 会返回相对路径。
合并时 ffmpeg 工作目录被改成输出目录,相对路径到那边就找不着了;而且 Windows 上
exec 会直接拒绝执行「相对当前目录」找到的可执行文件,报错还跟路径完全看不出关系。
验证:
- gofmt -l 空;go vet ./... 空;go build ./... 与 GOOS=linux go build 通过
- go test ./... 全绿(10.8s);go test -race ./... 全绿(14.7s)
- 新增 chunks_test.go 10 个用例;6 项修复逐条反向验证(临时退回修复 → 对应测试确实失败)
- 51897 隔离实例上新产物实测:e2e.py 24 项全过;smoke_extra.py 34 项全过
(含并发合并返回 409 并报出在跑的任务名、口令失败后正确口令仍立刻可用、
超大头像被拒不误伤正常头像且只尝试 1 次、停止后无临时文件残留)
|
2026-09-26 09:35:51 +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
|
7dd2487cb8
|
合并任务的可视化进度、预计剩余时间与暂停/停止
用户反馈:合并的进度只体现在日志里(ffmpeg 的 frame=/speed= 行),看不出
百分比、当前环节和还要多久,也没有办法中途暂停或放弃。
1. 新增全局任务状态(progress.go)
一份状态同时服务自动合并和手动合并,记录阶段、总进度、速度、帧率、
已处理时长、输入/预计输出/已写出体积、ETA 和速度折线采样点。
总进度按阶段加权:准备 0.02 / 分段拼接 0.08 / 获取头像 0.10 /
画面渲染 0.75 / 拼接与落盘 0.05,渲染阶段按「已渲染 ÷ 总时长」折算。
ETA 在渲染阶段用「剩余时长 ÷ 倍速」,其它阶段按已用时间线性外推。
2. 用 ffmpeg 的 -progress pipe:1 取结构化进度
runFFmpeg 拆出带回调的版本:解析 out_time_us / speed / fps / total_size,
分块渲染时把块内时间折算到整片时间轴,并把已完成块的字节数累计进来。
ffmpeg 自己的统计行仍在 stderr,单独用命令行时不会少东西。
预计成片体积先按「(视频码率 + 音频 192k)× 时长 ÷ 8」估,渲染过 5%
之后换成「已写出字节 ÷ 已完成比例」外推(estimateAudioKbps)。
3. 暂停 / 停止
Windows 用 ntdll 的 NtSuspendProcess / NtResumeProcess(照任务管理器
的「挂起」语义),Linux 用 SIGSTOP / SIGCONT,放在带 build tag 的
jobctl_windows.go / jobctl_unix.go 里。
非 ffmpeg 环节(下载头像、逐块渲染之间)用 jobGate 闸门生效。
停止 = 杀掉当前 ffmpeg 并置位,各层返回 errJobStopped:不再回退其它
编码器、不落成片、清掉分块临时文件,任务以「已停止」结束(而不是失败)。
4. 接口
GET /api/job 给页面轮询任务快照;POST /api/job/control 接受
pause / resume / stop,没有任务时 409、未知动作 400。
5. 界面
监控分区顶部新增「转换任务」卡片:阶段步骤条、总进度条、实时指标、
预计剩余时间、源片与预计成片的体积对比条、渲染速度折线(每秒一个点、
最多两分钟),以及暂停/继续/停止按钮;页面最顶上再加一条全局细进度条,
切到哪个分区都看得到。有任务时每秒轮询,空闲时 4 秒。任务结束后卡片
再停留 20 秒显示结果。
已验证:
- 新增 progress_test.go 12 个用例:阶段权重和为 1、进度按阶段折算、阶段
不回退、ETA、体积预估与实测外推的切换、折线采样、暂停/停止的闸门语义、
控制接口在无任务时报错、speed 字段与 -progress 文本的解析。
测试里把 errJobStopped 和真失败区分开(jobErrText)。
- 端到端(独立实例 51897 + 独立 config,全程没碰仓库里的实例和
acfunlive.exe):跑通完整合并,进度到 100%、预计体积与成片实际大小
相差 0.4%;暂停后进度冻结且 ffmpeg 的 CPU 时间 5 秒内 0 增长;
继续后进度恢复;停止后任务以「已停止」结束、没有半成品成片、
没有残留临时文件、没有残留 ffmpeg 进程。
- 界面(playwright 驱动系统 Edge):步骤条 5 个阶段且只有一个处于进行中、
进度条与顶部细条同步、5 项指标(速度/帧率/已处理时长/已用时间/已写出)、
ETA 文案、体积对比条两段宽度按比例、折线有采样点、界面按钮能暂停/继续/
停止、结束后按钮隐藏且文案是「已停止」、深色主题与 390 宽移动端无横向溢出。
- 过程中修掉三个真缺陷:停止时残留分块临时文件(part 登记晚于渲染)、
界面「继续」按钮点不动(onclick 写死了 pause)、停止被当成失败报红
(现在按 message 区分 已完成/已停止/失败)。
- gofmt / go vet / go build / GOOS=linux go build / go test 全过。
|
2026-09-26 02:05:47 +08:00 |
|
tamakyi
|
e35a4f7cef
|
合并先在本地做完再落到成片目录;头像支持 gif 和静态 webp
起因是核查合并落盘和头像处理时发现的两处问题:
1) 成片落盘:ffmpeg 原来是直接写目标目录(automerge.go 里那句注释「不再事后再
移动一次」)。成片目录可能是网络盘或外接盘,写到一半失败会在那里留下一个
看起来正常的半成品。现在改成先本地合成、完成后再落盘:
- 输出先写到弹幕文件所在目录(本地)的 acfunlive_chunk_merging_*
- 写完后由 placeOutput 落盘:同盘直接 os.Rename(瞬时),跨盘退回拷贝;
拷贝中途失败留下的是 *.acfunlive.part,一眼看得出不是成品
- 成功或失败都会清掉本地临时文件,失败时两个目录都不留东西
- 临时文件沿用 chunkTempPrefix,不再出现在文件管理的列表里(listFiles 过滤)
- 启动时清理上次残留;删不掉时提示一句 —— 强杀程序不会连带杀掉 ffmpeg
子进程,它可能还在写这个文件,这种情况下次启动会再清一次
2) 头像:downloadAvatar 只注册了 jpeg 和 png 解码器,AcFun 上的 gif 头像必然
解码失败,静态 webp 同样。加上 _ "image/gif"(多帧取第一帧)和
_ "golang.org/x/image/webp"(x/image 本来就是直接依赖,零新增模块)。
动画 webp 两条路都解不开(x/image 报 invalid format,ffmpeg 5.0 也不行),
保持只烧文字。解码失败时错误信息带上 Content-Type,方便排查是哪张图。
在隔离实例上用真 ffmpeg 跑了真合并验证:gif 头像和 webp 头像都进了缓存并烧进
画面(抽帧确认圆形头像);日志里 ffmpeg 的输出是 record 下的临时文件,完成后
才出现在 merged;坏视频合成失败后成片目录没有半成品、录播目录零残留;合并
中途强杀留下的临时文件在下次启动时被清掉。
新增 output_test.go(7 个)和 avatar_test.go(3 个);go test ./...、gofmt、
go vet 干净。
|
2026-09-25 17:34:31 +08:00 |
|
tamakyi
|
ed4e47e305
|
init
|
2026-09-25 13:16:54 +08:00 |
|