Initial commit
This commit is contained in:
122
src/musicbrainz.rs
Normal file
122
src/musicbrainz.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use shanty_tag::provider::MetadataProvider;
|
||||
use shanty_tag::MusicBrainzClient;
|
||||
|
||||
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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user