@jwplayer/jwplayer-react-native 1.2.0 → 1.3.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 (35) hide show
  1. package/README.md +114 -21
  2. package/RNJWPlayer.podspec +1 -1
  3. package/android/build.gradle +31 -5
  4. package/android/src/ima/java/com/jwplayer/rnjwplayer/ImaHelper.java +143 -0
  5. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerAds.java +41 -129
  6. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java +19 -4
  7. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +385 -105
  8. package/android/src/main/java/com/jwplayer/rnjwplayer/Util.java +13 -1
  9. package/android/src/noima/java/com/jwplayer/rnjwplayer/ImaHelper.java +24 -0
  10. package/badges/version.svg +1 -1
  11. package/docs/CONFIG-REFERENCE.md +747 -0
  12. package/docs/MIGRATION-GUIDE.md +617 -0
  13. package/docs/PLATFORM-DIFFERENCES.md +693 -0
  14. package/docs/props.md +15 -3
  15. package/index.d.ts +207 -249
  16. package/ios/RNJWPlayer/RNJWPlayerView.swift +278 -21
  17. package/ios/RNJWPlayer/RNJWPlayerViewController.swift +33 -16
  18. package/package.json +2 -2
  19. package/types/advertising.d.ts +514 -0
  20. package/types/index.d.ts +21 -0
  21. package/types/legacy.d.ts +82 -0
  22. package/types/platform-specific.d.ts +641 -0
  23. package/types/playlist.d.ts +410 -0
  24. package/types/unified-config.d.ts +591 -0
  25. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  26. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  27. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  28. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  29. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  30. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  31. package/android/.gradle/8.9/gc.properties +0 -0
  32. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  33. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  34. package/android/.gradle/vcs-1/gc.properties +0 -0
  35. package/docs/types.md +0 -254
@@ -2,21 +2,13 @@ package com.jwplayer.rnjwplayer;
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray;
4
4
  import com.facebook.react.bridge.ReadableMap;
5
- import com.google.ads.interactivemedia.v3.api.FriendlyObstruction;
6
- import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
7
- import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
8
5
  import com.jwplayer.pub.api.configuration.ads.AdRules;
9
6
  import com.jwplayer.pub.api.configuration.ads.AdvertisingConfig;
10
7
  import com.jwplayer.pub.api.configuration.ads.VastAdvertisingConfig;
11
- import com.jwplayer.pub.api.configuration.ads.dai.ImaDaiAdvertisingConfig;
12
- import com.jwplayer.pub.api.configuration.ads.ima.ImaAdvertisingConfig;
13
8
  import com.jwplayer.pub.api.media.ads.AdBreak;
14
- import com.jwplayer.pub.api.media.ads.dai.ImaDaiSettings;
15
9
 
16
10
  import java.util.ArrayList;
17
- import java.util.HashMap;
18
11
  import java.util.List;
19
- import java.util.Map;
20
12
  import java.util.Objects;
21
13
 
22
14
  public class RNJWPlayerAds {
@@ -27,103 +19,43 @@ public class RNJWPlayerAds {
27
19
  return null;
28
20
  }
29
21
 
30
- String adClientType = ads.getString("adClient");
22
+ // Check both "client" (JWPlayer JSON format) and "adClient" (RN wrapper format)
23
+ String adClientType = null;
24
+ if (ads.hasKey("adClient")) {
25
+ adClientType = ads.getString("adClient");
26
+ } else if (ads.hasKey("client")) {
27
+ adClientType = ads.getString("client");
28
+ }
29
+
30
+ // Validate client type exists and is not null
31
+ if (adClientType == null) {
32
+ throw new IllegalArgumentException("Missing required 'adClient' or 'client' field in advertising config");
33
+ }
34
+
35
+ // Normalize to lowercase for case-insensitive matching
36
+ adClientType = adClientType.toLowerCase();
37
+
31
38
  switch (adClientType) {
32
39
  case "ima":
33
- try {
34
- return configureImaAdvertising(ads);
35
- } catch (Exception e) {
36
- throw new RuntimeException(e);
37
- }
40
+ case "googima": // Support legacy "googima" format
38
41
  case "ima_dai":
42
+ // Delegate to ImaHelper (implementation selected by Gradle)
43
+ // Note: Only parse adSchedule for regular IMA, not for DAI (ads are embedded in stream)
44
+ List<AdBreak> adSchedule = ("ima".equals(adClientType) || "googima".equals(adClientType))
45
+ ? getAdSchedule(ads)
46
+ : new ArrayList<>();
39
47
  try {
40
- return configureImaDaiAdvertising(ads);
41
- } catch (Exception e) {
42
- throw new RuntimeException(e);
48
+ return ImaHelper.configureImaOrDai(ads, adSchedule);
49
+ } catch (RuntimeException e) {
50
+ // IMA not enabled - log error and return null (graceful degradation)
51
+ android.util.Log.e("RNJWPlayerAds", "Failed to configure IMA ads: " + e.getMessage());
52
+ return null;
43
53
  }
44
54
  default: // Defaulting to VAST
45
55
  return configureVastAdvertising(ads);
46
56
  }
47
57
  }
