MediaRemote Adapter

repository·master·Indexed 42 days ago

https://github.com/ungive/mediaremote-adapter

A macOS tool that allows applications to access now playing information and control media playback using the MediaRemote framework. It uses a Perl script to dynamically load a custom helper framework, bypassing the need for specific app entitlements and overcoming restrictions introduced in macOS 15.4. It provides a CLI interface via mediaremote-adapter.pl to stream real-time updates, fetch metadata as JSON, and send playback commands like play, pause, seek, shuffle, and repeat.

Tokens
3.3K
Snippets
10
Records
16
Agent score
5%

What's inside MediaRemote Adapter

  1. How the MediaRemote Adapter works

    master

    The adapter works by leveraging the fact that processes with a bundle identifier starting with com.apple. are granted permission to access the MediaRemote framework.

    Because the adapter uses a mechanism that identifies as a com.apple.* bundle (similar to how /usr/bin/perl identifies as com.apple.perl), it can bypass the restrictions introduced in macOS 15.4 that prevent direct loading of the MediaRemote framework from within standard third-party applications.

  2. Why use MediaRemote Adapter instead of MediaRemote directly

    master

    Starting with macOS 15.4, the MediaRemote framework has become non-functional when loaded directly from within a third-party application.

    This project provides a functional alternative to allow developers to:

    • Read 'now playing' information.
    • Control media playback on the device.
    • Access song artwork (which is not possible via osascript / AppleScript solutions).
    • Receive real-time updates (which is not possible via osascript / AppleScript solutions).
  3. Build MediaRemote Adapter from source

    master

    You can build the MediaRemoteAdapter.framework and the MediaRemoteAdapterTestClient using CMake. The resulting binaries support x86_64 and arm64 architectures.

    $ git clone https://github.com/ungive/mediaremote-adapter.git
    $ cd mediaremote-adapter
    $ mkdir build && cd build
    $ cmake ..
    $ cmake --build .
    $ cd ..
    
    # To run a stream immediately after building:
    $ FRAMEWORK_PATH=$(realpath ./build/MediaRemoteAdapter.framework)
    $ /usr/bin/perl ./bin/mediaremote-adapter.pl "$FRAMEWORK_PATH" stream
    
    # To run a test immediately after building:
    $ HELPER_PATH=$(realpath ./build/MediaRemoteAdapterTestClient)
    $ /usr/bin/perl ./bin/mediaremote-adapter.pl "$FRAMEWORK_PATH" "$HELPER_PATH" test
  4. Use the MediaRemote Adapter in your application

    master

    The MediaRemote Adapter provides a Perl script (mediaremote-adapter.pl) that acts as a CLI interface to read now playing information and control media players.

    To use it, you must bundle the mediaremote-adapter.pl script and the MediaRemoteAdapter.framework with your application. The framework should be passed as a script argument but must not be linked against your application. The script uses /usr/bin/perl to load the framework dynamically, leveraging system entitlements to access the MediaRemote framework.

    /usr/bin/perl /path/to/mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework COMMAND
  5. Verify adapter functionality with `test`

    master

    The test command checks if the adapter is correctly entitled to use the MediaRemote framework and can execute commands. This is useful for detecting if a macOS update has broken access.

    How it works:

    1. Attempts to read media via get.
    2. If no media is found, it launches MediaRemoteAdapterTestClient to simulate playback.
    3. Attempts to read media again.
    4. If media is detected during simulation, it exits with code 0. Otherwise, it exits with code 1.

    Warning: This may briefly create a fake media entry in the macOS Now Playing interface if no other media is playing.

    Exit Codes:

    • 0: Functional and safe to use.
    • Non-zero: Likely broken; consider falling back to other detection methods like AppleScript.
    # Run the test command
    /usr/bin/perl /path/to/mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework /path/to/MediaRemoteAdapterTestClient test
  6. Install the media-control CLI to see an example

    master

    To see the MediaRemote Adapter in action, you can install the media-control CLI tool via Homebrew. This tool demonstrates how to stream now playing information on macOS.

    $ brew tap ungive/media-control
    $ brew install media-control
    $ media-control stream
  7. Test the adapter functionality

    master

    To verify if the adapter is functional (for example, to decide whether to fall back to AppleScript), you can use the test command. This requires the MediaRemoteAdapterTestClient executable to be bundled with your app and passed as an argument. An exit code of 0 indicates the adapter is functional and safe to use.

    # Using the test client path as an argument
    /usr/bin/perl /path/to/mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework /path/to/MediaRemoteAdapterTestClient test
    
    # Alternatively, passing the test client path directly
    /usr/bin/perl /path/to/mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework /path/to/MediaRemoteAdapterTestClient COMMAND
  8. Use the mediaremote-adapter.pl CLI

    master

    The mediaremote-adapter.pl script is a CLI entrypoint used to interact with the MediaRemote framework. It allows you to stream real-time playback updates, fetch current metadata, or send playback commands.

    Basic Usage Pattern: mediaremote-adapter.pl FRAMEWORK_PATH [TEST_CLIENT_PATH] FUNCTION [PARAMS|OPTIONS...]

    • FRAMEWORK_PATH: The absolute path to the MediaRemoteAdapter.framework directory.
    • TEST_CLIENT_PATH: (Optional) The absolute path to the MediaRemoteAdapterTestClient executable. This is only used when calling the test function.
    • FUNCTION: The specific action to perform (e.g., stream, get, send, seek, etc.).
    mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework stream --no-diff
  9. Stream real-time updates with `stream`

    master

    The stream command provides a continuous stream of JSON updates until a SIGTERM is received.

    Output Format: Each update is a JSON object:

    • type: Always the string "data".
    • diff: A boolean.
      • If false: The payload is the full current state.
      • If true: The payload contains only updated fields. To reconstruct the full state, you must apply these updates to the last known non-diff payload. If a key is missing in a diff payload, it should be set to null in your local state.
    • payload: A dictionary structurally identical to the get command output.

    Options:

    • --no-diff: Disables diffing; diff will always be false and payload will always be the full state.
    • --debounce=N: Adds a delay of N milliseconds between detection and printing to prevent bursts of small updates.
    • --micros, --allow-missing-title, --no-artwork: Identical behavior to the get command.
    # Stream full updates (no diffing) without artwork
    /usr/bin/perl mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework /path/to/MediaRemoteAdapterTestClient stream --no-diff --no-artwork
  10. Get current now playing information with `get`

    master

    The get command prints the current now playing metadata once as a JSON object.

    Mandatory Keys:

    • bundleIdentifier
    • playing (boolean)
    • title

    If any mandatory keys cannot be determined, the command prints null. Media without a title is considered invalid unless the --allow-missing-title flag is used.

    Important Metadata Notes:

    • artworkData and artworkMimeType may take time to load and are not guaranteed to be present in a single get call. Use stream or poll get regularly to capture them.
    • mediaType values include MRMediaRemoteMediaTypeMusic and kMRMediaRemoteNowPlayingInfoTypeAudio.
    # Example usage (assuming paths are set)
    /usr/bin/perl mediaremote-adapter.pl /path/to/MediaRemoteAdapter.framework /path/to/MediaRemoteAdapterTestClient get
  11. Control playback with `seek`, `shuffle`, `repeat`, and `speed`

    master

    Use these commands to manipulate playback state:

    • seek POSITION: Seeks to a specific timeline position. POSITION must be a positive integer representing microseconds.
    • shuffle MODE: Sets shuffle behavior. MODE IDs:
      • 1: Disable
      • 2: Shuffle albums
      • 3: Shuffle tracks
    • repeat MODE: Sets repeat behavior. MODE IDs:
      • 1: Disable
      • 2: Repeat track
      • 3: Repeat playlist
    • speed SPEED: Sets playback speed. SPEED must be a positive integer.
  12. Send MediaRemote commands with `send COMMAND`

    master

    Sends a specific control command to the currently active media player. The COMMAND must be one of the following integer IDs:

    IDMediaRemote keyDescription
    0kMRPlayStart playback
    1kMRPausePause playback
    2kMRTogglePlayPauseToggle play/pause
    3kMRStopStop playback
    4kMRNextTrackSkip to next track
    5kMRPreviousTrackReturn to previous track
    6kMRToggleShuffleToggle shuffle mode
    7kMRToggleRepeatToggle repeat mode
    8kMRStartForwardSeekStart seeking forward
    9kMREndForwardSeekStop seeking forward
    10kMRStartBackwardSeekStart seeking backward
    11kMREndBackwardSeekStop seeking backward
    12kMRGoBackFifteenSecondsGo back 15 seconds
    13kMRSkipFifteenSecondsSkip ahead 15 seconds