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 balance_after: i64,
42
43 #[serde(default)]
45 pub request_id: String,
46}
47
48#[derive(Debug, Clone, Deserialize)]
50pub struct GeneratedVideo {
51 pub base64: String,
53
54 pub format: String,
56
57 pub size_bytes: i64,
59
60 pub index: i32,
62}
63
64#[derive(Debug, Clone, Deserialize)]
70pub struct JobResponse {
71 pub job_id: String,
73
74 #[serde(default)]
76 pub status: String,
77
78 #[serde(default)]
80 pub cost_ticks: i64,
81
82 #[serde(flatten)]
84 pub extra: HashMap<String, serde_json::Value>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, Default)]
93pub struct StudioClip {
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub avatar_id: Option<String>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub voice_id: Option<String>,
101
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub script: Option<String>,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
108 pub background: Option<serde_json::Value>,
109}
110
111#[derive(Debug, Clone, Serialize, Default)]
113pub struct VideoStudioRequest {
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub title: Option<String>,
117
118 pub clips: Vec<StudioClip>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub dimension: Option<String>,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub aspect_ratio: Option<String>,
128}
129
130pub type StudioVideoRequest = VideoStudioRequest;
136
137#[derive(Debug, Clone, Serialize, Default)]
139pub struct VideoTranslateRequest {
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub video_url: Option<String>,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
146 pub video_base64: Option<String>,
147
148 pub target_language: String,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub source_language: Option<String>,
154}
155
156pub type TranslateRequest = VideoTranslateRequest;
158
159#[derive(Debug, Clone, Serialize, Default)]
165pub struct PhotoAvatarRequest {
166 pub photo_base64: String,
168
169 pub script: String,
171
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub voice_id: Option<String>,
175
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub aspect_ratio: Option<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Default)]
187pub struct DigitalTwinRequest {
188 pub avatar_id: String,
190
191 pub script: String,
193
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub voice_id: Option<String>,
197
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub aspect_ratio: Option<String>,
201}
202
203#[derive(Debug, Clone, Deserialize)]
209pub struct Avatar {
210 pub avatar_id: String,
212
213 #[serde(default)]
215 pub name: Option<String>,
216
217 #[serde(default)]
219 pub gender: Option<String>,
220
221 #[serde(default)]
223 pub preview_url: Option<String>,
224
225 #[serde(flatten)]
227 pub extra: HashMap<String, serde_json::Value>,
228}
229
230#[derive(Debug, Clone, Deserialize)]
232pub struct AvatarsResponse {
233 pub avatars: Vec<Avatar>,
234}
235
236#[derive(Debug, Clone, Deserialize)]
242pub struct VideoTemplate {
243 pub template_id: String,
245
246 #[serde(default)]
248 pub name: Option<String>,
249
250 #[serde(default)]
252 pub preview_url: Option<String>,
253
254 #[serde(flatten)]
256 pub extra: HashMap<String, serde_json::Value>,
257}
258
259#[derive(Debug, Clone, Deserialize)]
261pub struct VideoTemplatesResponse {
262 pub templates: Vec<VideoTemplate>,
263}
264
265#[derive(Debug, Clone, Deserialize)]
271pub struct HeyGenAvatarsResponse {
272 #[serde(default)]
274 pub avatars: Vec<serde_json::Value>,
275
276 #[serde(default)]
278 pub request_id: String,
279}
280
281#[derive(Debug, Clone, Deserialize)]
283pub struct HeyGenTemplatesResponse {
284 #[serde(default)]
286 pub templates: Vec<serde_json::Value>,
287
288 #[serde(default)]
290 pub request_id: String,
291}
292
293#[derive(Debug, Clone, Deserialize)]
299pub struct HeyGenVoice {
300 pub voice_id: String,
302
303 #[serde(default)]
305 pub name: Option<String>,
306
307 #[serde(default)]
309 pub language: Option<String>,
310
311 #[serde(default)]
313 pub gender: Option<String>,
314
315 #[serde(flatten)]
317 pub extra: HashMap<String, serde_json::Value>,
318}
319
320#[derive(Debug, Clone, Deserialize)]
322pub struct HeyGenVoicesResponse {
323 pub voices: Vec<HeyGenVoice>,
324}
325
326impl Client {
331 pub async fn generate_video(&self, req: &VideoRequest) -> Result<VideoResponse> {
336 let (mut resp, meta) = self
337 .post_json::<VideoRequest, VideoResponse>("/qai/v1/video/generate", req)
338 .await?;
339 if resp.cost_ticks == 0 {
340 resp.cost_ticks = meta.cost_ticks;
341 }
342 if resp.request_id.is_empty() {
343 resp.request_id = meta.request_id;
344 }
345 Ok(resp)
346 }
347
348 pub async fn video_studio(&self, req: &VideoStudioRequest) -> Result<JobResponse> {
350 let (resp, _meta) = self
351 .post_json::<VideoStudioRequest, JobResponse>("/qai/v1/video/studio", req)
352 .await?;
353 Ok(resp)
354 }
355
356 pub async fn video_translate(&self, req: &VideoTranslateRequest) -> Result<JobResponse> {
358 let (resp, _meta) = self
359 .post_json::<VideoTranslateRequest, JobResponse>("/qai/v1/video/translate", req)
360 .await?;
361 Ok(resp)
362 }
363
364 pub async fn video_photo_avatar(&self, req: &PhotoAvatarRequest) -> Result<JobResponse> {
366 let (resp, _meta) = self
367 .post_json::<PhotoAvatarRequest, JobResponse>("/qai/v1/video/photo-avatar", req)
368 .await?;
369 Ok(resp)
370 }
371
372 pub async fn video_digital_twin(&self, req: &DigitalTwinRequest) -> Result<JobResponse> {
374 let (resp, _meta) = self
375 .post_json::<DigitalTwinRequest, JobResponse>("/qai/v1/video/digital-twin", req)
376 .await?;
377 Ok(resp)
378 }
379
380 pub async fn video_avatars(&self) -> Result<AvatarsResponse> {
382 let (resp, _meta) = self
383 .get_json::<AvatarsResponse>("/qai/v1/video/avatars")
384 .await?;
385 Ok(resp)
386 }
387
388 pub async fn video_templates(&self) -> Result<VideoTemplatesResponse> {
390 let (resp, _meta) = self
391 .get_json::<VideoTemplatesResponse>("/qai/v1/video/templates")
392 .await?;
393 Ok(resp)
394 }
395
396 pub async fn video_heygen_voices(&self) -> Result<HeyGenVoicesResponse> {
398 let (resp, _meta) = self
399 .get_json::<HeyGenVoicesResponse>("/qai/v1/video/heygen-voices")
400 .await?;
401 Ok(resp)
402 }
403}