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
- // Standard PiP API (Chrome, Firefox, etc.)
978
- if ('requestPictureInPicture' in this.video) {
913
+ if ((this.video as any).requestPictureInPicture) {
979
914
  await (this.video as any).requestPictureInPicture();
980
- this.debugLog('PiP entered using standard API');
981
- return;
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
- this.debugError('Failed to enter PiP:', (error as Error).message);
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
- // Check if currently in PiP mode
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
- position: absolute;
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: auto;
1905
- height: auto;
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
- /* Specific fixes for iPhone X series and newer with notches */
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
- /* Landscape orientation fixes for mobile */
3172
- @media screen and (max-width: 767px) and (orientation: landscape) {
3173
- html, body {
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
- padding: 0;
2975
+ position: fixed !important;
2976
+ top: 0;
2977
+ left: 0;
3200
2978
  width: 100vw !important;
3201
- height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
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
- position: relative;
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: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
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
- width: 100vw !important;
2997
+ position: absolute;
2998
+ top: 0;
2999
+ left: 0;
3000
+ width: 100% !important;
3215
3001
  height: 100% !important;
3216
- min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
3217
- }
3218
-
3219
- @supports (height: 100dvh) {
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
- width: 100vw !important;
3010
+ position: relative;
3011
+ width: 100% !important;
3227
3012
  height: 100% !important;
3013
+ flex: 1;
3228
3014
  aspect-ratio: unset !important;
3229
- min-height: inherit;
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
- /* Enhanced mobile controls bar with safe area padding */
3035
+ /* Mobile controls positioned with safe areas */
3233
3036
  .uvf-controls-bar {
3234
- position: absolute;
3235
- bottom: 0;
3236
- left: 0;
3237
- right: 0;
3238
- padding: 16px 12px;
3239
- padding-bottom: calc(16px + var(--uvf-safe-area-bottom));
3240
- padding-left: calc(12px + var(--uvf-safe-area-left));
3241
- padding-right: calc(12px + var(--uvf-safe-area-right));
3242
- background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
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: 1000;
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-first responsive controls layout */
3080
+ /* Mobile-optimized controls layout */
3252
3081
  .uvf-controls-row {
3253
- gap: 8px;
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
- /* Settings menu - hidden by default, accessible via menu */
3248
+ /* Enhanced mobile settings menu */
3387
3249
  .uvf-settings-menu {
3388
- min-width: 160px;
3389
- bottom: 60px;
3390
- right: 12px;
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
- max-height: 50vh;
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: 12px 16px;
3397
- font-size: 14px;
3398
- min-height: 44px;
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
- padding-left: 20px;
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 - conditionally add based on support
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 (this.isPictureInPictureActive()) {
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
- const errorMessage = (error as Error).message;
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