@jwplayer/jwplayer-react-native 1.0.3 → 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.
@@ -40,6 +40,7 @@ import com.facebook.react.bridge.LifecycleEventListener;
40
40
  import com.facebook.react.bridge.ReactApplicationContext;
41
41
  import com.facebook.react.bridge.ReadableArray;
42
42
  import com.facebook.react.bridge.ReadableMap;
43
+ import com.facebook.react.bridge.ReadableType;
43
44
  import com.facebook.react.bridge.WritableArray;
44
45
  import com.facebook.react.bridge.WritableMap;
45
46
  import com.facebook.react.common.MapBuilder;
@@ -156,6 +157,7 @@ public class RNJWPlayerView extends RelativeLayout implements
156
157
  VideoPlayerEvents.OnCaptionsListListener,
157
158
  VideoPlayerEvents.OnCaptionsChangedListener,
158
159
  VideoPlayerEvents.OnMetaListener,
160
+ VideoPlayerEvents.PlaylistItemCallbackListener,
159
161
 
160
162
  CastingEvents.OnCastListener,
161
163
 
@@ -236,6 +238,9 @@ public class RNJWPlayerView extends RelativeLayout implements
236
238
  private MediaServiceController mMediaServiceController;
237
239
  private PipHandlerReceiver mReceiver = null;
238
240
 
