unified-video-framework 1.4.211 → 1.4.213
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.
|
@@ -1328,9 +1328,57 @@ export class WebPlayer extends BasePlayer {
|
|
|
1328
1328
|
}
|
|
1329
1329
|
}
|
|
1330
1330
|
|
|
1331
|
+
/**
|
|
1332
|
+
* Safely set video currentTime with validation to prevent non-finite value errors
|
|
1333
|
+
*/
|
|
1334
|
+
private safeSetCurrentTime(time: number): boolean {
|
|
1335
|
+
if (!this.video) {
|
|
1336
|
+
this.debugWarn('Cannot set currentTime: video element not available');
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// Validate the time value is finite
|
|
1341
|
+
if (!isFinite(time) || isNaN(time)) {
|
|
1342
|
+
this.debugWarn('Attempted to set invalid currentTime value:', time);
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Ensure time is non-negative
|
|
1347
|
+
const safeTime = Math.max(0, time);
|
|
1348
|
+
|
|
1349
|
+
// Additional validation: check if video duration is available and valid
|
|
1350
|
+
const duration = this.video.duration;
|
|
1351
|
+
if (isFinite(duration) && duration > 0) {
|
|
1352
|
+
// Clamp time to valid range [0, duration]
|
|
1353
|
+
const clampedTime = Math.min(safeTime, duration);
|
|
1354
|
+
try {
|
|
1355
|
+
this.video.currentTime = clampedTime;
|
|
1356
|
+
return true;
|
|
1357
|
+
} catch (error) {
|
|
1358
|
+
this.debugError('Error setting currentTime:', error);
|
|
1359
|
+
return false;
|
|
1360
|
+
}
|
|
1361
|
+
} else {
|
|
1362
|
+
// Duration not available yet, but still set time if it's valid
|
|
1363
|
+
try {
|
|
1364
|
+
this.video.currentTime = safeTime;
|
|
1365
|
+
return true;
|
|
1366
|
+
} catch (error) {
|
|
1367
|
+
this.debugError('Error setting currentTime:', error);
|
|
1368
|
+
return false;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1331
1373
|
seek(time: number): void {
|
|
1332
1374
|
if (!this.video) return;
|
|
1333
1375
|
|
|
1376
|
+
// Validate input time
|
|
1377
|
+
if (!isFinite(time) || isNaN(time)) {
|
|
1378
|
+
this.debugWarn('Invalid seek time:', time);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1334
1382
|
// Security check: Prevent seeking beyond free preview limit
|
|
1335
1383
|
const freeDuration = Number(this.config.freeDuration || 0);
|
|
1336
1384
|
if (freeDuration > 0 && !this.paymentSuccessful) {
|
|
@@ -1339,17 +1387,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
1339
1387
|
this.debugWarn('Seek blocked - beyond free preview limit');
|
|
1340
1388
|
this.enforcePaywallSecurity();
|
|
1341
1389
|
// Reset to safe position
|
|
1342
|
-
this.
|
|
1390
|
+
this.safeSetCurrentTime(freeDuration - 1);
|
|
1343
1391
|
return;
|
|
1344
1392
|
}
|
|
1345
1393
|
}
|
|
1346
1394
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
this.video.currentTime = Math.max(0, Math.min(time, d));
|
|
1350
|
-
} else {
|
|
1351
|
-
this.video.currentTime = Math.max(0, time);
|
|
1352
|
-
}
|
|
1395
|
+
// Use safe setter with validated time
|
|
1396
|
+
this.safeSetCurrentTime(time);
|
|
1353
1397
|
}
|
|
1354
1398
|
|
|
1355
1399
|
setVolume(level: number): void {
|
|
@@ -3386,6 +3430,99 @@ export class WebPlayer extends BasePlayer {
|
|
|
3386
3430
|
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));
|
|
3387
3431
|
}
|
|
3388
3432
|
|
|
3433
|
+
/* Cast Button Specific Styling */
|
|
3434
|
+
#uvf-cast-btn {
|
|
3435
|
+
background: var(--uvf-button-bg);
|
|
3436
|
+
border: 1px solid var(--uvf-button-border);
|
|
3437
|
+
position: relative;
|
|
3438
|
+
z-index: 10;
|
|
3439
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
#uvf-cast-btn:hover {
|
|
3443
|
+
transform: scale(1.08);
|
|
3444
|
+
box-shadow: 0 4px 12px var(--uvf-button-shadow);
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
#uvf-cast-btn:active {
|
|
3448
|
+
transform: scale(0.95);
|
|
3449
|
+
transition: all 0.1s ease;
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
#uvf-cast-btn svg {
|
|
3453
|
+
opacity: 0.9;
|
|
3454
|
+
transition: all 0.3s ease;
|
|
3455
|
+
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3));
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
#uvf-cast-btn:hover svg {
|
|
3459
|
+
opacity: 1;
|
|
3460
|
+
transform: scale(1.05);
|
|
3461
|
+
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
/* Share Button Specific Styling */
|
|
3465
|
+
#uvf-share-btn {
|
|
3466
|
+
background: var(--uvf-button-bg);
|
|
3467
|
+
border: 1px solid var(--uvf-button-border);
|
|
3468
|
+
position: relative;
|
|
3469
|
+
z-index: 10;
|
|
3470
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
#uvf-share-btn:hover {
|
|
3474
|
+
transform: scale(1.08);
|
|
3475
|
+
box-shadow: 0 4px 12px var(--uvf-button-shadow);
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
#uvf-share-btn:active {
|
|
3479
|
+
transform: scale(0.95);
|
|
3480
|
+
transition: all 0.1s ease;
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
#uvf-share-btn svg {
|
|
3484
|
+
opacity: 0.9;
|
|
3485
|
+
transition: all 0.3s ease;
|
|
3486
|
+
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3));
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
#uvf-share-btn:hover svg {
|
|
3490
|
+
opacity: 1;
|
|
3491
|
+
transform: scale(1.05);
|
|
3492
|
+
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
/* EPG Button Specific Styling */
|
|
3496
|
+
#uvf-epg-btn {
|
|
3497
|
+
background: var(--uvf-button-bg);
|
|
3498
|
+
border: 1px solid var(--uvf-button-border);
|
|
3499
|
+
position: relative;
|
|
3500
|
+
z-index: 10;
|
|
3501
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
#uvf-epg-btn:hover {
|
|
3505
|
+
transform: scale(1.08);
|
|
3506
|
+
box-shadow: 0 4px 12px var(--uvf-button-shadow);
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
#uvf-epg-btn:active {
|
|
3510
|
+
transform: scale(0.95);
|
|
3511
|
+
transition: all 0.1s ease;
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
#uvf-epg-btn svg {
|
|
3515
|
+
opacity: 0.9;
|
|
3516
|
+
transition: all 0.3s ease;
|
|
3517
|
+
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3));
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
#uvf-epg-btn:hover svg {
|
|
3521
|
+
opacity: 1;
|
|
3522
|
+
transform: scale(1.05);
|
|
3523
|
+
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3389
3526
|
.uvf-control-btn.play-pause {
|
|
3390
3527
|
width: 50px;
|
|
3391
3528
|
height: 50px;
|
|
@@ -3784,68 +3921,50 @@ export class WebPlayer extends BasePlayer {
|
|
|
3784
3921
|
flex-shrink: 0;
|
|
3785
3922
|
}
|
|
3786
3923
|
|
|
3787
|
-
/* Navigation button styles */
|
|
3924
|
+
/* Navigation button styles - Follow theme like skip/volume buttons */
|
|
3788
3925
|
.uvf-nav-btn {
|
|
3789
3926
|
width: 40px;
|
|
3790
3927
|
height: 40px;
|
|
3791
3928
|
min-width: 40px;
|
|
3792
3929
|
min-height: 40px;
|
|
3793
3930
|
border-radius: 50%;
|
|
3794
|
-
background:
|
|
3795
|
-
border: 1px solid
|
|
3931
|
+
background: var(--uvf-button-bg);
|
|
3932
|
+
border: 1px solid var(--uvf-button-border);
|
|
3796
3933
|
color: white;
|
|
3797
3934
|
cursor: pointer;
|
|
3798
3935
|
display: flex;
|
|
3799
3936
|
align-items: center;
|
|
3800
3937
|
justify-content: center;
|
|
3801
3938
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
3802
|
-
backdrop-filter: blur(
|
|
3939
|
+
backdrop-filter: blur(10px);
|
|
3803
3940
|
position: relative;
|
|
3804
3941
|
overflow: hidden;
|
|
3942
|
+
z-index: 10;
|
|
3805
3943
|
}
|
|
3806
3944
|
|
|
3807
3945
|
.uvf-nav-btn:hover {
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
transform: scale(1.05);
|
|
3811
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
3946
|
+
transform: scale(1.08);
|
|
3947
|
+
box-shadow: 0 4px 12px var(--uvf-button-shadow);
|
|
3812
3948
|
}
|
|
3813
3949
|
|
|
3814
3950
|
.uvf-nav-btn:active {
|
|
3815
3951
|
transform: scale(0.95);
|
|
3952
|
+
transition: all 0.1s ease;
|
|
3816
3953
|
}
|
|
3817
3954
|
|
|
3818
3955
|
.uvf-nav-btn svg {
|
|
3819
3956
|
width: 20px;
|
|
3820
3957
|
height: 20px;
|
|
3821
3958
|
fill: currentColor;
|
|
3822
|
-
|
|
3959
|
+
opacity: 0.9;
|
|
3960
|
+
transition: all 0.3s ease;
|
|
3961
|
+
filter: drop-shadow(0 1px 2px rgba(0,0,0,0.3));
|
|
3823
3962
|
}
|
|
3824
3963
|
|
|
3825
3964
|
.uvf-nav-btn:hover svg {
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
/* Back button specific styles */
|
|
3830
|
-
#uvf-back-btn {
|
|
3831
|
-
background: rgba(0, 0, 0, 0.7);
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
|
-
#uvf-back-btn:hover {
|
|
3835
|
-
background: rgba(255, 255, 255, 0.2);
|
|
3836
|
-
border-color: var(--uvf-accent-1, #ff0000);
|
|
3837
|
-
}
|
|
3838
|
-
|
|
3839
|
-
/* Close button specific styles */
|
|
3840
|
-
#uvf-close-btn {
|
|
3841
|
-
background: rgba(220, 53, 69, 0.8);
|
|
3842
|
-
border-color: rgba(220, 53, 69, 0.6);
|
|
3843
|
-
}
|
|
3844
|
-
|
|
3845
|
-
#uvf-close-btn:hover {
|
|
3846
|
-
background: rgba(220, 53, 69, 1);
|
|
3847
|
-
border-color: rgba(220, 53, 69, 1);
|
|
3848
|
-
box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4);
|
|
3965
|
+
opacity: 1;
|
|
3966
|
+
transform: scale(1.05);
|
|
3967
|
+
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));
|
|
3849
3968
|
}
|
|
3850
3969
|
|
|
3851
3970
|
.uvf-player-wrapper:hover .uvf-top-bar,
|
|
@@ -4301,6 +4420,83 @@ export class WebPlayer extends BasePlayer {
|
|
|
4301
4420
|
100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
|
|
4302
4421
|
}
|
|
4303
4422
|
|
|
4423
|
+
/* Responsive shortcut/volume indicator for mobile and tablet */
|
|
4424
|
+
@media screen and (max-width: 767px) {
|
|
4425
|
+
.uvf-shortcut-indicator {
|
|
4426
|
+
padding: 16px 24px;
|
|
4427
|
+
font-size: 20px;
|
|
4428
|
+
max-width: calc(100vw - 40px);
|
|
4429
|
+
border-radius: 12px;
|
|
4430
|
+
backdrop-filter: blur(10px);
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
/* Volume indicator - more compact on mobile */
|
|
4434
|
+
.uvf-shortcut-indicator .uvf-ki-volume {
|
|
4435
|
+
flex-direction: column;
|
|
4436
|
+
gap: 12px;
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
.uvf-shortcut-indicator .uvf-ki-vol-icon svg {
|
|
4440
|
+
width: 32px;
|
|
4441
|
+
height: 32px;
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
.uvf-shortcut-indicator .uvf-ki-vol-bar {
|
|
4445
|
+
width: clamp(200px, 70vw, 280px);
|
|
4446
|
+
height: 10px;
|
|
4447
|
+
border-radius: 5px;
|
|
4448
|
+
}
|
|
4449
|
+
|
|
4450
|
+
.uvf-shortcut-indicator .uvf-ki-vol-text {
|
|
4451
|
+
font-size: 22px;
|
|
4452
|
+
font-weight: 700;
|
|
4453
|
+
min-width: auto;
|
|
4454
|
+
text-align: center;
|
|
4455
|
+
width: 100%;
|
|
4456
|
+
}
|
|
4457
|
+
|
|
4458
|
+
/* Skip indicators - slightly smaller */
|
|
4459
|
+
.uvf-shortcut-indicator .uvf-ki-skip {
|
|
4460
|
+
width: 90px;
|
|
4461
|
+
height: 90px;
|
|
4462
|
+
}
|
|
4463
|
+
|
|
4464
|
+
.uvf-shortcut-indicator .uvf-ki-skip svg {
|
|
4465
|
+
width: 90px;
|
|
4466
|
+
height: 90px;
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4469
|
+
.uvf-shortcut-indicator .uvf-ki-skip .uvf-ki-skip-num {
|
|
4470
|
+
font-size: 19px;
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4473
|
+
/* Icon indicators */
|
|
4474
|
+
.uvf-shortcut-indicator .uvf-ki svg {
|
|
4475
|
+
width: 56px;
|
|
4476
|
+
height: 56px;
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
.uvf-shortcut-indicator .uvf-ki-text {
|
|
4480
|
+
font-size: 16px;
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
@media screen and (min-width: 768px) and (max-width: 1023px) {
|
|
4485
|
+
/* Tablet optimization */
|
|
4486
|
+
.uvf-shortcut-indicator .uvf-ki-vol-bar {
|
|
4487
|
+
width: 160px;
|
|
4488
|
+
}
|
|
4489
|
+
|
|
4490
|
+
.uvf-shortcut-indicator .uvf-ki-vol-icon svg {
|
|
4491
|
+
width: 34px;
|
|
4492
|
+
height: 34px;
|
|
4493
|
+
}
|
|
4494
|
+
|
|
4495
|
+
.uvf-shortcut-indicator .uvf-ki-vol-text {
|
|
4496
|
+
font-size: 15px;
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4304
4500
|
/* Hide top bar when no cursor */
|
|
4305
4501
|
.uvf-player-wrapper.no-cursor .uvf-top-bar {
|
|
4306
4502
|
opacity: 0 !important;
|
|
@@ -5915,9 +6111,17 @@ export class WebPlayer extends BasePlayer {
|
|
|
5915
6111
|
}
|
|
5916
6112
|
});
|
|
5917
6113
|
|
|
5918
|
-
// Skip buttons
|
|
5919
|
-
skipBackBtn?.addEventListener('click', () =>
|
|
5920
|
-
|
|
6114
|
+
// Skip buttons with null safety
|
|
6115
|
+
skipBackBtn?.addEventListener('click', () => {
|
|
6116
|
+
if (this.video && !isNaN(this.video.duration)) {
|
|
6117
|
+
this.seek(Math.max(0, this.video.currentTime - 10));
|
|
6118
|
+
}
|
|
6119
|
+
});
|
|
6120
|
+
skipForwardBtn?.addEventListener('click', () => {
|
|
6121
|
+
if (this.video && !isNaN(this.video.duration)) {
|
|
6122
|
+
this.seek(Math.min(this.video.duration, this.video.currentTime + 10));
|
|
6123
|
+
}
|
|
6124
|
+
});
|
|
5921
6125
|
|
|
5922
6126
|
// Volume control
|
|
5923
6127
|
volumeBtn?.addEventListener('click', (e) => {
|
|
@@ -5970,13 +6174,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
5970
6174
|
|
|
5971
6175
|
// Progress bar interactions
|
|
5972
6176
|
progressBar?.addEventListener('click', (e) => {
|
|
5973
|
-
this.
|
|
6177
|
+
this.seekToPosition(e as MouseEvent);
|
|
5974
6178
|
});
|
|
5975
6179
|
|
|
5976
6180
|
progressBar?.addEventListener('mousedown', (e) => {
|
|
5977
6181
|
this.isDragging = true;
|
|
5978
6182
|
this.showTimeTooltip = true;
|
|
5979
|
-
this.
|
|
6183
|
+
this.seekToPosition(e as MouseEvent);
|
|
5980
6184
|
this.updateTimeTooltip(e as MouseEvent);
|
|
5981
6185
|
});
|
|
5982
6186
|
|
|
@@ -6003,7 +6207,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
6003
6207
|
e.preventDefault(); // Prevent scrolling
|
|
6004
6208
|
this.isDragging = true;
|
|
6005
6209
|
const touch = e.touches[0];
|
|
6006
|
-
|
|
6210
|
+
const mouseEvent = new MouseEvent('mousedown', {
|
|
6211
|
+
clientX: touch.clientX,
|
|
6212
|
+
clientY: touch.clientY
|
|
6213
|
+
});
|
|
6214
|
+
this.seekToPosition(mouseEvent);
|
|
6007
6215
|
}, { passive: false });
|
|
6008
6216
|
|
|
6009
6217
|
// Global mouse and touch events for enhanced dragging
|
|
@@ -6012,7 +6220,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6012
6220
|
this.handleVolumeChange(e);
|
|
6013
6221
|
}
|
|
6014
6222
|
if (this.isDragging && progressBar) {
|
|
6015
|
-
this.
|
|
6223
|
+
this.seekToPosition(e);
|
|
6016
6224
|
// Update tooltip position during dragging
|
|
6017
6225
|
this.updateTimeTooltip(e);
|
|
6018
6226
|
}
|
|
@@ -6022,7 +6230,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
6022
6230
|
if (this.isDragging && progressBar) {
|
|
6023
6231
|
e.preventDefault(); // Prevent scrolling
|
|
6024
6232
|
const touch = e.touches[0];
|
|
6025
|
-
|
|
6233
|
+
const mouseEvent = new MouseEvent('mousemove', {
|
|
6234
|
+
clientX: touch.clientX,
|
|
6235
|
+
clientY: touch.clientY
|
|
6236
|
+
});
|
|
6237
|
+
this.seekToPosition(mouseEvent);
|
|
6026
6238
|
}
|
|
6027
6239
|
}, { passive: false });
|
|
6028
6240
|
|
|
@@ -6360,14 +6572,18 @@ export class WebPlayer extends BasePlayer {
|
|
|
6360
6572
|
case 'ArrowLeft':
|
|
6361
6573
|
e.preventDefault();
|
|
6362
6574
|
e.stopImmediatePropagation(); // Prevent duplicate handler triggers
|
|
6363
|
-
this.
|
|
6364
|
-
|
|
6575
|
+
if (this.video && !isNaN(this.video.duration)) {
|
|
6576
|
+
this.seek(Math.max(0, this.video.currentTime - 10));
|
|
6577
|
+
shortcutText = '-10s';
|
|
6578
|
+
}
|
|
6365
6579
|
break;
|
|
6366
6580
|
case 'ArrowRight':
|
|
6367
6581
|
e.preventDefault();
|
|
6368
6582
|
e.stopImmediatePropagation(); // Prevent duplicate handler triggers
|
|
6369
|
-
|
|
6370
|
-
|
|
6583
|
+
if (this.video && !isNaN(this.video.duration)) {
|
|
6584
|
+
this.seek(Math.min(this.video.duration, this.video.currentTime + 10));
|
|
6585
|
+
shortcutText = '+10s';
|
|
6586
|
+
}
|
|
6371
6587
|
break;
|
|
6372
6588
|
case 'ArrowUp':
|
|
6373
6589
|
e.preventDefault();
|
|
@@ -6432,8 +6648,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
6432
6648
|
case '8':
|
|
6433
6649
|
case '9':
|
|
6434
6650
|
e.preventDefault();
|
|
6435
|
-
|
|
6436
|
-
if (this.video) {
|
|
6651
|
+
// Only jump to position if video is loaded and duration is valid
|
|
6652
|
+
if (this.video && !isNaN(this.video.duration) && this.video.duration > 0) {
|
|
6653
|
+
const percent = parseInt(e.key) * 10;
|
|
6437
6654
|
this.video.currentTime = (this.video.duration * percent) / 100;
|
|
6438
6655
|
shortcutText = `${percent}%`;
|
|
6439
6656
|
}
|
|
@@ -6759,7 +6976,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6759
6976
|
// Use deferred pause to avoid race conditions
|
|
6760
6977
|
this.requestPause();
|
|
6761
6978
|
if (fromSeek || ((this.video.currentTime || 0) > lim)) {
|
|
6762
|
-
this.
|
|
6979
|
+
this.safeSetCurrentTime(lim - 0.1);
|
|
6763
6980
|
}
|
|
6764
6981
|
} catch (_) {}
|
|
6765
6982
|
}
|
|
@@ -6947,16 +7164,29 @@ export class WebPlayer extends BasePlayer {
|
|
|
6947
7164
|
}
|
|
6948
7165
|
}
|
|
6949
7166
|
|
|
6950
|
-
private
|
|
6951
|
-
const progressBar = document.
|
|
7167
|
+
private seekToPosition(e: MouseEvent): void {
|
|
7168
|
+
const progressBar = document.querySelector('.uvf-progress-bar') as HTMLElement;
|
|
6952
7169
|
const progressFilled = document.getElementById('uvf-progress-filled') as HTMLElement;
|
|
6953
7170
|
const progressHandle = document.getElementById('uvf-progress-handle') as HTMLElement;
|
|
6954
7171
|
if (!progressBar || !this.video) return;
|
|
6955
7172
|
|
|
7173
|
+
const duration = this.video.duration;
|
|
7174
|
+
// Validate duration before calculating seek time
|
|
7175
|
+
if (!isFinite(duration) || isNaN(duration) || duration <= 0) {
|
|
7176
|
+
this.debugWarn('Invalid video duration, cannot seek via progress bar');
|
|
7177
|
+
return;
|
|
7178
|
+
}
|
|
7179
|
+
|
|
6956
7180
|
const rect = progressBar.getBoundingClientRect();
|
|
6957
7181
|
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
|
|
6958
7182
|
const percent = (x / rect.width) * 100;
|
|
6959
|
-
const time = (percent / 100) *
|
|
7183
|
+
const time = (percent / 100) * duration;
|
|
7184
|
+
|
|
7185
|
+
// Validate calculated time
|
|
7186
|
+
if (!isFinite(time) || isNaN(time)) {
|
|
7187
|
+
this.debugWarn('Calculated seek time is invalid:', time);
|
|
7188
|
+
return;
|
|
7189
|
+
}
|
|
6960
7190
|
|
|
6961
7191
|
// Update UI immediately for responsive feedback
|
|
6962
7192
|
if (progressFilled) {
|
|
@@ -7195,15 +7425,24 @@ export class WebPlayer extends BasePlayer {
|
|
|
7195
7425
|
const wrapperWidth = wrapperRect.width;
|
|
7196
7426
|
const isLeftSide = tapPosition < wrapperWidth / 2;
|
|
7197
7427
|
|
|
7428
|
+
const currentTime = this.video.currentTime;
|
|
7429
|
+
const duration = this.video.duration;
|
|
7430
|
+
|
|
7431
|
+
// Validate current time and duration before calculating new time
|
|
7432
|
+
if (!isFinite(currentTime) || isNaN(currentTime) || !isFinite(duration) || isNaN(duration)) {
|
|
7433
|
+
this.debugWarn('Invalid video time values, skipping double-tap action');
|
|
7434
|
+
return;
|
|
7435
|
+
}
|
|
7436
|
+
|
|
7198
7437
|
if (isLeftSide) {
|
|
7199
7438
|
// Skip backward
|
|
7200
|
-
const newTime = Math.max(0,
|
|
7439
|
+
const newTime = Math.max(0, currentTime - SKIP_SECONDS);
|
|
7201
7440
|
this.seek(newTime);
|
|
7202
7441
|
this.showShortcutIndicator(`-${SKIP_SECONDS}s`);
|
|
7203
7442
|
this.debugLog('Double tap left - skip backward');
|
|
7204
7443
|
} else {
|
|
7205
7444
|
// Skip forward
|
|
7206
|
-
const newTime = Math.min(
|
|
7445
|
+
const newTime = Math.min(duration, currentTime + SKIP_SECONDS);
|
|
7207
7446
|
this.seek(newTime);
|
|
7208
7447
|
this.showShortcutIndicator(`+${SKIP_SECONDS}s`);
|
|
7209
7448
|
this.debugLog('Double tap right - skip forward');
|
|
@@ -7269,8 +7508,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
7269
7508
|
|
|
7270
7509
|
this.fastBackwardInterval = setInterval(() => {
|
|
7271
7510
|
if (this.video) {
|
|
7272
|
-
const
|
|
7273
|
-
|
|
7511
|
+
const currentTime = this.video.currentTime;
|
|
7512
|
+
if (isFinite(currentTime) && !isNaN(currentTime)) {
|
|
7513
|
+
const newTime = Math.max(0, currentTime - 0.1); // Go back 0.1s every frame
|
|
7514
|
+
this.safeSetCurrentTime(newTime);
|
|
7515
|
+
}
|
|
7274
7516
|
}
|
|
7275
7517
|
}, 50); // Update every 50ms for smooth backward motion
|
|
7276
7518
|
}
|
|
@@ -7787,8 +8029,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
7787
8029
|
}
|
|
7788
8030
|
|
|
7789
8031
|
const chapter = this.coreChapterManager.seekToChapter(chapterId);
|
|
7790
|
-
if (chapter) {
|
|
7791
|
-
this.
|
|
8032
|
+
if (chapter && isFinite(chapter.startTime) && !isNaN(chapter.startTime)) {
|
|
8033
|
+
this.safeSetCurrentTime(chapter.startTime);
|
|
7792
8034
|
this.debugLog('Seeked to chapter:', chapter.title);
|
|
7793
8035
|
}
|
|
7794
8036
|
}
|
|
@@ -8517,18 +8759,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
8517
8759
|
});
|
|
8518
8760
|
}
|
|
8519
8761
|
|
|
8520
|
-
//
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
}, 300);
|
|
8525
|
-
} else {
|
|
8526
|
-
// Desktop: just refresh the menu to update current values
|
|
8527
|
-
setTimeout(() => {
|
|
8528
|
-
this.generateAccordionMenu();
|
|
8529
|
-
this.setupSettingsEventListeners();
|
|
8530
|
-
}, 100);
|
|
8531
|
-
}
|
|
8762
|
+
// Close settings menu after selection on all devices
|
|
8763
|
+
setTimeout(() => {
|
|
8764
|
+
this.hideSettingsMenu();
|
|
8765
|
+
}, 200);
|
|
8532
8766
|
}
|
|
8533
8767
|
|
|
8534
8768
|
/**
|
|
@@ -9165,12 +9399,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
9165
9399
|
|
|
9166
9400
|
// Additional check: ensure video is paused if not authenticated
|
|
9167
9401
|
if (this.video && !this.video.paused && !this.paymentSuccessful) {
|
|
9168
|
-
this.debugWarn('Unauthorized
|
|
9402
|
+
this.debugWarn('Unauthorized playback detected, pausing video');
|
|
9169
9403
|
try {
|
|
9170
9404
|
this.video.pause();
|
|
9171
9405
|
const freeDuration = Number(this.config.freeDuration || 0);
|
|
9172
|
-
if (freeDuration > 0) {
|
|
9173
|
-
this.
|
|
9406
|
+
if (freeDuration > 0 && isFinite(freeDuration)) {
|
|
9407
|
+
this.safeSetCurrentTime(freeDuration - 1);
|
|
9174
9408
|
}
|
|
9175
9409
|
} catch (_) {}
|
|
9176
9410
|
}
|
|
@@ -9186,7 +9420,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
9186
9420
|
// Disable video completely
|
|
9187
9421
|
if (this.video) {
|
|
9188
9422
|
this.video.pause();
|
|
9189
|
-
this.
|
|
9423
|
+
this.safeSetCurrentTime(0);
|
|
9190
9424
|
this.video.src = ''; // Clear video source
|
|
9191
9425
|
this.video.style.display = 'none';
|
|
9192
9426
|
}
|