""" 空镜素材 API ============ 提供空镜素材匹配接口(基于 PostgreSQL + Redis 去重)。 """ from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession from app.db.session import get_db from app.schemas.common import ApiResponse, success_response from app.schemas.materials import ( BatchMatchMaterialRequest, BatchMatchMaterialResponse, MatchMaterialRequest, MaterialInfo, ) from app.services.material_service import batch_match, match_material router = APIRouter() @router.post("/match", response_model=ApiResponse[MaterialInfo | None]) async def match_material_endpoint( request: MatchMaterialRequest, db: AsyncSession = Depends(get_db), ): """ 根据场景描述和所需时长匹配空镜素材 返回匹配到的素材信息,无匹配返回 data: null """ result = await match_material( db, scene=request.scene, required_duration=request.duration, project_id=request.project_id, ) if result is None: return success_response(data=None, message="未匹配到素材") await db.commit() return success_response( data=MaterialInfo(url=result["url"], duration=result["duration"]), message="匹配成功", ) @router.post("/batch-match", response_model=ApiResponse[BatchMatchMaterialResponse]) async def batch_match_materials_endpoint( request: BatchMatchMaterialRequest, db: AsyncSession = Depends(get_db), ): """ 批量匹配空镜素材 根据分镜列表一次性匹配所有素材,自动进行项目级去重。 """ raw_scenes = [{"scene": s.scene, "duration": s.duration} for s in request.scenes] results = await batch_match( db, scenes=raw_scenes, project_id=request.project_id, ) matched: list[MaterialInfo | None] = [ MaterialInfo(url=r["url"], duration=r["duration"]) if r else None for r in results ] await db.commit() success_count = sum(1 for r in matched if r is not None) return success_response( data=BatchMatchMaterialResponse( project_id=request.project_id, results=matched, ), message=f"批量匹配完成,成功 {success_count}/{len(matched)}", )