vidply 1.1.19 → 1.2.1
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/README.md +13 -6
- package/dist/dev/{vidply.AudioDescriptionManager-NRMNKZAL.js → vidply.AudioDescriptionManager-6RFVF27Z.js} +281 -23
- package/dist/dev/vidply.AudioDescriptionManager-6RFVF27Z.js.map +7 -0
- package/dist/dev/vidply.CaptionStyleMenu-R2SCQ6VP.js +300 -0
- package/dist/dev/vidply.CaptionStyleMenu-R2SCQ6VP.js.map +7 -0
- package/dist/dev/{vidply.DASHRenderer-SYXX6RRS.js → vidply.DASHRenderer-EOVEW73H.js} +39 -32
- package/dist/dev/vidply.DASHRenderer-EOVEW73H.js.map +7 -0
- package/dist/dev/{vidply.FloatingPlayerManager-HJVGUQLT.js → vidply.FloatingPlayerManager-V5XQWS75.js} +10 -5
- package/dist/dev/vidply.FloatingPlayerManager-V5XQWS75.js.map +7 -0
- package/dist/dev/{vidply.HLSRenderer-YAFEUBMC.js → vidply.HLSRenderer-ZBEFVH5W.js} +80 -45
- package/dist/dev/vidply.HLSRenderer-ZBEFVH5W.js.map +7 -0
- package/dist/dev/vidply.HTML5Renderer-P7ICKYU4.js +12 -0
- package/dist/dev/{vidply.SignLanguageManager-ZZ6DIDTP.js → vidply.SignLanguageManager-7IAJOVF6.js} +14 -13
- package/dist/dev/vidply.SignLanguageManager-7IAJOVF6.js.map +7 -0
- package/dist/dev/{vidply.SoundCloudRenderer-OFBGL7UV.js → vidply.SoundCloudRenderer-OA7QQYGN.js} +30 -18
- package/dist/dev/vidply.SoundCloudRenderer-OA7QQYGN.js.map +7 -0
- package/dist/dev/{vidply.TranscriptManager-4553RRA6.js → vidply.TranscriptManager-BPM3T23L.js} +24 -21
- package/dist/dev/vidply.TranscriptManager-BPM3T23L.js.map +7 -0
- package/dist/dev/{vidply.VimeoRenderer-EVW2RNSF.js → vidply.VimeoRenderer-BRHFYPNJ.js} +8 -9
- package/dist/dev/vidply.VimeoRenderer-BRHFYPNJ.js.map +7 -0
- package/dist/dev/{vidply.YouTubeRenderer-PZHT3GMF.js → vidply.YouTubeRenderer-HRTN4EJ5.js} +10 -25
- package/dist/dev/vidply.YouTubeRenderer-HRTN4EJ5.js.map +7 -0
- package/dist/dev/vidply.chunk-5IK3IC5V.js +57 -0
- package/dist/dev/vidply.chunk-5IK3IC5V.js.map +7 -0
- package/dist/dev/{vidply.chunk-RQGZAPWY.js → vidply.chunk-C55OBT5H.js} +5 -2
- package/dist/dev/vidply.chunk-C55OBT5H.js.map +7 -0
- package/dist/dev/vidply.chunk-HOYEMJSD.js +87 -0
- package/dist/dev/vidply.chunk-HOYEMJSD.js.map +7 -0
- package/dist/dev/{vidply.chunk-24QLI7ZK.js → vidply.chunk-JAKZNGOR.js} +2 -2
- package/dist/dev/vidply.chunk-JAKZNGOR.js.map +7 -0
- package/dist/dev/{vidply.chunk-AQDY6B5B.js → vidply.chunk-LDVFAFGC.js} +15 -48
- package/dist/dev/vidply.chunk-LDVFAFGC.js.map +7 -0
- package/dist/dev/{vidply.chunk-KMACPXZ5.js → vidply.chunk-NSMAZCMB.js} +10 -9
- package/dist/dev/vidply.chunk-NSMAZCMB.js.map +7 -0
- package/dist/dev/{vidply.chunk-MQAA3FKI.js → vidply.chunk-RQG3MA7W.js} +2 -2
- package/dist/dev/vidply.chunk-RQG3MA7W.js.map +7 -0
- package/dist/dev/{vidply.chunk-QF77EYBM.js → vidply.chunk-UHMVDBDB.js} +7 -4
- package/dist/dev/{vidply.chunk-QF77EYBM.js.map → vidply.chunk-UHMVDBDB.js.map} +2 -2
- package/dist/dev/{vidply.chunk-TM5HFGG5.js → vidply.chunk-W5BR65JY.js} +35 -19
- package/dist/dev/vidply.chunk-W5BR65JY.js.map +7 -0
- package/dist/dev/{vidply.chunk-PTYRD6ZP.js → vidply.chunk-XAK3DPTO.js} +28 -24
- package/dist/dev/vidply.chunk-XAK3DPTO.js.map +7 -0
- package/dist/dev/{vidply.chunk-NUTGO3RK.js → vidply.chunk-YVCYQZJI.js} +6 -6
- package/dist/dev/vidply.chunk-YVCYQZJI.js.map +7 -0
- package/dist/dev/{vidply.de-27ZM6TOB.js → vidply.de-5W5LBFSJ.js} +2 -2
- package/dist/dev/{vidply.es-XNGJJLWC.js → vidply.es-SHDAT6L6.js} +2 -2
- package/dist/dev/vidply.esm.js +270 -391
- package/dist/dev/vidply.esm.js.map +2 -2
- package/dist/dev/{vidply.fr-AVZKPUBH.js → vidply.fr-2JANOMNB.js} +2 -2
- package/dist/dev/{vidply.ja-IZK2UXDC.js → vidply.ja-WWMSTEJH.js} +2 -2
- package/dist/legacy/vidply.js +1185 -565
- package/dist/legacy/vidply.js.map +4 -4
- package/dist/legacy/vidply.min.js +2 -2
- package/dist/legacy/vidply.min.meta.json +157 -56
- package/dist/prod/vidply.AudioDescriptionManager-YTDVDQBO.min.js +6 -0
- package/dist/prod/vidply.CaptionStyleMenu-2AFF3HF2.min.js +6 -0
- package/dist/prod/vidply.DASHRenderer-URDOTOKS.min.js +6 -0
- package/dist/prod/{vidply.FloatingPlayerManager-FNM5V76W.min.js → vidply.FloatingPlayerManager-QUBFK7CQ.min.js} +2 -2
- package/dist/prod/vidply.HLSRenderer-46KNUVAB.min.js +6 -0
- package/dist/prod/vidply.HTML5Renderer-7TQDMIGY.min.js +6 -0
- package/dist/prod/vidply.SignLanguageManager-H4C36EVL.min.js +6 -0
- package/dist/prod/vidply.SoundCloudRenderer-OYSWMW7D.min.js +6 -0
- package/dist/prod/vidply.TranscriptManager-BA3DAS63.min.js +6 -0
- package/dist/prod/vidply.VimeoRenderer-NF3COTHU.min.js +6 -0
- package/dist/prod/vidply.YouTubeRenderer-UCQKYIJ4.min.js +6 -0
- package/dist/prod/vidply.chunk-2HNCP64N.min.js +6 -0
- package/dist/prod/vidply.chunk-6ECBIYVI.min.js +6 -0
- package/dist/prod/vidply.chunk-7S5AFTST.min.js +6 -0
- package/dist/prod/vidply.chunk-EUOA2DJK.min.js +6 -0
- package/dist/prod/vidply.chunk-G6KGXNEW.min.js +6 -0
- package/dist/prod/{vidply.chunk-WE6VR5E2.min.js → vidply.chunk-JPMAK3NS.min.js} +1 -1
- package/dist/prod/{vidply.chunk-D7PUX3IJ.min.js → vidply.chunk-L4O44K63.min.js} +1 -1
- package/dist/prod/vidply.chunk-M76ABMKB.min.js +6 -0
- package/dist/prod/vidply.chunk-WRVDVD33.min.js +6 -0
- package/dist/prod/{vidply.chunk-HKXIYL5T.min.js → vidply.chunk-X2FQEQYR.min.js} +2 -2
- package/dist/prod/vidply.chunk-XXUH7ADU.min.js +6 -0
- package/dist/prod/{vidply.de-RZ2FVGU4.min.js → vidply.de-JMCATTKZ.min.js} +1 -1
- package/dist/prod/{vidply.es-KUCGSMAA.min.js → vidply.es-YVG37V7Z.min.js} +1 -1
- package/dist/prod/vidply.esm.min.js +2 -2
- package/dist/prod/{vidply.fr-2FOZ6M7L.min.js → vidply.fr-OGKGN2R4.min.js} +1 -1
- package/dist/prod/{vidply.ja-5H6AMU2R.min.js → vidply.ja-Z6DS7QLS.min.js} +1 -1
- package/dist/vidply.css +5796 -5892
- package/dist/vidply.esm.min.meta.json +430 -241
- package/dist/vidply.min.css +1 -1
- package/package.json +95 -92
- package/src/controls/CaptionManager.ts +11 -4
- package/src/controls/CaptionStyleMenu.ts +370 -0
- package/src/controls/ControlBar.ts +93 -403
- package/src/controls/KeyboardManager.ts +78 -2
- package/src/controls/SettingsDialog.ts +32 -2
- package/src/controls/TranscriptManager.ts +12 -10
- package/src/core/AudioDescriptionManager.ts +114 -23
- package/src/core/DescriptionSpeechManager.ts +232 -0
- package/src/core/FloatingPlayerManager.ts +674 -663
- package/src/core/MetadataAlertsManager.ts +2 -1
- package/src/core/Player.ts +97 -33
- package/src/core/PseudoFullscreen.ts +4 -2
- package/src/core/SignLanguageManager.ts +9 -7
- package/src/features/PlaylistManager.ts +56 -30
- package/src/i18n/translations.ts +28 -28
- package/src/icons/Icons.ts +167 -167
- package/src/index.ts +4 -1
- package/src/renderers/DASHRenderer.ts +39 -30
- package/src/renderers/HLSRenderer.ts +99 -53
- package/src/renderers/HTML5Renderer.ts +389 -382
- package/src/renderers/SoundCloudRenderer.ts +33 -18
- package/src/renderers/VimeoRenderer.ts +298 -298
- package/src/renderers/YouTubeRenderer.ts +290 -305
- package/src/styles/vidply.css +5796 -5892
- package/src/types/events.ts +2 -0
- package/src/types/globals.d.ts +313 -279
- package/src/types/index.ts +4 -4
- package/src/types/options.ts +3 -0
- package/src/types/renderer.ts +58 -63
- package/src/types/state.ts +24 -24
- package/src/utils/DOMUtils.ts +4 -3
- package/src/utils/DownloadInfo.ts +2 -2
- package/src/utils/DraggableResizable.ts +997 -994
- package/src/utils/EventEmitter.ts +59 -59
- package/src/utils/FocusUtils.ts +26 -26
- package/src/utils/FormUtils.ts +95 -95
- package/src/utils/MenuFactory.ts +483 -461
- package/src/utils/MenuUtils.ts +190 -172
- package/src/utils/PerformanceUtils.ts +63 -40
- package/src/utils/ResizeHandles.ts +58 -58
- package/src/utils/ScriptLoader.ts +137 -0
- package/src/utils/SettingsMenuFactory.ts +289 -276
- package/src/utils/StorageManager.ts +4 -1
- package/src/utils/TimeUtils.ts +67 -67
- package/src/utils/WindowComponents.ts +250 -250
- package/dist/dev/vidply.AudioDescriptionManager-NRMNKZAL.js.map +0 -7
- package/dist/dev/vidply.DASHRenderer-SYXX6RRS.js.map +0 -7
- package/dist/dev/vidply.FloatingPlayerManager-HJVGUQLT.js.map +0 -7
- package/dist/dev/vidply.HLSRenderer-YAFEUBMC.js.map +0 -7
- package/dist/dev/vidply.HTML5Renderer-ITDUMSTL.js +0 -12
- package/dist/dev/vidply.SignLanguageManager-ZZ6DIDTP.js.map +0 -7
- package/dist/dev/vidply.SoundCloudRenderer-OFBGL7UV.js.map +0 -7
- package/dist/dev/vidply.TranscriptManager-4553RRA6.js.map +0 -7
- package/dist/dev/vidply.VimeoRenderer-EVW2RNSF.js.map +0 -7
- package/dist/dev/vidply.YouTubeRenderer-PZHT3GMF.js.map +0 -7
- package/dist/dev/vidply.chunk-24QLI7ZK.js.map +0 -7
- package/dist/dev/vidply.chunk-AQDY6B5B.js.map +0 -7
- package/dist/dev/vidply.chunk-KMACPXZ5.js.map +0 -7
- package/dist/dev/vidply.chunk-MQAA3FKI.js.map +0 -7
- package/dist/dev/vidply.chunk-NUTGO3RK.js.map +0 -7
- package/dist/dev/vidply.chunk-PTYRD6ZP.js.map +0 -7
- package/dist/dev/vidply.chunk-RQGZAPWY.js.map +0 -7
- package/dist/dev/vidply.chunk-TM5HFGG5.js.map +0 -7
- package/dist/prod/vidply.AudioDescriptionManager-QVL4TD4R.min.js +0 -6
- package/dist/prod/vidply.DASHRenderer-DVBO6JRL.min.js +0 -6
- package/dist/prod/vidply.HLSRenderer-AHFQI64X.min.js +0 -6
- package/dist/prod/vidply.HTML5Renderer-WNGD745H.min.js +0 -6
- package/dist/prod/vidply.SignLanguageManager-E65WKF37.min.js +0 -6
- package/dist/prod/vidply.SoundCloudRenderer-XD3TMM5O.min.js +0 -6
- package/dist/prod/vidply.TranscriptManager-6JM6CJTM.min.js +0 -6
- package/dist/prod/vidply.VimeoRenderer-7L6RWMWE.min.js +0 -6
- package/dist/prod/vidply.YouTubeRenderer-ZAQTBKM4.min.js +0 -6
- package/dist/prod/vidply.chunk-3L3VQFPN.min.js +0 -6
- package/dist/prod/vidply.chunk-5644WA3F.min.js +0 -6
- package/dist/prod/vidply.chunk-6DG7I4DA.min.js +0 -6
- package/dist/prod/vidply.chunk-OFMLXH4A.min.js +0 -6
- package/dist/prod/vidply.chunk-VX4CE6WF.min.js +0 -6
- package/dist/prod/vidply.chunk-W5YQ5TEO.min.js +0 -6
- /package/dist/dev/{vidply.HTML5Renderer-ITDUMSTL.js.map → vidply.HTML5Renderer-P7ICKYU4.js.map} +0 -0
- /package/dist/dev/{vidply.de-27ZM6TOB.js.map → vidply.de-5W5LBFSJ.js.map} +0 -0
- /package/dist/dev/{vidply.es-XNGJJLWC.js.map → vidply.es-SHDAT6L6.js.map} +0 -0
- /package/dist/dev/{vidply.fr-AVZKPUBH.js.map → vidply.fr-2JANOMNB.js.map} +0 -0
- /package/dist/dev/{vidply.ja-IZK2UXDC.js.map → vidply.ja-WWMSTEJH.js.map} +0 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ internationalized with support for 5 languages and complete WCAG 2.2 AA complian
|
|
|
10
10
|

