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:
parent
13c2e79517
commit
001b679183
|
@ -18,7 +18,15 @@ function runApp() {
|
||||||
showSearchWithGoogle: false,
|
showSearchWithGoogle: false,
|
||||||
showSaveImageAs: true,
|
showSaveImageAs: true,
|
||||||
showCopyImageAddress: 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
|
const localDataStorage = app.getPath('userData') // Grabs the userdata directory based on the user's OS
|
||||||
|
|
|
@ -11,6 +11,7 @@ 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'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'FtVideoPlayer',
|
name: 'FtVideoPlayer',
|
||||||
|
@ -132,6 +133,22 @@ export default Vue.extend({
|
||||||
2.75,
|
2.75,
|
||||||
3
|
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() {
|
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: {
|
||||||
|
selectedQuality: function() {
|
||||||
|
this.currentFps()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
|
@ -334,6 +381,7 @@ export default Vue.extend({
|
||||||
const settings = this.player.textTrackSettings.getValues()
|
const settings = this.player.textTrackSettings.getValues()
|
||||||
this.updateDefaultCaptionSettings(JSON.stringify(settings))
|
this.updateDefaultCaptionSettings(JSON.stringify(settings))
|
||||||
})
|
})
|
||||||
|
this.addPlayerStatsEvent()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1483,6 +1531,85 @@ export default Vue.extend({
|
||||||
'updateDefaultCaptionSettings',
|
'updateDefaultCaptionSettings',
|
||||||
'showToast',
|
'showToast',
|
||||||
'sponsorBlockSkipSegments'
|
'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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -541,6 +541,15 @@ Video:
|
||||||
reversing playlists: reversing playlists
|
reversing playlists: reversing playlists
|
||||||
shuffling playlists: shuffling playlists
|
shuffling playlists: shuffling playlists
|
||||||
looping playlists: looping 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
|
||||||
Videos:
|
Videos:
|
||||||
#& Sort By
|
#& Sort By
|
||||||
|
|
|
@ -589,6 +589,15 @@ Video:
|
||||||
UnsupportedActionTemplate: '$ non pris en charge : %'
|
UnsupportedActionTemplate: '$ non pris en charge : %'
|
||||||
OpeningTemplate: Ouverture de $ en %…
|
OpeningTemplate: Ouverture de $ en %…
|
||||||
OpenInTemplate: Ouvrir avec $
|
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
|
Premieres on: Première le
|
||||||
Videos:
|
Videos:
|
||||||
#& Sort By
|
#& Sort By
|
||||||
|
|
Loading…
Reference in New Issue