Key Handler
KeyHandlerPlugin is the video player’s keyboard binding layer.
It subclasses the core’s base KeyHandlerPlugin and adds the full v1 binding set on top of the core’s cooldown, scope, when, and cleanup infrastructure.
Register it explicitly on any deployment where keyboard control is needed.
DesktopUiPlugin does not include or register KeyHandlerPlugin - both plugins must be added independently via addPlugin().
Plugin id
'video-key-handler'
Import
import { KeyHandlerPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
What it does
All binding groups are protected methods, so you can subclass and override one group without touching the rest.
Override addDefaults() to drop the entire video binding set and start from scratch.
The plugin binds the following groups by default:
| Group | Keys |
|---|---|
| Playback | Space, MediaPlay, MediaPause, MediaPlayPause, MediaStop, MediaRewind, MediaFastForward |
| Navigation | ArrowLeft (rewind), ArrowRight (forward), disabled on TV |
| Volume | ArrowUp / ArrowDown (desktop only, not TV or mobile), m (mute) |
| Media keys | Subtitle / 5 / v cycles subtitles; Audio / 2 / b cycles audio tracks |
| Modifier seeks | Shift+Arrow 3s, Alt+Arrow 10s, Ctrl+Arrow 60s |
| Quick skip | 1 forward 120s, 3 forward 30s, 6 forward 60s, 9 forward 90s; TV color buttons ColorF0Red 30s, ColorF1Green 60s, ColorF2Yellow 90s, ColorF3Blue 120s |
| Next / Prev | MediaTrackNext, MediaTrackPrevious, n, p |
| Chapters | Shift+N (next chapter), Shift+P (previous chapter) |
| Fullscreen | f, F11, Escape (exits fullscreen only) |
| Speed | ] faster, [ slower, = reset to 1x |
| Frame advance | e advances one frame (~1/30s) when paused |
| Show time | t shows current and remaining time as an OSD message |
| Subtitle size | + or Shift++ emits 'subtitle-size-up'; - emits 'subtitle-size-down' |
| Aspect ratio | a, BrowserFavorites cycles aspect ratio |
| Stop | s |
| Help | Shift+? emits 'plugin:desktop-ui:shortcuts-toggle' |
Options
KeyHandlerPlugin inherits KeyHandlerOptions from the core base class.
No video-specific options are added.
interface KeyHandlerOptions<P> {
/** Where the keydown listener is attached. Default: 'document'. */
scope?: 'document' | 'container' | HTMLElement;
/** Extra bindings merged on top of the default groups. Same combo wins over the default. */
bindings?: Record<string, (player: P) => void>;
/** When false, default bindings are cleared before opts.bindings is applied. Default: true. */
extend?: boolean;
/** Gate predicate. Return false to suppress all key handling for that event. */
when?: (e: KeyboardEvent) => boolean;
/** Minimum milliseconds between consecutive fires of the same key. Default: 300. */
cooldownMs?: number;
/** When true, hardware media keys (MediaPlay, MediaPause, etc.) are silently ignored. Default: false. */
disableMediaControls?: boolean;
}
| Option | Type | Default | Description |
|---|---|---|---|
scope | 'document' | 'container' | HTMLElement | 'document' | Where the keydown listener is attached |
bindings | Record<string, fn> | undefined | Additional combos merged on top of the video default groups |
extend | boolean | true | false clears all defaults before bindings is applied |
when | (e: KeyboardEvent) => boolean | none | Return false to suppress all keys for that event |
cooldownMs | number | 300 | Minimum ms between consecutive fires of the same key |
disableMediaControls | boolean | false | Suppress hardware media key handling |
The disableControls and disableMediaControls flags in VideoPlayerConfig are also respected.
Pass them in setup() rather than in the plugin options when the intent is to disable keyboard control globally.
Methods
These public methods are inherited from the core base class.
Access the plugin instance with player.getPlugin(KeyHandlerPlugin).
| Method | Signature | Description |
|---|---|---|
bind | (combo: string, fn: (player) => void): void | Register or replace a handler for a combo string |
unbind | (combo: string): void | Remove the handler for a combo string |
replace | (combo: string, fn: (player) => void): void | Semantic alias for bind |
bindings | (): ReadonlyMap<string, fn> | Snapshot of the current binding map |
scope | (): EventTarget | The target the keydown listener is attached to |
Registration
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { KeyHandlerPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
const player = nmplayer('player')
.addPlugin(KeyHandlerPlugin)
.setup({
baseUrl: 'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Films',
playlist: [
{
title: 'Big Buck Bunny',
url: '/Big.Buck.Bunny.(2008)/Big.Buck.Bunny.(2008).NoMercy.m3u8',
},
],
});
Disabling controls
Set disableControls: true in VideoPlayerConfig to suppress all keyboard bindings.
Set disableMediaControls: true to suppress only the hardware media keys (MediaPlay, MediaPause, MediaStop, MediaRewind, MediaFastForward, MediaTrackNext, MediaTrackPrevious):
player.setup({
disableControls: false,
disableMediaControls: false,
playlist: [ /* ... */ ],
});
Subclassing
All binding groups are protected methods.
Subclass KeyHandlerPlugin and override any group without rewriting the rest:
import { KeyHandlerPlugin as VideoKeyHandler } from '@nomercy-entertainment/nomercy-video-player/plugins';
class CustomKeyHandler extends VideoKeyHandler {
protected override addNavigationKeys(): void {
this.bind('ArrowLeft', () => {
void this.player.rewind?.(30);
});
this.bind('ArrowRight', () => {
void this.player.forward?.(30);
});
}
}
OSD messages
Bindings that change state (speed, aspect ratio) use this.message(text) to emit an OSD notification.
This calls player.displayMessage(text) if the player exposes it, and always emits 'display-message' on the player so any custom OSD widget can subscribe:
player.on('display-message', ({ text }) => {
showOsd(text);
});