@capgo/capacitor-jw-player 0.0.13 → 0.0.20

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.
@@ -2,7 +2,6 @@ import Foundation
2
2
  import Capacitor
3
3
  import JWPlayerKit
4
4
  import AVKit // Import AVKit for AVAudioSession
5
- import GoogleCast
6
5
  /**
7
6
  * Please read the Capacitor iOS Plugin Development Guide
8
7
  * here: https://capacitorjs.com/docs/plugins/ios
@@ -16,7 +15,8 @@ protocol CallbackHandler {
16
15
  }
17
16
 
18
17
  @objc(JwPlayerPlugin)
19
- public class JwPlayerPlugin: CAPPlugin, CAPBridgedPlugin, GCKLoggerDelegate {
18
+ public class JwPlayerPlugin: CAPPlugin, CAPBridgedPlugin {
19
+ // public class JwPlayerPlugin: CAPPlugin, CAPBridgedPlugin, GCKLoggerDelegate {
20
20
  public let identifier = "JwPlayerPlugin"
21
21
  public let jsName = "JwPlayer"
22
22
  public let pluginMethods: [CAPPluginMethod] = [
@@ -46,8 +46,8 @@ public class JwPlayerPlugin: CAPPlugin, CAPBridgedPlugin, GCKLoggerDelegate {
46
46
  override public func load() {
47
47
  // Configure the audio session when the plugin loads
48
48
  setupAudioSession()
49
- print("[JWPlayer] Plugin loaded. Note: Chromecast is initialized in AppDelegate.")
50
- initializeChromecast()
49
+ // print("[JWPlayer] Plugin loaded. Note: Chromecast is initialized in AppDelegate.")
50
+ // initializeChromecast()
51
51
  super.load()
52
52
  }
53
53
 
@@ -63,46 +63,46 @@ public class JwPlayerPlugin: CAPPlugin, CAPBridgedPlugin, GCKLoggerDelegate {
63
63
  }
64
64
  }
65
65
 
66
- private func initializeChromecast() {
67
- print("[ChromecastDebug] Starting Chromecast initialization")
68
- let kReceiverAppID = "CC1AD845" // Custom App ID for Chromecast
69
- let discoveryCriteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
70
- let options = GCKCastOptions(discoveryCriteria: discoveryCriteria)
71
- options.physicalVolumeButtonsWillControlDeviceVolume = true
72
- options.disableDiscoveryAutostart = false
73
- print("[ChromecastDebug] Setting custom options for Chromecast")
74
- do {
75
- GCKCastContext.setSharedInstanceWith(options)
76
- print("[ChromecastDebug] Chromecast context initialized with App ID: \(kReceiverAppID)")
77
- } catch {
78
- print("[ChromecastDebug] Error initializing Chromecast context: \(error.localizedDescription)")
79
- }
80
-
81
- let filter = GCKLoggerFilter.init()
82
- filter.minimumLevel = .verbose
83
- GCKLogger.sharedInstance().filter = filter
84
- GCKLogger.sharedInstance().delegate = self
85
- print("[ChromecastDebug] Logger initialized")
86
- }
87
-
88
- public func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) {
89
- print(function + " - " + message)
90
- }
91
-
92
- @objc func initialize(_ call: CAPPluginCall) {
93
- print("[JWPlayer] initialize called")
94
- guard let licenseKey = call.getString("licenseKey") else {
95
- print("[JWPlayer] Error: licenseKey is missing")
96
- call.reject("licenseKey is required for initialize")
97
- return
98
- }
99
-
100
- print("[JWPlayer] Setting license key")
101
- JWPlayerKitLicense.setLicenseKey(licenseKey)
102
- print("[JWPlayer] License key set successfully")
103
-
104
- call.resolve()
105
- }
66
+ // private func initializeChromecast() {
67
+ // print("[ChromecastDebug] Starting Chromecast initialization")
68
+ // let kReceiverAppID = "CC1AD845" // Custom App ID for Chromecast
69
+ // let discoveryCriteria = GCKDiscoveryCriteria(applicationID: kReceiverAppID)
70
+ // let options = GCKCastOptions(discoveryCriteria: discoveryCriteria)
71
+ // options.physicalVolumeButtonsWillControlDeviceVolume = true
72
+ // options.disableDiscoveryAutostart = false
73
+ // print("[ChromecastDebug] Setting custom options for Chromecast")
74
+ // do {
75
+ // GCKCastContext.setSharedInstanceWith(options)
76
+ // print("[ChromecastDebug] Chromecast context initialized with App ID: \(kReceiverAppID)")
77
+ // } catch {
78
+ // print("[ChromecastDebug] Error initializing Chromecast context: \(error.localizedDescription)")
79
+ // }
80
+
81
+ // let filter = GCKLoggerFilter.init()
82
+ // filter.minimumLevel = .verbose
83
+ // GCKLogger.sharedInstance().filter = filter
84
+ // GCKLogger.sharedInstance().delegate = self
85
+ // print("[ChromecastDebug] Logger initialized")
86
+ // }
87
+
88
+ // public func logMessage(_ message: String, at level: GCKLoggerLevel, fromFunction function: String, location: String) {
89
+ // print(function + " - " + message)
90
+ // }
91
+
92
+ // @objc func initialize(_ call: CAPPluginCall) {
93
+ // print("[JWPlayer] initialize called")
94
+ // guard let licenseKey = call.getString("licenseKey") else {
95
+ // print("[JWPlayer] Error: licenseKey is missing")
96
+ // call.reject("licenseKey is required for initialize")
97
+ // return
98
+ // }
99
+
100
+ // print("[JWPlayer] Setting license key")
101
+ // JWPlayerKitLicense.setLicenseKey(licenseKey)
102
+ // print("[JWPlayer] License key set successfully")
103
+
104
+ // call.resolve()
105
+ // }
106
106
 
107
107
  @objc func play(_ call: CAPPluginCall) {
108
108
  print("[JWPlayer] play called with options: \(call.options)")
@@ -114,7 +114,7 @@ public class JwPlayerPlugin: CAPPlugin, CAPBridgedPlugin, GCKLoggerDelegate {
114
114
 
115
115
  guard let mediaUrl = URL(string: mediaUrlStr) else {
116
116
  print("[JWPlayer] Error: mediaUrl is invalid URL: \(mediaUrlStr)")
117
- call.reject("mediaUrl is invalid URL")
117
+ call.reject("mediaUrl is invalid URL: \(mediaUrlStr)")
118
118
  return
119
119
  }
120
120
 
@@ -580,6 +580,7 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
580
580
  private var callbackHandler: CallbackHandler?
581
581
  private var playerConfig: JWPlayerConfiguration?
582
582
  private var closeButton: UIButton! // Custom close button
583
+ private var timeUpdateTimer: Timer? // Timer for periodic time updates
583
584
 
584
585
  // Standard init is unavailable
585
586
  @available(*, unavailable)
@@ -642,6 +643,9 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
642
643
  print("[JWPlayer] Configuring player from init config")
643
644
  player.configurePlayer(with: config)
644
645
  print("[JWPlayer] Player configured successfully from init config")
646
+
647
+ // JWPlayerViewController already has self as delegates
648
+ // We are just implementing the delegate methods
645
649
  } else {
646
650
  print("[JWPlayer] Error: Player configuration is missing in viewDidLoad")
647
651
  self.callbackHandler?.notifyEventListener("error", data: ["message": "Player configuration missing"])
@@ -649,19 +653,59 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
649
653
 
650
654
  // Add the custom close button
651
655
  setupCloseButton()
656
+
657
+ // Start time update timer after player is ready
658
+ startTimeUpdateTimer()
659
+ }
660
+
661
+ // Start a timer to emit time updates periodically
662
+ private func startTimeUpdateTimer() {
663
+ // Cancel any existing timer
664
+ timeUpdateTimer?.invalidate()
665
+
666
+ // Create a new timer that fires every second
667
+ timeUpdateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
668
+ guard let self = self else { return }
669
+
670
+ if self.player.getState() == .playing {
671
+ let position = self.player.time.position
672
+ let duration = self.player.time.duration
673
+
674
+ let timeData: [String: Any] = [
675
+ "position": position,
676
+ "duration": duration
677
+ ]
678
+
679
+ self.callbackHandler?.notifyEventListener("time", data: timeData)
680
+ }
681
+ }
682
+ }
683
+
684
+ // Stop the time update timer
685
+ private func stopTimeUpdateTimer() {
686
+ timeUpdateTimer?.invalidate()
687
+ timeUpdateTimer = nil
652
688
  }
653
689
 
654
690
  override func viewDidAppear(_ animated: Bool) {
655
691
  print("[JWPlayer] viewDidAppear")
656
692
  super.viewDidAppear(animated)
657
-
658
- // Start playback automatically
659
- // print("[JWPlayer] Starting playback")
660
- // player.play()
661
- // print("[JWPlayer] Playback started")
662
693
  print("[JWPlayer] Button exists \(view.viewWithTag(2136) !== nil)")
663
694
  }
664
695
 
696
+ override func viewWillDisappear(_ animated: Bool) {
697
+ super.viewWillDisappear(animated)
698
+
699
+ // Stop the time update timer when view disappears
700
+ stopTimeUpdateTimer()
701
+
702
+ // Only trigger the dismissal callback if it's a manual dismissal
703
+ if self.isBeingDismissed || self.isMovingFromParent {
704
+ print("[JWPlayer] Manual dismissal detected in viewWillDisappear")
705
+ callbackHandler?.onPlayerDismissed(isPiPDismissal: false)
706
+ }
707
+ }
708
+
665
709
  // Setup and add the custom close button
666
710
  private func setupCloseButton() {
667
711
  closeButton = UIButton(type: .custom)
@@ -703,6 +747,89 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
703
747
  }
704
748
  }
705
749
 
750
+ // MARK: - JWPlayerDelegate Methods
751
+
752
+ override func jwplayerIsReady(_ player: JWPlayer) {
753
+ super.jwplayerIsReady(player)
754
+ print("[JWPlayer] Player is ready")
755
+ callbackHandler?.notifyEventListener("ready", data: nil)
756
+ }
757
+
758
+ override func jwplayer(_ player: JWPlayer, failedWithError code: UInt, message: String) {
759
+ super.jwplayer(player, failedWithError: code, message: message)
760
+ print("[JWPlayer] Error: \(message), code: \(code)")
761
+ let errorData: [String: Any] = [
762
+ "message": message,
763
+ "code": code
764
+ ]
765
+ callbackHandler?.notifyEventListener("error", data: errorData)
766
+ }
767
+
768
+ override func jwplayer(_ player: JWPlayer, failedWithSetupError code: UInt, message: String) {
769
+ super.jwplayer(player, failedWithSetupError: code, message: message)
770
+ print("[JWPlayer] Setup error: \(message), code: \(code)")
771
+ let errorData: [String: Any] = [
772
+ "message": message,
773
+ "code": code
774
+ ]
775
+ callbackHandler?.notifyEventListener("error", data: errorData)
776
+ }
777
+
778
+ // MARK: - JWPlayerStateDelegate Methods
779
+
780
+ override func jwplayer(_ player: JWPlayer, isPlayingWithReason reason: JWPlayReason) {
781
+ super.jwplayer(player, isPlayingWithReason: reason)
782
+ print("[JWPlayer] Playing with reason: \(reason)")
783
+ callbackHandler?.notifyEventListener("play", data: ["reason": reason.rawValue])
784
+ }
785
+
786
+ override func jwplayer(_ player: JWPlayer, didPauseWithReason reason: JWPauseReason) {
787
+ super.jwplayer(player, didPauseWithReason: reason)
788
+ print("[JWPlayer] Paused with reason: \(reason)")
789
+ callbackHandler?.notifyEventListener("pause", data: ["reason": reason.rawValue])
790
+ }
791
+
792
+ override func jwplayerContentDidComplete(_ player: JWPlayer) {
793
+ super.jwplayerContentDidComplete(player)
794
+ print("[JWPlayer] Content completed")
795
+ callbackHandler?.notifyEventListener("complete", data: nil)
796
+ }
797
+
798
+ override func jwplayer(_ player: JWPlayer, seekedFrom position: TimeInterval, to offset: TimeInterval) {
799
+ super.jwplayer(player, seekedFrom: position, to: offset)
800
+ print("[JWPlayer] Seeked from \(position) to \(offset)")
801
+ let seekData: [String: Any] = [
802
+ "position": position,
803
+ "offset": offset
804
+ ]
805
+ callbackHandler?.notifyEventListener("seek", data: seekData)
806
+ }
807
+
808
+ override func jwplayerHasSeeked(_ player: JWPlayer) {
809
+ super.jwplayerHasSeeked(player)
810
+ print("[JWPlayer] Seeked")
811
+ callbackHandler?.notifyEventListener("seeked", data: nil)
812
+ }
813
+
814
+ override func jwplayer(_ player: JWPlayer, didLoadPlaylistItem item: JWPlayerItem, at index: UInt) {
815
+ super.jwplayer(player, didLoadPlaylistItem: item, at: index)
816
+ print("[JWPlayer] Playlist item loaded at index: \(index)")
817
+ var itemData: [String: Any] = ["index": index]
818
+
819
+ // Add title if available
820
+ if let title = item.title, !title.isEmpty {
821
+ itemData["title"] = title
822
+ }
823
+
824
+ callbackHandler?.notifyEventListener("playlistItem", data: itemData)
825
+ }
826
+
827
+ override func jwplayerPlaylistHasCompleted(_ player: JWPlayer) {
828
+ super.jwplayerPlaylistHasCompleted(player)
829
+ print("[JWPlayer] Playlist completed")
830
+ callbackHandler?.notifyEventListener("playlistComplete", data: nil)
831
+ }
832
+
706
833
  // MARK: - JWPlayerViewControllerUIDelegate Method
707
834
 
708
835
  func playerViewController(_ controller: JWPlayerViewController, controlBarVisibilityChanged isVisible: Bool, frame: CGRect) {
@@ -713,6 +840,9 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
713
840
  // Ensure closeButton is not nil before accessing
714
841
  guard let button = self.closeButton else { return }
715
842
  button.alpha = targetAlpha
843
+
844
+ // Emit controlsChanged event to match other platforms
845
+ callbackHandler?.notifyEventListener("controlsChanged", data: ["visible": isVisible])
716
846
  }
717
847
 
718
848
  // MARK: - AVPictureInPictureControllerDelegate Methods
@@ -729,11 +859,13 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
729
859
  override func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
730
860
  print("[JWPlayer] Delegate: PiP Did Start")
731
861
  super.pictureInPictureControllerDidStartPictureInPicture(pictureInPictureController)
862
+ callbackHandler?.notifyEventListener("pipStarted", data: ["isInPictureInPictureMode": true])
732
863
  }
733
864
 
734
865
  override func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
735
866
  print("[JWPlayer] Delegate: PiP Failed to Start: \(error.localizedDescription)")
736
867
  super.pictureInPictureController(pictureInPictureController, failedToStartPictureInPictureWithError: error)
868
+ callbackHandler?.notifyEventListener("error", data: ["message": "Failed to start PiP: \(error.localizedDescription)"])
737
869
  }
738
870
 
739
871
  override func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
@@ -744,17 +876,10 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
744
876
  override func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
745
877
  print("[JWPlayer] Delegate: PiP Did Stop")
746
878
  super.pictureInPictureControllerDidStopPictureInPicture(pictureInPictureController)
747
-
748
- // If PiP is stopped directly by the user without restoration, notify the plugin
749
- // print("[JWPlayer] PiP stopped, cleaning up plugin reference.")
750
- // self.callbackHandler?.onPlayerDismissed(isPiPDismissal: false)
879
+ callbackHandler?.notifyEventListener("pipStopped", data: ["isInPictureInPictureMode": false])
751
880
  }
752
881
 
753
882
  override func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
754
- // print("[JWPlayer] Delegate: PiP Restore UI Requested")
755
- // Ask the plugin to re-present this view controller instance
756
- // callbackHandler?.rePresentPlayerRequested()
757
-
758
883
  // Here I will REMOVE the button to close and readd it
759
884
  if let closeButton = self.closeButton {
760
885
  closeButton.removeFromSuperview()
@@ -766,16 +891,6 @@ class CustomPlayerViewController: JWPlayerViewController, JWPlayerViewController
766
891
  super.pictureInPictureController(pictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: completionHandler)
767
892
  }
768
893
 
769
- override func viewWillDisappear(_ animated: Bool) {
770
- super.viewWillDisappear(animated)
771
-
772
- // Only trigger the dismissal callback if it's a manual dismissal
773
- if self.isBeingDismissed || self.isMovingFromParent {
774
- print("[JWPlayer] Manual dismissal detected in viewWillDisappear")
775
- callbackHandler?.onPlayerDismissed(isPiPDismissal: false)
776
- }
777
- }
778
-
779
894
  // MARK: - JWCastDelegate
780
895
  // Optionally, override the following methods to receive and respond to events when casting.
781
896
  // Always call the superclass's method when overriding these methods.
@@ -1,3 +1,4 @@
1
+ import Foundation
1
2
  //
2
3
  // VideosToPlay.swift
3
4
  // Pods
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-jw-player",
3
- "version": "0.0.13",
3
+ "version": "0.0.20",
4
4
  "description": "Playes videos from jwplayer.com",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -59,7 +59,8 @@
59
59
  "rimraf": "^6.0.1",
60
60
  "rollup": "^4.30.1",
61
61
  "swiftlint": "^2.0.0",
62
- "typescript": "~4.1.5"
62
+ "typescript": "~4.1.5",
63
+ "@types/jwplayer": "^8.31.3"
63
64
  },
64
65
  "peerDependencies": {
65
66
  "@capacitor/core": ">=7.2.0"