Compare commits

..

1 Commits

Author SHA1 Message Date
Connor Johnstone 8e3d86c25e Added the import/cleanup functionality 2026-03-24 15:58:14 -04:00
2 changed files with 38 additions and 8 deletions
+37 -7
View File
@@ -80,17 +80,21 @@ pub fn score_match(track: &track::Model, candidate: &RecordingMatch) -> f64 {
let mut score = 0.6 * title_sim + 0.4 * artist_sim;
// Bonus: album name matches a release
// Album match bonus: strongly prefer recordings that appear on the track's album.
// This is critical for imported files that already have correct album tags.
if let Some(ref album) = track.album {
let track_album = normalize(album);
if !track_album.is_empty() {
let mut best_album_sim = 0.0f64;
for release in &candidate.releases {
let release_title = normalize(&release.title);
let album_sim = strsim::jaro_winkler(&track_album, &release_title);
if album_sim > 0.85 {
score += 0.05;
break;
}
let sim = strsim::jaro_winkler(&track_album, &release_title);
best_album_sim = best_album_sim.max(sim);
}
if best_album_sim > 0.85 {
score += 0.15; // Strong bonus for matching album
} else if best_album_sim < 0.5 {
score -= 0.10; // Penalty for clearly wrong album
}
}
}
@@ -125,7 +129,8 @@ pub fn select_best_match(
);
if confidence >= threshold {
let best_release = candidate.releases.first().cloned();
// Pick the release that best matches the track's album name
let best_release = pick_best_release(track, &candidate.releases);
let scored = ScoredMatch {
recording: candidate,
confidence,
@@ -141,6 +146,31 @@ pub fn select_best_match(
best
}
/// Pick the best release from candidates based on the track's album metadata.
/// If the track has an album name, prefer the release with the closest title match.
/// Otherwise, fall back to the first release.
pub fn pick_best_release(track: &track::Model, releases: &[ReleaseRef]) -> Option<ReleaseRef> {
if releases.is_empty() {
return None;
}
let track_album = track.album.as_deref().map(normalize).unwrap_or_default();
if track_album.is_empty() {
return releases.first().cloned();
}
let mut best: Option<(f64, &ReleaseRef)> = None;
for release in releases {
let sim = strsim::jaro_winkler(&track_album, &normalize(&release.title));
match best {
Some((best_sim, _)) if sim <= best_sim => {}
_ => best = Some((sim, release)),
}
}
best.map(|(_, r)| r.clone())
}
#[cfg(test)]
mod tests {
use super::*;
+1 -1
View File
@@ -61,7 +61,7 @@ pub async fn tag_track(
}
let details = provider.get_recording(mbid).await?;
let best_release = details.releases.first().cloned();
let best_release = crate::matcher::pick_best_release(track, &details.releases);
(details, best_release)
} else {
// No MBID — search by artist + title