diff options
| -rw-r--r-- | src/actions.rs | 51 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/structs.rs | 61 | ||||
| -rw-r--r-- | src/utils.rs | 29 | 
4 files changed, 119 insertions, 28 deletions
| diff --git a/src/actions.rs b/src/actions.rs index 120d58b..8f54359 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -1,10 +1,11 @@  use regex::Regex;  use reqwest; +use rayon::prelude::*;  use rss::Channel; -use std::fs::{DirBuilder, File}; +use std::collections::HashSet; +use std::fs::{self, DirBuilder, File};  use std::io::{self, BufReader, Read, Write};  use std::process::Command; -use rayon::prelude::*;  use structs::*;  use utils::*; @@ -26,7 +27,7 @@ pub fn list_episodes(state: &State, search: &str) {      }  } -pub fn download_rss(url: &str) { +pub fn download_rss(url: &str, config: &Config) {      println!("Downloading RSS feed...");      let mut path = get_podcast_dir();      path.push(".rss"); @@ -40,23 +41,53 @@ pub fn download_rss(url: &str) {      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: &State) { -    println!("Updating RSS feeds..."); -    state.subscriptions().par_iter().for_each(|ref sub| { +pub fn update_rss(state: &mut State) { +    println!("Checking for new episodes..."); +    state.subs.par_iter_mut().for_each(|mut sub| {          let mut path = get_podcast_dir(); -        path.push(".rss"); +        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<u8> = 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()); +        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); +        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();      });  } diff --git a/src/main.rs b/src/main.rs index 6d56a33..800c867 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ extern crate serde;  #[macro_use]  extern crate serde_derive;  extern crate serde_json; +extern crate yaml_rust;  mod actions;  mod structs; @@ -19,7 +20,7 @@ use structs::*;  fn main() {      let mut state = State::new(); - +    let config = Config::new();      let matches = App::new("podcast")          .version("1.0")          .author("Nathan J. <njaremko@gmail.com>") @@ -117,10 +118,11 @@ fn main() {                      .unwrap()                      .value_of("URL")                      .unwrap(), +                &config,              )          }          Some("search") => (), -        Some("update") => update_rss(&state), +        Some("update") => update_rss(&mut state),          _ => (),      }      if let Err(err) = state.save() { diff --git a/src/structs.rs b/src/structs.rs index 07cb901..bb42cdb 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -8,11 +8,46 @@ use std::collections::BTreeSet;  use std::fs::{self, DirBuilder, File};  use std::io::{self, Read, Write};  use utils::*; +use yaml_rust::YamlLoader; + +pub struct Config { +    pub auto_download_limit: i64, +    pub auto_delete_limit: i64, +} + +impl Config { +    pub fn new() -> Config { +        let mut path = get_podcast_dir(); +        let mut download_limit = 1; +        let mut delete_limit = 0; +        path.push(".config"); +        if path.exists() { +            let mut s = String::new(); +            File::open(&path).unwrap().read_to_string(&mut s).unwrap(); +            let config = YamlLoader::load_from_str(&s).unwrap(); +            if config.len() > 0 { +                let doc = &config[0]; +                if let Some(val) = doc["auto_download_limit"].as_i64() { +                    download_limit = val; +                } +                if let Some(val) = doc["auto_delete_limit"].as_i64() { +                    delete_limit = val; +                } +            } +        } +        Config { +            auto_download_limit: download_limit, +            auto_delete_limit: delete_limit, +        } +    } +} +  #[derive(Serialize, Deserialize, Clone)]  pub struct Subscription { -    title: String, -    url: String, +    pub title: String, +    pub url: String, +    pub num_episodes: usize,  }  impl Subscription { @@ -27,8 +62,8 @@ impl Subscription {  #[derive(Serialize, Deserialize, Clone)]  pub struct State { -    last_run_time: DateTime<Utc>, -    subs: Vec<Subscription>, +    pub last_run_time: DateTime<Utc>, +    pub subs: Vec<Subscription>,  }  impl State { @@ -45,7 +80,7 @@ impl State {                  .signed_duration_since(Utc::now())                  .num_seconds() < -86400              { -                update_rss(&state.clone()); +                update_rss(&mut state);              }              state.last_run_time = Utc::now();              state @@ -57,22 +92,23 @@ impl State {          }      } -    pub fn subscribe(&mut self, url: &str) { +    pub fn subscribe(&mut self, url: &str, config: &Config) {          let mut set = BTreeSet::new();          for sub in self.subscriptions() {              set.insert(sub.title());          } -        let channel = Channel::from_url(url).unwrap(); -        if !set.contains(channel.title()) { +        let podcast = Podcast::from(Channel::from_url(url).unwrap()); +        if !set.contains(podcast.title()) {              self.subs.push(Subscription { -                title: String::from(channel.title()), +                title: String::from(podcast.title()),                  url: String::from(url), +                num_episodes: podcast.episodes().len(),              });          }          if let Err(err) = self.save() {              eprintln!("{}", err);          } -        download_rss(url); +        download_rss(url, config);      }      pub fn subscriptions(&self) -> Vec<Subscription> { @@ -195,13 +231,14 @@ impl Episode {                      "audio/mpeg" => Some(".mp3"),                      "audio/mp4" => Some(".m4a"),                      "audio/ogg" => Some(".ogg"), -                    _ => None, +                    _ => find_extension(self.url().unwrap()),                  }              }              None => None,          }      } +      pub fn download(&self, podcast_name: &str) -> Result<(), io::Error> {          let mut path = get_podcast_dir();          path.push(podcast_name); @@ -209,10 +246,10 @@ impl Episode {          if let Some(url) = self.url() {              if let Some(title) = self.title() { -                println!("Downloading: {}", title);                  let mut filename = String::from(title);                  filename.push_str(self.extension().unwrap());                  path.push(filename); +                println!("Downloading: {}", path.to_str().unwrap());                  let mut file = File::create(&path)?;                  let mut resp = reqwest::get(url).unwrap();                  let mut content: Vec<u8> = Vec::new(); diff --git a/src/utils.rs b/src/utils.rs index a3d7260..7d355b3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,33 @@ -use std::collections::BTreeSet; +use std::collections::HashSet;  use std::env;  use std::fs; -use std::path::PathBuf;  use std::num::ParseIntError; +use std::path::PathBuf; -pub fn already_downloaded(dir: &str) -> BTreeSet<String> { -    let mut result = BTreeSet::new(); +pub fn trim_extension(filename: &str) -> String { +    let name = String::from(filename); +    let index = name.rfind('.').unwrap(); +    String::from(&name[0..index]) +} + +pub fn find_extension(input: &str) -> Option<&str> { +    let tmp = String::from(input); +    if tmp.contains(".mp3") { +        Some(".mp3") +    } else if tmp.contains(".m4a") { +        Some(".m4a") +    } else if tmp.contains(".wav") { +        Some(".wav") +    } else if tmp.contains(".ogg") { +        Some(".ogg") +    } else { +        None +    } +} + +pub fn already_downloaded(dir: &str) -> HashSet<String> { +    let mut result = HashSet::new();      let mut path = get_podcast_dir();      path.push(dir); | 
