diff --git a/package-lock.json b/package-lock.json index 6a03ee85..6c3d242e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3560,12 +3560,12 @@ } }, "@silvermine/videojs-quality-selector": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@silvermine/videojs-quality-selector/-/videojs-quality-selector-1.2.4.tgz", - "integrity": "sha512-bLCoOk2kEhebXjuREMIjCqM1ZYQOnIJ8/McF2NcTsK6RluuHUSnFzZrH98sWkHGOTNrt+g9m27+kLlR6+x5jYg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@silvermine/videojs-quality-selector/-/videojs-quality-selector-1.2.5.tgz", + "integrity": "sha512-cielchUzL8r2EX01S7PfR54tTbxDZR53xIDJoUi9Wg6pM2X+ftdJD6XiIDVOjPlBoG94iuG9LJwUtjX5IhrWZQ==", "requires": { "class.extend": "0.9.1", - "underscore": "1.9.1" + "underscore": "1.13.1" } }, "@sindresorhus/is": { @@ -12769,12 +12769,6 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==" }, - "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", - "dev": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -14064,16 +14058,22 @@ "dev": true }, "postcss": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz", - "integrity": "sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==", + "version": "8.2.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", + "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==", "dev": true, "requires": { - "colorette": "^1.2.1", - "nanoid": "^3.1.20", + "colorette": "^1.2.2", + "nanoid": "^3.1.23", "source-map": "^0.6.1" }, "dependencies": { + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -16588,9 +16588,9 @@ "dev": true }, "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", diff --git a/package.json b/package.json index 57a2ea4a..a59e9527 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@fortawesome/free-brands-svg-icons": "^5.15.2", "@fortawesome/free-solid-svg-icons": "^5.15.2", "@fortawesome/vue-fontawesome": "^2.0.2", - "@silvermine/videojs-quality-selector": "^1.2.4", + "@silvermine/videojs-quality-selector": "^1.2.5", "autolinker": "^3.14.2", "bulma-pro": "^0.2.0", "dateformat": "^4.5.1", 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 cb0130d5..a2b82db1 100644 --- a/src/renderer/components/ft-video-player/ft-video-player.js +++ b/src/renderer/components/ft-video-player/ft-video-player.js @@ -43,6 +43,10 @@ export default Vue.extend({ type: Array, default: () => { return [] } }, + adaptiveFormats: { + type: Array, + default: () => { return [] } + }, dashSrc: { type: Array, default: null @@ -77,6 +81,7 @@ export default Vue.extend({ using60Fps: false, maxFramerate: 0, activeSourceList: [], + activeAdaptiveFormats: [], mouseTimeout: null, touchTimeout: null, lastTouchTime: null, @@ -178,6 +183,7 @@ export default Vue.extend({ }, methods: { initializePlayer: async function () { + console.log(this.adaptiveFormats) const videoPlayer = document.getElementById(this.id) if (videoPlayer !== null) { if (!this.useDash) { @@ -416,7 +422,28 @@ export default Vue.extend({ this.setDashQualityLevel('auto') } - this.player.qualityLevels().levels_.sort((a, b) => { + let formatsToTest = this.activeAdaptiveFormats.filter((format) => { + return format.height === this.defaultQuality + }) + + if (formatsToTest.length === 0) { + formatsToTest = this.activeAdaptiveFormats.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 + } + }) + + this.setDashQualityLevel(formatsToTest[0].bitrate) + + // Old logic. Revert if needed + /* this.player.qualityLevels().levels_.sort((a, b) => { if (a.height === b.height) { return a.bitrate - b.bitrate } else { @@ -449,11 +476,52 @@ export default Vue.extend({ } else if (index === (arr.length - 1) && quality < this.defaultQuality) { this.setDashQualityLevel(height) } - }) + }) */ }, - setDashQualityLevel: function (qualityLevel, is60Fps = false) { - if (this.selectedQuality === qualityLevel && this.using60Fps === is60Fps) { + setDashQualityLevel: function (bitrate) { + let adaptiveFormat = null + + if (bitrate !== 'auto') { + adaptiveFormat = this.activeAdaptiveFormats.find((format) => { + return format.bitrate === bitrate + }) + } + + 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) + } else { + ql.enabled = false + ql.enabled_(false) + } + }) + + const selectedQuality = bitrate === 'auto' ? 'auto' : adaptiveFormat.qualityLabel + + 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) { return } let foundSelectedQuality = false @@ -530,7 +598,7 @@ export default Vue.extend({ // const currentTime = this.player.currentTime() // this.player.currentTime(0) - // this.player.currentTime(currentTime) + // this.player.currentTime(currentTime) */ }, enableDashFormat: function () { @@ -725,10 +793,10 @@ export default Vue.extend({ VjsButton.call(this, player, options) }, handleClick: function(event) { + console.log(event) const selectedQuality = event.target.innerText - const quality = selectedQuality === 'auto' ? 'auto' : parseInt(selectedQuality.replace('p(60)?', '')) - const is60Fps = selectedQuality.includes('p60') - v.setDashQualityLevel(quality, is60Fps) + const bitrate = selectedQuality === 'auto' ? 'auto' : parseInt(event.target.attributes.bitrate.value) + v.setDashQualityLevel(bitrate) }, createControlTextEl: function (button) { const beginningHtml = `
@@ -738,7 +806,7 @@ export default Vue.extend({
' - let qualityHtml = `
  • + let qualityHtml = `
  • Auto
  • ` @@ -750,7 +818,23 @@ export default Vue.extend({ return b.height - a.height } }).forEach((quality, index, array) => { - let is60Fps = false + const adaptiveFormat = v.adaptiveFormats.find((format) => { + return format.bitrate === quality.bitrate + }) + + v.activeAdaptiveFormats.push(adaptiveFormat) + + const fps = adaptiveFormat.fps + const qualityLabel = adaptiveFormat.qualityLabel + const bitrate = quality.bitrate + + qualityHtml = qualityHtml + `
  • + ${qualityLabel} + +
  • ` + + // Old logic, revert if needed. + /* let is60Fps = false if (index < array.length - 1 && array[index + 1].height === quality.height) { if (array[index + 1].bitrate < quality.bitrate) { is60Fps = true @@ -760,7 +844,7 @@ export default Vue.extend({ qualityHtml = qualityHtml + `
  • ${qualityText} -
  • ` + ` */ }) return $(button).html( $(beginningHtml + qualityHtml + endingHtml).attr( diff --git a/src/renderer/videoJS.css b/src/renderer/videoJS.css index 7aadc33f..5250e5d1 100644 --- a/src/renderer/videoJS.css +++ b/src/renderer/videoJS.css @@ -664,10 +664,10 @@ body.vjs-full-window { display: none; position: absolute; bottom: 30px; - left: -15px; + left: -33px; z-index: 5; background-color: #151b17; - width: 70px; + width: 100px; max-height: 225px; overflow-y: auto; } diff --git a/src/renderer/views/Watch/Watch.js b/src/renderer/views/Watch/Watch.js index c0d6fa6b..8db9a4dd 100644 --- a/src/renderer/views/Watch/Watch.js +++ b/src/renderer/views/Watch/Watch.js @@ -69,6 +69,7 @@ export default Vue.extend({ activeSourceList: [], videoSourceList: [], audioSourceList: [], + adaptiveFormats: [], captionHybridList: [], // [] -> Promise[] -> string[] (URIs) recommendedVideos: [], downloadLinks: [], @@ -291,7 +292,7 @@ export default Vue.extend({ this.videoLikeCount = isNaN(result.videoDetails.likes) ? 0 : result.videoDetails.likes this.videoDislikeCount = isNaN(result.videoDetails.dislikes) ? 0 : result.videoDetails.dislikes } - this.isLive = result.player_response.videoDetails.isLive || result.player_response.videoDetails.isLiveContent + this.isLive = result.player_response.videoDetails.isLive this.isLiveContent = result.player_response.videoDetails.isLiveContent this.isUpcoming = result.player_response.videoDetails.isUpcoming ? result.player_response.videoDetails.isUpcoming : false @@ -311,7 +312,7 @@ export default Vue.extend({ } } - if (this.isLive && !this.isUpcoming) { + if ((this.isLive || this.isLiveContent) && !this.isUpcoming) { this.enableLegacyFormat() this.videoSourceList = result.formats.filter((format) => { @@ -364,6 +365,7 @@ export default Vue.extend({ } else { this.videoSourceList = result.player_response.streamingData.adaptiveFormats.reverse() } + this.adaptiveFormats = this.videoSourceList this.downloadLinks = result.formats.filter((format) => { return typeof format.mimeType !== 'undefined' }).map((format) => { @@ -434,14 +436,9 @@ export default Vue.extend({ if (this.proxyVideos) { this.dashSrc = await this.createInvidiousDashManifest() } else { - const adaptiveFormats = result.player_response.streamingData.adaptiveFormats.filter((video) => { - if (typeof (video.qualityLabel) !== 'undefined') { - return !video.qualityLabel.includes('HDR') - } else { - return true - } - }) + const adaptiveFormats = result.player_response.streamingData.adaptiveFormats this.dashSrc = await this.createLocalDashManifest(adaptiveFormats) + this.adaptiveFormats = adaptiveFormats } this.audioSourceList = result.player_response.streamingData.adaptiveFormats.filter((format) => { diff --git a/src/renderer/views/Watch/Watch.vue b/src/renderer/views/Watch/Watch.vue index 8aa78b32..e325435b 100644 --- a/src/renderer/views/Watch/Watch.vue +++ b/src/renderer/views/Watch/Watch.vue @@ -18,6 +18,7 @@ ref="videoPlayer" :dash-src="dashSrc" :source-list="activeSourceList" + :adaptive-formats="adaptiveFormats" :caption-hybrid-list="captionHybridList" :storyboard-src="videoStoryboardSrc" :format="activeFormat"