@ngxsp/engine-shaka 0.0.1-next.0 → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cedvict
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @ngxsp/engine-shaka
2
+
3
+ Shaka-based playback engine for the `@ngxsp/*` runtime stack.
4
+
5
+ ## What It Provides
6
+
7
+ - Shaka-backed DASH and HLS playback
8
+ - media track mapping into `@ngxsp/core`
9
+ - quality and audio/text track selection
10
+
11
+ ## Quick Start: HLS + DRM (Widevine)
12
+
13
+ Install the runtime, the engine, and Shaka:
14
+
15
+ ```bash
16
+ npm i @ngxsp/core @ngxsp/engine-shaka shaka-player
17
+ ```
18
+
19
+ Use `createDefaultConfig` from `@ngxsp/core` to build a complete `PlayerConfig`
20
+ without restating every mandatory sub-config, then register the Shaka engine via
21
+ `createShakaEngineFactory` and drive playback with `PlayerFacade`:
22
+
23
+ ```ts
24
+ import {
25
+ PlayerFacade,
26
+ createDefaultConfig,
27
+ type PlayerConfig,
28
+ } from '@ngxsp/core';
29
+ import { createShakaEngineFactory } from '@ngxsp/engine-shaka';
30
+
31
+ // videoEl: HTMLVideoElement (e.g. an Angular @ViewChild('video') ElementRef)
32
+ const config: PlayerConfig = createDefaultConfig({
33
+ source: {
34
+ type: 'hls',
35
+ url: 'https://cdn.example.com/movie/master.m3u8',
36
+ },
37
+ drm: {
38
+ keySystems: {
39
+ 'com.widevine.alpha': {
40
+ licenseServerUrl: 'https://drm.example.com/widevine',
41
+ headers: { Authorization: 'Bearer <token>' },
42
+ },
43
+ },
44
+ },
45
+ // Optional: override a single sub-config; the rest keeps sane defaults.
46
+ subtitles: { enabled: true, providers: ['local'], preferredLang: 'fr' },
47
+ });
48
+
49
+ const facade = new PlayerFacade();
50
+
51
+ // Register the Shaka engine (it is selected automatically for HLS/DASH/DRM).
52
+ facade.attach(videoEl, [], config, undefined, [createShakaEngineFactory()]);
53
+
54
+ await facade.load(config);
55
+ await facade.play();
56
+ ```
57
+
58
+ `createDefaultConfig` fills `progress`, `ui`, `resilience`, `subtitles`,
59
+ `metadata`, and `ads` with defaults and shallow-merges any override you pass, so
60
+ only `source` is strictly required.
61
+
62
+ ## Local Commands
63
+
64
+ From this directory:
65
+
66
+ ```bash
67
+ make help
68
+ make build
69
+ make clean
70
+ ```
71
+
72
+ Direct `pnpm` equivalent:
73
+
74
+ ```bash
75
+ pnpm run build
76
+ ```
package/index.cjs ADDED
@@ -0,0 +1,386 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ShakaEngine: () => ShakaEngine,
34
+ createShakaEngineFactory: () => createShakaEngineFactory
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+ async function importShaka() {
38
+ const mod = await import("shaka-player");
39
+ return mod?.default ?? mod;
40
+ }
41
+ function sourceToUrl(source) {
42
+ const s = source;
43
+ if (typeof s.url === "string") return s.url;
44
+ throw new Error("Invalid source url");
45
+ }
46
+ function mapTracks(shaka) {
47
+ const out = [];
48
+ try {
49
+ const v = shaka.getVariantTracks?.() ?? [];
50
+ for (const t of v) {
51
+ out.push({
52
+ id: String(t.id),
53
+ kind: "audio",
54
+ language: t.language ?? "und",
55
+ label: t.label ?? t.language ?? "audio",
56
+ active: !!t.active
57
+ });
58
+ out.push({
59
+ id: `video:${t.id}`,
60
+ kind: "video",
61
+ label: formatVideoLabel(t),
62
+ active: !!t.active,
63
+ width: Number.isFinite(t.width) ? t.width : void 0,
64
+ height: Number.isFinite(t.height) ? t.height : void 0,
65
+ bandwidth: Number.isFinite(t.bandwidth) ? t.bandwidth : void 0
66
+ });
67
+ }
68
+ } catch {
69
+ }
70
+ try {
71
+ const tt = shaka.getTextTracks?.() ?? [];
72
+ for (const t of tt) {
73
+ out.push({
74
+ id: String(t.id),
75
+ kind: "text",
76
+ language: t.language ?? "und",
77
+ label: t.label ?? t.language ?? "subtitles",
78
+ active: !!t.active
79
+ });
80
+ }
81
+ } catch {
82
+ }
83
+ return out;
84
+ }
85
+ function formatVideoLabel(track) {
86
+ const height = Number.isFinite(track?.height) ? `${track.height}p` : null;
87
+ const bandwidth = Number.isFinite(track?.bandwidth) ? `${(track.bandwidth / 1e6).toFixed(1)} Mbps` : null;
88
+ return [height, bandwidth].filter(Boolean).join(" \u2022 ") || "Quality";
89
+ }
90
+ var ShakaEngine = class {
91
+ id = "shaka";
92
+ shaka = null;
93
+ player = null;
94
+ video;
95
+ off = [];
96
+ emit;
97
+ lastAbrRestrictions = null;
98
+ async attach(videoEl) {
99
+ this.video = videoEl;
100
+ const listen = (type, event) => {
101
+ const listener = () => this.fire(event());
102
+ videoEl.addEventListener(type, listener);
103
+ this.off.push(() => videoEl.removeEventListener(type, listener));
104
+ };
105
+ listen("timeupdate", () => ({ type: "timeupdate", currentTime: videoEl.currentTime, duration: videoEl.duration || 0 }));
106
+ listen("playing", () => ({ type: "state", state: "playing" }));
107
+ listen("pause", () => ({ type: "state", state: "paused" }));
108
+ listen("waiting", () => ({ type: "state", state: "buffering" }));
109
+ listen("ended", () => ({ type: "state", state: "ended" }));
110
+ }
111
+ onEvent(cb) {
112
+ this.emit = cb;
113
+ return () => {
114
+ if (this.emit === cb) this.emit = void 0;
115
+ };
116
+ }
117
+ fire(ev) {
118
+ try {
119
+ this.emit?.(ev);
120
+ } catch {
121
+ }
122
+ }
123
+ async load(source, opts) {
124
+ const shaka = await importShaka();
125
+ if (!shaka?.Player) throw new Error("Shaka Player not available");
126
+ this.shaka = shaka;
127
+ try {
128
+ shaka.polyfill?.installAll?.();
129
+ } catch {
130
+ }
131
+ try {
132
+ await this.player?.destroy?.();
133
+ } catch {
134
+ }
135
+ this.player = new shaka.Player(this.video);
136
+ try {
137
+ await this.configureAbr(this.player, source);
138
+ } catch {
139
+ }
140
+ const streamHints = source?._shakaStreaming;
141
+ if (streamHints) {
142
+ try {
143
+ this.player.configure({
144
+ streaming: {
145
+ ...streamHints.lowLatencyMode != null ? { lowLatencyMode: streamHints.lowLatencyMode } : {},
146
+ ...streamHints.bufferingGoalSec != null ? { bufferingGoal: streamHints.bufferingGoalSec } : {},
147
+ ...streamHints.rebufferingGoalSec != null ? { rebufferingGoal: streamHints.rebufferingGoalSec } : {}
148
+ }
149
+ });
150
+ } catch {
151
+ }
152
+ }
153
+ const anySrc = source;
154
+ const drm = anySrc?.drm;
155
+ if (drm?.servers || drm?.keySystems) {
156
+ const servers = { ...drm?.servers ?? {} };
157
+ const advanced = {};
158
+ const licenseHeadersByKs = {};
159
+ if (drm?.keySystems && typeof drm.keySystems === "object") {
160
+ for (const [ks, v] of Object.entries(drm.keySystems)) {
161
+ if (v?.licenseServerUrl) servers[ks] = String(v.licenseServerUrl);
162
+ if (v?.videoRobustness || v?.audioRobustness) {
163
+ advanced[ks] = {
164
+ videoRobustness: v.videoRobustness,
165
+ audioRobustness: v.audioRobustness
166
+ };
167
+ }
168
+ if (v?.headers) licenseHeadersByKs[ks] = { ...v.headers };
169
+ }
170
+ }
171
+ const retry = drm?.licenseRetry ?? {};
172
+ const retryParameters = {
173
+ maxAttempts: retry.maxAttempts ?? 3,
174
+ baseDelay: retry.baseDelayMs ?? 500,
175
+ backoffFactor: retry.backoffFactor ?? 2,
176
+ fuzzFactor: retry.fuzzFactor ?? 0.5,
177
+ timeout: retry.timeoutMs ?? 0
178
+ };
179
+ this.player.configure({
180
+ drm: {
181
+ servers,
182
+ advanced: Object.keys(advanced).length ? advanced : void 0,
183
+ retryParameters,
184
+ persistentStateRequired: !!drm?.persistentLicense
185
+ }
186
+ });
187
+ try {
188
+ const net = this.player.getNetworkingEngine?.();
189
+ const ReqType = shaka.net?.NetworkingEngine?.RequestType;
190
+ if (net?.registerRequestFilter && ReqType?.LICENSE != null) {
191
+ net.registerRequestFilter((type, request) => {
192
+ if (type !== ReqType.LICENSE) return;
193
+ const headers = {};
194
+ for (const h of Object.values(licenseHeadersByKs)) Object.assign(headers, h);
195
+ request.headers = { ...request.headers ?? {}, ...headers };
196
+ });
197
+ }
198
+ } catch {
199
+ }
200
+ }
201
+ const onErrorEvent = (evt) => {
202
+ const detail = evt?.detail ?? evt;
203
+ const code = String(detail?.code ?? "SHAKA_ERROR");
204
+ const msg = detail?.message ? String(detail.message) : "Shaka error";
205
+ this.fire({ type: "error", error: { code, message: msg, fatal: true, details: detail } });
206
+ };
207
+ this.player.addEventListener("error", onErrorEvent);
208
+ this.off.push(() => this.player?.removeEventListener?.("error", onErrorEvent));
209
+ const url = sourceToUrl(source);
210
+ this.fire({ type: "state", state: "loading" });
211
+ await this.player.load(url);
212
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
213
+ if (opts?.startTimeSec != null && Number.isFinite(opts.startTimeSec)) {
214
+ try {
215
+ await this.seekTo(opts.startTimeSec);
216
+ } catch {
217
+ }
218
+ }
219
+ this.fire({ type: "state", state: this.video.paused ? "paused" : "playing" });
220
+ }
221
+ async configureAbr(player, source) {
222
+ const hints = source?.abr;
223
+ const restrictions = {};
224
+ let reason = "";
225
+ if (hints?.maxBandwidth != null) restrictions.maxBandwidth = hints.maxBandwidth;
226
+ if (hints?.maxHeight != null) restrictions.maxHeight = hints.maxHeight;
227
+ if (!Object.keys(restrictions).length) {
228
+ const tuned = await this.autoTuneAbrWithMediaCapabilities();
229
+ if (tuned) {
230
+ Object.assign(restrictions, tuned);
231
+ reason = "mediaCapabilities";
232
+ }
233
+ } else {
234
+ reason = "source.abr";
235
+ }
236
+ if (!Object.keys(restrictions).length) return;
237
+ try {
238
+ player.configure({ abr: { restrictions } });
239
+ this.lastAbrRestrictions = { ...restrictions, reason };
240
+ } catch {
241
+ }
242
+ }
243
+ async autoTuneAbrWithMediaCapabilities() {
244
+ const mc = globalThis.navigator?.mediaCapabilities;
245
+ if (!mc?.decodingInfo) return null;
246
+ try {
247
+ const info = await mc.decodingInfo({
248
+ type: "media-source",
249
+ video: {
250
+ contentType: 'video/mp4; codecs="avc1.640028"',
251
+ width: 1920,
252
+ height: 1080,
253
+ bitrate: 5e6,
254
+ framerate: 30
255
+ },
256
+ audio: {
257
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
258
+ channels: 2,
259
+ bitrate: 128e3,
260
+ samplerate: 48e3
261
+ }
262
+ });
263
+ if (info?.supported && info.smooth !== false && info.powerEfficient !== false) return null;
264
+ return { maxHeight: 720, maxBandwidth: 3e6 };
265
+ } catch {
266
+ return null;
267
+ }
268
+ }
269
+ async destroy() {
270
+ try {
271
+ for (const fn of this.off.splice(0)) fn();
272
+ } catch {
273
+ }
274
+ try {
275
+ await this.player?.destroy?.();
276
+ } catch {
277
+ }
278
+ this.player = null;
279
+ this.shaka = null;
280
+ }
281
+ async play() {
282
+ await this.video.play();
283
+ this.fire({ type: "state", state: "playing" });
284
+ }
285
+ async pause() {
286
+ this.video.pause();
287
+ this.fire({ type: "state", state: "paused" });
288
+ }
289
+ async seekTo(timeSec) {
290
+ this.video.currentTime = Math.max(0, timeSec);
291
+ this.fire({ type: "timeupdate", currentTime: this.video.currentTime, duration: this.video.duration || 0 });
292
+ }
293
+ async setPlaybackRate(rate) {
294
+ this.video.playbackRate = rate;
295
+ }
296
+ async setVolume(volume) {
297
+ const level = Math.min(1, Math.max(0, volume));
298
+ this.video.volume = level;
299
+ this.video.muted = level <= 0;
300
+ }
301
+ async setTextTrack(trackId) {
302
+ if (!this.player?.selectTextTrack) return;
303
+ if (!trackId) {
304
+ try {
305
+ this.player.setTextTrackVisibility(false);
306
+ } catch {
307
+ }
308
+ return;
309
+ }
310
+ try {
311
+ const tracks = this.player.getTextTracks?.() ?? [];
312
+ const t = tracks.find((x) => String(x.id) === String(trackId));
313
+ if (t) {
314
+ this.player.selectTextTrack(t);
315
+ this.player.setTextTrackVisibility(true);
316
+ }
317
+ } catch {
318
+ }
319
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
320
+ }
321
+ async setAudioTrack(trackId) {
322
+ if (!this.player?.selectVariantTrack || !trackId) return;
323
+ try {
324
+ const tracks = this.player.getVariantTracks?.() ?? [];
325
+ const t = tracks.find((x) => String(x.id) === String(trackId));
326
+ if (t) this.player.selectVariantTrack(t, true);
327
+ } catch {
328
+ }
329
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
330
+ }
331
+ async setVideoTrack(trackId) {
332
+ if (!this.player?.selectVariantTrack) return;
333
+ try {
334
+ if (!trackId) {
335
+ this.player.configure?.({ abr: { enabled: true } });
336
+ } else {
337
+ const normalizedTrackId = String(trackId).replace(/^video:/, "");
338
+ const tracks = this.player.getVariantTracks?.() ?? [];
339
+ const t = tracks.find((x) => String(x.id) === normalizedTrackId);
340
+ if (t) {
341
+ this.player.configure?.({ abr: { enabled: false } });
342
+ this.player.selectVariantTrack(t, true);
343
+ }
344
+ }
345
+ } catch {
346
+ }
347
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
348
+ }
349
+ async getTracks() {
350
+ if (!this.player) return [];
351
+ return mapTracks(this.player);
352
+ }
353
+ };
354
+ function createShakaEngineFactory() {
355
+ return {
356
+ id: "shaka",
357
+ // Higher than native; only selected when it *should* be used.
358
+ priority: 50,
359
+ supports: (cfg) => {
360
+ const t = cfg.source.type;
361
+ const url = t === "url" ? cfg.source.url : cfg.source.url;
362
+ const looksLikeHls = typeof url === "string" && /\.m3u8($|\?)/i.test(url);
363
+ const looksLikeDash = typeof url === "string" && /\.mpd($|\?)/i.test(url);
364
+ const hasDrm = !!(cfg.drm ?? cfg.source.drm);
365
+ return t === "hls" || t === "dash" || looksLikeHls || looksLikeDash || hasDrm;
366
+ },
367
+ supportsWithCaps: (cfg, caps) => {
368
+ const t = cfg.source.type;
369
+ const url = t === "url" ? cfg.source.url : cfg.source.url;
370
+ const looksLikeHls = typeof url === "string" && /\.m3u8($|\?)/i.test(url);
371
+ const looksLikeDash = typeof url === "string" && /\.mpd($|\?)/i.test(url);
372
+ const drm = cfg.drm ?? cfg.source.drm;
373
+ const hasDrm = !!(drm && (drm.widevine || drm.playready || drm.fairplay || Object.keys(drm).length));
374
+ if (!caps.mse) return false;
375
+ if (hasDrm && !caps.eme) return false;
376
+ return t === "hls" || t === "dash" || looksLikeHls || looksLikeDash || hasDrm;
377
+ },
378
+ create: () => new ShakaEngine()
379
+ };
380
+ }
381
+ // Annotate the CommonJS export names for ESM import in node:
382
+ 0 && (module.exports = {
383
+ ShakaEngine,
384
+ createShakaEngineFactory
385
+ });
386
+ //# sourceMappingURL=index.cjs.map
package/index.cjs.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n PlaybackEngine,\n EngineEvent,\n LoadOptions,\n PlayerSource,\n MediaTrack,\n EngineFactory,\n PlayerConfig,\n PlaybackCapabilities\n} from '@ngxsp/core';\n\ntype ShakaNS = any;\n\nasync function importShaka(): Promise<ShakaNS> {\n // Use dynamic import to keep Shaka optional and avoid bundler static resolution issues.\n const mod: any = await import('shaka-player');\n return (mod?.default ?? mod) as ShakaNS;\n}\n\nfunction sourceToUrl(source: PlayerSource): string {\n const s: any = source as any;\n if (typeof s.url === 'string') return s.url;\n throw new Error('Invalid source url');\n}\n\nfunction mapTracks(shaka: any): MediaTrack[] {\n const out: MediaTrack[] = [];\n try {\n const v = shaka.getVariantTracks?.() ?? [];\n for (const t of v) {\n out.push({\n id: String(t.id),\n kind: 'audio',\n language: t.language ?? 'und',\n label: t.label ?? t.language ?? 'audio',\n active: !!t.active\n });\n out.push({\n id: `video:${t.id}`,\n kind: 'video',\n label: formatVideoLabel(t),\n active: !!t.active,\n width: Number.isFinite(t.width) ? t.width : undefined,\n height: Number.isFinite(t.height) ? t.height : undefined,\n bandwidth: Number.isFinite(t.bandwidth) ? t.bandwidth : undefined\n });\n }\n } catch {}\n try {\n const tt = shaka.getTextTracks?.() ?? [];\n for (const t of tt) {\n out.push({\n id: String(t.id),\n kind: 'text',\n language: t.language ?? 'und',\n label: t.label ?? t.language ?? 'subtitles',\n active: !!t.active\n });\n }\n } catch {}\n return out;\n}\n\nfunction formatVideoLabel(track: any): string {\n const height = Number.isFinite(track?.height) ? `${track.height}p` : null;\n const bandwidth =\n Number.isFinite(track?.bandwidth) ? `${(track.bandwidth / 1_000_000).toFixed(1)} Mbps` : null;\n return [height, bandwidth].filter(Boolean).join(' • ') || 'Quality';\n}\n\nexport class ShakaEngine implements PlaybackEngine {\n readonly id = 'shaka';\n\n private shaka: ShakaNS | null = null;\n private player: any | null = null;\n private video!: HTMLVideoElement;\n private off: Array<() => void> = [];\n private emit?: (ev: EngineEvent) => void;\n\n private lastAbrRestrictions: { maxHeight?: number; maxBandwidth?: number; reason?: string } | null = null;\n\n async attach(videoEl: HTMLVideoElement): Promise<void> {\n this.video = videoEl;\n // Shaka plays through the element itself: its time and state are the element's, as with the native engine.\n // Without them the controls keep showing 0:00 and the host never hears the position to resume from.\n const listen = (type: string, event: () => EngineEvent) => {\n const listener = () => this.fire(event());\n videoEl.addEventListener(type, listener);\n this.off.push(() => videoEl.removeEventListener(type, listener));\n };\n listen('timeupdate', () => ({ type: 'timeupdate', currentTime: videoEl.currentTime, duration: videoEl.duration || 0 }));\n listen('playing', () => ({ type: 'state', state: 'playing' }));\n listen('pause', () => ({ type: 'state', state: 'paused' }));\n listen('waiting', () => ({ type: 'state', state: 'buffering' }));\n listen('ended', () => ({ type: 'state', state: 'ended' }));\n }\n\n onEvent(cb: (ev: EngineEvent) => void): () => void {\n this.emit = cb;\n return () => { if (this.emit === cb) this.emit = undefined; };\n }\n\n private fire(ev: EngineEvent): void {\n try { this.emit?.(ev); } catch {}\n }\n\n async load(source: PlayerSource, opts?: LoadOptions): Promise<void> {\n const shaka: ShakaNS = await importShaka();\n if (!shaka?.Player) throw new Error('Shaka Player not available');\n this.shaka = shaka;\n\n try { shaka.polyfill?.installAll?.(); } catch {}\n\n // Clean previous player if any\n try { await this.player?.destroy?.(); } catch {}\n this.player = new shaka.Player(this.video);\n\n // ABR tuning (best-effort):\n // - explicit hints from source.abr\n // - implicit hints via MediaCapabilities probes\n try { await this.configureAbr(this.player, source); } catch {}\n\n // Profile streaming hints (best-effort): filled by core via `source._shakaStreaming`.\n const streamHints = (source as any)?._shakaStreaming as\n | { lowLatencyMode?: boolean; bufferingGoalSec?: number; rebufferingGoalSec?: number }\n | undefined;\n if (streamHints) {\n try {\n this.player.configure({\n streaming: {\n ...(streamHints.lowLatencyMode != null ? { lowLatencyMode: streamHints.lowLatencyMode } : {}),\n ...(streamHints.bufferingGoalSec != null ? { bufferingGoal: streamHints.bufferingGoalSec } : {}),\n ...(streamHints.rebufferingGoalSec != null ? { rebufferingGoal: streamHints.rebufferingGoalSec } : {})\n }\n });\n } catch {}\n }\n\n // DRM config if present\n const anySrc: any = source as any;\n const drm: any = anySrc?.drm;\n if (drm?.servers || drm?.keySystems) {\n const servers: Record<string, string> = { ...(drm?.servers ?? {}) };\n const advanced: Record<string, any> = {};\n const licenseHeadersByKs: Record<string, Record<string, string>> = {};\n\n if (drm?.keySystems && typeof drm.keySystems === \"object\") {\n for (const [ks, v] of Object.entries<any>(drm.keySystems)) {\n if (v?.licenseServerUrl) servers[ks] = String(v.licenseServerUrl);\n if (v?.videoRobustness || v?.audioRobustness) {\n advanced[ks] = {\n videoRobustness: v.videoRobustness,\n audioRobustness: v.audioRobustness\n };\n }\n if (v?.headers) licenseHeadersByKs[ks] = { ...v.headers };\n }\n }\n\n const retry = drm?.licenseRetry ?? {};\n const retryParameters = {\n maxAttempts: retry.maxAttempts ?? 3,\n baseDelay: retry.baseDelayMs ?? 500,\n backoffFactor: retry.backoffFactor ?? 2,\n fuzzFactor: retry.fuzzFactor ?? 0.5,\n timeout: retry.timeoutMs ?? 0\n };\n\n this.player.configure({\n drm: {\n servers,\n advanced: Object.keys(advanced).length ? advanced : undefined,\n retryParameters,\n persistentStateRequired: !!drm?.persistentLicense\n }\n });\n\n // Inject per-keysystem license headers (Widevine/PlayReady etc.)\n try {\n const net = this.player.getNetworkingEngine?.();\n const ReqType = (shaka as any).net?.NetworkingEngine?.RequestType;\n if (net?.registerRequestFilter && ReqType?.LICENSE != null) {\n net.registerRequestFilter((type: any, request: any) => {\n if (type !== ReqType.LICENSE) return;\n const headers: Record<string, string> = {};\n for (const h of Object.values(licenseHeadersByKs)) Object.assign(headers, h);\n request.headers = { ...(request.headers ?? {}), ...headers };\n });\n }\n } catch {}\n }\n\n // forward shaka errors\n const onErrorEvent = (evt: any) => {\n const detail = evt?.detail ?? evt;\n const code = String(detail?.code ?? 'SHAKA_ERROR');\n const msg = detail?.message ? String(detail.message) : 'Shaka error';\n this.fire({ type: 'error', error: { code, message: msg, fatal: true, details: detail } });\n };\n this.player.addEventListener('error', onErrorEvent);\n this.off.push(() => this.player?.removeEventListener?.('error', onErrorEvent));\n\n const url = sourceToUrl(source);\n this.fire({ type: 'state', state: 'loading' });\n await this.player.load(url);\n\n // initial tracks\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n\n // start time\n if (opts?.startTimeSec != null && Number.isFinite(opts.startTimeSec)) {\n try { await this.seekTo(opts.startTimeSec); } catch {}\n }\n\n this.fire({ type: 'state', state: this.video.paused ? 'paused' : 'playing' });\n }\n\n private async configureAbr(player: any, source: PlayerSource): Promise<void> {\n const hints = (source as any)?.abr as { maxHeight?: number; maxBandwidth?: number } | undefined;\n\n const restrictions: any = {};\n let reason = '';\n\n if (hints?.maxBandwidth != null) restrictions.maxBandwidth = hints.maxBandwidth;\n if (hints?.maxHeight != null) restrictions.maxHeight = hints.maxHeight;\n\n if (!Object.keys(restrictions).length) {\n const tuned = await this.autoTuneAbrWithMediaCapabilities();\n if (tuned) {\n Object.assign(restrictions, tuned);\n reason = 'mediaCapabilities';\n }\n } else {\n reason = 'source.abr';\n }\n\n if (!Object.keys(restrictions).length) return;\n\n try {\n player.configure({ abr: { restrictions } });\n this.lastAbrRestrictions = { ...restrictions, reason };\n } catch {\n // ignore\n }\n }\n\n private async autoTuneAbrWithMediaCapabilities(): Promise<{ maxHeight?: number; maxBandwidth?: number } | null> {\n const mc: any = (globalThis as any).navigator?.mediaCapabilities;\n if (!mc?.decodingInfo) return null;\n\n try {\n const info = await mc.decodingInfo({\n type: 'media-source',\n video: {\n contentType: 'video/mp4; codecs=\"avc1.640028\"',\n width: 1920,\n height: 1080,\n bitrate: 5000000,\n framerate: 30\n },\n audio: {\n contentType: 'audio/mp4; codecs=\"mp4a.40.2\"',\n channels: 2,\n bitrate: 128000,\n samplerate: 48000\n }\n });\n // If 1080p H264 is not power-efficient or not smooth, cap to conservative defaults.\n if (info?.supported && info.smooth !== false && info.powerEfficient !== false) return null;\n return { maxHeight: 720, maxBandwidth: 3000000 };\n } catch {\n return null;\n }\n }\n\n async destroy(): Promise<void> {\n try { for (const fn of this.off.splice(0)) fn(); } catch {}\n try { await this.player?.destroy?.(); } catch {}\n this.player = null;\n this.shaka = null;\n }\n\n async play(): Promise<void> {\n await this.video.play();\n this.fire({ type: 'state', state: 'playing' });\n }\n\n async pause(): Promise<void> {\n this.video.pause();\n this.fire({ type: 'state', state: 'paused' });\n }\n\n async seekTo(timeSec: number): Promise<void> {\n this.video.currentTime = Math.max(0, timeSec);\n this.fire({ type: 'timeupdate', currentTime: this.video.currentTime, duration: this.video.duration || 0 });\n }\n\n async setPlaybackRate(rate: number): Promise<void> {\n this.video.playbackRate = rate;\n }\n\n async setVolume(volume: number): Promise<void> {\n const level = Math.min(1, Math.max(0, volume));\n this.video.volume = level;\n // iOS ignores volume writes on media elements: muting is the only way to silence playback there.\n this.video.muted = level <= 0;\n }\n\n async setTextTrack(trackId: string | null): Promise<void> {\n if (!this.player?.selectTextTrack) return;\n if (!trackId) {\n try { this.player.setTextTrackVisibility(false); } catch {}\n return;\n }\n try {\n const tracks = this.player.getTextTracks?.() ?? [];\n const t = tracks.find((x: any) => String(x.id) === String(trackId));\n if (t) {\n this.player.selectTextTrack(t);\n this.player.setTextTrackVisibility(true);\n }\n } catch {}\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n }\n\n async setAudioTrack(trackId: string | null): Promise<void> {\n if (!this.player?.selectVariantTrack || !trackId) return;\n try {\n const tracks = this.player.getVariantTracks?.() ?? [];\n const t = tracks.find((x: any) => String(x.id) === String(trackId));\n if (t) this.player.selectVariantTrack(t, true);\n } catch {}\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n }\n\n async setVideoTrack(trackId: string | null): Promise<void> {\n if (!this.player?.selectVariantTrack) return;\n try {\n if (!trackId) {\n this.player.configure?.({ abr: { enabled: true } });\n } else {\n const normalizedTrackId = String(trackId).replace(/^video:/, '');\n const tracks = this.player.getVariantTracks?.() ?? [];\n const t = tracks.find((x: any) => String(x.id) === normalizedTrackId);\n if (t) {\n this.player.configure?.({ abr: { enabled: false } });\n this.player.selectVariantTrack(t, true);\n }\n }\n } catch {}\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n }\n\n async getTracks(): Promise<MediaTrack[]> {\n if (!this.player) return [];\n return mapTracks(this.player);\n }\n}\n\nexport function createShakaEngineFactory(): EngineFactory {\n return {\n id: 'shaka',\n // Higher than native; only selected when it *should* be used.\n priority: 50,\n supports: (cfg: PlayerConfig) => {\n const t = cfg.source.type as any;\n const url = t === 'url' ? (cfg.source as any).url : (cfg.source as any).url;\n const looksLikeHls = typeof url === 'string' && /\\.m3u8($|\\?)/i.test(url);\n const looksLikeDash = typeof url === 'string' && /\\.mpd($|\\?)/i.test(url);\n const hasDrm = !!((cfg as any).drm ?? (cfg.source as any).drm);\n return t === 'hls' || t === 'dash' || looksLikeHls || looksLikeDash || hasDrm;\n },\n supportsWithCaps: (cfg: PlayerConfig, caps: PlaybackCapabilities) => {\n const t = cfg.source.type as any;\n const url = t === 'url' ? (cfg.source as any).url : (cfg.source as any).url;\n const looksLikeHls = typeof url === 'string' && /\\.m3u8($|\\?)/i.test(url);\n const looksLikeDash = typeof url === 'string' && /\\.mpd($|\\?)/i.test(url);\n const drm = ((cfg as any).drm ?? (cfg.source as any).drm) as any;\n const hasDrm = !!(drm && (drm.widevine || drm.playready || drm.fairplay || Object.keys(drm).length));\n\n // Shaka is an MSE engine.\n if (!caps.mse) return false;\n // DRM requires EME.\n if (hasDrm && !caps.eme) return false;\n\n return t === 'hls' || t === 'dash' || looksLikeHls || looksLikeDash || hasDrm;\n },\n create: () => new ShakaEngine()\n };\n}\n"],"mappings":"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","names":[]}
package/index.d.cts ADDED
@@ -0,0 +1,30 @@
1
+ import { PlaybackEngine, EngineEvent, PlayerSource, LoadOptions, MediaTrack, EngineFactory } from '@ngxsp/core';
2
+
3
+ declare class ShakaEngine implements PlaybackEngine {
4
+ readonly id = "shaka";
5
+ private shaka;
6
+ private player;
7
+ private video;
8
+ private off;
9
+ private emit?;
10
+ private lastAbrRestrictions;
11
+ attach(videoEl: HTMLVideoElement): Promise<void>;
12
+ onEvent(cb: (ev: EngineEvent) => void): () => void;
13
+ private fire;
14
+ load(source: PlayerSource, opts?: LoadOptions): Promise<void>;
15
+ private configureAbr;
16
+ private autoTuneAbrWithMediaCapabilities;
17
+ destroy(): Promise<void>;
18
+ play(): Promise<void>;
19
+ pause(): Promise<void>;
20
+ seekTo(timeSec: number): Promise<void>;
21
+ setPlaybackRate(rate: number): Promise<void>;
22
+ setVolume(volume: number): Promise<void>;
23
+ setTextTrack(trackId: string | null): Promise<void>;
24
+ setAudioTrack(trackId: string | null): Promise<void>;
25
+ setVideoTrack(trackId: string | null): Promise<void>;
26
+ getTracks(): Promise<MediaTrack[]>;
27
+ }
28
+ declare function createShakaEngineFactory(): EngineFactory;
29
+
30
+ export { ShakaEngine, createShakaEngineFactory };
package/index.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ import { PlaybackEngine, EngineEvent, PlayerSource, LoadOptions, MediaTrack, EngineFactory } from '@ngxsp/core';
2
+
3
+ declare class ShakaEngine implements PlaybackEngine {
4
+ readonly id = "shaka";
5
+ private shaka;
6
+ private player;
7
+ private video;
8
+ private off;
9
+ private emit?;
10
+ private lastAbrRestrictions;
11
+ attach(videoEl: HTMLVideoElement): Promise<void>;
12
+ onEvent(cb: (ev: EngineEvent) => void): () => void;
13
+ private fire;
14
+ load(source: PlayerSource, opts?: LoadOptions): Promise<void>;
15
+ private configureAbr;
16
+ private autoTuneAbrWithMediaCapabilities;
17
+ destroy(): Promise<void>;
18
+ play(): Promise<void>;
19
+ pause(): Promise<void>;
20
+ seekTo(timeSec: number): Promise<void>;
21
+ setPlaybackRate(rate: number): Promise<void>;
22
+ setVolume(volume: number): Promise<void>;
23
+ setTextTrack(trackId: string | null): Promise<void>;
24
+ setAudioTrack(trackId: string | null): Promise<void>;
25
+ setVideoTrack(trackId: string | null): Promise<void>;
26
+ getTracks(): Promise<MediaTrack[]>;
27
+ }
28
+ declare function createShakaEngineFactory(): EngineFactory;
29
+
30
+ export { ShakaEngine, createShakaEngineFactory };
package/index.js ADDED
@@ -0,0 +1,350 @@
1
+ // src/index.ts
2
+ async function importShaka() {
3
+ const mod = await import("shaka-player");
4
+ return mod?.default ?? mod;
5
+ }
6
+ function sourceToUrl(source) {
7
+ const s = source;
8
+ if (typeof s.url === "string") return s.url;
9
+ throw new Error("Invalid source url");
10
+ }
11
+ function mapTracks(shaka) {
12
+ const out = [];
13
+ try {
14
+ const v = shaka.getVariantTracks?.() ?? [];
15
+ for (const t of v) {
16
+ out.push({
17
+ id: String(t.id),
18
+ kind: "audio",
19
+ language: t.language ?? "und",
20
+ label: t.label ?? t.language ?? "audio",
21
+ active: !!t.active
22
+ });
23
+ out.push({
24
+ id: `video:${t.id}`,
25
+ kind: "video",
26
+ label: formatVideoLabel(t),
27
+ active: !!t.active,
28
+ width: Number.isFinite(t.width) ? t.width : void 0,
29
+ height: Number.isFinite(t.height) ? t.height : void 0,
30
+ bandwidth: Number.isFinite(t.bandwidth) ? t.bandwidth : void 0
31
+ });
32
+ }
33
+ } catch {
34
+ }
35
+ try {
36
+ const tt = shaka.getTextTracks?.() ?? [];
37
+ for (const t of tt) {
38
+ out.push({
39
+ id: String(t.id),
40
+ kind: "text",
41
+ language: t.language ?? "und",
42
+ label: t.label ?? t.language ?? "subtitles",
43
+ active: !!t.active
44
+ });
45
+ }
46
+ } catch {
47
+ }
48
+ return out;
49
+ }
50
+ function formatVideoLabel(track) {
51
+ const height = Number.isFinite(track?.height) ? `${track.height}p` : null;
52
+ const bandwidth = Number.isFinite(track?.bandwidth) ? `${(track.bandwidth / 1e6).toFixed(1)} Mbps` : null;
53
+ return [height, bandwidth].filter(Boolean).join(" \u2022 ") || "Quality";
54
+ }
55
+ var ShakaEngine = class {
56
+ id = "shaka";
57
+ shaka = null;
58
+ player = null;
59
+ video;
60
+ off = [];
61
+ emit;
62
+ lastAbrRestrictions = null;
63
+ async attach(videoEl) {
64
+ this.video = videoEl;
65
+ const listen = (type, event) => {
66
+ const listener = () => this.fire(event());
67
+ videoEl.addEventListener(type, listener);
68
+ this.off.push(() => videoEl.removeEventListener(type, listener));
69
+ };
70
+ listen("timeupdate", () => ({ type: "timeupdate", currentTime: videoEl.currentTime, duration: videoEl.duration || 0 }));
71
+ listen("playing", () => ({ type: "state", state: "playing" }));
72
+ listen("pause", () => ({ type: "state", state: "paused" }));
73
+ listen("waiting", () => ({ type: "state", state: "buffering" }));
74
+ listen("ended", () => ({ type: "state", state: "ended" }));
75
+ }
76
+ onEvent(cb) {
77
+ this.emit = cb;
78
+ return () => {
79
+ if (this.emit === cb) this.emit = void 0;
80
+ };
81
+ }
82
+ fire(ev) {
83
+ try {
84
+ this.emit?.(ev);
85
+ } catch {
86
+ }
87
+ }
88
+ async load(source, opts) {
89
+ const shaka = await importShaka();
90
+ if (!shaka?.Player) throw new Error("Shaka Player not available");
91
+ this.shaka = shaka;
92
+ try {
93
+ shaka.polyfill?.installAll?.();
94
+ } catch {
95
+ }
96
+ try {
97
+ await this.player?.destroy?.();
98
+ } catch {
99
+ }
100
+ this.player = new shaka.Player(this.video);
101
+ try {
102
+ await this.configureAbr(this.player, source);
103
+ } catch {
104
+ }
105
+ const streamHints = source?._shakaStreaming;
106
+ if (streamHints) {
107
+ try {
108
+ this.player.configure({
109
+ streaming: {
110
+ ...streamHints.lowLatencyMode != null ? { lowLatencyMode: streamHints.lowLatencyMode } : {},
111
+ ...streamHints.bufferingGoalSec != null ? { bufferingGoal: streamHints.bufferingGoalSec } : {},
112
+ ...streamHints.rebufferingGoalSec != null ? { rebufferingGoal: streamHints.rebufferingGoalSec } : {}
113
+ }
114
+ });
115
+ } catch {
116
+ }
117
+ }
118
+ const anySrc = source;
119
+ const drm = anySrc?.drm;
120
+ if (drm?.servers || drm?.keySystems) {
121
+ const servers = { ...drm?.servers ?? {} };
122
+ const advanced = {};
123
+ const licenseHeadersByKs = {};
124
+ if (drm?.keySystems && typeof drm.keySystems === "object") {
125
+ for (const [ks, v] of Object.entries(drm.keySystems)) {
126
+ if (v?.licenseServerUrl) servers[ks] = String(v.licenseServerUrl);
127
+ if (v?.videoRobustness || v?.audioRobustness) {
128
+ advanced[ks] = {
129
+ videoRobustness: v.videoRobustness,
130
+ audioRobustness: v.audioRobustness
131
+ };
132
+ }
133
+ if (v?.headers) licenseHeadersByKs[ks] = { ...v.headers };
134
+ }
135
+ }
136
+ const retry = drm?.licenseRetry ?? {};
137
+ const retryParameters = {
138
+ maxAttempts: retry.maxAttempts ?? 3,
139
+ baseDelay: retry.baseDelayMs ?? 500,
140
+ backoffFactor: retry.backoffFactor ?? 2,
141
+ fuzzFactor: retry.fuzzFactor ?? 0.5,
142
+ timeout: retry.timeoutMs ?? 0
143
+ };
144
+ this.player.configure({
145
+ drm: {
146
+ servers,
147
+ advanced: Object.keys(advanced).length ? advanced : void 0,
148
+ retryParameters,
149
+ persistentStateRequired: !!drm?.persistentLicense
150
+ }
151
+ });
152
+ try {
153
+ const net = this.player.getNetworkingEngine?.();
154
+ const ReqType = shaka.net?.NetworkingEngine?.RequestType;
155
+ if (net?.registerRequestFilter && ReqType?.LICENSE != null) {
156
+ net.registerRequestFilter((type, request) => {
157
+ if (type !== ReqType.LICENSE) return;
158
+ const headers = {};
159
+ for (const h of Object.values(licenseHeadersByKs)) Object.assign(headers, h);
160
+ request.headers = { ...request.headers ?? {}, ...headers };
161
+ });
162
+ }
163
+ } catch {
164
+ }
165
+ }
166
+ const onErrorEvent = (evt) => {
167
+ const detail = evt?.detail ?? evt;
168
+ const code = String(detail?.code ?? "SHAKA_ERROR");
169
+ const msg = detail?.message ? String(detail.message) : "Shaka error";
170
+ this.fire({ type: "error", error: { code, message: msg, fatal: true, details: detail } });
171
+ };
172
+ this.player.addEventListener("error", onErrorEvent);
173
+ this.off.push(() => this.player?.removeEventListener?.("error", onErrorEvent));
174
+ const url = sourceToUrl(source);
175
+ this.fire({ type: "state", state: "loading" });
176
+ await this.player.load(url);
177
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
178
+ if (opts?.startTimeSec != null && Number.isFinite(opts.startTimeSec)) {
179
+ try {
180
+ await this.seekTo(opts.startTimeSec);
181
+ } catch {
182
+ }
183
+ }
184
+ this.fire({ type: "state", state: this.video.paused ? "paused" : "playing" });
185
+ }
186
+ async configureAbr(player, source) {
187
+ const hints = source?.abr;
188
+ const restrictions = {};
189
+ let reason = "";
190
+ if (hints?.maxBandwidth != null) restrictions.maxBandwidth = hints.maxBandwidth;
191
+ if (hints?.maxHeight != null) restrictions.maxHeight = hints.maxHeight;
192
+ if (!Object.keys(restrictions).length) {
193
+ const tuned = await this.autoTuneAbrWithMediaCapabilities();
194
+ if (tuned) {
195
+ Object.assign(restrictions, tuned);
196
+ reason = "mediaCapabilities";
197
+ }
198
+ } else {
199
+ reason = "source.abr";
200
+ }
201
+ if (!Object.keys(restrictions).length) return;
202
+ try {
203
+ player.configure({ abr: { restrictions } });
204
+ this.lastAbrRestrictions = { ...restrictions, reason };
205
+ } catch {
206
+ }
207
+ }
208
+ async autoTuneAbrWithMediaCapabilities() {
209
+ const mc = globalThis.navigator?.mediaCapabilities;
210
+ if (!mc?.decodingInfo) return null;
211
+ try {
212
+ const info = await mc.decodingInfo({
213
+ type: "media-source",
214
+ video: {
215
+ contentType: 'video/mp4; codecs="avc1.640028"',
216
+ width: 1920,
217
+ height: 1080,
218
+ bitrate: 5e6,
219
+ framerate: 30
220
+ },
221
+ audio: {
222
+ contentType: 'audio/mp4; codecs="mp4a.40.2"',
223
+ channels: 2,
224
+ bitrate: 128e3,
225
+ samplerate: 48e3
226
+ }
227
+ });
228
+ if (info?.supported && info.smooth !== false && info.powerEfficient !== false) return null;
229
+ return { maxHeight: 720, maxBandwidth: 3e6 };
230
+ } catch {
231
+ return null;
232
+ }
233
+ }
234
+ async destroy() {
235
+ try {
236
+ for (const fn of this.off.splice(0)) fn();
237
+ } catch {
238
+ }
239
+ try {
240
+ await this.player?.destroy?.();
241
+ } catch {
242
+ }
243
+ this.player = null;
244
+ this.shaka = null;
245
+ }
246
+ async play() {
247
+ await this.video.play();
248
+ this.fire({ type: "state", state: "playing" });
249
+ }
250
+ async pause() {
251
+ this.video.pause();
252
+ this.fire({ type: "state", state: "paused" });
253
+ }
254
+ async seekTo(timeSec) {
255
+ this.video.currentTime = Math.max(0, timeSec);
256
+ this.fire({ type: "timeupdate", currentTime: this.video.currentTime, duration: this.video.duration || 0 });
257
+ }
258
+ async setPlaybackRate(rate) {
259
+ this.video.playbackRate = rate;
260
+ }
261
+ async setVolume(volume) {
262
+ const level = Math.min(1, Math.max(0, volume));
263
+ this.video.volume = level;
264
+ this.video.muted = level <= 0;
265
+ }
266
+ async setTextTrack(trackId) {
267
+ if (!this.player?.selectTextTrack) return;
268
+ if (!trackId) {
269
+ try {
270
+ this.player.setTextTrackVisibility(false);
271
+ } catch {
272
+ }
273
+ return;
274
+ }
275
+ try {
276
+ const tracks = this.player.getTextTracks?.() ?? [];
277
+ const t = tracks.find((x) => String(x.id) === String(trackId));
278
+ if (t) {
279
+ this.player.selectTextTrack(t);
280
+ this.player.setTextTrackVisibility(true);
281
+ }
282
+ } catch {
283
+ }
284
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
285
+ }
286
+ async setAudioTrack(trackId) {
287
+ if (!this.player?.selectVariantTrack || !trackId) return;
288
+ try {
289
+ const tracks = this.player.getVariantTracks?.() ?? [];
290
+ const t = tracks.find((x) => String(x.id) === String(trackId));
291
+ if (t) this.player.selectVariantTrack(t, true);
292
+ } catch {
293
+ }
294
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
295
+ }
296
+ async setVideoTrack(trackId) {
297
+ if (!this.player?.selectVariantTrack) return;
298
+ try {
299
+ if (!trackId) {
300
+ this.player.configure?.({ abr: { enabled: true } });
301
+ } else {
302
+ const normalizedTrackId = String(trackId).replace(/^video:/, "");
303
+ const tracks = this.player.getVariantTracks?.() ?? [];
304
+ const t = tracks.find((x) => String(x.id) === normalizedTrackId);
305
+ if (t) {
306
+ this.player.configure?.({ abr: { enabled: false } });
307
+ this.player.selectVariantTrack(t, true);
308
+ }
309
+ }
310
+ } catch {
311
+ }
312
+ this.fire({ type: "tracks", tracks: mapTracks(this.player) });
313
+ }
314
+ async getTracks() {
315
+ if (!this.player) return [];
316
+ return mapTracks(this.player);
317
+ }
318
+ };
319
+ function createShakaEngineFactory() {
320
+ return {
321
+ id: "shaka",
322
+ // Higher than native; only selected when it *should* be used.
323
+ priority: 50,
324
+ supports: (cfg) => {
325
+ const t = cfg.source.type;
326
+ const url = t === "url" ? cfg.source.url : cfg.source.url;
327
+ const looksLikeHls = typeof url === "string" && /\.m3u8($|\?)/i.test(url);
328
+ const looksLikeDash = typeof url === "string" && /\.mpd($|\?)/i.test(url);
329
+ const hasDrm = !!(cfg.drm ?? cfg.source.drm);
330
+ return t === "hls" || t === "dash" || looksLikeHls || looksLikeDash || hasDrm;
331
+ },
332
+ supportsWithCaps: (cfg, caps) => {
333
+ const t = cfg.source.type;
334
+ const url = t === "url" ? cfg.source.url : cfg.source.url;
335
+ const looksLikeHls = typeof url === "string" && /\.m3u8($|\?)/i.test(url);
336
+ const looksLikeDash = typeof url === "string" && /\.mpd($|\?)/i.test(url);
337
+ const drm = cfg.drm ?? cfg.source.drm;
338
+ const hasDrm = !!(drm && (drm.widevine || drm.playready || drm.fairplay || Object.keys(drm).length));
339
+ if (!caps.mse) return false;
340
+ if (hasDrm && !caps.eme) return false;
341
+ return t === "hls" || t === "dash" || looksLikeHls || looksLikeDash || hasDrm;
342
+ },
343
+ create: () => new ShakaEngine()
344
+ };
345
+ }
346
+ export {
347
+ ShakaEngine,
348
+ createShakaEngineFactory
349
+ };
350
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n PlaybackEngine,\n EngineEvent,\n LoadOptions,\n PlayerSource,\n MediaTrack,\n EngineFactory,\n PlayerConfig,\n PlaybackCapabilities\n} from '@ngxsp/core';\n\ntype ShakaNS = any;\n\nasync function importShaka(): Promise<ShakaNS> {\n // Use dynamic import to keep Shaka optional and avoid bundler static resolution issues.\n const mod: any = await import('shaka-player');\n return (mod?.default ?? mod) as ShakaNS;\n}\n\nfunction sourceToUrl(source: PlayerSource): string {\n const s: any = source as any;\n if (typeof s.url === 'string') return s.url;\n throw new Error('Invalid source url');\n}\n\nfunction mapTracks(shaka: any): MediaTrack[] {\n const out: MediaTrack[] = [];\n try {\n const v = shaka.getVariantTracks?.() ?? [];\n for (const t of v) {\n out.push({\n id: String(t.id),\n kind: 'audio',\n language: t.language ?? 'und',\n label: t.label ?? t.language ?? 'audio',\n active: !!t.active\n });\n out.push({\n id: `video:${t.id}`,\n kind: 'video',\n label: formatVideoLabel(t),\n active: !!t.active,\n width: Number.isFinite(t.width) ? t.width : undefined,\n height: Number.isFinite(t.height) ? t.height : undefined,\n bandwidth: Number.isFinite(t.bandwidth) ? t.bandwidth : undefined\n });\n }\n } catch {}\n try {\n const tt = shaka.getTextTracks?.() ?? [];\n for (const t of tt) {\n out.push({\n id: String(t.id),\n kind: 'text',\n language: t.language ?? 'und',\n label: t.label ?? t.language ?? 'subtitles',\n active: !!t.active\n });\n }\n } catch {}\n return out;\n}\n\nfunction formatVideoLabel(track: any): string {\n const height = Number.isFinite(track?.height) ? `${track.height}p` : null;\n const bandwidth =\n Number.isFinite(track?.bandwidth) ? `${(track.bandwidth / 1_000_000).toFixed(1)} Mbps` : null;\n return [height, bandwidth].filter(Boolean).join(' • ') || 'Quality';\n}\n\nexport class ShakaEngine implements PlaybackEngine {\n readonly id = 'shaka';\n\n private shaka: ShakaNS | null = null;\n private player: any | null = null;\n private video!: HTMLVideoElement;\n private off: Array<() => void> = [];\n private emit?: (ev: EngineEvent) => void;\n\n private lastAbrRestrictions: { maxHeight?: number; maxBandwidth?: number; reason?: string } | null = null;\n\n async attach(videoEl: HTMLVideoElement): Promise<void> {\n this.video = videoEl;\n // Shaka plays through the element itself: its time and state are the element's, as with the native engine.\n // Without them the controls keep showing 0:00 and the host never hears the position to resume from.\n const listen = (type: string, event: () => EngineEvent) => {\n const listener = () => this.fire(event());\n videoEl.addEventListener(type, listener);\n this.off.push(() => videoEl.removeEventListener(type, listener));\n };\n listen('timeupdate', () => ({ type: 'timeupdate', currentTime: videoEl.currentTime, duration: videoEl.duration || 0 }));\n listen('playing', () => ({ type: 'state', state: 'playing' }));\n listen('pause', () => ({ type: 'state', state: 'paused' }));\n listen('waiting', () => ({ type: 'state', state: 'buffering' }));\n listen('ended', () => ({ type: 'state', state: 'ended' }));\n }\n\n onEvent(cb: (ev: EngineEvent) => void): () => void {\n this.emit = cb;\n return () => { if (this.emit === cb) this.emit = undefined; };\n }\n\n private fire(ev: EngineEvent): void {\n try { this.emit?.(ev); } catch {}\n }\n\n async load(source: PlayerSource, opts?: LoadOptions): Promise<void> {\n const shaka: ShakaNS = await importShaka();\n if (!shaka?.Player) throw new Error('Shaka Player not available');\n this.shaka = shaka;\n\n try { shaka.polyfill?.installAll?.(); } catch {}\n\n // Clean previous player if any\n try { await this.player?.destroy?.(); } catch {}\n this.player = new shaka.Player(this.video);\n\n // ABR tuning (best-effort):\n // - explicit hints from source.abr\n // - implicit hints via MediaCapabilities probes\n try { await this.configureAbr(this.player, source); } catch {}\n\n // Profile streaming hints (best-effort): filled by core via `source._shakaStreaming`.\n const streamHints = (source as any)?._shakaStreaming as\n | { lowLatencyMode?: boolean; bufferingGoalSec?: number; rebufferingGoalSec?: number }\n | undefined;\n if (streamHints) {\n try {\n this.player.configure({\n streaming: {\n ...(streamHints.lowLatencyMode != null ? { lowLatencyMode: streamHints.lowLatencyMode } : {}),\n ...(streamHints.bufferingGoalSec != null ? { bufferingGoal: streamHints.bufferingGoalSec } : {}),\n ...(streamHints.rebufferingGoalSec != null ? { rebufferingGoal: streamHints.rebufferingGoalSec } : {})\n }\n });\n } catch {}\n }\n\n // DRM config if present\n const anySrc: any = source as any;\n const drm: any = anySrc?.drm;\n if (drm?.servers || drm?.keySystems) {\n const servers: Record<string, string> = { ...(drm?.servers ?? {}) };\n const advanced: Record<string, any> = {};\n const licenseHeadersByKs: Record<string, Record<string, string>> = {};\n\n if (drm?.keySystems && typeof drm.keySystems === \"object\") {\n for (const [ks, v] of Object.entries<any>(drm.keySystems)) {\n if (v?.licenseServerUrl) servers[ks] = String(v.licenseServerUrl);\n if (v?.videoRobustness || v?.audioRobustness) {\n advanced[ks] = {\n videoRobustness: v.videoRobustness,\n audioRobustness: v.audioRobustness\n };\n }\n if (v?.headers) licenseHeadersByKs[ks] = { ...v.headers };\n }\n }\n\n const retry = drm?.licenseRetry ?? {};\n const retryParameters = {\n maxAttempts: retry.maxAttempts ?? 3,\n baseDelay: retry.baseDelayMs ?? 500,\n backoffFactor: retry.backoffFactor ?? 2,\n fuzzFactor: retry.fuzzFactor ?? 0.5,\n timeout: retry.timeoutMs ?? 0\n };\n\n this.player.configure({\n drm: {\n servers,\n advanced: Object.keys(advanced).length ? advanced : undefined,\n retryParameters,\n persistentStateRequired: !!drm?.persistentLicense\n }\n });\n\n // Inject per-keysystem license headers (Widevine/PlayReady etc.)\n try {\n const net = this.player.getNetworkingEngine?.();\n const ReqType = (shaka as any).net?.NetworkingEngine?.RequestType;\n if (net?.registerRequestFilter && ReqType?.LICENSE != null) {\n net.registerRequestFilter((type: any, request: any) => {\n if (type !== ReqType.LICENSE) return;\n const headers: Record<string, string> = {};\n for (const h of Object.values(licenseHeadersByKs)) Object.assign(headers, h);\n request.headers = { ...(request.headers ?? {}), ...headers };\n });\n }\n } catch {}\n }\n\n // forward shaka errors\n const onErrorEvent = (evt: any) => {\n const detail = evt?.detail ?? evt;\n const code = String(detail?.code ?? 'SHAKA_ERROR');\n const msg = detail?.message ? String(detail.message) : 'Shaka error';\n this.fire({ type: 'error', error: { code, message: msg, fatal: true, details: detail } });\n };\n this.player.addEventListener('error', onErrorEvent);\n this.off.push(() => this.player?.removeEventListener?.('error', onErrorEvent));\n\n const url = sourceToUrl(source);\n this.fire({ type: 'state', state: 'loading' });\n await this.player.load(url);\n\n // initial tracks\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n\n // start time\n if (opts?.startTimeSec != null && Number.isFinite(opts.startTimeSec)) {\n try { await this.seekTo(opts.startTimeSec); } catch {}\n }\n\n this.fire({ type: 'state', state: this.video.paused ? 'paused' : 'playing' });\n }\n\n private async configureAbr(player: any, source: PlayerSource): Promise<void> {\n const hints = (source as any)?.abr as { maxHeight?: number; maxBandwidth?: number } | undefined;\n\n const restrictions: any = {};\n let reason = '';\n\n if (hints?.maxBandwidth != null) restrictions.maxBandwidth = hints.maxBandwidth;\n if (hints?.maxHeight != null) restrictions.maxHeight = hints.maxHeight;\n\n if (!Object.keys(restrictions).length) {\n const tuned = await this.autoTuneAbrWithMediaCapabilities();\n if (tuned) {\n Object.assign(restrictions, tuned);\n reason = 'mediaCapabilities';\n }\n } else {\n reason = 'source.abr';\n }\n\n if (!Object.keys(restrictions).length) return;\n\n try {\n player.configure({ abr: { restrictions } });\n this.lastAbrRestrictions = { ...restrictions, reason };\n } catch {\n // ignore\n }\n }\n\n private async autoTuneAbrWithMediaCapabilities(): Promise<{ maxHeight?: number; maxBandwidth?: number } | null> {\n const mc: any = (globalThis as any).navigator?.mediaCapabilities;\n if (!mc?.decodingInfo) return null;\n\n try {\n const info = await mc.decodingInfo({\n type: 'media-source',\n video: {\n contentType: 'video/mp4; codecs=\"avc1.640028\"',\n width: 1920,\n height: 1080,\n bitrate: 5000000,\n framerate: 30\n },\n audio: {\n contentType: 'audio/mp4; codecs=\"mp4a.40.2\"',\n channels: 2,\n bitrate: 128000,\n samplerate: 48000\n }\n });\n // If 1080p H264 is not power-efficient or not smooth, cap to conservative defaults.\n if (info?.supported && info.smooth !== false && info.powerEfficient !== false) return null;\n return { maxHeight: 720, maxBandwidth: 3000000 };\n } catch {\n return null;\n }\n }\n\n async destroy(): Promise<void> {\n try { for (const fn of this.off.splice(0)) fn(); } catch {}\n try { await this.player?.destroy?.(); } catch {}\n this.player = null;\n this.shaka = null;\n }\n\n async play(): Promise<void> {\n await this.video.play();\n this.fire({ type: 'state', state: 'playing' });\n }\n\n async pause(): Promise<void> {\n this.video.pause();\n this.fire({ type: 'state', state: 'paused' });\n }\n\n async seekTo(timeSec: number): Promise<void> {\n this.video.currentTime = Math.max(0, timeSec);\n this.fire({ type: 'timeupdate', currentTime: this.video.currentTime, duration: this.video.duration || 0 });\n }\n\n async setPlaybackRate(rate: number): Promise<void> {\n this.video.playbackRate = rate;\n }\n\n async setVolume(volume: number): Promise<void> {\n const level = Math.min(1, Math.max(0, volume));\n this.video.volume = level;\n // iOS ignores volume writes on media elements: muting is the only way to silence playback there.\n this.video.muted = level <= 0;\n }\n\n async setTextTrack(trackId: string | null): Promise<void> {\n if (!this.player?.selectTextTrack) return;\n if (!trackId) {\n try { this.player.setTextTrackVisibility(false); } catch {}\n return;\n }\n try {\n const tracks = this.player.getTextTracks?.() ?? [];\n const t = tracks.find((x: any) => String(x.id) === String(trackId));\n if (t) {\n this.player.selectTextTrack(t);\n this.player.setTextTrackVisibility(true);\n }\n } catch {}\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n }\n\n async setAudioTrack(trackId: string | null): Promise<void> {\n if (!this.player?.selectVariantTrack || !trackId) return;\n try {\n const tracks = this.player.getVariantTracks?.() ?? [];\n const t = tracks.find((x: any) => String(x.id) === String(trackId));\n if (t) this.player.selectVariantTrack(t, true);\n } catch {}\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n }\n\n async setVideoTrack(trackId: string | null): Promise<void> {\n if (!this.player?.selectVariantTrack) return;\n try {\n if (!trackId) {\n this.player.configure?.({ abr: { enabled: true } });\n } else {\n const normalizedTrackId = String(trackId).replace(/^video:/, '');\n const tracks = this.player.getVariantTracks?.() ?? [];\n const t = tracks.find((x: any) => String(x.id) === normalizedTrackId);\n if (t) {\n this.player.configure?.({ abr: { enabled: false } });\n this.player.selectVariantTrack(t, true);\n }\n }\n } catch {}\n this.fire({ type: 'tracks', tracks: mapTracks(this.player) });\n }\n\n async getTracks(): Promise<MediaTrack[]> {\n if (!this.player) return [];\n return mapTracks(this.player);\n }\n}\n\nexport function createShakaEngineFactory(): EngineFactory {\n return {\n id: 'shaka',\n // Higher than native; only selected when it *should* be used.\n priority: 50,\n supports: (cfg: PlayerConfig) => {\n const t = cfg.source.type as any;\n const url = t === 'url' ? (cfg.source as any).url : (cfg.source as any).url;\n const looksLikeHls = typeof url === 'string' && /\\.m3u8($|\\?)/i.test(url);\n const looksLikeDash = typeof url === 'string' && /\\.mpd($|\\?)/i.test(url);\n const hasDrm = !!((cfg as any).drm ?? (cfg.source as any).drm);\n return t === 'hls' || t === 'dash' || looksLikeHls || looksLikeDash || hasDrm;\n },\n supportsWithCaps: (cfg: PlayerConfig, caps: PlaybackCapabilities) => {\n const t = cfg.source.type as any;\n const url = t === 'url' ? (cfg.source as any).url : (cfg.source as any).url;\n const looksLikeHls = typeof url === 'string' && /\\.m3u8($|\\?)/i.test(url);\n const looksLikeDash = typeof url === 'string' && /\\.mpd($|\\?)/i.test(url);\n const drm = ((cfg as any).drm ?? (cfg.source as any).drm) as any;\n const hasDrm = !!(drm && (drm.widevine || drm.playready || drm.fairplay || Object.keys(drm).length));\n\n // Shaka is an MSE engine.\n if (!caps.mse) return false;\n // DRM requires EME.\n if (hasDrm && !caps.eme) return false;\n\n return t === 'hls' || t === 'dash' || looksLikeHls || looksLikeDash || hasDrm;\n },\n create: () => new ShakaEngine()\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,24 +1,32 @@
1
1
  {
2
2
  "name": "@ngxsp/engine-shaka",
3
- "version": "0.0.1-next.0",
4
- "type": "module",
5
- "main": "dist/index.cjs",
6
- "module": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "files": [
9
- "dist"
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "description": "Shaka-based playback engine for ngxsp audio and video runtimes.",
6
+ "keywords": [
7
+ "audio",
8
+ "video",
9
+ "media",
10
+ "player",
11
+ "shaka",
12
+ "dash",
13
+ "hls"
10
14
  ],
15
+ "type": "module",
16
+ "main": "index.cjs",
17
+ "module": "index.js",
18
+ "types": "index.d.ts",
11
19
  "sideEffects": false,
12
20
  "dependencies": {
13
21
  "shaka-player": "^5.0.2",
14
- "@ngxsp/core": "0.0.1-next.0"
22
+ "@ngxsp/core": "^0.1.0"
15
23
  },
16
24
  "exports": {
17
25
  ".": {
18
- "types": "./dist/index.d.ts",
19
- "import": "./dist/index.js",
20
- "require": "./dist/index.cjs",
21
- "default": "./dist/index.js"
26
+ "types": "./index.d.ts",
27
+ "import": "./index.js",
28
+ "require": "./index.cjs",
29
+ "default": "./index.js"
22
30
  }
23
31
  }
24
32
  }