Skip to content

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.

ParameterTypeRequiredDescription
playlistintegerNoPlaylist 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.
playlistGuidstringNoPersistent 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.
tracksinteger[]NoRows to queue, in order. Takes precedence over track. Each item: at least 0.
trackintegerNoA single row to queue; read only when tracks is absent. At least 0.
expectedPlaylistVersionstringNoAct 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

FieldTypeDescription
addedCountintegerHow many rows were queued.
queueCountintegerQueue 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.

js
// 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.

ParameterTypeRequiredDescription
pathsstring[]YesFile 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.
useQueuePlaylistbooleanNoAppend to the dedicated [WebView Queue] playlist, creating it when missing. false uses playlist, or the active playlist when that is omitted too. Default: true.
playlistintegerNoTarget 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.
playlistGuidstringNoThe target playlist's guid, instead of playlist; like playlist it is read only when useQueuePlaylist is false, and not checked at all otherwise.

Returns

FieldTypeDescription
addedCountintegerHow many tracks were appended and queued.
invalidCountintegerEntries 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.
playlistintegerThe playlist the tracks were appended to.
playlistGuidstringUnique identifier of the playlist, formatted as {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}. Pass as playlistGuid to reliably target this playlist.
queueCountintegerQueue 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.

js
await fb2k.invoke('queue.addPaths', { paths: ['C:\\Music\\a.flac'] });

queue.clear ​

Empty the queue.

This method takes no parameters.

Returns

FieldTypeDescription
clearedCountintegerHow many entries the queue held.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
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

FieldTypeDescription
clearedCountintegerHow many entries the queue held.

success is true on success. On failure the response is { success: false, error, code }; see Error codes for code.

js
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

FieldTypeDescription
itemsQueueItem[]The entries, in play order.
items[].…TrackEvery field of Track.
items[].queueIndexintegerPosition in the queue, from 0.
items[].playlistinteger | nullPlaylist 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[].playlistGuidstring | nullGUID of that playlist, as playlistGuid takes it; null exactly when playlist is null.
items[].playlistIteminteger | nullRow in that playlist; null exactly when playlist is null.
countintegerNumber of entries.
contentVersionstringPass 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.

js
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

FieldTypeDescription
countintegerNumber of entries.
hasItemsbooleanWhether 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.

js
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.

ParameterTypeRequiredDescription
indexintegerYesPosition of the entry to promote; 0 is already the front and is refused. At least 0.
expectedVersionstringNoExpected 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

FieldTypeDescription
movedIndexintegerThe position the entry came from.
queueCountintegerQueue length afterwards.
contentVersionstringAlready 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).

js
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.

ParameterTypeRequiredDescription
indexintegerNoOne queue position to remove. Takes precedence over indices. At least 0.
indicesinteger[]NoQueue positions to remove; read only when index is absent. Each item: at least 0.
expectedVersionstringNoExpected 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

FieldTypeDescription
removedIndexintegerThe position removed; only when index was given.
removedCountintegerHow many entries were removed; only when indices was given.
queueCountintegerQueue length afterwards.
contentVersionstringAlready 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.

js
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.

ParameterTypeRequiredDescription
itemsQueueContentRef[]YesThe new queue, in order. At most max(256, current queue length) entries; more fail with INVALID_PARAMS.
items[].queueIndexintegerNoPosition of an entry already in the queue. At least 0.
items[].playlistintegerNoPlaylist of the row to add; needs item as well. At least 0.
items[].playlistGuidstringNoThe playlist of the row to add by its guid, instead of playlist; needs item as well.
items[].itemintegerNoRow in that playlist; needs playlist or playlistGuid as well. At least 0.
items[].expectedPlaylistVersionstringNoOnly with a playlist row; given with queueIndex, the call fails with INVALID_PARAMS.
expectedVersionstringNoExpected 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

FieldTypeDescription
queueCountintegerQueue length afterwards.
contentVersionstringAlready 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.

js
// 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.

ParameterTypeRequiredDescription
pathsstring[]NoFile paths or URLs, each optionally with a |subsong:N suffix. The entries they produce carry no playlist position.
itemsQueueListRef[]NoPlaylist 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[].playlistintegerNoPlaylist index; give this or playlistGuid. At least 0.
items[].playlistGuidstringNoThe playlist's guid, instead of playlist.
items[].itemintegerYesRow in that playlist. At least 0.
items[].expectedPlaylistVersionstringNoAct 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.
positionintegerNoWhere to insert, counted after any moved entries are taken out; 0 is the front. At least 0. Default: 0.
expectedVersionstringNoExpected 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

FieldTypeDescription
insertedCountintegerTracks newly queued.
movedCountintegerEntries that were already queued and moved.
queueCountintegerQueue length afterwards.
invalidCountintegerInput 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.
contentVersionstringAlready 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.

js
// 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.

ParameterTypeRequiredDescription
indexintegerNoQueue position to play. At least 0. Default: 0.
expectedVersionstringNoExpected 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

FieldTypeDescription
playedIndexintegerThe position that was played.
queueCountintegerQueue length read right after playback started; the host may consume the played entry before or after that read.
contentVersionstringRead 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.

js
// 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:

js
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 queued

After 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.