Commit 3e40dec
[Mobile] Fix offline album track downloads after v1 SDK migration (#14275)
## Summary
Track downloads were failing in offline mode — the user-visible symptom
is *"clicks download, sees a track attempt, then a failure icon."* This
affects **both individual track downloads** (favoriting a track with
auto-download on, or any other path that queues a single track) **and
tracks inside an album/collection download**, because every track-job
entry point lands in the same queue and is processed by the same worker.
Two compounding issues in `downloadTrackWorker.ts`, both effectively
introduced by the v1 SDK migration (#13728):
1. **`downloadTrackAudio` skipped the v1 pre-signed stream URL.** It
built the URL via `sdk.tracks.getTrackStreamUrl()`, ignoring
`track.stream.url`. The v1 API returns a pre-signed stream URL (with
mirrors) on the track itself; `AudioPlayer` prefers it for online
streaming and only falls back to manual URL construction when
`stream.url` is missing. The offline worker skipped that working path
entirely, so it relied on the fallback for every track.
2. **`downloadTrackCoverArt` was fatal.** It threw when no artwork URI
returned 200, and because cover art runs in `all([cover, audio, json])`,
a single flaky artwork host failed the entire track download — even
though audio is the only essential payload. The collection cover art
download follows the opposite (best-effort) pattern; this PR brings the
track version in line.
Plus `downloadTrackAsync`'s `catch` block silently swallowed the error —
no logs, no Sentry breadcrumb. That's why this had been hard to
diagnose.
### Why this fixes both individual tracks and collections
Every track-job source — `requestDownloadCollectionSaga`,
`requestDownloadFavoritedCollectionSaga`,
`requestDownloadAllFavoritesSaga`, `watchSaveTrackSaga` (standalone
track favorite), `watchAddTrackToPlaylistSaga`, `syncCollectionWorker` —
dispatches `addOfflineEntries({ items: [..., { type: 'track' }, ...]
})`. The queue processor routes every `type === 'track'` job to a single
`downloadTrackWorker`, which calls the two functions patched here. So
both flows share the same broken code path.
## Changes
- `downloadTrackAudio`: prefer `track.stream.url` (with mirror
substitution), keep `getTrackStreamUrl()` + manual signing as a
last-resort fallback. Mirrors `AudioPlayer.tsx` logic.
- `downloadTrackCoverArt`: best-effort — return silently if all URIs
fail rather than throwing.
- Extract shared `buildMirrorUris(primary, mirrors)` helper used by both
audio and cover art.
- Add a `console.warn` in the catch block of `downloadTrackAsync` so
future failures are diagnosable instead of silent.
## Test plan
- [ ] Offline mode → favorite a single track with "download all
favorites" enabled. Track reaches SUCCESS, plays from local file.
- [ ] Offline mode → download an album you don't own (where you've
favorited it). All tracks reach SUCCESS, audio plays from local file.
- [ ] Offline mode → download an album where the artwork host is
degraded. Audio still downloads successfully; cover art is best-effort.
- [ ] Offline mode → download a single track via the track menu. Still
works (no regression).
- [ ] Offline mode → toggle "Download all favorites." Track downloads
succeed.
- [ ] Offline mode → play a downloaded track while offline. File-system
playback still works.
- [ ] Gated content (USDC / NFT) → confirm the manual-URL fallback path
still signs correctly when `stream.url` is unset.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 3f0b85e commit 3e40dec
1 file changed
Lines changed: 58 additions & 35 deletions
File tree
- packages/mobile/src/store/offline-downloads/sagas/offlineQueueSagas/workers
packages/mobile/src/store/offline-downloads/sagas/offlineQueueSagas/workers/downloadTrackWorker.ts
Lines changed: 58 additions & 35 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
139 | 139 | | |
140 | 140 | | |
141 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
142 | 146 | | |
143 | 147 | | |
144 | 148 | | |
145 | 149 | | |
146 | 150 | | |
147 | 151 | | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
148 | 166 | | |
149 | | - | |
| 167 | + | |
150 | 168 | | |
151 | 169 | | |
152 | 170 | | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
177 | 206 | | |
178 | 207 | | |
179 | 208 | | |
| |||
184 | 213 | | |
185 | 214 | | |
186 | 215 | | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
| 216 | + | |
196 | 217 | | |
197 | 218 | | |
198 | 219 | | |
| |||
201 | 222 | | |
202 | 223 | | |
203 | 224 | | |
204 | | - | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
205 | 228 | | |
206 | 229 | | |
207 | 230 | | |
| |||
0 commit comments