diff --git a/apps/worker/video_processing/chroma_key_engine.py b/apps/worker/video_processing/chroma_key_engine.py index 955c8da5f..5176f95f4 100755 --- a/apps/worker/video_processing/chroma_key_engine.py +++ b/apps/worker/video_processing/chroma_key_engine.py @@ -186,13 +186,7 @@ class ChromaKeyEngine: # 同时稍微提升红和蓝来补偿色偏 r_gain = 1.0 + spill * 0.15 b_gain = 1.0 + spill * 0.15 - parts.append( - f"colorchannelmixer=" - f"rr={r_gain}:" - f"gg={g_gain}:" - f"bb={b_gain}:" - f"aa=1" - ) + parts.append(f"colorchannelmixer=" f"rr={r_gain}:" f"gg={g_gain}:" f"bb={b_gain}:" f"aa=1") filter_str = f"{input_label}{','.join(parts)}{output_label}" return filter_str @@ -217,11 +211,7 @@ class ChromaKeyEngine: similarity = self.config.similarity blend = self.config.blend - return ( - f"{input_label}" - f"chromakey=color={color}:similarity={similarity}:blend={blend}" - f"{output_label}" - ) + return f"{input_label}" f"chromakey=color={color}:similarity={similarity}:blend={blend}" f"{output_label}" def apply_chroma_key_if_needed( diff --git a/apps/worker/video_processing/noise_reduction_engine.py b/apps/worker/video_processing/noise_reduction_engine.py index 04fc1f83b..d1d5e053f 100755 --- a/apps/worker/video_processing/noise_reduction_engine.py +++ b/apps/worker/video_processing/noise_reduction_engine.py @@ -29,9 +29,9 @@ logger = logging.getLogger(__name__) class NoiseReductionLevel(str, Enum): """降噪等级预设。""" - LOW = "low" # 轻度降噪,保留细节,适合轻微背景噪音 + LOW = "low" # 轻度降噪,保留细节,适合轻微背景噪音 MEDIUM = "medium" # 中度降噪,平衡效果和音质 - HIGH = "high" # 高度降噪,适合嘈杂环境,可能轻微影响音质 + HIGH = "high" # 高度降噪,适合嘈杂环境,可能轻微影响音质 CUSTOM = "custom" # 自定义参数 @@ -39,9 +39,9 @@ class NoiseReductionLevel(str, Enum): # 值越大(越接近 0),降噪越强;值越小(越负),降噪越弱 _LEVEL_PARAMS = { NoiseReductionLevel.LOW: { - "nf": -35, # 噪音阈值(dB),越负越保守 - "tn": -10, # 噪音频谱平滑度 - "tr": 50, # 时间分辨率(ms) + "nf": -35, # 噪音阈值(dB),越负越保守 + "tn": -10, # 噪音频谱平滑度 + "tr": 50, # 时间分辨率(ms) }, NoiseReductionLevel.MEDIUM: { "nf": -25, diff --git a/apps/worker/video_processing/render_audio.py b/apps/worker/video_processing/render_audio.py index 6cba224bb..3e89e78f6 100755 --- a/apps/worker/video_processing/render_audio.py +++ b/apps/worker/video_processing/render_audio.py @@ -152,7 +152,7 @@ def _apply_noise_reduction_if_needed(ctx: RenderContext, audio_path: Path) -> Pa engine = NoiseReductionEngine(config) filter_str = engine.build_filter("[0:a]", "[out]") # 提取滤镜部分(不带标签) - filter_part = filter_str[len("[0:a]"):-len("[out]")] + filter_part = filter_str[len("[0:a]") : -len("[out]")] nr_output_path = audio_path.with_name(f"{audio_path.stem}_nr.aac") command = [ diff --git a/apps/worker/video_processing/unified_render_service.py b/apps/worker/video_processing/unified_render_service.py index eb43ec418..895d8718b 100755 --- a/apps/worker/video_processing/unified_render_service.py +++ b/apps/worker/video_processing/unified_render_service.py @@ -28,6 +28,7 @@ from dataclasses import dataclass, field from pathlib import Path from typing import Any +from video_processing.chroma_key_engine import apply_chroma_key_if_needed from video_processing.color_grade_engine import ColorGradeConfig, ColorGradeEngine from video_processing.ffmpeg_utils import ( DEFAULT_FPS, @@ -47,7 +48,6 @@ from video_processing.subtitle_generator import generate_ass_from_timeline from video_processing.tts_engine import TtsEngine from packages.domain.tts_config import TtsConfig -from video_processing.chroma_key_engine import apply_chroma_key_if_needed logger = logging.getLogger(__name__) @@ -856,11 +856,12 @@ class UnifiedRenderService: # chroma key 绿幕抠像 try: from video_processing.chroma_key_engine import ChromaKeyConfig, ChromaKeyEngine + ck_config = ChromaKeyConfig.from_dict(clip.config.get("chroma_key")) if ck_config.has_effect(): ck_engine = ChromaKeyEngine(ck_config) ck_full = ck_engine.build_filter("[in]", "[out]") - ck_filter_part = ck_full[len("[in]"):-len("[out]")] + ck_filter_part = ck_full[len("[in]") : -len("[out]")] filters.append(ck_filter_part) except Exception as e: logger.warning("[unified-render] chroma key 直通模式应用失败,跳过: %s", e) @@ -908,12 +909,13 @@ class UnifiedRenderService: af_parts: list[str] = [] try: from video_processing.noise_reduction_engine import NoiseReductionConfig, NoiseReductionEngine + plan_config = getattr(self.plan, "config", {}) or {} nr_config = NoiseReductionConfig.from_dict(plan_config.get("audio_noise_reduction")) if nr_config.has_effect(): nr_engine = NoiseReductionEngine(nr_config) nr_full = nr_engine.build_filter("[in]", "[out]") - nr_filter_part = nr_full[len("[in]"):-len("[out]")] + nr_filter_part = nr_full[len("[in]") : -len("[out]")] af_parts.append(nr_filter_part) except Exception as e: logger.warning("[unified-render] 直通模式音频降噪应用失败,跳过: %s", e) @@ -1104,12 +1106,13 @@ class UnifiedRenderService: # chroma key 绿幕抠像(在 scale 之后,fps 之前) try: from video_processing.chroma_key_engine import ChromaKeyConfig, ChromaKeyEngine + ck_config = ChromaKeyConfig.from_dict(clip.config.get("chroma_key")) if ck_config.has_effect(): ck_engine = ChromaKeyEngine(ck_config) # 提取滤镜部分(不带输入输出标签) ck_full = ck_engine.build_filter("[in]", "[out]") - ck_filter_part = ck_full[len("[in]"):-len("[out]")] + ck_filter_part = ck_full[len("[in]") : -len("[out]")] filters.append(ck_filter_part) except Exception as e: logger.warning("[unified-render] chroma key 应用失败,跳过 clip=%s: %s", clip.clip_id, e) diff --git a/tests/unit/test_chroma_key_and_noise_reduction.py b/tests/unit/test_chroma_key_and_noise_reduction.py index a4c645b6f..85454bd10 100755 --- a/tests/unit/test_chroma_key_and_noise_reduction.py +++ b/tests/unit/test_chroma_key_and_noise_reduction.py @@ -3,7 +3,6 @@ from __future__ import annotations import pytest - from video_processing.chroma_key_engine import ( CHROMA_KEY_PRESETS, ChromaKeyConfig, @@ -17,7 +16,6 @@ from video_processing.noise_reduction_engine import ( apply_noise_reduction_if_needed, ) - # ═══════════════════════════════════════════════════════════════ # ChromaKeyConfig 测试 # ═══════════════════════════════════════════════════════════════ @@ -156,9 +154,7 @@ class TestChromaKeyEngine: def test_build_filter_with_spill_suppress(self): """溢色抑制时增加 colorchannelmixer.""" - config = ChromaKeyConfig( - enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1, spill_suppress=0.5 - ) + config = ChromaKeyConfig(enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1, spill_suppress=0.5) engine = ChromaKeyEngine(config) result = engine.build_filter("[v0]", "[v1]") assert "colorkey" in result @@ -168,9 +164,7 @@ class TestChromaKeyEngine: def test_build_filter_no_spill_suppress(self): """无溢色抑制时不含 colorchannelmixer.""" - config = ChromaKeyConfig( - enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1, spill_suppress=0.0 - ) + config = ChromaKeyConfig(enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1, spill_suppress=0.0) engine = ChromaKeyEngine(config) result = engine.build_filter("[v0]", "[v1]") assert "colorkey" in result @@ -238,9 +232,7 @@ class TestApplyChromaKeyIfNeeded: def test_disabled_chroma_key(self): """禁用的抠像配置返回 None.""" - result = apply_chroma_key_if_needed( - {"chroma_key": {"enabled": False}}, "[in]", "[out]" - ) + result = apply_chroma_key_if_needed({"chroma_key": {"enabled": False}}, "[in]", "[out]") assert result is None def test_enabled_chroma_key(self): @@ -320,9 +312,7 @@ class TestNoiseReductionConfig: def test_from_dict_custom_level(self): """自定义降噪等级.""" - config = NoiseReductionConfig.from_dict( - {"enabled": True, "level": "custom", "noise_floor": -30.0} - ) + config = NoiseReductionConfig.from_dict({"enabled": True, "level": "custom", "noise_floor": -30.0}) assert config.level == NoiseReductionLevel.CUSTOM assert config.get_effective_noise_floor() == -30.0 @@ -334,14 +324,10 @@ class TestNoiseReductionConfig: def test_noise_floor_clamp(self): """noise_floor 越界自动钳制.""" # 低于最小值 - config = NoiseReductionConfig.from_dict( - {"enabled": True, "level": "custom", "noise_floor": -100} - ) + config = NoiseReductionConfig.from_dict({"enabled": True, "level": "custom", "noise_floor": -100}) assert config.noise_floor == -60.0 # 高于最大值 - config = NoiseReductionConfig.from_dict( - {"enabled": True, "level": "custom", "noise_floor": 0} - ) + config = NoiseReductionConfig.from_dict({"enabled": True, "level": "custom", "noise_floor": 0}) assert config.noise_floor == -5.0 def test_voice_enhance(self): @@ -392,9 +378,7 @@ class TestNoiseReductionEngine: def test_custom_level(self): """自定义降噪等级.""" - config = NoiseReductionConfig( - enabled=True, level=NoiseReductionLevel.CUSTOM, noise_floor=-40.0 - ) + config = NoiseReductionConfig(enabled=True, level=NoiseReductionLevel.CUSTOM, noise_floor=-40.0) engine = NoiseReductionEngine(config) result = engine.build_filter("[in]", "[out]") assert "nf=-40" in result @@ -452,9 +436,7 @@ class TestApplyNoiseReductionIfNeeded: def test_enabled_config(self): """启用配置返回滤镜字符串.""" - result = apply_noise_reduction_if_needed( - {"enabled": True, "level": "medium"}, "[a0]", "[nr0]" - ) + result = apply_noise_reduction_if_needed({"enabled": True, "level": "medium"}, "[a0]", "[nr0]") assert result is not None assert "afftdn" in result assert "[a0]" in result @@ -462,9 +444,7 @@ class TestApplyNoiseReductionIfNeeded: def test_invalid_config_degrades(self): """无效配置不崩溃.""" - result = apply_noise_reduction_if_needed( - {"enabled": True, "level": 12345}, "[in]", "[out]" - ) + result = apply_noise_reduction_if_needed({"enabled": True, "level": 12345}, "[in]", "[out]") # 不抛异常,可能返回 None 或有效结果 assert result is None or isinstance(result, str) @@ -517,18 +497,16 @@ class TestParameterBoundaries: @pytest.mark.parametrize( "similarity,expected", [ - (0.0, 0.01), # 低于最小值 → 钳制到 min - (0.01, 0.01), # 最小值 - (0.5, 0.5), # 中间值 - (1.0, 1.0), # 最大值 - (2.0, 1.0), # 超过最大值 → 钳制到 max + (0.0, 0.01), # 低于最小值 → 钳制到 min + (0.01, 0.01), # 最小值 + (0.5, 0.5), # 中间值 + (1.0, 1.0), # 最大值 + (2.0, 1.0), # 超过最大值 → 钳制到 max ], ) def test_similarity_boundaries(self, similarity, expected): """similarity 边界值测试.""" - config = ChromaKeyConfig.from_dict( - {"enabled": True, "similarity": similarity} - ) + config = ChromaKeyConfig.from_dict({"enabled": True, "similarity": similarity}) assert abs(config.similarity - expected) < 0.001 @pytest.mark.parametrize( @@ -558,7 +536,5 @@ class TestParameterBoundaries: ) def test_noise_floor_boundaries(self, noise_floor, expected): """noise_floor 边界值测试.""" - config = NoiseReductionConfig.from_dict( - {"enabled": True, "level": "custom", "noise_floor": noise_floor} - ) + config = NoiseReductionConfig.from_dict({"enabled": True, "level": "custom", "noise_floor": noise_floor}) assert abs(config.noise_floor - expected) < 0.001