summaryrefslogtreecommitdiffstats
path: root/src/lastfm
diff options
context:
space:
mode:
Diffstat (limited to 'src/lastfm')
-rw-r--r--src/lastfm/api_types.rs2
-rw-r--r--src/lastfm/mod.rs187
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())
-}