unified-video-framework 1.4.117 → 1.4.119
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.
|
@@ -739,26 +739,87 @@ export class WebPlayer extends BasePlayer {
|
|
|
739
739
|
this.debugWarn('Failed to exit fullscreen:', error.message);
|
|
740
740
|
}
|
|
741
741
|
}
|
|
742
|
-
|
|
742
|
+
isPictureInPictureSupported() {
|
|
743
743
|
if (!this.video)
|
|
744
|
-
return;
|
|
744
|
+
return false;
|
|
745
|
+
const hasStandardPiP = 'requestPictureInPicture' in this.video &&
|
|
746
|
+
'pictureInPictureEnabled' in document;
|
|
747
|
+
const hasSafariPiP = 'webkitSupportsPresentationMode' in this.video &&
|
|
748
|
+
typeof this.video.webkitSupportsPresentationMode === 'function' &&
|
|
749
|
+
this.video.webkitSupportsPresentationMode('picture-in-picture');
|
|
750
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
751
|
+
const isIOS = /iphone|ipad|ipod/.test(userAgent);
|
|
752
|
+
const isAndroid = /android/.test(userAgent);
|
|
753
|
+
const isChrome = /chrome/.test(userAgent) && !/edge/.test(userAgent);
|
|
754
|
+
const isSafari = /safari/.test(userAgent) && !/chrome/.test(userAgent);
|
|
755
|
+
const isFirefox = /firefox/.test(userAgent);
|
|
756
|
+
const iosSupport = isIOS && isSafari && hasSafariPiP;
|
|
757
|
+
const androidChromeSupport = isAndroid && isChrome && hasStandardPiP;
|
|
758
|
+
const firefoxSupport = isFirefox && hasStandardPiP && !this.isMobileDevice();
|
|
759
|
+
this.debugLog('PiP Support Detection:', {
|
|
760
|
+
hasStandardPiP,
|
|
761
|
+
hasSafariPiP,
|
|
762
|
+
isIOS,
|
|
763
|
+
isAndroid,
|
|
764
|
+
isChrome,
|
|
765
|
+
isSafari,
|
|
766
|
+
isFirefox,
|
|
767
|
+
iosSupport,
|
|
768
|
+
androidChromeSupport,
|
|
769
|
+
firefoxSupport,
|
|
770
|
+
overall: hasStandardPiP || iosSupport || androidChromeSupport || firefoxSupport
|
|
771
|
+
});
|
|
772
|
+
return hasStandardPiP || iosSupport || androidChromeSupport || firefoxSupport;
|
|
773
|
+
}
|
|
774
|
+
isMobileDevice() {
|
|
775
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
776
|
+
return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/.test(userAgent) ||
|
|
777
|
+
(!!(navigator.maxTouchPoints && navigator.maxTouchPoints > 2) && /macintosh/.test(userAgent));
|
|
778
|
+
}
|
|
779
|
+
async enterPictureInPicture() {
|
|
780
|
+
if (!this.video) {
|
|
781
|
+
throw new Error('Video element not available');
|
|
782
|
+
}
|
|
783
|
+
if (!this.isPictureInPictureSupported()) {
|
|
784
|
+
throw new Error('Picture-in-Picture not supported on this device/browser');
|
|
785
|
+
}
|
|
745
786
|
try {
|
|
746
|
-
if (this.video
|
|
787
|
+
if ('requestPictureInPicture' in this.video) {
|
|
747
788
|
await this.video.requestPictureInPicture();
|
|
789
|
+
this.debugLog('PiP entered using standard API');
|
|
790
|
+
return;
|
|
748
791
|
}
|
|
749
|
-
|
|
750
|
-
|
|
792
|
+
if ('webkitSetPresentationMode' in this.video) {
|
|
793
|
+
this.video.webkitSetPresentationMode('picture-in-picture');
|
|
794
|
+
this.debugLog('PiP entered using WebKit API');
|
|
795
|
+
return;
|
|
751
796
|
}
|
|
797
|
+
throw new Error('No supported PiP API available');
|
|
752
798
|
}
|
|
753
799
|
catch (error) {
|
|
754
|
-
|
|
800
|
+
this.debugError('Failed to enter PiP:', error.message);
|
|
755
801
|
throw error;
|
|
756
802
|
}
|
|
757
803
|
}
|
|
758
804
|
async exitPictureInPicture() {
|
|
759
805
|
try {
|
|
760
|
-
|
|
806
|
+
const inStandardPiP = document.pictureInPictureElement;
|
|
807
|
+
const inWebkitPiP = this.video &&
|
|
808
|
+
'webkitPresentationMode' in this.video &&
|
|
809
|
+
this.video.webkitPresentationMode === 'picture-in-picture';
|
|
810
|
+
if (!inStandardPiP && !inWebkitPiP) {
|
|
811
|
+
this.debugLog('Not currently in PiP mode');
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
if (inStandardPiP && 'exitPictureInPicture' in document) {
|
|
761
815
|
await document.exitPictureInPicture();
|
|
816
|
+
this.debugLog('PiP exited using standard API');
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
if (inWebkitPiP && this.video && 'webkitSetPresentationMode' in this.video) {
|
|
820
|
+
this.video.webkitSetPresentationMode('inline');
|
|
821
|
+
this.debugLog('PiP exited using WebKit API');
|
|
822
|
+
return;
|
|
762
823
|
}
|
|
763
824
|
}
|
|
764
825
|
catch (error) {
|
|
@@ -1459,19 +1520,51 @@ export class WebPlayer extends BasePlayer {
|
|
|
1459
1520
|
.uvf-video-container {
|
|
1460
1521
|
position: relative;
|
|
1461
1522
|
width: 100%;
|
|
1462
|
-
|
|
1523
|
+
aspect-ratio: 16 / 9;
|
|
1463
1524
|
background: radial-gradient(ellipse at center, #1a1a2e 0%, #000 100%);
|
|
1464
1525
|
overflow: hidden;
|
|
1526
|
+
display: flex;
|
|
1527
|
+
align-items: center;
|
|
1528
|
+
justify-content: center;
|
|
1465
1529
|
}
|
|
1466
1530
|
|
|
1467
1531
|
.uvf-video {
|
|
1468
1532
|
position: absolute;
|
|
1469
|
-
top:
|
|
1470
|
-
left:
|
|
1471
|
-
|
|
1472
|
-
|
|
1533
|
+
top: 50%;
|
|
1534
|
+
left: 50%;
|
|
1535
|
+
transform: translate(-50%, -50%);
|
|
1536
|
+
max-width: 100%;
|
|
1537
|
+
max-height: 100%;
|
|
1538
|
+
width: auto;
|
|
1539
|
+
height: auto;
|
|
1473
1540
|
background: #000;
|
|
1474
1541
|
object-fit: contain;
|
|
1542
|
+
/* Ensure proper centering and scaling */
|
|
1543
|
+
object-position: center;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
/* Mobile-specific video centering improvements */
|
|
1547
|
+
@media screen and (max-width: 767px) {
|
|
1548
|
+
.uvf-video {
|
|
1549
|
+
/* Force full width/height on mobile for better centering */
|
|
1550
|
+
width: 100%;
|
|
1551
|
+
height: 100%;
|
|
1552
|
+
top: 0;
|
|
1553
|
+
left: 0;
|
|
1554
|
+
transform: none;
|
|
1555
|
+
object-fit: contain;
|
|
1556
|
+
object-position: center;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
.uvf-video-container {
|
|
1560
|
+
/* Remove aspect ratio constraint on mobile for full height usage */
|
|
1561
|
+
aspect-ratio: unset;
|
|
1562
|
+
min-height: 100%;
|
|
1563
|
+
height: 100%;
|
|
1564
|
+
display: flex;
|
|
1565
|
+
align-items: center;
|
|
1566
|
+
justify-content: center;
|
|
1567
|
+
}
|
|
1475
1568
|
}
|
|
1476
1569
|
|
|
1477
1570
|
.uvf-watermark-layer {
|
|
@@ -1658,6 +1751,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
1658
1751
|
width: 100%;
|
|
1659
1752
|
position: relative;
|
|
1660
1753
|
cursor: pointer;
|
|
1754
|
+
padding: 16px 0;
|
|
1661
1755
|
overflow: visible;
|
|
1662
1756
|
}
|
|
1663
1757
|
|
|
@@ -1771,105 +1865,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
1771
1865
|
|
|
1772
1866
|
/* Mobile responsive design with enhanced touch targets */
|
|
1773
1867
|
@media (max-width: 768px) {
|
|
1774
|
-
.uvf-
|
|
1775
|
-
|
|
1776
|
-
width: 100% !important;
|
|
1777
|
-
height: auto !important;
|
|
1778
|
-
min-height: 200px;
|
|
1868
|
+
.uvf-progress-bar-wrapper {
|
|
1869
|
+
padding: 20px 0; /* Larger touch area */
|
|
1779
1870
|
}
|
|
1780
1871
|
|
|
1781
|
-
.uvf-video-container {
|
|
1782
|
-
width: 100% !important;
|
|
1783
|
-
height: auto !important;
|
|
1784
|
-
aspect-ratio: 16/9;
|
|
1785
|
-
position: relative;
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
.uvf-video {
|
|
1789
|
-
width: 100% !important;
|
|
1790
|
-
height: 100% !important;
|
|
1791
|
-
object-fit: contain;
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
/* Fix controls bar positioning */
|
|
1795
|
-
.uvf-controls-bar {
|
|
1796
|
-
position: absolute;
|
|
1797
|
-
bottom: 0;
|
|
1798
|
-
left: 0;
|
|
1799
|
-
right: 0;
|
|
1800
|
-
padding: 8px 12px !important;
|
|
1801
|
-
padding-bottom: max(8px, env(safe-area-inset-bottom)) !important;
|
|
1802
|
-
background: linear-gradient(to top,
|
|
1803
|
-
rgba(0,0,0,0.9) 0%,
|
|
1804
|
-
rgba(0,0,0,0.7) 70%,
|
|
1805
|
-
transparent 100%) !important;
|
|
1806
|
-
z-index: 1000;
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
/* Ensure controls are properly sized */
|
|
1810
|
-
.uvf-controls-row {
|
|
1811
|
-
display: flex;
|
|
1812
|
-
align-items: center;
|
|
1813
|
-
gap: 8px;
|
|
1814
|
-
flex-wrap: nowrap;
|
|
1815
|
-
min-height: 44px; /* Minimum touch target size */
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
/* Touch-friendly button sizes */
|
|
1819
|
-
.uvf-control-btn {
|
|
1820
|
-
width: 44px !important;
|
|
1821
|
-
height: 44px !important;
|
|
1822
|
-
min-width: 44px !important;
|
|
1823
|
-
min-height: 44px !important;
|
|
1824
|
-
border-radius: 22px !important;
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
.uvf-control-btn.play-pause {
|
|
1828
|
-
width: 52px !important;
|
|
1829
|
-
height: 52px !important;
|
|
1830
|
-
min-width: 52px !important;
|
|
1831
|
-
min-height: 52px !important;
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
/* Progress bar adjustments */
|
|
1835
|
-
.uvf-progress-section {
|
|
1836
|
-
margin-bottom: 12px !important;
|
|
1837
|
-
padding: 0 8px;
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
.uvf-progress-bar {
|
|
1841
|
-
height: 4px !important;
|
|
1842
|
-
margin-bottom: 8px;
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
/* Time display adjustments */
|
|
1846
|
-
.uvf-time-display {
|
|
1847
|
-
font-size: 12px !important;
|
|
1848
|
-
min-width: 90px !important;
|
|
1849
|
-
padding: 4px 8px !important;
|
|
1850
|
-
background: rgba(0,0,0,0.5);
|
|
1851
|
-
border-radius: 12px;
|
|
1852
|
-
margin: 0 4px;
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
/* Right controls spacing */
|
|
1856
|
-
.uvf-right-controls {
|
|
1857
|
-
gap: 6px !important;
|
|
1858
|
-
margin-left: auto;
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
/* Hide non-essential elements on mobile */
|
|
1862
|
-
.uvf-quality-badge {
|
|
1863
|
-
display: none !important;
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
/* Ensure settings menu is accessible */
|
|
1867
|
-
.uvf-settings-menu {
|
|
1868
|
-
bottom: 60px !important;
|
|
1869
|
-
right: 12px !important;
|
|
1870
|
-
max-height: 60vh !important;
|
|
1871
|
-
min-width: 160px !important;
|
|
1872
|
-
}
|
|
1873
1872
|
.uvf-progress-bar {
|
|
1874
1873
|
height: 3px; /* Slightly thicker on mobile */
|
|
1875
1874
|
}
|
|
@@ -1877,31 +1876,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
1877
1876
|
.uvf-progress-bar-wrapper:hover .uvf-progress-bar {
|
|
1878
1877
|
height: 5px;
|
|
1879
1878
|
}
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
/* Mobile Landscape */
|
|
1883
|
-
@media screen and (max-width: 767px) and (orientation: landscape) {
|
|
1884
|
-
.uvf-controls-bar {
|
|
1885
|
-
padding: 6px 10px !important;
|
|
1886
|
-
padding-bottom: max(6px, env(safe-area-inset-bottom)) !important;
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
.uvf-control-btn {
|
|
1890
|
-
width: 40px !important;
|
|
1891
|
-
height: 40px !important;
|
|
1892
|
-
min-width: 40px !important;
|
|
1893
|
-
min-height: 40px !important;
|
|
1894
|
-
}
|
|
1895
1879
|
|
|
1896
|
-
.uvf-control-btn.play-pause {
|
|
1897
|
-
width: 46px !important;
|
|
1898
|
-
height: 46px !important;
|
|
1899
|
-
}
|
|
1900
|
-
|
|
1901
|
-
.uvf-time-display {
|
|
1902
|
-
font-size: 11px !important;
|
|
1903
|
-
min-width: 80px !important;
|
|
1904
|
-
}
|
|
1905
1880
|
}
|
|
1906
1881
|
|
|
1907
1882
|
/* Controls Row */
|
|
@@ -2631,28 +2606,276 @@ export class WebPlayer extends BasePlayer {
|
|
|
2631
2606
|
}
|
|
2632
2607
|
}
|
|
2633
2608
|
|
|
2609
|
+
/* Safe Area Variables - Support for modern mobile devices */
|
|
2610
|
+
:root {
|
|
2611
|
+
/* iOS Safe Area Fallbacks */
|
|
2612
|
+
--uvf-safe-area-top: env(safe-area-inset-top, 0px);
|
|
2613
|
+
--uvf-safe-area-right: env(safe-area-inset-right, 0px);
|
|
2614
|
+
--uvf-safe-area-bottom: env(safe-area-inset-bottom, 0px);
|
|
2615
|
+
--uvf-safe-area-left: env(safe-area-inset-left, 0px);
|
|
2616
|
+
|
|
2617
|
+
/* Dynamic Viewport Support */
|
|
2618
|
+
--uvf-dvh: 1dvh;
|
|
2619
|
+
--uvf-svh: 1svh;
|
|
2620
|
+
--uvf-lvh: 1lvh;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
/* Cross-Browser Mobile Viewport Fixes */
|
|
2624
|
+
|
|
2625
|
+
/* Modern browsers with dynamic viewport support */
|
|
2626
|
+
@supports (height: 100dvh) {
|
|
2627
|
+
.uvf-player-wrapper,
|
|
2628
|
+
.uvf-video-container {
|
|
2629
|
+
height: 100dvh;
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
.uvf-responsive-container {
|
|
2633
|
+
height: 100dvh;
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
|
|
2637
|
+
/* iOS Safari specific fixes - address bar handling */
|
|
2638
|
+
@supports (-webkit-appearance: none) {
|
|
2639
|
+
.uvf-player-wrapper.uvf-fullscreen,
|
|
2640
|
+
.uvf-video-container.uvf-fullscreen {
|
|
2641
|
+
height: -webkit-fill-available;
|
|
2642
|
+
min-height: -webkit-fill-available;
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
/* Handle iOS Safari's dynamic address bar */
|
|
2646
|
+
@media screen and (max-width: 767px) {
|
|
2647
|
+
.uvf-responsive-container {
|
|
2648
|
+
height: -webkit-fill-available;
|
|
2649
|
+
min-height: 100vh;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
.uvf-player-wrapper {
|
|
2653
|
+
height: -webkit-fill-available;
|
|
2654
|
+
min-height: 100vh;
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
/* Android Chrome specific fixes */
|
|
2660
|
+
@supports (display: -webkit-box) {
|
|
2661
|
+
.uvf-responsive-container {
|
|
2662
|
+
min-height: 100vh;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
/* Fix for Android Chrome's address bar behavior */
|
|
2666
|
+
@media screen and (max-width: 767px) {
|
|
2667
|
+
.uvf-video-container {
|
|
2668
|
+
min-height: calc(100vh - 56px); /* Chrome mobile address bar height */
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
/* Samsung Internet Browser fixes */
|
|
2674
|
+
@media screen and (-webkit-min-device-pixel-ratio: 1) {
|
|
2675
|
+
@media screen and (max-width: 767px) {
|
|
2676
|
+
.uvf-responsive-container {
|
|
2677
|
+
position: fixed;
|
|
2678
|
+
top: 0;
|
|
2679
|
+
left: 0;
|
|
2680
|
+
width: 100vw;
|
|
2681
|
+
height: 100vh;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
/* Universal mobile fixes for all browsers */
|
|
2687
|
+
@media screen and (max-width: 767px) {
|
|
2688
|
+
html, body {
|
|
2689
|
+
overflow-x: hidden;
|
|
2690
|
+
/* Prevent iOS Safari address bar bounce */
|
|
2691
|
+
position: fixed;
|
|
2692
|
+
height: 100%;
|
|
2693
|
+
width: 100%;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
.uvf-player-wrapper {
|
|
2697
|
+
/* Prevent scroll bounce on iOS */
|
|
2698
|
+
-webkit-overflow-scrolling: touch;
|
|
2699
|
+
overflow: hidden;
|
|
2700
|
+
|
|
2701
|
+
/* Prevent zoom on double tap */
|
|
2702
|
+
touch-action: manipulation;
|
|
2703
|
+
|
|
2704
|
+
/* Ensure full viewport usage */
|
|
2705
|
+
position: relative;
|
|
2706
|
+
width: 100vw;
|
|
2707
|
+
height: 100vh;
|
|
2708
|
+
|
|
2709
|
+
/* iOS Safari fix for viewport height */
|
|
2710
|
+
min-height: -webkit-fill-available;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
.uvf-video-container {
|
|
2714
|
+
/* Full viewport container */
|
|
2715
|
+
width: 100vw;
|
|
2716
|
+
height: 100vh;
|
|
2717
|
+
min-height: -webkit-fill-available;
|
|
2718
|
+
position: relative;
|
|
2719
|
+
overflow: hidden;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
.uvf-video {
|
|
2723
|
+
/* Prevent video from being selectable */
|
|
2724
|
+
-webkit-user-select: none;
|
|
2725
|
+
-moz-user-select: none;
|
|
2726
|
+
-ms-user-select: none;
|
|
2727
|
+
user-select: none;
|
|
2728
|
+
|
|
2729
|
+
/* Ensure hardware acceleration */
|
|
2730
|
+
-webkit-transform: translateZ(0);
|
|
2731
|
+
transform: translateZ(0);
|
|
2732
|
+
|
|
2733
|
+
/* Full viewport video with proper centering */
|
|
2734
|
+
width: 100%;
|
|
2735
|
+
height: 100%;
|
|
2736
|
+
object-fit: contain;
|
|
2737
|
+
object-position: center center;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
/* Fix for controls being cut off by virtual keyboard */
|
|
2741
|
+
.uvf-controls-bar {
|
|
2742
|
+
position: fixed !important;
|
|
2743
|
+
bottom: var(--uvf-safe-area-bottom, 0) !important;
|
|
2744
|
+
left: var(--uvf-safe-area-left, 0) !important;
|
|
2745
|
+
right: var(--uvf-safe-area-right, 0) !important;
|
|
2746
|
+
width: auto !important;
|
|
2747
|
+
z-index: 9999;
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
/* Top controls safe area positioning */
|
|
2751
|
+
.uvf-top-controls {
|
|
2752
|
+
position: fixed !important;
|
|
2753
|
+
top: var(--uvf-safe-area-top, 10px) !important;
|
|
2754
|
+
right: var(--uvf-safe-area-right, 10px) !important;
|
|
2755
|
+
z-index: 9999;
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
.uvf-title-bar {
|
|
2759
|
+
position: fixed !important;
|
|
2760
|
+
top: var(--uvf-safe-area-top, 10px) !important;
|
|
2761
|
+
left: var(--uvf-safe-area-left, 10px) !important;
|
|
2762
|
+
right: calc(120px + var(--uvf-safe-area-right, 10px)) !important; /* Leave space for top controls */
|
|
2763
|
+
z-index: 9999;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
/* Ensure controls stay above virtual keyboards */
|
|
2767
|
+
@supports (bottom: env(keyboard-inset-height)) {
|
|
2768
|
+
.uvf-controls-bar {
|
|
2769
|
+
bottom: max(var(--uvf-safe-area-bottom, 0), env(keyboard-inset-height, 0)) !important;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
/* Enhanced safe area support for newer devices */
|
|
2774
|
+
@supports (padding: max(0px)) {
|
|
2775
|
+
.uvf-controls-bar {
|
|
2776
|
+
padding-bottom: max(16px, calc(16px + var(--uvf-safe-area-bottom, 0)));
|
|
2777
|
+
padding-left: max(12px, calc(12px + var(--uvf-safe-area-left, 0)));
|
|
2778
|
+
padding-right: max(12px, calc(12px + var(--uvf-safe-area-right, 0)));
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
.uvf-top-controls {
|
|
2782
|
+
top: max(10px, calc(10px + var(--uvf-safe-area-top, 0))) !important;
|
|
2783
|
+
right: max(10px, calc(10px + var(--uvf-safe-area-right, 0))) !important;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
.uvf-title-bar {
|
|
2787
|
+
top: max(10px, calc(10px + var(--uvf-safe-area-top, 0))) !important;
|
|
2788
|
+
left: max(10px, calc(10px + var(--uvf-safe-area-left, 0))) !important;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
/* Specific fixes for iPhone X series and newer with notches */
|
|
2794
|
+
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
2795
|
+
@supports (top: env(safe-area-inset-top)) {
|
|
2796
|
+
.uvf-responsive-container,
|
|
2797
|
+
.uvf-player-wrapper,
|
|
2798
|
+
.uvf-video-container {
|
|
2799
|
+
height: 100vh;
|
|
2800
|
+
height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
/* Landscape orientation fixes for mobile */
|
|
2806
|
+
@media screen and (max-width: 767px) and (orientation: landscape) {
|
|
2807
|
+
html, body {
|
|
2808
|
+
height: 100vh;
|
|
2809
|
+
overflow: hidden;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
.uvf-responsive-container,
|
|
2813
|
+
.uvf-player-wrapper,
|
|
2814
|
+
.uvf-video-container {
|
|
2815
|
+
height: 100vh;
|
|
2816
|
+
width: 100vw;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
@supports (height: 100dvh) {
|
|
2820
|
+
.uvf-responsive-container,
|
|
2821
|
+
.uvf-player-wrapper,
|
|
2822
|
+
.uvf-video-container {
|
|
2823
|
+
height: 100dvh;
|
|
2824
|
+
width: 100dvw;
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2634
2829
|
/* Enhanced Responsive Media Queries with UX Best Practices */
|
|
2635
|
-
/* Mobile devices (portrait) - Enhanced UX */
|
|
2830
|
+
/* Mobile devices (portrait) - Enhanced UX with Safe Areas */
|
|
2636
2831
|
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
2637
2832
|
.uvf-responsive-container {
|
|
2638
2833
|
padding: 0;
|
|
2639
2834
|
width: 100vw !important;
|
|
2835
|
+
height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
2640
2836
|
margin: 0;
|
|
2837
|
+
position: relative;
|
|
2838
|
+
overflow: hidden;
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
@supports (height: 100dvh) {
|
|
2842
|
+
.uvf-responsive-container {
|
|
2843
|
+
height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
2844
|
+
}
|
|
2641
2845
|
}
|
|
2642
2846
|
|
|
2643
2847
|
.uvf-responsive-container .uvf-player-wrapper {
|
|
2644
2848
|
width: 100vw !important;
|
|
2849
|
+
height: 100% !important;
|
|
2850
|
+
min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
@supports (height: 100dvh) {
|
|
2854
|
+
.uvf-responsive-container .uvf-player-wrapper {
|
|
2855
|
+
min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
2856
|
+
}
|
|
2645
2857
|
}
|
|
2646
2858
|
|
|
2647
2859
|
.uvf-responsive-container .uvf-video-container {
|
|
2648
2860
|
width: 100vw !important;
|
|
2649
|
-
|
|
2861
|
+
height: 100% !important;
|
|
2862
|
+
aspect-ratio: unset !important;
|
|
2863
|
+
min-height: inherit;
|
|
2650
2864
|
}
|
|
2651
2865
|
|
|
2652
|
-
/* Enhanced mobile controls bar with
|
|
2866
|
+
/* Enhanced mobile controls bar with safe area padding */
|
|
2653
2867
|
.uvf-controls-bar {
|
|
2868
|
+
position: absolute;
|
|
2869
|
+
bottom: 0;
|
|
2870
|
+
left: 0;
|
|
2871
|
+
right: 0;
|
|
2654
2872
|
padding: 16px 12px;
|
|
2873
|
+
padding-bottom: calc(16px + var(--uvf-safe-area-bottom));
|
|
2874
|
+
padding-left: calc(12px + var(--uvf-safe-area-left));
|
|
2875
|
+
padding-right: calc(12px + var(--uvf-safe-area-right));
|
|
2655
2876
|
background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
|
|
2877
|
+
box-sizing: border-box;
|
|
2878
|
+
z-index: 1000;
|
|
2656
2879
|
}
|
|
2657
2880
|
|
|
2658
2881
|
.uvf-progress-section {
|
|
@@ -2824,11 +3047,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
2824
3047
|
}
|
|
2825
3048
|
}
|
|
2826
3049
|
|
|
2827
|
-
/* Enhanced top controls for mobile with
|
|
3050
|
+
/* Enhanced top controls for mobile with safe area support */
|
|
2828
3051
|
.uvf-top-controls {
|
|
2829
3052
|
position: absolute;
|
|
2830
|
-
top: 12px;
|
|
2831
|
-
right: 12px;
|
|
3053
|
+
top: calc(12px + var(--uvf-safe-area-top));
|
|
3054
|
+
right: calc(12px + var(--uvf-safe-area-right));
|
|
2832
3055
|
display: flex;
|
|
2833
3056
|
align-items: center;
|
|
2834
3057
|
gap: 8px;
|
|
@@ -2856,9 +3079,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
2856
3079
|
display: flex;
|
|
2857
3080
|
}
|
|
2858
3081
|
|
|
2859
|
-
/* Enhanced title bar for mobile */
|
|
3082
|
+
/* Enhanced title bar for mobile with safe area support */
|
|
2860
3083
|
.uvf-title-bar {
|
|
2861
3084
|
padding: 12px;
|
|
3085
|
+
padding-top: calc(12px + var(--uvf-safe-area-top));
|
|
3086
|
+
padding-left: calc(12px + var(--uvf-safe-area-left));
|
|
3087
|
+
padding-right: calc(12px + var(--uvf-safe-area-right));
|
|
2862
3088
|
}
|
|
2863
3089
|
|
|
2864
3090
|
.uvf-video-title {
|
|
@@ -2985,30 +3211,55 @@ export class WebPlayer extends BasePlayer {
|
|
|
2985
3211
|
}
|
|
2986
3212
|
}
|
|
2987
3213
|
|
|
2988
|
-
/* Mobile devices (landscape) - Optimized for fullscreen viewing */
|
|
3214
|
+
/* Mobile devices (landscape) - Optimized for fullscreen viewing with safe areas */
|
|
2989
3215
|
@media screen and (max-width: 767px) and (orientation: landscape) {
|
|
2990
3216
|
.uvf-responsive-container {
|
|
2991
3217
|
width: 100vw !important;
|
|
2992
|
-
height: 100vh
|
|
3218
|
+
height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
2993
3219
|
margin: 0;
|
|
2994
3220
|
padding: 0;
|
|
3221
|
+
position: relative;
|
|
3222
|
+
overflow: hidden;
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
@supports (height: 100dvh) {
|
|
3226
|
+
.uvf-responsive-container {
|
|
3227
|
+
height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
3228
|
+
}
|
|
2995
3229
|
}
|
|
2996
3230
|
|
|
2997
3231
|
.uvf-responsive-container .uvf-player-wrapper {
|
|
2998
3232
|
width: 100vw !important;
|
|
2999
|
-
height:
|
|
3233
|
+
height: 100% !important;
|
|
3234
|
+
min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
@supports (height: 100dvh) {
|
|
3238
|
+
.uvf-responsive-container .uvf-player-wrapper {
|
|
3239
|
+
min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
3240
|
+
}
|
|
3000
3241
|
}
|
|
3001
3242
|
|
|
3002
3243
|
.uvf-responsive-container .uvf-video-container {
|
|
3003
3244
|
width: 100vw !important;
|
|
3004
|
-
height:
|
|
3245
|
+
height: 100% !important;
|
|
3005
3246
|
aspect-ratio: unset !important;
|
|
3247
|
+
min-height: inherit;
|
|
3006
3248
|
}
|
|
3007
3249
|
|
|
3008
|
-
/* Compact controls for landscape */
|
|
3250
|
+
/* Compact controls for landscape with safe area padding */
|
|
3009
3251
|
.uvf-controls-bar {
|
|
3252
|
+
position: absolute;
|
|
3253
|
+
bottom: 0;
|
|
3254
|
+
left: 0;
|
|
3255
|
+
right: 0;
|
|
3010
3256
|
padding: 10px 12px;
|
|
3257
|
+
padding-bottom: calc(10px + var(--uvf-safe-area-bottom));
|
|
3258
|
+
padding-left: calc(12px + var(--uvf-safe-area-left));
|
|
3259
|
+
padding-right: calc(12px + var(--uvf-safe-area-right));
|
|
3011
3260
|
background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
|
|
3261
|
+
box-sizing: border-box;
|
|
3262
|
+
z-index: 1000;
|
|
3012
3263
|
}
|
|
3013
3264
|
|
|
3014
3265
|
.uvf-progress-section {
|
|
@@ -3044,13 +3295,20 @@ export class WebPlayer extends BasePlayer {
|
|
|
3044
3295
|
height: 22px;
|
|
3045
3296
|
}
|
|
3046
3297
|
|
|
3047
|
-
/* Compact top controls */
|
|
3298
|
+
/* Compact top controls with safe area padding */
|
|
3048
3299
|
.uvf-top-controls {
|
|
3049
|
-
top: 8px;
|
|
3050
|
-
right: 12px;
|
|
3300
|
+
top: calc(8px + var(--uvf-safe-area-top));
|
|
3301
|
+
right: calc(12px + var(--uvf-safe-area-right));
|
|
3051
3302
|
gap: 6px;
|
|
3052
3303
|
}
|
|
3053
3304
|
|
|
3305
|
+
.uvf-title-bar {
|
|
3306
|
+
padding: 8px 12px;
|
|
3307
|
+
padding-top: calc(8px + var(--uvf-safe-area-top));
|
|
3308
|
+
padding-left: calc(12px + var(--uvf-safe-area-left));
|
|
3309
|
+
padding-right: calc(12px + var(--uvf-safe-area-right));
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3054
3312
|
.uvf-top-btn {
|
|
3055
3313
|
width: 40px;
|
|
3056
3314
|
height: 40px;
|
|
@@ -3097,17 +3355,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
3097
3355
|
height: 16px;
|
|
3098
3356
|
}
|
|
3099
3357
|
}
|
|
3100
|
-
|
|
3101
|
-
.uvf-video-container {
|
|
3102
|
-
aspect-ratio: auto !important;
|
|
3103
|
-
height: 100% !important;
|
|
3104
|
-
}
|
|
3105
|
-
.uvf-controls-bar {
|
|
3106
|
-
bottom: 0 !important;
|
|
3107
|
-
padding-bottom: 10px !important;
|
|
3108
|
-
}
|
|
3109
|
-
}
|
|
3110
|
-
|
|
3358
|
+
|
|
3111
3359
|
/* Tablet devices - Enhanced UX with desktop features */
|
|
3112
3360
|
@media screen and (min-width: 768px) and (max-width: 1023px) {
|
|
3113
3361
|
.uvf-controls-bar {
|
|
@@ -3888,7 +4136,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
3888
4136
|
pipBtn.id = 'uvf-pip-btn';
|
|
3889
4137
|
pipBtn.title = 'Picture-in-Picture';
|
|
3890
4138
|
pipBtn.innerHTML = '<svg viewBox="0 0 24 24"><path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z"/></svg>';
|
|
3891
|
-
|
|
4139
|
+
if (this.isPictureInPictureSupported()) {
|
|
4140
|
+
rightControls.appendChild(pipBtn);
|
|
4141
|
+
this.debugLog('PiP button added - support detected');
|
|
4142
|
+
}
|
|
4143
|
+
else {
|
|
4144
|
+
this.debugLog('PiP button not added - no support detected');
|
|
4145
|
+
}
|
|
3892
4146
|
const fullscreenBtn = document.createElement('button');
|
|
3893
4147
|
fullscreenBtn.className = 'uvf-control-btn';
|
|
3894
4148
|
fullscreenBtn.id = 'uvf-fullscreen-btn';
|
|
@@ -5001,17 +5255,41 @@ export class WebPlayer extends BasePlayer {
|
|
|
5001
5255
|
this.setAutoQuality(true);
|
|
5002
5256
|
}
|
|
5003
5257
|
}
|
|
5258
|
+
isPictureInPictureActive() {
|
|
5259
|
+
const inStandardPiP = !!document.pictureInPictureElement;
|
|
5260
|
+
const inWebkitPiP = !!(this.video &&
|
|
5261
|
+
'webkitPresentationMode' in this.video &&
|
|
5262
|
+
this.video.webkitPresentationMode === 'picture-in-picture');
|
|
5263
|
+
return inStandardPiP || inWebkitPiP;
|
|
5264
|
+
}
|
|
5004
5265
|
async togglePiP() {
|
|
5266
|
+
if (!this.isPictureInPictureSupported()) {
|
|
5267
|
+
this.showShortcutIndicator('PiP not supported');
|
|
5268
|
+
this.debugWarn('PiP not supported on this device/browser');
|
|
5269
|
+
return;
|
|
5270
|
+
}
|
|
5005
5271
|
try {
|
|
5006
|
-
if (
|
|
5272
|
+
if (this.isPictureInPictureActive()) {
|
|
5007
5273
|
await this.exitPictureInPicture();
|
|
5274
|
+
this.showShortcutIndicator('Exit PiP');
|
|
5275
|
+
this.debugLog('PiP deactivated');
|
|
5008
5276
|
}
|
|
5009
5277
|
else {
|
|
5010
5278
|
await this.enterPictureInPicture();
|
|
5279
|
+
this.showShortcutIndicator('Enter PiP');
|
|
5280
|
+
this.debugLog('PiP activated');
|
|
5011
5281
|
}
|
|
5012
5282
|
}
|
|
5013
5283
|
catch (error) {
|
|
5014
|
-
|
|
5284
|
+
const errorMessage = error.message;
|
|
5285
|
+
this.showShortcutIndicator(`PiP Error: ${errorMessage}`);
|
|
5286
|
+
this.debugError('PiP toggle failed:', errorMessage);
|
|
5287
|
+
if (errorMessage.includes('not supported')) {
|
|
5288
|
+
this.showShortcutIndicator('PiP not supported');
|
|
5289
|
+
}
|
|
5290
|
+
else if (errorMessage.includes('user gesture')) {
|
|
5291
|
+
this.showShortcutIndicator('PiP requires user interaction');
|
|
5292
|
+
}
|
|
5015
5293
|
}
|
|
5016
5294
|
}
|
|
5017
5295
|
setupCastContextSafe() {
|