use shanty_tag::MusicBrainzClient; use shanty_tag::provider::MetadataProvider; use crate::error::SearchResult; use crate::provider::{ AlbumResult, ArtistResult, Discography, DiscographyEntry, SearchProvider, TrackResult, }; /// MusicBrainz implementation of `SearchProvider`, wrapping shanty-tag's client. pub struct MusicBrainzSearch { client: MusicBrainzClient, } impl MusicBrainzSearch { pub fn new() -> SearchResult { let client = MusicBrainzClient::new() .map_err(|e| crate::error::SearchError::Provider(e.to_string()))?; Ok(Self { client }) } } impl SearchProvider for MusicBrainzSearch { async fn search_artist(&self, query: &str, limit: u32) -> SearchResult> { let results = self.client.search_artist(query, limit).await?; Ok(results .into_iter() .map(|a| ArtistResult { id: a.mbid, name: a.name, disambiguation: a.disambiguation, country: a.country, artist_type: a.artist_type, score: a.score, }) .collect()) } async fn search_album( &self, query: &str, artist_hint: Option<&str>, limit: u32, ) -> SearchResult> { let artist = artist_hint.unwrap_or(""); let results = self.client.search_release(artist, query).await?; Ok(results .into_iter() .take(limit as usize) .map(|r| AlbumResult { id: r.mbid, title: r.title, artist: r.artist, artist_id: r.artist_mbid, year: r .date .as_deref() .and_then(|d| d.split('-').next()) .map(String::from), track_count: r.track_count, score: r.score, }) .collect()) } async fn search_track( &self, query: &str, artist_hint: Option<&str>, limit: u32, ) -> SearchResult> { let artist = artist_hint.unwrap_or(""); let results = self.client.search_recording(artist, query).await?; Ok(results .into_iter() .take(limit as usize) .map(|r| TrackResult { id: r.mbid, title: r.title, artist: r.artist, artist_id: r.artist_mbid, album: r.releases.first().map(|rel| rel.title.clone()), duration_ms: None, // not in search results score: r.score, }) .collect()) } async fn get_discography(&self, artist_id: &str) -> SearchResult { let releases = self.client.get_artist_releases(artist_id, 100).await?; // Try to get the artist name from the first release, or use the MBID let artist_name = if !releases.is_empty() { // We don't have the artist name from this endpoint directly, // so do a quick artist search by MBID let artists = self.client.search_artist(artist_id, 1).await.ok(); artists .and_then(|a| a.into_iter().next()) .map(|a| a.name) .unwrap_or_else(|| artist_id.to_string()) } else { artist_id.to_string() }; Ok(Discography { artist_name, artist_id: artist_id.to_string(), releases: releases .into_iter() .map(|r| DiscographyEntry { id: r.mbid, title: r.title, date: r.date, release_type: r.release_type, track_count: r.track_count, }) .collect(), }) } async fn get_release_groups( &self, artist_id: &str, ) -> SearchResult> { let groups = self.client.get_artist_release_groups(artist_id).await?; Ok(groups .into_iter() .map(|rg| crate::provider::ReleaseGroupResult { id: rg.mbid, title: rg.title, primary_type: rg.primary_type, secondary_types: rg.secondary_types, first_release_date: rg.first_release_date, first_release_id: rg.first_release_mbid, }) .collect()) } }