From 6f0a8253f6bdb4ebf3a1a8714ba3d6eadc801f62 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Mon, 13 Jul 2026 21:06:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(P1-1/P1-2):=20processor.py=20=E8=A1=A5=20lo?= =?UTF-8?q?gger=20=E5=AE=9A=E4=B9=89=20+=20=E6=B5=8B=E8=AF=95=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=AF=BC=E5=85=A5=E8=B7=AF=E5=BE=84=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=88=B0=E6=8B=86=E5=88=86=E5=90=8E=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1-1: processor.py 使用了 logger 但未定义,补 import logging + getLogger P1-2: test_unified_render_service.py 导入路径同步: - 音频函数 (mix_audio/merge_audio_video/clip_has_audio/RenderContext) → render_audio - 字幕函数 (_hex_to_ass_color/_position_to_ass_alignment/generate_ass_subtitles) → render_subtitles - patch.object(svc, '_mix_audio') → patch('video_processing.unified_render_service.mix_audio') - patch.object(svc, '_merge_audio_video') → patch('video_processing.unified_render_service.merge_audio_video') - probe_has_audio/run_ffmpeg patch 路径对齐到 render_audio 模块 全部 84 个测试通过。 --- apps/worker/video_processing/processor.py | 3 + tests/unit/test_unified_render_service.py | 118 +++++++++++++--------- 2 files changed, 75 insertions(+), 46 deletions(-) diff --git a/apps/worker/video_processing/processor.py b/apps/worker/video_processing/processor.py index 8b2257280..5b2e3e7ca 100644 --- a/apps/worker/video_processing/processor.py +++ b/apps/worker/video_processing/processor.py @@ -2,6 +2,7 @@ 视频处理核心类 """ +import logging import os import tempfile from dataclasses import dataclass @@ -9,6 +10,8 @@ from typing import List import ffmpeg +logger = logging.getLogger(__name__) + @dataclass class VideoResult: diff --git a/tests/unit/test_unified_render_service.py b/tests/unit/test_unified_render_service.py index b3223fa13..a9dc6d5a2 100755 --- a/tests/unit/test_unified_render_service.py +++ b/tests/unit/test_unified_render_service.py @@ -11,14 +11,22 @@ from typing import Any from unittest.mock import MagicMock, patch import pytest +from video_processing.render_audio import ( + RenderContext, + clip_has_audio, + merge_audio_video, + mix_audio, +) +from video_processing.render_subtitles import ( + _hex_to_ass_color, + _position_to_ass_alignment, + generate_ass_subtitles, +) from video_processing.unified_render_service import ( RenderResult, ResolvedClip, UnifiedRenderService, - _hex_to_ass_color, - _position_to_ass_alignment, _resolve_layer_role, - generate_ass_subtitles, ) # ── Fixtures ────────────────────────────────────────────────────────────────── @@ -101,6 +109,11 @@ def _patch_path_exists(): return patch("pathlib.Path.exists", return_value=True) +def _make_ctx() -> RenderContext: + """创建测试用 RenderContext。""" + return RenderContext(work_dir=Path("/tmp/test_render"), plan_id="plan_001") + + # ── 测试 _resolve_layer_role ───────────────────────────────────────────────── @@ -572,7 +585,7 @@ class TestPassThrough: patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_render_pass_through") as mock_pass, patch.object(svc, "_execute_ffmpeg") as mock_exec, - patch.object(svc, "_mix_audio", return_value=None), + patch("video_processing.unified_render_service.mix_audio", return_value=None), patch("shutil.copy2"), patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)), ): @@ -599,7 +612,7 @@ class TestPassThrough: patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_render_pass_through") as mock_pass, patch.object(svc, "_execute_ffmpeg") as mock_exec, - patch.object(svc, "_mix_audio", return_value=None), + patch("video_processing.unified_render_service.mix_audio", return_value=None), patch("shutil.copy2"), patch.object(svc, "_probe_output", return_value=(5.5, 2048, 1280, 720)), ): @@ -819,7 +832,7 @@ class TestRender: _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_render_pass_through") as mock_pass, - patch.object(svc, "_mix_audio", return_value=None), + patch("video_processing.unified_render_service.mix_audio", return_value=None), patch("shutil.copy2"), patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)), ): @@ -848,7 +861,7 @@ class TestRender: _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_execute_ffmpeg") as mock_exec, - patch.object(svc, "_mix_audio", return_value=None), + patch("video_processing.unified_render_service.mix_audio", return_value=None), patch("shutil.copy2"), patch.object(svc, "_probe_output", return_value=(5.5, 2048, 1280, 720)), ): @@ -929,10 +942,11 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None assert result.name == "audio_plan_001.aac" @@ -957,10 +971,11 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 4.5) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 4.5) assert result is not None mock_run.assert_called_once() @@ -991,10 +1006,11 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None mock_run.assert_called_once() @@ -1018,7 +1034,8 @@ class TestAudioMixing: # 没有素材的clip会被跳过,layers为空 resolved = svc._resolve_clips() layers = svc._group_clips_into_layers(resolved) - result = svc._mix_audio(layers, 3.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 3.0) assert result is None @@ -1037,10 +1054,11 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None mock_run.assert_called_once() @@ -1066,10 +1084,11 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None mock_run.assert_called_once() @@ -1088,10 +1107,11 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None mock_run.assert_called_once() @@ -1108,11 +1128,12 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=10.0), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) + ctx = _make_ctx() # video_duration 只有 3.0,小于 clip 的 10.0 - result = svc._mix_audio(layers, 3.0) + result = mix_audio(ctx, layers, 3.0) assert result is not None mock_run.assert_called_once() @@ -1125,13 +1146,13 @@ class TestAudioMixing: def test_merge_audio_video(self): """合并音视频命令正确。""" - svc = _make_service([], {}) video_path = Path("/tmp/video.mp4") audio_path = Path("/tmp/audio.aac") output_path = Path("/tmp/output.mp4") + ctx = _make_ctx() - with patch("video_processing.unified_render_service.run_ffmpeg") as mock_run: - svc._merge_audio_video(video_path, audio_path, output_path) + with patch("video_processing.render_audio.run_ffmpeg") as mock_run: + merge_audio_video(ctx, video_path, audio_path, output_path) mock_run.assert_called_once() cmd = mock_run.call_args[0][0] @@ -1156,8 +1177,8 @@ class TestAudioMixing: _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_execute_ffmpeg"), - patch.object(svc, "_mix_audio", return_value=Path("/tmp/audio.aac")) as mock_mix, - patch.object(svc, "_merge_audio_video") as mock_merge, + patch("video_processing.unified_render_service.mix_audio", return_value=Path("/tmp/audio.aac")) as mock_mix, + patch("video_processing.unified_render_service.merge_audio_video") as mock_merge, patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)), ): result = svc.render() @@ -1182,7 +1203,7 @@ class TestAudioMixing: _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_execute_ffmpeg"), - patch.object(svc, "_mix_audio", return_value=None), + patch("video_processing.unified_render_service.mix_audio", return_value=None), patch("shutil.copy2") as mock_copy, patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)), ): @@ -1201,8 +1222,8 @@ class TestAudioMixing: _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), patch.object(svc, "_render_pass_through", return_value=True) as mock_pt, - patch.object(svc, "_mix_audio") as mock_mix, - patch.object(svc, "_merge_audio_video") as mock_merge, + patch("video_processing.unified_render_service.mix_audio") as mock_mix, + patch("video_processing.unified_render_service.merge_audio_video") as mock_merge, patch("shutil.copy2") as mock_copy, patch.object(svc, "_probe_output", return_value=(5.0, 1024, 1280, 720)), ): @@ -1266,11 +1287,12 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.ffmpeg_utils.probe_has_audio", return_value=False), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.probe_has_audio", return_value=False), + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is None # 没有音频流时不应调用 FFmpeg @@ -1295,11 +1317,12 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.ffmpeg_utils.probe_has_audio", side_effect=fake_has_audio), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.probe_has_audio", side_effect=fake_has_audio), + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None mock_run.assert_called_once() @@ -1333,11 +1356,12 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.ffmpeg_utils.probe_has_audio", side_effect=fake_has_audio), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.probe_has_audio", side_effect=fake_has_audio), + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is not None mock_run.assert_called_once() @@ -1367,11 +1391,12 @@ class TestAudioMixing: with ( _patch_path_exists(), patch("video_processing.unified_render_service.probe_duration", return_value=5.0), - patch("video_processing.ffmpeg_utils.probe_has_audio", return_value=False), - patch("video_processing.unified_render_service.run_ffmpeg") as mock_run, + patch("video_processing.render_audio.probe_has_audio", return_value=False), + patch("video_processing.render_audio.run_ffmpeg") as mock_run, ): layers = svc._group_clips_into_layers(svc._resolve_clips()) - result = svc._mix_audio(layers, 5.0) + ctx = _make_ctx() + result = mix_audio(ctx, layers, 5.0) assert result is None mock_run.assert_not_called() @@ -1389,11 +1414,12 @@ class TestAudioMixing: resolved = svc._resolve_clips() clip = resolved[0] - with patch("video_processing.ffmpeg_utils.probe_has_audio", return_value=True) as mock_probe: + with patch("video_processing.render_audio.probe_has_audio", return_value=True) as mock_probe: # 调用 3 次 - r1 = svc._clip_has_audio(clip) - r2 = svc._clip_has_audio(clip) - r3 = svc._clip_has_audio(clip) + ctx = _make_ctx() + r1 = clip_has_audio(ctx, clip) + r2 = clip_has_audio(ctx, clip) + r3 = clip_has_audio(ctx, clip) assert r1 is True and r2 is True and r3 is True # 实际只探测了 1 次