refactor: move auto-download to experimental section (#611)

* feat: add offline mode foundation with auto-download liked tracks

- add storage.ts with Cache API + IndexedDB for offline audio storage
- add GET /audio/{file_id}/url endpoint returning direct R2 URLs for caching
- add auto_download_liked preference (stored in localStorage, device-specific)
- add settings toggle that bulk-downloads all liked tracks when enabled
- auto-download new tracks when liking (if preference enabled)
- Player component checks for cached audio before streaming
- fix TLFM reauth notice to show correct message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: require auth for audio URL endpoint

adds authentication to GET /audio/{file_id}/url to prevent
unauthenticated enumeration of audio URLs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: improve IndexedDB robustness

- close database connections after each operation (fixes connection leak)
- deduplicate concurrent downloads using in-flight promise map
- verify cache entry exists in isDownloaded() and clean up stale metadata

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: move auto-download toggle to experimental section

keeps the feature available but clearly marked as experimental

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

authored by zzstoatzz.io Claude Opus 4.5 and committed by GitHub e8fb7294 a8d74c40

Changed files
+20
frontend
src
routes
settings
+20
frontend/src/routes/settings/+page.svelte
··· 734 734 {/if} 735 735 </div> 736 736 </section> 737 + 738 + <section class="settings-section"> 739 + <h2>experimental</h2> 740 + <div class="settings-card"> 741 + <div class="setting-row"> 742 + <div class="setting-info"> 743 + <h3>auto-download liked</h3> 744 + <p>automatically download tracks for offline playback when you like them</p> 745 + </div> 746 + <label class="toggle-switch"> 747 + <input 748 + type="checkbox" 749 + checked={autoDownloadLiked} 750 + onchange={(e) => handleAutoDownloadToggle((e.target as HTMLInputElement).checked)} 751 + /> 752 + <span class="toggle-slider"></span> 753 + </label> 754 + </div> 755 + </div> 756 + </section> 737 757 </main> 738 758 {/if} 739 759