Skip to content

V1 Compatibility Plugin

V1VideoCompatPlugin is a temporary migration shim that lets a v1 consumer install @nomercy-entertainment/nomercy-video-player v2 and have their existing code keep working without any changes. Register the plugin once, then migrate call sites at your own pace guided by the deprecation warnings in the browser console.

This plugin is temporary. It will be removed in the first stable 2.x release after the migration window closes. New projects should use the v2 API directly and never register this plugin.

Plugin id

'v1-compat'

When to use this plugin

Use it when you have an existing v1 integration that you need to move to v2 without a big-bang rewrite. The typical path is:

  1. Install the v2 package.
  2. Register V1VideoCompatPlugin.
  3. Your code runs. The browser console shows deprecation warnings naming the v2 replacement for each v1 API you use.
  4. Work through the warnings one by one, migrating each call site to v2.
  5. Once no warnings appear, remove the plugin.

Do not use this plugin for new projects. If you are starting fresh, use the v2 API directly and read the full migration guide.

Import

The plugin ships on its own subpath export so it does not add to the bundle of projects that do not need it.

TypeScript
import { V1VideoCompatPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins/v1-compat';

Registration

Register the plugin immediately after constructing the player, before calling setup() or any v1 methods.

TypeScript
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { V1VideoCompatPlugin } from '@nomercy-entertainment/nomercy-video-player/plugins/v1-compat';

const player = nmplayer('player-1');
player.addPlugin(V1VideoCompatPlugin);

await player.setup({
// your existing v1 setup options
});

// Your existing v1 code works unchanged:
player.setPlaylist([{ id: '1', file: 'video.m3u8', title: 'My Video' }]);
player.playVideo(0);
player.on('item', (item) => console.log('Now playing', item.title));

What the plugin does

When registered, V1VideoCompatPlugin:

  • Patches every renamed v1 method onto the player instance, each delegating to its v2 equivalent with any required argument corrections applied internally.
  • Intercepts player.on() and transparently re-wires v1 event names to the corresponding v2 events, reshaping payloads to match what v1 listeners expect.
  • Emits a console.warn deprecation notice once per distinct v1 API name across the lifetime of the page. Calling the same v1 method a hundred times produces exactly one warning.
  • Removes all patches and bridges cleanly on player.dispose() or player.removePlugin(V1VideoCompatPlugin).

Deprecation warnings

Each warning names the v1 API and the v2 replacement:

Code
[nomercy-video-player] DEPRECATED "seek(seconds)" — use "time(seconds)" instead.
This shim is provided by V1VideoCompatPlugin and will be removed in the first stable 2.x release.

Use these warnings as a work list. When you have migrated every call site and no warnings appear on any page, the plugin is safe to remove.

The seekToIndex / playVideo index offset

This is the one semantic difference that is not a simple rename.

player.playVideo(index) in v1 was 0-based: playVideo(0) loaded the first item. player.seekToIndex(position) in v2 is 1-based: seekToIndex(1) loads the first item, and passing 0 throws RangeError.

The shim applies the +1 correction internally, so your existing v1 calls continue to work unchanged. When you migrate a call site manually, remember to add the offset:

TypeScript
// v1 (shimmed — still works via the compat plugin)
player.playVideo(0); // first item
player.playVideo(2); // third item

// v2 (after migrating the call site)
player.seekToIndex(1); // first item
player.seekToIndex(3); // third item

The same offset applies to playlistItem(index) when called with an argument: the shim translates playlistItem(0) to seekToIndex(1) internally.

Quality index offset

In v1, getQualityLevels() returned an array with an “Auto” entry at index 0, so real levels started at index 1. getCurrentQuality() returned 0 for Auto and 1+ for real levels. setCurrentQuality(0) selected Auto.

The shim preserves this shape: getQualityLevels() still prepends an Auto entry and getCurrentQuality() / setCurrentQuality() apply the ±1 translation to v2’s quality().

v1 method mapping

Every v1 method shimmed by this plugin is listed below with its v2 replacement.

Playback

v1 methodv2 replacement
seek(seconds)time(seconds)
speed()playbackRate()
speed(rate)playbackRate(rate)
speeds()playbackRates()
hasSpeeds()playbackRates().length > 1
seekByPercentage(pct)seekByPercentage(pct)
setSpeed(rate)playbackRate(rate)
getSpeed()playbackRate()
getSpeeds()playbackRates()
rewindVideo(time?)rewind(seconds)
forwardVideo(time?)forward(seconds)

Volume

v1 methodv2 replacement
muted()volumeState()
muted(value)mute() / unmute()
getVolume()volume()
setVolume(value)volume(v)
getMute()volumeState()
setMute(muted)mute() / unmute()

Playlist / queue

v1 methodv2 replacement
playlist()queue()
playlist(items)queue(items)
setPlaylist(items)queue(items)
load(items)queue(items)
playlistIndex()index()
playlistItem()item()
playlistItem(index)seekToIndex(index + 1)
playVideo(index)seekToIndex(index + 1)
hasPlaylists()queue().length > 1
isFirstPlaylistItem()index() === 0
isLastPlaylistItem()index() === queue().length - 1

Quality

v1 methodv2 replacement
getQualityLevels()qualityLevels() (shim prepends Auto entry)
getCurrentQuality()quality() (shim applies +1 offset)
setCurrentQuality(index)quality(idx) (shim applies -1 offset)

Subtitles

v1 methodv2 replacement
getSubtitles()subtitles()
getCaptionsList()subtitles() (shim prepends Off entry)
hasCaptions()subtitles().length > 0
getCurrentCaption()subtitle()
getCaptionIndex()subtitle()?.index (shim applies +1 offset)
setCurrentCaption(index)subtitle(idx) (shim applies -1 offset)
setSubtitleStyle(style)subtitleStyle(patch)
getSubtitleStyle()subtitleStyle()

Audio tracks

v1 methodv2 replacement
getAudioTracks()audioTracks()
getCurrentAudioTrack()audioTrack()
getAudioTrackIndex()audioTrack()?.index
setCurrentAudioTrack(index)audioTrack(index)

Time / duration

v1 methodv2 replacement
currentTime()time()
getCurrentTime()time()
getDuration()duration()
getTimeData()timeData()
getBuffer()bufferedRanges()
getState()playState()
state()playState()

Chapters

v1 methodv2 replacement
getChapters()chapters()
getPreviousChapter()previousChapter()
getNextChapter()nextChapter()

Display

v1 methodv2 replacement
getFullscreen()fullscreen()
setFullscreen(state)fullscreen(state)
aspect(value?)aspectRatio(value?)
getCurrentAspect()aspectRatio()
setAspect(value)aspectRatio(value)
getWidth()container.getBoundingClientRect().width
getHeight()container.getBoundingClientRect().height
getElement()container
getVideoElement()videoElement

Plugin access

v1 methodv2 replacement
getPlugin(name: string)getPlugin(PluginClass)

v1 event bridge

These v1 event names are intercepted by the player.on() proxy. Each is re-wired to the corresponding v2 event, and the payload is reshaped to match what v1 listeners expect.

v1 eventv2 eventPayload notes
playplayReceives a v1 TimeData object
pausepauseReceives a v1 TimeData object
timetime{ currentTime, duration, percentage, remaining, … } (v1 TimeData shape)
seektimeSame v1 TimeData shape
seekedtimeSame v1 TimeData shape
volumevolume{ isMuted: false, volume: level }
mutemute{ isMuted: boolean }
itemcurrentReceives the item object directly (v1 shape)
completeendedPasses through
finishedendedPasses through
firstFramefirstFramePasses through
readyreadyPasses through
disposedisposePasses through
errorerrorPasses through
fullscreenfullscreenReshapes { active: boolean } to boolean
pippipReshapes { active: boolean } to boolean
theatertheaterReshapes { active: boolean } to boolean
controlsactiveReshapes to boolean
showControlsactiveReshapes to boolean
captionsChangedsubtitleCuePasses through
captionsListreadyPasses through
levelslevelsExtracts levels array
speedplaybackRatePasses through
playbackRateChangedplaybackRatePasses through

Unshimmable APIs

These v1 APIs have no automated shim because they cannot be replicated transparently. Each no-ops (warns, then returns an empty value) rather than throwing. The warning message gives the consumer-side replacement.

v1 APIWhy unshimmableConsumer replacement
fetchPlaylist(url)v2 URL-loading is async with a separate parser registry; cannot be replicated as a synchronous method patchUse player.loadQueue(url, parser?) directly
tracks()v1 tracks(kind?) collapsed subtitles, audio, and chapters into one array; v2 exposes each through its own typed getterCall player.subtitles(), player.audioTracks(), player.chapters() separately
on('playlist', fn)v2 fires queue:changed with no payload; the v1 event carried the array. Cannot reconstruct reliablyListen to queue:changed and read player.queue() in the handler
getCurrentChapter(time)Chapter-at-time lookup is a consumer concern in v2player.chapters().find(ch => time >= ch.start && time < ch.end)
getChapterFile()Chapter URL is on the queue item in v2player.item()?.chapters
getSkippers()Skipper data is managed by SkipperPlugin in v2player.getPlugin(SkipperPlugin).skippers()
getSkip()Managed by SkipperPluginplayer.getPlugin(SkipperPlugin).skip()
getSkipFile()Skip data is on the queue itemplayer.item()?.skippers
getTimeFile() / getSpriteFile()Preview sprite URL is on the queue itemplayer.item()?.previewSpriteUrl
getFontsFile()Font loading is handled by OctopusPlugin in v2Register OctopusPlugin; fonts are on item().fonts[]
playAd()Ad integration is a consumer concern in v2; the player has no ad slotImplement ad logic in your own code
requestCast() / stopCasting()Cast is a plugin in v2Use CastSenderPlugin
gain() / addGainNode() / removeGainNode()Gain control is in AudioGraphPluginRegister AudioGraphPlugin
loadSource(url)v2 load() requires a full item object, not a bare URLplayer.load({ id: url, url })
seasons()Season grouping is a consumer concernGroup player.queue() by item.season in your own code
setEpisode(season, ep)Best-effort: the shim searches the queue and calls seekToIndex if a match is found, but warns that this is impreciseFind the item in player.queue() and call seekToIndex(idx + 1)

Removal timeline

This plugin will be deleted in the first stable 2.x release after the migration window closes. There will be no replacement; the expectation is that consumers have migrated to v2 by that point. Watch the changelog and the deprecation warnings in your console to track your progress.