youtube_dl_parser/state/parsed_state/
download_state.rs1use std::num::ParseFloatError;
2
3pub enum DownloadState {
5 Destination(String),
6 Resuming(u64),
7 Downloading(f32, u64, u64, u64),
8 Downloaded(f32, u64, u64),
9 ParseError(String),
10}
11
12impl DownloadState {
13 pub fn parse<'a>(mut split: impl DoubleEndedIterator<Item = &'a str> + Send) -> DownloadState {
14 let Some(download_indicator) = split.next() else { return DownloadState::ParseError("No download indicator detected".to_owned()); };
15 match download_indicator {
16 "Resuming" => match Self::parse_resuming(split) {
17 Ok(state) => state,
18 Err(err) => DownloadState::ParseError(err),
19 },
20 "Destination:" => Self::parse_destination(split),
21 _ => match Self::parse_progress(download_indicator, split) {
22 Ok(state) => state,
23 Err(err) => DownloadState::ParseError(err),
24 },
25 }
26 }
27
28 fn parse_resuming<'a>(
29 mut split: impl DoubleEndedIterator<Item = &'a str> + Send,
30 ) -> Result<DownloadState, String> {
31 split.next();
32 split.next();
33 split.next();
34 let Some(byte) = split.next() else { return Err("No resume byte detected".to_owned()) };
35 let byte = Self::parse_resume_byte(byte)?;
36 Ok(DownloadState::Resuming(byte))
37 }
38
39 fn parse_destination<'a>(
40 split: impl DoubleEndedIterator<Item = &'a str> + Send,
41 ) -> DownloadState {
42 let destination = split.collect::<Vec<&str>>().join(" ");
43 DownloadState::Destination(destination)
44 }
45
46 fn parse_progress<'a>(
47 download_indicator: &str,
48 mut split: impl DoubleEndedIterator<Item = &'a str> + Send,
49 ) -> Result<DownloadState, String> {
50 let progress = Self::parse_percentage(download_indicator).map_err(|err| {
52 format!("Unable to parse progress \'{download_indicator}\' with error: {err}")
53 })?;
54
55 split.next();
57
58 let Some(total_size) = split.next() else { return Err("No total size detected".to_owned()); };
60 let total_size = Self::parse_total_size(total_size).map_err(|err| {
61 format!("Unable to parse total size \'{total_size}\' with error: {err}")
62 })?;
63
64 let Some(in_or_at)= split.next() else {return Err("Unable to get if still downloading".to_owned())};
66
67 match in_or_at.trim() {
68 "at" => {
69 let Some(download_speed) = split.next() else { return Err("No download speed detected".to_owned()); };
71 let download_speed = Self::parse_download_speed(download_speed).map_err(|err| {
72 format!("Unable to parse download speed \'{download_speed}\' with error: {err}")
73 })?;
74 split.next();
76 let Some(eta) = split.next() else { return Err("No ETA detected".to_owned()); };
78 let eta = Self::parse_time(eta)
79 .map_err(|err| format!("Unable to parse eta \'{eta}\' with error: {err}"))?;
80
81 Ok(DownloadState::Downloading(
82 progress,
83 total_size,
84 download_speed,
85 eta,
86 ))
87 }
88 "in" => {
89 let Some(completion_time) = split.next() else { return Err("No completion time detected".to_owned()); };
91 let completion_time = Self::parse_time(completion_time).map_err(|err| {
92 format!("Unable to parse completion \'{completion_time}\' with error: {err}")
93 })?;
94
95 Ok(DownloadState::Downloaded(
96 progress,
97 total_size,
98 completion_time,
99 ))
100 }
101 _ => Err("Unable to get if still downloading".to_owned()),
102 }
103 }
104
105 fn parse_resume_byte(byte: &str) -> Result<u64, String> {
106 byte.trim().parse::<u64>().map_err(|err| err.to_string())
107 }
108
109 fn parse_percentage(percentage: &str) -> Result<f32, String> {
110 let mut chars = percentage.chars();
111 chars.next_back();
112 chars
113 .as_str()
114 .trim()
115 .parse::<f32>()
116 .map_err(|err| err.to_string())
117 }
118
119 fn parse_total_size(size_str: &str) -> Result<u64, String> {
120 let Some(last_digit_index) = size_str.rfind(|char: char| char.is_ascii_digit()) else{
123 return Err("Incorrectly formatted size string".to_owned());
124 };
125 let (value_str, unit) = size_str.split_at(last_digit_index + 1);
126
127 let value: f64 = value_str
129 .trim()
130 .parse()
131 .map_err(|err: ParseFloatError| err.to_string())?;
132
133 let bytes = match unit {
135 "B" => value,
136 "KB" => value * 1_000.0,
137 "MB" => value * 1_000_000.0,
138 "GB" => value * 1_000_000_000.0,
139 "TB" => value * 1_000_000_000_000.0,
140 "KiB" => value * 1_024.0,
141 "MiB" => value * 1_048_576.0,
142 "GiB" => value * 1_073_741_824.0,
143 "TiB" => value * 1_099_511_627_776.0,
144 _ => return Err(format!("Unrecognized unit: {unit}")),
145 } as u64;
146
147 Ok(bytes)
148 }
149
150 fn parse_download_speed(size_str: &str) -> Result<u64, String> {
151 let Some(last_digit_index) = size_str.rfind(|char: char| char.is_ascii_digit()) else{
154 return Err("Incorrectly formatted size".to_owned());
155 };
156
157 let (value_str, mut unit) = size_str.split_at(last_digit_index + 1);
158
159 let value: f64 = value_str
161 .trim()
162 .parse()
163 .map_err(|err: ParseFloatError| err.to_string())?;
164
165 unit = &unit[..unit.len() - 2];
166
167 let bytes = match unit {
169 "B" => value,
170 "KB" => value * 1_000.0,
171 "MB" => value * 1_000_000.0,
172 "GB" => value * 1_000_000_000.0,
173 "TB" => value * 1_000_000_000_000.0,
174 "KiB" => value * 1_024.0,
175 "MiB" => value * 1_048_576.0,
176 "GiB" => value * 1_073_741_824.0,
177 "TiB" => value * 1_099_511_627_776.0,
178 _ => return Err(format!("Unrecognized unit: {unit}")),
179 } as u64;
180
181 Ok(bytes)
182 }
183
184 fn parse_time(time: &str) -> Result<u64, String> {
185 let parts: Vec<&str> = time.split(':').collect();
186 match parts.len() {
187 1 => {
188 let seconds = parts[0].parse::<u64>().map_err(|err| err.to_string())?;
190 Ok(seconds)
191 }
192 2 => {
193 let minutes = parts[0].parse::<u64>().map_err(|err| err.to_string())?;
195 let seconds = parts[1].parse::<u64>().map_err(|err| err.to_string())?;
196 Ok((minutes * 60) + seconds)
197 }
198 3 => {
199 let hours = parts[0].parse::<u64>().map_err(|err| err.to_string())?;
201 let minutes = parts[1].parse::<u64>().map_err(|err| err.to_string())?;
202 let seconds = parts[2].parse::<u64>().map_err(|err| err.to_string())?;
203 Ok((hours * 3600) + (minutes * 60) + seconds)
204 }
205 4 => {
206 let days = parts[0].parse::<u64>().map_err(|err| err.to_string())?;
208 let hours = parts[1].parse::<u64>().map_err(|err| err.to_string())?;
209 let minutes = parts[2].parse::<u64>().map_err(|err| err.to_string())?;
210 let seconds = parts[3].parse::<u64>().map_err(|err| err.to_string())?;
211 Ok((days * 86400) + (hours * 3600) + (minutes * 60) + seconds)
212 }
213 _ => {
214 Err("Invalid time format".to_owned())
216 }
217 }
218 }
219}