use serde::Serialize;
use validator::*;
use super::super::traits::*;
#[derive(Debug, Clone, Validate, Serialize)]
#[validate(schema(function = "validate_prompt_or_image"))]
pub struct VideoBody<N>
where
N: ModelName + Serialize,
{
pub model: N,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_url: Option<ImageUrl>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(max = 1500))]
pub prompt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quality: Option<VideoQuality>,
#[serde(skip_serializing_if = "Option::is_none")]
pub with_audio: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub watermark_enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<VideoSize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fps: Option<Fps>,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration: Option<VideoDuration>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(length(min = 6, max = 128))]
pub user_id: Option<String>,
}
impl<N> VideoBody<N>
where
N: ModelName + Serialize,
{
pub fn new(model: N) -> Self {
Self {
model,
prompt: None,
quality: None,
with_audio: None,
watermark_enabled: None,
image_url: None,
size: None,
fps: None,
duration: None,
request_id: None,
user_id: None,
}
}
pub fn with_prompt(mut self, prompt: impl Into<String>) -> Self {
self.prompt = Some(prompt.into());
self
}
pub fn with_quality(mut self, quality: VideoQuality) -> Self {
self.quality = Some(quality);
self
}
pub fn with_audio(mut self, with_audio: bool) -> Self {
self.with_audio = Some(with_audio);
self
}
pub fn with_watermark_enabled(mut self, watermark_enabled: bool) -> Self {
self.watermark_enabled = Some(watermark_enabled);
self
}
pub fn with_image_url(mut self, image_url: ImageUrl) -> Self {
self.image_url = Some(image_url);
self
}
pub fn with_size(mut self, size: VideoSize) -> Self {
self.size = Some(size);
self
}
pub fn with_fps(mut self, fps: Fps) -> Self {
self.fps = Some(fps);
self
}
pub fn with_duration(mut self, duration: VideoDuration) -> Self {
self.duration = Some(duration);
self
}
pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
self.request_id = Some(request_id.into());
self
}
pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
self.user_id = Some(user_id.into());
self
}
pub fn prompt_only(model: N, prompt: impl Into<String>) -> Self {
Self::new(model).with_prompt(prompt)
}
pub fn with_single_image(
model: N,
image_url: impl Into<String>,
prompt: impl Into<String>,
) -> Self {
Self::new(model)
.with_image_url(ImageUrl::from_url(image_url))
.with_prompt(prompt)
}
pub fn with_multiple_images(
model: N,
mut image_urls: Vec<impl Into<String>>,
prompt: impl Into<String>,
) -> Result<Self, crate::ZaiError> {
let image_url = if image_urls.len() == 1 {
ImageUrl::from_url(image_urls.remove(0))
} else if image_urls.len() == 2 {
ImageUrl::from_two_urls(image_urls.remove(0), image_urls.remove(0))
} else {
return Err(crate::ZaiError::ApiError {
code: crate::client::error::codes::SDK_VALIDATION,
message: "with_multiple_images requires 1 or 2 URLs".to_string(),
});
};
Ok(Self::new(model)
.with_image_url(image_url)
.with_prompt(prompt))
}
pub fn validate_body(&self) -> crate::ZaiResult<()> {
self.validate()
.map_err(crate::client::error::ZaiError::from)
}
}
fn validate_prompt_or_image<N>(body: &VideoBody<N>) -> Result<(), validator::ValidationError>
where
N: ModelName + Serialize,
{
let has_prompt = body.prompt.as_ref().map(|s| !s.is_empty()).unwrap_or(false);
let has_image = body.image_url.is_some();
if has_prompt || has_image {
Ok(())
} else {
Err(validator::ValidationError::new("prompt_or_image_required"))
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum VideoQuality {
Speed,
Quality,
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum ImageUrl {
Base64(String),
VecUrl(Vec<String>),
}
impl ImageUrl {
pub fn base64(data: impl Into<String>) -> Self {
ImageUrl::Base64(data.into())
}
pub fn from_url(url: impl Into<String>) -> Self {
ImageUrl::VecUrl(vec![url.into()])
}
pub fn from_two_urls(u1: impl Into<String>, u2: impl Into<String>) -> Self {
ImageUrl::VecUrl(vec![u1.into(), u2.into()])
}
}
#[derive(Debug, Clone, Serialize)]
pub enum VideoSize {
#[serde(rename = "1280x720")]
Size1280x720,
#[serde(rename = "720x1280")]
Size720x1280,
#[serde(rename = "1024x1024")]
Size1024x1024,
#[serde(rename = "1920x1080")]
Size1920x1080,
#[serde(rename = "1080x1920")]
Size1080x1920,
#[serde(rename = "2048x1080")]
Size2048x1080,
#[serde(rename = "3840x2160")]
Size3840x2160,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Fps {
Fps30,
Fps60,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum VideoDuration {
Duration5,
Duration10,
}