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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unified-video-framework",
3
- "version": "1.4.77",
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":"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"}
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
- throw new Error('Fullscreen API not supported');
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
- console.error('Failed to enter fullscreen:', error);
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
- console.error('Failed to exit fullscreen:', error);
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
- console.error('Failed to exit PiP:', error);
721
- throw error;
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
- this.enterFullscreen();
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);