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.
- package/package.json +1 -1
- package/packages/core/dist/interfaces.d.ts +0 -1
- package/packages/core/dist/interfaces.d.ts.map +1 -1
- package/packages/core/src/interfaces.ts +0 -1
- package/packages/web/dist/WebPlayer.d.ts +18 -1
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +605 -236
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/src/WebPlayer.ts +712 -244
|
@@ -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
|
-
|
|
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
|
-
/*
|
|
3936
|
-
.uvf-
|
|
3937
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3948
|
-
margin:
|
|
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
|
-
/*
|
|
3952
|
-
.uvf-
|
|
4008
|
+
/* Top black section (25%) - Tap zone overlay */
|
|
4009
|
+
.uvf-player-wrapper.uvf-material-you-mobile::before {
|
|
3953
4010
|
content: '';
|
|
3954
|
-
|
|
4011
|
+
position: absolute;
|
|
4012
|
+
top: 0;
|
|
4013
|
+
left: 0;
|
|
4014
|
+
width: 100vw;
|
|
4015
|
+
height: 25vh;
|
|
4016
|
+
height: 25dvh;
|
|
3955
4017
|
background: #000;
|
|
3956
|
-
|
|
4018
|
+
z-index: 1;
|
|
4019
|
+
pointer-events: all;
|
|
4020
|
+
touch-action: manipulation;
|
|
3957
4021
|
}
|
|
3958
4022
|
|
|
3959
|
-
/*
|
|
3960
|
-
.uvf-
|
|
4023
|
+
/* Bottom black section (25%) - Surface container */
|
|
4024
|
+
.uvf-player-wrapper.uvf-material-you-mobile::after {
|
|
3961
4025
|
content: '';
|
|
3962
|
-
|
|
3963
|
-
|
|
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
|
-
/*
|
|
3968
|
-
.uvf-
|
|
3969
|
-
|
|
3970
|
-
|
|
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
|
-
|
|
3973
|
-
justify-content:
|
|
3974
|
-
|
|
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
|
-
|
|
3985
|
-
|
|
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
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
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
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
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
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
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
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
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
|
-
/*
|
|
4078
|
-
.uvf-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
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
|
-
|
|
4087
|
-
|
|
4088
|
-
opacity: 1 !important;
|
|
4089
|
-
visibility: visible !important;
|
|
4128
|
+
.uvf-chapter-marker.intro {
|
|
4129
|
+
background: #4CAF50;
|
|
4090
4130
|
}
|
|
4091
4131
|
|
|
4092
|
-
.uvf-
|
|
4093
|
-
|
|
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-
|
|
4099
|
-
background:
|
|
4100
|
-
height: 100%;
|
|
4101
|
-
border-radius: 2px;
|
|
4136
|
+
.uvf-chapter-marker.credits {
|
|
4137
|
+
background: #9C27B0;
|
|
4102
4138
|
}
|
|
4103
4139
|
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
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
|
-
/*
|
|
4117
|
-
.uvf-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
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
|
-
/*
|
|
4127
|
-
.uvf-control-btn {
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
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
|
-
|
|
4133
|
-
|
|
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
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
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
|
-
|
|
4143
|
-
|
|
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
|
-
|
|
4148
|
-
|
|
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-
|
|
4156
|
-
|
|
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-
|
|
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
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
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
|
-
/*
|
|
4176
|
-
.uvf-
|
|
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
|
-
|
|
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-
|
|
4186
|
-
|
|
4302
|
+
.uvf-longpress-indicator.active {
|
|
4303
|
+
opacity: 1;
|
|
4187
4304
|
}
|
|
4188
4305
|
|
|
4189
|
-
/*
|
|
4190
|
-
.uvf-top-controls
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
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
|
-
/*
|
|
4198
|
-
.uvf-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
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
|
|
5630
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
8118
|
-
this.debugLog('EPG button shown
|
|
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.
|
|
8128
|
-
this.debugLog('EPG button hidden
|
|
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
|
-
|
|
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) {
|