241
+ // Add completion handler field
242
+ PlaylistItemDecision itemUpdatePromise = null;
243
+
239
244
  private void doBindService() {
240
245
  if (mMediaServiceController != null) {
241
246
  if (!isBackgroundAudioServiceRunning()) {
@@ -379,6 +384,9 @@ public class RNJWPlayerView extends RelativeLayout implements
379
384
  mActivity.getLifecycle().removeObserver(lifecycleObserver);
380
385
  mPlayer.deregisterActivityForPip();
381
386
 
387
+ // Remove playlist item callback listener
388
+ mPlayer.removePlaylistItemCallbackListener();
389
+
382
390
  mPlayer.removeListeners(this,
383
391
  // VideoPlayerEvents
384
392
  EventType.READY,
@@ -456,7 +464,7 @@ public class RNJWPlayerView extends RelativeLayout implements
456
464
  }
457
465
  }
458
466
 
459
- public void setupPlayerView(Boolean backgroundAudioEnabled) {
467
+ public void setupPlayerView(Boolean backgroundAudioEnabled, Boolean playlistItemCallbackEnabled) {
460
468
  if (mPlayer != null) {
461
469
 
462
470
  mPlayer.addListeners(this,
@@ -470,6 +478,8 @@ public class RNJWPlayerView extends RelativeLayout implements
470
478
  EventType.SETUP_ERROR,
471
479
  EventType.BUFFER,
472
480
  EventType.TIME,
481
+ EventType.AUDIO_TRACKS,
482
+ EventType.AUDIO_TRACK_CHANGED,
473
483
  EventType.PLAYLIST,
474
484
  EventType.PLAYLIST_ITEM,
475
485
  EventType.PLAYLIST_COMPLETE,
@@ -519,9 +529,33 @@ public class RNJWPlayerView extends RelativeLayout implements
519
529
  } else {
520
530
  mPlayer.setFullscreenHandler(new fullscreenHandler());
521
531
  }
522
-
523
532
  mPlayer.allowBackgroundAudio(backgroundAudioEnabled);
533
+
534
+ if (playlistItemCallbackEnabled) {
535
+ mPlayer.setPlaylistItemCallbackListener(this);
536
+ }
537
+ }
538
+ }
539
+
540
+ public void resolveNextPlaylistItem(ReadableMap playlistItem) {
541
+ if (itemUpdatePromise == null) {
542
+ return;
543
+ }
544
+
545
+ if (playlistItem == null) {
546
+ itemUpdatePromise.continuePlayback();
547
+ itemUpdatePromise = null;
548
+ return;
524
549
  }
550
+
551
+ try {
552
+ PlaylistItem updatedPlaylistItem = Util.getPlaylistItem(playlistItem);
553
+ itemUpdatePromise.modify(updatedPlaylistItem);
554
+ } catch (Exception exception) {
555
+ itemUpdatePromise.continuePlayback();
556
+ }
557
+
558
+ itemUpdatePromise = null;
525
559
  }
526
560
 
527
561
  /**
@@ -625,6 +659,18 @@ public class RNJWPlayerView extends RelativeLayout implements
625
659
  return delegate;
626
660
  }
627
661
 
662
+ @Override
663
+ public void onBeforeNextPlaylistItem(PlaylistItemDecision playlistItemDecision, PlaylistItem nextItem, int indexOfNextItem) {
664
+ WritableMap event = Arguments.createMap();
665
+ Gson gson = new Gson();
666
+ event.putString("message", "onBeforeNextPlaylistItem");
667
+ event.putInt("index", indexOfNextItem);
668
+ event.putString("playlistItem", gson.toJson(nextItem));
669
+ getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topBeforeNextPlaylistItem", event);
670
+
671
+ itemUpdatePromise = playlistItemDecision;
672
+ }
673
+
628
674
  private class fullscreenHandler implements FullscreenHandler {
629
675
  ViewGroup mPlayerViewContainer = (ViewGroup) mPlayerView.getParent();
630
676
  private View mDecorView;
@@ -901,7 +947,7 @@ public class RNJWPlayerView extends RelativeLayout implements
901
947
  return differences.size() == 1 && differences.containsKey(keyName);
902
948
  }
903
949
 
904
- boolean playlistNotTheSame(ReadableMap prop) {
950
+ private boolean playlistNotTheSame(ReadableMap prop) {
905
951
  return prop.hasKey("playlist") && mPlaylistProp != prop.getArray("playlist") && !Arrays
906
952
  .deepEquals(new ReadableArray[]{mPlaylistProp}, new ReadableArray[]{prop.getArray("playlist")});
907
953
  }
@@ -913,6 +959,7 @@ public class RNJWPlayerView extends RelativeLayout implements
913
959
  JSONObject obj;
914
960
  PlayerConfig jwConfig = null;
915
961
  Boolean forceLegacy = prop.hasKey("forceLegacyConfig") ? prop.getBoolean("forceLegacyConfig") : false;
962
+ Boolean playlistItemCallbackEnabled = prop.hasKey("playlistItemCallbackEnabled") ? prop.getBoolean("playlistItemCallbackEnabled") : false;
916
963
  Boolean isJwConfig = false;
917
964
  if (!forceLegacy) {
918
965
  try {
@@ -1019,9 +1066,11 @@ public class RNJWPlayerView extends RelativeLayout implements
1019
1066
  ReadableArray uiGroupsArray = prop.getArray("hideUIGroups");
1020
1067
  UiConfig.Builder hideConfigBuilder = new UiConfig.Builder().displayAllControls();
1021
1068
  for (int i = 0; i < uiGroupsArray.size(); i++) {
1022
- UiGroup uiGroup = GROUP_TYPES.get(uiGroupsArray.getString(i));
1023
- if (uiGroup != null) {
1024
- hideConfigBuilder.hide(uiGroup);
1069
+ if (uiGroupsArray.getType(i) == ReadableType.String) {
1070
+ UiGroup uiGroup = GROUP_TYPES.get(uiGroupsArray.getString(i));
1071
+ if (uiGroup != null) {
1072
+ hideConfigBuilder.hide(uiGroup);
1073
+ }
1025
1074
  }
1026
1075
  }
1027
1076
  UiConfig hideJwControlbarUiConfig = hideConfigBuilder.build();
@@ -1046,7 +1095,7 @@ public class RNJWPlayerView extends RelativeLayout implements
1046
1095
  addView(mPlayerView);
1047
1096
 
1048
1097
  // Ensure we have a valid state before applying to the player
1049
- registry.setCurrentState(Lifecycle.State.STARTED);
1098
+ registry.setCurrentState(registry.getCurrentState()); // This is a hack to ensure player and view know the lifecycle state
1050
1099
 
1051
1100
  mPlayer = mPlayerView.getPlayer(this);
1052
1101
 
@@ -1150,7 +1199,7 @@ public class RNJWPlayerView extends RelativeLayout implements
1150
1199
  backgroundAudioEnabled = prop.getBoolean("backgroundAudioEnabled");
1151
1200
  }
1152
1201
 
1153
- setupPlayerView(backgroundAudioEnabled);
1202
+ setupPlayerView(backgroundAudioEnabled, playlistItemCallbackEnabled);
1154
1203
 
1155
1204
  if (backgroundAudioEnabled) {
1156
1205
  audioManager = (AudioManager) simpleContext.getSystemService(Context.AUDIO_SERVICE);
@@ -1312,7 +1361,7 @@ public class RNJWPlayerView extends RelativeLayout implements
1312
1361
  public void onAdLoaded(AdLoadedEvent adLoadedEvent) {
1313
1362
  WritableMap event = Arguments.createMap();
1314
1363
  event.putString("message", "onAdEvent");
1315
- event.putString("client", adLoadedEvent.getClient().toString());
1364
+ event.putInt("client", Util.getAdEventClientValue(adLoadedEvent));
1316
1365
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1317
1366
  }
1318
1367
 
@@ -1320,7 +1369,7 @@ public class RNJWPlayerView extends RelativeLayout implements
1320
1369
  public void onAdLoadedXml(AdLoadedXmlEvent adLoadedXmlEvent) {
1321
1370
  WritableMap event = Arguments.createMap();
1322
1371
  event.putString("message", "onAdEvent");
1323
- event.putString("client", adLoadedXmlEvent.getClient().toString());
1372
+ event.putInt("client", Util.getAdEventClientValue(adLoadedXmlEvent));
1324
1373
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1325
1374
  }
1326
1375
 
@@ -1329,7 +1378,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1329
1378
  WritableMap event = Arguments.createMap();
1330
1379
  event.putString("message", "onAdEvent");
1331
1380
  event.putString("reason", adPauseEvent.getAdPauseReason().toString());
1332
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypePause));
1381
+ event.putInt("client", Util.getAdEventClientValue(adPauseEvent));
1382
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypePause));
1333
1383
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1334
1384
  }
1335
1385
 
@@ -1338,7 +1388,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1338
1388
  WritableMap event = Arguments.createMap();
1339
1389
  event.putString("message", "onAdEvent");
1340
1390
  event.putString("reason", adPlayEvent.getAdPlayReason().toString());
1341
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypePlay));
1391
+ event.putInt("client", Util.getAdEventClientValue(adPlayEvent));
1392
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypePlay));
1342
1393
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1343
1394
  }
1344
1395
 
@@ -1346,8 +1397,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1346
1397
  public void onAdBreakEnd(AdBreakEndEvent adBreakEndEvent) {
1347
1398
  WritableMap event = Arguments.createMap();
1348
1399
  event.putString("message", "onAdEvent");
1349
- event.putString("client", adBreakEndEvent.getClient().toString());
1350
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeAdBreakEnd));
1400
+ event.putInt("client", Util.getAdEventClientValue(adBreakEndEvent));
1401
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeAdBreakEnd));
1351
1402
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1352
1403
  }
1353
1404
 
@@ -1355,8 +1406,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1355
1406
  public void onAdBreakStart(AdBreakStartEvent adBreakStartEvent) {
1356
1407
  WritableMap event = Arguments.createMap();
1357
1408
  event.putString("message", "onAdEvent");
1358
- event.putString("client", adBreakStartEvent.getClient().toString());
1359
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeAdBreakStart));
1409
+ event.putInt("client", Util.getAdEventClientValue(adBreakStartEvent));
1410
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeAdBreakStart));
1360
1411
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1361
1412
  }
1362
1413
 
@@ -1364,7 +1415,7 @@ public class RNJWPlayerView extends RelativeLayout implements
1364
1415
  public void onAdBreakIgnored(AdBreakIgnoredEvent adBreakIgnoredEvent) {
1365
1416
  WritableMap event = Arguments.createMap();
1366
1417
  event.putString("message", "onAdEvent");
1367
- event.putString("client", adBreakIgnoredEvent.getClient().toString());
1418
+ event.putInt("client", Util.getAdEventClientValue(adBreakIgnoredEvent));
1368
1419
  // missing type code
1369
1420
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1370
1421
  }
@@ -1373,8 +1424,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1373
1424
  public void onAdClick(AdClickEvent adClickEvent) {
1374
1425
  WritableMap event = Arguments.createMap();
1375
1426
  event.putString("message", "onAdEvent");
1376
- event.putString("client", adClickEvent.getClient().toString());
1377
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeClicked));
1427
+ event.putInt("client", Util.getAdEventClientValue(adClickEvent));
1428
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeClicked));
1378
1429
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1379
1430
  }
1380
1431
 
@@ -1382,7 +1433,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1382
1433
  public void onAdCompanions(AdCompanionsEvent adCompanionsEvent) {
1383
1434
  WritableMap event = Arguments.createMap();
1384
1435
  event.putString("message", "onAdEvent");
1385
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeCompanion));
1436
+ event.putInt("client", Util.getAdEventClientValue(adCompanionsEvent));
1437
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeCompanion));
1386
1438
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1387
1439
  }
1388
1440
 
@@ -1390,8 +1442,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1390
1442
  public void onAdComplete(AdCompleteEvent adCompleteEvent) {
1391
1443
  WritableMap event = Arguments.createMap();
1392
1444
  event.putString("message", "onAdEvent");
1393
- event.putString("client", adCompleteEvent.getClient().toString());
1394
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeComplete));
1445
+ event.putInt("client", Util.getAdEventClientValue(adCompleteEvent));
1446
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeComplete));
1395
1447
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1396
1448
  }
1397
1449
 
@@ -1419,8 +1471,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1419
1471
  public void onAdImpression(AdImpressionEvent adImpressionEvent) {
1420
1472
  WritableMap event = Arguments.createMap();
1421
1473
  event.putString("message", "onAdEvent");
1422
- event.putString("client", adImpressionEvent.getClient().toString());
1423
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeImpression));
1474
+ event.putInt("client", Util.getAdEventClientValue(adImpressionEvent));
1475
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeImpression));
1424
1476
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1425
1477
  }
1426
1478
 
@@ -1428,8 +1480,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1428
1480
  public void onAdMeta(AdMetaEvent adMetaEvent) {
1429
1481
  WritableMap event = Arguments.createMap();
1430
1482
  event.putString("message", "onAdEvent");
1431
- event.putString("client", adMetaEvent.getClient().toString());
1432
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeMeta));
1483
+ event.putInt("client", Util.getAdEventClientValue(adMetaEvent));
1484
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeMeta));
1433
1485
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1434
1486
  }
1435
1487
 
@@ -1437,8 +1489,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1437
1489
  public void onAdRequest(AdRequestEvent adRequestEvent) {
1438
1490
  WritableMap event = Arguments.createMap();
1439
1491
  event.putString("message", "onAdEvent");
1440
- event.putString("client", adRequestEvent.getClient().toString());
1441
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeRequest));
1492
+ event.putInt("client", Util.getAdEventClientValue(adRequestEvent));
1493
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeRequest));
1442
1494
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1443
1495
  }
1444
1496
 
@@ -1446,8 +1498,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1446
1498
  public void onAdSchedule(AdScheduleEvent adScheduleEvent) {
1447
1499
  WritableMap event = Arguments.createMap();
1448
1500
  event.putString("message", "onAdEvent");
1449
- event.putString("client", adScheduleEvent.getClient().toString());
1450
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeSchedule));
1501
+ event.putInt("client", Util.getAdEventClientValue(adScheduleEvent));
1502
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeSchedule));
1451
1503
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1452
1504
  }
1453
1505
 
@@ -1455,8 +1507,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1455
1507
  public void onAdSkipped(AdSkippedEvent adSkippedEvent) {
1456
1508
  WritableMap event = Arguments.createMap();
1457
1509
  event.putString("message", "onAdEvent");
1458
- event.putString("client", adSkippedEvent.getClient().toString());
1459
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeSkipped));
1510
+ event.putInt("client", Util.getAdEventClientValue(adSkippedEvent));
1511
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeSkipped));
1460
1512
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1461
1513
  }
1462
1514
 
@@ -1464,7 +1516,8 @@ public class RNJWPlayerView extends RelativeLayout implements
1464
1516
  public void onAdStarted(AdStartedEvent adStartedEvent) {
1465
1517
  WritableMap event = Arguments.createMap();
1466
1518
  event.putString("message", "onAdEvent");
1467
- event.putInt("type", Util.getEventTypeValue(Util.AdEventType.JWAdEventTypeStarted));
1519
+ event.putInt("client", Util.getAdEventClientValue(adStartedEvent));
1520
+ event.putInt("type", Util.getAdEventTypeValue(Util.AdEventType.JWAdEventTypeStarted));
1468
1521
  getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topAdEvent", event);
1469
1522
  }
1470
1523
 
@@ -1,4 +1,3 @@
1
-
2
1
  package com.jwplayer.rnjwplayer;
3
2
 
4
3
  import com.facebook.react.bridge.ReactApplicationContext;
@@ -41,6 +40,9 @@ public class RNJWPlayerViewManager extends SimpleViewManager<RNJWPlayerView> {
41
40
 
42
41
  @ReactProp(name = "controls")
43
42
  public void setControls(RNJWPlayerView view, Boolean controls) {
43
+ if (view == null || view.mPlayerView == null) {
44
+ return;
45
+ }
44
46
  view.mPlayerView.getPlayer().setControls(controls);
45
47
  }
46
48
 
@@ -171,6 +173,10 @@ public class RNJWPlayerViewManager extends SimpleViewManager<RNJWPlayerView> {
171
173
  MapBuilder.of(
172
174
  "phasedRegistrationNames",
173
175
  MapBuilder.of("bubbled", "onLoaded")))
176
+ .put("topBeforeNextPlaylistItem",
177
+ MapBuilder.of(
178
+ "phasedRegistrationNames",
179
+ MapBuilder.of("bubbled", "onBeforeNextPlaylistItem")))
174
180
  .build();
175
181
  }
176
182
 
@@ -9,6 +9,8 @@ import android.webkit.URLUtil;
9
9
  import com.facebook.react.bridge.ReadableArray;
10
10
  import com.facebook.react.bridge.ReadableMap;
11
11
  import com.jwplayer.pub.api.JsonHelper;
12
+ import com.jwplayer.pub.api.configuration.ads.AdvertisingConfig;
13
+ import com.jwplayer.pub.api.events.Event;
12
14
  import com.jwplayer.pub.api.media.ads.AdBreak;
13
15
  import com.jwplayer.pub.api.media.captions.Caption;
14
16
  import com.jwplayer.pub.api.media.captions.CaptionType;
@@ -253,7 +255,42 @@ public class Util {
253
255
  }
254
256
 
255
257
  // Method to get the event type value
256
- public static int getEventTypeValue(AdEventType eventType) {
258
+ public static int getAdEventTypeValue(AdEventType eventType) {
257
259
  return eventType.getValue();
258
260
  }
259
- }
261
+
262
+ public enum AdEventClient {
263
+ JWAdEventClientJWPlayer(0),
264
+ JWAdEventClientGoogleIMA(1),
265
+ JWAdEventClientGoogleIMADAI(2),
266
+ JWAdEventClientUnknown(3);
267
+
268
+ private final int value;
269
+
270
+ AdEventClient(int value) {
271
+ this.value = value;
272
+ }
273
+
274
+ public int getValue() {
275
+ return value;
276
+ }
277
+ }
278
+
279
+ public static int getAdEventClientValue(Event adEvent) {
280
+ AdvertisingConfig adConfig = adEvent.getPlayer().getConfig().getAdvertisingConfig();
281
+ if (adConfig == null) {
282
+ return AdEventClient.JWAdEventClientUnknown.getValue();
283
+ }
284
+
285
+ switch (adConfig.getAdClient()) {
286
+ case IMA:
287
+ return AdEventClient.JWAdEventClientGoogleIMA.getValue();
288
+ case IMA_DAI:
289
+ return AdEventClient.JWAdEventClientGoogleIMADAI.getValue();
290
+ case VAST:
291
+ return AdEventClient.JWAdEventClientJWPlayer.getValue();
292
+ default:
293
+ return AdEventClient.JWAdEventClientUnknown.getValue();
294
+ }
295
+ }
296
+ }
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="version: 1.0.3"><title>version: 1.0.3</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="39" height="20" fill="#007ec6"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">version</text><text aria-hidden="true" x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">1.0.3</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.0.3</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="version: 1.1.1"><title>version: 1.1.1</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="39" height="20" fill="#007ec6"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">version</text><text aria-hidden="true" x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">1.1.1</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.1.1</text></g></svg>
package/index.d.ts CHANGED
@@ -6,6 +6,10 @@ declare module "@jwplayer/jwplayer-react-native" {
6
6
  pid?: string;
7
7
  mute?: boolean;
8
8
  forceLegacyConfig?: boolean;
9
+ /**
10
+ * If true, `onBeforeNextPlaylistItem` MUST be impelemented with `player.resolveNextPlaylistItem()` called in the callback or content will hang
11
+ */
12
+ playlistItemCallbackEnabled?: boolean;
9
13
  useTextureView?: boolean;
10
14
  autostart?: boolean;
11
15
  nextupoffset?: string | number; // String with % or number
@@ -518,7 +522,7 @@ declare module "@jwplayer/jwplayer-react-native" {
518
522
  adErrorCode?: number; // Android only
519
523
  }
520
524
  interface AdEventProps {
521
- client?: string;
525
+ client: number;
522
526
  reason?: string;
523
527
  type: number;
524
528
  }
@@ -570,6 +574,7 @@ declare module "@jwplayer/jwplayer-react-native" {
570
574
  onCaptionsList?: (event: BaseEvent<CaptionsListEventProps>) => void;
571
575
  onAudioTracks?: () => void;
572
576
  shouldComponentUpdate?: (nextProps: any, nextState: any) => boolean;
577
+ onBeforeNextPlaylistItem?: (event: BaseEvent<PlaylistItemEventProps>) => void;
573
578
  }
574
579
 
575
580
  export default class JWPlayer extends React.Component<PropsType> {
@@ -586,7 +591,16 @@ declare module "@jwplayer/jwplayer-react-native" {
586
591
  setControls(show: boolean): void;
587
592
  setLockScreenControls(show: boolean): void;
588
593
  seekTo(time: number): void;
589
- loadPlaylist(playlistItems: PlaylistItem[] | JwPlaylistItem[] | string): void;
594
+ /**
595
+ * Side load playlist items into an already setup player
596
+ * @param playlistItems `PlaylistItem` or `JwPlaylistItem`
597
+ */
598
+ loadPlaylist(playlistItems: PlaylistItem[] | JwPlaylistItem[]): void;
599
+ /**
600
+ * Side load playlist via URL into an already setup player
601
+ * @param playlistUrl URL for playlist to load (format for response: json)
602
+ */
603
+ loadPlaylistWithUrl(playlistUrl: string): void;
590
604
  setFullscreen(fullScreen: boolean): void;
591
605
  position(): Promise<number>;
592
606
  setUpCastController(): void;
@@ -601,5 +615,10 @@ declare module "@jwplayer/jwplayer-react-native" {
601
615
  setCurrentCaptions(index: number): void;
602
616
  getCurrentCaptions(): Promise<number | null>;
603
617
  setVisibility(visibility: boolean, controls: JWControlType[]): void;
618
+ /**
619
+ * Only called inside `onBeforeNextPlaylistItem` callback, and once per callback
620
+ * @param playlistItem `PlaylistItem` or `JwPlaylistItem`
621
+ */
622
+ resolveNextPlaylistItem(playlistItem: PlaylistItem | JwPlaylistItem): void;
604
623
  }
605
624
  }
package/index.js CHANGED
@@ -174,6 +174,12 @@ export default class JWPlayer extends Component {
174
174
  config: PropTypes.shape({
175
175
  license: PropTypes.string.isRequired,
176
176
  forceLegacyConfig: PropTypes.bool,
177
+ /**
178
+ * Only enable if you are prepared to implement `onBeforeNextPlaylistItem` on the JS side
179
+ * And resolve the promise with `resolveNextPlaylistItem` inside the callback
180
+ * Otherwise, the player will not proceed to the next item
181
+ */
182
+ playlistItemCallbackEnabled: PropTypes.bool,
177
183
  backgroundAudioEnabled: PropTypes.bool,
178
184
  category: PropTypes.oneOf([
179
185
  'Ambient',
@@ -374,6 +380,16 @@ export default class JWPlayer extends Component {
374
380
  onCaptionsChanged: PropTypes.func,
375
381
  onCaptionsList: PropTypes.func,
376
382
  onAudioTracks: PropTypes.func,
383
+ /**
384
+ * Callback that is fired when the player is about to play the next playlist item.
385
+ * Indented to be paired with `playlistItemCallbackEnabled` prop
386
+ *
387
+ * Android will only fire after index 0 (starting at index 1)
388
+ *
389
+ * iOS will fire all playlist items (including index 0)
390
+ */
391
+ onBeforeNextPlaylistItem: PropTypes.func,
392
+ resolveNextPlaylistItem: PropTypes.func
377
393
  };
378
394
 
379
395
  constructor(props) {
@@ -381,8 +397,7 @@ export default class JWPlayer extends Component {
381
397
 
382
398
  this._playerId = playerId++;
383
399
  this.ref_key = `${RCT_RNJWPLAYER_REF}-${this._playerId}`;
384
-
385
- this.quite();
400
+
386
401
  }
387
402
 
388
403
  shouldComponentUpdate(nextProps, nextState) {
@@ -520,6 +535,11 @@ export default class JWPlayer extends Component {
520
535
  RNJWPlayerManager.loadPlaylist(this.getRNJWPlayerBridgeHandle(), playlistItems);
521
536
  }
522
537
 
538
+ loadPlaylistWithUrl(playlistUrl) {
539
+ if (RNJWPlayerManager)
540
+ RNJWPlayerManager.loadPlaylistWithUrl(this.getRNJWPlayerBridgeHandle(), playlistUrl);
541
+ }
542
+
523
543
  setFullscreen(fullscreen) {
524
544
  if (RNJWPlayerManager)
525
545
  RNJWPlayerManager.setFullscreen(
@@ -707,6 +727,22 @@ export default class JWPlayer extends Component {
707
727
  }
708
728
  }
709
729
 
730
+ /**
731
+ * Only to be called in the onBeforeNextPlaylistItem callback.
732
+ * If called outside of the callback, it will not have any effect.
733
+ *
734
+ * Can only be called once inside the onBeforeNextPlaylistItem callback.
735
+ * @param {PlaylistItem | JwPlaylistItem} playlistItem
736
+ */
737
+ resolveNextPlaylistItem(playlistItem) {
738
+ if (RNJWPlayerManager) {
739
+ RNJWPlayerManager.resolveNextPlaylistItem(
740
+ this.getRNJWPlayerBridgeHandle(),
741
+ playlistItem
742
+ );
743
+ }
744
+ }
745
+
710
746
  getRNJWPlayerBridgeHandle() {
711
747
  return findNodeHandle(this[this.ref_key]);
712
748
  }
@@ -16,7 +16,12 @@ import JWPlayerKit
16
16
  import GoogleCast
17
17
  #endif
18
18
 
19
- class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDelegate, JWAVDelegate, JWPlayerViewDelegate, JWPlayerViewControllerDelegate, JWDRMContentKeyDataSource, JWTimeEventListener, AVPictureInPictureControllerDelegate {
19
+ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
20
+ JWAdDelegate, JWAVDelegate, JWPlayerViewDelegate,
21
+ JWPlayerViewControllerFullScreenDelegate, JWPlayerViewControllerUIDelegate,
22
+ JWPlayerViewControllerRelatedDelegate, JWDRMContentKeyDataSource,
23
+ JWTimeEventListener, AVPictureInPictureControllerDelegate
24
+ {
20
25
 
21
26
  // MARK: - RNJWPlayer allocation
22
27
 
@@ -41,6 +46,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
41
46
  var castController: JWCastController!
42
47
  var isCasting: Bool = false
43
48
  var availableDevices: [AnyObject]!
49
+ var onBeforeNextPlaylistItemCompletion: ((JWPlayerItem?) -> ())?
44
50
 
45
51
  @objc var onBuffer: RCTDirectEventBlock?
46
52
  @objc var onUpdateBuffer: RCTDirectEventBlock?
@@ -87,6 +93,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
87
93
  @objc var onCastingFailed: RCTDirectEventBlock?
88
94
  @objc var onCaptionsChanged: RCTDirectEventBlock?
89
95
  @objc var onCaptionsList: RCTDirectEventBlock?
96
+ @objc var onBeforeNextPlaylistItem: RCTDirectEventBlock?
90
97
 
91
98
  init() {
92
99
  super.init(frame: CGRect(x: 20, y: 0, width: UIScreen.main.bounds.width - 40, height: 300))
@@ -283,6 +290,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
283
290
 
284
291
  func setNewConfig(config: [String : Any]) {
285
292
  let forceLegacyConfig = config["forceLegacyConfig"] as? Bool?
293
+ let playlistItemCallback = config["playlistItemCallbackEnabled"] as? Bool?
286
294
  let data:Data! = try? JSONSerialization.data(withJSONObject: config, options:.prettyPrinted)
287
295
  let jwConfig = try? JWJSONParser.config(from:data)
288
296
 
@@ -348,6 +356,11 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
348
356
  self.setupPlayerViewController(config: config, playerConfig: jwConfig!)
349
357
  }
350
358
  }
359
+
360
+ if playlistItemCallback == true {
361
+ self.setupPlaylistItemCallback()
362
+ }
363
+
351
364
  } catch {
352
365
  print(error)
353
366
  }
@@ -362,6 +375,47 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
362
375
  }
363
376
  }
364
377
 
378
+ func setupPlaylistItemCallback() {
379
+ playerViewController.player.setPlaylistItemCallback { [weak self] item, index, completion in
380
+ print("setPlaylistItemCallback called with index \(index)")
381
+ guard let self = self else {
382
+ print("setPlaylistItemCallback: self is nil, resuming normally")
383
+ completion(item)
384
+ return
385
+ }
386
+
387
+ if let onBeforeNextPlaylistItem = self.onBeforeNextPlaylistItem {
388
+ print("Storing completion handler and triggering onBeforeNextPlaylistItem")
389
+ // Store the completion handler first, before any other operations
390
+ self.onBeforeNextPlaylistItemCompletion = completion
391
+ print("Completion handler stored: \(self.onBeforeNextPlaylistItemCompletion != nil)")
392
+
393
+ do {
394
+ let data = try JSONSerialization.data(withJSONObject: item.toJSONObject(), options: [.prettyPrinted])
395
+ let jsonString = String(data: data, encoding: .utf8) ?? "{}"
396
+
397
+ print("Triggering onBeforeNextPlaylistItem with index \(index)")
398
+ print("Completion handler before event: \(self.onBeforeNextPlaylistItemCompletion != nil)")
399
+
400
+ // Pass the playlist item to the React Native side
401
+ onBeforeNextPlaylistItem([
402
+ "playlistItem": jsonString,
403
+ "index": index
404
+ ])
405
+ print("Completion handler after event: \(self.onBeforeNextPlaylistItemCompletion != nil)")
406
+
407
+ } catch {
408
+ print("Error serializing playlist item: \(error)")
409
+ self.onBeforeNextPlaylistItemCompletion?(item) // Call completion handler directly on error
410
+ self.onBeforeNextPlaylistItemCompletion = nil
411
+ }
412
+ } else {
413
+ print("No onBeforeNextPlaylistItem handler set, calling completion directly")
414
+ completion(item)
415
+ }
416
+ }
417
+ }
418
+
365
419
  // MARK: - RNJWPlayer styling
366
420
 
367
421
  func colorWithHexString(hex:String!) -> UIColor! {
@@ -1231,10 +1285,6 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
1231
1285
  func jwplayerContentDidComplete(_ player:JWPlayer) {
1232
1286
  self.onComplete?([:])
1233
1287
  }
1234
-
1235
- func jwplayerContentIsBuffering(_ player: any JWPlayerKit.JWPlayer) {
1236
-
1237
- }
1238
1288
 
1239
1289
  func jwplayer(_ player:JWPlayer, didLoadPlaylistItem item:JWPlayerItem, at index:UInt) {
1240
1290
  // var sourceDict: [String: Any] = [:]
@@ -1384,7 +1434,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
1384
1434
  // MARK: - JWPlayer Ad Delegate
1385
1435
 
1386
1436
  func jwplayer(_ player:JWPlayer, adEvent event:JWAdEvent) {
1387
- self.onAdEvent?(["client": event.client, "type": event.type])
1437
+ self.onAdEvent?(["client": event.client.rawValue, "type": event.type.rawValue])
1388
1438
  }
1389
1439
 
1390
1440
  // MARK: - JWPlayer AV Delegate