Skip to main content

Media Player — media_player

Anything that originates content: a streamer, a disc player, a cable box, a music service. It is a source in the signal graph — a room's watch or listen names one of these.

Capabilities​

Declared in the driver manifest under [[proxy]] capabilities. Anything not declared takes the default below.

CapabilityTypeDefaultMeaning
has_app_launcherboolfalseThe player's top-level application launcher can be opened
has_appsboolfalseInstalled applications can be listed and launched
has_browseboolfalseContent can be listed and selected by id
has_crossfadeboolfalseBlends the end of one track into the next
has_deep_linkboolfalselaunch_app can open a particular title, not just the app.

Separate from has_apps because the two come apart in both directions and the difference is invisible from outside: a Roku set to Limited control still launches channels and a tvOS app that dropped its deep links still opens. Without this, content_id is a parameter every app-capable driver accepts and some silently ignore — so "play Severance" reports success and leaves somebody on a home screen.

Whoever is resolving a title reads this to decide whether to bother looking one up, and to tell a person the truth when the answer is no. | | has_discrete_volume | bool | false | | | has_dpad | bool | false | Cursor navigation for an on-screen UI | | has_hold | bool | false | A key can be held down and later released, rather than only tapped. See hold.

Separate from the commands it holds because the two really do come apart: an IR emitter can ramp anything it has a code for, and a box reached over HTTP one request at a time can ramp nothing at all however many keys it has. | | has_metadata | bool | false | Reports title/artist/art | | has_mute | bool | false | | | has_number_keys | bool | false | | | has_playlists | bool | false | | | has_queue | bool | false | Content can be queued rather than only played now | | has_scan | bool | false | Fast forward / rewind | | has_search | bool | false | | | has_seek | bool | false | | | has_shuffle_repeat | bool | false | | | has_skip | bool | false | | | has_transport | bool | true | Play/pause/stop | | has_up_down_volume | bool | false | | | is_audio_source | bool | true | | | is_video_source | bool | false | | | volume_max | u32 | 100 | |

Commands​

browse​

Only present when has_browse is declared true.

List what is under node. Results do not come back from this call — a command cannot return — they arrive as browse_results carrying the same token.

ParameterTypeNotes
limitu32 (optional)
nodestring (optional)A container id from an earlier result. Absent means the root.
offsetu32 (optional)
tokenstring (optional)Echoed back on the matching browse_results

dpad​

Only present when has_dpad is declared true.

ParameterTypeNotes
keyone of up · down · left · right · select · back · menu · home · info

hold​

Only present when has_hold is declared true.

Begin holding a key down, and keep holding it until release.

The difference between a step and a ramp. Sending volume_up forty times is not the same thing as holding it: an IR emitter has to keep the carrier up rather than re-send a frame, and a box on the network wants a press with no release rather than forty presses. Both are the same intent — somebody has their finger on a button — and this is where that intent goes.

Pairs with keypad's held and released, which exist to bracket exactly this.

what names the command being held rather than there being a hold_volume_up, a hold_scan_forward and six more — and each key is gated by the capability that provides it, so a box is only ever offered the keys it has. An Apple TV over IR has arrows and no volume; the same television over its network link has both; neither has a scan key. All three are the same contract and none of them advertises a key it would only refuse.

ParameterTypeNotes
whatone of volume_up (needs has_up_down_volume) · volume_down (needs has_up_down_volume) · scan_forward (needs has_scan) · scan_reverse (needs has_scan) · up (needs has_dpad) · down (needs has_dpad) · left (needs has_dpad) · right (needs has_dpad)

launch_app​

Only present when has_apps is declared true.

Open an installed application. The valid names are whatever apps reports for this device.

ParameterTypeNotes
appstring
content_idstring (optional)Deep link to a specific title. Ignored by a device that does not declare has_deep_link.

Opaque, and per service rather than per device — which is what makes it portable. A Disney+ video id is the same id whether it is being opened on a Roku or an Apple TV; what differs is only how each box spells the request, and spelling it is the driver's job. So the portable reference to a piece of content is the app name, this id, and content_kind — never a URL, which is already one vendor's dialect. | | content_kind | one of movie · series · season · episode · live · short (optional) | What content_id refers to. Absent means the driver should do whatever it does by default.

Needed because some devices will not deep link without being told: Roku's ECP takes a mediaType alongside the content id and opens the wrong thing — or nothing — when it is wrong, and a driver with no way to be told has to guess movie and be wrong for every series. | | launch_id | string (optional) | The string this platform uses for app, from the shared app catalog — a Roku channel number, an Android package, a bundle id, whatever that platform's launch call takes.

Filled in by core, never by a caller: a driver whose manifest declares [driver] app_platform gets this alongside app, looked up under whichever spelling was asked for. That is what keeps a table of channel numbers and bundle ids out of every driver, and lets a correction reach houses without rebuilding one — see https://github.com/junohouse/apps.

Absent means the catalog had no id for this platform, which is ordinary. A device's own list still wins: a box that reported what it has installed knows the truth about itself, where the catalog knows only what a box of this kind can usually run. Use this when the device said nothing — a set whose local API cannot list its apps at all, or an app it did not mention. |

