@jwplayer/jwplayer-react-native 1.1.1 → 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 +41 -1
- package/RNJWPlayer.podspec +2 -2
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java +34 -6
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +23 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/Util.java +10 -0
- package/badges/version.svg +1 -1
- package/index.d.ts +77 -7
- package/index.js +1 -0
- package/ios/RNJWPlayer/RNJWPlayerView.swift +32 -8
- package/ios/RNJWPlayer/RNJWPlayerViewManager.m +1 -1
- package/package.json +1 -1
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
|
package/RNJWPlayer.podspec
CHANGED
|
@@ -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, "
|
|
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.
|
|
15
|
+
s.dependency 'JWPlayerKit', '4.23.1'
|
|
16
16
|
s.dependency 'React-Core'
|
|
17
17
|
s.static_framework = true
|
|
18
18
|
s.info_plist = {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/android/build.gradle
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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);
|
package/badges/version.svg
CHANGED
|
@@ -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.
|
|
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?: {
|
|
386
|
+
timeslider?: {
|
|
387
|
+
thumb?: string;
|
|
388
|
+
rail?: string;
|
|
389
|
+
slider?: string;
|
|
390
|
+
};
|
|
375
391
|
};
|
|
376
392
|
font?: Font;
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
474
|
-
pipEnabled
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
@@ -318,11 +337,13 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
|
|
|
318
337
|
self.deinitAudioSession()
|
|
319
338
|
}
|
|
320
339
|
|
|
340
|
+
// Pull out top level iOS DRM values from config if present
|
|
341
|
+
// This is most often used in non-legacy configs using JWP DRM solutions
|
|
342
|
+
processSpcUrl = config["processSpcUrl"] as? String
|
|
343
|
+
fairplayCertUrl = config["certificateUrl"] as? String
|
|
344
|
+
contentUUID = config["contentUUID"] as? String
|
|
345
|
+
|
|
321
346
|
if forceLegacyConfig == true {
|
|
322
|
-
// Pull from top level of config
|
|
323
|
-
processSpcUrl = config["processSpcUrl"] as? String
|
|
324
|
-
fairplayCertUrl = config["certificateUrl"] as? String
|
|
325
|
-
contentUUID = config["contentUUID"] as? String
|
|
326
347
|
|
|
327
348
|
// Dangerous: check playlist for processSpcUrl / fairplayCertUrl in playlist
|
|
328
349
|
// Only checks first playlist item as multi-item DRM playlists are ill advised
|
|
@@ -338,7 +359,6 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
|
|
|
338
359
|
}
|
|
339
360
|
}
|
|
340
361
|
} else {
|
|
341
|
-
// TODO -- Ensure JWJSONParser pulls out cert/spc for sources (Expected in JW iOS SDK v4.19.0)
|
|
342
362
|
}
|
|
343
363
|
|
|
344
364
|
do {
|
|
@@ -652,8 +672,12 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
|
|
|
652
672
|
}
|
|
653
673
|
|
|
654
674
|
// Process other properties
|
|
655
|
-
if let mediaId = item["mediaId"] as? String {
|
|
656
|
-
|
|
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)
|
|
657
681
|
}
|
|
658
682
|
|
|
659
683
|
if let title = item["title"] as? String {
|