049fb2878e
* recsys wip better logging fix floating player popout playing uri bug with recsys lint add empty entries to create use beacon; fire on visibilitychange cleanup, not record recs if not seen ifweb recsys beacon recsys handle embeds, cleanup use history.listen to trigger events fix recsys embed bug bugfix more default data cleaner cleaner * remove tentative * disable recsys debug logging
178 lines
4.8 KiB
JavaScript
178 lines
4.8 KiB
JavaScript
// Created by xander on 6/21/2021
|
|
import videojs from 'video.js';
|
|
|
|
import RecSys from 'recsys';
|
|
const VERSION = '0.0.1';
|
|
|
|
/* RecSys */
|
|
const PlayerEvent = {
|
|
event: {
|
|
start: 0, // event types
|
|
stop: 1,
|
|
scrub: 2,
|
|
speed: 3,
|
|
},
|
|
};
|
|
|
|
function newRecsysPlayerEvent(eventType, offset, arg) {
|
|
if (arg) {
|
|
return {
|
|
event: eventType,
|
|
offset: offset,
|
|
arg: arg,
|
|
};
|
|
} else {
|
|
return {
|
|
event: eventType,
|
|
offset: offset,
|
|
};
|
|
}
|
|
}
|
|
|
|
const defaults = {
|
|
videoId: null,
|
|
userId: 0,
|
|
debug: false,
|
|
embedded: false,
|
|
};
|
|
|
|
const Component = videojs.getComponent('Component');
|
|
const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
|
|
|
class RecsysPlugin extends Component {
|
|
constructor(player, options) {
|
|
super(player, options);
|
|
|
|
// Plugin started
|
|
if (options.debug) {
|
|
this.log(`Created recsys plugin for: videoId:${options.videoId}`);
|
|
}
|
|
|
|
// To help with debugging, we'll add a global vjs object with the video js player
|
|
window.vjs = player;
|
|
|
|
this.player = player;
|
|
this.lastTimeUpdate = null;
|
|
this.currentTimeUpdate = null;
|
|
this.inPause = false;
|
|
|
|
// Plugin event listeners
|
|
player.on('playing', (event) => this.onPlay(event));
|
|
player.on('pause', (event) => this.onPause(event));
|
|
player.on('ended', (event) => this.onEnded(event));
|
|
player.on('ratechange', (event) => this.onRateChange(event));
|
|
player.on('timeupdate', (event) => this.onTimeUpdate(event));
|
|
player.on('seeked', (event) => this.onSeeked(event));
|
|
|
|
// Event trigger to send recsys event
|
|
player.on('dispose', (event) => this.onDispose(event));
|
|
}
|
|
|
|
onPlay(event) {
|
|
const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.start, this.player.currentTime());
|
|
this.log('onPlay', recsysEvent);
|
|
RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent, this.options_.embedded);
|
|
|
|
this.inPause = false;
|
|
this.lastTimeUpdate = recsysEvent.offset;
|
|
}
|
|
|
|
onPause(event) {
|
|
const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.stop, this.player.currentTime());
|
|
this.log('onPause', recsysEvent);
|
|
RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
|
|
|
|
this.inPause = true;
|
|
}
|
|
|
|
onEnded(event) {
|
|
const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.stop, this.player.currentTime());
|
|
this.log('onEnded', recsysEvent);
|
|
RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
|
|
}
|
|
|
|
onRateChange(event) {
|
|
const recsysEvent = newRecsysPlayerEvent(
|
|
PlayerEvent.event.speed,
|
|
this.player.currentTime(),
|
|
this.player.playbackRate()
|
|
);
|
|
this.log('onRateChange', recsysEvent);
|
|
RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
|
|
}
|
|
|
|
onTimeUpdate(event) {
|
|
const nextCurrentTime = this.player.currentTime();
|
|
|
|
if (!this.inPause && Math.abs(this.lastTimeUpdate - nextCurrentTime) < 0.5) {
|
|
// Don't update lastTimeUpdate if we are in a pause segment.
|
|
//
|
|
// However, if we aren't in a pause and the time jumped
|
|
// the onTimeUpdate event probably fired before the pause and seek.
|
|
// Don't update in that case, either.
|
|
this.lastTimeUpdate = this.currentTimeUpdate;
|
|
}
|
|
|
|
this.currentTimeUpdate = nextCurrentTime;
|
|
}
|
|
|
|
onSeeked(event) {
|
|
const curTime = this.player.currentTime();
|
|
|
|
// There are three patterns for seeking:
|
|
//
|
|
// Assuming the video is playing,
|
|
//
|
|
// 1. Dragging the player head emits: onPause -> onSeeked -> onSeeked -> ... -> onPlay
|
|
// 2. Key press left right emits: onSeeked -> onPlay
|
|
// 3. Clicking a position emits: onPause -> onSeeked -> onPlay
|
|
//
|
|
// If the video is NOT playing,
|
|
//
|
|
// 1. Dragging the player head emits: onSeeked
|
|
// 2. Key press left right emits: onSeeked
|
|
// 3. Clicking a position emits: onSeeked
|
|
const fromTime = this.lastTimeUpdate;
|
|
|
|
if (fromTime !== curTime) {
|
|
// This removes duplicates that aren't useful.
|
|
const recsysEvent = newRecsysPlayerEvent(PlayerEvent.event.scrub, fromTime, curTime);
|
|
this.log('onSeeked', recsysEvent);
|
|
RecSys.onRecsysPlayerEvent(this.options_.videoId, recsysEvent);
|
|
}
|
|
}
|
|
|
|
onDispose(event) {
|
|
RecSys.onPlayerDispose(this.options_.videoId, this.options_.embedded);
|
|
}
|
|
|
|
log(...args) {
|
|
if (this.options_.debug) {
|
|
console.log(`Recsys Player Debug:`, JSON.stringify(args));
|
|
}
|
|
}
|
|
}
|
|
|
|
videojs.registerComponent('recsys', RecsysPlugin);
|
|
|
|
const onPlayerReady = (player, options) => {
|
|
player.recsys = new RecsysPlugin(player, options);
|
|
};
|
|
|
|
/**
|
|
* Initialize the plugin.
|
|
*
|
|
* @function plugin
|
|
* @param {Object} [options={}]
|
|
*/
|
|
const plugin = function (options) {
|
|
this.ready(() => {
|
|
onPlayerReady(this, videojs.mergeOptions(defaults, options));
|
|
});
|
|
};
|
|
|
|
plugin.VERSION = VERSION;
|
|
|
|
registerPlugin('recsys', plugin);
|
|
|
|
export default plugin;
|