podcasts/podcasts-gtk/src/manager.rs
Jordan Petridis 471f6ff93b
Source: Remove ignore_etags option
This is never used anywhere else apart from the testsuite. Instead
of ignoring etags we should instead not save them if the feed does
not return 200 or 304. See #64.
2018-08-14 13:40:33 +03:00

185 lines
5.3 KiB
Rust

use failure::Error;
use rayon;
// use podcasts_data::Episode;
use podcasts_data::dbqueries;
use podcasts_downloader::downloader::{get_episode, DownloadProgress};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
// use std::sync::atomic::AtomicUsize;
// use std::path::PathBuf;
// This is messy, undocumented and hacky af.
// I am terrible at writing downloaders and download managers.
#[derive(Debug)]
pub(crate) struct Progress {
total_bytes: u64,
downloaded_bytes: u64,
cancel: bool,
}
impl Default for Progress {
fn default() -> Self {
Progress {
total_bytes: 0,
downloaded_bytes: 0,
cancel: false,
}
}
}
impl Progress {
pub(crate) fn get_fraction(&self) -> f64 {
let ratio = self.downloaded_bytes as f64 / self.total_bytes as f64;
debug!("{:?}", self);
debug!("Ratio completed: {}", ratio);
if ratio >= 1.0 {
return 1.0;
};
ratio
}
pub(crate) fn get_total_size(&self) -> u64 {
self.total_bytes
}
pub(crate) fn get_downloaded(&self) -> u64 {
self.downloaded_bytes
}
pub(crate) fn cancel(&mut self) {
self.cancel = true;
}
}
impl DownloadProgress for Progress {
fn set_downloaded(&mut self, downloaded: u64) {
self.downloaded_bytes = downloaded
}
fn set_size(&mut self, bytes: u64) {
self.total_bytes = bytes;
}
fn should_cancel(&self) -> bool {
self.cancel
}
}
lazy_static! {
pub(crate) static ref ACTIVE_DOWNLOADS: Arc<RwLock<HashMap<i32, Arc<Mutex<Progress>>>>> =
{ Arc::new(RwLock::new(HashMap::new())) };
static ref DLPOOL: rayon::ThreadPool = rayon::ThreadPoolBuilder::new().build().unwrap();
}
pub(crate) fn add(id: i32, directory: String) -> Result<(), Error> {
// Create a new `Progress` struct to keep track of dl progress.
let prog = Arc::new(Mutex::new(Progress::default()));
match ACTIVE_DOWNLOADS.write() {
Ok(mut guard) => guard.insert(id, prog.clone()),
Err(err) => return Err(format_err!("ActiveDownloads: {}.", err)),
};
DLPOOL.spawn(move || {
if let Ok(mut episode) = dbqueries::get_episode_widget_from_rowid(id) {
let id = episode.rowid();
get_episode(&mut episode, directory.as_str(), Some(prog))
.map_err(|err| error!("Download Failed: {}", err))
.ok();
if let Ok(mut m) = ACTIVE_DOWNLOADS.write() {
let foo = m.remove(&id);
debug!("Removed: {:?}", foo);
}
// if let Ok(m) = ACTIVE_DOWNLOADS.read() {
// debug!("ACTIVE DOWNLOADS: {:#?}", m);
// }
}
});
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use podcasts_data::dbqueries;
use podcasts_data::pipeline;
use podcasts_data::utils::get_download_folder;
use podcasts_data::{Episode, Source};
use podcasts_downloader::downloader::get_episode;
use std::fs;
use std::path::Path;
use std::{thread, time};
#[test]
// This test inserts an rss feed to your `XDG_DATA/podcasts/podcasts.db` so we make it explicit
// to run it.
#[ignore]
// THIS IS NOT A RELIABLE TEST
// Just quick sanity check
fn test_start_dl() {
let url = "https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff";
// Create and index a source
let source = Source::from_url(url).unwrap();
// Copy its id
let sid = source.id();
pipeline::run(vec![source]).unwrap();
// Get the podcast
let pd = dbqueries::get_podcast_from_source_id(sid).unwrap();
let title = "Coming soon... The Tip Off";
// Get an episode
let episode: Episode = dbqueries::get_episode_from_pk(title, pd.id()).unwrap();
let download_fold = get_download_folder(&pd.title()).unwrap();
let fold2 = download_fold.clone();
add(episode.rowid(), download_fold).unwrap();
assert_eq!(ACTIVE_DOWNLOADS.read().unwrap().len(), 1);
// Give it some time to download the file
thread::sleep(time::Duration::from_secs(20));
let final_path = format!("{}/{}.mp3", &fold2, episode.rowid());
assert!(Path::new(&final_path).exists());
fs::remove_file(final_path).unwrap();
}
#[test]
// This test needs access to local system so we ignore it by default.
#[ignore]
fn test_dl_steal_the_stars() {
let url =
"https://web.archive.org/web/20180120104957if_/https://rss.art19.com/steal-the-stars";
// Create and index a source
let source = Source::from_url(url).unwrap();
// Copy its id
let sid = source.id();
pipeline::run(vec![source]).unwrap();
// Get the podcast
let pd = dbqueries::get_podcast_from_source_id(sid).unwrap();
let title = "Introducing Steal the Stars";
// Get an episode
let mut episode = dbqueries::get_episode_from_pk(title, pd.id())
.unwrap()
.into();
let download_fold = get_download_folder(&pd.title()).unwrap();
get_episode(&mut episode, &download_fold, None).unwrap();
let final_path = format!("{}/{}.mp3", &download_fold, episode.rowid());
assert!(Path::new(&final_path).exists());
fs::remove_file(final_path).unwrap();
}
}