unified-video-framework 1.4.448 → 1.4.449

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.
@@ -1725,8 +1725,44 @@ export class WebPlayer extends BasePlayer {
1725
1725
  existingPlayer.remove();
1726
1726
  }
1727
1727
 
1728
+ // Remove existing overlay if any
1729
+ const existingOverlay = container.querySelector('.uvf-youtube-overlay');
1730
+ if (existingOverlay) {
1731
+ existingOverlay.remove();
1732
+ }
1733
+
1728
1734
  container.appendChild(iframeContainer);
1729
1735
 
1736
+ // Create transparent overlay to block YouTube's native controls (when custom controls are enabled)
1737
+ if (!this.youtubeNativeControls) {
1738
+ const overlay = document.createElement('div');
1739
+ overlay.className = 'uvf-youtube-overlay';
1740
+ // Styles are handled in CSS, just set essentials here
1741
+ overlay.style.cssText = `
1742
+ position: absolute;
1743
+ top: 0;
1744
+ left: 0;
1745
+ width: 100%;
1746
+ z-index: 2;
1747
+ `;
1748
+
1749
+ // Handle click to toggle play/pause
1750
+ overlay.addEventListener('click', () => {
1751
+ this.togglePlayPause();
1752
+ });
1753
+
1754
+ // Handle double-click for fullscreen
1755
+ overlay.addEventListener('dblclick', () => {
1756
+ if (this.isFullscreen()) {
1757
+ this.exitFullscreen();
1758
+ } else {
1759
+ this.enterFullscreen();
1760
+ }
1761
+ });
1762
+
1763
+ container.appendChild(overlay);
1764
+ }
1765
+
1730
1766
  // Load YouTube IFrame API if not loaded
1731
1767
  if (!window.YT) {
1732
1768
  await this.loadYouTubeAPI();
@@ -1802,6 +1838,10 @@ export class WebPlayer extends BasePlayer {
1802
1838
  this.youtubePlayerReady = true;
1803
1839
  this.debugLog('YouTube player ready');
1804
1840
 
1841
+ // Hide loading spinner when YouTube player is ready
1842
+ const loading = document.getElementById('uvf-loading');
1843
+ if (loading) loading.classList.remove('active');
1844
+
1805
1845
  // If YouTube native controls are enabled, hide custom controls
1806
1846
  if (this.youtubeNativeControls && this.playerWrapper) {
1807
1847
  this.debugLog('[YouTube] Native controls enabled - hiding custom controls');
@@ -1851,6 +1891,10 @@ export class WebPlayer extends BasePlayer {
1851
1891
  const state = event.data;
1852
1892
 
1853
1893
  switch (state) {
1894
+ case window.YT.PlayerState.UNSTARTED: // -1: Video is loaded but not started
1895
+ this.updateYouTubeUI('unstarted');
1896
+ break;
1897
+
1854
1898
  case window.YT.PlayerState.PLAYING:
1855
1899
  this.state.isPlaying = true;
1856
1900
  this.state.isPaused = false;
@@ -1903,12 +1947,20 @@ export class WebPlayer extends BasePlayer {
1903
1947
  const playIcon = document.getElementById('uvf-play-icon');
1904
1948
  const pauseIcon = document.getElementById('uvf-pause-icon');
1905
1949
  const centerPlay = document.getElementById('uvf-center-play');
1950
+ const loading = document.getElementById('uvf-loading');
1951
+
1952
+ // Handle loading spinner for YouTube
1953
+ if (state === 'buffering') {
1954
+ if (loading) loading.classList.add('active');
1955
+ } else if (state === 'playing' || state === 'paused' || state === 'cued' || state === 'ended' || state === 'unstarted') {
1956
+ if (loading) loading.classList.remove('active');
1957
+ }
1906
1958
 
1907
1959
  if (state === 'playing' || state === 'buffering') {
1908
1960
  if (playIcon) playIcon.style.display = 'none';
1909
1961
  if (pauseIcon) pauseIcon.style.display = 'block';
1910
1962
  if (centerPlay) centerPlay.classList.add('hidden');
1911
- } else if (state === 'paused' || state === 'cued' || state === 'ended') {
1963
+ } else if (state === 'paused' || state === 'cued' || state === 'ended' || state === 'unstarted') {
1912
1964
  if (playIcon) playIcon.style.display = 'block';
1913
1965
  if (pauseIcon) pauseIcon.style.display = 'none';
1914
1966
  if (centerPlay) centerPlay.classList.remove('hidden');
@@ -5804,7 +5856,8 @@ export class WebPlayer extends BasePlayer {
5804
5856
  z-index: 25;
5805
5857
  opacity: 0;
5806
5858
  transform: translateX(-50%) translateY(8px);
5807
- transition: opacity 0.15s ease, transform 0.15s ease;
5859
+ transition: opacity 0.15s ease, transform 0.15s ease, left 0.05s ease-out;
5860
+ will-change: left, transform, opacity;
5808
5861
  }
5809
5862
 
5810
5863
  .uvf-thumbnail-preview.visible {
@@ -5820,6 +5873,8 @@ export class WebPlayer extends BasePlayer {
5820
5873
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.1);
5821
5874
  backdrop-filter: blur(12px);
5822
5875
  -webkit-backdrop-filter: blur(12px);
5876
+ /* Prevent container size changes */
5877
+ flex-shrink: 0;
5823
5878
  }
5824
5879
 
5825
5880
  .uvf-thumbnail-preview-image-wrapper {
@@ -5829,6 +5884,8 @@ export class WebPlayer extends BasePlayer {
5829
5884
  border-radius: 6px;
5830
5885
  overflow: hidden;
5831
5886
  background: rgba(30, 30, 30, 0.95);
5887
+ /* Prevent layout shifts during image loading */
5888
+ flex-shrink: 0;
5832
5889
  }
5833
5890
 
5834
5891
  .uvf-thumbnail-preview-image {
@@ -5838,6 +5895,10 @@ export class WebPlayer extends BasePlayer {
5838
5895
  display: block;
5839
5896
  opacity: 0;
5840
5897
  transition: opacity 0.2s ease;
5898
+ /* Prevent reflow/repaint jitter during load */
5899
+ will-change: opacity;
5900
+ backface-visibility: hidden;
5901
+ -webkit-backface-visibility: hidden;
5841
5902
  }
5842
5903
 
5843
5904
  .uvf-thumbnail-preview-image.loaded {
@@ -8588,7 +8649,25 @@ export class WebPlayer extends BasePlayer {
8588
8649
  opacity: 0 !important;
8589
8650
  pointer-events: none !important;
8590
8651
  }
8591
-
8652
+
8653
+ /* YouTube overlay to block native controls */
8654
+ .uvf-youtube-overlay {
8655
+ position: absolute;
8656
+ top: 0;
8657
+ left: 0;
8658
+ width: 100%;
8659
+ height: calc(100% - 60px); /* Leave bottom area for our controls */
8660
+ z-index: 2;
8661
+ cursor: pointer;
8662
+ background: transparent;
8663
+ pointer-events: auto;
8664
+ }
8665
+
8666
+ /* When controls are hidden, cover entire video */
8667
+ .uvf-player-wrapper:not(:hover) .uvf-youtube-overlay {
8668
+ height: 100%;
8669
+ }
8670
+
8592
8671
  /* Ultra-wide screens */
8593
8672
  @media screen and (min-width: 1440px) {
8594
8673
  .uvf-video-title {