unified-video-framework 1.4.212 → 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 {
|
|
@@ -4376,6 +4420,83 @@ export class WebPlayer extends BasePlayer {
|
|
|
4376
4420
|
100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
|
|
4377
4421
|
}
|
|
4378
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
|
+
|
|
4379
4500
|
/* Hide top bar when no cursor */
|
|
4380
4501
|
.uvf-player-wrapper.no-cursor .uvf-top-bar {
|
|
4381
4502
|
opacity: 0 !important;
|
|
@@ -5990,9 +6111,17 @@ export class WebPlayer extends BasePlayer {
|
|
|
5990
6111
|
}
|
|
5991
6112
|
});
|
|
5992
6113
|
|
|
5993
|
-
// Skip buttons
|
|
5994
|
-
skipBackBtn?.addEventListener('click', () =>
|
|
5995
|
-
|
|
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
|
+
});
|
|
5996
6125
|
|
|
5997
6126
|
// Volume control
|
|
5998
6127
|
volumeBtn?.addEventListener('click', (e) => {
|
|
@@ -6045,13 +6174,13 @@ export class WebPlayer extends BasePlayer {
|
|
|
6045
6174
|
|
|
6046
6175
|
// Progress bar interactions
|
|
6047
6176
|
progressBar?.addEventListener('click', (e) => {
|
|
6048
|
-
this.
|
|
6177
|
+
this.seekToPosition(e as MouseEvent);
|
|
6049
6178
|
});
|
|
6050
6179
|
|
|
6051
6180
|
progressBar?.addEventListener('mousedown', (e) => {
|
|
6052
6181
|
this.isDragging = true;
|
|
6053
6182
|
this.showTimeTooltip = true;
|
|
6054
|
-
this.
|
|
6183
|
+
this.seekToPosition(e as MouseEvent);
|
|
6055
6184
|
this.updateTimeTooltip(e as MouseEvent);
|
|
6056
6185
|
});
|
|
6057
6186
|
|
|
@@ -6078,7 +6207,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
6078
6207
|
e.preventDefault(); // Prevent scrolling
|
|
6079
6208
|
this.isDragging = true;
|
|
6080
6209
|
const touch = e.touches[0];
|
|
6081
|
-
|
|
6210
|
+
const mouseEvent = new MouseEvent('mousedown', {
|
|
6211
|
+
clientX: touch.clientX,
|
|
6212
|
+
clientY: touch.clientY
|
|
6213
|
+
});
|
|
6214
|
+
this.seekToPosition(mouseEvent);
|
|
6082
6215
|
}, { passive: false });
|
|
6083
6216
|
|
|
6084
6217
|
// Global mouse and touch events for enhanced dragging
|
|
@@ -6087,7 +6220,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6087
6220
|
this.handleVolumeChange(e);
|
|
6088
6221
|
}
|
|
6089
6222
|
if (this.isDragging && progressBar) {
|
|
6090
|
-
this.
|
|
6223
|
+
this.seekToPosition(e);
|
|
6091
6224
|
// Update tooltip position during dragging
|
|
6092
6225
|
this.updateTimeTooltip(e);
|
|
6093
6226
|
}
|
|
@@ -6097,7 +6230,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
6097
6230
|
if (this.isDragging && progressBar) {
|
|
6098
6231
|
e.preventDefault(); // Prevent scrolling
|
|
6099
6232
|
const touch = e.touches[0];
|
|
6100
|
-
|
|
6233
|
+
const mouseEvent = new MouseEvent('mousemove', {
|
|
6234
|
+
clientX: touch.clientX,
|
|
6235
|
+
clientY: touch.clientY
|
|
6236
|
+
});
|
|
6237
|
+
this.seekToPosition(mouseEvent);
|
|
6101
6238
|
}
|
|
6102
6239
|
}, { passive: false });
|
|
6103
6240
|
|
|
@@ -6435,14 +6572,18 @@ export class WebPlayer extends BasePlayer {
|
|
|
6435
6572
|
case 'ArrowLeft':
|
|
6436
6573
|
e.preventDefault();
|
|
6437
6574
|
e.stopImmediatePropagation(); // Prevent duplicate handler triggers
|
|
6438
|
-
this.
|
|
6439
|
-
|
|
6575
|
+
if (this.video && !isNaN(this.video.duration)) {
|
|
6576
|
+
this.seek(Math.max(0, this.video.currentTime - 10));
|
|
6577
|
+
shortcutText = '-10s';
|
|
6578
|
+
}
|
|
6440
6579
|
break;
|
|
6441
6580
|
case 'ArrowRight':
|
|
6442
6581
|
e.preventDefault();
|
|
6443
6582
|
e.stopImmediatePropagation(); // Prevent duplicate handler triggers
|
|
6444
|
-
|
|
6445
|
-
|
|
6583
|
+
if (this.video && !isNaN(this.video.duration)) {
|
|
6584
|
+
this.seek(Math.min(this.video.duration, this.video.currentTime + 10));
|
|
6585
|
+
shortcutText = '+10s';
|
|
6586
|
+
}
|
|
6446
6587
|
break;
|
|
6447
6588
|
case 'ArrowUp':
|
|
6448
6589
|
e.preventDefault();
|
|
@@ -6507,8 +6648,9 @@ export class WebPlayer extends BasePlayer {
|
|
|
6507
6648
|
case '8':
|
|
6508
6649
|
case '9':
|
|
6509
6650
|
e.preventDefault();
|
|
6510
|
-
|
|
6511
|
-
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;
|
|
6512
6654
|
this.video.currentTime = (this.video.duration * percent) / 100;
|
|
6513
6655
|
shortcutText = `${percent}%`;
|
|
6514
6656
|
}
|
|
@@ -6834,7 +6976,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
6834
6976
|
// Use deferred pause to avoid race conditions
|
|
6835
6977
|
this.requestPause();
|
|
6836
6978
|
if (fromSeek || ((this.video.currentTime || 0) > lim)) {
|
|
6837
|
-
this.
|
|
6979
|
+
this.safeSetCurrentTime(lim - 0.1);
|
|
6838
6980
|
}
|
|
6839
6981
|
} catch (_) {}
|
|
6840
6982
|
}
|
|
@@ -7022,16 +7164,29 @@ export class WebPlayer extends BasePlayer {
|
|
|
7022
7164
|
}
|
|
7023
7165
|
}
|
|
7024
7166
|
|
|
7025
|
-
private
|
|
7026
|
-
const progressBar = document.
|
|
7167
|
+
private seekToPosition(e: MouseEvent): void {
|
|
7168
|
+
const progressBar = document.querySelector('.uvf-progress-bar') as HTMLElement;
|
|
7027
7169
|
const progressFilled = document.getElementById('uvf-progress-filled') as HTMLElement;
|
|
7028
7170
|
const progressHandle = document.getElementById('uvf-progress-handle') as HTMLElement;
|
|
7029
7171
|
if (!progressBar || !this.video) return;
|
|
7030
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
|
+
|
|
7031
7180
|
const rect = progressBar.getBoundingClientRect();
|
|
7032
7181
|
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
|
|
7033
7182
|
const percent = (x / rect.width) * 100;
|
|
7034
|
-
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
|
+
}
|
|
7035
7190
|
|
|
7036
7191
|
// Update UI immediately for responsive feedback
|
|
7037
7192
|
if (progressFilled) {
|
|
@@ -7270,15 +7425,24 @@ export class WebPlayer extends BasePlayer {
|
|
|
7270
7425
|
const wrapperWidth = wrapperRect.width;
|
|
7271
7426
|
const isLeftSide = tapPosition < wrapperWidth / 2;
|
|
7272
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
|
+
|
|
7273
7437
|
if (isLeftSide) {
|
|
7274
7438
|
// Skip backward
|
|
7275
|
-
const newTime = Math.max(0,
|
|
7439
|
+
const newTime = Math.max(0, currentTime - SKIP_SECONDS);
|
|
7276
7440
|
this.seek(newTime);
|
|
7277
7441
|
this.showShortcutIndicator(`-${SKIP_SECONDS}s`);
|
|
7278
7442
|
this.debugLog('Double tap left - skip backward');
|
|
7279
7443
|
} else {
|
|
7280
7444
|
// Skip forward
|
|
7281
|
-
const newTime = Math.min(
|
|
7445
|
+
const newTime = Math.min(duration, currentTime + SKIP_SECONDS);
|
|
7282
7446
|
this.seek(newTime);
|
|
7283
7447
|
this.showShortcutIndicator(`+${SKIP_SECONDS}s`);
|
|
7284
7448
|
this.debugLog('Double tap right - skip forward');
|
|
@@ -7344,8 +7508,11 @@ export class WebPlayer extends BasePlayer {
|
|
|
7344
7508
|
|
|
7345
7509
|
this.fastBackwardInterval = setInterval(() => {
|
|
7346
7510
|
if (this.video) {
|
|
7347
|
-
const
|
|
7348
|
-
|
|
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
|
+
}
|
|
7349
7516
|
}
|
|
7350
7517
|
}, 50); // Update every 50ms for smooth backward motion
|
|
7351
7518
|
}
|
|
@@ -7862,8 +8029,8 @@ export class WebPlayer extends BasePlayer {
|
|
|
7862
8029
|
}
|
|
7863
8030
|
|
|
7864
8031
|
const chapter = this.coreChapterManager.seekToChapter(chapterId);
|
|
7865
|
-
if (chapter) {
|
|
7866
|
-
this.
|
|
8032
|
+
if (chapter && isFinite(chapter.startTime) && !isNaN(chapter.startTime)) {
|
|
8033
|
+
this.safeSetCurrentTime(chapter.startTime);
|
|
7867
8034
|
this.debugLog('Seeked to chapter:', chapter.title);
|
|
7868
8035
|
}
|
|
7869
8036
|
}
|
|
@@ -8592,18 +8759,10 @@ export class WebPlayer extends BasePlayer {
|
|
|
8592
8759
|
});
|
|
8593
8760
|
}
|
|
8594
8761
|
|
|
8595
|
-
//
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
}, 300);
|
|
8600
|
-
} else {
|
|
8601
|
-
// Desktop: just refresh the menu to update current values
|
|
8602
|
-
setTimeout(() => {
|
|
8603
|
-
this.generateAccordionMenu();
|
|
8604
|
-
this.setupSettingsEventListeners();
|
|
8605
|
-
}, 100);
|
|
8606
|
-
}
|
|
8762
|
+
// Close settings menu after selection on all devices
|
|
8763
|
+
setTimeout(() => {
|
|
8764
|
+
this.hideSettingsMenu();
|
|
8765
|
+
}, 200);
|
|
8607
8766
|
}
|
|
8608
8767
|
|
|
8609
8768
|
/**
|
|
@@ -9240,12 +9399,12 @@ export class WebPlayer extends BasePlayer {
|
|
|
9240
9399
|
|
|
9241
9400
|
// Additional check: ensure video is paused if not authenticated
|
|
9242
9401
|
if (this.video && !this.video.paused && !this.paymentSuccessful) {
|
|
9243
|
-
this.debugWarn('Unauthorized
|
|
9402
|
+
this.debugWarn('Unauthorized playback detected, pausing video');
|
|
9244
9403
|
try {
|
|
9245
9404
|
this.video.pause();
|
|
9246
9405
|
const freeDuration = Number(this.config.freeDuration || 0);
|
|
9247
|
-
if (freeDuration > 0) {
|
|
9248
|
-
this.
|
|
9406
|
+
if (freeDuration > 0 && isFinite(freeDuration)) {
|
|
9407
|
+
this.safeSetCurrentTime(freeDuration - 1);
|
|
9249
9408
|
}
|
|
9250
9409
|
} catch (_) {}
|
|
9251
9410
|
}
|
|
@@ -9261,7 +9420,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
9261
9420
|
// Disable video completely
|
|
9262
9421
|
if (this.video) {
|
|
9263
9422
|
this.video.pause();
|
|
9264
|
-
this.
|
|
9423
|
+
this.safeSetCurrentTime(0);
|
|
9265
9424
|
this.video.src = ''; // Clear video source
|
|
9266
9425
|
this.video.style.display = 'none';
|
|
9267
9426
|
}
|