Media playback control
Reading what is playing
Section titled “Reading what is playing”from uda import Uda
with Uda() as uda: track = uda.media.now_playing # None when nothing is playing if track: print(f"{track.title} — {track.artist}") print(track.album, track.duration_ms, "ms") print(uda.media.status) # 'playing' | 'paused' | 'stopped' | 'unknown'const track = uda.media.nowPlaying; // null when nothing is playingif (track) console.log(`${track.title} — ${track.artist}`);Every field is owned and pre-joined for display: MPRIS publishes xesam:artist as an array, but a UI wants one string, so the backend performs the join and the core struct stays free of the wire types. An unpublished field is an empty string — a radio stream often has no album.
Sending commands
Section titled “Sending commands”uda.media.send("play")uda.media.send("pause")uda.media.send("toggle") # switch between playing and pauseduda.media.send("next")uda.media.send("previous")uda.media.send("stop")The full command set: play, pause, toggle, next, previous, stop.
Playback status
Section titled “Playback status”status maps onto four values, with unknown as a first-class state rather than an error:
| Value | Meaning |
|---|---|
playing |
a track is actively progressing |
paused |
a track is selected and progress is halted |
stopped |
nothing is loaded, or playback reached the end |
unknown |
no session has focus, the player published nothing, or the value was unparseable |
Never treat unknown as paused.
Platform backends
Section titled “Platform backends”| Platform | Protocol | Notes |
|---|---|---|
| Linux | MPRIS v2 over the session bus | any player exposing org.mpris.MediaPlayer2.* |
| Windows | WinRT SMTC (GlobalSystemMediaTransportControls) |
reports the session holding system focus |
Three SMTC quirks worth knowing, because they cost hours when missed:
- Every
Try*Async()command returnsbool, not aResult.falsemeans the session refused the command (for exampleNextwhen the app does not enable it), whiletrueonly means the app accepted it — it can still fail afterwards. UDA mapsfalseto a typed error so a caller can tell “sent” from “refused”. TimeSpanis in 100-nanosecond ticks. A zeroDurationmeans “unknown” (a live stream), and is reported as absent rather thanSome(0).- “No current session” arrives as an
Err, not a null. The absence of a player looks like a failure unless the caller reads it as a value — which is exactly why a machine with nothing playing answersOk(None)/Ok(Unknown)and only commands raiseNotSupported. This matches the Linux backend, where a session bus with no player name yieldsNone.
MPRIS metadata type quirks (Linux)
Section titled “MPRIS metadata type quirks (Linux)”The MPRIS Metadata dictionary deviates from its own specification in practice:
xesam:titleis specified as an array of strings, but some players (mostly early versions) send a single string. UDA accepts both.xesam:artistbehaves the same way.mpris:lengthis in microseconds; UDA converts to milliseconds.0denotes a live stream rather than a zero-length track, and a negative value is treated as invalid.- A type mismatch is not reported as an error; the value degrades to empty.
What counts as a failed command
Section titled “What counts as a failed command”A player that refuses a command (pausing an already-paused stream, Next when the app disables the button) is still a successful call: the platform cannot distinguish “declined” from “done”, and reporting an error would make a perfectly normal toggle look like a failure.
Only a genuine transport failure — no session at all — produces an error.
See also
Section titled “See also”- Troubleshooting — when
now_playingis alwaysNone - Platform support — per-desktop coverage
