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). popularity signal is still useful as a tiebreak).
``prefer_known_duration``: when True, recordings with non-zero ``prefer_known_duration``: when True, recordings with non-zero
``duration_ms`` are ranked ahead of duplicate-score recordings ``duration_ms`` get a score boost. Used for MusicBrainz, which
that lack length data. Used for MusicBrainz which often has often has several recordings per song (single edition, album
several recordings per song (single edition, album edition, edition, compilations, remasters) where some carry length data
compilations, remasters) where some carry length and some don't. and some don't. The boost is set above the album_type weight
Sort key sits between score and the stable-order tiebreaker so spread so length-known recordings can beat length-less
relevance still wins length is only a tiebreaker on equal siblings even when the sibling sits on a higher-weighted
scores, not a global re-shuffle. 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 No-op when both ``expected_title`` and ``expected_artist`` are
empty (no signal to rank against return input order).""" empty (no signal to rank against return input order)."""
@ -323,11 +330,18 @@ def rerank_tracks(
for idx, t in enumerate(tracks) for idx, t in enumerate(tracks)
] ]
if prefer_known_duration: if prefer_known_duration:
# Sort key: score desc, has-length first (0 before 1), idx asc. # Multiplier sized above the album-type weight spread (album 1.0
scored.sort(key=lambda x: (-x[0], 0 if (x[2].duration_ms or 0) > 0 else 1, x[1])) # vs single 0.85 = ~18%) so length-known recordings can overcome
else: # the album-vs-single penalty when scores would otherwise tie on
# Sort by score desc; idx asc as tiebreaker preserves stable order. # title + artist match. Penalty multipliers (cover/karaoke=0.05,
scored.sort(key=lambda x: (-x[0], x[1])) # 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] return [t for _score, _idx, t in scored]