vidply 1.0.8 → 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 +806 -70
- package/dist/vidply.esm.js.map +2 -2
- package/dist/vidply.esm.min.js +3 -3
- package/dist/vidply.esm.min.meta.json +7 -7
- package/dist/vidply.js +806 -70
- package/dist/vidply.js.map +2 -2
- package/dist/vidply.min.css +1 -1
- package/dist/vidply.min.js +3 -3
- package/dist/vidply.min.meta.json +7 -7
- package/package.json +1 -1
- package/src/controls/TranscriptManager.js +328 -27
- package/src/core/Player.js +682 -56
- 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) {
|
|
@@ -5472,12 +5705,21 @@ var Player = class extends EventEmitter {
|
|
|
5472
5705
|
this.audioDescriptionSourceElement = null;
|
|
5473
5706
|
this.originalAudioDescriptionSource = null;
|
|
5474
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();
|
|
5475
5715
|
this.container = null;
|
|
5476
5716
|
this.renderer = null;
|
|
5477
5717
|
this.controlBar = null;
|
|
5478
5718
|
this.captionManager = null;
|
|
5479
5719
|
this.keyboardManager = null;
|
|
5480
5720
|
this.settingsDialog = null;
|
|
5721
|
+
this.metadataCueChangeHandler = null;
|
|
5722
|
+
this.metadataAlertHandlers = /* @__PURE__ */ new Map();
|
|
5481
5723
|
this.init();
|
|
5482
5724
|
}
|
|
5483
5725
|
async init() {
|
|
@@ -5509,6 +5751,7 @@ var Player = class extends EventEmitter {
|
|
|
5509
5751
|
if (this.options.transcript || this.options.transcriptButton) {
|
|
5510
5752
|
this.transcriptManager = new TranscriptManager(this);
|
|
5511
5753
|
}
|
|
5754
|
+
this.setupMetadataHandling();
|
|
5512
5755
|
if (this.options.keyboard) {
|
|
5513
5756
|
this.keyboardManager = new KeyboardManager(this);
|
|
5514
5757
|
}
|
|
@@ -5594,6 +5837,8 @@ var Player = class extends EventEmitter {
|
|
|
5594
5837
|
if (this.element.tagName === "VIDEO") {
|
|
5595
5838
|
this.createPlayButtonOverlay();
|
|
5596
5839
|
}
|
|
5840
|
+
this.element.vidply = this;
|
|
5841
|
+
_Player.instances.push(this);
|
|
5597
5842
|
this.element.style.cursor = "pointer";
|
|
5598
5843
|
this.element.addEventListener("click", (e) => {
|
|
5599
5844
|
if (e.target === this.element) {
|
|
@@ -5626,7 +5871,7 @@ var Player = class extends EventEmitter {
|
|
|
5626
5871
|
if (!src) {
|
|
5627
5872
|
throw new Error("No media source found");
|
|
5628
5873
|
}
|
|
5629
|
-
const sourceElements = this.
|
|
5874
|
+
const sourceElements = this.sourceElements;
|
|
5630
5875
|
for (const sourceEl of sourceElements) {
|
|
5631
5876
|
const descSrc = sourceEl.getAttribute("data-desc-src");
|
|
5632
5877
|
const origSrc = sourceEl.getAttribute("data-orig-src");
|
|
@@ -5655,7 +5900,7 @@ var Player = class extends EventEmitter {
|
|
|
5655
5900
|
}
|
|
5656
5901
|
}
|
|
5657
5902
|
}
|
|
5658
|
-
const trackElements = this.
|
|
5903
|
+
const trackElements = this.trackElements;
|
|
5659
5904
|
trackElements.forEach((trackEl) => {
|
|
5660
5905
|
const trackKind = trackEl.getAttribute("kind");
|
|
5661
5906
|
const trackDescSrc = trackEl.getAttribute("data-desc-src");
|
|
@@ -5689,6 +5934,106 @@ var Player = class extends EventEmitter {
|
|
|
5689
5934
|
this.log(`Using ${renderer.name} renderer`);
|
|
5690
5935
|
this.renderer = new renderer(this);
|
|
5691
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
|
+
}
|
|
5692
6037
|
}
|
|
5693
6038
|
/**
|
|
5694
6039
|
* Load new media source (for playlists)
|
|
@@ -5704,8 +6049,9 @@ var Player = class extends EventEmitter {
|
|
|
5704
6049
|
if (this.renderer) {
|
|
5705
6050
|
this.pause();
|
|
5706
6051
|
}
|
|
5707
|
-
const existingTracks = this.
|
|
6052
|
+
const existingTracks = this.trackElements;
|
|
5708
6053
|
existingTracks.forEach((track) => track.remove());
|
|
6054
|
+
this.invalidateTrackCache();
|
|
5709
6055
|
this.element.src = config.src;
|
|
5710
6056
|
if (config.type) {
|
|
5711
6057
|
this.element.type = config.type;
|
|
@@ -5725,6 +6071,7 @@ var Player = class extends EventEmitter {
|
|
|
5725
6071
|
}
|
|
5726
6072
|
this.element.appendChild(track);
|
|
5727
6073
|
});
|
|
6074
|
+
this.invalidateTrackCache();
|
|
5728
6075
|
}
|
|
5729
6076
|
const shouldChangeRenderer = this.shouldChangeRenderer(config.src);
|
|
5730
6077
|
if (shouldChangeRenderer && this.renderer) {
|
|
@@ -5972,7 +6319,7 @@ var Player = class extends EventEmitter {
|
|
|
5972
6319
|
}
|
|
5973
6320
|
// Audio Description
|
|
5974
6321
|
async enableAudioDescription() {
|
|
5975
|
-
const hasSourceElementsWithDesc =
|
|
6322
|
+
const hasSourceElementsWithDesc = this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
5976
6323
|
const hasTracksWithDesc = this.audioDescriptionCaptionTracks.length > 0;
|
|
5977
6324
|
if (!this.audioDescriptionSrc && !hasSourceElementsWithDesc && !hasTracksWithDesc) {
|
|
5978
6325
|
console.warn("VidPly: No audio description source, source elements, or tracks provided");
|
|
@@ -5983,7 +6330,7 @@ var Player = class extends EventEmitter {
|
|
|
5983
6330
|
let swappedTracksForTranscript = [];
|
|
5984
6331
|
if (this.audioDescriptionSourceElement) {
|
|
5985
6332
|
const currentSrc = this.element.currentSrc || this.element.src;
|
|
5986
|
-
const sourceElements =
|
|
6333
|
+
const sourceElements = this.sourceElements;
|
|
5987
6334
|
let sourceElementToUpdate = null;
|
|
5988
6335
|
let descSrc = this.audioDescriptionSrc;
|
|
5989
6336
|
for (const sourceEl of sourceElements) {
|
|
@@ -6080,8 +6427,9 @@ var Player = class extends EventEmitter {
|
|
|
6080
6427
|
trackInfo.trackElement = newTrackElement;
|
|
6081
6428
|
});
|
|
6082
6429
|
this.element.load();
|
|
6430
|
+
this.invalidateTrackCache();
|
|
6083
6431
|
const setupNewTracks = () => {
|
|
6084
|
-
|
|
6432
|
+
this.setManagedTimeout(() => {
|
|
6085
6433
|
swappedTracksForTranscript.forEach((trackInfo) => {
|
|
6086
6434
|
const trackElement = trackInfo.trackElement;
|
|
6087
6435
|
const newTextTrack = trackElement.track;
|
|
@@ -6117,7 +6465,7 @@ var Player = class extends EventEmitter {
|
|
|
6117
6465
|
const skippedCount = validationResults.length - tracksToSwap.length;
|
|
6118
6466
|
}
|
|
6119
6467
|
}
|
|
6120
|
-
const allSourceElements =
|
|
6468
|
+
const allSourceElements = this.sourceElements;
|
|
6121
6469
|
const sourcesToUpdate = [];
|
|
6122
6470
|
allSourceElements.forEach((sourceEl) => {
|
|
6123
6471
|
const descSrcAttr = sourceEl.getAttribute("data-desc-src");
|
|
@@ -6295,7 +6643,7 @@ var Player = class extends EventEmitter {
|
|
|
6295
6643
|
}, 100);
|
|
6296
6644
|
}
|
|
6297
6645
|
}
|
|
6298
|
-
const fallbackSourceElements =
|
|
6646
|
+
const fallbackSourceElements = this.sourceElements;
|
|
6299
6647
|
const hasSourceElementsWithDesc2 = fallbackSourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6300
6648
|
if (hasSourceElementsWithDesc2) {
|
|
6301
6649
|
const fallbackSourcesToUpdate = [];
|
|
@@ -6343,6 +6691,7 @@ var Player = class extends EventEmitter {
|
|
|
6343
6691
|
this.element.appendChild(newSource);
|
|
6344
6692
|
});
|
|
6345
6693
|
this.element.load();
|
|
6694
|
+
this.invalidateTrackCache();
|
|
6346
6695
|
} else {
|
|
6347
6696
|
this.element.src = this.audioDescriptionSrc;
|
|
6348
6697
|
}
|
|
@@ -6357,7 +6706,7 @@ var Player = class extends EventEmitter {
|
|
|
6357
6706
|
if (this.element.tagName === "VIDEO" && currentTime === 0 && !wasPlaying) {
|
|
6358
6707
|
if (this.element.readyState >= 1) {
|
|
6359
6708
|
this.element.currentTime = 1e-3;
|
|
6360
|
-
|
|
6709
|
+
this.setManagedTimeout(() => {
|
|
6361
6710
|
this.element.currentTime = 0;
|
|
6362
6711
|
}, 10);
|
|
6363
6712
|
}
|
|
@@ -6397,7 +6746,8 @@ var Player = class extends EventEmitter {
|
|
|
6397
6746
|
const swappedTracks = typeof swappedTracksForTranscript !== "undefined" ? swappedTracksForTranscript : [];
|
|
6398
6747
|
if (swappedTracks.length > 0) {
|
|
6399
6748
|
const onMetadataLoaded = () => {
|
|
6400
|
-
|
|
6749
|
+
this.invalidateTrackCache();
|
|
6750
|
+
const allTextTracks = this.textTracks;
|
|
6401
6751
|
const freshTracks = swappedTracks.map((trackInfo) => {
|
|
6402
6752
|
const trackEl = trackInfo.trackElement;
|
|
6403
6753
|
const expectedSrc = trackEl.getAttribute("src");
|
|
@@ -6407,9 +6757,7 @@ var Player = class extends EventEmitter {
|
|
|
6407
6757
|
if (!foundTrack) {
|
|
6408
6758
|
foundTrack = allTextTracks.find((track) => {
|
|
6409
6759
|
if (track.language === srclang && (track.kind === kind || kind === "captions" && track.kind === "subtitles")) {
|
|
6410
|
-
const trackElementForTrack =
|
|
6411
|
-
(el) => el.track === track
|
|
6412
|
-
);
|
|
6760
|
+
const trackElementForTrack = this.findTrackElement(track);
|
|
6413
6761
|
if (trackElementForTrack) {
|
|
6414
6762
|
const actualSrc = trackElementForTrack.getAttribute("src");
|
|
6415
6763
|
if (actualSrc === expectedSrc) {
|
|
@@ -6421,9 +6769,7 @@ var Player = class extends EventEmitter {
|
|
|
6421
6769
|
});
|
|
6422
6770
|
}
|
|
6423
6771
|
if (foundTrack) {
|
|
6424
|
-
const trackElement =
|
|
6425
|
-
(el) => el.track === foundTrack
|
|
6426
|
-
);
|
|
6772
|
+
const trackElement = this.findTrackElement(foundTrack);
|
|
6427
6773
|
if (trackElement && trackElement.getAttribute("src") !== expectedSrc) {
|
|
6428
6774
|
return null;
|
|
6429
6775
|
}
|
|
@@ -6431,7 +6777,7 @@ var Player = class extends EventEmitter {
|
|
|
6431
6777
|
return foundTrack;
|
|
6432
6778
|
}).filter(Boolean);
|
|
6433
6779
|
if (freshTracks.length === 0) {
|
|
6434
|
-
|
|
6780
|
+
this.setManagedTimeout(() => {
|
|
6435
6781
|
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6436
6782
|
this.transcriptManager.loadTranscriptData();
|
|
6437
6783
|
}
|
|
@@ -6447,14 +6793,13 @@ var Player = class extends EventEmitter {
|
|
|
6447
6793
|
const checkLoaded = () => {
|
|
6448
6794
|
loadedCount++;
|
|
6449
6795
|
if (loadedCount >= freshTracks.length) {
|
|
6450
|
-
|
|
6796
|
+
this.setManagedTimeout(() => {
|
|
6451
6797
|
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6452
|
-
|
|
6798
|
+
this.invalidateTrackCache();
|
|
6799
|
+
const allTextTracks2 = this.textTracks;
|
|
6453
6800
|
const swappedTrackSrcs = swappedTracks.map((t) => t.describedSrc);
|
|
6454
6801
|
const hasCorrectTracks = freshTracks.some((track) => {
|
|
6455
|
-
const trackEl =
|
|
6456
|
-
(el) => el.track === track
|
|
6457
|
-
);
|
|
6802
|
+
const trackEl = this.findTrackElement(track);
|
|
6458
6803
|
return trackEl && swappedTrackSrcs.includes(trackEl.getAttribute("src"));
|
|
6459
6804
|
});
|
|
6460
6805
|
if (hasCorrectTracks || freshTracks.length > 0) {
|
|
@@ -6468,9 +6813,7 @@ var Player = class extends EventEmitter {
|
|
|
6468
6813
|
if (track.mode === "disabled") {
|
|
6469
6814
|
track.mode = "hidden";
|
|
6470
6815
|
}
|
|
6471
|
-
const trackElementForTrack =
|
|
6472
|
-
(el) => el.track === track
|
|
6473
|
-
);
|
|
6816
|
+
const trackElementForTrack = this.findTrackElement(track);
|
|
6474
6817
|
const actualSrc = trackElementForTrack ? trackElementForTrack.getAttribute("src") : null;
|
|
6475
6818
|
const expectedTrackInfo = swappedTracks.find((t) => {
|
|
6476
6819
|
const tEl = t.trackElement;
|
|
@@ -6488,10 +6831,10 @@ var Player = class extends EventEmitter {
|
|
|
6488
6831
|
track.mode = "hidden";
|
|
6489
6832
|
}
|
|
6490
6833
|
const onTrackLoad = () => {
|
|
6491
|
-
|
|
6834
|
+
this.setManagedTimeout(checkLoaded, 300);
|
|
6492
6835
|
};
|
|
6493
6836
|
if (track.readyState >= 2) {
|
|
6494
|
-
|
|
6837
|
+
this.setManagedTimeout(() => {
|
|
6495
6838
|
if (track.cues && track.cues.length > 0) {
|
|
6496
6839
|
checkLoaded();
|
|
6497
6840
|
} else {
|
|
@@ -6508,12 +6851,12 @@ var Player = class extends EventEmitter {
|
|
|
6508
6851
|
});
|
|
6509
6852
|
};
|
|
6510
6853
|
const waitForTracks = () => {
|
|
6511
|
-
|
|
6854
|
+
this.setManagedTimeout(() => {
|
|
6512
6855
|
if (this.element.readyState >= 1) {
|
|
6513
6856
|
onMetadataLoaded();
|
|
6514
6857
|
} else {
|
|
6515
6858
|
this.element.addEventListener("loadedmetadata", onMetadataLoaded, { once: true });
|
|
6516
|
-
|
|
6859
|
+
this.setManagedTimeout(onMetadataLoaded, 2e3);
|
|
6517
6860
|
}
|
|
6518
6861
|
}, 500);
|
|
6519
6862
|
};
|
|
@@ -6547,7 +6890,7 @@ var Player = class extends EventEmitter {
|
|
|
6547
6890
|
}
|
|
6548
6891
|
});
|
|
6549
6892
|
}
|
|
6550
|
-
const allSourceElements =
|
|
6893
|
+
const allSourceElements = this.sourceElements;
|
|
6551
6894
|
const hasSourceElementsToSwap = allSourceElements.some((el) => el.getAttribute("data-orig-src"));
|
|
6552
6895
|
if (hasSourceElementsToSwap) {
|
|
6553
6896
|
const sourcesToRestore = [];
|
|
@@ -6610,7 +6953,7 @@ var Player = class extends EventEmitter {
|
|
|
6610
6953
|
this.play();
|
|
6611
6954
|
}
|
|
6612
6955
|
if (this.transcriptManager && this.transcriptManager.isVisible) {
|
|
6613
|
-
|
|
6956
|
+
this.setManagedTimeout(() => {
|
|
6614
6957
|
if (this.transcriptManager && this.transcriptManager.loadTranscriptData) {
|
|
6615
6958
|
this.transcriptManager.loadTranscriptData();
|
|
6616
6959
|
}
|
|
@@ -6620,16 +6963,37 @@ var Player = class extends EventEmitter {
|
|
|
6620
6963
|
this.emit("audiodescriptiondisabled");
|
|
6621
6964
|
}
|
|
6622
6965
|
async toggleAudioDescription() {
|
|
6623
|
-
const
|
|
6624
|
-
const
|
|
6625
|
-
const hasAudioDescriptionSrc = this.audioDescriptionSrc || Array.from(this.element.querySelectorAll("source")).some((el) => el.getAttribute("data-desc-src"));
|
|
6966
|
+
const descriptionTrack = this.findTextTrack("descriptions");
|
|
6967
|
+
const hasAudioDescriptionSrc = this.audioDescriptionSrc || this.sourceElements.some((el) => el.getAttribute("data-desc-src"));
|
|
6626
6968
|
if (descriptionTrack && hasAudioDescriptionSrc) {
|
|
6627
6969
|
if (this.state.audioDescriptionEnabled) {
|
|
6628
6970
|
descriptionTrack.mode = "hidden";
|
|
6629
6971
|
await this.disableAudioDescription();
|
|
6630
6972
|
} else {
|
|
6631
6973
|
await this.enableAudioDescription();
|
|
6632
|
-
|
|
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
|
+
}
|
|
6633
6997
|
}
|
|
6634
6998
|
} else if (descriptionTrack) {
|
|
6635
6999
|
if (descriptionTrack.mode === "showing") {
|
|
@@ -7042,9 +7406,25 @@ var Player = class extends EventEmitter {
|
|
|
7042
7406
|
}
|
|
7043
7407
|
}
|
|
7044
7408
|
// Logging
|
|
7045
|
-
log(
|
|
7046
|
-
if (this.options.debug) {
|
|
7047
|
-
|
|
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);
|
|
7048
7428
|
}
|
|
7049
7429
|
}
|
|
7050
7430
|
// Setup responsive handlers
|
|
@@ -7104,7 +7484,7 @@ var Player = class extends EventEmitter {
|
|
|
7104
7484
|
this.controlBar.updateFullscreenButton();
|
|
7105
7485
|
}
|
|
7106
7486
|
if (this.signLanguageWrapper && this.signLanguageWrapper.style.display !== "none") {
|
|
7107
|
-
|
|
7487
|
+
this.setManagedTimeout(() => {
|
|
7108
7488
|
requestAnimationFrame(() => {
|
|
7109
7489
|
this.storage.saveSignLanguagePreferences({ size: null });
|
|
7110
7490
|
this.signLanguageDesiredPosition = "bottom-right";
|
|
@@ -7167,12 +7547,368 @@ var Player = class extends EventEmitter {
|
|
|
7167
7547
|
document.removeEventListener("MSFullscreenChange", this.fullscreenChangeHandler);
|
|
7168
7548
|
this.fullscreenChangeHandler = null;
|
|
7169
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
|
+
}
|
|
7170
7568
|
if (this.container && this.container.parentNode) {
|
|
7171
7569
|
this.container.parentNode.insertBefore(this.element, this.container);
|
|
7172
7570
|
this.container.parentNode.removeChild(this.container);
|
|
7173
7571
|
}
|
|
7174
7572
|
this.removeAllListeners();
|
|
7175
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
|
+
}
|
|
7176
7912
|
};
|
|
7177
7913
|
Player.instances = [];
|
|
7178
7914
|
|