myetv-player 1.6.2 → 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 +303 -4
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +224 -75
- package/dist/myetv-player.min.js +180 -60
- package/package.json +2 -1
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,6 +1308,7 @@ createPlayerStructure() {
|
|
|
1287
1308
|
if (this.options.showTitleOverlay) {
|
|
1288
1309
|
this.createTitleOverlay();
|
|
1289
1310
|
}
|
|
1311
|
+
this.createTopBar();
|
|
1290
1312
|
}
|
|
1291
1313
|
|
|
1292
1314
|
createInitialLoading() {
|
|
@@ -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
|
|
@@ -2165,7 +2279,7 @@ createBrandLogo() {
|
|
|
2165
2279
|
if (this.options.brandLogoLinkUrl) {
|
|
2166
2280
|
// Use custom tooltip text if provided, otherwise fallback to URL
|
|
2167
2281
|
logo.title = this.options.brandLogoTooltipText || this.options.brandLogoLinkUrl;
|
|
2168
|
-
//
|
|
2282
|
+
// do not use data-tooltip to not be overwritten
|
|
2169
2283
|
}
|
|
2170
2284
|
|
|
2171
2285
|
// Handle loading error
|
|
@@ -2860,6 +2974,13 @@ addEventListener(eventType, callback) {
|
|
|
2860
2974
|
this.pauseIcon.classList.remove('hidden');
|
|
2861
2975
|
}
|
|
2862
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
|
+
|
|
2863
2984
|
// Trigger playing event - video is now actually playing
|
|
2864
2985
|
this.triggerEvent('playing', {
|
|
2865
2986
|
currentTime: this.getCurrentTime(),
|
|
@@ -2867,6 +2988,20 @@ addEventListener(eventType, callback) {
|
|
|
2867
2988
|
});
|
|
2868
2989
|
});
|
|
2869
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
|
+
|
|
2870
3005
|
this.video.addEventListener('waiting', () => {
|
|
2871
3006
|
if (!this.isChangingQuality) {
|
|
2872
3007
|
this.showLoading();
|
|
@@ -3359,31 +3494,36 @@ resetAutoHideTimer() {
|
|
|
3359
3494
|
this.autoHideTimer = null;
|
|
3360
3495
|
}
|
|
3361
3496
|
|
|
3362
|
-
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
|
3497
|
+
const isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
|
3498
|
+
|
|
3363
3499
|
if (this.mouseOverControls && !isTouchDevice) {
|
|
3364
|
-
if (this.autoHideDebug)
|
|
3365
|
-
if (this.options.debug) console.log('Not starting timer - mouse on controls');
|
|
3366
|
-
}
|
|
3500
|
+
if (this.autoHideDebug && this.options.debug) console.log('Not starting timer - mouse on controls');
|
|
3367
3501
|
return;
|
|
3368
3502
|
}
|
|
3369
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)
|
|
3370
3506
|
if (this.video && this.video.paused) {
|
|
3371
|
-
if (
|
|
3372
|
-
|
|
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)');
|
|
3373
3517
|
}
|
|
3374
|
-
return;
|
|
3375
3518
|
}
|
|
3376
3519
|
|
|
3520
|
+
// Start timer...
|
|
3377
3521
|
this.autoHideTimer = setTimeout(() => {
|
|
3378
|
-
if (this.autoHideDebug) {
|
|
3379
|
-
if (this.options.debug) console.log(`Timer expired after ${this.options.autoHideDelay}ms - nascondo controlli`);
|
|
3380
|
-
}
|
|
3522
|
+
if (this.autoHideDebug && this.options.debug) console.log(`Timer expired after ${this.options.autoHideDelay}ms - hiding controls`);
|
|
3381
3523
|
this.hideControlsNow();
|
|
3382
3524
|
}, this.options.autoHideDelay);
|
|
3383
3525
|
|
|
3384
|
-
if (this.autoHideDebug) {
|
|
3385
|
-
if (this.options.debug) console.log(`Auto-hide timer started: ${this.options.autoHideDelay}ms`);
|
|
3386
|
-
}
|
|
3526
|
+
if (this.autoHideDebug && this.options.debug) console.log(`Auto-hide timer started (${this.options.autoHideDelay}ms)`);
|
|
3387
3527
|
}
|
|
3388
3528
|
|
|
3389
3529
|
showControlsNow() {
|
|
@@ -3410,7 +3550,7 @@ showControlsNow() {
|
|
|
3410
3550
|
// *show cursor when controls are shown*
|
|
3411
3551
|
this.showCursor();
|
|
3412
3552
|
|
|
3413
|
-
if (this.autoHideDebug && this.options.debug) console.log('
|
|
3553
|
+
if (this.autoHideDebug && this.options.debug) console.log('Controls shown');
|
|
3414
3554
|
}
|
|
3415
3555
|
}
|
|
3416
3556
|
|
|
@@ -3451,7 +3591,7 @@ hideControlsNow() {
|
|
|
3451
3591
|
// *hide cursor after controls are hidden*
|
|
3452
3592
|
this.hideCursor();
|
|
3453
3593
|
|
|
3454
|
-
if (this.autoHideDebug && this.options.debug) console.log('
|
|
3594
|
+
if (this.autoHideDebug && this.options.debug) console.log('Controls hidden');
|
|
3455
3595
|
}
|
|
3456
3596
|
}
|
|
3457
3597
|
|
|
@@ -3699,10 +3839,9 @@ createControls() {
|
|
|
3699
3839
|
</div>
|
|
3700
3840
|
|
|
3701
3841
|
<div class="time-display">
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
</div>
|
|
3842
|
+
<span class="current-time">00:00</span>
|
|
3843
|
+
<span class="duration">00:00</span>
|
|
3844
|
+
</div>
|
|
3706
3845
|
</div>
|
|
3707
3846
|
|
|
3708
3847
|
<div class="controls-right">
|
|
@@ -3713,13 +3852,6 @@ createControls() {
|
|
|
3713
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>
|
|
3714
3853
|
</button>
|
|
3715
3854
|
|
|
3716
|
-
<div class="settings-control">
|
|
3717
|
-
<button class="control-btn settings-btn" data-tooltip="settings_menu">
|
|
3718
|
-
<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>
|
|
3719
|
-
</button>
|
|
3720
|
-
<div class="settings-menu"></div>
|
|
3721
|
-
</div>
|
|
3722
|
-
|
|
3723
3855
|
${(this.options.showQualitySelector && this.originalSources && this.originalSources.length > 1) || this.options.adaptiveQualityControl ? `
|
|
3724
3856
|
<div class="quality-control">
|
|
3725
3857
|
<button class="control-btn quality-btn" data-tooltip="video_quality">
|
|
@@ -3842,18 +3974,21 @@ checkScreenSize() {
|
|
|
3842
3974
|
|
|
3843
3975
|
/* Update settings menu visibility */
|
|
3844
3976
|
updateSettingsMenuVisibility() {
|
|
3845
|
-
|
|
3977
|
+
// SEARCH IN CONTAINER
|
|
3978
|
+
const settingsControl = this.container?.querySelector('.settings-control');
|
|
3979
|
+
|
|
3846
3980
|
if (!settingsControl) return;
|
|
3847
3981
|
|
|
3848
|
-
//
|
|
3982
|
+
// Always show settings
|
|
3849
3983
|
settingsControl.style.display = 'block';
|
|
3850
3984
|
|
|
3851
3985
|
// Populate settings menu
|
|
3852
3986
|
this.populateSettingsMenu();
|
|
3853
3987
|
|
|
3854
|
-
//
|
|
3988
|
+
// Hide speed and subtitles controls in bottom bar
|
|
3855
3989
|
const speedControl = this.controls.querySelector('.speed-control');
|
|
3856
3990
|
const subtitlesControl = this.controls.querySelector('.subtitles-control');
|
|
3991
|
+
|
|
3857
3992
|
if (speedControl) speedControl.style.display = 'none';
|
|
3858
3993
|
if (subtitlesControl) subtitlesControl.style.display = 'none';
|
|
3859
3994
|
}
|
|
@@ -3862,44 +3997,52 @@ updateSettingsMenuVisibility() {
|
|
|
3862
3997
|
* Populate settings menu with controls
|
|
3863
3998
|
*/
|
|
3864
3999
|
populateSettingsMenu() {
|
|
3865
|
-
|
|
4000
|
+
// SEARCH IN CONTAINER
|
|
4001
|
+
const settingsMenu = this.container?.querySelector('.settings-menu');
|
|
4002
|
+
|
|
3866
4003
|
if (!settingsMenu) return;
|
|
3867
4004
|
|
|
3868
4005
|
let menuHTML = '';
|
|
3869
4006
|
|
|
3870
4007
|
// SPEED - always included
|
|
3871
4008
|
if (this.options.showSpeedControl) {
|
|
3872
|
-
const speedLabel = this.t('playback_speed')
|
|
4009
|
+
const speedLabel = this.t('playback_speed');
|
|
3873
4010
|
const currentSpeed = this.video ? this.video.playbackRate : 1;
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
<
|
|
3878
|
-
|
|
3879
|
-
|
|
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;">`;
|
|
3880
4019
|
|
|
3881
4020
|
const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
|
|
3882
4021
|
speeds.forEach(speed => {
|
|
3883
4022
|
const isActive = Math.abs(speed - currentSpeed) < 0.01;
|
|
3884
4023
|
menuHTML += `<div class="settings-suboption ${isActive ? 'active' : ''}" data-speed="${speed}">${speed}x</div>`;
|
|
3885
4024
|
});
|
|
4025
|
+
|
|
3886
4026
|
menuHTML += `</div></div>`;
|
|
3887
4027
|
}
|
|
3888
4028
|
|
|
3889
4029
|
// SUBTITLES - always included
|
|
3890
4030
|
if (this.options.showSubtitles && this.textTracks && this.textTracks.length > 0) {
|
|
3891
|
-
const subtitlesLabel = this.t('subtitles')
|
|
4031
|
+
const subtitlesLabel = this.t('subtitles');
|
|
3892
4032
|
const currentTrack = this.currentSubtitleTrack;
|
|
3893
|
-
const currentLabel = this.subtitlesEnabled ?
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
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;">`;
|
|
3901
4044
|
|
|
3902
|
-
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>`;
|
|
3903
4046
|
|
|
3904
4047
|
this.textTracks.forEach((trackData, index) => {
|
|
3905
4048
|
const isActive = this.currentSubtitleTrack === trackData.track;
|
|
@@ -3972,43 +4115,50 @@ addSettingsMenuScrollbar() {
|
|
|
3972
4115
|
* Bind settings menu events
|
|
3973
4116
|
*/
|
|
3974
4117
|
bindSettingsMenuEvents() {
|
|
3975
|
-
|
|
3976
|
-
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
|
+
|
|
3977
4122
|
if (!settingsMenu || !settingsBtn) return;
|
|
3978
4123
|
|
|
3979
|
-
//
|
|
4124
|
+
// Toggle menu on button click
|
|
3980
4125
|
settingsBtn.addEventListener('click', (e) => {
|
|
3981
4126
|
e.stopPropagation();
|
|
3982
4127
|
settingsMenu.classList.toggle('active');
|
|
3983
4128
|
|
|
3984
|
-
//
|
|
4129
|
+
// When menu is opened, set max height and overflow
|
|
3985
4130
|
if (settingsMenu.classList.contains('active')) {
|
|
3986
|
-
const
|
|
3987
|
-
const containerRect = settingsMenu.parentElement.parentElement.getBoundingClientRect();
|
|
4131
|
+
const containerRect = this.container.getBoundingClientRect();
|
|
3988
4132
|
const btnRect = settingsBtn.getBoundingClientRect();
|
|
3989
|
-
|
|
3990
|
-
|
|
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));
|
|
3991
4139
|
|
|
3992
4140
|
settingsMenu.style.maxHeight = `${maxMenuHeight}px`;
|
|
3993
4141
|
settingsMenu.style.overflowY = 'auto';
|
|
3994
4142
|
settingsMenu.style.overflowX = 'hidden';
|
|
4143
|
+
|
|
4144
|
+
if (this.options.debug) {
|
|
4145
|
+
console.log(`Settings menu opened: height=${maxMenuHeight}px (available=${spaceBelow}px)`);
|
|
4146
|
+
}
|
|
3995
4147
|
} else {
|
|
3996
|
-
|
|
3997
|
-
settingsMenu.style.
|
|
4148
|
+
// Reset when closing
|
|
4149
|
+
settingsMenu.style.maxHeight = '600px'; // Default max height
|
|
4150
|
+
settingsMenu.style.overflowY = 'auto';
|
|
3998
4151
|
}
|
|
3999
|
-
|
|
4000
4152
|
});
|
|
4001
4153
|
|
|
4002
|
-
//
|
|
4154
|
+
// Close menu when clicking outside
|
|
4003
4155
|
document.addEventListener('click', (e) => {
|
|
4004
4156
|
if (!settingsBtn?.contains(e.target) && !settingsMenu?.contains(e.target)) {
|
|
4005
4157
|
settingsMenu?.classList.remove('active');
|
|
4006
|
-
settingsMenu.style.maxHeight = 'none';
|
|
4007
|
-
settingsMenu.style.overflowY = 'visible';
|
|
4008
4158
|
}
|
|
4009
4159
|
});
|
|
4010
4160
|
|
|
4011
|
-
//
|
|
4161
|
+
// Manage clicks inside the menu
|
|
4012
4162
|
settingsMenu.addEventListener('click', (e) => {
|
|
4013
4163
|
e.stopPropagation();
|
|
4014
4164
|
|
|
@@ -4048,7 +4198,7 @@ bindSettingsMenuEvents() {
|
|
|
4048
4198
|
const trigger = wrapper.querySelector('.expandable-trigger');
|
|
4049
4199
|
const action = trigger.getAttribute('data-action');
|
|
4050
4200
|
|
|
4051
|
-
if (action === '
|
|
4201
|
+
if (action === 'speed_expand') {
|
|
4052
4202
|
const speed = parseFloat(e.target.getAttribute('data-speed'));
|
|
4053
4203
|
if (speed && speed > 0 && this.video && !this.isChangingQuality) {
|
|
4054
4204
|
this.video.playbackRate = speed;
|
|
@@ -4061,13 +4211,13 @@ bindSettingsMenuEvents() {
|
|
|
4061
4211
|
const label = trigger.querySelector('.settings-option-label');
|
|
4062
4212
|
if (label) {
|
|
4063
4213
|
const speedLabel = this.t('playback_speed') || 'Playback Speed';
|
|
4064
|
-
label.
|
|
4214
|
+
label.innerHTML = `${speedLabel} <strong>${speed}x</strong>`;
|
|
4065
4215
|
}
|
|
4066
4216
|
|
|
4067
4217
|
// Trigger event
|
|
4068
4218
|
this.triggerEvent('speedchange', { speed, previousSpeed: this.video.playbackRate });
|
|
4069
4219
|
}
|
|
4070
|
-
} else if (action === '
|
|
4220
|
+
} else if (action === 'subtitles_expand') {
|
|
4071
4221
|
const trackData = e.target.getAttribute('data-track');
|
|
4072
4222
|
if (trackData === 'off') {
|
|
4073
4223
|
this.disableSubtitles();
|
|
@@ -4084,7 +4234,7 @@ bindSettingsMenuEvents() {
|
|
|
4084
4234
|
const label = trigger.querySelector('.settings-option-label');
|
|
4085
4235
|
if (label) {
|
|
4086
4236
|
const subtitlesLabel = this.t('subtitles') || 'Subtitles';
|
|
4087
|
-
label.
|
|
4237
|
+
label.innerHTML = `${subtitlesLabel} <strong>${e.target.textContent}</strong>`;
|
|
4088
4238
|
}
|
|
4089
4239
|
}
|
|
4090
4240
|
}
|
|
@@ -8439,8 +8589,7 @@ updateTimeDisplay() {
|
|
|
8439
8589
|
}
|
|
8440
8590
|
}
|
|
8441
8591
|
const fallback = {
|
|
8442
|
-
'loading': 'Loading...'
|
|
8443
|
-
'encodinginprogress': 'Encoding in progress...'
|
|
8592
|
+
'loading': 'Loading...'
|
|
8444
8593
|
};
|
|
8445
8594
|
return fallback[key] || key;
|
|
8446
8595
|
};
|
|
@@ -8453,7 +8602,7 @@ updateTimeDisplay() {
|
|
|
8453
8602
|
this.durationEl.classList.add('loading-state');
|
|
8454
8603
|
} else if (isDurationInvalid) {
|
|
8455
8604
|
// CHANGED: Move text to center overlay
|
|
8456
|
-
this.updateLoadingText(t('
|
|
8605
|
+
this.updateLoadingText(t('loading'));
|
|
8457
8606
|
// Optional: you might want to keep encoding text in bar OR move it too.
|
|
8458
8607
|
// If you want it ONLY in center:
|
|
8459
8608
|
this.durationEl.textContent = "--:--";
|