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 { media_streams: Some(MediaStreams::AudioVideo), format: FormatChoice::Best, profile: None, dry_run: None }
22 }
23
24 #[must_use]
26 pub const fn audio() -> Self {
27 Self { media_streams: Some(MediaStreams::Audio), format: FormatChoice::Best, profile: None, dry_run: None }
28 }
29
30 #[must_use]
32 pub const fn video() -> Self {
33 Self { media_streams: Some(MediaStreams::Video), format: FormatChoice::Best, profile: None, dry_run: None }
34 }
35
36 #[must_use]
42 pub const fn transfer() -> Self {
43 Self { media_streams: None, format: FormatChoice::Best, profile: Some(SyncProfile::Copy), dry_run: Some(true) }
44 }
45
46 #[must_use]
48 pub const fn profile(&self) -> Option<SyncProfile> {
49 self.profile
50 }
51
52 #[must_use]
57 pub const fn performer(&self) -> Performer {
58 if self.profile.is_some() { Performer::Program } else { Performer::Retrieval }
59 }
60
61 #[must_use]
63 pub const fn with_profile(mut self, profile: SyncProfile) -> Self {
64 if self.profile.is_some() {
65 self.profile = Some(profile);
66 }
67 self
68 }
69
70 #[must_use]
75 pub const fn rehearsing(mut self) -> Self {
76 if self.dry_run.is_some() {
77 self.dry_run = Some(true);
78 }
79 self
80 }
81
82 #[must_use]
84 pub const fn dry_run(&self) -> Option<bool> {
85 self.dry_run
86 }
87
88 pub const fn set_dry_run(&mut self, dry_run: bool) {
90 if self.dry_run.is_some() {
91 self.dry_run = Some(dry_run);
92 }
93 }
94
95 #[must_use]
97 pub const fn media_streams(&self) -> Option<MediaStreams> {
98 self.media_streams
99 }
100
101 #[must_use]
103 pub const fn format(&self) -> &FormatChoice {
104 &self.format
105 }
106
107 #[must_use]
109 pub fn with_media_streams(mut self, media_streams: MediaStreams) -> Self {
110 self.media_streams = Some(media_streams);
111 self.format = FormatChoice::Best;
112 self
113 }
114
115 #[must_use]
117 pub fn with_format(mut self, format: FormatChoice) -> Self {
118 self.format = format;
119 self
120 }
121}
122
123#[derive(Clone, Debug, PartialEq, Eq)]
125pub enum FormatChoice {
126 Best,
128 Id(String),
130}
131
132#[derive(Clone, Debug, PartialEq)]
134pub enum FormatCatalog {
135 Unknown,
137 Waiting,
139 Inspecting,
141 Available(Vec<Format>),
143 Failed(String),
145}
146
147impl FormatCatalog {
148 #[must_use]
150 pub fn available(&self) -> Option<&[Format]> {
151 match self {
152 Self::Available(formats) => Some(formats),
153 Self::Unknown | Self::Waiting | Self::Inspecting | Self::Failed(_) => None,
154 }
155 }
156}
157
158#[must_use]
160pub fn role_accepts(streams: MediaStreams, format: &Format) -> bool {
161 predicate_accepts(&MediaSyntax.stream_role_format(streams), format)
162}
163
164impl FormatDescriptionAlg for Format {
165 fn format_identity(&self) -> &str {
166 &self.id
167 }
168
169 fn format_extension(&self) -> Option<&str> {
170 self.extension()
171 }
172
173 fn format_streams(&self) -> Option<MediaStreams> {
174 match (self.has_audio(), self.has_video()) {
175 (true, true) => Some(MediaStreams::AudioVideo),
176 (true, false) => Some(MediaStreams::Audio),
177 (false, true) => Some(MediaStreams::Video),
178 (false, false) => None,
179 }
180 }
181
182 fn format_quality(&self) -> Option<&str> {
183 self.text("quality")
184 }
185
186 fn format_height(&self) -> Option<u64> {
187 format_count(self, "height")
188 }
189
190 fn format_width(&self) -> Option<u64> {
191 format_count(self, "width")
192 }
193
194 fn format_bitrate(&self) -> Option<u64> {
195 format_count(self, "bitrate")
196 }
197
198 fn format_size(&self) -> Option<u64> {
199 format_count(self, "content_length")
200 }
201
202 fn format_codecs(&self) -> Option<&str> {
203 self.text("codecs")
204 }
205}
206
207fn format_count(format: &Format, key: &str) -> Option<u64> {
209 match format.observe(key) {
210 Some(InfoValue::Integer(value)) => u64::try_from(*value).ok(),
211 Some(
212 InfoValue::Null
213 | InfoValue::Bool(_)
214 | InfoValue::Float(_)
215 | InfoValue::String(_)
216 | InfoValue::List(_)
217 | InfoValue::Record(_),
218 )
219 | None => None,
220 }
221}