Skip to content

Lyrics API ​

The lyrics namespace provides methods to query, read, and save embedded tags and sidecar lyrics files.

SDK Wrapper

We recommend using the fb.lyrics.* SDK wrapper rather than constructing raw JSON parameters manually.

lyrics ​

lyrics.exists ​

List the known lyrics tags and sidecar files that exist for a track. File candidates and access checks match lyrics.get, but contents are not read: empty tags and files still count as existing.

ParameterTypeRequiredDescription
pathstringYesTrack path; a |subsong:N suffix selects a subsong. Must not be empty.
filenamestringNoExact file name, including any extension, next to the audio. Replaces the automatic file candidates; embedded tags are still checked. Empty means automatic lookup. The same Windows filename rules and INVALID_PARAMS failure as lyrics.get apply.

Returns

FieldTypeDescription
existsbooleanWhether any source has lyrics.
sourcesstring[]Every source found: embedded for a lyrics tag, then file:<name> for each sidecar file, in the same candidate order as lyrics.get with format=any, or just the explicit file.

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

js
const res = await fb2k.invoke('lyrics.exists', {
	path: 'C:\\Music\\song.flac',
});
if (res.success === false) throw new Error(res.error);
const { exists, sources } = res;

lyrics.get ​

Read non-empty lyrics from the five known lyrics tags, then files next to the audio. Automatic lookup tries the audio file's stem only for a verified single-track file, followed by <first artist> - <title>; subsongs use only the latter. Unknown input layouts skip the audio-stem candidate. No numbered or shared album fallback is used. Results are cached for 1200 ms per track, filename and filters. Successful file saves and metadata changes invalidate the cache. Invalid UTF-8 in the returned lyrics is replaced with U+FFFD; no encoding is guessed and the source is not rewritten.

ParameterTypeRequiredDescription
pathstringNoTrack path; a |subsong:N suffix selects a subsong. Omit it for the playing track; with nothing playing the call fails with NO_ACTIVE_ITEM. The playing track keeps its subsong. Must not be empty.
filenamestringNoExact file name, including any extension, next to the audio. A non-empty value replaces all automatic file candidates and ignores format; source and type still apply. Empty means automatic lookup. Must be a valid single Windows filename, without path syntax, device names or trailing dots/spaces; invalid names fail with INVALID_PARAMS.
source"embedded" | "file" | "any"NoWhere to look: the embedded tags, the sidecar files, or both in that order. Default: "any".
type"synced" | "unsynced" | "any"NoKeep only synced lyrics (with LRC timestamps) or only unsynced ones. Default: "any".
format"lrc" | "txt" | "any"NoSidecar extension to try; .lrc candidates are tried before .txt with any. Applies to automatic sidecar lookup only; a non-empty filename takes precedence. Default: "any".

Returns

FieldTypeDescription
availablebooleanWhether lyrics were found.
pathstringThe path looked up: as given, or the playing track's path including its subsong suffix.
source"embedded" | "file"Where the lyrics came from; present when available is true.
sourcePathstringFull path of the sidecar file; present when source is file.
tagNamestringThe tag that matched a type filter, one of LYRICS, UNSYNCED LYRICS, UNSYNCEDLYRICS, SYNCEDLYRICS, SYNCED LYRICS; present when source is embedded, type was not any and a known tag matched.
lyricsstringThe lyrics text; present when available is true.
syncedbooleanWhether the text carries LRC timestamps; present when available is true.

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

js
const res = await fb2k.invoke('lyrics.get', {
	path: 'C:\\Music\\song.flac',
	type: 'synced',
});
if (res.success === false) throw new Error(res.error);
const { available, lyrics, synced } = res;

lyrics.save ​

Save lyrics to a sidecar file, an embedded tag, or both. A file save invalidates the read cache; an embedded save dispatches an asynchronous write and metadata changes invalidate the cache. Without filename, verified single-track files use the audio stem; other tracks use <first artist> - <title>. Missing tags in the latter case fail that target with INVALID_PARAMS. Existing files are overwritten, so use distinct filenames when tracks have the same artist and title. With one target the result describes that write; with several, each outcome is under results and OPERATION_FAILED means all failed.

ParameterTypeRequiredDescription
pathstringYesTrack path the lyrics belong to; a |subsong:N suffix selects a subsong. Must not be empty.
lyricsstringYesLyrics text to save. Must not be empty.
targetstring[]NoWhere to write: file (a sidecar next to the audio file), embedded (a tag in the file), or all for both; several entries write to each. Any other value fails with INVALID_PARAMS. Omitted means file. Must not be empty.
filenamestringNoExact file name, including any extension, for the file target. A non-empty value replaces the derived name and ignores format; empty means the default name. The same Windows filename rules and INVALID_PARAMS failure as lyrics.get apply.
tagNamestringNoTag the embedded target writes. Default: "LYRICS".
format"lrc" | "txt"NoExtension for the default sidecar name; ignored when filename is non-empty. Default: "lrc".

