Skip to content

Spike: macOS now-playing media backend (MediaRemote equivalent of MPRIS) #56

Description

@jonocodes

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

Activity

  1. added
    enhancementNew feature or request
    spikeDesign-doc spike work (input injection, focus watcher, etc.)
    on Jul 27, 2026
  2. jonocodes commented on Aug 7, 2026

    @jonocodes
    OwnerAuthor

    Off-Mac spike results

    The off-Mac research is complete. Full report: docs/research/issue-56-macos-nowplaying.md.

    • MPNowPlayingInfoCenter and MPRemoteCommandCenter are publish/receive-side APIs, not system-wide read APIs.
    • Direct unentitled MediaRemote reads are blocked since macOS 15.4; commands remain available but are insufficient without read access.
    • The currently viable route is shelling out to ungive/mediaremote-adapter or nowplaying-cli, whose shared /usr/bin/perl shim works on Sequoia 15.7 and Tahoe 26.3.
    • The existing MprisBackend seam and now-playing UI support the macOS shape cleanly: one row or zero rows, with no 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.

    The only remaining acceptance criterion is empirical verification on a live Mac: confirm get, stream, and transport commands on a stated macOS version without a private entitlement. The workaround is undocumented and may disappear, so this should remain a prerequisite before creating the implementation ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestspikeDesign-doc spike work (input injection, focus watcher, etc.)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions