@bluebillywig/react-native-bb-player 8.42.15 → 8.44.0

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.
@@ -1,6 +1,42 @@
1
1
  import Foundation
2
2
  import React
3
3
 
4
+ /**
5
+ * Global registry for BBPlayerView instances.
6
+ * This is needed because the bridge.uiManager.view(forReactTag:) doesn't work
7
+ * with the New Architecture (Fabric). Views register themselves when created.
8
+ */
9
+ class BBPlayerViewRegistry: NSObject {
10
+ static let shared = BBPlayerViewRegistry()
11
+
12
+ private var views: [Int: BBPlayerView] = [:]
13
+ private let lock = NSLock()
14
+
15
+ private override init() {
16
+ super.init()
17
+ }
18
+
19
+ func register(_ view: BBPlayerView, tag: Int) {
20
+ lock.lock()
21
+ defer { lock.unlock() }
22
+ views[tag] = view
23
+ NSLog("BBPlayerViewRegistry: Registered view with tag %d (total: %d)", tag, views.count)
24
+ }
25
+
26
+ func unregister(tag: Int) {
27
+ lock.lock()
28
+ defer { lock.unlock() }
29
+ views.removeValue(forKey: tag)
30
+ NSLog("BBPlayerViewRegistry: Unregistered view with tag %d (total: %d)", tag, views.count)
31
+ }
32
+
33
+ func getView(tag: Int) -> BBPlayerView? {
34
+ lock.lock()
35
+ defer { lock.unlock() }
36
+ return views[tag]
37
+ }
38
+ }
39
+
4
40
  /**
5
41
  * Native Module for BBPlayer commands.
6
42
  * This module looks up BBPlayerView instances by their React tag and dispatches commands to them.
@@ -21,11 +57,20 @@ class BBPlayerModule: NSObject {
21
57
  // MARK: - Helper to get view by tag
22
58
 
23
59
  private func getView(_ reactTag: NSNumber) -> BBPlayerView? {
24
- guard let bridge = self.bridge else {
25
- NSLog("BBPlayerModule: Bridge is nil")
26
- return nil
60
+ // First try the view registry (works with both old and new architecture)
61
+ if let view = BBPlayerViewRegistry.shared.getView(tag: reactTag.intValue) {
62
+ return view
27
63
  }
28
- return bridge.uiManager.view(forReactTag: reactTag) as? BBPlayerView
64
+
65
+ // Fallback to bridge.uiManager for old architecture
66
+ if let bridge = self.bridge {
67
+ if let view = bridge.uiManager.view(forReactTag: reactTag) as? BBPlayerView {
68
+ return view
69
+ }
70
+ }
71
+
72
+ NSLog("BBPlayerModule: Could not find view with tag %@", reactTag)
73
+ return nil
29
74
  }
30
75
 
31
76
  // MARK: - Commands
@@ -151,10 +196,15 @@ class BBPlayerModule: NSObject {
151
196
  }
152
197
 
153
198
  @objc func loadWithJsonUrl(_ viewTag: NSNumber, jsonUrl: String?, autoPlay: Bool) {
199
+ NSLog("BBPlayerModule.loadWithJsonUrl called - viewTag: %@, jsonUrl: %@, autoPlay: %d", viewTag, jsonUrl ?? "nil", autoPlay)
154
200
  DispatchQueue.main.async {
155
- // Load new JSON URL - update the jsonUrl property and re-setup
156
- if let view = self.getView(viewTag), let url = jsonUrl {
157
- view.jsonUrl = url
201
+ let view = self.getView(viewTag)
202
+ NSLog("BBPlayerModule.loadWithJsonUrl - view found: %@", view != nil ? "YES" : "NO")
203
+ if let view = view, let url = jsonUrl {
204
+ NSLog("BBPlayerModule.loadWithJsonUrl - calling view.loadWithJsonUrl with url: %@", url)
205
+ view.loadWithJsonUrl(url, autoPlay: autoPlay)
206
+ } else {
207
+ NSLog("BBPlayerModule.loadWithJsonUrl - FAILED: view=%@, url=%@", view != nil ? "found" : "nil", jsonUrl ?? "nil")
158
208
  }
159
209
  }
160
210
  }
@@ -148,6 +148,11 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
148
148
  self.layer.drawsAsynchronously = true
149
149
  self.isOpaque = true
150
150
 
151
+ // Register with view registry for command dispatch (supports New Architecture)
152
+ if let tag = self.reactTag {
153
+ BBPlayerViewRegistry.shared.register(self, tag: tag.intValue)
154
+ }
155
+
151
156
  setupAppLifecycleObservers()
152
157
 
153
158
  // Find the parent view controller from the responder chain
@@ -170,6 +175,11 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
170
175
  } else {
171
176
  log("BBPlayerView.didMoveToWindow - view removed from window, isInFullscreen: \(isInFullscreen)")
172
177
 
178
+ // Unregister from view registry when removed from window (unless in fullscreen)
179
+ if !isInFullscreen, let tag = self.reactTag {
180
+ BBPlayerViewRegistry.shared.unregister(tag: tag.intValue)
181
+ }
182
+
173
183
  if !isInFullscreen {
174
184
  stopTimeUpdates()
175
185
  }
@@ -203,6 +213,10 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
203
213
  }
204
214
 
205
215
  deinit {
216
+ // Unregister from view registry
217
+ if let tag = self.reactTag {
218
+ BBPlayerViewRegistry.shared.unregister(tag: tag.intValue)
219
+ }
206
220
  stopTimeUpdates()
207
221
  removeAppLifecycleObservers()
208
222
  independentCastButton?.removeFromSuperview()
@@ -714,6 +728,100 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
714
728
  playerView?.player.loadWithProjectJson(projectJson: projectJson, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?)
715
729
  }
716
730
 
731
+ /**
732
+ * Load content from a JSON URL into the existing player.
733
+ * Extracts IDs from the URL and uses the native SDK's loadWithXxxId methods.
734
+ * This is more reliable than parsing JSON because the SDK handles all the loading internally.
735
+ *
736
+ * Note: Shorts URLs (/sh/{id}.json) are NOT supported here - use BBShortsView instead.
737
+ */
738
+ func loadWithJsonUrl(_ url: String, autoPlay: Bool) {
739
+ NSLog("BBPlayerView.loadWithJsonUrl called - url: %@, autoPlay: %d", url, autoPlay)
740
+ guard playerView != nil else {
741
+ NSLog("BBPlayerView.loadWithJsonUrl ERROR - playerView not initialized")
742
+ return
743
+ }
744
+
745
+ NSLog("BBPlayerView.loadWithJsonUrl - playerView exists, parsing URL")
746
+
747
+ // Extract ID from URL patterns like:
748
+ // /c/{id}.json or /mediaclip/{id}.json -> clip ID
749
+ // /l/{id}.json or /mediacliplist/{id}.json -> clip list ID
750
+ // /pj/{id}.json or /project/{id}.json -> project ID
751
+
752
+ let clipIdPattern = "/c/([0-9]+)\\.json|/mediaclip/([0-9]+)"
753
+ let clipListIdPattern = "/l/([0-9]+)\\.json|/mediacliplist/([0-9]+)"
754
+ let projectIdPattern = "/pj/([0-9]+)\\.json|/project/([0-9]+)"
755
+ let shortsIdPattern = "/sh/([0-9]+)\\.json"
756
+
757
+ if let shortsMatch = url.range(of: shortsIdPattern, options: .regularExpression) {
758
+ // Shorts require a separate BBShortsView component
759
+ log("ERROR - Shorts URLs are not supported in BBPlayerView. Use BBShortsView instead.", level: .error)
760
+ onDidFailWithError?(["payload": "Shorts URLs are not supported in BBPlayerView. Use BBShortsView instead."])
761
+ return
762
+ }
763
+
764
+ if let match = url.range(of: clipListIdPattern, options: .regularExpression) {
765
+ // Extract the cliplist ID
766
+ if let clipListId = extractIdFromUrl(url, pattern: clipListIdPattern) {
767
+ NSLog("BBPlayerView.loadWithJsonUrl - Loading ClipList by ID: %@", clipListId)
768
+ playerView?.player.loadWithClipListId(clipListId: clipListId, initiator: "external", autoPlay: autoPlay, seekTo: nil)
769
+ } else {
770
+ NSLog("BBPlayerView.loadWithJsonUrl ERROR - Failed to extract cliplist ID from URL: %@", url)
771
+ }
772
+ return
773
+ }
774
+
775
+ if let match = url.range(of: projectIdPattern, options: .regularExpression) {
776
+ // Extract the project ID
777
+ if let projectId = extractIdFromUrl(url, pattern: projectIdPattern) {
778
+ NSLog("BBPlayerView.loadWithJsonUrl - Loading Project by ID: %@", projectId)
779
+ playerView?.player.loadWithProjectId(projectId: projectId, initiator: "external", autoPlay: autoPlay, seekTo: nil)
780
+ } else {
781
+ NSLog("BBPlayerView.loadWithJsonUrl ERROR - Failed to extract project ID from URL: %@", url)
782
+ }
783
+ return
784
+ }
785
+
786
+ if let match = url.range(of: clipIdPattern, options: .regularExpression) {
787
+ // Extract the clip ID
788
+ if let clipId = extractIdFromUrl(url, pattern: clipIdPattern) {
789
+ NSLog("BBPlayerView.loadWithJsonUrl - Loading Clip by ID: %@", clipId)
790
+ playerView?.player.loadWithClipId(clipId: clipId, initiator: "external", autoPlay: autoPlay, seekTo: nil)
791
+ } else {
792
+ NSLog("BBPlayerView.loadWithJsonUrl ERROR - Failed to extract clip ID from URL: %@", url)
793
+ }
794
+ return
795
+ }
796
+
797
+ NSLog("BBPlayerView.loadWithJsonUrl ERROR - Unknown URL format, cannot extract ID: %@", url)
798
+ onDidFailWithError?(["payload": "Cannot load content: unsupported URL format"])
799
+ }
800
+
801
+ /**
802
+ * Helper to extract numeric ID from URL using regex pattern with capture groups
803
+ */
804
+ private func extractIdFromUrl(_ url: String, pattern: String) -> String? {
805
+ do {
806
+ let regex = try NSRegularExpression(pattern: pattern, options: [])
807
+ let range = NSRange(url.startIndex..., in: url)
808
+ if let match = regex.firstMatch(in: url, options: [], range: range) {
809
+ // Try each capture group (pattern has multiple alternatives with |)
810
+ for i in 1..<match.numberOfRanges {
811
+ if let groupRange = Range(match.range(at: i), in: url) {
812
+ let extracted = String(url[groupRange])
813
+ if !extracted.isEmpty {
814
+ return extracted
815
+ }
816
+ }
817
+ }
818
+ }
819
+ } catch {
820
+ log("ERROR - Regex error: \(error)", level: .error)
821
+ }
822
+ return nil
823
+ }
824
+
717
825
  func showCastPicker() {
718
826
  log("showCastPicker called")
719
827
 
@@ -146,6 +146,14 @@ class BBPlayerViewManager: RCTViewManager {
146
146
  }
147
147
  }
148
148
 
149
+ @objc func loadWithJsonUrl(_ reactTag: NSNumber, jsonUrl: String?, autoPlay: Bool) {
150
+ DispatchQueue.main.async {
151
+ if let url = jsonUrl {
152
+ self.getView(reactTag)?.loadWithJsonUrl(url, autoPlay: autoPlay)
153
+ }
154
+ }
155
+ }
156
+
149
157
  // Note: loadWithShortsId is NOT supported on BBPlayerView.
150
158
  // For Shorts playback, use the BBShortsView component instead.
151
159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluebillywig/react-native-bb-player",
3
- "version": "8.42.15",
3
+ "version": "8.44.0",
4
4
  "description": "Blue Billywig Native Video Player for React Native - iOS AVPlayer and Android ExoPlayer integration",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",