Utility Functions
This page covers the utilities and constants available to plugin authors in v2. If you are migrating from v1, read the v1 to v2 section at the bottom first, because the public export surface changed significantly.
What is actually exported in v2
The v2 package exposes a focused, lean public surface. Unlike v1, there is no batch of standalone string, time, and colour helpers re-exported from the package root. The utilities that used to live at @nomercy-entertainment/nomercy-video-player are now either:
- Internalised inside the plugin that needs them (time formatting inside
DesktopUiPlugin’s progress bar, colour parsing inside its subtitle-settings menu). - Available through the player API, for example reading or writing
SubtitleStylethroughplayer.subtitleStyle()rather than hand-building a style object from exported constants. - Not ported, because v2 plugin architecture removed the use cases that required them (title-casing and episode-title splitting are application-layer concerns that belong in your consumer plugin, not in the player package).
Subtitle style constants
The subtitle-style constants that were exported from v1 (defaultSubtitleStyles, edgeStyles, fontFamilies) exist in v2 inside packages/nomercy-video-player-v2/src/plugins/desktop-ui/buttons.ts. They are used internally by DesktopUiPlugin’s settings menu. They are not part of the public package export.
The recommended approach for reading and writing subtitle style from a plugin is to go through the player API:
import { Plugin } from '@nomercy-entertainment/nomercy-player-core';
import type { NMVideoPlayer } from '@nomercy-entertainment/nomercy-video-player';
export class MySubtitleSettingsPlugin extends Plugin<NMVideoPlayer> {
static readonly id = 'my:subtitle-settings';
use(): void {
// Read the current subtitle style (always returns a copy):
const style = this.player.subtitleStyle();
// Write a partial patch — only the fields you change are updated:
this.player.subtitleStyle({ fontSize: 120, edgeStyle: 'dropShadow' });
// Listen for external style changes (another plugin or the user's settings):
this.on('subtitleStyle', (updated) => {
this.applyToMyRenderer(updated);
});
}
dispose(): void {}
private applyToMyRenderer(style: import('@nomercy-entertainment/nomercy-player-core').SubtitleStyle): void {
// ...
}
}
The SubtitleStyle type is exported from @nomercy-entertainment/nomercy-player-core:
import type { SubtitleStyle } from '@nomercy-entertainment/nomercy-player-core';
SubtitleStyle fields
| Field | Type | Default |
|---|---|---|
fontSize | number | 100 (percent) |
fontFamily | string | 'ReithSans, sans-serif' |
textColor | string | 'white' |
textOpacity | number | 100 (percent) |
backgroundColor | string | 'black' |
backgroundOpacity | number | 0 (percent) |
edgeStyle | EdgeStyle | 'textShadow' |
areaColor | string | 'black' |
windowOpacity | number | 0 (percent) |
EdgeStyle is one of 'none' | 'depressed' | 'dropShadow' | 'textShadow' | 'raised' | 'uniform'.
Time formatting
v2 does not export time-formatting helpers. The DesktopUiPlugin formats time internally for its progress bar display. If you need a MM:SS / HH:MM:SS formatter in your own plugin, implement one directly or pull a small date-format library. Here is the pattern the desktop UI uses:
function humanTime(seconds: number): string {
const totalSeconds = Math.floor(seconds) || 0;
const days = Math.floor(totalSeconds / 86400);
const hours = Math.floor((totalSeconds % 86400) / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const secs = totalSeconds % 60;
const pad = (n: number) => String(n).padStart(2, '0');
if (days > 0) return `${days}:${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
if (hours > 0) return `${pad(hours)}:${pad(minutes)}:${pad(secs)}`;
return `${pad(minutes)}:${pad(secs)}`;
}
function convertToSeconds(hms: string | null): number {
if (!hms) return 0;
const parts = hms.split(':').map(Number);
if (parts.length === 2) parts.unshift(0);
return (parts[0] ?? 0) * 3600 + (parts[1] ?? 0) * 60 + (parts[2] ?? 0);
}
These are not exports you import. Paste them into your plugin file.
For live time display in a UI plugin, the player already fires 'time' events you can listen to:
use(): void {
this.on('time', ({ time }) => {
this.timeDisplay.textContent = humanTime(time);
});
}
Colour parsing
The colour helpers from v1 (parseColorToHex, rgbToHex, hslToHex, normalizeHex, namedColors) were used internally by the subtitle-settings UI in v1. In v2, DesktopUiPlugin’s settings menu works with the named CSS colour values stored in SubtitleStyle directly, so these conversion utilities are not needed for normal subtitle styling.
If you are building a custom subtitle-settings UI that needs to render colour swatches or convert between formats, use the browser’s own canvas API or a library such as colord. The v1 parseColorToHex implementation itself used canvas.getContext('2d').fillStyle under the hood, so the browser approach is equivalent:
function cssColorToHex(color: string, opacity = 1): string {
const canvas = document.createElement('canvas');
canvas.width = canvas.height = 1;
const ctx = canvas.getContext('2d')!;
ctx.fillStyle = color;
const computed = ctx.fillStyle; // browser normalises to #rrggbb or rgb(...)
// Append alpha channel manually if needed
const alpha = Math.round(opacity * 255).toString(16).padStart(2, '0').toUpperCase();
if (computed.startsWith('#')) return `${computed.toUpperCase()}${alpha}`;
// Handle rgb(...) output from older browsers
const match = computed.match(/\d+/g);
if (!match) return `#000000${alpha}`;
const [r, g, b] = match.map(Number);
return `#${r!.toString(16).padStart(2, '0')}${g!.toString(16).padStart(2, '0')}${b!.toString(16).padStart(2, '0')}${alpha}`.toUpperCase();
}
Array deduplication
The unique<T>(array, key) helper from v1 is a plain array utility. In v2 it is not exported. Use standard JavaScript:
// Deduplicate by a key property
function unique<T extends Record<string, unknown>>(array: T[], key: string): T[] {
const seen = new Set<unknown>();
return array.filter(item => {
const value = item[key];
if (seen.has(value)) return false;
seen.add(value);
return true;
});
}
String helpers
The title-casing and episode-title helpers (toTitleCase, lineBreakShowTitle, breakEpisodeTitle, breakLogoTitle, limitSentenceByCharacters) are not exported in v2. These are application-layer concerns. Put them in your own app code or consumer plugin, they do not belong in the player package.
v1 to v2
In v1, the following were re-exported from the package root:
// v1 — no longer available in v2 (reference only; these imports do not exist in v2)
import {
breakEpisodeTitle,
breakLogoTitle,
convertToSeconds,
defaultSubtitleStyles,
edgeStyles,
fontFamilies,
getEdgeStyle,
hslToHex,
humanTime,
limitSentenceByCharacters,
lineBreakShowTitle,
namedColors,
normalizeHex,
pad,
parseColorToHex,
rgbToHex,
toTitleCase,
unique,
} from '@nomercy-entertainment/nomercy-video-player';
None of these are exported from the v2 package. The migration path:
| v1 export | v2 replacement |
|---|---|
defaultSubtitleStyles | Read via player.subtitleStyle() or define your own defaults matching SubtitleStyle |
edgeStyles | Define locally as { name, value }[] using the EdgeStyle union type |
fontFamilies | Define locally, same structure |
humanTime / convertToSeconds / pad | Implement in your plugin or consumer code (see patterns above) |
parseColorToHex / rgbToHex / hslToHex / normalizeHex / namedColors | Use the browser canvas API (see pattern above) or a library like colord |
unique | Use a Set-based filter (see pattern above) |
toTitleCase | Application-layer utility, implement in your codebase |
lineBreakShowTitle / breakLogoTitle / breakEpisodeTitle / limitSentenceByCharacters | Application-layer, implement in your codebase |
For the full migration guide, see Migration: v1 to v2.