bitmovin-player-react-native 0.4.0 → 0.5.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.
Files changed (43) hide show
  1. package/README.md +249 -1
  2. package/RNBitmovinPlayer.podspec +3 -1
  3. package/android/build.gradle +3 -2
  4. package/android/src/main/java/com/bitmovin/player/reactnative/AnalyticsModule.kt +154 -0
  5. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +45 -0
  6. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +25 -4
  7. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +3 -0
  8. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +172 -0
  9. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/Any.kt +27 -0
  10. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt +8 -0
  11. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/String.kt +8 -0
  12. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt +37 -0
  13. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +73 -0
  14. package/ios/AnalyticsModule.m +14 -0
  15. package/ios/AnalyticsModule.swift +180 -0
  16. package/ios/Event+JSON.swift +11 -0
  17. package/ios/FullscreenHandlerBridge.swift +33 -0
  18. package/ios/FullscreenHandlerModule.m +9 -0
  19. package/ios/FullscreenHandlerModule.swift +71 -0
  20. package/ios/RCTConvert+BitmovinPlayer.swift +174 -0
  21. package/ios/RNPlayerView+PlayerListener.swift +5 -1
  22. package/ios/RNPlayerView+UserInterfaceListener.swift +16 -0
  23. package/ios/RNPlayerView.swift +5 -0
  24. package/ios/RNPlayerViewManager.m +6 -0
  25. package/ios/RNPlayerViewManager.swift +21 -0
  26. package/lib/index.d.ts +498 -51
  27. package/lib/index.js +186 -42
  28. package/lib/index.mjs +167 -26
  29. package/package.json +1 -1
  30. package/src/analytics/collector.ts +97 -0
  31. package/src/analytics/config.ts +218 -0
  32. package/src/analytics/index.ts +2 -0
  33. package/src/components/PlayerView/events.ts +10 -0
  34. package/src/components/PlayerView/index.tsx +38 -1
  35. package/src/components/PlayerView/native.ts +4 -1
  36. package/src/events.ts +43 -0
  37. package/src/index.ts +2 -0
  38. package/src/media.ts +33 -0
  39. package/src/player.ts +21 -0
  40. package/src/source.ts +4 -0
  41. package/src/styleConfig.ts +87 -0
  42. package/src/ui/fullscreenhandler.ts +19 -0
  43. package/src/ui/fullscreenhandlerbridge.ts +59 -0
