Search for both mbid and name
This commit is contained in:
154
src/lastfm.rs
154
src/lastfm.rs
@@ -92,7 +92,9 @@ impl LastfmClient {
|
|||||||
.replace('\u{2012}', "-")
|
.replace('\u{2012}', "-")
|
||||||
.replace('\u{2013}', "-")
|
.replace('\u{2013}', "-")
|
||||||
.replace('\u{2014}', "-")
|
.replace('\u{2014}', "-")
|
||||||
.replace('\u{2015}', "-");
|
.replace('\u{2015}', "-")
|
||||||
|
.replace('\u{2018}', "'")
|
||||||
|
.replace('\u{2019}', "'");
|
||||||
let encoded = urlencoding::encode(&name);
|
let encoded = urlencoding::encode(&name);
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}?method={}&artist={}&api_key={}{}&format=json",
|
"{}?method={}&artist={}&api_key={}{}&format=json",
|
||||||
@@ -103,41 +105,9 @@ impl LastfmClient {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try fetching by MBID first, fall back to artist name.
|
fn parse_similar_artists(body: &str) -> Result<Vec<SimilarArtist>, Box<dyn std::error::Error>> {
|
||||||
fn fetch_with_fallback(
|
let resp: SimilarArtistsResponse = serde_json::from_str(body)?;
|
||||||
&self,
|
Ok(resp
|
||||||
method: &str,
|
|
||||||
artist_mbid: &str,
|
|
||||||
artist_name: Option<&str>,
|
|
||||||
extra_params: &str,
|
|
||||||
) -> Result<Option<String>, Box<dyn std::error::Error>> {
|
|
||||||
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<Vec<SimilarArtist>, Box<dyn std::error::Error>> {
|
|
||||||
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<SimilarArtist> = resp
|
|
||||||
.similarartists
|
.similarartists
|
||||||
.artist
|
.artist
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -149,47 +119,43 @@ impl LastfmClient {
|
|||||||
match_score: a.match_score.parse().unwrap_or(0.0),
|
match_score: a.match_score.parse().unwrap_or(0.0),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_top_tracks(
|
pub fn get_similar_artists(
|
||||||
&self,
|
&self,
|
||||||
artist_mbid: &str,
|
artist_mbid: &str,
|
||||||
artist_name: Option<&str>,
|
artist_name: Option<&str>,
|
||||||
) -> Result<Vec<TopTrack>, Box<dyn std::error::Error>> {
|
) -> Result<Vec<SimilarArtist>, Box<dyn std::error::Error>> {
|
||||||
let Some(body) = self.fetch_with_fallback(
|
let method = "artist.getSimilar";
|
||||||
"artist.getTopTracks",
|
let extra = "&limit=500";
|
||||||
artist_mbid,
|
|
||||||
artist_name,
|
// Try MBID lookup
|
||||||
"&limit=1000",
|
let mbid_url = format!(
|
||||||
)? else {
|
"{}?method={}&mbid={}&api_key={}{}&format=json",
|
||||||
return Ok(Vec::new());
|
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)?;
|
// Try name lookup and return whichever has more results
|
||||||
let results: Vec<TopTrack> = resp
|
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<Vec<TopTrack>, Box<dyn std::error::Error>> {
|
||||||
|
let resp: TopTracksResponse = serde_json::from_str(body)?;
|
||||||
|
Ok(resp
|
||||||
.toptracks
|
.toptracks
|
||||||
.track
|
.track
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -199,25 +165,37 @@ impl LastfmClient {
|
|||||||
playcount: t.playcount.parse().unwrap_or(0),
|
playcount: t.playcount.parse().unwrap_or(0),
|
||||||
listeners: t.listeners.parse().unwrap_or(0),
|
listeners: t.listeners.parse().unwrap_or(0),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
if results.is_empty() {
|
pub fn get_top_tracks(
|
||||||
if let Some(body) = self.fetch_by_name("artist.getTopTracks", artist_name, "&limit=1000")? {
|
&self,
|
||||||
let resp: TopTracksResponse = serde_json::from_str(&body)?;
|
artist_mbid: &str,
|
||||||
return Ok(resp
|
artist_name: Option<&str>,
|
||||||
.toptracks
|
) -> Result<Vec<TopTrack>, Box<dyn std::error::Error>> {
|
||||||
.track
|
let method = "artist.getTopTracks";
|
||||||
.into_iter()
|
let extra = "&limit=1000";
|
||||||
.map(|t| TopTrack {
|
|
||||||
name: t.name,
|
// Try MBID lookup
|
||||||
mbid: t.mbid.filter(|s| !s.is_empty()),
|
let mbid_url = format!(
|
||||||
playcount: t.playcount.parse().unwrap_or(0),
|
"{}?method={}&mbid={}&api_key={}{}&format=json",
|
||||||
listeners: t.listeners.parse().unwrap_or(0),
|
BASE_URL, method, artist_mbid, self.api_key, extra
|
||||||
})
|
);
|
||||||
.collect());
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user