vidply 1.2.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +974 -946
  2. package/dist/dev/{vidply.AudioDescriptionManager-R47Q4YR4.js → vidply.AudioDescriptionManager-JYYLK5KB.js} +275 -19
  3. package/dist/dev/vidply.AudioDescriptionManager-JYYLK5KB.js.map +7 -0
  4. package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js → vidply.CaptionStyleMenu-47RCKQF5.js} +4 -4
  5. package/dist/dev/{vidply.DASHRenderer-PWEMC3BT.js → vidply.DASHRenderer-VEJ4HDHO.js} +3 -3
  6. package/dist/dev/vidply.DASHRenderer-VEJ4HDHO.js.map +7 -0
  7. package/dist/dev/{vidply.FloatingPlayerManager-PKNNNAS2.js → vidply.FloatingPlayerManager-C2UVRPZY.js} +5 -5
  8. package/dist/dev/vidply.FloatingPlayerManager-C2UVRPZY.js.map +7 -0
  9. package/dist/dev/{vidply.HLSRenderer-E7RQ74HI.js → vidply.HLSRenderer-ZVBK2TNQ.js} +4 -4
  10. package/dist/dev/vidply.HLSRenderer-ZVBK2TNQ.js.map +7 -0
  11. package/dist/dev/vidply.HTML5Renderer-HLRQUOCU.js +12 -0
  12. package/dist/dev/{vidply.SignLanguageManager-FET5FQZ7.js → vidply.SignLanguageManager-FLUCBW5H.js} +8 -8
  13. package/dist/dev/vidply.SignLanguageManager-FLUCBW5H.js.map +7 -0
  14. package/dist/dev/{vidply.SoundCloudRenderer-RVSJZODO.js → vidply.SoundCloudRenderer-MGMAJ7NK.js} +3 -3
  15. package/dist/dev/vidply.SoundCloudRenderer-MGMAJ7NK.js.map +7 -0
  16. package/dist/dev/{vidply.TranscriptManager-5GXOQCLX.js → vidply.TranscriptManager-Y2RYXHJW.js} +10 -10
  17. package/dist/dev/vidply.TranscriptManager-Y2RYXHJW.js.map +7 -0
  18. package/dist/dev/{vidply.VimeoRenderer-ER2LSBBW.js → vidply.VimeoRenderer-RQFQD7OE.js} +3 -3
  19. package/dist/dev/vidply.VimeoRenderer-RQFQD7OE.js.map +7 -0
  20. package/dist/dev/{vidply.YouTubeRenderer-NFTOWJAB.js → vidply.YouTubeRenderer-EU2BARZF.js} +3 -3
  21. package/dist/dev/vidply.YouTubeRenderer-EU2BARZF.js.map +7 -0
  22. package/dist/dev/{vidply.chunk-BNFIUL2Z.js → vidply.chunk-6LYZYI5G.js} +3 -3
  23. package/dist/dev/vidply.chunk-6LYZYI5G.js.map +7 -0
  24. package/dist/dev/{vidply.chunk-NQPURMQY.js → vidply.chunk-A4ESEOQ2.js} +6 -6
  25. package/dist/dev/vidply.chunk-A4ESEOQ2.js.map +7 -0
  26. package/dist/dev/{vidply.chunk-W25HUCHS.js → vidply.chunk-CGYIED2I.js} +2 -2
  27. package/dist/dev/vidply.chunk-CGYIED2I.js.map +7 -0
  28. package/dist/dev/{vidply.chunk-SK4V2DAU.js → vidply.chunk-HZ7UOKOU.js} +3 -3
  29. package/dist/dev/{vidply.chunk-RZWZGDV7.js → vidply.chunk-I6EDRYGJ.js} +33 -6
  30. package/dist/dev/vidply.chunk-I6EDRYGJ.js.map +7 -0
  31. package/dist/dev/{vidply.chunk-64WXMTGL.js → vidply.chunk-JKMQGLCJ.js} +5 -5
  32. package/dist/dev/vidply.chunk-JKMQGLCJ.js.map +7 -0
  33. package/dist/dev/{vidply.chunk-DZSDO3B7.js → vidply.chunk-JZNZX32Z.js} +2 -2
  34. package/dist/dev/{vidply.chunk-VMSQAZM7.js → vidply.chunk-P25MRJ6O.js} +2 -2
  35. package/dist/dev/{vidply.chunk-6RQA4ROD.js → vidply.chunk-PRFNESYA.js} +2 -2
  36. package/dist/dev/{vidply.chunk-KI7C3QVL.js → vidply.chunk-UI3RYCVF.js} +4 -3
  37. package/dist/dev/vidply.chunk-UI3RYCVF.js.map +7 -0
  38. package/dist/dev/{vidply.chunk-JJ47NO24.js → vidply.chunk-UNFGMABP.js} +2 -2
  39. package/dist/dev/vidply.chunk-UNFGMABP.js.map +7 -0
  40. package/dist/dev/{vidply.de-4YAT7FFM.js → vidply.de-VAUVSQXC.js} +29 -2
  41. package/dist/dev/vidply.de-VAUVSQXC.js.map +7 -0
  42. package/dist/dev/{vidply.es-LEWIRUBH.js → vidply.es-G65HFWO6.js} +29 -2
  43. package/dist/dev/vidply.es-G65HFWO6.js.map +7 -0
  44. package/dist/dev/vidply.esm.js +648 -35
  45. package/dist/dev/vidply.esm.js.map +3 -3
  46. package/dist/dev/{vidply.fr-Q4GV2NOE.js → vidply.fr-MJNOZELM.js} +29 -2
  47. package/dist/dev/vidply.fr-MJNOZELM.js.map +7 -0
  48. package/dist/dev/{vidply.ja-S6ITA6WU.js → vidply.ja-INMDHIYJ.js} +29 -2
  49. package/dist/dev/vidply.ja-INMDHIYJ.js.map +7 -0
  50. package/dist/legacy/vidply.js +1062 -34
  51. package/dist/legacy/vidply.js.map +3 -3
  52. package/dist/legacy/vidply.min.js +2 -2
  53. package/dist/legacy/vidply.min.meta.json +127 -55
  54. package/dist/prod/vidply.AudioDescriptionManager-EKWUNOPE.min.js +6 -0
  55. package/dist/prod/{vidply.CaptionStyleMenu-M3KVQWB5.min.js → vidply.CaptionStyleMenu-JTWSNHRQ.min.js} +2 -2
  56. package/dist/prod/{vidply.DASHRenderer-DN3S27AV.min.js → vidply.DASHRenderer-CIDAHFWM.min.js} +2 -2
  57. package/dist/prod/{vidply.FloatingPlayerManager-SKAZ4J43.min.js → vidply.FloatingPlayerManager-VZXHPCPM.min.js} +2 -2
  58. package/dist/prod/{vidply.HLSRenderer-62HH4YTK.min.js → vidply.HLSRenderer-Q6EFYKID.min.js} +2 -2
  59. package/dist/prod/vidply.HTML5Renderer-XRDJUTOO.min.js +6 -0
  60. package/dist/prod/{vidply.SignLanguageManager-V3OBTSSF.min.js → vidply.SignLanguageManager-JUOSA5AM.min.js} +2 -2
  61. package/dist/prod/{vidply.SoundCloudRenderer-CXRVZIAJ.min.js → vidply.SoundCloudRenderer-7ODD46PO.min.js} +2 -2
  62. package/dist/prod/{vidply.TranscriptManager-SYM3DFUY.min.js → vidply.TranscriptManager-4B2QKYGQ.min.js} +2 -2
  63. package/dist/prod/{vidply.VimeoRenderer-72TWRYFQ.min.js → vidply.VimeoRenderer-MR3UYLY7.min.js} +2 -2
  64. package/dist/prod/{vidply.YouTubeRenderer-UMHEV3KD.min.js → vidply.YouTubeRenderer-ITRTC7NR.min.js} +2 -2
  65. package/dist/prod/{vidply.chunk-YEENJS3Q.min.js → vidply.chunk-3EEDXU4T.min.js} +2 -2
  66. package/dist/prod/{vidply.chunk-P4UXOHWA.min.js → vidply.chunk-5HTX5QXW.min.js} +1 -1
  67. package/dist/prod/{vidply.chunk-ABPXVDLZ.min.js → vidply.chunk-6OGHHIYP.min.js} +2 -2
  68. package/dist/prod/{vidply.chunk-UK7YGOB5.min.js → vidply.chunk-GUPGMFUZ.min.js} +2 -2
  69. package/dist/prod/{vidply.chunk-WX3MVEAL.min.js → vidply.chunk-MWEBITCP.min.js} +1 -1
  70. package/dist/prod/{vidply.chunk-2TB6MUHO.min.js → vidply.chunk-PL3P3JHR.min.js} +1 -1
  71. package/dist/prod/{vidply.chunk-EKEYJWMG.min.js → vidply.chunk-QQZGV7TD.min.js} +2 -2
  72. package/dist/prod/{vidply.chunk-Z3YDSA7O.min.js → vidply.chunk-U5IRRYG5.min.js} +1 -1
  73. package/dist/prod/{vidply.chunk-TM6AXJVD.min.js → vidply.chunk-VKHV4QHA.min.js} +2 -2
  74. package/dist/prod/{vidply.chunk-P3PQBY7N.min.js → vidply.chunk-XA4M574A.min.js} +1 -1
  75. package/dist/prod/{vidply.chunk-RI3X7WHK.min.js → vidply.chunk-ZPTV4JEP.min.js} +2 -2
  76. package/dist/prod/{vidply.de-BCHXRQ53.min.js → vidply.de-UZHFCEZU.min.js} +2 -2
  77. package/dist/prod/{vidply.es-PICAKMZD.min.js → vidply.es-BOISUV2S.min.js} +2 -2
  78. package/dist/prod/vidply.esm.min.js +2 -2
  79. package/dist/prod/{vidply.fr-ZILI7CDR.min.js → vidply.fr-ICSMKPFM.min.js} +2 -2
  80. package/dist/prod/{vidply.ja-5WQ4OK4J.min.js → vidply.ja-VQ2HKGAY.min.js} +2 -2
  81. package/dist/vidply.css +5852 -5796
  82. package/dist/vidply.esm.min.meta.json +204 -149
  83. package/dist/vidply.min.css +1 -1
  84. package/package.json +11 -11
  85. package/src/controls/CaptionManager.ts +598 -598
  86. package/src/controls/ControlBar.ts +4191 -4159
  87. package/src/controls/KeyboardHelp.ts +326 -0
  88. package/src/controls/KeyboardManager.ts +387 -383
  89. package/src/controls/SettingsDialog.ts +488 -488
  90. package/src/controls/TranscriptManager.ts +2426 -2426
  91. package/src/core/AudioDescriptionManager.ts +863 -774
  92. package/src/core/DescriptionSpeechManager.ts +232 -0
  93. package/src/core/FloatingPlayerManager.ts +674 -674
  94. package/src/core/LazyInit.ts +80 -80
  95. package/src/core/MediaSessionManager.ts +341 -0
  96. package/src/core/MetadataAlertsManager.ts +514 -514
  97. package/src/core/Player.ts +2973 -2872
  98. package/src/core/PosterManager.ts +169 -169
  99. package/src/core/PseudoFullscreen.ts +179 -179
  100. package/src/core/ResponsiveManager.ts +231 -231
  101. package/src/core/ResumeManager.ts +236 -236
  102. package/src/core/SignLanguageManager.ts +1308 -1308
  103. package/src/core/ThemeManager.ts +157 -157
  104. package/src/features/PlaylistManager.ts +1838 -1827
  105. package/src/i18n/i18n.ts +223 -223
  106. package/src/i18n/languages/de.ts +244 -217
  107. package/src/i18n/languages/en.ts +245 -218
  108. package/src/i18n/languages/es.ts +241 -214
  109. package/src/i18n/languages/fr.ts +241 -214
  110. package/src/i18n/languages/ja.ts +241 -214
  111. package/src/icons/Icons.ts +168 -167
  112. package/src/index.ts +196 -193
  113. package/src/renderers/DASHRenderer.ts +1077 -1077
  114. package/src/renderers/HLSRenderer.ts +944 -944
  115. package/src/renderers/HTML5Renderer.ts +389 -389
  116. package/src/renderers/SoundCloudRenderer.ts +437 -437
  117. package/src/renderers/VimeoRenderer.ts +298 -298
  118. package/src/renderers/YouTubeRenderer.ts +290 -290
  119. package/src/styles/vidply.css +5852 -5796
  120. package/src/types/events.ts +128 -122
  121. package/src/types/globals.d.ts +313 -313
  122. package/src/types/options.ts +199 -182
  123. package/src/types/renderer.ts +58 -58
  124. package/src/utils/DOMUtils.ts +281 -281
  125. package/src/utils/DownloadInfo.ts +171 -171
  126. package/src/utils/DraggablePanel.ts +539 -539
  127. package/src/utils/DraggablePanelMenu.ts +195 -195
  128. package/src/utils/DraggableResizable.ts +997 -997
  129. package/src/utils/EventEmitter.ts +59 -59
  130. package/src/utils/FormUtils.ts +95 -95
  131. package/src/utils/MenuFactory.ts +483 -483
  132. package/src/utils/Sanitize.ts +70 -70
  133. package/src/utils/SettingsMenuFactory.ts +289 -289
  134. package/src/utils/StorageManager.ts +287 -287
  135. package/src/utils/TrackLabelUtils.ts +52 -52
  136. package/src/utils/UrlSafe.ts +69 -69
  137. package/src/utils/VideoFrameCapture.ts +114 -114
  138. package/src/utils/WindowComponents.ts +250 -250
  139. package/dist/dev/vidply.AudioDescriptionManager-R47Q4YR4.js.map +0 -7
  140. package/dist/dev/vidply.DASHRenderer-PWEMC3BT.js.map +0 -7
  141. package/dist/dev/vidply.FloatingPlayerManager-PKNNNAS2.js.map +0 -7
  142. package/dist/dev/vidply.HLSRenderer-E7RQ74HI.js.map +0 -7
  143. package/dist/dev/vidply.HTML5Renderer-BU4QJJJY.js +0 -12
  144. package/dist/dev/vidply.SignLanguageManager-FET5FQZ7.js.map +0 -7
  145. package/dist/dev/vidply.SoundCloudRenderer-RVSJZODO.js.map +0 -7
  146. package/dist/dev/vidply.TranscriptManager-5GXOQCLX.js.map +0 -7
  147. package/dist/dev/vidply.VimeoRenderer-ER2LSBBW.js.map +0 -7
  148. package/dist/dev/vidply.YouTubeRenderer-NFTOWJAB.js.map +0 -7
  149. package/dist/dev/vidply.chunk-64WXMTGL.js.map +0 -7
  150. package/dist/dev/vidply.chunk-BNFIUL2Z.js.map +0 -7
  151. package/dist/dev/vidply.chunk-JJ47NO24.js.map +0 -7
  152. package/dist/dev/vidply.chunk-KI7C3QVL.js.map +0 -7
  153. package/dist/dev/vidply.chunk-NQPURMQY.js.map +0 -7
  154. package/dist/dev/vidply.chunk-RZWZGDV7.js.map +0 -7
  155. package/dist/dev/vidply.chunk-W25HUCHS.js.map +0 -7
  156. package/dist/dev/vidply.de-4YAT7FFM.js.map +0 -7
  157. package/dist/dev/vidply.es-LEWIRUBH.js.map +0 -7
  158. package/dist/dev/vidply.fr-Q4GV2NOE.js.map +0 -7
  159. package/dist/dev/vidply.ja-S6ITA6WU.js.map +0 -7
  160. package/dist/prod/vidply.AudioDescriptionManager-RHLIJW5C.min.js +0 -6
  161. package/dist/prod/vidply.HTML5Renderer-IU5T3WEW.min.js +0 -6
  162. /package/dist/dev/{vidply.CaptionStyleMenu-7XHRBWUM.js.map → vidply.CaptionStyleMenu-47RCKQF5.js.map} +0 -0
  163. /package/dist/dev/{vidply.HTML5Renderer-BU4QJJJY.js.map → vidply.HTML5Renderer-HLRQUOCU.js.map} +0 -0
  164. /package/dist/dev/{vidply.chunk-SK4V2DAU.js.map → vidply.chunk-HZ7UOKOU.js.map} +0 -0
  165. /package/dist/dev/{vidply.chunk-DZSDO3B7.js.map → vidply.chunk-JZNZX32Z.js.map} +0 -0
  166. /package/dist/dev/{vidply.chunk-VMSQAZM7.js.map → vidply.chunk-P25MRJ6O.js.map} +0 -0
  167. /package/dist/dev/{vidply.chunk-6RQA4ROD.js.map → vidply.chunk-PRFNESYA.js.map} +0 -0
