Added last.fm support
This commit is contained in:
89
src/lastfm.rs
Normal file
89
src/lastfm.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
const BASE_URL: &str = "https://ws.audioscrobbler.com/2.0/";
|
||||
|
||||
pub struct LastfmClient {
|
||||
api_key: String,
|
||||
artist_cache: HashMap<String, Vec<SimilarArtist>>,
|
||||
}
|
||||
|
||||
pub struct SimilarArtist {
|
||||
pub name: String,
|
||||
pub mbid: Option<String>,
|
||||
pub match_score: f64,
|
||||
}
|
||||
|
||||
// Last.fm returns {"error": N, "message": "..."} on failure
|
||||
#[derive(Deserialize)]
|
||||
struct ApiError {
|
||||
error: u32,
|
||||
message: String,
|
||||
}
|
||||
|
||||
// Deserialization structs for the Last.fm API responses
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SimilarArtistsResponse {
|
||||
similarartists: SimilarArtistsWrapper,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SimilarArtistsWrapper {
|
||||
artist: Vec<ArtistEntry>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ArtistEntry {
|
||||
name: String,
|
||||
mbid: Option<String>,
|
||||
#[serde(rename = "match")]
|
||||
match_score: String,
|
||||
}
|
||||
|
||||
impl LastfmClient {
|
||||
pub fn new(api_key: String) -> Self {
|
||||
Self {
|
||||
api_key,
|
||||
artist_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_similar_artists(
|
||||
&mut self,
|
||||
artist_mbid: &str,
|
||||
) -> Result<&[SimilarArtist], Box<dyn std::error::Error>> {
|
||||
if !self.artist_cache.contains_key(artist_mbid) {
|
||||
let url = format!(
|
||||
"{}?method=artist.getSimilar&mbid={}&api_key={}&format=json",
|
||||
BASE_URL, artist_mbid, self.api_key
|
||||
);
|
||||
let body: String = ureq::get(&url).call()?.body_mut().read_to_string()?;
|
||||
|
||||
let artists = if let Ok(err) = serde_json::from_str::<ApiError>(&body) {
|
||||
eprintln!(" Last.fm: {}", err.message);
|
||||
Vec::new()
|
||||
} else {
|
||||
let resp: SimilarArtistsResponse = serde_json::from_str(&body)?;
|
||||
resp.similarartists
|
||||
.artist
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
let mbid = a.mbid.filter(|s| !s.is_empty());
|
||||
SimilarArtist {
|
||||
name: a.name,
|
||||
mbid,
|
||||
match_score: a.match_score.parse().unwrap_or(0.0),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
self.artist_cache.insert(artist_mbid.to_string(), artists);
|
||||
}
|
||||
|
||||
Ok(self.artist_cache.get(artist_mbid).unwrap())
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user