unified-video-framework 1.4.162 → 1.4.164

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.
@@ -4329,217 +4329,6 @@ export class WebPlayer extends BasePlayer {
4329
4329
  @media screen and (max-width: 767px) {
4330
4330
  html, body {
4331
4331
  overflow-x: hidden;
4332
- background: #000;
4333
- }
4334
-
4335
- /* Mobile Portrait Layout - Centered Player with Top/Bottom Black Areas */
4336
- @media screen and (orientation: portrait) {
4337
- .uvf-responsive-container {
4338
- display: flex;
4339
- flex-direction: column;
4340
- height: 100vh;
4341
- height: 100dvh;
4342
- background: #000;
4343
- overflow: hidden;
4344
- position: fixed;
4345
- top: 0;
4346
- left: 0;
4347
- width: 100vw;
4348
- }
4349
-
4350
- /* Top black area - 25% of viewport */
4351
- .uvf-responsive-container::before {
4352
- content: '';
4353
- flex: 0 0 25vh;
4354
- background: #000;
4355
- pointer-events: none;
4356
- }
4357
-
4358
- /* Bottom black area - 25% of viewport */
4359
- .uvf-responsive-container::after {
4360
- content: '';
4361
- flex: 0 0 25vh;
4362
- background: #000;
4363
- pointer-events: none;
4364
- }
4365
-
4366
- /* Centered video player wrapper - 50% of viewport */
4367
- .uvf-responsive-container .uvf-player-wrapper {
4368
- flex: 1;
4369
- width: 100vw;
4370
- display: flex;
4371
- align-items: center;
4372
- justify-content: center;
4373
- background: #000;
4374
- position: relative;
4375
- }
4376
-
4377
- /* Video container fills player wrapper */
4378
- .uvf-responsive-container .uvf-video-container {
4379
- width: 100%;
4380
- height: 100%;
4381
- position: relative;
4382
- background: #000;
4383
- border-radius: 0;
4384
- overflow: hidden;
4385
- }
4386
-
4387
- /* Video element fills container */
4388
- .uvf-responsive-container .uvf-video {
4389
- width: 100%;
4390
- height: 100%;
4391
- object-fit: contain;
4392
- background: #000;
4393
- }
4394
-
4395
- /* All controls positioned inside video container */
4396
- .uvf-controls-bar {
4397
- position: absolute !important;
4398
- bottom: 0 !important;
4399
- left: 0 !important;
4400
- right: 0 !important;
4401
- background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.6) 60%, transparent 100%);
4402
- padding: 12px 16px;
4403
- padding-bottom: calc(12px + var(--uvf-safe-area-bottom));
4404
- z-index: 1000;
4405
- /* Ensure controls are always visible */
4406
- opacity: 1 !important;
4407
- visibility: visible !important;
4408
- display: flex !important;
4409
- flex-direction: column !important;
4410
- /* Ensure hardware acceleration */
4411
- -webkit-transform: translate3d(0,0,0);
4412
- transform: translate3d(0,0,0);
4413
- }
4414
-
4415
- /* Force controls visibility on mobile portrait */
4416
- .uvf-responsive-container .uvf-controls-bar {
4417
- opacity: 1 !important;
4418
- visibility: visible !important;
4419
- transform: translateY(0) !important;
4420
- pointer-events: auto !important;
4421
- }
4422
-
4423
- /* Progress section - ensure visibility */
4424
- .uvf-progress-section {
4425
- width: 100%;
4426
- margin-bottom: 8px;
4427
- opacity: 1 !important;
4428
- visibility: visible !important;
4429
- display: block !important;
4430
- }
4431
-
4432
- /* Progress bar styling */
4433
- .uvf-progress-bar-wrapper {
4434
- opacity: 1 !important;
4435
- visibility: visible !important;
4436
- }
4437
-
4438
- .uvf-progress-bar {
4439
- height: 4px;
4440
- background: rgba(255, 255, 255, 0.3);
4441
- border-radius: 2px;
4442
- }
4443
-
4444
- .uvf-progress-filled {
4445
- background: var(--uvf-accent-1, #8B5CF6);
4446
- height: 100%;
4447
- border-radius: 2px;
4448
- }
4449
-
4450
- /* Controls row alignment - ensure visibility */
4451
- .uvf-controls-row {
4452
- width: 100%;
4453
- display: flex !important;
4454
- align-items: center;
4455
- justify-content: flex-start;
4456
- gap: 12px;
4457
- opacity: 1 !important;
4458
- visibility: visible !important;
4459
- margin-top: 8px;
4460
- }
4461
-
4462
- /* Time display visibility */
4463
- .uvf-time-display {
4464
- color: #fff;
4465
- font-size: 12px;
4466
- font-weight: 500;
4467
- opacity: 1 !important;
4468
- visibility: visible !important;
4469
- display: block !important;
4470
- }
4471
-
4472
- /* Control buttons visibility */
4473
- .uvf-control-btn {
4474
- opacity: 1 !important;
4475
- visibility: visible !important;
4476
- display: flex !important;
4477
- color: #fff;
4478
- }
4479
-
4480
- .uvf-control-btn svg {
4481
- fill: #fff;
4482
- opacity: 1;
4483
- }
4484
-
4485
- /* Right controls - ensure visibility */
4486
- .uvf-right-controls {
4487
- margin-left: auto;
4488
- display: flex !important;
4489
- align-items: center;
4490
- gap: 8px;
4491
- opacity: 1 !important;
4492
- visibility: visible !important;
4493
- }
4494
-
4495
- /* Force controls to be always visible in portrait mode */
4496
- .uvf-responsive-container .uvf-player-wrapper.uvf-controls-visible .uvf-controls-bar,
4497
- .uvf-responsive-container .uvf-player-wrapper:hover .uvf-controls-bar,
4498
- .uvf-responsive-container .uvf-controls-bar {
4499
- opacity: 1 !important;
4500
- transform: translateY(0) !important;
4501
- visibility: visible !important;
4502
- display: flex !important;
4503
- pointer-events: auto !important;
4504
- }
4505
-
4506
- /* Ensure all child elements are visible */
4507
- .uvf-responsive-container .uvf-controls-bar * {
4508
- opacity: 1 !important;
4509
- visibility: visible !important;
4510
- }
4511
-
4512
- /* Center play button positioned within video */
4513
- .uvf-center-play-container {
4514
- position: absolute;
4515
- top: 50%;
4516
- left: 50%;
4517
- transform: translate(-50%, -50%);
4518
- z-index: 8;
4519
- pointer-events: none;
4520
- }
4521
-
4522
- .uvf-center-play-btn {
4523
- pointer-events: auto;
4524
- }
4525
-
4526
- /* Top controls within video */
4527
- .uvf-top-controls {
4528
- position: absolute;
4529
- top: calc(12px + var(--uvf-safe-area-top));
4530
- right: calc(16px + var(--uvf-safe-area-right));
4531
- z-index: 9;
4532
- }
4533
-
4534
- /* Title bar within video */
4535
- .uvf-title-bar {
4536
- position: absolute;
4537
- top: calc(12px + var(--uvf-safe-area-top));
4538
- left: calc(16px + var(--uvf-safe-area-left));
4539
- right: calc(80px + var(--uvf-safe-area-right));
4540
- z-index: 9;
4541
- padding: 8px 0;
4542
- }
4543
4332
  }
4544
4333
 
4545
4334
  .uvf-player-wrapper {
@@ -4563,37 +4352,6 @@ export class WebPlayer extends BasePlayer {
4563
4352
  transform: translateZ(0);
4564
4353
  }
4565
4354
 
4566
- /* Mobile controls optimization for touch */
4567
- .uvf-control-btn {
4568
- min-width: 44px;
4569
- min-height: 44px;
4570
- display: flex;
4571
- align-items: center;
4572
- justify-content: center;
4573
- border-radius: 50%;
4574
- transition: all 0.2s ease;
4575
- background: rgba(255, 255, 255, 0.1);
4576
- backdrop-filter: blur(8px);
4577
- }
4578
-
4579
- .uvf-control-btn:active {
4580
- transform: scale(0.95);
4581
- background: rgba(255, 255, 255, 0.2);
4582
- }
4583
-
4584
- /* Play/pause button prominence */
4585
- .uvf-control-btn.play-pause {
4586
- background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
4587
- min-width: 52px;
4588
- min-height: 52px;
4589
- box-shadow: 0 2px 8px rgba(0,0,0,0.3);
4590
- }
4591
-
4592
- .uvf-control-btn.play-pause:active {
4593
- transform: scale(0.92);
4594
- box-shadow: 0 1px 4px rgba(0,0,0,0.4);
4595
- }
4596
-
4597
4355
  /* Fix for controls being cut off by virtual keyboard */
4598
4356
  .uvf-controls-bar {
4599
4357
  position: absolute !important;
@@ -4640,61 +4398,278 @@ export class WebPlayer extends BasePlayer {
4640
4398
  }
4641
4399
 
4642
4400
  /* Enhanced Responsive Media Queries with UX Best Practices */
4643
- /* Mobile devices (portrait) - Enhanced UX with Safe Areas */
4401
+ /* Mobile Portrait Layout - CENTERED PLAYER with TOP/BOTTOM BLACK AREAS */
4644
4402
  @media screen and (max-width: 767px) and (orientation: portrait) {
4403
+ /* CENTERED LAYOUT: 25% top black + 50% player + 25% bottom black */
4645
4404
  .uvf-responsive-container {
4405
+ display: flex;
4406
+ flex-direction: column;
4407
+ height: 100vh;
4408
+ height: 100dvh;
4409
+ background: #000;
4410
+ overflow: hidden;
4411
+ position: fixed;
4412
+ top: 0;
4413
+ left: 0;
4414
+ width: 100vw;
4646
4415
  padding: 0;
4647
- width: 100vw !important;
4648
- height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4649
4416
  margin: 0;
4650
- position: relative;
4651
- overflow: hidden;
4652
4417
  }
4653
4418
 
4654
- @supports (height: 100dvh) {
4655
- .uvf-responsive-container {
4656
- height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4657
- }
4419
+ /* TOP BLACK AREA - 25% of viewport - COMPLETELY EMPTY */
4420
+ .uvf-responsive-container::before {
4421
+ content: '';
4422
+ flex: 0 0 25vh;
4423
+ background: #000;
4424
+ pointer-events: none;
4658
4425
  }
4659
4426
 
4660
- .uvf-responsive-container .uvf-player-wrapper {
4661
- width: 100vw !important;
4662
- height: 100% !important;
4663
- min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4427
+ /* BOTTOM BLACK AREA - 25% of viewport - COMPLETELY EMPTY */
4428
+ .uvf-responsive-container::after {
4429
+ content: '';
4430
+ flex: 0 0 25vh;
4431
+ background: #000;
4432
+ pointer-events: none;
4664
4433
  }
4665
4434
 
4666
- @supports (height: 100dvh) {
4667
- .uvf-responsive-container .uvf-player-wrapper {
4668
- min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
4669
- }
4435
+ /* CENTERED VIDEO PLAYER WRAPPER - 50% of viewport */
4436
+ .uvf-responsive-container .uvf-player-wrapper {
4437
+ flex: 1; /* Takes remaining 50% */
4438
+ width: 100vw;
4439
+ display: flex;
4440
+ align-items: center;
4441
+ justify-content: center;
4442
+ background: #000;
4443
+ position: relative;
4444
+ overflow: hidden !important; /* CRITICAL: Ensure nothing extends beyond this area */
4445
+ /* STRICT CONTAINMENT - Force all child elements within bounds */
4446
+ contain: layout style paint size !important;
4447
+ isolation: isolate !important;
4448
+ /* Create strict clipping boundary */
4449
+ clip: rect(0, 100vw, 50vh, 0) !important;
4670
4450
  }
4671
4451
 
4452
+ /* Video container fills player wrapper */
4672
4453
  .uvf-responsive-container .uvf-video-container {
4673
- width: 100vw !important;
4674
- height: 100% !important;
4675
- aspect-ratio: unset !important;
4676
- min-height: inherit;
4454
+ width: 100%;
4455
+ height: 100%;
4456
+ position: relative;
4457
+ background: #000;
4458
+ border-radius: 0;
4459
+ overflow: hidden;
4460
+ }
4461
+
4462
+ /* Video element fills container */
4463
+ .uvf-responsive-container .uvf-video {
4464
+ width: 100%;
4465
+ height: 100%;
4466
+ object-fit: contain;
4467
+ background: #000;
4677
4468
  }
4678
4469
 
4679
- /* Enhanced mobile controls bar with safe area padding - iOS Safari specific fixes */
4470
+ /* CONTROLS STRICTLY CONTAINED WITHIN VIDEO AREA - NEVER EXTEND TO BLACK AREAS */
4680
4471
  .uvf-controls-bar {
4681
4472
  position: absolute !important;
4682
- bottom: 0 !important;
4683
- left: 0 !important;
4473
+ /* Keep controls INSIDE video container with margins from all edges */
4474
+ bottom: 20px !important; /* 20px margin from video bottom */
4475
+ left: 16px !important; /* 16px margin from video left */
4476
+ right: 16px !important; /* 16px margin from video right */
4477
+ top: auto !important;
4478
+ background: linear-gradient(to top, rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.7) 60%, transparent 100%);
4479
+ padding: 12px 16px;
4480
+ border-radius: 12px;
4481
+ z-index: 1000;
4482
+ /* Ensure controls are visible and contained */
4483
+ opacity: 1 !important;
4484
+ visibility: visible !important;
4485
+ display: flex !important;
4486
+ flex-direction: column !important;
4487
+ /* Hardware acceleration */
4488
+ -webkit-transform: translate3d(0,0,0);
4489
+ transform: translate3d(0,0,0);
4490
+ /* CRITICAL: Prevent any overflow into black areas */
4491
+ max-height: calc(100% - 40px) !important; /* Leave 20px margin from top and bottom */
4492
+ max-width: calc(100% - 32px) !important; /* Leave 16px margin from left and right */
4493
+ box-sizing: border-box !important;
4494
+ /* Visual containment indicators */
4495
+ border: 1px solid rgba(255, 255, 255, 0.1);
4496
+ backdrop-filter: blur(10px);
4497
+ /* STRICT CSS containment with size to prevent overflow */
4498
+ contain: layout style paint size !important;
4499
+ /* Absolute overflow prevention */
4500
+ overflow: hidden !important;
4501
+ /* Force clip to container bounds */
4502
+ clip-path: inset(0) !important;
4503
+ }
4504
+
4505
+ /* Force controls and all child elements to stay within video container */
4506
+ .uvf-responsive-container .uvf-controls-bar {
4507
+ opacity: 1 !important;
4508
+ visibility: visible !important;
4509
+ transform: translateY(0) !important;
4510
+ pointer-events: auto !important;
4511
+ }
4512
+
4513
+ /* CRITICAL: Prevent ALL child elements from extending beyond controls container */
4514
+ .uvf-responsive-container .uvf-controls-bar *,
4515
+ .uvf-responsive-container .uvf-controls-bar *::before,
4516
+ .uvf-responsive-container .uvf-controls-bar *::after {
4517
+ max-width: 100% !important;
4518
+ max-height: 100% !important;
4519
+ overflow: hidden !important;
4520
+ box-sizing: border-box !important;
4521
+ position: relative !important;
4522
+ }
4523
+
4524
+ /* Ensure settings menu stays within video container bounds */
4525
+ .uvf-responsive-container .uvf-settings-menu {
4526
+ max-height: calc(50vh - 80px) !important; /* Video height minus controls margin */
4527
+ max-width: calc(100vw - 64px) !important; /* Video width minus side margins */
4528
+ overflow-y: auto !important;
4529
+ position: absolute !important;
4530
+ /* Keep menu within video boundaries */
4531
+ bottom: 60px !important;
4684
4532
  right: 0 !important;
4685
- padding: 16px 12px;
4686
- padding-bottom: calc(16px + var(--uvf-safe-area-bottom, 0px));
4687
- padding-left: calc(12px + var(--uvf-safe-area-left, 0px));
4688
- padding-right: calc(12px + var(--uvf-safe-area-right, 0px));
4689
- background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
4533
+ left: auto !important;
4534
+ top: auto !important;
4535
+ /* Ensure no child elements extend beyond container */
4536
+ contain: layout style paint;
4537
+ }
4538
+
4539
+ /* Ensure all child elements of controls stay within bounds */
4540
+ .uvf-responsive-container .uvf-controls-bar * {
4541
+ max-width: 100%;
4690
4542
  box-sizing: border-box;
4691
- z-index: 1000 !important;
4692
- /* iOS Safari specific fixes */
4693
- transform: translateZ(0);
4694
- -webkit-transform: translateZ(0);
4695
- will-change: transform;
4696
- /* Ensure proper stacking */
4697
- isolation: isolate;
4543
+ }
4544
+
4545
+ /* Progress section contained within controls */
4546
+ .uvf-progress-section {
4547
+ width: 100%;
4548
+ margin-bottom: 8px;
4549
+ opacity: 1 !important;
4550
+ visibility: visible !important;
4551
+ display: block !important;
4552
+ }
4553
+
4554
+ /* Progress bar styling */
4555
+ .uvf-progress-bar-wrapper {
4556
+ opacity: 1 !important;
4557
+ visibility: visible !important;
4558
+ }
4559
+
4560
+ .uvf-progress-bar {
4561
+ height: 4px;
4562
+ background: rgba(255, 255, 255, 0.3);
4563
+ border-radius: 2px;
4564
+ }
4565
+
4566
+ .uvf-progress-filled {
4567
+ background: var(--uvf-accent-1, #8B5CF6);
4568
+ height: 100%;
4569
+ border-radius: 2px;
4570
+ }
4571
+
4572
+ /* Controls row alignment - ensure visibility */
4573
+ .uvf-controls-row {
4574
+ width: 100%;
4575
+ display: flex !important;
4576
+ align-items: center;
4577
+ justify-content: flex-start;
4578
+ gap: 12px;
4579
+ opacity: 1 !important;
4580
+ visibility: visible !important;
4581
+ margin-top: 8px;
4582
+ }
4583
+
4584
+ /* Time display visibility */
4585
+ .uvf-time-display {
4586
+ color: #fff;
4587
+ font-size: 12px;
4588
+ font-weight: 500;
4589
+ opacity: 1 !important;
4590
+ visibility: visible !important;
4591
+ display: block !important;
4592
+ }
4593
+
4594
+ /* Control buttons visibility */
4595
+ .uvf-control-btn {
4596
+ min-width: 44px;
4597
+ min-height: 44px;
4598
+ display: flex !important;
4599
+ align-items: center;
4600
+ justify-content: center;
4601
+ border-radius: 50%;
4602
+ background: rgba(255, 255, 255, 0.1);
4603
+ backdrop-filter: blur(8px);
4604
+ color: #fff;
4605
+ opacity: 1 !important;
4606
+ visibility: visible !important;
4607
+ transition: all 0.2s ease;
4608
+ }
4609
+
4610
+ .uvf-control-btn:active {
4611
+ transform: scale(0.95);
4612
+ background: rgba(255, 255, 255, 0.2);
4613
+ }
4614
+
4615
+ /* Play/pause button prominence */
4616
+ .uvf-control-btn.play-pause {
4617
+ background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
4618
+ min-width: 52px;
4619
+ min-height: 52px;
4620
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
4621
+ }
4622
+
4623
+ .uvf-control-btn.play-pause:active {
4624
+ transform: scale(0.92);
4625
+ box-shadow: 0 1px 4px rgba(0,0,0,0.4);
4626
+ }
4627
+
4628
+ .uvf-control-btn svg {
4629
+ fill: #fff;
4630
+ opacity: 1;
4631
+ }
4632
+
4633
+ /* Right controls */
4634
+ .uvf-right-controls {
4635
+ margin-left: auto;
4636
+ display: flex !important;
4637
+ align-items: center;
4638
+ gap: 8px;
4639
+ opacity: 1 !important;
4640
+ visibility: visible !important;
4641
+ }
4642
+
4643
+ /* Center play button positioned within video */
4644
+ .uvf-center-play-container {
4645
+ position: absolute;
4646
+ top: 50%;
4647
+ left: 50%;
4648
+ transform: translate(-50%, -50%);
4649
+ z-index: 8;
4650
+ pointer-events: none;
4651
+ }
4652
+
4653
+ .uvf-center-play-btn {
4654
+ pointer-events: auto;
4655
+ }
4656
+
4657
+ /* Top controls within video */
4658
+ .uvf-top-controls {
4659
+ position: absolute;
4660
+ top: calc(12px + var(--uvf-safe-area-top));
4661
+ right: calc(16px + var(--uvf-safe-area-right));
4662
+ z-index: 9;
4663
+ }
4664
+
4665
+ /* Title bar within video */
4666
+ .uvf-title-bar {
4667
+ position: absolute;
4668
+ top: calc(12px + var(--uvf-safe-area-top));
4669
+ left: calc(16px + var(--uvf-safe-area-left));
4670
+ right: calc(80px + var(--uvf-safe-area-right));
4671
+ z-index: 9;
4672
+ padding: 8px 0;
4698
4673
  }
4699
4674
 
4700
4675
  .uvf-progress-section {
@@ -5075,69 +5050,55 @@ export class WebPlayer extends BasePlayer {
5075
5050
  }
5076
5051
  }
5077
5052
 
5078
- /* Mobile devices (landscape) - Fullscreen with controls inside video */
5053
+ /* Mobile devices (landscape) - Optimized for fullscreen viewing with safe areas */
5079
5054
  @media screen and (max-width: 767px) and (orientation: landscape) {
5080
5055
  .uvf-responsive-container {
5081
- position: fixed;
5082
- top: 0;
5083
- left: 0;
5084
5056
  width: 100vw !important;
5085
- height: 100vh !important;
5086
- height: 100dvh !important;
5087
- background: #000;
5088
- z-index: 9999;
5089
- display: block; /* Override portrait flexbox */
5057
+ height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
5090
5058
  margin: 0;
5091
5059
  padding: 0;
5060
+ position: relative;
5092
5061
  overflow: hidden;
5093
5062
  }
5094
5063
 
5095
- /* Remove pseudo-elements in landscape */
5096
- .uvf-responsive-container::before,
5097
- .uvf-responsive-container::after {
5098
- display: none !important;
5064
+ @supports (height: 100dvh) {
5065
+ .uvf-responsive-container {
5066
+ height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
5067
+ }
5099
5068
  }
5100
5069
 
5101
5070
  .uvf-responsive-container .uvf-player-wrapper {
5102
5071
  width: 100vw !important;
5103
- height: 100vh !important;
5104
- height: 100dvh !important;
5105
- position: relative;
5106
- display: block;
5072
+ height: 100% !important;
5073
+ min-height: calc(100vh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
5107
5074
  }
5108
5075
 
5109
- .uvf-responsive-container .uvf-video-container {
5110
- width: 100vw !important;
5111
- height: 100vh !important;
5112
- height: 100dvh !important;
5113
- position: relative;
5114
- background: #000;
5115
- overflow: hidden;
5076
+ @supports (height: 100dvh) {
5077
+ .uvf-responsive-container .uvf-player-wrapper {
5078
+ min-height: calc(100dvh - var(--uvf-safe-area-top) - var(--uvf-safe-area-bottom));
5079
+ }
5116
5080
  }
5117
-
5118
- .uvf-responsive-container .uvf-video {
5119
- width: 100%;
5120
- height: 100%;
5121
- object-fit: contain;
5122
- background: #000;
5081
+
5082
+ .uvf-responsive-container .uvf-video-container {
5083
+ width: 100vw !important;
5084
+ height: 100% !important;
5085
+ aspect-ratio: unset !important;
5086
+ min-height: inherit;
5123
5087
  }
5124
5088
 
5125
- /* Compact controls for landscape - positioned inside video container */
5089
+ /* Compact controls for landscape with safe area padding */
5126
5090
  .uvf-controls-bar {
5127
- position: absolute !important;
5128
- bottom: 0 !important;
5129
- left: 0 !important;
5130
- right: 0 !important;
5091
+ position: absolute;
5092
+ bottom: 0;
5093
+ left: 0;
5094
+ right: 0;
5131
5095
  padding: 10px 12px;
5132
5096
  padding-bottom: calc(10px + var(--uvf-safe-area-bottom));
5133
5097
  padding-left: calc(12px + var(--uvf-safe-area-left));
5134
5098
  padding-right: calc(12px + var(--uvf-safe-area-right));
5135
- background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.6) 60%, transparent 100%);
5099
+ background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 80%, var(--uvf-overlay-transparent) 100%);
5136
5100
  box-sizing: border-box;
5137
5101
  z-index: 1000;
5138
- /* Ensure hardware acceleration */
5139
- -webkit-transform: translate3d(0,0,0);
5140
- transform: translate3d(0,0,0);
5141
5102
  }
5142
5103
 
5143
5104
  .uvf-progress-section {
@@ -5173,36 +5134,16 @@ export class WebPlayer extends BasePlayer {
5173
5134
  height: 22px;
5174
5135
  }
5175
5136
 
5176
- /* Compact top controls with safe area padding - inside video container */
5137
+ /* Compact top controls with safe area padding */
5177
5138
  .uvf-top-controls {
5178
- position: absolute;
5179
5139
  top: calc(8px + var(--uvf-safe-area-top));
5180
5140
  right: calc(12px + var(--uvf-safe-area-right));
5181
5141
  gap: 6px;
5182
- z-index: 9;
5183
- }
5184
-
5185
- /* Center play button positioned within video container */
5186
- .uvf-center-play-container {
5187
- position: absolute;
5188
- top: 50%;
5189
- left: 50%;
5190
- transform: translate(-50%, -50%);
5191
- z-index: 8;
5192
- pointer-events: none;
5193
- }
5194
-
5195
- .uvf-center-play-btn {
5196
- pointer-events: auto;
5197
5142
  }
5198
5143
 
5199
5144
  .uvf-title-bar {
5200
- position: absolute;
5201
- top: calc(8px + var(--uvf-safe-area-top));
5202
- left: calc(12px + var(--uvf-safe-area-left));
5203
- right: calc(80px + var(--uvf-safe-area-right));
5204
- z-index: 9;
5205
- padding: 8px 0;
5145
+ padding: 8px 12px;
5146
+ padding-top: calc(8px + var(--uvf-safe-area-top));
5206
5147
  padding-left: calc(12px + var(--uvf-safe-area-left));
5207
5148
  padding-right: calc(12px + var(--uvf-safe-area-right));
5208
5149
  }
@@ -5390,234 +5331,13 @@ export class WebPlayer extends BasePlayer {
5390
5331
 
5391
5332
  /* Large screens - Enhanced desktop experience */
5392
5333
  @media screen and (min-width: 1024px) {
5393
- /* Reset mobile portrait styles for desktop */
5394
5334
  .uvf-responsive-container {
5395
- display: block !important; /* Override mobile flexbox */
5396
- position: relative !important; /* Override mobile fixed */
5397
- height: auto !important; /* Override mobile viewport height */
5398
- background: transparent !important; /* Override mobile black background */
5399
5335
  padding: 10px;
5400
5336
  }
5401
5337
 
5402
- /* Remove mobile pseudo-elements on desktop */
5403
- .uvf-responsive-container::before,
5404
- .uvf-responsive-container::after {
5405
- display: none !important;
5406
- }
5407
-
5408
- /* Desktop player wrapper */
5409
- .uvf-responsive-container .uvf-player-wrapper {
5410
- width: 100% !important; /* Override mobile width */
5411
- height: auto !important; /* Override mobile height */
5412
- display: block !important; /* Override mobile flexbox */
5413
- position: relative !important;
5414
- max-width: none; /* Allow full width if needed */
5415
- }
5416
-
5417
- /* Desktop video container */
5418
- .uvf-responsive-container .uvf-video-container {
5419
- width: 100% !important;
5420
- height: auto !important;
5421
- position: relative;
5422
- aspect-ratio: 16/9; /* Maintain desktop aspect ratio */
5423
- background: #000;
5424
- }
5425
-
5426
- /* Desktop controls positioning */
5427
5338
  .uvf-controls-bar {
5428
- position: absolute !important;
5429
- bottom: 0 !important;
5430
- left: 0 !important;
5431
- right: 0 !important;
5432
5339
  padding: 20px;
5433
5340
  background: linear-gradient(to top, var(--uvf-overlay-strong) 0%, var(--uvf-overlay-medium) 60%, var(--uvf-overlay-transparent) 100%);
5434
- /* Reset mobile overrides for desktop */
5435
- opacity: 0; /* Default hidden state on desktop */
5436
- transform: translateY(10px); /* Default hidden position */
5437
- transition: all 0.3s ease;
5438
- }
5439
-
5440
- /* Desktop hover behavior */
5441
- .uvf-player-wrapper:hover .uvf-controls-bar,
5442
- .uvf-player-wrapper.controls-visible .uvf-controls-bar {
5443
- opacity: 1 !important;
5444
- transform: translateY(0) !important;
5445
- }
5446
-
5447
- /* Desktop progress section */
5448
- .uvf-progress-section {
5449
- margin-bottom: 15px;
5450
- width: 100%;
5451
- }
5452
-
5453
- /* Desktop controls row */
5454
- .uvf-controls-row {
5455
- display: flex;
5456
- align-items: center;
5457
- justify-content: flex-start;
5458
- gap: 14px;
5459
- width: 100%;
5460
- }
5461
-
5462
- /* Desktop control buttons */
5463
- .uvf-control-btn {
5464
- width: 40px;
5465
- height: 40px;
5466
- min-width: 40px;
5467
- min-height: 40px;
5468
- display: flex;
5469
- align-items: center;
5470
- justify-content: center;
5471
- border: none;
5472
- border-radius: 50%;
5473
- background: rgba(255, 255, 255, 0.1);
5474
- color: #fff;
5475
- cursor: pointer;
5476
- transition: all 0.2s cubic-bezier(0.4, 0.0, 0.2, 1);
5477
- backdrop-filter: blur(8px);
5478
- }
5479
-
5480
- .uvf-control-btn:hover {
5481
- transform: scale(1.1);
5482
- background: rgba(255, 255, 255, 0.2);
5483
- }
5484
-
5485
- .uvf-control-btn.play-pause {
5486
- width: 50px;
5487
- height: 50px;
5488
- min-width: 50px;
5489
- min-height: 50px;
5490
- background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
5491
- box-shadow: 0 2px 8px rgba(0,0,0,0.2);
5492
- }
5493
-
5494
- .uvf-control-btn.play-pause:hover {
5495
- transform: scale(1.08);
5496
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
5497
- }
5498
-
5499
- .uvf-control-btn svg {
5500
- width: 20px;
5501
- height: 20px;
5502
- fill: #fff;
5503
- }
5504
-
5505
- .uvf-control-btn.play-pause svg {
5506
- width: 24px;
5507
- height: 24px;
5508
- }
5509
-
5510
- /* Desktop right controls */
5511
- .uvf-right-controls {
5512
- margin-left: auto;
5513
- display: flex;
5514
- align-items: center;
5515
- gap: 8px;
5516
- }
5517
-
5518
- /* Desktop time display */
5519
- .uvf-time-display {
5520
- color: #fff;
5521
- font-size: 14px;
5522
- font-weight: 500;
5523
- padding: 0 10px;
5524
- }
5525
-
5526
- /* Desktop volume control */
5527
- .uvf-volume-panel {
5528
- display: flex !important; /* Show volume slider on desktop */
5529
- align-items: center;
5530
- gap: 8px;
5531
- }
5532
-
5533
- .uvf-volume-slider {
5534
- width: 80px;
5535
- height: 4px;
5536
- background: rgba(255, 255, 255, 0.3);
5537
- border-radius: 2px;
5538
- cursor: pointer;
5539
- }
5540
-
5541
- /* Desktop settings and other controls */
5542
- .uvf-quality-badge {
5543
- display: block;
5544
- background: rgba(255, 255, 255, 0.1);
5545
- color: #fff;
5546
- padding: 4px 8px;
5547
- border-radius: 4px;
5548
- font-size: 11px;
5549
- font-weight: 500;
5550
- }
5551
-
5552
- /* Desktop progress bar */
5553
- .uvf-progress-bar {
5554
- height: 2px;
5555
- background: rgba(255, 255, 255, 0.2);
5556
- border-radius: 4px;
5557
- cursor: pointer;
5558
- transition: height 0.2s ease;
5559
- }
5560
-
5561
- .uvf-progress-bar-wrapper:hover .uvf-progress-bar {
5562
- height: 4px;
5563
- }
5564
-
5565
- .uvf-progress-filled {
5566
- background: var(--uvf-accent-1, #8B5CF6);
5567
- height: 100%;
5568
- border-radius: 4px;
5569
- }
5570
-
5571
- .uvf-progress-handle {
5572
- width: 12px;
5573
- height: 12px;
5574
- background: #fff;
5575
- border-radius: 50%;
5576
- position: absolute;
5577
- top: 50%;
5578
- transform: translateY(-50%);
5579
- cursor: grab;
5580
- opacity: 0;
5581
- transition: opacity 0.2s ease;
5582
- }
5583
-
5584
- .uvf-progress-bar-wrapper:hover .uvf-progress-handle {
5585
- opacity: 1;
5586
- }
5587
-
5588
- /* Desktop center play button */
5589
- .uvf-center-play-container {
5590
- position: absolute;
5591
- top: 50%;
5592
- left: 50%;
5593
- transform: translate(-50%, -50%);
5594
- z-index: 8;
5595
- }
5596
-
5597
- .uvf-center-play-btn {
5598
- width: clamp(56px, 10vw, 72px);
5599
- height: clamp(56px, 10vw, 72px);
5600
- background: linear-gradient(135deg, var(--uvf-accent-1), var(--uvf-accent-2));
5601
- border: none;
5602
- border-radius: 50%;
5603
- display: flex;
5604
- align-items: center;
5605
- justify-content: center;
5606
- cursor: pointer;
5607
- box-shadow: 0 4px 16px rgba(0,0,0,0.3);
5608
- transition: all 0.25s ease;
5609
- }
5610
-
5611
- .uvf-center-play-btn:hover {
5612
- transform: scale(1.1);
5613
- box-shadow: 0 6px 20px rgba(0,0,0,0.4);
5614
- }
5615
-
5616
- .uvf-center-play-btn svg {
5617
- width: clamp(24px, 4vw, 30px);
5618
- height: clamp(24px, 4vw, 30px);
5619
- fill: #fff;
5620
- margin-left: 2px;
5621
5341
  }
5622
5342
 
5623
5343
  .uvf-progress-section {
@@ -6438,7 +6158,7 @@ export class WebPlayer extends BasePlayer {
6438
6158
  this.debugLog('Settings button NOT created - settings disabled');
6439
6159
  }
6440
6160
 
6441
- // EPG button (Electronic Program Guide)
6161
+ // EPG button (Electronic Program Guide) - Only show if EPG data is available
6442
6162
  const epgBtn = document.createElement('button');
6443
6163
  epgBtn.className = 'uvf-control-btn';
6444
6164
  epgBtn.id = 'uvf-epg-btn';
@@ -6449,8 +6169,17 @@ export class WebPlayer extends BasePlayer {
6449
6169
  <rect x="19" y="3" width="2" height="2"/>
6450
6170
  <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"/>
6451
6171
  </svg>`;
6452
- epgBtn.style.display = 'none'; // Initially hidden, will be shown when EPG data is available
6453
- rightControls.appendChild(epgBtn);
6172
+ // CRITICAL: EPG button should ONLY be visible when EPG data is available
6173
+ epgBtn.style.display = 'none !important'; // Force hidden - will only show when EPG data is loaded
6174
+
6175
+ // Only add to controls if EPG functionality is enabled and data will be available
6176
+ const epgConfig = (this.config as any).epg;
6177
+ if (epgConfig && epgConfig.enabled) {
6178
+ rightControls.appendChild(epgBtn);
6179
+ this.debugLog('EPG button created but hidden - will show when EPG data is loaded');
6180
+ } else {
6181
+ this.debugLog('EPG button not created - EPG functionality disabled');
6182
+ }
6454
6183
 
6455
6184
  // PiP button - only show on desktop/supported browsers
6456
6185
  const pipBtn = document.createElement('button');
@@ -8765,27 +8494,8 @@ export class WebPlayer extends BasePlayer {
8765
8494
  // Speed options
8766
8495
  settingsMenu.querySelectorAll('.speed-option').forEach(option => {
8767
8496
  option.addEventListener('click', (e) => {
8768
- e.preventDefault();
8769
- e.stopPropagation();
8770
-
8771
8497
  const speed = parseFloat((e.target as HTMLElement).dataset.speed || '1');
8772
-
8773
- // Update active state immediately
8774
- this.updateSettingsActiveStates('speed-option', e.target as HTMLElement);
8775
-
8776
- // Set the speed
8777
8498
  this.setPlaybackRateFromSettings(speed);
8778
-
8779
- // Update the accordion header text immediately
8780
- const speedLabel = speed === 1 ? 'Normal' : `${speed}x`;
8781
- const accordionCurrent = option.closest('.uvf-accordion-item')?.querySelector('.uvf-accordion-current');
8782
- if (accordionCurrent) {
8783
- accordionCurrent.textContent = speedLabel;
8784
- }
8785
-
8786
- this.debugLog(`Speed selected: ${speed}x`);
8787
-
8788
- // Update accordion after selection
8789
8499
  this.updateAccordionAfterSelection('speed');
8790
8500
  });
8791
8501
  });
@@ -8793,28 +8503,8 @@ export class WebPlayer extends BasePlayer {
8793
8503
  // Quality options
8794
8504
  settingsMenu.querySelectorAll('.quality-option').forEach(option => {
8795
8505
  option.addEventListener('click', (e) => {
8796
- e.preventDefault();
8797
- e.stopPropagation();
8798
-
8799
8506
  const quality = (e.target as HTMLElement).dataset.quality || 'auto';
8800
-
8801
- // Update active state immediately
8802
- this.updateSettingsActiveStates('quality-option', e.target as HTMLElement);
8803
-
8804
- // Set the quality
8805
8507
  this.setQualityFromSettings(quality);
8806
-
8807
- // Update the accordion header text immediately
8808
- const qualityItem = this.availableQualities.find(q => q.value === quality);
8809
- const qualityLabel = qualityItem ? qualityItem.label : 'Auto';
8810
- const accordionCurrent = option.closest('.uvf-accordion-item')?.querySelector('.uvf-accordion-current');
8811
- if (accordionCurrent) {
8812
- accordionCurrent.textContent = qualityLabel;
8813
- }
8814
-
8815
- this.debugLog(`Quality selected: ${quality} (${qualityLabel})`);
8816
-
8817
- // Update accordion after selection
8818
8508
  this.updateAccordionAfterSelection('quality');
8819
8509
  });
8820
8510
  });
@@ -8822,28 +8512,8 @@ export class WebPlayer extends BasePlayer {
8822
8512
  // Subtitle options
8823
8513
  settingsMenu.querySelectorAll('.subtitle-option').forEach(option => {
8824
8514
  option.addEventListener('click', (e) => {
8825
- e.preventDefault();
8826
- e.stopPropagation();
8827
-
8828
8515
  const subtitle = (e.target as HTMLElement).dataset.subtitle || 'off';
8829
-
8830
- // Update active state immediately
8831
- this.updateSettingsActiveStates('subtitle-option', e.target as HTMLElement);
8832
-
8833
- // Set the subtitle
8834
8516
  this.setSubtitle(subtitle);
8835
-
8836
- // Update the accordion header text immediately
8837
- const subtitleItem = this.availableSubtitles.find(s => s.value === subtitle);
8838
- const subtitleLabel = subtitleItem ? subtitleItem.label : 'Off';
8839
- const accordionCurrent = option.closest('.uvf-accordion-item')?.querySelector('.uvf-accordion-current');
8840
- if (accordionCurrent) {
8841
- accordionCurrent.textContent = subtitleLabel;
8842
- }
8843
-
8844
- this.debugLog(`Subtitle selected: ${subtitle} (${subtitleLabel})`);
8845
-
8846
- // Update accordion after selection
8847
8517
  this.updateAccordionAfterSelection('subtitles');
8848
8518
  });
8849
8519
  });
@@ -8899,25 +8569,13 @@ export class WebPlayer extends BasePlayer {
8899
8569
  * Update accordion after user makes a selection
8900
8570
  */
8901
8571
  private updateAccordionAfterSelection(section: string): void {
8902
- // Close the accordion section after selection
8903
- const settingsMenu = document.getElementById('uvf-settings-menu');
8904
- if (settingsMenu) {
8905
- settingsMenu.querySelectorAll('.uvf-accordion-item.expanded').forEach(item => {
8906
- item.classList.remove('expanded');
8907
- });
8908
- }
8909
-
8910
- // Update the current values and refresh the menu
8572
+ // Just update the current values without closing
8573
+ // User can manually close or it will close when they click outside
8911
8574
  setTimeout(() => {
8912
8575
  // Refresh the menu to update current values
8913
8576
  this.generateAccordionMenu();
8914
8577
  this.setupSettingsEventListeners();
8915
- }, 50);
8916
-
8917
- // Auto-hide the settings menu after selection (optional)
8918
- setTimeout(() => {
8919
- this.hideSettingsMenu();
8920
- }, 150);
8578
+ }, 100);
8921
8579
  }
8922
8580
 
8923
8581
  /**
@@ -9485,8 +9143,9 @@ export class WebPlayer extends BasePlayer {
9485
9143
  public showEPGButton(): void {
9486
9144
  const epgBtn = document.getElementById('uvf-epg-btn');
9487
9145
  if (epgBtn) {
9488
- epgBtn.style.display = 'block';
9489
- this.debugLog('EPG button shown');
9146
+ // Remove any forced display none and set to block
9147
+ epgBtn.style.setProperty('display', 'block', 'important');
9148
+ this.debugLog('EPG button shown with !important override');
9490
9149
  } else {
9491
9150
  this.debugLog('EPG button not found in DOM');
9492
9151
  }
@@ -9498,8 +9157,8 @@ export class WebPlayer extends BasePlayer {
9498
9157
  public hideEPGButton(): void {
9499
9158
  const epgBtn = document.getElementById('uvf-epg-btn');
9500
9159
  if (epgBtn) {
9501
- epgBtn.style.display = 'none';
9502
- this.debugLog('EPG button hidden');
9160
+ epgBtn.style.setProperty('display', 'none', 'important');
9161
+ this.debugLog('EPG button hidden with !important');
9503
9162
  }
9504
9163
  }
9505
9164
 
@@ -9524,7 +9183,30 @@ export class WebPlayer extends BasePlayer {
9524
9183
  */
9525
9184
  public isEPGButtonVisible(): boolean {
9526
9185
  const epgBtn = document.getElementById('uvf-epg-btn');
9527
- return epgBtn ? epgBtn.style.display !== 'none' : false;
9186
+ if (epgBtn) {
9187
+ const isVisible = epgBtn.style.display !== 'none';
9188
+ this.debugLog(`EPG button visibility check: ${isVisible}, display style: ${epgBtn.style.display}`);
9189
+ return isVisible;
9190
+ }
9191
+ this.debugLog('EPG button visibility check: false (button not found in DOM)');
9192
+ return false;
9193
+ }
9194
+
9195
+ /**
9196
+ * Debug method to log current EPG button state and configuration
9197
+ */
9198
+ public debugEPGState(): void {
9199
+ const epgBtn = document.getElementById('uvf-epg-btn');
9200
+ const epgConfig = (this.config as any).epg;
9201
+ this.debugLog('=== EPG DEBUG STATE ===');
9202
+ this.debugLog(`EPG Config Enabled: ${epgConfig?.enabled || false}`);
9203
+ this.debugLog(`EPG Button Exists: ${!!epgBtn}`);
9204
+ if (epgBtn) {
9205
+ this.debugLog(`EPG Button Display Style: ${epgBtn.style.display}`);
9206
+ this.debugLog(`EPG Button Computed Display: ${window.getComputedStyle(epgBtn).display}`);
9207
+ this.debugLog(`EPG Button Visible: ${this.isEPGButtonVisible()}`);
9208
+ }
9209
+ this.debugLog('=====================');
9528
9210
  }
9529
9211
 
9530
9212
  private async cleanup(): Promise<void> {