from datetime import datetime from pydantic import BaseModel, Field, field_validator class ApiVideoContentPart(BaseModel): """视频生成内容部分:文本/图片/视频/音频参考。""" type: str = Field(..., description="内容类型: text | image_url | video_url | audio_url") text: str | None = None image_url: dict | None = Field(None, description="图片URL对象: {\"url\": \"...\"}") video_url: dict | None = Field(None, description="视频URL对象: {\"url\": \"...\"}") audio_url: dict | None = Field(None, description="音频URL对象: {\"url\": \"...\"}") role: str | None = Field( None, description="参考角色: first_frame | last_frame | reference_image | reference_video | reference_audio", ) @field_validator("type") @classmethod def validate_type(cls, v): allowed = {"text", "image_url", "video_url", "audio_url"} if v not in allowed: raise ValueError(f"type 必须是 {allowed} 之一,当前值: {v}") return v @field_validator("role") @classmethod def validate_role(cls, v, info): if v is None: return v type_value = info.data.get("type") role_map = { "image_url": {"first_frame", "last_frame", "reference_image"}, "video_url": {"reference_video"}, "audio_url": {"reference_audio"}, } allowed_roles = role_map.get(type_value, set()) if v not in allowed_roles: raise ValueError( f"type={type_value} 时 role 必须是 {allowed_roles} 之一,当前值: {v}" ) return v @field_validator("image_url") @classmethod def validate_image_url(cls, v, info): if v is None: return v type_value = info.data.get("type") if type_value == "image_url" and (not v or not v.get("url")): raise ValueError("type=image_url 时 image_url.url 不能为空") return v @field_validator("video_url") @classmethod def validate_video_url(cls, v, info): if v is None: return v type_value = info.data.get("type") if type_value == "video_url" and (not v or not v.get("url")): raise ValueError("type=video_url 时 video_url.url 不能为空") return v @field_validator("audio_url") @classmethod def validate_audio_url(cls, v, info): if v is None: return v type_value = info.data.get("type") if type_value == "audio_url" and (not v or not v.get("url")): raise ValueError("type=audio_url 时 audio_url.url 不能为空") return v class ApiVideoCreateRequest(BaseModel): """视频生成请求。支持全量 Volcano Ark SDK 参数。""" model: str = Field(..., description="模型名称, 如 doubao-seedance-2-0-260128") content: list[ApiVideoContentPart] = Field( ..., min_length=1, description="生成内容: 文本提示词 + 可选的图片/视频/音频参考" ) ratio: str | None = Field("16:9", description="视频比例: 16:9 | 9:16 | 1:1 | 4:3 | 3:4 | 21:9") duration: int | None = Field(5, ge=3, le=30, description="视频时长(秒): 3-30") resolution: str | None = Field("480p", description="分辨率: 480p | 720p | 1080p") generate_audio: bool | None = Field(True, description="是否生成音频") watermark: bool | None = Field(False, description="是否添加水印") idempotency_key: str | None = Field(None, description="幂等键,防止重复创建") @field_validator("ratio") @classmethod def validate_ratio(cls, v): if v is None: return v allowed = {"16:9", "9:16", "1:1", "4:3", "3:4", "21:9"} if v not in allowed: raise ValueError(f"ratio 必须是 {allowed} 之一,当前值: {v}") return v @field_validator("resolution") @classmethod def validate_resolution(cls, v): if v is None: return v allowed = {"480p", "720p", "1080p"} if v not in allowed: raise ValueError(f"resolution 必须是 {allowed} 之一,当前值: {v}") return v class ApiVideoCreateResponse(BaseModel): """视频任务创建响应。""" id: str = Field(..., description="任务ID") class ApiVideoContent(BaseModel): """视频内容(成功时返回)。""" video_url: str = Field(..., description="视频URL") class ApiVideoStatusResponse(BaseModel): """视频任务状态查询响应。""" id: str = Field(..., description="任务ID") model: str = Field(..., description="模型名称") status: str = Field(..., description="任务状态: queued | running | succeeded | failed | expired") created_at: int = Field(..., description="创建时间戳(Unix)") updated_at: int = Field(..., description="更新时间戳(Unix)") content: ApiVideoContent | None = Field(None, description="视频内容(成功时返回)") duration: int | None = Field(None, description="视频时长(秒)") ratio: str | None = Field(None, description="视频比例") resolution: str | None = Field(None, description="分辨率") error: str | None = Field(None, description="错误信息(失败时返回)")