react-native-theoplayer 2.11.0 → 2.12.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +6 -3
  3. package/android/build.gradle +2 -2
  4. package/android/src/main/java/com/theoplayer/ReactTHEOplayerContext.kt +14 -0
  5. package/android/src/main/java/com/theoplayer/ReactTHEOplayerView.kt +1 -3
  6. package/android/src/main/java/com/theoplayer/audio/AudioFocusManager.kt +145 -0
  7. package/android/src/main/java/com/theoplayer/drm/ContentProtectionModule.kt +1 -1
  8. package/ios/THEOplayerRCTBridge.m +0 -1
  9. package/ios/THEOplayerRCTNetworkUtils.swift +0 -2
  10. package/ios/THEOplayerRCTPlayerAPI.swift +14 -2
  11. package/ios/THEOplayerRCTPrintUtils.swift +0 -3
  12. package/ios/THEOplayerRCTSourceDescriptionBuilder.swift +2 -0
  13. package/ios/THEOplayerRCTTextTrackEventHandler.swift +32 -4
  14. package/ios/THEOplayerRCTViewManager.swift +0 -3
  15. package/ios/ads/THEOplayerRCTAdsAPI.swift +0 -4
  16. package/ios/backgroundAudio/THEOplayerRCTRemoteCommandsManager.swift +51 -36
  17. package/ios/casting/THEOplayerRCTCastAPI.swift +0 -3
  18. package/ios/contentprotection/THEOplayerRCTContentProtectionAPI.swift +0 -3
  19. package/ios/contentprotection/THEOplayerRCTProxyContentProtectionIntegration.swift +0 -2
  20. package/ios/contentprotection/THEOplayerRCTProxyContentProtectionIntegrationFactory.swift +0 -2
  21. package/lib/commonjs/api/source/SourceDescription.js.map +1 -1
  22. package/lib/commonjs/api/source/analytics/AnalyticsDescription.js +2 -0
  23. package/lib/commonjs/api/source/analytics/AnalyticsDescription.js.map +1 -0
  24. package/lib/commonjs/api/source/analytics/barrel.js +17 -0
  25. package/lib/commonjs/api/source/analytics/barrel.js.map +1 -0
  26. package/lib/commonjs/api/source/barrel.js +15 -4
  27. package/lib/commonjs/api/source/barrel.js.map +1 -1
  28. package/lib/commonjs/api/track/TextTrack.js +1 -1
  29. package/lib/commonjs/api/track/TextTrack.js.map +1 -1
  30. package/lib/commonjs/internal/adapter/THEOplayerWebAdapter.js.map +1 -1
  31. package/lib/module/api/source/SourceDescription.js.map +1 -1
  32. package/lib/module/api/source/analytics/AnalyticsDescription.js +2 -0
  33. package/lib/module/api/source/analytics/AnalyticsDescription.js.map +1 -0
  34. package/lib/module/api/source/analytics/barrel.js +2 -0
  35. package/lib/module/api/source/analytics/barrel.js.map +1 -0
  36. package/lib/module/api/source/barrel.js +1 -0
  37. package/lib/module/api/source/barrel.js.map +1 -1
  38. package/lib/module/api/track/TextTrack.js +1 -1
  39. package/lib/module/api/track/TextTrack.js.map +1 -1
  40. package/lib/module/internal/adapter/THEOplayerWebAdapter.js.map +1 -1
  41. package/lib/typescript/api/source/SourceDescription.d.ts +5 -0
  42. package/lib/typescript/api/source/analytics/AnalyticsDescription.d.ts +15 -0
  43. package/lib/typescript/api/source/analytics/barrel.d.ts +1 -0
  44. package/lib/typescript/api/source/barrel.d.ts +1 -0
  45. package/lib/typescript/api/track/TextTrack.d.ts +1 -1
  46. package/package.json +1 -1
  47. package/react-native-theoplayer.json +2 -1
  48. package/react-native-theoplayer.podspec +7 -3
  49. package/src/api/source/SourceDescription.ts +6 -0
  50. package/src/api/source/analytics/AnalyticsDescription.ts +16 -0
  51. package/src/api/source/analytics/barrel.ts +1 -0
  52. package/src/api/source/barrel.ts +1 -0
  53. package/src/api/track/TextTrack.ts +2 -2
  54. package/src/internal/adapter/THEOplayerWebAdapter.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/)
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.12.1] - 23-09-14
9
+
10
+ ### Fixed
11
+
12
+ - Fixed an issue on Android where the player SDK dependency could resolve to version 6.+.
13
+ - Fixed an issue on iOS where the player integration dependencies could resolve to version 6.+.
14
+
15
+ ## [2.12.0] - 23-09-04
16
+
17
+ ### Added
18
+
19
+ - Added an `AnalyticsDescription` property to `SourceDescription` to configure additional source-specific properties for analytics connectors.
20
+ - Added support for sideloaded webVTT and SRT texttracks on iOS.
21
+ - Added Audio Focus for Android, pausing play-out on audio focus loss and resuming play-out once focus has been regained.
22
+ - Added Audio Focus and Audio Becoming Noisy manager for Android.
23
+
24
+ ### Fixed
25
+
26
+ - Fixed an issue on Android that would cause the player not to reset when setting an empty source.
27
+ - Fixed an issue where a text track cue was not properly removed from the cue list on a TextTrackEventType.REMOVE_CUE event.
28
+ - Fixed an issue on tvOS that allowed the user to pause a CSAI ad using the apple remote control.
29
+ - Fixed an issue on iOS where the cue event listeners were not cleanup up when destroying the player instance, resulting in memory build-up.
30
+
8
31
  ## [2.11.0] - 23-08-10
