import videojs from 'video.js'
import { PlayerNetworkInfo } from '../peertube-videojs-typings'
import { getAverageBandwidthInStore } from '../peertube-player-local-storage'
import { bytes } from '../utils'
interface StatsCardOptions extends videojs.ComponentOptions {
videoUUID?: string,
videoIsLive?: boolean,
mode?: 'webtorrent' | 'p2p-media-loader'
}
function getListTemplate (
options: StatsCardOptions,
player: videojs.Player,
args: {
playerNetworkInfo?: any
videoFile?: any
progress?: number
}) {
const { playerNetworkInfo, videoFile, progress } = args
const videoQuality: VideoPlaybackQuality = player.getVideoPlaybackQuality()
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
const pr = (window.devicePixelRatio || 1).toFixed(2)
const colorspace = videoFile?.metadata?.streams[0]['color_space'] !== "unknown"
? videoFile?.metadata?.streams[0]['color_space']
: undefined
return `
${player.localize('Video UUID')}
${options.videoUUID || ''}
Viewport / ${player.localize('Frames')}
${vw}x${vh}*${pr} / ${videoQuality.droppedVideoFrames} dropped of ${videoQuality.totalVideoFrames}
${player.localize('Resolution')}
${videoFile?.resolution.label + videoFile?.fps}
${player.localize('Volume')}
${~~(player.volume() * 100)}%${player.muted() ? ' (muted)' : ''}
${player.localize('Codecs')}
${videoFile?.metadata?.streams[0]['codec_name'] || 'avc1'}
${player.localize('Color')}
${colorspace || 'bt709'}
${player.localize('Connection Speed')}
${playerNetworkInfo.averageBandwidth}
${player.localize('Network Activity')}
${playerNetworkInfo.downloadSpeed} ⇓ / ${playerNetworkInfo.uploadSpeed} ⇑
${player.localize('Total Transfered')}
${playerNetworkInfo.totalDownloaded} ⇓ / ${playerNetworkInfo.totalUploaded} ⇑
${player.localize('Download Breakdown')}
${playerNetworkInfo.downloadedFromServer} from server ยท ${playerNetworkInfo.downloadedFromPeers} from peers
${player.localize('Buffer Health')}
${(progress * 100).toFixed(1)}% (${(progress * videoFile?.metadata?.format.duration).toFixed(1)}s)
${player.localize('Live Latency')}
`
}
function getMainTemplate () {
return `
`
}
const Component = videojs.getComponent('Component')
class StatsCard extends Component {
options_: StatsCardOptions
container: HTMLDivElement
list: HTMLDivElement
closeButton: HTMLElement
update: any
source: any
interval = 300
playerNetworkInfo: any = {}
statsForNerdsEvents = new videojs.EventTarget()
constructor (player: videojs.Player, options: StatsCardOptions) {
super(player, options)
}
createEl () {
const container = super.createEl('div', {
className: 'vjs-stats-content',
innerHTML: getMainTemplate()
}) as HTMLDivElement
this.container = container
this.container.style.display = 'none'
this.closeButton = this.container.getElementsByClassName('vjs-stats-close')[0] as HTMLElement
this.closeButton.onclick = () => this.hide()
this.list = this.container.getElementsByClassName('vjs-stats-list')[0] as HTMLDivElement
console.log(this.player_.qualityLevels())
this.player_.on('p2pInfo', (event: any, data: PlayerNetworkInfo) => {
if (!data) return // HTTP fallback
this.source = data.source
const p2pStats = data.p2p
const httpStats = data.http
this.playerNetworkInfo.downloadSpeed = bytes(p2pStats.downloadSpeed + httpStats.downloadSpeed).join(' ')
this.playerNetworkInfo.uploadSpeed = bytes(p2pStats.uploadSpeed + httpStats.uploadSpeed).join(' ')
this.playerNetworkInfo.totalDownloaded = bytes(p2pStats.downloaded + httpStats.downloaded).join(' ')
this.playerNetworkInfo.totalUploaded = bytes(p2pStats.uploaded + httpStats.uploaded).join(' ')
this.playerNetworkInfo.numPeers = p2pStats.numPeers
this.playerNetworkInfo.averageBandwidth = bytes(getAverageBandwidthInStore() || p2pStats.downloaded + httpStats.downloaded).join(' ')
if (data.source === 'p2p-media-loader') {
this.playerNetworkInfo.downloadedFromServer = bytes(httpStats.downloaded).join(' ')
this.playerNetworkInfo.downloadedFromPeers = bytes(p2pStats.downloaded).join(' ')
}
})
return container
}
toggle () {
this.update
? this.hide()
: this.show()
}
show (options?: StatsCardOptions) {
if (options) this.options_ = options
let metadata = {}
this.container.style.display = 'block'
this.update = setInterval(async () => {
try {
if (this.source === 'webtorrent') {
const progress = this.player_.webtorrent().getTorrent()?.progress
const videoFile = this.player_.webtorrent().getCurrentVideoFile()
videoFile.metadata = metadata[videoFile.fileUrl] = videoFile.metadata || metadata[videoFile.fileUrl] || videoFile.metadataUrl && await fetch(videoFile.metadataUrl).then(res => res.json())
this.list.innerHTML = getListTemplate(this.options_, this.player_, { playerNetworkInfo: this.playerNetworkInfo, videoFile, progress })
} else {
this.list.innerHTML = getListTemplate(this.options_, this.player_, { playerNetworkInfo: this.playerNetworkInfo })
}
} catch (e) {
clearInterval(this.update)
}
}, this.interval)
}
hide () {
clearInterval(this.update)
this.container.style.display = 'none'
}
}
videojs.registerComponent('StatsCard', StatsCard)
export {
StatsCard,
StatsCardOptions
}