447f3c2ffe
后端: - 新增 BrollCategory/BrollMaterial/BrollTag 模型及表(mjk_categories/materials/tags) - 新增 Alembic 迁移 69274ce979a5 - 新增 broll_category/broll_material CRUD 层 - 重构 material_service:删除 JSON 配置,改用 PostgreSQL + Redis 去重 - 新增 /materials/batch-match 接口,删除 /materials/reload - usage_count 原子递增,Redis 失败自动降级 前端: - materials API 改为 projectId 去重,新增 batchMatch - VideoGeneration 批量匹配改用 batchMatch,删除 usedUrls 手动维护 - 修复积分不足时进度弹窗与充值弹窗叠加的 bug - 操作前预检积分,不足时显示提示条+立即充值按钮
88 lines
2.3 KiB
Python
88 lines
2.3 KiB
Python
"""
|
|
空镜素材 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)}",
|
|
)
|