unified-video-framework 1.4.164 → 1.4.166

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.
@@ -21,6 +21,7 @@ export class WebPlayer extends BasePlayer {
21
21
  this.currentSubtitle = 'off';
22
22
  this.currentPlaybackRate = 1;
23
23
  this.isDragging = false;
24
+ this.isSettingsOpen = false;
24
25
  this.settingsConfig = {
25
26
  enabled: true,
26
27
  speed: true,
@@ -52,6 +53,16 @@ export class WebPlayer extends BasePlayer {
52
53
  this.hasTriedButtonFallback = false;
53
54
  this.lastUserInteraction = 0;
54
55
  this.showTimeTooltip = false;
56
+ this.lastTapTime = 0;
57
+ this.tapCount = 0;
58
+ this.tapTimeout = null;
59
+ this.longPressTimer = null;
60
+ this.longPressActive = false;
61
+ this.longPressStartTime = 0;
62
+ this.originalPlaybackRate = 1;
63
+ this.dominantColor = '#ff0000';
64
+ this.accentColor = '#ff4d4f';
65
+ this.surfaceTint = 'rgba(255, 0, 0, 0.08)';
55
66
  this.autoplayCapabilities = {
56
67
  canAutoplay: false,
57
68
  canAutoplayMuted: false,
@@ -229,6 +240,26 @@ export class WebPlayer extends BasePlayer {
229
240
  catch (_) { }
230
241
  this.setupCastContextSafe();
231
242
  this.updateMetadataUI();
243
+ this.enableMaterialYouMobileIfNeeded();
244
+ }
245
+ enableMaterialYouMobileIfNeeded() {
246
+ const isMobile = this.isMobileDevice();
247
+ const isPortrait = window.innerHeight > window.innerWidth;
248
+ if (isMobile && isPortrait && this.playerWrapper) {
249
+ this.debugLog('Enabling Material You mobile layout');
250
+ this.playerWrapper.classList.add('uvf-material-you-mobile');
251
+ window.addEventListener('resize', () => {
252
+ const isNowPortrait = window.innerHeight > window.innerWidth;
253
+ if (this.playerWrapper) {
254
+ if (isMobile && isNowPortrait) {
255
+ this.playerWrapper.classList.add('uvf-material-you-mobile');
256
+ }
257
+ else {
258
+ this.playerWrapper.classList.remove('uvf-material-you-mobile');
259
+ }
260
+ }
261
+ });
262
+ }
232
263
  }
233
264
  setupVideoEventListeners() {
234
265
  if (!this.video)
@@ -306,6 +337,7 @@ export class WebPlayer extends BasePlayer {
306
337
  this.state.duration = this.video.duration || 0;
307
338
  this.debugLog('Metadata loaded - duration:', this.video.duration);
308
339
  this.updateTimeDisplay();
340
+ this.renderChapterMarkersOnProgressBar();
309
341
  this.emit('onLoadedMetadata', {
310
342
  duration: this.video.duration || 0,
311
343
  width: this.video.videoWidth || 0,
@@ -1875,6 +1907,8 @@ export class WebPlayer extends BasePlayer {
1875
1907
  --uvf-scrollbar-thumb-hover-start: rgba(255,0,0,0.5);
1876
1908
  --uvf-scrollbar-thumb-hover-end: rgba(255,0,0,0.6);
1877
1909
  --uvf-firefox-scrollbar-color: rgba(255,255,255,0.25);
1910
+ /* Material You surface tint */
1911
+ --uvf-surface-tint: rgba(255, 0, 0, 0.08);
1878
1912
  }
1879
1913
 
1880
1914
  /* Player focus styles for better UX */
@@ -3930,278 +3964,417 @@ export class WebPlayer extends BasePlayer {
3930
3964
  }
3931
3965
 
3932
3966
  /* Enhanced Responsive Media Queries with UX Best Practices */
3933
- /* Mobile Portrait Layout - CENTERED PLAYER with TOP/BOTTOM BLACK AREAS */
3967
+
3968
+ /* Material You Mobile Portrait Layout - 25% Black + 50% Video + 25% Black */
3934
3969
  @media screen and (max-width: 767px) and (orientation: portrait) {
3935
- /* CENTERED LAYOUT: 25% top black + 50% player + 25% bottom black */
3936
- .uvf-responsive-container {
3937
- display: flex;
3938
- flex-direction: column;
3970
+ /* Enable Material You mode with class flag */
3971
+ .uvf-player-wrapper.uvf-material-you-mobile {
3972
+ /* Full viewport height layout */
3939
3973
  height: 100vh;
3940
3974
  height: 100dvh;
3941
- background: #000;
3942
- overflow: hidden;
3975
+ width: 100vw;
3943
3976
  position: fixed;
3944
3977
  top: 0;
3945
3978
  left: 0;
3979
+ display: flex;
3980
+ flex-direction: column;
3981
+ background: #000;
3982
+ overflow: hidden;
3983
+ }
3984
+
3985
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-video-container {
3986
+ /* Video occupies middle 50% */
3987
+ height: 50vh;
3988
+ height: 50dvh;
3946
3989
  width: 100vw;
3947
- padding: 0;
3948
- margin: 0;
3990
+ position: relative;
3991
+ margin-top: 25vh;
3992
+ margin-top: 25dvh;
3993
+ aspect-ratio: unset !important;
3994
+ /* Match existing theme background */
3995
+ background: radial-gradient(ellipse at center, #1a1a2e 0%, #000 100%);
3996
+ /* Material Design elevation */
3997
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4),
3998
+ 0 4px 16px rgba(0, 0, 0, 0.3),
3999
+ 0 2px 8px rgba(0, 0, 0, 0.2);
4000
+ }
4001
+
4002
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-video {
4003
+ width: 100%;
4004
+ height: 100%;
4005
+ object-fit: contain;
3949
4006
  }
3950
4007
 
3951
- /* TOP BLACK AREA - 25% of viewport - COMPLETELY EMPTY */
3952
- .uvf-responsive-container::before {
4008
+ /* Top black section (25%) - Tap zone overlay */
4009
+ .uvf-player-wrapper.uvf-material-you-mobile::before {
3953
4010
  content: '';
3954
- flex: 0 0 25vh;
4011
+ position: absolute;
4012
+ top: 0;
4013
+ left: 0;
4014
+ width: 100vw;
4015
+ height: 25vh;
4016
+ height: 25dvh;
3955
4017
  background: #000;
3956
- pointer-events: none;
4018
+ z-index: 1;
4019
+ pointer-events: all;
4020
+ touch-action: manipulation;
3957
4021
  }
3958
4022
 
3959
- /* BOTTOM BLACK AREA - 25% of viewport - COMPLETELY EMPTY */
3960
- .uvf-responsive-container::after {
4023
+ /* Bottom black section (25%) - Surface container */
4024
+ .uvf-player-wrapper.uvf-material-you-mobile::after {
3961
4025
  content: '';
3962
- flex: 0 0 25vh;
3963
- background: #000;
4026
+ position: absolute;
4027
+ bottom: 0;
4028
+ left: 0;
4029
+ width: 100vw;
4030
+ height: 25vh;
4031
+ height: 25dvh;
4032
+ background: linear-gradient(to top,
4033
+ #000 0%,
4034
+ rgba(0, 0, 0, 0.98) 20%,
4035
+ rgba(0, 0, 0, 0.95) 100%);
4036
+ z-index: 1;
3964
4037
  pointer-events: none;
3965
4038
  }
3966
4039
 
3967
- /* CENTERED VIDEO PLAYER WRAPPER - 50% of viewport */
3968
- .uvf-responsive-container .uvf-player-wrapper {
3969
- flex: 1; /* Takes remaining 50% */
3970
- width: 100vw;
4040
+ /* Material surface container for controls */
4041
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-controls-bar {
4042
+ position: absolute;
4043
+ bottom: 0;
4044
+ left: 0;
4045
+ right: 0;
4046
+ height: auto;
4047
+ max-height: 25vh;
4048
+ max-height: 25dvh;
4049
+ padding: 16px 20px;
4050
+ padding-bottom: calc(16px + var(--uvf-safe-area-bottom, 0px));
4051
+ background: transparent;
4052
+ z-index: 2;
3971
4053
  display: flex;
3972
- align-items: center;
3973
- justify-content: center;
3974
- background: #000;
4054
+ flex-direction: column;
4055
+ justify-content: flex-end;
4056
+ /* Material Design surface with tint */
4057
+ backdrop-filter: blur(24px);
4058
+ -webkit-backdrop-filter: blur(24px);
4059
+ }
4060
+
4061
+ /* Material surface tint overlay */
4062
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-controls-bar::before {
4063
+ content: '';
4064
+ position: absolute;
4065
+ inset: 0;
4066
+ background: var(--uvf-surface-tint, rgba(255, 0, 0, 0.08));
4067
+ border-radius: 28px 28px 0 0;
4068
+ pointer-events: none;
4069
+ z-index: -1;
4070
+ }
4071
+
4072
+ /* Progress bar with chapter markers */
4073
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-section {
4074
+ margin-bottom: 12px;
3975
4075
  position: relative;
3976
- overflow: hidden !important; /* CRITICAL: Ensure nothing extends beyond this area */
3977
- /* STRICT CONTAINMENT - Force all child elements within bounds */
3978
- contain: layout style paint size !important;
3979
- isolation: isolate !important;
3980
- /* Create strict clipping boundary */
3981
- clip: rect(0, 100vw, 50vh, 0) !important;
3982
4076
  }
3983
4077
 
3984
- /* Video container fills player wrapper */
3985
- .uvf-responsive-container .uvf-video-container {
3986
- width: 100%;
3987
- height: 100%;
4078
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-bar-wrapper {
4079
+ padding: 12px 0;
3988
4080
  position: relative;
3989
- background: #000;
3990
- border-radius: 0;
3991
- overflow: hidden;
3992
4081
  }
3993
4082
 
3994
- /* Video element fills container */
3995
- .uvf-responsive-container .uvf-video {
3996
- width: 100%;
3997
- height: 100%;
3998
- object-fit: contain;
3999
- background: #000;
4083
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-bar {
4084
+ height: 4px;
4085
+ background: rgba(255, 255, 255, 0.2);
4086
+ border-radius: 4px;
4087
+ position: relative;
4088
+ overflow: visible;
4089
+ /* Material elevation */
4090
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
4000
4091
  }
4001
4092
 
4002
- /* CONTROLS STRICTLY CONTAINED WITHIN VIDEO AREA - NEVER EXTEND TO BLACK AREAS */
4003
- .uvf-controls-bar {
4004
- position: absolute !important;
4005
- /* Keep controls INSIDE video container with margins from all edges */
4006
- bottom: 20px !important; /* 20px margin from video bottom */
4007
- left: 16px !important; /* 16px margin from video left */
4008
- right: 16px !important; /* 16px margin from video right */
4009
- top: auto !important;
4010
- background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.7) 60%, transparent 100%);
4011
- padding: 12px 16px;
4012
- border-radius: 12px;
4013
- z-index: 1000;
4014
- /* Ensure controls are visible and contained */
4015
- opacity: 1 !important;
4016
- visibility: visible !important;
4017
- display: flex !important;
4018
- flex-direction: column !important;
4019
- /* Hardware acceleration */
4020
- -webkit-transform: translate3d(0,0,0);
4021
- transform: translate3d(0,0,0);
4022
- /* CRITICAL: Prevent any overflow into black areas */
4023
- max-height: calc(100% - 40px) !important; /* Leave 20px margin from top and bottom */
4024
- max-width: calc(100% - 32px) !important; /* Leave 16px margin from left and right */
4025
- box-sizing: border-box !important;
4026
- /* Visual containment indicators */
4027
- border: 1px solid rgba(255, 255, 255, 0.1);
4028
- backdrop-filter: blur(10px);
4029
- /* STRICT CSS containment with size to prevent overflow */
4030
- contain: layout style paint size !important;
4031
- /* Absolute overflow prevention */
4032
- overflow: hidden !important;
4033
- /* Force clip to container bounds */
4034
- clip-path: inset(0) !important;
4035
- }
4036
-
4037
- /* Force controls and all child elements to stay within video container */
4038
- .uvf-responsive-container .uvf-controls-bar {
4039
- opacity: 1 !important;
4040
- visibility: visible !important;
4041
- transform: translateY(0) !important;
4042
- pointer-events: auto !important;
4043
- }
4044
-
4045
- /* CRITICAL: Prevent ALL child elements from extending beyond controls container */
4046
- .uvf-responsive-container .uvf-controls-bar *,
4047
- .uvf-responsive-container .uvf-controls-bar *::before,
4048
- .uvf-responsive-container .uvf-controls-bar *::after {
4049
- max-width: 100% !important;
4050
- max-height: 100% !important;
4051
- overflow: hidden !important;
4052
- box-sizing: border-box !important;
4053
- position: relative !important;
4093
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-filled {
4094
+ background: var(--uvf-accent-1, #ff0000);
4095
+ box-shadow: 0 0 8px var(--uvf-accent-1, #ff0000);
4054
4096
  }
4055
4097
 
4056
- /* Ensure settings menu stays within video container bounds */
4057
- .uvf-responsive-container .uvf-settings-menu {
4058
- max-height: calc(50vh - 80px) !important; /* Video height minus controls margin */
4059
- max-width: calc(100vw - 64px) !important; /* Video width minus side margins */
4060
- overflow-y: auto !important;
4061
- position: absolute !important;
4062
- /* Keep menu within video boundaries */
4063
- bottom: 60px !important;
4064
- right: 0 !important;
4065
- left: auto !important;
4066
- top: auto !important;
4067
- /* Ensure no child elements extend beyond container */
4068
- contain: layout style paint;
4098
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-handle {
4099
+ width: 20px;
4100
+ height: 20px;
4101
+ background: var(--uvf-accent-1, #ff0000);
4102
+ /* Material Design state layer */
4103
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3),
4104
+ 0 0 0 0 var(--uvf-accent-1, #ff0000);
4105
+ transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
4069
4106
  }
4070
4107
 
4071
- /* Ensure all child elements of controls stay within bounds */
4072
- .uvf-responsive-container .uvf-controls-bar * {
4073
- max-width: 100%;
4074
- box-sizing: border-box;
4108
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-handle:active {
4109
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4),
4110
+ 0 0 0 12px rgba(255, 0, 0, 0.15);
4111
+ transform: translate(-50%, -50%) scale(1.2);
4075
4112
  }
4076
4113
 
4077
- /* Progress section contained within controls */
4078
- .uvf-progress-section {
4079
- width: 100%;
4080
- margin-bottom: 8px;
4081
- opacity: 1 !important;
4082
- visibility: visible !important;
4083
- display: block !important;
4114
+ /* Chapter markers on progress bar */
4115
+ .uvf-chapter-marker {
4116
+ position: absolute;
4117
+ top: 0;
4118
+ height: 100%;
4119
+ width: 3px;
4120
+ background: rgba(255, 255, 255, 0.4);
4121
+ border-radius: 2px;
4122
+ transform: translateX(-50%);
4123
+ pointer-events: none;
4124
+ z-index: 1;
4125
+ transition: all 0.2s ease;
4084
4126
  }
4085
4127
 
4086
- /* Progress bar styling */
4087
- .uvf-progress-bar-wrapper {
4088
- opacity: 1 !important;
4089
- visibility: visible !important;
4128
+ .uvf-chapter-marker.intro {
4129
+ background: #4CAF50;
4090
4130
  }
4091
4131
 
4092
- .uvf-progress-bar {
4093
- height: 4px;
4094
- background: rgba(255, 255, 255, 0.3);
4095
- border-radius: 2px;
4132
+ .uvf-chapter-marker.recap {
4133
+ background: #FFC107;
4096
4134
  }
4097
4135
 
4098
- .uvf-progress-filled {
4099
- background: var(--uvf-accent-1, #8B5CF6);
4100
- height: 100%;
4101
- border-radius: 2px;
4136
+ .uvf-chapter-marker.credits {
4137
+ background: #9C27B0;
4102
4138
  }
4103
4139
 
4104
- /* Controls row alignment - ensure visibility */
4105
- .uvf-controls-row {
4106
- width: 100%;
4107
- display: flex !important;
4108
- align-items: center;
4109
- justify-content: flex-start;
4110
- gap: 12px;
4111
- opacity: 1 !important;
4112
- visibility: visible !important;
4113
- margin-top: 8px;
4140
+ .uvf-progress-bar-wrapper:hover .uvf-chapter-marker {
4141
+ width: 4px;
4142
+ opacity: 1;
4114
4143
  }
4115
4144
 
4116
- /* Time display visibility */
4117
- .uvf-time-display {
4118
- color: #fff;
4119
- font-size: 12px;
4120
- font-weight: 500;
4121
- opacity: 1 !important;
4122
- visibility: visible !important;
4123
- display: block !important;
4145
+ /* Material Design control buttons */
4146
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-control-btn {
4147
+ width: 48px;
4148
+ height: 48px;
4149
+ min-width: 48px;
4150
+ min-height: 48px;
4151
+ background: rgba(255, 255, 255, 0.12);
4152
+ border-radius: 24px;
4153
+ /* Material elevation level 1 */
4154
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12),
4155
+ 0 1px 2px rgba(0, 0, 0, 0.24);
4156
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
4157
+ position: relative;
4158
+ overflow: hidden;
4124
4159
  }
4125
4160
 
4126
- /* Control buttons visibility */
4127
- .uvf-control-btn {
4128
- min-width: 44px;
4129
- min-height: 44px;
4130
- display: flex !important;
4161
+ /* Material ripple effect */
4162
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-control-btn::before {
4163
+ content: '';
4164
+ position: absolute;
4165
+ inset: 0;
4166
+ background: rgba(255, 255, 255, 0.1);
4167
+ border-radius: inherit;
4168
+ opacity: 0;
4169
+ transition: opacity 0.2s ease;
4170
+ }
4171
+
4172
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-control-btn:active::before {
4173
+ opacity: 1;
4174
+ }
4175
+
4176
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-control-btn:active {
4177
+ /* Material elevation level 2 */
4178
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16),
4179
+ 0 3px 6px rgba(0, 0, 0, 0.23);
4180
+ transform: scale(0.95);
4181
+ }
4182
+
4183
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-control-btn.play-pause {
4184
+ width: 56px;
4185
+ height: 56px;
4186
+ min-width: 56px;
4187
+ min-height: 56px;
4188
+ border-radius: 28px;
4189
+ /* Material elevated button */
4190
+ background: linear-gradient(135deg,
4191
+ var(--uvf-accent-1, #ff0000),
4192
+ var(--uvf-accent-2, #ff4d4f));
4193
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2),
4194
+ 0 2px 4px rgba(0, 0, 0, 0.15),
4195
+ 0 0 0 0 var(--uvf-accent-1, #ff0000);
4196
+ }
4197
+
4198
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-control-btn.play-pause:active {
4199
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25),
4200
+ 0 4px 8px rgba(0, 0, 0, 0.20),
4201
+ 0 0 0 8px rgba(255, 0, 0, 0.12);
4202
+ }
4203
+
4204
+ /* Controls row with Material spacing */
4205
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-controls-row {
4206
+ gap: 16px;
4207
+ padding: 0;
4131
4208
  align-items: center;
4132
- justify-content: center;
4133
- border-radius: 50%;
4209
+ }
4210
+
4211
+ /* Time display with Material surface */
4212
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-time-display {
4134
4213
  background: rgba(255, 255, 255, 0.1);
4135
4214
  backdrop-filter: blur(8px);
4136
- color: #fff;
4137
- opacity: 1 !important;
4138
- visibility: visible !important;
4139
- transition: all 0.2s ease;
4215
+ border-radius: 16px;
4216
+ padding: 6px 12px;
4217
+ font-size: 13px;
4218
+ font-weight: 500;
4219
+ font-feature-settings: 'tnum';
4220
+ /* Material elevation */
4221
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
4140
4222
  }
4141
4223
 
4142
- .uvf-control-btn:active {
4143
- transform: scale(0.95);
4224
+ /* Double-tap overlay indicators */
4225
+ .uvf-doubletap-indicator {
4226
+ position: absolute;
4227
+ top: 50%;
4228
+ transform: translateY(-50%);
4229
+ width: 80px;
4230
+ height: 80px;
4144
4231
  background: rgba(255, 255, 255, 0.2);
4232
+ backdrop-filter: blur(10px);
4233
+ border-radius: 40px;
4234
+ display: flex;
4235
+ align-items: center;
4236
+ justify-content: center;
4237
+ pointer-events: none;
4238
+ opacity: 0;
4239
+ z-index: 100;
4240
+ transition: opacity 0.3s ease;
4241
+ /* Material elevation level 3 */
4242
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19),
4243
+ 0 6px 6px rgba(0, 0, 0, 0.23);
4145
4244
  }
4146
4245
 
4147
- /* Play/pause button prominence */
4148
- .uvf-control-btn.play-pause {
4149
- background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
4150
- min-width: 52px;
4151
- min-height: 52px;
4152
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
4246
+ .uvf-doubletap-indicator.left {
4247
+ left: 15%;
4153
4248
  }
4154
4249
 
4155
- .uvf-control-btn.play-pause:active {
4156
- transform: scale(0.92);
4157
- box-shadow: 0 1px 4px rgba(0,0,0,0.4);
4250
+ .uvf-doubletap-indicator.right {
4251
+ right: 15%;
4158
4252
  }
4159
4253
 
4160
- .uvf-control-btn svg {
4161
- fill: #fff;
4254
+ .uvf-doubletap-indicator.active {
4162
4255
  opacity: 1;
4256
+ animation: doubletap-pulse 0.4s cubic-bezier(0.4, 0, 0.2, 1);
4163
4257
  }
4164
4258
 
4165
- /* Right controls */
4166
- .uvf-right-controls {
4167
- margin-left: auto;
4168
- display: flex !important;
4169
- align-items: center;
4170
- gap: 8px;
4171
- opacity: 1 !important;
4172
- visibility: visible !important;
4259
+ @keyframes doubletap-pulse {
4260
+ 0% {
4261
+ transform: translateY(-50%) scale(0.8);
4262
+ opacity: 0;
4263
+ }
4264
+ 50% {
4265
+ transform: translateY(-50%) scale(1.1);
4266
+ opacity: 1;
4267
+ }
4268
+ 100% {
4269
+ transform: translateY(-50%) scale(1);
4270
+ opacity: 1;
4271
+ }
4272
+ }
4273
+
4274
+ .uvf-doubletap-indicator svg {
4275
+ width: 40px;
4276
+ height: 40px;
4277
+ fill: #fff;
4173
4278
  }
4174
4279
 
4175
- /* Center play button positioned within video */
4176
- .uvf-center-play-container {
4280
+ /* Long-press 2x speed indicator */
4281
+ .uvf-longpress-indicator {
4177
4282
  position: absolute;
4178
4283
  top: 50%;
4179
4284
  left: 50%;
4180
4285
  transform: translate(-50%, -50%);
4181
- z-index: 8;
4286
+ background: rgba(0, 0, 0, 0.8);
4287
+ backdrop-filter: blur(16px);
4288
+ padding: 16px 24px;
4289
+ border-radius: 24px;
4290
+ color: #fff;
4291
+ font-size: 18px;
4292
+ font-weight: 600;
4182
4293
  pointer-events: none;
4294
+ opacity: 0;
4295
+ z-index: 100;
4296
+ transition: opacity 0.2s ease;
4297
+ /* Material elevation level 4 */
4298
+ box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
4299
+ 0 10px 10px rgba(0, 0, 0, 0.22);
4183
4300
  }
4184
4301
 
4185
- .uvf-center-play-btn {
4186
- pointer-events: auto;
4302
+ .uvf-longpress-indicator.active {
4303
+ opacity: 1;
4187
4304
  }
4188
4305
 
4189
- /* Top controls within video */
4190
- .uvf-top-controls {
4191
- position: absolute;
4192
- top: calc(12px + var(--uvf-safe-area-top));
4193
- right: calc(16px + var(--uvf-safe-area-right));
4194
- z-index: 9;
4306
+ /* Hide desktop elements in Material You mode */
4307
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-top-controls,
4308
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-title-bar,
4309
+ .uvf-player-wrapper.uvf-material-you-mobile .uvf-volume-control {
4310
+ display: none !important;
4195
4311
  }
4196
4312
 
4197
- /* Title bar within video */
4198
- .uvf-title-bar {
4199
- position: absolute;
4200
- top: calc(12px + var(--uvf-safe-area-top));
4201
- left: calc(16px + var(--uvf-safe-area-left));
4202
- right: calc(80px + var(--uvf-safe-area-right));
4203
- z-index: 9;
4204
- padding: 8px 0;
4313
+ /* Optimize settings button for Material You */
4314
+ .uvf-player-wrapper.uvf-material-you-mobile #uvf-settings-btn {
4315
+ width: 48px !important;
4316
+ height: 48px !important;
4317
+ min-width: 48px !important;
4318
+ min-height: 48px !important;
4319
+ border-radius: 24px !important;
4320
+ }
4321
+ }
4322
+
4323
+ /* Mobile devices (portrait) - Enhanced UX with Safe Areas */
4324
+ @media screen and (max-width: 767px) and (orientation: portrait) {
4325
+ .uvf-responsive-container {
4326
+ padding: 0;
4327
+ width: 100vw !important;
4328
+ height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4329
+ margin: 0;
4330
+ position: relative;
4331
+ overflow: hidden;
4332
+ }
4333
+
4334
+ @supports (height: 100dvh) {
4335
+ .uvf-responsive-container {
4336
+ height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4337
+ }
4338
+ }
4339
+
4340
+ .uvf-responsive-container .uvf-player-wrapper {
4341
+ width: 100vw !important;
4342
+ height: 100% !important;
4343
+ min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4344
+ }
4345
+
4346
+ @supports (height: 100dvh) {
4347
+ .uvf-responsive-container .uvf-player-wrapper {
4348
+ min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4349
+ }
4350
+ }
4351
+
4352
+ .uvf-responsive-container .uvf-video-container {
4353
+ width: 100vw !important;
4354
+ height: 100% !important;
4355
+ aspect-ratio: unset !important;
4356
+ min-height: inherit;
4357
+ }
4358
+
4359
+ /* Enhanced mobile controls bar with safe area padding - iOS Safari specific fixes */
4360
+ .uvf-controls-bar {
4361
+ position: absolute !important;
4362
+ bottom: 0 !important;
4363
+ left: 0 !important;
4364
+ right: 0 !important;
4365
+ padding: 16px 12px;
4366
+ padding-bottom: calc(16px + var(--uvf-safe-area-bottom, 0px));
4367
+ padding-left: calc(12px + var(--uvf-safe-area-left, 0px));
4368
+ padding-right: calc(12px + var(--uvf-safe-area-right, 0px));
4369
+ background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
4370
+ box-sizing: border-box;
4371
+ z-index: 1000 !important;
4372
+ /* iOS Safari specific fixes */
4373
+ transform: translateZ(0);
4374
+ -webkit-transform: translateZ(0);
4375
+ will-change: transform;
4376
+ /* Ensure proper stacking */
4377
+ isolation: isolate;
4205
4378
  }
4206
4379
 
4207
4380
  .uvf-progress-section {
@@ -5441,6 +5614,21 @@ export class WebPlayer extends BasePlayer {
5441
5614
  shortcutIndicator.className = 'uvf-shortcut-indicator';
5442
5615
  shortcutIndicator.id = 'uvf-shortcut-indicator';
5443
5616
  container.appendChild(shortcutIndicator);
5617
+ const doubleTapLeft = document.createElement('div');
5618
+ doubleTapLeft.className = 'uvf-doubletap-indicator left';
5619
+ doubleTapLeft.id = 'uvf-doubletap-left';
5620
+ doubleTapLeft.innerHTML = '<svg viewBox="0 0 24 24"><path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z"/></svg><div style="margin-top:4px;font-size:14px;font-weight:600;">-10s</div>';
5621
+ container.appendChild(doubleTapLeft);
5622
+ const doubleTapRight = document.createElement('div');
5623
+ doubleTapRight.className = 'uvf-doubletap-indicator right';
5624
+ doubleTapRight.id = 'uvf-doubletap-right';
5625
+ doubleTapRight.innerHTML = '<svg viewBox="0 0 24 24"><path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z"/></svg><div style="margin-top:4px;font-size:14px;font-weight:600;">+10s</div>';
5626
+ container.appendChild(doubleTapRight);
5627
+ const longPressIndicator = document.createElement('div');
5628
+ longPressIndicator.className = 'uvf-longpress-indicator';
5629
+ longPressIndicator.id = 'uvf-longpress-indicator';
5630
+ longPressIndicator.textContent = '2x';
5631
+ container.appendChild(longPressIndicator);
5444
5632
  const controlsBar = document.createElement('div');
5445
5633
  controlsBar.className = 'uvf-controls-bar';
5446
5634
  controlsBar.id = 'uvf-controls';
@@ -5626,15 +5814,8 @@ export class WebPlayer extends BasePlayer {
5626
5814
  <rect x="19" y="3" width="2" height="2"/>
5627
5815
  <path d="M17 1v2h2V1h2v2h1c.55 0 1 .45 1 1v16c0 .55-.45 1-1 1H2c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h1V1h2v2h12z" fill="none" stroke="currentColor" stroke-width="0.5"/>
5628
5816
  </svg>`;
5629
- epgBtn.style.display = 'none !important';
5630
- const epgConfig = this.config.epg;
5631
- if (epgConfig && epgConfig.enabled) {
5632
- rightControls.appendChild(epgBtn);
5633
- this.debugLog('EPG button created but hidden - will show when EPG data is loaded');
5634
- }
5635
- else {
5636
- this.debugLog('EPG button not created - EPG functionality disabled');
5637
- }
5817
+ epgBtn.style.display = 'none';
5818
+ rightControls.appendChild(epgBtn);
5638
5819
  const pipBtn = document.createElement('button');
5639
5820
  pipBtn.className = 'uvf-control-btn';
5640
5821
  pipBtn.id = 'uvf-pip-btn';
@@ -5680,7 +5861,7 @@ export class WebPlayer extends BasePlayer {
5680
5861
  });
5681
5862
  centerPlay?.addEventListener('click', () => this.togglePlayPause());
5682
5863
  playPauseBtn?.addEventListener('click', () => this.togglePlayPause());
5683
- this.video.addEventListener('click', () => this.togglePlayPause());
5864
+ this.setupMaterialYouGestures(wrapper);
5684
5865
  this.video.addEventListener('play', () => {
5685
5866
  const playIcon = document.getElementById('uvf-play-icon');
5686
5867
  const pauseIcon = document.getElementById('uvf-pause-icon');
@@ -5973,7 +6154,12 @@ export class WebPlayer extends BasePlayer {
5973
6154
  this.debugLog('Settings menu classes before toggle:', Array.from(settingsMenu?.classList || []).join(' '));
5974
6155
  settingsMenu?.classList.toggle('active');
5975
6156
  if (settingsMenu) {
5976
- if (settingsMenu.classList.contains('active')) {
6157
+ const activating = settingsMenu.classList.contains('active');
6158
+ if (activating) {
6159
+ this.isSettingsOpen = true;
6160
+ this.showControls();
6161
+ if (this.hideControlsTimeout)
6162
+ clearTimeout(this.hideControlsTimeout);
5977
6163
  settingsMenu.style.display = 'block';
5978
6164
  settingsMenu.style.visibility = 'visible';
5979
6165
  settingsMenu.style.opacity = '1';
@@ -5990,10 +6176,12 @@ export class WebPlayer extends BasePlayer {
5990
6176
  this.debugLog('Applied fallback styles to show menu');
5991
6177
  }
5992
6178
  else {
6179
+ this.isSettingsOpen = false;
5993
6180
  settingsMenu.style.display = 'none';
5994
6181
  settingsMenu.style.visibility = 'hidden';
5995
6182
  settingsMenu.style.opacity = '0';
5996
6183
  this.debugLog('Applied fallback styles to hide menu');
6184
+ this.scheduleHideControls();
5997
6185
  }
5998
6186
  }
5999
6187
  this.debugLog('Settings menu classes after toggle:', Array.from(settingsMenu?.classList || []).join(' '));
@@ -6021,6 +6209,19 @@ export class WebPlayer extends BasePlayer {
6021
6209
  this.hideSettingsMenu();
6022
6210
  }
6023
6211
  });
6212
+ if (settingsMenu) {
6213
+ const keepAlive = () => {
6214
+ if (!this.isSettingsOpen)
6215
+ return;
6216
+ this.showControls();
6217
+ if (this.hideControlsTimeout)
6218
+ clearTimeout(this.hideControlsTimeout);
6219
+ };
6220
+ settingsMenu.addEventListener('mouseenter', keepAlive);
6221
+ settingsMenu.addEventListener('mousemove', keepAlive);
6222
+ settingsMenu.addEventListener('touchstart', keepAlive, { passive: true });
6223
+ settingsMenu.addEventListener('touchmove', keepAlive, { passive: true });
6224
+ }
6024
6225
  document.addEventListener('keydown', (e) => {
6025
6226
  if (e.key === 'Escape' && settingsMenu?.classList.contains('active')) {
6026
6227
  this.hideSettingsMenu();
@@ -6461,6 +6662,187 @@ export class WebPlayer extends BasePlayer {
6461
6662
  this.mute();
6462
6663
  }
6463
6664
  }
6665
+ setupMaterialYouGestures(wrapper) {
6666
+ if (!wrapper || !this.video)
6667
+ return;
6668
+ const doubleTapLeft = document.getElementById('uvf-doubletap-left');
6669
+ const doubleTapRight = document.getElementById('uvf-doubletap-right');
6670
+ const longPressIndicator = document.getElementById('uvf-longpress-indicator');
6671
+ const videoElement = this.video;
6672
+ const videoContainer = wrapper.querySelector('.uvf-video-container');
6673
+ if (!videoContainer)
6674
+ return;
6675
+ videoContainer.addEventListener('touchstart', (e) => {
6676
+ const touchEvent = e;
6677
+ const touch = touchEvent.touches[0];
6678
+ if (!touch || !videoElement)
6679
+ return;
6680
+ this.longPressStartTime = Date.now();
6681
+ this.longPressTimer = setTimeout(() => {
6682
+ this.longPressActive = true;
6683
+ this.originalPlaybackRate = videoElement.playbackRate;
6684
+ videoElement.playbackRate = 2.0;
6685
+ if (longPressIndicator) {
6686
+ longPressIndicator.classList.add('active');
6687
+ }
6688
+ }, 500);
6689
+ }, { passive: true });
6690
+ videoContainer.addEventListener('touchend', (e) => {
6691
+ const touchEvent = e;
6692
+ const touch = touchEvent.changedTouches[0];
6693
+ if (!touch || !videoElement)
6694
+ return;
6695
+ if (this.longPressTimer) {
6696
+ clearTimeout(this.longPressTimer);
6697
+ this.longPressTimer = null;
6698
+ }
6699
+ if (this.longPressActive) {
6700
+ videoElement.playbackRate = this.originalPlaybackRate;
6701
+ this.longPressActive = false;
6702
+ if (longPressIndicator) {
6703
+ longPressIndicator.classList.remove('active');
6704
+ }
6705
+ return;
6706
+ }
6707
+ const now = Date.now();
6708
+ const timeSinceLastTap = now - this.lastTapTime;
6709
+ if (timeSinceLastTap < 300) {
6710
+ if (this.tapTimeout) {
6711
+ clearTimeout(this.tapTimeout);
6712
+ this.tapTimeout = null;
6713
+ }
6714
+ const rect = videoContainer.getBoundingClientRect();
6715
+ const x = touch.clientX - rect.left;
6716
+ const isLeftSide = x < rect.width / 2;
6717
+ if (isLeftSide) {
6718
+ this.seek(videoElement.currentTime - 10);
6719
+ if (doubleTapLeft) {
6720
+ doubleTapLeft.classList.add('active');
6721
+ setTimeout(() => {
6722
+ doubleTapLeft.classList.remove('active');
6723
+ }, 400);
6724
+ }
6725
+ }
6726
+ else {
6727
+ this.seek(videoElement.currentTime + 10);
6728
+ if (doubleTapRight) {
6729
+ doubleTapRight.classList.add('active');
6730
+ setTimeout(() => {
6731
+ doubleTapRight.classList.remove('active');
6732
+ }, 400);
6733
+ }
6734
+ }
6735
+ this.tapCount = 0;
6736
+ this.lastTapTime = 0;
6737
+ }
6738
+ else {
6739
+ this.tapCount = 1;
6740
+ this.lastTapTime = now;
6741
+ this.tapTimeout = setTimeout(() => {
6742
+ if (this.tapCount === 1) {
6743
+ this.toggleControls();
6744
+ }
6745
+ this.tapCount = 0;
6746
+ }, 300);
6747
+ }
6748
+ }, { passive: true });
6749
+ videoContainer.addEventListener('touchmove', () => {
6750
+ if (this.longPressTimer) {
6751
+ clearTimeout(this.longPressTimer);
6752
+ this.longPressTimer = null;
6753
+ }
6754
+ }, { passive: true });
6755
+ videoContainer.addEventListener('touchcancel', () => {
6756
+ if (this.longPressTimer) {
6757
+ clearTimeout(this.longPressTimer);
6758
+ this.longPressTimer = null;
6759
+ }
6760
+ if (this.longPressActive && videoElement) {
6761
+ videoElement.playbackRate = this.originalPlaybackRate;
6762
+ this.longPressActive = false;
6763
+ if (longPressIndicator) {
6764
+ longPressIndicator.classList.remove('active');
6765
+ }
6766
+ }
6767
+ }, { passive: true });
6768
+ }
6769
+ toggleControls() {
6770
+ const wrapper = this.container?.querySelector('.uvf-player-wrapper');
6771
+ if (wrapper) {
6772
+ if (wrapper.classList.contains('controls-visible')) {
6773
+ this.hideControls();
6774
+ }
6775
+ else {
6776
+ this.showControls();
6777
+ if (this.state.isPlaying) {
6778
+ this.scheduleHideControls();
6779
+ }
6780
+ }
6781
+ }
6782
+ }
6783
+ applyDynamicTheming(primaryColor) {
6784
+ if (!this.playerWrapper)
6785
+ return;
6786
+ const color = primaryColor || this.dominantColor;
6787
+ const rgb = this.hexToRgb(color);
6788
+ if (rgb) {
6789
+ this.playerWrapper.style.setProperty('--uvf-accent-1', color);
6790
+ this.playerWrapper.style.setProperty('--uvf-accent-2', this.lightenColor(color, 10));
6791
+ this.playerWrapper.style.setProperty('--uvf-surface-tint', `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.08)`);
6792
+ this.debugLog(`Applied dynamic theming with color: ${color}`);
6793
+ }
6794
+ }
6795
+ hexToRgb(hex) {
6796
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
6797
+ return result ? {
6798
+ r: parseInt(result[1], 16),
6799
+ g: parseInt(result[2], 16),
6800
+ b: parseInt(result[3], 16)
6801
+ } : null;
6802
+ }
6803
+ lightenColor(hex, percent) {
6804
+ const rgb = this.hexToRgb(hex);
6805
+ if (!rgb)
6806
+ return hex;
6807
+ const amount = Math.floor(255 * (percent / 100));
6808
+ const r = Math.min(255, rgb.r + amount);
6809
+ const g = Math.min(255, rgb.g + amount);
6810
+ const b = Math.min(255, rgb.b + amount);
6811
+ return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
6812
+ }
6813
+ renderChapterMarkersOnProgressBar() {
6814
+ if (!this.video || !this.chapterConfig.enabled || !this.chapterConfig.showChapterMarkers) {
6815
+ return;
6816
+ }
6817
+ const progressBar = document.querySelector('.uvf-progress-bar');
6818
+ if (!progressBar)
6819
+ return;
6820
+ const existingMarkers = progressBar.querySelectorAll('.uvf-chapter-marker');
6821
+ existingMarkers.forEach(marker => marker.remove());
6822
+ const chapters = this.chapterConfig.data;
6823
+ if (!chapters || !Array.isArray(chapters) || chapters.length === 0) {
6824
+ return;
6825
+ }
6826
+ const duration = this.video.duration;
6827
+ if (!duration || duration === 0)
6828
+ return;
6829
+ chapters.forEach((chapter) => {
6830
+ if (!chapter.startTime && chapter.startTime !== 0)
6831
+ return;
6832
+ const marker = document.createElement('div');
6833
+ marker.className = 'uvf-chapter-marker';
6834
+ if (chapter.type) {
6835
+ marker.classList.add(chapter.type.toLowerCase());
6836
+ }
6837
+ const percent = (chapter.startTime / duration) * 100;
6838
+ marker.style.left = `${percent}%`;
6839
+ if (chapter.title) {
6840
+ marker.setAttribute('title', chapter.title);
6841
+ }
6842
+ progressBar.appendChild(marker);
6843
+ });
6844
+ this.debugLog(`Rendered ${chapters.length} chapter markers on progress bar`);
6845
+ }
6464
6846
  isMobileDevice() {
6465
6847
  const userAgent = navigator.userAgent.toLowerCase();
6466
6848
  const mobileKeywords = ['android', 'iphone', 'ipad', 'ipod', 'blackberry', 'windows phone', 'mobile'];
@@ -6574,6 +6956,8 @@ export class WebPlayer extends BasePlayer {
6574
6956
  hideControls() {
6575
6957
  if (!this.state.isPlaying)
6576
6958
  return;
6959
+ if (this.isSettingsOpen)
6960
+ return;
6577
6961
  const wrapper = this.container?.querySelector('.uvf-player-wrapper');
6578
6962
  if (wrapper) {
6579
6963
  wrapper.classList.remove('controls-visible');
@@ -6583,11 +6967,13 @@ export class WebPlayer extends BasePlayer {
6583
6967
  scheduleHideControls() {
6584
6968
  if (!this.state.isPlaying)
6585
6969
  return;
6970
+ if (this.isSettingsOpen)
6971
+ return;
6586
6972
  if (this.hideControlsTimeout)
6587
6973
  clearTimeout(this.hideControlsTimeout);
6588
6974
  const timeout = this.isFullscreen() ? 4000 : 3000;
6589
6975
  this.hideControlsTimeout = setTimeout(() => {
6590
- if (this.state.isPlaying && !this.controlsContainer?.matches(':hover')) {
6976
+ if (this.state.isPlaying && !this.controlsContainer?.matches(':hover') && !this.isSettingsOpen) {
6591
6977
  this.hideControls();
6592
6978
  }
6593
6979
  }, timeout);
@@ -7624,6 +8010,7 @@ export class WebPlayer extends BasePlayer {
7624
8010
  if (!settingsMenu)
7625
8011
  return;
7626
8012
  settingsMenu.classList.remove('active');
8013
+ this.isSettingsOpen = false;
7627
8014
  settingsMenu.style.display = 'none';
7628
8015
  settingsMenu.style.visibility = 'hidden';
7629
8016
  settingsMenu.style.opacity = '0';
@@ -7631,6 +8018,7 @@ export class WebPlayer extends BasePlayer {
7631
8018
  item.classList.remove('expanded');
7632
8019
  });
7633
8020
  this.debugLog('Settings menu hidden via hideSettingsMenu()');
8021
+ this.scheduleHideControls();
7634
8022
  }
7635
8023
  updateAccordionAfterSelection(section) {
7636
8024
  setTimeout(() => {
@@ -8114,8 +8502,8 @@ export class WebPlayer extends BasePlayer {
8114
8502
  showEPGButton() {
8115
8503
  const epgBtn = document.getElementById('uvf-epg-btn');
8116
8504
  if (epgBtn) {
8117
- epgBtn.style.setProperty('display', 'block', 'important');
8118
- this.debugLog('EPG button shown with !important override');
8505
+ epgBtn.style.display = 'block';
8506
+ this.debugLog('EPG button shown');
8119
8507
  }
8120
8508
  else {
8121
8509
  this.debugLog('EPG button not found in DOM');
@@ -8124,8 +8512,8 @@ export class WebPlayer extends BasePlayer {
8124
8512
  hideEPGButton() {
8125
8513
  const epgBtn = document.getElementById('uvf-epg-btn');
8126
8514
  if (epgBtn) {
8127
- epgBtn.style.setProperty('display', 'none', 'important');
8128
- this.debugLog('EPG button hidden with !important');
8515
+ epgBtn.style.display = 'none';
8516
+ this.debugLog('EPG button hidden');
8129
8517
  }
8130
8518
  }
8131
8519
  setEPGData(epgData) {
@@ -8141,26 +8529,7 @@ export class WebPlayer extends BasePlayer {
8141
8529
  }
8142
8530
  isEPGButtonVisible() {
8143
8531
  const epgBtn = document.getElementById('uvf-epg-btn');
8144
- if (epgBtn) {
8145
- const isVisible = epgBtn.style.display !== 'none';
8146
- this.debugLog(`EPG button visibility check: ${isVisible}, display style: ${epgBtn.style.display}`);
8147
- return isVisible;
8148
- }
8149
- this.debugLog('EPG button visibility check: false (button not found in DOM)');
8150
- return false;
8151
- }
8152
- debugEPGState() {
8153
- const epgBtn = document.getElementById('uvf-epg-btn');
8154
- const epgConfig = this.config.epg;
8155
- this.debugLog('=== EPG DEBUG STATE ===');
8156
- this.debugLog(`EPG Config Enabled: ${epgConfig?.enabled || false}`);
8157
- this.debugLog(`EPG Button Exists: ${!!epgBtn}`);
8158
- if (epgBtn) {
8159
- this.debugLog(`EPG Button Display Style: ${epgBtn.style.display}`);
8160
- this.debugLog(`EPG Button Computed Display: ${window.getComputedStyle(epgBtn).display}`);
8161
- this.debugLog(`EPG Button Visible: ${this.isEPGButtonVisible()}`);
8162
- }
8163
- this.debugLog('=====================');
8532
+ return epgBtn ? epgBtn.style.display !== 'none' : false;
8164
8533
  }
8165
8534
  async cleanup() {
8166
8535
  if (this.hls) {