vidply 1.2.0 → 1.2.3

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