Repository navigation
feat(api): stream a track's stems as one zip - #1060
Merged
Merged
Conversation
GET /v1/tracks/{id}/stems/download streams every stem the requester may
download, plus the parent with include_parent=true, as an uncompressed zip.
The zip length is computed up front so the browser shows real progress, and
there is no job, queue or disk involved. Replaces the archiver service.
A node that drops or stalls mid-file is resumed on the next mirror with a
Range request. Files that can't be downloaded are left out. Downloads are
capped per pod and per user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dylanjeffers
added a commit
that referenced
this pull request
Oct 23, 2026
Follow-up to #1060, found validating it in prod. Stems indexed with no `orig_filename` often have the upload's filename as their title (`" 🍕DRUMS.wav"`). The zip's fallback took `createFilename`'s `title + ".mp3"`, stripped the `.mp3`, and added the extension from the blob's content type. Andrew Lux's "Alone" zip came out with `DRUMS.wav.wav`, `LEADS.wav.wav` and so on, 11 of 12 entries. Now a trailing audio extension in the title is replaced instead of kept. Tests cover the emoji/whitespace title, case, a different content type, and a title with a dot but no extension. Not changed here: the single-file `/tracks/{id}/download` names the same files `DRUMS.wav.mp3` even though it serves the WAV, because `createFilename` assumes the transcode whenever `orig_filename` is empty. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
GET /v1/tracks/{id}/stems/downloadstreams every stem the requester may download, plus the parent withinclude_parent=true, as one zip. It is the replacement for the archiver service (BullMQ + Redis + disk). The clients move over in AudiusProject/apps#14703, which depends on this.Content-Lengthand the browser's download manager shows progress./tracks/{id}/download: gated stems need access, and an owner or manager signature gets their own non-downloadable files viaownerDownloadLink.Range: bytes=<offset>-.X-Accel-Buffering: noso ingress-nginx doesn't spool large zips to disk.inspectCidis split out ofinspectTrackand now has a 10s timeout. It had none.Testing
v1_track_stems_download_test.go, against a fake content node: exact length vs. bytes written with CRCs verified byarchive/zip, resuming on a mirror after the primary drops mid-file, all mirrors failing, zip64 offsets past 4 GiB, filename dedupe and extension fallback, the handler end to end (parent + stems, without parent, missing blob, not found), and an owner getting a non-downloadable stem that a stranger doesn't.writeStemsZipagainst prod content nodes with links signed by api.audius.co: 3 of Andrew Lux's "Alone" stems streamed into a 155 MB zip in 19s, length matched the precomputed value exactly, all CRCs ok. A Range request against a real node (through its 302) also worked../api/...suite:TestFeedScheduledReleasesalso fails on main.TestSearchneeds Elasticsearch locally and wasn't run.Rollout
Additive, and no client calls it until the apps PR ships. Leave the archiver and the
/archive/*proxy running until older mobile store builds and desktop versions stop using them.🤖 Generated with Claude Code