@applicaster/quick-brick-player 12.0.0-alpha.4201443043 → 12.0.0-alpha.4220424021
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/package.json +9 -10
- package/src/CellPlayer/Assets/AssetsComponent.tsx +59 -49
- package/src/CellPlayer/ContextProvider.tsx +50 -1
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +1 -0
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +1 -0
- package/src/Player/AudioLayer/Layout/DockedLayout.tsx +13 -12
- package/src/Player/Utils/Utils.ts +6 -2
- package/src/Player/index.tsx +144 -1158
package/src/Player/index.tsx
CHANGED
|
@@ -1,173 +1,181 @@
|
|
|
1
|
+
/* eslint-disable react-native/no-inline-styles */
|
|
1
2
|
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
2
3
|
/* eslint-disable react-native/no-color-literals */
|
|
3
4
|
|
|
4
5
|
import React from "react";
|
|
5
|
-
import
|
|
6
|
+
import HLS from "hls.js";
|
|
7
|
+
import DASH from "dashjs";
|
|
8
|
+
|
|
9
|
+
import { View } from "react-native";
|
|
10
|
+
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
11
|
+
|
|
12
|
+
const EVENTS = [
|
|
13
|
+
"abort",
|
|
14
|
+
"error",
|
|
15
|
+
"loadeddata",
|
|
16
|
+
"loadedmetadata",
|
|
17
|
+
"playing",
|
|
18
|
+
"stalled",
|
|
19
|
+
"suspend",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const isHLS = (url) => {
|
|
23
|
+
return url.includes(".m3u8");
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const isDASH = (url) => {
|
|
27
|
+
return url.includes(".mpd");
|
|
28
|
+
};
|
|
6
29
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import TransportControlsTV from "@applicaster/quick-brick-tv-transport-controls";
|
|
30
|
+
const setupEventLogging = (player, events, onEvent) => {
|
|
31
|
+
let previous_event_time;
|
|
10
32
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
isApplePlatform,
|
|
15
|
-
isTV,
|
|
16
|
-
isTvOSPlatform,
|
|
17
|
-
isWeb,
|
|
18
|
-
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
33
|
+
events.map((event) =>
|
|
34
|
+
player.addEventListener(event, () => {
|
|
35
|
+
const event_time = Date.now();
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
const log = `<${event}>: ${event_time - window.start_time} ms${
|
|
38
|
+
previous_event_time ? ` (+${event_time - previous_event_time})` : ""
|
|
39
|
+
}`;
|
|
21
40
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
onEvent && onEvent(log);
|
|
42
|
+
previous_event_time = event_time;
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const DebugOverlay = ({ logs }) => (
|
|
48
|
+
<div
|
|
49
|
+
style={{
|
|
50
|
+
position: "absolute",
|
|
51
|
+
top: "10px",
|
|
52
|
+
left: "10px",
|
|
53
|
+
borderWidth: 1,
|
|
54
|
+
borderColor: "white",
|
|
55
|
+
padding: "10px",
|
|
56
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
57
|
+
color: "white",
|
|
58
|
+
fontSize: 20,
|
|
59
|
+
zIndex: 9990,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
{logs.map((log, index) => (
|
|
63
|
+
<p key={`log_${index}`}>{log}</p>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const setupHLSPlayer = (stream, playerRef) => {
|
|
69
|
+
if (!HLS.isSupported()) {
|
|
70
|
+
console.error("HLS is not supported in this browser.");
|
|
71
|
+
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
25
74
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
try {
|
|
76
|
+
const config = stream.license_url
|
|
77
|
+
? {
|
|
78
|
+
emeEnabled: true,
|
|
79
|
+
drmSystems: {
|
|
80
|
+
"com.widevine.alpha": {
|
|
81
|
+
licenseUrl: stream.license_url,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
drmSystemOptions: {
|
|
85
|
+
audioRobustness: "SW_SECURE_CRYPTO",
|
|
86
|
+
videoRobustness: "SW_SECURE_CRYPTO",
|
|
87
|
+
},
|
|
88
|
+
backBufferLength: 30, // https://github.com/video-dev/hls.js/blob/master/docs/API.md#backbufferlength
|
|
89
|
+
}
|
|
90
|
+
: undefined;
|
|
91
|
+
|
|
92
|
+
const hls = new HLS(config);
|
|
93
|
+
hls.loadSource(stream.src);
|
|
94
|
+
hls.attachMedia(playerRef);
|
|
95
|
+
|
|
96
|
+
return true;
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error(err);
|
|
99
|
+
}
|
|
45
100
|
|
|
46
|
-
|
|
47
|
-
QuickBrickPlayerPlugin: isApplePlatform()
|
|
48
|
-
? NativeModules.PlayerModule
|
|
49
|
-
: NativeModules.QuickBrickDefaultPlayerModule,
|
|
50
|
-
KalturaPlayerPlugin: isApplePlatform()
|
|
51
|
-
? NativeModules.KalturaPlayerModule
|
|
52
|
-
: NativeModules.QBKalturaPlayerModule,
|
|
101
|
+
return false;
|
|
53
102
|
};
|
|
54
103
|
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
audio: {
|
|
60
|
-
overflow: "hidden",
|
|
61
|
-
position: "absolute",
|
|
62
|
-
zIndex: 1,
|
|
63
|
-
},
|
|
64
|
-
playerSize: { width: "100%", height: "100%" },
|
|
65
|
-
});
|
|
104
|
+
const setupDASHPlayer = (stream, playerRef) => {
|
|
105
|
+
try {
|
|
106
|
+
const player = DASH.MediaPlayer().create();
|
|
107
|
+
player.initialize(playerRef, stream.src, true);
|
|
66
108
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
109
|
+
player.updateSettings({
|
|
110
|
+
streaming: {
|
|
111
|
+
buffer: {
|
|
112
|
+
bufferToKeep: 30, // https://cdn.dashjs.org/latest/jsdoc/module-Settings.html#~Buffer:~:text=buffer%20in%20seconds.-,bufferToKeep,-number
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (stream.license_url) {
|
|
118
|
+
player.setProtectionData({
|
|
119
|
+
"com.widevine.alpha": {
|
|
120
|
+
serverURL: stream.license_url,
|
|
121
|
+
priority: 0,
|
|
122
|
+
audioRobustness: "SW_SECURE_CRYPTO",
|
|
123
|
+
videoRobustness: "SW_SECURE_CRYPTO",
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return true;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(error);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return false;
|
|
134
|
+
};
|
|
71
135
|
|
|
72
136
|
export default class VideoPlayer extends React.Component<
|
|
73
137
|
QuickBrickPlayer.PlayerProps,
|
|
74
138
|
QuickBrickPlayer.NativePlayerState
|
|
75
139
|
> {
|
|
76
|
-
public static propTypes = {};
|
|
77
140
|
_root: any;
|
|
78
|
-
analyticsTracker: AnalyticsTracker;
|
|
79
141
|
playerPluginId: string;
|
|
80
142
|
takeParentStyle: Record<any, any>;
|
|
81
|
-
playbackStarted = false;
|
|
82
|
-
needToResumeOnForeground: boolean = null;
|
|
83
|
-
private onVideoLoadCalled: boolean;
|
|
84
|
-
playNextData: PlayNextData;
|
|
85
|
-
|
|
86
|
-
state: QuickBrickPlayer.NativePlayerState = {
|
|
87
|
-
audioTrackId: null,
|
|
88
|
-
textTrackId: null,
|
|
89
|
-
|
|
90
|
-
// TODO: Can be deleted, on controller logic
|
|
91
|
-
isAd: false,
|
|
92
|
-
|
|
93
|
-
// TODO: Remove paused
|
|
94
|
-
// TODO: Can be deleted, on controller logic
|
|
95
|
-
paused: false,
|
|
96
|
-
piped: false,
|
|
97
|
-
rate: 1.0,
|
|
98
|
-
// TODO: Hack for analytics, must br removed
|
|
99
|
-
resume: null,
|
|
100
|
-
|
|
101
|
-
// TODO: Replace with react method
|
|
102
|
-
seek: {},
|
|
103
|
-
|
|
104
|
-
// Player state
|
|
105
|
-
currentTime: 0,
|
|
106
|
-
duration: 0,
|
|
107
|
-
};
|
|
108
143
|
|
|
109
144
|
constructor(props) {
|
|
110
145
|
super(props);
|
|
111
|
-
|
|
146
|
+
|
|
147
|
+
this.state = {
|
|
148
|
+
logs: [],
|
|
149
|
+
};
|
|
112
150
|
|
|
113
151
|
this.takeParentStyle = isWeb()
|
|
114
152
|
? { width: "100%", flex: "1 1 auto", objectFit: "contain" }
|
|
115
153
|
: { flexBasis: "auto", flexGrow: 1, flexShrink: 1 };
|
|
116
154
|
|
|
117
|
-
const entry = this.props?.entry;
|
|
118
|
-
this.analyticsTracker = new AnalyticsTracker(entry);
|
|
119
155
|
this.playerPluginId = this.props?.playerPluginIdentifier;
|
|
120
156
|
}
|
|
121
157
|
|
|
122
|
-
componentWillUnmount() {
|
|
123
|
-
this.stopBackgroundPlayback();
|
|
124
|
-
|
|
125
|
-
this.playbackStarted = false;
|
|
126
|
-
this.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
127
|
-
this.onPlayerClosed();
|
|
128
|
-
|
|
129
|
-
if (isAndroidPlatform()) {
|
|
130
|
-
this.disablePiP();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
158
|
componentDidMount() {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
shouldComponentUpdate(nextProps) {
|
|
140
|
-
const srcPath = ["entry", "content", "src"];
|
|
159
|
+
const {
|
|
160
|
+
content: { src },
|
|
161
|
+
extensions: { drm },
|
|
162
|
+
} = this.props.entry;
|
|
141
163
|
|
|
142
|
-
|
|
143
|
-
// null when trying to exit the player. This prevents a crash in that scenario
|
|
144
|
-
return !(R.path(srcPath, this.props) && !R.path(srcPath, nextProps));
|
|
145
|
-
}
|
|
164
|
+
const stream = { src, license_url: drm?.widevine?.license_url };
|
|
146
165
|
|
|
147
|
-
|
|
148
|
-
if (
|
|
149
|
-
this.props.castAction?.state?.connected &&
|
|
150
|
-
!this.state.isCastConnected
|
|
151
|
-
) {
|
|
152
|
-
this.stopBackgroundPlayback();
|
|
153
|
-
this.setState({ isCastConnected: true });
|
|
154
|
-
}
|
|
166
|
+
window.start_time = Date.now();
|
|
155
167
|
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.setState({ isCastConnected: false });
|
|
168
|
+
if (isDASH(src)) {
|
|
169
|
+
setupDASHPlayer(stream, this._root);
|
|
170
|
+
} else if (isHLS(src)) {
|
|
171
|
+
setupHLSPlayer(stream, this._root);
|
|
161
172
|
}
|
|
162
173
|
|
|
163
|
-
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
}
|
|
174
|
+
setupEventLogging(this._root, EVENTS, (log) => {
|
|
175
|
+
this.setState(({ logs }) => ({ logs: [...logs, log] }));
|
|
176
|
+
});
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
getPlayNextData = () => this.props.playNextData as PlayNextData;
|
|
170
|
-
|
|
171
179
|
/**
|
|
172
180
|
* Assigns root / ref of the player.
|
|
173
181
|
*/
|
|
@@ -175,1034 +183,12 @@ export default class VideoPlayer extends React.Component<
|
|
|
175
183
|
this._root = component;
|
|
176
184
|
};
|
|
177
185
|
|
|
178
|
-
getPluginConfiguration = () => {
|
|
179
|
-
return this.props.pluginConfiguration;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Handles analytic events, generates a new analytics tracker and transmits events
|
|
184
|
-
* Passes entry data, and playerState to the tracker for additional context
|
|
185
|
-
* The end result is an event formatted and passed to AnalyticsBridge.postEvent
|
|
186
|
-
* When we get the synthetic event we need to pull the data in the nativeEvent object
|
|
187
|
-
* nativeEvent contains the return value from native
|
|
188
|
-
*/
|
|
189
|
-
handleAnalyticEvent = (event, data = {}) => {
|
|
190
|
-
const entry = this.props?.entry;
|
|
191
|
-
const playerState = this.getState();
|
|
192
|
-
|
|
193
|
-
if (this.analyticsTracker?.handleAnalyticEvent) {
|
|
194
|
-
this.analyticsTracker?.handleAnalyticEvent?.(
|
|
195
|
-
event,
|
|
196
|
-
{ ...data, player_type: this.props.playerPluginIdentifier },
|
|
197
|
-
entry,
|
|
198
|
-
playerState
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* This method is for the events that have no implementation in react but are being invoked by native
|
|
205
|
-
* When we get the synthetic event we need to pull the data in the nativeEvent object
|
|
206
|
-
* nativeEvent contains the return value from native
|
|
207
|
-
* Value could be null if nothing return
|
|
208
|
-
*/
|
|
209
|
-
unHandledEvent = (eventName) => {
|
|
210
|
-
return function dispatchUnhandledEvent({ nativeEvent = null }) {
|
|
211
|
-
logger.warning({
|
|
212
|
-
message:
|
|
213
|
-
"You have invoked an event that is being deprecated or unhandled",
|
|
214
|
-
data: { eventName, nativeEvent },
|
|
215
|
-
});
|
|
216
|
-
};
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
isReactNativeMethodsSupported = () =>
|
|
220
|
-
this.props.PlayerComponent === "QuickBrickDefaultPlayerView" ||
|
|
221
|
-
this.props.PlayerComponent === "PlayerModule";
|
|
222
|
-
|
|
223
|
-
pause = () => {
|
|
224
|
-
// TODO: Call player instance controller methods directly
|
|
225
|
-
// this._root was added here because chromecast,
|
|
226
|
-
// is checking player state for the pause
|
|
227
|
-
if (
|
|
228
|
-
this.isReactNativeMethodsSupported() &&
|
|
229
|
-
RNPlayerManager?.[this.playerPluginId]?.pause &&
|
|
230
|
-
this._root
|
|
231
|
-
) {
|
|
232
|
-
if (!this.state.paused) {
|
|
233
|
-
const node = findNodeHandle(this._root);
|
|
234
|
-
|
|
235
|
-
if (!node) {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
RNPlayerManager?.[this.playerPluginId]?.pause(node);
|
|
240
|
-
}
|
|
241
|
-
} else {
|
|
242
|
-
this.setState({ paused: true });
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// User invoked player pause action
|
|
246
|
-
this.needToResumeOnForeground = null;
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
play = () => {
|
|
250
|
-
// TODO: Call player instance controller methods directly
|
|
251
|
-
// this._root was added here because chromecast,
|
|
252
|
-
// is checking player state for the pause
|
|
253
|
-
if (
|
|
254
|
-
this.isReactNativeMethodsSupported() &&
|
|
255
|
-
RNPlayerManager?.[this.playerPluginId]?.play &&
|
|
256
|
-
this._root
|
|
257
|
-
) {
|
|
258
|
-
if (this.state.paused) {
|
|
259
|
-
const node = findNodeHandle(this._root);
|
|
260
|
-
|
|
261
|
-
if (!node) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
RNPlayerManager?.[this.playerPluginId]?.play(node);
|
|
266
|
-
}
|
|
267
|
-
} else {
|
|
268
|
-
this.setState({ paused: false });
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// User invoked player pause action
|
|
272
|
-
this.needToResumeOnForeground = null;
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
stopBackgroundPlayback = () => {
|
|
276
|
-
if (
|
|
277
|
-
this.isReactNativeMethodsSupported() &&
|
|
278
|
-
RNPlayerManager?.[this.playerPluginId]?.stopBackgroundPlayback
|
|
279
|
-
) {
|
|
280
|
-
RNPlayerManager?.[this.playerPluginId]?.stopBackgroundPlayback();
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
forward = (seekTime = SEEK_TIME) => {
|
|
285
|
-
const currentTime = this.state.currentTime;
|
|
286
|
-
|
|
287
|
-
const _duration = this.state.isLive
|
|
288
|
-
? this.state?.seekableDuration
|
|
289
|
-
: this.getDuration();
|
|
290
|
-
|
|
291
|
-
const _time = this.state.isLive
|
|
292
|
-
? R.clamp(0, _duration, currentTime - seekTime)
|
|
293
|
-
: R.clamp(0, _duration, currentTime + seekTime);
|
|
294
|
-
|
|
295
|
-
const seekTo = {
|
|
296
|
-
time: _time,
|
|
297
|
-
tolerance: 1,
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
this.setState({ seek: seekTo });
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
rewind = (seekTime = SEEK_TIME) => {
|
|
304
|
-
const currentTime = this.state.currentTime;
|
|
305
|
-
|
|
306
|
-
const _duration = this.state.isLive
|
|
307
|
-
? this.state?.seekableDuration
|
|
308
|
-
: this.getDuration();
|
|
309
|
-
|
|
310
|
-
const _time = this.state.isLive
|
|
311
|
-
? R.clamp(0, _duration, currentTime + seekTime)
|
|
312
|
-
: R.clamp(0, _duration, currentTime - seekTime);
|
|
313
|
-
|
|
314
|
-
const seekTo = {
|
|
315
|
-
time: _time,
|
|
316
|
-
tolerance: 1,
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
this.setState({ seek: seekTo });
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
seeking = (time, tolerance = 1) => {
|
|
323
|
-
if (isNaN(time)) {
|
|
324
|
-
logger.error({
|
|
325
|
-
message: `Specified time is not a number time: ${time}`,
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// This hack was added, if send same value to seek two times like 0,
|
|
332
|
-
// It will not property change callback to native.
|
|
333
|
-
// TODO: This func must be refactored to View React method.
|
|
334
|
-
if (time === this.state.seek.time) {
|
|
335
|
-
this.setState({ seek: {} }, () => {
|
|
336
|
-
this.setState({ seek: { time, tolerance } });
|
|
337
|
-
});
|
|
338
|
-
} else {
|
|
339
|
-
this.setState({ seek: { time, tolerance } });
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Sets either subtitle language, or audio language if we have a selected a new locale.
|
|
345
|
-
* If no locale is set it defaults to system language for audio, and leaves subtitles unaltered.
|
|
346
|
-
*/
|
|
347
|
-
setLanguage = (selected = null) => {
|
|
348
|
-
const { trackType, id, language } = selected || {};
|
|
349
|
-
|
|
350
|
-
const isOff = language === "Off";
|
|
351
|
-
|
|
352
|
-
if (!id) {
|
|
353
|
-
logger.error({
|
|
354
|
-
message:
|
|
355
|
-
"Failed to set Audio or Text track. Language track is missing required parameter ID",
|
|
356
|
-
data: { selected },
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
switch (trackType) {
|
|
363
|
-
case "text":
|
|
364
|
-
this.setState({
|
|
365
|
-
textTrackId: isOff ? null : id,
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
break;
|
|
369
|
-
case "audio":
|
|
370
|
-
this.setState({
|
|
371
|
-
audioTrackId: id,
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
break;
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
setPlaybackRate = (rate = 1.0) => {
|
|
379
|
-
this.setState({ rate });
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
getCurrentTime = () => {
|
|
383
|
-
return this.state.currentTime;
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
getDuration = () => {
|
|
387
|
-
return this.state.duration;
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
getState = () => {
|
|
391
|
-
return { ...this.state, fullscreen: Boolean(this.props?.fullscreen) };
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Handles key events transmitted by the native
|
|
396
|
-
* Adds the key event to the playerState so that it can be consumed by interaction manager
|
|
397
|
-
* Needs for Android
|
|
398
|
-
*/
|
|
399
|
-
onKeyDown = (event) => {
|
|
400
|
-
this.setState({
|
|
401
|
-
keyEvent: event,
|
|
402
|
-
});
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
onPlayerPresented = () => {
|
|
406
|
-
this.handleAnalyticEvent(GENERAL_EVENT.player_presented);
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
onPlayerClosed = () => {
|
|
410
|
-
this.handleAnalyticEvent(GENERAL_EVENT.player_closed);
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
onPlayerSessionEnd = () => {
|
|
414
|
-
// TODO: Implement method on native android, ios exists
|
|
415
|
-
this.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
onPlayerSessionStart = () => {
|
|
419
|
-
// TODO: Implement method on native android, ios exists
|
|
420
|
-
|
|
421
|
-
this.handleAnalyticEvent(GENERAL_EVENT.session_start);
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
onVideoEnd = () => {
|
|
425
|
-
this.setState({ displayOverlay: !this.state.displayOverlay }, () => {
|
|
426
|
-
if (this.state?.displayOverlay) {
|
|
427
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
|
|
428
|
-
this.props?.listener?.onVideoEnd({});
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
// TODO: Should be unified on both platform
|
|
434
|
-
onVideoError = ({ nativeEvent }) => {
|
|
435
|
-
const { message } = nativeEvent;
|
|
436
|
-
|
|
437
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.error, nativeEvent);
|
|
438
|
-
|
|
439
|
-
return this.props?.listener?.onError(new Error(message));
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
onPlayerPause = ({ nativeEvent = null }) => {
|
|
443
|
-
this.setState({ paused: true });
|
|
444
|
-
this.props?.listener?.onPlayerPause(nativeEvent);
|
|
445
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.pause, nativeEvent);
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
onPlayerResume = ({ nativeEvent = null }) => {
|
|
449
|
-
const { isAd, resume } = this.state;
|
|
450
|
-
const isNotAnAd = !isAd;
|
|
451
|
-
this.setState({ paused: false });
|
|
452
|
-
this.props?.listener?.onPlayerResume?.({ nativeEvent });
|
|
453
|
-
|
|
454
|
-
if (isNotAnAd && !resume) {
|
|
455
|
-
this.setState({ resume: true });
|
|
456
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.play, nativeEvent);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
onPlayerSeekComplete = ({ nativeEvent }) => {
|
|
461
|
-
this.props?.listener?.onPlayerSeekComplete(nativeEvent);
|
|
462
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, nativeEvent);
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
onPlayerSeekStart = ({ nativeEvent }) => {
|
|
466
|
-
this.props?.listener?.onPlayerSeekStart(nativeEvent);
|
|
467
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, nativeEvent);
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
onBufferStart = ({ nativeEvent }) => {
|
|
471
|
-
this.props?.listener?.onBufferStart(nativeEvent);
|
|
472
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, nativeEvent);
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
onBufferComplete = ({ nativeEvent }) => {
|
|
476
|
-
this.props?.listener?.onBufferComplete(nativeEvent);
|
|
477
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, nativeEvent);
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
onPlayerNext = ({ nativeEvent }) => {
|
|
481
|
-
// TODO: Implement method on native
|
|
482
|
-
|
|
483
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
onPlayerPrevious = ({ nativeEvent }) => {
|
|
487
|
-
// TODO: Implement method on native
|
|
488
|
-
|
|
489
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
onPlayerStop = ({ nativeEvent }) => {
|
|
493
|
-
// TODO: Implement method on native
|
|
494
|
-
|
|
495
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.stop, nativeEvent);
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
onVideoBandwidthUpdate = ({ nativeEvent }) => {
|
|
499
|
-
// TODO: Implement method on native
|
|
500
|
-
|
|
501
|
-
this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, nativeEvent);
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
onAdBegin = ({ nativeEvent }) => {
|
|
505
|
-
this.setState({ isAd: true });
|
|
506
|
-
this.handleAnalyticEvent(AD_EVENT.ad_start, nativeEvent);
|
|
507
|
-
this.props.listener?.onAdBegin?.(nativeEvent);
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
onAdBreakBegin = ({ nativeEvent }) => {
|
|
511
|
-
this.setState({ isAd: true });
|
|
512
|
-
this.handleAnalyticEvent(AD_EVENT.ad_break_start, nativeEvent);
|
|
513
|
-
this.props.listener?.onAdBreakBegin?.(nativeEvent);
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
onAdBreakEnd = ({ nativeEvent }) => {
|
|
517
|
-
this.setState({ isAd: false });
|
|
518
|
-
this.handleAnalyticEvent(AD_EVENT.ad_break_completed, nativeEvent);
|
|
519
|
-
this.props.listener?.onAdBreakEnd?.(nativeEvent);
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
onAdEnd = ({ nativeEvent }) => {
|
|
523
|
-
this.setState({ isAd: false });
|
|
524
|
-
this.handleAnalyticEvent(AD_EVENT.ad_complete, nativeEvent);
|
|
525
|
-
this.props.listener?.onAdEnd?.(nativeEvent);
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
onAdError = ({ nativeEvent }) => {
|
|
529
|
-
this.setState({ isAd: false });
|
|
530
|
-
this.handleAnalyticEvent(AD_EVENT.ad_error, nativeEvent);
|
|
531
|
-
this.props.listener?.onAdError?.(nativeEvent);
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
onAdRequest = ({ nativeEvent }) => {
|
|
535
|
-
this.handleAnalyticEvent(AD_EVENT.ad_request, nativeEvent);
|
|
536
|
-
this.props.listener?.onAdRequest?.(nativeEvent);
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
onAdClicked = ({ nativeEvent }) => {
|
|
540
|
-
this.handleAnalyticEvent(AD_EVENT.ad_clicked, nativeEvent);
|
|
541
|
-
// Broadcast click event
|
|
542
|
-
this.props.playerEvent("ad-clicked", nativeEvent);
|
|
543
|
-
this.props.listener?.onAdClicked?.(nativeEvent);
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
onAdTapped = ({ nativeEvent }) => {
|
|
547
|
-
this.handleAnalyticEvent(AD_EVENT.ad_tapped, nativeEvent);
|
|
548
|
-
// Broadcast click event
|
|
549
|
-
this.props.playerEvent("ad-tapped", nativeEvent);
|
|
550
|
-
this.props.listener?.onAdTapped?.(nativeEvent);
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
onPlaybackStalled = () => {
|
|
554
|
-
return this.props?.updatePlayerState?.("stalled");
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
onPlayerAttached = ({ nativeEvent }) => {
|
|
558
|
-
this.props.listener?.onPlayerAttached?.(nativeEvent);
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
onPlayerDetached = ({ nativeEvent }) => {
|
|
562
|
-
this.props.listener?.onPlayerDetached?.(nativeEvent);
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
onVideoFullscreenPlayerDidDismiss = () => {
|
|
566
|
-
this.props.listener?.onVideoFullscreenPlayerDidDismiss?.();
|
|
567
|
-
};
|
|
568
|
-
|
|
569
|
-
onVideoFullscreenPlayerDidPresent = () => {
|
|
570
|
-
this.props.listener?.onVideoFullscreenPlayerDidPresent?.();
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
onVideoFullscreenPlayerWillDismiss = () => {
|
|
574
|
-
this.props.listener?.onVideoFullscreenPlayerWillDismiss?.();
|
|
575
|
-
};
|
|
576
|
-
|
|
577
|
-
onVideoFullscreenPlayerWillPresent = () => {
|
|
578
|
-
this.props.listener?.onVideoFullscreenPlayerWillPresent?.();
|
|
579
|
-
};
|
|
580
|
-
|
|
581
|
-
onPlayerClose = () => {
|
|
582
|
-
this.props.listener?.onPlayerClose?.();
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
onPiPEnter = () => {
|
|
586
|
-
this.setState({ piped: true });
|
|
587
|
-
this.props.openPiP();
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
onPiPExit = () => {
|
|
591
|
-
this.setState({ piped: false });
|
|
592
|
-
this.props.closePiP();
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
onPiPCloseByUser = () => {
|
|
596
|
-
if (this.state.piped) {
|
|
597
|
-
const node = findNodeHandle(this._root);
|
|
598
|
-
|
|
599
|
-
if (!node) {
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
playerManager.close();
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
disablePiP = () => {
|
|
608
|
-
const node = findNodeHandle(this._root);
|
|
609
|
-
|
|
610
|
-
if (!node) {
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
RNPlayerManager?.[this.playerPluginId]?.disablePiP?.(node);
|
|
615
|
-
// TODO: Remove after implementing play next for PiP
|
|
616
|
-
RNPlayerManager?.[this.playerPluginId]?.closePiP?.(node);
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
onVideoBuffer = ({ nativeEvent }) => {
|
|
620
|
-
// video buffer event is invoked from native side when asking the player to pause
|
|
621
|
-
// this causes the buffer animation to trigger and incorrectly request to update state
|
|
622
|
-
if (this.state.paused) {
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// TODO: This events must be called from native directly
|
|
627
|
-
if (nativeEvent?.isBuffering) {
|
|
628
|
-
this.onBufferStart({ nativeEvent });
|
|
629
|
-
} else {
|
|
630
|
-
this.onBufferComplete({ nativeEvent });
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
return this.props?.updatePlayerState?.("loadstart");
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
onVideoLoad = ({ nativeEvent }) => {
|
|
637
|
-
const duration = nativeEvent.duration;
|
|
638
|
-
const currentTime = nativeEvent.currentTime;
|
|
639
|
-
|
|
640
|
-
// check if content is loaded
|
|
641
|
-
if (duration < 0) {
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
this.setState(
|
|
646
|
-
{
|
|
647
|
-
currentTime,
|
|
648
|
-
duration,
|
|
649
|
-
},
|
|
650
|
-
() => {
|
|
651
|
-
this.props?.listener?.onVideoLoad(nativeEvent);
|
|
652
|
-
|
|
653
|
-
if (!this.playbackStarted) {
|
|
654
|
-
this.playbackStarted = true;
|
|
655
|
-
// TODO: Remove not needed anymore, implemented on player.tsx
|
|
656
|
-
this.props?.listener?.onLoad(nativeEvent);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
if (!this.state.isAd && !this.onVideoLoadCalled) {
|
|
660
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
|
|
661
|
-
player_type: this.props.playerPluginIdentifier,
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
// TODO: Hack, needs to remove
|
|
665
|
-
this.onVideoLoadCalled = true;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
);
|
|
669
|
-
};
|
|
670
|
-
|
|
671
|
-
onTracksChanged = ({ nativeEvent }) => {
|
|
672
|
-
const { textTracks, audioTracks, audioTrackId, textTrackId } =
|
|
673
|
-
parseLanguageTracks(nativeEvent);
|
|
674
|
-
|
|
675
|
-
this.setState(
|
|
676
|
-
{
|
|
677
|
-
audioTrackId,
|
|
678
|
-
textTrackId,
|
|
679
|
-
},
|
|
680
|
-
() => {
|
|
681
|
-
this.props?.listener?.onTracksChanged?.({
|
|
682
|
-
textTracks,
|
|
683
|
-
audioTracks,
|
|
684
|
-
audioTrackId,
|
|
685
|
-
textTrackId,
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
);
|
|
689
|
-
};
|
|
690
|
-
|
|
691
|
-
onVideoProgress = ({ nativeEvent }) => {
|
|
692
|
-
const duration = nativeEvent.duration;
|
|
693
|
-
const currentTime = nativeEvent.currentTime;
|
|
694
|
-
const seekableDuration = nativeEvent.seekableDuration;
|
|
695
|
-
const isLive = nativeEvent.isLive;
|
|
696
|
-
|
|
697
|
-
// TODO: Delete states on view
|
|
698
|
-
this.setState({
|
|
699
|
-
currentTime,
|
|
700
|
-
duration,
|
|
701
|
-
resume: false,
|
|
702
|
-
seekableDuration,
|
|
703
|
-
isLive,
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
this.props?.listener?.onVideoProgress({
|
|
707
|
-
currentTime,
|
|
708
|
-
duration,
|
|
709
|
-
item: this.props?.entry,
|
|
710
|
-
seekableDuration,
|
|
711
|
-
isLive,
|
|
712
|
-
});
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
onPlaybackRateChange = ({ nativeEvent }) => {
|
|
716
|
-
if (nativeEvent.playbackRate !== this.state.rate) {
|
|
717
|
-
this.setState({ rate: nativeEvent.playbackRate });
|
|
718
|
-
|
|
719
|
-
if (this.props?.listener?.onPlaybackRateChange) {
|
|
720
|
-
this.props?.listener?.onPlaybackRateChange(nativeEvent);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (nativeEvent?.playbackRate > 0) {
|
|
724
|
-
if (this.props?.listener?.onPlayerResume) {
|
|
725
|
-
this.props?.listener?.onPlayerResume(nativeEvent);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
|
|
731
|
-
appStateChange = (appState, previousAppState) => {
|
|
732
|
-
const { pluginConfiguration } = this.props;
|
|
733
|
-
const shouldPauseOnExit = pluginConfiguration?.nowPlayingEnabled !== true;
|
|
734
|
-
|
|
735
|
-
// TODO: This const must be imported from upstate change of TS
|
|
736
|
-
const ACTIVE = "active";
|
|
737
|
-
const BACKGROUND = "background";
|
|
738
|
-
const INACTIVE = "inactive";
|
|
739
|
-
|
|
740
|
-
if (
|
|
741
|
-
previousAppState === ACTIVE &&
|
|
742
|
-
appState &&
|
|
743
|
-
(appState === BACKGROUND || appState === INACTIVE)
|
|
744
|
-
) {
|
|
745
|
-
if ((isTV() || shouldPauseOnExit) && this.props.mode !== "PIP") {
|
|
746
|
-
const wasPlaying = !this.state.paused;
|
|
747
|
-
this.pause();
|
|
748
|
-
this.needToResumeOnForeground = wasPlaying;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
if (appState && appState === ACTIVE && previousAppState !== ACTIVE) {
|
|
753
|
-
if (this.needToResumeOnForeground) {
|
|
754
|
-
this.play();
|
|
755
|
-
this.needToResumeOnForeground = null;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
};
|
|
759
|
-
|
|
760
|
-
childrenWithPlayNext = () => {
|
|
761
|
-
if (!isTvOSPlatform() || !this.getPlayNextData()) {
|
|
762
|
-
return this.props.children;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// tvos only
|
|
766
|
-
const { docked, entry } = this.props;
|
|
767
|
-
|
|
768
|
-
const children = R.pathOr([], "children", this.props);
|
|
769
|
-
|
|
770
|
-
return [
|
|
771
|
-
...children,
|
|
772
|
-
<PlayNextOverlay
|
|
773
|
-
key="play-next-overlay"
|
|
774
|
-
item={entry}
|
|
775
|
-
playerIsDocked={docked}
|
|
776
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
777
|
-
playerId={this.props.playerId}
|
|
778
|
-
playNextData={this.getPlayNextData()}
|
|
779
|
-
setNextVideoPreloadThresholdPercentage={
|
|
780
|
-
this.props.setNextVideoPreloadThresholdPercentage
|
|
781
|
-
}
|
|
782
|
-
/>,
|
|
783
|
-
];
|
|
784
|
-
};
|
|
785
|
-
|
|
786
186
|
render() {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
isModal,
|
|
794
|
-
isTabletPortrait,
|
|
795
|
-
muted,
|
|
796
|
-
style,
|
|
797
|
-
castAction,
|
|
798
|
-
controller,
|
|
799
|
-
startComponentsAnimation,
|
|
800
|
-
} = this.props;
|
|
801
|
-
|
|
802
|
-
const supportNativeCast = controller.supportNativeCast();
|
|
803
|
-
|
|
804
|
-
const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
|
|
805
|
-
this.state;
|
|
806
|
-
|
|
807
|
-
const isAudioItem =
|
|
808
|
-
entry?.content?.type?.includes?.("audio") ||
|
|
809
|
-
entry?.type?.value === "audio";
|
|
810
|
-
|
|
811
|
-
const getStyle = (dimensions) => {
|
|
812
|
-
const generalStyles = isAudioItem ? styles.audio : styles.video;
|
|
813
|
-
|
|
814
|
-
if (isAndroidPlatform() && isAudioItem) {
|
|
815
|
-
return generalStyles;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
return { ...generalStyles, ...dimensions };
|
|
819
|
-
};
|
|
820
|
-
|
|
821
|
-
const getBackgroundImageStyle = (dimensions): any =>
|
|
822
|
-
Object.assign(
|
|
823
|
-
{},
|
|
824
|
-
isAudioItem ? styles.audio : { position: "absolute" },
|
|
825
|
-
dimensions
|
|
826
|
-
);
|
|
827
|
-
|
|
828
|
-
const nativeResizeMode = "ScaleAspectFit";
|
|
829
|
-
|
|
830
|
-
const nowPlayingEnabled =
|
|
831
|
-
this.props?.pluginConfiguration?.nowPlayingEnabled;
|
|
832
|
-
|
|
833
|
-
const enableAutoAudioTrackSelection =
|
|
834
|
-
this.props.pluginConfiguration?.enable_auto_audio_track_selection;
|
|
835
|
-
|
|
836
|
-
const minimumAllowedSeekableDurationInSeconds =
|
|
837
|
-
this.props?.pluginConfiguration?.minimumAllowedSeekableDurationInSeconds;
|
|
838
|
-
// In case no seeking duration or it less than 10 min we do not want to enable live seeking feature
|
|
839
|
-
|
|
840
|
-
const liveSeekingEnabled =
|
|
841
|
-
(this.props.pluginConfiguration?.liveSeekingEnabled &&
|
|
842
|
-
this.state?.seekableDuration >
|
|
843
|
-
minimumAllowedSeekableDurationInSeconds) ||
|
|
844
|
-
false;
|
|
845
|
-
|
|
846
|
-
// keep undefined if not present, since not all native players have the property
|
|
847
|
-
const liveCatchUpEnabled =
|
|
848
|
-
this.props.pluginConfiguration?.liveCatchUpEnabled;
|
|
849
|
-
|
|
850
|
-
// TODO: Get screen data more reliable way useCurrentScreenData
|
|
851
|
-
const screenData = getTargetScreenDataFromEntry(this.props.playableItem);
|
|
852
|
-
|
|
853
|
-
const isInlineTV = screenData?.ui_components?.length > 0 && isTV();
|
|
854
|
-
|
|
855
|
-
let platformSpecificProps: any = isApplePlatform()
|
|
856
|
-
? {
|
|
857
|
-
resizeMode: nativeResizeMode,
|
|
858
|
-
}
|
|
859
|
-
: {};
|
|
860
|
-
|
|
861
|
-
const supportsNativeControls =
|
|
862
|
-
!!this.props.controller?.supportsNativeControls?.();
|
|
863
|
-
|
|
864
|
-
const controls = isAppleTV()
|
|
865
|
-
? supportsNativeControls && !isInlineTV && !this.getPlayNextData()
|
|
866
|
-
: supportsNativeControls && !this.getPlayNextData();
|
|
867
|
-
|
|
868
|
-
const shouldUseNativeControls =
|
|
869
|
-
controls &&
|
|
870
|
-
!startComponentsAnimation &&
|
|
871
|
-
!docked &&
|
|
872
|
-
!castAction?.state?.connected;
|
|
873
|
-
|
|
874
|
-
platformSpecificProps = {
|
|
875
|
-
...platformSpecificProps,
|
|
876
|
-
controls: shouldUseNativeControls,
|
|
877
|
-
};
|
|
878
|
-
|
|
879
|
-
const pictureInPictureEnabled =
|
|
880
|
-
this.props.pluginConfiguration?.pictureInPictureEnabled;
|
|
881
|
-
|
|
882
|
-
const nativeProps = {
|
|
883
|
-
entry, // Must be passed first in list
|
|
884
|
-
...platformSpecificProps,
|
|
885
|
-
audioTrackId,
|
|
886
|
-
docked,
|
|
887
|
-
enableAutoAudioTrackSelection,
|
|
888
|
-
fullscreen, // TODO: Check fullscreen params not crash
|
|
889
|
-
inline,
|
|
890
|
-
muted,
|
|
891
|
-
nowPlayingEnabled,
|
|
892
|
-
paused,
|
|
893
|
-
seek,
|
|
894
|
-
rate,
|
|
895
|
-
textTrackId,
|
|
896
|
-
liveSeekingEnabled,
|
|
897
|
-
liveCatchUpEnabled,
|
|
898
|
-
playerId: this.props.playerId,
|
|
899
|
-
pictureInPictureEnabled,
|
|
900
|
-
seekStep:
|
|
901
|
-
(this.props.pluginConfiguration?.seek_duration || SEEK_TIME) * 1000, // Only for Android
|
|
902
|
-
accessibilityProps: getAllAccessibilityProps(
|
|
903
|
-
this.getPluginConfiguration()
|
|
904
|
-
),
|
|
905
|
-
}; // rate temporary removed, because of bug with subtitles
|
|
906
|
-
|
|
907
|
-
if (
|
|
908
|
-
this.isReactNativeMethodsSupported() &&
|
|
909
|
-
RNPlayerManager?.[this.playerPluginId]?.play
|
|
910
|
-
) {
|
|
911
|
-
delete nativeProps["paused"];
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
const adEvents = {
|
|
915
|
-
onAdBegin: this.onAdBegin, // ad_start
|
|
916
|
-
onAdBreakBegin: this.onAdBreakBegin, // ad_break_start
|
|
917
|
-
onAdBreakEnd: this.onAdBreakEnd, // ad_break_completed
|
|
918
|
-
onAdEnd: this.onAdEnd, // ad_complete
|
|
919
|
-
onAdError: this.onAdError, // ad_error
|
|
920
|
-
onAdRequest: this.onAdRequest, // ad_request
|
|
921
|
-
onAdClicked: this.onAdClicked, // ad_clicked
|
|
922
|
-
onAdTapped: this.onAdTapped, // ad_tapped
|
|
923
|
-
};
|
|
924
|
-
|
|
925
|
-
const playbackEvents = {
|
|
926
|
-
onPlaybackRateChange: this.onPlaybackRateChange,
|
|
927
|
-
onPlayerPause: this.onPlayerPause, // pause
|
|
928
|
-
onPlayerResume: this.onPlayerResume, // play
|
|
929
|
-
onPlayerSeekComplete: this.onPlayerSeekComplete, // seek_complete
|
|
930
|
-
onPlayerSeekStart: this.onPlayerSeekStart, // seek_start
|
|
931
|
-
onPlayerSessionEnd: this.onPlayerSessionEnd, // session_end
|
|
932
|
-
onPlayerSessionStart: this.onPlayerSessionStart, // session_start
|
|
933
|
-
};
|
|
934
|
-
|
|
935
|
-
const lifecycleEvents = {
|
|
936
|
-
onReadyForDisplay: this.unHandledEvent("onReadyForDisplay"),
|
|
937
|
-
onVideoEnd: this.onVideoEnd, // complete
|
|
938
|
-
onVideoError: this.onVideoError, // error
|
|
939
|
-
onVideoProgress: this.onVideoProgress,
|
|
940
|
-
onVideoFullscreenPlayerDidDismiss: this.onVideoFullscreenPlayerDidDismiss,
|
|
941
|
-
onVideoFullscreenPlayerDidPresent: this.onVideoFullscreenPlayerDidPresent,
|
|
942
|
-
onVideoFullscreenPlayerWillDismiss:
|
|
943
|
-
this.onVideoFullscreenPlayerWillDismiss,
|
|
944
|
-
onVideoFullscreenPlayerWillPresent:
|
|
945
|
-
this.onVideoFullscreenPlayerWillPresent,
|
|
946
|
-
onVideoLoad: this.onVideoLoad,
|
|
947
|
-
onVideoLoadStart: this.unHandledEvent("onVideoLoadStart"),
|
|
948
|
-
onVideoBuffer: this.onVideoBuffer,
|
|
949
|
-
onPlaybackStalled: this.onPlaybackStalled,
|
|
950
|
-
onPlayerClose: this.onPlayerClose,
|
|
951
|
-
onPlayerAttached: this.onPlayerAttached,
|
|
952
|
-
onPlayerDetached: this.onPlayerDetached,
|
|
953
|
-
};
|
|
954
|
-
|
|
955
|
-
const androidLifecyclePiPEvents = isAndroidPlatform() && {
|
|
956
|
-
onPiPEnter: this.onPiPEnter,
|
|
957
|
-
onPiPExit: this.onPiPExit,
|
|
958
|
-
onPiPCloseByUser: this.onPiPCloseByUser,
|
|
959
|
-
};
|
|
960
|
-
|
|
961
|
-
const playerMetadataChangedEvents = {
|
|
962
|
-
onTimedMetadata: this.unHandledEvent("onTimedMetadata"),
|
|
963
|
-
onTracksChanged: this.onTracksChanged,
|
|
964
|
-
eventSubtitlesChange: this.unHandledEvent("eventSubtitlesChange"),
|
|
965
|
-
onVideoBandwidthUpdate: this.onVideoBandwidthUpdate,
|
|
966
|
-
};
|
|
967
|
-
|
|
968
|
-
const playerSystemEvents = {
|
|
969
|
-
onAudioFocusChanged: this.unHandledEvent("onAudioFocusChanged"),
|
|
970
|
-
onVideoExternalPlaybackChange: this.unHandledEvent(
|
|
971
|
-
"onVideoExternalPlaybackChange"
|
|
972
|
-
),
|
|
973
|
-
onVideoAudioBecomingNoisy: this.unHandledEvent(
|
|
974
|
-
"onVideoAudioBecomingNoisy"
|
|
975
|
-
),
|
|
976
|
-
};
|
|
977
|
-
|
|
978
|
-
const androidPlayerEvents = {
|
|
979
|
-
onKeyChanged: keyEvent,
|
|
980
|
-
};
|
|
981
|
-
|
|
982
|
-
const unsupportedEvents = {
|
|
983
|
-
onBufferComplete: this.onBufferComplete, // buffer_complete
|
|
984
|
-
onBufferStart: this.onBufferStart, // buffer_start
|
|
985
|
-
onPlayerNext: this.onPlayerNext, // next
|
|
986
|
-
onPlayerPrevious: this.onPlayerPrevious, // previous
|
|
987
|
-
onPlayerStop: this.onPlayerStop, // stop
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
const nativeEvents = {
|
|
991
|
-
...adEvents,
|
|
992
|
-
...androidPlayerEvents,
|
|
993
|
-
...lifecycleEvents,
|
|
994
|
-
...playerMetadataChangedEvents,
|
|
995
|
-
...playerSystemEvents,
|
|
996
|
-
...unsupportedEvents,
|
|
997
|
-
...playbackEvents,
|
|
998
|
-
...androidLifecyclePiPEvents,
|
|
999
|
-
};
|
|
1000
|
-
|
|
1001
|
-
const needToShowPlayNextOverlay = this.getPlayNextData() && !docked;
|
|
1002
|
-
|
|
1003
|
-
const playerContainerStyles = isApplePlatform()
|
|
1004
|
-
? styles.playerContainerBlack
|
|
1005
|
-
: isTV() && isAudioItem
|
|
1006
|
-
? styles.playerContainerTransparent
|
|
1007
|
-
: styles.playerContainerBlack;
|
|
1008
|
-
|
|
1009
|
-
const isInline = () => {
|
|
1010
|
-
if (isTV()) {
|
|
1011
|
-
return inline;
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
// HACK: on mobile overwrite inline value if player is fullScreen
|
|
1015
|
-
return fullscreen ? false : inline;
|
|
1016
|
-
};
|
|
1017
|
-
|
|
1018
|
-
const renderTransportControls = (playerDimensions) => {
|
|
1019
|
-
const needsToRender =
|
|
1020
|
-
!hideTransportControls &&
|
|
1021
|
-
!needToShowPlayNextOverlay &&
|
|
1022
|
-
this.props.mode !== "PIP";
|
|
1023
|
-
|
|
1024
|
-
if (shouldUseNativeControls) {
|
|
1025
|
-
return null;
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
const TransportControls = isTV()
|
|
1029
|
-
? TransportControlsTV
|
|
1030
|
-
: TransportControlsMobile;
|
|
1031
|
-
|
|
1032
|
-
return needsToRender ? (
|
|
1033
|
-
<TransportControls
|
|
1034
|
-
playerContent={entry}
|
|
1035
|
-
configuration={this.getPluginConfiguration()}
|
|
1036
|
-
playerDimensions={playerDimensions}
|
|
1037
|
-
inline={isInline()}
|
|
1038
|
-
docked={fullscreen ? false : docked}
|
|
1039
|
-
isModal={isModal}
|
|
1040
|
-
extraInfoText={castAction?.state?.friendlyStatus}
|
|
1041
|
-
controlsAlwaysVisible={castAction?.state?.connected}
|
|
1042
|
-
playNextData={this.getPlayNextData()}
|
|
1043
|
-
nativeControls={!!controls}
|
|
1044
|
-
/>
|
|
1045
|
-
) : null;
|
|
1046
|
-
};
|
|
1047
|
-
|
|
1048
|
-
const isFullScreenAudioPlayer =
|
|
1049
|
-
this.getPluginConfiguration()?.["full_screen_audio_player"] || false;
|
|
1050
|
-
|
|
1051
|
-
const sharedWrapperViewProps = {
|
|
1052
|
-
style,
|
|
1053
|
-
entry,
|
|
1054
|
-
inline,
|
|
1055
|
-
docked,
|
|
1056
|
-
isModal,
|
|
1057
|
-
pip: this.state.piped,
|
|
1058
|
-
layoutState: {
|
|
1059
|
-
inline,
|
|
1060
|
-
docked,
|
|
1061
|
-
isModal,
|
|
1062
|
-
pip: this.state.piped,
|
|
1063
|
-
},
|
|
1064
|
-
configuration: this.getPluginConfiguration(),
|
|
1065
|
-
fullscreen: fullscreen,
|
|
1066
|
-
isTabletPortrait: isTabletPortrait,
|
|
1067
|
-
playNextData: this.getPlayNextData(),
|
|
1068
|
-
};
|
|
1069
|
-
|
|
1070
|
-
const containerStyle = { flex: 1 };
|
|
1071
|
-
|
|
1072
|
-
const backgroundImageKey =
|
|
1073
|
-
this.getPluginConfiguration()?.player_preview_image_key;
|
|
1074
|
-
|
|
1075
|
-
const renderMobilePlayer = () => {
|
|
1076
|
-
return isFullScreenAudioPlayer && isAudioItem ? (
|
|
1077
|
-
<View style={containerStyle}>
|
|
1078
|
-
{(!castAction?.state?.connected || supportNativeCast) && (
|
|
1079
|
-
<this.props.PlayerComponent
|
|
1080
|
-
ref={this._assignRoot}
|
|
1081
|
-
{...nativeEvents}
|
|
1082
|
-
{...nativeProps}
|
|
1083
|
-
/>
|
|
1084
|
-
)}
|
|
1085
|
-
<AudioPlayerWrapper {...sharedWrapperViewProps} />
|
|
1086
|
-
</View>
|
|
1087
|
-
) : (
|
|
1088
|
-
<PlayerWrapper {...sharedWrapperViewProps}>
|
|
1089
|
-
{(playerDimensions) => {
|
|
1090
|
-
const shouldRenderBackground =
|
|
1091
|
-
!R.isNil(backgroundImageKey) &&
|
|
1092
|
-
backgroundImageKey.length > 0 &&
|
|
1093
|
-
!this.onVideoLoadCalled &&
|
|
1094
|
-
!this.state.isAd;
|
|
1095
|
-
|
|
1096
|
-
return (
|
|
1097
|
-
<View style={styles.container}>
|
|
1098
|
-
<View style={[playerContainerStyles, playerDimensions]}>
|
|
1099
|
-
{isAudioItem && (
|
|
1100
|
-
<AnimationComponent
|
|
1101
|
-
animationType={ComponentAnimationType.player}
|
|
1102
|
-
additionalData={{
|
|
1103
|
-
...playerDimensions,
|
|
1104
|
-
inlineAudioPlayer: true,
|
|
1105
|
-
}}
|
|
1106
|
-
style={getStyle(playerDimensions)}
|
|
1107
|
-
>
|
|
1108
|
-
<AudioPlayer
|
|
1109
|
-
style={
|
|
1110
|
-
isTV()
|
|
1111
|
-
? getStyle(playerDimensions)
|
|
1112
|
-
: styles.playerSize
|
|
1113
|
-
}
|
|
1114
|
-
audio_item={entry}
|
|
1115
|
-
plugin_configuration={this.getPluginConfiguration()}
|
|
1116
|
-
/>
|
|
1117
|
-
</AnimationComponent>
|
|
1118
|
-
)}
|
|
1119
|
-
|
|
1120
|
-
{shouldRenderBackground && (
|
|
1121
|
-
<PlayerImageBackground
|
|
1122
|
-
imageKey={backgroundImageKey}
|
|
1123
|
-
configuration={this.getPluginConfiguration()}
|
|
1124
|
-
entry={entry}
|
|
1125
|
-
style={getBackgroundImageStyle(playerDimensions)}
|
|
1126
|
-
/>
|
|
1127
|
-
)}
|
|
1128
|
-
|
|
1129
|
-
{(!castAction?.state?.connected || supportNativeCast) && (
|
|
1130
|
-
<AnimationComponent
|
|
1131
|
-
animationType={ComponentAnimationType.player}
|
|
1132
|
-
additionalData={playerDimensions}
|
|
1133
|
-
style={getStyle(playerDimensions)}
|
|
1134
|
-
>
|
|
1135
|
-
<this.props.PlayerComponent
|
|
1136
|
-
ref={this._assignRoot}
|
|
1137
|
-
{...nativeEvents}
|
|
1138
|
-
{...nativeProps}
|
|
1139
|
-
listener={this.props.listener}
|
|
1140
|
-
style={
|
|
1141
|
-
isTV()
|
|
1142
|
-
? getStyle(playerDimensions)
|
|
1143
|
-
: styles.playerSize
|
|
1144
|
-
}
|
|
1145
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
1146
|
-
controller={this.props.controller}
|
|
1147
|
-
>
|
|
1148
|
-
{this.childrenWithPlayNext()}
|
|
1149
|
-
</this.props.PlayerComponent>
|
|
1150
|
-
</AnimationComponent>
|
|
1151
|
-
)}
|
|
1152
|
-
</View>
|
|
1153
|
-
|
|
1154
|
-
{renderTransportControls(playerDimensions)}
|
|
1155
|
-
{!this.state.piped && (
|
|
1156
|
-
<PlayNextOverlay
|
|
1157
|
-
item={entry}
|
|
1158
|
-
playerIsDocked={docked}
|
|
1159
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
1160
|
-
playerId={this.props.playerId}
|
|
1161
|
-
playNextData={this.getPlayNextData()}
|
|
1162
|
-
setNextVideoPreloadThresholdPercentage={
|
|
1163
|
-
this.props.setNextVideoPreloadThresholdPercentage
|
|
1164
|
-
}
|
|
1165
|
-
/>
|
|
1166
|
-
)}
|
|
1167
|
-
</View>
|
|
1168
|
-
);
|
|
1169
|
-
}}
|
|
1170
|
-
</PlayerWrapper>
|
|
1171
|
-
);
|
|
1172
|
-
};
|
|
1173
|
-
|
|
1174
|
-
const renderTVPlayer = () => {
|
|
1175
|
-
return (
|
|
1176
|
-
<View style={this.takeParentStyle} testID={"player-view"}>
|
|
1177
|
-
<this.props.PlayerComponent
|
|
1178
|
-
ref={this._assignRoot}
|
|
1179
|
-
{...nativeEvents}
|
|
1180
|
-
{...nativeProps}
|
|
1181
|
-
listener={this.props.listener}
|
|
1182
|
-
style={this.takeParentStyle}
|
|
1183
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
1184
|
-
controller={this.props.controller}
|
|
1185
|
-
>
|
|
1186
|
-
{this.childrenWithPlayNext()}
|
|
1187
|
-
</this.props.PlayerComponent>
|
|
1188
|
-
|
|
1189
|
-
{renderTransportControls(this.takeParentStyle)}
|
|
1190
|
-
{!isTvOSPlatform() && (
|
|
1191
|
-
<PlayNextOverlay
|
|
1192
|
-
item={entry}
|
|
1193
|
-
playerIsDocked={docked}
|
|
1194
|
-
pluginConfiguration={this.getPluginConfiguration()}
|
|
1195
|
-
playerId={this.props.playerId}
|
|
1196
|
-
playNextData={this.getPlayNextData()}
|
|
1197
|
-
setNextVideoPreloadThresholdPercentage={
|
|
1198
|
-
this.props.setNextVideoPreloadThresholdPercentage
|
|
1199
|
-
}
|
|
1200
|
-
/>
|
|
1201
|
-
)}
|
|
1202
|
-
</View>
|
|
1203
|
-
);
|
|
1204
|
-
};
|
|
1205
|
-
|
|
1206
|
-
return isTV() ? renderTVPlayer() : renderMobilePlayer();
|
|
187
|
+
return (
|
|
188
|
+
<View style={this.takeParentStyle} testID={"player-view"}>
|
|
189
|
+
<video autoPlay controls ref={this._assignRoot}></video>
|
|
190
|
+
<DebugOverlay logs={this.state.logs} />
|
|
191
|
+
</View>
|
|
192
|
+
);
|
|
1207
193
|
}
|
|
1208
194
|
}
|