Returns

FieldTypeDescription
savedTostringFull path of the file written; present for a single file target.
dispatchedbooleantrue when a single embedded target dispatched the tag write; the final outcome arrives as metadata:writeComplete.
pathstringThe path as given; present for a single embedded target.
handlePathstringPath of the handle the tag write targets; present for a single embedded target.
subsongintegerSubsong the tag write targets; present for a single embedded target.
tagsAppliedRecord<string, string>The tag written, keyed by its upper-case name; present for a single embedded target.
tagsSetintegerNumber of tags set; present for a single embedded target.
tagsRemovedintegerNumber of tags removed; present for a single embedded target.
notestringA note about the dispatch; present for a single embedded target.
resultsRecord<string, any>With several targets: each target's own envelope ({ success, savedTo }, the tag write receipt, or { success: false, error, code }) under file or embedded.

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

js
await fb2k.invoke('lyrics.save', {
	path: 'C:\\Music\\song.flac',
	lyrics: '[00:12.00]First line\n[00:18.50]Second line\n',
});

Target options ​

TargetBehavior
fileWrites to the audio file directory; see the next section for default naming
embeddedAsynchronously writes to track metadata tags; listen for metadata:writeComplete for the final result
allWrites both the sidecar file and the embedded tag, reporting each outcome

The bridge target parameter is an array (defaults to ["file"], while the SDK also accepts a single string). Whether a container and track support embedded tag writes depends on the foobar2000 input decoder plugin; a successful dispatch indicates the task was queued, not that disk write has completed.

Sidecar lookup ​

Only the audio file's directory is searched. The profile folder and subdirectories are not searched, and names are not matched by similarity.

OrderFile nameWhen used
1The audio stem, such as song.lrc for song.wavThe input reader confirms exactly one track, with subsong ID 0
2<first artist> - <title>Both tags exist; invalid filename characters become _

All .lrc candidates are tried before any .txt candidates. An unknown input layout skips the audio-stem candidate. Missing artist or title skips the tagged candidate. Multiple artist values are not joined, and remix or live-version names are not treated as interchangeable.

lyrics.get returns the first readable, non-empty file matching type. lyrics.exists uses the same candidates and access checks but reports all existing sources, such as file:song.lrc. Empty files and known empty tags count as existing, so exists: true does not guarantee get.available: true.

Portable file-relative:// paths are supported. Archive entries (archive://, unpack://) do not identify a directory in which sidecars can be accessed.

Containers and subsongs ​

A subsong is an input plugin's track identifier, not an ordinal that can always be incremented to obtain a track number. Even subsong 0 in a multi-track container skips the shared audio-stem file.

Subsongs use the artist-title name for automatic reads and default saves. If either tag is missing, the file save fails with INVALID_PARAMS; specify filename instead. Tracks with identical artist and title may share a filename. Saves overwrite existing content, so use distinct names when needed.

Explicit filenames ​

All three methods accept filename, including its extension. A non-empty value restricts file access to that one name with no fallback, and overrides format; other extensions are allowed. source and type still apply. An empty string means the default lookup or name.

With source: 'any', embedded tags still take priority; use source: 'file' to read only the selected file. exists still checks embedded tags. The name must be a valid single Windows filename, without path separators, reserved device names, invalid characters or trailing dots/spaces.

javascript
const path = 'D:\\album.flac|subsong:2';
const filename = 'chosen-track.lrc';

const saved = await fb.lyrics.save(path, '[00:01.00]Track lyrics', {
    target: 'file',
    filename,
});
if (saved.success === false) throw new Error(saved.error);

const found = await fb.lyrics.exists(path, { filename });
if (found.success === false) throw new Error(found.error);

const result = await fb.lyrics.get(path, { source: 'file', filename });
if (result.success === false) throw new Error(result.error);
if (result.available) console.log(result.lyrics);

Save targets and cache ​

The Bridge takes target as an array, defaulting to ['file'] (the SDK also accepts a single string). file writes next to the audio file, embedded dispatches a tag write, and all writes to both and reports each outcome. Support for writing container tags depends on the foobar2000 input plugin.

Successful file saves and metadata changes invalidate the 1200 ms read cache. Embedded saves are asynchronous: subscribe to metadata:writeComplete before saving, then read after completion. Successful dispatch confirms queuing rather than disk persistence; see Metadata API for event subscription.

Migrating older callers ​

The config save target has been removed; all writes to file and embedded only. Legacy lyrics files stored in the profile folder are neither deleted nor searched.

Numbered .NN.lrc and shared album-stem files are no longer matched automatically for subsongs. If you know which track an existing file belongs to, pass its exact filename without renaming it.