Compare commits
1 Commits
82fed6ea6e
...
8e3d86c25e
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e3d86c25e |
+37
-7
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user