aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-03-18 16:01:09 +0100
committerChocobozzz <me@florianbigard.com>2022-03-18 16:01:09 +0100
commit68e72ba90c35fc7922e10b221f513ef711b35353 (patch)
tree42577a9fbf9563f24a29d3ad0f03994e12d0e322
parent6afc0d374cbb7c87c095eaa3235e1e83134c0337 (diff)
downloadPeerTube-68e72ba90c35fc7922e10b221f513ef711b35353.tar.gz
PeerTube-68e72ba90c35fc7922e10b221f513ef711b35353.tar.zst
PeerTube-68e72ba90c35fc7922e10b221f513ef711b35353.zip
Optimize improve stat card rendering in player
-rw-r--r--client/src/assets/player/shared/stats/stats-card.ts167
1 files changed, 121 insertions, 46 deletions
diff --git a/client/src/assets/player/shared/stats/stats-card.ts b/client/src/assets/player/shared/stats/stats-card.ts
index 1bf631d2c..e9f9b6bd2 100644
--- a/client/src/assets/player/shared/stats/stats-card.ts
+++ b/client/src/assets/player/shared/stats/stats-card.ts
@@ -22,15 +22,15 @@ interface PlayerNetworkInfo {
22 downloadedFromPeers?: string 22 downloadedFromPeers?: string
23} 23}
24 24
25interface InfoElement {
26 root: HTMLElement
27 value: HTMLElement
28}
29
25const Component = videojs.getComponent('Component') 30const Component = videojs.getComponent('Component')
26class StatsCard extends Component { 31class StatsCard extends Component {
27 options_: StatsCardOptions 32 options_: StatsCardOptions
28 33
29 container: HTMLDivElement
30
31 list: HTMLDivElement
32 closeButton: HTMLElement
33
34 updateInterval: any 34 updateInterval: any
35 35
36 mode: 'webtorrent' | 'p2p-media-loader' 36 mode: 'webtorrent' | 'p2p-media-loader'
@@ -40,18 +40,50 @@ class StatsCard extends Component {
40 intervalMs = 300 40 intervalMs = 300
41 playerNetworkInfo: PlayerNetworkInfo = {} 41 playerNetworkInfo: PlayerNetworkInfo = {}
42 42
43 private containerEl: HTMLDivElement
44 private infoListEl: HTMLDivElement
45
46 private playerMode: InfoElement
47 private p2p: InfoElement
48 private uuid: InfoElement
49 private viewport: InfoElement
50 private resolution: InfoElement
51 private volume: InfoElement
52 private codecs: InfoElement
53 private color: InfoElement
54 private connection: InfoElement
55
56 private network: InfoElement
57 private transferred: InfoElement
58 private download: InfoElement
59
60 private bufferProgress: InfoElement
61 private bufferState: InfoElement
62
63 private liveLatency: InfoElement
64
43 createEl () { 65 createEl () {
44 const container = super.createEl('div', { 66 this.containerEl = videojs.dom.createEl('div', {
45 className: 'vjs-stats-content', 67 className: 'vjs-stats-content'
46 innerHTML: this.getMainTemplate() 68 }) as HTMLDivElement
69 this.containerEl.style.display = 'none'
70
71 this.infoListEl = videojs.dom.createEl('div', {
72 className: 'vjs-stats-list'
47 }) as HTMLDivElement 73 }) as HTMLDivElement
48 this.container = container
49 this.container.style.display = 'none'
50 74
51 this.closeButton = this.container.getElementsByClassName('vjs-stats-close')[0] as HTMLElement 75 const closeButton = videojs.dom.createEl('button', {
52 this.closeButton.onclick = () => this.hide() 76 className: 'vjs-stats-close',
77 tabindex: '0',
78 title: 'Close stats',
79 innerText: '[x]'
80 }, { 'aria-label': 'Close stats' }) as HTMLElement
81 closeButton.onclick = () => this.hide()
53 82
54 this.list = this.container.getElementsByClassName('vjs-stats-list')[0] as HTMLDivElement 83 this.containerEl.appendChild(closeButton)
84 this.containerEl.appendChild(this.infoListEl)
85
86 this.populateInfoBlocks()
55 87
56 this.player_.on('p2pInfo', (event: any, data: EventPlayerNetworkInfo) => { 88 this.player_.on('p2pInfo', (event: any, data: EventPlayerNetworkInfo) => {
57 if (!data) return // HTTP fallback 89 if (!data) return // HTTP fallback
@@ -74,7 +106,7 @@ class StatsCard extends Component {
74 } 106 }
75 }) 107 })
76 108
77 return container 109 return this.containerEl
78 } 110 }
79 111
80 toggle () { 112 toggle () {
@@ -83,14 +115,15 @@ class StatsCard extends Component {
83 } 115 }
84 116
85 show () { 117 show () {
86 this.container.style.display = 'block' 118 this.containerEl.style.display = 'block'
119
87 this.updateInterval = setInterval(async () => { 120 this.updateInterval = setInterval(async () => {
88 try { 121 try {
89 const options = this.mode === 'p2p-media-loader' 122 const options = this.mode === 'p2p-media-loader'
90 ? this.buildHLSOptions() 123 ? this.buildHLSOptions()
91 : await this.buildWebTorrentOptions() // Default 124 : await this.buildWebTorrentOptions() // Default
92 125
93 this.list.innerHTML = this.getListTemplate(options) 126 this.populateInfoValues(options)
94 } catch (err) { 127 } catch (err) {
95 console.error('Cannot update stats.', err) 128 console.error('Cannot update stats.', err)
96 clearInterval(this.updateInterval) 129 clearInterval(this.updateInterval)
@@ -100,7 +133,7 @@ class StatsCard extends Component {
100 133
101 hide () { 134 hide () {
102 clearInterval(this.updateInterval) 135 clearInterval(this.updateInterval)
103 this.container.style.display = 'none' 136 this.containerEl.style.display = 'none'
104 } 137 }
105 138
106 private buildHLSOptions () { 139 private buildHLSOptions () {
@@ -169,7 +202,44 @@ class StatsCard extends Component {
169 } 202 }
170 } 203 }
171 204
172 private getListTemplate (options: { 205 private populateInfoBlocks () {
206 this.playerMode = this.buildInfoRow(this.player().localize('Player mode'))
207 this.p2p = this.buildInfoRow(this.player().localize('P2P'))
208 this.uuid = this.buildInfoRow(this.player().localize('Video UUID'))
209 this.viewport = this.buildInfoRow(this.player().localize('Viewport / Frames'))
210 this.resolution = this.buildInfoRow(this.player().localize('Resolution'))
211 this.volume = this.buildInfoRow(this.player().localize('Volume'))
212 this.codecs = this.buildInfoRow(this.player().localize('Codecs'))
213 this.color = this.buildInfoRow(this.player().localize('Color'))
214 this.connection = this.buildInfoRow(this.player().localize('Connection Speed'))
215
216 this.network = this.buildInfoRow(this.player().localize('Network Activity'))
217 this.transferred = this.buildInfoRow(this.player().localize('Total Transfered'))
218 this.download = this.buildInfoRow(this.player().localize('Download Breakdown'))
219
220 this.bufferProgress = this.buildInfoRow(this.player().localize('Buffer Progress'))
221 this.bufferState = this.buildInfoRow(this.player().localize('Buffer State'))
222
223 this.liveLatency = this.buildInfoRow(this.player().localize('Live Latency'))
224
225 this.infoListEl.appendChild(this.playerMode.root)
226 this.infoListEl.appendChild(this.p2p.root)
227 this.infoListEl.appendChild(this.uuid.root)
228 this.infoListEl.appendChild(this.viewport.root)
229 this.infoListEl.appendChild(this.resolution.root)
230 this.infoListEl.appendChild(this.volume.root)
231 this.infoListEl.appendChild(this.codecs.root)
232 this.infoListEl.appendChild(this.color.root)
233 this.infoListEl.appendChild(this.connection.root)
234 this.infoListEl.appendChild(this.network.root)
235 this.infoListEl.appendChild(this.transferred.root)
236 this.infoListEl.appendChild(this.download.root)
237 this.infoListEl.appendChild(this.bufferProgress.root)
238 this.infoListEl.appendChild(this.bufferState.root)
239 this.infoListEl.appendChild(this.liveLatency.root)
240 }
241
242 private populateInfoValues (options: {
173 playerNetworkInfo: PlayerNetworkInfo 243 playerNetworkInfo: PlayerNetworkInfo
174 progress: number 244 progress: number
175 codecs: string 245 codecs: string
@@ -208,45 +278,50 @@ class StatsCard extends Component {
208 ? `${(progress * 100).toFixed(1)}% (${(progress * duration).toFixed(1)}s)` 278 ? `${(progress * 100).toFixed(1)}% (${(progress * duration).toFixed(1)}s)`
209 : undefined 279 : undefined
210 280
211 return ` 281 this.setInfoValue(this.playerMode, this.mode || 'HTTP')
212 ${this.buildElement(player.localize('Player mode'), this.mode || 'HTTP')} 282 this.setInfoValue(this.p2p, player.localize(this.options_.p2pEnabled ? 'enabled' : 'disabled'))
213 ${this.buildElement(player.localize('P2P'), player.localize(this.options_.p2pEnabled ? 'enabled' : 'disabled'))} 283 this.setInfoValue(this.uuid, this.options_.videoUUID)
214
215 ${this.buildElement(player.localize('Video UUID'), this.options_.videoUUID)}
216 284
217 ${this.buildElement(player.localize('Viewport / Frames'), frames)} 285 this.setInfoValue(this.viewport, frames)
286 this.setInfoValue(this.resolution, resolution)
287 this.setInfoValue(this.volume, volume)
288 this.setInfoValue(this.codecs, codecs)
289 this.setInfoValue(this.color, colorSpace)
290 this.setInfoValue(this.connection, playerNetworkInfo.averageBandwidth)
218 291
219 ${this.buildElement(player.localize('Resolution'), resolution)} 292 this.setInfoValue(this.network, networkActivity)
293 this.setInfoValue(this.transferred, totalTransferred)
294 this.setInfoValue(this.download, downloadBreakdown)
220 295
221 ${this.buildElement(player.localize('Volume'), volume)} 296 this.setInfoValue(this.bufferProgress, bufferProgress)
297 this.setInfoValue(this.bufferState, buffer)
222 298
223 ${this.buildElement(player.localize('Codecs'), codecs)} 299 this.setInfoValue(this.liveLatency, latency)
224 ${this.buildElement(player.localize('Color'), colorSpace)} 300 }
225
226 ${this.buildElement(player.localize('Connection Speed'), playerNetworkInfo.averageBandwidth)}
227 301
228 ${this.buildElement(player.localize('Network Activity'), networkActivity)} 302 private setInfoValue (el: InfoElement, value: string) {
229 ${this.buildElement(player.localize('Total Transfered'), totalTransferred)} 303 if (!value) {
230 ${this.buildElement(player.localize('Download Breakdown'), downloadBreakdown)} 304 el.root.style.display = 'none'
305 return
306 }
231 307
232 ${this.buildElement(player.localize('Buffer Progress'), bufferProgress)} 308 el.root.style.display = 'block'
233 ${this.buildElement(player.localize('Buffer State'), buffer)}
234 309
235 ${this.buildElement(player.localize('Live Latency'), latency)} 310 if (el.value.innerHTML === value) return
236 ` 311 el.value.innerHTML = value
237 } 312 }
238 313
239 private getMainTemplate () { 314 private buildInfoRow (labelText: string, valueHTML?: string) {
240 return ` 315 const root = videojs.dom.createEl('div') as HTMLElement
241 <button class="vjs-stats-close" tabindex=0 aria-label="Close stats" title="Close stats">[x]</button> 316 root.style.display = 'none'
242 <div class="vjs-stats-list"></div> 317
243 ` 318 const label = videojs.dom.createEl('div', { innerText: labelText }) as HTMLElement
244 } 319 const value = videojs.dom.createEl('span', { innerHTML: valueHTML }) as HTMLElement
245 320
246 private buildElement (label: string, value?: string) { 321 root.appendChild(label)
247 if (!value) return '' 322 root.appendChild(value)
248 323
249 return `<div><div>${label}</div><span>${value}</span></div>` 324 return { root, value }
250 } 325 }
251 326
252 private timeRangesToString (r: videojs.TimeRange) { 327 private timeRangesToString (r: videojs.TimeRange) {