1use crate::{Format, InfoValue, MediaSyntax, predicate_accepts};
4use rsynko_manager::{MediaStreams, MediaStreamsExt, Performer};
5use rsynko_rsync::SyncProfile;
6use rsynko_ui::FormatDescriptionAlg;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct DownloadOptions {
11 pub(crate) media_streams: Option<MediaStreams>,
12 pub(crate) format: FormatChoice,
13 pub(crate) profile: Option<SyncProfile>,
14 pub(crate) dry_run: Option<bool>,
15}
16
17impl DownloadOptions {
18 #[must_use]
20 pub const fn progressive() -> Self {
21 Self {
22 media_streams: Some(MediaStreams::AudioVideo),
23 format: FormatChoice::Best,
24 profile: None,
25 dry_run: None,
26 }
27 }
28
29 #[must_use]
31 pub const fn audio() -> Self {
32 Self {
33 media_streams: Some(MediaStreams::Audio),
34 format: FormatChoice::Best,
35 profile: None,
36 dry_run: None,
37 }
38 }
39
40 #[must_use]
42 pub const fn video() -> Self {
43 Self {
44 media_streams: Some(MediaStreams::Video),
45 format: FormatChoice::Best,
46 profile: None,
47 dry_run: None,
48 }
49 }
50
51 #[must_use]
57 pub const fn transfer() -> Self {
58 Self {
59 media_streams: None,
60 format: FormatChoice::Best,
61 profile: Some(SyncProfile::Copy),
62 dry_run: Some(true),
63 }
64 }
65
66 #[must_use]
68 pub const fn profile(&self) -> Option<SyncProfile> {
69 self.profile
70 }
71
72 #[must_use]
77 pub const fn performer(&self) -> Performer {
78 if self.profile.is_some() {
79 Performer::Program
80 } else {
81 Performer::Retrieval
82 }
83 }
84
85 #[must_use]
87 pub const fn with_profile(mut self, profile: SyncProfile) -> Self {
88 if self.profile.is_some() {
89 self.profile = Some(profile);
90 }
91 self
92 }
93
94 #[must_use]
99 pub const fn rehearsing(mut self) -> Self {
100 if self.dry_run.is_some() {
101 self.dry_run = Some(true);
102 }
103 self
104 }
105
106 #[must_use]
108 pub const fn dry_run(&self) -> Option<bool> {
109 self.dry_run
110 }
111
112 pub const fn set_dry_run(&mut self, dry_run: bool) {
114 if self.dry_run.is_some() {
115 self.dry_run = Some(dry_run);
116 }
117 }
118
119 #[must_use]
121 pub const fn media_streams(&self) -> Option<MediaStreams> {
122 self.media_streams
123 }
124
125 #[must_use]
127 pub const fn format(&self) -> &FormatChoice {
128 &self.format
129 }
130
131 #[must_use]
133 pub fn with_media_streams(mut self, media_streams: MediaStreams) -> Self {
134 self.media_streams = Some(media_streams);
135 self.format = FormatChoice::Best;
136 self
137 }
138
139 #[must_use]
141 pub fn with_format(mut self, format: FormatChoice) -> Self {
142 self.format = format;
143 self
144 }
145}
146
147#[derive(Clone, Debug, PartialEq, Eq)]
149pub enum FormatChoice {
150 Best,
152 Id(String),
154}
155
156#[derive(Clone, Debug, PartialEq)]
158pub enum FormatCatalog {
159 Unknown,
161 Waiting,
163 Inspecting,
165 Available(Vec<Format>),
167 Failed(String),
169}
170
171impl FormatCatalog {
172 #[must_use]
174 pub fn available(&self) -> Option<&[Format]> {
175 match self {
176 Self::Available(formats) => Some(formats),
177 Self::Unknown | Self::Waiting | Self::Inspecting | Self::Failed(_) => None,
178 }
179 }
180}
181
182#[must_use]
184pub fn role_accepts(streams: MediaStreams, format: &Format) -> bool {
185 predicate_accepts(&MediaSyntax.stream_role_format(streams), format)
186}
187
188impl FormatDescriptionAlg for Format {
189 fn format_identity(&self) -> &str {
190 &self.id
191 }
192
193 fn format_extension(&self) -> Option<&str> {
194 self.extension()
195 }
196
197 fn format_streams(&self) -> Option<MediaStreams> {
198 match (self.has_audio(), self.has_video()) {
199 (true, true) => Some(MediaStreams::AudioVideo),
200 (true, false) => Some(MediaStreams::Audio),
201 (false, true) => Some(MediaStreams::Video),
202 (false, false) => None,
203 }
204 }
205
206 fn format_quality(&self) -> Option<&str> {
207 self.text("quality")
208 }
209
210 fn format_height(&self) -> Option<u64> {
211 format_count(self, "height")
212 }
213
214 fn format_width(&self) -> Option<u64> {
215 format_count(self, "width")
216 }
217
218 fn format_bitrate(&self) -> Option<u64> {
219 format_count(self, "bitrate")
220 }
221
222 fn format_size(&self) -> Option<u64> {
223 format_count(self, "content_length")
224 }
225
226 fn format_codecs(&self) -> Option<&str> {
227 self.text("codecs")
228 }
229}
230
231fn format_count(format: &Format, key: &str) -> Option<u64> {
233 match format.observe(key) {
234 Some(InfoValue::Integer(value)) => u64::try_from(*value).ok(),
235 Some(
236 InfoValue::Null
237 | InfoValue::Bool(_)
238 | InfoValue::Float(_)
239 | InfoValue::String(_)
240 | InfoValue::List(_)
241 | InfoValue::Record(_),
242 )
243 | None => None,
244 }
245}