@jwplayer/jwplayer-react-native 1.2.0 → 1.3.0
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 +114 -21
- package/RNJWPlayer.podspec +1 -1
- package/android/build.gradle +14 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java +19 -4
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +270 -105
- package/android/src/main/java/com/jwplayer/rnjwplayer/Util.java +13 -1
- package/badges/version.svg +1 -1
- package/docs/CONFIG-REFERENCE.md +747 -0
- package/docs/MIGRATION-GUIDE.md +617 -0
- package/docs/PLATFORM-DIFFERENCES.md +693 -0
- package/docs/props.md +15 -3
- package/index.d.ts +207 -249
- package/ios/RNJWPlayer/RNJWPlayerView.swift +278 -21
- package/ios/RNJWPlayer/RNJWPlayerViewController.swift +33 -16
- package/package.json +2 -2
- package/types/advertising.d.ts +514 -0
- package/types/index.d.ts +21 -0
- package/types/legacy.d.ts +82 -0
- package/types/platform-specific.d.ts +641 -0
- package/types/playlist.d.ts +410 -0
- package/types/unified-config.d.ts +591 -0
- 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/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/docs/types.md +0 -254
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
# Platform Differences Guide
|
|
2
|
+
|
|
3
|
+
This guide documents the differences between iOS and Android platforms when using JWPlayer React Native.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Configuration Naming Differences](#configuration-naming-differences)
|
|
9
|
+
- [iOS-Specific Features](#ios-specific-features)
|
|
10
|
+
- [Android-Specific Features](#android-specific-features)
|
|
11
|
+
- [Cross-Platform Best Practices](#cross-platform-best-practices)
|
|
12
|
+
- [Common Gotchas](#common-gotchas)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Overview
|
|
17
|
+
|
|
18
|
+
The JWPlayer React Native wrapper provides a **unified configuration interface** that works across both platforms. However, some features are platform-specific due to native SDK differences.
|
|
19
|
+
|
|
20
|
+
### Platform Detection
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Platform } from 'react-native';
|
|
24
|
+
|
|
25
|
+
const config: JWPlayerConfig = {
|
|
26
|
+
license: Platform.OS === 'ios' ? 'IOS_LICENSE' : 'ANDROID_LICENSE',
|
|
27
|
+
file: 'https://example.com/video.m3u8',
|
|
28
|
+
|
|
29
|
+
// Platform-specific feature
|
|
30
|
+
...(Platform.OS === 'ios' && {
|
|
31
|
+
styling: {
|
|
32
|
+
colors: { buttons: '#FF0000' }
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
|
|
36
|
+
...(Platform.OS === 'android' && {
|
|
37
|
+
uiConfig: {
|
|
38
|
+
hasControlbar: true,
|
|
39
|
+
hasOverlay: true
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
};
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Configuration Naming Differences
|
|
48
|
+
|
|
49
|
+
### IMA DAI Settings
|
|
50
|
+
|
|
51
|
+
**Field Name Differences:**
|
|
52
|
+
|
|
53
|
+
| Feature | iOS Parser | Android Parser | **Recommended** |
|
|
54
|
+
|---------|-----------|----------------|-----------------|
|
|
55
|
+
| DAI Settings Object | `googimadai` | `imaDaiSettings` | `imaDaiSettings` ✅ |
|
|
56
|
+
| DAI Settings (alternate) | `googleimadaisettings` | `imaDaiSettings` | `imaDaiSettings` ✅ |
|
|
57
|
+
| IMA Settings Object | `imaSettings` | `imaSdkSettings` | `imaSdkSettings` ✅ |
|
|
58
|
+
| Video ID | `videoID` | `videoId` | `videoId` ✅ |
|
|
59
|
+
| CMS ID | `cmsID` | `cmsId` | `cmsId` ✅ |
|
|
60
|
+
|
|
61
|
+
**✅ Recommended Cross-Platform Config:**
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const config: JWPlayerConfig = {
|
|
65
|
+
license: 'YOUR_LICENSE_KEY',
|
|
66
|
+
file: 'https://example.com/video.m3u8',
|
|
67
|
+
advertising: {
|
|
68
|
+
client: 'dai', // Works on both platforms
|
|
69
|
+
imaDaiSettings: { // Preferred naming
|
|
70
|
+
videoId: 'tears-of-steel', // camelCase
|
|
71
|
+
cmsId: '2528370', // camelCase
|
|
72
|
+
streamType: 'hls'
|
|
73
|
+
},
|
|
74
|
+
imaSdkSettings: { // Preferred naming
|
|
75
|
+
language: 'en'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**iOS-Specific Naming (also supported):**
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// iOS can also use these alternate names
|
|
85
|
+
const iosConfig = {
|
|
86
|
+
advertising: {
|
|
87
|
+
client: 'GoogleIMADAI', // iOS naming
|
|
88
|
+
googimadai: { // iOS naming
|
|
89
|
+
videoID: '...', // Uppercase ID
|
|
90
|
+
cmsID: '...' // Uppercase ID
|
|
91
|
+
},
|
|
92
|
+
imaSettings: { // iOS uses "imaSettings"
|
|
93
|
+
locale: 'en'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Ad Client Values
|
|
100
|
+
|
|
101
|
+
| Ad Type | iOS Values | Android Values | **Recommended** |
|
|
102
|
+
|---------|-----------|----------------|-----------------|
|
|
103
|
+
| VAST | `'vast'`, `'VAST'` | `'vast'`, `'VAST'` | `'vast'` ✅ |
|
|
104
|
+
| Google IMA | `'googima'` | `'googima'`, `'IMA'`, `'GOOGIMA'` | `'googima'` ✅ |
|
|
105
|
+
| Google DAI | `'dai'`, `'GoogleIMADAI'` | `'IMA_DAI'` | `'dai'` ✅ |
|
|
106
|
+
|
|
107
|
+
**Example:**
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// ✅ Cross-platform
|
|
111
|
+
advertising: { client: 'dai' }
|
|
112
|
+
|
|
113
|
+
// ❌ Platform-specific
|
|
114
|
+
advertising: { client: 'GoogleIMADAI' } // iOS only
|
|
115
|
+
advertising: { client: 'IMA_DAI' } // Android only
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Playlist Item Fields
|
|
119
|
+
|
|
120
|
+
| Field | iOS | Android | **Recommended** |
|
|
121
|
+
|-------|-----|---------|-----------------|
|
|
122
|
+
| Media ID | `mediaid` | `mediaid` | `mediaId` ✅ |
|
|
123
|
+
| Ad Schedule | `adschedule` | `adschedule` | `adSchedule` ✅ |
|
|
124
|
+
| Feed ID | `feedid` | `feedid` | `feedId` ✅ |
|
|
125
|
+
| DAI Settings | `daiSetting` | `imaDaiSettings` | `imaDaiSettings` ✅ |
|
|
126
|
+
| Start Time | `starttime` | `starttime` | `startTime` ✅ |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## iOS-Specific Features
|
|
131
|
+
|
|
132
|
+
### 1. Styling Configuration
|
|
133
|
+
|
|
134
|
+
**iOS Only** - Android uses XML styling
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const config: JWPlayerConfig = {
|
|
138
|
+
license: 'YOUR_LICENSE_KEY',
|
|
139
|
+
file: 'https://example.com/video.m3u8',
|
|
140
|
+
styling: { // ⚠️ iOS ONLY
|
|
141
|
+
colors: {
|
|
142
|
+
buttons: '#FF0000',
|
|
143
|
+
backgroundColor: '#000000',
|
|
144
|
+
fontColor: '#FFFFFF',
|
|
145
|
+
timeslider: {
|
|
146
|
+
thumb: '#FF0000',
|
|
147
|
+
rail: '#808080',
|
|
148
|
+
slider: '#FF0000'
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
font: {
|
|
152
|
+
name: 'Helvetica',
|
|
153
|
+
size: 14
|
|
154
|
+
},
|
|
155
|
+
showTitle: true,
|
|
156
|
+
showDesc: true
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Android Alternative:**
|
|
162
|
+
|
|
163
|
+
Use XML styling instead. See [Android Styling Guide](https://docs.jwplayer.com/players/docs/android-styling-guide).
|
|
164
|
+
|
|
165
|
+
### 2. Audio Session Configuration
|
|
166
|
+
|
|
167
|
+
**iOS Only** - Controls background audio behavior
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const config: JWPlayerConfig = {
|
|
171
|
+
license: 'YOUR_LICENSE_KEY',
|
|
172
|
+
file: 'https://example.com/video.m3u8',
|
|
173
|
+
backgroundAudioEnabled: true, // ⚠️ iOS ONLY
|
|
174
|
+
category: 'Playback', // ⚠️ iOS ONLY
|
|
175
|
+
categoryOptions: [ // ⚠️ iOS ONLY
|
|
176
|
+
'MixWithOthers',
|
|
177
|
+
'DuckOthers'
|
|
178
|
+
],
|
|
179
|
+
mode: 'MoviePlayback' // ⚠️ iOS ONLY
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Audio Session Categories:**
|
|
184
|
+
- `Ambient`: Mix with other audio, silence when locked
|
|
185
|
+
- `SoloAmbient`: Default, silences other audio
|
|
186
|
+
- `Playback`: For media playback
|
|
187
|
+
- `Record`: For recording
|
|
188
|
+
- `PlayAndRecord`: For VoIP
|
|
189
|
+
- `MultiRoute`: Multiple audio routes
|
|
190
|
+
|
|
191
|
+
**Category Options:**
|
|
192
|
+
- `MixWithOthers`: Mix with other audio
|
|
193
|
+
- `DuckOthers`: Lower volume of other audio
|
|
194
|
+
- `AllowBluetooth`: Allow Bluetooth A2DP
|
|
195
|
+
- `DefaultToSpeaker`: Route to speaker by default
|
|
196
|
+
- `InterruptSpokenAudioAndMix`: Interrupt spoken audio
|
|
197
|
+
- `AllowAirPlay`: Allow AirPlay
|
|
198
|
+
|
|
199
|
+
### 3. Caption Styling
|
|
200
|
+
|
|
201
|
+
**iOS Only** - Customize caption appearance
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const config: JWPlayerConfig = {
|
|
205
|
+
license: 'YOUR_LICENSE_KEY',
|
|
206
|
+
file: 'https://example.com/video.m3u8',
|
|
207
|
+
styling: { // ⚠️ iOS ONLY
|
|
208
|
+
captionsStyle: {
|
|
209
|
+
fontColor: '#FFFF00',
|
|
210
|
+
fontSize: 16,
|
|
211
|
+
backgroundColor: '#000000',
|
|
212
|
+
backgroundOpacity: 75,
|
|
213
|
+
windowColor: '#000000',
|
|
214
|
+
windowOpacity: 0,
|
|
215
|
+
edgeStyle: 'dropshadow', // 'none' | 'dropshadow' | 'raised' | 'depressed' | 'uniform'
|
|
216
|
+
fontFamily: 'Helvetica'
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Android:** Uses system accessibility settings for captions.
|
|
223
|
+
|
|
224
|
+
### 4. Interface Control
|
|
225
|
+
|
|
226
|
+
**iOS Only** - Control UI behavior
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const config: JWPlayerConfig = {
|
|
230
|
+
license: 'YOUR_LICENSE_KEY',
|
|
231
|
+
file: 'https://example.com/video.m3u8',
|
|
232
|
+
interfaceBehavior: 'normal', // ⚠️ iOS ONLY: 'normal' | 'hidden' | 'onscreen'
|
|
233
|
+
interfaceFadeDelay: 3, // ⚠️ iOS ONLY: Seconds before UI fades
|
|
234
|
+
hideUIGroups: [ // ⚠️ iOS ONLY
|
|
235
|
+
'settings_menu',
|
|
236
|
+
'casting_menu',
|
|
237
|
+
'quality_submenu'
|
|
238
|
+
]
|
|
239
|
+
};
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 5. FairPlay DRM
|
|
243
|
+
|
|
244
|
+
**iOS Only** - FairPlay DRM
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
const config: JWPlayerConfig = {
|
|
248
|
+
license: 'YOUR_LICENSE_KEY',
|
|
249
|
+
sources: [{
|
|
250
|
+
file: 'https://example.com/video.m3u8',
|
|
251
|
+
drm: {
|
|
252
|
+
fairplay: { // ⚠️ iOS ONLY
|
|
253
|
+
processSpcUrl: 'https://drm-server.com/spc',
|
|
254
|
+
certificateUrl: 'https://drm-server.com/cert'
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}],
|
|
258
|
+
// Global FairPlay settings (iOS only)
|
|
259
|
+
processSpcUrl: 'https://drm-server.com/spc',
|
|
260
|
+
fairplayCertUrl: 'https://drm-server.com/cert',
|
|
261
|
+
contentUUID: 'unique-content-id'
|
|
262
|
+
};
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 6. External Playback (AirPlay)
|
|
266
|
+
|
|
267
|
+
**iOS Only**
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
const config: JWPlayerConfig = {
|
|
271
|
+
license: 'YOUR_LICENSE_KEY',
|
|
272
|
+
file: 'https://example.com/video.m3u8',
|
|
273
|
+
externalPlaybackSettings: { // ⚠️ iOS ONLY
|
|
274
|
+
playbackEnabled: true,
|
|
275
|
+
usesExternalPlaybackWhileExternalScreenIsActive: true,
|
|
276
|
+
videoGravity: 'resizeAspect'
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Android-Specific Features
|
|
284
|
+
|
|
285
|
+
### 1. UI Configuration
|
|
286
|
+
|
|
287
|
+
**Android Only** - Granular UI control
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
const config: JWPlayerConfig = {
|
|
291
|
+
license: 'YOUR_LICENSE_KEY',
|
|
292
|
+
file: 'https://example.com/video.m3u8',
|
|
293
|
+
uiConfig: { // ⚠️ ANDROID ONLY
|
|
294
|
+
hasOverlay: true,
|
|
295
|
+
hasControlbar: true,
|
|
296
|
+
hasCenterControls: true,
|
|
297
|
+
hasNextUp: true,
|
|
298
|
+
hasQualitySubMenu: true,
|
|
299
|
+
hasCaptionsSubMenu: true,
|
|
300
|
+
hasPlaybackRatesSubMenu: true,
|
|
301
|
+
hasMenu: true,
|
|
302
|
+
hasAds: true
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**⚠️ Important:** When using `uiConfig`, ALL unspecified elements default to `false`. You must explicitly enable each element you want visible.
|
|
308
|
+
|
|
309
|
+
**iOS Alternative:** Use `hideUIGroups` or `interfaceBehavior`.
|
|
310
|
+
|
|
311
|
+
### 2. TextureView
|
|
312
|
+
|
|
313
|
+
**Android Only**
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const config: JWPlayerConfig = {
|
|
317
|
+
license: 'YOUR_LICENSE_KEY',
|
|
318
|
+
file: 'https://example.com/video.m3u8',
|
|
319
|
+
useTextureView: true // ⚠️ ANDROID ONLY
|
|
320
|
+
};
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Use Case:** Required for applying transformations (rotation, scaling) to the video view.
|
|
324
|
+
|
|
325
|
+
### 3. Ad Rules
|
|
326
|
+
|
|
327
|
+
**Android Primary** (iOS has limited support)
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const config: JWPlayerConfig = {
|
|
331
|
+
license: 'YOUR_LICENSE_KEY',
|
|
332
|
+
playlist: [...],
|
|
333
|
+
advertising: {
|
|
334
|
+
client: 'vast',
|
|
335
|
+
schedule: [...],
|
|
336
|
+
rules: {
|
|
337
|
+
startOn: 2, // ✅ Both platforms
|
|
338
|
+
frequency: 2, // ✅ Both platforms
|
|
339
|
+
timeBetweenAds: 300, // ⚠️ ANDROID ONLY
|
|
340
|
+
startOnSeek: 'pre' // ⚠️ ANDROID ONLY
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**Platform Support:**
|
|
347
|
+
- `startOn`: ✅ iOS, ✅ Android
|
|
348
|
+
- `frequency`: ✅ iOS, ✅ Android
|
|
349
|
+
- `timeBetweenAds`: ❌ iOS, ✅ Android
|
|
350
|
+
- `startOnSeek`: ❌ iOS, ✅ Android
|
|
351
|
+
|
|
352
|
+
### 4. Widevine DRM
|
|
353
|
+
|
|
354
|
+
**Android Only** - Widevine DRM
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
const config: JWPlayerConfig = {
|
|
358
|
+
license: 'YOUR_LICENSE_KEY',
|
|
359
|
+
sources: [{
|
|
360
|
+
file: 'https://example.com/video.mpd',
|
|
361
|
+
drm: {
|
|
362
|
+
widevine: { // ⚠️ ANDROID ONLY
|
|
363
|
+
url: 'https://license-server.com/license',
|
|
364
|
+
keySetId: 'optional-key-id'
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}]
|
|
368
|
+
};
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### 5. HTTP Headers
|
|
372
|
+
|
|
373
|
+
**Android Only**
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
const playlistItem: JWPlaylistItem = {
|
|
377
|
+
file: 'https://example.com/video.m3u8',
|
|
378
|
+
httpheaders: { // ⚠️ ANDROID ONLY
|
|
379
|
+
'Authorization': 'Bearer token',
|
|
380
|
+
'Custom-Header': 'value'
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 6. Cross-Protocol Redirects
|
|
386
|
+
|
|
387
|
+
**Android Only**
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
const config: JWPlayerConfig = {
|
|
391
|
+
license: 'YOUR_LICENSE_KEY',
|
|
392
|
+
file: 'https://example.com/video.m3u8',
|
|
393
|
+
allowCrossProtocolRedirectsSupport: true // ⚠️ ANDROID ONLY
|
|
394
|
+
};
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### 7. Display Title/Description
|
|
398
|
+
|
|
399
|
+
**Android Only** (iOS always shows based on `showTitle`/`showDesc` in styling)
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
const config: JWPlayerConfig = {
|
|
403
|
+
license: 'YOUR_LICENSE_KEY',
|
|
404
|
+
file: 'https://example.com/video.m3u8',
|
|
405
|
+
displaytitle: true, // ⚠️ ANDROID ONLY
|
|
406
|
+
displaydescription: true // ⚠️ ANDROID ONLY
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Cross-Platform Best Practices
|
|
413
|
+
|
|
414
|
+
### 1. Use Conditional Configuration
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { Platform } from 'react-native';
|
|
418
|
+
|
|
419
|
+
const config: JWPlayerConfig = {
|
|
420
|
+
license: Platform.OS === 'ios' ? IOS_LICENSE : ANDROID_LICENSE,
|
|
421
|
+
file: 'https://example.com/video.m3u8',
|
|
422
|
+
autostart: true,
|
|
423
|
+
|
|
424
|
+
// iOS-specific features
|
|
425
|
+
...(Platform.OS === 'ios' && {
|
|
426
|
+
styling: {
|
|
427
|
+
colors: { buttons: '#FF0000' }
|
|
428
|
+
},
|
|
429
|
+
backgroundAudioEnabled: true
|
|
430
|
+
}),
|
|
431
|
+
|
|
432
|
+
// Android-specific features
|
|
433
|
+
...(Platform.OS === 'android' && {
|
|
434
|
+
uiConfig: {
|
|
435
|
+
hasControlbar: true,
|
|
436
|
+
hasOverlay: true
|
|
437
|
+
},
|
|
438
|
+
useTextureView: true
|
|
439
|
+
})
|
|
440
|
+
};
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### 2. Unified Naming Convention
|
|
444
|
+
|
|
445
|
+
Always use the **recommended** cross-platform naming:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// ✅ GOOD - Cross-platform
|
|
449
|
+
const config: JWPlayerConfig = {
|
|
450
|
+
advertising: {
|
|
451
|
+
client: 'dai',
|
|
452
|
+
imaDaiSettings: {
|
|
453
|
+
videoId: 'tears-of-steel',
|
|
454
|
+
cmsId: '2528370'
|
|
455
|
+
},
|
|
456
|
+
imaSdkSettings: {
|
|
457
|
+
language: 'en'
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// ❌ AVOID - Platform-specific naming
|
|
463
|
+
const config: JWPlayerConfig = {
|
|
464
|
+
advertising: {
|
|
465
|
+
client: 'GoogleIMADAI', // iOS-specific
|
|
466
|
+
googimadai: { // iOS-specific
|
|
467
|
+
videoID: '...', // Uppercase (iOS)
|
|
468
|
+
cmsID: '...' // Uppercase (iOS)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### 3. Handle DRM Properly
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
import { Platform } from 'react-native';
|
|
478
|
+
|
|
479
|
+
const getDrmConfig = () => {
|
|
480
|
+
if (Platform.OS === 'ios') {
|
|
481
|
+
return {
|
|
482
|
+
fairplay: {
|
|
483
|
+
processSpcUrl: 'https://drm.example.com/fps',
|
|
484
|
+
certificateUrl: 'https://drm.example.com/cert'
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
} else {
|
|
488
|
+
return {
|
|
489
|
+
widevine: {
|
|
490
|
+
url: 'https://drm.example.com/widevine'
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
const config: JWPlayerConfig = {
|
|
497
|
+
license: Platform.OS === 'ios' ? IOS_LICENSE : ANDROID_LICENSE,
|
|
498
|
+
sources: [{
|
|
499
|
+
file: Platform.OS === 'ios'
|
|
500
|
+
? 'https://example.com/fairplay.m3u8'
|
|
501
|
+
: 'https://example.com/widevine.mpd',
|
|
502
|
+
label: '1080p',
|
|
503
|
+
drm: getDrmConfig()
|
|
504
|
+
}]
|
|
505
|
+
};
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### 4. Test on Both Platforms
|
|
509
|
+
|
|
510
|
+
Always test configurations on both iOS and Android devices:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
// Helper function for testing
|
|
514
|
+
export const validateConfig = (config: JWPlayerConfig): string[] => {
|
|
515
|
+
const warnings: string[] = [];
|
|
516
|
+
|
|
517
|
+
if (Platform.OS === 'android' && config.styling) {
|
|
518
|
+
warnings.push('`styling` is iOS-only. Use XML styling on Android.');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (Platform.OS === 'ios' && config.uiConfig) {
|
|
522
|
+
warnings.push('`uiConfig` is Android-only. iOS uses native UI.');
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (Platform.OS === 'ios' && config.useTextureView) {
|
|
526
|
+
warnings.push('`useTextureView` is Android-only.');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return warnings;
|
|
530
|
+
};
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## Common Gotchas
|
|
536
|
+
|
|
537
|
+
### 1. IMA DAI Naming Confusion
|
|
538
|
+
|
|
539
|
+
❌ **Wrong:**
|
|
540
|
+
```typescript
|
|
541
|
+
// Mixing iOS and Android naming
|
|
542
|
+
advertising: {
|
|
543
|
+
client: 'GoogleIMADAI', // iOS naming
|
|
544
|
+
imaDaiSettings: { // Android naming
|
|
545
|
+
videoID: '...', // iOS casing
|
|
546
|
+
cmsId: '...' // Android casing
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
✅ **Correct:**
|
|
552
|
+
```typescript
|
|
553
|
+
advertising: {
|
|
554
|
+
client: 'dai', // Cross-platform
|
|
555
|
+
imaDaiSettings: { // Preferred
|
|
556
|
+
videoId: '...', // camelCase
|
|
557
|
+
cmsId: '...' // camelCase
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### 2. UI Configuration Defaults
|
|
563
|
+
|
|
564
|
+
❌ **Wrong (Android):**
|
|
565
|
+
```typescript
|
|
566
|
+
// Only specifying some UI elements
|
|
567
|
+
uiConfig: {
|
|
568
|
+
hasControlbar: true
|
|
569
|
+
// Oops! All other elements are now hidden
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
✅ **Correct (Android):**
|
|
574
|
+
```typescript
|
|
575
|
+
// Specify ALL elements you want visible
|
|
576
|
+
uiConfig: {
|
|
577
|
+
hasOverlay: true,
|
|
578
|
+
hasControlbar: true,
|
|
579
|
+
hasCenterControls: true,
|
|
580
|
+
hasNextUp: true,
|
|
581
|
+
hasQualitySubMenu: true,
|
|
582
|
+
hasCaptionsSubMenu: true,
|
|
583
|
+
hasMenu: true,
|
|
584
|
+
hasAds: true
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### 3. License Keys
|
|
589
|
+
|
|
590
|
+
❌ **Wrong:**
|
|
591
|
+
```typescript
|
|
592
|
+
// Using same license for both platforms
|
|
593
|
+
const config = {
|
|
594
|
+
license: 'SINGLE_LICENSE_KEY' // Won't work!
|
|
595
|
+
};
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
✅ **Correct:**
|
|
599
|
+
```typescript
|
|
600
|
+
import { Platform } from 'react-native';
|
|
601
|
+
|
|
602
|
+
const config = {
|
|
603
|
+
license: Platform.OS === 'ios'
|
|
604
|
+
? process.env.IOS_LICENSE_KEY
|
|
605
|
+
: process.env.ANDROID_LICENSE_KEY
|
|
606
|
+
};
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### 4. Styling vs UI Config
|
|
610
|
+
|
|
611
|
+
❌ **Wrong:**
|
|
612
|
+
```typescript
|
|
613
|
+
// Trying to use both
|
|
614
|
+
const config = {
|
|
615
|
+
styling: { ... }, // iOS
|
|
616
|
+
uiConfig: { ... } // Android
|
|
617
|
+
// Will be ignored on wrong platform
|
|
618
|
+
};
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
✅ **Correct:**
|
|
622
|
+
```typescript
|
|
623
|
+
const config = {
|
|
624
|
+
...(Platform.OS === 'ios' && {
|
|
625
|
+
styling: { ... }
|
|
626
|
+
}),
|
|
627
|
+
...(Platform.OS === 'android' && {
|
|
628
|
+
uiConfig: { ... }
|
|
629
|
+
})
|
|
630
|
+
};
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### 5. Ad Rules Assumptions
|
|
634
|
+
|
|
635
|
+
❌ **Wrong:**
|
|
636
|
+
```typescript
|
|
637
|
+
// Assuming full ad rules support on iOS
|
|
638
|
+
advertising: {
|
|
639
|
+
client: 'vast',
|
|
640
|
+
rules: {
|
|
641
|
+
startOn: 2,
|
|
642
|
+
timeBetweenAds: 300 // iOS doesn't support this!
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
✅ **Correct:**
|
|
648
|
+
```typescript
|
|
649
|
+
advertising: {
|
|
650
|
+
client: 'vast',
|
|
651
|
+
rules: {
|
|
652
|
+
startOn: 2,
|
|
653
|
+
frequency: 2,
|
|
654
|
+
...(Platform.OS === 'android' && {
|
|
655
|
+
timeBetweenAds: 300,
|
|
656
|
+
startOnSeek: 'pre'
|
|
657
|
+
})
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## Summary Table
|
|
665
|
+
|
|
666
|
+
| Feature | iOS | Android | Recommendation |
|
|
667
|
+
|---------|-----|---------|----------------|
|
|
668
|
+
| **Styling** | ✅ Full | ❌ Use XML | Platform-specific |
|
|
669
|
+
| **UI Config** | ❌ Use hideUIGroups | ✅ Full | Platform-specific |
|
|
670
|
+
| **Background Audio** | ✅ Full | ❌ | iOS only |
|
|
671
|
+
| **Caption Styling** | ✅ Full | ❌ System settings | iOS only |
|
|
672
|
+
| **FairPlay DRM** | ✅ | ❌ | iOS only |
|
|
673
|
+
| **Widevine DRM** | ❌ | ✅ | Android only |
|
|
674
|
+
| **Ad Rules (full)** | ⚠️ Limited | ✅ Full | Platform-aware |
|
|
675
|
+
| **HTTP Headers** | ❌ | ✅ | Android only |
|
|
676
|
+
| **TextureView** | ❌ | ✅ | Android only |
|
|
677
|
+
| **AirPlay** | ✅ | ❌ | iOS only |
|
|
678
|
+
| **IMA DAI** | ✅ | ✅ | Use `imaDaiSettings` |
|
|
679
|
+
| **VAST/IMA** | ✅ | ✅ | Fully cross-platform |
|
|
680
|
+
|
|
681
|
+
---
|
|
682
|
+
|
|
683
|
+
## See Also
|
|
684
|
+
|
|
685
|
+
- [Configuration Reference](./CONFIG-REFERENCE.md) - Complete config documentation
|
|
686
|
+
- [Migration Guide](./MIGRATION-GUIDE.md) - Upgrading from legacy configs
|
|
687
|
+
- [TypeScript Example](../Example/app/jsx/screens/TypeScriptExample.tsx) - Example with platform-specific configurations
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
*Last Updated: October 2025*
|
|
692
|
+
*SDK Version: JWPlayer React Native*
|
|
693
|
+
|
package/docs/props.md
CHANGED
|
@@ -2,17 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
This wrapper implements the native methods exposed by the [Android](https://sdk.jwplayer.com/android/v4/reference/com/jwplayer/pub/api/JsonHelper.html) and [iOS](https://sdk.jwplayer.com/ios/v4/reference/Classes/JWJSONParser.html) SDK for parsing JSON objects into player configs.
|
|
4
4
|
|
|
5
|
+
## Component Props
|
|
6
|
+
|
|
5
7
|
| Prop | Type | Platform | Description | Default |
|
|
6
8
|
| --- | --- | --- | --- | --- |
|
|
7
|
-
| `config` | object | A, I | **(REQUIRED)** `
|
|
9
|
+
| `config` | object | A, I | **(REQUIRED)** `JWPlayerConfig` or `Config` object<br />See: [Configuration Reference](./CONFIG-REFERENCE.md) | `undefined` |
|
|
8
10
|
| `controls` | boolean | A, I | Determines if player controls are displayed | `true` |
|
|
9
11
|
|
|
10
12
|
<br /><br />
|
|
11
13
|
|
|
14
|
+
## Configuration Documentation
|
|
15
|
+
|
|
16
|
+
📚 **Complete documentation is now available in separate guides:**
|
|
17
|
+
|
|
18
|
+
- **[Configuration Reference](./CONFIG-REFERENCE.md)** - Complete field reference with examples
|
|
19
|
+
- **[Platform Differences](./PLATFORM-DIFFERENCES.md)** - iOS vs Android feature comparison
|
|
20
|
+
- **[Migration Guide](./MIGRATION-GUIDE.md)** - Upgrading to unified types
|
|
21
|
+
|
|
22
|
+
<br />
|
|
23
|
+
|
|
12
24
|
## Config
|
|
13
|
-
With the exception of `license` and `
|
|
25
|
+
With the exception of `license` and one of (`file`, `sources`, or `playlist`), all other fields are optional.
|
|
14
26
|
|
|
15
|
-
See [
|
|
27
|
+
See the [Configuration Reference](./CONFIG-REFERENCE.md) for complete documentation of all configuration options.
|
|
16
28
|
|
|
17
29
|
| Field | Type | Platform | Additional Notes |
|
|
18
30
|
| --- | --- | --- | --- |
|