diff --git a/src/musicbrainz.rs b/src/musicbrainz.rs index 54654bb..54317f7 100644 --- a/src/musicbrainz.rs +++ b/src/musicbrainz.rs @@ -5,8 +5,8 @@ use tokio::time::{Duration, Instant}; use crate::cleaning::escape_lucene; use crate::error::{TagError, TagResult}; use crate::provider::{ - ArtistSearchResult, DiscographyEntry, MetadataProvider, RecordingDetails, RecordingMatch, - ReleaseGroupEntry, ReleaseMatch, ReleaseRef, ReleaseTrack, + ArtistInfo, ArtistSearchResult, ArtistUrl, DiscographyEntry, MetadataProvider, + RecordingDetails, RecordingMatch, ReleaseGroupEntry, ReleaseMatch, ReleaseRef, ReleaseTrack, }; const BASE_URL: &str = "https://musicbrainz.org/ws/2"; @@ -61,6 +61,36 @@ impl MusicBrainzClient { let resp: MbArtistLookup = self.get_json(&url).await?; Ok((resp.name, resp.disambiguation.filter(|s| !s.is_empty()))) } + + /// Look up detailed artist info by MBID, including URLs and metadata. + pub async fn get_artist_info(&self, mbid: &str) -> TagResult { + let url = format!("{BASE_URL}/artist/{mbid}?inc=url-rels&fmt=json"); + let resp: MbArtistFull = self.get_json(&url).await?; + + let begin_year = resp.life_span + .and_then(|ls| ls.begin) + .and_then(|d| d.split('-').next().map(String::from)); + + let urls = resp.relations + .unwrap_or_default() + .into_iter() + .filter_map(|rel| { + rel.url.map(|u| ArtistUrl { + url: u.resource, + link_type: rel.relation_type, + }) + }) + .collect(); + + Ok(ArtistInfo { + name: resp.name, + disambiguation: resp.disambiguation.filter(|s| !s.is_empty()), + country: resp.country.filter(|s| !s.is_empty()), + artist_type: resp.artist_type, + begin_year, + urls, + }) + } } impl MetadataProvider for MusicBrainzClient { @@ -334,6 +364,35 @@ struct MbArtistLookup { disambiguation: Option, } +#[derive(Deserialize)] +struct MbArtistFull { + name: String, + disambiguation: Option, + country: Option, + #[serde(rename = "type")] + artist_type: Option, + #[serde(rename = "life-span")] + life_span: Option, + relations: Option>, +} + +#[derive(Deserialize)] +struct MbLifeSpan { + begin: Option, +} + +#[derive(Deserialize)] +struct MbRelation { + #[serde(rename = "type")] + relation_type: String, + url: Option, +} + +#[derive(Deserialize)] +struct MbRelationUrl { + resource: String, +} + #[derive(Deserialize)] struct MbRecordingSearchResponse { recordings: Vec, diff --git a/src/provider.rs b/src/provider.rs index b56fc45..6425216 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -50,6 +50,24 @@ pub struct RecordingDetails { pub secondary_artists: Vec<(String, String)>, } +/// Detailed artist info from a direct MBID lookup. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ArtistInfo { + pub name: String, + pub disambiguation: Option, + pub country: Option, + pub artist_type: Option, + pub begin_year: Option, + pub urls: Vec, +} + +/// An external URL linked to an artist on MusicBrainz. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ArtistUrl { + pub url: String, + pub link_type: String, +} + /// An artist match from a search query. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ArtistSearchResult {