1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::client::Client;
6use crate::error::Result;
7
8#[derive(Debug, Clone, Serialize, Default)]
10pub struct VideoRequest {
11 pub model: String,
13
14 pub prompt: String,
16
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub duration_seconds: Option<i32>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub aspect_ratio: Option<String>,
24}
25
26#[derive(Debug, Clone, Deserialize)]
28pub struct VideoResponse {
29 pub videos: Vec<GeneratedVideo>,
31
32 pub model: String,
34
35 #[serde(default)]
37 pub cost_ticks: i64,
38
39 #[serde(default)]
41 pub request_id: String,
42}
43
44#[derive(Debug, Clone, Deserialize)]
46pub struct GeneratedVideo {
47 pub base64: String,
49
50 pub format: String,
52
53 pub size_bytes: i64,
55
56 pub index: i32,
58}
59
60#[derive(Debug, Clone, Deserialize)]
66pub struct JobResponse {
67 pub job_id: String,
69
70 #[serde(default)]
72 pub status: String,
73
74 #[serde(default)]
76 pub cost_ticks: i64,
77
78 #[serde(flatten)]
80 pub extra: HashMap<String, serde_json::Value>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, Default)]
89pub struct StudioClip {
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub avatar_id: Option<String>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub voice_id: Option<String>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub script: Option<String>,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub background: Option<serde_json::Value>,
105}
106
107#[derive(Debug, Clone, Serialize, Default)]
109pub struct VideoStudioRequest {
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub title: Option<String>,
113
114 pub clips: Vec<StudioClip>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
119 pub dimension: Option<String>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub aspect_ratio: Option<String>,
124}
125
126pub type StudioVideoRequest = VideoStudioRequest;
132
133#[derive(Debug, Clone, Serialize, Default)]
135pub struct VideoTranslateRequest {
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub video_url: Option<String>,
139
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub video_base64: Option<String>,
143
144 pub target_language: String,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub source_language: Option<String>,
150}
151
152pub type TranslateRequest = VideoTranslateRequest;
154
155#[derive(Debug, Clone, Serialize, Default)]
161pub struct PhotoAvatarRequest {
162 pub photo_base64: String,
164
165 pub script: String,
167
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub voice_id: Option<String>,
171
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub aspect_ratio: Option<String>,
175}
176
177#[derive(Debug, Clone, Serialize, Default)]
183pub struct DigitalTwinRequest {
184 pub avatar_id: String,
186
187 pub script: String,
189
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub voice_id: Option<String>,
193
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub aspect_ratio: Option<String>,
197}
198
199#[derive(Debug, Clone, Deserialize)]
205pub struct Avatar {
206 pub avatar_id: String,
208
209 #[serde(default)]
211 pub name: Option<String>,
212
213 #[serde(default)]
215 pub gender: Option<String>,
216
217 #[serde(default)]
219 pub preview_url: Option<String>,
220
221 #[serde(flatten)]
223 pub extra: HashMap<String, serde_json::Value>,
224}
225
226#[derive(Debug, Clone, Deserialize)]
228pub struct AvatarsResponse {
229 pub avatars: Vec<Avatar>,
230}
231
232#[derive(Debug, Clone, Deserialize)]
238pub struct VideoTemplate {
239 pub template_id: String,
241
242 #[serde(default)]
244 pub name: Option<String>,
245
246 #[serde(default)]
248 pub preview_url: Option<String>,
249
250 #[serde(flatten)]
252 pub extra: HashMap<String, serde_json::Value>,
253}
254
255#[derive(Debug, Clone, Deserialize)]
257pub struct VideoTemplatesResponse {
258 pub templates: Vec<VideoTemplate>,
259}
260
261#[derive(Debug, Clone, Deserialize)]
267pub struct HeyGenAvatarsResponse {
268 #[serde(default)]
270 pub avatars: Vec<serde_json::Value>,
271
272 #[serde(default)]
274 pub request_id: String,
275}
276
277#[derive(Debug, Clone, Deserialize)]
279pub struct HeyGenTemplatesResponse {
280 #[serde(default)]
282 pub templates: Vec<serde_json::Value>,
283
284 #[serde(default)]
286 pub request_id: String,
287}
288
289#[derive(Debug, Clone, Deserialize)]
295pub struct HeyGenVoice {
296 pub voice_id: String,
298
299 #[serde(default)]
301 pub name: Option<String>,
302
303 #[serde(default)]
305 pub language: Option<String>,
306
307 #[serde(default)]
309 pub gender: Option<String>,
310
311 #[serde(flatten)]
313 pub extra: HashMap<String, serde_json::Value>,
314}
315
316#[derive(Debug, Clone, Deserialize)]
318pub struct HeyGenVoicesResponse {
319 pub voices: Vec<HeyGenVoice>,
320}
321
322impl Client {
327 pub async fn generate_video(&self, req: &VideoRequest) -> Result<VideoResponse> {
332 let (mut resp, meta) = self
333 .post_json::<VideoRequest, VideoResponse>("/qai/v1/video/generate", req)
334 .await?;
335 if resp.cost_ticks == 0 {
336 resp.cost_ticks = meta.cost_ticks;
337 }
338 if resp.request_id.is_empty() {
339 resp.request_id = meta.request_id;
340 }
341 Ok(resp)
342 }
343
344 pub async fn video_studio(&self, req: &VideoStudioRequest) -> Result<JobResponse> {
346 let (resp, _meta) = self
347 .post_json::<VideoStudioRequest, JobResponse>("/qai/v1/video/studio", req)
348 .await?;
349 Ok(resp)
350 }
351
352 pub async fn video_translate(&self, req: &VideoTranslateRequest) -> Result<JobResponse> {
354 let (resp, _meta) = self
355 .post_json::<VideoTranslateRequest, JobResponse>("/qai/v1/video/translate", req)
356 .await?;
357 Ok(resp)
358 }
359
360 pub async fn video_photo_avatar(&self, req: &PhotoAvatarRequest) -> Result<JobResponse> {
362 let (resp, _meta) = self
363 .post_json::<PhotoAvatarRequest, JobResponse>("/qai/v1/video/photo-avatar", req)
364 .await?;
365 Ok(resp)
366 }
367
368 pub async fn video_digital_twin(&self, req: &DigitalTwinRequest) -> Result<JobResponse> {
370 let (resp, _meta) = self
371 .post_json::<DigitalTwinRequest, JobResponse>("/qai/v1/video/digital-twin", req)
372 .await?;
373 Ok(resp)
374 }
375
376 pub async fn video_avatars(&self) -> Result<AvatarsResponse> {
378 let (resp, _meta) = self
379 .get_json::<AvatarsResponse>("/qai/v1/video/avatars")
380 .await?;
381 Ok(resp)
382 }
383
384 pub async fn video_templates(&self) -> Result<VideoTemplatesResponse> {
386 let (resp, _meta) = self
387 .get_json::<VideoTemplatesResponse>("/qai/v1/video/templates")
388 .await?;
389 Ok(resp)
390 }
391
392 pub async fn video_heygen_voices(&self) -> Result<HeyGenVoicesResponse> {
394 let (resp, _meta) = self
395 .get_json::<HeyGenVoicesResponse>("/qai/v1/video/heygen-voices")
396 .await?;
397 Ok(resp)
398 }
399}