Skip to main content

openai_tools/videos/
response.rs

1//! OpenAI Videos API Response Module
2//!
3//! Response types returned by the Videos API (`/v1/videos`).
4
5use serde::{Deserialize, Serialize};
6
7/// Lifecycle status of a video generation job.
8///
9/// Unrecognised values deserialize into [`Other`](VideoStatus::Other) so that a
10/// new server-side status does not break existing clients.
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13#[non_exhaustive]
14pub enum VideoStatus {
15    /// Job accepted and waiting to start
16    Queued,
17    /// Job is currently generating
18    InProgress,
19    /// Job finished successfully; assets are downloadable
20    Completed,
21    /// Job failed; see [`Video::error`]
22    Failed,
23    /// A status this library does not know about yet
24    #[serde(untagged)]
25    Other(String),
26}
27
28/// Error details attached to a failed video job.
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct VideoError {
31    /// Machine-readable error code
32    pub code: String,
33    /// Human-readable description
34    pub message: String,
35}
36
37/// A video generation job.
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Video {
40    /// Unique identifier for the job
41    pub id: String,
42    /// Object type, always "video"
43    pub object: String,
44    /// The model used for generation
45    pub model: String,
46    /// Current lifecycle status
47    pub status: VideoStatus,
48    /// Approximate completion percentage (0-100)
49    #[serde(default)]
50    pub progress: u32,
51    /// The prompt used for generation
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub prompt: Option<String>,
54    /// Unix timestamp (seconds) when the job was created
55    pub created_at: i64,
56    /// Unix timestamp (seconds) when the job finished, if it has
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub completed_at: Option<i64>,
59    /// Unix timestamp (seconds) when the generated assets expire
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub expires_at: Option<i64>,
62    /// Source video ID when this job is a remix
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub remixed_from_video_id: Option<String>,
65    /// Clip duration in seconds, as returned by the API (a string)
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub seconds: Option<String>,
68    /// Output resolution
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub size: Option<String>,
71    /// Error details when [`status`](Video::status) is
72    /// [`Failed`](VideoStatus::Failed)
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub error: Option<VideoError>,
75}
76
77impl Video {
78    /// Returns `true` once the job has settled - successfully or not.
79    ///
80    /// Use this to end a polling loop.
81    ///
82    /// # Example
83    ///
84    /// ```rust
85    /// use openai_tools::videos::response::{Video, VideoStatus};
86    ///
87    /// let json = r#"{"id":"v1","object":"video","model":"sora-2",
88    ///     "status":"completed","progress":100,"created_at":1}"#;
89    /// let video: Video = serde_json::from_str(json).unwrap();
90    /// assert!(video.is_terminal());
91    /// ```
92    pub fn is_terminal(&self) -> bool {
93        matches!(self.status, VideoStatus::Completed | VideoStatus::Failed)
94    }
95
96    /// Returns `true` only when the job completed successfully and its assets
97    /// can be downloaded.
98    pub fn is_completed(&self) -> bool {
99        self.status == VideoStatus::Completed
100    }
101}
102
103/// A page of video jobs.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct VideoListResponse {
106    /// Object type, always "list"
107    pub object: String,
108    /// The videos in this page
109    pub data: Vec<Video>,
110    /// ID of the first item, for pagination
111    #[serde(default, skip_serializing_if = "Option::is_none")]
112    pub first_id: Option<String>,
113    /// ID of the last item, for pagination
114    #[serde(default, skip_serializing_if = "Option::is_none")]
115    pub last_id: Option<String>,
116    /// Whether more items are available
117    #[serde(default)]
118    pub has_more: bool,
119}
120
121/// Result of deleting a video.
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct DeleteVideoResponse {
124    /// The deleted video identifier
125    pub id: String,
126    /// Object type, always "video.deleted"
127    pub object: String,
128    /// Whether the video was successfully deleted
129    pub deleted: bool,
130}