Skip to content

FAQ

Do I need to install the core if I’m just using the video player?

No. nomercy-video-player and nomercy-music-player both re-export everything from nomercy-player-core. Install only the package you need. The core is a separate install only if you are writing plugins that work against the raw core interface without a library package.

Can I use both the video player and music player on the same page?

Yes. Each player instance is isolated by its id. The video player and music player share no global state beyond the internal instance registry.

TypeScript
const videoPlayer = nmplayer('video').setup({ ... });
const musicPlayer = nmMPlayer('music').setup({ ... });

They do not interfere. You can have both playing at the same time (subject to browser audio policy). If you need one to pause when the other plays, wire that coordination yourself in a consumer plugin or in your app code.

How do I write my own subtitle renderer?

Replace the built-in SubtitleOverlayPlugin (VTT text rendering) with your own plugin that implements the same interface. Register your plugin with static readonly replaces = 'subtitle-overlay'.

For ASS/SSA subtitles with full typesetting, add OctopusPlugin, which loads @nomercy-entertainment/nomercy-subtitle-octopus dynamically and handles its own rendering. Do not combine OctopusPlugin and a custom renderer for the same subtitle track.

See Video, Subtitle Overlay for the full API.

Does the player handle 401 / 403 differently?

Yes. The auth pipeline treats them differently by design:

  • 401, token expired. refreshOnUnauthenticated is called once, the new token is applied, and the request is retried. If refresh fails, the request errors with core:auth/refresh-failed (the auth:failed event itself fires from player.refreshAuth()).
  • 403, access denied. The error propagates immediately. refreshOnUnauthenticated is never called for 403s. The server has denied access even with valid credentials.

Never lump 401 and 403 in the same catch block. A 403 may signal an entitlement problem that no amount of token refreshing will fix.

How do I add auto-advance (play next track when the current one ends)?

On the video player it is built in and on by default — set autoAdvance: false in setup() to turn it off. On the music player, add AutoAdvancePlugin:

TypeScript
import { AutoAdvancePlugin } from '@nomercy-entertainment/nomercy-music-player/plugins';
player.addPlugin(AutoAdvancePlugin);

The music plugin listens to the ended event and calls player.next(). When crossfade is enabled, it triggers crossfadeTo() instead.

How do I pause all players at once?

There is no global play/pause. You control each instance by id:

TypeScript
const main = nmplayer('main');
const pip = nmplayer('pip');

main.pause();
pip.pause();

If you need coordinated playback across multiple players (group listening), use GroupListeningPlugin, planned for v2.1.

Can I use the player with TypeScript strict mode?

Yes. The packages ship full TypeScript types. All public methods are typed. The generic parameter T extends VideoPlaylistItem or T extends MusicPlaylistItem threads your playlist item type through current(), queue(), beforeLoad, and current events, no casts required.

How do I render the player without the built-in UI?

Do not add DesktopUiPlugin. The player itself renders nothing, all chrome is opt-in through plugins. Build your own controls using the event API:

TypeScript
player.on('play', () => updatePlayButton('pause'));
player.on('pause', () => updatePlayButton('play'));
player.on('time', ({ time }) => updateSeekBar(time, player.duration()));

How do I restrict HLS quality to avoid high bitrate on slow networks?

OctopusPlugin is not related to quality. Quality selection lives in the video player’s HLS adapter. Set a maximum level via the config:

TypeScript
player.setup({
defaultQuality: 'auto', // ABR is active
});

To hard-cap to a specific quality level index at runtime:

TypeScript
player.quality(2); // lock to level index 2
player.quality('auto'); // back to auto (ABR)

HDR-aware ABR is automatic, so on SDR displays, the player filters out HDR levels from the ABR selection pool. No configuration needed. See Video, HLS for the full quality API.

How do I listen to events from a plugin I did not write?

Use the string form with the auto-namespaced event name on player.on():

TypeScript
player.on('plugin:equalizer:band:changed', ({ band }) => {
console.log(`Band at ${band.frequency} Hz set to ${band.gain} dB`);
});

Plugin events are always namespaced as plugin:<id>:<event>. The class-form this.on(PluginClass, 'event', fn) is only available inside a Plugin subclass body, it is a protected method and does not exist on the external player.on() surface.

Is the player compatible with Capacitor, Tauri, or Electron?

Yes, via the platform adapter system. Override individual sub-ports without rebuilding the entire bundle:

TypeScript
import { browserPlatform } from '@nomercy-entertainment/nomercy-player-core';

player.setup({
platform: {
...browserPlatform,
wakeLock: myCapacitorWakeLock,
network: myCapacitorNetworkMonitor,
},
});

The player’s core logic only calls the interfaces, it does not import Capacitor or Tauri directly.

Where do I report bugs?