From cd574be4e79e5b1aa59442ecd4903089a8734b80 Mon Sep 17 00:00:00 2001 From: Preston Date: Sat, 19 Feb 2022 17:17:58 -0500 Subject: [PATCH] Stats for nerds cleanup and fix linter errors --- src/main/index.js | 2 +- .../ft-video-player/ft-video-player.js | 258 ++++++++---------- src/renderer/store/modules/utils.js | 8 +- src/renderer/videoJS.css | 21 ++ static/locales/en-US.yaml | 17 +- 5 files changed, 146 insertions(+), 160 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index 175bd2cc..d27f8947 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -25,7 +25,7 @@ function runApp() { label: 'Show Video Statistics', visible: parameters.mediaType === 'video', click: () => { - browserWindow.webContents.send('showVideoStatistics', 'show') + browserWindow.webContents.send('showVideoStatistics') } } ] diff --git a/src/renderer/components/ft-video-player/ft-video-player.js b/src/renderer/components/ft-video-player/ft-video-player.js index 9c722bf4..7616f5a8 100644 --- a/src/renderer/components/ft-video-player/ft-video-player.js +++ b/src/renderer/components/ft-video-player/ft-video-player.js @@ -11,7 +11,6 @@ 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' import { IpcChannels } from '../../../constants' @@ -85,6 +84,10 @@ export default Vue.extend({ useHls: false, selectedDefaultQuality: '', selectedQuality: '', + selectedResolution: '', + selectedBitrate: '', + selectedMimeType: '', + selectedFPS: 0, using60Fps: false, maxFramerate: 0, activeSourceList: [], @@ -92,6 +95,10 @@ export default Vue.extend({ mouseTimeout: null, touchTimeout: null, lastTouchTime: null, + playerStats: null, + statsModal: null, + showStatsModal: false, + statsModalEventName: 'updateStats', dataSetup: { fluid: true, nativeTextTracks: false, @@ -132,44 +139,8 @@ export default Vue.extend({ 2.25, 2.5, 2.75, - 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 + 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 - } } } }, @@ -229,36 +200,11 @@ 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 = '' - return formattedStats } }, watch: { - selectedQuality: function() { - this.currentFps() + showStatsModal: function() { + this.player.trigger(this.statsModalEventName) } }, mounted: function () { @@ -411,6 +357,7 @@ export default Vue.extend({ this.player.on('ready', () => { this.$emit('ready') this.checkAspectRatio() + this.createStatsModal() if (this.captionHybridList.length !== 0) { 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', (_) => { const settings = this.player.textTrackSettings.getValues() 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 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() $('.quality-item').removeClass('quality-selected') @@ -1426,7 +1410,64 @@ export default Vue.extend({ handleTouchEnd: function (event) { 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 = `

${stat[0]}: ${stat[1]}

` + listContentHTML += content + }) + return listContentHTML + }, + + // This function should always be at the bottom of this file keyboardShortcutHandler: function (event) { const activeInputs = $('.ft-input') @@ -1520,6 +1561,11 @@ export default Vue.extend({ event.preventDefault() this.changeDurationBySeconds(this.defaultSkipInterval * 1) break + case 73: + // I Key + event.preventDefault() + this.toggleShowStatsModal() + break case 49: // 1 Key // 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([ 'calculateColorLuminance', 'updateDefaultCaptionSettings', diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js index 886cf699..3482f2b6 100644 --- a/src/renderer/store/modules/utils.js +++ b/src/renderer/store/modules/utils.js @@ -4,7 +4,6 @@ import fs from 'fs' import i18n from '../../i18n/index' import { IpcChannels } from '../../../constants' -import { ipcRenderer } from 'electron' const state = { isSideNavOpen: false, @@ -223,8 +222,6 @@ const actions = { }) const reader = response.body.getReader() - const contentLength = response.headers.get('Content-Length') - let receivedLength = 0 const chunks = [] const handleError = (err) => { @@ -240,9 +237,10 @@ const actions = { } chunks.push(value) - receivedLength += value.length // 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) } diff --git a/src/renderer/videoJS.css b/src/renderer/videoJS.css index b74ddd97..66227ed8 100644 --- a/src/renderer/videoJS.css +++ b/src/renderer/videoJS.css @@ -2160,3 +2160,24 @@ video::-webkit-media-text-track-display { font-size: xx-large; 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%; + } +} diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml index 579f709a..b2a705bf 100644 --- a/static/locales/en-US.yaml +++ b/static/locales/en-US.yaml @@ -556,14 +556,15 @@ Video: 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" + Video ID: Video ID + Resolution: Resolution + Player Dimensions: Player Dimensions + Bitrate: Bitrate + Volume: Volume + Bandwidth: Bandwidth + Buffered: Buffered + Dropped / Total Frames: Dropped / Total Frames + Mimetype: Mimetype #& Videos Videos: #& Sort By