136 lines
5.2 KiB
Python
136 lines
5.2 KiB
Python
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="错误信息(失败时返回)")
|