From fb012624ca575526e6c2fbcf4d54152c5292373c Mon Sep 17 00:00:00 2001 From: absidue <48293849+absidue@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:16:50 +0200 Subject: [PATCH 01/99] Migrate video player from video.js to shaka-player --- .stylelintrc.json | 2 +- _scripts/_domParser.js | 5 - _scripts/dev-runner.js | 32 +- _scripts/getShakaLocales.js | 114 + _scripts/patchShaka.mjs | 105 + _scripts/webpack.renderer.config.js | 35 +- _scripts/webpack.web.config.js | 36 +- jsconfig.json | 3 + package.json | 21 +- src/main/index.js | 42 +- src/renderer/App.vue | 1 - src/renderer/assets/font/VideoJS.woff | Bin 4324 -> 0 bytes src/renderer/assets/img/camera.svg | 13 - src/renderer/assets/img/close_fullwindow.svg | 1 - src/renderer/assets/img/close_theatre.svg | 1 - src/renderer/assets/img/loop.svg | 1 - src/renderer/assets/img/open_fullwindow.svg | 1 - src/renderer/assets/img/open_theatre.svg | 1 - .../ft-shaka-video-player.css | 186 ++ .../ft-shaka-video-player.js | 2487 +++++++++++++++++ .../ft-shaka-video-player.vue | 84 + .../player-components/FullWindowButton.js | 84 + .../LegacyQualitySelection.js | 150 + .../player-components/ScreenshotButton.js | 57 + .../player-components/StatsButton.js | 80 + .../player-components/TheatreModeButton.js | 84 + .../ft-video-player/ft-video-player.css | 37 - .../ft-video-player/ft-video-player.js | 2312 --------------- .../ft-video-player/ft-video-player.vue | 36 - .../player-settings/player-settings.js | 13 +- .../player-settings/player-settings.scss | 4 - .../player-settings/player-settings.vue | 8 - src/renderer/helpers/api/invidious.js | 58 +- src/renderer/helpers/api/local.js | 2 +- .../player/legacyFormatsVttCueParser.js | 292 ++ src/renderer/helpers/player/utils.js | 190 ++ src/renderer/helpers/sponsorblock.js | 21 + src/renderer/helpers/utils.js | 35 - src/renderer/main.js | 12 + src/renderer/store/modules/index.js | 4 +- src/renderer/store/modules/player.js | 39 + src/renderer/store/modules/settings.js | 1 - src/renderer/videoJS.css | 2226 --------------- src/renderer/views/Watch/Watch.js | 946 +++---- src/renderer/views/Watch/Watch.scss | 42 +- src/renderer/views/Watch/Watch.vue | 53 +- static/locales/ar.yaml | 31 - static/locales/be.yaml | 20 - static/locales/bg.yaml | 32 - static/locales/ca.yaml | 7 - static/locales/ckb.yaml | 20 - static/locales/cs.yaml | 23 - static/locales/cy.yaml | 20 - static/locales/da.yaml | 20 - static/locales/de-DE.yaml | 32 - static/locales/el.yaml | 31 - static/locales/en-US.yaml | 47 +- static/locales/en_GB.yaml | 54 +- static/locales/es-MX.yaml | 19 - static/locales/es.yaml | 32 - static/locales/es_AR.yaml | 5 - static/locales/et.yaml | 32 - static/locales/eu.yaml | 24 - static/locales/fa.yaml | 24 - static/locales/fi.yaml | 31 - static/locales/fr-FR.yaml | 32 - static/locales/gl.yaml | 24 - static/locales/he.yaml | 30 - static/locales/hr.yaml | 32 - static/locales/hu.yaml | 24 - static/locales/id.yaml | 26 - static/locales/is.yaml | 31 - static/locales/it.yaml | 32 - static/locales/ja.yaml | 29 - static/locales/ko.yaml | 22 - static/locales/lt.yaml | 28 - static/locales/lv.yaml | 20 - static/locales/nb_NO.yaml | 23 - static/locales/nl.yaml | 32 - static/locales/nn.yaml | 19 - static/locales/or.yaml | 1 - static/locales/pl.yaml | 32 - static/locales/pt-BR.yaml | 32 - static/locales/pt-PT.yaml | 24 - static/locales/pt.yaml | 32 - static/locales/ro.yaml | 32 - static/locales/ru.yaml | 32 - static/locales/sk.yaml | 16 - static/locales/sl.yaml | 9 - static/locales/sm.yaml | 20 - static/locales/sr.yaml | 24 - static/locales/sv.yaml | 23 - static/locales/tr.yaml | 31 - static/locales/uk.yaml | 32 - static/locales/vi.yaml | 24 - static/locales/zh-CN.yaml | 29 - static/locales/zh-TW.yaml | 29 - yarn.lock | 521 +--- 98 files changed, 4681 insertions(+), 7127 deletions(-) delete mode 100644 _scripts/_domParser.js create mode 100644 _scripts/getShakaLocales.js create mode 100644 _scripts/patchShaka.mjs delete mode 100644 src/renderer/assets/font/VideoJS.woff delete mode 100644 src/renderer/assets/img/camera.svg delete mode 100644 src/renderer/assets/img/close_fullwindow.svg delete mode 100644 src/renderer/assets/img/close_theatre.svg delete mode 100644 src/renderer/assets/img/loop.svg delete mode 100644 src/renderer/assets/img/open_fullwindow.svg delete mode 100644 src/renderer/assets/img/open_theatre.svg create mode 100644 src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.css create mode 100644 src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.js create mode 100644 src/renderer/components/ft-shaka-video-player/ft-shaka-video-player.vue create mode 100644 src/renderer/components/ft-shaka-video-player/player-components/FullWindowButton.js create mode 100644 src/renderer/components/ft-shaka-video-player/player-components/LegacyQualitySelection.js create mode 100644 src/renderer/components/ft-shaka-video-player/player-components/ScreenshotButton.js create mode 100644 src/renderer/components/ft-shaka-video-player/player-components/StatsButton.js create mode 100644 src/renderer/components/ft-shaka-video-player/player-components/TheatreModeButton.js delete mode 100644 src/renderer/components/ft-video-player/ft-video-player.css delete mode 100644 src/renderer/components/ft-video-player/ft-video-player.js delete mode 100644 src/renderer/components/ft-video-player/ft-video-player.vue create mode 100644 src/renderer/helpers/player/legacyFormatsVttCueParser.js create mode 100644 src/renderer/helpers/player/utils.js create mode 100644 src/renderer/store/modules/player.js delete mode 100644 src/renderer/videoJS.css diff --git a/.stylelintrc.json b/.stylelintrc.json index e32dc52314fb9..8b2065bb53ec6 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -26,7 +26,7 @@ "selector-pseudo-class-no-unknown": [ true, { - "ignorePseudoClasses": ["deep"] + "ignorePseudoClasses": ["deep", "global"] } ], "a11y/no-outline-none": true, diff --git a/_scripts/_domParser.js b/_scripts/_domParser.js deleted file mode 100644 index 36d81182b9282..0000000000000 --- a/_scripts/_domParser.js +++ /dev/null @@ -1,5 +0,0 @@ -const DOMParser = window.DOMParser - -export { - DOMParser -} diff --git a/_scripts/dev-runner.js b/_scripts/dev-runner.js index fe25a836fff17..9103bd63291b6 100644 --- a/_scripts/dev-runner.js +++ b/_scripts/dev-runner.js @@ -17,10 +17,14 @@ const web = process.argv.indexOf('--web') !== -1 let mainConfig let rendererConfig let webConfig +let SHAKA_LOCALES_TO_BE_BUNDLED if (!web) { mainConfig = require('./webpack.main.config') rendererConfig = require('./webpack.renderer.config') + + SHAKA_LOCALES_TO_BE_BUNDLED = rendererConfig.SHAKA_LOCALES_TO_BE_BUNDLED + delete rendererConfig.SHAKA_LOCALES_TO_BE_BUNDLED } else { webConfig = require('./webpack.web.config') } @@ -110,16 +114,26 @@ function startRenderer(callback) { }) const server = new WebpackDevServer({ - static: { - directory: path.resolve(__dirname, '..', 'static'), - watch: { - ignored: [ - /(dashFiles|storyboards)\/*/, - '/**/.DS_Store', - ] + static: [ + { + directory: path.resolve(__dirname, '..', 'static'), + watch: { + ignored: [ + /(dashFiles|storyboards)\/*/, + '/**/.DS_Store', + ] + }, + publicPath: '/static' }, - publicPath: '/static' - }, + { + directory: path.resolve(__dirname, '..', 'node_modules', 'shaka-player', 'ui', 'locales'), + publicPath: '/static/shaka-player-locales', + watch: { + // Ignore everything that isn't one of the locales that we would bundle in production mode + ignored: `**/!(${SHAKA_LOCALES_TO_BE_BUNDLED.join('|')}).json` + } + } + ], port }, compiler) diff --git a/_scripts/getShakaLocales.js b/_scripts/getShakaLocales.js new file mode 100644 index 0000000000000..4550f3a73ca10 --- /dev/null +++ b/_scripts/getShakaLocales.js @@ -0,0 +1,114 @@ +const { readFileSync, readdirSync } = require('fs') + +function getPreloadedLocales() { + const localesFile = readFileSync(`${__dirname}/../node_modules/shaka-player/dist/locales.js`, 'utf-8') + + const localesLine = localesFile.match(/^\/\/ LOCALES: ([\w, -]+)$/m) + + if (!localesLine) { + throw new Error('Failed to parse shaka-player's preloaded locales') + } + + return localesLine[1].split(',').map(locale => locale.trim()) +} + +function getAllLocales() { + const filenames = readdirSync(`${__dirname}/../node_modules/shaka-player/ui/locales`) + + return new Set(filenames + .filter(filename => filename !== 'source.json' && filename.endsWith('.json')) + .map(filename => filename.replace('.json', ''))) +} + +/** + * Maps the shaka locales to FreeTube's active ones + * This allows us to know which locale files are actually needed + * and which shaka locale needs to be activated for a given FreeTube one. + * @param {Set} shakaLocales + * @param {string[]} freeTubeLocales + */ +function getMappings(shakaLocales, freeTubeLocales) { + /** + * @type {[string, string][]} + * Using this structure as it gets passed to `new Map()` in the player component + * The first element is the FreeTube locale, the second one is the shaka-player one + **/ + const mappings = [] + + for (const locale of freeTubeLocales) { + if (shakaLocales.has(locale)) { + mappings.push([ + locale, + locale + ]) + } else if (shakaLocales.has(locale.replace('_', '-'))) { + mappings.push([ + locale, + locale.replace('_', '-') + ]) + } else if (shakaLocales.has(locale.split(/[-_]/)[0])) { + mappings.push([ + locale, + locale.split(/[-_]/)[0] + ]) + } + } + + // special cases + + mappings.push( + // according to https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + // "no" is the macro language for "nb" and "nn" + [ + 'nb_NO', + 'no' + ], + [ + 'nn', + 'no' + ], + + // according to https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + // "iw" is the old/original code for Hewbrew, these days it's "he" + [ + 'he', + 'iw' + ], + + // not sure why we have pt, pt-PT and pt-BR in the FreeTube locales + // as pt and pt-PT are the same thing, but we should handle it here anyway + [ + 'pt', + 'pt-PT' + ] + ) + + return mappings +} + +function getShakaLocales() { + const shakaLocales = getAllLocales() + + /** @type {string[]} */ + const freeTubeLocales = JSON.parse(readFileSync(`${__dirname}/../static/locales/activeLocales.json`, 'utf-8')) + + const mappings = getMappings(shakaLocales, freeTubeLocales) + + const preloaded = getPreloadedLocales() + + const shakaMappings = mappings.map(mapping => mapping[1]) + + // use a set to deduplicate the list + // we don't need to bundle any locale files that are already embedded in shaka-player/preloaded + + /** @type {string[]} */ + const toBeBundled = [...new Set(shakaMappings.filter(locale => !preloaded.includes(locale)))] + + return { + SHAKA_LOCALE_MAPPINGS: mappings, + SHAKA_LOCALES_PREBUNDLED: preloaded, + SHAKA_LOCALES_TO_BE_BUNDLED: toBeBundled + } +} + +module.exports = getShakaLocales() diff --git a/_scripts/patchShaka.mjs b/_scripts/patchShaka.mjs new file mode 100644 index 0000000000000..f31d57b3053d3 --- /dev/null +++ b/_scripts/patchShaka.mjs @@ -0,0 +1,105 @@ +// This script fixes shaka not exporting its type definitions and referencing remote google fonts in its CSS +// by adding an export line to the type definitions and donwloading the fonts and updating the CSS to point to the local files +// this script only makes changes if they are needed, so running it multiple times doesn't cause any problems + +import { appendFileSync, closeSync, ftruncateSync, openSync, readFileSync, writeFileSync, writeSync } from 'fs' +import { resolve } from 'path' + +const SHAKA_DIST_DIR = resolve(import.meta.dirname, '../node_modules/shaka-player/dist') + +function fixTypes() { + let fixedTypes = false + + let fileHandleNormal + try { + fileHandleNormal = openSync(`${SHAKA_DIST_DIR}/shaka-player.ui.d.ts`, 'a+') + + const contents = readFileSync(fileHandleNormal, 'utf-8') + + // This script is run after every `yarn install`, even if shaka-player wasn't updated + // So we want to check first, if we actually need to make any changes + // or if the ones from the previous run are still intact + if (!contents.includes('export default shaka')) { + appendFileSync(fileHandleNormal, 'export default shaka;\n') + + fixedTypes = true + } + } finally { + if (typeof fileHandleNormal !== 'undefined') { + closeSync(fileHandleNormal) + } + } + + let fileHandleDebug + try { + fileHandleDebug = openSync(`${SHAKA_DIST_DIR}/shaka-player.ui.debug.d.ts`, 'a+') + + const contents = readFileSync(fileHandleDebug, 'utf-8') + + // This script is run after every `yarn install`, even if shaka-player wasn't updated + // So we want to check first, if we actually need to make any changes + // or if the ones from the previous run are still intact + if (!contents.includes('export default shaka')) { + appendFileSync(fileHandleDebug, 'export default shaka;\n') + + fixedTypes = true + } + } finally { + if (typeof fileHandleDebug !== 'undefined') { + closeSync(fileHandleDebug) + } + } + + if (fixedTypes) { + console.log('Fixed shaka-player types') + } +} + +async function fixRemoteFonts() { + let cssFileHandle + try { + cssFileHandle = openSync(`${SHAKA_DIST_DIR}/controls.css`, 'r+') + + let cssContents = readFileSync(cssFileHandle, 'utf-8') + + const beforeRobotoReplacement = cssContents.length + cssContents = cssContents.replace(/@font-face\{font-family:Roboto;[^}]+\}/, '') + + if (cssContents.length !== beforeRobotoReplacement) { + console.log('Removed shaka-player Roboto font, so it uses ours') + } + + const remoteFontsRegex = /https:\/\/fonts\.gstatic\.com\/s\/(?[^\/]+)\/(?[^\/]+)\/[^.]+\.(?[a-z]+)/g + /** @type {RegExpMatchArray[]} */ + const remoteFontMatches = [...cssContents.matchAll(remoteFontsRegex)] + + if (remoteFontMatches.length > 0) { + console.log('Downloading shaka-player remote fonts...') + + for (const match of remoteFontMatches) { + const url = match[0] + const { name, version, extension } = match.groups + + const response = await fetch(url) + const fontContent = new Uint8Array(await response.arrayBuffer()) + + const filename = `shaka-${name}-${version}.${extension}` + writeFileSync(`${SHAKA_DIST_DIR}/${filename}`, fontContent) + + cssContents = cssContents.replace(url, `./${filename}`) + } + + ftruncateSync(cssFileHandle) + writeSync(cssFileHandle, cssContents, 0, 'utf-8') + + console.log('Localised shaka-player fonts') + } + } finally { + if (typeof cssFileHandle !== 'undefined') { + closeSync(cssFileHandle) + } + } +} + +fixTypes() +await fixRemoteFonts() diff --git a/_scripts/webpack.renderer.config.js b/_scripts/webpack.renderer.config.js index fcd9b0e580980..7cdabf7348603 100644 --- a/_scripts/webpack.renderer.config.js +++ b/_scripts/webpack.renderer.config.js @@ -8,6 +8,11 @@ const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const ProcessLocalesPlugin = require('./ProcessLocalesPlugin') const WatchExternalFilesPlugin = require('webpack-watch-external-files-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') +const { + SHAKA_LOCALE_MAPPINGS, + SHAKA_LOCALES_PREBUNDLED, + SHAKA_LOCALES_TO_BE_BUNDLED +} = require('./getShakaLocales') const isDevMode = process.env.NODE_ENV === 'development' @@ -122,7 +127,9 @@ const config = { 'process.env.SUPPORTS_LOCAL_API': true, 'process.env.LOCALE_NAMES': JSON.stringify(processLocalesPlugin.localeNames), 'process.env.GEOLOCATION_NAMES': JSON.stringify(readdirSync(path.join(__dirname, '..', 'static', 'geolocations')).map(filename => filename.replace('.json', ''))), - 'process.env.SWIPER_VERSION': `'${swiperVersion}'` + 'process.env.SWIPER_VERSION': `'${swiperVersion}'`, + 'process.env.SHAKA_LOCALE_MAPPINGS': JSON.stringify(SHAKA_LOCALE_MAPPINGS), + 'process.env.SHAKA_LOCALES_PREBUNDLED': JSON.stringify(SHAKA_LOCALES_PREBUNDLED) }), new HtmlWebpackPlugin({ excludeChunks: ['processTaskWorker'], @@ -143,7 +150,21 @@ const config = { transformAll: (assets) => { return Buffer.concat(assets.map(asset => asset.data)) } - } + }, + // Don't need to copy them in dev mode, + // as we configure WebpackDevServer to serve them + ...(isDevMode ? [] : [ + { + from: path.join(__dirname, '../node_modules/shaka-player/ui/locales', `{${SHAKA_LOCALES_TO_BE_BUNDLED.join(',')}}.json`).replaceAll('\\', '/'), + to: path.join(__dirname, '../dist/static/shaka-player-locales'), + context: path.join(__dirname, '../node_modules/shaka-player/ui/locales'), + transform: { + transformer: (input) => { + return JSON.stringify(JSON.parse(input.toString('utf-8'))) + } + } + } + ]) ] }) ], @@ -155,10 +176,8 @@ const config = { 'youtubei.js$': 'youtubei.js/web', - // video.js's mpd-parser uses @xmldom/xmldom so that it can support both node and web browsers - // as FreeTube only runs in electron and web browsers we can use the native DOMParser class, instead of the "polyfill" - // https://caniuse.com/mdn-api_domparser - '@xmldom/xmldom$': path.resolve(__dirname, '_domParser.js') + // change to "shaka-player.ui.debug.js" to get debug logs (update jsconfig to get updated types) + 'shaka-player$': 'shaka-player/dist/shaka-player.ui.js', }, extensions: ['.js', '.vue'] }, @@ -175,6 +194,10 @@ if (isDevMode) { ], }), ) + + // hack to pass it through to the dev-runner.js script + // gets removed there before the config object is passed to webpack + config.SHAKA_LOCALES_TO_BE_BUNDLED = SHAKA_LOCALES_TO_BE_BUNDLED } module.exports = config diff --git a/_scripts/webpack.web.config.js b/_scripts/webpack.web.config.js index 04f4b9803d35b..87fc8a87012ce 100644 --- a/_scripts/webpack.web.config.js +++ b/_scripts/webpack.web.config.js @@ -8,6 +8,11 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const JsonMinimizerPlugin = require('json-minimizer-webpack-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') const ProcessLocalesPlugin = require('./ProcessLocalesPlugin') +const { + SHAKA_LOCALE_MAPPINGS, + SHAKA_LOCALES_PREBUNDLED, + SHAKA_LOCALES_TO_BE_BUNDLED +} = require('./getShakaLocales') const isDevMode = process.env.NODE_ENV === 'development' @@ -116,19 +121,7 @@ const config = { 'process.env.IS_ELECTRON': false, 'process.env.IS_ELECTRON_MAIN': false, 'process.env.SUPPORTS_LOCAL_API': false, - 'process.env.SWIPER_VERSION': `'${swiperVersion}'`, - - // video.js' vhs-utils supports both atob() in web browsers and Buffer in node - // As the FreeTube web build only runs in web browsers, we can override their check for atob() here: https://github.com/videojs/vhs-utils/blob/main/src/decode-b64-to-uint8-array.js#L3 - // overriding that check means we don't need to include a Buffer polyfill - // https://caniuse.com/atob-btoa - - // NOTE FOR THE FUTURE: this override won't work with vite as their define does a find and replace in the code for production builds, - // but uses globals in development builds to save build time, so this would replace the actual atob() function with true if used with vite - // this works in webpack as webpack does a find and replace in the source code for both development and production builds - // https://vitejs.dev/config/shared-options.html#define - // https://webpack.js.org/plugins/define-plugin/ - 'window.atob': true + 'process.env.SWIPER_VERSION': `'${swiperVersion}'` }), new webpack.ProvidePlugin({ process: 'process/browser' @@ -162,10 +155,8 @@ const config = { 'DB_HANDLERS_ELECTRON_RENDERER_OR_WEB$': path.resolve(__dirname, '../src/datastores/handlers/web.js'), - // video.js's mpd-parser uses @xmldom/xmldom so that it can support both node and web browsers - // As FreeTube only runs in electron and web browsers, we can use the native DOMParser class, instead of the "polyfill" - // https://caniuse.com/mdn-api_domparser - '@xmldom/xmldom$': path.resolve(__dirname, '_domParser.js') + // change to "shaka-player.ui.debug.js" to get debug logs (update jsconfig to get updated types) + 'shaka-player$': 'shaka-player/dist/shaka-player.ui.js', }, fallback: { 'fs/promises': path.resolve(__dirname, '_empty.js'), @@ -186,7 +177,9 @@ config.plugins.push( processLocalesPlugin, new webpack.DefinePlugin({ 'process.env.LOCALE_NAMES': JSON.stringify(processLocalesPlugin.localeNames), - 'process.env.GEOLOCATION_NAMES': JSON.stringify(fs.readdirSync(path.join(__dirname, '..', 'static', 'geolocations')).map(filename => filename.replace('.json', ''))) + 'process.env.GEOLOCATION_NAMES': JSON.stringify(fs.readdirSync(path.join(__dirname, '..', 'static', 'geolocations')).map(filename => filename.replace('.json', ''))), + 'process.env.SHAKA_LOCALE_MAPPINGS': JSON.stringify(SHAKA_LOCALE_MAPPINGS), + 'process.env.SHAKA_LOCALES_PREBUNDLED': JSON.stringify(SHAKA_LOCALES_PREBUNDLED) }), new CopyWebpackPlugin({ patterns: [ @@ -201,7 +194,12 @@ config.plugins.push( dot: true, ignore: ['**/.*', '**/locales/**', '**/pwabuilder-sw.js', '**/dashFiles/**', '**/storyboards/**'], }, - }, + }, + { + from: path.join(__dirname, '../node_modules/shaka-player/ui/locales', `{${SHAKA_LOCALES_TO_BE_BUNDLED.join(',')}}.json`).replaceAll('\\', '/'), + to: path.join(__dirname, '../dist/web/static/shaka-player-locales'), + context: path.join(__dirname, '../node_modules/shaka-player/ui/locales') + } ] }) ) diff --git a/jsconfig.json b/jsconfig.json index 3dd46670d49e2..dad8a794996bf 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -9,6 +9,9 @@ "DB_HANDLERS_ELECTRON_RENDERER_OR_WEB": [ "src/datastores/handlers/electron", "src/datastores/handlers/web" + ], + "shaka-player": [ + "./node_modules/shaka-player/dist/shaka-player.ui" ] } } diff --git a/package.json b/package.json index a9ae18cc225a1..9467bf5a471ce 100644 --- a/package.json +++ b/package.json @@ -19,19 +19,20 @@ "url": "https://github.com/FreeTubeApp/FreeTube/issues" }, "scripts": { - "build": "run-s rebuild:electron pack build-release", - "build:arm64": "run-s rebuild:electron pack build-release:arm64", - "build:arm32": "run-s rebuild:electron pack build-release:arm32", + "build": "run-s rebuild:electron patch-shaka pack build-release", + "build:arm64": "run-s rebuild:electron patch-shaka pack build-release:arm64", + "build:arm32": "run-s rebuild:electron patch-shaka pack build-release:arm32", "build-release": "node _scripts/build.js", "build-release:arm64": "node _scripts/build.js arm64", "build-release:arm32": "node _scripts/build.js arm32", "clean": "rimraf build/ dist/", - "debug": "run-s rebuild:electron debug-runner", + "debug": "run-s rebuild:electron patch-shaka debug-runner", "debug-runner": "node _scripts/dev-runner.js --remote-debug", - "dev": "run-s rebuild:electron dev-runner", + "dev": "run-s rebuild:electron patch-shaka dev-runner", "dev:web": "node _scripts/dev-runner.js --web", "dev-runner": "node _scripts/dev-runner.js", "get-instances": "node _scripts/getInstances.js", + "patch-shaka": "node _scripts/patchShaka.mjs", "get-regions": "node _scripts/getRegions.mjs", "lint-all": "run-p lint lint-json", "lint": "run-p eslint-lint lint-style", @@ -46,7 +47,7 @@ "pack:main": "webpack --mode=production --node-env=production --config _scripts/webpack.main.config.js", "pack:renderer": "webpack --mode=production --node-env=production --config _scripts/webpack.renderer.config.js", "pack:web": "webpack --mode=production --node-env=production --config _scripts/webpack.web.config.js", - "postinstall": "yarn run --silent rebuild:electron", + "postinstall": "run-s --silent rebuild:electron patch-shaka", "prettier": "prettier --write \"{src,_scripts}/**/*.{js,vue}\"", "rebuild:electron": "electron-builder install-app-deps", "release": "run-s test build", @@ -58,20 +59,14 @@ "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/vue-fontawesome": "^2.0.10", "@seald-io/nedb": "^4.0.4", - "@silvermine/videojs-quality-selector": "^1.3.1", "autolinker": "^4.0.0", "electron-context-menu": "^3.6.1", "lodash.debounce": "^4.0.8", "marked": "^12.0.1", "path-browserify": "^1.0.1", "process": "^0.11.10", + "shaka-player": "^4.7.13", "swiper": "^11.1.1", - "video.js": "7.21.5", - "videojs-contrib-quality-levels": "^3.0.0", - "videojs-http-source-selector": "^1.1.6", - "videojs-mobile-ui": "^0.8.0", - "videojs-overlay": "^3.1.0", - "videojs-vtt-thumbnails-freetube": "0.0.15", "vue": "^2.7.16", "vue-i18n": "^8.28.2", "vue-observe-visibility": "^1.0.0", diff --git a/src/main/index.js b/src/main/index.js index 928dfd7b6597c..e07d56f93acc9 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -28,13 +28,6 @@ function runApp() { showSelectAll: false, showCopyLink: false, prepend: (defaultActions, parameters, browserWindow) => [ - { - label: 'Show / Hide Video Statistics', - visible: parameters.mediaType === 'video', - click: () => { - browserWindow.webContents.send('showVideoStatistics') - } - }, { label: 'Open in a New Window', // Only show the option for in-app URLs and not external ones @@ -302,7 +295,7 @@ function runApp() { // InnerTube rejects requests if the referer isn't YouTube or empty const innertubeAndMediaRequestFilter = { urls: ['https://www.youtube.com/youtubei/*', 'https://*.googlevideo.com/videoplayback?*'] } - session.defaultSession.webRequest.onBeforeSendHeaders(innertubeAndMediaRequestFilter, ({ requestHeaders, url, resourceType }, callback) => { + session.defaultSession.webRequest.onBeforeSendHeaders(innertubeAndMediaRequestFilter, ({ requestHeaders, url }, callback) => { requestHeaders.Referer = 'https://www.youtube.com/' requestHeaders.Origin = 'https://www.youtube.com' @@ -312,39 +305,6 @@ function runApp() { // YouTube doesn't send the Content-Type header for the media requests, so we shouldn't either delete requestHeaders['Content-Type'] } - - // YouTube throttles the adaptive formats if you request a chunk larger than 10MiB. - // For the DASH formats we are fine as video.js doesn't seem to ever request chunks that big. - // The legacy formats don't have any chunk size limits. - // For the audio formats we need to handle it ourselves, as the browser requests the entire audio file, - // which means that for most videos that are longer than 10 mins, we get throttled, as the audio track file sizes surpass that 10MiB limit. - - // This code checks if the file is larger than the limit, by checking the `clen` query param, - // which YouTube helpfully populates with the content length for us. - // If it does surpass that limit, it then checks if the requested range is larger than the limit - // (seeking right at the end of the video, would result in a small enough range to be under the chunk limit) - // if that surpasses the limit too, it then limits the requested range to 10MiB, by setting the range to `start-${start + 10MiB}`. - if (resourceType === 'media' && url.includes('&mime=audio') && requestHeaders.Range) { - const TEN_MIB = 10 * 1024 * 1024 - - const contentLength = parseInt(new URL(url).searchParams.get('clen')) - - if (contentLength > TEN_MIB) { - const [startStr, endStr] = requestHeaders.Range.split('=')[1].split('-') - - const start = parseInt(startStr) - - // handle open ended ranges like `0-` and `1234-` - const end = endStr.length === 0 ? contentLength : parseInt(endStr) - - if (end - start > TEN_MIB) { - const newEnd = start + TEN_MIB - - requestHeaders.Range = `bytes=${start}-${newEnd}` - } - } - } - // eslint-disable-next-line n/no-callback-literal callback({ requestHeaders }) }) diff --git a/src/renderer/App.vue b/src/renderer/App.vue index fb49f342eec88..f818c28f98736 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -91,5 +91,4 @@