1use serde::{Deserialize, Serialize};
7
8use crate::image::ImageFormat;
9use crate::variant::VariantClass;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct FilePreset {
14 pub name: String,
16 pub allowed_media_classes: Vec<VariantClass>,
18 pub image_variants: Vec<String>,
20 pub video_variants: Vec<String>,
22 pub audio_variants: Vec<String>,
24 pub extract_audio: bool,
26 pub generate_thumbnail: bool,
28 pub max_variant: Option<String>,
30 pub thumbnail_variant: Option<String>,
32 pub store_original: bool,
34}
35
36impl Default for FilePreset {
37 fn default() -> Self {
38 Self {
39 name: "default".to_string(),
40 allowed_media_classes: vec![
41 VariantClass::Visual,
42 VariantClass::Video,
43 VariantClass::Audio,
44 VariantClass::Document,
45 ],
46 image_variants: vec![
47 "vis.tn".into(),
48 "vis.sd".into(),
49 "vis.md".into(),
50 "vis.hd".into(),
51 ],
52 video_variants: vec!["vid.sd".into(), "vid.md".into(), "vid.hd".into()],
53 audio_variants: vec!["aud.md".into()],
54 extract_audio: false,
55 generate_thumbnail: true,
56 max_variant: Some("vid.hd".into()),
57 thumbnail_variant: Some("vis.tn".into()),
58 store_original: true,
59 }
60 }
61}
62
63pub mod presets {
65 use super::{FilePreset, VariantClass};
66
67 pub fn default() -> FilePreset {
69 FilePreset::default()
70 }
71
72 pub fn podcast() -> FilePreset {
74 FilePreset {
75 name: "podcast".to_string(),
76 allowed_media_classes: vec![VariantClass::Audio, VariantClass::Video],
77 image_variants: vec!["vis.tn".into()], video_variants: vec!["vid.sd".into()],
79 audio_variants: vec!["aud.sd".into(), "aud.md".into(), "aud.hd".into()],
80 extract_audio: true,
81 generate_thumbnail: true,
82 max_variant: Some("vid.sd".into()),
83 thumbnail_variant: Some("vis.tn".into()),
84 store_original: true,
85 }
86 }
87
88 pub fn archive() -> FilePreset {
90 FilePreset {
91 name: "archive".to_string(),
92 allowed_media_classes: vec![
93 VariantClass::Visual,
94 VariantClass::Video,
95 VariantClass::Audio,
96 VariantClass::Document,
97 VariantClass::Raw,
98 ],
99 image_variants: vec!["vis.tn".into()], video_variants: vec![],
101 audio_variants: vec![],
102 extract_audio: false,
103 generate_thumbnail: true,
104 max_variant: None, thumbnail_variant: Some("vis.tn".into()),
106 store_original: true, }
108 }
109
110 pub fn high_quality() -> FilePreset {
112 FilePreset {
113 name: "high_quality".to_string(),
114 allowed_media_classes: vec![
115 VariantClass::Visual,
116 VariantClass::Video,
117 VariantClass::Audio,
118 ],
119 image_variants: vec![
120 "vis.tn".into(),
121 "vis.sd".into(),
122 "vis.md".into(),
123 "vis.hd".into(),
124 "vis.xd".into(),
125 ],
126 video_variants: vec![
127 "vid.sd".into(),
128 "vid.md".into(),
129 "vid.hd".into(),
130 "vid.xd".into(),
131 ],
132 audio_variants: vec!["aud.md".into(), "aud.hd".into()],
133 extract_audio: false,
134 generate_thumbnail: true,
135 max_variant: Some("vid.xd".into()),
136 thumbnail_variant: Some("vis.tn".into()),
137 store_original: true,
138 }
139 }
140
141 pub fn mobile() -> FilePreset {
143 FilePreset {
144 name: "mobile".to_string(),
145 allowed_media_classes: vec![
146 VariantClass::Visual,
147 VariantClass::Video,
148 VariantClass::Audio,
149 ],
150 image_variants: vec!["vis.tn".into(), "vis.sd".into(), "vis.md".into()],
151 video_variants: vec!["vid.sd".into(), "vid.md".into()],
152 audio_variants: vec!["aud.sd".into()],
153 extract_audio: false,
154 generate_thumbnail: true,
155 max_variant: Some("vid.md".into()),
156 thumbnail_variant: Some("vis.tn".into()),
157 store_original: false, }
159 }
160
161 pub fn video() -> FilePreset {
163 FilePreset {
164 name: "video".to_string(),
165 allowed_media_classes: vec![VariantClass::Video],
166 image_variants: vec![
167 "vis.tn".into(),
168 "vis.sd".into(),
169 "vis.md".into(),
170 "vis.hd".into(),
171 ],
172 video_variants: vec!["vid.sd".into(), "vid.md".into(), "vid.hd".into()],
173 audio_variants: vec![], extract_audio: false, generate_thumbnail: true,
176 max_variant: Some("vid.hd".into()),
177 thumbnail_variant: Some("vis.tn".into()),
178 store_original: true,
179 }
180 }
181
182 pub fn profile_picture() -> FilePreset {
184 FilePreset {
185 name: "profile-picture".to_string(),
186 allowed_media_classes: vec![VariantClass::Visual],
187 image_variants: vec![
188 "vis.pf".into(),
189 "vis.tn".into(),
190 "vis.sd".into(),
191 "vis.md".into(),
192 "vis.hd".into(),
193 ],
194 video_variants: vec![],
195 audio_variants: vec![],
196 extract_audio: false,
197 generate_thumbnail: false, max_variant: Some("vis.hd".into()),
199 thumbnail_variant: Some("vis.pf".into()),
200 store_original: false, }
202 }
203
204 pub fn cover() -> FilePreset {
206 FilePreset {
207 name: "cover".to_string(),
208 allowed_media_classes: vec![VariantClass::Visual],
209 image_variants: vec![
210 "vis.tn".into(),
211 "vis.sd".into(),
212 "vis.md".into(),
213 "vis.hd".into(),
214 ],
215 video_variants: vec![],
216 audio_variants: vec![],
217 extract_audio: false,
218 generate_thumbnail: false, max_variant: Some("vis.hd".into()),
220 thumbnail_variant: Some("vis.tn".into()),
221 store_original: false, }
223 }
224
225 pub fn get(name: &str) -> Option<FilePreset> {
227 match name {
228 "default" => Some(default()),
229 "podcast" => Some(podcast()),
230 "archive" => Some(archive()),
231 "high_quality" => Some(high_quality()),
232 "mobile" => Some(mobile()),
233 "video" => Some(video()),
234 "profile-picture" => Some(profile_picture()),
235 "cover" => Some(cover()),
236 _ => None,
237 }
238 }
239
240 pub fn list() -> Vec<&'static str> {
242 vec![
243 "default",
244 "podcast",
245 "archive",
246 "high_quality",
247 "mobile",
248 "video",
249 "profile-picture",
250 "cover",
251 ]
252 }
253}
254
255#[derive(Debug, Clone, Copy)]
257pub struct VideoQualityTier {
258 pub name: &'static str,
259 pub max_dim: u32,
260 pub bitrate: u32,
261}
262
263#[derive(Debug, Clone, Copy)]
265pub struct AudioQualityTier {
266 pub name: &'static str,
267 pub bitrate: u32,
268}
269
270#[derive(Debug, Clone, Copy)]
272pub struct ImageQualityTier {
273 pub name: &'static str,
274 pub max_dim: u32,
275 pub format: Option<ImageFormat>,
277}
278
279pub const VIDEO_TIERS: &[VideoQualityTier] = &[
281 VideoQualityTier { name: "vid.sd", max_dim: 720, bitrate: 1500 },
282 VideoQualityTier { name: "vid.md", max_dim: 1280, bitrate: 3000 },
283 VideoQualityTier { name: "vid.hd", max_dim: 1920, bitrate: 5000 },
284 VideoQualityTier { name: "vid.xd", max_dim: 3840, bitrate: 15000 },
285];
286
287pub const AUDIO_TIERS: &[AudioQualityTier] = &[
289 AudioQualityTier { name: "aud.sd", bitrate: 64 },
290 AudioQualityTier { name: "aud.md", bitrate: 128 },
291 AudioQualityTier { name: "aud.hd", bitrate: 256 },
292];
293
294pub const IMAGE_TIERS: &[ImageQualityTier] = &[
296 ImageQualityTier { name: "vis.pf", max_dim: 80, format: Some(ImageFormat::Avif) },
297 ImageQualityTier { name: "vis.tn", max_dim: 256, format: None },
298 ImageQualityTier { name: "vis.sd", max_dim: 720, format: None },
299 ImageQualityTier { name: "vis.md", max_dim: 1280, format: None },
300 ImageQualityTier { name: "vis.hd", max_dim: 1920, format: None },
301 ImageQualityTier { name: "vis.xd", max_dim: 3840, format: None },
302];
303
304pub fn get_video_tier(variant: &str) -> Option<&'static VideoQualityTier> {
306 VIDEO_TIERS.iter().find(|t| t.name == variant)
307}
308
309pub fn get_audio_tier(variant: &str) -> Option<&'static AudioQualityTier> {
311 AUDIO_TIERS.iter().find(|t| t.name == variant)
312}
313
314pub fn get_image_tier(variant: &str) -> Option<&'static ImageQualityTier> {
316 IMAGE_TIERS.iter().find(|t| t.name == variant)
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_default_preset() {
325 let preset = presets::default();
326 assert_eq!(preset.name, "default");
327 assert!(preset.video_variants.contains(&"vid.hd".to_string()));
328 assert!(preset.generate_thumbnail);
329 }
330
331 #[test]
332 fn test_podcast_preset() {
333 let preset = presets::podcast();
334 assert_eq!(preset.name, "podcast");
335 assert!(preset.extract_audio);
336 assert!(preset.audio_variants.contains(&"aud.hd".to_string()));
337 }
338
339 #[test]
340 fn test_get_preset() {
341 assert!(presets::get("default").is_some());
342 assert!(presets::get("podcast").is_some());
343 assert!(presets::get("nonexistent").is_none());
344 }
345
346 #[test]
347 fn test_video_tiers() {
348 let tier = get_video_tier("vid.hd");
349 assert!(tier.is_some());
350 let tier = tier.unwrap();
351 assert_eq!(tier.max_dim, 1920);
352 assert_eq!(tier.bitrate, 5000);
353 }
354
355 #[test]
356 fn test_audio_tiers() {
357 let tier = get_audio_tier("aud.md");
358 assert!(tier.is_some());
359 let tier = tier.unwrap();
360 assert_eq!(tier.bitrate, 128);
361 }
362
363 #[test]
364 fn test_image_tiers() {
365 let tier = get_image_tier("vis.hd");
366 assert!(tier.is_some());
367 let tier = tier.unwrap();
368 assert_eq!(tier.max_dim, 1920);
369 }
370}
371
372