]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - client/src/assets/player/shared/metrics/metrics-plugin.ts
Catch metrics error
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / shared / metrics / metrics-plugin.ts
1 import videojs from 'video.js'
2 import { PlaybackMetricCreate } from '../../../../../../shared/models'
3 import { MetricsPluginOptions, PlayerMode, PlayerNetworkInfo } from '../../types'
4 import { logger } from '@root-helpers/logger'
5
6 const Plugin = videojs.getPlugin('plugin')
7
8 class MetricsPlugin extends Plugin {
9 private readonly metricsUrl: string
10 private readonly videoUUID: string
11 private readonly mode: PlayerMode
12
13 private downloadedBytesP2P = 0
14 private downloadedBytesHTTP = 0
15 private uploadedBytesP2P = 0
16
17 private resolutionChanges = 0
18 private errors = 0
19
20 private lastPlayerNetworkInfo: PlayerNetworkInfo
21
22 private metricsInterval: any
23
24 private readonly CONSTANTS = {
25 METRICS_INTERVAL: 15000
26 }
27
28 constructor (player: videojs.Player, options: MetricsPluginOptions) {
29 super(player)
30
31 this.metricsUrl = options.metricsUrl
32 this.videoUUID = options.videoUUID
33 this.mode = options.mode
34
35 this.player.one('play', () => {
36 this.runMetricsInterval()
37
38 this.trackBytes()
39 this.trackResolutionChange()
40 this.trackErrors()
41 })
42 }
43
44 dispose () {
45 if (this.metricsInterval) clearInterval(this.metricsInterval)
46 }
47
48 private runMetricsInterval () {
49 this.metricsInterval = setInterval(() => {
50 let resolution: number
51 let fps: number
52
53 if (this.mode === 'p2p-media-loader') {
54 const level = this.player.p2pMediaLoader().getCurrentLevel()
55 if (!level) return
56
57 resolution = Math.min(level.height || 0, level.width || 0)
58
59 const framerate = level?.attrs['FRAME-RATE']
60 fps = framerate
61 ? parseInt(framerate, 10)
62 : undefined
63 } else { // webtorrent
64 const videoFile = this.player.webtorrent().getCurrentVideoFile()
65 if (!videoFile) return
66
67 resolution = videoFile.resolution.id
68 fps = videoFile.fps && videoFile.fps !== -1
69 ? videoFile.fps
70 : undefined
71 }
72
73 const body: PlaybackMetricCreate = {
74 resolution,
75 fps,
76
77 playerMode: this.mode,
78
79 resolutionChanges: this.resolutionChanges,
80
81 errors: this.errors,
82
83 downloadedBytesP2P: this.downloadedBytesP2P,
84 downloadedBytesHTTP: this.downloadedBytesHTTP,
85
86 uploadedBytesP2P: this.uploadedBytesP2P,
87
88 videoId: this.videoUUID
89 }
90
91 this.resolutionChanges = 0
92
93 this.downloadedBytesP2P = 0
94 this.downloadedBytesHTTP = 0
95
96 this.uploadedBytesP2P = 0
97
98 this.errors = 0
99
100 const headers = new Headers({ 'Content-type': 'application/json; charset=UTF-8' })
101
102 return fetch(this.metricsUrl, { method: 'POST', body: JSON.stringify(body), headers })
103 .catch(err => logger.error('Cannot send metrics to the server.', err))
104 }, this.CONSTANTS.METRICS_INTERVAL)
105 }
106
107 private trackBytes () {
108 this.player.on('p2pInfo', (_event, data: PlayerNetworkInfo) => {
109 if (!data) return
110
111 this.downloadedBytesHTTP += data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0)
112 this.downloadedBytesP2P += data.p2p.downloaded - (this.lastPlayerNetworkInfo?.p2p.downloaded || 0)
113
114 this.uploadedBytesP2P += data.p2p.uploaded - (this.lastPlayerNetworkInfo?.p2p.uploaded || 0)
115
116 this.lastPlayerNetworkInfo = data
117 })
118 }
119
120 private trackResolutionChange () {
121 this.player.on('engineResolutionChange', () => {
122 this.resolutionChanges++
123 })
124 }
125
126 private trackErrors () {
127 this.player.on('error', () => {
128 this.errors++
129 })
130 }
131 }
132
133 videojs.registerPlugin('metrics', MetricsPlugin)
134 export { MetricsPlugin }