@jwplayer/jwplayer-react-native 1.1.3 → 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.
Files changed (36) hide show
  1. package/README.md +114 -21
  2. package/RNJWPlayer.podspec +1 -1
  3. package/android/build.gradle +14 -1
  4. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java +27 -0
  5. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +373 -204
  6. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java +16 -0
  7. package/android/src/main/java/com/jwplayer/rnjwplayer/Util.java +13 -1
  8. package/badges/version.svg +1 -1
  9. package/docs/CONFIG-REFERENCE.md +747 -0
  10. package/docs/MIGRATION-GUIDE.md +617 -0
  11. package/docs/PLATFORM-DIFFERENCES.md +693 -0
  12. package/docs/props.md +15 -3
  13. package/index.d.ts +225 -216
  14. package/index.js +34 -0
  15. package/ios/RNJWPlayer/RNJWPlayerView.swift +365 -10
  16. package/ios/RNJWPlayer/RNJWPlayerViewController.swift +45 -16
  17. package/ios/RNJWPlayer/RNJWPlayerViewManager.m +2 -0
  18. package/ios/RNJWPlayer/RNJWPlayerViewManager.swift +13 -0
  19. package/package.json +2 -2
  20. package/types/advertising.d.ts +514 -0
  21. package/types/index.d.ts +21 -0
  22. package/types/legacy.d.ts +82 -0
  23. package/types/platform-specific.d.ts +641 -0
  24. package/types/playlist.d.ts +410 -0
  25. package/types/unified-config.d.ts +591 -0
  26. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  27. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  28. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  29. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  30. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  31. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  32. package/android/.gradle/8.9/gc.properties +0 -0
  33. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  34. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  35. package/android/.gradle/vcs-1/gc.properties +0 -0
  36. package/docs/types.md +0 -254
