zai_rs/model/gen_video_async/
video_request.rs1use super::super::traits::*;
2use serde::Serialize;
3use validator::*;
4
5#[derive(Debug, Clone, Validate, Serialize)]
6#[validate(schema(function = "validate_prompt_or_image"))]
7pub struct VideoBody<N>
8where
9 N: ModelName + Serialize,
10{
11 pub model: N,
13 #[serde(skip_serializing_if = "Option::is_none")]
18 pub image_url: Option<ImageUrl>,
19 #[serde(skip_serializing_if = "Option::is_none")]
22 #[validate(length(max = 1500))]
23 pub prompt: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
28 pub quality: Option<VideoQuality>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub with_audio: Option<bool>,
32 #[serde(skip_serializing_if = "Option::is_none")]
36 pub watermark_enabled: Option<bool>,
37 #[serde(skip_serializing_if = "Option::is_none")]
41 pub size: Option<VideoSize>,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub fps: Option<Fps>,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub duration: Option<VideoDuration>,
48 #[serde(skip_serializing_if = "Option::is_none")]
51 pub request_id: Option<String>,
52 #[serde(skip_serializing_if = "Option::is_none")]
55 #[validate(length(min = 6, max = 128))]
56 pub user_id: Option<String>,
57}
58
59impl<N> VideoBody<N>
60where
61 N: ModelName + Serialize,
62{
63 pub fn new(model: N) -> Self {
65 Self {
66 model,
67 prompt: None,
68 quality: None,
69 with_audio: None,
70 watermark_enabled: None,
71 image_url: None,
72 size: None,
73 fps: None,
74 duration: None,
75 request_id: None,
76 user_id: None,
77 }
78 }
79
80 pub fn with_prompt(mut self, prompt: impl Into<String>) -> Self {
82 self.prompt = Some(prompt.into());
83 self
84 }
85
86 pub fn with_quality(mut self, quality: VideoQuality) -> Self {
88 self.quality = Some(quality);
89 self
90 }
91
92 pub fn with_audio(mut self, with_audio: bool) -> Self {
94 self.with_audio = Some(with_audio);
95 self
96 }
97
98 pub fn with_watermark_enabled(mut self, watermark_enabled: bool) -> Self {
100 self.watermark_enabled = Some(watermark_enabled);
101 self
102 }
103
104 pub fn with_image_url(mut self, image_url: ImageUrl) -> Self {
106 self.image_url = Some(image_url);
107 self
108 }
109
110 pub fn with_size(mut self, size: VideoSize) -> Self {
112 self.size = Some(size);
113 self
114 }
115
116 pub fn with_fps(mut self, fps: Fps) -> Self {
118 self.fps = Some(fps);
119 self
120 }
121
122 pub fn with_duration(mut self, duration: VideoDuration) -> Self {
124 self.duration = Some(duration);
125 self
126 }
127
128 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
130 self.request_id = Some(request_id.into());
131 self
132 }
133
134 pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
136 self.user_id = Some(user_id.into());
137 self
138 }
139
140 pub fn prompt_only(model: N, prompt: impl Into<String>) -> Self {
142 Self::new(model).with_prompt(prompt)
143 }
144
145 pub fn with_single_image(
147 model: N,
148 image_url: impl Into<String>,
149 prompt: impl Into<String>,
150 ) -> Self {
151 Self::new(model)
152 .with_image_url(ImageUrl::from_url(image_url))
153 .with_prompt(prompt)
154 }
155
156 pub fn with_multiple_images(
158 model: N,
159 mut image_urls: Vec<impl Into<String>>,
160 prompt: impl Into<String>,
161 ) -> Self {
162 let image_url = if image_urls.len() == 1 {
163 ImageUrl::from_url(image_urls.remove(0))
164 } else if image_urls.len() == 2 {
165 ImageUrl::from_two_urls(image_urls.remove(0), image_urls.remove(0))
166 } else {
167 panic!("with_multiple_images requires 1 or 2 URLs");
168 };
169
170 Self::new(model)
171 .with_image_url(image_url)
172 .with_prompt(prompt)
173 }
174}
175
176#[allow(dead_code)]
178fn validate_prompt_or_image<N>(body: &VideoBody<N>) -> Result<(), validator::ValidationError>
179where
180 N: ModelName + Serialize,
181{
182 let has_prompt = body.prompt.as_ref().map(|s| !s.is_empty()).unwrap_or(false);
183 let has_image = body.image_url.is_some();
184 if has_prompt || has_image {
185 Ok(())
186 } else {
187 Err(validator::ValidationError::new("prompt_or_image_required"))
188 }
189}
190
191#[derive(Debug, Clone, Serialize)]
192#[serde(rename_all = "lowercase")]
193pub enum VideoQuality {
194 Speed,
196 Quality,
198}
199
200#[derive(Debug, Clone, Serialize)]
201#[serde(untagged)]
202pub enum ImageUrl {
203 Base64(String),
205 VecUrl(Vec<String>),
207}
208
209impl ImageUrl {
210 pub fn base64(data: impl Into<String>) -> Self {
212 ImageUrl::Base64(data.into())
213 }
214
215 pub fn from_url(url: impl Into<String>) -> Self {
217 ImageUrl::VecUrl(vec![url.into()])
218 }
219
220 pub fn from_two_urls(u1: impl Into<String>, u2: impl Into<String>) -> Self {
222 ImageUrl::VecUrl(vec![u1.into(), u2.into()])
223 }
224}
225
226#[derive(Debug, Clone, Serialize)]
227pub enum VideoSize {
228 #[serde(rename = "1280x720")]
230 Size1280x720,
231 #[serde(rename = "720x1280")]
233 Size720x1280,
234 #[serde(rename = "1024x1024")]
236 Size1024x1024,
237 #[serde(rename = "1920x1080")]
239 Size1920x1080,
240 #[serde(rename = "1080x1920")]
242 Size1080x1920,
243 #[serde(rename = "2048x1080")]
245 Size2048x1080,
246 #[serde(rename = "3840x2160")]
248 Size3840x2160,
249}
250
251#[derive(Debug, Clone, Serialize)]
252#[serde(rename_all = "lowercase")]
253pub enum Fps {
254 Fps30,
256 Fps60,
258}
259
260#[derive(Debug, Clone, Serialize)]
261#[serde(rename_all = "lowercase")]
262pub enum VideoDuration {
263 Duration5,
265 Duration10,
267}