Slightly improved error recoverability
This commit is contained in:
parent
9edb2daf86
commit
052c3a358a
@ -25,6 +25,9 @@ pub enum DlStatus {
|
|||||||
Done {
|
Done {
|
||||||
duration_ms: u64
|
duration_ms: u64
|
||||||
},
|
},
|
||||||
|
DoneErr {
|
||||||
|
filename: String
|
||||||
|
},
|
||||||
Message(String),
|
Message(String),
|
||||||
/// Like Message but triggers a display refresh
|
/// Like Message but triggers a display refresh
|
||||||
MessageNow(String)
|
MessageNow(String)
|
||||||
@ -36,6 +39,7 @@ pub struct DlReport {
|
|||||||
pub status: DlStatus
|
pub status: DlStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct DlReporter {
|
pub struct DlReporter {
|
||||||
id: i32,
|
id: i32,
|
||||||
transmitter: mpsc::UnboundedSender<DlReport>
|
transmitter: mpsc::UnboundedSender<DlReport>
|
||||||
@ -191,6 +195,23 @@ pub async fn watch_and_print_reports(mut receiver: mpsc::UnboundedReceiver<DlRep
|
|||||||
|
|
||||||
statuses.remove(&update.id);
|
statuses.remove(&update.id);
|
||||||
|
|
||||||
|
},
|
||||||
|
DlStatus::DoneErr {
|
||||||
|
filename
|
||||||
|
} => {
|
||||||
|
|
||||||
|
msg_queue.push_back(format!(
|
||||||
|
"Error: Download failed: '{}'", filename
|
||||||
|
));
|
||||||
|
|
||||||
|
// Don't care if it exists, just make sure it is gone
|
||||||
|
statuses.remove(&update.id);
|
||||||
|
|
||||||
|
|
||||||
|
// Refresh display
|
||||||
|
moved_lines = print_accumulated_report(&statuses, &mut msg_queue, moved_lines)?;
|
||||||
|
t_last = SystemTime::now();
|
||||||
|
|
||||||
},
|
},
|
||||||
DlStatus::Message(msg) => {
|
DlStatus::Message(msg) => {
|
||||||
msg_queue.push_back(msg);
|
msg_queue.push_back(msg);
|
||||||
@ -209,7 +230,7 @@ pub async fn watch_and_print_reports(mut receiver: mpsc::UnboundedReceiver<DlRep
|
|||||||
execute!(
|
execute!(
|
||||||
stdout(),
|
stdout(),
|
||||||
MoveToPreviousLine(2),
|
MoveToPreviousLine(2),
|
||||||
Print("All done!"),
|
Print("All done!\n"),
|
||||||
Clear(ClearType::FromCursorDown)
|
Clear(ClearType::FromCursorDown)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
108
src/download.rs
108
src/download.rs
@ -4,7 +4,8 @@ use std::time::SystemTime;
|
|||||||
use percent_encoding::percent_decode_str;
|
use percent_encoding::percent_decode_str;
|
||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use futures::future::join_all;
|
use futures::stream::FuturesUnordered;
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::dlreport::*;
|
use crate::dlreport::*;
|
||||||
@ -244,7 +245,7 @@ pub async fn download_feedback_multi(url: &str, into_file: &str, rep: DlReporter
|
|||||||
from_to.1 += rest;
|
from_to.1 += rest;
|
||||||
}
|
}
|
||||||
|
|
||||||
download_feedback_chunks(&url, &into_file, rep, Some(from_to), true).await.unwrap();
|
download_feedback_chunks(&url, &into_file, rep, Some(from_to), true).await.map_err(|e| e.to_string())
|
||||||
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -256,60 +257,85 @@ pub async fn download_feedback_multi(url: &str, into_file: &str, rep: DlReporter
|
|||||||
filename: into_file.to_string()
|
filename: into_file.to_string()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut update_counter = 0;
|
let rep_task = rep.clone();
|
||||||
let mut dl_speeds = vec![0.0_f32; numparal as usize];
|
|
||||||
let mut progresses = vec![0; numparal as usize];
|
|
||||||
|
|
||||||
while let Some(update) = rx.recv().await {
|
joiners.push(tokio::task::spawn(async move {
|
||||||
match update.status {
|
|
||||||
|
|
||||||
DlStatus::Init {
|
let rep = rep_task;
|
||||||
bytes_total: _,
|
let mut update_counter: i32 = 0;
|
||||||
filename: _
|
let mut dl_speeds = vec![0.0_f32; numparal as usize];
|
||||||
} => {
|
let mut progresses = vec![0; numparal as usize];
|
||||||
|
|
||||||
},
|
while let Some(update) = rx.recv().await {
|
||||||
DlStatus::Update {
|
match update.status {
|
||||||
speed_mbps,
|
|
||||||
bytes_curr
|
|
||||||
} => {
|
|
||||||
|
|
||||||
dl_speeds[update.id as usize] = speed_mbps;
|
DlStatus::Init {
|
||||||
progresses[update.id as usize] = bytes_curr;
|
bytes_total: _,
|
||||||
|
filename: _
|
||||||
|
} => {
|
||||||
|
|
||||||
if update_counter >= 0 {
|
},
|
||||||
update_counter = 0;
|
DlStatus::Update {
|
||||||
|
speed_mbps,
|
||||||
|
bytes_curr
|
||||||
|
} => {
|
||||||
|
|
||||||
let speed = dl_speeds.iter().sum();
|
dl_speeds[update.id as usize] = speed_mbps;
|
||||||
let curr = progresses.iter().sum();
|
progresses[update.id as usize] = bytes_curr;
|
||||||
|
|
||||||
rep.send(DlStatus::Update {
|
if update_counter >= 0 {
|
||||||
speed_mbps: speed,
|
update_counter = 0;
|
||||||
bytes_curr: curr
|
|
||||||
})?;
|
|
||||||
|
|
||||||
} else {
|
let speed = dl_speeds.iter().sum();
|
||||||
update_counter += 1;
|
let curr = progresses.iter().sum();
|
||||||
}
|
|
||||||
|
|
||||||
},
|
rep.send(DlStatus::Update {
|
||||||
DlStatus::Done {
|
speed_mbps: speed,
|
||||||
duration_ms: _
|
bytes_curr: curr
|
||||||
} => {
|
}).unwrap();
|
||||||
|
|
||||||
dl_speeds[update.id as usize] = 0.0;
|
} else {
|
||||||
|
update_counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
DlStatus::Done {
|
||||||
|
duration_ms: _
|
||||||
|
} => {
|
||||||
|
|
||||||
// Just forwared everything else to the calling receiver
|
dl_speeds[update.id as usize] = 0.0;
|
||||||
_ => rep.send(update.status)?
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// Just forwared everything else to the calling receiver
|
||||||
|
_ => rep.send(update.status).unwrap()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
let mut joiners: FuturesUnordered<_> = joiners.into_iter().collect();
|
||||||
|
// Validate if the tasks were successful. This will always grab the next completed
|
||||||
|
// task, independent from the original order in the joiners list
|
||||||
|
while let Some(output) = joiners.next().await {
|
||||||
|
|
||||||
|
// If any of the download tasks fail, abort the rest and delete the file
|
||||||
|
// since it is non-recoverable anyways
|
||||||
|
if let Err(e) = output? {
|
||||||
|
|
||||||
|
for handle in joiners.iter() {
|
||||||
|
handle.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::fs::remove_file(&into_file).await?;
|
||||||
|
|
||||||
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
join_all(joiners).await;
|
|
||||||
|
|
||||||
|
|
||||||
// Remove the additional byte at the file end
|
// Remove the additional byte at the file end
|
||||||
let ofile = tokio::fs::OpenOptions::new()
|
let ofile = tokio::fs::OpenOptions::new()
|
||||||
.create(false)
|
.create(false)
|
||||||
@ -320,8 +346,6 @@ pub async fn download_feedback_multi(url: &str, into_file: &str, rep: DlReporter
|
|||||||
|
|
||||||
ofile.set_len(content_length).await?;
|
ofile.set_len(content_length).await?;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rep.send(DlStatus::Done {
|
rep.send(DlStatus::Done {
|
||||||
duration_ms: t_start.elapsed()?.as_millis() as u64
|
duration_ms: t_start.elapsed()?.as_millis() as u64
|
||||||
})?;
|
})?;
|
||||||
|
|||||||
21
src/main.rs
21
src/main.rs
@ -212,13 +212,10 @@ async fn download_multiple(urls: Vec<String>, outdir: &str, numparal: i32, boost
|
|||||||
let url = if is_zippy {
|
let url = if is_zippy {
|
||||||
match zippy::resolve_link(&url).await {
|
match zippy::resolve_link(&url).await {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
Err(e) => {
|
Err(_e) => {
|
||||||
rep.send(
|
rep.send(
|
||||||
DlStatus::MessageNow(format!("Zippyshare link could not be resolved: {}", url))
|
DlStatus::MessageNow(format!("Zippyshare link could not be resolved: {}", url))
|
||||||
).unwrap();
|
).unwrap();
|
||||||
rep.send(
|
|
||||||
DlStatus::MessageNow(format!("{}", e))
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -246,16 +243,18 @@ async fn download_multiple(urls: Vec<String>, outdir: &str, numparal: i32, boost
|
|||||||
}
|
}
|
||||||
|
|
||||||
if boost == 1 {
|
if boost == 1 {
|
||||||
if let Err(e) = download::download_feedback(&url, &into_file, rep).await {
|
if let Err(_e) = download::download_feedback(&url, &into_file, rep.clone()).await {
|
||||||
eprintln!("Error while downloading '{}'", file_name);
|
rep.send(DlStatus::DoneErr {
|
||||||
eprintln!("{}", e);
|
filename: into_file.to_string()
|
||||||
|
}).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Err(e) = download::download_feedback_multi(&url, &into_file, rep, boost).await {
|
if let Err(_e) = download::download_feedback_multi(&url, &into_file, rep.clone(), boost).await {
|
||||||
eprintln!("Error while downloading '{}'", file_name);
|
rep.send(DlStatus::DoneErr {
|
||||||
eprintln!("{}", e);
|
filename: into_file.to_string()
|
||||||
|
}).unwrap();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user