@bluebillywig/react-native-bb-player 8.44.0 → 8.45.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/README.md +80 -59
- package/android/build.gradle +2 -1
- package/android/src/main/java/com/bluebillywig/bbplayer/BBPlayerModule.kt +146 -28
- package/android/src/main/java/com/bluebillywig/bbplayer/BBPlayerView.kt +74 -165
- package/android/src/main/java/com/bluebillywig/bbplayer/BBPlayerViewManager.kt +0 -6
- package/android/src/paper/java/com/bluebillywig/bbplayer/NativeBBPlayerModuleSpec.java +19 -8
- package/ios/BBPlayerModule.mm +17 -8
- package/ios/BBPlayerModule.swift +138 -26
- package/ios/BBPlayerView.swift +41 -140
- package/ios/BBPlayerViewManager.m +0 -2
- package/lib/commonjs/BBModalPlayer.js +21 -0
- package/lib/commonjs/BBModalPlayer.js.map +1 -0
- package/lib/commonjs/BBOutstreamView.js +0 -1
- package/lib/commonjs/BBOutstreamView.js.map +1 -1
- package/lib/commonjs/BBPlayerView.js +0 -1
- package/lib/commonjs/BBPlayerView.js.map +1 -1
- package/lib/commonjs/NativeCommands.js +24 -24
- package/lib/commonjs/NativeCommands.js.map +1 -1
- package/lib/commonjs/index.js +9 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/specs/NativeBBPlayerModule.js.map +1 -1
- package/lib/module/BBModalPlayer.js +17 -0
- package/lib/module/BBModalPlayer.js.map +1 -0
- package/lib/module/BBOutstreamView.js +0 -1
- package/lib/module/BBOutstreamView.js.map +1 -1
- package/lib/module/BBPlayerView.js +0 -1
- package/lib/module/BBPlayerView.js.map +1 -1
- package/lib/module/NativeCommands.js +24 -24
- package/lib/module/NativeCommands.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/specs/NativeBBPlayerModule.js.map +1 -1
- package/lib/typescript/src/BBModalPlayer.d.ts +13 -0
- package/lib/typescript/src/BBModalPlayer.d.ts.map +1 -0
- package/lib/typescript/src/BBOutstreamView.d.ts.map +1 -1
- package/lib/typescript/src/BBPlayer.types.d.ts +24 -20
- package/lib/typescript/src/BBPlayer.types.d.ts.map +1 -1
- package/lib/typescript/src/BBPlayerView.d.ts +0 -2
- package/lib/typescript/src/BBPlayerView.d.ts.map +1 -1
- package/lib/typescript/src/NativeCommands.d.ts +8 -9
- package/lib/typescript/src/NativeCommands.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeBBPlayerModule.d.ts +13 -8
- package/lib/typescript/src/specs/NativeBBPlayerModule.d.ts.map +1 -1
- package/package.json +8 -11
- package/src/BBModalPlayer.ts +32 -0
- package/src/BBOutstreamView.tsx +0 -1
- package/src/BBPlayer.types.ts +31 -17
- package/src/BBPlayerView.tsx +0 -12
- package/src/NativeCommands.ts +37 -26
- package/src/index.ts +2 -0
- package/src/specs/NativeBBPlayerModule.ts +25 -8
- package/android/proguard-rules.pro +0 -59
package/ios/BBPlayerModule.swift
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import React
|
|
3
|
+
import BBNativePlayerKit
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Global registry for BBPlayerView instances.
|
|
@@ -39,21 +40,44 @@ class BBPlayerViewRegistry: NSObject {
|
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
42
|
* Native Module for BBPlayer commands.
|
|
43
|
+
* Extends RCTEventEmitter to support module-level events (modal player).
|
|
42
44
|
* This module looks up BBPlayerView instances by their React tag and dispatches commands to them.
|
|
43
45
|
*/
|
|
44
46
|
@objc(BBPlayerModule)
|
|
45
|
-
class BBPlayerModule:
|
|
47
|
+
class BBPlayerModule: RCTEventEmitter {
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
private var modalPlayerView: BBNativePlayerView?
|
|
50
|
+
private var modalDelegate: ModalPlayerDelegate?
|
|
51
|
+
private var hasListeners = false
|
|
48
52
|
|
|
49
|
-
@objc static func requiresMainQueueSetup() -> Bool {
|
|
53
|
+
@objc override static func requiresMainQueueSetup() -> Bool {
|
|
50
54
|
return true
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
@objc static func moduleName() -> String {
|
|
57
|
+
@objc override static func moduleName() -> String! {
|
|
54
58
|
return "BBPlayerModule"
|
|
55
59
|
}
|
|
56
60
|
|
|
61
|
+
override func supportedEvents() -> [String]! {
|
|
62
|
+
return [
|
|
63
|
+
"modalPlayerDismissed",
|
|
64
|
+
"modalPlayerPlay",
|
|
65
|
+
"modalPlayerPause",
|
|
66
|
+
"modalPlayerEnded",
|
|
67
|
+
"modalPlayerError",
|
|
68
|
+
"modalPlayerApiReady",
|
|
69
|
+
"modalPlayerCanPlay",
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override func startObserving() {
|
|
74
|
+
hasListeners = true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override func stopObserving() {
|
|
78
|
+
hasListeners = false
|
|
79
|
+
}
|
|
80
|
+
|
|
57
81
|
// MARK: - Helper to get view by tag
|
|
58
82
|
|
|
59
83
|
private func getView(_ reactTag: NSNumber) -> BBPlayerView? {
|
|
@@ -159,50 +183,50 @@ class BBPlayerModule: NSObject {
|
|
|
159
183
|
}
|
|
160
184
|
}
|
|
161
185
|
|
|
162
|
-
@objc func loadWithClipId(_ viewTag: NSNumber, clipId: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?) {
|
|
186
|
+
@objc func loadWithClipId(_ viewTag: NSNumber, clipId: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?, contextJson: String?) {
|
|
163
187
|
DispatchQueue.main.async {
|
|
164
|
-
self.getView(viewTag)?.loadWithClipId(clipId ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue)
|
|
188
|
+
self.getView(viewTag)?.loadWithClipId(clipId ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue, contextJson: contextJson)
|
|
165
189
|
}
|
|
166
190
|
}
|
|
167
191
|
|
|
168
|
-
@objc func loadWithClipListId(_ viewTag: NSNumber, clipListId: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?) {
|
|
192
|
+
@objc func loadWithClipListId(_ viewTag: NSNumber, clipListId: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?, contextJson: String?) {
|
|
169
193
|
DispatchQueue.main.async {
|
|
170
|
-
self.getView(viewTag)?.loadWithClipListId(clipListId ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue)
|
|
194
|
+
self.getView(viewTag)?.loadWithClipListId(clipListId ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue, contextJson: contextJson)
|
|
171
195
|
}
|
|
172
196
|
}
|
|
173
197
|
|
|
174
|
-
@objc func loadWithProjectId(_ viewTag: NSNumber, projectId: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?) {
|
|
198
|
+
@objc func loadWithProjectId(_ viewTag: NSNumber, projectId: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?, contextJson: String?) {
|
|
175
199
|
DispatchQueue.main.async {
|
|
176
|
-
self.getView(viewTag)?.loadWithProjectId(projectId ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue)
|
|
200
|
+
self.getView(viewTag)?.loadWithProjectId(projectId ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue, contextJson: contextJson)
|
|
177
201
|
}
|
|
178
202
|
}
|
|
179
203
|
|
|
180
|
-
@objc func loadWithClipJson(_ viewTag: NSNumber, clipJson: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?) {
|
|
204
|
+
@objc func loadWithClipJson(_ viewTag: NSNumber, clipJson: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?, contextJson: String?) {
|
|
181
205
|
DispatchQueue.main.async {
|
|
182
|
-
self.getView(viewTag)?.loadWithClipJson(clipJson ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue)
|
|
206
|
+
self.getView(viewTag)?.loadWithClipJson(clipJson ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue, contextJson: contextJson)
|
|
183
207
|
}
|
|
184
208
|
}
|
|
185
209
|
|
|
186
|
-
@objc func loadWithClipListJson(_ viewTag: NSNumber, clipListJson: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?) {
|
|
210
|
+
@objc func loadWithClipListJson(_ viewTag: NSNumber, clipListJson: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?, contextJson: String?) {
|
|
187
211
|
DispatchQueue.main.async {
|
|
188
|
-
self.getView(viewTag)?.loadWithClipListJson(clipListJson ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue)
|
|
212
|
+
self.getView(viewTag)?.loadWithClipListJson(clipListJson ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue, contextJson: contextJson)
|
|
189
213
|
}
|
|
190
214
|
}
|
|
191
215
|
|
|
192
|
-
@objc func loadWithProjectJson(_ viewTag: NSNumber, projectJson: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?) {
|
|
216
|
+
@objc func loadWithProjectJson(_ viewTag: NSNumber, projectJson: String?, initiator: String?, autoPlay: Bool, seekTo: NSNumber?, contextJson: String?) {
|
|
193
217
|
DispatchQueue.main.async {
|
|
194
|
-
self.getView(viewTag)?.loadWithProjectJson(projectJson ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue)
|
|
218
|
+
self.getView(viewTag)?.loadWithProjectJson(projectJson ?? "", initiator: initiator, autoPlay: autoPlay, seekTo: seekTo?.doubleValue, contextJson: contextJson)
|
|
195
219
|
}
|
|
196
220
|
}
|
|
197
221
|
|
|
198
|
-
@objc func loadWithJsonUrl(_ viewTag: NSNumber, jsonUrl: String?, autoPlay: Bool) {
|
|
199
|
-
NSLog("BBPlayerModule.loadWithJsonUrl called - viewTag: %@, jsonUrl: %@, autoPlay: %d", viewTag, jsonUrl ?? "nil", autoPlay)
|
|
222
|
+
@objc func loadWithJsonUrl(_ viewTag: NSNumber, jsonUrl: String?, autoPlay: Bool, contextJson: String?) {
|
|
223
|
+
NSLog("BBPlayerModule.loadWithJsonUrl called - viewTag: %@, jsonUrl: %@, autoPlay: %d, context: %@", viewTag, jsonUrl ?? "nil", autoPlay, contextJson ?? "nil")
|
|
200
224
|
DispatchQueue.main.async {
|
|
201
225
|
let view = self.getView(viewTag)
|
|
202
226
|
NSLog("BBPlayerModule.loadWithJsonUrl - view found: %@", view != nil ? "YES" : "NO")
|
|
203
227
|
if let view = view, let url = jsonUrl {
|
|
204
228
|
NSLog("BBPlayerModule.loadWithJsonUrl - calling view.loadWithJsonUrl with url: %@", url)
|
|
205
|
-
view.loadWithJsonUrl(url, autoPlay: autoPlay)
|
|
229
|
+
view.loadWithJsonUrl(url, autoPlay: autoPlay, contextJson: contextJson)
|
|
206
230
|
} else {
|
|
207
231
|
NSLog("BBPlayerModule.loadWithJsonUrl - FAILED: view=%@, url=%@", view != nil ? "found" : "nil", jsonUrl ?? "nil")
|
|
208
232
|
}
|
|
@@ -218,13 +242,6 @@ class BBPlayerModule: NSObject {
|
|
|
218
242
|
}
|
|
219
243
|
}
|
|
220
244
|
|
|
221
|
-
@objc func getCurrentTime(_ viewTag: NSNumber, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
222
|
-
DispatchQueue.main.async {
|
|
223
|
-
let currentTime = self.getView(viewTag)?.currentTime()
|
|
224
|
-
resolver(currentTime)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
245
|
@objc func getMuted(_ viewTag: NSNumber, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
229
246
|
DispatchQueue.main.async {
|
|
230
247
|
let muted = self.getView(viewTag)?.muted()
|
|
@@ -280,4 +297,99 @@ class BBPlayerModule: NSObject {
|
|
|
280
297
|
resolver(playoutData)
|
|
281
298
|
}
|
|
282
299
|
}
|
|
300
|
+
|
|
301
|
+
// MARK: - Modal Player API
|
|
302
|
+
|
|
303
|
+
@objc func presentModalPlayer(_ jsonUrl: String, optionsJson: String?) {
|
|
304
|
+
DispatchQueue.main.async {
|
|
305
|
+
guard let rootVC = RCTPresentedViewController() else {
|
|
306
|
+
NSLog("BBPlayerModule: No root view controller found")
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Parse options from JSON string
|
|
311
|
+
var options: [String: Any]? = nil
|
|
312
|
+
if let json = optionsJson, let data = json.data(using: .utf8) {
|
|
313
|
+
options = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Create modal player via native SDK
|
|
317
|
+
let playerView = BBNativePlayer.createModalPlayerView(
|
|
318
|
+
uiViewContoller: rootVC,
|
|
319
|
+
jsonUrl: jsonUrl,
|
|
320
|
+
options: options
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
// Set up delegate for event forwarding
|
|
324
|
+
let delegate = ModalPlayerDelegate(module: self)
|
|
325
|
+
playerView.delegate = delegate
|
|
326
|
+
|
|
327
|
+
self.modalPlayerView = playerView
|
|
328
|
+
self.modalDelegate = delegate
|
|
329
|
+
|
|
330
|
+
NSLog("BBPlayerModule: Modal player presented with URL: %@", jsonUrl)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@objc func dismissModalPlayer() {
|
|
335
|
+
DispatchQueue.main.async {
|
|
336
|
+
self.modalPlayerView?.player.closeModalPlayer()
|
|
337
|
+
self.modalPlayerView = nil
|
|
338
|
+
self.modalDelegate = nil
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
@objc override func addListener(_ eventName: String) {
|
|
343
|
+
// Required for RCTEventEmitter
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@objc override func removeListeners(_ count: Double) {
|
|
347
|
+
// Required for RCTEventEmitter
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private func emitEvent(_ name: String, body: Any? = nil) {
|
|
351
|
+
if hasListeners {
|
|
352
|
+
sendEvent(withName: name, body: body)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// MARK: - Modal Player Delegate
|
|
357
|
+
|
|
358
|
+
private class ModalPlayerDelegate: NSObject, BBNativePlayerViewDelegate {
|
|
359
|
+
weak var module: BBPlayerModule?
|
|
360
|
+
|
|
361
|
+
init(module: BBPlayerModule) {
|
|
362
|
+
self.module = module
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
func bbNativePlayerView(didTriggerPlay playerView: BBNativePlayerView) {
|
|
366
|
+
module?.emitEvent("modalPlayerPlay")
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
func bbNativePlayerView(didTriggerPause playerView: BBNativePlayerView) {
|
|
370
|
+
module?.emitEvent("modalPlayerPause")
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
func bbNativePlayerView(didTriggerEnded playerView: BBNativePlayerView) {
|
|
374
|
+
module?.emitEvent("modalPlayerEnded")
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func bbNativePlayerView(playerView: BBNativePlayerView, didFailWithError error: String?) {
|
|
378
|
+
module?.emitEvent("modalPlayerError", body: ["error": error ?? "Unknown error"])
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
func bbNativePlayerView(didTriggerApiReady playerView: BBNativePlayerView) {
|
|
382
|
+
module?.emitEvent("modalPlayerApiReady")
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
func bbNativePlayerView(didTriggerCanPlay playerView: BBNativePlayerView) {
|
|
386
|
+
module?.emitEvent("modalPlayerCanPlay")
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
func bbNativePlayerView(didCloseModalPlayer playerView: BBNativePlayerView) {
|
|
390
|
+
module?.emitEvent("modalPlayerDismissed")
|
|
391
|
+
module?.modalPlayerView = nil
|
|
392
|
+
module?.modalDelegate = nil
|
|
393
|
+
}
|
|
394
|
+
}
|
|
283
395
|
}
|
package/ios/BBPlayerView.swift
CHANGED
|
@@ -42,16 +42,9 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
42
42
|
private var playerView: BBNativePlayerView?
|
|
43
43
|
private var hasSetup: Bool = false
|
|
44
44
|
|
|
45
|
-
// Timer for periodic time updates (opt-in for performance)
|
|
46
|
-
private var timeUpdateTimer: Timer?
|
|
47
45
|
private var isPlaying: Bool = false
|
|
48
46
|
private var currentDuration: Double = 0.0
|
|
49
|
-
private var lastKnownTime: Double = 0.0
|
|
50
|
-
private var playbackStartTimestamp: CFTimeInterval = 0
|
|
51
|
-
private var lastEmittedTime: Double = 0.0
|
|
52
47
|
private var isInFullscreen: Bool = false
|
|
53
|
-
private var backgroundObserver: NSObjectProtocol?
|
|
54
|
-
private var foregroundObserver: NSObjectProtocol?
|
|
55
48
|
// Independent Google Cast button for showing the cast picker
|
|
56
49
|
private var independentCastButton: GCKUICastButton?
|
|
57
50
|
// Store parent view controller reference for SDK
|
|
@@ -71,18 +64,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
71
64
|
}
|
|
72
65
|
}
|
|
73
66
|
|
|
74
|
-
@objc var enableTimeUpdates: Bool = false {
|
|
75
|
-
didSet {
|
|
76
|
-
log("Time updates \(enableTimeUpdates ? "enabled" : "disabled")")
|
|
77
|
-
|
|
78
|
-
if !enableTimeUpdates && timeUpdateTimer != nil {
|
|
79
|
-
stopTimeUpdates()
|
|
80
|
-
} else if enableTimeUpdates && isPlaying && timeUpdateTimer == nil {
|
|
81
|
-
startTimeUpdates()
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
67
|
// MARK: - Event handlers (RCTDirectEventBlock)
|
|
87
68
|
|
|
88
69
|
@objc var onDidFailWithError: RCTDirectEventBlock?
|
|
@@ -123,7 +104,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
123
104
|
@objc var onDidTriggerViewFinished: RCTDirectEventBlock?
|
|
124
105
|
@objc var onDidTriggerViewStarted: RCTDirectEventBlock?
|
|
125
106
|
@objc var onDidTriggerVolumeChange: RCTDirectEventBlock?
|
|
126
|
-
@objc var onDidTriggerTimeUpdate: RCTDirectEventBlock?
|
|
127
107
|
@objc var onDidTriggerApiReady: RCTDirectEventBlock?
|
|
128
108
|
|
|
129
109
|
override var intrinsicContentSize: CGSize {
|
|
@@ -153,8 +133,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
153
133
|
BBPlayerViewRegistry.shared.register(self, tag: tag.intValue)
|
|
154
134
|
}
|
|
155
135
|
|
|
156
|
-
setupAppLifecycleObservers()
|
|
157
|
-
|
|
158
136
|
// Find the parent view controller from the responder chain
|
|
159
137
|
var responder = self.next
|
|
160
138
|
while responder != nil {
|
|
@@ -180,91 +158,19 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
180
158
|
BBPlayerViewRegistry.shared.unregister(tag: tag.intValue)
|
|
181
159
|
}
|
|
182
160
|
|
|
183
|
-
|
|
184
|
-
stopTimeUpdates()
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Start periodic time updates (1x per second, only if enabled)
|
|
190
|
-
private func startTimeUpdates() {
|
|
191
|
-
guard enableTimeUpdates, timeUpdateTimer == nil else { return }
|
|
192
|
-
|
|
193
|
-
timeUpdateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
|
194
|
-
guard let self = self, self.isPlaying else { return }
|
|
195
|
-
|
|
196
|
-
let elapsedSeconds = CACurrentMediaTime() - self.playbackStartTimestamp
|
|
197
|
-
let estimatedTime = self.lastKnownTime + elapsedSeconds
|
|
198
|
-
let currentTime = min(estimatedTime, self.currentDuration)
|
|
199
|
-
|
|
200
|
-
if self.currentDuration > 0 && abs(currentTime - self.lastEmittedTime) >= 0.5 {
|
|
201
|
-
self.lastEmittedTime = currentTime
|
|
202
|
-
self.onDidTriggerTimeUpdate?([
|
|
203
|
-
"currentTime": currentTime,
|
|
204
|
-
"duration": self.currentDuration
|
|
205
|
-
])
|
|
206
|
-
}
|
|
161
|
+
// No cleanup needed when not in fullscreen
|
|
207
162
|
}
|
|
208
163
|
}
|
|
209
164
|
|
|
210
|
-
private func stopTimeUpdates() {
|
|
211
|
-
timeUpdateTimer?.invalidate()
|
|
212
|
-
timeUpdateTimer = nil
|
|
213
|
-
}
|
|
214
|
-
|
|
215
165
|
deinit {
|
|
216
166
|
// Unregister from view registry
|
|
217
167
|
if let tag = self.reactTag {
|
|
218
168
|
BBPlayerViewRegistry.shared.unregister(tag: tag.intValue)
|
|
219
169
|
}
|
|
220
|
-
stopTimeUpdates()
|
|
221
|
-
removeAppLifecycleObservers()
|
|
222
170
|
independentCastButton?.removeFromSuperview()
|
|
223
171
|
independentCastButton = nil
|
|
224
172
|
}
|
|
225
173
|
|
|
226
|
-
// MARK: - App Lifecycle Management
|
|
227
|
-
|
|
228
|
-
private func setupAppLifecycleObservers() {
|
|
229
|
-
guard backgroundObserver == nil else { return }
|
|
230
|
-
|
|
231
|
-
backgroundObserver = NotificationCenter.default.addObserver(
|
|
232
|
-
forName: UIApplication.didEnterBackgroundNotification,
|
|
233
|
-
object: nil,
|
|
234
|
-
queue: .main
|
|
235
|
-
) { [weak self] _ in
|
|
236
|
-
guard let self = self else { return }
|
|
237
|
-
if self.isPlaying {
|
|
238
|
-
self.lastKnownTime = self.calculateCurrentTime()
|
|
239
|
-
}
|
|
240
|
-
self.stopTimeUpdates()
|
|
241
|
-
log("Timer paused - app entered background", level: .debug)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
foregroundObserver = NotificationCenter.default.addObserver(
|
|
245
|
-
forName: UIApplication.willEnterForegroundNotification,
|
|
246
|
-
object: nil,
|
|
247
|
-
queue: .main
|
|
248
|
-
) { [weak self] _ in
|
|
249
|
-
guard let self = self else { return }
|
|
250
|
-
if self.isPlaying && self.enableTimeUpdates {
|
|
251
|
-
self.playbackStartTimestamp = CACurrentMediaTime()
|
|
252
|
-
self.startTimeUpdates()
|
|
253
|
-
log("Timer resumed - app entered foreground", level: .debug)
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
private func removeAppLifecycleObservers() {
|
|
259
|
-
if let observer = backgroundObserver {
|
|
260
|
-
NotificationCenter.default.removeObserver(observer)
|
|
261
|
-
backgroundObserver = nil
|
|
262
|
-
}
|
|
263
|
-
if let observer = foregroundObserver {
|
|
264
|
-
NotificationCenter.default.removeObserver(observer)
|
|
265
|
-
foregroundObserver = nil
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
174
|
|
|
269
175
|
// MARK: - Player Setup (Simplified - no intermediate view controller)
|
|
270
176
|
|
|
@@ -409,7 +315,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
409
315
|
|
|
410
316
|
func bbNativePlayerView(didTriggerEnded playerView: BBNativePlayerView) {
|
|
411
317
|
isPlaying = false
|
|
412
|
-
stopTimeUpdates()
|
|
413
318
|
onDidTriggerEnded?([:])
|
|
414
319
|
}
|
|
415
320
|
|
|
@@ -451,7 +356,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
451
356
|
|
|
452
357
|
func bbNativePlayerView(didTriggerPause playerView: BBNativePlayerView) {
|
|
453
358
|
isPlaying = false
|
|
454
|
-
stopTimeUpdates()
|
|
455
359
|
onDidTriggerPause?([:])
|
|
456
360
|
}
|
|
457
361
|
|
|
@@ -465,10 +369,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
465
369
|
|
|
466
370
|
func bbNativePlayerView(didTriggerPlaying playerView: BBNativePlayerView) {
|
|
467
371
|
isPlaying = true
|
|
468
|
-
playbackStartTimestamp = CACurrentMediaTime()
|
|
469
|
-
lastEmittedTime = 0.0
|
|
470
|
-
lastKnownTime = calculateCurrentTime()
|
|
471
|
-
startTimeUpdates()
|
|
472
372
|
onDidTriggerPlaying?([:])
|
|
473
373
|
}
|
|
474
374
|
|
|
@@ -500,9 +400,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
500
400
|
}
|
|
501
401
|
|
|
502
402
|
func bbNativePlayerView(playerView: BBNativePlayerView, didTriggerSeeked seekOffset: Double) {
|
|
503
|
-
lastKnownTime = seekOffset
|
|
504
|
-
playbackStartTimestamp = CACurrentMediaTime()
|
|
505
|
-
lastEmittedTime = 0.0
|
|
506
403
|
onDidTriggerSeeked?(["payload": seekOffset as Any])
|
|
507
404
|
}
|
|
508
405
|
|
|
@@ -557,18 +454,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
557
454
|
addSubview(castButton)
|
|
558
455
|
}
|
|
559
456
|
|
|
560
|
-
// MARK: - Private Helper Methods
|
|
561
|
-
|
|
562
|
-
private func calculateCurrentTime() -> Double {
|
|
563
|
-
if isPlaying && playbackStartTimestamp > 0 {
|
|
564
|
-
let elapsedSeconds = CACurrentMediaTime() - playbackStartTimestamp
|
|
565
|
-
let estimatedTime = lastKnownTime + elapsedSeconds
|
|
566
|
-
return min(estimatedTime, currentDuration)
|
|
567
|
-
} else {
|
|
568
|
-
return lastKnownTime
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
457
|
// MARK: - Public API Methods
|
|
573
458
|
|
|
574
459
|
func adMediaHeight() -> Int? {
|
|
@@ -587,10 +472,6 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
587
472
|
return nil
|
|
588
473
|
}
|
|
589
474
|
|
|
590
|
-
func currentTime() -> Double? {
|
|
591
|
-
return calculateCurrentTime()
|
|
592
|
-
}
|
|
593
|
-
|
|
594
475
|
func duration() -> Double? {
|
|
595
476
|
return playerView?.player.duration
|
|
596
477
|
}
|
|
@@ -691,9 +572,7 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
691
572
|
}
|
|
692
573
|
|
|
693
574
|
func seekRelative(_ offsetInSeconds: Double) {
|
|
694
|
-
|
|
695
|
-
let newPosition = max(0, min(currentDuration, currentTime + offsetInSeconds))
|
|
696
|
-
playerView?.player.seek(offsetInSeconds: newPosition as NSNumber)
|
|
575
|
+
playerView?.player.seekRelative(offsetInSeconds: offsetInSeconds as NSNumber)
|
|
697
576
|
}
|
|
698
577
|
|
|
699
578
|
func setMuted(_ muted: Bool) {
|
|
@@ -704,28 +583,48 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
704
583
|
playerView?.setApiProperty(property: .volume, value: Float(volume))
|
|
705
584
|
}
|
|
706
585
|
|
|
707
|
-
|
|
708
|
-
|
|
586
|
+
// Helper to parse context JSON into a dictionary for the native SDK
|
|
587
|
+
private func parseContext(_ contextJson: String?) -> [String: Any]? {
|
|
588
|
+
guard let jsonString = contextJson, !jsonString.isEmpty else { return nil }
|
|
589
|
+
guard let data = jsonString.data(using: .utf8) else { return nil }
|
|
590
|
+
do {
|
|
591
|
+
if let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
592
|
+
return dict
|
|
593
|
+
}
|
|
594
|
+
} catch {
|
|
595
|
+
log("Failed to parse context JSON: \(error)", level: .warning)
|
|
596
|
+
}
|
|
597
|
+
return nil
|
|
709
598
|
}
|
|
710
599
|
|
|
711
|
-
func
|
|
712
|
-
|
|
600
|
+
func loadWithClipId(_ clipId: String, initiator: String?, autoPlay: Bool?, seekTo: Double?, contextJson: String? = nil) {
|
|
601
|
+
let context = parseContext(contextJson)
|
|
602
|
+
playerView?.player.loadWithClipId(clipId: clipId, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?, context: context)
|
|
713
603
|
}
|
|
714
604
|
|
|
715
|
-
func
|
|
716
|
-
|
|
605
|
+
func loadWithClipListId(_ clipListId: String, initiator: String?, autoPlay: Bool?, seekTo: Double?, contextJson: String? = nil) {
|
|
606
|
+
let context = parseContext(contextJson)
|
|
607
|
+
playerView?.player.loadWithClipListId(clipListId: clipListId, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?, context: context)
|
|
717
608
|
}
|
|
718
609
|
|
|
719
|
-
func
|
|
720
|
-
|
|
610
|
+
func loadWithProjectId(_ projectId: String, initiator: String?, autoPlay: Bool?, seekTo: Double?, contextJson: String? = nil) {
|
|
611
|
+
let context = parseContext(contextJson)
|
|
612
|
+
playerView?.player.loadWithProjectId(projectId: projectId, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?, context: context)
|
|
721
613
|
}
|
|
722
614
|
|
|
723
|
-
func
|
|
724
|
-
|
|
615
|
+
func loadWithClipJson(_ clipJson: String, initiator: String?, autoPlay: Bool?, seekTo: Double?, contextJson: String? = nil) {
|
|
616
|
+
let context = parseContext(contextJson)
|
|
617
|
+
playerView?.player.loadWithClipJson(clipJson: clipJson, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?, context: context)
|
|
725
618
|
}
|
|
726
619
|
|
|
727
|
-
func
|
|
728
|
-
|
|
620
|
+
func loadWithClipListJson(_ clipListJson: String, initiator: String?, autoPlay: Bool?, seekTo: Double?, contextJson: String? = nil) {
|
|
621
|
+
let context = parseContext(contextJson)
|
|
622
|
+
playerView?.player.loadWithClipListJson(clipListJson: clipListJson, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?, context: context)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
func loadWithProjectJson(_ projectJson: String, initiator: String?, autoPlay: Bool?, seekTo: Double?, contextJson: String? = nil) {
|
|
626
|
+
let context = parseContext(contextJson)
|
|
627
|
+
playerView?.player.loadWithProjectJson(projectJson: projectJson, initiator: initiator, autoPlay: autoPlay, seekTo: seekTo as NSNumber?, context: context)
|
|
729
628
|
}
|
|
730
629
|
|
|
731
630
|
/**
|
|
@@ -735,8 +634,8 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
735
634
|
*
|
|
736
635
|
* Note: Shorts URLs (/sh/{id}.json) are NOT supported here - use BBShortsView instead.
|
|
737
636
|
*/
|
|
738
|
-
func loadWithJsonUrl(_ url: String, autoPlay: Bool) {
|
|
739
|
-
NSLog("BBPlayerView.loadWithJsonUrl called - url: %@, autoPlay: %d", url, autoPlay)
|
|
637
|
+
func loadWithJsonUrl(_ url: String, autoPlay: Bool, contextJson: String? = nil) {
|
|
638
|
+
NSLog("BBPlayerView.loadWithJsonUrl called - url: %@, autoPlay: %d, context: %@", url, autoPlay, contextJson ?? "nil")
|
|
740
639
|
guard playerView != nil else {
|
|
741
640
|
NSLog("BBPlayerView.loadWithJsonUrl ERROR - playerView not initialized")
|
|
742
641
|
return
|
|
@@ -754,6 +653,8 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
754
653
|
let projectIdPattern = "/pj/([0-9]+)\\.json|/project/([0-9]+)"
|
|
755
654
|
let shortsIdPattern = "/sh/([0-9]+)\\.json"
|
|
756
655
|
|
|
656
|
+
let context = parseContext(contextJson)
|
|
657
|
+
|
|
757
658
|
if let shortsMatch = url.range(of: shortsIdPattern, options: .regularExpression) {
|
|
758
659
|
// Shorts require a separate BBShortsView component
|
|
759
660
|
log("ERROR - Shorts URLs are not supported in BBPlayerView. Use BBShortsView instead.", level: .error)
|
|
@@ -765,7 +666,7 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
765
666
|
// Extract the cliplist ID
|
|
766
667
|
if let clipListId = extractIdFromUrl(url, pattern: clipListIdPattern) {
|
|
767
668
|
NSLog("BBPlayerView.loadWithJsonUrl - Loading ClipList by ID: %@", clipListId)
|
|
768
|
-
playerView?.player.loadWithClipListId(clipListId: clipListId, initiator: "external", autoPlay: autoPlay, seekTo: nil)
|
|
669
|
+
playerView?.player.loadWithClipListId(clipListId: clipListId, initiator: "external", autoPlay: autoPlay, seekTo: nil, context: context)
|
|
769
670
|
} else {
|
|
770
671
|
NSLog("BBPlayerView.loadWithJsonUrl ERROR - Failed to extract cliplist ID from URL: %@", url)
|
|
771
672
|
}
|
|
@@ -776,7 +677,7 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
776
677
|
// Extract the project ID
|
|
777
678
|
if let projectId = extractIdFromUrl(url, pattern: projectIdPattern) {
|
|
778
679
|
NSLog("BBPlayerView.loadWithJsonUrl - Loading Project by ID: %@", projectId)
|
|
779
|
-
playerView?.player.loadWithProjectId(projectId: projectId, initiator: "external", autoPlay: autoPlay, seekTo: nil)
|
|
680
|
+
playerView?.player.loadWithProjectId(projectId: projectId, initiator: "external", autoPlay: autoPlay, seekTo: nil, context: context)
|
|
780
681
|
} else {
|
|
781
682
|
NSLog("BBPlayerView.loadWithJsonUrl ERROR - Failed to extract project ID from URL: %@", url)
|
|
782
683
|
}
|
|
@@ -787,7 +688,7 @@ class BBPlayerView: UIView, BBNativePlayerViewDelegate {
|
|
|
787
688
|
// Extract the clip ID
|
|
788
689
|
if let clipId = extractIdFromUrl(url, pattern: clipIdPattern) {
|
|
789
690
|
NSLog("BBPlayerView.loadWithJsonUrl - Loading Clip by ID: %@", clipId)
|
|
790
|
-
playerView?.player.loadWithClipId(clipId: clipId, initiator: "external", autoPlay: autoPlay, seekTo: nil)
|
|
691
|
+
playerView?.player.loadWithClipId(clipId: clipId, initiator: "external", autoPlay: autoPlay, seekTo: nil, context: context)
|
|
791
692
|
} else {
|
|
792
693
|
NSLog("BBPlayerView.loadWithJsonUrl ERROR - Failed to extract clip ID from URL: %@", url)
|
|
793
694
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
// Props
|
|
8
8
|
RCT_EXPORT_VIEW_PROPERTY(jsonUrl, NSString)
|
|
9
9
|
RCT_EXPORT_VIEW_PROPERTY(options, NSDictionary)
|
|
10
|
-
RCT_EXPORT_VIEW_PROPERTY(enableTimeUpdates, BOOL)
|
|
11
10
|
|
|
12
11
|
// Event handlers
|
|
13
12
|
RCT_EXPORT_VIEW_PROPERTY(onDidFailWithError, RCTDirectEventBlock)
|
|
@@ -48,7 +47,6 @@ RCT_EXPORT_VIEW_PROPERTY(onDidTriggerStateChange, RCTDirectEventBlock)
|
|
|
48
47
|
RCT_EXPORT_VIEW_PROPERTY(onDidTriggerViewFinished, RCTDirectEventBlock)
|
|
49
48
|
RCT_EXPORT_VIEW_PROPERTY(onDidTriggerViewStarted, RCTDirectEventBlock)
|
|
50
49
|
RCT_EXPORT_VIEW_PROPERTY(onDidTriggerVolumeChange, RCTDirectEventBlock)
|
|
51
|
-
RCT_EXPORT_VIEW_PROPERTY(onDidTriggerTimeUpdate, RCTDirectEventBlock)
|
|
52
50
|
RCT_EXPORT_VIEW_PROPERTY(onDidTriggerApiReady, RCTDirectEventBlock)
|
|
53
51
|
|
|
54
52
|
// Commands (methods callable from JS)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BBModalPlayer = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
const BBPlayerModule = _reactNative.NativeModules.BBPlayerModule;
|
|
9
|
+
const eventEmitter = new _reactNative.NativeEventEmitter(BBPlayerModule);
|
|
10
|
+
const BBModalPlayer = exports.BBModalPlayer = {
|
|
11
|
+
present(jsonUrl, options) {
|
|
12
|
+
BBPlayerModule?.presentModalPlayer(jsonUrl, options ? JSON.stringify(options) : null);
|
|
13
|
+
},
|
|
14
|
+
dismiss() {
|
|
15
|
+
BBPlayerModule?.dismissModalPlayer();
|
|
16
|
+
},
|
|
17
|
+
addEventListener(event, callback) {
|
|
18
|
+
return eventEmitter.addListener(event, callback);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=BBModalPlayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","BBPlayerModule","NativeModules","eventEmitter","NativeEventEmitter","BBModalPlayer","exports","present","jsonUrl","options","presentModalPlayer","JSON","stringify","dismiss","dismissModalPlayer","addEventListener","event","callback","addListener"],"sourceRoot":"../../src","sources":["BBModalPlayer.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,cAAc,GAAGC,0BAAa,CAACD,cAAc;AACnD,MAAME,YAAY,GAAG,IAAIC,+BAAkB,CAACH,cAAc,CAAC;AAUpD,MAAMI,aAAa,GAAAC,OAAA,CAAAD,aAAA,GAAG;EAC3BE,OAAOA,CAACC,OAAe,EAAEC,OAA4B,EAAE;IACrDR,cAAc,EAAES,kBAAkB,CAChCF,OAAO,EACPC,OAAO,GAAGE,IAAI,CAACC,SAAS,CAACH,OAAO,CAAC,GAAG,IACtC,CAAC;EACH,CAAC;EAEDI,OAAOA,CAAA,EAAG;IACRZ,cAAc,EAAEa,kBAAkB,CAAC,CAAC;EACtC,CAAC;EAEDC,gBAAgBA,CACdC,KAAa,EACbC,QAAkC,EAClC;IACA,OAAOd,YAAY,CAACe,WAAW,CAACF,KAAK,EAAEC,QAAQ,CAAC;EAClD;AACF,CAAC","ignoreList":[]}
|
|
@@ -171,7 +171,6 @@ const BBOutstreamView = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
|
171
171
|
loadWithProjectJson: (...args) => playerRef.current?.loadWithProjectJson(...args),
|
|
172
172
|
loadWithJsonUrl: (...args) => playerRef.current?.loadWithJsonUrl(...args),
|
|
173
173
|
getDuration: () => playerRef.current?.getDuration() ?? Promise.resolve(null),
|
|
174
|
-
getCurrentTime: () => playerRef.current?.getCurrentTime() ?? Promise.resolve(null),
|
|
175
174
|
getMuted: () => playerRef.current?.getMuted() ?? Promise.resolve(null),
|
|
176
175
|
getVolume: () => playerRef.current?.getVolume() ?? Promise.resolve(null),
|
|
177
176
|
getPhase: () => playerRef.current?.getPhase() ?? Promise.resolve(null),
|