diff options
| author | Nathan Jaremko | 2019-03-08 01:22:45 -0500 | 
|---|---|---|
| committer | Nathan Jaremko | 2019-03-08 01:22:45 -0500 | 
| commit | 1f529fde9e67f5b9c5a4984453d844357532910f (patch) | |
| tree | a262eaa17b8eff551ba4c67c9c5c6cded1bab698 /src | |
| parent | cf8d69e765549bd08b4e05313927068b96c7cbe7 (diff) | |
| download | podcast-1f529fde9e67f5b9c5a4984453d844357532910f.tar.bz2 | |
Add podcast search support
Diffstat (limited to 'src')
| -rw-r--r-- | src/actions.rs | 24 | ||||
| -rw-r--r-- | src/arg_parser.rs | 60 | ||||
| -rw-r--r-- | src/command_handler.rs | 4 | ||||
| -rw-r--r-- | src/download.rs | 34 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/parser.rs | 12 | ||||
| -rw-r--r-- | src/utils.rs | 13 | 
7 files changed, 115 insertions, 38 deletions
| diff --git a/src/actions.rs b/src/actions.rs index 847d068..2ae30f5 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,7 +1,7 @@  use crate::download;  use crate::errors::*;  use crate::structs::*; -use crate::utils::*; +use crate::utils;  use std::collections::HashSet;  use std::fs::{self, File}; @@ -18,8 +18,8 @@ use toml;  pub fn list_episodes(search: &str) -> Result<()> {      let re = Regex::new(&format!("(?i){}", &search))?; -    let path = get_xml_dir()?; -    create_dir_if_not_exist(&path)?; +    let path = utils::get_xml_dir()?; +    utils::create_dir_if_not_exist(&path)?;      for entry in fs::read_dir(&path)? {          let entry = entry?; @@ -51,14 +51,14 @@ pub fn list_episodes(search: &str) -> Result<()> {  pub fn update_subscription(sub: &mut Subscription) -> Result<()> {      println!("Updating {}", sub.title); -    let mut path: PathBuf = get_podcast_dir()?; +    let mut path: PathBuf = utils::get_podcast_dir()?;      path.push(&sub.title); -    create_dir_if_not_exist(&path)?; +    utils::create_dir_if_not_exist(&path)?;      let mut titles = HashSet::new();      for entry in fs::read_dir(&path)? {          let unwrapped_entry = &entry?; -        titles.insert(trim_extension( +        titles.insert(utils::trim_extension(              &unwrapped_entry.file_name().into_string().unwrap(),          ));      } @@ -66,9 +66,9 @@ pub fn update_subscription(sub: &mut Subscription) -> Result<()> {      let resp = reqwest::get(&sub.url)?;      let podcast = Podcast::from(Channel::read_from(BufReader::new(resp))?); -    let mut podcast_rss_path = get_xml_dir()?; -    podcast_rss_path.push(podcast.title()); -    podcast_rss_path.set_extension("xml"); +    let mut podcast_rss_path = utils::get_xml_dir()?; +    let title = utils::append_extension(podcast.title(), "xml"); +    podcast_rss_path.push(title);      let file = File::create(&podcast_rss_path)?;      (*podcast).write_to(BufWriter::new(file))?; @@ -112,7 +112,7 @@ pub fn check_for_update(version: &str) -> Result<()> {      let config = resp.parse::<toml::Value>()?;      let latest = config["package"]["version"]          .as_str() -        .expect(&format!("Cargo.toml didn't have a version {:?}", config)); +        .unwrap_or_else(|| panic!("Cargo.toml didn't have a version {:?}", config));      if version != latest {          println!("New version available: {} -> {}", version, latest);      } @@ -122,7 +122,7 @@ pub fn check_for_update(version: &str) -> Result<()> {  pub fn remove_podcast(state: &mut State, p_search: &str) -> Result<()> {      if p_search == "*" {          state.subscriptions = vec![]; -        return delete_all(); +        return utils::delete_all();      }      let re_pod = Regex::new(&format!("(?i){}", &p_search))?; @@ -131,7 +131,7 @@ pub fn remove_podcast(state: &mut State, p_search: &str) -> Result<()> {          let title = state.subscriptions[subscription].title.clone();          if re_pod.is_match(&title) {              state.subscriptions.remove(subscription); -            delete(&title)?; +            utils::delete(&title)?;          }      }      Ok(()) diff --git a/src/arg_parser.rs b/src/arg_parser.rs index 8f9650a..6438af3 100644 --- a/src/arg_parser.rs +++ b/src/arg_parser.rs @@ -1,12 +1,15 @@  use clap::{App, ArgMatches};  use std::env; +use std::io; +use std::io::Write;  use std::path::Path;  use crate::actions::*;  use crate::download;  use crate::errors::*;  use crate::playback; +use crate::search;  use crate::structs::*;  pub fn download(state: &mut State, matches: &ArgMatches) -> Result<()> { @@ -60,14 +63,19 @@ pub fn play(state: &mut State, matches: &ArgMatches) -> Result<()> {      Ok(())  } -pub fn subscribe(state: &mut State, config: &Config, matches: &ArgMatches) -> Result<()> { +pub fn subscribe(state: &mut State, config: Config, matches: &ArgMatches) -> Result<()> {      let subscribe_matches = matches          .subcommand_matches("sub")          .or_else(|| matches.subcommand_matches("subscribe"))          .unwrap();      let url = subscribe_matches.value_of("URL").unwrap(); +    sub(state, config, url)?; +    Ok(()) +} + +fn sub(state: &mut State, config: Config, url: &str) -> Result<()> {      state.subscribe(url)?; -    download::download_rss(&config, url)?; +    download::download_rss(config, url)?;      Ok(())  } @@ -94,3 +102,51 @@ pub fn complete(app: &mut App, matches: &ArgMatches) -> Result<()> {      }      Ok(())  } + +pub fn search(state: &mut State, config: Config, matches: &ArgMatches) -> Result<()> { +    let matches = matches.subcommand_matches("search").unwrap(); +    let podcast = matches.value_of("PODCAST").unwrap(); +    let resp = search::search_for_podcast(podcast)?; +    if resp.found().is_empty() { +        println!("No Results"); +        return Ok(()); +    } + +    { +        let stdout = io::stdout(); +        let mut lock = stdout.lock(); +        for (i, r) in resp.found().iter().enumerate() { +            writeln!(&mut lock, "({}) {}", i, r)?; +        } +    } + +    print!("Would you like to subscribe to any of these? (y/n): "); +    io::stdout().flush().ok(); +    let mut input = String::new(); +    io::stdin().read_line(&mut input)?; +    if input.to_lowercase().trim() != "y" { +        return Ok(()); +    } + +    print!("Which one? (#): "); +    io::stdout().flush().ok(); +    let mut num_input = String::new(); +    io::stdin().read_line(&mut num_input)?; +    let n: usize = num_input.trim().parse()?; +    if n > resp.found().len() { +        eprintln!("Invalid!"); +        return Ok(()); +    } + +    let rss_resp = search::retrieve_rss(&resp.found()[n])?; +    if let Some(err) = rss_resp.error() { +        eprintln!("{}", err); +        return Ok(()); +    } +    match rss_resp.url() { +        Some(r) => sub(state, config, r)?, +        None => eprintln!("Subscription failed. No url in API response."), +    } +     +    Ok(()) +} diff --git a/src/command_handler.rs b/src/command_handler.rs index 742b79e..f65028b 100644 --- a/src/command_handler.rs +++ b/src/command_handler.rs @@ -24,7 +24,7 @@ pub fn parse_sub_command(matches: &ArgMatches) -> commands::Command {  pub fn handle_matches(      version: &str,      state: &mut State, -    config: &Config, +    config: Config,      app: &mut App,      matches: &ArgMatches,  ) -> Result<()> { @@ -43,7 +43,7 @@ pub fn handle_matches(              arg_parser::subscribe(state, config, matches)?;          }          commands::Command::Search => { -            println!("This feature is coming soon..."); +            arg_parser::search(state, config, matches)?;          }          commands::Command::Remove => {              arg_parser::remove(state, matches)?; diff --git a/src/download.rs b/src/download.rs index c58c9d8..7acc0b0 100644 --- a/src/download.rs +++ b/src/download.rs @@ -1,10 +1,10 @@  use crate::structs::*; -use crate::utils::*; +use crate::utils; +use failure::Fail;  use std::collections::HashSet;  use std::fs::File;  use std::io::{self, BufReader, BufWriter, Write}; -use failure::Fail;  use failure::Error;  use rayon::prelude::*; @@ -53,23 +53,18 @@ pub fn download_episode_by_num(state: &State, p_search: &str, e_search: &str) ->  #[derive(Debug, Fail)]  enum DownloadError {      #[fail(display = "File already exists: {}", path)] -    AlreadyExists { -        path: String, -    } +    AlreadyExists { path: String },  }  pub fn download(podcast_name: &str, episode: &Episode) -> Result<(), Error> { -    let mut path = get_podcast_dir()?; +    let mut path = utils::get_podcast_dir()?;      path.push(podcast_name); -    create_dir_if_not_exist(&path)?; +    utils::create_dir_if_not_exist(&path)?;      if let (Some(mut title), Some(url)) = (episode.title(), episode.url()) { -        episode.extension().map(|ext|  { -            if !title.ends_with(".") { -                title.push_str("."); -            } -            title.push_str(&ext); -        });    +        if let Some(ext) = episode.extension() { +            title = utils::append_extension(&title, &ext); +        }          path.push(title);          if !path.exists() {              println!("Downloading: {:?}", &path); @@ -79,7 +74,10 @@ pub fn download(podcast_name: &str, episode: &Episode) -> Result<(), Error> {              let mut writer = BufWriter::new(file);              io::copy(&mut reader, &mut writer)?;          } else { -            return Err(DownloadError::AlreadyExists{path: path.to_str().unwrap().to_string()}.into()); +            return Err(DownloadError::AlreadyExists { +                path: path.to_str().unwrap().to_string(), +            } +            .into());          }      }      Ok(()) @@ -142,10 +140,10 @@ pub fn download_all(state: &State, p_search: &str) -> Result<(), Error> {                  return Ok(());              } -            let mut path = get_podcast_dir()?; +            let mut path = utils::get_podcast_dir()?;              path.push(podcast.title()); -            already_downloaded(podcast.title()).map(|downloaded| { +            utils::already_downloaded(podcast.title()).map(|downloaded| {                  podcast                      .episodes()                      .par_iter() @@ -160,8 +158,8 @@ pub fn download_all(state: &State, p_search: &str) -> Result<(), Error> {      Ok(())  } -pub fn download_rss(config: &Config, url: &str) -> Result<(), Error> { -    let channel = download_rss_feed(url)?; +pub fn download_rss(config: Config, url: &str) -> Result<(), Error> { +    let channel = utils::download_rss_feed(url)?;      let mut download_limit = config.auto_download_limit as usize;      if 0 < download_limit {          println!( diff --git a/src/main.rs b/src/main.rs index 780f87b..08fa545 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ extern crate reqwest;  extern crate rss;  #[macro_use]  extern crate serde_derive; +extern crate percent_encoding;  extern crate serde_json;  extern crate serde_yaml;  extern crate toml; @@ -26,6 +27,7 @@ mod download;  mod migration_handler;  mod parser;  mod playback; +mod search;  mod structs;  mod utils; @@ -38,7 +40,7 @@ mod errors {  use self::structs::*;  use errors::Result; -const VERSION: &str = "0.10.3"; +const VERSION: &str = "0.11.0";  fn main() -> Result<()> {      utils::create_directories()?; @@ -47,6 +49,6 @@ fn main() -> Result<()> {      let config = Config::new()?;      let mut app = parser::get_app(&VERSION);      let matches = app.clone().get_matches(); -    command_handler::handle_matches(&VERSION, &mut state, &config, &mut app, &matches)?; +    command_handler::handle_matches(&VERSION, &mut state, config, &mut app, &matches)?;      state.save()  } diff --git a/src/parser.rs b/src/parser.rs index caae070..743da6d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -83,6 +83,18 @@ pub fn get_app<'a, 'b>(version: &'a str) -> App<'a, 'b> {                      Arg::with_name("debug")                          .short("d")                          .help("print debug information verbosely"), +                ) +                .arg( +                    Arg::with_name("PODCAST") +                        .help("Regex for subscribed podcast") +                        .required(true) +                        .index(1), +                ) +                .arg( +                    Arg::with_name("EPISODE") +                        .help("Episode index") +                        .required(false) +                        .index(2),                  ),          )          .subcommand( diff --git a/src/utils.rs b/src/utils.rs index bb96909..21b01a7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -13,7 +13,7 @@ const UNSUBSCRIBE_NOTE: &str = "Note: this does NOT delete any downloaded podcas  pub fn trim_extension(filename: &str) -> Option<String> {      let name = String::from(filename); -    if name.contains(".") { +    if name.contains('.') {          name.rfind('.').map(|index| String::from(&name[0..index]))      } else {          Some(name) @@ -21,7 +21,7 @@ pub fn trim_extension(filename: &str) -> Option<String> {  }  pub fn find_extension(input: &str) -> Option<String> { -    let s: Vec<String> = input.split(".").map(|s| s.to_string()).collect(); +    let s: Vec<String> = input.split('.').map(|s| s.to_string()).collect();      if s.len() > 1 {          return s.last().cloned();      } @@ -39,6 +39,15 @@ pub fn get_podcast_dir() -> Result<PathBuf> {      }  } +pub fn append_extension(filename: &str, ext: &str) -> String { +    let mut f = filename.to_string(); +    if !f.ends_with('.') { +        f.push_str("."); +    } +    f.push_str(ext); +    f +} +  pub fn create_dir_if_not_exist(path: &PathBuf) -> Result<()> {      DirBuilder::new().recursive(true).create(&path)?;      Ok(()) | 
