@aller/blink
Version:
A library for tracking user behaviour.
133 lines (112 loc) • 3.37 kB
text/typescript
import {
PlayerStateEvent,
PlayerShownEvent,
PlayerHiddenEvent,
PlayerState,
} from '../reducers/player';
export interface GeneralVideoEvent {
time: Date;
videoId: string;
playerId: string;
muted: boolean;
volume: number;
position: number;
sticky?: boolean;
streamingMode?: string;
channelId?: string;
}
export interface VideoPlayEvent extends GeneralVideoEvent {
type: 'start';
reason: 'interaction' | 'autostart' | 'viewable';
}
export interface VideoStopEvent extends GeneralVideoEvent {
type: 'stop';
reason: 'pause' | 'complete' | 'exit' | 'viewable' | 'stickyClosed';
}
export type VideoTimeEvent = VideoPlayEvent | VideoStopEvent;
export type VideoEvent = PlayerStateEvent | VideoTimeEvent;
export enum PLAYER_TIMER_STATES {
RUNNING = 'RUNNING',
STOPPED = 'STOPPED',
}
export enum PLAYER_VISIBILITY_STATES {
VISIBLE = 'VISIBLE',
HIDDEN = 'HIDDEN',
}
export interface VideoWatchEvent {
watchTime: number;
startEvent: VideoTimeEvent | PlayerShownEvent;
stopEvent: VideoTimeEvent | PlayerHiddenEvent;
}
export function calculateVideoEventTime(
times: VideoTimeEvent[],
videoId: string,
players: PlayerState,
playerId: string,
time?: Date,
): VideoWatchEvent[] {
if (!times || times.length === 0) {
return [];
}
const forSpecificVideo = times.filter(
t => t.videoId === videoId && t.playerId === playerId,
);
const playerEvents = players[playerId] || [];
const events: VideoEvent[] = [...playerEvents, ...forSpecificVideo];
const filtered = filterValidEvents(events);
const result = filtered.reduce((all: any, curr, i, source) => {
// When a stop occurs, create a new event, and calculate time from start
if (curr.type === 'stop' || curr.type === 'hidden') {
const timestamp =
i > 0 ? curr.time.getTime() - source[i - 1].time.getTime() : 0;
all.push({
watchTime: timestamp,
startEvent: source[i - 1],
stopEvent: curr,
});
}
return all;
}, []);
return result;
}
function filterValidEvents(times: VideoEvent[]) {
let currentState = PLAYER_TIMER_STATES.STOPPED;
let currentVisibility = PLAYER_VISIBILITY_STATES.HIDDEN;
const sorted = times.sort((a, b) => a.time.getTime() - b.time.getTime());
return sorted.filter(t => {
if (currentState === PLAYER_TIMER_STATES.RUNNING) {
if (currentVisibility === PLAYER_VISIBILITY_STATES.VISIBLE) {
if (t.type === 'hidden') {
currentVisibility = PLAYER_VISIBILITY_STATES.HIDDEN;
return true;
}
if (t.type === 'stop') {
currentState = PLAYER_TIMER_STATES.STOPPED;
return true;
}
} else {
if (t.type === 'shown') {
currentVisibility = PLAYER_VISIBILITY_STATES.VISIBLE;
return true;
}
}
}
if (currentState === PLAYER_TIMER_STATES.STOPPED) {
if (currentVisibility === PLAYER_VISIBILITY_STATES.VISIBLE) {
if (t.type === 'start') {
currentState = PLAYER_TIMER_STATES.RUNNING;
return true;
}
} else {
if (t.type === 'shown') {
currentVisibility = PLAYER_VISIBILITY_STATES.VISIBLE;
return false;
}
if (t.type === 'start') {
currentState = PLAYER_TIMER_STATES.RUNNING;
return false;
}
}
}
});
}