vidply 1.1.19 → 1.2.0

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