"""独立的从素材创建片段端点(不依赖 template_id 路径参数). POST /api/v1/clips/from-assets - 与 /api/v1/templates/{template_id}/editor/clips/from-assets 功能一致 - 区别:template_id 从 body 传入(可选),为空时后端自动创建/查找默认模板 - 解决前端首次加载时 templateId 为空导致双斜杠 404 的问题(P0 #1922) - 内部复用 resolve_draft_plan_id 和 create_clips_from_assets_editor 的核心逻辑 """ from __future__ import annotations import logging from app.auth import AuthenticatedUser, get_current_user from app.dependencies import get_asset_repository, get_db_session from app.services.edit_plan_service import EditPlanService from app.services.edit_template_service import EditTemplateService from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status from sqlalchemy.orm import Session from packages.adapters.sqlalchemy_impl.asset_repository import SQLAlchemyAssetRepository from ._default_template import get_or_create_default_template_id from .templates_editor.clips import create_clips_from_assets_editor from .templates_editor.dependencies import resolve_draft_plan_id from .templates_editor.schemas import ClipsFromAssetsRequest, ClipsFromAssetsResponse logger = logging.getLogger(__name__) router = APIRouter(tags=["Clips"]) class StandaloneClipsRequest(ClipsFromAssetsRequest): """扩展请求:template_id 可选(不传则后端自动兜底默认模板)。""" template_id: str | None = None def _get_editor_services_direct(db: Session) -> tuple[EditTemplateService, EditPlanService]: """直接构造服务实例(非 Depends 版本,供独立端点内部调用)。""" return EditTemplateService(db), EditPlanService(db) @router.post("/clips/from-assets", response_model=ClipsFromAssetsResponse) def create_clips_from_assets( body: StandaloneClipsRequest, background_tasks: BackgroundTasks, db: Session = Depends(get_db_session), current_user: AuthenticatedUser = Depends(get_current_user), asset_repo: SQLAlchemyAssetRepository = Depends(get_asset_repository), ) -> ClipsFromAssetsResponse: """从素材批量创建片段(template_id 可选,为空自动兜底)。""" user_id = str(current_user.user.id) services = _get_editor_services_direct(db) # 1. 解析/兜底 template_id,拿到 plan_id template_id = (body.template_id or "").strip() if not template_id: template_id = get_or_create_default_template_id(db, user_id) if not template_id: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="无法自动创建默认模板,请刷新页面重试", ) plan_id = resolve_draft_plan_id( template_id=template_id, services=services, current_user=current_user, db=db, auto_create_default=False, # 上面已兜底过 ) # 2. 构造标准化请求(去除独立端扩展字段),复用原端点核心逻辑 core_body = ClipsFromAssetsRequest( asset_ids=body.asset_ids, clip_type=body.clip_type, clip_count=body.clip_count, required_clips_count=body.required_clips_count, ) # 3. 直接调用原端点函数(此时所有 Depends 依赖已手动传入) return create_clips_from_assets_editor( template_id=template_id, body=core_body, background_tasks=background_tasks, plan_id=plan_id, services=services, asset_repo=asset_repo, db=db, current_user=current_user, )