Add per-site background audio playback - #501
Open
theoden8 wants to merge 6 commits into
Open
Conversation
theoden8
force-pushed
the
claude/background-audio-playback-mulsg3
branch
from
July 20, 2026 12:39
9ad3d17 to
786ea12
Compare
Streaming players stall when lifecycle pauses freeze JS; iOS also suspends the app without a .playback session. Toggle exempts the site from both pauses and keeps CI-testable via lifecycle injection + beaconing fixture. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
macOS CI's ad-hoc-entitled test app gets EPERM reading repo files at runtime. Drift test pins the mirror to the authoritative .html. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
WPE in the CI container (no GStreamer plugin sets or audio sink) crash-loops the web process on media-pipeline init, so no beacon ever arrived. The audio element is now built in JS and skipped under ?noMedia=1; the lifecycle test asserts JS-timer liveness only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
Lock the settings-save round-trip + effective getter so an enabled toggle can't silently regress to jsPause=true after restart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
…dio (BGAUDIO-006) Lightweight exemption kept audio best-effort and showed no controls. A mediaPlayback foreground service with a MediaStyle notification now keeps playback alive when backgrounded and gives lockscreen/notification play-pause, driven by a page-JS media-session bridge. Media playback is a Play-accepted FGS type, unlike SPECIAL_USE. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
Deliberate root-only field (media notification is a root-site concern, not a privacy posture) — PLUMBING, like backForwardGestures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
theoden8
force-pushed
the
claude/background-audio-playback-mulsg3
branch
from
July 21, 2026 14:34
6146803 to
776ded6
Compare
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.
Answers the "can you play audio in the background?" user question with a per-site Background audio toggle (spec:
openspec/specs/background-audio/spec.md, BGAUDIO-001..005).Why audio stopped
pauseTimers()alert-deadlock hack — it freezes the page's JS thread, so MSE/streaming players (YouTube Music, radio sites) stall within seconds even though the decoder itself survives.pauseForAppLifecycle()freezes JS timers process-globally on Android, and iOS suspends the whole app without a.playbackaudio session.What the toggle does
pauseWebView()early-returns for background-audio sites (mirrors the notification-site exemption; archive-tier sites stay paused per ARCH-006).AppLifecycleEngine.backgroundPlanskips the app-background JS pause when any loaded site has the toggle — Android'spauseTimers()is process-global, so pausing the active site would starve a backgrounded player. Resume mirrors; state capture is not gated. Thepausedbranch now logs a non-sensitiveApp background: jsPause=<bool> capture=<bool>decision line.audioadded toUIBackgroundModes;BackgroundTaskPluginswitches the sharedAVAudioSessionto.playbackwhile a background-audio site is loaded (back to.ambientotherwise — silent-switch behavior unchanged for ordinary sites). Category only, no forced activation, so no audio-focus stealing while idle.notificationeviction tier (no formal-model change —retention.tlaabstracts the retained flag).WebViewModel.toJson(conditional key, archive byte-identity preserved), the settings-QR share set, and the per-site settings UI; strings added to all 68 ARB locales.No Android foreground service (same Play-review posture as notifications): audio plays while the process lives; the OS's reluctance to kill audio-playing processes plus the retention tier mitigate the rest.
Testing background mode in CI
Real OS backgrounding can't be driven from an integration test, but the whole path hangs off
didChangeAppLifecycleState, sointegration_test/background_audio_lifecycle_test.dartinjects lifecycle states viatester.binding.handleAppLifecycleStateChangedand observes two things without touching private app state:integration_test/fixtures/background_audio.html) beaconsGET /beacon?ticks=N&audio=<playState>to the test's loopback server every 250 ms — beacons continuing through the injected background window, with monotonically increasing ticks, prove the same live page kept running. Runs on the existing Linux (WPE) and macOS CI jobs.The "JS actually freezes for non-exempt sites" direction is only true on Android/iOS hardware (Linux/macOS plugins implement no
pauseTimers()), so that matrix lives intest/app_lifecycle_engine_test.dartinstead.Verification
flutter analyze: 0 errorsflutter test: 1858 passed (incl. new engine + pause-contract cases)npm run test:js: 284 passed (l10n key parity + CLD3 language identity over the 67 translated locales)npx openspec validate --no-interactive --all: 50 passed🤖 Generated with Claude Code
https://claude.ai/code/session_01EizMhpW94e7q2fYVQW8JQC
Generated by Claude Code