From 01365dbb80d3f5fe3e1677a8787c6d677245425c Mon Sep 17 00:00:00 2001 From: Connor Johnstone Date: Wed, 1 Apr 2026 22:32:52 -0400 Subject: [PATCH] several small ui updates, plus hopefully a track-matching fix --- shanty-playlist/src/scoring.rs | 32 +++++++++++++++++++++++++------- shanty-playlist/src/types.rs | 2 +- shanty-web | 2 +- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/shanty-playlist/src/scoring.rs b/shanty-playlist/src/scoring.rs index efb3ba7..60def05 100644 --- a/shanty-playlist/src/scoring.rs +++ b/shanty-playlist/src/scoring.rs @@ -38,12 +38,17 @@ pub fn score_tracks( .cloned() .unwrap_or_default(); - // Build playcount lookup by lowercase name + // Build playcount lookups by lowercase name and by MBID let playcount_by_name: HashMap = top_tracks .iter() .map(|t| (t.name.to_lowercase(), t.playcount)) .collect(); + let playcount_by_mbid: HashMap = top_tracks + .iter() + .filter_map(|t| t.mbid.as_ref().map(|m| (m.clone(), t.playcount))) + .collect(); + let max_playcount = playcount_by_name .values() .copied() @@ -54,6 +59,7 @@ pub fn score_tracks( for track in local_tracks { let title_lower = track.title.as_ref().map(|t| t.to_lowercase()); + // Match by: exact title, MBID, or prefix/contains (for parenthetical suffixes) let playcount = title_lower .as_ref() .and_then(|t| playcount_by_name.get(t).copied()) @@ -61,19 +67,31 @@ pub fn score_tracks( track .musicbrainz_id .as_ref() - .and_then(|id| playcount_by_name.get(id).copied()) + .and_then(|id| playcount_by_mbid.get(id).copied()) + }) + .or_else(|| { + // Fuzzy: local title starts with a top track name, or vice versa + title_lower.as_ref().and_then(|local| { + playcount_by_name + .iter() + .filter(|(top_name, _)| { + local.starts_with(top_name.as_str()) + || top_name.starts_with(local.as_str()) + }) + .max_by_key(|(_, &pc)| pc) + .map(|(_, &pc)| pc) + }) }); - // If we have popularity data, require a match; otherwise assign uniform score + // If we have popularity data, use it; unmatched tracks get a low base score let (popularity, similarity, score) = if !playcount_by_name.is_empty() { - let Some(playcount) = playcount else { - continue; - }; + let playcount = playcount.unwrap_or(0); let popularity = if playcount > 0 { (playcount as f64 / max_playcount as f64).powf(POPULARITY_EXPONENTS[bias]) } else { - 0.0 + // Unmatched track: small base score so it can still appear + 0.01 }; let similarity = (match_score.exp()) / std::f64::consts::E; diff --git a/shanty-playlist/src/types.rs b/shanty-playlist/src/types.rs index 057141e..b3e1fd7 100644 --- a/shanty-playlist/src/types.rs +++ b/shanty-playlist/src/types.rs @@ -72,7 +72,7 @@ impl SimilarConfig { } fn default_count() -> usize { - 50 + 30 } fn default_popularity_bias() -> u8 { diff --git a/shanty-web b/shanty-web index 4c42cf0..8193eeb 160000 --- a/shanty-web +++ b/shanty-web @@ -1 +1 @@ -Subproject commit 4c42cf0131284bdb2aa351645e02bd2f6a069df1 +Subproject commit 8193eebf1369c2bf32b19c6441245cf7d1c9c182