Tab Leader
TabLeaderPlugin solves a specific annoyance: you open your library in two browser tabs and both start playing at once.
The plugin uses the browser’s Web Locks API to hold a named lock while a tab is playing.
Only one tab holds the lock at a time.
When a second tab requests it, the browser queues that request and hands the lock over the moment the first tab releases it or closes.
Reach for this plugin whenever you need to guarantee that only one player instance on the same origin is active at a time.
If you want finer control, such as scoping leadership to a single player instance rather than the whole origin, pass a getLockKey factory in the options.
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { TabLeaderPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
import type { TabLeaderOptions } from '@nomercy-entertainment/nomercy-video-player/plugins';
Plugin id: 'tab-leader'
What it does
On use() the plugin calls navigator.locks.request(key, callback).
The browser hands the lock to the first tab that requests it.
That tab emits 'leader-acquired' and is free to play.
Other tabs queue behind it.
When the leader tab closes or calls releaseLock(), the browser hands the lock to the next queued tab, which emits 'leader-acquired' in turn.
In environments without navigator.locks (Safari below 15.4) the plugin emits 'unsupported' and becomes a no-op.
Playback is unaffected, it just does not enforce single-tab behaviour.
Options
| Option | Type | Default | Description |
|---|---|---|---|
onLost | 'pause' | 'mute' | 'pause' | Action taken when this tab loses the leader lock. 'pause' calls player.pause(). 'mute' calls player.mute() so audio stops but buffering continues. |
handoffOnVisible | boolean | true | Re-request the lock whenever the tab becomes visible via document.visibilitychange. Set explicitly to false to disable; absence is treated as true. |
getLockKey | () => string | undefined | Factory returning the lock key. When absent, the plugin falls back to the shared key 'nomercy-player-leader', which is global across all instances on the same origin. Return a per-player or per-session string to scope leadership more narrowly. |
Events
| Event | Payload | Description |
|---|---|---|
'leader-acquired' | void | This tab now holds the lock and is the active player. |
'leader-released' | void | Lock released voluntarily via releaseLock() or when the plugin disposes. |
'unsupported' | void | Web Locks not available in this environment. The plugin is inert. |
Methods
isLeader
plugin.isLeader(): boolean
Returns true when this tab currently holds the leader lock.
requestLock
plugin.requestLock(): Promise<void>
Queue a lock-acquire request.
The returned promise resolves when the lock is released (via releaseLock(), plugin disposal, or tab close), not when it is first acquired.
Calling while an election is already in progress returns the existing pending promise rather than starting a second one.
releaseLock
plugin.releaseLock(): void
Voluntarily release the leader lock.
Emits 'leader-released' when this tab was the leader.
The next queued tab acquires the lock immediately.
No-ops if this tab is not currently the leader.
requestLeadership / releaseLeadership
plugin.requestLeadership(): Promise<boolean>
plugin.releaseLeadership(): void
Backwards-compatible aliases for requestLock() and releaseLock().
requestLeadership() resolves to true when leadership is held after the attempt, false otherwise.
Prefer the non-alias names in new code.
Registration
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { TabLeaderPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
const player = nmplayer('player')
.addPlugin(TabLeaderPlugin, { onLost: 'pause' })
.setup({
baseUrl: 'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Films',
playlist: [
{
id: 'sintel-2010',
url: '/Sintel.(2010)/Sintel.(2010).NoMercy.m3u8',
},
],
});
player.on('plugin:tab-leader:leader-acquired', () => {
console.log('this tab is the leader');
});
player.on('plugin:tab-leader:leader-released', () => {
console.log('this tab released the lock');
});
Scoping to a specific player
By default the lock key is global to the origin, so one tab plays at a time across all player instances on that origin.
Pass getLockKey to scope it to a server ID, content item, or session:
const serverId = 'abc123';
player.addPlugin(TabLeaderPlugin, {
getLockKey: () => `nomercy-leader-${serverId}`,
});
Reading leader state
import { TabLeaderPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins';
const leaderPlugin = player.getPlugin(TabLeaderPlugin);
if (leaderPlugin?.isLeader()) {
// safe to start playback
}