chore: black + isort 格式化
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m10s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m17s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Successful in 3m16s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1m36s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m10s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m17s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Successful in 3m16s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1m36s
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user