@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.
- package/ios/BBPlayerModule.swift +57 -7
- package/ios/BBPlayerView.swift +108 -0
- package/ios/BBPlayerViewManager.swift +8 -0
- package/package.json +1 -1
package/ios/BBPlayerModule.swift
CHANGED
|
@@ -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
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
}
|
package/ios/BBPlayerView.swift
CHANGED
|
@@ -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.
|
|
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",
|