Make fetch also fetch artist MBID if it is missing #201
2
src/external/musicbrainz/api/mod.rs
vendored
2
src/external/musicbrainz/api/mod.rs
vendored
@ -70,7 +70,7 @@ impl ApiDisplay {
|
|||||||
},
|
},
|
||||||
None => format!("{year}"),
|
None => format!("{year}"),
|
||||||
},
|
},
|
||||||
None => format!("*"),
|
None => String::from("*"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
src/external/musicbrainz/api/search/artist.rs
vendored
71
src/external/musicbrainz/api/search/artist.rs
vendored
@ -2,7 +2,10 @@ use std::fmt;
|
|||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{collection::{artist::ArtistId, musicbrainz::Mbid}, external::musicbrainz::api::SerdeMbid};
|
use crate::{
|
||||||
|
collection::{artist::ArtistId, musicbrainz::Mbid},
|
||||||
|
external::musicbrainz::api::SerdeMbid,
|
||||||
|
};
|
||||||
|
|
||||||
use super::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin};
|
use super::query::{impl_term, EmptyQuery, EmptyQueryJoin, Query, QueryJoin};
|
||||||
|
|
||||||
@ -74,3 +77,69 @@ impl From<DeserializeSearchArtistResponseArtist> for SearchArtistResponseArtist
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use mockall::predicate;
|
||||||
|
|
||||||
|
use crate::external::musicbrainz::{api::MusicBrainzClient, MockIMusicBrainzHttp};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn de_response() -> DeserializeSearchArtistResponse {
|
||||||
|
let de_artist = DeserializeSearchArtistResponseArtist {
|
||||||
|
score: 67,
|
||||||
|
id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()),
|
||||||
|
name: String::from("an artist"),
|
||||||
|
sort_name: String::from("artist, an"),
|
||||||
|
disambiguation: None,
|
||||||
|
};
|
||||||
|
DeserializeSearchArtistResponse {
|
||||||
|
artists: vec![de_artist.clone()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn response(de_response: DeserializeSearchArtistResponse) -> SearchArtistResponse {
|
||||||
|
SearchArtistResponse {
|
||||||
|
artists: de_response
|
||||||
|
.artists
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| SearchArtistResponseArtist {
|
||||||
|
score: 67,
|
||||||
|
id: a.id.0,
|
||||||
|
name: a.name.clone().into(),
|
||||||
|
sort: Some(a.sort_name).filter(|sn| sn != &a.name).map(Into::into),
|
||||||
|
disambiguation: a.disambiguation,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_no_field() {
|
||||||
|
let mut http = MockIMusicBrainzHttp::new();
|
||||||
|
let url = format!(
|
||||||
|
"https://musicbrainz.org/ws/2\
|
||||||
|
/artist\
|
||||||
|
?query=%22{no_field}%22",
|
||||||
|
no_field = "an+artist",
|
||||||
|
);
|
||||||
|
|
||||||
|
let de_response = de_response();
|
||||||
|
let response = response(de_response.clone());
|
||||||
|
|
||||||
|
http.expect_get()
|
||||||
|
.times(1)
|
||||||
|
.with(predicate::eq(url))
|
||||||
|
.return_once(|_| Ok(de_response));
|
||||||
|
|
||||||
|
let mut client = MusicBrainzClient::new(http);
|
||||||
|
|
||||||
|
let name = "an artist";
|
||||||
|
|
||||||
|
let query = SearchArtistRequest::new().no_field(name);
|
||||||
|
|
||||||
|
let matches = client.search_artist(query).unwrap();
|
||||||
|
assert_eq!(matches, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
51
src/external/musicbrainz/api/search/query.rs
vendored
51
src/external/musicbrainz/api/search/query.rs
vendored
@ -141,6 +141,7 @@ impl<Entity: fmt::Display> fmt::Display for Query<Entity> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Entity> Query<Entity> {
|
impl<Entity> Query<Entity> {
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
pub fn new() -> EmptyQuery<Entity> {
|
pub fn new() -> EmptyQuery<Entity> {
|
||||||
EmptyQuery::default()
|
EmptyQuery::default()
|
||||||
}
|
}
|
||||||
@ -219,30 +220,54 @@ pub(crate) use impl_term;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::external::musicbrainz::api::search::SearchReleaseGroupRequest;
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub enum TestEntity<'a> {
|
||||||
|
NoField(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Display for TestEntity<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NoField(s) => write!(f, "\"{s}\""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestEntityRequest<'a> = Query<TestEntity<'a>>;
|
||||||
|
|
||||||
|
impl_term!(no_field, TestEntity<'a>, NoField, &'a str);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lucene_logical() {
|
fn lucene_logical() {
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = TestEntityRequest::new()
|
||||||
.no_field("jakarta apache")
|
.no_field("jakarta apache")
|
||||||
.or()
|
.or()
|
||||||
.no_field("jakarta");
|
.no_field("jakarta");
|
||||||
assert_eq!(format!("{query}"), "\"jakarta apache\" OR \"jakarta\"");
|
assert_eq!(format!("{query}"), "\"jakarta apache\" OR \"jakarta\"");
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = TestEntityRequest::new()
|
||||||
.no_field("jakarta apache")
|
.no_field("jakarta apache")
|
||||||
.and()
|
.and()
|
||||||
.no_field("jakarta");
|
.no_field("jakarta");
|
||||||
assert_eq!(format!("{query}"), "\"jakarta apache\" AND \"jakarta\"");
|
assert_eq!(format!("{query}"), "\"jakarta apache\" AND \"jakarta\"");
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = TestEntityRequest::new()
|
||||||
.require()
|
.require()
|
||||||
.no_field("jakarta")
|
.no_field("jakarta")
|
||||||
.or()
|
.or()
|
||||||
.no_field("lucene");
|
.no_field("lucene");
|
||||||
assert_eq!(format!("{query}"), "+\"jakarta\" OR \"lucene\"");
|
assert_eq!(format!("{query}"), "+\"jakarta\" OR \"lucene\"");
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = TestEntityRequest::new()
|
||||||
|
.no_field("lucene")
|
||||||
|
.require()
|
||||||
|
.no_field("jakarta");
|
||||||
|
assert_eq!(format!("{query}"), "\"lucene\" +\"jakarta\"");
|
||||||
|
|
||||||
|
let query = TestEntityRequest::new()
|
||||||
.no_field("jakarta apache")
|
.no_field("jakarta apache")
|
||||||
.not()
|
.not()
|
||||||
.no_field("Apache Lucene");
|
.no_field("Apache Lucene");
|
||||||
@ -251,7 +276,17 @@ mod tests {
|
|||||||
"\"jakarta apache\" NOT \"Apache Lucene\""
|
"\"jakarta apache\" NOT \"Apache Lucene\""
|
||||||
);
|
);
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = TestEntityRequest::new()
|
||||||
|
.prohibit()
|
||||||
|
.no_field("Apache Lucene")
|
||||||
|
.or()
|
||||||
|
.no_field("jakarta apache");
|
||||||
|
assert_eq!(
|
||||||
|
format!("{query}"),
|
||||||
|
"-\"Apache Lucene\" OR \"jakarta apache\""
|
||||||
|
);
|
||||||
|
|
||||||
|
let query = TestEntityRequest::new()
|
||||||
.no_field("jakarta apache")
|
.no_field("jakarta apache")
|
||||||
.prohibit()
|
.prohibit()
|
||||||
.no_field("Apache Lucene");
|
.no_field("Apache Lucene");
|
||||||
@ -260,9 +295,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lucene_grouping() {
|
fn lucene_grouping() {
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = TestEntityRequest::new()
|
||||||
.expression(
|
.expression(
|
||||||
SearchReleaseGroupRequest::new()
|
TestEntityRequest::new()
|
||||||
.no_field("jakarta")
|
.no_field("jakarta")
|
||||||
.or()
|
.or()
|
||||||
.no_field("apache"),
|
.no_field("apache"),
|
||||||
|
161
src/external/musicbrainz/api/search/release_group.rs
vendored
161
src/external/musicbrainz/api/search/release_group.rs
vendored
@ -4,7 +4,7 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::{
|
collection::{
|
||||||
album::{AlbumDate, AlbumPrimaryType, AlbumSecondaryType},
|
album::{AlbumDate, AlbumId, AlbumPrimaryType, AlbumSecondaryType},
|
||||||
musicbrainz::Mbid,
|
musicbrainz::Mbid,
|
||||||
},
|
},
|
||||||
external::musicbrainz::api::{
|
external::musicbrainz::api::{
|
||||||
@ -74,7 +74,7 @@ impl From<DeserializeSearchReleaseGroupResponse> for SearchReleaseGroupResponse
|
|||||||
pub struct SearchReleaseGroupResponseReleaseGroup {
|
pub struct SearchReleaseGroupResponseReleaseGroup {
|
||||||
pub score: u8,
|
pub score: u8,
|
||||||
pub id: Mbid,
|
pub id: Mbid,
|
||||||
pub title: String,
|
pub title: AlbumId,
|
||||||
pub first_release_date: AlbumDate,
|
pub first_release_date: AlbumDate,
|
||||||
pub primary_type: AlbumPrimaryType,
|
pub primary_type: AlbumPrimaryType,
|
||||||
pub secondary_types: Option<Vec<AlbumSecondaryType>>,
|
pub secondary_types: Option<Vec<AlbumSecondaryType>>,
|
||||||
@ -82,7 +82,7 @@ pub struct SearchReleaseGroupResponseReleaseGroup {
|
|||||||
|
|
||||||
#[derive(Clone, Deserialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||||
struct DeserializeSearchReleaseGroupResponseReleaseGroup {
|
pub struct DeserializeSearchReleaseGroupResponseReleaseGroup {
|
||||||
score: u8,
|
score: u8,
|
||||||
id: SerdeMbid,
|
id: SerdeMbid,
|
||||||
title: String,
|
title: String,
|
||||||
@ -98,7 +98,7 @@ impl From<DeserializeSearchReleaseGroupResponseReleaseGroup>
|
|||||||
SearchReleaseGroupResponseReleaseGroup {
|
SearchReleaseGroupResponseReleaseGroup {
|
||||||
score: value.score,
|
score: value.score,
|
||||||
id: value.id.into(),
|
id: value.id.into(),
|
||||||
title: value.title,
|
title: value.title.into(),
|
||||||
first_release_date: value.first_release_date.into(),
|
first_release_date: value.first_release_date.into(),
|
||||||
primary_type: value.primary_type.into(),
|
primary_type: value.primary_type.into(),
|
||||||
secondary_types: value
|
secondary_types: value
|
||||||
@ -110,33 +110,13 @@ impl From<DeserializeSearchReleaseGroupResponseReleaseGroup>
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use mockall::{predicate, Sequence};
|
use mockall::predicate;
|
||||||
|
|
||||||
use crate::{
|
use crate::external::musicbrainz::{api::MusicBrainzClient, MockIMusicBrainzHttp};
|
||||||
collection::album::AlbumId,
|
|
||||||
external::musicbrainz::{api::MusicBrainzClient, MockIMusicBrainzHttp},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
fn de_response() -> DeserializeSearchReleaseGroupResponse {
|
||||||
fn search_release_group() {
|
|
||||||
let mut http = MockIMusicBrainzHttp::new();
|
|
||||||
let url_title = format!(
|
|
||||||
"https://musicbrainz.org/ws/2\
|
|
||||||
/release-group\
|
|
||||||
?query=arid%3A{arid}+AND+firstreleasedate%3A{date}+AND+releasegroup%3A%22{title}%22",
|
|
||||||
arid = "00000000-0000-0000-0000-000000000000",
|
|
||||||
date = "1986-04",
|
|
||||||
title = "an+album",
|
|
||||||
);
|
|
||||||
let url_rgid = format!(
|
|
||||||
"https://musicbrainz.org/ws/2\
|
|
||||||
/release-group\
|
|
||||||
?query=rgid%3A{rgid}",
|
|
||||||
rgid = "11111111-1111-1111-1111-111111111111",
|
|
||||||
);
|
|
||||||
|
|
||||||
let de_release_group = DeserializeSearchReleaseGroupResponseReleaseGroup {
|
let de_release_group = DeserializeSearchReleaseGroupResponseReleaseGroup {
|
||||||
score: 67,
|
score: 67,
|
||||||
id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()),
|
id: SerdeMbid("11111111-1111-1111-1111-111111111111".try_into().unwrap()),
|
||||||
@ -145,78 +125,72 @@ mod tests {
|
|||||||
primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album),
|
primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album),
|
||||||
secondary_types: Some(vec![SerdeAlbumSecondaryType(AlbumSecondaryType::Live)]),
|
secondary_types: Some(vec![SerdeAlbumSecondaryType(AlbumSecondaryType::Live)]),
|
||||||
};
|
};
|
||||||
let de_response = DeserializeSearchReleaseGroupResponse {
|
DeserializeSearchReleaseGroupResponse {
|
||||||
release_groups: vec![de_release_group.clone()],
|
release_groups: vec![de_release_group.clone()],
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let release_group = SearchReleaseGroupResponseReleaseGroup {
|
fn response(de_response: DeserializeSearchReleaseGroupResponse) -> SearchReleaseGroupResponse {
|
||||||
score: 67,
|
SearchReleaseGroupResponse {
|
||||||
id: de_release_group.id.0,
|
release_groups: de_response
|
||||||
title: de_release_group.title,
|
.release_groups
|
||||||
first_release_date: de_release_group.first_release_date.0,
|
.into_iter()
|
||||||
primary_type: de_release_group.primary_type.0,
|
.map(|rg| SearchReleaseGroupResponseReleaseGroup {
|
||||||
secondary_types: de_release_group
|
score: 67,
|
||||||
.secondary_types
|
id: rg.id.0,
|
||||||
.map(|v| v.into_iter().map(|st| st.0).collect()),
|
title: rg.title.into(),
|
||||||
};
|
first_release_date: rg.first_release_date.0,
|
||||||
let response = SearchReleaseGroupResponse {
|
primary_type: rg.primary_type.0,
|
||||||
release_groups: vec![release_group.clone()],
|
secondary_types: rg
|
||||||
};
|
.secondary_types
|
||||||
|
.map(|v| v.into_iter().map(|st| st.0).collect()),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut seq = Sequence::new();
|
#[test]
|
||||||
|
fn search_no_field() {
|
||||||
|
let mut http = MockIMusicBrainzHttp::new();
|
||||||
|
let url = format!(
|
||||||
|
"https://musicbrainz.org/ws/2\
|
||||||
|
/release-group\
|
||||||
|
?query=%22{title}%22",
|
||||||
|
title = "an+album",
|
||||||
|
);
|
||||||
|
|
||||||
|
let de_response = de_response();
|
||||||
|
let response = response(de_response.clone());
|
||||||
|
|
||||||
let title_response = de_response.clone();
|
|
||||||
http.expect_get()
|
http.expect_get()
|
||||||
.times(1)
|
.times(1)
|
||||||
.with(predicate::eq(url_title))
|
.with(predicate::eq(url))
|
||||||
.return_once(|_| Ok(title_response))
|
.return_once(|_| Ok(de_response));
|
||||||
.in_sequence(&mut seq);
|
|
||||||
|
|
||||||
let rgid_response = de_response;
|
|
||||||
http.expect_get()
|
|
||||||
.times(1)
|
|
||||||
.with(predicate::eq(url_rgid))
|
|
||||||
.return_once(|_| Ok(rgid_response))
|
|
||||||
.in_sequence(&mut seq);
|
|
||||||
|
|
||||||
let mut client = MusicBrainzClient::new(http);
|
let mut client = MusicBrainzClient::new(http);
|
||||||
|
|
||||||
let arid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap();
|
let title = "an album";
|
||||||
let title: AlbumId = AlbumId::new("an album");
|
|
||||||
let date = (1986, 4).into();
|
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = SearchReleaseGroupRequest::new().no_field(title);
|
||||||
.arid(&arid)
|
|
||||||
.and()
|
|
||||||
.release_group(&title.title)
|
|
||||||
.and()
|
|
||||||
.first_release_date(&date);
|
|
||||||
|
|
||||||
let matches = client.search_release_group(query).unwrap();
|
|
||||||
assert_eq!(matches, response);
|
|
||||||
|
|
||||||
let rgid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
|
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new().rgid(&rgid);
|
|
||||||
|
|
||||||
let matches = client.search_release_group(query).unwrap();
|
let matches = client.search_release_group(query).unwrap();
|
||||||
assert_eq!(matches, response);
|
assert_eq!(matches, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn search_release_group_empty_date() {
|
fn search_arid_album_date_release_group() {
|
||||||
let mut http = MockIMusicBrainzHttp::new();
|
let mut http = MockIMusicBrainzHttp::new();
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://musicbrainz.org/ws/2\
|
"https://musicbrainz.org/ws/2\
|
||||||
/release-group\
|
/release-group\
|
||||||
?query=arid%3A{arid}+AND+releasegroup%3A%22{title}%22",
|
?query=arid%3A{arid}+AND+releasegroup%3A%22{title}%22+AND+firstreleasedate%3A{date}",
|
||||||
arid = "00000000-0000-0000-0000-000000000000",
|
arid = "00000000-0000-0000-0000-000000000000",
|
||||||
|
date = "1986-04",
|
||||||
title = "an+album",
|
title = "an+album",
|
||||||
);
|
);
|
||||||
|
|
||||||
let de_response = DeserializeSearchReleaseGroupResponse {
|
let de_response = de_response();
|
||||||
release_groups: vec![],
|
let response = response(de_response.clone());
|
||||||
};
|
|
||||||
|
|
||||||
http.expect_get()
|
http.expect_get()
|
||||||
.times(1)
|
.times(1)
|
||||||
@ -226,16 +200,45 @@ mod tests {
|
|||||||
let mut client = MusicBrainzClient::new(http);
|
let mut client = MusicBrainzClient::new(http);
|
||||||
|
|
||||||
let arid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap();
|
let arid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap();
|
||||||
let title: AlbumId = AlbumId::new("an album");
|
let title = "an album";
|
||||||
let date = AlbumDate::default();
|
let date = (1986, 4).into();
|
||||||
|
|
||||||
let query = SearchReleaseGroupRequest::new()
|
let query = SearchReleaseGroupRequest::new()
|
||||||
.arid(&arid)
|
.arid(&arid)
|
||||||
.and()
|
.and()
|
||||||
.release_group(&title.title)
|
.release_group(title)
|
||||||
.and()
|
.and()
|
||||||
.first_release_date(&date);
|
.first_release_date(&date);
|
||||||
|
|
||||||
let _ = client.search_release_group(query).unwrap();
|
let matches = client.search_release_group(query).unwrap();
|
||||||
|
assert_eq!(matches, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn search_rgid() {
|
||||||
|
let mut http = MockIMusicBrainzHttp::new();
|
||||||
|
let url = format!(
|
||||||
|
"https://musicbrainz.org/ws/2\
|
||||||
|
/release-group\
|
||||||
|
?query=rgid%3A{rgid}",
|
||||||
|
rgid = "11111111-1111-1111-1111-111111111111",
|
||||||
|
);
|
||||||
|
|
||||||
|
let de_response = de_response();
|
||||||
|
let response = response(de_response.clone());
|
||||||
|
|
||||||
|
http.expect_get()
|
||||||
|
.times(1)
|
||||||
|
.with(predicate::eq(url))
|
||||||
|
.return_once(|_| Ok(de_response));
|
||||||
|
|
||||||
|
let mut client = MusicBrainzClient::new(http);
|
||||||
|
|
||||||
|
let rgid: Mbid = "11111111-1111-1111-1111-111111111111".try_into().unwrap();
|
||||||
|
|
||||||
|
let query = SearchReleaseGroupRequest::new().rgid(&rgid);
|
||||||
|
|
||||||
|
let matches = client.search_release_group(query).unwrap();
|
||||||
|
assert_eq!(matches, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user