From 1ce2462a9330218c4d184c92911bf310b75a2f43 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 02:01:47 +0200 Subject: [PATCH 01/20] raw ingredients done adding functionality --- .env.defaults | 2 + ui/analytics.js | 137 +++++++++++++++++----- ui/component/viewers/videoViewer/index.js | 2 + ui/component/viewers/videoViewer/view.jsx | 30 +++++ 4 files changed, 143 insertions(+), 28 deletions(-) diff --git a/.env.defaults b/.env.defaults index af17f2375..7a244cd80 100644 --- a/.env.defaults +++ b/.env.defaults @@ -109,3 +109,5 @@ ENABLE_UI_NOTIFICATIONS=false #IMAGES_ENABLED=true #FILES_ENABLED=true #MODELS_ENABLED=true + +BRANDED_SITE=odysee diff --git a/ui/analytics.js b/ui/analytics.js index 69c52f3a5..3f30d047d 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -76,7 +76,114 @@ if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEna // if (window.localStorage.getItem(SHARE_THIRD_PARTY) === 'true') thirdPartyAnalyticsEnabled = true; // @endif +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +var bufferTime; + +async function sendData(value){ + console.log(value) +} + +async function runSendingData(){ + await sendData('hello1234') + await sleep(1000 * 30); + runSendingData() +} + +runSendingData() + +/** + * Determine the mobile operating system. + * This function returns one of 'iOS', 'Android', 'Windows Phone', or 'unknown'. + * + * @returns {String} + */ +function getMobileOperatingSystem() { + var userAgent = navigator.userAgent || navigator.vendor || window.opera; + + if (/android/i.test(userAgent)) { + return "and"; + } + + // iOS detection from: http://stackoverflow.com/a/9039885/177710 + if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { + return "ios"; + } + + return "web"; +} + +async function sendWatchmanData(body){ + const response = await fetch('https://watchman.na-backend.odysee.com/reports/playback', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }); + + return response +} + +// fetch('https://watchman.na-backend.odysee.com/reports/playback', { +// method: 'post', +// headers: { +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify({ +// "device": "web", +// "protocol": "stb", +// "duration": 1234, +// "format": "hls", +// "player": "sg-p2", +// "position": 1156513664, +// "rate": 1633176499, +// "rebuf_count": 64944106, +// "rebuf_duration": 32061, +// "rel_position": 43, +// "url": "fred", +// "user_id": 2068464011 +// }), +// }); + +console.log('ANALYTICS RELOADED'); + +var duration = 0; +var amountOfBufferEvents = 0; +var amountOfBufferTimeInMS = 0; +var videoIsPlaying = false; + + +// function getTimeSinceLastEvent(function(){ +// +// }) + + + const analytics: Analytics = { + videoBufferEvent: async (claim, data) => { + console.log('data here'); + console.log(data); + console.log(claim) + bufferTime = data.bufferDuration; + + const dataForWatchman = { + device : getMobileOperatingSystem() + } + + const response = await sendWatchmanData(dataForWatchman); + console.log(response); + + }, + onDispose : () => { + + }, + videoIsPlaying: (contentIsPlaying) => { + videoIsPlaying = contentIsPlaying; + }, error: (message) => { return new Promise((resolve) => { if (internalAnalyticsEnabled && isProduction) { @@ -197,37 +304,10 @@ const analytics: Analytics = { }, videoStartEvent: (claimId, duration) => { + // TODO: hook into here sendPromMetric('time_to_start', duration); sendMatomoEvent('Media', 'TimeToStart', claimId, duration); }, - - videoBufferEvent: (claim, data) => { - sendMatomoEvent('Media', 'BufferTimestamp', claim.claim_id, data.timeAtBuffer); - - if (LBRY_WEB_BUFFER_API) { - fetch(LBRY_WEB_BUFFER_API, { - method: 'post', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - device: 'web', - type: 'buffering', - client: data.userId, - data: { - url: claim.canonical_url, - position: data.timeAtBuffer, - duration: data.bufferDuration, - player: data.playerPoweredBy, - readyState: data.readyState, - stream_duration: data.duration, - stream_bitrate: data.bitRate, - }, - }), - }); - } - }, adsFetchedEvent: () => { sendMatomoEvent('Media', 'AdsFetched'); }, @@ -241,6 +321,7 @@ const analytics: Analytics = { sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite'); }, playerStartedEvent: (embedded) => { + videoPlaying = true; sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite'); }, tagFollowEvent: (tag, following) => { diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index cae14b521..274c6614f 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -15,6 +15,8 @@ const select = (state, props) => { const { search } = props.location; const urlParams = new URLSearchParams(search); const autoplay = urlParams.get('autoplay'); + // get the position that will be used to start the video at, if t variable or saved in state + // TODO: save and load this position from the db so can be used in display and const position = urlParams.get('t') !== null ? urlParams.get('t') : makeSelectContentPositionForUri(props.uri)(state); const userId = selectUser(state) && selectUser(state).id; diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 0619c9047..aca79bb13 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -127,6 +127,13 @@ function VideoViewer(props: Props) { }, [embedded, videoPlaybackRate]); function doTrackingBuffered(e: Event, data: any) { + console.log('BUFFER'); + console.log(this); + console.log('BUFFER'); + this.pause() + + // doAnalyticsBuffer(uri, data); + fetch(source, { method: 'HEAD' }).then((response) => { data.playerPoweredBy = response.headers.get('x-powered-by'); doAnalyticsBuffer(uri, data); @@ -148,6 +155,8 @@ function VideoViewer(props: Props) { } const onEnded = React.useCallback(() => { + analytics.videoIsPlaying(false); + if (adUrl) { setAdUrl(null); return; @@ -167,15 +176,18 @@ function VideoViewer(props: Props) { setIsPlaying(true); setShowAutoplayCountdown(false); setIsEndededEmbed(false); + analytics.videoIsPlaying(true); } function onPause(event, player) { setIsPlaying(false); handlePosition(player); + analytics.videoIsPlaying(false); } function onDispose(event, player) { handlePosition(player); + analytics.videoIsPlaying(false); } function handlePosition(player) { @@ -228,6 +240,15 @@ function VideoViewer(props: Props) { // delay from the header-fetch. This is a temp change until the next // re-factoring. player.on('loadedmetadata', () => restorePlaybackRate(player)); + player.on('seeking', function(e, data){ + console.log('here3') + console.log(this) + console.log(e) + console.log(data) + console.log(player) + console.log('here4') + /*this.pause()*/ + }); player.on('tracking:buffered', doTrackingBuffered); player.on('tracking:firstplay', doTrackingFirstPlay); @@ -235,6 +256,15 @@ function VideoViewer(props: Props) { player.on('play', onPlay); player.on('pause', (event) => onPause(event, player)); player.on('dispose', (event) => onDispose(event, player)); + + player.on('dispose', function(event){ + + + + console.log('PLAYER DISPOSED OF'); + console.log(event); + }); + player.on('error', () => { const error = player.error(); if (error) { -- 2.45.2 From 06dad631c1e0082aabde566b1722399c1b9ae4b8 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 03:04:19 +0200 Subject: [PATCH 02/20] essentially working just need a cleanup --- ui/analytics.js | 146 +++++++++++----------- ui/component/viewers/videoViewer/index.js | 5 +- ui/component/viewers/videoViewer/view.jsx | 8 +- ui/redux/actions/app.js | 4 +- 4 files changed, 84 insertions(+), 79 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 3f30d047d..cf9e08118 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -76,23 +76,6 @@ if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEna // if (window.localStorage.getItem(SHARE_THIRD_PARTY) === 'true') thirdPartyAnalyticsEnabled = true; // @endif -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -var bufferTime; - -async function sendData(value){ - console.log(value) -} - -async function runSendingData(){ - await sendData('hello1234') - await sleep(1000 * 30); - runSendingData() -} - -runSendingData() /** * Determine the mobile operating system. @@ -100,7 +83,7 @@ runSendingData() * * @returns {String} */ -function getMobileOperatingSystem() { +function getDeviceType() { var userAgent = navigator.userAgent || navigator.vendor || window.opera; if (/android/i.test(userAgent)) { @@ -115,74 +98,94 @@ function getMobileOperatingSystem() { return "web"; } +console.log('ANALYTICS RELOADED'); + +var durationInSeconds = 30; +var amountOfBufferEvents = 0; +var amountOfBufferTimeInMS = 0; +var videoType, userId, claimUrl, totalDurationInSeconds, currentVideoPosition, playerPoweredBy, timeAtBuffer; + +async function sendAndResetWatchmanData(){ + console.log('running!') + var protocol; + if (videoType === 'application/x-mpegURL'){ + protocol = 'hls'; + } else { + protocol = 'stb'; + } + + const objectToSend = { + rebuf_count: amountOfBufferEvents, + rebuf_duration: amountOfBufferTimeInMS, + url: claimUrl, + device: getDeviceType(), + duration: durationInSeconds * 1000, + protocol, + player: playerPoweredBy, + user_id: userId, + position: timeAtBuffer, + rel_position: (timeAtBuffer / totalDurationInSeconds * 1000) * 100, + } + + await sendWatchmanData(objectToSend); + + // TODO: send here + amountOfBufferEvents = 0; + amountOfBufferTimeInMS = 0; + videoType, userId, claimUrl, totalDurationInSeconds, currentVideoPosition, playerPoweredBy, timeAtBuffer = null; +} + +var watchmanInterval; +function stopWatchmanInterval() { + clearInterval(watchmanInterval); + watchmanInterval = null; +} +function startWatchmanIntervalIfNotRunning() { + if (!watchmanInterval) { + watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * 30); + } +} + async function sendWatchmanData(body){ const response = await fetch('https://watchman.na-backend.odysee.com/reports/playback', { method: 'POST', headers: { 'Accept': 'application/json', - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', }, - body: JSON.stringify(body) + body: JSON.stringify(body), }); - return response + return response; } -// fetch('https://watchman.na-backend.odysee.com/reports/playback', { -// method: 'post', -// headers: { -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify({ -// "device": "web", -// "protocol": "stb", -// "duration": 1234, -// "format": "hls", -// "player": "sg-p2", -// "position": 1156513664, -// "rate": 1633176499, -// "rebuf_count": 64944106, -// "rebuf_duration": 32061, -// "rel_position": 43, -// "url": "fred", -// "user_id": 2068464011 -// }), -// }); - -console.log('ANALYTICS RELOADED'); - -var duration = 0; -var amountOfBufferEvents = 0; -var amountOfBufferTimeInMS = 0; -var videoIsPlaying = false; - - -// function getTimeSinceLastEvent(function(){ -// -// }) - - - const analytics: Analytics = { - videoBufferEvent: async (claim, data) => { - console.log('data here'); + videoBufferEvent: async (claim, data, player) => { + + amountOfBufferEvents = amountOfBufferEvents + 1; + amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration; + + userId = data.userId; + claimUrl = claim.canonical_url; + playerPoweredBy = data.playerPoweredBy; + + timeAtBuffer = claim.timeatBuffer; + + totalDurationInSeconds = claim.duration; + + console.log('RUNNING HERE'); + + console.log(claim); console.log(data); - console.log(claim) - bufferTime = data.bufferDuration; - - const dataForWatchman = { - device : getMobileOperatingSystem() - } - - const response = await sendWatchmanData(dataForWatchman); - console.log(response); + console.log(player); + console.log('done1234'); }, - onDispose : () => { - + onDispose: () => { + stopWatchmanInterval(); }, - videoIsPlaying: (contentIsPlaying) => { - videoIsPlaying = contentIsPlaying; + videoIsPlaying: () => { + startWatchmanIntervalIfNotRunning(); }, error: (message) => { return new Promise((resolve) => { @@ -321,7 +324,6 @@ const analytics: Analytics = { sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite'); }, playerStartedEvent: (embedded) => { - videoPlaying = true; sendMatomoEvent('Player', 'Started', embedded ? 'embedded' : 'onsite'); }, tagFollowEvent: (tag, following) => { diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index 274c6614f..9486702d2 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -11,6 +11,9 @@ import { selectDaemonSettings, makeSelectClientSetting, selectHomepageData } fro import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings'; import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user'; +console.log('hereasdfasdf') +console.log(doAnalyticsBuffer) + const select = (state, props) => { const { search } = props.location; const urlParams = new URLSearchParams(search); @@ -43,7 +46,7 @@ const perform = (dispatch) => ({ clearPosition: (uri) => dispatch(clearPosition(uri)), changeMute: (muted) => dispatch(doChangeMute(muted)), doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)), - doAnalyticsBuffer: (uri, bufferData) => dispatch(doAnalyticsBuffer(uri, bufferData)), + doAnalyticsBuffer: (uri, bufferData, player) => dispatch(doAnalyticsBuffer(uri, bufferData, player)), claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()), toggleVideoTheaterMode: () => dispatch(toggleVideoTheaterMode()), setVideoPlaybackRate: (rate) => dispatch(doSetClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE, rate)), diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index aca79bb13..a2a449d94 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -42,7 +42,7 @@ type Props = { autoplayIfEmbedded: boolean, desktopPlayStartTime?: number, doAnalyticsView: (string, number) => Promise, - doAnalyticsBuffer: (string, any) => void, + doAnalyticsBuffer: (string, any, any) => void, claimRewards: () => void, savePosition: (string, number) => void, clearPosition: (string) => void, @@ -130,13 +130,13 @@ function VideoViewer(props: Props) { console.log('BUFFER'); console.log(this); console.log('BUFFER'); - this.pause() - + // this.pause() + // // doAnalyticsBuffer(uri, data); fetch(source, { method: 'HEAD' }).then((response) => { data.playerPoweredBy = response.headers.get('x-powered-by'); - doAnalyticsBuffer(uri, data); + doAnalyticsBuffer(uri, data, this); }); } diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index 67a00c4a0..021b553c4 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -490,7 +490,7 @@ export function doAnalyticsView(uri, timeToStart) { }; } -export function doAnalyticsBuffer(uri, bufferData) { +export function doAnalyticsBuffer(uri, bufferData, player) { return (dispatch, getState) => { const state = getState(); const claim = makeSelectClaimForUri(uri)(state); @@ -514,7 +514,7 @@ export function doAnalyticsBuffer(uri, bufferData) { duration: fileDurationInSeconds, playerPoweredBy: bufferData.playerPoweredBy, readyState: bufferData.readyState, - }); + }, player); } }; } -- 2.45.2 From 45c3d3eb3d6e491e1c526dc3ae263c2155d8dd54 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 03:21:33 +0200 Subject: [PATCH 03/20] almost working with a couple bugs --- ui/analytics.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index cf9e08118..266330015 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -114,6 +114,10 @@ async function sendAndResetWatchmanData(){ protocol = 'stb'; } + console.log('time at buffer'); + console.log(timeAtBuffer); + console.log(totalDurationInSeconds); + const objectToSend = { rebuf_count: amountOfBufferEvents, rebuf_duration: amountOfBufferTimeInMS, @@ -122,9 +126,9 @@ async function sendAndResetWatchmanData(){ duration: durationInSeconds * 1000, protocol, player: playerPoweredBy, - user_id: userId, + user_id: Number(userId), position: timeAtBuffer, - rel_position: (timeAtBuffer / totalDurationInSeconds * 1000) * 100, + rel_position: Math.round((timeAtBuffer / (totalDurationInSeconds * 1000)) * 100), } await sendWatchmanData(objectToSend); @@ -142,7 +146,7 @@ function stopWatchmanInterval() { } function startWatchmanIntervalIfNotRunning() { if (!watchmanInterval) { - watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * 30); + watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * durationInSeconds); } } @@ -169,9 +173,9 @@ const analytics: Analytics = { claimUrl = claim.canonical_url; playerPoweredBy = data.playerPoweredBy; - timeAtBuffer = claim.timeatBuffer; + timeAtBuffer = data.timeAtBuffer; - totalDurationInSeconds = claim.duration; + totalDurationInSeconds = data.duration; console.log('RUNNING HERE'); -- 2.45.2 From aef7afcbfa235e051bc30198fb32810b25f28d0b Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 03:43:04 +0200 Subject: [PATCH 04/20] almost working but a bug or two --- ui/analytics.js | 64 +++++++++++++++-------- ui/component/viewers/videoViewer/view.jsx | 20 +++---- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 266330015..22918b86b 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -100,13 +100,17 @@ function getDeviceType() { console.log('ANALYTICS RELOADED'); -var durationInSeconds = 30; +var durationInSeconds = 10; var amountOfBufferEvents = 0; var amountOfBufferTimeInMS = 0; var videoType, userId, claimUrl, totalDurationInSeconds, currentVideoPosition, playerPoweredBy, timeAtBuffer; + async function sendAndResetWatchmanData(){ - console.log('running!') + + // console.log('running!') + // console.log(player); + var protocol; if (videoType === 'application/x-mpegURL'){ protocol = 'hls'; @@ -114,9 +118,17 @@ async function sendAndResetWatchmanData(){ protocol = 'stb'; } - console.log('time at buffer'); - console.log(timeAtBuffer); - console.log(totalDurationInSeconds); + if(!totalDurationInSeconds){ + totalDurationInSeconds = player.currentTime(); + } + + if(!timeAtBuffer){ + timeAtBuffer = player.duration(); + } + // + // console.log('time at buffer'); + // console.log(timeAtBuffer); + // console.log(totalDurationInSeconds); const objectToSend = { rebuf_count: amountOfBufferEvents, @@ -127,16 +139,18 @@ async function sendAndResetWatchmanData(){ protocol, player: playerPoweredBy, user_id: Number(userId), - position: timeAtBuffer, + position: Math.round(timeAtBuffer) , rel_position: Math.round((timeAtBuffer / (totalDurationInSeconds * 1000)) * 100), } + console.log('About to send the data!'); + await sendWatchmanData(objectToSend); // TODO: send here amountOfBufferEvents = 0; amountOfBufferTimeInMS = 0; - videoType, userId, claimUrl, totalDurationInSeconds, currentVideoPosition, playerPoweredBy, timeAtBuffer = null; + timeAtBuffer = null; } var watchmanInterval; @@ -151,21 +165,27 @@ function startWatchmanIntervalIfNotRunning() { } async function sendWatchmanData(body){ - const response = await fetch('https://watchman.na-backend.odysee.com/reports/playback', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); + try { + const response = await fetch('https://watchman.na-backend.odysee.com/reports/playback', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); - return response; + return response; + } catch (err){ + console.log(err); + } } const analytics: Analytics = { videoBufferEvent: async (claim, data, player) => { + console.log('BUFFERING!'); + amountOfBufferEvents = amountOfBufferEvents + 1; amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration; @@ -177,12 +197,12 @@ const analytics: Analytics = { totalDurationInSeconds = data.duration; - console.log('RUNNING HERE'); - - console.log(claim); - console.log(data); - console.log(player); - console.log('done1234'); + // console.log('RUNNING HERE'); + // + // console.log(claim); + // console.log(data); + // console.log(player); + // console.log('done1234'); }, onDispose: () => { diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index a2a449d94..8298e9710 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -241,12 +241,12 @@ function VideoViewer(props: Props) { // re-factoring. player.on('loadedmetadata', () => restorePlaybackRate(player)); player.on('seeking', function(e, data){ - console.log('here3') - console.log(this) - console.log(e) - console.log(data) - console.log(player) - console.log('here4') + // console.log('here3') + // console.log(this) + // console.log(e) + // console.log(data) + // console.log(player) + // console.log('here4') /*this.pause()*/ }); @@ -257,14 +257,6 @@ function VideoViewer(props: Props) { player.on('pause', (event) => onPause(event, player)); player.on('dispose', (event) => onDispose(event, player)); - player.on('dispose', function(event){ - - - - console.log('PLAYER DISPOSED OF'); - console.log(event); - }); - player.on('error', () => { const error = player.error(); if (error) { -- 2.45.2 From 5b2c808f783329e2159f898f6452cccf8fbec65e Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 03:58:41 +0200 Subject: [PATCH 05/20] seems to be working well --- ui/analytics.js | 26 ++++++----------------- ui/component/viewers/videoViewer/view.jsx | 6 +++--- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 22918b86b..04ab08bfe 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -103,14 +103,11 @@ console.log('ANALYTICS RELOADED'); var durationInSeconds = 10; var amountOfBufferEvents = 0; var amountOfBufferTimeInMS = 0; -var videoType, userId, claimUrl, totalDurationInSeconds, currentVideoPosition, playerPoweredBy, timeAtBuffer; +var videoType, userId, claimUrl, currentVideoPosition, playerPoweredBy, timeAtBuffer; async function sendAndResetWatchmanData(){ - // console.log('running!') - // console.log(player); - var protocol; if (videoType === 'application/x-mpegURL'){ protocol = 'hls'; @@ -118,36 +115,27 @@ async function sendAndResetWatchmanData(){ protocol = 'stb'; } - if(!totalDurationInSeconds){ - totalDurationInSeconds = player.currentTime(); + if(!timeAtBuffer){ + timeAtBuffer = Math.round(player.currentTime()) * 1000; } - if(!timeAtBuffer){ - timeAtBuffer = player.duration(); - } - // - // console.log('time at buffer'); - // console.log(timeAtBuffer); - // console.log(totalDurationInSeconds); + var totalDurationInSeconds = Math.round(player.duration()); const objectToSend = { rebuf_count: amountOfBufferEvents, rebuf_duration: amountOfBufferTimeInMS, url: claimUrl, device: getDeviceType(), - duration: durationInSeconds * 1000, + duration: Math.round(durationInSeconds) * 1000, protocol, player: playerPoweredBy, user_id: Number(userId), position: Math.round(timeAtBuffer) , rel_position: Math.round((timeAtBuffer / (totalDurationInSeconds * 1000)) * 100), - } - - console.log('About to send the data!'); + }; await sendWatchmanData(objectToSend); - // TODO: send here amountOfBufferEvents = 0; amountOfBufferTimeInMS = 0; timeAtBuffer = null; @@ -195,8 +183,6 @@ const analytics: Analytics = { timeAtBuffer = data.timeAtBuffer; - totalDurationInSeconds = data.duration; - // console.log('RUNNING HERE'); // // console.log(claim); diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 8298e9710..30abb9b59 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -127,9 +127,9 @@ function VideoViewer(props: Props) { }, [embedded, videoPlaybackRate]); function doTrackingBuffered(e: Event, data: any) { - console.log('BUFFER'); - console.log(this); - console.log('BUFFER'); + // console.log('BUFFER'); + // console.log(this); + // console.log('BUFFER'); // this.pause() // // doAnalyticsBuffer(uri, data); -- 2.45.2 From 4895218279f32afcc2472afd7493e14c45420d95 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 04:29:42 +0200 Subject: [PATCH 06/20] seems to be working well but needs a cleanup --- ui/analytics.js | 32 +++++++++++++++-------- ui/component/viewers/videoViewer/view.jsx | 17 ++++++++++-- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 04ab08bfe..fd42409d8 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -104,7 +104,7 @@ var durationInSeconds = 10; var amountOfBufferEvents = 0; var amountOfBufferTimeInMS = 0; var videoType, userId, claimUrl, currentVideoPosition, playerPoweredBy, timeAtBuffer; - +var passedPlayer; async function sendAndResetWatchmanData(){ @@ -115,6 +115,16 @@ async function sendAndResetWatchmanData(){ protocol = 'stb'; } + var playerToUse = player || passedPlayer; + + if(!playerToUse){ + return console.log('no player to use') + } + + if(!userId){ + return console.log('no user id') + } + if(!timeAtBuffer){ timeAtBuffer = Math.round(player.currentTime()) * 1000; } @@ -182,17 +192,11 @@ const analytics: Analytics = { playerPoweredBy = data.playerPoweredBy; timeAtBuffer = data.timeAtBuffer; - - // console.log('RUNNING HERE'); - // - // console.log(claim); - // console.log(data); - // console.log(player); - // console.log('done1234'); - }, + onDispose: () => { stopWatchmanInterval(); + // TODO: clear data here }, videoIsPlaying: () => { startWatchmanIntervalIfNotRunning(); @@ -316,8 +320,14 @@ const analytics: Analytics = { } }, - videoStartEvent: (claimId, duration) => { - // TODO: hook into here + videoStartEvent: (claimId, duration, poweredBy, canonicalUrl, playerFromView, passedUserId) => { + + userId = passedUserId + + passedPlayer = playerFromView + playerPoweredBy = poweredBy; + claimUrl = canonicalUrl + // TODO: add claim url , userId sendPromMetric('time_to_start', duration); sendMatomoEvent('Media', 'TimeToStart', claimId, duration); }, diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 30abb9b59..9c320adad 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -141,6 +141,10 @@ function VideoViewer(props: Props) { } function doTrackingFirstPlay(e: Event, data: any) { + + console.log('running here'); + console.log(userId); + let timeToStart = data.secondsToLoad; if (desktopPlayStartTime !== undefined) { @@ -148,7 +152,13 @@ function VideoViewer(props: Props) { timeToStart += differenceToAdd; } analytics.playerStartedEvent(embedded); - analytics.videoStartEvent(claimId, timeToStart); + // TODO: add userId, claim URL, + + fetch(source, { method: 'HEAD' }).then((response) => { + var playerPoweredBy = response.headers.get('x-powered-by'); + analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, claim && claim.canonical_url, player, userId); + }); + doAnalyticsView(uri, timeToStart).then(() => { claimRewards(); }); @@ -251,7 +261,10 @@ function VideoViewer(props: Props) { }); player.on('tracking:buffered', doTrackingBuffered); - player.on('tracking:firstplay', doTrackingFirstPlay); + + if(userId){ + player.on('tracking:firstplay', doTrackingFirstPlay); + } player.on('ended', onEnded); player.on('play', onPlay); player.on('pause', (event) => onPause(event, player)); -- 2.45.2 From 3699b22ad6f9387958a32875d7e1f9e407a98ffc Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 14:22:03 +0200 Subject: [PATCH 07/20] couple of bug fixes --- ui/analytics.js | 14 ++++++++++---- ui/component/viewers/videoViewer/view.jsx | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index fd42409d8..290f1748e 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -139,7 +139,7 @@ async function sendAndResetWatchmanData(){ duration: Math.round(durationInSeconds) * 1000, protocol, player: playerPoweredBy, - user_id: Number(userId), + user_id: userId.toString(), position: Math.round(timeAtBuffer) , rel_position: Math.round((timeAtBuffer / (totalDurationInSeconds * 1000)) * 100), }; @@ -198,8 +198,13 @@ const analytics: Analytics = { stopWatchmanInterval(); // TODO: clear data here }, - videoIsPlaying: () => { - startWatchmanIntervalIfNotRunning(); + videoIsPlaying: (isPlaying) => { + if(isPlaying){ + startWatchmanIntervalIfNotRunning(); + } else { + stopWatchmanInterval(); + } + }, error: (message) => { return new Promise((resolve) => { @@ -320,8 +325,9 @@ const analytics: Analytics = { } }, - videoStartEvent: (claimId, duration, poweredBy, canonicalUrl, playerFromView, passedUserId) => { + videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, playerFromView) => { + console.log('Video start'); userId = passedUserId passedPlayer = playerFromView diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 9c320adad..3447793b8 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -156,7 +156,7 @@ function VideoViewer(props: Props) { fetch(source, { method: 'HEAD' }).then((response) => { var playerPoweredBy = response.headers.get('x-powered-by'); - analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, claim && claim.canonical_url, player, userId); + analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim && claim.canonical_url, player); }); doAnalyticsView(uri, timeToStart).then(() => { -- 2.45.2 From 0b7a9dbf677bd166517435d4f3fc6af0b43669ed Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 17:06:02 +0200 Subject: [PATCH 08/20] basically working now cleaning up --- ui/analytics.js | 46 +++++++++-------------- ui/component/viewers/videoViewer/view.jsx | 7 ++-- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 290f1748e..149230ebd 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -117,17 +117,7 @@ async function sendAndResetWatchmanData(){ var playerToUse = player || passedPlayer; - if(!playerToUse){ - return console.log('no player to use') - } - - if(!userId){ - return console.log('no user id') - } - - if(!timeAtBuffer){ - timeAtBuffer = Math.round(player.currentTime()) * 1000; - } + timeAtBuffer = Math.round(player.currentTime()) * 1000; var totalDurationInSeconds = Math.round(player.duration()); @@ -187,10 +177,6 @@ const analytics: Analytics = { amountOfBufferEvents = amountOfBufferEvents + 1; amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration; - userId = data.userId; - claimUrl = claim.canonical_url; - playerPoweredBy = data.playerPoweredBy; - timeAtBuffer = data.timeAtBuffer; }, @@ -206,6 +192,23 @@ const analytics: Analytics = { } }, + videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, playerFromView) => { + + console.log('Video start'); + userId = passedUserId + claimUrl = canonicalUrl + playerPoweredBy = poweredBy; + + videoType = player.currentSource().type + + console.log(userId, canonicalUrl, playerPoweredBy); + + passedPlayer = playerFromView + + // TODO: add claim url , userId + sendPromMetric('time_to_start', duration); + sendMatomoEvent('Media', 'TimeToStart', claimId, duration); + }, error: (message) => { return new Promise((resolve) => { if (internalAnalyticsEnabled && isProduction) { @@ -324,19 +327,6 @@ const analytics: Analytics = { Lbryio.call('content_tags', 'sync', params); } }, - - videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, playerFromView) => { - - console.log('Video start'); - userId = passedUserId - - passedPlayer = playerFromView - playerPoweredBy = poweredBy; - claimUrl = canonicalUrl - // TODO: add claim url , userId - sendPromMetric('time_to_start', duration); - sendMatomoEvent('Media', 'TimeToStart', claimId, duration); - }, adsFetchedEvent: () => { sendMatomoEvent('Media', 'AdsFetched'); }, diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 3447793b8..ef80a2b7e 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -134,7 +134,7 @@ function VideoViewer(props: Props) { // // doAnalyticsBuffer(uri, data); - fetch(source, { method: 'HEAD' }).then((response) => { + fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { data.playerPoweredBy = response.headers.get('x-powered-by'); doAnalyticsBuffer(uri, data, this); }); @@ -152,12 +152,11 @@ function VideoViewer(props: Props) { timeToStart += differenceToAdd; } analytics.playerStartedEvent(embedded); - // TODO: add userId, claim URL, - fetch(source, { method: 'HEAD' }).then((response) => { + fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { var playerPoweredBy = response.headers.get('x-powered-by'); analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim && claim.canonical_url, player); - }); + }) doAnalyticsView(uri, timeToStart).then(() => { claimRewards(); -- 2.45.2 From 2c115b2e768ad66300375a15ceb4a62a8e4cc621 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 17:46:28 +0200 Subject: [PATCH 09/20] seems to be working pretty well --- ui/analytics.js | 31 ++++++++++++++++------- ui/component/viewers/videoViewer/index.js | 3 --- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 149230ebd..2a2d440ba 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -98,12 +98,10 @@ function getDeviceType() { return "web"; } -console.log('ANALYTICS RELOADED'); - var durationInSeconds = 10; var amountOfBufferEvents = 0; var amountOfBufferTimeInMS = 0; -var videoType, userId, claimUrl, currentVideoPosition, playerPoweredBy, timeAtBuffer; +var videoType, userId, claimUrl, playerPoweredBy, timeAtBuffer; var passedPlayer; async function sendAndResetWatchmanData(){ @@ -115,7 +113,7 @@ async function sendAndResetWatchmanData(){ protocol = 'stb'; } - var playerToUse = player || passedPlayer; + console.log(claimUrl) timeAtBuffer = Math.round(player.currentTime()) * 1000; @@ -143,11 +141,14 @@ async function sendAndResetWatchmanData(){ var watchmanInterval; function stopWatchmanInterval() { + console.log('turning off watchman interval') clearInterval(watchmanInterval); watchmanInterval = null; } function startWatchmanIntervalIfNotRunning() { + console.log('turning on watchman interval') if (!watchmanInterval) { + console.log('watchman interval turned back on') watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * durationInSeconds); } } @@ -184,13 +185,25 @@ const analytics: Analytics = { stopWatchmanInterval(); // TODO: clear data here }, - videoIsPlaying: (isPlaying) => { - if(isPlaying){ - startWatchmanIntervalIfNotRunning(); - } else { - stopWatchmanInterval(); + videoIsPlaying: (isPlaying, event) => { + console.log('event'); + console.log(event); + + console.log('is seeking'); + console.log(player.seeking()) + + // have to use this because videojs pauses/unpauses during seek + var playerIsSeeking = player.seeking(); + + if(!playerIsSeeking){ + if(isPlaying){ + startWatchmanIntervalIfNotRunning(); + } else { + stopWatchmanInterval(); + } } + }, videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, playerFromView) => { diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index 9486702d2..8a3313fff 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -11,9 +11,6 @@ import { selectDaemonSettings, makeSelectClientSetting, selectHomepageData } fro import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings'; import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user'; -console.log('hereasdfasdf') -console.log(doAnalyticsBuffer) - const select = (state, props) => { const { search } = props.location; const urlParams = new URLSearchParams(search); -- 2.45.2 From 24c8145d1ec2e82e22bfc85a07e14e541af248c5 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 17:55:38 +0200 Subject: [PATCH 10/20] cleanup unnecessary changes --- ui/component/viewers/videoViewer/index.js | 2 +- ui/component/viewers/videoViewer/view.jsx | 27 +++++------------------ ui/redux/actions/app.js | 4 ++-- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index 8a3313fff..274c6614f 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -43,7 +43,7 @@ const perform = (dispatch) => ({ clearPosition: (uri) => dispatch(clearPosition(uri)), changeMute: (muted) => dispatch(doChangeMute(muted)), doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)), - doAnalyticsBuffer: (uri, bufferData, player) => dispatch(doAnalyticsBuffer(uri, bufferData, player)), + doAnalyticsBuffer: (uri, bufferData) => dispatch(doAnalyticsBuffer(uri, bufferData)), claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()), toggleVideoTheaterMode: () => dispatch(toggleVideoTheaterMode()), setVideoPlaybackRate: (rate) => dispatch(doSetClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE, rate)), diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index ef80a2b7e..9cc5e91c4 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -42,7 +42,7 @@ type Props = { autoplayIfEmbedded: boolean, desktopPlayStartTime?: number, doAnalyticsView: (string, number) => Promise, - doAnalyticsBuffer: (string, any, any) => void, + doAnalyticsBuffer: (string, any) => void, claimRewards: () => void, savePosition: (string, number) => void, clearPosition: (string) => void, @@ -127,16 +127,10 @@ function VideoViewer(props: Props) { }, [embedded, videoPlaybackRate]); function doTrackingBuffered(e: Event, data: any) { - // console.log('BUFFER'); - // console.log(this); - // console.log('BUFFER'); - // this.pause() - // - // doAnalyticsBuffer(uri, data); fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { data.playerPoweredBy = response.headers.get('x-powered-by'); - doAnalyticsBuffer(uri, data, this); + doAnalyticsBuffer(uri, data); }); } @@ -155,7 +149,7 @@ function VideoViewer(props: Props) { fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { var playerPoweredBy = response.headers.get('x-powered-by'); - analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim && claim.canonical_url, player); + analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId); }) doAnalyticsView(uri, timeToStart).then(() => { @@ -249,21 +243,12 @@ function VideoViewer(props: Props) { // delay from the header-fetch. This is a temp change until the next // re-factoring. player.on('loadedmetadata', () => restorePlaybackRate(player)); - player.on('seeking', function(e, data){ - // console.log('here3') - // console.log(this) - // console.log(e) - // console.log(data) - // console.log(player) - // console.log('here4') - /*this.pause()*/ - }); + // used for tracking buffering for watchman player.on('tracking:buffered', doTrackingBuffered); - if(userId){ - player.on('tracking:firstplay', doTrackingFirstPlay); - } + // first play tracking, used for initializing the watchman api + player.on('tracking:firstplay', doTrackingFirstPlay); player.on('ended', onEnded); player.on('play', onPlay); player.on('pause', (event) => onPause(event, player)); diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index 021b553c4..67a00c4a0 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -490,7 +490,7 @@ export function doAnalyticsView(uri, timeToStart) { }; } -export function doAnalyticsBuffer(uri, bufferData, player) { +export function doAnalyticsBuffer(uri, bufferData) { return (dispatch, getState) => { const state = getState(); const claim = makeSelectClaimForUri(uri)(state); @@ -514,7 +514,7 @@ export function doAnalyticsBuffer(uri, bufferData, player) { duration: fileDurationInSeconds, playerPoweredBy: bufferData.playerPoweredBy, readyState: bufferData.readyState, - }, player); + }); } }; } -- 2.45.2 From e95e79042490b8ad7f0ac403b57a633b979e7f8e Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 6 Aug 2021 20:03:29 +0200 Subject: [PATCH 11/20] eslint fixes --- ui/analytics.js | 71 ++++++++++------------- ui/component/viewers/videoViewer/view.jsx | 12 ++-- 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 2a2d440ba..4e0f2388d 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -9,7 +9,7 @@ import Native from 'native'; import ElectronCookies from '@exponent/electron-cookies'; import { generateInitialUrl } from 'util/url'; // @endif -import { MATOMO_ID, MATOMO_URL, LBRY_WEB_BUFFER_API } from 'config'; +import { MATOMO_ID, MATOMO_URL } from 'config'; const isProduction = process.env.NODE_ENV === 'production'; const devInternalApis = process.env.LBRY_API_URL && process.env.LBRY_API_URL.includes('dev'); @@ -76,7 +76,6 @@ if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEna // if (window.localStorage.getItem(SHARE_THIRD_PARTY) === 'true') thirdPartyAnalyticsEnabled = true; // @endif - /** * Determine the mobile operating system. * This function returns one of 'iOS', 'Android', 'Windows Phone', or 'unknown'. @@ -87,37 +86,36 @@ function getDeviceType() { var userAgent = navigator.userAgent || navigator.vendor || window.opera; if (/android/i.test(userAgent)) { - return "and"; + return 'and'; } // iOS detection from: http://stackoverflow.com/a/9039885/177710 if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { - return "ios"; + return 'ios'; } - return "web"; + // default as web, this can be optimized + return 'web'; } var durationInSeconds = 10; var amountOfBufferEvents = 0; var amountOfBufferTimeInMS = 0; -var videoType, userId, claimUrl, playerPoweredBy, timeAtBuffer; -var passedPlayer; - -async function sendAndResetWatchmanData(){ +var videoType, userId, claimUrl, playerPoweredBy, timeAtBuffer, videoPlayer; +async function sendAndResetWatchmanData() { var protocol; - if (videoType === 'application/x-mpegURL'){ + if (videoType === 'application/x-mpegURL') { protocol = 'hls'; } else { protocol = 'stb'; } - console.log(claimUrl) + console.log(claimUrl); - timeAtBuffer = Math.round(player.currentTime()) * 1000; + timeAtBuffer = Math.round(videoPlayer.currentTime()) * 1000; - var totalDurationInSeconds = Math.round(player.duration()); + var totalDurationInSeconds = Math.round(videoPlayer.duration()); const objectToSend = { rebuf_count: amountOfBufferEvents, @@ -128,7 +126,7 @@ async function sendAndResetWatchmanData(){ protocol, player: playerPoweredBy, user_id: userId.toString(), - position: Math.round(timeAtBuffer) , + position: Math.round(timeAtBuffer), rel_position: Math.round((timeAtBuffer / (totalDurationInSeconds * 1000)) * 100), }; @@ -141,19 +139,19 @@ async function sendAndResetWatchmanData(){ var watchmanInterval; function stopWatchmanInterval() { - console.log('turning off watchman interval') + console.log('turning off watchman interval'); clearInterval(watchmanInterval); watchmanInterval = null; } function startWatchmanIntervalIfNotRunning() { - console.log('turning on watchman interval') + console.log('turning on watchman interval'); if (!watchmanInterval) { - console.log('watchman interval turned back on') + console.log('watchman interval turned back on'); watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * durationInSeconds); } } -async function sendWatchmanData(body){ +async function sendWatchmanData(body) { try { const response = await fetch('https://watchman.na-backend.odysee.com/reports/playback', { method: 'POST', @@ -165,14 +163,13 @@ async function sendWatchmanData(body){ }); return response; - } catch (err){ + } catch (err) { console.log(err); } } const analytics: Analytics = { - videoBufferEvent: async (claim, data, player) => { - + videoBufferEvent: async (claim, data) => { console.log('BUFFERING!'); amountOfBufferEvents = amountOfBufferEvents + 1; @@ -180,44 +177,38 @@ const analytics: Analytics = { timeAtBuffer = data.timeAtBuffer; }, - onDispose: () => { stopWatchmanInterval(); // TODO: clear data here }, - videoIsPlaying: (isPlaying, event) => { - console.log('event'); - console.log(event); - - console.log('is seeking'); - console.log(player.seeking()) + videoIsPlaying: (isPlaying, passedPlayer) => { + var playerToUse = videoPlayer || passedPlayer; // have to use this because videojs pauses/unpauses during seek - var playerIsSeeking = player.seeking(); + var playerIsSeeking = playerToUse.seeking(); - if(!playerIsSeeking){ - if(isPlaying){ + console.log('player is seeking'); + console.log(playerIsSeeking); + + if (!playerIsSeeking) { + if (isPlaying) { startWatchmanIntervalIfNotRunning(); } else { stopWatchmanInterval(); } } - - }, - videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, playerFromView) => { - + videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, passedPlayer) => { console.log('Video start'); - userId = passedUserId - claimUrl = canonicalUrl + userId = passedUserId; + claimUrl = canonicalUrl; playerPoweredBy = poweredBy; - videoType = player.currentSource().type + videoType = passedPlayer.currentSource().type; + videoPlayer = passedPlayer; console.log(userId, canonicalUrl, playerPoweredBy); - passedPlayer = playerFromView - // TODO: add claim url , userId sendPromMetric('time_to_start', duration); sendMatomoEvent('Media', 'TimeToStart', claimId, duration); diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 9cc5e91c4..b687eb7dd 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -127,7 +127,6 @@ function VideoViewer(props: Props) { }, [embedded, videoPlaybackRate]); function doTrackingBuffered(e: Event, data: any) { - fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { data.playerPoweredBy = response.headers.get('x-powered-by'); doAnalyticsBuffer(uri, data); @@ -135,7 +134,6 @@ function VideoViewer(props: Props) { } function doTrackingFirstPlay(e: Event, data: any) { - console.log('running here'); console.log(userId); @@ -149,8 +147,8 @@ function VideoViewer(props: Props) { fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { var playerPoweredBy = response.headers.get('x-powered-by'); - analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId); - }) + analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim.canonical_url, this); + }); doAnalyticsView(uri, timeToStart).then(() => { claimRewards(); @@ -179,18 +177,18 @@ function VideoViewer(props: Props) { setIsPlaying(true); setShowAutoplayCountdown(false); setIsEndededEmbed(false); - analytics.videoIsPlaying(true); + analytics.videoIsPlaying(true, player); } function onPause(event, player) { setIsPlaying(false); handlePosition(player); - analytics.videoIsPlaying(false); + analytics.videoIsPlaying(false, player); } function onDispose(event, player) { handlePosition(player); - analytics.videoIsPlaying(false); + analytics.videoIsPlaying(false, player); } function handlePosition(player) { -- 2.45.2 From e70803814d9d0c73592646a72aebadac93b60d0b Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 9 Aug 2021 19:00:07 +0200 Subject: [PATCH 12/20] bugfix seek event --- ui/analytics.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 4e0f2388d..6ef7d0903 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -185,14 +185,17 @@ const analytics: Analytics = { var playerToUse = videoPlayer || passedPlayer; // have to use this because videojs pauses/unpauses during seek - var playerIsSeeking = playerToUse.seeking(); - - console.log('player is seeking'); - console.log(playerIsSeeking); + // sometimes the seeking function isn't populated yet so check for it as well + if(playerToUse && playerToUse.seeking){ + var playerIsSeeking = playerToUse.seeking(); + } + // if player isn't seeking it's not a seeking caused start or pause if (!playerIsSeeking) { + // if it's a play signal, start tracking if (isPlaying) { startWatchmanIntervalIfNotRunning(); + // if it's a stop signal, stop tracking } else { stopWatchmanInterval(); } -- 2.45.2 From a64f513eee050f54e1655a070c922b5f5fdc8c3f Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 9 Aug 2021 20:14:45 +0200 Subject: [PATCH 13/20] bugfix and andrey fix and better docs --- ui/analytics.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 6ef7d0903..8d8be4934 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -77,8 +77,8 @@ if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEna // @endif /** - * Determine the mobile operating system. - * This function returns one of 'iOS', 'Android', 'Windows Phone', or 'unknown'. + * Determine the mobile device type viewing the data + * This function returns one of 'and' (Android), 'ios', or 'web'. * * @returns {String} */ @@ -120,7 +120,7 @@ async function sendAndResetWatchmanData() { const objectToSend = { rebuf_count: amountOfBufferEvents, rebuf_duration: amountOfBufferTimeInMS, - url: claimUrl, + url: claimUrl.replace('lbry://', ''), device: getDeviceType(), duration: Math.round(durationInSeconds) * 1000, protocol, @@ -182,12 +182,10 @@ const analytics: Analytics = { // TODO: clear data here }, videoIsPlaying: (isPlaying, passedPlayer) => { - var playerToUse = videoPlayer || passedPlayer; - // have to use this because videojs pauses/unpauses during seek // sometimes the seeking function isn't populated yet so check for it as well - if(playerToUse && playerToUse.seeking){ - var playerIsSeeking = playerToUse.seeking(); + if (passedPlayer && passedPlayer.seeking) { + var playerIsSeeking = passedPlayer.seeking(); } // if player isn't seeking it's not a seeking caused start or pause -- 2.45.2 From 91571ddc3ca684600dcea527df4a9acd19ade6d1 Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 9 Aug 2021 22:00:05 +0200 Subject: [PATCH 14/20] getting ready to add last piece of functionality --- ui/analytics.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 8d8be4934..89e19e465 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -98,10 +98,10 @@ function getDeviceType() { return 'web'; } -var durationInSeconds = 10; -var amountOfBufferEvents = 0; -var amountOfBufferTimeInMS = 0; -var videoType, userId, claimUrl, playerPoweredBy, timeAtBuffer, videoPlayer; +let durationInSeconds = 10; +let amountOfBufferEvents = 0; +let amountOfBufferTimeInMS = 0; +let videoType, userId, claimUrl, playerPoweredBy, timeAtBuffer, videoPlayer; async function sendAndResetWatchmanData() { var protocol; @@ -137,7 +137,7 @@ async function sendAndResetWatchmanData() { timeAtBuffer = null; } -var watchmanInterval; +let watchmanInterval; function stopWatchmanInterval() { console.log('turning off watchman interval'); clearInterval(watchmanInterval); @@ -179,13 +179,18 @@ const analytics: Analytics = { }, onDispose: () => { stopWatchmanInterval(); - // TODO: clear data here }, + /** + * Detects whether video was started or paused, and adjusts interval accordingly + * @param {boolean} isPlaying - Whether video was started or paused + * @param {object} passedPlayer - VideoJS Player object + */ videoIsPlaying: (isPlaying, passedPlayer) => { + let playerIsSeeking = false; // have to use this because videojs pauses/unpauses during seek // sometimes the seeking function isn't populated yet so check for it as well if (passedPlayer && passedPlayer.seeking) { - var playerIsSeeking = passedPlayer.seeking(); + playerIsSeeking = passedPlayer.seeking(); } // if player isn't seeking it's not a seeking caused start or pause -- 2.45.2 From eb31708031c0722e9b5b24314c66560ba7095248 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 10 Aug 2021 14:30:31 +0200 Subject: [PATCH 15/20] handle seek events properly --- ui/analytics.js | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 89e19e465..a6361a84a 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -181,7 +181,7 @@ const analytics: Analytics = { stopWatchmanInterval(); }, /** - * Detects whether video was started or paused, and adjusts interval accordingly + * Is told whether video is being started or paused, and adjusts interval accordingly * @param {boolean} isPlaying - Whether video was started or paused * @param {object} passedPlayer - VideoJS Player object */ @@ -193,16 +193,24 @@ const analytics: Analytics = { playerIsSeeking = passedPlayer.seeking(); } - // if player isn't seeking it's not a seeking caused start or pause - if (!playerIsSeeking) { - // if it's a play signal, start tracking - if (isPlaying) { - startWatchmanIntervalIfNotRunning(); - // if it's a stop signal, stop tracking - } else { - stopWatchmanInterval(); - } + // if being paused, and not seeking, send existing data and stop interval + if (!isPlaying && !playerIsSeeking) { + sendAndResetWatchmanData(); + stopWatchmanInterval(); + // if being told to pause, and seeking, send and restart interval + } else if (!isPlaying && playerIsSeeking) { + sendAndResetWatchmanData(); + stopWatchmanInterval(); + startWatchmanIntervalIfNotRunning(); + // is being told to play, and seeking, don't do anything, + // assume it's been started already from pause + } else if (isPlaying && playerIsSeeking) { + + // start but not a seek, assuming a start from paused content + } else if (isPlaying && !playerIsSeeking) { + startWatchmanIntervalIfNotRunning(); } + }, videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, passedPlayer) => { console.log('Video start'); -- 2.45.2 From d85a7eb64e80239cf937682c4bfc06b56b46cae1 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 10 Aug 2021 16:39:00 +0200 Subject: [PATCH 16/20] add dynamic duration to calculate interval properly --- ui/analytics.js | 63 +++++++++++++---------- ui/component/viewers/videoViewer/index.js | 3 +- ui/component/viewers/videoViewer/view.jsx | 2 - 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index a6361a84a..073f1e17d 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -17,6 +17,9 @@ const devInternalApis = process.env.LBRY_API_URL && process.env.LBRY_API_URL.inc export const SHARE_INTERNAL = 'shareInternal'; const SHARE_THIRD_PARTY = 'shareThirdParty'; +const WATCHMAN_BACKEND_ENDPOINT = 'https://watchman.na-backend.odysee.com/reports/playback'; +const SEND_DATA_TO_WATCHMAN_INTERVAL = 30; // in seconds + // @if TARGET='app' if (isProduction) { ElectronCookies.enable({ @@ -97,63 +100,77 @@ function getDeviceType() { // default as web, this can be optimized return 'web'; } - -let durationInSeconds = 10; +// variables initialized for watchman let amountOfBufferEvents = 0; let amountOfBufferTimeInMS = 0; -let videoType, userId, claimUrl, playerPoweredBy, timeAtBuffer, videoPlayer; +let videoType, userId, claimUrl, playerPoweredBy, videoPlayer; +let lastSentTime; +// calculate data for backend, send them, and reset buffer data for next interval async function sendAndResetWatchmanData() { - var protocol; + if (!userId) { + return 'Can only be used with a user id' + } + + let timeSinceLastIntervalSend = new Date() - lastSentTime; + console.log(timeSinceLastIntervalSend); + lastSentTime = new Date(); + + let protocol; if (videoType === 'application/x-mpegURL') { protocol = 'hls'; } else { protocol = 'stb'; } - console.log(claimUrl); + // current position in video in MS + const positionInVideo = Math.round(videoPlayer.currentTime()) * 1000; - timeAtBuffer = Math.round(videoPlayer.currentTime()) * 1000; - - var totalDurationInSeconds = Math.round(videoPlayer.duration()); + // get the duration marking the time in the video for relative position calculation + const totalDurationInSeconds = Math.round(videoPlayer.duration()); + // build object for watchman backend const objectToSend = { rebuf_count: amountOfBufferEvents, rebuf_duration: amountOfBufferTimeInMS, url: claimUrl.replace('lbry://', ''), device: getDeviceType(), - duration: Math.round(durationInSeconds) * 1000, + duration: timeSinceLastIntervalSend, protocol, player: playerPoweredBy, user_id: userId.toString(), - position: Math.round(timeAtBuffer), - rel_position: Math.round((timeAtBuffer / (totalDurationInSeconds * 1000)) * 100), + position: Math.round(positionInVideo), + rel_position: Math.round((positionInVideo / (totalDurationInSeconds * 1000)) * 100), }; + // post to watchman await sendWatchmanData(objectToSend); + // reset buffer data amountOfBufferEvents = 0; amountOfBufferTimeInMS = 0; - timeAtBuffer = null; } let watchmanInterval; +// clear watchman interval and mark it as null (when video paused) function stopWatchmanInterval() { - console.log('turning off watchman interval'); clearInterval(watchmanInterval); watchmanInterval = null; } + +// creates the setInterval that will run send to watchman on recurring basis function startWatchmanIntervalIfNotRunning() { - console.log('turning on watchman interval'); if (!watchmanInterval) { - console.log('watchman interval turned back on'); - watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * durationInSeconds); + lastSentTime = new Date(); + + watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * SEND_DATA_TO_WATCHMAN_INTERVAL); } } +// post data to the backend async function sendWatchmanData(body) { try { - const response = await fetch('https://watchman.na-backend.odysee.com/reports/playback', { + const response = await fetch(WATCHMAN_BACKEND_ENDPOINT, { method: 'POST', headers: { 'Accept': 'application/json', @@ -164,18 +181,16 @@ async function sendWatchmanData(body) { return response; } catch (err) { + console.log('ERROR FROM WATCHMAN BACKEND'); console.log(err); } } const analytics: Analytics = { + // receive buffer events from tracking plugin and jklj videoBufferEvent: async (claim, data) => { - console.log('BUFFERING!'); - amountOfBufferEvents = amountOfBufferEvents + 1; amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration; - - timeAtBuffer = data.timeAtBuffer; }, onDispose: () => { stopWatchmanInterval(); @@ -210,10 +225,9 @@ const analytics: Analytics = { } else if (isPlaying && !playerIsSeeking) { startWatchmanIntervalIfNotRunning(); } - }, videoStartEvent: (claimId, duration, poweredBy, passedUserId, canonicalUrl, passedPlayer) => { - console.log('Video start'); + // populate values for watchman when video starts userId = passedUserId; claimUrl = canonicalUrl; playerPoweredBy = poweredBy; @@ -221,9 +235,6 @@ const analytics: Analytics = { videoType = passedPlayer.currentSource().type; videoPlayer = passedPlayer; - console.log(userId, canonicalUrl, playerPoweredBy); - - // TODO: add claim url , userId sendPromMetric('time_to_start', duration); sendMatomoEvent('Media', 'TimeToStart', claimId, duration); }, diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index 274c6614f..c251959da 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -15,8 +15,7 @@ const select = (state, props) => { const { search } = props.location; const urlParams = new URLSearchParams(search); const autoplay = urlParams.get('autoplay'); - // get the position that will be used to start the video at, if t variable or saved in state - // TODO: save and load this position from the db so can be used in display and + // TODO: eventually this should be received from DB and not local state (https://github.com/lbryio/lbry-desktop/issues/6796) const position = urlParams.get('t') !== null ? urlParams.get('t') : makeSelectContentPositionForUri(props.uri)(state); const userId = selectUser(state) && selectUser(state).id; diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index b687eb7dd..924a78fb7 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -134,8 +134,6 @@ function VideoViewer(props: Props) { } function doTrackingFirstPlay(e: Event, data: any) { - console.log('running here'); - console.log(userId); let timeToStart = data.secondsToLoad; -- 2.45.2 From 38f9570ab4447b25cef4bdd14510f491f74f278c Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 10 Aug 2021 16:51:10 +0200 Subject: [PATCH 17/20] fix lint errors --- ui/analytics.js | 6 +++--- ui/component/viewers/videoViewer/view.jsx | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 073f1e17d..0bc134b87 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -40,7 +40,8 @@ type Analytics = { tagFollowEvent: (string, boolean, ?string) => void, playerLoadedEvent: (?boolean) => void, playerStartedEvent: (?boolean) => void, - videoStartEvent: (string, number) => void, + videoStartEvent: (string, number, string, number, string, any) => void, + videoIsPlaying: (boolean, any) => void, videoBufferEvent: ( StreamClaim, { @@ -109,11 +110,10 @@ let lastSentTime; // calculate data for backend, send them, and reset buffer data for next interval async function sendAndResetWatchmanData() { if (!userId) { - return 'Can only be used with a user id' + return 'Can only be used with a user id'; } let timeSinceLastIntervalSend = new Date() - lastSentTime; - console.log(timeSinceLastIntervalSend); lastSentTime = new Date(); let protocol; diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 924a78fb7..c29ebf862 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -134,7 +134,6 @@ function VideoViewer(props: Props) { } function doTrackingFirstPlay(e: Event, data: any) { - let timeToStart = data.secondsToLoad; if (desktopPlayStartTime !== undefined) { -- 2.45.2 From 3c30c2eda4fe9571bd384c42607bc2e433c6e376 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 10 Aug 2021 17:18:18 +0200 Subject: [PATCH 18/20] last couple changes --- ui/analytics.js | 2 +- ui/component/viewers/videoViewer/view.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index 0bc134b87..b0a269520 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -18,7 +18,7 @@ export const SHARE_INTERNAL = 'shareInternal'; const SHARE_THIRD_PARTY = 'shareThirdParty'; const WATCHMAN_BACKEND_ENDPOINT = 'https://watchman.na-backend.odysee.com/reports/playback'; -const SEND_DATA_TO_WATCHMAN_INTERVAL = 30; // in seconds +const SEND_DATA_TO_WATCHMAN_INTERVAL = 10; // in seconds // @if TARGET='app' if (isProduction) { diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index c29ebf862..650f96c01 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -143,7 +143,7 @@ function VideoViewer(props: Props) { analytics.playerStartedEvent(embedded); fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { - var playerPoweredBy = response.headers.get('x-powered-by'); + let playerPoweredBy = response.headers.get('x-powered-by'); analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim.canonical_url, this); }); -- 2.45.2 From d4f88ec75d7f29be050d5d45c2889f40345109af Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 10 Aug 2021 18:26:36 +0200 Subject: [PATCH 19/20] only run watchman with analytics on and on prod --- ui/analytics.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/analytics.js b/ui/analytics.js index b0a269520..cde9eb2e6 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -161,9 +161,13 @@ function stopWatchmanInterval() { // creates the setInterval that will run send to watchman on recurring basis function startWatchmanIntervalIfNotRunning() { if (!watchmanInterval) { + // instantiate the first time to calculate duration from lastSentTime = new Date(); - watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * SEND_DATA_TO_WATCHMAN_INTERVAL); + // only set an interval if analytics are enabled and is prod + if (internalAnalyticsEnabled && isProduction) { + watchmanInterval = setInterval(sendAndResetWatchmanData, 1000 * SEND_DATA_TO_WATCHMAN_INTERVAL); + } } } -- 2.45.2 From 64321bbdf56634f7130563fe85d65fdbccb5f2bf Mon Sep 17 00:00:00 2001 From: zeppi Date: Tue, 10 Aug 2021 13:08:00 -0400 Subject: [PATCH 20/20] flow fixes --- ui/analytics.js | 13 ++++++------- ui/component/viewers/videoViewer/view.jsx | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ui/analytics.js b/ui/analytics.js index cde9eb2e6..9dafd0339 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -53,7 +53,7 @@ type Analytics = { playerPoweredBy: string, readyState: number, } - ) => void, + ) => Promise, adsFetchedEvent: () => void, adsReceivedEvent: (any) => void, adsErrorEvent: (any) => void, @@ -177,7 +177,7 @@ async function sendWatchmanData(body) { const response = await fetch(WATCHMAN_BACKEND_ENDPOINT, { method: 'POST', headers: { - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify(body), @@ -216,16 +216,15 @@ const analytics: Analytics = { if (!isPlaying && !playerIsSeeking) { sendAndResetWatchmanData(); stopWatchmanInterval(); - // if being told to pause, and seeking, send and restart interval + // if being told to pause, and seeking, send and restart interval } else if (!isPlaying && playerIsSeeking) { sendAndResetWatchmanData(); stopWatchmanInterval(); startWatchmanIntervalIfNotRunning(); - // is being told to play, and seeking, don't do anything, - // assume it's been started already from pause + // is being told to play, and seeking, don't do anything, + // assume it's been started already from pause } else if (isPlaying && playerIsSeeking) { - - // start but not a seek, assuming a start from paused content + // start but not a seek, assuming a start from paused content } else if (isPlaying && !playerIsSeeking) { startWatchmanIntervalIfNotRunning(); } diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 650f96c01..e8617d57d 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -143,7 +143,7 @@ function VideoViewer(props: Props) { analytics.playerStartedEvent(embedded); fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => { - let playerPoweredBy = response.headers.get('x-powered-by'); + let playerPoweredBy = response.headers.get('x-powered-by') || ''; analytics.videoStartEvent(claimId, timeToStart, playerPoweredBy, userId, claim.canonical_url, this); }); -- 2.45.2