myetv-player 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MYETV Player - Facebook Plugin
3
- * File: myetv-player-facebook-plugin.js
3
+ * File: myetv-player-facebook-plugin.js
4
4
  * Integrates Facebook videos with full API control
5
5
  * Created by https://www.myetv.tv https://oskarcosimo.com
6
6
  */
@@ -14,23 +14,19 @@
14
14
  this.options = {
15
15
  // Facebook App ID (required for API features)
16
16
  appId: options.appId || null,
17
-
18
17
  // Video source (Facebook video URL or ID)
19
18
  videoUrl: options.videoUrl || null,
20
19
  videoId: options.videoId || null,
21
-
22
20
  // Embed options
23
21
  width: options.width || 500,
24
22
  autoplay: options.autoplay || false,
25
23
  allowFullscreen: options.allowFullscreen !== false,
26
24
  showText: options.showText !== false,
27
25
  showCaptions: options.showCaptions !== false,
28
-
29
26
  // Plugin options
30
27
  debug: options.debug || false,
31
28
  replaceNativePlayer: options.replaceNativePlayer !== false,
32
29
  autoLoadFromData: options.autoLoadFromData !== false,
33
-
34
30
  ...options
35
31
  };
36
32
 
@@ -38,6 +34,18 @@
38
34
  this.fbContainer = null;
39
35
  this.isFBSDKLoaded = false;
40
36
  this.isPlayerReady = false;
37
+ this.mouseMoveOverlay = null;
38
+ this.timeUpdateInterval = null;
39
+ this.isPlaying = false;
40
+ this.userHasInteracted = false; // Track if user has clicked directly on FB player
41
+ this.waitingForUserClick = false; // Track if we're waiting for user to click FB player
42
+ this.autoplayAttempted = false; // Track if we tried autoplay
43
+ this.volumeSliderListener = null; // Track volume slider listener
44
+ this.progressTooltipListener = null; // Track progress tooltip listener
45
+ this.currentDuration = 0; // Store current video duration for tooltips
46
+ this.styleObserver = null;
47
+ this.isFullscreen = false;
48
+
41
49
 
42
50
  // Get plugin API
43
51
  this.api = player.getPluginAPI ? player.getPluginAPI() : {
@@ -48,7 +56,14 @@
48
56
  debug: (msg) => {
49
57
  if (this.options.debug) console.log('📘 Facebook Plugin:', msg);
50
58
  },
51
- triggerEvent: (event, data) => player.triggerEvent(event, data)
59
+ triggerEvent: (event, data) => player.triggerEvent(event, data),
60
+ registerHook: (hookName, callback) => {
61
+ if (player.registerHook) {
62
+ player.registerHook(hookName, callback);
63
+ } else if (player.hooks && player.hooks[hookName]) {
64
+ player.hooks[hookName].push(callback);
65
+ }
66
+ }
52
67
  };
53
68
 
