Skip to main content

musevideo_client/
lib.rs

1//! Typed primitives for assembling Muse Video generation jobs.
2//!
3//! The crate stays deliberately small: a fluent [`VideoRequest`] builder and an
4//! [`AspectRatio`] enum, with optional Serde support behind the `serde` feature.
5//! It models the shape of a job — it does not perform any network I/O.
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10/// Output aspect ratios supported by a generation job.
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub enum AspectRatio {
14    /// 16:9 widescreen video.
15    Widescreen,
16    /// 9:16 vertical video for short-form platforms.
17    Vertical,
18    /// 1:1 square video.
19    Square,
20    /// Custom aspect ratio, such as "4:3" or "21:9".
21    Custom(String),
22}
23
24/// A single video generation job, built up fluently from a prompt.
25#[derive(Debug, Clone, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27pub struct VideoRequest {
28    /// Main text prompt.
29    pub prompt: String,
30    /// Desired aspect ratio.
31    pub aspect_ratio: AspectRatio,
32    /// Duration in seconds.
33    pub duration_seconds: u32,
34    /// Optional negative prompt.
35    pub negative_prompt: Option<String>,
36    /// Optional model name or provider identifier.
37    pub model: Option<String>,
38}
39
40impl VideoRequest {
41    /// Creates a new video request with sensible defaults.
42    pub fn new(prompt: impl Into<String>) -> Self {
43        Self {
44            prompt: prompt.into(),
45            aspect_ratio: AspectRatio::Widescreen,
46            duration_seconds: 8,
47            negative_prompt: None,
48            model: None,
49        }
50    }
51
52    /// Sets the aspect ratio.
53    pub fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
54        self.aspect_ratio = aspect_ratio;
55        self
56    }
57
58    /// Sets the duration in seconds.
59    pub fn duration_seconds(mut self, seconds: u32) -> Self {
60        self.duration_seconds = seconds;
61        self
62    }
63
64    /// Sets a negative prompt.
65    pub fn negative_prompt(mut self, prompt: impl Into<String>) -> Self {
66        self.negative_prompt = Some(prompt.into());
67        self
68    }
69
70    /// Sets a model identifier.
71    pub fn model(mut self, model: impl Into<String>) -> Self {
72        self.model = Some(model.into());
73        self
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn creates_default_video_request() {
83        let request = VideoRequest::new("Golden-hour timelapse over a mountain ridge");
84
85        assert_eq!(request.duration_seconds, 8);
86        assert_eq!(request.aspect_ratio, AspectRatio::Widescreen);
87    }
88
89    #[test]
90    fn updates_video_request_fields() {
91        let request = VideoRequest::new("A handheld shot walking through a rainy alley")
92            .aspect_ratio(AspectRatio::Vertical)
93            .duration_seconds(12)
94            .negative_prompt("blurry")
95            .model("muse-video");
96
97        assert_eq!(request.duration_seconds, 12);
98        assert_eq!(request.aspect_ratio, AspectRatio::Vertical);
99        assert_eq!(request.negative_prompt, Some("blurry".to_string()));
100        assert_eq!(request.model, Some("muse-video".to_string()));
101    }
102}