import videojs from 'video.js' import { PlaybackMetricCreate } from '../../../../../../shared/models' import { MetricsPluginOptions, PlayerMode, PlayerNetworkInfo } from '../../types' import { logger } from '@root-helpers/logger' const Plugin = videojs.getPlugin('plugin') class MetricsPlugin extends Plugin { private readonly metricsUrl: string private readonly videoUUID: string private readonly mode: PlayerMode private downloadedBytesP2P = 0 private downloadedBytesHTTP = 0 private uploadedBytesP2P = 0 private resolutionChanges = 0 private errors = 0 private lastPlayerNetworkInfo: PlayerNetworkInfo private metricsInterval: any private readonly CONSTANTS = { METRICS_INTERVAL: 15000 } constructor (player: videojs.Player, options: MetricsPluginOptions) { super(player) this.metricsUrl = options.metricsUrl this.videoUUID = options.videoUUID this.mode = options.mode this.player.one('play', () => { this.runMetricsInterval() this.trackBytes() this.trackResolutionChange() this.trackErrors() }) } dispose () { if (this.metricsInterval) clearInterval(this.metricsInterval) } private runMetricsInterval () { this.metricsInterval = setInterval(() => { let resolution: number let fps: number if (this.mode === 'p2p-media-loader') { const level = this.player.p2pMediaLoader().getCurrentLevel() if (!level) return resolution = Math.min(level.height || 0, level.width || 0) const framerate = level?.attrs['FRAME-RATE'] fps = framerate ? parseInt(framerate, 10) : undefined } else { // webtorrent const videoFile = this.player.webtorrent().getCurrentVideoFile() if (!videoFile) return resolution = videoFile.resolution.id fps = videoFile.fps && videoFile.fps !== -1 ? videoFile.fps : undefined } const body: PlaybackMetricCreate = { resolution, fps, playerMode: this.mode, resolutionChanges: this.resolutionChanges, errors: this.errors, downloadedBytesP2P: this.downloadedBytesP2P, downloadedBytesHTTP: this.downloadedBytesHTTP, uploadedBytesP2P: this.uploadedBytesP2P, videoId: this.videoUUID } this.resolutionChanges = 0 this.downloadedBytesP2P = 0 this.downloadedBytesHTTP = 0 this.uploadedBytesP2P = 0 this.errors = 0 const headers = new Headers({ 'Content-type': 'application/json; charset=UTF-8' }) return fetch(this.metricsUrl, { method: 'POST', body: JSON.stringify(body), headers }) .catch(err => logger.error('Cannot send metrics to the server.', err)) }, this.CONSTANTS.METRICS_INTERVAL) } private trackBytes () { this.player.on('p2pInfo', (_event, data: PlayerNetworkInfo) => { if (!data) return this.downloadedBytesHTTP += data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0) this.downloadedBytesP2P += data.p2p.downloaded - (this.lastPlayerNetworkInfo?.p2p.downloaded || 0) this.uploadedBytesP2P += data.p2p.uploaded - (this.lastPlayerNetworkInfo?.p2p.uploaded || 0) this.lastPlayerNetworkInfo = data }) } private trackResolutionChange () { this.player.on('engineResolutionChange', () => { this.resolutionChanges++ }) } private trackErrors () { this.player.on('error', () => { this.errors++ }) } } videojs.registerPlugin('metrics', MetricsPlugin) export { MetricsPlugin }