Queue API
Methods for inspecting the playback queue, adding tracks, reordering entries, and controlling next-track playback.
queue
queue.add
Queue one or more tracks by their position in a playlist. Positions past the last row are skipped; when none is in range the call fails with INVALID_INDEX. A negative position fails with INVALID_PARAMS.
| Parameter | Type | Required | Description |
|---|---|---|---|
playlist | integer | No | Playlist the positions refer to; the active playlist when omitted. An index past the last playlist fails with INVALID_INDEX; with this omitted and no active playlist the call fails with NO_ACTIVE_ITEM. At least 0. |
playlistGuid | string | No | Persistent unique identifier of a playlist (bracketed GUID, case-insensitive), used in place of a mutable index. Mutually exclusive with playlist; passing both or an invalid format fails with INVALID_PARAMS. Fails with NOT_FOUND if the playlist does not exist, never falling back. Invalid-format and not-found failures return the given GUID in details.playlistGuid. |
tracks | integer[] | No | Rows to queue, in order. Takes precedence over track. Each item: at least 0. |
track | integer | No | A single row to queue; read only when tracks is absent. At least 0. |
expectedPlaylistVersion | string | No | Act only while the playlist that the rows belong to still has this contentVersion, as its reads report it. A different version changes nothing and fails with STALE_TARGET, the current version carried as details.current. Omitted, no check is made. |
Returns
| Field | Type | Description |
|---|---|---|
addedCount | integer | How many rows were queued. |
queueCount | integer | Queue length afterwards. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Queues one or more tracks by their position in a playlist.
Supply either tracks or track, not both — when tracks is an array it takes precedence and track is ignored. Out-of-range track indices are skipped, so a call that matches nothing fails with INVALID_INDEX and carries addedCount: 0; the same is true for an empty tracks array. An invalid playlist fails with INVALID_INDEX (Invalid playlist index), and omitting both fields fails with INVALID_PARAMS. Entries in tracks must be integers — non-numeric elements are rejected by parameter validation rather than skipped.
// queue several tracks from the active playlist
await fb2k.invoke('queue.add', { tracks: [0, 1, 2] });
// queue a single track
await fb2k.invoke('queue.add', { track: 0 });queue.addPaths
Queue tracks by path. Queueing needs playlist membership, so the paths are appended to a playlist first: by default a dedicated [WebView Queue] playlist, created when missing. A locked target playlist fails with LOCKED, and nothing is added or queued. The target is looked up again after the paths are resolved, as in playlist.addPaths: moved meanwhile, it still gets the tracks and playlist in the result is its new index; removed meanwhile, the call fails with OPERATION_FAILED and nothing is queued. A path the media-root check refuses fails the whole call with PERMISSION_DENIED. Paths that resolve to nothing are dropped; when none resolves the call fails with NOT_FOUND, the failure carrying invalidCount.
| Parameter | Type | Required | Description |
|---|---|---|---|
paths | string[] | Yes | File paths or URLs, each optionally with a |subsong:N suffix. Entries longer than 2048 characters are dropped and counted in invalidCount. Must not be empty. |
useQueuePlaylist | boolean | No | Append to the dedicated [WebView Queue] playlist, creating it when missing. false uses playlist, or the active playlist when that is omitted too. Default: true. |
playlist | integer | No | Target playlist; read only when useQueuePlaylist is false. An index past the last playlist fails with INVALID_INDEX; with this and playlistGuid omitted and no active playlist the call fails with NO_ACTIVE_ITEM. At least 0. |
playlistGuid | string | No | The target playlist's guid, instead of playlist; like playlist it is read only when useQueuePlaylist is false, and not checked at all otherwise. |
Returns
| Field | Type | Description |
|---|---|---|
addedCount | integer | How many tracks were appended and queued. |
invalidCount | integer | Entries of paths dropped: empty ones, ones longer than 2048 characters, a |subsong:N no track could be made for, and the plain paths when none of them resolved. A plain path that resolves to nothing while another plain path of the call resolves is not counted, so a call that succeeds can have dropped more than this. |
playlist | integer | The playlist the tracks were appended to. |
playlistGuid | string | Unique identifier of the playlist, formatted as {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}. Pass as playlistGuid to reliably target this playlist. |
queueCount | integer | Queue length afterwards. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Queues tracks by file path, adding them to a playlist first.
Because queueing requires playlist membership, this method appends the paths to a playlist and then queues them. By default that target is a dedicated [WebView Queue] playlist rather than your active one. Setting useQueuePlaylist: false targets playlist instead, or the active playlist when playlist is also omitted — which fails with No active playlist if there is none.
A locked target playlist fails with LOCKED (Playlist is locked), with details carrying playlist and isLocked: true. When nothing resolves, the NOT_FOUND failure carries invalidCount. An empty paths array is refused with INVALID_PARAMS, an invalid playlist with INVALID_INDEX, and no active playlist with NO_ACTIVE_ITEM.
await fb2k.invoke('queue.addPaths', { paths: ['C:\\Music\\a.flac'] });queue.clear
Empty the queue.
This method takes no parameters.
Returns
| Field | Type | Description |
|---|---|---|
clearedCount | integer | How many entries the queue held. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
const result = await fb2k.invoke('queue.clear');queue.flush
Empty the queue; the same operation as queue.clear under its older name.
This method takes no parameters.
Returns
| Field | Type | Description |
|---|---|---|
clearedCount | integer | How many entries the queue held. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
const result = await fb2k.invoke('queue.flush');queue.get
Read the whole playback queue, in play order. No paging: every entry comes back.
This method takes no parameters.
Returns
| Field | Type | Description |
|---|---|---|
items | QueueItem[] | The entries, in play order. |
items[].… | Track | Every field of Track. |
items[].queueIndex | integer | Position in the queue, from 0. |
items[].playlist | integer | null | Playlist the entry was queued from; null when it carries no playlist position: queued by path, or the position foobar2000 keeps for it no longer holds this track (the row or the playlist was removed, or the rows moved). |
items[].playlistGuid | string | null | GUID of that playlist, as playlistGuid takes it; null exactly when playlist is null. |
items[].playlistItem | integer | null | Row in that playlist; null exactly when playlist is null. |
count | integer | Number of entries. |
contentVersion | string | Pass it as expectedVersion so that a queue changed meanwhile fails the call instead of hitting other entries. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Returns the entire playback queue.
There is no paging; the whole queue is always returned. Each entry is the shared Track row plus queueIndex and the originating playlist, playlistGuid and playlistItem. All three position fields are always present. An entry whose recorded position still holds its track reports it; the others report all three as null: one queued through the paths of queue.insertNext, or one whose recorded position no longer holds its track after rows or the playlist were removed or moved. A reported position therefore never points at a different track. item.playlist == null is the whole test.
const res = await fb2k.invoke('queue.get');
if (res.success === false) throw new Error(res.error);
const { items, count } = res;queue.getCount
Report how many entries the queue holds.
This method takes no parameters.
Returns
| Field | Type | Description |
|---|---|---|
count | integer | Number of entries. |
hasItems | boolean | Whether the queue holds anything; the same as count > 0. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Returns the queue length.
hasItems is exactly count > 0, provided as a convenience.
const res = await fb2k.invoke('queue.getCount');
if (res.success === false) throw new Error(res.error);
const { count } = res;queue.moveToTop
Move a queued entry to the front so it plays next. Only queue order changes; the relative order of the other entries is kept. An index of 0, one past the end, and any index while the queue is empty fail with INVALID_INDEX. When the queue cannot be rebuilt the call fails with OPERATION_FAILED, the failure carrying queueCount.
| Parameter | Type | Required | Description |
|---|---|---|---|
index | integer | Yes | Position of the entry to promote; 0 is already the front and is refused. At least 0. |
expectedVersion | string | No | Expected content version for optimistic concurrency control. When mismatched, mutations are aborted with STALE_TARGET (details.current carries current version). Omitted, no check is made. |
Returns
| Field | Type | Description |
|---|---|---|
movedIndex | integer | The position the entry came from. |
queueCount | integer | Queue length afterwards. |
contentVersion | string | Already includes this change; pass it as expectedVersion to the next call. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Moves a queued entry to the front of the queue.
Only queue order changes — playlist membership is untouched. The relative order of the remaining entries is preserved. A missing index is refused with INVALID_PARAMS; an out-of-range value, an entry already at the top, or an empty queue all fail with INVALID_INDEX (Invalid index or already at top).
await fb2k.invoke('queue.moveToTop', { index: 3 });queue.remove
Remove one entry by index, or several by indices. An empty queue fails with NOT_FOUND whatever is given. An index past the end fails with INVALID_INDEX. In a batch, duplicate indices and those past the end of the queue are skipped; when none is in range the call fails with INVALID_INDEX, the failure carrying removedCount 0 and queueCount. Giving neither key, or a negative index, fails with INVALID_PARAMS.
| Parameter | Type | Required | Description |
|---|---|---|---|
index | integer | No | One queue position to remove. Takes precedence over indices. At least 0. |
indices | integer[] | No | Queue positions to remove; read only when index is absent. Each item: at least 0. |
expectedVersion | string | No | Expected content version for optimistic concurrency control. When mismatched, mutations are aborted with STALE_TARGET (details.current carries current version). Omitted, no check is made. |
Returns
| Field | Type | Description |
|---|---|---|
removedIndex | integer | The position removed; only when index was given. |
removedCount | integer | How many entries were removed; only when indices was given. |
queueCount | integer | Queue length afterwards. |
contentVersion | string | Already includes this change; pass it as expectedVersion to the next call. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Removes one or more entries from the queue.
Supply either index or indices, not both — index wins when present. Duplicate and out-of-range values in indices are skipped, and a batch that matches nothing fails with INVALID_INDEX carrying removedCount: 0. An empty queue fails with NOT_FOUND, an out-of-range index with INVALID_INDEX, and neither field with INVALID_PARAMS.
await fb2k.invoke('queue.remove', { indices: [0, 2] });queue.setContents
Replace the whole queue with an ordered list of references. One unusable reference fails the whole call before anything is written. An empty list clears the queue. A playlist index is read as the call arrives, so one taken before the playlist list changed queues a row of another playlist; playlistGuid keeps naming the playlist it was read from. An entry giving both playlist and playlistGuid, or a malformed GUID, fails with INVALID_PARAMS, a GUID no playlist has with NOT_FOUND.
| Parameter | Type | Required | Description |
|---|---|---|---|
items | QueueContentRef[] | Yes | The new queue, in order. At most max(256, current queue length) entries; more fail with INVALID_PARAMS. |
items[].queueIndex | integer | No | Position of an entry already in the queue. At least 0. |
items[].playlist | integer | No | Playlist of the row to add; needs item as well. At least 0. |
items[].playlistGuid | string | No | The playlist of the row to add by its guid, instead of playlist; needs item as well. |
items[].item | integer | No | Row in that playlist; needs playlist or playlistGuid as well. At least 0. |
items[].expectedPlaylistVersion | string | No | Only with a playlist row; given with queueIndex, the call fails with INVALID_PARAMS. |
expectedVersion | string | No | Expected content version for optimistic concurrency control. When mismatched, mutations are aborted with STALE_TARGET (details.current carries current version). Omitted, no check is made. |
Returns
| Field | Type | Description |
|---|---|---|
queueCount | integer | Queue length afterwards. |
contentVersion | string | Already includes this change; pass it as expectedVersion to the next call. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Replaces the entire queue with an ordered list of references.
An entry that matches neither shape fails the whole call before anything is written — unlike queue.add, which skips bad entries individually, any single invalid reference here rejects the entire call and leaves the queue untouched. queueCount is present on every response, success or failure. Passing an empty array clears the queue, equivalent to queue.clear. items.length is capped at max(256, current queue length); exceeding it fails without changing the queue.
Call pattern for drag-to-reorder UIs
Call setContents once when the user releases the drag, not on every intermediate frame — each call flushes and rebuilds the whole queue, so per-frame calls during a drag gesture are wasted work and can visibly stutter.
// Reorder: move queue slot 2 to the front, keep the rest in order
await fb2k.invoke('queue.setContents', {
items: [{ queueIndex: 2 }, { queueIndex: 0 }, { queueIndex: 1 }],
});
// Clear the queue
await fb2k.invoke('queue.setContents', { items: [] });queue.insertNext
Insert tracks so they play next, ahead of everything already queued. A track that is already queued is moved instead of queued twice. items land before paths; each block keeps its own order. A call with neither paths nor items fails with INVALID_PARAMS. Every entry of items is checked before any path is resolved, and one bad entry fails the whole call with nothing written: an entry naming its playlist by neither or both of playlist and playlistGuid, or by a malformed GUID, fails with INVALID_PARAMS, a GUID no playlist has with NOT_FOUND, a playlist index or row out of range with INVALID_INDEX. A path the media-root check refuses fails the whole call with PERMISSION_DENIED; a path that resolves to nothing is dropped and counted in invalidCount, and when nothing at all is left to insert the call fails with NOT_FOUND. A playlist row that changed while the paths were resolved fails the call with OPERATION_FAILED, and so does a queue that cannot be rebuilt. expectedVersion is compared when the queue is written, after the paths are resolved.
| Parameter | Type | Required | Description |
|---|---|---|---|
paths | string[] | No | File paths or URLs, each optionally with a |subsong:N suffix. The entries they produce carry no playlist position. |
items | QueueListRef[] | No | Playlist rows; the entries they produce carry that position, so the playback cursor follows them. At most max(256, current queue length) entries; more fail with INVALID_PARAMS. |
items[].playlist | integer | No | Playlist index; give this or playlistGuid. At least 0. |
items[].playlistGuid | string | No | The playlist's guid, instead of playlist. |
items[].item | integer | Yes | Row in that playlist. At least 0. |
items[].expectedPlaylistVersion | string | No | Act only while the playlist that the rows belong to still has this contentVersion, as its reads report it. A different version changes nothing and fails with STALE_TARGET, the current version carried as details.current. Omitted, no check is made. |
position | integer | No | Where to insert, counted after any moved entries are taken out; 0 is the front. At least 0. Default: 0. |
expectedVersion | string | No | Expected content version for optimistic concurrency control. When mismatched, mutations are aborted with STALE_TARGET (details.current carries current version). Omitted, no check is made. |
Returns
| Field | Type | Description |
|---|---|---|
insertedCount | integer | Tracks newly queued. |
movedCount | integer | Entries that were already queued and moved. |
queueCount | integer | Queue length afterwards. |
invalidCount | integer | Input path count minus the tracks resolved from paths, floored at zero. A folder or playlist file resolves to several tracks, so this is not a count of the paths that failed. |
contentVersion | string | Already includes this change; pass it as expectedVersion to the next call. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Inserts tracks so they play next, ahead of everything already queued. Entries are given as file paths, as playlist positions, or both.
¹ At least one of paths and items must be non-empty.
A track already present in the queue is moved to position instead of being queued a second time; only one occurrence moves, selected by the coordinate rules below. Within one call the items block lands first and the paths block after it; each block keeps its own order. insertedCount counts newly queued tracks and movedCount counts relocated entries. invalidCount is the input path count minus the number of tracks resolved from paths, floored at zero. It is not an exact count of failed paths: folder or container expansion can offset failures. items never counts toward invalidCount: one bad entry, such as a non-object, missing playlist or item, or an out-of-range position, fails the whole call with an error naming items[i]. Nothing is written, and the paths of the same call are not queued either.
All input entries are deduplicated by track, not by position. For duplicate items, the first supplied coordinate already present in the queue is retained; if none matches, the first supplied coordinate is used. The track keeps its first-occurrence order within the input block. An existing entry at that coordinate moves first; otherwise the first queued entry for the track moves and takes the supplied coordinate. A path-only move preserves the existing entry's usable coordinate; only a newly inserted path lacks one. queue.add and queue.setContents preserve duplicate references. A lower insertedCount + movedCount can reflect deduplication or invalid paths; folders and containers can also expand to multiple tracks.
When both parameters are omitted or empty, the invocation fails with INVALID_PARAMS (No paths or items specified). A non-array items fails with items must be an array. When no items are specified and paths resolves zero valid tracks, the call fails with NOT_FOUND (No valid tracks found), carrying invalidCount. Out-of-range coordinates fail with INVALID_INDEX, non-existent playlist items with NOT_FOUND, and playlist mutations during path resolution with OPERATION_FAILED, each naming the offending items[i].
Known limitation: legacy |subsong:N matching
A track that entered the queue through the path|subsong:N suffix accepted by queue.addPaths may fail to match existing queue entries when passed to insertNext, resulting in duplicate queueing rather than moving. This only affects tracks queued before upgrading; new calls are unaffected. For files with multiple subsongs, a bare path and a path with an explicit |subsong:0 suffix represent distinct track identities and do not deduplicate against each other.
Known limitation: the playlist cursor does not follow a track inserted by path
Entries added through paths carry no playlist position. foobar2000 treats them as played outside playlists: while such an entry plays, playback.getCurrentTrackIndex reports no position, and playback resumes from the item following the track that was playing before the queue item was consumed, rather than from the inserted track's position. When the inserted track also exists in the active playlist, the sequence continues from its pre-queue position (e.g. item 8 → queued item → item 9), and the track will play again when normal playlist progression reaches it. Entries added through items carry explicit playlist coordinates, so playback advances from that position. When a track's playlist location is known, supply items instead. Existing entries whose playlist position has become invalid fall back to the same position-less form after moveToTop, setContents, or playNow rebuild the queue.
playback.previous is affected the same way. foobar2000 resolves "previous" from the cursor position in playlist order, not from playback history, and a consumed queue entry is never put back. While a position-less entry plays, previous lands on the item before the one that was interrupted (the interrupted track itself is skipped); while a positioned entry plays, it lands on the item before that entry's own playlist position. Neither returns to the interrupted track.
// By path: the entry carries no playlist position
await fb2k.invoke('queue.insertNext', { paths: ['C:\\Music\\a.flac'], position: 0 });
// By playlist position: the cursor follows the entry once it plays
await fb2k.invoke('queue.insertNext', { items: [{ playlist: 0, item: 12 }] });queue.playNow
Play the queue entry at index now, moving it to the front first when it is not already there. An empty queue fails with NOT_FOUND, an index past the end with INVALID_INDEX. When the entry has to be moved and the queue cannot be rebuilt, the call fails with OPERATION_FAILED and playback is not started.
| Parameter | Type | Required | Description |
|---|---|---|---|
index | integer | No | Queue position to play. At least 0. Default: 0. |
expectedVersion | string | No | Expected content version for optimistic concurrency control. When mismatched, mutations are aborted with STALE_TARGET (details.current carries current version). Omitted, no check is made. |
Returns
| Field | Type | Description |
|---|---|---|
playedIndex | integer | The position that was played. |
queueCount | integer | Queue length read right after playback started; the host may consume the played entry before or after that read. |
contentVersion | string | Read together with queueCount. |
success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.
Plays the queue entry at index immediately, promoting it to the front of the queue first if it is not already there.
Fails with NOT_FOUND (Queue is empty) when nothing is queued, or INVALID_INDEX (Invalid queue index) for an out-of-range index; a negative or non-integer value is refused by parameter validation.
queueCount timing is not guaranteed
queueCount is read immediately after playback starts. The host does not guarantee that its consumption of the queue head happens synchronously with that read, so the value may reflect the queue either just before or just after the played entry is removed. Call queue.getCount afterward if the exact post-play length matters.
// Play whatever is at the head of the queue
await fb2k.invoke('queue.playNow');
// Play the 3rd entry, promoting it to the head first
await fb2k.invoke('queue.playNow', { index: 2 });Explicit playback clears the queue
playlist.playTrack, playlist.replaceAllAndPlay, playback.playPath, playback.playPaths, and jitQueue.preloadBatch when it starts playback (first call, or replace: true) all go through foobar2000's default-action path, which flushes the whole playback queue first. This is core behaviour, not something the component adds: the core implements "play this item" as clear the queue, enqueue the target, consume it, which is why you observe three playback:queueChanged events in a row (user_removed with count: 0, user_added, playback_advance). None of these calls can be asked to keep the queue.
Play a playlist item while keeping the queue. Put the target at the head with setContents, then consume it with playNow:
const res = await fb2k.invoke('queue.getCount');
if (res.success === false) throw new Error(res.error);
const { count } = res;
await fb2k.invoke('queue.setContents', {
items: [
{ playlist, item }, // the track to play now
...Array.from({ length: count }, (_, i) => ({ queueIndex: i })), // existing entries, in order
],
});
await fb2k.invoke('queue.playNow'); // consumes the head, leaves the rest queuedAfter the count query, this uses two calls and one queue rebuild. The played track enters the queue with a playlist position, so the cursor follows it and the "cursor does not follow" limitation above does not apply.
Restore a queue that was cleared. The host keeps no record of flushed entries, so snapshot queue.get before the call that starts playback. Entries whose playlist is not null can be added through setContents or insertNext({ items }); entries with playlist: null need insertNext({ paths }). Both forms can share one insertNext call, but it groups coordinates before paths and deduplicates by track. Use setContents afterwards to restore ordering and duplicate counts, not necessarily the original coordinate forms: if one track originally had both a coordinate entry and a coordinate-less entry, copying its remaining queueIndex also copies that entry's coordinate. Recreating every entry by path loses playlist coordinates and may collapse duplicates.