Skip to content

Keep what the media session knew when the provider cannot help - #21

Closed
revtex wants to merge 1 commit into
feat/spotify-artist-genresfrom
feat/smtc-metadata-floor
Closed

Keep what the media session knew when the provider cannot help#21
revtex wants to merge 1 commit into
feat/spotify-artist-genresfrom
feat/smtc-metadata-floor

Conversation

@revtex

Copy link
Copy Markdown
Owner

Stacked on #20 — based on feat/spotify-artist-genres, so it targets that branch rather than main. Merge #20 first and this retargets cleanly. Say the word if you'd rather I rebase it onto main instead.

Implements API-first with the media session underneath, as agreed.

Why not literally "client first, API as fallback"

Worth stating because it sounds equivalent and isn't. The two sources have asymmetric coverage:

  • SMTC supplies: title, artist, album, album artist, track number
  • SMTC never supplies: year, release date, genre, album track count, disc, copyright, high-res art

So the API is the only source for half the tag set. A literal client-first rule would still call it on every track — because year alone is always missing — and save nothing. The direction that's actually worth having is the one you picked: the provider wins wherever it answers, and what the client already knew fills the gaps it leaves.

The gap this closes

It was real and silent. When the match guard rejected all four attempts — or no provider was configured, or Spotify was down — the file was written bare. Meanwhile the media session had already reported artist, title, album, album artist and position for that exact track, with more certainty than any lookup can offer, and every bit of it was discarded.

This is the same class of failure as the 9Pm (Till I Come) bug: not that the answer was wrong, but that a partial failure degraded all the way to nothing instead of to the next-best thing.

What changed

SmtcSnapshot gains AlbumArtist and TrackNumber, both optional — so the window-title path (which supplies neither) and every existing construction site are untouched. Two mapping rules:

  • A track number of 0 maps to null. Spotify numbers from 1, so zero is "not reported", not a zeroth track.
  • A blank album artist maps to null, not to an album credited to the empty string — which would otherwise beat a provider that did know, given the change below.

Both mappers now fill rather than clear. This is the load-bearing half and a deliberate behaviour change: Album, AlbumArtists and AlbumPosition are assigned only when the provider actually has a value, in SpotifyTrackMapper and LastFmTrackMapper alike. The SetArtistFromApi / SetTitleFromApi idiom already worked this way for artist and title; this extends it to the three fields SMTC can now supply.

One test asserted the opposite

ApplyAlbum_WithAnEmptyResponse_ReturnsExpectedTrack pinned that an empty album object wrote "" and [] over whatever was there. I updated it rather than working around it, and left the reasoning in the test: that behaviour was harmless while the only other source was a window title, which carries no album at all. It stopped being harmless the moment the media session started carrying one, because a provider that couldn't answer would erase what the client had said for certain.

Deliberately not included

Cover art from the SMTC thumbnail. It's much smaller than the API's 640×640, and anything that could win over the API's image is a downgrade people notice in a library. Defensible someday as a last resort for a file that would otherwise have no art at all; left out until there's a reason to want it. Flagging it since it's the one piece of SMTC data now going unused.

Genre isn't here — the media session has none to give (count=0, probed live). That's #20's job.

Verification

dotnet build clean, dotnet format --verify-no-changes clean, 1024 tests green, 14 new:

  • 6 over the SMTC mapping: album artist and track number carried; 0 and null positions not reported; blank album artist not reported; trimming; snapshots without the optional fields unchanged.
  • 3 over the mappers: empty response leaves fields alone; detected values survive; a provider that knows better still wins.
  • 3 end-to-end on the provider: nothing matches → detected metadata intact; API fault → intact; partial answer → gaps filled from the floor.

Worth checking on a real recording: sign out of Spotify (or set the provider to None) and confirm files still get album, album artist and track number rather than just artist/title.

🤖 Generated with Claude Code

Precedence is API first, media session underneath — not the reverse, and not
"client first with the API as fallback", which sounds equivalent and is not.
The two sources have asymmetric coverage. SMTC supplies title, artist, album,
album artist and track number; it never supplies year, release date, genre,
album track count, disc or copyright. The API is the only source for half the
tag set, so a literal client-first rule would call it on every track anyway
and save nothing. The direction worth having is the other one: the provider
wins wherever it answers, and what the client already knew fills the gaps.
The gap that closes was real and silent. When the match guard rejected every
attempt — or no provider was configured, or Spotify was down — the file was
written bare, while the media session had already reported artist, title,
album, album artist and position for that exact track, with more certainty
than any lookup can offer. All of it was discarded.
`SmtcSnapshot` gains `AlbumArtist` and `TrackNumber`, both optional so the
window-title path and every existing construction site are unchanged. A track
number of 0 maps to null, since Spotify numbers from 1 and zero means "not
reported"; a blank album artist maps to null rather than to an album credited
to the empty string, which would otherwise beat a provider that did know.
The load-bearing half is that both mappers now fill rather than clear. `Album`,
`AlbumArtists` and `AlbumPosition` are assigned only when the provider has a
value. The `SetArtistFromApi` idiom already worked this way for artist and
title; this extends it to the three fields the media session can now supply.
One existing test asserted the opposite and was updated rather than worked
around: `ApplyAlbum_WithAnEmptyResponse` pinned that an empty album object
wrote "" and [] over whatever was there. Harmless while the only other source
was a window title carrying no album; not harmless once the media session
carries one.
Cover art is deliberately not taken from SMTC — its thumbnail is far smaller
than the API's 640x640, and anything that could win over the API's image is a
downgrade users notice.
1024 tests green, 14 new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@revtex
revtex deleted the branch feat/spotify-artist-genresAugust 14, 2026 05:52
@revtexrevtex closed this Aug 14, 2026
Sign up for freeto 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.

1 participant

@revtex