vidply 1.0.7 → 1.0.9
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/dist/vidply.css +113 -44
- package/dist/vidply.esm.js +1457 -44
- package/dist/vidply.esm.js.map +3 -3
- package/dist/vidply.esm.min.js +6 -6
- package/dist/vidply.esm.min.meta.json +7 -7
- package/dist/vidply.js +1457 -44
- package/dist/vidply.js.map +3 -3
- package/dist/vidply.min.css +1 -1
- package/dist/vidply.min.js +6 -6
- package/dist/vidply.min.meta.json +7 -7
- package/package.json +2 -2
- package/src/controls/TranscriptManager.js +328 -27
- package/src/core/Player.js +1621 -16
- package/src/i18n/translations.js +10 -5
- package/src/icons/Icons.js +2 -2
- package/src/styles/vidply.css +113 -44
package/dist/vidply.esm.js
CHANGED
|
@@ -500,7 +500,8 @@ var translations = {
|
|
|
500
500
|
resizeWindow: "Resize Window",
|
|
501
501
|
styleTranscript: "Open transcript style settings",
|
|
502
502
|
closeMenu: "Close Menu",
|
|
503
|
-
styleTitle: "Transcript Style"
|
|
503
|
+
styleTitle: "Transcript Style",
|
|
504
|
+
autoscroll: "Autoscroll"
|
|
504
505
|
},
|
|
505
506
|
settings: {
|
|
506
507
|
title: "Settings",
|
|
@@ -621,7 +622,8 @@ var translations = {
|
|
|
621
622
|
resizeWindow: "Fenster vergr\xF6\xDFern/verkleinern",
|
|
622
623
|
styleTranscript: "Transkript-Stileinstellungen \xF6ffnen",
|
|
623
624
|
closeMenu: "Men\xFC schlie\xDFen",
|
|
624
|
-
styleTitle: "Transkript-Stil"
|
|
625
|
+
styleTitle: "Transkript-Stil",
|
|
626
|
+
autoscroll: "Automatisches Scrollen"
|
|
625
627
|
},
|
|
626
628
|
settings: {
|
|
627
629
|
title: "Einstellungen",
|
|
@@ -742,7 +744,8 @@ var translations = {
|
|
|
742
744
|
resizeWindow: "Cambiar tama\xF1o de ventana",
|
|
743
745
|
styleTranscript: "Abrir configuraci\xF3n de estilo de transcripci\xF3n",
|
|
744
746
|
closeMenu: "Cerrar men\xFA",
|
|
745
|
-
styleTitle: "Estilo de Transcripci\xF3n"
|
|
747
|
+
styleTitle: "Estilo de Transcripci\xF3n",
|
|
748
|
+
autoscroll: "Desplazamiento autom\xE1tico"
|
|
746
749
|
},
|
|
747
750
|
settings: {
|
|
748
751
|
title: "Configuraci\xF3n",
|
|
@@ -863,7 +866,8 @@ var translations = {
|
|
|
863
866
|
resizeWindow: "Redimensionner la fen\xEAtre",
|
|
864
867
|
styleTranscript: "Ouvrir les param\xE8tres de style de transcription",
|
|
865
868
|
closeMenu: "Fermer le menu",
|
|
866
|
-
styleTitle: "Style de Transcription"
|
|
869
|
+
styleTitle: "Style de Transcription",
|
|
870
|
+
autoscroll: "D\xE9filement automatique"
|
|
867
871
|
},
|
|
868
872
|
settings: {
|
|
869
873
|
title: "Param\xE8tres",
|
|
@@ -984,7 +988,8 @@ var translations = {
|
|
|
984
988
|
resizeWindow: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u30B5\u30A4\u30BA\u5909\u66F4",
|
|
985
989
|
styleTranscript: "\u6587\u5B57\u8D77\u3053\u3057\u30B9\u30BF\u30A4\u30EB\u8A2D\u5B9A\u3092\u958B\u304F",
|
|
986
990
|
closeMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
|
|
987
|
-
styleTitle: "\u6587\u5B57\u8D77\u3053\u3057\u30B9\u30BF\u30A4\u30EB"
|
|
991
|
+
styleTitle: "\u6587\u5B57\u8D77\u3053\u3057\u30B9\u30BF\u30A4\u30EB",
|
|
992
|
+
autoscroll: "\u81EA\u52D5\u30B9\u30AF\u30ED\u30FC\u30EB"
|
|
988
993
|
},
|
|
989
994
|
settings: {
|
|
990
995
|
title: "\u8A2D\u5B9A",
|
|
@@ -1163,8 +1168,8 @@ var iconPaths = {
|
|
|
1163
1168
|
language: `<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>`,
|
|
1164
1169
|
hd: `<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-8 12H9.5v-2h-2v2H6V9h1.5v2.5h2V9H11v6zm7-1c0 .55-.45 1-1 1h-.75v1.5h-1.5V15H14c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v4zm-3.5-.5h2v-3h-2v3z"/>`,
|
|
1165
1170
|
transcript: `<path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/>`,
|
|
1166
|
-
audioDescription: `<rect x="2" y="5" width="20" height="14" rx="2" fill="
|
|
1167
|
-
audioDescriptionOn: `<rect x="2" y="5" width="20" height="14" rx="2" fill="
|
|
1171
|
+
audioDescription: `<rect x="2" y="5" width="20" height="14" rx="2" fill="#ffffff" stroke="#ffffff" stroke-width="2"/><text x="12" y="16" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="#1a1a1a">AD</text>`,
|
|
1172
|
+
audioDescriptionOn: `<rect x="2" y="5" width="20" height="14" rx="2" fill="none" stroke="currentColor" stroke-width="2"/><text x="12" y="16" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="currentColor">AD</text>`,
|
|
1168
1173
|
signLanguage: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
|
|
1169
1174
|
signLanguageOn: `<g transform="scale(1.5)"><path d="M16 11.3c-.1-.9-4.8 1.3-5.4 1.1-2.6-1 5.8-1.3 5.1-2.9s-5.1 1.5-6 1.4C6.5 9.4 16.5 9.1 13.5 8c-1.9-.6-8.8 2.9-6.8.4.7-.6.7-1.9-.7-1.7-9.7 7.2-.7 12.2 8.8 7 0-1.3-3.5.4-4.1.4-2.6 0 5.6-2 5.4-3ZM3.9 7.8c3.2-4.2 3.7 1.2 6 .1s.2-.2.2-.3c.7-2.7 2.5-7.5-1.5-1.3-1.6 0 1.1-4 1-4.6C8.9-1 7.3 4.4 7.2 4.9c-1.6.7-.9-1.4-.7-1.5 3-6-.6-3.1-.9.4-2.5 1.8 0-2.8 0-3.5C2.8-.9 4 9.4 1.1 4.9S.1 4.6 0 5c-.4 2.7 2.6 7.2 3.9 2.8Z"/></g>`,
|
|
1170
1175
|
speaker: `<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z"/>`,
|
|
@@ -3321,7 +3326,12 @@ var TranscriptManager = class {
|
|
|
3321
3326
|
this.styleDialog = null;
|
|
3322
3327
|
this.styleDialogVisible = false;
|
|
3323
3328
|
this.styleDialogJustOpened = false;
|
|
3329
|
+
this.languageSelector = null;
|
|
3330
|
+
this.currentTranscriptLanguage = null;
|
|
3331
|
+
this.availableTranscriptLanguages = [];
|
|
3332
|
+
this.languageSelectorHandler = null;
|
|
3324
3333
|
const savedPreferences = this.storage.getTranscriptPreferences();
|
|
3334
|
+
this.autoscrollEnabled = (savedPreferences == null ? void 0 : savedPreferences.autoscroll) !== void 0 ? savedPreferences.autoscroll : true;
|
|
3325
3335
|
this.transcriptStyle = {
|
|
3326
3336
|
fontSize: (savedPreferences == null ? void 0 : savedPreferences.fontSize) || this.player.options.transcriptFontSize || "100%",
|
|
3327
3337
|
fontFamily: (savedPreferences == null ? void 0 : savedPreferences.fontFamily) || this.player.options.transcriptFontFamily || "sans-serif",
|
|
@@ -3344,13 +3354,15 @@ var TranscriptManager = class {
|
|
|
3344
3354
|
documentClick: null,
|
|
3345
3355
|
styleDialogKeydown: null
|
|
3346
3356
|
};
|
|
3357
|
+
this.timeouts = /* @__PURE__ */ new Set();
|
|
3347
3358
|
this.init();
|
|
3348
3359
|
}
|
|
3349
3360
|
init() {
|
|
3361
|
+
this.setupMetadataHandlingOnLoad();
|
|
3350
3362
|
this.player.on("timeupdate", this.handlers.timeupdate);
|
|
3351
3363
|
this.player.on("fullscreenchange", () => {
|
|
3352
3364
|
if (this.isVisible) {
|
|
3353
|
-
|
|
3365
|
+
this.setManagedTimeout(() => this.positionTranscript(), 100);
|
|
3354
3366
|
}
|
|
3355
3367
|
});
|
|
3356
3368
|
}
|
|
@@ -3371,7 +3383,7 @@ var TranscriptManager = class {
|
|
|
3371
3383
|
if (this.transcriptWindow) {
|
|
3372
3384
|
this.transcriptWindow.style.display = "flex";
|
|
3373
3385
|
this.isVisible = true;
|
|
3374
|
-
|
|
3386
|
+
this.setManagedTimeout(() => {
|
|
3375
3387
|
if (this.settingsButton) {
|
|
3376
3388
|
this.settingsButton.focus();
|
|
3377
3389
|
}
|
|
@@ -3382,8 +3394,8 @@ var TranscriptManager = class {
|
|
|
3382
3394
|
this.loadTranscriptData();
|
|
3383
3395
|
if (this.transcriptWindow) {
|
|
3384
3396
|
this.transcriptWindow.style.display = "flex";
|
|
3385
|
-
|
|
3386
|
-
|
|
3397
|
+
this.setManagedTimeout(() => this.positionTranscript(), 0);
|
|
3398
|
+
this.setManagedTimeout(() => {
|
|
3387
3399
|
if (this.settingsButton) {
|
|
3388
3400
|
this.settingsButton.focus();
|
|
3389
3401
|
}
|
|
@@ -3460,8 +3472,41 @@ var TranscriptManager = class {
|
|
|
3460
3472
|
const title = DOMUtils.createElement("h3", {
|
|
3461
3473
|
textContent: i18n.t("transcript.title")
|
|
3462
3474
|
});
|
|
3475
|
+
const autoscrollLabel = DOMUtils.createElement("label", {
|
|
3476
|
+
className: `${this.player.options.classPrefix}-transcript-autoscroll-label`,
|
|
3477
|
+
attributes: {
|
|
3478
|
+
"title": i18n.t("transcript.autoscroll")
|
|
3479
|
+
}
|
|
3480
|
+
});
|
|
3481
|
+
this.autoscrollCheckbox = DOMUtils.createElement("input", {
|
|
3482
|
+
attributes: {
|
|
3483
|
+
"type": "checkbox",
|
|
3484
|
+
"checked": this.autoscrollEnabled,
|
|
3485
|
+
"aria-label": i18n.t("transcript.autoscroll")
|
|
3486
|
+
}
|
|
3487
|
+
});
|
|
3488
|
+
const autoscrollText = DOMUtils.createElement("span", {
|
|
3489
|
+
textContent: i18n.t("transcript.autoscroll"),
|
|
3490
|
+
className: `${this.player.options.classPrefix}-transcript-autoscroll-text`
|
|
3491
|
+
});
|
|
3492
|
+
autoscrollLabel.appendChild(this.autoscrollCheckbox);
|
|
3493
|
+
autoscrollLabel.appendChild(autoscrollText);
|
|
3494
|
+
this.autoscrollCheckbox.addEventListener("change", (e) => {
|
|
3495
|
+
this.autoscrollEnabled = e.target.checked;
|
|
3496
|
+
this.saveAutoscrollPreference();
|
|
3497
|
+
});
|
|
3463
3498
|
this.headerLeft.appendChild(this.settingsButton);
|
|
3464
3499
|
this.headerLeft.appendChild(title);
|
|
3500
|
+
this.headerLeft.appendChild(autoscrollLabel);
|
|
3501
|
+
this.languageSelector = DOMUtils.createElement("select", {
|
|
3502
|
+
className: `${this.player.options.classPrefix}-transcript-language-select`,
|
|
3503
|
+
attributes: {
|
|
3504
|
+
"aria-label": i18n.t("settings.language") || "Language",
|
|
3505
|
+
"style": "display: none;"
|
|
3506
|
+
// Hidden until we detect multiple languages
|
|
3507
|
+
}
|
|
3508
|
+
});
|
|
3509
|
+
this.headerLeft.appendChild(this.languageSelector);
|
|
3465
3510
|
const closeButton = DOMUtils.createElement("button", {
|
|
3466
3511
|
className: `${this.player.options.classPrefix}-transcript-close`,
|
|
3467
3512
|
attributes: {
|
|
@@ -3504,8 +3549,10 @@ var TranscriptManager = class {
|
|
|
3504
3549
|
this.documentClickHandlerAdded = false;
|
|
3505
3550
|
let resizeTimeout;
|
|
3506
3551
|
this.handlers.resize = () => {
|
|
3507
|
-
|
|
3508
|
-
|
|
3552
|
+
if (resizeTimeout) {
|
|
3553
|
+
this.clearManagedTimeout(resizeTimeout);
|
|
3554
|
+
}
|
|
3555
|
+
resizeTimeout = this.setManagedTimeout(() => this.positionTranscript(), 100);
|
|
3509
3556
|
};
|
|
3510
3557
|
window.addEventListener("resize", this.handlers.resize);
|
|
3511
3558
|
}
|
|
@@ -3575,17 +3622,94 @@ var TranscriptManager = class {
|
|
|
3575
3622
|
}
|
|
3576
3623
|
}
|
|
3577
3624
|
}
|
|
3625
|
+
/**
|
|
3626
|
+
* Get available transcript languages from tracks
|
|
3627
|
+
*/
|
|
3628
|
+
getAvailableTranscriptLanguages() {
|
|
3629
|
+
const textTracks = this.player.textTracks;
|
|
3630
|
+
const languages = /* @__PURE__ */ new Map();
|
|
3631
|
+
textTracks.forEach((track) => {
|
|
3632
|
+
if ((track.kind === "captions" || track.kind === "subtitles") && track.language) {
|
|
3633
|
+
if (!languages.has(track.language)) {
|
|
3634
|
+
languages.set(track.language, {
|
|
3635
|
+
language: track.language,
|
|
3636
|
+
label: track.label || track.language,
|
|
3637
|
+
track
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
});
|
|
3642
|
+
return Array.from(languages.values());
|
|
3643
|
+
}
|
|
3644
|
+
/**
|
|
3645
|
+
* Update language selector dropdown
|
|
3646
|
+
*/
|
|
3647
|
+
updateLanguageSelector() {
|
|
3648
|
+
if (!this.languageSelector) return;
|
|
3649
|
+
this.availableTranscriptLanguages = this.getAvailableTranscriptLanguages();
|
|
3650
|
+
this.languageSelector.innerHTML = "";
|
|
3651
|
+
if (this.availableTranscriptLanguages.length < 2) {
|
|
3652
|
+
this.languageSelector.style.display = "none";
|
|
3653
|
+
return;
|
|
3654
|
+
}
|
|
3655
|
+
this.languageSelector.style.display = "block";
|
|
3656
|
+
this.availableTranscriptLanguages.forEach((langInfo, index) => {
|
|
3657
|
+
const option = DOMUtils.createElement("option", {
|
|
3658
|
+
textContent: langInfo.label,
|
|
3659
|
+
attributes: {
|
|
3660
|
+
"value": langInfo.language
|
|
3661
|
+
}
|
|
3662
|
+
});
|
|
3663
|
+
this.languageSelector.appendChild(option);
|
|
3664
|
+
});
|
|
3665
|
+
if (this.currentTranscriptLanguage) {
|
|
3666
|
+
this.languageSelector.value = this.currentTranscriptLanguage;
|
|
3667
|
+
} else if (this.availableTranscriptLanguages.length > 0) {
|
|
3668
|
+
const activeTrack = this.player.textTracks.find(
|
|
3669
|
+
(track) => (track.kind === "captions" || track.kind === "subtitles") && track.mode === "showing"
|
|
3670
|
+
);
|
|
3671
|
+
this.currentTranscriptLanguage = activeTrack ? activeTrack.language : this.availableTranscriptLanguages[0].language;
|
|
3672
|
+
this.languageSelector.value = this.currentTranscriptLanguage;
|
|
3673
|
+
}
|
|
3674
|
+
if (this.languageSelectorHandler) {
|
|
3675
|
+
this.languageSelector.removeEventListener("change", this.languageSelectorHandler);
|
|
3676
|
+
}
|
|
3677
|
+
this.languageSelectorHandler = (e) => {
|
|
3678
|
+
this.currentTranscriptLanguage = e.target.value;
|
|
3679
|
+
this.loadTranscriptData();
|
|
3680
|
+
};
|
|
3681
|
+
this.languageSelector.addEventListener("change", this.languageSelectorHandler);
|
|
3682
|
+
}
|
|
3578
3683
|
/**
|
|
3579
3684
|
* Load transcript data from caption/subtitle tracks
|
|
3580
3685
|
*/
|
|
3581
3686
|
loadTranscriptData() {
|
|
3582
3687
|
this.transcriptEntries = [];
|
|
3583
3688
|
this.transcriptContent.innerHTML = "";
|
|
3584
|
-
const textTracks =
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3689
|
+
const textTracks = this.player.textTracks;
|
|
3690
|
+
let captionTrack = null;
|
|
3691
|
+
if (this.currentTranscriptLanguage) {
|
|
3692
|
+
captionTrack = textTracks.find(
|
|
3693
|
+
(track) => (track.kind === "captions" || track.kind === "subtitles") && track.language === this.currentTranscriptLanguage
|
|
3694
|
+
);
|
|
3695
|
+
}
|
|
3696
|
+
if (!captionTrack) {
|
|
3697
|
+
captionTrack = textTracks.find(
|
|
3698
|
+
(track) => track.kind === "captions" || track.kind === "subtitles"
|
|
3699
|
+
);
|
|
3700
|
+
if (captionTrack) {
|
|
3701
|
+
this.currentTranscriptLanguage = captionTrack.language;
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
let descriptionTrack = null;
|
|
3705
|
+
if (this.currentTranscriptLanguage) {
|
|
3706
|
+
descriptionTrack = textTracks.find(
|
|
3707
|
+
(track) => track.kind === "descriptions" && track.language === this.currentTranscriptLanguage
|
|
3708
|
+
);
|
|
3709
|
+
}
|
|
3710
|
+
if (!descriptionTrack) {
|
|
3711
|
+
descriptionTrack = textTracks.find((track) => track.kind === "descriptions");
|
|
3712
|
+
}
|
|
3589
3713
|
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
3590
3714
|
if (!captionTrack && !descriptionTrack && !metadataTrack) {
|
|
3591
3715
|
this.showNoTranscriptMessage();
|
|
@@ -3614,7 +3738,7 @@ var TranscriptManager = class {
|
|
|
3614
3738
|
tracksToLoad.forEach((track) => {
|
|
3615
3739
|
track.addEventListener("load", onLoad, { once: true });
|
|
3616
3740
|
});
|
|
3617
|
-
|
|
3741
|
+
this.setManagedTimeout(() => {
|
|
3618
3742
|
this.loadTranscriptData();
|
|
3619
3743
|
}, 500);
|
|
3620
3744
|
return;
|
|
@@ -3647,24 +3771,61 @@ var TranscriptManager = class {
|
|
|
3647
3771
|
this.transcriptContent.appendChild(entry);
|
|
3648
3772
|
});
|
|
3649
3773
|
this.applyTranscriptStyles();
|
|
3774
|
+
this.updateLanguageSelector();
|
|
3775
|
+
}
|
|
3776
|
+
/**
|
|
3777
|
+
* Setup metadata handling on player load
|
|
3778
|
+
* This runs independently of transcript loading
|
|
3779
|
+
*/
|
|
3780
|
+
setupMetadataHandlingOnLoad() {
|
|
3781
|
+
const setupMetadata = () => {
|
|
3782
|
+
const textTracks = this.player.textTracks;
|
|
3783
|
+
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
3784
|
+
if (metadataTrack) {
|
|
3785
|
+
if (metadataTrack.mode === "disabled") {
|
|
3786
|
+
metadataTrack.mode = "hidden";
|
|
3787
|
+
}
|
|
3788
|
+
if (this.metadataCueChangeHandler) {
|
|
3789
|
+
metadataTrack.removeEventListener("cuechange", this.metadataCueChangeHandler);
|
|
3790
|
+
}
|
|
3791
|
+
this.metadataCueChangeHandler = () => {
|
|
3792
|
+
const activeCues = Array.from(metadataTrack.activeCues || []);
|
|
3793
|
+
if (activeCues.length > 0) {
|
|
3794
|
+
if (this.player.options.debug) {
|
|
3795
|
+
console.log("[VidPly Metadata] Active cues:", activeCues.map((c) => ({
|
|
3796
|
+
start: c.startTime,
|
|
3797
|
+
end: c.endTime,
|
|
3798
|
+
text: c.text
|
|
3799
|
+
})));
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
activeCues.forEach((cue) => {
|
|
3803
|
+
this.handleMetadataCue(cue);
|
|
3804
|
+
});
|
|
3805
|
+
};
|
|
3806
|
+
metadataTrack.addEventListener("cuechange", this.metadataCueChangeHandler);
|
|
3807
|
+
if (this.player.options.debug) {
|
|
3808
|
+
const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;
|
|
3809
|
+
console.log("[VidPly Metadata] Track enabled,", cueCount, "cues available");
|
|
3810
|
+
}
|
|
3811
|
+
} else if (this.player.options.debug) {
|
|
3812
|
+
console.warn("[VidPly Metadata] No metadata track found");
|
|
3813
|
+
}
|
|
3814
|
+
};
|
|
3815
|
+
setupMetadata();
|
|
3816
|
+
this.player.on("loadedmetadata", setupMetadata);
|
|
3650
3817
|
}
|
|
3651
3818
|
/**
|
|
3652
3819
|
* Setup metadata handling
|
|
3653
3820
|
* Metadata cues are not displayed but can be used programmatically
|
|
3821
|
+
* This is called when transcript data is loaded (for storing cues)
|
|
3654
3822
|
*/
|
|
3655
3823
|
setupMetadataHandling() {
|
|
3656
3824
|
if (!this.metadataCues || this.metadataCues.length === 0) {
|
|
3657
3825
|
return;
|
|
3658
3826
|
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
if (metadataTrack) {
|
|
3662
|
-
metadataTrack.addEventListener("cuechange", () => {
|
|
3663
|
-
const activeCues = Array.from(metadataTrack.activeCues || []);
|
|
3664
|
-
activeCues.forEach((cue) => {
|
|
3665
|
-
this.handleMetadataCue(cue);
|
|
3666
|
-
});
|
|
3667
|
-
});
|
|
3827
|
+
if (this.player.options.debug) {
|
|
3828
|
+
console.log("[VidPly Metadata]", this.metadataCues.length, "cues stored from transcript load");
|
|
3668
3829
|
}
|
|
3669
3830
|
}
|
|
3670
3831
|
/**
|
|
@@ -3673,6 +3834,12 @@ var TranscriptManager = class {
|
|
|
3673
3834
|
*/
|
|
3674
3835
|
handleMetadataCue(cue) {
|
|
3675
3836
|
const text = cue.text.trim();
|
|
3837
|
+
if (this.player.options.debug) {
|
|
3838
|
+
console.log("[VidPly Metadata] Processing cue:", {
|
|
3839
|
+
time: cue.startTime,
|
|
3840
|
+
text
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
3676
3843
|
this.player.emit("metadata", {
|
|
3677
3844
|
time: cue.startTime,
|
|
3678
3845
|
endTime: cue.endTime,
|
|
@@ -3680,18 +3847,40 @@ var TranscriptManager = class {
|
|
|
3680
3847
|
cue
|
|
3681
3848
|
});
|
|
3682
3849
|
if (text.includes("PAUSE")) {
|
|
3850
|
+
if (!this.player.state.paused) {
|
|
3851
|
+
if (this.player.options.debug) {
|
|
3852
|
+
console.log("[VidPly Metadata] Pausing video at", cue.startTime);
|
|
3853
|
+
}
|
|
3854
|
+
this.player.pause();
|
|
3855
|
+
}
|
|
3683
3856
|
this.player.emit("metadata:pause", { time: cue.startTime, text });
|
|
3684
3857
|
}
|
|
3685
3858
|
const focusMatch = text.match(/FOCUS:([\w#-]+)/);
|
|
3686
3859
|
if (focusMatch) {
|
|
3860
|
+
const targetSelector = focusMatch[1];
|
|
3861
|
+
const targetElement = document.querySelector(targetSelector);
|
|
3862
|
+
if (targetElement) {
|
|
3863
|
+
if (this.player.options.debug) {
|
|
3864
|
+
console.log("[VidPly Metadata] Focusing element:", targetSelector);
|
|
3865
|
+
}
|
|
3866
|
+
this.setManagedTimeout(() => {
|
|
3867
|
+
targetElement.focus();
|
|
3868
|
+
}, 10);
|
|
3869
|
+
} else if (this.player.options.debug) {
|
|
3870
|
+
console.warn("[VidPly Metadata] Element not found:", targetSelector);
|
|
3871
|
+
}
|
|
3687
3872
|
this.player.emit("metadata:focus", {
|
|
3688
3873
|
time: cue.startTime,
|
|
3689
|
-
target:
|
|
3874
|
+
target: targetSelector,
|
|
3875
|
+
element: targetElement,
|
|
3690
3876
|
text
|
|
3691
3877
|
});
|
|
3692
3878
|
}
|
|
3693
3879
|
const hashtags = text.match(/#[\w-]+/g);
|
|
3694
3880
|
if (hashtags) {
|
|
3881
|
+
if (this.player.options.debug) {
|
|
3882
|
+
console.log("[VidPly Metadata] Hashtags found:", hashtags);
|
|
3883
|
+
}
|
|
3695
3884
|
this.player.emit("metadata:hashtags", {
|
|
3696
3885
|
time: cue.startTime,
|
|
3697
3886
|
hashtags,
|
|
@@ -3785,7 +3974,7 @@ var TranscriptManager = class {
|
|
|
3785
3974
|
* Scroll transcript window to show active entry
|
|
3786
3975
|
*/
|
|
3787
3976
|
scrollToEntry(entryElement) {
|
|
3788
|
-
if (!this.transcriptContent) return;
|
|
3977
|
+
if (!this.transcriptContent || !this.autoscrollEnabled) return;
|
|
3789
3978
|
const contentRect = this.transcriptContent.getBoundingClientRect();
|
|
3790
3979
|
const entryRect = entryElement.getBoundingClientRect();
|
|
3791
3980
|
if (entryRect.top < contentRect.top || entryRect.bottom > contentRect.bottom) {
|
|
@@ -3796,6 +3985,14 @@ var TranscriptManager = class {
|
|
|
3796
3985
|
});
|
|
3797
3986
|
}
|
|
3798
3987
|
}
|
|
3988
|
+
/**
|
|
3989
|
+
* Save autoscroll preference to localStorage
|
|
3990
|
+
*/
|
|
3991
|
+
saveAutoscrollPreference() {
|
|
3992
|
+
const savedPreferences = this.storage.getTranscriptPreferences() || {};
|
|
3993
|
+
savedPreferences.autoscroll = this.autoscrollEnabled;
|
|
3994
|
+
this.storage.saveTranscriptPreferences(savedPreferences);
|
|
3995
|
+
}
|
|
3799
3996
|
/**
|
|
3800
3997
|
* Setup drag and drop functionality
|
|
3801
3998
|
*/
|
|
@@ -3808,6 +4005,9 @@ var TranscriptManager = class {
|
|
|
3808
4005
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings`)) {
|
|
3809
4006
|
return;
|
|
3810
4007
|
}
|
|
4008
|
+
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-language-select`)) {
|
|
4009
|
+
return;
|
|
4010
|
+
}
|
|
3811
4011
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings-menu`)) {
|
|
3812
4012
|
return;
|
|
3813
4013
|
}
|
|
@@ -3834,6 +4034,9 @@ var TranscriptManager = class {
|
|
|
3834
4034
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings`)) {
|
|
3835
4035
|
return;
|
|
3836
4036
|
}
|
|
4037
|
+
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-language-select`)) {
|
|
4038
|
+
return;
|
|
4039
|
+
}
|
|
3837
4040
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings-menu`)) {
|
|
3838
4041
|
return;
|
|
3839
4042
|
}
|
|
@@ -4588,6 +4791,30 @@ var TranscriptManager = class {
|
|
|
4588
4791
|
entry.style.fontFamily = this.transcriptStyle.fontFamily;
|
|
4589
4792
|
});
|
|
4590
4793
|
}
|
|
4794
|
+
/**
|
|
4795
|
+
* Set a managed timeout that will be cleaned up on destroy
|
|
4796
|
+
* @param {Function} callback - Callback function
|
|
4797
|
+
* @param {number} delay - Delay in milliseconds
|
|
4798
|
+
* @returns {number} Timeout ID
|
|
4799
|
+
*/
|
|
4800
|
+
setManagedTimeout(callback, delay) {
|
|
4801
|
+
const timeoutId = setTimeout(() => {
|
|
4802
|
+
this.timeouts.delete(timeoutId);
|
|
4803
|
+
callback();
|
|
4804
|
+
}, delay);
|
|
4805
|
+
this.timeouts.add(timeoutId);
|
|
4806
|
+
return timeoutId;
|
|
4807
|
+
}
|
|
4808
|
+
/**
|
|
4809
|
+
* Clear a managed timeout
|
|
4810
|
+
* @param {number} timeoutId - Timeout ID to clear
|
|
4811
|
+
*/
|
|
4812
|
+
clearManagedTimeout(timeoutId) {
|
|
4813
|
+
if (timeoutId) {
|
|
4814
|
+
clearTimeout(timeoutId);
|
|
4815
|
+
this.timeouts.delete(timeoutId);
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4591
4818
|
/**
|
|
4592
4819
|
* Cleanup
|
|
4593
4820
|
*/
|
|
@@ -4641,6 +4868,8 @@ var TranscriptManager = class {
|
|
|
4641
4868
|
if (this.handlers.resize) {
|
|
4642
4869
|
window.removeEventListener("resize", this.handlers.resize);
|
|
4643
4870
|
}
|
|
4871
|
+
this.timeouts.forEach((timeoutId) => clearTimeout(timeoutId));
|
|
4872
|
+
this.timeouts.clear();
|
|
4644
4873
|
this.handlers = null;
|
|
4645
4874
|
if (this.transcriptWindow && this.transcriptWindow.parentNode) {
|
|
4646
4875
|
this.transcriptWindow.parentNode.removeChild(this.transcriptWindow);
|
|
@@ -5312,7 +5541,7 @@ var HLSRenderer = class {
|
|
|
5312
5541
|
};
|
|
5313
5542
|
|
|
5314
5543
|
// src/core/Player.js
|
|
5315
|
-
var Player = class extends EventEmitter {
|
|
5544
|
+
var Player = class _Player extends EventEmitter {
|
|
5316
5545
|
constructor(element, options = {}) {
|
|
5317
5546
|
super();
|
|
5318
5547
|
this.element = typeof element === "string" ? document.querySelector(element) : element;
|
|
@@ -5420,6 +5649,8 @@ var Player = class extends EventEmitter {
|
|
|
5420
5649
|
screenReaderAnnouncements: true,
|
|
5421
5650
|
highContrast: false,
|
|
5422
5651
|
focusHighlight: true,
|
|
5652
|
+
metadataAlerts: {},
|
|
5653
|
+
metadataHashtags: {},
|
|
5423
5654
|
// Languages
|
|
5424
5655
|
language: "en",
|
|
5425
5656
|
languages: ["en"],
|
|
@@ -5438,6 +5669,8 @@ var Player = class extends EventEmitter {
|
|
|
5438
5669
|
onError: null,
|
|
5439
5670
|
...options
|
|
5440
5671
|
};
|
|
5672
|
+
this.options.metadataAlerts = this.options.metadataAlerts || {};
|
|
5673
|
+
this.options.metadataHashtags = this.options.metadataHashtags || {};
|
|
5441
5674
|
this.storage = new StorageManager("vidply");
|
|
5442
5675
|
const savedPrefs = this.storage.getPlayerPreferences();
|
|
5443
5676
|
if (savedPrefs) {
|
|
@@ -5469,12 +5702,24 @@ var Player = class extends EventEmitter {
|
|
|
5469
5702
|
this.audioDescriptionSrc = this.options.audioDescriptionSrc;
|
|
5470
5703
|
this.signLanguageSrc = this.options.signLanguageSrc;
|
|
5471
5704
|
this.signLanguageVideo = null;
|
|
5705
|
+
this.audioDescriptionSourceElement = null;
|
|
5706
|
+
this.originalAudioDescriptionSource = null;
|
|
5707
|
+
this.audioDescriptionCaptionTracks = [];
|
|
5708
|
+
this._textTracksCache = null;
|
|
5709
|
+
this._textTracksDirty = true;
|
|
5710
|
+
this._sourceElementsCache = null;
|
|
5711
|
+
this._sourceElementsDirty = true;
|
|
5712
|
+
this._trackElementsCache = null;
|
|
5713
|
+
this._trackElementsDirty = true;
|
|
5714
|
+
this.timeouts = /* @__PURE__ */ new Set();
|
|
5472
5715
|
this.container = null;
|
|
5473
5716
|
this.renderer = null;
|
|
5474
5717
|
this.controlBar = null;
|
|
5475
5718
|
this.captionManager = null;
|
|
5476
5719
|
this.keyboardManager = null;
|
|
5477
5720
|
this.settingsDialog = null;
|
|
5721
|
+
this.metadataCueChangeHandler = null;
|
|
5722
|
+
this.metadataAlertHandlers = /* @__PURE__ */ new Map();
|
|
5478
5723
|
this.init();
|
|
5479
5724
|
}
|
|
5480
5725
|
async init() {
|
|
@@ -5506,6 +5751,7 @@ var Player = class extends EventEmitter {
|
|
|
5506
5751
|
if (this.options.transcript || this.options.transcriptButton) {
|
|
5507
5752
|
this.transcriptManager = new TranscriptManager(this);
|
|
5508
5753
|
}
|
|
5754
|
+
this.setupMetadataHandling();
|
|
5509
5755
|
if (this.options.keyboard) {
|
|
5510
5756
|
this.keyboardManager = new KeyboardManager(this);
|
|
5511
5757
|
}
|
|
@@ -5591,6 +5837,8 @@ var Player = class extends EventEmitter {
|
|
|
5591
5837
|
if (this.element.tagName === "VIDEO") {
|
|
5592
5838
|
this.createPlayButtonOverlay();
|
|
5593
5839
|
}
|
|
5840
|
+
this.element.vidply = this;
|
|
5841
|
+
_Player.instances.push(this);
|
|
5594
5842
|
this.element.style.cursor = "pointer";
|
|
5595
5843
|
this.element.addEventListener("click", (e) => {
|
|
5596
5844
|
if (e.target === this.element) {
|
|
@@ -5623,6 +5871,53 @@ var Player = class extends EventEmitter {
|
|
|
5623
5871
|
if (!src) {
|
|
5624
5872
|
throw new Error("No media source found");
|
|
5625
5873
|
}
|
|
5874
|
+
const sourceElements = this.sourceElements;
|
|
5875
|
+
for (const sourceEl of sourceElements) {
|
|
5876
|
+
const descSrc = sourceEl.getAttribute("data-desc-src");
|
|
5877
|
+
const origSrc = sourceEl.getAttribute("data-orig-src");
|
|
5878
|
+
if (descSrc || origSrc) {
|
|
5879
|
+
if (!this.audioDescriptionSourceElement) {
|
|
5880
|
+
this.audioDescriptionSourceElement = sourceEl;
|
|
5881
|
+
}
|
|
5882
|
+
if (origSrc) {
|
|
5883
|
+
if (!this.originalAudioDescriptionSource) {
|
|
5884
|
+
this.originalAudioDescriptionSource = origSrc;
|
|
5885
|
+
}
|
|
5886
|
+
if (!this.originalSrc) {
|
|
5887
|
+
this.originalSrc = origSrc;
|
|
5888
|
+
}
|
|
5889
|
+
} else {
|
|
5890
|
+
const currentSrcAttr = sourceEl.getAttribute("src");
|
|
5891
|
+
if (!this.originalAudioDescriptionSource && currentSrcAttr) {
|
|
5892
|
+
this.originalAudioDescriptionSource = currentSrcAttr;
|
|
5893
|
+
}
|
|
5894
|
+
if (!this.originalSrc && currentSrcAttr) {
|
|
5895
|
+
this.originalSrc = currentSrcAttr;
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
if (descSrc && !this.audioDescriptionSrc) {
|
|
5899
|
+
this.audioDescriptionSrc = descSrc;
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5903
|
+
const trackElements = this.trackElements;
|
|
5904
|
+
trackElements.forEach((trackEl) => {
|
|
5905
|
+
const trackKind = trackEl.getAttribute("kind");
|
|
5906
|
+
const trackDescSrc = trackEl.getAttribute("data-desc-src");
|
|
5907
|
+
if (trackKind === "captions" || trackKind === "subtitles" || trackKind === "chapters") {
|
|
5908
|
+
if (trackDescSrc) {
|
|
5909
|
+
this.audioDescriptionCaptionTracks.push({
|
|
5910
|
+
trackElement: trackEl,
|
|
5911
|
+
originalSrc: trackEl.getAttribute("src"),
|
|
5912
|
+
describedSrc: trackDescSrc,
|
|
5913
|
+
originalTrackSrc: trackEl.getAttribute("data-orig-src") || trackEl.getAttribute("src"),
|
|
5914
|
+
explicit: true
|
|
5915
|
+
// Explicitly defined, so we should validate it
|
|
5916
|
+
});
|
|
5917
|
+
this.log(`Found explicit described ${trackKind} track: ${trackEl.getAttribute("src")} -> ${trackDescSrc}`);
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
});
|
|
5626
5921
|
if (!this.originalSrc) {
|
|
5627
5922
|
this.originalSrc = src;
|
|
5628
5923
|
}
|
|
@@ -5639,6 +5934,106 @@ var Player = class extends EventEmitter {
|
|
|
5639
5934
|
this.log(`Using ${renderer.name} renderer`);
|
|
5640
5935
|
this.renderer = new renderer(this);
|
|
5641
5936
|
await this.renderer.init();
|
|
5937
|
+
this.invalidateTrackCache();
|
|
5938
|
+
}
|
|
5939
|
+
/**
|
|
5940
|
+
* Get cached text tracks array
|
|
5941
|
+
* @returns {Array} Array of text tracks
|
|
5942
|
+
*/
|
|
5943
|
+
get textTracks() {
|
|
5944
|
+
if (!this._textTracksCache || this._textTracksDirty) {
|
|
5945
|
+
this._textTracksCache = Array.from(this.element.textTracks || []);
|
|
5946
|
+
this._textTracksDirty = false;
|
|
5947
|
+
}
|
|
5948
|
+
return this._textTracksCache;
|
|
5949
|
+
}
|
|
5950
|
+
/**
|
|
5951
|
+
* Get cached source elements array
|
|
5952
|
+
* @returns {Array} Array of source elements
|
|
5953
|
+
*/
|
|
5954
|
+
get sourceElements() {
|
|
5955
|
+
if (!this._sourceElementsCache || this._sourceElementsDirty) {
|
|
5956
|
+
this._sourceElementsCache = Array.from(this.element.querySelectorAll("source"));
|
|
5957
|
+
this._sourceElementsDirty = false;
|
|
5958
|
+
}
|
|
5959
|
+
return this._sourceElementsCache;
|
|
5960
|
+
}
|
|
5961
|
+
/**
|
|
5962
|
+
* Get cached track elements array
|
|
5963
|
+
* @returns {Array} Array of track elements
|
|
5964
|
+
*/
|
|
5965
|
+
get trackElements() {
|
|
5966
|
+
if (!this._trackElementsCache || this._trackElementsDirty) {
|
|
5967
|
+
this._trackElementsCache = Array.from(this.element.querySelectorAll("track"));
|
|
5968
|
+
this._trackElementsDirty = false;
|
|
5969
|
+
}
|
|
5970
|
+
return this._trackElementsCache;
|
|
5971
|
+
}
|
|
5972
|
+
/**
|
|
5973
|
+
* Invalidate DOM query cache (call when tracks/sources change)
|
|
5974
|
+
*/
|
|
5975
|
+
invalidateTrackCache() {
|
|
5976
|
+
this._textTracksDirty = true;
|
|
5977
|
+
this._trackElementsDirty = true;
|
|
5978
|
+
this._sourceElementsDirty = true;
|
|
5979
|
+
}
|
|
5980
|
+
/**
|
|
5981
|
+
* Find a text track by kind and optionally language
|
|
5982
|
+
* @param {string} kind - Track kind (captions, subtitles, descriptions, chapters, metadata)
|
|
5983
|
+
* @param {string} [language] - Optional language code
|
|
5984
|
+
* @returns {TextTrack|null} Found track or null
|
|
5985
|
+
*/
|
|
5986
|
+
findTextTrack(kind, language = null) {
|
|
5987
|
+
const tracks = this.textTracks;
|
|
5988
|
+
if (language) {
|
|
5989
|
+
return tracks.find((t) => t.kind === kind && t.language === language);
|
|
5990
|
+
}
|
|
5991
|
+
return tracks.find((t) => t.kind === kind);
|
|
5992
|
+
}
|
|
5993
|
+
/**
|
|
5994
|
+
* Find a source element by attribute
|
|
5995
|
+
* @param {string} attribute - Attribute name (e.g., 'data-desc-src')
|
|
5996
|
+
* @param {string} [value] - Optional attribute value
|
|
5997
|
+
* @returns {Element|null} Found source element or null
|
|
5998
|
+
*/
|
|
5999
|
+
findSourceElement(attribute, value = null) {
|
|
6000
|
+
const sources = this.sourceElements;
|
|
6001
|
+
if (value) {
|
|
6002
|
+
return sources.find((el) => el.getAttribute(attribute) === value);
|
|
6003
|
+
}
|
|
6004
|
+
return sources.find((el) => el.hasAttribute(attribute));
|
|
6005
|
+
}
|
|
6006
|
+
/**
|
|
6007
|
+
* Find a track element by its associated TextTrack
|
|
6008
|
+
* @param {TextTrack} track - The TextTrack object
|
|
6009
|
+
* @returns {Element|null} Found track element or null
|
|
6010
|
+
*/
|
|
6011
|
+
findTrackElement(track) {
|
|
6012
|
+
return this.trackElements.find((el) => el.track === track);
|
|
6013
|
+
}
|
|
6014
|
+
/**
|
|
6015
|
+
* Set a managed timeout that will be cleaned up on destroy
|
|
6016
|
+
* @param {Function} callback - Callback function
|
|
6017
|
+
* @param {number} delay - Delay in milliseconds
|
|
6018
|
+
* @returns {number} Timeout ID
|
|
6019
|
+
*/
|
|
6020
|
+
setManagedTimeout(callback, delay) {
|
|
6021
|
+
const timeoutId = setTimeout(() => {
|
|
6022
|
+
this.timeouts.delete(timeoutId);
|
|
6023
|
+
callback();
|
|
6024
|
+
}, delay);
|
|
6025
|
+
this.timeouts.add(timeoutId);
|
|
6026
|
+
return timeoutId;
|
|
6027
|
+
}
|
|
6028
|
+
/**
|
|
6029
|
+
* Clear a managed timeout
|
|
6030
|
+
* @param {number} timeoutId - Timeout ID to clear
|
|
6031
|
+
*/
|
|
6032
|
+
clearManagedTimeout(timeoutId) {
|
|
6033
|
+
if (timeoutId) {
|
|
6034
|
+
clearTimeout(timeoutId);
|
|
6035
|
+
this.timeouts.delete(timeoutId);
|
|
6036
|
+
}
|
|
5642
6037
|
}
|
|
5643
6038
|
/**
|
|
5644
6039
|
* Load new media source (for playlists)
|
|
@@ -5654,8 +6049,9 @@ var Player = class extends EventEmitter {
|
|
|
5654
6049
|
if (this.renderer) {
|
|
5655
6050
|
this.pause();
|
|
5656
6051
|
}
|
|
5657
|
-
const existingTracks = this.
|
|
6052
|
+
const existingTracks = this.trackElements;
|
|
5658
6053
|
existingTracks.forEach((track) => track.remove());
|
|
6054
|
+
this.invalidateTrackCache();
|
|
5659
6055
|
this.element.src = config.src;
|
|
5660
6056
|
if (config.type) {
|
|
5661
6057
|
this.element.type = config.type;
|
|
@@ -5675,6 +6071,7 @@ var Player = class extends EventEmitter {
|
|
|
5675
6071
|
}
|
|
5676
6072
|
this.element.appendChild(track);
|
|
5677
6073
|
});
|
|
6074
|
+
this.invalidateTrackCache();
|
|
5678
6075
|
}
|
|
5679
6076
|
const shouldChangeRenderer = this.shouldChangeRenderer(config.src);
|
|
5680
6077
|
if (shouldChangeRenderer && this.renderer) {
|
|
@@ -5907,15 +6304,398 @@ var Player = class extends EventEmitter {
|
|
|
5907
6304
|
this.enableCaptions();
|
|
5908
6305
|
}
|
|
5909
6306
|
}
|
|
6307
|
+
/**
|
|
6308
|
+
* Check if a track file exists
|
|
6309
|
+
* @param {string} url - Track file URL
|
|
6310
|
+
* @returns {Promise<boolean>} - True if file exists
|
|
6311
|
+
*/
|
|
6312
|
+
async validateTrackExists(url) {
|
|
6313
|
+
try {
|
|
6314
|
+
const response = await fetch(url, { method: "HEAD", cache: "no-cache" });
|
|
6315
|
+
return response.ok;
|
|
6316
|
+
} catch (error) {
|
|
6317
|
+
return false;
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
5910
6320
|
// Audio Description
|
|
5911
6321
|
async enableAudioDescription() {
|
|
5912
|
-
|
|
5913
|
-
|
|
6322
|
+
const hasSourceElementsWithDesc = this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6323
|
+
const hasTracksWithDesc = this.audioDescriptionCaptionTracks.length > 0;
|
|
6324
|
+
if (!this.audioDescriptionSrc && !hasSourceElementsWithDesc && !hasTracksWithDesc) {
|
|
6325
|
+
console.warn("VidPly: No audio description source, source elements, or tracks provided");
|
|
5914
6326
|
return;
|
|
5915
6327
|
}
|
|
5916
6328
|
const currentTime = this.state.currentTime;
|
|
5917
6329
|
const wasPlaying = this.state.playing;
|
|
5918
|
-
|
|
6330
|
+
let swappedTracksForTranscript = [];
|
|
6331
|
+
if (this.audioDescriptionSourceElement) {
|
|
6332
|
+
const currentSrc = this.element.currentSrc || this.element.src;
|
|
6333
|
+
const sourceElements = this.sourceElements;
|
|
6334
|
+
let sourceElementToUpdate = null;
|
|
6335
|
+
let descSrc = this.audioDescriptionSrc;
|
|
6336
|
+
for (const sourceEl of sourceElements) {
|
|
6337
|
+
const sourceSrc = sourceEl.getAttribute("src");
|
|
6338
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6339
|
+
const sourceFilename = sourceSrc ? sourceSrc.split("/").pop() : "";
|
|
6340
|
+
const currentFilename = currentSrc ? currentSrc.split("/").pop() : "";
|
|
6341
|
+
if (currentSrc && (currentSrc === sourceSrc || currentSrc.includes(sourceSrc) || currentSrc.includes(sourceFilename) || sourceFilename && currentFilename === sourceFilename)) {
|
|
6342
|
+
sourceElementToUpdate = sourceEl;
|
|
6343
|
+
if (descSrcAttr) {
|
|
6344
|
+
descSrc = descSrcAttr;
|
|
6345
|
+
} else if (sourceSrc) {
|
|
6346
|
+
descSrc = this.audioDescriptionSrc || descSrc;
|
|
6347
|
+
}
|
|
6348
|
+
break;
|
|
6349
|
+
}
|
|
6350
|
+
}
|
|
6351
|
+
if (!sourceElementToUpdate) {
|
|
6352
|
+
sourceElementToUpdate = this.audioDescriptionSourceElement;
|
|
6353
|
+
const storedDescSrc = sourceElementToUpdate.getAttribute("data-desc-src");
|
|
6354
|
+
if (storedDescSrc) {
|
|
6355
|
+
descSrc = storedDescSrc;
|
|
6356
|
+
}
|
|
6357
|
+
}
|
|
6358
|
+
if (this.audioDescriptionCaptionTracks.length > 0) {
|
|
6359
|
+
const validationPromises = this.audioDescriptionCaptionTracks.map(async (trackInfo) => {
|
|
6360
|
+
if (trackInfo.trackElement && trackInfo.describedSrc) {
|
|
6361
|
+
if (trackInfo.explicit === true) {
|
|
6362
|
+
try {
|
|
6363
|
+
const exists = await this.validateTrackExists(trackInfo.describedSrc);
|
|
6364
|
+
return { trackInfo, exists };
|
|
6365
|
+
} catch (error) {
|
|
6366
|
+
return { trackInfo, exists: false };
|
|
6367
|
+
}
|
|
6368
|
+
} else {
|
|
6369
|
+
return { trackInfo, exists: false };
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
return { trackInfo, exists: false };
|
|
6373
|
+
});
|
|
6374
|
+
const validationResults = await Promise.all(validationPromises);
|
|
6375
|
+
const tracksToSwap = validationResults.filter((result) => result.exists);
|
|
6376
|
+
if (tracksToSwap.length > 0) {
|
|
6377
|
+
const trackModes = /* @__PURE__ */ new Map();
|
|
6378
|
+
tracksToSwap.forEach(({ trackInfo }) => {
|
|
6379
|
+
const textTrack = trackInfo.trackElement.track;
|
|
6380
|
+
if (textTrack) {
|
|
6381
|
+
trackModes.set(trackInfo, {
|
|
6382
|
+
wasShowing: textTrack.mode === "showing",
|
|
6383
|
+
wasHidden: textTrack.mode === "hidden"
|
|
6384
|
+
});
|
|
6385
|
+
} else {
|
|
6386
|
+
trackModes.set(trackInfo, {
|
|
6387
|
+
wasShowing: false,
|
|
6388
|
+
wasHidden: false
|
|
6389
|
+
});
|
|
6390
|
+
}
|
|
6391
|
+
});
|
|
6392
|
+
const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {
|
|
6393
|
+
const oldSrc = trackInfo.trackElement.getAttribute("src");
|
|
6394
|
+
const parent = trackInfo.trackElement.parentNode;
|
|
6395
|
+
const nextSibling = trackInfo.trackElement.nextSibling;
|
|
6396
|
+
const attributes = {};
|
|
6397
|
+
Array.from(trackInfo.trackElement.attributes).forEach((attr) => {
|
|
6398
|
+
attributes[attr.name] = attr.value;
|
|
6399
|
+
});
|
|
6400
|
+
return {
|
|
6401
|
+
trackInfo,
|
|
6402
|
+
oldSrc,
|
|
6403
|
+
parent,
|
|
6404
|
+
nextSibling,
|
|
6405
|
+
attributes
|
|
6406
|
+
};
|
|
6407
|
+
});
|
|
6408
|
+
tracksToReadd.forEach(({ trackInfo }) => {
|
|
6409
|
+
trackInfo.trackElement.remove();
|
|
6410
|
+
});
|
|
6411
|
+
this.element.load();
|
|
6412
|
+
setTimeout(() => {
|
|
6413
|
+
tracksToReadd.forEach(({ trackInfo, oldSrc, parent, nextSibling, attributes }) => {
|
|
6414
|
+
swappedTracksForTranscript.push(trackInfo);
|
|
6415
|
+
const newTrackElement = document.createElement("track");
|
|
6416
|
+
newTrackElement.setAttribute("src", trackInfo.describedSrc);
|
|
6417
|
+
Object.keys(attributes).forEach((attrName) => {
|
|
6418
|
+
if (attrName !== "src" && attrName !== "data-desc-src") {
|
|
6419
|
+
newTrackElement.setAttribute(attrName, attributes[attrName]);
|
|
6420
|
+
}
|
|
6421
|
+
});
|
|
6422
|
+
if (nextSibling && nextSibling.parentNode) {
|
|
6423
|
+
parent.insertBefore(newTrackElement, nextSibling);
|
|
6424
|
+
} else {
|
|
6425
|
+
parent.appendChild(newTrackElement);
|
|
6426
|
+
}
|
|
6427
|
+
trackInfo.trackElement = newTrackElement;
|
|
6428
|
+
});
|
|
6429
|
+
this.element.load();
|
|
6430
|
+
this.invalidateTrackCache();
|
|
6431
|
+
const setupNewTracks = () => {
|
|
6432
|
+
this.setManagedTimeout(() => {
|
|
6433
|
+
swappedTracksForTranscript.forEach((trackInfo) => {
|
|
6434
|
+
const trackElement = trackInfo.trackElement;
|
|
6435
|
+
const newTextTrack = trackElement.track;
|
|
6436
|
+
if (newTextTrack) {
|
|
6437
|
+
const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };
|
|
6438
|
+
newTextTrack.mode = "hidden";
|
|
6439
|
+
const restoreMode = () => {
|
|
6440
|
+
if (modeInfo.wasShowing) {
|
|
6441
|
+
newTextTrack.mode = "hidden";
|
|
6442
|
+
} else if (modeInfo.wasHidden) {
|
|
6443
|
+
newTextTrack.mode = "hidden";
|
|
6444
|
+
} else {
|
|
6445
|
+
newTextTrack.mode = "disabled";
|
|
6446
|
+
}
|
|
6447
|
+
};
|
|
6448
|
+
if (newTextTrack.readyState >= 2) {
|
|
6449
|
+
restoreMode();
|
|
6450
|
+
} else {
|
|
6451
|
+
newTextTrack.addEventListener("load", restoreMode, { once: true });
|
|
6452
|
+
newTextTrack.addEventListener("error", restoreMode, { once: true });
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
});
|
|
6456
|
+
}, 300);
|
|
6457
|
+
};
|
|
6458
|
+
if (this.element.readyState >= 1) {
|
|
6459
|
+
setTimeout(setupNewTracks, 200);
|
|
6460
|
+
} else {
|
|
6461
|
+
this.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
|
|
6462
|
+
setTimeout(setupNewTracks, 2e3);
|
|
6463
|
+
}
|
|
6464
|
+
}, 100);
|
|
6465
|
+
const skippedCount = validationResults.length - tracksToSwap.length;
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
const allSourceElements = this.sourceElements;
|
|
6469
|
+
const sourcesToUpdate = [];
|
|
6470
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6471
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6472
|
+
const currentSrc2 = sourceEl.getAttribute("src");
|
|
6473
|
+
if (descSrcAttr) {
|
|
6474
|
+
const type = sourceEl.getAttribute("type");
|
|
6475
|
+
let origSrc = sourceEl.getAttribute("data-orig-src");
|
|
6476
|
+
if (!origSrc) {
|
|
6477
|
+
origSrc = currentSrc2;
|
|
6478
|
+
}
|
|
6479
|
+
sourcesToUpdate.push({
|
|
6480
|
+
src: descSrcAttr,
|
|
6481
|
+
// Use described version
|
|
6482
|
+
type,
|
|
6483
|
+
origSrc,
|
|
6484
|
+
descSrc: descSrcAttr
|
|
6485
|
+
});
|
|
6486
|
+
} else {
|
|
6487
|
+
const type = sourceEl.getAttribute("type");
|
|
6488
|
+
const src = sourceEl.getAttribute("src");
|
|
6489
|
+
sourcesToUpdate.push({
|
|
6490
|
+
src,
|
|
6491
|
+
type,
|
|
6492
|
+
origSrc: null,
|
|
6493
|
+
descSrc: null
|
|
6494
|
+
});
|
|
6495
|
+
}
|
|
6496
|
+
});
|
|
6497
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6498
|
+
sourceEl.remove();
|
|
6499
|
+
});
|
|
6500
|
+
sourcesToUpdate.forEach((sourceInfo) => {
|
|
6501
|
+
const newSource = document.createElement("source");
|
|
6502
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
6503
|
+
if (sourceInfo.type) {
|
|
6504
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
6505
|
+
}
|
|
6506
|
+
if (sourceInfo.origSrc) {
|
|
6507
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
6508
|
+
}
|
|
6509
|
+
if (sourceInfo.descSrc) {
|
|
6510
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
6511
|
+
}
|
|
6512
|
+
this.element.appendChild(newSource);
|
|
6513
|
+
});
|
|
6514
|
+
this.element.load();
|
|
6515
|
+
await new Promise((resolve) => {
|
|
6516
|
+
const onLoadedMetadata = () => {
|
|
6517
|
+
this.element.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
6518
|
+
resolve();
|
|
6519
|
+
};
|
|
6520
|
+
this.element.addEventListener("loadedmetadata", onLoadedMetadata);
|
|
6521
|
+
});
|
|
6522
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
6523
|
+
if (this.element.tagName === "VIDEO" && currentTime === 0 && !wasPlaying) {
|
|
6524
|
+
if (this.element.readyState >= 1) {
|
|
6525
|
+
this.element.currentTime = 1e-3;
|
|
6526
|
+
setTimeout(() => {
|
|
6527
|
+
this.element.currentTime = 0;
|
|
6528
|
+
}, 10);
|
|
6529
|
+
}
|
|
6530
|
+
}
|
|
6531
|
+
this.seek(currentTime);
|
|
6532
|
+
if (wasPlaying) {
|
|
6533
|
+
this.play();
|
|
6534
|
+
}
|
|
6535
|
+
this.state.audioDescriptionEnabled = true;
|
|
6536
|
+
this.emit("audiodescriptionenabled");
|
|
6537
|
+
} else {
|
|
6538
|
+
if (this.audioDescriptionCaptionTracks.length > 0) {
|
|
6539
|
+
const validationPromises = this.audioDescriptionCaptionTracks.map(async (trackInfo) => {
|
|
6540
|
+
if (trackInfo.trackElement && trackInfo.describedSrc) {
|
|
6541
|
+
if (trackInfo.explicit === true) {
|
|
6542
|
+
try {
|
|
6543
|
+
const exists = await this.validateTrackExists(trackInfo.describedSrc);
|
|
6544
|
+
return { trackInfo, exists };
|
|
6545
|
+
} catch (error) {
|
|
6546
|
+
return { trackInfo, exists: false };
|
|
6547
|
+
}
|
|
6548
|
+
} else {
|
|
6549
|
+
return { trackInfo, exists: false };
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
return { trackInfo, exists: false };
|
|
6553
|
+
});
|
|
6554
|
+
const validationResults = await Promise.all(validationPromises);
|
|
6555
|
+
const tracksToSwap = validationResults.filter((result) => result.exists);
|
|
6556
|
+
if (tracksToSwap.length > 0) {
|
|
6557
|
+
const trackModes = /* @__PURE__ */ new Map();
|
|
6558
|
+
tracksToSwap.forEach(({ trackInfo }) => {
|
|
6559
|
+
const textTrack = trackInfo.trackElement.track;
|
|
6560
|
+
if (textTrack) {
|
|
6561
|
+
trackModes.set(trackInfo, {
|
|
6562
|
+
wasShowing: textTrack.mode === "showing",
|
|
6563
|
+
wasHidden: textTrack.mode === "hidden"
|
|
6564
|
+
});
|
|
6565
|
+
} else {
|
|
6566
|
+
trackModes.set(trackInfo, {
|
|
6567
|
+
wasShowing: false,
|
|
6568
|
+
wasHidden: false
|
|
6569
|
+
});
|
|
6570
|
+
}
|
|
6571
|
+
});
|
|
6572
|
+
const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {
|
|
6573
|
+
const oldSrc = trackInfo.trackElement.getAttribute("src");
|
|
6574
|
+
const parent = trackInfo.trackElement.parentNode;
|
|
6575
|
+
const nextSibling = trackInfo.trackElement.nextSibling;
|
|
6576
|
+
const attributes = {};
|
|
6577
|
+
Array.from(trackInfo.trackElement.attributes).forEach((attr) => {
|
|
6578
|
+
attributes[attr.name] = attr.value;
|
|
6579
|
+
});
|
|
6580
|
+
return {
|
|
6581
|
+
trackInfo,
|
|
6582
|
+
oldSrc,
|
|
6583
|
+
parent,
|
|
6584
|
+
nextSibling,
|
|
6585
|
+
attributes
|
|
6586
|
+
};
|
|
6587
|
+
});
|
|
6588
|
+
tracksToReadd.forEach(({ trackInfo }) => {
|
|
6589
|
+
trackInfo.trackElement.remove();
|
|
6590
|
+
});
|
|
6591
|
+
this.element.load();
|
|
6592
|
+
setTimeout(() => {
|
|
6593
|
+
tracksToReadd.forEach(({ trackInfo, oldSrc, parent, nextSibling, attributes }) => {
|
|
6594
|
+
swappedTracksForTranscript.push(trackInfo);
|
|
6595
|
+
const newTrackElement = document.createElement("track");
|
|
6596
|
+
newTrackElement.setAttribute("src", trackInfo.describedSrc);
|
|
6597
|
+
Object.keys(attributes).forEach((attrName) => {
|
|
6598
|
+
if (attrName !== "src" && attrName !== "data-desc-src") {
|
|
6599
|
+
newTrackElement.setAttribute(attrName, attributes[attrName]);
|
|
6600
|
+
}
|
|
6601
|
+
});
|
|
6602
|
+
if (nextSibling && nextSibling.parentNode) {
|
|
6603
|
+
parent.insertBefore(newTrackElement, nextSibling);
|
|
6604
|
+
} else {
|
|
6605
|
+
parent.appendChild(newTrackElement);
|
|
6606
|
+
}
|
|
6607
|
+
trackInfo.trackElement = newTrackElement;
|
|
6608
|
+
});
|
|
6609
|
+
this.element.load();
|
|
6610
|
+
const setupNewTracks = () => {
|
|
6611
|
+
setTimeout(() => {
|
|
6612
|
+
swappedTracksForTranscript.forEach((trackInfo) => {
|
|
6613
|
+
const trackElement = trackInfo.trackElement;
|
|
6614
|
+
const newTextTrack = trackElement.track;
|
|
6615
|
+
if (newTextTrack) {
|
|
6616
|
+
const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };
|
|
6617
|
+
newTextTrack.mode = "hidden";
|
|
6618
|
+
const restoreMode = () => {
|
|
6619
|
+
if (modeInfo.wasShowing) {
|
|
6620
|
+
newTextTrack.mode = "hidden";
|
|
6621
|
+
} else if (modeInfo.wasHidden) {
|
|
6622
|
+
newTextTrack.mode = "hidden";
|
|
6623
|
+
} else {
|
|
6624
|
+
newTextTrack.mode = "disabled";
|
|
6625
|
+
}
|
|
6626
|
+
};
|
|
6627
|
+
if (newTextTrack.readyState >= 2) {
|
|
6628
|
+
restoreMode();
|
|
6629
|
+
} else {
|
|
6630
|
+
newTextTrack.addEventListener("load", restoreMode, { once: true });
|
|
6631
|
+
newTextTrack.addEventListener("error", restoreMode, { once: true });
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
});
|
|
6635
|
+
}, 300);
|
|
6636
|
+
};
|
|
6637
|
+
if (this.element.readyState >= 1) {
|
|
6638
|
+
setTimeout(setupNewTracks, 200);
|
|
6639
|
+
} else {
|
|
6640
|
+
this.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
|
|
6641
|
+
setTimeout(setupNewTracks, 2e3);
|
|
6642
|
+
}
|
|
6643
|
+
}, 100);
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
const fallbackSourceElements = this.sourceElements;
|
|
6647
|
+
const hasSourceElementsWithDesc2 = fallbackSourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6648
|
+
if (hasSourceElementsWithDesc2) {
|
|
6649
|
+
const fallbackSourcesToUpdate = [];
|
|
6650
|
+
fallbackSourceElements.forEach((sourceEl) => {
|
|
6651
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6652
|
+
const currentSrc = sourceEl.getAttribute("src");
|
|
6653
|
+
if (descSrcAttr) {
|
|
6654
|
+
const type = sourceEl.getAttribute("type");
|
|
6655
|
+
let origSrc = sourceEl.getAttribute("data-orig-src");
|
|
6656
|
+
if (!origSrc) {
|
|
6657
|
+
origSrc = currentSrc;
|
|
6658
|
+
}
|
|
6659
|
+
fallbackSourcesToUpdate.push({
|
|
6660
|
+
src: descSrcAttr,
|
|
6661
|
+
type,
|
|
6662
|
+
origSrc,
|
|
6663
|
+
descSrc: descSrcAttr
|
|
6664
|
+
});
|
|
6665
|
+
} else {
|
|
6666
|
+
const type = sourceEl.getAttribute("type");
|
|
6667
|
+
const src = sourceEl.getAttribute("src");
|
|
6668
|
+
fallbackSourcesToUpdate.push({
|
|
6669
|
+
src,
|
|
6670
|
+
type,
|
|
6671
|
+
origSrc: null,
|
|
6672
|
+
descSrc: null
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
});
|
|
6676
|
+
fallbackSourceElements.forEach((sourceEl) => {
|
|
6677
|
+
sourceEl.remove();
|
|
6678
|
+
});
|
|
6679
|
+
fallbackSourcesToUpdate.forEach((sourceInfo) => {
|
|
6680
|
+
const newSource = document.createElement("source");
|
|
6681
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
6682
|
+
if (sourceInfo.type) {
|
|
6683
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
6684
|
+
}
|
|
6685
|
+
if (sourceInfo.origSrc) {
|
|
6686
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
6687
|
+
}
|
|
6688
|
+
if (sourceInfo.descSrc) {
|
|
6689
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
6690
|
+
}
|
|
6691
|
+
this.element.appendChild(newSource);
|
|
6692
|
+
});
|
|
6693
|
+
this.element.load();
|
|
6694
|
+
this.invalidateTrackCache();
|
|
6695
|
+
} else {
|
|
6696
|
+
this.element.src = this.audioDescriptionSrc;
|
|
6697
|
+
}
|
|
6698
|
+
}
|
|
5919
6699
|
await new Promise((resolve) => {
|
|
5920
6700
|
const onLoadedMetadata = () => {
|
|
5921
6701
|
this.element.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
@@ -5923,10 +6703,177 @@ var Player = class extends EventEmitter {
|
|
|
5923
6703
|
};
|
|
5924
6704
|
this.element.addEventListener("loadedmetadata", onLoadedMetadata);
|
|
5925
6705
|
});
|
|
6706
|
+
if (this.element.tagName === "VIDEO" && currentTime === 0 && !wasPlaying) {
|
|
6707
|
+
if (this.element.readyState >= 1) {
|
|
6708
|
+
this.element.currentTime = 1e-3;
|
|
6709
|
+
this.setManagedTimeout(() => {
|
|
6710
|
+
this.element.currentTime = 0;
|
|
6711
|
+
}, 10);
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
5926
6714
|
this.seek(currentTime);
|
|
5927
6715
|
if (wasPlaying) {
|
|
5928
6716
|
this.play();
|
|
5929
6717
|
}
|
|
6718
|
+
if (swappedTracksForTranscript.length > 0 && this.captionManager) {
|
|
6719
|
+
const wasCaptionsEnabled = this.state.captionsEnabled;
|
|
6720
|
+
let currentTrackInfo = null;
|
|
6721
|
+
if (this.captionManager.currentTrack) {
|
|
6722
|
+
const currentTrackIndex = this.captionManager.tracks.findIndex((t) => t.track === this.captionManager.currentTrack.track);
|
|
6723
|
+
if (currentTrackIndex >= 0) {
|
|
6724
|
+
currentTrackInfo = {
|
|
6725
|
+
language: this.captionManager.tracks[currentTrackIndex].language,
|
|
6726
|
+
kind: this.captionManager.tracks[currentTrackIndex].kind
|
|
6727
|
+
};
|
|
6728
|
+
}
|
|
6729
|
+
}
|
|
6730
|
+
setTimeout(() => {
|
|
6731
|
+
this.captionManager.tracks = [];
|
|
6732
|
+
this.captionManager.loadTracks();
|
|
6733
|
+
if (wasCaptionsEnabled && currentTrackInfo && this.captionManager.tracks.length > 0) {
|
|
6734
|
+
const matchingTrackIndex = this.captionManager.tracks.findIndex(
|
|
6735
|
+
(t) => t.language === currentTrackInfo.language && t.kind === currentTrackInfo.kind
|
|
6736
|
+
);
|
|
6737
|
+
if (matchingTrackIndex >= 0) {
|
|
6738
|
+
this.captionManager.enable(matchingTrackIndex);
|
|
6739
|
+
} else if (this.captionManager.tracks.length > 0) {
|
|
6740
|
+
this.captionManager.enable(0);
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
}, 600);
|
|
6744
|
+
}
|
|
6745
|
+
if (this.transcriptManager && this.transcriptManager.isVisible) {
|
|
6746
|
+
const swappedTracks = typeof swappedTracksForTranscript !== "undefined" ? swappedTracksForTranscript : [];
|
|
6747
|
+
if (swappedTracks.length > 0) {
|
|
6748
|
+
const onMetadataLoaded = () => {
|
|
6749
|
+
this.invalidateTrackCache();
|
|
6750
|
+
const allTextTracks = this.textTracks;
|
|
6751
|
+
const freshTracks = swappedTracks.map((trackInfo) => {
|
|
6752
|
+
const trackEl = trackInfo.trackElement;
|
|
6753
|
+
const expectedSrc = trackEl.getAttribute("src");
|
|
6754
|
+
const srclang = trackEl.getAttribute("srclang");
|
|
6755
|
+
const kind = trackEl.getAttribute("kind");
|
|
6756
|
+
let foundTrack = allTextTracks.find((track) => trackEl.track === track);
|
|
6757
|
+
if (!foundTrack) {
|
|
6758
|
+
foundTrack = allTextTracks.find((track) => {
|
|
6759
|
+
if (track.language === srclang && (track.kind === kind || kind === "captions" && track.kind === "subtitles")) {
|
|
6760
|
+
const trackElementForTrack = this.findTrackElement(track);
|
|
6761
|
+
if (trackElementForTrack) {
|
|
6762
|
+
const actualSrc = trackElementForTrack.getAttribute("src");
|
|
6763
|
+
if (actualSrc === expectedSrc) {
|
|
6764
|
+
return true;
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
return false;
|
|
6769
|
+
});
|
|
6770
|
+
}
|
|
6771
|
+
if (foundTrack) {
|
|
6772
|
+
const trackElement = this.findTrackElement(foundTrack);
|
|
6773
|
+
if (trackElement && trackElement.getAttribute("src") !== expectedSrc) {
|
|
6774
|
+
return null;
|
|
6775
|
+
}
|
|
6776
|
+
}
|
|
6777
|
+
return foundTrack;
|
|
6778
|
+
}).filter(Boolean);
|
|
6779
|
+
if (freshTracks.length === 0) {
|
|
6780
|
+
this.setManagedTimeout(() => {
|
|
6781
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6782
|
+
this.transcriptManager.loadTranscriptData();
|
|
6783
|
+
}
|
|
6784
|
+
}, 1e3);
|
|
6785
|
+
return;
|
|
6786
|
+
}
|
|
6787
|
+
freshTracks.forEach((track) => {
|
|
6788
|
+
if (track.mode === "disabled") {
|
|
6789
|
+
track.mode = "hidden";
|
|
6790
|
+
}
|
|
6791
|
+
});
|
|
6792
|
+
let loadedCount = 0;
|
|
6793
|
+
const checkLoaded = () => {
|
|
6794
|
+
loadedCount++;
|
|
6795
|
+
if (loadedCount >= freshTracks.length) {
|
|
6796
|
+
this.setManagedTimeout(() => {
|
|
6797
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6798
|
+
this.invalidateTrackCache();
|
|
6799
|
+
const allTextTracks2 = this.textTracks;
|
|
6800
|
+
const swappedTrackSrcs = swappedTracks.map((t) => t.describedSrc);
|
|
6801
|
+
const hasCorrectTracks = freshTracks.some((track) => {
|
|
6802
|
+
const trackEl = this.findTrackElement(track);
|
|
6803
|
+
return trackEl && swappedTrackSrcs.includes(trackEl.getAttribute("src"));
|
|
6804
|
+
});
|
|
6805
|
+
if (hasCorrectTracks || freshTracks.length > 0) {
|
|
6806
|
+
this.transcriptManager.loadTranscriptData();
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
}, 800);
|
|
6810
|
+
}
|
|
6811
|
+
};
|
|
6812
|
+
freshTracks.forEach((track) => {
|
|
6813
|
+
if (track.mode === "disabled") {
|
|
6814
|
+
track.mode = "hidden";
|
|
6815
|
+
}
|
|
6816
|
+
const trackElementForTrack = this.findTrackElement(track);
|
|
6817
|
+
const actualSrc = trackElementForTrack ? trackElementForTrack.getAttribute("src") : null;
|
|
6818
|
+
const expectedTrackInfo = swappedTracks.find((t) => {
|
|
6819
|
+
const tEl = t.trackElement;
|
|
6820
|
+
return tEl && (tEl.track === track || tEl.getAttribute("srclang") === track.language && tEl.getAttribute("kind") === track.kind);
|
|
6821
|
+
});
|
|
6822
|
+
const expectedSrc = expectedTrackInfo ? expectedTrackInfo.describedSrc : null;
|
|
6823
|
+
if (expectedSrc && actualSrc && actualSrc !== expectedSrc) {
|
|
6824
|
+
checkLoaded();
|
|
6825
|
+
return;
|
|
6826
|
+
}
|
|
6827
|
+
if (track.readyState >= 2 && track.cues && track.cues.length > 0) {
|
|
6828
|
+
checkLoaded();
|
|
6829
|
+
} else {
|
|
6830
|
+
if (track.mode === "disabled") {
|
|
6831
|
+
track.mode = "hidden";
|
|
6832
|
+
}
|
|
6833
|
+
const onTrackLoad = () => {
|
|
6834
|
+
this.setManagedTimeout(checkLoaded, 300);
|
|
6835
|
+
};
|
|
6836
|
+
if (track.readyState >= 2) {
|
|
6837
|
+
this.setManagedTimeout(() => {
|
|
6838
|
+
if (track.cues && track.cues.length > 0) {
|
|
6839
|
+
checkLoaded();
|
|
6840
|
+
} else {
|
|
6841
|
+
track.addEventListener("load", onTrackLoad, { once: true });
|
|
6842
|
+
}
|
|
6843
|
+
}, 100);
|
|
6844
|
+
} else {
|
|
6845
|
+
track.addEventListener("load", onTrackLoad, { once: true });
|
|
6846
|
+
track.addEventListener("error", () => {
|
|
6847
|
+
checkLoaded();
|
|
6848
|
+
}, { once: true });
|
|
6849
|
+
}
|
|
6850
|
+
}
|
|
6851
|
+
});
|
|
6852
|
+
};
|
|
6853
|
+
const waitForTracks = () => {
|
|
6854
|
+
this.setManagedTimeout(() => {
|
|
6855
|
+
if (this.element.readyState >= 1) {
|
|
6856
|
+
onMetadataLoaded();
|
|
6857
|
+
} else {
|
|
6858
|
+
this.element.addEventListener("loadedmetadata", onMetadataLoaded, { once: true });
|
|
6859
|
+
this.setManagedTimeout(onMetadataLoaded, 2e3);
|
|
6860
|
+
}
|
|
6861
|
+
}, 500);
|
|
6862
|
+
};
|
|
6863
|
+
waitForTracks();
|
|
6864
|
+
setTimeout(() => {
|
|
6865
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6866
|
+
this.transcriptManager.loadTranscriptData();
|
|
6867
|
+
}
|
|
6868
|
+
}, 5e3);
|
|
6869
|
+
} else {
|
|
6870
|
+
setTimeout(() => {
|
|
6871
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6872
|
+
this.transcriptManager.loadTranscriptData();
|
|
6873
|
+
}
|
|
6874
|
+
}, 800);
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
5930
6877
|
this.state.audioDescriptionEnabled = true;
|
|
5931
6878
|
this.emit("audiodescriptionenabled");
|
|
5932
6879
|
}
|
|
@@ -5936,7 +6883,64 @@ var Player = class extends EventEmitter {
|
|
|
5936
6883
|
}
|
|
5937
6884
|
const currentTime = this.state.currentTime;
|
|
5938
6885
|
const wasPlaying = this.state.playing;
|
|
5939
|
-
this.
|
|
6886
|
+
if (this.audioDescriptionCaptionTracks.length > 0) {
|
|
6887
|
+
this.audioDescriptionCaptionTracks.forEach((trackInfo) => {
|
|
6888
|
+
if (trackInfo.trackElement && trackInfo.originalTrackSrc) {
|
|
6889
|
+
trackInfo.trackElement.setAttribute("src", trackInfo.originalTrackSrc);
|
|
6890
|
+
}
|
|
6891
|
+
});
|
|
6892
|
+
}
|
|
6893
|
+
const allSourceElements = this.sourceElements;
|
|
6894
|
+
const hasSourceElementsToSwap = allSourceElements.some((el) => el.getAttribute("data-orig-src"));
|
|
6895
|
+
if (hasSourceElementsToSwap) {
|
|
6896
|
+
const sourcesToRestore = [];
|
|
6897
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6898
|
+
const origSrcAttr = sourceEl.getAttribute("data-orig-src");
|
|
6899
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6900
|
+
if (origSrcAttr) {
|
|
6901
|
+
const type = sourceEl.getAttribute("type");
|
|
6902
|
+
sourcesToRestore.push({
|
|
6903
|
+
src: origSrcAttr,
|
|
6904
|
+
// Use original version
|
|
6905
|
+
type,
|
|
6906
|
+
origSrc: origSrcAttr,
|
|
6907
|
+
descSrc: descSrcAttr
|
|
6908
|
+
// Keep data-desc-src for future swaps
|
|
6909
|
+
});
|
|
6910
|
+
} else {
|
|
6911
|
+
const type = sourceEl.getAttribute("type");
|
|
6912
|
+
const src = sourceEl.getAttribute("src");
|
|
6913
|
+
sourcesToRestore.push({
|
|
6914
|
+
src,
|
|
6915
|
+
type,
|
|
6916
|
+
origSrc: null,
|
|
6917
|
+
descSrc: descSrcAttr
|
|
6918
|
+
});
|
|
6919
|
+
}
|
|
6920
|
+
});
|
|
6921
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6922
|
+
sourceEl.remove();
|
|
6923
|
+
});
|
|
6924
|
+
sourcesToRestore.forEach((sourceInfo) => {
|
|
6925
|
+
const newSource = document.createElement("source");
|
|
6926
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
6927
|
+
if (sourceInfo.type) {
|
|
6928
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
6929
|
+
}
|
|
6930
|
+
if (sourceInfo.origSrc) {
|
|
6931
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
6932
|
+
}
|
|
6933
|
+
if (sourceInfo.descSrc) {
|
|
6934
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
6935
|
+
}
|
|
6936
|
+
this.element.appendChild(newSource);
|
|
6937
|
+
});
|
|
6938
|
+
this.element.load();
|
|
6939
|
+
} else {
|
|
6940
|
+
const originalSrcToUse = this.originalAudioDescriptionSource || this.originalSrc;
|
|
6941
|
+
this.element.src = originalSrcToUse;
|
|
6942
|
+
this.element.load();
|
|
6943
|
+
}
|
|
5940
6944
|
await new Promise((resolve) => {
|
|
5941
6945
|
const onLoadedMetadata = () => {
|
|
5942
6946
|
this.element.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
@@ -5948,13 +6952,50 @@ var Player = class extends EventEmitter {
|
|
|
5948
6952
|
if (wasPlaying) {
|
|
5949
6953
|
this.play();
|
|
5950
6954
|
}
|
|
6955
|
+
if (this.transcriptManager && this.transcriptManager.isVisible) {
|
|
6956
|
+
this.setManagedTimeout(() => {
|
|
6957
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6958
|
+
this.transcriptManager.loadTranscriptData();
|
|
6959
|
+
}
|
|
6960
|
+
}, 500);
|
|
6961
|
+
}
|
|
5951
6962
|
this.state.audioDescriptionEnabled = false;
|
|
5952
6963
|
this.emit("audiodescriptiondisabled");
|
|
5953
6964
|
}
|
|
5954
6965
|
async toggleAudioDescription() {
|
|
5955
|
-
const
|
|
5956
|
-
const
|
|
5957
|
-
if (descriptionTrack) {
|
|
6966
|
+
const descriptionTrack = this.findTextTrack("descriptions");
|
|
6967
|
+
const hasAudioDescriptionSrc = this.audioDescriptionSrc || this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6968
|
+
if (descriptionTrack && hasAudioDescriptionSrc) {
|
|
6969
|
+
if (this.state.audioDescriptionEnabled) {
|
|
6970
|
+
descriptionTrack.mode = "hidden";
|
|
6971
|
+
await this.disableAudioDescription();
|
|
6972
|
+
} else {
|
|
6973
|
+
await this.enableAudioDescription();
|
|
6974
|
+
const enableDescriptionTrack = () => {
|
|
6975
|
+
this.invalidateTrackCache();
|
|
6976
|
+
const descTrack = this.findTextTrack("descriptions");
|
|
6977
|
+
if (descTrack) {
|
|
6978
|
+
if (descTrack.mode === "disabled") {
|
|
6979
|
+
descTrack.mode = "hidden";
|
|
6980
|
+
this.setManagedTimeout(() => {
|
|
6981
|
+
descTrack.mode = "showing";
|
|
6982
|
+
}, 50);
|
|
6983
|
+
} else {
|
|
6984
|
+
descTrack.mode = "showing";
|
|
6985
|
+
}
|
|
6986
|
+
} else if (this.element.readyState < 2) {
|
|
6987
|
+
this.setManagedTimeout(enableDescriptionTrack, 100);
|
|
6988
|
+
}
|
|
6989
|
+
};
|
|
6990
|
+
if (this.element.readyState >= 1) {
|
|
6991
|
+
this.setManagedTimeout(enableDescriptionTrack, 200);
|
|
6992
|
+
} else {
|
|
6993
|
+
this.element.addEventListener("loadedmetadata", () => {
|
|
6994
|
+
this.setManagedTimeout(enableDescriptionTrack, 200);
|
|
6995
|
+
}, { once: true });
|
|
6996
|
+
}
|
|
6997
|
+
}
|
|
6998
|
+
} else if (descriptionTrack) {
|
|
5958
6999
|
if (descriptionTrack.mode === "showing") {
|
|
5959
7000
|
descriptionTrack.mode = "hidden";
|
|
5960
7001
|
this.state.audioDescriptionEnabled = false;
|
|
@@ -5964,7 +7005,7 @@ var Player = class extends EventEmitter {
|
|
|
5964
7005
|
this.state.audioDescriptionEnabled = true;
|
|
5965
7006
|
this.emit("audiodescriptionenabled");
|
|
5966
7007
|
}
|
|
5967
|
-
} else if (
|
|
7008
|
+
} else if (hasAudioDescriptionSrc) {
|
|
5968
7009
|
if (this.state.audioDescriptionEnabled) {
|
|
5969
7010
|
await this.disableAudioDescription();
|
|
5970
7011
|
} else {
|
|
@@ -6365,9 +7406,25 @@ var Player = class extends EventEmitter {
|
|
|
6365
7406
|
}
|
|
6366
7407
|
}
|
|
6367
7408
|
// Logging
|
|
6368
|
-
log(
|
|
6369
|
-
if (this.options.debug) {
|
|
6370
|
-
|
|
7409
|
+
log(...messages) {
|
|
7410
|
+
if (!this.options.debug) {
|
|
7411
|
+
return;
|
|
7412
|
+
}
|
|
7413
|
+
let type = "log";
|
|
7414
|
+
if (messages.length > 0) {
|
|
7415
|
+
const potentialType = messages[messages.length - 1];
|
|
7416
|
+
if (typeof potentialType === "string" && console[potentialType]) {
|
|
7417
|
+
type = potentialType;
|
|
7418
|
+
messages = messages.slice(0, -1);
|
|
7419
|
+
}
|
|
7420
|
+
}
|
|
7421
|
+
if (messages.length === 0) {
|
|
7422
|
+
messages = [""];
|
|
7423
|
+
}
|
|
7424
|
+
if (typeof console[type] === "function") {
|
|
7425
|
+
console[type]("[VidPly]", ...messages);
|
|
7426
|
+
} else {
|
|
7427
|
+
console.log("[VidPly]", ...messages);
|
|
6371
7428
|
}
|
|
6372
7429
|
}
|
|
6373
7430
|
// Setup responsive handlers
|
|
@@ -6427,7 +7484,7 @@ var Player = class extends EventEmitter {
|
|
|
6427
7484
|
this.controlBar.updateFullscreenButton();
|
|
6428
7485
|
}
|
|
6429
7486
|
if (this.signLanguageWrapper && this.signLanguageWrapper.style.display !== "none") {
|
|
6430
|
-
|
|
7487
|
+
this.setManagedTimeout(() => {
|
|
6431
7488
|
requestAnimationFrame(() => {
|
|
6432
7489
|
this.storage.saveSignLanguagePreferences({ size: null });
|
|
6433
7490
|
this.signLanguageDesiredPosition = "bottom-right";
|
|
@@ -6490,12 +7547,368 @@ var Player = class extends EventEmitter {
|
|
|
6490
7547
|
document.removeEventListener("MSFullscreenChange", this.fullscreenChangeHandler);
|
|
6491
7548
|
this.fullscreenChangeHandler = null;
|
|
6492
7549
|
}
|
|
7550
|
+
this.timeouts.forEach((timeoutId) => clearTimeout(timeoutId));
|
|
7551
|
+
this.timeouts.clear();
|
|
7552
|
+
if (this.metadataCueChangeHandler) {
|
|
7553
|
+
const textTracks = this.textTracks;
|
|
7554
|
+
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
7555
|
+
if (metadataTrack) {
|
|
7556
|
+
metadataTrack.removeEventListener("cuechange", this.metadataCueChangeHandler);
|
|
7557
|
+
}
|
|
7558
|
+
this.metadataCueChangeHandler = null;
|
|
7559
|
+
}
|
|
7560
|
+
if (this.metadataAlertHandlers && this.metadataAlertHandlers.size > 0) {
|
|
7561
|
+
this.metadataAlertHandlers.forEach(({ button, handler }) => {
|
|
7562
|
+
if (button && handler) {
|
|
7563
|
+
button.removeEventListener("click", handler);
|
|
7564
|
+
}
|
|
7565
|
+
});
|
|
7566
|
+
this.metadataAlertHandlers.clear();
|
|
7567
|
+
}
|
|
6493
7568
|
if (this.container && this.container.parentNode) {
|
|
6494
7569
|
this.container.parentNode.insertBefore(this.element, this.container);
|
|
6495
7570
|
this.container.parentNode.removeChild(this.container);
|
|
6496
7571
|
}
|
|
6497
7572
|
this.removeAllListeners();
|
|
6498
7573
|
}
|
|
7574
|
+
/**
|
|
7575
|
+
* Setup metadata track handling
|
|
7576
|
+
* This enables metadata tracks and listens for cue changes to trigger actions
|
|
7577
|
+
*/
|
|
7578
|
+
setupMetadataHandling() {
|
|
7579
|
+
const setupMetadata = () => {
|
|
7580
|
+
const textTracks = this.textTracks;
|
|
7581
|
+
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
7582
|
+
if (metadataTrack) {
|
|
7583
|
+
if (metadataTrack.mode === "disabled") {
|
|
7584
|
+
metadataTrack.mode = "hidden";
|
|
7585
|
+
}
|
|
7586
|
+
if (this.metadataCueChangeHandler) {
|
|
7587
|
+
metadataTrack.removeEventListener("cuechange", this.metadataCueChangeHandler);
|
|
7588
|
+
}
|
|
7589
|
+
this.metadataCueChangeHandler = () => {
|
|
7590
|
+
const activeCues = Array.from(metadataTrack.activeCues || []);
|
|
7591
|
+
if (activeCues.length > 0) {
|
|
7592
|
+
if (this.options.debug) {
|
|
7593
|
+
this.log("[Metadata] Active cues:", activeCues.map((c) => ({
|
|
7594
|
+
start: c.startTime,
|
|
7595
|
+
end: c.endTime,
|
|
7596
|
+
text: c.text
|
|
7597
|
+
})));
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
7600
|
+
activeCues.forEach((cue) => {
|
|
7601
|
+
this.handleMetadataCue(cue);
|
|
7602
|
+
});
|
|
7603
|
+
};
|
|
7604
|
+
metadataTrack.addEventListener("cuechange", this.metadataCueChangeHandler);
|
|
7605
|
+
if (this.options.debug) {
|
|
7606
|
+
const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;
|
|
7607
|
+
this.log("[Metadata] Track enabled,", cueCount, "cues available");
|
|
7608
|
+
}
|
|
7609
|
+
} else if (this.options.debug) {
|
|
7610
|
+
this.log("[Metadata] No metadata track found");
|
|
7611
|
+
}
|
|
7612
|
+
};
|
|
7613
|
+
setupMetadata();
|
|
7614
|
+
this.on("loadedmetadata", setupMetadata);
|
|
7615
|
+
}
|
|
7616
|
+
normalizeMetadataSelector(selector) {
|
|
7617
|
+
if (!selector) {
|
|
7618
|
+
return null;
|
|
7619
|
+
}
|
|
7620
|
+
const trimmed = selector.trim();
|
|
7621
|
+
if (!trimmed) {
|
|
7622
|
+
return null;
|
|
7623
|
+
}
|
|
7624
|
+
if (trimmed.startsWith("#") || trimmed.startsWith(".") || trimmed.startsWith("[")) {
|
|
7625
|
+
return trimmed;
|
|
7626
|
+
}
|
|
7627
|
+
return `#${trimmed}`;
|
|
7628
|
+
}
|
|
7629
|
+
resolveMetadataConfig(map, key) {
|
|
7630
|
+
if (!map || !key) {
|
|
7631
|
+
return null;
|
|
7632
|
+
}
|
|
7633
|
+
if (Object.prototype.hasOwnProperty.call(map, key)) {
|
|
7634
|
+
return map[key];
|
|
7635
|
+
}
|
|
7636
|
+
const withoutHash = key.replace(/^#/, "");
|
|
7637
|
+
if (Object.prototype.hasOwnProperty.call(map, withoutHash)) {
|
|
7638
|
+
return map[withoutHash];
|
|
7639
|
+
}
|
|
7640
|
+
return null;
|
|
7641
|
+
}
|
|
7642
|
+
cacheMetadataAlertContent(element, config = {}) {
|
|
7643
|
+
if (!element) {
|
|
7644
|
+
return;
|
|
7645
|
+
}
|
|
7646
|
+
const titleSelector = config.titleSelector || "[data-vidply-alert-title], h3, header";
|
|
7647
|
+
const messageSelector = config.messageSelector || "[data-vidply-alert-message], p";
|
|
7648
|
+
const titleEl = element.querySelector(titleSelector);
|
|
7649
|
+
if (titleEl && !titleEl.dataset.vidplyAlertTitleOriginal) {
|
|
7650
|
+
titleEl.dataset.vidplyAlertTitleOriginal = titleEl.textContent.trim();
|
|
7651
|
+
}
|
|
7652
|
+
const messageEl = element.querySelector(messageSelector);
|
|
7653
|
+
if (messageEl && !messageEl.dataset.vidplyAlertMessageOriginal) {
|
|
7654
|
+
messageEl.dataset.vidplyAlertMessageOriginal = messageEl.textContent.trim();
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
restoreMetadataAlertContent(element, config = {}) {
|
|
7658
|
+
if (!element) {
|
|
7659
|
+
return;
|
|
7660
|
+
}
|
|
7661
|
+
const titleSelector = config.titleSelector || "[data-vidply-alert-title], h3, header";
|
|
7662
|
+
const messageSelector = config.messageSelector || "[data-vidply-alert-message], p";
|
|
7663
|
+
const titleEl = element.querySelector(titleSelector);
|
|
7664
|
+
if (titleEl && titleEl.dataset.vidplyAlertTitleOriginal) {
|
|
7665
|
+
titleEl.textContent = titleEl.dataset.vidplyAlertTitleOriginal;
|
|
7666
|
+
}
|
|
7667
|
+
const messageEl = element.querySelector(messageSelector);
|
|
7668
|
+
if (messageEl && messageEl.dataset.vidplyAlertMessageOriginal) {
|
|
7669
|
+
messageEl.textContent = messageEl.dataset.vidplyAlertMessageOriginal;
|
|
7670
|
+
}
|
|
7671
|
+
}
|
|
7672
|
+
focusMetadataTarget(target, fallbackElement = null) {
|
|
7673
|
+
var _a, _b;
|
|
7674
|
+
if (!target || target === "none") {
|
|
7675
|
+
return;
|
|
7676
|
+
}
|
|
7677
|
+
if (target === "alert" && fallbackElement) {
|
|
7678
|
+
fallbackElement.focus();
|
|
7679
|
+
return;
|
|
7680
|
+
}
|
|
7681
|
+
if (target === "player") {
|
|
7682
|
+
if (this.container) {
|
|
7683
|
+
this.container.focus();
|
|
7684
|
+
}
|
|
7685
|
+
return;
|
|
7686
|
+
}
|
|
7687
|
+
if (target === "media") {
|
|
7688
|
+
this.element.focus();
|
|
7689
|
+
return;
|
|
7690
|
+
}
|
|
7691
|
+
if (target === "playButton") {
|
|
7692
|
+
const playButton = (_b = (_a = this.controlBar) == null ? void 0 : _a.controls) == null ? void 0 : _b.playPause;
|
|
7693
|
+
if (playButton) {
|
|
7694
|
+
playButton.focus();
|
|
7695
|
+
}
|
|
7696
|
+
return;
|
|
7697
|
+
}
|
|
7698
|
+
if (typeof target === "string") {
|
|
7699
|
+
const targetElement = document.querySelector(target);
|
|
7700
|
+
if (targetElement) {
|
|
7701
|
+
if (targetElement.tabIndex === -1 && !targetElement.hasAttribute("tabindex")) {
|
|
7702
|
+
targetElement.setAttribute("tabindex", "-1");
|
|
7703
|
+
}
|
|
7704
|
+
targetElement.focus();
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
7708
|
+
handleMetadataAlert(selector, options = {}) {
|
|
7709
|
+
if (!selector) {
|
|
7710
|
+
return;
|
|
7711
|
+
}
|
|
7712
|
+
const config = this.resolveMetadataConfig(this.options.metadataAlerts, selector) || {};
|
|
7713
|
+
const element = options.element || document.querySelector(selector);
|
|
7714
|
+
if (!element) {
|
|
7715
|
+
if (this.options.debug) {
|
|
7716
|
+
this.log("[Metadata] Alert element not found:", selector);
|
|
7717
|
+
}
|
|
7718
|
+
return;
|
|
7719
|
+
}
|
|
7720
|
+
if (this.options.debug) {
|
|
7721
|
+
this.log("[Metadata] Handling alert", selector, { reason: options.reason, config });
|
|
7722
|
+
}
|
|
7723
|
+
this.cacheMetadataAlertContent(element, config);
|
|
7724
|
+
if (!element.dataset.vidplyAlertOriginalDisplay) {
|
|
7725
|
+
element.dataset.vidplyAlertOriginalDisplay = element.style.display || "";
|
|
7726
|
+
}
|
|
7727
|
+
if (!element.dataset.vidplyAlertDisplay) {
|
|
7728
|
+
element.dataset.vidplyAlertDisplay = config.display || "block";
|
|
7729
|
+
}
|
|
7730
|
+
const shouldShow = options.show !== void 0 ? options.show : config.show !== false;
|
|
7731
|
+
if (shouldShow) {
|
|
7732
|
+
const displayValue = config.display || element.dataset.vidplyAlertDisplay || "block";
|
|
7733
|
+
element.style.display = displayValue;
|
|
7734
|
+
element.hidden = false;
|
|
7735
|
+
element.removeAttribute("hidden");
|
|
7736
|
+
element.setAttribute("aria-hidden", "false");
|
|
7737
|
+
element.setAttribute("data-vidply-alert-active", "true");
|
|
7738
|
+
}
|
|
7739
|
+
const shouldReset = config.resetContent !== false && options.reason === "focus";
|
|
7740
|
+
if (shouldReset) {
|
|
7741
|
+
this.restoreMetadataAlertContent(element, config);
|
|
7742
|
+
}
|
|
7743
|
+
const shouldFocus = options.focus !== void 0 ? options.focus : config.focusOnShow ?? options.reason !== "focus";
|
|
7744
|
+
if (shouldShow && shouldFocus) {
|
|
7745
|
+
if (element.tabIndex === -1 && !element.hasAttribute("tabindex")) {
|
|
7746
|
+
element.setAttribute("tabindex", "-1");
|
|
7747
|
+
}
|
|
7748
|
+
element.focus();
|
|
7749
|
+
}
|
|
7750
|
+
if (shouldShow && config.autoScroll !== false && options.autoScroll !== false) {
|
|
7751
|
+
element.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
7752
|
+
}
|
|
7753
|
+
const continueSelector = config.continueButton;
|
|
7754
|
+
if (continueSelector) {
|
|
7755
|
+
let continueButton = null;
|
|
7756
|
+
if (continueSelector === "self") {
|
|
7757
|
+
continueButton = element;
|
|
7758
|
+
} else if (element.matches(continueSelector)) {
|
|
7759
|
+
continueButton = element;
|
|
7760
|
+
} else {
|
|
7761
|
+
continueButton = element.querySelector(continueSelector) || document.querySelector(continueSelector);
|
|
7762
|
+
}
|
|
7763
|
+
if (continueButton && !this.metadataAlertHandlers.has(selector)) {
|
|
7764
|
+
const handler = () => {
|
|
7765
|
+
const hideOnContinue = config.hideOnContinue !== false;
|
|
7766
|
+
if (hideOnContinue) {
|
|
7767
|
+
const originalDisplay = element.dataset.vidplyAlertOriginalDisplay || "";
|
|
7768
|
+
element.style.display = config.hideDisplay || originalDisplay || "none";
|
|
7769
|
+
element.setAttribute("aria-hidden", "true");
|
|
7770
|
+
element.removeAttribute("data-vidply-alert-active");
|
|
7771
|
+
}
|
|
7772
|
+
if (config.resume !== false && this.state.paused) {
|
|
7773
|
+
this.play();
|
|
7774
|
+
}
|
|
7775
|
+
const focusTarget = config.focusTarget || "playButton";
|
|
7776
|
+
this.setManagedTimeout(() => {
|
|
7777
|
+
this.focusMetadataTarget(focusTarget, element);
|
|
7778
|
+
}, config.focusDelay ?? 100);
|
|
7779
|
+
};
|
|
7780
|
+
continueButton.addEventListener("click", handler);
|
|
7781
|
+
this.metadataAlertHandlers.set(selector, { button: continueButton, handler });
|
|
7782
|
+
}
|
|
7783
|
+
}
|
|
7784
|
+
return element;
|
|
7785
|
+
}
|
|
7786
|
+
handleMetadataHashtags(hashtags) {
|
|
7787
|
+
if (!Array.isArray(hashtags) || hashtags.length === 0) {
|
|
7788
|
+
return;
|
|
7789
|
+
}
|
|
7790
|
+
const configMap = this.options.metadataHashtags;
|
|
7791
|
+
if (!configMap) {
|
|
7792
|
+
return;
|
|
7793
|
+
}
|
|
7794
|
+
hashtags.forEach((tag) => {
|
|
7795
|
+
const config = this.resolveMetadataConfig(configMap, tag);
|
|
7796
|
+
if (!config) {
|
|
7797
|
+
return;
|
|
7798
|
+
}
|
|
7799
|
+
const selector = this.normalizeMetadataSelector(config.alert || config.selector || config.target);
|
|
7800
|
+
if (!selector) {
|
|
7801
|
+
return;
|
|
7802
|
+
}
|
|
7803
|
+
const element = document.querySelector(selector);
|
|
7804
|
+
if (!element) {
|
|
7805
|
+
if (this.options.debug) {
|
|
7806
|
+
this.log("[Metadata] Hashtag target not found:", selector);
|
|
7807
|
+
}
|
|
7808
|
+
return;
|
|
7809
|
+
}
|
|
7810
|
+
if (this.options.debug) {
|
|
7811
|
+
this.log("[Metadata] Handling hashtag", tag, { selector, config });
|
|
7812
|
+
}
|
|
7813
|
+
this.cacheMetadataAlertContent(element, config);
|
|
7814
|
+
if (config.title) {
|
|
7815
|
+
const titleSelector = config.titleSelector || "[data-vidply-alert-title], h3, header";
|
|
7816
|
+
const titleEl = element.querySelector(titleSelector);
|
|
7817
|
+
if (titleEl) {
|
|
7818
|
+
titleEl.textContent = config.title;
|
|
7819
|
+
}
|
|
7820
|
+
}
|
|
7821
|
+
if (config.message) {
|
|
7822
|
+
const messageSelector = config.messageSelector || "[data-vidply-alert-message], p";
|
|
7823
|
+
const messageEl = element.querySelector(messageSelector);
|
|
7824
|
+
if (messageEl) {
|
|
7825
|
+
messageEl.textContent = config.message;
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
const show = config.show !== false;
|
|
7829
|
+
const focus = config.focus !== void 0 ? config.focus : false;
|
|
7830
|
+
this.handleMetadataAlert(selector, {
|
|
7831
|
+
element,
|
|
7832
|
+
show,
|
|
7833
|
+
focus,
|
|
7834
|
+
autoScroll: config.autoScroll,
|
|
7835
|
+
reason: "hashtag"
|
|
7836
|
+
});
|
|
7837
|
+
});
|
|
7838
|
+
}
|
|
7839
|
+
/**
|
|
7840
|
+
* Handle individual metadata cues
|
|
7841
|
+
* Parses metadata text and emits events or triggers actions
|
|
7842
|
+
*/
|
|
7843
|
+
handleMetadataCue(cue) {
|
|
7844
|
+
const text = cue.text.trim();
|
|
7845
|
+
if (this.options.debug) {
|
|
7846
|
+
this.log("[Metadata] Processing cue:", {
|
|
7847
|
+
time: cue.startTime,
|
|
7848
|
+
text
|
|
7849
|
+
});
|
|
7850
|
+
}
|
|
7851
|
+
this.emit("metadata", {
|
|
7852
|
+
time: cue.startTime,
|
|
7853
|
+
endTime: cue.endTime,
|
|
7854
|
+
text,
|
|
7855
|
+
cue
|
|
7856
|
+
});
|
|
7857
|
+
if (text.includes("PAUSE")) {
|
|
7858
|
+
if (!this.state.paused) {
|
|
7859
|
+
if (this.options.debug) {
|
|
7860
|
+
this.log("[Metadata] Pausing video at", cue.startTime);
|
|
7861
|
+
}
|
|
7862
|
+
this.pause();
|
|
7863
|
+
}
|
|
7864
|
+
this.emit("metadata:pause", { time: cue.startTime, text });
|
|
7865
|
+
}
|
|
7866
|
+
const focusMatch = text.match(/FOCUS:([\w#-]+)/);
|
|
7867
|
+
if (focusMatch) {
|
|
7868
|
+
const targetSelector = focusMatch[1];
|
|
7869
|
+
const normalizedSelector = this.normalizeMetadataSelector(targetSelector);
|
|
7870
|
+
const targetElement = normalizedSelector ? document.querySelector(normalizedSelector) : null;
|
|
7871
|
+
if (targetElement) {
|
|
7872
|
+
if (this.options.debug) {
|
|
7873
|
+
this.log("[Metadata] Focusing element:", normalizedSelector);
|
|
7874
|
+
}
|
|
7875
|
+
if (targetElement.tabIndex === -1 && !targetElement.hasAttribute("tabindex")) {
|
|
7876
|
+
targetElement.setAttribute("tabindex", "-1");
|
|
7877
|
+
}
|
|
7878
|
+
this.setManagedTimeout(() => {
|
|
7879
|
+
targetElement.focus();
|
|
7880
|
+
targetElement.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
7881
|
+
}, 10);
|
|
7882
|
+
} else if (this.options.debug) {
|
|
7883
|
+
this.log("[Metadata] Element not found:", normalizedSelector || targetSelector);
|
|
7884
|
+
}
|
|
7885
|
+
this.emit("metadata:focus", {
|
|
7886
|
+
time: cue.startTime,
|
|
7887
|
+
target: targetSelector,
|
|
7888
|
+
selector: normalizedSelector,
|
|
7889
|
+
element: targetElement,
|
|
7890
|
+
text
|
|
7891
|
+
});
|
|
7892
|
+
if (normalizedSelector) {
|
|
7893
|
+
this.handleMetadataAlert(normalizedSelector, {
|
|
7894
|
+
element: targetElement,
|
|
7895
|
+
reason: "focus"
|
|
7896
|
+
});
|
|
7897
|
+
}
|
|
7898
|
+
}
|
|
7899
|
+
const hashtags = text.match(/#[\w-]+/g);
|
|
7900
|
+
if (hashtags) {
|
|
7901
|
+
if (this.options.debug) {
|
|
7902
|
+
this.log("[Metadata] Hashtags found:", hashtags);
|
|
7903
|
+
}
|
|
7904
|
+
this.emit("metadata:hashtags", {
|
|
7905
|
+
time: cue.startTime,
|
|
7906
|
+
hashtags,
|
|
7907
|
+
text
|
|
7908
|
+
});
|
|
7909
|
+
this.handleMetadataHashtags(hashtags);
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
6499
7912
|
};
|
|
6500
7913
|
Player.instances = [];
|
|
6501
7914
|
|