Add ListenBrainz type
The ListenBrainz struct is a more ergonomic client than raw::Client. It supports authentication using a ListenBrainz token. Currently, it allows submitting "listened" and "now playing" requests to the API in a more convenient way than manually creating all request data.
This commit is contained in:
parent
02060d8476
commit
7f07ecfb32
|
@ -26,6 +26,10 @@ pub enum Error {
|
|||
/// There was some other HTTP error while interacting with the API.
|
||||
#[error("HTTP error")]
|
||||
Http(#[source] Box<ureq::Error>),
|
||||
|
||||
/// Tried to access a service that requires authentication.
|
||||
#[error("not authenticated")]
|
||||
NotAuthenticated,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
@ -3,5 +3,7 @@
|
|||
mod endpoint;
|
||||
mod error;
|
||||
pub mod raw;
|
||||
mod wrapper;
|
||||
|
||||
pub use crate::error::Error;
|
||||
pub use crate::wrapper::ListenBrainz;
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
use std::convert::TryInto;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::raw::Client;
|
||||
use crate::raw::request::{ListenType, Payload, SubmitListens, TrackMetadata};
|
||||
|
||||
/// Contains a ListenBrainz token and the associated username
|
||||
/// for authentication purposes.
|
||||
struct Auth {
|
||||
token: String,
|
||||
user: String,
|
||||
}
|
||||
|
||||
/// An ergonomic ListenBrainz client.
|
||||
///
|
||||
/// As opposed to [`Client`](crate::raw::Client), this aims to be a convenient and high-level
|
||||
/// wrapper of the ListenBrainz API.
|
||||
pub struct ListenBrainz {
|
||||
client: Client,
|
||||
auth: Option<Auth>,
|
||||
}
|
||||
|
||||
impl ListenBrainz {
|
||||
/// Construct a new ListenBrainz client.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
auth: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this client is authenticated.
|
||||
pub fn is_authenticated(&self) -> bool {
|
||||
self.auth.is_some()
|
||||
}
|
||||
|
||||
/// Return the token if authenticated or [`None`] if not.
|
||||
pub fn authenticated_token(&self) -> Option<&str> {
|
||||
self.auth.as_ref().map(|auth| auth.token.as_str())
|
||||
}
|
||||
|
||||
/// Return the user if authenticated or [`None`] if not.
|
||||
pub fn authenticated_user(&self) -> Option<&str> {
|
||||
self.auth.as_ref().map(|auth| auth.user.as_str())
|
||||
}
|
||||
|
||||
/// Authenticate this client with the given token.
|
||||
/// If the token is valid, authenticates the client and returns true, otherwise returns false.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If there was an error while validating the token, that error is returned.
|
||||
/// See the Errors section of [`Client`] for more info on what errors might occur.
|
||||
pub fn authenticate(&mut self, token: &str) -> Result<bool, Error> {
|
||||
let result = self.client.validate_token(token)?;
|
||||
if result.valid && result.user_name.is_some() {
|
||||
self.auth.replace(Auth {
|
||||
token: token.to_string(),
|
||||
user: result.user_name.unwrap(),
|
||||
});
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Helper method to submit a listen (either "single" or "playing now").
|
||||
fn submit_listen(
|
||||
&self,
|
||||
listen_type: ListenType,
|
||||
timestamp: Option<i64>,
|
||||
artist: &str,
|
||||
track: &str,
|
||||
release: &str,
|
||||
) -> Result<(), Error> {
|
||||
if !self.is_authenticated() {
|
||||
return Err(Error::NotAuthenticated);
|
||||
}
|
||||
|
||||
let payload = Payload {
|
||||
listened_at: timestamp,
|
||||
track_metadata: TrackMetadata {
|
||||
artist_name: artist,
|
||||
track_name: track,
|
||||
release_name: Some(release),
|
||||
additional_info: None,
|
||||
},
|
||||
};
|
||||
|
||||
let token = self.authenticated_token().unwrap();
|
||||
self.client.submit_listens(
|
||||
token,
|
||||
SubmitListens {
|
||||
listen_type,
|
||||
payload: &[payload],
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Submit a listened track with the current time as the listen time.
|
||||
/// This requires authentication.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If not authenticated, returns [`Error::NotAuthenticated`].
|
||||
/// Otherwise, see the Errors section of [`Client`] for more info on
|
||||
/// what errors might occur.
|
||||
pub fn listen(&self, artist: &str, track: &str, release: &str) -> Result<(), Error> {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
self.submit_listen(ListenType::Single, Some(now), artist, track, release)
|
||||
}
|
||||
|
||||
/// Submit a currently playing track.
|
||||
pub fn playing_now(&self, artist: &str, track: &str, release: &str) -> Result<(), Error> {
|
||||
self.submit_listen(ListenType::PlayingNow, None, artist, track, release)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue