Skip to main content

oximedia_clips/proxy/
link.rs

1//! Proxy link types.
2
3use crate::clip::ClipId;
4use crate::error::{ClipError, ClipResult};
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7use uuid::Uuid;
8
9/// Unique identifier for a proxy link.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct ProxyLinkId(Uuid);
12
13impl ProxyLinkId {
14    /// Creates a new random proxy link ID.
15    #[must_use]
16    pub fn new() -> Self {
17        Self(Uuid::new_v4())
18    }
19
20    /// Creates a proxy link ID from a UUID.
21    #[must_use]
22    pub const fn from_uuid(uuid: Uuid) -> Self {
23        Self(uuid)
24    }
25
26    /// Returns the inner UUID.
27    #[must_use]
28    pub const fn as_uuid(&self) -> &Uuid {
29        &self.0
30    }
31}
32
33impl Default for ProxyLinkId {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39/// Proxy quality level.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
41pub enum ProxyQuality {
42    /// Low quality proxy (e.g., 480p).
43    Low,
44    /// Medium quality proxy (e.g., 720p).
45    Medium,
46    /// High quality proxy (e.g., 1080p).
47    High,
48    /// Custom quality.
49    Custom,
50}
51
52impl ProxyQuality {
53    /// Returns all quality levels.
54    #[must_use]
55    pub const fn all() -> [Self; 4] {
56        [Self::Low, Self::Medium, Self::High, Self::Custom]
57    }
58
59    /// Parses a proxy quality from a string.
60    ///
61    /// # Errors
62    ///
63    /// Returns an error if the string is invalid.
64    pub fn parse(s: &str) -> ClipResult<Self> {
65        match s.to_lowercase().as_str() {
66            "low" => Ok(Self::Low),
67            "medium" => Ok(Self::Medium),
68            "high" => Ok(Self::High),
69            "custom" => Ok(Self::Custom),
70            _ => Err(ClipError::InvalidProxyQuality(s.to_string())),
71        }
72    }
73}
74
75impl std::fmt::Display for ProxyQuality {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        match self {
78            Self::Low => write!(f, "Low"),
79            Self::Medium => write!(f, "Medium"),
80            Self::High => write!(f, "High"),
81            Self::Custom => write!(f, "Custom"),
82        }
83    }
84}
85
86/// A link between a clip and its proxy media.
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct ProxyLink {
89    /// Unique identifier.
90    pub id: ProxyLinkId,
91
92    /// Original clip ID.
93    pub clip_id: ClipId,
94
95    /// Proxy file path.
96    pub proxy_path: PathBuf,
97
98    /// Proxy quality.
99    pub quality: ProxyQuality,
100
101    /// Resolution (e.g., "1920x1080").
102    pub resolution: Option<String>,
103
104    /// Bitrate in kbps.
105    pub bitrate: Option<u32>,
106
107    /// Codec used.
108    pub codec: Option<String>,
109}
110
111impl ProxyLink {
112    /// Creates a new proxy link.
113    #[must_use]
114    pub fn new(clip_id: ClipId, proxy_path: PathBuf, quality: ProxyQuality) -> Self {
115        Self {
116            id: ProxyLinkId::new(),
117            clip_id,
118            proxy_path,
119            quality,
120            resolution: None,
121            bitrate: None,
122            codec: None,
123        }
124    }
125
126    /// Sets the resolution.
127    pub fn set_resolution(&mut self, resolution: impl Into<String>) {
128        self.resolution = Some(resolution.into());
129    }
130
131    /// Sets the bitrate.
132    pub fn set_bitrate(&mut self, bitrate: u32) {
133        self.bitrate = Some(bitrate);
134    }
135
136    /// Sets the codec.
137    pub fn set_codec(&mut self, codec: impl Into<String>) {
138        self.codec = Some(codec.into());
139    }
140
141    /// Checks if the proxy file exists.
142    #[must_use]
143    pub fn proxy_exists(&self) -> bool {
144        self.proxy_path.exists()
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn test_proxy_quality() {
154        assert_eq!(
155            ProxyQuality::parse("low").expect("parse should succeed"),
156            ProxyQuality::Low
157        );
158        assert_eq!(
159            ProxyQuality::parse("medium").expect("parse should succeed"),
160            ProxyQuality::Medium
161        );
162        assert!(ProxyQuality::parse("invalid").is_err());
163    }
164
165    #[test]
166    fn test_proxy_link() {
167        let clip_id = ClipId::new();
168        let proxy_path = PathBuf::from("/proxy/test.mov");
169        let mut link = ProxyLink::new(clip_id, proxy_path, ProxyQuality::Medium);
170
171        link.set_resolution("1920x1080");
172        link.set_bitrate(10_000);
173        link.set_codec("ProRes");
174
175        assert_eq!(link.resolution, Some("1920x1080".to_string()));
176        assert_eq!(link.bitrate, Some(10_000));
177    }
178}