chore: rebase到develop + 格式化代码
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1m26s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1541h17m16s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1541h17m18s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1541h17m18s
CI/CD Pipeline / Build Production Runtime Images (pull_request) Failing after 1541h17m20s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Failing after 1541h17m20s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1541h48m55s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1m26s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1541h17m16s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1541h17m18s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1541h17m18s
CI/CD Pipeline / Build Production Runtime Images (pull_request) Failing after 1541h17m20s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Failing after 1541h17m20s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1541h48m55s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
This commit is contained in:
@@ -46,8 +46,8 @@ from packages.application.voice_library.use_cases import (
|
||||
CreateVoiceLibraryUseCase,
|
||||
QuotaExceededError,
|
||||
)
|
||||
from packages.ports.user_repository import UserRepository
|
||||
from packages.domain.voice_presets import list_voices
|
||||
from packages.ports.user_repository import UserRepository
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -244,9 +244,7 @@ class TtsEngine:
|
||||
# 音量调整
|
||||
# 用 adelay 延迟到字幕开始时间
|
||||
delay_ms = int(max(0, int(seg.start_time * 1000)))
|
||||
filter_parts.append(
|
||||
f"[{i}:a]adelay={delay_ms}:all=1,volume=0.8[{seg_label}]"
|
||||
)
|
||||
filter_parts.append(f"[{i}:a]adelay={delay_ms}:all=1,volume=0.8[{seg_label}]")
|
||||
delay_labels.append(f"[{seg_label}]")
|
||||
|
||||
if not delay_labels:
|
||||
@@ -262,9 +260,7 @@ class TtsEngine:
|
||||
filter_parts.append(f"{delay_labels[0]}[{tts_label}]")
|
||||
else:
|
||||
# 多个片段 amix 叠加
|
||||
filter_parts.append(
|
||||
f"{mix_inputs}amix=inputs={n_inputs}:duration=longest[{tts_label}]"
|
||||
)
|
||||
filter_parts.append(f"{mix_inputs}amix=inputs={n_inputs}:duration=longest[{tts_label}]")
|
||||
|
||||
return ";".join(filter_parts), audio_files
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ from video_processing.render_audio import RenderContext, merge_audio_video, mix_
|
||||
from video_processing.render_subtitles import generate_ass_subtitles
|
||||
from video_processing.subtitle_generator import generate_ass_from_timeline
|
||||
from video_processing.tts_engine import TtsEngine
|
||||
|
||||
from packages.domain.tts_config import TtsConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -534,9 +535,7 @@ class UnifiedRenderService:
|
||||
tts_engine = TtsEngine(tts_service, self.work_dir / "tts")
|
||||
|
||||
# 整段配音模式
|
||||
result = tts_engine.generate_full_voiceover(
|
||||
tts_config, total_duration=video_duration
|
||||
)
|
||||
result = tts_engine.generate_full_voiceover(tts_config, total_duration=video_duration)
|
||||
|
||||
if not result.success or not result.segments:
|
||||
logger.warning("TTS 配音生成失败,跳过: %s", result.error_message)
|
||||
|
||||
@@ -168,9 +168,7 @@ class MockTtsService(TtsService):
|
||||
filter_parts = []
|
||||
|
||||
# 主音 + 轻微频率调制
|
||||
filter_parts.append(
|
||||
f"sine=frequency={base_freq}:duration={duration}:sample_rate={sample_rate}"
|
||||
)
|
||||
filter_parts.append(f"sine=frequency={base_freq}:duration={duration}:sample_rate={sample_rate}")
|
||||
|
||||
# 颤音效果
|
||||
filter_parts.append(f"tremolo=f={tremolo_freq}:d={tremolo_depth}")
|
||||
|
||||
@@ -212,12 +212,7 @@ def list_voices(
|
||||
|
||||
if keyword:
|
||||
kw = keyword.lower()
|
||||
result = [
|
||||
v for v in result
|
||||
if kw in v.name.lower()
|
||||
or kw in v.description.lower()
|
||||
or kw in v.voice_id.lower()
|
||||
]
|
||||
result = [v for v in result if kw in v.name.lower() or kw in v.description.lower() or kw in v.voice_id.lower()]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from apps.worker.video_processing.tts_engine import TtsEngine, VoiceoverResult, VoiceoverSegment
|
||||
from packages.adapters.tts.mock_tts_service import MockTtsService
|
||||
from packages.domain.tts_config import TtsConfig
|
||||
from packages.domain.voice_presets import (
|
||||
VoiceGender,
|
||||
VoicePreset,
|
||||
@@ -12,11 +15,7 @@ from packages.domain.voice_presets import (
|
||||
get_voice,
|
||||
list_voices,
|
||||
)
|
||||
from packages.domain.tts_config import TtsConfig
|
||||
from packages.ports.tts_service import TtsError, TtsService
|
||||
from packages.adapters.tts.mock_tts_service import MockTtsService
|
||||
from apps.worker.video_processing.tts_engine import TtsEngine, VoiceoverResult, VoiceoverSegment
|
||||
|
||||
|
||||
# ─── TtsConfig 配置解析 ────────────────────────────────────
|
||||
|
||||
@@ -75,16 +74,18 @@ class TestTtsConfig:
|
||||
assert config.volume == 1.0
|
||||
|
||||
def test_parse_invalid_types(self):
|
||||
config = TtsConfig.parse({
|
||||
"enabled": True,
|
||||
"speed": "fast",
|
||||
"pitch": "high",
|
||||
"volume": "loud",
|
||||
"voice_id": 123,
|
||||
"text": 456,
|
||||
"align_mode": "invalid",
|
||||
"overlap_mode": "invalid",
|
||||
})
|
||||
config = TtsConfig.parse(
|
||||
{
|
||||
"enabled": True,
|
||||
"speed": "fast",
|
||||
"pitch": "high",
|
||||
"volume": "loud",
|
||||
"voice_id": 123,
|
||||
"text": 456,
|
||||
"align_mode": "invalid",
|
||||
"overlap_mode": "invalid",
|
||||
}
|
||||
)
|
||||
assert config.speed == 1.0
|
||||
assert config.pitch == 0.0
|
||||
assert config.volume == 0.8
|
||||
@@ -178,9 +179,7 @@ class TestMockTtsService:
|
||||
voices = ["female_warm", "male_stable", "child_cute"]
|
||||
for vid in voices:
|
||||
output = tmp_path / f"{vid}.wav"
|
||||
result = self.service.synthesize(
|
||||
"测试", voice_id=vid, output_path=output
|
||||
)
|
||||
result = self.service.synthesize("测试", voice_id=vid, output_path=output)
|
||||
assert result.exists()
|
||||
|
||||
def test_synthesize_speed_faster(self, tmp_path):
|
||||
@@ -199,9 +198,7 @@ class TestMockTtsService:
|
||||
|
||||
def test_synthesize_pitch_changes(self, tmp_path):
|
||||
output = tmp_path / "high_pitch.wav"
|
||||
result = self.service.synthesize(
|
||||
"测试", voice_id="female_warm", pitch=6, output_path=output
|
||||
)
|
||||
result = self.service.synthesize("测试", voice_id="female_warm", pitch=6, output_path=output)
|
||||
assert result.exists()
|
||||
|
||||
def test_synthesize_empty_text_raises(self):
|
||||
@@ -229,9 +226,7 @@ class TestMockTtsService:
|
||||
def test_synthesize_unknown_voice_fallback(self, tmp_path):
|
||||
output = tmp_path / "fallback.wav"
|
||||
# 未知音色应该 fallback 到默认音色,不报错
|
||||
result = self.service.synthesize(
|
||||
"测试", voice_id="unknown_voice", output_path=output
|
||||
)
|
||||
result = self.service.synthesize("测试", voice_id="unknown_voice", output_path=output)
|
||||
assert result.exists()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user