@capgo/capacitor-jw-player 0.0.13 → 0.0.17

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.
package/dist/esm/web.js CHANGED
@@ -1,65 +1,341 @@
1
+ /// <reference types="jwplayer" />
1
2
  import { WebPlugin } from '@capacitor/core';
2
3
  export class JwPlayerWeb extends WebPlugin {
4
+ constructor() {
5
+ super(...arguments);
6
+ // Track if our overlay is currently displayed
7
+ this.overlayDiv = null;
8
+ // Store JW Player instance
9
+ this.jwPlayerInstance = null;
10
+ }
11
+ // Generate a UUID that works in all browsers
12
+ generateUUID() {
13
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
14
+ const r = Math.random() * 16 | 0;
15
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
16
+ return v.toString(16);
17
+ });
18
+ }
3
19
  async initialize(options) {
4
20
  console.log('INITIALIZE', options);
21
+ if (!options.playerUrl) {
22
+ throw new Error('playerUrl is required for web implementation');
23
+ }
24
+ const playerUrl = options.playerUrl;
25
+ await new Promise((resolve, reject) => {
26
+ const script = document.createElement('script');
27
+ script.src = playerUrl;
28
+ script.onload = () => resolve(null);
29
+ script.onerror = () => reject(new Error('Failed to load JW Player script'));
30
+ document.head.appendChild(script);
31
+ });
32
+ // Check if jwplayer() is available globally
33
+ if (typeof window.jwplayer !== 'function') {
34
+ throw new Error('JW Player script failed to load properly - jwplayer() function not found');
35
+ }
36
+ }
37
+ async play(options) {
38
+ if (!window.jwplayer) {
39
+ throw new Error('JW Player script failed to load properly - jwplayer() function not found');
40
+ }
41
+ // Create fullscreen overlay div
42
+ if (this.overlayDiv) {
43
+ // Remove existing overlay if it exists
44
+ document.body.removeChild(this.overlayDiv);
45
+ this.overlayDiv = null;
46
+ }
47
+ // Create new overlay div (this will be our red background container)
48
+ this.overlayDiv = document.createElement('div');
49
+ // Style the outer overlay to take up 100% of screen
50
+ Object.assign(this.overlayDiv.style, {
51
+ position: 'fixed',
52
+ top: '0',
53
+ left: '0',
54
+ width: '100%',
55
+ height: '100%',
56
+ zIndex: '9999',
57
+ display: 'flex',
58
+ });
59
+ // Create the inner div for the player
60
+ const playerDiv = document.createElement('div');
61
+ const uniqueId = this.generateUUID();
62
+ const id = `jw-player-${uniqueId}`;
63
+ playerDiv.id = id;
64
+ // Style the inner player div
65
+ Object.assign(playerDiv.style, {
66
+ width: '100%',
67
+ height: '100%',
68
+ margin: '0 auto'
69
+ });
70
+ // Create close (X) button
71
+ const closeButton = document.createElement('button');
72
+ closeButton.textContent = 'X';
73
+ Object.assign(closeButton.style, {
74
+ position: 'absolute',
75
+ top: '10px',
76
+ left: '10px',
77
+ background: 'rgba(0, 0, 0, 0.5)',
78
+ color: 'white',
79
+ border: 'none',
80
+ borderRadius: '50%',
81
+ width: '30px',
82
+ height: '30px',
83
+ fontSize: '16px',
84
+ fontWeight: 'bold',
85
+ cursor: 'pointer',
86
+ zIndex: '10000',
87
+ display: 'flex',
88
+ justifyContent: 'center',
89
+ alignItems: 'center',
90
+ padding: '0'
91
+ });
92
+ // Add click handler to close button
93
+ closeButton.addEventListener('click', () => {
94
+ if (this.jwPlayerInstance) {
95
+ this.jwPlayerInstance.remove();
96
+ this.jwPlayerInstance = null;
97
+ }
98
+ if (this.overlayDiv && this.overlayDiv.parentNode) {
99
+ document.body.removeChild(this.overlayDiv);
100
+ this.overlayDiv = null;
101
+ this.notifyListeners('playerDismissed', {});
102
+ }
103
+ });
104
+ // Append inner div to the red overlay
105
+ this.overlayDiv.appendChild(playerDiv);
106
+ // Append close button to overlay
107
+ this.overlayDiv.appendChild(closeButton);
108
+ // Append outer div to body
109
+ document.body.appendChild(this.overlayDiv);
110
+ // Setup player on the inner div
111
+ this.jwPlayerInstance = jwplayer(id).setup({
112
+ playlist: options.mediaType === 'playlist' ? options.mediaUrl : [{ file: options.mediaUrl, title: 'Video' }],
113
+ width: '100%',
114
+ height: '100%',
115
+ autostart: options.autostart || false,
116
+ controls: true,
117
+ displaytitle: false,
118
+ });
119
+ // Add unified event handlers to match iOS and Android
120
+ this.setupEventHandlers();
5
121
  return;
6
122
  }
