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.
@@ -1253,13 +1253,34 @@ markPlayerReady() {
1253
1253
 
1254
1254
  // Autoplay
1255
1255
  if (this.options.autoplay) {
1256
- if (this.options.debug) console.log('🎬 Autoplay enabled');
1256
+ if (this.options.debug) console.log('Autoplay enabled');
1257
1257
  setTimeout(() => {
1258
- this.video.play().catch(error => {
1259
- if (this.options.debug) console.warn('⚠️ Autoplay blocked:', error);
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('Errore aggiornamento tooltip:', error);
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
- // NON usare data-tooltip per evitare che venga sovrascritto da updateTooltips()
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 (this.autoHideDebug) {
3360
- if (this.options.debug) console.log('Not starting timer - video paused');
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('Controls shown');
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('Controls hidden');
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
- <span class="current-time">0:00</span>
3691
- <span>/</span>
3692
- <span class="duration">0:00</span>
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
- const settingsControl = this.controls?.querySelector('.settings-control');
3977
+ // SEARCH IN CONTAINER
3978
+ const settingsControl = this.container?.querySelector('.settings-control');
3979
+
3834
3980
  if (!settingsControl) return;
3835
3981
 
3836
- // always show settings
3982
+ // Always show settings
3837
3983
  settingsControl.style.display = 'block';
3838
3984
 
3839
3985
  // Populate settings menu
3840
3986
  this.populateSettingsMenu();
3841
3987
 
3842
- // hide speed and subtitles controls
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
- const settingsMenu = this.controls?.querySelector('.settings-menu');
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') || 'Playback Speed';
4009
+ const speedLabel = this.t('playback_speed');
3861
4010
  const currentSpeed = this.video ? this.video.playbackRate : 1;
3862
- menuHTML += `<div class="settings-expandable-wrapper">
3863
- <div class="settings-option expandable-trigger" data-action="speed-expand">
3864
- <span class="settings-option-label">${speedLabel} <strong>${currentSpeed}x</strong></span>
3865
- <span class="expand-arrow">▶</span>
3866
- </div>
3867
- <div class="settings-expandable-content" style="display: none;">`;
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') || 'Subtitles';
4031
+ const subtitlesLabel = this.t('subtitles');
3880
4032
  const currentTrack = this.currentSubtitleTrack;
3881
- const currentLabel = this.subtitlesEnabled ? (currentTrack ? currentTrack.label : 'Unknown') : (this.t('subtitlesoff') || 'Off');
3882
-
3883
- menuHTML += `<div class="settings-expandable-wrapper">
3884
- <div class="settings-option expandable-trigger" data-action="subtitles-expand">
3885
- <span class="settings-option-label">${subtitlesLabel} <strong>${currentLabel}</strong></span>
3886
- <span class="expand-arrow">▶</span>
3887
- </div>
3888
- <div class="settings-expandable-content" style="display: none;">`;
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('subtitlesoff') || 'Off'}</div>`;
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
- const settingsBtn = this.controls?.querySelector('.settings-btn');
3964
- const settingsMenu = this.controls?.querySelector('.settings-menu');
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
- // toggle menu on button click
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
- // when menu is opened, set max height and overflow
4129
+ // When menu is opened, set max height and overflow
3973
4130
  if (settingsMenu.classList.contains('active')) {
3974
- const settingsBtn = document.querySelector('.settings-btn');
3975
- const containerRect = settingsMenu.parentElement.parentElement.getBoundingClientRect();
4131
+ const containerRect = this.container.getBoundingClientRect();
3976
4132
  const btnRect = settingsBtn.getBoundingClientRect();
3977
- const spaceBelow = containerRect.bottom - btnRect.bottom;
3978
- const maxMenuHeight = Math.max(100, Math.min(250, spaceBelow - 20));
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
- settingsMenu.style.maxHeight = 'none';
3985
- settingsMenu.style.overflowY = 'visible';
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
- // close menu when clicking outside
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
- // manage clicks inside the menu
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 === 'speed-expand') {
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.textContent = `${speedLabel}: ${speed}x`;
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 === 'subtitles-expand') {
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.textContent = `${subtitlesLabel}: ${e.target.textContent}`;
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
- // readyState < 2 means not enough data to play (HAVE_NOTHING or HAVE_METADATA)
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
- // Initial buffering - show loading message
8428
- this.durationEl.textContent = t('loading');
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
- // Video is encoding (FFmpeg still processing) - show encoding badge
8433
- this.durationEl.textContent = t('encoding_in_progress');
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
  }