Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Popularity Endpoints #34

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/raw/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,17 @@ impl Client {

ResponseType::from_response(response)
}

// ---------- Popularity Endpoints
/// Endpoint: [`popularity/top-recordings-for-artist`](https://listenbrainz.readthedocs.io/en/latest/users/api/popularity.html#get--1-popularity-top-recordings-for-artist-(artist_mbid))
pub fn popularity_top_recordings_for_artist(&self, artist_mbid: &str) -> Result<ListenbrainzResponse<PopularityTopRecordingsForArtistResponse>, Error>{
let endpoint = format!("{}{}", self.api_root_url, Endpoint::PopularityTopReleaseGroupsForArtist(artist_mbid));

let mut request = attohttpc::get(endpoint);
let response = request.send()?;

ResponseType::from_response(response)
}
}

impl Default for Client {
Expand Down
16 changes: 16 additions & 0 deletions src/raw/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ pub enum Endpoint<'a> {
StatsUserArtists(&'a str),
StatsReleaseGroupListeners(&'a str),
StatusGetDumpInfo,

// Popularity endpoints
PopularityTopRecordingsForArtist(&'a str),
PopularityTopReleaseGroupsForArtist(&'a str),
PopularityRecording,
PopularityArtist,
PopularityRelease,
PopularityReleaseGroup,
}

impl<'a> fmt::Display for Endpoint<'a> {
Expand All @@ -42,6 +50,14 @@ impl<'a> fmt::Display for Endpoint<'a> {
Self::StatsUserArtists(user) => return write!(f, "stats/user/{user}/artists"),
Self::StatsReleaseGroupListeners(release_group_mbid) => return write!(f, "stats/release-group/{release_group_mbid}/listeners"),
Self::StatusGetDumpInfo => "status/get-dump-info",

// Popularity endpoints
Self::PopularityTopRecordingsForArtist(artist) => return write!(f, "popularity/top-recordings-for-artist/{artist}"),
Self::PopularityTopReleaseGroupsForArtist(artist) => return write!(f, "popularity/top-release-groups-for-artist/{artist}"),
Self::PopularityRecording => "popularity/recording",
Self::PopularityArtist => "popularity/artist",
Self::PopularityRelease => "popularity/release",
Self::PopularityReleaseGroup => "popularity/release-group",
};
write!(f, "{s}")
}
Expand Down
18 changes: 17 additions & 1 deletion src/raw/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::borrow::Borrow;

use serde::Serialize;
use serde::{Deserialize, Serialize};

// --------- submit-listens

Expand Down Expand Up @@ -91,3 +91,19 @@ impl Borrow<str> for Empty {
unreachable!("Should never be used as a value")
}
}


// ---------- Popularity Endpoints Requests

// ---------- popularity/recording

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PopularityRecordingRequest {
recording_mbids: Vec<String>
}

impl From<Vec<String>> for PopularityRecordingRequest {
fn from(value: Vec<String>) -> Self {
Self { recording_mbids: value }
}
}
110 changes: 108 additions & 2 deletions src/raw/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
#![allow(missing_docs)]

use std::collections::HashMap;
use std::ops::Deref;

use attohttpc::Response;
use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

use crate::Error;

Expand All @@ -21,7 +22,7 @@ use crate::Error;
/// as the former is resilient against clients with incorrect clocks.
///
/// [API docs]: https://listenbrainz.readthedocs.io/en/production/dev/api/#rate-limiting
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct RateLimit {
pub limit: u64,
pub remaining: u64,
Expand Down Expand Up @@ -107,6 +108,35 @@ macro_rules! response_type {
}
}

/// A generic response type for listenbrainz responses
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct ListenbrainzResponse<T> {
pub rate_limit: Option<RateLimit>,
pub data: T
}

impl<T> ResponseType for ListenbrainzResponse<T> where T: DeserializeOwned {
fn from_response(response: Response) -> Result<Self, Error> {
let response = Error::try_from_error_response(response)?;

let rate_limit = RateLimit::from_headers(&response);
let mut inner_data: T = response.json()?;

Ok(Self {
rate_limit,
data: inner_data,
})
}
}

impl<T> Deref for ListenbrainzResponse<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.data
}
}

// --------- submit-listens

response_type! {
Expand Down Expand Up @@ -544,3 +574,79 @@ pub struct StatsReleaseGroupListenersListeners {
pub username_name: String,
}

// ---------- Popularity Endpoints Responses

// ---------- popularity/top-recordings-for-artist/(artist_mbid)
pub type PopularityTopRecordingsForArtistResponse = Vec<PopularityTopRecordingsForArtistResponseItem>;

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PopularityTopRecordingsForArtistResponseItem {
pub artist_mbids: Vec<String>,
pub artist_name: String,
pub caa_id: Option<i64>,
pub caa_release_mbid: Option<String>,
pub length: u64,
pub recording_mbid: String,
pub recording_name: String,
pub release_color: ReleaseColor,
pub release_mbid: String,
pub total_listen_count: u64,
pub total_user_count: u64
}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct ReleaseColor {
blue: u8,
green: u8,
red: u8
}

// ---------- popularity/top-release-groups-for-artist/(artist_mbid)

pub type PopularityTopReleaseGroupsForArtistResponse = Vec<PopularityTopRecordingsForArtistResponseItem>;

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PopularityTopReleaseGroupsForArtistResponseItem {
pub artist: PopularityTopReleaseGroupsForArtistResponseItemArtist,

}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PopularityTopReleaseGroupsForArtistResponseItemArtist {
artist_credit_id: u64,
//artists: Vec<???>,
name: String
}

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PopularityTopReleaseGroupsForArtistResponseItemRelease {
caa_id: i64,
caa_release_mbid: String,
date: String, //TODO: Serialize into naive date
name: String,
//rels: Vec<???>,
//type: String
}

// ---------- popularity/recording

pub type PopularityRecordingResponse = Vec<PopularityTopRecordingsForArtistResponseItem>;

#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct PopularityRecordingResponseItem {
pub recording_mbid: String,
pub total_listen_count: u64,
pub total_user_count: u64
}

// ---------- popularity/artist

// TODO

// ---------- popularity/release

// TODO

// ---------- popularity/release-grou

// TODO
Loading