vidply 1.1.16 → 1.1.18
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 +939 -939
- package/dist/dev/{vidply.AudioDescriptionManager-DRSYPKJ6.js → vidply.AudioDescriptionManager-GJO454ID.js} +5 -5
- package/dist/dev/vidply.AudioDescriptionManager-GJO454ID.js.map +7 -0
- package/dist/dev/{vidply.DASHRenderer-A6G2BQJ6.js → vidply.DASHRenderer-CAIEEBIY.js} +2 -2
- package/dist/dev/vidply.DASHRenderer-CAIEEBIY.js.map +7 -0
- package/dist/dev/{vidply.FloatingPlayerManager-JXGJXNFA.js → vidply.FloatingPlayerManager-V4UV3DSB.js} +13 -6
- package/dist/dev/vidply.FloatingPlayerManager-V4UV3DSB.js.map +7 -0
- package/dist/dev/{vidply.HLSRenderer-Q2JSD7RA.js → vidply.HLSRenderer-KQLJ6EEO.js} +3 -3
- package/dist/dev/vidply.HLSRenderer-KQLJ6EEO.js.map +7 -0
- package/dist/dev/vidply.HTML5Renderer-TZTNNAQK.js +12 -0
- package/dist/dev/{vidply.SignLanguageManager-VS46ZIEP.js → vidply.SignLanguageManager-3YJHYEJS.js} +7 -7
- package/dist/dev/vidply.SignLanguageManager-3YJHYEJS.js.map +7 -0
- package/dist/dev/{vidply.SoundCloudRenderer-3PTUSVC2.js → vidply.SoundCloudRenderer-SAJ6RQNB.js} +2 -2
- package/dist/dev/vidply.SoundCloudRenderer-SAJ6RQNB.js.map +7 -0
- package/dist/dev/{vidply.TranscriptManager-JBZE7C5T.js → vidply.TranscriptManager-KRLWUIL5.js} +9 -9
- package/dist/dev/vidply.TranscriptManager-KRLWUIL5.js.map +7 -0
- package/dist/dev/{vidply.VimeoRenderer-EUAXCCV6.js → vidply.VimeoRenderer-H425TIXI.js} +2 -2
- package/dist/dev/{vidply.YouTubeRenderer-SZIXDNNX.js → vidply.YouTubeRenderer-RJFSC7DO.js} +2 -2
- package/dist/dev/{vidply.chunk-RTPBWCLO.js → vidply.chunk-4VFODBFZ.js} +3 -3
- package/dist/dev/vidply.chunk-4VFODBFZ.js.map +7 -0
- package/dist/dev/{vidply.chunk-XC5S5TJQ.js → vidply.chunk-BQ3SUKRY.js} +5 -5
- package/dist/dev/vidply.chunk-BQ3SUKRY.js.map +7 -0
- package/dist/dev/{vidply.chunk-SE2O3UVV.js → vidply.chunk-DEVK3LVC.js} +14 -3
- package/dist/dev/vidply.chunk-DEVK3LVC.js.map +7 -0
- package/dist/dev/{vidply.chunk-R6IULD5Y.js → vidply.chunk-I3Y5BLC6.js} +4 -4
- package/dist/dev/vidply.chunk-I3Y5BLC6.js.map +7 -0
- package/dist/dev/{vidply.chunk-ZJNPHZJJ.js → vidply.chunk-KGJQUMTY.js} +2 -2
- package/dist/dev/{vidply.chunk-HXHMFMHC.js → vidply.chunk-PROLQGB6.js} +2 -2
- package/dist/dev/vidply.chunk-PROLQGB6.js.map +7 -0
- package/dist/dev/{vidply.chunk-C7HDJYKX.js → vidply.chunk-QGWFSJ7R.js} +6 -6
- package/dist/dev/vidply.chunk-QGWFSJ7R.js.map +7 -0
- package/dist/dev/{vidply.chunk-TTVAXCEY.js → vidply.chunk-ZFY7FNCU.js} +2 -2
- package/dist/dev/{vidply.chunk-VUS3KFUI.js → vidply.chunk-ZR5JQPCM.js} +3 -3
- package/dist/dev/vidply.chunk-ZR5JQPCM.js.map +7 -0
- package/dist/dev/{vidply.de-56C63R72.js → vidply.de-NCS32MR5.js} +2 -2
- package/dist/dev/vidply.de-NCS32MR5.js.map +7 -0
- package/dist/dev/{vidply.es-LGU3X3UX.js → vidply.es-KTLE6NCH.js} +2 -2
- package/dist/dev/vidply.es-KTLE6NCH.js.map +7 -0
- package/dist/dev/vidply.esm.js +17 -17
- package/dist/dev/vidply.esm.js.map +1 -1
- package/dist/dev/{vidply.fr-2JONULHF.js → vidply.fr-7CEEBQ77.js} +2 -2
- package/dist/dev/vidply.fr-7CEEBQ77.js.map +7 -0
- package/dist/dev/{vidply.ja-MH6HHB6U.js → vidply.ja-5UIYTCX7.js} +2 -2
- package/dist/dev/vidply.ja-5UIYTCX7.js.map +7 -0
- package/dist/legacy/vidply.js +21 -3
- package/dist/legacy/vidply.js.map +2 -2
- package/dist/legacy/vidply.min.js +2 -2
- package/dist/legacy/vidply.min.meta.json +44 -44
- package/dist/prod/{vidply.AudioDescriptionManager-SJKJGDIZ.min.js → vidply.AudioDescriptionManager-BHLFV4AX.min.js} +2 -2
- package/dist/prod/{vidply.DASHRenderer-JG6IH7ZH.min.js → vidply.DASHRenderer-EU76KE2D.min.js} +1 -1
- package/dist/prod/vidply.FloatingPlayerManager-RRMX5WBA.min.js +6 -0
- package/dist/prod/{vidply.HLSRenderer-GPHLTIF3.min.js → vidply.HLSRenderer-2KUQ6CXF.min.js} +2 -2
- package/dist/prod/vidply.HTML5Renderer-6MT7K3S3.min.js +6 -0
- package/dist/prod/{vidply.SignLanguageManager-6GAWSH6N.min.js → vidply.SignLanguageManager-45XEBRUO.min.js} +2 -2
- package/dist/prod/{vidply.SoundCloudRenderer-6THKJJ4P.min.js → vidply.SoundCloudRenderer-UTH6RXEP.min.js} +1 -1
- package/dist/prod/{vidply.TranscriptManager-7U6EVG52.min.js → vidply.TranscriptManager-JEWHNZRE.min.js} +2 -2
- package/dist/prod/{vidply.VimeoRenderer-KMP7OYF5.min.js → vidply.VimeoRenderer-46FRK7KO.min.js} +1 -1
- package/dist/prod/{vidply.YouTubeRenderer-YRFOTEQW.min.js → vidply.YouTubeRenderer-AB7Q7ZX3.min.js} +1 -1
- package/dist/prod/{vidply.chunk-I727HSZS.min.js → vidply.chunk-3Y7DZFWE.min.js} +1 -1
- package/dist/prod/{vidply.chunk-G2VT3POS.min.js → vidply.chunk-AHEIUUN3.min.js} +1 -1
- package/dist/prod/{vidply.chunk-72BLBALO.min.js → vidply.chunk-CIOBWWPJ.min.js} +2 -2
- package/dist/prod/vidply.chunk-H7OZCZSW.min.js +6 -0
- package/dist/prod/{vidply.chunk-63LS37ZO.min.js → vidply.chunk-J45YW6UC.min.js} +2 -2
- package/dist/prod/{vidply.chunk-6FJLQPFL.min.js → vidply.chunk-S7UXDVRO.min.js} +2 -2
- package/dist/prod/{vidply.chunk-M5AGRIJK.min.js → vidply.chunk-T3GGXROP.min.js} +2 -2
- package/dist/prod/{vidply.chunk-IREBITW2.min.js → vidply.chunk-TKTTOLX5.min.js} +1 -1
- package/dist/prod/{vidply.chunk-RYYSYGOB.min.js → vidply.chunk-YXAFRFSQ.min.js} +2 -2
- package/dist/prod/{vidply.de-R3S3GYZY.min.js → vidply.de-NVBWZKXD.min.js} +1 -1
- package/dist/prod/{vidply.es-DBFW2CCV.min.js → vidply.es-EVA5ETFV.min.js} +1 -1
- package/dist/prod/vidply.esm.min.js +2 -2
- package/dist/prod/{vidply.fr-MLCUJBE6.min.js → vidply.fr-2BFHYAIB.min.js} +1 -1
- package/dist/prod/{vidply.ja-QZ5LFAZD.min.js → vidply.ja-KLEUHUWZ.min.js} +1 -1
- package/dist/vidply.css +5892 -5881
- package/dist/vidply.esm.min.meta.json +115 -115
- package/dist/vidply.min.css +1 -1
- package/package.json +8 -8
- package/src/controls/CaptionManager.ts +591 -591
- package/src/controls/ControlBar.ts +4469 -4469
- package/src/controls/KeyboardManager.ts +307 -307
- package/src/controls/SettingsDialog.ts +458 -458
- package/src/controls/TranscriptManager.ts +2424 -2424
- package/src/core/AudioDescriptionManager.ts +772 -772
- package/src/core/FloatingPlayerManager.ts +14 -1
- package/src/core/LazyInit.ts +80 -80
- package/src/core/MetadataAlertsManager.ts +513 -513
- package/src/core/Player.ts +2851 -2851
- package/src/core/PosterManager.ts +169 -169
- package/src/core/PseudoFullscreen.ts +177 -177
- package/src/core/ResponsiveManager.ts +231 -231
- package/src/core/ResumeManager.ts +236 -236
- package/src/core/SignLanguageManager.ts +1306 -1306
- package/src/core/ThemeManager.ts +157 -157
- package/src/features/PlaylistManager.ts +1812 -1812
- package/src/i18n/i18n.ts +223 -223
- package/src/i18n/languages/de.ts +217 -217
- package/src/i18n/languages/en.ts +218 -218
- package/src/i18n/languages/es.ts +214 -214
- package/src/i18n/languages/fr.ts +214 -214
- package/src/i18n/languages/ja.ts +214 -214
- package/src/i18n/translations.ts +28 -28
- package/src/index.ts +193 -193
- package/src/renderers/DASHRenderer.ts +1068 -1068
- package/src/renderers/HLSRenderer.ts +898 -898
- package/src/renderers/SoundCloudRenderer.ts +422 -422
- package/src/styles/vidply.css +5892 -5881
- package/src/types/events.ts +122 -122
- package/src/types/index.ts +4 -4
- package/src/types/options.ts +182 -182
- package/src/types/state.ts +24 -24
- package/src/utils/DOMUtils.ts +280 -280
- package/src/utils/DownloadInfo.ts +171 -171
- package/src/utils/DraggablePanel.ts +539 -539
- package/src/utils/DraggablePanelMenu.ts +195 -195
- package/src/utils/DraggableResizable.ts +30 -2
- package/src/utils/FocusUtils.ts +26 -26
- package/src/utils/MenuUtils.ts +172 -172
- package/src/utils/PerformanceUtils.ts +40 -40
- package/src/utils/ResizeHandles.ts +58 -58
- package/src/utils/Sanitize.ts +70 -70
- package/src/utils/StorageManager.ts +284 -284
- package/src/utils/TimeUtils.ts +67 -67
- package/src/utils/TrackLabelUtils.ts +52 -52
- package/src/utils/UrlSafe.ts +69 -69
- package/src/utils/VideoFrameCapture.ts +114 -114
- package/dist/dev/vidply.AudioDescriptionManager-DRSYPKJ6.js.map +0 -7
- package/dist/dev/vidply.DASHRenderer-A6G2BQJ6.js.map +0 -7
- package/dist/dev/vidply.FloatingPlayerManager-JXGJXNFA.js.map +0 -7
- package/dist/dev/vidply.HLSRenderer-Q2JSD7RA.js.map +0 -7
- package/dist/dev/vidply.HTML5Renderer-R6HBNQGN.js +0 -12
- package/dist/dev/vidply.SignLanguageManager-VS46ZIEP.js.map +0 -7
- package/dist/dev/vidply.SoundCloudRenderer-3PTUSVC2.js.map +0 -7
- package/dist/dev/vidply.TranscriptManager-JBZE7C5T.js.map +0 -7
- package/dist/dev/vidply.chunk-C7HDJYKX.js.map +0 -7
- package/dist/dev/vidply.chunk-HXHMFMHC.js.map +0 -7
- package/dist/dev/vidply.chunk-R6IULD5Y.js.map +0 -7
- package/dist/dev/vidply.chunk-RTPBWCLO.js.map +0 -7
- package/dist/dev/vidply.chunk-SE2O3UVV.js.map +0 -7
- package/dist/dev/vidply.chunk-VUS3KFUI.js.map +0 -7
- package/dist/dev/vidply.chunk-XC5S5TJQ.js.map +0 -7
- package/dist/dev/vidply.de-56C63R72.js.map +0 -7
- package/dist/dev/vidply.es-LGU3X3UX.js.map +0 -7
- package/dist/dev/vidply.fr-2JONULHF.js.map +0 -7
- package/dist/dev/vidply.ja-MH6HHB6U.js.map +0 -7
- package/dist/prod/vidply.FloatingPlayerManager-MBEVSQCW.min.js +0 -6
- package/dist/prod/vidply.HTML5Renderer-MYPIJQEW.min.js +0 -6
- package/dist/prod/vidply.chunk-RTY62AYD.min.js +0 -6
- /package/dist/dev/{vidply.HTML5Renderer-R6HBNQGN.js.map → vidply.HTML5Renderer-TZTNNAQK.js.map} +0 -0
- /package/dist/dev/{vidply.VimeoRenderer-EUAXCCV6.js.map → vidply.VimeoRenderer-H425TIXI.js.map} +0 -0
- /package/dist/dev/{vidply.YouTubeRenderer-SZIXDNNX.js.map → vidply.YouTubeRenderer-RJFSC7DO.js.map} +0 -0
- /package/dist/dev/{vidply.chunk-ZJNPHZJJ.js.map → vidply.chunk-KGJQUMTY.js.map} +0 -0
- /package/dist/dev/{vidply.chunk-TTVAXCEY.js.map → vidply.chunk-ZFY7FNCU.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,939 +1,939 @@
|
|
|
1
|
-
# <img src="favicon.svg" width="32" alt="VidPly" /> VidPly
|
|
2
|
-
|
|
3
|
-
**Universal, Accessible Video & Audio Player**
|
|
4
|
-
|
|
5
|
-
A modern, feature-rich media player authored in strict TypeScript and shipped as a zero-dependency ES module. Combines
|
|
6
|
-
the best accessibility features from AblePlayer with the streaming capabilities of MediaElement.js. Fully
|
|
7
|
-
internationalized with support for 5 languages and complete WCAG 2.2 AA compliance.
|
|
8
|
-
|
|
9
|
-

|
|
10
|
-

|
|
11
|
-

|
|
12
|
-

|
|
13
|
-

|
|
14
|
-
|
|
15
|
-
## Live Demos
|
|
16
|
-
|
|
17
|
-
Try VidPly in action:
|
|
18
|
-
|
|
19
|
-
- **[Main Demo](https://matthiaspeltzer.github.io/vidply/demo/demo.html)** - Full-featured video player showcase
|
|
20
|
-
- **[Audio Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-audio.html)** - Audio player with playlist
|
|
21
|
-
support
|
|
22
|
-
- **[Video Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-video.html)** - Video playlist with
|
|
23
|
-
thumbnails
|
|
24
|
-
- **[Mixed Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-mixed.html)** - Combined audio and video
|
|
25
|
-
playlist
|
|
26
|
-
- **[HLS Streaming](https://matthiaspeltzer.github.io/vidply/demo/hls-test.html)** - Adaptive bitrate streaming demo
|
|
27
|
-
- **[DASH Streaming](https://matthiaspeltzer.github.io/vidply/demo/dash-test.html)** - MPEG-DASH streaming demo
|
|
28
|
-
|
|
29
|
-
## Why VidPly?
|
|
30
|
-
|
|
31
|
-
- **Zero Dependencies** - Pure vanilla JavaScript / TypeScript, no frameworks required
|
|
32
|
-
- **TypeScript Native** - Authored in strict TypeScript with shipped type declarations (`dist/types/index.d.ts`)
|
|
33
|
-
- **Accessibility First** - WCAG 2.2 AA compliant with full keyboard and screen reader support
|
|
34
|
-
- **Multilingual** - Built-in translations for 5 languages with easy extensibility
|
|
35
|
-
- **Fully Customizable** - CSS variables and comprehensive API
|
|
36
|
-
- **Modern Build** - ES modules with tree-shaking, code-splitting and source maps
|
|
37
|
-
- **Production Ready** - Thoroughly tested with real-world media content
|
|
38
|
-
|
|
39
|
-
## Features
|
|
40
|
-
|
|
41
|
-
### Core Media Support
|
|
42
|
-
|
|
43
|
-
- **Audio & Video Playback** - Native HTML5 support for both media types
|
|
44
|
-
- **Multiple Formats** - MP3, OGG, WAV (audio) / MP4, WebM (video)
|
|
45
|
-
- **YouTube Integration** - Embed YouTube videos with unified controls
|
|
46
|
-
- **Vimeo Integration** - Seamless Vimeo player integration
|
|
47
|
-
- **SoundCloud Integration** - Play SoundCloud tracks and sets via the Widget API with unified controls
|
|
48
|
-
- **HLS Streaming** - Adaptive bitrate streaming with quality selection and dynamic subtitle detection
|
|
49
|
-
- Uses `hls.js` on Chrome / Firefox / Edge / desktop Safari for full feature parity (quality menu, captions,
|
|
50
|
-
transcript)
|
|
51
|
-
- Falls back to native HLS on iOS / iPadOS where MSE is unavailable; native text tracks are still surfaced through the
|
|
52
|
-
VidPly captions and transcript UI
|
|
53
|
-
- **DASH Streaming** - MPEG-DASH support via dash.js with adaptive quality, TTML and WebVTT subtitles
|
|
54
|
-
- **Buffering Spinner** - Centered loading spinner shown automatically while media is buffering (HTML5, HLS, DASH)
|
|
55
|
-
- **Download Button** - Optional download control with custom URL support (`downloadButton` + `downloadUrl`)
|
|
56
|
-
- **Preview Thumbnails** - Video preview thumbnails on progress bar hover
|
|
57
|
-
- **Playlists** - Full playlist support with auto-advance and navigation
|
|
58
|
-
- Audio playlists with track info
|
|
59
|
-
- Video playlists with thumbnails
|
|
60
|
-
- **Mixed playlists** - Combine audio and video in a single playlist
|
|
61
|
-
- Previous/Next controls
|
|
62
|
-
- Visual playlist panel
|
|
63
|
-
- **Fullscreen Mode**: YouTube-style horizontal carousel
|
|
64
|
-
- Auto-show/hide based on playback state
|
|
65
|
-
- Swipeable touch interface
|
|
66
|
-
- Responsive card layout
|
|
67
|
-
|
|
68
|
-
### Accessibility Features (WCAG 2.2 AA Compliant)
|
|
69
|
-
|
|
70
|
-
- **Full Keyboard Navigation** - All features accessible via keyboard, custom shortcuts, menu navigation with Arrow keys
|
|
71
|
-
- **Screen Reader Support** - Complete ARIA labels (`aria-controls`, `aria-expanded`, `aria-haspopup`), live regions
|
|
72
|
-
- **Interactive Transcripts** - Click-to-seek and auto-scroll with proper semantic HTML
|
|
73
|
-
- **Sign Language Overlay** - Picture-in-picture with drag/resize, keyboard accessible
|
|
74
|
-
- **Audio Description** - Alternate audio track with visual content descriptions
|
|
75
|
-
- **Caption Styling** - Fully customizable (font, size, color, opacity, edge style)
|
|
76
|
-
- **High Contrast Mode** - Windows HCM support, color-independent design
|
|
77
|
-
- **Focus Management** - Logical focus order, programmatic focus handling, visible indicators
|
|
78
|
-
- **Touch Accessibility** - Buttons sized at or above the WCAG 2.2 AA 24×24 CSS-pixel minimum (SC 2.5.8); swipeable
|
|
79
|
-
interfaces
|
|
80
|
-
|
|
81
|
-
### Captions & Subtitles
|
|
82
|
-
|
|
83
|
-
- **WebVTT Support** - Standard caption format
|
|
84
|
-
- **Multiple Languages** - Multi-track support
|
|
85
|
-
- **Caption Selector** - Easy track switching with CC button
|
|
86
|
-
- **Caption Styling** - Dedicated styling dialog (font, size, color, opacity)
|
|
87
|
-
- **Chapter Navigation** - Jump to video chapters
|
|
88
|
-
- **Interactive Transcripts** - Click-to-seek transcript panel (browser Find-in-page works for searching)
|
|
89
|
-
|
|
90
|
-
### Playback Features
|
|
91
|
-
|
|
92
|
-
- **Adjustable Speed** - 0.25x to 2x playback
|
|
93
|
-
- **Seek Controls** - Forward/backward navigation
|
|
94
|
-
- **Volume Control** - 0-100% with mute
|
|
95
|
-
- **Loop Playback** - Single or playlist loop
|
|
96
|
-
- **Fullscreen Mode** - Native fullscreen API with smart playlist overlay
|
|
97
|
-
- **Picture-in-Picture** - Native browser PiP support (toggled via the standard PiP button)
|
|
98
|
-
- **Custom Floating Player (Miniplayer)** - Optional in-page floating window that
|
|
99
|
-
- automatically floats when the original player scrolls out of the viewport and docks back when it scrolls in
|
|
100
|
-
- can be pinned/unpinned manually via the PiP button (manual pin overrides scroll behavior)
|
|
101
|
-
- is fully draggable and resizable, with persistent geometry per player
|
|
102
|
-
- keeps VidPly captions, transport controls and fullscreen working inside the floating shell
|
|
103
|
-
- suppresses native browser PiP automatically while enabled
|
|
104
|
-
- is desktop-only (disabled below 768 px viewport width by default)
|
|
105
|
-
|
|
106
|
-
### Internationalization
|
|
107
|
-
|
|
108
|
-
Built-in support for 5 languages:
|
|
109
|
-
|
|
110
|
-
- English (en)
|
|
111
|
-
- Spanish (es) - Español
|
|
112
|
-
- French (fr) - Français
|
|
113
|
-
- German (de) - Deutsch
|
|
114
|
-
- Japanese (ja) - 日本語
|
|
115
|
-
|
|
116
|
-
All UI elements are fully translated, including:
|
|
117
|
-
|
|
118
|
-
- Control buttons and menus
|
|
119
|
-
- Time display and duration formatting
|
|
120
|
-
- Keyboard shortcuts
|
|
121
|
-
- Error messages and notifications
|
|
122
|
-
- ARIA labels for screen readers
|
|
123
|
-
|
|
124
|
-
**Custom Translations**: Easily add your own languages by loading JSON or YAML translation files via data attributes or
|
|
125
|
-
JavaScript options. The player automatically detects the HTML `lang` attribute and loads matching translations.
|
|
126
|
-
|
|
127
|
-
## Installation
|
|
128
|
-
|
|
129
|
-
### Build from Source
|
|
130
|
-
|
|
131
|
-
First, build the player:
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
# Install dependencies
|
|
135
|
-
npm install
|
|
136
|
-
|
|
137
|
-
# Build production files
|
|
138
|
-
npm run build
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
This creates minified files in the `dist/` folder.
|
|
142
|
-
|
|
143
|
-
### Option 1: Using Built Files (Recommended for Production)
|
|
144
|
-
|
|
145
|
-
```html
|
|
146
|
-
<!DOCTYPE html>
|
|
147
|
-
<html>
|
|
148
|
-
<head>
|
|
149
|
-
<link rel="stylesheet" href="dist/vidply.min.css">
|
|
150
|
-
</head>
|
|
151
|
-
<body>
|
|
152
|
-
<video data-vidply src="video.mp4" width="800" height="450">
|
|
153
|
-
<track kind="subtitles" src="captions.vtt" srclang="en" label="English">
|
|
154
|
-
</video>
|
|
155
|
-
|
|
156
|
-
<script type="module">
|
|
157
|
-
import Player from './dist/prod/vidply.esm.min.js';
|
|
158
|
-
// Auto-initialized via data-vidply attribute
|
|
159
|
-
</script>
|
|
160
|
-
</body>
|
|
161
|
-
</html>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Option 2: Traditional Script Tag (IIFE)
|
|
165
|
-
|
|
166
|
-
```html
|
|
167
|
-
<link rel="stylesheet" href="dist/vidply.min.css">
|
|
168
|
-
<script src="dist/legacy/vidply.min.js"></script>
|
|
169
|
-
|
|
170
|
-
<video id="my-video" src="video.mp4"></video>
|
|
171
|
-
|
|
172
|
-
<script>
|
|
173
|
-
const player = new VidPly.Player('#my-video', {
|
|
174
|
-
controls: true,
|
|
175
|
-
autoplay: false,
|
|
176
|
-
volume: 0.8,
|
|
177
|
-
language: 'en'
|
|
178
|
-
});
|
|
179
|
-
</script>
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Option 3: Development (TypeScript Sources)
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
import Player from './src/index';
|
|
186
|
-
|
|
187
|
-
const player = new Player('#my-video', {
|
|
188
|
-
controls: true,
|
|
189
|
-
autoplay: false,
|
|
190
|
-
volume: 0.8,
|
|
191
|
-
language: 'en'
|
|
192
|
-
});
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
> The library is authored in strict TypeScript. Type declarations ship to `dist/types/index.d.ts` so consumers using
|
|
196
|
-
`tsc` or `vite` get full IntelliSense without any extra `@types` package.
|
|
197
|
-
|
|
198
|
-
## Quick Start
|
|
199
|
-
|
|
200
|
-
### 1. Build the Player
|
|
201
|
-
|
|
202
|
-
```bash
|
|
203
|
-
npm install
|
|
204
|
-
npm run build
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### 2. Add to Your Page
|
|
208
|
-
|
|
209
|
-
```html
|
|
210
|
-
<link rel="stylesheet" href="dist/vidply.min.css">
|
|
211
|
-
<script type="module">
|
|
212
|
-
import Player from './dist/prod/vidply.esm.min.js';
|
|
213
|
-
</script>
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### 3. Create a Video Player
|
|
217
|
-
|
|
218
|
-
```html
|
|
219
|
-
<video data-vidply width="800" height="450">
|
|
220
|
-
<source src="video.mp4" type="video/mp4">
|
|
221
|
-
<track kind="subtitles" src="captions-en.vtt" srclang="en" label="English">
|
|
222
|
-
<track kind="subtitles" src="captions-es.vtt" srclang="es" label="Español">
|
|
223
|
-
</video>
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
That's it! The player auto-initializes.
|
|
227
|
-
|
|
228
|
-
### YouTube Player
|
|
229
|
-
|
|
230
|
-
```html
|
|
231
|
-
<video data-vidply src="https://www.youtube.com/watch?v=VIDEO_ID"></video>
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Vimeo Player
|
|
235
|
-
|
|
236
|
-
```html
|
|
237
|
-
<video data-vidply src="https://vimeo.com/VIDEO_ID"></video>
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
### Audio Player
|
|
241
|
-
|
|
242
|
-
```html
|
|
243
|
-
<audio data-vidply>
|
|
244
|
-
<source src="audio.mp3" type="audio/mpeg">
|
|
245
|
-
</audio>
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### HLS Streaming
|
|
249
|
-
|
|
250
|
-
```html
|
|
251
|
-
<video data-vidply src="https://example.com/stream.m3u8"></video>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### DASH Streaming
|
|
255
|
-
|
|
256
|
-
```html
|
|
257
|
-
<video data-vidply src="https://example.com/manifest.mpd"></video>
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### SoundCloud
|
|
261
|
-
|
|
262
|
-
```html
|
|
263
|
-
<audio data-vidply src="https://soundcloud.com/artist/track"></audio>
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Download Button
|
|
267
|
-
|
|
268
|
-
Enable the download button and (optionally) provide a custom URL:
|
|
269
|
-
|
|
270
|
-
```html
|
|
271
|
-
<video
|
|
272
|
-
data-vidply
|
|
273
|
-
data-vidply-download-button="true"
|
|
274
|
-
data-vidply-download-url="/files/lecture.mp4"
|
|
275
|
-
src="/streams/lecture/manifest.mpd">
|
|
276
|
-
</video>
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
```javascript
|
|
280
|
-
const player = new Player('#my-video', {
|
|
281
|
-
downloadButton: true,
|
|
282
|
-
downloadUrl: '/files/lecture.mp4' // optional, falls back to current src
|
|
283
|
-
});
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
### Custom Floating Player (Miniplayer)
|
|
287
|
-
|
|
288
|
-
Enable the in-page floating player ("own PiP"). When the original video scrolls
|
|
289
|
-
out of the viewport, VidPly pops up a draggable, resizable floating shell in the
|
|
290
|
-
chosen corner; when the original scrolls back in, it docks again. Users can also
|
|
291
|
-
manually pin/unpin the floating player via the PiP button in the control bar.
|
|
292
|
-
The native browser Picture-in-Picture API is automatically suppressed while
|
|
293
|
-
floating is enabled, so users get a single, consistent experience.
|
|
294
|
-
|
|
295
|
-
Enable via the `data-vidply-options` JSON blob:
|
|
296
|
-
|
|
297
|
-
```html
|
|
298
|
-
<video
|
|
299
|
-
data-vidply
|
|
300
|
-
data-vidply-options='{"floating": true, "floatingPosition": "bottom-right", "floatingMinViewportWidth": 768}'
|
|
301
|
-
src="video.mp4"
|
|
302
|
-
width="800" height="450">
|
|
303
|
-
<track kind="subtitles" src="captions.vtt" srclang="en" label="English">
|
|
304
|
-
</video>
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
Or programmatically:
|
|
308
|
-
|
|
309
|
-
```javascript
|
|
310
|
-
const player = new Player('#my-video', {
|
|
311
|
-
floating: true,
|
|
312
|
-
floatingPosition: 'bottom-right', // or 'bottom-left' | 'top-right' | 'top-left'
|
|
313
|
-
floatingMinViewportWidth: 768 // disable feature below this viewport width
|
|
314
|
-
});
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Notes:
|
|
318
|
-
|
|
319
|
-
- Audio-only players (`<audio>`) ignore the floating option.
|
|
320
|
-
- Closing the floating window pauses playback and prevents auto-float again until the next user-initiated `play`.
|
|
321
|
-
- The floating window persists its size/position per player via local storage.
|
|
322
|
-
- Below `floatingMinViewportWidth` (default 768 px) the PiP button is hidden and the floating feature is disabled.
|
|
323
|
-
|
|
324
|
-
### DASH + HLS + MP4 Fallback
|
|
325
|
-
|
|
326
|
-
For maximum device compatibility, provide all three formats:
|
|
327
|
-
|
|
328
|
-
```html
|
|
329
|
-
<video data-vidply width="800" height="450" poster="preview.jpg">
|
|
330
|
-
<source src="video/dash/manifest.mpd" type="application/dash+xml">
|
|
331
|
-
<source src="video/hls/master.m3u8" type="application/x-mpegURL">
|
|
332
|
-
<source src="video/fallback.mp4" type="video/mp4">
|
|
333
|
-
<track kind="subtitles" src="video/vtt/subtitles.de.vtt" srclang="de" label="Deutsch" default>
|
|
334
|
-
<track kind="subtitles" src="video/vtt/subtitles.en.vtt" srclang="en" label="English">
|
|
335
|
-
<track kind="chapters" src="video/vtt/chapters.de.vtt" srclang="de" label="Kapitel">
|
|
336
|
-
</video>
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
VidPly auto-detects the source type by file extension (`.mpd` / `.m3u8` / `.mp4`) and selects the appropriate renderer.
|
|
340
|
-
|
|
341
|
-
## Configuration Options
|
|
342
|
-
|
|
343
|
-
```javascript
|
|
344
|
-
const player = new Player('#video', {
|
|
345
|
-
// Display
|
|
346
|
-
width: 800,
|
|
347
|
-
height: 450,
|
|
348
|
-
poster: 'poster.jpg',
|
|
349
|
-
responsive: true,
|
|
350
|
-
|
|
351
|
-
// Playback
|
|
352
|
-
autoplay: false,
|
|
353
|
-
loop: false,
|
|
354
|
-
muted: false,
|
|
355
|
-
volume: 0.8,
|
|
356
|
-
playbackSpeed: 1.0,
|
|
357
|
-
startTime: 0,
|
|
358
|
-
|
|
359
|
-
// Controls
|
|
360
|
-
controls: true,
|
|
361
|
-
hideControlsDelay: 3000,
|
|
362
|
-
playPauseButton: true,
|
|
363
|
-
progressBar: true,
|
|
364
|
-
currentTime: true,
|
|
365
|
-
duration: true,
|
|
366
|
-
volumeControl: true,
|
|
367
|
-
muteButton: true,
|
|
368
|
-
chaptersButton: true,
|
|
369
|
-
qualityButton: true,
|
|
370
|
-
captionStyleButton: true,
|
|
371
|
-
speedButton: true,
|
|
372
|
-
captionsButton: true,
|
|
373
|
-
transcriptButton: true,
|
|
374
|
-
audioDescriptionButton: true,
|
|
375
|
-
signLanguageButton: true,
|
|
376
|
-
fullscreenButton: true,
|
|
377
|
-
pipButton: false,
|
|
378
|
-
downloadButton: false, // Show a download button in the control bar
|
|
379
|
-
downloadUrl: null, // Optional explicit download URL (falls back to current src)
|
|
380
|
-
downloadFormat: null, // Optional override for the displayed download format (e.g. "MP4")
|
|
381
|
-
downloadFileSize: null, // Optional override for the displayed file size (bytes)
|
|
382
|
-
downloadFetchSize: true, // Issue a HEAD request to detect file size when not provided
|
|
383
|
-
|
|
384
|
-
// Custom Floating Player (in-page miniplayer / "own PiP")
|
|
385
|
-
floating: false, // Enable the custom floating player; also disables native browser PiP
|
|
386
|
-
floatingPosition: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
|
|
387
|
-
floatingMinViewportWidth: 768, // Floating feature is hidden below this viewport width (px)
|
|
388
|
-
|
|
389
|
-
// Seeking
|
|
390
|
-
seekInterval: 10,
|
|
391
|
-
seekIntervalLarge: 30,
|
|
392
|
-
|
|
393
|
-
// Captions
|
|
394
|
-
captions: true,
|
|
395
|
-
captionsDefault: false,
|
|
396
|
-
captionsFontSize: '100%',
|
|
397
|
-
captionsFontFamily: 'sans-serif',
|
|
398
|
-
captionsColor: '#FFFFFF',
|
|
399
|
-
captionsBackgroundColor: '#000000',
|
|
400
|
-
captionsOpacity: 0.8,
|
|
401
|
-
|
|
402
|
-
// Audio Description
|
|
403
|
-
audioDescription: true,
|
|
404
|
-
audioDescriptionSrc: null, // URL to audio-described version
|
|
405
|
-
|
|
406
|
-
// Sign Language
|
|
407
|
-
signLanguage: true,
|
|
408
|
-
signLanguageSrc: null, // URL to sign language video
|
|
409
|
-
signLanguagePosition: 'bottom-right', // 'bottom-right', 'bottom-left', 'top-right', 'top-left'
|
|
410
|
-
signLanguageDisplayMode: 'both', // 'pip' (overlay) | 'main' (source swap) | 'both'
|
|
411
|
-
signLanguageSources: undefined, // { en: '/sl/en.mp4', de: '/sl/de.mp4', ... }
|
|
412
|
-
|
|
413
|
-
// Transcripts
|
|
414
|
-
transcript: false,
|
|
415
|
-
transcriptPosition: 'external',
|
|
416
|
-
transcriptContainer: null,
|
|
417
|
-
|
|
418
|
-
// Keyboard (defaults; do not assign the same key to two actions)
|
|
419
|
-
keyboard: true,
|
|
420
|
-
keyboardShortcuts: {
|
|
421
|
-
'play-pause': [' ', 'p', 'k'],
|
|
422
|
-
'seek-forward': ['ArrowRight'],
|
|
423
|
-
'seek-backward': ['ArrowLeft'],
|
|
424
|
-
'volume-up': ['ArrowUp'],
|
|
425
|
-
'volume-down': ['ArrowDown'],
|
|
426
|
-
'mute': ['m'],
|
|
427
|
-
'fullscreen': ['f'],
|
|
428
|
-
'captions': ['c'],
|
|
429
|
-
'caption-style-menu': ['a'],
|
|
430
|
-
'speed-up': ['>'],
|
|
431
|
-
'speed-down': ['<'],
|
|
432
|
-
'speed-menu': ['s'],
|
|
433
|
-
'quality-menu': ['q'],
|
|
434
|
-
'chapters-menu': ['j'],
|
|
435
|
-
'transcript-toggle': ['t']
|
|
436
|
-
},
|
|
437
|
-
|
|
438
|
-
// Accessibility
|
|
439
|
-
screenReaderAnnouncements: true,
|
|
440
|
-
focusHighlight: true,
|
|
441
|
-
highContrast: false,
|
|
442
|
-
ariaLabels: {}, // Override individual ARIA labels by i18n key
|
|
443
|
-
metadataAlerts: {}, // Map of metadata-cue keys to alert handlers (opt-in)
|
|
444
|
-
metadataHashtags: {}, // Map of metadata-cue hashtags to handler config (opt-in)
|
|
445
|
-
|
|
446
|
-
// Internationalization
|
|
447
|
-
language: 'en',
|
|
448
|
-
languages: ['en'],
|
|
449
|
-
languageFiles: undefined, // { pt: '/i18n/pt.json', it: '/i18n/it.json' }
|
|
450
|
-
languageFile: undefined, // Single language code to load
|
|
451
|
-
languageFileUrl: undefined, // URL for the single language file
|
|
452
|
-
|
|
453
|
-
// Resume Playback
|
|
454
|
-
resumePlayback: true, // Save and offer to resume playback position
|
|
455
|
-
resumeThreshold: 10, // Seconds; do not offer resume if less than this was watched
|
|
456
|
-
resumePrompt: true, // false = silently auto-resume
|
|
457
|
-
|
|
458
|
-
// Thumbnail Preview
|
|
459
|
-
thumbnailPreview: true,
|
|
460
|
-
thumbnailCacheSize: 50,
|
|
461
|
-
thumbnailPregenerate: true,
|
|
462
|
-
thumbnailInterval: 10,
|
|
463
|
-
thumbnailWidth: 160,
|
|
464
|
-
thumbnailHeight: 90,
|
|
465
|
-
thumbnailQuality: 0.8,
|
|
466
|
-
|
|
467
|
-
// Lazy Loading
|
|
468
|
-
lazyInit: true,
|
|
469
|
-
lazyMargin: '200px',
|
|
470
|
-
|
|
471
|
-
// Theming
|
|
472
|
-
theme: 'dark', // 'dark' | 'light' | 'minimal' | 'high-contrast'
|
|
473
|
-
themeVariables: {}, // Custom --vidply-* CSS variable overrides
|
|
474
|
-
|
|
475
|
-
// Callbacks
|
|
476
|
-
onReady: () => console.log('Ready!'),
|
|
477
|
-
onPlay: () => console.log('Playing!'),
|
|
478
|
-
onPause: () => console.log('Paused!'),
|
|
479
|
-
onEnded: () => console.log('Ended!'),
|
|
480
|
-
onTimeUpdate: (t) => {},
|
|
481
|
-
onVolumeChange: (v) => {},
|
|
482
|
-
onError: (err) => console.error(err),
|
|
483
|
-
|
|
484
|
-
// Streaming
|
|
485
|
-
hideSpeedForHls: false, // Hide speed control for ALL HLS streams
|
|
486
|
-
hideSpeedForHlsVideo: false, // Hide speed control only for HLS video (e.g. live streams)
|
|
487
|
-
hideSpeedForDash: false, // Hide speed control for ALL DASH streams
|
|
488
|
-
hideSpeedForDashVideo: false, // Hide speed control only for DASH video
|
|
489
|
-
|
|
490
|
-
// Advanced
|
|
491
|
-
debug: false,
|
|
492
|
-
pauseOthersOnPlay: true,
|
|
493
|
-
classPrefix: 'vidply', // CSS class prefix and event/storage namespace
|
|
494
|
-
iconType: 'svg',
|
|
495
|
-
initialDuration: 0, // Optional duration shown before metadata is loaded
|
|
496
|
-
requirePlaybackForAccessibilityToggles: false, // If true, AD/SL toggles before play show a notice instead of starting playback
|
|
497
|
-
fillContainer: false,
|
|
498
|
-
playsInline: true, // Inline playback on iOS
|
|
499
|
-
|
|
500
|
-
// Performance
|
|
501
|
-
preload: 'metadata', // 'none', 'metadata', or 'auto'
|
|
502
|
-
deferLoad: false // Delay loading until user plays (good for many players)
|
|
503
|
-
});
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
## Keyboard Shortcuts
|
|
507
|
-
|
|
508
|
-
| Key | Action |
|
|
509
|
-
|------------------------------------------------|-----------------------------------------------|
|
|
510
|
-
| <kbd>Space</kbd> / <kbd>P</kbd> / <kbd>K</kbd> | Play/Pause |
|
|
511
|
-
| <kbd>F</kbd> | Toggle Fullscreen |
|
|
512
|
-
| <kbd>M</kbd> | Mute/Unmute |
|
|
513
|
-
| <kbd>↑</kbd> / <kbd>↓</kbd> | Volume Up/Down |
|
|
514
|
-
| <kbd>←</kbd> / <kbd>→</kbd> | Seek -10s / +10s |
|
|
515
|
-
| <kbd>C</kbd> | Toggle Captions (or open menu if multiple) |
|
|
516
|
-
| <kbd>A</kbd> | Open Caption Style Menu |
|
|
517
|
-
| <kbd><</kbd> / <kbd>></kbd> | Decrease/Increase Speed |
|
|
518
|
-
| <kbd>S</kbd> | Open Speed Menu |
|
|
519
|
-
| <kbd>Q</kbd> | Open Quality Menu |
|
|
520
|
-
| <kbd>J</kbd> | Open Chapters Menu |
|
|
521
|
-
| <kbd>T</kbd> | Toggle Transcript |
|
|
522
|
-
| <kbd>D</kbd> | Toggle Drag Mode (Transcript/Sign Language) |
|
|
523
|
-
| <kbd>R</kbd> | Toggle Resize Mode (Transcript/Sign Language) |
|
|
524
|
-
| <kbd>Escape</kbd> | Exit Drag/Resize Mode or Close Menus |
|
|
525
|
-
| <kbd>Home</kbd> | Reset Transcript/Sign Language Position |
|
|
526
|
-
|
|
527
|
-
## API Reference
|
|
528
|
-
|
|
529
|
-
### Playback Control
|
|
530
|
-
|
|
531
|
-
```javascript
|
|
532
|
-
player.play() // Start playback
|
|
533
|
-
player.pause() // Pause playback
|
|
534
|
-
player.stop() // Stop and reset
|
|
535
|
-
player.toggle() // Toggle play/pause
|
|
536
|
-
player.seek(30) // Seek to 30 seconds
|
|
537
|
-
player.seekForward(10) // Skip forward 10 seconds
|
|
538
|
-
player.seekBackward(10) // Skip backward 10 seconds
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
### Volume Control
|
|
542
|
-
|
|
543
|
-
```javascript
|
|
544
|
-
player.setVolume(0.5) // Set volume (0.0-1.0)
|
|
545
|
-
player.getVolume() // Get current volume
|
|
546
|
-
player.mute() // Mute audio
|
|
547
|
-
player.unmute() // Unmute audio
|
|
548
|
-
player.toggleMute() // Toggle mute state
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
**Note on Mobile Devices:** On touch devices (iOS, Android, tablets), only a **mute/unmute button** is shown instead of
|
|
552
|
-
the volume slider. Mobile browsers control HTML5 video volume through **hardware device volume buttons** - this is
|
|
553
|
-
standard behavior that cannot be overridden by web apps for security reasons. The mute button provides quick silencing
|
|
554
|
-
functionality while hardware buttons control actual volume levels.
|
|
555
|
-
|
|
556
|
-
### Playback Speed
|
|
557
|
-
|
|
558
|
-
```javascript
|
|
559
|
-
player.setPlaybackSpeed(1.5) // Set speed (0.25-2.0)
|
|
560
|
-
player.getPlaybackSpeed() // Get current speed
|
|
561
|
-
```
|
|
562
|
-
|
|
563
|
-
### Fullscreen
|
|
564
|
-
|
|
565
|
-
```javascript
|
|
566
|
-
player.enterFullscreen() // Enter fullscreen
|
|
567
|
-
player.exitFullscreen() // Exit fullscreen
|
|
568
|
-
player.toggleFullscreen() // Toggle fullscreen
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
**Note on iOS/Mobile Safari:** Since iOS doesn't support the Fullscreen API on container elements, VidPly automatically
|
|
572
|
-
falls back to a "pseudo-fullscreen" mode that positions the player to fill the entire viewport using CSS. This provides
|
|
573
|
-
a fullscreen-like experience on iOS devices while maintaining all player functionality.
|
|
574
|
-
|
|
575
|
-
### Captions
|
|
576
|
-
|
|
577
|
-
```javascript
|
|
578
|
-
player.enableCaptions() // Enable captions
|
|
579
|
-
player.disableCaptions() // Disable captions
|
|
580
|
-
player.toggleCaptions() // Toggle captions
|
|
581
|
-
|
|
582
|
-
// Switch between caption tracks
|
|
583
|
-
player.captionManager.switchTrack(0) // Switch to first track
|
|
584
|
-
player.captionManager.getAvailableTracks() // Get all tracks
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### Transcript
|
|
588
|
-
|
|
589
|
-
```javascript
|
|
590
|
-
// Show/Hide Transcript
|
|
591
|
-
player.transcriptManager.showTranscript() // Show transcript window
|
|
592
|
-
player.transcriptManager.hideTranscript() // Hide transcript window
|
|
593
|
-
player.transcriptManager.toggleTranscript() // Toggle transcript visibility
|
|
594
|
-
|
|
595
|
-
// Drag & Resize Modes (Desktop only, mobile breakpoint: 768px)
|
|
596
|
-
player.transcriptManager.toggleKeyboardDragMode() // Toggle drag mode (D key)
|
|
597
|
-
player.transcriptManager.toggleResizeMode() // Toggle resize mode (R key)
|
|
598
|
-
|
|
599
|
-
// Settings Menu
|
|
600
|
-
player.transcriptManager.showSettingsMenu() // Show settings dropdown
|
|
601
|
-
player.transcriptManager.hideSettingsMenu() // Hide settings dropdown
|
|
602
|
-
|
|
603
|
-
// Check State
|
|
604
|
-
if (player.transcriptManager.isVisible) {
|
|
605
|
-
console.log('Transcript is visible');
|
|
606
|
-
}
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
### Audio Description
|
|
610
|
-
|
|
611
|
-
```javascript
|
|
612
|
-
player.enableAudioDescription() // Switch to described version
|
|
613
|
-
player.disableAudioDescription() // Switch back to original
|
|
614
|
-
player.toggleAudioDescription() // Toggle audio description
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
### Sign Language
|
|
618
|
-
|
|
619
|
-
```javascript
|
|
620
|
-
// Show/Hide Sign Language Video
|
|
621
|
-
player.enableSignLanguage() // Show sign language overlay
|
|
622
|
-
player.disableSignLanguage() // Hide sign language overlay
|
|
623
|
-
player.toggleSignLanguage() // Toggle sign language
|
|
624
|
-
|
|
625
|
-
// Multi-Language Support
|
|
626
|
-
player.switchSignLanguage('de') // Switch to German sign language
|
|
627
|
-
|
|
628
|
-
// Drag & Resize (available via settings menu or keyboard)
|
|
629
|
-
// D key - Toggle drag mode with arrow keys
|
|
630
|
-
// R key - Toggle resize mode (shows resize handles)
|
|
631
|
-
// Home key - Reset position
|
|
632
|
-
// Escape - Exit drag/resize mode
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
### Playlists
|
|
636
|
-
|
|
637
|
-
```javascript
|
|
638
|
-
import { Player, PlaylistManager } from './dist/prod/vidply.esm.min.js';
|
|
639
|
-
|
|
640
|
-
// Create player
|
|
641
|
-
const player = new Player('#my-player');
|
|
642
|
-
|
|
643
|
-
// Create playlist manager
|
|
644
|
-
const playlist = new PlaylistManager(player, {
|
|
645
|
-
autoAdvance: true, // Auto-play next track
|
|
646
|
-
loop: false, // Loop back to start
|
|
647
|
-
showPanel: true // Show playlist UI
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
// Load tracks
|
|
651
|
-
playlist.loadPlaylist([
|
|
652
|
-
{
|
|
653
|
-
src: 'track1.mp3',
|
|
654
|
-
title: 'Track 1',
|
|
655
|
-
artist: 'Artist Name',
|
|
656
|
-
poster: 'thumb1.jpg'
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
src: 'track2.mp3',
|
|
660
|
-
title: 'Track 2',
|
|
661
|
-
artist: 'Artist Name',
|
|
662
|
-
tracks: [
|
|
663
|
-
{ src: 'captions.vtt', kind: 'captions', srclang: 'en' }
|
|
664
|
-
]
|
|
665
|
-
}
|
|
666
|
-
]);
|
|
667
|
-
|
|
668
|
-
// Control playlist
|
|
669
|
-
playlist.next() // Go to next track
|
|
670
|
-
playlist.previous() // Go to previous track
|
|
671
|
-
playlist.goToTrack(2) // Jump to specific track
|
|
672
|
-
playlist.hasNext() // Check if next track exists
|
|
673
|
-
playlist.hasPrevious() // Check if previous track exists
|
|
674
|
-
|
|
675
|
-
// Listen for track changes
|
|
676
|
-
player.on('playlisttrackchange', (e) => {
|
|
677
|
-
// e: { index: number, item: PlaylistTrack, total: number, previousIndex?: number }
|
|
678
|
-
console.log(`Now playing track ${e.index + 1} / ${e.total}:`, e.item.title);
|
|
679
|
-
});
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
### Settings
|
|
683
|
-
|
|
684
|
-
```javascript
|
|
685
|
-
player.showSettings() // Open settings dialog
|
|
686
|
-
player.hideSettings() // Close settings dialog
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
### State Information
|
|
690
|
-
|
|
691
|
-
```javascript
|
|
692
|
-
player.getCurrentTime() // Get current time
|
|
693
|
-
player.getDuration() // Get duration
|
|
694
|
-
player.isPlaying() // Check if playing
|
|
695
|
-
player.isPaused() // Check if paused
|
|
696
|
-
player.isEnded() // Check if ended
|
|
697
|
-
player.isMuted() // Check if muted
|
|
698
|
-
player.isFullscreen() // Check if fullscreen
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
### Event Listeners
|
|
702
|
-
|
|
703
|
-
```javascript
|
|
704
|
-
player.on('ready', () => {})
|
|
705
|
-
player.on('play', () => {})
|
|
706
|
-
player.on('pause', () => {})
|
|
707
|
-
player.on('ended', () => {})
|
|
708
|
-
player.on('timeupdate', (time) => {})
|
|
709
|
-
player.on('volumechange', (volume) => {})
|
|
710
|
-
player.on('playbackspeedchange', (speed) => {})
|
|
711
|
-
player.on('fullscreenchange', (isFullscreen) => {})
|
|
712
|
-
player.on('hlsmanifestparsed', (data) => {})
|
|
713
|
-
player.on('dashqualitychanged', (data) => {})
|
|
714
|
-
player.on('textcuesupdate', () => {})
|
|
715
|
-
player.on('captionsenabled', (track) => {})
|
|
716
|
-
player.on('captionsdisabled', () => {})
|
|
717
|
-
player.on('error', (error) => {})
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
### Cleanup
|
|
721
|
-
|
|
722
|
-
```javascript
|
|
723
|
-
player.destroy() // Remove player and cleanup
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
## Customization
|
|
727
|
-
|
|
728
|
-
### Custom Styling
|
|
729
|
-
|
|
730
|
-
VidPly provides extensive CSS variables for easy customization:
|
|
731
|
-
|
|
732
|
-
```css
|
|
733
|
-
/* Override default colors and sizing */
|
|
734
|
-
.vidply-player {
|
|
735
|
-
/* Colors */
|
|
736
|
-
--vidply-primary-color: #3b82f6;
|
|
737
|
-
--vidply-background: rgba(0, 0, 0, 0.8);
|
|
738
|
-
--vidply-text-color: #ffffff;
|
|
739
|
-
|
|
740
|
-
/* Sizing */
|
|
741
|
-
--vidply-button-size: 40px;
|
|
742
|
-
--vidply-icon-size: 20px;
|
|
743
|
-
|
|
744
|
-
/* Spacing */
|
|
745
|
-
--vidply-gap-sm: 4px;
|
|
746
|
-
--vidply-gap-md: 8px;
|
|
747
|
-
--vidply-gap-lg: 12px;
|
|
748
|
-
|
|
749
|
-
/* Border radius */
|
|
750
|
-
--vidply-radius-sm: 4px;
|
|
751
|
-
--vidply-radius-md: 8px;
|
|
752
|
-
--vidply-radius-lg: 12px;
|
|
753
|
-
|
|
754
|
-
/* Transitions */
|
|
755
|
-
--vidply-transition-fast: 150ms;
|
|
756
|
-
--vidply-transition-normal: 300ms;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/* Custom progress bar */
|
|
760
|
-
.vidply-progress-played {
|
|
761
|
-
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
/* Custom buttons */
|
|
765
|
-
.vidply-button:hover {
|
|
766
|
-
background: rgba(59, 130, 246, 0.2);
|
|
767
|
-
}
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
### Add Custom Language
|
|
771
|
-
|
|
772
|
-
#### Option 1: Load from URL (Recommended)
|
|
773
|
-
|
|
774
|
-
```html
|
|
775
|
-
<video
|
|
776
|
-
data-vidply
|
|
777
|
-
data-vidply-language-files='{"pt": "languages/pt.json", "it": "languages/it.json"}'
|
|
778
|
-
src="video.mp4"
|
|
779
|
-
></video>
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
#### Option 2: JavaScript API
|
|
783
|
-
|
|
784
|
-
```typescript
|
|
785
|
-
import { i18n } from './src/i18n/i18n';
|
|
786
|
-
|
|
787
|
-
// Load language file from URL
|
|
788
|
-
await i18n.loadLanguageFromUrl('pt', 'languages/pt.json');
|
|
789
|
-
|
|
790
|
-
// Or load multiple languages
|
|
791
|
-
await i18n.loadLanguagesFromUrls({
|
|
792
|
-
'pt': 'languages/pt.json',
|
|
793
|
-
'it': 'languages/it.json'
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
// Set the language
|
|
797
|
-
i18n.setLanguage('pt');
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
#### Option 3: Add Translations Programmatically
|
|
801
|
-
|
|
802
|
-
```typescript
|
|
803
|
-
import { i18n } from './src/i18n/i18n';
|
|
804
|
-
|
|
805
|
-
i18n.addTranslation('pt', {
|
|
806
|
-
player: {
|
|
807
|
-
play: 'Reproduzir',
|
|
808
|
-
pause: 'Pausar',
|
|
809
|
-
mute: 'Silenciar',
|
|
810
|
-
unmute: 'Ativar som'
|
|
811
|
-
}
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
i18n.setLanguage('pt');
|
|
815
|
-
```
|
|
816
|
-
|
|
817
|
-
#### Language File Format
|
|
818
|
-
|
|
819
|
-
Create `languages/pt.json`:
|
|
820
|
-
|
|
821
|
-
```json
|
|
822
|
-
{
|
|
823
|
-
"player": {
|
|
824
|
-
"play": "Reproduzir",
|
|
825
|
-
"pause": "Pausar",
|
|
826
|
-
"mute": "Silenciar",
|
|
827
|
-
"unmute": "Ativar som",
|
|
828
|
-
"fullscreen": "Tela cheia",
|
|
829
|
-
"captions": "Legendas"
|
|
830
|
-
},
|
|
831
|
-
"time": {
|
|
832
|
-
"currentTime": "Tempo atual",
|
|
833
|
-
"duration": "Duração"
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
```
|
|
837
|
-
|
|
838
|
-
The player supports both JSON and YAML formats for language files.
|
|
839
|
-
|
|
840
|
-
## Build Process
|
|
841
|
-
|
|
842
|
-
VidPly uses a modern build system with esbuild for TypeScript bundling, the TypeScript compiler for `.d.ts`
|
|
843
|
-
declarations, and clean-css for CSS.
|
|
844
|
-
|
|
845
|
-
### Available Scripts
|
|
846
|
-
|
|
847
|
-
```bash
|
|
848
|
-
npm run build # Build everything (JS + types + CSS)
|
|
849
|
-
npm run build:js # Bundle TypeScript with esbuild (ESM + IIFE)
|
|
850
|
-
npm run build:types # Emit type declarations to dist/types/
|
|
851
|
-
npm run build:css # Build CSS only
|
|
852
|
-
npm run typecheck # Run tsc --noEmit
|
|
853
|
-
npm run watch # Watch mode for development
|
|
854
|
-
npm run clean # Clean dist directory
|
|
855
|
-
npm run dev # Start dev server
|
|
856
|
-
npm run test # Run unit tests (Vitest)
|
|
857
|
-
npm run test:e2e # Run end-to-end tests (Playwright)
|
|
858
|
-
npm run test:all # Run all tests
|
|
859
|
-
```
|
|
860
|
-
|
|
861
|
-
### Output Files
|
|
862
|
-
|
|
863
|
-
- `dist/dev/vidply.esm.js` - ES Module (development)
|
|
864
|
-
- `dist/prod/vidply.esm.min.js` - ES Module (production)
|
|
865
|
-
- `dist/legacy/vidply.js` - IIFE (development)
|
|
866
|
-
- `dist/legacy/vidply.min.js` - IIFE (production)
|
|
867
|
-
- `dist/types/index.d.ts` - TypeScript declarations
|
|
868
|
-
- `dist/vidply.css` - Styles (unminified)
|
|
869
|
-
- `dist/vidply.min.css` - Styles (minified)
|
|
870
|
-
|
|
871
|
-
See [BUILD.md](docs/BUILD.md) for detailed build documentation.
|
|
872
|
-
|
|
873
|
-
## Browser Support
|
|
874
|
-
|
|
875
|
-
The library ships two bundles. Pick the one that matches your audience:
|
|
876
|
-
|
|
877
|
-
**Modern ESM bundle** (`dist/prod/vidply.esm.min.js`) — recommended.
|
|
878
|
-
|
|
879
|
-
- Chrome 100+
|
|
880
|
-
- Firefox 100+
|
|
881
|
-
- Safari 15+
|
|
882
|
-
- Edge 100+
|
|
883
|
-
- iOS Safari 15+
|
|
884
|
-
- Android Chrome 100+
|
|
885
|
-
|
|
886
|
-
**Legacy IIFE bundle** (`dist/legacy/vidply.min.js`) — for older browser support.
|
|
887
|
-
|
|
888
|
-
- Chrome 80+
|
|
889
|
-
- Firefox 78+
|
|
890
|
-
- Safari 14+
|
|
891
|
-
- Edge 88+
|
|
892
|
-
|
|
893
|
-
The TypeScript declarations target ES2022; both bundles are produced with esbuild + Terser.
|
|
894
|
-
See [BUILD.md](docs/BUILD.md) for the exact targets.
|
|
895
|
-
|
|
896
|
-
## License
|
|
897
|
-
|
|
898
|
-
GNU General Public License v2.0 or later
|
|
899
|
-
|
|
900
|
-
Copyright (C) 2026 Matthias Peltzer
|
|
901
|
-
|
|
902
|
-
This program is free software; you can redistribute it and/or modify
|
|
903
|
-
it under the terms of the GNU General Public License as published by
|
|
904
|
-
the Free Software Foundation; either version 2 of the License, or
|
|
905
|
-
(at your option) any later version.
|
|
906
|
-
|
|
907
|
-
See [LICENSE](LICENSE) for full license text.
|
|
908
|
-
|
|
909
|
-
## Contributing
|
|
910
|
-
|
|
911
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
912
|
-
|
|
913
|
-
## Documentation
|
|
914
|
-
|
|
915
|
-
### Guides
|
|
916
|
-
|
|
917
|
-
- **[User's Guide](docs/Users-Guide.md)** - Complete integration guide for web developers
|
|
918
|
-
- **[Developer Quickstart](docs/Developers-Quickstart.md)** - Quick reference for contributors
|
|
919
|
-
|
|
920
|
-
### Reference
|
|
921
|
-
|
|
922
|
-
- [Getting Started Guide](docs/GETTING_STARTED.md) - Basic setup and usage
|
|
923
|
-
- [Usage Guide](docs/USAGE.md) - Detailed usage examples
|
|
924
|
-
- [Playlist Guide](docs/PLAYLIST.md) - Audio/video playlists with fullscreen support
|
|
925
|
-
- [Transcript Guide](docs/TRANSCRIPT.md) - Interactive transcripts
|
|
926
|
-
- [Keyboard Shortcuts](docs/KEYBOARD.md) - Complete keyboard reference
|
|
927
|
-
- [Build Guide](docs/BUILD.md) - Build system and development
|
|
928
|
-
- [Changelog](docs/CHANGELOG.md) - Version history and updates
|
|
929
|
-
|
|
930
|
-
## Credits
|
|
931
|
-
|
|
932
|
-
Inspired by:
|
|
933
|
-
|
|
934
|
-
- [AblePlayer](https://github.com/ableplayer/ableplayer) - Accessibility features
|
|
935
|
-
- [MediaElement.js](https://github.com/mediaelement/mediaelement) - Streaming support
|
|
936
|
-
|
|
937
|
-
---
|
|
938
|
-
|
|
939
|
-
Made with Vanilla JavaScript by Matthias Peltzer
|
|
1
|
+
# <img src="favicon.svg" width="32" alt="VidPly" /> VidPly
|
|
2
|
+
|
|
3
|
+
**Universal, Accessible Video & Audio Player**
|
|
4
|
+
|
|
5
|
+
A modern, feature-rich media player authored in strict TypeScript and shipped as a zero-dependency ES module. Combines
|
|
6
|
+
the best accessibility features from AblePlayer with the streaming capabilities of MediaElement.js. Fully
|
|
7
|
+
internationalized with support for 5 languages and complete WCAG 2.2 AA compliance.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
## Live Demos
|
|
16
|
+
|
|
17
|
+
Try VidPly in action:
|
|
18
|
+
|
|
19
|
+
- **[Main Demo](https://matthiaspeltzer.github.io/vidply/demo/demo.html)** - Full-featured video player showcase
|
|
20
|
+
- **[Audio Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-audio.html)** - Audio player with playlist
|
|
21
|
+
support
|
|
22
|
+
- **[Video Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-video.html)** - Video playlist with
|
|
23
|
+
thumbnails
|
|
24
|
+
- **[Mixed Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-mixed.html)** - Combined audio and video
|
|
25
|
+
playlist
|
|
26
|
+
- **[HLS Streaming](https://matthiaspeltzer.github.io/vidply/demo/hls-test.html)** - Adaptive bitrate streaming demo
|
|
27
|
+
- **[DASH Streaming](https://matthiaspeltzer.github.io/vidply/demo/dash-test.html)** - MPEG-DASH streaming demo
|
|
28
|
+
|
|
29
|
+
## Why VidPly?
|
|
30
|
+
|
|
31
|
+
- **Zero Dependencies** - Pure vanilla JavaScript / TypeScript, no frameworks required
|
|
32
|
+
- **TypeScript Native** - Authored in strict TypeScript with shipped type declarations (`dist/types/index.d.ts`)
|
|
33
|
+
- **Accessibility First** - WCAG 2.2 AA compliant with full keyboard and screen reader support
|
|
34
|
+
- **Multilingual** - Built-in translations for 5 languages with easy extensibility
|
|
35
|
+
- **Fully Customizable** - CSS variables and comprehensive API
|
|
36
|
+
- **Modern Build** - ES modules with tree-shaking, code-splitting and source maps
|
|
37
|
+
- **Production Ready** - Thoroughly tested with real-world media content
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
### Core Media Support
|
|
42
|
+
|
|
43
|
+
- **Audio & Video Playback** - Native HTML5 support for both media types
|
|
44
|
+
- **Multiple Formats** - MP3, OGG, WAV (audio) / MP4, WebM (video)
|
|
45
|
+
- **YouTube Integration** - Embed YouTube videos with unified controls
|
|
46
|
+
- **Vimeo Integration** - Seamless Vimeo player integration
|
|
47
|
+
- **SoundCloud Integration** - Play SoundCloud tracks and sets via the Widget API with unified controls
|
|
48
|
+
- **HLS Streaming** - Adaptive bitrate streaming with quality selection and dynamic subtitle detection
|
|
49
|
+
- Uses `hls.js` on Chrome / Firefox / Edge / desktop Safari for full feature parity (quality menu, captions,
|
|
50
|
+
transcript)
|
|
51
|
+
- Falls back to native HLS on iOS / iPadOS where MSE is unavailable; native text tracks are still surfaced through the
|
|
52
|
+
VidPly captions and transcript UI
|
|
53
|
+
- **DASH Streaming** - MPEG-DASH support via dash.js with adaptive quality, TTML and WebVTT subtitles
|
|
54
|
+
- **Buffering Spinner** - Centered loading spinner shown automatically while media is buffering (HTML5, HLS, DASH)
|
|
55
|
+
- **Download Button** - Optional download control with custom URL support (`downloadButton` + `downloadUrl`)
|
|
56
|
+
- **Preview Thumbnails** - Video preview thumbnails on progress bar hover
|
|
57
|
+
- **Playlists** - Full playlist support with auto-advance and navigation
|
|
58
|
+
- Audio playlists with track info
|
|
59
|
+
- Video playlists with thumbnails
|
|
60
|
+
- **Mixed playlists** - Combine audio and video in a single playlist
|
|
61
|
+
- Previous/Next controls
|
|
62
|
+
- Visual playlist panel
|
|
63
|
+
- **Fullscreen Mode**: YouTube-style horizontal carousel
|
|
64
|
+
- Auto-show/hide based on playback state
|
|
65
|
+
- Swipeable touch interface
|
|
66
|
+
- Responsive card layout
|
|
67
|
+
|
|
68
|
+
### Accessibility Features (WCAG 2.2 AA Compliant)
|
|
69
|
+
|
|
70
|
+
- **Full Keyboard Navigation** - All features accessible via keyboard, custom shortcuts, menu navigation with Arrow keys
|
|
71
|
+
- **Screen Reader Support** - Complete ARIA labels (`aria-controls`, `aria-expanded`, `aria-haspopup`), live regions
|
|
72
|
+
- **Interactive Transcripts** - Click-to-seek and auto-scroll with proper semantic HTML
|
|
73
|
+
- **Sign Language Overlay** - Picture-in-picture with drag/resize, keyboard accessible
|
|
74
|
+
- **Audio Description** - Alternate audio track with visual content descriptions
|
|
75
|
+
- **Caption Styling** - Fully customizable (font, size, color, opacity, edge style)
|
|
76
|
+
- **High Contrast Mode** - Windows HCM support, color-independent design
|
|
77
|
+
- **Focus Management** - Logical focus order, programmatic focus handling, visible indicators
|
|
78
|
+
- **Touch Accessibility** - Buttons sized at or above the WCAG 2.2 AA 24×24 CSS-pixel minimum (SC 2.5.8); swipeable
|
|
79
|
+
interfaces
|
|
80
|
+
|
|
81
|
+
### Captions & Subtitles
|
|
82
|
+
|
|
83
|
+
- **WebVTT Support** - Standard caption format
|
|
84
|
+
- **Multiple Languages** - Multi-track support
|
|
85
|
+
- **Caption Selector** - Easy track switching with CC button
|
|
86
|
+
- **Caption Styling** - Dedicated styling dialog (font, size, color, opacity)
|
|
87
|
+
- **Chapter Navigation** - Jump to video chapters
|
|
88
|
+
- **Interactive Transcripts** - Click-to-seek transcript panel (browser Find-in-page works for searching)
|
|
89
|
+
|
|
90
|
+
### Playback Features
|
|
91
|
+
|
|
92
|
+
- **Adjustable Speed** - 0.25x to 2x playback
|
|
93
|
+
- **Seek Controls** - Forward/backward navigation
|
|
94
|
+
- **Volume Control** - 0-100% with mute
|
|
95
|
+
- **Loop Playback** - Single or playlist loop
|
|
96
|
+
- **Fullscreen Mode** - Native fullscreen API with smart playlist overlay
|
|
97
|
+
- **Picture-in-Picture** - Native browser PiP support (toggled via the standard PiP button)
|
|
98
|
+
- **Custom Floating Player (Miniplayer)** - Optional in-page floating window that
|
|
99
|
+
- automatically floats when the original player scrolls out of the viewport and docks back when it scrolls in
|
|
100
|
+
- can be pinned/unpinned manually via the PiP button (manual pin overrides scroll behavior)
|
|
101
|
+
- is fully draggable and resizable, with persistent geometry per player
|
|
102
|
+
- keeps VidPly captions, transport controls and fullscreen working inside the floating shell
|
|
103
|
+
- suppresses native browser PiP automatically while enabled
|
|
104
|
+
- is desktop-only (disabled below 768 px viewport width by default)
|
|
105
|
+
|
|
106
|
+
### Internationalization
|
|
107
|
+
|
|
108
|
+
Built-in support for 5 languages:
|
|
109
|
+
|
|
110
|
+
- English (en)
|
|
111
|
+
- Spanish (es) - Español
|
|
112
|
+
- French (fr) - Français
|
|
113
|
+
- German (de) - Deutsch
|
|
114
|
+
- Japanese (ja) - 日本語
|
|
115
|
+
|
|
116
|
+
All UI elements are fully translated, including:
|
|
117
|
+
|
|
118
|
+
- Control buttons and menus
|
|
119
|
+
- Time display and duration formatting
|
|
120
|
+
- Keyboard shortcuts
|
|
121
|
+
- Error messages and notifications
|
|
122
|
+
- ARIA labels for screen readers
|
|
123
|
+
|
|
124
|
+
**Custom Translations**: Easily add your own languages by loading JSON or YAML translation files via data attributes or
|
|
125
|
+
JavaScript options. The player automatically detects the HTML `lang` attribute and loads matching translations.
|
|
126
|
+
|
|
127
|
+
## Installation
|
|
128
|
+
|
|
129
|
+
### Build from Source
|
|
130
|
+
|
|
131
|
+
First, build the player:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Install dependencies
|
|
135
|
+
npm install
|
|
136
|
+
|
|
137
|
+
# Build production files
|
|
138
|
+
npm run build
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
This creates minified files in the `dist/` folder.
|
|
142
|
+
|
|
143
|
+
### Option 1: Using Built Files (Recommended for Production)
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<!DOCTYPE html>
|
|
147
|
+
<html>
|
|
148
|
+
<head>
|
|
149
|
+
<link rel="stylesheet" href="dist/vidply.min.css">
|
|
150
|
+
</head>
|
|
151
|
+
<body>
|
|
152
|
+
<video data-vidply src="video.mp4" width="800" height="450">
|
|
153
|
+
<track kind="subtitles" src="captions.vtt" srclang="en" label="English">
|
|
154
|
+
</video>
|
|
155
|
+
|
|
156
|
+
<script type="module">
|
|
157
|
+
import Player from './dist/prod/vidply.esm.min.js';
|
|
158
|
+
// Auto-initialized via data-vidply attribute
|
|
159
|
+
</script>
|
|
160
|
+
</body>
|
|
161
|
+
</html>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Option 2: Traditional Script Tag (IIFE)
|
|
165
|
+
|
|
166
|
+
```html
|
|
167
|
+
<link rel="stylesheet" href="dist/vidply.min.css">
|
|
168
|
+
<script src="dist/legacy/vidply.min.js"></script>
|
|
169
|
+
|
|
170
|
+
<video id="my-video" src="video.mp4"></video>
|
|
171
|
+
|
|
172
|
+
<script>
|
|
173
|
+
const player = new VidPly.Player('#my-video', {
|
|
174
|
+
controls: true,
|
|
175
|
+
autoplay: false,
|
|
176
|
+
volume: 0.8,
|
|
177
|
+
language: 'en'
|
|
178
|
+
});
|
|
179
|
+
</script>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Option 3: Development (TypeScript Sources)
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import Player from './src/index';
|
|
186
|
+
|
|
187
|
+
const player = new Player('#my-video', {
|
|
188
|
+
controls: true,
|
|
189
|
+
autoplay: false,
|
|
190
|
+
volume: 0.8,
|
|
191
|
+
language: 'en'
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
> The library is authored in strict TypeScript. Type declarations ship to `dist/types/index.d.ts` so consumers using
|
|
196
|
+
`tsc` or `vite` get full IntelliSense without any extra `@types` package.
|
|
197
|
+
|
|
198
|
+
## Quick Start
|
|
199
|
+
|
|
200
|
+
### 1. Build the Player
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npm install
|
|
204
|
+
npm run build
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 2. Add to Your Page
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<link rel="stylesheet" href="dist/vidply.min.css">
|
|
211
|
+
<script type="module">
|
|
212
|
+
import Player from './dist/prod/vidply.esm.min.js';
|
|
213
|
+
</script>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 3. Create a Video Player
|
|
217
|
+
|
|
218
|
+
```html
|
|
219
|
+
<video data-vidply width="800" height="450">
|
|
220
|
+
<source src="video.mp4" type="video/mp4">
|
|
221
|
+
<track kind="subtitles" src="captions-en.vtt" srclang="en" label="English">
|
|
222
|
+
<track kind="subtitles" src="captions-es.vtt" srclang="es" label="Español">
|
|
223
|
+
</video>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
That's it! The player auto-initializes.
|
|
227
|
+
|
|
228
|
+
### YouTube Player
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<video data-vidply src="https://www.youtube.com/watch?v=VIDEO_ID"></video>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Vimeo Player
|
|
235
|
+
|
|
236
|
+
```html
|
|
237
|
+
<video data-vidply src="https://vimeo.com/VIDEO_ID"></video>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Audio Player
|
|
241
|
+
|
|
242
|
+
```html
|
|
243
|
+
<audio data-vidply>
|
|
244
|
+
<source src="audio.mp3" type="audio/mpeg">
|
|
245
|
+
</audio>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### HLS Streaming
|
|
249
|
+
|
|
250
|
+
```html
|
|
251
|
+
<video data-vidply src="https://example.com/stream.m3u8"></video>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### DASH Streaming
|
|
255
|
+
|
|
256
|
+
```html
|
|
257
|
+
<video data-vidply src="https://example.com/manifest.mpd"></video>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### SoundCloud
|
|
261
|
+
|
|
262
|
+
```html
|
|
263
|
+
<audio data-vidply src="https://soundcloud.com/artist/track"></audio>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Download Button
|
|
267
|
+
|
|
268
|
+
Enable the download button and (optionally) provide a custom URL:
|
|
269
|
+
|
|
270
|
+
```html
|
|
271
|
+
<video
|
|
272
|
+
data-vidply
|
|
273
|
+
data-vidply-download-button="true"
|
|
274
|
+
data-vidply-download-url="/files/lecture.mp4"
|
|
275
|
+
src="/streams/lecture/manifest.mpd">
|
|
276
|
+
</video>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
const player = new Player('#my-video', {
|
|
281
|
+
downloadButton: true,
|
|
282
|
+
downloadUrl: '/files/lecture.mp4' // optional, falls back to current src
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Custom Floating Player (Miniplayer)
|
|
287
|
+
|
|
288
|
+
Enable the in-page floating player ("own PiP"). When the original video scrolls
|
|
289
|
+
out of the viewport, VidPly pops up a draggable, resizable floating shell in the
|
|
290
|
+
chosen corner; when the original scrolls back in, it docks again. Users can also
|
|
291
|
+
manually pin/unpin the floating player via the PiP button in the control bar.
|
|
292
|
+
The native browser Picture-in-Picture API is automatically suppressed while
|
|
293
|
+
floating is enabled, so users get a single, consistent experience.
|
|
294
|
+
|
|
295
|
+
Enable via the `data-vidply-options` JSON blob:
|
|
296
|
+
|
|
297
|
+
```html
|
|
298
|
+
<video
|
|
299
|
+
data-vidply
|
|
300
|
+
data-vidply-options='{"floating": true, "floatingPosition": "bottom-right", "floatingMinViewportWidth": 768}'
|
|
301
|
+
src="video.mp4"
|
|
302
|
+
width="800" height="450">
|
|
303
|
+
<track kind="subtitles" src="captions.vtt" srclang="en" label="English">
|
|
304
|
+
</video>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Or programmatically:
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
const player = new Player('#my-video', {
|
|
311
|
+
floating: true,
|
|
312
|
+
floatingPosition: 'bottom-right', // or 'bottom-left' | 'top-right' | 'top-left'
|
|
313
|
+
floatingMinViewportWidth: 768 // disable feature below this viewport width
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Notes:
|
|
318
|
+
|
|
319
|
+
- Audio-only players (`<audio>`) ignore the floating option.
|
|
320
|
+
- Closing the floating window pauses playback and prevents auto-float again until the next user-initiated `play`.
|
|
321
|
+
- The floating window persists its size/position per player via local storage.
|
|
322
|
+
- Below `floatingMinViewportWidth` (default 768 px) the PiP button is hidden and the floating feature is disabled.
|
|
323
|
+
|
|
324
|
+
### DASH + HLS + MP4 Fallback
|
|
325
|
+
|
|
326
|
+
For maximum device compatibility, provide all three formats:
|
|
327
|
+
|
|
328
|
+
```html
|
|
329
|
+
<video data-vidply width="800" height="450" poster="preview.jpg">
|
|
330
|
+
<source src="video/dash/manifest.mpd" type="application/dash+xml">
|
|
331
|
+
<source src="video/hls/master.m3u8" type="application/x-mpegURL">
|
|
332
|
+
<source src="video/fallback.mp4" type="video/mp4">
|
|
333
|
+
<track kind="subtitles" src="video/vtt/subtitles.de.vtt" srclang="de" label="Deutsch" default>
|
|
334
|
+
<track kind="subtitles" src="video/vtt/subtitles.en.vtt" srclang="en" label="English">
|
|
335
|
+
<track kind="chapters" src="video/vtt/chapters.de.vtt" srclang="de" label="Kapitel">
|
|
336
|
+
</video>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
VidPly auto-detects the source type by file extension (`.mpd` / `.m3u8` / `.mp4`) and selects the appropriate renderer.
|
|
340
|
+
|
|
341
|
+
## Configuration Options
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
const player = new Player('#video', {
|
|
345
|
+
// Display
|
|
346
|
+
width: 800,
|
|
347
|
+
height: 450,
|
|
348
|
+
poster: 'poster.jpg',
|
|
349
|
+
responsive: true,
|
|
350
|
+
|
|
351
|
+
// Playback
|
|
352
|
+
autoplay: false,
|
|
353
|
+
loop: false,
|
|
354
|
+
muted: false,
|
|
355
|
+
volume: 0.8,
|
|
356
|
+
playbackSpeed: 1.0,
|
|
357
|
+
startTime: 0,
|
|
358
|
+
|
|
359
|
+
// Controls
|
|
360
|
+
controls: true,
|
|
361
|
+
hideControlsDelay: 3000,
|
|
362
|
+
playPauseButton: true,
|
|
363
|
+
progressBar: true,
|
|
364
|
+
currentTime: true,
|
|
365
|
+
duration: true,
|
|
366
|
+
volumeControl: true,
|
|
367
|
+
muteButton: true,
|
|
368
|
+
chaptersButton: true,
|
|
369
|
+
qualityButton: true,
|
|
370
|
+
captionStyleButton: true,
|
|
371
|
+
speedButton: true,
|
|
372
|
+
captionsButton: true,
|
|
373
|
+
transcriptButton: true,
|
|
374
|
+
audioDescriptionButton: true,
|
|
375
|
+
signLanguageButton: true,
|
|
376
|
+
fullscreenButton: true,
|
|
377
|
+
pipButton: false,
|
|
378
|
+
downloadButton: false, // Show a download button in the control bar
|
|
379
|
+
downloadUrl: null, // Optional explicit download URL (falls back to current src)
|
|
380
|
+
downloadFormat: null, // Optional override for the displayed download format (e.g. "MP4")
|
|
381
|
+
downloadFileSize: null, // Optional override for the displayed file size (bytes)
|
|
382
|
+
downloadFetchSize: true, // Issue a HEAD request to detect file size when not provided
|
|
383
|
+
|
|
384
|
+
// Custom Floating Player (in-page miniplayer / "own PiP")
|
|
385
|
+
floating: false, // Enable the custom floating player; also disables native browser PiP
|
|
386
|
+
floatingPosition: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
|
|
387
|
+
floatingMinViewportWidth: 768, // Floating feature is hidden below this viewport width (px)
|
|
388
|
+
|
|
389
|
+
// Seeking
|
|
390
|
+
seekInterval: 10,
|
|
391
|
+
seekIntervalLarge: 30,
|
|
392
|
+
|
|
393
|
+
// Captions
|
|
394
|
+
captions: true,
|
|
395
|
+
captionsDefault: false,
|
|
396
|
+
captionsFontSize: '100%',
|
|
397
|
+
captionsFontFamily: 'sans-serif',
|
|
398
|
+
captionsColor: '#FFFFFF',
|
|
399
|
+
captionsBackgroundColor: '#000000',
|
|
400
|
+
captionsOpacity: 0.8,
|
|
401
|
+
|
|
402
|
+
// Audio Description
|
|
403
|
+
audioDescription: true,
|
|
404
|
+
audioDescriptionSrc: null, // URL to audio-described version
|
|
405
|
+
|
|
406
|
+
// Sign Language
|
|
407
|
+
signLanguage: true,
|
|
408
|
+
signLanguageSrc: null, // URL to sign language video
|
|
409
|
+
signLanguagePosition: 'bottom-right', // 'bottom-right', 'bottom-left', 'top-right', 'top-left'
|
|
410
|
+
signLanguageDisplayMode: 'both', // 'pip' (overlay) | 'main' (source swap) | 'both'
|
|
411
|
+
signLanguageSources: undefined, // { en: '/sl/en.mp4', de: '/sl/de.mp4', ... }
|
|
412
|
+
|
|
413
|
+
// Transcripts
|
|
414
|
+
transcript: false,
|
|
415
|
+
transcriptPosition: 'external',
|
|
416
|
+
transcriptContainer: null,
|
|
417
|
+
|
|
418
|
+
// Keyboard (defaults; do not assign the same key to two actions)
|
|
419
|
+
keyboard: true,
|
|
420
|
+
keyboardShortcuts: {
|
|
421
|
+
'play-pause': [' ', 'p', 'k'],
|
|
422
|
+
'seek-forward': ['ArrowRight'],
|
|
423
|
+
'seek-backward': ['ArrowLeft'],
|
|
424
|
+
'volume-up': ['ArrowUp'],
|
|
425
|
+
'volume-down': ['ArrowDown'],
|
|
426
|
+
'mute': ['m'],
|
|
427
|
+
'fullscreen': ['f'],
|
|
428
|
+
'captions': ['c'],
|
|
429
|
+
'caption-style-menu': ['a'],
|
|
430
|
+
'speed-up': ['>'],
|
|
431
|
+
'speed-down': ['<'],
|
|
432
|
+
'speed-menu': ['s'],
|
|
433
|
+
'quality-menu': ['q'],
|
|
434
|
+
'chapters-menu': ['j'],
|
|
435
|
+
'transcript-toggle': ['t']
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
// Accessibility
|
|
439
|
+
screenReaderAnnouncements: true,
|
|
440
|
+
focusHighlight: true,
|
|
441
|
+
highContrast: false,
|
|
442
|
+
ariaLabels: {}, // Override individual ARIA labels by i18n key
|
|
443
|
+
metadataAlerts: {}, // Map of metadata-cue keys to alert handlers (opt-in)
|
|
444
|
+
metadataHashtags: {}, // Map of metadata-cue hashtags to handler config (opt-in)
|
|
445
|
+
|
|
446
|
+
// Internationalization
|
|
447
|
+
language: 'en',
|
|
448
|
+
languages: ['en'],
|
|
449
|
+
languageFiles: undefined, // { pt: '/i18n/pt.json', it: '/i18n/it.json' }
|
|
450
|
+
languageFile: undefined, // Single language code to load
|
|
451
|
+
languageFileUrl: undefined, // URL for the single language file
|
|
452
|
+
|
|
453
|
+
// Resume Playback
|
|
454
|
+
resumePlayback: true, // Save and offer to resume playback position
|
|
455
|
+
resumeThreshold: 10, // Seconds; do not offer resume if less than this was watched
|
|
456
|
+
resumePrompt: true, // false = silently auto-resume
|
|
457
|
+
|
|
458
|
+
// Thumbnail Preview
|
|
459
|
+
thumbnailPreview: true,
|
|
460
|
+
thumbnailCacheSize: 50,
|
|
461
|
+
thumbnailPregenerate: true,
|
|
462
|
+
thumbnailInterval: 10,
|
|
463
|
+
thumbnailWidth: 160,
|
|
464
|
+
thumbnailHeight: 90,
|
|
465
|
+
thumbnailQuality: 0.8,
|
|
466
|
+
|
|
467
|
+
// Lazy Loading
|
|
468
|
+
lazyInit: true,
|
|
469
|
+
lazyMargin: '200px',
|
|
470
|
+
|
|
471
|
+
// Theming
|
|
472
|
+
theme: 'dark', // 'dark' | 'light' | 'minimal' | 'high-contrast'
|
|
473
|
+
themeVariables: {}, // Custom --vidply-* CSS variable overrides
|
|
474
|
+
|
|
475
|
+
// Callbacks
|
|
476
|
+
onReady: () => console.log('Ready!'),
|
|
477
|
+
onPlay: () => console.log('Playing!'),
|
|
478
|
+
onPause: () => console.log('Paused!'),
|
|
479
|
+
onEnded: () => console.log('Ended!'),
|
|
480
|
+
onTimeUpdate: (t) => {},
|
|
481
|
+
onVolumeChange: (v) => {},
|
|
482
|
+
onError: (err) => console.error(err),
|
|
483
|
+
|
|
484
|
+
// Streaming
|
|
485
|
+
hideSpeedForHls: false, // Hide speed control for ALL HLS streams
|
|
486
|
+
hideSpeedForHlsVideo: false, // Hide speed control only for HLS video (e.g. live streams)
|
|
487
|
+
hideSpeedForDash: false, // Hide speed control for ALL DASH streams
|
|
488
|
+
hideSpeedForDashVideo: false, // Hide speed control only for DASH video
|
|
489
|
+
|
|
490
|
+
// Advanced
|
|
491
|
+
debug: false,
|
|
492
|
+
pauseOthersOnPlay: true,
|
|
493
|
+
classPrefix: 'vidply', // CSS class prefix and event/storage namespace
|
|
494
|
+
iconType: 'svg',
|
|
495
|
+
initialDuration: 0, // Optional duration shown before metadata is loaded
|
|
496
|
+
requirePlaybackForAccessibilityToggles: false, // If true, AD/SL toggles before play show a notice instead of starting playback
|
|
497
|
+
fillContainer: false,
|
|
498
|
+
playsInline: true, // Inline playback on iOS
|
|
499
|
+
|
|
500
|
+
// Performance
|
|
501
|
+
preload: 'metadata', // 'none', 'metadata', or 'auto'
|
|
502
|
+
deferLoad: false // Delay loading until user plays (good for many players)
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## Keyboard Shortcuts
|
|
507
|
+
|
|
508
|
+
| Key | Action |
|
|
509
|
+
|------------------------------------------------|-----------------------------------------------|
|
|
510
|
+
| <kbd>Space</kbd> / <kbd>P</kbd> / <kbd>K</kbd> | Play/Pause |
|
|
511
|
+
| <kbd>F</kbd> | Toggle Fullscreen |
|
|
512
|
+
| <kbd>M</kbd> | Mute/Unmute |
|
|
513
|
+
| <kbd>↑</kbd> / <kbd>↓</kbd> | Volume Up/Down |
|
|
514
|
+
| <kbd>←</kbd> / <kbd>→</kbd> | Seek -10s / +10s |
|
|
515
|
+
| <kbd>C</kbd> | Toggle Captions (or open menu if multiple) |
|
|
516
|
+
| <kbd>A</kbd> | Open Caption Style Menu |
|
|
517
|
+
| <kbd><</kbd> / <kbd>></kbd> | Decrease/Increase Speed |
|
|
518
|
+
| <kbd>S</kbd> | Open Speed Menu |
|
|
519
|
+
| <kbd>Q</kbd> | Open Quality Menu |
|
|
520
|
+
| <kbd>J</kbd> | Open Chapters Menu |
|
|
521
|
+
| <kbd>T</kbd> | Toggle Transcript |
|
|
522
|
+
| <kbd>D</kbd> | Toggle Drag Mode (Transcript/Sign Language) |
|
|
523
|
+
| <kbd>R</kbd> | Toggle Resize Mode (Transcript/Sign Language) |
|
|
524
|
+
| <kbd>Escape</kbd> | Exit Drag/Resize Mode or Close Menus |
|
|
525
|
+
| <kbd>Home</kbd> | Reset Transcript/Sign Language Position |
|
|
526
|
+
|
|
527
|
+
## API Reference
|
|
528
|
+
|
|
529
|
+
### Playback Control
|
|
530
|
+
|
|
531
|
+
```javascript
|
|
532
|
+
player.play() // Start playback
|
|
533
|
+
player.pause() // Pause playback
|
|
534
|
+
player.stop() // Stop and reset
|
|
535
|
+
player.toggle() // Toggle play/pause
|
|
536
|
+
player.seek(30) // Seek to 30 seconds
|
|
537
|
+
player.seekForward(10) // Skip forward 10 seconds
|
|
538
|
+
player.seekBackward(10) // Skip backward 10 seconds
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Volume Control
|
|
542
|
+
|
|
543
|
+
```javascript
|
|
544
|
+
player.setVolume(0.5) // Set volume (0.0-1.0)
|
|
545
|
+
player.getVolume() // Get current volume
|
|
546
|
+
player.mute() // Mute audio
|
|
547
|
+
player.unmute() // Unmute audio
|
|
548
|
+
player.toggleMute() // Toggle mute state
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**Note on Mobile Devices:** On touch devices (iOS, Android, tablets), only a **mute/unmute button** is shown instead of
|
|
552
|
+
the volume slider. Mobile browsers control HTML5 video volume through **hardware device volume buttons** - this is
|
|
553
|
+
standard behavior that cannot be overridden by web apps for security reasons. The mute button provides quick silencing
|
|
554
|
+
functionality while hardware buttons control actual volume levels.
|
|
555
|
+
|
|
556
|
+
### Playback Speed
|
|
557
|
+
|
|
558
|
+
```javascript
|
|
559
|
+
player.setPlaybackSpeed(1.5) // Set speed (0.25-2.0)
|
|
560
|
+
player.getPlaybackSpeed() // Get current speed
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Fullscreen
|
|
564
|
+
|
|
565
|
+
```javascript
|
|
566
|
+
player.enterFullscreen() // Enter fullscreen
|
|
567
|
+
player.exitFullscreen() // Exit fullscreen
|
|
568
|
+
player.toggleFullscreen() // Toggle fullscreen
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**Note on iOS/Mobile Safari:** Since iOS doesn't support the Fullscreen API on container elements, VidPly automatically
|
|
572
|
+
falls back to a "pseudo-fullscreen" mode that positions the player to fill the entire viewport using CSS. This provides
|
|
573
|
+
a fullscreen-like experience on iOS devices while maintaining all player functionality.
|
|
574
|
+
|
|
575
|
+
### Captions
|
|
576
|
+
|
|
577
|
+
```javascript
|
|
578
|
+
player.enableCaptions() // Enable captions
|
|
579
|
+
player.disableCaptions() // Disable captions
|
|
580
|
+
player.toggleCaptions() // Toggle captions
|
|
581
|
+
|
|
582
|
+
// Switch between caption tracks
|
|
583
|
+
player.captionManager.switchTrack(0) // Switch to first track
|
|
584
|
+
player.captionManager.getAvailableTracks() // Get all tracks
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Transcript
|
|
588
|
+
|
|
589
|
+
```javascript
|
|
590
|
+
// Show/Hide Transcript
|
|
591
|
+
player.transcriptManager.showTranscript() // Show transcript window
|
|
592
|
+
player.transcriptManager.hideTranscript() // Hide transcript window
|
|
593
|
+
player.transcriptManager.toggleTranscript() // Toggle transcript visibility
|
|
594
|
+
|
|
595
|
+
// Drag & Resize Modes (Desktop only, mobile breakpoint: 768px)
|
|
596
|
+
player.transcriptManager.toggleKeyboardDragMode() // Toggle drag mode (D key)
|
|
597
|
+
player.transcriptManager.toggleResizeMode() // Toggle resize mode (R key)
|
|
598
|
+
|
|
599
|
+
// Settings Menu
|
|
600
|
+
player.transcriptManager.showSettingsMenu() // Show settings dropdown
|
|
601
|
+
player.transcriptManager.hideSettingsMenu() // Hide settings dropdown
|
|
602
|
+
|
|
603
|
+
// Check State
|
|
604
|
+
if (player.transcriptManager.isVisible) {
|
|
605
|
+
console.log('Transcript is visible');
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### Audio Description
|
|
610
|
+
|
|
611
|
+
```javascript
|
|
612
|
+
player.enableAudioDescription() // Switch to described version
|
|
613
|
+
player.disableAudioDescription() // Switch back to original
|
|
614
|
+
player.toggleAudioDescription() // Toggle audio description
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### Sign Language
|
|
618
|
+
|
|
619
|
+
```javascript
|
|
620
|
+
// Show/Hide Sign Language Video
|
|
621
|
+
player.enableSignLanguage() // Show sign language overlay
|
|
622
|
+
player.disableSignLanguage() // Hide sign language overlay
|
|
623
|
+
player.toggleSignLanguage() // Toggle sign language
|
|
624
|
+
|
|
625
|
+
// Multi-Language Support
|
|
626
|
+
player.switchSignLanguage('de') // Switch to German sign language
|
|
627
|
+
|
|
628
|
+
// Drag & Resize (available via settings menu or keyboard)
|
|
629
|
+
// D key - Toggle drag mode with arrow keys
|
|
630
|
+
// R key - Toggle resize mode (shows resize handles)
|
|
631
|
+
// Home key - Reset position
|
|
632
|
+
// Escape - Exit drag/resize mode
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Playlists
|
|
636
|
+
|
|
637
|
+
```javascript
|
|
638
|
+
import { Player, PlaylistManager } from './dist/prod/vidply.esm.min.js';
|
|
639
|
+
|
|
640
|
+
// Create player
|
|
641
|
+
const player = new Player('#my-player');
|
|
642
|
+
|
|
643
|
+
// Create playlist manager
|
|
644
|
+
const playlist = new PlaylistManager(player, {
|
|
645
|
+
autoAdvance: true, // Auto-play next track
|
|
646
|
+
loop: false, // Loop back to start
|
|
647
|
+
showPanel: true // Show playlist UI
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Load tracks
|
|
651
|
+
playlist.loadPlaylist([
|
|
652
|
+
{
|
|
653
|
+
src: 'track1.mp3',
|
|
654
|
+
title: 'Track 1',
|
|
655
|
+
artist: 'Artist Name',
|
|
656
|
+
poster: 'thumb1.jpg'
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
src: 'track2.mp3',
|
|
660
|
+
title: 'Track 2',
|
|
661
|
+
artist: 'Artist Name',
|
|
662
|
+
tracks: [
|
|
663
|
+
{ src: 'captions.vtt', kind: 'captions', srclang: 'en' }
|
|
664
|
+
]
|
|
665
|
+
}
|
|
666
|
+
]);
|
|
667
|
+
|
|
668
|
+
// Control playlist
|
|
669
|
+
playlist.next() // Go to next track
|
|
670
|
+
playlist.previous() // Go to previous track
|
|
671
|
+
playlist.goToTrack(2) // Jump to specific track
|
|
672
|
+
playlist.hasNext() // Check if next track exists
|
|
673
|
+
playlist.hasPrevious() // Check if previous track exists
|
|
674
|
+
|
|
675
|
+
// Listen for track changes
|
|
676
|
+
player.on('playlisttrackchange', (e) => {
|
|
677
|
+
// e: { index: number, item: PlaylistTrack, total: number, previousIndex?: number }
|
|
678
|
+
console.log(`Now playing track ${e.index + 1} / ${e.total}:`, e.item.title);
|
|
679
|
+
});
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Settings
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
player.showSettings() // Open settings dialog
|
|
686
|
+
player.hideSettings() // Close settings dialog
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### State Information
|
|
690
|
+
|
|
691
|
+
```javascript
|
|
692
|
+
player.getCurrentTime() // Get current time
|
|
693
|
+
player.getDuration() // Get duration
|
|
694
|
+
player.isPlaying() // Check if playing
|
|
695
|
+
player.isPaused() // Check if paused
|
|
696
|
+
player.isEnded() // Check if ended
|
|
697
|
+
player.isMuted() // Check if muted
|
|
698
|
+
player.isFullscreen() // Check if fullscreen
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
### Event Listeners
|
|
702
|
+
|
|
703
|
+
```javascript
|
|
704
|
+
player.on('ready', () => {})
|
|
705
|
+
player.on('play', () => {})
|
|
706
|
+
player.on('pause', () => {})
|
|
707
|
+
player.on('ended', () => {})
|
|
708
|
+
player.on('timeupdate', (time) => {})
|
|
709
|
+
player.on('volumechange', (volume) => {})
|
|
710
|
+
player.on('playbackspeedchange', (speed) => {})
|
|
711
|
+
player.on('fullscreenchange', (isFullscreen) => {})
|
|
712
|
+
player.on('hlsmanifestparsed', (data) => {})
|
|
713
|
+
player.on('dashqualitychanged', (data) => {})
|
|
714
|
+
player.on('textcuesupdate', () => {})
|
|
715
|
+
player.on('captionsenabled', (track) => {})
|
|
716
|
+
player.on('captionsdisabled', () => {})
|
|
717
|
+
player.on('error', (error) => {})
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Cleanup
|
|
721
|
+
|
|
722
|
+
```javascript
|
|
723
|
+
player.destroy() // Remove player and cleanup
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## Customization
|
|
727
|
+
|
|
728
|
+
### Custom Styling
|
|
729
|
+
|
|
730
|
+
VidPly provides extensive CSS variables for easy customization:
|
|
731
|
+
|
|
732
|
+
```css
|
|
733
|
+
/* Override default colors and sizing */
|
|
734
|
+
.vidply-player {
|
|
735
|
+
/* Colors */
|
|
736
|
+
--vidply-primary-color: #3b82f6;
|
|
737
|
+
--vidply-background: rgba(0, 0, 0, 0.8);
|
|
738
|
+
--vidply-text-color: #ffffff;
|
|
739
|
+
|
|
740
|
+
/* Sizing */
|
|
741
|
+
--vidply-button-size: 40px;
|
|
742
|
+
--vidply-icon-size: 20px;
|
|
743
|
+
|
|
744
|
+
/* Spacing */
|
|
745
|
+
--vidply-gap-sm: 4px;
|
|
746
|
+
--vidply-gap-md: 8px;
|
|
747
|
+
--vidply-gap-lg: 12px;
|
|
748
|
+
|
|
749
|
+
/* Border radius */
|
|
750
|
+
--vidply-radius-sm: 4px;
|
|
751
|
+
--vidply-radius-md: 8px;
|
|
752
|
+
--vidply-radius-lg: 12px;
|
|
753
|
+
|
|
754
|
+
/* Transitions */
|
|
755
|
+
--vidply-transition-fast: 150ms;
|
|
756
|
+
--vidply-transition-normal: 300ms;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/* Custom progress bar */
|
|
760
|
+
.vidply-progress-played {
|
|
761
|
+
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* Custom buttons */
|
|
765
|
+
.vidply-button:hover {
|
|
766
|
+
background: rgba(59, 130, 246, 0.2);
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Add Custom Language
|
|
771
|
+
|
|
772
|
+
#### Option 1: Load from URL (Recommended)
|
|
773
|
+
|
|
774
|
+
```html
|
|
775
|
+
<video
|
|
776
|
+
data-vidply
|
|
777
|
+
data-vidply-language-files='{"pt": "languages/pt.json", "it": "languages/it.json"}'
|
|
778
|
+
src="video.mp4"
|
|
779
|
+
></video>
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
#### Option 2: JavaScript API
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
import { i18n } from './src/i18n/i18n';
|
|
786
|
+
|
|
787
|
+
// Load language file from URL
|
|
788
|
+
await i18n.loadLanguageFromUrl('pt', 'languages/pt.json');
|
|
789
|
+
|
|
790
|
+
// Or load multiple languages
|
|
791
|
+
await i18n.loadLanguagesFromUrls({
|
|
792
|
+
'pt': 'languages/pt.json',
|
|
793
|
+
'it': 'languages/it.json'
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// Set the language
|
|
797
|
+
i18n.setLanguage('pt');
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
#### Option 3: Add Translations Programmatically
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
import { i18n } from './src/i18n/i18n';
|
|
804
|
+
|
|
805
|
+
i18n.addTranslation('pt', {
|
|
806
|
+
player: {
|
|
807
|
+
play: 'Reproduzir',
|
|
808
|
+
pause: 'Pausar',
|
|
809
|
+
mute: 'Silenciar',
|
|
810
|
+
unmute: 'Ativar som'
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
i18n.setLanguage('pt');
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
#### Language File Format
|
|
818
|
+
|
|
819
|
+
Create `languages/pt.json`:
|
|
820
|
+
|
|
821
|
+
```json
|
|
822
|
+
{
|
|
823
|
+
"player": {
|
|
824
|
+
"play": "Reproduzir",
|
|
825
|
+
"pause": "Pausar",
|
|
826
|
+
"mute": "Silenciar",
|
|
827
|
+
"unmute": "Ativar som",
|
|
828
|
+
"fullscreen": "Tela cheia",
|
|
829
|
+
"captions": "Legendas"
|
|
830
|
+
},
|
|
831
|
+
"time": {
|
|
832
|
+
"currentTime": "Tempo atual",
|
|
833
|
+
"duration": "Duração"
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
The player supports both JSON and YAML formats for language files.
|
|
839
|
+
|
|
840
|
+
## Build Process
|
|
841
|
+
|
|
842
|
+
VidPly uses a modern build system with esbuild for TypeScript bundling, the TypeScript compiler for `.d.ts`
|
|
843
|
+
declarations, and clean-css for CSS.
|
|
844
|
+
|
|
845
|
+
### Available Scripts
|
|
846
|
+
|
|
847
|
+
```bash
|
|
848
|
+
npm run build # Build everything (JS + types + CSS)
|
|
849
|
+
npm run build:js # Bundle TypeScript with esbuild (ESM + IIFE)
|
|
850
|
+
npm run build:types # Emit type declarations to dist/types/
|
|
851
|
+
npm run build:css # Build CSS only
|
|
852
|
+
npm run typecheck # Run tsc --noEmit
|
|
853
|
+
npm run watch # Watch mode for development
|
|
854
|
+
npm run clean # Clean dist directory
|
|
855
|
+
npm run dev # Start dev server
|
|
856
|
+
npm run test # Run unit tests (Vitest)
|
|
857
|
+
npm run test:e2e # Run end-to-end tests (Playwright)
|
|
858
|
+
npm run test:all # Run all tests
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
### Output Files
|
|
862
|
+
|
|
863
|
+
- `dist/dev/vidply.esm.js` - ES Module (development)
|
|
864
|
+
- `dist/prod/vidply.esm.min.js` - ES Module (production)
|
|
865
|
+
- `dist/legacy/vidply.js` - IIFE (development)
|
|
866
|
+
- `dist/legacy/vidply.min.js` - IIFE (production)
|
|
867
|
+
- `dist/types/index.d.ts` - TypeScript declarations
|
|
868
|
+
- `dist/vidply.css` - Styles (unminified)
|
|
869
|
+
- `dist/vidply.min.css` - Styles (minified)
|
|
870
|
+
|
|
871
|
+
See [BUILD.md](docs/BUILD.md) for detailed build documentation.
|
|
872
|
+
|
|
873
|
+
## Browser Support
|
|
874
|
+
|
|
875
|
+
The library ships two bundles. Pick the one that matches your audience:
|
|
876
|
+
|
|
877
|
+
**Modern ESM bundle** (`dist/prod/vidply.esm.min.js`) — recommended.
|
|
878
|
+
|
|
879
|
+
- Chrome 100+
|
|
880
|
+
- Firefox 100+
|
|
881
|
+
- Safari 15+
|
|
882
|
+
- Edge 100+
|
|
883
|
+
- iOS Safari 15+
|
|
884
|
+
- Android Chrome 100+
|
|
885
|
+
|
|
886
|
+
**Legacy IIFE bundle** (`dist/legacy/vidply.min.js`) — for older browser support.
|
|
887
|
+
|
|
888
|
+
- Chrome 80+
|
|
889
|
+
- Firefox 78+
|
|
890
|
+
- Safari 14+
|
|
891
|
+
- Edge 88+
|
|
892
|
+
|
|
893
|
+
The TypeScript declarations target ES2022; both bundles are produced with esbuild + Terser.
|
|
894
|
+
See [BUILD.md](docs/BUILD.md) for the exact targets.
|
|
895
|
+
|
|
896
|
+
## License
|
|
897
|
+
|
|
898
|
+
GNU General Public License v2.0 or later
|
|
899
|
+
|
|
900
|
+
Copyright (C) 2026 Matthias Peltzer
|
|
901
|
+
|
|
902
|
+
This program is free software; you can redistribute it and/or modify
|
|
903
|
+
it under the terms of the GNU General Public License as published by
|
|
904
|
+
the Free Software Foundation; either version 2 of the License, or
|
|
905
|
+
(at your option) any later version.
|
|
906
|
+
|
|
907
|
+
See [LICENSE](LICENSE) for full license text.
|
|
908
|
+
|
|
909
|
+
## Contributing
|
|
910
|
+
|
|
911
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
912
|
+
|
|
913
|
+
## Documentation
|
|
914
|
+
|
|
915
|
+
### Guides
|
|
916
|
+
|
|
917
|
+
- **[User's Guide](docs/Users-Guide.md)** - Complete integration guide for web developers
|
|
918
|
+
- **[Developer Quickstart](docs/Developers-Quickstart.md)** - Quick reference for contributors
|
|
919
|
+
|
|
920
|
+
### Reference
|
|
921
|
+
|
|
922
|
+
- [Getting Started Guide](docs/GETTING_STARTED.md) - Basic setup and usage
|
|
923
|
+
- [Usage Guide](docs/USAGE.md) - Detailed usage examples
|
|
924
|
+
- [Playlist Guide](docs/PLAYLIST.md) - Audio/video playlists with fullscreen support
|
|
925
|
+
- [Transcript Guide](docs/TRANSCRIPT.md) - Interactive transcripts
|
|
926
|
+
- [Keyboard Shortcuts](docs/KEYBOARD.md) - Complete keyboard reference
|
|
927
|
+
- [Build Guide](docs/BUILD.md) - Build system and development
|
|
928
|
+
- [Changelog](docs/CHANGELOG.md) - Version history and updates
|
|
929
|
+
|
|
930
|
+
## Credits
|
|
931
|
+
|
|
932
|
+
Inspired by:
|
|
933
|
+
|
|
934
|
+
- [AblePlayer](https://github.com/ableplayer/ableplayer) - Accessibility features
|
|
935
|
+
- [MediaElement.js](https://github.com/mediaelement/mediaelement) - Streaming support
|
|
936
|
+
|
|
937
|
+
---
|
|
938
|
+
|
|
939
|
+
Made with Vanilla JavaScript by Matthias Peltzer
|