@@ -1,298 +1,298 @@
1
- import type { Renderer } from '../types/renderer.js';
2
- import type { Player } from '../core/Player.js';
3
- import { loadScriptOnce } from '../utils/ScriptLoader.js';
4
-
5
- export class VimeoRenderer implements Renderer {
6
- player: Player;
7
- media: HTMLMediaElement;
8
- vimeo: VimeoPlayer | null;
9
- videoId: string | null;
10
- isReady: boolean;
11
- iframe: HTMLDivElement | null;
12
-
13
- constructor(player: Player) {
14
- this.player = player;
15
- this.media = player.element;
16
- this.vimeo = null;
17
- this.videoId = null;
18
- this.isReady = false;
19
- this.iframe = null;
20
- }
21
-
22
- async init() {
23
- // Extract video ID from URL - use currentSource which works for external renderers
24
- const src = this.player.currentSource || this.player.element.src;
25
- this.videoId = this.extractVideoId(src);
26
-
27
- if (!this.videoId) {
28
- throw new Error('Invalid Vimeo URL');
29
- }
30
-
31
- // Load Vimeo Player API
32
- await this.loadVimeoAPI();
33
-
34
- // Create iframe
35
- this.createIframe();
36
-
37
- // Initialize player
38
- await this.initializePlayer();
39
- }
40
-
41
- extractVideoId(url: string) {
42
- const patterns = [
43
- /vimeo\.com\/(\d+)/,
44
- /vimeo\.com\/video\/(\d+)/,
45
- /player\.vimeo\.com\/video\/(\d+)/
46
- ];
47
-
48
- for (const pattern of patterns) {
49
- const match = url.match(pattern);
50
- if (match && match[1]) {
51
- return match[1];
52
- }
53
- }
54
-
55
- return null;
56
- }
57
-
58
- async loadVimeoAPI() {
59
- // Check if API is already loaded
60
- if (window.Vimeo && window.Vimeo.Player) {
61
- return Promise.resolve();
62
- }
63
-
64
- return loadScriptOnce('https://player.vimeo.com/api/player.js', {
65
- isReady: () => !!(window.Vimeo && window.Vimeo.Player)
66
- });
67
- }
68
-
69
- createIframe() {
70
- // Hide original element
71
- this.player.element.style.display = 'none';
72
-
73
- // Create container for iframe
74
- this.iframe = document.createElement('div');
75
- this.iframe.id = `vimeo-player-${Math.random().toString(36).substr(2, 9)}`;
76
- this.iframe.style.width = '100%';
77
- this.iframe.style.maxHeight = '100%';
78
-
79
- this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);
80
- }
81
-
82
- async initializePlayer() {
83
- const options: Record<string, unknown> = {
84
- id: this.videoId,
85
- width: '100%',
86
- height: '100%',
87
- controls: true,
88
- autoplay: this.player.options.autoplay,
89
- muted: this.player.options.muted,
90
- loop: this.player.options.loop,
91
- keyboard: false
92
- };
93
-
94
- if (this.player.options.startTime > 0) {
95
- options.startTime = this.player.options.startTime;
96
- }
97
-
98
- if (!window.Vimeo || !this.iframe) {
99
- // The SDK never became available (or the iframe container is missing).
100
- // Reject so Player.init() surfaces a real failure instead of treating a
101
- // non-functional embed as a successful initialization.
102
- throw new Error('Vimeo Player API is not available');
103
- }
104
- this.vimeo = new window.Vimeo.Player(this.iframe.id, options as VimeoPlayerOptions);
105
-
106
- // Wait for player to be ready
107
- await this.vimeo.ready();
108
- this.isReady = true;
109
-
110
- // Ensure the iframe has 100% width and height
111
- const vimeoIframe = this.iframe?.querySelector('iframe');
112
- if (vimeoIframe) {
113
- vimeoIframe.style.width = '100%';
114
- vimeoIframe.style.height = '100%';
115
- vimeoIframe.setAttribute('width', '100%');
116
- vimeoIframe.setAttribute('height', '100%');
117
- }
118
-
119
- // Hide VidPly controls - Vimeo has its own
120
- if (this.player.container) {
121
- this.player.container.classList.add('vidply-external-controls');
122
- }
123
-
124
- this.attachEvents();
125
-
126
- // Get initial duration
127
- try {
128
- const duration = await this.vimeo.getDuration();
129
- this.player.state.duration = duration;
130
- this.player.emit('loadedmetadata');
131
- } catch (error) {
132
- this.player.log('Error getting duration:', error, 'warn');
133
- }
134
- }
135
-
136
- attachEvents() {
137
- const vimeo = this.vimeo;
138
- if (!vimeo) return;
139
-
140
- // Vimeo's on() callback signature is (...args: unknown[]) => void, so
141
- // each handler narrows its own payload before reading fields.
142
- vimeo.on('play', () => {
143
- this.player.state.playing = true;
144
- this.player.state.paused = false;
145
- this.player.state.ended = false;
146
- this.player.emit('play');
147
-
148
- if (this.player.options.onPlay) {
149
- this.player.options.onPlay.call(this.player);
150
- }
151
- });
152
-
153
- vimeo.on('pause', () => {
154
- this.player.state.playing = false;
155
- this.player.state.paused = true;
156
- this.player.emit('pause');
157
-
158
- if (this.player.options.onPause) {
159
- this.player.options.onPause.call(this.player);
160
- }
161
- });
162
-
163
- vimeo.on('ended', () => {
164
- this.player.state.playing = false;
165
- this.player.state.paused = true;
166
- this.player.state.ended = true;
167
- this.player.emit('ended');
168
-
169
- if (this.player.options.onEnded) {
170
- this.player.options.onEnded.call(this.player);
171
- }
172
- });
173
-
174
- vimeo.on('timeupdate', (...args: unknown[]) => {
175
- const data = args[0] as { seconds: number; duration: number };
176
- this.player.state.currentTime = data.seconds;
177
- this.player.state.duration = data.duration;
178
- this.player.emit('timeupdate', data.seconds);
179
-
180
- if (this.player.options.onTimeUpdate) {
181
- this.player.options.onTimeUpdate.call(this.player, data.seconds);
182
- }
183
- });
184
-
185
- vimeo.on('volumechange', (...args: unknown[]) => {
186
- const data = args[0] as { volume: number };
187
- this.player.state.volume = data.volume;
188
- this.player.emit('volumechange', data.volume);
189
- });
190
-
191
- vimeo.on('bufferstart', () => {
192
- this.player.state.buffering = true;
193
- this.player.emit('waiting');
194
- });
195
-
196
- vimeo.on('bufferend', () => {
197
- this.player.state.buffering = false;
198
- this.player.emit('canplay');
199
- });
200
-
201
- vimeo.on('seeking', () => {
202
- this.player.state.seeking = true;
203
- this.player.emit('seeking');
204
- });
205
-
206
- vimeo.on('seeked', () => {
207
- this.player.state.seeking = false;
208
- this.player.emit('seeked');
209
- });
210
-
211
- vimeo.on('playbackratechange', (...args: unknown[]) => {
212
- const data = args[0] as { playbackRate: number };
213
- this.player.state.playbackSpeed = data.playbackRate;
214
- this.player.emit('ratechange', data.playbackRate);
215
- });
216
-
217
- vimeo.on('error', (...args: unknown[]) => {
218
- const error = args[0] as { message?: string } | undefined;
219
- this.player.handleError(new Error(`Vimeo error: ${error?.message ?? 'unknown'}`));
220
- });
221
- }
222
-
223
- play() {
224
- if (this.isReady && this.vimeo) {
225
- // Save scroll position to prevent browser from scrolling to video
226
- const scrollX = window.scrollX;
227
- const scrollY = window.scrollY;
228
-
229
- this.vimeo.play().catch((error: unknown) => {
230
- this.player.log('Play error:', error, 'warn');
231
- });
232
-
233
- // Restore scroll position immediately to prevent auto-scroll
234
- window.scrollTo(scrollX, scrollY);
235
- }
236
- }
237
-
238
- pause() {
239
- if (this.isReady && this.vimeo) {
240
- this.vimeo.pause().catch((error: unknown) => {
241
- this.player.log('Pause error:', error, 'warn');
242
- });
243
- }
244
- }
245
-
246
- seek(time: number) {
247
- if (this.isReady && this.vimeo) {
248
- this.vimeo.setCurrentTime(time).catch((error: unknown) => {
249
- this.player.log('Seek error:', error, 'warn');
250
- });
251
- }
252
- }
253
-
254
- setVolume(volume: number) {
255
- if (this.isReady && this.vimeo) {
256
- this.vimeo.setVolume(volume).catch((error: unknown) => {
257
- this.player.log('Volume error:', error, 'warn');
258
- });
259
- this.player.state.volume = volume;
260
- }
261
- }
262
-
263
- setMuted(muted: boolean) {
264
- if (this.isReady && this.vimeo) {
265
- if (muted) {
266
- this.vimeo.setVolume(0);
267
- } else {
268
- this.vimeo.setVolume(this.player.state.volume);
269
- }
270
- this.player.state.muted = muted;
271
- }
272
- }
273
-
274
- setPlaybackSpeed(speed: number) {
275
- if (this.isReady && this.vimeo) {
276
- this.vimeo.setPlaybackRate(speed).catch((error: unknown) => {
277
- this.player.log('Playback rate error:', error, 'warn');
278
- });
279
- this.player.state.playbackSpeed = speed;
280
- }
281
- }
282
-
283
- destroy() {
284
- if (this.vimeo && this.vimeo.destroy) {
285
- this.vimeo.destroy();
286
- }
287
-
288
- if (this.iframe && this.iframe.parentNode) {
289
- this.iframe.parentNode.removeChild(this.iframe);
290
- }
291
-
292
- // Show original element
293
- if (this.player.element) {
294
- this.player.element.style.display = '';
295
- }
296
- }
297
- }
298
-
1
+ import type { Renderer } from '../types/renderer.js';
2
+ import type { Player } from '../core/Player.js';
3
+ import { loadScriptOnce } from '../utils/ScriptLoader.js';
4
+
5
+ export class VimeoRenderer implements Renderer {
6
+ player: Player;
7
+ media: HTMLMediaElement;
8
+ vimeo: VimeoPlayer | null;
9
+ videoId: string | null;
10
+ isReady: boolean;
11
+ iframe: HTMLDivElement | null;
12
+
13
+ constructor(player: Player) {
14
+ this.player = player;
15
+ this.media = player.element;
16
+ this.vimeo = null;
17
+ this.videoId = null;
18
+ this.isReady = false;
19
+ this.iframe = null;
20
+ }
21
+
22
+ async init() {
23
+ // Extract video ID from URL - use currentSource which works for external renderers
24
+ const src = this.player.currentSource || this.player.element.src;
25
+ this.videoId = this.extractVideoId(src);
26
+
27
+ if (!this.videoId) {
28
+ throw new Error('Invalid Vimeo URL');
29
+ }
30
+
31
+ // Load Vimeo Player API
32
+ await this.loadVimeoAPI();
33
+
34
+ // Create iframe
35
+ this.createIframe();
36
+
37
+ // Initialize player
38
+ await this.initializePlayer();
39
+ }
40
+
41
+ extractVideoId(url: string) {
42
+ const patterns = [
43
+ /vimeo\.com\/(\d+)/,
44
+ /vimeo\.com\/video\/(\d+)/,
45
+ /player\.vimeo\.com\/video\/(\d+)/
46
+ ];
47
+
48
+ for (const pattern of patterns) {
49
+ const match = url.match(pattern);
50
+ if (match && match[1]) {
51
+ return match[1];
52
+ }
53
+ }
54
+
55
+ return null;
56
+ }
57
+
58
+ async loadVimeoAPI() {
59
+ // Check if API is already loaded
60
+ if (window.Vimeo && window.Vimeo.Player) {
61
+ return Promise.resolve();
62
+ }
63
+
64
+ return loadScriptOnce('https://player.vimeo.com/api/player.js', {
65
+ isReady: () => !!(window.Vimeo && window.Vimeo.Player)
66
+ });
67
+ }
68
+
69
+ createIframe() {
70
+ // Hide original element
71
+ this.player.element.style.display = 'none';
72
+
73
+ // Create container for iframe
74
+ this.iframe = document.createElement('div');
75
+ this.iframe.id = `vimeo-player-${Math.random().toString(36).substr(2, 9)}`;
76
+ this.iframe.style.width = '100%';
77
+ this.iframe.style.maxHeight = '100%';
78
+
79
+ this.player.element.parentNode?.insertBefore(this.iframe, this.player.element);
80
+ }
81
+
82
+ async initializePlayer() {
83
+ const options: Record<string, unknown> = {
84
+ id: this.videoId,
85
+ width: '100%',
86
+ height: '100%',
87
+ controls: true,
88
+ autoplay: this.player.options.autoplay,
89
+ muted: this.player.options.muted,
90
+ loop: this.player.options.loop,
91
+ keyboard: false
92
+ };
93
+
94
+ if (this.player.options.startTime > 0) {
95
+ options.startTime = this.player.options.startTime;
96
+ }
97
+
98
+ if (!window.Vimeo || !this.iframe) {
99
+ // The SDK never became available (or the iframe container is missing).
100
+ // Reject so Player.init() surfaces a real failure instead of treating a
101
+ // non-functional embed as a successful initialization.
102
+ throw new Error('Vimeo Player API is not available');
103
+ }
104
+ this.vimeo = new window.Vimeo.Player(this.iframe.id, options as VimeoPlayerOptions);
105
+
106
+ // Wait for player to be ready
107
+ await this.vimeo.ready();
108
+ this.isReady = true;
109
+
110
+ // Ensure the iframe has 100% width and height
111
+ const vimeoIframe = this.iframe?.querySelector('iframe');
112
+ if (vimeoIframe) {
113
+ vimeoIframe.style.width = '100%';
114
+ vimeoIframe.style.height = '100%';
115
+ vimeoIframe.setAttribute('width', '100%');
116
+ vimeoIframe.setAttribute('height', '100%');
117
+ }
118
+
119
+ // Hide VidPly controls - Vimeo has its own
120
+ if (this.player.container) {
121
+ this.player.container.classList.add('vidply-external-controls');
122
+ }
123
+
124
+ this.attachEvents();
125
+
126
+ // Get initial duration
127
+ try {
128
+ const duration = await this.vimeo.getDuration();
129
+ this.player.state.duration = duration;
130
+ this.player.emit('loadedmetadata');
131
+ } catch (error) {
132
+ this.player.log('Error getting duration:', error, 'warn');
133
+ }
134
+ }
135
+
136
+ attachEvents() {
137
+ const vimeo = this.vimeo;
138
+ if (!vimeo) return;
139
+
140
+ // Vimeo's on() callback signature is (...args: unknown[]) => void, so
141
+ // each handler narrows its own payload before reading fields.
142
+ vimeo.on('play', () => {
143
+ this.player.state.playing = true;
144
+ this.player.state.paused = false;
145
+ this.player.state.ended = false;
146
+ this.player.emit('play');
147
+
148
+ if (this.player.options.onPlay) {
149
+ this.player.options.onPlay.call(this.player);
150
+ }
151
+ });
152
+
153
+ vimeo.on('pause', () => {
154
+ this.player.state.playing = false;
155
+ this.player.state.paused = true;
156
+ this.player.emit('pause');
157
+
158
+ if (this.player.options.onPause) {
159
+ this.player.options.onPause.call(this.player);
160
+ }
161
+ });
162
+
163
+ vimeo.on('ended', () => {
164
+ this.player.state.playing = false;
165
+ this.player.state.paused = true;
166
+ this.player.state.ended = true;
167
+ this.player.emit('ended');
168
+
169
+ if (this.player.options.onEnded) {
170
+ this.player.options.onEnded.call(this.player);
171
+ }
172
+ });
173
+
174
+ vimeo.on('timeupdate', (...args: unknown[]) => {
175
+ const data = args[0] as { seconds: number; duration: number };
176
+ this.player.state.currentTime = data.seconds;
177
+ this.player.state.duration = data.duration;
178
+ this.player.emit('timeupdate', data.seconds);
179
+
180
+ if (this.player.options.onTimeUpdate) {
181
+ this.player.options.onTimeUpdate.call(this.player, data.seconds);
182
+ }
183
+ });
184
+
185
+ vimeo.on('volumechange', (...args: unknown[]) => {
186
+ const data = args[0] as { volume: number };
187
+ this.player.state.volume = data.volume;
188
+ this.player.emit('volumechange', data.volume);
189
+ });
190
+
191
+ vimeo.on('bufferstart', () => {
192
+ this.player.state.buffering = true;
193
+ this.player.emit('waiting');
194
+ });
195
+
196
+ vimeo.on('bufferend', () => {
197
+ this.player.state.buffering = false;
198
+ this.player.emit('canplay');
199
+ });
200
+
201
+ vimeo.on('seeking', () => {
202
+ this.player.state.seeking = true;
203
+ this.player.emit('seeking');
204
+ });
205
+
206
+ vimeo.on('seeked', () => {
207
+ this.player.state.seeking = false;
208
+ this.player.emit('seeked');
209
+ });
210
+
211
+ vimeo.on('playbackratechange', (...args: unknown[]) => {
212
+ const data = args[0] as { playbackRate: number };
213
+ this.player.state.playbackSpeed = data.playbackRate;
214
+ this.player.emit('ratechange', data.playbackRate);
215
+ });
216
+
217
+ vimeo.on('error', (...args: unknown[]) => {
218
+ const error = args[0] as { message?: string } | undefined;
219
+ this.player.handleError(new Error(`Vimeo error: ${error?.message ?? 'unknown'}`));
220
+ });
221
+ }
222
+
223
+ play() {
224
+ if (this.isReady && this.vimeo) {
225
+ // Save scroll position to prevent browser from scrolling to video
226
+ const scrollX = window.scrollX;
227
+ const scrollY = window.scrollY;
228
+
229
+ this.vimeo.play().catch((error: unknown) => {
230
+ this.player.log('Play error:', error, 'warn');
231
+ });
232
+
233
+ // Restore scroll position immediately to prevent auto-scroll
234
+ window.scrollTo(scrollX, scrollY);
235
+ }
236
+ }
237
+
238
+ pause() {
239
+ if (this.isReady && this.vimeo) {
240
+ this.vimeo.pause().catch((error: unknown) => {
241
+ this.player.log('Pause error:', error, 'warn');
242
+ });
243
+ }
244
+ }
245
+
246
+ seek(time: number) {
247
+ if (this.isReady && this.vimeo) {
248
+ this.vimeo.setCurrentTime(time).catch((error: unknown) => {
249
+ this.player.log('Seek error:', error, 'warn');
250
+ });
251
+ }
252
+ }
253
+
254
+ setVolume(volume: number) {
255
+ if (this.isReady && this.vimeo) {
256
+ this.vimeo.setVolume(volume).catch((error: unknown) => {
257
+ this.player.log('Volume error:', error, 'warn');
258
+ });
259
+ this.player.state.volume = volume;
260
+ }
261
+ }
262
+
263
+ setMuted(muted: boolean) {
264
+ if (this.isReady && this.vimeo) {
265
+ if (muted) {
266
+ this.vimeo.setVolume(0);
267
+ } else {
268
+ this.vimeo.setVolume(this.player.state.volume);
269
+ }
270
+ this.player.state.muted = muted;
271
+ }
272
+ }
273
+
274
+ setPlaybackSpeed(speed: number) {
275
+ if (this.isReady && this.vimeo) {
276
+ this.vimeo.setPlaybackRate(speed).catch((error: unknown) => {
277
+ this.player.log('Playback rate error:', error, 'warn');
278
+ });
279
+ this.player.state.playbackSpeed = speed;
280
+ }
281
+ }
282
+
283
+ destroy() {
284
+ if (this.vimeo && this.vimeo.destroy) {
285
+ this.vimeo.destroy();
286
+ }
287
+
288
+ if (this.iframe && this.iframe.parentNode) {
289
+ this.iframe.parentNode.removeChild(this.iframe);
290
+ }
291
+
292
+ // Show original element
293
+ if (this.player.element) {
294
+ this.player.element.style.display = '';
295
+ }
296
+ }
297
+ }
298
+