@jwplayer/jwplayer-react-native 1.0.0 → 1.0.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.
- package/.github/CODEOWNERS +1 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +2 -2
- package/.github/ISSUE_TEMPLATE/feature_request.md +2 -2
- package/.github/ISSUE_TEMPLATE/implement.md +17 -0
- package/.github/ISSUE_TEMPLATE/question.md +2 -2
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/README.md +24 -3
- package/RNJWPlayer.podspec +3 -3
- package/android/.gradle/8.1.1/checksums/checksums.lock +0 -0
- package/android/.gradle/8.1.1/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.1.1/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.4/checksums/checksums.lock +0 -0
- package/android/.gradle/8.4/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.4/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock +0 -0
- package/android/.gradle/8.4/executionHistory/executionHistory.bin +0 -0
- package/android/.gradle/8.4/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.4/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.4/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +1 -1
- package/android/.gradle/file-system.probe +0 -0
- package/android/.idea/compiler.xml +6 -0
- package/android/.idea/gradle.xml +6 -0
- package/android/.idea/misc.xml +1 -1
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +14 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/Util.java +24 -3
- package/badges/version.svg +1 -1
- package/index.d.ts +9 -2
- package/index.js +4 -0
- package/ios/RNJWPlayer/RNJWPlayerView.swift +201 -195
- package/ios/RNJWPlayer/RNJWPlayerViewController.swift +26 -22
- package/ios/RNJWPlayer/RNJWPlayerViewManager.swift +2 -0
- package/ios/RNJWPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/RNJWPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/RNJWPlayer.xcodeproj/project.xcworkspace/xcuserdata/jmilham.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/RNJWPlayer.xcodeproj/xcuserdata/jmilham.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/package.json +1 -1
- package/android/.gradle/8.2/checksums/checksums.lock +0 -0
- package/android/.gradle/8.2/dependencies-accessors/dependencies-accessors.lock +0 -0
- package/android/.gradle/8.2/fileHashes/fileHashes.lock +0 -0
- package/android/.idea/workspace.xml +0 -54
- /package/android/.gradle/{8.2 → 8.4}/dependencies-accessors/gc.properties +0 -0
- /package/android/.gradle/{8.2 → 8.4}/fileChanges/last-build.bin +0 -0
- /package/android/.gradle/{8.2 → 8.4}/gc.properties +0 -0
package/.github/CODEOWNERS
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# SDK Team
|
|
2
|
-
@jwplayer/team_sdks
|
|
2
|
+
* @jwplayer/team_sdks
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Implementation
|
|
3
|
+
about: Request an implementation of a native feature or upgrade of SDK
|
|
4
|
+
title: "[IMPLEMENT]"
|
|
5
|
+
labels: 'needs-grooming'
|
|
6
|
+
assignees: '@jwplayer/team_sdks'
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**What needs to be implemented**
|
|
11
|
+
|
|
12
|
+
**How should it be implemented**
|
|
13
|
+
|
|
14
|
+
**Acceptance Criteria**
|
|
15
|
+
|
|
16
|
+
**Additional context**
|
|
17
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/jwplayer-react-native.iml" filepath="$PROJECT_DIR$/.idea/jwplayer-react-native.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
package/.idea/vcs.xml
ADDED
package/README.md
CHANGED
|
@@ -49,8 +49,9 @@ Follow these steps to add the library to your Android project:
|
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
For more details and guidance regarding configuration and requirements, see the [JWP Android SDK documentation](https://docs.jwplayer.com/players/docs/android-overview#requirements).
|
|
52
53
|
|
|
53
|
-
<br
|
|
54
|
+
<br />
|
|
54
55
|
|
|
55
56
|
### iOS
|
|
56
57
|
|
|
@@ -71,6 +72,8 @@ Follow these steps to add the library to your iOS project:
|
|
|
71
72
|
pod install
|
|
72
73
|
```
|
|
73
74
|
|
|
75
|
+
For more details and guidance regarding configuration and requirements, see the [JWP iOS SDK documentation](https://docs.jwplayer.com/players/docs/ios-overview#requirements).
|
|
76
|
+
|
|
74
77
|
<br /><br />
|
|
75
78
|
|
|
76
79
|
## Usage
|
|
@@ -138,7 +141,7 @@ Follow these steps to configure the media playback experience in your app:
|
|
|
138
141
|
: 'YOUR_IOS_SDK_KEY',
|
|
139
142
|
backgroundAudioEnabled: true,
|
|
140
143
|
autostart: true,
|
|
141
|
-
styling: {
|
|
144
|
+
styling: { // only (mostly) compatible with iOS
|
|
142
145
|
colors: {
|
|
143
146
|
timeslider: {
|
|
144
147
|
rail: "0000FF",
|
|
@@ -238,7 +241,7 @@ Follow these steps to run the example project:
|
|
|
238
241
|
|
|
239
242
|
## Advanced Topics
|
|
240
243
|
|
|
241
|
-
[Advertising](#advertising) | [Background Audio](#background-audio) | [Casting](#casting) | [DRM](#drm) | [Picture in Picture (PiP)](#picture-in-picture-pip)
|
|
244
|
+
[Advertising](#advertising) | [Background Audio](#background-audio) | [Casting](#casting) | [DRM](#drm) | [Picture in Picture (PiP)](#picture-in-picture-pip) | [Styling](#styling)
|
|
242
245
|
|
|
243
246
|
<br />
|
|
244
247
|
|
|
@@ -408,6 +411,24 @@ If you use a different provider for DRM or this does not work for your use case,
|
|
|
408
411
|
|
|
409
412
|
<br /><br />
|
|
410
413
|
|
|
414
|
+
### Styling
|
|
415
|
+
|
|
416
|
+
[Android](#android-styling) | [iOS](#ios-styling)
|
|
417
|
+
|
|
418
|
+
#### Android Styling
|
|
419
|
+
The `styling` prop will not work when using the modern prop convention that matches the JWP Delivery API. Even when using the `forceLegacyConfig` prop, Android may not respect your choices.
|
|
420
|
+
|
|
421
|
+
Android styling is best handled through overring JWP IDs in your apps resources files. See the documentation [here](https://docs.jwplayer.com/players/docs/android-styling-guide).
|
|
422
|
+
|
|
423
|
+
A sample of overring a color via XML can be seen in this [colors file](Example/android/app/src/main/res/values/colors.xml). The color specified here is the default, but if you wish to change it, the color will be updated on the player.
|
|
424
|
+
|
|
425
|
+
<br />
|
|
426
|
+
|
|
427
|
+
#### iOS Styling
|
|
428
|
+
You can use the styling elements as defined in the [Legacy Readme](/docs/legacy_readme.md#styling).
|
|
429
|
+
|
|
430
|
+
<br /><br />
|
|
431
|
+
|
|
411
432
|
## Contributing
|
|
412
433
|
|
|
413
434
|
- All contributions should correlate to an open issue. We hope to avoid doubling the work between contributors.
|
package/RNJWPlayer.podspec
CHANGED
|
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
|
|
|
12
12
|
s.platform = :ios, "14.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', '
|
|
15
|
+
s.dependency 'JWPlayerKit', '>= 4.19.0'
|
|
16
16
|
s.dependency 'React-Core'
|
|
17
17
|
s.static_framework = true
|
|
18
18
|
s.info_plist = {
|
|
@@ -28,14 +28,14 @@ Pod::Spec.new do |s|
|
|
|
28
28
|
|
|
29
29
|
if defined?($RNJWPlayerUseGoogleCast)
|
|
30
30
|
Pod::UI.puts "RNJWPlayer: enable Google Cast"
|
|
31
|
-
s.dependency 'google-cast-sdk', '
|
|
31
|
+
s.dependency 'google-cast-sdk', '4.8.1'
|
|
32
32
|
s.pod_target_xcconfig = {
|
|
33
33
|
'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_CAST'
|
|
34
34
|
}
|
|
35
35
|
end
|
|
36
36
|
if defined?($RNJWPlayerUseGoogleIMA)
|
|
37
37
|
Pod::UI.puts "RNJWPlayer: enable IMA SDK"
|
|
38
|
-
s.dependency 'GoogleAds-IMA-iOS-SDK', '
|
|
38
|
+
s.dependency 'GoogleAds-IMA-iOS-SDK', '3.22.0'
|
|
39
39
|
s.pod_target_xcconfig = {
|
|
40
40
|
'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_IMA'
|
|
41
41
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#
|
|
1
|
+
#Wed May 22 15:58:05 CEST 2024
|
|
2
2
|
gradle.version=8.1.1
|
|
Binary file
|
package/android/.idea/gradle.xml
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<project version="4">
|
|
3
|
+
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
3
4
|
<component name="GradleSettings">
|
|
4
5
|
<option name="linkedExternalProjectsSettings">
|
|
5
6
|
<GradleProjectSettings>
|
|
6
7
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
7
8
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
|
9
|
+
<option name="modules">
|
|
10
|
+
<set>
|
|
11
|
+
<option value="$PROJECT_DIR$" />
|
|
12
|
+
</set>
|
|
13
|
+
</option>
|
|
8
14
|
<option name="resolveExternalAnnotations" value="false" />
|
|
9
15
|
</GradleProjectSettings>
|
|
10
16
|
</option>
|
package/android/.idea/misc.xml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<project version="4">
|
|
3
3
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
4
|
-
<component name="ProjectRootManager">
|
|
4
|
+
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
|
5
5
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
|
6
6
|
</component>
|
|
7
7
|
<component name="ProjectType">
|
package/android/build.gradle
CHANGED
|
@@ -66,7 +66,7 @@ allprojects {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
def jwPlayerVersion = "4.
|
|
69
|
+
def jwPlayerVersion = "4.18.0"
|
|
70
70
|
def exoplayerVersion = "2.18.7" // Deprecated. Use Media3 when targeting JW SDK > 4.16.0
|
|
71
71
|
def media3ExoVersion = "1.1.1"
|
|
72
72
|
|
|
@@ -82,7 +82,7 @@ dependencies {
|
|
|
82
82
|
|
|
83
83
|
// Ad dependencies
|
|
84
84
|
if (useIMA) {
|
|
85
|
-
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.
|
|
85
|
+
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.33.0'
|
|
86
86
|
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -785,7 +785,10 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
785
785
|
}
|
|
786
786
|
|
|
787
787
|
// Legacy
|
|
788
|
-
// This isn't the ideal way to do this on Android
|
|
788
|
+
// This isn't the ideal way to do this on Android. All drawables/colors/themes shoudld
|
|
789
|
+
// be targed using styling. See `https://docs.jwplayer.com/players/docs/android-styling-guide`
|
|
790
|
+
// for more information on how best to override the JWP styles using XML. If you are unsure of a
|
|
791
|
+
// color/drawable/theme, open an `Ask` issue.
|
|
789
792
|
if (mColors != null) {
|
|
790
793
|
if (mColors.hasKey("backgroundColor")) {
|
|
791
794
|
mPlayerView.setBackgroundColor(Color.parseColor("#" + mColors.getString("backgroundColor")));
|
|
@@ -1448,6 +1451,16 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1448
1451
|
event.putBoolean("active", castEvent.isActive());
|
|
1449
1452
|
event.putBoolean("available", castEvent.isAvailable());
|
|
1450
1453
|
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCasting", event);
|
|
1454
|
+
// stop/start the background audio service if it's running and we're casting
|
|
1455
|
+
if (castEvent.isActive()) {
|
|
1456
|
+
doUnbindService();
|
|
1457
|
+
} else {
|
|
1458
|
+
if (backgroundAudioEnabled) {
|
|
1459
|
+
mMediaServiceController = new MediaServiceController.Builder((AppCompatActivity) mActivity, mPlayer)
|
|
1460
|
+
.build();
|
|
1461
|
+
doBindService();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1451
1464
|
}
|
|
1452
1465
|
|
|
1453
1466
|
// LifecycleEventListener
|
|
@@ -21,6 +21,7 @@ import java.net.URL;
|
|
|
21
21
|
import java.util.ArrayList;
|
|
22
22
|
import java.util.List;
|
|
23
23
|
import java.util.Map;
|
|
24
|
+
import java.util.Locale;
|
|
24
25
|
|
|
25
26
|
public class Util {
|
|
26
27
|
|
|
@@ -98,7 +99,7 @@ public class Util {
|
|
|
98
99
|
if (sourceProp.hasKey("file")) {
|
|
99
100
|
String file = sourceProp.getString("file");
|
|
100
101
|
String label = sourceProp.getString("label");
|
|
101
|
-
boolean isDefault = sourceProp.getBoolean("default");
|
|
102
|
+
boolean isDefault = sourceProp.hasKey("default") ? sourceProp.getBoolean("default") : false;
|
|
102
103
|
MediaSource source = new MediaSource.Builder().file(file).label(label).isDefault(isDefault).build();
|
|
103
104
|
sources.add(source);
|
|
104
105
|
}
|
|
@@ -147,8 +148,13 @@ public class Util {
|
|
|
147
148
|
if (trackProp.hasKey("file")) {
|
|
148
149
|
String file = trackProp.getString("file");
|
|
149
150
|
String label = trackProp.getString("label");
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
CaptionType kind = CaptionType.CAPTIONS;
|
|
152
|
+
// Being safe to old implementations though this should be required.
|
|
153
|
+
if (trackProp.hasKey("kind")) {
|
|
154
|
+
kind = getCaptionType(trackProp.getString("kind").toUpperCase(Locale.US));
|
|
155
|
+
}
|
|
156
|
+
boolean isDefault = trackProp.hasKey("default") ? trackProp.getBoolean("default") : false;
|
|
157
|
+
Caption caption = new Caption.Builder().file(file).label(label).kind(kind).isDefault(isDefault).build();
|
|
152
158
|
tracks.add(caption);
|
|
153
159
|
}
|
|
154
160
|
}
|
|
@@ -186,6 +192,21 @@ public class Util {
|
|
|
186
192
|
return itemBuilder.build();
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Internal helper for parsing a caption type from a known string
|
|
197
|
+
* @param type one of "CAPTIONS", "CHAPTERS", "THUMBNAILS"
|
|
198
|
+
* @return the correct Enum CaptionType
|
|
199
|
+
*/
|
|
200
|
+
public static CaptionType getCaptionType(String type){
|
|
201
|
+
for (CaptionType captionType: CaptionType.values()) {
|
|
202
|
+
if (captionType.name().equals(type)) {
|
|
203
|
+
return CaptionType.valueOf(type);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// This should never happen. If a null is returned, expect a crash. Check `type` is specified type
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
189
210
|
public enum AdEventType {
|
|
190
211
|
JWAdEventTypeAdBreakEnd(0),
|
|
191
212
|
JWAdEventTypeAdBreakStart(1),
|
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.0.
|
|
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.1"><title>version: 1.0.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.0.1</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.0.1</text></g></svg>
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare module "jwplayer-react-native" {
|
|
1
|
+
declare module "@jwplayer/jwplayer-react-native" {
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { ViewStyle } from "react-native";
|
|
4
4
|
|
|
@@ -216,7 +216,7 @@ declare module "jwplayer-react-native" {
|
|
|
216
216
|
interface JwTrack {
|
|
217
217
|
id?: string;
|
|
218
218
|
file?: string;
|
|
219
|
-
kind:
|
|
219
|
+
kind: TrackKind;
|
|
220
220
|
label?: string;
|
|
221
221
|
default?: boolean;
|
|
222
222
|
}
|
|
@@ -251,9 +251,13 @@ declare module "jwplayer-react-native" {
|
|
|
251
251
|
label: string;
|
|
252
252
|
default?: boolean;
|
|
253
253
|
}
|
|
254
|
+
|
|
255
|
+
type TrackKind = "captions" | "chapters" | "thumbnails";
|
|
256
|
+
|
|
254
257
|
interface Track {
|
|
255
258
|
file: string;
|
|
256
259
|
label: string;
|
|
260
|
+
kind: TrackKind;
|
|
257
261
|
default?: boolean;
|
|
258
262
|
}
|
|
259
263
|
interface JWAdSettings {
|
|
@@ -349,6 +353,9 @@ declare module "jwplayer-react-native" {
|
|
|
349
353
|
size?: number;
|
|
350
354
|
}
|
|
351
355
|
type EdgeStyles = "none" | "dropshadow" | "raised" | "depressed" | "uniform";
|
|
356
|
+
|
|
357
|
+
// All `Styling` is only intended to be used with iOS. Android requires overloading
|
|
358
|
+
// of the JWP IDs seen here: https://docs.jwplayer.com/players/docs/android-styling-guide
|
|
352
359
|
interface Styling {
|
|
353
360
|
colors?: {
|
|
354
361
|
buttons?: string;
|
package/index.js
CHANGED
|
@@ -232,6 +232,8 @@ export default class JWPlayer extends Component {
|
|
|
232
232
|
PropTypes.shape({
|
|
233
233
|
file: PropTypes.string,
|
|
234
234
|
label: PropTypes.string,
|
|
235
|
+
kind: PropTypes.oneOf(['captions', 'thumbnails', 'chapters']),
|
|
236
|
+
default: PropTypes.bool
|
|
235
237
|
})
|
|
236
238
|
),
|
|
237
239
|
adSchedule: PropTypes.arrayOf(
|
|
@@ -252,6 +254,8 @@ export default class JWPlayer extends Component {
|
|
|
252
254
|
'hidden',
|
|
253
255
|
'onscreen',
|
|
254
256
|
]),
|
|
257
|
+
// All `Styling` is only intended to be used with iOS. Android requires overloading
|
|
258
|
+
// of the JWP IDs seen here: https://docs.jwplayer.com/players/docs/android-styling-guide
|
|
255
259
|
styling: PropTypes.shape({
|
|
256
260
|
colors: PropTypes.shape({
|
|
257
261
|
buttons: PropTypes.string,
|
|
@@ -10,14 +10,17 @@ import AVFoundation
|
|
|
10
10
|
import AVKit
|
|
11
11
|
import MediaPlayer
|
|
12
12
|
import React
|
|
13
|
-
import GoogleCast
|
|
14
13
|
import JWPlayerKit
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
#if USE_GOOGLE_CAST
|
|
16
|
+
import GoogleCast
|
|
17
|
+
#endif
|
|
18
|
+
|
|
19
|
+
class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDelegate, JWAVDelegate, JWPlayerViewDelegate, JWPlayerViewControllerDelegate, JWDRMContentKeyDataSource, JWTimeEventListener, AVPictureInPictureControllerDelegate {
|
|
17
20
|
|
|
18
21
|
// MARK: - RNJWPlayer allocation
|
|
19
22
|
|
|
20
|
-
var playerViewController:RNJWPlayerViewController!
|
|
23
|
+
var playerViewController: RNJWPlayerViewController!
|
|
21
24
|
var playerView: JWPlayerView!
|
|
22
25
|
var audioSession: AVAudioSession!
|
|
23
26
|
var pipEnabled: Bool = true
|
|
@@ -39,49 +42,49 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
|
|
|
39
42
|
var isCasting: Bool = false
|
|
40
43
|
var availableDevices: [AnyObject]!
|
|
41
44
|
|
|
42
|
-
@objc var onBuffer:RCTDirectEventBlock?
|
|
43
|
-
@objc var onUpdateBuffer:RCTDirectEventBlock?
|
|
44
|
-
@objc var onPlay:RCTDirectEventBlock?
|
|
45
|
-
@objc var onBeforePlay:RCTDirectEventBlock?
|
|
46
|
-
@objc var onAttemptPlay:RCTDirectEventBlock?
|
|
47
|
-
@objc var onPause:RCTDirectEventBlock?
|
|
48
|
-
@objc var onIdle:RCTDirectEventBlock?
|
|
49
|
-
@objc var onPlaylistItem:RCTDirectEventBlock?
|
|
50
|
-
@objc var onLoaded:RCTDirectEventBlock?
|
|
51
|
-
@objc var onVisible:RCTDirectEventBlock?
|
|
52
|
-
@objc var onTime:RCTDirectEventBlock?
|
|
53
|
-
@objc var onSeek:RCTDirectEventBlock?
|
|
54
|
-
@objc var onSeeked:RCTDirectEventBlock?
|
|
55
|
-
@objc var onRateChanged:RCTDirectEventBlock?
|
|
56
|
-
@objc var onPlaylist:RCTDirectEventBlock?
|
|
57
|
-
@objc var onPlaylistComplete:RCTDirectEventBlock?
|
|
58
|
-
@objc var onBeforeComplete:RCTDirectEventBlock?
|
|
59
|
-
@objc var onComplete:RCTDirectEventBlock?
|
|
60
|
-
@objc var onAudioTracks:RCTDirectEventBlock?
|
|
61
|
-
@objc var onPlayerReady:RCTDirectEventBlock?
|
|
62
|
-
@objc var onSetupPlayerError:RCTDirectEventBlock?
|
|
63
|
-
@objc var onPlayerError:RCTDirectEventBlock?
|
|
64
|
-
@objc var onPlayerWarning:RCTDirectEventBlock?
|
|
65
|
-
@objc var onPlayerAdWarning:RCTDirectEventBlock?
|
|
66
|
-
@objc var onPlayerAdError:RCTDirectEventBlock?
|
|
67
|
-
@objc var onAdEvent:RCTDirectEventBlock?
|
|
68
|
-
@objc var onAdTime:RCTDirectEventBlock?
|
|
69
|
-
@objc var onScreenTapped:RCTDirectEventBlock?
|
|
70
|
-
@objc var onControlBarVisible:RCTDirectEventBlock?
|
|
71
|
-
@objc var onFullScreen:RCTDirectEventBlock?
|
|
72
|
-
@objc var onFullScreenRequested:RCTDirectEventBlock?
|
|
73
|
-
@objc var onFullScreenExit:RCTDirectEventBlock?
|
|
74
|
-
@objc var onFullScreenExitRequested:RCTDirectEventBlock?
|
|
75
|
-
@objc var onPlayerSizeChange:RCTDirectEventBlock?
|
|
76
|
-
@objc var onCastingDevicesAvailable:RCTDirectEventBlock?
|
|
77
|
-
@objc var onConnectedToCastingDevice:RCTDirectEventBlock?
|
|
78
|
-
@objc var onDisconnectedFromCastingDevice:RCTDirectEventBlock?
|
|
79
|
-
@objc var onConnectionTemporarilySuspended:RCTDirectEventBlock?
|
|
80
|
-
@objc var onConnectionRecovered:RCTDirectEventBlock?
|
|
81
|
-
@objc var onConnectionFailed:RCTDirectEventBlock?
|
|
82
|
-
@objc var onCasting:RCTDirectEventBlock?
|
|
83
|
-
@objc var onCastingEnded:RCTDirectEventBlock?
|
|
84
|
-
@objc var onCastingFailed:RCTDirectEventBlock?
|
|
45
|
+
@objc var onBuffer: RCTDirectEventBlock?
|
|
46
|
+
@objc var onUpdateBuffer: RCTDirectEventBlock?
|
|
47
|
+
@objc var onPlay: RCTDirectEventBlock?
|
|
48
|
+
@objc var onBeforePlay: RCTDirectEventBlock?
|
|
49
|
+
@objc var onAttemptPlay: RCTDirectEventBlock?
|
|
50
|
+
@objc var onPause: RCTDirectEventBlock?
|
|
51
|
+
@objc var onIdle: RCTDirectEventBlock?
|
|
52
|
+
@objc var onPlaylistItem: RCTDirectEventBlock?
|
|
53
|
+
@objc var onLoaded: RCTDirectEventBlock?
|
|
54
|
+
@objc var onVisible: RCTDirectEventBlock?
|
|
55
|
+
@objc var onTime: RCTDirectEventBlock?
|
|
56
|
+
@objc var onSeek: RCTDirectEventBlock?
|
|
57
|
+
@objc var onSeeked: RCTDirectEventBlock?
|
|
58
|
+
@objc var onRateChanged: RCTDirectEventBlock?
|
|
59
|
+
@objc var onPlaylist: RCTDirectEventBlock?
|
|
60
|
+
@objc var onPlaylistComplete: RCTDirectEventBlock?
|
|
61
|
+
@objc var onBeforeComplete: RCTDirectEventBlock?
|
|
62
|
+
@objc var onComplete: RCTDirectEventBlock?
|
|
63
|
+
@objc var onAudioTracks: RCTDirectEventBlock?
|
|
64
|
+
@objc var onPlayerReady: RCTDirectEventBlock?
|
|
65
|
+
@objc var onSetupPlayerError: RCTDirectEventBlock?
|
|
66
|
+
@objc var onPlayerError: RCTDirectEventBlock?
|
|
67
|
+
@objc var onPlayerWarning: RCTDirectEventBlock?
|
|
68
|
+
@objc var onPlayerAdWarning: RCTDirectEventBlock?
|
|
69
|
+
@objc var onPlayerAdError: RCTDirectEventBlock?
|
|
70
|
+
@objc var onAdEvent: RCTDirectEventBlock?
|
|
71
|
+
@objc var onAdTime: RCTDirectEventBlock?
|
|
72
|
+
@objc var onScreenTapped: RCTDirectEventBlock?
|
|
73
|
+
@objc var onControlBarVisible: RCTDirectEventBlock?
|
|
74
|
+
@objc var onFullScreen: RCTDirectEventBlock?
|
|
75
|
+
@objc var onFullScreenRequested: RCTDirectEventBlock?
|
|
76
|
+
@objc var onFullScreenExit: RCTDirectEventBlock?
|
|
77
|
+
@objc var onFullScreenExitRequested: RCTDirectEventBlock?
|
|
78
|
+
@objc var onPlayerSizeChange: RCTDirectEventBlock?
|
|
79
|
+
@objc var onCastingDevicesAvailable: RCTDirectEventBlock?
|
|
80
|
+
@objc var onConnectedToCastingDevice: RCTDirectEventBlock?
|
|
81
|
+
@objc var onDisconnectedFromCastingDevice: RCTDirectEventBlock?
|
|
82
|
+
@objc var onConnectionTemporarilySuspended: RCTDirectEventBlock?
|
|
83
|
+
@objc var onConnectionRecovered: RCTDirectEventBlock?
|
|
84
|
+
@objc var onConnectionFailed: RCTDirectEventBlock?
|
|
85
|
+
@objc var onCasting: RCTDirectEventBlock?
|
|
86
|
+
@objc var onCastingEnded: RCTDirectEventBlock?
|
|
87
|
+
@objc var onCastingFailed: RCTDirectEventBlock?
|
|
85
88
|
|
|
86
89
|
init() {
|
|
87
90
|
super.init(frame: CGRect(x: 20, y: 0, width: UIScreen.main.bounds.width - 40, height: 300))
|
|
@@ -279,7 +282,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
|
|
|
279
282
|
func setNewConfig(config: [String : Any]) {
|
|
280
283
|
let forceLegacyConfig = config["forceLegacyConfig"] as? Bool?
|
|
281
284
|
let data:Data! = try? JSONSerialization.data(withJSONObject: config, options:.prettyPrinted)
|
|
282
|
-
let jwConfig = try? JWJSONParser.
|
|
285
|
+
let jwConfig = try? JWJSONParser.config(from:data)
|
|
283
286
|
|
|
284
287
|
currentConfig = config
|
|
285
288
|
|
|
@@ -1071,11 +1074,11 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
|
|
|
1071
1074
|
|
|
1072
1075
|
// MARK: Time events
|
|
1073
1076
|
|
|
1074
|
-
func onAdTimeEvent(time:JWTimeData) {
|
|
1077
|
+
func onAdTimeEvent(_ time:JWTimeData) {
|
|
1075
1078
|
self.onAdTime?(["position": time.position, "duration": time.duration])
|
|
1076
1079
|
}
|
|
1077
1080
|
|
|
1078
|
-
func onMediaTimeEvent(time:JWTimeData) {
|
|
1081
|
+
func onMediaTimeEvent(_ time:JWTimeData) {
|
|
1079
1082
|
self.onTime?(["position": time.position, "duration": time.duration])
|
|
1080
1083
|
}
|
|
1081
1084
|
|
|
@@ -1382,152 +1385,6 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
|
|
|
1382
1385
|
self.onAdEvent?(["client": event.client, "type": event.type])
|
|
1383
1386
|
}
|
|
1384
1387
|
|
|
1385
|
-
// pragma Mark - Casting methods
|
|
1386
|
-
|
|
1387
|
-
func setUpCastController() {
|
|
1388
|
-
if (playerView != nil) && playerView.player as! Bool && (castController == nil) {
|
|
1389
|
-
castController = JWCastController(player:playerView.player)
|
|
1390
|
-
castController.delegate = self
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
self.scanForDevices()
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
func scanForDevices() {
|
|
1397
|
-
if castController != nil {
|
|
1398
|
-
castController.startDiscovery()
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
func stopScanForDevices() {
|
|
1403
|
-
if castController != nil {
|
|
1404
|
-
castController.stopDiscovery()
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
func presentCastDialog() {
|
|
1409
|
-
GCKCastContext.sharedInstance().presentCastDialog()
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
func startDiscovery() {
|
|
1413
|
-
GCKCastContext.sharedInstance().discoveryManager.startDiscovery()
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
func stopDiscovery() {
|
|
1417
|
-
GCKCastContext.sharedInstance().discoveryManager.stopDiscovery()
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
func discoveryActive() -> Bool {
|
|
1421
|
-
return GCKCastContext.sharedInstance().discoveryManager.discoveryActive
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
func hasDiscoveredDevices() -> Bool {
|
|
1425
|
-
return GCKCastContext.sharedInstance().discoveryManager.hasDiscoveredDevices
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
func discoveryState() -> GCKDiscoveryState {
|
|
1429
|
-
return GCKCastContext.sharedInstance().discoveryManager.discoveryState
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
func setPassiveScan(passive:Bool) {
|
|
1433
|
-
GCKCastContext.sharedInstance().discoveryManager.passiveScan = passive
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
func castState() -> GCKCastState {
|
|
1437
|
-
return GCKCastContext.sharedInstance().castState
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
func deviceCount() -> UInt {
|
|
1441
|
-
return GCKCastContext.sharedInstance().discoveryManager.deviceCount
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
func getAvailableDevices() -> [JWCastingDevice]! {
|
|
1445
|
-
return castController.availableDevices
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
func connectedDevice() -> JWCastingDevice! {
|
|
1449
|
-
return castController.connectedDevice
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
func connectToDevice(device:JWCastingDevice!) {
|
|
1453
|
-
return castController.connectToDevice(device)
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
func cast() {
|
|
1457
|
-
return castController.cast()
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
func stopCasting() {
|
|
1461
|
-
return castController.stopCasting()
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
// MARK: - JWPlayer Cast Delegate
|
|
1465
|
-
|
|
1466
|
-
func castController(_ controller: JWCastController, castingBeganWithDevice device: JWCastingDevice) {
|
|
1467
|
-
self.onCasting?([:])
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
func castController(_ controller:JWCastController, castingEndedWithError error: Error?) {
|
|
1471
|
-
self.onCastingEnded?(["error": error as Any])
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
func castController(_ controller:JWCastController, castingFailedWithError error: Error) {
|
|
1475
|
-
self.onCastingFailed?(["error": error as Any])
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
func castController(_ controller:JWCastController, connectedTo device: JWCastingDevice) {
|
|
1479
|
-
let dict:NSMutableDictionary! = NSMutableDictionary()
|
|
1480
|
-
|
|
1481
|
-
dict.setObject(device.name, forKey:"name" as NSCopying)
|
|
1482
|
-
dict.setObject(device.identifier, forKey:"identifier" as NSCopying)
|
|
1483
|
-
|
|
1484
|
-
do {
|
|
1485
|
-
let data:Data! = try JSONSerialization.data(withJSONObject: dict as Any, options:.prettyPrinted)
|
|
1486
|
-
|
|
1487
|
-
self.onConnectedToCastingDevice?(["device": String(data:data as Data, encoding:String.Encoding.utf8) as Any])
|
|
1488
|
-
} catch {
|
|
1489
|
-
print("Error converting dictionary to JSON data: \(error)")
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
func castController(_ controller:JWCastController, connectionFailedWithError error: Error) {
|
|
1494
|
-
self.onConnectionFailed?(["error": error as Any])
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
func castController(_ controller: JWCastController, connectionRecoveredWithDevice device:JWCastingDevice) {
|
|
1498
|
-
self.onConnectionRecovered?([:])
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
func castController(_ controller: JWCastController, connectionSuspendedWithDevice device: JWCastingDevice) {
|
|
1502
|
-
self.onConnectionTemporarilySuspended?([:])
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
func castController(_ controller: JWCastController, devicesAvailable devices:[JWCastingDevice]) {
|
|
1506
|
-
self.availableDevices = devices
|
|
1507
|
-
|
|
1508
|
-
var devicesInfo: [[String: Any]] = []
|
|
1509
|
-
for device in devices {
|
|
1510
|
-
var dict: [String: Any] = [:]
|
|
1511
|
-
|
|
1512
|
-
dict["name"] = device.name
|
|
1513
|
-
dict["identifier"] = device.identifier
|
|
1514
|
-
|
|
1515
|
-
devicesInfo.append(dict)
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
do {
|
|
1519
|
-
let data:Data! = try JSONSerialization.data(withJSONObject: devicesInfo as Any, options:.prettyPrinted)
|
|
1520
|
-
|
|
1521
|
-
self.onCastingDevicesAvailable?(["devices": String(data:data as Data, encoding:String.Encoding.utf8) as Any])
|
|
1522
|
-
} catch {
|
|
1523
|
-
print("Error converting dictionary to JSON data: \(error)")
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
func castController(_ controller: JWCastController, disconnectedWithError error: (Error)?) {
|
|
1528
|
-
self.onDisconnectedFromCastingDevice?(["error": error as Any])
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
1388
|
// MARK: - JWPlayer AV Delegate
|
|
1532
1389
|
|
|
1533
1390
|
func jwplayer(_ player:JWPlayer, audioTracksUpdated levels:[JWMediaSelectionOption]) {
|
|
@@ -1835,3 +1692,152 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
|
|
|
1835
1692
|
}
|
|
1836
1693
|
|
|
1837
1694
|
}
|
|
1695
|
+
|
|
1696
|
+
#if USE_GOOGLE_CAST
|
|
1697
|
+
extension RNJWPlayerView: JWCastDelegate {
|
|
1698
|
+
// pragma Mark - Casting methods
|
|
1699
|
+
func setUpCastController() {
|
|
1700
|
+
if (playerView != nil) && playerView.player as! Bool && (castController == nil) {
|
|
1701
|
+
castController = JWCastController(player:playerView.player)
|
|
1702
|
+
castController.delegate = self
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
self.scanForDevices()
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
func scanForDevices() {
|
|
1709
|
+
if castController != nil {
|
|
1710
|
+
castController.startDiscovery()
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
func stopScanForDevices() {
|
|
1715
|
+
if castController != nil {
|
|
1716
|
+
castController.stopDiscovery()
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
func presentCastDialog() {
|
|
1721
|
+
GCKCastContext.sharedInstance().presentCastDialog()
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
func startDiscovery() {
|
|
1725
|
+
GCKCastContext.sharedInstance().discoveryManager.startDiscovery()
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
func stopDiscovery() {
|
|
1729
|
+
GCKCastContext.sharedInstance().discoveryManager.stopDiscovery()
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
func discoveryActive() -> Bool {
|
|
1733
|
+
return GCKCastContext.sharedInstance().discoveryManager.discoveryActive
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
func hasDiscoveredDevices() -> Bool {
|
|
1737
|
+
return GCKCastContext.sharedInstance().discoveryManager.hasDiscoveredDevices
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
func discoveryState() -> GCKDiscoveryState {
|
|
1741
|
+
return GCKCastContext.sharedInstance().discoveryManager.discoveryState
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
func setPassiveScan(passive:Bool) {
|
|
1745
|
+
GCKCastContext.sharedInstance().discoveryManager.passiveScan = passive
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
func castState() -> GCKCastState {
|
|
1749
|
+
return GCKCastContext.sharedInstance().castState
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
func deviceCount() -> UInt {
|
|
1753
|
+
return GCKCastContext.sharedInstance().discoveryManager.deviceCount
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
func getAvailableDevices() -> [JWCastingDevice]! {
|
|
1757
|
+
return castController.availableDevices
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
func connectedDevice() -> JWCastingDevice! {
|
|
1761
|
+
return castController.connectedDevice
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
func connectToDevice(device:JWCastingDevice!) {
|
|
1765
|
+
return castController.connectToDevice(device)
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
func cast() {
|
|
1769
|
+
return castController.cast()
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
func stopCasting() {
|
|
1773
|
+
return castController.stopCasting()
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
// MARK: - JWPlayer Cast Delegate
|
|
1777
|
+
|
|
1778
|
+
func castController(_ controller: JWCastController, castingBeganWithDevice device: JWCastingDevice) {
|
|
1779
|
+
self.onCasting?([:])
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
func castController(_ controller:JWCastController, castingEndedWithError error: Error?) {
|
|
1783
|
+
self.onCastingEnded?(["error": error as Any])
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
func castController(_ controller:JWCastController, castingFailedWithError error: Error) {
|
|
1787
|
+
self.onCastingFailed?(["error": error as Any])
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
func castController(_ controller:JWCastController, connectedTo device: JWCastingDevice) {
|
|
1791
|
+
let dict:NSMutableDictionary! = NSMutableDictionary()
|
|
1792
|
+
|
|
1793
|
+
dict.setObject(device.name, forKey:"name" as NSCopying)
|
|
1794
|
+
dict.setObject(device.identifier, forKey:"identifier" as NSCopying)
|
|
1795
|
+
|
|
1796
|
+
do {
|
|
1797
|
+
let data:Data! = try JSONSerialization.data(withJSONObject: dict as Any, options:.prettyPrinted)
|
|
1798
|
+
|
|
1799
|
+
self.onConnectedToCastingDevice?(["device": String(data:data as Data, encoding:String.Encoding.utf8) as Any])
|
|
1800
|
+
} catch {
|
|
1801
|
+
print("Error converting dictionary to JSON data: \(error)")
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
func castController(_ controller:JWCastController, connectionFailedWithError error: Error) {
|
|
1806
|
+
self.onConnectionFailed?(["error": error as Any])
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
func castController(_ controller: JWCastController, connectionRecoveredWithDevice device:JWCastingDevice) {
|
|
1810
|
+
self.onConnectionRecovered?([:])
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
func castController(_ controller: JWCastController, connectionSuspendedWithDevice device: JWCastingDevice) {
|
|
1814
|
+
self.onConnectionTemporarilySuspended?([:])
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
func castController(_ controller: JWCastController, devicesAvailable devices:[JWCastingDevice]) {
|
|
1818
|
+
self.availableDevices = devices
|
|
1819
|
+
|
|
1820
|
+
var devicesInfo: [[String: Any]] = []
|
|
1821
|
+
for device in devices {
|
|
1822
|
+
var dict: [String: Any] = [:]
|
|
1823
|
+
|
|
1824
|
+
dict["name"] = device.name
|
|
1825
|
+
dict["identifier"] = device.identifier
|
|
1826
|
+
|
|
1827
|
+
devicesInfo.append(dict)
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
do {
|
|
1831
|
+
let data:Data! = try JSONSerialization.data(withJSONObject: devicesInfo as Any, options:.prettyPrinted)
|
|
1832
|
+
|
|
1833
|
+
self.onCastingDevicesAvailable?(["devices": String(data:data as Data, encoding:String.Encoding.utf8) as Any])
|
|
1834
|
+
} catch {
|
|
1835
|
+
print("Error converting dictionary to JSON data: \(error)")
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
func castController(_ controller: JWCastController, disconnectedWithError error: (Error)?) {
|
|
1840
|
+
self.onDisconnectedFromCastingDevice?(["error": error as Any])
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
#endif
|
|
@@ -10,11 +10,14 @@ import AVFoundation
|
|
|
10
10
|
import AVKit
|
|
11
11
|
import MediaPlayer
|
|
12
12
|
import React
|
|
13
|
-
import GoogleCast
|
|
14
13
|
import JWPlayerKit
|
|
15
14
|
|
|
15
|
+
#if USE_GOOGLE_CAST
|
|
16
|
+
import GoogleCast
|
|
17
|
+
#endif
|
|
18
|
+
|
|
16
19
|
class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerDelegate, JWDRMContentKeyDataSource {
|
|
17
|
-
|
|
20
|
+
|
|
18
21
|
var parentView:RNJWPlayerView!
|
|
19
22
|
|
|
20
23
|
func setDelegates() {
|
|
@@ -155,27 +158,27 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
155
158
|
func playerViewControllerDidDismissFullScreen(_ controller:JWPlayerViewController) {
|
|
156
159
|
parentView?.onFullScreenExit?([:])
|
|
157
160
|
}
|
|
158
|
-
|
|
161
|
+
|
|
159
162
|
func playerViewController(_ controller:JWPlayerViewController, relatedMenuClosedWithMethod method:JWRelatedInteraction) {
|
|
160
163
|
|
|
161
164
|
}
|
|
162
|
-
|
|
165
|
+
|
|
163
166
|
func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, relatedMenuOpenedWithItems items: [JWPlayerKit.JWPlayerItem], withMethod method: JWPlayerKit.JWRelatedInteraction) {
|
|
164
|
-
|
|
167
|
+
|
|
165
168
|
}
|
|
166
|
-
|
|
169
|
+
|
|
167
170
|
func playerViewController(_ controller: JWPlayerKit.JWPlayerViewController, relatedItemBeganPlaying item: JWPlayerKit.JWPlayerItem, atIndex index: Int, withMethod method: JWPlayerKit.JWRelatedMethod) {
|
|
168
|
-
|
|
171
|
+
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
// MARK: Time events
|
|
172
175
|
|
|
173
|
-
func onAdTimeEvent(time:JWTimeData) {
|
|
176
|
+
override func onAdTimeEvent(_ time:JWTimeData) {
|
|
174
177
|
super.onAdTimeEvent(time)
|
|
175
178
|
parentView?.onAdTime?(["position": time.position, "duration": time.duration])
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
func onMediaTimeEvent(time:JWTimeData) {
|
|
181
|
+
override func onMediaTimeEvent(_ time:JWTimeData) {
|
|
179
182
|
super.onMediaTimeEvent(time)
|
|
180
183
|
parentView?.onTime?(["position": time.position, "duration": time.duration])
|
|
181
184
|
}
|
|
@@ -191,7 +194,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
191
194
|
guard let fairplayCertUrlString = parentView?.fairplayCertUrl, let fairplayCertUrl = URL(string: fairplayCertUrlString) else {
|
|
192
195
|
return
|
|
193
196
|
}
|
|
194
|
-
|
|
197
|
+
|
|
195
198
|
let request = URLRequest(url: fairplayCertUrl)
|
|
196
199
|
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
|
197
200
|
if let error = error {
|
|
@@ -201,7 +204,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
201
204
|
}
|
|
202
205
|
task.resume()
|
|
203
206
|
}
|
|
204
|
-
|
|
207
|
+
|
|
205
208
|
func contentKeyWithSPCData(_ spcData: Data, completionHandler handler: @escaping (Data?, Date?, String?) -> Void) {
|
|
206
209
|
if parentView?.processSpcUrl == nil {
|
|
207
210
|
return
|
|
@@ -212,21 +215,21 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
212
215
|
ckcRequest.httpMethod = "POST"
|
|
213
216
|
ckcRequest.httpBody = spcData
|
|
214
217
|
ckcRequest.addValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
|
|
215
|
-
|
|
218
|
+
|
|
216
219
|
URLSession.shared.dataTask(with: ckcRequest as URLRequest) { (data, response, error) in
|
|
217
220
|
if let httpResponse = response as? HTTPURLResponse, (error != nil || httpResponse.statusCode != 200) {
|
|
218
221
|
NSLog("DRM ckc request error - %@", error.debugDescription)
|
|
219
222
|
handler(nil, nil, nil)
|
|
220
223
|
return
|
|
221
224
|
}
|
|
222
|
-
|
|
225
|
+
|
|
223
226
|
handler(data, nil, "application/octet-stream")
|
|
224
227
|
}.resume()
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
|
|
228
231
|
// MARK: - AV Picture In Picture Delegate
|
|
229
|
-
|
|
232
|
+
|
|
230
233
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
|
231
234
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
|
232
235
|
// if (keyPath == "playbackLikelyToKeepUp") {
|
|
@@ -287,7 +290,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
287
290
|
|
|
288
291
|
override func jwplayer(_ player:JWPlayer, isPlayingWithReason reason:JWPlayReason) {
|
|
289
292
|
super.jwplayer(player, isPlayingWithReason:reason)
|
|
290
|
-
|
|
293
|
+
|
|
291
294
|
parentView?.onPlay?([:])
|
|
292
295
|
|
|
293
296
|
parentView?.userPaused = false
|
|
@@ -332,7 +335,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
332
335
|
|
|
333
336
|
override func jwplayer(_ player:JWPlayer, didLoadPlaylistItem item:JWPlayerItem, at index:UInt) {
|
|
334
337
|
super.jwplayer(player, didLoadPlaylistItem: item, at: index)
|
|
335
|
-
|
|
338
|
+
|
|
336
339
|
// var sourceDict: [String: Any] = [:]
|
|
337
340
|
// var file: String?
|
|
338
341
|
//
|
|
@@ -393,7 +396,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
393
396
|
|
|
394
397
|
override func jwplayer(_ player:JWPlayer, didLoadPlaylist playlist:[JWPlayerItem]) {
|
|
395
398
|
super.jwplayer(player, didLoadPlaylist: playlist)
|
|
396
|
-
|
|
399
|
+
|
|
397
400
|
let playlistArray:NSMutableArray! = NSMutableArray()
|
|
398
401
|
|
|
399
402
|
for item:JWPlayerItem? in playlist {
|
|
@@ -492,7 +495,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
492
495
|
}
|
|
493
496
|
|
|
494
497
|
// MARK: - JWPlayer Cast Delegate
|
|
495
|
-
|
|
498
|
+
#if USE_GOOGLE_CAST
|
|
496
499
|
override func castController(_ controller:JWCastController, castingBeganWithDevice device:JWCastingDevice) {
|
|
497
500
|
super.castController(controller, castingBeganWithDevice:device)
|
|
498
501
|
parentView?.onCasting?([:])
|
|
@@ -538,7 +541,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
538
541
|
super.castController(controller, connectionSuspendedWithDevice:device)
|
|
539
542
|
parentView?.onConnectionTemporarilySuspended?([:])
|
|
540
543
|
}
|
|
541
|
-
|
|
544
|
+
|
|
542
545
|
override func castController(_ controller: JWCastController, devicesAvailable devices:[JWCastingDevice]) {
|
|
543
546
|
super.castController(controller, devicesAvailable:devices)
|
|
544
547
|
parentView?.availableDevices = devices
|
|
@@ -566,6 +569,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
566
569
|
super.castController(controller, disconnectedWithError:error)
|
|
567
570
|
parentView?.onDisconnectedFromCastingDevice?(["error": error as Any])
|
|
568
571
|
}
|
|
572
|
+
#endif
|
|
569
573
|
|
|
570
574
|
// MARK: - JWPlayer AV Delegate
|
|
571
575
|
|
|
@@ -597,12 +601,12 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
597
601
|
override func jwplayer(_ player:JWPlayer, updatedCaptionList options:[JWMediaSelectionOption]) {
|
|
598
602
|
super.jwplayer(player, updatedCaptionList:options)
|
|
599
603
|
}
|
|
600
|
-
|
|
604
|
+
|
|
601
605
|
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
|
602
606
|
super.viewWillTransition(to: size, with: coordinator)
|
|
603
607
|
coordinator.animate(alongsideTransition: { _ in
|
|
604
608
|
let orientation = UIDevice.current.orientation
|
|
605
|
-
switch orientation {
|
|
609
|
+
switch orientation {
|
|
606
610
|
case .portrait, .portraitUpsideDown:
|
|
607
611
|
if self.parentView?.currentConfig["exitFullScreenOnPortrait"] as? Bool ?? false {
|
|
608
612
|
super.dismissFullScreen(animated: true)
|
|
@@ -612,5 +616,5 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
|
|
|
612
616
|
}
|
|
613
617
|
})
|
|
614
618
|
}
|
|
615
|
-
|
|
619
|
+
|
|
616
620
|
}
|
|
@@ -205,6 +205,7 @@ class RNJWPlayerViewManager: RCTViewManager {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
#if USE_GOOGLE_CAST
|
|
208
209
|
@objc func setUpCastController(_ reactTag: NSNumber) {
|
|
209
210
|
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
|
|
210
211
|
guard let view = viewRegistry?[reactTag] as? RNJWPlayerView else {
|
|
@@ -295,6 +296,7 @@ class RNJWPlayerViewManager: RCTViewManager {
|
|
|
295
296
|
resolve(view.castState)
|
|
296
297
|
}
|
|
297
298
|
}
|
|
299
|
+
#endif
|
|
298
300
|
|
|
299
301
|
@objc func getAudioTracks(_ reactTag: NSNumber, _ resolve: @escaping RCTPromiseResolveBlock, _ reject: @escaping RCTPromiseRejectBlock) {
|
|
300
302
|
self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
|
|
Binary file
|
package/ios/RNJWPlayer.xcodeproj/xcuserdata/jmilham.xcuserdatad/xcschemes/xcschememanagement.plist
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>RNJWPlayer.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="AutoImportSettings">
|
|
4
|
-
<option name="autoReloadType" value="NONE" />
|
|
5
|
-
</component>
|
|
6
|
-
<component name="ChangeListManager">
|
|
7
|
-
<list default="true" id="daffcf00-d6d8-483c-a28f-ba2d6899d4c5" name="Changes" comment="">
|
|
8
|
-
<change beforePath="$PROJECT_DIR$/../Example/app/jsx/screens/DRMExample.js" beforeDir="false" afterPath="$PROJECT_DIR$/../Example/app/jsx/screens/DRMExample.js" afterDir="false" />
|
|
9
|
-
<change beforePath="$PROJECT_DIR$/../Example/ios/Podfile.lock" beforeDir="false" afterPath="$PROJECT_DIR$/../Example/ios/Podfile.lock" afterDir="false" />
|
|
10
|
-
<change beforePath="$PROJECT_DIR$/../Example/yarn.lock" beforeDir="false" afterPath="$PROJECT_DIR$/../Example/yarn.lock" afterDir="false" />
|
|
11
|
-
<change beforePath="$PROJECT_DIR$/../ios/RNJWPlayer/RNJWPlayerView.swift" beforeDir="false" afterPath="$PROJECT_DIR$/../ios/RNJWPlayer/RNJWPlayerView.swift" afterDir="false" />
|
|
12
|
-
</list>
|
|
13
|
-
<option name="SHOW_DIALOG" value="false" />
|
|
14
|
-
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
15
|
-
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
16
|
-
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
17
|
-
</component>
|
|
18
|
-
<component name="ClangdSettings">
|
|
19
|
-
<option name="formatViaClangd" value="false" />
|
|
20
|
-
</component>
|
|
21
|
-
<component name="Git.Settings">
|
|
22
|
-
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
|
23
|
-
</component>
|
|
24
|
-
<component name="ProjectColorInfo"><![CDATA[{
|
|
25
|
-
"associatedIndex": 5
|
|
26
|
-
}]]></component>
|
|
27
|
-
<component name="ProjectId" id="2fKP8rLRDpFiPXLGLH4cN1RyMXV" />
|
|
28
|
-
<component name="ProjectViewState">
|
|
29
|
-
<option name="showLibraryContents" value="true" />
|
|
30
|
-
</component>
|
|
31
|
-
<component name="PropertiesComponent"><![CDATA[{
|
|
32
|
-
"keyToString": {
|
|
33
|
-
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
|
34
|
-
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
35
|
-
"RunOnceActivity.cidr.known.project.marker": "true",
|
|
36
|
-
"android.gradle.sync.needed": "true",
|
|
37
|
-
"cf.first.check.clang-format": "false",
|
|
38
|
-
"cidr.known.project.marker": "true",
|
|
39
|
-
"git-widget-placeholder": "release/react-1.0.0",
|
|
40
|
-
"last_opened_file_path": "/Users/jmilham/source/repos/SDK/react/jwplayer-react-native/android"
|
|
41
|
-
}
|
|
42
|
-
}]]></component>
|
|
43
|
-
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
44
|
-
<component name="TaskManager">
|
|
45
|
-
<task active="true" id="Default" summary="Default task">
|
|
46
|
-
<changelist id="daffcf00-d6d8-483c-a28f-ba2d6899d4c5" name="Changes" comment="" />
|
|
47
|
-
<created>1713546640047</created>
|
|
48
|
-
<option name="number" value="Default" />
|
|
49
|
-
<option name="presentableId" value="Default" />
|
|
50
|
-
<updated>1713546640047</updated>
|
|
51
|
-
</task>
|
|
52
|
-
<servers />
|
|
53
|
-
</component>
|
|
54
|
-
</project>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|