Create plugin to hold code for recsys
This commit is contained in:
parent
153eae1a41
commit
d781dead32
4 changed files with 158 additions and 54 deletions
|
@ -9,13 +9,14 @@ import { withRouter } from 'react-router';
|
||||||
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
||||||
import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
||||||
import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings';
|
import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const { search } = props.location;
|
const { search } = props.location;
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const autoplay = urlParams.get('autoplay');
|
const autoplay = urlParams.get('autoplay');
|
||||||
const position = urlParams.get('t') !== null ? urlParams.get('t') : makeSelectContentPositionForUri(props.uri)(state);
|
const position = urlParams.get('t') !== null ? urlParams.get('t') : makeSelectContentPositionForUri(props.uri)(state);
|
||||||
|
const userId = selectUser(state).id;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
autoplayIfEmbedded: Boolean(autoplay),
|
autoplayIfEmbedded: Boolean(autoplay),
|
||||||
|
@ -29,6 +30,7 @@ const select = (state, props) => {
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
homepageData: selectHomepageData(state),
|
homepageData: selectHomepageData(state),
|
||||||
authenticated: selectUserVerifiedEmail(state),
|
authenticated: selectUserVerifiedEmail(state),
|
||||||
|
userId: userId,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
// Created by xander on 6/21/2021
|
||||||
|
import videojs from 'video.js/dist/video.min.js';
|
||||||
|
const VERSION = '0.0.1';
|
||||||
|
|
||||||
|
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
|
||||||
|
const recsysId = 'lighthouse-v0';
|
||||||
|
|
||||||
|
/* RecSys */
|
||||||
|
const RecsysData = {
|
||||||
|
event: {
|
||||||
|
start: 0,
|
||||||
|
stop: 1,
|
||||||
|
scrub: 2,
|
||||||
|
speed: 3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function newRecsysEvent(eventType, offset, arg) {
|
||||||
|
if (arg) {
|
||||||
|
return {
|
||||||
|
event: eventType,
|
||||||
|
offset: offset,
|
||||||
|
arg: arg,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
event: eventType,
|
||||||
|
offset: offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendRecsysEvent(recsysEvent) {
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(recsysEvent),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fetch(recsysEndpoint, requestOptions)
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
console.log(`Recsys response data:`, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaults = {
|
||||||
|
endpoint: recsysEndpoint,
|
||||||
|
recsysId: recsysId,
|
||||||
|
videoId: '0',
|
||||||
|
userId: '0',
|
||||||
|
debug: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Component = videojs.getComponent('Component');
|
||||||
|
const registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||||
|
|
||||||
|
class RecsysPlugin extends Component {
|
||||||
|
constructor(player, options) {
|
||||||
|
super(player, options);
|
||||||
|
|
||||||
|
// Plugin started
|
||||||
|
console.log(`created recsys plugin for: videoId:${options.videoId}, userId:${options.userId}`);
|
||||||
|
|
||||||
|
// To help with debugging, we'll add a global vjs object with the video js player
|
||||||
|
window.vjs = player;
|
||||||
|
|
||||||
|
this.player = player;
|
||||||
|
|
||||||
|
// 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('seeking', (event) => this.onSeeking(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
onPlay(event) {
|
||||||
|
const recsysEvent = newRecsysEvent(RecsysData.event.start, this.player.currentTime());
|
||||||
|
this.log('onPlay', recsysEvent);
|
||||||
|
sendRecsysEvent(recsysEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPause(event) {
|
||||||
|
const recsysEvent = newRecsysEvent(RecsysData.event.stop, this.player.currentTime());
|
||||||
|
this.log('onPause', recsysEvent);
|
||||||
|
sendRecsysEvent(recsysEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnded(event) {
|
||||||
|
const recsysEvent = newRecsysEvent(RecsysData.event.stop, this.player.currentTime());
|
||||||
|
this.log('onEnded', recsysEvent);
|
||||||
|
sendRecsysEvent(recsysEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRateChange(event) {
|
||||||
|
const recsysEvent = newRecsysEvent(RecsysData.event.speed, this.player.currentTime());
|
||||||
|
this.log('onRateChange', recsysEvent);
|
||||||
|
sendRecsysEvent(recsysEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSeeking(event) {
|
||||||
|
const recsysEvent = newRecsysEvent(RecsysData.event.scrub, this.player.currentTime());
|
||||||
|
this.log('onSeeking', recsysEvent);
|
||||||
|
sendRecsysEvent(recsysEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(...args) {
|
||||||
|
console.log(`Recsys 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;
|
|
@ -9,6 +9,7 @@ import eventTracking from 'videojs-event-tracking';
|
||||||
import * as OVERLAY from './overlays';
|
import * as OVERLAY from './overlays';
|
||||||
import './plugins/videojs-mobile-ui/plugin';
|
import './plugins/videojs-mobile-ui/plugin';
|
||||||
import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
|
import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin';
|
||||||
|
import recsys from './plugins/videojs-recsys/plugin';
|
||||||
import qualityLevels from 'videojs-contrib-quality-levels';
|
import qualityLevels from 'videojs-contrib-quality-levels';
|
||||||
import isUserTyping from 'util/detect-typing';
|
import isUserTyping from 'util/detect-typing';
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ type Props = {
|
||||||
autoplay: boolean,
|
autoplay: boolean,
|
||||||
toggleVideoTheaterMode: () => void,
|
toggleVideoTheaterMode: () => void,
|
||||||
adUrl: ?string,
|
adUrl: ?string,
|
||||||
|
claimId: ?string,
|
||||||
};
|
};
|
||||||
|
|
||||||
// type VideoJSOptions = {
|
// type VideoJSOptions = {
|
||||||
|
@ -121,6 +123,10 @@ if (!Object.keys(videojs.getPlugins()).includes('qualityLevels')) {
|
||||||
videojs.registerPlugin('qualityLevels', qualityLevels);
|
videojs.registerPlugin('qualityLevels', qualityLevels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(videojs.getPlugins()).includes('recsys')) {
|
||||||
|
videojs.registerPlugin('recsys', recsys);
|
||||||
|
}
|
||||||
|
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
// LbryVolumeBarClass
|
// LbryVolumeBarClass
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
|
@ -180,6 +186,8 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
onPlayerReady,
|
onPlayerReady,
|
||||||
toggleVideoTheaterMode,
|
toggleVideoTheaterMode,
|
||||||
adUrl,
|
adUrl,
|
||||||
|
claimId,
|
||||||
|
userId,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [reload, setReload] = useState('initial');
|
const [reload, setReload] = useState('initial');
|
||||||
|
@ -578,6 +586,12 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
displayCurrentQuality: true,
|
displayCurrentQuality: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add recsys plugin
|
||||||
|
player.recsys({
|
||||||
|
videoId: claimId,
|
||||||
|
userId: userId,
|
||||||
|
});
|
||||||
|
|
||||||
// Update player source
|
// Update player source
|
||||||
player.src({
|
player.src({
|
||||||
src: finalSource,
|
src: finalSource,
|
||||||
|
|
|
@ -24,48 +24,6 @@ import { useHistory } from 'react-router';
|
||||||
const PLAY_TIMEOUT_ERROR = 'play_timeout_error';
|
const PLAY_TIMEOUT_ERROR = 'play_timeout_error';
|
||||||
const PLAY_TIMEOUT_LIMIT = 2000;
|
const PLAY_TIMEOUT_LIMIT = 2000;
|
||||||
|
|
||||||
/* TODO: Move constants elsewhere */
|
|
||||||
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
|
|
||||||
const recsysId = 'lighthouse-v0';
|
|
||||||
|
|
||||||
/* RecSys */
|
|
||||||
const Recsys = {
|
|
||||||
event: {
|
|
||||||
start: 0,
|
|
||||||
stop: 1,
|
|
||||||
scrub: 2,
|
|
||||||
speed: 3,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function newRecsysEvent(eventType, offset, arg) {
|
|
||||||
if (arg) {
|
|
||||||
return {
|
|
||||||
event: eventType,
|
|
||||||
offset: offset,
|
|
||||||
arg: arg,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
event: eventType,
|
|
||||||
offset: offset,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendRecsysEvent(recsysEvent) {
|
|
||||||
const requestOptions = {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(recsysEvent),
|
|
||||||
};
|
|
||||||
fetch(recsysEndpoint, requestOptions)
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
console.log(`Recsys response data:`, data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
position: number,
|
position: number,
|
||||||
changeVolume: (number) => void,
|
changeVolume: (number) => void,
|
||||||
|
@ -89,6 +47,7 @@ type Props = {
|
||||||
toggleVideoTheaterMode: () => void,
|
toggleVideoTheaterMode: () => void,
|
||||||
setVideoPlaybackRate: (number) => void,
|
setVideoPlaybackRate: (number) => void,
|
||||||
authenticated: boolean,
|
authenticated: boolean,
|
||||||
|
userId: number,
|
||||||
homepageData: {
|
homepageData: {
|
||||||
PRIMARY_CONTENT_CHANNEL_IDS?: Array<string>,
|
PRIMARY_CONTENT_CHANNEL_IDS?: Array<string>,
|
||||||
ENLIGHTENMENT_CHANNEL_IDS?: Array<string>,
|
ENLIGHTENMENT_CHANNEL_IDS?: Array<string>,
|
||||||
|
@ -130,6 +89,7 @@ function VideoViewer(props: Props) {
|
||||||
setVideoPlaybackRate,
|
setVideoPlaybackRate,
|
||||||
homepageData,
|
homepageData,
|
||||||
authenticated,
|
authenticated,
|
||||||
|
userId,
|
||||||
} = props;
|
} = props;
|
||||||
const {
|
const {
|
||||||
PRIMARY_CONTENT_CHANNEL_IDS = [],
|
PRIMARY_CONTENT_CHANNEL_IDS = [],
|
||||||
|
@ -186,14 +146,6 @@ function VideoViewer(props: Props) {
|
||||||
};
|
};
|
||||||
}, [embedded, videoPlaybackRate]);
|
}, [embedded, videoPlaybackRate]);
|
||||||
|
|
||||||
// Used to detect and send recsys events
|
|
||||||
useEffect(() => {
|
|
||||||
history.listen((location) => {
|
|
||||||
console.log(`You changed the page to: ${location.pathname}`);
|
|
||||||
// todo: recsys videoid change goes here
|
|
||||||
});
|
|
||||||
}, [history]);
|
|
||||||
|
|
||||||
function doTrackingBuffered(e: Event, data: any) {
|
function doTrackingBuffered(e: Event, data: any) {
|
||||||
fetch(source, { method: 'HEAD' }).then((response) => {
|
fetch(source, { method: 'HEAD' }).then((response) => {
|
||||||
data.playerPoweredBy = response.headers.get('x-powered-by');
|
data.playerPoweredBy = response.headers.get('x-powered-by');
|
||||||
|
@ -249,8 +201,6 @@ function VideoViewer(props: Props) {
|
||||||
clearPosition(uri);
|
clearPosition(uri);
|
||||||
} else {
|
} else {
|
||||||
savePosition(uri, player.currentTime());
|
savePosition(uri, player.currentTime());
|
||||||
const rsevent = newRecsysEvent(Recsys.event.scrub, player.currentTime());
|
|
||||||
sendRecsysEvent(rsevent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +256,7 @@ function VideoViewer(props: Props) {
|
||||||
player.on('tracking:buffered', doTrackingBuffered);
|
player.on('tracking:buffered', doTrackingBuffered);
|
||||||
player.on('tracking:firstplay', doTrackingFirstPlay);
|
player.on('tracking:firstplay', doTrackingFirstPlay);
|
||||||
player.on('ended', onEnded);
|
player.on('ended', onEnded);
|
||||||
player.on('play', onPlay);
|
player.on('play', () => onPlay(player));
|
||||||
player.on('pause', () => onPause(player));
|
player.on('pause', () => onPause(player));
|
||||||
player.on('dispose', () => onDispose(player));
|
player.on('dispose', () => onDispose(player));
|
||||||
|
|
||||||
|
@ -393,6 +343,8 @@ function VideoViewer(props: Props) {
|
||||||
startMuted={autoplayIfEmbedded}
|
startMuted={autoplayIfEmbedded}
|
||||||
toggleVideoTheaterMode={toggleVideoTheaterMode}
|
toggleVideoTheaterMode={toggleVideoTheaterMode}
|
||||||
autoplay={!embedded || autoplayIfEmbedded}
|
autoplay={!embedded || autoplayIfEmbedded}
|
||||||
|
claimId={claimId}
|
||||||
|
userId={userId}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue