unified-video-framework 1.4.119 → 1.4.120
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.
|
@@ -906,122 +906,26 @@ export class WebPlayer extends BasePlayer {
|
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
908
|
|
|
909
|
-
/**
|
|
910
|
-
* Checks if Picture-in-Picture is supported on the current device/browser
|
|
911
|
-
*/
|
|
912
|
-
isPictureInPictureSupported(): boolean {
|
|
913
|
-
if (!this.video) return false;
|
|
914
|
-
|
|
915
|
-
// Check standard PiP API support
|
|
916
|
-
const hasStandardPiP = 'requestPictureInPicture' in this.video &&
|
|
917
|
-
'pictureInPictureEnabled' in document;
|
|
918
|
-
|
|
919
|
-
// Check for Safari/WebKit PiP support
|
|
920
|
-
const hasSafariPiP = 'webkitSupportsPresentationMode' in this.video &&
|
|
921
|
-
typeof (this.video as any).webkitSupportsPresentationMode === 'function' &&
|
|
922
|
-
(this.video as any).webkitSupportsPresentationMode('picture-in-picture');
|
|
923
|
-
|
|
924
|
-
// Mobile browser detection for PiP support
|
|
925
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
926
|
-
const isIOS = /iphone|ipad|ipod/.test(userAgent);
|
|
927
|
-
const isAndroid = /android/.test(userAgent);
|
|
928
|
-
const isChrome = /chrome/.test(userAgent) && !/edge/.test(userAgent);
|
|
929
|
-
const isSafari = /safari/.test(userAgent) && !/chrome/.test(userAgent);
|
|
930
|
-
const isFirefox = /firefox/.test(userAgent);
|
|
931
|
-
|
|
932
|
-
// iOS Safari supports PiP from iOS 14+ (with video element)
|
|
933
|
-
const iosSupport = isIOS && isSafari && hasSafariPiP;
|
|
934
|
-
|
|
935
|
-
// Android Chrome supports PiP from Chrome 70+
|
|
936
|
-
const androidChromeSupport = isAndroid && isChrome && hasStandardPiP;
|
|
937
|
-
|
|
938
|
-
// Firefox mobile doesn't support PiP yet
|
|
939
|
-
const firefoxSupport = isFirefox && hasStandardPiP && !this.isMobileDevice();
|
|
940
|
-
|
|
941
|
-
this.debugLog('PiP Support Detection:', {
|
|
942
|
-
hasStandardPiP,
|
|
943
|
-
hasSafariPiP,
|
|
944
|
-
isIOS,
|
|
945
|
-
isAndroid,
|
|
946
|
-
isChrome,
|
|
947
|
-
isSafari,
|
|
948
|
-
isFirefox,
|
|
949
|
-
iosSupport,
|
|
950
|
-
androidChromeSupport,
|
|
951
|
-
firefoxSupport,
|
|
952
|
-
overall: hasStandardPiP || iosSupport || androidChromeSupport || firefoxSupport
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
return hasStandardPiP || iosSupport || androidChromeSupport || firefoxSupport;
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
/**
|
|
959
|
-
* Detects if running on a mobile device
|
|
960
|
-
*/
|
|
961
|
-
private isMobileDevice(): boolean {
|
|
962
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
963
|
-
return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/.test(userAgent) ||
|
|
964
|
-
(!!(navigator.maxTouchPoints && navigator.maxTouchPoints > 2) && /macintosh/.test(userAgent));
|
|
965
|
-
}
|
|
966
|
-
|
|
967
909
|
async enterPictureInPicture(): Promise<void> {
|
|
968
|
-
if (!this.video)
|
|
969
|
-
throw new Error('Video element not available');
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
if (!this.isPictureInPictureSupported()) {
|
|
973
|
-
throw new Error('Picture-in-Picture not supported on this device/browser');
|
|
974
|
-
}
|
|
910
|
+
if (!this.video) return;
|
|
975
911
|
|
|
976
912
|
try {
|
|
977
|
-
|
|
978
|
-
if ('requestPictureInPicture' in this.video) {
|
|
913
|
+
if ((this.video as any).requestPictureInPicture) {
|
|
979
914
|
await (this.video as any).requestPictureInPicture();
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
// Safari/WebKit PiP API
|
|
985
|
-
if ('webkitSetPresentationMode' in this.video) {
|
|
986
|
-
(this.video as any).webkitSetPresentationMode('picture-in-picture');
|
|
987
|
-
this.debugLog('PiP entered using WebKit API');
|
|
988
|
-
return;
|
|
915
|
+
} else {
|
|
916
|
+
throw new Error('Picture-in-Picture not supported');
|
|
989
917
|
}
|
|
990
|
-
|
|
991
|
-
throw new Error('No supported PiP API available');
|
|
992
918
|
} catch (error) {
|
|
993
|
-
|
|
919
|
+
console.error('Failed to enter PiP:', error);
|
|
994
920
|
throw error;
|
|
995
921
|
}
|
|
996
922
|
}
|
|
997
923
|
|
|
998
924
|
async exitPictureInPicture(): Promise<void> {
|
|
999
925
|
try {
|
|
1000
|
-
|
|
1001
|
-
const inStandardPiP = (document as any).pictureInPictureElement;
|
|
1002
|
-
const inWebkitPiP = this.video &&
|
|
1003
|
-
'webkitPresentationMode' in this.video &&
|
|
1004
|
-
(this.video as any).webkitPresentationMode === 'picture-in-picture';
|
|
1005
|
-
|
|
1006
|
-
if (!inStandardPiP && !inWebkitPiP) {
|
|
1007
|
-
this.debugLog('Not currently in PiP mode');
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// Standard PiP exit
|
|
1012
|
-
if (inStandardPiP && 'exitPictureInPicture' in document) {
|
|
926
|
+
if ((document as any).exitPictureInPicture) {
|
|
1013
927
|
await (document as any).exitPictureInPicture();
|
|
1014
|
-
this.debugLog('PiP exited using standard API');
|
|
1015
|
-
return;
|
|
1016
928
|
}
|
|
1017
|
-
|
|
1018
|
-
// Safari/WebKit PiP exit
|
|
1019
|
-
if (inWebkitPiP && this.video && 'webkitSetPresentationMode' in this.video) {
|
|
1020
|
-
(this.video as any).webkitSetPresentationMode('inline');
|
|
1021
|
-
this.debugLog('PiP exited using WebKit API');
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
929
|
} catch (error) {
|
|
1026
930
|
this.debugWarn('Failed to exit PiP:', (error as Error).message);
|
|
1027
931
|
// Don't re-throw the error to prevent breaking the user experience
|
|
@@ -1895,44 +1799,16 @@ export class WebPlayer extends BasePlayer {
|
|
|
1895
1799
|
}
|
|
1896
1800
|
|
|
1897
1801
|
.uvf-video {
|
|
1898
|
-
|
|
1899
|
-
top: 50%;
|
|
1900
|
-
left: 50%;
|
|
1901
|
-
transform: translate(-50%, -50%);
|
|
1802
|
+
display: block;
|
|
1902
1803
|
max-width: 100%;
|
|
1903
1804
|
max-height: 100%;
|
|
1904
|
-
width:
|
|
1905
|
-
height:
|
|
1805
|
+
width: 100%;
|
|
1806
|
+
height: 100%;
|
|
1906
1807
|
background: #000;
|
|
1907
1808
|
object-fit: contain;
|
|
1908
|
-
/* Ensure proper centering and scaling */
|
|
1909
1809
|
object-position: center;
|
|
1910
1810
|
}
|
|
1911
1811
|
|
|
1912
|
-
/* Mobile-specific video centering improvements */
|
|
1913
|
-
@media screen and (max-width: 767px) {
|
|
1914
|
-
.uvf-video {
|
|
1915
|
-
/* Force full width/height on mobile for better centering */
|
|
1916
|
-
width: 100%;
|
|
1917
|
-
height: 100%;
|
|
1918
|
-
top: 0;
|
|
1919
|
-
left: 0;
|
|
1920
|
-
transform: none;
|
|
1921
|
-
object-fit: contain;
|
|
1922
|
-
object-position: center;
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
.uvf-video-container {
|
|
1926
|
-
/* Remove aspect ratio constraint on mobile for full height usage */
|
|
1927
|
-
aspect-ratio: unset;
|
|
1928
|
-
min-height: 100%;
|
|
1929
|
-
height: 100%;
|
|
1930
|
-
display: flex;
|
|
1931
|
-
align-items: center;
|
|
1932
|
-
justify-content: center;
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
1812
|
.uvf-watermark-layer {
|
|
1937
1813
|
position: absolute;
|
|
1938
1814
|
top: 0;
|
|
@@ -3053,10 +2929,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
3053
2929
|
@media screen and (max-width: 767px) {
|
|
3054
2930
|
html, body {
|
|
3055
2931
|
overflow-x: hidden;
|
|
3056
|
-
/* Prevent iOS Safari address bar bounce */
|
|
3057
|
-
position: fixed;
|
|
3058
|
-
height: 100%;
|
|
3059
|
-
width: 100%;
|
|
3060
2932
|
}
|
|
3061
2933
|
|
|
3062
2934
|
.uvf-player-wrapper {
|
|
@@ -3066,23 +2938,6 @@ export class WebPlayer extends BasePlayer {
|
|
|
3066
2938
|
|
|
3067
2939
|
/* Prevent zoom on double tap */
|
|
3068
2940
|
touch-action: manipulation;
|
|
3069
|
-
|
|
3070
|
-
/* Ensure full viewport usage */
|
|
3071
|
-
position: relative;
|
|
3072
|
-
width: 100vw;
|
|
3073
|
-
height: 100vh;
|
|
3074
|
-
|
|
3075
|
-
/* iOS Safari fix for viewport height */
|
|
3076
|
-
min-height: -webkit-fill-available;
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
|
-
.uvf-video-container {
|
|
3080
|
-
/* Full viewport container */
|
|
3081
|
-
width: 100vw;
|
|
3082
|
-
height: 100vh;
|
|
3083
|
-
min-height: -webkit-fill-available;
|
|
3084
|
-
position: relative;
|
|
3085
|
-
overflow: hidden;
|
|
3086
2941
|
}
|
|
3087
2942
|
|
|
3088
2943
|
.uvf-video {
|
|
@@ -3095,38 +2950,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
3095
2950
|
/* Ensure hardware acceleration */
|
|
3096
2951
|
-webkit-transform: translateZ(0);
|
|
3097
2952
|
transform: translateZ(0);
|
|
3098
|
-
|
|
3099
|
-
/* Full viewport video with proper centering */
|
|
3100
|
-
width: 100%;
|
|
3101
|
-
height: 100%;
|
|
3102
|
-
object-fit: contain;
|
|
3103
|
-
object-position: center center;
|
|
3104
2953
|
}
|
|
3105
2954
|
|
|
3106
2955
|
/* Fix for controls being cut off by virtual keyboard */
|
|
3107
2956
|
.uvf-controls-bar {
|
|
3108
2957
|
position: fixed !important;
|
|
3109
2958
|
bottom: var(--uvf-safe-area-bottom, 0) !important;
|
|
3110
|
-
left: var(--uvf-safe-area-left, 0) !important;
|
|
3111
|
-
right: var(--uvf-safe-area-right, 0) !important;
|
|
3112
|
-
width: auto !important;
|
|
3113
|
-
z-index: 9999;
|
|
3114
|
-
}
|
|
3115
|
-
|
|
3116
|
-
/* Top controls safe area positioning */
|
|
3117
|
-
.uvf-top-controls {
|
|
3118
|
-
position: fixed !important;
|
|
3119
|
-
top: var(--uvf-safe-area-top, 10px) !important;
|
|
3120
|
-
right: var(--uvf-safe-area-right, 10px) !important;
|
|
3121
|
-
z-index: 9999;
|
|
3122
|
-
}
|
|
3123
|
-
|
|
3124
|
-
.uvf-title-bar {
|
|
3125
|
-
position: fixed !important;
|
|
3126
|
-
top: var(--uvf-safe-area-top, 10px) !important;
|
|
3127
|
-
left: var(--uvf-safe-area-left, 10px) !important;
|
|
3128
|
-
right: calc(120px + var(--uvf-safe-area-right, 10px)) !important; /* Leave space for top controls */
|
|
3129
|
-
z-index: 9999;
|
|
3130
2959
|
}
|
|
3131
2960
|
|
|
3132
2961
|
/* Ensure controls stay above virtual keyboards */
|
|
@@ -3135,127 +2964,160 @@ export class WebPlayer extends BasePlayer {
|
|
|
3135
2964
|
bottom: max(var(--uvf-safe-area-bottom, 0), env(keyboard-inset-height, 0)) !important;
|
|
3136
2965
|
}
|
|
3137
2966
|
}
|
|
3138
|
-
|
|
3139
|
-
/* Enhanced safe area support for newer devices */
|
|
3140
|
-
@supports (padding: max(0px)) {
|
|
3141
|
-
.uvf-controls-bar {
|
|
3142
|
-
padding-bottom: max(16px, calc(16px + var(--uvf-safe-area-bottom, 0)));
|
|
3143
|
-
padding-left: max(12px, calc(12px + var(--uvf-safe-area-left, 0)));
|
|
3144
|
-
padding-right: max(12px, calc(12px + var(--uvf-safe-area-right, 0)));
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
.uvf-top-controls {
|
|
3148
|
-
top: max(10px, calc(10px + var(--uvf-safe-area-top, 0))) !important;
|
|
3149
|
-
right: max(10px, calc(10px + var(--uvf-safe-area-right, 0))) !important;
|
|
3150
|
-
}
|
|
3151
|
-
|
|
3152
|
-
.uvf-title-bar {
|
|
3153
|
-
top: max(10px, calc(10px + var(--uvf-safe-area-top, 0))) !important;
|
|
3154
|
-
left: max(10px, calc(10px + var(--uvf-safe-area-left, 0))) !important;
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
2967
|
}
|
|
3158
2968
|
|
|
3159
|
-
/*
|
|
3160
|
-
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
3161
|
-
@supports (top: env(safe-area-inset-top)) {
|
|
3162
|
-
.uvf-responsive-container,
|
|
3163
|
-
.uvf-player-wrapper,
|
|
3164
|
-
.uvf-video-container {
|
|
3165
|
-
height: 100vh;
|
|
3166
|
-
height: calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
|
|
3167
|
-
}
|
|
3168
|
-
}
|
|
3169
|
-
}
|
|
2969
|
+
/* Mobile-First Responsive Video Player Layout */
|
|
3170
2970
|
|
|
3171
|
-
/*
|
|
3172
|
-
@media screen and (max-width: 767px)
|
|
3173
|
-
|
|
3174
|
-
height: 100vh;
|
|
3175
|
-
overflow: hidden;
|
|
3176
|
-
}
|
|
3177
|
-
|
|
3178
|
-
.uvf-responsive-container,
|
|
3179
|
-
.uvf-player-wrapper,
|
|
3180
|
-
.uvf-video-container {
|
|
3181
|
-
height: 100vh;
|
|
3182
|
-
width: 100vw;
|
|
3183
|
-
}
|
|
3184
|
-
|
|
3185
|
-
@supports (height: 100dvh) {
|
|
3186
|
-
.uvf-responsive-container,
|
|
3187
|
-
.uvf-player-wrapper,
|
|
3188
|
-
.uvf-video-container {
|
|
3189
|
-
height: 100dvh;
|
|
3190
|
-
width: 100dvw;
|
|
3191
|
-
}
|
|
3192
|
-
}
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
/* Enhanced Responsive Media Queries with UX Best Practices */
|
|
3196
|
-
/* Mobile devices (portrait) - Enhanced UX with Safe Areas */
|
|
3197
|
-
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
2971
|
+
/* Mobile devices (all orientations) - Base mobile styles */
|
|
2972
|
+
@media screen and (max-width: 767px) {
|
|
2973
|
+
/* Force full viewport usage with proper safe area handling */
|
|
3198
2974
|
.uvf-responsive-container {
|
|
3199
|
-
|
|
2975
|
+
position: fixed !important;
|
|
2976
|
+
top: 0;
|
|
2977
|
+
left: 0;
|
|
3200
2978
|
width: 100vw !important;
|
|
3201
|
-
height:
|
|
2979
|
+
height: 100vh !important;
|
|
2980
|
+
height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
|
|
3202
2981
|
margin: 0;
|
|
3203
|
-
|
|
2982
|
+
padding: 0;
|
|
3204
2983
|
overflow: hidden;
|
|
2984
|
+
z-index: 1000;
|
|
3205
2985
|
}
|
|
3206
2986
|
|
|
2987
|
+
/* Modern viewport support */
|
|
3207
2988
|
@supports (height: 100dvh) {
|
|
3208
2989
|
.uvf-responsive-container {
|
|
3209
|
-
height:
|
|
2990
|
+
height: 100dvh !important;
|
|
2991
|
+
height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
|
|
3210
2992
|
}
|
|
3211
2993
|
}
|
|
3212
2994
|
|
|
2995
|
+
/* Player wrapper fills container completely */
|
|
3213
2996
|
.uvf-responsive-container .uvf-player-wrapper {
|
|
3214
|
-
|
|
2997
|
+
position: absolute;
|
|
2998
|
+
top: 0;
|
|
2999
|
+
left: 0;
|
|
3000
|
+
width: 100% !important;
|
|
3215
3001
|
height: 100% !important;
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
.uvf-responsive-container .uvf-player-wrapper {
|
|
3221
|
-
min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
|
|
3222
|
-
}
|
|
3002
|
+
max-width: none;
|
|
3003
|
+
max-height: none;
|
|
3004
|
+
display: flex;
|
|
3005
|
+
flex-direction: column;
|
|
3223
3006
|
}
|
|
3224
3007
|
|
|
3008
|
+
/* Video container optimized for mobile centering */
|
|
3225
3009
|
.uvf-responsive-container .uvf-video-container {
|
|
3226
|
-
|
|
3010
|
+
position: relative;
|
|
3011
|
+
width: 100% !important;
|
|
3227
3012
|
height: 100% !important;
|
|
3013
|
+
flex: 1;
|
|
3228
3014
|
aspect-ratio: unset !important;
|
|
3229
|
-
|
|
3015
|
+
display: flex;
|
|
3016
|
+
align-items: center;
|
|
3017
|
+
justify-content: center;
|
|
3018
|
+
background: #000;
|
|
3019
|
+
overflow: hidden;
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
/* Perfectly centered mobile video */
|
|
3023
|
+
.uvf-responsive-container .uvf-video {
|
|
3024
|
+
position: relative;
|
|
3025
|
+
display: block;
|
|
3026
|
+
width: 100%;
|
|
3027
|
+
height: 100%;
|
|
3028
|
+
max-width: 100%;
|
|
3029
|
+
max-height: 100%;
|
|
3030
|
+
object-fit: contain;
|
|
3031
|
+
object-position: center center;
|
|
3032
|
+
background: transparent;
|
|
3230
3033
|
}
|
|
3231
3034
|
|
|
3232
|
-
/*
|
|
3035
|
+
/* Mobile controls positioned with safe areas */
|
|
3233
3036
|
.uvf-controls-bar {
|
|
3234
|
-
position: absolute;
|
|
3235
|
-
bottom:
|
|
3236
|
-
left:
|
|
3237
|
-
right:
|
|
3238
|
-
|
|
3239
|
-
padding
|
|
3240
|
-
padding-
|
|
3241
|
-
padding-
|
|
3242
|
-
|
|
3037
|
+
position: absolute !important;
|
|
3038
|
+
bottom: env(safe-area-inset-bottom, 0px);
|
|
3039
|
+
left: env(safe-area-inset-left, 0px);
|
|
3040
|
+
right: env(safe-area-inset-right, 0px);
|
|
3041
|
+
width: auto;
|
|
3042
|
+
padding: 16px;
|
|
3043
|
+
padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
|
|
3044
|
+
padding-left: calc(16px + env(safe-area-inset-left, 0px));
|
|
3045
|
+
padding-right: calc(16px + env(safe-area-inset-right, 0px));
|
|
3046
|
+
background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.6) 70%, transparent 100%);
|
|
3047
|
+
backdrop-filter: blur(10px);
|
|
3048
|
+
-webkit-backdrop-filter: blur(10px);
|
|
3243
3049
|
box-sizing: border-box;
|
|
3244
|
-
z-index:
|
|
3050
|
+
z-index: 2000;
|
|
3051
|
+
opacity: 1;
|
|
3052
|
+
transform: none;
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
/* Top controls with safe area */
|
|
3056
|
+
.uvf-top-controls {
|
|
3057
|
+
position: absolute !important;
|
|
3058
|
+
top: calc(16px + env(safe-area-inset-top, 0px));
|
|
3059
|
+
right: calc(16px + env(safe-area-inset-right, 0px));
|
|
3060
|
+
z-index: 2000;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
/* Title bar with safe area */
|
|
3064
|
+
.uvf-title-bar {
|
|
3065
|
+
position: absolute !important;
|
|
3066
|
+
top: calc(16px + env(safe-area-inset-top, 0px));
|
|
3067
|
+
left: calc(16px + env(safe-area-inset-left, 0px));
|
|
3068
|
+
right: calc(120px + env(safe-area-inset-right, 0px));
|
|
3069
|
+
z-index: 2000;
|
|
3245
3070
|
}
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
/* Mobile Portrait - Optimized vertical layout */
|
|
3074
|
+
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
3246
3075
|
|
|
3247
3076
|
.uvf-progress-section {
|
|
3248
3077
|
margin-bottom: 16px;
|
|
3249
3078
|
}
|
|
3250
3079
|
|
|
3251
|
-
/* Mobile-
|
|
3080
|
+
/* Mobile-optimized controls layout */
|
|
3252
3081
|
.uvf-controls-row {
|
|
3253
|
-
|
|
3254
|
-
flex-wrap: nowrap;
|
|
3082
|
+
display: flex;
|
|
3255
3083
|
align-items: center;
|
|
3256
3084
|
justify-content: space-between;
|
|
3257
|
-
position: relative;
|
|
3258
3085
|
width: 100%;
|
|
3086
|
+
gap: 12px;
|
|
3087
|
+
flex-wrap: nowrap;
|
|
3088
|
+
position: relative;
|
|
3089
|
+
min-height: 52px;
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
/* Ensure controls are always visible */
|
|
3093
|
+
.uvf-controls-row > * {
|
|
3094
|
+
flex-shrink: 0;
|
|
3095
|
+
display: flex;
|
|
3096
|
+
align-items: center;
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
/* Control groups with proper flex behavior */
|
|
3100
|
+
.uvf-left-controls {
|
|
3101
|
+
display: flex;
|
|
3102
|
+
align-items: center;
|
|
3103
|
+
gap: 8px;
|
|
3104
|
+
flex: 0 0 auto;
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
.uvf-center-controls {
|
|
3108
|
+
display: flex;
|
|
3109
|
+
align-items: center;
|
|
3110
|
+
gap: 8px;
|
|
3111
|
+
flex: 1 1 auto;
|
|
3112
|
+
justify-content: center;
|
|
3113
|
+
min-width: 0;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
.uvf-right-controls {
|
|
3117
|
+
display: flex !important;
|
|
3118
|
+
align-items: center;
|
|
3119
|
+
gap: 8px;
|
|
3120
|
+
flex: 0 0 auto;
|
|
3259
3121
|
}
|
|
3260
3122
|
|
|
3261
3123
|
/* Left side controls group */
|
|
@@ -3383,26 +3245,68 @@ export class WebPlayer extends BasePlayer {
|
|
|
3383
3245
|
display: none !important;
|
|
3384
3246
|
}
|
|
3385
3247
|
|
|
3386
|
-
/*
|
|
3248
|
+
/* Enhanced mobile settings menu */
|
|
3387
3249
|
.uvf-settings-menu {
|
|
3388
|
-
|
|
3389
|
-
bottom:
|
|
3390
|
-
right:
|
|
3250
|
+
position: fixed !important;
|
|
3251
|
+
bottom: calc(80px + env(safe-area-inset-bottom, 0px));
|
|
3252
|
+
right: calc(16px + env(safe-area-inset-right, 0px));
|
|
3253
|
+
min-width: 200px;
|
|
3254
|
+
max-width: 280px;
|
|
3255
|
+
max-height: 60vh;
|
|
3256
|
+
background: rgba(0,0,0,0.95);
|
|
3257
|
+
backdrop-filter: blur(20px);
|
|
3258
|
+
-webkit-backdrop-filter: blur(20px);
|
|
3259
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
3260
|
+
border-radius: 12px;
|
|
3261
|
+
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
|
3262
|
+
z-index: 3000;
|
|
3263
|
+
overflow: hidden;
|
|
3391
3264
|
font-size: 14px;
|
|
3392
|
-
|
|
3265
|
+
opacity: 0;
|
|
3266
|
+
transform: translateY(10px) scale(0.95);
|
|
3267
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3268
|
+
pointer-events: none;
|
|
3393
3269
|
}
|
|
3394
3270
|
|
|
3271
|
+
.uvf-settings-menu.active {
|
|
3272
|
+
opacity: 1;
|
|
3273
|
+
transform: translateY(0) scale(1);
|
|
3274
|
+
pointer-events: auto;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
/* Touch-optimized settings options */
|
|
3395
3278
|
.uvf-settings-option {
|
|
3396
|
-
padding:
|
|
3397
|
-
font-size:
|
|
3398
|
-
min-height:
|
|
3279
|
+
padding: 16px 20px;
|
|
3280
|
+
font-size: 16px;
|
|
3281
|
+
min-height: 56px;
|
|
3399
3282
|
display: flex;
|
|
3400
3283
|
align-items: center;
|
|
3284
|
+
cursor: pointer;
|
|
3285
|
+
transition: all 0.2s ease;
|
|
3286
|
+
border: none;
|
|
3287
|
+
background: transparent;
|
|
3288
|
+
color: #fff;
|
|
3289
|
+
width: 100%;
|
|
3290
|
+
text-align: left;
|
|
3401
3291
|
}
|
|
3402
3292
|
|
|
3403
|
-
.uvf-settings-option:hover
|
|
3293
|
+
.uvf-settings-option:hover,
|
|
3294
|
+
.uvf-settings-option:focus {
|
|
3404
3295
|
background: rgba(255,255,255,0.15);
|
|
3405
|
-
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
.uvf-settings-option.active {
|
|
3299
|
+
background: rgba(255,77,79,0.2);
|
|
3300
|
+
color: #ff4d4f;
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
/* Settings groups with proper spacing */
|
|
3304
|
+
.uvf-settings-group {
|
|
3305
|
+
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
.uvf-settings-group:last-child {
|
|
3309
|
+
border-bottom: none;
|
|
3406
3310
|
}
|
|
3407
3311
|
|
|
3408
3312
|
/* Simplified settings - hide complex options */
|
|
@@ -3543,11 +3447,46 @@ export class WebPlayer extends BasePlayer {
|
|
|
3543
3447
|
display: block;
|
|
3544
3448
|
}
|
|
3545
3449
|
|
|
3450
|
+
/* Ensure settings button is always visible and functional */
|
|
3451
|
+
#uvf-settings-btn {
|
|
3452
|
+
display: flex !important;
|
|
3453
|
+
align-items: center;
|
|
3454
|
+
justify-content: center;
|
|
3455
|
+
width: 44px;
|
|
3456
|
+
height: 44px;
|
|
3457
|
+
min-width: 44px;
|
|
3458
|
+
min-height: 44px;
|
|
3459
|
+
background: rgba(255,255,255,0.15);
|
|
3460
|
+
backdrop-filter: blur(8px);
|
|
3461
|
+
border: 1px solid rgba(255,255,255,0.2);
|
|
3462
|
+
border-radius: 22px;
|
|
3463
|
+
transition: all 0.2s ease;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
#uvf-settings-btn:hover {
|
|
3467
|
+
background: rgba(255,255,255,0.25);
|
|
3468
|
+
transform: scale(1.05);
|
|
3469
|
+
}
|
|
3470
|
+
|
|
3471
|
+
#uvf-settings-btn svg {
|
|
3472
|
+
width: 20px;
|
|
3473
|
+
height: 20px;
|
|
3474
|
+
fill: #fff;
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3546
3477
|
/* Essential controls in right section - Settings, PiP, and Fullscreen only */
|
|
3547
3478
|
.uvf-right-controls > *:not(#uvf-settings-btn):not(#uvf-fullscreen-btn):not(#uvf-pip-btn) {
|
|
3548
3479
|
display: none;
|
|
3549
3480
|
}
|
|
3550
3481
|
|
|
3482
|
+
/* Make sure right controls are properly spaced */
|
|
3483
|
+
.uvf-right-controls {
|
|
3484
|
+
display: flex;
|
|
3485
|
+
align-items: center;
|
|
3486
|
+
gap: 8px;
|
|
3487
|
+
flex-shrink: 0;
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3551
3490
|
/* Hide skip buttons on small mobile devices to save space */
|
|
3552
3491
|
@media screen and (max-width: 480px) {
|
|
3553
3492
|
#uvf-skip-back,
|
|
@@ -4541,20 +4480,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
4541
4480
|
epgBtn.style.display = 'none'; // Initially hidden, will be shown when EPG data is available
|
|
4542
4481
|
rightControls.appendChild(epgBtn);
|
|
4543
4482
|
|
|
4544
|
-
// PiP button
|
|
4483
|
+
// PiP button
|
|
4545
4484
|
const pipBtn = document.createElement('button');
|
|
4546
4485
|
pipBtn.className = 'uvf-control-btn';
|
|
4547
4486
|
pipBtn.id = 'uvf-pip-btn';
|
|
4548
4487
|
pipBtn.title = 'Picture-in-Picture';
|
|
4549
4488
|
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>';
|
|
4550
|
-
|
|
4551
|
-
// Only show PiP button if supported
|
|
4552
|
-
if (this.isPictureInPictureSupported()) {
|
|
4553
|
-
rightControls.appendChild(pipBtn);
|
|
4554
|
-
this.debugLog('PiP button added - support detected');
|
|
4555
|
-
} else {
|
|
4556
|
-
this.debugLog('PiP button not added - no support detected');
|
|
4557
|
-
}
|
|
4489
|
+
rightControls.appendChild(pipBtn);
|
|
4558
4490
|
|
|
4559
4491
|
// Fullscreen button
|
|
4560
4492
|
const fullscreenBtn = document.createElement('button');
|
|
@@ -5823,49 +5755,15 @@ export class WebPlayer extends BasePlayer {
|
|
|
5823
5755
|
}
|
|
5824
5756
|
}
|
|
5825
5757
|
|
|
5826
|
-
/**
|
|
5827
|
-
* Checks if currently in Picture-in-Picture mode
|
|
5828
|
-
*/
|
|
5829
|
-
isPictureInPictureActive(): boolean {
|
|
5830
|
-
// Check standard PiP
|
|
5831
|
-
const inStandardPiP = !!(document as any).pictureInPictureElement;
|
|
5832
|
-
|
|
5833
|
-
// Check Safari/WebKit PiP
|
|
5834
|
-
const inWebkitPiP = !!(this.video &&
|
|
5835
|
-
'webkitPresentationMode' in this.video &&
|
|
5836
|
-
(this.video as any).webkitPresentationMode === 'picture-in-picture');
|
|
5837
|
-
|
|
5838
|
-
return inStandardPiP || inWebkitPiP;
|
|
5839
|
-
}
|
|
5840
|
-
|
|
5841
5758
|
private async togglePiP(): Promise<void> {
|
|
5842
|
-
if (!this.isPictureInPictureSupported()) {
|
|
5843
|
-
this.showShortcutIndicator('PiP not supported');
|
|
5844
|
-
this.debugWarn('PiP not supported on this device/browser');
|
|
5845
|
-
return;
|
|
5846
|
-
}
|
|
5847
|
-
|
|
5848
5759
|
try {
|
|
5849
|
-
if (
|
|
5760
|
+
if ((document as any).pictureInPictureElement) {
|
|
5850
5761
|
await this.exitPictureInPicture();
|
|
5851
|
-
this.showShortcutIndicator('Exit PiP');
|
|
5852
|
-
this.debugLog('PiP deactivated');
|
|
5853
5762
|
} else {
|
|
5854
5763
|
await this.enterPictureInPicture();
|
|
5855
|
-
this.showShortcutIndicator('Enter PiP');
|
|
5856
|
-
this.debugLog('PiP activated');
|
|
5857
5764
|
}
|
|
5858
5765
|
} catch (error) {
|
|
5859
|
-
|
|
5860
|
-
this.showShortcutIndicator(`PiP Error: ${errorMessage}`);
|
|
5861
|
-
this.debugError('PiP toggle failed:', errorMessage);
|
|
5862
|
-
|
|
5863
|
-
// Show user-friendly message for common errors
|
|
5864
|
-
if (errorMessage.includes('not supported')) {
|
|
5865
|
-
this.showShortcutIndicator('PiP not supported');
|
|
5866
|
-
} else if (errorMessage.includes('user gesture')) {
|
|
5867
|
-
this.showShortcutIndicator('PiP requires user interaction');
|
|
5868
|
-
}
|
|
5766
|
+
console.error('PiP toggle failed:', error);
|
|
5869
5767
|
}
|
|
5870
5768
|
}
|
|
5871
5769
|
|