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.
Files changed (151) hide show
  1. package/README.md +939 -939
  2. package/dist/dev/{vidply.AudioDescriptionManager-DRSYPKJ6.js → vidply.AudioDescriptionManager-GJO454ID.js} +5 -5
  3. package/dist/dev/vidply.AudioDescriptionManager-GJO454ID.js.map +7 -0
  4. package/dist/dev/{vidply.DASHRenderer-A6G2BQJ6.js → vidply.DASHRenderer-CAIEEBIY.js} +2 -2
  5. package/dist/dev/vidply.DASHRenderer-CAIEEBIY.js.map +7 -0
  6. package/dist/dev/{vidply.FloatingPlayerManager-JXGJXNFA.js → vidply.FloatingPlayerManager-V4UV3DSB.js} +13 -6
  7. package/dist/dev/vidply.FloatingPlayerManager-V4UV3DSB.js.map +7 -0
  8. package/dist/dev/{vidply.HLSRenderer-Q2JSD7RA.js → vidply.HLSRenderer-KQLJ6EEO.js} +3 -3
  9. package/dist/dev/vidply.HLSRenderer-KQLJ6EEO.js.map +7 -0
  10. package/dist/dev/vidply.HTML5Renderer-TZTNNAQK.js +12 -0
  11. package/dist/dev/{vidply.SignLanguageManager-VS46ZIEP.js → vidply.SignLanguageManager-3YJHYEJS.js} +7 -7
  12. package/dist/dev/vidply.SignLanguageManager-3YJHYEJS.js.map +7 -0
  13. package/dist/dev/{vidply.SoundCloudRenderer-3PTUSVC2.js → vidply.SoundCloudRenderer-SAJ6RQNB.js} +2 -2
  14. package/dist/dev/vidply.SoundCloudRenderer-SAJ6RQNB.js.map +7 -0
  15. package/dist/dev/{vidply.TranscriptManager-JBZE7C5T.js → vidply.TranscriptManager-KRLWUIL5.js} +9 -9
  16. package/dist/dev/vidply.TranscriptManager-KRLWUIL5.js.map +7 -0
  17. package/dist/dev/{vidply.VimeoRenderer-EUAXCCV6.js → vidply.VimeoRenderer-H425TIXI.js} +2 -2
  18. package/dist/dev/{vidply.YouTubeRenderer-SZIXDNNX.js → vidply.YouTubeRenderer-RJFSC7DO.js} +2 -2
  19. package/dist/dev/{vidply.chunk-RTPBWCLO.js → vidply.chunk-4VFODBFZ.js} +3 -3
  20. package/dist/dev/vidply.chunk-4VFODBFZ.js.map +7 -0
  21. package/dist/dev/{vidply.chunk-XC5S5TJQ.js → vidply.chunk-BQ3SUKRY.js} +5 -5
  22. package/dist/dev/vidply.chunk-BQ3SUKRY.js.map +7 -0
  23. package/dist/dev/{vidply.chunk-SE2O3UVV.js → vidply.chunk-DEVK3LVC.js} +14 -3
  24. package/dist/dev/vidply.chunk-DEVK3LVC.js.map +7 -0
  25. package/dist/dev/{vidply.chunk-R6IULD5Y.js → vidply.chunk-I3Y5BLC6.js} +4 -4
  26. package/dist/dev/vidply.chunk-I3Y5BLC6.js.map +7 -0
  27. package/dist/dev/{vidply.chunk-ZJNPHZJJ.js → vidply.chunk-KGJQUMTY.js} +2 -2
  28. package/dist/dev/{vidply.chunk-HXHMFMHC.js → vidply.chunk-PROLQGB6.js} +2 -2
  29. package/dist/dev/vidply.chunk-PROLQGB6.js.map +7 -0
  30. package/dist/dev/{vidply.chunk-C7HDJYKX.js → vidply.chunk-QGWFSJ7R.js} +6 -6
  31. package/dist/dev/vidply.chunk-QGWFSJ7R.js.map +7 -0
  32. package/dist/dev/{vidply.chunk-TTVAXCEY.js → vidply.chunk-ZFY7FNCU.js} +2 -2
  33. package/dist/dev/{vidply.chunk-VUS3KFUI.js → vidply.chunk-ZR5JQPCM.js} +3 -3
  34. package/dist/dev/vidply.chunk-ZR5JQPCM.js.map +7 -0
  35. package/dist/dev/{vidply.de-56C63R72.js → vidply.de-NCS32MR5.js} +2 -2
  36. package/dist/dev/vidply.de-NCS32MR5.js.map +7 -0
  37. package/dist/dev/{vidply.es-LGU3X3UX.js → vidply.es-KTLE6NCH.js} +2 -2
  38. package/dist/dev/vidply.es-KTLE6NCH.js.map +7 -0
  39. package/dist/dev/vidply.esm.js +17 -17
  40. package/dist/dev/vidply.esm.js.map +1 -1
  41. package/dist/dev/{vidply.fr-2JONULHF.js → vidply.fr-7CEEBQ77.js} +2 -2
  42. package/dist/dev/vidply.fr-7CEEBQ77.js.map +7 -0
  43. package/dist/dev/{vidply.ja-MH6HHB6U.js → vidply.ja-5UIYTCX7.js} +2 -2
  44. package/dist/dev/vidply.ja-5UIYTCX7.js.map +7 -0
  45. package/dist/legacy/vidply.js +21 -3
  46. package/dist/legacy/vidply.js.map +2 -2
  47. package/dist/legacy/vidply.min.js +2 -2
  48. package/dist/legacy/vidply.min.meta.json +44 -44
  49. package/dist/prod/{vidply.AudioDescriptionManager-SJKJGDIZ.min.js → vidply.AudioDescriptionManager-BHLFV4AX.min.js} +2 -2
  50. package/dist/prod/{vidply.DASHRenderer-JG6IH7ZH.min.js → vidply.DASHRenderer-EU76KE2D.min.js} +1 -1
  51. package/dist/prod/vidply.FloatingPlayerManager-RRMX5WBA.min.js +6 -0
  52. package/dist/prod/{vidply.HLSRenderer-GPHLTIF3.min.js → vidply.HLSRenderer-2KUQ6CXF.min.js} +2 -2
  53. package/dist/prod/vidply.HTML5Renderer-6MT7K3S3.min.js +6 -0
  54. package/dist/prod/{vidply.SignLanguageManager-6GAWSH6N.min.js → vidply.SignLanguageManager-45XEBRUO.min.js} +2 -2
  55. package/dist/prod/{vidply.SoundCloudRenderer-6THKJJ4P.min.js → vidply.SoundCloudRenderer-UTH6RXEP.min.js} +1 -1
  56. package/dist/prod/{vidply.TranscriptManager-7U6EVG52.min.js → vidply.TranscriptManager-JEWHNZRE.min.js} +2 -2
  57. package/dist/prod/{vidply.VimeoRenderer-KMP7OYF5.min.js → vidply.VimeoRenderer-46FRK7KO.min.js} +1 -1
  58. package/dist/prod/{vidply.YouTubeRenderer-YRFOTEQW.min.js → vidply.YouTubeRenderer-AB7Q7ZX3.min.js} +1 -1
  59. package/dist/prod/{vidply.chunk-I727HSZS.min.js → vidply.chunk-3Y7DZFWE.min.js} +1 -1
  60. package/dist/prod/{vidply.chunk-G2VT3POS.min.js → vidply.chunk-AHEIUUN3.min.js} +1 -1
  61. package/dist/prod/{vidply.chunk-72BLBALO.min.js → vidply.chunk-CIOBWWPJ.min.js} +2 -2
  62. package/dist/prod/vidply.chunk-H7OZCZSW.min.js +6 -0
  63. package/dist/prod/{vidply.chunk-63LS37ZO.min.js → vidply.chunk-J45YW6UC.min.js} +2 -2
  64. package/dist/prod/{vidply.chunk-6FJLQPFL.min.js → vidply.chunk-S7UXDVRO.min.js} +2 -2
  65. package/dist/prod/{vidply.chunk-M5AGRIJK.min.js → vidply.chunk-T3GGXROP.min.js} +2 -2
  66. package/dist/prod/{vidply.chunk-IREBITW2.min.js → vidply.chunk-TKTTOLX5.min.js} +1 -1
  67. package/dist/prod/{vidply.chunk-RYYSYGOB.min.js → vidply.chunk-YXAFRFSQ.min.js} +2 -2
  68. package/dist/prod/{vidply.de-R3S3GYZY.min.js → vidply.de-NVBWZKXD.min.js} +1 -1
  69. package/dist/prod/{vidply.es-DBFW2CCV.min.js → vidply.es-EVA5ETFV.min.js} +1 -1
  70. package/dist/prod/vidply.esm.min.js +2 -2
  71. package/dist/prod/{vidply.fr-MLCUJBE6.min.js → vidply.fr-2BFHYAIB.min.js} +1 -1
  72. package/dist/prod/{vidply.ja-QZ5LFAZD.min.js → vidply.ja-KLEUHUWZ.min.js} +1 -1
  73. package/dist/vidply.css +5892 -5881
  74. package/dist/vidply.esm.min.meta.json +115 -115
  75. package/dist/vidply.min.css +1 -1
  76. package/package.json +8 -8
  77. package/src/controls/CaptionManager.ts +591 -591
  78. package/src/controls/ControlBar.ts +4469 -4469
  79. package/src/controls/KeyboardManager.ts +307 -307
  80. package/src/controls/SettingsDialog.ts +458 -458
  81. package/src/controls/TranscriptManager.ts +2424 -2424
  82. package/src/core/AudioDescriptionManager.ts +772 -772
  83. package/src/core/FloatingPlayerManager.ts +14 -1
  84. package/src/core/LazyInit.ts +80 -80
  85. package/src/core/MetadataAlertsManager.ts +513 -513
  86. package/src/core/Player.ts +2851 -2851
  87. package/src/core/PosterManager.ts +169 -169
  88. package/src/core/PseudoFullscreen.ts +177 -177
  89. package/src/core/ResponsiveManager.ts +231 -231
  90. package/src/core/ResumeManager.ts +236 -236
  91. package/src/core/SignLanguageManager.ts +1306 -1306
  92. package/src/core/ThemeManager.ts +157 -157
  93. package/src/features/PlaylistManager.ts +1812 -1812
  94. package/src/i18n/i18n.ts +223 -223
  95. package/src/i18n/languages/de.ts +217 -217
  96. package/src/i18n/languages/en.ts +218 -218
  97. package/src/i18n/languages/es.ts +214 -214
  98. package/src/i18n/languages/fr.ts +214 -214
  99. package/src/i18n/languages/ja.ts +214 -214
  100. package/src/i18n/translations.ts +28 -28
  101. package/src/index.ts +193 -193
  102. package/src/renderers/DASHRenderer.ts +1068 -1068
  103. package/src/renderers/HLSRenderer.ts +898 -898
  104. package/src/renderers/SoundCloudRenderer.ts +422 -422
  105. package/src/styles/vidply.css +5892 -5881
  106. package/src/types/events.ts +122 -122
  107. package/src/types/index.ts +4 -4
  108. package/src/types/options.ts +182 -182
  109. package/src/types/state.ts +24 -24
  110. package/src/utils/DOMUtils.ts +280 -280
  111. package/src/utils/DownloadInfo.ts +171 -171
  112. package/src/utils/DraggablePanel.ts +539 -539
  113. package/src/utils/DraggablePanelMenu.ts +195 -195
  114. package/src/utils/DraggableResizable.ts +30 -2
  115. package/src/utils/FocusUtils.ts +26 -26
  116. package/src/utils/MenuUtils.ts +172 -172
  117. package/src/utils/PerformanceUtils.ts +40 -40
  118. package/src/utils/ResizeHandles.ts +58 -58
  119. package/src/utils/Sanitize.ts +70 -70
  120. package/src/utils/StorageManager.ts +284 -284
  121. package/src/utils/TimeUtils.ts +67 -67
  122. package/src/utils/TrackLabelUtils.ts +52 -52
  123. package/src/utils/UrlSafe.ts +69 -69
  124. package/src/utils/VideoFrameCapture.ts +114 -114
  125. package/dist/dev/vidply.AudioDescriptionManager-DRSYPKJ6.js.map +0 -7
  126. package/dist/dev/vidply.DASHRenderer-A6G2BQJ6.js.map +0 -7
  127. package/dist/dev/vidply.FloatingPlayerManager-JXGJXNFA.js.map +0 -7
  128. package/dist/dev/vidply.HLSRenderer-Q2JSD7RA.js.map +0 -7
  129. package/dist/dev/vidply.HTML5Renderer-R6HBNQGN.js +0 -12
  130. package/dist/dev/vidply.SignLanguageManager-VS46ZIEP.js.map +0 -7
  131. package/dist/dev/vidply.SoundCloudRenderer-3PTUSVC2.js.map +0 -7
  132. package/dist/dev/vidply.TranscriptManager-JBZE7C5T.js.map +0 -7
  133. package/dist/dev/vidply.chunk-C7HDJYKX.js.map +0 -7
  134. package/dist/dev/vidply.chunk-HXHMFMHC.js.map +0 -7
  135. package/dist/dev/vidply.chunk-R6IULD5Y.js.map +0 -7
  136. package/dist/dev/vidply.chunk-RTPBWCLO.js.map +0 -7
  137. package/dist/dev/vidply.chunk-SE2O3UVV.js.map +0 -7
  138. package/dist/dev/vidply.chunk-VUS3KFUI.js.map +0 -7
  139. package/dist/dev/vidply.chunk-XC5S5TJQ.js.map +0 -7
  140. package/dist/dev/vidply.de-56C63R72.js.map +0 -7
  141. package/dist/dev/vidply.es-LGU3X3UX.js.map +0 -7
  142. package/dist/dev/vidply.fr-2JONULHF.js.map +0 -7
  143. package/dist/dev/vidply.ja-MH6HHB6U.js.map +0 -7
  144. package/dist/prod/vidply.FloatingPlayerManager-MBEVSQCW.min.js +0 -6
  145. package/dist/prod/vidply.HTML5Renderer-MYPIJQEW.min.js +0 -6
  146. package/dist/prod/vidply.chunk-RTY62AYD.min.js +0 -6
  147. /package/dist/dev/{vidply.HTML5Renderer-R6HBNQGN.js.map → vidply.HTML5Renderer-TZTNNAQK.js.map} +0 -0
  148. /package/dist/dev/{vidply.VimeoRenderer-EUAXCCV6.js.map → vidply.VimeoRenderer-H425TIXI.js.map} +0 -0
  149. /package/dist/dev/{vidply.YouTubeRenderer-SZIXDNNX.js.map → vidply.YouTubeRenderer-RJFSC7DO.js.map} +0 -0
  150. /package/dist/dev/{vidply.chunk-ZJNPHZJJ.js.map → vidply.chunk-KGJQUMTY.js.map} +0 -0
  151. /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
- ![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue.svg)
10
- ![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)
11
- ![ESM](https://img.shields.io/badge/ESM-Module-yellow.svg)
12
- ![WCAG](https://img.shields.io/badge/WCAG-2.2%20AA-green.svg)
13
- ![Version](https://img.shields.io/badge/version-1.1.10-brightgreen.svg)
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
+ ![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue.svg)
10
+ ![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)
11
+ ![ESM](https://img.shields.io/badge/ESM-Module-yellow.svg)
12
+ ![WCAG](https://img.shields.io/badge/WCAG-2.2%20AA-green.svg)
13
+ ![Version](https://img.shields.io/badge/version-1.1.10-brightgreen.svg)
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