Stats for nerds (#1867)

* transition duration of 0.5s added to watched videos

* small code reformating

* extra white spaces deleted

* typo in the word transition corrected

* original whitespaces respected

* transition added when hovering end

* video stat components started and properties chosen

* ft-video-stats integraded into the video player for dev and debugging

* using a timer to get video stats and a method to update the statistic every second

* getting statistic from vhs and adaptativeFormat

* frame drop capture

* stats capture in the form of event

* useless comment deleted

* stats render with a for loop in the template

* stats correctly displayed

* overlay stats added

* video stats component deleted

* video stats component deleted inside template video player

* video stats component fully deleted

* modal solution working need more styling and code messy

* lint

* modal working with stats

* keyboard shortcut for stats

* lint fix

* network state is now a string

* new line deleted

* useless whitespace deleted

* package-lock.json remove and ignore

* keyboard shortcut restricted to up arrow

* stats overlay made larger

* align to left corner

* useless formatting of string deleted

* renaming of variable formatedStrats for formattedStats

* keyboard shortcut made into a variable

* lint-fix

* key change for i

* label translated

* whitespace added for gitignore

* lock file not ignored

* videoId stat deleted

* ft-video-player.js, en-US.yaml, fr-FR.yaml: changing percentage stats display

changing the display for percentage stats for the format 'x%' instead of 'xx.xx'

* ft-video-player.js, en-US.yaml, fr-FR.yaml: network state video statistic deleted

* ft-video-player.js: made stats modal background color darker

* ft-video-player.js, en-US.yaml, fr-FR.yaml: video id are now related to the one of youtube

* ft-video-player.js, en-US.yaml, fr-FR.yaml: stats displayed made closet to the youtube implementation

the name are capitalized, the order of display is changed and fps is combined with viewport

* lint-fix

* en-US.yaml, fr-FR.yaml: network state possibilities deleted because not used

* package.json.lock: deleted

* ft-video-player.js: formated_stats renamed for formatted_stats

* lock file deleted

* index.js, ft-video-player.js: handling of right click context menu

via electon ipc bus an event is send to tell the vue component to show the stats modal

* ft-video-player.js, index.js: renaming of video stats display event and definition of it as a variable

* index.js, en-US.yaml: inconsistant capitalization of video statistics label solved

* index.js: pluralized video stats

* ft-video-player.js: fix right click undefined this.player

change the arrow function inside the closure for a function with a bind to this

* ft-video-player.js: handling of the case when this.player is not defined

the property this.stats.display.activated as been added and manage when the to show the stats. In this way in the runtime (it is still refered in the run time but it is capture in an event loop) with dont have to refer to this.player so when it is not defined it doesnt affect the behavior.

* lint fix

* src/renderer/components/ft-video-player/ft-video-player.js: modal.close move into the display event of the statistic context

* lint fix

* src/renderer/components/ft-video-player/ft-video-player.js, static/locales/en-US.yaml, static/locales/fr-FR.yaml: better capitalization of the stats labels

* static/locales/en-US.yaml: fps capitalized

* static/locales/fr-FR.yaml, static/locales/en-US.yaml: capitalized label
This commit is contained in:
constraintAutomaton 2021-11-23 06:34:04 -05:00 committed by GitHub
parent 13c2e79517
commit 001b679183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 155 additions and 2 deletions

View File

@ -18,7 +18,15 @@ function runApp() {
showSearchWithGoogle: false,
showSaveImageAs: true,
showCopyImageAddress: true,
prepend: (params, browserWindow) => []
prepend: (defaultActions, parameters, browserWindow) => [
{
label: 'Show Video Statistics',
visible: parameters.mediaType === 'video',
click: () => {
browserWindow.webContents.send('showVideoStatistics', 'show')
}
}
]
})
const localDataStorage = app.getPath('userData') // Grabs the userdata directory based on the user's OS

View File

@ -11,6 +11,7 @@ import 'videojs-overlay/dist/videojs-overlay.css'
import 'videojs-vtt-thumbnails-freetube'
import 'videojs-contrib-quality-levels'
import 'videojs-http-source-selector'
import { ipcRenderer } from 'electron'
export default Vue.extend({
name: 'FtVideoPlayer',
@ -132,6 +133,22 @@ export default Vue.extend({
2.75,
3
]
},
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
}
}
}
},
@ -187,6 +204,36 @@ export default Vue.extend({
displayVideoPlayButton: function() {
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: {
selectedQuality: function() {
this.currentFps()
}
},
mounted: function () {
@ -334,6 +381,7 @@ export default Vue.extend({
const settings = this.player.textTrackSettings.getValues()
this.updateDefaultCaptionSettings(JSON.stringify(settings))
})
this.addPlayerStatsEvent()
}
},
@ -1483,6 +1531,85 @@ export default Vue.extend({
'updateDefaultCaptionSettings',
'showToast',
'sponsorBlockSkipSegments'
])
]),
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
}
}
}
}
})

View File

@ -541,6 +541,15 @@ Video:
reversing playlists: reversing playlists
shuffling playlists: shuffling playlists
looping playlists: looping playlists
Stats:
video id: "Video ID (YouTube)"
player resolution: "Viewport"
volume: "Volume"
fps: "FPS"
frame drop: "Frame Drop"
bandwidth: "Connection Speed"
buffered: "Buffered"
out of: "out of"
#& Videos
Videos:
#& Sort By

View File

@ -589,6 +589,15 @@ Video:
UnsupportedActionTemplate: '$ non pris en charge : %'
OpeningTemplate: Ouverture de $ en %…
OpenInTemplate: Ouvrir avec $
Stats:
video id: "Id de la Vidéo (YouTube)"
player resolution: "Résolution du Lecteur"
volume: "Volume"
fps: "IPS"
frame drop: "Perte d'image"
bandwidth: "Bande Passante"
buffered: "En Tampon"
out of: "sur"
Premieres on: Première le
Videos:
#& Sort By