Skip to content

Commit 78b955d

Browse files
committed
feat: added art/grid API endpoint
1 parent d7b1caf commit 78b955d

File tree

5 files changed

+160
-4
lines changed

5 files changed

+160
-4
lines changed

src/raw/client.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,54 @@ impl Client {
533533

534534
ResponseType::from_response(response)
535535
}
536+
537+
/// Endpoint: [`art/grid/`](https://listenbrainz.readthedocs.io/en/latest/users/api/art.html#post--1-art-grid-)
538+
/// ```no_run
539+
/// use listenbrainz::raw::Client;
540+
/// use listenbrainz::raw::request::ArtGrid;
541+
/// use listenbrainz::raw::request::ArtGridBackground;
542+
///
543+
/// let grid = ArtGrid {
544+
/// background: ArtGridBackground::Transparent,
545+
/// image_size: 750,
546+
/// dimension: 4,
547+
/// skip_missing: false,
548+
/// show_caa: true,
549+
/// tiles: Some(vec![
550+
/// "0,1,4,5".to_string(),
551+
/// "10,11,14,15".to_string(),
552+
/// "2".to_string(),
553+
/// "3".to_string(),
554+
/// "6".to_string(),
555+
/// "7".to_string(),
556+
/// "8".to_string(),
557+
/// "9".to_string(),
558+
/// "12".to_string(),
559+
/// "13".to_string()
560+
/// ]),
561+
/// release_mbids: Some(vec![
562+
/// "d101e395-0c04-4237-a3d2-167b1d88056c".to_string(),
563+
/// "4211382c-39e8-4a72-a32d-e4046fd96356".to_string(),
564+
/// "6d895dfa-8688-4867-9730-2b98050dae04".to_string(),
565+
/// "773e54bb-3f43-4813-826c-ca762bfa8318".to_string(),
566+
/// "ec782dbe-9204-4ec3-bf50-576c7cf3dfb3".to_string(),
567+
/// "10dffffc-c2aa-4ddd-81fd-42b5e125f240".to_string(),
568+
/// "be5f714d-02eb-4c89-9a06-5e544f132604".to_string(),
569+
/// "3eee4ed1-b48e-4894-8a05-f535f16a4985".to_string()
570+
/// ]),
571+
/// release_group_mbids: None,
572+
/// cover_art_size: None
573+
/// };
574+
///
575+
/// println!("{:#?}", Client::new().art_grid(grid).unwrap());
576+
/// ```
577+
pub fn art_grid(&self, body: ArtGrid) -> Result<ArtGridResponse, Error> {
578+
let endpoint = format!("{}{}", self.api_root_url, Endpoint::ArtGrid);
579+
580+
let response = attohttpc::post(endpoint).json(&body)?.send()?;
581+
582+
ResponseType::from_response(response)
583+
}
536584
}
537585

