Added last.fm support

This commit is contained in:
Connor Johnstone
2026-03-02 21:46:39 -05:00
parent 8a07a3edc2
commit 16e8962be1
6 changed files with 516 additions and 23 deletions

89
src/lastfm.rs Normal file
View 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())
}
}