chore: squash merge feature/smart-edit-plan into develop (resolve conflicts)
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Waiting to run
Tests / test (pull_request) Waiting to run
Tests / lint (pull_request) Waiting to run
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
Deploy / Deploy Staging (push) Has been cancelled
Deploy / Build Production Runtime Images (push) Has been cancelled
Deploy / Deploy Production (push) Has been cancelled
Deploy / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Waiting to run
Tests / test (pull_request) Waiting to run
Tests / lint (pull_request) Waiting to run
CI/CD Pipeline / Validate Code Quality And Tests (push) Has been cancelled
Deploy / Deploy Staging (push) Has been cancelled
Deploy / Build Production Runtime Images (push) Has been cancelled
Deploy / Deploy Production (push) Has been cancelled
Deploy / Production Browser E2E (push) Has been cancelled
- Resolve CI/CD and deploy workflow conflicts with develop version - Resolve database and dependencies conflicts with develop version - Keep new edit plan generator and API functionality from feature branch
This commit is contained in:
@@ -9,7 +9,17 @@ from app.dependencies import (
|
||||
get_project_repository,
|
||||
get_workspace_member_repository,
|
||||
)
|
||||
from app.schemas.edit_plan import CreateEditPlanRequest, EditPlanClipResponse, EditPlanResponse, EditTemplateResponse
|
||||
from app.schemas.edit_plan import (
|
||||
AutoGenerateEditPlanRequest,
|
||||
CreateEditPlanRequest,
|
||||
EditPlanClipResponse,
|
||||
EditPlanResponse,
|
||||
EditTemplateResponse,
|
||||
)
|
||||
from apps.worker.worker_app.tasks.edit_plan_generator import (
|
||||
EditingMode,
|
||||
SmartEditPlanGenerator,
|
||||
)
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -81,6 +91,7 @@ def _to_plan_response(
|
||||
title_id=plan.title_id,
|
||||
status=plan.status,
|
||||
summary=plan.summary,
|
||||
editing_mode=plan.editing_mode,
|
||||
clips=[
|
||||
EditPlanClipResponse(
|
||||
id=clip.id,
|
||||
@@ -90,6 +101,7 @@ def _to_plan_response(
|
||||
start_time=float(clip.start_time or 0),
|
||||
duration=float(clip.duration or 0),
|
||||
reason=clip.reason,
|
||||
layer=clip.layer,
|
||||
)
|
||||
for clip in clips
|
||||
],
|
||||
@@ -98,7 +110,7 @@ def _to_plan_response(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/templates", response_model=list[EditTemplateResponse])
|
||||
@router.get("/templates/", response_model=list[EditTemplateResponse])
|
||||
def list_edit_templates(
|
||||
project_id: str,
|
||||
workspace_id: str,
|
||||
@@ -178,6 +190,98 @@ def create_edit_plan(
|
||||
return _to_plan_response(plan, clips, {asset.id: asset.name for asset in selected})
|
||||
|
||||
|
||||
@router.post("/auto-generate", response_model=EditPlanResponse)
|
||||
def auto_generate_edit_plan(
|
||||
project_id: str,
|
||||
request: AutoGenerateEditPlanRequest,
|
||||
authenticated_user: AuthenticatedUser = Depends(get_current_user),
|
||||
project_repository=Depends(get_project_repository),
|
||||
asset_repository=Depends(get_asset_repository),
|
||||
workspace_member_repository: WorkspaceMemberRepository = Depends(get_workspace_member_repository),
|
||||
session: Session = Depends(get_db_session),
|
||||
) -> EditPlanResponse:
|
||||
"""
|
||||
智能生成剪辑计划
|
||||
|
||||
根据素材的分类结果和质量评分,自动编排剪辑计划。
|
||||
支持多种剪辑模式:
|
||||
- one-take: 按分类分组,组内按质量排序,顺序拼接
|
||||
- pip: 第一个高质量素材为主画面,其余为画中画
|
||||
- voiceover: person 类素材为主播口播,其余穿插为 B-roll
|
||||
- voice_pip: 结合 voiceover 和 pip,第一个高质量 person 素材为主画面
|
||||
"""
|
||||
_ensure_project(
|
||||
project_id, request.workspace_id, authenticated_user, project_repository, workspace_member_repository
|
||||
)
|
||||
|
||||
# 获取素材库中的所有素材
|
||||
assets = asset_repository.list_by_library(request.asset_library_id)
|
||||
|
||||
if not assets:
|
||||
raise HTTPException(status_code=422, detail="素材库中暂无素材")
|
||||
|
||||
# 使用智能生成器
|
||||
generator = SmartEditPlanGenerator(project_id, assets)
|
||||
|
||||
try:
|
||||
plan_result = generator.generate_plan(
|
||||
editing_mode=request.editing_mode,
|
||||
target_duration=request.target_duration
|
||||
)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
if not plan_result.clips:
|
||||
raise HTTPException(status_code=422, detail="无符合条件的视频素材")
|
||||
|
||||
# 获取模板
|
||||
template = (
|
||||
session.query(EditTemplateModel).filter(EditTemplateModel.id == request.template_id).first()
|
||||
if request.template_id
|
||||
else None
|
||||
)
|
||||
if template is None:
|
||||
template = _default_template(session, request.workspace_id, project_id, authenticated_user.user.id)
|
||||
|
||||
# 创建剪辑计划
|
||||
plan = EditPlanModel(
|
||||
id=uuid4().hex,
|
||||
workspace_id=request.workspace_id,
|
||||
project_id=project_id,
|
||||
template_id=template.id,
|
||||
asset_library_id=request.asset_library_id,
|
||||
title_id=request.title_id or "",
|
||||
status="draft",
|
||||
editing_mode=request.editing_mode,
|
||||
summary=plan_result.summary,
|
||||
created_by_user_id=authenticated_user.user.id,
|
||||
updated_at=datetime.now(timezone.utc),
|
||||
)
|
||||
session.add(plan)
|
||||
|
||||
# 创建剪辑片段
|
||||
clips: list[EditPlanClipModel] = []
|
||||
asset_name_map = {asset.id: asset.name for asset in assets}
|
||||
|
||||
for clip_plan in plan_result.clips:
|
||||
clip = EditPlanClipModel(
|
||||
id=uuid4().hex,
|
||||
edit_plan_id=plan.id,
|
||||
asset_id=clip_plan.asset_id,
|
||||
sequence=clip_plan.sequence,
|
||||
start_time=clip_plan.start_time,
|
||||
duration=clip_plan.duration,
|
||||
reason=clip_plan.reason,
|
||||
layer=clip_plan.layer,
|
||||
)
|
||||
session.add(clip)
|
||||
clips.append(clip)
|
||||
|
||||
session.commit()
|
||||
|
||||
return _to_plan_response(plan, clips, asset_name_map)
|
||||
|
||||
|
||||
@router.get("/{plan_id}", response_model=EditPlanResponse)
|
||||
def get_edit_plan(
|
||||
project_id: str,
|
||||
|
||||
Reference in New Issue
Block a user