@nomercy-entertainment/nomercy-video-player 2.2.2 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/adapters/video-backend/IVideoBackend.d.ts +15 -1
- package/dist/adapters/video-backend/html5.d.ts +15 -6
- package/dist/adapters/video-backend/html5.js +34 -6
- package/dist/index.d.ts +5 -2
- package/dist/index.js +11 -2
- package/dist/nomercy-video-player.iife.js +1 -1
- package/dist/player/preload.d.ts +5 -1
- package/dist/player/preload.js +5 -1
- package/dist/plugins/cast-sender/index.js +7 -2
- package/dist/plugins/desktop-ui/data/buttons.js +12 -0
- package/dist/plugins/desktop-ui/helpers/menuControl.js +6 -1
- package/dist/plugins/desktop-ui/mixins/domMethods.js +10 -1
- package/dist/plugins/key-handler/index.d.ts +6 -0
- package/dist/plugins/key-handler/index.js +7 -1
- package/dist/plugins/touch-zones/index.js +1 -1
- package/dist/plugins/tv-key-handler/index.js +5 -1
- package/dist/types.d.ts +10 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.2.3] — 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `disableClickToPause` in `setup()` stops a click from pausing the video.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Streams added with `registerStream()` are now used for playback.
|
|
14
|
+
- Playlist thumbnails load from the configured image URL.
|
|
15
|
+
- Changing auth at runtime now applies to media requests.
|
|
16
|
+
- Georgia subtitles fall back to a serif font, and subtitle colors match the palette.
|
|
17
|
+
- One press of `?` opens the shortcuts overlay.
|
|
18
|
+
- Cast artwork shows the right image, and the media-key option works.
|
|
19
|
+
- The help key on a TV remote works.
|
|
20
|
+
|
|
5
21
|
## [2.2.2] — 2026-09-04
|
|
6
22
|
|
|
7
23
|
### Fixed
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import type { AudioTrack, AuthHeaderProvider, BackendLoaderState, BackendState, HdrOnSdrFallback, MinimalBackendEventPayload, QualityLevel, SubtitleCueChange, SubtitleTrack } from '@nomercy-entertainment/nomercy-player-core';
|
|
1
|
+
import type { AudioTrack, AuthHeaderProvider, BackendLoaderState, BackendState, HdrOnSdrFallback, IStreamSource, MinimalBackendEventPayload, QualityLevel, SubtitleCueChange, SubtitleTrack } from '@nomercy-entertainment/nomercy-player-core';
|
|
2
2
|
import type { HtmlPreloadMode } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Asks the player's stream registry whether a consumer registered a factory for
|
|
5
|
+
* this URL. Returns a source to play it with, or `undefined` to let the backend
|
|
6
|
+
* handle the URL the way it always has.
|
|
7
|
+
*/
|
|
8
|
+
export type StreamResolver = (url: string, contentType?: string) => IStreamSource | undefined;
|
|
3
9
|
/**
|
|
4
10
|
* Per-event payload map. Each backend event has a fixed payload shape so
|
|
5
11
|
* `IVideoBackend.on(event, fn)` narrows the listener's parameter type
|
|
@@ -136,6 +142,14 @@ export interface IVideoBackend {
|
|
|
136
142
|
* stack omit it.
|
|
137
143
|
*/
|
|
138
144
|
setAuthHeaderProvider?(provider: AuthHeaderProvider): void;
|
|
145
|
+
/**
|
|
146
|
+
* Wire the lookup that asks the player's stream registry whether a consumer
|
|
147
|
+
* has registered a factory for a URL. The backend calls it before its own
|
|
148
|
+
* HLS and progressive handling, and falls through to that handling when the
|
|
149
|
+
* lookup returns nothing, so a player with no registered factory behaves
|
|
150
|
+
* exactly as it did before. Optional — backends with no registry omit it.
|
|
151
|
+
*/
|
|
152
|
+
setStreamResolver?(resolver: StreamResolver): void;
|
|
139
153
|
/**
|
|
140
154
|
* Consumer policy for an all-HDR item on a display that can't show HDR and
|
|
141
155
|
* a backend that can't tone-map it. Optional — backends that never face the
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import type { AudioTrack, HdrOnSdrFallback, QualityLevel, SubtitleTrack } from '@nomercy-entertainment/nomercy-player-core';
|
|
2
2
|
import type { HtmlPreloadMode } from '../../types.js';
|
|
3
|
-
import type { BackendEventPayload, BackendState, IVideoBackend } from './IVideoBackend.js';
|
|
3
|
+
import type { BackendEventPayload, BackendState, IVideoBackend, StreamResolver } from './IVideoBackend.js';
|
|
4
4
|
import { MediaElementBackend } from '@nomercy-entertainment/nomercy-player-core';
|
|
5
5
|
/**
|
|
6
6
|
* Default video backend. Wraps an `<HTMLVideoElement>` for transport.
|
|
7
7
|
*
|
|
8
|
-
* HLS support: native pass-through when `canPlayType` reports support
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* HLS support: native pass-through when `canPlayType` reports support,
|
|
9
|
+
* otherwise dynamically imports `hls.js` and attaches it. `hls.js` is a
|
|
10
|
+
* runtime dependency of nomercy-player-core, not a peer dependency, so an
|
|
11
|
+
* npm consumer installs it without asking. The IIFE build externalizes it
|
|
12
|
+
* as `window.Hls`; the ESM build resolves it from node_modules. MSE /
|
|
13
|
+
* WebCodecs backends ship later.
|
|
13
14
|
*/
|
|
14
15
|
export declare class Html5VideoBackend extends MediaElementBackend<HTMLVideoElement, BackendEventPayload> implements IVideoBackend {
|
|
15
16
|
readonly kind: "html5";
|
|
@@ -137,6 +138,14 @@ export declare class Html5VideoBackend extends MediaElementBackend<HTMLVideoElem
|
|
|
137
138
|
unload(): void;
|
|
138
139
|
dispose(): void;
|
|
139
140
|
private _teardownAudioGraph;
|
|
141
|
+
private _streamResolver?;
|
|
142
|
+
private _customSource?;
|
|
143
|
+
/**
|
|
144
|
+
* Wire the lookup that asks the player's stream registry for a consumer's
|
|
145
|
+
* factory. Without it the backend keeps its own HLS and progressive handling
|
|
146
|
+
* and a registered factory is never consulted.
|
|
147
|
+
*/
|
|
148
|
+
setStreamResolver(resolver: StreamResolver): void;
|
|
140
149
|
buffered(): number;
|
|
141
150
|
videoWidth(): number;
|
|
142
151
|
videoHeight(): number;
|
|
@@ -34,11 +34,12 @@ function humanCodec(raw) {
|
|
|
34
34
|
/**
|
|
35
35
|
* Default video backend. Wraps an `<HTMLVideoElement>` for transport.
|
|
36
36
|
*
|
|
37
|
-
* HLS support: native pass-through when `canPlayType` reports support
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* HLS support: native pass-through when `canPlayType` reports support,
|
|
38
|
+
* otherwise dynamically imports `hls.js` and attaches it. `hls.js` is a
|
|
39
|
+
* runtime dependency of nomercy-player-core, not a peer dependency, so an
|
|
40
|
+
* npm consumer installs it without asking. The IIFE build externalizes it
|
|
41
|
+
* as `window.Hls`; the ESM build resolves it from node_modules. MSE /
|
|
42
|
+
* WebCodecs backends ship later.
|
|
42
43
|
*/
|
|
43
44
|
export class Html5VideoBackend extends MediaElementBackend {
|
|
44
45
|
kind = 'html5';
|
|
@@ -238,6 +239,21 @@ export class Html5VideoBackend extends MediaElementBackend {
|
|
|
238
239
|
resetMediaElement(this.element);
|
|
239
240
|
performance.mark('nm:backend:load:start');
|
|
240
241
|
const headerValue = this._authHeaderProvider?.(url);
|
|
242
|
+
// A consumer's factory is asked first, because the branches below cover
|
|
243
|
+
// only HLS and whatever the element itself decodes. Nothing registered
|
|
244
|
+
// means nothing returned, and the built-in path runs untouched.
|
|
245
|
+
if (this._customSource) {
|
|
246
|
+
this._customSource.destroy();
|
|
247
|
+
this._customSource = undefined;
|
|
248
|
+
}
|
|
249
|
+
const customSource = this._streamResolver?.(url);
|
|
250
|
+
if (customSource) {
|
|
251
|
+
this._customSource = customSource;
|
|
252
|
+
await customSource.attach(this.element);
|
|
253
|
+
this._state = 'ready';
|
|
254
|
+
performance.mark('nm:backend:load:end');
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
241
257
|
if (hlsUrl && !nativeHls) {
|
|
242
258
|
const { default: Hls } = await import('hls.js');
|
|
243
259
|
if (!Hls?.isSupported?.()) {
|
|
@@ -349,6 +365,16 @@ export class Html5VideoBackend extends MediaElementBackend {
|
|
|
349
365
|
// playbackRate / volume / mute / unmute / captureStream / setSinkId /
|
|
350
366
|
// getSinkId / mediaKeys / setMediaKeys / mediaElement / pauseLoader /
|
|
351
367
|
// resumeLoader / loaderState / setAuthHeaderProvider
|
|
368
|
+
_streamResolver;
|
|
369
|
+
_customSource;
|
|
370
|
+
/**
|
|
371
|
+
* Wire the lookup that asks the player's stream registry for a consumer's
|
|
372
|
+
* factory. Without it the backend keeps its own HLS and progressive handling
|
|
373
|
+
* and a registered factory is never consulted.
|
|
374
|
+
*/
|
|
375
|
+
setStreamResolver(resolver) {
|
|
376
|
+
this._streamResolver = resolver;
|
|
377
|
+
}
|
|
352
378
|
// ── Time / position (video-specific: buffered uses currentTime walk) ──
|
|
353
379
|
buffered() {
|
|
354
380
|
const ranges = this.element.buffered;
|
|
@@ -381,7 +407,9 @@ export class Html5VideoBackend extends MediaElementBackend {
|
|
|
381
407
|
default: hlsAudioTrack.default === true,
|
|
382
408
|
}));
|
|
383
409
|
}
|
|
384
|
-
// Native: HTMLMediaElement.audioTracks
|
|
410
|
+
// Native: HTMLMediaElement.audioTracks. Measured absent in Chrome 152,
|
|
411
|
+
// Edge 152 and bundled Chromium, so this branch is Safari in practice
|
|
412
|
+
// and yields [] elsewhere; the HLS path above is what fills the list.
|
|
385
413
|
const nativeTracks = this.element.audioTracks;
|
|
386
414
|
if (nativeTracks && nativeTracks.length > 0) {
|
|
387
415
|
const out = [];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActionOptions, AriaLiveLevel, AudioTrack, AuthConfig, BasePlaylistItem, BufferState, CanPlayResult, CastState, CastTarget, Chapter, CurrentAudioTrackSelection, CurrentQualitySelection, CurrentSubtitleSelection, DeviceCapabilities, ICueParser, IPlatform, IPlayer, IPreloadStrategy, IStreamFactory, ITransitionStrategy, IUrlResolver, TimeState as KitTimeState, LoadOptions, NetworkState, PlaybackMetrics, PlayerExperimental, PlayerPhase, Plugin, PluginCtorWithId, QualityLevel, ResolvedUrl, SetupState, SidecarSubtitleInput, SubtitleStyle, SubtitleTrack, Translations, UrlCategory, VisibilityState } from '@nomercy-entertainment/nomercy-player-core';
|
|
1
|
+
import type { ActionOptions, AriaLiveLevel, AudioTrack, AuthConfig, BasePlaylistItem, BufferState, CanPlayResult, CastState, CastTarget, Chapter, CurrentAudioTrackSelection, CurrentQualitySelection, CurrentSubtitleSelection, DeviceCapabilities, ICueParser, IPlatform, IPlayer, IPreloadStrategy, IStreamFactory, IStreamSource, ITransitionStrategy, IUrlResolver, TimeState as KitTimeState, LoadOptions, NetworkState, PlaybackMetrics, PlayerExperimental, PlayerPhase, Plugin, PluginCtorWithId, QualityLevel, ResolvedUrl, SetupState, SidecarSubtitleInput, SubtitleStyle, SubtitleTrack, Translations, UrlCategory, VisibilityState } from '@nomercy-entertainment/nomercy-player-core';
|
|
2
2
|
import type { IVideoBackend, VideoBackendKind } from './adapters/video-backend/IVideoBackend.js';
|
|
3
3
|
import type { AudioTrackState, IVideoPlayer, PlayState, QualityState, RepeatState, SegmentOptions, ShuffleState, Stretching, VideoEventMap, VideoPlayerConfig, VideoPlaylistItem, VideoRect, VolumeState } from './types.js';
|
|
4
4
|
import { EventEmitter } from '@nomercy-entertainment/nomercy-player-core';
|
|
@@ -200,12 +200,15 @@ export declare class NMVideoPlayer<T extends VideoPlaylistItem = VideoPlaylistIt
|
|
|
200
200
|
* `mediaReady` fires so the backend's track lists are populated.
|
|
201
201
|
*
|
|
202
202
|
* Language matching: exact tag first, then prefix (e.g. `'en'` matches
|
|
203
|
-
* `'en-US'`). No match
|
|
203
|
+
* `'en-US'`). No match leaves an UNSELECTED slot alone, but where a track is
|
|
204
|
+
* already selected the slot is handed to the stream's own default rendition
|
|
205
|
+
* instead. No warning either way.
|
|
204
206
|
*/
|
|
205
207
|
private _applyDefaultTracks;
|
|
206
208
|
/** Test-only: clear the registry. Not part of the public API. */
|
|
207
209
|
static _resetRegistry(): void;
|
|
208
210
|
registerStream: (factory: IStreamFactory, prepend?: boolean) => this;
|
|
211
|
+
resolveCustomStream: (url: string, contentType?: string) => IStreamSource | undefined;
|
|
209
212
|
unregisterStream: (id: string) => this;
|
|
210
213
|
streams: () => ReadonlyArray<string>;
|
|
211
214
|
getStreamFactory: (id: string) => IStreamFactory | undefined;
|
package/dist/index.js
CHANGED
|
@@ -212,7 +212,9 @@ export class NMVideoPlayer extends EventEmitter {
|
|
|
212
212
|
* `mediaReady` fires so the backend's track lists are populated.
|
|
213
213
|
*
|
|
214
214
|
* Language matching: exact tag first, then prefix (e.g. `'en'` matches
|
|
215
|
-
* `'en-US'`). No match
|
|
215
|
+
* `'en-US'`). No match leaves an UNSELECTED slot alone, but where a track is
|
|
216
|
+
* already selected the slot is handed to the stream's own default rendition
|
|
217
|
+
* instead. No warning either way.
|
|
216
218
|
*/
|
|
217
219
|
_applyDefaultTracks() {
|
|
218
220
|
const remembered = this.languageMemory.subtitleChoice();
|
|
@@ -308,7 +310,14 @@ export class NMVideoPlayer extends EventEmitter {
|
|
|
308
310
|
// lazily so getter-style tokens (Vue refs, stores) stay live.
|
|
309
311
|
// Read per request rather than captured, so a consumer whose token or
|
|
310
312
|
// rule changes mid-session is not answered from a stale closure.
|
|
311
|
-
|
|
313
|
+
// Live config, not `options.auth`: setup seeds it and `player.auth()`
|
|
314
|
+
// updates it, so a rule set after setup reaches media requests too.
|
|
315
|
+
instance.setAuthHeaderProvider?.((url) => this.auth()?.mediaAuthorization?.(url));
|
|
316
|
+
// Without this the stream registry is seeded and never asked, so
|
|
317
|
+
// `registerStream` records a factory that no load path consults. Only
|
|
318
|
+
// consumer-registered factories are offered: HLS and progressive files
|
|
319
|
+
// stay on the backend's own path exactly as before.
|
|
320
|
+
instance.setStreamResolver?.((url, contentType) => this.resolveCustomStream?.(url, contentType));
|
|
312
321
|
// Consumer's HDR-on-SDR policy, read once here rather than normalized
|
|
313
322
|
// into `options` — `hdrDecision`'s own default ('play') applies at the
|
|
314
323
|
// point of use when this was never pushed at all.
|