@applicaster/zapp-react-native-utils 12.0.0-alpha.4960304100 → 12.0.0-alpha.5000126055
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/analyticsUtils/AnalyticPlayerListener.ts +155 -0
- package/analyticsUtils/AnalyticsEvents/index.ts +6 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +6 -0
- package/analyticsUtils/index.tsx +6 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +180 -0
- package/appUtils/playerManager/index.ts +9 -27
- package/appUtils/playerManager/player.ts +14 -5
- package/appUtils/playerManager/playerNative.ts +9 -3
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +23 -4
- package/cellUtils/__tests__/cellUtils.test.ts +9 -20
- package/cellUtils/index.ts +6 -12
- package/dateUtils/index.ts +11 -0
- package/headersUtils/index.ts +1 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/localizationUtils/__tests__/localizationUtils.test.ts +72 -53
- package/localizationUtils/index.ts +13 -22
- package/numberUtils/index.ts +4 -0
- package/package.json +2 -2
- package/reactHooks/cell-click/__tests__/index.test.js +5 -0
- package/reactHooks/cell-click/index.ts +2 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/navigation/__mocks__/index.ts +13 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +11 -8
- package/reactHooks/navigation/index.ts +21 -65
- package/reactHooks/player/__tests__/useAutoSeek.test.tsx +8 -1
- package/reactHooks/player/useAutoSeek.tsx +28 -8
- package/reactHooks/player/useSeek.tsx +4 -2
- package/reactHooks/player/useTapSeek.tsx +34 -7
- package/reactHooks/screen/useScreenContext.ts +4 -3
- package/screenState/index.ts +0 -8
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +24 -22
- package/types.ts +4 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { PlayerAnalyticsTracker } from "./playerAnalyticsTracker";
|
|
2
|
+
import { Player } from "../appUtils/playerManager/player";
|
|
3
|
+
import { AD_EVENT, GENERAL_EVENT, PLAYBACK_EVENT, QOS_EVENT } from "./events";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
analyticsTracker: any;
|
|
7
|
+
player: any;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/* eslint brace-style: 0 */ // --> OFF
|
|
11
|
+
export class AnalyticPlayerListener
|
|
12
|
+
implements QuickBrickPlayer.SharedPlayerCallBacks
|
|
13
|
+
{
|
|
14
|
+
static analyticsListenerID = "analytics-listener";
|
|
15
|
+
private analyticsTracker: PlayerAnalyticsTracker;
|
|
16
|
+
private player: Player;
|
|
17
|
+
|
|
18
|
+
private onVideoLoadCalled; // android only
|
|
19
|
+
|
|
20
|
+
constructor(props: Props) {
|
|
21
|
+
this.analyticsTracker = props.analyticsTracker;
|
|
22
|
+
this.player = props.player;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
handleAnalyticEvent = (event, data = {}) => {
|
|
26
|
+
if (this.analyticsTracker?.handleAnalyticEvent) {
|
|
27
|
+
this.analyticsTracker?.handleAnalyticEvent?.(event, {
|
|
28
|
+
...data,
|
|
29
|
+
player_type: this.player.playerPluginId,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
onVideoEnd = () => {
|
|
35
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
onError = (err: Error) => {
|
|
39
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.error, err); // TODO: Check error format
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// HACK: This is a hack to ignore the next resume event after a delayed seek
|
|
43
|
+
private ignoreNextResume = false;
|
|
44
|
+
onLoad = () => {
|
|
45
|
+
this.ignoreNextResume = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onPlayerPause = (event) => {
|
|
49
|
+
if (!this.player.isInDelayedSeek()) {
|
|
50
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.pause, event);
|
|
51
|
+
} else {
|
|
52
|
+
this.ignoreNextResume = true;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
onPlayerResume = (event) => {
|
|
57
|
+
if (!this.player.isInDelayedSeek() && !this.ignoreNextResume) {
|
|
58
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.play, event);
|
|
59
|
+
} else {
|
|
60
|
+
this.ignoreNextResume = false;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
onPlayerSeekComplete = (event) => {
|
|
65
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, event);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
onPlayerSeekStart = (event) => {
|
|
69
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, event);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
onBufferStart = (event) => {
|
|
73
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, event);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
onBufferComplete = (event) => {
|
|
77
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, event);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Not implemented
|
|
81
|
+
onPlayerStop = (event) => {
|
|
82
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.stop, event);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// TODO: Implement method on native
|
|
86
|
+
onVideoBandwidthUpdate = (event) => {
|
|
87
|
+
this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, event);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
onAdBegin = (event) => {
|
|
91
|
+
this.handleAnalyticEvent(AD_EVENT.ad_start, event);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
onAdBreakBegin = (event) => {
|
|
95
|
+
this.handleAnalyticEvent(AD_EVENT.ad_break_start, event);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
onAdBreakEnd = (event) => {
|
|
99
|
+
this.handleAnalyticEvent(AD_EVENT.ad_break_completed, event);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
onAdEnd = (event) => {
|
|
103
|
+
this.handleAnalyticEvent(AD_EVENT.ad_complete, event);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
onAdError = (event) => {
|
|
107
|
+
this.handleAnalyticEvent(AD_EVENT.ad_error, event);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
onAdRequest = (event) => {
|
|
111
|
+
this.handleAnalyticEvent(AD_EVENT.ad_request, event);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
onAdClicked = (event) => {
|
|
115
|
+
this.handleAnalyticEvent(AD_EVENT.ad_clicked, event);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
onAdTapped = (event) => {
|
|
119
|
+
this.handleAnalyticEvent(AD_EVENT.ad_tapped, event);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
onVideoLoad = () => {
|
|
123
|
+
// if (!this.state.isAd && !this.onVideoLoadCalled) {
|
|
124
|
+
if (!this.player.isAd() && !this.onVideoLoadCalled) {
|
|
125
|
+
this.onVideoLoadCalled = true;
|
|
126
|
+
|
|
127
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
|
|
128
|
+
player_type: this.player.playerPluginId,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
onPlayerSessionEnd = () => {
|
|
134
|
+
// TODO: Implement method on native android, ios exists
|
|
135
|
+
this.handleAnalyticEvent(GENERAL_EVENT.session_end);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
onPlayerSessionStart = () => {
|
|
139
|
+
// TODO: Implement method on native android, ios exists
|
|
140
|
+
|
|
141
|
+
this.handleAnalyticEvent(GENERAL_EVENT.session_start);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
onPlayerNext = ({ nativeEvent }) => {
|
|
145
|
+
// TODO: Implement method on native
|
|
146
|
+
|
|
147
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
onPlayerPrevious = ({ nativeEvent }) => {
|
|
151
|
+
// TODO: Implement method on native
|
|
152
|
+
|
|
153
|
+
this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
@@ -7,6 +7,10 @@ import { sendHeaderClickEvent } from "./sendHeaderClickEvent";
|
|
|
7
7
|
import { sendHardwareBackButtonClickEvent } from "./sendHardwareBackButtonClickEvent";
|
|
8
8
|
import { sendDownloadsEvent } from "./sendDownloadsEvent";
|
|
9
9
|
import { sendScreenEvent } from "./sendScreenEvent";
|
|
10
|
+
import {
|
|
11
|
+
sendSearchCanceledEvent,
|
|
12
|
+
sendSearchExecutedEvent,
|
|
13
|
+
} from "./searchEvents";
|
|
10
14
|
|
|
11
15
|
export {
|
|
12
16
|
sendScreenEvent,
|
|
@@ -18,4 +22,6 @@ export {
|
|
|
18
22
|
sendHeaderClickEvent,
|
|
19
23
|
sendHardwareBackButtonClickEvent,
|
|
20
24
|
sendDownloadsEvent,
|
|
25
|
+
sendSearchCanceledEvent,
|
|
26
|
+
sendSearchExecutedEvent,
|
|
21
27
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { log_debug } from "../logger";
|
|
2
|
+
import { postAnalyticEvent } from "../manager";
|
|
3
|
+
import { ANALYTICS_CORE_EVENTS } from "../events";
|
|
4
|
+
|
|
5
|
+
export const sendSearchCanceledEvent = (analyticsProps) => {
|
|
6
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_CANCELED;
|
|
7
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
8
|
+
|
|
9
|
+
log_debug(`sendSearchCanceledEvent: send event: ${eventName}`, {
|
|
10
|
+
analyticsProps,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const sendSearchExecutedEvent = (analyticsProps) => {
|
|
15
|
+
const eventName = ANALYTICS_CORE_EVENTS.SEARCH_EXECUTED;
|
|
16
|
+
postAnalyticEvent(eventName, analyticsProps);
|
|
17
|
+
|
|
18
|
+
log_debug(`sendSearchExecutedEvent: send event: ${eventName}`, {
|
|
19
|
+
analyticsProps,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PlayerAnalyticsTracker,
|
|
3
|
+
PlayerAnalyticsTrackerI,
|
|
4
|
+
} from "./playerAnalyticsTracker";
|
|
5
|
+
import {
|
|
6
|
+
PlayerLifecycleListener,
|
|
7
|
+
PlayerManager,
|
|
8
|
+
} from "../appUtils/playerManager";
|
|
9
|
+
import { Player } from "../appUtils/playerManager/player";
|
|
10
|
+
import { AnalyticPlayerListener } from "./AnalyticPlayerListener";
|
|
11
|
+
import { PlayerRole } from "../appUtils/playerManager/conts";
|
|
12
|
+
|
|
13
|
+
enum PlayerAnalyticsTrackerType {
|
|
14
|
+
PlayerAnalyticsTracker = "PlayerAnalyticsTracker",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class PlayerAnalyticsTrackerFactory {
|
|
18
|
+
createTracker(type: PlayerAnalyticsTrackerType) {
|
|
19
|
+
switch (type) {
|
|
20
|
+
case PlayerAnalyticsTrackerType.PlayerAnalyticsTracker:
|
|
21
|
+
return new PlayerAnalyticsTracker();
|
|
22
|
+
default:
|
|
23
|
+
return new PlayerAnalyticsTracker();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class PlayerAnalyticsManager implements PlayerLifecycleListener {
|
|
29
|
+
static instance: PlayerAnalyticsManager;
|
|
30
|
+
private trackers: Record<string, PlayerAnalyticsTrackerI> = {};
|
|
31
|
+
private trackerFactory: PlayerAnalyticsTrackerFactory;
|
|
32
|
+
|
|
33
|
+
public getTracker(
|
|
34
|
+
id: string,
|
|
35
|
+
type: PlayerAnalyticsTrackerType = PlayerAnalyticsTrackerType.PlayerAnalyticsTracker
|
|
36
|
+
): PlayerAnalyticsTrackerI {
|
|
37
|
+
if (!this.trackers[id]) {
|
|
38
|
+
this.trackers[id] = this.trackerFactory.createTracker(type);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return this.trackers[id];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
constructor(trackerFactory: PlayerAnalyticsTrackerFactory) {
|
|
45
|
+
this.trackerFactory = trackerFactory;
|
|
46
|
+
this.startListenPlayerManager();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static getInstance(
|
|
50
|
+
trackerFactory: PlayerAnalyticsTrackerFactory = new PlayerAnalyticsTrackerFactory()
|
|
51
|
+
): PlayerAnalyticsManager {
|
|
52
|
+
if (!PlayerAnalyticsManager.instance) {
|
|
53
|
+
PlayerAnalyticsManager.instance = new PlayerAnalyticsManager(
|
|
54
|
+
trackerFactory
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return PlayerAnalyticsManager.instance;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private startListenPlayerManager() {
|
|
62
|
+
PlayerManager.getInstance().addLifecycleListener(this);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// PlayerLifecycleListener
|
|
66
|
+
|
|
67
|
+
onRegistered = (player: Player) => {
|
|
68
|
+
if (player.playerRole !== PlayerRole.Primary) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const tracker = this.getTracker(player.playerId);
|
|
73
|
+
tracker.setPlayer(player);
|
|
74
|
+
|
|
75
|
+
const analyticsListener = new AnalyticPlayerListener({
|
|
76
|
+
analyticsTracker: tracker,
|
|
77
|
+
player,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
player.addListener({
|
|
81
|
+
id: AnalyticPlayerListener.analyticsListenerID,
|
|
82
|
+
listener: analyticsListener,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
onUnRegistered = (player: Player) => {
|
|
87
|
+
player.removeListener(AnalyticPlayerListener.analyticsListenerID);
|
|
88
|
+
delete this.trackers[player.playerId];
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
3
|
+
|
|
4
|
+
export const getVideojsAnalyticPluginsConfig = async (entry: ZappEntry) => {
|
|
5
|
+
let videojsConfiguration = {};
|
|
6
|
+
|
|
7
|
+
const plugins = appStore.get("plugins");
|
|
8
|
+
const analyticPlugins = R.filter(R.propEq("type", "analytics"), plugins);
|
|
9
|
+
|
|
10
|
+
const configurations = await Promise.all(
|
|
11
|
+
analyticPlugins.map(async (plugin) => {
|
|
12
|
+
const { configuration, module } = plugin;
|
|
13
|
+
|
|
14
|
+
return module?.getPlayerConfiguration?.(configuration, entry);
|
|
15
|
+
})
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
configurations.forEach((analyticConfig) => {
|
|
19
|
+
if (typeof analyticConfig === "object" && analyticConfig !== null) {
|
|
20
|
+
videojsConfiguration = { ...videojsConfiguration, ...analyticConfig };
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return videojsConfiguration;
|
|
25
|
+
};
|
package/analyticsUtils/events.ts
CHANGED
|
@@ -23,10 +23,16 @@ export const TAPPING_EVENTS = {
|
|
|
23
23
|
TAP_NAVBAR_BACK_BUTTON: "tap_navbar_back_button",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
export const SEARCH_EVENTS = {
|
|
27
|
+
SEARCH_EXECUTED: "search_executed",
|
|
28
|
+
SEARCH_CANCELED: "search_canceled",
|
|
29
|
+
};
|
|
30
|
+
|
|
26
31
|
export const ANALYTICS_CORE_EVENTS = {
|
|
27
32
|
...LEGACY_EVENTS,
|
|
28
33
|
...SCREEN_VIEW_EVENTS,
|
|
29
34
|
...TAPPING_EVENTS,
|
|
35
|
+
...SEARCH_EVENTS,
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
export const WEBVIEW_EVENTS = {
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
sendHardwareBackButtonClickEvent,
|
|
19
19
|
sendDownloadsEvent,
|
|
20
20
|
sendScreenEvent,
|
|
21
|
+
sendSearchCanceledEvent,
|
|
22
|
+
sendSearchExecutedEvent,
|
|
21
23
|
} from "./AnalyticsEvents";
|
|
22
24
|
|
|
23
25
|
import { postAnalyticEvent } from "./manager";
|
|
@@ -66,6 +68,8 @@ type AnalyticsFunctions = {
|
|
|
66
68
|
item: ZappEntry;
|
|
67
69
|
state: OfflineItemState;
|
|
68
70
|
}) => void;
|
|
71
|
+
sendSearchCanceledEvent: (props: AnalyticsDefaultProperties) => void;
|
|
72
|
+
sendSearchExecutedEvent: (props: AnalyticsDefaultProperties) => void;
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
type GetAnalyticsFunctions = (
|
|
@@ -102,6 +106,8 @@ export function getAnalyticsFunctions({
|
|
|
102
106
|
sendHardwareBackButtonClickEvent,
|
|
103
107
|
sendScreenEvent,
|
|
104
108
|
sendDownloadsEvent,
|
|
109
|
+
sendSearchCanceledEvent,
|
|
110
|
+
sendSearchExecutedEvent,
|
|
105
111
|
};
|
|
106
112
|
}
|
|
107
113
|
|
|
@@ -5,16 +5,25 @@ import { postAnalyticEvent } from "./manager";
|
|
|
5
5
|
import { isLive } from "../playerUtils";
|
|
6
6
|
import { extensionsEvents } from "./AnalyticsEvents/helper";
|
|
7
7
|
import { AD_EVENT, EVENT_TYPES, PLAYER_DISPLAY_STATES } from "./events";
|
|
8
|
+
import { Player } from "../appUtils/playerManager/player";
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
+
export interface PlayerAnalyticsTrackerI {
|
|
11
|
+
handleAnalyticEvent(
|
|
12
|
+
event: QuickBrickPlayer.Events,
|
|
13
|
+
eventData?: QuickBrickPlayer.EventData
|
|
14
|
+
): void;
|
|
15
|
+
setPlayer(player: Player): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
|
|
10
19
|
adTime: number | string;
|
|
11
20
|
mediaTime: number | string;
|
|
12
|
-
entry: ZappEntry;
|
|
13
21
|
eventType: string;
|
|
14
22
|
playerState: QuickBrickPlayer.NativePlayerState;
|
|
23
|
+
private player: Player;
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
this.
|
|
25
|
+
setPlayer(player: Player) {
|
|
26
|
+
this.player = player;
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
isAdBreak(event: string): boolean {
|
|
@@ -30,6 +39,10 @@ export class AnalyticsTracker {
|
|
|
30
39
|
return !!isAdBreak;
|
|
31
40
|
}
|
|
32
41
|
|
|
42
|
+
get entry(): ZappEntry {
|
|
43
|
+
return this.player?.getEntry();
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
getDuration(
|
|
34
47
|
event: QuickBrickPlayer.Events,
|
|
35
48
|
eventData: QuickBrickPlayer.EventData
|
|
@@ -215,13 +228,9 @@ export class AnalyticsTracker {
|
|
|
215
228
|
|
|
216
229
|
handleAnalyticEvent(
|
|
217
230
|
event: QuickBrickPlayer.Events,
|
|
218
|
-
eventData: QuickBrickPlayer.EventData = {}
|
|
219
|
-
entry: ZappEntry,
|
|
220
|
-
playerState = null
|
|
231
|
+
eventData: QuickBrickPlayer.EventData = {}
|
|
221
232
|
) {
|
|
222
|
-
this.
|
|
223
|
-
this.playerState = playerState;
|
|
224
|
-
|
|
233
|
+
this.playerState = this.player?.getState();
|
|
225
234
|
postAnalyticEvent(event, this.prepareAnalyticPayload(event, eventData));
|
|
226
235
|
}
|
|
227
236
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Player } from "./player";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
|
|
4
|
+
export class DelayedSeekPlayerWrapper extends Player {
|
|
5
|
+
private wrappedPlayer: Player;
|
|
6
|
+
private delayedSeekPosition: number;
|
|
7
|
+
private wasPlaying: boolean;
|
|
8
|
+
|
|
9
|
+
constructor(props, player: Player) {
|
|
10
|
+
super(props);
|
|
11
|
+
this.wrappedPlayer = player;
|
|
12
|
+
this.delayedSeekPosition = null;
|
|
13
|
+
|
|
14
|
+
this.wrappedPlayer.addListener({
|
|
15
|
+
id: "Master-Wrapper-Listener",
|
|
16
|
+
listener: this,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
this.onPlayerSeekComplete = this.onPlayerSeekComplete.bind(this);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getOverlayObservable = () => {
|
|
23
|
+
return this.wrappedPlayer.getOverlayObservable();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
getEntryObservable = (): BehaviorSubject<ZappEntry | null> => {
|
|
27
|
+
return this.wrappedPlayer.getEntryObservable();
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
getEntry = (): ZappEntry | null => this.wrappedPlayer.getEntry();
|
|
31
|
+
|
|
32
|
+
getListener = (): QuickBrickPlayer.SharedPlayerCallBacks => {
|
|
33
|
+
return this.wrappedPlayer.getListener();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
togglePlayPause = () => {
|
|
37
|
+
this.wrappedPlayer.togglePlayPause();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
selectTrack = (_track) => this.wrappedPlayer.selectTrack(_track);
|
|
41
|
+
|
|
42
|
+
// Implement all default methods by delegating them to wrapped player
|
|
43
|
+
public seekTo = (position: number) => {
|
|
44
|
+
this.wrappedPlayer.seekTo(position);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
public isPlaying = () => {
|
|
48
|
+
return this.wrappedPlayer.isPlaying();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
public isPaused = () => {
|
|
52
|
+
return this.wrappedPlayer.isPaused();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
public play = () => {
|
|
56
|
+
this.wrappedPlayer.play();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
public pause = () => {
|
|
60
|
+
this.wrappedPlayer.pause();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
getState = () => {
|
|
64
|
+
const currentState = this.wrappedPlayer.getState();
|
|
65
|
+
|
|
66
|
+
if (this.delayedSeekPosition === null) {
|
|
67
|
+
return currentState;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...currentState,
|
|
72
|
+
contentPosition: this.delayedSeekPosition,
|
|
73
|
+
isPaused: false,
|
|
74
|
+
seekPosition: this.delayedSeekPosition,
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
forward = (deltaTime: number) => {
|
|
79
|
+
if (this.delayedSeekPosition !== null) {
|
|
80
|
+
this.delayedSeekPosition += deltaTime;
|
|
81
|
+
this.playerState.seekPosition = this.delayedSeekPosition;
|
|
82
|
+
|
|
83
|
+
this.onVideoProgress({
|
|
84
|
+
currentTime: this.playerState.seekPosition,
|
|
85
|
+
duration: this.getDuration(),
|
|
86
|
+
seekableDuration: this.playerState.seekableDuration,
|
|
87
|
+
isLive: this.playerState.isLive,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// this.scheduleResumePlayback(DELAYED_SEEK_DELAY, this.delayedSeekPosition);
|
|
91
|
+
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (this.playerState.seekPosition == null) {
|
|
96
|
+
this.playerState.seekPosition = this.playerState.contentPosition;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.playerState.seekPosition += deltaTime;
|
|
100
|
+
|
|
101
|
+
this.wrappedPlayer.forward(deltaTime);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
rewind = (deltaTime: number) => {
|
|
105
|
+
if (this.delayedSeekPosition !== null) {
|
|
106
|
+
this.delayedSeekPosition -= deltaTime;
|
|
107
|
+
|
|
108
|
+
if (this.delayedSeekPosition < 0) {
|
|
109
|
+
this.delayedSeekPosition = 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.playerState.seekPosition = this.delayedSeekPosition;
|
|
113
|
+
|
|
114
|
+
this.onVideoProgress({
|
|
115
|
+
currentTime: this.playerState.seekPosition,
|
|
116
|
+
duration: this.getDuration(),
|
|
117
|
+
seekableDuration: this.playerState.seekableDuration,
|
|
118
|
+
isLive: this.playerState.isLive,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// this.scheduleResumePlayback(DELAYED_SEEK_DELAY, this.delayedSeekPosition);
|
|
122
|
+
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.playerState.seekPosition == null) {
|
|
127
|
+
this.playerState.seekPosition = this.playerState.contentPosition;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.playerState.seekPosition -= deltaTime;
|
|
131
|
+
this.wrappedPlayer.rewind(deltaTime);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
public getConfig = () => this.wrappedPlayer.getConfig();
|
|
135
|
+
|
|
136
|
+
// TODO: replace with enum of supported orientations
|
|
137
|
+
isFullScreenSupported(): boolean {
|
|
138
|
+
return this.wrappedPlayer.isFullScreenSupported();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public supportsNativeControls = (): boolean =>
|
|
142
|
+
this.wrappedPlayer.supportsNativeControls();
|
|
143
|
+
|
|
144
|
+
public supportNativeCast = (): boolean =>
|
|
145
|
+
this.wrappedPlayer.supportNativeCast();
|
|
146
|
+
|
|
147
|
+
public isInDelayedSeek = () => this.delayedSeekPosition !== null;
|
|
148
|
+
|
|
149
|
+
public enterDelayedSeek() {
|
|
150
|
+
if (this.delayedSeekPosition !== null) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.playerState.seekPosition === null) {
|
|
155
|
+
this.playerState.seekPosition = this.playerState.contentPosition;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.delayedSeekPosition = this.playerState.seekPosition;
|
|
159
|
+
|
|
160
|
+
if (!this.isPaused()) {
|
|
161
|
+
this.wasPlaying = true;
|
|
162
|
+
this.wrappedPlayer.pause();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public exitDelayedSeek() {
|
|
167
|
+
if (this.delayedSeekPosition === null) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.wrappedPlayer.seekTo(this.delayedSeekPosition);
|
|
172
|
+
|
|
173
|
+
if (this.wasPlaying) {
|
|
174
|
+
this.wasPlaying = false;
|
|
175
|
+
this.wrappedPlayer.play();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this.delayedSeekPosition = null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -3,7 +3,6 @@ import * as R from "ramda";
|
|
|
3
3
|
import { subscriber } from "../../functionUtils";
|
|
4
4
|
import { utilsLogger, createLogger } from "../../logger";
|
|
5
5
|
import { Player } from "./player";
|
|
6
|
-
import { AnalyticsTracker } from "../../analyticsUtils/analyticsTracker";
|
|
7
6
|
import {
|
|
8
7
|
hasTizenAPIs,
|
|
9
8
|
subscribeToDeviceNetworkChanges,
|
|
@@ -36,7 +35,10 @@ export interface PlayerLifecycleListener {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
type PlayersMap = {
|
|
39
|
-
[id: string]: {
|
|
38
|
+
[id: string]: {
|
|
39
|
+
player: Player;
|
|
40
|
+
isInViewport: boolean;
|
|
41
|
+
};
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
export class PlayerManager {
|
|
@@ -45,7 +47,6 @@ export class PlayerManager {
|
|
|
45
47
|
private playersMap: PlayersMap = {};
|
|
46
48
|
private manager = subscriber();
|
|
47
49
|
private castingReceiver: Player;
|
|
48
|
-
private analyticsTracker: AnalyticsTracker;
|
|
49
50
|
private lifecycleListeners: PlayerLifecycleListener[] = [];
|
|
50
51
|
|
|
51
52
|
public static getInstance() {
|
|
@@ -56,7 +57,7 @@ export class PlayerManager {
|
|
|
56
57
|
return PlayerManager.instance;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
public getActivePlayerId = ():
|
|
60
|
+
public getActivePlayerId = (): string | null => {
|
|
60
61
|
return this.activePlayerId;
|
|
61
62
|
};
|
|
62
63
|
|
|
@@ -101,7 +102,10 @@ export class PlayerManager {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
private addPlayer = (id: string, player: Player) => {
|
|
104
|
-
this.playersMap[id] = {
|
|
105
|
+
this.playersMap[id] = {
|
|
106
|
+
player: player,
|
|
107
|
+
isInViewport: false,
|
|
108
|
+
};
|
|
105
109
|
};
|
|
106
110
|
|
|
107
111
|
public isPlayerRegistered = (id: string) => !!this.playersMap[id];
|
|
@@ -246,28 +250,6 @@ export class PlayerManager {
|
|
|
246
250
|
this.castingReceiver = null;
|
|
247
251
|
}
|
|
248
252
|
|
|
249
|
-
/**
|
|
250
|
-
* Handles analytic events, generates a new analytics tracker and transmits events
|
|
251
|
-
* Passes entry data, and playerState to the tracker for additional context
|
|
252
|
-
* The end result is an event formatted and passed to AnalyticsBridge.postEvent
|
|
253
|
-
* When we get the synthetic event we need to pull the data in the e object
|
|
254
|
-
* e contains the return value from native
|
|
255
|
-
*/
|
|
256
|
-
public handleAnalyticEvent(event, data = {}, entry: ZappEntry) {
|
|
257
|
-
const playerState = this.getActivePlayer()?.getState();
|
|
258
|
-
|
|
259
|
-
if (!this.analyticsTracker?.handleAnalyticEvent) {
|
|
260
|
-
this.analyticsTracker = new AnalyticsTracker(entry);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
this.analyticsTracker?.handleAnalyticEvent?.(
|
|
264
|
-
event,
|
|
265
|
-
data,
|
|
266
|
-
entry,
|
|
267
|
-
playerState
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
253
|
/**
|
|
272
254
|
* Method retrieves the instantiated player instance controller
|
|
273
255
|
* Instance controller exposes methods like getContentDuration, getContentPosision
|