9
32
 
10
33
  ### Added
package/README.md CHANGED
@@ -9,9 +9,9 @@ This projects falls under the license as defined in https://github.com/THEOplaye
9
9
  ## Table of Contents
10
10
 
11
11
  1. [Overview](#overview)
12
- 1. [How to use these guides](#how-to-use-these-guides)
13
- 1. [Prerequisites](#prerequisites)
14
- 2. [Getting Started](#getting-started)
12
+ 2. [How to use these guides](#how-to-use-these-guides)
13
+ 3. [Prerequisites](#prerequisites)
14
+ 4. [Getting Started](#getting-started)
15
15
 
16
16
  ## Overview
17
17
 
@@ -58,9 +58,11 @@ functionality. Currently, the following connectors are available:
58
58
  | Package name | Purpose | Registry |
59
59
  |---------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
60
60
  | [`@theoplayer/react-native-analytics-adobe`](https://github.com/THEOplayer/react-native-theoplayer-analytics) | Adobe analytics connector | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-analytics-adobe)](https://www.npmjs.com/package/@theoplayer/react-native-analytics-adobe) |
61
+ | [`@theoplayer/react-native-analytics-agama`](https://github.com/THEOplayer/react-native-theoplayer-analytics) | Agama analytics connector | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-analytics-agama)](https://www.npmjs.com/package/@theoplayer/react-native-analytics-agama) |
61
62
  | [`@theoplayer/react-native-analytics-comscore`](https://github.com/THEOplayer/react-native-theoplayer-analytics) | Comscore analytics connector | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-analytics-comscore)](https://www.npmjs.com/package/@theoplayer/react-native-analytics-comscore) |
62
63
  | [`@theoplayer/react-native-analytics-conviva`](https://github.com/THEOplayer/react-native-theoplayer-analytics) | Conviva analytics connector | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-analytics-conviva)](https://www.npmjs.com/package/@theoplayer/react-native-analytics-conviva) |
63
64
  | [`@theoplayer/react-native-analytics-nielsen`](https://github.com/THEOplayer/react-native-theoplayer-analytics) | Nielsen analytics connector | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-analytics-nielsen)](https://www.npmjs.com/package/@theoplayer/react-native-analytics-nielsen) |
65
+ | [`@theoplayer/react-native-analytics-youbora`](https://github.com/THEOplayer/react-native-theoplayer-analytics) | Youbora analytics connector | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-analytics-youbora)](https://www.npmjs.com/package/@theoplayer/react-native-analytics-youbora) |
64
66
  | [`@theoplayer/react-native-drm`](https://github.com/THEOplayer/react-native-theoplayer-drm) | Content protection (DRM) connectors | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-drm)](https://www.npmjs.com/package/@theoplayer/react-native-drm) |
65
67
  | [`@theoplayer/react-native-ui`](https://github.com/THEOplayer/react-native-theoplayer-ui) | React Native user interface | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-ui)](https://www.npmjs.com/package/@theoplayer/react-native-ui) |
66
68
  | [`@theoplayer/react-native-connector-template`](https://github.com/THEOplayer/react-native-theoplayer-connector-template) | A template for `react-native-theoplayer` connectors. | [![npm](https://img.shields.io/npm/v/@theoplayer/react-native-connector-template)](https://www.npmjs.com/package/@theoplayer/react-native-connector-template) |
@@ -83,6 +85,7 @@ and discussed in the next section. Finally, an overview of features, limitations
83
85
  - Knowledge Base
84
86
  - [Adaptive Bitrate (ABR)](./doc/abr.md)
85
87
  - [Advertisements](./doc/ads.md)
88
+ - [Audio Control Management](./doc/audio-control.md)
86
89
  - [Background playback and notifications](./doc/background.md)
87
90
  - [Casting with Chromecast and Airplay](./doc/cast.md)
88
91
  - [Custom iOS framework](./doc/custom-ios-framework.md)
@@ -105,8 +105,8 @@ dependencies {
105
105
  implementation "com.theoplayer.theoplayer-sdk-android:ads-wrapper:4.8.0"
106
106
  implementation "androidx.appcompat:appcompat:1.4.+"
107
107
 
108
- // The minimum supported version is 5.0.1
109
- def theoplayer_sdk_version = safeExtGet('THEOplayer_sdk', '[5.0.1,)')
108
+ // The minimum supported version is 5.10.0
109
+ def theoplayer_sdk_version = safeExtGet('THEOplayer_sdk', '[5.10,6.0)')
110
110
 
111
111
  // def theoplayer_mediasession_version = safeExtGet('THEOplayer_mediasession', theoplayer_sdk_version)
112
112
  def theoplayer_mediasession_version = "5.2.0-local"
@@ -27,6 +27,8 @@ import com.theoplayer.android.api.event.EventListener
27
27
  import com.theoplayer.android.api.event.player.*
28
28
  import com.theoplayer.android.api.player.Player
29
29
  import com.theoplayer.android.connector.mediasession.MediaSessionConnector
30
+ import com.theoplayer.audio.AudioBecomingNoisyManager
31
+ import com.theoplayer.audio.AudioFocusManager
30
32
  import com.theoplayer.audio.BackgroundAudioConfig
31
33
  import com.theoplayer.media.MediaPlaybackService
32
34
  import java.util.concurrent.atomic.AtomicBoolean
@@ -49,6 +51,11 @@ class ReactTHEOplayerContext private constructor(
49
51
  private var isBound = AtomicBoolean()
50
52
  private var binder: MediaPlaybackService.MediaPlaybackBinder? = null
51
53
  private var mediaSessionConnector: MediaSessionConnector? = null
54
+ private var audioBecomingNoisyManager = AudioBecomingNoisyManager(reactContext) {
55
+ // Audio is about to become 'noisy' due to a change in audio outputs: pause the player
56
+ player.pause()
57
+ }
58
+ private var audioFocusManager: AudioFocusManager? = null
52
59
 
53
60
  var backgroundAudioConfig: BackgroundAudioConfig = BackgroundAudioConfig(enabled = false)
54
61
  set(value) {
@@ -207,6 +214,8 @@ class ReactTHEOplayerContext private constructor(
207
214
  addIntegrations(playerConfig)
208
215
  addListeners()
209
216
 
217
+ audioFocusManager = AudioFocusManager(reactContext, player)
218
+
210
219
  if (!BuildConfig.USE_PLAYBACK_SERVICE || !isBackgroundAudioEnabled) {
211
220
  initDefaultMediaSession()
212
221
  }
@@ -282,11 +291,14 @@ class ReactTHEOplayerContext private constructor(
282
291
  }
283
292
  binder?.updateNotification(PlaybackStateCompat.STATE_PLAYING)
284
293
  applyAllowedMediaControls()
294
+ audioBecomingNoisyManager.setEnabled(true)
295
+ audioFocusManager?.retrieveAudioFocus()
285
296
  }
286
297
 
287
298
  private val onPause = EventListener<PauseEvent> {
288
299
  binder?.updateNotification(PlaybackStateCompat.STATE_PAUSED)
289
300
  applyAllowedMediaControls()
301
+ audioBecomingNoisyManager.setEnabled(false)
290
302
  }
291
303
 
292
304
  private fun addListeners() {
@@ -334,6 +346,7 @@ class ReactTHEOplayerContext private constructor(
334
346
  fun onHostResume() {
335
347
  mediaSessionConnector?.setActive(true)
336
348
  playerView.onResume()
349
+ audioFocusManager?.retrieveAudioFocus()
337
350
  }
338
351
 
339
352
  fun destroy() {
@@ -346,6 +359,7 @@ class ReactTHEOplayerContext private constructor(
346
359
  // Unbind client from background service so it can stop
347
360
  unbindMediaPlaybackService()
348
361
  }
362
+ audioFocusManager?.abandonAudioFocus()
349
363
  mediaSessionConnector?.destroy()
350
364
  playerView.onDestroy()
351
365
  }
@@ -130,9 +130,7 @@ class ReactTHEOplayerView(private val reactContext: ThemedReactContext) :
130
130
  try {
131
131
  val sourceDescription = SourceAdapter().parseSourceFromJS(source)
132
132
  adsApi.setSource(sourceDescription)
133
- if (sourceDescription != null) {
134
- player?.source = sourceDescription
135
- }
133
+ player?.source = sourceDescription
136
134
  } catch (exception: THEOplayerException) {
137
135
  Log.e(TAG, exception.message ?: "")
138
136
  eventEmitter.emitError(exception)
@@ -0,0 +1,145 @@
1
+ package com.theoplayer.audio
2
+
3
+ import android.app.UiModeManager
4
+ import android.content.Context
5
+ import android.content.res.Configuration
6
+ import android.media.AudioManager
7
+ import android.util.Log
8
+ import androidx.media.AudioAttributesCompat
9
+ import androidx.media.AudioFocusRequestCompat
10
+ import androidx.media.AudioManagerCompat
11
+ import com.theoplayer.BuildConfig
12
+ import com.theoplayer.android.api.player.Player
13
+
14
+ private const val TAG = "AudioFocusManager"
15
+
16
+ /**
17
+ * Manages audio focus for the application, ensuring proper handling of audio focus changes
18
+ * to control media playback behavior.
19
+ *
20
+ * @param context The context used to access system services.
21
+ * @param player The media player instance associated with this audio focus manager. It can be
22
+ * provided optionally to control playback behavior.
23
+ */
24
+ class AudioFocusManager(
25
+ context: Context,
26
+ private val player: Player? = null
27
+ ) : AudioManager.OnAudioFocusChangeListener {
28
+
29
+ private val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
30
+ private val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as? UiModeManager
31
+ private var resumeOnFocusGain = false
32
+ private val focusLock = Any()
33
+
34
+ private val audioFocusRequest = AudioFocusRequestCompat.Builder(AudioManagerCompat.AUDIOFOCUS_GAIN)
35
+ .setAudioAttributes(
36
+ AudioAttributesCompat.Builder()
37
+ // Usage value to use when the usage is media, such as music, or movie soundtracks.
38
+ .setUsage(AudioAttributesCompat.USAGE_MEDIA)
39
+ // Content type value to use when the content type is a soundtrack, typically accompanying
40
+ // a movie or TV program.
41
+ .setContentType(AudioAttributesCompat.CONTENT_TYPE_MOVIE)
42
+ .build()
43
+ )
44
+ .setOnAudioFocusChangeListener(this)
45
+ .setWillPauseWhenDucked(true)
46
+ .build()
47
+
48
+ /**
49
+ * Called on the listener to notify it the audio focus for this listener has been changed.
50
+ */
51
+ override fun onAudioFocusChange(focusChange: Int) {
52
+ if (uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
53
+ // Ignore changes in audioFocus for Connected TVs.
54
+ return
55
+ }
56
+ if (BuildConfig.DEBUG) {
57
+ Log.d(TAG, "onAudioFocusChange: ${fromAudioFocusChange(focusChange)}")
58
+ }
59
+ when (focusChange) {
60
+ // Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
61
+ AudioManager.AUDIOFOCUS_GAIN -> {
62
+ if (resumeOnFocusGain) {
63
+ synchronized(focusLock) {
64
+ // Reset resume flag
65
+ resumeOnFocusGain = false
66
+ }
67
+ player?.play()
68
+ }
69
+ }
70
+
71
+ // Used to indicate a loss of audio focus of unknown duration.
72
+ AudioManager.AUDIOFOCUS_LOSS -> {
73
+ synchronized (focusLock) {
74
+ // This is not a transient loss, we shouldn't automatically resume for now
75
+ resumeOnFocusGain = false;
76
+ }
77
+ }
78
+
79
+ // Used to indicate a transient loss of audio focus.
80
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
81
+
82
+ // Used to indicate a transient loss of audio focus where the loser of the audio focus can
83
+ // lower its output volume if it wants to continue playing (also referred to as "ducking"),
84
+ // as the new focus owner doesn't require others to be silent.
85
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
86
+ synchronized (focusLock) {
87
+ // We should only resume if playback was interrupted
88
+ resumeOnFocusGain = !(player?.isPaused ?: true)
89
+ }
90
+ player?.pause()
91
+ }
92
+ }
93
+ }
94
+
95
+ private fun fromAudioFocusChange(focusChange: Int?): String {
96
+ return when (focusChange) {
97
+ AudioManager.AUDIOFOCUS_GAIN -> "AUDIOFOCUS_GAIN"
98
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT -> "AUDIOFOCUS_GAIN_TRANSIENT"
99
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE -> "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"
100
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK -> "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"
101
+ AudioManager.AUDIOFOCUS_LOSS -> "AUDIOFOCUS_LOSS"
102
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> "AUDIOFOCUS_LOSS_TRANSIENT"
103
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"
104
+ else -> "AUDIOFOCUS_NONE"
105
+ }
106
+ }
107
+
108
+ private fun fromAudioFocusRequest(focusRequest: Int?): String {
109
+ return when(focusRequest) {
110
+ AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> "AUDIOFOCUS_REQUEST_GRANTED"
111
+ AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> "AUDIOFOCUS_REQUEST_DELAYED"
112
+ else -> "AUDIOFOCUS_REQUEST_FAILED"
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Send a request to obtain the audio focus
118
+ *
119
+ * @return True if audio focus is granted, false otherwise.
120
+ */
121
+ fun retrieveAudioFocus(): Boolean {
122
+ val result = audioManager?.let {
123
+ AudioManagerCompat.requestAudioFocus(it, audioFocusRequest)
124
+ }
125
+ if (BuildConfig.DEBUG) {
126
+ Log.d(TAG, "retrieveAudioFocus: ${fromAudioFocusRequest(result)}")
127
+ }
128
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
129
+ }
130
+
131
+ /**
132
+ * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
133
+ */
134
+ fun abandonAudioFocus() {
135
+ synchronized (focusLock) {
136
+ resumeOnFocusGain = false
137
+ }
138
+ val result = audioManager?.let {
139
+ AudioManagerCompat.abandonAudioFocusRequest(it, audioFocusRequest)
140
+ }
141
+ if (BuildConfig.DEBUG) {
142
+ Log.d(TAG, "abandonAudioFocus: ${fromAudioFocusRequest(result)}")
143
+ }
144
+ }
145
+ }
@@ -111,7 +111,7 @@ class ContentProtectionModule(private val context: ReactApplicationContext) :
111
111
  }
112
112
  emit(
113
113
  EVENT_BUILD_INTEGRATION, payload,
114
- onResult = hashMapOf(),
114
+ onResult = hashMapOf(EVENT_BUILD_PROCESSED to { /*NoOp*/ }),
115
115
  onError = {}
116
116
  )
117
117
  }
@@ -3,7 +3,6 @@
3
3
  // Theoplayer
4
4
  //
5
5
  // Created by William van Haevre on 14/01/2022.
6
- // Copyright © 2022 Facebook. All rights reserved.
7
6
  //
8
7
 
9
8
  #import <React/RCTViewManager.h>
@@ -2,8 +2,6 @@
2
2
  // THEOplayerRCTNetworkUtils.swift
3
3
  // Theoplayer
4
4
  //
5
- // Created by William van Haevre on 14/11/2022.
6
- //
7
5
 
8
6
  import Foundation
9
7
 
@@ -6,6 +6,10 @@ import Foundation
6
6
  import UIKit
7
7
  import THEOplayerSDK
8
8
 
9
+ #if canImport(THEOplayerConnectorSideloadedSubtitle)
10
+ import THEOplayerConnectorSideloadedSubtitle
11
+ #endif
12
+
9
13
  let ERROR_MESSAGE_PLAYER_ABR_UNSUPPORTED_FEATURE: String = "Setting an ABRconfig is not supported on iOS/tvOS."
10
14
  let ERROR_MESSAGE_PLAYER_QUALITY_UNSUPPORTED_FEATURE: String = "Setting a target video quality is not supported on iOS/tvOS."
11
15
  let ERROR_MESSAGE_PLAYER_FULLSCREEN_UNSUPPORTED_FEATURE: String = "Fullscreen presentationmode should be implemented on the RN level for iOS/tvOS."
@@ -56,8 +60,7 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
56
60
  if let theView = self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView,
57
61
  let srcDescription = THEOplayerRCTSourceDescriptionBuilder.buildSourceDescription(src) {
58
62
  if let player = theView.player {
59
- if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Setting new source on TheoPlayer") }
60
- player.source = srcDescription
63
+ self.setNewSourceDescription(player: player, srcDescription: srcDescription)
61
64
  }
62
65
  } else {
63
66
  if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Failed to update THEOplayer source.") }
@@ -65,6 +68,15 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
65
68
  }
66
69
  }
67
70
 
71
+ private func setNewSourceDescription(player: THEOplayer, srcDescription: SourceDescription) {
72
+ if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Setting new source on TheoPlayer") }
73
+ #if canImport(THEOplayerConnectorSideloadedSubtitle)
74
+ player.setSourceWithSubtitles(source: srcDescription)
75
+ #else
76
+ player.source = srcDescription
77
+ #endif
78
+ }
79
+
68
80
  @objc(setABRConfig:abrConfig:)
69
81
  func setABRConfig(_ node: NSNumber, setABRConfig: NSDictionary) -> Void {
70
82
  if DEBUG_PLAYER_API { print(ERROR_MESSAGE_PLAYER_ABR_UNSUPPORTED_FEATURE) }
@@ -2,9 +2,6 @@
2
2
  // Printing.swift
3
3
  // THEOliveSDK
4
4
  //
5
- // Created by William van Haevre on 21/06/2021.
6
- //
7
-
8
5
  import Foundation
9
6
 
10
7
  class PrintUtils {
@@ -375,6 +375,8 @@ class THEOplayerRCTSourceDescriptionBuilder {
375
375
 
376
376
  if format == "webvtt" {
377
377
  return THEOplayerSDK.TextTrackFormat.WebVTT
378
+ } else if format == "srt" {
379
+ return THEOplayerSDK.TextTrackFormat.SRT
378
380
  } else {
379
381
  return THEOplayerSDK.TextTrackFormat.none
380
382
  }
@@ -79,16 +79,19 @@ class THEOplayerRCTTextTrackEventHandler {
79
79
  "type" : TrackListEventType.REMOVE_TRACK.rawValue
80
80
  ])
81
81
  // stop listening for cue events on this track
82
- if let addCueListener = welf.addCueListeners[textTrack.uid],
83
- let removeCueListener = welf.removeCueListeners[textTrack.uid],
84
- let enterCueListener = welf.enterCueListeners[textTrack.uid],
85
- let exitCueListener = welf.exitCueListeners[textTrack.uid] {
82
+ if let addCueListener = welf.addCueListeners.removeValue(forKey: textTrack.uid) {
86
83
  textTrack.removeEventListener(type: TextTrackEventTypes.ADD_CUE, listener: addCueListener)
87
84
  if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] AddCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
85
+ }
86
+ if let removeCueListener = welf.removeCueListeners.removeValue(forKey: textTrack.uid) {
88
87
  textTrack.removeEventListener(type: TextTrackEventTypes.REMOVE_CUE, listener: removeCueListener)
89
88
  if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] RemoveCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
89
+ }
90
+ if let enterCueListener = welf.enterCueListeners.removeValue(forKey: textTrack.uid) {
90
91
  textTrack.removeEventListener(type: TextTrackEventTypes.ENTER_CUE, listener: enterCueListener)
91
92
  if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] EnterCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
93
+ }
94
+ if let exitCueListener = welf.exitCueListeners.removeValue(forKey: textTrack.uid) {
92
95
  textTrack.removeEventListener(type: TextTrackEventTypes.EXIT_CUE, listener: exitCueListener)
93
96
  if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] ExitCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
94
97
  }
@@ -134,6 +137,31 @@ class THEOplayerRCTTextTrackEventHandler {
134
137
  player.textTracks.removeEventListener(type: TextTrackListEventTypes.CHANGE, listener: changeTrackListener)
135
138
  if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] ChangeTrack listener dettached from THEOplayer textTrack list") }
136
139
  }
140
+
141
+ // ADD_CUE, REMOVE_CUE, ENTER_CUE, EXIT_CUE
142
+ let textTrackCount = player.textTracks.count
143
+ if textTrackCount > 0 {
144
+ for i in 0..<textTrackCount {
145
+ let textTrack = player.textTracks[i]
146
+ // stop listening for cue events on this track
147
+ if let addCueListener = self.addCueListeners.removeValue(forKey: textTrack.uid) {
148
+ textTrack.removeEventListener(type: TextTrackEventTypes.ADD_CUE, listener: addCueListener)
149
+ if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] AddCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
150
+ }
151
+ if let removeCueListener = self.removeCueListeners.removeValue(forKey: textTrack.uid) {
152
+ textTrack.removeEventListener(type: TextTrackEventTypes.REMOVE_CUE, listener: removeCueListener)
153
+ if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] RemoveCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
154
+ }
155
+ if let enterCueListener = self.enterCueListeners.removeValue(forKey: textTrack.uid) {
156
+ textTrack.removeEventListener(type: TextTrackEventTypes.ENTER_CUE, listener: enterCueListener)
157
+ if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] EnterCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
158
+ }
159
+ if let exitCueListener = self.exitCueListeners.removeValue(forKey: textTrack.uid) {
160
+ textTrack.removeEventListener(type: TextTrackEventTypes.EXIT_CUE, listener: exitCueListener)
161
+ if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] ExitCue listener removed from THEOplayer textTrack with uid \(textTrack.uid)") }
162
+ }
163
+ }
164
+ }
137
165
  }
138
166
 
139
167
  // MARK: - dynamic textTrack Listeners
@@ -2,9 +2,6 @@
2
2
  // THEOplayerRCTViewManager.swift
3
3
  // Theoplayer
4
4
  //
5
- // Created by William van Haevre on 14/01/2022.
6
- // Copyright © 2022 Facebook. All rights reserved.
7
- //
8
5
 
9
6
  import Foundation
10
7
  import UIKit
@@ -2,10 +2,6 @@
2
2
  // THEOplayerRCTAdsAPI.swift
3
3
  // Theoplayer
4
4
  //
5
- // Created by William van Haevre on 09/09/2022.
6
- // Copyright © 2022 Facebook. All rights reserved.
7
- //
8
-
9
5
  import Foundation
10
6
  import UIKit
11
7
  #if canImport(THEOplayerGoogleIMAIntegration)
@@ -72,100 +72,113 @@ class THEOplayerRCTRemoteCommandsManager: NSObject {
72
72
 
73
73
  func updateRemoteCommands() {
74
74
  let commandCenter = MPRemoteCommandCenter.shared()
75
+
76
+ // update the enabled state to have correct visual representation in the lockscreen
75
77
  commandCenter.playCommand.isEnabled = !self.inAd && self.backgroundaudioConfig.enabled
76
78
  commandCenter.pauseCommand.isEnabled = !self.inAd && self.backgroundaudioConfig.enabled
77
79
  commandCenter.togglePlayPauseCommand.isEnabled = !self.inAd && self.backgroundaudioConfig.enabled
78
80
  commandCenter.stopCommand.isEnabled = !self.inAd && self.backgroundaudioConfig.enabled
79
- commandCenter.changePlaybackPositionCommand.isEnabled = !isLive && !self.inAd && self.backgroundaudioConfig.enabled
80
- commandCenter.skipForwardCommand.isEnabled = !isLive && !self.inAd && self.backgroundaudioConfig.enabled
81
- commandCenter.skipBackwardCommand.isEnabled = !isLive && !self.inAd && self.backgroundaudioConfig.enabled
81
+ commandCenter.changePlaybackPositionCommand.isEnabled = !self.isLive && !self.inAd && self.backgroundaudioConfig.enabled
82
+ commandCenter.skipForwardCommand.isEnabled = !self.isLive && !self.inAd && self.backgroundaudioConfig.enabled
83
+ commandCenter.skipBackwardCommand.isEnabled = !self.isLive && !self.inAd && self.backgroundaudioConfig.enabled
84
+
82
85
  if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Remote commands updated for \(self.isLive ? "LIVE" : "VOD") (\(self.inAd ? "AD IS PLAYING" : "NO AD PLAYING"), \(self.backgroundaudioConfig.enabled ? "BGAUDIO ENABLED" : "BGAUDIO DISABLED") ).") }
83
86
  }
84
87
 
85
88
  @objc private func onPlayCommand(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
86
89
  if let player = self.player,
90
+ !self.inAd,
87
91
  player.paused {
88
92
  player.play()
89
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Play command triggered.") }
90
- return .success
93
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Play command handled.") }
94
+ } else {
95
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Play command not handled.") }
91
96
  }
92
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Play command Failed.") }
93
- return .commandFailed
97
+ return .success
94
98
  }
95
99
 
96
100
  @objc private func onPauseCommand(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
97
101
  if let player = self.player,
102
+ !self.inAd,
98
103
  !player.paused {
99
104
  player.pause()
100
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Pause command triggered.") }
101
- return .success
105
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Pause command handled.") }
106
+ } else {
107
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Pause command not handled.") }
102
108
  }
103
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Pause command Failed.") }
104
- return .commandFailed
109
+ return .success
105
110
  }
106
111
 
107
112
  @objc private func onTogglePlayPauseCommand(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
108
- if let player = self.player {
113
+ if let player = self.player,
114
+ !self.inAd {
109
115
  if player.paused {
110
116
  player.play()
111
117
  } else {
112
118
  player.pause()
113
119
  }
114
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Toggle play/pause command triggered.") }
115
- return .success
120
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Toggle play/pause command handled.") }
121
+ } else {
122
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Toggle play/pause command not handled.") }
116
123
  }
117
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Toggle play/pause command Failed.") }
118
- return .commandFailed
124
+ return .success
119
125
  }
120
126
 
121
127
  @objc private func onStopCommand(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
122
- if let player = self.player {
128
+ if let player = self.player,
129
+ !self.inAd {
123
130
  if !player.paused {
124
131
  player.pause()
125
132
  }
126
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Stop command triggered.") }
127
- return .success
133
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Stop command handled.") }
134
+ } else {
135
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Stop command not handled.") }
128
136
  }
129
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Stop command Failed.") }
130
- return .commandFailed
137
+ return .success
131
138
  }
132
139
 
133
140
  @objc private func onScrubCommand(_ event: MPChangePlaybackPositionCommandEvent) -> MPRemoteCommandHandlerStatus {
134
- if let player = self.player {
141
+ if let player = self.player,
142
+ !self.isLive,
143
+ !self.inAd {
135
144
  player.setCurrentTime(event.positionTime)
136
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Scrub command triggered.") }
137
- return .success
145
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Scrub command handled.") }
146
+ } else {
147
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Scrub command not handled.") }
138
148
  }
139
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Scrub command Failed.") }
140
- return .commandFailed
149
+ return .success
141
150
  }
142
151
 
143
152
  @objc private func onSkipForwardCommand(_ event: MPSkipIntervalCommandEvent) -> MPRemoteCommandHandlerStatus {
144
- if let player = self.player {
153
+ if let player = self.player,
154
+ !self.isLive,
155
+ !self.inAd {
145
156
  player.requestCurrentTime(completionHandler: { time, error in
146
157
  if let currentTime = time {
147
158
  player.setCurrentTime(currentTime + event.interval)
148
159
  }
149
160
  })
150
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip forward command triggered.") }
151
- return .success
161
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip forward command handled.") }
162
+ } else {
163
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip forward command not handled.") }
152
164
  }
153
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip forward command Failed.") }
154
- return .commandFailed
165
+ return .success
155
166
  }
156
167
 
157
168
  @objc private func onSkipBackwardCommand(_ event: MPSkipIntervalCommandEvent) -> MPRemoteCommandHandlerStatus {
158
- if let player = self.player {
169
+ if let player = self.player ,
170
+ !self.isLive,
171
+ !self.inAd {
159
172
  player.requestCurrentTime(completionHandler: { time, error in
160
173
  if let currentTime = time {
161
174
  player.setCurrentTime(currentTime - event.interval)
162
175
  }
163
176
  })
164
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip backward command triggered.") }
165
- return .success
177
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip backward command handled.") }
178
+ } else {
179
+ if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip backward command not handled.") }
166
180
  }
167
- if DEBUG_REMOTECOMMANDS { PrintUtils.printLog(logText: "[NATIVE] Skip backward command Failed.") }
168
- return .commandFailed
181
+ return .success
169
182
  }
170
183
 
171
184
  private func attachListeners() {
@@ -193,11 +206,13 @@ class THEOplayerRCTRemoteCommandsManager: NSObject {
193
206
  // ADBREAK_BEGIN
194
207
  self.adBreakBeginListener = player.ads.addEventListener(type: AdsEventTypes.AD_BREAK_BEGIN) { [weak self] event in
195
208
  self?.inAd = true
209
+ self?.updateRemoteCommands()
196
210
  }
197
211
 
198
212
  // ADBREAK_END
199
213
  self.adBreakEndListener = player.ads.addEventListener(type: AdsEventTypes.AD_BREAK_END) { [weak self] event in
200
214
  self?.inAd = false
215
+ self?.updateRemoteCommands()
201
216
  }
202
217
 
203
218
  #endif
@@ -2,9 +2,6 @@
2
2
  // THEOplayerRCTAdsAPI.swift
3
3
  // Theoplayer
4
4
  //
5
- // Created by William van Haevre on 09/09/2022.
6
- // Copyright © 2022 Facebook. All rights reserved.
7
- //
8
5
 
9
6
  import Foundation
10
7
 
@@ -2,9 +2,6 @@
2
2
  // THEOplayerRCTContentProtectionAPI.swift
3
3
  // Theoplayer
4
4
  //
5
- // Created by William van Haevre on 09/09/2022.
6
- // Copyright © 2022 Facebook. All rights reserved.
7
- //
8
5
 
9
6
  import Foundation
10
7
  import UIKit