Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

mpris

MPRIS: media players with track metadata, playback state and position.

local player = mantle.mpris:map(function(mpris)
    return mpris and mpris.players[1]
end)

button {
    on_click = function()
        local current = player:get()
        if current then
            mantle.mpris:control(current.id, "play_pause")
        end
    end,
    children = {
        text {
            max_width = 240,
            elide = "End",
            content = player:map(function(current)
                return current and (current.play_state .. ": " .. current.title) or ""
            end),
        },
    },
}

State

mantle.mpris:get() returns MprisState, nil before the first push. A field marked ? may be absent.

FieldTypeDescription
playersPlayerState[]Every controllable MPRIS player except playerctld, longest-running first, so players[1] stays put; empty when none runs.

PlayerState

FieldTypeDescription
album_art_pathstringCover art as an existing absolute path, or empty; a remote artUrl is not fetched.
artiststringArtists joined with ", "; empty when unset.
desktop_entrystringThe player’s .desktop basename, e.g. "firefox", for app matching; empty when unset.
idstringBus-name suffix after org.mpris.MediaPlayer2., e.g. "spotify"; every action takes it.
identitystringDisplay name, e.g. "Spotify"; empty if unanswered.
lengthintegerTrack length in microseconds, or -1 when unknown, as for a live stream.
play_statestring"Playing", "Paused" or "Stopped"; keeps the last value when a read fails, empty if none.
positionintegerPlayback offset in microseconds as of position_updated_at, not polled while playing: add elapsed time. -1 when unknown.
position_updated_atintegerCLOCK_MONOTONIC microseconds when position was read. No Lua clock shares this epoch (not mantle.system.monotonic); only compare it with itself.
titlestringTrack title; empty when unset, normal between tracks.
urlstringxesam:url as sent, e.g. a file:// path or an https:// page; empty when unset.

Actions

Call each as mantle.mpris:<action>(arguments...); ? marks an argument you may omit.

ActionArgumentsDescription
controlid: string, cmd: PlayerCommandSends a playback command to players[].id.
seekid: string, position_us: integerSeeks to an absolute position in microseconds, clamped to [0, length] (only >= 0 when length is -1).
seek_relativeid: string, offset_us: integerSeeks by a signed offset in microseconds, unclamped; past the end may skip to the next track.

PlayerCommand

One of "play", "pause", "play_pause", "next", "previous".

Backend

ContractBehavior
DiscoverySession bus ListNames once, then NameOwnerChanged for org.mpris.MediaPlayer2.*. Skips playerctld and any player reporting CanControl = false
PushesOn a PlaybackStatus or Metadata change and on Seeked. A status change re-reads Position 100 ms later. Nothing polls
Seekseek calls SetPosition with the cached mpris:trackid. A player without one gets a relative Seek from a live Position read

How do I…

TaskAnswer
Show a live progress barStamp each new position_updated_at with mantle.system.monotonic in on_change, then add the seconds since: Media player

Gotchas

TrapFix
position stands still while playingIt is the offset at position_updated_at, not polled. Extrapolate, as above
position_updated_at compared with mantle.system.monotonic gives nonsenseDifferent clocks and units: CLOCK_MONOTONIC microseconds against seconds since system started. Only compare it with itself

See also: Media player recipe.

Source: supervisor/src/capabilities/mpris/