tauri_plugin_media_toolkit/
models.rs

1use serde::{Deserialize, Serialize};
2
3/// Media type
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5#[serde(rename_all = "lowercase")]
6pub enum MediaType {
7    Audio,
8    Video,
9    Unknown,
10}
11
12/// Output format for conversion/trim
13#[derive(Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum OutputFormat {
16    Mp3,
17    Mp4,
18    Wav,
19    Aac,
20    M4a,
21    Aiff,
22    Caf,
23    Webm,
24    Ogg,
25    Flac,
26}
27
28impl OutputFormat {
29    pub fn extension(&self) -> &'static str {
30        match self {
31            OutputFormat::Mp3 => "mp3",
32            OutputFormat::Mp4 => "mp4",
33            OutputFormat::Wav => "wav",
34            OutputFormat::Aac => "aac",
35            OutputFormat::M4a => "m4a",
36            OutputFormat::Aiff => "aiff",
37            OutputFormat::Caf => "caf",
38            OutputFormat::Webm => "webm",
39            OutputFormat::Ogg => "ogg",
40            OutputFormat::Flac => "flac",
41        }
42    }
43}
44
45/// Audio quality preset
46#[derive(Debug, Clone, Serialize, Deserialize, Default)]
47#[serde(rename_all = "lowercase")]
48pub enum AudioQuality {
49    Low,
50    #[default]
51    Medium,
52    High,
53    Lossless,
54}
55
56impl AudioQuality {
57    pub fn bitrate(&self) -> u32 {
58        match self {
59            AudioQuality::Low => 96_000,
60            AudioQuality::Medium => 192_000,
61            AudioQuality::High => 320_000,
62            AudioQuality::Lossless => 0, // Use original
63        }
64    }
65}
66
67/// Video quality preset
68#[derive(Debug, Clone, Serialize, Deserialize, Default)]
69#[serde(rename_all = "lowercase")]
70pub enum VideoQuality {
71    Low,
72    #[default]
73    Medium,
74    High,
75    Original,
76}
77
78/// Trim configuration
79#[derive(Debug, Clone, Serialize, Deserialize)]
80#[serde(rename_all = "camelCase")]
81pub struct TrimConfig {
82    /// Input file path
83    pub input_path: String,
84    /// Output file path (without extension)
85    pub output_path: String,
86    /// Start time in milliseconds
87    pub start_ms: u64,
88    /// End time in milliseconds
89    pub end_ms: u64,
90    /// Output format (optional, defaults to same as input)
91    pub format: Option<OutputFormat>,
92    /// Audio quality (for audio output)
93    pub audio_quality: Option<AudioQuality>,
94    /// Video quality (for video output)
95    pub video_quality: Option<VideoQuality>,
96    /// Whether to preserve original quality (no re-encoding)
97    #[serde(default)]
98    pub preserve_quality: bool,
99}
100
101/// Convert configuration
102#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(rename_all = "camelCase")]
104pub struct ConvertConfig {
105    /// Input file path
106    pub input_path: String,
107    /// Output file path (without extension)
108    pub output_path: String,
109    /// Output format
110    pub format: OutputFormat,
111    /// Audio quality
112    pub audio_quality: Option<AudioQuality>,
113    /// Video quality
114    pub video_quality: Option<VideoQuality>,
115}
116
117/// Extract audio configuration
118#[derive(Debug, Clone, Serialize, Deserialize)]
119#[serde(rename_all = "camelCase")]
120pub struct ExtractAudioConfig {
121    /// Input video file path
122    pub input_path: String,
123    /// Output audio file path (without extension)
124    pub output_path: String,
125    /// Output format
126    pub format: OutputFormat,
127    /// Audio quality
128    pub audio_quality: Option<AudioQuality>,
129}
130
131/// Media information
132#[derive(Debug, Clone, Serialize, Deserialize)]
133#[serde(rename_all = "camelCase")]
134pub struct MediaInfo {
135    /// File path
136    pub path: String,
137    /// Media type (audio/video)
138    pub media_type: MediaType,
139    /// Duration in milliseconds
140    pub duration_ms: u64,
141    /// File size in bytes
142    pub file_size: u64,
143    /// Format/container name
144    pub format: String,
145    /// Whether the file has audio stream
146    pub has_audio: bool,
147    /// Whether the file has video stream
148    pub has_video: bool,
149    /// Audio codec (if present)
150    pub audio_codec: Option<String>,
151    /// Video codec (if present)
152    pub video_codec: Option<String>,
153    /// Audio sample rate (if present)
154    pub sample_rate: Option<u32>,
155    /// Audio channels (if present)
156    pub channels: Option<u32>,
157    /// Audio bitrate in bps (if present)
158    pub audio_bitrate: Option<u32>,
159    /// Video width (if present)
160    pub width: Option<u32>,
161    /// Video height (if present)
162    pub height: Option<u32>,
163    /// Video frame rate (if present)
164    pub frame_rate: Option<f64>,
165    /// Video bitrate in bps (if present)
166    pub video_bitrate: Option<u32>,
167}
168
169/// Operation result
170#[derive(Debug, Clone, Serialize, Deserialize)]
171#[serde(rename_all = "camelCase")]
172pub struct OperationResult {
173    /// Whether the operation succeeded
174    pub success: bool,
175    /// Output file path
176    pub output_path: String,
177    /// Duration of output in milliseconds
178    pub duration_ms: u64,
179    /// File size in bytes
180    pub file_size: u64,
181    /// Warning message (e.g., format conversion on iOS)
182    pub warning: Option<String>,
183}
184
185/// Playback state
186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
187#[serde(rename_all = "lowercase")]
188pub enum PlaybackState {
189    Idle,
190    Playing,
191    Paused,
192    Stopped,
193}
194
195/// Playback status
196#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct PlaybackStatus {
199    /// Current state
200    pub state: PlaybackState,
201    /// Current position in milliseconds
202    pub position_ms: u64,
203    /// Total duration in milliseconds
204    pub duration_ms: u64,
205    /// Current volume (0.0 to 1.0)
206    pub volume: f32,
207    /// Currently playing file
208    pub file_path: Option<String>,
209}
210
211/// Play configuration
212#[derive(Debug, Clone, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct PlayConfig {
215    /// File path to play
216    pub file_path: String,
217    /// Start position in milliseconds (optional)
218    pub start_ms: Option<u64>,
219    /// Volume (0.0 to 1.0, default 1.0)
220    pub volume: Option<f32>,
221    /// Whether to loop
222    #[serde(default)]
223    pub loop_playback: bool,
224}
225
226/// Seek configuration
227#[derive(Debug, Clone, Serialize, Deserialize)]
228#[serde(rename_all = "camelCase")]
229pub struct SeekConfig {
230    /// Position to seek to in milliseconds
231    pub position_ms: u64,
232}
233
234/// File selection result (Android only)
235#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub struct FileSelectionResult {
238    /// Whether the selection succeeded
239    pub success: bool,
240    /// Path to the selected file (copied to cache on Android)
241    pub file_path: String,
242}
243
244/// Permission status response
245#[derive(Debug, Clone, Serialize, Deserialize)]
246#[serde(rename_all = "camelCase")]
247pub struct PermissionResponse {
248    /// Whether permission is granted
249    pub granted: bool,
250    /// Whether permission can be requested (not permanently denied)
251    pub can_request: bool,
252}
253
254/// Cache cleanup result
255#[derive(Debug, Clone, Serialize, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub struct CleanupResult {
258    /// Whether cleanup succeeded
259    pub success: bool,
260    /// Number of files deleted
261    pub files_deleted: u32,
262    /// Bytes freed
263    pub bytes_freed: u64,
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn test_media_type_serialization() {
272        assert_eq!(
273            serde_json::to_string(&MediaType::Audio).unwrap(),
274            "\"audio\""
275        );
276        assert_eq!(
277            serde_json::to_string(&MediaType::Video).unwrap(),
278            "\"video\""
279        );
280        assert_eq!(
281            serde_json::to_string(&MediaType::Unknown).unwrap(),
282            "\"unknown\""
283        );
284    }
285
286    #[test]
287    fn test_media_type_deserialization() {
288        assert_eq!(
289            serde_json::from_str::<MediaType>("\"audio\"").unwrap(),
290            MediaType::Audio
291        );
292        assert_eq!(
293            serde_json::from_str::<MediaType>("\"video\"").unwrap(),
294            MediaType::Video
295        );
296    }
297
298    #[test]
299    fn test_output_format_extension() {
300        assert_eq!(OutputFormat::Mp3.extension(), "mp3");
301        assert_eq!(OutputFormat::Mp4.extension(), "mp4");
302        assert_eq!(OutputFormat::Wav.extension(), "wav");
303        assert_eq!(OutputFormat::Flac.extension(), "flac");
304    }
305
306    #[test]
307    fn test_audio_quality_bitrate() {
308        assert_eq!(AudioQuality::Low.bitrate(), 96_000);
309        assert_eq!(AudioQuality::Medium.bitrate(), 192_000);
310        assert_eq!(AudioQuality::High.bitrate(), 320_000);
311        assert_eq!(AudioQuality::Lossless.bitrate(), 0);
312    }
313
314    #[test]
315    fn test_trim_config_deserialization() {
316        let json = r#"{
317            "inputPath": "/path/to/input.mp4",
318            "outputPath": "/path/to/output",
319            "startMs": 1000,
320            "endMs": 5000
321        }"#;
322
323        let config: TrimConfig = serde_json::from_str(json).unwrap();
324        assert_eq!(config.input_path, "/path/to/input.mp4");
325        assert_eq!(config.output_path, "/path/to/output");
326        assert_eq!(config.start_ms, 1000);
327        assert_eq!(config.end_ms, 5000);
328        assert!(config.format.is_none());
329    }
330
331    #[test]
332    fn test_playback_state_serialization() {
333        assert_eq!(
334            serde_json::to_string(&PlaybackState::Playing).unwrap(),
335            "\"playing\""
336        );
337        assert_eq!(
338            serde_json::to_string(&PlaybackState::Paused).unwrap(),
339            "\"paused\""
340        );
341    }
342}