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