myetv-player 1.6.2 → 1.6.4

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);
@@ -1283,10 +1304,7 @@ createPlayerStructure() {
1283
1304
  this.createControls();
1284
1305
  this.createBrandLogo();
1285
1306
  this.detectPlaylist();
1286
-
1287
- if (this.options.showTitleOverlay) {
1288
- this.createTitleOverlay();
1289
- }
1307
+ this.createTopBar();
1290
1308
  }
1291
1309
 
1292
1310
  createInitialLoading() {
@@ -1310,52 +1328,31 @@ createLoadingOverlay() {
1310
1328
  this.loadingOverlay = overlay;
1311
1329
  }
1312
1330
 
1313
- createTitleOverlay() {
1314
- const overlay = document.createElement('div');
1315
- overlay.className = 'title-overlay';
1316
- overlay.id = 'titleOverlay-' + this.getUniqueId();
1317
-
1318
- const titleText = document.createElement('h2');
1319
- titleText.className = 'title-text';
1320
- titleText.textContent = this.decodeHTMLEntities(this.options.videoTitle) || '';
1321
- overlay.appendChild(titleText);
1322
-
1323
- // add subtitles
1324
- if (this.options.videoSubtitle) {
1325
- const subtitleText = document.createElement('p');
1326
- subtitleText.className = 'subtitle-text';
1327
- subtitleText.textContent = this.decodeHTMLEntities(this.options.videoSubtitle);
1328
- overlay.appendChild(subtitleText);
1329
- }
1330
-
1331
- if (this.controls) {
1332
- this.container.insertBefore(overlay, this.controls);
1333
- } else {
1334
- this.container.appendChild(overlay);
1335
- }
1336
-
1337
- this.titleOverlay = overlay;
1338
-
1339
- if (this.options.persistentTitle && this.options.videoTitle) {
1340
- this.showTitleOverlay();
1341
- }
1342
- }
1343
-
1344
1331
  updateTooltips() {
1345
1332
  if (!this.controls) return;
1346
1333
 
1347
1334
  try {
1335
+ // Update tooltips in controls
1348
1336
  this.controls.querySelectorAll('[data-tooltip]').forEach(element => {
1349
1337
  const key = element.getAttribute('data-tooltip');
1350
1338
  element.title = this.t(key);
1351
1339
  });
1352
1340
 
1341
+ // Update tooltips in top bar
1342
+ if (this.topBar) {
1343
+ this.topBar.querySelectorAll('[data-tooltip]').forEach(element => {
1344
+ const key = element.getAttribute('data-tooltip');
1345
+ element.title = this.t(key);
1346
+ });
1347
+ }
1348
+
1349
+ // Update "Auto" option in quality menu
1353
1350
  const autoOption = this.controls.querySelector('.quality-option[data-quality="auto"]');
1354
1351
  if (autoOption) {
1355
1352
  autoOption.textContent = this.t('auto');
1356
1353
  }
1357
1354
  } catch (error) {
1358
- if (this.options.debug) console.warn('Errore aggiornamento tooltip:', error);
1355
+ if (this.options.debug) console.warn('Tooltip update error', error);
1359
1356
  }
1360
1357
  }
1361
1358
 
@@ -1373,27 +1370,44 @@ setLanguage(lang) {
1373
1370
  return false;
1374
1371
  }
1375
1372
 
1373
+ /**
1374
+ * Set video title
1375
+ * @param {string} title - Video title
1376
+ * @returns {Object} this
1377
+ */
1376
1378
  setVideoTitle(title) {
1377
- this.options.videoTitle = title || '';
1379
+ this.options.videoTitle = title;
1378
1380
 
1379
- if (this.titleOverlay) {
1380
- const titleElement = this.titleOverlay.querySelector('.title-text');
1381
- if (titleElement) {
1382
- titleElement.textContent = this.decodeHTMLEntities(this.options.videoTitle);
1381
+ if (this.topBarTitle) {
1382
+ this.topBarTitle.textContent = this.decodeHTMLEntities(title);
1383
+
1384
+ // show top bar if title overlay is enabled
1385
+ if (title && this.options.showTitleOverlay) {
1386
+ const titleSection = this.topBar.querySelector('.top-bar-title');
1387
+ if (titleSection) {
1388
+ titleSection.style.display = '';
1389
+ }
1383
1390
  }
1391
+ } else if (this.topBar && title) {
1392
+ // create title section
1393
+ const titleSection = this.topBar.querySelector('.top-bar-title');
1394
+ if (!titleSection) {
1395
+ const newTitleSection = document.createElement('div');
1396
+ newTitleSection.className = 'top-bar-title';
1384
1397
 
1385
- if (title) {
1386
- this.showTitleOverlay();
1398
+ const titleElement = document.createElement('h3');
1399
+ titleElement.className = 'video-title';
1400
+ titleElement.textContent = this.decodeHTMLEntities(title);
1401
+ newTitleSection.appendChild(titleElement);
1387
1402
 
1388
- if (!this.options.persistentTitle) {
1389
- this.clearTitleTimeout();
1390
- this.titleTimeout = setTimeout(() => {
1391
- this.hideTitleOverlay();
1392
- }, 3000);
1393
- }
1403
+ const settingsControl = this.topBar.querySelector('.settings-control');
1404
+ this.topBar.insertBefore(newTitleSection, settingsControl);
1405
+
1406
+ this.topBarTitle = titleElement;
1394
1407
  }
1395
1408
  }
1396
1409
 
1410
+ if (this.options.debug) console.log('Video title set:', title);
1397
1411
  return this;
1398
1412
  }
1399
1413
 
@@ -1402,23 +1416,23 @@ getVideoTitle() {
1402
1416
  }
1403
1417
 
1404
1418
  setVideoSubtitle(subtitle) {
1405
- this.options.videoSubtitle = subtitle || '';
1419
+ this.options.videoSubtitle = subtitle;
1406
1420
 
1407
- if (this.titleOverlay) {
1408
- let subtitleElement = this.titleOverlay.querySelector('.subtitle-text');
1409
-
1410
- if (subtitle) {
1411
- if (!subtitleElement) {
1412
- subtitleElement = document.createElement('p');
1413
- subtitleElement.className = 'subtitle-text';
1414
- this.titleOverlay.appendChild(subtitleElement);
1415
- }
1416
- subtitleElement.textContent = subtitle;
1417
- } else if (subtitleElement) {
1418
- subtitleElement.remove();
1421
+ if (this.topBarSubtitle) {
1422
+ this.topBarSubtitle.textContent = subtitle;
1423
+ } else if (subtitle && this.topBar) {
1424
+ // Create subtitle element
1425
+ const titleSection = this.topBar.querySelector('.top-bar-title');
1426
+ if (titleSection) {
1427
+ const subtitleEl = document.createElement('span');
1428
+ subtitleEl.className = 'video-subtitle';
1429
+ subtitleEl.textContent = subtitle;
1430
+ titleSection.appendChild(subtitleEl);
1431
+ this.topBarSubtitle = subtitleEl;
1419
1432
  }
1420
1433
  }
1421
1434
 
1435
+ if (this.options.debug) console.log('Video subtitle set:', subtitle);
1422
1436
  return this;
1423
1437
  }
1424
1438
 
@@ -1426,41 +1440,65 @@ getVideoSubtitle() {
1426
1440
  return this.options.videoSubtitle;
1427
1441
  }
1428
1442
 
1443
+ /**
1444
+ * Set persistent title (always visible)
1445
+ * @param {boolean} persistent - If true, title stays visible
1446
+ * @returns {Object} this
1447
+ */
1429
1448
  setPersistentTitle(persistent) {
1430
1449
  this.options.persistentTitle = persistent;
1431
1450
 
1432
- if (this.titleOverlay && this.options.videoTitle) {
1451
+ if (this.topBar) {
1433
1452
  if (persistent) {
1434
- this.showTitleOverlay();
1435
- this.clearTitleTimeout();
1453
+ this.topBar.classList.add('persistent');
1436
1454
  } else {
1437
- this.titleOverlay.classList.remove('persistent');
1438
- if (this.titleOverlay.classList.contains('show')) {
1439
- this.clearTitleTimeout();
1440
- this.titleTimeout = setTimeout(() => {
1441
- this.hideTitleOverlay();
1442
- }, 3000);
1443
- }
1455
+ this.topBar.classList.remove('persistent');
1444
1456
  }
1445
1457
  }
1446
1458
 
1447
1459
  return this;
1448
1460
  }
1449
1461
 
1462
+ /**
1463
+ * Enable title overlay (shows top bar with title)
1464
+ * @returns {Object} this
1465
+ */
1450
1466
  enableTitleOverlay() {
1451
- if (!this.titleOverlay && !this.options.showTitleOverlay) {
1452
- this.options.showTitleOverlay = true;
1453
- this.createTitleOverlay();
1467
+ if (!this.topBar) {
1468
+ if (this.options.debug) console.warn('Top bar not available');
1469
+ return this;
1470
+ }
1471
+
1472
+ this.options.showTitleOverlay = true;
1473
+
1474
+ // show top bar
1475
+ if (this.options.videoTitle) {
1476
+ const titleSection = this.topBar.querySelector('.top-bar-title');
1477
+ if (titleSection) {
1478
+ titleSection.style.display = '';
1479
+ }
1454
1480
  }
1481
+
1482
+ if (this.options.debug) console.log('Title overlay enabled');
1455
1483
  return this;
1456
1484
  }
1457
1485
 
1486
+ /**
1487
+ * Disable title overlay (hides top bar)
1488
+ * @returns {Object} this
1489
+ */
1458
1490
  disableTitleOverlay() {
1459
- if (this.titleOverlay) {
1460
- this.titleOverlay.remove();
1461
- this.titleOverlay = null;
1462
- }
1491
+ if (!this.topBar) return this;
1492
+
1463
1493
  this.options.showTitleOverlay = false;
1494
+
1495
+ // hide top bar
1496
+ const titleSection = this.topBar.querySelector('.top-bar-title');
1497
+ if (titleSection) {
1498
+ titleSection.style.display = 'none';
1499
+ }
1500
+
1501
+ if (this.options.debug) console.log('Title overlay disabled');
1464
1502
  return this;
1465
1503
  }
1466
1504
 
@@ -1501,6 +1539,10 @@ initializeElements() {
1501
1539
  this.speedMenu = this.controls?.querySelector('.speed-menu');
1502
1540
  this.qualityMenu = this.controls?.querySelector('.quality-menu');
1503
1541
  this.subtitlesMenu = this.controls?.querySelector('.subtitles-menu');
1542
+
1543
+ this.settingsBtn = this.container?.querySelector('.settings-btn');
1544
+ this.settingsMenu = this.container?.querySelector('.settings-menu');
1545
+
1504
1546
  // Apply seek handle shape from options
1505
1547
  if (this.progressHandle && this.options.seekHandleShape) {
1506
1548
  this.setSeekHandleShape(this.options.seekHandleShape);
@@ -1581,6 +1623,88 @@ setupMenuToggles() {
1581
1623
  }
1582
1624
  }
1583
1625
 
1626
+ /**
1627
+ * Create integrated top bar with settings menu
1628
+ * Respects showTitleOverlay, videoTitle, videoSubtitle, persistentTitle options
1629
+ * @returns {void}
1630
+ */
1631
+ createTopBar() {
1632
+ if (!this.container) return;
1633
+
1634
+ // Create top bar element
1635
+ const topBar = document.createElement('div');
1636
+ topBar.className = 'player-top-bar';
1637
+ topBar.id = `topBar${this.getUniqueId()}`;
1638
+
1639
+ // Left section - Title (ALWAYS create structure)
1640
+ const titleSection = document.createElement('div');
1641
+ titleSection.className = 'top-bar-title';
1642
+
1643
+ // Main title
1644
+ const titleElement = document.createElement('h3');
1645
+ titleElement.className = 'video-title';
1646
+ titleElement.textContent = this.options.videoTitle ? this.decodeHTMLEntities(this.options.videoTitle) : '';
1647
+ titleSection.appendChild(titleElement);
1648
+
1649
+ // Optional subtitle (if present)
1650
+ if (this.options.videoSubtitle) {
1651
+ const subtitleElement = document.createElement('span');
1652
+ subtitleElement.className = 'video-subtitle';
1653
+ subtitleElement.textContent = this.decodeHTMLEntities(this.options.videoSubtitle);
1654
+ titleSection.appendChild(subtitleElement);
1655
+ }
1656
+
1657
+ // Hide title section if showTitleOverlay is false
1658
+ if (!this.options.showTitleOverlay || !this.options.videoTitle) {
1659
+ titleSection.style.display = 'none';
1660
+ }
1661
+
1662
+ topBar.appendChild(titleSection);
1663
+
1664
+ // Right section - Settings control
1665
+ const settingsControl = document.createElement('div');
1666
+ settingsControl.className = 'settings-control settings-top-bar';
1667
+
1668
+ // Settings button
1669
+ const settingsBtn = document.createElement('button');
1670
+ settingsBtn.className = 'control-btn settings-btn';
1671
+ settingsBtn.setAttribute('data-tooltip', 'settingsmenu');
1672
+
1673
+ const icon = document.createElement('span');
1674
+ icon.className = 'icon';
1675
+ icon.innerHTML = `<svg viewBox="0 0 24 24" width="20" height="20" 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>`;
1676
+ settingsBtn.appendChild(icon);
1677
+
1678
+ // Settings menu
1679
+ const settingsMenu = document.createElement('div');
1680
+ settingsMenu.className = 'settings-menu';
1681
+
1682
+ settingsControl.appendChild(settingsBtn);
1683
+ settingsControl.appendChild(settingsMenu);
1684
+ topBar.appendChild(settingsControl);
1685
+
1686
+ // Add persistent class if persistentTitle is enabled
1687
+ if (this.options.persistentTitle) {
1688
+ topBar.classList.add('persistent');
1689
+ }
1690
+
1691
+ // Insert top bar as first child in container
1692
+ this.container.insertBefore(topBar, this.container.firstChild);
1693
+
1694
+ // Save references
1695
+ this.topBar = topBar;
1696
+ this.topBarTitle = topBar.querySelector('.video-title');
1697
+ this.topBarSubtitle = topBar.querySelector('.video-subtitle');
1698
+
1699
+ if (this.options.debug) {
1700
+ console.log('Top bar created with integrated settings', {
1701
+ showTitle: this.options.showTitleOverlay,
1702
+ persistent: this.options.persistentTitle,
1703
+ opacity: this.options.titleOverlayOpacity
1704
+ });
1705
+ }
1706
+ }
1707
+
1584
1708
  /**
1585
1709
  * Restart video from beginning - Works with HTML5 video and ALL plugins
1586
1710
  * @returns {this} Returns this for method chaining
@@ -2165,7 +2289,7 @@ createBrandLogo() {
2165
2289
  if (this.options.brandLogoLinkUrl) {
2166
2290
  // Use custom tooltip text if provided, otherwise fallback to URL
2167
2291
  logo.title = this.options.brandLogoTooltipText || this.options.brandLogoLinkUrl;
2168
- // NON usare data-tooltip per evitare che venga sovrascritto da updateTooltips()
2292
+ // do not use data-tooltip to not be overwritten
2169
2293
  }
2170
2294
 
2171
2295
  // Handle loading error
@@ -2860,6 +2984,13 @@ addEventListener(eventType, callback) {
2860
2984
  this.pauseIcon.classList.remove('hidden');
2861
2985
  }
2862
2986
 
2987
+ // Reset auto-hide timer when video starts playing
2988
+ if (this.options.autoHide && this.autoHideInitialized) {
2989
+ if (this.options.debug) console.log('Video playing - reset auto-hide timer');
2990
+ this.showControlsNow();
2991
+ this.resetAutoHideTimer();
2992
+ }
2993
+
2863
2994
  // Trigger playing event - video is now actually playing
2864
2995
  this.triggerEvent('playing', {
2865
2996
  currentTime: this.getCurrentTime(),
@@ -2867,6 +2998,20 @@ addEventListener(eventType, callback) {
2867
2998
  });
2868
2999
  });
2869
3000
 
3001
+ // Show controls and cancel timer when video is paused
3002
+ this.video.addEventListener('pause', () => {
3003
+ if (this.options.autoHide && this.autoHideInitialized) {
3004
+ if (this.options.debug) console.log('Video paused - show controls and cancel timer');
3005
+ this.showControlsNow();
3006
+
3007
+ // Cancel timer so controls stay visible while paused
3008
+ if (this.autoHideTimer) {
3009
+ clearTimeout(this.autoHideTimer);
3010
+ this.autoHideTimer = null;
3011
+ }
3012
+ }
3013
+ });
3014
+
2870
3015
  this.video.addEventListener('waiting', () => {
2871
3016
  if (!this.isChangingQuality) {
2872
3017
  this.showLoading();
@@ -3359,30 +3504,37 @@ resetAutoHideTimer() {
3359
3504
  this.autoHideTimer = null;
3360
3505
  }
3361
3506
 
3362
- const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
3507
+ const isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
3508
+
3363
3509
  if (this.mouseOverControls && !isTouchDevice) {
3364
- if (this.autoHideDebug) {
3365
- if (this.options.debug) console.log('Not starting timer - mouse on controls');
3366
- }
3510
+ if (this.autoHideDebug && this.options.debug) console.log('Not starting timer - mouse on controls');
3367
3511
  return;
3368
3512
  }
3369
3513
 
3514
+ // Allow timer if video is paused at start (autoplay blocked)
3370
3515
  if (this.video && this.video.paused) {
3371
- if (this.autoHideDebug) {
3372
- if (this.options.debug) console.log('Not starting timer - video paused');
3516
+ const isInitialPause = this.video.currentTime === 0 && !this.video.ended;
3517
+
3518
+ if (!isInitialPause) {
3519
+ if (this.autoHideDebug && this.options.debug) console.log('Not starting timer - video paused by user');
3520
+ return;
3521
+ }
3522
+
3523
+ if (this.autoHideDebug && this.options.debug) {
3524
+ console.log('Video paused but at start - allowing timer (autoplay blocked)');
3373
3525
  }
3374
- return;
3375
3526
  }
3376
3527
 
3528
+ // Start timer
3377
3529
  this.autoHideTimer = setTimeout(() => {
3378
- if (this.autoHideDebug) {
3379
- if (this.options.debug) console.log(`Timer expired after ${this.options.autoHideDelay}ms - nascondo controlli`);
3530
+ if (this.autoHideDebug && this.options.debug) {
3531
+ console.log(`Timer expired after ${this.options.autoHideDelay}ms - hiding controls`);
3380
3532
  }
3381
3533
  this.hideControlsNow();
3382
3534
  }, this.options.autoHideDelay);
3383
3535
 
3384
- if (this.autoHideDebug) {
3385
- if (this.options.debug) console.log(`Auto-hide timer started: ${this.options.autoHideDelay}ms`);
3536
+ if (this.autoHideDebug && this.options.debug) {
3537
+ console.log(`Auto-hide timer started (${this.options.autoHideDelay}ms)`);
3386
3538
  }
3387
3539
  }
3388
3540
 
@@ -3410,7 +3562,7 @@ showControlsNow() {
3410
3562
  // *show cursor when controls are shown*
3411
3563
  this.showCursor();
3412
3564
 
3413
- if (this.autoHideDebug && this.options.debug) console.log('Controls shown');
3565
+ if (this.autoHideDebug && this.options.debug) console.log('Controls shown');
3414
3566
  }
3415
3567
  }
3416
3568
 
@@ -3451,7 +3603,7 @@ hideControlsNow() {
3451
3603
  // *hide cursor after controls are hidden*
3452
3604
  this.hideCursor();
3453
3605
 
3454
- if (this.autoHideDebug && this.options.debug) console.log('Controls hidden');
3606
+ if (this.autoHideDebug && this.options.debug) console.log('Controls hidden');
3455
3607
  }
3456
3608
  }
3457
3609
 
@@ -3477,9 +3629,7 @@ clearControlsTimeout() {
3477
3629
 
3478
3630
  // Default controlbar styles injection
3479
3631
  injectDefaultControlbarStyles() {
3480
- if (document.getElementById('default-controlbar-styles')) {
3481
- return;
3482
- }
3632
+ if (document.getElementById('default-controlbar-styles')) return;
3483
3633
 
3484
3634
  const controlBarOpacity = Math.max(0, Math.min(1, this.options.controlBarOpacity));
3485
3635
  const titleOverlayOpacity = Math.max(0, Math.min(1, this.options.titleOverlayOpacity));
@@ -3501,7 +3651,7 @@ injectDefaultControlbarStyles() {
3501
3651
  min-height: 60px;
3502
3652
  padding-bottom: 10px;
3503
3653
  }
3504
-
3654
+
3505
3655
  .video-wrapper:not(.youtube-active):not(.vimeo-active):not(.facebook-active) .title-overlay {
3506
3656
  background: linear-gradient(
3507
3657
  to bottom,
@@ -3516,8 +3666,12 @@ injectDefaultControlbarStyles() {
3516
3666
  min-height: 80px;
3517
3667
  padding-top: 20px;
3518
3668
  }
3669
+
3670
+ /* ✅ NEW: Set CSS custom property for top bar opacity */
3671
+ .video-wrapper {
3672
+ --player-topbar-opacity: ${titleOverlayOpacity};
3673
+ }
3519
3674
  `;
3520
-
3521
3675
  document.head.appendChild(style);
3522
3676
  }
3523
3677
 
@@ -3699,10 +3853,9 @@ createControls() {
3699
3853
  </div>
3700
3854
 
3701
3855
  <div class="time-display">
3702
- <span class="current-time">0:00</span>
3703
- <span>/</span>
3704
- <span class="duration">0:00</span>
3705
- </div>
3856
+ <span class="current-time">00:00</span>
3857
+ <span class="duration">00:00</span>
3858
+ </div>
3706
3859
  </div>
3707
3860
 
3708
3861
  <div class="controls-right">
@@ -3713,13 +3866,6 @@ createControls() {
3713
3866
  <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
3867
  </button>
3715
3868
 
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
3869
  ${(this.options.showQualitySelector && this.originalSources && this.originalSources.length > 1) || this.options.adaptiveQualityControl ? `
3724
3870
  <div class="quality-control">
3725
3871
  <button class="control-btn quality-btn" data-tooltip="video_quality">
@@ -3842,18 +3988,21 @@ checkScreenSize() {
3842
3988
 
3843
3989
  /* Update settings menu visibility */
3844
3990
  updateSettingsMenuVisibility() {
3845
- const settingsControl = this.controls?.querySelector('.settings-control');
3991
+ // SEARCH IN CONTAINER
3992
+ const settingsControl = this.container?.querySelector('.settings-control');
3993
+
3846
3994
  if (!settingsControl) return;
3847
3995
 
3848
- // always show settings
3996
+ // Always show settings
3849
3997
  settingsControl.style.display = 'block';
3850
3998
 
3851
3999
  // Populate settings menu
3852
4000
  this.populateSettingsMenu();
3853
4001
 
3854
- // hide speed and subtitles controls
4002
+ // Hide speed and subtitles controls in bottom bar
3855
4003
  const speedControl = this.controls.querySelector('.speed-control');
3856
4004
  const subtitlesControl = this.controls.querySelector('.subtitles-control');
4005
+
3857
4006
  if (speedControl) speedControl.style.display = 'none';
3858
4007
  if (subtitlesControl) subtitlesControl.style.display = 'none';
3859
4008
  }
@@ -3862,44 +4011,52 @@ updateSettingsMenuVisibility() {
3862
4011
  * Populate settings menu with controls
3863
4012
  */
3864
4013
  populateSettingsMenu() {
3865
- const settingsMenu = this.controls?.querySelector('.settings-menu');
4014
+ // SEARCH IN CONTAINER
4015
+ const settingsMenu = this.container?.querySelector('.settings-menu');
4016
+
3866
4017
  if (!settingsMenu) return;
3867
4018
 
3868
4019
  let menuHTML = '';
3869
4020
 
3870
4021
  // SPEED - always included
3871
4022
  if (this.options.showSpeedControl) {
3872
- const speedLabel = this.t('playback_speed') || 'Playback Speed';
4023
+ const speedLabel = this.t('playback_speed');
3873
4024
  const currentSpeed = this.video ? this.video.playbackRate : 1;
3874
- menuHTML += `<div class="settings-expandable-wrapper">
3875
- <div class="settings-option expandable-trigger" data-action="speed-expand">
3876
- <span class="settings-option-label">${speedLabel} <strong>${currentSpeed}x</strong></span>
3877
- <span class="expand-arrow">▶</span>
3878
- </div>
3879
- <div class="settings-expandable-content" style="display: none;">`;
4025
+
4026
+ menuHTML += `
4027
+ <div class="settings-expandable-wrapper">
4028
+ <div class="settings-option expandable-trigger" data-action="speed_expand">
4029
+ <span class="settings-option-label">${speedLabel} <strong>${currentSpeed}x</strong></span>
4030
+ <span class="expand-arrow">▼</span>
4031
+ </div>
4032
+ <div class="settings-expandable-content" style="display: none;">`;
3880
4033
 
3881
4034
  const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
3882
4035
  speeds.forEach(speed => {
3883
4036
  const isActive = Math.abs(speed - currentSpeed) < 0.01;
3884
4037
  menuHTML += `<div class="settings-suboption ${isActive ? 'active' : ''}" data-speed="${speed}">${speed}x</div>`;
3885
4038
  });
4039
+
3886
4040
  menuHTML += `</div></div>`;
3887
4041
  }
3888
4042
 
3889
4043
  // SUBTITLES - always included
3890
4044
  if (this.options.showSubtitles && this.textTracks && this.textTracks.length > 0) {
3891
- const subtitlesLabel = this.t('subtitles') || 'Subtitles';
4045
+ const subtitlesLabel = this.t('subtitles');
3892
4046
  const currentTrack = this.currentSubtitleTrack;
3893
- const currentLabel = this.subtitlesEnabled ? (currentTrack ? currentTrack.label : 'Unknown') : (this.t('subtitlesoff') || 'Off');
3894
-
3895
- menuHTML += `<div class="settings-expandable-wrapper">
3896
- <div class="settings-option expandable-trigger" data-action="subtitles-expand">
3897
- <span class="settings-option-label">${subtitlesLabel} <strong>${currentLabel}</strong></span>
3898
- <span class="expand-arrow">▶</span>
3899
- </div>
3900
- <div class="settings-expandable-content" style="display: none;">`;
4047
+ const currentLabel = this.subtitlesEnabled ?
4048
+ (currentTrack ? currentTrack.label : 'Unknown') :
4049
+ this.t('subtitles_off'); //
4050
+
4051
+ menuHTML += `
4052
+ <div class="settings-expandable-wrapper">
4053
+ <div class="settings-option expandable-trigger" data-action="subtitles_expand">
4054
+ <span class="settings-option-label">${subtitlesLabel} <strong>${currentLabel}</strong></span>
4055
+ <span class="expand-arrow">▼</span>
4056
+ </div>
4057
+ <div class="settings-expandable-content" style="display: none;">`;
3901
4058
 
3902
- menuHTML += `<div class="settings-suboption ${!this.subtitlesEnabled ? 'active' : ''}" data-track="off">${this.t('subtitlesoff') || 'Off'}</div>`;
4059
+ menuHTML += `<div class="settings-suboption ${!this.subtitlesEnabled ? 'active' : ''}" data-track="off">${this.t('subtitles_off')}</div>`;
3903
4060
 
3904
4061
  this.textTracks.forEach((trackData, index) => {
3905
4062
  const isActive = this.currentSubtitleTrack === trackData.track;
@@ -3972,43 +4129,50 @@ addSettingsMenuScrollbar() {
3972
4129
  * Bind settings menu events
3973
4130
  */
3974
4131
  bindSettingsMenuEvents() {
3975
- const settingsBtn = this.controls?.querySelector('.settings-btn');
3976
- const settingsMenu = this.controls?.querySelector('.settings-menu');
4132
+ // Search in container instead of controls (for top bar)
4133
+ const settingsBtn = this.container?.querySelector('.settings-btn');
4134
+ const settingsMenu = this.container?.querySelector('.settings-menu');
4135
+
3977
4136
  if (!settingsMenu || !settingsBtn) return;
3978
4137
 
3979
- // toggle menu on button click
4138
+ // Toggle menu on button click
3980
4139
  settingsBtn.addEventListener('click', (e) => {
3981
4140
  e.stopPropagation();
3982
4141
  settingsMenu.classList.toggle('active');
3983
4142
 
3984
- // when menu is opened, set max height and overflow
4143
+ // When menu is opened, set max height and overflow
3985
4144
  if (settingsMenu.classList.contains('active')) {
3986
- const settingsBtn = document.querySelector('.settings-btn');
3987
- const containerRect = settingsMenu.parentElement.parentElement.getBoundingClientRect();
4145
+ const containerRect = this.container.getBoundingClientRect();
3988
4146
  const btnRect = settingsBtn.getBoundingClientRect();
3989
- const spaceBelow = containerRect.bottom - btnRect.bottom;
3990
- const maxMenuHeight = Math.max(100, Math.min(250, spaceBelow - 20));
4147
+
4148
+ // Calculate available space below the button
4149
+ const spaceBelow = containerRect.bottom - btnRect.bottom - 30; // 30px margin
4150
+
4151
+ // Minimum 300px, maximum 600px or available space
4152
+ const maxMenuHeight = Math.max(300, Math.min(600, spaceBelow));
3991
4153
 
3992
4154
  settingsMenu.style.maxHeight = `${maxMenuHeight}px`;
3993
4155
  settingsMenu.style.overflowY = 'auto';
3994
4156
  settingsMenu.style.overflowX = 'hidden';
4157
+
4158
+ if (this.options.debug) {
4159
+ console.log(`Settings menu opened: height=${maxMenuHeight}px (available=${spaceBelow}px)`);
4160
+ }
3995
4161
  } else {
3996
- settingsMenu.style.maxHeight = 'none';
3997
- settingsMenu.style.overflowY = 'visible';
4162
+ // Reset when closing
4163
+ settingsMenu.style.maxHeight = '600px'; // Default max height
4164
+ settingsMenu.style.overflowY = 'auto';
3998
4165
  }
3999
-
4000
4166
  });
4001
4167
 
4002
- // close menu when clicking outside
4168
+ // Close menu when clicking outside
4003
4169
  document.addEventListener('click', (e) => {
4004
4170
  if (!settingsBtn?.contains(e.target) && !settingsMenu?.contains(e.target)) {
4005
4171
  settingsMenu?.classList.remove('active');
4006
- settingsMenu.style.maxHeight = 'none';
4007
- settingsMenu.style.overflowY = 'visible';
4008
4172
  }
4009
4173
  });
4010
4174
 
4011
- // manage clicks inside the menu
4175
+ // Manage clicks inside the menu
4012
4176
  settingsMenu.addEventListener('click', (e) => {
4013
4177
  e.stopPropagation();
4014
4178
 
@@ -4048,7 +4212,7 @@ bindSettingsMenuEvents() {
4048
4212
  const trigger = wrapper.querySelector('.expandable-trigger');
4049
4213
  const action = trigger.getAttribute('data-action');
4050
4214
 
4051
- if (action === 'speed-expand') {
4215
+ if (action === 'speed_expand') {
4052
4216
  const speed = parseFloat(e.target.getAttribute('data-speed'));
4053
4217
  if (speed && speed > 0 && this.video && !this.isChangingQuality) {
4054
4218
  this.video.playbackRate = speed;
@@ -4061,13 +4225,13 @@ bindSettingsMenuEvents() {
4061
4225
  const label = trigger.querySelector('.settings-option-label');
4062
4226
  if (label) {
4063
4227
  const speedLabel = this.t('playback_speed') || 'Playback Speed';
4064
- label.textContent = `${speedLabel}: ${speed}x`;
4228
+ label.innerHTML = `${speedLabel} <strong>${speed}x</strong>`;
4065
4229
  }
4066
4230
 
4067
4231
  // Trigger event
4068
4232
  this.triggerEvent('speedchange', { speed, previousSpeed: this.video.playbackRate });
4069
4233
  }
4070
- } else if (action === 'subtitles-expand') {
4234
+ } else if (action === 'subtitles_expand') {
4071
4235
  const trackData = e.target.getAttribute('data-track');
4072
4236
  if (trackData === 'off') {
4073
4237
  this.disableSubtitles();
@@ -4084,7 +4248,7 @@ bindSettingsMenuEvents() {
4084
4248
  const label = trigger.querySelector('.settings-option-label');
4085
4249
  if (label) {
4086
4250
  const subtitlesLabel = this.t('subtitles') || 'Subtitles';
4087
- label.textContent = `${subtitlesLabel}: ${e.target.textContent}`;
4251
+ label.innerHTML = `${subtitlesLabel} <strong>${e.target.textContent}</strong>`;
4088
4252
  }
4089
4253
  }
4090
4254
  }
@@ -6470,35 +6634,35 @@ bindChapterEvents() {
6470
6634
  }
6471
6635
 
6472
6636
  /**
6473
- * Update chapter name in title overlay dynamically
6637
+ * Update chapter name in top bar subtitle dynamically
6638
+ * Shows current chapter title as subtitle in the top bar
6474
6639
  */
6475
6640
  updateChapterInTitleOverlay() {
6476
6641
  if (!this.video || !this.chapters || this.chapters.length === 0) return;
6477
6642
 
6478
- const titleOverlay = this.container ? this.container.querySelector('.title-overlay') : null;
6479
- if (!titleOverlay) return;
6480
-
6481
- // Find or create chapter name element
6482
- let chapterElement = titleOverlay.querySelector('.chapter-name');
6483
- if (!chapterElement) {
6484
- chapterElement = document.createElement('div');
6485
- chapterElement.className = 'chapter-name';
6486
- chapterElement.style.cssText = `
6487
- font-size: 13px;
6488
- font-weight: 500;
6489
- color: rgba(255, 255, 255, 0.9);
6490
- margin-top: 6px;
6491
- max-width: 400px;
6492
- overflow: hidden;
6493
- text-overflow: ellipsis;
6494
- white-space: nowrap;
6495
- `;
6496
- titleOverlay.appendChild(chapterElement);
6643
+ // Use topBar instead of old titleOverlay
6644
+ if (!this.topBar) return;
6645
+
6646
+ // Find or create subtitle element in top bar
6647
+ let subtitleElement = this.topBar.querySelector('.video-subtitle');
6648
+
6649
+ if (!subtitleElement) {
6650
+ // Create subtitle element if it doesn't exist
6651
+ const titleSection = this.topBar.querySelector('.top-bar-title');
6652
+ if (!titleSection) return;
6653
+
6654
+ subtitleElement = document.createElement('span');
6655
+ subtitleElement.className = 'video-subtitle';
6656
+ titleSection.appendChild(subtitleElement);
6657
+
6658
+ // Save reference
6659
+ this.topBarSubtitle = subtitleElement;
6497
6660
  }
6498
6661
 
6499
6662
  // Find current chapter
6500
6663
  const currentTime = this.video.currentTime;
6501
6664
  let currentChapter = null;
6665
+
6502
6666
  for (let i = this.chapters.length - 1; i >= 0; i--) {
6503
6667
  if (currentTime >= this.chapters[i].time) {
6504
6668
  currentChapter = this.chapters[i];
@@ -6506,12 +6670,22 @@ updateChapterInTitleOverlay() {
6506
6670
  }
6507
6671
  }
6508
6672
 
6509
- // Update or hide chapter name
6673
+ // Update subtitle with chapter title or hide if no chapter
6510
6674
  if (currentChapter) {
6511
- chapterElement.textContent = currentChapter.title;
6512
- chapterElement.style.display = 'block';
6675
+ subtitleElement.textContent = currentChapter.title;
6676
+ subtitleElement.style.display = 'block';
6513
6677
  } else {
6514
- chapterElement.style.display = 'none';
6678
+ // Se non c'è un capitolo attivo, mostra il sottotitolo originale o nascondi
6679
+ if (this.options.videoSubtitle) {
6680
+ subtitleElement.textContent = this.options.videoSubtitle;
6681
+ subtitleElement.style.display = 'block';
6682
+ } else {
6683
+ subtitleElement.style.display = 'none';
6684
+ }
6685
+ }
6686
+
6687
+ if (this.options.debug) {
6688
+ console.log('Chapter overlay updated:', currentChapter ? currentChapter.title : 'No chapter');
6515
6689
  }
6516
6690
  }
6517
6691
 
@@ -8439,8 +8613,7 @@ updateTimeDisplay() {
8439
8613
  }
8440
8614
  }
8441
8615
  const fallback = {
8442
- 'loading': 'Loading...',
8443
- 'encodinginprogress': 'Encoding in progress...'
8616
+ 'loading': 'Loading...'
8444
8617
  };
8445
8618
  return fallback[key] || key;
8446
8619
  };
@@ -8453,7 +8626,7 @@ updateTimeDisplay() {
8453
8626
  this.durationEl.classList.add('loading-state');
8454
8627
  } else if (isDurationInvalid) {
8455
8628
  // CHANGED: Move text to center overlay
8456
- this.updateLoadingText(t('encodinginprogress'));
8629
+ this.updateLoadingText(t('loading'));
8457
8630
  // Optional: you might want to keep encoding text in bar OR move it too.
8458
8631
  // If you want it ONLY in center:
8459
8632
  this.durationEl.textContent = "--:--";