From 51ededc61248be5bbeaa6cce0c534027cc1bf26f Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Tue, 3 Mar 2026 13:45:52 -0500 Subject: [PATCH] Search for both mbid and name --- src/lastfm.rs | 154 ++++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 88 deletions(-) diff --git a/src/lastfm.rs b/src/lastfm.rs index a5564ab..953dbaf 100644 --- a/src/lastfm.rs +++ b/src/lastfm.rs @@ -92,7 +92,9 @@ impl LastfmClient { .replace('\u{2012}', "-") .replace('\u{2013}', "-") .replace('\u{2014}', "-") - .replace('\u{2015}', "-"); + .replace('\u{2015}', "-") + .replace('\u{2018}', "'") + .replace('\u{2019}', "'"); let encoded = urlencoding::encode(&name); let url = format!( "{}?method={}&artist={}&api_key={}{}&format=json", @@ -103,41 +105,9 @@ impl LastfmClient { Ok(None) } - /// Try fetching by MBID first, fall back to artist name. - fn fetch_with_fallback( - &self, - method: &str, - artist_mbid: &str, - artist_name: Option<&str>, - extra_params: &str, - ) -> Result, Box> { - let url = format!( - "{}?method={}&mbid={}&api_key={}{}&format=json", - BASE_URL, method, artist_mbid, self.api_key, extra_params - ); - if let Some(body) = self.fetch_or_none(&url)? { - return Ok(Some(body)); - } - - self.fetch_by_name(method, artist_name, extra_params) - } - - pub fn get_similar_artists( - &self, - artist_mbid: &str, - artist_name: Option<&str>, - ) -> Result, Box> { - let Some(body) = self.fetch_with_fallback( - "artist.getSimilar", - artist_mbid, - artist_name, - "&limit=500", - )? else { - return Ok(Vec::new()); - }; - - let resp: SimilarArtistsResponse = serde_json::from_str(&body)?; - let results: Vec = resp + fn parse_similar_artists(body: &str) -> Result, Box> { + let resp: SimilarArtistsResponse = serde_json::from_str(body)?; + Ok(resp .similarartists .artist .into_iter() @@ -149,47 +119,43 @@ impl LastfmClient { match_score: a.match_score.parse().unwrap_or(0.0), } }) - .collect(); - - // MBID lookup can return valid but empty results; retry with name - if results.is_empty() { - if let Some(body) = self.fetch_by_name("artist.getSimilar", artist_name, "&limit=500")? { - let resp: SimilarArtistsResponse = serde_json::from_str(&body)?; - return Ok(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()); - } - } - - Ok(results) + .collect()) } - pub fn get_top_tracks( + pub fn get_similar_artists( &self, artist_mbid: &str, artist_name: Option<&str>, - ) -> Result, Box> { - let Some(body) = self.fetch_with_fallback( - "artist.getTopTracks", - artist_mbid, - artist_name, - "&limit=1000", - )? else { - return Ok(Vec::new()); + ) -> Result, Box> { + let method = "artist.getSimilar"; + let extra = "&limit=500"; + + // Try MBID lookup + let mbid_url = format!( + "{}?method={}&mbid={}&api_key={}{}&format=json", + BASE_URL, method, artist_mbid, self.api_key, extra + ); + let mbid_results = match self.fetch_or_none(&mbid_url)? { + Some(body) => Self::parse_similar_artists(&body).unwrap_or_default(), + None => Vec::new(), }; - let resp: TopTracksResponse = serde_json::from_str(&body)?; - let results: Vec = resp + // Try name lookup and return whichever has more results + let name_results = match self.fetch_by_name(method, artist_name, extra)? { + Some(body) => Self::parse_similar_artists(&body).unwrap_or_default(), + None => Vec::new(), + }; + + if name_results.len() > mbid_results.len() { + Ok(name_results) + } else { + Ok(mbid_results) + } + } + + fn parse_top_tracks(body: &str) -> Result, Box> { + let resp: TopTracksResponse = serde_json::from_str(body)?; + Ok(resp .toptracks .track .into_iter() @@ -199,25 +165,37 @@ impl LastfmClient { playcount: t.playcount.parse().unwrap_or(0), listeners: t.listeners.parse().unwrap_or(0), }) - .collect(); + .collect()) + } - if results.is_empty() { - if let Some(body) = self.fetch_by_name("artist.getTopTracks", artist_name, "&limit=1000")? { - let resp: TopTracksResponse = serde_json::from_str(&body)?; - return Ok(resp - .toptracks - .track - .into_iter() - .map(|t| TopTrack { - name: t.name, - mbid: t.mbid.filter(|s| !s.is_empty()), - playcount: t.playcount.parse().unwrap_or(0), - listeners: t.listeners.parse().unwrap_or(0), - }) - .collect()); - } + pub fn get_top_tracks( + &self, + artist_mbid: &str, + artist_name: Option<&str>, + ) -> Result, Box> { + let method = "artist.getTopTracks"; + let extra = "&limit=1000"; + + // Try MBID lookup + let mbid_url = format!( + "{}?method={}&mbid={}&api_key={}{}&format=json", + BASE_URL, method, artist_mbid, self.api_key, extra + ); + let mbid_results = match self.fetch_or_none(&mbid_url)? { + Some(body) => Self::parse_top_tracks(&body).unwrap_or_default(), + None => Vec::new(), + }; + + // Try name lookup and return whichever has more results + let name_results = match self.fetch_by_name(method, artist_name, extra)? { + Some(body) => Self::parse_top_tracks(&body).unwrap_or_default(), + None => Vec::new(), + }; + + if name_results.len() > mbid_results.len() { + Ok(name_results) + } else { + Ok(mbid_results) } - - Ok(results) } }