Phone remote: media controls (now-playing, transport, album art)#42
Merged
Conversation
Adds a Now Playing tab to the phone remote: album art, title/artist, a scrubber, and play/pause/skip/seek. Bridges onto the launcher's existing now-playing stack (NowPlayingHub / MediaSessionReader) — the same media session that drives the on-TV header mini-player — so it works with any media app and across the device switcher, with no new permissions. - RemoteMedia: stateJson (now-playing as JSON), command (transport on the main thread), artPng. Art prefers the in-memory session bitmap; otherwise resolves the metadata art URI — content://file:// via ContentResolver (device-local, the phone can't fetch it), http(s) via a bounded fetch — downscaled and relayed. artVersion lets the phone cache the cover per-track. - NowPlayingHub/MediaSessionReader: add seek() (transportControls.seekTo) for the scrubber. - RemoteRoutes: GET /remote/nowplaying (authed), GET /remote/art (open like icons), POST /remote/media (authed; 409 when nothing's playing, 400 on bad action). - RemoteHtml: a Media tab with the now-playing card; polls only while visible and interpolates position between polls for a smooth scrubber. - RemoteMediaTest covers the pure serializer (active/paused/null, URI-art, artVersion stability). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The media controls used Unicode media symbols (▶ ⏸ ⏮ ⏭, U+25B6/23F8/23EE/23ED), which iOS Safari renders as colour emoji rather than glyphs. Replace them with inline monochrome SVG (fill=currentColor) for the prev/play-pause/next buttons — crisp and identical across platforms. Verified the rendered controls in a mobile viewport. 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.
Requested by a remote user: media controls from the phone. Adds a Now Playing tab — album art, title/artist, a scrubber, and play/pause/skip/seek — bridged onto the launcher's existing now-playing stack (
NowPlayingHub/MediaSessionReader, the same session that drives the on-TV header mini-player). Works with any media app, across the device switcher, with no new permissions (rides the notification-listener access already held).Backend
RemoteMedia—stateJson(now-playing → JSON),command(transport, dispatched on the main thread),artPng.content:///file://viaContentResolver(device-local, the phone can't fetch it itself),http(s)://via a bounded fetch — downscaled and relayed.artVersionlets the phone cache the cover and refetch only on track change.seek()added toNowPlayingHub/MediaSessionReaderfor the scrubber.GET /remote/nowplaying(authed),GET /remote/art(open, like icons),POST /remote/media(authed; 409 when idle, 400 on bad action).UI
A Media tab that polls only while visible and interpolates position between polls so the scrubber moves smoothly without hammering. Pauses polling when the page is backgrounded.
Verification (on the Portal+, against a live session)
There was real audio playing (Alexa/Falcon → Hamilton), so I tested the live path:
nowplayingreturned full metadata;mediatoggledplayingtrue↔false;artreturned a real 600×600 PNG — and because it exceeded the in-memory bitmap cap, it came through the URI-resolution path, confirming that case end-to-end. Idle → graceful (active:false, 409, 404); bad action → 400. (Left their playback as found.)RemoteMediaTest(pure serializer) + the rest of the remote suite pass.🤖 Generated with Claude Code