use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::client::Client;
use crate::error::Result;
#[derive(Debug, Clone, Serialize, Default)]
pub struct VideoRequest {
pub model: String,
pub prompt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VideoResponse {
pub videos: Vec<GeneratedVideo>,
pub model: String,
#[serde(default)]
pub cost_ticks: i64,
#[serde(default)]
pub balance_after: i64,
#[serde(default)]
pub request_id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct GeneratedVideo {
pub base64: String,
pub format: String,
pub size_bytes: i64,
pub index: i32,
}
#[derive(Debug, Clone, Deserialize)]
pub struct JobResponse {
pub job_id: String,
#[serde(default)]
pub status: String,
#[serde(default)]
pub cost_ticks: i64,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StudioClip {
#[serde(skip_serializing_if = "Option::is_none")]
pub avatar_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub voice_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub script: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct VideoStudioRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
pub clips: Vec<StudioClip>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dimension: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
}
pub type StudioVideoRequest = VideoStudioRequest;
#[derive(Debug, Clone, Serialize, Default)]
pub struct VideoTranslateRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub video_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub video_base64: Option<String>,
pub target_language: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_language: Option<String>,
}
pub type TranslateRequest = VideoTranslateRequest;
#[derive(Debug, Clone, Serialize, Default)]
pub struct PhotoAvatarRequest {
pub photo_base64: String,
pub script: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub voice_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct DigitalTwinRequest {
pub avatar_id: String,
pub script: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub voice_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub aspect_ratio: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Avatar {
pub avatar_id: String,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub gender: Option<String>,
#[serde(default)]
pub preview_url: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AvatarsResponse {
pub avatars: Vec<Avatar>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VideoTemplate {
pub template_id: String,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub preview_url: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct VideoTemplatesResponse {
pub templates: Vec<VideoTemplate>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HeyGenAvatarsResponse {
#[serde(default)]
pub avatars: Vec<serde_json::Value>,
#[serde(default)]
pub request_id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HeyGenTemplatesResponse {
#[serde(default)]
pub templates: Vec<serde_json::Value>,
#[serde(default)]
pub request_id: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HeyGenVoice {
pub voice_id: String,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub language: Option<String>,
#[serde(default)]
pub gender: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HeyGenVoicesResponse {
pub voices: Vec<HeyGenVoice>,
}
impl Client {
pub async fn generate_video(&self, req: &VideoRequest) -> Result<VideoResponse> {
let (mut resp, meta) = self
.post_json::<VideoRequest, VideoResponse>("/qai/v1/video/generate", req)
.await?;
if resp.cost_ticks == 0 {
resp.cost_ticks = meta.cost_ticks;
}
if resp.request_id.is_empty() {
resp.request_id = meta.request_id;
}
Ok(resp)
}
pub async fn video_studio(&self, req: &VideoStudioRequest) -> Result<JobResponse> {
let (resp, _meta) = self
.post_json::<VideoStudioRequest, JobResponse>("/qai/v1/video/studio", req)
.await?;
Ok(resp)
}
pub async fn video_translate(&self, req: &VideoTranslateRequest) -> Result<JobResponse> {
let (resp, _meta) = self
.post_json::<VideoTranslateRequest, JobResponse>("/qai/v1/video/translate", req)
.await?;
Ok(resp)
}
pub async fn video_photo_avatar(&self, req: &PhotoAvatarRequest) -> Result<JobResponse> {
let (resp, _meta) = self
.post_json::<PhotoAvatarRequest, JobResponse>("/qai/v1/video/photo-avatar", req)
.await?;
Ok(resp)
}
pub async fn video_digital_twin(&self, req: &DigitalTwinRequest) -> Result<JobResponse> {
let (resp, _meta) = self
.post_json::<DigitalTwinRequest, JobResponse>("/qai/v1/video/digital-twin", req)
.await?;
Ok(resp)
}
pub async fn video_avatars(&self) -> Result<AvatarsResponse> {
let (resp, _meta) = self
.get_json::<AvatarsResponse>("/qai/v1/video/avatars")
.await?;
Ok(resp)
}
pub async fn video_templates(&self) -> Result<VideoTemplatesResponse> {
let (resp, _meta) = self
.get_json::<VideoTemplatesResponse>("/qai/v1/video/templates")
.await?;
Ok(resp)
}
pub async fn video_heygen_voices(&self) -> Result<HeyGenVoicesResponse> {
let (resp, _meta) = self
.get_json::<HeyGenVoicesResponse>("/qai/v1/video/heygen-voices")
.await?;
Ok(resp)
}
}