vidply 1.1.1 → 1.1.3
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 +56 -16
- package/dist/dev/{vidply.AudioDescriptionManager-2LDAC6BR.js → vidply.AudioDescriptionManager-AXPP5OKO.js} +2 -2
- package/dist/dev/{vidply.HLSRenderer-7F6FCZSH.js → vidply.HLSRenderer-IVT5CQQ4.js} +12 -10
- package/dist/dev/vidply.HLSRenderer-IVT5CQQ4.js.map +7 -0
- package/dist/dev/{vidply.chunk-ELJA4GMZ.js → vidply.chunk-55W33EGZ.js} +26 -1
- package/dist/dev/vidply.chunk-55W33EGZ.js.map +7 -0
- package/dist/dev/vidply.esm.js +3 -3
- package/dist/legacy/vidply.js +37 -9
- package/dist/legacy/vidply.js.map +2 -2
- package/dist/legacy/vidply.min.js +1 -1
- package/dist/legacy/vidply.min.meta.json +5 -5
- package/dist/prod/{vidply.AudioDescriptionManager-ILVU6JG2.min.js → vidply.AudioDescriptionManager-623CQKLU.min.js} +1 -1
- package/dist/prod/vidply.HLSRenderer-DBXM4KLR.min.js +6 -0
- package/dist/prod/vidply.chunk-2YTNSNRD.min.js +6 -0
- package/dist/prod/vidply.esm.min.js +1 -1
- package/dist/vidply.esm.min.meta.json +13 -13
- package/package.json +12 -6
- package/src/controls/CaptionManager.ts +32 -0
- package/src/renderers/HLSRenderer.ts +22 -9
- package/dist/dev/vidply.HLSRenderer-7F6FCZSH.js.map +0 -7
- package/dist/dev/vidply.chunk-ELJA4GMZ.js.map +0 -7
- package/dist/prod/vidply.HLSRenderer-5UAQA4DD.min.js +0 -6
- package/dist/prod/vidply.chunk-6XO2O3OT.min.js +0 -6
- /package/dist/dev/{vidply.AudioDescriptionManager-2LDAC6BR.js.map → vidply.AudioDescriptionManager-AXPP5OKO.js.map} +0 -0
package/README.md
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
**Universal, Accessible Video & Audio Player**
|
|
4
4
|
|
|
5
|
-
A modern, feature-rich media player
|
|
5
|
+
A modern, feature-rich media player authored in strict TypeScript and shipped as a zero-dependency ES module. Combines the best accessibility features from AblePlayer with the streaming capabilities of MediaElement.js. Fully internationalized with support for 5 languages and complete WCAG 2.2 AA compliance.
|
|
6
6
|
|
|
7
7
|

|
|
8
|
-

|
|
9
|
+

|
|
9
10
|

|
|
10
|
-

