Skip to main content

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

ParameterTypeDescription
sourcestringURI or file path.
options?PlaylistAddOptionsSee 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:

positionplay: false (default)play: true
(omitted)appendappend-play
'next'insert-nextinsert-next-play
numberinsert-at + indexinsert-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

ParameterTypeDescription
indexnumberPlaylist 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

ParameterType
fromnumber
tonumber

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

ParameterTypeDescription
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 0 to 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

ParameterType
indexnumber

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-pos almost 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-read state.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.