Skip to content

Web Audio: full player (seek/skip/repeat/shuffle/queue) + add-to-playlist / create playlist like mobile (#21) - #22

Merged
ehsan6sha merged 1 commit into
mainfrom
feat/web-audio-player
Jun 14, 2026
Merged

Web Audio: full player (seek/skip/repeat/shuffle/queue) + add-to-playlist / create playlist like mobile (#21)#22
ehsan6sha merged 1 commit into
mainfrom
feat/web-audio-player

Conversation

@ehsan6sha

Copy link
Copy Markdown
Member

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 (via Dialog.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.
  • Cloud playlist WRITE (WebPlaylistService, additive): "create playlist" and "add to playlist" build Playlist.toJson() and call the same shared encryptAndUpload(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.
  • Playlist-detail playback unified onto the new player (no more divergent audio UIs).

Decisions / safety

  • Dialog, not a route (built-in advisor): a route only pays off with a persistent mini-player (not in scope); without it a route just adds reload-breakage. Consistent with Added NFT Generation #3/Web Documents: inline text/code viewer (xml, ts, js, html, …) like mobile, not download-only #19.
  • Write path is minimal-blast-radius by construction: only ever PUTs one user-playlists/<id>.json via 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).
  • Encryption parity is by construction + empirical: the same shared 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 through Playlist.fromJson; the model's casts are null-safe).
  • Concurrency: add-to-existing is last-writer-wins per playlist object — identical to native's fire-and-forget sync (no ETag in the design). Data-loss-only on one object, never corruption.

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, _ensureBucket propagates errors, write-path guardrails above.

Tests

  • 17 unit tests: queue/repeat/shuffle transitions (12) and playlist-write transforms incl. a Playlist.fromJson round-trip (5).
  • The just_audio/blob playback glue and the cloud write are verified live (no VM coverage by design).
  • flutter analyze clean on all changed files; full suite green; flutter build web --release green.

Mandatory live verification (interaction-heavy; deploy ≠ works)

  • Audio player: open an audio file → seek, play/pause, skip prev/next across the folder, rewind/forward, repeat, shuffle.
  • Add-to-playlist: "create new" → reopen /playlists, the playlist + track appear; "add to existing" → the track appears.
  • Cross-platform: open a web-created playlist in the native app — it reads.
  • Leading-slash round-trip: add a bucket track to a playlist, then play it from the playlist (confirms the stored key resolves through downloadTrack).

Known limitations (follow-up)

  • No next-track prefetch (a brief gap between tracks); no mini-player / background playback (browser tab must stay active — audio_service is a web no-op).

Part of the mobile->web parity work.

#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>
@ehsan6sha
ehsan6sha merged commit d8bc23d into main Jun 14, 2026
1 check passed
@ehsan6sha
ehsan6sha deleted the feat/web-audio-player branch June 14, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web Audio: full player (seek, skip, repeat, shuffle, queue) + add-to-playlist / create playlist like mobile

1 participant