@applicaster/quick-brick-player 12.0.0-alpha.3788876021 → 12.0.0-alpha.3823240084
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 +6 -6
- package/src/CellPlayer/Assets/AssetsComponent.tsx +59 -49
- package/src/CellPlayer/ContextProvider.tsx +50 -1
- package/src/Player/AudioLayer/AudioPlayerWrapper.tsx +3 -1
- package/src/Player/AudioLayer/Layout/DefaultLayout.tsx +42 -30
- package/src/Player/AudioLayer/Layout/DockedLayout.tsx +13 -12
- package/src/Player/AudioLayer/MagicBG/index.tsx +46 -14
- package/src/Player/Utils/Utils.ts +9 -22
- package/src/Player/index.tsx +48 -121
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.3823240084",
|
|
4
4
|
"description": "Quick Brick Player",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@applicaster/quick-brick-mobile-transport-controls": "12.0.0-
|
|
40
|
-
"@applicaster/quick-brick-tv-transport-controls": "
|
|
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.
|
|
39
|
+
"@applicaster/quick-brick-mobile-transport-controls": "12.0.0-rc.106",
|
|
40
|
+
"@applicaster/quick-brick-tv-transport-controls": "12.0.0-rc.109",
|
|
41
|
+
"@applicaster/zapp-react-native-tvos-app": "12.0.0-alpha.3823240084",
|
|
42
|
+
"@applicaster/zapp-react-native-ui-components": "12.0.0-alpha.3823240084",
|
|
43
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.3823240084",
|
|
44
44
|
"keymirror": "^0.1.1",
|
|
45
45
|
"query-string": "7.1.3",
|
|
46
46
|
"react-native-web-linear-gradient": "1.1.2",
|
|
@@ -10,66 +10,76 @@ type Asset = string | undefined;
|
|
|
10
10
|
const isCurrentPlayer = (playerId) =>
|
|
11
11
|
LiveImageManager.instance.getCurrentlyPlaying()?.playerId === playerId;
|
|
12
12
|
|
|
13
|
+
const liveImageButtonsListenerKey = "live-image-buttons";
|
|
14
|
+
|
|
13
15
|
const usePlayPauseListeners = (playerId, player, asset, setImageUri) =>
|
|
14
16
|
useEffect(() => {
|
|
15
17
|
if (playerId && player) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
setImageUri(null);
|
|
19
|
+
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (player.playerState.isReadyToPlay) {
|
|
24
|
+
if (player.playerState.isPaused) {
|
|
25
|
+
setImageUri(asset.active);
|
|
26
|
+
} else {
|
|
27
|
+
setImageUri(asset.inactive);
|
|
22
28
|
}
|
|
29
|
+
}
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
},
|
|
30
|
-
onPlayerPause: () => {
|
|
31
|
-
setImageUri(asset.active);
|
|
32
|
-
},
|
|
33
|
-
onPlayerClose: () => {
|
|
34
|
-
setImageUri(null);
|
|
35
|
-
},
|
|
36
|
-
onLoad() {
|
|
37
|
-
setImageUri(asset.inactive);
|
|
38
|
-
},
|
|
31
|
+
return player.addListener({
|
|
32
|
+
id: liveImageButtonsListenerKey,
|
|
33
|
+
listener: {
|
|
34
|
+
onPlayerResume: () => {
|
|
35
|
+
setImageUri(asset.inactive);
|
|
39
36
|
},
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
onPlayerPause: () => {
|
|
38
|
+
setImageUri(asset.active);
|
|
39
|
+
},
|
|
40
|
+
onPlayerClose: () => {
|
|
41
|
+
setImageUri(null);
|
|
42
|
+
},
|
|
43
|
+
onLoad() {
|
|
44
|
+
setImageUri(asset.inactive);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
42
48
|
}, [player, playerId, asset]);
|
|
43
49
|
|
|
44
50
|
const useMuteUnmuteListeners = (playerId, player, asset, setImageUri) =>
|
|
45
51
|
useEffect(() => {
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
listener: {
|
|
51
|
-
onPlayerMute: () => {
|
|
52
|
-
if (isCurrentPlayer(playerId)) {
|
|
53
|
-
setImageUri(asset.active);
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
onPlayerUnmute: () => {
|
|
57
|
-
if (isCurrentPlayer(playerId)) {
|
|
58
|
-
setImageUri(asset.inactive);
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
onLoad() {
|
|
62
|
-
setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
|
|
63
|
-
},
|
|
64
|
-
onPlayerClose: () => {
|
|
65
|
-
setImageUri(null);
|
|
66
|
-
},
|
|
67
|
-
onVideoEnd: () => {
|
|
68
|
-
setImageUri(null);
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
});
|
|
52
|
+
if (!player || !playerId) {
|
|
53
|
+
setImageUri(null);
|
|
54
|
+
|
|
55
|
+
return;
|
|
72
56
|
}
|
|
57
|
+
|
|
58
|
+
// active action = muted, inactive = unmuted
|
|
59
|
+
return player.addListener({
|
|
60
|
+
id: liveImageButtonsListenerKey,
|
|
61
|
+
listener: {
|
|
62
|
+
onPlayerMute: () => {
|
|
63
|
+
if (isCurrentPlayer(playerId)) {
|
|
64
|
+
setImageUri(asset.active);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
onPlayerUnmute: () => {
|
|
68
|
+
if (isCurrentPlayer(playerId)) {
|
|
69
|
+
setImageUri(asset.inactive);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
onLoad() {
|
|
73
|
+
setImageUri(player?.getIsMuted() ? asset.active : asset.inactive);
|
|
74
|
+
},
|
|
75
|
+
onPlayerClose: () => {
|
|
76
|
+
setImageUri(null);
|
|
77
|
+
},
|
|
78
|
+
onVideoEnd: () => {
|
|
79
|
+
setImageUri(null);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
73
83
|
}, [player, playerId, asset]);
|
|
74
84
|
|
|
75
85
|
const useListeners = MUTED_USAGE
|
|
@@ -21,7 +21,7 @@ const playActionCallback = (player) => {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
const invokeAction = () => {
|
|
24
|
+
const invokeAction = (_item: ZappEntry, _object) => {
|
|
25
25
|
const actionCallback = MUTED_USAGE ? muteActionCallback : playActionCallback;
|
|
26
26
|
// We only allow one player to be present at a time
|
|
27
27
|
const player = LiveImageManager.instance.getCurrentlyPlaying()?.player;
|
|
@@ -29,6 +29,54 @@ const invokeAction = () => {
|
|
|
29
29
|
actionCallback(player);
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
// Not exported because currently the is no way
|
|
33
|
+
// to subscribe to the action availability change.
|
|
34
|
+
// We currently return a null state to disable the action.
|
|
35
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
36
|
+
const isActionAvailable = (item: ZappEntry) => {
|
|
37
|
+
return (
|
|
38
|
+
LiveImageManager.instance.getCurrentlyPlaying()?.getPlayer().entry.id ===
|
|
39
|
+
item.id
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const addListener = (
|
|
44
|
+
entryId: string,
|
|
45
|
+
listener: CellActionStateListenerFunction
|
|
46
|
+
) => {
|
|
47
|
+
const callback = (event: Record<string, any>) => {
|
|
48
|
+
if (event.entry?.id !== entryId) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const player = LiveImageManager.instance.getCurrentlyPlaying()?.getPlayer();
|
|
53
|
+
|
|
54
|
+
if (!player || player.entry?.id !== entryId) {
|
|
55
|
+
listener(null);
|
|
56
|
+
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const actionState = MUTED_USAGE ? player.getIsMuted() : player.isPaused();
|
|
61
|
+
|
|
62
|
+
listener({
|
|
63
|
+
state: actionState ? 1 : 0,
|
|
64
|
+
label: "",
|
|
65
|
+
asset: AssetComponent,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return LiveImageManager.instance.addListener({
|
|
70
|
+
id: `cell-player-${entryId}`,
|
|
71
|
+
listener: {
|
|
72
|
+
onEnd: callback,
|
|
73
|
+
onStart: callback,
|
|
74
|
+
onError: callback,
|
|
75
|
+
onDestroy: callback,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
32
80
|
const emptyState = [];
|
|
33
81
|
|
|
34
82
|
const initialEntryState = () => ({
|
|
@@ -46,6 +94,7 @@ export function ContextProvider(Component: ReactComponent<any>) {
|
|
|
46
94
|
state: emptyState,
|
|
47
95
|
invokeAction,
|
|
48
96
|
initialEntryState,
|
|
97
|
+
addListener,
|
|
49
98
|
}),
|
|
50
99
|
[invokeAction, initialEntryState]
|
|
51
100
|
);
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
AnimatedScrollModal,
|
|
33
33
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
34
34
|
import { PADDING_HORIZONTAL } from "./Layout/defaultLayoutsConts";
|
|
35
|
+
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
35
36
|
|
|
36
37
|
const { log_debug } = createLogger({
|
|
37
38
|
subsystem: "UIComponent",
|
|
@@ -294,7 +295,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
294
295
|
playerState.isPaused ? player?.play() : player.pause();
|
|
295
296
|
}, [playerState, player]);
|
|
296
297
|
|
|
297
|
-
const seekDuration =
|
|
298
|
+
const seekDuration = toNumberWithDefault(10, value("seek_duration"));
|
|
298
299
|
|
|
299
300
|
const onRewindButtonPress = React.useCallback(() => {
|
|
300
301
|
player?.rewind(seekDuration);
|
|
@@ -387,6 +388,7 @@ export const AudioPlayerWrapper: React.FC<Props> = (props: Props) => {
|
|
|
387
388
|
<AnimationComponent
|
|
388
389
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
389
390
|
additionalData={{ saveArea: true }}
|
|
391
|
+
style={Styles.playerContainer}
|
|
390
392
|
>
|
|
391
393
|
<AnimatedScrollModal>
|
|
392
394
|
<View style={Styles.playerContainer} onLayout={onLayout}>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
import { View } from "react-native";
|
|
4
|
+
import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
ProgressBar,
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
ComponentAnimationType,
|
|
34
35
|
} from "@applicaster/zapp-react-native-ui-components/Components/VideoModal/ModalAnimation";
|
|
35
36
|
import DockedLayout from "./DockedLayout";
|
|
37
|
+
import { isValidDate } from "../../Utils";
|
|
36
38
|
|
|
37
39
|
type Dimensions = {
|
|
38
40
|
width: number;
|
|
@@ -79,7 +81,7 @@ type LayoutProps = {
|
|
|
79
81
|
|
|
80
82
|
const Styles = {
|
|
81
83
|
container: { flex: 1 },
|
|
82
|
-
tabletContainer: (isPortraitMode) => ({
|
|
84
|
+
tabletContainer: (isPortraitMode: boolean) => ({
|
|
83
85
|
flex: 1,
|
|
84
86
|
alignItems: "center",
|
|
85
87
|
justifyContent: isPortraitMode ? "center" : "flex-start",
|
|
@@ -109,10 +111,10 @@ const Styles = {
|
|
|
109
111
|
};
|
|
110
112
|
},
|
|
111
113
|
backgroundImageContainerImageStyle: (
|
|
112
|
-
isTablet,
|
|
113
|
-
docked,
|
|
114
|
-
startComponentsAnimation,
|
|
115
|
-
isActiveGesture
|
|
114
|
+
isTablet: boolean,
|
|
115
|
+
docked: boolean,
|
|
116
|
+
startComponentsAnimation: boolean,
|
|
117
|
+
isActiveGesture: boolean
|
|
116
118
|
) => ({
|
|
117
119
|
borderRadius:
|
|
118
120
|
(startComponentsAnimation && isActiveGesture) || docked
|
|
@@ -190,8 +192,31 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
190
192
|
|
|
191
193
|
let showFullControls = !controller?.isAd();
|
|
192
194
|
|
|
195
|
+
const startTime = entry.extensions?.live?.start_time;
|
|
196
|
+
const endTime = entry.extensions?.live?.end_time;
|
|
197
|
+
|
|
198
|
+
const hasValidStartTime = isValidDate(startTime);
|
|
199
|
+
const hasValidEndTime = isValidDate(endTime);
|
|
200
|
+
|
|
201
|
+
const isDateBasedLiveContent =
|
|
202
|
+
isLiveContent && hasValidStartTime && hasValidEndTime;
|
|
203
|
+
|
|
204
|
+
const needsToShowProgressBar =
|
|
205
|
+
(isLiveContent &&
|
|
206
|
+
(Boolean(value("liveSeekingEnabled")) || isDateBasedLiveContent)) ||
|
|
207
|
+
!isLiveContent;
|
|
208
|
+
|
|
209
|
+
const startTimestamp = hasValidStartTime
|
|
210
|
+
? dayjs.utc(startTime!).valueOf()
|
|
211
|
+
: undefined;
|
|
212
|
+
|
|
213
|
+
const endTimestamp = hasValidEndTime
|
|
214
|
+
? dayjs.utc(endTime!).valueOf()
|
|
215
|
+
: undefined;
|
|
216
|
+
|
|
193
217
|
if (isLiveContent) {
|
|
194
218
|
const seekEnabled = Boolean(value("liveSeekingEnabled"));
|
|
219
|
+
|
|
195
220
|
showFullControls = !!(controller?.hasSeekableDuration?.() && seekEnabled);
|
|
196
221
|
}
|
|
197
222
|
|
|
@@ -221,12 +246,14 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
221
246
|
|
|
222
247
|
const rewind = rewindButton({
|
|
223
248
|
isTablet,
|
|
224
|
-
onPress: controller?.
|
|
249
|
+
onPress: controller?.hasSeekableDuration?.() ? onRewindButtonPress : null,
|
|
225
250
|
});
|
|
226
251
|
|
|
227
252
|
const forward = forwardButton({
|
|
228
253
|
isTablet,
|
|
229
|
-
onPress: controller?.
|
|
254
|
+
onPress: controller?.hasSeekableDuration?.()
|
|
255
|
+
? onForwardButtonPress
|
|
256
|
+
: null,
|
|
230
257
|
});
|
|
231
258
|
|
|
232
259
|
const speed = speedButton({
|
|
@@ -255,7 +282,6 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
255
282
|
return [skipToStart, rewind, play, forward, skipToLive];
|
|
256
283
|
}
|
|
257
284
|
|
|
258
|
-
// Temporary empty button until we add sleep button. It will ensure the buttons will not break in their position
|
|
259
285
|
return [speed, rewind, play, forward, sleep];
|
|
260
286
|
}, [
|
|
261
287
|
playerState,
|
|
@@ -276,15 +302,12 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
276
302
|
|
|
277
303
|
useEffect(() => {
|
|
278
304
|
if (showCounter && sleepTimerDate !== 0) {
|
|
279
|
-
// Setting the initial timeLeft based on sleepTimerDate
|
|
280
305
|
setTimeLeft(sleepTimerDate - Date.now());
|
|
281
306
|
|
|
282
|
-
// Setting up an interval to update the remaining time every second
|
|
283
307
|
const intervalId = setInterval(() => {
|
|
284
308
|
setTimeLeft(() => {
|
|
285
309
|
const newTimeLeft = sleepTimerDate - Date.now();
|
|
286
310
|
|
|
287
|
-
// If the new remaining time is less than or equal to 0, clear the interval and trigger the cancelSleepTimer function
|
|
288
311
|
if (newTimeLeft <= 0) {
|
|
289
312
|
clearInterval(intervalId);
|
|
290
313
|
|
|
@@ -292,36 +315,24 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
292
315
|
|
|
293
316
|
return 0;
|
|
294
317
|
} else {
|
|
295
|
-
// Otherwise, update the state with the new remaining time
|
|
296
318
|
return newTimeLeft;
|
|
297
319
|
}
|
|
298
320
|
});
|
|
299
321
|
}, 1000);
|
|
300
322
|
|
|
301
|
-
// Clearing the interval when the component unmounts or dependencies changea
|
|
302
323
|
return () => clearInterval(intervalId);
|
|
303
324
|
} else {
|
|
304
|
-
// Ensure the timeLeft state is set correctly if showCounter is false
|
|
305
325
|
setTimeLeft(0);
|
|
306
326
|
}
|
|
307
327
|
}, [sleepTimerDate, showCounter, cancelSleepTimer]);
|
|
308
328
|
|
|
309
329
|
const Separator = () => <View style={Styles.separator} />;
|
|
310
330
|
|
|
311
|
-
const liveSeekingEnabled = value("liveSeekingEnabled");
|
|
312
|
-
|
|
313
|
-
const startTime = entry.extensions.start_time; // October 28, 2024, at 14:27 UTC
|
|
314
|
-
const endTime = entry.extensions.end_time; // October 28, 2024, at 16:27 UTC
|
|
315
|
-
|
|
316
|
-
const needsToShowProgressBar =
|
|
317
|
-
(isLiveContent && (liveSeekingEnabled || (startTime && endTime))) ||
|
|
318
|
-
!isLiveContent;
|
|
319
|
-
|
|
320
331
|
const defaultImageDimensions = React.useMemo(
|
|
321
332
|
() =>
|
|
322
333
|
defaultDimensions &&
|
|
323
334
|
Styles.backgroundImageContainer(defaultDimensions, isTablet),
|
|
324
|
-
[defaultDimensions]
|
|
335
|
+
[defaultDimensions, isTablet]
|
|
325
336
|
);
|
|
326
337
|
|
|
327
338
|
const { isActiveGesture, startComponentsAnimation } =
|
|
@@ -345,7 +356,7 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
345
356
|
</AnimationComponent>
|
|
346
357
|
<AnimationComponent
|
|
347
358
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
348
|
-
style={isTablet ? Styles.playerImageWrapper :
|
|
359
|
+
style={isTablet ? Styles.playerImageWrapper : undefined}
|
|
349
360
|
>
|
|
350
361
|
<AnimationComponent
|
|
351
362
|
animationType={ComponentAnimationType.moveComponentHorizontal}
|
|
@@ -360,7 +371,7 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
360
371
|
}
|
|
361
372
|
: null,
|
|
362
373
|
}}
|
|
363
|
-
style={isTablet ? Styles.alignItemsCenter :
|
|
374
|
+
style={isTablet ? Styles.alignItemsCenter : undefined}
|
|
364
375
|
>
|
|
365
376
|
<PlayerImageBackground
|
|
366
377
|
entry={entry}
|
|
@@ -380,7 +391,7 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
380
391
|
<View
|
|
381
392
|
style={[
|
|
382
393
|
Styles.paddingHorizontal,
|
|
383
|
-
isTablet ? Styles.tabletLayout :
|
|
394
|
+
isTablet ? Styles.tabletLayout : undefined,
|
|
384
395
|
]}
|
|
385
396
|
>
|
|
386
397
|
{!docked && (
|
|
@@ -405,9 +416,10 @@ export default function DefaultLayout(props: LayoutProps) {
|
|
|
405
416
|
playerState={playerState}
|
|
406
417
|
disableFullscreen={true}
|
|
407
418
|
sliderStyle={2}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
419
|
+
startTimestamp={startTimestamp}
|
|
420
|
+
endTimestamp={endTimestamp}
|
|
421
|
+
isDateBasedLiveContent={isDateBasedLiveContent}
|
|
422
|
+
onSkipLive={onSkipLive}
|
|
411
423
|
/>
|
|
412
424
|
|
|
413
425
|
{showCounter && timeLeft >= 0 && (
|
|
@@ -117,7 +117,6 @@ const playerDimensionsStyle = (
|
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
const animatedContainerStyle = (docked: boolean): ViewStyle => ({
|
|
120
|
-
top: docked ? 2 : null,
|
|
121
120
|
position: "absolute",
|
|
122
121
|
width: "100%",
|
|
123
122
|
zIndex: docked ? 1 : -1,
|
|
@@ -245,17 +244,19 @@ export default function DockedLayout(props: LayoutProps) {
|
|
|
245
244
|
animatedContainerStyle(isActiveGesture || layoutState.docked),
|
|
246
245
|
]}
|
|
247
246
|
>
|
|
248
|
-
{!playerState.isLive &&
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
247
|
+
{!playerState.isLive &&
|
|
248
|
+
(layoutState.docked || startComponentsAnimation) && (
|
|
249
|
+
<ProgressBar
|
|
250
|
+
content={entry}
|
|
251
|
+
value={value}
|
|
252
|
+
visible={visible}
|
|
253
|
+
layoutState={layoutState}
|
|
254
|
+
onPlayerSeek={onPlayerSeek}
|
|
255
|
+
playerState={playerState}
|
|
256
|
+
startComponentsAnimation={startComponentsAnimation}
|
|
257
|
+
docked
|
|
258
|
+
/>
|
|
259
|
+
)}
|
|
259
260
|
|
|
260
261
|
<View style={styles.content}>
|
|
261
262
|
<View
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// Disabling for now as lint can't recognise typescript definition here
|
|
2
1
|
/* eslint-disable react/prop-types */
|
|
3
2
|
import * as React from "react";
|
|
4
|
-
import { StyleSheet, View } from "react-native";
|
|
3
|
+
import { Animated, StyleSheet, View } from "react-native";
|
|
5
4
|
import LinearGradient from "../LinearGradient";
|
|
6
5
|
import { getDominantColor, getPalettes } from "../../../utils/utils";
|
|
7
6
|
import { useEffect } from "react";
|
|
@@ -11,7 +10,7 @@ type MagicBGProps = {
|
|
|
11
10
|
image: string;
|
|
12
11
|
fallbackUrl?: string;
|
|
13
12
|
defaultColor: string;
|
|
14
|
-
colors: string[];
|
|
13
|
+
colors: string[];
|
|
15
14
|
};
|
|
16
15
|
|
|
17
16
|
const defaultColors = {
|
|
@@ -66,31 +65,46 @@ export const MagicBG: React.FC<MagicBGProps> = ({
|
|
|
66
65
|
defaultColors.defaultGradientEnd,
|
|
67
66
|
]);
|
|
68
67
|
|
|
68
|
+
const [backgroundColorAnim] = React.useState(new Animated.Value(0)); // from 0 to 1
|
|
69
|
+
const [gradientOpacityAnim] = React.useState(new Animated.Value(0)); // from 0 to 1
|
|
70
|
+
|
|
69
71
|
useEffect(() => {
|
|
72
|
+
const animateColors = () => {
|
|
73
|
+
Animated.timing(backgroundColorAnim, {
|
|
74
|
+
toValue: 1,
|
|
75
|
+
duration: 500,
|
|
76
|
+
useNativeDriver: false,
|
|
77
|
+
}).start();
|
|
78
|
+
|
|
79
|
+
Animated.timing(gradientOpacityAnim, {
|
|
80
|
+
toValue: 1,
|
|
81
|
+
duration: 500,
|
|
82
|
+
useNativeDriver: false,
|
|
83
|
+
}).start();
|
|
84
|
+
};
|
|
85
|
+
|
|
70
86
|
const fetchColors = async () => {
|
|
71
|
-
// Check if colors are provided and not empty
|
|
72
87
|
if (colors && colors.length > 0) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
88
|
+
setGradientColors(colors.slice(0, 2));
|
|
89
|
+
setDominantColor(colors[0]);
|
|
90
|
+
animateColors();
|
|
76
91
|
} else if (image) {
|
|
77
|
-
// If no colors, try fetching from image
|
|
78
92
|
try {
|
|
79
93
|
const { gradientColors, dominantColor } =
|
|
80
94
|
await fetchPaletteAndDominantColor(image);
|
|
81
95
|
|
|
82
96
|
setGradientColors(gradientColors);
|
|
83
97
|
setDominantColor(dominantColor);
|
|
98
|
+
animateColors();
|
|
84
99
|
} catch (error) {
|
|
85
|
-
// If image fetch fails, try fallback URL
|
|
86
100
|
try {
|
|
87
101
|
const { gradientColors, dominantColor } =
|
|
88
102
|
await fetchPaletteAndDominantColor(fallbackUrl);
|
|
89
103
|
|
|
90
104
|
setGradientColors(gradientColors);
|
|
91
105
|
setDominantColor(dominantColor);
|
|
106
|
+
animateColors();
|
|
92
107
|
} catch {
|
|
93
|
-
// If everything fails, use default color
|
|
94
108
|
setGradientColors([
|
|
95
109
|
defaultColors.defaultGradientStart,
|
|
96
110
|
defaultColors.defaultGradientEnd,
|
|
@@ -100,7 +114,6 @@ export const MagicBG: React.FC<MagicBGProps> = ({
|
|
|
100
114
|
}
|
|
101
115
|
}
|
|
102
116
|
} else {
|
|
103
|
-
// If no image or colors, use default color
|
|
104
117
|
setGradientColors([
|
|
105
118
|
defaultColors.defaultGradientStart,
|
|
106
119
|
defaultColors.defaultGradientEnd,
|
|
@@ -113,11 +126,30 @@ export const MagicBG: React.FC<MagicBGProps> = ({
|
|
|
113
126
|
fetchColors();
|
|
114
127
|
}, []);
|
|
115
128
|
|
|
129
|
+
const interpolatedBackgroundColor = backgroundColorAnim.interpolate({
|
|
130
|
+
inputRange: [0, 1],
|
|
131
|
+
outputRange: ["black", dominantColor],
|
|
132
|
+
});
|
|
133
|
+
|
|
116
134
|
return (
|
|
117
|
-
<View
|
|
135
|
+
<Animated.View
|
|
136
|
+
style={[
|
|
137
|
+
styles.container,
|
|
138
|
+
{ backgroundColor: interpolatedBackgroundColor },
|
|
139
|
+
]}
|
|
140
|
+
>
|
|
118
141
|
<View style={styles.fullScreenOverlay} />
|
|
119
|
-
|
|
142
|
+
|
|
143
|
+
<Animated.View
|
|
144
|
+
style={[styles.gradientOverlay, { opacity: gradientOpacityAnim }]}
|
|
145
|
+
>
|
|
146
|
+
<LinearGradient
|
|
147
|
+
colors={gradientColors}
|
|
148
|
+
style={StyleSheet.absoluteFill}
|
|
149
|
+
/>
|
|
150
|
+
</Animated.View>
|
|
151
|
+
|
|
120
152
|
{children}
|
|
121
|
-
</View>
|
|
153
|
+
</Animated.View>
|
|
122
154
|
);
|
|
123
155
|
};
|
|
@@ -1,26 +1,5 @@
|
|
|
1
1
|
import queryString from "query-string";
|
|
2
|
-
|
|
3
|
-
export const toTypeString = (obj) => {
|
|
4
|
-
switch (typeof obj) {
|
|
5
|
-
case "object":
|
|
6
|
-
return obj instanceof Date ? obj.toISOString() : JSON.stringify(obj); // object, null
|
|
7
|
-
case "undefined":
|
|
8
|
-
return "";
|
|
9
|
-
default:
|
|
10
|
-
// boolean, number, string
|
|
11
|
-
return obj.toString();
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const stringsOnlyObject = (obj) => {
|
|
16
|
-
const strObj = {};
|
|
17
|
-
|
|
18
|
-
Object.keys(obj).forEach((x) => {
|
|
19
|
-
strObj[x] = toTypeString(obj[x]);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
return strObj;
|
|
23
|
-
};
|
|
2
|
+
import { dayjs } from "@applicaster/zapp-react-native-utils/dateUtils";
|
|
24
3
|
|
|
25
4
|
export const getLiveUrl = (url: string) => {
|
|
26
5
|
// Use the replace method with a regular expression to replace all instances of subClass with livePage
|
|
@@ -42,3 +21,11 @@ export const addQueryParam = (
|
|
|
42
21
|
|
|
43
22
|
return `${urlWithoutQuery}?${updatedQueryString}`;
|
|
44
23
|
};
|
|
24
|
+
|
|
25
|
+
export const isValidDate = (dateString: string | undefined): boolean => {
|
|
26
|
+
return (
|
|
27
|
+
typeof dateString === "string" &&
|
|
28
|
+
dateString.trim() !== "" &&
|
|
29
|
+
dayjs.utc(dateString).isValid()
|
|
30
|
+
);
|
|
31
|
+
};
|
package/src/Player/index.tsx
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import React from "react";
|
|
5
5
|
import * as R from "ramda";
|
|
6
6
|
|
|
7
|
-
import { findNodeHandle,
|
|
7
|
+
import { findNodeHandle, StyleSheet, View } from "react-native";
|
|
8
8
|
import TransportControlsMobile from "@applicaster/quick-brick-mobile-transport-controls";
|
|
9
9
|
import TransportControlsTV from "@applicaster/quick-brick-tv-transport-controls";
|
|
10
10
|
|
|
@@ -21,14 +21,8 @@ import { PlayerWrapper } from "@applicaster/zapp-react-native-ui-components/Comp
|
|
|
21
21
|
|
|
22
22
|
import { PlayNextOverlay } from "./PlayNextOverlay";
|
|
23
23
|
import { PlayerImageBackground } from "@applicaster/zapp-react-native-ui-components/Components/PlayerImageBackground";
|
|
24
|
-
import { AnalyticsTracker } from "@applicaster/zapp-react-native-utils/analyticsUtils/analyticsTracker";
|
|
25
24
|
|
|
26
|
-
import {
|
|
27
|
-
AD_EVENT,
|
|
28
|
-
GENERAL_EVENT,
|
|
29
|
-
PLAYBACK_EVENT,
|
|
30
|
-
QOS_EVENT,
|
|
31
|
-
} from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
|
|
25
|
+
import { GENERAL_EVENT } from "@applicaster/zapp-react-native-utils/analyticsUtils/events";
|
|
32
26
|
import { Categories, createLogger, Subsystem } from "../logger";
|
|
33
27
|
import { parseLanguageTracks } from "@applicaster/zapp-react-native-utils/playerUtils/configurationUtils";
|
|
34
28
|
import { DEFAULT_COLORS, SEEK_TIME } from "../utils/const";
|
|
@@ -42,15 +36,10 @@ import {
|
|
|
42
36
|
AnimationComponent,
|
|
43
37
|
ComponentAnimationType,
|
|
44
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";
|
|
45
41
|
|
|
46
|
-
|
|
47
|
-
QuickBrickPlayerPlugin: isApplePlatform()
|
|
48
|
-
? NativeModules.PlayerModule
|
|
49
|
-
: NativeModules.QuickBrickDefaultPlayerModule,
|
|
50
|
-
KalturaPlayerPlugin: isApplePlatform()
|
|
51
|
-
? NativeModules.KalturaPlayerModule
|
|
52
|
-
: NativeModules.QBKalturaPlayerModule,
|
|
53
|
-
};
|
|
42
|
+
import { RNPlayerManager } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/nativePlayerManager";
|
|
54
43
|
|
|
55
44
|
const styles = StyleSheet.create({
|
|
56
45
|
container: { flex: 1 },
|
|
@@ -75,7 +64,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
75
64
|
> {
|
|
76
65
|
public static propTypes = {};
|
|
77
66
|
_root: any;
|
|
78
|
-
analyticsTracker: AnalyticsTracker;
|
|
79
67
|
playerPluginId: string;
|
|
80
68
|
takeParentStyle: Record<any, any>;
|
|
81
69
|
playbackStarted = false;
|
|
@@ -106,6 +94,17 @@ export default class VideoPlayer extends React.Component<
|
|
|
106
94
|
duration: 0,
|
|
107
95
|
};
|
|
108
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
|
+
}
|
|
107
|
+
|
|
109
108
|
constructor(props) {
|
|
110
109
|
super(props);
|
|
111
110
|
this.onVideoLoadCalled = false; // android
|
|
@@ -114,16 +113,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
114
113
|
? { width: "100%", flex: "1 1 auto", objectFit: "contain" }
|
|
115
114
|
: { flexBasis: "auto", flexGrow: 1, flexShrink: 1 };
|
|
116
115
|
|
|
117
|
-
const entry = this.props?.entry;
|
|
118
|
-
this.analyticsTracker = new AnalyticsTracker(entry);
|
|
119
116
|
this.playerPluginId = this.props?.playerPluginIdentifier;
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
componentWillUnmount() {
|
|
123
|
-
this.stopBackgroundPlayback();
|
|
124
|
-
|
|
125
120
|
this.playbackStarted = false;
|
|
126
|
-
|
|
121
|
+
|
|
122
|
+
this.analyticsTracker.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
127
123
|
this.onPlayerClosed();
|
|
128
124
|
|
|
129
125
|
if (isAndroidPlatform()) {
|
|
@@ -166,6 +162,11 @@ export default class VideoPlayer extends React.Component<
|
|
|
166
162
|
}
|
|
167
163
|
}
|
|
168
164
|
|
|
165
|
+
// For close button
|
|
166
|
+
public closeNativePlayer = () => {
|
|
167
|
+
this.stopBackgroundPlayback();
|
|
168
|
+
};
|
|
169
|
+
|
|
169
170
|
getPlayNextData = () => this.props.playNextData as PlayNextData;
|
|
170
171
|
|
|
171
172
|
/**
|
|
@@ -179,27 +180,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
179
180
|
return this.props.pluginConfiguration;
|
|
180
181
|
};
|
|
181
182
|
|
|
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
183
|
/**
|
|
204
184
|
* This method is for the events that have no implementation in react but are being invoked by native
|
|
205
185
|
* When we get the synthetic event we need to pull the data in the nativeEvent object
|
|
@@ -340,11 +320,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
340
320
|
}
|
|
341
321
|
};
|
|
342
322
|
|
|
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) => {
|
|
323
|
+
selectTrack = (selected) => {
|
|
348
324
|
const { trackType, id, language } = selected || {};
|
|
349
325
|
|
|
350
326
|
const isOff = language === "Off";
|
|
@@ -403,28 +379,16 @@ export default class VideoPlayer extends React.Component<
|
|
|
403
379
|
};
|
|
404
380
|
|
|
405
381
|
onPlayerPresented = () => {
|
|
406
|
-
this.handleAnalyticEvent(GENERAL_EVENT.player_presented);
|
|
382
|
+
this.analyticsTracker?.handleAnalyticEvent(GENERAL_EVENT.player_presented);
|
|
407
383
|
};
|
|
408
384
|
|
|
409
385
|
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);
|
|
386
|
+
this.analyticsTracker?.handleAnalyticEvent(GENERAL_EVENT.player_closed);
|
|
422
387
|
};
|
|
423
388
|
|
|
424
389
|
onVideoEnd = () => {
|
|
425
390
|
this.setState({ displayOverlay: !this.state.displayOverlay }, () => {
|
|
426
391
|
if (this.state?.displayOverlay) {
|
|
427
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
|
|
428
392
|
this.props?.listener?.onVideoEnd({});
|
|
429
393
|
}
|
|
430
394
|
});
|
|
@@ -434,15 +398,12 @@ export default class VideoPlayer extends React.Component<
|
|
|
434
398
|
onVideoError = ({ nativeEvent }) => {
|
|
435
399
|
const { message } = nativeEvent;
|
|
436
400
|
|
|
437
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.error, nativeEvent);
|
|
438
|
-
|
|
439
401
|
return this.props?.listener?.onError(new Error(message));
|
|
440
402
|
};
|
|
441
403
|
|
|
442
404
|
onPlayerPause = ({ nativeEvent = null }) => {
|
|
443
405
|
this.setState({ paused: true });
|
|
444
406
|
this.props?.listener?.onPlayerPause(nativeEvent);
|
|
445
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.pause, nativeEvent);
|
|
446
407
|
};
|
|
447
408
|
|
|
448
409
|
onPlayerResume = ({ nativeEvent = null }) => {
|
|
@@ -453,98 +414,61 @@ export default class VideoPlayer extends React.Component<
|
|
|
453
414
|
|
|
454
415
|
if (isNotAnAd && !resume) {
|
|
455
416
|
this.setState({ resume: true });
|
|
456
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.play, nativeEvent);
|
|
457
417
|
}
|
|
458
418
|
};
|
|
459
419
|
|
|
460
420
|
onPlayerSeekComplete = ({ nativeEvent }) => {
|
|
461
421
|
this.props?.listener?.onPlayerSeekComplete(nativeEvent);
|
|
462
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, nativeEvent);
|
|
463
422
|
};
|
|
464
423
|
|
|
465
424
|
onPlayerSeekStart = ({ nativeEvent }) => {
|
|
466
425
|
this.props?.listener?.onPlayerSeekStart(nativeEvent);
|
|
467
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, nativeEvent);
|
|
468
426
|
};
|
|
469
427
|
|
|
470
428
|
onBufferStart = ({ nativeEvent }) => {
|
|
471
429
|
this.props?.listener?.onBufferStart(nativeEvent);
|
|
472
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, nativeEvent);
|
|
473
430
|
};
|
|
474
431
|
|
|
475
432
|
onBufferComplete = ({ nativeEvent }) => {
|
|
476
433
|
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
434
|
};
|
|
503
435
|
|
|
504
436
|
onAdBegin = ({ nativeEvent }) => {
|
|
505
437
|
this.setState({ isAd: true });
|
|
506
|
-
this.handleAnalyticEvent(AD_EVENT.ad_start, nativeEvent);
|
|
507
438
|
this.props.listener?.onAdBegin?.(nativeEvent);
|
|
508
439
|
};
|
|
509
440
|
|
|
510
441
|
onAdBreakBegin = ({ nativeEvent }) => {
|
|
511
442
|
this.setState({ isAd: true });
|
|
512
|
-
this.handleAnalyticEvent(AD_EVENT.ad_break_start, nativeEvent);
|
|
513
443
|
this.props.listener?.onAdBreakBegin?.(nativeEvent);
|
|
514
444
|
};
|
|
515
445
|
|
|
516
446
|
onAdBreakEnd = ({ nativeEvent }) => {
|
|
517
447
|
this.setState({ isAd: false });
|
|
518
|
-
this.handleAnalyticEvent(AD_EVENT.ad_break_completed, nativeEvent);
|
|
519
448
|
this.props.listener?.onAdBreakEnd?.(nativeEvent);
|
|
520
449
|
};
|
|
521
450
|
|
|
522
451
|
onAdEnd = ({ nativeEvent }) => {
|
|
523
452
|
this.setState({ isAd: false });
|
|
524
|
-
this.handleAnalyticEvent(AD_EVENT.ad_complete, nativeEvent);
|
|
525
453
|
this.props.listener?.onAdEnd?.(nativeEvent);
|
|
526
454
|
};
|
|
527
455
|
|
|
528
456
|
onAdError = ({ nativeEvent }) => {
|
|
529
457
|
this.setState({ isAd: false });
|
|
530
|
-
this.handleAnalyticEvent(AD_EVENT.ad_error, nativeEvent);
|
|
531
458
|
this.props.listener?.onAdError?.(nativeEvent);
|
|
532
459
|
};
|
|
533
460
|
|
|
534
461
|
onAdRequest = ({ nativeEvent }) => {
|
|
535
|
-
this.handleAnalyticEvent(AD_EVENT.ad_request, nativeEvent);
|
|
536
462
|
this.props.listener?.onAdRequest?.(nativeEvent);
|
|
537
463
|
};
|
|
538
464
|
|
|
539
465
|
onAdClicked = ({ nativeEvent }) => {
|
|
540
|
-
this.handleAnalyticEvent(AD_EVENT.ad_clicked, nativeEvent);
|
|
541
466
|
// Broadcast click event
|
|
542
467
|
this.props.playerEvent("ad-clicked", nativeEvent);
|
|
543
468
|
this.props.listener?.onAdClicked?.(nativeEvent);
|
|
544
469
|
};
|
|
545
470
|
|
|
546
471
|
onAdTapped = ({ nativeEvent }) => {
|
|
547
|
-
this.handleAnalyticEvent(AD_EVENT.ad_tapped, nativeEvent);
|
|
548
472
|
// Broadcast click event
|
|
549
473
|
this.props.playerEvent("ad-tapped", nativeEvent);
|
|
550
474
|
this.props.listener?.onAdTapped?.(nativeEvent);
|
|
@@ -657,10 +581,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
657
581
|
}
|
|
658
582
|
|
|
659
583
|
if (!this.state.isAd && !this.onVideoLoadCalled) {
|
|
660
|
-
this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
|
|
661
|
-
player_type: this.props.playerPluginIdentifier,
|
|
662
|
-
});
|
|
663
|
-
|
|
664
584
|
// TODO: Hack, needs to remove
|
|
665
585
|
this.onVideoLoadCalled = true;
|
|
666
586
|
}
|
|
@@ -785,7 +705,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
785
705
|
|
|
786
706
|
render() {
|
|
787
707
|
const {
|
|
788
|
-
castAction,
|
|
789
708
|
docked,
|
|
790
709
|
entry,
|
|
791
710
|
fullscreen,
|
|
@@ -795,8 +714,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
795
714
|
isTabletPortrait,
|
|
796
715
|
muted,
|
|
797
716
|
style,
|
|
717
|
+
castAction,
|
|
718
|
+
controller,
|
|
719
|
+
startComponentsAnimation,
|
|
798
720
|
} = this.props;
|
|
799
721
|
|
|
722
|
+
const supportNativeCast = controller.supportNativeCast();
|
|
723
|
+
|
|
800
724
|
const { audioTrackId, keyEvent, paused, seek, textTrackId, rate } =
|
|
801
725
|
this.state;
|
|
802
726
|
|
|
@@ -858,10 +782,14 @@ export default class VideoPlayer extends React.Component<
|
|
|
858
782
|
!!this.props.controller?.supportsNativeControls?.();
|
|
859
783
|
|
|
860
784
|
const controls = isAppleTV()
|
|
861
|
-
? supportsNativeControls && !isInlineTV
|
|
785
|
+
? (supportsNativeControls && !isInlineTV) || !!this.getPlayNextData()
|
|
862
786
|
: supportsNativeControls && !this.getPlayNextData();
|
|
863
787
|
|
|
864
|
-
const shouldUseNativeControls =
|
|
788
|
+
const shouldUseNativeControls =
|
|
789
|
+
controls &&
|
|
790
|
+
!startComponentsAnimation &&
|
|
791
|
+
!docked &&
|
|
792
|
+
!castAction?.state?.connected;
|
|
865
793
|
|
|
866
794
|
platformSpecificProps = {
|
|
867
795
|
...platformSpecificProps,
|
|
@@ -920,8 +848,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
920
848
|
onPlayerResume: this.onPlayerResume, // play
|
|
921
849
|
onPlayerSeekComplete: this.onPlayerSeekComplete, // seek_complete
|
|
922
850
|
onPlayerSeekStart: this.onPlayerSeekStart, // seek_start
|
|
923
|
-
onPlayerSessionEnd: this.onPlayerSessionEnd, // session_end
|
|
924
|
-
onPlayerSessionStart: this.onPlayerSessionStart, // session_start
|
|
925
851
|
};
|
|
926
852
|
|
|
927
853
|
const lifecycleEvents = {
|
|
@@ -954,7 +880,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
954
880
|
onTimedMetadata: this.unHandledEvent("onTimedMetadata"),
|
|
955
881
|
onTracksChanged: this.onTracksChanged,
|
|
956
882
|
eventSubtitlesChange: this.unHandledEvent("eventSubtitlesChange"),
|
|
957
|
-
onVideoBandwidthUpdate: this.onVideoBandwidthUpdate,
|
|
958
883
|
};
|
|
959
884
|
|
|
960
885
|
const playerSystemEvents = {
|
|
@@ -974,9 +899,6 @@ export default class VideoPlayer extends React.Component<
|
|
|
974
899
|
const unsupportedEvents = {
|
|
975
900
|
onBufferComplete: this.onBufferComplete, // buffer_complete
|
|
976
901
|
onBufferStart: this.onBufferStart, // buffer_start
|
|
977
|
-
onPlayerNext: this.onPlayerNext, // next
|
|
978
|
-
onPlayerPrevious: this.onPlayerPrevious, // previous
|
|
979
|
-
onPlayerStop: this.onPlayerStop, // stop
|
|
980
902
|
};
|
|
981
903
|
|
|
982
904
|
const nativeEvents = {
|
|
@@ -990,7 +912,13 @@ export default class VideoPlayer extends React.Component<
|
|
|
990
912
|
...androidLifecyclePiPEvents,
|
|
991
913
|
};
|
|
992
914
|
|
|
993
|
-
const needToShowPlayNextOverlay =
|
|
915
|
+
const needToShowPlayNextOverlay =
|
|
916
|
+
isTV() && this.getPlayNextData() && !docked;
|
|
917
|
+
|
|
918
|
+
const needToShowDefaultControls = !(
|
|
919
|
+
(controls && !castAction?.state?.connected) ||
|
|
920
|
+
this.getPlayNextData()
|
|
921
|
+
);
|
|
994
922
|
|
|
995
923
|
const playerContainerStyles = isApplePlatform()
|
|
996
924
|
? styles.playerContainerBlack
|
|
@@ -1032,6 +960,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1032
960
|
extraInfoText={castAction?.state?.friendlyStatus}
|
|
1033
961
|
controlsAlwaysVisible={castAction?.state?.connected}
|
|
1034
962
|
playNextData={this.getPlayNextData()}
|
|
963
|
+
needToShowDefaultControls={needToShowDefaultControls}
|
|
1035
964
|
/>
|
|
1036
965
|
) : null;
|
|
1037
966
|
};
|
|
@@ -1066,16 +995,14 @@ export default class VideoPlayer extends React.Component<
|
|
|
1066
995
|
const renderMobilePlayer = () => {
|
|
1067
996
|
return isFullScreenAudioPlayer && isAudioItem ? (
|
|
1068
997
|
<View style={containerStyle}>
|
|
1069
|
-
{!castAction?.state?.connected && (
|
|
998
|
+
{(!castAction?.state?.connected || supportNativeCast) && (
|
|
1070
999
|
<this.props.PlayerComponent
|
|
1071
1000
|
ref={this._assignRoot}
|
|
1072
1001
|
{...nativeEvents}
|
|
1073
1002
|
{...nativeProps}
|
|
1074
1003
|
/>
|
|
1075
1004
|
)}
|
|
1076
|
-
<AudioPlayerWrapper {...sharedWrapperViewProps}
|
|
1077
|
-
<></>
|
|
1078
|
-
</AudioPlayerWrapper>
|
|
1005
|
+
<AudioPlayerWrapper {...sharedWrapperViewProps} />
|
|
1079
1006
|
</View>
|
|
1080
1007
|
) : (
|
|
1081
1008
|
<PlayerWrapper {...sharedWrapperViewProps}>
|
|
@@ -1119,7 +1046,7 @@ export default class VideoPlayer extends React.Component<
|
|
|
1119
1046
|
/>
|
|
1120
1047
|
)}
|
|
1121
1048
|
|
|
1122
|
-
{!castAction?.state?.connected && (
|
|
1049
|
+
{(!castAction?.state?.connected || supportNativeCast) && (
|
|
1123
1050
|
<AnimationComponent
|
|
1124
1051
|
animationType={ComponentAnimationType.player}
|
|
1125
1052
|
additionalData={playerDimensions}
|