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.
- package/README.md +76 -2
- package/css/myetv-player.css +321 -208
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +219 -37
- package/dist/myetv-player.min.js +204 -26
- package/package.json +3 -1
- package/plugins/cloudflare/README.md +26 -4
- package/plugins/cloudflare/myetv-player-cloudflare-stream-plugin.js +1273 -217
- package/plugins/facebook/myetv-player-facebook-plugin.js +1340 -164
- package/plugins/twitch/myetv-player-twitch-plugin.js +428 -167
- package/plugins/vimeo/README.md +1 -1
- package/plugins/vimeo/myetv-player-vimeo.js +560 -247
- package/plugins/youtube/README.md +18 -7
- package/plugins/youtube/myetv-player-youtube-plugin.js +1485 -190
- package/scss/_base.scss +0 -15
- package/scss/_controls.scss +182 -2
- package/scss/_menus.scss +51 -0
- package/scss/_responsive.scss +187 -321
- package/scss/_title-overlay.scss +27 -0
- package/scss/_video.scss +0 -75
- package/scss/_watermark.scss +120 -0
- package/scss/myetv-player.scss +7 -7
- package/src/controls.js +72 -21
- package/src/core.js +43 -5
- package/src/events.js +33 -5
- package/src/utils.js +20 -6
- package/src/watermark.js +51 -0
|
@@ -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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
if (
|
|
133
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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.
|
|
185
|
-
this.api.debug
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (!
|
|
196
|
-
this.api.debug
|
|
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
|
-
|
|
201
|
-
|
|
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
|
-
//
|
|
206
|
-
this.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
222
|
-
fbVideo.setAttribute('data-
|
|
223
|
-
fbVideo.setAttribute('data-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
263
|
-
|
|
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
|
|
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
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
*
|
|
870
|
+
* CRITICAL: Update progress bar tooltip with correct time
|
|
299
871
|
*/
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
305
|
-
|
|
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
|
-
*
|
|
952
|
+
* Format time
|
|
310
953
|
*/
|
|
311
|
-
|
|
312
|
-
if (!
|
|
313
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1263
|
+
* CRITICAL: Setup volume slider event listeners
|
|
331
1264
|
*/
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
*
|
|
1340
|
+
* Update volume UI when volume changes
|
|
341
1341
|
*/
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
-
*
|
|
1377
|
+
* Update mute UI when mute state changes
|
|
352
1378
|
*/
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
*
|
|
1393
|
+
* Get current time from Facebook player
|
|
1394
|
+
* CRITICAL: getCurrentPosition is a METHOD, not a property
|
|
363
1395
|
*/
|
|
364
|
-
|
|
365
|
-
if (!this.fbPlayer)
|
|
366
|
-
|
|
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
|
-
*
|
|
1406
|
+
* Get duration from Facebook player
|
|
1407
|
+
* CRITICAL: getDuration is a METHOD, not a property
|
|
373
1408
|
*/
|
|
374
|
-
|
|
375
|
-
if (!this.fbPlayer)
|
|
376
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
1479
|
+
* Hide poster overlays
|
|
394
1480
|
*/
|
|
395
|
-
|
|
396
|
-
this.
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
-
|
|
404
|
-
this.
|
|
405
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|