Skip to content

PlayTree: recursive queue model behind the three playback buffersΒ #201

Description

@JarbasAl

πŸ€– Auto-generated by Claude Fable 5 (claude-fable-5) via Claude Code β€” NOT human-reviewed. Verify before acting. Design directed by the owner; this issue records the agreed direction and open questions.

PlayTree: recursive queue model behind the three playback buffers

The player's queue model becomes a tree with a cursor, and the three familiar buffers become views of it:

  • Currently Playing β€” the active leaf plus a small preload window.
  • Playlist β€” the flattened remainder of the tree from the cursor onward. May contain playlists inside playlists: albums as coherent units in a larger queue, provider playlists as lazy handles, "queue this album next" without splicing.
  • Search Results β€” staging subtrees populated by voice queries, grafted into the playlist when selected for playback.

This matches the wire triad (media / playlist / disambiguation) that ovos.common_play.play already carries; what changes is that nesting stops being eagerly flattened at decode time and becomes structure the player understands.

Core semantics (decided)

  • Playback policy attaches to nodes, not the global player. Shuffle the album order while playing each album in track order; repeat one sub-playlist; a sequential news-briefing node inside a shuffled music queue. The global shuffle/repeat flags become the root node's policy β€” current behavior is the degenerate single-node case.
  • next/previous always move by leaf, depth-first. "Skip this album/playlist" is a future distinct verb, not an overload of next.
  • Tree inside, flat on the wire. Every wire surface (status payload, playlist_position/playlist_size, disambiguation query, GUI views) reports flat projections in current traversal order. The frozen ovos.common_play.* contract does not change; only OCP-1 Β§4.5's media-entry shape needs to admit a playlist-as-entry.
  • Currently Playing as a list means the gapless preload window, nothing more for now. Transport verbs and track.state bind to exactly one active leaf. Multi-part logical tracks (audiobook chapters spanning seek/length arithmetic) are a possible later per-node "atomic group" policy, out of scope here.
  • Graft by copy, not reference. Selecting search results into the playlist snapshots them; a later search never mutates the live queue, and "save this search" is a coherent snapshot.
  • Failure recurses with bounds. A child subtree whose leaves all fail marks its parent entry failed and advances (preserving the existing all-failed hot-loop guards); recursion depth is bounded; cycle detection is mandatory β€” a stored playlist referencing itself must not hang the dispatcher.
  • Lazy expansion prefetches one ahead, sharing machinery with the preload window, so provider-playlist expansion never lands its latency on a track boundary.
  • Stored playlists (Playlists: generalize beyond Liked SongsΒ #199) become persistent subtrees β€” "save this queue as X" serializes a node. The Playlists: generalize beyond Liked SongsΒ #199 store builds on the node abstraction, not before it.

Open questions

  • playlist_position under per-node shuffle: defined as the index into the current flattened traversal order (queue views reorder when a node reshuffles) β€” needs a spec sentence.
  • Preload window size and whether backends participate (a load_track ahead of time on a second backend instance vs. serial preload on one).
  • How a lazy provider handle is represented in a MediaEntry (uri scheme? provider callback topic?) β€” interacts with the Β§4.5 shape.

Plan

  1. OCP-1 Β§4.5 spec text: media-entry shape admitting playlist-as-entry, the flat-projection rule, and the traversal-order sentence for position fields (architecture repo; coordinated with the in-flight OCP-1 edits).
  2. PlayTree model layer in ovos-media as its own module with exhaustive unit tests (nodes, cursor, per-node policy, flatten, failure propagation, cycle/depth guards, copy-graft) β€” draft PR, no player rewiring yet.
  3. Adopt: PlayQueue reimplemented on the tree, buffers become views, preload window, lazy expansion. Post-v2-train, internal-only. The adoption pass also collapses _next_sibling_leaf/_prev_sibling_leaf and next_leaf/prev_leaf into direction-parameterized traversal β€” deferred from the model PR so its fuzz-verified logic ships unrefactored.
  4. Playlists: generalize beyond Liked SongsΒ #199 store lands on top as persistent subtrees.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions