@codenkay/video-nsgplayer-core 1.0.0 → 2.0.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/CHANGELOG.md +14 -0
- package/dist/index.cjs +201 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +201 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `@codenkay/video-nsgplayer-ui` control chrome package; emit timeupdate/progress and text/audio track Runtime APIs from core for host UI parity.
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- Emit `timeupdate` and `progress` from the mounted media element for host UI seek bars.
|
|
14
|
+
- Add text/audio track Runtime API: `getTextTracks`, `getSelectedTextTrack`, `setTextTrack`, `getAudioTracks`, `getSelectedAudioTrack`, `setAudioTrack`, plus `texttrackchange` / `audiotrackchange` events.
|
|
15
|
+
- Add `setFullscreenElement` for host chrome fullscreen targeting. Zoom remains UI-only (CSS transform).
|
|
16
|
+
|
|
3
17
|
## 1.0.0
|
|
4
18
|
|
|
5
19
|
### Major Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1037,6 +1037,11 @@ var HlsEngine = class {
|
|
|
1037
1037
|
this.mediaRecoveryCount = 0;
|
|
1038
1038
|
this.qualities = [];
|
|
1039
1039
|
this.qualitySelection = "auto";
|
|
1040
|
+
this.textTracks = [];
|
|
1041
|
+
this.textTrackSelection = "off";
|
|
1042
|
+
this.audioTracks = [];
|
|
1043
|
+
this.audioTrackSelection = 0;
|
|
1044
|
+
this.nativeTextTrackCleanup = null;
|
|
1040
1045
|
}
|
|
1041
1046
|
async start() {
|
|
1042
1047
|
const { videoEl, playbackUrl, startMuted, volume } = this.options;
|
|
@@ -1056,6 +1061,7 @@ var HlsEngine = class {
|
|
|
1056
1061
|
this.options.callbacks.onSupportMode?.("native");
|
|
1057
1062
|
videoEl.src = this.options.authParams.applyToUrl(playbackUrl);
|
|
1058
1063
|
this.bindMediaEvents();
|
|
1064
|
+
this.bindNativeTextTracks();
|
|
1059
1065
|
this.options.callbacks.onStateChange?.("ready");
|
|
1060
1066
|
await this.applyStartPositionAndAutoplay();
|
|
1061
1067
|
return;
|
|
@@ -1099,9 +1105,19 @@ var HlsEngine = class {
|
|
|
1099
1105
|
label: level.height != null && level.height > 0 ? `${level.height}p` : `Level ${index}`
|
|
1100
1106
|
}));
|
|
1101
1107
|
callbacks.onQualitiesChange?.(this.qualities);
|
|
1108
|
+
this.syncHlsSubtitleTracks();
|
|
1109
|
+
this.syncHlsAudioTracks();
|
|
1102
1110
|
callbacks.onStateChange?.("ready");
|
|
1103
1111
|
void this.applyStartPositionAndAutoplay();
|
|
1104
1112
|
});
|
|
1113
|
+
this.hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, () => {
|
|
1114
|
+
if (this.destroyed) return;
|
|
1115
|
+
this.syncHlsSubtitleTracks();
|
|
1116
|
+
});
|
|
1117
|
+
this.hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
|
|
1118
|
+
if (this.destroyed) return;
|
|
1119
|
+
this.syncHlsAudioTracks();
|
|
1120
|
+
});
|
|
1105
1121
|
this.hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
1106
1122
|
this.handleHlsError(Hls, data);
|
|
1107
1123
|
});
|
|
@@ -1227,12 +1243,120 @@ var HlsEngine = class {
|
|
|
1227
1243
|
getQualities() {
|
|
1228
1244
|
return this.qualities;
|
|
1229
1245
|
}
|
|
1246
|
+
getTextTracks() {
|
|
1247
|
+
return this.textTracks;
|
|
1248
|
+
}
|
|
1249
|
+
getTextTrackSelection() {
|
|
1250
|
+
return this.textTrackSelection;
|
|
1251
|
+
}
|
|
1252
|
+
setTextTrack(selection) {
|
|
1253
|
+
if (this.destroyed) return;
|
|
1254
|
+
this.textTrackSelection = selection;
|
|
1255
|
+
this.options.callbacks.onTextTrackSelectionChange?.(selection);
|
|
1256
|
+
if (this.supportMode === "hls.js" && this.hls) {
|
|
1257
|
+
this.hls.subtitleDisplay = selection !== "off";
|
|
1258
|
+
this.hls.subtitleTrack = selection === "off" ? -1 : selection;
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
if (this.supportMode === "native" && this.options.videoEl) {
|
|
1262
|
+
const tracks = this.options.videoEl.textTracks;
|
|
1263
|
+
for (let i = 0; i < tracks.length; i += 1) {
|
|
1264
|
+
const track = tracks[i];
|
|
1265
|
+
track.mode = selection !== "off" && i === selection ? "showing" : "disabled";
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
getAudioTracks() {
|
|
1270
|
+
return this.audioTracks;
|
|
1271
|
+
}
|
|
1272
|
+
getAudioTrackSelection() {
|
|
1273
|
+
return this.audioTrackSelection;
|
|
1274
|
+
}
|
|
1275
|
+
setAudioTrack(index) {
|
|
1276
|
+
if (this.destroyed) return;
|
|
1277
|
+
if (!Number.isInteger(index) || index < 0 || this.audioTracks.length > 0 && index >= this.audioTracks.length) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
this.audioTrackSelection = index;
|
|
1281
|
+
this.options.callbacks.onAudioTrackSelectionChange?.(index);
|
|
1282
|
+
if (this.supportMode === "hls.js" && this.hls) {
|
|
1283
|
+
this.hls.audioTrack = index;
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (this.supportMode === "native" && this.options.videoEl) {
|
|
1287
|
+
const audioTracks = this.options.videoEl.audioTracks;
|
|
1288
|
+
if (audioTracks && audioTracks.length > 0) {
|
|
1289
|
+
for (let i = 0; i < audioTracks.length; i += 1) {
|
|
1290
|
+
audioTracks[i].enabled = i === index;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1230
1295
|
getSupportMode() {
|
|
1231
1296
|
return this.supportMode;
|
|
1232
1297
|
}
|
|
1298
|
+
syncHlsSubtitleTracks() {
|
|
1299
|
+
if (!this.hls) return;
|
|
1300
|
+
const tracks = (this.hls.subtitleTracks ?? []).map(
|
|
1301
|
+
(track, index) => ({
|
|
1302
|
+
index,
|
|
1303
|
+
id: String(track.id ?? index),
|
|
1304
|
+
label: track.name || track.lang || `Subtitles ${index + 1}`,
|
|
1305
|
+
language: track.lang
|
|
1306
|
+
})
|
|
1307
|
+
);
|
|
1308
|
+
this.textTracks = tracks;
|
|
1309
|
+
this.options.callbacks.onTextTracksChange?.(tracks);
|
|
1310
|
+
}
|
|
1311
|
+
syncHlsAudioTracks() {
|
|
1312
|
+
if (!this.hls) return;
|
|
1313
|
+
const tracks = (this.hls.audioTracks ?? []).map(
|
|
1314
|
+
(track, index) => ({
|
|
1315
|
+
index,
|
|
1316
|
+
id: String(track.id ?? index),
|
|
1317
|
+
label: track.name || track.lang || `Audio ${index + 1}`,
|
|
1318
|
+
language: track.lang
|
|
1319
|
+
})
|
|
1320
|
+
);
|
|
1321
|
+
this.audioTracks = tracks;
|
|
1322
|
+
this.options.callbacks.onAudioTracksChange?.(tracks);
|
|
1323
|
+
if (tracks.length > 0) {
|
|
1324
|
+
this.audioTrackSelection = 0;
|
|
1325
|
+
this.options.callbacks.onAudioTrackSelectionChange?.(0);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
bindNativeTextTracks() {
|
|
1329
|
+
const { videoEl } = this.options;
|
|
1330
|
+
const syncFromElement = () => {
|
|
1331
|
+
if (this.destroyed || this.textTracks.length > 0) return;
|
|
1332
|
+
const list = videoEl.textTracks;
|
|
1333
|
+
const tracks = [];
|
|
1334
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
1335
|
+
const track = list[i];
|
|
1336
|
+
if (track.kind !== "subtitles" && track.kind !== "captions") continue;
|
|
1337
|
+
tracks.push({
|
|
1338
|
+
index: tracks.length,
|
|
1339
|
+
id: track.id || `native-${tracks.length}`,
|
|
1340
|
+
label: track.label || track.language || `Track ${tracks.length + 1}`,
|
|
1341
|
+
language: track.language || void 0
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
if (tracks.length === 0) return;
|
|
1345
|
+
this.textTracks = tracks;
|
|
1346
|
+
this.options.callbacks.onTextTracksChange?.(tracks);
|
|
1347
|
+
};
|
|
1348
|
+
const textTracks = videoEl.textTracks;
|
|
1349
|
+
textTracks.addEventListener?.("addtrack", syncFromElement);
|
|
1350
|
+
syncFromElement();
|
|
1351
|
+
this.nativeTextTrackCleanup = () => {
|
|
1352
|
+
textTracks.removeEventListener?.("addtrack", syncFromElement);
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1233
1355
|
destroy() {
|
|
1234
1356
|
if (this.destroyed) return;
|
|
1235
1357
|
this.destroyed = true;
|
|
1358
|
+
this.nativeTextTrackCleanup?.();
|
|
1359
|
+
this.nativeTextTrackCleanup = null;
|
|
1236
1360
|
this.hls?.destroy();
|
|
1237
1361
|
this.hls = null;
|
|
1238
1362
|
const { videoEl } = this.options;
|
|
@@ -1289,11 +1413,40 @@ function createVideoPlayer(options) {
|
|
|
1289
1413
|
let refreshManager = null;
|
|
1290
1414
|
let fullscreenEl = null;
|
|
1291
1415
|
let isLiveStream = false;
|
|
1416
|
+
let mediaCleanup = null;
|
|
1292
1417
|
const setState = (next) => {
|
|
1293
1418
|
if (destroyed && next !== "destroyed") return;
|
|
1294
1419
|
state = next;
|
|
1295
1420
|
events.emit("statechange", { state: next });
|
|
1296
1421
|
};
|
|
1422
|
+
const bindMediaProgressEvents = (el) => {
|
|
1423
|
+
mediaCleanup?.();
|
|
1424
|
+
const onTimeUpdate = () => {
|
|
1425
|
+
if (destroyed) return;
|
|
1426
|
+
events.emit("timeupdate", {
|
|
1427
|
+
currentTime: el.currentTime,
|
|
1428
|
+
duration: Number.isFinite(el.duration) ? el.duration : 0
|
|
1429
|
+
});
|
|
1430
|
+
};
|
|
1431
|
+
const onProgress = () => {
|
|
1432
|
+
if (destroyed) return;
|
|
1433
|
+
let bufferedEnd = 0;
|
|
1434
|
+
try {
|
|
1435
|
+
if (el.buffered.length > 0) {
|
|
1436
|
+
bufferedEnd = el.buffered.end(el.buffered.length - 1);
|
|
1437
|
+
}
|
|
1438
|
+
} catch {
|
|
1439
|
+
bufferedEnd = 0;
|
|
1440
|
+
}
|
|
1441
|
+
events.emit("progress", { bufferedEnd });
|
|
1442
|
+
};
|
|
1443
|
+
el.addEventListener("timeupdate", onTimeUpdate);
|
|
1444
|
+
el.addEventListener("progress", onProgress);
|
|
1445
|
+
mediaCleanup = () => {
|
|
1446
|
+
el.removeEventListener("timeupdate", onTimeUpdate);
|
|
1447
|
+
el.removeEventListener("progress", onProgress);
|
|
1448
|
+
};
|
|
1449
|
+
};
|
|
1297
1450
|
const authManager = new AuthManager(
|
|
1298
1451
|
options.auth,
|
|
1299
1452
|
options.auth?.refreshBeforeExpiryMs ?? 6e4,
|
|
@@ -1344,6 +1497,7 @@ function createVideoPlayer(options) {
|
|
|
1344
1497
|
isLiveStream = Boolean(source.live);
|
|
1345
1498
|
}
|
|
1346
1499
|
const el = ensureVideoElement();
|
|
1500
|
+
bindMediaProgressEvents(el);
|
|
1347
1501
|
engine?.destroy();
|
|
1348
1502
|
engine = new HlsEngine({
|
|
1349
1503
|
videoEl: el,
|
|
@@ -1402,6 +1556,30 @@ function createVideoPlayer(options) {
|
|
|
1402
1556
|
label: typeof selection === "number" ? level?.label : "auto"
|
|
1403
1557
|
});
|
|
1404
1558
|
},
|
|
1559
|
+
onTextTracksChange: (tracks) => {
|
|
1560
|
+
events.emit("texttrackchange", {
|
|
1561
|
+
tracks,
|
|
1562
|
+
selection: engine?.getTextTrackSelection() ?? "off"
|
|
1563
|
+
});
|
|
1564
|
+
},
|
|
1565
|
+
onTextTrackSelectionChange: (selection) => {
|
|
1566
|
+
events.emit("texttrackchange", {
|
|
1567
|
+
tracks: engine?.getTextTracks() ?? [],
|
|
1568
|
+
selection
|
|
1569
|
+
});
|
|
1570
|
+
},
|
|
1571
|
+
onAudioTracksChange: (tracks) => {
|
|
1572
|
+
events.emit("audiotrackchange", {
|
|
1573
|
+
tracks,
|
|
1574
|
+
selection: engine?.getAudioTrackSelection() ?? 0
|
|
1575
|
+
});
|
|
1576
|
+
},
|
|
1577
|
+
onAudioTrackSelectionChange: (selection) => {
|
|
1578
|
+
events.emit("audiotrackchange", {
|
|
1579
|
+
tracks: engine?.getAudioTracks() ?? [],
|
|
1580
|
+
selection
|
|
1581
|
+
});
|
|
1582
|
+
},
|
|
1405
1583
|
onEnded: () => events.emit("ended")
|
|
1406
1584
|
}
|
|
1407
1585
|
});
|
|
@@ -1560,6 +1738,24 @@ function createVideoPlayer(options) {
|
|
|
1560
1738
|
getAvailableQualities() {
|
|
1561
1739
|
return engine?.getQualities() ?? [];
|
|
1562
1740
|
},
|
|
1741
|
+
getTextTracks() {
|
|
1742
|
+
return engine?.getTextTracks() ?? [];
|
|
1743
|
+
},
|
|
1744
|
+
getSelectedTextTrack() {
|
|
1745
|
+
return engine?.getTextTrackSelection() ?? "off";
|
|
1746
|
+
},
|
|
1747
|
+
setTextTrack(selection) {
|
|
1748
|
+
engine?.setTextTrack(selection);
|
|
1749
|
+
},
|
|
1750
|
+
getAudioTracks() {
|
|
1751
|
+
return engine?.getAudioTracks() ?? [];
|
|
1752
|
+
},
|
|
1753
|
+
getSelectedAudioTrack() {
|
|
1754
|
+
return engine?.getAudioTrackSelection() ?? 0;
|
|
1755
|
+
},
|
|
1756
|
+
setAudioTrack(index) {
|
|
1757
|
+
engine?.setAudioTrack(index);
|
|
1758
|
+
},
|
|
1563
1759
|
async enterFullscreen() {
|
|
1564
1760
|
const target = fullscreenEl ?? videoEl?.parentElement ?? videoEl;
|
|
1565
1761
|
if (target && "requestFullscreen" in target) {
|
|
@@ -1576,6 +1772,9 @@ function createVideoPlayer(options) {
|
|
|
1576
1772
|
isFullscreen() {
|
|
1577
1773
|
return Boolean(document.fullscreenElement);
|
|
1578
1774
|
},
|
|
1775
|
+
setFullscreenElement(el) {
|
|
1776
|
+
fullscreenEl = el;
|
|
1777
|
+
},
|
|
1579
1778
|
async reload() {
|
|
1580
1779
|
refreshManager?.stop();
|
|
1581
1780
|
engine?.destroy();
|
|
@@ -1584,6 +1783,8 @@ function createVideoPlayer(options) {
|
|
|
1584
1783
|
destroy() {
|
|
1585
1784
|
if (destroyed) return;
|
|
1586
1785
|
destroyed = true;
|
|
1786
|
+
mediaCleanup?.();
|
|
1787
|
+
mediaCleanup = null;
|
|
1587
1788
|
refreshManager?.stop();
|
|
1588
1789
|
refreshManager = null;
|
|
1589
1790
|
engine?.destroy();
|