myetv-player 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/myetv-player.css +317 -4
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +263 -85
- package/dist/myetv-player.min.js +214 -64
- package/package.json +3 -1
- package/plugins/iframe-ads/myetv-iframe-banner-ads.js +127 -16
package/dist/myetv-player.js
CHANGED
|
@@ -1253,13 +1253,34 @@ markPlayerReady() {
|
|
|
1253
1253
|
|
|
1254
1254
|
// Autoplay
|
|
1255
1255
|
if (this.options.autoplay) {
|
|
1256
|
-
if (this.options.debug) console.log('
|
|
1256
|
+
if (this.options.debug) console.log('Autoplay enabled');
|
|
1257
1257
|
setTimeout(() => {
|
|
1258
|
-
this.video.play()
|
|
1259
|
-
|
|
1260
|
-
|
|
1258
|
+
this.video.play()
|
|
1259
|
+
.then(() => {
|
|
1260
|
+
// Autoplay succeeded
|
|
1261
|
+
if (this.options.debug) console.log('Autoplay started successfully');
|
|
1262
|
+
})
|
|
1263
|
+
.catch(error => {
|
|
1264
|
+
// Autoplay blocked by browser
|
|
1265
|
+
if (this.options.debug) console.warn('Autoplay blocked', error);
|
|
1266
|
+
|
|
1267
|
+
// Start auto-hide timer even if autoplay is blocked
|
|
1268
|
+
// This ensures controls hide after delay when video is paused
|
|
1269
|
+
if (this.options.autoHide && this.autoHideInitialized) {
|
|
1270
|
+
// Show controls initially (user needs to see play button)
|
|
1271
|
+
this.showControlsNow();
|
|
1272
|
+
|
|
1273
|
+
// Start timer to hide controls
|
|
1274
|
+
this.resetAutoHideTimer();
|
|
1275
|
+
|
|
1276
|
+
if (this.options.debug) {
|
|
1277
|
+
console.log('Auto-hide timer started (autoplay blocked - video paused)');
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1261
1281
|
}, 100);
|
|
1262
1282
|
}
|
|
1283
|
+
|
|
1263
1284
|
}, 200);
|
|
1264
1285
|
|
|
1265
1286
|
}, 100);
|
|
@@ -1287,12 +1308,13 @@ createPlayerStructure() {
|
|
|
1287
1308
|
if (this.options.showTitleOverlay) {
|
|
1288
1309
|
this.createTitleOverlay();
|
|
1289
1310
|
}
|
|
1311
|
+
this.createTopBar();
|
|
1290
1312
|
}
|
|
1291
1313
|
|
|
1292
1314
|
createInitialLoading() {
|
|
1293
1315
|
const initialLoader = document.createElement('div');
|
|
1294
1316
|
initialLoader.className = 'initial-loading';
|
|
1295
|
-
initialLoader.innerHTML = '<div class="loading-spinner"></div>';
|
|
1317
|
+
initialLoader.innerHTML = '<div class="loading-spinner"></div><div class="loading-text"></div>';
|
|
1296
1318
|
this.container.appendChild(initialLoader);
|
|
1297
1319
|
this.initialLoading = initialLoader;
|
|
1298
1320
|
}
|
|
@@ -1305,7 +1327,7 @@ createLoadingOverlay() {
|
|
|
1305
1327
|
const overlay = document.createElement('div');
|
|
1306
1328
|
overlay.className = 'loading-overlay';
|
|
1307
1329
|
overlay.id = 'loadingOverlay-' + this.getUniqueId();
|
|
1308
|
-
overlay.innerHTML = '<div class="loading-spinner"></div>';
|
|
1330
|
+
overlay.innerHTML = '<div class="loading-spinner"></div><div class="loading-text"></div>';
|
|
1309
1331
|
this.container.appendChild(overlay);
|
|
1310
1332
|
this.loadingOverlay = overlay;
|
|
1311
1333
|
}
|
|
@@ -1345,17 +1367,27 @@ updateTooltips() {
|
|
|
1345
1367
|
if (!this.controls) return;
|
|
1346
1368
|
|
|
1347
1369
|
try {
|
|
1370
|
+
// Update tooltips in controls
|
|
1348
1371
|
this.controls.querySelectorAll('[data-tooltip]').forEach(element => {
|
|
1349
1372
|
const key = element.getAttribute('data-tooltip');
|
|
1350
1373
|
element.title = this.t(key);
|
|
1351
1374
|
});
|
|
1352
1375
|
|
|
1376
|
+
// Update tooltips in top bar
|
|
1377
|
+
if (this.topBar) {
|
|
1378
|
+
this.topBar.querySelectorAll('[data-tooltip]').forEach(element => {
|
|
1379
|
+
const key = element.getAttribute('data-tooltip');
|
|
1380
|
+
element.title = this.t(key);
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// Update "Auto" option in quality menu
|
|
1353
1385
|
const autoOption = this.controls.querySelector('.quality-option[data-quality="auto"]');
|
|
1354
1386
|
if (autoOption) {
|
|
1355
1387
|
autoOption.textContent = this.t('auto');
|
|
1356
1388
|
}
|
|
1357
1389
|
} catch (error) {
|
|
1358
|
-
if (this.options.debug) console.warn('
|
|
1390
|
+
if (this.options.debug) console.warn('Tooltip update error', error);
|
|
1359
1391
|
}
|
|
1360
1392
|
}
|
|
1361
1393
|
|
|
@@ -1501,6 +1533,10 @@ initializeElements() {
|
|
|
1501
1533
|
this.speedMenu = this.controls?.querySelector('.speed-menu');
|
|
1502
1534
|
this.qualityMenu = this.controls?.querySelector('.quality-menu');
|
|
1503
1535
|
this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
|
|
1536
|
+
|
|
1537
|
+
this.settingsBtn = this.container?.querySelector('.settings-btn');
|
|
1538
|
+
this.settingsMenu = this.container?.querySelector('.settings-menu');
|
|
1539
|
+
|
|
1504
1540
|
// Apply seek handle shape from options
|
|
1505
1541
|
if (this.progressHandle && this.options.seekHandleShape) {
|
|
1506
1542
|
this.setSeekHandleShape(this.options.seekHandleShape);
|
|
@@ -1581,6 +1617,84 @@ setupMenuToggles() {
|
|
|
1581
1617
|
}
|
|
1582
1618
|
}
|
|
1583
1619
|
|
|
1620
|
+
/**
|
|
1621
|
+
* Create integrated top bar with settings menu
|
|
1622
|
+
* Netflix/Prime Video style top bar with title and settings on opposite sides
|
|
1623
|
+
* This does NOT replace the existing title-overlay system
|
|
1624
|
+
* @returns {void}
|
|
1625
|
+
*/
|
|
1626
|
+
createTopBar() {
|
|
1627
|
+
if (!this.container) return;
|
|
1628
|
+
|
|
1629
|
+
// Create top bar element
|
|
1630
|
+
const topBar = document.createElement('div');
|
|
1631
|
+
topBar.className = 'player-top-bar';
|
|
1632
|
+
topBar.id = `topBar_${this.getUniqueId()}`;
|
|
1633
|
+
|
|
1634
|
+
// Left section: Title (if enabled)
|
|
1635
|
+
if (this.options.showTitleOverlay && this.options.videoTitle) {
|
|
1636
|
+
const titleSection = document.createElement('div');
|
|
1637
|
+
titleSection.className = 'top-bar-title';
|
|
1638
|
+
|
|
1639
|
+
// Main title
|
|
1640
|
+
const titleElement = document.createElement('h3');
|
|
1641
|
+
titleElement.className = 'video-title';
|
|
1642
|
+
titleElement.textContent = this.decodeHTMLEntities(this.options.videoTitle);
|
|
1643
|
+
titleSection.appendChild(titleElement);
|
|
1644
|
+
|
|
1645
|
+
// Optional subtitle
|
|
1646
|
+
if (this.options.videoSubtitle) {
|
|
1647
|
+
const subtitleElement = document.createElement('span');
|
|
1648
|
+
subtitleElement.className = 'video-subtitle';
|
|
1649
|
+
subtitleElement.textContent = this.decodeHTMLEntities(this.options.videoSubtitle);
|
|
1650
|
+
titleSection.appendChild(subtitleElement);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
topBar.appendChild(titleSection);
|
|
1654
|
+
} else {
|
|
1655
|
+
// Spacer if no title
|
|
1656
|
+
const spacer = document.createElement('div');
|
|
1657
|
+
spacer.className = 'top-bar-spacer';
|
|
1658
|
+
topBar.appendChild(spacer);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// Right section: Settings control
|
|
1662
|
+
const settingsControl = document.createElement('div');
|
|
1663
|
+
settingsControl.className = 'settings-control settings-top-bar';
|
|
1664
|
+
|
|
1665
|
+
// Settings button
|
|
1666
|
+
const settingsBtn = document.createElement('button');
|
|
1667
|
+
settingsBtn.className = 'control-btn settings-btn';
|
|
1668
|
+
settingsBtn.setAttribute('data-tooltip', 'settings_menu'); // ✅ Correct: underscore
|
|
1669
|
+
|
|
1670
|
+
const icon = document.createElement('span');
|
|
1671
|
+
icon.className = 'icon';
|
|
1672
|
+
icon.innerHTML = `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
|
|
1673
|
+
<path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/>
|
|
1674
|
+
</svg>`;
|
|
1675
|
+
settingsBtn.appendChild(icon);
|
|
1676
|
+
|
|
1677
|
+
// Settings menu
|
|
1678
|
+
const settingsMenu = document.createElement('div');
|
|
1679
|
+
settingsMenu.className = 'settings-menu';
|
|
1680
|
+
|
|
1681
|
+
settingsControl.appendChild(settingsBtn);
|
|
1682
|
+
settingsControl.appendChild(settingsMenu);
|
|
1683
|
+
topBar.appendChild(settingsControl);
|
|
1684
|
+
|
|
1685
|
+
// Insert top bar as first child in container
|
|
1686
|
+
this.container.insertBefore(topBar, this.container.firstChild);
|
|
1687
|
+
|
|
1688
|
+
// Save references
|
|
1689
|
+
this.topBar = topBar;
|
|
1690
|
+
this.topBarTitle = topBar.querySelector('.video-title');
|
|
1691
|
+
this.topBarSubtitle = topBar.querySelector('.video-subtitle');
|
|
1692
|
+
|
|
1693
|
+
if (this.options.debug) {
|
|
1694
|
+
console.log('✅ Top bar created with integrated settings');
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1584
1698
|
/**
|
|
1585
1699
|
* Restart video from beginning - Works with HTML5 video and ALL plugins
|
|
1586
1700
|
* @returns {this} Returns this for method chaining
|
|
@@ -1921,6 +2035,18 @@ updateProgress() {
|
|
|
1921
2035
|
}
|
|
1922
2036
|
}
|
|
1923
2037
|
|
|
2038
|
+
// Helper to update loading text in center overlays
|
|
2039
|
+
updateLoadingText(text) {
|
|
2040
|
+
if (this.initialLoading) {
|
|
2041
|
+
const textEl = this.initialLoading.querySelector('.loading-text');
|
|
2042
|
+
if (textEl) textEl.textContent = text;
|
|
2043
|
+
}
|
|
2044
|
+
if (this.loadingOverlay) {
|
|
2045
|
+
const textEl = this.loadingOverlay.querySelector('.loading-text');
|
|
2046
|
+
if (textEl) textEl.textContent = text;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1924
2050
|
updateBuffer() {
|
|
1925
2051
|
if (!this.video || !this.progressBuffer) return;
|
|
1926
2052
|
|
|
@@ -2153,7 +2279,7 @@ createBrandLogo() {
|
|
|
2153
2279
|
if (this.options.brandLogoLinkUrl) {
|
|
2154
2280
|
// Use custom tooltip text if provided, otherwise fallback to URL
|
|
2155
2281
|
logo.title = this.options.brandLogoTooltipText || this.options.brandLogoLinkUrl;
|
|
2156
|
-
//
|
|
2282
|
+
// do not use data-tooltip to not be overwritten
|
|
2157
2283
|
}
|
|
2158
2284
|
|
|
2159
2285
|
// Handle loading error
|
|
@@ -2848,6 +2974,13 @@ addEventListener(eventType, callback) {
|
|
|
2848
2974
|
this.pauseIcon.classList.remove('hidden');
|
|
2849
2975
|
}
|
|
2850
2976
|
|
|
2977
|
+
// Reset auto-hide timer when video starts playing
|
|
2978
|
+
if (this.options.autoHide && this.autoHideInitialized) {
|
|
2979
|
+
if (this.options.debug) console.log('Video playing - reset auto-hide timer');
|
|
2980
|
+
this.showControlsNow();
|
|
2981
|
+
this.resetAutoHideTimer();
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2851
2984
|
// Trigger playing event - video is now actually playing
|
|
2852
2985
|
this.triggerEvent('playing', {
|
|
2853
2986
|
currentTime: this.getCurrentTime(),
|
|
@@ -2855,6 +2988,20 @@ addEventListener(eventType, callback) {
|
|
|
2855
2988
|
});
|
|
2856
2989
|
});
|
|
2857
2990
|
|
|
2991
|
+
// Show controls and cancel timer when video is paused
|
|
2992
|
+
this.video.addEventListener('pause', () => {
|
|
2993
|
+
if (this.options.autoHide && this.autoHideInitialized) {
|
|
2994
|
+
if (this.options.debug) console.log('Video paused - show controls and cancel timer');
|
|
2995
|
+
this.showControlsNow();
|
|
2996
|
+
|
|
2997
|
+
// Cancel timer so controls stay visible while paused
|
|
2998
|
+
if (this.autoHideTimer) {
|
|
2999
|
+
clearTimeout(this.autoHideTimer);
|
|
3000
|
+
this.autoHideTimer = null;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
});
|
|
3004
|
+
|
|
2858
3005
|
this.video.addEventListener('waiting', () => {
|
|
2859
3006
|
if (!this.isChangingQuality) {
|
|
2860
3007
|
this.showLoading();
|
|
@@ -3347,31 +3494,36 @@ resetAutoHideTimer() {
|
|
|
3347
3494
|
this.autoHideTimer = null;
|
|
3348
3495
|
}
|
|
3349
3496
|
|
|
3350
|
-
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
3497
|
+
const isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
|
3498
|
+
|
|
3351
3499
|
if (this.mouseOverControls && !isTouchDevice) {
|
|
3352
|
-
if (this.autoHideDebug)
|
|
3353
|
-
if (this.options.debug) console.log('Not starting timer - mouse on controls');
|
|
3354
|
-
}
|
|
3500
|
+
if (this.autoHideDebug && this.options.debug) console.log('Not starting timer - mouse on controls');
|
|
3355
3501
|
return;
|
|
3356
3502
|
}
|
|
3357
3503
|
|
|
3504
|
+
// Only block timer if video is paused AND user explicitly paused it
|
|
3505
|
+
// Don't block if paused due to autoplay blocked (currentTime === 0 and no interaction yet)
|
|
3358
3506
|
if (this.video && this.video.paused) {
|
|
3359
|
-
if (
|
|
3360
|
-
|
|
3507
|
+
// Allow timer if this is initial pause (autoplay blocked scenario)
|
|
3508
|
+
const isInitialPause = this.video.currentTime === 0 && !this.video.ended;
|
|
3509
|
+
|
|
3510
|
+
if (!isInitialPause) {
|
|
3511
|
+
if (this.autoHideDebug && this.options.debug) console.log('Not starting timer - video paused by user');
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3515
|
+
if (this.autoHideDebug && this.options.debug) {
|
|
3516
|
+
console.log('Video paused but at start - allowing timer (autoplay blocked scenario)');
|
|
3361
3517
|
}
|
|
3362
|
-
return;
|
|
3363
3518
|
}
|
|
3364
3519
|
|
|
3520
|
+
// Start timer...
|
|
3365
3521
|
this.autoHideTimer = setTimeout(() => {
|
|
3366
|
-
if (this.autoHideDebug) {
|
|
3367
|
-
if (this.options.debug) console.log(`Timer expired after ${this.options.autoHideDelay}ms - nascondo controlli`);
|
|
3368
|
-
}
|
|
3522
|
+
if (this.autoHideDebug && this.options.debug) console.log(`Timer expired after ${this.options.autoHideDelay}ms - hiding controls`);
|
|
3369
3523
|
this.hideControlsNow();
|
|
3370
3524
|
}, this.options.autoHideDelay);
|
|
3371
3525
|
|
|
3372
|
-
if (this.autoHideDebug) {
|
|
3373
|
-
if (this.options.debug) console.log(`Auto-hide timer started: ${this.options.autoHideDelay}ms`);
|
|
3374
|
-
}
|
|
3526
|
+
if (this.autoHideDebug && this.options.debug) console.log(`Auto-hide timer started (${this.options.autoHideDelay}ms)`);
|
|
3375
3527
|
}
|
|
3376
3528
|
|
|
3377
3529
|
showControlsNow() {
|
|
@@ -3398,7 +3550,7 @@ showControlsNow() {
|
|
|
3398
3550
|
// *show cursor when controls are shown*
|
|
3399
3551
|
this.showCursor();
|
|
3400
3552
|
|
|
3401
|
-
if (this.autoHideDebug && this.options.debug) console.log('
|
|
3553
|
+
if (this.autoHideDebug && this.options.debug) console.log('Controls shown');
|
|
3402
3554
|
}
|
|
3403
3555
|
}
|
|
3404
3556
|
|
|
@@ -3439,7 +3591,7 @@ hideControlsNow() {
|
|
|
3439
3591
|
// *hide cursor after controls are hidden*
|
|
3440
3592
|
this.hideCursor();
|
|
3441
3593
|
|
|
3442
|
-
if (this.autoHideDebug && this.options.debug) console.log('
|
|
3594
|
+
if (this.autoHideDebug && this.options.debug) console.log('Controls hidden');
|
|
3443
3595
|
}
|
|
3444
3596
|
}
|
|
3445
3597
|
|
|
@@ -3687,10 +3839,9 @@ createControls() {
|
|
|
3687
3839
|
</div>
|
|
3688
3840
|
|
|
3689
3841
|
<div class="time-display">
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
</div>
|
|
3842
|
+
<span class="current-time">00:00</span>
|
|
3843
|
+
<span class="duration">00:00</span>
|
|
3844
|
+
</div>
|
|
3694
3845
|
</div>
|
|
3695
3846
|
|
|
3696
3847
|
<div class="controls-right">
|
|
@@ -3701,13 +3852,6 @@ createControls() {
|
|
|
3701
3852
|
<span class="icon"><svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor"><path d="M12.5 4v8l-7-4zm-8 0v8l7-4z"/></svg></span>
|
|
3702
3853
|
</button>
|
|
3703
3854
|
|
|
3704
|
-
<div class="settings-control">
|
|
3705
|
-
<button class="control-btn settings-btn" data-tooltip="settings_menu">
|
|
3706
|
-
<span class="icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg></span>
|
|
3707
|
-
</button>
|
|
3708
|
-
<div class="settings-menu"></div>
|
|
3709
|
-
</div>
|
|
3710
|
-
|
|
3711
3855
|
${(this.options.showQualitySelector && this.originalSources && this.originalSources.length > 1) || this.options.adaptiveQualityControl ? `
|
|
3712
3856
|
<div class="quality-control">
|
|
3713
3857
|
<button class="control-btn quality-btn" data-tooltip="video_quality">
|
|
@@ -3830,18 +3974,21 @@ checkScreenSize() {
|
|
|
3830
3974
|
|
|
3831
3975
|
/* Update settings menu visibility */
|
|
3832
3976
|
updateSettingsMenuVisibility() {
|
|
3833
|
-
|
|
3977
|
+
// SEARCH IN CONTAINER
|
|
3978
|
+
const settingsControl = this.container?.querySelector('.settings-control');
|
|
3979
|
+
|
|
3834
3980
|
if (!settingsControl) return;
|
|
3835
3981
|
|
|
3836
|
-
//
|
|
3982
|
+
// Always show settings
|
|
3837
3983
|
settingsControl.style.display = 'block';
|
|
3838
3984
|
|
|
3839
3985
|
// Populate settings menu
|
|
3840
3986
|
this.populateSettingsMenu();
|
|
3841
3987
|
|
|
3842
|
-
//
|
|
3988
|
+
// Hide speed and subtitles controls in bottom bar
|
|
3843
3989
|
const speedControl = this.controls.querySelector('.speed-control');
|
|
3844
3990
|
const subtitlesControl = this.controls.querySelector('.subtitles-control');
|
|
3991
|
+
|
|
3845
3992
|
if (speedControl) speedControl.style.display = 'none';
|
|
3846
3993
|
if (subtitlesControl) subtitlesControl.style.display = 'none';
|
|
3847
3994
|
}
|
|
@@ -3850,44 +3997,52 @@ updateSettingsMenuVisibility() {
|
|
|
3850
3997
|
* Populate settings menu with controls
|
|
3851
3998
|
*/
|
|
3852
3999
|
populateSettingsMenu() {
|
|
3853
|
-
|
|
4000
|
+
// SEARCH IN CONTAINER
|
|
4001
|
+
const settingsMenu = this.container?.querySelector('.settings-menu');
|
|
4002
|
+
|
|
3854
4003
|
if (!settingsMenu) return;
|
|
3855
4004
|
|
|
3856
4005
|
let menuHTML = '';
|
|
3857
4006
|
|
|
3858
4007
|
// SPEED - always included
|
|
3859
4008
|
if (this.options.showSpeedControl) {
|
|
3860
|
-
const speedLabel = this.t('playback_speed')
|
|
4009
|
+
const speedLabel = this.t('playback_speed');
|
|
3861
4010
|
const currentSpeed = this.video ? this.video.playbackRate : 1;
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
<
|
|
3866
|
-
|
|
3867
|
-
|
|
4011
|
+
|
|
4012
|
+
menuHTML += `
|
|
4013
|
+
<div class="settings-expandable-wrapper">
|
|
4014
|
+
<div class="settings-option expandable-trigger" data-action="speed_expand">
|
|
4015
|
+
<span class="settings-option-label">${speedLabel} <strong>${currentSpeed}x</strong></span>
|
|
4016
|
+
<span class="expand-arrow">▼</span>
|
|
4017
|
+
</div>
|
|
4018
|
+
<div class="settings-expandable-content" style="display: none;">`;
|
|
3868
4019
|
|
|
3869
4020
|
const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
|
|
3870
4021
|
speeds.forEach(speed => {
|
|
3871
4022
|
const isActive = Math.abs(speed - currentSpeed) < 0.01;
|
|
3872
4023
|
menuHTML += `<div class="settings-suboption ${isActive ? 'active' : ''}" data-speed="${speed}">${speed}x</div>`;
|
|
3873
4024
|
});
|
|
4025
|
+
|
|
3874
4026
|
menuHTML += `</div></div>`;
|
|
3875
4027
|
}
|
|
3876
4028
|
|
|
3877
4029
|
// SUBTITLES - always included
|
|
3878
4030
|
if (this.options.showSubtitles && this.textTracks && this.textTracks.length > 0) {
|
|
3879
|
-
const subtitlesLabel = this.t('subtitles')
|
|
4031
|
+
const subtitlesLabel = this.t('subtitles');
|
|
3880
4032
|
const currentTrack = this.currentSubtitleTrack;
|
|
3881
|
-
const currentLabel = this.subtitlesEnabled ?
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
4033
|
+
const currentLabel = this.subtitlesEnabled ?
|
|
4034
|
+
(currentTrack ? currentTrack.label : 'Unknown') :
|
|
4035
|
+
this.t('subtitles_off'); //
|
|
4036
|
+
|
|
4037
|
+
menuHTML += `
|
|
4038
|
+
<div class="settings-expandable-wrapper">
|
|
4039
|
+
<div class="settings-option expandable-trigger" data-action="subtitles_expand">
|
|
4040
|
+
<span class="settings-option-label">${subtitlesLabel} <strong>${currentLabel}</strong></span>
|
|
4041
|
+
<span class="expand-arrow">▼</span>
|
|
4042
|
+
</div>
|
|
4043
|
+
<div class="settings-expandable-content" style="display: none;">`;
|
|
3889
4044
|
|
|
3890
|
-
menuHTML += `<div class="settings-suboption ${!this.subtitlesEnabled ? 'active' : ''}" data-track="off">${this.t('
|
|
4045
|
+
menuHTML += `<div class="settings-suboption ${!this.subtitlesEnabled ? 'active' : ''}" data-track="off">${this.t('subtitles_off')}</div>`;
|
|
3891
4046
|
|
|
3892
4047
|
this.textTracks.forEach((trackData, index) => {
|
|
3893
4048
|
const isActive = this.currentSubtitleTrack === trackData.track;
|
|
@@ -3960,43 +4115,50 @@ addSettingsMenuScrollbar() {
|
|
|
3960
4115
|
* Bind settings menu events
|
|
3961
4116
|
*/
|
|
3962
4117
|
bindSettingsMenuEvents() {
|
|
3963
|
-
|
|
3964
|
-
const
|
|
4118
|
+
// Search in container instead of controls (for top bar)
|
|
4119
|
+
const settingsBtn = this.container?.querySelector('.settings-btn');
|
|
4120
|
+
const settingsMenu = this.container?.querySelector('.settings-menu');
|
|
4121
|
+
|
|
3965
4122
|
if (!settingsMenu || !settingsBtn) return;
|
|
3966
4123
|
|
|
3967
|
-
//
|
|
4124
|
+
// Toggle menu on button click
|
|
3968
4125
|
settingsBtn.addEventListener('click', (e) => {
|
|
3969
4126
|
e.stopPropagation();
|
|
3970
4127
|
settingsMenu.classList.toggle('active');
|
|
3971
4128
|
|
|
3972
|
-
//
|
|
4129
|
+
// When menu is opened, set max height and overflow
|
|
3973
4130
|
if (settingsMenu.classList.contains('active')) {
|
|
3974
|
-
const
|
|
3975
|
-
const containerRect = settingsMenu.parentElement.parentElement.getBoundingClientRect();
|
|
4131
|
+
const containerRect = this.container.getBoundingClientRect();
|
|
3976
4132
|
const btnRect = settingsBtn.getBoundingClientRect();
|
|
3977
|
-
|
|
3978
|
-
|
|
4133
|
+
|
|
4134
|
+
// Calculate available space below the button
|
|
4135
|
+
const spaceBelow = containerRect.bottom - btnRect.bottom - 30; // 30px margin
|
|
4136
|
+
|
|
4137
|
+
// Minimum 300px, maximum 600px or available space
|
|
4138
|
+
const maxMenuHeight = Math.max(300, Math.min(600, spaceBelow));
|
|
3979
4139
|
|
|
3980
4140
|
settingsMenu.style.maxHeight = `${maxMenuHeight}px`;
|
|
3981
4141
|
settingsMenu.style.overflowY = 'auto';
|
|
3982
4142
|
settingsMenu.style.overflowX = 'hidden';
|
|
4143
|
+
|
|
4144
|
+
if (this.options.debug) {
|
|
4145
|
+
console.log(`Settings menu opened: height=${maxMenuHeight}px (available=${spaceBelow}px)`);
|
|
4146
|
+
}
|
|
3983
4147
|
} else {
|
|
3984
|
-
|
|
3985
|
-
settingsMenu.style.
|
|
4148
|
+
// Reset when closing
|
|
4149
|
+
settingsMenu.style.maxHeight = '600px'; // Default max height
|
|
4150
|
+
settingsMenu.style.overflowY = 'auto';
|
|
3986
4151
|
}
|
|
3987
|
-
|
|
3988
4152
|
});
|
|
3989
4153
|
|
|
3990
|
-
//
|
|
4154
|
+
// Close menu when clicking outside
|
|
3991
4155
|
document.addEventListener('click', (e) => {
|
|
3992
4156
|
if (!settingsBtn?.contains(e.target) && !settingsMenu?.contains(e.target)) {
|
|
3993
4157
|
settingsMenu?.classList.remove('active');
|
|
3994
|
-
settingsMenu.style.maxHeight = 'none';
|
|
3995
|
-
settingsMenu.style.overflowY = 'visible';
|
|
3996
4158
|
}
|
|
3997
4159
|
});
|
|
3998
4160
|
|
|
3999
|
-
//
|
|
4161
|
+
// Manage clicks inside the menu
|
|
4000
4162
|
settingsMenu.addEventListener('click', (e) => {
|
|
4001
4163
|
e.stopPropagation();
|
|
4002
4164
|
|
|
@@ -4036,7 +4198,7 @@ bindSettingsMenuEvents() {
|
|
|
4036
4198
|
const trigger = wrapper.querySelector('.expandable-trigger');
|
|
4037
4199
|
const action = trigger.getAttribute('data-action');
|
|
4038
4200
|
|
|
4039
|
-
if (action === '
|
|
4201
|
+
if (action === 'speed_expand') {
|
|
4040
4202
|
const speed = parseFloat(e.target.getAttribute('data-speed'));
|
|
4041
4203
|
if (speed && speed > 0 && this.video && !this.isChangingQuality) {
|
|
4042
4204
|
this.video.playbackRate = speed;
|
|
@@ -4049,13 +4211,13 @@ bindSettingsMenuEvents() {
|
|
|
4049
4211
|
const label = trigger.querySelector('.settings-option-label');
|
|
4050
4212
|
if (label) {
|
|
4051
4213
|
const speedLabel = this.t('playback_speed') || 'Playback Speed';
|
|
4052
|
-
label.
|
|
4214
|
+
label.innerHTML = `${speedLabel} <strong>${speed}x</strong>`;
|
|
4053
4215
|
}
|
|
4054
4216
|
|
|
4055
4217
|
// Trigger event
|
|
4056
4218
|
this.triggerEvent('speedchange', { speed, previousSpeed: this.video.playbackRate });
|
|
4057
4219
|
}
|
|
4058
|
-
} else if (action === '
|
|
4220
|
+
} else if (action === 'subtitles_expand') {
|
|
4059
4221
|
const trackData = e.target.getAttribute('data-track');
|
|
4060
4222
|
if (trackData === 'off') {
|
|
4061
4223
|
this.disableSubtitles();
|
|
@@ -4072,7 +4234,7 @@ bindSettingsMenuEvents() {
|
|
|
4072
4234
|
const label = trigger.querySelector('.settings-option-label');
|
|
4073
4235
|
if (label) {
|
|
4074
4236
|
const subtitlesLabel = this.t('subtitles') || 'Subtitles';
|
|
4075
|
-
label.
|
|
4237
|
+
label.innerHTML = `${subtitlesLabel} <strong>${e.target.textContent}</strong>`;
|
|
4076
4238
|
}
|
|
4077
4239
|
}
|
|
4078
4240
|
}
|
|
@@ -8402,12 +8564,10 @@ skipTime(seconds) {
|
|
|
8402
8564
|
}
|
|
8403
8565
|
|
|
8404
8566
|
updateTimeDisplay() {
|
|
8405
|
-
// Update current time
|
|
8406
8567
|
if (this.currentTimeEl && this.video) {
|
|
8407
8568
|
this.currentTimeEl.textContent = this.formatTime(this.video.currentTime || 0);
|
|
8408
8569
|
}
|
|
8409
8570
|
|
|
8410
|
-
// Update duration or show appropriate message
|
|
8411
8571
|
if (this.durationEl && this.video) {
|
|
8412
8572
|
const duration = this.video.duration;
|
|
8413
8573
|
const readyState = this.video.readyState;
|
|
@@ -8415,28 +8575,46 @@ updateTimeDisplay() {
|
|
|
8415
8575
|
const networkState = this.video.networkState;
|
|
8416
8576
|
|
|
8417
8577
|
// Check for initial buffering state
|
|
8418
|
-
|
|
8419
|
-
// currentTime === 0 and duration === 0 indicates initial loading
|
|
8420
|
-
const isInitialBuffering = (readyState < 2 && currentTime === 0) ||
|
|
8421
|
-
(currentTime === 0 && (!duration || duration === 0) && networkState === 2);
|
|
8422
|
-
|
|
8578
|
+
const isInitialBuffering = (readyState < 2 && currentTime === 0) || (currentTime === 0 && !duration) || (duration === 0 && networkState === 2);
|
|
8423
8579
|
// Check if duration is invalid (NaN or Infinity)
|
|
8424
8580
|
const isDurationInvalid = !duration || isNaN(duration) || !isFinite(duration);
|
|
8425
8581
|
|
|
8582
|
+
// Text for translations
|
|
8583
|
+
const t = (key) => {
|
|
8584
|
+
if (this.isI18nAvailable()) {
|
|
8585
|
+
try {
|
|
8586
|
+
return VideoPlayerTranslations.t(key);
|
|
8587
|
+
} catch (error) {
|
|
8588
|
+
return key;
|
|
8589
|
+
}
|
|
8590
|
+
}
|
|
8591
|
+
const fallback = {
|
|
8592
|
+
'loading': 'Loading...'
|
|
8593
|
+
};
|
|
8594
|
+
return fallback[key] || key;
|
|
8595
|
+
};
|
|
8596
|
+
|
|
8426
8597
|
if (isInitialBuffering) {
|
|
8427
|
-
//
|
|
8428
|
-
this.
|
|
8598
|
+
// CHANGED: Move text to center overlay, clear control bar text
|
|
8599
|
+
this.updateLoadingText(t('loading'));
|
|
8600
|
+
this.durationEl.textContent = this.formatTime(0); // Just show 00:00 or empty
|
|
8429
8601
|
this.durationEl.classList.remove('encoding-state');
|
|
8430
8602
|
this.durationEl.classList.add('loading-state');
|
|
8431
8603
|
} else if (isDurationInvalid) {
|
|
8432
|
-
//
|
|
8433
|
-
this.
|
|
8604
|
+
// CHANGED: Move text to center overlay
|
|
8605
|
+
this.updateLoadingText(t('loading'));
|
|
8606
|
+
// Optional: you might want to keep encoding text in bar OR move it too.
|
|
8607
|
+
// If you want it ONLY in center:
|
|
8608
|
+
this.durationEl.textContent = "--:--";
|
|
8609
|
+
|
|
8434
8610
|
this.durationEl.classList.remove('loading-state');
|
|
8435
8611
|
this.durationEl.classList.add('encoding-state');
|
|
8436
8612
|
} else {
|
|
8437
8613
|
// Valid duration - show normal time
|
|
8438
8614
|
this.durationEl.textContent = this.formatTime(duration);
|
|
8439
8615
|
this.durationEl.classList.remove('encoding-state', 'loading-state');
|
|
8616
|
+
// Clear loading text when playing normally
|
|
8617
|
+
this.updateLoadingText('');
|
|
8440
8618
|
}
|
|
8441
8619
|
}
|
|
8442
8620
|
}
|