191 lines
7.6 KiB
Python
191 lines
7.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""P2V 요청/응답 스키마.
|
|
|
|
응답 형태는 P2V 서버의 잡 JSON 과 최대한 호환되게 유지한다 — 프론트엔드
|
|
(o2o-castad-frontend p2vApi.ts) 가 이미 그 형태(artifacts 딕셔너리, stages 등)에
|
|
맞춰 작성돼 있어, 필드명을 바꾸면 P2V 직접 호출 → castad 프록시 전환 때
|
|
갈아엎어야 할 코드가 배로 늘기 때문이다. 단 `id` 는 castad 잡 id(정수)다 —
|
|
P2V 잡 id 는 절대 밖으로 내보내지 않는다.
|
|
|
|
검증 범위·기본값은 여기서만 강제한다. DB 모델에 두면 진실이 두 곳에 생긴다.
|
|
"""
|
|
|
|
from typing import Literal, Optional
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
F1StatusLiteral = Literal[
|
|
"queued", "running", "awaiting_review", "archiving", "done", "failed"
|
|
]
|
|
F2StatusLiteral = Literal["queued", "running", "archiving", "done", "failed"]
|
|
FormatLiteral = Literal["poster", "story", "feed", "square"]
|
|
|
|
|
|
# =============================================================================
|
|
# 공통
|
|
# =============================================================================
|
|
|
|
|
|
class P2vJobCreateResponse(BaseModel):
|
|
"""생성 요청 접수. `id` 로 폴링한다."""
|
|
|
|
id: int = Field(..., description="castad 잡 ID (P2V 서버 식별자가 아니다)")
|
|
status: F1StatusLiteral = Field(..., description="접수 직후 상태")
|
|
poll_interval_seconds: int = Field(..., description="권장 폴링 간격(초)")
|
|
|
|
|
|
class P2vJobError(BaseModel):
|
|
"""실패 정보 — P2V 의 {stage, detail} 형태를 그대로 중계한다"""
|
|
|
|
stage: str = Field(..., description="실패한 스테이지")
|
|
detail: str = Field(..., description="실패 사유")
|
|
|
|
|
|
class P2vMetadata(BaseModel):
|
|
"""행사 메타데이터 (검수 대상)"""
|
|
|
|
event_name: str = Field(default="", description="행사명")
|
|
date_text: str = Field(default="", description="일시 표기")
|
|
place: str = Field(default="", description="장소")
|
|
category: Optional[str] = Field(None, description="분류")
|
|
keywords: Optional[list[str]] = Field(None, description="키워드 목록")
|
|
region_guess: Optional[str] = Field(None, description="추정 지역")
|
|
|
|
|
|
class P2vDeleteResponse(BaseModel):
|
|
"""삭제 결과"""
|
|
|
|
id: int = Field(..., description="삭제한 castad 잡 ID")
|
|
removed: bool = Field(..., description="삭제 여부")
|
|
|
|
|
|
# =============================================================================
|
|
# F1 — 무빙 포스터
|
|
# =============================================================================
|
|
|
|
|
|
class P2vF1StatusResponse(BaseModel):
|
|
"""F1 잡 상태 (폴링 응답).
|
|
|
|
진행 중에는 P2V 실시간 값을, archived 이후에는 castad DB 미러를 내려준다.
|
|
artifacts 값은 전부 Azure Blob 공개 URL 또는 castad 프록시 경로(/p2v/files/...)다.
|
|
"""
|
|
|
|
id: int = Field(..., description="castad 잡 ID")
|
|
status: F1StatusLiteral = Field(..., description="진행 상태")
|
|
stage: Optional[str] = Field(None, description="현재 스테이지 (진행 중에만)")
|
|
stages: Optional[dict] = Field(
|
|
None, description="스테이지별 상태 (P2V 원형 그대로 — 진행 중에만)"
|
|
)
|
|
name: Optional[str] = Field(None, description="행사명")
|
|
narration: Optional[list[str]] = Field(None, description="나레이션 3문장 (검수 대상)")
|
|
metadata: Optional[P2vMetadata] = Field(None, description="행사 메타데이터 (검수 대상)")
|
|
motion_elements: Optional[list[str]] = Field(None, description="채택된 모션 요소")
|
|
duration: Optional[float] = Field(None, description="완성 영상 길이(초)")
|
|
queue_size: Optional[int] = Field(None, description="P2V 대기열 크기 (진행 중에만)")
|
|
error: Optional[P2vJobError] = Field(None, description="실패 정보")
|
|
artifacts: dict[str, str] = Field(
|
|
default_factory=dict,
|
|
description="산출물 URL — video/thumbnail 은 Blob, check_jpg 는 프록시 경로",
|
|
)
|
|
|
|
|
|
class NarrationUpdateRequest(BaseModel):
|
|
"""검수 중 나레이션 수정"""
|
|
|
|
narration: list[str] = Field(
|
|
..., min_length=1, max_length=5, description="수정한 나레이션 문장 목록"
|
|
)
|
|
|
|
|
|
class MetadataUpdateRequest(BaseModel):
|
|
"""검수 중 행사 메타데이터 수정"""
|
|
|
|
event_name: str = Field(..., max_length=200, description="행사명")
|
|
date_text: str = Field(default="", max_length=100, description="일시 표기")
|
|
place: str = Field(..., max_length=200, description="장소")
|
|
|
|
|
|
class ApproveRequest(BaseModel):
|
|
"""검수 승인/재시도. motions 를 주면 모션 선정을 덮어쓴다"""
|
|
|
|
motions: Optional[list[str]] = Field(
|
|
None, description="모션 키 목록 (None 이면 서버 선정 유지)"
|
|
)
|
|
|
|
|
|
# =============================================================================
|
|
# F2 — 포스터 스타일링
|
|
# =============================================================================
|
|
|
|
|
|
class P2vF2StatusResponse(BaseModel):
|
|
"""F2 잡 상태 (폴링 응답)"""
|
|
|
|
id: int = Field(..., description="castad 잡 ID")
|
|
status: F2StatusLiteral = Field(..., description="진행 상태")
|
|
stage: Optional[str] = Field(None, description="현재 스테이지 (진행 중에만)")
|
|
stages: Optional[dict] = Field(None, description="스테이지별 상태 (진행 중에만)")
|
|
template_id: Optional[str] = Field(None, description="사용한 템플릿 id")
|
|
queue_size: Optional[int] = Field(None, description="P2V 대기열 크기 (진행 중에만)")
|
|
error: Optional[P2vJobError] = Field(None, description="실패 정보")
|
|
artifacts: dict[str, str] = Field(
|
|
default_factory=dict, description="산출물 URL — image 는 Blob 공개 URL"
|
|
)
|
|
|
|
|
|
class F2TemplateResponse(BaseModel):
|
|
"""스타일 템플릿 (P2V 목록 중계 — thumb_url 은 castad 프록시 경로로 재작성됨)"""
|
|
|
|
id: str = Field(..., description="템플릿 id")
|
|
name_ko: str = Field(..., description="한국어 이름")
|
|
thumb_url: str = Field(..., description="썸네일 (castad 프록시 경로 /p2v/files/...)")
|
|
category: str = Field(..., description="카테고리 id")
|
|
license: str = Field(..., description="배포 등급 (public-domain/internal-only/user-uploaded)")
|
|
attribution: str = Field(default="", description="출처 표기")
|
|
license_note: str = Field(default="", description="등급 설명")
|
|
removable: bool = Field(default=False, description="사용자 레퍼런스 여부 (삭제 가능)")
|
|
small_ref: bool = Field(default=False, description="저해상 레퍼런스 경고 여부")
|
|
|
|
|
|
class F2CategoryResponse(BaseModel):
|
|
"""템플릿 카테고리"""
|
|
|
|
id: str = Field(..., description="카테고리 id")
|
|
label: str = Field(..., description="표시 이름")
|
|
|
|
|
|
class F2FormatResponse(BaseModel):
|
|
"""출력 포맷"""
|
|
|
|
id: str = Field(..., description="포맷 id")
|
|
label: str = Field(..., description="표시 이름")
|
|
|
|
|
|
class F2UploadHintResponse(BaseModel):
|
|
"""사용자 레퍼런스 업로드 안내"""
|
|
|
|
enabled: bool = Field(..., description="업로드 허용 여부")
|
|
min_long_edge: int = Field(..., description="권장 최소 긴 변(px)")
|
|
ref_long_edge: Optional[int] = Field(None, description="분석 시 리사이즈 기준(px)")
|
|
|
|
|
|
__all__ = [
|
|
"F1StatusLiteral",
|
|
"F2StatusLiteral",
|
|
"FormatLiteral",
|
|
"P2vJobCreateResponse",
|
|
"P2vJobError",
|
|
"P2vMetadata",
|
|
"P2vDeleteResponse",
|
|
"P2vF1StatusResponse",
|
|
"NarrationUpdateRequest",
|
|
"MetadataUpdateRequest",
|
|
"ApproveRequest",
|
|
"P2vF2StatusResponse",
|
|
"F2TemplateResponse",
|
|
"F2CategoryResponse",
|
|
"F2FormatResponse",
|
|
"F2UploadHintResponse",
|
|
]
|