MPRIS player control in the odio API
The MPRIS backend auto-discovers every MPRIS-compatible player (Spotify, VLC, Firefox, MPD, Kodi, Bluetooth devices) and exposes unified playback controls. Players appear and disappear in real time — no configuration needed.
Endpoints
Section titled “Endpoints”List players
Section titled “List players”GET /playersReturns all active players with their current state: playback status, track metadata, volume, shuffle, loop mode, position, and whether the player exposes a tracklist.
Playback control
Section titled “Playback control”POST /players/{player}/playPOST /players/{player}/pausePOST /players/{player}/play_pausePOST /players/{player}/stopPOST /players/{player}/nextPOST /players/{player}/previousSeek and position
Section titled “Seek and position”POST /players/{player}/seek{ "offset": 1000000 }POST /players/{player}/position{ "track_id": "...", "position": 0 }Volume, loop, shuffle
Section titled “Volume, loop, shuffle”POST /players/{player}/volume{ "volume": 0.5 }POST /players/{player}/loop{ "loop": "None|Track|Playlist" }POST /players/{player}/shuffle{ "shuffle": true }Tracklist
Section titled “Tracklist”Since odio-api v0.16.0, players implementing the MPRIS TrackList interface expose their queue. /players reports tracklist_supported per player, and the routes below answer 404 on players that don’t implement the interface.
Who implements it
Section titled “Who implements it”TrackList is optional in the MPRIS spec, and most players skip it — a player advertises it through the HasTrackList root property, which is what tracklist_supported mirrors.
| Player | Tracklist |
|---|---|
| MPD via mpd2mpris 0.13.0+ | The MPD queue, editable when the server allows queue edits |
| VLC | Its playlist, always editable |
Bluetooth devices via BlueZ mpris-proxy | Only when the connected phone or laptop exposes a browsable now-playing list over AVRCP |
| spotifyd | Not implemented, HasTrackList is hardcoded to false |
| Shairport Sync | Not implemented, the property isn’t exposed at all |
Read the queue
Section titled “Read the queue”GET /players/{player}/tracklist{ "can_edit_tracks": true, "tracks": [ { "track_id": "/org/mpris/MediaPlayer2/Track/42", "metadata": { "xesam:title": "..." } } ]}The list is served from the player cache, kept live by the TrackListReplaced, TrackAdded, TrackRemoved and TrackMetadataChanged D-Bus signals.
Jump, add, remove
Section titled “Jump, add, remove”POST /players/{player}/tracklist/goto/{trackid}POST /players/{player}/tracklist/remove/{trackid}POST /players/{player}/tracklist/add{ "uri": "file:///media/USB/album/01.flac", "after_track": "", "set_as_current": false }{trackid} is either the last segment of the track’s object path (42) or the full %2F-encoded path. uri must be absolute and its scheme must be one the player declares in SupportedUriSchemes, otherwise the request is rejected with 400. An empty after_track appends, NoTrack prepends.
add and remove require can_edit_tracks and answer 403 otherwise. goto is not an edit operation per the spec, so it works on read-only tracklists too.
Events
Section titled “Events”| Event | Trigger |
|---|---|
player.updated | Playback state, volume, or metadata change |
player.added | New player appeared |
player.removed | Player closed |
player.position | Periodic position tick |
player.tracklist.updated | Queue replaced, or a track added, removed, or its metadata changed |
How it works
Section titled “How it works”The backend listens on D-Bus for org.mpris.MediaPlayer2 interfaces. Player state is cached and invalidated via D-Bus signals, with a heartbeat for accurate position tracking.