From 5aceb5dc3cf484d8b08e1940b659a66d6393f5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=B5=E5=BA=94?= Date: Wed, 8 Jul 2026 17:13:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=94=9F=E6=88=90=E5=85=9C=E5=BA=95?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=8A=A8=E7=B4=A0=E6=9D=90=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=EF=BC=88=E4=BB=8E=E9=A1=B9=E7=9B=AE=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=E5=BA=93=E8=87=AA=E5=8A=A8=E9=80=89=E5=8F=96?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/app/api/routes/edit_plans.py | 91 ++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/apps/api/app/api/routes/edit_plans.py b/apps/api/app/api/routes/edit_plans.py index 8926b19b2..d5ea0fc82 100644 --- a/apps/api/app/api/routes/edit_plans.py +++ b/apps/api/app/api/routes/edit_plans.py @@ -22,7 +22,7 @@ from typing import Any, List, Optional from app.auth import AuthenticatedUser, get_current_user from app.core.celery_app import celery_app -from app.dependencies import get_db_session, get_project_repository +from app.dependencies import get_asset_library_repository, get_asset_repository, get_db_session, get_project_repository from app.schemas.generation_task import GenerationTaskResponse from app.services import EditPlanService from fastapi import APIRouter, Depends, HTTPException, Query, status @@ -35,6 +35,12 @@ from packages.adapters.sqlalchemy_impl.generation_task_repository import ( from packages.adapters.sqlalchemy_impl.template_clip_config_repository import ( SQLAlchemyTemplateClipConfigRepository, ) +from packages.adapters.sqlalchemy_impl.asset_library_repository import ( + SQLAlchemyAssetLibraryRepository, +) +from packages.adapters.sqlalchemy_impl.asset_repository import ( + SQLAlchemyAssetRepository, +) from packages.adapters.sqlalchemy_impl.template_repository import ( SQLAlchemyTemplateRepository, ) @@ -441,6 +447,8 @@ def generate_plan( db: Session = Depends(get_db_session), current_user: AuthenticatedUser = Depends(get_current_user), project_repository: Any = Depends(get_project_repository), + asset_library_repo: Any = Depends(get_asset_library_repository), + asset_repo: Any = Depends(get_asset_repository), ) -> EditPlanGenerateResponse: """触发剪辑计划渲染生成 @@ -509,22 +517,71 @@ def generate_plan( ) logger.info("自动兜底: plan=%s 从旧模型 template_segments 复制了 %d 个片段", plan_id, len(segments)) - # ── 自动兜底 3: 为没有素材的片段分配素材 ────────────────────────── - # 如果 plan.config.asset_ids 有素材,但 clips 没有 asset_id,自动按顺序分配 - all_clips = svc.list_clips(plan_id) - clips_without_asset = [c for c in all_clips if not c.asset_id] - config_asset_ids = (plan_check.config or {}).get("asset_ids", []) - if clips_without_asset and config_asset_ids: - logger.info( - "自动兜底: plan=%s 为 %d 个无素材片段分配 %d 个素材", - plan_id, - len(clips_without_asset), - len(config_asset_ids), - ) - for i, clip in enumerate(clips_without_asset): - asset_idx = i % len(config_asset_ids) - svc.assign_asset(clip.id, config_asset_ids[asset_idx]) - logger.info("自动兜底: plan=%s 素材分配完成", plan_id) + # ── 自动兜底 3: 为没有素材的片段分配素材 ────────────────────────── + # 如果 plan.config.asset_ids 有素材,但 clips 没有 asset_id,自动按顺序分配 + all_clips = svc.list_clips(plan_id) + clips_without_asset = [c for c in all_clips if not c.asset_id] + config_asset_ids = (plan_check.config or {}).get("asset_ids", []) + material_mode = (plan_check.config or {}).get("material_mode", "manual") + + if clips_without_asset and config_asset_ids: + logger.info( + "自动兜底3: plan=%s 为 %d 个无素材片段分配 %d 个指定素材", + plan_id, + len(clips_without_asset), + len(config_asset_ids), + ) + for i, clip in enumerate(clips_without_asset): + asset_idx = i % len(config_asset_ids) + svc.assign_asset(clip.id, config_asset_ids[asset_idx]) + logger.info("自动兜底3: plan=%s 素材分配完成", plan_id) + clips_without_asset = [] # 已分配完 + + # ── 自动兜底 4: 自动素材模式 → 从项目默认视频素材库选取 ──────────── + if clips_without_asset and material_mode == "auto" and plan_check.project_id: + import random + + logger.info( + "自动兜底4: plan=%s 自动素材模式,从项目素材库选取素材 (%d 个片段需要)", + plan_id, + len(clips_without_asset), + ) + # 找到项目的视频素材库 + libs = asset_library_repo.find_by_project(plan_check.project_id) + video_lib = None + for lib in libs: + lib_kind = lib.kind.value if hasattr(lib.kind, "value") else lib.kind + if lib_kind == "video": + video_lib = lib + break + + if video_lib: + assets = asset_repo.find_by_library(video_lib.id) + # 筛选 ready 状态的视频素材 + ready_videos = [ + a + for a in assets + if (a.status.value if hasattr(a.status, "value") else a.status) == "ready" + and a.mime_type + and a.mime_type.startswith("video") + ] + if ready_videos: + # 随机选取,按片段数轮询分配 + random.shuffle(ready_videos) + for i, clip in enumerate(clips_without_asset): + asset = ready_videos[i % len(ready_videos)] + svc.assign_asset(clip.id, asset.id) + logger.info( + "自动兜底4: plan=%s 从素材库 %s 分配了 %d 个素材给 %d 个片段", + plan_id, + video_lib.name, + len(ready_videos), + len(clips_without_asset), + ) + else: + logger.warning("自动兜底4: plan=%s 素材库无可用视频素材", plan_id) + else: + logger.warning("自动兜底4: plan=%s 项目无视频素材库", plan_id) # 检查是否可生成 try: