Import
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.
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
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.
interface WithCurrentItem<T extends BasePlaylistItem = BasePlaylistItem> {
item(): T | undefined;
}
Both NMMusicPlayer<T> and NMVideoPlayer<T> satisfy this interface structurally — no implements required.
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.
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
| Field | Type | Default | Description |
|---|---|---|---|
auth | AuthConfig | none | Bearer token, headers, transformUrl, signRequest, refresh hook |
logLevel | LogLevel | 'info' | Verbosity threshold |
baseUrl | string | none | Prefixed to all relative media URLs |
beforeEventTimeoutMs | number | 10000 | Max wait for delay() promises in before-events |
pluginInitTimeoutMs | number | 30000 | Max wait for async use() per plugin |
platform | IPlatform | browserPlatform | Swap wake-lock, network, fullscreen, PiP adapters (spread browserPlatform to override one) |
mutationGuards | false | 'all' | ReadonlyArray<string> | none | Which mutations emit beforeMutation |
preloadStrategy | IPreloadStrategy | DefaultPreloadStrategy | When and what to buffer ahead |
transitionStrategy | ITransitionStrategy | GaplessTransitionStrategy | How 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.
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:
auth: {
bearerToken: () => myStore.token,
refreshOnUnauthenticated: async () => {
await myStore.refresh();
},
}
CastConfig
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.
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.
| Group | Example keys |
|---|---|
| Setup lifecycle | beforeSetup, setupStart, ready, playlistReady, mediaReady |
| Setup errors | setupStartError, pluginsRegisteringError, playlistResolveError, mediaReadyError |
| Play-path | beforePlay, play, playing, beforePause, pause, stop, ended |
| Seek | beforeSeek, seek, seeked, seekPrevented |
| Mutation guards | beforeMutation, mutationPrevented |
| Time | time, duration, progress |
| Volume / mode | volume, mute, repeat, shuffle |
| Queue | queue:append, queue:prepend, queue:insert, queue:remove, queue:clear, queue:exhausted, current |
| Media tracks | subtitle, subtitleCue, audioTrack, qualityState, chapters |
| Network / visibility | network:online, network:offline, network:slow, visibility:visible, visibility:hidden |
| Auth | auth:refreshed, auth:failed |
| Cast | castState |
| Plugin | plugin:installed, plugin:disposed, plugin:enabled, plugin:disabled, plugin:error, plugin:warning |
| Error severity | fatal, error, warning, info |
BeforeEvent
All before* events carry a BeforeEvent<TData> payload:
interface BeforeEvent<TData> {
data: TData;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopImmediatePropagation(): void;
isPropagationStopped(): boolean;
delay(promise: Promise<unknown>): void;
isDelayed(): boolean;
}
datais 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>Preventedevent.delay(promise)blocks the player. Multiple delays compose viaPromise.all. Bounded bybeforeEventTimeoutMs(default 10 000 ms).
BasePlaylistItem
Minimum shape every playlist item must satisfy.
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.
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.
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
- Runtime Classes:
Plugin,Logger,LifecycleRegistry - Enums:
SetupState,BufferState,CastState, and more - Error Classes:
PlayerErrorand subclasses - Events Reference: full event payload table
- Setup:
BasePlayerConfigin context