ok
This commit is contained in:
@@ -50,13 +50,62 @@ The central server is the heart of the application. Its main features are:
|
||||
allowlist for playback agents.}
|
||||
]
|
||||
|
||||
The browser is a thin remote control, not a second player. It polls a complete
|
||||
JSON state snapshot once per second and sends commands to the central server;
|
||||
The browser is a thin remote control, not a second player. It polls player
|
||||
status once per second and sends commands to the central server;
|
||||
playlists, preferences and playback state are kept there. The server exposes
|
||||
the browser application under the root URL and its JSON API under @tt{/api}.
|
||||
The API includes state, discovery, playback commands, preferences, artwork and
|
||||
the agent registration, polling and media routes.
|
||||
|
||||
@subsection{Playlist synchronization}
|
||||
|
||||
State responses include an opaque @tt{playlistVersion} for the selected
|
||||
playlist. The browser sends its last received version as the
|
||||
@tt{playlistVersion} query parameter on @tt{/api/state}, @tt{/api/discover}
|
||||
and @tt{/api/command/}@italic{command}. When that version still matches,
|
||||
@tt{tracks} is JSON @tt{null}; otherwise the response includes the complete
|
||||
track array. Requests without a version continue to receive all tracks.
|
||||
The version and track data are returned together in one consistent snapshot.
|
||||
|
||||
The server caches serialized tracks, count and total duration per playlist.
|
||||
Changing the track list invalidates that snapshot and generates a fresh version
|
||||
when it is next requested. Renaming a tab or updating playback position does
|
||||
not invalidate the tracks. Versions are renewed after a server restart and
|
||||
are distinct between playlists and users. Changes from another browser are
|
||||
picked up by the next poll.
|
||||
|
||||
The browser keeps the active playlist in memory and serializes state-producing
|
||||
requests through @tt{PlayerStateClient}, so a delayed response cannot replace
|
||||
newer state. Unchanged playlist rows are reused; changing the interface language
|
||||
still rebuilds their translated labels.
|
||||
|
||||
@subsection{Saved playlists in the library}
|
||||
|
||||
The library has @italic{Folders} and @italic{Playlists} tabs. The playlist
|
||||
toolbar's @italic{Save playlist} button names and saves the current tab in
|
||||
the user's library. The library row's @tt{+} button opens that playlist as
|
||||
its own tab, or selects its existing tab. @tt{▶} does the same and starts
|
||||
playback. These actions never append tracks to or replace another tab.
|
||||
|
||||
A saved playlist and its open tab share one UUID and one track list. Track
|
||||
edits and renaming therefore update the saved playlist automatically. Closing
|
||||
the tab keeps it in the library. Closing the last saved tab leaves a new empty
|
||||
default tab. Existing tabs remain open and appear in the library only after
|
||||
the user explicitly saves them.
|
||||
|
||||
The keystore retains @tt{playlists-for-}@italic{username} for the ordered open
|
||||
tabs and adds @tt{saved-playlists-for-}@italic{username} for library playlists.
|
||||
Each UUID has one stored value, shared by both indexes. The private storage
|
||||
procedure @tt{load-user-playlists} reads the open index by default and the saved
|
||||
index with @tt{#:saved? #t}. @tt{save-user-playlists!} accepts the saved collection
|
||||
through @tt{#:saved} and updates both indexes atomically. Values absent from
|
||||
both indexes are removed. Legacy stores without a saved index need no migration.
|
||||
|
||||
Browser state includes @tt{savedPlaylists} summaries without track arrays and
|
||||
a @tt{saved} flag on each tab. @tt{playlist-save} takes an open tab's @tt{id}
|
||||
and a @tt{name}; @tt{playlist-open} and @tt{playlist-play} take a saved playlist's
|
||||
@tt{id}. All three commands operate within the requesting user's collection.
|
||||
|
||||
@subsection{Player agents}
|
||||
|
||||
A player agent is an additional renderer implemented by this package. It runs
|
||||
|
||||
Reference in New Issue
Block a user