Interface: PlaylistApi
Defined in: packages/player/src/player.ts:1199
Queue manipulation, backed by mpv's own playlist (which is what makes gapless transitions gapless — the next entry is demuxed before the current one ends).
Methods
add()
add(source: string, options?: PlaylistAddOptions): Promise<void>;
Defined in: packages/player/src/player.ts:1286
Add a source to the playlist — at the end, next, or at an exact index.
Parameters
| Parameter | Type | Description |
|---|---|---|
source | string | URI or file path. |
options? | PlaylistAddOptions | See PlaylistAddOptions. Omit it entirely for a plain append. |
Returns
Promise<void>
Throws
PlayerErrorException with code invalid-state when
position is a number that is not an integer in 0 … playlist.count —
see PlaylistAddOptions.position.
Remarks
One mpv command, whatever the cell. The six combinations of position
and play each map to exactly one loadfile action:
position | play: false (default) | play: true |
|---|---|---|
| (omitted) | append | append-play |
'next' | insert-next | insert-next-play |
number | insert-at + index | insert-at-play + index |
The insert-* actions arrived in mpv 0.38, together with loadfile's
third index argument (see LOADFILE_NO_INDEX). This library never
emulates them with an append + playlist-move pair: that is two commands
with a window in between where the queue is briefly wrong — observable
through playlist-count, and readable by mpv's own prefetch, which consults
the queue on its own schedule.
Carries the same .m3u8/.m3u demuxer=lavf guard as Player.load
— it is the identical loadfile command and the identical hazard.
Example
await player.playlist.add(uri) // to the end
await player.playlist.add(uri, { position: 'next' }) // play after this one
await player.playlist.add(uri, { position: 0 }) // to the head
await player.playlist.add(uri, { play: true }) // …and start if idle
clear()
clear(): Promise<void>;
Defined in: packages/player/src/player.ts:1355
Remove every entry except the one currently playing.
Returns
Promise<void>
entries()
entries(): readonly PlaylistEntry[];
Defined in: packages/player/src/player.ts:1246
The queue's actual contents — every entry's logical URI, mpv's own entry id, and which one is current.
Returns
readonly PlaylistEntry[]
The entries in playlist order. [] when nothing is queued.
Remarks
One bounded synchronous native read, on demand. It is a single
mpv_get_property("playlist", MPV_FORMAT_NODE) — constant, whatever the
queue length — and it is a pull, not a subscription, deliberately.
The temptation is to observe the playlist and publish it in
PlayerState. That would put a variable-size array on the bridge
every time the queue is touched, and it would make the snapshot a second
copy of state mpv already owns — the exact shape of the mistake this
library avoids everywhere else (position is anchored and projected rather
than streamed; metadata is pulled rather than pushed). Cheap reads on
demand beat an expensive feed nobody asked for, and the read is cheap
precisely because it is one node rather than an N + 1 sub-property walk.
It is also coherent, which the walk was not: mpv builds the node under
its own lock, so the result is one generation of the queue. A walk of
playlist/0/filename … playlist/N/filename can interleave with a
playlist-move and return two halves of two different orders.
Call it when something tells you the queue moved — PlayerEventMap.queueChanged, or the value returned to you by shuffle/unshuffle — not on a timer, and not per render.
Prefer entryId over the array index for identity. Ids are "unique for
the entire life time of the current mpv core instance" (mpv 0.41.0
input.rst) and survive move/remove/shuffle; positions do not. See
PlaylistAddOptions.position for what that costs an app that keys on
index instead.
Throws
PlayerErrorException if the player has been destroyed.
Example
player.on('queueChanged', () => {
for (const entry of player.playlist.entries()) {
console.log(entry.current ? '▶' : ' ', entry.entryId, entry.uri)
}
})
jumpTo()
jumpTo(index: number, options?: {
autoPlay?: boolean;
}): Promise<void>;
Defined in: packages/player/src/player.ts:1319
Jump to index and (re)start playback of it.
Parameters
| Parameter | Type | Description |
|---|---|---|
index | number | Playlist index to make current. |
options? | { autoPlay?: boolean; } | autoPlay: false keeps the current pause state instead of starting playback. |
options.autoPlay? | boolean | - |
Returns
Promise<void>
Remarks
mpv's playlist-play-index restarts the entry, but pause is a global
player property it does not touch — so on a player that was loaded with
autoPlay: false, a bare jump used to select the entry, open the network
stream, fill the demuxer cache and then sit there silently. (Measured
on-device against a Shoutcast station: mpv logged playback restart complete @ 0.000000, audio=playing, video=eof (paused) and the cache grew
past 45 s with nothing audible.) Jumping to an entry means playing it, so
this clears pause by default, exactly like Player.load.
move()
move(from: number, to: number): Promise<void>;
Defined in: packages/player/src/player.ts:1301
Move the entry at from so that it ends up at to.
Parameters
| Parameter | Type |
|---|---|
from | number |
to | number |
Returns
Promise<void>
Remarks
mpv's playlist-move takes "the entry that index1 should take the place
of", which is off by one for downward moves; this method takes ordinary
array semantics and does the adjustment.
next()
next(): Promise<void>;
Defined in: packages/player/src/player.ts:1324
Go to the next entry.
Returns
Promise<void>
previous()
previous(options?: {
restartThreshold?: number;
}): Promise<void>;
Defined in: packages/player/src/player.ts:1353
The ⏮ button: restart the current entry, or go to the previous one.
Parameters
| Parameter | Type | Description |
|---|---|---|
options? | { restartThreshold?: number; } | restartThreshold in seconds; defaults to DEFAULT_RESTART_THRESHOLD_SECONDS (3). Pass 0 to always move. |
options.restartThreshold? | number | - |
Returns
Promise<void>
Remarks
The universal music-app convention, implemented once here rather than in
every app. More than restartThreshold seconds into an entry, this seeks
back to 0; before that, it moves to the previous entry (mpv's
playlist-prev weak). The same shape mpv itself uses for chapters — see
--chapter-seek-threshold and Player.previousChapter.
Two cases where it always moves instead of restarting, both forced rather than chosen:
- A live stream (PlayerState.isLive): there is no position
0to return to, and mpv would reject the seek. restartThreshold: 0: the opt-out, for an app that draws separate "restart" and "previous" controls.
At the head of the queue with nothing to go back to, restarting is still
what happens if you are past the threshold — and if you are not, mpv's
playlist-prev does nothing, which is the same thing every player does.
The position it compares against is the locally projected one (Player.getPosition), so this costs no native round-trip.
remove()
remove(index: number): Promise<void>;
Defined in: packages/player/src/player.ts:1292
Remove the entry at index.
Removing the current entry stops it and starts the next one.
Parameters
| Parameter | Type |
|---|---|
index | number |
Returns
Promise<void>
shuffle()
shuffle(): Promise<readonly PlaylistEntry[]>;
Defined in: packages/player/src/player.ts:1385
Shuffle the queue in place (mpv's playlist-shuffle).
Returns
Promise<readonly PlaylistEntry[]>
The queue after the shuffle, exactly as entries would
report it — because a permutation nobody can see is not a feature. mpv's
playlist-shuffle reports nothing about what it did, so before this
returned anything an app's only options were to re-read the playlist itself
or to guess; the read is one node round-trip and it happens here, once,
where it is unmissable.
Also emits PlayerEventMap.queueChanged with reason: 'reordered',
for listeners that are not the caller.
Remarks
mpv 0.35.1 input.rst: "Shuffle the playlist. This is similar to what is
done on start if the --shuffle option is used."
Two consequences worth knowing, both read off playlist_shuffle() in
mpv's common/playlist.c:
- Every entry is shuffled, including the one playing. It is the entry
mpv keeps current, not the index, so the current track keeps playing
uninterrupted — but its
playlist-posalmost certainly changes, which surfaces here as a PlayerEventMap.trackChanged event for a track that did not actually change. Treat that event as "the cursor moved", and re-readstate.playlist.index. - Each shuffle overwrites the entries' recorded original order, which is exactly why unshuffle can only undo the most recent one.
unshuffle()
unshuffle(): Promise<readonly PlaylistEntry[]>;
Defined in: packages/player/src/player.ts:1414
Undo the most recent shuffle (mpv's playlist-unshuffle).
Returns
Promise<readonly PlaylistEntry[]>
The queue after the restore, exactly as entries would
report it — including when the undo did nothing, which is how a caller can
tell. Also emits PlayerEventMap.queueChanged with
reason: 'reordered'.
Remarks
mpv 0.35.1 input.rst documents the limitation precisely: "Attempt to
revert the previous playlist-shuffle command. This works only once
(multiple successive playlist-unshuffle commands do nothing). May not
work correctly if new recursive playlists have been opened since a
playlist-shuffle command."
Concretely: mpv restores the order by sorting on an original_index
stamped at shuffle time, so this is a one-level undo, not a history. If you
need to return to a user-visible order after several shuffles, keep that
order in your app and rebuild the queue with Player.loadPlaylist.
Calling it when nothing was shuffled is harmless (mpv sorts an already sorted list); it is not an error.
Carries the same shuffle caveat about trackChanged: mpv keeps the
entry current, so the music does not stop, but playlist-pos almost
certainly moves and surfaces as a trackChanged for a track that did not
change.