Skip to content

Import

TypeScript
import type {
IPlayer,
WithCurrentItem,
BasePlayerConfig,
BaseEventMap,
BasePlaylistItem,
AuthConfig,
CastConfig,
DrmConfig,
ActionOptions,
LoadOptions,
BeforeEvent,
} from '@nomercy-entertainment/nomercy-player-core';

IPlayer

Minimum surface both NMMusicPlayer and NMVideoPlayer satisfy. Plugins typed against IPlayer<E> work uniformly against either player.

TypeScript
interface IPlayer<E extends BaseEventMap = BaseEventMap> extends DispatchTarget {
// Identity
readonly playerId: string;
readonly id: string; // alias for playerId
readonly container: HTMLElement;

// Event system
on<K extends keyof E>(event: K, fn: (data: E[K]) => void): void;
off<K extends keyof E>(event: K, fn: (data: E[K]) => void): void;
once<K extends keyof E>(event: K, fn: (data: E[K]) => void): void;
emit<K extends keyof E>(event: K, data?: E[K]): void;
hasListeners<K extends keyof E>(event: K): boolean;

// Runtime URL
baseUrl(): string | undefined;
baseUrl(url: string): void;

// Audio context
audioContext(): AudioContext | undefined;

// URL resolution
resolveUrl(url: string, category?: UrlCategory): Promise<ResolvedUrl>;
urlResolver(): IUrlResolver | undefined;
urlResolver(resolver: IUrlResolver | undefined): void;

// Phase + dispatch
phase(): PlayerPhase;
dispatching(): ReadonlyArray<string>;

// Experimental override surface
readonly experimental: PlayerExperimental;

// i18n
t(key: string, vars?: Record<string, string>): string;
language(): string;
language(lang: string): Promise<void>;
addTranslations(bundle: Translations): void;
translation(lang: string, key: string): string | undefined;
translation(lang: string, key: string, value: string): void;
removeTranslations(prefix: string, lang?: string): void;

// Cue parsers
registerCueParser(parser: ICueParser, prepend?: boolean): void;
unregisterCueParser(id: string): void;
resolveCueParser(url: string): ICueParser | undefined;

// Auth
auth(): Readonly<AuthConfig> | undefined;
auth(config: AuthConfig): void;
auth(partial: Partial<AuthConfig>): void;

// Metrics
metrics(): PlaybackMetrics;

// Tracks (playback)
subtitle(): CurrentSubtitleSelection | null;
subtitle(idx: number | null): void;
audioTrack(): CurrentAudioTrackSelection | null;
audioTrack(idx: number): void;
quality(): CurrentQualitySelection | 'auto';
quality(idx: number | 'auto'): void;
chapters(): ReadonlyArray<Chapter>;
chapter(): Chapter | null;
}

metrics()

Returns a PlaybackMetrics snapshot — always available, no extra constraint needed. metrics() is on IPlayer directly, so any plugin typed against IPlayer<E> can call it without a cast.

playerId vs id

Both always return the same value. id is the shorthand; playerId is the stable explicit form used internally.

Phantom event map

TypeScript
declare readonly __eventMap__: E;

This phantom property exists purely for TypeScript inference. It is never set at runtime. PlayerEventMap<P> in the plugin base reads it to extract E from a concrete player type without hitting conditional-type widening.

WithCurrentItem

Capability interface for plugins that need to read the active playlist item. IPlayer<E> is intentionally item-type-agnostic; item() is not on it. Plugins that need the active item add & WithCurrentItem<YourItemType> to their P generic.

TypeScript
interface WithCurrentItem<T extends BasePlaylistItem = BasePlaylistItem> {
item(): T | undefined;
}

Both NMMusicPlayer<T> and NMVideoPlayer<T> satisfy this interface structurally — no implements required.

TypeScript
import { Plugin } from '@nomercy-entertainment/nomercy-player-core';
import type {
IPlayer,
WithCurrentItem,
BaseEventMap,
BasePlaylistItem,
} from '@nomercy-entertainment/nomercy-player-core';

class MyPlugin<
P extends IPlayer<BaseEventMap> & WithCurrentItem<BasePlaylistItem>,
I extends BasePlaylistItem = BasePlaylistItem,
> extends Plugin<P, BaseEventMap, I> {
onEnded(): void {
const item = this.player.item(); // BasePlaylistItem — typed via I
const snapshot = this.player.metrics(); // PlaybackMetrics — no constraint needed
}
}

Plugins that don’t read the active item omit & WithCurrentItem<...>; metrics(), getPlugin(), and transport methods are on IPlayer directly.

BasePlayerConfig

Full configuration passed to setup(). All fields are optional, and the player applies safe defaults.

TypeScript
interface BasePlayerConfig {
// Auth
auth?: AuthConfig;

// Logging
logger?: ILogger;
logLevel?: LogLevel;

// Storage
storage?: IStorage;

// Platform adapter
platform?: IPlatform;

// Realtime / WebSocket factory
websocketFactory?: RealtimeFactory;

// Translator adapter (server-side i18n)
translator?: ITranslator;

// URL resolver
urlResolver?: IUrlResolver;

// Base URL for relative media URLs
baseUrl?: string;

// Before-event timeout (ms). Default 10 000.
beforeEventTimeoutMs?: number;

// Plugin init timeout (ms). Default 30 000.
pluginInitTimeoutMs?: number;

// Cast
cast?: CastConfig;

// Playlist / initial load
playlist?: BasePlaylistItem[] | string;

// Custom cue parsers
cueParsers?: ICueParser[];

// Mutation guards
mutationGuards?: false | 'all' | ReadonlyArray<string>;

// Preload / transition strategies
preloadStrategy?: IPreloadStrategy;
transitionStrategy?: ITransitionStrategy;

// i18n
language?: string;
translations?: Translations;
loadTranslations?: TranslationLoader;
onMissingTranslation?: (key: string, lang: string) => string;

// Initial volume (0–100 scale)
defaultVolume?: number;
}