@@ -0,0 +1,71 @@
1
+ import BitmovinPlayer
2
+
3
+ @objc(FullscreenHandlerModule)
4
+ class FullscreenHandlerModule: NSObject, RCTBridgeModule {
5
+ /// React bridge reference.
6
+ @objc var bridge: RCTBridge!
7
+
8
+ /// JS module name.
9
+ static func moduleName() -> String! {
10
+ "FullscreenHandlerModule"
11
+ }
12
+
13
+ /// Module requires main thread initialization.
14
+ static func requiresMainQueueSetup() -> Bool {
15
+ true
16
+ }
17
+
18
+ /// Use `UIManager.addBlock` to enqueue module methods on UI thread.
19
+ var methodQueue: DispatchQueue! {
20
+ bridge.uiManager.methodQueue
21
+ }
22
+
23
+ /// In-memory mapping from `nativeId`s to `FullscreenHandler` instances.
24
+ private var fullscreenHandlers: Registry<FullscreenHandlerBridge> = [:]
25
+
26
+ /// Dispatch group used for blocking thread while waiting for state change
27
+ private let fullscreenChangeDispatchGroup = DispatchGroup()
28
+
29
+ /**
30
+ Fetches the `FullscreenHandlerBridge` instance associated with `nativeId` from internal fullscreenHandlers.
31
+ - Parameter nativeId: `FullscreenHandlerBridge` instance ID.
32
+ - Returns: The associated `FullscreenHandlerBridge` instance or `nil`.
33
+ */
34
+ @objc func retrieve(_ nativeId: NativeId) -> FullscreenHandlerBridge? {
35
+ fullscreenHandlers[nativeId]
36
+ }
37
+
38
+ /**
39
+ Removes the `FullscreenHandlerBridge` instance associated with `nativeId` from `fullscreenHandlers`.
40
+ - Parameter nativeId Instance to be disposed.
41
+ */
42
+ @objc(destroy:)
43
+ func destroy(_ nativeId: NativeId) {
44
+ fullscreenHandlers.removeValue(forKey: nativeId)
45
+ }
46
+
47
+ @objc(onFullscreenChanged:isFullscreenEnabled:)
48
+ func onFullscreenChanged(_ nativeId: NativeId, isFullscreenEnabled: Bool) -> Any? {
49
+ fullscreenHandlers[nativeId]?.isFullscreen = isFullscreenEnabled
50
+ fullscreenChangeDispatchGroup.leave()
51
+ return nil
52
+ }
53
+
54
+ @objc(registerHandler:)
55
+ func registerHandler(_ nativeId: NativeId) {
56
+ guard fullscreenHandlers[nativeId] == nil else { return }
57
+ fullscreenHandlers[nativeId] = FullscreenHandlerBridge(nativeId, bridge: bridge)
58
+ }
59
+
60
+ func onFullscreenRequested(nativeId: NativeId) {
61
+ fullscreenChangeDispatchGroup.enter()
62
+ bridge.enqueueJSCall("FullscreenBridge-\(nativeId)", method: "enterFullscreen", args: []) {}
63
+ fullscreenChangeDispatchGroup.wait()
64
+ }
65
+
66
+ func onFullscreenExitRequested(nativeId: NativeId) {
67
+ fullscreenChangeDispatchGroup.enter()
68
+ bridge.enqueueJSCall("FullscreenBridge-\(nativeId)", method: "exitFullscreen", args: []) {}
69
+ fullscreenChangeDispatchGroup.wait()
70
+ }
71
+ }
@@ -1,5 +1,6 @@
1
1
  import Foundation
2
2
  import BitmovinPlayer
3
+ import BitmovinAnalyticsCollector
3
4
 
