95 lines
2.8 KiB
Python
95 lines
2.8 KiB
Python
"""剧本分镜写库服务:将分镜结果落到 Chapter/Shot/ShotDetail。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import uuid
|
|
|
|
from fastapi import HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.studio import CameraAngle, CameraMovement, CameraShotType, Chapter, Shot, ShotDetail, VFXType
|
|
from app.schemas.skills.script_processing import ScriptDivisionResult
|
|
from app.services.common import entity_not_found, require_entity
|
|
|
|
|
|
def _append_division_rows(
|
|
db_add,
|
|
*,
|
|
chapter_id: str,
|
|
result: ScriptDivisionResult,
|
|
) -> None:
|
|
for shot_division in result.shots:
|
|
title = (shot_division.shot_name or "").strip() or f"镜头 {shot_division.index}"
|
|
shot_id = str(uuid.uuid4())
|
|
db_add(
|
|
Shot(
|
|
id=shot_id,
|
|
chapter_id=chapter_id,
|
|
index=shot_division.index,
|
|
title=title,
|
|
script_excerpt=shot_division.script_excerpt,
|
|
)
|
|
)
|
|
db_add(
|
|
ShotDetail(
|
|
id=shot_id,
|
|
camera_shot=CameraShotType.ms,
|
|
angle=CameraAngle.eye_level,
|
|
movement=CameraMovement.static,
|
|
follow_atmosphere=True,
|
|
vfx_type=VFXType.none,
|
|
duration=4,
|
|
)
|
|
)
|
|
|
|
|
|
async def write_division_result_to_chapter(
|
|
db: AsyncSession,
|
|
*,
|
|
chapter_id: str,
|
|
result: ScriptDivisionResult,
|
|
) -> None:
|
|
"""将分镜结果写入指定章节;若章节已有镜头则拒绝写入。"""
|
|
await require_entity(
|
|
db,
|
|
Chapter,
|
|
chapter_id,
|
|
detail=entity_not_found("Chapter"),
|
|
status_code=400,
|
|
)
|
|
|
|
existing = await db.execute(select(Shot.id).where(Shot.chapter_id == chapter_id).limit(1))
|
|
if existing.first() is not None:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Chapter already has shots; refusing to write (write_strategy=fail)",
|
|
)
|
|
|
|
_append_division_rows(db.add, chapter_id=chapter_id, result=result)
|
|
|
|
# 触发唯一约束与外键检查,确保在返回前失败。
|
|
await db.flush()
|
|
|
|
|
|
def write_division_result_to_chapter_sync(
|
|
db: Session,
|
|
*,
|
|
chapter_id: str,
|
|
result: ScriptDivisionResult,
|
|
) -> None:
|
|
chapter = db.get(Chapter, chapter_id)
|
|
if chapter is None:
|
|
raise HTTPException(status_code=400, detail=entity_not_found("Chapter"))
|
|
|
|
existing = db.execute(select(Shot.id).where(Shot.chapter_id == chapter_id).limit(1))
|
|
if existing.first() is not None:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Chapter already has shots; refusing to write (write_strategy=fail)",
|
|
)
|
|
|
|
_append_division_rows(db.add, chapter_id=chapter_id, result=result)
|
|
db.flush()
|