Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,35 @@ version 2: its `{{ … }}` sequences become substitution points, and its
and state consumers; unknown fields are ignored by consumers.
`length` counts in milliseconds with `-1` = unknown/live — one time
convention across the media surface (OVOS-GUI-1 §3.4).
- §3 — the state model is five orthogonal axes, not three: `PlayerState`
(§3.1), `MediaState` (§3.2), loop/shuffle (§3.3), the previously
unenumerated track state (new §3.4), and `PlaybackType` (new §3.5),
each with an enumeration table. Track-state's implied lifecycle
(`disambiguation` → `queued` → `playing`) is stated explicitly;
transitions not listed are not valid in this version.
- §4.4 (new §4.6 "Open items") — the dangling "(see Follow-ups)"
pointer resolved: the pending `MediaState` and track-state numeric
code assignments are named as open items instead of citing a
nonexistent section.
- §4.5 — `status` retyped from `number` to *reserved*: producers SHOULD
omit it and consumers MUST ignore any value present until the §4.6
track-state numeric codes are assigned.
- §4.2.1 — media/playlist relationship defined: when `playlist` is
present, `media` MUST be a member of it, located by `uri` match;
`next`/`previous` (§4.3) move relative to that position. The base
`ovos.common_play.search` payload is confirmed implementation-defined
in this version, alongside its bracketing Messages.
- §8 — three failure MUSTs added: reject a `play` request whose media
entry lacks a `uri` before dispatch; announce `MediaState`
`INVALID_MEDIA` on acquisition failure; announce `PlayerState`
`STOPPED` if the playback backend dies mid-track.
- §4.5's GUI-1 time-convention citation reworded to name the convention
OVOS-GUI-1 v2 adopts, rather than asserting it as already uniform
(OVOS-GUI-1's own unification of `SYSTEM_audio_player` /
`SYSTEM_media_player` time units is pending in PR #108).
- Dependencies / See also — "the media pipeline" reworded to "a
pipeline plugin conformant to OVOS-PIPELINE-1" to stop implying
PIPELINE-1 names a single pipeline component.

### 1

Expand Down
109 changes: 84 additions & 25 deletions ocp-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ intent stack gives one utterance one handler.

Dependencies: OVOS-MSG-1 (envelope and the `context.session` carrier),
OVOS-SESSION-1 (session field registry), OVOS-SESSION-2 (session
assignment and mutation boundaries), OVOS-PIPELINE-1 (the media pipeline
that matches playback and control utterances and dispatches into this
surface), OVOS-STOP-1 (global stop cascade, of which media stop is one
subscriber).
assignment and mutation boundaries), OVOS-PIPELINE-1 (a pipeline plugin
conformant to OVOS-PIPELINE-1 that classifies playback and control
utterances and dispatches into this surface), OVOS-STOP-1 (global stop
cascade, of which media stop is one subscriber).

The key words **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, **MAY**
are used as in RFC 2119.
Expand Down Expand Up @@ -77,10 +77,11 @@ requests act on that source.

## 3. State model

The player exposes three orthogonal state axes. Each axis has a fixed
enumeration; an implementation **MUST NOT** report a value outside its
axis, and **SHOULD** treat unknown received values as the axis's neutral
member.
The player exposes five orthogonal state axes: `PlayerState` (§3.1),
`MediaState` (§3.2), loop/shuffle (§3.3), track state (§3.4), and
`PlaybackType` (§3.5). Each axis has a fixed enumeration; an
implementation **MUST NOT** report a value outside its axis, and
**SHOULD** treat unknown received values as the axis's neutral member.

### 3.1 Player state

Expand Down Expand Up @@ -110,6 +111,40 @@ The player is the **single writer** of its own state. It emits a state
event when state changes (§4.4); it **MUST NOT** derive its authoritative
state by subscribing to its own emitted events.

### 3.4 Track state

Track state reports where the now-playing track is in its per-backend
lifecycle. It is reported on `ovos.common_play.track.state` (§4.4) and
recorded on a media entry's `status` field (§4.5) once numeric codes are
assigned (§4.4, Open items).

