unified-video-framework 1.4.77 → 1.4.79
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.
- package/package.json +1 -1
- package/packages/ios/README.md +84 -0
- package/packages/web/dist/WebPlayer.d.ts +2 -0
- package/packages/web/dist/WebPlayer.d.ts.map +1 -1
- package/packages/web/dist/WebPlayer.js +132 -19
- package/packages/web/dist/WebPlayer.js.map +1 -1
- package/packages/web/dist/epg/EPGController.d.ts +78 -0
- package/packages/web/dist/epg/EPGController.d.ts.map +1 -0
- package/packages/web/dist/epg/EPGController.js +476 -0
- package/packages/web/dist/epg/EPGController.js.map +1 -0
- package/packages/web/dist/paywall/EmailAuthController.d.ts +60 -0
- package/packages/web/dist/paywall/PaywallController.d.ts +74 -0
- package/packages/web/dist/react/EPG.d.ts +8 -0
- package/packages/web/dist/react/WebPlayerViewWithEPG.d.ts +97 -0
- package/packages/web/dist/react/components/EPGNavigationControls.d.ts +15 -0
- package/packages/web/dist/react/components/{EPGOverlay-improved-positioning.d.ts → EPGOverlay.d.ts} +1 -1
- package/packages/web/dist/react/components/EPGProgramDetails.d.ts +13 -0
- package/packages/web/dist/react/components/EPGProgramGrid.d.ts +0 -2
- package/packages/web/dist/react/components/EPGTimelineHeader.d.ts +0 -2
- package/packages/web/dist/react/types/EPGTypes.d.ts +101 -0
- package/packages/web/dist/react/utils/EPGUtils.d.ts +21 -0
- package/packages/web/dist/test/epg-test.d.ts +3 -0
- package/packages/web/src/WebPlayer.ts +161 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unified-video-framework",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.79",
|
|
4
4
|
"description": "Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more",
|
|
5
5
|
"main": "packages/core/dist/index.js",
|
|
6
6
|
"types": "packages/core/dist/index.d.ts",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# UnifiedVideoPlayer (iOS SDK)
|
|
2
|
+
|
|
3
|
+
A unified iOS video player SDK with:
|
|
4
|
+
- HLS playback (AVPlayer)
|
|
5
|
+
- FairPlay DRM (SPC/CKC)
|
|
6
|
+
- Subtitles/audio track selection (AVMediaSelection)
|
|
7
|
+
- Picture-in-Picture (AVPictureInPictureController)
|
|
8
|
+
- AirPlay (AVRoutePickerView)
|
|
9
|
+
- Remote Command Center + Now Playing (lock screen controls)
|
|
10
|
+
- Background audio (AVAudioSession)
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
### CocoaPods
|
|
15
|
+
1) Ensure this SDK is in a public Git repository. Update the podspec `s.source` to point at that repo and tag.
|
|
16
|
+
2) In your Podfile:
|
|
17
|
+
```ruby
|
|
18
|
+
platform :ios, '13.0'
|
|
19
|
+
use_frameworks!
|
|
20
|
+
|
|
21
|
+
target 'YourApp' do
|
|
22
|
+
pod 'UnifiedVideoPlayer', :git => 'https://github.com/yourcompany/unified-video-ios.git', :tag => '1.0.0'
|
|
23
|
+
end
|
|
24
|
+
```
|
|
25
|
+
3) Run:
|
|
26
|
+
```bash
|
|
27
|
+
pod install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Swift Package Manager
|
|
31
|
+
Add the package at the repository URL or use Add Local Package pointing to `packages/ios`.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
```swift
|
|
35
|
+
import UnifiedVideoPlayer
|
|
36
|
+
|
|
37
|
+
let containerView = UIView(frame: .zero)
|
|
38
|
+
let player = UnifiedVideoPlayer()
|
|
39
|
+
let config = PlayerConfiguration()
|
|
40
|
+
config.autoPlay = true
|
|
41
|
+
|
|
42
|
+
player.initialize(container: containerView, configuration: config)
|
|
43
|
+
|
|
44
|
+
let source = MediaSource(url: "https://example.com/stream.m3u8")
|
|
45
|
+
source.metadata = ["title": "Demo Stream"]
|
|
46
|
+
player.onReady = { print("ready") }
|
|
47
|
+
player.onQualityChange = { br in print("bitrate: \(br)") }
|
|
48
|
+
|
|
49
|
+
player.load(source: source)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### FairPlay DRM
|
|
53
|
+
```swift
|
|
54
|
+
let drm = DRMConfiguration(type: "fairplay", licenseUrl: "https://license.example.com/fps")
|
|
55
|
+
drm.certificateUrl = "https://license.example.com/cert"
|
|
56
|
+
drm.headers = ["X-Tenant-ID": "default"]
|
|
57
|
+
|
|
58
|
+
let source = MediaSource(url: "https://cdn.example.com/protected/playlist.m3u8")
|
|
59
|
+
source.drm = drm
|
|
60
|
+
player.load(source: source)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Tracks
|
|
64
|
+
```swift
|
|
65
|
+
let audios = player.audioTracks() // [String]
|
|
66
|
+
let subs = player.subtitleTracks() // [String]
|
|
67
|
+
player.selectAudioTrack(index: 0)
|
|
68
|
+
player.selectSubtitleTrack(index: -1) // off
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### PiP & AirPlay
|
|
72
|
+
```swift
|
|
73
|
+
player.startPictureInPicture()
|
|
74
|
+
player.stopPictureInPicture()
|
|
75
|
+
let airPlay = player.makeAirPlayPickerView()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Capabilities
|
|
79
|
+
- Enable Background Modes > Audio
|
|
80
|
+
- For DRM endpoints, configure ATS exceptions if necessary.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
MIT
|
|
84
|
+
|
|
@@ -77,6 +77,8 @@ export declare class WebPlayer extends BasePlayer {
|
|
|
77
77
|
exitFullscreen(): Promise<void>;
|
|
78
78
|
enterPictureInPicture(): Promise<void>;
|
|
79
79
|
exitPictureInPicture(): Promise<void>;
|
|
80
|
+
focusPlayer(): void;
|
|
81
|
+
attemptFullscreen(): Promise<boolean>;
|
|
80
82
|
protected applySubtitleTrack(track: any): void;
|
|
81
83
|
protected removeSubtitles(): void;
|
|
82
84
|
private injectStyles;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebPlayer.d.ts","sourceRoot":"","sources":["../src/WebPlayer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAUjD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,GAAG,EAAE,GAAG,CAAC;QACT,MAAM,EAAE,GAAG,CAAC;QACZ,IAAI,CAAC,EAAE,GAAG,CAAC;QACX,MAAM,CAAC,EAAE,GAAG,CAAC;QACb,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;KACxD;CACF;AAED,qBAAa,SAAU,SAAQ,UAAU;IACvC,SAAS,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAChD,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,mBAAmB,CAAc;IACzC,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,mBAAmB,CAAa;IACxC,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,aAAa,CAA4B;IAEjD,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,iBAAiB,CAAkB;IAG3C,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,sBAAsB,CAAa;IAC3C,OAAO,CAAC,sBAAsB,CAAa;IAC3C,OAAO,CAAC,yBAAyB,CAAa;IAC9C,OAAO,CAAC,iBAAiB,CAAa;IAGtC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,mBAAmB,CAAiB;IAC5C,OAAO,CAAC,KAAK,CAAa;IAG1B,OAAO,CAAC,iBAAiB,CAAa;IAGtC,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,mBAAmB,CAAO;IAGlC,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,SAAS;cAOD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA+I5C,OAAO,CAAC,wBAAwB;IAsHhC,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,oBAAoB;IAYtB,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDtC,OAAO,CAAC,gBAAgB;YAcV,OAAO;IAsDrB,OAAO,CAAC,cAAc;YAyBR,QAAQ;IAyDtB,OAAO,CAAC,iBAAiB;YAQX,UAAU;IAMxB,SAAS,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhD,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,gBAAgB;IAOlB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+C3B,KAAK,IAAI,IAAI;IAkBN,YAAY,IAAI,IAAI;IAO3B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAwBxB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM9B,IAAI,IAAI,IAAI;IAMZ,MAAM,IAAI,IAAI;IAMd,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMnC,cAAc,IAAI,MAAM;IAOxB,YAAY,IAAI,GAAG,EAAE;IAIrB,iBAAiB,IAAI,GAAG;IAIxB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAW/B,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAkBhC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"WebPlayer.d.ts","sourceRoot":"","sources":["../src/WebPlayer.ts"],"names":[],"mappings":"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"}
|
|
@@ -646,43 +646,80 @@ export class WebPlayer extends BasePlayer {
|
|
|
646
646
|
if (!this.playerWrapper)
|
|
647
647
|
return;
|
|
648
648
|
try {
|
|
649
|
+
if (!document.fullscreenEnabled &&
|
|
650
|
+
!document.webkitFullscreenEnabled &&
|
|
651
|
+
!document.mozFullScreenEnabled &&
|
|
652
|
+
!document.msFullscreenEnabled) {
|
|
653
|
+
this.debugWarn('Fullscreen not supported by browser');
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (document.fullscreenElement ||
|
|
657
|
+
document.webkitFullscreenElement ||
|
|
658
|
+
document.mozFullScreenElement ||
|
|
659
|
+
document.msFullscreenElement) {
|
|
660
|
+
this.debugLog('Already in fullscreen mode');
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
649
663
|
const element = this.playerWrapper;
|
|
650
664
|
if (element.requestFullscreen) {
|
|
651
|
-
await element.requestFullscreen()
|
|
665
|
+
await element.requestFullscreen().catch(err => {
|
|
666
|
+
this.debugWarn('Fullscreen request failed:', err.message);
|
|
667
|
+
});
|
|
652
668
|
}
|
|
653
669
|
else if (element.webkitRequestFullscreen) {
|
|
654
|
-
await element.webkitRequestFullscreen()
|
|
670
|
+
await element.webkitRequestFullscreen().catch((err) => {
|
|
671
|
+
this.debugWarn('WebKit fullscreen request failed:', err.message);
|
|
672
|
+
});
|
|
655
673
|
}
|
|
656
674
|
else if (element.mozRequestFullScreen) {
|
|
657
|
-
await element.mozRequestFullScreen()
|
|
675
|
+
await element.mozRequestFullScreen().catch((err) => {
|
|
676
|
+
this.debugWarn('Mozilla fullscreen request failed:', err.message);
|
|
677
|
+
});
|
|
658
678
|
}
|
|
659
679
|
else if (element.msRequestFullscreen) {
|
|
660
|
-
await element.msRequestFullscreen()
|
|
680
|
+
await element.msRequestFullscreen().catch((err) => {
|
|
681
|
+
this.debugWarn('MS fullscreen request failed:', err.message);
|
|
682
|
+
});
|
|
661
683
|
}
|
|
662
684
|
else {
|
|
663
|
-
|
|
685
|
+
this.debugWarn('Fullscreen API not supported by this browser');
|
|
686
|
+
return;
|
|
664
687
|
}
|
|
665
688
|
this.playerWrapper.classList.add('uvf-fullscreen');
|
|
666
689
|
this.emit('onFullscreenChanged', true);
|
|
667
690
|
}
|
|
668
691
|
catch (error) {
|
|
669
|
-
|
|
670
|
-
throw error;
|
|
692
|
+
this.debugWarn('Failed to enter fullscreen:', error.message);
|
|
671
693
|
}
|
|
672
694
|
}
|
|
673
695
|
async exitFullscreen() {
|
|
674
696
|
try {
|
|
697
|
+
if (!document.fullscreenElement &&
|
|
698
|
+
!document.webkitFullscreenElement &&
|
|
699
|
+
!document.mozFullScreenElement &&
|
|
700
|
+
!document.msFullscreenElement) {
|
|
701
|
+
this.debugLog('Not in fullscreen mode');
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
675
704
|
if (document.exitFullscreen) {
|
|
676
|
-
await document.exitFullscreen()
|
|
705
|
+
await document.exitFullscreen().catch(err => {
|
|
706
|
+
this.debugWarn('Exit fullscreen failed:', err.message);
|
|
707
|
+
});
|
|
677
708
|
}
|
|
678
709
|
else if (document.webkitExitFullscreen) {
|
|
679
|
-
await document.webkitExitFullscreen()
|
|
710
|
+
await document.webkitExitFullscreen().catch((err) => {
|
|
711
|
+
this.debugWarn('WebKit exit fullscreen failed:', err.message);
|
|
712
|
+
});
|
|
680
713
|
}
|
|
681
714
|
else if (document.mozCancelFullScreen) {
|
|
682
|
-
await document.mozCancelFullScreen()
|
|
715
|
+
await document.mozCancelFullScreen().catch((err) => {
|
|
716
|
+
this.debugWarn('Mozilla exit fullscreen failed:', err.message);
|
|
717
|
+
});
|
|
683
718
|
}
|
|
684
719
|
else if (document.msExitFullscreen) {
|
|
685
|
-
await document.msExitFullscreen()
|
|
720
|
+
await document.msExitFullscreen().catch((err) => {
|
|
721
|
+
this.debugWarn('MS exit fullscreen failed:', err.message);
|
|
722
|
+
});
|
|
686
723
|
}
|
|
687
724
|
if (this.playerWrapper) {
|
|
688
725
|
this.playerWrapper.classList.remove('uvf-fullscreen');
|
|
@@ -690,8 +727,7 @@ export class WebPlayer extends BasePlayer {
|
|
|
690
727
|
this.emit('onFullscreenChanged', false);
|
|
691
728
|
}
|
|
692
729
|
catch (error) {
|
|
693
|
-
|
|
694
|
-
throw error;
|
|
730
|
+
this.debugWarn('Failed to exit fullscreen:', error.message);
|
|
695
731
|
}
|
|
696
732
|
}
|
|
697
733
|
async enterPictureInPicture() {
|
|
@@ -717,8 +753,32 @@ export class WebPlayer extends BasePlayer {
|
|
|
717
753
|
}
|
|
718
754
|
}
|
|
719
755
|
catch (error) {
|
|
720
|
-
|
|
721
|
-
|
|
756
|
+
this.debugWarn('Failed to exit PiP:', error.message);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
focusPlayer() {
|
|
760
|
+
if (this.playerWrapper) {
|
|
761
|
+
this.playerWrapper.focus();
|
|
762
|
+
this.debugLog('Player focused programmatically');
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async attemptFullscreen() {
|
|
766
|
+
try {
|
|
767
|
+
await this.enterFullscreen();
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
catch (error) {
|
|
771
|
+
const errorMessage = error.message;
|
|
772
|
+
if (errorMessage.includes('user gesture') || errorMessage.includes('user activation')) {
|
|
773
|
+
this.showShortcutIndicator('Click the fullscreen button');
|
|
774
|
+
this.debugWarn('Fullscreen requires user interaction. Please click the fullscreen button or press F while the video area is focused.');
|
|
775
|
+
this.focusPlayer();
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
this.debugWarn('Fullscreen failed:', errorMessage);
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
722
782
|
}
|
|
723
783
|
}
|
|
724
784
|
applySubtitleTrack(track) {
|
|
@@ -779,6 +839,21 @@ export class WebPlayer extends BasePlayer {
|
|
|
779
839
|
--uvf-firefox-scrollbar-color: rgba(255,255,255,0.25);
|
|
780
840
|
}
|
|
781
841
|
|
|
842
|
+
/* Player focus styles for better UX */
|
|
843
|
+
.uvf-player-wrapper:focus {
|
|
844
|
+
outline: 2px solid var(--uvf-accent-1);
|
|
845
|
+
outline-offset: -2px;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.uvf-player-wrapper:focus-visible {
|
|
849
|
+
outline: 2px solid var(--uvf-accent-1);
|
|
850
|
+
outline-offset: -2px;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.uvf-player-wrapper:focus:not(:focus-visible) {
|
|
854
|
+
outline: none;
|
|
855
|
+
}
|
|
856
|
+
|
|
782
857
|
/* Responsive Container Styles */
|
|
783
858
|
.uvf-responsive-container {
|
|
784
859
|
width: 100%;
|
|
@@ -2533,10 +2608,14 @@ export class WebPlayer extends BasePlayer {
|
|
|
2533
2608
|
});
|
|
2534
2609
|
fullscreenBtn?.addEventListener('click', () => {
|
|
2535
2610
|
if (this.isFullscreen()) {
|
|
2536
|
-
this.exitFullscreen()
|
|
2611
|
+
this.exitFullscreen().catch(err => {
|
|
2612
|
+
this.debugWarn('Exit fullscreen button failed:', err.message);
|
|
2613
|
+
});
|
|
2537
2614
|
}
|
|
2538
2615
|
else {
|
|
2539
|
-
this.enterFullscreen()
|
|
2616
|
+
this.enterFullscreen().catch(err => {
|
|
2617
|
+
this.debugWarn('Fullscreen button failed:', err.message);
|
|
2618
|
+
});
|
|
2540
2619
|
}
|
|
2541
2620
|
});
|
|
2542
2621
|
const updateFullscreenIcon = () => {
|
|
@@ -2675,12 +2754,37 @@ export class WebPlayer extends BasePlayer {
|
|
|
2675
2754
|
break;
|
|
2676
2755
|
case 'f':
|
|
2677
2756
|
e.preventDefault();
|
|
2757
|
+
const isLikelyUserGesture = e.isTrusted &&
|
|
2758
|
+
(e.target === document.body ||
|
|
2759
|
+
e.target === this.playerWrapper ||
|
|
2760
|
+
e.target === this.video);
|
|
2678
2761
|
if (!document.fullscreenElement) {
|
|
2679
|
-
|
|
2762
|
+
if (isLikelyUserGesture || target.tagName !== 'BUTTON') {
|
|
2763
|
+
this.attemptFullscreen().then(success => {
|
|
2764
|
+
if (success) {
|
|
2765
|
+
this.showShortcutIndicator('Fullscreen');
|
|
2766
|
+
}
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
else {
|
|
2770
|
+
const fullscreenBtn = document.getElementById('uvf-fullscreen-btn');
|
|
2771
|
+
if (fullscreenBtn) {
|
|
2772
|
+
this.debugLog('Triggering fullscreen button click instead of direct API call');
|
|
2773
|
+
fullscreenBtn.click();
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
else {
|
|
2777
|
+
this.debugWarn('Fullscreen keyboard shortcut not available from button elements. Click the fullscreen button instead.');
|
|
2778
|
+
this.showShortcutIndicator('Click Fullscreen Button');
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2680
2782
|
shortcutText = 'Fullscreen';
|
|
2681
2783
|
}
|
|
2682
2784
|
else {
|
|
2683
|
-
this.exitFullscreen()
|
|
2785
|
+
this.exitFullscreen().catch(err => {
|
|
2786
|
+
this.debugWarn('Exit fullscreen shortcut failed:', err.message);
|
|
2787
|
+
});
|
|
2684
2788
|
shortcutText = 'Exit Fullscreen';
|
|
2685
2789
|
}
|
|
2686
2790
|
break;
|
|
@@ -2722,6 +2826,15 @@ export class WebPlayer extends BasePlayer {
|
|
|
2722
2826
|
if (this.playerWrapper) {
|
|
2723
2827
|
this.playerWrapper.addEventListener('keydown', handleKeydown);
|
|
2724
2828
|
this.playerWrapper.setAttribute('tabindex', '0');
|
|
2829
|
+
this.playerWrapper.addEventListener('focus', () => {
|
|
2830
|
+
this.debugLog('Player focused - keyboard shortcuts available');
|
|
2831
|
+
});
|
|
2832
|
+
this.playerWrapper.addEventListener('click', (e) => {
|
|
2833
|
+
const target = e.target;
|
|
2834
|
+
if (!target.closest('.uvf-controls')) {
|
|
2835
|
+
this.playerWrapper?.focus();
|
|
2836
|
+
}
|
|
2837
|
+
});
|
|
2725
2838
|
}
|
|
2726
2839
|
if (this.video) {
|
|
2727
2840
|
this.video.addEventListener('keydown', handleKeydown);
|