Skip to content

AutoAdvancePlugin

Most players need nothing more than “when the current track ends, play the next one.” That is exactly what AutoAdvancePlugin does by default. You can also tell it to buffer the next track a few seconds early, or hand off to a crossfade instead of a hard cut. The plugin wires to two player events: ended for the actual advance, and trackEndingSoon for the early-warning preload and crossfade path.

The one time you leave this plugin out is when something else already drives next(), such as a WebSocket sync layer or a Cast session orchestrator. In that case let the orchestrator call next() and skip this plugin entirely.

TypeScript
import nmMPlayer from '@nomercy-entertainment/nomercy-music-player';
import { AutoAdvancePlugin } from '@nomercy-entertainment/nomercy-music-player/plugins';
import type { MusicPlaylistItem } from '@nomercy-entertainment/nomercy-music-player';

Plugin id: 'auto-advance'

Options

OptionTypeDefaultDescription
enabledbooleantrueMaster toggle. Set to false to pause all auto-advance behaviour without removing the plugin.
preloadNextOnEndingbooleanfalseWhen true, calls player.load(next, { slot: 'next' }) on the trackEndingSoon event, buffering the next track before it is needed.
crossfadebooleanfalseWhen true, calls player.crossfadeTo(next, { duration: crossfadeDuration }) on the trackEndingSoon event.
crossfadeDurationnumber0Crossfade duration in seconds when crossfade: true. 0 means a hard cut. Set this to match or be less than trackEndingSoonThreshold in the player config.

Methods

advance()

TypeScript
advance(): Promise<void>

Force-advance to the next track immediately, regardless of playback state. Calls player.next({ source: 'auto-advance' }).

preloadNext()

TypeScript
preloadNext(): Promise<void>

Peek the queue head and load it into the next slot. No-ops when there is no next track. Safe to call at any time.

addEndedHandler(fn)

TypeScript
addEndedHandler(fn: () => void | Promise<void>): void

Register an additional callback that runs after the built-in ended advance logic. Use this for analytics, logging, or server sync that should follow the queue advance.

addPreloadHandler(fn)

TypeScript
addPreloadHandler(fn: (next: MusicPlaylistItem | undefined) => void | Promise<void>): void

Register an additional trackEndingSoon handler for preload behaviour. next is the upcoming track, or undefined when the queue is exhausted.

addCrossfadeHandler(fn)

TypeScript
addCrossfadeHandler(fn: (next: MusicPlaylistItem | undefined, duration: number) => void | Promise<void>): void

Register an additional trackEndingSoon handler for crossfade behaviour. duration is the resolved crossfadeDuration in seconds.

Execution order

When trackEndingSoon fires:

  1. If preloadNextOnEnding: true, calls preloadNext()
  2. If crossfade: true and there is a next track, calls crossfadeTo(next, { duration })
  3. All registered preloadHandlers run
  4. All registered crossfadeHandlers run

When ended fires:

  1. Calls player.next({ source: 'auto-advance' })
  2. All registered endedHandlers run

Registration

TypeScript
const player = nmMPlayer('main')
.addPlugin(AutoAdvancePlugin)
.setup({ playlist: myTracks });

With options:

TypeScript
player.addPlugin(AutoAdvancePlugin, {
enabled: true,
preloadNextOnEnding: true,
crossfade: true,
crossfadeDuration: 5,
});

Full example with crossfade, preload, and an analytics hook:

TypeScript
const player = nmMPlayer('main')
.addPlugin(AutoAdvancePlugin, {
crossfade: true,
crossfadeDuration: 5,
preloadNextOnEnding: true,
})
.setup({
baseUrl: 'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Music',
trackEndingSoonThreshold: 8,
crossfadeDefaults: {
duration: 5,
curve: 'equal-power',
},
playlist: [
{
id: 'kjc-01',
name: 'Thaw You Out',
url: '/D/Derek%20Clegg/%5B2010%5D%20KJC/01%20Thaw%20You%20Out.mp3',
artist: 'Derek Clegg',
},
{
id: 'kjc-02',
name: 'Hope',
url: '/D/Derek%20Clegg/%5B2010%5D%20KJC/02%20Hope.mp3',
artist: 'Derek Clegg',
},
],
});

const plugin = player.getPlugin(AutoAdvancePlugin)!;

plugin.addEndedHandler(() => {
analytics.track('track_completed', { id: player.item()?.id });
});

player.on('ready', () => {
player.item(0, { autoplay: true });
});

See also