|
|
11
11
|

|
|
12
12
|

|
|
13
|
-

|
|
14
14
|
|
|
15
15
|
## Live Demos
|
|
16
16
|
|
|
@@ -68,15 +68,22 @@ Try VidPly in action:
|
|
|
68
68
|
### Accessibility Features (WCAG 2.2 AA Compliant)
|
|
69
69
|
|
|
70
70
|
- **Full Keyboard Navigation** - All features accessible via keyboard, custom shortcuts, menu navigation with Arrow keys
|
|
71
|
-
-
|
|
72
|
-
- **
|
|
71
|
+
(non-interactive/disabled menu items are excluded from roving focus)
|
|
72
|
+
- **Screen Reader Support** - Complete ARIA labels (`aria-controls`, `aria-expanded`, `aria-haspopup`), live regions;
|
|
73
|
+
single-select menus (speed/quality/captions) use `role="menuitemradio"` + `aria-checked`, and both mouse- and
|
|
74
|
+
keyboard-driven play/pause, caption and volume changes are announced
|
|
75
|
+
- **Interactive Transcripts** - Click-to-seek and auto-scroll with proper semantic HTML and a focus trap while open
|
|
73
76
|
- **Sign Language Overlay** - Picture-in-picture with drag/resize, keyboard accessible
|
|
74
77
|
- **Audio Description** - Alternate audio track with visual content descriptions
|
|
75
78
|
- **Caption Styling** - Fully customizable (font, size, color, opacity, edge style)
|
|
76
79
|
- **High Contrast Mode** - Windows HCM support, color-independent design
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
79
|
-
|
|
80
|
+
- **Reduced Motion** - Smooth scrolling and animations honor `prefers-reduced-motion` (SC 2.3.3)
|
|
81
|
+
- **Zoom & Reflow** - Pinch-zoom is preserved even in (pseudo-)fullscreen; no fixed `maximum-scale` lock (SC 1.4.4 /
|
|
82
|
+
1.4.10)
|
|
83
|
+
- **Focus Management** - Logical focus order, programmatic focus handling, visible indicators, focus traps in the
|
|
84
|
+
transcript window and settings dialog
|
|
85
|
+
- **Touch Accessibility** - Buttons, resize handles and slider thumbs sized at or above the WCAG 2.2 AA 24×24
|
|
86
|
+
CSS-pixel minimum (SC 2.5.8); swipeable interfaces
|
|
80
87
|
|
|
81
88
|
### Captions & Subtitles
|
|
82
89
|
|
|
@@ -1,13 +1,193 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* VidPly v1.1
|
|
2
|
+
* VidPly v1.2.1 - Universal, Accessible Video Player
|
|
3
3
|
* (c) 2026 Matthias Peltzer
|
|
4
4
|
* Released under GPL-2.0-or-later License
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
CaptionManager
|
|
8
|
-
} from "./vidply.chunk-
|
|
9
|
-
import "./vidply.chunk-
|
|
10
|
-
import "./vidply.chunk-
|
|
8
|
+
} from "./vidply.chunk-LDVFAFGC.js";
|
|
9
|
+
import "./vidply.chunk-UHMVDBDB.js";
|
|
10
|
+
import "./vidply.chunk-5IK3IC5V.js";
|
|
11
|
+
import "./vidply.chunk-NSMAZCMB.js";
|
|
12
|
+
|
|
13
|
+
// src/core/DescriptionSpeechManager.ts
|
|
14
|
+
var DescriptionSpeechManager = class {
|
|
15
|
+
player;
|
|
16
|
+
enabled = false;
|
|
17
|
+
descriptionTrack = null;
|
|
18
|
+
cueChangeHandler = null;
|
|
19
|
+
seekedHandler = null;
|
|
20
|
+
wasPlayingBeforeCue = false;
|
|
21
|
+
speaking = false;
|
|
22
|
+
lastSpokenCueKey = null;
|
|
23
|
+
_pendingUtterance = null;
|
|
24
|
+
constructor(player) {
|
|
25
|
+
this.player = player;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Whether speech synthesis is available and enabled in player options.
|
|
29
|
+
*/
|
|
30
|
+
canUseSpeech() {
|
|
31
|
+
if (this.player.options.audioDescriptionSpeech === false) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return typeof window !== "undefined" && "speechSynthesis" in window;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the descriptions track, preferring the active caption language.
|
|
38
|
+
*/
|
|
39
|
+
findDescriptionTrack() {
|
|
40
|
+
const textTracks = this.player.element ? Array.from(this.player.element.textTracks || []) : [];
|
|
41
|
+
const descriptionsTracks = textTracks.filter(
|
|
42
|
+
(track) => track.kind === "descriptions" && !track._vidplyStale
|
|
43
|
+
);
|
|
44
|
+
if (descriptionsTracks.length === 0) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const captionLang = this.player.captionManager?.currentTrack?.track?.language;
|
|
48
|
+
if (captionLang) {
|
|
49
|
+
const match = descriptionsTracks.find((track) => track.language === captionLang);
|
|
50
|
+
if (match) {
|
|
51
|
+
return match;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return descriptionsTracks[0] ?? null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Enable VTT speech mode: wire cuechange/seeked listeners.
|
|
58
|
+
*/
|
|
59
|
+
enable() {
|
|
60
|
+
const track = this.findDescriptionTrack();
|
|
61
|
+
if (!track) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (!this.canUseSpeech()) {
|
|
65
|
+
track.mode = "showing";
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
this.descriptionTrack = track;
|
|
69
|
+
track.mode = "hidden";
|
|
70
|
+
this.cueChangeHandler = () => {
|
|
71
|
+
this.handleCueChange();
|
|
72
|
+
};
|
|
73
|
+
track.addEventListener("cuechange", this.cueChangeHandler);
|
|
74
|
+
this.seekedHandler = () => {
|
|
75
|
+
this.cancelSpeech();
|
|
76
|
+
this.lastSpokenCueKey = null;
|
|
77
|
+
this.handleCueChange();
|
|
78
|
+
};
|
|
79
|
+
this.player.on("seeked", this.seekedHandler);
|
|
80
|
+
this.enabled = true;
|
|
81
|
+
this.handleCueChange();
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Disable VTT speech mode and restore track state.
|
|
86
|
+
*/
|
|
87
|
+
disable() {
|
|
88
|
+
this.cancelSpeech();
|
|
89
|
+
this.lastSpokenCueKey = null;
|
|
90
|
+
this.enabled = false;
|
|
91
|
+
if (this.descriptionTrack && this.cueChangeHandler) {
|
|
92
|
+
this.descriptionTrack.removeEventListener("cuechange", this.cueChangeHandler);
|
|
93
|
+
}
|
|
94
|
+
if (this.seekedHandler) {
|
|
95
|
+
this.player.off("seeked", this.seekedHandler);
|
|
96
|
+
}
|
|
97
|
+
if (this.descriptionTrack) {
|
|
98
|
+
this.descriptionTrack.mode = "hidden";
|
|
99
|
+
}
|
|
100
|
+
this.cueChangeHandler = null;
|
|
101
|
+
this.seekedHandler = null;
|
|
102
|
+
this.descriptionTrack = null;
|
|
103
|
+
this.wasPlayingBeforeCue = false;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Handle active description cues on the wired track.
|
|
107
|
+
*/
|
|
108
|
+
handleCueChange() {
|
|
109
|
+
if (!this.enabled || !this.descriptionTrack || !this.canUseSpeech()) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const activeCues = this.descriptionTrack.activeCues;
|
|
113
|
+
if (!activeCues || activeCues.length === 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const cue = activeCues[0];
|
|
117
|
+
const text = cue.text?.trim();
|
|
118
|
+
if (!text) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const cueKey = `${cue.startTime}:${cue.endTime}:${text}`;
|
|
122
|
+
if (this.speaking && this.lastSpokenCueKey === cueKey) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (this.lastSpokenCueKey === cueKey && !this.player.state.playing) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.speakCue(cue, cueKey);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Pause playback and speak a description cue.
|
|
132
|
+
*/
|
|
133
|
+
speakCue(cue, cueKey) {
|
|
134
|
+
if (!this.canUseSpeech()) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (this.player.state.paused && !this.speaking && !this.wasPlayingBeforeCue) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.cancelSpeech();
|
|
141
|
+
this.wasPlayingBeforeCue = this.player.state.playing;
|
|
142
|
+
if (this.wasPlayingBeforeCue) {
|
|
143
|
+
this.player.pause();
|
|
144
|
+
}
|
|
145
|
+
this.speaking = true;
|
|
146
|
+
this.lastSpokenCueKey = cueKey;
|
|
147
|
+
const detail = {
|
|
148
|
+
time: cue.startTime,
|
|
149
|
+
endTime: cue.endTime,
|
|
150
|
+
text: cue.text.trim(),
|
|
151
|
+
cue
|
|
152
|
+
};
|
|
153
|
+
this.player.emit("audiodescriptioncuestart", detail);
|
|
154
|
+
const utterance = new SpeechSynthesisUtterance(detail.text);
|
|
155
|
+
const lang = this.descriptionTrack?.language || this.player.options.language || "en";
|
|
156
|
+
utterance.lang = lang;
|
|
157
|
+
const finish = () => {
|
|
158
|
+
if (this._pendingUtterance !== utterance) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this._pendingUtterance = null;
|
|
162
|
+
this.speaking = false;
|
|
163
|
+
this.player.emit("audiodescriptioncueend", detail);
|
|
164
|
+
const extended = this.player.options.audioDescriptionExtended !== false;
|
|
165
|
+
const shouldResume = extended ? this.wasPlayingBeforeCue : this.wasPlayingBeforeCue && this.player.state.currentTime < cue.endTime;
|
|
166
|
+
if (shouldResume && this.enabled) {
|
|
167
|
+
void this.player.play();
|
|
168
|
+
}
|
|
169
|
+
this.wasPlayingBeforeCue = false;
|
|
170
|
+
};
|
|
171
|
+
utterance.onend = finish;
|
|
172
|
+
utterance.onerror = finish;
|
|
173
|
+
this._pendingUtterance = utterance;
|
|
174
|
+
window.speechSynthesis.speak(utterance);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Cancel any in-progress speech synthesis.
|
|
178
|
+
*/
|
|
179
|
+
cancelSpeech() {
|
|
180
|
+
if (typeof window !== "undefined" && window.speechSynthesis) {
|
|
181
|
+
window.speechSynthesis.cancel();
|
|
182
|
+
}
|
|
183
|
+
this._pendingUtterance = null;
|
|
184
|
+
this.speaking = false;
|
|
185
|
+
this.wasPlayingBeforeCue = false;
|
|
186
|
+
}
|
|
187
|
+
destroy() {
|
|
188
|
+
this.disable();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
11
191
|
|
|
12
192
|
// src/core/AudioDescriptionManager.ts
|
|
13
193
|
var AudioDescriptionManager = class {
|
|
@@ -18,6 +198,7 @@ var AudioDescriptionManager = class {
|
|
|
18
198
|
originalSource;
|
|
19
199
|
sourceElement;
|
|
20
200
|
src;
|
|
201
|
+
speechManager;
|
|
21
202
|
constructor(player) {
|
|
22
203
|
this.player = player;
|
|
23
204
|
this.enabled = false;
|
|
@@ -26,6 +207,49 @@ var AudioDescriptionManager = class {
|
|
|
26
207
|
this.sourceElement = null;
|
|
27
208
|
this.originalSource = null;
|
|
28
209
|
this.captionTracks = [];
|
|
210
|
+
this.speechManager = null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Whether a described video source swap is configured.
|
|
214
|
+
*/
|
|
215
|
+
_hasSwapSource() {
|
|
216
|
+
const hasSourceElementsWithDesc = this.player.sourceElements.some(
|
|
217
|
+
(el) => el.getAttribute("data-desc-src")
|
|
218
|
+
);
|
|
219
|
+
return Boolean(this.src || hasSourceElementsWithDesc);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Whether a descriptions VTT track is present on the media element.
|
|
223
|
+
*/
|
|
224
|
+
_hasDescriptionsTrack() {
|
|
225
|
+
return Boolean(this.player.findTextTrack("descriptions"));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Resolve which audio-description delivery mode applies for the current media.
|
|
229
|
+
*/
|
|
230
|
+
resolveDeliveryMode() {
|
|
231
|
+
const configured = this.player.options.audioDescriptionMode ?? "auto";
|
|
232
|
+
const hasSwap = this._hasSwapSource();
|
|
233
|
+
const hasDescriptions = this._hasDescriptionsTrack();
|
|
234
|
+
if (configured === "swap") {
|
|
235
|
+
return hasSwap ? "swap" : "none";
|
|
236
|
+
}
|
|
237
|
+
if (configured === "vtt_speech") {
|
|
238
|
+
return hasDescriptions ? "vtt_speech" : "none";
|
|
239
|
+
}
|
|
240
|
+
if (hasSwap) {
|
|
241
|
+
return "swap";
|
|
242
|
+
}
|
|
243
|
+
if (hasDescriptions) {
|
|
244
|
+
return "vtt_speech";
|
|
245
|
+
}
|
|
246
|
+
return "none";
|
|
247
|
+
}
|
|
248
|
+
_ensureSpeechManager() {
|
|
249
|
+
if (!this.speechManager) {
|
|
250
|
+
this.speechManager = new DescriptionSpeechManager(this.player);
|
|
251
|
+
}
|
|
252
|
+
return this.speechManager;
|
|
29
253
|
}
|
|
30
254
|
/**
|
|
31
255
|
* Initialize audio description from source elements
|
|
@@ -79,24 +303,35 @@ var AudioDescriptionManager = class {
|
|
|
79
303
|
* Check if audio description is available
|
|
80
304
|
*/
|
|
81
305
|
isAvailable() {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
306
|
+
const mode = this.resolveDeliveryMode();
|
|
307
|
+
if (mode === "swap" || mode === "vtt_speech") {
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return this.captionTracks.length > 0;
|
|
86
311
|
}
|
|
87
312
|
/**
|
|
88
313
|
* Enable audio description
|
|
89
314
|
*/
|
|
90
315
|
async enable() {
|
|
91
|
-
const
|
|
92
|
-
(el) => el.getAttribute("data-desc-src")
|
|
93
|
-
);
|
|
316
|
+
const deliveryMode = this.resolveDeliveryMode();
|
|
94
317
|
const hasTracksWithDesc = this.captionTracks.length > 0;
|
|
95
|
-
if (
|
|
96
|
-
console.warn("VidPly: No audio description source,
|
|
318
|
+
if (deliveryMode === "none" && !hasTracksWithDesc) {
|
|
319
|
+
console.warn("VidPly: No audio description source, descriptions track, or tracks provided");
|
|
97
320
|
return;
|
|
98
321
|
}
|
|
99
322
|
this.desiredState = true;
|
|
323
|
+
if (deliveryMode === "vtt_speech") {
|
|
324
|
+
const speechManager = this._ensureSpeechManager();
|
|
325
|
+
const started = speechManager.enable();
|
|
326
|
+
if (!started) {
|
|
327
|
+
console.warn("VidPly: No descriptions track available for VTT speech mode");
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
this.enabled = true;
|
|
331
|
+
this.player.state.audioDescriptionEnabled = true;
|
|
332
|
+
this.player.emit("audiodescriptionenabled");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
100
335
|
const currentTime = this.player.state.currentTime;
|
|
101
336
|
const wasPlaying = this.player.state.playing;
|
|
102
337
|
const posterValue = this.player.element.poster || this.player.element.getAttribute("poster") || this.player.options.poster;
|
|
@@ -117,6 +352,13 @@ var AudioDescriptionManager = class {
|
|
|
117
352
|
*/
|
|
118
353
|
async disable() {
|
|
119
354
|
this.desiredState = false;
|
|
355
|
+
if (this.speechManager?.enabled) {
|
|
356
|
+
this.speechManager.disable();
|
|
357
|
+
this.enabled = false;
|
|
358
|
+
this.player.state.audioDescriptionEnabled = false;
|
|
359
|
+
this.player.emit("audiodescriptiondisabled");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
120
362
|
const hasTracksWithDesc = this.captionTracks.length > 0;
|
|
121
363
|
if (!this.sourceElement && !this.src && hasTracksWithDesc) {
|
|
122
364
|
await this._swapCaptionTracks(false);
|
|
@@ -142,19 +384,30 @@ var AudioDescriptionManager = class {
|
|
|
142
384
|
* Toggle audio description
|
|
143
385
|
*/
|
|
144
386
|
async toggle() {
|
|
387
|
+
const deliveryMode = this.resolveDeliveryMode();
|
|
145
388
|
const descriptionTrack = this.player.findTextTrack("descriptions");
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
389
|
+
const hasSwapOrTracks = this._hasSwapSource() || this.captionTracks.length > 0;
|
|
390
|
+
if (deliveryMode === "vtt_speech") {
|
|
391
|
+
if (this.enabled) {
|
|
392
|
+
await this.disable();
|
|
393
|
+
} else {
|
|
394
|
+
await this.enable();
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (descriptionTrack && !hasSwapOrTracks) {
|
|
148
399
|
if (descriptionTrack.mode === "showing") {
|
|
149
400
|
descriptionTrack.mode = "hidden";
|
|
150
401
|
this.enabled = false;
|
|
402
|
+
this.player.state.audioDescriptionEnabled = false;
|
|
151
403
|
this.player.emit("audiodescriptiondisabled");
|
|
152
404
|
} else {
|
|
153
405
|
descriptionTrack.mode = "showing";
|
|
154
406
|
this.enabled = true;
|
|
407
|
+
this.player.state.audioDescriptionEnabled = true;
|
|
155
408
|
this.player.emit("audiodescriptionenabled");
|
|
156
409
|
}
|
|
157
|
-
} else if (descriptionTrack &&
|
|
410
|
+
} else if (descriptionTrack && hasSwapOrTracks) {
|
|
158
411
|
if (this.enabled) {
|
|
159
412
|
this.desiredState = false;
|
|
160
413
|
await this.disable();
|
|
@@ -163,7 +416,7 @@ var AudioDescriptionManager = class {
|
|
|
163
416
|
this.desiredState = true;
|
|
164
417
|
await this.enable();
|
|
165
418
|
}
|
|
166
|
-
} else if (
|
|
419
|
+
} else if (hasSwapOrTracks) {
|
|
167
420
|
if (this.enabled) {
|
|
168
421
|
this.desiredState = false;
|
|
169
422
|
await this.disable();
|
|
@@ -261,7 +514,7 @@ var AudioDescriptionManager = class {
|
|
|
261
514
|
});
|
|
262
515
|
this.player.element.load();
|
|
263
516
|
await new Promise((resolve) => {
|
|
264
|
-
|
|
517
|
+
this.player.setManagedTimeout(() => {
|
|
265
518
|
tracksToReadd.forEach(({ trackInfo, parent, nextSibling, attributes }) => {
|
|
266
519
|
const newSrc = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;
|
|
267
520
|
if (!newSrc) {
|
|
@@ -271,8 +524,9 @@ var AudioDescriptionManager = class {
|
|
|
271
524
|
const newTrackElement = document.createElement("track");
|
|
272
525
|
newTrackElement.setAttribute("src", newSrc);
|
|
273
526
|
Object.keys(attributes).forEach((attrName) => {
|
|
274
|
-
|
|
275
|
-
|
|
527
|
+
const attrValue = attributes[attrName];
|
|
528
|
+
if (attrName !== "src" && attrName !== "data-desc-src" && attrValue !== void 0) {
|
|
529
|
+
newTrackElement.setAttribute(attrName, attrValue);
|
|
276
530
|
}
|
|
277
531
|
});
|
|
278
532
|
const targetParent = parent || this.player.element;
|
|
@@ -310,10 +564,10 @@ var AudioDescriptionManager = class {
|
|
|
310
564
|
}, 300);
|
|
311
565
|
};
|
|
312
566
|
if (this.player.element.readyState >= 1) {
|
|
313
|
-
|
|
567
|
+
this.player.setManagedTimeout(setupNewTracks, 200);
|
|
314
568
|
} else {
|
|
315
569
|
this.player.element.addEventListener("loadedmetadata", setupNewTracks, { once: true });
|
|
316
|
-
|
|
570
|
+
this.player.setManagedTimeout(setupNewTracks, 2e3);
|
|
317
571
|
}
|
|
318
572
|
resolve();
|
|
319
573
|
}, 100);
|
|
@@ -561,6 +815,8 @@ var AudioDescriptionManager = class {
|
|
|
561
815
|
* Update sources (called when playlist changes)
|
|
562
816
|
*/
|
|
563
817
|
updateSources(audioDescriptionSrc) {
|
|
818
|
+
this.speechManager?.destroy();
|
|
819
|
+
this.speechManager = null;
|
|
564
820
|
this.src = audioDescriptionSrc || null;
|
|
565
821
|
this.enabled = false;
|
|
566
822
|
this.desiredState = false;
|
|
@@ -579,6 +835,8 @@ var AudioDescriptionManager = class {
|
|
|
579
835
|
* Cleanup
|
|
580
836
|
*/
|
|
581
837
|
destroy() {
|
|
838
|
+
this.speechManager?.destroy();
|
|
839
|
+
this.speechManager = null;
|
|
582
840
|
this.enabled = false;
|
|
583
841
|
this.desiredState = false;
|
|
584
842
|
this.captionTracks = [];
|
|
@@ -589,4 +847,4 @@ var AudioDescriptionManager = class {
|
|
|
589
847
|
export {
|
|
590
848
|
AudioDescriptionManager
|
|
591
849
|
};
|
|
592
|
-
//# sourceMappingURL=vidply.AudioDescriptionManager-
|
|
850
|
+
//# sourceMappingURL=vidply.AudioDescriptionManager-6RFVF27Z.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/DescriptionSpeechManager.ts", "../../src/core/AudioDescriptionManager.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Extended audio description via WebVTT descriptions cues and speech synthesis.\n * Pauses the video, speaks the cue text, then resumes playback when finished.\n */\n\nimport type { Player } from './Player.js';\n\nexport interface DescriptionCueDetail {\n time: number;\n endTime: number;\n text: string;\n cue: TextTrackCue;\n}\n\nexport class DescriptionSpeechManager {\n player: Player;\n enabled = false;\n descriptionTrack: TextTrack | null = null;\n cueChangeHandler: (() => void) | null = null;\n seekedHandler: (() => void) | null = null;\n wasPlayingBeforeCue = false;\n speaking = false;\n lastSpokenCueKey: string | null = null;\n private _pendingUtterance: SpeechSynthesisUtterance | null = null;\n\n constructor(player: Player) {\n this.player = player;\n }\n\n /**\n * Whether speech synthesis is available and enabled in player options.\n */\n canUseSpeech(): boolean {\n if (this.player.options.audioDescriptionSpeech === false) {\n return false;\n }\n return typeof window !== 'undefined' && 'speechSynthesis' in window;\n }\n\n /**\n * Resolve the descriptions track, preferring the active caption language.\n */\n findDescriptionTrack(): TextTrack | null {\n const textTracks = this.player.element\n ? Array.from(this.player.element.textTracks || [])\n : [];\n\n const descriptionsTracks = textTracks.filter(\n (track) => track.kind === 'descriptions' && !(track as TextTrack & { _vidplyStale?: boolean })._vidplyStale\n );\n\n if (descriptionsTracks.length === 0) {\n return null;\n }\n\n const captionLang = this.player.captionManager?.currentTrack?.track?.language;\n if (captionLang) {\n const match = descriptionsTracks.find((track) => track.language === captionLang);\n if (match) {\n return match;\n }\n }\n\n return descriptionsTracks[0] ?? null;\n }\n\n /**\n * Enable VTT speech mode: wire cuechange/seeked listeners.\n */\n enable(): boolean {\n const track = this.findDescriptionTrack();\n if (!track) {\n return false;\n }\n\n if (!this.canUseSpeech()) {\n track.mode = 'showing';\n return true;\n }\n\n this.descriptionTrack = track;\n track.mode = 'hidden';\n\n this.cueChangeHandler = () => {\n this.handleCueChange();\n };\n track.addEventListener('cuechange', this.cueChangeHandler);\n\n this.seekedHandler = () => {\n this.cancelSpeech();\n this.lastSpokenCueKey = null;\n this.handleCueChange();\n };\n this.player.on('seeked', this.seekedHandler);\n\n this.enabled = true;\n this.handleCueChange();\n return true;\n }\n\n /**\n * Disable VTT speech mode and restore track state.\n */\n disable(): void {\n this.cancelSpeech();\n this.lastSpokenCueKey = null;\n this.enabled = false;\n\n if (this.descriptionTrack && this.cueChangeHandler) {\n this.descriptionTrack.removeEventListener('cuechange', this.cueChangeHandler);\n }\n if (this.seekedHandler) {\n this.player.off('seeked', this.seekedHandler);\n }\n\n if (this.descriptionTrack) {\n this.descriptionTrack.mode = 'hidden';\n }\n\n this.cueChangeHandler = null;\n this.seekedHandler = null;\n this.descriptionTrack = null;\n this.wasPlayingBeforeCue = false;\n }\n\n /**\n * Handle active description cues on the wired track.\n */\n handleCueChange(): void {\n if (!this.enabled || !this.descriptionTrack || !this.canUseSpeech()) {\n return;\n }\n\n const activeCues = this.descriptionTrack.activeCues;\n if (!activeCues || activeCues.length === 0) {\n return;\n }\n\n const cue = activeCues[0] as VTTCue;\n const text = cue.text?.trim();\n if (!text) {\n return;\n }\n\n const cueKey = `${cue.startTime}:${cue.endTime}:${text}`;\n if (this.speaking && this.lastSpokenCueKey === cueKey) {\n return;\n }\n if (this.lastSpokenCueKey === cueKey && !this.player.state.playing) {\n return;\n }\n\n this.speakCue(cue, cueKey);\n }\n\n /**\n * Pause playback and speak a description cue.\n */\n speakCue(cue: VTTCue, cueKey: string): void {\n if (!this.canUseSpeech()) {\n return;\n }\n\n // Do not hijack an intentional user pause (video already paused and not mid-speech).\n if (this.player.state.paused && !this.speaking && !this.wasPlayingBeforeCue) {\n return;\n }\n\n this.cancelSpeech();\n\n this.wasPlayingBeforeCue = this.player.state.playing;\n if (this.wasPlayingBeforeCue) {\n this.player.pause();\n }\n\n this.speaking = true;\n this.lastSpokenCueKey = cueKey;\n\n const detail: DescriptionCueDetail = {\n time: cue.startTime,\n endTime: cue.endTime,\n text: cue.text.trim(),\n cue\n };\n this.player.emit('audiodescriptioncuestart', detail);\n\n const utterance = new SpeechSynthesisUtterance(detail.text);\n const lang = this.descriptionTrack?.language || this.player.options.language || 'en';\n utterance.lang = lang;\n\n const finish = () => {\n if (this._pendingUtterance !== utterance) {\n return;\n }\n this._pendingUtterance = null;\n this.speaking = false;\n this.player.emit('audiodescriptioncueend', detail);\n\n const extended = this.player.options.audioDescriptionExtended !== false;\n const shouldResume = extended\n ? this.wasPlayingBeforeCue\n : this.wasPlayingBeforeCue && this.player.state.currentTime < cue.endTime;\n\n if (shouldResume && this.enabled) {\n void this.player.play();\n }\n this.wasPlayingBeforeCue = false;\n };\n\n utterance.onend = finish;\n utterance.onerror = finish;\n\n this._pendingUtterance = utterance;\n window.speechSynthesis.speak(utterance);\n }\n\n /**\n * Cancel any in-progress speech synthesis.\n */\n cancelSpeech(): void {\n if (typeof window !== 'undefined' && window.speechSynthesis) {\n window.speechSynthesis.cancel();\n }\n this._pendingUtterance = null;\n this.speaking = false;\n this.wasPlayingBeforeCue = false;\n }\n\n destroy(): void {\n this.disable();\n }\n}\n", "/**\n * Audio Description Manager\n * Handles audio-described video source switching and caption track swapping\n */\n\nimport { CaptionManager } from '../controls/CaptionManager.js';\nimport type { Player } from './Player.js';\nimport { DescriptionSpeechManager } from './DescriptionSpeechManager.js';\n\nexport type AudioDescriptionDeliveryMode = 'swap' | 'vtt_speech' | 'none';\n\n/**\n * Caption-track entry tracked by the audio-description manager. Each\n * entry pairs a `<track>` element with its described/original source\n * URLs so the manager can swap the rendered captions when the user\n * toggles audio description.\n */\ninterface CaptionTrackInfo {\n trackElement: HTMLTrackElement;\n originalSrc: string | null;\n describedSrc: string;\n originalTrackSrc: string | null;\n explicit: boolean;\n}\n\nexport class AudioDescriptionManager {\n player: Player;\n captionTracks: CaptionTrackInfo[];\n desiredState: boolean;\n enabled: boolean;\n originalSource: string | null;\n sourceElement: Element | null;\n src: string | null;\n speechManager: DescriptionSpeechManager | null;\n\n constructor(player: Player) {\n this.player = player;\n \n // State\n this.enabled = false;\n this.desiredState = false;\n \n // Sources\n this.src = player.options.audioDescriptionSrc;\n this.sourceElement = null;\n this.originalSource = null;\n this.captionTracks = [];\n this.speechManager = null;\n }\n\n /**\n * Whether a described video source swap is configured.\n */\n _hasSwapSource(): boolean {\n const hasSourceElementsWithDesc = this.player.sourceElements.some(\n (el: Element) => el.getAttribute('data-desc-src')\n );\n return Boolean(this.src || hasSourceElementsWithDesc);\n }\n\n /**\n * Whether a descriptions VTT track is present on the media element.\n */\n _hasDescriptionsTrack(): boolean {\n return Boolean(this.player.findTextTrack('descriptions'));\n }\n\n /**\n * Resolve which audio-description delivery mode applies for the current media.\n */\n resolveDeliveryMode(): AudioDescriptionDeliveryMode {\n const configured = this.player.options.audioDescriptionMode ?? 'auto';\n const hasSwap = this._hasSwapSource();\n const hasDescriptions = this._hasDescriptionsTrack();\n\n if (configured === 'swap') {\n return hasSwap ? 'swap' : 'none';\n }\n if (configured === 'vtt_speech') {\n return hasDescriptions ? 'vtt_speech' : 'none';\n }\n // auto: described video takes precedence over VTT speech\n if (hasSwap) {\n return 'swap';\n }\n if (hasDescriptions) {\n return 'vtt_speech';\n }\n return 'none';\n }\n\n _ensureSpeechManager(): DescriptionSpeechManager {\n if (!this.speechManager) {\n this.speechManager = new DescriptionSpeechManager(this.player);\n }\n return this.speechManager;\n }\n\n /**\n * Initialize audio description from source elements\n * Called during player initialization\n */\n initFromSourceElements(sourceElements: Element[], trackElements: Element[]) {\n // Check for source elements with audio description attributes\n for (const sourceEl of sourceElements) {\n const descSrc = sourceEl.getAttribute('data-desc-src');\n const origSrc = sourceEl.getAttribute('data-orig-src');\n \n if (descSrc || origSrc) {\n if (!this.sourceElement) {\n this.sourceElement = sourceEl;\n }\n \n if (origSrc) {\n if (!this.originalSource) {\n this.originalSource = origSrc;\n }\n if (!this.player.originalSrc) {\n this.player.originalSrc = origSrc;\n }\n } else {\n const currentSrcAttr = sourceEl.getAttribute('src');\n if (!this.originalSource && currentSrcAttr) {\n this.originalSource = currentSrcAttr;\n }\n if (!this.player.originalSrc && currentSrcAttr) {\n this.player.originalSrc = currentSrcAttr;\n }\n }\n \n if (descSrc && !this.src) {\n this.src = descSrc;\n }\n }\n }\n\n // Check for text tracks with audio description versions\n trackElements.forEach((trackEl: Element) => {\n const trackKind = trackEl.getAttribute('kind');\n const trackDescSrc = trackEl.getAttribute('data-desc-src');\n\n if ((trackKind === 'captions' || trackKind === 'subtitles' ||\n trackKind === 'chapters' || trackKind === 'descriptions') &&\n trackDescSrc && trackEl instanceof HTMLTrackElement) {\n this.captionTracks.push({\n trackElement: trackEl,\n originalSrc: trackEl.getAttribute('src'),\n describedSrc: trackDescSrc,\n originalTrackSrc: trackEl.getAttribute('data-orig-src') || trackEl.getAttribute('src'),\n explicit: true\n });\n this.player.log(`Found explicit described ${trackKind} track: ${trackEl.getAttribute('src')} -> ${trackDescSrc}`);\n }\n });\n }\n\n /**\n * Check if audio description is available\n */\n isAvailable() {\n const mode = this.resolveDeliveryMode();\n if (mode === 'swap' || mode === 'vtt_speech') {\n return true;\n }\n return this.captionTracks.length > 0;\n }\n\n /**\n * Enable audio description\n */\n async enable() {\n const deliveryMode = this.resolveDeliveryMode();\n const hasTracksWithDesc = this.captionTracks.length > 0;\n\n if (deliveryMode === 'none' && !hasTracksWithDesc) {\n console.warn('VidPly: No audio description source, descriptions track, or tracks provided');\n return;\n }\n\n this.desiredState = true;\n\n if (deliveryMode === 'vtt_speech') {\n const speechManager = this._ensureSpeechManager();\n const started = speechManager.enable();\n if (!started) {\n console.warn('VidPly: No descriptions track available for VTT speech mode');\n return;\n }\n this.enabled = true;\n this.player.state.audioDescriptionEnabled = true;\n this.player.emit('audiodescriptionenabled');\n return;\n }\n\n // Store current state for restoration\n const currentTime = this.player.state.currentTime;\n const wasPlaying = this.player.state.playing;\n const posterValue = (this.player.element as HTMLVideoElement).poster || \n this.player.element.getAttribute('poster') || \n this.player.options.poster;\n const shouldKeepPoster = currentTime < 0.1 && !wasPlaying;\n\n // Get current caption text for synchronization\n const currentCaptionText = this._getCurrentCaptionText();\n\n // Switch to audio-described version based on what's available\n if (this.sourceElement) {\n // Use source element approach (data-desc-src on <source> elements)\n await this._enableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText);\n } else if (this.src) {\n // Use direct src approach (audioDescriptionSrc option)\n await this._enableWithDirectSrc(currentTime, wasPlaying, posterValue, shouldKeepPoster);\n } else if (hasTracksWithDesc) {\n // Only caption tracks with descriptions - swap tracks without changing video source\n await this._swapCaptionTracks(true);\n this.enabled = true;\n this.player.emit('audiodescriptionenabled');\n }\n // If none of the above, we already returned at the top check\n }\n\n /**\n * Disable audio description\n */\n async disable() {\n this.desiredState = false;\n\n if (this.speechManager?.enabled) {\n this.speechManager.disable();\n this.enabled = false;\n this.player.state.audioDescriptionEnabled = false;\n this.player.emit('audiodescriptiondisabled');\n return;\n }\n\n // If we only had caption tracks (no video source swap), just swap tracks back\n const hasTracksWithDesc = this.captionTracks.length > 0;\n if (!this.sourceElement && !this.src && hasTracksWithDesc) {\n await this._swapCaptionTracks(false);\n this.enabled = false;\n this.player.emit('audiodescriptiondisabled');\n return;\n }\n\n if (!this.player.originalSrc) {\n return;\n }\n\n // Store current state\n const currentTime = this.player.state.currentTime;\n const wasPlaying = this.player.state.playing;\n const posterValue = (this.player.element as HTMLVideoElement).poster || \n this.player.element.getAttribute('poster') || \n this.player.options.poster;\n const shouldKeepPoster = currentTime < 0.1 && !wasPlaying;\n\n // Get current caption for sync\n const currentCaptionText = this._getCurrentCaptionText();\n\n if (this.sourceElement) {\n await this._disableWithSourceElement(currentTime, wasPlaying, posterValue, shouldKeepPoster, currentCaptionText);\n } else if (this.src) {\n await this._disableWithDirectSrc(currentTime, wasPlaying, posterValue);\n }\n }\n\n /**\n * Toggle audio description\n */\n async toggle() {\n const deliveryMode = this.resolveDeliveryMode();\n const descriptionTrack = this.player.findTextTrack('descriptions');\n const hasSwapOrTracks = this._hasSwapSource() || this.captionTracks.length > 0;\n\n if (deliveryMode === 'vtt_speech') {\n if (this.enabled) {\n await this.disable();\n } else {\n await this.enable();\n }\n return;\n }\n\n if (descriptionTrack && !hasSwapOrTracks) {\n // Text-only fallback when speech synthesis is unavailable\n if (descriptionTrack.mode === 'showing') {\n descriptionTrack.mode = 'hidden';\n this.enabled = false;\n this.player.state.audioDescriptionEnabled = false;\n this.player.emit('audiodescriptiondisabled');\n } else {\n descriptionTrack.mode = 'showing';\n this.enabled = true;\n this.player.state.audioDescriptionEnabled = true;\n this.player.emit('audiodescriptionenabled');\n }\n } else if (descriptionTrack && hasSwapOrTracks) {\n // Toggle both\n if (this.enabled) {\n this.desiredState = false;\n await this.disable();\n } else {\n descriptionTrack.mode = 'showing';\n this.desiredState = true;\n await this.enable();\n }\n } else if (hasSwapOrTracks) {\n // Toggle source swap\n if (this.enabled) {\n this.desiredState = false;\n await this.disable();\n } else {\n this.desiredState = true;\n await this.enable();\n }\n }\n }\n\n /**\n * Get current caption text for synchronization\n */\n _getCurrentCaptionText() {\n if (this.player.captionManager && \n this.player.captionManager.currentTrack && \n this.player.captionManager.currentCue) {\n return this.player.captionManager.currentCue.text;\n }\n return null;\n }\n\n /**\n * Validate that a track URL exists. Bounded by the player's lifecycle\n * AbortController + an 8s timeout so a torn-down player cannot leak\n * the request.\n */\n async _validateTrackExists(url: string): Promise<boolean> {\n if (typeof url !== 'string' || !url) return false;\n const signals: AbortSignal[] = [];\n const lifecycle = (this.player as { lifecycleSignal?: AbortSignal }).lifecycleSignal;\n if (lifecycle) signals.push(lifecycle);\n if (typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function') {\n signals.push(AbortSignal.timeout(8000));\n }\n let signal: AbortSignal | undefined;\n if (signals.length === 1) signal = signals[0];\n else if (signals.length > 1) {\n const anyFn = (AbortSignal as { any?: (s: AbortSignal[]) => AbortSignal }).any;\n signal = anyFn ? anyFn(signals) : signals[0];\n }\n try {\n const response = await fetch(url, { method: 'HEAD', signal });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Swap caption tracks to described versions\n */\n async _swapCaptionTracks(toDescribed = true) {\n if (this.captionTracks.length === 0) return [];\n\n const swappedTracks: CaptionTrackInfo[] = [];\n \n const validationPromises = this.captionTracks.map(async (trackInfo) => {\n if (trackInfo.trackElement && trackInfo.describedSrc) {\n if (trackInfo.explicit === true) {\n const url = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;\n if (!url) return { trackInfo, exists: false };\n try {\n const exists = await this._validateTrackExists(url);\n return { trackInfo, exists };\n } catch {\n return { trackInfo, exists: false };\n }\n }\n }\n return { trackInfo, exists: false };\n });\n \n const validationResults = await Promise.all(validationPromises);\n const tracksToSwap = validationResults.filter(result => result.exists);\n \n if (tracksToSwap.length > 0) {\n // Store track modes before removing\n const trackModes = new Map();\n tracksToSwap.forEach(({ trackInfo }) => {\n const textTrack = trackInfo.trackElement.track;\n if (textTrack) {\n trackModes.set(trackInfo, {\n wasShowing: textTrack.mode === 'showing',\n wasHidden: textTrack.mode === 'hidden'\n });\n } else {\n trackModes.set(trackInfo, { wasShowing: false, wasHidden: false });\n }\n });\n \n // Store track info and remove\n const tracksToReadd = tracksToSwap.map(({ trackInfo }) => {\n const attributes: Record<string, string> = {};\n Array.from(trackInfo.trackElement.attributes).forEach((attr: Attr) => {\n attributes[attr.name] = attr.value;\n });\n \n const result = {\n trackInfo,\n oldSrc: trackInfo.trackElement.getAttribute('src'),\n parent: trackInfo.trackElement.parentNode,\n nextSibling: trackInfo.trackElement.nextSibling,\n attributes\n };\n \n trackInfo.trackElement.remove();\n return result;\n });\n \n // Force browser to process removal\n this.player.element.load();\n \n // Re-add tracks with new src. Use a managed timeout so the swap\n // is cancelled (and won't mutate a torn-down player) on destroy.\n await new Promise<void>(resolve => {\n this.player.setManagedTimeout(() => {\n tracksToReadd.forEach(({ trackInfo, parent, nextSibling, attributes }) => {\n const newSrc = toDescribed ? trackInfo.describedSrc : trackInfo.originalSrc;\n if (!newSrc) {\n // Skip tracks without a usable src; no swap can occur.\n return;\n }\n swappedTracks.push(trackInfo);\n\n const newTrackElement = document.createElement('track');\n newTrackElement.setAttribute('src', newSrc);\n \n Object.keys(attributes).forEach(attrName => {\n const attrValue = attributes[attrName];\n if (attrName !== 'src' && attrName !== 'data-desc-src' && attrValue !== undefined) {\n newTrackElement.setAttribute(attrName, attrValue);\n }\n });\n \n // Use the player's video element as parent if the original parent is null\n const targetParent = parent || this.player.element;\n \n if (nextSibling && nextSibling.parentNode) {\n targetParent.insertBefore(newTrackElement, nextSibling);\n } else {\n targetParent.appendChild(newTrackElement);\n }\n \n trackInfo.trackElement = newTrackElement;\n });\n \n this.player.invalidateTrackCache();\n \n // Setup new tracks\n const setupNewTracks = () => {\n this.player.setManagedTimeout(() => {\n swappedTracks.forEach((trackInfo) => {\n const trackElement = trackInfo.trackElement;\n const newTextTrack = trackElement.track;\n if (newTextTrack) {\n const modeInfo = trackModes.get(trackInfo) || { wasShowing: false, wasHidden: false };\n newTextTrack.mode = 'hidden';\n\n const restoreMode = () => {\n if (modeInfo.wasShowing || modeInfo.wasHidden) {\n newTextTrack.mode = 'hidden';\n } else {\n newTextTrack.mode = 'disabled';\n }\n };\n\n // `readyState` and the `load`/`error`\n // events live on `<track>`, not on\n // the underlying TextTrack object.\n if (trackElement.readyState >= 2) {\n restoreMode();\n } else {\n trackElement.addEventListener('load', restoreMode, { once: true });\n trackElement.addEventListener('error', restoreMode, { once: true });\n }\n }\n });\n }, 300);\n };\n \n if (this.player.element.readyState >= 1) {\n this.player.setManagedTimeout(setupNewTracks, 200);\n } else {\n this.player.element.addEventListener('loadedmetadata', setupNewTracks, { once: true });\n this.player.setManagedTimeout(setupNewTracks, 2000);\n }\n \n resolve();\n }, 100);\n });\n }\n \n return swappedTracks;\n }\n\n /**\n * Update source elements to described versions\n */\n _updateSourceElements(toDescribed = true) {\n const sourceElements = this.player.sourceElements;\n const sourcesToUpdate: Array<{ src: string | null; type: string | null; origSrc: string | null; descSrc: string | null }> = [];\n \n sourceElements.forEach((sourceEl: Element) => {\n const descSrcAttr = sourceEl.getAttribute('data-desc-src');\n const currentSrc = sourceEl.getAttribute('src');\n \n if (descSrcAttr) {\n const type = sourceEl.getAttribute('type');\n const origSrc = sourceEl.getAttribute('data-orig-src') || currentSrc;\n \n sourcesToUpdate.push({\n src: toDescribed ? descSrcAttr : origSrc,\n type,\n origSrc,\n descSrc: descSrcAttr\n });\n } else {\n sourcesToUpdate.push({\n src: sourceEl.getAttribute('src'),\n type: sourceEl.getAttribute('type'),\n origSrc: null,\n descSrc: null\n });\n }\n });\n \n // Remove src attribute if present\n if (this.player.element.hasAttribute('src')) {\n this.player.element.removeAttribute('src');\n }\n \n // Remove all source elements\n sourceElements.forEach((sourceEl: Element) => sourceEl.remove());\n \n // Re-add with updated src\n sourcesToUpdate.forEach((sourceInfo) => {\n if (!sourceInfo.src) {\n return;\n }\n const newSource = document.createElement('source');\n newSource.setAttribute('src', sourceInfo.src);\n if (sourceInfo.type) {\n newSource.setAttribute('type', sourceInfo.type);\n }\n if (sourceInfo.origSrc) {\n newSource.setAttribute('data-orig-src', sourceInfo.origSrc);\n }\n if (sourceInfo.descSrc) {\n newSource.setAttribute('data-desc-src', sourceInfo.descSrc);\n }\n \n const firstTrack = this.player.element.querySelector('track');\n if (firstTrack) {\n this.player.element.insertBefore(newSource, firstTrack);\n } else {\n this.player.element.appendChild(newSource);\n }\n });\n \n this.player._sourceElementsDirty = true;\n this.player._sourceElementsCache = null;\n }\n\n /**\n * Wait for media to be ready\n */\n async _waitForMediaReady(needSeek = false) {\n // Wait for metadata\n await new Promise<void>((resolve) => {\n if (this.player.element.readyState >= 1) {\n resolve();\n } else {\n const onLoad = () => {\n this.player.element.removeEventListener('loadedmetadata', onLoad);\n resolve();\n };\n this.player.element.addEventListener('loadedmetadata', onLoad);\n }\n });\n \n // Wait for tracks\n await new Promise(resolve => setTimeout(resolve, 300));\n \n // Wait for playback if needed\n if (needSeek) {\n await new Promise<void>((resolve) => {\n if (this.player.element.readyState >= 3) {\n resolve();\n } else {\n const onCanPlay = () => {\n this.player.element.removeEventListener('canplay', onCanPlay);\n this.player.element.removeEventListener('canplaythrough', onCanPlay);\n resolve();\n };\n this.player.element.addEventListener('canplay', onCanPlay, { once: true });\n this.player.element.addEventListener('canplaythrough', onCanPlay, { once: true });\n setTimeout(() => {\n this.player.element.removeEventListener('canplay', onCanPlay);\n this.player.element.removeEventListener('canplaythrough', onCanPlay);\n resolve();\n }, 3000);\n }\n });\n }\n }\n\n /**\n * Restore playback state after source change\n */\n async _restorePlaybackState(currentTime: number, wasPlaying: boolean, shouldKeepPoster: boolean, currentCaptionText: string | null) {\n // Try to find matching caption for sync\n let syncTime = currentTime;\n if (currentCaptionText && this.player.captionManager && this.player.captionManager.tracks.length > 0) {\n await new Promise(resolve => setTimeout(resolve, 500));\n const matchingTime = this.player.findMatchingCaptionTime(\n currentCaptionText, \n this.player.captionManager.tracks\n );\n if (matchingTime !== null) {\n syncTime = matchingTime;\n if (this.player.options.debug) {\n this.player.log(`[VidPly] Syncing via caption: ${currentTime}s -> ${syncTime}s`);\n }\n }\n }\n \n // Seek\n if (syncTime > 0) {\n this.player.seek(syncTime);\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n \n // Play/pause\n if (wasPlaying) {\n await this.player.play();\n this.player.setManagedTimeout(() => {\n this.player.hidePosterOverlay();\n }, 100);\n } else {\n this.player.pause();\n if (!shouldKeepPoster) {\n this.player.hidePosterOverlay();\n }\n }\n }\n\n /**\n * Enable with source element method\n */\n async _enableWithSourceElement(currentTime: number, wasPlaying: boolean, posterValue: string | null, shouldKeepPoster: boolean, currentCaptionText: string | null) {\n // Swap caption tracks\n await this._swapCaptionTracks(true);\n \n // Update source elements\n this._updateSourceElements(true);\n \n // Preserve poster\n if (posterValue && this.player.element.tagName === 'VIDEO') {\n (this.player.element as HTMLVideoElement).poster = posterValue;\n }\n \n // Reload\n this.player.element.load();\n \n // Wait for ready\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\n \n // Restore playback\n await this._restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText);\n \n // Update state\n if (!this.desiredState) return;\n this.enabled = true;\n this.player.state.audioDescriptionEnabled = true;\n this.player.emit('audiodescriptionenabled');\n \n // Reload transcript if visible\n this._reloadTranscript();\n }\n\n /**\n * Enable with direct src method\n */\n async _enableWithDirectSrc(currentTime: number, wasPlaying: boolean, posterValue: string | null, shouldKeepPoster: boolean) {\n // Swap caption tracks\n await this._swapCaptionTracks(true);\n \n // Set poster\n if (posterValue && this.player.element.tagName === 'VIDEO') {\n (this.player.element as HTMLVideoElement).poster = posterValue;\n }\n \n // Set src (this method should only be called when this.src exists)\n if (!this.src) return;\n this.player.element.src = this.src;\n \n // Wait and restore\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\n \n if (currentTime > 0) {\n this.player.seek(currentTime);\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n \n if (wasPlaying) {\n await this.player.play();\n } else {\n this.player.pause();\n if (!shouldKeepPoster) {\n this.player.hidePosterOverlay();\n }\n }\n \n if (!this.desiredState) return;\n this.enabled = true;\n this.player.state.audioDescriptionEnabled = true;\n this.player.emit('audiodescriptionenabled');\n \n // Reload transcript if visible\n this._reloadTranscript();\n }\n\n /**\n * Disable with source element method\n */\n async _disableWithSourceElement(currentTime: number, wasPlaying: boolean, posterValue: string | null, shouldKeepPoster: boolean, currentCaptionText: string | null) {\n // Swap caption tracks back\n await this._swapCaptionTracks(false);\n \n // Update source elements\n this._updateSourceElements(false);\n \n // Preserve poster\n if (posterValue && this.player.element.tagName === 'VIDEO') {\n (this.player.element as HTMLVideoElement).poster = posterValue;\n }\n \n // Reload\n this.player.element.load();\n this.player.invalidateTrackCache();\n \n // Wait for ready\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\n \n // Restore playback\n await this._restorePlaybackState(currentTime, wasPlaying, shouldKeepPoster, currentCaptionText);\n \n // Reinitialize caption manager\n if (this.player.captionManager) {\n this.player.captionManager.destroy();\n this.player.captionManager = new CaptionManager(this.player);\n }\n \n // Update state\n if (this.desiredState) return;\n this.enabled = false;\n this.player.state.audioDescriptionEnabled = false;\n this.player.emit('audiodescriptiondisabled');\n \n // Reload transcript if visible\n this._reloadTranscript();\n }\n\n /**\n * Disable with direct src method\n */\n async _disableWithDirectSrc(currentTime: number, wasPlaying: boolean, posterValue: string | null) {\n // Swap caption tracks back\n await this._swapCaptionTracks(false);\n \n // Set poster\n if (posterValue && this.player.element.tagName === 'VIDEO') {\n (this.player.element as HTMLVideoElement).poster = posterValue;\n }\n \n // Restore original src\n const originalSrcToUse = this.originalSource || this.player.originalSrc;\n if (!originalSrcToUse) {\n return;\n }\n this.player.element.src = originalSrcToUse;\n this.player.element.load();\n \n // Wait and restore\n await this._waitForMediaReady(currentTime > 0 || wasPlaying);\n \n if (currentTime > 0) {\n this.player.seek(currentTime);\n }\n \n if (wasPlaying) {\n await this.player.play();\n }\n \n if (this.desiredState) return;\n this.enabled = false;\n this.player.state.audioDescriptionEnabled = false;\n this.player.emit('audiodescriptiondisabled');\n \n // Reload transcript if visible\n this._reloadTranscript();\n }\n\n /**\n * Reload transcript after audio description state change\n */\n _reloadTranscript() {\n if (this.player.transcriptManager && this.player.transcriptManager.isVisible) {\n // Wait for tracks to load after source swap\n this.player.setManagedTimeout(() => {\n if (this.player.transcriptManager && this.player.transcriptManager.loadTranscriptData) {\n this.player.transcriptManager.loadTranscriptData();\n }\n }, 800);\n }\n }\n\n /**\n * Update sources (called when playlist changes)\n */\n updateSources(audioDescriptionSrc: string | null | undefined) {\n this.speechManager?.destroy();\n this.speechManager = null;\n this.src = audioDescriptionSrc || null;\n // Reset state for new playlist item\n this.enabled = false;\n this.desiredState = false;\n this.sourceElement = null;\n this.originalSource = null;\n this.captionTracks = [];\n }\n \n /**\n * Reinitialize from current player elements (called after playlist loads new tracks)\n */\n reinitialize() {\n this.player.invalidateTrackCache();\n this.initFromSourceElements(this.player.sourceElements, this.player.trackElements);\n }\n\n /**\n * Cleanup\n */\n destroy() {\n this.speechManager?.destroy();\n this.speechManager = null;\n this.enabled = false;\n this.desiredState = false;\n this.captionTracks = [];\n this.sourceElement = null;\n this.originalSource = null;\n }\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|