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