MB rerank: prefer_known_duration is now a score boost, not a tiebreaker

Live smoke against `/api/musicbrainz/search_tracks?track=Coffee+Break&artist=Zeds+Dead`
exposed the edge case the tiebreaker implementation couldn't reach:

The canonical Zeds Dead "Coffee Break" recording (mbid 6e2d4a70, length
184000ms) lives on the Coffee Break Single release — album_type='single',
which carries a 0.85 album_type_weight in `score_track`. A sibling
length-less recording (mbid 3b89bf3c) lives on an Album release —
album_type='album', weight 1.0. After multiplying by EXACT_ARTIST_BOOST
the canonical sat at 1.275 while the length-less sibling sat at 1.5.

The previous tiebreaker only kicked in on equal scores, so the
length-less album edition wins and the user sees 0:00 first instead of
the actionable 3:04 row. Bug reproduced: ordering came out
length-less / canonical / Omar-LinX-collab.

Switched `prefer_known_duration` to a 1.25x score boost on recordings
with non-zero duration_ms. The multiplier is sized above the
album-vs-single weight spread (0.176) so length-known recordings can
overcome an album-type penalty when scores would otherwise tie on
title + artist match, but stays small enough that cover/karaoke
penalty (0.05) and variant-tag penalty (0.85) still dominate — a
length-known tribute still loses to a length-less canonical.

Post-fix live response: 6e2d4a70 (canonical, 184000ms) sits first,
8ec2ce3f (Zeds Dead + Omar LinX collab, 153000ms) second, 3b89bf3c
(length-less album edition) third.

Verified Björk diacritic fallback path unaffected — `Bjork` + `Army of
Me` still cascades strict-empty → bare and returns all 10 Björk
recordings.

122 metadata tests pass — the three `prefer_known_duration` cases were
designed to pin behaviour, not the specific multiplier value, so they
all still pass under the boost implementation: ties promote
length-known, relevance still beats length-pref, default-off behaviour
unchanged.
This commit is contained in:
Broque Thomas 2026-05-27 08:13:28 -07:00
parent 8dbbf13c61
commit 6125ef8834

View file

@ -306,13 +306,20 @@ def rerank_tracks(
popularity signal is still useful as a tiebreak).
``prefer_known_duration``: when True, recordings with non-zero
``duration_ms`` are ranked ahead of duplicate-score recordings
that lack length data. Used for MusicBrainz which often has
several recordings per song (single edition, album edition,
compilations, remasters) where some carry length and some don't.
Sort key sits between score and the stable-order tiebreaker so
relevance still wins length is only a tiebreaker on equal
scores, not a global re-shuffle.
``duration_ms`` get a score boost. Used for MusicBrainz, which
often has several recordings per song (single edition, album
edition, compilations, remasters) where some carry length data
and some don't. The boost is set above the album_type weight
spread so length-known recordings can beat length-less
siblings even when the sibling sits on a higher-weighted
album-type real case: Zeds Dead "Coffee Break" canonical
recording lives on the Single release (album_type='single',
weight 0.85) while a length-less sibling lives on an Album
release (weight 1.0). Without the boost, the length-less album
edition wins and the user sees 0:00 instead of 3:04. Cover /
karaoke penalties dominate the boost (their penalty is 0.05)
so a length-known tribute still loses to a length-less
canonical match.
No-op when both ``expected_title`` and ``expected_artist`` are
empty (no signal to rank against return input order)."""
@ -323,11 +330,18 @@ def rerank_tracks(
for idx, t in enumerate(tracks)
]
if prefer_known_duration:
# Sort key: score desc, has-length first (0 before 1), idx asc.
scored.sort(key=lambda x: (-x[0], 0 if (x[2].duration_ms or 0) > 0 else 1, x[1]))
else:
# Sort by score desc; idx asc as tiebreaker preserves stable order.
scored.sort(key=lambda x: (-x[0], x[1]))
# Multiplier sized above the album-type weight spread (album 1.0
# vs single 0.85 = ~18%) so length-known recordings can overcome
# the album-vs-single penalty when scores would otherwise tie on
# title + artist match. Penalty multipliers (cover/karaoke=0.05,
# variant=0.85) still dominate, so this only flips order among
# close-relevance siblings — exactly the MB-duplicate case.
scored = [
(score * 1.25 if (t.duration_ms or 0) > 0 else score, idx, t)
for score, idx, t in scored
]
# Sort by score desc; idx asc as tiebreaker preserves stable order.
scored.sort(key=lambda x: (-x[0], x[1]))
return [t for _score, _idx, t in scored]