Stats for nerds cleanup and fix linter errors
This commit is contained in:
parent
1da67594b6
commit
cd574be4e7
|
@ -25,7 +25,7 @@ function runApp() {
|
||||||
label: 'Show Video Statistics',
|
label: 'Show Video Statistics',
|
||||||
visible: parameters.mediaType === 'video',
|
visible: parameters.mediaType === 'video',
|
||||||
click: () => {
|
click: () => {
|
||||||
browserWindow.webContents.send('showVideoStatistics', 'show')
|
browserWindow.webContents.send('showVideoStatistics')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'videojs-overlay/dist/videojs-overlay.css'
|
||||||
import 'videojs-vtt-thumbnails-freetube'
|
import 'videojs-vtt-thumbnails-freetube'
|
||||||
import 'videojs-contrib-quality-levels'
|
import 'videojs-contrib-quality-levels'
|
||||||
import 'videojs-http-source-selector'
|
import 'videojs-http-source-selector'
|
||||||
import { ipcRenderer } from 'electron'
|
|
||||||
|
|
||||||
import { IpcChannels } from '../../../constants'
|
import { IpcChannels } from '../../../constants'
|
||||||
|
|
||||||
|
@ -85,6 +84,10 @@ export default Vue.extend({
|
||||||
useHls: false,
|
useHls: false,
|
||||||
selectedDefaultQuality: '',
|
selectedDefaultQuality: '',
|
||||||
selectedQuality: '',
|
selectedQuality: '',
|
||||||
|
selectedResolution: '',
|
||||||
|
selectedBitrate: '',
|
||||||
|
selectedMimeType: '',
|
||||||
|
selectedFPS: 0,
|
||||||
using60Fps: false,
|
using60Fps: false,
|
||||||
maxFramerate: 0,
|
maxFramerate: 0,
|
||||||
activeSourceList: [],
|
activeSourceList: [],
|
||||||
|
@ -92,6 +95,10 @@ export default Vue.extend({
|
||||||
mouseTimeout: null,
|
mouseTimeout: null,
|
||||||
touchTimeout: null,
|
touchTimeout: null,
|
||||||
lastTouchTime: null,
|
lastTouchTime: null,
|
||||||
|
playerStats: null,
|
||||||
|
statsModal: null,
|
||||||
|
showStatsModal: false,
|
||||||
|
statsModalEventName: 'updateStats',
|
||||||
dataSetup: {
|
dataSetup: {
|
||||||
fluid: true,
|
fluid: true,
|
||||||
nativeTextTracks: false,
|
nativeTextTracks: false,
|
||||||
|
@ -132,44 +139,8 @@ export default Vue.extend({
|
||||||
2.25,
|
2.25,
|
||||||
2.5,
|
2.5,
|
||||||
2.75,
|
2.75,
|
||||||
3,
|
3
|
||||||
3.25,
|
|
||||||
3.5,
|
|
||||||
3.75,
|
|
||||||
4,
|
|
||||||
4.25,
|
|
||||||
4.5,
|
|
||||||
4.75,
|
|
||||||
5,
|
|
||||||
5.25,
|
|
||||||
5.5,
|
|
||||||
5.75,
|
|
||||||
6,
|
|
||||||
6.25,
|
|
||||||
6.5,
|
|
||||||
6.75,
|
|
||||||
7,
|
|
||||||
7.25,
|
|
||||||
7.5,
|
|
||||||
7.75,
|
|
||||||
8
|
|
||||||
]
|
]
|
||||||
},
|
|
||||||
stats: {
|
|
||||||
videoId: '',
|
|
||||||
playerResolution: null,
|
|
||||||
frameInfo: null,
|
|
||||||
volume: 0,
|
|
||||||
bandwidth: null,
|
|
||||||
bufferPercent: 0,
|
|
||||||
fps: null,
|
|
||||||
display: {
|
|
||||||
modal: null,
|
|
||||||
event: 'statsUpdated',
|
|
||||||
keyboardShortcut: 'KeyI',
|
|
||||||
rightClickEvent: 'showVideoStatistics',
|
|
||||||
activated: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -229,36 +200,11 @@ export default Vue.extend({
|
||||||
|
|
||||||
displayVideoPlayButton: function() {
|
displayVideoPlayButton: function() {
|
||||||
return this.$store.getters.getDisplayVideoPlayButton
|
return this.$store.getters.getDisplayVideoPlayButton
|
||||||
},
|
|
||||||
formatted_stats: function() {
|
|
||||||
let resolution = ''
|
|
||||||
let dropFrame = ''
|
|
||||||
if (this.stats.playerResolution != null) {
|
|
||||||
resolution = `(${this.stats.playerResolution.height}X${this.stats.playerResolution.width}) @ ${this.stats.fps} ${this.$t('Video.Stats.fps')}`
|
|
||||||
}
|
|
||||||
if (this.stats.frameInfo != null) {
|
|
||||||
dropFrame = `${this.stats.frameInfo.droppedVideoFrames} ${this.$t('Video.Stats.out of')} ${this.stats.frameInfo.totalVideoFrames}`
|
|
||||||
}
|
|
||||||
const stats = [
|
|
||||||
[this.$t('Video.Stats.video id'), this.stats.videoId],
|
|
||||||
[this.$t('Video.Stats.frame drop'), dropFrame],
|
|
||||||
[this.$t('Video.Stats.player resolution'), resolution],
|
|
||||||
[this.$t('Video.Stats.volume'), `${(this.stats.volume * 100).toFixed(0)} %`],
|
|
||||||
[this.$t('Video.Stats.bandwidth'), `${(this.stats.bandwidth / 1000).toFixed(2)} Kbps`],
|
|
||||||
[this.$t('Video.Stats.buffered'), `${(this.stats.bufferPercent * 100).toFixed(0)} %`]
|
|
||||||
]
|
|
||||||
|
|
||||||
let formattedStats = '<ul style="list-style-type: none;text-align:left; padding-left:0px";>'
|
|
||||||
for (const stat of stats) {
|
|
||||||
formattedStats += `<li style="font-size: 75%">${stat[0]}: ${stat[1]}</li>`
|
|
||||||
}
|
|
||||||
formattedStats += '</ul>'
|
|
||||||
return formattedStats
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedQuality: function() {
|
showStatsModal: function() {
|
||||||
this.currentFps()
|
this.player.trigger(this.statsModalEventName)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
|
@ -411,6 +357,7 @@ export default Vue.extend({
|
||||||
this.player.on('ready', () => {
|
this.player.on('ready', () => {
|
||||||
this.$emit('ready')
|
this.$emit('ready')
|
||||||
this.checkAspectRatio()
|
this.checkAspectRatio()
|
||||||
|
this.createStatsModal()
|
||||||
if (this.captionHybridList.length !== 0) {
|
if (this.captionHybridList.length !== 0) {
|
||||||
this.transformAndInsertCaptions()
|
this.transformAndInsertCaptions()
|
||||||
}
|
}
|
||||||
|
@ -440,11 +387,36 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.player.on(this.statsModalEventName, () => {
|
||||||
|
if (this.showStatsModal) {
|
||||||
|
this.statsModal.open()
|
||||||
|
this.player.controls(true)
|
||||||
|
this.statsModal.contentEl().innerHTML = this.getFormattedStats()
|
||||||
|
} else {
|
||||||
|
this.statsModal.close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.player.on('timeupdate', () => {
|
||||||
|
if (this.format === 'dash') {
|
||||||
|
this.playerStats = this.player.tech({ IWillNotUseThisInPlugins: true }).vhs.stats
|
||||||
|
this.updateStatsContent()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.player.textTrackSettings.on('modalclose', (_) => {
|
this.player.textTrackSettings.on('modalclose', (_) => {
|
||||||
const settings = this.player.textTrackSettings.getValues()
|
const settings = this.player.textTrackSettings.getValues()
|
||||||
this.updateDefaultCaptionSettings(JSON.stringify(settings))
|
this.updateDefaultCaptionSettings(JSON.stringify(settings))
|
||||||
})
|
})
|
||||||
this.addPlayerStatsEvent()
|
|
||||||
|
// right click menu
|
||||||
|
if (this.usingElectron) {
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
ipcRenderer.removeAllListeners('showVideoStatistics')
|
||||||
|
ipcRenderer.on('showVideoStatistics', (event) => {
|
||||||
|
this.toggleShowStatsModal()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -851,6 +823,18 @@ export default Vue.extend({
|
||||||
qualityElement.innerText = selectedQuality
|
qualityElement.innerText = selectedQuality
|
||||||
this.selectedQuality = selectedQuality
|
this.selectedQuality = selectedQuality
|
||||||
|
|
||||||
|
if (selectedQuality !== 'auto') {
|
||||||
|
this.selectedResolution = `${adaptiveFormat.width}x${adaptiveFormat.height}`
|
||||||
|
this.selectedFPS = adaptiveFormat.fps
|
||||||
|
this.selectedBitrate = adaptiveFormat.bitrate
|
||||||
|
this.selectedMimeType = adaptiveFormat.mimeType
|
||||||
|
} else {
|
||||||
|
this.selectedResolution = 'auto'
|
||||||
|
this.selectedFPS = 'auto'
|
||||||
|
this.selectedBitrate = 'auto'
|
||||||
|
this.selectedMimeType = 'auto'
|
||||||
|
}
|
||||||
|
|
||||||
const qualityItems = $('.quality-item').get()
|
const qualityItems = $('.quality-item').get()
|
||||||
|
|
||||||
$('.quality-item').removeClass('quality-selected')
|
$('.quality-item').removeClass('quality-selected')
|
||||||
|
@ -1426,7 +1410,64 @@ export default Vue.extend({
|
||||||
handleTouchEnd: function (event) {
|
handleTouchEnd: function (event) {
|
||||||
clearTimeout(this.touchPauseTimeout)
|
clearTimeout(this.touchPauseTimeout)
|
||||||
},
|
},
|
||||||
|
toggleShowStatsModal: function() {
|
||||||
|
console.log(this.format)
|
||||||
|
if (this.format !== 'dash') {
|
||||||
|
this.showToast({
|
||||||
|
message: 'Video statistics are not available for legacy videos'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.showStatsModal = !this.showStatsModal
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createStatsModal: function() {
|
||||||
|
const ModalDialog = videojs.getComponent('ModalDialog')
|
||||||
|
this.statsModal = new ModalDialog(this.player, {
|
||||||
|
temporary: false,
|
||||||
|
pauseOnOpen: false
|
||||||
|
})
|
||||||
|
this.player.addChild(this.statsModal)
|
||||||
|
this.statsModal.el_.classList.add('statsModal')
|
||||||
|
this.statsModal.on('modalclose', () => {
|
||||||
|
this.showStatsModal = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateStatsContent: function() {
|
||||||
|
if (this.showStatsModal) {
|
||||||
|
this.statsModal.contentEl().innerHTML = this.getFormattedStats()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getFormattedStats: function() {
|
||||||
|
const currentVolume = this.player.muted() ? 0 : this.player.volume()
|
||||||
|
const volume = `${(currentVolume * 100).toFixed(0)}%`
|
||||||
|
const bandwidth = `${(this.playerStats.bandwidth / 1000).toFixed(2)}kbps`
|
||||||
|
const buffered = `${(this.player.bufferedPercent() * 100).toFixed(0)}%`
|
||||||
|
const droppedFrames = this.playerStats.videoPlaybackQuality.droppedVideoFrames
|
||||||
|
const totalFrames = this.playerStats.videoPlaybackQuality.totalVideoFrames
|
||||||
|
const frames = `${droppedFrames} / ${totalFrames}`
|
||||||
|
const resolution = `${this.selectedResolution}@${this.selectedFPS}fps`
|
||||||
|
const playerDimensions = `${this.playerStats.playerDimensions.width}x${this.playerStats.playerDimensions.height}`
|
||||||
|
const statsArray = [
|
||||||
|
[this.$t('Video.Stats.Video ID'), this.videoId],
|
||||||
|
[this.$t('Video.Stats.Resolution'), resolution],
|
||||||
|
[this.$t('Video.Stats.Player Dimensions'), playerDimensions],
|
||||||
|
[this.$t('Video.Stats.Bitrate'), this.selectedBitrate],
|
||||||
|
[this.$t('Video.Stats.Volume'), volume],
|
||||||
|
[this.$t('Video.Stats.Bandwidth'), bandwidth],
|
||||||
|
[this.$t('Video.Stats.Buffered'), buffered],
|
||||||
|
[this.$t('Video.Stats.Dropped / Total Frames'), frames],
|
||||||
|
[this.$t('Video.Stats.Mimetype'), this.selectedMimeType]
|
||||||
|
]
|
||||||
|
let listContentHTML = ''
|
||||||
|
|
||||||
|
statsArray.forEach((stat) => {
|
||||||
|
const content = `<p>${stat[0]}: ${stat[1]}</p>`
|
||||||
|
listContentHTML += content
|
||||||
|
})
|
||||||
|
return listContentHTML
|
||||||
|
},
|
||||||
|
|
||||||
|
// This function should always be at the bottom of this file
|
||||||
keyboardShortcutHandler: function (event) {
|
keyboardShortcutHandler: function (event) {
|
||||||
const activeInputs = $('.ft-input')
|
const activeInputs = $('.ft-input')
|
||||||
|
|
||||||
|
@ -1520,6 +1561,11 @@ export default Vue.extend({
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.changeDurationBySeconds(this.defaultSkipInterval * 1)
|
this.changeDurationBySeconds(this.defaultSkipInterval * 1)
|
||||||
break
|
break
|
||||||
|
case 73:
|
||||||
|
// I Key
|
||||||
|
event.preventDefault()
|
||||||
|
this.toggleShowStatsModal()
|
||||||
|
break
|
||||||
case 49:
|
case 49:
|
||||||
// 1 Key
|
// 1 Key
|
||||||
// Jump to 10% in the video
|
// Jump to 10% in the video
|
||||||
|
@ -1619,86 +1665,6 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addPlayerStatsEvent: function() {
|
|
||||||
this.stats.videoId = this.videoId
|
|
||||||
this.player.on('volumechange', () => {
|
|
||||||
this.stats.volume = this.player.volume()
|
|
||||||
this.player.trigger(this.stats.display.event)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.player.on('timeupdate', () => {
|
|
||||||
const stats = this.player.tech({ IWillNotUseThisInPlugins: true }).vhs.stats
|
|
||||||
this.stats.frameInfo = stats.videoPlaybackQuality
|
|
||||||
this.player.trigger(this.stats.display.event)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.player.on('progress', () => {
|
|
||||||
const stats = this.player.tech({ IWillNotUseThisInPlugins: true }).vhs.stats
|
|
||||||
|
|
||||||
this.stats.bandwidth = stats.bandwidth
|
|
||||||
this.stats.bufferPercent = this.player.bufferedPercent()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.player.on('playerresize', () => {
|
|
||||||
this.stats.playerResolution = this.player.currentDimensions()
|
|
||||||
this.player.trigger(this.stats.display.event)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.createStatsModal()
|
|
||||||
|
|
||||||
this.player.on(this.stats.display.event, () => {
|
|
||||||
if (this.stats.display.activated) {
|
|
||||||
this.stats.display.modal.open()
|
|
||||||
this.player.controls(true)
|
|
||||||
this.stats.display.modal.contentEl().innerHTML = this.formatted_stats
|
|
||||||
} else {
|
|
||||||
this.stats.display.modal.close()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// keyboard shortcut
|
|
||||||
window.addEventListener('keyup', (event) => {
|
|
||||||
if (event.code === this.stats.display.keyboardShortcut) {
|
|
||||||
if (this.stats.display.activated) {
|
|
||||||
this.deactivateStatsDisplay()
|
|
||||||
} else {
|
|
||||||
this.activateStatsDisplay()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, true)
|
|
||||||
// right click menu
|
|
||||||
ipcRenderer.on(this.stats.display.rightClickEvent, () => {
|
|
||||||
this.activateStatsDisplay()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
createStatsModal: function() {
|
|
||||||
const ModalDialog = videojs.getComponent('ModalDialog')
|
|
||||||
this.stats.display.modal = new ModalDialog(this.player, {
|
|
||||||
temporary: false,
|
|
||||||
pauseOnOpen: false
|
|
||||||
})
|
|
||||||
this.player.addChild(this.stats.display.modal)
|
|
||||||
this.stats.display.modal.height('35%')
|
|
||||||
this.stats.display.modal.width('50%')
|
|
||||||
this.stats.display.modal.contentEl().style.backgroundColor = 'rgba(0, 0, 0, 0.55)'
|
|
||||||
this.stats.display.modal.on('modalclose', () => {
|
|
||||||
this.deactivateStatsDisplay()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
activateStatsDisplay: function() {
|
|
||||||
this.stats.display.activated = true
|
|
||||||
},
|
|
||||||
deactivateStatsDisplay: function() {
|
|
||||||
this.stats.display.activated = false
|
|
||||||
},
|
|
||||||
currentFps: function() {
|
|
||||||
for (const el of this.activeAdaptiveFormats) {
|
|
||||||
if (el.qualityLabel === this.selectedQuality) {
|
|
||||||
this.stats.fps = el.fps
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
...mapActions([
|
...mapActions([
|
||||||
'calculateColorLuminance',
|
'calculateColorLuminance',
|
||||||
'updateDefaultCaptionSettings',
|
'updateDefaultCaptionSettings',
|
||||||
|
|
|
@ -4,7 +4,6 @@ import fs from 'fs'
|
||||||
import i18n from '../../i18n/index'
|
import i18n from '../../i18n/index'
|
||||||
|
|
||||||
import { IpcChannels } from '../../../constants'
|
import { IpcChannels } from '../../../constants'
|
||||||
import { ipcRenderer } from 'electron'
|
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
isSideNavOpen: false,
|
isSideNavOpen: false,
|
||||||
|
@ -223,8 +222,6 @@ const actions = {
|
||||||
})
|
})
|
||||||
|
|
||||||
const reader = response.body.getReader()
|
const reader = response.body.getReader()
|
||||||
const contentLength = response.headers.get('Content-Length')
|
|
||||||
let receivedLength = 0
|
|
||||||
const chunks = []
|
const chunks = []
|
||||||
|
|
||||||
const handleError = (err) => {
|
const handleError = (err) => {
|
||||||
|
@ -240,9 +237,10 @@ const actions = {
|
||||||
}
|
}
|
||||||
|
|
||||||
chunks.push(value)
|
chunks.push(value)
|
||||||
receivedLength += value.length
|
|
||||||
// Can be used in the future to determine download percentage
|
// Can be used in the future to determine download percentage
|
||||||
const percentage = receivedLength / contentLength
|
// const contentLength = response.headers.get('Content-Length')
|
||||||
|
// const receivedLength = value.length
|
||||||
|
// const percentage = receivedLength / contentLength
|
||||||
await reader.read().then(processText).catch(handleError)
|
await reader.read().then(processText).catch(handleError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2160,3 +2160,24 @@ video::-webkit-media-text-track-display {
|
||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vjs-modal-dialog.statsModal {
|
||||||
|
line-height: 10px;
|
||||||
|
width: 550px;
|
||||||
|
height: 225px;
|
||||||
|
font-size: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vjs-modal-dialog.statsModal p {
|
||||||
|
line-height: 10px;
|
||||||
|
position:relative;
|
||||||
|
bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 775px) {
|
||||||
|
.vjs-modal-dialog.statsModal {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -556,14 +556,15 @@ Video:
|
||||||
shuffling playlists: shuffling playlists
|
shuffling playlists: shuffling playlists
|
||||||
looping playlists: looping playlists
|
looping playlists: looping playlists
|
||||||
Stats:
|
Stats:
|
||||||
video id: "Video ID (YouTube)"
|
Video ID: Video ID
|
||||||
player resolution: "Viewport"
|
Resolution: Resolution
|
||||||
volume: "Volume"
|
Player Dimensions: Player Dimensions
|
||||||
fps: "FPS"
|
Bitrate: Bitrate
|
||||||
frame drop: "Frame Drop"
|
Volume: Volume
|
||||||
bandwidth: "Connection Speed"
|
Bandwidth: Bandwidth
|
||||||
buffered: "Buffered"
|
Buffered: Buffered
|
||||||
out of: "out of"
|
Dropped / Total Frames: Dropped / Total Frames
|
||||||
|
Mimetype: Mimetype
|
||||||
#& Videos
|
#& Videos
|
||||||
Videos:
|
Videos:
|
||||||
#& Sort By
|
#& Sort By
|
||||||
|
|
Loading…
Reference in New Issue