48
58
 
49
- // Configure IMA Advertising
50
- private static ImaAdvertisingConfig configureImaAdvertising(ReadableMap ads) throws Exception {
51
- if (!BuildConfig.USE_IMA) {
52
- throw new Exception("Error: Google ads services is not installed. Add RNJWPlayerUseGoogleIMA = true to your app/build.gradle ext {}");
53
- }
54
-
55
- ImaAdvertisingConfig.Builder builder = new ImaAdvertisingConfig.Builder();
56
-
57
- List<AdBreak> adScheduleList = getAdSchedule(ads);
58
- builder.schedule(adScheduleList);
59
-
60
- if (ads.hasKey("imaSettings")) {
61
- builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
62
- }
63
-
64
- // companionSlots
65
-
66
- return builder.build();
67
- }
68
-
69
- // Configure IMA DAI Advertising
70
- private static ImaDaiAdvertisingConfig configureImaDaiAdvertising(ReadableMap ads) throws Exception {
71
- if (!BuildConfig.USE_IMA) {
72
- throw new Exception("Error: Google ads services is not installed. Add RNJWPlayerUseGoogleIMA = true to your app/build.gradle ext {}");
73
- }
74
-
75
- ImaDaiAdvertisingConfig.Builder builder = new ImaDaiAdvertisingConfig.Builder();
76
-
77
- if (ads.hasKey("imaSettings")) {
78
- builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
79
- }
80
-
81
- if (ads.hasKey("imaDaiSettings")) {
82
- builder.imaDaiSettings(getImaDaiSettings(Objects.requireNonNull(ads.getMap("imaDaiSettings"))));
83
- }
84
-
85
- return builder.build();
86
- }
87
-
88
- // You'll need to implement this method based on how you pass ImaDaiSettings from React Native
89
- private static ImaDaiSettings getImaDaiSettings(ReadableMap imaDaiSettingsMap) {
90
- String videoId = imaDaiSettingsMap.hasKey("videoId") ? imaDaiSettingsMap.getString("videoId") : null;
91
- String cmsId = imaDaiSettingsMap.hasKey("cmsId") ? imaDaiSettingsMap.getString("cmsId") : null;
92
- String assetKey = imaDaiSettingsMap.hasKey("assetKey") ? imaDaiSettingsMap.getString("assetKey") : null;
93
- String apiKey = imaDaiSettingsMap.hasKey("apiKey") ? imaDaiSettingsMap.getString("apiKey") : null;
94
-
95
- // Extracting adTagParameters from imaDaiSettingsMap if present
96
- Map<String, String> adTagParameters = null;
97
- if (imaDaiSettingsMap.hasKey("adTagParameters") && imaDaiSettingsMap.getMap("adTagParameters") != null) {
98
- adTagParameters = new HashMap<>();
99
- ReadableMap adTagParamsMap = imaDaiSettingsMap.getMap("adTagParameters");
100
- for (Map.Entry<String, Object> entry : adTagParamsMap.toHashMap().entrySet()) {
101
- if (entry.getValue() instanceof String) {
102
- adTagParameters.put(entry.getKey(), (String) entry.getValue());
103
- }
104
- }
105
- }
106
-
107
- // Handling streamType
108
- ImaDaiSettings.StreamType streamType = ImaDaiSettings.StreamType.HLS; // Default to HLS
109
- if (imaDaiSettingsMap.hasKey("streamType")) {
110
- String streamTypeStr = imaDaiSettingsMap.getString("streamType");
111
- if ("DASH".equalsIgnoreCase(streamTypeStr)) {
112
- streamType = ImaDaiSettings.StreamType.DASH;
113
- }
114
- }
115
- // Create ImaDaiSettings based on the provided values
116
- ImaDaiSettings imaDaiSettings = (assetKey != null) ?
117
- new ImaDaiSettings(assetKey, streamType, apiKey) :
118
- new ImaDaiSettings(videoId, cmsId, streamType, apiKey);
119
-
120
- if (adTagParameters != null) {
121
- imaDaiSettings.setAdTagParameters(adTagParameters);
122
- }
123
-
124
- return imaDaiSettings;
125
- }
126
-
127
59
  // Configure VAST Advertising
