fix(api): stop signing media links for non-streamable tracks - #1024
Conversation
#1023 guarded the stream and download endpoints, but that only closed two routes. `dbv1.TracksKeyed` builds the Stream/Download/Preview media links with no reference to `is_streamable`, so `/v1/tracks/{id}` and `/v1/resolve` keep handing out a signed content-node URL for tracks whose owner is deactivated or delisted. Verified against the delisted account from the original report: the signed URL from the track response served the full mp3. Hoist the is_streamable expression above the media-link block and gate all three on it, matching the empty-cid case directly above, which already leaves the link nil and lets the endpoints report the track as unavailable. Preview is included because a preview clip is still the artist's audio. Signatures expire after 48h on the content node, so URLs already handed out age out on their own - the leak is that the API mints a fresh one per request. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…der (#14571) Follow-up to #14570, which gated the web and mobile track pages on `is_streamable`. The embed player is its own app and was missed — it still rendered the full card (title, artist, artwork, play button) for a track whose owner deactivated their own account or was delisted by the trusted notifier. AudiusProject/api#1023 already made `/v1/tracks/{id}/stream` 404, so the player couldn't actually play these. It just showed the metadata and then failed silently on press. ## Change Route non-streamable tracks into the existing not-available treatment (the same path a 404 takes), with its own copy rather than reusing the deleted-by-creator string — the same flag covers a self deactivation and a delisted account, and we shouldn't tell listeners the creator removed a track when moderation suppressed it. Wording matches the web tombstone from #14570. The check is an explicit `=== false`, matching `isTrackUnavailable` in common: an absent field must not read as unavailable. (The embed depends on `@audius/sdk` rather than `@audius/common`, so the helper isn't importable here.) ## Verification Ran against prod data using `audius.co/rehoxx/just-for-tonight-wmellark-hoonds` (`ENxw4`), the track from the original report: | | | |---|---| | `card` | "This track can no longer be streamed on Audius." | | `compact` | same | | `tiny` | "Track Unavailable" | Both routes covered — hash id (`getTrack`) and permalink (`getBulkTracks`). A streamable trending track still renders normally with artwork and play button. `vite build`, `eslint`, and `jest` all pass. ## Note The remaining gap is server-side: `/v1/tracks/{id}` still returns a signed content-node URL for these tracks, which AudiusProject/api#1024 fixes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
raymondjacobson
left a comment
There was a problem hiding this comment.
I think this looks good but want to confirm only users get treatment here because tracks have their own handling elsewhere?
Not quite — it covers both, because #1023 already made The separate "deleted by artist" handling you're thinking of is client-side and keys off So the only behavior change for deleted tracks is |
Follow-up to #1023.
#1023 guarded
/v1/tracks/{id}/streamand/download, but that only closed two routes.dbv1.TracksKeyedbuilds theStream/Download/Previewmedia links with no reference tois_streamable, so/v1/tracks/{id}and/v1/resolvestill hand out a working signed content-node URL for tracks whose owner is deactivated or delisted by the trusted notifier.Verified against the delisted account from the original report (
audius.co/rehoxx/just-for-tonight-wmellark-hoonds, trackENxw4):/streamcorrectly 404s, but thestream.urlin the track response returned206 audio/mpegwith the full file.Change
Hoist the
is_streamableexpression above the media-link block and gate all three links on it. This matches the empty-cid case directly above, which already leaves the link nil so the endpoints report the track as unavailable — same intent, and now the two reasons sit under one comment.previewis included because a preview clip is still the artist's audio.Notes
TestGetTrack_StreamableKeepsMediaLinks.serve_blob.go:544), so URLs already in the wild age out on their own. The leak was that the API minted a fresh one on every request.Test plan
TestGetTrack_NonStreamableOmitsMediaLinks— deactivated owner and deleted track both getstream/download/preview= nullTestGetTrack_StreamableKeepsMediaLinks— active owner still gets all three, signedgo test ./api/...passes🤖 Generated with Claude Code