package/index.d.ts CHANGED
@@ -2,35 +2,117 @@ declare module "@jwplayer/jwplayer-react-native" {
2
2
  import React from "react";
3
3
  import { ViewStyle } from "react-native";
4
4
 
5
+ // ============================================================================
6
+ // UNIFIED CONFIGURATION TYPES
7
+ // ============================================================================
8
+ // For detailed type definitions, see types/ directory
9
+ // Types are also available by importing from '@jwplayer/jwplayer-react-native/types'
10
+
11
+ // Main configuration interface - see types/unified-config.d.ts for full documentation
12
+ export interface JWPlayerConfig {
13
+ license: string;
14
+ file?: string;
15
+ sources?: any[];
16
+ playlist?: any[] | string;
17
+ playlistIndex?: number;
18
+ autostart?: boolean;
19
+ mute?: boolean;
20
+ repeat?: boolean;
21
+ preload?: 'auto' | 'none' | boolean;
22
+ stretching?: 'uniform' | 'fill' | 'exactfit' | 'none';
23
+ playbackRates?: number[];
24
+ playbackRateControls?: boolean;
25
+ bitrateUpperBound?: number;
26
+ advertising?: any;
27
+ related?: any;
28
+ pid?: string;
29
+ playerId?: string;
30
+ styling?: any;
31
+ uiConfig?: any;
32
+ backgroundAudioEnabled?: boolean;
33
+ category?: string;
34
+ categoryOptions?: string[];
35
+ mode?: string;
36
+ forceLegacyConfig?: boolean;
37
+ playlistItemCallbackEnabled?: boolean;
38
+ playerInModal?: boolean;
39
+ fullScreenOnLandscape?: boolean;
40
+ landscapeOnFullScreen?: boolean;
41
+ portraitOnExitFullScreen?: boolean;
42
+ exitFullScreenOnPortrait?: boolean;
43
+ enableLockScreenControls?: boolean;
44
+ pipEnabled?: boolean;
45
+ useTextureView?: boolean;
46
+ allowCrossProtocolRedirectsSupport?: boolean;
47
+ displaytitle?: boolean;
48
+ displayTitle?: boolean;
49
+ displaydescription?: boolean;
50
+ displayDescription?: boolean;
51
+ nextupoffset?: string | number;
52
+ thumbnailPreview?: number;
53
+ logoView?: any;
54
+ title?: string;
55
+ description?: string;
56
+ image?: string;
57
+ tracks?: any[];
58
+ starttime?: number;
59
+ mediaid?: string;
60
+ mediaId?: string;
61
+ [key: string]: any;
62
+ }
63
+
64
+ // Main configuration type alias
65
+ export type Config = JWPlayerConfig;
66
+
67
+ // Re-export common types with proper names
68
+ export type JWAdvertisingConfig = any;
69
+ export type JWPlaylistItem = any;
70
+ export type JWSource = any;
71
+ export type JWTrack = any;
72
+ export type JWImaDaiSettings = any;
73
+ export type JWImaSdkSettings = any;
74
+ export type JWAdBreak = any;
75
+ export type JWAdRules = any;
76
+ export type JWUiConfig = any;
77
+ export type JWLogoView = any;
78
+ export type JWRelatedConfig = any;
79
+ export type JWStyling = any;
80
+ export type ThumbnailPreview = 101 | 102 | 103;
81
+ export type Stretching = 'uniform' | 'fill' | 'exactfit' | 'none';
82
+
83
+ // ============================================================================
84
+ // BACKWARD COMPATIBILITY
85
+ // ============================================================================
86
+
87
+ /**
88
+ * @deprecated Use JWPlayerConfig or Config instead
89
+ * Maintained for backward compatibility
90
+ */
5
91
  interface JwConfig {
6
92
  pid?: string;
7
93
  mute?: boolean;
8
94
  forceLegacyConfig?: boolean;
9
- /**
10
- * If true, `onBeforeNextPlaylistItem` MUST be impelemented with `player.resolveNextPlaylistItem()` called in the callback or content will hang
11
- */
12
95
  playlistItemCallbackEnabled?: boolean;
13
96
  useTextureView?: boolean;
14
97
  autostart?: boolean;
15
- nextupoffset?: string | number; // String with % or number
98
+ nextupoffset?: string | number;
16
99
  repeat?: boolean;
17
- allowCrossProtocolRedirectsSupport?: boolean; // maybe android only?
100
+ allowCrossProtocolRedirectsSupport?: boolean;
18
101
  displaytitle?: boolean;
19
102
  displaydescription?: boolean;
20
- stretching?: JwStretching;
21
- thumbnailPreview?: JwThumbnailPreview;
103
+ stretching?: string;
104
+ thumbnailPreview?: number;
22
105
  preload?: boolean;
23
- playlist?: JwPlaylistItem[] | string;
24
- sources?: JwSource[]; // Can be used in place of `playlist` to build a playlist, but not recommended (cannot be used for DRM)
25
- file?: string; // Can be used in place of `playlist` to build a playlist, but not recommended (cannot be used for DRM)
106
+ playlist?: any[] | string;
107
+ sources?: any[];
108
+ file?: string;
26
109
  playlistIndex?: number;
27
- related?: JwRelatedConfig;
28
- uiConfig?: JwUiConfig;
29
- logoView?: JwLogoView;
30
- advertising?: JwAdvertisingConfig;
110
+ related?: any;
111
+ uiConfig?: any;
112
+ logoView?: any;
113
+ advertising?: any;
31
114
  playbackRates?: number[];
32
115
  playbackRateControls?: boolean;
33
- // Non-Json Parsing props
34
116
  license: string;
35
117
  playerInModal?: boolean;
36
118
  fullScreenOnLandscape?: boolean;
@@ -39,208 +121,72 @@ declare module "@jwplayer/jwplayer-react-native" {
39
121
  exitFullScreenOnPortrait?: boolean;
40
122
  enableLockScreenControls?: boolean;
41
123
  pipEnabled?: boolean;
42
- }
43
-
44
- type JwThumbnailPreview = 101 | 102 | 103;
45
-
46
- type JwStretching = "uniform" | "fill" | "exactfit" | "none";
47
-
48
- type JwAdvertisingConfig = VmapAdvertisingConfig | VastAdvertisingConfig | ImaVmapAdvertisingConfig
49
- | ImaAdvertisingConfig | ImaDaiAdvertisingConfig;
50
-
51
- interface JwAdRules {
52
- startOn?: number;
53
- frequency?: number;
54
- timeBetweenAds?: number;
55
- startOnSeek?: JwStartOnSeek;
56
- }
57
-
124
+ [key: string]: any;
125
+ }
126
+
127
+ // ============================================================================
128
+ // TYPE ALIASES FOR BACKWARD COMPATIBILITY
129
+ // ============================================================================
130
+
131
+ /**
132
+ * @deprecated Use Stretching from unified types
133
+ */
134
+ type JwStretching = Stretching;
135
+
136
+ /**
137
+ * @deprecated Use ThumbnailPreview from unified types
138
+ */
139
+ type JwThumbnailPreview = ThumbnailPreview;
140
+
141
+ // Re-export advertising types with legacy names for backward compatibility
142
+ /**
143
+ * @deprecated Advertising types are now imported from unified types
144
+ * Use JWAdvertisingConfig instead
145
+ */
146
+ type VmapAdvertisingConfig = import('./types').VmapAdvertisingConfig;
147
+ type VastAdvertisingConfig = import('./types').VastAdvertisingConfig;
148
+ type ImaVmapAdvertisingConfig = import('./types').ImaAdvertisingConfig;
149
+ type ImaAdvertisingConfig = import('./types').ImaAdvertisingConfig;
150
+ type ImaDaiAdvertisingConfig = import('./types').ImaDaiAdvertisingConfig;
151
+
152
+ /**
153
+ * @deprecated Use JWAdRules from unified types
154
+ */
58
155
  type JwStartOnSeek = "pre" | "none";
59
- interface VmapAdvertisingConfig {
60
- cuetext?: string;
61
- adpodmessage?: string;
62
- vpaidcontrols?: boolean;
63
- requestTimeout?: number;
64
- creativeTimeout?: number;
65
- conditionaladoptout?: boolean;
66
- schedule: string; // Must bestring for VMAP
67
- rules?: JwAdRules;
68
- allowedOmidVendors?: string[];
69
- omidSupport?: JwOmidSupport;
70
- admessage?: string;
71
- skipmessage?: string;
72
- skiptext?: string;
73
- skipoffset?: number;
74
- }
75
-
76
- interface VastAdvertisingConfig {
77
- cuetext?: string;
78
- adpodmessage?: string;
79
- vpaidcontrols?: boolean;
80
- requestTimeout?: number;
81
- creativeTimeout?: number;
82
- conditionaladoptout?: boolean;
83
- schedule?: JwAdBreak[]; // Array of breaks or object of breaks
84
- rules: JwAdRules;
85
- allowedOmidVendors?: string[];
86
- omidSupport?: JwOmidSupport;
87
- admessage?: string;
88
- skipmessage?: string;
89
- skiptext?: string;
90
- skipoffset?: number;
91
- }
92
-
156
+
157
+ /**
158
+ * @deprecated Use JWAdBreak from unified types
159
+ */
160
+ type JwAdType = "LINEAR" | "NONLINEAR";
161
+
162
+ /**
163
+ * @deprecated Use OmidSupport from unified types
164
+ */
93
165
  type JwOmidSupport = "auto" | "enabled" | "disabled";
94
-
95
- interface ImaVmapAdvertisingConfig {
96
- imaSdkSettings?: JwImaSdkSettings;
97
- tag?: string;
98
- }
99
-
100
- interface ImaAdvertisingConfig {
101
- imaSdkSettings?: JwImaSdkSettings;
102
- schedule?: JwAdBreak[] | JwAdBreak; // Array of breaks or object of breaks
103
- }
104
-
105
- interface ImaDaiAdvertisingConfig {
106
- imaDaiSettings?: JwImaDaiSettings;
107
- imaSdkSettings?: JwImaSdkSettings;
108
- }
109
-
110
- interface JwImaSdkSettings {
111
- sessionId?: string;
112
- ppid?: string;
113
- autoPlayAdBreaks?: boolean;
114
- language?: string;
115
- maxRedirects?: number; //int
116
- playerType?: string;
117
- playerVersion?: string;
118
- isDebugMode?: boolean;
119
- doesRestrictToCustomPlayer?: boolean;
120
- }
121
-
122
- interface JwLogoView {
123
- imageFile: string;
124
- fades: boolean; // margin required for fade on Android
125
- margin?: number;
126
- position?: JwLogoPosition;
127
- webLink: string;
128
- }
129
-
166
+
167
+ /**
168
+ * @deprecated Use LogoPosition from unified types
169
+ */
130
170
  type JwLogoPosition = "topLeft" | "topRight" | "bottomLeft" | "bottomRight";
131
171
 
132
- interface JwUiConfig { // all default to false. When using this, it's specify all or they are assumed false
133
- hasOverlay?: boolean;
134
- hasControlbar?: boolean;
135
- hasCenterControls?: boolean;
136
- hasNextUp?: boolean;
137
- hasSideSeek?: boolean;
138
- hasLogoView?: boolean;
139
- hasError?: boolean;
140
- hasPlaylist?: boolean;
141
- hasQualitySubMenu?: boolean;
142
- hasCaptionsSubMenu?: boolean;
143
- hasPlaybackRatesSubMenu?: boolean;
144
- hasAudiotracksSubMenu?: boolean;
145
- hasMenu?: boolean;
146
- hasPlayerControlsContainer?: boolean;
147
- hasCastingMenu?: boolean;
148
- hasChapters?: boolean;
149
- hasAds?: boolean;
150
- }
151
-
152
- interface JwRelatedConfig {
153
- file?: string;
154
- oncomplete?: JwRelatedOnComplete;
155
- onclick?: JwOnRelatedClick;
156
- // autoplaymessage: string; // deprecated
157
- autoplaytimer: number;
158
- }
159
-
160
- interface JwPlaylistItem {
161
- title?: string;
162
- description?: string;
163
- file?: string;
164
- image?: string;
165
- mediaid?: string;
166
- feedid?: string;
167
- recommendations?: string;
168
- starttime?: number; // double -- default 0.0
169
- duration: number; // int -- default 0
170
- tracks?: JwTrack[];
171
- sources?: JwSource[];
172
- externalMetadata?: JwExternalMetadata[];
173
- adschedule?: JwAdBreak[]; // array of schedules
174
- schedule?: { [key: string]: JwAdBreak };
175
- imaDaiSettings?: JwImaDaiSettings;
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 };
181
- }
182
-
183
- interface JwImaDaiSettings {
184
- videoID?: string;
185
- cmsID?: string;
186
- assetKey?: string;
187
- apiKey?: string;
188
- streamType?: string;
189
- adTagParameters?: { [key: string]: string };
190
- }
191
-
192
- interface JwAdBreak {
193
- ad?: string | string[];
194
- offset?: string;
195
- skipoffset?: number;
196
- type?: JwAdType;
197
- custParams?: { [key: string]: string };
198
- }
199
-
200
- type JwAdType = "LINEAR" | "NONLINEAR";
201
-
202
- interface JwExternalMetadata {
203
- startTime?: number; // double
204
- endTime?: number; // double
205
- id: number; // int
206
- }
207
-
208
- interface JwSource {
209
- drm?: JwDrm;
210
- file?: string;
211
- label?: string;
212
- default?: string;
213
- type?: string;
214
- httpheaders?: { [key: string]: string };
215
- }
216
-
217
- interface JwDrm {
218
- widevine?: JwWidevine;
219
- fairplay?: JwFairplay;
220
- }
221
-
222
- interface JwFairplay {
223
- processSpcUrl?: string;
224
- certificateUrl?: string;
225
- }
226
-
227
- interface JwWidevine {
228
- url?: string;
229
- keySetId?: string;
230
- }
231
-
232
- interface JwTrack {
233
- id?: string;
234
- file?: string;
235
- kind: TrackKind;
236
- label?: string;
237
- default?: boolean;
238
- }
239
-
172
+ // Re-export playlist and media types for backward compatibility
173
+ /**
174
+ * @deprecated These types are now imported from unified types
175
+ * Direct usage is discouraged - import from './types' instead
176
+ */
240
177
  type JwRelatedOnComplete = "hide" | "show" | "none" | "autoplay";
