1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum VideoQuality {
10 #[default]
12 Best,
13 High,
15 Medium,
17 Low,
19 Worst,
21 CustomHeight(u32),
23 CustomWidth(u32),
25}
26
27impl fmt::Display for VideoQuality {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 VideoQuality::Best => f.write_str("Best"),
31 VideoQuality::High => f.write_str("High"),
32 VideoQuality::Medium => f.write_str("Medium"),
33 VideoQuality::Low => f.write_str("Low"),
34 VideoQuality::Worst => f.write_str("Worst"),
35 VideoQuality::CustomHeight(h) => write!(f, "CustomHeight(height={h})"),
36 VideoQuality::CustomWidth(w) => write!(f, "CustomWidth(width={w})"),
37 }
38 }
39}
40
41#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
43pub enum AudioQuality {
44 #[default]
46 Best,
47 High,
49 Medium,
51 Low,
53 Worst,
55 CustomBitrate(u32),
57}
58
59impl fmt::Display for AudioQuality {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 AudioQuality::Best => f.write_str("Best"),
63 AudioQuality::High => f.write_str("High"),
64 AudioQuality::Medium => f.write_str("Medium"),
65 AudioQuality::Low => f.write_str("Low"),
66 AudioQuality::Worst => f.write_str("Worst"),
67 AudioQuality::CustomBitrate(b) => write!(f, "CustomBitrate(bitrate={b})"),
68 }
69 }
70}
71
72#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
74pub enum VideoCodecPreference {
75 VP9,
77 AVC1,
79 AV1,
81 Custom(String),
83 #[default]
85 Any,
86}
87
88impl fmt::Display for VideoCodecPreference {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 match self {
91 VideoCodecPreference::VP9 => f.write_str("VP9"),
92 VideoCodecPreference::AVC1 => f.write_str("AVC1"),
93 VideoCodecPreference::AV1 => f.write_str("AV1"),
94 VideoCodecPreference::Custom(c) => write!(f, "Custom(codec={c})"),
95 VideoCodecPreference::Any => f.write_str("Any"),
96 }
97 }
98}
99
100#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
102pub enum AudioCodecPreference {
103 Opus,
105 AAC,
107 MP3,
109 Custom(String),
111 #[default]
113 Any,
114}
115
116impl fmt::Display for AudioCodecPreference {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match self {
119 AudioCodecPreference::Opus => f.write_str("Opus"),
120 AudioCodecPreference::AAC => f.write_str("AAC"),
121 AudioCodecPreference::MP3 => f.write_str("MP3"),
122 AudioCodecPreference::Custom(c) => write!(f, "Custom(codec={c})"),
123 AudioCodecPreference::Any => f.write_str("Any"),
124 }
125 }
126}
127
128#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
133pub enum StoryboardQuality {
134 #[default]
136 Best,
137 Worst,
139}
140
141impl fmt::Display for StoryboardQuality {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 StoryboardQuality::Best => f.write_str("Best"),
145 StoryboardQuality::Worst => f.write_str("Worst"),
146 }
147 }
148}
149
150#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
152pub enum ThumbnailQuality {
153 #[default]
155 Best,
156 MinimumResolution(u32, u32),
158 Worst,
160}
161
162impl fmt::Display for ThumbnailQuality {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 match self {
165 ThumbnailQuality::Best => f.write_str("Best"),
166 ThumbnailQuality::MinimumResolution(w, h) => {
167 write!(f, "MinimumResolution(width={w}, height={h})")
168 }
169 ThumbnailQuality::Worst => f.write_str("Worst"),
170 }
171 }
172}
173
174fn contains_ignore_ascii_case(haystack: &str, needle: &str) -> bool {
176 if needle.len() > haystack.len() {
177 return false;
178 }
179 haystack
180 .as_bytes()
181 .windows(needle.len())
182 .any(|w| w.eq_ignore_ascii_case(needle.as_bytes()))
183}
184
185pub fn matches_video_codec(codec: &str, preference: &VideoCodecPreference) -> bool {
200 match preference {
201 VideoCodecPreference::VP9 => contains_ignore_ascii_case(codec, "vp9"),
202 VideoCodecPreference::AVC1 => {
203 contains_ignore_ascii_case(codec, "avc1")
204 || contains_ignore_ascii_case(codec, "h264")
205 || contains_ignore_ascii_case(codec, "h.264")
206 }
207 VideoCodecPreference::AV1 => {
208 contains_ignore_ascii_case(codec, "av1") || contains_ignore_ascii_case(codec, "av01")
209 }
210 VideoCodecPreference::Custom(custom) => contains_ignore_ascii_case(codec, custom),
211 VideoCodecPreference::Any => true,
212 }
213}
214
215pub fn matches_audio_codec(codec: &str, preference: &AudioCodecPreference) -> bool {
230 match preference {
231 AudioCodecPreference::Opus => contains_ignore_ascii_case(codec, "opus"),
232 AudioCodecPreference::AAC => {
233 contains_ignore_ascii_case(codec, "aac") || contains_ignore_ascii_case(codec, "mp4a")
234 }
235 AudioCodecPreference::MP3 => contains_ignore_ascii_case(codec, "mp3"),
236 AudioCodecPreference::Custom(custom) => contains_ignore_ascii_case(codec, custom),
237 AudioCodecPreference::Any => true,
238 }
239}
240
241#[derive(Debug, Default, Clone, PartialEq, Eq)]
246pub struct FormatPreferences {
247 pub video_quality: Option<VideoQuality>,
249 pub audio_quality: Option<AudioQuality>,
251 pub video_codec: Option<VideoCodecPreference>,
253 pub audio_codec: Option<AudioCodecPreference>,
255}
256
257impl FormatPreferences {
258 pub fn has_any(&self) -> bool {
260 self.video_quality.is_some()
261 || self.audio_quality.is_some()
262 || self.video_codec.is_some()
263 || self.audio_codec.is_some()
264 }
265}
266
267impl fmt::Display for FormatPreferences {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write!(
270 f,
271 "FormatPreferences(video_quality={}, audio_quality={}, video_codec={}, audio_codec={})",
272 self.video_quality
273 .as_ref()
274 .map_or("none".to_string(), |q| q.to_string()),
275 self.audio_quality
276 .as_ref()
277 .map_or("none".to_string(), |q| q.to_string()),
278 self.video_codec.as_ref().map_or("none".to_string(), |c| c.to_string()),
279 self.audio_codec.as_ref().map_or("none".to_string(), |c| c.to_string()),
280 )
281 }
282}