7
- async play(_options) {
8
- throw this.unimplemented('JW Player is not available in web.');
123
+ // Setup unified event handlers to match iOS and Android
124
+ setupEventHandlers() {
125
+ if (!this.jwPlayerInstance)
126
+ return;
127
+ // Ready event
128
+ this.jwPlayerInstance.on('ready', () => {
129
+ this.notifyListeners('ready', {});
130
+ });
131
+ // Error event
132
+ this.jwPlayerInstance.on('error', (error) => {
133
+ this.notifyListeners('error', {
134
+ message: (error === null || error === void 0 ? void 0 : error.message) || 'Unknown error',
135
+ code: (error === null || error === void 0 ? void 0 : error.code) || -1
136
+ });
137
+ });
138
+ // Play event
139
+ this.jwPlayerInstance.on('play', () => {
140
+ this.notifyListeners('play', { reason: 'external' });
141
+ });
142
+ // Pause event
143
+ this.jwPlayerInstance.on('pause', () => {
144
+ this.notifyListeners('pause', { reason: 'external' });
145
+ });
146
+ // Complete event
147
+ this.jwPlayerInstance.on('complete', () => {
148
+ this.notifyListeners('complete', {});
149
+ });
150
+ // Seek event
151
+ this.jwPlayerInstance.on('seek', (event) => {
152
+ this.notifyListeners('seek', {
153
+ position: event.position,
154
+ offset: event.offset
155
+ });
156
+ });
157
+ // Time event (throttled to not fire too often)
158
+ let lastTimeUpdate = 0;
159
+ this.jwPlayerInstance.on('time', (event) => {
160
+ const now = Date.now();
161
+ // Throttle to once per second max
162
+ if (now - lastTimeUpdate > 1000) {
163
+ this.notifyListeners('time', {
164
+ position: event.position,
165
+ duration: event.duration
166
+ });
167
+ lastTimeUpdate = now;
168
+ }
169
+ });
170
+ // Playlist item event
171
+ this.jwPlayerInstance.on('playlistItem', (event) => {
172
+ var _a;
173
+ this.notifyListeners('playlistItem', {
174
+ index: event.index,
175
+ title: ((_a = event.item) === null || _a === void 0 ? void 0 : _a.title) || ''
176
+ });
177
+ });
178
+ // Playlist complete event
179
+ this.jwPlayerInstance.on('playlistComplete', () => {
180
+ this.notifyListeners('playlistComplete', {});
181
+ });
182
+ // Controls visibility event
183
+ this.jwPlayerInstance.on('controls', (event) => {
184
+ this.notifyListeners('controlsChanged', {
185
+ visible: event.controls
186
+ });
187
+ });
9
188
  }
10
189
  async pause() {
11
- throw this.unimplemented('JW Player is not available in web.');
190
+ if (this.jwPlayerInstance) {
191
+ this.jwPlayerInstance.pause();
192
+ // Event is fired by the event handler
193
+ return;
194
+ }
195
+ throw new Error('Player not active');
12
196
  }
13
197
  async stop() {
14
- throw this.unimplemented('JW Player is not available in web.');
198
+ if (this.jwPlayerInstance) {
199
+ this.jwPlayerInstance.remove();
200
+ this.jwPlayerInstance = null;
201
+ if (this.overlayDiv && this.overlayDiv.parentNode) {
202
+ document.body.removeChild(this.overlayDiv);
203
+ this.overlayDiv = null;
204
+ }
205
+ // The complete event will be triggered by the JW Player event handler,
206
+ // but since we're removing the player instance, we need to send it manually here
207
+ this.notifyListeners('playerDismissed', {});
208
+ return;
209
+ }
210
+ throw new Error('Player not active');
15
211
  }
16
- async seekTo(_options) {
17
- throw this.unimplemented('JW Player is not available in web.');
212
+ async seekTo(options) {
213
+ if (this.jwPlayerInstance) {
214
+ this.jwPlayerInstance.seek(options.time);
215
+ return;
216
+ }
217
+ throw new Error('Player not active');
18
218
  }
19
- async setVolume(_options) {
20
- throw this.unimplemented('JW Player is not available in web.');
219
+ async setVolume(options) {
220
+ if (this.jwPlayerInstance) {
221
+ this.jwPlayerInstance.setVolume(options.volume);
222
+ return;
223
+ }
224
+ throw new Error('Player not active');
21
225
  }
22
226
  async getPosition() {
23
- throw this.unimplemented('JW Player is not available in web.');
227
+ if (this.jwPlayerInstance) {
228
+ return { position: this.jwPlayerInstance.getPosition() };
229
+ }
230
+ throw new Error('Player not active');
24
231
  }
25
232
  async getState() {
26
- throw this.unimplemented('JW Player is not available in web.');
233
+ if (!this.jwPlayerInstance) {
234
+ // IDLE (no player)
235
+ return { state: 0 };
236
+ }
237
+ // Convert JW Player state to our state format
238
+ // jwplayer: BUFFERING(3), IDLE(0), COMPLETE(4), PAUSED(2), PLAYING(1)
239
+ // our API: IDLE(0), BUFFERING(1), PLAYING(2), PAUSED(3), COMPLETE(4)
240
+ const jwState = this.jwPlayerInstance.getState();
241
+ let state = 0;
242
+ switch (jwState) {
243
+ case 'buffering':
244
+ state = 1;
245
+ break;
246
+ case 'playing':
247
+ state = 2;
248
+ break;
249
+ case 'paused':
250
+ state = 3;
251
+ break;
252
+ default: state = 0; // idle
253
+ }
254
+ return { state };
27
255
  }
28
- async setSpeed(_options) {
29
- throw this.unimplemented('JW Player is not available in web.');
256
+ async setSpeed(options) {
257
+ if (this.jwPlayerInstance) {
258
+ this.jwPlayerInstance.setPlaybackRate(options.speed);
259
+ return;
260
+ }
261
+ throw new Error('Player not active');
30
262
  }
31
- async setPlaylistIndex(_options) {
32
- throw this.unimplemented('JW Player is not available in web.');
263
+ async setPlaylistIndex(options) {
264
+ if (this.jwPlayerInstance) {
265
+ this.jwPlayerInstance.playlistItem(options.index);
266
+ return;
267
+ }
268
+ throw new Error('Player not active');
33
269
  }
34
- async loadPlaylist(_options) {
35
- throw this.unimplemented('JW Player is not available in web.');
270
+ async loadPlaylist(options) {
271
+ if (this.jwPlayerInstance) {
272
+ this.jwPlayerInstance.load(options.playlistUrl);
273
+ return;
274
+ }
275
+ throw new Error('Player not active');
36
276
  }
37
- async loadPlaylistWithItems(_options) {
38
- throw this.unimplemented('JW Player is not available in web.');
277
+ async loadPlaylistWithItems(options) {
278
+ if (this.jwPlayerInstance) {
279
+ this.jwPlayerInstance.load(options.playlist);
280
+ return;
281
+ }
282
+ throw new Error('Player not active');
39
283
  }
40
284
  async getAudioTracks() {
41
- throw this.unimplemented('JW Player is not available in web.');
285
+ if (this.jwPlayerInstance) {
286
+ const tracks = this.jwPlayerInstance.getAudioTracks();
287
+ return { tracks };
288
+ }
289
+ throw new Error('Player not active');
42
290
  }
43
291
  async getCurrentAudioTrack() {
44
- throw this.unimplemented('JW Player is not available in web.');
292
+ if (this.jwPlayerInstance) {
293
+ const index = this.jwPlayerInstance.getCurrentAudioTrack();
294
+ return { index };
295
+ }
296
+ throw new Error('Player not active');
45
297
  }
46
- async setCurrentAudioTrack(_options) {
47
- throw this.unimplemented('JW Player is not available in web.');
298
+ async setCurrentAudioTrack(options) {
299
+ if (this.jwPlayerInstance) {
300
+ this.jwPlayerInstance.setCurrentAudioTrack(options.index);
301
+ return;
302
+ }
303
+ throw new Error('Player not active');
48
304
  }
49
305
  async getCaptions() {
50
- throw this.unimplemented('JW Player is not available in web.');
306
+ if (this.jwPlayerInstance) {
307
+ const captions = this.jwPlayerInstance.getCaptionsList();
308
+ return { captions };
309
+ }
310
+ throw new Error('Player not active');
51
311
  }
52
312
  async getCurrentCaptions() {
53
- throw this.unimplemented('JW Player is not available in web.');
313
+ if (this.jwPlayerInstance) {
314
+ const index = this.jwPlayerInstance.getCurrentCaptions();
315
+ return { index };
316
+ }
317
+ throw new Error('Player not active');
54
318
  }
55
- async setCurrentCaptions(_options) {
56
- throw this.unimplemented('JW Player is not available in web.');
319
+ async setCurrentCaptions(options) {
320
+ if (this.jwPlayerInstance) {
321
+ this.jwPlayerInstance.setCurrentCaptions(options.index);
322
+ return;
323
+ }
324
+ throw new Error('Player not active');
57
325
  }
58
326
  async currentPlaylist() {
59
- throw this.unimplemented('JW Player is not available in web.');
327
+ if (this.jwPlayerInstance) {
328
+ return this.jwPlayerInstance.getPlaylist();
329
+ }
330
+ throw new Error('Player not active');
60
331
  }
61
332
  async resume() {
62
- throw this.unimplemented('JW Player is not available in web.');
333
+ if (this.jwPlayerInstance) {
334
+ this.jwPlayerInstance.play();
335
+ // Event is fired by the event handler
336
+ return;
337
+ }
338
+ throw new Error('Player not active');
63
339
  }
64
340
  }
65
341
  //# sourceMappingURL=web.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,WAAY,SAAQ,SAAS;IACxC,KAAK,CAAC,UAAU,CAAC,OAA+B;QAC9C,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAA+D;QACxE,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAA0B;QACrC,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAA4B;QAC1C,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAA2B;QACxC,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,QAA2B;QAChD,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,QAAiC;QAClD,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,QAA6B;QACvD,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,oBAAoB;QACxB,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,QAA2B;QACpD,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,QAA2B;QAClD,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,aAAa,CAAC,oCAAoC,CAAC,CAAC;IACjE,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type { JwPlayerPlugin } from './definitions';\n\nexport class JwPlayerWeb extends WebPlugin implements JwPlayerPlugin {\n async initialize(options: { licenseKey: string }): Promise<void> {\n console.log('INITIALIZE', options);\n return;\n }\n\n async play(_options: { mediaUrl: string; mediaType: 'video' | 'playlist' }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async pause(): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async stop(): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async seekTo(_options: { time: number }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async setVolume(_options: { volume: number }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async getPosition(): Promise<{ position: number }> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async getState(): Promise<{ state: number }> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async setSpeed(_options: { speed: number }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async setPlaylistIndex(_options: { index: number }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async loadPlaylist(_options: { playlistUrl: string }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async loadPlaylistWithItems(_options: { playlist: any[] }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async getAudioTracks(): Promise<{ tracks: any[] }> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async getCurrentAudioTrack(): Promise<{ index: number }> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async setCurrentAudioTrack(_options: { index: number }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async getCaptions(): Promise<{ captions: any[] }> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async getCurrentCaptions(): Promise<{ index: number }> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async setCurrentCaptions(_options: { index: number }): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async currentPlaylist(): Promise<any> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n\n async resume(): Promise<void> {\n throw this.unimplemented('JW Player is not available in web.');\n }\n}\n"]}
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"jwplayer\" />\nimport { WebPlugin } from '@capacitor/core';\n\nimport type { JwPlayerPlugin } from './definitions';\n\ndeclare global {\n interface Window { jwplayer: JWPlayerStatic; }\n}\n\nexport class JwPlayerWeb extends WebPlugin implements JwPlayerPlugin {\n // Track if our overlay is currently displayed\n private overlayDiv: HTMLDivElement | null = null;\n // Store JW Player instance\n private jwPlayerInstance: jwplayer.JWPlayer | null = null;\n\n // Generate a UUID that works in all browsers\n private generateUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }\n\n async initialize(options: { licenseKey: string; playerUrl?: string }): Promise<void> {\n console.log('INITIALIZE', options);\n \n if (!options.playerUrl) {\n throw new Error('playerUrl is required for web implementation');\n }\n \n const playerUrl = options.playerUrl;\n \n await new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = playerUrl;\n script.onload = () => resolve(null);\n script.onerror = () => reject(new Error('Failed to load JW Player script'));\n document.head.appendChild(script);\n });\n\n // Check if jwplayer() is available globally\n if (typeof window.jwplayer !== 'function') {\n throw new Error('JW Player script failed to load properly - jwplayer() function not found');\n }\n }\n\n async play(options: { mediaUrl: string; mediaType: 'video' | 'playlist'; autostart?: boolean }): Promise<void> {\n if (!window.jwplayer) {\n throw new Error('JW Player script failed to load properly - jwplayer() function not found');\n }\n\n // Create fullscreen overlay div\n if (this.overlayDiv) {\n // Remove existing overlay if it exists\n document.body.removeChild(this.overlayDiv);\n this.overlayDiv = null;\n }\n\n // Create new overlay div (this will be our red background container)\n this.overlayDiv = document.createElement('div');\n \n // Style the outer overlay to take up 100% of screen\n Object.assign(this.overlayDiv.style, {\n position: 'fixed',\n top: '0',\n left: '0',\n width: '100%',\n height: '100%',\n zIndex: '9999',\n display: 'flex',\n });\n \n // Create the inner div for the player\n const playerDiv = document.createElement('div');\n const uniqueId = this.generateUUID();\n const id = `jw-player-${uniqueId}`;\n playerDiv.id = id;\n \n // Style the inner player div\n Object.assign(playerDiv.style, {\n width: '100%',\n height: '100%',\n margin: '0 auto'\n });\n \n // Create close (X) button\n const closeButton = document.createElement('button');\n closeButton.textContent = 'X';\n Object.assign(closeButton.style, {\n position: 'absolute',\n top: '10px',\n left: '10px',\n background: 'rgba(0, 0, 0, 0.5)',\n color: 'white',\n border: 'none',\n borderRadius: '50%',\n width: '30px',\n height: '30px',\n fontSize: '16px',\n fontWeight: 'bold',\n cursor: 'pointer',\n zIndex: '10000',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '0'\n });\n \n // Add click handler to close button\n closeButton.addEventListener('click', () => {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.remove();\n this.jwPlayerInstance = null;\n }\n if (this.overlayDiv && this.overlayDiv.parentNode) {\n document.body.removeChild(this.overlayDiv);\n this.overlayDiv = null;\n this.notifyListeners('playerDismissed', {});\n }\n });\n \n // Append inner div to the red overlay\n this.overlayDiv.appendChild(playerDiv);\n \n // Append close button to overlay\n this.overlayDiv.appendChild(closeButton);\n \n // Append outer div to body\n document.body.appendChild(this.overlayDiv);\n \n // Setup player on the inner div\n this.jwPlayerInstance = jwplayer(id).setup({\n playlist: options.mediaType === 'playlist' ? options.mediaUrl : [{ file: options.mediaUrl, title: 'Video'}],\n width: '100%',\n height: '100%',\n autostart: options.autostart || false,\n controls: true,\n displaytitle: false,\n });\n\n // Add unified event handlers to match iOS and Android\n this.setupEventHandlers();\n \n return;\n }\n\n // Setup unified event handlers to match iOS and Android\n private setupEventHandlers() {\n if (!this.jwPlayerInstance) return;\n \n // Ready event\n this.jwPlayerInstance.on('ready', () => {\n this.notifyListeners('ready', {});\n });\n \n // Error event\n this.jwPlayerInstance.on('error', (error: any) => {\n this.notifyListeners('error', {\n message: error?.message || 'Unknown error',\n code: error?.code || -1\n });\n });\n \n // Play event \n this.jwPlayerInstance.on('play', () => {\n this.notifyListeners('play', { reason: 'external' });\n });\n \n // Pause event\n this.jwPlayerInstance.on('pause', () => {\n this.notifyListeners('pause', { reason: 'external' });\n });\n \n // Complete event\n this.jwPlayerInstance.on('complete', () => {\n this.notifyListeners('complete', {});\n });\n \n // Seek event\n this.jwPlayerInstance.on('seek', (event: { position: number; offset: number }) => {\n this.notifyListeners('seek', {\n position: event.position,\n offset: event.offset\n });\n });\n \n // Time event (throttled to not fire too often)\n let lastTimeUpdate = 0;\n this.jwPlayerInstance.on('time', (event: { position: number; duration: number }) => {\n const now = Date.now();\n // Throttle to once per second max\n if (now - lastTimeUpdate > 1000) {\n this.notifyListeners('time', {\n position: event.position,\n duration: event.duration\n });\n lastTimeUpdate = now;\n }\n });\n \n // Playlist item event\n this.jwPlayerInstance.on('playlistItem', (event: { index: number; item: any }) => {\n this.notifyListeners('playlistItem', {\n index: event.index,\n title: event.item?.title || ''\n });\n });\n \n // Playlist complete event\n this.jwPlayerInstance.on('playlistComplete', () => {\n this.notifyListeners('playlistComplete', {});\n });\n \n // Controls visibility event\n this.jwPlayerInstance.on('controls', (event: { controls: boolean }) => {\n this.notifyListeners('controlsChanged', {\n visible: event.controls\n });\n });\n }\n\n async pause(): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.pause();\n // Event is fired by the event handler\n return;\n }\n throw new Error('Player not active');\n }\n\n async stop(): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.remove();\n this.jwPlayerInstance = null;\n \n if (this.overlayDiv && this.overlayDiv.parentNode) {\n document.body.removeChild(this.overlayDiv);\n this.overlayDiv = null;\n }\n \n // The complete event will be triggered by the JW Player event handler,\n // but since we're removing the player instance, we need to send it manually here\n this.notifyListeners('playerDismissed', {});\n return;\n }\n throw new Error('Player not active');\n }\n\n async seekTo(options: { time: number }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.seek(options.time);\n return;\n }\n throw new Error('Player not active');\n }\n\n async setVolume(options: { volume: number }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.setVolume(options.volume);\n return;\n }\n throw new Error('Player not active');\n }\n\n async getPosition(): Promise<{ position: number }> {\n if (this.jwPlayerInstance) {\n return { position: this.jwPlayerInstance.getPosition() };\n }\n throw new Error('Player not active');\n }\n\n async getState(): Promise<{ state: number }> {\n if (!this.jwPlayerInstance) {\n // IDLE (no player)\n return { state: 0 };\n }\n \n // Convert JW Player state to our state format\n // jwplayer: BUFFERING(3), IDLE(0), COMPLETE(4), PAUSED(2), PLAYING(1)\n // our API: IDLE(0), BUFFERING(1), PLAYING(2), PAUSED(3), COMPLETE(4)\n const jwState = this.jwPlayerInstance.getState();\n let state = 0;\n \n switch (jwState) {\n case 'buffering': state = 1; break;\n case 'playing': state = 2; break;\n case 'paused': state = 3; break;\n default: state = 0; // idle\n }\n \n return { state };\n }\n\n async setSpeed(options: { speed: number }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.setPlaybackRate(options.speed);\n return;\n }\n throw new Error('Player not active');\n }\n\n async setPlaylistIndex(options: { index: number }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.playlistItem(options.index);\n return;\n }\n throw new Error('Player not active');\n }\n\n async loadPlaylist(options: { playlistUrl: string }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.load(options.playlistUrl);\n return;\n }\n throw new Error('Player not active');\n }\n\n async loadPlaylistWithItems(options: { playlist: any[] }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.load(options.playlist);\n return;\n }\n throw new Error('Player not active');\n }\n\n async getAudioTracks(): Promise<{ tracks: any[] }> {\n if (this.jwPlayerInstance) {\n const tracks = this.jwPlayerInstance.getAudioTracks();\n return { tracks };\n }\n throw new Error('Player not active');\n }\n\n async getCurrentAudioTrack(): Promise<{ index: number }> {\n if (this.jwPlayerInstance) {\n const index = this.jwPlayerInstance.getCurrentAudioTrack();\n return { index };\n }\n throw new Error('Player not active');\n }\n\n async setCurrentAudioTrack(options: { index: number }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.setCurrentAudioTrack(options.index);\n return;\n }\n throw new Error('Player not active');\n }\n\n async getCaptions(): Promise<{ captions: any[] }> {\n if (this.jwPlayerInstance) {\n const captions = this.jwPlayerInstance.getCaptionsList();\n return { captions };\n }\n throw new Error('Player not active');\n }\n\n async getCurrentCaptions(): Promise<{ index: number }> {\n if (this.jwPlayerInstance) {\n const index = this.jwPlayerInstance.getCurrentCaptions();\n return { index };\n }\n throw new Error('Player not active');\n }\n\n async setCurrentCaptions(options: { index: number }): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.setCurrentCaptions(options.index);\n return;\n }\n throw new Error('Player not active');\n }\n\n async currentPlaylist(): Promise<any> {\n if (this.jwPlayerInstance) {\n return this.jwPlayerInstance.getPlaylist();\n }\n throw new Error('Player not active');\n }\n\n async resume(): Promise<void> {\n if (this.jwPlayerInstance) {\n this.jwPlayerInstance.play();\n // Event is fired by the event handler\n return;\n }\n throw new Error('Player not active');\n }\n}\n"]}