diff --git a/apps/api/app/api/routes/generation_tasks.py b/apps/api/app/api/routes/generation_tasks.py index f7431ac0a..f13a0121d 100644 --- a/apps/api/app/api/routes/generation_tasks.py +++ b/apps/api/app/api/routes/generation_tasks.py @@ -5,6 +5,7 @@ from app.api.routes.permissions import require_workspace_member from app.core.celery_app import celery_app from app.dependencies import ( get_asset_library_repository, + get_asset_repository, get_generated_video_repository, get_generation_task_repository, get_project_repository, @@ -63,6 +64,18 @@ def _to_generated_video_response(item) -> GeneratedVideoResponse: ) +def _ensure_library_has_ready_video_assets(assets) -> None: + ready_video_assets = [ + asset for asset in assets + if asset.status.value == "ready" and asset.mime_type.startswith("video") + ] + if not ready_video_assets: + raise HTTPException( + status_code=422, + detail="当前素材库没有 ready 状态的视频素材,请先上传并等待导入完成后再生成。", + ) + + @router.post("/tasks", response_model=GenerationTaskResponse) def create_generation_task( request: CreateGenerationTaskRequest, @@ -70,6 +83,7 @@ def create_generation_task( generation_task_repository: Any = Depends(get_generation_task_repository), project_repository: Any = Depends(get_project_repository), asset_library_repository: Any = Depends(get_asset_library_repository), + asset_repository: Any = Depends(get_asset_repository), workspace_member_repository: WorkspaceMemberRepository = Depends(get_workspace_member_repository), ) -> GenerationTaskResponse: require_workspace_member(request.workspace_id, authenticated_user, workspace_member_repository) @@ -79,6 +93,8 @@ def create_generation_task( library = asset_library_repository.get(request.asset_library_id) if library is None or library.workspace_id != request.workspace_id or library.project_id != request.project_id: raise HTTPException(status_code=404, detail=f"AssetLibrary {request.asset_library_id} not found") + assets = asset_repository.list_by_library(request.asset_library_id) + _ensure_library_has_ready_video_assets(assets) use_case = CreateGenerationTaskUseCase(generation_task_repository) task = use_case.execute( diff --git a/tests/unit/test_generation_preflight.py b/tests/unit/test_generation_preflight.py new file mode 100644 index 000000000..955d65da3 --- /dev/null +++ b/tests/unit/test_generation_preflight.py @@ -0,0 +1,40 @@ +import sys +from pathlib import Path + +import pytest +from fastapi import HTTPException + +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api")) + +from app.api.routes.generation_tasks import _ensure_library_has_ready_video_assets +from packages.domain import Asset, AssetStatus + + +def _asset(name: str, mime_type: str, status: AssetStatus) -> Asset: + return Asset.create( + workspace_id="workspace-1", + project_id="project-1", + library_id="library-1", + name=name, + storage_key=f"uploads/{name}", + mime_type=mime_type, + file_size=1024, + status=status, + ) + + +def test_generation_preflight_rejects_library_without_ready_video(): + with pytest.raises(HTTPException) as exc_info: + _ensure_library_has_ready_video_assets([ + _asset("image.jpg", "image/jpeg", AssetStatus.READY), + _asset("video.mp4", "video/mp4", AssetStatus.UPLOADING), + ]) + + assert exc_info.value.status_code == 422 + assert "ready 状态的视频素材" in exc_info.value.detail + + +def test_generation_preflight_accepts_ready_video(): + _ensure_library_has_ready_video_assets([ + _asset("video.mp4", "video/mp4", AssetStatus.READY), + ])