Library Handler

A LibraryHandler is the plugin component that exposes a music source to Pipe Bomb. It is responsible for discovering tracks, verifying they still exist, and producing audio for playback.

Registration

ctx.registerLibraryHandler({
	id: "my-library",
	enable(apiCtx) {
		/* called once */
	},
	getName() {
		return "My Music Library";
	},
	async scan(taskCtx) {
		/* discover tracks */
	},
	async doTracksExist(trackIds) {
		/* verify */ return trackIds;
	},
	async getAudioProducer(trackId, type) {
		/* return producer */
	},
});

One plugin can call registerLibraryHandler multiple times to expose multiple libraries.

Interface

id

Unique string within the plugin. Combined with the plugin ID to form a (pluginId, libraryId) pair that identifies every track in the database.

enable(apiCtx)

Called once when the library is registered. The LibraryHandlerApiContext gives you:

Method Purpose
addTrack(track, runId) Register a track with the server during a scan
removeTrack(id) Remove a track by its track ID
useAttributeSource(source) Register an inline attribute source scoped to this library
registerPluginTask(task) Register a task specific to this library

getName()

Returns the human-readable name shown in the Libraries settings page. This can be static or read from plugin config.

scan(taskCtx)

Called by the server when a library scan is triggered. Your implementation should walk the source (filesystem, API, etc.) and call addTrack() for every track it finds.

async scan(taskCtx) {
    const tracks = await discoverMyTracks();
    for (const [i, track] of tracks.entries()) {
        await apiCtx.addTrack({
            id: track.uniqueId,
            /* Track fields */
        }, taskCtx.getRunId());
        taskCtx.update(i / tracks.length);
    }
}

The runId passed to addTrack is stored on the track record. After scanning, the server uses it to detect tracks that weren't reported in this run and calls doTracksExist on them.

doTracksExist(trackIds)

Given a list of track IDs that were not seen in the latest scan, return the subset that still exist. Tracks not returned are deleted from the database.

async doTracksExist(trackIds) {
    return trackIds.filter(id => mySource.has(id));
}

getAudioProducer(trackId, type)

Called when a user plays a track. Return an AudioProducer matching the requested type, or null if the type cannot be served.

See development/Streaming for AudioProducer details and the difference between stream and HLS modes.

Track object

The Track passed to addTrack is a plain object:

{
	id: string; // your unique ID for this track within this library
	// any other fields your attribute source will use
}

Metadata (title, duration, album art) comes from development/Attribute-Source, not from addTrack. The track id is all the server stores from the scan itself.