538586
impl Default for Client {

src/raw/endpoint.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub enum Endpoint<'a> {
3030
UserFollowing(&'a str),
3131
UserUnfollow(&'a str),
3232
UserFollow(&'a str),
33+
ArtGrid,
3334
}
3435

3536
impl<'a> fmt::Display for Endpoint<'a> {
@@ -74,6 +75,7 @@ impl<'a> fmt::Display for Endpoint<'a> {
7475
Self::UserFollowing(user) => write!(f, "user/{}/following", user),
7576
Self::UserUnfollow(user) => write!(f, "user/{}/unfollow", user),
7677
Self::UserFollow(user) => write!(f, "user/{}/follow", user),
78+
Self::ArtGrid => write!(f, "art/grid"),
7779
}
7880
}
7981
}

src/raw/request.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,87 @@ impl Borrow<str> for Empty {
148148
unreachable!("Should never be used as a value")
149149
}
150150
}
151+
152+
// --- Art Requests ---
153+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
154+
pub struct ArtGrid {
155+
/// The background for the cover art
156+
pub background: ArtGridBackground,
157+
158+
/// The size of the cover art image
159+
pub image_size: u64,
160+
161+
/// The dimension to use for this grid. A grid of dimension 3 has 3 images across and 3 images down, for a total of 9 images.
162+
pub dimension: u64,
163+
164+
/// If cover art is missing for a given release_mbid, skip it and move on to the next one, if true is passed.
165+
/// If false, the show-caa option will decide what happens.
166+
#[serde(rename = "skip-missing")]
167+
pub skip_missing: bool,
168+
169+
/// If cover art is missing and skip-missing is false,
170+
/// then show-caa will determine if a blank square is shown or if the Cover Art Archive missing image is shown.
171+
#[serde(rename = "show-caa")]
172+
pub show_caa: bool,
173+
174+
/// The tiles parameter is a list of strings that determines the location where cover art images should be placed.
175+
/// Each string is a comma separated list of image cells.
176+
/// A grid of dimension 3 has 9 cells, from 0 in the upper left hand corner, 2 in the upper right hand corner,
177+
/// 6 in the lower left corner and 8 in the lower right corner.
178+
///
179+
/// ```plaintext
180+
/// 0 1 2
181+
/// 3 4 5
182+
/// 6 7 8
183+
/// ```
184+
///
185+
/// Specifying only a single cell will have the image cover that cell exactly.
186+
/// If more than one cell is specified, the image will cover the area defined by the bounding box of all the given cells.
187+
/// These tiles only define bounding box areas – no clipping of images that may fall outside of these tiles will be performed.
188+
///
189+
/// ## Exemple
190+
/// For this exemple, the dimension is set to 3
191+
/// ```
192+
/// let tiles = vec![
193+
/// "0, 1, 3, 4".to_string(), // The first image cover the slots 0, 1, 3, and 4
194+
/// "2".to_string(),
195+
/// "5".to_string(),
196+
/// "6".to_string(),
197+
/// "7".to_string(),
198+
/// "8".to_string(),
199+
/// ];
200+
/// ```
201+
///
202+
/// This will result in this arrangement:
203+
///
204+
/// ```plaintext
205+
/// 1 1 2
206+
/// 1 1 3
207+
/// 4 5 6
208+
/// ```
209+
pub tiles: Option<Vec<String>>,
210+
211+
/// An ordered list of release_mbids. The images will be loaded and processed in the order that this list is in.
212+
/// The cover art for the release_mbids will be placed on the tiles defined by the tiles parameter.
213+
/// If release_group_mbids are supplied as well, ONLY cover arts for release_group_mbids will be processed.
214+
pub release_mbids: Option<Vec<String>>,
215+
216+
/// An ordered list of release_group_mbids. The images will be loaded and processed in the order that this list is in.
217+
/// The cover art for the release_group_mbids will be placed on the tiles defined by the tiles parameter.
218+
/// If release_mbids are supplied as well, ONLY cover arts for release_mbids will be processed.
219+
pub release_group_mbids: Option<Vec<String>>,
220+
221+
/// Size in pixels of each cover art in the composited image. Can be either 250 or 500
222+
pub cover_art_size: Option<u64>,
223+
}
224+
225+
/// The background for the cover art
226+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
227+
pub enum ArtGridBackground {
228+
#[serde(rename = "transparent")]
229+
Transparent,
230+
#[serde(rename = "white")]
231+
White,
232+
#[serde(rename = "black")]
233+
Black,
234+
}

src/raw/response/art/mod.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1-
//TODO: https://listenbrainz.readthedocs.io/en/latest/users/api/art.html
2-
// Currently no api responses have been made for this page.
3-
// The header comments will be made as needed
1+
use serde::Deserialize;
2+
use serde::Serialize;
3+
4+
// --------- POST /1/art/grid/
5+
// https://listenbrainz.readthedocs.io/en/latest/users/api/art.html#post--1-art-grid-
6+
7+
/// Response type for [`Client::user_similar_users`](super::Client::user_similar_users).
8+
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
9+
pub struct ArtGridResponse {
10+
pub rate_limit: Option<crate::raw::response::RateLimit>,
11+
pub image: String,
12+
}
13+
14+
// This response is special as it return a SVG directly. So we implement `ResponseType` manually
15+
impl crate::raw::response::ResponseType for ArtGridResponse {
16+
fn from_response(
17+
response: crate::raw::response::Response,
18+
) -> Result<Self, crate::raw::response::Error> {
19+
let response = crate::raw::response::Error::try_from_error_response(response)?;
20+
let rate_limit = crate::raw::response::RateLimit::from_headers(&response);
21+
let image = response.text()?;
22+
Ok(Self { image, rate_limit })
23+
}
24+
}

src/raw/response/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use attohttpc::Response;
1010
use serde::de::DeserializeOwned;
1111
use serde::Deserialize;
12+
use serde::Serialize;
1213

1314
use crate::Error;
1415

@@ -45,7 +46,7 @@ pub use crate::raw::response::statistics::*;
4546
/// as the former is resilient against clients with incorrect clocks.
4647
///
4748
/// [API docs]: https://listenbrainz.readthedocs.io/en/production/dev/api/#rate-limiting
48-
#[derive(Debug, Clone, PartialEq, Eq)]
49+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
4950
pub struct RateLimit {
5051
pub limit: u64,
5152
pub remaining: u64,

0 commit comments

Comments
 (0)