vidply 1.2.0 → 1.2.1

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 (164) hide show
  1. package/README.md +946 -946
  2. package/dist/dev/{vidply.AudioDescriptionManager-R47Q4YR4.js → vidply.AudioDescriptionManager-6RFVF27Z.js} +275 -19
  3. package/dist/dev/vidply.AudioDescriptionManager-6RFVF27Z.js.map +7 -0
  4. package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js → vidply.CaptionStyleMenu-R2SCQ6VP.js} +4 -4
  5. package/dist/dev/{vidply.DASHRenderer-PWEMC3BT.js → vidply.DASHRenderer-EOVEW73H.js} +3 -3
  6. package/dist/dev/vidply.DASHRenderer-EOVEW73H.js.map +7 -0
  7. package/dist/dev/{vidply.FloatingPlayerManager-PKNNNAS2.js → vidply.FloatingPlayerManager-V5XQWS75.js} +5 -5
  8. package/dist/dev/vidply.FloatingPlayerManager-V5XQWS75.js.map +7 -0
  9. package/dist/dev/{vidply.HLSRenderer-E7RQ74HI.js → vidply.HLSRenderer-ZBEFVH5W.js} +4 -4
  10. package/dist/dev/vidply.HLSRenderer-ZBEFVH5W.js.map +7 -0
  11. package/dist/dev/vidply.HTML5Renderer-P7ICKYU4.js +12 -0
  12. package/dist/dev/{vidply.SignLanguageManager-FET5FQZ7.js → vidply.SignLanguageManager-7IAJOVF6.js} +8 -8
  13. package/dist/dev/vidply.SignLanguageManager-7IAJOVF6.js.map +7 -0
  14. package/dist/dev/{vidply.SoundCloudRenderer-RVSJZODO.js → vidply.SoundCloudRenderer-OA7QQYGN.js} +3 -3
  15. package/dist/dev/vidply.SoundCloudRenderer-OA7QQYGN.js.map +7 -0
  16. package/dist/dev/{vidply.TranscriptManager-5GXOQCLX.js → vidply.TranscriptManager-BPM3T23L.js} +10 -10
  17. package/dist/dev/vidply.TranscriptManager-BPM3T23L.js.map +7 -0
  18. package/dist/dev/{vidply.VimeoRenderer-ER2LSBBW.js → vidply.VimeoRenderer-BRHFYPNJ.js} +3 -3
  19. package/dist/dev/vidply.VimeoRenderer-BRHFYPNJ.js.map +7 -0
  20. package/dist/dev/{vidply.YouTubeRenderer-NFTOWJAB.js → vidply.YouTubeRenderer-HRTN4EJ5.js} +3 -3
  21. package/dist/dev/vidply.YouTubeRenderer-HRTN4EJ5.js.map +7 -0
  22. package/dist/dev/{vidply.chunk-6RQA4ROD.js → vidply.chunk-5IK3IC5V.js} +2 -2
  23. package/dist/dev/{vidply.chunk-JJ47NO24.js → vidply.chunk-C55OBT5H.js} +2 -2
  24. package/dist/dev/vidply.chunk-C55OBT5H.js.map +7 -0
  25. package/dist/dev/{vidply.chunk-DZSDO3B7.js → vidply.chunk-HOYEMJSD.js} +2 -2
  26. package/dist/dev/{vidply.chunk-VMSQAZM7.js → vidply.chunk-JAKZNGOR.js} +2 -2
  27. package/dist/dev/{vidply.chunk-64WXMTGL.js → vidply.chunk-LDVFAFGC.js} +5 -5
  28. package/dist/dev/vidply.chunk-LDVFAFGC.js.map +7 -0
  29. package/dist/dev/{vidply.chunk-RZWZGDV7.js → vidply.chunk-NSMAZCMB.js} +6 -6
  30. package/dist/dev/vidply.chunk-NSMAZCMB.js.map +7 -0
  31. package/dist/dev/{vidply.chunk-KI7C3QVL.js → vidply.chunk-RQG3MA7W.js} +2 -2
  32. package/dist/dev/vidply.chunk-RQG3MA7W.js.map +7 -0
  33. package/dist/dev/{vidply.chunk-BNFIUL2Z.js → vidply.chunk-UHMVDBDB.js} +3 -3
  34. package/dist/dev/vidply.chunk-UHMVDBDB.js.map +7 -0
  35. package/dist/dev/{vidply.chunk-NQPURMQY.js → vidply.chunk-W5BR65JY.js} +6 -6
  36. package/dist/dev/vidply.chunk-W5BR65JY.js.map +7 -0
  37. package/dist/dev/{vidply.chunk-W25HUCHS.js → vidply.chunk-XAK3DPTO.js} +2 -2
  38. package/dist/dev/vidply.chunk-XAK3DPTO.js.map +7 -0
  39. package/dist/dev/{vidply.chunk-SK4V2DAU.js → vidply.chunk-YVCYQZJI.js} +3 -3
  40. package/dist/dev/{vidply.de-4YAT7FFM.js → vidply.de-5W5LBFSJ.js} +2 -2
  41. package/dist/dev/vidply.de-5W5LBFSJ.js.map +7 -0
  42. package/dist/dev/{vidply.es-LEWIRUBH.js → vidply.es-SHDAT6L6.js} +2 -2
  43. package/dist/dev/vidply.es-SHDAT6L6.js.map +7 -0
  44. package/dist/dev/vidply.esm.js +73 -34
  45. package/dist/dev/vidply.esm.js.map +2 -2
  46. package/dist/dev/{vidply.fr-Q4GV2NOE.js → vidply.fr-2JANOMNB.js} +2 -2
  47. package/dist/dev/vidply.fr-2JANOMNB.js.map +7 -0
  48. package/dist/dev/{vidply.ja-S6ITA6WU.js → vidply.ja-WWMSTEJH.js} +2 -2
  49. package/dist/dev/vidply.ja-WWMSTEJH.js.map +7 -0
  50. package/dist/legacy/vidply.js +345 -32
  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 +66 -47
  54. package/dist/prod/vidply.AudioDescriptionManager-YTDVDQBO.min.js +6 -0
  55. package/dist/prod/{vidply.CaptionStyleMenu-M3KVQWB5.min.js → vidply.CaptionStyleMenu-2AFF3HF2.min.js} +2 -2
  56. package/dist/prod/{vidply.DASHRenderer-DN3S27AV.min.js → vidply.DASHRenderer-URDOTOKS.min.js} +2 -2
  57. package/dist/prod/{vidply.FloatingPlayerManager-SKAZ4J43.min.js → vidply.FloatingPlayerManager-QUBFK7CQ.min.js} +2 -2
  58. package/dist/prod/{vidply.HLSRenderer-62HH4YTK.min.js → vidply.HLSRenderer-46KNUVAB.min.js} +2 -2
  59. package/dist/prod/vidply.HTML5Renderer-7TQDMIGY.min.js +6 -0
  60. package/dist/prod/{vidply.SignLanguageManager-V3OBTSSF.min.js → vidply.SignLanguageManager-H4C36EVL.min.js} +2 -2
  61. package/dist/prod/{vidply.SoundCloudRenderer-CXRVZIAJ.min.js → vidply.SoundCloudRenderer-OYSWMW7D.min.js} +2 -2
  62. package/dist/prod/{vidply.TranscriptManager-SYM3DFUY.min.js → vidply.TranscriptManager-BA3DAS63.min.js} +2 -2
  63. package/dist/prod/{vidply.VimeoRenderer-72TWRYFQ.min.js → vidply.VimeoRenderer-NF3COTHU.min.js} +2 -2
  64. package/dist/prod/{vidply.YouTubeRenderer-UMHEV3KD.min.js → vidply.YouTubeRenderer-UCQKYIJ4.min.js} +2 -2
  65. package/dist/prod/{vidply.chunk-Z3YDSA7O.min.js → vidply.chunk-2HNCP64N.min.js} +1 -1
  66. package/dist/prod/{vidply.chunk-EKEYJWMG.min.js → vidply.chunk-6ECBIYVI.min.js} +2 -2
  67. package/dist/prod/{vidply.chunk-YEENJS3Q.min.js → vidply.chunk-7S5AFTST.min.js} +2 -2
  68. package/dist/prod/{vidply.chunk-P4UXOHWA.min.js → vidply.chunk-EUOA2DJK.min.js} +1 -1
  69. package/dist/prod/{vidply.chunk-2TB6MUHO.min.js → vidply.chunk-G6KGXNEW.min.js} +1 -1
  70. package/dist/prod/{vidply.chunk-ABPXVDLZ.min.js → vidply.chunk-JPMAK3NS.min.js} +1 -1
  71. package/dist/prod/{vidply.chunk-P3PQBY7N.min.js → vidply.chunk-L4O44K63.min.js} +1 -1
  72. package/dist/prod/{vidply.chunk-WX3MVEAL.min.js → vidply.chunk-M76ABMKB.min.js} +1 -1
  73. package/dist/prod/{vidply.chunk-TM6AXJVD.min.js → vidply.chunk-WRVDVD33.min.js} +2 -2
  74. package/dist/prod/{vidply.chunk-RI3X7WHK.min.js → vidply.chunk-X2FQEQYR.min.js} +2 -2
  75. package/dist/prod/{vidply.chunk-UK7YGOB5.min.js → vidply.chunk-XXUH7ADU.min.js} +2 -2
  76. package/dist/prod/{vidply.de-BCHXRQ53.min.js → vidply.de-JMCATTKZ.min.js} +1 -1
  77. package/dist/prod/{vidply.es-PICAKMZD.min.js → vidply.es-YVG37V7Z.min.js} +1 -1
  78. package/dist/prod/vidply.esm.min.js +2 -2
  79. package/dist/prod/{vidply.fr-ZILI7CDR.min.js → vidply.fr-OGKGN2R4.min.js} +1 -1
  80. package/dist/prod/{vidply.ja-5WQ4OK4J.min.js → vidply.ja-Z6DS7QLS.min.js} +1 -1
  81. package/dist/vidply.css +5796 -5796
  82. package/dist/vidply.esm.min.meta.json +148 -135
  83. package/package.json +7 -7
  84. package/src/controls/CaptionManager.ts +598 -598
  85. package/src/controls/ControlBar.ts +4159 -4159
  86. package/src/controls/KeyboardManager.ts +383 -383
  87. package/src/controls/SettingsDialog.ts +488 -488
  88. package/src/controls/TranscriptManager.ts +2426 -2426
  89. package/src/core/AudioDescriptionManager.ts +863 -774
  90. package/src/core/DescriptionSpeechManager.ts +232 -0
  91. package/src/core/FloatingPlayerManager.ts +674 -674
  92. package/src/core/LazyInit.ts +80 -80
  93. package/src/core/MetadataAlertsManager.ts +514 -514
  94. package/src/core/Player.ts +2915 -2872
  95. package/src/core/PosterManager.ts +169 -169
  96. package/src/core/PseudoFullscreen.ts +179 -179
  97. package/src/core/ResponsiveManager.ts +231 -231
  98. package/src/core/ResumeManager.ts +236 -236
  99. package/src/core/SignLanguageManager.ts +1308 -1308
  100. package/src/core/ThemeManager.ts +157 -157
  101. package/src/features/PlaylistManager.ts +1838 -1827
  102. package/src/i18n/i18n.ts +223 -223
  103. package/src/i18n/languages/de.ts +217 -217
  104. package/src/i18n/languages/en.ts +218 -218
  105. package/src/i18n/languages/es.ts +214 -214
  106. package/src/i18n/languages/fr.ts +214 -214
  107. package/src/i18n/languages/ja.ts +214 -214
  108. package/src/icons/Icons.ts +167 -167
  109. package/src/index.ts +196 -193
  110. package/src/renderers/DASHRenderer.ts +1077 -1077
  111. package/src/renderers/HLSRenderer.ts +944 -944
  112. package/src/renderers/HTML5Renderer.ts +389 -389
  113. package/src/renderers/SoundCloudRenderer.ts +437 -437
  114. package/src/renderers/VimeoRenderer.ts +298 -298
  115. package/src/renderers/YouTubeRenderer.ts +290 -290
  116. package/src/styles/vidply.css +5796 -5796
  117. package/src/types/events.ts +124 -122
  118. package/src/types/globals.d.ts +313 -313
  119. package/src/types/options.ts +185 -182
  120. package/src/types/renderer.ts +58 -58
  121. package/src/utils/DOMUtils.ts +281 -281
  122. package/src/utils/DownloadInfo.ts +171 -171
  123. package/src/utils/DraggablePanel.ts +539 -539
  124. package/src/utils/DraggablePanelMenu.ts +195 -195
  125. package/src/utils/DraggableResizable.ts +997 -997
  126. package/src/utils/EventEmitter.ts +59 -59
  127. package/src/utils/FormUtils.ts +95 -95
  128. package/src/utils/MenuFactory.ts +483 -483
  129. package/src/utils/Sanitize.ts +70 -70
  130. package/src/utils/SettingsMenuFactory.ts +289 -289
  131. package/src/utils/StorageManager.ts +287 -287
  132. package/src/utils/TrackLabelUtils.ts +52 -52
  133. package/src/utils/UrlSafe.ts +69 -69
  134. package/src/utils/VideoFrameCapture.ts +114 -114
  135. package/src/utils/WindowComponents.ts +250 -250
  136. package/dist/dev/vidply.AudioDescriptionManager-R47Q4YR4.js.map +0 -7
  137. package/dist/dev/vidply.DASHRenderer-PWEMC3BT.js.map +0 -7
  138. package/dist/dev/vidply.FloatingPlayerManager-PKNNNAS2.js.map +0 -7
  139. package/dist/dev/vidply.HLSRenderer-E7RQ74HI.js.map +0 -7
  140. package/dist/dev/vidply.HTML5Renderer-BU4QJJJY.js +0 -12
  141. package/dist/dev/vidply.SignLanguageManager-FET5FQZ7.js.map +0 -7
  142. package/dist/dev/vidply.SoundCloudRenderer-RVSJZODO.js.map +0 -7
  143. package/dist/dev/vidply.TranscriptManager-5GXOQCLX.js.map +0 -7
  144. package/dist/dev/vidply.VimeoRenderer-ER2LSBBW.js.map +0 -7
  145. package/dist/dev/vidply.YouTubeRenderer-NFTOWJAB.js.map +0 -7
  146. package/dist/dev/vidply.chunk-64WXMTGL.js.map +0 -7
  147. package/dist/dev/vidply.chunk-BNFIUL2Z.js.map +0 -7
  148. package/dist/dev/vidply.chunk-JJ47NO24.js.map +0 -7
  149. package/dist/dev/vidply.chunk-KI7C3QVL.js.map +0 -7
  150. package/dist/dev/vidply.chunk-NQPURMQY.js.map +0 -7
  151. package/dist/dev/vidply.chunk-RZWZGDV7.js.map +0 -7
  152. package/dist/dev/vidply.chunk-W25HUCHS.js.map +0 -7
  153. package/dist/dev/vidply.de-4YAT7FFM.js.map +0 -7
  154. package/dist/dev/vidply.es-LEWIRUBH.js.map +0 -7
  155. package/dist/dev/vidply.fr-Q4GV2NOE.js.map +0 -7
  156. package/dist/dev/vidply.ja-S6ITA6WU.js.map +0 -7
  157. package/dist/prod/vidply.AudioDescriptionManager-RHLIJW5C.min.js +0 -6
  158. package/dist/prod/vidply.HTML5Renderer-IU5T3WEW.min.js +0 -6
  159. /package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js.map → vidply.CaptionStyleMenu-R2SCQ6VP.js.map} +0 -0
  160. /package/dist/dev/{vidply.HTML5Renderer-BU4QJJJY.js.map → vidply.HTML5Renderer-P7ICKYU4.js.map} +0 -0
  161. /package/dist/dev/{vidply.chunk-6RQA4ROD.js.map → vidply.chunk-5IK3IC5V.js.map} +0 -0
  162. /package/dist/dev/{vidply.chunk-DZSDO3B7.js.map → vidply.chunk-HOYEMJSD.js.map} +0 -0
  163. /package/dist/dev/{vidply.chunk-VMSQAZM7.js.map → vidply.chunk-JAKZNGOR.js.map} +0 -0
  164. /package/dist/dev/{vidply.chunk-SK4V2DAU.js.map → vidply.chunk-YVCYQZJI.js.map} +0 -0
package/README.md CHANGED
@@ -1,946 +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
- ![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
+ - **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