128
60
  private static VastAdvertisingConfig configureVastAdvertising(ReadableMap ads) {
129
61
  VastAdvertisingConfig.Builder builder = new VastAdvertisingConfig.Builder();
@@ -154,9 +86,23 @@ public class RNJWPlayerAds {
154
86
 
155
87
  private static List<AdBreak> getAdSchedule(ReadableMap ads) {
156
88
  List<AdBreak> adScheduleList = new ArrayList<>();
89
+
90
+ // Check if adSchedule exists
91
+ if (!ads.hasKey("adSchedule")) {
92
+ return adScheduleList; // Return empty list
93
+ }
94
+
157
95
  ReadableArray adSchedule = ads.getArray("adSchedule");
96
+ if (adSchedule == null) {
97
+ return adScheduleList; // Return empty list if null
98
+ }
99
+
158
100
  for (int i = 0; i < adSchedule.size(); i++) {
159
101
  ReadableMap adBreakProp = adSchedule.getMap(i);
102
+ // Skip null entries in the adSchedule array
103
+ if (adBreakProp == null) {
104
+ continue;
105
+ }
160
106
  String offset = adBreakProp.hasKey("offset") ? adBreakProp.getString("offset") : "pre";
161
107
  if (adBreakProp.hasKey("tag")) {
162
108
  AdBreak adBreak = new AdBreak.Builder()
@@ -202,38 +148,4 @@ public class RNJWPlayerAds {
202
148
  return AdRules.RULES_START_ON_SEEK_NONE;
203
149
  }
204
150
 
205
- // public static List<FriendlyObstruction> getFriendlyObstructions(ReadableArray obstructionsArray) {
206
- // List<FriendlyObstruction> obstructions = new ArrayList<>();
207
- // // Example: Parse and create FriendlyObstruction objects from obstructionsArray
208
- // return obstructions;
209
- // }
210
-
211
- public static ImaSdkSettings getImaSettings(ReadableMap imaSettingsMap) {
212
- ImaSdkSettings settings = ImaSdkFactory.getInstance().createImaSdkSettings();
213
-
214
- if (imaSettingsMap.hasKey("maxRedirects")) {
215
- settings.setMaxRedirects(imaSettingsMap.getInt("maxRedirects"));
216
- }
217
- if (imaSettingsMap.hasKey("language")) {
218
- settings.setLanguage(imaSettingsMap.getString("language"));
219
- }
220
- if (imaSettingsMap.hasKey("ppid")) {
221
- settings.setPpid(imaSettingsMap.getString("ppid"));
222
- }
223
- if (imaSettingsMap.hasKey("playerType")) {
224
- settings.setPlayerType(imaSettingsMap.getString("playerType"));
225
- }
226
- if (imaSettingsMap.hasKey("playerVersion")) {
227
- settings.setPlayerVersion(imaSettingsMap.getString("playerVersion"));
228
- }
229
- if (imaSettingsMap.hasKey("sessionId")) {
230
- settings.setSessionId(imaSettingsMap.getString("sessionId"));
231
- }
232
- if (imaSettingsMap.hasKey("debugMode")) {
233
- settings.setDebugMode(imaSettingsMap.getBoolean("debugMode"));
234
- }
235
- // Add other settings as needed
236
-
237
- return settings;
238
- }
239
151
  }
@@ -434,12 +434,27 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
434
434
 
435
435
  @ReactMethod
436
436
  /**
437
- * Stub method for recreatePlayerWithConfig - this method is iOS only
438
- * On Android, create a new player instance with the new configuration instead
437
+ * Reconfigures or recreates the player with a new configuration.
438
+ *
439
+ * This method intelligently determines whether to:
440
+ * 1. Reconfigure the existing player (preferred, ~90% of cases)
441
+ * 2. Recreate the player (only when necessary, e.g., license changes)
442
+ *
443
+ * Note: Despite the name "recreate", this method usually does NOT recreate the player.
444
+ * It follows the JWPlayer SDK's design intent of reusing player instances via setup() calls.
445
+ *
446
+ * This provides API parity with iOS while being more efficient on Android.
439
447
  */
440
448
  public void recreatePlayerWithConfig(final int reactTag, final ReadableMap config) {
441
- // No-op on Android - this method is iOS only
442
- Log.w("RNJWPlayer", "recreatePlayerWithConfig is not supported on Android. Create a new player instance with the new configuration instead.");
449
+ new Handler(Looper.getMainLooper()).post(() -> {
450
+ RNJWPlayerView playerView = getPlayerView(reactTag);
451
+ if (playerView != null) {
452
+ // Delegate to setConfig() which intelligently handles reconfiguration vs recreation
453
+ playerView.setConfig(config);
454
+ } else {
455
+ Log.e("RNJWPlayer", "recreatePlayerWithConfig: Player view not found for tag " + reactTag);
456
+ }
457
+ });
443
458
  }
444
459
 
445
460
  private int stateToInt(PlayerState playerState) {