Web Audio: full player (seek/skip/repeat/shuffle/queue) + add-to-playlist / create playlist like mobile (#21) - #22
Merged
Conversation
#21) Replace the bare audio dialog on the web app with a full mobile-style player, and let users add tracks to playlists / create playlists from the web (cloud-backed, cross-device with the native app). New WebAudioPlayer (full-screen via Dialog.fullscreen, like the image and text previews) for AUDIO; video stays on Chewie. Now-playing, seek slider, play/pause, skip prev/next, rewind/forward 10s, repeat (off/one/all), shuffle, a tappable queue, add-to-playlist and download. New WebAudioController (just_audio): queue is the current folder audio files (start at the tapped track) or a playlist tracks; downloads on demand to a Blob URL then setUrl/play, revoking the previous blob on every track change and on dispose (a queue churns many blobs); repeat-one replays without re-downloading. New cloud playlist WRITE (WebPlaylistService, additive): create and add build Playlist.toJson and call the SAME shared encryptAndUpload native uses (writeBucket playlists, user-playlists/<id>.json), so native and web read each other playlists. Add-to-existing loads, appends (dedup by path), re-PUTs the single per-id object. Never deletes, never writes tombstones. Playlist-detail playback unified onto the new player. Pure logic (queue/repeat/shuffle transitions; playlist build/append/ serialize) lives in web_audio_queue.dart + web_playlist_write_logic.dart and is VM-unit-tested (17 tests incl. a Playlist.fromJson round-trip); the just_audio/blob glue and the cloud write are verified live. Encryption parity is by construction and empirical: the same shared encryptAndUpload core runs on both platforms keyed off the session, and web already decrypts native-written playlists. Concurrency is last-writer-wins per object, identical to native fire-and-forget sync. Reviewed by Gemini + the built-in advisor (Codex/Cursor/Copilot all rate-limited this session). Applied: aggressive blob revocation, cloudKey-based shuffle lookup, repeat-one replay-without-reload, _ensureBucket error propagation. analyze clean, full suite green (537), web build green. Co-Authored-By: Claude Opus 4.8 <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.
Summary
Replaces the bare audio dialog on the web app (files.fx.land/app/) with a full mobile-style audio player, and lets users add tracks to playlists / create playlists from the web (cloud-backed, cross-device with the native app). Previously web audio played in
MediaPreviewDialog(play/pause + a position label) and the web playlists page was view-only.Closes #21.
What's new
WebAudioPlayer— full-screen player (viaDialog.fullscreen, consistent with the image/text previews) replacing the dialog for AUDIO (video stays on Chewie): now-playing, seek slider, play/pause, skip prev/next, rewind/forward 10s, repeat (off/one/all), shuffle, a tappable queue, add-to-playlist, download.WebAudioController(just_audio): queue = the current folder's audio files (start at the tapped track), or a playlist's tracks. Downloads on demand → Blob URL →setUrl→ play; revokes the previous blob on every track change and on dispose (a queue churns many blobs — leaks crash mobile browsers). Repeat-one replays without re-downloading.WebPlaylistService, additive): "create playlist" and "add to playlist" buildPlaylist.toJson()and call the same sharedencryptAndUpload(writeBucket('playlists'), 'user-playlists/<id>.json', …, contentType:'application/json')native uses, so native + web read each other's playlists. "Add to existing" = load → append (dedup by path) → re-PUT the single per-id object.Decisions / safety
user-playlists/<id>.jsonvia the shared method with native-identical bucket/key/format; never deletes, never writes tombstones. Writes the canonical key in the read-merged write bucket (v8 wins).encryptAndUpload/fula_client core runs on both platforms keyed off the session, and web already decrypts native-written playlists today (WebFeatures.loadPlaylists) — so the round-trip is symmetric. Format fidelity is unit-tested (round-trip throughPlaylist.fromJson; the model's casts are null-safe).Advisor review note
Codex, Cursor, AND Copilot were all rate-limited/unavailable this session, so the cloud-write path was reviewed by Gemini (independent of Claude) + the built-in advisor rather than the usual Codex pass (the user opted into this reduced-review tier for this feature). Applied from review: aggressive blob revocation, cloudKey-based shuffle lookup, repeat-one replay-without-reload,
_ensureBucketpropagates errors, write-path guardrails above.Tests
Playlist.fromJsonround-trip (5).flutter analyzeclean on all changed files; full suite green;flutter build web --releasegreen.Mandatory live verification (interaction-heavy; deploy ≠ works)
/playlists, the playlist + track appear; "add to existing" → the track appears.downloadTrack).Known limitations (follow-up)
audio_serviceis a web no-op).Part of the mobile->web parity work.