mute_toggle​

Only present when has_mute is declared true.

No parameters.

open_app_launcher​

Only present when has_app_launcher is declared true.

Open the player's top-level application launcher.

This is separate from dpad { key = "home" }: some televisions expose their app launcher as a dedicated input or command, while others reach it with a Home key. The driver owns that difference. Core uses this command when the media player is selected as a room source.

No parameters.

pause​

No parameters.

play​

No parameters.

play_item​

Only present when has_browse is declared true.

Play a content id previously returned by browse or search

ParameterTypeNotes
idstring
queue_actionone of now · next · append · replace (optional)What to do with the queue. Absent means now, which is the only thing a player without
has_queue can do — and why this is optional rather than gated: requires lives on a command,
not a parameter, so a queueless player ignores it rather than being unable to receive it.
shufflebool (optional)Set shuffle as part of loading, rather than as a second command that races the first. "Shuffle
my Discover Weekly" is one intent and every service treats it as one call.

release​

Only present when has_hold is declared true.

Stop holding whatever hold started. Takes no argument: one thing is held at a time, because one finger is on one button, and a release that had to name its key would be a way to get them out of step.

A driver is expected to release on its own if the connection drops mid-hold. A stuck ramp is worse than a lost one — it runs the volume to the top of its range with nobody touching it.

No parameters.

scan_forward​

Only present when has_scan is declared true.

No parameters.

scan_reverse​

Only present when has_scan is declared true.

No parameters.

Only present when has_search is declared true.

ParameterTypeNotes
querystring
tokenstring (optional)Echoed back on the matching search_results

seek​

Only present when has_seek is declared true.

ParameterTypeNotes
position_msu32

set_crossfade​

Only present when has_crossfade is declared true.

ParameterTypeNotes
crossfadebool

set_mute​

Only present when has_mute is declared true.

ParameterTypeNotes
mutebool

set_repeat​

Only present when has_shuffle_repeat is declared true.

ParameterTypeNotes
modeone of off · one · all

set_shuffle​

Only present when has_shuffle_repeat is declared true.

ParameterTypeNotes
shufflebool

set_volume​

Only present when has_discrete_volume is declared true.

ParameterTypeNotes
levelu32 0–100

skip_back​

Only present when has_skip is declared true.

No parameters.

skip_forward​

Only present when has_skip is declared true.

No parameters.

stop​

No parameters.

volume_down​

Only present when has_up_down_volume is declared true.

No parameters.

volume_up​

Only present when has_up_down_volume is declared true.

No parameters.

Notifications​

app_changed​

Only present when has_apps is declared true.

ParameterTypeNotes
appstring

apps_changed​

Only present when has_apps is declared true.

The installed application list, read from the device.

ParameterTypeNotes
app_iconsstring[] (optional)One artwork URL per entry in apps, same order, empty string where the device has none. A
control surface draws the channel the way its owner recognizes it — by its logo, not its name.

These are addresses on the device, and they stay there: a client asks the controller for /artwork?binding=..&i=.. and the controller fetches the picture. That is what makes this work from a phone that is not on the television's network, and it means a driver only has to say where its artwork lives, not serve it. | | apps | string[] | |

browse_results​

Only present when has_browse is declared true.

One page of what browse was asked for, carrying that call's token.

items is json rather than a described structure because its shape belongs to a music service and not to us: a Sonos search hit, a Spotify album and a DLNA folder agree on almost nothing except that each has a name and can be selected. Every consumer draws them as a list and sends the id back, which is the whole contract. Each entry:

{ "id": "...", "name": "...", "subtitle": "...", "art": "...",
"playable": true, "container": false }

container marks something to browse into — an album, a folder, a service. playable and container are independent: an album is both.

token is whatever the browse carried, echoed. A driver that was given none sends none, and core treats the result as the latest for that binding — which is what one person at one screen actually does.

ParameterTypeNotes
itemsjson
offsetu32 (optional)
tokenstring (optional)
totalu32 (optional)Across all pages, when the source knows it

metadata_changed​

Only present when has_metadata is declared true.

ParameterTypeNotes
albumstring (optional)
art_urlstring (optional)
artiststring (optional)
duration_msu32 (optional)
titlestring (optional)

mute_changed​

Only present when has_mute is declared true.

ParameterTypeNotes
mutebool

online_changed​

ParameterTypeNotes
onlinebool

position_changed​

Only present when has_seek is declared true.

ParameterTypeNotes
position_msu32

search_results​

Only present when has_search is declared true.

As browse_results, for a search.

ParameterTypeNotes
itemsjson
tokenstring (optional)
totalu32 (optional)

transport_changed​

ParameterTypeNotes
stateone of playing · paused · stopped · buffering

volume_changed​

Only present when has_discrete_volume is declared true.

ParameterTypeNotes
levelu32

State​

Last-known values core keeps for a binding of this proxy.

KeyTypeMeaning
appstringApplication currently in the foreground
app_iconsstring[]Artwork for apps, one per entry, same order
appsstring[]Installed applications, as read from the device
artiststring
mutebool
onlinebool
titlestring
transportstring
volumeu32