2020-02-16 18:30:00 +00:00
|
|
|
import Vue from 'vue'
|
2021-01-13 03:56:31 +00:00
|
|
|
import { mapActions } from 'vuex'
|
2020-02-16 18:30:00 +00:00
|
|
|
import FtCard from '../ft-card/ft-card.vue'
|
|
|
|
|
2020-02-20 20:58:21 +00:00
|
|
|
import $ from 'jquery'
|
2020-02-16 18:30:00 +00:00
|
|
|
import videojs from 'video.js'
|
2020-02-18 20:59:01 +00:00
|
|
|
import qualitySelector from '@silvermine/videojs-quality-selector'
|
2020-11-13 22:29:41 +00:00
|
|
|
import fs from 'fs'
|
2020-11-15 19:13:18 +00:00
|
|
|
import 'videojs-overlay/dist/videojs-overlay'
|
|
|
|
import 'videojs-overlay/dist/videojs-overlay.css'
|
2020-09-01 10:04:54 +00:00
|
|
|
import 'videojs-vtt-thumbnails-freetube'
|
2020-02-18 20:59:01 +00:00
|
|
|
import 'videojs-contrib-quality-levels'
|
|
|
|
import 'videojs-http-source-selector'
|
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
2021-11-23 11:34:04 +00:00
|
|
|
import { ipcRenderer } from 'electron'
|
2020-02-16 18:30:00 +00:00
|
|
|
|
|
|
|
export default Vue.extend({
|
|
|
|
name: 'FtVideoPlayer',
|
|
|
|
components: {
|
|
|
|
'ft-card': FtCard
|
|
|
|
},
|
2020-10-04 18:30:54 +00:00
|
|
|
beforeRouteLeave: function () {
|
2020-12-22 18:58:17 +00:00
|
|
|
if (this.player !== null) {
|
|
|
|
this.exitFullWindow()
|
|
|
|
}
|
2020-10-04 18:30:54 +00:00
|
|
|
if (this.player !== null && !this.player.isInPictureInPicture()) {
|
|
|
|
this.player.dispose()
|
|
|
|
this.player = null
|
|
|
|
clearTimeout(this.mouseTimeout)
|
|
|
|
} else if (this.player.isInPictureInPicture()) {
|
|
|
|
this.player.play()
|
|
|
|
}
|
2021-01-13 20:56:25 +00:00
|
|
|
|
|
|
|
if (this.usingElectron && this.powerSaveBlocker !== null) {
|
2021-05-21 23:49:48 +00:00
|
|
|
const { ipcRenderer } = require('electron')
|
|
|
|
ipcRenderer.send('stopPowerSaveBlocker', this.powerSaveBlocker)
|
2021-01-13 20:56:25 +00:00
|
|
|
}
|
2020-10-04 18:30:54 +00:00
|
|
|
},
|
2020-02-16 18:30:00 +00:00
|
|
|
props: {
|
2020-02-19 03:31:10 +00:00
|
|
|
format: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
|
|
|
},
|
2020-02-18 20:59:01 +00:00
|
|
|
sourceList: {
|
|
|
|
type: Array,
|
2020-04-21 20:22:41 +00:00
|
|
|
default: () => { return [] }
|
2020-02-18 20:59:01 +00:00
|
|
|
},
|
2021-05-15 19:08:41 +00:00
|
|
|
adaptiveFormats: {
|
|
|
|
type: Array,
|
|
|
|
default: () => { return [] }
|
|
|
|
},
|
2020-02-18 20:59:01 +00:00
|
|
|
dashSrc: {
|
2020-02-19 03:31:10 +00:00
|
|
|
type: Array,
|
2020-02-18 20:59:01 +00:00
|
|
|
default: null
|
|
|
|
},
|
|
|
|
hlsSrc: {
|
2020-02-19 03:31:10 +00:00
|
|
|
type: Array,
|
2020-02-18 20:59:01 +00:00
|
|
|
default: null
|
|
|
|
},
|
2021-03-17 01:28:25 +00:00
|
|
|
captionHybridList: {
|
2020-02-16 18:30:00 +00:00
|
|
|
type: Array,
|
|
|
|
default: () => { return [] }
|
|
|
|
},
|
2020-02-18 20:59:01 +00:00
|
|
|
storyboardSrc: {
|
2020-02-16 18:30:00 +00:00
|
|
|
type: String,
|
2020-02-18 20:59:01 +00:00
|
|
|
default: ''
|
2020-06-01 02:47:22 +00:00
|
|
|
},
|
|
|
|
thumbnail: {
|
|
|
|
type: String,
|
|
|
|
default: ''
|
2021-05-16 20:01:24 +00:00
|
|
|
},
|
|
|
|
videoId: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
2020-02-16 18:30:00 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
data: function () {
|
|
|
|
return {
|
|
|
|
id: '',
|
2021-01-11 20:45:46 +00:00
|
|
|
powerSaveBlocker: null,
|
2020-03-01 03:37:02 +00:00
|
|
|
volume: 1,
|
2020-02-16 18:30:00 +00:00
|
|
|
player: null,
|
2020-02-18 20:59:01 +00:00
|
|
|
useDash: false,
|
|
|
|
useHls: false,
|
2020-09-25 02:35:13 +00:00
|
|
|
selectedDefaultQuality: '',
|
2021-03-12 21:09:08 +00:00
|
|
|
selectedQuality: '',
|
2021-04-30 21:18:45 +00:00
|
|
|
using60Fps: false,
|
2020-11-13 22:29:41 +00:00
|
|
|
maxFramerate: 0,
|
2020-02-18 20:59:01 +00:00
|
|
|
activeSourceList: [],
|
2021-05-15 19:08:41 +00:00
|
|
|
activeAdaptiveFormats: [],
|
2020-02-21 18:31:32 +00:00
|
|
|
mouseTimeout: null,
|
2021-01-16 03:34:49 +00:00
|
|
|
touchTimeout: null,
|
|
|
|
lastTouchTime: null,
|
2020-02-16 18:30:00 +00:00
|
|
|
dataSetup: {
|
2021-01-15 04:20:42 +00:00
|
|
|
fluid: true,
|
2020-02-18 20:59:01 +00:00
|
|
|
nativeTextTracks: false,
|
|
|
|
plugins: {},
|
|
|
|
controlBar: {
|
|
|
|
children: [
|
|
|
|
'playToggle',
|
|
|
|
'volumePanel',
|
|
|
|
'currentTimeDisplay',
|
|
|
|
'timeDivider',
|
|
|
|
'durationDisplay',
|
|
|
|
'progressControl',
|
|
|
|
'liveDisplay',
|
|
|
|
'seekToLive',
|
|
|
|
'remainingTimeDisplay',
|
|
|
|
'customControlSpacer',
|
|
|
|
'playbackRateMenuButton',
|
2021-01-13 03:56:31 +00:00
|
|
|
'loopButton',
|
2020-02-18 20:59:01 +00:00
|
|
|
'chaptersButton',
|
|
|
|
'descriptionsButton',
|
|
|
|
'subsCapsButton',
|
|
|
|
'audioTrackButton',
|
2020-12-14 23:25:51 +00:00
|
|
|
'pictureInPictureToggle',
|
2021-08-24 07:36:10 +00:00
|
|
|
'toggleTheatreModeButton',
|
2020-12-14 23:25:51 +00:00
|
|
|
'fullWindowButton',
|
2020-12-14 22:37:58 +00:00
|
|
|
'qualitySelector',
|
2020-12-14 23:25:51 +00:00
|
|
|
'fullscreenToggle'
|
2020-02-18 20:59:01 +00:00
|
|
|
]
|
|
|
|
},
|
2020-02-16 18:30:00 +00:00
|
|
|
playbackRates: [
|
2020-12-17 16:22:05 +00:00
|
|
|
0.25,
|
2020-12-15 20:09:49 +00:00
|
|
|
0.5,
|
2020-12-17 16:22:05 +00:00
|
|
|
0.75,
|
|
|
|
1,
|
|
|
|
1.25,
|
|
|
|
1.5,
|
|
|
|
1.75,
|
|
|
|
2,
|
|
|
|
2.25,
|
|
|
|
2.5,
|
|
|
|
2.75,
|
2021-11-24 21:52:56 +00:00
|
|
|
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
|
2020-02-16 18:30:00 +00:00
|
|
|
]
|
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
2021-11-23 11:34:04 +00:00
|
|
|
},
|
|
|
|
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
|
|
|
|
}
|
2020-02-16 18:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
2021-01-11 20:45:46 +00:00
|
|
|
usingElectron: function () {
|
|
|
|
return this.$store.getters.getUsingElectron
|
|
|
|
},
|
|
|
|
|
2021-09-20 02:12:14 +00:00
|
|
|
currentLocale: function () {
|
|
|
|
return this.$store.getters.getCurrentLocale
|
|
|
|
},
|
|
|
|
|
2020-03-01 03:37:02 +00:00
|
|
|
defaultPlayback: function () {
|
|
|
|
return this.$store.getters.getDefaultPlayback
|
2020-02-20 20:58:21 +00:00
|
|
|
},
|
|
|
|
|
2021-08-05 20:17:01 +00:00
|
|
|
defaultSkipInterval: function () {
|
|
|
|
return this.$store.getters.getDefaultSkipInterval
|
|
|
|
},
|
|
|
|
|
2020-05-17 20:12:58 +00:00
|
|
|
defaultQuality: function () {
|
2020-09-26 16:10:56 +00:00
|
|
|
return parseInt(this.$store.getters.getDefaultQuality)
|
2020-05-17 20:12:58 +00:00
|
|
|
},
|
|
|
|
|
2021-06-10 19:35:00 +00:00
|
|
|
defaultCaptionSettings: function () {
|
|
|
|
try {
|
|
|
|
return JSON.parse(this.$store.getters.getDefaultCaptionSettings)
|
|
|
|
} catch (e) {
|
|
|
|
console.log(e)
|
|
|
|
return {}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-03-01 03:37:02 +00:00
|
|
|
defaultVideoFormat: function () {
|
|
|
|
return this.$store.getters.getDefaultVideoFormat
|
|
|
|
},
|
|
|
|
|
|
|
|
autoplayVideos: function () {
|
|
|
|
return this.$store.getters.getAutoplayVideos
|
2021-05-16 20:01:24 +00:00
|
|
|
},
|
|
|
|
|
2021-05-26 15:55:11 +00:00
|
|
|
videoVolumeMouseScroll: function () {
|
|
|
|
return this.$store.getters.getVideoVolumeMouseScroll
|
|
|
|
},
|
|
|
|
|
2021-11-24 21:52:56 +00:00
|
|
|
videoPlaybackRateMouseScroll: function () {
|
|
|
|
return this.$store.getters.getVideoPlaybackRateMouseScroll
|
|
|
|
},
|
|
|
|
|
2021-05-16 20:01:24 +00:00
|
|
|
useSponsorBlock: function () {
|
|
|
|
return this.$store.getters.getUseSponsorBlock
|
|
|
|
},
|
|
|
|
|
|
|
|
sponsorBlockShowSkippedToast: function () {
|
|
|
|
return this.$store.getters.getSponsorBlockShowSkippedToast
|
2021-05-29 18:35:28 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
displayVideoPlayButton: function() {
|
|
|
|
return this.$store.getters.getDisplayVideoPlayButton
|
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
2021-11-23 11:34:04 +00:00
|
|
|
},
|
|
|
|
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()
|
2020-02-16 18:30:00 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
mounted: function () {
|
|
|
|
this.id = this._uid
|
2020-02-18 20:59:01 +00:00
|
|
|
|
2020-03-01 03:37:02 +00:00
|
|
|
const volume = sessionStorage.getItem('volume')
|
|
|
|
|
|
|
|
if (volume !== null) {
|
|
|
|
this.volume = volume
|
|
|
|
}
|
|
|
|
|
2020-12-14 23:25:51 +00:00
|
|
|
this.createFullWindowButton()
|
2021-01-13 03:56:31 +00:00
|
|
|
this.createLoopButton()
|
2021-08-24 07:36:10 +00:00
|
|
|
this.createToggleTheatreModeButton()
|
2020-02-18 20:59:01 +00:00
|
|
|
this.determineFormatType()
|
2020-11-13 22:29:41 +00:00
|
|
|
this.determineMaxFramerate()
|
2020-02-16 18:30:00 +00:00
|
|
|
},
|
2020-02-20 20:58:21 +00:00
|
|
|
beforeDestroy: function () {
|
2020-12-22 18:58:17 +00:00
|
|
|
if (this.player !== null) {
|
|
|
|
this.exitFullWindow()
|
|
|
|
|
|
|
|
if (!this.player.isInPictureInPicture()) {
|
|
|
|
this.player.dispose()
|
|
|
|
this.player = null
|
|
|
|
clearTimeout(this.mouseTimeout)
|
|
|
|
}
|
2020-02-20 20:58:21 +00:00
|
|
|
}
|
2021-01-13 20:56:25 +00:00
|
|
|
|
|
|
|
if (this.usingElectron && this.powerSaveBlocker !== null) {
|
2021-05-21 23:49:48 +00:00
|
|
|
const { ipcRenderer } = require('electron')
|
|
|
|
ipcRenderer.send('stopPowerSaveBlocker', this.powerSaveBlocker)
|
2021-01-13 20:56:25 +00:00
|
|
|
}
|
2020-02-20 20:58:21 +00:00
|
|
|
},
|
2020-02-16 18:30:00 +00:00
|
|
|
methods: {
|
2020-09-25 02:35:13 +00:00
|
|
|
initializePlayer: async function () {
|
2021-05-15 19:08:41 +00:00
|
|
|
console.log(this.adaptiveFormats)
|
2020-02-16 18:30:00 +00:00
|
|
|
const videoPlayer = document.getElementById(this.id)
|
|
|
|
if (videoPlayer !== null) {
|
2020-05-24 21:32:02 +00:00
|
|
|
if (!this.useDash) {
|
2020-02-18 20:59:01 +00:00
|
|
|
qualitySelector(videojs, { showQualitySelectionLabelInControlBar: true })
|
2020-09-26 21:03:42 +00:00
|
|
|
await this.determineDefaultQualityLegacy()
|
2020-02-18 20:59:01 +00:00
|
|
|
}
|
|
|
|
|
2020-10-28 21:20:07 +00:00
|
|
|
this.player = videojs(videoPlayer, {
|
|
|
|
html5: {
|
2021-03-17 01:28:25 +00:00
|
|
|
preloadTextTracks: false,
|
2020-10-28 21:20:07 +00:00
|
|
|
vhs: {
|
2020-12-27 03:21:06 +00:00
|
|
|
limitRenditionByPlayerDimensions: false,
|
|
|
|
smoothQualityChange: false,
|
2021-03-12 21:09:08 +00:00
|
|
|
allowSeeksWithinUnsafeLiveWindow: true,
|
|
|
|
handlePartialData: true
|
2020-10-28 21:20:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2020-12-14 23:25:51 +00:00
|
|
|
|
2020-03-01 03:37:02 +00:00
|
|
|
this.player.volume(this.volume)
|
|
|
|
this.player.playbackRate(this.defaultPlayback)
|
2021-06-10 19:35:00 +00:00
|
|
|
this.player.textTrackSettings.setValues(this.defaultCaptionSettings)
|
2021-05-28 20:29:35 +00:00
|
|
|
// Remove big play button
|
|
|
|
// https://github.com/videojs/video.js/blob/v7.12.1/docs/guides/components.md#basic-example
|
2021-05-29 18:35:28 +00:00
|
|
|
if (!this.displayVideoPlayButton) {
|
|
|
|
this.player.removeChild('BigPlayButton')
|
|
|
|
}
|
2020-03-01 03:37:02 +00:00
|
|
|
|
2021-11-30 22:08:33 +00:00
|
|
|
// Makes the playback rate menu focus the current item on mouse hover
|
|
|
|
// or the closest item if the playback rate is between two items
|
|
|
|
// which is likely to be the case when the playback rate is changed by scrolling
|
|
|
|
const playbackRateMenuButton = this.player.controlBar.getChild('playbackRateMenuButton')
|
|
|
|
playbackRateMenuButton.on(playbackRateMenuButton.menuButton_, 'mouseenter', () => {
|
|
|
|
const playbackRate = this.player.playbackRate()
|
|
|
|
const rates = this.player.playbackRates()
|
|
|
|
|
|
|
|
// iterate through the items in reverse order as the highest is displayed first
|
|
|
|
// `slice` must be used as `reverse` does reversing in place
|
|
|
|
const targetPlaybackRateMenuItemIndex = rates.slice().reverse().findIndex((rate) => {
|
|
|
|
return rate === playbackRate || rate < playbackRate
|
|
|
|
})
|
|
|
|
|
|
|
|
// center the selected item in the middle of the visible area
|
|
|
|
// the first and last items will never be in the center so it can be skipped for them
|
|
|
|
if (targetPlaybackRateMenuItemIndex !== 0 && targetPlaybackRateMenuItemIndex !== rates.length - 1) {
|
|
|
|
const playbackRateMenu = playbackRateMenuButton.menu
|
|
|
|
const menuElement = playbackRateMenu.contentEl()
|
|
|
|
|
|
|
|
const itemHeight = playbackRateMenu.children()[targetPlaybackRateMenuItemIndex].contentEl().clientHeight
|
|
|
|
|
|
|
|
// clientHeight is the height of the visible part of an element
|
|
|
|
const centerOfVisibleArea = (menuElement.clientHeight - itemHeight) / 2
|
|
|
|
const menuScrollOffset = (itemHeight * targetPlaybackRateMenuItemIndex) - centerOfVisibleArea
|
|
|
|
|
|
|
|
menuElement.scrollTo({ top: menuScrollOffset })
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-09-25 02:35:13 +00:00
|
|
|
if (this.storyboardSrc !== '') {
|
|
|
|
this.player.vttThumbnails({
|
|
|
|
src: this.storyboardSrc,
|
|
|
|
showTimestamp: true
|
|
|
|
})
|
|
|
|
}
|
2020-02-18 20:59:01 +00:00
|
|
|
|
2020-05-24 21:32:02 +00:00
|
|
|
if (this.useDash) {
|
2021-03-12 21:09:08 +00:00
|
|
|
// this.dataSetup.plugins.httpSourceSelector = {
|
|
|
|
// default: 'auto'
|
|
|
|
// }
|
2020-02-18 20:59:01 +00:00
|
|
|
|
2021-03-12 21:09:08 +00:00
|
|
|
// this.player.httpSourceSelector()
|
|
|
|
this.createDashQualitySelector(this.player.qualityLevels())
|
2020-02-18 20:59:01 +00:00
|
|
|
}
|
2020-02-20 20:58:21 +00:00
|
|
|
|
2020-03-01 03:37:02 +00:00
|
|
|
if (this.autoplayVideos) {
|
2020-02-20 20:58:21 +00:00
|
|
|
// Calling play() won't happen right away, so a quick timeout will make it function properly.
|
|
|
|
setTimeout(() => {
|
|
|
|
this.player.play()
|
2020-05-24 21:32:02 +00:00
|
|
|
}, 200)
|
2020-02-20 20:58:21 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 22:46:42 +00:00
|
|
|
// Remove built-in progress bar mouse over current time display
|
|
|
|
// `MouseTimeDisplay` in
|
|
|
|
// https://github.com/videojs/video.js/blob/v7.13.3/docs/guides/components.md#default-component-tree
|
|
|
|
this.player.controlBar.progressControl.seekBar.playProgressBar.removeChild('timeTooltip')
|
|
|
|
|
2021-05-20 03:00:39 +00:00
|
|
|
if (this.useSponsorBlock) {
|
|
|
|
this.initializeSponsorBlock()
|
|
|
|
}
|
|
|
|
|
2020-12-14 23:25:51 +00:00
|
|
|
$(document).on('keydown', this.keyboardShortcutHandler)
|
2020-02-21 18:31:32 +00:00
|
|
|
|
|
|
|
this.player.on('mousemove', this.hideMouseTimeout)
|
|
|
|
this.player.on('mouseleave', this.removeMouseTimeout)
|
2020-10-27 19:20:41 +00:00
|
|
|
|
2020-03-01 03:37:02 +00:00
|
|
|
this.player.on('volumechange', this.updateVolume)
|
2021-05-26 15:55:11 +00:00
|
|
|
if (this.videoVolumeMouseScroll) {
|
|
|
|
this.player.on('wheel', this.mouseScrollVolume)
|
|
|
|
} else {
|
|
|
|
this.player.controlBar.getChild('volumePanel').on('wheel', this.mouseScrollVolume)
|
|
|
|
}
|
2020-02-21 18:31:32 +00:00
|
|
|
|
2021-11-24 21:52:56 +00:00
|
|
|
if (this.videoPlaybackRateMouseScroll) {
|
|
|
|
this.player.on('wheel', this.mouseScrollPlaybackRate)
|
|
|
|
// Removes the 'out-of-the-box' click event and adds a custom click event so that a user can
|
|
|
|
// ctrl-click (or command+click on a mac) without toggling play/pause
|
|
|
|
this.player.el_.firstChild.style.pointerEvents = 'none'
|
|
|
|
this.player.on('click', this.handlePlayerClick)
|
|
|
|
}
|
|
|
|
|
2020-11-15 19:13:18 +00:00
|
|
|
this.player.on('fullscreenchange', this.fullscreenOverlay)
|
2021-04-22 18:41:50 +00:00
|
|
|
this.player.on('fullscreenchange', this.toggleFullscreenClass)
|
2020-11-15 19:13:18 +00:00
|
|
|
|
2021-05-21 23:52:11 +00:00
|
|
|
this.player.on('ready', () => {
|
|
|
|
this.$emit('ready')
|
|
|
|
this.checkAspectRatio()
|
|
|
|
if (this.captionHybridList.length !== 0) {
|
|
|
|
this.transformAndInsertCaptions()
|
2021-03-17 01:28:25 +00:00
|
|
|
}
|
2020-08-20 02:39:44 +00:00
|
|
|
})
|
|
|
|
|
2021-05-21 23:52:11 +00:00
|
|
|
this.player.on('ended', () => {
|
|
|
|
this.$emit('ended')
|
2020-05-17 20:12:58 +00:00
|
|
|
})
|
|
|
|
|
2021-05-21 23:52:11 +00:00
|
|
|
this.player.on('error', (error, message) => {
|
|
|
|
this.$emit('error', error.target.player.error_)
|
2020-02-21 18:31:32 +00:00
|
|
|
})
|
2021-01-11 20:45:46 +00:00
|
|
|
|
2021-05-21 23:49:48 +00:00
|
|
|
this.player.on('play', async function () {
|
2021-01-11 20:45:46 +00:00
|
|
|
if (this.usingElectron) {
|
2021-05-21 23:49:48 +00:00
|
|
|
const { ipcRenderer } = require('electron')
|
|
|
|
this.powerSaveBlocker =
|
|
|
|
await ipcRenderer.invoke('startPowerSaveBlocker', 'prevent-display-sleep')
|
2021-01-11 20:45:46 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
this.player.on('pause', function () {
|
|
|
|
if (this.usingElectron && this.powerSaveBlocker !== null) {
|
2021-05-21 23:49:48 +00:00
|
|
|
const { ipcRenderer } = require('electron')
|
|
|
|
ipcRenderer.send('stopPowerSaveBlocker', this.powerSaveBlocker)
|
2021-01-11 20:45:46 +00:00
|
|
|
this.powerSaveBlocker = null
|
|
|
|
}
|
|
|
|
})
|
2021-06-10 19:35:00 +00:00
|
|
|
|
|
|
|
this.player.textTrackSettings.on('modalclose', (_) => {
|
|
|
|
const settings = this.player.textTrackSettings.getValues()
|
|
|
|
this.updateDefaultCaptionSettings(JSON.stringify(settings))
|
|
|
|
})
|
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
2021-11-23 11:34:04 +00:00
|
|
|
this.addPlayerStatsEvent()
|
2020-02-16 18:30:00 +00:00
|
|
|
}
|
2021-05-16 20:01:24 +00:00
|
|
|
},
|
|
|
|
|
2021-05-20 03:00:39 +00:00
|
|
|
initializeSponsorBlock() {
|
2021-05-21 23:56:32 +00:00
|
|
|
this.sponsorBlockSkipSegments({
|
2021-05-20 03:00:39 +00:00
|
|
|
videoId: this.videoId,
|
|
|
|
categories: ['sponsor']
|
|
|
|
}).then((skipSegments) => {
|
|
|
|
if (skipSegments.length === 0) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.player.ready(() => {
|
2021-05-16 20:01:24 +00:00
|
|
|
this.player.on('timeupdate', () => {
|
|
|
|
this.skipSponsorBlocks(skipSegments)
|
|
|
|
})
|
2021-05-20 03:00:39 +00:00
|
|
|
|
2021-05-16 20:01:24 +00:00
|
|
|
skipSegments.forEach(({
|
|
|
|
category,
|
|
|
|
segment: [startTime, endTime]
|
|
|
|
}) => {
|
|
|
|
this.addSponsorBlockMarker({
|
|
|
|
time: startTime,
|
|
|
|
duration: endTime - startTime,
|
|
|
|
color: this.sponsorBlockCategoryColor(category)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2021-05-20 03:00:39 +00:00
|
|
|
})
|
2021-05-16 20:01:24 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
skipSponsorBlocks(skipSegments) {
|
|
|
|
const currentTime = this.player.currentTime()
|
2021-05-20 03:00:39 +00:00
|
|
|
const duration = this.player.duration()
|
2021-05-16 20:01:24 +00:00
|
|
|
let newTime = null
|
|
|
|
let skippedCategory = null
|
|
|
|
skipSegments.forEach(({ category, segment: [startTime, endTime] }) => {
|
|
|
|
if (startTime <= currentTime && currentTime < endTime) {
|
|
|
|
newTime = endTime
|
|
|
|
skippedCategory = category
|
|
|
|
}
|
|
|
|
})
|
2021-05-20 03:00:39 +00:00
|
|
|
if (newTime !== null && Math.abs(duration - currentTime) > 0.500) {
|
2021-05-16 20:01:24 +00:00
|
|
|
if (this.sponsorBlockShowSkippedToast) {
|
|
|
|
this.showSkippedSponsorSegmentInformation(skippedCategory)
|
|
|
|
}
|
|
|
|
this.player.currentTime(newTime)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
showSkippedSponsorSegmentInformation(category) {
|
|
|
|
const translatedCategory = this.sponsorBlockTranslatedCategory(category)
|
|
|
|
this.showToast({
|
|
|
|
message: `${this.$t('Video.Skipped segment')} ${translatedCategory}`
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
sponsorBlockTranslatedCategory(category) {
|
|
|
|
switch (category) {
|
|
|
|
case 'sponsor':
|
|
|
|
return this.$t('Video.Sponsor Block category.sponsor')
|
|
|
|
case 'intro':
|
|
|
|
return this.$t('Video.Sponsor Block category.intro')
|
|
|
|
case 'outro':
|
|
|
|
return this.$t('Video.Sponsor Block category.outro')
|
|
|
|
case 'selfpromo':
|
|
|
|
return this.$t('Video.Sponsor Block category.self-promotion')
|
|
|
|
case 'interaction':
|
|
|
|
return this.$t('Video.Sponsor Block category.interaction')
|
|
|
|
case 'music_offtopic':
|
|
|
|
return this.$t('Video.Sponsor Block category.music offtopic')
|
|
|
|
default:
|
|
|
|
console.error(`Unknown translation for SponsorBlock category ${category}`)
|
|
|
|
return category
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
sponsorBlockCategoryColor(category) {
|
|
|
|
// TODO: allow to set these colors in settings
|
|
|
|
switch (category) {
|
|
|
|
case 'sponsor':
|
2021-05-17 19:05:42 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
case 'intro':
|
2021-05-17 19:05:42 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
case 'outro':
|
2021-05-17 19:05:42 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
case 'selfpromo':
|
2021-05-17 19:05:42 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
case 'interaction':
|
2021-05-17 19:05:42 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
case 'music_offtopic':
|
2021-05-17 19:05:42 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
default:
|
|
|
|
console.error(`Unknown SponsorBlock category ${category}`)
|
2021-05-17 19:06:10 +00:00
|
|
|
return 'var(--accent-color)'
|
2021-05-16 20:01:24 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
addSponsorBlockMarker(marker) {
|
|
|
|
const markerDiv = videojs.dom.createEl('div', {}, {})
|
|
|
|
|
|
|
|
markerDiv.className = 'sponsorBlockMarker'
|
|
|
|
markerDiv.style.height = '100%'
|
|
|
|
markerDiv.style.position = 'absolute'
|
|
|
|
markerDiv.style['background-color'] = marker.color
|
|
|
|
markerDiv.style.width = (marker.duration / this.player.duration()) * 100 + '%'
|
|
|
|
markerDiv.style.marginLeft = (marker.time / this.player.duration()) * 100 + '%'
|
|
|
|
|
|
|
|
this.player.el().querySelector('.vjs-progress-holder').appendChild(markerDiv)
|
2020-02-18 20:59:01 +00:00
|
|
|
},
|
|
|
|
|
2021-01-15 04:20:42 +00:00
|
|
|
checkAspectRatio() {
|
|
|
|
const videoWidth = this.player.videoWidth()
|
|
|
|
const videoHeight = this.player.videoHeight()
|
|
|
|
|
|
|
|
if (videoWidth === 0 || videoHeight === 0) {
|
|
|
|
setTimeout(() => {
|
|
|
|
this.checkAspectRatio()
|
|
|
|
}, 200)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-01-21 15:28:45 +00:00
|
|
|
if ((videoWidth - videoHeight) <= 240) {
|
2021-01-15 04:20:42 +00:00
|
|
|
this.player.fluid(false)
|
|
|
|
this.player.aspectRatio('16:9')
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-06-14 17:15:46 +00:00
|
|
|
updateVolume: function (_event) {
|
|
|
|
// 0 means muted
|
|
|
|
// https://docs.videojs.com/html5#volume
|
|
|
|
const volume = this.player.muted() ? 0 : this.player.volume()
|
2020-03-01 03:37:02 +00:00
|
|
|
sessionStorage.setItem('volume', volume)
|
|
|
|
},
|
|
|
|
|
2020-10-27 19:20:41 +00:00
|
|
|
mouseScrollVolume: function (event) {
|
2021-09-05 19:39:44 +00:00
|
|
|
if (event.target && !event.currentTarget.querySelector('.vjs-menu:hover')) {
|
2020-10-27 19:20:41 +00:00
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
if (this.player.muted() && event.wheelDelta > 0) {
|
|
|
|
this.player.muted(false)
|
|
|
|
this.player.volume(0)
|
|
|
|
}
|
|
|
|
|
2021-11-24 21:52:56 +00:00
|
|
|
if (!event.ctrlKey && !event.metaKey) {
|
|
|
|
if (!this.player.muted()) {
|
|
|
|
if (event.wheelDelta > 0) {
|
|
|
|
this.changeVolume(0.05)
|
|
|
|
} else if (event.wheelDelta < 0) {
|
|
|
|
this.changeVolume(-0.05)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
mouseScrollPlaybackRate: function (event) {
|
|
|
|
if (event.target && !event.currentTarget.querySelector('.vjs-menu:hover')) {
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
if (event.ctrlKey || event.metaKey) {
|
2020-10-27 19:20:41 +00:00
|
|
|
if (event.wheelDelta > 0) {
|
2021-11-24 21:52:56 +00:00
|
|
|
this.changePlayBackRate(0.05)
|
2020-10-27 19:20:41 +00:00
|
|
|
} else if (event.wheelDelta < 0) {
|
2021-11-24 21:52:56 +00:00
|
|
|
this.changePlayBackRate(-0.05)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
handlePlayerClick: function (event) {
|
|
|
|
if (event.target.matches('.ftVideoPlayer')) {
|
|
|
|
if (event.ctrlKey || event.metaKey) {
|
|
|
|
this.player.playbackRate(this.defaultPlayback)
|
|
|
|
} else {
|
|
|
|
if (this.player.paused() || !this.player.hasStarted()) {
|
|
|
|
this.player.play()
|
|
|
|
} else {
|
|
|
|
this.player.pause()
|
2020-10-27 19:20:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-02-18 20:59:01 +00:00
|
|
|
determineFormatType: function () {
|
2020-02-19 03:31:10 +00:00
|
|
|
if (this.format === 'dash') {
|
2020-02-18 20:59:01 +00:00
|
|
|
this.enableDashFormat()
|
|
|
|
} else {
|
|
|
|
this.enableLegacyFormat()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-11-13 22:29:41 +00:00
|
|
|
determineMaxFramerate: function() {
|
2020-11-15 18:46:18 +00:00
|
|
|
if (this.dashSrc.length === 0) {
|
|
|
|
this.maxFramerate = 60
|
|
|
|
return
|
|
|
|
}
|
2020-11-13 22:29:41 +00:00
|
|
|
fs.readFile(this.dashSrc[0].url, (err, data) => {
|
|
|
|
if (err) {
|
|
|
|
this.maxFramerate = 60
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (data.includes('frameRate="60"')) {
|
|
|
|
this.maxFramerate = 60
|
|
|
|
} else {
|
|
|
|
this.maxFramerate = 30
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
2020-09-26 21:03:42 +00:00
|
|
|
determineDefaultQualityLegacy: function () {
|
2020-05-24 21:46:26 +00:00
|
|
|
if (this.useDash) {
|
2020-09-25 02:35:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.sourceList.length === 0) {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof (this.sourceList[0].qualityLabel) === 'number') {
|
|
|
|
return ''
|
2020-05-24 21:46:26 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 01:20:30 +00:00
|
|
|
if (this.sourceList[this.sourceList.length - 1].qualityLabel === this.$t('Video.Audio.Low')) {
|
|
|
|
this.selectedDefaultQuality = this.sourceList[0].qualityLabel
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-09-26 21:03:42 +00:00
|
|
|
let defaultQuality = this.defaultQuality
|
|
|
|
|
|
|
|
if (defaultQuality === 'auto') {
|
|
|
|
defaultQuality = 720
|
|
|
|
}
|
|
|
|
|
2020-09-25 02:35:13 +00:00
|
|
|
let maxAvailableQuality = parseInt(this.sourceList[this.sourceList.length - 1].qualityLabel.replace(/p|k/, ''))
|
|
|
|
|
|
|
|
if (maxAvailableQuality === 4) {
|
|
|
|
maxAvailableQuality = 2160
|
|
|
|
}
|
|
|
|
|
|
|
|
if (maxAvailableQuality === 8) {
|
|
|
|
maxAvailableQuality = 4320
|
|
|
|
}
|
|
|
|
|
2020-09-26 21:03:42 +00:00
|
|
|
if (maxAvailableQuality < defaultQuality) {
|
2020-09-25 02:35:13 +00:00
|
|
|
this.selectedDefaultQuality = this.sourceList[this.sourceList.length - 1].qualityLabel
|
|
|
|
}
|
|
|
|
|
|
|
|
const reversedList = [].concat(this.sourceList).reverse()
|
|
|
|
|
|
|
|
reversedList.forEach((source, index) => {
|
|
|
|
let qualityNumber = parseInt(source.qualityLabel.replace(/p|k/, ''))
|
|
|
|
if (qualityNumber === 4) {
|
|
|
|
qualityNumber = 2160
|
2020-05-17 20:12:58 +00:00
|
|
|
}
|
2020-09-25 02:35:13 +00:00
|
|
|
if (qualityNumber === 8) {
|
|
|
|
qualityNumber = 4320
|
|
|
|
}
|
|
|
|
|
2020-09-26 21:03:42 +00:00
|
|
|
if (defaultQuality === qualityNumber) {
|
2020-09-26 16:10:56 +00:00
|
|
|
this.selectedDefaultQuality = source.qualityLabel
|
|
|
|
}
|
|
|
|
|
2020-09-25 02:35:13 +00:00
|
|
|
if (index < (this.sourceList.length - 1)) {
|
|
|
|
let upperQualityNumber = parseInt(reversedList[index + 1].qualityLabel.replace(/p|k/, ''))
|
|
|
|
if (upperQualityNumber === 4) {
|
|
|
|
upperQualityNumber = 2160
|
|
|
|
}
|
|
|
|
if (upperQualityNumber === 8) {
|
|
|
|
upperQualityNumber = 4320
|
|
|
|
}
|
2020-09-26 21:03:42 +00:00
|
|
|
if (defaultQuality >= qualityNumber && defaultQuality < upperQualityNumber) {
|
2020-09-25 02:35:13 +00:00
|
|
|
this.selectedDefaultQuality = source.qualityLabel
|
|
|
|
}
|
2020-09-26 21:03:42 +00:00
|
|
|
} else if (qualityNumber <= defaultQuality) {
|
2020-09-25 02:35:13 +00:00
|
|
|
this.selectedDefaultQuality = source.qualityLabel
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (this.selectedDefaultQuality === '') {
|
|
|
|
this.selectedDefaultQuality = this.sourceList[this.sourceList.length - 1].qualityLabel
|
2020-05-17 20:12:58 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-09-26 21:03:42 +00:00
|
|
|
determineDefaultQualityDash: function () {
|
|
|
|
if (this.defaultQuality === 'auto') {
|
2021-03-12 21:09:08 +00:00
|
|
|
this.setDashQualityLevel('auto')
|
2020-09-26 21:03:42 +00:00
|
|
|
}
|
|
|
|
|
2021-05-17 01:40:34 +00:00
|
|
|
let formatsToTest
|
2021-05-15 19:08:41 +00:00
|
|
|
|
2021-05-17 01:40:34 +00:00
|
|
|
if (typeof this.activeAdaptiveFormats !== 'undefined' && this.activeAdaptiveFormats.length > 0) {
|
2021-05-15 19:08:41 +00:00
|
|
|
formatsToTest = this.activeAdaptiveFormats.filter((format) => {
|
2021-05-17 01:40:34 +00:00
|
|
|
return format.height === this.defaultQuality
|
2021-05-15 19:08:41 +00:00
|
|
|
})
|
|
|
|
|
2021-05-17 01:40:34 +00:00
|
|
|
if (formatsToTest.length === 0) {
|
|
|
|
formatsToTest = this.activeAdaptiveFormats.filter((format) => {
|
|
|
|
return format.height < this.defaultQuality
|
|
|
|
})
|
2021-05-15 19:08:41 +00:00
|
|
|
}
|
2021-05-17 01:40:34 +00:00
|
|
|
|
|
|
|
formatsToTest = formatsToTest.sort((a, b) => {
|
|
|
|
if (a.height === b.height) {
|
|
|
|
return b.bitrate - a.bitrate
|
|
|
|
} else {
|
|
|
|
return b.height - a.height
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
formatsToTest = this.player.qualityLevels().levels_.filter((format) => {
|
|
|
|
return format.height === this.defaultQuality
|
|
|
|
})
|
|
|
|
|
|
|
|
if (formatsToTest.length === 0) {
|
|
|
|
formatsToTest = this.player.qualityLevels().levels_.filter((format) => {
|
|
|
|
return format.height < this.defaultQuality
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
formatsToTest = formatsToTest.sort((a, b) => {
|
|
|
|
if (a.height === b.height) {
|
|
|
|
return b.bitrate - a.bitrate
|
|
|
|
} else {
|
|
|
|
return b.height - a.height
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2021-05-15 19:08:41 +00:00
|
|
|
|
2021-05-15 19:13:17 +00:00
|
|
|
// TODO: Test formats to determine if HDR / 60 FPS and skip them based on
|
|
|
|
// User settings
|
2021-05-15 19:08:41 +00:00
|
|
|
this.setDashQualityLevel(formatsToTest[0].bitrate)
|
|
|
|
|
|
|
|
// Old logic. Revert if needed
|
|
|
|
/* this.player.qualityLevels().levels_.sort((a, b) => {
|
2021-04-30 21:18:45 +00:00
|
|
|
if (a.height === b.height) {
|
|
|
|
return a.bitrate - b.bitrate
|
|
|
|
} else {
|
|
|
|
return a.height - b.height
|
|
|
|
}
|
2020-09-26 21:03:42 +00:00
|
|
|
}).forEach((ql, index, arr) => {
|
|
|
|
const height = ql.height
|
|
|
|
const width = ql.width
|
|
|
|
const quality = width < height ? width : height
|
|
|
|
let upperLevel = null
|
|
|
|
|
|
|
|
if (index < arr.length - 1) {
|
|
|
|
upperLevel = arr[index + 1]
|
|
|
|
}
|
|
|
|
|
2021-04-30 21:18:45 +00:00
|
|
|
if (this.defaultQuality === quality && upperLevel === null) {
|
|
|
|
this.setDashQualityLevel(height, true)
|
2020-09-26 21:03:42 +00:00
|
|
|
} else if (upperLevel !== null) {
|
|
|
|
const upperHeight = upperLevel.height
|
|
|
|
const upperWidth = upperLevel.width
|
|
|
|
const upperQuality = upperWidth < upperHeight ? upperWidth : upperHeight
|
|
|
|
|
2021-04-30 21:18:45 +00:00
|
|
|
if (this.defaultQuality >= quality && this.defaultQuality === upperQuality) {
|
|
|
|
this.setDashQualityLevel(height, true)
|
|
|
|
} else if (this.defaultQuality >= quality && this.defaultQuality < upperQuality) {
|
2021-03-12 21:09:08 +00:00
|
|
|
this.setDashQualityLevel(height)
|
2020-09-26 21:03:42 +00:00
|
|
|
}
|
|
|
|
} else if (index === 0 && quality > this.defaultQuality) {
|
2021-03-12 21:09:08 +00:00
|
|
|
this.setDashQualityLevel(height)
|
2020-09-26 21:03:42 +00:00
|
|
|
} else if (index === (arr.length - 1) && quality < this.defaultQuality) {
|
2021-03-12 21:09:08 +00:00
|
|
|
this.setDashQualityLevel(height)
|
|
|
|
}
|
2021-05-15 19:08:41 +00:00
|
|
|
}) */
|
2021-03-12 21:09:08 +00:00
|
|
|
},
|
|
|
|
|
2021-05-15 19:08:41 +00:00
|
|
|
setDashQualityLevel: function (bitrate) {
|
|
|
|
let adaptiveFormat = null
|
|
|
|
|
|
|
|
if (bitrate !== 'auto') {
|
|
|
|
adaptiveFormat = this.activeAdaptiveFormats.find((format) => {
|
|
|
|
return format.bitrate === bitrate
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-05-17 01:40:34 +00:00
|
|
|
let qualityLabel = adaptiveFormat ? adaptiveFormat.qualityLabel : ''
|
|
|
|
|
2021-05-15 19:08:41 +00:00
|
|
|
this.player.qualityLevels().levels_.sort((a, b) => {
|
|
|
|
if (a.height === b.height) {
|
|
|
|
return a.bitrate - b.bitrate
|
|
|
|
} else {
|
|
|
|
return a.height - b.height
|
|
|
|
}
|
|
|
|
}).forEach((ql, index, arr) => {
|
|
|
|
if (bitrate === 'auto' || bitrate === ql.bitrate) {
|
|
|
|
ql.enabled = true
|
|
|
|
ql.enabled_(true)
|
2021-05-17 01:40:34 +00:00
|
|
|
if (bitrate !== 'auto' && qualityLabel === '') {
|
|
|
|
qualityLabel = ql.height + 'p'
|
|
|
|
}
|
2021-05-15 19:08:41 +00:00
|
|
|
} else {
|
|
|
|
ql.enabled = false
|
|
|
|
ql.enabled_(false)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-05-17 01:40:34 +00:00
|
|
|
const selectedQuality = bitrate === 'auto' ? 'auto' : qualityLabel
|
2021-05-15 19:08:41 +00:00
|
|
|
|
|
|
|
const qualityElement = document.getElementById('vjs-current-quality')
|
|
|
|
qualityElement.innerText = selectedQuality
|
|
|
|
this.selectedQuality = selectedQuality
|
|
|
|
|
|
|
|
const qualityItems = $('.quality-item').get()
|
|
|
|
|
|
|
|
$('.quality-item').removeClass('quality-selected')
|
|
|
|
|
|
|
|
qualityItems.forEach((item) => {
|
|
|
|
const qualityText = $(item).find('.vjs-menu-item-text').get(0)
|
|
|
|
if (qualityText.innerText === selectedQuality.toLowerCase()) {
|
|
|
|
$(item).addClass('quality-selected')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
/* if (this.selectedQuality === qualityLevel && this.using60Fps === is60Fps) {
|
2021-03-12 21:09:08 +00:00
|
|
|
return
|
|
|
|
}
|
2021-04-30 21:18:45 +00:00
|
|
|
let foundSelectedQuality = false
|
|
|
|
this.using60Fps = is60Fps
|
2021-03-12 21:09:08 +00:00
|
|
|
this.player.qualityLevels().levels_.sort((a, b) => {
|
2021-04-30 21:18:45 +00:00
|
|
|
if (a.height === b.height) {
|
|
|
|
return a.bitrate - b.bitrate
|
|
|
|
} else {
|
|
|
|
return a.height - b.height
|
|
|
|
}
|
2021-03-12 21:09:08 +00:00
|
|
|
}).forEach((ql, index, arr) => {
|
2021-04-30 21:18:45 +00:00
|
|
|
if (foundSelectedQuality) {
|
|
|
|
ql.enabled = false
|
|
|
|
ql.enabled_(false)
|
|
|
|
} else if (qualityLevel === 'auto') {
|
|
|
|
ql.enabled = true
|
|
|
|
ql.enabled_(true)
|
|
|
|
} else if (ql.height === qualityLevel) {
|
2020-09-26 21:03:42 +00:00
|
|
|
ql.enabled = true
|
2021-03-12 21:09:08 +00:00
|
|
|
ql.enabled_(true)
|
2021-04-30 21:18:45 +00:00
|
|
|
foundSelectedQuality = true
|
|
|
|
|
|
|
|
let lowerQuality
|
|
|
|
let higherQuality
|
|
|
|
|
|
|
|
if ((index - 1) !== -1) {
|
|
|
|
lowerQuality = arr[index - 1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((index + 1) < arr.length) {
|
|
|
|
higherQuality = arr[index + 1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof (lowerQuality) !== 'undefined' && lowerQuality.height === ql.height && lowerQuality.bitrate < ql.bitrate && !is60Fps) {
|
|
|
|
ql.enabled = false
|
|
|
|
ql.enabled_(false)
|
|
|
|
foundSelectedQuality = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof (higherQuality) !== 'undefined' && higherQuality.height === ql.height && higherQuality.bitrate > ql.bitrate && is60Fps) {
|
|
|
|
ql.enabled = false
|
|
|
|
ql.enabled_(false)
|
|
|
|
foundSelectedQuality = false
|
|
|
|
}
|
2020-09-26 21:03:42 +00:00
|
|
|
} else {
|
|
|
|
ql.enabled = false
|
2021-03-12 21:09:08 +00:00
|
|
|
ql.enabled_(false)
|
2020-09-26 21:03:42 +00:00
|
|
|
}
|
|
|
|
})
|
2021-03-12 21:09:08 +00:00
|
|
|
|
2021-04-30 21:18:45 +00:00
|
|
|
let selectedQuality = qualityLevel
|
|
|
|
|
|
|
|
if (selectedQuality !== 'auto' && is60Fps) {
|
|
|
|
selectedQuality = selectedQuality + 'p60'
|
|
|
|
} else if (selectedQuality !== 'auto') {
|
|
|
|
selectedQuality = selectedQuality + 'p'
|
|
|
|
}
|
2021-03-12 21:09:08 +00:00
|
|
|
|
|
|
|
const qualityElement = document.getElementById('vjs-current-quality')
|
|
|
|
qualityElement.innerText = selectedQuality
|
|
|
|
this.selectedQuality = qualityLevel
|
|
|
|
|
|
|
|
const qualityItems = $('.quality-item').get()
|
|
|
|
|
|
|
|
$('.quality-item').removeClass('quality-selected')
|
|
|
|
|
|
|
|
qualityItems.forEach((item) => {
|
|
|
|
const qualityText = $(item).find('.vjs-menu-item-text').get(0)
|
|
|
|
if (qualityText.innerText === selectedQuality) {
|
|
|
|
$(item).addClass('quality-selected')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// const currentTime = this.player.currentTime()
|
|
|
|
|
|
|
|
// this.player.currentTime(0)
|
2021-05-15 19:08:41 +00:00
|
|
|
// this.player.currentTime(currentTime) */
|
2020-09-26 21:03:42 +00:00
|
|
|
},
|
|
|
|
|
2020-02-18 20:59:01 +00:00
|
|
|
enableDashFormat: function () {
|
|
|
|
if (this.dashSrc === null) {
|
|
|
|
console.log('No dash format available.')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.useDash = true
|
|
|
|
this.useHls = false
|
|
|
|
this.activeSourceList = this.dashSrc
|
|
|
|
|
2020-04-22 02:59:09 +00:00
|
|
|
setTimeout(this.initializePlayer, 100)
|
2020-02-18 20:59:01 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
enableLegacyFormat: function () {
|
|
|
|
if (this.sourceList.length === 0) {
|
|
|
|
console.log('No sources available')
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.useDash = false
|
|
|
|
this.useHls = false
|
|
|
|
this.activeSourceList = this.sourceList
|
|
|
|
|
|
|
|
setTimeout(this.initializePlayer, 100)
|
2020-02-20 20:58:21 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
togglePlayPause: function () {
|
|
|
|
if (this.player.paused()) {
|
|
|
|
this.player.play()
|
|
|
|
} else {
|
|
|
|
this.player.pause()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
changeDurationBySeconds: function (seconds) {
|
|
|
|
const currentTime = this.player.currentTime()
|
|
|
|
const newTime = currentTime + seconds
|
|
|
|
|
|
|
|
if (newTime < 0) {
|
|
|
|
this.player.currentTime(0)
|
|
|
|
} else if (newTime > this.player.duration) {
|
|
|
|
this.player.currentTime(this.player.duration)
|
|
|
|
} else {
|
|
|
|
this.player.currentTime(newTime)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
changeDurationByPercentage: function (percentage) {
|
|
|
|
const duration = this.player.duration()
|
|
|
|
const newTime = duration * percentage
|
|
|
|
|
|
|
|
this.player.currentTime(newTime)
|
|
|
|
},
|
|
|
|
|
|
|
|
changePlayBackRate: function (rate) {
|
2021-11-24 21:52:56 +00:00
|
|
|
const newPlaybackRate = (this.player.playbackRate() + rate).toFixed(2)
|
2020-02-20 20:58:21 +00:00
|
|
|
|
2021-11-24 21:52:56 +00:00
|
|
|
if (newPlaybackRate >= 0.25 && newPlaybackRate <= 8) {
|
2020-02-20 20:58:21 +00:00
|
|
|
this.player.playbackRate(newPlaybackRate)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-11-13 22:29:41 +00:00
|
|
|
framebyframe: function (step) {
|
|
|
|
this.player.pause()
|
2021-10-07 11:44:39 +00:00
|
|
|
const quality = this.useDash ? this.player.qualityLevels()[this.player.qualityLevels().selectedIndex] : {}
|
|
|
|
let fps = 30
|
2020-11-13 22:29:41 +00:00
|
|
|
// Non-Dash formats are 30fps only
|
2021-10-07 11:44:39 +00:00
|
|
|
if (this.maxFramerate === 60 && quality.height >= 480) {
|
|
|
|
for (let i = 0; i < this.adaptiveFormats.length; i++) {
|
|
|
|
if (this.adaptiveFormats[i].bitrate === quality.bitrate) {
|
|
|
|
fps = this.adaptiveFormats[i].fps
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-11-13 22:29:41 +00:00
|
|
|
}
|
2021-10-07 11:44:39 +00:00
|
|
|
|
2020-11-13 22:29:41 +00:00
|
|
|
// The 3 lines below were taken from the videojs-framebyframe node module by Helena Rasche
|
|
|
|
const frameTime = 1 / fps
|
|
|
|
const dist = frameTime * step
|
|
|
|
this.player.currentTime(this.player.currentTime() + dist)
|
2021-10-07 11:44:39 +00:00
|
|
|
console.log(fps)
|
2020-11-13 22:29:41 +00:00
|
|
|
},
|
|
|
|
|
2020-02-20 20:58:21 +00:00
|
|
|
changeVolume: function (volume) {
|
|
|
|
const currentVolume = this.player.volume()
|
|
|
|
const newVolume = currentVolume + volume
|
|
|
|
|
|
|
|
if (newVolume < 0) {
|
|
|
|
this.player.volume(0)
|
|
|
|
} else if (newVolume > 1) {
|
|
|
|
this.player.volume(1)
|
|
|
|
} else {
|
|
|
|
this.player.volume(newVolume)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleMute: function () {
|
|
|
|
if (this.player.muted()) {
|
|
|
|
this.player.muted(false)
|
|
|
|
} else {
|
|
|
|
this.player.muted(true)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleCaptions: function () {
|
|
|
|
const tracks = this.player.textTracks().tracks_
|
|
|
|
|
|
|
|
if (tracks.length > 1) {
|
|
|
|
if (tracks[1].mode === 'showing') {
|
|
|
|
tracks[1].mode = 'disabled'
|
|
|
|
} else {
|
|
|
|
tracks[1].mode = 'showing'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-01-13 03:56:31 +00:00
|
|
|
createLoopButton: function () {
|
|
|
|
const VjsButton = videojs.getComponent('Button')
|
|
|
|
const loopButton = videojs.extend(VjsButton, {
|
|
|
|
constructor: function(player, options) {
|
|
|
|
VjsButton.call(this, player, options)
|
|
|
|
},
|
2021-05-21 23:52:11 +00:00
|
|
|
handleClick: () => {
|
|
|
|
this.toggleVideoLoop()
|
2021-01-13 03:56:31 +00:00
|
|
|
},
|
|
|
|
createControlTextEl: function (button) {
|
|
|
|
return $(button).html($('<div id="loopButton" class="vjs-icon-loop loop-white vjs-button loopWhite"></div>')
|
|
|
|
.attr('title', 'Toggle Loop'))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
videojs.registerComponent('loopButton', loopButton)
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleVideoLoop: async function () {
|
|
|
|
if (!this.player.loop()) {
|
|
|
|
const currentTheme = localStorage.getItem('mainColor')
|
|
|
|
const colorNames = this.$store.state.utils.colorClasses
|
|
|
|
const colorValues = this.$store.state.utils.colorValues
|
|
|
|
|
|
|
|
const nameIndex = colorNames.findIndex((color) => {
|
|
|
|
return color === currentTheme
|
|
|
|
})
|
|
|
|
|
|
|
|
const themeTextColor = await this.calculateColorLuminance(colorValues[nameIndex])
|
|
|
|
|
|
|
|
$('#loopButton').addClass('vjs-icon-loop-active')
|
|
|
|
|
|
|
|
if (themeTextColor === '#000000') {
|
|
|
|
$('#loopButton').addClass('loop-black')
|
|
|
|
$('#loopButton').removeClass('loop-white')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.player.loop(true)
|
|
|
|
} else {
|
|
|
|
$('#loopButton').removeClass('vjs-icon-loop-active')
|
|
|
|
$('#loopButton').removeClass('loop-black')
|
|
|
|
$('#loopButton').addClass('loop-white')
|
|
|
|
this.player.loop(false)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
createFullWindowButton: function () {
|
2020-12-14 22:37:58 +00:00
|
|
|
const VjsButton = videojs.getComponent('Button')
|
|
|
|
const fullWindowButton = videojs.extend(VjsButton, {
|
|
|
|
constructor: function(player, options) {
|
|
|
|
VjsButton.call(this, player, options)
|
|
|
|
},
|
2021-05-21 23:52:11 +00:00
|
|
|
handleClick: () => {
|
|
|
|
this.toggleFullWindow()
|
2020-12-14 22:37:58 +00:00
|
|
|
},
|
|
|
|
createControlTextEl: function (button) {
|
2021-04-22 18:41:50 +00:00
|
|
|
// Add class name to button to be able to target it with CSS selector
|
|
|
|
return $(button)
|
|
|
|
.addClass('vjs-button-fullwindow')
|
|
|
|
.html($('<div id="fullwindow" class="vjs-icon-fullwindow-enter vjs-button"></div>')
|
|
|
|
.attr('title', 'Full Window'))
|
2020-12-14 22:37:58 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
videojs.registerComponent('fullWindowButton', fullWindowButton)
|
|
|
|
},
|
|
|
|
|
2021-08-24 07:36:10 +00:00
|
|
|
createToggleTheatreModeButton: function() {
|
|
|
|
if (!this.$parent.theatrePossible) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-02 18:22:06 +00:00
|
|
|
const theatreModeActive = this.$parent.useTheatreMode ? ' vjs-icon-theatre-active' : ''
|
2021-08-24 07:36:10 +00:00
|
|
|
|
|
|
|
const VjsButton = videojs.getComponent('Button')
|
|
|
|
const toggleTheatreModeButton = videojs.extend(VjsButton, {
|
|
|
|
constructor: function(player, options) {
|
|
|
|
VjsButton.call(this, player, options)
|
|
|
|
},
|
|
|
|
handleClick: () => {
|
|
|
|
this.toggleTheatreMode()
|
|
|
|
},
|
|
|
|
createControlTextEl: function (button) {
|
|
|
|
return $(button)
|
|
|
|
.addClass('vjs-button-theatre')
|
|
|
|
.html($(`<div id="toggleTheatreModeButton" class="vjs-icon-theatre-inactive${theatreModeActive} vjs-button"></div>`))
|
|
|
|
.attr('title', 'Toggle Theatre Mode')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
videojs.registerComponent('toggleTheatreModeButton', toggleTheatreModeButton)
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleTheatreMode: function() {
|
|
|
|
if (!this.player.isFullscreen_) {
|
|
|
|
const toggleTheatreModeButton = $('#toggleTheatreModeButton')
|
|
|
|
if (!this.$parent.useTheatreMode) {
|
|
|
|
toggleTheatreModeButton.addClass('vjs-icon-theatre-active')
|
|
|
|
} else {
|
|
|
|
toggleTheatreModeButton.removeClass('vjs-icon-theatre-active')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$parent.toggleTheatreMode()
|
|
|
|
},
|
|
|
|
|
2021-03-12 21:09:08 +00:00
|
|
|
createDashQualitySelector: function (levels) {
|
|
|
|
if (levels.levels_.length === 0) {
|
|
|
|
setTimeout(() => {
|
|
|
|
this.createDashQualitySelector(this.player.qualityLevels())
|
|
|
|
}, 200)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const VjsButton = videojs.getComponent('Button')
|
|
|
|
const dashQualitySelector = videojs.extend(VjsButton, {
|
|
|
|
constructor: function(player, options) {
|
|
|
|
VjsButton.call(this, player, options)
|
|
|
|
},
|
2021-05-21 23:52:11 +00:00
|
|
|
handleClick: (event) => {
|
2021-05-15 19:08:41 +00:00
|
|
|
console.log(event)
|
2021-03-12 21:09:08 +00:00
|
|
|
const selectedQuality = event.target.innerText
|
2021-05-15 19:08:41 +00:00
|
|
|
const bitrate = selectedQuality === 'auto' ? 'auto' : parseInt(event.target.attributes.bitrate.value)
|
2021-05-21 23:52:11 +00:00
|
|
|
this.setDashQualityLevel(bitrate)
|
2021-03-12 21:09:08 +00:00
|
|
|
},
|
2021-05-21 23:52:11 +00:00
|
|
|
createControlTextEl: (button) => {
|
2021-03-12 21:09:08 +00:00
|
|
|
const beginningHtml = `<div class="vjs-quality-level-value">
|
|
|
|
<span id="vjs-current-quality">1080p</span>
|
|
|
|
</div>
|
|
|
|
<div class="vjs-quality-level-menu vjs-menu">
|
|
|
|
<ul class="vjs-menu-content" role="menu">`
|
|
|
|
const endingHtml = '</ul></div>'
|
|
|
|
|
2021-05-15 19:08:41 +00:00
|
|
|
let qualityHtml = `<li class="vjs-menu-item quality-item" role="menuitemradio" tabindex="-1" aria-checked="false" aria-disabled="false">
|
2021-03-12 21:09:08 +00:00
|
|
|
<span class="vjs-menu-item-text">Auto</span>
|
|
|
|
<span class="vjs-control-text" aria-live="polite"></span>
|
|
|
|
</li>`
|
|
|
|
|
|
|
|
levels.levels_.sort((a, b) => {
|
2021-04-30 21:18:45 +00:00
|
|
|
if (b.height === a.height) {
|
|
|
|
return b.bitrate - a.bitrate
|
|
|
|
} else {
|
|
|
|
return b.height - a.height
|
|
|
|
}
|
|
|
|
}).forEach((quality, index, array) => {
|
2021-05-17 01:40:34 +00:00
|
|
|
let fps
|
|
|
|
let qualityLabel
|
|
|
|
let bitrate
|
2021-05-15 19:08:41 +00:00
|
|
|
|
2021-05-21 23:52:11 +00:00
|
|
|
if (typeof this.adaptiveFormats !== 'undefined' && this.adaptiveFormats.length > 0) {
|
|
|
|
const adaptiveFormat = this.adaptiveFormats.find((format) => {
|
2021-05-17 01:40:34 +00:00
|
|
|
return format.bitrate === quality.bitrate
|
|
|
|
})
|
2021-05-15 19:08:41 +00:00
|
|
|
|
2021-05-21 23:52:11 +00:00
|
|
|
this.activeAdaptiveFormats.push(adaptiveFormat)
|
2021-05-17 01:40:34 +00:00
|
|
|
|
2021-10-12 21:03:22 +00:00
|
|
|
fps = adaptiveFormat.fps ? adaptiveFormat.fps : 30
|
2021-05-17 01:40:34 +00:00
|
|
|
qualityLabel = adaptiveFormat.qualityLabel ? adaptiveFormat.qualityLabel : quality.height + 'p'
|
|
|
|
bitrate = quality.bitrate
|
|
|
|
} else {
|
|
|
|
fps = 30
|
|
|
|
qualityLabel = quality.height + 'p'
|
|
|
|
bitrate = quality.bitrate
|
|
|
|
}
|
2021-05-15 19:08:41 +00:00
|
|
|
|
|
|
|
qualityHtml = qualityHtml + `<li class="vjs-menu-item quality-item" role="menuitemradio" tabindex="-1" aria-checked="false" aria-disabled="false" fps="${fps}" bitrate="${bitrate}">
|
|
|
|
<span class="vjs-menu-item-text" fps="${fps}" bitrate="${bitrate}">${qualityLabel}</span>
|
|
|
|
<span class="vjs-control-text" aria-live="polite"></span>
|
|
|
|
</li>`
|
|
|
|
|
|
|
|
// Old logic, revert if needed.
|
|
|
|
/* let is60Fps = false
|
2021-04-30 21:18:45 +00:00
|
|
|
if (index < array.length - 1 && array[index + 1].height === quality.height) {
|
|
|
|
if (array[index + 1].bitrate < quality.bitrate) {
|
|
|
|
is60Fps = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const qualityText = is60Fps ? quality.height + 'p60' : quality.height + 'p'
|
2021-03-12 21:09:08 +00:00
|
|
|
qualityHtml = qualityHtml + `<li class="vjs-menu-item quality-item" role="menuitemradio" tabindex="-1" aria-checked="false aria-disabled="false">
|
2021-04-30 21:18:45 +00:00
|
|
|
<span class="vjs-menu-item-text">${qualityText}</span>
|
2021-03-12 21:09:08 +00:00
|
|
|
<span class="vjs-control-text" aria-live="polite"></span>
|
2021-05-15 19:08:41 +00:00
|
|
|
</li>` */
|
2021-03-12 21:09:08 +00:00
|
|
|
})
|
|
|
|
return $(button).html(
|
|
|
|
$(beginningHtml + qualityHtml + endingHtml).attr(
|
|
|
|
'title',
|
|
|
|
'Select Quality'
|
|
|
|
))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
videojs.registerComponent('dashQualitySelector', dashQualitySelector)
|
|
|
|
this.player.controlBar.addChild('dashQualitySelector', {}, this.player.controlBar.children_.length - 1)
|
|
|
|
this.determineDefaultQualityDash()
|
|
|
|
},
|
|
|
|
|
2021-09-20 02:12:14 +00:00
|
|
|
sortCaptions: function (captionList) {
|
|
|
|
return captionList.sort((captionA, captionB) => {
|
|
|
|
const aCode = captionA.languageCode.split('-') // ex. [en,US]
|
|
|
|
const bCode = captionB.languageCode.split('-')
|
|
|
|
const aName = (captionA.label || captionA.name.simpleText) // ex: english (auto-generated)
|
|
|
|
const bName = (captionB.label || captionB.name.simpleText)
|
|
|
|
const userLocale = this.currentLocale.split(/-|_/) // ex. [en,US]
|
|
|
|
if (aCode[0] === userLocale[0]) { // caption a has same language as user's locale
|
|
|
|
if (bCode[0] === userLocale[0]) { // caption b has same language as user's locale
|
|
|
|
if (bName.search('auto') !== -1) {
|
|
|
|
// prefer caption a: b is auto-generated captions
|
|
|
|
return -1
|
|
|
|
} else if (aName.search('auto') !== -1) {
|
|
|
|
// prefer caption b: a is auto-generated captions
|
|
|
|
return 1
|
|
|
|
} else if (aCode[1] === userLocale[1]) {
|
|
|
|
// prefer caption a: caption a has same county code as user's locale
|
|
|
|
return -1
|
|
|
|
} else if (bCode[1] === userLocale[1]) {
|
|
|
|
// prefer caption b: caption b has same county code as user's locale
|
|
|
|
return 1
|
|
|
|
} else if (aCode[1] === undefined) {
|
|
|
|
// prefer caption a: no country code is better than wrong country code
|
|
|
|
return -1
|
|
|
|
} else if (bCode[1] === undefined) {
|
|
|
|
// prefer caption b: no country code is better than wrong country code
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// prefer caption a: b does not match user's language
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
} else if (bCode[0] === userLocale[0]) {
|
|
|
|
// prefer caption b: a does not match user's language
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
// sort alphabetically
|
|
|
|
return aName.localeCompare(bName)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
2021-03-17 01:28:25 +00:00
|
|
|
transformAndInsertCaptions: async function() {
|
|
|
|
let captionList
|
|
|
|
if (this.captionHybridList[0] instanceof Promise) {
|
|
|
|
captionList = await Promise.all(this.captionHybridList)
|
|
|
|
this.$emit('store-caption-list', captionList)
|
|
|
|
} else {
|
|
|
|
captionList = this.captionHybridList
|
|
|
|
}
|
|
|
|
|
2021-09-20 02:12:14 +00:00
|
|
|
for (const caption of this.sortCaptions(captionList)) {
|
2021-03-17 01:28:25 +00:00
|
|
|
this.player.addRemoteTextTrack({
|
|
|
|
kind: 'subtitles',
|
|
|
|
src: caption.baseUrl || caption.url,
|
|
|
|
srclang: caption.languageCode,
|
|
|
|
label: caption.label || caption.name.simpleText,
|
|
|
|
type: caption.type
|
|
|
|
}, true)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-12-14 22:37:58 +00:00
|
|
|
toggleFullWindow: function() {
|
|
|
|
if (!this.player.isFullscreen_) {
|
|
|
|
if (this.player.isFullWindow) {
|
|
|
|
this.player.removeClass('vjs-full-screen')
|
|
|
|
this.player.isFullWindow = false
|
|
|
|
document.documentElement.style.overflow = this.player.docOrigOverflow
|
|
|
|
$('body').removeClass('vjs-full-window')
|
|
|
|
$('#fullwindow').removeClass('vjs-icon-fullwindow-exit')
|
|
|
|
this.player.trigger('exitFullWindow')
|
|
|
|
} else {
|
|
|
|
this.player.addClass('vjs-full-screen')
|
|
|
|
this.player.isFullscreen_ = false
|
|
|
|
this.player.isFullWindow = true
|
|
|
|
this.player.docOrigOverflow = document.documentElement.style.overflow
|
|
|
|
document.documentElement.style.overflow = 'hidden'
|
|
|
|
$('body').addClass('vjs-full-window')
|
|
|
|
$('#fullwindow').addClass('vjs-icon-fullwindow-exit')
|
|
|
|
this.player.trigger('enterFullWindow')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
exitFullWindow: function() {
|
|
|
|
if (this.player.isFullWindow) {
|
|
|
|
this.player.isFullWindow = false
|
|
|
|
document.documentElement.style.overflow = this.player.docOrigOverflow
|
|
|
|
this.player.removeClass('vjs-full-screen')
|
|
|
|
$('body').removeClass('vjs-full-window')
|
|
|
|
$('#fullwindow').removeClass('vjs-icon-fullwindow-exit')
|
|
|
|
this.player.trigger('exitFullWindow')
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
toggleFullscreen: function () {
|
|
|
|
if (this.player.isFullscreen()) {
|
|
|
|
this.player.exitFullscreen()
|
|
|
|
} else {
|
|
|
|
this.player.requestFullscreen()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-02-21 18:31:32 +00:00
|
|
|
hideMouseTimeout: function () {
|
|
|
|
if (this.id === '') {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const videoPlayer = $(`#${this.id} video`).get(0)
|
|
|
|
if (typeof (videoPlayer) !== 'undefined') {
|
|
|
|
videoPlayer.style.cursor = 'default'
|
|
|
|
clearTimeout(this.mouseTimeout)
|
|
|
|
this.mouseTimeout = window.setTimeout(function () {
|
|
|
|
videoPlayer.style.cursor = 'none'
|
|
|
|
}, 2650)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
removeMouseTimeout: function () {
|
|
|
|
if (this.mouseTimeout !== null) {
|
|
|
|
clearTimeout(this.mouseTimeout)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-11-15 19:13:18 +00:00
|
|
|
fullscreenOverlay: function () {
|
|
|
|
const title = document.title.replace('- FreeTube', '')
|
|
|
|
|
|
|
|
if (this.player.isFullscreen()) {
|
2021-05-21 23:52:11 +00:00
|
|
|
this.player.ready(() => {
|
|
|
|
this.player.overlay({
|
2020-11-15 19:13:18 +00:00
|
|
|
overlays: [{
|
|
|
|
showBackground: false,
|
|
|
|
content: title,
|
|
|
|
start: 'mousemove',
|
|
|
|
end: 'userinactive'
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
2021-05-21 23:52:11 +00:00
|
|
|
this.player.ready(() => {
|
|
|
|
this.player.overlay({
|
2020-11-15 19:13:18 +00:00
|
|
|
overlays: [{
|
|
|
|
showBackground: false,
|
|
|
|
content: ' ',
|
|
|
|
start: 'play',
|
|
|
|
end: 'loadstart'
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-04-22 18:41:50 +00:00
|
|
|
toggleFullscreenClass: function () {
|
|
|
|
if (this.player.isFullscreen()) {
|
|
|
|
$('body').addClass('vjs--full-screen-enabled')
|
|
|
|
} else {
|
|
|
|
$('body').removeClass('vjs--full-screen-enabled')
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-01-16 03:34:49 +00:00
|
|
|
handleTouchStart: function (event) {
|
|
|
|
this.touchPauseTimeout = setTimeout(() => {
|
2021-05-21 23:52:11 +00:00
|
|
|
this.togglePlayPause()
|
2021-01-16 03:34:49 +00:00
|
|
|
}, 1000)
|
|
|
|
|
|
|
|
const touchTime = new Date()
|
|
|
|
|
|
|
|
if (this.lastTouchTime !== null && (touchTime.getTime() - this.lastTouchTime.getTime()) < 250) {
|
|
|
|
this.toggleFullscreen()
|
|
|
|
}
|
|
|
|
|
|
|
|
this.lastTouchTime = touchTime
|
|
|
|
},
|
|
|
|
|
|
|
|
handleTouchEnd: function (event) {
|
|
|
|
clearTimeout(this.touchPauseTimeout)
|
|
|
|
},
|
|
|
|
|
2020-02-20 20:58:21 +00:00
|
|
|
keyboardShortcutHandler: function (event) {
|
|
|
|
const activeInputs = $('.ft-input')
|
|
|
|
|
|
|
|
for (let i = 0; i < activeInputs.length; i++) {
|
|
|
|
if (activeInputs[i] === document.activeElement) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-21 19:43:50 +00:00
|
|
|
if (event.ctrlKey) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-20 20:58:21 +00:00
|
|
|
if (this.player !== null) {
|
|
|
|
switch (event.which) {
|
|
|
|
case 32:
|
|
|
|
// Space Bar
|
|
|
|
// Toggle Play/Pause
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.togglePlayPause()
|
|
|
|
break
|
|
|
|
case 74:
|
|
|
|
// J Key
|
2021-08-05 20:17:01 +00:00
|
|
|
// Rewind by 2x the time-skip interval (in seconds)
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2021-08-05 20:17:01 +00:00
|
|
|
this.changeDurationBySeconds(-this.defaultSkipInterval * 2)
|
2020-02-20 20:58:21 +00:00
|
|
|
break
|
|
|
|
case 75:
|
|
|
|
// K Key
|
|
|
|
// Toggle Play/Pause
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.togglePlayPause()
|
|
|
|
break
|
|
|
|
case 76:
|
|
|
|
// L Key
|
2021-08-05 20:17:01 +00:00
|
|
|
// Fast-Forward by 2x the time-skip interval (in seconds)
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2021-08-05 20:17:01 +00:00
|
|
|
this.changeDurationBySeconds(this.defaultSkipInterval * 2)
|
2020-02-20 20:58:21 +00:00
|
|
|
break
|
|
|
|
case 79:
|
|
|
|
// O Key
|
|
|
|
// Decrease playback rate by 0.25x
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changePlayBackRate(-0.25)
|
|
|
|
break
|
|
|
|
case 80:
|
|
|
|
// P Key
|
|
|
|
// Increase playback rate by 0.25x
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changePlayBackRate(0.25)
|
|
|
|
break
|
|
|
|
case 70:
|
|
|
|
// F Key
|
|
|
|
// Toggle Fullscreen Playback
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.toggleFullscreen()
|
|
|
|
break
|
|
|
|
case 77:
|
|
|
|
// M Key
|
|
|
|
// Toggle Mute
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.toggleMute()
|
|
|
|
break
|
|
|
|
case 67:
|
|
|
|
// C Key
|
|
|
|
// Toggle Captions
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.toggleCaptions()
|
|
|
|
break
|
|
|
|
case 38:
|
|
|
|
// Up Arrow Key
|
|
|
|
// Increase volume
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeVolume(0.05)
|
|
|
|
break
|
|
|
|
case 40:
|
|
|
|
// Down Arrow Key
|
2020-11-15 19:13:18 +00:00
|
|
|
// Decrease Volume
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeVolume(-0.05)
|
|
|
|
break
|
|
|
|
case 37:
|
|
|
|
// Left Arrow Key
|
2021-08-05 20:17:01 +00:00
|
|
|
// Rewind by the time-skip interval (in seconds)
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2021-08-05 20:17:01 +00:00
|
|
|
this.changeDurationBySeconds(-this.defaultSkipInterval * 1)
|
2020-02-20 20:58:21 +00:00
|
|
|
break
|
|
|
|
case 39:
|
|
|
|
// Right Arrow Key
|
2021-08-05 20:17:01 +00:00
|
|
|
// Fast-Forward by the time-skip interval (in seconds)
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2021-08-05 20:17:01 +00:00
|
|
|
this.changeDurationBySeconds(this.defaultSkipInterval * 1)
|
2020-02-20 20:58:21 +00:00
|
|
|
break
|
|
|
|
case 49:
|
|
|
|
// 1 Key
|
|
|
|
// Jump to 10% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.1)
|
|
|
|
break
|
|
|
|
case 50:
|
|
|
|
// 2 Key
|
|
|
|
// Jump to 20% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.2)
|
|
|
|
break
|
|
|
|
case 51:
|
|
|
|
// 3 Key
|
|
|
|
// Jump to 30% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.3)
|
|
|
|
break
|
|
|
|
case 52:
|
|
|
|
// 4 Key
|
|
|
|
// Jump to 40% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.4)
|
|
|
|
break
|
|
|
|
case 53:
|
|
|
|
// 5 Key
|
|
|
|
// Jump to 50% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.5)
|
|
|
|
break
|
|
|
|
case 54:
|
|
|
|
// 6 Key
|
|
|
|
// Jump to 60% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.6)
|
|
|
|
break
|
|
|
|
case 55:
|
|
|
|
// 7 Key
|
|
|
|
// Jump to 70% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.7)
|
|
|
|
break
|
|
|
|
case 56:
|
|
|
|
// 8 Key
|
|
|
|
// Jump to 80% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.8)
|
|
|
|
break
|
|
|
|
case 57:
|
|
|
|
// 9 Key
|
|
|
|
// Jump to 90% in the video
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0.9)
|
|
|
|
break
|
|
|
|
case 48:
|
|
|
|
// 0 Key
|
|
|
|
// Jump to 0% in the video (The beginning)
|
2020-09-26 15:57:57 +00:00
|
|
|
event.preventDefault()
|
2020-02-20 20:58:21 +00:00
|
|
|
this.changeDurationByPercentage(0)
|
|
|
|
break
|
2020-11-13 22:29:41 +00:00
|
|
|
case 188:
|
|
|
|
// , Key
|
|
|
|
// Return to previous frame
|
|
|
|
this.framebyframe(-1)
|
|
|
|
break
|
|
|
|
case 190:
|
|
|
|
// . Key
|
|
|
|
// Advance to next frame
|
|
|
|
this.framebyframe(1)
|
|
|
|
break
|
2020-12-15 19:07:40 +00:00
|
|
|
case 68:
|
|
|
|
// D Key
|
|
|
|
// Toggle Picture in Picture Mode
|
|
|
|
if (!this.player.isInPictureInPicture()) {
|
|
|
|
this.player.requestPictureInPicture()
|
|
|
|
} else if (this.player.isInPictureInPicture()) {
|
|
|
|
this.player.exitPictureInPicture()
|
|
|
|
}
|
|
|
|
break
|
2020-12-14 22:37:58 +00:00
|
|
|
case 27:
|
|
|
|
// esc Key
|
|
|
|
// Exit full window
|
|
|
|
event.preventDefault()
|
|
|
|
this.exitFullWindow()
|
|
|
|
break
|
2020-12-15 19:07:40 +00:00
|
|
|
case 83:
|
|
|
|
// S Key
|
|
|
|
// Toggle Full Window Mode
|
|
|
|
this.toggleFullWindow()
|
|
|
|
break
|
2021-09-15 02:00:34 +00:00
|
|
|
case 84:
|
|
|
|
// T Key
|
|
|
|
// Toggle Theatre Mode
|
|
|
|
this.toggleTheatreMode()
|
|
|
|
break
|
2020-02-20 20:58:21 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-13 03:56:31 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
...mapActions([
|
2021-05-21 23:56:32 +00:00
|
|
|
'calculateColorLuminance',
|
2021-06-10 19:35:00 +00:00
|
|
|
'updateDefaultCaptionSettings',
|
|
|
|
'showToast',
|
2021-05-21 23:56:32 +00:00
|
|
|
'sponsorBlockSkipSegments'
|
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
2021-11-23 11:34:04 +00:00
|
|
|
]),
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-16 18:30:00 +00:00
|
|
|
}
|
|
|
|
})
|