vidply 1.1.8 → 1.1.12

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