remove watchman (and errors!) #7710
3 changed files with 37 additions and 180 deletions
|
@ -2318,5 +2318,6 @@
|
||||||
"Odysee Connect --[Section in Help Page]--": "Odysee Connect",
|
"Odysee Connect --[Section in Help Page]--": "Odysee Connect",
|
||||||
"Your hub has blocked this content because it subscribes to the following blocking channel:": "Your hub has blocked this content because it subscribes to the following blocking channel:",
|
"Your hub has blocked this content because it subscribes to the following blocking channel:": "Your hub has blocked this content because it subscribes to the following blocking channel:",
|
||||||
"Your hub has blocked access to this content do to a complaint received under the US Digital Millennium Copyright Act.": "Your hub has blocked access to this content do to a complaint received under the US Digital Millennium Copyright Act.",
|
"Your hub has blocked access to this content do to a complaint received under the US Digital Millennium Copyright Act.": "Your hub has blocked access to this content do to a complaint received under the US Digital Millennium Copyright Act.",
|
||||||
|
"Autoplay Next is on.": "Autoplay Next is on.",
|
||||||
"--end--": "--end--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
164
ui/analytics.js
164
ui/analytics.js
|
@ -1,4 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
/*
|
||||||
|
Removed Watchman (internal view tracking) code.
|
||||||
|
This file may eventually implement cantina
|
||||||
|
Refer to 0cc0e213a5c5bf9e2a76316df5d9da4b250a13c3 for initial integration commit
|
||||||
|
refer to ___ for removal commit.
|
||||||
|
*/
|
||||||
|
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import MatomoTracker from '@datapunt/matomo-tracker-js';
|
import MatomoTracker from '@datapunt/matomo-tracker-js';
|
||||||
|
@ -14,9 +21,6 @@ const devInternalApis = process.env.LBRY_API_URL && process.env.LBRY_API_URL.inc
|
||||||
export const SHARE_INTERNAL = 'shareInternal';
|
export const SHARE_INTERNAL = 'shareInternal';
|
||||||
const SHARE_THIRD_PARTY = 'shareThirdParty';
|
const SHARE_THIRD_PARTY = 'shareThirdParty';
|
||||||
|
|
||||||
const WATCHMAN_BACKEND_ENDPOINT = 'https://watchman.na-backend.odysee.com/reports/playback';
|
|
||||||
// const SEND_DATA_TO_WATCHMAN_INTERVAL = 10; // in seconds
|
|
||||||
|
|
||||||
if (isProduction) {
|
if (isProduction) {
|
||||||
ElectronCookies.enable({
|
ElectronCookies.enable({
|
||||||
origin: 'https://lbry.tv',
|
origin: 'https://lbry.tv',
|
||||||
|
@ -68,114 +72,10 @@ type LogPublishParams = {
|
||||||
let internalAnalyticsEnabled: boolean = false;
|
let internalAnalyticsEnabled: boolean = false;
|
||||||
if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEnabled = true;
|
if (window.localStorage.getItem(SHARE_INTERNAL) === 'true') internalAnalyticsEnabled = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the mobile device type viewing the data
|
|
||||||
* This function returns one of 'and' (Android), 'ios', or 'web'.
|
|
||||||
*
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
function getDeviceType() {
|
|
||||||
return 'dsk';
|
|
||||||
}
|
|
||||||
// variables initialized for watchman
|
|
||||||
let amountOfBufferEvents = 0;
|
|
||||||
let amountOfBufferTimeInMS = 0;
|
|
||||||
let videoType, userId, claimUrl, playerPoweredBy, videoPlayer, bitrateAsBitsPerSecond;
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!videoPlayer) {
|
|
||||||
return 'Video player not initialized';
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeSinceLastIntervalSend = new Date() - lastSentTime;
|
|
||||||
lastSentTime = new Date();
|
|
||||||
|
|
||||||
let protocol;
|
|
||||||
if (videoType === 'application/x-mpegURL') {
|
|
||||||
protocol = 'hls';
|
|
||||||
// get bandwidth if it exists from the texttrack (so it's accurate if user changes quality)
|
|
||||||
// $FlowFixMe
|
|
||||||
bitrateAsBitsPerSecond = videoPlayer.textTracks?.().tracks_[0]?.activeCues[0]?.value?.bandwidth;
|
|
||||||
} else {
|
|
||||||
protocol = 'stb';
|
|
||||||
}
|
|
||||||
|
|
||||||
// current position in video in MS
|
|
||||||
const positionInVideo = Math.round(videoPlayer.currentTime()) * 1000;
|
|
||||||
|
|
||||||
// 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: timeSinceLastIntervalSend,
|
|
||||||
protocol,
|
|
||||||
player: playerPoweredBy,
|
|
||||||
user_id: userId.toString(),
|
|
||||||
position: Math.round(positionInVideo),
|
|
||||||
rel_position: Math.round((positionInVideo / (totalDurationInSeconds * 1000)) * 100),
|
|
||||||
bitrate: bitrateAsBitsPerSecond,
|
|
||||||
bandwidth: undefined,
|
|
||||||
// ...(userDownloadBandwidthInBitsPerSecond && {bandwidth: userDownloadBandwidthInBitsPerSecond}), // add bandwidth if populated
|
|
||||||
};
|
|
||||||
|
|
||||||
// post to watchman
|
|
||||||
await sendWatchmanData(objectToSend);
|
|
||||||
|
|
||||||
// reset buffer data
|
|
||||||
amountOfBufferEvents = 0;
|
|
||||||
amountOfBufferTimeInMS = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let watchmanInterval;
|
|
||||||
// clear watchman interval and mark it as null (when video paused)
|
|
||||||
function stopWatchmanInterval() {
|
|
||||||
clearInterval(watchmanInterval);
|
|
||||||
watchmanInterval = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// post data to the backend
|
|
||||||
async function sendWatchmanData(body) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(WATCHMAN_BACKEND_ENDPOINT, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (err) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const analytics: Analytics = {
|
const analytics: Analytics = {
|
||||||
// receive buffer events from tracking plugin and save buffer amounts and times for backend call
|
// receive buffer events from tracking plugin and save buffer amounts and times for backend call
|
||||||
videoBufferEvent: async (claim, data) => {
|
videoBufferEvent: async (claim, data) => {
|
||||||
amountOfBufferEvents = amountOfBufferEvents + 1;
|
// stub
|
||||||
amountOfBufferTimeInMS = amountOfBufferTimeInMS + data.bufferDuration;
|
|
||||||
},
|
|
||||||
onDispose: () => {
|
|
||||||
stopWatchmanInterval();
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Is told whether video is being started or paused, and adjusts interval accordingly
|
* Is told whether video is being started or paused, and adjusts interval accordingly
|
||||||
|
@ -183,40 +83,9 @@ const analytics: Analytics = {
|
||||||
* @param {object} passedPlayer - VideoJS Player object
|
* @param {object} passedPlayer - VideoJS Player object
|
||||||
*/
|
*/
|
||||||
videoIsPlaying: (isPlaying, passedPlayer) => {
|
videoIsPlaying: (isPlaying, passedPlayer) => {
|
||||||
let playerIsSeeking = false;
|
// stub
|
||||||
// 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) {
|
|
||||||
playerIsSeeking = passedPlayer.seeking();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, timeToStartVideo, poweredBy, passedUserId, canonicalUrl, passedPlayer, videoBitrate) => {
|
videoStartEvent: (claimId, timeToStartVideo, poweredBy, passedUserId, canonicalUrl, passedPlayer, videoBitrate) => {
|
||||||
// populate values for watchman when video starts
|
|
||||||
userId = passedUserId;
|
|
||||||
claimUrl = canonicalUrl;
|
|
||||||
playerPoweredBy = poweredBy;
|
|
||||||
|
|
||||||
videoType = passedPlayer.currentSource().type;
|
|
||||||
videoPlayer = passedPlayer;
|
|
||||||
bitrateAsBitsPerSecond = videoBitrate;
|
|
||||||
|
|
||||||
// sendPromMetric('time_to_start', duration);
|
// sendPromMetric('time_to_start', duration);
|
||||||
sendMatomoEvent('Media', 'TimeToStart', claimId, timeToStartVideo);
|
sendMatomoEvent('Media', 'TimeToStart', claimId, timeToStartVideo);
|
||||||
},
|
},
|
||||||
|
@ -382,24 +251,9 @@ function sendMatomoEvent(category, action, name, value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prometheus
|
|
||||||
// function sendPromMetric(name: string, value?: number) {
|
|
||||||
// if (IS_WEB) {
|
|
||||||
// let url = new URL(SDK_API_PATH + '/metric/ui');
|
|
||||||
// const params = { name: name, value: value ? value.toString() : '' };
|
|
||||||
// url.search = new URLSearchParams(params).toString();
|
|
||||||
// return fetch(url, { method: 'post' });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
const MatomoInstance = new MatomoTracker({
|
const MatomoInstance = new MatomoTracker({
|
||||||
urlBase: MATOMO_URL,
|
urlBase: MATOMO_URL,
|
||||||
siteId: MATOMO_ID, // optional, default value: `1`
|
siteId: MATOMO_ID, // optional, default value: `1`
|
||||||
// heartBeat: { // optional, enabled by default
|
|
||||||
// active: true, // optional, default value: true
|
|
||||||
// seconds: 10 // optional, default value: `15
|
|
||||||
// },
|
|
||||||
// linkTracking: false // optional, default value: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
analytics.pageView(generateInitialUrl(window.location.hash));
|
analytics.pageView(generateInitialUrl(window.location.hash));
|
||||||
|
|
|
@ -458,32 +458,34 @@ export function doAnalyticsView(uri, timeToStart) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doAnalyticsBuffer(uri, bufferData) {
|
export function doAnalyticsBuffer(uri, bufferData) {
|
||||||
return (dispatch, getState) => {
|
return () => {
|
||||||
const state = getState();
|
// return (dispatch, getState) => {
|
||||||
const claim = selectClaimForUri(state, uri);
|
// const state = getState();
|
||||||
const user = selectUser(state);
|
// const claim = selectClaimForUri(state, uri);
|
||||||
const {
|
// const user = selectUser(state);
|
||||||
value: { video, audio, source },
|
// const {
|
||||||
} = claim;
|
// value: { video, audio, source },
|
||||||
const timeAtBuffer = parseInt(bufferData.currentTime ? bufferData.currentTime * 1000 : 0);
|
// } = claim;
|
||||||
const bufferDuration = parseInt(bufferData.secondsToLoad ? bufferData.secondsToLoad * 1000 : 0);
|
// const timeAtBuffer = parseInt(bufferData.currentTime ? bufferData.currentTime * 1000 : 0);
|
||||||
const fileDurationInSeconds = (video && video.duration) || (audio && audio.duration);
|
// const bufferDuration = parseInt(bufferData.secondsToLoad ? bufferData.secondsToLoad * 1000 : 0);
|
||||||
const fileSize = source.size; // size in bytes
|
// const fileDurationInSeconds = (video && video.duration) || (audio && audio.duration);
|
||||||
const fileSizeInBits = fileSize * 8;
|
// const fileSize = source.size; // size in bytes
|
||||||
const bitRate = parseInt(fileSizeInBits / fileDurationInSeconds);
|
// const fileSizeInBits = fileSize * 8;
|
||||||
const userId = user && user.id.toString();
|
// const bitRate = parseInt(fileSizeInBits / fileDurationInSeconds);
|
||||||
|
// const userId = user && user.id.toString();
|
||||||
// if there's a logged in user, send buffer event data to watchman
|
// if there's a logged in user, send buffer event data to watchman
|
||||||
if (userId) {
|
// if (<condition>) {
|
||||||
analytics.videoBufferEvent(claim, {
|
// STUB: any buffer events here
|
||||||
timeAtBuffer,
|
// analytics.videoBufferEvent(claim, {
|
||||||
bufferDuration,
|
// timeAtBuffer,
|
||||||
bitRate,
|
// bufferDuration,
|
||||||
userId,
|
// bitRate,
|
||||||
duration: fileDurationInSeconds,
|
// userId,
|
||||||
playerPoweredBy: bufferData.playerPoweredBy,
|
// duration: fileDurationInSeconds,
|
||||||
readyState: bufferData.readyState,
|
// playerPoweredBy: bufferData.playerPoweredBy,
|
||||||
});
|
// readyState: bufferData.readyState,
|
||||||
}
|
// });
|
||||||
|
// }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue