fix: 批量进度只统计无字幕视频,SKIPPED 不计入 total/done
用户反馈数量展示错误:整批 431 个视频中 383 个已有字幕被 SKIPPED, 此前 total=全部视频、done 也含 SKIPPED,把已处理过的视频混进计数。 新口径(2026-09): - total = 创建时扫描出的无字幕(需处理)视频数,创建即固定;SKIPPED 不计 - done = 实际处理完成(COMPLETED)数,SKIPPED 不计;failed 单独计 - 全部视频都有字幕(total=0)→ 创建即视为 COMPLETED,不排队空跑 - sync_batch_job_progress 同步修正 total(老任务读取时自动收敛到新口径) - 前端 batchProgress 相应更新:total=0 显示"无待处理视频" tests: 更新 4 条旧口径断言 + 新增 1 条全跳过 API 测试,62 项相关测试通过。
This commit is contained in:
+58
-13
@@ -318,14 +318,19 @@ def test_create_job_skips_videos_with_existing_subtitles(tmp_path) -> None:
|
||||
|
||||
|
||||
def test_create_job_all_videos_skipped_still_created(tmp_path) -> None:
|
||||
"""文件夹里全部视频都已有字幕时任务仍可创建(全部 SKIPPED,不再处理)。"""
|
||||
"""文件夹里全部视频都已有字幕时任务直接视为完成(无字幕视频数为 0)。
|
||||
|
||||
新口径(2026-09):total = 扫描出的无字幕视频数,全被 SKIPPED 时
|
||||
total=0 且没有可处理项,创建即置 COMPLETED,不排队空跑。
|
||||
"""
|
||||
db = _db(tmp_path)
|
||||
_seed_echo_workflow(db)
|
||||
folder = _video_folder(tmp_path, names=("a.mp4", "b.mp4"))
|
||||
(folder / "a.CN.srt").write_text("x", encoding="utf-8")
|
||||
(folder / "b.CN_dual_eye.ass").write_text("x", encoding="utf-8")
|
||||
job = _make_job(db, folder)
|
||||
assert job["status"] == "QUEUED"
|
||||
assert job["status"] == "COMPLETED"
|
||||
assert job["total"] == 0 and job["done"] == 0
|
||||
assert all(v["status"] == "SKIPPED" for v in db.list_batch_videos(job["id"]))
|
||||
|
||||
|
||||
@@ -364,31 +369,39 @@ def test_batch_worker_processes_all_videos_and_cleans_up(tmp_path) -> None:
|
||||
|
||||
|
||||
def test_batch_worker_skips_videos_with_existing_subtitles(tmp_path) -> None:
|
||||
"""已有字幕的视频在创建时记 SKIPPED,引擎运行时不再为它触发流水线。"""
|
||||
"""已有字幕的视频在创建时记 SKIPPED,不进入 total/done,只统计真正处理的。
|
||||
|
||||
新口径(2026-09):total=无字幕视频数(不含 SKIPPED),done=实际完成数。
|
||||
"""
|
||||
db = _db(tmp_path)
|
||||
_seed_echo_workflow(db)
|
||||
folder = _video_folder(tmp_path, names=("a.mp4", "b.mp4"))
|
||||
(folder / "a.CN_dual_eye.ass").write_text("已处理", encoding="utf-8")
|
||||
job = _make_job(db, folder)
|
||||
# 创建时 total 已固定为无字幕数(b 一个),SKIPPED 不计入。
|
||||
assert db.get_batch_job(job["id"])["total"] == 1
|
||||
BatchWorker(db, interval_seconds=0.05)._process_job(job)
|
||||
videos = {Path(v["video_path"]).name: v for v in db.list_batch_videos(job["id"])}
|
||||
assert videos["a.mp4"]["status"] == "SKIPPED"
|
||||
assert videos["a.mp4"]["run_id"] is None
|
||||
assert videos["b.mp4"]["status"] == "COMPLETED"
|
||||
assert db.get_batch_job(job["id"])["done"] == 2
|
||||
job = db.get_batch_job(job["id"])
|
||||
# done 只计实际完成的 b(1 个),不再把 SKIPPED 的 a 计入。
|
||||
assert job["done"] == 1 and job["total"] == 1
|
||||
|
||||
|
||||
def test_batch_worker_all_skipped_job_completes(tmp_path) -> None:
|
||||
"""任务里全部视频都是 SKIPPED 时引擎正常完成,不创建任何 run。"""
|
||||
"""全部视频都有字幕时创建即视为完成(total=0),不创建任何 run。"""
|
||||
db = _db(tmp_path)
|
||||
_seed_echo_workflow(db)
|
||||
folder = _video_folder(tmp_path, names=("a.mp4", "b.mp4"))
|
||||
(folder / "a.srt").write_text("x", encoding="utf-8")
|
||||
(folder / "b.CN.srt").write_text("x", encoding="utf-8")
|
||||
job = _make_job(db, folder)
|
||||
BatchWorker(db, interval_seconds=0.05)._process_job(job)
|
||||
# 新口径:无字幕视频数为 0 → 创建即 COMPLETED,引擎不需要再跑。
|
||||
assert db.get_batch_job(job["id"])["status"] == "COMPLETED"
|
||||
assert db.get_batch_job(job["id"])["done"] == 2
|
||||
assert db.get_batch_job(job["id"])["total"] == 0
|
||||
assert db.get_batch_job(job["id"])["done"] == 0
|
||||
assert db.list_runs() == []
|
||||
|
||||
|
||||
@@ -917,7 +930,10 @@ def test_batch_progress_sync_recounts_done_failed(tmp_path) -> None:
|
||||
|
||||
|
||||
def test_batch_progress_sync_counts_skipped(tmp_path) -> None:
|
||||
"""SKIPPED 视频同样计入 done(它们不需要处理,属于已完成的工作量)。"""
|
||||
"""SKIPPED 视频不计入 total 也不计入 done(它不需要本批处理)。
|
||||
|
||||
新口径(2026-09):进度只反映"本批无字幕待处理"的视频。
|
||||
"""
|
||||
db = _db(tmp_path)
|
||||
_seed_echo_workflow(db)
|
||||
folder = _video_folder(tmp_path, names=("a.mp4",))
|
||||
@@ -925,7 +941,9 @@ def test_batch_progress_sync_counts_skipped(tmp_path) -> None:
|
||||
# 把 a 改为 SKIPPED(等同创建时旁挂字幕被跳过的语义)。
|
||||
db.update_batch_video(db.list_batch_videos(job["id"])[0]["id"], status="SKIPPED", updated_at=_now_iso())
|
||||
db.sync_batch_job_progress(job["id"])
|
||||
assert db.get_batch_job(job["id"])["done"] == 1
|
||||
job = db.get_batch_job(job["id"])
|
||||
assert job["done"] == 0
|
||||
assert job["total"] == 0
|
||||
|
||||
|
||||
def test_batch_progress_sync_returns_refreshed_job(tmp_path) -> None:
|
||||
@@ -979,22 +997,29 @@ def test_batch_worker_paused_job_reports_real_done(tmp_path, monkeypatch) -> Non
|
||||
|
||||
|
||||
def test_batch_worker_skipped_continue_syncs_done(tmp_path) -> None:
|
||||
"""循环里遇到 SKIPPED/COMPLETED 的 continue 分支也会把汇总实时对齐。"""
|
||||
"""循环里遇到 SKIPPED/COMPLETED 的 continue 分支不把它们计入 done。
|
||||
|
||||
新口径:total=无字幕视频数(本例 a 有字幕 SKIPPED → total=1 即 b),
|
||||
done 只计实际完成(b=COMPLETED → done=1)。SKIPPED 不占分母也不占分子。
|
||||
"""
|
||||
db = _db(tmp_path)
|
||||
_seed_echo_workflow(db)
|
||||
folder = _video_folder(tmp_path, names=("a.mp4", "b.mp4"))
|
||||
(folder / "a.CN.srt").write_text("x", encoding="utf-8") # a 创建即 SKIPPED
|
||||
job = _make_job(db, folder)
|
||||
job_id = job["id"]
|
||||
# b 也手工置为 COMPLETED(引擎会因 continue 分支跳过它)。
|
||||
# 创建即固定:total=无字幕数(仅 b=1)。
|
||||
assert db.get_batch_job(job_id)["total"] == 1
|
||||
# b 手工置为 COMPLETED(模拟此前已完成);a 在循环里走 continue 分支。
|
||||
db.update_batch_video(
|
||||
[v for v in db.list_batch_videos(job_id) if v["video_path"].endswith("b.mp4")][0]["id"],
|
||||
status="COMPLETED", updated_at=_now_iso(),
|
||||
)
|
||||
# 任务先置 PAUSED:首轮循环在 a(SKIPPED)即停下,若 continue 前未对齐则 done=0。
|
||||
db.update_batch_job(job_id, status="PAUSED", updated_at=_now_iso())
|
||||
BatchWorker(db, interval_seconds=0.05)._run_job(job_id)
|
||||
assert db.get_batch_job(job_id)["done"] == 2
|
||||
job = db.get_batch_job(job_id)
|
||||
# done 只计实际完成的 b(1),SKIPPED 的 a 不计;total 保持 1。
|
||||
assert job["done"] == 1 and job["total"] == 1
|
||||
|
||||
def test_batch_worker_ghost_job_id_returns(tmp_path) -> None:
|
||||
"""_run_job 在任务不存在时直接返回(幽灵任务处理无副作用)。"""
|
||||
@@ -1059,6 +1084,8 @@ def test_batch_api_create_list_detail(tmp_path) -> None:
|
||||
detail = client.get(f"/api/batch/jobs/{job['id']}").json()
|
||||
assert detail["workflow_id"] == "echo-app"
|
||||
assert len(detail["videos"]) == 2
|
||||
# 新口径:total = 无字幕需处理数(b 一个),SKIPPED 的 a 不占分母。
|
||||
assert detail["total"] == 1 and detail["done"] == 0
|
||||
|
||||
missing = client.get("/api/batch/jobs/ghost")
|
||||
assert missing.status_code == 404
|
||||
@@ -1066,6 +1093,24 @@ def test_batch_api_create_list_detail(tmp_path) -> None:
|
||||
client.__exit__(None, None, None)
|
||||
|
||||
|
||||
|
||||
|
||||
def test_batch_api_all_skipped_created_completed(tmp_path) -> None:
|
||||
"""全部视频已有字幕时,创建响应直接为 COMPLETED,total=0。"""
|
||||
client, folder = _client_with_echo_workflow(tmp_path)
|
||||
try:
|
||||
# 两个视频都已有旁挂字幕 → 无任何无字幕项。
|
||||
(folder / "a.CN.srt").write_text("x", encoding="utf-8")
|
||||
(folder / "b.CN_dual_eye.ass").write_text("x", encoding="utf-8")
|
||||
job = client.post(
|
||||
"/api/batch/jobs",
|
||||
json={"folder": str(folder), "workflow_id": "echo-app"},
|
||||
).json()
|
||||
assert job["status"] == "COMPLETED"
|
||||
assert job["total"] == 0 and job["done"] == 0
|
||||
assert all(v["status"] == "SKIPPED" for v in job["videos"])
|
||||
finally:
|
||||
client.__exit__(None, None, None)
|
||||
def test_batch_api_creation_errors(tmp_path) -> None:
|
||||
"""批量 API 拒绝:文件夹不存在、无视频、未发布工作流。"""
|
||||
client, folder = _client_with_echo_workflow(tmp_path)
|
||||
|
||||
Reference in New Issue
Block a user