//! API bindings for ListenBrainz. use serde::de::DeserializeOwned; use serde::Serialize; use ureq::Agent; mod endpoint; mod error; pub mod models; use endpoint::Endpoint; pub use error::Error; use models::request::*; use models::response::*; const API_ROOT_URL: &str = "https://api.listenbrainz.org/1/"; /// Low-level client that more-or-less directly wraps the ListenBrainz HTTP API. /// /// Client exposes functions that map one-to-one to the API methods described /// in the [ListenBrainz API docs](https://listenbrainz.readthedocs.io/en/production/dev/api/). pub struct Client { agent: Agent, } impl Client { /// Construct a new client. pub fn new() -> Self { Self { agent: ureq::agent(), } } fn get(&mut self, endpoint: Endpoint) -> Result { let endpoint = format!("{}{}", API_ROOT_URL, endpoint); self.agent .get(&endpoint) .call()? .into_json() .map_err(Error::ResponseJson) } fn post(&mut self, endpoint: Endpoint, token: &str, data: D) -> Result where D: Serialize, R: DeserializeOwned, { let data = serde_json::to_value(data).map_err(Error::RequestJson)?; let endpoint = format!("{}{}", API_ROOT_URL, endpoint); self.agent .post(&endpoint) .set("Authorization", &format!("Token {}", token)) .send_json(data)? .into_json() .map_err(Error::ResponseJson) } /// Endpoint: `submit-listens` pub fn submit_listens( &mut self, token: &str, data: SubmitListens, ) -> Result { self.post(Endpoint::SubmitListens, token, data) } /// Endpoint: `validate-token` pub fn validate_token(&mut self, token: &str) -> Result { let endpoint = format!("{}{}", API_ROOT_URL, Endpoint::ValidateToken); self.agent .get(&endpoint) .query("token", token) .call()? .into_json() .map_err(Error::ResponseJson) } /// Endpoint: `delete-listen` pub fn delete_listen( &mut self, token: &str, data: DeleteListen, ) -> Result { self.post(Endpoint::DeleteListen, token, data) } /// Endpoint: `users/{user_list}/recent-listens pub fn users_recent_listens( &mut self, user_list: &[&str], ) -> Result { self.get(Endpoint::UsersRecentListens(user_list)) } /// Endpoint: `user/{user_name}/listen-count` pub fn user_listen_count(&mut self, user_name: &str) -> Result { self.get(Endpoint::UserListenCount(user_name)) } /// Endpoint: `user/{user_name}/playing-now` pub fn user_playing_now(&mut self, user_name: &str) -> Result { self.get(Endpoint::UserPlayingNow(user_name)) } pub fn user_listens( &mut self, user_name: &str, min_ts: Option, max_ts: Option, count: Option, time_range: Option ) -> Result { let endpoint = format!("{}{}", API_ROOT_URL, Endpoint::UserListens(user_name)); let mut request = self.agent.get(&endpoint); if let Some(min_ts) = min_ts { request = request.query("min_ts", &min_ts.to_string()); } if let Some(max_ts) = max_ts { request = request.query("max_ts", &max_ts.to_string()); } if let Some(count) = count { request = request.query("count", &count.to_string()); } if let Some(time_range) = time_range { request = request.query("time_range", &time_range.to_string()); } request.call()?.into_json().map_err(Error::ResponseJson) } /// Endpoint: `latest-import` (GET) pub fn get_latest_import(&mut self, user_name: &str) -> Result { let endpoint = format!("{}{}", API_ROOT_URL, Endpoint::LatestImport); self.agent .get(&endpoint) .query("user_name", user_name) .call()? .into_json() .map_err(Error::ResponseJson) } /// Endpoint: `latest-import` (POST) pub fn update_latest_import( &mut self, token: &str, data: UpdateLatestImport, ) -> Result { self.post(Endpoint::LatestImport, token, data) } /// Endpoint: `stats/sitewide/artists` pub fn stats_sitewide_artists( &mut self, count: Option, offset: Option, range: Option<&str> ) -> Result { let endpoint = format!("{}{}", API_ROOT_URL, Endpoint::StatsSitewideArtists); let mut request = self.agent.get(&endpoint); if let Some(count) = count { request = request.query("count", &count.to_string()); } if let Some(offset) = offset { request = request.query("offset", &offset.to_string()); } if let Some(range) = range { request = request.query("range", range); } request.call()?.into_json().map_err(Error::ResponseJson) } // /// Endpoint: `stats/user/{user_name}/listening-activity` // /// Endpoint: `stats/user/{user_name}/daily-activity` // /// Endpoint: `stats/user/{user_name}/recordings` // /// Endpoint: `stats/user/{user_name}/artist-map` // /// Endpoint: `stats/user/{user_name}/releases` // /// Endpoint: `stats/user/{user_name}/artists` /// Endpoint: `status/get-dump-info` pub fn status_get_dump_info( &mut self, id: Option, ) -> Result { let endpoint = format!("{}{}", API_ROOT_URL, Endpoint::StatusGetDumpInfo); let mut request = self.agent.get(&endpoint); if let Some(id) = id { request = request.query("id", &id.to_string()); } request.call()?.into_json().map_err(Error::ResponseJson) } }