aboutsummaryrefslogtreecommitdiffstats
path: root/src/download.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/download.rs')
-rw-r--r--src/download.rs199
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)
+}