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:
- Install the v2 package.
- Register
V1VideoCompatPlugin. - Your code runs. The browser console shows deprecation warnings naming the v2 replacement for each v1 API you use.
- Work through the warnings one by one, migrating each call site to v2.
- 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.
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.
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.warndeprecation 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()orplayer.removePlugin(V1VideoCompatPlugin).
Deprecation warnings
Each warning names the v1 API and the v2 replacement:
[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:
// 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 method | v2 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 method | v2 replacement |
|---|---|
muted() | volumeState() |
muted(value) | mute() / unmute() |
getVolume() | volume() |
setVolume(value) | volume(v) |
getMute() | volumeState() |
setMute(muted) | mute() / unmute() |
Playlist / queue
| v1 method | v2 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 method | v2 replacement |
|---|---|
getQualityLevels() | qualityLevels() (shim prepends Auto entry) |
getCurrentQuality() | quality() (shim applies +1 offset) |
setCurrentQuality(index) | quality(idx) (shim applies -1 offset) |
Subtitles
| v1 method | v2 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 method | v2 replacement |
|---|---|
getAudioTracks() | audioTracks() |
getCurrentAudioTrack() | audioTrack() |
getAudioTrackIndex() | audioTrack()?.index |
setCurrentAudioTrack(index) | audioTrack(index) |
Time / duration
| v1 method | v2 replacement |
|---|---|
currentTime() | time() |
getCurrentTime() | time() |
getDuration() | duration() |
getTimeData() | timeData() |
getBuffer() | bufferedRanges() |
getState() | playState() |
state() | playState() |
Chapters
| v1 method | v2 replacement |
|---|---|
getChapters() | chapters() |
getPreviousChapter() | previousChapter() |
getNextChapter() | nextChapter() |
Display
| v1 method | v2 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 method | v2 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 event | v2 event | Payload notes |
|---|---|---|
play | play | Receives a v1 TimeData object |
pause | pause | Receives a v1 TimeData object |
time | time | { currentTime, duration, percentage, remaining, … } (v1 TimeData shape) |
seek | time | Same v1 TimeData shape |
seeked | time | Same v1 TimeData shape |
volume | volume | { isMuted: false, volume: level } |
mute | mute | { isMuted: boolean } |
item | current | Receives the item object directly (v1 shape) |
complete | ended | Passes through |
finished | ended | Passes through |
firstFrame | firstFrame | Passes through |
ready | ready | Passes through |
dispose | dispose | Passes through |
error | error | Passes through |
fullscreen | fullscreen | Reshapes { active: boolean } to boolean |
pip | pip | Reshapes { active: boolean } to boolean |
theater | theater | Reshapes { active: boolean } to boolean |
controls | active | Reshapes to boolean |
showControls | active | Reshapes to boolean |
captionsChanged | subtitleCue | Passes through |
captionsList | ready | Passes through |
levels | levels | Extracts levels array |
speed | playbackRate | Passes through |
playbackRateChanged | playbackRate | Passes 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 API | Why unshimmable | Consumer replacement |
|---|---|---|
fetchPlaylist(url) | v2 URL-loading is async with a separate parser registry; cannot be replicated as a synchronous method patch | Use player.loadQueue(url, parser?) directly |
tracks() | v1 tracks(kind?) collapsed subtitles, audio, and chapters into one array; v2 exposes each through its own typed getter | Call 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 reliably | Listen to queue:changed and read player.queue() in the handler |
getCurrentChapter(time) | Chapter-at-time lookup is a consumer concern in v2 | player.chapters().find(ch => time >= ch.start && time < ch.end) |
getChapterFile() | Chapter URL is on the queue item in v2 | player.item()?.chapters |
getSkippers() | Skipper data is managed by SkipperPlugin in v2 | player.getPlugin(SkipperPlugin).skippers() |
getSkip() | Managed by SkipperPlugin | player.getPlugin(SkipperPlugin).skip() |
getSkipFile() | Skip data is on the queue item | player.item()?.skippers |
getTimeFile() / getSpriteFile() | Preview sprite URL is on the queue item | player.item()?.previewSpriteUrl |
getFontsFile() | Font loading is handled by OctopusPlugin in v2 | Register OctopusPlugin; fonts are on item().fonts[] |
playAd() | Ad integration is a consumer concern in v2; the player has no ad slot | Implement ad logic in your own code |
requestCast() / stopCasting() | Cast is a plugin in v2 | Use CastSenderPlugin |
gain() / addGainNode() / removeGainNode() | Gain control is in AudioGraphPlugin | Register AudioGraphPlugin |
loadSource(url) | v2 load() requires a full item object, not a bare URL | player.load({ id: url, url }) |
seasons() | Season grouping is a consumer concern | Group 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 imprecise | Find 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.
Related pages
- Migration guide: v1 to v2 — complete breaking-change tables and manual migration reference
- Plugin development — how the plugin system works