diff options
Diffstat (limited to 'src/download.rs')
| -rw-r--r-- | src/download.rs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/download.rs b/src/download.rs new file mode 100644 index 0000000..69dc853 --- /dev/null +++ b/src/download.rs @@ -0,0 +1,199 @@ +use crate::structs::*; +use crate::utils::*; + +use std::collections::HashSet; +use std::fs::File; +use std::io::{self, BufReader, BufWriter, Write}; + +use failure::Error; +use rayon::prelude::*; +use regex::Regex; +use reqwest; + +pub fn download_range(state: &State, p_search: &str, e_search: &str) -> Result<(), Error> { + let re_pod = Regex::new(&format!("(?i){}", &p_search))?; + + for subscription in &state.subscriptions { + if re_pod.is_match(&subscription.title) { + let podcast = Podcast::from_title(&subscription.title)?; + let downloaded = already_downloaded(podcast.title())?; + let episodes = podcast.episodes(); + let episodes_to_download = parse_download_episodes(e_search)?; + + episodes_to_download + .par_iter() + .map(|ep_num| &episodes[episodes.len() - ep_num]) + .filter(|e| e.title().is_some()) + .filter(|e| !downloaded.contains(&e.title().unwrap())) + .map(|ep| download(podcast.title(), ep)) + .flat_map(|e| e.err()) + .for_each(|err| eprintln!("Error: {}", err)); + } + } + Ok(()) +} + +pub fn download_episode_by_num(state: &State, p_search: &str, e_search: &str) -> Result<(), Error> { + let re_pod = Regex::new(&format!("(?i){}", &p_search))?; + + if let Ok(ep_num) = e_search.parse::<usize>() { + for subscription in &state.subscriptions { + if re_pod.is_match(&subscription.title) { + let podcast = Podcast::from_title(&subscription.title)?; + let episodes = podcast.episodes(); + download(podcast.title(), &episodes[episodes.len() - ep_num])?; + } + } + } else { + eprintln!("Failed to parse episode number...\nAttempting to find episode by name..."); + download_episode_by_name(state, p_search, e_search, false)?; + } + + Ok(()) +} + +pub fn download(podcast_name: &str, episode: &Episode) -> Result<(), Error> { + let mut path = get_podcast_dir()?; + path.push(podcast_name); + create_dir_if_not_exist(&path)?; + + if let (Some(title), Some(url)) = (episode.title(), episode.url()) { + path.push(title); + episode.extension().map(|ext| path.set_extension(ext)); + if !path.exists() { + println!("Downloading: {:?}", &path); + let resp = reqwest::get(url)?; + let file = File::create(&path)?; + let mut reader = BufReader::new(resp); + let mut writer = BufWriter::new(file); + io::copy(&mut reader, &mut writer)?; + } else { + eprintln!("File already exists: {:?}", &path); + } + } + Ok(()) +} + +pub fn download_episode_by_name( + state: &State, + p_search: &str, + e_search: &str, + download_all: bool, +) -> Result<(), Error> { + let re_pod = Regex::new(&format!("(?i){}", &p_search))?; + + for subscription in &state.subscriptions { + if re_pod.is_match(&subscription.title) { + let podcast = Podcast::from_title(&subscription.title)?; + let episodes = podcast.episodes(); + let filtered_episodes = + episodes + .iter() + .filter(|ep| ep.title().is_some()) + .filter(|ep| { + ep.title() + .unwrap() + .to_lowercase() + .contains(&e_search.to_lowercase()) + }); + + if download_all { + filtered_episodes + .map(|ep| download(podcast.title(), ep)) + .flat_map(|e| e.err()) + .for_each(|err| eprintln!("Error: {}", err)); + } else { + filtered_episodes + .take(1) + .map(|ep| download(podcast.title(), ep)) + .flat_map(|e| e.err()) + .for_each(|err| eprintln!("Error: {}", err)); + } + } + } + Ok(()) +} + +pub fn download_all(state: &State, p_search: &str) -> Result<(), Error> { + let re_pod = Regex::new(&format!("(?i){}", &p_search))?; + + for subscription in &state.subscriptions { + if re_pod.is_match(&subscription.title) { + let podcast = Podcast::from_title(&subscription.title)?; + print!( + "You are about to download all episodes of {} (y/n): ", + podcast.title() + ); + io::stdout().flush().ok(); + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + if input.to_lowercase().trim() != "y" { + return Ok(()); + } + + let mut path = get_podcast_dir()?; + path.push(podcast.title()); + + already_downloaded(podcast.title()).map(|downloaded| { + podcast + .episodes() + .par_iter() + .filter(|e| e.title().is_some()) + .filter(|e| !downloaded.contains(&e.title().unwrap())) + .map(|e| download(podcast.title(), e)) + .flat_map(|e| e.err()) + .for_each(|err| eprintln!("Error: {}", err)) + })?; + } + } + Ok(()) +} + +pub fn download_rss(config: &Config, url: &str) -> Result<(), Error> { + let channel = download_rss_feed(url)?; + let mut download_limit = config.auto_download_limit as usize; + if 0 < download_limit { + println!( + "Subscribe auto-download limit set to: {}\nDownloading episode(s)...", + download_limit + ); + let podcast = Podcast::from(channel); + let episodes = podcast.episodes(); + if episodes.len() < download_limit { + download_limit = episodes.len() + } + + episodes[..download_limit] + .par_iter() + .map(|ep| download(podcast.title(), ep)) + .flat_map(|e| e.err()) + .for_each(|err| eprintln!("Error downloading {}: {}", podcast.title(), err)); + } + Ok(()) +} + +fn parse_download_episodes(e_search: &str) -> Result<HashSet<usize>, Error> { + let input = String::from(e_search); + let mut ranges = Vec::<(usize, usize)>::new(); + let mut elements = HashSet::<usize>::new(); + let comma_separated: Vec<&str> = input.split(',').collect(); + for elem in comma_separated { + if elem.contains('-') { + let range: Vec<usize> = elem + .split('-') + .map(|i| i.parse::<usize>()) + .collect::<Result<Vec<usize>, std::num::ParseIntError>>()?; + ranges.push((range[0], range[1])); + } else { + elements.insert(elem.parse::<usize>()?); + } + } + + for range in ranges { + // Include given episode in the download + for num in range.0..=range.1 { + elements.insert(num); + } + } + Ok(elements) +} |
