react-native-tpstreams 1.1.0 → 1.1.1
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.
|
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
|
|
|
21
21
|
'DEFINES_MODULE' => 'YES',
|
|
22
22
|
'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES'
|
|
23
23
|
}
|
|
24
|
-
s.dependency "TPStreamsSDK" , "1.2.
|
|
24
|
+
s.dependency "TPStreamsSDK" , "1.2.14"
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
# Ensure the module is not built as a framework to avoid bridging header conflicts
|
|
@@ -19,7 +19,18 @@ class TPStreamsModule: NSObject {
|
|
|
19
19
|
|
|
20
20
|
private func initializeDownloadModule() {
|
|
21
21
|
_ = TPStreamsDownloadModule.shared
|
|
22
|
+
ensureDownloadModuleInitialized()
|
|
22
23
|
}
|
|
24
|
+
|
|
25
|
+
private func ensureDownloadModuleInitialized() {
|
|
26
|
+
guard let bridge = RCTBridge.current() else {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if let module = bridge.module(for: TPStreamsDownloadModule.self) as? TPStreamsDownloadModule {
|
|
31
|
+
module.initializeModule()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
23
34
|
|
|
24
35
|
@objc
|
|
25
36
|
static func requiresMainQueueSetup() -> Bool {
|
|
@@ -24,7 +24,15 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
24
24
|
private var playbackSpeedObserver: NSKeyValueObservation?
|
|
25
25
|
private var timeControlStatusObserver: NSKeyValueObservation?
|
|
26
26
|
private var playerStateObserver: NSKeyValueObservation?
|
|
27
|
+
private lazy var loadingIndicator: UIActivityIndicatorView = {
|
|
28
|
+
let indicator = UIActivityIndicatorView(style: .large)
|
|
29
|
+
indicator.hidesWhenStopped = true
|
|
30
|
+
indicator.color = .white
|
|
31
|
+
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
32
|
+
return indicator
|
|
33
|
+
}()
|
|
27
34
|
private var setupScheduled = false
|
|
35
|
+
private var isPlayerReady = false
|
|
28
36
|
private var pendingOfflineCredentialsCompletion: ((String?, Double) -> Void)?
|
|
29
37
|
private var _offlineLicenseExpireTime: Double = TPStreamsRNPlayerView.maxOfflineLicenseDuration
|
|
30
38
|
|
|
@@ -57,11 +65,13 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
57
65
|
override init(frame: CGRect) {
|
|
58
66
|
super.init(frame: frame)
|
|
59
67
|
backgroundColor = .black
|
|
68
|
+
setupLoadingIndicator()
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
required init?(coder: NSCoder) {
|
|
63
72
|
super.init(coder: coder)
|
|
64
73
|
backgroundColor = .black
|
|
74
|
+
setupLoadingIndicator()
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
override func layoutSubviews() {
|
|
@@ -85,6 +95,7 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
85
95
|
|
|
86
96
|
private func setupPlayer() {
|
|
87
97
|
cleanupPlayer()
|
|
98
|
+
isPlayerReady = false
|
|
88
99
|
|
|
89
100
|
player = TPStreamsDownloadManager.shared.isAssetDownloaded(assetID: videoId as String)
|
|
90
101
|
? createOfflinePlayer()
|
|
@@ -95,6 +106,7 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
95
106
|
setupScheduled = false
|
|
96
107
|
return
|
|
97
108
|
}
|
|
109
|
+
showLoadingIndicator()
|
|
98
110
|
setupTokenDelegate()
|
|
99
111
|
configurePlayerView()
|
|
100
112
|
observePlayerChanges()
|
|
@@ -199,6 +211,7 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
199
211
|
playerVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
200
212
|
playerVC.view.isHidden = false
|
|
201
213
|
bringSubviewToFront(playerVC.view)
|
|
214
|
+
bringSubviewToFront(loadingIndicator)
|
|
202
215
|
playerVC.didMove(toParent: parentVC)
|
|
203
216
|
}
|
|
204
217
|
|
|
@@ -235,8 +248,17 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
235
248
|
|
|
236
249
|
timeControlStatusObserver = player.observe(\.timeControlStatus, options: [.new, .initial]) { [weak self] player, _ in
|
|
237
250
|
DispatchQueue.main.async {
|
|
238
|
-
let
|
|
239
|
-
|
|
251
|
+
guard let self = self else { return }
|
|
252
|
+
let timeStatus = player.timeControlStatus
|
|
253
|
+
let isPlaying = timeStatus == .playing
|
|
254
|
+
|
|
255
|
+
// Show loader while buffering; hide only after player is ready.
|
|
256
|
+
if timeStatus == .waitingToPlayAtSpecifiedRate {
|
|
257
|
+
self.showLoadingIndicator()
|
|
258
|
+
} else if self.isPlayerReady {
|
|
259
|
+
self.hideLoadingIndicator()
|
|
260
|
+
}
|
|
261
|
+
self.onIsPlayingChanged?(["isPlaying": isPlaying])
|
|
240
262
|
}
|
|
241
263
|
}
|
|
242
264
|
}
|
|
@@ -246,8 +268,25 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
246
268
|
|
|
247
269
|
playerStateObserver = player.observe(\.status, options: [.new, .initial]) { [weak self] player, _ in
|
|
248
270
|
DispatchQueue.main.async {
|
|
249
|
-
let
|
|
250
|
-
|
|
271
|
+
guard let self = self else { return }
|
|
272
|
+
let status = player.status
|
|
273
|
+
let state = self.mapPlayerStateToAndroid(status) ?? PlaybackState.idle.rawValue
|
|
274
|
+
self.onPlayerStateChanged?(["playbackState": state])
|
|
275
|
+
|
|
276
|
+
// Drive readiness and loader from AVPlayer.status.
|
|
277
|
+
switch status {
|
|
278
|
+
case .readyToPlay:
|
|
279
|
+
self.isPlayerReady = true
|
|
280
|
+
if player.timeControlStatus != .waitingToPlayAtSpecifiedRate {
|
|
281
|
+
self.hideLoadingIndicator()
|
|
282
|
+
}
|
|
283
|
+
case .failed:
|
|
284
|
+
self.isPlayerReady = false
|
|
285
|
+
self.hideLoadingIndicator()
|
|
286
|
+
default:
|
|
287
|
+
self.isPlayerReady = false
|
|
288
|
+
self.showLoadingIndicator()
|
|
289
|
+
}
|
|
251
290
|
}
|
|
252
291
|
}
|
|
253
292
|
|
|
@@ -287,6 +326,30 @@ class TPStreamsRNPlayerView: UIView {
|
|
|
287
326
|
TPStreamsDownloadModule.shared?.setAccessTokenDelegate(self)
|
|
288
327
|
}
|
|
289
328
|
|
|
329
|
+
private func setupLoadingIndicator() {
|
|
330
|
+
guard loadingIndicator.superview == nil else { return }
|
|
331
|
+
|
|
332
|
+
addSubview(loadingIndicator)
|
|
333
|
+
|
|
334
|
+
NSLayoutConstraint.activate([
|
|
335
|
+
loadingIndicator.centerXAnchor.constraint(equalTo: centerXAnchor),
|
|
336
|
+
loadingIndicator.centerYAnchor.constraint(equalTo: centerYAnchor)
|
|
337
|
+
])
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private func showLoadingIndicator() {
|
|
341
|
+
DispatchQueue.main.async {
|
|
342
|
+
self.loadingIndicator.startAnimating()
|
|
343
|
+
self.bringSubviewToFront(self.loadingIndicator)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private func hideLoadingIndicator() {
|
|
348
|
+
DispatchQueue.main.async {
|
|
349
|
+
self.loadingIndicator.stopAnimating()
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
290
353
|
private static func sanitizeLicenseDuration(_ value: Double) -> Double {
|
|
291
354
|
guard value > 0 else { return maxOfflineLicenseDuration }
|
|
292
355
|
return min(value, maxOfflineLicenseDuration)
|
|
@@ -380,4 +443,4 @@ extension TPStreamsRNPlayerView: TokenRequestDelegate {
|
|
|
380
443
|
pendingTokenCompletion = completion
|
|
381
444
|
onAccessTokenExpired(["videoId": assetId])
|
|
382
445
|
}
|
|
383
|
-
}
|
|
446
|
+
}
|
|
@@ -6,23 +6,12 @@ import AVFoundation
|
|
|
6
6
|
@objc(TPStreamsRNPlayerViewManager)
|
|
7
7
|
class TPStreamsRNPlayerViewManager: RCTViewManager {
|
|
8
8
|
override func view() -> UIView! {
|
|
9
|
-
ensureDownloadModuleInitialized()
|
|
10
9
|
return TPStreamsRNPlayerView()
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
override static func requiresMainQueueSetup() -> Bool {
|
|
14
13
|
return true
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
private func ensureDownloadModuleInitialized() {
|
|
18
|
-
guard let bridge = bridge else {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if let module = bridge.module(for: TPStreamsDownloadModule.self) as? TPStreamsDownloadModule {
|
|
23
|
-
module.initializeModule()
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
15
|
|
|
27
16
|
// MARK: - Player Commands - Simply delegate to the view
|
|
28
17
|
|