Playlist Generator
Port for “what comes next?” queue decision logic, the IPlaylistGenerator contract. Receives the current queue snapshot and the currently-playing index, returns the index of the next item (or undefined for end-of-queue).
TypeScript
import type { IPlaylistGenerator } from '@nomercy-entertainment/nomercy-music-player/adapters/playlist-generator';
Interface
TypeScript
interface IPlaylistGenerator<T extends BasePlaylistItem = BasePlaylistItem> {
readonly id: string;
next(items: ReadonlyArray<T>, currentIndex: number): number | undefined;
previous(items: ReadonlyArray<T>, currentIndex: number): number | undefined;
}
next(items, currentIndex)
Resolve the next item index. currentIndex is -1 when nothing is playing. Return undefined to signal end-of-queue (no next track).
previous(items, currentIndex)
Resolve the previous item index. Return undefined when there is no previous item.
Built-in adapters
LinearPlaylistGenerator
TypeScript
import { LinearPlaylistGenerator } from '@nomercy-entertainment/nomercy-music-player/adapters/playlist-generator';
Plays items in order (0, 1, 2, …). The default.
SmartShuffleGenerator
TypeScript
import { SmartShuffleGenerator } from '@nomercy-entertainment/nomercy-music-player/adapters/playlist-generator';
Tag-aware shuffle within the library. It penalizes playing the same genre or decade back-to-back when there is enough variety to avoid it, and falls back to uniform random when items carry no tags.
Custom implementation: server-side recommendations
TypeScript
import type { IPlaylistGenerator } from '@nomercy-entertainment/nomercy-music-player/adapters/playlist-generator';
import type { MusicPlaylistItem } from '@nomercy-entertainment/nomercy-music-player';
class RecommendationGenerator implements IPlaylistGenerator<MusicPlaylistItem> {
readonly id = 'recommendation-engine';
private nextIdCache: string | number | null = null;
// Pre-fetch the next recommended track:
async prefetchNext(current: MusicPlaylistItem): Promise<void> {
const response = await fetch(`/api/recommend?after=${current.id}`);
const data = await response.json();
this.nextIdCache = data.nextTrackId;
}
next(items: ReadonlyArray<MusicPlaylistItem>, currentIndex: number): number | undefined {
if (this.nextIdCache !== null) {
const idx = items.findIndex((item) => item.id === this.nextIdCache);
this.nextIdCache = null;
if (idx >= 0) return idx;
}
// Fallback: linear
const next = currentIndex + 1;
return next < items.length ? next : undefined;
}
previous(items: ReadonlyArray<MusicPlaylistItem>, currentIndex: number): number | undefined {
const prev = currentIndex - 1;
return prev >= 0 ? prev : undefined;
}
}
See also
- Similarity Engine: the port for “more like this” / radio in your own recommendation flows
- Configuration: where adapters are wired
- Events:
queue:exhaustedfires whennext()returnsundefined