138 lines
4.5 KiB
Rust
138 lines
4.5 KiB
Rust
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<Self> {
|
|
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<Vec<ArtistResult>> {
|
|
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<Vec<AlbumResult>> {
|
|
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<Vec<TrackResult>> {
|
|
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<Discography> {
|
|
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<Vec<crate::provider::ReleaseGroupResult>> {
|
|
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())
|
|
}
|
|
}
|