Parent
Sibling to #46 (Global MPRIS media controls — the Linux feature this mirrors), and part of the macOS platform-backend revisit groundwork (#24). Depends on nothing; feasibility is the open question, hence a spike.
Why file this now
The MPRIS media browser (#46) shipped behind a clean seam: the MprisBackend Protocol in daemon/deckd/mpris.py (row_ids(), read_state(row_id) -> MediaState | None, send_command(row_id, command)), with DbusMprisBackend (real D-Bus) and FakeMprisBackend (tests) as the two implementations. The whole browser — the mediabrowser widget, the chrome view, the per-row transport, the media_command dispatch — is written against that Protocol and knows nothing about D-Bus. A macOS "now playing" backend is, architecturally, just a third implementation of the same three methods. This ticket sizes whether that's actually feasible on modern macOS.
Research result
The off-Mac investigation is complete. See docs/research/issue-56-macos-nowplaying.md.
MPNowPlayingInfoCenter and MPRemoteCommandCenter are publish/receive-side APIs, not system-wide read APIs. The correct target is private MediaRemote.framework.
- Direct unentitled
MediaRemote reads are blocked since macOS 15.4. Transport commands remain available, but control without read access is insufficient.
- The currently viable route is shelling out to
ungive/mediaremote-adapter or nowplaying-cli, whose shared /usr/bin/perl shim is reported working on Sequoia 15.7 and Tahoe 26.3.
- The existing
MprisBackend seam and now-playing UI support one row or zero rows without client changes.
- AppleScript is a narrower per-app fallback for Music/Spotify, not a system-wide replacement.
Recommendation
Build behind the existing seam, keep the helper swappable and optional, and degrade cleanly if Apple closes the workaround. Do not make this undocumented path load-bearing.
Remaining Mac-only verification
Before creating an implementation ticket, verify on a live Mac and record the exact macOS version:
nowplaying-cli get or the adapter equivalent returns the fields deckd renders.
stream produces usable updates.
- Play/pause/next/previous work without a private entitlement.
The workaround is undocumented and may disappear, so this remains the sole open acceptance criterion.
Out of scope
Parent
Sibling to #46 (Global MPRIS media controls — the Linux feature this mirrors), and part of the macOS platform-backend revisit groundwork (#24). Depends on nothing; feasibility is the open question, hence a spike.
Why file this now
The MPRIS media browser (#46) shipped behind a clean seam: the
MprisBackendProtocol indaemon/deckd/mpris.py(row_ids(),read_state(row_id) -> MediaState | None,send_command(row_id, command)), withDbusMprisBackend(real D-Bus) andFakeMprisBackend(tests) as the two implementations. The whole browser — themediabrowserwidget, the chrome view, the per-row transport, themedia_commanddispatch — is written against that Protocol and knows nothing about D-Bus. A macOS "now playing" backend is, architecturally, just a third implementation of the same three methods. This ticket sizes whether that's actually feasible on modern macOS.Research result
The off-Mac investigation is complete. See
docs/research/issue-56-macos-nowplaying.md.MPNowPlayingInfoCenterandMPRemoteCommandCenterare publish/receive-side APIs, not system-wide read APIs. The correct target is privateMediaRemote.framework.MediaRemotereads are blocked since macOS 15.4. Transport commands remain available, but control without read access is insufficient.ungive/mediaremote-adapterornowplaying-cli, whose shared/usr/bin/perlshim is reported working on Sequoia 15.7 and Tahoe 26.3.MprisBackendseam and now-playing UI support one row or zero rows without client changes.Recommendation
Build behind the existing seam, keep the helper swappable and optional, and degrade cleanly if Apple closes the workaround. Do not make this undocumented path load-bearing.
Remaining Mac-only verification
Before creating an implementation ticket, verify on a live Mac and record the exact macOS version:
nowplaying-cli getor the adapter equivalent returns the fields deckd renders.streamproduces usable updates.The workaround is undocumented and may disappear, so this remains the sole open acceptance criterion.
Out of scope
MacNowPlayingBackend— that's a follow-up ticket after live verification.GlobalSystemMediaTransportControlsSessionManager/ WinRT) — that's the natural sibling for Investigation: Windows platform backend (focus, injection, power sync) #32, filed separately if wanted.