| `TrackState` member | Meaning |
|---|---|
| `disambiguation` | A result exists (§4.2.1's `disambiguation` set) but is not queued to any backend. |
| `queued` | The track is waiting for a backend to start it. Qualified by backend kind (§3.5): skill-internal, audio, video, web view, external OS player. |
| `playing` | A backend has confirmed playback. Qualified by backend kind (§3.5). A `playing`-family value implies `PlayerState.PLAYING` (§3.1). |

Pausing is a `PlayerState` (§3.1) / `MediaState` (§3.2) concern and
**MUST NOT** be represented as a track-state value.

The lifecycle implied by this axis is `disambiguation` → `queued` →
`playing`. Transitions not listed here are not defined by this version
of the spec; an implementation **MUST NOT** treat them as valid.

### 3.5 Playback type

`PlaybackType` names the backend kind a media entry targets or a
queued/playing track state is qualified by (§3.4, §4.5 `playback`
field):

| `PlaybackType` member | Meaning |
|---|---|
| `skill-internal` | The originating skill renders the media itself (no shared backend). |
| `audio` | An audio backend (OVOS-AUDIO-1-adjacent playback service). |
| `video` | A video-capable backend / display surface. |
| `web view` | An embedded web view renders the media. |
| `external OS player` | An MPRIS-bridged external player (§6) is the target. |

---

## 4. Bus surface
Expand Down Expand Up @@ -149,8 +184,15 @@ implementation.
| `disambiguation` | array of media entries | no | The full candidate result set the queue was chosen from, kept for "play something else" style follow-ups. When absent, defaults to the playlist. |
| `repeat` | boolean | no | When `true`, the player enters loop mode `REPEAT` (§3.3). |

The search-bracketing Messages (`search`, `search.start`, `search.end`)
carry implementation-defined payloads in this version.
When `playlist` is present, `media` **MUST** be a member of it: the
player locates `media`'s position within `playlist` by matching `uri`
(§4.5). `ovos.common_play.next` and `ovos.common_play.previous` (§4.3)
move relative to that located position, not to an externally supplied
index.

The `ovos.common_play.search` payload and the payloads of its
bracketing Messages (`search.start`, `search.end`) are all
implementation-defined in this version.

#### 4.2.2 `ovos.common_play.seek` payload

Expand Down Expand Up @@ -194,20 +236,30 @@ All three share one payload shape:
|-------|------|----------|---------|
| `state` | number | yes | The numeric code of the new state on the topic's axis (§3.1 `PlayerState`, §3.2 `MediaState`, or the track axis below). |

Currently, numeric codes are assigned only for `PlayerState` (0/1/2); `MediaState` and track-state values remain symbolic pending future assignment (see Follow-ups).
Currently, numeric codes are assigned only for `PlayerState` (0/1/2);
`MediaState` and track-state values remain symbolic pending future
assignment (§4.6 Open items).

`ovos.common_play.track.state` reports where the now-playing track is
in its per-backend lifecycle. Its axis distinguishes *disambiguation*
(a result exists but is not queued), *queued* (waiting for a backend
to start), and *playing* (a backend confirmed playback), with the
queued/playing members qualified by backend kind (skill-internal,
audio, video, web view, external OS player). A `playing`-family value
implies `PlayerState.PLAYING`; pausing is a `PlayerState` /
`MediaState` concern and never a track-state value.
`ovos.common_play.track.state` reports the §3.4 `TrackState` value.

A consumer **MUST NOT** assume it can read player state synchronously; the
state reports are the contract.

### 4.6 Open items

This version of the spec leaves the following unresolved; implementers
**MUST NOT** assume a numeric encoding for them beyond what is stated:

- **`MediaState` numeric codes** (§3.2) — members are named but no
stable numeric code is assigned yet, unlike `PlayerState` (§3.1).
- **Track-state numeric codes** (§3.4) — the `TrackState` members
(`disambiguation`, `queued`, `playing`, each qualified by
`PlaybackType`, §3.5) are named but not yet numbered.

Until these are assigned, `MediaState` and track-state values travel
symbolically; a future version of this spec fixes their numeric codes
the way §3.1 already fixes `PlayerState`'s.

### 4.5 The media entry

Playback requests and state consumers exchange tracks as **media
Expand All @@ -219,10 +271,10 @@ entry** objects:
| `title` | string | no | Display title. |
| `artist` | string | no | Display artist. |
| `image` | string | no | Artwork, delivered per the GUI image rules (OVOS-GUI-1 §3.5). |
| `playback` | number | no | Requested playback kind on the `PlaybackType` axis (skill-internal, audio, video, web view, external OS player). |
| `status` | number | no | The entry's current track-state value (§4.4). |
| `playback` | number | no | Requested playback kind on the `PlaybackType` axis (§3.5). |
| `status` | *reserved* | no | The entry's current track-state value (§3.4, §4.4). Reserved in this version pending the track-state numeric assignment (§4.6 Open items): producers **SHOULD** omit this field, and consumers **MUST** ignore any value present until numeric `TrackState` codes are assigned. |
| `media_type` | number | no | Content classification (music, radio, podcast, video, …) used for result ranking. |
| `length` | number (ms) | no | Track duration in milliseconds; `-1` = unknown/live. One time convention across the media surface: durations and positions count in milliseconds with `-1` meaning unknown/live (§4.2.2, OVOS-GUI-1 §3.4). |
| `length` | number (ms) | no | Track duration in milliseconds; `-1` = unknown/live. One time convention across the media surface: durations and positions count in milliseconds with `-1` meaning unknown/live (§4.2.2) — the convention OVOS-GUI-1 v2 adopts uniformly for its media templates (OVOS-GUI-1 §3.4). |
| `match_confidence` | number 0–100 | no | Provider's self-reported relevance for the originating query. |
| `skill_id` | string | no | The provider that produced this entry. |

Expand Down Expand Up @@ -317,7 +369,13 @@ another session's playback.
`ovos.common_play.player.state` / `…media.state` / `…track.state` (§3.3,
§4.4);
- treat control requests as no-ops when no media is present (§4.3);
- scope a stop to the inbound session (§7).
- scope a stop to the inbound session (§7);
- reject a `ovos.common_play.play` request whose `media` entry lacks a
`uri` before dispatching it to any backend (§4.2.1, §4.5);
- on acquisition failure (the resolved media cannot be loaded), announce
`MediaState` `INVALID_MEDIA` (§3.2, §4.4);
- if the playback backend dies mid-track, announce `PlayerState`
`STOPPED` (§3.1, §4.4).

### A Virtual Media Player implementation **SHOULD**:

Expand All @@ -334,8 +392,9 @@ another session's playback.

## See also

- **OVOS-PIPELINE-1** — the media pipeline that classifies playback vs.
control utterances and dispatches into the §4 surface.
- **OVOS-PIPELINE-1** — a pipeline plugin conformant to OVOS-PIPELINE-1
that classifies playback vs. control utterances and dispatches into the
§4 surface.
- **OVOS-STOP-1** — global stop cascade; media stop is a subscriber (§7).
- **OVOS-SESSION-1 / OVOS-SESSION-2** — the `context.session` carrier and
per-session ownership that scope the player (§5).
Expand Down