Key config fields

FieldTypeDefaultDescription
authAuthConfignoneBearer token, headers, transformUrl, signRequest, refresh hook
logLevelLogLevel'info'Verbosity threshold
baseUrlstringnonePrefixed to all relative media URLs
beforeEventTimeoutMsnumber10000Max wait for delay() promises in before-events
pluginInitTimeoutMsnumber30000Max wait for async use() per plugin
platformIPlatformbrowserPlatformSwap wake-lock, network, fullscreen, PiP adapters (spread browserPlatform to override one)
mutationGuardsfalse | 'all' | ReadonlyArray<string>noneWhich mutations emit beforeMutation
preloadStrategyIPreloadStrategyDefaultPreloadStrategyWhen and what to buffer ahead
transitionStrategyITransitionStrategyGaplessTransitionStrategyHow track boundaries feel

AuthConfig

Unified auth pipeline applied to every core-internal fetch. Hard rules:

  • 401 (unauthenticated) may invoke refreshOnUnauthenticated, then retry once.
  • 403 (unauthorized) propagates immediately. Never refreshed, never retried.
TypeScript
interface AuthConfig {
bearerToken?: string | (() => string) | (() => Promise<string>);
headers?: Record<string, string | (() => string) | (() => Promise<string>)>;
credentials?: 'omit' | 'same-origin' | 'include';
transformUrl?: (url: string) => string | Promise<string>;
signRequest?: (request: Request) => Request | Promise<Request>;
refreshOnUnauthenticated?: () => Promise<void>;
retryAfterRefresh?: number; // default 1
}

All bearerToken and headers values accept a static string, a sync getter, or an async getter, so Vue refs, signals, and reactive stores all work:

TypeScript
auth: {
bearerToken: () => myStore.token,
refreshOnUnauthenticated: async () => {
await myStore.refresh();
},
}

CastConfig

TypeScript
interface CastConfig {
autoLoad?: boolean; // default false; auto-injects Cast SDK on first transferTo
receiverApplicationId?: string; // default: standard Google media receiver
// plus autoJoinPolicy, resumeSavedSession, scriptUrl, loadTimeoutMs
// — see the Cast page for the full interface and defaults
}

DrmConfig

Passed to a library-specific DRM plugin (e.g. DrmPlugin in the video package), not directly to BasePlayerConfig.

TypeScript
interface DrmConfig {
keySystem: string; // e.g. 'com.widevine.alpha', 'com.apple.fps'
licenseUrl: string;
certificate?: ArrayBuffer | string;
customSignRequest?: (request: Request) => Request | Promise<Request>;
}

BaseEventMap

Complete event map emitted by every core-based player. Library-specific maps (MusicEventMap, VideoEventMap) extend this.

The table below shows the top-level groups. For the full per-event payload reference see Events Reference.

GroupExample keys
Setup lifecyclebeforeSetup, setupStart, ready, playlistReady, mediaReady
Setup errorssetupStartError, pluginsRegisteringError, playlistResolveError, mediaReadyError
Play-pathbeforePlay, play, playing, beforePause, pause, stop, ended
SeekbeforeSeek, seek, seeked, seekPrevented
Mutation guardsbeforeMutation, mutationPrevented
Timetime, duration, progress
Volume / modevolume, mute, repeat, shuffle
Queuequeue:append, queue:prepend, queue:insert, queue:remove, queue:clear, queue:exhausted, current
Media trackssubtitle, subtitleCue, audioTrack, qualityState, chapters
Network / visibilitynetwork:online, network:offline, network:slow, visibility:visible, visibility:hidden
Authauth:refreshed, auth:failed
CastcastState
Pluginplugin:installed, plugin:disposed, plugin:enabled, plugin:disabled, plugin:error, plugin:warning
Error severityfatal, error, warning, info

BeforeEvent

All before* events carry a BeforeEvent<TData> payload:

TypeScript
interface BeforeEvent<TData> {
data: TData;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopImmediatePropagation(): void;
isPropagationStopped(): boolean;
delay(promise: Promise<unknown>): void;
isDelayed(): boolean;
}
  • data is mutable, so listeners modify it and the player reads the mutated value when running the default action.
  • preventDefault() skips the default action AND its post-event chain. Consumer sees a <action>Prevented event.
  • delay(promise) blocks the player. Multiple delays compose via Promise.all. Bounded by beforeEventTimeoutMs (default 10 000 ms).

BasePlaylistItem

Minimum shape every playlist item must satisfy.

TypeScript
interface BasePlaylistItem {
id: number | string;
title?: string;
image?: string;
}

Library-specific packages extend this. The video package adds fields like sources, subtitles, audio, chapters, and tracks. The music package adds artists, album, lrc, and duration.

ActionOptions

Common options accepted by every transport and queue action.

TypeScript
interface ActionOptions {
source?: ActionSource; // 'user' | 'remote' | 'plugin' | string; default 'user'
silent?: boolean; // skip lifecycle events and before* guards
autoplay?: boolean; // call play() immediately after load resolves
}

source is stamped on emitted events. Remote-sync plugins filter out their own remote-applied actions by checking event.source === 'remote'.

LoadOptions

Options for player.load(item, opts?). Extends ActionOptions.

TypeScript
interface LoadOptions extends ActionOptions {
slot?: 'current' | 'next'; // default 'current'
startAt?: number; // start position (seconds)
fadeIn?: number; // fade-in duration (seconds); 0 = immediate
}

slot: 'next' preloads without interrupting the currently-playing item, used by the preload pipeline.

See also