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.video.currentTime = Math.max(0, freeDuration - 1);
1390
+ this.safeSetCurrentTime(freeDuration - 1);
1343
1391
  return;
1344
1392
  }
1345
1393
  }
1346
1394
 
1347
- const d = this.video.duration;
1348
- if (typeof d === 'number' && isFinite(d) && d > 0) {
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: rgba(0, 0, 0, 0.6);
3795
- border: 1px solid rgba(255, 255, 255, 0.2);
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(8px);
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
- background: rgba(255, 255, 255, 0.15);
3809
- border-color: rgba(255, 255, 255, 0.4);
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
- transition: all 0.2s ease;
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
- transform: scale(1.1);
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', () => this.seek(this.video!.currentTime - 10));
5920
- skipForwardBtn?.addEventListener('click', () => this.seek(this.video!.currentTime + 10));
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.handleProgressChange(e as MouseEvent);
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.handleProgressChange(e as MouseEvent);
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
- this.handleProgressChange(touch);
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.handleProgressChange(e);
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
- this.handleProgressChange(touch);
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.seek(Math.max(0, this.video!.currentTime - 10));
6364
- shortcutText = '-10s';
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
- this.seek(Math.min(this.video!.duration, this.video!.currentTime + 10));
6370
- shortcutText = '+10s';
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
- const percent = parseInt(e.key) * 10;
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.video.currentTime = Math.max(0, lim - 0.1);
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 handleProgressChange(e: MouseEvent | Touch): void {
6951
- const progressBar = document.getElementById('uvf-progress-bar');
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) * this.video.duration;
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, this.video.currentTime - SKIP_SECONDS);
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(this.video.duration, this.video.currentTime + SKIP_SECONDS);
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 newTime = Math.max(0, this.video.currentTime - 0.1); // Go back 0.1s every frame
7273
- this.video.currentTime = newTime;
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.video.currentTime = chapter.startTime;
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
- // Auto-close settings menu on mobile after a short delay
8521
- if (this.isMobileDevice()) {
8522
- setTimeout(() => {
8523
- this.hideSettingsMenu();
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 playbook detected, pausing video');
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.video.currentTime = Math.max(0, freeDuration - 1);
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.video.currentTime = 0;
9423
+ this.safeSetCurrentTime(0);
9190
9424
  this.video.src = ''; // Clear video source
9191
9425
  this.video.style.display = 'none';
9192
9426
  }