|
|
11
12
|
|
|
12
13
|
## Live Demos
|
|
13
14
|
|
|
@@ -21,11 +22,12 @@ Try VidPly in action:
|
|
|
21
22
|
|
|
22
23
|
## Why VidPly?
|
|
23
24
|
|
|
24
|
-
- **Zero Dependencies** - Pure vanilla JavaScript, no frameworks required
|
|
25
|
+
- **Zero Dependencies** - Pure vanilla JavaScript / TypeScript, no frameworks required
|
|
26
|
+
- **TypeScript Native** - Authored in strict TypeScript with shipped type declarations (`dist/types/index.d.ts`)
|
|
25
27
|
- **Accessibility First** - WCAG 2.2 AA compliant with full keyboard and screen reader support
|
|
26
28
|
- **Multilingual** - Built-in translations for 5 languages with easy extensibility
|
|
27
29
|
- **Fully Customizable** - CSS variables and comprehensive API
|
|
28
|
-
- **Modern Build** -
|
|
30
|
+
- **Modern Build** - ES modules with tree-shaking, code-splitting and source maps
|
|
29
31
|
- **Production Ready** - Thoroughly tested with real-world media content
|
|
30
32
|
|
|
31
33
|
## Features
|
|
@@ -35,8 +37,13 @@ Try VidPly in action:
|
|
|
35
37
|
- **Multiple Formats** - MP3, OGG, WAV (audio) / MP4, WebM (video)
|
|
36
38
|
- **YouTube Integration** - Embed YouTube videos with unified controls
|
|
37
39
|
- **Vimeo Integration** - Seamless Vimeo player integration
|
|
40
|
+
- **SoundCloud Integration** - Play SoundCloud tracks and sets via the Widget API with unified controls
|
|
38
41
|
- **HLS Streaming** - Adaptive bitrate streaming with quality selection and dynamic subtitle detection
|
|
42
|
+
- Uses `hls.js` on Chrome / Firefox / Edge / desktop Safari for full feature parity (quality menu, captions, transcript)
|
|
43
|
+
- Falls back to native HLS on iOS / iPadOS where MSE is unavailable; native text tracks are still surfaced through the VidPly captions and transcript UI
|
|
39
44
|
- **DASH Streaming** - MPEG-DASH support via dash.js with adaptive quality, TTML and WebVTT subtitles
|
|
45
|
+
- **Buffering Spinner** - Centered loading spinner shown automatically while media is buffering (HTML5, HLS, DASH)
|
|
46
|
+
- **Download Button** - Optional download control with custom URL support (`downloadButton` + `downloadUrl`)
|
|
40
47
|
- **Preview Thumbnails** - Video preview thumbnails on progress bar hover
|
|
41
48
|
- **Playlists** - Full playlist support with auto-advance and navigation
|
|
42
49
|
- Audio playlists with track info
|
|
@@ -148,10 +155,10 @@ This creates minified files in the `dist/` folder.
|
|
|
148
155
|
</script>
|
|
149
156
|
```
|
|
150
157
|
|
|
151
|
-
### Option 3: Development (
|
|
158
|
+
### Option 3: Development (TypeScript Sources)
|
|
152
159
|
|
|
153
|
-
```
|
|
154
|
-
import Player from './src/index
|
|
160
|
+
```typescript
|
|
161
|
+
import Player from './src/index';
|
|
155
162
|
|
|
156
163
|
const player = new Player('#my-video', {
|
|
157
164
|
controls: true,
|
|
@@ -161,6 +168,8 @@ const player = new Player('#my-video', {
|
|
|
161
168
|
});
|
|
162
169
|
```
|
|
163
170
|
|
|
171
|
+
> The library is authored in strict TypeScript. Type declarations ship to `dist/types/index.d.ts` so consumers using `tsc` or `vite` get full IntelliSense without any extra `@types` package.
|
|
172
|
+
|
|
164
173
|
## Quick Start
|
|
165
174
|
|
|
166
175
|
### 1. Build the Player
|
|
@@ -223,6 +232,32 @@ That's it! The player auto-initializes.
|
|
|
223
232
|
<video data-vidply src="https://example.com/manifest.mpd"></video>
|
|
224
233
|
```
|
|
225
234
|
|
|
235
|
+
### SoundCloud
|
|
236
|
+
|
|
237
|
+
```html
|
|
238
|
+
<audio data-vidply src="https://soundcloud.com/artist/track"></audio>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Download Button
|
|
242
|
+
|
|
243
|
+
Enable the download button and (optionally) provide a custom URL:
|
|
244
|
+
|
|
245
|
+
```html
|
|
246
|
+
<video
|
|
247
|
+
data-vidply
|
|
248
|
+
data-vidply-download-button="true"
|
|
249
|
+
data-vidply-download-url="/files/lecture.mp4"
|
|
250
|
+
src="/streams/lecture/manifest.mpd">
|
|
251
|
+
</video>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
const player = new Player('#my-video', {
|
|
256
|
+
downloadButton: true,
|
|
257
|
+
downloadUrl: '/files/lecture.mp4' // optional, falls back to current src
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
226
261
|
### DASH + HLS + MP4 Fallback
|
|
227
262
|
|
|
228
263
|
For maximum device compatibility, provide all three formats:
|
|
@@ -274,7 +309,9 @@ const player = new Player('#video', {
|
|
|
274
309
|
signLanguageButton: true,
|
|
275
310
|
fullscreenButton: true,
|
|
276
311
|
pipButton: true,
|
|
277
|
-
|
|
312
|
+
downloadButton: false, // Show a download button in the control bar
|
|
313
|
+
downloadUrl: null, // Optional explicit download URL (falls back to current src)
|
|
314
|
+
|
|
278
315
|
// Captions
|
|
279
316
|
captions: true,
|
|
280
317
|
captionsDefault: false,
|
|
@@ -613,8 +650,8 @@ VidPly provides extensive CSS variables for easy customization:
|
|
|
613
650
|
|
|
614
651
|
#### Option 2: JavaScript API
|
|
615
652
|
|
|
616
|
-
```
|
|
617
|
-
import { i18n } from './src/i18n/i18n
|
|
653
|
+
```typescript
|
|
654
|
+
import { i18n } from './src/i18n/i18n';
|
|
618
655
|
|
|
619
656
|
// Load language file from URL
|
|
620
657
|
await i18n.loadLanguageFromUrl('pt', 'languages/pt.json');
|
|
@@ -631,8 +668,8 @@ i18n.setLanguage('pt');
|
|
|
631
668
|
|
|
632
669
|
#### Option 3: Add Translations Programmatically
|
|
633
670
|
|
|
634
|
-
```
|
|
635
|
-
import { i18n } from './src/i18n/i18n
|
|
671
|
+
```typescript
|
|
672
|
+
import { i18n } from './src/i18n/i18n';
|
|
636
673
|
|
|
637
674
|
i18n.addTranslation('pt', {
|
|
638
675
|
player: {
|
|
@@ -671,14 +708,16 @@ The player supports both JSON and YAML formats for language files.
|
|
|
671
708
|
|
|
672
709
|
## Build Process
|
|
673
710
|
|
|
674
|
-
VidPly uses a modern build system with esbuild for
|
|
711
|
+
VidPly uses a modern build system with esbuild for TypeScript bundling, the TypeScript compiler for `.d.ts` declarations, and clean-css for CSS.
|
|
675
712
|
|
|
676
713
|
### Available Scripts
|
|
677
714
|
|
|
678
715
|
```bash
|
|
679
|
-
npm run build # Build everything (JS + CSS)
|
|
680
|
-
npm run build:js #
|
|
716
|
+
npm run build # Build everything (JS + types + CSS)
|
|
717
|
+
npm run build:js # Bundle TypeScript with esbuild (ESM + IIFE)
|
|
718
|
+
npm run build:types # Emit type declarations to dist/types/
|
|
681
719
|
npm run build:css # Build CSS only
|
|
720
|
+
npm run typecheck # Run tsc --noEmit
|
|
682
721
|
npm run watch # Watch mode for development
|
|
683
722
|
npm run clean # Clean dist directory
|
|
684
723
|
npm run dev # Start dev server
|
|
@@ -693,6 +732,7 @@ npm run test:all # Run all tests
|
|
|
693
732
|
- `dist/prod/vidply.esm.min.js` - ES Module (production)
|
|
694
733
|
- `dist/legacy/vidply.js` - IIFE (development)
|
|
695
734
|
- `dist/legacy/vidply.min.js` - IIFE (production)
|
|
735
|
+
- `dist/types/index.d.ts` - TypeScript declarations
|
|
696
736
|
- `dist/vidply.css` - Styles (unminified)
|
|
697
737
|
- `dist/vidply.min.css` - Styles (minified)
|
|
698
738
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
CaptionManager
|
|
8
|
-
} from "./vidply.chunk-
|
|
8
|
+
} from "./vidply.chunk-55W33EGZ.js";
|
|
9
9
|
import "./vidply.chunk-SXDZXXZD.js";
|
|
10
10
|
import "./vidply.chunk-VLF6H5PZ.js";
|
|
11
11
|
|
|
@@ -569,4 +569,4 @@ var AudioDescriptionManager = class {
|
|
|
569
569
|
export {
|
|
570
570
|
AudioDescriptionManager
|
|
571
571
|
};
|
|
572
|
-
//# sourceMappingURL=vidply.AudioDescriptionManager-
|
|
572
|
+
//# sourceMappingURL=vidply.AudioDescriptionManager-AXPP5OKO.js.map
|
|
@@ -213,18 +213,20 @@ var HLSRenderer = class {
|
|
|
213
213
|
this.hls.on(window.Hls.Events.ERROR, (_event, data) => {
|
|
214
214
|
this.handleHlsError(data);
|
|
215
215
|
});
|
|
216
|
-
this.hls.on(window.Hls.Events.FRAG_BUFFERED, (_event,
|
|
216
|
+
this.hls.on(window.Hls.Events.FRAG_BUFFERED, (_event, _data) => {
|
|
217
217
|
this.player.state.buffering = false;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}, 100);
|
|
218
|
+
});
|
|
219
|
+
this.hls.on(window.Hls.Events.SUBTITLE_FRAG_PROCESSED, (_event, data) => {
|
|
220
|
+
if (!data || !data.success) return;
|
|
221
|
+
const count = this._getTotalCueCount();
|
|
222
|
+
if (count > this._lastKnownCueCount) {
|
|
223
|
+
this._lastKnownCueCount = count;
|
|
224
|
+
this.player.emit("textcuesupdate");
|
|
226
225
|
}
|
|
227
226
|
});
|
|
227
|
+
this.hls.on(window.Hls.Events.CUES_PARSED, (_event, _data) => {
|
|
228
|
+
this.player.emit("textcuesupdate");
|
|
229
|
+
});
|
|
228
230
|
}
|
|
229
231
|
_getTotalCueCount() {
|
|
230
232
|
const textTracks = this.media.textTracks;
|
|
@@ -514,4 +516,4 @@ var HLSRenderer = class {
|
|
|
514
516
|
export {
|
|
515
517
|
HLSRenderer
|
|
516
518
|
};
|
|
517
|
-
//# sourceMappingURL=vidply.HLSRenderer-
|
|
519
|
+
//# sourceMappingURL=vidply.HLSRenderer-IVT5CQQ4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/renderers/HLSRenderer.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Renderer } from '../types/renderer.js';\r\nimport type { Player } from '../core/Player.js';\r\n\r\nexport class HLSRenderer implements Renderer {\r\n player: Player;\r\n media: HTMLMediaElement;\r\n hls: any;\r\n _hlsSourceLoaded: boolean;\r\n _pendingSrc: string | null;\r\n _hlsSubtitleTracksCount: number | undefined;\r\n _cueUpdateTimer: ReturnType<typeof setInterval> | null;\r\n _lastKnownCueCount: number;\r\n _nativeTrackListenersDestroyed?: boolean;\r\n _didDeferredLoad?: boolean;\r\n _cleanupNativeTextTrackListeners: () => void;\r\n\r\n constructor(player: Player) {\r\n this.player = player;\r\n this.media = player.element;\r\n this.hls = null;\r\n this._hlsSourceLoaded = false;\r\n this._pendingSrc = null;\r\n this._hlsSubtitleTracksCount = undefined;\r\n this._cueUpdateTimer = null;\r\n this._lastKnownCueCount = 0;\r\n this._cleanupNativeTextTrackListeners = () => {};\r\n }\r\n\r\n async init() {\r\n // Check if browser natively supports HLS (Safari)\r\n if (this.canPlayNatively()) {\r\n this.player.log('Using native HLS support');\r\n await this.initNative();\r\n } else {\r\n this.player.log('Using hls.js for HLS support');\r\n await this.initHlsJs();\r\n }\r\n }\r\n\r\n canPlayNatively() {\r\n // Use native HLS only on iOS/iPadOS (MSE unavailable); desktop macOS Safari uses hls.js\r\n // for quality switching and parity with Chrome/Firefox.\r\n const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;\r\n // iPad in desktop mode reports MacIntel but has touch\r\n const isIPadDesktopMode = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;\r\n\r\n if (!isIOS && !isIPadDesktopMode) {\r\n return false;\r\n }\r\n\r\n const video = document.createElement('video');\r\n return video.canPlayType('application/vnd.apple.mpegurl') !== '';\r\n }\r\n\r\n async initNative() {\r\n // Use HTML5 renderer for native HLS support\r\n const HTML5Renderer = (await import('./HTML5Renderer.js')).HTML5Renderer;\r\n const renderer = new HTML5Renderer(this.player);\r\n await renderer.init();\r\n\r\n // Copy methods from HTML5Renderer onto this instance\r\n Object.getOwnPropertyNames(Object.getPrototypeOf(renderer)).forEach((method: string) => {\r\n if (method !== 'constructor' && typeof (renderer as any)[method] === 'function') {\r\n (this as any)[method] = (renderer as any)[method].bind(renderer);\r\n }\r\n });\r\n\r\n this._attachNativeTextTrackListeners();\r\n\r\n // The method-copy above shadows HLSRenderer.prototype.destroy with\r\n // HTML5Renderer's destroy. Wrap it so our listeners are also cleaned up.\r\n const html5Destroy = this.destroy;\r\n this.destroy = () => {\r\n this._cleanupNativeTextTrackListeners();\r\n html5Destroy();\r\n };\r\n }\r\n\r\n /**\r\n * Listen for HLS-exposed text tracks so captions/transcript buttons appear on native HLS.\r\n * Debounces rapid addtrack bursts (one per subtitle rendition in the manifest).\r\n */\r\n _attachNativeTextTrackListeners() {\r\n let debounceTimer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n const checkTracks = () => {\r\n clearTimeout(debounceTimer);\r\n debounceTimer = setTimeout(() => {\r\n if (this._nativeTrackListenersDestroyed) return;\r\n const tracks = this.media.textTracks;\r\n let count = 0;\r\n for (let i = 0; i < tracks.length; i++) {\r\n const k = tracks[i].kind;\r\n if (k === 'subtitles' || k === 'captions') {\r\n count++;\r\n }\r\n }\r\n this._hlsSubtitleTracksCount = count;\r\n this.updateCaptionButtonsForHls();\r\n }, 150);\r\n };\r\n\r\n this.media.textTracks.addEventListener('addtrack', checkTracks);\r\n this.media.textTracks.addEventListener('removetrack', checkTracks);\r\n this.media.addEventListener('loadedmetadata', checkTracks);\r\n\r\n this._cleanupNativeTextTrackListeners = () => {\r\n this._nativeTrackListenersDestroyed = true;\r\n clearTimeout(debounceTimer);\r\n this.media.textTracks.removeEventListener('addtrack', checkTracks);\r\n this.media.textTracks.removeEventListener('removetrack', checkTracks);\r\n this.media.removeEventListener('loadedmetadata', checkTracks);\r\n };\r\n }\r\n\r\n async initHlsJs() {\r\n // Hide native controls\r\n this.media.controls = false;\r\n this.media.removeAttribute('controls');\r\n \r\n // Load hls.js if not already loaded\r\n if (!window.Hls) {\r\n await this.loadHlsJs();\r\n }\r\n\r\n if (!window.Hls?.isSupported()) {\r\n throw new Error('HLS is not supported in this browser');\r\n }\r\n const HlsCtor = window.Hls!;\r\n\r\n // HTML5 spec: If video has src attribute, <source> children are not allowed.\r\n // hls.js sets a blob: URL on the src attribute, so we must remove any <source> elements\r\n // to maintain valid HTML. Store the original source URL first.\r\n const sourceElements = Array.from(this.media.querySelectorAll('source'));\r\n let originalSrc = null;\r\n if (sourceElements.length > 0) {\r\n originalSrc = sourceElements[0].getAttribute('src');\r\n sourceElements.forEach(source => source.remove());\r\n this.player.log('Removed <source> elements for HTML5 validity (hls.js uses src attribute)');\r\n }\r\n\r\n // Create hls.js instance with better error recovery\r\n this.hls = new HlsCtor({\r\n debug: this.player.options.debug,\r\n // When deferLoad is enabled, do not start loading until the first play().\r\n autoStartLoad: !this.player.options.deferLoad,\r\n enableWorker: true,\r\n lowLatencyMode: false,\r\n backBufferLength: 90,\r\n maxBufferLength: 30,\r\n maxMaxBufferLength: 600,\r\n maxBufferSize: 60 * 1000 * 1000,\r\n maxBufferHole: 0.5,\r\n // Network retry settings\r\n manifestLoadingTimeOut: 10000,\r\n manifestLoadingMaxRetry: 4,\r\n manifestLoadingRetryDelay: 1000,\r\n manifestLoadingMaxRetryTimeout: 64000,\r\n levelLoadingTimeOut: 10000,\r\n levelLoadingMaxRetry: 4,\r\n levelLoadingRetryDelay: 1000,\r\n levelLoadingMaxRetryTimeout: 64000,\r\n fragLoadingTimeOut: 20000,\r\n fragLoadingMaxRetry: 6,\r\n fragLoadingRetryDelay: 1000,\r\n fragLoadingMaxRetryTimeout: 64000\r\n });\r\n\r\n // Attach media element\r\n this.hls.attachMedia(this.media);\r\n\r\n // Load source - use currentSource for external renderers, or get from attribute\r\n // Priority: currentSource > originalSrc (from removed <source>) > data-vidply-src > src attribute\r\n let src = this.player.currentSource;\r\n \r\n if (!src && originalSrc) {\r\n src = originalSrc;\r\n }\r\n \r\n if (!src) {\r\n // Try data-vidply-src attribute (used by TYPO3 integration)\r\n src = this.player.element.getAttribute('data-vidply-src');\r\n }\r\n \r\n if (!src) {\r\n // Fallback to element's src attribute (but not blob: URLs)\r\n const elementSrc = this.player.element.getAttribute('src') || this.player.element.src;\r\n if (elementSrc && !elementSrc.startsWith('blob:')) {\r\n src = elementSrc;\r\n }\r\n }\r\n \r\n this.player.log(`Loading HLS source: ${src}`, 'log');\r\n \r\n if (!src) {\r\n throw new Error('No HLS source found');\r\n }\r\n \r\n if (this.player.options.deferLoad) {\r\n // Defer manifest/segment loading until first play()\r\n this._pendingSrc = src;\r\n } else {\r\n this.hls.loadSource(src);\r\n this._hlsSourceLoaded = true;\r\n }\r\n\r\n // Attach events\r\n this.attachHlsEvents();\r\n this.attachMediaEvents();\r\n }\r\n\r\n async loadHlsJs() {\r\n return new Promise<void>((resolve, reject) => {\r\n const script = document.createElement('script');\r\n script.src = 'https://cdn.jsdelivr.net/npm/hls.js@latest';\r\n script.onload = () => resolve();\r\n script.onerror = () => reject(new Error('Failed to load hls.js'));\r\n document.head.appendChild(script);\r\n });\r\n }\r\n\r\n attachHlsEvents() {\r\n this.hls.on(window.Hls!.Events.MANIFEST_PARSED, (_event: any, data: any) => {\r\n this.player.log('HLS manifest loaded, found ' + data.levels.length + ' quality levels');\r\n this.player.emit('hlsmanifestparsed', data);\r\n \r\n // Show VidPly controls (remove external controls class if present)\r\n if (this.player.container) {\r\n this.player.container.classList.remove('vidply-external-controls');\r\n }\r\n \r\n // Check for subtitle tracks after manifest parse\r\n // This handles streams without subtitles (SUBTITLE_TRACKS_UPDATED won't fire for them)\r\n setTimeout(() => {\r\n if (this._hlsSubtitleTracksCount === undefined || this._hlsSubtitleTracksCount === 0) {\r\n const currentCount = this.hls?.subtitleTracks?.length || 0;\r\n if (currentCount === 0) {\r\n this._hlsSubtitleTracksCount = 0;\r\n this.updateCaptionButtonsForHls();\r\n }\r\n }\r\n }, 500);\r\n });\r\n\r\n this.hls.on(window.Hls!.Events.LEVEL_SWITCHED, (_event: any, data: any) => {\r\n this.player.log('HLS level switched to ' + data.level);\r\n this.player.emit('hlslevelswitched', data);\r\n });\r\n\r\n // Handle HLS subtitle tracks\r\n this.hls.on(window.Hls!.Events.SUBTITLE_TRACKS_UPDATED, (_event: any, data: any) => {\r\n this.player.log('HLS subtitle tracks updated, found ' + data.subtitleTracks.length + ' tracks');\r\n this.player.emit('hlssubtitletracksupdated', data);\r\n this._hlsSubtitleTracksCount = data.subtitleTracks.length;\r\n this.updateCaptionButtonsForHls();\r\n if (data.subtitleTracks.length > 0) {\r\n this._startCueUpdatePolling();\r\n }\r\n });\r\n\r\n this.hls.on(window.Hls!.Events.SUBTITLE_TRACK_SWITCH, (_event: any, data: any) => {\r\n this.player.log('HLS subtitle track switched to ' + data.id);\r\n this.player.emit('hlssubtitletrackswitch', data);\r\n this._lastKnownCueCount = 0;\r\n this._startCueUpdatePolling();\r\n });\r\n\r\n this.hls.on(window.Hls!.Events.ERROR, (_event: any, data: any) => {\r\n this.handleHlsError(data);\r\n });\r\n\r\n this.hls.on(window.Hls!.Events.FRAG_BUFFERED, (_event: any, _data: any) => {\r\n this.player.state.buffering = false;\r\n });\r\n\r\n // Subtitle fragments do NOT go through the media source buffer, so\r\n // FRAG_BUFFERED is unreliable for them. SUBTITLE_FRAG_PROCESSED fires\r\n // immediately after hls.js has parsed the WebVTT and appended cues to\r\n // the TextTrack via `addCueToTrack`, which is exactly when we need to\r\n // refresh captions/transcript UIs.\r\n this.hls.on(window.Hls!.Events.SUBTITLE_FRAG_PROCESSED, (_event: any, data: any) => {\r\n if (!data || !data.success) return;\r\n const count = this._getTotalCueCount();\r\n if (count > this._lastKnownCueCount) {\r\n this._lastKnownCueCount = count;\r\n this.player.emit('textcuesupdate');\r\n }\r\n });\r\n\r\n // Some streams (e.g. IMSC1/TTML rendered externally by hls.js) deliver\r\n // their cues via CUES_PARSED instead of through a native TextTrack. Echo\r\n // that through to the transcript so it can refresh regardless of the\r\n // underlying subtitle format.\r\n this.hls.on(window.Hls!.Events.CUES_PARSED, (_event: any, _data: any) => {\r\n this.player.emit('textcuesupdate');\r\n });\r\n }\r\n\r\n _getTotalCueCount() {\r\n const textTracks = this.media.textTracks;\r\n let total = 0;\r\n if (!textTracks) return total;\r\n for (let i = 0; i < textTracks.length; i++) {\r\n const track = textTracks[i];\r\n if ((track.kind === 'subtitles' || track.kind === 'captions') && track.cues) {\r\n total += track.cues.length;\r\n }\r\n }\r\n return total;\r\n }\r\n\r\n _startCueUpdatePolling() {\r\n this._stopCueUpdatePolling();\r\n let prevCueCount = 0;\r\n let stableRounds = 0;\r\n\r\n this._cueUpdateTimer = setInterval(() => {\r\n const count = this._getTotalCueCount();\r\n\r\n if (count > prevCueCount) {\r\n prevCueCount = count;\r\n stableRounds = 0;\r\n this.player.emit('textcuesupdate');\r\n } else {\r\n stableRounds++;\r\n if (stableRounds >= 8) {\r\n this._stopCueUpdatePolling();\r\n if (count > 0) {\r\n this.player.emit('textcuesupdate');\r\n }\r\n }\r\n }\r\n }, 500);\r\n }\r\n\r\n _stopCueUpdatePolling() {\r\n if (this._cueUpdateTimer) {\r\n clearInterval(this._cueUpdateTimer);\r\n this._cueUpdateTimer = null;\r\n }\r\n }\r\n\r\n /**\r\n * Update caption buttons based on HLS subtitle tracks\r\n * Handles the case where control bar may not exist yet\r\n */\r\n updateCaptionButtonsForHls() {\r\n const tracksCount = this._hlsSubtitleTracksCount || 0;\r\n \r\n const doUpdate = () => {\r\n this.player.invalidateTrackCache();\r\n \r\n if (tracksCount > 0) {\r\n // HLS has subtitle tracks - refresh managers and add buttons\r\n if (this.player.captionManager) {\r\n this.player.captionManager.refreshTracks();\r\n }\r\n \r\n if (this.player.transcriptManager?.isVisible) {\r\n this.player.transcriptManager.loadTranscriptData();\r\n this.player.transcriptManager.updateLanguageSelector();\r\n }\r\n \r\n if (this.player.controlBar) {\r\n this.player.controlBar.ensureCaptionsButton();\r\n this.player.controlBar.ensureCaptionStyleButton();\r\n this.player.controlBar.ensureTranscriptButton();\r\n }\r\n } else {\r\n // No HLS subtitle tracks - clean up\r\n if (this.player.captionManager) {\r\n this.player.captionManager.refreshTracks();\r\n }\r\n \r\n if (this.player.transcriptManager?.isVisible) {\r\n this.player.transcriptManager.hideTranscript();\r\n }\r\n \r\n if (this.player.controlBar) {\r\n this.player.controlBar.removeHlsCaptionButtons(true);\r\n }\r\n }\r\n };\r\n \r\n if (this.player.controlBar) {\r\n doUpdate();\r\n return;\r\n }\r\n \r\n // Control bar doesn't exist yet - wait for ready event\r\n const onReady = () => {\r\n this.player.off('ready', onReady);\r\n doUpdate();\r\n };\r\n this.player.on('ready', onReady);\r\n }\r\n\r\n attachMediaEvents() {\r\n // Use same events as HTML5 renderer\r\n this.media.addEventListener('loadedmetadata', () => {\r\n this.player.state.duration = this.media.duration;\r\n this.player.emit('loadedmetadata');\r\n });\r\n\r\n this.media.addEventListener('durationchange', () => {\r\n const duration = this.media.duration;\r\n if (duration && isFinite(duration) && duration > 0) {\r\n this.player.state.duration = duration;\r\n this.player.emit('durationchange', duration);\r\n }\r\n });\r\n\r\n this.media.addEventListener('play', () => {\r\n this.player.state.playing = true;\r\n this.player.state.paused = false;\r\n this.player.state.ended = false;\r\n this.player.emit('play');\r\n \r\n if (this.player.options.onPlay) {\r\n this.player.options.onPlay.call(this.player);\r\n }\r\n });\r\n\r\n this.media.addEventListener('pause', () => {\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.emit('pause');\r\n \r\n if (this.player.options.onPause) {\r\n this.player.options.onPause.call(this.player);\r\n }\r\n });\r\n\r\n this.media.addEventListener('ended', () => {\r\n this.player.state.playing = false;\r\n this.player.state.paused = true;\r\n this.player.state.ended = true;\r\n this.player.emit('ended');\r\n \r\n if (this.player.options.onEnded) {\r\n this.player.options.onEnded.call(this.player);\r\n }\r\n \r\n if (this.player.options.loop) {\r\n this.player.seek(0);\r\n this.player.play();\r\n }\r\n });\r\n\r\n this.media.addEventListener('timeupdate', () => {\r\n this.player.state.currentTime = this.media.currentTime;\r\n this.player.emit('timeupdate', this.media.currentTime);\r\n \r\n if (this.player.options.onTimeUpdate) {\r\n this.player.options.onTimeUpdate.call(this.player, this.media.currentTime);\r\n }\r\n });\r\n\r\n this.media.addEventListener('volumechange', () => {\r\n this.player.state.volume = this.media.volume;\r\n this.player.state.muted = this.media.muted;\r\n this.player.emit('volumechange', this.media.volume);\r\n });\r\n\r\n this.media.addEventListener('waiting', () => {\r\n this.player.state.buffering = true;\r\n this.player.emit('waiting');\r\n });\r\n\r\n this.media.addEventListener('canplay', () => {\r\n this.player.state.buffering = false;\r\n this.player.emit('canplay');\r\n });\r\n\r\n this.media.addEventListener('error', () => {\r\n this.player.handleError(this.media.error);\r\n });\r\n }\r\n\r\n handleHlsError(data: any) {\r\n // Log detailed error info\r\n this.player.log(`HLS Error - Type: ${data.type}, Details: ${data.details}, Fatal: ${data.fatal}`, 'warn');\r\n if (data.response) {\r\n this.player.log(`Response code: ${data.response.code}, URL: ${data.response.url}`, 'warn');\r\n }\r\n \r\n if (data.fatal) {\r\n switch (data.type) {\r\n case window.Hls!.ErrorTypes.NETWORK_ERROR:\r\n this.player.log('Fatal network error, trying to recover...', 'error');\r\n this.player.log(`Network error details: ${data.details}`, 'error');\r\n setTimeout(() => {\r\n this.hls.startLoad();\r\n }, 1000);\r\n break;\r\n \r\n case window.Hls!.ErrorTypes.MEDIA_ERROR:\r\n this.player.log('Fatal media error, trying to recover...', 'error');\r\n this.hls.recoverMediaError();\r\n break;\r\n \r\n default:\r\n this.player.log('Fatal error, cannot recover', 'error');\r\n this.player.handleError(new Error(`HLS Error: ${data.type} - ${data.details}`));\r\n this.hls.destroy();\r\n break;\r\n }\r\n } else {\r\n this.player.log('Non-fatal HLS error: ' + data.details, 'warn');\r\n }\r\n }\r\n\r\n /**\r\n * Ensure the HLS manifest/initial loading is started without starting playback.\r\n * This makes playlist selection behave more like single-video initialization.\r\n */\r\n ensureLoaded() {\r\n if (!this.player.options.deferLoad) {\r\n return;\r\n }\r\n\r\n // Native HLS path delegates to HTML5Renderer; if we got here and have no hls.js instance,\r\n // there's nothing to do.\r\n if (!this.hls) {\r\n return;\r\n }\r\n\r\n if (this._hlsSourceLoaded) {\r\n return;\r\n }\r\n\r\n const src = this._pendingSrc || this.player._pendingSource || this.player.currentSource;\r\n if (!src) {\r\n return;\r\n }\r\n\r\n try {\r\n this.hls.loadSource(src);\r\n this._hlsSourceLoaded = true;\r\n // Start loading so manifest is parsed and levels/tracks become available.\r\n // Note: this may fetch initial fragments depending on stream/config.\r\n this.hls.startLoad();\r\n } catch (e) {\r\n // ignore\r\n }\r\n }\r\n\r\n play() {\r\n // Save scroll position to prevent browser from scrolling to video\r\n const scrollX = window.scrollX;\r\n const scrollY = window.scrollY;\r\n\r\n // If deferLoad is enabled, start HLS loading only on the first user play request.\r\n if (this.player.options.deferLoad && this.hls && !this._hlsSourceLoaded) {\r\n const src = this._pendingSrc || this.player.currentSource;\r\n if (src) {\r\n try {\r\n this.hls.loadSource(src);\r\n this.hls.startLoad();\r\n this._hlsSourceLoaded = true;\r\n } catch (e) {\r\n // ignore and let media.play() surface errors if any\r\n }\r\n }\r\n }\r\n \r\n const promise = this.media.play();\r\n \r\n // Restore scroll position immediately to prevent auto-scroll\r\n window.scrollTo(scrollX, scrollY);\r\n \r\n if (promise !== undefined) {\r\n promise.catch(error => {\r\n this.player.log('Play failed:', error, 'warn');\r\n });\r\n }\r\n }\r\n\r\n pause() {\r\n this.media.pause();\r\n }\r\n\r\n seek(time: number) {\r\n this.media.currentTime = time;\r\n }\r\n\r\n setVolume(volume: number) {\r\n this.media.volume = volume;\r\n }\r\n\r\n setMuted(muted: boolean) {\r\n this.media.muted = muted;\r\n }\r\n\r\n setPlaybackSpeed(speed: number) {\r\n this.media.playbackRate = speed;\r\n }\r\n\r\n switchQuality(levelIndex: number) {\r\n if (this.hls) {\r\n this.hls.currentLevel = levelIndex;\r\n }\r\n }\r\n\r\n getQualities() {\r\n if (this.hls && this.hls.levels) {\r\n // hls.js creates separate levels for each video+audio-group combination,\r\n // producing duplicates (e.g. three \"720p\" entries). Deduplicate by\r\n // resolution height, keeping the entry with the highest bitrate per height.\r\n const byHeight = new Map();\r\n\r\n this.hls.levels.forEach((level: any, index: number) => {\r\n const height = Number(level.height) || 0;\r\n const bitrate = Number(level.bitrate) || 0;\r\n const key = height > 0 ? height : `br_${bitrate}`;\r\n const existing = byHeight.get(key);\r\n\r\n if (!existing || bitrate > (existing.bitrate || 0)) {\r\n byHeight.set(key, { index, height: level.height, width: level.width, bitrate, level });\r\n }\r\n });\r\n\r\n return Array.from(byHeight.values()).map((entry: any) => {\r\n const height = Number(entry.height) || 0;\r\n const kb = entry.bitrate > 0 ? Math.round(entry.bitrate / 1000) : 0;\r\n const name = height > 0 ? `${height}p` : (kb > 0 ? `${kb} kb` : 'Auto');\r\n return { index: entry.index, height: entry.height, width: entry.width, bitrate: entry.bitrate, name };\r\n });\r\n }\r\n return [];\r\n }\r\n\r\n getCurrentQuality() {\r\n if (this.hls) {\r\n return this.hls.currentLevel;\r\n }\r\n return -1;\r\n }\r\n\r\n supportsAutoQuality() {\r\n return true;\r\n }\r\n\r\n isAutoQuality() {\r\n return this.hls?.currentLevel === -1;\r\n }\r\n\r\n destroy() {\r\n this._stopCueUpdatePolling();\r\n this._lastKnownCueCount = 0;\r\n if (this.hls) {\r\n this.hls.destroy();\r\n this.hls = null;\r\n }\r\n }\r\n}\r\n\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -146,10 +146,34 @@ var CaptionManager = class {
|
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Sync hls.js subtitle rendition to match the given language.
|
|
151
|
+
* Matches by lang, language, or falls back to name/label.
|
|
152
|
+
*/
|
|
153
|
+
_syncHlsSubtitleTrack(targetLang, targetLabel) {
|
|
154
|
+
const renderer = this.player.renderer;
|
|
155
|
+
if (!renderer?.hls || !renderer.hls.subtitleTracks?.length) return;
|
|
156
|
+
const tracks = renderer.hls.subtitleTracks;
|
|
157
|
+
let hlsIndex = tracks.findIndex(
|
|
158
|
+
(t) => t.lang === targetLang
|
|
159
|
+
);
|
|
160
|
+
if (hlsIndex < 0 && targetLabel) {
|
|
161
|
+
hlsIndex = tracks.findIndex(
|
|
162
|
+
(t) => t.name === targetLabel
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (hlsIndex >= 0 && renderer.hls.subtitleTrack !== hlsIndex) {
|
|
166
|
+
renderer.hls.subtitleTrack = hlsIndex;
|
|
167
|
+
this.player.log(`HLS subtitle track set to index ${hlsIndex} (${targetLang})`, "info");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
149
170
|
attachEvents() {
|
|
150
171
|
this.player.on("timeupdate", () => {
|
|
151
172
|
this.updateCaptions();
|
|
152
173
|
});
|
|
174
|
+
this.player.on("textcuesupdate", () => {
|
|
175
|
+
this.updateCaptions();
|
|
176
|
+
});
|
|
153
177
|
this.player.on("captionschange", () => {
|
|
154
178
|
this.updateStyles();
|
|
155
179
|
});
|
|
@@ -228,6 +252,7 @@ var CaptionManager = class {
|
|
|
228
252
|
ensureTrackReady();
|
|
229
253
|
}
|
|
230
254
|
});
|
|
255
|
+
this._syncHlsSubtitleTrack(selectedTrack.language, selectedTrack.label);
|
|
231
256
|
this.player.emit("captionsenabled", selectedTrack);
|
|
232
257
|
}
|
|
233
258
|
}
|
|
@@ -452,4 +477,4 @@ export {
|
|
|
452
477
|
rafWithTimeout,
|
|
453
478
|
CaptionManager
|
|
454
479
|
};
|
|
455
|
-
//# sourceMappingURL=vidply.chunk-
|
|
480
|
+
//# sourceMappingURL=vidply.chunk-55W33EGZ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/PerformanceUtils.ts", "../../src/controls/CaptionManager.ts"],
|
|
4
|
+
"sourcesContent": ["export function debounce<T extends (...args: unknown[]) => void>(func: T, wait = 100): T {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n return function executedFunction(this: unknown, ...args: unknown[]) {\n const later = () => {\n clearTimeout(timeout);\n func.apply(this, args);\n };\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n } as unknown as T;\n}\n\nexport function throttle<T extends (...args: unknown[]) => void>(func: T, limit = 100): T {\n let inThrottle = false;\n return function executedFunction(this: unknown, ...args: unknown[]) {\n if (!inThrottle) {\n func.apply(this, args);\n inThrottle = true;\n setTimeout(() => inThrottle = false, limit);\n }\n } as unknown as T;\n}\n\nexport function isMobile(breakpoint = 768): boolean {\n return window.innerWidth < breakpoint;\n}\n\nexport function rafWithTimeout(callback: () => void, timeout = 100): void {\n let called = false;\n\n const execute = () => {\n if (!called) {\n called = true;\n callback();\n }\n };\n\n requestAnimationFrame(execute);\n setTimeout(execute, timeout);\n}\n", "/**\n * Caption/Subtitle Manager\n */\n\nimport {DOMUtils} from '../utils/DOMUtils.js';\nimport {i18n} from '../i18n/i18n.js';\nimport {StorageManager} from '../utils/StorageManager.js';\nimport {debounce, isMobile, rafWithTimeout} from '../utils/PerformanceUtils.js';\nimport type { Player } from '../core/Player.js';\n\nexport class CaptionManager {\n player: Player;\n _altCueChangeHandler: any;\n cueChangeHandler: any;\n currentCue: any;\n currentTrack: any;\n debouncedPositionCaptions: any;\n element: any;\n storage: StorageManager;\n tracks: any[];\n\n constructor(player: Player) {\n this.player = player;\n this.element = null;\n this.tracks = [];\n this.currentTrack = null;\n this.currentCue = null;\n \n // Storage manager\n this.storage = new StorageManager('vidply');\n \n // Load saved preferences\n this.loadSavedPreferences();\n\n this.init();\n }\n \n loadSavedPreferences() {\n const saved = this.storage.getCaptionPreferences();\n if (saved) {\n // Override player options with saved preferences\n if (saved.fontSize) this.player.options.captionsFontSize = saved.fontSize;\n if (saved.fontFamily) this.player.options.captionsFontFamily = saved.fontFamily;\n if (saved.color) this.player.options.captionsColor = saved.color;\n if (saved.backgroundColor) this.player.options.captionsBackgroundColor = saved.backgroundColor;\n if (saved.opacity !== undefined) this.player.options.captionsOpacity = saved.opacity;\n }\n }\n \n saveCaptionPreferences() {\n this.storage.saveCaptionPreferences({\n fontSize: this.player.options.captionsFontSize,\n fontFamily: this.player.options.captionsFontFamily,\n color: this.player.options.captionsColor,\n backgroundColor: this.player.options.captionsBackgroundColor,\n opacity: this.player.options.captionsOpacity\n });\n }\n\n init() {\n this.createElement();\n this.loadTracks();\n this.attachEvents();\n\n // Only enable captions via captionsDefault option if no default track was found\n // (loadTracks() already enables tracks with the default attribute)\n if (this.player.options.captionsDefault && this.tracks.length > 0 && !this.currentTrack) {\n this.enable();\n }\n }\n\n createElement() {\n this.element = DOMUtils.createElement('div', {\n className: `${this.player.options.classPrefix}-captions`,\n attributes: {\n 'role': 'region',\n 'aria-label': i18n.t('player.captions')\n }\n });\n\n // Apply caption styles\n this.updateStyles();\n\n // Append to videoWrapper if it exists, otherwise to container\n const target = this.player.videoWrapper || this.player.container;\n target.appendChild(this.element);\n }\n\n loadTracks() {\n const textTracks = this.player.element.textTracks;\n let defaultTrackIndex = -1;\n\n // Safari's native HLS can expose both SUBTITLES (WebVTT sidecar) and\n // CLOSED-CAPTIONS (inband CEA-608) groups as separate TextTrack objects\n // with the same language and label. Deduplicate to one menu entry per\n // language+label, storing the extras as alternatives. enable() will\n // listen on all of them and use whichever delivers cues first.\n const seen = new Map();\n\n for (let i = 0; i < textTracks.length; i++) {\n const track = textTracks[i];\n\n if ((track.kind === 'subtitles' || track.kind === 'captions') && !track._vidplyStale) {\n track.mode = 'hidden';\n\n const dedupeKey = `${track.language}|${track.label}`;\n const existing = seen.get(dedupeKey);\n\n if (existing) {\n existing.alternatives.push(track);\n continue;\n }\n\n const trackElement = this.player.findTrackElement(track);\n const isDefault = trackElement && trackElement.hasAttribute('default');\n const entry = {\n track,\n language: track.language,\n label: track.label,\n kind: track.kind,\n index: i,\n isDefault,\n alternatives: [] as TextTrack[]\n };\n\n this.tracks.push(entry);\n seen.set(dedupeKey, entry);\n\n if (isDefault) {\n defaultTrackIndex = this.tracks.length - 1;\n }\n }\n }\n \n if (defaultTrackIndex >= 0) {\n requestAnimationFrame(() => {\n this.enable(defaultTrackIndex);\n });\n }\n }\n\n /**\n * Sync hls.js subtitle rendition to match the given language.\n * Matches by lang, language, or falls back to name/label.\n */\n private _syncHlsSubtitleTrack(targetLang: string, targetLabel?: string) {\n const renderer = (this.player as any).renderer;\n if (!renderer?.hls || !renderer.hls.subtitleTracks?.length) return;\n\n const tracks = renderer.hls.subtitleTracks;\n let hlsIndex = tracks.findIndex(\n (t: any) => t.lang === targetLang\n );\n // Fallback: match by name/label\n if (hlsIndex < 0 && targetLabel) {\n hlsIndex = tracks.findIndex(\n (t: any) => t.name === targetLabel\n );\n }\n if (hlsIndex >= 0 && renderer.hls.subtitleTrack !== hlsIndex) {\n renderer.hls.subtitleTrack = hlsIndex;\n this.player.log(`HLS subtitle track set to index ${hlsIndex} (${targetLang})`, 'info');\n }\n }\n\n attachEvents() {\n this.player.on('timeupdate', () => {\n this.updateCaptions();\n });\n\n // When hls.js finishes loading new subtitle cues, refresh captions\n this.player.on('textcuesupdate', () => {\n this.updateCaptions();\n });\n\n this.player.on('captionschange', () => {\n this.updateStyles();\n });\n \n // Debounced resize handler to avoid excessive recalculations\n this.debouncedPositionCaptions = debounce(() => {\n this.positionCaptionsOnMobile();\n }, 150);\n \n window.addEventListener('resize', this.debouncedPositionCaptions);\n \n // Recalculate on fullscreen change with RAF\n this.player.on('enterfullscreen', () => {\n rafWithTimeout(() => this.positionCaptionsOnMobile(), 100);\n });\n \n this.player.on('exitfullscreen', () => {\n rafWithTimeout(() => this.positionCaptionsOnMobile(), 100);\n });\n }\n\n enable(trackIndex = 0) {\n if (this.tracks.length === 0) {\n return;\n }\n\n // Disable current track and clean up alternative listeners\n this._cleanupTrackListeners();\n\n // Enable selected track\n const selectedTrack = this.tracks[trackIndex];\n\n if (selectedTrack && selectedTrack.track) {\n selectedTrack.track.mode = 'hidden';\n this.currentTrack = selectedTrack;\n this.player.state.captionsEnabled = true;\n \n if (selectedTrack.language) {\n this.element.setAttribute('lang', selectedTrack.language);\n }\n\n this.cueChangeHandler = () => {\n this.updateCaptions();\n };\n selectedTrack.track.addEventListener('cuechange', this.cueChangeHandler);\n\n // Native HLS dedup: Safari can expose duplicate TextTrack objects\n // for the same language (SUBTITLES vs CLOSED-CAPTIONS groups) but\n // only one actually delivers cues. Listen on all alternatives and\n // swap to whichever fires first with real cues.\n if (selectedTrack.alternatives && selectedTrack.alternatives.length > 0) {\n this._altCueChangeHandler = () => {\n if (this.currentTrack !== selectedTrack) return;\n for (const alt of selectedTrack.alternatives) {\n if (alt.activeCues && alt.activeCues.length > 0) {\n this.player.log(`Switching to alternative caption track for \"${selectedTrack.label}\"`, 'info');\n selectedTrack.track.removeEventListener('cuechange', this.cueChangeHandler);\n selectedTrack.alternatives.forEach((a: TextTrack) => a.removeEventListener('cuechange', this._altCueChangeHandler));\n selectedTrack.track = alt;\n selectedTrack.track.addEventListener('cuechange', this.cueChangeHandler);\n this._altCueChangeHandler = null;\n this.updateCaptions();\n return;\n }\n }\n };\n selectedTrack.alternatives.forEach((alt: TextTrack) => {\n alt.mode = 'hidden';\n alt.addEventListener('cuechange', this._altCueChangeHandler);\n });\n }\n\n const ensureTrackReady = () => {\n if (selectedTrack.track.readyState < 2) {\n const onTrackLoad = () => {\n selectedTrack.track.removeEventListener('load', onTrackLoad);\n selectedTrack.track.removeEventListener('error', onTrackLoad);\n requestAnimationFrame(() => {\n if (this.currentTrack && this.currentTrack.track === selectedTrack.track) {\n this.updateCaptions();\n }\n });\n };\n selectedTrack.track.addEventListener('load', onTrackLoad, { once: true });\n selectedTrack.track.addEventListener('error', onTrackLoad, { once: true });\n } else {\n requestAnimationFrame(() => {\n if (this.currentTrack && this.currentTrack.track === selectedTrack.track) {\n this.updateCaptions();\n }\n });\n }\n };\n\n requestAnimationFrame(() => {\n if (this.currentTrack && this.currentTrack.track === selectedTrack.track) {\n ensureTrackReady();\n }\n });\n\n // Sync hls.js subtitle rendition to match the selected track language\n this._syncHlsSubtitleTrack(selectedTrack.language, selectedTrack.label);\n\n this.player.emit('captionsenabled', selectedTrack);\n }\n }\n\n _cleanupTrackListeners() {\n if (this.currentTrack && this.currentTrack.track) {\n if (this.cueChangeHandler) {\n this.currentTrack.track.removeEventListener('cuechange', this.cueChangeHandler);\n }\n if (this._altCueChangeHandler && this.currentTrack.alternatives) {\n this.currentTrack.alternatives.forEach((alt: TextTrack) => {\n alt.removeEventListener('cuechange', this._altCueChangeHandler);\n });\n }\n this.currentTrack.track.mode = 'hidden';\n }\n this._altCueChangeHandler = null;\n }\n\n disable() {\n this._cleanupTrackListeners();\n this.currentTrack = null;\n\n this.element.style.display = 'none';\n this.element.innerHTML = '';\n this.element.removeAttribute('lang');\n this.currentCue = null;\n this.player.state.captionsEnabled = false;\n this.player.emit('captionsdisabled');\n }\n\n updateCaptions() {\n if (!this.currentTrack || !this.currentTrack.track) {\n return;\n }\n\n // When the renderer handles its own caption display (e.g. dash.js TTML\n // rendering div), skip VidPly's caption overlay entirely.\n if (this.player.renderer?.handlesOwnCaptions?.()) {\n return;\n }\n\n // Ensure track mode is set to 'hidden' (not 'disabled') to receive cue updates\n // 'hidden' mode loads cues and fires cuechange events, but doesn't show native captions\n if (this.currentTrack.track.mode === 'disabled') {\n this.currentTrack.track.mode = 'hidden';\n }\n\n if (this.currentTrack.track.mode === 'showing') {\n this.currentTrack.track.mode = 'hidden';\n }\n\n if (!this.currentTrack.track.activeCues) {\n if (this.currentTrack.track.cues && this.currentTrack.track.cues.length > 0) {\n if (this.currentCue) {\n this.element.innerHTML = '';\n this.element.style.display = 'none';\n this.currentCue = null;\n }\n }\n return;\n }\n\n const activeCues = this.currentTrack.track.activeCues;\n const isAudioPlayer = this.player.element.tagName.toLowerCase() === 'audio';\n\n if (activeCues.length > 0) {\n const cue = activeCues[0];\n\n if (this.currentCue !== cue) {\n this.currentCue = cue;\n\n let text = cue.text || '';\n text = this.parseVTTFormatting(text);\n const sanitized = DOMUtils.sanitizeHTML(text);\n\n if (!sanitized.trim()) {\n return;\n }\n\n if (isAudioPlayer) {\n const existingCues = this.element.querySelectorAll(`.${this.player.options.classPrefix}-caption-cue`);\n existingCues.forEach((el: Element) => el.classList.remove(`${this.player.options.classPrefix}-caption-active`));\n \n const cueId = `cue-${cue.startTime}-${cue.endTime}`;\n let cueElement = this.element.querySelector(`[data-cue-id=\"${cueId}\"]`);\n \n if (!cueElement) {\n cueElement = document.createElement('div');\n cueElement.className = `${this.player.options.classPrefix}-caption-cue`;\n cueElement.setAttribute('data-cue-id', cueId);\n cueElement.innerHTML = sanitized;\n this.element.appendChild(cueElement);\n }\n \n cueElement.classList.add(`${this.player.options.classPrefix}-caption-active`);\n \n requestAnimationFrame(() => {\n if (cueElement) {\n cueElement.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n });\n } else {\n this.element.innerHTML = sanitized;\n }\n\n this.element.style.display = 'block';\n this.positionCaptionsOnMobile();\n this.player.emit('captionchange', cue);\n }\n } else if (this.currentCue) {\n if (!isAudioPlayer) {\n this.element.innerHTML = '';\n this.element.style.display = 'none';\n }\n this.currentCue = null;\n }\n }\n \n positionCaptionsOnMobile() {\n if (!this.element || this.element.style.display === 'none') {\n return;\n }\n \n const isFullscreen = this.player.state?.fullscreen || false;\n const mobile = isMobile();\n \n if (!mobile && !isFullscreen) {\n // Reset to CSS defaults on desktop\n this.element.style.bottom = '';\n return;\n }\n \n const controls = this.player.controlBar?.element;\n if (!controls) {\n return;\n }\n \n // Use requestAnimationFrame to ensure layout is complete\n requestAnimationFrame(() => {\n if (!this.element || this.element.style.display === 'none') {\n return;\n }\n \n const controlsRect = controls.getBoundingClientRect();\n const wrapperRect = this.player.videoWrapper.getBoundingClientRect();\n const bottomOffset = wrapperRect.bottom - controlsRect.top + 16;\n \n this.element.style.bottom = `${bottomOffset}px`;\n \n if (this.player.options.debug) {\n console.log('[VidPly] Caption position:', {\n mobile,\n isFullscreen,\n controlsHeight: controlsRect.height,\n bottomOffset: `${bottomOffset}px`\n });\n }\n });\n }\n\n parseVTTFormatting(text: string) {\n // Basic VTT tag support\n text = text.replace(/<c[^>]*>(.*?)<\\/c>/g, '<span class=\"caption-class\">$1</span>');\n text = text.replace(/<b>(.*?)<\\/b>/g, '<strong>$1</strong>');\n text = text.replace(/<i>(.*?)<\\/i>/g, '<em>$1</em>');\n text = text.replace(/<u>(.*?)<\\/u>/g, '<u>$1</u>');\n\n // Voice tags\n text = text.replace(/<v\\s+([^>]+)>(.*?)<\\/v>/g, '<span class=\"caption-voice\" data-voice=\"$1\">$2</span>');\n\n return text;\n }\n\n updateStyles() {\n if (!this.element) return;\n\n const options = this.player.options;\n\n this.element.style.fontSize = options.captionsFontSize;\n this.element.style.fontFamily = options.captionsFontFamily;\n this.element.style.color = options.captionsColor;\n this.element.style.backgroundColor = this.hexToRgba(\n options.captionsBackgroundColor,\n options.captionsOpacity\n );\n }\n\n hexToRgba(hex: string, alpha: number) {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (result) {\n return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${alpha})`;\n }\n return hex;\n }\n\n setCaptionStyle(property: string, value: string | number) {\n switch (property) {\n case 'fontSize':\n this.player.options.captionsFontSize = value;\n break;\n case 'fontFamily':\n this.player.options.captionsFontFamily = value;\n break;\n case 'color':\n this.player.options.captionsColor = value;\n break;\n case 'backgroundColor':\n this.player.options.captionsBackgroundColor = value;\n break;\n case 'opacity':\n this.player.options.captionsOpacity = value;\n break;\n }\n\n this.updateStyles();\n this.saveCaptionPreferences();\n this.player.emit('captionschange');\n }\n\n getAvailableTracks() {\n return this.tracks.map((t: any, index: number) => ({\n index,\n language: t.language,\n label: t.label || t.language,\n kind: t.kind\n }));\n }\n\n /**\n * Refresh tracks list - useful when HLS adds subtitle tracks dynamically\n */\n refreshTracks() {\n // Store current track info to restore after refresh\n const currentLanguage = this.currentTrack?.language;\n const wasEnabled = this.player.state.captionsEnabled;\n \n // Disable current caption\n if (this.currentTrack) {\n this.disable();\n }\n \n // Clear existing tracks\n this.tracks = [];\n \n // Reload tracks from video element\n this.loadTracks();\n \n this.player.log(`Caption tracks refreshed, found ${this.tracks.length} tracks`, 'info');\n \n // Try to restore previous track if it exists\n if (wasEnabled && currentLanguage && this.tracks.length > 0) {\n const matchingIndex = this.tracks.findIndex((t: any) => t.language === currentLanguage);\n if (matchingIndex >= 0) {\n this.enable(matchingIndex);\n }\n }\n \n return this.tracks.length;\n }\n\n switchTrack(trackIndex: number) {\n if (trackIndex >= 0 && trackIndex < this.tracks.length) {\n this.disable();\n this.enable(trackIndex);\n }\n }\n\n destroy() {\n this.disable();\n\n if (this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n }\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/dev/vidply.esm.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
isMobile,
|
|
16
16
|
rafWithTimeout,
|
|
17
17
|
throttle
|
|
18
|
-
} from "./vidply.chunk-
|
|
18
|
+
} from "./vidply.chunk-55W33EGZ.js";
|
|
19
19
|
import {
|
|
20
20
|
StorageManager
|
|
21
21
|
} from "./vidply.chunk-SXDZXXZD.js";
|
|
@@ -3397,7 +3397,7 @@ var AudioDescriptionManagerModule = null;
|
|
|
3397
3397
|
var SignLanguageManagerModule = null;
|
|
3398
3398
|
async function loadAudioDescriptionManager() {
|
|
3399
3399
|
if (!AudioDescriptionManagerModule) {
|
|
3400
|
-
const module = await import("./vidply.AudioDescriptionManager-
|
|
3400
|
+
const module = await import("./vidply.AudioDescriptionManager-AXPP5OKO.js");
|
|
3401
3401
|
AudioDescriptionManagerModule = module.AudioDescriptionManager;
|
|
3402
3402
|
}
|
|
3403
3403
|
return AudioDescriptionManagerModule;
|
|
@@ -4560,7 +4560,7 @@ var Player = class _Player extends EventEmitter {
|
|
|
4560
4560
|
const module = await import("./vidply.VimeoRenderer-6CLUVHOQ.js");
|
|
4561
4561
|
return module.VimeoRenderer || module.default;
|
|
4562
4562
|
} else if (src.includes(".m3u8")) {
|
|
4563
|
-
const module = await import("./vidply.HLSRenderer-
|
|
4563
|
+
const module = await import("./vidply.HLSRenderer-IVT5CQQ4.js");
|
|
4564
4564
|
return module.HLSRenderer || module.default;
|
|
4565
4565
|
} else if (src.includes(".mpd")) {
|
|
4566
4566
|
const module = await import("./vidply.DASHRenderer-HP7BXLGV.js");
|
package/dist/legacy/vidply.js
CHANGED
|
@@ -1812,10 +1812,35 @@
|
|
|
1812
1812
|
});
|
|
1813
1813
|
}
|
|
1814
1814
|
}
|
|
1815
|
+
/**
|
|
1816
|
+
* Sync hls.js subtitle rendition to match the given language.
|
|
1817
|
+
* Matches by lang, language, or falls back to name/label.
|
|
1818
|
+
*/
|
|
1819
|
+
_syncHlsSubtitleTrack(targetLang, targetLabel) {
|
|
1820
|
+
var _a;
|
|
1821
|
+
const renderer = this.player.renderer;
|
|
1822
|
+
if (!(renderer == null ? void 0 : renderer.hls) || !((_a = renderer.hls.subtitleTracks) == null ? void 0 : _a.length)) return;
|
|
1823
|
+
const tracks = renderer.hls.subtitleTracks;
|
|
1824
|
+
let hlsIndex = tracks.findIndex(
|
|
1825
|
+
(t) => t.lang === targetLang
|
|
1826
|
+
);
|
|
1827
|
+
if (hlsIndex < 0 && targetLabel) {
|
|
1828
|
+
hlsIndex = tracks.findIndex(
|
|
1829
|
+
(t) => t.name === targetLabel
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
if (hlsIndex >= 0 && renderer.hls.subtitleTrack !== hlsIndex) {
|
|
1833
|
+
renderer.hls.subtitleTrack = hlsIndex;
|
|
1834
|
+
this.player.log(`HLS subtitle track set to index ${hlsIndex} (${targetLang})`, "info");
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1815
1837
|
attachEvents() {
|
|
1816
1838
|
this.player.on("timeupdate", () => {
|
|
1817
1839
|
this.updateCaptions();
|
|
1818
1840
|
});
|
|
1841
|
+
this.player.on("textcuesupdate", () => {
|
|
1842
|
+
this.updateCaptions();
|
|
1843
|
+
});
|
|
1819
1844
|
this.player.on("captionschange", () => {
|
|
1820
1845
|
this.updateStyles();
|
|
1821
1846
|
});
|
|
@@ -1894,6 +1919,7 @@
|
|
|
1894
1919
|
ensureTrackReady();
|
|
1895
1920
|
}
|
|
1896
1921
|
});
|
|
1922
|
+
this._syncHlsSubtitleTrack(selectedTrack.language, selectedTrack.label);
|
|
1897
1923
|
this.player.emit("captionsenabled", selectedTrack);
|
|
1898
1924
|
}
|
|
1899
1925
|
}
|
|
@@ -7706,18 +7732,20 @@
|
|
|
7706
7732
|
this.hls.on(window.Hls.Events.ERROR, (_event, data) => {
|
|
7707
7733
|
this.handleHlsError(data);
|
|
7708
7734
|
});
|
|
7709
|
-
this.hls.on(window.Hls.Events.FRAG_BUFFERED, (_event,
|
|
7735
|
+
this.hls.on(window.Hls.Events.FRAG_BUFFERED, (_event, _data) => {
|
|
7710
7736
|
this.player.state.buffering = false;
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
}, 100);
|
|
7737
|
+
});
|
|
7738
|
+
this.hls.on(window.Hls.Events.SUBTITLE_FRAG_PROCESSED, (_event, data) => {
|
|
7739
|
+
if (!data || !data.success) return;
|
|
7740
|
+
const count = this._getTotalCueCount();
|
|
7741
|
+
if (count > this._lastKnownCueCount) {
|
|
7742
|
+
this._lastKnownCueCount = count;
|
|
7743
|
+
this.player.emit("textcuesupdate");
|
|
7719
7744
|
}
|
|
7720
7745
|
});
|
|
7746
|
+
this.hls.on(window.Hls.Events.CUES_PARSED, (_event, _data) => {
|
|
7747
|
+
this.player.emit("textcuesupdate");
|
|
7748
|
+
});
|
|
7721
7749
|
}
|
|
7722
7750
|
_getTotalCueCount() {
|
|
7723
7751
|
const textTracks = this.media.textTracks;
|