unified-video-framework 1.4.165 → 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.
|
@@ -53,6 +53,16 @@ export class WebPlayer extends BasePlayer {
|
|
|
53
53
|
this.hasTriedButtonFallback = false;
|
|
54
54
|
this.lastUserInteraction = 0;
|
|
55
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)';
|
|
56
66
|
this.autoplayCapabilities = {
|
|
57
67
|
canAutoplay: false,
|
|
58
68
|
canAutoplayMuted: false,
|
|
@@ -230,6 +240,26 @@ export class WebPlayer extends BasePlayer {
|
|
|
230
240
|
catch (_) { }
|
|
231
241
|
this.setupCastContextSafe();
|
|
232
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
|
+
}
|
|
233
263
|
}
|
|
234
264
|
setupVideoEventListeners() {
|
|
235
265
|
if (!this.video)
|
|
@@ -307,6 +337,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
307
337
|
this.state.duration = this.video.duration || 0;
|
|
308
338
|
this.debugLog('Metadata loaded - duration:', this.video.duration);
|
|
309
339
|
this.updateTimeDisplay();
|
|
340
|
+
this.renderChapterMarkersOnProgressBar();
|
|
310
341
|
this.emit('onLoadedMetadata', {
|
|
311
342
|
duration: this.video.duration || 0,
|
|
312
343
|
width: this.video.videoWidth || 0,
|
|
@@ -1876,6 +1907,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
1876
1907
|
--uvf-scrollbar-thumb-hover-start: rgba(255,0,0,0.5);
|
|
1877
1908
|
--uvf-scrollbar-thumb-hover-end: rgba(255,0,0,0.6);
|
|
1878
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);
|
|
1879
1912
|
}
|
|
1880
1913
|
|
|
1881
1914
|
/* Player focus styles for better UX */
|
|
@@ -3931,6 +3964,362 @@ export class WebPlayer extends BasePlayer {
|
|
|
3931
3964
|
}
|
|
3932
3965
|
|
|
3933
3966
|
/* Enhanced Responsive Media Queries with UX Best Practices */
|
|
3967
|
+
|
|
3968
|
+
/* Material You Mobile Portrait Layout - 25% Black + 50% Video + 25% Black */
|
|
3969
|
+
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
3970
|
+
/* Enable Material You mode with class flag */
|
|
3971
|
+
.uvf-player-wrapper.uvf-material-you-mobile {
|
|
3972
|
+
/* Full viewport height layout */
|
|
3973
|
+
height: 100vh;
|
|
3974
|
+
height: 100dvh;
|
|
3975
|
+
width: 100vw;
|
|
3976
|
+
position: fixed;
|
|
3977
|
+
top: 0;
|
|
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;
|
|
3989
|
+
width: 100vw;
|
|
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;
|
|
4006
|
+
}
|
|
4007
|
+
|
|
4008
|
+
/* Top black section (25%) - Tap zone overlay */
|
|
4009
|
+
.uvf-player-wrapper.uvf-material-you-mobile::before {
|
|
4010
|
+
content: '';
|
|
4011
|
+
position: absolute;
|
|
4012
|
+
top: 0;
|
|
4013
|
+
left: 0;
|
|
4014
|
+
width: 100vw;
|
|
4015
|
+
height: 25vh;
|
|
4016
|
+
height: 25dvh;
|
|
4017
|
+
background: #000;
|
|
4018
|
+
z-index: 1;
|
|
4019
|
+
pointer-events: all;
|
|
4020
|
+
touch-action: manipulation;
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
/* Bottom black section (25%) - Surface container */
|
|
4024
|
+
.uvf-player-wrapper.uvf-material-you-mobile::after {
|
|
4025
|
+
content: '';
|
|
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;
|
|
4037
|
+
pointer-events: none;
|
|
4038
|
+
}
|
|
4039
|
+
|
|
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;
|
|
4053
|
+
display: flex;
|
|
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;
|
|
4075
|
+
position: relative;
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
.uvf-player-wrapper.uvf-material-you-mobile .uvf-progress-bar-wrapper {
|
|
4079
|
+
padding: 12px 0;
|
|
4080
|
+
position: relative;
|
|
4081
|
+
}
|
|
4082
|
+
|
|
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);
|
|
4091
|
+
}
|
|
4092
|
+
|
|
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);
|
|
4096
|
+
}
|
|
4097
|
+
|
|
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);
|
|
4106
|
+
}
|
|
4107
|
+
|
|
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);
|
|
4112
|
+
}
|
|
4113
|
+
|
|
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;
|
|
4126
|
+
}
|
|
4127
|
+
|
|
4128
|
+
.uvf-chapter-marker.intro {
|
|
4129
|
+
background: #4CAF50;
|
|
4130
|
+
}
|
|
4131
|
+
|
|
4132
|
+
.uvf-chapter-marker.recap {
|
|
4133
|
+
background: #FFC107;
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
.uvf-chapter-marker.credits {
|
|
4137
|
+
background: #9C27B0;
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4140
|
+
.uvf-progress-bar-wrapper:hover .uvf-chapter-marker {
|
|
4141
|
+
width: 4px;
|
|
4142
|
+
opacity: 1;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
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;
|
|
4159
|
+
}
|
|
4160
|
+
|
|
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;
|
|
4208
|
+
align-items: center;
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
/* Time display with Material surface */
|
|
4212
|
+
.uvf-player-wrapper.uvf-material-you-mobile .uvf-time-display {
|
|
4213
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4214
|
+
backdrop-filter: blur(8px);
|
|
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);
|
|
4222
|
+
}
|
|
4223
|
+
|
|
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;
|
|
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);
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
.uvf-doubletap-indicator.left {
|
|
4247
|
+
left: 15%;
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
.uvf-doubletap-indicator.right {
|
|
4251
|
+
right: 15%;
|
|
4252
|
+
}
|
|
4253
|
+
|
|
4254
|
+
.uvf-doubletap-indicator.active {
|
|
4255
|
+
opacity: 1;
|
|
4256
|
+
animation: doubletap-pulse 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
4257
|
+
}
|
|
4258
|
+
|
|
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;
|
|
4278
|
+
}
|
|
4279
|
+
|
|
4280
|
+
/* Long-press 2x speed indicator */
|
|
4281
|
+
.uvf-longpress-indicator {
|
|
4282
|
+
position: absolute;
|
|
4283
|
+
top: 50%;
|
|
4284
|
+
left: 50%;
|
|
4285
|
+
transform: translate(-50%, -50%);
|
|
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;
|
|
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);
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
.uvf-longpress-indicator.active {
|
|
4303
|
+
opacity: 1;
|
|
4304
|
+
}
|
|
4305
|
+
|
|
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;
|
|
4311
|
+
}
|
|
4312
|
+
|
|
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
|
+
|
|
3934
4323
|
/* Mobile devices (portrait) - Enhanced UX with Safe Areas */
|
|
3935
4324
|
@media screen and (max-width: 767px) and (orientation: portrait) {
|
|
3936
4325
|
.uvf-responsive-container {
|
|
@@ -5225,6 +5614,21 @@ export class WebPlayer extends BasePlayer {
|
|
|
5225
5614
|
shortcutIndicator.className = 'uvf-shortcut-indicator';
|
|
5226
5615
|
shortcutIndicator.id = 'uvf-shortcut-indicator';
|
|
5227
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);
|
|
5228
5632
|
const controlsBar = document.createElement('div');
|
|
5229
5633
|
controlsBar.className = 'uvf-controls-bar';
|
|
5230
5634
|
controlsBar.id = 'uvf-controls';
|
|
@@ -5457,7 +5861,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
5457
5861
|
});
|
|
5458
5862
|
centerPlay?.addEventListener('click', () => this.togglePlayPause());
|
|
5459
5863
|
playPauseBtn?.addEventListener('click', () => this.togglePlayPause());
|
|
5460
|
-
this.
|
|
5864
|
+
this.setupMaterialYouGestures(wrapper);
|
|
5461
5865
|
this.video.addEventListener('play', () => {
|
|
5462
5866
|
const playIcon = document.getElementById('uvf-play-icon');
|
|
5463
5867
|
const pauseIcon = document.getElementById('uvf-pause-icon');
|
|
@@ -6258,6 +6662,187 @@ export class WebPlayer extends BasePlayer {
|
|
|
6258
6662
|
this.mute();
|
|
6259
6663
|
}
|
|
6260
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
|
+
}
|
|
6261
6846
|
isMobileDevice() {
|
|
6262
6847
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
6263
6848
|
const mobileKeywords = ['android', 'iphone', 'ipad', 'ipod', 'blackberry', 'windows phone', 'mobile'];
|