Skip to content

Let Music Assistant resolve Tidal tracks by name when no URI works - #2364

Merged
mholzi merged 2 commits into
mainfrom
feat/tidal-name-fallback
Aug 23, 2026
Merged

Let Music Assistant resolve Tidal tracks by name when no URI works#2364
mholzi merged 2 commits into
mainfrom
feat/tidal-name-fallback

Conversation

@mholzi

@mholzi mholzi commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Why

Odesli retired its public API on 2026-07-31api.song.link now answers 401 PUBLIC_API_ACCESS_DEPRECATED, and the docs confirm keys are no longer handed out. It was the only source Beatify ever had for Tidal ids, so a missing uri_tidal can no longer be filled by the backfill. 1063 catalogue gaps are permanently unreachable that way.

Music Assistant can resolve a track from name + artist, and already does for ma_library. This extends that path to tidal.

What this is, and what it is not

It is a net behind the stored URIs. The candidate loop runs first, so a song with a working uri_tidal never reaches the fallback — the existing 5942 Tidal URIs keep their precedence, because they are more precise than any search. Only ma_library and tidal opt in; every other provider keeps today's hard failure.

The non-obvious part

The early return for an empty candidate list had to yield to the fallback. Without that change the new path could never fire: a Tidal song without a URI has no candidates at all and returned before reaching it.

The risk, measured rather than assumed

A name search can land on a remix, a live take or a karaoke version. _titles_plausibly_match does not catch that — it accepts a normalized prefix, and a title is always a prefix of its own remix. That leniency is load-bearing for #1381 (Das Modell vs The Model), so it stays; a stricter, one-directional edition check runs on the fallback path only.

How big the risk actually is was measured against ~2000 catalogue tracks with a known Deezer id — same question, and Deezer can be searched without a key:

sample (60 each) right recording wrong title not found
across the main playlists 98.3 % 1 0
community/edm-anthems 81.0 % 11 2

The risk is a function of genre, not a single number — roughly 2 % for pop and rock, 19 % for EDM. Every rejection case in the new tests is verbatim from that measurement: SatisfactionSatisfaction (Uk Radio Edit), Scary Monsters and Nice Sprites… (Zedd Remix), Without You (feat. Usher)… (Extended).

This matters more here than it would in a music player. The game asks players to guess the release year, so a 2014 remix standing in for a 1998 original does not merely sound different — it makes the round's correct answer wrong, and nothing on screen reveals that.

Tests

21 new tests in tests/unit/test_tidal_name_fallback.py. Per the house rule they were run against the unpatched code first: the three behaviour tests fail on origin/main and pass here; the three guard tests pass on both, which is what they are for. The 134 existing media-player tests stay green.

Known gap, deliberately left

ma_library has the same wrong-edition exposure and does not get the new check — changing its behaviour is out of scope for this PR. Worth a follow-up.

Why draft

The measurement above says the fallback will occasionally hand a player the wrong edition of the right song. That trade — a round that plays something instead of failing outright — is a product decision, not a technical one.

🤖 Generated with Claude Code

https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt

claude added 2 commits August 23, 2026 21:07
… last resort

Odesli retired its public API on 2026-07-31 (401
PUBLIC_API_ACCESS_DEPRECATED). It was the only source Beatify ever had for
Tidal ids, so a missing `uri_tidal` can no longer be filled by the backfill —
1063 catalogue gaps are now permanently unreachable that way.

Music Assistant can resolve a track from name + artist, and already does so
for `ma_library`. This extends that path to `tidal`.

It is a net *behind* the stored URIs, never a replacement: the candidate loop
runs first, so a song with a working `uri_tidal` never reaches the fallback.
Only `ma_library` and `tidal` opt in; every other provider keeps today's hard
failure.

Two supporting changes:

* The early return for an empty candidate list now yields to the fallback.
  Without this the new path could never fire, because a Tidal song without a
  URI has no candidates at all and returned before reaching it.
* A name search can land on a remix, a live take or a karaoke version, and
  `_titles_plausibly_match` does not catch that — it accepts a normalized
  prefix, and a title is always a prefix of its own remix. That leniency is
  load-bearing for #1381 ("Das Modell" vs "The Model"), so it stays; a stricter
  edition check is applied on the fallback path only.

The risk is measured, not assumed. Against ~2000 catalogue tracks with a known
Deezer id, a plain "artist title" search returned a different recording for 2%
of mainstream tracks but 19% of EDM ones. Every rejection case in the new tests
is verbatim from that measurement.

This matters more here than it would in a music player: the game asks players
to guess the release year, so a 2014 remix standing in for a 1998 original does
not merely sound different — it makes the round's correct answer wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt
`test_stale_uris_fall_back_to_a_name_lookup` matched the literal string
"MA library fallback" in the source. That log line now reads "MA name
fallback (<provider>)" because Tidal shares the path, so the assertion broke
on a rename while the behaviour it guards was untouched.

It now checks `_NAME_FALLBACK_PROVIDERS` and the `ma_library` membership —
the guard that actually decides whether the fallback runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt
@mholzi
mholzi marked this pull request as ready for review August 23, 2026 19:31
@mholzi
mholzi merged commit 2af187c into main Aug 23, 2026
11 checks passed
@mholzi
mholzi deleted the feat/tidal-name-fallback branch August 23, 2026 19:33
mholzi added a commit that referenced this pull request Aug 23, 2026
Bumps the manifest to 4.3.1-rc2, adds the changelog entry for what landed
since the previous build, and adds the pre-release notes.

Two changes since the last tag: the Tidal name fallback (#2364) and the
start-failure banner layout fix (#2365 / #2366). The dead YouTube links
repaired in #2362 are catalogue maintenance and stay out of the user-facing
notes, as pre-release notes always do.

The notes file is force-added — `docs/` is gitignored and every previous
release-notes file was added the same way.


Claude-Session: https://claude.ai/code/session_012W9Pj7vqs7Yv31j19B4vRt

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants