myetv-player 1.1.4 → 1.1.6
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 +16 -0
- package/css/myetv-player.css +21 -0
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +158 -49
- package/dist/myetv-player.min.js +114 -29
- package/package.json +3 -1
- package/plugins/facebook/README.md +3 -0
- package/plugins/facebook/myetv-player-facebook-plugin.js +283 -17
- package/plugins/vimeo/README.md +3 -0
- package/plugins/vimeo/myetv-player-vimeo.js +556 -39
- package/plugins/youtube/README.md +7 -0
- package/plugins/youtube/myetv-player-youtube-plugin.js +817 -288
- package/scss/_controls.scss +21 -0
- package/src/controls.js +136 -32
- package/src/core.js +22 -17
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
apiKey: options.apiKey || null,
|
|
14
14
|
autoplay: options.autoplay !== undefined ? options.autoplay : false,
|
|
15
15
|
showYouTubeUI: options.showYouTubeUI !== undefined ? options.showYouTubeUI : false,
|
|
16
|
+
showNativeControlsButton: options.showNativeControlsButton !== undefined ? options.showNativeControlsButton : true,
|
|
17
|
+
controlBarOpacity: options.controlBarOpacity !== undefined
|
|
18
|
+
? options.controlBarOpacity
|
|
19
|
+
: (player.options.controlBarOpacity !== undefined ? player.options.controlBarOpacity : 0.95),
|
|
20
|
+
titleOverlayOpacity: options.titleOverlayOpacity !== undefined
|
|
21
|
+
? options.titleOverlayOpacity
|
|
22
|
+
: (player.options.titleOverlayOpacity !== undefined ? player.options.titleOverlayOpacity : 0.95),
|
|
16
23
|
autoLoadFromData: options.autoLoadFromData !== undefined ? options.autoLoadFromData : true,
|
|
17
24
|
quality: options.quality || 'default',
|
|
18
25
|
|
|
@@ -367,10 +374,20 @@
|
|
|
367
374
|
|
|
368
375
|
// Hide original speed menu option from settings (if exists)
|
|
369
376
|
if (settingsMenu) {
|
|
377
|
+
// Hide old non-expandable speed option
|
|
370
378
|
const originalSpeedOption = settingsMenu.querySelector('[data-action="speed"]');
|
|
371
379
|
if (originalSpeedOption) {
|
|
372
380
|
originalSpeedOption.style.display = 'none';
|
|
373
381
|
}
|
|
382
|
+
|
|
383
|
+
// Hide new expandable speed option
|
|
384
|
+
const expandableSpeedWrapper = settingsMenu.querySelector('[data-action="speed-expand"]');
|
|
385
|
+
if (expandableSpeedWrapper) {
|
|
386
|
+
const wrapper = expandableSpeedWrapper.closest('.settings-expandable-wrapper');
|
|
387
|
+
if (wrapper) {
|
|
388
|
+
wrapper.style.display = 'none';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
374
391
|
}
|
|
375
392
|
|
|
376
393
|
// Add subtitles option to settings menu
|
|
@@ -647,6 +664,15 @@
|
|
|
647
664
|
if (originalSpeedOption) {
|
|
648
665
|
originalSpeedOption.style.display = '';
|
|
649
666
|
}
|
|
667
|
+
|
|
668
|
+
// Show expandable speed option again
|
|
669
|
+
const expandableSpeedWrapper = settingsMenu.querySelector('[data-action="speed-expand"]');
|
|
670
|
+
if (expandableSpeedWrapper) {
|
|
671
|
+
const wrapper = expandableSpeedWrapper.closest('.settings-expandable-wrapper');
|
|
672
|
+
if (wrapper) {
|
|
673
|
+
wrapper.style.display = '';
|
|
674
|
+
}
|
|
675
|
+
}
|
|
650
676
|
}
|
|
651
677
|
|
|
652
678
|
// Remove from settings
|
|
@@ -871,7 +897,7 @@
|
|
|
871
897
|
|
|
872
898
|
const playerVars = {
|
|
873
899
|
autoplay: this.options.autoplay ? 1 : 0,
|
|
874
|
-
controls:
|
|
900
|
+
controls: 1,
|
|
875
901
|
fs: this.options.showYouTubeUI ? 1 : 0,
|
|
876
902
|
disablekb: 1,
|
|
877
903
|
modestbranding: 1,
|
|
@@ -912,130 +938,74 @@
|
|
|
912
938
|
this.api.triggerEvent('youtubeplugin:videoloaded', { videoId });
|
|
913
939
|
}
|
|
914
940
|
|
|
915
|
-
setAdaptiveQuality() {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
try {
|
|
919
|
-
// Check network connection speed if available
|
|
920
|
-
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
921
|
-
let suggestedQuality = 'default';
|
|
922
|
-
|
|
923
|
-
if (connection) {
|
|
924
|
-
const effectiveType = connection.effectiveType; // '4g', '3g', '2g', 'slow-2g'
|
|
925
|
-
const downlink = connection.downlink; // Mbps
|
|
926
|
-
|
|
927
|
-
if (this.api.player.options.debug) {
|
|
928
|
-
console.log('[YT Plugin] Connection:', effectiveType, 'Downlink:', downlink, 'Mbps');
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Set quality based on connection speed
|
|
932
|
-
if (effectiveType === 'slow-2g' || downlink < 0.5) {
|
|
933
|
-
suggestedQuality = 'small'; // 240p
|
|
934
|
-
} else if (effectiveType === '2g' || downlink < 1) {
|
|
935
|
-
suggestedQuality = 'medium'; // 360p
|
|
936
|
-
} else if (effectiveType === '3g' || downlink < 2.5) {
|
|
937
|
-
suggestedQuality = 'large'; // 480p
|
|
938
|
-
} else if (downlink < 5) {
|
|
939
|
-
suggestedQuality = 'hd720'; // 720p
|
|
940
|
-
} else if (downlink < 10) {
|
|
941
|
-
suggestedQuality = 'hd1080'; // 1080p
|
|
942
|
-
} else if (downlink < 20) {
|
|
943
|
-
suggestedQuality = 'hd1440'; // 1440p (2K)
|
|
944
|
-
} else if (downlink < 35) {
|
|
945
|
-
suggestedQuality = 'hd2160'; // 2160p (4K)
|
|
946
|
-
} else {
|
|
947
|
-
suggestedQuality = 'highres'; // 8K o migliore disponibile
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
if (this.api.player.options.debug) {
|
|
951
|
-
console.log('[YT Plugin] Setting suggested quality:', suggestedQuality);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
this.ytPlayer.setPlaybackQuality(suggestedQuality);
|
|
955
|
-
} else {
|
|
956
|
-
// Fallback: start with medium quality on unknown devices
|
|
957
|
-
if (this.api.player.options.debug) {
|
|
958
|
-
console.log('[YT Plugin] Connection API not available, using large (480p) as safe default');
|
|
959
|
-
}
|
|
960
|
-
this.ytPlayer.setPlaybackQuality('large'); // 480p come default sicuro
|
|
961
|
-
}
|
|
962
|
-
} catch (error) {
|
|
963
|
-
if (this.api.player.options.debug) {
|
|
964
|
-
console.error('[YT Plugin] Error setting adaptive quality:', error);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
startBufferMonitoring() {
|
|
970
|
-
if (this.bufferMonitorInterval) {
|
|
971
|
-
clearInterval(this.bufferMonitorInterval);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
let bufferingCount = 0;
|
|
975
|
-
let lastState = null;
|
|
976
|
-
|
|
977
|
-
this.bufferMonitorInterval = setInterval(() => {
|
|
978
|
-
if (!this.ytPlayer) return;
|
|
979
|
-
|
|
980
|
-
try {
|
|
981
|
-
const state = this.ytPlayer.getPlayerState();
|
|
941
|
+
setAdaptiveQuality() {
|
|
942
|
+
if (!this.ytPlayer) return;
|
|
982
943
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
944
|
+
try {
|
|
945
|
+
// Check video duration first
|
|
946
|
+
const duration = this.ytPlayer.getDuration();
|
|
947
|
+
const durationMinutes = duration / 60;
|
|
986
948
|
|
|
987
949
|
if (this.api.player.options.debug) {
|
|
988
|
-
console.log('[YT Plugin]
|
|
950
|
+
console.log('[YT Plugin] Video duration:', Math.floor(durationMinutes), 'minutes');
|
|
989
951
|
}
|
|
990
952
|
|
|
991
|
-
//
|
|
992
|
-
if (
|
|
993
|
-
const currentQuality = this.ytPlayer.getPlaybackQuality();
|
|
994
|
-
const availableQualities = this.ytPlayer.getAvailableQualityLevels();
|
|
995
|
-
|
|
953
|
+
// For videos longer than 2 hours, cap at 1080p
|
|
954
|
+
if (durationMinutes > 120) {
|
|
996
955
|
if (this.api.player.options.debug) {
|
|
997
|
-
console.log('[YT Plugin]
|
|
998
|
-
console.log('[YT Plugin] Available qualities:', availableQualities);
|
|
956
|
+
console.log('[YT Plugin] Long video detected - capping at 1080p');
|
|
999
957
|
}
|
|
958
|
+
this.ytPlayer.setPlaybackQuality('hd1080');
|
|
959
|
+
return; // Exit early, don't check connection
|
|
960
|
+
}
|
|
1000
961
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
962
|
+
// Normal adaptive quality for shorter videos
|
|
963
|
+
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
964
|
+
let suggestedQuality = 'default';
|
|
1004
965
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
966
|
+
if (connection) {
|
|
967
|
+
const effectiveType = connection.effectiveType;
|
|
968
|
+
const downlink = connection.downlink;
|
|
1008
969
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
console.log('[YT Plugin] Reducing quality to:', lowerQuality);
|
|
1013
|
-
}
|
|
970
|
+
if (this.api.player.options.debug) {
|
|
971
|
+
console.log('[YT Plugin] Connection:', effectiveType, 'Downlink:', downlink, 'Mbps');
|
|
972
|
+
}
|
|
1014
973
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
974
|
+
if (effectiveType === 'slow-2g' || downlink < 0.5) {
|
|
975
|
+
suggestedQuality = 'small';
|
|
976
|
+
} else if (effectiveType === '2g' || downlink < 1) {
|
|
977
|
+
suggestedQuality = 'medium';
|
|
978
|
+
} else if (effectiveType === '3g' || downlink < 2.5) {
|
|
979
|
+
suggestedQuality = 'large';
|
|
980
|
+
} else if (downlink < 5) {
|
|
981
|
+
suggestedQuality = 'hd720';
|
|
982
|
+
} else if (downlink < 10) {
|
|
983
|
+
suggestedQuality = 'hd1080';
|
|
984
|
+
} else if (downlink < 20) {
|
|
985
|
+
suggestedQuality = 'hd1440';
|
|
986
|
+
} else if (downlink < 35) {
|
|
987
|
+
suggestedQuality = 'hd2160';
|
|
988
|
+
} else {
|
|
989
|
+
suggestedQuality = 'highres';
|
|
1018
990
|
}
|
|
991
|
+
|
|
992
|
+
if (this.api.player.options.debug) {
|
|
993
|
+
console.log('[YT Plugin] Setting suggested quality:', suggestedQuality);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
this.ytPlayer.setPlaybackQuality(suggestedQuality);
|
|
997
|
+
} else {
|
|
998
|
+
if (this.api.player.options.debug) {
|
|
999
|
+
console.log('[YT Plugin] Connection API not available, using large (480p) as safe default');
|
|
1000
|
+
}
|
|
1001
|
+
this.ytPlayer.setPlaybackQuality('large');
|
|
1019
1002
|
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
setTimeout(() => {
|
|
1024
|
-
if (this.ytPlayer.getPlayerState() === YT.PlayerState.PLAYING) {
|
|
1025
|
-
bufferingCount = Math.max(0, bufferingCount - 1);
|
|
1026
|
-
}
|
|
1027
|
-
}, 5000); // Wait 5 seconds of smooth playback
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
if (this.api.player.options.debug) {
|
|
1005
|
+
console.error('[YT Plugin] Error setting adaptive quality:', error);
|
|
1028
1006
|
}
|
|
1029
1007
|
}
|
|
1030
|
-
|
|
1031
|
-
lastState = state;
|
|
1032
|
-
} catch (error) {
|
|
1033
|
-
if (this.api.player.options.debug) {
|
|
1034
|
-
console.error('[YT Plugin] Error in buffer monitoring:', error);
|
|
1035
|
-
}
|
|
1036
1008
|
}
|
|
1037
|
-
}, 1000); // Check every second
|
|
1038
|
-
}
|
|
1039
1009
|
|
|
1040
1010
|
createMouseMoveOverlay() {
|
|
1041
1011
|
if (this.mouseMoveOverlay) return;
|
|
@@ -1329,11 +1299,17 @@ startBufferMonitoring() {
|
|
|
1329
1299
|
this.hideLoadingOverlay();
|
|
1330
1300
|
this.hideInitialLoading();
|
|
1331
1301
|
this.injectYouTubeCSSOverride();
|
|
1302
|
+
// inject controlbar gradient styles
|
|
1303
|
+
this.injectControlbarGradientStyles();
|
|
1332
1304
|
|
|
1333
1305
|
this.syncControls();
|
|
1306
|
+
|
|
1334
1307
|
// Start buffer monitoring for auto quality adjustment
|
|
1335
1308
|
this.startBufferMonitoring();
|
|
1336
1309
|
|
|
1310
|
+
// Create YouTube controls button
|
|
1311
|
+
this.createYouTubeControlsButton();
|
|
1312
|
+
|
|
1337
1313
|
// Hide custom controls when YouTube native UI is enabled
|
|
1338
1314
|
if (this.options.showYouTubeUI) {
|
|
1339
1315
|
// Hide controls
|
|
@@ -1343,6 +1319,17 @@ startBufferMonitoring() {
|
|
|
1343
1319
|
this.api.controls.style.visibility = 'hidden';
|
|
1344
1320
|
this.api.controls.style.pointerEvents = 'none';
|
|
1345
1321
|
}
|
|
1322
|
+
// Hide YouTube controls with CSS if showYouTubeUI is false
|
|
1323
|
+
if (!this.options.showYouTubeUI) {
|
|
1324
|
+
const iframe = this.ytPlayerContainer.querySelector('iframe');
|
|
1325
|
+
if (iframe) {
|
|
1326
|
+
iframe.style.pointerEvents = 'none'; // Block clicks to YouTube controls
|
|
1327
|
+
|
|
1328
|
+
if (this.api.player.options.debug) {
|
|
1329
|
+
console.log('[YT Plugin] YouTube controls hidden with CSS (showYouTubeUI: false)');
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1346
1333
|
|
|
1347
1334
|
// Hide overlay title
|
|
1348
1335
|
const overlayTitle = this.api.container.querySelector('.title-overlay');
|
|
@@ -1409,12 +1396,101 @@ startBufferMonitoring() {
|
|
|
1409
1396
|
// Check initial caption state AFTER captions are loaded and menu is built
|
|
1410
1397
|
setTimeout(() => {
|
|
1411
1398
|
this.checkInitialCaptionState();
|
|
1412
|
-
}, 2500); //
|
|
1413
|
-
|
|
1399
|
+
}, 2500); // after 2.5s
|
|
1400
|
+
|
|
1401
|
+
// Initialize cursor state based on controls visibility
|
|
1402
|
+
if (!this.options.showYouTubeUI && this.api.player.options.hideCursor) {
|
|
1403
|
+
// Check if controls are visible
|
|
1404
|
+
const controlsVisible = this.api.controls && this.api.controls.classList.contains('show');
|
|
1405
|
+
if (!controlsVisible) {
|
|
1406
|
+
this.hideCursor();
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (this.api.player.options.debug) console.log('YT Plugin: Setup completed');
|
|
1414
1410
|
this.api.triggerEvent('youtubeplugin:playerready', {});
|
|
1415
1411
|
|
|
1416
1412
|
}
|
|
1417
1413
|
|
|
1414
|
+
showYouTubeControls() {
|
|
1415
|
+
console.log('[YT Plugin] 🔴 Showing YouTube controls');
|
|
1416
|
+
|
|
1417
|
+
// Find iframe
|
|
1418
|
+
let iframe = this.api.container.querySelector('iframe');
|
|
1419
|
+
if (!iframe) iframe = document.querySelector('iframe');
|
|
1420
|
+
|
|
1421
|
+
// Find video wrapper (contains all overlays and controls)
|
|
1422
|
+
const videoWrapper = this.api.container.querySelector('.video-wrapper');
|
|
1423
|
+
const controlbar = this.api.container.querySelector('.controls');
|
|
1424
|
+
|
|
1425
|
+
console.log('[YT Plugin] iframe:', iframe);
|
|
1426
|
+
console.log('[YT Plugin] videoWrapper:', videoWrapper);
|
|
1427
|
+
console.log('[YT Plugin] controlbar:', controlbar);
|
|
1428
|
+
|
|
1429
|
+
if (iframe) {
|
|
1430
|
+
console.log('[YT Plugin] ✅ Showing YouTube controls');
|
|
1431
|
+
|
|
1432
|
+
// Enable clicks on YouTube iframe
|
|
1433
|
+
iframe.style.pointerEvents = 'auto';
|
|
1434
|
+
iframe.style.zIndex = '9999'; // Bring to front
|
|
1435
|
+
|
|
1436
|
+
// Hide entire video wrapper (this hides ALL overlays and controls)
|
|
1437
|
+
if (videoWrapper) {
|
|
1438
|
+
// Save original position
|
|
1439
|
+
this.originalWrapperChildren = Array.from(videoWrapper.children).filter(child => child !== iframe);
|
|
1440
|
+
|
|
1441
|
+
// Hide all children except iframe
|
|
1442
|
+
this.originalWrapperChildren.forEach(child => {
|
|
1443
|
+
child.style.display = 'none';
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
console.log('[YT Plugin] Video wrapper children hidden');
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// OR hide controlbar directly
|
|
1450
|
+
if (controlbar) {
|
|
1451
|
+
controlbar.style.display = 'none';
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// Auto-restore after 10 seconds
|
|
1455
|
+
this.youtubeControlsTimeout = setTimeout(() => {
|
|
1456
|
+
console.log('[YT Plugin] ⏰ Restoring custom controls');
|
|
1457
|
+
this.hideYouTubeControls();
|
|
1458
|
+
}, 10000);
|
|
1459
|
+
} else {
|
|
1460
|
+
console.log('[YT Plugin] ❌ iframe not found');
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
hideYouTubeControls() {
|
|
1465
|
+
console.log('[YT Plugin] 🔙 Hiding YouTube controls');
|
|
1466
|
+
|
|
1467
|
+
const iframe = this.api.container.querySelector('iframe');
|
|
1468
|
+
const controlbar = this.api.container.querySelector('.controls');
|
|
1469
|
+
|
|
1470
|
+
if (iframe) {
|
|
1471
|
+
// Disable clicks on YouTube controls
|
|
1472
|
+
if (!this.options.showYouTubeUI) {
|
|
1473
|
+
iframe.style.pointerEvents = 'none';
|
|
1474
|
+
}
|
|
1475
|
+
iframe.style.zIndex = ''; // Reset z-index
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// Restore video wrapper children
|
|
1479
|
+
if (this.originalWrapperChildren) {
|
|
1480
|
+
this.originalWrapperChildren.forEach(child => {
|
|
1481
|
+
child.style.display = '';
|
|
1482
|
+
});
|
|
1483
|
+
this.originalWrapperChildren = null;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Restore controlbar
|
|
1487
|
+
if (controlbar) {
|
|
1488
|
+
controlbar.style.display = '';
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
console.log('[YT Plugin] ✅ Custom controls restored');
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1418
1494
|
forceHideCustomControls() {
|
|
1419
1495
|
const existingStyle = document.getElementById('yt-force-hide-controls');
|
|
1420
1496
|
if (existingStyle) {
|
|
@@ -1965,7 +2041,7 @@ startBufferMonitoring() {
|
|
|
1965
2041
|
visibility: visible !important;
|
|
1966
2042
|
opacity: 1 !important;
|
|
1967
2043
|
}
|
|
1968
|
-
|
|
2044
|
+
|
|
1969
2045
|
/* Make watermark circular */
|
|
1970
2046
|
.video-wrapper .watermark,
|
|
1971
2047
|
.video-wrapper .watermark-image,
|
|
@@ -1973,12 +2049,113 @@ startBufferMonitoring() {
|
|
|
1973
2049
|
border-radius: 50% !important;
|
|
1974
2050
|
overflow: hidden !important;
|
|
1975
2051
|
}
|
|
2052
|
+
|
|
2053
|
+
/* Hide cursor */
|
|
2054
|
+
.video-wrapper.hide-cursor,
|
|
2055
|
+
.video-wrapper.hide-cursor * {
|
|
2056
|
+
cursor: none !important;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
/* Ensure cursor is visible on controls */
|
|
2060
|
+
.video-wrapper.hide-cursor .controls,
|
|
2061
|
+
.video-wrapper.hide-cursor .controls * {
|
|
2062
|
+
cursor: default !important;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
/* Ensure cursor is visible on buttons */
|
|
2066
|
+
.video-wrapper.hide-cursor .control-btn,
|
|
2067
|
+
.video-wrapper.hide-cursor .control-btn * {
|
|
2068
|
+
cursor: pointer !important;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
/* Ensure iframe doesn't override */
|
|
2072
|
+
.video-wrapper.hide-cursor iframe {
|
|
2073
|
+
cursor: auto !important;
|
|
2074
|
+
}
|
|
1976
2075
|
`;
|
|
2076
|
+
|
|
1977
2077
|
document.head.appendChild(style);
|
|
1978
2078
|
this.api.container.classList.add('youtube-active');
|
|
1979
2079
|
|
|
1980
2080
|
if (this.api.player.options.debug) {
|
|
1981
|
-
console.log('
|
|
2081
|
+
console.log('YT Plugin: CSS override injected');
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
/**
|
|
2086
|
+
* Inject CSS styles for controlbar and title overlay gradients
|
|
2087
|
+
* Uses YouTube-specific selectors to avoid conflicts with other plugins
|
|
2088
|
+
*/
|
|
2089
|
+
injectControlbarGradientStyles() {
|
|
2090
|
+
// Check if styles are already injected
|
|
2091
|
+
if (document.getElementById('yt-controlbar-gradient-styles')) {
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// Validate opacity values (must be between 0 and 1)
|
|
2096
|
+
const controlBarOpacity = Math.max(0, Math.min(1, this.options.controlBarOpacity));
|
|
2097
|
+
const titleOverlayOpacity = Math.max(0, Math.min(1, this.options.titleOverlayOpacity));
|
|
2098
|
+
|
|
2099
|
+
// Create style element
|
|
2100
|
+
const style = document.createElement('style');
|
|
2101
|
+
style.id = 'yt-controlbar-gradient-styles';
|
|
2102
|
+
|
|
2103
|
+
// CSS with YouTube-specific selectors to avoid conflicts
|
|
2104
|
+
style.textContent = `
|
|
2105
|
+
/* Controlbar gradient - dark opaque at bottom, semi-transparent at top */
|
|
2106
|
+
/* ONLY applied when YouTube plugin is active */
|
|
2107
|
+
.video-wrapper.youtube-active .controls {
|
|
2108
|
+
background: linear-gradient(
|
|
2109
|
+
to top,
|
|
2110
|
+
rgba(0, 0, 0, ${controlBarOpacity}) 0%, /* Maximum opacity at bottom */
|
|
2111
|
+
rgba(0, 0, 0, ${controlBarOpacity * 0.89}) 20%, /* 89% of max opacity */
|
|
2112
|
+
rgba(0, 0, 0, ${controlBarOpacity * 0.74}) 40%, /* 74% */
|
|
2113
|
+
rgba(0, 0, 0, ${controlBarOpacity * 0.53}) 60%, /* 53% */
|
|
2114
|
+
rgba(0, 0, 0, ${controlBarOpacity * 0.32}) 80%, /* 32% */
|
|
2115
|
+
rgba(0, 0, 0, ${controlBarOpacity * 0.21}) 100% /* 21% at top */
|
|
2116
|
+
) !important;
|
|
2117
|
+
backdrop-filter: blur(3px);
|
|
2118
|
+
min-height: 60px;
|
|
2119
|
+
padding-bottom: 10px;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
/* Title overlay gradient - dark opaque at top, semi-transparent at bottom */
|
|
2123
|
+
/* ONLY applied when YouTube plugin is active */
|
|
2124
|
+
.video-wrapper.youtube-active .title-overlay {
|
|
2125
|
+
background: linear-gradient(
|
|
2126
|
+
to bottom,
|
|
2127
|
+
rgba(0, 0, 0, ${titleOverlayOpacity}) 0%, /* Maximum opacity at top */
|
|
2128
|
+
rgba(0, 0, 0, ${titleOverlayOpacity * 0.89}) 20%, /* 89% of max opacity */
|
|
2129
|
+
rgba(0, 0, 0, ${titleOverlayOpacity * 0.74}) 40%, /* 74% */
|
|
2130
|
+
rgba(0, 0, 0, ${titleOverlayOpacity * 0.53}) 60%, /* 53% */
|
|
2131
|
+
rgba(0, 0, 0, ${titleOverlayOpacity * 0.32}) 80%, /* 32% */
|
|
2132
|
+
rgba(0, 0, 0, ${titleOverlayOpacity * 0.21}) 100% /* 21% at bottom */
|
|
2133
|
+
) !important;
|
|
2134
|
+
backdrop-filter: blur(3px);
|
|
2135
|
+
min-height: 80px;
|
|
2136
|
+
padding-top: 20px;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
/* Keep controlbar visible when video is paused */
|
|
2140
|
+
.video-wrapper.youtube-active.video-paused .controls.show {
|
|
2141
|
+
opacity: 1 !important;
|
|
2142
|
+
visibility: visible !important;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
/* Keep title overlay visible when video is paused */
|
|
2146
|
+
.video-wrapper.youtube-active.video-paused .title-overlay.show {
|
|
2147
|
+
opacity: 1 !important;
|
|
2148
|
+
visibility: visible !important;
|
|
2149
|
+
}
|
|
2150
|
+
`;
|
|
2151
|
+
|
|
2152
|
+
// Append style to document head
|
|
2153
|
+
document.head.appendChild(style);
|
|
2154
|
+
|
|
2155
|
+
// Debug logging
|
|
2156
|
+
if (this.api.player.options.debug) {
|
|
2157
|
+
console.log('[YT Plugin] Controlbar and title overlay gradient styles injected');
|
|
2158
|
+
console.log(`[YT Plugin] ControlBar opacity: ${controlBarOpacity}, TitleOverlay opacity: ${titleOverlayOpacity}`);
|
|
1982
2159
|
}
|
|
1983
2160
|
}
|
|
1984
2161
|
|
|
@@ -2066,9 +2243,10 @@ startBufferMonitoring() {
|
|
|
2066
2243
|
return;
|
|
2067
2244
|
}
|
|
2068
2245
|
|
|
2246
|
+
const qualityText = this.api.player.t('video_quality');
|
|
2069
2247
|
const qualityHTML = `
|
|
2070
2248
|
<div class="quality-control">
|
|
2071
|
-
<button class="control-btn quality-btn"
|
|
2249
|
+
<button class="control-btn quality-btn" title="${qualityText}">
|
|
2072
2250
|
<div class="quality-btn-text">
|
|
2073
2251
|
<div class="selected-quality">Auto</div>
|
|
2074
2252
|
<div class="current-quality"></div>
|
|
@@ -2126,23 +2304,178 @@ startBufferMonitoring() {
|
|
|
2126
2304
|
});
|
|
2127
2305
|
qualityMenu.appendChild(autoItem);
|
|
2128
2306
|
|
|
2129
|
-
// Add quality options
|
|
2307
|
+
// Add quality options (all disabled)
|
|
2130
2308
|
this.availableQualities.forEach(quality => {
|
|
2131
2309
|
const menuItem = document.createElement('div');
|
|
2132
|
-
menuItem.className = 'quality-option';
|
|
2310
|
+
menuItem.className = 'quality-option disabled';
|
|
2133
2311
|
menuItem.textContent = quality.label;
|
|
2134
2312
|
menuItem.dataset.quality = quality.value;
|
|
2135
|
-
menuItem.
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
this.updateQualityButtonDisplay(quality.label, '');
|
|
2139
|
-
});
|
|
2313
|
+
menuItem.style.opacity = '0.5';
|
|
2314
|
+
menuItem.style.cursor = 'not-allowed';
|
|
2315
|
+
menuItem.style.pointerEvents = 'none';
|
|
2140
2316
|
qualityMenu.appendChild(menuItem);
|
|
2141
2317
|
});
|
|
2142
2318
|
|
|
2319
|
+
// Add separator
|
|
2320
|
+
const separator = document.createElement('div');
|
|
2321
|
+
separator.style.cssText = 'height: 1px; background: rgba(255,255,255,0.1); margin: 8px 0;';
|
|
2322
|
+
qualityMenu.appendChild(separator);
|
|
2323
|
+
|
|
2324
|
+
// Add YouTube native controls button with icon
|
|
2325
|
+
const ytBtn = document.createElement('div');
|
|
2326
|
+
ytBtn.className = 'quality-option youtube-controls-option';
|
|
2327
|
+
ytBtn.style.cssText = 'color: #ff0000; font-weight: 500; cursor: pointer; display: flex; align-items: center; gap: 8px;';
|
|
2328
|
+
|
|
2329
|
+
// Add YouTube icon
|
|
2330
|
+
ytBtn.innerHTML = `
|
|
2331
|
+
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18" style="flex-shrink: 0;">
|
|
2332
|
+
<path d="M21.582,6.186c-0.23-0.86-0.908-1.538-1.768-1.768C18.254,4,12,4,12,4S5.746,4,4.186,4.418 c-0.86,0.23-1.538,0.908-1.768,1.768C2,7.746,2,12,2,12s0,4.254,0.418,5.814c0.23,0.86,0.908,1.538,1.768,1.768 C5.746,20,12,20,12,20s6.254,0,7.814-0.418c0.861-0.23,1.538-0.908,1.768-1.768C22,16.254,22,12,22,12S22,7.746,21.582,6.186z M10,15.464V8.536L16,12L10,15.464z"/>
|
|
2333
|
+
</svg>
|
|
2334
|
+
<span>Show Native Controls</span>
|
|
2335
|
+
`;
|
|
2336
|
+
|
|
2337
|
+
ytBtn.addEventListener('click', () => {
|
|
2338
|
+
this.showYouTubeControls();
|
|
2339
|
+
qualityMenu.classList.remove('show');
|
|
2340
|
+
});
|
|
2341
|
+
qualityMenu.appendChild(ytBtn);
|
|
2342
|
+
|
|
2143
2343
|
if (this.api.player.options.debug) console.log('[YT Plugin] Quality menu updated');
|
|
2144
2344
|
}
|
|
2145
2345
|
|
|
2346
|
+
showYouTubeControls() {
|
|
2347
|
+
if (this.api.player.options.debug) {
|
|
2348
|
+
console.log('[YT Plugin] 🔴 Showing YouTube controls');
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
let iframe = this.api.container.querySelector('iframe');
|
|
2352
|
+
if (!iframe) iframe = document.querySelector('iframe');
|
|
2353
|
+
|
|
2354
|
+
const controlbar = this.api.container.querySelector('.controls');
|
|
2355
|
+
const titleOverlay = this.api.container.querySelector('.title-overlay');
|
|
2356
|
+
|
|
2357
|
+
if (iframe) {
|
|
2358
|
+
if (this.api.player.options.debug) {
|
|
2359
|
+
console.log('[YT Plugin] ✅ Showing YouTube controls');
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
// Bring iframe to front
|
|
2363
|
+
iframe.style.pointerEvents = 'auto';
|
|
2364
|
+
iframe.style.zIndex = '9999';
|
|
2365
|
+
|
|
2366
|
+
// Hide controlbar and title
|
|
2367
|
+
if (controlbar) {
|
|
2368
|
+
controlbar.style.display = 'none';
|
|
2369
|
+
}
|
|
2370
|
+
if (titleOverlay) {
|
|
2371
|
+
titleOverlay.style.display = 'none';
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
// Auto-restore after 10 seconds
|
|
2375
|
+
this.youtubeControlsTimeout = setTimeout(() => {
|
|
2376
|
+
if (this.api.player.options.debug) {
|
|
2377
|
+
console.log('[YT Plugin] ⏰ Restoring custom controls after 10 seconds');
|
|
2378
|
+
}
|
|
2379
|
+
this.hideYouTubeControls();
|
|
2380
|
+
}, 10000);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
hideYouTubeControls() {
|
|
2385
|
+
if (this.api.player.options.debug) {
|
|
2386
|
+
console.log('[YT Plugin] 🔙 Hiding YouTube controls');
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// Clear timeout if exists
|
|
2390
|
+
if (this.youtubeControlsTimeout) {
|
|
2391
|
+
clearTimeout(this.youtubeControlsTimeout);
|
|
2392
|
+
this.youtubeControlsTimeout = null;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
const iframe = this.api.container.querySelector('iframe');
|
|
2396
|
+
const controlbar = this.api.container.querySelector('.controls');
|
|
2397
|
+
const titleOverlay = this.api.container.querySelector('.title-overlay');
|
|
2398
|
+
|
|
2399
|
+
if (iframe) {
|
|
2400
|
+
// Disable clicks on YouTube controls
|
|
2401
|
+
if (!this.options.showYouTubeUI) {
|
|
2402
|
+
iframe.style.pointerEvents = 'none';
|
|
2403
|
+
}
|
|
2404
|
+
// Keep iframe at low z-index to prevent poster
|
|
2405
|
+
iframe.style.zIndex = '1';
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
// Restore controlbar and title
|
|
2409
|
+
if (controlbar) {
|
|
2410
|
+
controlbar.style.display = '';
|
|
2411
|
+
controlbar.style.zIndex = '10'; // Above iframe
|
|
2412
|
+
}
|
|
2413
|
+
if (titleOverlay) {
|
|
2414
|
+
titleOverlay.style.display = '';
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
if (this.api.player.options.debug) {
|
|
2418
|
+
console.log('[YT Plugin] ✅ Custom controls restored');
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
createYouTubeControlsButton() {
|
|
2423
|
+
// Check if button is enabled
|
|
2424
|
+
if (!this.options.showNativeControlsButton) {
|
|
2425
|
+
if (this.api.player.options.debug) {
|
|
2426
|
+
console.log('[YT Plugin] Native controls button disabled by option');
|
|
2427
|
+
}
|
|
2428
|
+
return;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// Check if button already exists
|
|
2432
|
+
if (this.api.container.querySelector('.youtube-controls-btn')) {
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
const controlsRight = this.api.container.querySelector('.controls-right');
|
|
2437
|
+
if (!controlsRight) return;
|
|
2438
|
+
|
|
2439
|
+
const buttonHTML = `
|
|
2440
|
+
<button class="control-btn youtube-controls-btn" title="Show YouTube Controls">
|
|
2441
|
+
<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
|
|
2442
|
+
<path d="M21.582,6.186c-0.23-0.86-0.908-1.538-1.768-1.768C18.254,4,12,4,12,4S5.746,4,4.186,4.418 c-0.86,0.23-1.538,0.908-1.768,1.768C2,7.746,2,12,2,12s0,4.254,0.418,5.814c0.23,0.86,0.908,1.538,1.768,1.768 C5.746,20,12,20,12,20s6.254,0,7.814-0.418c0.861-0.23,1.538-0.908,1.768-1.768C22,16.254,22,12,22,12S22,7.746,21.582,6.186z M10,15.464V8.536L16,12L10,15.464z"/>
|
|
2443
|
+
</svg>
|
|
2444
|
+
</button>
|
|
2445
|
+
`;
|
|
2446
|
+
|
|
2447
|
+
// Insert before quality control
|
|
2448
|
+
const qualityControl = controlsRight.querySelector('.quality-control');
|
|
2449
|
+
if (qualityControl) {
|
|
2450
|
+
qualityControl.insertAdjacentHTML('beforebegin', buttonHTML);
|
|
2451
|
+
} else {
|
|
2452
|
+
const fullscreenBtn = controlsRight.querySelector('.fullscreen-btn');
|
|
2453
|
+
if (fullscreenBtn) {
|
|
2454
|
+
fullscreenBtn.insertAdjacentHTML('beforebegin', buttonHTML);
|
|
2455
|
+
} else {
|
|
2456
|
+
controlsRight.insertAdjacentHTML('beforeend', buttonHTML);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
// Add click listener
|
|
2461
|
+
const btn = this.api.container.querySelector('.youtube-controls-btn');
|
|
2462
|
+
if (btn) {
|
|
2463
|
+
btn.addEventListener('click', () => {
|
|
2464
|
+
if (this.api.player.options.debug) {
|
|
2465
|
+
console.log('[YT Plugin] YouTube controls button clicked');
|
|
2466
|
+
}
|
|
2467
|
+
this.showYouTubeControls();
|
|
2468
|
+
});
|
|
2469
|
+
|
|
2470
|
+
// Add custom styling to make it red like YouTube
|
|
2471
|
+
btn.style.color = '#ff0000';
|
|
2472
|
+
|
|
2473
|
+
if (this.api.player.options.debug) {
|
|
2474
|
+
console.log('[YT Plugin] YouTube controls button created');
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2146
2479
|
updateQualityMenuActiveState(qualityValue) {
|
|
2147
2480
|
const qualityMenu = this.api.container.querySelector('.quality-menu');
|
|
2148
2481
|
if (!qualityMenu) return;
|
|
@@ -2204,72 +2537,202 @@ startBufferMonitoring() {
|
|
|
2204
2537
|
}
|
|
2205
2538
|
|
|
2206
2539
|
setQuality(quality) {
|
|
2207
|
-
|
|
2540
|
+
console.log('[YT Plugin] 🎯 CSS Trick - Setting quality to:', quality);
|
|
2208
2541
|
|
|
2209
|
-
|
|
2210
|
-
// Track user's quality choice for display
|
|
2211
|
-
this.userQualityChoice = quality;
|
|
2212
|
-
if (this.api.player.options.debug) {
|
|
2213
|
-
console.log('[YT Plugin] Setting quality to:', quality);
|
|
2214
|
-
console.log('[YT Plugin] Current quality:', this.ytPlayer.getPlaybackQuality());
|
|
2215
|
-
console.log('[YT Plugin] Available qualities:', this.ytPlayer.getAvailableQualityLevels());
|
|
2216
|
-
}
|
|
2542
|
+
if (!this.ytPlayer) return;
|
|
2217
2543
|
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2544
|
+
this.userQualityChoice = quality;
|
|
2545
|
+
this.currentQuality = quality;
|
|
2546
|
+
|
|
2547
|
+
const iframe = this.ytPlayerContainer.querySelector('iframe');
|
|
2548
|
+
if (!iframe) return;
|
|
2549
|
+
|
|
2550
|
+
// Map quality to iframe size that forces that quality
|
|
2551
|
+
const qualityToSize = {
|
|
2552
|
+
'tiny': { width: 200, height: 113 }, // 144p
|
|
2553
|
+
'small': { width: 320, height: 180 }, // 240p
|
|
2554
|
+
'medium': { width: 480, height: 270 }, // 360p
|
|
2555
|
+
'large': { width: 640, height: 360 }, // 480p
|
|
2556
|
+
'hd720': { width: 1280, height: 720 }, // 720p
|
|
2557
|
+
'hd1080': { width: 1920, height: 1080 }, // 1080p
|
|
2558
|
+
'hd1440': { width: 2560, height: 1440 }, // 1440p
|
|
2559
|
+
'hd2160': { width: 3840, height: 2160 }, // 4K
|
|
2560
|
+
'default': { width: 1920, height: 1080 } // Auto = 1080p max
|
|
2561
|
+
};
|
|
2225
2562
|
|
|
2226
|
-
|
|
2227
|
-
this.currentQuality = quality;
|
|
2563
|
+
const targetSize = qualityToSize[quality] || qualityToSize['hd720'];
|
|
2228
2564
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2565
|
+
// Get actual container size
|
|
2566
|
+
const container = this.ytPlayerContainer;
|
|
2567
|
+
const containerWidth = container.offsetWidth;
|
|
2568
|
+
const containerHeight = container.offsetHeight;
|
|
2231
2569
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2570
|
+
console.log('[YT Plugin] 📐 Container:', containerWidth, 'x', containerHeight);
|
|
2571
|
+
console.log('[YT Plugin] 🎯 Target iframe:', targetSize.width, 'x', targetSize.height);
|
|
2572
|
+
|
|
2573
|
+
// Calculate scale to fit target size into container
|
|
2574
|
+
const scaleX = containerWidth / targetSize.width;
|
|
2575
|
+
const scaleY = containerHeight / targetSize.height;
|
|
2576
|
+
const scale = Math.max(scaleX, scaleY); // Use larger scale to cover container
|
|
2577
|
+
|
|
2578
|
+
console.log('[YT Plugin] 📏 Scale:', scale);
|
|
2579
|
+
|
|
2580
|
+
// Set iframe to target size
|
|
2581
|
+
iframe.style.width = targetSize.width + 'px';
|
|
2582
|
+
iframe.style.height = targetSize.height + 'px';
|
|
2236
2583
|
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2584
|
+
// Scale up with CSS transform to fill container
|
|
2585
|
+
iframe.style.transform = `scale(${scale})`;
|
|
2586
|
+
iframe.style.transformOrigin = 'top left';
|
|
2240
2587
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2588
|
+
// Reload video with new size
|
|
2589
|
+
const currentTime = this.ytPlayer.getCurrentTime();
|
|
2590
|
+
const wasPlaying = this.ytPlayer.getPlayerState() === YT.PlayerState.PLAYING;
|
|
2591
|
+
|
|
2592
|
+
this.ytPlayer.loadVideoById({
|
|
2593
|
+
videoId: this.videoId,
|
|
2594
|
+
startSeconds: currentTime,
|
|
2595
|
+
suggestedQuality: quality
|
|
2596
|
+
});
|
|
2597
|
+
|
|
2598
|
+
if (wasPlaying) {
|
|
2599
|
+
setTimeout(() => {
|
|
2600
|
+
this.ytPlayer.playVideo();
|
|
2601
|
+
console.log('[YT Plugin] ▶️ Auto-play');
|
|
2602
|
+
}, 800);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
// Verify after 2 seconds
|
|
2606
|
+
setTimeout(() => {
|
|
2607
|
+
if (!this.ytPlayer) return;
|
|
2608
|
+
const actualQuality = this.ytPlayer.getPlaybackQuality();
|
|
2609
|
+
console.log('[YT Plugin] 🎬 Quality AFTER trick:', actualQuality);
|
|
2610
|
+
|
|
2611
|
+
if (actualQuality === quality || quality === 'default') {
|
|
2612
|
+
console.log('[YT Plugin] ✅ CSS TRICK WORKED!');
|
|
2244
2613
|
} else {
|
|
2245
|
-
|
|
2246
|
-
this.updateQualityButtonDisplay('Auto', '');
|
|
2614
|
+
console.log('[YT Plugin] ⚠️ Partially worked - got:', actualQuality);
|
|
2247
2615
|
}
|
|
2616
|
+
}, 2000);
|
|
2248
2617
|
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2618
|
+
this.updateQualityMenuActiveState(quality);
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
startBufferMonitoring() {
|
|
2622
|
+
if (this.bufferMonitorInterval) {
|
|
2623
|
+
clearInterval(this.bufferMonitorInterval);
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
let consecutiveBuffers = 0;
|
|
2627
|
+
let bufferingStartTime = null;
|
|
2628
|
+
let lastBufferTime = 0;
|
|
2629
|
+
let lastQualityDowngrade = 0;
|
|
2630
|
+
|
|
2631
|
+
this.bufferMonitorInterval = setInterval(() => {
|
|
2632
|
+
if (!this.ytPlayer) return;
|
|
2633
|
+
|
|
2634
|
+
try {
|
|
2635
|
+
const state = this.ytPlayer.getPlayerState();
|
|
2636
|
+
const currentTime = Date.now();
|
|
2637
|
+
|
|
2638
|
+
// Detect buffering
|
|
2639
|
+
if (state === YT.PlayerState.BUFFERING) {
|
|
2640
|
+
if (bufferingStartTime === null) {
|
|
2641
|
+
bufferingStartTime = currentTime;
|
|
2642
|
+
|
|
2643
|
+
// Count consecutive buffers (within 45 seconds)
|
|
2644
|
+
if (currentTime - lastBufferTime < 45000) {
|
|
2645
|
+
consecutiveBuffers++;
|
|
2646
|
+
} else {
|
|
2647
|
+
consecutiveBuffers = 1;
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
lastBufferTime = currentTime;
|
|
2651
|
+
|
|
2652
|
+
if (this.api.player.options.debug) {
|
|
2653
|
+
console.log('[YT Plugin] 🔴 Buffer #' + consecutiveBuffers);
|
|
2260
2654
|
}
|
|
2261
2655
|
}
|
|
2262
|
-
}, 1000); // Check every 1 second for faster updates
|
|
2263
|
-
}
|
|
2264
2656
|
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2657
|
+
const bufferingDuration = currentTime - bufferingStartTime;
|
|
2658
|
+
const timeSinceLastDowngrade = currentTime - lastQualityDowngrade;
|
|
2659
|
+
|
|
2660
|
+
// Only intervene if:
|
|
2661
|
+
// - 4+ buffers in 45 seconds, OR
|
|
2662
|
+
// - Single buffer lasts 3+ seconds
|
|
2663
|
+
// - AND at least 10 seconds since last downgrade (prevent loops)
|
|
2664
|
+
if ((consecutiveBuffers >= 4 || bufferingDuration > 3000) && timeSinceLastDowngrade > 10000) {
|
|
2665
|
+
|
|
2666
|
+
// Only auto-reduce in AUTO mode
|
|
2667
|
+
if (this.userQualityChoice === 'auto' || this.userQualityChoice === 'default') {
|
|
2668
|
+
const currentQuality = this.ytPlayer.getPlaybackQuality();
|
|
2669
|
+
const availableQualities = this.ytPlayer.getAvailableQualityLevels();
|
|
2670
|
+
|
|
2671
|
+
if (this.api.player.options.debug) {
|
|
2672
|
+
console.log('[YT Plugin] 🔻 Excessive buffering - attempting quality reduction');
|
|
2673
|
+
console.log('[YT Plugin] Current:', currentQuality);
|
|
2674
|
+
console.log('[YT Plugin] Available:', availableQualities);
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
// Quality hierarchy (highest to lowest)
|
|
2678
|
+
const qualityLevels = ['highres', 'hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny'];
|
|
2679
|
+
const currentIndex = qualityLevels.indexOf(currentQuality);
|
|
2680
|
+
|
|
2681
|
+
// Go down 2 levels for noticeable improvement
|
|
2682
|
+
if (currentIndex !== -1 && currentIndex < qualityLevels.length - 2) {
|
|
2683
|
+
const lowerQuality = qualityLevels[currentIndex + 2];
|
|
2684
|
+
|
|
2685
|
+
if (availableQualities.includes(lowerQuality)) {
|
|
2686
|
+
if (this.api.player.options.debug) {
|
|
2687
|
+
console.log('[YT Plugin] 📉 Downgrading to:', lowerQuality);
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
// Set quality WITHOUT interrupting playback
|
|
2691
|
+
this.ytPlayer.setPlaybackQuality(lowerQuality);
|
|
2692
|
+
|
|
2693
|
+
// Update tracking
|
|
2694
|
+
lastQualityDowngrade = currentTime;
|
|
2695
|
+
consecutiveBuffers = 0;
|
|
2696
|
+
bufferingStartTime = null;
|
|
2697
|
+
|
|
2698
|
+
// Update UI
|
|
2699
|
+
setTimeout(() => {
|
|
2700
|
+
const actualQuality = this.ytPlayer.getPlaybackQuality();
|
|
2701
|
+
const qualityLabel = this.getQualityLabel(actualQuality);
|
|
2702
|
+
this.updateQualityButtonDisplay('Auto', qualityLabel);
|
|
2703
|
+
}, 1000);
|
|
2704
|
+
}
|
|
2705
|
+
} else {
|
|
2706
|
+
if (this.api.player.options.debug) {
|
|
2707
|
+
console.log('[YT Plugin] ⚠️ Already at lowest quality');
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
} else if (state === YT.PlayerState.PLAYING) {
|
|
2714
|
+
// Reset buffering timer
|
|
2715
|
+
bufferingStartTime = null;
|
|
2716
|
+
|
|
2717
|
+
// Gradually reduce counter during smooth playback (every 15 seconds)
|
|
2718
|
+
if (consecutiveBuffers > 0) {
|
|
2719
|
+
setTimeout(() => {
|
|
2720
|
+
if (this.ytPlayer && this.ytPlayer.getPlayerState() === YT.PlayerState.PLAYING) {
|
|
2721
|
+
consecutiveBuffers = Math.max(0, consecutiveBuffers - 1);
|
|
2722
|
+
if (this.api.player.options.debug) {
|
|
2723
|
+
console.log('[YT Plugin] ✅ Smooth playback - buffer count reduced to:', consecutiveBuffers);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}, 15000);
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
} catch (error) {
|
|
2731
|
+
if (this.api.player.options.debug) {
|
|
2732
|
+
console.error('[YT Plugin] Error in buffer monitoring:', error);
|
|
2733
|
+
}
|
|
2270
2734
|
}
|
|
2271
|
-
|
|
2272
|
-
}
|
|
2735
|
+
}, 500); // Check every 500ms
|
|
2273
2736
|
}
|
|
2274
2737
|
|
|
2275
2738
|
// ===== SUBTITLE METHODS =====
|
|
@@ -3068,6 +3531,20 @@ startBufferMonitoring() {
|
|
|
3068
3531
|
}
|
|
3069
3532
|
break;
|
|
3070
3533
|
}
|
|
3534
|
+
|
|
3535
|
+
if (event.data === YT.PlayerState.PAUSED) {
|
|
3536
|
+
// add pause class
|
|
3537
|
+
if (this.api.container) {
|
|
3538
|
+
this.api.container.classList.add('video-paused');
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
if (event.data === YT.PlayerState.PLAYING) {
|
|
3543
|
+
// remove pause class
|
|
3544
|
+
if (this.api.container) {
|
|
3545
|
+
this.api.container.classList.remove('video-paused');
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3071
3548
|
}
|
|
3072
3549
|
|
|
3073
3550
|
onPlaybackQualityChange(event) {
|
|
@@ -3086,7 +3563,7 @@ startBufferMonitoring() {
|
|
|
3086
3563
|
console.error('[YT Plugin] Player error:', errorCode);
|
|
3087
3564
|
}
|
|
3088
3565
|
|
|
3089
|
-
// Error codes
|
|
3566
|
+
// Error codes that indicate video is unavailable
|
|
3090
3567
|
const unavailableErrors = [
|
|
3091
3568
|
2, // Invalid video ID
|
|
3092
3569
|
5, // HTML5 player error
|
|
@@ -3131,7 +3608,7 @@ startBufferMonitoring() {
|
|
|
3131
3608
|
}
|
|
3132
3609
|
};
|
|
3133
3610
|
|
|
3134
|
-
// Override pause method
|
|
3611
|
+
// Override pause method
|
|
3135
3612
|
const originalPause = this.player.pause;
|
|
3136
3613
|
this.player.pause = () => {
|
|
3137
3614
|
if (this.ytPlayer && this.ytPlayer.pauseVideo) {
|
|
@@ -3203,15 +3680,13 @@ startBufferMonitoring() {
|
|
|
3203
3680
|
// Override mute toggle
|
|
3204
3681
|
const originalToggleMute = this.player.toggleMute;
|
|
3205
3682
|
this.player.toggleMute = () => {
|
|
3206
|
-
if (this.ytPlayer && this.ytPlayer.isMuted
|
|
3683
|
+
if (this.ytPlayer && this.ytPlayer.isMuted) {
|
|
3207
3684
|
const isMuted = this.ytPlayer.isMuted();
|
|
3208
|
-
|
|
3209
3685
|
if (isMuted) {
|
|
3210
3686
|
this.ytPlayer.unMute();
|
|
3211
3687
|
} else {
|
|
3212
3688
|
this.ytPlayer.mute();
|
|
3213
3689
|
}
|
|
3214
|
-
|
|
3215
3690
|
this.updateMuteButtonState(!isMuted);
|
|
3216
3691
|
|
|
3217
3692
|
if (!isMuted) {
|
|
@@ -3225,97 +3700,6 @@ startBufferMonitoring() {
|
|
|
3225
3700
|
}
|
|
3226
3701
|
};
|
|
3227
3702
|
|
|
3228
|
-
// Volume tooltip events for YouTube
|
|
3229
|
-
if (this.api.player.volumeSlider) {
|
|
3230
|
-
const volumeSlider = this.api.player.volumeSlider;
|
|
3231
|
-
const volumeContainer = this.api.container.querySelector('.volume-container');
|
|
3232
|
-
|
|
3233
|
-
// Remove existing listeners to avoid duplicates
|
|
3234
|
-
const newVolumeSlider = volumeSlider.cloneNode(true);
|
|
3235
|
-
volumeSlider.parentNode.replaceChild(newVolumeSlider, volumeSlider);
|
|
3236
|
-
this.api.player.volumeSlider = newVolumeSlider;
|
|
3237
|
-
|
|
3238
|
-
// Update tooltip on input (slider drag)
|
|
3239
|
-
newVolumeSlider.addEventListener('input', (e) => {
|
|
3240
|
-
const value = parseFloat(e.target.value);
|
|
3241
|
-
this.player.updateVolume(value);
|
|
3242
|
-
|
|
3243
|
-
// Update tooltip position and text during drag
|
|
3244
|
-
if (this.api.player.updateVolumeTooltipPosition) {
|
|
3245
|
-
this.api.player.updateVolumeTooltipPosition(value / 100);
|
|
3246
|
-
}
|
|
3247
|
-
if (this.api.player.updateVolumeTooltip) {
|
|
3248
|
-
this.api.player.updateVolumeTooltip();
|
|
3249
|
-
}
|
|
3250
|
-
});
|
|
3251
|
-
|
|
3252
|
-
// Update tooltip position on mousemove over slider
|
|
3253
|
-
newVolumeSlider.addEventListener('mousemove', (e) => {
|
|
3254
|
-
const rect = newVolumeSlider.getBoundingClientRect();
|
|
3255
|
-
const mouseX = e.clientX - rect.left;
|
|
3256
|
-
const percentage = Math.max(0, Math.min(1, mouseX / rect.width));
|
|
3257
|
-
|
|
3258
|
-
// Update tooltip position as mouse moves
|
|
3259
|
-
if (this.api.player.updateVolumeTooltipPosition) {
|
|
3260
|
-
this.api.player.updateVolumeTooltipPosition(percentage);
|
|
3261
|
-
}
|
|
3262
|
-
|
|
3263
|
-
// Update tooltip text to show value under mouse
|
|
3264
|
-
const volumeTooltip = this.api.container.querySelector('.volume-tooltip');
|
|
3265
|
-
if (volumeTooltip) {
|
|
3266
|
-
volumeTooltip.textContent = Math.round(percentage * 100) + '%';
|
|
3267
|
-
}
|
|
3268
|
-
});
|
|
3269
|
-
|
|
3270
|
-
// Show/hide tooltip on hover
|
|
3271
|
-
if (volumeContainer) {
|
|
3272
|
-
volumeContainer.addEventListener('mouseenter', () => {
|
|
3273
|
-
const volumeTooltip = this.api.container.querySelector('.volume-tooltip');
|
|
3274
|
-
if (volumeTooltip) {
|
|
3275
|
-
volumeTooltip.classList.add('visible');
|
|
3276
|
-
}
|
|
3277
|
-
});
|
|
3278
|
-
|
|
3279
|
-
volumeContainer.addEventListener('mouseleave', () => {
|
|
3280
|
-
const volumeTooltip = this.api.container.querySelector('.volume-tooltip');
|
|
3281
|
-
if (volumeTooltip) {
|
|
3282
|
-
volumeTooltip.classList.remove('visible');
|
|
3283
|
-
}
|
|
3284
|
-
});
|
|
3285
|
-
}
|
|
3286
|
-
|
|
3287
|
-
if (this.api.player.options.debug) {
|
|
3288
|
-
console.log('[YT Plugin] Volume tooltip events bound');
|
|
3289
|
-
}
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
|
-
// Override playback speed
|
|
3293
|
-
const originalChangeSpeed = this.player.changeSpeed;
|
|
3294
|
-
if (originalChangeSpeed) {
|
|
3295
|
-
this.player.changeSpeed = (e) => {
|
|
3296
|
-
if (!e.target.classList.contains('speed-option')) return;
|
|
3297
|
-
|
|
3298
|
-
const speed = parseFloat(e.target.getAttribute('data-speed'));
|
|
3299
|
-
|
|
3300
|
-
if (this.ytPlayer && this.ytPlayer.setPlaybackRate && speed > 0) {
|
|
3301
|
-
this.ytPlayer.setPlaybackRate(speed);
|
|
3302
|
-
|
|
3303
|
-
const speedBtn = this.api.container.querySelector('.speed-btn');
|
|
3304
|
-
if (speedBtn) speedBtn.textContent = `${speed}x`;
|
|
3305
|
-
|
|
3306
|
-
const speedMenu = this.api.container.querySelector('.speed-menu');
|
|
3307
|
-
if (speedMenu) {
|
|
3308
|
-
speedMenu.querySelectorAll('.speed-option').forEach(option => {
|
|
3309
|
-
option.classList.remove('active');
|
|
3310
|
-
});
|
|
3311
|
-
e.target.classList.add('active');
|
|
3312
|
-
}
|
|
3313
|
-
} else {
|
|
3314
|
-
originalChangeSpeed.call(this.player, e);
|
|
3315
|
-
}
|
|
3316
|
-
};
|
|
3317
|
-
}
|
|
3318
|
-
|
|
3319
3703
|
// Override progress bar seeking
|
|
3320
3704
|
if (this.api.player.progressContainer) {
|
|
3321
3705
|
const progressContainer = this.api.player.progressContainer;
|
|
@@ -3330,7 +3714,22 @@ startBufferMonitoring() {
|
|
|
3330
3714
|
// Create tooltip for seek preview
|
|
3331
3715
|
const seekTooltip = document.createElement('div');
|
|
3332
3716
|
seekTooltip.className = 'yt-seek-tooltip';
|
|
3333
|
-
seekTooltip.style.cssText =
|
|
3717
|
+
seekTooltip.style.cssText = `
|
|
3718
|
+
position: absolute;
|
|
3719
|
+
bottom: calc(100% + 10px);
|
|
3720
|
+
left: 0;
|
|
3721
|
+
background: rgba(28,28,28,0.95);
|
|
3722
|
+
color: #fff;
|
|
3723
|
+
padding: 6px 10px;
|
|
3724
|
+
border-radius: 3px;
|
|
3725
|
+
font-size: 13px;
|
|
3726
|
+
font-weight: 500;
|
|
3727
|
+
white-space: nowrap;
|
|
3728
|
+
pointer-events: none;
|
|
3729
|
+
visibility: hidden;
|
|
3730
|
+
z-index: 99999;
|
|
3731
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
3732
|
+
`;
|
|
3334
3733
|
newProgressContainer.appendChild(seekTooltip);
|
|
3335
3734
|
|
|
3336
3735
|
// Format time function for tooltip
|
|
@@ -3348,25 +3747,32 @@ startBufferMonitoring() {
|
|
|
3348
3747
|
let isSeeking = false;
|
|
3349
3748
|
|
|
3350
3749
|
const handleSeek = (e) => {
|
|
3351
|
-
if (!this.ytPlayer || !this.ytPlayer.getDuration) return;
|
|
3750
|
+
if (!this.ytPlayer || !this.ytPlayer.getDuration()) return;
|
|
3352
3751
|
|
|
3353
3752
|
const rect = newProgressContainer.getBoundingClientRect();
|
|
3354
|
-
|
|
3753
|
+
|
|
3754
|
+
// Support both mouse and touch events
|
|
3755
|
+
const clientX = e.clientX !== undefined ? e.clientX :
|
|
3756
|
+
(e.touches && e.touches[0] ? e.touches[0].clientX :
|
|
3757
|
+
(e.changedTouches && e.changedTouches[0] ? e.changedTouches[0].clientX : 0));
|
|
3758
|
+
|
|
3759
|
+
const clickX = clientX - rect.left;
|
|
3355
3760
|
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
3356
3761
|
const duration = this.ytPlayer.getDuration();
|
|
3357
3762
|
const targetTime = percentage * duration;
|
|
3358
3763
|
|
|
3359
3764
|
this.ytPlayer.seekTo(targetTime, true);
|
|
3360
3765
|
|
|
3361
|
-
const progress = percentage * 100
|
|
3766
|
+
const progress = percentage * 100;
|
|
3362
3767
|
if (this.api.player.progressFilled) {
|
|
3363
|
-
this.api.player.progressFilled.style.width = progress
|
|
3768
|
+
this.api.player.progressFilled.style.width = `${progress}%`;
|
|
3364
3769
|
}
|
|
3365
3770
|
if (this.api.player.progressHandle) {
|
|
3366
|
-
this.api.player.progressHandle.style.left = progress
|
|
3771
|
+
this.api.player.progressHandle.style.left = `${progress}%`;
|
|
3367
3772
|
}
|
|
3368
3773
|
};
|
|
3369
3774
|
|
|
3775
|
+
// MOUSE EVENTS
|
|
3370
3776
|
newProgressContainer.addEventListener('mousedown', (e) => {
|
|
3371
3777
|
isSeeking = true;
|
|
3372
3778
|
handleSeek(e);
|
|
@@ -3378,7 +3784,7 @@ startBufferMonitoring() {
|
|
|
3378
3784
|
}
|
|
3379
3785
|
|
|
3380
3786
|
// Show tooltip with timestamp
|
|
3381
|
-
if (!isSeeking && this.ytPlayer && this.ytPlayer.getDuration) {
|
|
3787
|
+
if (!isSeeking && this.ytPlayer && this.ytPlayer.getDuration()) {
|
|
3382
3788
|
const rect = newProgressContainer.getBoundingClientRect();
|
|
3383
3789
|
const mouseX = e.clientX - rect.left;
|
|
3384
3790
|
const percentage = Math.max(0, Math.min(1, mouseX / rect.width));
|
|
@@ -3386,7 +3792,7 @@ startBufferMonitoring() {
|
|
|
3386
3792
|
const time = percentage * duration;
|
|
3387
3793
|
|
|
3388
3794
|
seekTooltip.textContent = formatTimeForTooltip(time);
|
|
3389
|
-
seekTooltip.style.left = mouseX
|
|
3795
|
+
seekTooltip.style.left = `${mouseX}px`;
|
|
3390
3796
|
seekTooltip.style.visibility = 'visible';
|
|
3391
3797
|
}
|
|
3392
3798
|
});
|
|
@@ -3399,10 +3805,50 @@ startBufferMonitoring() {
|
|
|
3399
3805
|
isSeeking = false;
|
|
3400
3806
|
});
|
|
3401
3807
|
|
|
3808
|
+
// TOUCH EVENTS - AGGIUNTI QUI!
|
|
3809
|
+
newProgressContainer.addEventListener('touchstart', (e) => {
|
|
3810
|
+
e.preventDefault(); // scroll prevention during drag
|
|
3811
|
+
isSeeking = true;
|
|
3812
|
+
handleSeek(e);
|
|
3813
|
+
}, { passive: false });
|
|
3814
|
+
|
|
3815
|
+
newProgressContainer.addEventListener('touchmove', (e) => {
|
|
3816
|
+
e.preventDefault(); // scroll prevention during drag
|
|
3817
|
+
|
|
3818
|
+
if (isSeeking) {
|
|
3819
|
+
handleSeek(e);
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3822
|
+
// Show tooltip with timestamp during touch
|
|
3823
|
+
if (!isSeeking && this.ytPlayer && this.ytPlayer.getDuration()) {
|
|
3824
|
+
const rect = newProgressContainer.getBoundingClientRect();
|
|
3825
|
+
const touch = e.touches[0];
|
|
3826
|
+
const touchX = touch.clientX - rect.left;
|
|
3827
|
+
const percentage = Math.max(0, Math.min(1, touchX / rect.width));
|
|
3828
|
+
const duration = this.ytPlayer.getDuration();
|
|
3829
|
+
const time = percentage * duration;
|
|
3830
|
+
|
|
3831
|
+
seekTooltip.textContent = formatTimeForTooltip(time);
|
|
3832
|
+
seekTooltip.style.left = `${touchX}px`;
|
|
3833
|
+
seekTooltip.style.visibility = 'visible';
|
|
3834
|
+
}
|
|
3835
|
+
}, { passive: false });
|
|
3836
|
+
|
|
3837
|
+
newProgressContainer.addEventListener('touchend', () => {
|
|
3838
|
+
isSeeking = false;
|
|
3839
|
+
seekTooltip.style.visibility = 'hidden';
|
|
3840
|
+
});
|
|
3841
|
+
|
|
3842
|
+
newProgressContainer.addEventListener('touchcancel', () => {
|
|
3843
|
+
isSeeking = false;
|
|
3844
|
+
seekTooltip.style.visibility = 'hidden';
|
|
3845
|
+
});
|
|
3846
|
+
|
|
3847
|
+
// CLICK EVENT
|
|
3402
3848
|
newProgressContainer.addEventListener('click', handleSeek);
|
|
3403
|
-
}
|
|
3404
3849
|
|
|
3405
|
-
|
|
3850
|
+
this.bindVolumeSlider();
|
|
3851
|
+
}
|
|
3406
3852
|
|
|
3407
3853
|
// Time update interval
|
|
3408
3854
|
if (this.timeUpdateInterval) {
|
|
@@ -3423,14 +3869,13 @@ startBufferMonitoring() {
|
|
|
3423
3869
|
} else {
|
|
3424
3870
|
// For regular videos, calculate normally
|
|
3425
3871
|
progress = (currentTime / duration) * 100;
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
// Check if live badge exists = it's a live stream
|
|
3429
|
-
const liveBadge = this.api.container.querySelector('.live-badge');
|
|
3430
3872
|
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3873
|
+
// Check if live badge exists = it's a live stream
|
|
3874
|
+
const liveBadge = this.api.container.querySelector('.live-badge');
|
|
3875
|
+
if (liveBadge) {
|
|
3876
|
+
// Force 100% for live streams
|
|
3877
|
+
progress = 100;
|
|
3878
|
+
}
|
|
3434
3879
|
}
|
|
3435
3880
|
|
|
3436
3881
|
this.api.player.progressFilled.style.width = `${progress}%`;
|
|
@@ -3442,10 +3887,82 @@ startBufferMonitoring() {
|
|
|
3442
3887
|
const currentTimeEl = this.api.container.querySelector('.current-time');
|
|
3443
3888
|
const durationEl = this.api.container.querySelector('.duration');
|
|
3444
3889
|
|
|
3445
|
-
if (currentTimeEl)
|
|
3446
|
-
|
|
3890
|
+
if (currentTimeEl) {
|
|
3891
|
+
currentTimeEl.textContent = this.formatTime(currentTime);
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
if (durationEl && duration) {
|
|
3895
|
+
durationEl.textContent = this.formatTime(duration);
|
|
3896
|
+
}
|
|
3447
3897
|
}
|
|
3448
3898
|
}, 250);
|
|
3899
|
+
|
|
3900
|
+
// **Cursor sync interval - only if the option is true**
|
|
3901
|
+
if (!this.options.showYouTubeUI && this.api.player.options.hideCursor) {
|
|
3902
|
+
if (this.cursorSyncInterval) {
|
|
3903
|
+
clearInterval(this.cursorSyncInterval);
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
this.cursorSyncInterval = setInterval(() => {
|
|
3907
|
+
if (this.api.controls) {
|
|
3908
|
+
const controlsVisible = this.api.controls.classList.contains('show');
|
|
3909
|
+
|
|
3910
|
+
if (controlsVisible) {
|
|
3911
|
+
// Controls are visible, show cursor
|
|
3912
|
+
this.showCursor();
|
|
3913
|
+
} else {
|
|
3914
|
+
// Controls are hidden, hide cursor
|
|
3915
|
+
this.hideCursor();
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
}, 100); // Check every 100ms
|
|
3919
|
+
|
|
3920
|
+
if (this.api.player.options.debug) {
|
|
3921
|
+
console.log('[YT Plugin] Cursor sync enabled');
|
|
3922
|
+
}
|
|
3923
|
+
} else {
|
|
3924
|
+
// if cursor sync was previously enabled, clear it
|
|
3925
|
+
if (this.cursorSyncInterval) {
|
|
3926
|
+
clearInterval(this.cursorSyncInterval);
|
|
3927
|
+
this.cursorSyncInterval = null;
|
|
3928
|
+
this.showCursor(); // Assicurati che il cursore sia visibile
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
/**
|
|
3934
|
+
* Hide mouse cursor in YouTube player
|
|
3935
|
+
* Only works when showYouTubeUI is false (custom controls)
|
|
3936
|
+
*/
|
|
3937
|
+
hideCursor() {
|
|
3938
|
+
// Don't hide cursor if YouTube native UI is active
|
|
3939
|
+
if (this.options.showYouTubeUI) {
|
|
3940
|
+
return;
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
// Add hide-cursor class to MAIN PLAYER CONTAINER
|
|
3944
|
+
// This ensures cursor is hidden everywhere in the player
|
|
3945
|
+
if (this.api.container) {
|
|
3946
|
+
this.api.container.classList.add('hide-cursor');
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
if (this.api.player.options.debug) {
|
|
3950
|
+
console.log('[YT Plugin] Cursor hidden on main container');
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3954
|
+
/**
|
|
3955
|
+
* Show mouse cursor in YouTube player
|
|
3956
|
+
*/
|
|
3957
|
+
showCursor() {
|
|
3958
|
+
// Remove hide-cursor class from MAIN PLAYER CONTAINER
|
|
3959
|
+
if (this.api.container) {
|
|
3960
|
+
this.api.container.classList.remove('hide-cursor');
|
|
3961
|
+
}
|
|
3962
|
+
|
|
3963
|
+
if (this.api.player.options.debug) {
|
|
3964
|
+
console.log('[YT Plugin] Cursor shown on main container');
|
|
3965
|
+
}
|
|
3449
3966
|
}
|
|
3450
3967
|
|
|
3451
3968
|
bindVolumeSlider() {
|
|
@@ -3576,6 +4093,18 @@ startBufferMonitoring() {
|
|
|
3576
4093
|
dispose() {
|
|
3577
4094
|
if (this.api.player.options.debug) console.log('[YT Plugin] Disposing');
|
|
3578
4095
|
|
|
4096
|
+
// Stop cursor sync interval
|
|
4097
|
+
if (this.cursorSyncInterval) {
|
|
4098
|
+
clearInterval(this.cursorSyncInterval);
|
|
4099
|
+
this.cursorSyncInterval = null;
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// Remove cursor hide styles
|
|
4103
|
+
const cursorStyleEl = document.getElementById('youtube-cursor-hide-styles');
|
|
4104
|
+
if (cursorStyleEl) {
|
|
4105
|
+
cursorStyleEl.remove();
|
|
4106
|
+
}
|
|
4107
|
+
|
|
3579
4108
|
// Cleanup timeout
|
|
3580
4109
|
if (this.playAttemptTimeout) {
|
|
3581
4110
|
clearTimeout(this.playAttemptTimeout);
|