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