自动合并按文件实际位置找弹幕,修掉「jsonl 文件不存在」

为什么:

录制结束时 moveFile 已经把 .ass / .nick.ass / .jsonl 搬到保存目录,但合并时只有视频
分段走了 findMovedFile 兜底,弹幕 jsonl 一直拿着录制时算出来的旧路径。于是文件明明
安安稳稳躺在保存目录里,burnDanmu 却去旧位置找,自动合并以「jsonl 文件不存在」告终
并推送一条失败通知。

连带还有两处也指向错误位置:tempMergeOutput 用 filepath.Dir(jsonlFile) 决定中间成片
写到哪个目录,mergeDelDanmu 按它去删那三个弹幕文件。

处理方式是新增 resolveAfterMove(path)——找不着就退回原路径,这样报错还能说清本来想
找哪儿——然后在两处归一:prepareForMerge 让返回的 jsonl 是解析后的路径;autoMergeJob
里也要归一,因为中间成片写到哪、事后删哪几个文件都看这条路径。

上一个提交(录制直接写保存目录)落地后这条会退化成空操作,文件压根不再挪窝;留着它
是为了兜住「运行中改了保存目录」「老录播补合并」这类场景。

验证:

- 新增 automerge_test.go 两条:prepareForMerge 返回的视频与 jsonl 都从录制位置解析到
  保存位置;目标目录里没有同名文件时退回原路径而不是返回空串。
- go test ./... 23.6s 与 go test -race ./... 26.4s 均全绿,215 个顶层用例。
- 反向验证新增一项:删掉 prepareForMerge 里那句归一,对应用例确实变红。
- 验证边界说明:测到的是路径解析这一层,没有构造一整场录制跑真 ffmpeg 走完合并流程
  ——那需要一整套直播素材,成本远高于收益。
This commit is contained in:
tamakyi
2026-09-28 02:04:10 +08:00
parent 2b1705a970
commit 40f4f36aa6
2 changed files with 92 additions and 0 deletions
+16
View File
@@ -85,6 +85,14 @@ func findMovedFile(path string) string {
return ""
}
// 收拢到实际位置:找不着时退回原路径,让后面的报错还能指出「本来想找哪儿」
func resolveAfterMove(path string) string {
if p := findMovedFile(path); p != "" {
return p
}
return path
}
// 录播因为断流可能分成多段:单段直接使用,多段先拼成完整视频并把弹幕时间映射到拼接后的时间轴。
// 返回合并要用的视频、字幕文件和弹幕相对视频起点的时间偏移、**实际参与合并的源文件**,
// 以及清理临时文件的函数。used 是给「合并完删源视频」用的:中途回退成「只合并第一个分段」时,
@@ -92,6 +100,10 @@ func findMovedFile(path string) string {
func prepareForMerge(ffmpegFile string, segs []recordSegment, jsonlFile string, danmuStartNs int64) (string, string, float64, []string, func(), error) {
noop := func() {}
// 弹幕文件同样会被移动过去。以前只有视频分段走了查找,弹幕还拿着录制时的路径,
// 结果就是视频找得到、弹幕找不到,合并以「jsonl 文件不存在」告终
jsonlFile = resolveAfterMove(jsonlFile)
// 录制结束时文件可能已经被移动到保存目录,逐个找实际位置
files := make([]string, 0, len(segs))
starts := make([]int64, 0, len(segs))
@@ -288,6 +300,10 @@ func autoMergeOutFile(jsonlFile string) string {
func autoMergeJob(uid int, segs []recordSegment, jsonlFile string, danmuStartNs int64) error {
lPrintln("开始自动合并" + longID(uid) + "的录播和弹幕层")
// 录制结束时弹幕文件可能已被移动到保存目录,先把路径收拢到实际位置:
// 中间成片写到哪、事后删哪几个弹幕文件都按这一条路径来,用旧路径会指向错误的地方
jsonlFile = resolveAfterMove(jsonlFile)
// 视频和弹幕是一次录制产生的,文件名去掉扩展名后应该一致(扩展名由 recordFormat 决定)
if len(segs) > 0 {
videoBase := filepath.Base(strings.TrimSuffix(segs[0].file, filepath.Ext(segs[0].file)))