use rayon::prelude::*; use regex::Regex; use reqwest; use rss::Channel; use std::collections::HashSet; use std::fs::{self, DirBuilder, File}; use std::io::{self, BufReader, Read, Write}; use std::process::Command; use structs::*; use utils::*; pub fn list_episodes(search: &str) { let re = Regex::new(&format!("(?i){}", &search)).expect("Failed to parse regex"); let mut path = get_podcast_dir(); path.push(".rss"); DirBuilder::new().recursive(true).create(&path).unwrap(); for entry in fs::read_dir(&path).unwrap() { let entry = entry.unwrap(); if re.is_match(&entry.file_name().into_string().unwrap()) { let file = File::open(&entry.path()).unwrap(); let channel = Channel::read_from(BufReader::new(file)).unwrap(); let podcast = Podcast::from(channel); let episodes = podcast.episodes(); for (num, ep) in episodes.iter().enumerate() { println!("({}) {}", episodes.len() - num, ep.title().unwrap()); } return; } } } pub fn download_rss(url: &str, config: &Config) { println!("Downloading RSS feed..."); let mut path = get_podcast_dir(); path.push(".rss"); DirBuilder::new().recursive(true).create(&path).unwrap(); let mut resp = reqwest::get(url).unwrap(); let mut content: Vec = Vec::new(); resp.read_to_end(&mut content).unwrap(); let channel = Channel::read_from(BufReader::new(&content[..])).unwrap(); let mut filename = String::from(channel.title()); filename.push_str(".xml"); path.push(filename); let mut file = File::create(&path).unwrap(); file.write_all(&content).unwrap(); let download_limit = config.auto_download_limit as usize; if download_limit > 0 { let podcast = Podcast::from(channel); let episodes = podcast.episodes(); &episodes[..download_limit].par_iter().for_each(|ref ep| { if let Err(err) = ep.download(podcast.title()) { eprintln!("Error downloading {}: {}", podcast.title(), err); } }); } } pub fn update_rss(state: &mut State) { println!("Checking for new episodes..."); &state.subs.par_iter_mut().for_each(|sub| { let mut path = get_podcast_dir(); path.push(&sub.title); DirBuilder::new().recursive(true).create(&path).unwrap(); let mut titles = HashSet::new(); for entry in fs::read_dir(&path).unwrap() { let entry = entry.unwrap(); titles.insert(trim_extension(&entry.file_name().into_string().unwrap())); } let mut resp = reqwest::get(&sub.url).unwrap(); let mut content: Vec = Vec::new(); resp.read_to_end(&mut content).unwrap(); let podcast = Podcast::from(Channel::read_from(BufReader::new(&content[..])).unwrap()); path = get_podcast_dir(); path.push(".rss"); let mut filename = String::from(podcast.title()); filename.push_str(".xml"); path.push(&filename); let mut file = File::create(&path).unwrap(); file.write_all(&content).unwrap(); if podcast.episodes().len() > sub.num_episodes { &podcast.episodes()[..podcast.episodes().len() - sub.num_episodes] .par_iter() .for_each(|ref ep| { if let Err(err) = ep.download(podcast.title()) { eprintln!("Error downloading {}: {}", podcast.title(), err); } }); } sub.num_episodes = podcast.episodes().len(); }); } pub fn list_subscriptions(state: &State) { for podcast in &state.subscriptions() { println!("{}", &podcast.title); } } pub fn download_range(state: &State, p_search: &str, e_search: &str) { let re_pod = Regex::new(&format!("(?i){}", &p_search)).expect("Failed to parse regex");; for subscription in &state.subs { if re_pod.is_match(&subscription.title) { match Podcast::from_title(&subscription.title) { Ok(podcast) => match parse_download_episodes(e_search) { Ok(episodes_to_download) => { if let Err(err) = podcast.download_specific(episodes_to_download) { eprintln!("Error: {}", err); } } Err(err) => eprintln!("Error: {}", err), }, Err(err) => eprintln!("Error: {}", err), } } } } pub fn download_episode(state: &State, p_search: &str, e_search: &str) { let re_pod = Regex::new(p_search).unwrap(); let ep_num = e_search.parse::().unwrap(); for subscription in &state.subs { if re_pod.is_match(&subscription.title) { match Podcast::from_title(&subscription.title) { Ok(podcast) => { let episodes = podcast.episodes(); if let Err(err) = episodes[episodes.len() - ep_num].download(podcast.title()) { eprintln!("{}", err); } } Err(err) => eprintln!("Error: {}", err), } } } } pub fn download_all(state: &State, p_search: &str) { let re_pod = Regex::new(&format!("(?i){}", &p_search)).expect("Failed to parse regex"); for subscription in &state.subs { if re_pod.is_match(&subscription.title) { match Podcast::from_title(&subscription.title) { Ok(podcast) => if let Err(err) = podcast.download() { eprintln!("{}", err); }, Err(err) => eprintln!("Error: {}", err), } } } } pub fn play_episode(state: &State, p_search: &str, ep_num_string: &str) { let re_pod = Regex::new(&format!("(?i){}", &p_search)).expect("Failed to parse regex"); let ep_num = ep_num_string.parse::().unwrap(); let mut path = get_xml_dir(); if let Err(err) = DirBuilder::new().recursive(true).create(&path) { eprintln!( "Couldn't create directory: {}\nReason: {}", path.to_str().unwrap(), err ); return; } for subscription in &state.subs { if re_pod.is_match(&subscription.title) { let mut filename = subscription.title.clone(); filename.push_str(".xml"); path.push(filename); let mut file = File::open(&path).unwrap(); let mut content: Vec = Vec::new(); file.read_to_end(&mut content).unwrap(); let podcast = Podcast::from(Channel::read_from(content.as_slice()).unwrap()); let episodes = podcast.episodes(); let episode = episodes[episodes.len() - ep_num].clone(); filename = String::from(episode.title().unwrap()); filename.push_str(episode.extension().unwrap()); path = get_podcast_dir(); path.push(podcast.title()); path.push(filename); if path.exists() { launch_player(path.to_str().unwrap()); } else { launch_player(episode.url().unwrap()); } return; } } } pub fn check_for_update(state: &mut State) { println!("Checking for updates..."); } fn launch_player(url: &str) { if let Err(_) = launch_mpv(&url) { launch_vlc(&url) } } fn launch_mpv(url: &str) -> Result<(), io::Error> { if let Err(err) = Command::new("mpv") .args(&["--audio-display=no", "--ytdl=no", url]) .status() { match err.kind() { io::ErrorKind::NotFound => { eprintln!("Couldn't open mpv\nTrying vlc..."); return Err(err); } _ => eprintln!("Error: {}", err), } } Ok(()) } fn launch_vlc(url: &str) { if let Err(err) = Command::new("vlc").args(&["-I ncurses", url]).status() { match err.kind() { io::ErrorKind::NotFound => { eprintln!("vlc not found in PATH\nAborting..."); } _ => eprintln!("Error: {}", err), } } }