feat: P2 素材库自动匹配 - 支持 all/random/smart 三种素材选取模式
CI/CD Pipeline / Frontend Lint (push) Failing after 45h41m20s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 45h41m20s
CI/CD Pipeline / Production Browser E2E (push) Failing after 1704h16m48s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1704h16m50s
CI/CD Pipeline / Build Production Runtime Images (push) Failing after 1704h16m52s
CI/CD Pipeline / Deploy Staging (push) Failing after 1704h16m52s
CI/CD Pipeline / Deploy Production (push) Failing after 1704h48m18s

- Schema: 新增 asset_select_mode + asset_select_count 字段
- Domain: GenerationTask 新增 asset_select_mode 字段
- Application: Command/UseCase 透传 asset_select_mode
- Route: _select_assets_from_library() 辅助函数 + 创建任务集成
- SQLAlchemy: Model/Repository 映射 asset_select_mode
- Alembic: 032 号迁移
- Worker: _download_library_assets() 支持 asset_ids 过滤
- 单测: 15 个测试覆盖三种模式 + 边界情况
This commit is contained in:
灵应
2026-07-07 15:13:05 +08:00
parent 4fee87c5e8
commit 1ea8fd3989
9 changed files with 293 additions and 13 deletions
+57 -1
View File
@@ -1,3 +1,4 @@
import random
from typing import Any
from app.auth import AuthenticatedUser, get_current_user
@@ -52,6 +53,7 @@ def _to_generation_task_response(task) -> GenerationTaskResponse:
title_ids=task.title_ids,
voice_ids=task.voice_ids,
source_edit_plan_id=task.source_edit_plan_id or "",
asset_select_mode=getattr(task, "asset_select_mode", ""),
status=task.status,
progress=task.progress,
result_count=task.result_count,
@@ -86,6 +88,49 @@ def _ensure_library_has_ready_video_assets(assets) -> None:
)
def _select_assets_from_library(
assets: list,
mode: str,
count: int,
) -> list[str]:
"""根据选取模式从素材库中选取 ready 状态的视频素材 ID。
Args:
assets: 素材库中所有素材(Asset 实体列表)
mode: 选取模式 — all=全部, random=随机, smart=按质量评分
count: 选取数量,0 表示全部(仅 random/smart 模式有效)
Returns:
选中的素材 ID 列表
"""
ready_video_assets = [a for a in assets if a.status.value == "ready" and a.mime_type.startswith("video")]
if not ready_video_assets:
return []
if mode == "random":
selected = (
ready_video_assets if count <= 0 else random.sample(ready_video_assets, min(count, len(ready_video_assets)))
)
return [a.id for a in selected]
if mode == "smart":
# 按质量分降序排列(质量分高的优先),质量分相同时按时长降序
sorted_assets = sorted(
ready_video_assets,
key=lambda a: (
a.quality_score if a.quality_score is not None else 0.0,
a.duration if a.duration is not None else 0.0,
),
reverse=True,
)
selected = sorted_assets if count <= 0 else sorted_assets[:count]
return [a.id for a in selected]
# 默认 all 模式:返回全部 ready 视频素材
return [a.id for a in ready_video_assets]
def _resolve_project_and_library(
request: CreateGenerationTaskRequest,
project_repository: Any,
@@ -137,6 +182,7 @@ def create_generation_task(
)
# asset_library 存在性校验(仅在提供了 asset_library_id 时)
resolved_asset_ids: list[str] = list(request.asset_ids)
if asset_library_id:
library = asset_library_repository.get(asset_library_id)
if library is None or (project_id and library.project_id != project_id):
@@ -145,6 +191,14 @@ def create_generation_task(
assets = asset_repository.find_by_library(asset_library_id)
_ensure_library_has_ready_video_assets(assets)
# 素材库自动匹配:当未显式指定 asset_ids 时,按模式自动选取
if not resolved_asset_ids:
resolved_asset_ids = _select_assets_from_library(
assets,
mode=request.asset_select_mode,
count=request.asset_select_count,
)
use_case = CreateGenerationTaskUseCase(generation_task_repository)
count = request.count
created_tasks = []
@@ -157,11 +211,12 @@ def create_generation_task(
strategy_id=request.strategy_id,
voice_library_id=request.voice_library_id,
template_id=request.template_id,
asset_ids=request.asset_ids,
asset_ids=resolved_asset_ids,
title_ids=request.title_ids,
voice_ids=request.voice_ids,
created_by_user_id=authenticated_user.user.id,
source_edit_plan_id=request.source_edit_plan_id,
asset_select_mode=request.asset_select_mode,
)
)
celery_app.send_task("worker.generate_video", args=[task.id])
@@ -245,6 +300,7 @@ def retry_generation_task(
voice_ids=task.voice_ids,
created_by_user_id=authenticated_user.user.id,
source_edit_plan_id=task.source_edit_plan_id or "",
asset_select_mode=getattr(task, "asset_select_mode", ""),
)
)
celery_app.send_task("worker.generate_video", args=[retried.id])