Downloader: Update the progress index of downloads.
This commit is contained in:
parent
eef83fc98c
commit
193117f579
@ -8,6 +8,7 @@ use std::fs::{rename, DirBuilder, File};
|
|||||||
use std::io::{BufWriter, Read, Write};
|
use std::io::{BufWriter, Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
|
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
|
||||||
@ -16,6 +17,11 @@ use hammond_data::xdg_dirs::{DL_DIR, HAMMOND_CACHE};
|
|||||||
// TODO: Replace path that are of type &str with std::path.
|
// TODO: Replace path that are of type &str with std::path.
|
||||||
// TODO: Have a convention/document absolute/relative paths, if they should end with / or not.
|
// TODO: Have a convention/document absolute/relative paths, if they should end with / or not.
|
||||||
|
|
||||||
|
pub trait DownloadProgress {
|
||||||
|
fn set_downloaded(&mut self, downloaded: u64);
|
||||||
|
fn set_size(&mut self, bytes: u64);
|
||||||
|
}
|
||||||
|
|
||||||
// Adapted from https://github.com/mattgathu/rget .
|
// Adapted from https://github.com/mattgathu/rget .
|
||||||
// I never wanted to write a custom downloader.
|
// I never wanted to write a custom downloader.
|
||||||
// Sorry to those who will have to work with that code.
|
// Sorry to those who will have to work with that code.
|
||||||
@ -23,7 +29,12 @@ use hammond_data::xdg_dirs::{DL_DIR, HAMMOND_CACHE};
|
|||||||
// or bindings for a lib like youtube-dl(python),
|
// or bindings for a lib like youtube-dl(python),
|
||||||
// But cant seem to find one.
|
// But cant seem to find one.
|
||||||
// TODO: Write unit-tests.
|
// TODO: Write unit-tests.
|
||||||
fn download_into(dir: &str, file_title: &str, url: &str) -> Result<String> {
|
fn download_into(
|
||||||
|
dir: &str,
|
||||||
|
file_title: &str,
|
||||||
|
url: &str,
|
||||||
|
progress: Option<Arc<Mutex<DownloadProgress>>>,
|
||||||
|
) -> Result<String> {
|
||||||
info!("GET request to: {}", url);
|
info!("GET request to: {}", url);
|
||||||
let client = reqwest::Client::builder().referer(false).build()?;
|
let client = reqwest::Client::builder().referer(false).build()?;
|
||||||
let mut resp = client.get(url).send()?;
|
let mut resp = client.get(url).send()?;
|
||||||
@ -47,8 +58,15 @@ fn download_into(dir: &str, file_title: &str, url: &str) -> Result<String> {
|
|||||||
let tempdir = TempDir::new_in(HAMMOND_CACHE.to_str().unwrap(), "temp_download")?;
|
let tempdir = TempDir::new_in(HAMMOND_CACHE.to_str().unwrap(), "temp_download")?;
|
||||||
let out_file = format!("{}/temp.part", tempdir.path().to_str().unwrap(),);
|
let out_file = format!("{}/temp.part", tempdir.path().to_str().unwrap(),);
|
||||||
|
|
||||||
|
ct_len.map(|x| {
|
||||||
|
if let Some(p) = progress.clone() {
|
||||||
|
let mut m = p.lock().unwrap();
|
||||||
|
m.set_size(x);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Save requested content into the file.
|
// Save requested content into the file.
|
||||||
save_io(&out_file, &mut resp, ct_len)?;
|
save_io(&out_file, &mut resp, ct_len, progress)?;
|
||||||
|
|
||||||
// Construct the desired path.
|
// Construct the desired path.
|
||||||
let target = format!("{}/{}.{}", dir, file_title, ext);
|
let target = format!("{}/{}.{}", dir, file_title, ext);
|
||||||
@ -73,8 +91,14 @@ fn get_ext(content: Option<ContentType>) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write unit-tests.
|
// TODO: Write unit-tests.
|
||||||
|
// TODO: Refactor... Somehow.
|
||||||
/// Handles the I/O of fetching a remote file and saving into a Buffer and A File.
|
/// Handles the I/O of fetching a remote file and saving into a Buffer and A File.
|
||||||
fn save_io(file: &str, resp: &mut reqwest::Response, content_lenght: Option<u64>) -> Result<()> {
|
fn save_io(
|
||||||
|
file: &str,
|
||||||
|
resp: &mut reqwest::Response,
|
||||||
|
content_lenght: Option<u64>,
|
||||||
|
progress: Option<Arc<Mutex<DownloadProgress>>>,
|
||||||
|
) -> Result<()> {
|
||||||
info!("Downloading into: {}", file);
|
info!("Downloading into: {}", file);
|
||||||
let chunk_size = match content_lenght {
|
let chunk_size = match content_lenght {
|
||||||
Some(x) => x as usize / 99,
|
Some(x) => x as usize / 99,
|
||||||
@ -89,6 +113,14 @@ fn save_io(file: &str, resp: &mut reqwest::Response, content_lenght: Option<u64>
|
|||||||
buffer.truncate(bcount);
|
buffer.truncate(bcount);
|
||||||
if !buffer.is_empty() {
|
if !buffer.is_empty() {
|
||||||
writer.write_all(buffer.as_slice())?;
|
writer.write_all(buffer.as_slice())?;
|
||||||
|
if let Some(prog) = progress.clone() {
|
||||||
|
// This sucks.
|
||||||
|
let len = writer.get_ref().metadata().map(|x| x.len());
|
||||||
|
if let Ok(l) = len {
|
||||||
|
let mut m = prog.lock().unwrap();
|
||||||
|
m.set_downloaded(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -107,7 +139,11 @@ pub fn get_download_folder(pd_title: &str) -> Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refactor
|
// TODO: Refactor
|
||||||
pub fn get_episode(ep: &mut EpisodeWidgetQuery, download_folder: &str) -> Result<()> {
|
pub fn get_episode(
|
||||||
|
ep: &mut EpisodeWidgetQuery,
|
||||||
|
download_folder: &str,
|
||||||
|
progress: Option<Arc<Mutex<DownloadProgress>>>,
|
||||||
|
) -> Result<()> {
|
||||||
// Check if its alrdy downloaded
|
// Check if its alrdy downloaded
|
||||||
if ep.local_uri().is_some() {
|
if ep.local_uri().is_some() {
|
||||||
if Path::new(ep.local_uri().unwrap()).exists() {
|
if Path::new(ep.local_uri().unwrap()).exists() {
|
||||||
@ -119,7 +155,12 @@ pub fn get_episode(ep: &mut EpisodeWidgetQuery, download_folder: &str) -> Result
|
|||||||
ep.save()?;
|
ep.save()?;
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = download_into(download_folder, &ep.rowid().to_string(), ep.uri().unwrap());
|
let res = download_into(
|
||||||
|
download_folder,
|
||||||
|
&ep.rowid().to_string(),
|
||||||
|
ep.uri().unwrap(),
|
||||||
|
progress,
|
||||||
|
);
|
||||||
|
|
||||||
if let Ok(path) = res {
|
if let Ok(path) = res {
|
||||||
// If download succedes set episode local_uri to dlpath.
|
// If download succedes set episode local_uri to dlpath.
|
||||||
@ -166,7 +207,7 @@ pub fn cache_image(pd: &PodcastCoverQuery) -> Option<String> {
|
|||||||
.create(&cache_download_fold)
|
.create(&cache_download_fold)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match download_into(&cache_download_fold, "cover", &url) {
|
match download_into(&cache_download_fold, "cover", &url, None) {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
info!("Cached img into: {}", &path);
|
info!("Cached img into: {}", &path);
|
||||||
Some(path)
|
Some(path)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// use hammond_data::Episode;
|
// use hammond_data::Episode;
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
use hammond_downloader::downloader::get_episode;
|
use hammond_downloader::downloader::get_episode;
|
||||||
|
use hammond_downloader::downloader::DownloadProgress;
|
||||||
|
|
||||||
use app::Action;
|
use app::Action;
|
||||||
|
|
||||||
@ -18,15 +19,33 @@ pub struct Progress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Progress {
|
impl Progress {
|
||||||
pub fn new(size: u64) -> Self {
|
pub fn get_fraction(&self) -> f64 {
|
||||||
|
info!("Progress: {:?}", self);
|
||||||
|
let ratio = self.downloaded_bytes as f64 / self.total_bytes as f64;
|
||||||
|
|
||||||
|
if ratio >= 1.0 {
|
||||||
|
return 1.0;
|
||||||
|
};
|
||||||
|
ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Progress {
|
||||||
|
fn default() -> Self {
|
||||||
Progress {
|
Progress {
|
||||||
total_bytes: size,
|
total_bytes: 0,
|
||||||
downloaded_bytes: 0,
|
downloaded_bytes: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_fraction(&self) -> f64 {
|
impl DownloadProgress for Progress {
|
||||||
self.downloaded_bytes as f64 / self.total_bytes as f64
|
fn set_downloaded(&mut self, downloaded: u64) {
|
||||||
|
self.downloaded_bytes = downloaded
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_size(&mut self, bytes: u64) {
|
||||||
|
self.total_bytes = bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,17 +55,23 @@ lazy_static! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(id: i32, directory: &str, sender: Sender<Action>, prog: Arc<Mutex<Progress>>) {
|
pub fn add(id: i32, directory: &str, sender: Sender<Action>) {
|
||||||
|
// Create a new `Progress` struct to keep track of dl progress.
|
||||||
|
let prog = Arc::new(Mutex::new(Progress::default()));
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut m = ACTIVE_DOWNLOADS.write().unwrap();
|
let mut m = ACTIVE_DOWNLOADS.write().unwrap();
|
||||||
m.insert(id, prog.clone());
|
m.insert(id, prog.clone());
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
let m = ACTIVE_DOWNLOADS.read().unwrap();
|
||||||
|
info!("ACTIVE DOWNLOADS: {:#?}", m);
|
||||||
|
}
|
||||||
|
|
||||||
let dir = directory.to_owned();
|
let dir = directory.to_owned();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
info!("{:?}", prog); // just checking that it compiles
|
|
||||||
let episode = dbqueries::get_episode_from_rowid(id).unwrap();
|
let episode = dbqueries::get_episode_from_rowid(id).unwrap();
|
||||||
let e = get_episode(&mut episode.into(), dir.as_str());
|
let e = get_episode(&mut episode.into(), dir.as_str(), Some(prog));
|
||||||
if let Err(err) = e {
|
if let Err(err) = e {
|
||||||
error!("Error: {}", err);
|
error!("Error: {}", err);
|
||||||
};
|
};
|
||||||
@ -105,10 +130,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (sender, _rx) = channel();
|
let (sender, _rx) = channel();
|
||||||
let prog = Arc::new(Mutex::new(Progress::new(42)));
|
|
||||||
|
|
||||||
let download_fold = downloader::get_download_folder(&pd.title()).unwrap();
|
let download_fold = downloader::get_download_folder(&pd.title()).unwrap();
|
||||||
add(episode.rowid(), download_fold.as_str(), sender, prog);
|
add(episode.rowid(), download_fold.as_str(), sender);
|
||||||
|
|
||||||
// Give it soem time to download the file
|
// Give it soem time to download the file
|
||||||
thread::sleep(time::Duration::from_secs(40));
|
thread::sleep(time::Duration::from_secs(40));
|
||||||
|
|||||||
@ -17,7 +17,6 @@ use app::Action;
|
|||||||
use manager;
|
use manager;
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -80,8 +79,6 @@ impl EpisodeWidget {
|
|||||||
widget
|
widget
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: calculate lenght.
|
|
||||||
// TODO: wire the progress_bar to the downloader.
|
|
||||||
// TODO: wire the cancel button.
|
// TODO: wire the cancel button.
|
||||||
fn init(&self, episode: &mut EpisodeWidgetQuery, sender: Sender<Action>) {
|
fn init(&self, episode: &mut EpisodeWidgetQuery, sender: Sender<Action>) {
|
||||||
WidgetExt::set_name(&self.container, &episode.rowid().to_string());
|
WidgetExt::set_name(&self.container, &episode.rowid().to_string());
|
||||||
@ -218,13 +215,24 @@ impl EpisodeWidget {
|
|||||||
let m = prog.lock().unwrap();
|
let m = prog.lock().unwrap();
|
||||||
m.get_fraction()
|
m.get_fraction()
|
||||||
};
|
};
|
||||||
progress_bar.set_fraction(fraction);
|
|
||||||
// info!("Fraction: {}", progress_bar.get_fraction());
|
|
||||||
|
|
||||||
if fraction != 1.0{
|
// I hate floating points.
|
||||||
glib::Continue(true)
|
if (fraction >= 0.0) && (fraction <= 1.0) && (!fraction.is_nan()) {
|
||||||
} else {
|
progress_bar.set_fraction(fraction);
|
||||||
|
}
|
||||||
|
// info!("Fraction: {}", progress_bar.get_fraction());
|
||||||
|
// info!("Fraction: {}", fraction);
|
||||||
|
let active = {
|
||||||
|
let m = manager::ACTIVE_DOWNLOADS.read().unwrap();
|
||||||
|
m.contains_key(&id)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (fraction >= 1.0) && (!fraction.is_nan()){
|
||||||
glib::Continue(false)
|
glib::Continue(false)
|
||||||
|
} else if !active {
|
||||||
|
glib::Continue(false)
|
||||||
|
}else {
|
||||||
|
glib::Continue(true)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -236,10 +244,8 @@ fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: Sender<Action>) {
|
|||||||
let pd = dbqueries::get_podcast_from_id(ep.podcast_id()).unwrap();
|
let pd = dbqueries::get_podcast_from_id(ep.podcast_id()).unwrap();
|
||||||
let download_fold = downloader::get_download_folder(&pd.title().to_owned()).unwrap();
|
let download_fold = downloader::get_download_folder(&pd.title().to_owned()).unwrap();
|
||||||
|
|
||||||
// Create a new `Progress` struct to keep track of dl progress.
|
|
||||||
let prog = Arc::new(Mutex::new(manager::Progress::new(42)));
|
|
||||||
// Start a new download.
|
// Start a new download.
|
||||||
manager::add(ep.rowid(), &download_fold, sender.clone(), prog.clone());
|
manager::add(ep.rowid(), &download_fold, sender.clone());
|
||||||
|
|
||||||
// Update Views
|
// Update Views
|
||||||
sender.send(Action::RefreshEpisodesView).unwrap();
|
sender.send(Action::RefreshEpisodesView).unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user