Skip to content

feat(player): add media session controls and honor playback defaults - #411

Merged
chhoumann merged 5 commits into
masterfrom
feat/media-session
Oct 12, 2026
Merged

chhoumann merged 5 commits into
masterfrom
feat/media-session

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

PodNotes publishes the episode title, podcast, artwork, and playback position to the main window's Media Session. Play, pause, configured backward and forward skips, seek-to, fixed 30-second next-track, and previous-track timestamp capture use the player. Empty selection and plugin unload clear the session. Popouts keep the main window's single registration.

The player remembers one volume across episodes and restarts, including changes through api.volume. The Settings Default Volume slider now changes playing audio immediately. Default Playback Rate applies when a different episode loads. Reloading the shown episode preserves its live speed, including restored first play, error retry, source replacement, and replay after the episode ends. A live speed change never overwrites the saved default.

Fixes #350. Android's portion is closed as unsupported by Obsidian's WebView, with sources below.

Verification

  • Node 22.23.2. All six checks pass after merging current origin/master: npm run lint, npm run format:check, npm run typecheck, npm run build, npm run test, and npm run docs:build. 1,712 tests across 98 files; 18 docs pages and 112 legacy fragments pass.
  • npm run test:e2e passes all 72 tests across 11 files in real Obsidian 1.13.7 on Linux. The first run had a checkpoint timeout and a mismatch between the reused vault's 7/11-second skip settings and the test's expected 15/15. The final fresh-vault run passes all 72 with recorded command exit 0. The report retains the earlier fixture failures and terminal-wrapper exit 143.
  • F1/F2 tests failed before the fix. All nine targeted mutations are detected, including every-load speed reset, missing volume persistence, per-load volume reset, Settings volume, and import rollback. Two independent fix reviews found no production correctness defect.
  • The live player keeps 2.3x and 50% through restored first play. API volume saves to disk, the Settings slider changes playing audio immediately, and reload restores the saved volume. Unfixed first play, fixed before Play, and fixed after Play. The final proof uses Obsidian 1.13.7, a generated 60-second local WAV, and the exact integrated build. No runtime errors. Correction report and reproducible evidence.
  • Media Session metadata, all seven actions, timestamp capture, popout ownership, reload, unload, and the secret check passed independent V-411 verification. Native Linux control delivery was verified through MPRIS. The adapter and Media Session wiring are unchanged by this fix. Earlier platform evidence is in the archive; its volume and speed conclusions are superseded by this correction.
  • npm run size passes at 284,862 raw bytes and 90,640 gzip bytes, 807 gzip bytes above the brief's 89,833-byte baseline, including upstream fix: make podcast name lookups exact and safe #413.

Android

Obsidian's Android WebView does not support Media Session. Android 14 emulator, Obsidian mobile 1.14.4 (versionCode 389), Android System WebView 113.0.5672.136. Inside Obsidian, both navigator.mediaSession and MediaMetadata are undefined in a secure context. npm run --silent mobile:verify passed before the V-411 volume and speed correction. The Android player passed in-app play/pause and configured skips on that build. The Media Session adapter is unchanged by the correction; Android was not rerun for it.

This is a platform restriction. MDN browser-compat-data records webview_android.version_added: false; Navigator and MediaMetadata data agree. Chrome Platform Status lists Chrome Android 57 and desktop 73 with no WebView milestone. Chromium source explicitly sets kDisableMediaSessionAPI, explaining that WebView has no metadata/control UI; issue 40611412 tracks WebView support. No supported newer WebView was found, so an APK update cannot remove this intentional restriction and was not attempted. No native bridge is included.

A separate plain <audio> played the local 60-second, 8 kHz mono WAV while locked. Android recorded Obsidian as an audio client but no md.obsidian media session or notification. Playback probes and Android service dumps are the primary evidence; the screenshots show no card on the actual lock screen or notification shade. The shade screenshot has no simultaneous playback reading.

iOS: expected, not verified

This Linux host has no iOS simulator. MDN Navigator compatibility data records Safari/iOS 15 support for navigator.mediaSession and mirrors it to webview_ios. WebKit enables MediaSession by default and builds it on Cocoa except watchOS. WKWebView API availability is expected on iOS 15+, but Obsidian's background playback, lock-screen controls and headset behavior remain unverified.

Release impact

No schema, API signature, or migration change. Saved volume and its documented API persistence remain compatible. Settings import applies volume immediately; imported default speed applies when a different episode loads. Remappable media actions and headset-capture feedback remain follow-ups. Physical media keys and headsets remain unverified; Linux native controls were verified through MPRIS. The OS does not route controls before the first playback after startup, as on master.

Docs at release

No docs files change in this PR. Apply these updates when the feature is released:

  • mobile.md. Document all seven actions. State that Android Obsidian has no Media Session controls or previous-track capture because its WebView disables the API. Label iOS lock-screen and headset behavior expected, not verified.
  • api.md. Retain the existing statement that setting volume updates saved Default Volume. Explain that live speed lasts until a different episode loads and never overwrites Default Playback Rate.
  • settings.md, Playback table. Default Volume changes live audio and remains the one remembered volume. Default Playback Rate applies at startup and when a different episode loads; same-episode reloads preserve live speed.
  • sync.md. Keep volume as a saved preference. Explain that current speed remains unsaved, while the saved default applies to different episodes.
  • import_export.md, Import your settings. Imported volume applies immediately. Imported default speed applies on the next different episode, leaving the shown episode's speed unchanged.

Note

Add Media Session controls and honor playback volume/speed defaults

  • Adds bindMediaSession in mediaSession.ts. It registers OS/lock-screen controls for play, pause, seeking, skipping, and timestamp capture, and reports episode metadata, playback state, and position. Unsupported or throwing browser calls are isolated and do not reach player state subscribers.
  • Reloads of the currently shown episode keep its live playback speed; a different episode starts at the normalized default speed (PlayerController.load).
  • The Default Volume slider now routes through PlayerController.setVolume instead of writing settings directly (PodNotesSettingsTab.ts).
  • Importing default volume applies immediately; importing default playback rate affects the next episode, not the currently playing one.
  • Behavioral Change: Media Session registration moves from plugin load to the player state lifecycle. Plugin unload clears handlers, metadata, playback state, and position state via the binding's store unsubscriber in main.ts.

Macroscope summarized 2076d8e.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Deploying podnotes with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2076d8e
Status: ✅  Deploy successful!
Preview URL: https://d99f26b2.podnotes.pages.dev
Branch Preview URL: https://feat-media-session.podnotes.pages.dev

View logs

@chhoumann
chhoumann merged commit 33e0e62 into master Oct 12, 2026
11 checks passed
@chhoumann
chhoumann deleted the feat/media-session branch October 12, 2026 20:04
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.

Provide lock-screen metadata and headset seek controls through the Media Session API

1 participant