Skip to main content

xai_rust/models/
videos.rs

1//! Video generation and editing types.
2
3use serde::{Deserialize, Serialize};
4
5/// Request to generate a new video.
6#[derive(Debug, Clone, Serialize)]
7pub struct VideoGenerationRequest {
8    /// The model to use for generation.
9    pub model: String,
10    /// Prompt for video generation.
11    pub prompt: String,
12    /// Optional output duration in seconds.
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub duration_seconds: Option<u32>,
15    /// Optional result container format.
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub response_format: Option<VideoResponseFormat>,
18    /// Optional number of generated outputs.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub n: Option<u32>,
21}
22
23impl VideoGenerationRequest {
24    /// Create a generator request.
25    pub fn new(model: impl Into<String>, prompt: impl Into<String>) -> Self {
26        Self {
27            model: model.into(),
28            prompt: prompt.into(),
29            duration_seconds: None,
30            response_format: None,
31            n: None,
32        }
33    }
34
35    /// Set output duration.
36    pub fn duration_seconds(mut self, duration_seconds: u32) -> Self {
37        self.duration_seconds = Some(duration_seconds);
38        self
39    }
40
41    /// Set response format.
42    pub fn response_format(mut self, format: VideoResponseFormat) -> Self {
43        self.response_format = Some(format);
44        self
45    }
46
47    /// Set number of outputs.
48    pub fn n(mut self, n: u32) -> Self {
49        self.n = Some(n);
50        self
51    }
52}
53
54/// Request to edit an existing video.
55#[derive(Debug, Clone, Serialize)]
56pub struct VideoEditRequest {
57    /// The model to use for editing.
58    pub model: String,
59    /// The source video to edit.
60    pub video: String,
61    /// Prompt describing edits.
62    pub prompt: String,
63    /// Optional output duration in seconds.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub duration_seconds: Option<u32>,
66    /// Optional output format.
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub response_format: Option<VideoResponseFormat>,
69}
70
71impl VideoEditRequest {
72    /// Create an edit request.
73    pub fn new(
74        model: impl Into<String>,
75        video: impl Into<String>,
76        prompt: impl Into<String>,
77    ) -> Self {
78        Self {
79            model: model.into(),
80            video: video.into(),
81            prompt: prompt.into(),
82            duration_seconds: None,
83            response_format: None,
84        }
85    }
86
87    /// Set output duration.
88    pub fn duration_seconds(mut self, duration_seconds: u32) -> Self {
89        self.duration_seconds = Some(duration_seconds);
90        self
91    }
92
93    /// Set response format.
94    pub fn response_format(mut self, format: VideoResponseFormat) -> Self {
95        self.response_format = Some(format);
96        self
97    }
98}
99
100/// Response format for generated/edited video.
101#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103pub enum VideoResponseFormat {
104    /// URL representation.
105    #[default]
106    Url,
107    /// Base64-encoded video bytes.
108    B64Json,
109}
110
111/// Response for video generation/editing jobs.
112#[derive(Debug, Clone, Deserialize)]
113pub struct VideoGenerationResponse {
114    /// Generated/edited items.
115    pub data: Vec<VideoData>,
116    /// Optional creation timestamp.
117    #[serde(default)]
118    pub created: Option<i64>,
119}
120
121impl VideoGenerationResponse {
122    /// Get the first item URL if available.
123    pub fn first_url(&self) -> Option<&str> {
124        self.data.first().and_then(|v| v.url.as_deref())
125    }
126
127    /// Get the first item base64 value if available.
128    pub fn first_base64(&self) -> Option<&str> {
129        self.data.first().and_then(|v| v.b64_json.as_deref())
130    }
131}
132
133/// A generated or edited video item.
134#[derive(Debug, Clone, Deserialize)]
135pub struct VideoData {
136    /// Public URL for video output.
137    #[serde(default)]
138    pub url: Option<String>,
139    /// Base64 representation.
140    #[serde(default)]
141    pub b64_json: Option<String>,
142}
143
144/// Video object returned by `/videos/{id}`.
145#[derive(Debug, Clone, Deserialize)]
146pub struct Video {
147    /// Video identifier.
148    pub id: String,
149    /// Current status.
150    #[serde(default)]
151    pub status: Option<String>,
152    /// Optional output URL.
153    #[serde(default)]
154    pub url: Option<String>,
155    /// Optional object tag.
156    #[serde(default)]
157    pub object: String,
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn video_generation_request_helpers() {
166        let request = VideoGenerationRequest::new("grok-video", "A cat")
167            .duration_seconds(4)
168            .response_format(VideoResponseFormat::Url)
169            .n(2);
170
171        assert_eq!(request.model, "grok-video");
172        assert_eq!(request.duration_seconds, Some(4));
173        assert_eq!(request.n, Some(2));
174        assert_eq!(request.response_format, Some(VideoResponseFormat::Url));
175    }
176
177    #[test]
178    fn video_edit_request_helpers() {
179        let request = VideoEditRequest::new("grok-video", "video-id", "slow motion")
180            .duration_seconds(4)
181            .response_format(VideoResponseFormat::B64Json);
182        assert_eq!(request.video, "video-id");
183        assert_eq!(request.duration_seconds, Some(4));
184    }
185
186    #[test]
187    fn video_response_helpers() {
188        let response = VideoGenerationResponse {
189            created: Some(1),
190            data: vec![VideoData {
191                url: Some("https://example.com/video.mp4".to_string()),
192                b64_json: Some("b64".to_string()),
193            }],
194        };
195        assert_eq!(response.first_url(), Some("https://example.com/video.mp4"));
196    }
197}