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.js
CHANGED
|
@@ -520,7 +520,8 @@ var VidPly = (() => {
|
|
|
520
520
|
resizeWindow: "Resize Window",
|
|
521
521
|
styleTranscript: "Open transcript style settings",
|
|
522
522
|
closeMenu: "Close Menu",
|
|
523
|
-
styleTitle: "Transcript Style"
|
|
523
|
+
styleTitle: "Transcript Style",
|
|
524
|
+
autoscroll: "Autoscroll"
|
|
524
525
|
},
|
|
525
526
|
settings: {
|
|
526
527
|
title: "Settings",
|
|
@@ -641,7 +642,8 @@ var VidPly = (() => {
|
|
|
641
642
|
resizeWindow: "Fenster vergr\xF6\xDFern/verkleinern",
|
|
642
643
|
styleTranscript: "Transkript-Stileinstellungen \xF6ffnen",
|
|
643
644
|
closeMenu: "Men\xFC schlie\xDFen",
|
|
644
|
-
styleTitle: "Transkript-Stil"
|
|
645
|
+
styleTitle: "Transkript-Stil",
|
|
646
|
+
autoscroll: "Automatisches Scrollen"
|
|
645
647
|
},
|
|
646
648
|
settings: {
|
|
647
649
|
title: "Einstellungen",
|
|
@@ -762,7 +764,8 @@ var VidPly = (() => {
|
|
|
762
764
|
resizeWindow: "Cambiar tama\xF1o de ventana",
|
|
763
765
|
styleTranscript: "Abrir configuraci\xF3n de estilo de transcripci\xF3n",
|
|
764
766
|
closeMenu: "Cerrar men\xFA",
|
|
765
|
-
styleTitle: "Estilo de Transcripci\xF3n"
|
|
767
|
+
styleTitle: "Estilo de Transcripci\xF3n",
|
|
768
|
+
autoscroll: "Desplazamiento autom\xE1tico"
|
|
766
769
|
},
|
|
767
770
|
settings: {
|
|
768
771
|
title: "Configuraci\xF3n",
|
|
@@ -883,7 +886,8 @@ var VidPly = (() => {
|
|
|
883
886
|
resizeWindow: "Redimensionner la fen\xEAtre",
|
|
884
887
|
styleTranscript: "Ouvrir les param\xE8tres de style de transcription",
|
|
885
888
|
closeMenu: "Fermer le menu",
|
|
886
|
-
styleTitle: "Style de Transcription"
|
|
889
|
+
styleTitle: "Style de Transcription",
|
|
890
|
+
autoscroll: "D\xE9filement automatique"
|
|
887
891
|
},
|
|
888
892
|
settings: {
|
|
889
893
|
title: "Param\xE8tres",
|
|
@@ -1004,7 +1008,8 @@ var VidPly = (() => {
|
|
|
1004
1008
|
resizeWindow: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u30B5\u30A4\u30BA\u5909\u66F4",
|
|
1005
1009
|
styleTranscript: "\u6587\u5B57\u8D77\u3053\u3057\u30B9\u30BF\u30A4\u30EB\u8A2D\u5B9A\u3092\u958B\u304F",
|
|
1006
1010
|
closeMenu: "\u30E1\u30CB\u30E5\u30FC\u3092\u9589\u3058\u308B",
|
|
1007
|
-
styleTitle: "\u6587\u5B57\u8D77\u3053\u3057\u30B9\u30BF\u30A4\u30EB"
|
|
1011
|
+
styleTitle: "\u6587\u5B57\u8D77\u3053\u3057\u30B9\u30BF\u30A4\u30EB",
|
|
1012
|
+
autoscroll: "\u81EA\u52D5\u30B9\u30AF\u30ED\u30FC\u30EB"
|
|
1008
1013
|
},
|
|
1009
1014
|
settings: {
|
|
1010
1015
|
title: "\u8A2D\u5B9A",
|
|
@@ -1183,8 +1188,8 @@ var VidPly = (() => {
|
|
|
1183
1188
|
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"/>`,
|
|
1184
1189
|
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"/>`,
|
|
1185
1190
|
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"/>`,
|
|
1186
|
-
audioDescription: `<rect x="2" y="5" width="20" height="14" rx="2" fill="
|
|
1187
|
-
audioDescriptionOn: `<rect x="2" y="5" width="20" height="14" rx="2" fill="
|
|
1191
|
+
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>`,
|
|
1192
|
+
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>`,
|
|
1188
1193
|
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>`,
|
|
1189
1194
|
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>`,
|
|
1190
1195
|
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"/>`,
|
|
@@ -3341,7 +3346,12 @@ var VidPly = (() => {
|
|
|
3341
3346
|
this.styleDialog = null;
|
|
3342
3347
|
this.styleDialogVisible = false;
|
|
3343
3348
|
this.styleDialogJustOpened = false;
|
|
3349
|
+
this.languageSelector = null;
|
|
3350
|
+
this.currentTranscriptLanguage = null;
|
|
3351
|
+
this.availableTranscriptLanguages = [];
|
|
3352
|
+
this.languageSelectorHandler = null;
|
|
3344
3353
|
const savedPreferences = this.storage.getTranscriptPreferences();
|
|
3354
|
+
this.autoscrollEnabled = (savedPreferences == null ? void 0 : savedPreferences.autoscroll) !== void 0 ? savedPreferences.autoscroll : true;
|
|
3345
3355
|
this.transcriptStyle = {
|
|
3346
3356
|
fontSize: (savedPreferences == null ? void 0 : savedPreferences.fontSize) || this.player.options.transcriptFontSize || "100%",
|
|
3347
3357
|
fontFamily: (savedPreferences == null ? void 0 : savedPreferences.fontFamily) || this.player.options.transcriptFontFamily || "sans-serif",
|
|
@@ -3364,13 +3374,15 @@ var VidPly = (() => {
|
|
|
3364
3374
|
documentClick: null,
|
|
3365
3375
|
styleDialogKeydown: null
|
|
3366
3376
|
};
|
|
3377
|
+
this.timeouts = /* @__PURE__ */ new Set();
|
|
3367
3378
|
this.init();
|
|
3368
3379
|
}
|
|
3369
3380
|
init() {
|
|
3381
|
+
this.setupMetadataHandlingOnLoad();
|
|
3370
3382
|
this.player.on("timeupdate", this.handlers.timeupdate);
|
|
3371
3383
|
this.player.on("fullscreenchange", () => {
|
|
3372
3384
|
if (this.isVisible) {
|
|
3373
|
-
|
|
3385
|
+
this.setManagedTimeout(() => this.positionTranscript(), 100);
|
|
3374
3386
|
}
|
|
3375
3387
|
});
|
|
3376
3388
|
}
|
|
@@ -3391,7 +3403,7 @@ var VidPly = (() => {
|
|
|
3391
3403
|
if (this.transcriptWindow) {
|
|
3392
3404
|
this.transcriptWindow.style.display = "flex";
|
|
3393
3405
|
this.isVisible = true;
|
|
3394
|
-
|
|
3406
|
+
this.setManagedTimeout(() => {
|
|
3395
3407
|
if (this.settingsButton) {
|
|
3396
3408
|
this.settingsButton.focus();
|
|
3397
3409
|
}
|
|
@@ -3402,8 +3414,8 @@ var VidPly = (() => {
|
|
|
3402
3414
|
this.loadTranscriptData();
|
|
3403
3415
|
if (this.transcriptWindow) {
|
|
3404
3416
|
this.transcriptWindow.style.display = "flex";
|
|
3405
|
-
|
|
3406
|
-
|
|
3417
|
+
this.setManagedTimeout(() => this.positionTranscript(), 0);
|
|
3418
|
+
this.setManagedTimeout(() => {
|
|
3407
3419
|
if (this.settingsButton) {
|
|
3408
3420
|
this.settingsButton.focus();
|
|
3409
3421
|
}
|
|
@@ -3480,8 +3492,41 @@ var VidPly = (() => {
|
|
|
3480
3492
|
const title = DOMUtils.createElement("h3", {
|
|
3481
3493
|
textContent: i18n.t("transcript.title")
|
|
3482
3494
|
});
|
|
3495
|
+
const autoscrollLabel = DOMUtils.createElement("label", {
|
|
3496
|
+
className: `${this.player.options.classPrefix}-transcript-autoscroll-label`,
|
|
3497
|
+
attributes: {
|
|
3498
|
+
"title": i18n.t("transcript.autoscroll")
|
|
3499
|
+
}
|
|
3500
|
+
});
|
|
3501
|
+
this.autoscrollCheckbox = DOMUtils.createElement("input", {
|
|
3502
|
+
attributes: {
|
|
3503
|
+
"type": "checkbox",
|
|
3504
|
+
"checked": this.autoscrollEnabled,
|
|
3505
|
+
"aria-label": i18n.t("transcript.autoscroll")
|
|
3506
|
+
}
|
|
3507
|
+
});
|
|
3508
|
+
const autoscrollText = DOMUtils.createElement("span", {
|
|
3509
|
+
textContent: i18n.t("transcript.autoscroll"),
|
|
3510
|
+
className: `${this.player.options.classPrefix}-transcript-autoscroll-text`
|
|
3511
|
+
});
|
|
3512
|
+
autoscrollLabel.appendChild(this.autoscrollCheckbox);
|
|
3513
|
+
autoscrollLabel.appendChild(autoscrollText);
|
|
3514
|
+
this.autoscrollCheckbox.addEventListener("change", (e) => {
|
|
3515
|
+
this.autoscrollEnabled = e.target.checked;
|
|
3516
|
+
this.saveAutoscrollPreference();
|
|
3517
|
+
});
|
|
3483
3518
|
this.headerLeft.appendChild(this.settingsButton);
|
|
3484
3519
|
this.headerLeft.appendChild(title);
|
|
3520
|
+
this.headerLeft.appendChild(autoscrollLabel);
|
|
3521
|
+
this.languageSelector = DOMUtils.createElement("select", {
|
|
3522
|
+
className: `${this.player.options.classPrefix}-transcript-language-select`,
|
|
3523
|
+
attributes: {
|
|
3524
|
+
"aria-label": i18n.t("settings.language") || "Language",
|
|
3525
|
+
"style": "display: none;"
|
|
3526
|
+
// Hidden until we detect multiple languages
|
|
3527
|
+
}
|
|
3528
|
+
});
|
|
3529
|
+
this.headerLeft.appendChild(this.languageSelector);
|
|
3485
3530
|
const closeButton = DOMUtils.createElement("button", {
|
|
3486
3531
|
className: `${this.player.options.classPrefix}-transcript-close`,
|
|
3487
3532
|
attributes: {
|
|
@@ -3524,8 +3569,10 @@ var VidPly = (() => {
|
|
|
3524
3569
|
this.documentClickHandlerAdded = false;
|
|
3525
3570
|
let resizeTimeout;
|
|
3526
3571
|
this.handlers.resize = () => {
|
|
3527
|
-
|
|
3528
|
-
|
|
3572
|
+
if (resizeTimeout) {
|
|
3573
|
+
this.clearManagedTimeout(resizeTimeout);
|
|
3574
|
+
}
|
|
3575
|
+
resizeTimeout = this.setManagedTimeout(() => this.positionTranscript(), 100);
|
|
3529
3576
|
};
|
|
3530
3577
|
window.addEventListener("resize", this.handlers.resize);
|
|
3531
3578
|
}
|
|
@@ -3595,17 +3642,94 @@ var VidPly = (() => {
|
|
|
3595
3642
|
}
|
|
3596
3643
|
}
|
|
3597
3644
|
}
|
|
3645
|
+
/**
|
|
3646
|
+
* Get available transcript languages from tracks
|
|
3647
|
+
*/
|
|
3648
|
+
getAvailableTranscriptLanguages() {
|
|
3649
|
+
const textTracks = this.player.textTracks;
|
|
3650
|
+
const languages = /* @__PURE__ */ new Map();
|
|
3651
|
+
textTracks.forEach((track) => {
|
|
3652
|
+
if ((track.kind === "captions" || track.kind === "subtitles") && track.language) {
|
|
3653
|
+
if (!languages.has(track.language)) {
|
|
3654
|
+
languages.set(track.language, {
|
|
3655
|
+
language: track.language,
|
|
3656
|
+
label: track.label || track.language,
|
|
3657
|
+
track
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
});
|
|
3662
|
+
return Array.from(languages.values());
|
|
3663
|
+
}
|
|
3664
|
+
/**
|
|
3665
|
+
* Update language selector dropdown
|
|
3666
|
+
*/
|
|
3667
|
+
updateLanguageSelector() {
|
|
3668
|
+
if (!this.languageSelector) return;
|
|
3669
|
+
this.availableTranscriptLanguages = this.getAvailableTranscriptLanguages();
|
|
3670
|
+
this.languageSelector.innerHTML = "";
|
|
3671
|
+
if (this.availableTranscriptLanguages.length < 2) {
|
|
3672
|
+
this.languageSelector.style.display = "none";
|
|
3673
|
+
return;
|
|
3674
|
+
}
|
|
3675
|
+
this.languageSelector.style.display = "block";
|
|
3676
|
+
this.availableTranscriptLanguages.forEach((langInfo, index) => {
|
|
3677
|
+
const option = DOMUtils.createElement("option", {
|
|
3678
|
+
textContent: langInfo.label,
|
|
3679
|
+
attributes: {
|
|
3680
|
+
"value": langInfo.language
|
|
3681
|
+
}
|
|
3682
|
+
});
|
|
3683
|
+
this.languageSelector.appendChild(option);
|
|
3684
|
+
});
|
|
3685
|
+
if (this.currentTranscriptLanguage) {
|
|
3686
|
+
this.languageSelector.value = this.currentTranscriptLanguage;
|
|
3687
|
+
} else if (this.availableTranscriptLanguages.length > 0) {
|
|
3688
|
+
const activeTrack = this.player.textTracks.find(
|
|
3689
|
+
(track) => (track.kind === "captions" || track.kind === "subtitles") && track.mode === "showing"
|
|
3690
|
+
);
|
|
3691
|
+
this.currentTranscriptLanguage = activeTrack ? activeTrack.language : this.availableTranscriptLanguages[0].language;
|
|
3692
|
+
this.languageSelector.value = this.currentTranscriptLanguage;
|
|
3693
|
+
}
|
|
3694
|
+
if (this.languageSelectorHandler) {
|
|
3695
|
+
this.languageSelector.removeEventListener("change", this.languageSelectorHandler);
|
|
3696
|
+
}
|
|
3697
|
+
this.languageSelectorHandler = (e) => {
|
|
3698
|
+
this.currentTranscriptLanguage = e.target.value;
|
|
3699
|
+
this.loadTranscriptData();
|
|
3700
|
+
};
|
|
3701
|
+
this.languageSelector.addEventListener("change", this.languageSelectorHandler);
|
|
3702
|
+
}
|
|
3598
3703
|
/**
|
|
3599
3704
|
* Load transcript data from caption/subtitle tracks
|
|
3600
3705
|
*/
|
|
3601
3706
|
loadTranscriptData() {
|
|
3602
3707
|
this.transcriptEntries = [];
|
|
3603
3708
|
this.transcriptContent.innerHTML = "";
|
|
3604
|
-
const textTracks =
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3709
|
+
const textTracks = this.player.textTracks;
|
|
3710
|
+
let captionTrack = null;
|
|
3711
|
+
if (this.currentTranscriptLanguage) {
|
|
3712
|
+
captionTrack = textTracks.find(
|
|
3713
|
+
(track) => (track.kind === "captions" || track.kind === "subtitles") && track.language === this.currentTranscriptLanguage
|
|
3714
|
+
);
|
|
3715
|
+
}
|
|
3716
|
+
if (!captionTrack) {
|
|
3717
|
+
captionTrack = textTracks.find(
|
|
3718
|
+
(track) => track.kind === "captions" || track.kind === "subtitles"
|
|
3719
|
+
);
|
|
3720
|
+
if (captionTrack) {
|
|
3721
|
+
this.currentTranscriptLanguage = captionTrack.language;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
let descriptionTrack = null;
|
|
3725
|
+
if (this.currentTranscriptLanguage) {
|
|
3726
|
+
descriptionTrack = textTracks.find(
|
|
3727
|
+
(track) => track.kind === "descriptions" && track.language === this.currentTranscriptLanguage
|
|
3728
|
+
);
|
|
3729
|
+
}
|
|
3730
|
+
if (!descriptionTrack) {
|
|
3731
|
+
descriptionTrack = textTracks.find((track) => track.kind === "descriptions");
|
|
3732
|
+
}
|
|
3609
3733
|
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
3610
3734
|
if (!captionTrack && !descriptionTrack && !metadataTrack) {
|
|
3611
3735
|
this.showNoTranscriptMessage();
|
|
@@ -3634,7 +3758,7 @@ var VidPly = (() => {
|
|
|
3634
3758
|
tracksToLoad.forEach((track) => {
|
|
3635
3759
|
track.addEventListener("load", onLoad, { once: true });
|
|
3636
3760
|
});
|
|
3637
|
-
|
|
3761
|
+
this.setManagedTimeout(() => {
|
|
3638
3762
|
this.loadTranscriptData();
|
|
3639
3763
|
}, 500);
|
|
3640
3764
|
return;
|
|
@@ -3667,24 +3791,61 @@ var VidPly = (() => {
|
|
|
3667
3791
|
this.transcriptContent.appendChild(entry);
|
|
3668
3792
|
});
|
|
3669
3793
|
this.applyTranscriptStyles();
|
|
3794
|
+
this.updateLanguageSelector();
|
|
3795
|
+
}
|
|
3796
|
+
/**
|
|
3797
|
+
* Setup metadata handling on player load
|
|
3798
|
+
* This runs independently of transcript loading
|
|
3799
|
+
*/
|
|
3800
|
+
setupMetadataHandlingOnLoad() {
|
|
3801
|
+
const setupMetadata = () => {
|
|
3802
|
+
const textTracks = this.player.textTracks;
|
|
3803
|
+
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
3804
|
+
if (metadataTrack) {
|
|
3805
|
+
if (metadataTrack.mode === "disabled") {
|
|
3806
|
+
metadataTrack.mode = "hidden";
|
|
3807
|
+
}
|
|
3808
|
+
if (this.metadataCueChangeHandler) {
|
|
3809
|
+
metadataTrack.removeEventListener("cuechange", this.metadataCueChangeHandler);
|
|
3810
|
+
}
|
|
3811
|
+
this.metadataCueChangeHandler = () => {
|
|
3812
|
+
const activeCues = Array.from(metadataTrack.activeCues || []);
|
|
3813
|
+
if (activeCues.length > 0) {
|
|
3814
|
+
if (this.player.options.debug) {
|
|
3815
|
+
console.log("[VidPly Metadata] Active cues:", activeCues.map((c) => ({
|
|
3816
|
+
start: c.startTime,
|
|
3817
|
+
end: c.endTime,
|
|
3818
|
+
text: c.text
|
|
3819
|
+
})));
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
activeCues.forEach((cue) => {
|
|
3823
|
+
this.handleMetadataCue(cue);
|
|
3824
|
+
});
|
|
3825
|
+
};
|
|
3826
|
+
metadataTrack.addEventListener("cuechange", this.metadataCueChangeHandler);
|
|
3827
|
+
if (this.player.options.debug) {
|
|
3828
|
+
const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;
|
|
3829
|
+
console.log("[VidPly Metadata] Track enabled,", cueCount, "cues available");
|
|
3830
|
+
}
|
|
3831
|
+
} else if (this.player.options.debug) {
|
|
3832
|
+
console.warn("[VidPly Metadata] No metadata track found");
|
|
3833
|
+
}
|
|
3834
|
+
};
|
|
3835
|
+
setupMetadata();
|
|
3836
|
+
this.player.on("loadedmetadata", setupMetadata);
|
|
3670
3837
|
}
|
|
3671
3838
|
/**
|
|
3672
3839
|
* Setup metadata handling
|
|
3673
3840
|
* Metadata cues are not displayed but can be used programmatically
|
|
3841
|
+
* This is called when transcript data is loaded (for storing cues)
|
|
3674
3842
|
*/
|
|
3675
3843
|
setupMetadataHandling() {
|
|
3676
3844
|
if (!this.metadataCues || this.metadataCues.length === 0) {
|
|
3677
3845
|
return;
|
|
3678
3846
|
}
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
if (metadataTrack) {
|
|
3682
|
-
metadataTrack.addEventListener("cuechange", () => {
|
|
3683
|
-
const activeCues = Array.from(metadataTrack.activeCues || []);
|
|
3684
|
-
activeCues.forEach((cue) => {
|
|
3685
|
-
this.handleMetadataCue(cue);
|
|
3686
|
-
});
|
|
3687
|
-
});
|
|
3847
|
+
if (this.player.options.debug) {
|
|
3848
|
+
console.log("[VidPly Metadata]", this.metadataCues.length, "cues stored from transcript load");
|
|
3688
3849
|
}
|
|
3689
3850
|
}
|
|
3690
3851
|
/**
|
|
@@ -3693,6 +3854,12 @@ var VidPly = (() => {
|
|
|
3693
3854
|
*/
|
|
3694
3855
|
handleMetadataCue(cue) {
|
|
3695
3856
|
const text = cue.text.trim();
|
|
3857
|
+
if (this.player.options.debug) {
|
|
3858
|
+
console.log("[VidPly Metadata] Processing cue:", {
|
|
3859
|
+
time: cue.startTime,
|
|
3860
|
+
text
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3696
3863
|
this.player.emit("metadata", {
|
|
3697
3864
|
time: cue.startTime,
|
|
3698
3865
|
endTime: cue.endTime,
|
|
@@ -3700,18 +3867,40 @@ var VidPly = (() => {
|
|
|
3700
3867
|
cue
|
|
3701
3868
|
});
|
|
3702
3869
|
if (text.includes("PAUSE")) {
|
|
3870
|
+
if (!this.player.state.paused) {
|
|
3871
|
+
if (this.player.options.debug) {
|
|
3872
|
+
console.log("[VidPly Metadata] Pausing video at", cue.startTime);
|
|
3873
|
+
}
|
|
3874
|
+
this.player.pause();
|
|
3875
|
+
}
|
|
3703
3876
|
this.player.emit("metadata:pause", { time: cue.startTime, text });
|
|
3704
3877
|
}
|
|
3705
3878
|
const focusMatch = text.match(/FOCUS:([\w#-]+)/);
|
|
3706
3879
|
if (focusMatch) {
|
|
3880
|
+
const targetSelector = focusMatch[1];
|
|
3881
|
+
const targetElement = document.querySelector(targetSelector);
|
|
3882
|
+
if (targetElement) {
|
|
3883
|
+
if (this.player.options.debug) {
|
|
3884
|
+
console.log("[VidPly Metadata] Focusing element:", targetSelector);
|
|
3885
|
+
}
|
|
3886
|
+
this.setManagedTimeout(() => {
|
|
3887
|
+
targetElement.focus();
|
|
3888
|
+
}, 10);
|
|
3889
|
+
} else if (this.player.options.debug) {
|
|
3890
|
+
console.warn("[VidPly Metadata] Element not found:", targetSelector);
|
|
3891
|
+
}
|
|
3707
3892
|
this.player.emit("metadata:focus", {
|
|
3708
3893
|
time: cue.startTime,
|
|
3709
|
-
target:
|
|
3894
|
+
target: targetSelector,
|
|
3895
|
+
element: targetElement,
|
|
3710
3896
|
text
|
|
3711
3897
|
});
|
|
3712
3898
|
}
|
|
3713
3899
|
const hashtags = text.match(/#[\w-]+/g);
|
|
3714
3900
|
if (hashtags) {
|
|
3901
|
+
if (this.player.options.debug) {
|
|
3902
|
+
console.log("[VidPly Metadata] Hashtags found:", hashtags);
|
|
3903
|
+
}
|
|
3715
3904
|
this.player.emit("metadata:hashtags", {
|
|
3716
3905
|
time: cue.startTime,
|
|
3717
3906
|
hashtags,
|
|
@@ -3805,7 +3994,7 @@ var VidPly = (() => {
|
|
|
3805
3994
|
* Scroll transcript window to show active entry
|
|
3806
3995
|
*/
|
|
3807
3996
|
scrollToEntry(entryElement) {
|
|
3808
|
-
if (!this.transcriptContent) return;
|
|
3997
|
+
if (!this.transcriptContent || !this.autoscrollEnabled) return;
|
|
3809
3998
|
const contentRect = this.transcriptContent.getBoundingClientRect();
|
|
3810
3999
|
const entryRect = entryElement.getBoundingClientRect();
|
|
3811
4000
|
if (entryRect.top < contentRect.top || entryRect.bottom > contentRect.bottom) {
|
|
@@ -3816,6 +4005,14 @@ var VidPly = (() => {
|
|
|
3816
4005
|
});
|
|
3817
4006
|
}
|
|
3818
4007
|
}
|
|
4008
|
+
/**
|
|
4009
|
+
* Save autoscroll preference to localStorage
|
|
4010
|
+
*/
|
|
4011
|
+
saveAutoscrollPreference() {
|
|
4012
|
+
const savedPreferences = this.storage.getTranscriptPreferences() || {};
|
|
4013
|
+
savedPreferences.autoscroll = this.autoscrollEnabled;
|
|
4014
|
+
this.storage.saveTranscriptPreferences(savedPreferences);
|
|
4015
|
+
}
|
|
3819
4016
|
/**
|
|
3820
4017
|
* Setup drag and drop functionality
|
|
3821
4018
|
*/
|
|
@@ -3828,6 +4025,9 @@ var VidPly = (() => {
|
|
|
3828
4025
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings`)) {
|
|
3829
4026
|
return;
|
|
3830
4027
|
}
|
|
4028
|
+
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-language-select`)) {
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
3831
4031
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings-menu`)) {
|
|
3832
4032
|
return;
|
|
3833
4033
|
}
|
|
@@ -3854,6 +4054,9 @@ var VidPly = (() => {
|
|
|
3854
4054
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings`)) {
|
|
3855
4055
|
return;
|
|
3856
4056
|
}
|
|
4057
|
+
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-language-select`)) {
|
|
4058
|
+
return;
|
|
4059
|
+
}
|
|
3857
4060
|
if (e.target.closest(`.${this.player.options.classPrefix}-transcript-settings-menu`)) {
|
|
3858
4061
|
return;
|
|
3859
4062
|
}
|
|
@@ -4608,6 +4811,30 @@ var VidPly = (() => {
|
|
|
4608
4811
|
entry.style.fontFamily = this.transcriptStyle.fontFamily;
|
|
4609
4812
|
});
|
|
4610
4813
|
}
|
|
4814
|
+
/**
|
|
4815
|
+
* Set a managed timeout that will be cleaned up on destroy
|
|
4816
|
+
* @param {Function} callback - Callback function
|
|
4817
|
+
* @param {number} delay - Delay in milliseconds
|
|
4818
|
+
* @returns {number} Timeout ID
|
|
4819
|
+
*/
|
|
4820
|
+
setManagedTimeout(callback, delay) {
|
|
4821
|
+
const timeoutId = setTimeout(() => {
|
|
4822
|
+
this.timeouts.delete(timeoutId);
|
|
4823
|
+
callback();
|
|
4824
|
+
}, delay);
|
|
4825
|
+
this.timeouts.add(timeoutId);
|
|
4826
|
+
return timeoutId;
|
|
4827
|
+
}
|
|
4828
|
+
/**
|
|
4829
|
+
* Clear a managed timeout
|
|
4830
|
+
* @param {number} timeoutId - Timeout ID to clear
|
|
4831
|
+
*/
|
|
4832
|
+
clearManagedTimeout(timeoutId) {
|
|
4833
|
+
if (timeoutId) {
|
|
4834
|
+
clearTimeout(timeoutId);
|
|
4835
|
+
this.timeouts.delete(timeoutId);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4611
4838
|
/**
|
|
4612
4839
|
* Cleanup
|
|
4613
4840
|
*/
|
|
@@ -4661,6 +4888,8 @@ var VidPly = (() => {
|
|
|
4661
4888
|
if (this.handlers.resize) {
|
|
4662
4889
|
window.removeEventListener("resize", this.handlers.resize);
|
|
4663
4890
|
}
|
|
4891
|
+
this.timeouts.forEach((timeoutId) => clearTimeout(timeoutId));
|
|
4892
|
+
this.timeouts.clear();
|
|
4664
4893
|
this.handlers = null;
|
|
4665
4894
|
if (this.transcriptWindow && this.transcriptWindow.parentNode) {
|
|
4666
4895
|
this.transcriptWindow.parentNode.removeChild(this.transcriptWindow);
|
|
@@ -5332,7 +5561,7 @@ var VidPly = (() => {
|
|
|
5332
5561
|
};
|
|
5333
5562
|
|
|
5334
5563
|
// src/core/Player.js
|
|
5335
|
-
var Player = class extends EventEmitter {
|
|
5564
|
+
var Player = class _Player extends EventEmitter {
|
|
5336
5565
|
constructor(element, options = {}) {
|
|
5337
5566
|
super();
|
|
5338
5567
|
this.element = typeof element === "string" ? document.querySelector(element) : element;
|
|
@@ -5440,6 +5669,8 @@ var VidPly = (() => {
|
|
|
5440
5669
|
screenReaderAnnouncements: true,
|
|
5441
5670
|
highContrast: false,
|
|
5442
5671
|
focusHighlight: true,
|
|
5672
|
+
metadataAlerts: {},
|
|
5673
|
+
metadataHashtags: {},
|
|
5443
5674
|
// Languages
|
|
5444
5675
|
language: "en",
|
|
5445
5676
|
languages: ["en"],
|
|
@@ -5458,6 +5689,8 @@ var VidPly = (() => {
|
|
|
5458
5689
|
onError: null,
|
|
5459
5690
|
...options
|
|
5460
5691
|
};
|
|
5692
|
+
this.options.metadataAlerts = this.options.metadataAlerts || {};
|
|
5693
|
+
this.options.metadataHashtags = this.options.metadataHashtags || {};
|
|
5461
5694
|
this.storage = new StorageManager("vidply");
|
|
5462
5695
|
const savedPrefs = this.storage.getPlayerPreferences();
|
|
5463
5696
|
if (savedPrefs) {
|
|
@@ -5489,12 +5722,24 @@ var VidPly = (() => {
|
|
|
5489
5722
|
this.audioDescriptionSrc = this.options.audioDescriptionSrc;
|
|
5490
5723
|
this.signLanguageSrc = this.options.signLanguageSrc;
|
|
5491
5724
|
this.signLanguageVideo = null;
|
|
5725
|
+
this.audioDescriptionSourceElement = null;
|
|
5726
|
+
this.originalAudioDescriptionSource = null;
|
|
5727
|
+
this.audioDescriptionCaptionTracks = [];
|
|
5728
|
+
this._textTracksCache = null;
|
|
5729
|
+
this._textTracksDirty = true;
|
|
5730
|
+
this._sourceElementsCache = null;
|
|
5731
|
+
this._sourceElementsDirty = true;
|
|
5732
|
+
this._trackElementsCache = null;
|
|
5733
|
+
this._trackElementsDirty = true;
|
|
5734
|
+
this.timeouts = /* @__PURE__ */ new Set();
|
|
5492
5735
|
this.container = null;
|
|
5493
5736
|
this.renderer = null;
|
|
5494
5737
|
this.controlBar = null;
|
|
5495
5738
|
this.captionManager = null;
|
|
5496
5739
|
this.keyboardManager = null;
|
|
5497
5740
|
this.settingsDialog = null;
|
|
5741
|
+
this.metadataCueChangeHandler = null;
|
|
5742
|
+
this.metadataAlertHandlers = /* @__PURE__ */ new Map();
|
|
5498
5743
|
this.init();
|
|
5499
5744
|
}
|
|
5500
5745
|
async init() {
|
|
@@ -5526,6 +5771,7 @@ var VidPly = (() => {
|
|
|
5526
5771
|
if (this.options.transcript || this.options.transcriptButton) {
|
|
5527
5772
|
this.transcriptManager = new TranscriptManager(this);
|
|
5528
5773
|
}
|
|
5774
|
+
this.setupMetadataHandling();
|
|
5529
5775
|
if (this.options.keyboard) {
|
|
5530
5776
|
this.keyboardManager = new KeyboardManager(this);
|
|
5531
5777
|
}
|
|
@@ -5611,6 +5857,8 @@ var VidPly = (() => {
|
|
|
5611
5857
|
if (this.element.tagName === "VIDEO") {
|
|
5612
5858
|
this.createPlayButtonOverlay();
|
|
5613
5859
|
}
|
|
5860
|
+
this.element.vidply = this;
|
|
5861
|
+
_Player.instances.push(this);
|
|
5614
5862
|
this.element.style.cursor = "pointer";
|
|
5615
5863
|
this.element.addEventListener("click", (e) => {
|
|
5616
5864
|
if (e.target === this.element) {
|
|
@@ -5643,6 +5891,53 @@ var VidPly = (() => {
|
|
|
5643
5891
|
if (!src) {
|
|
5644
5892
|
throw new Error("No media source found");
|
|
5645
5893
|
}
|
|
5894
|
+
const sourceElements = this.sourceElements;
|
|
5895
|
+
for (const sourceEl of sourceElements) {
|
|
5896
|
+
const descSrc = sourceEl.getAttribute("data-desc-src");
|
|
5897
|
+
const origSrc = sourceEl.getAttribute("data-orig-src");
|
|
5898
|
+
if (descSrc || origSrc) {
|
|
5899
|
+
if (!this.audioDescriptionSourceElement) {
|
|
5900
|
+
this.audioDescriptionSourceElement = sourceEl;
|
|
5901
|
+
}
|
|
5902
|
+
if (origSrc) {
|
|
5903
|
+
if (!this.originalAudioDescriptionSource) {
|
|
5904
|
+
this.originalAudioDescriptionSource = origSrc;
|
|
5905
|
+
}
|
|
5906
|
+
if (!this.originalSrc) {
|
|
5907
|
+
this.originalSrc = origSrc;
|
|
5908
|
+
}
|
|
5909
|
+
} else {
|
|
5910
|
+
const currentSrcAttr = sourceEl.getAttribute("src");
|
|
5911
|
+
if (!this.originalAudioDescriptionSource && currentSrcAttr) {
|
|
5912
|
+
this.originalAudioDescriptionSource = currentSrcAttr;
|
|
5913
|
+
}
|
|
5914
|
+
if (!this.originalSrc && currentSrcAttr) {
|
|
5915
|
+
this.originalSrc = currentSrcAttr;
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
if (descSrc && !this.audioDescriptionSrc) {
|
|
5919
|
+
this.audioDescriptionSrc = descSrc;
|
|
5920
|
+
}
|
|
5921
|
+
}
|
|
5922
|
+
}
|
|
5923
|
+
const trackElements = this.trackElements;
|
|
5924
|
+
trackElements.forEach((trackEl) => {
|
|
5925
|
+
const trackKind = trackEl.getAttribute("kind");
|
|
5926
|
+
const trackDescSrc = trackEl.getAttribute("data-desc-src");
|
|
5927
|
+
if (trackKind === "captions" || trackKind === "subtitles" || trackKind === "chapters") {
|
|
5928
|
+
if (trackDescSrc) {
|
|
5929
|
+
this.audioDescriptionCaptionTracks.push({
|
|
5930
|
+
trackElement: trackEl,
|
|
5931
|
+
originalSrc: trackEl.getAttribute("src"),
|
|
5932
|
+
describedSrc: trackDescSrc,
|
|
5933
|
+
originalTrackSrc: trackEl.getAttribute("data-orig-src") || trackEl.getAttribute("src"),
|
|
5934
|
+
explicit: true
|
|
5935
|
+
// Explicitly defined, so we should validate it
|
|
5936
|
+
});
|
|
5937
|
+
this.log(`Found explicit described ${trackKind} track: ${trackEl.getAttribute("src")} -> ${trackDescSrc}`);
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
});
|
|
5646
5941
|
if (!this.originalSrc) {
|
|
5647
5942
|
this.originalSrc = src;
|
|
5648
5943
|
}
|
|
@@ -5659,6 +5954,106 @@ var VidPly = (() => {
|
|
|
5659
5954
|
this.log(`Using ${renderer.name} renderer`);
|
|
5660
5955
|
this.renderer = new renderer(this);
|
|
5661
5956
|
await this.renderer.init();
|
|
5957
|
+
this.invalidateTrackCache();
|
|
5958
|
+
}
|
|
5959
|
+
/**
|
|
5960
|
+
* Get cached text tracks array
|
|
5961
|
+
* @returns {Array} Array of text tracks
|
|
5962
|
+
*/
|
|
5963
|
+
get textTracks() {
|
|
5964
|
+
if (!this._textTracksCache || this._textTracksDirty) {
|
|
5965
|
+
this._textTracksCache = Array.from(this.element.textTracks || []);
|
|
5966
|
+
this._textTracksDirty = false;
|
|
5967
|
+
}
|
|
5968
|
+
return this._textTracksCache;
|
|
5969
|
+
}
|
|
5970
|
+
/**
|
|
5971
|
+
* Get cached source elements array
|
|
5972
|
+
* @returns {Array} Array of source elements
|
|
5973
|
+
*/
|
|
5974
|
+
get sourceElements() {
|
|
5975
|
+
if (!this._sourceElementsCache || this._sourceElementsDirty) {
|
|
5976
|
+
this._sourceElementsCache = Array.from(this.element.querySelectorAll("source"));
|
|
5977
|
+
this._sourceElementsDirty = false;
|
|
5978
|
+
}
|
|
5979
|
+
return this._sourceElementsCache;
|
|
5980
|
+
}
|
|
5981
|
+
/**
|
|
5982
|
+
* Get cached track elements array
|
|
5983
|
+
* @returns {Array} Array of track elements
|
|
5984
|
+
*/
|
|
5985
|
+
get trackElements() {
|
|
5986
|
+
if (!this._trackElementsCache || this._trackElementsDirty) {
|
|
5987
|
+
this._trackElementsCache = Array.from(this.element.querySelectorAll("track"));
|
|
5988
|
+
this._trackElementsDirty = false;
|
|
5989
|
+
}
|
|
5990
|
+
return this._trackElementsCache;
|
|
5991
|
+
}
|
|
5992
|
+
/**
|
|
5993
|
+
* Invalidate DOM query cache (call when tracks/sources change)
|
|
5994
|
+
*/
|
|
5995
|
+
invalidateTrackCache() {
|
|
5996
|
+
this._textTracksDirty = true;
|
|
5997
|
+
this._trackElementsDirty = true;
|
|
5998
|
+
this._sourceElementsDirty = true;
|
|
5999
|
+
}
|
|
6000
|
+
/**
|
|
6001
|
+
* Find a text track by kind and optionally language
|
|
6002
|
+
* @param {string} kind - Track kind (captions, subtitles, descriptions, chapters, metadata)
|
|
6003
|
+
* @param {string} [language] - Optional language code
|
|
6004
|
+
* @returns {TextTrack|null} Found track or null
|
|
6005
|
+
*/
|
|
6006
|
+
findTextTrack(kind, language = null) {
|
|
6007
|
+
const tracks = this.textTracks;
|
|
6008
|
+
if (language) {
|
|
6009
|
+
return tracks.find((t) => t.kind === kind && t.language === language);
|
|
6010
|
+
}
|
|
6011
|
+
return tracks.find((t) => t.kind === kind);
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Find a source element by attribute
|
|
6015
|
+
* @param {string} attribute - Attribute name (e.g., 'data-desc-src')
|
|
6016
|
+
* @param {string} [value] - Optional attribute value
|
|
6017
|
+
* @returns {Element|null} Found source element or null
|
|
6018
|
+
*/
|
|
6019
|
+
findSourceElement(attribute, value = null) {
|
|
6020
|
+
const sources = this.sourceElements;
|
|
6021
|
+
if (value) {
|
|
6022
|
+
return sources.find((el) => el.getAttribute(attribute) === value);
|
|
6023
|
+
}
|
|
6024
|
+
return sources.find((el) => el.hasAttribute(attribute));
|
|
6025
|
+
}
|
|
6026
|
+
/**
|
|
6027
|
+
* Find a track element by its associated TextTrack
|
|
6028
|
+
* @param {TextTrack} track - The TextTrack object
|
|
6029
|
+
* @returns {Element|null} Found track element or null
|
|
6030
|
+
*/
|
|
6031
|
+
findTrackElement(track) {
|
|
6032
|
+
return this.trackElements.find((el) => el.track === track);
|
|
6033
|
+
}
|
|
6034
|
+
/**
|
|
6035
|
+
* Set a managed timeout that will be cleaned up on destroy
|
|
6036
|
+
* @param {Function} callback - Callback function
|
|
6037
|
+
* @param {number} delay - Delay in milliseconds
|
|
6038
|
+
* @returns {number} Timeout ID
|
|
6039
|
+
*/
|
|
6040
|
+
setManagedTimeout(callback, delay) {
|
|
6041
|
+
const timeoutId = setTimeout(() => {
|
|
6042
|
+
this.timeouts.delete(timeoutId);
|
|
6043
|
+
callback();
|
|
6044
|
+
}, delay);
|
|
6045
|
+
this.timeouts.add(timeoutId);
|
|
6046
|
+
return timeoutId;
|
|
6047
|
+
}
|
|
6048
|
+
/**
|
|
6049
|
+
* Clear a managed timeout
|
|
6050
|
+
* @param {number} timeoutId - Timeout ID to clear
|
|
6051
|
+
*/
|
|
6052
|
+
clearManagedTimeout(timeoutId) {
|
|
6053
|
+
if (timeoutId) {
|
|
6054
|
+
clearTimeout(timeoutId);
|
|
6055
|
+
this.timeouts.delete(timeoutId);
|
|
6056
|
+
}
|
|
5662
6057
|
}
|
|
5663
6058
|
/**
|
|
5664
6059
|
* Load new media source (for playlists)
|
|
@@ -5674,8 +6069,9 @@ var VidPly = (() => {
|
|
|
5674
6069
|
if (this.renderer) {
|
|
5675
6070
|
this.pause();
|
|
5676
6071
|
}
|
|
5677
|
-
const existingTracks = this.
|
|
6072
|
+
const existingTracks = this.trackElements;
|
|
5678
6073
|
existingTracks.forEach((track) => track.remove());
|
|
6074
|
+
this.invalidateTrackCache();
|
|
5679
6075
|
this.element.src = config.src;
|
|
5680
6076
|
if (config.type) {
|
|
5681
6077
|
this.element.type = config.type;
|
|
@@ -5695,6 +6091,7 @@ var VidPly = (() => {
|
|
|
5695
6091
|
}
|
|
5696
6092
|
this.element.appendChild(track);
|
|
5697
6093
|
});
|
|
6094
|
+
this.invalidateTrackCache();
|
|
5698
6095
|
}
|
|
5699
6096
|
const shouldChangeRenderer = this.shouldChangeRenderer(config.src);
|
|
5700
6097
|
if (shouldChangeRenderer && this.renderer) {
|
|
@@ -5927,15 +6324,398 @@ var VidPly = (() => {
|
|
|
5927
6324
|
this.enableCaptions();
|
|
5928
6325
|
}
|
|
5929
6326
|
}
|
|
6327
|
+
/**
|
|
6328
|
+
* Check if a track file exists
|
|
6329
|
+
* @param {string} url - Track file URL
|
|
6330
|
+
* @returns {Promise<boolean>} - True if file exists
|
|
6331
|
+
*/
|
|
6332
|
+
async validateTrackExists(url) {
|
|
6333
|
+
try {
|
|
6334
|
+
const response = await fetch(url, { method: "HEAD", cache: "no-cache" });
|
|
6335
|
+
return response.ok;
|
|
6336
|
+
} catch (error) {
|
|
6337
|
+
return false;
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
5930
6340
|
// Audio Description
|
|
5931
6341
|
async enableAudioDescription() {
|
|
5932
|
-
|
|
5933
|
-
|
|
6342
|
+
const hasSourceElementsWithDesc = this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6343
|
+
const hasTracksWithDesc = this.audioDescriptionCaptionTracks.length > 0;
|
|
6344
|
+
if (!this.audioDescriptionSrc && !hasSourceElementsWithDesc && !hasTracksWithDesc) {
|
|
6345
|
+
console.warn("VidPly: No audio description source, source elements, or tracks provided");
|
|
5934
6346
|
return;
|
|
5935
6347
|
}
|
|
5936
6348
|
const currentTime = this.state.currentTime;
|
|
5937
6349
|
const wasPlaying = this.state.playing;
|
|
5938
|
-
|
|
6350
|
+
let swappedTracksForTranscript = [];
|
|
6351
|
+
if (this.audioDescriptionSourceElement) {
|
|
6352
|
+
const currentSrc = this.element.currentSrc || this.element.src;
|
|
6353
|
+
const sourceElements = this.sourceElements;
|
|
6354
|
+
let sourceElementToUpdate = null;
|
|
6355
|
+
let descSrc = this.audioDescriptionSrc;
|
|
6356
|
+
for (const sourceEl of sourceElements) {
|
|
6357
|
+
const sourceSrc = sourceEl.getAttribute("src");
|
|
6358
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6359
|
+
const sourceFilename = sourceSrc ? sourceSrc.split("/").pop() : "";
|
|
6360
|
+
const currentFilename = currentSrc ? currentSrc.split("/").pop() : "";
|
|
6361
|
+
if (currentSrc && (currentSrc === sourceSrc || currentSrc.includes(sourceSrc) || currentSrc.includes(sourceFilename) || sourceFilename && currentFilename === sourceFilename)) {
|
|
6362
|
+
sourceElementToUpdate = sourceEl;
|
|
6363
|
+
if (descSrcAttr) {
|
|
6364
|
+
descSrc = descSrcAttr;
|
|
6365
|
+
} else if (sourceSrc) {
|
|
6366
|
+
descSrc = this.audioDescriptionSrc || descSrc;
|
|
6367
|
+
}
|
|
6368
|
+
break;
|
|
6369
|
+
}
|
|
6370
|
+
}
|
|
6371
|
+
if (!sourceElementToUpdate) {
|
|
6372
|
+
sourceElementToUpdate = this.audioDescriptionSourceElement;
|
|
6373
|
+
const storedDescSrc = sourceElementToUpdate.getAttribute("data-desc-src");
|
|
6374
|
+
if (storedDescSrc) {
|
|
6375
|
+
descSrc = storedDescSrc;
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
if (this.audioDescriptionCaptionTracks.length > 0) {
|
|
6379
|
+
const validationPromises = this.audioDescriptionCaptionTracks.map(async (trackInfo) => {
|
|
6380
|
+
if (trackInfo.trackElement && trackInfo.describedSrc) {
|
|
6381
|
+
if (trackInfo.explicit === true) {
|
|
6382
|
+
try {
|
|
6383
|
+
const exists = await this.validateTrackExists(trackInfo.describedSrc);
|
|
6384
|
+
return { trackInfo, exists };
|
|
6385
|
+
} catch (error) {
|
|
6386
|
+
return { trackInfo, exists: false };
|
|
6387
|
+
}
|
|
6388
|
+
} else {
|
|
6389
|
+
return { trackInfo, exists: false };
|
|
6390
|
+
}
|
|
6391
|
+
}
|
|
6392
|
+
return { trackInfo, exists: false };
|
|
6393
|
+
});
|
|
6394
|
+
const validationResults = await Promise.all(validationPromises);
|
|
6395
|
+
const tracksToSwap = validationResults.filter((result) => result.exists);
|
|
6396
|
+
if (tracksToSwap.length > 0) {
|
|
6397
|
+
const trackModes = /* @__PURE__ */ new Map();
|
|
6398
|
+
tracksToSwap.forEach(({ trackInfo }) => {
|
|
6399
|
+
const textTrack = trackInfo.trackElement.track;
|
|
6400
|
+
if (textTrack) {
|
|
6401
|
+
trackModes.set(trackInfo, {
|
|
6402
|
+
wasShowing: textTrack.mode === "showing",
|
|
6403
|
+
wasHidden: textTrack.mode === "hidden"
|
|
6404
|
+
});
|
|
6405
|
+
} else {
|
|
6406
|
+
trackModes.set(trackInfo, {
|
|
6407
|
+
wasShowing: false,
|
|
6408
|
+
wasHidden: false
|
|
6409
|
+
});
|
|
6410
|
+
}
|
|
6411
|
+
});
|
|
6412
|
+
const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {
|
|
6413
|
+
const oldSrc = trackInfo.trackElement.getAttribute("src");
|
|
6414
|
+
const parent = trackInfo.trackElement.parentNode;
|
|
6415
|
+
const nextSibling = trackInfo.trackElement.nextSibling;
|
|
6416
|
+
const attributes = {};
|
|
6417
|
+
Array.from(trackInfo.trackElement.attributes).forEach((attr) => {
|
|
6418
|
+
attributes[attr.name] = attr.value;
|
|
6419
|
+
});
|
|
6420
|
+
return {
|
|
6421
|
+
trackInfo,
|
|
6422
|
+
oldSrc,
|
|
6423
|
+
parent,
|
|
6424
|
+
nextSibling,
|
|
6425
|
+
attributes
|
|
6426
|
+
};
|
|
6427
|
+
});
|
|
6428
|
+
tracksToReadd.forEach(({ trackInfo }) => {
|
|
6429
|
+
trackInfo.trackElement.remove();
|
|
6430
|
+
});
|
|
6431
|
+
this.element.load();
|
|
6432
|
+
setTimeout(() => {
|
|
6433
|
+
tracksToReadd.forEach(({ trackInfo, oldSrc, parent, nextSibling, attributes }) => {
|
|
6434
|
+
swappedTracksForTranscript.push(trackInfo);
|
|
6435
|
+
const newTrackElement = document.createElement("track");
|
|
6436
|
+
newTrackElement.setAttribute("src", trackInfo.describedSrc);
|
|
6437
|
+
Object.keys(attributes).forEach((attrName) => {
|
|
6438
|
+
if (attrName !== "src" && attrName !== "data-desc-src") {
|
|
6439
|
+
newTrackElement.setAttribute(attrName, attributes[attrName]);
|
|
6440
|
+
}
|
|
6441
|
+
});
|
|
6442
|
+
if (nextSibling && nextSibling.parentNode) {
|
|
6443
|
+
parent.insertBefore(newTrackElement, nextSibling);
|
|
6444
|
+
} else {
|
|
6445
|
+
parent.appendChild(newTrackElement);
|
|
6446
|
+
}
|
|
6447
|
+
trackInfo.trackElement = newTrackElement;
|
|
6448
|
+
});
|
|
6449
|
+
this.element.load();
|
|
6450
|
+
this.invalidateTrackCache();
|
|
6451
|
+
const setupNewTracks = () => {
|
|
6452
|
+
this.setManagedTimeout(() => {
|
|
6453
|
+
swappedTracksForTranscript.forEach((trackInfo) => {
|
|
6454
|
+
const trackElement = trackInfo.trackElement;
|
|
6455
|
+
const newTextTrack = trackElement.track;
|
|
6456
|
+
if (newTextTrack) {
|
|
6457
|
+
const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };
|
|
6458
|
+
newTextTrack.mode = "hidden";
|
|
6459
|
+
const restoreMode = () => {
|
|
6460
|
+
if (modeInfo.wasShowing) {
|
|
6461
|
+
newTextTrack.mode = "hidden";
|
|
6462
|
+
} else if (modeInfo.wasHidden) {
|
|
6463
|
+
newTextTrack.mode = "hidden";
|
|
6464
|
+
} else {
|
|
6465
|
+
newTextTrack.mode = "disabled";
|
|
6466
|
+
}
|
|
6467
|
+
};
|
|
6468
|
+
if (newTextTrack.readyState >= 2) {
|
|
6469
|
+
restoreMode();
|
|
6470
|
+
} else {
|
|
6471
|
+
newTextTrack.addEventListener("load", restoreMode, { once: true });
|
|
6472
|
+
newTextTrack.addEventListener("error", restoreMode, { once: true });
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
});
|
|
6476
|
+
}, 300);
|
|
6477
|
+
};
|
|
6478
|
+
if (this.element.readyState >= 1) {
|
|
6479
|
+
setTimeout(setupNewTracks, 200);
|
|
6480
|
+
} else {
|
|
6481
|
+
this.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
|
|
6482
|
+
setTimeout(setupNewTracks, 2e3);
|
|
6483
|
+
}
|
|
6484
|
+
}, 100);
|
|
6485
|
+
const skippedCount = validationResults.length - tracksToSwap.length;
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
const allSourceElements = this.sourceElements;
|
|
6489
|
+
const sourcesToUpdate = [];
|
|
6490
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6491
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6492
|
+
const currentSrc2 = sourceEl.getAttribute("src");
|
|
6493
|
+
if (descSrcAttr) {
|
|
6494
|
+
const type = sourceEl.getAttribute("type");
|
|
6495
|
+
let origSrc = sourceEl.getAttribute("data-orig-src");
|
|
6496
|
+
if (!origSrc) {
|
|
6497
|
+
origSrc = currentSrc2;
|
|
6498
|
+
}
|
|
6499
|
+
sourcesToUpdate.push({
|
|
6500
|
+
src: descSrcAttr,
|
|
6501
|
+
// Use described version
|
|
6502
|
+
type,
|
|
6503
|
+
origSrc,
|
|
6504
|
+
descSrc: descSrcAttr
|
|
6505
|
+
});
|
|
6506
|
+
} else {
|
|
6507
|
+
const type = sourceEl.getAttribute("type");
|
|
6508
|
+
const src = sourceEl.getAttribute("src");
|
|
6509
|
+
sourcesToUpdate.push({
|
|
6510
|
+
src,
|
|
6511
|
+
type,
|
|
6512
|
+
origSrc: null,
|
|
6513
|
+
descSrc: null
|
|
6514
|
+
});
|
|
6515
|
+
}
|
|
6516
|
+
});
|
|
6517
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6518
|
+
sourceEl.remove();
|
|
6519
|
+
});
|
|
6520
|
+
sourcesToUpdate.forEach((sourceInfo) => {
|
|
6521
|
+
const newSource = document.createElement("source");
|
|
6522
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
6523
|
+
if (sourceInfo.type) {
|
|
6524
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
6525
|
+
}
|
|
6526
|
+
if (sourceInfo.origSrc) {
|
|
6527
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
6528
|
+
}
|
|
6529
|
+
if (sourceInfo.descSrc) {
|
|
6530
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
6531
|
+
}
|
|
6532
|
+
this.element.appendChild(newSource);
|
|
6533
|
+
});
|
|
6534
|
+
this.element.load();
|
|
6535
|
+
await new Promise((resolve) => {
|
|
6536
|
+
const onLoadedMetadata = () => {
|
|
6537
|
+
this.element.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
6538
|
+
resolve();
|
|
6539
|
+
};
|
|
6540
|
+
this.element.addEventListener("loadedmetadata", onLoadedMetadata);
|
|
6541
|
+
});
|
|
6542
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
6543
|
+
if (this.element.tagName === "VIDEO" && currentTime === 0 && !wasPlaying) {
|
|
6544
|
+
if (this.element.readyState >= 1) {
|
|
6545
|
+
this.element.currentTime = 1e-3;
|
|
6546
|
+
setTimeout(() => {
|
|
6547
|
+
this.element.currentTime = 0;
|
|
6548
|
+
}, 10);
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
this.seek(currentTime);
|
|
6552
|
+
if (wasPlaying) {
|
|
6553
|
+
this.play();
|
|
6554
|
+
}
|
|
6555
|
+
this.state.audioDescriptionEnabled = true;
|
|
6556
|
+
this.emit("audiodescriptionenabled");
|
|
6557
|
+
} else {
|
|
6558
|
+
if (this.audioDescriptionCaptionTracks.length > 0) {
|
|
6559
|
+
const validationPromises = this.audioDescriptionCaptionTracks.map(async (trackInfo) => {
|
|
6560
|
+
if (trackInfo.trackElement && trackInfo.describedSrc) {
|
|
6561
|
+
if (trackInfo.explicit === true) {
|
|
6562
|
+
try {
|
|
6563
|
+
const exists = await this.validateTrackExists(trackInfo.describedSrc);
|
|
6564
|
+
return { trackInfo, exists };
|
|
6565
|
+
} catch (error) {
|
|
6566
|
+
return { trackInfo, exists: false };
|
|
6567
|
+
}
|
|
6568
|
+
} else {
|
|
6569
|
+
return { trackInfo, exists: false };
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
return { trackInfo, exists: false };
|
|
6573
|
+
});
|
|
6574
|
+
const validationResults = await Promise.all(validationPromises);
|
|
6575
|
+
const tracksToSwap = validationResults.filter((result) => result.exists);
|
|
6576
|
+
if (tracksToSwap.length > 0) {
|
|
6577
|
+
const trackModes = /* @__PURE__ */ new Map();
|
|
6578
|
+
tracksToSwap.forEach(({ trackInfo }) => {
|
|
6579
|
+
const textTrack = trackInfo.trackElement.track;
|
|
6580
|
+
if (textTrack) {
|
|
6581
|
+
trackModes.set(trackInfo, {
|
|
6582
|
+
wasShowing: textTrack.mode === "showing",
|
|
6583
|
+
wasHidden: textTrack.mode === "hidden"
|
|
6584
|
+
});
|
|
6585
|
+
} else {
|
|
6586
|
+
trackModes.set(trackInfo, {
|
|
6587
|
+
wasShowing: false,
|
|
6588
|
+
wasHidden: false
|
|
6589
|
+
});
|
|
6590
|
+
}
|
|
6591
|
+
});
|
|
6592
|
+
const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {
|
|
6593
|
+
const oldSrc = trackInfo.trackElement.getAttribute("src");
|
|
6594
|
+
const parent = trackInfo.trackElement.parentNode;
|
|
6595
|
+
const nextSibling = trackInfo.trackElement.nextSibling;
|
|
6596
|
+
const attributes = {};
|
|
6597
|
+
Array.from(trackInfo.trackElement.attributes).forEach((attr) => {
|
|
6598
|
+
attributes[attr.name] = attr.value;
|
|
6599
|
+
});
|
|
6600
|
+
return {
|
|
6601
|
+
trackInfo,
|
|
6602
|
+
oldSrc,
|
|
6603
|
+
parent,
|
|
6604
|
+
nextSibling,
|
|
6605
|
+
attributes
|
|
6606
|
+
};
|
|
6607
|
+
});
|
|
6608
|
+
tracksToReadd.forEach(({ trackInfo }) => {
|
|
6609
|
+
trackInfo.trackElement.remove();
|
|
6610
|
+
});
|
|
6611
|
+
this.element.load();
|
|
6612
|
+
setTimeout(() => {
|
|
6613
|
+
tracksToReadd.forEach(({ trackInfo, oldSrc, parent, nextSibling, attributes }) => {
|
|
6614
|
+
swappedTracksForTranscript.push(trackInfo);
|
|
6615
|
+
const newTrackElement = document.createElement("track");
|
|
6616
|
+
newTrackElement.setAttribute("src", trackInfo.describedSrc);
|
|
6617
|
+
Object.keys(attributes).forEach((attrName) => {
|
|
6618
|
+
if (attrName !== "src" && attrName !== "data-desc-src") {
|
|
6619
|
+
newTrackElement.setAttribute(attrName, attributes[attrName]);
|
|
6620
|
+
}
|
|
6621
|
+
});
|
|
6622
|
+
if (nextSibling && nextSibling.parentNode) {
|
|
6623
|
+
parent.insertBefore(newTrackElement, nextSibling);
|
|
6624
|
+
} else {
|
|
6625
|
+
parent.appendChild(newTrackElement);
|
|
6626
|
+
}
|
|
6627
|
+
trackInfo.trackElement = newTrackElement;
|
|
6628
|
+
});
|
|
6629
|
+
this.element.load();
|
|
6630
|
+
const setupNewTracks = () => {
|
|
6631
|
+
setTimeout(() => {
|
|
6632
|
+
swappedTracksForTranscript.forEach((trackInfo) => {
|
|
6633
|
+
const trackElement = trackInfo.trackElement;
|
|
6634
|
+
const newTextTrack = trackElement.track;
|
|
6635
|
+
if (newTextTrack) {
|
|
6636
|
+
const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };
|
|
6637
|
+
newTextTrack.mode = "hidden";
|
|
6638
|
+
const restoreMode = () => {
|
|
6639
|
+
if (modeInfo.wasShowing) {
|
|
6640
|
+
newTextTrack.mode = "hidden";
|
|
6641
|
+
} else if (modeInfo.wasHidden) {
|
|
6642
|
+
newTextTrack.mode = "hidden";
|
|
6643
|
+
} else {
|
|
6644
|
+
newTextTrack.mode = "disabled";
|
|
6645
|
+
}
|
|
6646
|
+
};
|
|
6647
|
+
if (newTextTrack.readyState >= 2) {
|
|
6648
|
+
restoreMode();
|
|
6649
|
+
} else {
|
|
6650
|
+
newTextTrack.addEventListener("load", restoreMode, { once: true });
|
|
6651
|
+
newTextTrack.addEventListener("error", restoreMode, { once: true });
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
});
|
|
6655
|
+
}, 300);
|
|
6656
|
+
};
|
|
6657
|
+
if (this.element.readyState >= 1) {
|
|
6658
|
+
setTimeout(setupNewTracks, 200);
|
|
6659
|
+
} else {
|
|
6660
|
+
this.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
|
|
6661
|
+
setTimeout(setupNewTracks, 2e3);
|
|
6662
|
+
}
|
|
6663
|
+
}, 100);
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
const fallbackSourceElements = this.sourceElements;
|
|
6667
|
+
const hasSourceElementsWithDesc2 = fallbackSourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6668
|
+
if (hasSourceElementsWithDesc2) {
|
|
6669
|
+
const fallbackSourcesToUpdate = [];
|
|
6670
|
+
fallbackSourceElements.forEach((sourceEl) => {
|
|
6671
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6672
|
+
const currentSrc = sourceEl.getAttribute("src");
|
|
6673
|
+
if (descSrcAttr) {
|
|
6674
|
+
const type = sourceEl.getAttribute("type");
|
|
6675
|
+
let origSrc = sourceEl.getAttribute("data-orig-src");
|
|
6676
|
+
if (!origSrc) {
|
|
6677
|
+
origSrc = currentSrc;
|
|
6678
|
+
}
|
|
6679
|
+
fallbackSourcesToUpdate.push({
|
|
6680
|
+
src: descSrcAttr,
|
|
6681
|
+
type,
|
|
6682
|
+
origSrc,
|
|
6683
|
+
descSrc: descSrcAttr
|
|
6684
|
+
});
|
|
6685
|
+
} else {
|
|
6686
|
+
const type = sourceEl.getAttribute("type");
|
|
6687
|
+
const src = sourceEl.getAttribute("src");
|
|
6688
|
+
fallbackSourcesToUpdate.push({
|
|
6689
|
+
src,
|
|
6690
|
+
type,
|
|
6691
|
+
origSrc: null,
|
|
6692
|
+
descSrc: null
|
|
6693
|
+
});
|
|
6694
|
+
}
|
|
6695
|
+
});
|
|
6696
|
+
fallbackSourceElements.forEach((sourceEl) => {
|
|
6697
|
+
sourceEl.remove();
|
|
6698
|
+
});
|
|
6699
|
+
fallbackSourcesToUpdate.forEach((sourceInfo) => {
|
|
6700
|
+
const newSource = document.createElement("source");
|
|
6701
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
6702
|
+
if (sourceInfo.type) {
|
|
6703
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
6704
|
+
}
|
|
6705
|
+
if (sourceInfo.origSrc) {
|
|
6706
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
6707
|
+
}
|
|
6708
|
+
if (sourceInfo.descSrc) {
|
|
6709
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
6710
|
+
}
|
|
6711
|
+
this.element.appendChild(newSource);
|
|
6712
|
+
});
|
|
6713
|
+
this.element.load();
|
|
6714
|
+
this.invalidateTrackCache();
|
|
6715
|
+
} else {
|
|
6716
|
+
this.element.src = this.audioDescriptionSrc;
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
5939
6719
|
await new Promise((resolve) => {
|
|
5940
6720
|
const onLoadedMetadata = () => {
|
|
5941
6721
|
this.element.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
@@ -5943,10 +6723,177 @@ var VidPly = (() => {
|
|
|
5943
6723
|
};
|
|
5944
6724
|
this.element.addEventListener("loadedmetadata", onLoadedMetadata);
|
|
5945
6725
|
});
|
|
6726
|
+
if (this.element.tagName === "VIDEO" && currentTime === 0 && !wasPlaying) {
|
|
6727
|
+
if (this.element.readyState >= 1) {
|
|
6728
|
+
this.element.currentTime = 1e-3;
|
|
6729
|
+
this.setManagedTimeout(() => {
|
|
6730
|
+
this.element.currentTime = 0;
|
|
6731
|
+
}, 10);
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
5946
6734
|
this.seek(currentTime);
|
|
5947
6735
|
if (wasPlaying) {
|
|
5948
6736
|
this.play();
|
|
5949
6737
|
}
|
|
6738
|
+
if (swappedTracksForTranscript.length > 0 && this.captionManager) {
|
|
6739
|
+
const wasCaptionsEnabled = this.state.captionsEnabled;
|
|
6740
|
+
let currentTrackInfo = null;
|
|
6741
|
+
if (this.captionManager.currentTrack) {
|
|
6742
|
+
const currentTrackIndex = this.captionManager.tracks.findIndex((t) => t.track === this.captionManager.currentTrack.track);
|
|
6743
|
+
if (currentTrackIndex >= 0) {
|
|
6744
|
+
currentTrackInfo = {
|
|
6745
|
+
language: this.captionManager.tracks[currentTrackIndex].language,
|
|
6746
|
+
kind: this.captionManager.tracks[currentTrackIndex].kind
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
setTimeout(() => {
|
|
6751
|
+
this.captionManager.tracks = [];
|
|
6752
|
+
this.captionManager.loadTracks();
|
|
6753
|
+
if (wasCaptionsEnabled && currentTrackInfo && this.captionManager.tracks.length > 0) {
|
|
6754
|
+
const matchingTrackIndex = this.captionManager.tracks.findIndex(
|
|
6755
|
+
(t) => t.language === currentTrackInfo.language && t.kind === currentTrackInfo.kind
|
|
6756
|
+
);
|
|
6757
|
+
if (matchingTrackIndex >= 0) {
|
|
6758
|
+
this.captionManager.enable(matchingTrackIndex);
|
|
6759
|
+
} else if (this.captionManager.tracks.length > 0) {
|
|
6760
|
+
this.captionManager.enable(0);
|
|
6761
|
+
}
|
|
6762
|
+
}
|
|
6763
|
+
}, 600);
|
|
6764
|
+
}
|
|
6765
|
+
if (this.transcriptManager && this.transcriptManager.isVisible) {
|
|
6766
|
+
const swappedTracks = typeof swappedTracksForTranscript !== "undefined" ? swappedTracksForTranscript : [];
|
|
6767
|
+
if (swappedTracks.length > 0) {
|
|
6768
|
+
const onMetadataLoaded = () => {
|
|
6769
|
+
this.invalidateTrackCache();
|
|
6770
|
+
const allTextTracks = this.textTracks;
|
|
6771
|
+
const freshTracks = swappedTracks.map((trackInfo) => {
|
|
6772
|
+
const trackEl = trackInfo.trackElement;
|
|
6773
|
+
const expectedSrc = trackEl.getAttribute("src");
|
|
6774
|
+
const srclang = trackEl.getAttribute("srclang");
|
|
6775
|
+
const kind = trackEl.getAttribute("kind");
|
|
6776
|
+
let foundTrack = allTextTracks.find((track) => trackEl.track === track);
|
|
6777
|
+
if (!foundTrack) {
|
|
6778
|
+
foundTrack = allTextTracks.find((track) => {
|
|
6779
|
+
if (track.language === srclang && (track.kind === kind || kind === "captions" && track.kind === "subtitles")) {
|
|
6780
|
+
const trackElementForTrack = this.findTrackElement(track);
|
|
6781
|
+
if (trackElementForTrack) {
|
|
6782
|
+
const actualSrc = trackElementForTrack.getAttribute("src");
|
|
6783
|
+
if (actualSrc === expectedSrc) {
|
|
6784
|
+
return true;
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6788
|
+
return false;
|
|
6789
|
+
});
|
|
6790
|
+
}
|
|
6791
|
+
if (foundTrack) {
|
|
6792
|
+
const trackElement = this.findTrackElement(foundTrack);
|
|
6793
|
+
if (trackElement && trackElement.getAttribute("src") !== expectedSrc) {
|
|
6794
|
+
return null;
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
return foundTrack;
|
|
6798
|
+
}).filter(Boolean);
|
|
6799
|
+
if (freshTracks.length === 0) {
|
|
6800
|
+
this.setManagedTimeout(() => {
|
|
6801
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6802
|
+
this.transcriptManager.loadTranscriptData();
|
|
6803
|
+
}
|
|
6804
|
+
}, 1e3);
|
|
6805
|
+
return;
|
|
6806
|
+
}
|
|
6807
|
+
freshTracks.forEach((track) => {
|
|
6808
|
+
if (track.mode === "disabled") {
|
|
6809
|
+
track.mode = "hidden";
|
|
6810
|
+
}
|
|
6811
|
+
});
|
|
6812
|
+
let loadedCount = 0;
|
|
6813
|
+
const checkLoaded = () => {
|
|
6814
|
+
loadedCount++;
|
|
6815
|
+
if (loadedCount >= freshTracks.length) {
|
|
6816
|
+
this.setManagedTimeout(() => {
|
|
6817
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6818
|
+
this.invalidateTrackCache();
|
|
6819
|
+
const allTextTracks2 = this.textTracks;
|
|
6820
|
+
const swappedTrackSrcs = swappedTracks.map((t) => t.describedSrc);
|
|
6821
|
+
const hasCorrectTracks = freshTracks.some((track) => {
|
|
6822
|
+
const trackEl = this.findTrackElement(track);
|
|
6823
|
+
return trackEl && swappedTrackSrcs.includes(trackEl.getAttribute("src"));
|
|
6824
|
+
});
|
|
6825
|
+
if (hasCorrectTracks || freshTracks.length > 0) {
|
|
6826
|
+
this.transcriptManager.loadTranscriptData();
|
|
6827
|
+
}
|
|
6828
|
+
}
|
|
6829
|
+
}, 800);
|
|
6830
|
+
}
|
|
6831
|
+
};
|
|
6832
|
+
freshTracks.forEach((track) => {
|
|
6833
|
+
if (track.mode === "disabled") {
|
|
6834
|
+
track.mode = "hidden";
|
|
6835
|
+
}
|
|
6836
|
+
const trackElementForTrack = this.findTrackElement(track);
|
|
6837
|
+
const actualSrc = trackElementForTrack ? trackElementForTrack.getAttribute("src") : null;
|
|
6838
|
+
const expectedTrackInfo = swappedTracks.find((t) => {
|
|
6839
|
+
const tEl = t.trackElement;
|
|
6840
|
+
return tEl && (tEl.track === track || tEl.getAttribute("srclang") === track.language && tEl.getAttribute("kind") === track.kind);
|
|
6841
|
+
});
|
|
6842
|
+
const expectedSrc = expectedTrackInfo ? expectedTrackInfo.describedSrc : null;
|
|
6843
|
+
if (expectedSrc && actualSrc && actualSrc !== expectedSrc) {
|
|
6844
|
+
checkLoaded();
|
|
6845
|
+
return;
|
|
6846
|
+
}
|
|
6847
|
+
if (track.readyState >= 2 && track.cues && track.cues.length > 0) {
|
|
6848
|
+
checkLoaded();
|
|
6849
|
+
} else {
|
|
6850
|
+
if (track.mode === "disabled") {
|
|
6851
|
+
track.mode = "hidden";
|
|
6852
|
+
}
|
|
6853
|
+
const onTrackLoad = () => {
|
|
6854
|
+
this.setManagedTimeout(checkLoaded, 300);
|
|
6855
|
+
};
|
|
6856
|
+
if (track.readyState >= 2) {
|
|
6857
|
+
this.setManagedTimeout(() => {
|
|
6858
|
+
if (track.cues && track.cues.length > 0) {
|
|
6859
|
+
checkLoaded();
|
|
6860
|
+
} else {
|
|
6861
|
+
track.addEventListener("load", onTrackLoad, { once: true });
|
|
6862
|
+
}
|
|
6863
|
+
}, 100);
|
|
6864
|
+
} else {
|
|
6865
|
+
track.addEventListener("load", onTrackLoad, { once: true });
|
|
6866
|
+
track.addEventListener("error", () => {
|
|
6867
|
+
checkLoaded();
|
|
6868
|
+
}, { once: true });
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
});
|
|
6872
|
+
};
|
|
6873
|
+
const waitForTracks = () => {
|
|
6874
|
+
this.setManagedTimeout(() => {
|
|
6875
|
+
if (this.element.readyState >= 1) {
|
|
6876
|
+
onMetadataLoaded();
|
|
6877
|
+
} else {
|
|
6878
|
+
this.element.addEventListener("loadedmetadata", onMetadataLoaded, { once: true });
|
|
6879
|
+
this.setManagedTimeout(onMetadataLoaded, 2e3);
|
|
6880
|
+
}
|
|
6881
|
+
}, 500);
|
|
6882
|
+
};
|
|
6883
|
+
waitForTracks();
|
|
6884
|
+
setTimeout(() => {
|
|
6885
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6886
|
+
this.transcriptManager.loadTranscriptData();
|
|
6887
|
+
}
|
|
6888
|
+
}, 5e3);
|
|
6889
|
+
} else {
|
|
6890
|
+
setTimeout(() => {
|
|
6891
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6892
|
+
this.transcriptManager.loadTranscriptData();
|
|
6893
|
+
}
|
|
6894
|
+
}, 800);
|
|
6895
|
+
}
|
|
6896
|
+
}
|
|
5950
6897
|
this.state.audioDescriptionEnabled = true;
|
|
5951
6898
|
this.emit("audiodescriptionenabled");
|
|
5952
6899
|
}
|
|
@@ -5956,7 +6903,64 @@ var VidPly = (() => {
|
|
|
5956
6903
|
}
|
|
5957
6904
|
const currentTime = this.state.currentTime;
|
|
5958
6905
|
const wasPlaying = this.state.playing;
|
|
5959
|
-
this.
|
|
6906
|
+
if (this.audioDescriptionCaptionTracks.length > 0) {
|
|
6907
|
+
this.audioDescriptionCaptionTracks.forEach((trackInfo) => {
|
|
6908
|
+
if (trackInfo.trackElement && trackInfo.originalTrackSrc) {
|
|
6909
|
+
trackInfo.trackElement.setAttribute("src", trackInfo.originalTrackSrc);
|
|
6910
|
+
}
|
|
6911
|
+
});
|
|
6912
|
+
}
|
|
6913
|
+
const allSourceElements = this.sourceElements;
|
|
6914
|
+
const hasSourceElementsToSwap = allSourceElements.some((el) => el.getAttribute("data-orig-src"));
|
|
6915
|
+
if (hasSourceElementsToSwap) {
|
|
6916
|
+
const sourcesToRestore = [];
|
|
6917
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6918
|
+
const origSrcAttr = sourceEl.getAttribute("data-orig-src");
|
|
6919
|
+
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
6920
|
+
if (origSrcAttr) {
|
|
6921
|
+
const type = sourceEl.getAttribute("type");
|
|
6922
|
+
sourcesToRestore.push({
|
|
6923
|
+
src: origSrcAttr,
|
|
6924
|
+
// Use original version
|
|
6925
|
+
type,
|
|
6926
|
+
origSrc: origSrcAttr,
|
|
6927
|
+
descSrc: descSrcAttr
|
|
6928
|
+
// Keep data-desc-src for future swaps
|
|
6929
|
+
});
|
|
6930
|
+
} else {
|
|
6931
|
+
const type = sourceEl.getAttribute("type");
|
|
6932
|
+
const src = sourceEl.getAttribute("src");
|
|
6933
|
+
sourcesToRestore.push({
|
|
6934
|
+
src,
|
|
6935
|
+
type,
|
|
6936
|
+
origSrc: null,
|
|
6937
|
+
descSrc: descSrcAttr
|
|
6938
|
+
});
|
|
6939
|
+
}
|
|
6940
|
+
});
|
|
6941
|
+
allSourceElements.forEach((sourceEl) => {
|
|
6942
|
+
sourceEl.remove();
|
|
6943
|
+
});
|
|
6944
|
+
sourcesToRestore.forEach((sourceInfo) => {
|
|
6945
|
+
const newSource = document.createElement("source");
|
|
6946
|
+
newSource.setAttribute("src", sourceInfo.src);
|
|
6947
|
+
if (sourceInfo.type) {
|
|
6948
|
+
newSource.setAttribute("type", sourceInfo.type);
|
|
6949
|
+
}
|
|
6950
|
+
if (sourceInfo.origSrc) {
|
|
6951
|
+
newSource.setAttribute("data-orig-src", sourceInfo.origSrc);
|
|
6952
|
+
}
|
|
6953
|
+
if (sourceInfo.descSrc) {
|
|
6954
|
+
newSource.setAttribute("data-desc-src", sourceInfo.descSrc);
|
|
6955
|
+
}
|
|
6956
|
+
this.element.appendChild(newSource);
|
|
6957
|
+
});
|
|
6958
|
+
this.element.load();
|
|
6959
|
+
} else {
|
|
6960
|
+
const originalSrcToUse = this.originalAudioDescriptionSource || this.originalSrc;
|
|
6961
|
+
this.element.src = originalSrcToUse;
|
|
6962
|
+
this.element.load();
|
|
6963
|
+
}
|
|
5960
6964
|
await new Promise((resolve) => {
|
|
5961
6965
|
const onLoadedMetadata = () => {
|
|
5962
6966
|
this.element.removeEventListener("loadedmetadata", onLoadedMetadata);
|
|
@@ -5968,13 +6972,50 @@ var VidPly = (() => {
|
|
|
5968
6972
|
if (wasPlaying) {
|
|
5969
6973
|
this.play();
|
|
5970
6974
|
}
|
|
6975
|
+
if (this.transcriptManager && this.transcriptManager.isVisible) {
|
|
6976
|
+
this.setManagedTimeout(() => {
|
|
6977
|
+
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6978
|
+
this.transcriptManager.loadTranscriptData();
|
|
6979
|
+
}
|
|
6980
|
+
}, 500);
|
|
6981
|
+
}
|
|
5971
6982
|
this.state.audioDescriptionEnabled = false;
|
|
5972
6983
|
this.emit("audiodescriptiondisabled");
|
|
5973
6984
|
}
|
|
5974
6985
|
async toggleAudioDescription() {
|
|
5975
|
-
const
|
|
5976
|
-
const
|
|
5977
|
-
if (descriptionTrack) {
|
|
6986
|
+
const descriptionTrack = this.findTextTrack("descriptions");
|
|
6987
|
+
const hasAudioDescriptionSrc = this.audioDescriptionSrc || this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6988
|
+
if (descriptionTrack && hasAudioDescriptionSrc) {
|
|
6989
|
+
if (this.state.audioDescriptionEnabled) {
|
|
6990
|
+
descriptionTrack.mode = "hidden";
|
|
6991
|
+
await this.disableAudioDescription();
|
|
6992
|
+
} else {
|
|
6993
|
+
await this.enableAudioDescription();
|
|
6994
|
+
const enableDescriptionTrack = () => {
|
|
6995
|
+
this.invalidateTrackCache();
|
|
6996
|
+
const descTrack = this.findTextTrack("descriptions");
|
|
6997
|
+
if (descTrack) {
|
|
6998
|
+
if (descTrack.mode === "disabled") {
|
|
6999
|
+
descTrack.mode = "hidden";
|
|
7000
|
+
this.setManagedTimeout(() => {
|
|
7001
|
+
descTrack.mode = "showing";
|
|
7002
|
+
}, 50);
|
|
7003
|
+
} else {
|
|
7004
|
+
descTrack.mode = "showing";
|
|
7005
|
+
}
|
|
7006
|
+
} else if (this.element.readyState < 2) {
|
|
7007
|
+
this.setManagedTimeout(enableDescriptionTrack, 100);
|
|
7008
|
+
}
|
|
7009
|
+
};
|
|
7010
|
+
if (this.element.readyState >= 1) {
|
|
7011
|
+
this.setManagedTimeout(enableDescriptionTrack, 200);
|
|
7012
|
+
} else {
|
|
7013
|
+
this.element.addEventListener("loadedmetadata", () => {
|
|
7014
|
+
this.setManagedTimeout(enableDescriptionTrack, 200);
|
|
7015
|
+
}, { once: true });
|
|
7016
|
+
}
|
|
7017
|
+
}
|
|
7018
|
+
} else if (descriptionTrack) {
|
|
5978
7019
|
if (descriptionTrack.mode === "showing") {
|
|
5979
7020
|
descriptionTrack.mode = "hidden";
|
|
5980
7021
|
this.state.audioDescriptionEnabled = false;
|
|
@@ -5984,7 +7025,7 @@ var VidPly = (() => {
|
|
|
5984
7025
|
this.state.audioDescriptionEnabled = true;
|
|
5985
7026
|
this.emit("audiodescriptionenabled");
|
|
5986
7027
|
}
|
|
5987
|
-
} else if (
|
|
7028
|
+
} else if (hasAudioDescriptionSrc) {
|
|
5988
7029
|
if (this.state.audioDescriptionEnabled) {
|
|
5989
7030
|
await this.disableAudioDescription();
|
|
5990
7031
|
} else {
|
|
@@ -6385,9 +7426,25 @@ var VidPly = (() => {
|
|
|
6385
7426
|
}
|
|
6386
7427
|
}
|
|
6387
7428
|
// Logging
|
|
6388
|
-
log(
|
|
6389
|
-
if (this.options.debug) {
|
|
6390
|
-
|
|
7429
|
+
log(...messages) {
|
|
7430
|
+
if (!this.options.debug) {
|
|
7431
|
+
return;
|
|
7432
|
+
}
|
|
7433
|
+
let type = "log";
|
|
7434
|
+
if (messages.length > 0) {
|
|
7435
|
+
const potentialType = messages[messages.length - 1];
|
|
7436
|
+
if (typeof potentialType === "string" && console[potentialType]) {
|
|
7437
|
+
type = potentialType;
|
|
7438
|
+
messages = messages.slice(0, -1);
|
|
7439
|
+
}
|
|
7440
|
+
}
|
|
7441
|
+
if (messages.length === 0) {
|
|
7442
|
+
messages = [""];
|
|
7443
|
+
}
|
|
7444
|
+
if (typeof console[type] === "function") {
|
|
7445
|
+
console[type]("[VidPly]", ...messages);
|
|
7446
|
+
} else {
|
|
7447
|
+
console.log("[VidPly]", ...messages);
|
|
6391
7448
|
}
|
|
6392
7449
|
}
|
|
6393
7450
|
// Setup responsive handlers
|
|
@@ -6447,7 +7504,7 @@ var VidPly = (() => {
|
|
|
6447
7504
|
this.controlBar.updateFullscreenButton();
|
|
6448
7505
|
}
|
|
6449
7506
|
if (this.signLanguageWrapper && this.signLanguageWrapper.style.display !== "none") {
|
|
6450
|
-
|
|
7507
|
+
this.setManagedTimeout(() => {
|
|
6451
7508
|
requestAnimationFrame(() => {
|
|
6452
7509
|
this.storage.saveSignLanguagePreferences({ size: null });
|
|
6453
7510
|
this.signLanguageDesiredPosition = "bottom-right";
|
|
@@ -6510,12 +7567,368 @@ var VidPly = (() => {
|
|
|
6510
7567
|
document.removeEventListener("MSFullscreenChange", this.fullscreenChangeHandler);
|
|
6511
7568
|
this.fullscreenChangeHandler = null;
|
|
6512
7569
|
}
|
|
7570
|
+
this.timeouts.forEach((timeoutId) => clearTimeout(timeoutId));
|
|
7571
|
+
this.timeouts.clear();
|
|
7572
|
+
if (this.metadataCueChangeHandler) {
|
|
7573
|
+
const textTracks = this.textTracks;
|
|
7574
|
+
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
7575
|
+
if (metadataTrack) {
|
|
7576
|
+
metadataTrack.removeEventListener("cuechange", this.metadataCueChangeHandler);
|
|
7577
|
+
}
|
|
7578
|
+
this.metadataCueChangeHandler = null;
|
|
7579
|
+
}
|
|
7580
|
+
if (this.metadataAlertHandlers && this.metadataAlertHandlers.size > 0) {
|
|
7581
|
+
this.metadataAlertHandlers.forEach(({ button, handler }) => {
|
|
7582
|
+
if (button && handler) {
|
|
7583
|
+
button.removeEventListener("click", handler);
|
|
7584
|
+
}
|
|
7585
|
+
});
|
|
7586
|
+
this.metadataAlertHandlers.clear();
|
|
7587
|
+
}
|
|
6513
7588
|
if (this.container && this.container.parentNode) {
|
|
6514
7589
|
this.container.parentNode.insertBefore(this.element, this.container);
|
|
6515
7590
|
this.container.parentNode.removeChild(this.container);
|
|
6516
7591
|
}
|
|
6517
7592
|
this.removeAllListeners();
|
|
6518
7593
|
}
|
|
7594
|
+
/**
|
|
7595
|
+
* Setup metadata track handling
|
|
7596
|
+
* This enables metadata tracks and listens for cue changes to trigger actions
|
|
7597
|
+
*/
|
|
7598
|
+
setupMetadataHandling() {
|
|
7599
|
+
const setupMetadata = () => {
|
|
7600
|
+
const textTracks = this.textTracks;
|
|
7601
|
+
const metadataTrack = textTracks.find((track) => track.kind === "metadata");
|
|
7602
|
+
if (metadataTrack) {
|
|
7603
|
+
if (metadataTrack.mode === "disabled") {
|
|
7604
|
+
metadataTrack.mode = "hidden";
|
|
7605
|
+
}
|
|
7606
|
+
if (this.metadataCueChangeHandler) {
|
|
7607
|
+
metadataTrack.removeEventListener("cuechange", this.metadataCueChangeHandler);
|
|
7608
|
+
}
|
|
7609
|
+
this.metadataCueChangeHandler = () => {
|
|
7610
|
+
const activeCues = Array.from(metadataTrack.activeCues || []);
|
|
7611
|
+
if (activeCues.length > 0) {
|
|
7612
|
+
if (this.options.debug) {
|
|
7613
|
+
this.log("[Metadata] Active cues:", activeCues.map((c) => ({
|
|
7614
|
+
start: c.startTime,
|
|
7615
|
+
end: c.endTime,
|
|
7616
|
+
text: c.text
|
|
7617
|
+
})));
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
activeCues.forEach((cue) => {
|
|
7621
|
+
this.handleMetadataCue(cue);
|
|
7622
|
+
});
|
|
7623
|
+
};
|
|
7624
|
+
metadataTrack.addEventListener("cuechange", this.metadataCueChangeHandler);
|
|
7625
|
+
if (this.options.debug) {
|
|
7626
|
+
const cueCount = metadataTrack.cues ? metadataTrack.cues.length : 0;
|
|
7627
|
+
this.log("[Metadata] Track enabled,", cueCount, "cues available");
|
|
7628
|
+
}
|
|
7629
|
+
} else if (this.options.debug) {
|
|
7630
|
+
this.log("[Metadata] No metadata track found");
|
|
7631
|
+
}
|
|
7632
|
+
};
|
|
7633
|
+
setupMetadata();
|
|
7634
|
+
this.on("loadedmetadata", setupMetadata);
|
|
7635
|
+
}
|
|
7636
|
+
normalizeMetadataSelector(selector) {
|
|
7637
|
+
if (!selector) {
|
|
7638
|
+
return null;
|
|
7639
|
+
}
|
|
7640
|
+
const trimmed = selector.trim();
|
|
7641
|
+
if (!trimmed) {
|
|
7642
|
+
return null;
|
|
7643
|
+
}
|
|
7644
|
+
if (trimmed.startsWith("#") || trimmed.startsWith(".") || trimmed.startsWith("[")) {
|
|
7645
|
+
return trimmed;
|
|
7646
|
+
}
|
|
7647
|
+
return `#${trimmed}`;
|
|
7648
|
+
}
|
|
7649
|
+
resolveMetadataConfig(map, key) {
|
|
7650
|
+
if (!map || !key) {
|
|
7651
|
+
return null;
|
|
7652
|
+
}
|
|
7653
|
+
if (Object.prototype.hasOwnProperty.call(map, key)) {
|
|
7654
|
+
return map[key];
|
|
7655
|
+
}
|
|
7656
|
+
const withoutHash = key.replace(/^#/, "");
|
|
7657
|
+
if (Object.prototype.hasOwnProperty.call(map, withoutHash)) {
|
|
7658
|
+
return map[withoutHash];
|
|
7659
|
+
}
|
|
7660
|
+
return null;
|
|
7661
|
+
}
|
|
7662
|
+
cacheMetadataAlertContent(element, config = {}) {
|
|
7663
|
+
if (!element) {
|
|
7664
|
+
return;
|
|
7665
|
+
}
|
|
7666
|
+
const titleSelector = config.titleSelector || "[data-vidply-alert-title], h3, header";
|
|
7667
|
+
const messageSelector = config.messageSelector || "[data-vidply-alert-message], p";
|
|
7668
|
+
const titleEl = element.querySelector(titleSelector);
|
|
7669
|
+
if (titleEl && !titleEl.dataset.vidplyAlertTitleOriginal) {
|
|
7670
|
+
titleEl.dataset.vidplyAlertTitleOriginal = titleEl.textContent.trim();
|
|
7671
|
+
}
|
|
7672
|
+
const messageEl = element.querySelector(messageSelector);
|
|
7673
|
+
if (messageEl && !messageEl.dataset.vidplyAlertMessageOriginal) {
|
|
7674
|
+
messageEl.dataset.vidplyAlertMessageOriginal = messageEl.textContent.trim();
|
|
7675
|
+
}
|
|
7676
|
+
}
|
|
7677
|
+
restoreMetadataAlertContent(element, config = {}) {
|
|
7678
|
+
if (!element) {
|
|
7679
|
+
return;
|
|
7680
|
+
}
|
|
7681
|
+
const titleSelector = config.titleSelector || "[data-vidply-alert-title], h3, header";
|
|
7682
|
+
const messageSelector = config.messageSelector || "[data-vidply-alert-message], p";
|
|
7683
|
+
const titleEl = element.querySelector(titleSelector);
|
|
7684
|
+
if (titleEl && titleEl.dataset.vidplyAlertTitleOriginal) {
|
|
7685
|
+
titleEl.textContent = titleEl.dataset.vidplyAlertTitleOriginal;
|
|
7686
|
+
}
|
|
7687
|
+
const messageEl = element.querySelector(messageSelector);
|
|
7688
|
+
if (messageEl && messageEl.dataset.vidplyAlertMessageOriginal) {
|
|
7689
|
+
messageEl.textContent = messageEl.dataset.vidplyAlertMessageOriginal;
|
|
7690
|
+
}
|
|
7691
|
+
}
|
|
7692
|
+
focusMetadataTarget(target, fallbackElement = null) {
|
|
7693
|
+
var _a, _b;
|
|
7694
|
+
if (!target || target === "none") {
|
|
7695
|
+
return;
|
|
7696
|
+
}
|
|
7697
|
+
if (target === "alert" && fallbackElement) {
|
|
7698
|
+
fallbackElement.focus();
|
|
7699
|
+
return;
|
|
7700
|
+
}
|
|
7701
|
+
if (target === "player") {
|
|
7702
|
+
if (this.container) {
|
|
7703
|
+
this.container.focus();
|
|
7704
|
+
}
|
|
7705
|
+
return;
|
|
7706
|
+
}
|
|
7707
|
+
if (target === "media") {
|
|
7708
|
+
this.element.focus();
|
|
7709
|
+
return;
|
|
7710
|
+
}
|
|
7711
|
+
if (target === "playButton") {
|
|
7712
|
+
const playButton = (_b = (_a = this.controlBar) == null ? void 0 : _a.controls) == null ? void 0 : _b.playPause;
|
|
7713
|
+
if (playButton) {
|
|
7714
|
+
playButton.focus();
|
|
7715
|
+
}
|
|
7716
|
+
return;
|
|
7717
|
+
}
|
|
7718
|
+
if (typeof target === "string") {
|
|
7719
|
+
const targetElement = document.querySelector(target);
|
|
7720
|
+
if (targetElement) {
|
|
7721
|
+
if (targetElement.tabIndex === -1 && !targetElement.hasAttribute("tabindex")) {
|
|
7722
|
+
targetElement.setAttribute("tabindex", "-1");
|
|
7723
|
+
}
|
|
7724
|
+
targetElement.focus();
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
handleMetadataAlert(selector, options = {}) {
|
|
7729
|
+
if (!selector) {
|
|
7730
|
+
return;
|
|
7731
|
+
}
|
|
7732
|
+
const config = this.resolveMetadataConfig(this.options.metadataAlerts, selector) || {};
|
|
7733
|
+
const element = options.element || document.querySelector(selector);
|
|
7734
|
+
if (!element) {
|
|
7735
|
+
if (this.options.debug) {
|
|
7736
|
+
this.log("[Metadata] Alert element not found:", selector);
|
|
7737
|
+
}
|
|
7738
|
+
return;
|
|
7739
|
+
}
|
|
7740
|
+
if (this.options.debug) {
|
|
7741
|
+
this.log("[Metadata] Handling alert", selector, { reason: options.reason, config });
|
|
7742
|
+
}
|
|
7743
|
+
this.cacheMetadataAlertContent(element, config);
|
|
7744
|
+
if (!element.dataset.vidplyAlertOriginalDisplay) {
|
|
7745
|
+
element.dataset.vidplyAlertOriginalDisplay = element.style.display || "";
|
|
7746
|
+
}
|
|
7747
|
+
if (!element.dataset.vidplyAlertDisplay) {
|
|
7748
|
+
element.dataset.vidplyAlertDisplay = config.display || "block";
|
|
7749
|
+
}
|
|
7750
|
+
const shouldShow = options.show !== void 0 ? options.show : config.show !== false;
|
|
7751
|
+
if (shouldShow) {
|
|
7752
|
+
const displayValue = config.display || element.dataset.vidplyAlertDisplay || "block";
|
|
7753
|
+
element.style.display = displayValue;
|
|
7754
|
+
element.hidden = false;
|
|
7755
|
+
element.removeAttribute("hidden");
|
|
7756
|
+
element.setAttribute("aria-hidden", "false");
|
|
7757
|
+
element.setAttribute("data-vidply-alert-active", "true");
|
|
7758
|
+
}
|
|
7759
|
+
const shouldReset = config.resetContent !== false && options.reason === "focus";
|
|
7760
|
+
if (shouldReset) {
|
|
7761
|
+
this.restoreMetadataAlertContent(element, config);
|
|
7762
|
+
}
|
|
7763
|
+
const shouldFocus = options.focus !== void 0 ? options.focus : config.focusOnShow ?? options.reason !== "focus";
|
|
7764
|
+
if (shouldShow && shouldFocus) {
|
|
7765
|
+
if (element.tabIndex === -1 && !element.hasAttribute("tabindex")) {
|
|
7766
|
+
element.setAttribute("tabindex", "-1");
|
|
7767
|
+
}
|
|
7768
|
+
element.focus();
|
|
7769
|
+
}
|
|
7770
|
+
if (shouldShow && config.autoScroll !== false && options.autoScroll !== false) {
|
|
7771
|
+
element.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
7772
|
+
}
|
|
7773
|
+
const continueSelector = config.continueButton;
|
|
7774
|
+
if (continueSelector) {
|
|
7775
|
+
let continueButton = null;
|
|
7776
|
+
if (continueSelector === "self") {
|
|
7777
|
+
continueButton = element;
|
|
7778
|
+
} else if (element.matches(continueSelector)) {
|
|
7779
|
+
continueButton = element;
|
|
7780
|
+
} else {
|
|
7781
|
+
continueButton = element.querySelector(continueSelector) || document.querySelector(continueSelector);
|
|
7782
|
+
}
|
|
7783
|
+
if (continueButton && !this.metadataAlertHandlers.has(selector)) {
|
|
7784
|
+
const handler = () => {
|
|
7785
|
+
const hideOnContinue = config.hideOnContinue !== false;
|
|
7786
|
+
if (hideOnContinue) {
|
|
7787
|
+
const originalDisplay = element.dataset.vidplyAlertOriginalDisplay || "";
|
|
7788
|
+
element.style.display = config.hideDisplay || originalDisplay || "none";
|
|
7789
|
+
element.setAttribute("aria-hidden", "true");
|
|
7790
|
+
element.removeAttribute("data-vidply-alert-active");
|
|
7791
|
+
}
|
|
7792
|
+
if (config.resume !== false && this.state.paused) {
|
|
7793
|
+
this.play();
|
|
7794
|
+
}
|
|
7795
|
+
const focusTarget = config.focusTarget || "playButton";
|
|
7796
|
+
this.setManagedTimeout(() => {
|
|
7797
|
+
this.focusMetadataTarget(focusTarget, element);
|
|
7798
|
+
}, config.focusDelay ?? 100);
|
|
7799
|
+
};
|
|
7800
|
+
continueButton.addEventListener("click", handler);
|
|
7801
|
+
this.metadataAlertHandlers.set(selector, { button: continueButton, handler });
|
|
7802
|
+
}
|
|
7803
|
+
}
|
|
7804
|
+
return element;
|
|
7805
|
+
}
|
|
7806
|
+
handleMetadataHashtags(hashtags) {
|
|
7807
|
+
if (!Array.isArray(hashtags) || hashtags.length === 0) {
|
|
7808
|
+
return;
|
|
7809
|
+
}
|
|
7810
|
+
const configMap = this.options.metadataHashtags;
|
|
7811
|
+
if (!configMap) {
|
|
7812
|
+
return;
|
|
7813
|
+
}
|
|
7814
|
+
hashtags.forEach((tag) => {
|
|
7815
|
+
const config = this.resolveMetadataConfig(configMap, tag);
|
|
7816
|
+
if (!config) {
|
|
7817
|
+
return;
|
|
7818
|
+
}
|
|
7819
|
+
const selector = this.normalizeMetadataSelector(config.alert || config.selector || config.target);
|
|
7820
|
+
if (!selector) {
|
|
7821
|
+
return;
|
|
7822
|
+
}
|
|
7823
|
+
const element = document.querySelector(selector);
|
|
7824
|
+
if (!element) {
|
|
7825
|
+
if (this.options.debug) {
|
|
7826
|
+
this.log("[Metadata] Hashtag target not found:", selector);
|
|
7827
|
+
}
|
|
7828
|
+
return;
|
|
7829
|
+
}
|
|
7830
|
+
if (this.options.debug) {
|
|
7831
|
+
this.log("[Metadata] Handling hashtag", tag, { selector, config });
|
|
7832
|
+
}
|
|
7833
|
+
this.cacheMetadataAlertContent(element, config);
|
|
7834
|
+
if (config.title) {
|
|
7835
|
+
const titleSelector = config.titleSelector || "[data-vidply-alert-title], h3, header";
|
|
7836
|
+
const titleEl = element.querySelector(titleSelector);
|
|
7837
|
+
if (titleEl) {
|
|
7838
|
+
titleEl.textContent = config.title;
|
|
7839
|
+
}
|
|
7840
|
+
}
|
|
7841
|
+
if (config.message) {
|
|
7842
|
+
const messageSelector = config.messageSelector || "[data-vidply-alert-message], p";
|
|
7843
|
+
const messageEl = element.querySelector(messageSelector);
|
|
7844
|
+
if (messageEl) {
|
|
7845
|
+
messageEl.textContent = config.message;
|
|
7846
|
+
}
|
|
7847
|
+
}
|
|
7848
|
+
const show = config.show !== false;
|
|
7849
|
+
const focus = config.focus !== void 0 ? config.focus : false;
|
|
7850
|
+
this.handleMetadataAlert(selector, {
|
|
7851
|
+
element,
|
|
7852
|
+
show,
|
|
7853
|
+
focus,
|
|
7854
|
+
autoScroll: config.autoScroll,
|
|
7855
|
+
reason: "hashtag"
|
|
7856
|
+
});
|
|
7857
|
+
});
|
|
7858
|
+
}
|
|
7859
|
+
/**
|
|
7860
|
+
* Handle individual metadata cues
|
|
7861
|
+
* Parses metadata text and emits events or triggers actions
|
|
7862
|
+
*/
|
|
7863
|
+
handleMetadataCue(cue) {
|
|
7864
|
+
const text = cue.text.trim();
|
|
7865
|
+
if (this.options.debug) {
|
|
7866
|
+
this.log("[Metadata] Processing cue:", {
|
|
7867
|
+
time: cue.startTime,
|
|
7868
|
+
text
|
|
7869
|
+
});
|
|
7870
|
+
}
|
|
7871
|
+
this.emit("metadata", {
|
|
7872
|
+
time: cue.startTime,
|
|
7873
|
+
endTime: cue.endTime,
|
|
7874
|
+
text,
|
|
7875
|
+
cue
|
|
7876
|
+
});
|
|
7877
|
+
if (text.includes("PAUSE")) {
|
|
7878
|
+
if (!this.state.paused) {
|
|
7879
|
+
if (this.options.debug) {
|
|
7880
|
+
this.log("[Metadata] Pausing video at", cue.startTime);
|
|
7881
|
+
}
|
|
7882
|
+
this.pause();
|
|
7883
|
+
}
|
|
7884
|
+
this.emit("metadata:pause", { time: cue.startTime, text });
|
|
7885
|
+
}
|
|
7886
|
+
const focusMatch = text.match(/FOCUS:([\w#-]+)/);
|
|
7887
|
+
if (focusMatch) {
|
|
7888
|
+
const targetSelector = focusMatch[1];
|
|
7889
|
+
const normalizedSelector = this.normalizeMetadataSelector(targetSelector);
|
|
7890
|
+
const targetElement = normalizedSelector ? document.querySelector(normalizedSelector) : null;
|
|
7891
|
+
if (targetElement) {
|
|
7892
|
+
if (this.options.debug) {
|
|
7893
|
+
this.log("[Metadata] Focusing element:", normalizedSelector);
|
|
7894
|
+
}
|
|
7895
|
+
if (targetElement.tabIndex === -1 && !targetElement.hasAttribute("tabindex")) {
|
|
7896
|
+
targetElement.setAttribute("tabindex", "-1");
|
|
7897
|
+
}
|
|
7898
|
+
this.setManagedTimeout(() => {
|
|
7899
|
+
targetElement.focus();
|
|
7900
|
+
targetElement.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
7901
|
+
}, 10);
|
|
7902
|
+
} else if (this.options.debug) {
|
|
7903
|
+
this.log("[Metadata] Element not found:", normalizedSelector || targetSelector);
|
|
7904
|
+
}
|
|
7905
|
+
this.emit("metadata:focus", {
|
|
7906
|
+
time: cue.startTime,
|
|
7907
|
+
target: targetSelector,
|
|
7908
|
+
selector: normalizedSelector,
|
|
7909
|
+
element: targetElement,
|
|
7910
|
+
text
|
|
7911
|
+
});
|
|
7912
|
+
if (normalizedSelector) {
|
|
7913
|
+
this.handleMetadataAlert(normalizedSelector, {
|
|
7914
|
+
element: targetElement,
|
|
7915
|
+
reason: "focus"
|
|
7916
|
+
});
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
const hashtags = text.match(/#[\w-]+/g);
|
|
7920
|
+
if (hashtags) {
|
|
7921
|
+
if (this.options.debug) {
|
|
7922
|
+
this.log("[Metadata] Hashtags found:", hashtags);
|
|
7923
|
+
}
|
|
7924
|
+
this.emit("metadata:hashtags", {
|
|
7925
|
+
time: cue.startTime,
|
|
7926
|
+
hashtags,
|
|
7927
|
+
text
|
|
7928
|
+
});
|
|
7929
|
+
this.handleMetadataHashtags(hashtags);
|
|
7930
|
+
}
|
|
7931
|
+
}
|
|
6519
7932
|
};
|
|
6520
7933
|
Player.instances = [];
|
|
6521
7934
|
|