54
69
  if (!this.options.appId) {
@@ -60,45 +75,78 @@
60
75
  * Setup plugin
61
76
  */
62
77
  setup() {
63
- this.api.debug('Setup started');
78
+ if (this.api.player.options.debug) {
79
+ console.log('FB Plugin: Setup started');
80
+ }
81
+
82
+ // Load Facebook SDK
83
+ this.loadFacebookSDK();
64
84
 
65
- // Auto-detect video URL from data attributes
85
+ // Add player methods
86
+ this.addPlayerMethods();
87
+
88
+ // Register hooks
89
+ this.registerHooks();
90
+
91
+ // Auto-detect video URL
66
92
  if (this.options.autoLoadFromData) {
67
93
  this.autoDetectVideoUrl();
68
94
  }
69
95
 
70
- // Load Facebook SDK
71
- this.loadFacebookSDK().then(() => {
72
- if (this.options.videoUrl || this.options.videoId) {
73
- this.createFacebookPlayer();
74
- }
75
- }).catch(error => {
76
- console.error('📘 Facebook Plugin: Failed to load SDK', error);
77
- });
96
+ // If we have a video, wait for SDK then load
97
+ if (this.options.videoUrl || this.options.videoId) {
98
+ this.waitForSDKThenLoad();
99
+ }
100
+
101
+ if (this.api.player.options.debug) {
102
+ console.log('FB Plugin: Setup completed');
103
+ }
104
+ }
78
105
 
79
- this.api.debug('Setup completed');
106
+ /**
107
+ * Add player methods
108
+ */
109
+ addPlayerMethods() {
110
+ this.player.loadFacebookVideo = (videoUrl, options = {}) => this.loadVideo(videoUrl, options);
111
+ this.player.getFacebookVideoUrl = () => this.options.videoUrl;
112
+ this.player.isFacebookActive = () => this.fbPlayer !== null;
113
+ }
114
+
115
+ /**
116
+ * Register hooks
117
+ */
118
+ registerHooks() {
119
+ this.api.registerHook('beforePlay', (data) => {
120
+ return this.checkForFacebookUrl();
121
+ });
80
122
  }
81
123
 
82
124
  /**
83
- * Auto-detect video URL from various sources
125
+ * Auto-detect video URL
84
126
  */
85
127
  autoDetectVideoUrl() {
86
128
  // Priority 1: Check data attributes
87
129
  const dataVideoUrl = this.api.video.getAttribute('data-video-url');
88
130
  const dataVideoType = this.api.video.getAttribute('data-video-type');
89
-
90
131
  if (dataVideoUrl && dataVideoType === 'facebook') {
91
132
  this.options.videoUrl = dataVideoUrl;
92
- this.api.debug('Video URL detected from data attribute: ' + dataVideoUrl);
133
+ if (this.api.player.options.debug) {
134
+ console.log('FB Plugin: Video URL from data-video-url:', this.options.videoUrl);
135
+ }
93
136
  return;
94
137
  }
95
138
 
96
139
  // Priority 2: Check video src
97
140
  const src = this.api.video.src || this.api.video.currentSrc;
98
- if (src && this.isFacebookUrl(src)) {
99
- this.options.videoUrl = src;
100
- this.api.debug('Video URL detected from src: ' + src);
101
- return;
141
+ if (src) {
142
+ const extractedUrl = this.extractFacebookVideoUrl(src);
143
+ if (extractedUrl) {
144
+ this.options.videoUrl = extractedUrl;
145
+ if (this.api.player.options.debug) {
146
+ console.log('FB Plugin: Video URL from src:', this.options.videoUrl);
147
+ }
148
+ return;
149
+ }
102
150
  }
103
151
 
104
152
  // Priority 3: Check source elements
@@ -106,59 +154,117 @@
106
154
  for (const source of sources) {
107
155
  const sourceSrc = source.getAttribute('src');
108
156
  const sourceType = source.getAttribute('type');
109
-
110
157
  if ((sourceType === 'video/facebook' || this.isFacebookUrl(sourceSrc)) && sourceSrc) {
111
158
  this.options.videoUrl = sourceSrc;
112
- this.api.debug('Video URL detected from source: ' + sourceSrc);
159
+ if (this.api.player.options.debug) {
160
+ console.log('FB Plugin: Video URL from source:', this.options.videoUrl);
161
+ }
113
162
  return;
114
163
  }
115
164
  }
116
165
  }
117
166
 
167
+ /**
168
+ * Wait for SDK then load
169
+ */
170
+ waitForSDKThenLoad() {
171
+ if (this.api.player.options.debug) {
172
+ console.log('FB Plugin: Waiting for Facebook SDK...');
173
+ }
174
+
175
+ const checkAndLoad = () => {
176
+ if (window.FB && window.FB.init && this.isFBSDKLoaded) {
177
+ if (this.api.player.options.debug) {
178
+ console.log('FB Plugin: SDK confirmed ready, loading video');
179
+ }
180
+ this.loadVideo(this.options.videoUrl || this.options.videoId);
181
+ } else {
182
+ if (this.api.player.options.debug) {
183
+ console.log('FB Plugin: SDK not ready, retrying in 100ms...');
184
+ }
185
+ setTimeout(checkAndLoad, 100);
186
+ }
187
+ };
188
+ checkAndLoad();
189
+ }
190
+
191
+ /**
192
+ * Check for Facebook URL
193
+ */
194
+ checkForFacebookUrl() {
195
+ const src = this.api.video.src || this.api.video.currentSrc;
196
+ const videoUrl = this.extractFacebookVideoUrl(src);
197
+ if (videoUrl && videoUrl !== this.options.videoUrl) {
198
+ this.loadVideo(videoUrl);
199
+ return true;
200
+ }
201
+ return false;
202
+ }
203
+
204
+ /**
205
+ * Extract Facebook video URL
206
+ */
207
+ extractFacebookVideoUrl(url) {
208
+ if (!url) return null;
209
+ url = url.trim();
210
+
211
+ const patterns = [
212
+ /facebook\.com\/.*\/videos\/(\d+)/,
213
+ /facebook\.com\/watch\/?\?v=(\d+)/,
214
+ /facebook\.com\/video\.php\?v=(\d+)/,
215
+ /fb\.watch\/([a-zA-Z0-9_-]+)/
216
+ ];
217
+
218
+ for (const pattern of patterns) {
219
+ const match = url.match(pattern);
220
+ if (match && match[1]) {
221
+ return url;
222
+ }
223
+ }
224
+ return null;
225
+ }
226
+
118
227
  /**
119
228
  * Check if URL is a Facebook video URL
120
229
  */
121
230
  isFacebookUrl(url) {
122
231
  if (!url) return false;
123
- return /facebook\.com\/(.*\/)?videos?\//.test(url);
232
+ return /facebook\.com\/(.*\/)?videos?\//.test(url) || /fb\.watch\//.test(url);
124
233
  }
125
234
 
126
235
  /**
127
236
  * Load Facebook SDK
128
237
  */
129
238
  loadFacebookSDK() {
130
- return new Promise((resolve, reject) => {
131
- // Check if already loaded
132
- if (window.FB && window.FB.init) {
133
- this.isFBSDKLoaded = true;
134
- this.initializeFBSDK();
135
- resolve();
136
- return;
239
+ if (window.FB && window.FB.init) {
240
+ this.isFBSDKLoaded = true;
241
+ if (this.api.player.options.debug) {
242
+ console.log('FB Plugin: Facebook SDK already loaded');
137
243
  }
244
+ return;
245
+ }
138
246
 
139
- // Load SDK
140
- window.fbAsyncInit = () => {
141
- this.initializeFBSDK();
142
- this.isFBSDKLoaded = true;
143
- this.api.debug('Facebook SDK loaded');
144
- resolve();
145
- };
247
+ window.fbAsyncInit = () => {
248
+ this.initializeFBSDK();
249
+ this.isFBSDKLoaded = true;
250
+ if (this.api.player.options.debug) {
251
+ console.log('FB Plugin: Facebook SDK loaded and ready');
252
+ }
253
+ this.api.triggerEvent('facebookplugin:ready', {});
254
+ };
146
255
 
147
- // Inject script
148
- const script = document.createElement('script');
149
- script.src = 'https://connect.facebook.net/en_US/sdk.js';
150
- script.async = true;
151
- script.defer = true;
152
- script.onerror = () => reject(new Error('Failed to load Facebook SDK'));
256
+ const script = document.createElement('script');
257
+ script.src = 'https://connect.facebook.net/en_US/sdk.js';
258
+ script.async = true;
259
+ script.defer = true;
153
260
 
154
- if (!document.getElementById('fb-root')) {
155
- const fbRoot = document.createElement('div');
156
- fbRoot.id = 'fb-root';
157
- document.body.insertBefore(fbRoot, document.body.firstChild);
158
- }
261
+ if (!document.getElementById('fb-root')) {
262
+ const fbRoot = document.createElement('div');
263
+ fbRoot.id = 'fb-root';
264
+ document.body.insertBefore(fbRoot, document.body.firstChild);
265
+ }
159
266
 
160
- document.head.appendChild(script);
161
- });
267
+ document.head.appendChild(script);
162
268
  }
163
269
 
164
270
  /**
@@ -181,249 +287,1319 @@
181
287
  if (msg.type === 'video') {
182
288
  this.fbPlayer = msg.instance;
183
289
  this.isPlayerReady = true;
184
- this.setupEventListeners();
185
- this.api.debug('Facebook player ready');
290
+ this.onPlayerReady();
291
+ if (this.api.player.options.debug) {
292
+ console.log('FB Plugin: Facebook player ready');
293
+ }
186
294
  this.api.triggerEvent('facebookplugin:playerready', {});
187
295
  }
188
296
  });
189
297
  }
190
298
 
191
- /**
192
- * Create Facebook player
193
- */
194
- createFacebookPlayer() {
195
- if (!this.options.videoUrl && !this.options.videoId) {
196
- this.api.debug('No video URL or ID provided');
299
+ /**
300
+ * Load Facebook video
301
+ */
302
+ loadVideo(videoInput, options = {}) {
303
+ if (!videoInput) {
304
+ if (this.api.player.options.debug) {
305
+ console.error('FB Plugin: No video URL or ID provided');
306
+ }
307
+ return;
308
+ }
309
+
310
+ if (!this.isFBSDKLoaded) {
311
+ if (this.api.player.options.debug) {
312
+ console.log('FB Plugin: Waiting for SDK...');
313
+ }
314
+ setTimeout(() => this.loadVideo(videoInput, options), 100);
197
315
  return;
198
316
  }
199
317
 
200
- // Hide native player
201
- if (this.options.replaceNativePlayer) {
202
- this.api.video.style.display = 'none';
318
+ if (this.api.player.options.debug) {
319
+ console.log('FB Plugin: Loading video:', videoInput);
203
320
  }
204
321
 
205
- // Create container
206
- this.fbContainer = document.createElement('div');
207
- this.fbContainer.className = 'fb-video-container';
208
- this.fbContainer.style.cssText = `
209
- position: absolute;
210
- top: 0;
211
- left: 0;
212
- width: 100%;
213
- height: 100%;
214
- z-index: 100;
215
- `;
322
+ // Determine video URL
323
+ this.options.videoUrl = this.isFacebookUrl(videoInput) ?
324
+ videoInput :
325
+ `https://www.facebook.com/video.php?v=${videoInput}`;
326
+
327
+ // Hide native video
328
+ this.api.video.style.pointerEvents = 'none';
329
+
330
+ // Hide overlays
331
+ this.hidePosterOverlay();
332
+ this.hideInitialLoading();
333
+ this.hideLoadingOverlay();
334
+
335
+ // Create Facebook container
336
+ if (!this.fbContainer) {
337
+ this.fbContainer = document.createElement('div');
338
+ this.fbContainer.id = 'fb-player-' + Date.now();
339
+ this.fbContainer.className = 'fb-player-container';
340
+
341
+ this.fbContainer.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:2;overflow:hidden;display:flex;align-items:center;justify-content:center;';
342
+
343
+ // Ensure controls are above Facebook player
344
+ if (this.api.controls) {
345
+ this.api.controls.style.zIndex = '10';
346
+ this.api.controls.style.pointerEvents = 'auto';
347
+ }
348
+
349
+ // Ensure title overlay is above Facebook player
350
+ const titleOverlay = this.api.container.querySelector('.title-overlay');
351
+ if (titleOverlay) {
352
+ titleOverlay.style.zIndex = '15';
353
+ titleOverlay.style.pointerEvents = 'none'; // Allow clicks to pass through
354
+ if (this.api.player.options.debug) {
355
+ console.log('FB Plugin: Title overlay z-index set to 15');
356
+ }
357
+ }
358
+
359
+ this.api.container.insertBefore(this.fbContainer, this.api.controls);
360
+ } else {
361
+ this.fbContainer.style.visibility = 'visible';
362
+ this.fbContainer.style.opacity = '1';
363
+ }
364
+
365
+ // Create mouse overlay
366
+ this.createMouseMoveOverlay();
367
+
368
+ // Clear container
369
+ this.fbContainer.innerHTML = '';
370
+
371
+ if (this.api.player.options.debug) {
372
+ console.log('FB Plugin: Using dimensions: 100%');
373
+ }
216
374
 
217
375
  // Create Facebook video element
218
376
  const fbVideo = document.createElement('div');
219
377
  fbVideo.className = 'fb-video';
378
+ fbVideo.style.cssText = `position:absolute;top:0;left:0;width:100%!important;height:100%!important;`;
220
379
 
221
- const videoUrl = this.options.videoUrl || `https://www.facebook.com/video.php?v=${this.options.videoId}`;
222
- fbVideo.setAttribute('data-href', videoUrl);
223
- fbVideo.setAttribute('data-width', this.options.width);
380
+ fbVideo.setAttribute('data-href', this.options.videoUrl);
381
+ fbVideo.setAttribute('data-width', 'auto');
382
+ fbVideo.setAttribute('data-height', 'auto');
224
383
  fbVideo.setAttribute('data-allowfullscreen', this.options.allowFullscreen);
225
- fbVideo.setAttribute('data-autoplay', this.options.autoplay);
384
+
385
+ // CRITICAL: Always set autoplay=false on embed, we'll handle autoplay via API
386
+ fbVideo.setAttribute('data-autoplay', 'false');
387
+
226
388
  fbVideo.setAttribute('data-show-text', this.options.showText);
227
389
  fbVideo.setAttribute('data-show-captions', this.options.showCaptions);
228
390
 
391
+ fbVideo.setAttribute('data-controls', this.options.replaceNativePlayer);
392
+
229
393
  this.fbContainer.appendChild(fbVideo);
230
- this.api.container.appendChild(this.fbContainer);
231
394
 
232
395
  // Parse XFBML
233
396
  if (window.FB && window.FB.XFBML) {
234
397
  FB.XFBML.parse(this.fbContainer);
398
+
399
+ // Force styling usando la nuova funzione
400
+ const forceStyles = () => this.forceVideoStyles();
401
+
402
+ setTimeout(forceStyles, 500);
403
+ setTimeout(forceStyles, 1500);
404
+
405
+ // Setup MutationObserver
406
+ setTimeout(() => {
407
+ const span = this.fbContainer?.querySelector('span');
408
+ const iframe = this.fbContainer?.querySelector('iframe');
409
+
410
+ const observer = new MutationObserver(forceStyles);
411
+
412
+ if (span) observer.observe(span, { attributes: true, attributeFilter: ['style'] });
413
+ if (iframe) observer.observe(iframe, { attributes: true, attributeFilter: ['style'] });
414
+
415
+ this.styleObserver = observer;
416
+ }, 2000);
417
+
418
+ }
419
+
420
+ if (this.api.player.options.debug) {
421
+ console.log('FB Plugin: Facebook player created');
422
+ }
423
+ this.api.triggerEvent('facebookplugin:videoloaded', { videoUrl: this.options.videoUrl });
424
+ }
425
+
426
+ /**
427
+ * Player ready handler
428
+ */
429
+ onPlayerReady() {
430
+ if (this.api.player.options.debug) {
431
+ console.log('FB Plugin: Player ready event fired');
432
+ }
433
+
434
+ // Force visibility
435
+ this.api.video.style.removeProperty('display');
436
+ this.fbContainer.style.display = 'block';
437
+ this.fbContainer.style.visibility = 'visible';
438
+ this.fbContainer.style.opacity = '1';
439
+ this.fbContainer.style.zIndex = '2';
440
+
441
+ // CRITICAL: Remove ALL overlays to expose Facebook player
442
+ this.hideLoadingOverlay();
443
+ this.hideInitialLoading();
444
+ this.hidePosterOverlay();
445
+ this.removeAllPlayerOverlays();
446
+
447
+ // Ensure title overlay is visible and above Facebook player
448
+ const titleOverlay = this.api.container.querySelector('.title-overlay');
449
+ if (titleOverlay) {
450
+ titleOverlay.style.zIndex = '15';
451
+ titleOverlay.style.pointerEvents = 'none'; // Allow clicks to pass through
452
+ titleOverlay.style.display = ''; // Remove any display:none
453
+ titleOverlay.style.visibility = ''; // Remove any visibility:hidden
454
+ if (this.api.player.options.debug) {
455
+ console.log('FB Plugin: Title overlay visibility restored');
456
+ }
457
+ }
458
+
459
+ // Setup event listeners
460
+ this.setupEventListeners();
461
+
462
+ // Start time update
463
+ this.startTimeUpdate();
464
+
465
+ // Sync controls
466
+ this.syncControls();
467
+
468
+ // Setup speed and PiP controls
469
+ this.setupSpeedAndPip();
470
+
471
+ // Setup fullscreen listener
472
+ this.setupFullscreenListener();
473
+
474
+ // Get initial volume once
475
+ if (this.fbPlayer && this.fbPlayer.getVolume) {
476
+ this.fbPlayer.getVolume().then(vol => {
477
+ const percent = Math.round(vol * 100);
478
+ this.updateVolumeUI(percent);
479
+
480
+ // Also check initial mute state
481
+ if (this.fbPlayer.isMuted) {
482
+ this.fbPlayer.isMuted().then(isMuted => {
483
+ this.updateMuteUI(isMuted);
484
+ }).catch(() => { });
485
+ }
486
+ }).catch(error => {
487
+ if (this.api.player.options.debug) {
488
+ console.warn('FB Plugin: Could not get initial volume', error);
489
+ }
490
+ });
491
+ }
492
+
493
+ // CRITICAL: If autoplay was requested, try it ONCE via API
494
+ // If browser blocks it, user will need to click on FB player directly
495
+ if (this.options.autoplay && !this.autoplayAttempted) {
496
+ this.autoplayAttempted = true;
497
+
498
+ if (this.api.player.options.debug) {
499
+ console.log('FB Plugin: Attempting autoplay via API (may be blocked by browser)');
500
+ }
501
+
502
+ setTimeout(() => {
503
+ this.tryAutoplay();
504
+ }, 500);
505
+ }
506
+
507
+ this.api.triggerEvent('facebookplugin:playerready', {});
508
+ }
509
+
510
+ /**
511
+ * CRITICAL: Try autoplay ONCE via API
512
+ * If browser blocks it, user must click directly on Facebook player
513
+ */
514
+ tryAutoplay() {
515
+ if (!this.fbPlayer) return;
516
+
517
+ try {
518
+ // Try to play
519
+ this.fbPlayer.play();
520
+
521
+ // Facebook auto-mutes on autoplay, try to unmute
522
+ // This will likely fail in browser due to autoplay policy
523
+ this.fbPlayer.unmute();
524
+
525
+ if (this.api.player.options.debug) {
526
+ console.log('FB Plugin: Autoplay attempted (browser may have blocked it)');
527
+ }
528
+ } catch (error) {
529
+ if (this.api.player.options.debug) {
530
+ console.log('FB Plugin: Autoplay blocked by browser - user must click FB player directly');
531
+ }
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Setup speed menu and PiP button based on Facebook capabilities
537
+ */
538
+ setupSpeedAndPip() {
539
+ if (this.api.player.options.debug) {
540
+ console.log('FB Plugin: Setting up speed and PiP controls');
541
+ }
542
+
543
+ // SPEED MENU: Facebook doesn't support playback rate, so hide it
544
+ const speedButton = this.api.container.querySelector('.speed-btn');
545
+ const speedMenu = this.api.container.querySelector('.speed-menu');
546
+ const speedControl = this.api.container.querySelector('.speed-control');
547
+
548
+ if (speedButton) {
549
+ speedButton.style.display = 'none';
550
+ if (this.api.player.options.debug) {
551
+ console.log('FB Plugin: Speed button hidden (not supported by Facebook)');
552
+ }
553
+ }
554
+
555
+ if (speedMenu) {
556
+ speedMenu.style.display = 'none';
557
+ }
558
+
559
+ if (speedControl) {
560
+ speedControl.style.display = 'none';
561
+ }
562
+
563
+ // PIP BUTTON: Facebook iframe doesn't support PiP, so hide it
564
+ const pipButton = this.api.container.querySelector('.pip-btn');
565
+
566
+ if (pipButton) {
567
+ pipButton.style.display = 'none';
568
+ if (this.api.player.options.debug) {
569
+ console.log('FB Plugin: PiP button hidden (not supported by Facebook iframe)');
570
+ }
571
+ }
572
+ }
573
+
574
+ /**
575
+ * Setup fullscreen event listener
576
+ */
577
+ setupFullscreenListener() {
578
+ const fullscreenChangeHandler = () => {
579
+ const isNowFullscreen = !!(
580
+ document.fullscreenElement ||
581
+ document.webkitFullscreenElement ||
582
+ document.mozFullScreenElement ||
583
+ document.msFullscreenElement
584
+ );
585
+
586
+ this.isFullscreen = isNowFullscreen;
587
+
588
+ if (this.api.player.options.debug) {
589
+ console.log('FB Plugin: Fullscreen changed:', isNowFullscreen);
590
+ }
591
+
592
+ // Forza stili dopo cambio fullscreen
593
+ setTimeout(() => {
594
+ this.forceVideoStyles();
595
+ }, 100);
596
+ };
597
+
598
+ document.addEventListener('fullscreenchange', fullscreenChangeHandler);
599
+ document.addEventListener('webkitfullscreenchange', fullscreenChangeHandler);
600
+ document.addEventListener('mozfullscreenchange', fullscreenChangeHandler);
601
+ document.addEventListener('MSFullscreenChange', fullscreenChangeHandler);
602
+
603
+ // Salva per cleanup
604
+ this.fullscreenChangeHandler = fullscreenChangeHandler;
605
+ }
606
+
607
+ /**
608
+ * Force video styles (estratto per riuso)
609
+ */
610
+ forceVideoStyles() {
611
+ const span = this.fbContainer?.querySelector('span');
612
+ const iframe = this.fbContainer?.querySelector('iframe');
613
+
614
+ if (span) {
615
+ span.style.setProperty('position', 'absolute', 'important');
616
+ span.style.setProperty('top', '0', 'important');
617
+ span.style.setProperty('left', '0', 'important');
618
+ span.style.setProperty('width', '100%', 'important');
619
+ span.style.setProperty('height', '100%', 'important');
620
+ span.style.setProperty('display', 'flex', 'important');
621
+ span.style.setProperty('align-items', 'center', 'important');
622
+ span.style.setProperty('justify-content', 'center', 'important');
235
623
  }
236
624
 
237
- this.api.debug('Facebook player created');
238
- this.api.triggerEvent('facebookplugin:videoloaded', { videoUrl });
625
+ if (iframe) {
626
+ // IN FULLSCREEN: usa tutto lo schermo
627
+ if (this.isFullscreen) {
628
+ iframe.style.setProperty('position', 'absolute', 'important');
629
+ iframe.style.setProperty('top', '0', 'important');
630
+ iframe.style.setProperty('left', '0', 'important');
631
+ iframe.style.setProperty('width', '100%', 'important');
632
+ iframe.style.setProperty('height', '100%', 'important');
633
+ iframe.style.setProperty('transform', 'none', 'important');
634
+ iframe.style.setProperty('border', 'none', 'important');
635
+
636
+ if (this.api.player.options.debug) {
637
+ console.log('FB Plugin: Fullscreen mode - 100% dimensions');
638
+ }
639
+ } else {
640
+ // NORMALE: mantieni aspect ratio 16:9
641
+ const container = this.api.container;
642
+ const containerWidth = container.clientWidth;
643
+ const containerHeight = container.clientHeight;
644
+ const containerRatio = containerWidth / containerHeight;
645
+ const videoRatio = 16 / 9;
646
+
647
+ let iframeWidth, iframeHeight;
648
+
649
+ if (containerRatio > videoRatio) {
650
+ iframeHeight = containerHeight;
651
+ iframeWidth = iframeHeight * videoRatio;
652
+ } else {
653
+ iframeWidth = containerWidth;
654
+ iframeHeight = iframeWidth / videoRatio;
655
+ }
656
+
657
+ iframe.style.setProperty('position', 'absolute', 'important');
658
+ iframe.style.setProperty('top', '50%', 'important');
659
+ iframe.style.setProperty('left', '50%', 'important');
660
+ iframe.style.setProperty('width', iframeWidth + 'px', 'important');
661
+ iframe.style.setProperty('height', iframeHeight + 'px', 'important');
662
+ iframe.style.setProperty('transform', 'translate(-50%, -50%)', 'important');
663
+ iframe.style.setProperty('border', 'none', 'important');
664
+
665
+ if (this.api.player.options.debug) {
666
+ console.log('FB Plugin: Normal mode - aspect ratio maintained');
667
+ }
668
+ }
669
+ }
239
670
  }
240
671
 
241
672
  /**
242
- * Setup event listeners
673
+ * Create mouse move overlay
674
+ * CRITICAL: Must be completely transparent and NOT block clicks to FB player
675
+ */
676
+ createMouseMoveOverlay() {
677
+ if (this.mouseMoveOverlay) return;
678
+
679
+ this.mouseMoveOverlay = document.createElement('div');
680
+ this.mouseMoveOverlay.className = 'fb-mousemove-overlay';
681
+ // CRITICAL: pointer-events:none to let clicks pass through to Facebook player
682
+ this.mouseMoveOverlay.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;z-index:5;background:transparent;pointer-events:none;';
683
+
684
+ this.api.container.insertBefore(this.mouseMoveOverlay, this.api.controls);
685
+
686
+ // ONLY capture mousemove for controls auto-hide, DON'T block clicks
687
+ this.mouseMoveOverlay.addEventListener('mousemove', (e) => {
688
+ if (this.api.player.onMouseMove) {
689
+ this.api.player.onMouseMove(e);
690
+ }
691
+ });
692
+
693
+ // Don't handle clicks on this overlay - let them pass through to FB player
694
+ if (this.api.player.options.debug) {
695
+ console.log('FB Plugin: Mouse move overlay created with pointer-events:none');
696
+ }
697
+ }
698
+
699
+ /**
700
+ * Toggle play/pause
701
+ */
702
+ togglePlayPause() {
703
+ if (!this.fbPlayer) return;
704
+
705
+ try {
706
+ if (this.isPlaying) {
707
+ this.fbPlayer.pause();
708
+ } else {
709
+ this.fbPlayer.play();
710
+ }
711
+ } catch (error) {
712
+ if (this.api.player.options.debug) {
713
+ console.error('FB Plugin: Error toggling play/pause:', error);
714
+ }
715
+ }
716
+ }
717
+
718
+ /**
719
+ * Setup event listeners with proper main player state sync
243
720
  */
244
721
  setupEventListeners() {
245
722
  if (!this.fbPlayer) return;
246
723
 
247
- // Started playing
724
+ // Started playing - marks successful user interaction
248
725
  this.fbPlayer.subscribe('startedPlaying', () => {
249
- this.api.debug('Video started playing');
726
+ if (this.api.player.options.debug) {
727
+ console.log('FB Plugin: Video started playing');
728
+ }
729
+ this.isPlaying = true;
730
+ this.userHasInteracted = true; // Video playing = user clicked FB player
731
+ this.waitingForUserClick = false;
732
+
733
+ // Update play/pause icons
734
+ const playIcon = this.api.container.querySelector('.play-icon');
735
+ const pauseIcon = this.api.container.querySelector('.pause-icon');
736
+ if (playIcon) playIcon.classList.add('hidden');
737
+ if (pauseIcon) pauseIcon.classList.remove('hidden');
738
+
739
+ this.api.triggerEvent('played', {
740
+ currentTime: this.getCurrentTime(),
741
+ duration: this.getDuration()
742
+ });
743
+
250
744
  this.api.triggerEvent('play', {});
251
745
  this.api.triggerEvent('playing', {});
746
+ this.hideLoadingOverlay();
252
747
  });
253
748
 
254
749
  // Paused
255
750
  this.fbPlayer.subscribe('paused', () => {
256
- this.api.debug('Video paused');
751
+ if (this.api.player.options.debug) {
752
+ console.log('FB Plugin: Video paused');
753
+ }
754
+ this.isPlaying = false;
755
+
756
+ // Update play/pause icons
757
+ const playIcon = this.api.container.querySelector('.play-icon');
758
+ const pauseIcon = this.api.container.querySelector('.pause-icon');
759
+ if (playIcon) playIcon.classList.remove('hidden');
760
+ if (pauseIcon) pauseIcon.classList.add('hidden');
761
+
762
+ this.api.triggerEvent('paused', {
763
+ currentTime: this.getCurrentTime(),
764
+ duration: this.getDuration()
765
+ });
766
+
257
767
  this.api.triggerEvent('pause', {});
258
768
  });
259
769
 
260
770
  // Finished playing
261
771
  this.fbPlayer.subscribe('finishedPlaying', () => {
262
- this.api.debug('Video finished');
263
- this.api.triggerEvent('ended', {});
772
+ if (this.api.player.options.debug) {
773
+ console.log('FB Plugin: Video finished');
774
+ }
775
+ this.isPlaying = false;
776
+
777
+ // Update play/pause icons
778
+ const playIcon = this.api.container.querySelector('.play-icon');
779
+ const pauseIcon = this.api.container.querySelector('.pause-icon');
780
+ if (playIcon) playIcon.classList.remove('hidden');
781
+ if (pauseIcon) pauseIcon.classList.add('hidden');
782
+
783
+ this.api.triggerEvent('ended', {
784
+ currentTime: this.getCurrentTime(),
785
+ duration: this.getDuration()
786
+ });
264
787
  });
265
788
 
266
789
  // Started buffering
267
790
  this.fbPlayer.subscribe('startedBuffering', () => {
268
- this.api.debug('Buffering started');
791
+ if (this.api.player.options.debug) {
792
+ console.log('FB Plugin: Video buffering started');
793
+ }
269
794
  this.api.triggerEvent('waiting', {});
795
+ this.showLoadingOverlay();
270
796
  });
271
797
 
272
798
  // Finished buffering
273
799
  this.fbPlayer.subscribe('finishedBuffering', () => {
274
- this.api.debug('Buffering finished');
800
+ if (this.api.player.options.debug) {
801
+ console.log('FB Plugin: Video buffering finished');
802
+ }
275
803
  this.api.triggerEvent('canplay', {});
804
+ this.hideLoadingOverlay();
276
805
  });
277
806
 
278
807
  // Error
279
808
  this.fbPlayer.subscribe('error', (error) => {
280
- console.error('📘 Facebook player error:', error);
809
+ console.error('FB Plugin: Facebook player error:', error);
281
810
  this.api.triggerEvent('error', error);
282
811
  this.api.triggerEvent('facebookplugin:error', error);
283
812
  });
813
+
814
+ if (this.api.player.options.debug) {
815
+ console.log('FB Plugin: Event listeners setup completed');
816
+ }
284
817
  }
285
818
 
286
- /**
287
- * Play video
288
- */
289
- play() {
290
- if (!this.fbPlayer) {
291
- return Promise.reject('Player not initialized');
819
+ startTimeUpdate() {
820
+ if (this.timeUpdateInterval) {
821
+ clearInterval(this.timeUpdateInterval);
292
822
  }
293
- this.fbPlayer.play();
294
- return Promise.resolve();
823
+
824
+ this.timeUpdateInterval = setInterval(() => {
825
+ if (this.fbPlayer && this.fbPlayer.getCurrentPosition !== undefined && this.fbPlayer.getDuration !== undefined) {
826
+ try {
827
+ // CRITICAL FIX: These are properties, not methods
828
+ const currentTime = this.fbPlayer.getCurrentPosition();
829
+ const duration = this.fbPlayer.getDuration();
830
+
831
+ if (duration && duration > 0) {
832
+ const progress = (currentTime / duration) * 100;
833
+
834
+ // Update progress bar
835
+ if (this.api.player.progressFilled) {
836
+ this.api.player.progressFilled.style.width = progress + '%';
837
+ }
838
+ if (this.api.player.progressHandle) {
839
+ this.api.player.progressHandle.style.left = progress + '%';
840
+ }
841
+
842
+ // Update time display
843
+ const currentTimeEl = this.api.container.querySelector('.current-time');
844
+ const durationEl = this.api.container.querySelector('.duration');
845
+ if (currentTimeEl) {
846
+ currentTimeEl.textContent = this.formatTime(currentTime);
847
+ }
848
+ if (durationEl) {
849
+ durationEl.textContent = this.formatTime(duration);
850
+ }
851
+
852
+ // Create tooltip if it doesn't exist
853
+ this.createProgressTooltip();
854
+
855
+ // CRITICAL: Update progress bar tooltip with current duration
856
+ this.updateProgressTooltip(duration);
857
+ }
858
+
859
+ // REMOVED: Volume monitoring - Facebook API doesn't have volume events
860
+ // Volume is only updated via setupVolumeSlider() when user changes it
861
+
862
+ } catch (error) {
863
+ // Ignore timing errors
864
+ }
865
+ }
866
+ }, 250);
295
867
  }
296
868
 
297
869
  /**
298
- * Pause video
870
+ * CRITICAL: Update progress bar tooltip with correct time
299
871
  */
300
- pause() {
301
- if (!this.fbPlayer) {
302
- return Promise.reject('Player not initialized');
872
+ updateProgressTooltip() {
873
+ const progContainer = this.api.container.querySelector('.progress-container');
874
+ if (!progContainer) return;
875
+
876
+ // Remove old listener if exists
877
+ if (this.progressTooltipListener) {
878
+ progContainer.removeEventListener('mousemove', this.progressTooltipListener);
303
879
  }
304
- this.fbPlayer.pause();
305
- return Promise.resolve();
880
+
881
+ // Create new listener that gets duration in real-time
882
+ this.progressTooltipListener = (e) => {
883
+ const tooltip = this.api.container.querySelector('.progress-tooltip');
884
+ if (!tooltip) return;
885
+
886
+ // Get duration from Facebook player every time (like control bar does)
887
+ let duration = 0;
888
+ if (this.fbPlayer && this.fbPlayer.getDuration) {
889
+ try {
890
+ duration = this.fbPlayer.getDuration();
891
+ } catch (error) {
892
+ return;
893
+ }
894
+ }
895
+
896
+ if (!duration || duration <= 0) return;
897
+
898
+ const rect = progContainer.getBoundingClientRect();
899
+ const x = e.clientX - rect.left;
900
+ const percentage = Math.max(0, Math.min(1, x / rect.width));
901
+ const hoverTime = percentage * duration;
902
+
903
+ tooltip.textContent = this.formatTime(hoverTime);
904
+ tooltip.style.left = (percentage * 100) + '%';
905
+ };
906
+
907
+ progContainer.addEventListener('mousemove', this.progressTooltipListener);
908
+ }
909
+
910
+ /**
911
+ * Create progress tooltip element if it doesn't exist
912
+ */
913
+ createProgressTooltip() {
914
+ const progContainer = this.api.container.querySelector('.progress-container');
915
+ if (!progContainer) return;
916
+
917
+ // Check if tooltip already exists
918
+ let tooltip = progContainer.querySelector('.progress-tooltip');
919
+ if (tooltip) return; // Already exists
920
+
921
+ // Create tooltip element
922
+ tooltip = document.createElement('div');
923
+ tooltip.className = 'progress-tooltip';
924
+ tooltip.textContent = '';
925
+ tooltip.style.cssText = `
926
+ position: absolute;
927
+ bottom: 100%;
928
+ transform: translateX(-50%);
929
+ background: rgba(0, 0, 0, 0.8);
930
+ color: white;
931
+ padding: 4px 8px;
932
+ border-radius: 3px;
933
+ font-size: 12px;
934
+ pointer-events: none;
935
+ white-space: nowrap;
936
+ display: none;
937
+ margin-bottom: 5px;
938
+ `;
939
+
940
+ progContainer.appendChild(tooltip);
941
+
942
+ // Show/hide tooltip on hover
943
+ progContainer.addEventListener('mouseenter', () => {
944
+ tooltip.style.display = 'block';
945
+ });
946
+ progContainer.addEventListener('mouseleave', () => {
947
+ tooltip.style.display = 'none';
948
+ });
306
949
  }
307
950
 
308
951
  /**
309
- * Seek to position
952
+ * Format time
310
953
  */
311
- seek(seconds) {
312
- if (!this.fbPlayer) {
313
- return Promise.reject('Player not initialized');
954
+ formatTime(seconds) {
955
+ if (!seconds || isNaN(seconds) || seconds < 0) return '0:00';
956
+
957
+ const hours = Math.floor(seconds / 3600);
958
+ const minutes = Math.floor((seconds % 3600) / 60);
959
+ const secs = Math.floor(seconds % 60);
960
+
961
+ if (hours > 0) {
962
+ return hours + ':' + minutes.toString().padStart(2, '0') + ':' + secs.toString().padStart(2, '0');
963
+ } else {
964
+ return minutes + ':' + secs.toString().padStart(2, '0');
314
965
  }
315
- this.fbPlayer.seek(seconds);
316
- return Promise.resolve(seconds);
317
966
  }
318
967
 
319
968
  /**
320
- * Get current time
969
+ * Sync controls with proper override and volume handling
970
+ * CRITICAL: Don't prevent API calls, just let them fail gracefully if browser blocks them
321
971
  */
322
- getCurrentTime() {
323
- if (!this.fbPlayer) {
324
- return Promise.reject('Player not initialized');
972
+ syncControls() {
973
+ // Hide/show MYETV controls based on replaceNativePlayer option
974
+ if (!this.options.replaceNativePlayer) {
975
+ // Hide MYETV control bar, show only Facebook native controls
976
+ if (this.api.controls) {
977
+ this.api.controls.style.display = 'none';
978
+ if (this.api.player.options.debug) {
979
+ console.log('FB Plugin: MYETV controls hidden (replaceNativePlayer is false)');
980
+ }
981
+ }
982
+ return; // Don't sync controls, use Facebook's native controls
983
+ } else {
984
+ // Show MYETV control bar, hide Facebook native controls (not possible, they'll coexist)
985
+ if (this.api.controls) {
986
+ this.api.controls.style.display = '';
987
+ if (this.api.player.options.debug) {
988
+ console.log('FB Plugin: MYETV controls visible (replaceNativePlayer is true)');
989
+ }
990
+ }
325
991
  }
326
- return Promise.resolve(this.fbPlayer.getCurrentPosition());
992
+
993
+ if (this.api.player.options.debug) {
994
+ console.log('FB Plugin: Syncing controls');
995
+ }
996
+
997
+ // Store original methods before overriding
998
+ if (!this.originalMethods) {
999
+ this.originalMethods = {
1000
+ play: this.player.play ? this.player.play.bind(this.player) : null,
1001
+ pause: this.player.pause ? this.player.pause.bind(this.player) : null,
1002
+ togglePlayPause: this.player.togglePlayPause ? this.player.togglePlayPause.bind(this.player) : null,
1003
+ seek: this.player.seek ? this.player.seek.bind(this.player) : null,
1004
+ setVolume: this.player.setVolume ? this.player.setVolume.bind(this.player) : null,
1005
+ updateVolume: this.player.updateVolume ? this.player.updateVolume.bind(this.player) : null,
1006
+ setMuted: this.player.setMuted ? this.player.setMuted.bind(this.player) : null,
1007
+ toggleMute: this.player.toggleMute ? this.player.toggleMute.bind(this.player) : null
1008
+ };
1009
+ }
1010
+
1011
+ // CRITICAL: Setup volume slider event listeners
1012
+ this.setupVolumeSlider();
1013
+
1014
+ // Override play method
1015
+ // Let it try, but if it fails browser will just not play
1016
+ this.player.play = () => {
1017
+ if (this.fbPlayer) {
1018
+ if (this.api.player.options.debug) {
1019
+ console.log('📘 FB Plugin: Calling fbPlayer.play()');
1020
+ }
1021
+
1022
+ try {
1023
+ this.fbPlayer.play();
1024
+ } catch (error) {
1025
+ if (this.api.player.options.debug) {
1026
+ console.log('📘 FB Plugin: Play via API failed (expected in browser without user gesture)');
1027
+ }
1028
+ // Mark that we're waiting for user to click FB player
1029
+ this.waitingForUserClick = true;
1030
+ }
1031
+ } else {
1032
+ // fbPlayer not ready yet - wait with retry
1033
+ if (this.api.player.options.debug) {
1034
+ console.log('📘 FB Plugin: fbPlayer not ready, waiting...');
1035
+ }
1036
+
1037
+ let attempts = 0;
1038
+ const waitForPlayer = () => {
1039
+ attempts++;
1040
+ if (this.fbPlayer) {
1041
+ if (this.api.player.options.debug) {
1042
+ console.log('📘 FB Plugin: fbPlayer ready after', attempts, 'attempts');
1043
+ }
1044
+ try {
1045
+ this.fbPlayer.play();
1046
+ } catch (error) {
1047
+ this.waitingForUserClick = true;
1048
+ }
1049
+ } else if (attempts < 50) {
1050
+ setTimeout(waitForPlayer, 100);
1051
+ } else {
1052
+ console.error('📘 FB Plugin: Timeout waiting for fbPlayer');
1053
+ if (this.originalMethods.play) {
1054
+ this.originalMethods.play();
1055
+ }
1056
+ }
1057
+ };
1058
+ waitForPlayer();
1059
+ }
1060
+ };
1061
+
1062
+ // Override pause method
1063
+ this.player.pause = () => {
1064
+ if (this.fbPlayer) {
1065
+ try {
1066
+ this.fbPlayer.pause();
1067
+ this.userHasInteracted = true; // Pause is a user interaction
1068
+ } catch (error) {
1069
+ if (this.api.player.options.debug) {
1070
+ console.error('FB Plugin: Pause error:', error);
1071
+ }
1072
+ }
1073
+ } else if (this.originalMethods.pause) {
1074
+ this.originalMethods.pause();
1075
+ }
1076
+ };
1077
+
1078
+ // Override togglePlayPause method
1079
+ this.player.togglePlayPause = () => {
1080
+ if (this.fbPlayer) {
1081
+ this.userHasInteracted = true; // Toggle is a user interaction
1082
+ this.togglePlayPause();
1083
+ } else if (this.originalMethods.togglePlayPause) {
1084
+ this.originalMethods.togglePlayPause();
1085
+ }
1086
+ };
1087
+
1088
+ // Override seek method - handles BOTH event and time
1089
+ this.player.seek = (eventOrTime) => {
1090
+ if (!this.fbPlayer || !this.fbPlayer.getDuration) {
1091
+ if (this.originalMethods.seek) {
1092
+ this.originalMethods.seek(eventOrTime);
1093
+ }
1094
+ return;
1095
+ }
1096
+
1097
+ // Mark user interaction
1098
+ this.userHasInteracted = true;
1099
+
1100
+ // Check if it's an event from progress bar (mouse or touch)
1101
+ if (eventOrTime && typeof eventOrTime === 'object') {
1102
+ // It's a click/drag/touch event
1103
+ const progContainer = this.api.container.querySelector('.progress-container');
1104
+ if (progContainer) {
1105
+ try {
1106
+ const duration = this.fbPlayer.getDuration();
1107
+ if (!duration || duration <= 0) {
1108
+ if (this.api.player.options.debug) {
1109
+ console.warn('FB Plugin: Duration not available for seek');
1110
+ }
1111
+ return;
1112
+ }
1113
+
1114
+ const rect = progContainer.getBoundingClientRect();
1115
+
1116
+ // Support both mouse and touch events
1117
+ const clientX = eventOrTime.clientX !== undefined
1118
+ ? eventOrTime.clientX
1119
+ : (eventOrTime.touches && eventOrTime.touches[0])
1120
+ ? eventOrTime.touches[0].clientX
1121
+ : (eventOrTime.changedTouches && eventOrTime.changedTouches[0])
1122
+ ? eventOrTime.changedTouches[0].clientX
1123
+ : 0;
1124
+
1125
+ const clickX = clientX - rect.left;
1126
+ const percentage = Math.max(0, Math.min(1, clickX / rect.width));
1127
+ const seekTime = percentage * duration;
1128
+
1129
+ // Update UI immediately
1130
+ const progress = percentage * 100;
1131
+ if (this.api.player.progressFilled) {
1132
+ this.api.player.progressFilled.style.width = progress + '%';
1133
+ }
1134
+ if (this.api.player.progressHandle) {
1135
+ this.api.player.progressHandle.style.left = progress + '%';
1136
+ }
1137
+
1138
+ // Perform seek
1139
+ this.fbPlayer.seek(seekTime);
1140
+
1141
+ if (this.api.player.options.debug) {
1142
+ console.log('FB Plugin: Seeked to', seekTime.toFixed(2), 's');
1143
+ }
1144
+ } catch (error) {
1145
+ if (this.api.player.options.debug) {
1146
+ console.error('FB Plugin: Seek error', error);
1147
+ }
1148
+ }
1149
+ }
1150
+ } else if (typeof eventOrTime === 'number') {
1151
+ // Direct time value
1152
+ if (eventOrTime >= 0) {
1153
+ try {
1154
+ this.fbPlayer.seek(eventOrTime);
1155
+ if (this.api.player.options.debug) {
1156
+ console.log('FB Plugin: Direct seek to', eventOrTime, 's');
1157
+ }
1158
+ } catch (error) {
1159
+ if (this.api.player.options.debug) {
1160
+ console.error('FB Plugin: Seek error', error);
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1165
+ };
1166
+
1167
+ // Override volume methods
1168
+ this.player.setVolume = (volume) => {
1169
+ if (this.fbPlayer && this.fbPlayer.setVolume) {
1170
+ this.userHasInteracted = true; // Volume change is user interaction
1171
+
1172
+ const fbVolume = volume / 100;
1173
+ try {
1174
+ this.fbPlayer.setVolume(fbVolume);
1175
+
1176
+ // Auto-unmute when user changes volume
1177
+ if (volume > 0) {
1178
+ this.fbPlayer.isMuted().then(isMuted => {
1179
+ if (isMuted) {
1180
+ this.fbPlayer.unmute();
1181
+ this.updateMuteUI(false);
1182
+ }
1183
+ }).catch(() => { });
1184
+ }
1185
+
1186
+ this.updateVolumeUI(volume);
1187
+ } catch (error) {
1188
+ if (this.api.player.options.debug) {
1189
+ console.error('FB Plugin: Volume error:', error);
1190
+ }
1191
+ }
1192
+ } else if (this.originalMethods.setVolume) {
1193
+ this.originalMethods.setVolume(volume);
1194
+ }
1195
+ };
1196
+
1197
+ this.player.updateVolume = (volume) => {
1198
+ if (this.fbPlayer && this.fbPlayer.setVolume) {
1199
+ this.userHasInteracted = true;
1200
+ try {
1201
+ this.fbPlayer.setVolume(volume / 100);
1202
+ this.updateVolumeUI(volume);
1203
+ } catch (error) {
1204
+ if (this.api.player.options.debug) {
1205
+ console.error('FB Plugin: Volume update error:', error);
1206
+ }
1207
+ }
1208
+ } else if (this.originalMethods.updateVolume) {
1209
+ this.originalMethods.updateVolume(volume);
1210
+ }
1211
+ };
1212
+
1213
+ this.player.setMuted = (muted) => {
1214
+ if (this.fbPlayer) {
1215
+ this.userHasInteracted = true;
1216
+ try {
1217
+ if (muted) {
1218
+ this.fbPlayer.mute();
1219
+ } else {
1220
+ this.fbPlayer.unmute();
1221
+ }
1222
+ this.updateMuteUI(muted);
1223
+ } catch (error) {
1224
+ if (this.api.player.options.debug) {
1225
+ console.error('FB Plugin: Mute error:', error);
1226
+ }
1227
+ }
1228
+ } else if (this.originalMethods.setMuted) {
1229
+ this.originalMethods.setMuted(muted);
1230
+ }
1231
+ };
1232
+
1233
+ this.player.toggleMute = () => {
1234
+ if (this.fbPlayer && this.fbPlayer.isMuted) {
1235
+ this.userHasInteracted = true;
1236
+
1237
+ try {
1238
+ const isMuted = this.fbPlayer.isMuted();
1239
+ if (isMuted) {
1240
+ this.fbPlayer.unmute();
1241
+ this.updateMuteUI(false);
1242
+ } else {
1243
+ this.fbPlayer.mute();
1244
+ this.updateMuteUI(true);
1245
+ }
1246
+
1247
+ if (this.api.player.options.debug) {
1248
+ console.log('FB Plugin: Mute toggled to', !isMuted);
1249
+ }
1250
+ } catch (error) {
1251
+ if (this.api.player.options.debug) {
1252
+ console.error('FB Plugin: Error toggling mute', error);
1253
+ }
1254
+ }
1255
+ } else if (this.originalMethods.toggleMute) {
1256
+ this.originalMethods.toggleMute();
1257
+ }
1258
+ };
1259
+
327
1260
  }
328
1261
 
329
1262
  /**
330
- * Get duration
1263
+ * CRITICAL: Setup volume slider event listeners
331
1264
  */
332
- getDuration() {
333
- if (!this.fbPlayer) {
334
- return Promise.reject('Player not initialized');
1265
+ setupVolumeSlider() {
1266
+ const volumeSlider = this.api.container.querySelector('.volume-slider');
1267
+ if (!volumeSlider) {
1268
+ if (this.api.player.options.debug) {
1269
+ console.warn('FB Plugin: Volume slider not found');
1270
+ }
1271
+ return;
1272
+ }
1273
+
1274
+ // Remove existing listeners if any
1275
+ if (this.volumeSliderListener) {
1276
+ volumeSlider.removeEventListener('input', this.volumeSliderListener, true);
1277
+ }
1278
+
1279
+ // Create new listener
1280
+ this.volumeSliderListener = (e) => {
1281
+ // CRITICAL: Stop propagation to prevent main player from handling this
1282
+ e.stopPropagation();
1283
+ e.stopImmediatePropagation();
1284
+
1285
+ const volume = parseFloat(e.target.value);
1286
+
1287
+ if (this.api.player.options.debug) {
1288
+ console.log('FB Plugin: Volume slider changed to', volume);
1289
+ }
1290
+
1291
+ this.userHasInteracted = true;
1292
+
1293
+ if (this.fbPlayer && this.fbPlayer.setVolume) {
1294
+ try {
1295
+ // Set volume on Facebook player (0-1 range)
1296
+ this.fbPlayer.setVolume(volume / 100);
1297
+
1298
+ // CRITICAL FIX: Update UI immediately
1299
+ const fill = this.api.container.querySelector('.volume-filled');
1300
+ if (fill) {
1301
+ fill.style.width = volume + '%';
1302
+ }
1303
+
1304
+ // Update CSS custom property
1305
+ if (this.api.container) {
1306
+ this.api.container.style.setProperty('--player-volume-fill', volume + '%');
1307
+ }
1308
+
1309
+ // Auto-unmute when user changes volume
1310
+ if (volume > 0) {
1311
+ this.fbPlayer.isMuted().then(isMuted => {
1312
+ if (isMuted) {
1313
+ this.fbPlayer.unmute();
1314
+ this.updateMuteUI(false);
1315
+ }
1316
+ }).catch(() => { });
1317
+ }
1318
+
1319
+ if (this.api.player.options.debug) {
1320
+ console.log('FB Plugin: Volume set to', volume, '%, Facebook volume:', (volume / 100));
1321
+ }
1322
+
1323
+ } catch (error) {
1324
+ if (this.api.player.options.debug) {
1325
+ console.error('FB Plugin: Volume slider error', error);
1326
+ }
1327
+ }
1328
+ }
1329
+ };
1330
+
1331
+ // CRITICAL: Use capture phase to intercept BEFORE main player
1332
+ volumeSlider.addEventListener('input', this.volumeSliderListener, true);
1333
+
1334
+ if (this.api.player.options.debug) {
1335
+ console.log('FB Plugin: Volume slider listener attached with capture=true');
335
1336
  }
336
- return Promise.resolve(this.fbPlayer.getDuration());
337
1337
  }
338
1338
 
339
1339
  /**
340
- * Mute video
1340
+ * Update volume UI when volume changes
341
1341
  */
342
- mute() {
343
- if (!this.fbPlayer) {
344
- return Promise.reject('Player not initialized');
1342
+ updateVolumeUI(volume) {
1343
+ // Slider
1344
+ const volumeSlider = this.api.container.querySelector('.volume-slider');
1345
+ if (volumeSlider && volumeSlider.value != volume) {
1346
+ volumeSlider.value = volume;
1347
+ }
1348
+
1349
+ // Progress bar fill
1350
+ const fill = this.api.container.querySelector('.volume-filled');
1351
+ if (fill) {
1352
+ fill.style.width = volume + '%';
1353
+ }
1354
+
1355
+ // Update volume visual (main player method)
1356
+ if (this.api.player.updateVolumeSliderVisual) {
1357
+ this.api.player.updateVolumeSliderVisual();
1358
+ }
1359
+
1360
+ // Update CSS custom property
1361
+ if (this.api.container) {
1362
+ this.api.container.style.setProperty('--player-volume-fill', volume + '%');
1363
+ }
1364
+
1365
+ // Update tooltip
1366
+ if (this.api.player.updateVolumeTooltip) {
1367
+ this.api.player.updateVolumeTooltip();
1368
+ }
1369
+
1370
+ // CRITICAL FIX: Also update tooltip position
1371
+ if (this.api.player.updateVolumeTooltipPosition) {
1372
+ this.api.player.updateVolumeTooltipPosition(volume / 100);
345
1373
  }
346
- this.fbPlayer.mute();
347
- return Promise.resolve();
348
1374
  }
349
1375
 
350
1376
  /**
351
- * Unmute video
1377
+ * Update mute UI when mute state changes
352
1378
  */
353
- unmute() {
354
- if (!this.fbPlayer) {
355
- return Promise.reject('Player not initialized');
1379
+ updateMuteUI(isMuted) {
1380
+ const volumeIcon = this.api.container.querySelector('.volume-icon');
1381
+ const muteIcon = this.api.container.querySelector('.mute-icon');
1382
+
1383
+ if (isMuted) {
1384
+ if (volumeIcon) volumeIcon.classList.add('hidden');
1385
+ if (muteIcon) muteIcon.classList.remove('hidden');
1386
+ } else {
1387
+ if (volumeIcon) volumeIcon.classList.remove('hidden');
1388
+ if (muteIcon) muteIcon.classList.add('hidden');
356
1389
  }
357
- this.fbPlayer.unmute();
358
- return Promise.resolve();
359
1390
  }
360
1391
 
361
1392
  /**
362
- * Check if muted
1393
+ * Get current time from Facebook player
1394
+ * CRITICAL: getCurrentPosition is a METHOD, not a property
363
1395
  */
364
- isMuted() {
365
- if (!this.fbPlayer) {
366
- return Promise.reject('Player not initialized');
1396
+ getCurrentTime() {
1397
+ if (!this.fbPlayer) return 0;
1398
+ try {
1399
+ return this.fbPlayer.getCurrentPosition() || 0;
1400
+ } catch (error) {
1401
+ return 0;
367
1402
  }
368
- return Promise.resolve(this.fbPlayer.isMuted());
369
1403
  }
370
1404
 
371
1405
  /**
372
- * Set volume
1406
+ * Get duration from Facebook player
1407
+ * CRITICAL: getDuration is a METHOD, not a property
373
1408
  */
374
- setVolume(volume) {
375
- if (!this.fbPlayer) {
376
- return Promise.reject('Player not initialized');
1409
+ getDuration() {
1410
+ if (!this.fbPlayer) return 0;
1411
+ try {
1412
+ return this.fbPlayer.getDuration() || 0;
1413
+ } catch (error) {
1414
+ return 0;
377
1415
  }
378
- this.fbPlayer.setVolume(volume);
379
- return Promise.resolve(volume);
380
1416
  }
381
1417
 
382
1418
  /**
383
- * Get volume
384
- */
385
- getVolume() {
386
- if (!this.fbPlayer) {
387
- return Promise.reject('Player not initialized');
1419
+ * CRITICAL: Remove ALL player overlays to expose Facebook player completely
1420
+ * EXCEPT the title overlay which should remain visible
1421
+ */
1422
+ removeAllPlayerOverlays() {
1423
+ if (this.api.player.options.debug) {
1424
+ console.log('FB Plugin: Removing all player overlays to expose Facebook player');
1425
+ }
1426
+
1427
+ // Remove poster overlay
1428
+ const posterOverlay = this.api.container.querySelector('.video-poster-overlay');
1429
+ if (posterOverlay) {
1430
+ posterOverlay.style.display = 'none';
1431
+ posterOverlay.style.visibility = 'hidden';
1432
+ posterOverlay.style.opacity = '0';
1433
+ posterOverlay.style.pointerEvents = 'none';
1434
+ }
1435
+
1436
+ // Remove initial loading
1437
+ const initialLoading = this.api.container.querySelector('.initial-loading');
1438
+ if (initialLoading) {
1439
+ initialLoading.style.display = 'none';
1440
+ initialLoading.style.visibility = 'hidden';
1441
+ initialLoading.style.pointerEvents = 'none';
1442
+ }
1443
+
1444
+ // Remove loading overlay
1445
+ const loadingOverlay = this.api.container.querySelector('.loading-overlay');
1446
+ if (loadingOverlay) {
1447
+ loadingOverlay.style.display = 'none';
1448
+ loadingOverlay.style.visibility = 'hidden';
1449
+ loadingOverlay.style.opacity = '0';
1450
+ loadingOverlay.style.pointerEvents = 'none';
388
1451
  }
389
- return Promise.resolve(this.fbPlayer.getVolume());
1452
+
1453
+ // Remove big play button if exists
1454
+ const bigPlayButton = this.api.container.querySelector('.big-play-button');
1455
+ if (bigPlayButton) {
1456
+ bigPlayButton.style.display = 'none';
1457
+ bigPlayButton.style.visibility = 'hidden';
1458
+ bigPlayButton.style.pointerEvents = 'none';
1459
+ }
1460
+
1461
+ // Remove any custom overlays EXCEPT title overlay
1462
+ const customOverlays = this.api.container.querySelectorAll('[class*="overlay"]');
1463
+ customOverlays.forEach(overlay => {
1464
+ // Don't remove mouse move overlay, loading overlay we control, or title overlay
1465
+ if (!overlay.classList.contains('fb-mousemove-overlay') &&
1466
+ !overlay.classList.contains('title-overlay')) {
1467
+ if (this.api.player.options.debug) {
1468
+ console.log('FB Plugin: Hiding overlay:', overlay.className);
1469
+ }
1470
+ overlay.style.display = 'none';
1471
+ overlay.style.visibility = 'hidden';
1472
+ overlay.style.opacity = '0';
1473
+ overlay.style.pointerEvents = 'none';
1474
+ }
1475
+ });
390
1476
  }
391
1477
 
392
1478
  /**
393
- * Load new video
1479
+ * Hide poster overlays
394
1480
  */
395
- loadVideo(videoUrl) {
396
- this.options.videoUrl = videoUrl;
1481
+ hidePosterOverlay() {
1482
+ const posterOverlay = this.api.container.querySelector('.video-poster-overlay');
1483
+ if (posterOverlay) {
1484
+ posterOverlay.classList.add('hidden');
1485
+ posterOverlay.classList.remove('visible');
1486
+ posterOverlay.style.display = 'none';
1487
+ posterOverlay.style.pointerEvents = 'none';
1488
+ }
1489
+ }
397
1490
 
398
- // Remove existing player
399
- if (this.fbContainer) {
400
- this.fbContainer.remove();
1491
+ hideInitialLoading() {
1492
+ const initialLoading = this.api.container.querySelector('.initial-loading');
1493
+ if (initialLoading) initialLoading.style.display = 'none';
1494
+ }
1495
+
1496
+ hideLoadingOverlay() {
1497
+ const loadingOverlay = this.api.container.querySelector('.loading-overlay');
1498
+ if (loadingOverlay) {
1499
+ loadingOverlay.classList.remove('show');
1500
+ loadingOverlay.style.display = 'none';
401
1501
  }
1502
+ }
402
1503
 
403
- // Create new player
404
- this.createFacebookPlayer();
405
- return Promise.resolve();
1504
+ showLoadingOverlay() {
1505
+ const loadingOverlay = this.api.container.querySelector('.loading-overlay');
1506
+ if (loadingOverlay) {
1507
+ loadingOverlay.classList.add('show');
1508
+ loadingOverlay.style.display = 'block';
1509
+ }
406
1510
  }
407
1511
 
408
1512
  /**
409
- * Dispose plugin
1513
+ * Remove mouse move overlay
1514
+ */
1515
+ removeMouseMoveOverlay() {
1516
+ if (this.mouseMoveOverlay) {
1517
+ this.mouseMoveOverlay.remove();
1518
+ this.mouseMoveOverlay = null;
1519
+ }
1520
+ }
1521
+
1522
+ /**
1523
+ * Dispose plugin with proper cleanup
410
1524
  */
411
1525
  dispose() {
412
- this.api.debug('Disposing plugin');
1526
+ if (this.api.player.options.debug) {
1527
+ console.log('FB Plugin: Disposing');
1528
+ }
1529
+
1530
+ if (this.timeUpdateInterval) {
1531
+ clearInterval(this.timeUpdateInterval);
1532
+ this.timeUpdateInterval = null;
1533
+ }
1534
+
1535
+ if (this.styleObserver) {
1536
+ this.styleObserver.disconnect();
1537
+ this.styleObserver = null;
1538
+ }
1539
+
1540
+ if (this.fbPlayer) {
1541
+ this.fbPlayer = null;
1542
+ }
413
1543
 
414
1544
  if (this.fbContainer) {
415
1545
  this.fbContainer.remove();
416
1546
  this.fbContainer = null;
417
1547
  }
418
1548
 
419
- this.fbPlayer = null;
1549
+ // Dopo il cleanup di styleObserver
1550
+ if (this.fullscreenChangeHandler) {
1551
+ document.removeEventListener('fullscreenchange', this.fullscreenChangeHandler);
1552
+ document.removeEventListener('webkitfullscreenchange', this.fullscreenChangeHandler);
1553
+ document.removeEventListener('mozfullscreenchange', this.fullscreenChangeHandler);
1554
+ document.removeEventListener('MSFullscreenChange', this.fullscreenChangeHandler);
1555
+ this.fullscreenChangeHandler = null;
1556
+ }
1557
+
1558
+ this.removeMouseMoveOverlay();
1559
+
1560
+ // CRITICAL: Restore original methods
1561
+ if (this.originalMethods) {
1562
+ if (this.originalMethods.play) this.player.play = this.originalMethods.play;
1563
+ if (this.originalMethods.pause) this.player.pause = this.originalMethods.pause;
1564
+ if (this.originalMethods.togglePlayPause) this.player.togglePlayPause = this.originalMethods.togglePlayPause;
1565
+ if (this.originalMethods.seek) this.player.seek = this.originalMethods.seek;
1566
+ if (this.originalMethods.setVolume) this.player.setVolume = this.originalMethods.setVolume;
1567
+ if (this.originalMethods.updateVolume) this.player.updateVolume = this.originalMethods.updateVolume;
1568
+ if (this.originalMethods.setMuted) this.player.setMuted = this.originalMethods.setMuted;
1569
+ if (this.originalMethods.toggleMute) this.player.toggleMute = this.originalMethods.toggleMute;
1570
+ }
1571
+
1572
+ // CRITICAL: Restore speed and PiP buttons
1573
+ const speedButton = this.api.container.querySelector('.speed-btn');
1574
+ const speedMenu = this.api.container.querySelector('.speed-menu');
1575
+ const speedControl = this.api.container.querySelector('.speed-control');
1576
+ const pipButton = this.api.container.querySelector('.pip-btn');
1577
+
1578
+ if (speedButton) speedButton.style.display = '';
1579
+ if (speedMenu) speedMenu.style.display = '';
1580
+ if (speedControl) speedControl.style.display = '';
1581
+ if (pipButton) pipButton.style.display = '';
420
1582
 
421
1583
  // Restore native player
422
1584
  if (this.api.video && this.options.replaceNativePlayer) {
423
1585
  this.api.video.style.display = '';
1586
+ this.api.video.style.pointerEvents = '';
1587
+ }
1588
+
1589
+ // Restore MYETV controls visibility
1590
+ if (this.api.controls) {
1591
+ this.api.controls.style.display = '';
424
1592
  }
425
1593
 
426
- this.api.debug('Plugin disposed');
1594
+ // Reset interaction flags
1595
+ this.userHasInteracted = false;
1596
+ this.userHasInteracted = false;
1597
+ this.waitingForUserClick = false;
1598
+ this.autoplayAttempted = false;
1599
+
1600
+ if (this.api.player.options.debug) {
1601
+ console.log('FB Plugin: Disposed');
1602
+ }
427
1603
  }
428
1604
  }
429
1605