@jwplayer/jwplayer-react-native 1.1.2 → 1.1.3

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.
package/README.md CHANGED
@@ -275,11 +275,44 @@ Follow these steps to enable background audio sessions:
275
275
  1. Set `backgroundAudioEnabled` to `true` in the `config`.
276
276
  2. Ensure that background audio is set for [Android](https://docs.jwplayer.com/players/docs/android-enable-background-audio) or [iOS](https://docs.jwplayer.com/players/docs/ios-player-backgrounding-reference#configure-audio-playback).
277
277
 
278
+ #### Android Opt-Out Information
279
+ Suppose you are **not** using the background audio service on Android. In that case, you will need to modify the [manifest](https://github.com/jwplayer/jwplayer-react-native/blob/master/android/src/main/AndroidManifest.xml) from our library (either in the node_modules, or in your fork before compilation) so as not to register a service that you will never use. Google can and will reject your Play Store submission if this is skipped. See the modified manifest below, which shows what to remove (commented-out permissions and services). Additionally, once this is done, you can never set `backgroundAudioEnabled` to true on Android, or you will run into a fatal crash.
280
+
281
+ ```xml
282
+
283
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
284
+ package="com.jwplayer.rnjwplayer">
285
+ <uses-permission android:name="android.permission.INTERNET" />
286
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
287
+ <!-- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> -->
288
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
289
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
290
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
291
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
292
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
293
+ <!-- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" /> -->
294
+ <!-- READ_MEDIA_IMAGES, READ_MEDIA_VIDEO or READ_MEDIA_AUDIO.-->
295
+
296
+ <application>
297
+ <!-- <service
298
+ android:name="com.jwplayer.pub.api.background.MediaService"
299
+ android:foregroundServiceType="mediaPlayback"
300
+ android:exported="false">
301
+ <intent-filter>
302
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
303
+ </intent-filter>
304
+ </service> -->
305
+ </application>
306
+
307
+ </manifest>
308
+
309
+ ```
310
+
278
311
  <br /><br />
279
312
 
280
313
  ### Casting
281
314
 
282
- [Android](#android-casting) | [iOS](#ios-casting)
315
+ [Android](#android-casting) | [iOS](#ios-casting) | [DRM Casting](#drm-casting)
283
316
 
284
317
  JWP enables casting by default with a casting button.
285
318
 
@@ -342,6 +375,13 @@ typedef NS_ENUM(NSUInteger, GCKCastState) {
342
375
  };
343
376
  ```
344
377
 
378
+ #### DRM Casting
379
+ Casting your DRM protected content requires some additional configuration and most likely a custom Chromecast Receiver.
380
+
381
+ See our Android and iOS documentation about creating ([iOS](https://docs.jwplayer.com/players/docs/ios-create-a-custom-receiver) / [Android](https://docs.jwplayer.com/players/docs/android-create-a-custom-receiver)) and sending data ([iOS](https://docs.jwplayer.com/players/docs/ios-enable-casting-to-chromecast-devices#send-custom-data-to-a-custom-receiver) / [Android](https://docs.jwplayer.com/players/docs/android-enable-casting-to-chromecast-devices#send-custom-data-to-a-custom-receiver)) for more information.
382
+
383
+ To send custom data to your receiver, use the `userInfo` prop and avoid using the reserved `sources` key, as the JWP SDK will append the DRM source information here for you to parse in your DRM enabled receiver.
384
+
345
385
  <br /><br />
346
386
 
347
387
  ### DRM
@@ -9,10 +9,10 @@ Pod::Spec.new do |s|
9
9
  s.license = package['license']
10
10
  s.authors = package['author']
11
11
  s.homepage = package['homepage']
12
- s.platform = :ios, "14.0"
12
+ s.platform = :ios, "15.0"
13
13
  s.source = { :git => "https://github.com/jwplayer/jwplayer-react-native.git", :tag => "v#{s.version}" }
14
14
  s.source_files = "ios/RNJWPlayer/*.{h,m,swift}"
15
- s.dependency 'JWPlayerKit', '4.22.0'
15
+ s.dependency 'JWPlayerKit', '4.23.1'
16
16
  s.dependency 'React-Core'
17
17
  s.static_framework = true
18
18
  s.info_plist = {
@@ -13,17 +13,16 @@ import com.facebook.react.bridge.UIManager;
13
13
  import com.facebook.react.bridge.ReadableMap;
14
14
  import com.facebook.react.bridge.WritableArray;
15
15
  import com.facebook.react.bridge.WritableMap;
16
- import com.facebook.react.uimanager.IllegalViewOperationException;
17
- import com.facebook.react.uimanager.NativeViewHierarchyManager;
18
- import com.facebook.react.uimanager.UIBlock;
19
16
  import com.facebook.react.uimanager.UIManagerHelper;
20
- import com.facebook.react.uimanager.UIManagerModule;
21
17
  import com.facebook.react.uimanager.common.UIManagerType;
22
18
  import com.jwplayer.pub.api.JWPlayer;
23
19
  import com.jwplayer.pub.api.PlayerState;
20
+ import com.jwplayer.pub.api.UiGroup;
24
21
  import com.jwplayer.pub.api.configuration.PlayerConfig;
22
+ import com.jwplayer.pub.api.configuration.UiConfig;
25
23
  import com.jwplayer.pub.api.media.adaptive.QualityLevel;
26
24
  import com.jwplayer.pub.api.media.audio.AudioTrack;
25
+
27
26
  import java.util.List;
28
27
 
29
28
  public class RNJWPlayerModule extends ReactContextBaseJavaModule {
@@ -58,6 +57,21 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
58
57
  }
59
58
  }
60
59
 
60
+ /**
61
+ * Creates a UiConfig that ensures PLAYER_CONTROLS_CONTAINER is always shown.
62
+ * If controls are not shown, the PLAYER_CONTROLS_CONTAINER UI Group is not displayed.
63
+ * This logic ensures that the PLAYER_CONTROLS_CONTAINER UI Group is displayed regardless if controls are shown or not.
64
+ * There is no way to recover controls if you do not show this UiGroup.
65
+ * But you are able to hide the controls still if it is shown.
66
+ */
67
+ private UiConfig createUiConfigWithControlsContainer(JWPlayer player, UiConfig originalUiConfig) {
68
+ if (!player.getControls()) {
69
+ return new UiConfig.Builder(originalUiConfig).show(UiGroup.PLAYER_CONTROLS_CONTAINER).build();
70
+ } else {
71
+ return originalUiConfig;
72
+ }
73
+ }
74
+
61
75
  @ReactMethod
62
76
  public void loadPlaylist(final int reactTag, final ReadableArray playlistItems) {
63
77
  new Handler(Looper.getMainLooper()).post(() -> {
@@ -66,6 +80,8 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
66
80
  JWPlayer player = playerView.mPlayerView.getPlayer();
67
81
 
68
82
  PlayerConfig oldConfig = player.getConfig();
83
+ boolean wasFullscreen = player.getFullscreen();
84
+ UiConfig uiConfig = createUiConfigWithControlsContainer(player, oldConfig.getUiConfig());
69
85
  PlayerConfig config = new PlayerConfig.Builder()
70
86
  .autostart(oldConfig.getAutostart())
71
87
  .nextUpOffset(oldConfig.getNextUpOffset())
@@ -75,7 +91,7 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
75
91
  .displayTitle(oldConfig.getDisplayTitle())
76
92
  .advertisingConfig(oldConfig.getAdvertisingConfig())
77
93
  .stretching(oldConfig.getStretching())
78
- .uiConfig(oldConfig.getUiConfig())
94
+ .uiConfig(uiConfig)
79
95
  .playlist(Util.createPlaylist(playlistItems))
80
96
  .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects())
81
97
  .preload(oldConfig.getPreload())
@@ -85,6 +101,11 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
85
101
  .build();
86
102
 
87
103
  player.setup(config);
104
+ // if the player was fullscreen, set it to fullscreen again as the player is recreated
105
+ // The fullscreen view is still active but the internals don't know it is
106
+ if (wasFullscreen) {
107
+ player.setFullscreen(true, true);
108
+ }
88
109
  }
89
110
  });
90
111
  }
@@ -97,6 +118,8 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
97
118
  JWPlayer player = playerView.mPlayerView.getPlayer();
98
119
 
99
120
  PlayerConfig oldConfig = player.getConfig();
121
+ boolean wasFullscreen = player.getFullscreen();
122
+ UiConfig uiConfig = createUiConfigWithControlsContainer(player, oldConfig.getUiConfig());
100
123
  PlayerConfig config = new PlayerConfig.Builder()
101
124
  .autostart(oldConfig.getAutostart())
102
125
  .nextUpOffset(oldConfig.getNextUpOffset())
@@ -106,7 +129,7 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
106
129
  .displayTitle(oldConfig.getDisplayTitle())
107
130
  .advertisingConfig(oldConfig.getAdvertisingConfig())
108
131
  .stretching(oldConfig.getStretching())
109
- .uiConfig(oldConfig.getUiConfig())
132
+ .uiConfig(uiConfig)
110
133
  .playlistUrl(playlistUrl)
111
134
  .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects())
112
135
  .preload(oldConfig.getPreload())
@@ -116,6 +139,11 @@ public class RNJWPlayerModule extends ReactContextBaseJavaModule {
116
139
  .build();
117
140
 
118
141
  player.setup(config);
142
+ // if the player was fullscreen, set it to fullscreen again as the player is recreated
143
+ // The fullscreen view is still active but the internals don't know it is
144
+ if (wasFullscreen) {
145
+ player.setFullscreen(true, true);
146
+ }
119
147
  }
120
148
  });
121
149
  }
@@ -879,11 +879,28 @@ public class RNJWPlayerView extends RelativeLayout implements
879
879
 
880
880
  }
881
881
 
882
+ /**
883
+ * Creates a UiConfig that ensures PLAYER_CONTROLS_CONTAINER is always shown.
884
+ * If controls are not shown, the PLAYER_CONTROLS_CONTAINER UI Group is not displayed.
885
+ * This logic ensures that the PLAYER_CONTROLS_CONTAINER UI Group is displayed regardless if controls are shown or not.
886
+ * There is no way to recover controls if you do not show this UiGroup.
887
+ * But you are able to hide the controls still if it is shown.
888
+ */
889
+ private UiConfig createUiConfigWithControlsContainer(JWPlayer player, UiConfig originalUiConfig) {
890
+ if (!player.getControls()) {
891
+ return new UiConfig.Builder(originalUiConfig).show(UiGroup.PLAYER_CONTROLS_CONTAINER).build();
892
+ } else {
893
+ return originalUiConfig;
894
+ }
895
+ }
896
+
882
897
  public void setConfig(ReadableMap prop) {
883
898
  if (mConfig == null || !mConfig.equals(prop)) {
884
899
  if (mConfig != null && isOnlyDiff(prop, "playlist") && mPlayer != null) { // still safe check, even with JW
885
900
  // JSON change
886
901
  PlayerConfig oldConfig = mPlayer.getConfig();
902
+ boolean wasFullscreen = mPlayer.getFullscreen();
903
+ UiConfig uiConfig = createUiConfigWithControlsContainer(mPlayer, oldConfig.getUiConfig());
887
904
  PlayerConfig config = new PlayerConfig.Builder()
888
905
  .autostart(oldConfig.getAutostart())
889
906
  .nextUpOffset(oldConfig.getNextUpOffset())
@@ -893,7 +910,7 @@ public class RNJWPlayerView extends RelativeLayout implements
893
910
  .displayTitle(oldConfig.getDisplayTitle())
894
911
  .advertisingConfig(oldConfig.getAdvertisingConfig())
895
912
  .stretching(oldConfig.getStretching())
896
- .uiConfig(oldConfig.getUiConfig())
913
+ .uiConfig(uiConfig)
897
914
  .playlist(Util.createPlaylist(mPlaylistProp))
898
915
  .allowCrossProtocolRedirects(oldConfig.getAllowCrossProtocolRedirects())
899
916
  .preload(oldConfig.getPreload())
@@ -903,6 +920,11 @@ public class RNJWPlayerView extends RelativeLayout implements
903
920
  .build();
904
921
 
905
922
  mPlayer.setup(config);
923
+ // if the player was fullscreen, set it to fullscreen again as the player is recreated
924
+ // The fullscreen view is still active but the internals don't know it is
925
+ if (wasFullscreen) {
926
+ mPlayer.setFullscreen(true, true);
927
+ }
906
928
  } else {
907
929
  if (prop.hasKey("license")) {
908
930
  new LicenseUtil().setLicenseKey(getReactContext(), prop.getString("license"));
@@ -17,6 +17,7 @@ import com.jwplayer.pub.api.media.captions.CaptionType;
17
17
  import com.jwplayer.pub.api.media.playlists.MediaSource;
18
18
  import com.jwplayer.pub.api.media.playlists.PlaylistItem;
19
19
 
20
+ import org.json.JSONException;
20
21
  import org.json.JSONObject;
21
22
 
22
23
  import java.io.IOException;
@@ -139,6 +140,15 @@ public class Util {
139
140
  itemBuilder.description(desc);
140
141
  }
141
142
 
143
+ if (playlistItem.hasKey("userInfo")) {
144
+ try {
145
+ JSONObject info = MapUtil.toJSONObject(playlistItem.getMap("userInfo"));
146
+ itemBuilder.userInfo(info);
147
+ } catch (JSONException e) {
148
+ Log.e("userInfo", "Error parsing `userInfo` from your playlist. Message: " + e.getLocalizedMessage());
149
+ }
150
+ }
151
+
142
152
  if (playlistItem.hasKey("image")) {
143
153
  String image = playlistItem.getString("image");
144
154
  itemBuilder.image(image);
@@ -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.1.2"><title>version: 1.1.2</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.2</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.1.2</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.3"><title>version: 1.1.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.1.3</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.1.3</text></g></svg>
package/index.d.ts CHANGED
@@ -37,6 +37,8 @@ declare module "@jwplayer/jwplayer-react-native" {
37
37
  landscapeOnFullScreen?: boolean;
38
38
  portraitOnExitFullScreen?: boolean;
39
39
  exitFullScreenOnPortrait?: boolean;
40
+ enableLockScreenControls?: boolean;
41
+ pipEnabled?: boolean;
40
42
  }
41
43
 
42
44
  type JwThumbnailPreview = 101 | 102 | 103;
@@ -172,6 +174,10 @@ declare module "@jwplayer/jwplayer-react-native" {
172
174
  schedule?: { [key: string]: JwAdBreak };
173
175
  imaDaiSettings?: JwImaDaiSettings;
174
176
  httpheaders?: { [key: string]: string };
177
+ /**
178
+ * Data to be passed to Chromecast receiver (optional and typically used for DRM implementations)
179
+ */
180
+ userInfo?: { [key: string]: any };
175
181
  }
176
182
 
177
183
  interface JwImaDaiSettings {
@@ -347,6 +353,12 @@ declare module "@jwplayer/jwplayer-react-native" {
347
353
  recommendations?: string;
348
354
  startTime?: number;
349
355
  autostart?: boolean;
356
+ /**
357
+ * Data to be passed to Chromecast receiver (optional and typically used for DRM implementations)
358
+ *
359
+ * Only made available in legacy objects as there is no way to pass this otherwise
360
+ */
361
+ userInfo?: { [key: string]: any };
350
362
  }
351
363
  type RelatedOnClicks = "play" | "link";
352
364
  type RelatedOnCompletes = "show" | "hide" | "autoplay";
@@ -371,11 +383,15 @@ declare module "@jwplayer/jwplayer-react-native" {
371
383
  buttons?: string;
372
384
  backgroundColor?: string;
373
385
  fontColor?: string;
374
- timeslider?: { progress?: string; rail?: string; thumb?: string };
386
+ timeslider?: {
387
+ thumb?: string;
388
+ rail?: string;
389
+ slider?: string;
390
+ };
375
391
  };
376
392
  font?: Font;
377
- displayTitle?: boolean;
378
- displayDescription?: boolean;
393
+ showTitle?: boolean;
394
+ showDesc?: boolean;
379
395
  captionsStyle?: {
380
396
  font?: Font;
381
397
  fontColor?: string;
@@ -383,7 +399,7 @@ declare module "@jwplayer/jwplayer-react-native" {
383
399
  highlightColor?: string;
384
400
  edgeStyle?: EdgeStyles;
385
401
  };
386
- menuStyle: {
402
+ menuStyle?: {
387
403
  font?: Font;
388
404
  fontColor?: string;
389
405
  backgroundColor?: string;
@@ -470,8 +486,12 @@ declare module "@jwplayer/jwplayer-react-native" {
470
486
  fairplayCertUrl?: string;
471
487
  contentUUID?: string;
472
488
  viewOnly?: boolean;
473
- enableLockScreenControls: boolean;
474
- pipEnabled: boolean;
489
+ enableLockScreenControls?: boolean;
490
+ pipEnabled?: boolean;
491
+ offlineMessage?: string;
492
+ offlineImage?: string;
493
+ forceFullScreenOnLandscape?: boolean;
494
+ forceLandscapeOnFullScreen?: boolean;
475
495
  }
476
496
  interface BaseEvent<T> {
477
497
  nativeEvent: T;
@@ -577,7 +597,54 @@ declare module "@jwplayer/jwplayer-react-native" {
577
597
  onBeforeNextPlaylistItem?: (event: BaseEvent<PlaylistItemEventProps>) => void;
578
598
  }
579
599
 
600
+ export const JWPlayerAdEvents: {
601
+ /// This event is reported when the ad break has come to an end.
602
+ JWAdEventTypeAdBreakEnd: 0;
603
+ /// This event is reported when the ad break has begun.
604
+ JWAdEventTypeAdBreakStart: 1;
605
+ /// This event is reported when the user taps the ad.
606
+ JWAdEventTypeClicked: 2;
607
+ /// This event is reported when the ad is done playing.
608
+ JWAdEventTypeComplete: 3;
609
+ /// This event is used to report the ad impression, supplying additional detailed information about the ad.
610
+ JWAdEventTypeImpression: 4;
611
+ /// This event reports meta data information associated with the ad.
612
+ JWAdEventTypeMeta: 5;
613
+ /// The event is reported when the ad pauses.
614
+ JWAdEventTypePause: 6;
615
+ /// This event is reported when the ad begins playing, even in the middle of the stream after it was paused.
616
+ JWAdEventTypePlay: 7;
617
+ /// The event reports data about the ad request, when the ad is about to be loaded.
618
+ JWAdEventTypeRequest: 8;
619
+ /// This event reports the schedule of ads across the currently playing content.
620
+ JWAdEventTypeSchedule: 9;
621
+ /// This event is reported when the user skips the ad.
622
+ JWAdEventTypeSkipped: 10;
623
+ /// This event is reported when the ad begins.
624
+ JWAdEventTypeStarted: 11;
625
+ /// This event relays information about ad companions.
626
+ JWAdEventTypeCompanion: 12;
627
+ };
628
+
629
+ export const JWPlayerState: {
630
+ JWPlayerStateUnknown?: number;
631
+ JWPlayerStateIdle: number;
632
+ JWPlayerStateBuffering: number;
633
+ JWPlayerStatePlaying: number;
634
+ JWPlayerStatePaused: number;
635
+ JWPlayerStateComplete: number;
636
+ JWPlayerStateError: number | null;
637
+ };
638
+
639
+ export const JWPlayerAdClients: {
640
+ JWAdClientJWPlayer: 0;
641
+ JWAdClientGoogleIMA: 1;
642
+ JWAdClientGoogleIMADAI: 2;
643
+ JWAdClientUnknown: 3;
644
+ };
645
+
580
646
  export default class JWPlayer extends React.Component<PropsType> {
647
+ quite(): void;
581
648
  pause(): void;
582
649
  play(): void;
583
650
  stop(): void;
@@ -591,6 +658,7 @@ declare module "@jwplayer/jwplayer-react-native" {
591
658
  setControls(show: boolean): void;
592
659
  setLockScreenControls(show: boolean): void;
593
660
  seekTo(time: number): void;
661
+ changePlaylist(fileUrl: string): void;
594
662
  /**
595
663
  * Side load playlist items into an already setup player
596
664
  * @param playlistItems `PlaylistItem` or `JwPlaylistItem`
@@ -602,7 +670,9 @@ declare module "@jwplayer/jwplayer-react-native" {
602
670
  */
603
671
  loadPlaylistWithUrl(playlistUrl: string): void;
604
672
  setFullscreen(fullScreen: boolean): void;
605
- position(): Promise<number>;
673
+ time(): Promise<number | null>;
674
+ position(): Promise<number | null>;
675
+ togglePIP(): void;
606
676
  setUpCastController(): void;
607
677
  presentCastDialog(): void;
608
678
  connectedDevice(): Promise<CastingDevice | null>;
package/index.js CHANGED
@@ -233,6 +233,7 @@ export default class JWPlayer extends Component {
233
233
  desc: PropTypes.string,
234
234
  mediaId: PropTypes.string,
235
235
  autostart: PropTypes.bool,
236
+ userInfo: PropTypes.object,
236
237
  recommendations: PropTypes.string,
237
238
  tracks: PropTypes.arrayOf(
238
239
  PropTypes.shape({
@@ -275,9 +275,28 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
275
275
  }
276
276
  }
277
277
 
278
+ // Check if player is in PiP mode before loading new playlist
279
+ var isPipActive = false
280
+ var pipController: AVPictureInPictureController?
281
+
282
+ if let playerView = playerView {
283
+ pipController = playerView.pictureInPictureController
284
+ isPipActive = pipController?.isPictureInPictureActive ?? false
285
+ } else if let playerViewController = playerViewController {
286
+ pipController = playerViewController.playerView.pictureInPictureController
287
+ isPipActive = pipController?.isPictureInPictureActive ?? false
288
+ }
289
+
278
290
  if let playerViewController = playerViewController {
279
- playerViewController.player.loadPlaylist(items: playlistArray)
291
+ // We must treat PiP mode differently and setup as a new config
292
+ // or else the player will become unresponsive
293
+ if isPipActive {
294
+ setNewConfig(config: config)
295
+ } else {
296
+ playerViewController.player.loadPlaylist(items: playlistArray)
297
+ }
280
298
  } else if let playerView = playerView {
299
+ // If you use player only, consider doing a simpliar check for PiP as above
281
300
  playerView.player.loadPlaylist(items: playlistArray)
282
301
  } else {
283
302
  setNewConfig(config: config)
@@ -653,8 +672,12 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
653
672
  }
654
673
 
655
674
  // Process other properties
656
- if let mediaId = item["mediaId"] as? String {
657
- itemBuilder.mediaId(mediaId)
675
+ if let mediaId = (item["mediaId"] as? String) ?? (item["mediaid"] as? String) {
676
+ itemBuilder.mediaId(mediaId)
677
+ }
678
+
679
+ if let userInfo = item["userInfo"] as? Dictionary<String, Any> {
680
+ itemBuilder.userInfo(userInfo)
658
681
  }
659
682
 
660
683
  if let title = item["title"] as? String {
@@ -4,7 +4,7 @@
4
4
  #import "RCTViewManager.h"
5
5
  #endif
6
6
 
7
- #import <JWPlayerKit/JWPlayerKit-swift.h>
7
+ #import <JWPlayerKit/JWPlayerKit-Swift.h>
8
8
 
9
9
  #import "RCTUIManager.h"
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwplayer/jwplayer-react-native",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "React-native Android/iOS plugin for JWPlayer SDK (https://www.jwplayer.com/)",
5
5
  "main": "index.js",
6
6
  "types": "./index.d.ts",