Skip to content

Migrating from v1

V1VideoCompatPlugin (id 'v1-video-compat') attaches the old v1 method names (seek(), speed(), playlist(), enterFullscreen(), and the rest of the v1 surface) onto a v2 player instance. Every shim delegates to the real v2 method underneath and logs one deprecation line per call, nothing here is new behavior, it’s a compatibility surface over the API this whole collection documents. It is entirely opt-in, a v2 app that never adds this plugin never carries the shim code or the deprecation logging at all.

Options

None. V1VideoCompatPlugin takes no configuration, add it and every shim installs:

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

const player = nmplayer('player');
player.addPlugin(V1VideoCompatPlugin); // before setup()
player.setup({
baseUrl: 'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Films',
baseImageUrl: 'https://image.tmdb.org/t/p',
muted: true,
autoPlay: true,
playlist: [
{
id: 'sintel',
title: 'Sintel',
url: '/Sintel.(2010)/Sintel.(2010).NoMercy.m3u8',
image: '/w780/q2bVM5z90tCGbmXYtq2J38T5hSX.jpg',
},
],
});

What it shims

Grouped by concern, every entry maps directly to a v2 method already covered elsewhere in this collection:

v1 methodv2 equivalent
seek(seconds), currentTime() / currentTime(seconds), currentSrc()time(seconds), videoElement.currentSrc / videoElement.src
speed() / speed(rate), speeds(), hasSpeeds()playbackRate(), playbackRates()
muted() / muted(state), gain(), isPlaying, state()volumeState(), mute()/unmute(), videoElement.volume, playState()
enterFullscreen(), exitFullscreen(), hasPIP(), setFloatingPlayer(active)fullscreen(state), platform().pip, pip(active)
aspect() / aspect(value), setAspect(value)aspectRatio() / aspectRatio(value)
resize(), width(), height(), element()no-op (v2’s ResizeObserver tracks size automatically), container.clientWidth, container.clientHeight, container
subtitleIndex(), subtitleIndexBy(lang), hasSubtitles()subtitle()?.index, a subtitles() search, subtitles().length > 0
audioTrackIndex(), audioTrackIndexByLanguage(lang), hasAudioTracks()the audio-track equivalents of the row above
hasQualities()qualityLevels().length > 0
chapterText(idx?)chapter()?.title / chapters()[idx]?.title
playlist() / playlist(items), playlistItem(), playlistIndex(), setPlaylist(items), loadSource(url)queue() / queue(items), item(), index(), load({ id, url })
load(items[])queue(items), the array-overload only, a single-item load(item, opts) call still delegates straight through to the real v2 load().
playVideo(target)item(target, { autoplay: true })
setEpisode(season, episode)find the index in queue() by season/episode, then item(index)
isFirstPlaylistItem(), isLastPlaylistItem(), hasPlaylists()derived from index() and queueLength()
seasons()grouped from queue() by the item’s season field
localize(key, vars), addTranslation(key, value)t(key, vars), translation(language(), key, value)
registerPlugin(...), usePlugin(...), plugin(id)addPlugin(...), getPluginById(id)
displayMessage(text, ms)emit 'display-message' / 'remove-message' directly

setup() itself is shimmed too: v1 allowed a second setup({ playlist, autoPlay }) call to swap sources on a live player; v2’s real setup() throws on re-entry. With this plugin loaded, a repeat call after the first queues the incoming playlist and autoplays it instead of throwing, matching the old behavior.

What has no v2 equivalent

A handful of v1 methods throw NotImplementedError rather than silently no-opping, because there’s no v2 data model to translate them into:

  • subtitleFile(url), chapterFile(url), skipFile(url), skip(): dynamic sidecar/skip-segment ingestion. Add the URL to the playlist item’s tracks / chapters field instead, or call time(seconds) at the boundary directly for skip segments.
  • skippers(): always returns an empty array, there is no v2 skip-segment data model yet.

A second group keeps working exactly as it did in v1 (nothing throws), but has no v2 method to migrate onto, either because v2 solved the underlying problem structurally or because the behavior is a pure utility outside the player’s typed surface. Delete these call sites when you remove the plugin, replicating them yourself if your app still needs them:

  • allowFullscreen / setAllowFullscreen(allowed): gates enterFullscreen() only, fullscreen() itself is never gated in v2.
  • stretchOptions: the v1 array ['uniform', 'fill', 'exactfit', 'none']. No v2 accessor reads this list back, cycleAspectRatio() cycles the same four values internally, but there’s nothing public to iterate yourself against. Hardcode the array in your own app code if you still need to render an aspect-ratio menu from it.
  • doubleTap(el, callback, thresholdMs): a pure DOM double-tap detector, unrelated to player state.
  • appendScriptFilesToDocument(urls): a pure DOM utility, appends <script> tags to document.head in sequence, awaiting each onload before starting the next. Unrelated to player state, replicate it in your own app code if you still need it.
  • snakeToCamel(value), spaceToCamel(value): pure string utilities, no player state involved either.
  • setTitle(title): writes document.title directly.
  • tracks(kind): reads the item’s raw v1 tracks field, v2’s typed subtitles() / audioTracks() replace it for the formats they cover, there’s no v2 generic-track equivalent.
  • addTranslations(entries) (the list-form, { key, value }[], overload): loops calling translation(language(), entry.key, entry.value) for you. The bundle-form overload, addTranslations(bundle), is real v2 API and passes straight through to it, the plugin still logs the deprecation warning first even though that call needed no translation.

Rules & restrictions

  • Every shim logs @deprecated player.<method>() is a v1 compatibility shim — migrate to the v2 API before it is removed. through the player’s logger on every call, so grepping logs during a migration surfaces exactly which call sites still need updating.
  • Two events bridge automatically while this plugin is loaded: 'playbackRate' also emits as 'speed', and 'queue' also emits as 'playlist', matching the v1 event names a legacy listener might still be attached to.
  • Must be added before setup(), like any plugin, adding it after ready() misses the plugin’s own use() call during the setup pipeline.

How to remove it

Delete the player.addPlugin(V1VideoCompatPlugin) call, then work through the deprecation log output (every remaining call site logs its own replacement method), migrating call sites to the v2 methods in the table above. Once the log is empty, remove the import too, there is nothing else in the codebase that depends on this plugin being present.

Next steps