241
-
242
178
  type JwOnRelatedClick = "play" | "link";
243
179
 
180
+ // ============================================================================
181
+ // LEGACY TYPE DEFINITIONS
182
+ // ============================================================================
183
+ // The following types are maintained for backward compatibility with existing code.
184
+ // New code should use the unified types imported from './types' above.
185
+ // These legacy definitions may be removed in a future major version.
186
+
187
+ /**
188
+ * @deprecated Use types from './types' instead
189
+ */
244
190
  interface AudioTrack {
245
191
  autoSelect: boolean;
246
192
  defaultTrack: boolean;
@@ -458,7 +404,10 @@ declare module "@jwplayer/jwplayer-react-native" {
458
404
  | "settings"
459
405
  | "languages"
460
406
  | "fullscreen";
461
- interface Config {
407
+ /**
408
+ * @deprecated Legacy iOS config interface - use JWPlayerConfig instead
409
+ */
410
+ interface LegacyIOSConfig {
462
411
  license: string;
463
412
  advertising?: Advertising;
464
413
  autostart?: boolean;
@@ -559,7 +508,7 @@ declare module "@jwplayer/jwplayer-react-native" {
559
508
  type NativeError = (event: BaseEvent<PlayerErrorEventProps> | BaseEvent<PlayerSetupErrorProps> | BaseEvent<PlayerErrorProps>) => void;
560
509
  type NativeWarning = (event: BaseEvent<PlayerWarningEventProps>) => void;
561
510
  interface PropsType {
562
- config: Config | JwConfig;
511
+ config: JWPlayerConfig | JwConfig | LegacyIOSConfig;
563
512
  style?: ViewStyle;
564
513
  controls?: boolean;
565
514
  forceLegacyConfig?: boolean;
@@ -661,9 +610,9 @@ declare module "@jwplayer/jwplayer-react-native" {
661
610
  changePlaylist(fileUrl: string): void;
662
611
  /**
663
612
  * Side load playlist items into an already setup player
664
- * @param playlistItems `PlaylistItem` or `JwPlaylistItem`
613
+ * @param playlistItems `PlaylistItem` or `JWPlaylistItem`
665
614
  */
666
- loadPlaylist(playlistItems: PlaylistItem[] | JwPlaylistItem[]): void;
615
+ loadPlaylist(playlistItems: PlaylistItem[] | JWPlaylistItem[]): void;
667
616
  /**
668
617
  * Side load playlist via URL into an already setup player
669
618
  * @param playlistUrl URL for playlist to load (format for response: json)
@@ -685,10 +634,70 @@ declare module "@jwplayer/jwplayer-react-native" {
685
634
  setCurrentCaptions(index: number): void;
686
635
  getCurrentCaptions(): Promise<number | null>;
687
636
  setVisibility(visibility: boolean, controls: JWControlType[]): void;
637
+ /**
638
+ * Reconfigures or recreates the player with a new configuration.
639
+ *
640
+ * IMPORTANT: This method should only be called after the player has been properly
641
+ * initialized and is ready (i.e., after onPlayerReady has fired). Calling this
642
+ * method before the player is ready may lead to undefined behavior.
643
+ *
644
+ * **Platform Behavior:**
645
+ *
646
+ * - **iOS**: Always performs a complete player recreation by:
647
+ * 1. Safely handling PiP state if active (waits for PiP to close)
648
+ * 2. Performing complete cleanup of the current player instance
649
+ * 3. Creating a new player instance with the provided config
650
+ *
651
+ * - **Android**: Intelligently determines whether to recreate or reconfigure:
652
+ * - ~90% of cases: Reconfigures existing player (preserves player instance, faster)
653
+ * - ~10% of cases: Recreates player (only when necessary, e.g., license changes)
654
+ * - Automatically handles state preservation (fullscreen, PiP)
655
+ *
656
+ * **Use this method when you need to:**
657
+ * - Switch to a new playlist or video programmatically
658
+ * - Update player configuration dynamically (e.g., from user settings)
659
+ * - Handle content changes during PiP mode
660
+ * - Write cross-platform code (same API works on both platforms)
661
+ *
662
+ * **Do NOT use this method:**
663
+ * - Before the player is ready (wait for onPlayerReady)
664
+ * - When the player is not properly initialized
665
+ *
666
+ * **For simple playlist updates, consider using `loadPlaylist()` instead.**
667
+ *
668
+ * @example
669
+ * ```typescript
670
+ * // Cross-platform example - works the same on both iOS and Android
671
+ * const switchVideo = () => {
672
+ * playerRef.current?.recreatePlayerWithConfig({
673
+ * license: 'YOUR_LICENSE_KEY',
674
+ * playlist: [{
675
+ * file: 'https://example.com/video.mp4',
676
+ * title: 'New Video'
677
+ * }],
678
+ * autostart: true
679
+ * });
680
+ * };
681
+ *
682
+ * // Use inside onPlayerReady
683
+ * <JWPlayer
684
+ * ref={playerRef}
685
+ * config={initialConfig}
686
+ * onPlayerReady={() => {
687
+ * // Now safe to use recreatePlayerWithConfig
688
+ * console.log('Player ready, can switch content if needed');
689
+ * }}
690
+ * />
691
+ * ```
692
+ *
693
+ * @param config The new configuration to apply
694
+ * @throws May throw if called before player is ready or with invalid config
695
+ */
696
+ recreatePlayerWithConfig(config: JWPlayerConfig | JwConfig | LegacyIOSConfig): void;
688
697
  /**
689
698
  * Only called inside `onBeforeNextPlaylistItem` callback, and once per callback
690
- * @param playlistItem `PlaylistItem` or `JwPlaylistItem`
699
+ * @param playlistItem `PlaylistItem` or `JWPlaylistItem`
691
700
  */
692
- resolveNextPlaylistItem(playlistItem: PlaylistItem | JwPlaylistItem): void;
701
+ resolveNextPlaylistItem(playlistItem: PlaylistItem | JWPlaylistItem): void;
693
702
  }
694
703
  }
package/index.js CHANGED
@@ -744,6 +744,40 @@ export default class JWPlayer extends Component {
744
744
  }
745
745
  }
746
746
 
747
+ /**
748
+ * Recreates the player with a new configuration, handling cleanup and PiP state.
749
+ *
750
+ * IMPORTANT: This method should only be called after the player has been properly
751
+ * initialized and is ready (i.e., after onPlayerReady has fired). Calling this
752
+ * method before the player is ready may lead to undefined behavior.
753
+ *
754
+ * This method:
755
+ * 1. Safely handles PiP state if active
756
+ * 2. Performs complete cleanup of the current player instance
757
+ * 3. Creates a new player instance with the provided config
758
+ *
759
+ * Use this method when you need to:
760
+ * - Switch between different DRM configurations
761
+ * - Handle content changes during PiP mode
762
+ * - Force a complete player recreation
763
+ *
764
+ * @param {Config | JwConfig} config The new configuration to apply to the recreated player
765
+ * @throws {Error} May throw if called before player is ready or with invalid config
766
+ */
767
+ recreatePlayerWithConfig(config) {
768
+ if (!config) {
769
+ console.warn('JWPlayer: Attempting to recreate player with null/undefined config');
770
+ return;
771
+ }
772
+
773
+ if (RNJWPlayerManager) {
774
+ RNJWPlayerManager.recreatePlayerWithConfig(
775
+ this.getRNJWPlayerBridgeHandle(),
776
+ config
777
+ );
778
+ }
779
+ }
780
+
747
781
  getRNJWPlayerBridgeHandle() {
748
782
  return findNodeHandle(this[this.ref_key]);
749
783
  }