diff --git a/apps/api/app/api/routes/generation_preview.py b/apps/api/app/api/routes/generation_preview.py index 3072a8f7b..db03026e9 100755 --- a/apps/api/app/api/routes/generation_preview.py +++ b/apps/api/app/api/routes/generation_preview.py @@ -312,7 +312,7 @@ def create_preview_generation_task( project_id="", asset_library_id="", strategy_id=strategy_id, - voice_library_id="", + voice_library_id=request.voice_library_id, template_id=request.template_id, asset_ids=list(request.asset_ids), title_ids=list(request.title_ids), diff --git a/apps/api/app/schemas/generation_task.py b/apps/api/app/schemas/generation_task.py index 8c458a9d5..3d6015276 100755 --- a/apps/api/app/schemas/generation_task.py +++ b/apps/api/app/schemas/generation_task.py @@ -161,6 +161,9 @@ class CreatePreviewGenerationTaskRequest(BaseModel): asset_ids: list[str] = Field(default_factory=list) title_ids: list[str] = Field(default_factory=list) voice_ids: list[str] = Field(default_factory=list) + voice_library_id: str = Field( + default="", description="配音素材库ID(用户上传的音频或AI配音),对应配音选择页面选择的配音素材" + ) video_title: str = Field(default="", description="生成视频的标题/名称,为空则使用默认命名") duration: float = Field(default=0.0, ge=0, description="期望视频时长(秒),0 表示由模板决定") video_ratio: str = Field(default="", description="视频比例,如 16:9 / 9:16,为空使用模板默认") diff --git a/apps/web/src/pages/generate/generate.css b/apps/web/src/pages/generate/generate.css index 538b2914a..7bc13a098 100644 --- a/apps/web/src/pages/generate/generate.css +++ b/apps/web/src/pages/generate/generate.css @@ -2383,7 +2383,6 @@ font-size: 14px; } - /* ── 加载中状态 ── */ .xx-preview-loading { text-align: center; diff --git a/tests/unit/test_preview_voice_library_id.py b/tests/unit/test_preview_voice_library_id.py new file mode 100644 index 000000000..20664c564 --- /dev/null +++ b/tests/unit/test_preview_voice_library_id.py @@ -0,0 +1,74 @@ +"""预览生成 voice_library_id 透传修复测试. + +Bug: 预览生成接口硬编码 voice_library_id="",导致用户选择的上传音频 +在预览渲染时从未下载和混入,预览视频无声。 + +Fix: CreatePreviewGenerationTaskRequest 增加 voice_library_id 字段, +预览端点透传 request.voice_library_id。 +""" + +from __future__ import annotations + +import os +import sys +from pathlib import Path + +os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing") +os.environ.setdefault("DATABASE_URL", "sqlite:///test.db") + +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) + + +class TestPreviewVoiceLibraryIdSchema: + """CreatePreviewGenerationTaskRequest voice_library_id 字段测试.""" + + def test_default_empty_string(self): + """不传 voice_library_id 时默认为空字符串.""" + from app.schemas.generation_task import CreatePreviewGenerationTaskRequest + + req = CreatePreviewGenerationTaskRequest( + template_id="tmpl_1", + asset_ids=["a1"], + ) + assert req.voice_library_id == "" + + def test_accepts_voice_library_id(self): + """传入 voice_library_id 正常接收.""" + from app.schemas.generation_task import CreatePreviewGenerationTaskRequest + + req = CreatePreviewGenerationTaskRequest( + template_id="tmpl_1", + asset_ids=["a1"], + voice_library_id="asset_abc123", + ) + assert req.voice_library_id == "asset_abc123" + + def test_accepts_empty_voice_library_id(self): + """显式传空字符串也正常.""" + from app.schemas.generation_task import CreatePreviewGenerationTaskRequest + + req = CreatePreviewGenerationTaskRequest( + template_id="tmpl_1", + asset_ids=["a1"], + voice_library_id="", + ) + assert req.voice_library_id == "" + + def test_all_fields_including_voice_library_id(self): + """包含 voice_library_id 的完整请求.""" + from app.schemas.generation_task import CreatePreviewGenerationTaskRequest + + req = CreatePreviewGenerationTaskRequest( + template_id="tmpl_123", + asset_ids=["a1", "a2"], + title_ids=["t1"], + voice_ids=["v1"], + voice_library_id="voice_asset_456", + video_title="测试预览", + duration=30.0, + video_ratio="9:16", + bgm_config={"enabled": True, "volume": 0.5}, + ) + assert req.voice_library_id == "voice_asset_456" + assert req.asset_ids == ["a1", "a2"] + assert req.bgm_config["enabled"] is True