Improve progress reporting and video format coverage

This commit is contained in:
kim
2026-03-30 15:24:46 +08:00
parent 6125a9389c
commit 7e74de3e08
8 changed files with 244 additions and 17 deletions
+48
View File
@@ -1,3 +1,5 @@
import time
from typer.testing import CliRunner
from tgvideo_check.cli import app
@@ -167,6 +169,52 @@ def test_scan_command_reports_discovery_and_stage_progress(tmp_path, monkeypatch
assert "Stage2 progress: 1/1 (100%) passed_stage2=1" in result.stdout
def test_scan_command_reports_stage2_heartbeat_while_waiting(tmp_path, monkeypatch) -> None:
root = tmp_path / "videos"
root.mkdir()
for index in range(60):
(root / f"risky-{index:03d}.mp4").write_bytes(b"risky")
def fake_run_ffprobe(path, **kwargs):
return ProbeExecutionResult(
status="completed",
command=("ffprobe", str(path)),
exit_code=0,
stdout_text='{"format":{"format_name":"mov,mp4,m4a,3gp,3g2,mj2","duration":"20.0"},"streams":[{"codec_type":"video"}]}',
stderr_text="",
elapsed_ms=1,
)
def fake_run_ffmpeg(path, **kwargs):
time.sleep(0.02)
return FfmpegExecutionResult(
status="completed",
command=("ffmpeg", str(path)),
exit_code=0,
stdout_text="frame=10",
stderr_text="",
elapsed_ms=20,
reason_code=None,
)
monkeypatch.setattr("tgvideo_check.cli.PROGRESS_HEARTBEAT_SECONDS", 0.01)
monkeypatch.setattr("tgvideo_check.cli.run_ffprobe", fake_run_ffprobe)
monkeypatch.setattr("tgvideo_check.cli.run_ffmpeg_verification", fake_run_ffmpeg)
result = runner.invoke(
app,
[
"scan",
str(root),
"--db",
str(tmp_path / "scan_state.db"),
],
)
assert result.exit_code == 0
assert "Stage2 heartbeat:" in result.stdout
def test_scan_resume_preserves_reason_code_in_exported_report(tmp_path, monkeypatch) -> None:
root = tmp_path / "videos"
root.mkdir()
+41 -2
View File
@@ -1,4 +1,4 @@
from tgvideo_check.config import ScanConfig
from tgvideo_check.config import DEFAULT_VIDEO_EXTENSIONS, ScanConfig
from tgvideo_check.profiles import get_profile, resolve_config
@@ -12,6 +12,46 @@ def test_tg_hdd_profile_uses_conservative_defaults() -> None:
assert profile.high_risk_extensions == ("mp4", "mov", "m4v", "mkv", "webm")
def test_default_extensions_cover_broad_common_video_formats() -> None:
assert DEFAULT_VIDEO_EXTENSIONS == (
"mp4",
"mov",
"m4v",
"mkv",
"webm",
"avi",
"flv",
"f4v",
"ts",
"m2ts",
"mts",
"mpg",
"mpeg",
"mpe",
"mpv",
"m2v",
"wmv",
"asf",
"3gp",
"3g2",
"ogv",
"ogm",
"vob",
"qt",
"rm",
"rmvb",
"mxf",
"divx",
)
assert ScanConfig(roots=()).extensions == DEFAULT_VIDEO_EXTENSIONS
def test_deep_verify_profile_treats_all_supported_formats_as_high_risk() -> None:
profile = get_profile("deep-verify")
assert profile.high_risk_extensions == DEFAULT_VIDEO_EXTENSIONS
def test_resolve_config_applies_profile_overrides() -> None:
config = resolve_config(
ScanConfig(
@@ -26,4 +66,3 @@ def test_resolve_config_applies_profile_overrides() -> None:
assert config.stage1_workers == 3
assert config.stage2_workers == 1
assert config.extensions == ("mp4", "mkv")
+32
View File
@@ -169,3 +169,35 @@ def test_iter_stage_batch_results_yields_items_as_they_finish() -> None:
results = list(iter_stage_batch_results(batch, worker))
assert [(item.file_id, output) for item, output in results] == [(2, 20), (1, 10)]
def test_iter_stage_batch_results_can_emit_heartbeat_while_waiting() -> None:
batch = build_stage_plan(
stage1_files=[
make_stored_file(file_id=1, discovery_order=0, stage1_status="pending_stage1"),
],
stage2_files=[],
stage1_workers=1,
stage2_workers=1,
queue_limit=64,
)[0]
heartbeats: list[tuple[int, ...]] = []
def worker(item: StoredFile) -> int:
time.sleep(0.03)
return item.file_id
results = list(
iter_stage_batch_results(
batch,
worker,
heartbeat_seconds=0.01,
on_heartbeat=lambda items: heartbeats.append(
tuple(entry.file_id for entry in items)
),
)
)
assert results == [(batch.files[0], 1)]
assert heartbeats
assert heartbeats[0] == (1,)