Decide carefully where external::musicbrainz belongs #196

Merged
wojtek merged 11 commits from 193---decide-carefully-where-external--musicbrainz-belongs into main 2024-08-28 18:21:13 +02:00
2 changed files with 86 additions and 30 deletions
Showing only changes of commit 76173e0468 - Show all commits

View File

@ -15,10 +15,7 @@ use url::form_urlencoded;
use crate::{
collection::album::AlbumDate,
core::{
collection::{
album::{Album, AlbumPrimaryType, AlbumSecondaryType},
musicbrainz::IMusicBrainzRef,
},
collection::album::{AlbumPrimaryType, AlbumSecondaryType},
interface::musicbrainz::Mbid,
},
interface::musicbrainz::MbidError,
@ -81,44 +78,91 @@ impl<Http> MusicBrainzClient<Http> {
}
impl<Http: IMusicBrainzHttp> MusicBrainzClient<Http> {
fn display_album_date(date: &AlbumDate) -> Option<String> {
match date.year {
Some(year) => match date.month {
Some(month) => match date.day {
Some(day) => Some(format!("{year}-{month:02}-{day:02}")),
None => Some(format!("{year}-{month:02}")),
},
None => Some(format!("{year}")),
},
None => None,
}
}
pub fn search_release_group(
&mut self,
arid: &Mbid,
album: &Album,
) -> Result<ResponseSearchReleaseGroup, Error> {
let title = &album.id.title;
let arid = arid.uuid().as_hyphenated().to_string();
let mut query = format!("arid:{arid}");
request: SearchReleaseGroupRequest,
) -> Result<SearchReleaseGroupResponse, Error> {
let mut query: Vec<String> = vec![];
match album.musicbrainz {
Some(ref mbref) => {
let rgid = mbref.mbid().uuid().as_hyphenated().to_string();
query.push_str(&format!(" AND rgid:{rgid}"));
}
None => {
query.push_str(&format!(" AND releasegroup:\"{title}\""));
if let Some(year) = album.date.year {
query.push_str(&format!(" AND firstreleasedate:{year}"));
}
if let Some(arid) = request.arid {
query.push(format!("arid:{}", arid.uuid().as_hyphenated().to_string()));
}
if let Some(date) = request.first_release_date {
if let Some(date_string) = Self::display_album_date(date) {
query.push(format!("firstreleasedate:{date_string}"))
}
}
let query: String = form_urlencoded::byte_serialize(query.as_bytes()).collect();
if let Some(release_group) = request.release_group {
query.push(format!("releasegroup:\"{release_group}\""));
}
if let Some(rgid) = request.rgid {
query.push(format!("rgid:{}", rgid.uuid().as_hyphenated().to_string()));
}
let query: String =
form_urlencoded::byte_serialize(query.join(" AND ").as_bytes()).collect();
let url = format!("{MB_BASE_URL}/release-group?query={query}");
Ok(self.http.get(&url)?)
}
}
#[derive(Default)]
pub struct SearchReleaseGroupRequest<'a> {
arid: Option<&'a Mbid>,
first_release_date: Option<&'a AlbumDate>,
release_group: Option<&'a str>,
rgid: Option<&'a Mbid>,
}
impl<'a> SearchReleaseGroupRequest<'a> {
pub fn arid(&mut self, arid: &'a Mbid) -> &mut Self {
self.arid = Some(arid);
self
}
pub fn first_release_date(&mut self, first_release_date: &'a AlbumDate) -> &mut Self {
self.first_release_date = Some(first_release_date);
self
}
pub fn release_group(&mut self, release_group: &'a str) -> &mut Self {
self.release_group = Some(release_group);
self
}
pub fn rgid(&mut self, rgid: &'a Mbid) -> &mut Self {
self.rgid = Some(rgid);
self
}
}
// TODO: Separate deserialize types from internal types like in JSON code.
#[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))]
pub struct ResponseSearchReleaseGroup {
pub release_groups: Vec<SearchReleaseGroup>,
pub struct SearchReleaseGroupResponse {
pub release_groups: Vec<SearchReleaseGroupResponseUnit>,
}
#[derive(Deserialize)]
#[serde(rename_all(deserialize = "kebab-case"))]
pub struct SearchReleaseGroup {
pub struct SearchReleaseGroupResponseUnit {
pub score: u8,
pub id: Mbid,
pub title: String,
@ -229,7 +273,7 @@ impl<'de> Visitor<'de> for AlbumSecondaryTypeVisitor {
{
let variant = match v {
"Compilation" => AlbumSecondaryType::Compilation,
"Soundtrack" => AlbumSecondaryType::Soundtrack,
"Soundtrack" => AlbumSecondaryType::Soundtrack,
"Spokenword" => AlbumSecondaryType::Spokenword,
"Interview" => AlbumSecondaryType::Interview,
"Audiobook" => AlbumSecondaryType::Audiobook,

View File

@ -1,8 +1,11 @@
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
use musichoard::{
collection::album::Album,
external::musicbrainz::{IMusicBrainzHttp, MusicBrainzClient, SearchReleaseGroup},
collection::{album::Album, musicbrainz::IMusicBrainzRef},
external::musicbrainz::{
IMusicBrainzHttp, MusicBrainzClient, SearchReleaseGroupRequest,
SearchReleaseGroupResponseUnit,
},
interface::musicbrainz::Mbid,
};
@ -24,16 +27,25 @@ impl<Http: IMusicBrainzHttp> IMusicBrainz for MusicBrainz<Http> {
arid: &Mbid,
album: &Album,
) -> Result<Vec<Match<Album>>, Error> {
let mb_response = self.client.search_release_group(arid, album)?;
let mut request = SearchReleaseGroupRequest::default();
request
.arid(arid)
.release_group(&album.id.title)
.first_release_date(&album.date);
if let Some(ref mbref) = album.musicbrainz {
request.rgid(mbref.mbid());
}
let mb_response = self.client.search_release_group(request)?;
Ok(mb_response
.release_groups
.into_iter()
.map(from_search_release_group)
.map(from_search_release_group_response_unit)
.collect())
}
}
fn from_search_release_group(entity: SearchReleaseGroup) -> Match<Album> {
fn from_search_release_group_response_unit(entity: SearchReleaseGroupResponseUnit) -> Match<Album> {
let mut album = Album::new(
entity.title,
entity.first_release_date,