several small ui updates, plus hopefully a track-matching fix
CI / check (push) Failing after 25s
CI / docker (push) Has been skipped

This commit is contained in:
Connor Johnstone
2026-04-01 22:32:52 -04:00
parent b2f030b52d
commit 01365dbb80
3 changed files with 27 additions and 9 deletions
+25 -7
View File
@@ -38,12 +38,17 @@ pub fn score_tracks(
.cloned() .cloned()
.unwrap_or_default(); .unwrap_or_default();
// Build playcount lookup by lowercase name // Build playcount lookups by lowercase name and by MBID
let playcount_by_name: HashMap<String, u64> = top_tracks let playcount_by_name: HashMap<String, u64> = top_tracks
.iter() .iter()
.map(|t| (t.name.to_lowercase(), t.playcount)) .map(|t| (t.name.to_lowercase(), t.playcount))
.collect(); .collect();
let playcount_by_mbid: HashMap<String, u64> = top_tracks
.iter()
.filter_map(|t| t.mbid.as_ref().map(|m| (m.clone(), t.playcount)))
.collect();
let max_playcount = playcount_by_name let max_playcount = playcount_by_name
.values() .values()
.copied() .copied()
@@ -54,6 +59,7 @@ pub fn score_tracks(
for track in local_tracks { for track in local_tracks {
let title_lower = track.title.as_ref().map(|t| t.to_lowercase()); 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 let playcount = title_lower
.as_ref() .as_ref()
.and_then(|t| playcount_by_name.get(t).copied()) .and_then(|t| playcount_by_name.get(t).copied())
@@ -61,19 +67,31 @@ pub fn score_tracks(
track track
.musicbrainz_id .musicbrainz_id
.as_ref() .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 (popularity, similarity, score) = if !playcount_by_name.is_empty() {
let Some(playcount) = playcount else { let playcount = playcount.unwrap_or(0);
continue;
};
let popularity = if playcount > 0 { let popularity = if playcount > 0 {
(playcount as f64 / max_playcount as f64).powf(POPULARITY_EXPONENTS[bias]) (playcount as f64 / max_playcount as f64).powf(POPULARITY_EXPONENTS[bias])
} else { } else {
0.0 // Unmatched track: small base score so it can still appear
0.01
}; };
let similarity = (match_score.exp()) / std::f64::consts::E; let similarity = (match_score.exp()) / std::f64::consts::E;
+1 -1
View File
@@ -72,7 +72,7 @@ impl SimilarConfig {
} }
fn default_count() -> usize { fn default_count() -> usize {
50 30
} }
fn default_popularity_bias() -> u8 { fn default_popularity_bias() -> u8 {