diff options
Diffstat (limited to 'src/lastfm')
-rw-r--r-- | src/lastfm/api_types.rs | 2 | ||||
-rw-r--r-- | src/lastfm/mod.rs | 187 |
2 files changed, 2 insertions, 187 deletions
diff --git a/src/lastfm/api_types.rs b/src/lastfm/api_types.rs index 148b17f..5a87956 100644 --- a/src/lastfm/api_types.rs +++ b/src/lastfm/api_types.rs @@ -1,6 +1,8 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] +use serde_derive::Deserialize; + #[derive(Deserialize)] pub struct track_artist { #[serde(rename = "#text")] diff --git a/src/lastfm/mod.rs b/src/lastfm/mod.rs deleted file mode 100644 index 0704e5a..0000000 --- a/src/lastfm/mod.rs +++ /dev/null @@ -1,187 +0,0 @@ -use util; - -use failure::Fail; -use std::io::{Read, Write}; - -mod api_types; - -const API_ROOT: &str = "https://ws.audioscrobbler.com/2.0/"; - -pub struct LastFMClient { - client: reqwest::Client, - api_key: String, - user: String, -} - -pub struct Track { - pub artist: String, - pub album: String, - pub name: String, - pub timestamp: i64, -} - -pub struct Tracks<'a> { - client: &'a LastFMClient, - page: Option<u64>, - buf: Vec<Track>, - from: Option<i64>, -} - -impl<'a> Tracks<'a> { - fn new(client: &LastFMClient, from: Option<i64>) -> Tracks { - Tracks { - client, - page: None, - buf: vec![], - from, - } - } - - fn get_next_page(&mut self) -> failure::Fallible<()> { - if self.page.is_none() { - self.page = Some(self.client.get_total_pages(self.from)?); - } - let page = self.page.unwrap(); - if page < 1 { - return Ok(()); - } - - let req = self.client.client.get(API_ROOT).query(&[ - ("method", "user.getrecenttracks"), - ("api_key", &self.client.api_key), - ("user", &self.client.user), - ("format", "json"), - ("page", &format!("{}", page)), - ("limit", "200"), - ]); - let req = if let Some(from) = self.from { - req.query(&[("from", &format!("{}", from))]) - } else { - req - }; - - let mut res = req.send()?; - - if res.status().is_success() { - let data: api_types::recent_tracks = res.json()?; - self.buf = data - .recenttracks - .track - .iter() - .filter(|t| t.date.is_some()) - .map(|t| { - Ok(Track { - artist: t.artist.text.clone(), - album: t.album.text.clone(), - name: t.name.clone(), - timestamp: t.date.as_ref().unwrap().uts.parse()?, - }) - }) - .collect::<failure::Fallible<Vec<Track>>>()?; - self.page = Some(page - 1); - Ok(()) - } else { - Err(failure::err_msg(res.status().as_str().to_string())) - } - } -} - -impl<'a> Iterator for Tracks<'a> { - type Item = Track; - - fn next(&mut self) -> Option<Track> { - if self.buf.is_empty() { - let result = self.get_next_page(); - if result.is_err() { - return None; - } - } - self.buf.pop() - } -} - -impl LastFMClient { - pub fn new(user: &str) -> failure::Fallible<LastFMClient> { - Ok(LastFMClient { - client: reqwest::Client::new(), - api_key: find_api_key()?, - user: user.to_string(), - }) - } - - pub fn track_count(&self, from: Option<i64>) -> failure::Fallible<u64> { - let data = self.recent_tracks(from)?; - Ok(data.recenttracks.attr.total.parse()?) - } - - pub fn tracks(&self, from: Option<i64>) -> Tracks { - Tracks::new(&self, from) - } - - fn get_total_pages(&self, from: Option<i64>) -> failure::Fallible<u64> { - let data = self.recent_tracks(from)?; - Ok(data.recenttracks.attr.totalPages.parse()?) - } - - fn recent_tracks( - &self, - from: Option<i64>, - ) -> failure::Fallible<api_types::recent_tracks> { - let req = self.client.get(API_ROOT).query(&[ - ("method", "user.getrecenttracks"), - ("api_key", &self.api_key), - ("user", &self.user), - ("format", "json"), - ("limit", "200"), - ]); - let req = if let Some(from) = from { - req.query(&[("from", &format!("{}", from))]) - } else { - req - }; - - let mut res = req.send()?; - - if res.status().is_success() { - let data: api_types::recent_tracks = res.json()?; - Ok(data) - } else { - Err(failure::err_msg(res.status().as_str().to_string())) - } - } -} - -fn find_api_key() -> failure::Fallible<String> { - let api_key_path = util::api_key_path() - .map_err(|e| e.context("failed to determine api key path"))?; - let api_key = if api_key_path.exists() { - let mut api_key = String::new(); - let mut f = std::fs::File::open(&api_key_path).map_err(|e| { - e.context(format!("failed to open {}", api_key_path.display())) - })?; - f.read_to_string(&mut api_key).map_err(|e| { - e.context(format!( - "failed to read from {}", - api_key_path.display() - )) - })?; - api_key - } else { - let api_key = rpassword::prompt_password_stderr(&format!( - "last.fm api key (will be stored in {}): ", - api_key_path.display() - ))?; - std::fs::create_dir_all(api_key_path.parent().unwrap())?; - let mut f = std::fs::File::create(&api_key_path).map_err(|e| { - e.context(format!("failed to open {}", api_key_path.display())) - })?; - f.write_all(api_key.as_bytes()).map_err(|e| { - e.context(format!( - "failed to write to {}", - api_key_path.display() - )) - })?; - api_key - }; - Ok(api_key.trim_end().to_string()) -} |