4
5
  extension RCTConvert {
5
6
  /**
@@ -18,6 +19,9 @@ extension RCTConvert {
18
19
  if let playbackConfig = RCTConvert.playbackConfig(json["playbackConfig"]) {
19
20
  playerConfig.playbackConfig = playbackConfig
20
21
  }
22
+ if let styleConfig = RCTConvert.styleConfig(json["styleConfig"]) {
23
+ playerConfig.styleConfig = styleConfig
24
+ }
21
25
  if let tweaksConfig = RCTConvert.tweaksConfig(json["tweaksConfig"]) {
22
26
  playerConfig.tweaksConfig = tweaksConfig
23
27
  }
@@ -55,6 +59,46 @@ extension RCTConvert {
55
59
  return playbackConfig
56
60
  }
57
61
 
62
+ /**
63
+ Utility method to instantiate a `StyleConfig` from a JS object.
64
+ - Parameter json: JS object.
65
+ - Returns: The produced `StyleConfig` object.
66
+ */
67
+ static func styleConfig(_ json: Any?) -> StyleConfig? {
68
+ guard let json = json as? [String: Any?] else {
69
+ return nil
70
+ }
71
+ let styleConfig = StyleConfig()
72
+ if let isUiEnabled = json["isUiEnabled"] as? Bool {
73
+ styleConfig.isUiEnabled = isUiEnabled
74
+ }
75
+ #if !os(tvOS)
76
+ if let playerUiCss = json["playerUiCss"] as? String {
77
+ styleConfig.playerUiCss = RCTConvert.nsurl(playerUiCss)
78
+ }
79
+ if let supplementalPlayerUiCss = json["supplementalPlayerUiCss"] as? String {
80
+ styleConfig.supplementalPlayerUiCss = RCTConvert.nsurl(supplementalPlayerUiCss)
81
+ }
82
+ if let playerUiJs = json["playerUiJs"] as? String {
83
+ styleConfig.playerUiJs = RCTConvert.nsurl(playerUiJs)
84
+ }
85
+ #endif
86
+ if let scalingMode = json["scalingMode"] as? String {
87
+ switch scalingMode {
88
+ case "Fit":
89
+ styleConfig.scalingMode = .fit
90
+ case "Stretch":
91
+ styleConfig.scalingMode = .stretch
92
+ case "Zoom":
93
+ styleConfig.scalingMode = .zoom
94
+ default:
95
+ styleConfig.scalingMode = .fit
96
+ break
97
+ }
98
+ }
99
+ return styleConfig
100
+ }
101
+
58
102
  /**
59
103
  Utility method to instantiate a `TweaksConfig` from a JS object.
60
104
  - Parameter json: JS object.
@@ -202,6 +246,9 @@ extension RCTConvert {
202
246
  }
203
247
  }
204
248
  }
249
+ if let thumbnailTrack = json["thumbnailTrack"] as? String {
250
+ sourceConfig.thumbnailTrack = RCTConvert.thumbnailTrack(thumbnailTrack)
251
+ }
205
252
  return sourceConfig
206
253
  }
207
254
 
@@ -266,6 +313,25 @@ extension RCTConvert {
266
313
  return fairplayConfig
267
314
  }
268
315
 
316
+ /**
317
+ Utility method to get a `ThumbnailTrack` instance from a JS object.
318
+ - Parameter url: String.
319
+ - Returns: The generated `ThumbnailTrack`.
320
+ */
321
+ static func thumbnailTrack(_ url: String?) -> ThumbnailTrack? {
322
+ guard
323
+ let url = RCTConvert.nsurl(url)
324
+ else {
325
+ return nil
326
+ }
327
+ return ThumbnailTrack(
328
+ url: url,
329
+ label: "Thumbnails",
330
+ identifier: UUID().uuidString,
331
+ isDefaultTrack: false
332
+ )
333
+ }
334
+
269
335
  /**
270
336
  Utility method to get a `SubtitleTrack` instance from a JS object.
271
337
  - Parameter json: JS object.
@@ -482,4 +548,112 @@ extension RCTConvert {
482
548
  "minBitrate": adData.minBitrate
483
549
  ]
484
550
  }
551
+
552
+ /**
553
+ Utility method to get a `BitmovinAnalyticsConfig` value from a JS object.
554
+ - Parameter json: JS object.
555
+ - Returns: The associated `BitmovinAnalyticsConfig` value or nil.
556
+ */
557
+ static func analyticsConfig(_ json: Any?) -> BitmovinAnalyticsConfig? {
558
+ guard
559
+ let json = json as? [String: Any?],
560
+ let key = json["key"] as? String
561
+ else {
562
+ return nil
563
+ }
564
+ let config: BitmovinAnalyticsConfig
565
+ if let playerKey = json["playerKey"] as? String {
566
+ config = BitmovinAnalyticsConfig(key: key, playerKey: playerKey)
567
+ } else {
568
+ config = BitmovinAnalyticsConfig(key: key)
569
+ }
570
+ if let cdnProvider = json["cdnProvider"] as? String {
571
+ config.cdnProvider = cdnProvider
572
+ }
573
+ if let customerUserId = json["customUserId"] as? String {
574
+ config.customerUserId = customerUserId
575
+ }
576
+ if let experimentName = json["experimentName"] as? String {
577
+ config.experimentName = experimentName
578
+ }
579
+ if let videoId = json["videoId"] as? String {
580
+ config.videoId = videoId
581
+ }
582
+ if let title = json["title"] as? String {
583
+ config.title = title
584
+ }
585
+ if let path = json["path"] as? String {
586
+ config.path = path
587
+ }
588
+ if let isLive = json["isLive"] as? Bool {
589
+ config.isLive = isLive
590
+ }
591
+ if let ads = json["ads"] as? Bool {
592
+ config.ads = ads
593
+ }
594
+ if let randomizeUserId = json["randomizeUserId"] as? Bool {
595
+ config.randomizeUserId = randomizeUserId
596
+ }
597
+ for n in 1..<30 {
598
+ if let customDataN = json["customData\(n)"] as? String {
599
+ config.setValue(customDataN, forKey: "customData\(n)")
600
+ }
601
+ }
602
+ return config
603
+ }
604
+
605
+ /**
606
+ Utility method to get an analytics `CustomData` value from a JS object.
607
+ - Parameter json: JS object.
608
+ - Returns: The associated `CustomData` value or nil.
609
+ */
610
+ static func analyticsCustomData(_ json: Any?) -> CustomData? {
611
+ guard let json = json as? [String: Any?] else {
612
+ return nil
613
+ }
614
+ let customData = CustomData()
615
+ for n in 1..<30 {
616
+ if let customDataN = json["customData\(n)"] as? String {
617
+ customData.setValue(customDataN, forKey: "customData\(n)")
618
+ }
619
+ }
620
+ return customData
621
+ }
622
+
623
+ /**
624
+ Utility method to get a JS value from a `CustomData` object.
625
+ - Parameter analyticsCustomData: Analytics custom data object.
626
+ - Returns: The JS value representing the given object.
627
+ */
628
+ static func toJson(analyticsCustomData: CustomData?) -> [String: Any?]? {
629
+ guard let analyticsCustomData = analyticsCustomData else {
630
+ return nil
631
+ }
632
+ var json: [String: Any?] = [:]
633
+ for n in 1..<30 {
634
+ if let customDataN = analyticsCustomData.value(forKey: "customData\(n)") {
635
+ json["customData\(n)"] = customDataN
636
+ }
637
+ }
638
+ return json
639
+ }
640
+
641
+ /**
642
+ Utility method to compute a JS value from a `VideoQuality` object.
643
+ - Parameter videoQuality `VideoQuality` object to be converted.
644
+ - Returns: The produced JS object.
645
+ */
646
+ static func toJson(videoQuality: VideoQuality?) -> [String: Any?]? {
647
+ guard let videoQuality = videoQuality else {
648
+ return nil
649
+ }
650
+ return [
651
+ "id": videoQuality.identifier,
652
+ "label": videoQuality.label,
653
+ "height": videoQuality.height,
654
+ "width": videoQuality.width,
655
+ "codec": videoQuality.codec,
656
+ "bitrate": videoQuality.bitrate,
657
+ ]
658
+ }
485
659
  }
