from __future__ import annotations from typing import Any from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from app.enums.common import ( VIDEO_SCHEMA_EDITABLE_TEXT_MAX_LEN, VIDEO_SCHEMA_FLOW_CONTENT_MAX_LEN, VIDEO_SCHEMA_TIME_DESC_MAX_LEN, VIDEO_SCHEMA_TIME_STAGE_MAX_LEN, ) from app.schemas.common import NaiveDatetimeOptional class VideoPromptSchemaConfigSaveRequest(BaseModel): """保存管理后台视频提词 Schema 配置。""" model_config = ConfigDict(extra="forbid") is_enabled: bool = Field(True, description="是否启用后台自定义配置;关闭后生成时回落默认 CLIENT_SCHEMA_V1") data: dict[str, Any] = Field(..., description="管理后台配置数据,包含 sections、time_plan_rules 等。管理字段不会传给 AI") @field_validator("data") @classmethod def _validate_data(cls, value: dict[str, Any]) -> dict[str, Any]: if not isinstance(value, dict) or not value: raise ValueError("data 必须是非空 JSON 对象") return value class VideoPromptSchemaConfigImportRequest(VideoPromptSchemaConfigSaveRequest): """导入 JSON 配置。""" class VideoPromptSchemaPreviewRequest(BaseModel): """预览指定配置在某个视频规格下的运行时 schema。""" model_config = ConfigDict(extra="forbid") is_enabled: bool = Field(True, description="是否按自定义配置预览;关闭时按默认配置预览") data: dict[str, Any] = Field(..., description="待预览的管理后台配置 JSON") duration: int = Field(8, ge=1, le=3600, description="视频时长,单位秒") aspect_ratio: str = Field("9:16", min_length=1, max_length=16, description="视频比例,例如 9:16") resolution: str = Field("1080p", min_length=1, max_length=32, description="清晰度,例如 720p/1080p") frame_rate: str = Field("30fps", min_length=1, max_length=32, description="帧率,例如 30fps") supported_durations: list[int] = Field(default_factory=list, description="支持时长列表,仅用于预览输出规格限制") supported_ratios: list[str] = Field(default_factory=list, description="支持比例列表,仅用于预览输出规格限制") supported_resolutions: list[str] = Field(default_factory=list, description="支持分辨率列表,仅用于预览输出规格限制") @model_validator(mode="after") def _ensure_data(self) -> "VideoPromptSchemaPreviewRequest": if not self.data: raise ValueError("data 不能为空") return self def to_video_config(self) -> dict[str, Any]: return { "duration": self.duration, "aspect_ratio": self.aspect_ratio, "resolution": self.resolution, "frame_rate": self.frame_rate, "supported_durations": self.supported_durations, "supported_ratios": self.supported_ratios, "supported_resolutions": self.supported_resolutions, } class VideoPromptSchemaConfigOut(BaseModel): """管理后台视频提词 Schema 配置详情。""" id: str | None = Field(None, description="system_configs.id;未保存时为空") key: str = Field(..., description="system_configs.key") description: str | None = Field(None, description="配置说明") is_enabled: bool = Field(False, description="当前是否启用数据库自定义配置") using_default: bool = Field(False, description="当前是否正在使用默认 CLIENT_SCHEMA_V1 兜底") data: dict[str, Any] = Field(..., description="当前管理后台配置数据") default_data: dict[str, Any] = Field(..., description="由 CLIENT_SCHEMA_V1 转换得到的默认配置") created_at: NaiveDatetimeOptional = None updated_at: NaiveDatetimeOptional = None class VideoPromptSchemaExportOut(BaseModel): version: str key: str is_enabled: bool data: dict[str, Any] class VideoPromptSchemaPreviewOut(BaseModel): schema_config_snapshot: dict[str, Any] = Field(..., description="本次预览使用的配置快照") runtime_schema: dict[str, Any] = Field(..., description="传给 AI 的纯运行时 schema,不包含 label/editable/enabled 等管理字段") time_plan: list[dict[str, Any]] = Field(default_factory=list, description="根据秒数切片规则生成的动态时间规划") class VideoPromptSchemaLengthLimitsOut(BaseModel): editable_text_max_len: int = VIDEO_SCHEMA_EDITABLE_TEXT_MAX_LEN flow_content_max_len: int = VIDEO_SCHEMA_FLOW_CONTENT_MAX_LEN time_stage_max_len: int = VIDEO_SCHEMA_TIME_STAGE_MAX_LEN time_desc_max_len: int = VIDEO_SCHEMA_TIME_DESC_MAX_LEN