From 2f76509a5b550ddb47121d02f786f5c8dc7c6e2b Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Mon, 24 Aug 2026 00:10:07 +0800 Subject: [PATCH] fix: address AI code review - probe defaults to no audio, safe af filter - render_audio.py: simple mode builds af_parts list then joins into single -af (defensive against future multi-clip) - unified_render_service.py: probe_has_audio exception now defaults to False with warning log (was unsafe True) - test: add test_pass_through_probe_exception_defaults_to_no_audio --- apps/worker/video_processing/render_audio.py | 6 +++++- .../unified_render_service.py | 5 +++-- tests/unit/test_original_audio_retained.py | 21 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/worker/video_processing/render_audio.py b/apps/worker/video_processing/render_audio.py index 6d6f1e514..9de62265f 100755 --- a/apps/worker/video_processing/render_audio.py +++ b/apps/worker/video_processing/render_audio.py @@ -294,9 +294,13 @@ def concat_main_audio( "-ac", "2", ] + # 单 clip 简单模式:将所有音频滤镜合并为单个 -af 参数(避免多个 -af 冲突) + af_parts: list[str] = [] vol = _clip_volume(clip) if abs(vol - 1.0) >= 1e-6: - command.extend(["-af", f"volume={vol:.4f}"]) + af_parts.append(f"volume={vol:.4f}") + if af_parts: + command.extend(["-af", ",".join(af_parts)]) if trim_start > 0: command.extend(["-ss", f"{trim_start:.3f}"]) if final_duration > 0: diff --git a/apps/worker/video_processing/unified_render_service.py b/apps/worker/video_processing/unified_render_service.py index 6ca6e8d04..313b30e02 100755 --- a/apps/worker/video_processing/unified_render_service.py +++ b/apps/worker/video_processing/unified_render_service.py @@ -1282,8 +1282,9 @@ class UnifiedRenderService: if role != "background" and clip_volume > 0: try: has_audio = probe_has_audio(clip.local_path) - except Exception: - has_audio = True + except Exception as e: + logger.warning("[unified-render] 探测音频流失败,假设无音频: %s: %s", clip.local_path, e) + has_audio = False else: has_audio = False if has_audio: diff --git a/tests/unit/test_original_audio_retained.py b/tests/unit/test_original_audio_retained.py index fd892ae2e..91abd1a09 100644 --- a/tests/unit/test_original_audio_retained.py +++ b/tests/unit/test_original_audio_retained.py @@ -191,3 +191,24 @@ class TestPassThroughAudioProbe: def test_clip_volume_static_helper(self): assert UnifiedRenderService._clip_volume(_clip("c1")) == 1.0 assert UnifiedRenderService._clip_volume(_clip("c2", config={"volume": 0.7})) == pytest.approx(0.7) + + def test_pass_through_probe_exception_defaults_to_no_audio(self): + """probe_has_audio 抛异常时必须默认无音频,不能盲目映射不存在的音轨。""" + clips = [_clip("c1")] + svc = _service(clips) + with ( + patch("pathlib.Path.exists", return_value=True), + patch("video_processing.unified_render_service.probe_duration", return_value=5.0), + patch( + "video_processing.unified_render_service.probe_has_audio", + side_effect=RuntimeError("probe failed"), + ) as mock_probe, + patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + ): + layers = svc._group_clips_into_layers(clips) + has_audio = svc._render_pass_through(layers, Path("/tmp/out.mp4"), video_duration=5.0) + + assert has_audio is False + mock_probe.assert_called() + cmd_str = " ".join(mock_run.call_args[0][0]) + assert "aac" not in cmd_str