@@ -56,7 +56,7 @@ extension RNPlayerView: PlayerListener {
56
56
  func onSeeked(_ event: SeekedEvent, player: Player) {
57
57
  onSeeked?(event.toJSON())
58
58
  }
59
-
59
+
60
60
  func onStallStarted(_ event: StallStartedEvent, player: Player) {
61
61
  onStallStarted?(event.toJSON())
62
62
  }
@@ -144,4 +144,8 @@ extension RNPlayerView: PlayerListener {
144
144
  func onAdStarted(_ event: AdStartedEvent, player: Player) {
145
145
  onAdStarted?(event.toJSON())
146
146
  }
147
+
148
+ func onVideoPlaybackQualityChanged(_ event: VideoDownloadQualityChangedEvent, player: Player) {
149
+ onVideoPlaybackQualityChanged?(event.toJSON())
150
+ }
147
151
  }
@@ -16,4 +16,20 @@ extension RNPlayerView: UserInterfaceListener {
16
16
  func onPictureInPictureExited(_ event: PictureInPictureExitedEvent, view: PlayerView) {
17
17
  onPictureInPictureExited?(event.toJSON())
18
18
  }
19
+
20
+ func onFullscreenEnter(_ event: FullscreenEnterEvent, view: PlayerView) {
21
+ onFullscreenEnter?(event.toJSON())
22
+ }
23
+
24
+ func onFullscreenExit(_ event: FullscreenExitEvent, view: PlayerView) {
25
+ onFullscreenExit?(event.toJSON())
26
+ }
27
+
28
+ func onFullscreenEnabled(_ event: FullscreenEnabledEvent, view: PlayerView) {
29
+ onFullscreenEnabled?(event.toJSON())
30
+ }
31
+
32
+ func onFullscreenDisabled(_ event: FullscreenDisabledEvent, view: PlayerView) {
33
+ onFullscreenDisabled?(event.toJSON())
34
+ }
19
35
  }
@@ -43,6 +43,11 @@ class RNPlayerView: UIView {
43
43
  @objc var onAdScheduled: RCTBubblingEventBlock?
44
44
  @objc var onAdSkipped: RCTBubblingEventBlock?
45
45
  @objc var onAdStarted: RCTBubblingEventBlock?
46
+ @objc var onVideoPlaybackQualityChanged: RCTBubblingEventBlock?
47
+ @objc var onFullscreenEnabled: RCTBubblingEventBlock?
48
+ @objc var onFullscreenDisabled: RCTBubblingEventBlock?
49
+ @objc var onFullscreenEnter: RCTBubblingEventBlock?
50
+ @objc var onFullscreenExit: RCTBubblingEventBlock?
46
51
 
47
52
  /// The `PlayerView` subview.
48
53
  var playerView: PlayerView? {
@@ -42,7 +42,13 @@ RCT_EXPORT_VIEW_PROPERTY(onAdQuartile, RCTBubblingEventBlock)
42
42
  RCT_EXPORT_VIEW_PROPERTY(onAdScheduled, RCTBubblingEventBlock)
43
43
  RCT_EXPORT_VIEW_PROPERTY(onAdSkipped, RCTBubblingEventBlock)
44
44
  RCT_EXPORT_VIEW_PROPERTY(onAdStarted, RCTBubblingEventBlock)
45
+ RCT_EXPORT_VIEW_PROPERTY(onVideoPlaybackQualityChanged, RCTBubblingEventBlock)
46
+ RCT_EXPORT_VIEW_PROPERTY(onFullscreenEnabled, RCTBubblingEventBlock)
47
+ RCT_EXPORT_VIEW_PROPERTY(onFullscreenDisabled, RCTBubblingEventBlock)
48
+ RCT_EXPORT_VIEW_PROPERTY(onFullscreenEnter, RCTBubblingEventBlock)
49
+ RCT_EXPORT_VIEW_PROPERTY(onFullscreenExit, RCTBubblingEventBlock)
45
50
 
46
51
  RCT_EXTERN_METHOD(attachPlayer:(nonnull NSNumber *)viewId playerId:(NSString *)playerId playerConfig:(nullable NSDictionary *)playerConfig)
52
+ RCT_EXTERN_METHOD(attachFullscreenBridge:(nonnull NSNumber *)viewId fullscreenBridgeId:(NSString *)fullscreenBridgeId)
47
53
 
48
54
  @end
@@ -37,8 +37,29 @@ class RNPlayerViewManager: RCTViewManager {
37
37
  }
38
38
  }
39
39
 
40
+ @objc func attachFullscreenBridge(_ viewId: NSNumber, fullscreenBridgeId: NativeId) {
41
+ bridge.uiManager.addUIBlock { [weak self] _, views in
42
+ guard
43
+ let view = views?[viewId] as? RNPlayerView,
44
+ let fullscreenBridge = self?.getFullscreenHandlerModule()?.retrieve(fullscreenBridgeId)
45
+ else {
46
+ return
47
+ }
48
+ guard let playerView = view.playerView else {
49
+ return
50
+ }
51
+
52
+ playerView.fullscreenHandler = fullscreenBridge
53
+ }
54
+ }
55
+
40
56
  /// Fetches the initialized `PlayerModule` instance on RN's bridge object.
41
57
  private func getPlayerModule() -> PlayerModule? {
42
58
  bridge.module(for: PlayerModule.self) as? PlayerModule
43
59
  }
60
+
61
+ /// Fetches the initialized `FullscreenHandlerModule` instance on RN's bridge object.
62
+ private func getFullscreenHandlerModule() -> FullscreenHandlerModule? {
63
+ bridge.module(for: FullscreenHandlerModule.self) as? FullscreenHandlerModule
64
+ }
44
65
  }