diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index db4c6d9b..00000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index e24971ce..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - greasemonkey: true, - node: true, - }, - extends: ["eslint:recommended", "prettier"], - plugins: ["prettier"], - overrides: [ - { - env: { - node: true, - }, - files: [".eslintrc.{js,cjs}"], - parserOptions: { - sourceType: "script", - }, - }, - ], - parserOptions: { - ecmaVersion: "latest", - sourceType: "module", - }, - rules: { - "prettier/prettier": [ - "error", - { - endOfLine: "auto", - }, - ], - "no-control-regex": 0, - "no-async-promise-executor": 0, - }, - globals: { - // IMPORTED SCRIPTS - protobuf: "readonly", - Hls: "readonly", - // WEBPACK ENVIRONMENT - BUILD_MODE: "readonly", - DEBUG_MODE: "readonly", - IS_BETA_VERSION: "readonly", - __MK_GLOBAL__: "readonly", - // YOUTUBE PAGE API - ytplayer: "readonly", - }, -}; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3f719b3..00aa3f8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [16.x, 20.x, 22.x] + node-version: [18.x, 22.x] steps: - name: Checkout 🛎️ uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index b56d4952..13b8d1c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +dist/test-ui.user.js +dist/*.txt + src/localization/locales/generate_phrase.py src/localization/locales/regenerate_locales.py src/localization/locales/remove_phrase.py diff --git a/.oxlintignore b/.oxlintignore new file mode 100644 index 00000000..53c37a16 --- /dev/null +++ b/.oxlintignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/.webpack/config.test.js b/.webpack/config.test.js new file mode 100644 index 00000000..47e83a8c --- /dev/null +++ b/.webpack/config.test.js @@ -0,0 +1,66 @@ +import path from "path"; +import { fileURLToPath } from "url"; + +import webpack from "webpack"; + +import { monkey } from "webpack-monkey"; +import { styleLoaderInsertStyleElement } from "webpack-monkey/lib/client/css.js"; +import ESLintPlugin from "eslint-webpack-plugin"; +import TerserPlugin from "terser-webpack-plugin"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.resolve(path.dirname(__filename), ".."); + +export default () => { + return monkey({ + mode: "production", + resolve: { + extensions: [".js"], + }, + performance: { + hints: "error", + maxEntrypointSize: 2000 * 10 ** 3, + maxAssetSize: 2000 * 10 ** 3, + }, + entry: path.resolve(__dirname, "tests", "ui.js"), + output: { + path: path.resolve(__dirname, "dist"), + filename: "test-ui.user.js", + }, + monkey: { + debug: false, + meta: { + resolve: path.resolve(__dirname, "tests", "headers.json"), + }, + }, + plugins: [ + new ESLintPlugin({ + configType: "flat", + }), + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + new webpack.DefinePlugin({ + DEBUG_MODE: true, + IS_BETA_VERSION: false, + __MK_GLOBAL__: { + styleLoaderInsertStyleElement, + }, + }), + ], + module: { + rules: [ + { + test: /\.(css|scss|sass)$/i, + use: ["style-loader", "css-loader", "sass-loader"], + }, + ], + }, + optimization: { + emitOnErrors: true, + moduleIds: "named", + minimize: false, + minimizer: [new TerserPlugin()], + }, + }); +}; diff --git a/webpack.config.js b/.webpack/webpack.config.js similarity index 64% rename from webpack.config.js rename to .webpack/webpack.config.js index 554dc081..479a6467 100644 --- a/webpack.config.js +++ b/.webpack/webpack.config.js @@ -9,11 +9,18 @@ import { styleLoaderInsertStyleElement } from "webpack-monkey/lib/client/css.js" import ESLintPlugin from "eslint-webpack-plugin"; import TerserPlugin from "terser-webpack-plugin"; +import { + sitesInvidious, + sitesPiped, + sitesProxiTok, + sitesPeertube, +} from "vot.js/alternativeUrls"; + const repo = "https://raw.githubusercontent.com/ilyhalight/voice-over-translation"; const dev = process.env.NODE_ENV === "development"; const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +const __dirname = path.resolve(path.dirname(__filename), ".."); let isBeta = getHeaders().version.includes("beta"); console.log("development mode: ", dev); @@ -29,16 +36,16 @@ function getHeaders(lang) { } export default (env) => { - const build_mode = env.build_mode; + //const build_mode = env.build_mode; const build_type = env.build_type; - console.log("build mode: ", build_mode); + //console.log("build mode: ", build_mode); console.log("build type: ", build_type); - function get_filename() { + function getFilename() { let name = "vot"; - if (build_mode === "cloudflare") { - name += "-cloudflare"; - } + // if (build_mode === "cloudflare") { + // name += "-cloudflare"; + // } if (build_type === "minify") { name += "-min"; @@ -47,15 +54,24 @@ export default (env) => { return name + ".user.js"; } - function get_name_by_build_mode(name) { - let finalName = - build_mode === "cloudflare" - ? name.replace("[VOT]", "[VOT Cloudflare]") - : name; - - return finalName; + function altUrlsToMatch() { + // autogenerating match list by alternative urls sites + return [sitesInvidious, sitesPiped, sitesProxiTok, sitesPeertube] + .map((sites) => + sites.map((site) => { + const isSubdomain = site.match(/\./g)?.length > 1; + return `*://${isSubdomain ? "" : "*."}${site}/*`; + }), + ) + .flat(); } + // function getNameByBuildMode(name) { + // return build_mode === "cloudflare" + // ? name.replace("[VOT]", "[VOT Cloudflare]") + // : name; + // } + return monkey({ mode: dev ? "development" : "production", resolve: { @@ -69,14 +85,14 @@ export default (env) => { entry: path.resolve(__dirname, "src", "index.js"), output: { path: path.resolve(__dirname, "dist"), - ...(!dev ? { filename: get_filename() } : {}), + ...(!dev ? { filename: getFilename() } : {}), }, monkey: { debug: dev, meta: { - resolve: "headers.json", + resolve: path.resolve(__dirname, "src", "headers.json"), transform({ meta }) { - const extFileName = get_filename().slice(0, -8); + const extFileName = getFilename().slice(0, -8); const finalURL = `${repo}/${ isBeta ? "dev" : "master" }/dist/${extFileName}.user.js`; @@ -84,10 +100,10 @@ export default (env) => { meta.namespace = extFileName; meta.updateURL = meta.downloadURL = finalURL; - if (build_mode === "cloudflare") { - meta.name = meta.name.replace("[VOT]", "[VOT Cloudflare]"); - meta["inject-into"] = "page"; - } + // if (build_mode === "cloudflare") { + // meta.name = meta.name.replace("[VOT]", "[VOT Cloudflare]"); + // meta["inject-into"] = "page"; + // } const files = fs.readdirSync( path.resolve(__dirname, "src", "locales"), @@ -105,22 +121,29 @@ export default (env) => { const localeHeaders = getHeaders(file); const lang = file.substring(0, 2); - meta.name[lang] = get_name_by_build_mode(localeHeaders.name); + meta.name[lang] = localeHeaders.name; meta.description[lang] = localeHeaders.description; } + meta.match = Array.from( + new Set([...meta.match, ...altUrlsToMatch()]), + ); + return meta; }, }, }, plugins: [ - new ESLintPlugin(), + new ESLintPlugin({ + configType: "flat", + }), new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1, }), new webpack.DefinePlugin({ - BUILD_MODE: JSON.stringify(build_mode), - DEBUG_MODE: dev, + // BUILD_MODE: JSON.stringify(build_mode), + // DEBUG_MODE: dev, + DEBUG_MODE: true, IS_BETA_VERSION: isBeta, ...(() => { if (!dev) { @@ -144,7 +167,7 @@ export default (env) => { optimization: { emitOnErrors: true, moduleIds: "named", - minimize: build_type === "minify" ? true : false, + minimize: build_type === "minify", minimizer: [new TerserPlugin()], }, }); diff --git a/README-EN.md b/README-EN.md index da927c78..af4294ad 100644 --- a/README-EN.md +++ b/README-EN.md @@ -3,6 +3,9 @@ [![ru](https://img.shields.io/badge/%D1%8F%D0%B7%D1%8B%D0%BA-%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%20%F0%9F%87%B7%F0%9F%87%BA-white)](README.md) [![en](https://img.shields.io/badge/lang-English%20%F0%9F%87%AC%F0%9F%87%A7-white)](README-EN.md) +> [!CAUTION] +> WIP. All necessary depends will be opened with the release of beta + > [!CAUTION] > Before creating Issues, we strongly recommend that you read the [FAQ](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) section, as well as with existing [Issues](https://github.com/ilyhalight/voice-over-translation/issues) @@ -78,6 +81,11 @@ You can see all the restrictions related to site support in [wiki](https://githu - **[ProxiTok](https://proxitok.pabloferreiro.es/)** - **[Invidious](https://yewtu.be)** - **[Piped](https://piped.video)** +- **[Kodik (player)](https://kodik.cc)** +- **[Patreon](https://patreon.com)** +- **[Reddit](https://reddit.com)** +- **[Kick](https://kick.com)** +- **[Apple Developer](https://developer.apple.com)** - **Any direct web links to `.mp4`** ⚠️ - Requires additional actions, more in **[Wiki](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-Supported-sites)** @@ -103,7 +111,7 @@ These domains can be set in the extension settings (only those domains that can ## How to build an extension? -1. Install NodeJS 18+ +1. Install Node.js 18.18+ / Bun.sh 2. Install dependencies: ```bash @@ -153,6 +161,25 @@ npm i npm run prepare ``` +### How to patch vot.js + +1. Make the necessary changes to node_modules/vot.js/the required file +2. Save the changes using: + +NPM (not tested, but it should work): + +```bash +npx patch-package vot.js +``` + +Bun: + +```bash +bunx patch-package --use-yarn vot.js +``` + +Do not use patches from the `bun patch` or from any npm packages. They will be incompatible with the patch-package, and may also break when updating the package. + ## Customization of appearance: The extension supports customization of the appearance using Stylus, Stylish and other similar extensions @@ -178,7 +205,6 @@ Example of changing styles: --vot-surface-rgb: 32, 33, 36; --vot-onsurface-rgb: 227, 227, 227; - --vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8); --vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227)); --vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243)); } @@ -186,24 +212,25 @@ Example of changing styles: ## The extension has been tested in the following browsers: -| Status | Browser | Browser Version | Platform | Extension | -| ------ | ------------------------- | --------------------------------- | ----------------------- | ------------------------------------------------------------------ | -| ⠀✅ | Firefox Developer Edition | v106 — v128, 64 bit | Windows | Tampermonkey (MV2) | -| ⠀✅ | Firefox | v116.0.2 | Windows, Linux, Android | Tampermonkey (MV2), Violetmonkey | -| ⠀✅ | Firefox Nightly | v118.0a1 | Windows, Android | Tampermonkey (MV2) | -| ⠀✅ | LibreWolf | v100.0.2-1 | Windows | Tampermonkey (MV2) | -| ⠀✅ | Brave | v106.0.5249.91 | Windows | Tampermonkey (MV2) | -| ⠀✅ | MS Edge | v106.0.1370.34 | Windows, Linux | Tampermonkey (MV2) | -| ⠀✅ | Cent Browser | v4.3.9.248, 32 bit | Windows | Tampermonkey (MV2) | -| ⠀✅ | Cent Browser Beta | v5.0.1002.182, 64 bit | Windows | Tampermonkey (MV2) | -| ⠀✅ | Google Chrome | v106 — 126 | Windows, MacOS, Linux | Tampermonkey (MV2), Tampermonkey (MV3), Violetmonkey, OrangeMonkey | -| ⠀✅ | Opera GX | LVL4 (core: 91) - LVL5 (core 109) | Windows | Tampermonkey Opera | -| ⠀✅ | Opera | v92.0.4561.43 | Windows | Tampermonkey Opera | -| ⠀✅ | Vivaldi | 5.7.2921.63 | Windows, Linux | Tampermonkey (MV2) | -| ⠀✅ | Safari | v15.6.1 | MacOS, iOS | Userscripts | -| ⠀✅ | Kiwi Browser | v116.0.5845.61 | Android | Tampermonkey (MV2) | -| ⠀✅ | Yandex Browser | v24.4-24.6 | Windows | Tampermonkey (MV2), Tampermonkey (MV3) | -| ⠀✅ | Arc | v1.6.1 | Windows | Tampermonkey (MV3) | +| Status | Browser | Browser Version | Platform | Extension | +| ------ | ------------------------- | --------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------- | +| ⠀✅ | Firefox Developer Edition | v106 — v129, 64 bit | Windows | Tampermonkey (MV2), FireMonkey, VioletMonkey, Greasemonkey | +| ⠀✅ | Firefox | v116.0.2 | Windows, Linux, Android | Tampermonkey (MV2), Violetmonkey | +| ⠀✅ | Firefox Nightly | v118.0a1 | Windows, Android | Tampermonkey (MV2) | +| ⠀✅ | LibreWolf | v100.0.2-1 | Windows | Tampermonkey (MV2) | +| ⠀✅ | Brave | v1.46 - v1.68.134 | Windows | Tampermonkey (MV2) | +| ⠀✅ | MS Edge | v106.0.1370.34 | Windows, Linux | Tampermonkey (MV2) | +| ⠀✅ | Cent Browser | v4.3.9.248, 32 bit | Windows | Tampermonkey (MV2) | +| ⠀✅ | Cent Browser Beta | v5.0.1002.182, 64 bit | Windows | Tampermonkey (MV2) | +| ⠀✅ | Google Chrome | v106 — v127 | Windows, MacOS, Linux | Tampermonkey (MV2), Tampermonkey (MV3), Violetmonkey, OrangeMonkey, User Javascript and CSS | +| ⠀✅ | Opera GX | LVL4 (core: 91) - LVL5 (core 109) | Windows | Tampermonkey Opera | +| ⠀✅ | Opera | v92.0.4561.43 | Windows | Tampermonkey Opera | +| ⠀✅ | Vivaldi | 5.7.2921.63 | Windows, Linux | Tampermonkey (MV2) | +| ⠀✅ | Safari | v15.6.1 | MacOS, iOS | Userscripts | +| ⠀✅ | Kiwi Browser | v116.0.5845.61 | Android | Tampermonkey (MV2) | +| ⠀✅ | Yandex Browser | v24.4-24.6 | Windows | Tampermonkey (MV2), Tampermonkey (MV3) | +| ⠀✅ | Arc | v1.6.1 | Windows | Tampermonkey (MV3) | +| ⠀✅ | Incognition | v4.1.1.0 (v125) | Windows | Tampermonkey (MV3), Tampermonkey (MV2) | Working with the modern version of Tampermonkey (MV3) has not been tested in most browsers. If there are problems, it is recommended to use Tampermonkey Legacy (MV2) @@ -211,19 +238,18 @@ To activate the script in Tampermonkey (MV3), you must [enable "Developer Mode"] ## Tested in the following extensions for user scripts: -| Status | Browser | Extension | -| ---------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | -| ⠀✅ | Any | Tampermonkey Legacy (MV2) | -| ⠀✅ | Opera | Tampermonkey Opera | -| ⠀✅ | Chrome | Tampermonkey (MV3) | -| ⠀[⚠️ Download](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Safari | Userscripts | -| ⠀[⚠️ Download](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Any | Violetmonkey | -| ⠀[⚠️ Download](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Any | [AdGuard Usercripts](https://kb.adguard.com/en/general/userscripts#supported-apps) | -| ⠀[⚠️ Download](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Firefox | Firemonkey | -| ⠀[⚠️ Download](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Any | Greasemonkey | -| ⠀[⚠️ Download](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Any | OrangeMonkey | - -⚠️ - They are not priority extensions. These extensions, due to the "cloudflare" version of the user script, do not have cross-site synchronization of settings, and are also (practically) not tested before the release of a new version of the user script. +| Status | Browser | Extension | +| -------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | +| ⠀✅ | Any | Tampermonkey Legacy (MV2) | +| ⠀✅ | Opera | Tampermonkey Opera | +| ⠀✅ | Chrome | Tampermonkey (MV3) | +| ⠀❔ | Safari | Userscripts | +| ⠀✅ | Any | Violetmonkey | +| ⠀❔ | Any | [AdGuard Usercripts](https://kb.adguard.com/en/general/userscripts#supported-apps) | +| ⠀[Install guide](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ#how-to-use-the-extension-with-firemonkey) | Firefox | Firemonkey | +| ⠀✅ | Firefox | Greasemonkey | +| ⚠️ requestIdleCallback is full of errors, but it works | Any | OrangeMonkey | +| ⠀[Install guide](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ#how-to-use-the-extension-with-user-js-and-css) | Any | User Javascript and CSS | ![example btn](https://github.com/ilyhalight/voice-over-translation/blob/master/img/example_en.png "btn") diff --git a/README.md b/README.md index c7cfc676..40dc606e 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ [![en](https://img.shields.io/badge/lang-English%20%F0%9F%87%AC%F0%9F%87%A7-white)](README-EN.md) [![ru](https://img.shields.io/badge/%D1%8F%D0%B7%D1%8B%D0%BA-%D0%A0%D1%83%D1%81%D1%81%D0%BA%D0%B8%D0%B9%20%F0%9F%87%B7%F0%9F%87%BA-white)](README.md) +> [!CAUTION] +> WIP. Все необходимые зависимости будут открыты с выходом беты + > [!CAUTION] > Перед созданием Issues настоятельно рекомендуем ознакомиться с разделом [FAQ](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ), а так же с уже существующими [Issues](https://github.com/ilyhalight/voice-over-translation/issues) @@ -79,6 +82,11 @@ - **[ProxiTok](https://proxitok.pabloferreiro.es/)** - **[Invidious](https://yewtu.be)** - **[Piped](https://piped.video)** +- **[Kodik (player)](https://kodik.cc)** +- **[Patreon](https://patreon.com)** +- **[Reddit](https://reddit.com)** +- **[Kick](https://kick.com)** +- **[Apple Developer](https://developer.apple.com)** - **Любые прямые веб-ссылки на `.mp4`** ⚠️ - Требует дополнительных действий, подробнее в **[Wiki](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-Supported-sites)** @@ -104,11 +112,19 @@ ## Как собрать расширение? -1. Установите NodeJS 18+ +1. Установите Node.js 18.18+ / Bun 2. Установите зависимости: +NPM: + ```bash -npm i +npm install +``` + +Bun: + +```bash +bun install --yarn ``` 3. Сборка расширения: @@ -154,6 +170,25 @@ npm i npm run prepare ``` +### Как патчить vot.js + +1. Внесите нужные изменения в node_modules/vot.js/нужный файл +2. Сохраните изменения с помощью: + +NPM (не тестил, но должно работать): + +```bash +npx patch-package vot.js +``` + +Bun: + +```bash +bunx patch-package --use-yarn vot.js +``` + +Не пользуйтесь патчами из `bun patch` или из каких-либо npm пакетов. Они будут несовместимы с patch-package, а так же могут ломаться при обновлении пакета. + ## Кастомизация внешнего вида: Расширение поддерживает кастомизацию внешнего вида с помощью Stylus, Stylish и других подобных расширений @@ -179,7 +214,6 @@ npm i --vot-surface-rgb: 32, 33, 36; --vot-onsurface-rgb: 227, 227, 227; - --vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8); --vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227)); --vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243)); } @@ -187,24 +221,25 @@ npm i ## Расширение протестировано в следующих браузерах: -| Статус | Браузер | Версия браузера | Платформа | Расширение | -| ------ | ------------------------- | --------------------------------- | ----------------------- | ------------------------------------------------------------------ | -| ⠀✅ | Firefox Developer Edition | v106 — v128, 64 bit | Windows | Tampermonkey (MV2) | -| ⠀✅ | Firefox | v116.0.2 | Windows, Linux, Android | Tampermonkey (MV2), Violetmonkey | -| ⠀✅ | Firefox Nightly | v118.0a1 | Windows, Android | Tampermonkey (MV2) | -| ⠀✅ | LibreWolf | v100.0.2-1 | Windows | Tampermonkey (MV2) | -| ⠀✅ | Brave | v106.0.5249.91 | Windows | Tampermonkey (MV2) | -| ⠀✅ | MS Edge | v106.0.1370.34 | Windows, Linux | Tampermonkey (MV2) | -| ⠀✅ | Cent Browser | v4.3.9.248, 32 bit | Windows | Tampermonkey (MV2) | -| ⠀✅ | Cent Browser Beta | v5.0.1002.182, 64 bit | Windows | Tampermonkey (MV2) | -| ⠀✅ | Google Chrome | v106 — 126 | Windows, MacOS, Linux | Tampermonkey (MV2), Tampermonkey (MV3), Violetmonkey, OrangeMonkey | -| ⠀✅ | Opera GX | LVL4 (core: 91) - LVL5 (core 109) | Windows | Tampermonkey Opera | -| ⠀✅ | Opera | v92.0.4561.43 | Windows | Tampermonkey Opera | -| ⠀✅ | Vivaldi | 5.7.2921.63 | Windows, Linux | Tampermonkey (MV2) | -| ⠀✅ | Safari | v15.6.1 | MacOS, iOS | Userscripts | -| ⠀✅ | Kiwi Browser | v116.0.5845.61 | Android | Tampermonkey (MV2) | -| ⠀✅ | Yandex Browser | v24.4-24.6 | Windows | Tampermonkey (MV2), Tampermonkey (MV3) | -| ⠀✅ | Arc | v1.6.1 | Windows | Tampermonkey (MV3) | +| Статус | Браузер | Версия браузера | Платформа | Расширение | +| ------ | ------------------------- | --------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------- | +| ⠀✅ | Firefox Developer Edition | v106 — v129, 64 bit | Windows | Tampermonkey (MV2), FireMonkey, VioletMonkey, Greasemonkey | +| ⠀✅ | Firefox | v116.0.2 | Windows, Linux, Android | Tampermonkey (MV2), Violetmonkey | +| ⠀✅ | Firefox Nightly | v118.0a1 | Windows, Android | Tampermonkey (MV2) | +| ⠀✅ | LibreWolf | v100.0.2-1 | Windows | Tampermonkey (MV2) | +| ⠀✅ | Brave | v1.46 - v1.68.134 | Windows | Tampermonkey (MV2) | +| ⠀✅ | MS Edge | v106.0.1370.34 | Windows, Linux | Tampermonkey (MV2) | +| ⠀✅ | Cent Browser | v4.3.9.248, 32 bit | Windows | Tampermonkey (MV2) | +| ⠀✅ | Cent Browser Beta | v5.0.1002.182, 64 bit | Windows | Tampermonkey (MV2) | +| ⠀✅ | Google Chrome | v106 — v127 | Windows, MacOS, Linux | Tampermonkey (MV2), Tampermonkey (MV3), Violetmonkey, OrangeMonkey, User Javascript and CSS | +| ⠀✅ | Opera GX | LVL4 (core: 91) - LVL5 (core 109) | Windows | Tampermonkey Opera | +| ⠀✅ | Opera | v92.0.4561.43 | Windows | Tampermonkey Opera | +| ⠀✅ | Vivaldi | 5.7.2921.63 | Windows, Linux | Tampermonkey (MV2) | +| ⠀✅ | Safari | v15.6.1 | MacOS, iOS | Userscripts | +| ⠀✅ | Kiwi Browser | v116.0.5845.61 | Android | Tampermonkey (MV2) | +| ⠀✅ | Yandex Browser | v24.4-24.6 | Windows | Tampermonkey (MV2), Tampermonkey (MV3) | +| ⠀✅ | Arc | v1.6.1 | Windows | Tampermonkey (MV3) | +| ⠀✅ | Incognition | v4.1.1.0 (v125) | Windows | Tampermonkey (MV3), Tampermonkey (MV2) | Работа с современной версией Tampermonkey (MV3) не тестировалась в большинстве браузеров. При наличие проблем рекомендуется использовать Tampermonkey Legacy (MV2) @@ -212,19 +247,18 @@ npm i ## Протестировано в следующих расширениях для юзерскриптов: -| Статус | Браузер | Расширение | -| ----------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | -| ⠀✅ | Любой | Tampermonkey Legacy (MV2) | -| ⠀✅ | Opera | Tampermonkey Opera | -| ⠀✅ | Chrome | Tampermonkey (MV3) | -| ⠀[⚠️ Загрузить](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Safari | Userscripts | -| ⠀[⚠️ Загрузить](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Любой | Violetmonkey | -| ⠀[⚠️ Загрузить](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Любой | [AdGuard Usercripts](https://kb.adguard.com/en/general/userscripts#supported-apps) | -| ⠀[⚠️ Загрузить](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Firefox | Firemonkey | -| ⠀[⚠️ Загрузить](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Любой | Greasemonkey | -| ⠀[⚠️ Загрузить](https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js) | Любой | OrangeMonkey | - -⚠️ - Не являются приоритетными расширениями. Эти расширения из-за "cloudflare" версии юзерскрипта не имеют межсайтовой синхронизации настроек, а так же (практически) не тестируются перед выходом новой версии юзерскрипта. +| Статус | Браузер | Расширение | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | +| ⠀✅ | Любой | Tampermonkey Legacy (MV2) | +| ⠀✅ | Opera | Tampermonkey Opera | +| ⠀✅ | Chrome | Tampermonkey (MV3) | +| ⠀❔ | Safari | Userscripts | +| ⠀✅ | Любой | Violetmonkey | +| ⠀❔ | Любой | [AdGuard Usercripts](https://kb.adguard.com/en/general/userscripts#supported-apps) | +| ⠀[Гайд по установке](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ#%D0%BA%D0%B0%D0%BA-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D0%B5-%D1%81-firemonkey) | Firefox | Firemonkey | +| ⠀✅ | Firefox | Greasemonkey | +| ⚠️ RequestIdleCallback сыпет ошибками, но работает | Любой | OrangeMonkey | +| ⠀[Гайд по установке](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ#%D0%BA%D0%B0%D0%BA-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D0%B5-%D1%81-user-js-and-css) | Любой | User Javascript and CSS | ![example btn](https://github.com/ilyhalight/voice-over-translation/blob/master/img/example.png "btn") diff --git a/bun.lockb b/bun.lockb index 656044c6..9ef29f3c 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/changelog.md b/changelog.md index ae28fc0c..b5397f28 100644 --- a/changelog.md +++ b/changelog.md @@ -1,35 +1,50 @@ - - - + + -# 1.x +# 1.6.0 -- Добавлена возможность увеличения громкости аудио до 900% (#449) -- Улучшена скорость получения айди видео (#686) -- Исправлен жирный шрифт селекторов меню на некоторых сайтах -- Добавлена поддержка русского домена XVideos (`xv-ru.com`) -- Стандартный домен в cloudflare-версии изменен на `vot-worker.toil.cc` +- Функционал запросов к API переписан с использованием vot.js +- Добавлена поддержка Kick (записи трансляций до 4 часов (/video/VIDEO_ID) + клипы) +- Добавлена поддержка Kodik +- Добавлена поддержка Reddit +- Добавлена поддержка Patreon - Добавлена поддержка Archive.org (#679) -- Теперь, при включенной опции "Не переводить с родного языка" проверяется соответствие только языка с которого переводится видео (ранее срабатывало только при одинаковой паре языков, например русский-русский, английкий-англиский и т.п.) -- Добавлена возможность выделения отдельных слов в субтитрах (#666) +- Добавлена поддержка курсов без субтитров для Udemy +- Добавлена поддержка просмотра видео на стене в VK - Добавлена поддержка VK Clips - Добавлена поддержка YouTube Live (https://youtube.com/live/VIDEO_ID) (#657) - Добавлена поддержка Bitchute embed +- Добавлена поддержка Apple Developer +- Добавлена поддержка русского домена XVideos (`xv-ru.com`) +- Для работы с Udemy больше не нужно вводить Udemy Access Token +- Обновлен список поддерживаемых сайтов для Invidious, ProxiTok, Peertube +- Убрана кнопка с прямых ссылок на видео с localhost / 127.0.0.1 +- Добавлена возможность увеличения громкости аудио до 900% (#449) +- Улучшена скорость получения айди видео (#686) +- Исправлен жирный шрифт селекторов меню на некоторых сайтах +- Стандартный домен для проксирования изменен на `vot-worker.toil.cc` +- Теперь, при включенной опции "Не переводить с родного языка" проверяется, только, соответствие языка с которого переводится видео (ранее срабатывало, только, при одинаковой паре языков, например русский-русский, английкий-англиский и т.п.) +- Добавлена возможность выделения отдельных слов в субтитрах (#666) +- Добавлена возможность изменить размер шрифта субтитров (#720) +- Добавлена возможность изменить прозрачность фона субтитров (#720) - Обновлена логика получения айди для clips.twitch.tv. Добавлена поддержка не только встроенных (embed) клипов - Исправлено неправильное формирование параметра запроса для weverse - Для dailymotion и yadisk итоговые ссылки заменены на короткие варианты - Исправлен дополнительный селектор для twitter +- Работа с UI частично переписана на lit для работы с "Trusted Types" +- Исправлено накладывание друг на друга названия и плейсхолдера поля ввода - Исправлен селектор для Bitchute - Исправлен селектор для Facebook - Proxytok переименован в Proxitok - Стандартный формат загружаемых субтитров изменен на srt (#644) - Фикс формирования строки с оставшимся временем перевода (#643) -- Добавлены часы в иконку при ожидание перевода +- Добавлена индикация долгих операций (ожидание перевода, ожидание перевода текста, ожидание проверки аудиодорожки) - Исправлен баг из-за которого реклама считалась за отдельные видео (#642) - Фикс отображения кнопки для youku (#636) +- Исправлены некоторые визуальные недочеты - Другие мелкие фиксы # 1.5.3.1 diff --git a/dist/vot-cloudflare-min.user.js b/dist/vot-cloudflare-min.user.js index 761fb723..27a7615d 100644 --- a/dist/vot-cloudflare-min.user.js +++ b/dist/vot-cloudflare-min.user.js @@ -77,6 +77,7 @@ // @match *://*.vimeo.com/* // @match *://*.9gag.com/* // @match *://*.twitter.com/* +// @match *://*.x.com/* // @match *://*.facebook.com/* // @match *://*.rutube.ru/* // @match *://*.bilibili.com/* @@ -121,6 +122,11 @@ // @match *://*.egghead.io/* // @match *://*.youku.com/* // @match *://*.archive.org/* +// @match *://*.patreon.com/* +// @match *://*.reddit.com/* +// @match *://*.kodik.info/* +// @match *://*.kodik.biz/* +// @match *://*.kodik.cc/* // @match *://*/*.mp4* // @exclude file://*/*.mp4* // @connect yandex.ru @@ -131,13 +137,13 @@ // @connect onrender.com // @connect workers.dev // @namespace vot-cloudflare-min -// @version 1.5.3.1 +// @version 1.6.0-beta.1 // @icon https://translate.yandex.ru/icons/favicon.ico // @author sodapng, mynovelhost, Toil, SashaXser, MrSoczekXD // @homepageURL https://github.com/ilyhalight/voice-over-translation/issues -// @updateURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare-min.user.js -// @downloadURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare-min.user.js +// @updateURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/dev/dist/vot-cloudflare-min.user.js +// @downloadURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/dev/dist/vot-cloudflare-min.user.js // @supportURL https://github.com/ilyhalight/voice-over-translation/issues // ==/UserScript== -(()=>{var t={"./node_modules/bowser/es5.js":function(t){t.exports=function(t){var e={};function o(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,o),i.l=!0,i.exports}return o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)o.d(n,i,function(e){return t[e]}.bind(null,i));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=90)}({17:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n=o(18),i=function(){function t(){}return t.getFirstMatch=function(t,e){var o=e.match(t);return o&&o.length>0&&o[1]||""},t.getSecondMatch=function(t,e){var o=e.match(t);return o&&o.length>1&&o[2]||""},t.matchAndReturnConst=function(t,e,o){if(t.test(e))return o},t.getWindowsVersionName=function(t){switch(t){case"NT":return"NT";case"XP":case"NT 5.1":return"XP";case"NT 5.0":return"2000";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return}},t.getMacOSVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),10===e[0])switch(e[1]){case 5:return"Leopard";case 6:return"Snow Leopard";case 7:return"Lion";case 8:return"Mountain Lion";case 9:return"Mavericks";case 10:return"Yosemite";case 11:return"El Capitan";case 12:return"Sierra";case 13:return"High Sierra";case 14:return"Mojave";case 15:return"Catalina";default:return}},t.getAndroidVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),!(1===e[0]&&e[1]<5))return 1===e[0]&&e[1]<6?"Cupcake":1===e[0]&&e[1]>=6?"Donut":2===e[0]&&e[1]<2?"Eclair":2===e[0]&&2===e[1]?"Froyo":2===e[0]&&e[1]>2?"Gingerbread":3===e[0]?"Honeycomb":4===e[0]&&e[1]<1?"Ice Cream Sandwich":4===e[0]&&e[1]<4?"Jelly Bean":4===e[0]&&e[1]>=4?"KitKat":5===e[0]?"Lollipop":6===e[0]?"Marshmallow":7===e[0]?"Nougat":8===e[0]?"Oreo":9===e[0]?"Pie":void 0},t.getVersionPrecision=function(t){return t.split(".").length},t.compareVersions=function(e,o,n){void 0===n&&(n=!1);var i=t.getVersionPrecision(e),a=t.getVersionPrecision(o),r=Math.max(i,a),s=0,l=t.map([e,o],(function(e){var o=r-t.getVersionPrecision(e),n=e+new Array(o+1).join(".0");return t.map(n.split("."),(function(t){return new Array(20-t.length).join("0")+t})).reverse()}));for(n&&(s=r-Math.min(i,a)),r-=1;r>=s;){if(l[0][r]>l[1][r])return 1;if(l[0][r]===l[1][r]){if(r===s)return 0;r-=1}else if(l[0][r]1?i-1:0),r=1;r0){var r=Object.keys(o),l=s.default.find(r,(function(t){return e.isOS(t)}));if(l){var d=this.satisfies(o[l]);if(void 0!==d)return d}var c=s.default.find(r,(function(t){return e.isPlatform(t)}));if(c){var u=this.satisfies(o[c]);if(void 0!==u)return u}}if(a>0){var h=Object.keys(i),p=s.default.find(h,(function(t){return e.isBrowser(t,!0)}));if(void 0!==p)return this.compareVersion(i[p])}},e.isBrowser=function(t,e){void 0===e&&(e=!1);var o=this.getBrowserName().toLowerCase(),n=t.toLowerCase(),i=s.default.getBrowserTypeByAlias(n);return e&&i&&(n=i.toLowerCase()),n===o},e.compareVersion=function(t){var e=[0],o=t,n=!1,i=this.getBrowserVersion();if("string"==typeof i)return">"===t[0]||"<"===t[0]?(o=t.substr(1),"="===t[1]?(n=!0,o=t.substr(2)):e=[],">"===t[0]?e.push(1):e.push(-1)):"="===t[0]?o=t.substr(1):"~"===t[0]&&(n=!0,o=t.substr(1)),e.indexOf(s.default.compareVersions(i,o,n))>-1},e.isOS=function(t){return this.getOSName(!0)===String(t).toLowerCase()},e.isPlatform=function(t){return this.getPlatformType(!0)===String(t).toLowerCase()},e.isEngine=function(t){return this.getEngineName(!0)===String(t).toLowerCase()},e.is=function(t,e){return void 0===e&&(e=!1),this.isBrowser(t,e)||this.isOS(t)||this.isPlatform(t)},e.some=function(t){var e=this;return void 0===t&&(t=[]),t.some((function(t){return e.is(t)}))},t}();e.default=d,t.exports=e.default},92:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=/version\/(\d+(\.?_?\d+)+)/i,r=[{test:[/googlebot/i],describe:function(t){var e={name:"Googlebot"},o=i.default.getFirstMatch(/googlebot\/(\d+(\.\d+))/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/opera/i],describe:function(t){var e={name:"Opera"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opr\/|opios/i],describe:function(t){var e={name:"Opera"},o=i.default.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/SamsungBrowser/i],describe:function(t){var e={name:"Samsung Internet for Android"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Whale/i],describe:function(t){var e={name:"NAVER Whale Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MZBrowser/i],describe:function(t){var e={name:"MZ Browser"},o=i.default.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/focus/i],describe:function(t){var e={name:"Focus"},o=i.default.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/swing/i],describe:function(t){var e={name:"Swing"},o=i.default.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/coast/i],describe:function(t){var e={name:"Opera Coast"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opt\/\d+(?:.?_?\d+)+/i],describe:function(t){var e={name:"Opera Touch"},o=i.default.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/yabrowser/i],describe:function(t){var e={name:"Yandex Browser"},o=i.default.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/ucbrowser/i],describe:function(t){var e={name:"UC Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Maxthon|mxios/i],describe:function(t){var e={name:"Maxthon"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/epiphany/i],describe:function(t){var e={name:"Epiphany"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/puffin/i],describe:function(t){var e={name:"Puffin"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sleipnir/i],describe:function(t){var e={name:"Sleipnir"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/k-meleon/i],describe:function(t){var e={name:"K-Meleon"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/micromessenger/i],describe:function(t){var e={name:"WeChat"},o=i.default.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/qqbrowser/i],describe:function(t){var e={name:/qqbrowserlite/i.test(t)?"QQ Browser Lite":"QQ Browser"},o=i.default.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/msie|trident/i],describe:function(t){var e={name:"Internet Explorer"},o=i.default.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/\sedg\//i],describe:function(t){var e={name:"Microsoft Edge"},o=i.default.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/edg([ea]|ios)/i],describe:function(t){var e={name:"Microsoft Edge"},o=i.default.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/vivaldi/i],describe:function(t){var e={name:"Vivaldi"},o=i.default.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/seamonkey/i],describe:function(t){var e={name:"SeaMonkey"},o=i.default.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sailfish/i],describe:function(t){var e={name:"Sailfish"},o=i.default.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i,t);return o&&(e.version=o),e}},{test:[/silk/i],describe:function(t){var e={name:"Amazon Silk"},o=i.default.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/phantom/i],describe:function(t){var e={name:"PhantomJS"},o=i.default.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/slimerjs/i],describe:function(t){var e={name:"SlimerJS"},o=i.default.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e={name:"BlackBerry"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e={name:"WebOS Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/bada/i],describe:function(t){var e={name:"Bada"},o=i.default.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/tizen/i],describe:function(t){var e={name:"Tizen"},o=i.default.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/qupzilla/i],describe:function(t){var e={name:"QupZilla"},o=i.default.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/firefox|iceweasel|fxios/i],describe:function(t){var e={name:"Firefox"},o=i.default.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/electron/i],describe:function(t){var e={name:"Electron"},o=i.default.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MiuiBrowser/i],describe:function(t){var e={name:"Miui"},o=i.default.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/chromium/i],describe:function(t){var e={name:"Chromium"},o=i.default.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/chrome|crios|crmo/i],describe:function(t){var e={name:"Chrome"},o=i.default.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/GSA/i],describe:function(t){var e={name:"Google Search"},o=i.default.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e={name:"Android Browser"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/playstation 4/i],describe:function(t){var e={name:"PlayStation 4"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/safari|applewebkit/i],describe:function(t){var e={name:"Safari"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/.*/i],describe:function(t){var e=-1!==t.search("\\(")?/^(.*)\/(.*)[ \t]\((.*)/:/^(.*)\/(.*) /;return{name:i.default.getFirstMatch(e,t),version:i.default.getSecondMatch(e,t)}}}];e.default=r,t.exports=e.default},93:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:[/Roku\/DVP/],describe:function(t){var e=i.default.getFirstMatch(/Roku\/DVP-(\d+\.\d+)/i,t);return{name:a.OS_MAP.Roku,version:e}}},{test:[/windows phone/i],describe:function(t){var e=i.default.getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.WindowsPhone,version:e}}},{test:[/windows /i],describe:function(t){var e=i.default.getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i,t),o=i.default.getWindowsVersionName(e);return{name:a.OS_MAP.Windows,version:e,versionName:o}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(t){var e={name:a.OS_MAP.iOS},o=i.default.getSecondMatch(/(Version\/)(\d[\d.]+)/,t);return o&&(e.version=o),e}},{test:[/macintosh/i],describe:function(t){var e=i.default.getFirstMatch(/mac os x (\d+(\.?_?\d+)+)/i,t).replace(/[_\s]/g,"."),o=i.default.getMacOSVersionName(e),n={name:a.OS_MAP.MacOS,version:e};return o&&(n.versionName=o),n}},{test:[/(ipod|iphone|ipad)/i],describe:function(t){var e=i.default.getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i,t).replace(/[_\s]/g,".");return{name:a.OS_MAP.iOS,version:e}}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e=i.default.getFirstMatch(/android[\s/-](\d+(\.\d+)*)/i,t),o=i.default.getAndroidVersionName(e),n={name:a.OS_MAP.Android,version:e};return o&&(n.versionName=o),n}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e=i.default.getFirstMatch(/(?:web|hpw)[o0]s\/(\d+(\.\d+)*)/i,t),o={name:a.OS_MAP.WebOS};return e&&e.length&&(o.version=e),o}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e=i.default.getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i,t)||i.default.getFirstMatch(/blackberry\d+\/(\d+([_\s]\d+)*)/i,t)||i.default.getFirstMatch(/\bbb(\d+)/i,t);return{name:a.OS_MAP.BlackBerry,version:e}}},{test:[/bada/i],describe:function(t){var e=i.default.getFirstMatch(/bada\/(\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.Bada,version:e}}},{test:[/tizen/i],describe:function(t){var e=i.default.getFirstMatch(/tizen[/\s](\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.Tizen,version:e}}},{test:[/linux/i],describe:function(){return{name:a.OS_MAP.Linux}}},{test:[/CrOS/],describe:function(){return{name:a.OS_MAP.ChromeOS}}},{test:[/PlayStation 4/],describe:function(t){var e=i.default.getFirstMatch(/PlayStation 4[/\s](\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.PlayStation4,version:e}}}];e.default=r,t.exports=e.default},94:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:[/googlebot/i],describe:function(){return{type:"bot",vendor:"Google"}}},{test:[/huawei/i],describe:function(t){var e=i.default.getFirstMatch(/(can-l01)/i,t)&&"Nova",o={type:a.PLATFORMS_MAP.mobile,vendor:"Huawei"};return e&&(o.model=e),o}},{test:[/nexus\s*(?:7|8|9|10).*/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Nexus"}}},{test:[/ipad/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/kftt build/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Amazon",model:"Kindle Fire HD 7"}}},{test:[/silk/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Amazon"}}},{test:[/tablet(?! pc)/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet}}},{test:function(t){var e=t.test(/ipod|iphone/i),o=t.test(/like (ipod|iphone)/i);return e&&!o},describe:function(t){var e=i.default.getFirstMatch(/(ipod|iphone)/i,t);return{type:a.PLATFORMS_MAP.mobile,vendor:"Apple",model:e}}},{test:[/nexus\s*[0-6].*/i,/galaxy nexus/i],describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"Nexus"}}},{test:[/[^-]mobi/i],describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"blackberry"===t.getBrowserName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"BlackBerry"}}},{test:function(t){return"bada"===t.getBrowserName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"windows phone"===t.getBrowserName()},describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"Microsoft"}}},{test:function(t){var e=Number(String(t.getOSVersion()).split(".")[0]);return"android"===t.getOSName(!0)&&e>=3},describe:function(){return{type:a.PLATFORMS_MAP.tablet}}},{test:function(t){return"android"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"macos"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop,vendor:"Apple"}}},{test:function(t){return"windows"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop}}},{test:function(t){return"linux"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop}}},{test:function(t){return"playstation 4"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.tv}}},{test:function(t){return"roku"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.tv}}}];e.default=r,t.exports=e.default},95:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:function(t){return"microsoft edge"===t.getBrowserName(!0)},describe:function(t){if(/\sedg\//i.test(t))return{name:a.ENGINE_MAP.Blink};var e=i.default.getFirstMatch(/edge\/(\d+(\.?_?\d+)+)/i,t);return{name:a.ENGINE_MAP.EdgeHTML,version:e}}},{test:[/trident/i],describe:function(t){var e={name:a.ENGINE_MAP.Trident},o=i.default.getFirstMatch(/trident\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){return t.test(/presto/i)},describe:function(t){var e={name:a.ENGINE_MAP.Presto},o=i.default.getFirstMatch(/presto\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=t.test(/gecko/i),o=t.test(/like gecko/i);return e&&!o},describe:function(t){var e={name:a.ENGINE_MAP.Gecko},o=i.default.getFirstMatch(/gecko\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(apple)?webkit\/537\.36/i],describe:function(){return{name:a.ENGINE_MAP.Blink}}},{test:[/(apple)?webkit/i],describe:function(t){var e={name:a.ENGINE_MAP.WebKit},o=i.default.getFirstMatch(/webkit\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}}];e.default=r,t.exports=e.default}})},"./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss":(t,e,o)=>{"use strict";o.d(e,{A:()=>s});var n=o("./node_modules/css-loader/dist/runtime/noSourceMaps.js"),i=o.n(n),a=o("./node_modules/css-loader/dist/runtime/api.js"),r=o.n(a)()(i());r.push([t.id,'.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button:disabled{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button:disabled::before,.vot-button:disabled::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button:disabled::before,.vot-outlined-button:disabled::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button:disabled::before,.vot-text-button:disabled::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button:disabled::before,.vot-icon-button:disabled::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input,.vot-textfield:hover>textarea{border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input+span::before,.vot-textfield:hover>input+span::after,.vot-textfield:hover>textarea+span::before,.vot-textfield:hover>textarea+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:10000}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-translating=true] #vot-translating-icon{display:block !important}.vot-segmented-button[data-translating=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:10000;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog-container{visibility:visible;position:absolute;z-index:10000}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:10000;left:25%;top:75%;pointer-events:none}.vot-subtitles{position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:1rem;pointer-events:all;padding:1rem;font-size:2rem;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8);--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block}',""]);const s=r},"./node_modules/css-loader/dist/runtime/api.js":t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var o="",n=void 0!==e[5];return e[4]&&(o+="@supports (".concat(e[4],") {")),e[2]&&(o+="@media ".concat(e[2]," {")),n&&(o+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),o+=t(e),n&&(o+="}"),e[2]&&(o+="}"),e[4]&&(o+="}"),o})).join("")},e.i=function(t,o,n,i,a){"string"==typeof t&&(t=[[null,t,void 0]]);var r={};if(n)for(var s=0;s0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=a),o&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=o):c[2]=o),i&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=i):c[4]="".concat(i)),e.push(c))}},e}},"./node_modules/css-loader/dist/runtime/noSourceMaps.js":t=>{"use strict";t.exports=function(t){return t[1]}},"./node_modules/requestidlecallback-polyfill/index.js":()=>{window.requestIdleCallback=window.requestIdleCallback||function(t){var e=Date.now();return setTimeout((function(){t({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-e))}})}),1)},window.cancelIdleCallback=window.cancelIdleCallback||function(t){clearTimeout(t)}},"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":t=>{"use strict";var e=[];function o(t){for(var o=-1,n=0;n{"use strict";var e={};t.exports=function(t,o){var n=function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(t){o=null}e[t]=o}return e[t]}(t);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(o)}},"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":(t,e,o)=>{"use strict";t.exports=function(t){var e=o.nc;e&&t.setAttribute("nonce",e)}},"./node_modules/style-loader/dist/runtime/styleDomAPI.js":t=>{"use strict";t.exports=function(t){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var e=t.insertStyleElement(t);return{update:function(o){!function(t,e,o){var n="";o.supports&&(n+="@supports (".concat(o.supports,") {")),o.media&&(n+="@media ".concat(o.media," {"));var i=void 0!==o.layer;i&&(n+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),n+=o.css,i&&(n+="}"),o.media&&(n+="}"),o.supports&&(n+="}");var a=o.sourceMap;a&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),e.styleTagTransform(n,t,e.options)}(e,t,o)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},"./node_modules/style-loader/dist/runtime/styleTagTransform.js":t=>{"use strict";t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}},"./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js":t=>{t.exports=function(){return function(t){return t.styleTagTransform=function(t,e){e?.remove(),GM_addStyle(t)},document.createElement("style")}.apply(null,arguments)}},"./src/config/config.js":(t,e,o)=>{"use strict";o.d(e,{Cc:()=>r,JD:()=>s,K2:()=>c,Pm:()=>i,QL:()=>u,S7:()=>a,T8:()=>l,mE:()=>d,rw:()=>h,se:()=>n});const n="m3u8-proxy.toil.cc",i="vot-worker.toil.cc",a="bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf",r="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 YaBrowser/24.4.0.0 Safari/537.36",s=.15,l=900,d="yandex",c="yandex",u={yandex:"https://translate.toil.cc/detect",rustServer:"https://rust-server-531j.onrender.com/detect"},h={yandex:"https://translate.toil.cc/translate",deepl:"https://translate-deepl.toil.cc/translate"}},"./src/config/constants.js":(t,e,o)=>{"use strict";o.d(e,{Ww:()=>i,xm:()=>n});const n=["ru","en","zh","ko","lt","lv","ar","fr","it","es","de","ja"],i=["ru","en","kk"]},"./src/localization/localizationProvider.js":(t,e,o)=>{"use strict";o.d(e,{z:()=>s,j:()=>l});const n=JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","neededAdditionalExtension":"An additional extension is needed to support this site","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyAccessTokenExpired":"Your entered Udemy Access Token has expired","udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","enterUdemyAccessToken":"Enter Udemy Access Token","VOTUdemyData":"Udemy Data","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase"}');var i=o("./src/utils/debug.js"),a=o("./src/utils/storage.js"),r=o("./src/utils/utils.js");const s=["auto","en","ru","af","am","ar","az","bg","bn","bs","ca","cs","cy","da","de","el","es","et","eu","fa","fi","fr","gl","hi","hr","hu","hy","id","it","ja","jv","kk","km","kn","ko","lo","mk","ml","mn","ms","mt","my","ne","nl","pa","pl","pt","ro","si","sk","sl","sq","sr","su","sv","sw","tr","uk","ur","uz","vi","zh","zu"],l=new class{lang="en";locale={};gmValues=["locale-phrases","locale-lang","locale-version","locale-lang-override"];constructor(){const t=a.d.syncGet("locale-lang-override","auto");this.lang=t&&"auto"!==t?t:(navigator.language||navigator.userLanguage)?.substr(0,2)?.toLowerCase()??"en",this.setLocaleFromJsonString(a.d.syncGet("locale-phrases",""))}reset(){for(let t=0;t{if("object"==typeof t&&t)return t[e]}),t);return void 0===o&&console.warn("[VOT] [localizationProvider] locale",t,"doesn't contain key",e),o}getDefault(t){return this.getFromLocale(n,t)??t}get(t){return this.getFromLocale(this.locale,t)??this.getFromLocale(n,t)??t}}},"./src/utils/debug.js":(t,e,o)=>{"use strict";o.d(e,{A:()=>i});const n={log:(...t)=>{}},i=n},"./src/utils/storage.js":(t,e,o)=>{"use strict";o.d(e,{d:()=>i});var n=o("./src/utils/debug.js");const i=new class{constructor(){this.gmSupport="function"==typeof GM_getValue,n.A.log(`GM Storage Status: ${this.gmSupport}`)}syncGet(t,e=void 0,o=!1){if(this.gmSupport)return GM_getValue(t,e);let n=window.localStorage.getItem(t);if("udemyData"===t&&"string"==typeof n)try{n=JSON.parse(n)}catch{n=e}return o?Number(n)??Number(e):n??e}async get(t,e=void 0,o=!1){return this.gmSupport?await GM_getValue(t,e):Promise.resolve(this.syncGet(t,e,o))}syncSet(t,e){return this.gmSupport?GM_setValue(t,e):("udemyData"===t&&(e=JSON.stringify(e)),window.localStorage.setItem(t,e))}async set(t,e){return this.gmSupport?await GM_setValue(t,e):Promise.resolve(this.syncSet(t,e))}syncDelete(t){return this.gmSupport?GM_deleteValue(t):window.localStorage.removeItem(t)}async delete(t){return this.gmSupport?await GM_deleteValue(t):Promise.resolve(this.syncDelete(t))}syncList(){return this.gmSupport?GM_listValues():["autoTranslate","dontTranslateLanguage","dontTranslateYourLang","autoSetVolumeYandexStyle","showVideoSlider","syncVolume","subtitlesMaxLength","highlightWords","responseLanguage","defaultVolume","udemyData","audioProxy","showPiPButton","locale-version","locale-lang","locale-phrases"]}async list(){return this.gmSupport?await GM_listValues():Promise.resolve(this.syncList())}}},"./src/utils/translateApis.js":(t,e,o)=>{"use strict";o.d(e,{Tl:()=>d,o0:()=>c,qh:()=>h,vN:()=>u});var n=o("./src/config/config.js"),i=o("./src/utils/storage.js");async function a(t,e={}){const o=new AbortController,n=setTimeout((()=>o.abort()),3e3);try{return await fetch(t,{...e,signal:o.signal})}catch(t){return console.error("Fetch timed-out. Error:",t),t}finally{clearTimeout(n)}}const r={async translate(t,e){try{const o=await a(`${n.rw.yandex}?${new URLSearchParams({text:t,lang:e})}`);if(o instanceof Error)throw o;const i=await o.json();if(200!==i.code)throw i.message;return i.text[0]}catch(e){return console.error("Error translating text:",e),t}},async detect(t){try{const e=await a(`${n.QL.yandex}?${new URLSearchParams({text:t})}`);if(e instanceof Error)throw e;const o=await e.json();if(200!==o.code)throw o.message;return o.lang??"en"}catch(t){return console.error("Error getting lang from text:",t),"en"}}},s={async detect(t){try{const e=await fetch(n.QL.rustServer,{method:"POST",body:t});if(e instanceof Error)throw e;return await e.text()}catch(t){return console.error("Error getting lang from text:",t),"en"}}},l={async translate(t,e="auto",o="ru"){try{const i=await a(n.rw.deepl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({text:t,source_lang:e,target_lang:o})});if(i instanceof Error)throw i;const r=await i.json();if(200!==r.code)throw r.message;return r.data}catch(e){return console.error("Error translating text:",e),t}}};async function d(t,e="",o="ru"){switch(await i.d.get("translationService",n.mE)){case"yandex":{const n=e&&o?`${e}-${o}`:o;return await r.translate(t,n)}case"deepl":return await l.translate(t,e,o);default:return t}}async function c(t){switch(await i.d.get("detectService",n.K2)){case"yandex":return await r.detect(t);case"rust-server":return await s.detect(t);default:return"en"}}const u=Object.keys(n.rw),h=Object.keys(n.QL).map((t=>"rustServer"===t?"rust-server":t))},"./src/utils/utils.js":(t,e,o)=>{"use strict";o.d(e,{Bs:()=>c,CK:()=>u,G3:()=>g,R4:()=>d,X5:()=>p,jI:()=>s,ox:()=>l,vV:()=>r});var n=o("./src/localization/localizationProvider.js"),i=o("./src/utils/youtubeUtils.js");const a=navigator.language||navigator.userLanguage,r=a?.substr(0,2)?.toLowerCase()??"en",s=(t,e)=>{let o=new URL(window.location.href);switch(t){case"piped":case"invidious":case"youtube":if(o.searchParams.has("enablejsapi")){const t=i.A.getPlayer().getVideoUrl();o=new URL(t)}return/(?:watch|embed|shorts|live)\/([^/]+)/.exec(o.pathname)?.[1]||o.searchParams.get("v");case"vk":{const t=/^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(o.pathname),e=o.searchParams.get("z"),n=o.searchParams.get("oid"),i=o.searchParams.get("id");return t?t[0].slice(1):e?e.split("/")[0]:n&&i?`video-${Math.abs(parseInt(n))}_${i}`:null}case"nine_gag":case"9gag":case"gag":return/gag\/([^/]+)/.exec(o.pathname)?.[1];case"twitch":{const t=/([^/]+)\/(?:clip)\/([^/]+)/.exec(o.pathname);if(/^m\.twitch\.tv$/.test(o.hostname))return/videos\/([^/]+)/.exec(o.href)?.[0]||o.pathname.slice(1);if(/^player\.twitch\.tv$/.test(o.hostname))return`videos/${o.searchParams.get("video")}`;if(/^clips\.twitch\.tv$/.test(o.hostname)){const t=document.querySelector("script[type='application/ld+json']"),e=o.pathname.slice(1);if(!t){const t="embed"===e,n=document.querySelector(t?".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']":".clips-player a:not([class])");if(!n)return;return`${n.href.replace("https://www.twitch.tv/","")}/clip/${t?o.searchParams.get("clip"):e}`}const n=JSON.parse(t.innerText),i=n["@graph"].find((t=>"VideoObject"===t["@type"]))?.creator.url;return`${i.replace("https://www.twitch.tv/","")}/clip/${e}`}return t?t[0]:/(?:videos)\/([^/]+)/.exec(o.pathname)?.[0]}case"proxitok":return/([^/]+)\/video\/([^/]+)/.exec(o.pathname)?.[0];case"tiktok":{let t=/([^/]+)\/video\/([^/]+)/.exec(o.pathname)?.[0];if(!t){const o=e.closest(".xgplayer-playing, .tiktok-web-player"),n=o?.closest('div[data-e2e="recommend-list-item-container"]'),i=n?.querySelector('a[data-e2e="video-author-avatar"]');if(o&&i){const e=o.id?.match(/^xgwrapper-\d+-(.*)$/)?.at(1),n=i.href?.match(/.*(@.*)$/)?.at(1);e&&n&&(t=`${n}/video/${e}`)}}return t}case"vimeo":{const t=o.searchParams.get("app_id"),e=/[^/]+\/[^/]+$/.exec(o.pathname)?.[0]||/[^/]+$/.exec(o.pathname)?.[0];return t?`${e}?app_id=${t}`:e}case"xvideos":return/[^/]+\/[^/]+$/.exec(o.pathname)?.[0];case"pornhub":return o.searchParams.get("viewkey")||/embed\/([^/]+)/.exec(o.pathname)?.[1];case"twitter":return/status\/([^/]+)/.exec(o.pathname)?.[1];case"udemy":case"rumble":case"facebook":return o.pathname.slice(1);case"rutube":return/(?:video|embed)\/([^/]+)/.exec(o.pathname)?.[1];case"coub":return/(?:view|embed)\/([^/]+)/.exec(o.pathname)?.[1]||document.querySelector(".coub.active")?.dataset?.permalink;case"bilibili":{const t=o.searchParams.get("bvid");if(t)return t;let e=/video\/([^/]+)/.exec(o.pathname)?.[1];return e&&null!==o.searchParams.get("p")&&(e+=`/?p=${o.searchParams.get("p")}`),e}case"mail_ru":{const t=o.pathname;if(t.startsWith("/v/")||t.startsWith("/mail/"))return t.slice(1);const e=/video\/embed\/([^/]+)/.exec(t)?.[1];if(!e)return null;const n=document.querySelector(".b-video-controls__mymail-link");return!!n&&n?.href.split("my.mail.ru")?.[1]}case"bitchute":return e.src?.startsWith("blob:")||!e.src?.includes(".mp4")?null:e.src;case"coursera":return/learn\/([^/]+)\/lecture\/([^/]+)/.exec(o.pathname)?.[0];case"eporner":return/video-([^/]+)\/([^/]+)/.exec(o.pathname)?.[0];case"peertube":return/\/w\/([^/]+)/.exec(o.pathname)?.[0];case"dailymotion":{const t=Array.from(document.querySelectorAll("*")).filter((t=>t.innerHTML.trim().includes(".m3u8")));try{let e=t[1].lastChild.src;return/\/video\/(\w+)\.m3u8/.exec(e)?.[1]}catch(t){return console.error("[VOT]",t),!1}}case"trovo":{const t=o.searchParams.get("vid");if(!t)return null;const e=/([^/]+)\/(\d+)/.exec(o.pathname)?.[0];return e?`${e}?vid=${t}`:null}case"yandexdisk":return/\/i\/([^/]+)/.exec(o.pathname)?.[1];case"coursehunter":{const t=/\/course\/([^/]+)/.exec(o.pathname)?.[1];return!!t&&t+o.search}case"ok.ru":return/\/video\/(\d+)/.exec(o.pathname)?.[1];case"googledrive":return o.searchParams.get("docid");case"bannedvideo":return o.searchParams.get("id");case"weverse":return/([^/]+)\/(live|media)\/([^/]+)/.exec(o.pathname)?.[0];case"newgrounds":return/([^/]+)\/(view)\/([^/]+)/.exec(o.pathname)?.[0];case"egghead":return o.pathname.slice(1);case"youku":return/v_show\/id_[\w=]+/.exec(o.pathname)?.[0];case"archive":return/(details|embed)\/([^/]+)/.exec(o.pathname)?.[2];case"directlink":return o.pathname+o.search;default:return!1}};function l(t){const e=Math.floor(t/60),o=Math.floor(t%60);return e>=60?n.j.get("translationTakeMoreThanHour"):1===e||0===e&&o>0?n.j.get("translationTakeAboutMinute"):11!==e&&e%10==1?n.j.get("translationTakeApproximatelyMinute2").replace("{0}",e):![12,13,14].includes(e)&&[2,3,4].includes(e%10)?n.j.get("translationTakeApproximatelyMinute").replace("{0}",e):n.j.get("translationTakeApproximatelyMinutes").replace("{0}",e)}function d(t){return t.toLowerCase().split(/[_;-]/)[0].trim()}function c(){return"pictureInPictureEnabled"in document&&document.pictureInPictureEnabled}function u(){return"undefined"!=typeof Hls&&Hls?.isSupported()?new Hls({debug:!1,lowLatencyMode:!0,backBufferLength:90}):void 0}const h=new RegExp([/(?:https?|ftp):\/\/\S+/g,/https?:\/\/\S+|www\.\S+/gm,/\b\S+\.\S+/gm,/#[^\s#]+/g,/Auto-generated by YouTube/g,/Provided to YouTube by/g,/Released on/g,/0x[a-fA-F0-9]{40}/g,/[13][a-km-zA-HJ-NP-Z1-9]{25,34}/g,/4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}/g,/Paypal/g].map((t=>t.source)).join("|"));function p(t,e){return`${t} ${e?e.split("\n").filter((t=>!h.test(t))).join(" "):""}`.slice(0,450).replace(/[^\p{L}\s]+|\s+/gu," ").trim()}async function g(t,e={}){try{return await fetch(t,e)}catch(o){return new Promise(((o,n)=>{GM_xmlhttpRequest({method:e.method||"GET",url:t,responseType:"blob",onload:t=>{o(new Response(t.response,{status:t.status,headers:Object.fromEntries(t.responseHeaders.trim().split("\r\n").map((t=>{let e=t.split(": ");if("set-cookie"!==e?.[0])return[e.shift(),e.join(": ")]})).filter((t=>t)))}))},ontimeout:()=>n(new Error("fetch timeout")),onerror:t=>n(t),onabort:()=>n(new Error("fetch abort"))})}))}}},"./src/utils/youtubeUtils.js":(t,e,o)=>{"use strict";o.d(e,{A:()=>u});var n=o("./src/utils/debug.js"),i=o("./src/config/constants.js"),a=o("./src/utils/utils.js"),r=o("./src/utils/translateApis.js");function s(){return/^m\.youtube\.com$/.test(window.location.hostname)}function l(){return window.location.pathname.startsWith("/shorts/")?s()?document.querySelector("#movie_player"):document.querySelector("#shorts-player"):document.querySelector("#movie_player")}function d(){const t=l();return t?.getPlayerResponse?t?.getPlayerResponse?.call()??null:t?.data?.playerResponse??null}function c(){const t=l();return t?.getVideoData?t?.getVideoData?.call()??null:t?.data?.playerResponse?.videoDetails??null}const u={isMobile:s,getPlayer:l,getPlayerResponse:d,getPlayerData:c,getVideoVolume:function(){const t=l();return t?.getVolume?t.getVolume.call()/100:1},getSubtitles:function(){const t=d();let e=t?.captions?.playerCaptionsTracklistRenderer?.captionTracks??[];return e=e.reduce(((t,e)=>{if("languageCode"in e){const o=e?.languageCode?(0,a.R4)(e?.languageCode):void 0,n=e?.url||e?.baseUrl;o&&n&&t.push({source:"youtube",language:o,isAutoGenerated:"asr"===e?.kind,url:`${n.startsWith("http")?n:`${window.location.origin}/${n}`}&fmt=json3`})}return t}),[]),n.A.log("youtube subtitles:",e),e},getVideoData:async function(){const t=l(),e=d(),o=c(),{title:s}=o??{},{shortDescription:u,isLive:h}=e?.videoDetails??{};let p=s?await async function(t,e,o,i){if(!window.location.hostname.includes("m.youtube.com")&&t?.getAudioTrack){const e=t.getAudioTrack(),o=e?.getLanguageInfo();if("und"!==o?.id)return(0,a.R4)(o.id.split(".")[0])}const s=e?.captions?.playerCaptionsTracklistRenderer?.captionTracks;if(s?.length){const t=s.find((t=>"asr"===t.kind));if(t&&t.languageCode)return(0,a.R4)(t.languageCode)}const l=(0,a.X5)(o,i);return n.A.log(`Detecting language text: ${l}`),(0,r.o0)(l)}(t,e,s,u):"en";p=i.xm.includes(p)?p:"en";const g={isLive:!!h,title:s,description:u,detectedLanguage:p};return n.A.log("youtube video data:",g),console.log("[VOT] Detected language: ",g.detectedLanguage),g},setVideoVolume:function(t){const e=l();if(e?.setVolume)return e.setVolume(Math.round(100*t)),!0},videoSeek:function(t,e){n.A.log("videoSeek",e);const o=(l()?.getProgressState()?.seekableEnd||t.currentTime)-e;t.currentTime=o},isMuted:function(){const t=l();return!!t?.isMuted&&t.isMuted.call()},isMusic:function(){const t=c().author,e=c().title.toUpperCase(),o=e.match(/\w+/g),n=document.body.querySelector("ytd-watch-flexy")?.playerData;return[e,document.URL,t,n?.microformat?.playerMicroformatRenderer.category,n?.title].some((t=>t?.toUpperCase().includes("MUSIC")))||document.body.querySelector("#upload-info #channel-name .badge-style-type-verified-artist")||t&&/(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(t)||t&&/(MUSIC|ROCK|SOUNDS|SONGS)/.test(t.toUpperCase())||o?.length&&["🎵","♫","SONG","SONGS","SOUNDTRACK","LYRIC","LYRICS","AMBIENT","MIX","VEVO","CLIP","KARAOKE","OPENING","COVER","COVERED","VOCAL","INSTRUMENTAL","ORCHESTRAL","DUBSTEP","DJ","DNB","BASS","BEAT","ALBUM","PLAYLIST","DUBSTEP","CHILL","RELAX","CLASSIC","CINEMATIC"].some((t=>o.includes(t)))||["OFFICIAL VIDEO","OFFICIAL AUDIO","FEAT.","FT.","LIVE RADIO","DANCE VER","HIP HOP","ROCK N ROLL","HOUR VER","HOURS VER","INTRO THEME"].some((t=>e.includes(t)))||o?.length&&["OP","ED","MV","OST","NCS","BGM","EDM","GMV","AMV","MMD","MAD"].some((t=>o.includes(t)))}}},"./src/yandexRequest-cloudflare.js":(t,e,o)=>{"use strict";o.r(e),o.d(e,{default:()=>s});var n=o("./src/config/config.js"),i=o("./src/utils/debug.js"),a=o("./src/utils/storage.js"),r=o("./src/utils/utils.js");const s=async function(t,e,o,s){let l,d;try{i.A.log("yandexRequest:",t);const s={method:"POST",mode:"cors",cache:"no-cache",headers:{"Content-Type":"application/json"},redirect:"follow",referrerPolicy:"no-referrer",body:JSON.stringify({headers:{Accept:"application/x-protobuf","Accept-Language":"en","Content-Type":"application/x-protobuf","User-Agent":n.Cc,Pragma:"no-cache","Cache-Control":"no-cache","Sec-Fetch-Mode":"no-cors",...o},body:Array.from(e)})},c=await a.d.get("proxyWorkerHost",n.Pm);l=await(0,r.G3)(`https://${c}${t}`,s),i.A.log("yandexRequest:",l.status,l),d=await l.arrayBuffer()}catch(t){console.error("[VOT]",t),l={status:-1},d=t}s(200==l.status,d)}}},e={};function o(n){var i=e[n];if(void 0!==i)return i.exports;var a=e[n]={id:n,exports:{}};return t[n].call(a.exports,a,a.exports,o),a.exports}o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.nc=void 0,(()=>{"use strict";const t=["invidious.snopyta.org","yewtu.be","invidious.kavin.rocks","vid.puffyan.us","invidious.namazso.eu","inv.riverside.rocks","yt.artemislena.eu","invidious.flokinet.to","invidious.esmailelbob.xyz","y.com.sb","invidious.nerdvpn.de","inv.vern.cc","invidious.slipfox.xyz","invidio.xamh.de","invidious.dhusch.de"],e=["piped.video","piped.tokhmi.xyz","piped.moomoo.me","piped.syncpundit.io","piped.mha.fi","watch.whatever.social","piped.garudalinux.org","efy.piped.pages.dev","watch.leptons.xyz","piped.lunar.icu","yt.dc09.ru","piped.mint.lgbt","il.ax","piped.privacy.com.de","piped.esmailelbob.xyz","piped.projectsegfau.lt","piped.in.projectsegfau.lt","piped.us.projectsegfau.lt","piped.privacydev.net","piped.palveluntarjoaja.eu","piped.smnz.de","piped.adminforge.de","piped.qdi.fi","piped.hostux.net","piped.chauvet.pro","piped.jotoma.de","piped.pfcd.me","piped.frontendfriendly.xyz"],n=["proxitok.pabloferreiro.es","proxitok.pussthecat.org","tok.habedieeh.re","proxitok.esmailelbob.xyz","proxitok.privacydev.net","tok.artemislena.eu","tok.adminforge.de","tik.hostux.net","tt.vern.cc","cringe.whatever.social","proxitok.lunar.icu","proxitok.privacy.com.de"],i=["peertube.1312.media","tube.shanti.cafe","bee-tube.fr","video.sadmin.io","dalek.zone","review.peertube.biz","peervideo.club","tube.la-dina.net","peertube.tmp.rcp.tf","peertube.su"];var a=o("./src/config/config.js"),r=o("./src/config/constants.js"),s=o("./src/localization/localizationProvider.js"),l=o("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"),d=o.n(l),c=o("./node_modules/style-loader/dist/runtime/styleDomAPI.js"),u=o.n(c),h=o("./node_modules/style-loader/dist/runtime/insertBySelector.js"),p=o.n(h),g=o("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js"),v=o.n(g),m=o("./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js"),b=o.n(m),f=o("./node_modules/style-loader/dist/runtime/styleTagTransform.js"),y=o.n(f),w=o("./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss"),x={};x.styleTagTransform=y(),x.setAttributes=v(),x.insert=p().bind(null,"head"),x.domAPI=u(),x.insertStyleElement=b();d()(w.A,x);w.A&&w.A.locals&&w.A.locals;function S(t){const e=document.createElement("vot-block");return e.classList.add("vot-icon-button"),e.innerHTML=t,e}function k(t){const e=parseFloat(t.value),o=""===t.min?0:parseFloat(t.min),n=(e-o)/((""===t.max?100:parseFloat(t.max))-o);t.parentElement.setAttribute("style",`--vot-progress: ${n}`)}function T(t,e="",o=" ",n=!1){const i=document.createElement("vot-block");i.classList.add("vot-textfield");const a=document.createElement(n?"textarea":"input");a.placeholder=o,a.value=e;const r=document.createElement("span");return r.innerHTML=t,i.appendChild(a),i.appendChild(r),{container:i,input:a,label:r}}function V(t){const e=document.createElement("vot-block");e.classList.add("vot-dialog-container"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-dialog-backdrop");const n=document.createElement("vot-block");n.classList.add("vot-dialog");const i=document.createElement("vot-block");i.classList.add("vot-dialog-content-wrapper");const a=document.createElement("vot-block");a.classList.add("vot-dialog-header-container");const r=document.createElement("vot-block");r.classList.add("vot-dialog-body-container");const s=document.createElement("vot-block");s.classList.add("vot-dialog-footer-container");const l=document.createElement("vot-block");l.classList.add("vot-dialog-title-container");const d=S('');d.classList.add("vot-dialog-close-button"),o.onclick=d.onclick=()=>{e.hidden=!0};const c=document.createElement("vot-block");return c.classList.add("vot-dialog-title"),c.innerHTML=t,e.appendChild(o),e.appendChild(n),n.appendChild(i),i.appendChild(a),i.appendChild(r),i.appendChild(s),a.appendChild(l),a.appendChild(d),l.appendChild(c),{container:e,backdrop:o,dialog:n,contentWrapper:i,headerContainer:a,bodyContainer:r,footerContainer:s,titleContainer:l,closeButton:d,title:c}}function M(t,e,o,n={}){const i=n.onSelectCb||function(){},a=n.labelElement||"";let r=[];const l=document.createElement("vot-block");l.classList.add("vot-select"),a&&l.appendChild(a);const d=document.createElement("vot-block");d.classList.add("vot-select-outer");const c=document.createElement("span");c.classList.add("vot-select-title"),c.innerText=t,void 0===t&&(c.innerText=o.find((t=>!0===t.selected))?.label);const u=document.createElement("vot-block");u.classList.add("vot-select-arrow-icon"),u.innerHTML='',d.append(c,u),d.onclick=()=>{const t=V(e);t.container.classList.add("vot-dialog-temp"),t.container.hidden=!1,document.documentElement.appendChild(t.container);const n=document.createElement("vot-block");n.classList.add("vot-select-content-list");for(const t of o){const e=document.createElement("vot-block");e.classList.add("vot-select-content-item"),e.innerText=t.label,e.dataset.votSelected=t.selected,e.dataset.votValue=t.value,t.disabled&&(e.inert=!0),e.onclick=async a=>{if(a.target.inert)return;const r=n.childNodes;for(let t of r)t.dataset.votSelected=!1;for(let e of o)e.selected=e.value===t.value;e.dataset.votSelected=!0,c.innerText=t.label,await i(a)},n.appendChild(e)}const a=T(s.j.get("searchField"));a.input.oninput=t=>{const e=t.target.value.toLowerCase();for(let t=0;t{t.container.remove(),r=[]}},l.append(d);return{container:l,title:c,arrowIcon:u,labelElement:a,setTitle:t=>{c.innerText=t},setSelected:t=>{const e=Array.from(r).filter((t=>!t.inert));for(let o=0;o{o=t}}}const L={createHeader:function(t,e=4){const o=document.createElement("vot-block");return o.classList.add("vot-header"),o.classList.add(`vot-header-level-${e}`),o.innerHTML=t,o},createInformation:function(t,e){const o=document.createElement("vot-block");o.classList.add("vot-info");const n=document.createElement("vot-block");n.innerHTML=t;const i=document.createElement("vot-block");return i.innerHTML=e,o.appendChild(n),o.appendChild(i),{container:o,header:n,value:i}},createButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-button"),e.innerHTML=t,e},createTextButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-text-button"),e.innerHTML=t,e},createOutlinedButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-outlined-button"),e.innerHTML=t,e},createIconButton:S,createCheckbox:function(t,e=!1){const o=document.createElement("label");o.classList.add("vot-checkbox");const n=document.createElement("input");n.type="checkbox",n.checked=Boolean(e);const i=document.createElement("span");return i.innerHTML=t,o.appendChild(n),o.appendChild(i),{container:o,input:n,label:i}},createSlider:function(t,e=50,o=0,n=100){const i=document.createElement("vot-block");i.classList.add("vot-slider");const a=document.createElement("input");a.type="range",a.min=o,a.max=n,a.value=e;const r=document.createElement("span");return r.innerHTML=t,i.appendChild(a),i.appendChild(r),a.addEventListener("input",(t=>k(t.target))),k(a),{container:i,input:a,label:r}},createTextfield:T,createDialog:V,createVOTButton:function(t){const e=document.createElement("vot-block");e.classList.add("vot-segmented-button");const o=document.createElement("vot-block");o.classList.add("vot-segment"),o.classList.add("vot-translate-button"),o.innerHTML='';const n=document.createElement("vot-block");n.classList.add("vot-separator");const i=document.createElement("vot-block");i.classList.add("vot-segment-only-icon"),i.innerHTML='';const a=document.createElement("vot-block");a.classList.add("vot-separator");const r=document.createElement("vot-block");r.classList.add("vot-segment-only-icon"),r.innerHTML='';const s=document.createElement("span");return s.classList.add("vot-segment-label"),s.innerHTML=t,e.appendChild(o),e.appendChild(n),e.appendChild(i),e.appendChild(a),e.appendChild(r),o.appendChild(s),{container:e,translateButton:o,separator:n,pipButton:i,separator2:a,menuButton:r,label:s}},createVOTMenu:function(t){const e=document.createElement("vot-block");e.classList.add("vot-menu"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-menu-content-wrapper");const n=document.createElement("vot-block");n.classList.add("vot-menu-header-container");const i=document.createElement("vot-block");i.classList.add("vot-menu-body-container");const a=document.createElement("vot-block");a.classList.add("vot-menu-footer-container");const r=document.createElement("vot-block");r.classList.add("vot-menu-title-container");const s=document.createElement("vot-block");return s.classList.add("vot-menu-title"),s.innerHTML=t,e.appendChild(o),o.appendChild(n),o.appendChild(i),o.appendChild(a),n.appendChild(r),r.appendChild(s),{container:e,contentWrapper:o,headerContainer:n,bodyContainer:i,footerContainer:a,titleContainer:r,title:s}},createVOTSelectLabel:function(t){const e=document.createElement("span");return e.classList.add("vot-select-label"),e.innerText=t,e},createVOTSelect:M,createVOTLanguageSelect:function(t){const e=t.fromTitle||"#UNDEFINED",o=t.fromDialogTitle||"#UNDEFINED",n=t.fromItems||[],i=t.fromOnSelectCB||function(){},a=t.toTitle||"#UNDEFINED",r=t.toDialogTitle||"#UNDEFINED",s=t.toItems||[],l=t.toOnSelectCB||function(){},d=document.createElement("vot-block");d.classList.add("vot-lang-select");const c=M(e,o,n,{onSelectCb:i}),u=document.createElement("vot-block");u.classList.add("vot-lang-select-icon"),u.innerHTML='';const h=M(a,r,s,{onSelectCb:l});return d.append(c.container,u,h.container),{container:d,fromSelect:c,icon:u,toSelect:h}},updateSlider:k};class A extends Error{constructor(t){super(s.j.getDefault(t)),this.name="VOTLocalizedError",this.unlocalizedMessage=t,this.localizedMessage=s.j.get(t)}}var C=o("./src/utils/debug.js"),P=o("./src/utils/utils.js");const O=new protobuf.Type("VideoTranslationHelpObject").add(new protobuf.Field("target",1,"string")).add(new protobuf.Field("targetUrl",2,"string")),E=new protobuf.Type("VideoTranslationRequest").add(new protobuf.Field("url",3,"string")).add(new protobuf.Field("deviceId",4,"string")).add(new protobuf.Field("firstRequest",5,"bool")).add(new protobuf.Field("duration",6,"double")).add(new protobuf.Field("unknown2",7,"int32")).add(new protobuf.Field("language",8,"string")).add(new protobuf.Field("forceSourceLang",9,"bool")).add(new protobuf.Field("unknown4",10,"int32")).add(new protobuf.Field("translationHelp",11,"VideoTranslationHelpObject","repeated")).add(new protobuf.Field("responseLanguage",14,"string")).add(new protobuf.Field("unknown5",15,"int32")).add(new protobuf.Field("unknown6",16,"int32")).add(new protobuf.Field("bypassCache",17,"bool")),B=new protobuf.Type("VideoSubtitlesRequest").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("language",2,"string")),R=new protobuf.Type("VideoStreamRequest").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("language",2,"string")).add(new protobuf.Field("responseLanguage",3,"string")),F=new protobuf.Type("VideoStreamPingRequest").add(new protobuf.Field("pingId",1,"int32")),j=new protobuf.Type("VideoTranslationResponse").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("duration",2,"double")).add(new protobuf.Field("status",4,"int32")).add(new protobuf.Field("remainingTime",5,"int32")).add(new protobuf.Field("unknown0",6,"int32")).add(new protobuf.Field("translationId",7,"string")).add(new protobuf.Field("language",8,"string")).add(new protobuf.Field("message",9,"string")),D=new protobuf.Type("VideoSubtitlesObject").add(new protobuf.Field("language",1,"string")).add(new protobuf.Field("url",2,"string")).add(new protobuf.Field("unknown2",3,"int32")).add(new protobuf.Field("translatedLanguage",4,"string")).add(new protobuf.Field("translatedUrl",5,"string")).add(new protobuf.Field("unknown5",6,"int32")).add(new protobuf.Field("unknown6",7,"int32")),_=new protobuf.Type("VideoSubtitlesResponse").add(new protobuf.Field("waiting",1,"int32")).add(new protobuf.Field("subtitles",2,"VideoSubtitlesObject","repeated")),q=new protobuf.Type("VideoStreamObject").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("timestamp",2,"int64")),z=new protobuf.Type("VideoStreamResponse").add(new protobuf.Field("interval",1,"int32")).add(new protobuf.Field("translatedInfo",2,"VideoStreamObject")).add(new protobuf.Field("pingId",3,"int32")),I=new protobuf.Type("YandexSessionRequest").add(new protobuf.Field("uuid",1,"string")).add(new protobuf.Field("module",2,"string")),N=new protobuf.Type("YandexSessionResponse").add(new protobuf.Field("sign",1,"string")).add(new protobuf.Field("expires",2,"int32")),$=(new protobuf.Root).define("yandex").add(O).add(E).add(j).add(B).add(D).add(_).add(F).add(R).add(q).add(z).add(I).add(N),H={encodeTranslationRequest:(t,e,o,n,i)=>$.VideoTranslationRequest.encode({url:t,firstRequest:!0,duration:e,unknown2:1,language:o,forceSourceLang:!1,unknown4:0,translationHelp:i,responseLanguage:n,unknown5:0,unknown6:1,bypassCache:!1}).finish(),decodeTranslationResponse:t=>$.VideoTranslationResponse.decode(new Uint8Array(t)),encodeSubtitlesRequest:(t,e)=>$.VideoSubtitlesRequest.encode({url:t,language:e}).finish(),decodeSubtitlesResponse:t=>$.VideoSubtitlesResponse.decode(new Uint8Array(t)),encodeStreamPingRequest:t=>$.VideoStreamPingRequest.encode({pingId:t}).finish(),encodeStreamRequest:(t,e,o)=>$.VideoStreamRequest.encode({url:t,language:e,responseLanguage:o}).finish(),decodeStreamResponse:t=>$.VideoStreamResponse.decode(new Uint8Array(t)),encodeYandexSessionRequest:(t,e)=>$.YandexSessionRequest.encode({uuid:t,module:e}).finish(),decodeYandexSessionResponse:t=>$.YandexSessionResponse.decode(new Uint8Array(t))};var U=o("./node_modules/bowser/es5.js");function W(){let t="";for(let e=0;e<32;e++){t+="0123456789ABCDEF"[Math.floor(16*Math.random())]}return t}const G=window.crypto.subtle.importKey("raw",(new TextEncoder).encode(a.S7),{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign","verify"]);async function Y(t){const e=await G;return new Uint8Array(await window.crypto.subtle.sign("HMAC",e,t)).reduce(((t,e)=>t+e.toString(16).padStart(2,"0")),"")}const Z=async function(t,e){try{C.A.log("requestStreamPing");const n=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest-cloudflare.js"))).default;C.A.log("Inited yandexRequest...");const i=H.encodeStreamPingRequest(t);await n("/stream-translation/ping-stream",i,{"Vtrans-Signature":await Y(i),"Sec-Vtrans-Token":W()},e)}catch(t){console.error("[VOT]",t),e(!1)}};const K=async function(t,e,n,i){try{C.A.log("requestStreamTranslation");const a=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest-cloudflare.js"))).default;C.A.log("Inited yandexRequest...");const r=H.encodeStreamRequest(t,e,n);await a("/stream-translation/translate-stream",r,{"Vtrans-Signature":await Y(r),"Sec-Vtrans-Token":W()},i)}catch(t){console.error("[VOT]",t),i(!1)}};const J=async function(t,e,n,i,a,r){try{C.A.log("requestVideoTranslation");const s=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest-cloudflare.js"))).default;C.A.log("Inited yandexRequest...");const l=H.encodeTranslationRequest(t,e,n,i,a);await s("/video-translation/translate",l,{"Vtrans-Signature":await Y(l),"Sec-Vtrans-Token":W()},r)}catch(t){console.error("[VOT]",t),r(!1)}};var Q=o("./src/utils/youtubeUtils.js");const X=async function(t,e,n){try{C.A.log("requestVideoSubtitles");const i=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest-cloudflare.js"))).default;C.A.log("Inited yandexRequest...");const a=H.encodeSubtitlesRequest(t,e);await i("/video-subtitles/get-subtitles",a,{"Vsubs-Signature":await Y(a),"Sec-Vsubs-Token":W()},n)}catch(t){console.error("[VOT]",t),n(!1)}};function tt(t){const e=t.startMs+t.durationMs;return t.tokens.reduce(((o,n,i)=>{const a=t.tokens[i+1];let r;o.length>0&&(r=o[o.length-1]);const s=r?.alignRange?.end??0,l=s+n.text.length;if(n.alignRange={start:s,end:l},o.push(n),a){const t=n.startMs+n.durationMs,i=a.startMs?a.startMs-t:e-t;o.push({text:" ",startMs:t,durationMs:i,alignRange:{start:l,end:l+1}})}return o}),[])}function et(t,e){const o=t.text.split(/([\n \t])/).reduce(((t,o)=>{if(o.length){const n=t[t.length-1]??e,i=n?.alignRange?.end??0,a=i+o.length;t.push({text:o,alignRange:{start:i,end:a}})}return t}),[]),n=Math.floor(t.durationMs/o.length),i=t.startMs+t.durationMs;return o.map(((e,a)=>{const r=a===o.length-1,s=t.startMs+n*a;return{...e,startMs:s,durationMs:r?i-s:n}}))}function ot(t){let e=Math.floor(t/3600),o=Math.floor(t%3600/60),n=Math.floor(t%60),i=Math.floor(t%1*1e3);return`${e.toString().padStart(2,"0")}:${o.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")},${i.toString().padStart(3,"0")}`}async function nt(t){const e=new Promise((t=>setTimeout((()=>t({containsTokens:!1,subtitles:[]})),5e3))),o=(async()=>{try{const e=await(0,P.G3)(t.url);return await e.json()}catch(t){return console.error("[VOT] Failed to fetch subtitles. Reason:",t),{containsTokens:!1,subtitles:[]}}})();let n=await Promise.race([e,o]);return"youtube"===t.source&&(n=function(t){const e={containsTokens:!1,subtitles:[]};if("object"!=typeof t||!("events"in t)||!Array.isArray(t.events))return console.error("[VOT] Failed to format youtube subtitles",t),e;for(let o=0;ot.utf8.replace(/^( +| +)$/g,""))).join(" ");let i=t.events[o].dDurationMs;t.events[o+1]&&t.events[o].tStartMs+t.events[o].dDurationMs>t.events[o+1].tStartMs&&(i=t.events[o+1].tStartMs-t.events[o].tStartMs),"\n"!==n&&e.subtitles.push({text:n,startMs:t.events[o].tStartMs,durationMs:i})}return e}(n)),n.subtitles=function(t,e){const o=[];let n;for(let i=0;i{setTimeout((()=>{i||(console.error("[VOT] Failed get yandex subtitles. Reason: timeout"),t([]))}),5e3)})),new Promise((n=>{X(`${t.url}${e}`,o,((t,o)=>{C.A.log("[exec callback] Requesting video subtitles",e),t||(console.error("[VOT] Failed get yandex subtitles"),i=!0,n([]));const a=H.decodeSubtitlesResponse(o);console.log("[VOT] Subtitles response: ",a);let r=a.subtitles??[];r=r.reduce(((t,e)=>(e.language&&!t.find((t=>{if("yandex"===t.source&&t.language===e.language&&!t.translatedFromLanguage)return t}))&&t.push({source:"yandex",language:e.language,url:e.url}),e.translatedLanguage&&t.push({source:"yandex",language:e.translatedLanguage,translatedFromLanguage:e.language,url:e.translatedUrl}),t)),[]),i=!0,n(r)}))}))]),r=[...a,...n].sort(((t,e)=>{if(t.source!==e.source)return"yandex"===t.source?-1:1;if(t.language!==e.language&&(t.language===P.vV||e.language===P.vV))return t.language===P.vV?-1:1;if("yandex"===t.source){if(t.translatedFromLanguage!==e.translatedFromLanguage)return t.translatedFromLanguage&&e.translatedFromLanguage?t.translatedFromLanguage===o?-1:1:t.language===e.language?t.translatedFromLanguage?1:-1:t.translatedFromLanguage?-1:1;if(!t.translatedFromLanguage)return t.language===o?-1:1}return"youtube"===t.source&&t.isAutoGenerated!==e.isAutoGenerated?t.isAutoGenerated?1:-1:0}));return console.log("[VOT] subtitles list",r),r}class at{dragging=!1;subtitlesContainerRect=null;containerRect=null;offsetX=null;offsetY=null;lastContent=null;highlightWords=!1;subtitles=null;maxLength=300;maxLengthRegexp=/.{1,300}(?:\s|$)/g;constructor(t,e,o){this.site=o,this.video=t,"youtube"===this.site.host&&"mobile"!==this.site.additionalData?this.container=e.parentElement:this.container=e,this.votSubtitlesContainer=document.createElement("vot-block"),this.votSubtitlesContainer.classList.add("vot-subtitles-widget"),this.container.appendChild(this.votSubtitlesContainer),this.onMouseDownBound=this.onMouseDown.bind(this),this.onMouseUpBound=this.onMouseUp.bind(this),this.onMouseMoveBound=this.onMouseMove.bind(this),this.onTimeUpdateBound=this.onTimeUpdate.bind(this),document.addEventListener("mousedown",this.onMouseDownBound),document.addEventListener("mouseup",this.onMouseUpBound),document.addEventListener("mousemove",this.onMouseMoveBound),this.video?.addEventListener("timeupdate",this.onTimeUpdateBound)}release(){this.video?.removeEventListener("timeupdate",this.onTimeUpdateBound),document.removeEventListener("mousedown",this.onMouseDownBound),document.removeEventListener("mouseup",this.onMouseUpBound),document.removeEventListener("mousemove",this.onMouseMoveBound),this.votSubtitlesContainer.remove()}onMouseDown(t){this.votSubtitlesContainer.contains(t.target)&&(this.subtitlesContainerRect=this.votSubtitlesContainer.getBoundingClientRect(),this.containerRect=this.container.getBoundingClientRect(),this.offsetX=t.clientX-this.subtitlesContainerRect.x,this.offsetY=t.clientY-this.subtitlesContainerRect.y,this.dragging=!0)}onMouseUp(){this.dragging=!1}onMouseMove(t){if(this.dragging){t.preventDefault();const e=t.clientX-this.offsetX,o=t.clientY-this.offsetY,n=o>=this.containerRect.top,i=o+this.subtitlesContainerRect.height<=this.containerRect.bottom,a=e>=this.containerRect.left,r=e+this.subtitlesContainerRect.width<=this.containerRect.right;this.votSubtitlesContainer.style.top=n&&i?o-this.containerRect.y+"px":n?this.containerRect.height-this.subtitlesContainerRect.height+"px":"0px",this.votSubtitlesContainer.style.left=a&&r?e-this.containerRect.x+"px":a?this.containerRect.width-this.subtitlesContainerRect.width+"px":"0px"}}onTimeUpdate(){this.update()}setContent(t){t&&this.video?(this.subtitles=t,this.update()):(this.subtitles=null,this.votSubtitlesContainer.innerHTML="")}setMaxLength(t){"number"==typeof t&&t&&(this.maxLength=t,this.maxLengthRegexp=new RegExp(`.{1,${t}}(?:\\s|$)`,"g"),this.update())}setHighlightWords(t){this.highlightWords!==!!t&&(this.highlightWords=!!t,this.update())}update(){if(!this.video)return;let t="",e=this.highlightWords&&this.subtitles?.containsTokens;const o=1e3*this.video.currentTime,n=this.subtitles?.subtitles?.findLast((t=>t.startMsthis.maxLength){let t=[],e=0,n=0,a=0;for(let o=0;othis.maxLength){let r=i.slice(e,n+1);r.at(0)&&" "===r.at(0).text&&(r=r.slice(1)),r.at(-1)&&" "===r.at(-1).text&&(r=r.slice(0,r.length-1)),t.push({startMs:i[e].startMs,durationMs:i[n].startMs+i[n].durationMs-i[e].startMs,tokens:r}),e=o,a=0}n=o}for(let e=0;er||o>a.startMs-100&&r-o<275)?'class="passed"':""}>${a.text}`}}t!==this.lastContent&&(this.lastContent=t,this.votSubtitlesContainer.innerHTML=t?`${t.replace("\\n","
")}
`:"")}}const rt={getVideoData:async function(){const t=window.course_id??document.querySelector('input[name="course_id"]')?.value,e=window.lessons??await async function(t){const e=await fetch(`https://coursehunter.net/api/v1/course/${t}/lessons`);return await e.json()}(t),o=parseInt(document.querySelector(".lessons-item_active")?.dataset?.index??1),n=e?.[o-1],{file:i,duration:a}=n;return C.A.log("coursehunter course data:",e),{url:i,duration:a}}};function st(){return lt()?.player}function lt(){return document.querySelector(".vjs-v6")}const dt={getPlayer:lt,getPlayerData:st,getVideoData:async function(t="en"){let e=null;const o=st(),{duration:n}=o?.cache_||{},{courseId:i,tracks:a,sources:s}=o?.options_||{},l=function(t){const e=t?.find((t=>"video/mp4"===t.type));return e?.src}(s),d=await async function(t){const e=await fetch(`https://www.coursera.org/api/onDemandCourses.v1/${t}`),o=await e.json();return o?.elements?.[0]}(i);let c=d?.primaryLanguageCodes?.[0];c=c?(0,P.R4)(c):"en",r.xm.includes(c)||(c="en");const u=function(t,e,o){let n=t?.find((t=>(0,P.R4)(t.srclang)===e));return n||(n=t?.find((t=>(0,P.R4)(t.srclang)===o))||t?.[0]),n?.src}(a,c,t);console.log(`videoURL: ${l}, subtitlesURL: ${u}`),u&&l?e=[{target:"video_file_url",targetUrl:l},{target:"subtitles_file_url",targetUrl:`https://www.coursera.org${u}`}]:l&&!u?(console.warn("[VOT] Subtitles files not found. Using the link only to the video file."),e={url:l}):console.error(`Failed to find subtitlesURL or videoURL. videoURL: ${l}, subtitlesURL: ${u}`);const h={duration:n,detectedLanguage:c,translationHelp:e};return C.A.log("coursera video data:",h),console.log("[VOT] Detected language: ",h.detectedLanguage),h}},ct="https://www.udemy.com/api-2.0",ut=2592e6;async function ht(t){const e=await fetch(`${ct}/courses/${t}/?`+new URLSearchParams({"fields[course]":"locale",use_remote_version:"true",caching_intent:"true"}));return await e.json()}async function pt(t,e,o){if(!(n=t.expires,n+ut>(new Date).getTime()&&t.accessToken))return void console.error(s.j.get("udemyAccessTokenExpired"));var n;const i=`Bearer ${t.accessToken}`,a=await fetch(`${ct}/users/me/subscribed-courses/${e}/lectures/${o}/?`+new URLSearchParams({"fields[lecture]":"asset","fields[asset]":"length,media_sources,captions"}),{headers:{"x-udemy-authorization":i,authorization:i}});return await a.json()}function gt(){return mt()?.player}function vt(){const t=document.querySelector(".ud-app-loader[data-module-id='course-taking']")?.dataset?.moduleArgs;return t?JSON.parse(t):(console.error(s.j.get("udemyModuleArgsNotFound")),{})}function mt(){return document.querySelector(".vjs-v7")}const bt={getPlayer:mt,getPlayerData:gt,getVideoData:async function(t,e="en"){let o=null;const n=gt();C.A.log("udemyData",t);const i=vt();C.A.log("moduleData: ",i);const a=i.courseId,s=/learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1];C.A.log(`CourseId: ${a}, lectureId: ${s}`);const l=await ht(a);C.A.log("courseLang Data:",l);const d=await pt(t,a,s);console.log("lecture Data:",d);let c=l?.locale?.locale;c=c?(0,P.R4)(c):"en",r.xm.includes(c)||(c="en");const u=d?.asset?.length||n?.cache_?.duration,h=function(t){const e=t?.find((t=>"video/webm"===t.type||"video/mp4"===t.type));return e?.src}(d?.asset?.media_sources)||function(){const t=mt()?.querySelector("video")?.src;return!t?.startsWith("blob:")&&t}(),p=function(t,e,o){let n=t?.find((t=>(0,P.R4)(t.locale_id)===e));return n||(n=t?.find((t=>(0,P.R4)(t.locale_id)===o))||t?.[0]),n?.url}(d?.asset?.captions,c,e);console.log(`videoURL: ${h}, subtitlesURL: ${p}`),p&&h?o=[{target:"video_file_url",targetUrl:h},{target:"subtitles_file_url",targetUrl:p}]:h&&!p?(console.warn("[VOT] Subtitles files not found. Using the link only to the video file."),o={url:h}):console.error(`Failed to find subtitlesURL or videoURL. videoURL: ${h}, subtitlesURL: ${p}`);const g={duration:u,detectedLanguage:c,translationHelp:o};return C.A.log("udemy video data:",g),console.log("[VOT] Detected language: ",g.detectedLanguage),g},getModuleData:vt,getCourseLang:ht,getLectureData:pt};const ft={getVideoData:async function(t){const e=await async function(t){return await fetch("https://api.banned.video/graphql",{method:"POST",body:JSON.stringify({operationName:"GetVideo",query:"query GetVideo($id: String!) {\n getVideo(id: $id) {\n ...DisplayVideoFields\n videoUrl: directUrl\n live\n }\n }\n\n fragment DisplayVideoFields on Video {\n title\n description: summary\n duration: videoDuration\n }",variables:{id:t}}),headers:{"User-Agent":"bannedVideoFrontEnd","apollographql-client-name":"banned-web","apollographql-client-version":"1.3","content-type":"application/json"}}).then((t=>t.json())).catch((t=>(console.error(t),{data:{getVideo:{}}})))}(t);C.A.log("banned.video video data:",e);const{videoUrl:o,duration:n,live:i,description:a,title:r}=e.data.getVideo;return{url:o,duration:n,live:i,title:r,description:a}}};const yt="https://global.apis.naver.com/weverse/wevweb",wt="be4d79eb8fc7bd008ee82c8ec4ff6fd4",xt="1b9cb6378d959b45714bec49971ade22e6e24e42";async function St(t){const e=Date.now();let o=t.substring(0,Math.min(255,t.length))+e;const n=await async function(t,e){try{const o=new TextEncoder("utf-8");e=o.encode(e);const n=await window.crypto.subtle.importKey("raw",o.encode(t),{name:"HMAC",hash:{name:"SHA-1"}},!1,["sign","verify"]),i=await window.crypto.subtle.sign("HMAC",n,e);return btoa(String.fromCharCode(...new Uint8Array(i)))}catch(t){return console.error(t),!1}}(xt,o);return{wmsgpad:e,wmd:n}}function kt(){return{appId:wt,language:"en",os:"WEB",platform:"WEB",wpf:"pc"}}const Tt={getVideoData:async function(){const t=/([^/]+)\/(live|media)\/([^/]+)/.exec(new URL(window.location).pathname)?.[3],e=await async function(t){const e=`/post/v1.0/post-${t}/preview?`+new URLSearchParams({fieldSet:"postForPreview",...kt()}),o=await St(e);try{const t=await fetch(yt+e+"&"+new URLSearchParams(o));return await t.json()}catch(t){return console.error(t),!1}}(t);if(!e)return;C.A.log("weverse video preview data:",e);const{videoId:o,serviceId:n,infraVideoId:i}=e.extension.video;if(!(o&&n&&i))return!1;const a=await async function(t){const e=`/video/v1.1/vod/${t}/inKey?`+new URLSearchParams({gcc:"RU",...kt()}),o=await St(e);try{const t=await fetch(yt+e+"&"+new URLSearchParams(o),{method:"POST"});return await t.json()}catch(t){return console.error(t),!1}}(o);if(C.A.log("weverse video inKey data:",e),!a)return!1;const r=await async function(t,e,o){const n=Date.now();try{const i=await fetch(`https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${t}?`+new URLSearchParams({key:e,sid:o,nonce:n,devt:"html5_pc",prv:"N",aup:"N",stpb:"N",cpl:"en",env:"prod",lc:"en",adi:JSON.stringify([{adSystem:null}]),adu:"/"}));return await i.json()}catch(t){return console.error(t),!1}}(i,a.inKey,n);C.A.log("weverse video info:",r);const s=r.videos.list.find((t=>!1===t.useP2P&&t.source.includes(".mp4")));return!!s&&{url:s.source,duration:s.duration}}},Vt=[{additionalData:"mobile",host:"youtube",url:"https://youtu.be/",match:/^m.youtube.com$/,selector:"shorts-video #player"},{additionalData:"mobile",host:"youtube",url:"https://youtu.be/",match:/^m.youtube.com$/,selector:".player-container"},{host:"youtube",url:"https://youtu.be/",match:/^(www.)?youtube(-nocookie|kids)?.com$/,selector:".html5-video-container:not(#inline-player *)"},{host:"tiktok",url:"https://www.tiktok.com/",match:/^(www.)?tiktok.com$/,selector:null},{host:"proxitok",url:"https://www.tiktok.com/",match:n,selector:".column.has-text-centered"},{host:"twitch",url:"https://twitch.tv/",match:[/^m.twitch.tv$/,/^www.twitch.tv$/,/^clips.twitch.tv$/,/^player.twitch.tv$/],selector:".video-ref, main > div > section > div > div > div"},{host:"xvideos",url:"https://www.xvideos.com/",match:/^www.(xvideos|xv-ru).com$/,selector:".video-bg-pic"},{host:"pornhub",url:"https://rt.pornhub.com/view_video.php?viewkey=",match:/^[a-z]+.pornhub.com$/,selector:".mainPlayerDiv > .video-element-wrapper-js > div"},{additionalData:"embed",host:"pornhub",url:"https://rt.pornhub.com/view_video.php?viewkey=",match:t=>t.host.includes("pornhub.com")&&t.pathname.startsWith("/embed/"),selector:"#player"},{additionalData:"mobile",host:"vk",url:"https://vk.com/video?z=",match:/^m.vk.(com|ru)$/,selector:"vk-video-player",shadowRoot:!0},{additionalData:"clips",host:"vk",url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:'div[data-testid="clipcontainer-video"]'},{host:"vk",url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:".videoplayer_media"},{host:"vimeo",url:"https://vimeo.com/",match:/^vimeo.com$/,selector:".player"},{additionalData:"embed",host:"vimeo",url:"https://player.vimeo.com/",match:/^player.vimeo.com$/,selector:".player"},{host:"ok.ru",url:"https://ok.ru/video/",match:/^ok.ru$/,selector:".html5-vpl_vid"},{host:"nine_gag",url:"https://9gag.com/gag/",match:/^9gag.com$/,selector:".video-post"},{host:"bitchute",url:"https://www.bitchute.com/video/",match:/^(www.)?bitchute.com$/,selector:".video-js"},{host:"rutube",url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:".video-player > div > div > div:nth-child(2)"},{additionalData:"embed",host:"rutube",url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:"#app > div > div"},{host:"bilibili",url:"https://www.bilibili.com/video/",match:/^(www|m|player).bilibili.com$/,selector:".bpx-player-video-wrap"},{additionalData:"old",host:"bilibili",url:"https://www.bilibili.com/video/",match:/^(www|m).bilibili.com$/,selector:null},{host:"twitter",url:"https://twitter.com/i/status/",match:/^twitter.com$/,selector:'div[data-testid="videoComponent"] > div:nth-child(1) > div'},{host:"mail_ru",url:"https://my.mail.ru/",match:/^my.mail.ru$/,selector:"#b-video-wrapper"},{host:"coursera",url:"https://www.coursera.org/",match:/coursera.org$/,selector:".vjs-v6"},{host:"udemy",url:"https://www.udemy.com/",match:/udemy.com$/,selector:'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)'},{host:"invidious",url:"https://youtu.be/",match:t,selector:"#player"},{host:"piped",url:"https://youtu.be/",match:e,selector:".shaka-video-container"},{host:"rumble",url:"https://rumble.com/",match:/^rumble.com$/,selector:"#videoPlayer > .videoPlayer-Rumble-cls > div"},{host:"eporner",url:"https://www.eporner.com/",match:/^(www.)?eporner.com$/,selector:".vjs-v7"},{host:"peertube",url:"stub",match:i,selector:".vjs-v7"},{host:"dailymotion",url:"https://dai.ly/",match:/^geo.dailymotion.com$/,selector:".player"},{host:"trovo",url:"https://trovo.live/s/",match:/^trovo.live$/,selector:".player-video"},{host:"yandexdisk",url:"https://yadi.sk/i/",match:/^disk.yandex.ru$/,selector:".video-player__player > div:nth-child(1)"},{host:"coursehunter",url:"https://coursehunter.net/course/",match:/^coursehunter.net$/,selector:"#oframeplayer > pjsdiv:nth-of-type(1)"},{host:"googledrive",url:"https://drive.google.com/file/d/",match:/^youtube.googleapis.com$/,selector:".html5-video-container"},{host:"bannedvideo",url:"https://banned.video/watch?id=",match:/^(www.)?banned.video$/,selector:".vjs-v7"},{host:"facebook",url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/videos/"),selector:'div[role="main"] div[data-pagelet$="video" i]'},{additionalData:"reels",host:"facebook",url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/reel/"),selector:'div[role="main"]'},{host:"weverse",url:"https://weverse.io/",match:/^weverse.io$/,selector:".webplayer-internal-source-wrapper"},{host:"newgrounds",url:"https://www.newgrounds.com/",match:/^www.newgrounds.com$/,selector:".ng-video-player"},{host:"egghead",url:"https://egghead.io/",match:/^egghead.io$/,selector:".cueplayer-react-video-holder"},{host:"youku",url:"https://v.youku.com/",match:/^v.youku.com$/,selector:"#ykPlayer"},{host:"archive",url:"https://archive.org/details/",match:/^archive.org$/,selector:".jw-media"},{host:"directlink",url:"stub",match:t=>/([^.]+).mp4/.test(t.pathname),selector:null}];o("./node_modules/requestidlecallback-polyfill/index.js");class Mt{constructor(){this.listeners=new Set}hasListener(t){return this.listeners.has(t)}dispatchToListener(t,...e){try{t(...e)}catch(t){console.error("[VOT]",t)}}addListener(t){if(this.hasListener(t))throw new Error("[VOT] The listener has already been added.");this.listeners.add(t)}removeListener(t){if(!this.hasListener(t))throw new Error("[VOT] The listener has not been added yet.");this.listeners.delete(t)}dispatch(...t){for(const e of Array.from(this.listeners))this.dispatchToListener(e,...t)}}function Lt(t){return Array.from(t).flatMap((t=>t instanceof HTMLVideoElement?[t]:t instanceof HTMLElement?Array.from(t.querySelectorAll("video")):t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll("video")):[]))}const At=/advertise|promo|sponsor|banner|commercial|preroll|midroll|postroll|ad-container|sponsored/i;var Ct=o("./src/utils/storage.js"),Pt=o("./src/utils/translateApis.js");const Ot=U.getParser(window.navigator.userAgent).getResult(),Et=[...t,...e],Bt=["playing","ratechange","play","waiting","pause"];function Rt(t,e){return t.map((t=>({label:s.j.get("langs")[t]??t.toUpperCase(),value:t,selected:e===t})))}var Ft=!1;class jt{translateFromLang="en";translateToLang=P.vV;timer;ytData="";videoData="";firstPlay=!0;audio=new Audio;audioContext=new(window.AudioContext||window.webkitAudioContext);gainNode=this.audioContext.createGain();hls=(0,P.CK)();videoTranslations=[];videoTranslationTTL=7200;downloadTranslationUrl=null;downloadSubtitlesUrl=null;autoRetry;streamPing;volumeOnStart;tempOriginalVolume;tempVolume;firstSyncVolume=!0;subtitlesList=[];subtitlesListVideoId=null;videoLastSrcObject=null;dragging;constructor(t,e,o){C.A.log("[VideoHandler] add video:",t,"container:",e,this),this.video=t,this.container=e,this.site=o,this.stopTranslationBound=this.stopTranslation.bind(this),this.handleVideoEventBound=this.handleVideoEvent.bind(this),this.changeOpacityOnEventBound=this.changeOpacityOnEvent.bind(this),this.resetTimerBound=this.resetTimer.bind(this),this.init()}async autoTranslate(){if(this.site.host,this.firstPlay&&1===this.data.autoTranslate&&this.videoData.videoId){this.firstPlay=!1;try{await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),this.transformBtn("error","VOTLocalizedError"===t?.name?t.localizedMessage:t)}}}async init(){if(this.initialized)return;const t="uk"===P.vV?1:0,e={autoTranslate:Ct.d.get("autoTranslate",0,!0),dontTranslateLanguage:Ct.d.get("dontTranslateLanguage",P.vV),dontTranslateYourLang:Ct.d.get("dontTranslateYourLang",1,!0),autoSetVolumeYandexStyle:Ct.d.get("autoSetVolumeYandexStyle",1,!0),autoVolume:Ct.d.get("autoVolume",a.JD,!0),buttonPos:Ct.d.get("buttonPos","default"),showVideoSlider:Ct.d.get("showVideoSlider",1,!0),syncVolume:Ct.d.get("syncVolume",0,!0),subtitlesMaxLength:Ct.d.get("subtitlesMaxLength",300,!0),highlightWords:Ct.d.get("highlightWords",0,!0),responseLanguage:Ct.d.get("responseLanguage",P.vV),defaultVolume:Ct.d.get("defaultVolume",100,!0),udemyData:Ct.d.get("udemyData",{accessToken:"",expires:0}),audioProxy:Ct.d.get("audioProxy",t,!0),showPiPButton:Ct.d.get("showPiPButton",0,!0),translateAPIErrors:Ct.d.get("translateAPIErrors",1,!0),translationService:Ct.d.get("translationService",a.mE),detectService:Ct.d.get("detectService",a.K2),m3u8ProxyHost:Ct.d.get("m3u8ProxyHost",a.se),proxyWorkerHost:Ct.d.get("proxyWorkerHost",a.Pm),audioBooster:Ct.d.get("audioBooster",0,!0)};this.data=Object.fromEntries(await Promise.all(Object.entries(e).map((async([t,e])=>[t,await e])))),console.log("[db] data from db: ",this.data),this.subtitlesWidget=new at(this.video,this.container,this.site),this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength),this.subtitlesWidget.setHighlightWords(this.data.highlightWords),this.audio.crossOrigin="anonymous",this.gainNode.connect(this.audioContext.destination),this.audioSource=this.audioContext.createMediaElementSource(this.audio),this.audioSource.connect(this.gainNode),this.initUI(),this.initUIEvents();const o=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=o,o?this.votMenu.container.hidden=!0:(this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage??"ru")),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),await this.autoTranslate(),this.translateToLang=this.data.responseLanguage??"ru",this.initExtraEvents(),this.initialized=!0}transformBtn(t="none",e){this.votButton.container.dataset.status=t,this.votButton.container.dataset.translating="error"===t&&e.includes(s.j.get("translationTake")),this.votButton.label.innerHTML=e,this.votButton.container.title="error"===t?e:""}initUI(){this.votButton=L.createVOTButton(s.j.get("translateVideo")),this.data?.buttonPos&&"default"!==this.data?.buttonPos&&this.container.clientWidth&&this.container.clientWidth>550?(this.votButton.container.dataset.direction="column",this.votButton.container.dataset.position=this.data?.buttonPos):(this.votButton.container.dataset.direction="row",this.votButton.container.dataset.position="default"),this.container.appendChild(this.votButton.container),this.votButton.pipButton.hidden=!(0,P.Bs)()||!this.data?.showPiPButton,this.votButton.separator2.hidden=!(0,P.Bs)()||!this.data?.showPiPButton,this.votButton.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votMenu=L.createVOTMenu(s.j.get("VOTSettings")),this.votMenu.container.dataset.position=this.container.clientWidth&&this.container.clientWidth>550?this.data?.buttonPos:"default",this.container.appendChild(this.votMenu.container),this.votDownloadButton=L.createIconButton(''),this.votDownloadButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadButton),this.votDownloadSubtitlesButton=L.createIconButton(''),this.votDownloadSubtitlesButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadSubtitlesButton),this.votSettingsButton=L.createIconButton(''),this.votMenu.headerContainer.appendChild(this.votSettingsButton),this.votTranslationLanguageSelect=L.createVOTLanguageSelect({fromTitle:s.j.get("langs")[this.video.detectedLanguage],fromDialogTitle:s.j.get("videoLanguage"),fromItems:[{label:s.j.get("langs").auto,value:"auto",selected:""},...Rt(r.xm,this.videoData.detectedLanguage)],fromOnSelectCB:async t=>{C.A.log("[fromOnSelectCB] select from language",t.target.dataset.votValue),this.videoData=await this.getVideoData(),this.setSelectMenuValues(t.target.dataset.votValue,this.videoData.responseLanguage)},toTitle:s.j.get("langs")[this.video.responseLanguage],toDialogTitle:s.j.get("translationLanguage"),toItems:Rt(r.Ww,this.videoData.responseLanguage),toOnSelectCB:async t=>{const e=t.target.dataset.votValue;C.A.log("[toOnSelectCB] select to language",e),this.data.responseLanguage=this.translateToLang=e,await Ct.d.set("responseLanguage",this.data.responseLanguage),C.A.log("Response Language value changed. New value: ",this.data.responseLanguage),this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage)}}),this.votMenu.bodyContainer.appendChild(this.votTranslationLanguageSelect.container),this.votSubtitlesSelect=L.createVOTSelect(s.j.get("VOTSubtitlesDisabled"),s.j.get("VOTSubtitles"),[{label:s.j.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1}],{onSelectCb:async t=>{await this.changeSubtitlesLang(t.target.dataset.votValue)},labelElement:L.createVOTSelectLabel(s.j.get("VOTSubtitles"))}),this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container),this.votVideoVolumeSlider=L.createSlider(`${s.j.get("VOTVolume")}: ${100*this.getVideoVolume()}%`,100*this.getVideoVolume()),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoVolumeSlider.container),this.votVideoTranslationVolumeSlider=L.createSlider(`${s.j.get("VOTVolumeTranslation")}: ${this.data?.defaultVolume??100}%`,this.data?.defaultVolume??100,0,this.data.audioBooster?a.T8:100),this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoTranslationVolumeSlider.container),this.votMenu.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votSettingsDialog=L.createDialog(s.j.get("VOTSettings")),document.documentElement.appendChild(this.votSettingsDialog.container),this.votTranslationHeader=L.createHeader(s.j.get("translationSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationHeader),this.votAutoTranslateCheckbox=L.createCheckbox(s.j.get("VOTAutoTranslate"),this.data?.autoTranslate??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoTranslateCheckbox.container),this.votDontTranslateYourLangSelect=L.createVOTSelect(s.j.get("langs")[Ct.d.syncGet("dontTranslateLanguage",P.vV)],s.j.get("VOTDontTranslateYourLang"),Rt(r.xm,Ct.d.syncGet("dontTranslateLanguage",P.vV)),{onSelectCb:async t=>{this.data.dontTranslateLanguage=t.target.dataset.votValue,await Ct.d.set("dontTranslateLanguage",this.data.dontTranslateLanguage)},labelElement:L.createCheckbox(s.j.get("VOTDontTranslateYourLang"),this.data?.dontTranslateYourLang??!0).container}),this.votSettingsDialog.bodyContainer.appendChild(this.votDontTranslateYourLangSelect.container),this.votAutoSetVolumeCheckbox=L.createCheckbox(`${s.j.get("VOTAutoSetVolume")}`,this.data?.autoSetVolumeYandexStyle??!0),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeCheckbox.container),this.votAutoSetVolumeSlider=L.createSlider(`${100*(this.data?.autoVolume??a.JD)}%`,100*(this.data?.autoVolume??a.JD),0,100),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeSlider.container),this.votShowVideoSliderCheckbox=L.createCheckbox(s.j.get("VOTShowVideoSlider"),this.data?.showVideoSlider??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votShowVideoSliderCheckbox.container),this.votAudioBoosterCheckbox=L.createCheckbox(s.j.get("VOTAudioBooster"),this.data?.audioBooster??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAudioBoosterCheckbox.container),this.votUdemyDataTextfield=L.createTextfield(s.j.get("VOTUdemyData"),this.data?.udemyData?.accessToken??""),this.votUdemyDataTextfield.container.hidden="udemy"!==this.site.host,this.votSettingsDialog.bodyContainer.appendChild(this.votUdemyDataTextfield.container),this.votSyncVolumeCheckbox=L.createCheckbox(s.j.get("VOTSyncVolume"),this.data?.syncVolume??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSyncVolumeCheckbox.container),this.votTranslationServiceSelect=L.createVOTSelect(Ct.d.syncGet("translationService",a.mE).toUpperCase(),s.j.get("VOTTranslationService"),Rt(Pt.vN,Ct.d.syncGet("translationService",a.mE)),{onSelectCb:async t=>{this.data.translationService=t.target.dataset.votValue,await Ct.d.set("translationService",this.data.translationService)},labelElement:L.createCheckbox(s.j.get("VOTTranslateAPIErrors"),this.data.translateAPIErrors??!0).container}),this.votTranslationServiceSelect.container.hidden="ru"===s.j.lang,this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationServiceSelect.container),this.votDetectServiceSelect=L.createVOTSelect(Ct.d.syncGet("detectService",a.K2).toUpperCase(),s.j.get("VOTDetectService"),Rt(Pt.qh,Ct.d.syncGet("detectService",a.K2)),{onSelectCb:async t=>{this.data.detectService=t.target.dataset.votValue,await Ct.d.set("detectService",this.data.detectService)},labelElement:L.createVOTSelectLabel(s.j.get("VOTDetectService"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votDetectServiceSelect.container),this.votSubtitlesHeader=L.createHeader(s.j.get("subtitlesSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader),this.votSubtitlesMaxLengthSlider=L.createSlider(`${s.j.get("VOTSubtitlesMaxLength")}: ${this.data?.subtitlesMaxLength??300}`,this.data?.subtitlesMaxLength??300,50,300),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesMaxLengthSlider.container),this.votSubtitlesHighlightWordsCheckbox=L.createCheckbox(s.j.get("VOTHighlightWords"),this.data?.highlightWords??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHighlightWordsCheckbox.container),this.votProxyHeader=L.createHeader(s.j.get("proxySettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votProxyHeader),this.votM3u8ProxyHostTextfield=L.createTextfield(s.j.get("VOTM3u8ProxyHost"),this.data?.m3u8ProxyHost,a.se),this.votSettingsDialog.bodyContainer.appendChild(this.votM3u8ProxyHostTextfield.container),this.votProxyWorkerHostTextfield=L.createTextfield(s.j.get("VOTProxyWorkerHost"),this.data?.proxyWorkerHost,a.Pm),this.votProxyWorkerHostTextfield.container.hidden=!1,this.votSettingsDialog.bodyContainer.appendChild(this.votProxyWorkerHostTextfield.container),this.votAudioProxyCheckbox=L.createCheckbox(s.j.get("VOTAudioProxy"),this.data?.audioProxy??!1),this.votAudioProxyCheckbox.container.hidden=!1,this.votSettingsDialog.bodyContainer.appendChild(this.votAudioProxyCheckbox.container),this.votAboutHeader=L.createHeader(s.j.get("about")),this.votSettingsDialog.bodyContainer.appendChild(this.votAboutHeader),this.votLanguageSelect=L.createVOTSelect(s.j.get("langs")[Ct.d.syncGet("locale-lang-override","auto")],s.j.get("VOTMenuLanguage"),Rt(s.z,Ct.d.syncGet("locale-lang-override","auto")),{onSelectCb:async t=>{await Ct.d.set("locale-lang-override",t.target.dataset.votValue)},labelElement:L.createVOTSelectLabel(s.j.get("VOTMenuLanguage"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votLanguageSelect.container),this.votShowPiPButtonCheckbox=L.createCheckbox(s.j.get("VOTShowPiPButton"),this.data?.showPiPButton??!1),this.votShowPiPButtonCheckbox.container.hidden=!(0,P.Bs)(),this.votSettingsDialog.bodyContainer.appendChild(this.votShowPiPButtonCheckbox.container),this.votVersionInfo=L.createInformation(`${s.j.get("VOTVersion")}:`,`cloudflare ${GM_info.script.version}`),this.votSettingsDialog.bodyContainer.appendChild(this.votVersionInfo.container),this.votAuthorsInfo=L.createInformation(`${s.j.get("VOTAuthors")}:`,GM_info.script.author),this.votSettingsDialog.bodyContainer.appendChild(this.votAuthorsInfo.container),this.votLoaderInfo=L.createInformation(`${s.j.get("VOTLoader")}:`,`${GM_info.scriptHandler} v${GM_info.version}`),this.votSettingsDialog.bodyContainer.appendChild(this.votLoaderInfo.container),this.votBrowserInfo=L.createInformation(`${s.j.get("VOTBrowser")}:`,`${Ot.browser.name} ${Ot.browser.version} (${Ot.os.name} ${Ot.os.version})`),this.votSettingsDialog.bodyContainer.appendChild(this.votBrowserInfo.container),this.votResetSettingsButton=L.createButton(s.j.get("resetSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votResetSettingsButton)}initUIEvents(){this.votButton.translateButton.addEventListener("click",(()=>{(async()=>{if(this.audio.src)return C.A.log("[click translationBtn] audio.src is not empty"),void this.stopTranslate();if(this.hls.url)return C.A.log("[click translationBtn] hls is not empty"),void this.stopTranslate();try{if(C.A.log("[click translationBtn] trying execute translation"),!this.videoData.videoId)throw new A("VOTNoVideoIDFound");"vk"===this.site.host&&"clips"===this.site.additionalData&&(this.videoData=await this.getVideoData()),await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),"VOTLocalizedError"===t?.name?this.transformBtn("error",t.localizedMessage):this.transformBtn("error",t)}})()})),this.votButton.pipButton.addEventListener("click",(()=>{(async()=>{this.video!==document.pictureInPictureElement?await this.video.requestPictureInPicture():await document.exitPictureInPicture()})()})),this.votButton.menuButton.addEventListener("click",(()=>{this.votMenu.container.hidden=!this.votMenu.container.hidden})),this.votButton.container.addEventListener("mousedown",(()=>{this.dragging=!0})),this.container.addEventListener("mouseup",(()=>{this.dragging=!1})),this.container.addEventListener("mousemove",(async t=>{if(this.dragging){t.preventDefault();const e=t.clientX/this.container.clientWidth*100;this.data.buttonPos=this.container.clientWidth&&this.container.clientWidth>550?e<=44?"left":e>=66?"right":"default":"default",this.votButton.container.dataset.direction="default"===this.data.buttonPos?"row":"column",this.votButton.container.dataset.position=this.data.buttonPos,this.votMenu.container.dataset.position=this.data.buttonPos,this.container.clientWidth&&this.container.clientWidth>550&&await Ct.d.set("buttonPos",this.data.buttonPos)}})),this.votDownloadButton.addEventListener("click",(()=>{this.downloadTranslationUrl&&window.open(this.downloadTranslationUrl,"_blank").focus()})),this.votDownloadSubtitlesButton.addEventListener("click",(async()=>{const t=function(t){let e="",o=1;for(const n of t.subtitles){let t=n.startMs/1e3,i=(n.startMs+n.durationMs)/1e3;e+=`${o}\n`,e+=`${ot(t)} --\x3e ${ot(i)}\n`,e+=`${n.text}\n\n`,o++}return e.trim()}(this.YandexSubtitles),e=new Blob([t],{type:"text/plain"}),o=URL.createObjectURL(e),n=document.createElement("a");n.href=o,n.download=`subtitles_${this.videoData.videoId}.srt`,n.click(),URL.revokeObjectURL(o)})),this.votSettingsButton.addEventListener("click",(()=>{this.votSettingsDialog.container.hidden=!this.votSettingsDialog.container.hidden,(document.fullscreenElement||document.webkitFullscreenElement)&&(document.webkitExitFullscreen&&document.webkitExitFullscreen(),document.exitFullscreen&&document.exitFullscreen())})),this.votVideoVolumeSlider.input.addEventListener("input",(t=>{const e=Number(t.target.value);this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`,this.setVideoVolume(e/100),this.data.syncVolume&&this.syncVolumeWrapper("video",e)})),this.votVideoTranslationVolumeSlider.input.addEventListener("input",(t=>{(async()=>{this.data.defaultVolume=Number(t.target.value),await Ct.d.set("defaultVolume",this.data.defaultVolume),this.votVideoTranslationVolumeSlider.label.querySelector("strong").innerHTML=`${this.data.defaultVolume}%`,this.gainNode.gain.value=this.data.defaultVolume/100,this.data.syncVolume&&(this.syncVolumeWrapper("translation",this.data.defaultVolume),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData&&this.setVideoVolume(this.tempOriginalVolume/100))})()})),this.votAutoTranslateCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoTranslate=Number(t.target.checked),await Ct.d.set("autoTranslate",this.data.autoTranslate),await this.autoTranslate(),C.A.log("autoTranslate value changed. New value: ",this.data.autoTranslate)})()})),this.votDontTranslateYourLangSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.dontTranslateYourLang=Number(t.target.checked),await Ct.d.set("dontTranslateYourLang",this.data.dontTranslateYourLang),C.A.log("dontTranslateYourLang value changed. New value: ",this.data.dontTranslateYourLang)})()})),this.votAutoSetVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoSetVolumeYandexStyle=Number(t.target.checked),await Ct.d.set("autoSetVolumeYandexStyle",this.data.autoSetVolumeYandexStyle),C.A.log("autoSetVolumeYandexStyle value changed. New value: ",this.data.autoSetVolumeYandexStyle)})()})),this.votAutoSetVolumeSlider.input.addEventListener("input",(t=>{(async()=>{const e=Number(t.target.value);this.data.autoVolume=(e/100).toFixed(2),await Ct.d.set("autoVolume",this.data.autoVolume),this.votAutoSetVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`})()})),this.votShowVideoSliderCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showVideoSlider=Number(t.target.checked),await Ct.d.set("showVideoSlider",this.data.showVideoSlider),C.A.log("showVideoSlider value changed. New value: ",this.data.showVideoSlider),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status})()})),this.votAudioBoosterCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioBooster=Number(t.target.checked),await Ct.d.set("audioBooster",this.data.audioBooster),C.A.log("audioBooster value changed. New value: ",this.data.audioBooster);const e=this.votVideoTranslationVolumeSlider.input.value;this.votVideoTranslationVolumeSlider.input.max=this.data.audioBooster?a.T8:100,this.data.audioBooster||(this.votVideoTranslationVolumeSlider.input.value=e>100?100:e,this.votVideoTranslationVolumeSlider.input.dispatchEvent(new Event("input")))})()})),this.votUdemyDataTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.udemyData={accessToken:t.target.value,expires:(new Date).getTime()},await Ct.d.set("udemyData",this.data.udemyData),C.A.log("udemyData value changed. New value: ",this.data.udemyData),window.location.reload()})()})),this.votSyncVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.syncVolume=Number(t.target.checked),await Ct.d.set("syncVolume",this.data.syncVolume),C.A.log("syncVolume value changed. New value: ",this.data.syncVolume)})()})),this.votTranslationServiceSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.translateAPIErrors=Number(t.target.checked),await Ct.d.set("translateAPIErrors",this.data.translateAPIErrors),C.A.log("translateAPIErrors value changed. New value: ",this.data.translateAPIErrors)})()})),this.votSubtitlesMaxLengthSlider.input.addEventListener("input",(t=>{(async()=>{this.data.subtitlesMaxLength=Number(t.target.value),await Ct.d.set("subtitlesMaxLength",this.data.subtitlesMaxLength),this.votSubtitlesMaxLengthSlider.label.querySelector("strong").innerHTML=`${this.data.subtitlesMaxLength}`,this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength)})()})),this.votSubtitlesHighlightWordsCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.highlightWords=Number(t.target.checked),await Ct.d.set("highlightWords",this.data.highlightWords),C.A.log("highlightWords value changed. New value: ",this.data.highlightWords),this.subtitlesWidget.setHighlightWords(this.data.highlightWords)})()})),this.votShowPiPButtonCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showPiPButton=Number(t.target.checked),await Ct.d.set("showPiPButton",this.data.showPiPButton),C.A.log("showPiPButton value changed. New value: ",this.data.showPiPButton),this.votButton.pipButton.hidden=!(0,P.Bs)()||!this.data.showPiPButton,this.votButton.separator2.hidden=!(0,P.Bs)()||!this.data.showPiPButton})()})),this.votM3u8ProxyHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.m3u8ProxyHost=t.target.value||a.se,await Ct.d.set("m3u8ProxyHost",this.data.m3u8ProxyHost),C.A.log("m3u8ProxyHost value changed. New value: ",this.data.m3u8ProxyHost)})()})),this.votProxyWorkerHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.proxyWorkerHost=t.target.value||a.Pm,await Ct.d.set("proxyWorkerHost",this.data.proxyWorkerHost),C.A.log("proxyWorkerHost value changed. New value: ",this.data.proxyWorkerHost),window.location.reload()})()})),this.votAudioProxyCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioProxy=Number(t.target.checked),await Ct.d.set("audioProxy",this.data.audioProxy),C.A.log("audioProxy value changed. New value: ",this.data.audioProxy)})()})),this.votResetSettingsButton.addEventListener("click",(()=>{(async()=>{s.j.reset();const t=await Ct.d.list();for(let e=0;e{this.extraEvents.push({element:t,event:e,handler:o}),t.addEventListener(e,o)},e=(e,o,n)=>{for(const i of o)t(e,i,n)};if(this.resizeObserver=new ResizeObserver((t=>{for(let e=0;e550;this.votButton.container.dataset.position=this.votMenu.container.dataset.position=e?this.data?.buttonPos:"default",this.votButton.container.dataset.direction=this.data?.buttonPos&&"default"!==this.data?.buttonPos&&e?"column":"row"})),this.resizeObserver.observe(this.video),this.votMenu.container.setAttribute("style",`--vot-container-height: ${this.video.getBoundingClientRect().height}px`),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData){this.syncVolumeObserver=new MutationObserver((t=>{if(this.audio.src&&this.data.syncVolume)for(let e=0;e{const e=t.target,o=this.votButton.container,n=this.votMenu.container,i=this.container,a=this.votSettingsDialog.container,r=document.querySelector(".vot-dialog-temp"),s=o.contains(e),l=n.contains(e),d=i.contains(e),c=a.contains(e),u=r?.contains(e)??!1;C.A.log(`[document click] ${s} ${l} ${d} ${c} ${u}`),s||l||c||u||(d||this.logout(0),this.votMenu.container.hidden=!0)})),o="pornhub"===this.site.host?"embed"===this.site.additionalData?document.querySelector("#player"):this.container.querySelector(".video-element-wrapper-js > div"):"twitter"===this.site.host?document.querySelector('div[data-testid="videoPlayer"]'):"yandexdisk"===this.site.host?document.querySelector(".video-player__player"):this.container,o&&e(o,["mousemove","mouseout"],this.resetTimerBound),t(this.votButton.container,"mousemove",this.changeOpacityOnEventBound),t(this.votMenu.container,"mousemove",this.changeOpacityOnEventBound),e(document,["touchstart","touchmove","touchend"],this.changeOpacityOnEventBound),t(this.votButton.container,"mousedown",(t=>{t.stopImmediatePropagation()})),t(this.votMenu.container,"mousedown",(t=>{t.stopImmediatePropagation()})),"youtube"===this.site.host&&(this.container.draggable=!1),"googledrive"===this.site.host&&(this.container.style.height="100%"),t(this.video,"canplaythrough",(async()=>{"rutube"===this.site.host&&this.video.src||(0,P.jI)(this.site.host,this.video)!==this.videoData.videoId&&(await this.handleSrcChanged(),C.A.log("lipsync mode is loadeddata"),await this.autoTranslate())})),t(this.video,"emptied",(()=>{this.video.src&&(0,P.jI)(this.site.host,this.video)===this.videoData.videoId||(C.A.log("lipsync mode is emptied"),this.videoData="",this.stopTranslation())})),["rutube","ok"].includes(this.site.host)||t(this.video,"volumechange",(()=>{this.syncVideoVolumeSlider()}))}logout(t){this.votMenu.container.hidden&&(this.votButton.container.style.opacity=t)}resetTimer(){clearTimeout(this.timer),this.logout(1),this.timer=setTimeout((()=>{this.logout(0)}),1e3)}changeOpacityOnEvent(t){clearTimeout(this.timer),this.logout(1),t.stopPropagation()}async changeSubtitlesLang(t){if(C.A.log("[onchange] subtitles",t),this.votSubtitlesSelect.setSelected(t),"disabled"===t)this.votSubtitlesSelect.setTitle(s.j.get("VOTSubtitlesDisabled")),this.subtitlesWidget.setContent(null),this.votDownloadSubtitlesButton.hidden=!0,this.YandexSubtitles=null;else{const e=await nt(this.subtitlesList.at(parseInt(t)));this.subtitlesWidget.setContent(e),this.votDownloadSubtitlesButton.hidden=!1,this.YandexSubtitles=e}}async updateSubtitlesLangSelect(){const t=[{label:s.j.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1},...this.subtitlesList.map(((t,e)=>({label:(s.j.get("langs")[t.language]??t.language.toUpperCase())+(t.translatedFromLanguage?` ${s.j.get("VOTTranslatedFrom")} ${s.j.get("langs")[t.translatedFromLanguage]??t.translatedFromLanguage.toUpperCase()}`:"")+("yandex"!==t.source?` ${t.source}`:"")+(t.isAutoGenerated?` (${s.j.get("VOTAutogenerated")})`:""),value:e,selected:!1,disabled:!1})))];this.votSubtitlesSelect.updateItems(t),await this.changeSubtitlesLang(t[0].value)}async updateSubtitles(){if(await this.changeSubtitlesLang("disabled"),!this.videoData.videoId)return console.error(`[VOT] ${s.j.getDefault("VOTNoVideoIDFound")}`),this.subtitlesList=[],this.subtitlesListVideoId=null,this.votButton.container.hidden=!0,void await this.updateSubtitlesLangSelect();this.votButton.container.hidden=!1,this.subtitlesListVideoId!==this.videoData.videoId&&(this.subtitlesList=await it(this.site,this.videoData.videoId,this.videoData.detectedLanguage),this.subtitlesList?this.subtitlesListVideoId=this.videoData.videoId:await this.changeSubtitlesLang("disabled"),await this.updateSubtitlesLangSelect())}getVideoVolume(){let t=this.video?.volume;return["youtube","googledrive"].includes(this.site.host)&&(t=Q.A.getVideoVolume()??t),t}setVideoVolume(t){if(["youtube","googledrive"].includes(this.site.host)){if(Q.A.setVideoVolume(t))return}this.video.volume=t}isMuted(){return["youtube","googledrive"].includes(this.site.host)?Q.A.isMuted():this.video?.muted}syncVideoVolumeSlider(){const t=this.isMuted()?0:100*this.getVideoVolume(),e=Math.round(t);this.votVideoVolumeSlider.input.value=e,this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`,L.updateSlider(this.votVideoVolumeSlider.input),1===this.data.syncVolume&&(this.tempOriginalVolume=Number(e))}setSelectMenuValues(t,e){this.votTranslationLanguageSelect.fromSelect.setTitle(s.j.get("langs")[t]),this.votTranslationLanguageSelect.toSelect.setTitle(s.j.get("langs")[e]),this.votTranslationLanguageSelect.fromSelect.setSelected(t),this.votTranslationLanguageSelect.toSelect.setSelected(e),console.log(`[VOT] Set translation from ${t} to ${e}`),this.videoData.detectedLanguage=t,this.videoData.responseLanguage=e}syncVolumeWrapper(t,e){const o="translation"===t?this.votVideoVolumeSlider:this.votVideoTranslationVolumeSlider,n=Number(o.input.value),i=function(t,e,o,n){let i=e;return e>n?(i=o+(e-n),i=i>100?100:Math.max(i,0),t.volume=i/100):e100?100:Math.max(i,0),t.volume=i/100),i}("translation"===t?this.video:this.audio,e,n,"translation"===t?this.tempVolume:this.tempOriginalVolume);o.input.value=i,o.label.querySelector("strong").innerHTML=`${i}%`,L.updateSlider(o.input),this.tempOriginalVolume="translation"===t?i:e,this.tempVolume="translation"===t?e:i}async getVideoData(){const t={translationHelp:null,isStream:!1,duration:this.video?.duration||343,videoId:(0,P.jI)(this.site.host,this.video),detectedLanguage:this.translateFromLang,responseLanguage:this.translateToLang};if(!t.videoId)return this.ytData={},t;if("youtube"===this.site.host)this.ytData=await Q.A.getVideoData(),t.isStream=this.ytData.isLive,this.ytData.title&&(t.detectedLanguage=this.ytData.detectedLanguage,t.responseLanguage=this.translateToLang);else if(["rutube","ok.ru","mail_ru"].includes(this.site.host))t.detectedLanguage="ru";else if("youku"===this.site.host)t.detectedLanguage="zh";else if("vk"===this.site.host){const e=document.getElementsByTagName("track")?.[0]?.srclang;t.detectedLanguage=e||"auto"}else if("coursera"===this.site.host){const e=await dt.getVideoData(this.translateToLang);t.duration=e.duration||t.duration,t.detectedLanguage=e.detectedLanguage,t.translationHelp=e.translationHelp}else if("coursehunter"===this.site.host){const e=await rt.getVideoData();t.translationHelp={url:e.url},t.duration=e.duration||t.duration}else if("bannedvideo"===this.site.host){const e=await ft.getVideoData(t.videoId);t.translationHelp={url:e.url},t.duration=e.duration||t.duration,t.isStream=e.live}else if("weverse"===this.site.host){const e=await Tt.getVideoData();t.detectedLanguage="ko",e&&(t.translationHelp={url:e.url},t.duration=e.duration||t.duration)}else if("udemy"===this.site.host){const e=await bt.getVideoData(this.data.udemyData,this.translateToLang);t.duration=e.duration||t.duration,t.detectedLanguage=e.detectedLanguage,t.translationHelp=e.translationHelp}else"bitchute"===this.site.host?t.translationHelp={url:t.videoId}:["bilibili","piped","invidious","bitchute","rumble","peertube","dailymotion","trovo","yandexdisk","coursehunter","archive","directlink"].includes(this.site.host)&&(t.detectedLanguage="auto");return t}videoValidator(){if(["youtube","ok.ru","vk"].includes(this.site.host)&&(C.A.log("VideoValidator videoData: ",this.videoData),1===this.data.dontTranslateYourLang&&this.videoData.detectedLanguage===this.data.dontTranslateLanguage))throw new A("VOTDisableFromYourLang");if(!this.videoData.isStream&&this.videoData.duration>14400)throw new A("VOTVideoIsTooLong");return!0}lipSync(t=!1){if(C.A.log("lipsync video",this.video),this.video)if(this.audio.currentTime=this.video.currentTime,this.audio.playbackRate=this.video.playbackRate,t)if("play"!=t)["pause","stop","waiting"].includes(t)&&(C.A.log(`lipsync mode is ${t}`),this.audio.pause()),"playing"==t&&(C.A.log("lipsync mode is playing"),this.audio.play());else{C.A.log("lipsync mode is play");const t=this.audio.play();void 0!==t&&t.catch((t=>{if(console.error("[VOT]",t),"NotAllowedError"===t.name)throw this.transformBtn("error",s.j.get("grantPermissionToAutoPlay")),new A("grantPermissionToAutoPlay");if("NotSupportedError"===t.name)throw this.transformBtn("error",Et.includes(window.location.hostname)?s.j.get("neededAdditionalExtension"):s.j.get("audioFormatNotSupported")),Et.includes(window.location.hostname)?new A("neededAdditionalExtension"):new A("audioFormatNotSupported")}))}else C.A.log("lipsync mode is not set")}handleVideoEvent(t){C.A.log(`video ${t.type}`),this.lipSync(t.type)}stopTranslate(){for(const t of Bt)this.video.removeEventListener(t,this.handleVideoEventBound);this.audio.pause(),this.audio.src="",this.audio.removeAttribute("src"),this.votVideoVolumeSlider.container.hidden=!0,this.votVideoTranslationVolumeSlider.container.hidden=!0,this.votDownloadButton.hidden=!0,this.downloadTranslationUrl=null,this.transformBtn("none",s.j.get("translateVideo")),C.A.log(`Volume on start: ${this.volumeOnStart}`),this.volumeOnStart&&this.setVideoVolume(this.volumeOnStart),this.volumeOnStart="",clearInterval(this.streamPing),clearTimeout(this.autoRetry),this.hls?.destroy(),this.hls=(0,P.CK)(),this.firstSyncVolume=!0}async translateExecutor(t){C.A.log("Run translateFunc",t),this.translateFunc(t,this.videoData.isStream,this.videoData.detectedLanguage,this.videoData.responseLanguage,this.videoData.translationHelp)}async updateTranslationErrorMsg(t){const e=s.j.get("translationTake"),o=s.j.get("VOTTranslatingError"),n=s.j.lang;if("VOTLocalizedError"===t?.name)this.transformBtn("error",t.localizedMessage);else if(1!==this.data.translateAPIErrors||t.includes(e)||"ru"===n)this.transformBtn("error",t);else{const e=await(0,Pt.Tl)(t,"ru",n);this.transformBtn("error",o),this.transformBtn("error",e)}}afterUpdateTranslation(t){this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,1===this.data.autoSetVolumeYandexStyle&&(this.votVideoVolumeSlider.input.value=100*this.data.autoVolume,this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=100*this.data.autoVolume+"%",L.updateSlider(this.votVideoVolumeSlider.input)),this.votDownloadButton.hidden=!1,this.downloadTranslationUrl=t}updateTranslation(t){if(this.audio.src=t,1===this.data.audioProxy&&t.startsWith("https://vtrans.s3-private.mds.yandex.net/tts/prod/")){const e=t.replace("https://vtrans.s3-private.mds.yandex.net/tts/prod/",""),o=`https://${this.data.proxyWorkerHost}/video-translation/audio-proxy/${e}`;console.log(`[VOT] Audio proxied via ${o}`),this.audio.src=o}if(this.volumeOnStart=this.getVideoVolume(),"number"==typeof this.data.defaultVolume&&(this.gainNode.gain.value=this.data.defaultVolume/100),"number"==typeof this.data.autoSetVolumeYandexStyle&&this.data.autoSetVolumeYandexStyle&&this.setVideoVolume(this.data.autoVolume),"twitter"===this.site.host)document.querySelector('button[data-testid="app-bar-back"][role="button"]').addEventListener("click",this.stopTranslationBound);this.video&&!this.video.paused&&this.lipSync("play");for(const t of Bt)this.video.addEventListener(t,this.handleVideoEventBound);this.transformBtn("success",s.j.get("disableTranslate")),this.afterUpdateTranslation(t)}translateFunc(t,e,o,n,i){console.log("[VOT] Video Data: ",this.videoData);const a=i?.url?i.url:`${this.site.url}${t}`;if(C.A.log("Run videoValidator"),this.videoValidator(),e)return C.A.log("Executed stream translation"),void function(t,e,o,n){C.A.log(`Translate stream (url: ${t}, requestLang: ${e}, responseLang: ${o})`),K(t,e,o,((t,e)=>{if(C.A.log("[exec callback] Requesting stream translation"),!t)return void n(!1,s.j.get("requestTranslationFailed"));const o=H.decodeStreamResponse(e);switch(console.log("[VOT] Stream Translation response: ",o),o.interval){case 10:n(!1,o.interval,s.j.get("translationTakeFewMinutes"));break;case 20:n(!0,o.interval,o||s.j.get("audioNotReceived"));break;case 0:n(!1,o.interval,s.j.get("streamNoConnectionToServer"))}}))}(a,o,n,(async(a,r,l)=>{if(C.A.log("[exec callback] translateStream callback"),(0,P.jI)(this.site.host,this.video)!==t)return;if(!a||!l.translatedInfo)return await this.updateTranslationErrorMsg(l),void(10===r&&(clearTimeout(this.autoRetry),this.autoRetry=setTimeout((()=>this.translateFunc(t,e,o,n,i)),1e3*r)));this.transformBtn("success",s.j.get("disableTranslate")),console.log(l);const d=l.pingId;C.A.log(`Stream pingId: ${d}`),this.streamPing=setInterval((async()=>await Z(d,(t=>C.A.log("Stream ping result: ",t)))),1e3*r),C.A.log(l.translatedInfo.url);const c=`https://${this.data.m3u8ProxyHost}/?all=yes&origin=${encodeURIComponent("https://strm.yandex.ru")}&referer=${encodeURIComponent("https://strm.yandex.ru")}&url=${encodeURIComponent(l.translatedInfo.url)}`;if(C.A.log(c),this.hls)this.hls.on(Hls.Events.MEDIA_ATTACHED,(function(){C.A.log("audio and hls.js are now bound together !")})),this.hls.on(Hls.Events.MANIFEST_PARSED,(function(t){C.A.log("manifest loaded, found "+t?.levels?.length+" quality level")})),this.hls.loadSource(c),this.hls.attachMedia(this.audio),this.hls.on(Hls.Events.ERROR,(function(t){if(t.fatal)switch(t.type){case Hls.ErrorTypes.MEDIA_ERROR:console.log("fatal media error encountered, try to recover"),this.hls.recoverMediaError();break;case Hls.ErrorTypes.NETWORK_ERROR:console.error("fatal network error encountered",t);break;default:this.hls.destroy()}})),C.A.log(this.hls);else{if(!this.audio.canPlayType("application/vnd.apple.mpegurl"))throw new A("audioFormatNotSupported");this.audio.src=c}if("youtube"===this.site.host&&Q.A.videoSeek(this.video,10),this.volumeOnStart=this.getVideoVolume(),"number"==typeof this.data.defaultVolume&&(this.gainNode.gain.value=this.data.defaultVolume/100),"number"==typeof this.data.autoSetVolumeYandexStyle&&this.data.autoSetVolumeYandexStyle&&this.setVideoVolume(this.data.autoVolume),this.video.src||this.video.currentSrc||this.video.srcObject){this.video&&!this.video.paused&&this.lipSync("play");for(const t of Bt)this.video.addEventListener(t,this.handleVideoEventBound);this.afterUpdateTranslation(c)}else this.stopTranslation()}));if(["udemy","coursera"].includes(this.site.host)&&!i)throw new A("VOTTranslationHelpNull");const r=this.videoTranslations.find((e=>e.videoId===t&&e.expires>Date.now()/1e3&&e.from===o&&e.to===n));if(r)return this.updateTranslation(r.url),void C.A.log("[translateFunc] A cached translate was received");const l=this.subtitlesList.some((t=>"yandex"===t.source))?2e4:3e4;!function(t,e,o,n,i,a){C.A.log(`Translate video (url: ${t}, duration: ${e}, requestLang: ${o}, responseLang: ${n})`),C.A.log("translationHelp:",i),Ft?C.A.log("translationPanding return"):(Ft=!0,J(t,e,o,n,i,((t,e)=>{if(Ft=!1,C.A.log("[exec callback] Requesting video translation"),!t)return void a(!1,s.j.get("requestTranslationFailed"));const o=H.decodeTranslationResponse(e);switch(console.log("[VOT] Translation response: ",o),o.status){case 0:a(!1,o.message);break;case 1:case 5:a(!!o.url,o.url||s.j.get("audioNotReceived"));break;case 2:a(!1,o.remainingTime?(0,P.ox)(o.remainingTime):s.j.get("translationTakeFewMinutes"));break;case 3:case 6:a(!1,s.j.get("videoBeingTranslated"))}})))}(a,this.videoData.duration,o,n,i,(async(a,r)=>{if(C.A.log("[exec callback] translateVideo callback"),(0,P.jI)(this.site.host,this.video)===t){if(!a)return await this.updateTranslationErrorMsg(r),r.includes(s.j.get("translationTake"))&&(clearTimeout(this.autoRetry),this.autoRetry=setTimeout((()=>this.translateFunc(t,e,o,n,i)),l)),void console.error("[VOT]",r);this.updateTranslation(r),this.subtitlesList.some((t=>"yandex"===t.source&&t.translatedFromLanguage===this.videoData.detectedLanguage&&t.language===this.videoData.responseLanguage))||(this.subtitlesList=await it(this.site,this.videoData.videoId,this.videoData.detectedLanguage),await this.updateSubtitlesLangSelect()),this.videoTranslations.push({videoId:t,from:o,to:n,url:r,expires:Date.now()/1e3+this.videoTranslationTTL})}}))}stopTranslation(){this.stopTranslate(),this.syncVideoVolumeSlider()}async handleSrcChanged(){C.A.log("[VideoHandler] src changed",this),this.firstPlay=!0,this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.videoData.responseLanguage);const t=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=t,t&&(this.votMenu.container.hidden=t),this.site.selector||(this.container=this.video.parentElement),this.container.contains(this.votButton.container)||(this.container.appendChild(this.votButton.container),this.container.appendChild(this.votMenu.container)),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),this.translateToLang=this.data.responseLanguage??"ru"}async release(){C.A.log("[VideoHandler] release"),this.initialized=!1,this.releaseExtraEvents(),this.subtitlesWidget.release(),this.votButton.container.remove(),this.votMenu.container.remove()}}const Dt=new class{constructor(){this.videoCache=new Set,this.onVideoAdded=new Mt,this.onVideoRemoved=new Mt,this.observer=new MutationObserver((t=>{window.requestIdleCallback((()=>{for(let e=0;e{for(let e=0;e=3}(t)?requestAnimationFrame(o):e(t)}()}(t,(t=>{this.handleVideoAdded(t)}))}handleVideoAdded=t=>{this.onVideoAdded.dispatch(t)};handleVideoRemoved=t=>{document.contains(t)||(this.videoCache.delete(t),this.onVideoRemoved.dispatch(t))}},_t=new WeakMap;function qt(t,e){if(t.shadowRoot){let o=t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.shadowRoot.contains(e))):e.parentElement;return o&&o.shadowRoot?o.parentElement:o}{const o=Ot.browser.version.split(".")[0];if(t.selector?.includes(":not")&&t.selector?.includes("*")&&o&&("Chrome"===Ot.browser.name&&Number(o)<88||"Firefox"===Ot.browser.name&&Number(o)<84)){const o=t.selector.split(" *")[0];return o?Array.from(document.querySelectorAll(o)).find((t=>t.contains(e))):e.parentElement}return t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.contains(e))):e.parentElement}}(async function(){C.A.log("Loading extension..."),await s.j.update(),C.A.log(`Selected menu language: ${s.j.lang}`),C.A.log("Extension compatibility passed..."),Dt.onVideoAdded.addListener((t=>{for(const e of function(){const t=window.location.hostname,e=new URL(window.location),o=o=>o instanceof RegExp?o.test(t):"string"==typeof o?t.includes(o):"function"==typeof o&&o(e);return Vt.filter((t=>(Array.isArray(t.match)?t.match.some(o):o(t.match))&&t.host&&t.url))}()){if(!e)continue;let o=qt(e,t);if(o&&(("rumble"!==e.host||t.style.display)&&(["peertube","directlink"].includes(e.host)&&(e.url=window.location.origin),!_t.has(t)))){_t.set(t,new jt(t,o,e));break}}})),Dt.onVideoRemoved.addListener((async t=>{_t.has(t)&&(await _t.get(t).release(),_t.delete(t))})),Dt.enable()})().catch((t=>{console.error("[VOT]",t)}))})()})(); \ No newline at end of file +(()=>{var t={"./node_modules/bowser/es5.js":function(t){t.exports=function(t){var e={};function o(n){if(e[n])return e[n].exports;var a=e[n]={i:n,l:!1,exports:{}};return t[n].call(a.exports,a,a.exports,o),a.l=!0,a.exports}return o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)o.d(n,a,function(e){return t[e]}.bind(null,a));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=90)}({17:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n=o(18),a=function(){function t(){}return t.getFirstMatch=function(t,e){var o=e.match(t);return o&&o.length>0&&o[1]||""},t.getSecondMatch=function(t,e){var o=e.match(t);return o&&o.length>1&&o[2]||""},t.matchAndReturnConst=function(t,e,o){if(t.test(e))return o},t.getWindowsVersionName=function(t){switch(t){case"NT":return"NT";case"XP":case"NT 5.1":return"XP";case"NT 5.0":return"2000";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return}},t.getMacOSVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),10===e[0])switch(e[1]){case 5:return"Leopard";case 6:return"Snow Leopard";case 7:return"Lion";case 8:return"Mountain Lion";case 9:return"Mavericks";case 10:return"Yosemite";case 11:return"El Capitan";case 12:return"Sierra";case 13:return"High Sierra";case 14:return"Mojave";case 15:return"Catalina";default:return}},t.getAndroidVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),!(1===e[0]&&e[1]<5))return 1===e[0]&&e[1]<6?"Cupcake":1===e[0]&&e[1]>=6?"Donut":2===e[0]&&e[1]<2?"Eclair":2===e[0]&&2===e[1]?"Froyo":2===e[0]&&e[1]>2?"Gingerbread":3===e[0]?"Honeycomb":4===e[0]&&e[1]<1?"Ice Cream Sandwich":4===e[0]&&e[1]<4?"Jelly Bean":4===e[0]&&e[1]>=4?"KitKat":5===e[0]?"Lollipop":6===e[0]?"Marshmallow":7===e[0]?"Nougat":8===e[0]?"Oreo":9===e[0]?"Pie":void 0},t.getVersionPrecision=function(t){return t.split(".").length},t.compareVersions=function(e,o,n){void 0===n&&(n=!1);var a=t.getVersionPrecision(e),i=t.getVersionPrecision(o),r=Math.max(a,i),s=0,l=t.map([e,o],(function(e){var o=r-t.getVersionPrecision(e),n=e+new Array(o+1).join(".0");return t.map(n.split("."),(function(t){return new Array(20-t.length).join("0")+t})).reverse()}));for(n&&(s=r-Math.min(a,i)),r-=1;r>=s;){if(l[0][r]>l[1][r])return 1;if(l[0][r]===l[1][r]){if(r===s)return 0;r-=1}else if(l[0][r]1?a-1:0),r=1;r0){var r=Object.keys(o),l=s.default.find(r,(function(t){return e.isOS(t)}));if(l){var d=this.satisfies(o[l]);if(void 0!==d)return d}var u=s.default.find(r,(function(t){return e.isPlatform(t)}));if(u){var c=this.satisfies(o[u]);if(void 0!==c)return c}}if(i>0){var h=Object.keys(a),g=s.default.find(h,(function(t){return e.isBrowser(t,!0)}));if(void 0!==g)return this.compareVersion(a[g])}},e.isBrowser=function(t,e){void 0===e&&(e=!1);var o=this.getBrowserName().toLowerCase(),n=t.toLowerCase(),a=s.default.getBrowserTypeByAlias(n);return e&&a&&(n=a.toLowerCase()),n===o},e.compareVersion=function(t){var e=[0],o=t,n=!1,a=this.getBrowserVersion();if("string"==typeof a)return">"===t[0]||"<"===t[0]?(o=t.substr(1),"="===t[1]?(n=!0,o=t.substr(2)):e=[],">"===t[0]?e.push(1):e.push(-1)):"="===t[0]?o=t.substr(1):"~"===t[0]&&(n=!0,o=t.substr(1)),e.indexOf(s.default.compareVersions(a,o,n))>-1},e.isOS=function(t){return this.getOSName(!0)===String(t).toLowerCase()},e.isPlatform=function(t){return this.getPlatformType(!0)===String(t).toLowerCase()},e.isEngine=function(t){return this.getEngineName(!0)===String(t).toLowerCase()},e.is=function(t,e){return void 0===e&&(e=!1),this.isBrowser(t,e)||this.isOS(t)||this.isPlatform(t)},e.some=function(t){var e=this;return void 0===t&&(t=[]),t.some((function(t){return e.is(t)}))},t}();e.default=d,t.exports=e.default},92:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,a=(n=o(17))&&n.__esModule?n:{default:n},i=/version\/(\d+(\.?_?\d+)+)/i,r=[{test:[/googlebot/i],describe:function(t){var e={name:"Googlebot"},o=a.default.getFirstMatch(/googlebot\/(\d+(\.\d+))/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/opera/i],describe:function(t){var e={name:"Opera"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opr\/|opios/i],describe:function(t){var e={name:"Opera"},o=a.default.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/SamsungBrowser/i],describe:function(t){var e={name:"Samsung Internet for Android"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Whale/i],describe:function(t){var e={name:"NAVER Whale Browser"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MZBrowser/i],describe:function(t){var e={name:"MZ Browser"},o=a.default.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/focus/i],describe:function(t){var e={name:"Focus"},o=a.default.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/swing/i],describe:function(t){var e={name:"Swing"},o=a.default.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/coast/i],describe:function(t){var e={name:"Opera Coast"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opt\/\d+(?:.?_?\d+)+/i],describe:function(t){var e={name:"Opera Touch"},o=a.default.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/yabrowser/i],describe:function(t){var e={name:"Yandex Browser"},o=a.default.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/ucbrowser/i],describe:function(t){var e={name:"UC Browser"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Maxthon|mxios/i],describe:function(t){var e={name:"Maxthon"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/epiphany/i],describe:function(t){var e={name:"Epiphany"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/puffin/i],describe:function(t){var e={name:"Puffin"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sleipnir/i],describe:function(t){var e={name:"Sleipnir"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/k-meleon/i],describe:function(t){var e={name:"K-Meleon"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/micromessenger/i],describe:function(t){var e={name:"WeChat"},o=a.default.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/qqbrowser/i],describe:function(t){var e={name:/qqbrowserlite/i.test(t)?"QQ Browser Lite":"QQ Browser"},o=a.default.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/msie|trident/i],describe:function(t){var e={name:"Internet Explorer"},o=a.default.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/\sedg\//i],describe:function(t){var e={name:"Microsoft Edge"},o=a.default.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/edg([ea]|ios)/i],describe:function(t){var e={name:"Microsoft Edge"},o=a.default.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/vivaldi/i],describe:function(t){var e={name:"Vivaldi"},o=a.default.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/seamonkey/i],describe:function(t){var e={name:"SeaMonkey"},o=a.default.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sailfish/i],describe:function(t){var e={name:"Sailfish"},o=a.default.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i,t);return o&&(e.version=o),e}},{test:[/silk/i],describe:function(t){var e={name:"Amazon Silk"},o=a.default.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/phantom/i],describe:function(t){var e={name:"PhantomJS"},o=a.default.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/slimerjs/i],describe:function(t){var e={name:"SlimerJS"},o=a.default.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e={name:"BlackBerry"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e={name:"WebOS Browser"},o=a.default.getFirstMatch(i,t)||a.default.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/bada/i],describe:function(t){var e={name:"Bada"},o=a.default.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/tizen/i],describe:function(t){var e={name:"Tizen"},o=a.default.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/qupzilla/i],describe:function(t){var e={name:"QupZilla"},o=a.default.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/firefox|iceweasel|fxios/i],describe:function(t){var e={name:"Firefox"},o=a.default.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/electron/i],describe:function(t){var e={name:"Electron"},o=a.default.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MiuiBrowser/i],describe:function(t){var e={name:"Miui"},o=a.default.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/chromium/i],describe:function(t){var e={name:"Chromium"},o=a.default.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i,t)||a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/chrome|crios|crmo/i],describe:function(t){var e={name:"Chrome"},o=a.default.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/GSA/i],describe:function(t){var e={name:"Google Search"},o=a.default.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e={name:"Android Browser"},o=a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/playstation 4/i],describe:function(t){var e={name:"PlayStation 4"},o=a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/safari|applewebkit/i],describe:function(t){var e={name:"Safari"},o=a.default.getFirstMatch(i,t);return o&&(e.version=o),e}},{test:[/.*/i],describe:function(t){var e=-1!==t.search("\\(")?/^(.*)\/(.*)[ \t]\((.*)/:/^(.*)\/(.*) /;return{name:a.default.getFirstMatch(e,t),version:a.default.getSecondMatch(e,t)}}}];e.default=r,t.exports=e.default},93:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,a=(n=o(17))&&n.__esModule?n:{default:n},i=o(18),r=[{test:[/Roku\/DVP/],describe:function(t){var e=a.default.getFirstMatch(/Roku\/DVP-(\d+\.\d+)/i,t);return{name:i.OS_MAP.Roku,version:e}}},{test:[/windows phone/i],describe:function(t){var e=a.default.getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i,t);return{name:i.OS_MAP.WindowsPhone,version:e}}},{test:[/windows /i],describe:function(t){var e=a.default.getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i,t),o=a.default.getWindowsVersionName(e);return{name:i.OS_MAP.Windows,version:e,versionName:o}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(t){var e={name:i.OS_MAP.iOS},o=a.default.getSecondMatch(/(Version\/)(\d[\d.]+)/,t);return o&&(e.version=o),e}},{test:[/macintosh/i],describe:function(t){var e=a.default.getFirstMatch(/mac os x (\d+(\.?_?\d+)+)/i,t).replace(/[_\s]/g,"."),o=a.default.getMacOSVersionName(e),n={name:i.OS_MAP.MacOS,version:e};return o&&(n.versionName=o),n}},{test:[/(ipod|iphone|ipad)/i],describe:function(t){var e=a.default.getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i,t).replace(/[_\s]/g,".");return{name:i.OS_MAP.iOS,version:e}}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e=a.default.getFirstMatch(/android[\s/-](\d+(\.\d+)*)/i,t),o=a.default.getAndroidVersionName(e),n={name:i.OS_MAP.Android,version:e};return o&&(n.versionName=o),n}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e=a.default.getFirstMatch(/(?:web|hpw)[o0]s\/(\d+(\.\d+)*)/i,t),o={name:i.OS_MAP.WebOS};return e&&e.length&&(o.version=e),o}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e=a.default.getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i,t)||a.default.getFirstMatch(/blackberry\d+\/(\d+([_\s]\d+)*)/i,t)||a.default.getFirstMatch(/\bbb(\d+)/i,t);return{name:i.OS_MAP.BlackBerry,version:e}}},{test:[/bada/i],describe:function(t){var e=a.default.getFirstMatch(/bada\/(\d+(\.\d+)*)/i,t);return{name:i.OS_MAP.Bada,version:e}}},{test:[/tizen/i],describe:function(t){var e=a.default.getFirstMatch(/tizen[/\s](\d+(\.\d+)*)/i,t);return{name:i.OS_MAP.Tizen,version:e}}},{test:[/linux/i],describe:function(){return{name:i.OS_MAP.Linux}}},{test:[/CrOS/],describe:function(){return{name:i.OS_MAP.ChromeOS}}},{test:[/PlayStation 4/],describe:function(t){var e=a.default.getFirstMatch(/PlayStation 4[/\s](\d+(\.\d+)*)/i,t);return{name:i.OS_MAP.PlayStation4,version:e}}}];e.default=r,t.exports=e.default},94:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,a=(n=o(17))&&n.__esModule?n:{default:n},i=o(18),r=[{test:[/googlebot/i],describe:function(){return{type:"bot",vendor:"Google"}}},{test:[/huawei/i],describe:function(t){var e=a.default.getFirstMatch(/(can-l01)/i,t)&&"Nova",o={type:i.PLATFORMS_MAP.mobile,vendor:"Huawei"};return e&&(o.model=e),o}},{test:[/nexus\s*(?:7|8|9|10).*/i],describe:function(){return{type:i.PLATFORMS_MAP.tablet,vendor:"Nexus"}}},{test:[/ipad/i],describe:function(){return{type:i.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(){return{type:i.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/kftt build/i],describe:function(){return{type:i.PLATFORMS_MAP.tablet,vendor:"Amazon",model:"Kindle Fire HD 7"}}},{test:[/silk/i],describe:function(){return{type:i.PLATFORMS_MAP.tablet,vendor:"Amazon"}}},{test:[/tablet(?! pc)/i],describe:function(){return{type:i.PLATFORMS_MAP.tablet}}},{test:function(t){var e=t.test(/ipod|iphone/i),o=t.test(/like (ipod|iphone)/i);return e&&!o},describe:function(t){var e=a.default.getFirstMatch(/(ipod|iphone)/i,t);return{type:i.PLATFORMS_MAP.mobile,vendor:"Apple",model:e}}},{test:[/nexus\s*[0-6].*/i,/galaxy nexus/i],describe:function(){return{type:i.PLATFORMS_MAP.mobile,vendor:"Nexus"}}},{test:[/[^-]mobi/i],describe:function(){return{type:i.PLATFORMS_MAP.mobile}}},{test:function(t){return"blackberry"===t.getBrowserName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.mobile,vendor:"BlackBerry"}}},{test:function(t){return"bada"===t.getBrowserName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.mobile}}},{test:function(t){return"windows phone"===t.getBrowserName()},describe:function(){return{type:i.PLATFORMS_MAP.mobile,vendor:"Microsoft"}}},{test:function(t){var e=Number(String(t.getOSVersion()).split(".")[0]);return"android"===t.getOSName(!0)&&e>=3},describe:function(){return{type:i.PLATFORMS_MAP.tablet}}},{test:function(t){return"android"===t.getOSName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.mobile}}},{test:function(t){return"macos"===t.getOSName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.desktop,vendor:"Apple"}}},{test:function(t){return"windows"===t.getOSName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.desktop}}},{test:function(t){return"linux"===t.getOSName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.desktop}}},{test:function(t){return"playstation 4"===t.getOSName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.tv}}},{test:function(t){return"roku"===t.getOSName(!0)},describe:function(){return{type:i.PLATFORMS_MAP.tv}}}];e.default=r,t.exports=e.default},95:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,a=(n=o(17))&&n.__esModule?n:{default:n},i=o(18),r=[{test:function(t){return"microsoft edge"===t.getBrowserName(!0)},describe:function(t){if(/\sedg\//i.test(t))return{name:i.ENGINE_MAP.Blink};var e=a.default.getFirstMatch(/edge\/(\d+(\.?_?\d+)+)/i,t);return{name:i.ENGINE_MAP.EdgeHTML,version:e}}},{test:[/trident/i],describe:function(t){var e={name:i.ENGINE_MAP.Trident},o=a.default.getFirstMatch(/trident\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){return t.test(/presto/i)},describe:function(t){var e={name:i.ENGINE_MAP.Presto},o=a.default.getFirstMatch(/presto\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=t.test(/gecko/i),o=t.test(/like gecko/i);return e&&!o},describe:function(t){var e={name:i.ENGINE_MAP.Gecko},o=a.default.getFirstMatch(/gecko\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(apple)?webkit\/537\.36/i],describe:function(){return{name:i.ENGINE_MAP.Blink}}},{test:[/(apple)?webkit/i],describe:function(t){var e={name:i.ENGINE_MAP.WebKit},o=a.default.getFirstMatch(/webkit\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}}];e.default=r,t.exports=e.default}})},"./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss":(t,e,o)=>{"use strict";o.d(e,{A:()=>s});var n=o("./node_modules/css-loader/dist/runtime/noSourceMaps.js"),a=o.n(n),i=o("./node_modules/css-loader/dist/runtime/api.js"),r=o.n(i)()(a());r.push([t.id,'.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button:disabled{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button:disabled::before,.vot-button:disabled::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button:disabled::before,.vot-outlined-button:disabled::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button:disabled::before,.vot-text-button:disabled::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button:disabled::before,.vot-icon-button:disabled::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input,.vot-textfield:hover>textarea{border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input+span::before,.vot-textfield:hover>input+span::after,.vot-textfield:hover>textarea+span::before,.vot-textfield:hover>textarea+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:10000}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-translating=true] #vot-translating-icon{display:block !important}.vot-segmented-button[data-translating=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:10000;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog-container{visibility:visible;position:absolute;z-index:10000}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:10000;left:25%;top:75%;pointer-events:none}.vot-subtitles{position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:1rem;pointer-events:all;padding:1rem;font-size:2rem;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8);--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block;visibility:visible !important}',""]);const s=r},"./node_modules/css-loader/dist/runtime/api.js":t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var o="",n=void 0!==e[5];return e[4]&&(o+="@supports (".concat(e[4],") {")),e[2]&&(o+="@media ".concat(e[2]," {")),n&&(o+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),o+=t(e),n&&(o+="}"),e[2]&&(o+="}"),e[4]&&(o+="}"),o})).join("")},e.i=function(t,o,n,a,i){"string"==typeof t&&(t=[[null,t,void 0]]);var r={};if(n)for(var s=0;s0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=i),o&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=o):u[2]=o),a&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=a):u[4]="".concat(a)),e.push(u))}},e}},"./node_modules/css-loader/dist/runtime/noSourceMaps.js":t=>{"use strict";t.exports=function(t){return t[1]}},"./node_modules/requestidlecallback-polyfill/index.js":()=>{window.requestIdleCallback=window.requestIdleCallback||function(t){var e=Date.now();return setTimeout((function(){t({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-e))}})}),1)},window.cancelIdleCallback=window.cancelIdleCallback||function(t){clearTimeout(t)}},"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":t=>{"use strict";var e=[];function o(t){for(var o=-1,n=0;n{"use strict";var e={};t.exports=function(t,o){var n=function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(t){o=null}e[t]=o}return e[t]}(t);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(o)}},"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":(t,e,o)=>{"use strict";t.exports=function(t){var e=o.nc;e&&t.setAttribute("nonce",e)}},"./node_modules/style-loader/dist/runtime/styleDomAPI.js":t=>{"use strict";t.exports=function(t){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var e=t.insertStyleElement(t);return{update:function(o){!function(t,e,o){var n="";o.supports&&(n+="@supports (".concat(o.supports,") {")),o.media&&(n+="@media ".concat(o.media," {"));var a=void 0!==o.layer;a&&(n+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),n+=o.css,a&&(n+="}"),o.media&&(n+="}"),o.supports&&(n+="}");var i=o.sourceMap;i&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(i))))," */")),e.styleTagTransform(n,t,e.options)}(e,t,o)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},"./node_modules/style-loader/dist/runtime/styleTagTransform.js":t=>{"use strict";t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}},"./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js":t=>{t.exports=function(){return function(t){return t.styleTagTransform=function(t,e){e?.remove(),GM_addStyle(t)},document.createElement("style")}.apply(null,arguments)}}},e={};function o(n){var a=e[n];if(void 0!==a)return a.exports;var i=e[n]={id:n,exports:{}};return t[n].call(i.exports,i,i.exports,o),i.exports}o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.nc=void 0,(()=>{"use strict";var t=o("./node_modules/bowser/es5.js");const e=protobuf;var n;function a(t){switch(t){case 0:case"NO_CONNECTION":return n.NO_CONNECTION;case 10:case"TRANSLATING":return n.TRANSLATING;case 20:case"STREAMING":return n.STREAMING;default:return n.UNRECOGNIZED}}!function(t){t[t.NO_CONNECTION=0]="NO_CONNECTION",t[t.TRANSLATING=10]="TRANSLATING",t[t.STREAMING=20]="STREAMING",t[t.UNRECOGNIZED=-1]="UNRECOGNIZED"}(n||(n={}));const i={encode:(t,o=e.Writer.create())=>(""!==t.target&&o.uint32(10).string(t.target),""!==t.targetUrl&&o.uint32(18).string(t.targetUrl),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={target:"",targetUrl:""};for(;n.pos>>3){case 1:if(10!==t)break;i.target=n.string();continue;case 2:if(18!==t)break;i.targetUrl=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({target:b(t.target)?globalThis.String(t.target):"",targetUrl:b(t.targetUrl)?globalThis.String(t.targetUrl):""}),toJSON(t){const e={};return""!==t.target&&(e.target=t.target),""!==t.targetUrl&&(e.targetUrl=t.targetUrl),e},create:t=>i.fromPartial(t??{}),fromPartial(t){const e={target:"",targetUrl:""};return e.target=t.target??"",e.targetUrl=t.targetUrl??"",e}};const r={encode(t,o=e.Writer.create()){""!==t.url&&o.uint32(26).string(t.url),void 0!==t.deviceId&&o.uint32(34).string(t.deviceId),!1!==t.firstRequest&&o.uint32(40).bool(t.firstRequest),0!==t.duration&&o.uint32(49).double(t.duration),0!==t.unknown0&&o.uint32(56).int32(t.unknown0),""!==t.language&&o.uint32(66).string(t.language),!1!==t.forceSourceLang&&o.uint32(72).bool(t.forceSourceLang),0!==t.unknown1&&o.uint32(80).int32(t.unknown1);for(const e of t.translationHelp)i.encode(e,o.uint32(90).fork()).ldelim();return""!==t.responseLanguage&&o.uint32(114).string(t.responseLanguage),0!==t.unknown2&&o.uint32(120).int32(t.unknown2),0!==t.unknown3&&o.uint32(128).int32(t.unknown3),!1!==t.bypassCache&&o.uint32(136).bool(t.bypassCache),o},decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const r={url:"",deviceId:void 0,firstRequest:!1,duration:0,unknown0:0,language:"",forceSourceLang:!1,unknown1:0,translationHelp:[],responseLanguage:"",unknown2:0,unknown3:0,bypassCache:!1};for(;n.pos>>3){case 3:if(26!==t)break;r.url=n.string();continue;case 4:if(34!==t)break;r.deviceId=n.string();continue;case 5:if(40!==t)break;r.firstRequest=n.bool();continue;case 6:if(49!==t)break;r.duration=n.double();continue;case 7:if(56!==t)break;r.unknown0=n.int32();continue;case 8:if(66!==t)break;r.language=n.string();continue;case 9:if(72!==t)break;r.forceSourceLang=n.bool();continue;case 10:if(80!==t)break;r.unknown1=n.int32();continue;case 11:if(90!==t)break;r.translationHelp.push(i.decode(n,n.uint32()));continue;case 14:if(114!==t)break;r.responseLanguage=n.string();continue;case 15:if(120!==t)break;r.unknown2=n.int32();continue;case 16:if(128!==t)break;r.unknown3=n.int32();continue;case 17:if(136!==t)break;r.bypassCache=n.bool();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return r},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",deviceId:b(t.deviceId)?globalThis.String(t.deviceId):void 0,firstRequest:!!b(t.firstRequest)&&globalThis.Boolean(t.firstRequest),duration:b(t.duration)?globalThis.Number(t.duration):0,unknown0:b(t.unknown0)?globalThis.Number(t.unknown0):0,language:b(t.language)?globalThis.String(t.language):"",forceSourceLang:!!b(t.forceSourceLang)&&globalThis.Boolean(t.forceSourceLang),unknown1:b(t.unknown1)?globalThis.Number(t.unknown1):0,translationHelp:globalThis.Array.isArray(t?.translationHelp)?t.translationHelp.map((t=>i.fromJSON(t))):[],responseLanguage:b(t.responseLanguage)?globalThis.String(t.responseLanguage):"",unknown2:b(t.unknown2)?globalThis.Number(t.unknown2):0,unknown3:b(t.unknown3)?globalThis.Number(t.unknown3):0,bypassCache:!!b(t.bypassCache)&&globalThis.Boolean(t.bypassCache)}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),void 0!==t.deviceId&&(e.deviceId=t.deviceId),!1!==t.firstRequest&&(e.firstRequest=t.firstRequest),0!==t.duration&&(e.duration=t.duration),0!==t.unknown0&&(e.unknown0=Math.round(t.unknown0)),""!==t.language&&(e.language=t.language),!1!==t.forceSourceLang&&(e.forceSourceLang=t.forceSourceLang),0!==t.unknown1&&(e.unknown1=Math.round(t.unknown1)),t.translationHelp?.length&&(e.translationHelp=t.translationHelp.map((t=>i.toJSON(t)))),""!==t.responseLanguage&&(e.responseLanguage=t.responseLanguage),0!==t.unknown2&&(e.unknown2=Math.round(t.unknown2)),0!==t.unknown3&&(e.unknown3=Math.round(t.unknown3)),!1!==t.bypassCache&&(e.bypassCache=t.bypassCache),e},create:t=>r.fromPartial(t??{}),fromPartial(t){const e={url:"",deviceId:void 0,firstRequest:!1,duration:0,unknown0:0,language:"",forceSourceLang:!1,unknown1:0,translationHelp:[],responseLanguage:"",unknown2:0,unknown3:0,bypassCache:!1};return e.url=t.url??"",e.deviceId=t.deviceId??void 0,e.firstRequest=t.firstRequest??!1,e.duration=t.duration??0,e.unknown0=t.unknown0??0,e.language=t.language??"",e.forceSourceLang=t.forceSourceLang??!1,e.unknown1=t.unknown1??0,e.translationHelp=t.translationHelp?.map((t=>i.fromPartial(t)))||[],e.responseLanguage=t.responseLanguage??"",e.unknown2=t.unknown2??0,e.unknown3=t.unknown3??0,e.bypassCache=t.bypassCache??!1,e}};const s={encode:(t,o=e.Writer.create())=>(void 0!==t.url&&o.uint32(10).string(t.url),void 0!==t.duration&&o.uint32(17).double(t.duration),0!==t.status&&o.uint32(32).int32(t.status),void 0!==t.remainingTime&&o.uint32(40).int32(t.remainingTime),void 0!==t.unknown0&&o.uint32(48).int32(t.unknown0),""!==t.translationId&&o.uint32(58).string(t.translationId),void 0!==t.language&&o.uint32(66).string(t.language),void 0!==t.message&&o.uint32(74).string(t.message),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={url:void 0,duration:void 0,status:0,remainingTime:void 0,unknown0:void 0,translationId:"",language:void 0,message:void 0};for(;n.pos>>3){case 1:if(10!==t)break;i.url=n.string();continue;case 2:if(17!==t)break;i.duration=n.double();continue;case 4:if(32!==t)break;i.status=n.int32();continue;case 5:if(40!==t)break;i.remainingTime=n.int32();continue;case 6:if(48!==t)break;i.unknown0=n.int32();continue;case 7:if(58!==t)break;i.translationId=n.string();continue;case 8:if(66!==t)break;i.language=n.string();continue;case 9:if(74!==t)break;i.message=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):void 0,duration:b(t.duration)?globalThis.Number(t.duration):void 0,status:b(t.status)?globalThis.Number(t.status):0,remainingTime:b(t.remainingTime)?globalThis.Number(t.remainingTime):void 0,unknown0:b(t.unknown0)?globalThis.Number(t.unknown0):void 0,translationId:b(t.translationId)?globalThis.String(t.translationId):"",language:b(t.language)?globalThis.String(t.language):void 0,message:b(t.message)?globalThis.String(t.message):void 0}),toJSON(t){const e={};return void 0!==t.url&&(e.url=t.url),void 0!==t.duration&&(e.duration=t.duration),0!==t.status&&(e.status=Math.round(t.status)),void 0!==t.remainingTime&&(e.remainingTime=Math.round(t.remainingTime)),void 0!==t.unknown0&&(e.unknown0=Math.round(t.unknown0)),""!==t.translationId&&(e.translationId=t.translationId),void 0!==t.language&&(e.language=t.language),void 0!==t.message&&(e.message=t.message),e},create:t=>s.fromPartial(t??{}),fromPartial(t){const e={url:void 0,duration:void 0,status:0,remainingTime:void 0,unknown0:void 0,translationId:"",language:void 0,message:void 0};return e.url=t.url??void 0,e.duration=t.duration??void 0,e.status=t.status??0,e.remainingTime=t.remainingTime??void 0,e.unknown0=t.unknown0??void 0,e.translationId=t.translationId??"",e.language=t.language??void 0,e.message=t.message??void 0,e}};const l={encode:(t,o=e.Writer.create())=>(""!==t.language&&o.uint32(10).string(t.language),""!==t.url&&o.uint32(18).string(t.url),0!==t.unknown0&&o.uint32(24).int32(t.unknown0),""!==t.translatedLanguage&&o.uint32(34).string(t.translatedLanguage),""!==t.translatedUrl&&o.uint32(42).string(t.translatedUrl),0!==t.unknown1&&o.uint32(48).int32(t.unknown1),0!==t.unknown2&&o.uint32(56).int32(t.unknown2),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={language:"",url:"",unknown0:0,translatedLanguage:"",translatedUrl:"",unknown1:0,unknown2:0};for(;n.pos>>3){case 1:if(10!==t)break;i.language=n.string();continue;case 2:if(18!==t)break;i.url=n.string();continue;case 3:if(24!==t)break;i.unknown0=n.int32();continue;case 4:if(34!==t)break;i.translatedLanguage=n.string();continue;case 5:if(42!==t)break;i.translatedUrl=n.string();continue;case 6:if(48!==t)break;i.unknown1=n.int32();continue;case 7:if(56!==t)break;i.unknown2=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({language:b(t.language)?globalThis.String(t.language):"",url:b(t.url)?globalThis.String(t.url):"",unknown0:b(t.unknown0)?globalThis.Number(t.unknown0):0,translatedLanguage:b(t.translatedLanguage)?globalThis.String(t.translatedLanguage):"",translatedUrl:b(t.translatedUrl)?globalThis.String(t.translatedUrl):"",unknown1:b(t.unknown1)?globalThis.Number(t.unknown1):0,unknown2:b(t.unknown2)?globalThis.Number(t.unknown2):0}),toJSON(t){const e={};return""!==t.language&&(e.language=t.language),""!==t.url&&(e.url=t.url),0!==t.unknown0&&(e.unknown0=Math.round(t.unknown0)),""!==t.translatedLanguage&&(e.translatedLanguage=t.translatedLanguage),""!==t.translatedUrl&&(e.translatedUrl=t.translatedUrl),0!==t.unknown1&&(e.unknown1=Math.round(t.unknown1)),0!==t.unknown2&&(e.unknown2=Math.round(t.unknown2)),e},create:t=>l.fromPartial(t??{}),fromPartial(t){const e={language:"",url:"",unknown0:0,translatedLanguage:"",translatedUrl:"",unknown1:0,unknown2:0};return e.language=t.language??"",e.url=t.url??"",e.unknown0=t.unknown0??0,e.translatedLanguage=t.translatedLanguage??"",e.translatedUrl=t.translatedUrl??"",e.unknown1=t.unknown1??0,e.unknown2=t.unknown2??0,e}};const d={encode:(t,o=e.Writer.create())=>(""!==t.url&&o.uint32(10).string(t.url),""!==t.language&&o.uint32(18).string(t.language),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={url:"",language:""};for(;n.pos>>3){case 1:if(10!==t)break;i.url=n.string();continue;case 2:if(18!==t)break;i.language=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",language:b(t.language)?globalThis.String(t.language):""}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),""!==t.language&&(e.language=t.language),e},create:t=>d.fromPartial(t??{}),fromPartial(t){const e={url:"",language:""};return e.url=t.url??"",e.language=t.language??"",e}};const u={encode(t,o=e.Writer.create()){!1!==t.waiting&&o.uint32(8).bool(t.waiting);for(const e of t.subtitles)l.encode(e,o.uint32(18).fork()).ldelim();return o},decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={waiting:!1,subtitles:[]};for(;n.pos>>3){case 1:if(8!==t)break;i.waiting=n.bool();continue;case 2:if(18!==t)break;i.subtitles.push(l.decode(n,n.uint32()));continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({waiting:!!b(t.waiting)&&globalThis.Boolean(t.waiting),subtitles:globalThis.Array.isArray(t?.subtitles)?t.subtitles.map((t=>l.fromJSON(t))):[]}),toJSON(t){const e={};return!1!==t.waiting&&(e.waiting=t.waiting),t.subtitles?.length&&(e.subtitles=t.subtitles.map((t=>l.toJSON(t)))),e},create:t=>u.fromPartial(t??{}),fromPartial(t){const e={waiting:!1,subtitles:[]};return e.waiting=t.waiting??!1,e.subtitles=t.subtitles?.map((t=>l.fromPartial(t)))||[],e}};const c={encode:(t,o=e.Writer.create())=>(""!==t.url&&o.uint32(10).string(t.url),""!==t.timestamp&&o.uint32(18).string(t.timestamp),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={url:"",timestamp:""};for(;n.pos>>3){case 1:if(10!==t)break;i.url=n.string();continue;case 2:if(18!==t)break;i.timestamp=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",timestamp:b(t.timestamp)?globalThis.String(t.timestamp):""}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),""!==t.timestamp&&(e.timestamp=t.timestamp),e},create:t=>c.fromPartial(t??{}),fromPartial(t){const e={url:"",timestamp:""};return e.url=t.url??"",e.timestamp=t.timestamp??"",e}};const h={encode:(t,o=e.Writer.create())=>(""!==t.url&&o.uint32(10).string(t.url),""!==t.language&&o.uint32(18).string(t.language),""!==t.responseLanguage&&o.uint32(26).string(t.responseLanguage),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={url:"",language:"",responseLanguage:""};for(;n.pos>>3){case 1:if(10!==t)break;i.url=n.string();continue;case 2:if(18!==t)break;i.language=n.string();continue;case 3:if(26!==t)break;i.responseLanguage=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",language:b(t.language)?globalThis.String(t.language):"",responseLanguage:b(t.responseLanguage)?globalThis.String(t.responseLanguage):""}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),""!==t.language&&(e.language=t.language),""!==t.responseLanguage&&(e.responseLanguage=t.responseLanguage),e},create:t=>h.fromPartial(t??{}),fromPartial(t){const e={url:"",language:"",responseLanguage:""};return e.url=t.url??"",e.language=t.language??"",e.responseLanguage=t.responseLanguage??"",e}};const g={encode:(t,o=e.Writer.create())=>(0!==t.interval&&o.uint32(8).int32(t.interval),void 0!==t.translatedInfo&&c.encode(t.translatedInfo,o.uint32(18).fork()).ldelim(),void 0!==t.pingId&&o.uint32(24).int32(t.pingId),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={interval:0,translatedInfo:void 0,pingId:void 0};for(;n.pos>>3){case 1:if(8!==t)break;i.interval=n.int32();continue;case 2:if(18!==t)break;i.translatedInfo=c.decode(n,n.uint32());continue;case 3:if(24!==t)break;i.pingId=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({interval:b(t.interval)?a(t.interval):0,translatedInfo:b(t.translatedInfo)?c.fromJSON(t.translatedInfo):void 0,pingId:b(t.pingId)?globalThis.Number(t.pingId):void 0}),toJSON(t){const e={};return 0!==t.interval&&(e.interval=function(t){switch(t){case n.NO_CONNECTION:return"NO_CONNECTION";case n.TRANSLATING:return"TRANSLATING";case n.STREAMING:return"STREAMING";case n.UNRECOGNIZED:default:return"UNRECOGNIZED"}}(t.interval)),void 0!==t.translatedInfo&&(e.translatedInfo=c.toJSON(t.translatedInfo)),void 0!==t.pingId&&(e.pingId=Math.round(t.pingId)),e},create:t=>g.fromPartial(t??{}),fromPartial(t){const e={interval:0,translatedInfo:void 0,pingId:void 0};return e.interval=t.interval??0,e.translatedInfo=void 0!==t.translatedInfo&&null!==t.translatedInfo?c.fromPartial(t.translatedInfo):void 0,e.pingId=t.pingId??void 0,e}};const p={encode:(t,o=e.Writer.create())=>(0!==t.pingId&&o.uint32(8).int32(t.pingId),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={pingId:0};for(;n.pos>>3){case 1:if(8!==t)break;i.pingId=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({pingId:b(t.pingId)?globalThis.Number(t.pingId):0}),toJSON(t){const e={};return 0!==t.pingId&&(e.pingId=Math.round(t.pingId)),e},create:t=>p.fromPartial(t??{}),fromPartial(t){const e={pingId:0};return e.pingId=t.pingId??0,e}};const m={encode:(t,o=e.Writer.create())=>(""!==t.uuid&&o.uint32(10).string(t.uuid),""!==t.module&&o.uint32(18).string(t.module),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={uuid:"",module:""};for(;n.pos>>3){case 1:if(10!==t)break;i.uuid=n.string();continue;case 2:if(18!==t)break;i.module=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({uuid:b(t.uuid)?globalThis.String(t.uuid):"",module:b(t.module)?globalThis.String(t.module):""}),toJSON(t){const e={};return""!==t.uuid&&(e.uuid=t.uuid),""!==t.module&&(e.module=t.module),e},create:t=>m.fromPartial(t??{}),fromPartial(t){const e={uuid:"",module:""};return e.uuid=t.uuid??"",e.module=t.module??"",e}};const v={encode:(t,o=e.Writer.create())=>(""!==t.secretKey&&o.uint32(10).string(t.secretKey),0!==t.expires&&o.uint32(16).int32(t.expires),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let a=void 0===o?n.len:n.pos+o;const i={secretKey:"",expires:0};for(;n.pos>>3){case 1:if(10!==t)break;i.secretKey=n.string();continue;case 2:if(16!==t)break;i.expires=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return i},fromJSON:t=>({secretKey:b(t.secretKey)?globalThis.String(t.secretKey):"",expires:b(t.expires)?globalThis.Number(t.expires):0}),toJSON(t){const e={};return""!==t.secretKey&&(e.secretKey=t.secretKey),0!==t.expires&&(e.expires=Math.round(t.expires)),e},create:t=>v.fromPartial(t??{}),fromPartial(t){const e={secretKey:"",expires:0};return e.secretKey=t.secretKey??"",e.expires=t.expires??0,e}};function b(t){return null!=t}const f={encodeTranslationRequest:(t,e,o,n,a)=>r.encode({url:t,firstRequest:!0,duration:e,unknown0:1,language:o,forceSourceLang:!1,unknown1:0,translationHelp:a||[],responseLanguage:n,unknown2:0,unknown3:1,bypassCache:!1}).finish(),decodeTranslationResponse:t=>s.decode(new Uint8Array(t)),encodeSubtitlesRequest:(t,e)=>d.encode({url:t,language:e}).finish(),decodeSubtitlesResponse:t=>u.decode(new Uint8Array(t)),encodeStreamPingRequest:t=>p.encode({pingId:t}).finish(),encodeStreamRequest:(t,e,o)=>h.encode({url:t,language:e,responseLanguage:o}).finish(),decodeStreamResponse:t=>g.decode(new Uint8Array(t)),encodeYandexSessionRequest:(t,e)=>m.encode({uuid:t,module:e}).finish(),decodeYandexSessionResponse:t=>v.decode(new Uint8Array(t))},w={host:"api.browser.yandex.ru",hostVOT:"https://vot-api.toil.cc/v1",userAgent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 YaBrowser/24.6.0.0 Safari/537.36",componentVersion:"24.6.1.766",hmac:"bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf",defaultDuration:343},y=new TextEncoder;async function x(t,e,o){const n=await crypto.subtle.importKey("raw",y.encode(e),{name:"HMAC",hash:{name:t}},!1,["sign","verify"]);return await crypto.subtle.sign("HMAC",n,o)}async function S(t){const e=await x("SHA-256",w.hmac,t);return new Uint8Array(e).reduce(((t,e)=>t+e.toString(16).padStart(2,"0")),"")}var k,T;async function L(t,e={headers:{"User-Agent":w.userAgent}}){const{timeout:o=3e3}=e,n=new AbortController,a=setTimeout((()=>n.abort()),o),i=await fetch(t,{...e,signal:n.signal});return clearTimeout(a),i}!function(t){t.custom="custom",t.directlink="custom",t.youtube="youtube",t.piped="piped",t.invidious="invidious",t.vk="vk",t.nine_gag="nine_gag",t.gag="nine_gag",t.twitch="twitch",t.proxitok="proxitok",t.tiktok="tiktok",t.vimeo="vimeo",t.xvideos="xvideos",t.pornhub="pornhub",t.twitter="twitter",t.rumble="rumble",t.facebook="facebook",t.rutube="rutube",t.coub="coub",t.bilibili="bilibili",t.mail_ru="mailru",t.mailru="mailru",t.bitchute="bitchute",t.coursera="coursera",t.udemy="udemy",t.eporner="eporner",t.peertube="peertube",t.dailymotion="dailymotion",t.trovo="trovo",t.yandexdisk="yandexdisk",t.coursehunter="coursehunter",t.ok_ru="okru",t.okru="okru",t.googledrive="googledrive",t.bannedvideo="bannedvideo",t.weverse="weverse",t.newgrounds="newgrounds",t.egghead="egghead",t.youku="youku",t.archive="archive",t.kodik="kodik",t.patreon="patreon",t.reddit="reddit"}(k||(k={})),function(t){t[t.FAILED=0]="FAILED",t[t.FINISHED=1]="FINISHED",t[t.WAITING=2]="WAITING",t[t.LONG_WAITING=3]="LONG_WAITING",t[t.PART_CONTENT=5]="PART_CONTENT",t[t.LONG_WAITING_2=6]="LONG_WAITING_2"}(T||(T={}));const M=["invidious.snopyta.org","yewtu.be","invidious.kavin.rocks","vid.puffyan.us","invidious.namazso.eu","inv.riverside.rocks","yt.artemislena.eu","invidious.flokinet.to","invidious.esmailelbob.xyz","y.com.sb","invidious.nerdvpn.de","inv.vern.cc","invidious.slipfox.xyz","invidio.xamh.de","invidious.dhusch.de"],V=["piped.video","piped.tokhmi.xyz","piped.moomoo.me","piped.syncpundit.io","piped.mha.fi","watch.whatever.social","piped.garudalinux.org","efy.piped.pages.dev","watch.leptons.xyz","piped.lunar.icu","yt.dc09.ru","piped.mint.lgbt","il.ax","piped.privacy.com.de","piped.esmailelbob.xyz","piped.projectsegfau.lt","piped.in.projectsegfau.lt","piped.us.projectsegfau.lt","piped.privacydev.net","piped.palveluntarjoaja.eu","piped.smnz.de","piped.adminforge.de","piped.qdi.fi","piped.hostux.net","piped.chauvet.pro","piped.jotoma.de","piped.pfcd.me","piped.frontendfriendly.xyz"],O=[{additionalData:"mobile",host:k.youtube,url:"https://youtu.be/",match:/^m.youtube.com$/,selector:"shorts-video #player"},{additionalData:"mobile",host:k.youtube,url:"https://youtu.be/",match:/^m.youtube.com$/,selector:".player-container"},{host:k.youtube,url:"https://youtu.be/",match:/^(www.)?youtube(-nocookie|kids)?.com$/,selector:".html5-video-container:not(#inline-player *)"},{host:k.invidious,url:"https://youtu.be/",match:M,selector:"#player"},{host:k.piped,url:"https://youtu.be/",match:V,selector:".shaka-video-container"},{additionalData:"mobile",host:k.vk,url:"https://vk.com/video?z=",match:/^m.vk.(com|ru)$/,selector:"vk-video-player",shadowRoot:!0},{additionalData:"clips",host:k.vk,url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:'div[data-testid="clipcontainer-video"]'},{host:k.vk,url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:".videoplayer_media"},{host:k.nine_gag,url:"https://9gag.com/gag/",match:/^9gag.com$/,selector:".video-post"},{host:k.twitch,url:"https://twitch.tv/",match:[/^m.twitch.tv$/,/^(www.)?twitch.tv$/,/^clips.twitch.tv$/,/^player.twitch.tv$/],selector:".video-ref, main > div > section > div > div > div"},{host:k.proxitok,url:"https://www.tiktok.com/",match:["proxitok.pabloferreiro.es","proxitok.pussthecat.org","tok.habedieeh.re","proxitok.esmailelbob.xyz","proxitok.privacydev.net","tok.artemislena.eu","tok.adminforge.de","tik.hostux.net","tt.vern.cc","cringe.whatever.social","proxitok.lunar.icu","proxitok.privacy.com.de"],selector:".column.has-text-centered"},{host:k.tiktok,url:"https://www.tiktok.com/",match:/^(www.)?tiktok.com$/,selector:null},{host:k.vimeo,url:"https://vimeo.com/",match:/^vimeo.com$/,selector:".player"},{additionalData:"embed",host:k.vimeo,url:"https://player.vimeo.com/",match:/^player.vimeo.com$/,selector:".player"},{host:k.xvideos,url:"https://www.xvideos.com/",match:/^(www.)?(xvideos|xv-ru).com$/,selector:".video-bg-pic"},{host:k.pornhub,url:"https://rt.pornhub.com/view_video.php?viewkey=",match:/^[a-z]+.pornhub.com$/,selector:".mainPlayerDiv > .video-element-wrapper-js > div"},{additionalData:"embed",host:k.pornhub,url:"https://rt.pornhub.com/view_video.php?viewkey=",match:t=>t.host.includes("pornhub.com")&&t.pathname.startsWith("/embed/"),selector:"#player"},{host:k.twitter,url:"https://twitter.com/i/status/",match:/^twitter.com$/,selector:'div[data-testid="videoComponent"] > div:nth-child(1) > div'},{host:k.rumble,url:"https://rumble.com/",match:/^rumble.com$/,selector:"#videoPlayer > .videoPlayer-Rumble-cls > div"},{host:k.facebook,url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/videos/"),selector:'div[role="main"] div[data-pagelet$="video" i]'},{additionalData:"reels",host:k.facebook,url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/reel/"),selector:'div[role="main"]'},{host:k.rutube,url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:".video-player > div > div > div:nth-child(2)"},{additionalData:"embed",host:k.rutube,url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:"#app > div > div"},{host:k.bilibili,url:"https://www.bilibili.com/video/",match:/^(www|m|player).bilibili.com$/,selector:".bpx-player-video-wrap"},{additionalData:"old",host:k.bilibili,url:"https://www.bilibili.com/video/",match:/^(www|m).bilibili.com$/,selector:null},{host:k.mailru,url:"https://my.mail.ru/",match:/^my.mail.ru$/,selector:"#b-video-wrapper"},{host:k.bitchute,url:"https://www.bitchute.com/video/",match:/^(www.)?bitchute.com$/,selector:".video-js"},{host:k.coursera,url:"https://www.coursera.org/",match:/coursera.org$/,selector:".vjs-v6",needExtraData:!0},{host:k.udemy,url:"https://www.udemy.com/",match:/udemy.com$/,selector:'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)',needExtraData:!0},{host:k.eporner,url:"https://www.eporner.com/",match:/^(www.)?eporner.com$/,selector:".vjs-v7"},{host:k.peertube,url:"stub",match:["peertube.1312.media","tube.shanti.cafe","bee-tube.fr","video.sadmin.io","dalek.zone","review.peertube.biz","peervideo.club","tube.la-dina.net","peertube.tmp.rcp.tf","peertube.su"],selector:".vjs-v7"},{host:k.dailymotion,url:"https://dai.ly/",match:/^geo.dailymotion.com$/,selector:".player"},{host:k.trovo,url:"https://trovo.live/s/",match:/^trovo.live$/,selector:".player-video"},{host:k.yandexdisk,url:"https://yadi.sk/i/",match:/^disk.yandex.ru$/,selector:".video-player__player > div:nth-child(1)"},{host:k.coursehunter,url:"https://coursehunter.net/course/",match:/^coursehunter.net$/,selector:"#oframeplayer > pjsdiv:nth-of-type(1)"},{host:k.okru,url:"https://ok.ru/video/",match:/^ok.ru$/,selector:".html5-vpl_vid"},{host:k.googledrive,url:"https://drive.google.com/file/d/",match:/^youtube.googleapis.com$/,selector:".html5-video-container"},{host:k.bannedvideo,url:"https://madmaxworld.tv/watch?id=",match:/^(www.)?banned.video|madmaxworld.tv$/,selector:".vjs-v7",needExtraData:!0},{host:k.weverse,url:"https://weverse.io/",match:/^weverse.io$/,selector:".webplayer-internal-source-wrapper",needExtraData:!0},{host:k.newgrounds,url:"https://www.newgrounds.com/",match:/^(www.)?newgrounds.com$/,selector:".ng-video-player"},{host:k.egghead,url:"https://egghead.io/",match:/^egghead.io$/,selector:".cueplayer-react-video-holder"},{host:k.youku,url:"https://v.youku.com/",match:/^v.youku.com$/,selector:"#ykPlayer"},{host:k.archive,url:"https://archive.org/details/",match:/^archive.org$/,selector:".jw-media"},{host:k.kodik,url:"stub",match:/^kodik.(info|biz|cc)$/,selector:".fp-player",needExtraData:!0},{host:k.patreon,url:"stub",match:/^(www.)?patreon.com$/,selector:'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div',needExtraData:!0},{host:k.reddit,url:"stub",match:/^(www.)?reddit.com$/,selector:"shreddit-player",shadowRoot:!0,needExtraData:!0},{host:k.custom,url:"stub",match:t=>/([^.]+).mp4/.test(t.pathname),rawResult:!0}];class C extends Error{constructor(t){super(t),this.name="VideoHelper",this.message=t}}class A{async getVideoData(t){try{const e=await L(`https://my.mail.ru/+/video/meta/${t}?xemail=&ajax_call=1&func_name=&mna=&mnb=&ext=1&_=${(new Date).getTime()}`);return await e.json()}catch(t){return void console.error("Failed to get mail.ru video info",t.message)}}}class P{API_ORIGIN="https://global.apis.naver.com/weverse/wevweb";API_APP_ID="be4d79eb8fc7bd008ee82c8ec4ff6fd4";API_HMAC_KEY="1b9cb6378d959b45714bec49971ade22e6e24e42";HEADERS={Accept:"application/json, text/plain, */*",Origin:"https://weverse.io",Referer:"https://weverse.io/"};getURLData(){return{appId:this.API_APP_ID,language:"en",os:"WEB",platform:"WEB",wpf:"pc"}}async createHash(t){const e=Date.now(),o=t.substring(0,Math.min(255,t.length))+e,n=await async function(t,e){try{const o=y.encode(e),n=await x("SHA-1",t,o);return btoa(String.fromCharCode(...new Uint8Array(n)))}catch(t){return console.error(t),!1}}(this.API_HMAC_KEY,o);if(!n)throw new C("Failed to get weverse HMAC signature");return{wmsgpad:e.toString(),wmd:n}}async getHashURLParams(t){const e=await this.createHash(t);return new URLSearchParams(e).toString()}async getPostPreview(t){const e=`/post/v1.0/post-${t}/preview?`+new URLSearchParams({fieldSet:"postForPreview",...this.getURLData()}).toString();try{const t=await this.getHashURLParams(e),o=await L(this.API_ORIGIN+e+"&"+t,{headers:this.HEADERS});return await o.json()}catch(e){return console.error(`Failed to get weverse post preview by postId: ${t}`,e.message),!1}}async getVideoInKey(t){const e=`/video/v1.1/vod/${t}/inKey?`+new URLSearchParams({gcc:"RU",...this.getURLData()}).toString();try{const t=await this.getHashURLParams(e),o=await L(this.API_ORIGIN+e+"&"+t,{method:"POST",headers:this.HEADERS});return await o.json()}catch(e){return console.error(`Failed to get weverse InKey by videoId: ${t}`,e.message),!1}}async getVideoInfo(t,e,o){const n=Date.now();try{const a=new URLSearchParams({key:e,sid:o,nonce:n.toString(),devt:"html5_pc",prv:"N",aup:"N",stpb:"N",cpl:"en",env:"prod",lc:"en",adi:JSON.stringify([{adSystem:null}]),adu:"/"}).toString(),i=await L(`https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${t}?`+a,{headers:this.HEADERS});return await i.json()}catch(n){return console.error(`Failed to get weverse video info (infraVideoId: ${t}, inkey: ${e}, serviceId: ${o}`,n.message),!1}}extractVideoInfo(t){return t.find((t=>!1===t.useP2P&&t.source.includes(".mp4")))}async getVideoData(t){const e=await this.getPostPreview(t);if(!e)return;const{videoId:o,serviceId:n,infraVideoId:a}=e.extension.video;if(!(o&&n&&a))return;const i=await this.getVideoInKey(o);if(!i)return;const r=await this.getVideoInfo(a,i.inKey,n);if(!r)return;const s=this.extractVideoInfo(r.videos.list);return s?{url:s.source,duration:s.duration}:void 0}}class E{API_ORIGIN=window.location.origin;async getSecureData(t){try{const e=this.API_ORIGIN+t,o=await L(e,{headers:{"User-Agent":w.userAgent,Origin:this.API_ORIGIN,Referer:this.API_ORIGIN}}),n=await o.text(),[a,i,r]=t.split("/").filter((t=>t)),s=(new DOMParser).parseFromString(n,"text/html"),l=Array.from(s.getElementsByTagName("script")).filter((t=>t.innerHTML.includes(`videoId = "${i}"`)));if(!l.length)throw new C("Failed to find secure script");const d=/'{[^']+}'/.exec(l[0].textContent.trim())?.[0];if(!d)throw new C("Secure json wasn't found in secure script");const u=JSON.parse(d.replaceAll("'",""));return{videoType:a,videoId:i,hash:r,...u}}catch(e){return console.error(`Failed to get kodik secure data by videoPath: ${t}.`,e.message),!1}}async getFtor(t){const{videoType:e,videoId:o,hash:n,d:a,d_sign:i,pd:r,pd_sign:s,ref:l,ref_sign:d}=t;try{const t=await L(this.API_ORIGIN+"/ftor",{method:"POST",headers:{"User-Agent":w.userAgent,Origin:this.API_ORIGIN,Referer:`${this.API_ORIGIN}/${e}/${o}/${n}/360p`},body:new URLSearchParams({d:a,d_sign:i,pd:r,pd_sign:s,ref:decodeURIComponent(l),ref_sign:d,bad_user:"false",cdn_is_working:"true",info:"{}",type:e,hash:n,id:o})});return await t.json()}catch(t){return console.error(`Failed to get kodik video data (type: ${e}, id: ${o}, hash: ${n})`,t.message),!1}}decryptUrl(t){return"https:"+atob(t.replace(/[a-zA-Z]/g,(function(t){const e=t.charCodeAt(0)+13;return String.fromCharCode((t<="Z"?90:122)>=e?e:e-26)})))}async getVideoData(t){const e=await this.getSecureData(t);if(!e)return;const o=await this.getFtor(e);if(!o)return;const n=Object.entries(o.links[o.default.toString()]),a=n.find((([t,e])=>"application/x-mpegURL"===e.type))?.[1];return a?{url:this.decryptUrl(a.src)}:void 0}}class I{async getPosts(t){try{const e=await L(`https://www.patreon.com/api/posts/${t}?json-api-use-default-includes=false`);return await e.json()}catch(e){return console.error(`Failed to get patreon posts by postId: ${t}.`,e.message),!1}}async getVideoData(t){const e=await this.getPosts(t);if(!e)return;const o=e.data.attributes.post_file.url;return o?{url:o}:void 0}}class R{async getVideoData(){const t=document.querySelector("source[type='application/vnd.apple.mpegURL']")?.src?.replaceAll("&","&");if(t)return{url:decodeURIComponent(t)}}}class N{async getVideoInfo(t){try{const e=await L("https://api.banned.video/graphql",{method:"POST",body:JSON.stringify({operationName:"GetVideo",query:"query GetVideo($id: String!) {\n getVideo(id: $id) {\n title\n description: summary\n duration: videoDuration\n videoUrl: directUrl\n isStream: live\n }\n }",variables:{id:t}}),headers:{"User-Agent":"bannedVideoFrontEnd","apollographql-client-name":"banned-web","apollographql-client-version":"1.3","content-type":"application/json"}});return await e.json()}catch(e){return console.error(`Failed to get bannedvideo video info by videoId: ${t}.`,e.message),!1}}async getVideoData(t){const e=await this.getVideoInfo(t);if(!e)return!1;const{videoUrl:o,duration:n,isStream:a,description:i,title:r}=e.data.getVideo;return{url:o,duration:n,isStream:a,title:r,description:i}}}class B{static[k.mailru]=new A;static[k.weverse]=new P;static[k.kodik]=new E;static[k.patreon]=new I;static[k.reddit]=new R;static[k.bannedvideo]=new N}class _ extends Error{constructor(t){super(t),this.name="VideoDataError",this.message=t}}async function D(t,e){const o=new URL(e);switch(t.host){case k.custom:return o.href;case k.piped:case k.invidious:case k.youtube:return"youtu.be"===o.hostname&&(o.search=`?v=${o.pathname.replace("/","")}`,o.pathname="/watch"),/(?:watch|embed|shorts|live)\/([^/]+)/.exec(o.pathname)?.[1]??o.searchParams.get("v");case k.vk:{const t=/^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(o.pathname),e=o.searchParams.get("z"),n=o.searchParams.get("oid"),a=o.searchParams.get("id");return t?t[0].slice(1):e?e.split("/")[0]:n&&a?`video-${Math.abs(parseInt(n))}_${a}`:null}case k.nine_gag:case k.gag:return/gag\/([^/]+)/.exec(o.pathname)?.[1];case k.twitch:{const t=/([^/]+)\/(?:clip)\/([^/]+)/.exec(o.pathname),e=/^clips\.twitch\.tv$/.test(o.hostname);if(/^m\.twitch\.tv$/.test(o.hostname))return/videos\/([^/]+)/.exec(o.href)?.[0]??o.pathname.slice(1);if(/^player\.twitch\.tv$/.test(o.hostname))return`videos/${o.searchParams.get("video")}`;if(e){const t=o.pathname.slice(1),e="embed"===t,n=await L(`https://clips.twitch.tv/${e?o.searchParams.get("clip"):o.pathname.slice(1)}`,{headers:{"User-Agent":"Googlebot/2.1 (+http://www.googlebot.com/bot.html)"}}),a=await n.text(),i=/"url":"https:\/\/www\.twitch\.tv\/([^"]+)"/.exec(a);return i?`${i[1]}/clip/${e?o.searchParams.get("clip"):t}`:null}return t?t[0]:/(?:videos)\/([^/]+)/.exec(o.pathname)?.[0]}case k.proxitok:case k.tiktok:return/([^/]+)\/video\/([^/]+)/.exec(o.pathname)?.[0];case k.vimeo:{const t=o.searchParams.get("app_id"),e=/[^/]+\/[^/]+$/.exec(o.pathname)?.[0]??/[^/]+$/.exec(o.pathname)?.[0];return t?`${e}?app_id=${t}`:e}case k.xvideos:return/[^/]+\/[^/]+$/.exec(o.pathname)?.[0];case k.pornhub:return o.searchParams.get("viewkey")??/embed\/([^/]+)/.exec(o.pathname)?.[1];case k.twitter:return/status\/([^/]+)/.exec(o.pathname)?.[1];case k.rumble:case k.facebook:return o.pathname.slice(1);case k.rutube:return/(?:video|embed)\/([^/]+)/.exec(o.pathname)?.[1];case k.bilibili:{const t=o.searchParams.get("bvid");if(t)return t;let e=/video\/([^/]+)/.exec(o.pathname)?.[1];return e&&null!==o.searchParams.get("p")&&(e+=`/?p=${o.searchParams.get("p")}`),e}case k.mailru:{const t=o.pathname;if(t.startsWith("/v/")||t.startsWith("/mail/"))return t.slice(1);const e=/video\/embed\/([^/]+)/.exec(t)?.[1];if(!e)return null;const n=await B.mailru.getVideoData(e);return n?n.meta.url.replace("//my.mail.ru/",""):null}case k.bitchute:return/(video|embed)\/([^/]+)/.exec(o.pathname)?.[2];case k.coursera:return/learn\/([^/]+)\/lecture\/([^/]+)/.exec(o.pathname)?.[0];case k.eporner:return/video-([^/]+)\/([^/]+)/.exec(o.pathname)?.[0];case k.peertube:return/\/w\/([^/]+)/.exec(o.pathname)?.[0];case k.dailymotion:return"dai.ly"===o.hostname?o.pathname.slice(1):/video\/([^/]+)/.exec(o.pathname)?.[1];case k.trovo:{const t=o.searchParams.get("vid");if(!t)return null;const e=/([^/]+)\/([\d]+)/.exec(o.pathname)?.[0];return e?`${e}?vid=${t}`:null}case k.yandexdisk:return/\/i\/([^/]+)/.exec(o.pathname)?.[1];case k.okru:return/\/video\/(\d+)/.exec(o.pathname)?.[1];case k.googledrive:return/\/file\/d\/([^/]+)/.exec(o.pathname)?.[1];case k.bannedvideo:return o.searchParams.get("id");case k.weverse:return/([^/]+)\/(live|media)\/([^/]+)/.exec(o.pathname)?.[3];case k.newgrounds:return/([^/]+)\/(view)\/([^/]+)/.exec(o.pathname)?.[0];case k.egghead:return o.pathname.slice(1);case k.youku:return/v_show\/id_[\w=]+/.exec(o.pathname)?.[0];case k.archive:return/(details|embed)\/([^/]+)/.exec(o.pathname)?.[2];case k.kodik:return/\/(seria|video)\/([^/]+)\/([^/]+)\/([\d]+)p/.exec(o.pathname)?.[0];case k.patreon:{const t=/posts\/([^/]+)/.exec(o.pathname)?.[1];if(!t)return;return t.replace(/[^\d.]/g,"")}case k.reddit:return/\/r\/(([^/]+)\/([^/]+)\/([^/]+)\/([^/]+))/.exec(o.pathname)?.[1];default:return}}async function F(t){const e=function(t){if(t.startsWith("file://"))return!1;let e;try{e=new URL(t)}catch(e){return console.error(`Invalid URL: ${t}. Have you forgotten https?`),!1}const o=e.hostname,n=t=>t instanceof RegExp?t.test(o):"string"==typeof t?o.includes(t):"function"==typeof t&&t(e);return O.find((t=>(Array.isArray(t.match)?t.match.some(n):n(t.match))&&t.host&&t.url))}(t);if(!e)throw new _(`URL: "${t}" is unknown service`);const o=await D(e,t);if(!o)throw new _(`Entered unsupported link: "${t}"`);if(e.host===k.peertube&&(e.url=new URL(t).origin),e.rawResult)return{url:o,videoId:o,host:e.host,duration:void 0};if(!e.needExtraData)return{url:e.url+o,videoId:o,host:e.host,duration:void 0};const n=await B[e.host].getVideoData(o);if(!n)throw new _(`Failed to get video raw url for ${e.host}`);return{...n,videoId:o,host:e.host}}const q=JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","neededAdditionalExtension":"An additional extension is needed to support this site","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyAccessTokenExpired":"Your entered Udemy Access Token has expired","udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","enterUdemyAccessToken":"Enter Udemy Access Token","VOTUdemyData":"Udemy Data","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase"}'),$={log:(...t)=>console.log("%c[VOT DEBUG]","background: #F2452D; color: #fff; padding: 5px;",...t)},z=$,U=new class{constructor(){this.gmSupport="function"==typeof GM_getValue,z.log(`GM Storage Status: ${this.gmSupport}`)}syncGet(t,e=void 0,o=!1){if(this.gmSupport)return GM_getValue(t,e);let n=window.localStorage.getItem(t);if("udemyData"===t&&"string"==typeof n)try{n=JSON.parse(n)}catch{n=e}const a=n??e;return o?Number(a):a}async get(t,e=void 0,o=!1){return this.gmSupport?await GM_getValue(t,e):Promise.resolve(this.syncGet(t,e,o))}syncSet(t,e){return this.gmSupport?GM_setValue(t,e):("udemyData"===t&&(e=JSON.stringify(e)),window.localStorage.setItem(t,e))}async set(t,e){return this.gmSupport?await GM_setValue(t,e):Promise.resolve(this.syncSet(t,e))}syncDelete(t){return this.gmSupport?GM_deleteValue(t):window.localStorage.removeItem(t)}async delete(t){return this.gmSupport?await GM_deleteValue(t):Promise.resolve(this.syncDelete(t))}syncList(){return this.gmSupport?GM_listValues():["autoTranslate","dontTranslateLanguage","dontTranslateYourLang","autoSetVolumeYandexStyle","showVideoSlider","syncVolume","subtitlesMaxLength","highlightWords","responseLanguage","defaultVolume","udemyData","audioProxy","showPiPButton","locale-version","locale-lang","locale-phrases"]}async list(){return this.gmSupport?await GM_listValues():Promise.resolve(this.syncList())}},H=navigator.language||navigator.userLanguage,j=H?.substr(0,2)?.toLowerCase()??"en";function W(t){return t.toLowerCase().split(/[_;-]/)[0].trim()}function G(){return"pictureInPictureEnabled"in document&&document.pictureInPictureEnabled}function Y(){return"undefined"!=typeof Hls&&Hls?.isSupported()?new Hls({debug:!0,lowLatencyMode:!0,backBufferLength:90}):void 0}const J=new RegExp([/(?:https?|ftp):\/\/\S+/g,/https?:\/\/\S+|www\.\S+/gm,/\b\S+\.\S+/gm,/#[^\s#]+/g,/Auto-generated by YouTube/g,/Provided to YouTube by/g,/Released on/g,/0x[a-fA-F0-9]{40}/g,/[13][a-km-zA-HJ-NP-Z1-9]{25,34}/g,/4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}/g,/Paypal/g].map((t=>t.source)).join("|"));async function K(t,e={}){try{if(t.includes("api.browser.yandex.ru"))throw new Error("Preventing yandex cors");return await fetch(t,e)}catch(o){return z.log("GM_fetch preventing cors by GM_xmlhttpRequest",o.message),new Promise(((o,n)=>{GM_xmlhttpRequest({method:"GET",url:t,responseType:"blob",...e,data:e.body,onload:t=>{o(new Response(t.response,{status:t.status,headers:Object.fromEntries(t.responseHeaders.trim().split("\n").map((t=>{let e=t.split(":");if("set-cookie"!==e?.[0])return[e.shift(),e.join(":")]})).filter((t=>t)))}))},ontimeout:()=>n(new Error("fetch timeout")),onerror:t=>n(t),onabort:()=>n(new Error("fetch abort"))})}))}}const Z=["auto","en","ru","af","am","ar","az","bg","bn","bs","ca","cs","cy","da","de","el","es","et","eu","fa","fi","fr","gl","hi","hr","hu","hy","id","it","ja","jv","kk","km","kn","ko","lo","mk","ml","mn","ms","mt","my","ne","nl","pa","pl","pt","ro","si","sk","sl","sq","sr","su","sv","sw","tr","uk","ur","uz","vi","zh","zu"],Q=new class{lang="en";locale={};gmValues=["locale-phrases","locale-lang","locale-version","locale-lang-override"];constructor(){const t=U.syncGet("locale-lang-override","auto");this.lang=t&&"auto"!==t?t:(navigator.language||navigator.userLanguage)?.substr(0,2)?.toLowerCase()??"en",this.setLocaleFromJsonString(U.syncGet("locale-phrases",""))}reset(){for(let t=0;t{if("object"==typeof t&&t)return t[e]}),t);return void 0===o&&console.warn("[VOT] [localizationProvider] locale",t,"doesn't contain key",e),o}getDefault(t){return this.getFromLocale(q,t)??t}get(t){return this.getFromLocale(this.locale,t)??this.getFromLocale(q,t)??t}};class X extends Error{constructor(t){super(Q.getDefault(t)),this.name="VOTLocalizedError",this.unlocalizedMessage=t,this.localizedMessage=Q.get(t)}}const{rE:tt}={rE:"0.5.0"};class et extends Error{data;constructor(t,e=void 0){super(t),this.data=e,this.name="VOTJSError",this.message=t}}class ot{host;hostVOT;schema;schemaVOT;fetch;fetchOpts;getVideoDataFn;sessions={};requestLang;responseLang;userAgent=w.userAgent;componentVersion=w.componentVersion;paths={videoTranslation:"/video-translation/translate",videoSubtitles:"/video-subtitles/get-subtitles",streamPing:"/stream-translation/ping-stream",streamTranslation:"/stream-translation/translate-stream",createSession:"/session/create"};isCustomFormat(t){return/\.(m3u8|m4(a|v)|mpd)/.exec(t)}headers={"User-Agent":this.userAgent,Accept:"application/x-protobuf","Accept-Language":"en","Content-Type":"application/x-protobuf",Pragma:"no-cache","Cache-Control":"no-cache","Sec-Fetch-Mode":"no-cors"};headersVOT={"User-Agent":`vot.js/${tt}`,"Content-Type":"application/json",Pragma:"no-cache","Cache-Control":"no-cache"};constructor({host:t=w.host,hostVOT:e=w.hostVOT,fetchFn:o=L,fetchOpts:n={},getVideoDataFn:a=F,requestLang:i="en",responseLang:r="ru",headers:s={}}={}){const l=/(http(s)?):\/\//,d=l.exec(t)?.[1];this.host=d?t.replace(`${d}://`,""):t,this.schema=d??"https";const u=l.exec(e)?.[1];this.hostVOT=u?e.replace(`${u}://`,""):e,this.schemaVOT=u??"https",this.fetch=o,this.fetchOpts=n,this.getVideoDataFn=a,this.requestLang=i,this.responseLang=r,this.headers={...this.headers,...s}}getOpts(t,e={}){return{method:"POST",headers:{...this.headers,...e},body:t,...this.fetchOpts}}async request(t,e,o={}){const n=this.getOpts(new Blob([e]),o);try{const e=await this.fetch(`${this.schema}://${this.host}${t}`,n),o=await e.arrayBuffer();return{success:200===e.status,data:o}}catch(t){return console.error("[vot.js]",t.message),{success:!1,data:null}}}async requestVOT(t,e,o={}){const n=this.getOpts(JSON.stringify(e),{...this.headersVOT,...o});try{console.log(`${this.schemaVOT}://${this.hostVOT}${t}`);const e=await this.fetch(`${this.schemaVOT}://${this.hostVOT}${t}`,n),o=await e.json();return{success:200===e.status,data:o}}catch(t){return console.error("[vot.js]",t.message),{success:!1,data:null}}}async getSession(t){const e=Math.floor(Date.now()/1e3),o=this.sessions[t];if(o&&o.timestamp+o.expires>e)return o;const{secretKey:n,expires:a,uuid:i}=await this.createSession(t);return this.sessions[t]={secretKey:n,expires:a,timestamp:e,uuid:i},this.sessions[t]}async translateVideoYAImpl({videoData:t,requestLang:e=this.requestLang,responseLang:o=this.responseLang,translationHelp:n=null,headers:a={}}){const{url:i,duration:r=w.defaultDuration}=t,{secretKey:s,uuid:l}=await this.getSession("video-translation"),d=f.encodeTranslationRequest(i,r,e,o,n),u=await S(d),c=await this.request(this.paths.videoTranslation,d,{"Vtrans-Signature":u,"Sec-Vtrans-Sk":s,"Sec-Vtrans-Token":`${u}:${l}:${this.paths.videoTranslation}:${this.componentVersion}`,...a});if(!c.success)throw new X("requestTranslationFailed");const h=f.decodeTranslationResponse(c.data);switch(h.status){case T.FAILED:throw h?.message?new et("Yandex couldn't translate video",h):new X("requestTranslationFailed");case T.FINISHED:case T.PART_CONTENT:if(!h.url)throw new X("audioNotReceived");return{translated:!0,url:h.url,remainingTime:h.remainingTime??-1};case T.WAITING:return{translated:!1,remainingTime:h.remainingTime};case T.LONG_WAITING:case T.LONG_WAITING_2:return{translated:!1,remainingTime:h.remainingTime??-1};default:throw console.error("[vot.js] Unknown response",h),new et("Unknown response from Yandex",h)}}async translateVideoVOTImpl({url:t,videoId:e,service:o,requestLang:n=this.requestLang,responseLang:a=this.responseLang,headers:i={}}){const r=function(t,e,o){return t===k.patreon?{service:"mux",videoId:new URL(o).pathname.slice(1)}:{service:t,videoId:e}}(o,e,t),s=await this.requestVOT(this.paths.videoTranslation,{provider:"yandex",service:r.service,videoId:r.videoId,fromLang:n,toLang:a,rawVideo:t},i);if(!s.success)throw new X("requestTranslationFailed",s);const l=s.data;switch(l.status){case"failed":throw new et("Yandex couldn't translate video",l);case"success":if(!l.translatedUrl)throw new X("audioNotReceived");return{translated:!0,url:l.translatedUrl,remainingTime:-1};case"waiting":return{translated:!1,remainingTime:l.remainingTime,message:l.message}}}async translateVideo({videoData:t,requestLang:e=this.requestLang,responseLang:o=this.responseLang,translationHelp:n=null,headers:a={}}){const{url:i,videoId:r,host:s}=t;return this.isCustomFormat(i)?await this.translateVideoVOTImpl({url:i,videoId:r,service:s,requestLang:e,responseLang:o,headers:a}):await this.translateVideoYAImpl({videoData:t,requestLang:e,responseLang:o,translationHelp:n,headers:a})}async getSubtitles({videoData:t,requestLang:e=this.requestLang,headers:o={}}){const{url:n}=t;if(this.isCustomFormat(n))throw new et("Unsupported video URL for getting subtitles");const{secretKey:a,uuid:i}=await this.getSession("video-translation"),r=f.encodeSubtitlesRequest(n,e),s=await S(r),l=await this.request(this.paths.videoSubtitles,r,{"Vsubs-Signature":await S(r),"Sec-Vsubs-Sk":a,"Sec-Vsubs-Token":`${s}:${i}:${this.paths.videoSubtitles}:${this.componentVersion}`,...o});if(!l.success)throw new et("Failed to request video subtitles",l);return f.decodeSubtitlesResponse(l.data)}async pingStream({pingId:t,headers:e={}}){const{secretKey:o,uuid:n}=await this.getSession("video-translation"),a=f.encodeStreamPingRequest(t),i=await S(a),r=await this.request(this.paths.streamPing,a,{"Vtrans-Signature":await S(a),"Sec-Vtrans-Sk":o,"Sec-Vtrans-Token":`${i}:${n}:${this.paths.streamPing}:${this.componentVersion}`,...e});if(!r.success)throw new et("Failed to request stream ping",r);return!0}async translateStream({videoData:t,requestLang:e=this.requestLang,responseLang:o=this.responseLang,headers:a={}}){const{url:i}=t;if(this.isCustomFormat(i))throw new et("Unsupported video URL for getting stream translation");const{secretKey:r,uuid:s}=await this.getSession("video-translation"),l=f.encodeStreamRequest(i,e,o),d=await S(l),u=await this.request(this.paths.streamTranslation,l,{"Vtrans-Signature":await S(l),"Sec-Vtrans-Sk":r,"Sec-Vtrans-Token":`${d}:${s}:${this.paths.streamTranslation}:${this.componentVersion}`,...a});if(!u.success)throw new et("Failed to request stream translation",u);const c=f.decodeStreamResponse(u.data),h=c.interval;switch(h){case n.NO_CONNECTION:case n.TRANSLATING:return{translated:!1,interval:h,message:h===n.NO_CONNECTION?"streamNoConnectionToServer":"translationTakeFewMinutes"};case n.STREAMING:return{translated:!0,interval:h,pingId:c.pingId,result:c.translatedInfo};default:throw console.error("[vot.js] Unknown response",c),new et("Unknown response from Yandex",c)}}async createSession(t){const e=function(){let t="";for(let e=0;e<32;e++)t+="0123456789ABCDEF"[Math.floor(16*Math.random())];return t}(),o=f.encodeYandexSessionRequest(e,t),n=await this.request(this.paths.createSession,o,{"Vtrans-Signature":await S(o)});if(!n.success)throw new et("Failed to request create session",n);return{...f.decodeYandexSessionResponse(n.data),uuid:e}}}class nt extends ot{async request(t,e,o={}){const n=this.getOpts(JSON.stringify({headers:{...this.headers,...o},body:Array.from(e)}),{"Content-Type":"application/json"});try{const e=await this.fetch(`${this.schema}://${this.host}${t}`,n),o=await e.arrayBuffer();return{success:200===e.status,data:o}}catch(t){return console.error("[vot.js]",t.message),{success:!1,data:null}}}}const at=["auto","ru","en","zh","ko","lt","lv","ar","fr","it","es","de","ja"],it=["ru","en","kk"];function rt(t){const e=Math.floor(t/3600),o=Math.floor(t%3600/60),n=Math.floor(t%60),a=Math.floor(t%1*1e3);return`${e.toString().padStart(2,"0")}:${o.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")},${a.toString().padStart(3,"0")}`}const st="m3u8-proxy.toil.cc",lt="vot-worker.toil.cc",dt=.15,ut="yandex",ct="yandex",ht={yandex:"https://translate.toil.cc/detect",rustServer:"https://rust-server-531j.onrender.com/detect"},gt={yandex:"https://translate.toil.cc/translate",deepl:"https://translate-deepl.toil.cc/translate"};var pt=o("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"),mt=o.n(pt),vt=o("./node_modules/style-loader/dist/runtime/styleDomAPI.js"),bt=o.n(vt),ft=o("./node_modules/style-loader/dist/runtime/insertBySelector.js"),wt=o.n(ft),yt=o("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js"),xt=o.n(yt),St=o("./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js"),kt=o.n(St),Tt=o("./node_modules/style-loader/dist/runtime/styleTagTransform.js"),Lt=o.n(Tt),Mt=o("./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss"),Vt={};Vt.styleTagTransform=Lt(),Vt.setAttributes=xt(),Vt.insert=wt().bind(null,"head"),Vt.domAPI=bt(),Vt.insertStyleElement=kt();mt()(Mt.A,Vt);Mt.A&&Mt.A.locals&&Mt.A.locals;const Ot="#UNDEFINED";function Ct(t){const e=document.createElement("vot-block");return e.classList.add("vot-icon-button"),e.innerHTML=t,e}function At(t){const e=parseFloat(t.value),o=""===t.min?0:parseFloat(t.min),n=(e-o)/((""===t.max?100:parseFloat(t.max))-o);t.parentElement.setAttribute("style",`--vot-progress: ${n}`)}function Pt(t,e="",o=" ",n=!1){const a=document.createElement("vot-block");a.classList.add("vot-textfield");const i=document.createElement(n?"textarea":"input");i.placeholder=o,i.value=e;const r=document.createElement("span");return r.innerHTML=t,a.appendChild(i),a.appendChild(r),{container:a,input:i,label:r}}function Et(t){const e=document.createElement("vot-block");e.classList.add("vot-dialog-container"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-dialog-backdrop");const n=document.createElement("vot-block");n.classList.add("vot-dialog");const a=document.createElement("vot-block");a.classList.add("vot-dialog-content-wrapper");const i=document.createElement("vot-block");i.classList.add("vot-dialog-header-container");const r=document.createElement("vot-block");r.classList.add("vot-dialog-body-container");const s=document.createElement("vot-block");s.classList.add("vot-dialog-footer-container");const l=document.createElement("vot-block");l.classList.add("vot-dialog-title-container");const d=Ct('');d.classList.add("vot-dialog-close-button"),o.onclick=d.onclick=()=>{e.hidden=!0};const u=document.createElement("vot-block");return u.classList.add("vot-dialog-title"),u.innerHTML=t,e.appendChild(o),e.appendChild(n),n.appendChild(a),a.appendChild(i),a.appendChild(r),a.appendChild(s),i.appendChild(l),i.appendChild(d),l.appendChild(u),{container:e,backdrop:o,dialog:n,contentWrapper:a,headerContainer:i,bodyContainer:r,footerContainer:s,titleContainer:l,closeButton:d,title:u}}function It(t,e,o,n={}){const{onSelectCb:a=function(){},labelElement:i=""}=n;let r=[];const s=document.createElement("vot-block");s.classList.add("vot-select"),i&&s.appendChild(i);const l=document.createElement("vot-block");l.classList.add("vot-select-outer");const d=document.createElement("span");d.classList.add("vot-select-title"),d.innerText=t,void 0===t&&(d.innerText=o.find((t=>!0===t.selected))?.label);const u=document.createElement("vot-block");u.classList.add("vot-select-arrow-icon"),u.innerHTML='',l.append(d,u),l.onclick=()=>{const t=Et(e);t.container.classList.add("vot-dialog-temp"),t.container.hidden=!1,document.documentElement.appendChild(t.container);const n=document.createElement("vot-block");n.classList.add("vot-select-content-list");for(const t of o){const e=document.createElement("vot-block");e.classList.add("vot-select-content-item"),e.innerText=t.label,e.dataset.votSelected=t.selected,e.dataset.votValue=t.value,t.disabled&&(e.inert=!0),e.onclick=async i=>{if(i.target.inert)return;const r=n.childNodes;for(let t of r)t.dataset.votSelected=!1;for(let e of o)e.selected=e.value===t.value;e.dataset.votSelected=!0,d.innerText=t.label,await a(i)},n.appendChild(e)}const i=Pt(Q.get("searchField"));i.input.oninput=t=>{const e=t.target.value.toLowerCase();for(let t=0;t{t.container.remove(),r=[]}},s.append(l);return{container:s,title:d,arrowIcon:u,labelElement:i,setTitle:t=>{d.innerText=t},setSelected:t=>{const e=Array.from(r).filter((t=>!t.inert));for(let o=0;o{o=t}}}const Rt={createHeader:function(t,e=4){const o=document.createElement("vot-block");return o.classList.add("vot-header"),o.classList.add(`vot-header-level-${e}`),o.innerHTML=t,o},createInformation:function(t,e){const o=document.createElement("vot-block");o.classList.add("vot-info");const n=document.createElement("vot-block");n.innerHTML=t;const a=document.createElement("vot-block");return a.innerHTML=e,o.appendChild(n),o.appendChild(a),{container:o,header:n,value:a}},createButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-button"),e.innerHTML=t,e},createTextButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-text-button"),e.innerHTML=t,e},createOutlinedButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-outlined-button"),e.innerHTML=t,e},createIconButton:Ct,createCheckbox:function(t,e=!1){const o=document.createElement("label");o.classList.add("vot-checkbox");const n=document.createElement("input");n.type="checkbox",n.checked=Boolean(e);const a=document.createElement("span");return a.innerHTML=t,o.appendChild(n),o.appendChild(a),{container:o,input:n,label:a}},createSlider:function(t,e=50,o=0,n=100){const a=document.createElement("vot-block");a.classList.add("vot-slider");const i=document.createElement("input");i.type="range",i.min=o,i.max=n,i.value=e;const r=document.createElement("span");return r.innerHTML=t,a.appendChild(i),a.appendChild(r),i.addEventListener("input",(t=>At(t.target))),At(i),{container:a,input:i,label:r}},createTextfield:Pt,createDialog:Et,createVOTButton:function(t){const e=document.createElement("vot-block");e.classList.add("vot-segmented-button");const o=document.createElement("vot-block");o.classList.add("vot-segment"),o.classList.add("vot-translate-button"),o.innerHTML='';const n=document.createElement("vot-block");n.classList.add("vot-separator");const a=document.createElement("vot-block");a.classList.add("vot-segment-only-icon"),a.innerHTML='';const i=document.createElement("vot-block");i.classList.add("vot-separator");const r=document.createElement("vot-block");r.classList.add("vot-segment-only-icon"),r.innerHTML='';const s=document.createElement("span");return s.classList.add("vot-segment-label"),s.innerHTML=t,e.appendChild(o),e.appendChild(n),e.appendChild(a),e.appendChild(i),e.appendChild(r),o.appendChild(s),{container:e,translateButton:o,separator:n,pipButton:a,separator2:i,menuButton:r,label:s}},createVOTMenu:function(t){const e=document.createElement("vot-block");e.classList.add("vot-menu"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-menu-content-wrapper");const n=document.createElement("vot-block");n.classList.add("vot-menu-header-container");const a=document.createElement("vot-block");a.classList.add("vot-menu-body-container");const i=document.createElement("vot-block");i.classList.add("vot-menu-footer-container");const r=document.createElement("vot-block");r.classList.add("vot-menu-title-container");const s=document.createElement("vot-block");return s.classList.add("vot-menu-title"),s.innerHTML=t,e.appendChild(o),o.appendChild(n),o.appendChild(a),o.appendChild(i),n.appendChild(r),r.appendChild(s),{container:e,contentWrapper:o,headerContainer:n,bodyContainer:a,footerContainer:i,titleContainer:r,title:s}},createVOTSelectLabel:function(t){const e=document.createElement("span");return e.classList.add("vot-select-label"),e.innerText=t,e},createVOTSelect:It,createVOTLanguageSelect:function(t){const{fromTitle:e=Ot,fromDialogTitle:o=Ot,fromItems:n=[],fromOnSelectCB:a=null,toTitle:i=Ot,toDialogTitle:r=Ot,toItems:s=[],toOnSelectCB:l=null}=t,d=document.createElement("vot-block");d.classList.add("vot-lang-select");const u=It(e,o,n,{onSelectCb:a}),c=document.createElement("vot-block");c.classList.add("vot-lang-select-icon"),c.innerHTML='';const h=It(i,r,s,{onSelectCb:l});return d.append(u.container,c,h.container),{container:d,fromSelect:u,icon:c,toSelect:h}},updateSlider:At};async function Nt(t,e={}){const o=new AbortController,n=setTimeout((()=>o.abort()),3e3);try{return await fetch(t,{...e,signal:o.signal})}catch(t){return console.error("Fetch timed-out. Error:",t),t}finally{clearTimeout(n)}}const Bt={async translate(t,e){try{const o=await Nt(`${gt.yandex}?${new URLSearchParams({text:t,lang:e})}`);if(o instanceof Error)throw o;const n=await o.json();if(200!==n.code)throw n.message;return n.text[0]}catch(e){return console.error("Error translating text:",e),t}},async detect(t){try{const e=await Nt(`${ht.yandex}?${new URLSearchParams({text:t})}`);if(e instanceof Error)throw e;const o=await e.json();if(200!==o.code)throw o.message;return o.lang??"en"}catch(t){return console.error("Error getting lang from text:",t),"en"}}},_t={async detect(t){try{const e=await fetch(ht.rustServer,{method:"POST",body:t});if(e instanceof Error)throw e;return await e.text()}catch(t){return console.error("Error getting lang from text:",t),"en"}}},Dt={async translate(t,e="auto",o="ru"){try{const n=await Nt(gt.deepl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({text:t,source_lang:e,target_lang:o})});if(n instanceof Error)throw n;const a=await n.json();if(200!==a.code)throw a.message;return a.data}catch(e){return console.error("Error translating text:",e),t}}};const Ft=Object.keys(gt),qt=Object.keys(ht).map((t=>"rustServer"===t?"rust-server":t));async function $t(t,e,o,n){if(!window.location.hostname.includes("m.youtube.com")&&t?.getAudioTrack){const e=t.getAudioTrack(),o=e?.getLanguageInfo();if("und"!==o?.id)return W(o.id.split(".")[0])}const a=e?.captions?.playerCaptionsTracklistRenderer?.captionTracks;if(a?.length){const t=a.find((t=>"asr"===t.kind));if(t&&t.languageCode)return W(t.languageCode)}const i=function(t,e){return`${t} ${e?e.split("\n").filter((t=>!J.test(t))).join(" "):""}`.slice(0,450).replace(/[^\p{L}\s]+|\s+/gu," ").trim()}(o,n);return z.log(`Detecting language text: ${i}`),async function(t){switch(await U.get("detectService",ct)){case"yandex":return await Bt.detect(t);case"rust-server":return await _t.detect(t);default:return"en"}}(i)}function zt(){return/^m\.youtube\.com$/.test(window.location.hostname)}function Ut(){return window.location.pathname.startsWith("/shorts/")?zt()?document.querySelector("#movie_player"):document.querySelector("#shorts-player"):document.querySelector("#movie_player")}function Ht(){const t=Ut();return t?.getPlayerResponse?t?.getPlayerResponse?.call()??null:t?.data?.playerResponse??null}function jt(){const t=Ut();return t?.getVideoData?t?.getVideoData?.call()??null:t?.data?.playerResponse?.videoDetails??null}const Wt={isMobile:zt,getPlayer:Ut,getPlayerResponse:Ht,getPlayerData:jt,getVideoVolume:function(){const t=Ut();return t?.getVolume?t.getVolume.call()/100:1},getSubtitles:function(){const t=Ht();let e=t?.captions?.playerCaptionsTracklistRenderer?.captionTracks??[];return e=e.reduce(((t,e)=>{if("languageCode"in e){const o=e?.languageCode?W(e?.languageCode):void 0,n=e?.url||e?.baseUrl;o&&n&&t.push({source:"youtube",language:o,isAutoGenerated:"asr"===e?.kind,url:`${n.startsWith("http")?n:`${window.location.origin}/${n}`}&fmt=json3`})}return t}),[]),z.log("youtube subtitles:",e),e},getVideoData:async function(){const t=Ut(),e=Ht(),o=jt(),{title:n}=o??{},{shortDescription:a,isLive:i}=e?.videoDetails??{};let r=n?await $t(t,e,n,a):"en";r=at.includes(r)?r:"en";const s={isLive:!!i,title:n,description:a,detectedLanguage:r};return z.log("youtube video data:",s),console.log("[VOT] Detected language: ",s.detectedLanguage),s},setVideoVolume:function(t){const e=Ut();if(e?.setVolume)return e.setVolume(Math.round(100*t)),!0},videoSeek:function(t,e){z.log("videoSeek",e);const o=(Ut()?.getProgressState()?.seekableEnd||t.currentTime)-e;t.currentTime=o},isMuted:function(){const t=Ut();return!!t?.isMuted&&t.isMuted.call()},isMusic:function(){const t=jt().author,e=jt().title.toUpperCase(),o=e.match(/\w+/g),n=document.body.querySelector("ytd-watch-flexy")?.playerData;return[e,document.URL,t,n?.microformat?.playerMicroformatRenderer.category,n?.title].some((t=>t?.toUpperCase().includes("MUSIC")))||document.body.querySelector("#upload-info #channel-name .badge-style-type-verified-artist")||t&&/(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(t)||t&&/(MUSIC|ROCK|SOUNDS|SONGS)/.test(t.toUpperCase())||o?.length&&["🎵","♫","SONG","SONGS","SOUNDTRACK","LYRIC","LYRICS","AMBIENT","MIX","VEVO","CLIP","KARAOKE","OPENING","COVER","COVERED","VOCAL","INSTRUMENTAL","ORCHESTRAL","DUBSTEP","DJ","DNB","BASS","BEAT","ALBUM","PLAYLIST","DUBSTEP","CHILL","RELAX","CLASSIC","CINEMATIC"].some((t=>o.includes(t)))||["OFFICIAL VIDEO","OFFICIAL AUDIO","FEAT.","FT.","LIVE RADIO","DANCE VER","HIP HOP","ROCK N ROLL","HOUR VER","HOURS VER","INTRO THEME"].some((t=>e.includes(t)))||o?.length&&["OP","ED","MV","OST","NCS","BGM","EDM","GMV","AMV","MMD","MAD"].some((t=>o.includes(t)))}};function Gt(t){const e=t.startMs+t.durationMs;return t.tokens.reduce(((o,n,a)=>{const i=t.tokens[a+1];let r;o.length>0&&(r=o[o.length-1]);const s=r?.alignRange?.end??0,l=s+n.text.length;if(n.alignRange={start:s,end:l},o.push(n),i){const t=n.startMs+n.durationMs,a=i.startMs?i.startMs-t:e-t;o.push({text:" ",startMs:t,durationMs:a,alignRange:{start:l,end:l+1}})}return o}),[])}function Yt(t,e){const o=t.text.split(/([\n \t])/).reduce(((t,o)=>{if(o.length){const n=t[t.length-1]??e,a=n?.alignRange?.end??0,i=a+o.length;t.push({text:o,alignRange:{start:a,end:i}})}return t}),[]),n=Math.floor(t.durationMs/o.length),a=t.startMs+t.durationMs;return o.map(((e,i)=>{const r=i===o.length-1,s=t.startMs+n*i;return{...e,startMs:s,durationMs:r?a-s:n}}))}async function Jt(t){const e=new Promise((t=>setTimeout((()=>t({containsTokens:!1,subtitles:[]})),5e3))),o=(async()=>{try{const e=await K(t.url);return await e.json()}catch(t){return console.error("[VOT] Failed to fetch subtitles. Reason:",t),{containsTokens:!1,subtitles:[]}}})();let n=await Promise.race([e,o]);return"youtube"===t.source&&(n=function(t){const e={containsTokens:!1,subtitles:[]};if("object"!=typeof t||!("events"in t)||!Array.isArray(t.events))return console.error("[VOT] Failed to format youtube subtitles",t),e;for(let o=0;ot.utf8.replace(/^( +| +)$/g,""))).join(" ");let a=t.events[o].dDurationMs;t.events[o+1]&&t.events[o].tStartMs+t.events[o].dDurationMs>t.events[o+1].tStartMs&&(a=t.events[o+1].tStartMs-t.events[o].tStartMs),"\n"!==n&&e.subtitles.push({text:n,startMs:t.events[o].tStartMs,durationMs:a})}return e}(n)),n.subtitles=function(t,e){const o=[];let n;for(let a=0;a{setTimeout((()=>{l||(console.error("[VOT] Failed get yandex subtitles. Reason: timeout"),t([]))}),5e3)})),new Promise((e=>{t.getSubtitles({videoData:{host:o,url:n,videoId:i,duration:r},requestLang:a}).then((t=>{console.log("[VOT] Subtitles response: ",t),t.waiting&&(console.error("[VOT] Failed get yandex subtitles"),l=!0,e([]));let o=t.subtitles??[];o=o.reduce(((t,e)=>(e.language&&!t.find((t=>{if("yandex"===t.source&&t.language===e.language&&!t.translatedFromLanguage)return t}))&&t.push({source:"yandex",language:e.language,url:e.url}),e.translatedLanguage&&t.push({source:"yandex",language:e.translatedLanguage,translatedFromLanguage:e.language,url:e.translatedUrl}),t)),[]),l=!0,e(o)}))}))]),u=[...d,...s].sort(((t,e)=>{if(t.source!==e.source)return"yandex"===t.source?-1:1;if(t.language!==e.language&&(t.language===j||e.language===j))return t.language===j?-1:1;if("yandex"===t.source){if(t.translatedFromLanguage!==e.translatedFromLanguage)return t.translatedFromLanguage&&e.translatedFromLanguage?t.translatedFromLanguage===a?-1:1:t.language===e.language?t.translatedFromLanguage?1:-1:t.translatedFromLanguage?-1:1;if(!t.translatedFromLanguage)return t.language===a?-1:1}return"youtube"===t.source&&t.isAutoGenerated!==e.isAutoGenerated?t.isAutoGenerated?1:-1:0}));return console.log("[VOT] subtitles list",u),u}class Zt{dragging=!1;subtitlesContainerRect=null;containerRect=null;offsetX=null;offsetY=null;lastContent=null;highlightWords=!1;subtitles=null;maxLength=300;maxLengthRegexp=/.{1,300}(?:\s|$)/g;constructor(t,e,o){this.site=o,this.video=t,"youtube"===this.site.host&&"mobile"!==this.site.additionalData?this.container=e.parentElement:this.container=e,this.votSubtitlesContainer=document.createElement("vot-block"),this.votSubtitlesContainer.classList.add("vot-subtitles-widget"),this.container.appendChild(this.votSubtitlesContainer),this.onMouseDownBound=this.onMouseDown.bind(this),this.onMouseUpBound=this.onMouseUp.bind(this),this.onMouseMoveBound=this.onMouseMove.bind(this),this.onTimeUpdateBound=this.onTimeUpdate.bind(this),document.addEventListener("mousedown",this.onMouseDownBound),document.addEventListener("mouseup",this.onMouseUpBound),document.addEventListener("mousemove",this.onMouseMoveBound),this.video?.addEventListener("timeupdate",this.onTimeUpdateBound)}release(){this.video?.removeEventListener("timeupdate",this.onTimeUpdateBound),document.removeEventListener("mousedown",this.onMouseDownBound),document.removeEventListener("mouseup",this.onMouseUpBound),document.removeEventListener("mousemove",this.onMouseMoveBound),this.votSubtitlesContainer.remove()}onMouseDown(t){this.votSubtitlesContainer.contains(t.target)&&(this.subtitlesContainerRect=this.votSubtitlesContainer.getBoundingClientRect(),this.containerRect=this.container.getBoundingClientRect(),this.offsetX=t.clientX-this.subtitlesContainerRect.x,this.offsetY=t.clientY-this.subtitlesContainerRect.y,this.dragging=!0)}onMouseUp(){this.dragging=!1}onMouseMove(t){if(this.dragging){t.preventDefault();const e=t.clientX-this.offsetX,o=t.clientY-this.offsetY,n=o>=this.containerRect.top,a=o+this.subtitlesContainerRect.height<=this.containerRect.bottom,i=e>=this.containerRect.left,r=e+this.subtitlesContainerRect.width<=this.containerRect.right;this.votSubtitlesContainer.style.top=n&&a?o-this.containerRect.y+"px":n?this.containerRect.height-this.subtitlesContainerRect.height+"px":"0px",this.votSubtitlesContainer.style.left=i&&r?e-this.containerRect.x+"px":i?this.containerRect.width-this.subtitlesContainerRect.width+"px":"0px"}}onTimeUpdate(){this.update()}setContent(t){t&&this.video?(this.subtitles=t,this.update()):(this.subtitles=null,this.votSubtitlesContainer.innerHTML="")}setMaxLength(t){"number"==typeof t&&t&&(this.maxLength=t,this.maxLengthRegexp=new RegExp(`.{1,${t}}(?:\\s|$)`,"g"),this.update())}setHighlightWords(t){this.highlightWords!==!!t&&(this.highlightWords=!!t,this.update())}update(){if(!this.video)return;let t="",e=this.highlightWords&&this.subtitles?.containsTokens;const o=1e3*this.video.currentTime,n=this.subtitles?.subtitles?.findLast((t=>t.startMsthis.maxLength){let t=[],e=0,n=0,i=0;for(let o=0;othis.maxLength){let r=a.slice(e,n+1);r.at(0)&&" "===r.at(0).text&&(r=r.slice(1)),r.at(-1)&&" "===r.at(-1).text&&(r=r.slice(0,r.length-1)),t.push({startMs:a[e].startMs,durationMs:a[n].startMs+a[n].durationMs-a[e].startMs,tokens:r}),e=o,i=0}n=o}for(let e=0;er||o>i.startMs-100&&r-o<275)?'class="passed"':""}>${i.text}`}}t!==this.lastContent&&(this.lastContent=t,this.votSubtitlesContainer.innerHTML=t?`${t.replace("\\n","
")}
`:"")}}const Qt={getVideoData:async function(){const t=window.course_id??document.querySelector('input[name="course_id"]')?.value,e=window.lessons??await async function(t){const e=await fetch(`https://coursehunter.net/api/v1/course/${t}/lessons`);return await e.json()}(t),o=parseInt(document.querySelector(".lessons-item_active")?.dataset?.index??1),n=e?.[o-1],{file:a,duration:i}=n;return z.log("coursehunter course data:",e),{url:a,duration:i}}};function Xt(){return te()?.player}function te(){return document.querySelector(".vjs-v6")}const ee={getPlayer:te,getPlayerData:Xt,getVideoData:async function(t="en"){let e=null;const o=Xt(),{duration:n}=o?.cache_||{},{courseId:a,tracks:i,sources:r}=o?.options_||{},s=function(t){const e=t?.find((t=>"video/mp4"===t.type));return e?.src}(r),l=await async function(t){const e=await fetch(`https://www.coursera.org/api/onDemandCourses.v1/${t}`),o=await e.json();return o?.elements?.[0]}(a);let d=l?.primaryLanguageCodes?.[0];d=d?W(d):"en",at.includes(d)||(d="en");const u=function(t,e,o){let n=t?.find((t=>W(t.srclang)===e));return n||(n=t?.find((t=>W(t.srclang)===o))||t?.[0]),n?.src}(i,d,t);console.log(`videoURL: ${s}, subtitlesURL: ${u}`),u&&s?e=[{target:"video_file_url",targetUrl:s},{target:"subtitles_file_url",targetUrl:`https://www.coursera.org${u}`}]:s&&!u?(console.warn("[VOT] Subtitles files not found. Using the link only to the video file."),e={url:s}):console.error(`Failed to find subtitlesURL or videoURL. videoURL: ${s}, subtitlesURL: ${u}`);const c={duration:n,detectedLanguage:d,translationHelp:e};return z.log("coursera video data:",c),console.log("[VOT] Detected language: ",c.detectedLanguage),c}},oe="https://www.udemy.com/api-2.0",ne=2592e6;async function ae(t){const e=await fetch(`${oe}/courses/${t}/?`+new URLSearchParams({"fields[course]":"locale",use_remote_version:"true",caching_intent:"true"}));return await e.json()}async function ie(t,e,o){if(!(n=t.expires,n+ne>(new Date).getTime()&&t.accessToken))return void console.error(Q.get("udemyAccessTokenExpired"));var n;const a=`Bearer ${t.accessToken}`,i=await fetch(`${oe}/users/me/subscribed-courses/${e}/lectures/${o}/?`+new URLSearchParams({"fields[lecture]":"asset","fields[asset]":"length,media_sources,captions"}),{headers:{"x-udemy-authorization":a,authorization:a}});return await i.json()}function re(){return le()?.player}function se(){const t=document.querySelector(".ud-app-loader[data-module-id='course-taking']")?.dataset?.moduleArgs;return t?JSON.parse(t):(console.error(Q.get("udemyModuleArgsNotFound")),{})}function le(){return document.querySelector(".vjs-v7")}const de={getPlayer:le,getPlayerData:re,getVideoData:async function(t,e="en"){let o=null;const n=re();z.log("udemyData",t);const a=se();z.log("moduleData: ",a);const i=a.courseId,r=/learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1];z.log(`CourseId: ${i}, lectureId: ${r}`);const s=await ae(i);z.log("courseLang Data:",s);const l=await ie(t,i,r);console.log("lecture Data:",l);let d=s?.locale?.locale;d=d?W(d):"en",at.includes(d)||(d="en");const u=l?.asset?.length||n?.cache_?.duration,c=function(t){const e=t?.find((t=>"video/webm"===t.type||"video/mp4"===t.type));return e?.src}(l?.asset?.media_sources)||function(){const t=le()?.querySelector("video")?.src;return!t?.startsWith("blob:")&&t}(),h=function(t,e,o){let n=t?.find((t=>W(t.locale_id)===e));return n||(n=t?.find((t=>W(t.locale_id)===o))||t?.[0]),n?.url}(l?.asset?.captions,d,e);console.log(`videoURL: ${c}, subtitlesURL: ${h}`),h&&c?o=[{target:"video_file_url",targetUrl:c},{target:"subtitles_file_url",targetUrl:h}]:c&&!h?(console.warn("[VOT] Subtitles files not found. Using the link only to the video file."),o={url:c}):console.error(`Failed to find subtitlesURL or videoURL. videoURL: ${c}, subtitlesURL: ${h}`);const g={duration:u,detectedLanguage:d,translationHelp:o};return z.log("udemy video data:",g),console.log("[VOT] Detected language: ",g.detectedLanguage),g},getModuleData:se,getCourseLang:ae,getLectureData:ie};o("./node_modules/requestidlecallback-polyfill/index.js");class ue{constructor(){this.listeners=new Set}hasListener(t){return this.listeners.has(t)}dispatchToListener(t,...e){try{t(...e)}catch(t){console.error("[VOT]",t)}}addListener(t){if(this.hasListener(t))throw new Error("[VOT] The listener has already been added.");this.listeners.add(t)}removeListener(t){if(!this.hasListener(t))throw new Error("[VOT] The listener has not been added yet.");this.listeners.delete(t)}dispatch(...t){for(const e of Array.from(this.listeners))this.dispatchToListener(e,...t)}}function ce(t){return Array.from(t).flatMap((t=>t instanceof HTMLVideoElement?[t]:t instanceof HTMLElement?Array.from(t.querySelectorAll("video")):t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll("video")):[]))}const he=/advertise|promo|sponsor|banner|commercial|preroll|midroll|postroll|ad-container|sponsored/i;const ge=t.getParser(window.navigator.userAgent).getResult(),pe=[...M,...V],me=["playing","ratechange","play","waiting","pause"];function ve(t,e){return t.map((t=>({label:Q.get("langs")[t]??t.toUpperCase(),value:t,selected:e===t})))}const be={headers:{},fetchFn:K,hostVOT:"https://vot-api.toil.cc/v1",host:lt};class fe{translateFromLang="en";translateToLang=j;timer;videoData="";firstPlay=!0;audio=new Audio;audioContext=new(window.AudioContext||window.webkitAudioContext);gainNode=this.audioContext.createGain();hls=Y();votClient=new nt(be);videoTranslations=[];videoTranslationTTL=7200;downloadTranslationUrl=null;downloadSubtitlesUrl=null;autoRetry;streamPing;volumeOnStart;tempOriginalVolume;tempVolume;firstSyncVolume=!0;subtitlesList=[];subtitlesListVideoId=null;videoLastSrcObject=null;dragging;constructor(t,e,o){z.log("[VideoHandler] add video:",t,"container:",e,this),this.video=t,this.container=e,this.site=o,this.stopTranslationBound=this.stopTranslation.bind(this),this.handleVideoEventBound=this.handleVideoEvent.bind(this),this.changeOpacityOnEventBound=this.changeOpacityOnEvent.bind(this),this.resetTimerBound=this.resetTimer.bind(this),this.init()}async translateVideoImpl(t,e,o,n=null){if(clearTimeout(this.autoRetry),z.log(t,`Translate video (requestLang: ${e}, responseLang: ${o})`),await D(this.site,window.location.href)!==t.videoId)return null;try{const a=await this.votClient.translateVideo({videoData:t,requestLang:e,responseLang:o,translationHelp:n});if(z.log("Translate video result",a),a.translated&&a.remainingTime<1)return z.log("Video translation finished with this data: ",a),a;await this.updateTranslationErrorMsg(a.remainingTime>0?function(t){const e=Math.floor(t/60),o=Math.floor(t%60);return e>=60?Q.get("translationTakeMoreThanHour"):1===e||0===e&&o>0?Q.get("translationTakeAboutMinute"):11!==e&&e%10==1?Q.get("translationTakeApproximatelyMinute2").replace("{0}",e):![12,13,14].includes(e)&&[2,3,4].includes(e%10)?Q.get("translationTakeApproximatelyMinute").replace("{0}",e):Q.get("translationTakeApproximatelyMinutes").replace("{0}",e)}(a.remainingTime):a.message??Q.get("translationTakeFewMinutes"))}catch(t){return console.error("[VOT] Failed to translate video",t),await this.updateTranslationErrorMsg(t.data?.message??t),null}return new Promise((a=>{const i=this.subtitlesList.some((t=>"yandex"===t.source))?2e4:3e4;this.autoRetry=setTimeout((async()=>{const i=await this.translateVideoImpl(t,e,o,n);(!i||i.translated&&i.remainingTime<1)&&a(i)}),i)}))}async translateStreamImpl(t,e,o){if(clearTimeout(this.autoRetry),z.log(t,`Translate stream (requestLang: ${e}, responseLang: ${o})`),await D(this.site,window.location.href)!==t.videoId)return null;try{const n=await this.votClient.translateStream({videoData:t,requestLang:e,responseLang:o});if(z.log("Translate stream result",n),!n.translated&&10===n.interval)return await this.updateTranslationErrorMsg(Q.get("translationTakeFewMinutes")),new Promise((a=>{this.autoRetry=setTimeout((async()=>{const n=await this.translateStreamImpl(t,e,o);n&&!n.translated&&10===n.interval||a(n)}),1e3*n.interval)}));if(n.message)throw z.log(`Stream translation aborted! Message: ${n.message}`),new X("streamNoConnectionToServer");if(!n.result)throw z.log("Failed to find translation result! Data:",n),new X("audioNotReceived");return z.log("Stream translated successfully. Running...",n),this.streamPing=setInterval((async()=>{z.log("Ping stream translation",n.pingId),this.votClient.pingStream({pingId:n.pingId})}),1e3*n.interval),n}catch(t){return console.error("[VOT] Failed to translate stream",t),await this.updateTranslationErrorMsg(t.data?.message??t),null}}async autoTranslate(){if(this.site.host,this.firstPlay&&1===this.data.autoTranslate&&this.videoData.videoId){this.firstPlay=!1;try{await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),this.transformBtn("error","VOTLocalizedError"===t?.name?t.localizedMessage:t)}}}async init(){if(this.initialized)return;const t="uk"===j?1:0,e={autoTranslate:U.get("autoTranslate",0,!0),dontTranslateLanguage:U.get("dontTranslateLanguage",j),dontTranslateYourLang:U.get("dontTranslateYourLang",1,!0),autoSetVolumeYandexStyle:U.get("autoSetVolumeYandexStyle",1,!0),autoVolume:U.get("autoVolume",dt,!0),buttonPos:U.get("buttonPos","default"),showVideoSlider:U.get("showVideoSlider",1,!0),syncVolume:U.get("syncVolume",0,!0),subtitlesMaxLength:U.get("subtitlesMaxLength",300,!0),highlightWords:U.get("highlightWords",0,!0),responseLanguage:U.get("responseLanguage",j),defaultVolume:U.get("defaultVolume",100,!0),udemyData:U.get("udemyData",{accessToken:"",expires:0}),audioProxy:U.get("audioProxy",t,!0),showPiPButton:U.get("showPiPButton",0,!0),translateAPIErrors:U.get("translateAPIErrors",1,!0),translationService:U.get("translationService",ut),detectService:U.get("detectService",ct),m3u8ProxyHost:U.get("m3u8ProxyHost",st),proxyWorkerHost:U.get("proxyWorkerHost",lt),audioBooster:U.get("audioBooster",0,!0)};this.data=Object.fromEntries(await Promise.all(Object.entries(e).map((async([t,e])=>[t,await e])))),console.log("[db] data from db: ",this.data),this.subtitlesWidget=new Zt(this.video,this.container,this.site),this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength),this.subtitlesWidget.setHighlightWords(this.data.highlightWords),this.audio.crossOrigin="anonymous",this.gainNode.connect(this.audioContext.destination),this.audioSource=this.audioContext.createMediaElementSource(this.audio),this.audioSource.connect(this.gainNode),this.initUI(),this.initUIEvents(),this.votClient.host=this.data.proxyWorkerHost;const o=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=o,o?this.votMenu.container.hidden=!0:(this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage??"ru")),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),await this.autoTranslate(),this.translateToLang=this.data.responseLanguage??"ru",this.initExtraEvents(),this.initialized=!0}transformBtn(t="none",e){this.votButton.container.dataset.status=t,this.votButton.container.dataset.translating="error"===t&&e.includes(Q.get("translationTake")),this.votButton.label.innerHTML=e,this.votButton.container.title="error"===t?e:""}initUI(){this.votButton=Rt.createVOTButton(Q.get("translateVideo")),this.data?.buttonPos&&"default"!==this.data?.buttonPos&&this.container.clientWidth&&this.container.clientWidth>550?(this.votButton.container.dataset.direction="column",this.votButton.container.dataset.position=this.data?.buttonPos):(this.votButton.container.dataset.direction="row",this.votButton.container.dataset.position="default"),this.container.appendChild(this.votButton.container),this.votButton.pipButton.hidden=!G()||!this.data?.showPiPButton,this.votButton.separator2.hidden=!G()||!this.data?.showPiPButton,this.votButton.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votMenu=Rt.createVOTMenu(Q.get("VOTSettings")),this.votMenu.container.dataset.position=this.container.clientWidth&&this.container.clientWidth>550?this.data?.buttonPos:"default",this.container.appendChild(this.votMenu.container),this.votDownloadButton=Rt.createIconButton(''),this.votDownloadButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadButton),this.votDownloadSubtitlesButton=Rt.createIconButton(''),this.votDownloadSubtitlesButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadSubtitlesButton),this.votSettingsButton=Rt.createIconButton(''),this.votMenu.headerContainer.appendChild(this.votSettingsButton),this.votTranslationLanguageSelect=Rt.createVOTLanguageSelect({fromTitle:Q.get("langs")[this.video.detectedLanguage],fromDialogTitle:Q.get("videoLanguage"),fromItems:ve(at,this.videoData.detectedLanguage),fromOnSelectCB:async t=>{z.log("[fromOnSelectCB] select from language",t.target.dataset.votValue),this.videoData=await this.getVideoData(),this.setSelectMenuValues(t.target.dataset.votValue,this.videoData.responseLanguage)},toTitle:Q.get("langs")[this.video.responseLanguage],toDialogTitle:Q.get("translationLanguage"),toItems:ve(it,this.videoData.responseLanguage),toOnSelectCB:async t=>{const e=t.target.dataset.votValue;z.log("[toOnSelectCB] select to language",e),this.data.responseLanguage=this.translateToLang=e,await U.set("responseLanguage",this.data.responseLanguage),z.log("Response Language value changed. New value: ",this.data.responseLanguage),this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage)}}),this.votMenu.bodyContainer.appendChild(this.votTranslationLanguageSelect.container),this.votSubtitlesSelect=Rt.createVOTSelect(Q.get("VOTSubtitlesDisabled"),Q.get("VOTSubtitles"),[{label:Q.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1}],{onSelectCb:async t=>{await this.changeSubtitlesLang(t.target.dataset.votValue)},labelElement:Rt.createVOTSelectLabel(Q.get("VOTSubtitles"))}),this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container),this.votVideoVolumeSlider=Rt.createSlider(`${Q.get("VOTVolume")}: ${100*this.getVideoVolume()}%`,100*this.getVideoVolume()),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoVolumeSlider.container),this.votVideoTranslationVolumeSlider=Rt.createSlider(`${Q.get("VOTVolumeTranslation")}: ${this.data?.defaultVolume??100}%`,this.data?.defaultVolume??100,0,this.data.audioBooster?900:100),this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoTranslationVolumeSlider.container),this.votMenu.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votSettingsDialog=Rt.createDialog(Q.get("VOTSettings")),document.documentElement.appendChild(this.votSettingsDialog.container),this.votTranslationHeader=Rt.createHeader(Q.get("translationSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationHeader),this.votAutoTranslateCheckbox=Rt.createCheckbox(Q.get("VOTAutoTranslate"),this.data?.autoTranslate??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoTranslateCheckbox.container),this.votDontTranslateYourLangSelect=Rt.createVOTSelect(Q.get("langs")[U.syncGet("dontTranslateLanguage",j)],Q.get("VOTDontTranslateYourLang"),ve(at,U.syncGet("dontTranslateLanguage",j)),{onSelectCb:async t=>{this.data.dontTranslateLanguage=t.target.dataset.votValue,await U.set("dontTranslateLanguage",this.data.dontTranslateLanguage)},labelElement:Rt.createCheckbox(Q.get("VOTDontTranslateYourLang"),this.data?.dontTranslateYourLang??!0).container}),this.votSettingsDialog.bodyContainer.appendChild(this.votDontTranslateYourLangSelect.container),this.votAutoSetVolumeCheckbox=Rt.createCheckbox(`${Q.get("VOTAutoSetVolume")}`,this.data?.autoSetVolumeYandexStyle??!0),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeCheckbox.container),this.votAutoSetVolumeSlider=Rt.createSlider(`${100*(this.data?.autoVolume??dt)}%`,100*(this.data?.autoVolume??dt),0,100),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeSlider.container),this.votShowVideoSliderCheckbox=Rt.createCheckbox(Q.get("VOTShowVideoSlider"),this.data?.showVideoSlider??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votShowVideoSliderCheckbox.container),this.votAudioBoosterCheckbox=Rt.createCheckbox(Q.get("VOTAudioBooster"),this.data?.audioBooster??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAudioBoosterCheckbox.container),this.votUdemyDataTextfield=Rt.createTextfield(Q.get("VOTUdemyData"),this.data?.udemyData?.accessToken??""),this.votUdemyDataTextfield.container.hidden="udemy"!==this.site.host,this.votSettingsDialog.bodyContainer.appendChild(this.votUdemyDataTextfield.container),this.votSyncVolumeCheckbox=Rt.createCheckbox(Q.get("VOTSyncVolume"),this.data?.syncVolume??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSyncVolumeCheckbox.container),this.votTranslationServiceSelect=Rt.createVOTSelect(U.syncGet("translationService",ut).toUpperCase(),Q.get("VOTTranslationService"),ve(Ft,U.syncGet("translationService",ut)),{onSelectCb:async t=>{this.data.translationService=t.target.dataset.votValue,await U.set("translationService",this.data.translationService)},labelElement:Rt.createCheckbox(Q.get("VOTTranslateAPIErrors"),this.data.translateAPIErrors??!0).container}),this.votTranslationServiceSelect.container.hidden="ru"===Q.lang,this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationServiceSelect.container),this.votDetectServiceSelect=Rt.createVOTSelect(U.syncGet("detectService",ct).toUpperCase(),Q.get("VOTDetectService"),ve(qt,U.syncGet("detectService",ct)),{onSelectCb:async t=>{this.data.detectService=t.target.dataset.votValue,await U.set("detectService",this.data.detectService)},labelElement:Rt.createVOTSelectLabel(Q.get("VOTDetectService"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votDetectServiceSelect.container),this.votSubtitlesHeader=Rt.createHeader(Q.get("subtitlesSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader),this.votSubtitlesMaxLengthSlider=Rt.createSlider(`${Q.get("VOTSubtitlesMaxLength")}: ${this.data?.subtitlesMaxLength??300}`,this.data?.subtitlesMaxLength??300,50,300),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesMaxLengthSlider.container),this.votSubtitlesHighlightWordsCheckbox=Rt.createCheckbox(Q.get("VOTHighlightWords"),this.data?.highlightWords??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHighlightWordsCheckbox.container),this.votProxyHeader=Rt.createHeader(Q.get("proxySettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votProxyHeader),this.votM3u8ProxyHostTextfield=Rt.createTextfield(Q.get("VOTM3u8ProxyHost"),this.data?.m3u8ProxyHost,st),this.votSettingsDialog.bodyContainer.appendChild(this.votM3u8ProxyHostTextfield.container),this.votProxyWorkerHostTextfield=Rt.createTextfield(Q.get("VOTProxyWorkerHost"),this.data?.proxyWorkerHost,lt),this.votProxyWorkerHostTextfield.container.hidden=!1,this.votSettingsDialog.bodyContainer.appendChild(this.votProxyWorkerHostTextfield.container),this.votAudioProxyCheckbox=Rt.createCheckbox(Q.get("VOTAudioProxy"),this.data?.audioProxy??!1),this.votAudioProxyCheckbox.container.hidden=!1,this.votSettingsDialog.bodyContainer.appendChild(this.votAudioProxyCheckbox.container),this.votAboutHeader=Rt.createHeader(Q.get("about")),this.votSettingsDialog.bodyContainer.appendChild(this.votAboutHeader),this.votLanguageSelect=Rt.createVOTSelect(Q.get("langs")[U.syncGet("locale-lang-override","auto")],Q.get("VOTMenuLanguage"),ve(Z,U.syncGet("locale-lang-override","auto")),{onSelectCb:async t=>{await U.set("locale-lang-override",t.target.dataset.votValue)},labelElement:Rt.createVOTSelectLabel(Q.get("VOTMenuLanguage"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votLanguageSelect.container),this.votShowPiPButtonCheckbox=Rt.createCheckbox(Q.get("VOTShowPiPButton"),this.data?.showPiPButton??!1),this.votShowPiPButtonCheckbox.container.hidden=!G(),this.votSettingsDialog.bodyContainer.appendChild(this.votShowPiPButtonCheckbox.container),this.votVersionInfo=Rt.createInformation(`${Q.get("VOTVersion")}:`,`cloudflare ${GM_info.script.version}`),this.votSettingsDialog.bodyContainer.appendChild(this.votVersionInfo.container),this.votAuthorsInfo=Rt.createInformation(`${Q.get("VOTAuthors")}:`,GM_info.script.author),this.votSettingsDialog.bodyContainer.appendChild(this.votAuthorsInfo.container),this.votLoaderInfo=Rt.createInformation(`${Q.get("VOTLoader")}:`,`${GM_info.scriptHandler} v${GM_info.version}`),this.votSettingsDialog.bodyContainer.appendChild(this.votLoaderInfo.container),this.votBrowserInfo=Rt.createInformation(`${Q.get("VOTBrowser")}:`,`${ge.browser.name} ${ge.browser.version} (${ge.os.name} ${ge.os.version})`),this.votSettingsDialog.bodyContainer.appendChild(this.votBrowserInfo.container),this.votResetSettingsButton=Rt.createButton(Q.get("resetSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votResetSettingsButton)}initUIEvents(){this.votButton.translateButton.addEventListener("click",(()=>{(async()=>{if(this.audio.src)return z.log("[click translationBtn] audio.src is not empty"),void this.stopTranslate();if(this.hls.url)return z.log("[click translationBtn] hls is not empty"),void this.stopTranslate();try{if(z.log("[click translationBtn] trying execute translation"),!this.videoData.videoId)throw new X("VOTNoVideoIDFound");"vk"===this.site.host&&"clips"===this.site.additionalData&&(this.videoData=await this.getVideoData()),await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),"VOTLocalizedError"===t?.name?this.transformBtn("error",t.localizedMessage):this.transformBtn("error",t)}})()})),this.votButton.pipButton.addEventListener("click",(()=>{(async()=>{this.video!==document.pictureInPictureElement?await this.video.requestPictureInPicture():await document.exitPictureInPicture()})()})),this.votButton.menuButton.addEventListener("click",(()=>{this.votMenu.container.hidden=!this.votMenu.container.hidden})),this.votButton.container.addEventListener("mousedown",(()=>{this.dragging=!0})),this.container.addEventListener("mouseup",(()=>{this.dragging=!1})),this.container.addEventListener("mousemove",(async t=>{if(this.dragging){t.preventDefault();const e=t.clientX/this.container.clientWidth*100;this.data.buttonPos=this.container.clientWidth&&this.container.clientWidth>550?e<=44?"left":e>=66?"right":"default":"default",this.votButton.container.dataset.direction="default"===this.data.buttonPos?"row":"column",this.votButton.container.dataset.position=this.data.buttonPos,this.votMenu.container.dataset.position=this.data.buttonPos,this.container.clientWidth&&this.container.clientWidth>550&&await U.set("buttonPos",this.data.buttonPos)}})),this.votDownloadButton.addEventListener("click",(()=>{this.downloadTranslationUrl&&window.open(this.downloadTranslationUrl,"_blank").focus()})),this.votDownloadSubtitlesButton.addEventListener("click",(async()=>{const t=function(t,e="srt"){const o=t.subtitles.map(((t,o)=>{const n=t.startMs/1e3,a=(t.startMs+t.durationMs)/1e3;return("srt"===e?`${o+1}\n`:"")+`${rt(n)} --\x3e ${rt(a)}\n${t.text}\n\n`})).join("").trim();return"vtt"===e?`WEBVTT\n\n${o}`:o}(this.yandexSubtitles,"srt"),e=new Blob([t],{type:"text/plain"}),o=URL.createObjectURL(e),n=document.createElement("a");n.href=o,n.download=`subtitles_${this.videoData.videoId}.srt`,n.click(),URL.revokeObjectURL(o)})),this.votSettingsButton.addEventListener("click",(()=>{this.votSettingsDialog.container.hidden=!this.votSettingsDialog.container.hidden,(document.fullscreenElement||document.webkitFullscreenElement)&&(document.webkitExitFullscreen&&document.webkitExitFullscreen(),document.exitFullscreen&&document.exitFullscreen())})),this.votVideoVolumeSlider.input.addEventListener("input",(t=>{const e=Number(t.target.value);this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`,this.setVideoVolume(e/100),this.data.syncVolume&&this.syncVolumeWrapper("video",e)})),this.votVideoTranslationVolumeSlider.input.addEventListener("input",(t=>{(async()=>{this.data.defaultVolume=Number(t.target.value),await U.set("defaultVolume",this.data.defaultVolume),this.votVideoTranslationVolumeSlider.label.querySelector("strong").innerHTML=`${this.data.defaultVolume}%`,this.gainNode.gain.value=this.data.defaultVolume/100,this.data.syncVolume&&(this.syncVolumeWrapper("translation",this.data.defaultVolume),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData&&this.setVideoVolume(this.tempOriginalVolume/100))})()})),this.votAutoTranslateCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoTranslate=Number(t.target.checked),await U.set("autoTranslate",this.data.autoTranslate),await this.autoTranslate(),z.log("autoTranslate value changed. New value: ",this.data.autoTranslate)})()})),this.votDontTranslateYourLangSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.dontTranslateYourLang=Number(t.target.checked),await U.set("dontTranslateYourLang",this.data.dontTranslateYourLang),z.log("dontTranslateYourLang value changed. New value: ",this.data.dontTranslateYourLang)})()})),this.votAutoSetVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoSetVolumeYandexStyle=Number(t.target.checked),await U.set("autoSetVolumeYandexStyle",this.data.autoSetVolumeYandexStyle),z.log("autoSetVolumeYandexStyle value changed. New value: ",this.data.autoSetVolumeYandexStyle)})()})),this.votAutoSetVolumeSlider.input.addEventListener("input",(t=>{(async()=>{const e=Number(t.target.value);this.data.autoVolume=(e/100).toFixed(2),await U.set("autoVolume",this.data.autoVolume),this.votAutoSetVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`})()})),this.votShowVideoSliderCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showVideoSlider=Number(t.target.checked),await U.set("showVideoSlider",this.data.showVideoSlider),z.log("showVideoSlider value changed. New value: ",this.data.showVideoSlider),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status})()})),this.votAudioBoosterCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioBooster=Number(t.target.checked),await U.set("audioBooster",this.data.audioBooster),z.log("audioBooster value changed. New value: ",this.data.audioBooster);const e=this.votVideoTranslationVolumeSlider.input.value;this.votVideoTranslationVolumeSlider.input.max=this.data.audioBooster?900:100,this.data.audioBooster||(this.votVideoTranslationVolumeSlider.input.value=e>100?100:e,this.votVideoTranslationVolumeSlider.input.dispatchEvent(new Event("input")))})()})),this.votUdemyDataTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.udemyData={accessToken:t.target.value,expires:(new Date).getTime()},await U.set("udemyData",this.data.udemyData),z.log("udemyData value changed. New value: ",this.data.udemyData),window.location.reload()})()})),this.votSyncVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.syncVolume=Number(t.target.checked),await U.set("syncVolume",this.data.syncVolume),z.log("syncVolume value changed. New value: ",this.data.syncVolume)})()})),this.votTranslationServiceSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.translateAPIErrors=Number(t.target.checked),await U.set("translateAPIErrors",this.data.translateAPIErrors),z.log("translateAPIErrors value changed. New value: ",this.data.translateAPIErrors)})()})),this.votSubtitlesMaxLengthSlider.input.addEventListener("input",(t=>{(async()=>{this.data.subtitlesMaxLength=Number(t.target.value),await U.set("subtitlesMaxLength",this.data.subtitlesMaxLength),this.votSubtitlesMaxLengthSlider.label.querySelector("strong").innerHTML=`${this.data.subtitlesMaxLength}`,this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength)})()})),this.votSubtitlesHighlightWordsCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.highlightWords=Number(t.target.checked),await U.set("highlightWords",this.data.highlightWords),z.log("highlightWords value changed. New value: ",this.data.highlightWords),this.subtitlesWidget.setHighlightWords(this.data.highlightWords)})()})),this.votShowPiPButtonCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showPiPButton=Number(t.target.checked),await U.set("showPiPButton",this.data.showPiPButton),z.log("showPiPButton value changed. New value: ",this.data.showPiPButton),this.votButton.pipButton.hidden=!G()||!this.data.showPiPButton,this.votButton.separator2.hidden=!G()||!this.data.showPiPButton})()})),this.votM3u8ProxyHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.m3u8ProxyHost=t.target.value||st,await U.set("m3u8ProxyHost",this.data.m3u8ProxyHost),z.log("m3u8ProxyHost value changed. New value: ",this.data.m3u8ProxyHost)})()})),this.votProxyWorkerHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.proxyWorkerHost=t.target.value||lt,await U.set("proxyWorkerHost",this.data.proxyWorkerHost),z.log("proxyWorkerHost value changed. New value: ",this.data.proxyWorkerHost),window.location.reload()})()})),this.votAudioProxyCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioProxy=Number(t.target.checked),await U.set("audioProxy",this.data.audioProxy),z.log("audioProxy value changed. New value: ",this.data.audioProxy)})()})),this.votResetSettingsButton.addEventListener("click",(()=>{(async()=>{Q.reset();const t=await U.list();for(let e=0;e{this.extraEvents.push({element:t,event:e,handler:o}),t.addEventListener(e,o)},e=(e,o,n)=>{for(const a of o)t(e,a,n)};if(this.resizeObserver=new ResizeObserver((t=>{for(let e=0;e550;this.votButton.container.dataset.position=this.votMenu.container.dataset.position=e?this.data?.buttonPos:"default",this.votButton.container.dataset.direction=this.data?.buttonPos&&"default"!==this.data?.buttonPos&&e?"column":"row"})),this.resizeObserver.observe(this.video),this.votMenu.container.setAttribute("style",`--vot-container-height: ${this.video.getBoundingClientRect().height}px`),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData){this.syncVolumeObserver=new MutationObserver((t=>{if(this.audio.src&&this.data.syncVolume)for(let e=0;e{const e=t.target,o=this.votButton.container,n=this.votMenu.container,a=this.container,i=this.votSettingsDialog.container,r=document.querySelector(".vot-dialog-temp"),s=o.contains(e),l=n.contains(e),d=a.contains(e),u=i.contains(e),c=r?.contains(e)??!1;z.log(`[document click] ${s} ${l} ${d} ${u} ${c}`),s||l||u||c||(d||this.logout(0),this.votMenu.container.hidden=!0)})),o="pornhub"===this.site.host?"embed"===this.site.additionalData?document.querySelector("#player"):this.container.querySelector(".video-element-wrapper-js > div"):"twitter"===this.site.host?document.querySelector('div[data-testid="videoPlayer"]'):"yandexdisk"===this.site.host?document.querySelector(".video-player__player"):"reddit"===this.site.host?document.querySelector("shreddit-post"):this.container,o&&e(o,["mousemove","mouseout"],this.resetTimerBound),t(this.votButton.container,"mousemove",this.changeOpacityOnEventBound),t(this.votMenu.container,"mousemove",this.changeOpacityOnEventBound),e(document,["touchstart","touchmove","touchend"],this.changeOpacityOnEventBound),t(this.votButton.container,"mousedown",(t=>{t.stopImmediatePropagation()})),t(this.votMenu.container,"mousedown",(t=>{t.stopImmediatePropagation()})),"youtube"===this.site.host&&(this.container.draggable=!1),"googledrive"===this.site.host&&(this.container.style.height="100%"),t(this.video,"canplay",(async()=>{"rutube"===this.site.host&&this.video.src||await D(this.site,window.location.href)!==this.videoData.videoId&&(await this.handleSrcChanged(),z.log("lipsync mode is loadeddata"),await this.autoTranslate())})),t(this.video,"emptied",(async()=>{this.video.src&&await D(this.site,window.location.href)===this.videoData.videoId||(z.log("lipsync mode is emptied"),this.videoData="",this.stopTranslation())})),["rutube","ok"].includes(this.site.host)||t(this.video,"volumechange",(()=>{this.syncVideoVolumeSlider()}))}logout(t){this.votMenu.container.hidden&&(this.votButton.container.style.opacity=t)}resetTimer(){clearTimeout(this.timer),this.logout(1),this.timer=setTimeout((()=>{this.logout(0)}),1e3)}changeOpacityOnEvent(t){clearTimeout(this.timer),this.logout(1),t.stopPropagation()}async changeSubtitlesLang(t){if(z.log("[onchange] subtitles",t),this.votSubtitlesSelect.setSelected(t),"disabled"===t)this.votSubtitlesSelect.setTitle(Q.get("VOTSubtitlesDisabled")),this.subtitlesWidget.setContent(null),this.votDownloadSubtitlesButton.hidden=!0,this.yandexSubtitles=null;else{const e=await Jt(this.subtitlesList.at(parseInt(t)));this.subtitlesWidget.setContent(e),this.votDownloadSubtitlesButton.hidden=!1,this.yandexSubtitles=e}}async updateSubtitlesLangSelect(){const t=[{label:Q.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1},...this.subtitlesList.map(((t,e)=>({label:(Q.get("langs")[t.language]??t.language.toUpperCase())+(t.translatedFromLanguage?` ${Q.get("VOTTranslatedFrom")} ${Q.get("langs")[t.translatedFromLanguage]??t.translatedFromLanguage.toUpperCase()}`:"")+("yandex"!==t.source?` ${t.source}`:"")+(t.isAutoGenerated?` (${Q.get("VOTAutogenerated")})`:""),value:e,selected:!1,disabled:!1})))];this.votSubtitlesSelect.updateItems(t),await this.changeSubtitlesLang(t[0].value)}async updateSubtitles(){if(await this.changeSubtitlesLang("disabled"),!this.videoData.videoId)return console.error(`[VOT] ${Q.getDefault("VOTNoVideoIDFound")}`),this.subtitlesList=[],this.subtitlesListVideoId=null,this.votButton.container.hidden=!0,void await this.updateSubtitlesLangSelect();this.votButton.container.hidden=!1,this.subtitlesListVideoId!==this.videoData.videoId&&(this.subtitlesList=await Kt(this.votClient,this.videoData),this.subtitlesList?this.subtitlesListVideoId=this.videoData.videoId:await this.changeSubtitlesLang("disabled"),await this.updateSubtitlesLangSelect())}getVideoVolume(){let t=this.video?.volume;return["youtube","googledrive"].includes(this.site.host)&&(t=Wt.getVideoVolume()??t),t}setVideoVolume(t){if(["youtube","googledrive"].includes(this.site.host)){if(Wt.setVideoVolume(t))return}this.video.volume=t}isMuted(){return["youtube","googledrive"].includes(this.site.host)?Wt.isMuted():this.video?.muted}syncVideoVolumeSlider(){const t=this.isMuted()?0:100*this.getVideoVolume(),e=Math.round(t);this.votVideoVolumeSlider.input.value=e,this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`,Rt.updateSlider(this.votVideoVolumeSlider.input),1===this.data.syncVolume&&(this.tempOriginalVolume=Number(e))}setSelectMenuValues(t,e){this.votTranslationLanguageSelect.fromSelect.setTitle(Q.get("langs")[t]),this.votTranslationLanguageSelect.toSelect.setTitle(Q.get("langs")[e]),this.votTranslationLanguageSelect.fromSelect.setSelected(t),this.votTranslationLanguageSelect.toSelect.setSelected(e),console.log(`[VOT] Set translation from ${t} to ${e}`),this.videoData.detectedLanguage=t,this.videoData.responseLanguage=e}syncVolumeWrapper(t,e){const o="translation"===t?this.votVideoVolumeSlider:this.votVideoTranslationVolumeSlider,n=Number(o.input.value),a=function(t,e,o,n){let a=e;return e>n?(a=o+(e-n),a=a>100?100:Math.max(a,0),t.volume=a/100):e100?100:Math.max(a,0),t.volume=a/100),a}("translation"===t?this.video:this.audio,e,n,"translation"===t?this.tempVolume:this.tempOriginalVolume);o.input.value=a,o.label.querySelector("strong").innerHTML=`${a}%`,Rt.updateSlider(o.input),this.tempOriginalVolume="translation"===t?a:e,this.tempVolume="translation"===t?e:a}async getVideoData(){const{duration:t,url:e,videoId:o,host:n}=await F(window.location.href),a={translationHelp:null,isStream:!1,duration:this.video?.duration||t||w.defaultDuration,videoId:o,url:e,host:n,detectedLanguage:this.translateFromLang,responseLanguage:this.translateToLang};if("youtube"===this.site.host){const t=await Wt.getVideoData();a.isStream=t.isLive,t.title&&(a.detectedLanguage=t.detectedLanguage)}else if(["rutube","ok.ru","mail_ru"].includes(this.site.host))a.detectedLanguage="ru";else if("youku"===this.site.host)a.detectedLanguage="zh";else if("vk"===this.site.host){const t=document.getElementsByTagName("track")?.[0]?.srclang;a.detectedLanguage=t||"auto"}else if("coursera"===this.site.host){const t=await ee.getVideoData(this.translateToLang);a.duration=t.duration||a.duration,a.detectedLanguage=t.detectedLanguage,a.translationHelp=t.translationHelp}else if("coursehunter"===this.site.host){const t=await Qt.getVideoData();a.translationHelp={url:t.url},a.duration=t.duration||a.duration}else if("weverse"===this.site.host)a.detectedLanguage="ko";else if("udemy"===this.site.host){const t=await de.getVideoData(this.data.udemyData,this.translateToLang);a.duration=t.duration||a.duration,a.detectedLanguage=t.detectedLanguage,a.translationHelp=t.translationHelp}else["bilibili","piped","invidious","bitchute","rumble","peertube","dailymotion","trovo","yandexdisk","coursehunter","archive","directlink"].includes(this.site.host)&&(a.detectedLanguage="auto");return a}videoValidator(){if(["youtube","ok.ru","vk"].includes(this.site.host)&&(z.log("VideoValidator videoData: ",this.videoData),1===this.data.dontTranslateYourLang&&this.videoData.detectedLanguage===this.data.dontTranslateLanguage))throw new X("VOTDisableFromYourLang");if(!this.videoData.isStream&&this.videoData.duration>14400)throw new X("VOTVideoIsTooLong");return!0}lipSync(t=!1){if(z.log("lipsync video",this.video),this.video)if(this.audio.currentTime=this.video.currentTime,this.audio.playbackRate=this.video.playbackRate,t)if("play"!=t)["pause","stop","waiting"].includes(t)&&(z.log(`lipsync mode is ${t}`),this.audio.pause()),"playing"==t&&(z.log("lipsync mode is playing"),this.audio.play());else{z.log("lipsync mode is play");const t=this.audio.play();void 0!==t&&t.catch((t=>{if(console.error("[VOT]",t),"NotAllowedError"===t.name)throw this.transformBtn("error",Q.get("grantPermissionToAutoPlay")),new X("grantPermissionToAutoPlay");if("NotSupportedError"===t.name)throw this.transformBtn("error",pe.includes(window.location.hostname)?Q.get("neededAdditionalExtension"):Q.get("audioFormatNotSupported")),pe.includes(window.location.hostname)?new X("neededAdditionalExtension"):new X("audioFormatNotSupported")}))}else z.log("lipsync mode is not set")}handleVideoEvent(t){z.log(`video ${t.type}`),this.lipSync(t.type)}stopTranslate(){for(const t of me)this.video.removeEventListener(t,this.handleVideoEventBound);this.audio.pause(),this.audio.src="",this.audio.removeAttribute("src"),this.votVideoVolumeSlider.container.hidden=!0,this.votVideoTranslationVolumeSlider.container.hidden=!0,this.votDownloadButton.hidden=!0,this.downloadTranslationUrl=null,this.transformBtn("none",Q.get("translateVideo")),z.log(`Volume on start: ${this.volumeOnStart}`),this.volumeOnStart&&this.setVideoVolume(this.volumeOnStart),this.volumeOnStart="",clearInterval(this.streamPing),clearTimeout(this.autoRetry),this.hls?.destroy(),this.hls=Y(),this.firstSyncVolume=!0}async translateExecutor(t){z.log("Run translateFunc",t),await this.translateFunc(t,this.videoData.isStream,this.videoData.detectedLanguage,this.videoData.responseLanguage,this.videoData.translationHelp)}async updateTranslationErrorMsg(t){const e=Q.get("translationTake"),o=Q.get("VOTTranslatingError"),n=Q.lang;if("VOTLocalizedError"===t?.name)this.transformBtn("error",t.localizedMessage);else if(1!==this.data.translateAPIErrors||t.includes(e)||"ru"===n)this.transformBtn("error",t);else{const e=await async function(t,e="",o="ru"){switch(await U.get("translationService",ut)){case"yandex":{const n=e&&o?`${e}-${o}`:o;return await Bt.translate(t,n)}case"deepl":return await Dt.translate(t,e,o);default:return t}}(t,"ru",n);this.transformBtn("error",o),this.transformBtn("error",e)}}afterUpdateTranslation(t){this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,1===this.data.autoSetVolumeYandexStyle&&(this.votVideoVolumeSlider.input.value=100*this.data.autoVolume,this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=100*this.data.autoVolume+"%",Rt.updateSlider(this.votVideoVolumeSlider.input)),this.votDownloadButton.hidden=!1,this.downloadTranslationUrl=t}updateTranslation(t){if(this.audio.src=t,1===this.data.audioProxy&&t.startsWith("https://vtrans.s3-private.mds.yandex.net/tts/prod/")){const e=t.replace("https://vtrans.s3-private.mds.yandex.net/tts/prod/",""),o=`https://${this.data.proxyWorkerHost}/video-translation/audio-proxy/${e}`;console.log(`[VOT] Audio proxied via ${o}`),this.audio.src=o}if(this.setupAudioSettings(),"twitter"===this.site.host)document.querySelector('button[data-testid="app-bar-back"][role="button"]').addEventListener("click",this.stopTranslationBound);this.video&&!this.video.paused&&this.lipSync("play");for(const t of me)this.video.addEventListener(t,this.handleVideoEventBound);this.transformBtn("success",Q.get("disableTranslate")),this.afterUpdateTranslation(t)}async translateFunc(t,e,o,n,a){if(console.log("[VOT] Video Data: ",this.videoData),z.log("Run videoValidator"),this.videoValidator(),e){let t=await this.translateStreamImpl(this.videoData,o,n);if(!t)return void z.log("Skip translation");this.transformBtn("success",Q.get("disableTranslate"));const e=`https://${this.data.m3u8ProxyHost}/?all=yes&origin=${encodeURIComponent("https://strm.yandex.ru")}&referer=${encodeURIComponent("https://strm.yandex.ru")}&url=${encodeURIComponent(t.result.url)}`;if(this.hls)this.setupHLS(e);else{if(!this.audio.canPlayType("application/vnd.apple.mpegurl"))throw new X("audioFormatNotSupported");this.audio.src=e}if("youtube"===this.site.host&&Wt.videoSeek(this.video,10),this.setupAudioSettings(),!this.video.src&&!this.video.currentSrc&&!this.video.srcObject)return this.stopTranslation();this.video&&!this.video.paused&&this.lipSync("play");for(const t of me)this.video.addEventListener(t,this.handleVideoEventBound);return this.afterUpdateTranslation(e)}if(["udemy","coursera"].includes(this.site.host)&&!a)throw new X("VOTTranslationHelpNull");const i=this.videoTranslations.find((e=>e.videoId===t&&e.expires>Date.now()/1e3&&e.from===o&&e.to===n));if(i)return this.updateTranslation(i.url),void z.log("[translateFunc] Cached translation was received");let r=await this.translateVideoImpl(this.videoData,o,n,a);z.log("[translateRes]",r),r?(this.updateTranslation(r.url),this.subtitlesList.some((t=>"yandex"===t.source&&t.translatedFromLanguage===this.videoData.detectedLanguage&&t.language===this.videoData.responseLanguage))||(this.subtitlesList=await Kt(this.votClient,this.videoData),await this.updateSubtitlesLangSelect()),this.videoTranslations.push({videoId:t,from:o,to:n,url:r.url,expires:Date.now()/1e3+this.videoTranslationTTL})):z.log("Skip translation")}setupHLS(t){this.hls.on(Hls.Events.MEDIA_ATTACHED,(function(){z.log("audio and hls.js are now bound together !")})),this.hls.on(Hls.Events.MANIFEST_PARSED,(function(t){z.log("manifest loaded, found "+t?.levels?.length+" quality level")})),this.hls.loadSource(t),this.hls.attachMedia(this.audio),this.hls.on(Hls.Events.ERROR,(function(t){if(t.fatal)switch(t.type){case Hls.ErrorTypes.MEDIA_ERROR:console.log("fatal media error encountered, try to recover"),this.hls.recoverMediaError();break;case Hls.ErrorTypes.NETWORK_ERROR:console.error("fatal network error encountered",t);break;default:this.hls.destroy()}})),z.log(this.hls)}setupAudioSettings(){this.volumeOnStart=this.getVideoVolume(),"number"==typeof this.data.defaultVolume&&(this.gainNode.gain.value=this.data.defaultVolume/100),"number"==typeof this.data.autoSetVolumeYandexStyle&&this.data.autoSetVolumeYandexStyle&&this.setVideoVolume(this.data.autoVolume)}stopTranslation(){this.stopTranslate(),this.syncVideoVolumeSlider()}async handleSrcChanged(){z.log("[VideoHandler] src changed",this),this.firstPlay=!0,this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.videoData.responseLanguage);const t=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=t,t&&(this.votMenu.container.hidden=t),this.site.selector||(this.container=this.video.parentElement),this.container.contains(this.votButton.container)||(this.container.appendChild(this.votButton.container),this.container.appendChild(this.votMenu.container)),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),this.translateToLang=this.data.responseLanguage??"ru"}async release(){z.log("[VideoHandler] release"),this.initialized=!1,this.releaseExtraEvents(),this.subtitlesWidget.release(),this.votButton.container.remove(),this.votMenu.container.remove()}}const we=new class{constructor(){this.videoCache=new Set,this.onVideoAdded=new ue,this.onVideoRemoved=new ue,this.observer=new MutationObserver((t=>{window.requestIdleCallback((()=>{for(let e=0;e{for(let e=0;e{o.has(t)||o.add(t)})),t.querySelectorAll("*").forEach((t=>{t.shadowRoot&&n(t.shadowRoot)})))}for(let t=0;t=3}(t)?requestAnimationFrame(o):e(t)}()}(t,(t=>{this.handleVideoAdded(t)}))}handleVideoAdded=t=>{this.onVideoAdded.dispatch(t)};handleVideoRemoved=t=>{document.contains(t)||(this.videoCache.delete(t),this.onVideoRemoved.dispatch(t))}},ye=new WeakMap;function xe(t,e){if(t.shadowRoot){let o=t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.shadowRoot.contains(e))):e.parentElement;return o&&o.shadowRoot?o.parentElement:o}const o=ge.browser.version.split(".")[0];if(t.selector?.includes(":not")&&t.selector?.includes("*")&&o&&("Chrome"===ge.browser.name&&Number(o)<88||"Firefox"===ge.browser.name&&Number(o)<84)){const o=t.selector.split(" *")[0];return o?Array.from(document.querySelectorAll(o)).find((t=>t.contains(e))):e.parentElement}return t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.contains(e))):e.parentElement}(async function(){z.log("Loading extension..."),await Q.update(),z.log(`Selected menu language: ${Q.lang}`),z.log("Extension compatibility passed..."),we.onVideoAdded.addListener((t=>{for(const e of function(){const t=window.location.hostname,e=new URL(window.location),o=o=>o instanceof RegExp?o.test(t):"string"==typeof o?t.includes(o):"function"==typeof o&&o(e);return O.filter((t=>(Array.isArray(t.match)?t.match.some(o):o(t.match))&&t.host&&t.url))}()){if(!e)continue;let o=xe(e,t);if(o&&(("rumble"!==e.host||t.style.display)&&(["peertube","directlink"].includes(e.host)&&(e.url=window.location.origin),!ye.has(t)))){ye.set(t,new fe(t,o,e));break}}})),we.onVideoRemoved.addListener((async t=>{ye.has(t)&&(await ye.get(t).release(),ye.delete(t))})),we.enable()})().catch((t=>{console.error("[VOT]",t)}))})()})(); \ No newline at end of file diff --git a/dist/vot-cloudflare.user.js b/dist/vot-cloudflare.user.js index 1fdfe648..32c3d9be 100644 --- a/dist/vot-cloudflare.user.js +++ b/dist/vot-cloudflare.user.js @@ -77,6 +77,7 @@ // @match *://*.vimeo.com/* // @match *://*.9gag.com/* // @match *://*.twitter.com/* +// @match *://*.x.com/* // @match *://*.facebook.com/* // @match *://*.rutube.ru/* // @match *://*.bilibili.com/* @@ -121,6 +122,11 @@ // @match *://*.egghead.io/* // @match *://*.youku.com/* // @match *://*.archive.org/* +// @match *://*.patreon.com/* +// @match *://*.reddit.com/* +// @match *://*.kodik.info/* +// @match *://*.kodik.biz/* +// @match *://*.kodik.cc/* // @match *://*/*.mp4* // @exclude file://*/*.mp4* // @connect yandex.ru @@ -131,12 +137,12 @@ // @connect onrender.com // @connect workers.dev // @namespace vot-cloudflare -// @version 1.5.3.1 +// @version 1.6.0-beta.1 // @icon https://translate.yandex.ru/icons/favicon.ico // @author sodapng, mynovelhost, Toil, SashaXser, MrSoczekXD // @homepageURL https://github.com/ilyhalight/voice-over-translation/issues -// @updateURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js -// @downloadURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/master/dist/vot-cloudflare.user.js +// @updateURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/dev/dist/vot-cloudflare.user.js +// @downloadURL https://raw.githubusercontent.com/ilyhalight/voice-over-translation/dev/dist/vot-cloudflare.user.js // @supportURL https://github.com/ilyhalight/voice-over-translation/issues // ==/UserScript== @@ -166,7 +172,7 @@ var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default())); // Module -___CSS_LOADER_EXPORT___.push([module.id, `.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button:disabled{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button:disabled::before,.vot-button:disabled::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button:disabled::before,.vot-outlined-button:disabled::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button:disabled::before,.vot-text-button:disabled::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button:disabled::before,.vot-icon-button:disabled::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input,.vot-textfield:hover>textarea{border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input+span::before,.vot-textfield:hover>input+span::after,.vot-textfield:hover>textarea+span::before,.vot-textfield:hover>textarea+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:10000}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-translating=true] #vot-translating-icon{display:block !important}.vot-segmented-button[data-translating=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:10000;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog-container{visibility:visible;position:absolute;z-index:10000}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:10000;left:25%;top:75%;pointer-events:none}.vot-subtitles{position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:1rem;pointer-events:all;padding:1rem;font-size:2rem;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8);--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block}`, ""]); +___CSS_LOADER_EXPORT___.push([module.id, `.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button:disabled{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button:disabled::before,.vot-button:disabled::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button:disabled::before,.vot-outlined-button:disabled::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button:disabled::before,.vot-text-button:disabled::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button:disabled::before,.vot-icon-button:disabled::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input,.vot-textfield:hover>textarea{border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input+span::before,.vot-textfield:hover>input+span::after,.vot-textfield:hover>textarea+span::before,.vot-textfield:hover>textarea+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:10000}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-translating=true] #vot-translating-icon{display:block !important}.vot-segmented-button[data-translating=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:10000;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog-container{visibility:visible;position:absolute;z-index:10000}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:10000;left:25%;top:75%;pointer-events:none}.vot-subtitles{position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:1rem;pointer-events:all;padding:1rem;font-size:2rem;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8);--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block;visibility:visible !important}`, ""]); // Exports /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); @@ -555,330 +561,2471 @@ module.exports = function () { } -/***/ }), - -/***/ "./src/config/config.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { +/***/ }) +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ id: moduleId, +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/nonce */ +/******/ (() => { +/******/ __webpack_require__.nc = undefined; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be in strict mode. +(() => { "use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ Cc: () => (/* binding */ yandexUserAgent), -/* harmony export */ JD: () => (/* binding */ defaultAutoVolume), -/* harmony export */ K2: () => (/* binding */ defaultDetectService), -/* harmony export */ Pm: () => (/* binding */ proxyWorkerHost), -/* harmony export */ QL: () => (/* binding */ detectUrls), -/* harmony export */ S7: () => (/* binding */ yandexHmacKey), -/* harmony export */ T8: () => (/* binding */ maxAudioVolume), -/* harmony export */ mE: () => (/* binding */ defaultTranslationService), -/* harmony export */ rw: () => (/* binding */ translateUrls), -/* harmony export */ se: () => (/* binding */ m3u8ProxyHost) -/* harmony export */ }); -/* unused harmony export workerHost */ -// CONFIGURATION -const workerHost = "api.browser.yandex.ru"; -const m3u8ProxyHost = "m3u8-proxy.toil.cc"; // used for streaming -const proxyWorkerHost = "vot-worker.toil.cc"; // used for cloudflare version -const yandexHmacKey = "bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf"; -const yandexUserAgent = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 YaBrowser/24.4.0.0 Safari/537.36"; -const defaultAutoVolume = 0.15; // 0.0 - 1.0 (0% - 100%) - default volume of the video with the translation -const maxAudioVolume = 900; -const defaultTranslationService = "yandex"; -const defaultDetectService = "yandex"; -const detectUrls = { - yandex: "https://translate.toil.cc/detect", - rustServer: "https://rust-server-531j.onrender.com/detect", +// EXTERNAL MODULE: ./node_modules/bowser/es5.js +var es5 = __webpack_require__("./node_modules/bowser/es5.js"); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/protos/yandex.js +const _m0 = protobuf; +const protobufPackage = ""; +var StreamInterval; +(function (StreamInterval) { + StreamInterval[StreamInterval["NO_CONNECTION"] = 0] = "NO_CONNECTION"; + StreamInterval[StreamInterval["TRANSLATING"] = 10] = "TRANSLATING"; + StreamInterval[StreamInterval["STREAMING"] = 20] = "STREAMING"; + StreamInterval[StreamInterval["UNRECOGNIZED"] = -1] = "UNRECOGNIZED"; +})(StreamInterval || (StreamInterval = {})); +function streamIntervalFromJSON(object) { + switch (object) { + case 0: + case "NO_CONNECTION": + return StreamInterval.NO_CONNECTION; + case 10: + case "TRANSLATING": + return StreamInterval.TRANSLATING; + case 20: + case "STREAMING": + return StreamInterval.STREAMING; + case -1: + case "UNRECOGNIZED": + default: + return StreamInterval.UNRECOGNIZED; + } +} +function streamIntervalToJSON(object) { + switch (object) { + case StreamInterval.NO_CONNECTION: + return "NO_CONNECTION"; + case StreamInterval.TRANSLATING: + return "TRANSLATING"; + case StreamInterval.STREAMING: + return "STREAMING"; + case StreamInterval.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} +function createBaseVideoTranslationHelpObject() { + return { target: "", targetUrl: "" }; +} +const VideoTranslationHelpObject = { + encode(message, writer = _m0.Writer.create()) { + if (message.target !== "") { + writer.uint32(10).string(message.target); + } + if (message.targetUrl !== "") { + writer.uint32(18).string(message.targetUrl); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTranslationHelpObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.target = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.targetUrl = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + target: isSet(object.target) ? globalThis.String(object.target) : "", + targetUrl: isSet(object.targetUrl) ? globalThis.String(object.targetUrl) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.target !== "") { + obj.target = message.target; + } + if (message.targetUrl !== "") { + obj.targetUrl = message.targetUrl; + } + return obj; + }, + create(base) { + return VideoTranslationHelpObject.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseVideoTranslationHelpObject(); + message.target = object.target ?? ""; + message.targetUrl = object.targetUrl ?? ""; + return message; + }, +}; +function createBaseVideoTranslationRequest() { + return { + url: "", + deviceId: undefined, + firstRequest: false, + duration: 0, + unknown0: 0, + language: "", + forceSourceLang: false, + unknown1: 0, + translationHelp: [], + responseLanguage: "", + unknown2: 0, + unknown3: 0, + bypassCache: false, + }; +} +const VideoTranslationRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(26).string(message.url); + } + if (message.deviceId !== undefined) { + writer.uint32(34).string(message.deviceId); + } + if (message.firstRequest !== false) { + writer.uint32(40).bool(message.firstRequest); + } + if (message.duration !== 0) { + writer.uint32(49).double(message.duration); + } + if (message.unknown0 !== 0) { + writer.uint32(56).int32(message.unknown0); + } + if (message.language !== "") { + writer.uint32(66).string(message.language); + } + if (message.forceSourceLang !== false) { + writer.uint32(72).bool(message.forceSourceLang); + } + if (message.unknown1 !== 0) { + writer.uint32(80).int32(message.unknown1); + } + for (const v of message.translationHelp) { + VideoTranslationHelpObject.encode(v, writer.uint32(90).fork()).ldelim(); + } + if (message.responseLanguage !== "") { + writer.uint32(114).string(message.responseLanguage); + } + if (message.unknown2 !== 0) { + writer.uint32(120).int32(message.unknown2); + } + if (message.unknown3 !== 0) { + writer.uint32(128).int32(message.unknown3); + } + if (message.bypassCache !== false) { + writer.uint32(136).bool(message.bypassCache); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTranslationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 3: + if (tag !== 26) { + break; + } + message.url = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + message.deviceId = reader.string(); + continue; + case 5: + if (tag !== 40) { + break; + } + message.firstRequest = reader.bool(); + continue; + case 6: + if (tag !== 49) { + break; + } + message.duration = reader.double(); + continue; + case 7: + if (tag !== 56) { + break; + } + message.unknown0 = reader.int32(); + continue; + case 8: + if (tag !== 66) { + break; + } + message.language = reader.string(); + continue; + case 9: + if (tag !== 72) { + break; + } + message.forceSourceLang = reader.bool(); + continue; + case 10: + if (tag !== 80) { + break; + } + message.unknown1 = reader.int32(); + continue; + case 11: + if (tag !== 90) { + break; + } + message.translationHelp.push(VideoTranslationHelpObject.decode(reader, reader.uint32())); + continue; + case 14: + if (tag !== 114) { + break; + } + message.responseLanguage = reader.string(); + continue; + case 15: + if (tag !== 120) { + break; + } + message.unknown2 = reader.int32(); + continue; + case 16: + if (tag !== 128) { + break; + } + message.unknown3 = reader.int32(); + continue; + case 17: + if (tag !== 136) { + break; + } + message.bypassCache = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + deviceId: isSet(object.deviceId) ? globalThis.String(object.deviceId) : undefined, + firstRequest: isSet(object.firstRequest) ? globalThis.Boolean(object.firstRequest) : false, + duration: isSet(object.duration) ? globalThis.Number(object.duration) : 0, + unknown0: isSet(object.unknown0) ? globalThis.Number(object.unknown0) : 0, + language: isSet(object.language) ? globalThis.String(object.language) : "", + forceSourceLang: isSet(object.forceSourceLang) ? globalThis.Boolean(object.forceSourceLang) : false, + unknown1: isSet(object.unknown1) ? globalThis.Number(object.unknown1) : 0, + translationHelp: globalThis.Array.isArray(object?.translationHelp) + ? object.translationHelp.map((e) => VideoTranslationHelpObject.fromJSON(e)) + : [], + responseLanguage: isSet(object.responseLanguage) ? globalThis.String(object.responseLanguage) : "", + unknown2: isSet(object.unknown2) ? globalThis.Number(object.unknown2) : 0, + unknown3: isSet(object.unknown3) ? globalThis.Number(object.unknown3) : 0, + bypassCache: isSet(object.bypassCache) ? globalThis.Boolean(object.bypassCache) : false, + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.deviceId !== undefined) { + obj.deviceId = message.deviceId; + } + if (message.firstRequest !== false) { + obj.firstRequest = message.firstRequest; + } + if (message.duration !== 0) { + obj.duration = message.duration; + } + if (message.unknown0 !== 0) { + obj.unknown0 = Math.round(message.unknown0); + } + if (message.language !== "") { + obj.language = message.language; + } + if (message.forceSourceLang !== false) { + obj.forceSourceLang = message.forceSourceLang; + } + if (message.unknown1 !== 0) { + obj.unknown1 = Math.round(message.unknown1); + } + if (message.translationHelp?.length) { + obj.translationHelp = message.translationHelp.map((e) => VideoTranslationHelpObject.toJSON(e)); + } + if (message.responseLanguage !== "") { + obj.responseLanguage = message.responseLanguage; + } + if (message.unknown2 !== 0) { + obj.unknown2 = Math.round(message.unknown2); + } + if (message.unknown3 !== 0) { + obj.unknown3 = Math.round(message.unknown3); + } + if (message.bypassCache !== false) { + obj.bypassCache = message.bypassCache; + } + return obj; + }, + create(base) { + return VideoTranslationRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseVideoTranslationRequest(); + message.url = object.url ?? ""; + message.deviceId = object.deviceId ?? undefined; + message.firstRequest = object.firstRequest ?? false; + message.duration = object.duration ?? 0; + message.unknown0 = object.unknown0 ?? 0; + message.language = object.language ?? ""; + message.forceSourceLang = object.forceSourceLang ?? false; + message.unknown1 = object.unknown1 ?? 0; + message.translationHelp = object.translationHelp?.map((e) => VideoTranslationHelpObject.fromPartial(e)) || []; + message.responseLanguage = object.responseLanguage ?? ""; + message.unknown2 = object.unknown2 ?? 0; + message.unknown3 = object.unknown3 ?? 0; + message.bypassCache = object.bypassCache ?? false; + return message; + }, +}; +function createBaseVideoTranslationResponse() { + return { + url: undefined, + duration: undefined, + status: 0, + remainingTime: undefined, + unknown0: undefined, + translationId: "", + language: undefined, + message: undefined, + }; +} +const VideoTranslationResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== undefined) { + writer.uint32(10).string(message.url); + } + if (message.duration !== undefined) { + writer.uint32(17).double(message.duration); + } + if (message.status !== 0) { + writer.uint32(32).int32(message.status); + } + if (message.remainingTime !== undefined) { + writer.uint32(40).int32(message.remainingTime); + } + if (message.unknown0 !== undefined) { + writer.uint32(48).int32(message.unknown0); + } + if (message.translationId !== "") { + writer.uint32(58).string(message.translationId); + } + if (message.language !== undefined) { + writer.uint32(66).string(message.language); + } + if (message.message !== undefined) { + writer.uint32(74).string(message.message); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTranslationResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + message.duration = reader.double(); + continue; + case 4: + if (tag !== 32) { + break; + } + message.status = reader.int32(); + continue; + case 5: + if (tag !== 40) { + break; + } + message.remainingTime = reader.int32(); + continue; + case 6: + if (tag !== 48) { + break; + } + message.unknown0 = reader.int32(); + continue; + case 7: + if (tag !== 58) { + break; + } + message.translationId = reader.string(); + continue; + case 8: + if (tag !== 66) { + break; + } + message.language = reader.string(); + continue; + case 9: + if (tag !== 74) { + break; + } + message.message = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : undefined, + duration: isSet(object.duration) ? globalThis.Number(object.duration) : undefined, + status: isSet(object.status) ? globalThis.Number(object.status) : 0, + remainingTime: isSet(object.remainingTime) ? globalThis.Number(object.remainingTime) : undefined, + unknown0: isSet(object.unknown0) ? globalThis.Number(object.unknown0) : undefined, + translationId: isSet(object.translationId) ? globalThis.String(object.translationId) : "", + language: isSet(object.language) ? globalThis.String(object.language) : undefined, + message: isSet(object.message) ? globalThis.String(object.message) : undefined, + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== undefined) { + obj.url = message.url; + } + if (message.duration !== undefined) { + obj.duration = message.duration; + } + if (message.status !== 0) { + obj.status = Math.round(message.status); + } + if (message.remainingTime !== undefined) { + obj.remainingTime = Math.round(message.remainingTime); + } + if (message.unknown0 !== undefined) { + obj.unknown0 = Math.round(message.unknown0); + } + if (message.translationId !== "") { + obj.translationId = message.translationId; + } + if (message.language !== undefined) { + obj.language = message.language; + } + if (message.message !== undefined) { + obj.message = message.message; + } + return obj; + }, + create(base) { + return VideoTranslationResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseVideoTranslationResponse(); + message.url = object.url ?? undefined; + message.duration = object.duration ?? undefined; + message.status = object.status ?? 0; + message.remainingTime = object.remainingTime ?? undefined; + message.unknown0 = object.unknown0 ?? undefined; + message.translationId = object.translationId ?? ""; + message.language = object.language ?? undefined; + message.message = object.message ?? undefined; + return message; + }, +}; +function createBaseSubtitlesObject() { + return { language: "", url: "", unknown0: 0, translatedLanguage: "", translatedUrl: "", unknown1: 0, unknown2: 0 }; +} +const SubtitlesObject = { + encode(message, writer = _m0.Writer.create()) { + if (message.language !== "") { + writer.uint32(10).string(message.language); + } + if (message.url !== "") { + writer.uint32(18).string(message.url); + } + if (message.unknown0 !== 0) { + writer.uint32(24).int32(message.unknown0); + } + if (message.translatedLanguage !== "") { + writer.uint32(34).string(message.translatedLanguage); + } + if (message.translatedUrl !== "") { + writer.uint32(42).string(message.translatedUrl); + } + if (message.unknown1 !== 0) { + writer.uint32(48).int32(message.unknown1); + } + if (message.unknown2 !== 0) { + writer.uint32(56).int32(message.unknown2); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSubtitlesObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.language = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.url = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + message.unknown0 = reader.int32(); + continue; + case 4: + if (tag !== 34) { + break; + } + message.translatedLanguage = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + message.translatedUrl = reader.string(); + continue; + case 6: + if (tag !== 48) { + break; + } + message.unknown1 = reader.int32(); + continue; + case 7: + if (tag !== 56) { + break; + } + message.unknown2 = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + language: isSet(object.language) ? globalThis.String(object.language) : "", + url: isSet(object.url) ? globalThis.String(object.url) : "", + unknown0: isSet(object.unknown0) ? globalThis.Number(object.unknown0) : 0, + translatedLanguage: isSet(object.translatedLanguage) ? globalThis.String(object.translatedLanguage) : "", + translatedUrl: isSet(object.translatedUrl) ? globalThis.String(object.translatedUrl) : "", + unknown1: isSet(object.unknown1) ? globalThis.Number(object.unknown1) : 0, + unknown2: isSet(object.unknown2) ? globalThis.Number(object.unknown2) : 0, + }; + }, + toJSON(message) { + const obj = {}; + if (message.language !== "") { + obj.language = message.language; + } + if (message.url !== "") { + obj.url = message.url; + } + if (message.unknown0 !== 0) { + obj.unknown0 = Math.round(message.unknown0); + } + if (message.translatedLanguage !== "") { + obj.translatedLanguage = message.translatedLanguage; + } + if (message.translatedUrl !== "") { + obj.translatedUrl = message.translatedUrl; + } + if (message.unknown1 !== 0) { + obj.unknown1 = Math.round(message.unknown1); + } + if (message.unknown2 !== 0) { + obj.unknown2 = Math.round(message.unknown2); + } + return obj; + }, + create(base) { + return SubtitlesObject.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseSubtitlesObject(); + message.language = object.language ?? ""; + message.url = object.url ?? ""; + message.unknown0 = object.unknown0 ?? 0; + message.translatedLanguage = object.translatedLanguage ?? ""; + message.translatedUrl = object.translatedUrl ?? ""; + message.unknown1 = object.unknown1 ?? 0; + message.unknown2 = object.unknown2 ?? 0; + return message; + }, +}; +function createBaseSubtitlesRequest() { + return { url: "", language: "" }; +} +const SubtitlesRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + if (message.language !== "") { + writer.uint32(18).string(message.language); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSubtitlesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.language = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + language: isSet(object.language) ? globalThis.String(object.language) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.language !== "") { + obj.language = message.language; + } + return obj; + }, + create(base) { + return SubtitlesRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseSubtitlesRequest(); + message.url = object.url ?? ""; + message.language = object.language ?? ""; + return message; + }, +}; +function createBaseSubtitlesResponse() { + return { waiting: false, subtitles: [] }; +} +const SubtitlesResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.waiting !== false) { + writer.uint32(8).bool(message.waiting); + } + for (const v of message.subtitles) { + SubtitlesObject.encode(v, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSubtitlesResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + message.waiting = reader.bool(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.subtitles.push(SubtitlesObject.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + waiting: isSet(object.waiting) ? globalThis.Boolean(object.waiting) : false, + subtitles: globalThis.Array.isArray(object?.subtitles) + ? object.subtitles.map((e) => SubtitlesObject.fromJSON(e)) + : [], + }; + }, + toJSON(message) { + const obj = {}; + if (message.waiting !== false) { + obj.waiting = message.waiting; + } + if (message.subtitles?.length) { + obj.subtitles = message.subtitles.map((e) => SubtitlesObject.toJSON(e)); + } + return obj; + }, + create(base) { + return SubtitlesResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseSubtitlesResponse(); + message.waiting = object.waiting ?? false; + message.subtitles = object.subtitles?.map((e) => SubtitlesObject.fromPartial(e)) || []; + return message; + }, +}; +function createBaseStreamTranslationObject() { + return { url: "", timestamp: "" }; +} +const StreamTranslationObject = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + if (message.timestamp !== "") { + writer.uint32(18).string(message.timestamp); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamTranslationObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.timestamp = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + timestamp: isSet(object.timestamp) ? globalThis.String(object.timestamp) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.timestamp !== "") { + obj.timestamp = message.timestamp; + } + return obj; + }, + create(base) { + return StreamTranslationObject.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamTranslationObject(); + message.url = object.url ?? ""; + message.timestamp = object.timestamp ?? ""; + return message; + }, +}; +function createBaseStreamTranslationRequest() { + return { url: "", language: "", responseLanguage: "" }; +} +const StreamTranslationRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + if (message.language !== "") { + writer.uint32(18).string(message.language); + } + if (message.responseLanguage !== "") { + writer.uint32(26).string(message.responseLanguage); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamTranslationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.language = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + message.responseLanguage = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + language: isSet(object.language) ? globalThis.String(object.language) : "", + responseLanguage: isSet(object.responseLanguage) ? globalThis.String(object.responseLanguage) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.language !== "") { + obj.language = message.language; + } + if (message.responseLanguage !== "") { + obj.responseLanguage = message.responseLanguage; + } + return obj; + }, + create(base) { + return StreamTranslationRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamTranslationRequest(); + message.url = object.url ?? ""; + message.language = object.language ?? ""; + message.responseLanguage = object.responseLanguage ?? ""; + return message; + }, +}; +function createBaseStreamTranslationResponse() { + return { interval: 0, translatedInfo: undefined, pingId: undefined }; +} +const StreamTranslationResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.interval !== 0) { + writer.uint32(8).int32(message.interval); + } + if (message.translatedInfo !== undefined) { + StreamTranslationObject.encode(message.translatedInfo, writer.uint32(18).fork()).ldelim(); + } + if (message.pingId !== undefined) { + writer.uint32(24).int32(message.pingId); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamTranslationResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + message.interval = reader.int32(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.translatedInfo = StreamTranslationObject.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 24) { + break; + } + message.pingId = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + interval: isSet(object.interval) ? streamIntervalFromJSON(object.interval) : 0, + translatedInfo: isSet(object.translatedInfo) + ? StreamTranslationObject.fromJSON(object.translatedInfo) + : undefined, + pingId: isSet(object.pingId) ? globalThis.Number(object.pingId) : undefined, + }; + }, + toJSON(message) { + const obj = {}; + if (message.interval !== 0) { + obj.interval = streamIntervalToJSON(message.interval); + } + if (message.translatedInfo !== undefined) { + obj.translatedInfo = StreamTranslationObject.toJSON(message.translatedInfo); + } + if (message.pingId !== undefined) { + obj.pingId = Math.round(message.pingId); + } + return obj; + }, + create(base) { + return StreamTranslationResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamTranslationResponse(); + message.interval = object.interval ?? 0; + message.translatedInfo = (object.translatedInfo !== undefined && object.translatedInfo !== null) + ? StreamTranslationObject.fromPartial(object.translatedInfo) + : undefined; + message.pingId = object.pingId ?? undefined; + return message; + }, +}; +function createBaseStreamPingRequest() { + return { pingId: 0 }; +} +const StreamPingRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.pingId !== 0) { + writer.uint32(8).int32(message.pingId); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamPingRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + message.pingId = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { pingId: isSet(object.pingId) ? globalThis.Number(object.pingId) : 0 }; + }, + toJSON(message) { + const obj = {}; + if (message.pingId !== 0) { + obj.pingId = Math.round(message.pingId); + } + return obj; + }, + create(base) { + return StreamPingRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamPingRequest(); + message.pingId = object.pingId ?? 0; + return message; + }, }; - -const translateUrls = { - yandex: "https://translate.toil.cc/translate", - deepl: "https://translate-deepl.toil.cc/translate", +function createBaseYandexSessionRequest() { + return { uuid: "", module: "" }; +} +const YandexSessionRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.uuid !== "") { + writer.uint32(10).string(message.uuid); + } + if (message.module !== "") { + writer.uint32(18).string(message.module); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseYandexSessionRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.uuid = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.module = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + uuid: isSet(object.uuid) ? globalThis.String(object.uuid) : "", + module: isSet(object.module) ? globalThis.String(object.module) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.uuid !== "") { + obj.uuid = message.uuid; + } + if (message.module !== "") { + obj.module = message.module; + } + return obj; + }, + create(base) { + return YandexSessionRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseYandexSessionRequest(); + message.uuid = object.uuid ?? ""; + message.module = object.module ?? ""; + return message; + }, }; +function createBaseYandexSessionResponse() { + return { secretKey: "", expires: 0 }; +} +const YandexSessionResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.secretKey !== "") { + writer.uint32(10).string(message.secretKey); + } + if (message.expires !== 0) { + writer.uint32(16).int32(message.expires); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseYandexSessionResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.secretKey = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + message.expires = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + secretKey: isSet(object.secretKey) ? globalThis.String(object.secretKey) : "", + expires: isSet(object.expires) ? globalThis.Number(object.expires) : 0, + }; + }, + toJSON(message) { + const obj = {}; + if (message.secretKey !== "") { + obj.secretKey = message.secretKey; + } + if (message.expires !== 0) { + obj.expires = Math.round(message.expires); + } + return obj; + }, + create(base) { + return YandexSessionResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseYandexSessionResponse(); + message.secretKey = object.secretKey ?? ""; + message.expires = object.expires ?? 0; + return message; + }, +}; +function isSet(value) { + return value !== null && value !== undefined; +} +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/protobuf.js +const yandexProtobuf = { + encodeTranslationRequest(url, duration, requestLang, responseLang, translationHelp) { + return VideoTranslationRequest.encode({ + url, + firstRequest: true, + duration, + unknown0: 1, + language: requestLang, + forceSourceLang: false, + unknown1: 0, + translationHelp: translationHelp ? translationHelp : [], + responseLanguage: responseLang, + unknown2: 0, + unknown3: 1, + bypassCache: false, + }).finish(); + }, + decodeTranslationResponse(response) { + return VideoTranslationResponse.decode(new Uint8Array(response)); + }, + encodeSubtitlesRequest(url, requestLang) { + return SubtitlesRequest.encode({ + url, + language: requestLang, + }).finish(); + }, + decodeSubtitlesResponse(response) { + return SubtitlesResponse.decode(new Uint8Array(response)); + }, + encodeStreamPingRequest(pingId) { + return StreamPingRequest.encode({ + pingId, + }).finish(); + }, + encodeStreamRequest(url, requestLang, responseLang) { + return StreamTranslationRequest.encode({ + url, + language: requestLang, + responseLanguage: responseLang, + }).finish(); + }, + decodeStreamResponse(response) { + return StreamTranslationResponse.decode(new Uint8Array(response)); + }, + encodeYandexSessionRequest(uuid, module) { + return YandexSessionRequest.encode({ + uuid, + module, + }).finish(); + }, + decodeYandexSessionResponse(response) { + return YandexSessionResponse.decode(new Uint8Array(response)); + }, +}; - -/***/ }), - -/***/ "./src/config/constants.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ Ww: () => (/* binding */ actualTTS), -/* harmony export */ xm: () => (/* binding */ availableLangs) -/* harmony export */ }); -/* unused harmony export cfOnlyExtensions */ -// available languages for translation -const availableLangs = [ - "ru", - "en", - "zh", - "ko", - "lt", - "lv", - "ar", - "fr", - "it", - "es", - "de", - "ja", -]; - -// up-to-date list of TTS working languages -const actualTTS = ["ru", "en", "kk"]; - -const cfOnlyExtensions = (/* unused pure expression or super */ null && ([ - "Violentmonkey", - "FireMonkey", - "Greasemonkey", - "AdGuard", - "OrangeMonkey", -])); - - - - -/***/ }), - -/***/ "./src/localization/localizationProvider.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; - -// EXPORTS -__webpack_require__.d(__webpack_exports__, { - z: () => (/* binding */ availableLocales), - j: () => (/* binding */ localizationProvider) +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/config/config.js +/* harmony default export */ const config = ({ + host: "api.browser.yandex.ru", + hostVOT: "https://vot-api.toil.cc/v1", + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 YaBrowser/24.6.0.0 Safari/537.36", + componentVersion: "24.6.1.766", + hmac: "bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf", + defaultDuration: 343, }); -;// CONCATENATED MODULE: ./src/localization/locales/en.json -const en_namespaceObject = /*#__PURE__*/JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","neededAdditionalExtension":"An additional extension is needed to support this site","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyAccessTokenExpired":"Your entered Udemy Access Token has expired","udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","enterUdemyAccessToken":"Enter Udemy Access Token","VOTUdemyData":"Udemy Data","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase"}'); -// EXTERNAL MODULE: ./src/utils/debug.js -var debug = __webpack_require__("./src/utils/debug.js"); -// EXTERNAL MODULE: ./src/utils/storage.js -var storage = __webpack_require__("./src/utils/storage.js"); -// EXTERNAL MODULE: ./src/utils/utils.js -var utils = __webpack_require__("./src/utils/utils.js"); -;// CONCATENATED MODULE: ./src/localization/localizationProvider.js - +;// CONCATENATED MODULE: ./node_modules/vot.js/package.json +const package_namespaceObject = {"rE":"0.5.0"}; +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/secure.js +const utf8Encoder = new TextEncoder(); +async function signHMAC(hashName, hmac, data) { + const key = await crypto.subtle.importKey("raw", utf8Encoder.encode(hmac), { name: "HMAC", hash: { name: hashName } }, false, ["sign", "verify"]); + return await crypto.subtle.sign("HMAC", key, data); +} +async function getSignature(body) { + const signature = await signHMAC("SHA-256", config.hmac, body); + return new Uint8Array(signature).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); +} +function getUUID() { + const hexDigits = "0123456789ABCDEF"; + let uuid = ""; + for (let i = 0; i < 32; i++) { + const randomDigit = Math.floor(Math.random() * 16); + uuid += hexDigits[randomDigit]; + } + return uuid; +} +async function getHmacSha1(hmacKey, salt) { + try { + const hmacSalt = utf8Encoder.encode(salt); + const signature = await signHMAC("SHA-1", hmacKey, hmacSalt); + return btoa(String.fromCharCode(...new Uint8Array(signature))); + } + catch (err) { + console.error(err); + return false; + } +} +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/types/yandex.js +var VideoService; +(function (VideoService) { + VideoService["custom"] = "custom"; + VideoService["directlink"] = "custom"; + VideoService["youtube"] = "youtube"; + VideoService["piped"] = "piped"; + VideoService["invidious"] = "invidious"; + VideoService["vk"] = "vk"; + VideoService["nine_gag"] = "nine_gag"; + VideoService["gag"] = "nine_gag"; + VideoService["twitch"] = "twitch"; + VideoService["proxitok"] = "proxitok"; + VideoService["tiktok"] = "tiktok"; + VideoService["vimeo"] = "vimeo"; + VideoService["xvideos"] = "xvideos"; + VideoService["pornhub"] = "pornhub"; + VideoService["twitter"] = "twitter"; + VideoService["rumble"] = "rumble"; + VideoService["facebook"] = "facebook"; + VideoService["rutube"] = "rutube"; + VideoService["coub"] = "coub"; + VideoService["bilibili"] = "bilibili"; + VideoService["mail_ru"] = "mailru"; + VideoService["mailru"] = "mailru"; + VideoService["bitchute"] = "bitchute"; + VideoService["coursera"] = "coursera"; + VideoService["udemy"] = "udemy"; + VideoService["eporner"] = "eporner"; + VideoService["peertube"] = "peertube"; + VideoService["dailymotion"] = "dailymotion"; + VideoService["trovo"] = "trovo"; + VideoService["yandexdisk"] = "yandexdisk"; + VideoService["coursehunter"] = "coursehunter"; + VideoService["ok_ru"] = "okru"; + VideoService["okru"] = "okru"; + VideoService["googledrive"] = "googledrive"; + VideoService["bannedvideo"] = "bannedvideo"; + VideoService["weverse"] = "weverse"; + VideoService["newgrounds"] = "newgrounds"; + VideoService["egghead"] = "egghead"; + VideoService["youku"] = "youku"; + VideoService["archive"] = "archive"; + VideoService["kodik"] = "kodik"; + VideoService["patreon"] = "patreon"; + VideoService["reddit"] = "reddit"; +})(VideoService || (VideoService = {})); +var VideoTranslationStatus; +(function (VideoTranslationStatus) { + VideoTranslationStatus[VideoTranslationStatus["FAILED"] = 0] = "FAILED"; + VideoTranslationStatus[VideoTranslationStatus["FINISHED"] = 1] = "FINISHED"; + VideoTranslationStatus[VideoTranslationStatus["WAITING"] = 2] = "WAITING"; + VideoTranslationStatus[VideoTranslationStatus["LONG_WAITING"] = 3] = "LONG_WAITING"; + VideoTranslationStatus[VideoTranslationStatus["PART_CONTENT"] = 5] = "PART_CONTENT"; + VideoTranslationStatus[VideoTranslationStatus["LONG_WAITING_2"] = 6] = "LONG_WAITING_2"; +})(VideoTranslationStatus || (VideoTranslationStatus = {})); + +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/utils.js + +async function fetchWithTimeout(url, options = { + headers: { + "User-Agent": config.userAgent, + }, +}) { + const { timeout = 3000 } = options; + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + const response = await fetch(url, { + ...options, + signal: controller.signal, + }); + clearTimeout(id); + return response; +} +function getTimestamp() { + return Math.floor(Date.now() / 1000); +} +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/config/alternativeUrls.js +const sitesInvidious = [ + "invidious.snopyta.org", + "yewtu.be", + "invidious.kavin.rocks", + "vid.puffyan.us", + "invidious.namazso.eu", + "inv.riverside.rocks", + "yt.artemislena.eu", + "invidious.flokinet.to", + "invidious.esmailelbob.xyz", + "y.com.sb", + "invidious.nerdvpn.de", + "inv.vern.cc", + "invidious.slipfox.xyz", + "invidio.xamh.de", + "invidious.dhusch.de", +]; +const sitesPiped = [ + "piped.video", + "piped.tokhmi.xyz", + "piped.moomoo.me", + "piped.syncpundit.io", + "piped.mha.fi", + "watch.whatever.social", + "piped.garudalinux.org", + "efy.piped.pages.dev", + "watch.leptons.xyz", + "piped.lunar.icu", + "yt.dc09.ru", + "piped.mint.lgbt", + "il.ax", + "piped.privacy.com.de", + "piped.esmailelbob.xyz", + "piped.projectsegfau.lt", + "piped.in.projectsegfau.lt", + "piped.us.projectsegfau.lt", + "piped.privacydev.net", + "piped.palveluntarjoaja.eu", + "piped.smnz.de", + "piped.adminforge.de", + "piped.qdi.fi", + "piped.hostux.net", + "piped.chauvet.pro", + "piped.jotoma.de", + "piped.pfcd.me", + "piped.frontendfriendly.xyz", +]; +const sitesProxiTok = [ + "proxitok.pabloferreiro.es", + "proxitok.pussthecat.org", + "tok.habedieeh.re", + "proxitok.esmailelbob.xyz", + "proxitok.privacydev.net", + "tok.artemislena.eu", + "tok.adminforge.de", + "tik.hostux.net", + "tt.vern.cc", + "cringe.whatever.social", + "proxitok.lunar.icu", + "proxitok.privacy.com.de", +]; +const sitesPeertube = [ + "peertube.1312.media", + "tube.shanti.cafe", + "bee-tube.fr", + "video.sadmin.io", + "dalek.zone", + "review.peertube.biz", + "peervideo.club", + "tube.la-dina.net", + "peertube.tmp.rcp.tf", + "peertube.su", +]; -const localesVersion = 4; -const localesUrl = `https://raw.githubusercontent.com/ilyhalight/voice-over-translation/${ - false ? 0 : "master" -}/src/localization/locales`; -const availableLocales = [ - "auto", - "en", - "ru", +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/config/sites.js - "af", - "am", - "ar", - "az", - "bg", - "bn", - "bs", - "ca", - "cs", - "cy", - "da", - "de", - "el", - "es", - "et", - "eu", - "fa", - "fi", - "fr", - "gl", - "hi", - "hr", - "hu", - "hy", - "id", - "it", - "ja", - "jv", - "kk", - "km", - "kn", - "ko", - "lo", - "mk", - "ml", - "mn", - "ms", - "mt", - "my", - "ne", - "nl", - "pa", - "pl", - "pt", - "ro", - "si", - "sk", - "sl", - "sq", - "sr", - "su", - "sv", - "sw", - "tr", - "uk", - "ur", - "uz", - "vi", - "zh", - "zu", -]; -const localizationProvider = new (class { - lang = "en"; - locale = {}; - gmValues = [ - "locale-phrases", - "locale-lang", - "locale-version", - "locale-lang-override", - ]; +/* harmony default export */ const sites = ([ + { + additionalData: "mobile", + host: VideoService.youtube, + url: "https://youtu.be/", + match: /^m.youtube.com$/, + selector: "shorts-video #player", + }, + { + additionalData: "mobile", + host: VideoService.youtube, + url: "https://youtu.be/", + match: /^m.youtube.com$/, + selector: ".player-container", + }, + { + host: VideoService.youtube, + url: "https://youtu.be/", + match: /^(www.)?youtube(-nocookie|kids)?.com$/, + selector: ".html5-video-container:not(#inline-player *)", + }, + { + host: VideoService.invidious, + url: "https://youtu.be/", + match: sitesInvidious, + selector: "#player", + }, + { + host: VideoService.piped, + url: "https://youtu.be/", + match: sitesPiped, + selector: ".shaka-video-container", + }, + { + additionalData: "mobile", + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^m.vk.(com|ru)$/, + selector: "vk-video-player", + shadowRoot: true, + }, + { + additionalData: "clips", + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^(www.|m.)?vk.(com|ru)$/, + selector: 'div[data-testid="clipcontainer-video"]', + }, + { + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^(www.|m.)?vk.(com|ru)$/, + selector: ".videoplayer_media" + }, + { + host: VideoService.nine_gag, + url: "https://9gag.com/gag/", + match: /^9gag.com$/, + selector: ".video-post", + }, + { + host: VideoService.twitch, + url: "https://twitch.tv/", + match: [ + /^m.twitch.tv$/, + /^(www.)?twitch.tv$/, + /^clips.twitch.tv$/, + /^player.twitch.tv$/, + ], + selector: ".video-ref, main > div > section > div > div > div", + }, + { + host: VideoService.proxitok, + url: "https://www.tiktok.com/", + match: sitesProxiTok, + selector: ".column.has-text-centered", + }, + { + host: VideoService.tiktok, + url: "https://www.tiktok.com/", + match: /^(www.)?tiktok.com$/, + selector: null + }, + { + host: VideoService.vimeo, + url: "https://vimeo.com/", + match: /^vimeo.com$/, + selector: ".player", + }, + { + additionalData: "embed", + host: VideoService.vimeo, + url: "https://player.vimeo.com/", + match: /^player.vimeo.com$/, + selector: ".player", + }, + { + host: VideoService.xvideos, + url: "https://www.xvideos.com/", + match: /^(www.)?(xvideos|xv-ru).com$/, + selector: ".video-bg-pic", + }, + { + host: VideoService.pornhub, + url: "https://rt.pornhub.com/view_video.php?viewkey=", + match: /^[a-z]+.pornhub.com$/, + selector: ".mainPlayerDiv > .video-element-wrapper-js > div", + }, + { + additionalData: "embed", + host: VideoService.pornhub, + url: "https://rt.pornhub.com/view_video.php?viewkey=", + match: (url) => + url.host.includes("pornhub.com") && url.pathname.startsWith("/embed/"), + selector: "#player", + }, + { + host: VideoService.twitter, + url: "https://twitter.com/i/status/", + match: /^twitter.com$/, + selector: 'div[data-testid="videoComponent"] > div:nth-child(1) > div', + }, + { + host: VideoService.rumble, + url: "https://rumble.com/", + match: /^rumble.com$/, + selector: "#videoPlayer > .videoPlayer-Rumble-cls > div", + }, + { + host: VideoService.facebook, + url: "https://facebook.com/", + match: (url) => + url.host.includes("facebook.com") && url.pathname.includes("/videos/"), + selector: 'div[role="main"] div[data-pagelet$="video" i]', + }, + { + additionalData: "reels", + host: VideoService.facebook, + url: "https://facebook.com/", + match: (url) => + url.host.includes("facebook.com") && url.pathname.includes("/reel/"), + selector: 'div[role="main"]', + }, + { + host: VideoService.rutube, + url: "https://rutube.ru/video/", + match: /^rutube.ru$/, + selector: ".video-player > div > div > div:nth-child(2)", + }, + { + additionalData: "embed", + host: VideoService.rutube, + url: "https://rutube.ru/video/", + match: /^rutube.ru$/, + selector: "#app > div > div", + }, + { + host: VideoService.bilibili, + url: "https://www.bilibili.com/video/", + match: /^(www|m|player).bilibili.com$/, + selector: ".bpx-player-video-wrap", + }, + // Добавляет лишние видео в обработчик + { + additionalData: "old", // /blackboard/webplayer/embed-old.html + host: VideoService.bilibili, + url: "https://www.bilibili.com/video/", + match: /^(www|m).bilibili.com$/, + selector: null, + }, + { + host: VideoService.mailru, + url: "https://my.mail.ru/", + match: /^my.mail.ru$/, + selector: "#b-video-wrapper", + }, + { + host: VideoService.bitchute, + url: "https://www.bitchute.com/video/", + match: /^(www.)?bitchute.com$/, + selector: ".video-js", + }, + { + // ONLY IF YOU LOGINED TO COURSERA /learn/NAME/lecture/XXXX + host: VideoService.coursera, + url: "https://www.coursera.org/", + match: /coursera.org$/, + selector: ".vjs-v6", + needExtraData: true, + }, + { + // ONLY IF YOU LOGINED TO UDEMY /course/NAME/learn/lecture/XXXX + host: VideoService.udemy, + url: "https://www.udemy.com/", + match: /udemy.com$/, + selector: + 'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)', + needExtraData: true, + }, + { + host: VideoService.eporner, + url: "https://www.eporner.com/", + match: /^(www.)?eporner.com$/, + selector: ".vjs-v7", + }, + { + host: VideoService.peertube, + url: "stub", + match: sitesPeertube, + selector: ".vjs-v7", + }, + { + host: VideoService.dailymotion, + url: "https://dai.ly/", + match: /^geo.dailymotion.com$/, + selector: ".player", + }, + { + host: VideoService.trovo, + url: "https://trovo.live/s/", + match: /^trovo.live$/, + selector: ".player-video", + }, + { + host: VideoService.yandexdisk, + url: "https://yadi.sk/i/", + match: /^disk.yandex.ru$/, + selector: ".video-player__player > div:nth-child(1)", + }, + { + host: VideoService.coursehunter, + url: "https://coursehunter.net/course/", + match: /^coursehunter.net$/, + selector: "#oframeplayer > pjsdiv:nth-of-type(1)", + }, + { + host: VideoService.okru, + url: "https://ok.ru/video/", + match: /^ok.ru$/, + selector: ".html5-vpl_vid", + }, + { + host: VideoService.googledrive, + url: "https://drive.google.com/file/d/", + match: /^youtube.googleapis.com$/, + selector: ".html5-video-container", + }, + { + host: VideoService.bannedvideo, + url: "https://madmaxworld.tv/watch?id=", + match: /^(www.)?banned.video|madmaxworld.tv$/, + selector: ".vjs-v7", + needExtraData: true, + }, + { + host: VideoService.weverse, + url: "https://weverse.io/", + match: /^weverse.io$/, + selector: ".webplayer-internal-source-wrapper", + needExtraData: true, + }, + { + host: VideoService.newgrounds, + url: "https://www.newgrounds.com/", + match: /^(www.)?newgrounds.com$/, + selector: ".ng-video-player", + }, + { + host: VideoService.egghead, + url: "https://egghead.io/", + match: /^egghead.io$/, + selector: ".cueplayer-react-video-holder", + }, + { + host: VideoService.youku, + url: "https://v.youku.com/", + match: /^v.youku.com$/, + selector: "#ykPlayer", + }, + { + host: VideoService.archive, + url: "https://archive.org/details/", + match: /^archive.org$/, + selector: ".jw-media", + }, + { + host: VideoService.kodik, + url: "stub", + match: /^kodik.(info|biz|cc)$/, + selector: ".fp-player", + needExtraData: true, + }, + { + host: VideoService.patreon, + url: "stub", + match: /^(www.)?patreon.com$/, + selector: + 'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div', + needExtraData: true, + }, + { + host: VideoService.reddit, + url: "stub", + match: /^(www.)?reddit.com$/, + selector: "shreddit-player", + shadowRoot: true, + needExtraData: true, + }, + { + host: VideoService.custom, + url: "stub", + match: (url) => /([^.]+).mp4/.test(url.pathname), + rawResult: true, + }, +]); - constructor() { - const langOverride = storage/* votStorage */.d.syncGet("locale-lang-override", "auto"); - if (langOverride && langOverride !== "auto") { - this.lang = langOverride; - } else { - this.lang = - (navigator.language || navigator.userLanguage) - ?.substr(0, 2) - ?.toLowerCase() ?? "en"; - } - this.setLocaleFromJsonString(storage/* votStorage */.d.syncGet("locale-phrases", "")); - } +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/helper.js - reset() { - for (let i = 0; i < this.gmValues.length; i++) { - storage/* votStorage */.d.syncDelete(this.gmValues[i]); - } - } - async update(force = false) { - const localeVersion = await storage/* votStorage */.d.get("locale-version", 0, true); - const localeLang = await storage/* votStorage */.d.get("locale-lang"); - if ( - !force && - localeVersion === localesVersion && - localeLang === this.lang - ) { - return; - } - debug/* default */.A.log("Updating locale..."); - try { - const response = await (0,utils/* GM_fetch */.G3)(`${localesUrl}/${this.lang}.json`); - if (response.status !== 200) throw response.status; - const text = await response.text(); - await storage/* votStorage */.d.set("locale-phrases", text); - this.setLocaleFromJsonString(text); - const version = this.getFromLocale(this.locale, "__version__"); - if (typeof version === "number") - await storage/* votStorage */.d.set("locale-version", version); - await storage/* votStorage */.d.set("locale-lang", this.lang); - } catch (error) { - console.error( - "[VOT] [localizationProvider] failed get locale, cause:", - error, - ); - this.setLocaleFromJsonString(await storage/* votStorage */.d.get("locale-phrases", "")); +class VideoHelperError extends Error { + constructor(message) { + super(message); + this.name = "VideoHelper"; + this.message = message; } - } - - setLocaleFromJsonString(json) { - try { - this.locale = JSON.parse(json) ?? {}; - } catch (exception) { - console.error("[VOT] [localizationProvider]", exception); - this.locale = {}; +} +class MailRuHelper { + async getVideoData(videoId) { + try { + const res = await fetchWithTimeout(`https://my.mail.ru/+/video/meta/${videoId}?xemail=&ajax_call=1&func_name=&mna=&mnb=&ext=1&_=${new Date().getTime()}`); + return (await res.json()); + } + catch (err) { + console.error("Failed to get mail.ru video info", err.message); + return undefined; + } } - } - - getFromLocale(locale, key) { - const result = key.split(".").reduce((locale, key) => { - if (typeof locale === "object" && locale) return locale[key]; - return undefined; - }, locale); - if (result === undefined) { - console.warn( - "[VOT] [localizationProvider] locale", - locale, - "doesn't contain key", - key, - ); +} +class WeverseHelper { + API_ORIGIN = "https://global.apis.naver.com/weverse/wevweb"; + API_APP_ID = "be4d79eb8fc7bd008ee82c8ec4ff6fd4"; + API_HMAC_KEY = "1b9cb6378d959b45714bec49971ade22e6e24e42"; + HEADERS = { + Accept: "application/json, text/plain, */*", + Origin: "https://weverse.io", + Referer: "https://weverse.io/", + }; + getURLData() { + return { + appId: this.API_APP_ID, + language: "en", + os: "WEB", + platform: "WEB", + wpf: "pc", + }; } - return result; - } + async createHash(pathname) { + const timestamp = Date.now(); + const salt = pathname.substring(0, Math.min(255, pathname.length)) + timestamp; + const sign = await getHmacSha1(this.API_HMAC_KEY, salt); + if (!sign) { + throw new VideoHelperError("Failed to get weverse HMAC signature"); + } + return { + wmsgpad: timestamp.toString(), + wmd: sign, + }; + } + async getHashURLParams(pathname) { + const hash = await this.createHash(pathname); + return new URLSearchParams(hash).toString(); + } + async getPostPreview(postId) { + const pathname = `/post/v1.0/post-${postId}/preview?` + + new URLSearchParams({ + fieldSet: "postForPreview", + ...this.getURLData(), + }).toString(); + try { + const urlParams = await this.getHashURLParams(pathname); + const res = await fetchWithTimeout(this.API_ORIGIN + pathname + "&" + urlParams, { + headers: this.HEADERS, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get weverse post preview by postId: ${postId}`, err.message); + return false; + } + } + async getVideoInKey(videoId) { + const pathname = `/video/v1.1/vod/${videoId}/inKey?` + + new URLSearchParams({ + gcc: "RU", + ...this.getURLData(), + }).toString(); + try { + const urlParams = await this.getHashURLParams(pathname); + const res = await fetchWithTimeout(this.API_ORIGIN + pathname + "&" + urlParams, { + method: "POST", + headers: this.HEADERS, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get weverse InKey by videoId: ${videoId}`, err.message); + return false; + } + } + async getVideoInfo(infraVideoId, inkey, serviceId) { + const timestamp = Date.now(); + try { + const urlParams = new URLSearchParams({ + key: inkey, + sid: serviceId, + nonce: timestamp.toString(), + devt: "html5_pc", + prv: "N", + aup: "N", + stpb: "N", + cpl: "en", + env: "prod", + lc: "en", + adi: JSON.stringify([ + { + adSystem: null, + }, + ]), + adu: "/", + }).toString(); + const res = await fetchWithTimeout(`https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${infraVideoId}?` + + urlParams, { + headers: this.HEADERS, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get weverse video info (infraVideoId: ${infraVideoId}, inkey: ${inkey}, serviceId: ${serviceId}`, err.message); + return false; + } + } + extractVideoInfo(videoList) { + return videoList.find((video) => video.useP2P === false && video.source.includes(".mp4")); + } + async getVideoData(postId) { + const videoPreview = await this.getPostPreview(postId); + if (!videoPreview) { + return undefined; + } + const { videoId, serviceId, infraVideoId } = videoPreview.extension.video; + if (!(videoId && serviceId && infraVideoId)) { + return undefined; + } + const inkeyData = await this.getVideoInKey(videoId); + if (!inkeyData) { + return undefined; + } + const videoInfo = await this.getVideoInfo(infraVideoId, inkeyData.inKey, serviceId); + if (!videoInfo) { + return undefined; + } + const videoItem = this.extractVideoInfo(videoInfo.videos.list); + if (!videoItem) { + return undefined; + } + return { + url: videoItem.source, + duration: videoItem.duration, + }; + } +} +class KodikHelper { + API_ORIGIN = window.location.origin; + async getSecureData(videoPath) { + try { + const url = this.API_ORIGIN + videoPath; + const res = await fetchWithTimeout(url, { + headers: { + "User-Agent": config.userAgent, + Origin: this.API_ORIGIN, + Referer: this.API_ORIGIN, + }, + }); + const content = await res.text(); + const [videoType, videoId, hash] = videoPath.split("/").filter((a) => a); + const parser = new DOMParser() + const doc = parser.parseFromString(content, "text/html") + const secureScript = Array.from(doc.getElementsByTagName("script")).filter((s) => s.innerHTML.includes(`videoId = "${videoId}"`)); + if (!secureScript.length) { + throw new VideoHelperError("Failed to find secure script"); + } + const secureContent = /'{[^']+}'/.exec(secureScript[0].textContent.trim())?.[0]; + if (!secureContent) { + throw new VideoHelperError("Secure json wasn't found in secure script"); + } + const secureJSON = JSON.parse(secureContent.replaceAll("'", "")); + return { + videoType: videoType, + videoId, + hash, + ...secureJSON, + }; + } + catch (err) { + console.error(`Failed to get kodik secure data by videoPath: ${videoPath}.`, err.message); + return false; + } + } + async getFtor(secureData) { + const { videoType, videoId: id, hash, d, d_sign, pd, pd_sign, ref, ref_sign, } = secureData; + try { + const res = await fetchWithTimeout(this.API_ORIGIN + "/ftor", { + method: "POST", + headers: { + "User-Agent": config.userAgent, + Origin: this.API_ORIGIN, + Referer: `${this.API_ORIGIN}/${videoType}/${id}/${hash}/360p`, + }, + body: new URLSearchParams({ + d, + d_sign, + pd, + pd_sign, + ref: decodeURIComponent(ref), + ref_sign, + bad_user: "false", + cdn_is_working: "true", + info: "{}", + type: videoType, + hash, + id, + }), + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get kodik video data (type: ${videoType}, id: ${id}, hash: ${hash})`, err.message); + return false; + } + } + decryptUrl(encryptedUrl) { + const decryptedUrl = atob(encryptedUrl.replace(/[a-zA-Z]/g, function (e) { + const charCode = e.charCodeAt(0) + 13; + return String.fromCharCode((e <= "Z" ? 90 : 122) >= charCode ? charCode : charCode - 26); + })); + return "https:" + decryptedUrl; + } + async getVideoData(videoPath) { + const secureData = await this.getSecureData(videoPath); + if (!secureData) { + return undefined; + } + const videoData = await this.getFtor(secureData); + if (!videoData) { + return undefined; + } + const videoDataLinks = Object.entries(videoData.links[videoData.default.toString()]); + const videoLink = videoDataLinks.find(([_, data]) => data.type === "application/x-mpegURL")?.[1]; + if (!videoLink) { + return undefined; + } + return { + url: this.decryptUrl(videoLink.src), + }; + } +} +class PatreonHelper { + async getPosts(postId) { + try { + const res = await fetchWithTimeout(`https://www.patreon.com/api/posts/${postId}?json-api-use-default-includes=false`); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get patreon posts by postId: ${postId}.`, err.message); + return false; + } + } + async getVideoData(postId) { + const postData = await this.getPosts(postId); + if (!postData) { + return undefined; + } + const postFileUrl = postData.data.attributes.post_file.url; + if (!postFileUrl) { + return undefined; + } + return { + url: postFileUrl, + }; + } +} +class RedditHelper { + async getVideoData() { + const contentUrl = document.querySelector("source[type='application/vnd.apple.mpegURL']")?.src + ?.replaceAll("&", "&"); + if (!contentUrl) { + return undefined; + } + return { + url: decodeURIComponent(contentUrl), + }; + } +} +class BannedVideoHelper { + async getVideoInfo(videoId) { + try { + const res = await fetchWithTimeout(`https://api.banned.video/graphql`, { + method: "POST", + body: JSON.stringify({ + operationName: "GetVideo", + query: `query GetVideo($id: String!) { + getVideo(id: $id) { + title + description: summary + duration: videoDuration + videoUrl: directUrl + isStream: live + } + }`, + variables: { + id: videoId, + }, + }), + headers: { + "User-Agent": "bannedVideoFrontEnd", + "apollographql-client-name": "banned-web", + "apollographql-client-version": "1.3", + "content-type": "application/json", + }, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get bannedvideo video info by videoId: ${videoId}.`, err.message); + return false; + } + } + async getVideoData(videoId) { + const videoInfo = await this.getVideoInfo(videoId); + if (!videoInfo) { + return false; + } + const { videoUrl, duration, isStream, description, title } = videoInfo.data.getVideo; + return { + url: videoUrl, + duration, + isStream, + title, + description, + }; + } +} +class VideoHelper { + static [VideoService.mailru] = new MailRuHelper(); + static [VideoService.weverse] = new WeverseHelper(); + static [VideoService.kodik] = new KodikHelper(); + static [VideoService.patreon] = new PatreonHelper(); + static [VideoService.reddit] = new RedditHelper(); + static [VideoService.bannedvideo] = new BannedVideoHelper(); +} - getDefault(key) { - return this.getFromLocale(en_namespaceObject, key) ?? key; - } +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/videoData.js - get(key) { - return ( - this.getFromLocale(this.locale, key) ?? - this.getFromLocale(en_namespaceObject, key) ?? - key - ); - } -})(); -/***/ }), -/***/ "./src/utils/debug.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { +class VideoDataError extends Error { + constructor(message) { + super(message); + this.name = "VideoDataError"; + this.message = message; + } +} +function getService(videoUrl) { + if (videoUrl.startsWith("file://")) + return false; + let enteredURL; + try { + enteredURL = new URL(videoUrl); + } + catch (e) { + console.error(`Invalid URL: ${videoUrl}. Have you forgotten https?`); + return false; + } + const hostname = enteredURL.hostname; + const isMathes = (match) => { + if (match instanceof RegExp) { + return match.test(hostname); + } + else if (typeof match === "string") { + return hostname.includes(match); + } + else if (typeof match === "function") { + return match(enteredURL); + } + return false; + }; + return sites.find((e) => { + return ((Array.isArray(e.match) ? e.match.some(isMathes) : isMathes(e.match)) && + e.host && + e.url); + }); +} +async function getVideoID(service, videoURL) { + const url = new URL(videoURL); + switch (service.host) { + case VideoService.custom: + return url.href; + case VideoService.piped: + case VideoService.invidious: + case VideoService.youtube: + if (url.hostname === "youtu.be") { + url.search = `?v=${url.pathname.replace("/", "")}`; + url.pathname = "/watch"; + } + return (/(?:watch|embed|shorts|live)\/([^/]+)/.exec(url.pathname)?.[1] ?? + url.searchParams.get("v")); + case VideoService.vk: { + const pathID = /^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(url.pathname); + const paramZ = url.searchParams.get("z"); + const paramOID = url.searchParams.get("oid"); + const paramID = url.searchParams.get("id"); + if (pathID) { + return pathID[0].slice(1); + } + else if (paramZ) { + return paramZ.split("/")[0]; + } + else if (paramOID && paramID) { + return `video-${Math.abs(parseInt(paramOID))}_${paramID}`; + } + return null; + } + case VideoService.nine_gag: + case VideoService.gag: + return /gag\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.twitch: { + const clipPath = /([^/]+)\/(?:clip)\/([^/]+)/.exec(url.pathname); + const isClipsDomain = /^clips\.twitch\.tv$/.test(url.hostname); + if (/^m\.twitch\.tv$/.test(url.hostname)) { + return /videos\/([^/]+)/.exec(url.href)?.[0] ?? url.pathname.slice(1); + } + else if (/^player\.twitch\.tv$/.test(url.hostname)) { + return `videos/${url.searchParams.get("video")}`; + } + else if (isClipsDomain) { + const pathname = url.pathname.slice(1); + const isEmbed = pathname === "embed"; + const res = await fetchWithTimeout(`https://clips.twitch.tv/${isEmbed ? url.searchParams.get("clip") : url.pathname.slice(1)}`, { + headers: { + "User-Agent": "Googlebot/2.1 (+http://www.googlebot.com/bot.html)", + }, + }); + const content = await res.text(); + const channelLink = /"url":"https:\/\/www\.twitch\.tv\/([^"]+)"/.exec(content); + if (!channelLink) { + return null; + } + return `${channelLink[1]}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; + } + else if (clipPath) { + return clipPath[0]; + } + return /(?:videos)\/([^/]+)/.exec(url.pathname)?.[0]; + } + case VideoService.proxitok: + case VideoService.tiktok: + return /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.vimeo: { + const appId = url.searchParams.get("app_id"); + const videoId = /[^/]+\/[^/]+$/.exec(url.pathname)?.[0] ?? + /[^/]+$/.exec(url.pathname)?.[0]; + return appId ? `${videoId}?app_id=${appId}` : videoId; + } + case VideoService.xvideos: + return /[^/]+\/[^/]+$/.exec(url.pathname)?.[0]; + case VideoService.pornhub: + return (url.searchParams.get("viewkey") ?? + /embed\/([^/]+)/.exec(url.pathname)?.[1]); + case VideoService.twitter: + return /status\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.rumble: + case VideoService.facebook: + return url.pathname.slice(1); + case VideoService.rutube: + return /(?:video|embed)\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.bilibili: { + const bvid = url.searchParams.get("bvid"); + if (bvid) { + return bvid; + } + let vid = /video\/([^/]+)/.exec(url.pathname)?.[1]; + if (vid && url.searchParams.get("p") !== null) { + vid += `/?p=${url.searchParams.get("p")}`; + } + return vid; + } + case VideoService.mailru: { + const pathname = url.pathname; + if (pathname.startsWith("/v/") || pathname.startsWith("/mail/")) { + return pathname.slice(1); + } + const videoId = /video\/embed\/([^/]+)/.exec(pathname)?.[1]; + if (!videoId) { + return null; + } + const videoData = await VideoHelper.mailru.getVideoData(videoId); + if (!videoData) { + return null; + } + return videoData.meta.url.replace("//my.mail.ru/", ""); + } + case VideoService.bitchute: + return /(video|embed)\/([^/]+)/.exec(url.pathname)?.[2]; + case VideoService.coursera: + return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <-- COURSE PASSING (IF YOU LOGINED TO COURSERA) + case VideoService.eporner: + return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.peertube: + return /\/w\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.dailymotion: { + return url.hostname === "dai.ly" + ? url.pathname.slice(1) + : /video\/([^/]+)/.exec(url.pathname)?.[1]; + } + case VideoService.trovo: { + const vid = url.searchParams.get("vid"); + if (!vid) { + return null; + } + const path = /([^/]+)\/([\d]+)/.exec(url.pathname)?.[0]; + if (!path) { + return null; + } + return `${path}?vid=${vid}`; + } + case VideoService.yandexdisk: + return /\/i\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.okru: { + return /\/video\/(\d+)/.exec(url.pathname)?.[1]; + } + case VideoService.googledrive: + return /\/file\/d\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.bannedvideo: { + return url.searchParams.get("id"); + } + case VideoService.weverse: + return /([^/]+)\/(live|media)\/([^/]+)/.exec(url.pathname)?.[3]; + case VideoService.newgrounds: + return /([^/]+)\/(view)\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.egghead: + return url.pathname.slice(1); + case VideoService.youku: + return /v_show\/id_[\w=]+/.exec(url.pathname)?.[0]; + case VideoService.archive: + return /(details|embed)\/([^/]+)/.exec(url.pathname)?.[2]; + case VideoService.kodik: + return /\/(seria|video)\/([^/]+)\/([^/]+)\/([\d]+)p/.exec(url.pathname)?.[0]; + case VideoService.patreon: { + const fullPostId = /posts\/([^/]+)/.exec(url.pathname)?.[1]; + if (!fullPostId) { + return undefined; + } + return fullPostId.replace(/[^\d.]/g, ""); + } + case VideoService.reddit: + return /\/r\/(([^/]+)\/([^/]+)\/([^/]+)\/([^/]+))/.exec(url.pathname)?.[1]; + default: + return undefined; + } +} +async function getVideoData(url) { + const service = getService(url); + if (!service) { + throw new VideoDataError(`URL: "${url}" is unknown service`); + } + const videoId = await getVideoID(service, url); + if (!videoId) { + throw new VideoDataError(`Entered unsupported link: "${url}"`); + } + if (service.host === VideoService.peertube) { + service.url = new URL(url).origin; + } + if (service.rawResult) { + return { + url: videoId, + videoId, + host: service.host, + duration: undefined, + }; + } + if (!service.needExtraData) { + return { + url: service.url + videoId, + videoId, + host: service.host, + duration: undefined, + }; + } + const result = await VideoHelper[service.host].getVideoData(videoId); + if (!result) { + throw new VideoDataError(`Failed to get video raw url for ${service.host}`); + } + return { + ...result, + videoId, + host: service.host, + }; +} + +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/vot.js -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); +function convertVOT(service, videoId, url) { + if (service === VideoService.patreon) { + return { + service: "mux", + videoId: new URL(url).pathname.slice(1), + }; + } + return { + service, + videoId, + }; +} + +;// CONCATENATED MODULE: ./src/localization/locales/en.json +const en_namespaceObject = /*#__PURE__*/JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","neededAdditionalExtension":"An additional extension is needed to support this site","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyAccessTokenExpired":"Your entered Udemy Access Token has expired","udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","enterUdemyAccessToken":"Enter Udemy Access Token","VOTUdemyData":"Udemy Data","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase"}'); +;// CONCATENATED MODULE: ./src/utils/debug.js const debug = {}; debug.log = (...text) => { - if (true) { - return; - } + if (false) {} return console.log( "%c[VOT DEBUG]", "background: #F2452D; color: #fff; padding: 5px;", ...text, ); -}; - -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (debug); - - -/***/ }), +}; -/***/ "./src/utils/storage.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { +/* harmony default export */ const utils_debug = (debug); -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ d: () => (/* binding */ votStorage) -/* harmony export */ }); -/* harmony import */ var _debug_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/utils/debug.js"); +;// CONCATENATED MODULE: ./src/utils/storage.js const votStorage = new (class { constructor() { this.gmSupport = typeof GM_getValue === "function"; - _debug_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A.log(`GM Storage Status: ${this.gmSupport}`); + utils_debug.log(`GM Storage Status: ${this.gmSupport}`); } syncGet(name, def = undefined, toNumber = false) { @@ -895,7 +3042,8 @@ const votStorage = new (class { } } - return toNumber ? Number(val) ?? Number(def) : val ?? def; + const result = val ?? def; + return toNumber ? Number(result) : result; } async get(name, def = undefined, toNumber = false) { @@ -920,571 +3068,90 @@ const votStorage = new (class { async set(name, value) { if (this.gmSupport) { - return await GM_setValue(name, value); - } - - return Promise.resolve(this.syncSet(name, value)); - } - - syncDelete(name) { - if (this.gmSupport) { - return GM_deleteValue(name); - } - - return window.localStorage.removeItem(name); - } - - async delete(name) { - if (this.gmSupport) { - return await GM_deleteValue(name); - } - - return Promise.resolve(this.syncDelete(name)); - } - - syncList() { - if (this.gmSupport) { - return GM_listValues(); - } - - return [ - "autoTranslate", - "dontTranslateLanguage", - "dontTranslateYourLang", - "autoSetVolumeYandexStyle", - "showVideoSlider", - "syncVolume", - "subtitlesMaxLength", - "highlightWords", - "responseLanguage", - "defaultVolume", - "udemyData", - "audioProxy", - "showPiPButton", - "locale-version", - "locale-lang", - "locale-phrases", - ]; - } - - async list() { - if (this.gmSupport) { - return await GM_listValues(); - } - - return Promise.resolve(this.syncList()); - } -})(); - - -/***/ }), - -/***/ "./src/utils/translateApis.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ Tl: () => (/* binding */ translate), -/* harmony export */ o0: () => (/* binding */ detect), -/* harmony export */ qh: () => (/* binding */ detectServices), -/* harmony export */ vN: () => (/* binding */ translateServices) -/* harmony export */ }); -/* harmony import */ var _config_config_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/config/config.js"); -/* harmony import */ var _storage_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/utils/storage.js"); - - - -const HTTP_TIMEOUT = 3000; - -async function fetchWithTimeout(url, options = {}) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), HTTP_TIMEOUT); - - try { - return await fetch(url, { - ...options, - signal: controller.signal, - }); - } catch (error) { - console.error("Fetch timed-out. Error:", error); - return error; - } finally { - clearTimeout(timeoutId); - } -} - -const YandexTranslateAPI = { - async translate(text, lang) { - // Limit: 10k symbols - // - // Lang examples: - // en-ru, uk-ru, ru-en... - // ru, en (instead of auto-ru, auto-en) - - try { - const response = await fetchWithTimeout( - `${_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .translateUrls */ .rw.yandex}?${new URLSearchParams({ - text, - lang, - })}`, - ); - - if (response instanceof Error) { - throw response; - } - - const content = await response.json(); - - if (content.code !== 200) { - throw content.message; - } - - return content.text[0]; - } catch (error) { - console.error("Error translating text:", error); - return text; - } - }, - - async detect(text) { - // Limit: 10k symbols - try { - const response = await fetchWithTimeout( - `${_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .detectUrls */ .QL.yandex}?${new URLSearchParams({ - text, - })}`, - ); - - if (response instanceof Error) { - throw response; - } - - const content = await response.json(); - if (content.code !== 200) { - throw content.message; - } - - return content.lang ?? "en"; - } catch (error) { - console.error("Error getting lang from text:", error); - return "en"; - } - }, -}; - -const RustServerAPI = { - async detect(text) { - try { - const response = await fetch(_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .detectUrls */ .QL.rustServer, { - method: "POST", - body: text, - }); - - if (response instanceof Error) { - throw response; - } - - return await response.text(); - } catch (error) { - console.error("Error getting lang from text:", error); - return "en"; - } - }, -}; - -const DeeplServerAPI = { - async translate(text, fromLang = "auto", toLang = "ru") { - try { - const response = await fetchWithTimeout(_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .translateUrls */ .rw.deepl, { - method: "POST", - headers: { - "content-type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams({ - text, - source_lang: fromLang, - target_lang: toLang, - }), - }); - - if (response instanceof Error) { - throw response; - } - - const content = await response.json(); - - if (content.code !== 200) { - throw content.message; - } - - return content.data; - } catch (error) { - console.error("Error translating text:", error); - return text; - } - }, -}; - -async function translate(text, fromLang = "", toLang = "ru") { - const service = await _storage_js__WEBPACK_IMPORTED_MODULE_1__/* .votStorage */ .d.get( - "translationService", - _config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .defaultTranslationService */ .mE, - ); - switch (service) { - case "yandex": { - const langPair = fromLang && toLang ? `${fromLang}-${toLang}` : toLang; - return await YandexTranslateAPI.translate(text, langPair); - } - case "deepl": { - return await DeeplServerAPI.translate(text, fromLang, toLang); - } - default: - return text; - } -} - -async function detect(text) { - const service = await _storage_js__WEBPACK_IMPORTED_MODULE_1__/* .votStorage */ .d.get("detectService", _config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .defaultDetectService */ .K2); - switch (service) { - case "yandex": - return await YandexTranslateAPI.detect(text); - case "rust-server": - return await RustServerAPI.detect(text); - default: - return "en"; - } -} - -const translateServices = Object.keys(_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .translateUrls */ .rw); -const detectServices = Object.keys(_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .detectUrls */ .QL).map((k) => - k === "rustServer" ? "rust-server" : k, -); - - - - -/***/ }), - -/***/ "./src/utils/utils.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ Bs: () => (/* binding */ isPiPAvailable), -/* harmony export */ CK: () => (/* binding */ initHls), -/* harmony export */ G3: () => (/* binding */ GM_fetch), -/* harmony export */ R4: () => (/* binding */ langTo6391), -/* harmony export */ X5: () => (/* binding */ cleanText), -/* harmony export */ jI: () => (/* binding */ getVideoId), -/* harmony export */ ox: () => (/* binding */ secsToStrTime), -/* harmony export */ vV: () => (/* binding */ lang) -/* harmony export */ }); -/* harmony import */ var _localization_localizationProvider_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/localization/localizationProvider.js"); -/* harmony import */ var _youtubeUtils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/utils/youtubeUtils.js"); - - - -const userlang = navigator.language || navigator.userLanguage; -const lang = userlang?.substr(0, 2)?.toLowerCase() ?? "en"; - -// not used -// function waitForElm(selector) { -// // https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists -// return new Promise((resolve) => { -// const element = document.querySelector(selector); -// if (element) { -// return resolve(element); -// } - -// const observer = new MutationObserver(() => { -// const element = document.querySelector(selector); -// if (element) { -// resolve(element); -// observer.disconnect(); -// } -// }); - -// observer.observe(document.body, { -// childList: true, -// subtree: true, -// once: true, -// }); -// }); -// } - -// not used -// const sleep = (m) => new Promise((r) => setTimeout(r, m)); - -const getVideoId = (service, video) => { - let url = new URL(window.location.href); - - switch (service) { - case "piped": - case "invidious": - case "youtube": { - if (url.searchParams.has("enablejsapi")) { - const videoUrl = _youtubeUtils_js__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.getPlayer().getVideoUrl(); - url = new URL(videoUrl); - } - - return ( - /(?:watch|embed|shorts|live)\/([^/]+)/.exec(url.pathname)?.[1] || - url.searchParams.get("v") - ); - } - case "vk": { - const pathID = /^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(url.pathname); - const paramZ = url.searchParams.get("z"); - const paramOID = url.searchParams.get("oid"); - const paramID = url.searchParams.get("id"); - if (pathID) { - return pathID[0].slice(1); - } else if (paramZ) { - return paramZ.split("/")[0]; - } else if (paramOID && paramID) { - return `video-${Math.abs(parseInt(paramOID))}_${paramID}`; - } - - return null; - } - case "nine_gag": - case "9gag": - case "gag": - return /gag\/([^/]+)/.exec(url.pathname)?.[1]; - case "twitch": { - const clipPath = /([^/]+)\/(?:clip)\/([^/]+)/.exec(url.pathname); - if (/^m\.twitch\.tv$/.test(url.hostname)) { - return /videos\/([^/]+)/.exec(url.href)?.[0] || url.pathname.slice(1); - } else if (/^player\.twitch\.tv$/.test(url.hostname)) { - return `videos/${url.searchParams.get("video")}`; - } else if (/^clips\.twitch\.tv$/.test(url.hostname)) { - // https://clips.twitch.tv/clipId - const schema = document.querySelector( - "script[type='application/ld+json']", - ); - const pathname = url.pathname.slice(1); - if (!schema) { - // иногда из-за не прогрузов твича это не работает, но пусть лучше будет (можно переделать все в async и ждать элемента, но нужно ли это ради 1 сайта) - // ссылки вида https://clips.twitch.tv/embed?clip=clipId грузятся нормально - const isEmbed = pathname === "embed"; - const channelLink = document.querySelector( - isEmbed - ? ".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']" - : ".clips-player a:not([class])", - ); - - if (!channelLink) { - return; - } - - const channelName = channelLink.href.replace( - "https://www.twitch.tv/", - "", - ); + return await GM_setValue(name, value); + } - return `${channelName}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; - } + return Promise.resolve(this.syncSet(name, value)); + } - const schemaJSON = JSON.parse(schema.innerText); - const channelLink = schemaJSON["@graph"].find( - (obj) => obj["@type"] === "VideoObject", - )?.creator.url; + syncDelete(name) { + if (this.gmSupport) { + return GM_deleteValue(name); + } - const channelName = channelLink.replace("https://www.twitch.tv/", ""); - return `${channelName}/clip/${pathname}`; - } else if (clipPath) { - return clipPath[0]; - } + return window.localStorage.removeItem(name); + } - return /(?:videos)\/([^/]+)/.exec(url.pathname)?.[0]; + async delete(name) { + if (this.gmSupport) { + return await GM_deleteValue(name); } - case "proxitok": - return /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; - case "tiktok": { - let id = /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; - if (!id) { - const playerEl = video.closest(".xgplayer-playing, .tiktok-web-player"); - const itemEl = playerEl?.closest( - 'div[data-e2e="recommend-list-item-container"]', - ); - const authorEl = itemEl?.querySelector( - 'a[data-e2e="video-author-avatar"]', - ); - if (playerEl && authorEl) { - const videoId = playerEl.id?.match(/^xgwrapper-\d+-(.*)$/)?.at(1); - const author = authorEl.href?.match(/.*(@.*)$/)?.at(1); - if (videoId && author) { - id = `${author}/video/${videoId}`; - } - } - } - return id; - } - case "vimeo": { - const appId = url.searchParams.get("app_id"); - const videoId = - /[^/]+\/[^/]+$/.exec(url.pathname)?.[0] || - /[^/]+$/.exec(url.pathname)?.[0]; - - return appId ? `${videoId}?app_id=${appId}` : videoId; - } - case "xvideos": - return /[^/]+\/[^/]+$/.exec(url.pathname)?.[0]; - case "pornhub": - return ( - url.searchParams.get("viewkey") || - /embed\/([^/]+)/.exec(url.pathname)?.[1] - ); - case "twitter": - return /status\/([^/]+)/.exec(url.pathname)?.[1]; - case "udemy": - case "rumble": - case "facebook": - return url.pathname.slice(1); - case "rutube": - return /(?:video|embed)\/([^/]+)/.exec(url.pathname)?.[1]; - case "coub": - return ( - /(?:view|embed)\/([^/]+)/.exec(url.pathname)?.[1] || - document.querySelector(".coub.active")?.dataset?.permalink - ); - case "bilibili": { - const bvid = url.searchParams.get("bvid"); - if (bvid) { - return bvid; - } - let vid = /video\/([^/]+)/.exec(url.pathname)?.[1]; - if (vid && url.searchParams.get("p") !== null) { - vid += `/?p=${url.searchParams.get("p")}`; - } + return Promise.resolve(this.syncDelete(name)); + } - return vid; + syncList() { + if (this.gmSupport) { + return GM_listValues(); } - case "mail_ru": { - const pathname = url.pathname; - if (pathname.startsWith("/v/") || pathname.startsWith("/mail/")) { - return pathname.slice(1); - } - - const videoId = /video\/embed\/([^/]+)/.exec(pathname)?.[1]; - if (!videoId) { - return null; - } - const referer = document.querySelector(".b-video-controls__mymail-link"); - if (!referer) { - return false; - } + return [ + "autoTranslate", + "dontTranslateLanguage", + "dontTranslateYourLang", + "autoSetVolumeYandexStyle", + "showVideoSlider", + "syncVolume", + "subtitlesMaxLength", + "highlightWords", + "responseLanguage", + "defaultVolume", + "udemyData", + "audioProxy", + "showPiPButton", + "locale-version", + "locale-lang", + "locale-phrases", + ]; + } - return referer?.href.split("my.mail.ru")?.[1]; + async list() { + if (this.gmSupport) { + return await GM_listValues(); } - case "bitchute": { - if (video.src?.startsWith("blob:") || !video.src?.includes(".mp4")) { - return null; - } - return video.src; - // doesn't want to translate using a bitchute link - // return /([^/]+)\/([^/]+).mp4/.exec(videoUrl.pathname)?.[2]; - } - case "coursera": - // ! LINK SHOULD BE LIKE THIS https://www.coursera.org/learn/learning-how-to-learn/lecture/75EsZ - // return url.pathname.match(/lecture\/([^/]+)\/([^/]+)/)?.[1]; // <--- COURSE PREVIEW - return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <--- COURSE PASSING (IF YOU LOGINED TO COURSERA) - case "eporner": - // ! LINK SHOULD BE LIKE THIS eporner.com/video-XXXXXXXXX/isdfsd-dfjsdfjsdf-dsfsdf-dsfsda-dsad-ddsd - return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; - case "peertube": - return /\/w\/([^/]+)/.exec(url.pathname)?.[0]; - case "dailymotion": { - // we work in the context of the player - // geo.dailymotion.com - const plainPlayerConfig = Array.from( - document.querySelectorAll("*"), - ).filter((s) => s.innerHTML.trim().includes(".m3u8")); - try { - let videoUrl = plainPlayerConfig[1].lastChild.src; - return /\/video\/(\w+)\.m3u8/.exec(videoUrl)?.[1]; - } catch (e) { - console.error("[VOT]", e); - return false; - } - } - case "trovo": { - const vid = url.searchParams.get("vid"); - if (!vid) { - return null; - } + return Promise.resolve(this.syncList()); + } +})(); - const path = /([^/]+)\/(\d+)/.exec(url.pathname)?.[0]; - if (!path) { - return null; - } +;// CONCATENATED MODULE: ./src/utils/utils.js - return `${path}?vid=${vid}`; - } - case "yandexdisk": - return /\/i\/([^/]+)/.exec(url.pathname)?.[1]; - case "coursehunter": { - const courseId = /\/course\/([^/]+)/.exec(url.pathname)?.[1]; - return courseId ? courseId + url.search : false; - } - case "ok.ru": { - return /\/video\/(\d+)/.exec(url.pathname)?.[1]; - } - case "googledrive": - return url.searchParams.get("docid"); - case "bannedvideo": - return url.searchParams.get("id"); - case "weverse": - return /([^/]+)\/(live|media)\/([^/]+)/.exec(url.pathname)?.[0]; - case "newgrounds": - return /([^/]+)\/(view)\/([^/]+)/.exec(url.pathname)?.[0]; - case "egghead": - return url.pathname.slice(1); - case "youku": - return /v_show\/id_[\w=]+/.exec(url.pathname)?.[0]; - case "archive": - return /(details|embed)\/([^/]+)/.exec(url.pathname)?.[2]; - // case "sibnet": { - // const videoId = url.searchParams.get("videoid"); - // if (videoId) { - // return `video${videoId}`; - // } - - // return /video([^/]+)/.exec(url.pathname)?.[0]; - // } - // case "patreon": - // return /posts\/([^/]+)/.exec(url.pathname)?.[0]; - case "directlink": - return url.pathname + url.search; - default: - return false; - } -}; + + +const userlang = navigator.language || navigator.userLanguage; +const lang = userlang?.substr(0, 2)?.toLowerCase() ?? "en"; function secsToStrTime(secs) { const minutes = Math.floor(secs / 60); const seconds = Math.floor(secs % 60); if (minutes >= 60) { - return _localization_localizationProvider_js__WEBPACK_IMPORTED_MODULE_0__/* .localizationProvider */ .j.get("translationTakeMoreThanHour"); + return localizationProvider.get("translationTakeMoreThanHour"); } else if (minutes === 1 || (minutes === 0 && seconds > 0)) { - return _localization_localizationProvider_js__WEBPACK_IMPORTED_MODULE_0__/* .localizationProvider */ .j.get("translationTakeAboutMinute"); + return localizationProvider.get("translationTakeAboutMinute"); } else if (minutes !== 11 && minutes % 10 === 1) { - return _localization_localizationProvider_js__WEBPACK_IMPORTED_MODULE_0__/* .localizationProvider */ .j + return localizationProvider .get("translationTakeApproximatelyMinute2") .replace("{0}", minutes); } else if ( ![12, 13, 14].includes(minutes) && [2, 3, 4].includes(minutes % 10) ) { - return _localization_localizationProvider_js__WEBPACK_IMPORTED_MODULE_0__/* .localizationProvider */ .j + return localizationProvider .get("translationTakeApproximatelyMinute") .replace("{0}", minutes); } - return _localization_localizationProvider_js__WEBPACK_IMPORTED_MODULE_0__/* .localizationProvider */ .j + return localizationProvider .get("translationTakeApproximatelyMinutes") .replace("{0}", minutes); } @@ -1503,7 +3170,7 @@ function isPiPAvailable() { function initHls() { return typeof Hls != "undefined" && Hls?.isSupported() ? new Hls({ - debug: false, // turn it on manually if necessary + debug: true, // turn it on manually if necessary lowLatencyMode: true, backBufferLength: 90, }) @@ -1540,36 +3207,41 @@ function cleanText(title, description) { return fullText.replace(/[^\p{L}\s]+|\s+/gu, " ").trim(); } -async function GM_fetch(url, opt = {}) { +async function GM_fetch(url, opts = {}) { try { - // Попытка выполнить запрос с помощью fetch - const response = await fetch(url, opt); - return response; - } catch (error) { + if (url.includes("api.browser.yandex.ru")) { + throw new Error("Preventing yandex cors"); + } + + return await fetch(url, opts); + } catch (err) { // Если fetch завершился ошибкой, используем GM_xmlhttpRequest // https://greasyfork.org/ru/scripts/421384-gm-fetch/code + utils_debug.log("GM_fetch preventing cors by GM_xmlhttpRequest", err.message); return new Promise((resolve, reject) => { - // https://www.tampermonkey.net/documentation.php?ext=dhdg#GM_xmlhttpRequest - // https://violentmonkey.github.io/api/gm/#gm_xmlhttprequest GM_xmlhttpRequest({ - method: opt.method || "GET", + method: "GET", url: url, responseType: "blob", + ...opts, + data: opts.body, onload: (resp) => { resolve( new Response(resp.response, { status: resp.status, + // chrome \n and ":", firefox \r\n and ": " (Only in GM_xmlhttpRequest) // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#examples headers: Object.fromEntries( resp.responseHeaders .trim() - .split("\r\n") + .split("\n") .map((line) => { - let parts = line.split(": "); + let parts = line.split(":"); if (parts?.[0] === "set-cookie") { return; } - return [parts.shift(), parts.join(": ")]; + + return [parts.shift(), parts.join(":")]; }) .filter((key) => key), ), @@ -1586,512 +3258,637 @@ async function GM_fetch(url, opt = {}) { +;// CONCATENATED MODULE: ./src/localization/localizationProvider.js +/* eslint-disable sonarjs/no-duplicate-string */ -/***/ }), -/***/ "./src/utils/youtubeUtils.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _debug_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/utils/debug.js"); -/* harmony import */ var _config_constants_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/config/constants.js"); -/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/utils/utils.js"); -/* harmony import */ var _translateApis_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./src/utils/translateApis.js"); +const localesVersion = 4; +const localesUrl = `https://raw.githubusercontent.com/ilyhalight/voice-over-translation/${ + true ? "dev" : 0 +}/src/localization/locales`; + +const availableLocales = [ + "auto", + "en", + "ru", + + "af", + "am", + "ar", + "az", + "bg", + "bn", + "bs", + "ca", + "cs", + "cy", + "da", + "de", + "el", + "es", + "et", + "eu", + "fa", + "fi", + "fr", + "gl", + "hi", + "hr", + "hu", + "hy", + "id", + "it", + "ja", + "jv", + "kk", + "km", + "kn", + "ko", + "lo", + "mk", + "ml", + "mn", + "ms", + "mt", + "my", + "ne", + "nl", + "pa", + "pl", + "pt", + "ro", + "si", + "sk", + "sl", + "sq", + "sr", + "su", + "sv", + "sw", + "tr", + "uk", + "ur", + "uz", + "vi", + "zh", + "zu", +]; + +const localizationProvider = new (class { + lang = "en"; + locale = {}; + gmValues = [ + "locale-phrases", + "locale-lang", + "locale-version", + "locale-lang-override", + ]; + + constructor() { + const langOverride = votStorage.syncGet("locale-lang-override", "auto"); + if (langOverride && langOverride !== "auto") { + this.lang = langOverride; + } else { + this.lang = + (navigator.language || navigator.userLanguage) + ?.substr(0, 2) + ?.toLowerCase() ?? "en"; + } + this.setLocaleFromJsonString(votStorage.syncGet("locale-phrases", "")); + } + + reset() { + for (let i = 0; i < this.gmValues.length; i++) { + votStorage.syncDelete(this.gmValues[i]); + } + } + + async update(force = false) { + const localeVersion = await votStorage.get("locale-version", 0, true); + const localeLang = await votStorage.get("locale-lang"); + if ( + !force && + localeVersion === localesVersion && + localeLang === this.lang + ) { + return; + } + utils_debug.log("Updating locale..."); + try { + const response = await GM_fetch(`${localesUrl}/${this.lang}.json`); + if (response.status !== 200) throw response.status; + const text = await response.text(); + await votStorage.set("locale-phrases", text); + this.setLocaleFromJsonString(text); + const version = this.getFromLocale(this.locale, "__version__"); + if (typeof version === "number") + await votStorage.set("locale-version", version); + await votStorage.set("locale-lang", this.lang); + } catch (error) { + console.error( + "[VOT] [localizationProvider] failed get locale, cause:", + error, + ); + this.setLocaleFromJsonString(await votStorage.get("locale-phrases", "")); + } + } -// Get the language code from the response or the text -async function getLanguage(player, response, title, description) { - if ( - !window.location.hostname.includes("m.youtube.com") && - player?.getAudioTrack - ) { - // ! Experimental ! get lang from selected audio track if availabled - const audioTracks = player.getAudioTrack(); - const trackInfo = audioTracks?.getLanguageInfo(); // get selected track info (id === "und" if tracks are not available) - if (trackInfo?.id !== "und") { - return (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__/* .langTo6391 */ .R4)(trackInfo.id.split(".")[0]); + setLocaleFromJsonString(json) { + try { + this.locale = JSON.parse(json) ?? {}; + } catch (exception) { + console.error("[VOT] [localizationProvider]", exception); + this.locale = {}; } } - // TODO: If the audio tracks will work fine, transfer the receipt of captions to the audioTracks variable - // Check if there is an automatic caption track in the response - const captionTracks = - response?.captions?.playerCaptionsTracklistRenderer?.captionTracks; - if (captionTracks?.length) { - const autoCaption = captionTracks.find((caption) => caption.kind === "asr"); - if (autoCaption && autoCaption.languageCode) { - return (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__/* .langTo6391 */ .R4)(autoCaption.languageCode); + getFromLocale(locale, key) { + const result = key.split(".").reduce((locale, key) => { + if (typeof locale === "object" && locale) return locale[key]; + return undefined; + }, locale); + if (result === undefined) { + console.warn( + "[VOT] [localizationProvider] locale", + locale, + "doesn't contain key", + key, + ); } + return result; } - // If there is no caption track, use detect to get the language code from the description + getDefault(key) { + return this.getFromLocale(en_namespaceObject, key) ?? key; + } + + get(key) { + return ( + this.getFromLocale(this.locale, key) ?? + this.getFromLocale(en_namespaceObject, key) ?? + key + ); + } +})(); - const text = (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__/* .cleanText */ .X5)(title, description); +;// CONCATENATED MODULE: ./src/utils/VOTLocalizedError.js - _debug_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A.log(`Detecting language text: ${text}`); - return (0,_translateApis_js__WEBPACK_IMPORTED_MODULE_2__/* .detect */ .o0)(text); +class VOTLocalizedError extends Error { + constructor(message) { + super(localizationProvider.getDefault(message)); + this.name = "VOTLocalizedError"; + this.unlocalizedMessage = message; + this.localizedMessage = localizationProvider.get(message); + } } -function isMobile() { - return /^m\.youtube\.com$/.test(window.location.hostname); -} +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/client.js -function getPlayer() { - if (window.location.pathname.startsWith("/shorts/")) { - return isMobile() - ? document.querySelector("#movie_player") - : document.querySelector("#shorts-player"); - } - return document.querySelector("#movie_player"); -} -function getPlayerResponse() { - const player = getPlayer(); - if (player?.getPlayerResponse) - return player?.getPlayerResponse?.call() ?? null; - return player?.data?.playerResponse ?? null; -} -function getPlayerData() { - const player = getPlayer(); - if (player?.getVideoData) return player?.getVideoData?.call() ?? null; - return player?.data?.playerResponse?.videoDetails ?? null; -} -function getVideoVolume() { - const player = getPlayer(); - if (player?.getVolume) { - return player.getVolume.call() / 100; - } - return 1; -} -function setVideoVolume(volume) { - const player = getPlayer(); - if (player?.setVolume) { - player.setVolume(Math.round(volume * 100)); - return true; - } -} -function isMuted() { - const player = getPlayer(); - if (player?.isMuted) { - return player.isMuted.call(); - } - return false; -} -function videoSeek(video, time) { - // * TIME IN MS - _debug_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A.log("videoSeek", time); - const preTime = - getPlayer()?.getProgressState()?.seekableEnd || video.currentTime; - const finalTime = preTime - time; // we always throw it to the end of the stream - time - video.currentTime = finalTime; -} -function isMusic() { - // Нужно доработать логику - const channelName = getPlayerData().author, - titleStr = getPlayerData().title.toUpperCase(), - titleWordsList = titleStr.match(/\w+/g), - playerData = document.body.querySelector("ytd-watch-flexy")?.playerData; - return ( - [ - titleStr, - document.URL, - channelName, - playerData?.microformat?.playerMicroformatRenderer.category, - playerData?.title, - ].some((i) => i?.toUpperCase().includes("MUSIC")) || - document.body.querySelector( - "#upload-info #channel-name .badge-style-type-verified-artist", - ) || - (channelName && - /(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(channelName)) || - (channelName && - /(MUSIC|ROCK|SOUNDS|SONGS)/.test(channelName.toUpperCase())) || - (titleWordsList?.length && - [ - "🎵", - "♫", - "SONG", - "SONGS", - "SOUNDTRACK", - "LYRIC", - "LYRICS", - "AMBIENT", - "MIX", - "VEVO", - "CLIP", - "KARAOKE", - "OPENING", - "COVER", - "COVERED", - "VOCAL", - "INSTRUMENTAL", - "ORCHESTRAL", - "DUBSTEP", - "DJ", - "DNB", - "BASS", - "BEAT", - "ALBUM", - "PLAYLIST", - "DUBSTEP", - "CHILL", - "RELAX", - "CLASSIC", - "CINEMATIC", - ].some((i) => titleWordsList.includes(i))) || - [ - "OFFICIAL VIDEO", - "OFFICIAL AUDIO", - "FEAT.", - "FT.", - "LIVE RADIO", - "DANCE VER", - "HIP HOP", - "ROCK N ROLL", - "HOUR VER", - "HOURS VER", - "INTRO THEME", - ].some((i) => titleStr.includes(i)) || - (titleWordsList?.length && - [ - "OP", - "ED", - "MV", - "OST", - "NCS", - "BGM", - "EDM", - "GMV", - "AMV", - "MMD", - "MAD", - ].some((i) => titleWordsList.includes(i))) - ); +const { /* version */ "rE": version } = package_namespaceObject; +class VOTJSError extends Error { + data; + constructor(message, data = undefined) { + super(message); + this.data = data; + this.name = "VOTJSError"; + this.message = message; + } } - -function getSubtitles() { - const response = getPlayerResponse(); - let captionTracks = - response?.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? []; - captionTracks = captionTracks.reduce((result, captionTrack) => { - if ("languageCode" in captionTrack) { - const language = captionTrack?.languageCode - ? (0,_utils_js__WEBPACK_IMPORTED_MODULE_1__/* .langTo6391 */ .R4)(captionTrack?.languageCode) - : undefined; - const url = captionTrack?.url || captionTrack?.baseUrl; - language && - url && - result.push({ - source: "youtube", - language, - isAutoGenerated: captionTrack?.kind === "asr", - url: `${ - url.startsWith("http") ? url : `${window.location.origin}/${url}` - }&fmt=json3`, +class VOTClient { + host; + hostVOT; + schema; + schemaVOT; + fetch; + fetchOpts; + getVideoDataFn; + sessions = {}; + requestLang; + responseLang; + userAgent = config.userAgent; + componentVersion = config.componentVersion; + paths = { + videoTranslation: "/video-translation/translate", + videoSubtitles: "/video-subtitles/get-subtitles", + streamPing: "/stream-translation/ping-stream", + streamTranslation: "/stream-translation/translate-stream", + createSession: "/session/create", + }; + isCustomFormat(url) { + return /\.(m3u8|m4(a|v)|mpd)/.exec(url); + } + headers = { + "User-Agent": this.userAgent, + Accept: "application/x-protobuf", + "Accept-Language": "en", + "Content-Type": "application/x-protobuf", + Pragma: "no-cache", + "Cache-Control": "no-cache", + "Sec-Fetch-Mode": "no-cors", + }; + headersVOT = { + "User-Agent": `vot.js/${version}`, + "Content-Type": "application/json", + Pragma: "no-cache", + "Cache-Control": "no-cache", + }; + constructor({ host = config.host, hostVOT = config.hostVOT, fetchFn = fetchWithTimeout, fetchOpts = {}, getVideoDataFn = getVideoData, requestLang = "en", responseLang = "ru", headers = {}, } = {}) { + const schemaRe = /(http(s)?):\/\//; + const schema = schemaRe.exec(host)?.[1]; + this.host = schema ? host.replace(`${schema}://`, "") : host; + this.schema = schema ?? "https"; + const schemaVOT = schemaRe.exec(hostVOT)?.[1]; + this.hostVOT = schemaVOT ? hostVOT.replace(`${schemaVOT}://`, "") : hostVOT; + this.schemaVOT = schemaVOT ?? "https"; + this.fetch = fetchFn; + this.fetchOpts = fetchOpts; + this.getVideoDataFn = getVideoDataFn; + this.requestLang = requestLang; + this.responseLang = responseLang; + this.headers = { ...this.headers, ...headers }; + } + getOpts(body, headers = {}) { + return { + method: "POST", + headers: { + ...this.headers, + ...headers, + }, + body, + ...this.fetchOpts, + }; + } + async request(path, body, headers = {}) { + const options = this.getOpts(new Blob([body]), headers); + try { + const res = await this.fetch(`${this.schema}://${this.host}${path}`, options); + const data = await res.arrayBuffer(); + return { + success: res.status === 200, + data, + }; + } + catch (err) { + console.error("[vot.js]", err.message); + return { + success: false, + data: null, + }; + } + } + async requestVOT(path, body, headers = {}) { + const options = this.getOpts(JSON.stringify(body), { + ...this.headersVOT, + ...headers, }); + try { + console.log(`${this.schemaVOT}://${this.hostVOT}${path}`); + const res = await this.fetch(`${this.schemaVOT}://${this.hostVOT}${path}`, options); + const data = (await res.json()); + return { + success: res.status === 200, + data, + }; + } + catch (err) { + console.error("[vot.js]", err.message); + return { + success: false, + data: null, + }; + } + } + async getSession(module) { + const timestamp = getTimestamp(); + const session = this.sessions[module]; + if (session && session.timestamp + session.expires > timestamp) { + return session; + } + const { secretKey, expires, uuid } = await this.createSession(module); + this.sessions[module] = { + secretKey, + expires, + timestamp, + uuid, + }; + return this.sessions[module]; + } + async translateVideoYAImpl({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, translationHelp = null, headers = {}, }) { + const { url, duration = config.defaultDuration } = videoData; + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeTranslationRequest(url, duration, requestLang, responseLang, translationHelp); + const sign = await getSignature(body); + const res = await this.request(this.paths.videoTranslation, body, { + "Vtrans-Signature": sign, + "Sec-Vtrans-Sk": secretKey, + "Sec-Vtrans-Token": `${sign}:${uuid}:${this.paths.videoTranslation}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTLocalizedError("requestTranslationFailed"); + } + const translationData = yandexProtobuf.decodeTranslationResponse(res.data); + switch (translationData.status) { + case VideoTranslationStatus.FAILED: + throw translationData?.message ? new VOTJSError("Yandex couldn't translate video", translationData) : new VOTLocalizedError("requestTranslationFailed"); + case VideoTranslationStatus.FINISHED: + case VideoTranslationStatus.PART_CONTENT: + if (!translationData.url) { + throw new VOTLocalizedError("audioNotReceived"); + } + return { + translated: true, + url: translationData.url, + remainingTime: translationData.remainingTime ?? -1, + }; + case VideoTranslationStatus.WAITING: + return { + translated: false, + remainingTime: translationData.remainingTime, + }; + case VideoTranslationStatus.LONG_WAITING: + case VideoTranslationStatus.LONG_WAITING_2: + return { + translated: false, + remainingTime: translationData.remainingTime ?? -1, + }; + default: + console.error("[vot.js] Unknown response", translationData); + throw new VOTJSError("Unknown response from Yandex", translationData); + } + } + async translateVideoVOTImpl({ url, videoId, service, requestLang = this.requestLang, responseLang = this.responseLang, headers = {}, }) { + const votData = convertVOT(service, videoId, url); + const res = await this.requestVOT(this.paths.videoTranslation, { + provider: "yandex", + service: votData.service, + videoId: votData.videoId, + fromLang: requestLang, + toLang: responseLang, + rawVideo: url, + }, headers); + if (!res.success) { + throw new VOTLocalizedError("requestTranslationFailed", res); + } + const translationData = res.data; + switch (translationData.status) { + case "failed": + throw new VOTJSError("Yandex couldn't translate video", translationData); + case "success": + if (!translationData.translatedUrl) { + throw new VOTLocalizedError("audioNotReceived"); + } + return { + translated: true, + url: translationData.translatedUrl, + remainingTime: -1, + }; + case "waiting": + return { + translated: false, + remainingTime: translationData.remainingTime, + message: translationData.message, + }; + } + } + async translateVideo({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, translationHelp = null, headers = {}, }) { + const { url, videoId, host } = videoData; + return this.isCustomFormat(url) + ? await this.translateVideoVOTImpl({ + url, + videoId, + service: host, + requestLang, + responseLang, + headers, + }) + : await this.translateVideoYAImpl({ + videoData, + requestLang, + responseLang, + translationHelp, + headers, + }); + } + async getSubtitles({ videoData, requestLang = this.requestLang, headers = {}, }) { + const { url } = videoData; + if (this.isCustomFormat(url)) { + throw new VOTJSError("Unsupported video URL for getting subtitles"); + } + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeSubtitlesRequest(url, requestLang); + const sign = await getSignature(body); + const res = await this.request(this.paths.videoSubtitles, body, { + "Vsubs-Signature": await getSignature(body), + "Sec-Vsubs-Sk": secretKey, + "Sec-Vsubs-Token": `${sign}:${uuid}:${this.paths.videoSubtitles}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTJSError("Failed to request video subtitles", res); + } + return yandexProtobuf.decodeSubtitlesResponse(res.data); + } + async pingStream({ pingId, headers = {} }) { + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeStreamPingRequest(pingId); + const sign = await getSignature(body); + const res = await this.request(this.paths.streamPing, body, { + "Vtrans-Signature": await getSignature(body), + "Sec-Vtrans-Sk": secretKey, + "Sec-Vtrans-Token": `${sign}:${uuid}:${this.paths.streamPing}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTJSError("Failed to request stream ping", res); + } + return true; + } + async translateStream({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, headers = {}, }) { + const { url } = videoData; + if (this.isCustomFormat(url)) { + throw new VOTJSError("Unsupported video URL for getting stream translation"); + } + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeStreamRequest(url, requestLang, responseLang); + const sign = await getSignature(body); + const res = await this.request(this.paths.streamTranslation, body, { + "Vtrans-Signature": await getSignature(body), + "Sec-Vtrans-Sk": secretKey, + "Sec-Vtrans-Token": `${sign}:${uuid}:${this.paths.streamTranslation}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTJSError("Failed to request stream translation", res); + } + const translateResponse = yandexProtobuf.decodeStreamResponse(res.data); + const interval = translateResponse.interval; + switch (interval) { + case StreamInterval.NO_CONNECTION: + case StreamInterval.TRANSLATING: + return { + translated: false, + interval, + message: interval === StreamInterval.NO_CONNECTION + ? "streamNoConnectionToServer" + : "translationTakeFewMinutes", + }; + case StreamInterval.STREAMING: { + return { + translated: true, + interval, + pingId: translateResponse.pingId, + result: translateResponse.translatedInfo, + }; + } + default: + console.error("[vot.js] Unknown response", translateResponse); + throw new VOTJSError("Unknown response from Yandex", translateResponse); + } + } + async createSession(module) { + const uuid = getUUID(); + const body = yandexProtobuf.encodeYandexSessionRequest(uuid, module); + const res = await this.request(this.paths.createSession, body, { + "Vtrans-Signature": await getSignature(body), + }); + if (!res.success) { + throw new VOTJSError("Failed to request create session", res); + } + const subtitlesResponse = yandexProtobuf.decodeYandexSessionResponse(res.data); + return { + ...subtitlesResponse, + uuid, + }; } - return result; - }, []); - _debug_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A.log("youtube subtitles:", captionTracks); - return captionTracks; } - -// Get the video data from the player -async function getVideoData() { - const player = getPlayer(); - const response = getPlayerResponse(); // null in /embed - const data = getPlayerData(); - const { title } = data ?? {}; - const { shortDescription: description, isLive } = - response?.videoDetails ?? {}; - let detectedLanguage = title - ? await getLanguage(player, response, title, description) - : "en"; - detectedLanguage = _config_constants_js__WEBPACK_IMPORTED_MODULE_3__/* .availableLangs */ .xm.includes(detectedLanguage) - ? detectedLanguage - : "en"; - const videoData = { - isLive: !!isLive, - title, - description, - detectedLanguage, - }; - _debug_js__WEBPACK_IMPORTED_MODULE_0__/* ["default"] */ .A.log("youtube video data:", videoData); - console.log("[VOT] Detected language: ", videoData.detectedLanguage); - return videoData; +class VOTWorkerClient extends VOTClient { + async request(path, body, headers = {}) { + const options = this.getOpts(JSON.stringify({ + headers: { + ...this.headers, + ...headers, + }, + body: Array.from(body), + }), { + "Content-Type": "application/json", + }); + try { + const res = await this.fetch(`${this.schema}://${this.host}${path}`, options); + const data = await res.arrayBuffer(); + return { + success: res.status === 200, + data, + }; + } + catch (err) { + console.error("[vot.js]", err.message); + return { + success: false, + data: null, + }; + } + } } -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({ - isMobile, - getPlayer, - getPlayerResponse, - getPlayerData, - getVideoVolume, - getSubtitles, - getVideoData, - setVideoVolume, - videoSeek, - isMuted, - isMusic, -}); - +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/index.js -/***/ }), -/***/ "./src/yandexRequest-cloudflare.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _config_config_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/config/config.js"); -/* harmony import */ var _utils_debug_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/utils/debug.js"); -/* harmony import */ var _utils_storage_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__("./src/utils/storage.js"); -/* harmony import */ var _utils_utils_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__("./src/utils/utils.js"); -async function yandexRequest(path, body, headers, callback) { - let response; - let responseBody; - try { - _utils_debug_js__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.log("yandexRequest:", path); - // Create a fetch options object with headers and body - const options = { - method: "POST", - mode: "cors", - cache: "no-cache", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ - headers: { - ...{ - Accept: "application/x-protobuf", - "Accept-Language": "en", - "Content-Type": "application/x-protobuf", - "User-Agent": _config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .yandexUserAgent */ .Cc, - Pragma: "no-cache", - "Cache-Control": "no-cache", - "Sec-Fetch-Mode": "no-cors", - }, - ...headers, - }, - body: Array.from(body), - }), - }; - const workerHost = await _utils_storage_js__WEBPACK_IMPORTED_MODULE_2__/* .votStorage */ .d.get("proxyWorkerHost", _config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .proxyWorkerHost */ .Pm); - // Fetch the translation from the worker host - response = await (0,_utils_utils_js__WEBPACK_IMPORTED_MODULE_3__/* .GM_fetch */ .G3)(`https://${workerHost}${path}`, options); - _utils_debug_js__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.log("yandexRequest:", response.status, response); - // Get the response body as an array buffer - responseBody = await response.arrayBuffer(); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - response = { status: -1 }; - responseBody = exception; - } - // Call the callback function with the result - callback(response.status == 200, responseBody); -} -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (yandexRequest); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/consts.js +const availableLangs = [ + "auto", + "ru", + "en", + "zh", + "ko", + "lt", + "lv", + "ar", + "fr", + "it", + "es", + "de", + "ja", +]; +const availableTTS = ["ru", "en", "kk"]; -/***/ }) -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ id: moduleId, -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __webpack_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/nonce */ -/******/ (() => { -/******/ __webpack_require__.nc = undefined; -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be in strict mode. -(() => { -"use strict"; +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/subs.js +function convertToSrtTimeFormat(seconds) { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainingSeconds = Math.floor(seconds % 60); + const milliseconds = Math.floor((seconds % 1) * 1000); + return `${hours.toString().padStart(2, "0")}:${minutes + .toString() + .padStart(2, "0")}:${remainingSeconds + .toString() + .padStart(2, "0")},${milliseconds.toString().padStart(3, "0")}`; +} +function convertSubs(data, output = "srt") { + const subs = data.subtitles + .map((sub, idx) => { + const startTime = sub.startMs / 1000.0; + const endTime = (sub.startMs + sub.durationMs) / 1000.0; + const result = output === "srt" ? `${idx + 1}\n` : ""; + return (result + + `${convertToSrtTimeFormat(startTime)} --> ${convertToSrtTimeFormat(endTime)}\n${sub.text}\n\n`); + }) + .join("") + .trim(); + return output === "vtt" ? `WEBVTT\n\n${subs}` : subs; +} -;// CONCATENATED MODULE: ./src/config/alternativeUrls.js -// Sites host Invidious. I tested the performance only on invidious.kevin.rocks, youtu.be and inv.vern.cc -const sitesInvidious = [ - "invidious.snopyta.org", - "yewtu.be", - "invidious.kavin.rocks", - "vid.puffyan.us", - "invidious.namazso.eu", - "inv.riverside.rocks", - "yt.artemislena.eu", - "invidious.flokinet.to", - "invidious.esmailelbob.xyz", - "y.com.sb", - "invidious.nerdvpn.de", - "inv.vern.cc", - "invidious.slipfox.xyz", - "invidio.xamh.de", - "invidious.dhusch.de", -]; +;// CONCATENATED MODULE: ./src/config/config.js +// CONFIGURATION +const workerHost = "api.browser.yandex.ru"; +const m3u8ProxyHost = "m3u8-proxy.toil.cc"; // used for streaming +const proxyWorkerHost = "vot-worker.toil.cc"; // used for cloudflare version +const votBackendUrl = "https://vot-api.toil.cc/v1"; -// Sites host Piped. I tested the performance only on piped.video -const sitesPiped = [ - "piped.video", - "piped.tokhmi.xyz", - "piped.moomoo.me", - "piped.syncpundit.io", - "piped.mha.fi", - "watch.whatever.social", - "piped.garudalinux.org", - "efy.piped.pages.dev", - "watch.leptons.xyz", - "piped.lunar.icu", - "yt.dc09.ru", - "piped.mint.lgbt", - "il.ax", - "piped.privacy.com.de", - "piped.esmailelbob.xyz", - "piped.projectsegfau.lt", - "piped.in.projectsegfau.lt", - "piped.us.projectsegfau.lt", - "piped.privacydev.net", - "piped.palveluntarjoaja.eu", - "piped.smnz.de", - "piped.adminforge.de", - "piped.qdi.fi", - "piped.hostux.net", - "piped.chauvet.pro", - "piped.jotoma.de", - "piped.pfcd.me", - "piped.frontendfriendly.xyz", -]; +const defaultAutoVolume = 0.15; // 0.0 - 1.0 (0% - 100%) - default volume of the video with the translation +const maxAudioVolume = 900; +const defaultTranslationService = "yandex"; +const defaultDetectService = "yandex"; -const sitesProxiTok = [ - "proxitok.pabloferreiro.es", - "proxitok.pussthecat.org", - "tok.habedieeh.re", - "proxitok.esmailelbob.xyz", - "proxitok.privacydev.net", - "tok.artemislena.eu", - "tok.adminforge.de", - "tik.hostux.net", // maybe instance doesn't working - "tt.vern.cc", - "cringe.whatever.social", - "proxitok.lunar.icu", - "proxitok.privacy.com.de", // maybe instance doesn't working -]; +const detectUrls = { + yandex: "https://translate.toil.cc/detect", + rustServer: "https://rust-server-531j.onrender.com/detect", +}; -// Sites host Peertube. I tested the performance only on dalek.zone and tube.shanti.cafe -const sitesPeertube = [ - "peertube.1312.media", - "tube.shanti.cafe", - "bee-tube.fr", - "video.sadmin.io", - "dalek.zone", - "review.peertube.biz", - "peervideo.club", - "tube.la-dina.net", - "peertube.tmp.rcp.tf", - "peertube.su", -]; +const translateUrls = { + yandex: "https://translate.toil.cc/translate", + deepl: "https://translate-deepl.toil.cc/translate", +}; -// EXTERNAL MODULE: ./src/config/config.js -var config = __webpack_require__("./src/config/config.js"); -// EXTERNAL MODULE: ./src/config/constants.js -var constants = __webpack_require__("./src/config/constants.js"); -// EXTERNAL MODULE: ./src/localization/localizationProvider.js + 1 modules -var localizationProvider = __webpack_require__("./src/localization/localizationProvider.js"); // EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js var injectStylesIntoStyleTag = __webpack_require__("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"); var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag); @@ -2142,6 +3939,8 @@ var update = injectStylesIntoStyleTag_default()(main/* default */.A, options); ;// CONCATENATED MODULE: ./src/ui.js +const undefinedPhrase = "#UNDEFINED"; + function createHeader(html, level = 4) { const header = document.createElement("vot-block"); header.classList.add("vot-header"); @@ -2438,8 +4237,7 @@ function createVOTSelectLabel(text) { } function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { - const onSelectCb = options.onSelectCb || function () {}; - const labelElement = options.labelElement || ""; + const { onSelectCb = function () {}, labelElement = "" } = options; let selectedItems = []; const container = document.createElement("vot-block"); @@ -2508,7 +4306,7 @@ function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { // search logic const votSearchLangTextfield = createTextfield( - localizationProvider/* localizationProvider */.j.get("searchField"), + localizationProvider.get("searchField"), ); votSearchLangTextfield.input.oninput = (e) => { @@ -2571,14 +4369,16 @@ function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { } function createVOTLanguageSelect(options) { - const fromTitle = options.fromTitle || "#UNDEFINED"; - const fromDialogTitle = options.fromDialogTitle || "#UNDEFINED"; - const fromItems = options.fromItems || []; - const fromOnSelectCB = options.fromOnSelectCB || function () {}; - const toTitle = options.toTitle || "#UNDEFINED"; - const toDialogTitle = options.toDialogTitle || "#UNDEFINED"; - const toItems = options.toItems || []; - const toOnSelectCB = options.toOnSelectCB || function () {}; + const { + fromTitle = undefinedPhrase, + fromDialogTitle = undefinedPhrase, + fromItems = [], + fromOnSelectCB = null, + toTitle = undefinedPhrase, + toDialogTitle = undefinedPhrase, + toItems = [], + toOnSelectCB = null, + } = options; const container = document.createElement("vot-block"); container.classList.add("vot-lang-select"); @@ -2624,22 +4424,6 @@ function createVOTLanguageSelect(options) { updateSlider, }); -;// CONCATENATED MODULE: ./src/utils/VOTLocalizedError.js - - -class VOTLocalizedError extends Error { - constructor(message) { - super(localizationProvider/* localizationProvider */.j.getDefault(message)); - this.name = "VOTLocalizedError"; - this.unlocalizedMessage = message; - this.localizedMessage = localizationProvider/* localizationProvider */.j.get(message); - } -} - -// EXTERNAL MODULE: ./src/utils/debug.js -var debug = __webpack_require__("./src/utils/debug.js"); -// EXTERNAL MODULE: ./src/utils/utils.js -var utils = __webpack_require__("./src/utils/utils.js"); ;// CONCATENATED MODULE: ./src/utils/volume.js // element - audio / video element function syncVolume(element, sliderVolume, otherSliderVolume, tempVolume) { @@ -2671,386 +4455,436 @@ function syncVolume(element, sliderVolume, otherSliderVolume, tempVolume) { -;// CONCATENATED MODULE: ./src/yandexProtobuf.js -// coursera & udemy translation help object -const VideoTranslationHelpObject = new protobuf.Type( - "VideoTranslationHelpObject", -) - .add(new protobuf.Field("target", 1, "string")) // video_file_url or subtitles_file_url - .add(new protobuf.Field("targetUrl", 2, "string")); // url to video_file or url to subtitles - -const VideoTranslationRequest = new protobuf.Type("VideoTranslationRequest") - .add(new protobuf.Field("url", 3, "string")) - .add(new protobuf.Field("deviceId", 4, "string")) // used in mobile version - .add(new protobuf.Field("firstRequest", 5, "bool")) // true for the first request, false for subsequent ones - .add(new protobuf.Field("duration", 6, "double")) - .add(new protobuf.Field("unknown2", 7, "int32")) // 1 1 - .add(new protobuf.Field("language", 8, "string")) // source language code - .add(new protobuf.Field("forceSourceLang", 9, "bool")) // 0 - auto detected by yabrowser, 1 - user set his own lang by dropdown - .add(new protobuf.Field("unknown4", 10, "int32")) // 0 0 - .add( - new protobuf.Field( - "translationHelp", - 11, - "VideoTranslationHelpObject", - "repeated", - ), - ) // array for translation assistance ([0] -> {2: link to video, 1: "video_file_url"}, [1] -> {2: link to subtitles, 1: "subtitles_file_url"}) - .add(new protobuf.Field("responseLanguage", 14, "string")) - .add(new protobuf.Field("unknown5", 15, "int32")) // 0 - .add(new protobuf.Field("unknown6", 16, "int32")) // 1 - .add(new protobuf.Field("bypassCache", 17, "bool")); // ? maybe they have some kind of bypass limiter from one IP, because after one such request it stopped working - -const VideoSubtitlesRequest = new protobuf.Type("VideoSubtitlesRequest") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("language", 2, "string")); // source language code - -const VideoStreamRequest = new protobuf.Type("VideoStreamRequest") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("language", 2, "string")) - .add(new protobuf.Field("responseLanguage", 3, "string")); - -const VideoStreamPingRequest = new protobuf.Type("VideoStreamPingRequest").add( - new protobuf.Field("pingId", 1, "int32"), -); +;// CONCATENATED MODULE: ./src/utils/translateApis.js -const VideoTranslationResponse = new protobuf.Type("VideoTranslationResponse") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("duration", 2, "double")) - .add(new protobuf.Field("status", 4, "int32")) - .add(new protobuf.Field("remainingTime", 5, "int32")) // secs before translation (used as interval before next request in yaBrowser) - .add(new protobuf.Field("unknown0", 6, "int32")) // unknown 0 (1st request) -> 10 (2nd, 3th and etc requests). (if status is 0) - .add(new protobuf.Field("translationId", 7, "string")) - .add(new protobuf.Field("language", 8, "string")) // detected language (if the wrong one is set) - .add(new protobuf.Field("message", 9, "string")); - -const VideoSubtitlesObject = new protobuf.Type("VideoSubtitlesObject") - .add(new protobuf.Field("language", 1, "string")) - .add(new protobuf.Field("url", 2, "string")) - .add(new protobuf.Field("unknown2", 3, "int32")) - .add(new protobuf.Field("translatedLanguage", 4, "string")) - .add(new protobuf.Field("translatedUrl", 5, "string")) - .add(new protobuf.Field("unknown5", 6, "int32")) - .add(new protobuf.Field("unknown6", 7, "int32")); - -const VideoSubtitlesResponse = new protobuf.Type("VideoSubtitlesResponse") - .add(new protobuf.Field("waiting", 1, "int32")) // 0 - finished/failed | 1 - waiting result (1 - ~10min, maybe more) - .add(new protobuf.Field("subtitles", 2, "VideoSubtitlesObject", "repeated")); - -const VideoStreamObject = new protobuf.Type("VideoStreamObject") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("timestamp", 2, "int64")); // timestamp in ms (probably means the time of 1 request to translate the stream) - -const VideoStreamResponse = new protobuf.Type("VideoStreamResponse") - .add(new protobuf.Field("interval", 1, "int32")) // 20s - streaming, 10s - translating, 0s - there is no connection with the server (the broadcast is finished or deleted) - .add(new protobuf.Field("translatedInfo", 2, "VideoStreamObject")) - .add(new protobuf.Field("pingId", 3, "int32")); - -// /session/create -const YandexSessionRequest = new protobuf.Type("YandexSessionRequest") - .add(new protobuf.Field("uuid", 1, "string")) - .add(new protobuf.Field("module", 2, "string")); - -const YandexSessionResponse = new protobuf.Type("YandexSessionResponse") - .add(new protobuf.Field("sign", 1, "string")) - .add(new protobuf.Field("expires", 2, "int32")); - -// * Yandex has been skipping any translation streams for a long time (whitelist always return true) -// * Most likely, it is already outdated and will not be used -// const VideoWhitelistStreamRequest = new protobuf.Type("VideoWhitelistStreamRequest") -// .add(new protobuf.Field("url", 1, "string")) -// .add(new protobuf.Field("deviceId", 4, "string")) - -// const VideoWhitelistStreamResponse = new protobuf.Type("VideoWhitelistStreamResponse") -// .add(new protobuf.Field("inWhitelist", 1, "bool")) - -// Create a root namespace and add the types -const root = new protobuf.Root() - .define("yandex") - .add(VideoTranslationHelpObject) - .add(VideoTranslationRequest) - .add(VideoTranslationResponse) - .add(VideoSubtitlesRequest) - .add(VideoSubtitlesObject) - .add(VideoSubtitlesResponse) - .add(VideoStreamPingRequest) - .add(VideoStreamRequest) - .add(VideoStreamObject) - .add(VideoStreamResponse) - .add(YandexSessionRequest) - .add(YandexSessionResponse); - -// Export the encoding and decoding functions -const yandexProtobuf = { - encodeTranslationRequest( - url, - duration, - requestLang, - responseLang, - translationHelp, - ) { - return root.VideoTranslationRequest.encode({ - url, - firstRequest: true, - duration, - unknown2: 1, - language: requestLang, - forceSourceLang: false, - unknown4: 0, - translationHelp, - responseLanguage: responseLang, - unknown5: 0, - unknown6: 1, - bypassCache: false, - }).finish(); - }, - decodeTranslationResponse(response) { - return root.VideoTranslationResponse.decode(new Uint8Array(response)); - }, - encodeSubtitlesRequest(url, requestLang) { - return root.VideoSubtitlesRequest.encode({ - url, - language: requestLang, - }).finish(); - }, - decodeSubtitlesResponse(response) { - return root.VideoSubtitlesResponse.decode(new Uint8Array(response)); - }, - encodeStreamPingRequest(pingId) { - return root.VideoStreamPingRequest.encode({ - pingId, - }).finish(); - }, - encodeStreamRequest(url, requestLang, responseLang) { - return root.VideoStreamRequest.encode({ - url, - language: requestLang, - responseLanguage: responseLang, - }).finish(); + + +const HTTP_TIMEOUT = 3000; + +async function translateApis_fetchWithTimeout(url, options = {}) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), HTTP_TIMEOUT); + + try { + return await fetch(url, { + ...options, + signal: controller.signal, + }); + } catch (error) { + console.error("Fetch timed-out. Error:", error); + return error; + } finally { + clearTimeout(timeoutId); + } +} + +const YandexTranslateAPI = { + async translate(text, lang) { + // Limit: 10k symbols + // + // Lang examples: + // en-ru, uk-ru, ru-en... + // ru, en (instead of auto-ru, auto-en) + + try { + const response = await translateApis_fetchWithTimeout( + `${translateUrls.yandex}?${new URLSearchParams({ + text, + lang, + })}`, + ); + + if (response instanceof Error) { + throw response; + } + + const content = await response.json(); + + if (content.code !== 200) { + throw content.message; + } + + return content.text[0]; + } catch (error) { + console.error("Error translating text:", error); + return text; + } }, - decodeStreamResponse(response) { - return root.VideoStreamResponse.decode(new Uint8Array(response)); + + async detect(text) { + // Limit: 10k symbols + try { + const response = await translateApis_fetchWithTimeout( + `${detectUrls.yandex}?${new URLSearchParams({ + text, + })}`, + ); + + if (response instanceof Error) { + throw response; + } + + const content = await response.json(); + if (content.code !== 200) { + throw content.message; + } + + return content.lang ?? "en"; + } catch (error) { + console.error("Error getting lang from text:", error); + return "en"; + } }, - encodeYandexSessionRequest(uuid, module) { - return root.YandexSessionRequest.encode({ - uuid, - module, - }).finish(); +}; + +const RustServerAPI = { + async detect(text) { + try { + const response = await fetch(detectUrls.rustServer, { + method: "POST", + body: text, + }); + + if (response instanceof Error) { + throw response; + } + + return await response.text(); + } catch (error) { + console.error("Error getting lang from text:", error); + return "en"; + } }, - decodeYandexSessionResponse(response) { - return root.YandexSessionResponse.decode(new Uint8Array(response)); +}; + +const DeeplServerAPI = { + async translate(text, fromLang = "auto", toLang = "ru") { + try { + const response = await translateApis_fetchWithTimeout(translateUrls.deepl, { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + text, + source_lang: fromLang, + target_lang: toLang, + }), + }); + + if (response instanceof Error) { + throw response; + } + + const content = await response.json(); + + if (content.code !== 200) { + throw content.message; + } + + return content.data; + } catch (error) { + console.error("Error translating text:", error); + return text; + } }, }; -// EXTERNAL MODULE: ./node_modules/bowser/es5.js -var es5 = __webpack_require__("./node_modules/bowser/es5.js"); -;// CONCATENATED MODULE: ./src/getUUID.js -function getUUID() { - const hexDigits = "0123456789ABCDEF"; - let uuid = ""; - for (let i = 0; i < 32; i++) { - const randomDigit = Math.floor(Math.random() * 16); - uuid += hexDigits[randomDigit]; +async function translate(text, fromLang = "", toLang = "ru") { + const service = await votStorage.get( + "translationService", + defaultTranslationService, + ); + switch (service) { + case "yandex": { + const langPair = fromLang && toLang ? `${fromLang}-${toLang}` : toLang; + return await YandexTranslateAPI.translate(text, langPair); + } + case "deepl": { + return await DeeplServerAPI.translate(text, fromLang, toLang); + } + default: + return text; } - return uuid; } +async function detect(text) { + const service = await votStorage.get("detectService", defaultDetectService); + switch (service) { + case "yandex": + return await YandexTranslateAPI.detect(text); + case "rust-server": + return await RustServerAPI.detect(text); + default: + return "en"; + } +} - -;// CONCATENATED MODULE: ./src/getSignature.js - - -// Create a key from the HMAC secret -const CryptoKey = window.crypto.subtle.importKey( - "raw", - new TextEncoder().encode(config/* yandexHmacKey */.S7), - { name: "HMAC", hash: { name: "SHA-256" } }, - false, - ["sign", "verify"], +const translateServices = Object.keys(translateUrls); +const detectServices = Object.keys(detectUrls).map((k) => + k === "rustServer" ? "rust-server" : k, ); -async function getSignature(body) { - const key = await CryptoKey; - return new Uint8Array( - // Sign the body with the key - await window.crypto.subtle.sign("HMAC", key, body), - // Convert the signature to a hex string - ).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); -} +;// CONCATENATED MODULE: ./src/utils/youtubeUtils.js -;// CONCATENATED MODULE: ./src/rsp.js -// Request stream ping from Yandex API -async function requestStreamPing(pingId, callback) { - try { - debug/* default */.A.log("requestStreamPing"); - // ! CURRENT CLOUDFLARE WORKER DOESN'T SUPPORT STREAM TRANSLATIONS - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest-cloudflare.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeStreamPingRequest(pingId); - // Send the request - await yandexRequest( - "/stream-translation/ping-stream", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); +// Get the language code from the response or the text +async function getLanguage(player, response, title, description) { + if ( + !window.location.hostname.includes("m.youtube.com") && + player?.getAudioTrack + ) { + // ! Experimental ! get lang from selected audio track if availabled + const audioTracks = player.getAudioTrack(); + const trackInfo = audioTracks?.getLanguageInfo(); // get selected track info (id === "und" if tracks are not available) + if (trackInfo?.id !== "und") { + return langTo6391(trackInfo.id.split(".")[0]); + } } -} - -/* harmony default export */ const rsp = (requestStreamPing); -;// CONCATENATED MODULE: ./src/rst.js + // TODO: If the audio tracks will work fine, transfer the receipt of captions to the audioTracks variable + // Check if there is an automatic caption track in the response + const captionTracks = + response?.captions?.playerCaptionsTracklistRenderer?.captionTracks; + if (captionTracks?.length) { + const autoCaption = captionTracks.find((caption) => caption.kind === "asr"); + if (autoCaption && autoCaption.languageCode) { + return langTo6391(autoCaption.languageCode); + } + } + // If there is no caption track, use detect to get the language code from the description + const text = cleanText(title, description); + utils_debug.log(`Detecting language text: ${text}`); + return detect(text); +} -// Request stream translation from Yandex API -async function requestStreamTranslation( - url, - requestLang, - responseLang, - callback, -) { - try { - debug/* default */.A.log("requestStreamTranslation"); - // ! CURRENT CLOUDFLARE WORKER DOESN'T SUPPORT STREAM TRANSLATIONS - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest-cloudflare.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeStreamRequest( - url, - requestLang, - responseLang, - ); - // Send the request - await yandexRequest( - "/stream-translation/translate-stream", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } +function isMobile() { + return /^m\.youtube\.com$/.test(window.location.hostname); } -/* harmony default export */ const rst = (requestStreamTranslation); +function getPlayer() { + if (window.location.pathname.startsWith("/shorts/")) { + return isMobile() + ? document.querySelector("#movie_player") + : document.querySelector("#shorts-player"); + } -;// CONCATENATED MODULE: ./src/rvt.js + return document.querySelector("#movie_player"); +} +function getPlayerResponse() { + const player = getPlayer(); + if (player?.getPlayerResponse) + return player?.getPlayerResponse?.call() ?? null; + return player?.data?.playerResponse ?? null; +} +function getPlayerData() { + const player = getPlayer(); + if (player?.getVideoData) return player?.getVideoData?.call() ?? null; + return player?.data?.playerResponse?.videoDetails ?? null; +} +function getVideoVolume() { + const player = getPlayer(); + if (player?.getVolume) { + return player.getVolume.call() / 100; + } + return 1; +} -// Request video translation from Yandex API -async function requestVideoTranslation( - url, - duration, - requestLang, - responseLang, - translationHelp, - callback, -) { - try { - debug/* default */.A.log("requestVideoTranslation"); - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest-cloudflare.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeTranslationRequest( - url, - duration, - requestLang, - responseLang, - translationHelp, - ); - // Send the request - await yandexRequest( - // "/stream-translation/whitelist-stream", - // "/stream-translation/translate-stream", - "/video-translation/translate", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); +function setVideoVolume(volume) { + const player = getPlayer(); + if (player?.setVolume) { + player.setVolume(Math.round(volume * 100)); + return true; } } -/* harmony default export */ const rvt = (requestVideoTranslation); +function isMuted() { + const player = getPlayer(); + if (player?.isMuted) { + return player.isMuted.call(); + } -// EXTERNAL MODULE: ./src/utils/youtubeUtils.js -var youtubeUtils = __webpack_require__("./src/utils/youtubeUtils.js"); -;// CONCATENATED MODULE: ./src/rvs.js + return false; +} +function videoSeek(video, time) { + // * TIME IN MS + utils_debug.log("videoSeek", time); + const preTime = + getPlayer()?.getProgressState()?.seekableEnd || video.currentTime; + const finalTime = preTime - time; // we always throw it to the end of the stream - time + video.currentTime = finalTime; +} +function isMusic() { + // Нужно доработать логику + const channelName = getPlayerData().author, + titleStr = getPlayerData().title.toUpperCase(), + titleWordsList = titleStr.match(/\w+/g), + playerData = document.body.querySelector("ytd-watch-flexy")?.playerData; + return ( + [ + titleStr, + document.URL, + channelName, + playerData?.microformat?.playerMicroformatRenderer.category, + playerData?.title, + ].some((i) => i?.toUpperCase().includes("MUSIC")) || + document.body.querySelector( + "#upload-info #channel-name .badge-style-type-verified-artist", + ) || + (channelName && + /(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(channelName)) || + (channelName && + /(MUSIC|ROCK|SOUNDS|SONGS)/.test(channelName.toUpperCase())) || + (titleWordsList?.length && + [ + "🎵", + "♫", + "SONG", + "SONGS", + "SOUNDTRACK", + "LYRIC", + "LYRICS", + "AMBIENT", + "MIX", + "VEVO", + "CLIP", + "KARAOKE", + "OPENING", + "COVER", + "COVERED", + "VOCAL", + "INSTRUMENTAL", + "ORCHESTRAL", + "DUBSTEP", + "DJ", + "DNB", + "BASS", + "BEAT", + "ALBUM", + "PLAYLIST", + "DUBSTEP", + "CHILL", + "RELAX", + "CLASSIC", + "CINEMATIC", + ].some((i) => titleWordsList.includes(i))) || + [ + "OFFICIAL VIDEO", + "OFFICIAL AUDIO", + "FEAT.", + "FT.", + "LIVE RADIO", + "DANCE VER", + "HIP HOP", + "ROCK N ROLL", + "HOUR VER", + "HOURS VER", + "INTRO THEME", + ].some((i) => titleStr.includes(i)) || + (titleWordsList?.length && + [ + "OP", + "ED", + "MV", + "OST", + "NCS", + "BGM", + "EDM", + "GMV", + "AMV", + "MMD", + "MAD", + ].some((i) => titleWordsList.includes(i))) + ); +} +function getSubtitles() { + const response = getPlayerResponse(); + let captionTracks = + response?.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? []; + captionTracks = captionTracks.reduce((result, captionTrack) => { + if ("languageCode" in captionTrack) { + const language = captionTrack?.languageCode + ? langTo6391(captionTrack?.languageCode) + : undefined; + const url = captionTrack?.url || captionTrack?.baseUrl; + language && + url && + result.push({ + source: "youtube", + language, + isAutoGenerated: captionTrack?.kind === "asr", + url: `${ + url.startsWith("http") ? url : `${window.location.origin}/${url}` + }&fmt=json3`, + }); + } + return result; + }, []); + utils_debug.log("youtube subtitles:", captionTracks); + return captionTracks; +} -// Request video subtitles from Yandex API -async function requestVideoSubtitles(url, requestLang, callback) { - try { - debug/* default */.A.log("requestVideoSubtitles"); - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest-cloudflare.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeSubtitlesRequest(url, requestLang); - // Send the request - await yandexRequest( - "/video-subtitles/get-subtitles", - body, - { - "Vsubs-Signature": await getSignature(body), - "Sec-Vsubs-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } +// Get the video data from the player +async function youtubeUtils_getVideoData() { + const player = getPlayer(); + const response = getPlayerResponse(); // null in /embed + const data = getPlayerData(); + const { title } = data ?? {}; + const { shortDescription: description, isLive } = + response?.videoDetails ?? {}; + let detectedLanguage = title + ? await getLanguage(player, response, title, description) + : "en"; + detectedLanguage = availableLangs.includes(detectedLanguage) + ? detectedLanguage + : "en"; + const videoData = { + isLive: !!isLive, + title, + description, + detectedLanguage, + }; + utils_debug.log("youtube video data:", videoData); + console.log("[VOT] Detected language: ", videoData.detectedLanguage); + return videoData; } -/* harmony default export */ const rvs = (requestVideoSubtitles); +/* harmony default export */ const youtubeUtils = ({ + isMobile, + getPlayer, + getPlayerResponse, + getPlayerData, + getVideoVolume, + getSubtitles, + getVideoData: youtubeUtils_getVideoData, + setVideoVolume, + videoSeek, + isMuted, + isMusic, +}); ;// CONCATENATED MODULE: ./src/subtitles.js - - - function formatYandexSubtitlesTokens(line) { const lineEndMs = line.startMs + line.durationMs; return line.tokens.reduce((result, token, index) => { @@ -3180,31 +5014,6 @@ function formatYoutubeSubtitles(subtitles) { return result; } -function convertToSrtTimeFormat(seconds) { - let hours = Math.floor(seconds / 3600); - let minutes = Math.floor((seconds % 3600) / 60); - let remainingSeconds = Math.floor(seconds % 60); - let milliseconds = Math.floor((seconds % 1) * 1000); - - return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")},${milliseconds.toString().padStart(3, "0")}`; -} - -function jsonToSrt(jsonData) { - let srtContent = ""; - let index = 1; - for (const entry of jsonData.subtitles) { - let startTime = entry.startMs / 1000.0; - let endTime = (entry.startMs + entry.durationMs) / 1000.0; - - srtContent += `${index}\n`; - srtContent += `${convertToSrtTimeFormat(startTime)} --> ${convertToSrtTimeFormat(endTime)}\n`; - srtContent += `${entry.text}\n\n`; - index++; - } - - return srtContent.trim(); -} - async function fetchSubtitles(subtitlesObject) { const timeoutPromise = new Promise((resolve) => setTimeout( @@ -3219,7 +5028,7 @@ async function fetchSubtitles(subtitlesObject) { const fetchPromise = (async () => { try { - const response = await (0,utils/* GM_fetch */.G3)(subtitlesObject.url); + const response = await GM_fetch(subtitlesObject.url); return await response.json(); } catch (error) { console.error("[VOT] Failed to fetch subtitles. Reason:", error); @@ -3241,9 +5050,9 @@ async function fetchSubtitles(subtitlesObject) { return subtitles; } -async function getSubtitles(site, videoId, requestLang) { - const ytSubtitles = - site.host === "youtube" ? youtubeUtils/* default */.A.getSubtitles() : []; +async function subtitles_getSubtitles(client, videoData) { + const { host, url, requestLang, videoId, duration } = videoData; + const ytSubtitles = host === "youtube" ? youtubeUtils.getSubtitles() : []; let resolved = false; const yaSubtitles = await Promise.race([ new Promise((resolve) => { @@ -3255,23 +5064,25 @@ async function getSubtitles(site, videoId, requestLang) { }, 5000); }), new Promise((resolve) => { - rvs( - `${site.url}${videoId}`, - requestLang, - (success, response) => { - debug/* default */.A.log("[exec callback] Requesting video subtitles", videoId); - - if (!success) { + client + .getSubtitles({ + videoData: { + host, + url, + videoId, + duration, + }, + requestLang, + }) + .then((res) => { + console.log("[VOT] Subtitles response: ", res); + if (res.waiting) { console.error("[VOT] Failed get yandex subtitles"); resolved = true; resolve([]); } - const subtitlesResponse = - yandexProtobuf.decodeSubtitlesResponse(response); - console.log("[VOT] Subtitles response: ", subtitlesResponse); - - let subtitles = subtitlesResponse.subtitles ?? []; + let subtitles = res.subtitles ?? []; subtitles = subtitles.reduce((result, yaSubtitlesObject) => { if ( yaSubtitlesObject.language && @@ -3303,10 +5114,10 @@ async function getSubtitles(site, videoId, requestLang) { }, []); resolved = true; resolve(subtitles); - }, - ); + }); }), ]); + const subtitles = [...yaSubtitles, ...ytSubtitles].sort((a, b) => { if (a.source !== b.source) { // sort by source @@ -3314,10 +5125,10 @@ async function getSubtitles(site, videoId, requestLang) { } if ( a.language !== b.language && - (a.language === utils/* lang */.vV || b.language === utils/* lang */.vV) + (a.language === lang || b.language === lang) ) { // sort by user language - return a.language === utils/* lang */.vV ? -1 : 1; + return a.language === lang ? -1 : 1; } if (a.source === "yandex") { // sort by translation @@ -3343,6 +5154,7 @@ async function getSubtitles(site, videoId, requestLang) { } return 0; }); + console.log("[VOT] subtitles list", subtitles); return subtitles; } @@ -3550,7 +5362,7 @@ async function getCourseData(courseId) { return await response.json(); } -async function getVideoData() { +async function coursehunterUtils_getVideoData() { const courseId = window.course_id ?? document.querySelector('input[name="course_id"]')?.value; @@ -3565,7 +5377,7 @@ async function getVideoData() { const { file: videoUrl, duration } = lessonData; - debug/* default */.A.log("coursehunter course data:", courseData); + utils_debug.log("coursehunter course data:", courseData); return { url: videoUrl, duration, @@ -3573,7 +5385,7 @@ async function getVideoData() { } /* harmony default export */ const coursehunterUtils = ({ - getVideoData, + getVideoData: coursehunterUtils_getVideoData, }); ;// CONCATENATED MODULE: ./src/utils/courseraUtils.js @@ -3581,6 +5393,7 @@ async function getVideoData() { + async function courseraUtils_getCourseData(courseId) { const response = await fetch( `https://www.coursera.org/api/onDemandCourses.v1/${courseId}`, @@ -3591,13 +5404,13 @@ async function courseraUtils_getCourseData(courseId) { function getSubtitlesFileURL(captions, detectedLanguage, responseLang) { let subtitle = captions?.find( - (caption) => (0,utils/* langTo6391 */.R4)(caption.srclang) === detectedLanguage, + (caption) => langTo6391(caption.srclang) === detectedLanguage, ); if (!subtitle) { subtitle = captions?.find( - (caption) => (0,utils/* langTo6391 */.R4)(caption.srclang) === responseLang, + (caption) => langTo6391(caption.srclang) === responseLang, ) || captions?.[0]; } @@ -3611,18 +5424,18 @@ function getVideoFileURL(sources) { return source?.src; } -function getPlayerData() { - return getPlayer()?.player; +function courseraUtils_getPlayerData() { + return courseraUtils_getPlayer()?.player; } -function getPlayer() { +function courseraUtils_getPlayer() { return document.querySelector(".vjs-v6"); } // Get the video data from the player async function courseraUtils_getVideoData(responseLang = "en") { let translationHelp = null; - const data = getPlayerData(); + const data = courseraUtils_getPlayerData(); const { duration } = data?.cache_ || {}; const { courseId, tracks, sources } = data?.options_ || {}; @@ -3631,9 +5444,9 @@ async function courseraUtils_getVideoData(responseLang = "en") { const courseData = await courseraUtils_getCourseData(courseId); let detectedLanguage = courseData?.primaryLanguageCodes?.[0]; - detectedLanguage = detectedLanguage ? (0,utils/* langTo6391 */.R4)(detectedLanguage) : "en"; + detectedLanguage = detectedLanguage ? langTo6391(detectedLanguage) : "en"; - if (!constants/* availableLangs */.xm.includes(detectedLanguage)) { + if (!availableLangs.includes(detectedLanguage)) { detectedLanguage = "en"; } @@ -3674,14 +5487,14 @@ async function courseraUtils_getVideoData(responseLang = "en") { translationHelp, }; - debug/* default */.A.log("coursera video data:", videoData); + utils_debug.log("coursera video data:", videoData); console.log("[VOT] Detected language: ", videoData.detectedLanguage); return videoData; } /* harmony default export */ const courseraUtils = ({ - getPlayer, - getPlayerData, + getPlayer: courseraUtils_getPlayer, + getPlayerData: courseraUtils_getPlayerData, getVideoData: courseraUtils_getVideoData, }); @@ -3691,6 +5504,7 @@ async function courseraUtils_getVideoData(responseLang = "en") { + const udemyAPIURL = "https://www.udemy.com/api-2.0"; const accessTokenLife = 2_592_000_000; // 30 days @@ -3713,7 +5527,7 @@ function checkUdemyTokenExpire(expires) { async function getLectureData(udemyData, courseId, lectureId) { // reference: https://greasyfork.org/ru/scripts/422576-udemy-subtitle-downloader-v3/code if (!checkUdemyTokenExpire(udemyData.expires) || !udemyData.accessToken) { - console.error(localizationProvider/* localizationProvider */.j.get("udemyAccessTokenExpired")); + console.error(localizationProvider.get("udemyAccessTokenExpired")); return undefined; } @@ -3736,13 +5550,13 @@ async function getLectureData(udemyData, courseId, lectureId) { function udemyUtils_getSubtitlesFileURL(captions, detectedLanguage, responseLang) { let subtitle = captions?.find( - (caption) => (0,utils/* langTo6391 */.R4)(caption.locale_id) === detectedLanguage, + (caption) => langTo6391(caption.locale_id) === detectedLanguage, ); if (!subtitle) { subtitle = captions?.find( - (caption) => (0,utils/* langTo6391 */.R4)(caption.locale_id) === responseLang, + (caption) => langTo6391(caption.locale_id) === responseLang, ) || captions?.[0]; } @@ -3766,7 +5580,7 @@ function getModuleData() { ".ud-app-loader[data-module-id='course-taking']", )?.dataset?.moduleArgs; if (!moduleArgs) { - console.error(localizationProvider/* localizationProvider */.j.get("udemyModuleArgsNotFound")); + console.error(localizationProvider.get("udemyModuleArgsNotFound")); return {}; } return JSON.parse(moduleArgs); @@ -3789,24 +5603,24 @@ function getVideoURLFromPlayer() { async function udemyUtils_getVideoData(udemyData, responseLang = "en") { let translationHelp = null; const data = udemyUtils_getPlayerData(); - debug/* default */.A.log("udemyData", udemyData); + utils_debug.log("udemyData", udemyData); const moduleData = getModuleData(); - debug/* default */.A.log("moduleData: ", moduleData); + utils_debug.log("moduleData: ", moduleData); const courseId = moduleData.courseId; const lectureId = getLectureId(); - debug/* default */.A.log(`CourseId: ${courseId}, lectureId: ${lectureId}`); + utils_debug.log(`CourseId: ${courseId}, lectureId: ${lectureId}`); const courseLang = await getCourseLang(courseId); - debug/* default */.A.log("courseLang Data:", courseLang); + utils_debug.log("courseLang Data:", courseLang); const lectureData = await getLectureData(udemyData, courseId, lectureId); console.log("lecture Data:", lectureData); let detectedLanguage = courseLang?.locale?.locale; - detectedLanguage = detectedLanguage ? (0,utils/* langTo6391 */.R4)(detectedLanguage) : "en"; + detectedLanguage = detectedLanguage ? langTo6391(detectedLanguage) : "en"; - if (!constants/* availableLangs */.xm.includes(detectedLanguage)) { + if (!availableLangs.includes(detectedLanguage)) { detectedLanguage = "en"; } @@ -3852,7 +5666,7 @@ async function udemyUtils_getVideoData(udemyData, responseLang = "en") { translationHelp, }; - debug/* default */.A.log("udemy video data:", videoData); + utils_debug.log("udemy video data:", videoData); console.log("[VOT] Detected language: ", videoData.detectedLanguage); return videoData; } @@ -3866,583 +5680,6 @@ async function udemyUtils_getVideoData(udemyData, responseLang = "en") { getLectureData, }); -;// CONCATENATED MODULE: ./src/utils/bannedvideoUtils.js - - -async function getVideoInfo(videoId) { - return await fetch(`https://api.banned.video/graphql`, { - method: "POST", - body: JSON.stringify({ - operationName: "GetVideo", - query: `query GetVideo($id: String!) { - getVideo(id: $id) { - ...DisplayVideoFields - videoUrl: directUrl - live - } - } - - fragment DisplayVideoFields on Video { - title - description: summary - duration: videoDuration - }`, - variables: { - id: videoId, - }, - }), - headers: { - "User-Agent": "bannedVideoFrontEnd", - "apollographql-client-name": "banned-web", - "apollographql-client-version": "1.3", - "content-type": "application/json", - }, - }) - .then((res) => res.json()) - .catch((err) => { - console.error(err); - return { - data: { - getVideo: {}, - }, - }; - }); -} - -async function bannedvideoUtils_getVideoData(videoId) { - const videoData = await getVideoInfo(videoId); - - debug/* default */.A.log("banned.video video data:", videoData); - - const { videoUrl, duration, live, description, title } = - videoData.data.getVideo; - - // TODO: Add detect language from title + description - - return { - url: videoUrl, - duration, - live, - title, - description, - }; -} - -/* harmony default export */ const bannedvideoUtils = ({ - getVideoData: bannedvideoUtils_getVideoData, -}); - -;// CONCATENATED MODULE: ./src/utils/crypto.js -async function getHmacSha1(hmacKey, salt) { - try { - const utf8Encoder = new TextEncoder("utf-8"); - salt = utf8Encoder.encode(salt); - const key = await window.crypto.subtle.importKey( - "raw", - utf8Encoder.encode(hmacKey), - { name: "HMAC", hash: { name: "SHA-1" } }, - false, - ["sign", "verify"], - ); - const signature = await window.crypto.subtle.sign("HMAC", key, salt); - return btoa(String.fromCharCode(...new Uint8Array(signature))); - } catch (err) { - console.error(err); - return false; - } -} - -;// CONCATENATED MODULE: ./src/utils/weverseUtils.js - - - -const API_ORIGIN = "https://global.apis.naver.com/weverse/wevweb"; // find as REACT_APP_API_GW_ORIGIN in main..js -const API_APP_ID = "be4d79eb8fc7bd008ee82c8ec4ff6fd4"; // find as REACT_APP_API_APP_ID in main..js -const API_HMAC_KEY = "1b9cb6378d959b45714bec49971ade22e6e24e42"; // find as c.active near `createHmac('sha1'...` in main..js - -async function createHash(pathname) { - // pathname example: /post/v1.0/post-3-142049908/preview?fieldSet=postForPreview... - const timestamp = Date.now(); - - // example salt is /video/v1.1/vod/67134/inKey?gcc=RU&appId=be4d79eb8fc7bd008ee82c8ec4ff6fd4&language=en&os=WEB&platform=WEB&wpf=pc1707527163588 - let salt = pathname.substring(0, Math.min(255, pathname.length)) + timestamp; - - const sign = await getHmacSha1(API_HMAC_KEY, salt); - - return { - wmsgpad: timestamp, - wmd: sign, - }; -} - -function getURLData() { - return { - appId: API_APP_ID, - language: "en", - os: "WEB", - platform: "WEB", - wpf: "pc", - }; -} - -async function getVideoPreview(postId) { - const pathname = - `/post/v1.0/post-${postId}/preview?` + - new URLSearchParams({ - fieldSet: "postForPreview", - ...getURLData(), - }); // ! DON'T EDIT ME - - const hash = await createHash(pathname); - - try { - const res = await fetch( - API_ORIGIN + pathname + "&" + new URLSearchParams(hash), - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -async function getVideoInKey(videoId) { - const pathname = - `/video/v1.1/vod/${videoId}/inKey?` + - new URLSearchParams({ - gcc: "RU", - ...getURLData(), - }); // ! DON'T EDIT ME - const hash = await createHash(pathname); - - try { - const res = await fetch( - API_ORIGIN + pathname + "&" + new URLSearchParams(hash), - { - method: "POST", - }, - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -async function weverseUtils_getVideoInfo(infraVideoId, inkey, serviceId) { - const timestamp = Date.now(); - try { - const res = await fetch( - `https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${infraVideoId}?` + - new URLSearchParams({ - key: inkey, - sid: serviceId, - nonce: timestamp, - devt: "html5_pc", - prv: "N", - aup: "N", - stpb: "N", - cpl: "en", - env: "prod", - lc: "en", - adi: JSON.stringify([ - { - adSystem: null, - }, - ]), - adu: "/", - }), - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -function extractVideoInfo(videoList) { - return videoList.find( - (video) => video.useP2P === false && video.source.includes(".mp4"), - ); -} - -async function weverseUtils_getVideoData() { - // ! When translating using a regular link (like this https://weverse.io/aespa/live/3-142049908), - // ! we will get an error, so we have to do this - - const postId = /([^/]+)\/(live|media)\/([^/]+)/.exec( - new URL(window.location).pathname, - )?.[3]; - - const videoPreview = await getVideoPreview(postId); - if (!videoPreview) { - return undefined; - } - - debug/* default */.A.log("weverse video preview data:", videoPreview); - - const { videoId, serviceId, infraVideoId } = videoPreview.extension.video; - if (!(videoId && serviceId && infraVideoId)) { - return false; - } - - const inkeyData = await getVideoInKey(videoId); - debug/* default */.A.log("weverse video inKey data:", videoPreview); - if (!inkeyData) { - return false; - } - - const videoInfo = await weverseUtils_getVideoInfo( - infraVideoId, - inkeyData.inKey, - serviceId, - ); - debug/* default */.A.log("weverse video info:", videoInfo); - - const videoItem = extractVideoInfo(videoInfo.videos.list); - if (!videoItem) { - return false; - } - - return { - url: videoItem.source, - duration: videoItem.duration, - }; -} - -/* harmony default export */ const weverseUtils = ({ - getVideoData: weverseUtils_getVideoData, -}); - -;// CONCATENATED MODULE: ./src/config/sites.js - - -const sites = () => { - return [ - { - additionalData: "mobile", - host: "youtube", - url: "https://youtu.be/", - match: /^m.youtube.com$/, - selector: "shorts-video #player", - }, - { - additionalData: "mobile", - host: "youtube", - url: "https://youtu.be/", - match: /^m.youtube.com$/, - selector: ".player-container", - }, - { - host: "youtube", - url: "https://youtu.be/", - match: /^(www.)?youtube(-nocookie|kids)?.com$/, - selector: ".html5-video-container:not(#inline-player *)", - }, - { - host: "tiktok", - url: "https://www.tiktok.com/", - match: /^(www.)?tiktok.com$/, - selector: null, - }, - { - host: "proxitok", - url: "https://www.tiktok.com/", - match: sitesProxiTok, - selector: ".column.has-text-centered", - }, - { - host: "twitch", - url: "https://twitch.tv/", - match: [ - /^m.twitch.tv$/, - /^www.twitch.tv$/, - /^clips.twitch.tv$/, - /^player.twitch.tv$/, - ], - selector: ".video-ref, main > div > section > div > div > div", - }, - { - host: "xvideos", - url: "https://www.xvideos.com/", - match: /^www.(xvideos|xv-ru).com$/, - selector: ".video-bg-pic", - }, - { - host: "pornhub", - url: "https://rt.pornhub.com/view_video.php?viewkey=", - match: /^[a-z]+.pornhub.com$/, - selector: ".mainPlayerDiv > .video-element-wrapper-js > div", - }, - { - additionalData: "embed", - host: "pornhub", - url: "https://rt.pornhub.com/view_video.php?viewkey=", - match: (url) => - url.host.includes("pornhub.com") && url.pathname.startsWith("/embed/"), - selector: "#player", - }, - { - additionalData: "mobile", - host: "vk", - url: "https://vk.com/video?z=", - match: /^m.vk.(com|ru)$/, - selector: "vk-video-player", - shadowRoot: true, - }, - { - additionalData: "clips", - host: "vk", - url: "https://vk.com/video?z=", - match: /^(www.|m.)?vk.(com|ru)$/, - selector: 'div[data-testid="clipcontainer-video"]', - }, - { - host: "vk", - url: "https://vk.com/video?z=", - match: /^(www.|m.)?vk.(com|ru)$/, - selector: ".videoplayer_media", - }, - { - host: "vimeo", - url: "https://vimeo.com/", - match: /^vimeo.com$/, - selector: ".player", - }, - { - additionalData: "embed", - host: "vimeo", - url: "https://player.vimeo.com/", - match: /^player.vimeo.com$/, - selector: ".player", - }, - { - host: "ok.ru", - url: "https://ok.ru/video/", - match: /^ok.ru$/, - selector: ".html5-vpl_vid", - }, - { - host: "nine_gag", - url: "https://9gag.com/gag/", - match: /^9gag.com$/, - selector: ".video-post", - }, - // { - // host: "coub", - // url: "https://coub.com/view/", - // match: /^coub.com$/, - // selector: ".viewer__player", - // }, - { - host: "bitchute", - url: "https://www.bitchute.com/video/", - match: /^(www.)?bitchute.com$/, - selector: ".video-js", - }, - { - host: "rutube", - url: "https://rutube.ru/video/", - match: /^rutube.ru$/, - selector: ".video-player > div > div > div:nth-child(2)", - }, - { - additionalData: "embed", - host: "rutube", - url: "https://rutube.ru/video/", - match: /^rutube.ru$/, - selector: "#app > div > div", - }, - { - host: "bilibili", - url: "https://www.bilibili.com/video/", - match: /^(www|m|player).bilibili.com$/, - selector: ".bpx-player-video-wrap", - }, - // Добавляет лишние видео в обработчик - { - additionalData: "old", // /blackboard/webplayer/embed-old.html - host: "bilibili", - url: "https://www.bilibili.com/video/", - match: /^(www|m).bilibili.com$/, - selector: null, - }, - { - host: "twitter", - url: "https://twitter.com/i/status/", - match: /^twitter.com$/, - selector: 'div[data-testid="videoComponent"] > div:nth-child(1) > div', - }, - { - host: "mail_ru", - url: "https://my.mail.ru/", - match: /^my.mail.ru$/, - selector: "#b-video-wrapper", - }, - { - // ONLY IF YOU LOGINED TO COURSERA /learn/NAME/lecture/XXXX - host: "coursera", - url: "https://www.coursera.org/", - match: /coursera.org$/, - selector: ".vjs-v6", - }, - { - // ONLY IF YOU LOGINED TO UDEMY /course/NAME/learn/lecture/XXXX - host: "udemy", - url: "https://www.udemy.com/", - match: /udemy.com$/, - selector: - 'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)', - }, - { - // Sites host Invidious. I tested the performance only on invidious.kevin.rocks, youtu.be and inv.vern.cc - host: "invidious", - url: "https://youtu.be/", - match: sitesInvidious, - selector: "#player", - }, - { - // Sites host Piped. I tested the performance only on piped.video - host: "piped", - url: "https://youtu.be/", - match: sitesPiped, - selector: ".shaka-video-container", - }, - { - host: "rumble", - url: "https://rumble.com/", - match: /^rumble.com$/, - selector: "#videoPlayer > .videoPlayer-Rumble-cls > div", - }, - { - host: "eporner", - url: "https://www.eporner.com/", - match: /^(www.)?eporner.com$/, - selector: ".vjs-v7", - }, - { - host: "peertube", - url: "stub", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: sitesPeertube, - selector: ".vjs-v7", - }, - { - host: "dailymotion", - url: "https://dai.ly/", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: /^geo.dailymotion.com$/, - selector: ".player", - }, - { - host: "trovo", - url: "https://trovo.live/s/", - match: /^trovo.live$/, - selector: ".player-video", - }, - { - host: "yandexdisk", - url: "https://yadi.sk/i/", - match: /^disk.yandex.ru$/, - selector: ".video-player__player > div:nth-child(1)", - }, - { - host: "coursehunter", - url: "https://coursehunter.net/course/", - match: /^coursehunter.net$/, - selector: "#oframeplayer > pjsdiv:nth-of-type(1)", - }, - { - host: "googledrive", - url: "https://drive.google.com/file/d/", - match: /^youtube.googleapis.com$/, - selector: ".html5-video-container", - }, - { - host: "bannedvideo", - url: "https://banned.video/watch?id=", - match: /^(www.)?banned.video$/, - selector: ".vjs-v7", - }, - { - host: "facebook", - url: "https://facebook.com/", - match: (url) => - url.host.includes("facebook.com") && url.pathname.includes("/videos/"), - selector: 'div[role="main"] div[data-pagelet$="video" i]', - }, - { - additionalData: "reels", - host: "facebook", - url: "https://facebook.com/", - match: (url) => - url.host.includes("facebook.com") && url.pathname.includes("/reel/"), - selector: 'div[role="main"]', - }, - { - host: "weverse", - url: "https://weverse.io/", - match: /^weverse.io$/, - selector: ".webplayer-internal-source-wrapper", - }, - { - host: "newgrounds", - url: "https://www.newgrounds.com/", - match: /^www.newgrounds.com$/, - selector: ".ng-video-player", - }, - { - // TODO: Добавить поддержку tips (сделать через m3u8 т.к. обычная ссылка не принимается) и платных курсов - host: "egghead", - url: "https://egghead.io/", - match: /^egghead.io$/, - selector: ".cueplayer-react-video-holder", - }, - { - host: "youku", - url: "https://v.youku.com/", - match: /^v.youku.com$/, - selector: "#ykPlayer", - }, - { - host: "archive", - url: "https://archive.org/details/", - match: /^archive.org$/, - selector: ".jw-media", - }, - { - host: "directlink", - url: "stub", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: (url) => /([^.]+).mp4/.test(url.pathname), - selector: null, - }, - // пока рано - // { - // host: "patreon", - // url: "https://www.patreon.com/", - // match: /^www.patreon.com$/, - // selector: - // 'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div', - // }, - // { - // host: "sibnet", - // url: "https://video.sibnet.ru/", - // match: /^video.sibnet.ru$/, - // selector: ".video-js", // #video_html5_wrapper - // }, - // Нужно куда-то заливать данные о плейлисте - // { - // host: "epicgames", - // url: "https://dev.epicgames.com/community/learning/tutorials/", - // match: /^dev.epicgames.com$/, - // selector: "#vjs_video_3", - // }, - ]; -}; - -/* harmony default export */ const config_sites = (sites()); - // EXTERNAL MODULE: ./node_modules/requestidlecallback-polyfill/index.js var requestidlecallback_polyfill = __webpack_require__("./node_modules/requestidlecallback-polyfill/index.js"); ;// CONCATENATED MODULE: ./src/utils/EventImpl.js @@ -4450,9 +5687,11 @@ class EventImpl { constructor() { this.listeners = new Set(); } + hasListener(e) { return this.listeners.has(e); } + dispatchToListener(handler, ...args) { try { handler(...args); @@ -4460,18 +5699,21 @@ class EventImpl { console.error("[VOT]", exception); } } + addListener(handler) { if (this.hasListener(handler)) { throw new Error("[VOT] The listener has already been added."); } this.listeners.add(handler); } + removeListener(handler) { if (!this.hasListener(handler)) { throw new Error("[VOT] The listener has not been added yet."); } this.listeners.delete(handler); } + dispatch(...args) { for (const handler of Array.from(this.listeners)) { this.dispatchToListener(handler, ...args); @@ -4578,7 +5820,7 @@ class VideoObserver { childList: true, subtree: true, }); - const videos = document.querySelectorAll("video"); + const videos = this.getAllVideoEls(); for (let i = 0; i < videos.length; i++) { this.checkAndHandleVideo(videos[i]); } @@ -4589,6 +5831,42 @@ class VideoObserver { this.intersectionObserver.disconnect(); } + getAllVideoEls() { + const videos = document.querySelectorAll("video"); + if (videos.length) { + return videos; + } + + // Use it only if we don't find videos + // It takes a long time to complete + const els = document.querySelectorAll("*"); + const videoElements = new Set(); + function traverseShadowRoot(root) { + if (!root) return; + root.querySelectorAll("video").forEach((video) => { + if (videoElements.has(video)) { + return; + } + videoElements.add(video); + }); + + root.querySelectorAll("*").forEach((element) => { + if (element.shadowRoot) { + traverseShadowRoot(element.shadowRoot); + } + }); + } + + for (let i = 0; i < els.length; i++) { + const el = els[i]; + if (el.shadowRoot) { + traverseShadowRoot(el); + } + } + + return Array.from(videoElements); + } + checkAndHandleVideo(video) { if (this.videoCache.has(video)) { return; @@ -4600,7 +5878,7 @@ class VideoObserver { handleIntersectingVideo(video) { this.intersectionObserver.unobserve(video); if (isAdVideo(video)) { - debug/* default */.A.log("The promotional video was ignored", video); + utils_debug.log("The promotional video was ignored", video); return; } waitForVideoReady(video, (readyVideo) => { @@ -4620,10 +5898,6 @@ class VideoObserver { }; } -// EXTERNAL MODULE: ./src/utils/storage.js -var storage = __webpack_require__("./src/utils/storage.js"); -// EXTERNAL MODULE: ./src/utils/translateApis.js -var translateApis = __webpack_require__("./src/utils/translateApis.js"); ;// CONCATENATED MODULE: ./src/index.js @@ -4651,17 +5925,19 @@ var translateApis = __webpack_require__("./src/utils/translateApis.js"); - - const browserInfo = es5.getParser(window.navigator.userAgent).getResult(); - const dontTranslateMusic = false; // Пока не придумал как стоит реализовать - const sitesChromiumBlocked = [...sitesInvidious, ...sitesPiped]; - +const cfOnlyExtensions = (/* unused pure expression or super */ null && ([ + "Violentmonkey", + "FireMonkey", + "Greasemonkey", + "AdGuard", + "OrangeMonkey", +])); const videoLipSyncEvents = [ "playing", "ratechange", @@ -4672,166 +5948,39 @@ const videoLipSyncEvents = [ function genOptionsByOBJ(obj, conditionString) { return obj.map((code) => ({ - label: localizationProvider/* localizationProvider */.j.get("langs")[code] ?? code.toUpperCase(), + label: localizationProvider.get("langs")[code] ?? code.toUpperCase(), value: code, selected: conditionString === code, })); } -if (true) { - var translationPanding = false; -} - -function translateVideo( - url, - duration, - requestLang, - responseLang, - translationHelp, - callback, -) { - debug/* default */.A.log( - `Translate video (url: ${url}, duration: ${duration}, requestLang: ${requestLang}, responseLang: ${responseLang})`, - ); - - debug/* default */.A.log("translationHelp:", translationHelp); - - if ( true && translationPanding) { - debug/* default */.A.log("translationPanding return"); - return; - } - - translationPanding = true; - - rvt( - url, - duration, - requestLang, - responseLang, - translationHelp, - (success, response) => { - translationPanding = false; - - debug/* default */.A.log("[exec callback] Requesting video translation"); - if (!success) { - callback(false, localizationProvider/* localizationProvider */.j.get("requestTranslationFailed")); - return; - } - - const translateResponse = - yandexProtobuf.decodeTranslationResponse(response); - console.log("[VOT] Translation response: ", translateResponse); - - switch (translateResponse.status) { - case 0: - callback(false, translateResponse.message); - break; - case 1: - case 5: - // status 5 (dzen) - // Отдает частичный контент т.е. аудио не для всего видео, а только для части (~10min) - // так же возвращается оставшееся время перевода (remainingTime) через которое нужно сделать повторный запрос, - // в котором будет возвращено полное аудио перевода и status 1. - // Если включена часть видео без перевода, то пишет "Эта часть видео еще не переведена" - callback( - !!translateResponse.url, - translateResponse.url || - localizationProvider/* localizationProvider */.j.get("audioNotReceived"), - ); - break; - case 2: - callback( - false, - translateResponse.remainingTime - ? (0,utils/* secsToStrTime */.ox)(translateResponse.remainingTime) - : localizationProvider/* localizationProvider */.j.get("translationTakeFewMinutes"), - ); - break; - case 3: - case 6: - /* - status: 3 - Иногда, в ответе приходит статус код 3, но видео всё, так же, ожидает перевода. - В конечном итоге, это занимает слишком много времени, - как-будто сервер не понимает, что данное видео уже недавно было переведено - и заместо возвращения готовой ссылки на перевод начинает переводить видео заново - при чём у него это получается за очень длительное время. - - status: 6 - Случайно встретил 6 статус код при котором видео так же продолжается перевод, - но после него ничего сверхъестественного не происходит. - Он появляется при первом запросе с 17=1, но не исключено, - что может появится и просто так - */ - callback(false, localizationProvider/* localizationProvider */.j.get("videoBeingTranslated")); - break; - } - }, - ); -} - -function translateStream(url, requestLang, responseLang, callback) { - debug/* default */.A.log( - `Translate stream (url: ${url}, requestLang: ${requestLang}, responseLang: ${responseLang})`, - ); - - rst( - url, - requestLang, - responseLang, - (success, response) => { - debug/* default */.A.log("[exec callback] Requesting stream translation"); - if (!success) { - callback(false, localizationProvider/* localizationProvider */.j.get("requestTranslationFailed")); - return; - } - - const streamResponse = yandexProtobuf.decodeStreamResponse(response); - console.log("[VOT] Stream Translation response: ", streamResponse); - - switch (streamResponse.interval) { - case 10: - callback( - false, - streamResponse.interval, - localizationProvider/* localizationProvider */.j.get("translationTakeFewMinutes"), - ); - break; - case 20: - callback( - true, - streamResponse.interval, - streamResponse || localizationProvider/* localizationProvider */.j.get("audioNotReceived"), - ); - break; - case 0: - // stream removed or ended - callback( - false, - streamResponse.interval, - localizationProvider/* localizationProvider */.j.get("streamNoConnectionToServer"), - ); - break; - } - }, - ); -} +const votOpts = { + headers: + true + ? {} + : 0, + fetchFn: GM_fetch, + hostVOT: votBackendUrl, + host: true ? proxyWorkerHost : 0, +}; class VideoHandler { // translate properties translateFromLang = "en"; // default language of video - translateToLang = utils/* lang */.vV; // default language of audio response + translateToLang = lang; // default language of audio response timer; - ytData = ""; videoData = ""; firstPlay = true; audio = new Audio(); audioContext = new (window.AudioContext || window.webkitAudioContext)(); gainNode = this.audioContext.createGain(); - hls = (0,utils/* initHls */.CK)(); // debug enabled only in dev mode + hls = initHls(); // debug enabled only in dev mode + votClient = new ( true ? VOTWorkerClient : 0)( + votOpts, + ); videoTranslations = []; videoTranslationTTL = 7200; @@ -4855,7 +6004,7 @@ class VideoHandler { dragging; constructor(video, container, site) { - debug/* default */.A.log( + utils_debug.log( "[VideoHandler] add video:", video, "container:", @@ -4872,11 +6021,139 @@ class VideoHandler { this.init(); } + async translateVideoImpl( + videoData, + requestLang, + responseLang, + translationHelp = null, + ) { + clearTimeout(this.autoRetry); + utils_debug.log( + videoData, + `Translate video (requestLang: ${requestLang}, responseLang: ${responseLang})`, + ); + + if ( + (await getVideoID(this.site, window.location.href)) !== videoData.videoId + ) { + return null; + } + + try { + const res = await this.votClient.translateVideo({ + videoData, + requestLang, + responseLang, + translationHelp, + }); + utils_debug.log("Translate video result", res); + if (res.translated && res.remainingTime < 1) { + utils_debug.log("Video translation finished with this data: ", res); + return res; + } + + await this.updateTranslationErrorMsg( + res.remainingTime > 0 + ? secsToStrTime(res.remainingTime) + : res.message ?? + localizationProvider.get("translationTakeFewMinutes"), + ); + } catch (err) { + console.error("[VOT] Failed to translate video", err); + await this.updateTranslationErrorMsg(err.data?.message ?? err); + return null; + } + + return new Promise((resolve) => { + const timeoutDuration = this.subtitlesList.some( + (item) => item.source === "yandex", + ) + ? 20_000 + : 30_000; + this.autoRetry = setTimeout(async () => { + const res = await this.translateVideoImpl( + videoData, + requestLang, + responseLang, + translationHelp, + ); + if (!res || (res.translated && res.remainingTime < 1)) { + resolve(res); + } + }, timeoutDuration); + }); + } + + async translateStreamImpl(videoData, requestLang, responseLang) { + clearTimeout(this.autoRetry); + utils_debug.log( + videoData, + `Translate stream (requestLang: ${requestLang}, responseLang: ${responseLang})`, + ); + + if ( + (await getVideoID(this.site, window.location.href)) !== videoData.videoId + ) { + return null; + } + + try { + const res = await this.votClient.translateStream({ + videoData, + requestLang, + responseLang, + }); + utils_debug.log("Translate stream result", res); + if (!res.translated && res.interval === 10) { + await this.updateTranslationErrorMsg( + localizationProvider.get("translationTakeFewMinutes"), + ); + return new Promise((resolve) => { + this.autoRetry = setTimeout(async () => { + const res = await this.translateStreamImpl( + videoData, + requestLang, + responseLang, + ); + if (!res || !(!res.translated && res.interval === 10)) { + resolve(res); + } + }, res.interval * 1000); + }); + } + + if (res.message) { + utils_debug.log(`Stream translation aborted! Message: ${res.message}`); + throw new VOTLocalizedError("streamNoConnectionToServer"); + } + + if (!res.result) { + utils_debug.log("Failed to find translation result! Data:", res); + throw new VOTLocalizedError("audioNotReceived"); + } + + utils_debug.log("Stream translated successfully. Running...", res); + + this.streamPing = setInterval(async () => { + utils_debug.log("Ping stream translation", res.pingId); + this.votClient.pingStream({ + pingId: res.pingId, + }); + }, res.interval * 1000); + + return res; + } catch (err) { + console.error("[VOT] Failed to translate stream", err); + await this.updateTranslationErrorMsg(err.data?.message ?? err); + return null; + } + } + async autoTranslate() { if ( this.site.host === "youtube" && dontTranslateMusic && - youtubeUtils/* default */.A.isMusic() + youtubeUtils.isMusic() ) { return; } @@ -4904,37 +6181,37 @@ class VideoHandler { if (this.initialized) return; const audioProxyDefault = - utils/* lang */.vV === "uk" && "cloudflare" === "cloudflare" ? 1 : 0; + lang === "uk" && "cloudflare" === "cloudflare" ? 1 : 0; const dataPromises = { - autoTranslate: storage/* votStorage */.d.get("autoTranslate", 0, true), - dontTranslateLanguage: storage/* votStorage */.d.get("dontTranslateLanguage", utils/* lang */.vV), - dontTranslateYourLang: storage/* votStorage */.d.get("dontTranslateYourLang", 1, true), - autoSetVolumeYandexStyle: storage/* votStorage */.d.get( + autoTranslate: votStorage.get("autoTranslate", 0, true), + dontTranslateLanguage: votStorage.get("dontTranslateLanguage", lang), + dontTranslateYourLang: votStorage.get("dontTranslateYourLang", 1, true), + autoSetVolumeYandexStyle: votStorage.get( "autoSetVolumeYandexStyle", 1, true, ), - autoVolume: storage/* votStorage */.d.get("autoVolume", config/* defaultAutoVolume */.JD, true), - buttonPos: storage/* votStorage */.d.get("buttonPos", "default"), - showVideoSlider: storage/* votStorage */.d.get("showVideoSlider", 1, true), - syncVolume: storage/* votStorage */.d.get("syncVolume", 0, true), - subtitlesMaxLength: storage/* votStorage */.d.get("subtitlesMaxLength", 300, true), - highlightWords: storage/* votStorage */.d.get("highlightWords", 0, true), - responseLanguage: storage/* votStorage */.d.get("responseLanguage", utils/* lang */.vV), - defaultVolume: storage/* votStorage */.d.get("defaultVolume", 100, true), - udemyData: storage/* votStorage */.d.get("udemyData", { accessToken: "", expires: 0 }), - audioProxy: storage/* votStorage */.d.get("audioProxy", audioProxyDefault, true), - showPiPButton: storage/* votStorage */.d.get("showPiPButton", 0, true), - translateAPIErrors: storage/* votStorage */.d.get("translateAPIErrors", 1, true), - translationService: storage/* votStorage */.d.get( + autoVolume: votStorage.get("autoVolume", defaultAutoVolume, true), + buttonPos: votStorage.get("buttonPos", "default"), + showVideoSlider: votStorage.get("showVideoSlider", 1, true), + syncVolume: votStorage.get("syncVolume", 0, true), + subtitlesMaxLength: votStorage.get("subtitlesMaxLength", 300, true), + highlightWords: votStorage.get("highlightWords", 0, true), + responseLanguage: votStorage.get("responseLanguage", lang), + defaultVolume: votStorage.get("defaultVolume", 100, true), + udemyData: votStorage.get("udemyData", { accessToken: "", expires: 0 }), + audioProxy: votStorage.get("audioProxy", audioProxyDefault, true), + showPiPButton: votStorage.get("showPiPButton", 0, true), + translateAPIErrors: votStorage.get("translateAPIErrors", 1, true), + translationService: votStorage.get( "translationService", - config/* defaultTranslationService */.mE, + defaultTranslationService, ), - detectService: storage/* votStorage */.d.get("detectService", config/* defaultDetectService */.K2), - m3u8ProxyHost: storage/* votStorage */.d.get("m3u8ProxyHost", config/* m3u8ProxyHost */.se), - proxyWorkerHost: storage/* votStorage */.d.get("proxyWorkerHost", config/* proxyWorkerHost */.Pm), - audioBooster: storage/* votStorage */.d.get("audioBooster", 0, true), + detectService: votStorage.get("detectService", defaultDetectService), + m3u8ProxyHost: votStorage.get("m3u8ProxyHost", m3u8ProxyHost), + proxyWorkerHost: votStorage.get("proxyWorkerHost", proxyWorkerHost), + audioBooster: votStorage.get("audioBooster", 0, true), }; this.data = Object.fromEntries( @@ -4965,6 +6242,10 @@ class VideoHandler { this.initUI(); this.initUIEvents(); + if (true) { + this.votClient.host = this.data.proxyWorkerHost; + } + const videoHasNoSource = !this.video.src && !this.video.currentSrc && !this.video.srcObject; this.votButton.container.hidden = videoHasNoSource; @@ -4992,7 +6273,7 @@ class VideoHandler { this.votButton.container.dataset.status = status; this.votButton.container.dataset.translating = status === "error" - ? text.includes(localizationProvider/* localizationProvider */.j.get("translationTake")) + ? text.includes(localizationProvider.get("translationTake")) : false; this.votButton.label.innerHTML = text; this.votButton.container.title = status === "error" ? text : ""; @@ -5002,7 +6283,7 @@ class VideoHandler { // VOT Button { this.votButton = ui.createVOTButton( - localizationProvider/* localizationProvider */.j.get("translateVideo"), + localizationProvider.get("translateVideo"), ); // use an additional check because sometimes this.video.clientWidth = 0 @@ -5021,9 +6302,9 @@ class VideoHandler { this.container.appendChild(this.votButton.container); this.votButton.pipButton.hidden = - !(0,utils/* isPiPAvailable */.Bs)() || !this.data?.showPiPButton; + !isPiPAvailable() || !this.data?.showPiPButton; this.votButton.separator2.hidden = - !(0,utils/* isPiPAvailable */.Bs)() || !this.data?.showPiPButton; + !isPiPAvailable() || !this.data?.showPiPButton; this.votButton.container.addEventListener("click", (e) => { e.preventDefault(); @@ -5034,7 +6315,7 @@ class VideoHandler { // VOT Menu { - this.votMenu = ui.createVOTMenu(localizationProvider/* localizationProvider */.j.get("VOTSettings")); + this.votMenu = ui.createVOTMenu(localizationProvider.get("VOTSettings")); this.votMenu.container.dataset.position = this.container.clientWidth && this.container.clientWidth > 550 ? this.data?.buttonPos @@ -5060,18 +6341,14 @@ class VideoHandler { this.votTranslationLanguageSelect = ui.createVOTLanguageSelect({ fromTitle: - localizationProvider/* localizationProvider */.j.get("langs")[this.video.detectedLanguage], - fromDialogTitle: localizationProvider/* localizationProvider */.j.get("videoLanguage"), - fromItems: [ - { - label: localizationProvider/* localizationProvider */.j.get("langs")["auto"], - value: "auto", - selected: "", - }, - ...genOptionsByOBJ(constants/* availableLangs */.xm, this.videoData.detectedLanguage), - ], + localizationProvider.get("langs")[this.video.detectedLanguage], + fromDialogTitle: localizationProvider.get("videoLanguage"), + fromItems: genOptionsByOBJ( + availableLangs, + this.videoData.detectedLanguage, + ), fromOnSelectCB: async (e) => { - debug/* default */.A.log( + utils_debug.log( "[fromOnSelectCB] select from language", e.target.dataset.votValue, ); @@ -5081,15 +6358,15 @@ class VideoHandler { this.videoData.responseLanguage, ); }, - toTitle: localizationProvider/* localizationProvider */.j.get("langs")[this.video.responseLanguage], - toDialogTitle: localizationProvider/* localizationProvider */.j.get("translationLanguage"), - toItems: genOptionsByOBJ(constants/* actualTTS */.Ww, this.videoData.responseLanguage), + toTitle: localizationProvider.get("langs")[this.video.responseLanguage], + toDialogTitle: localizationProvider.get("translationLanguage"), + toItems: genOptionsByOBJ(availableTTS, this.videoData.responseLanguage), toOnSelectCB: async (e) => { const newLang = e.target.dataset.votValue; - debug/* default */.A.log("[toOnSelectCB] select to language", newLang); + utils_debug.log("[toOnSelectCB] select to language", newLang); this.data.responseLanguage = this.translateToLang = newLang; - await storage/* votStorage */.d.set("responseLanguage", this.data.responseLanguage); - debug/* default */.A.log( + await votStorage.set("responseLanguage", this.data.responseLanguage); + utils_debug.log( "Response Language value changed. New value: ", this.data.responseLanguage, ); @@ -5106,11 +6383,11 @@ class VideoHandler { ); this.votSubtitlesSelect = ui.createVOTSelect( - localizationProvider/* localizationProvider */.j.get("VOTSubtitlesDisabled"), - localizationProvider/* localizationProvider */.j.get("VOTSubtitles"), + localizationProvider.get("VOTSubtitlesDisabled"), + localizationProvider.get("VOTSubtitles"), [ { - label: localizationProvider/* localizationProvider */.j.get("VOTSubtitlesDisabled"), + label: localizationProvider.get("VOTSubtitlesDisabled"), value: "disabled", selected: true, disabled: false, @@ -5121,7 +6398,7 @@ class VideoHandler { await this.changeSubtitlesLang(e.target.dataset.votValue); }, labelElement: ui.createVOTSelectLabel( - localizationProvider/* localizationProvider */.j.get("VOTSubtitles"), + localizationProvider.get("VOTSubtitles"), ), }, ); @@ -5129,7 +6406,7 @@ class VideoHandler { this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container); this.votVideoVolumeSlider = ui.createSlider( - `${localizationProvider/* localizationProvider */.j.get("VOTVolume")}: ${ + `${localizationProvider.get("VOTVolume")}: ${ this.getVideoVolume() * 100 }%`, this.getVideoVolume() * 100, @@ -5142,12 +6419,12 @@ class VideoHandler { ); this.votVideoTranslationVolumeSlider = ui.createSlider( - `${localizationProvider/* localizationProvider */.j.get("VOTVolumeTranslation")}: ${ + `${localizationProvider.get("VOTVolumeTranslation")}: ${ this.data?.defaultVolume ?? 100 }%`, this.data?.defaultVolume ?? 100, 0, - this.data.audioBooster ? config/* maxAudioVolume */.T8 : 100, + this.data.audioBooster ? maxAudioVolume : 100, ); this.votVideoTranslationVolumeSlider.container.hidden = this.votButton.container.dataset.status !== "success"; @@ -5165,19 +6442,19 @@ class VideoHandler { // VOT Settings { this.votSettingsDialog = ui.createDialog( - localizationProvider/* localizationProvider */.j.get("VOTSettings"), + localizationProvider.get("VOTSettings"), ); document.documentElement.appendChild(this.votSettingsDialog.container); this.votTranslationHeader = ui.createHeader( - localizationProvider/* localizationProvider */.j.get("translationSettings"), + localizationProvider.get("translationSettings"), ); this.votSettingsDialog.bodyContainer.appendChild( this.votTranslationHeader, ); this.votAutoTranslateCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTAutoTranslate"), + localizationProvider.get("VOTAutoTranslate"), this.data?.autoTranslate ?? false, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5185,24 +6462,24 @@ class VideoHandler { ); this.votDontTranslateYourLangSelect = ui.createVOTSelect( - localizationProvider/* localizationProvider */.j.get("langs")[ - storage/* votStorage */.d.syncGet("dontTranslateLanguage", utils/* lang */.vV) + localizationProvider.get("langs")[ + votStorage.syncGet("dontTranslateLanguage", lang) ], - localizationProvider/* localizationProvider */.j.get("VOTDontTranslateYourLang"), + localizationProvider.get("VOTDontTranslateYourLang"), genOptionsByOBJ( - constants/* availableLangs */.xm, - storage/* votStorage */.d.syncGet("dontTranslateLanguage", utils/* lang */.vV), + availableLangs, + votStorage.syncGet("dontTranslateLanguage", lang), ), { onSelectCb: async (e) => { this.data.dontTranslateLanguage = e.target.dataset.votValue; - await storage/* votStorage */.d.set( + await votStorage.set( "dontTranslateLanguage", this.data.dontTranslateLanguage, ); }, labelElement: ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTDontTranslateYourLang"), + localizationProvider.get("VOTDontTranslateYourLang"), this.data?.dontTranslateYourLang ?? true, ).container, }, @@ -5213,15 +6490,17 @@ class VideoHandler { ); this.votAutoSetVolumeCheckbox = ui.createCheckbox( - `${localizationProvider/* localizationProvider */.j.get("VOTAutoSetVolume")}`, + `${localizationProvider.get("VOTAutoSetVolume")}`, this.data?.autoSetVolumeYandexStyle ?? true, ); this.votSettingsDialog.bodyContainer.appendChild( this.votAutoSetVolumeCheckbox.container, ); this.votAutoSetVolumeSlider = ui.createSlider( - `${(this.data?.autoVolume ?? config/* defaultAutoVolume */.JD) * 100}%`, - (this.data?.autoVolume ?? config/* defaultAutoVolume */.JD) * 100, + `${ + (this.data?.autoVolume ?? defaultAutoVolume) * 100 + }%`, + (this.data?.autoVolume ?? defaultAutoVolume) * 100, 0, 100, ); @@ -5230,7 +6509,7 @@ class VideoHandler { ); this.votShowVideoSliderCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTShowVideoSlider"), + localizationProvider.get("VOTShowVideoSlider"), this.data?.showVideoSlider ?? false, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5238,7 +6517,7 @@ class VideoHandler { ); this.votAudioBoosterCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTAudioBooster"), + localizationProvider.get("VOTAudioBooster"), this.data?.audioBooster ?? false, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5247,7 +6526,7 @@ class VideoHandler { // udemy only this.votUdemyDataTextfield = ui.createTextfield( - localizationProvider/* localizationProvider */.j.get("VOTUdemyData"), + localizationProvider.get("VOTUdemyData"), this.data?.udemyData?.accessToken ?? "", ); this.votUdemyDataTextfield.container.hidden = this.site.host !== "udemy"; @@ -5256,7 +6535,7 @@ class VideoHandler { ); this.votSyncVolumeCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTSyncVolume"), + localizationProvider.get("VOTSyncVolume"), this.data?.syncVolume ?? false, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5264,48 +6543,48 @@ class VideoHandler { ); this.votTranslationServiceSelect = ui.createVOTSelect( - storage/* votStorage */.d - .syncGet("translationService", config/* defaultTranslationService */.mE) + votStorage + .syncGet("translationService", defaultTranslationService) .toUpperCase(), - localizationProvider/* localizationProvider */.j.get("VOTTranslationService"), + localizationProvider.get("VOTTranslationService"), genOptionsByOBJ( - translateApis/* translateServices */.vN, - storage/* votStorage */.d.syncGet("translationService", config/* defaultTranslationService */.mE), + translateServices, + votStorage.syncGet("translationService", defaultTranslationService), ), { onSelectCb: async (e) => { this.data.translationService = e.target.dataset.votValue; - await storage/* votStorage */.d.set( + await votStorage.set( "translationService", this.data.translationService, ); }, labelElement: ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTTranslateAPIErrors"), + localizationProvider.get("VOTTranslateAPIErrors"), this.data.translateAPIErrors ?? true, ).container, }, ); this.votTranslationServiceSelect.container.hidden = - localizationProvider/* localizationProvider */.j.lang === "ru"; + localizationProvider.lang === "ru"; this.votSettingsDialog.bodyContainer.appendChild( this.votTranslationServiceSelect.container, ); this.votDetectServiceSelect = ui.createVOTSelect( - storage/* votStorage */.d.syncGet("detectService", config/* defaultDetectService */.K2).toUpperCase(), - localizationProvider/* localizationProvider */.j.get("VOTDetectService"), + votStorage.syncGet("detectService", defaultDetectService).toUpperCase(), + localizationProvider.get("VOTDetectService"), genOptionsByOBJ( - translateApis/* detectServices */.qh, - storage/* votStorage */.d.syncGet("detectService", config/* defaultDetectService */.K2), + detectServices, + votStorage.syncGet("detectService", defaultDetectService), ), { onSelectCb: async (e) => { this.data.detectService = e.target.dataset.votValue; - await storage/* votStorage */.d.set("detectService", this.data.detectService); + await votStorage.set("detectService", this.data.detectService); }, labelElement: ui.createVOTSelectLabel( - localizationProvider/* localizationProvider */.j.get("VOTDetectService"), + localizationProvider.get("VOTDetectService"), ), }, ); @@ -5316,12 +6595,12 @@ class VideoHandler { // SUBTITLES this.votSubtitlesHeader = ui.createHeader( - localizationProvider/* localizationProvider */.j.get("subtitlesSettings"), + localizationProvider.get("subtitlesSettings"), ); this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader); this.votSubtitlesMaxLengthSlider = ui.createSlider( - `${localizationProvider/* localizationProvider */.j.get("VOTSubtitlesMaxLength")}: ${ + `${localizationProvider.get("VOTSubtitlesMaxLength")}: ${ this.data?.subtitlesMaxLength ?? 300 }`, this.data?.subtitlesMaxLength ?? 300, @@ -5333,7 +6612,7 @@ class VideoHandler { ); this.votSubtitlesHighlightWordsCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTHighlightWords"), + localizationProvider.get("VOTHighlightWords"), this.data?.highlightWords ?? false, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5343,14 +6622,14 @@ class VideoHandler { // PROXY this.votProxyHeader = ui.createHeader( - localizationProvider/* localizationProvider */.j.get("proxySettings"), + localizationProvider.get("proxySettings"), ); this.votSettingsDialog.bodyContainer.appendChild(this.votProxyHeader); this.votM3u8ProxyHostTextfield = ui.createTextfield( - localizationProvider/* localizationProvider */.j.get("VOTM3u8ProxyHost"), + localizationProvider.get("VOTM3u8ProxyHost"), this.data?.m3u8ProxyHost, - config/* m3u8ProxyHost */.se, + m3u8ProxyHost, ); this.votSettingsDialog.bodyContainer.appendChild( this.votM3u8ProxyHostTextfield.container, @@ -5358,9 +6637,9 @@ class VideoHandler { // cf version only this.votProxyWorkerHostTextfield = ui.createTextfield( - localizationProvider/* localizationProvider */.j.get("VOTProxyWorkerHost"), + localizationProvider.get("VOTProxyWorkerHost"), this.data?.proxyWorkerHost, - config/* proxyWorkerHost */.Pm, + proxyWorkerHost, ); this.votProxyWorkerHostTextfield.container.hidden = "cloudflare" !== "cloudflare"; @@ -5370,7 +6649,7 @@ class VideoHandler { // cf version only this.votAudioProxyCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTAudioProxy"), + localizationProvider.get("VOTAudioProxy"), this.data?.audioProxy ?? false, ); this.votAudioProxyCheckbox.container.hidden = "cloudflare" !== "cloudflare"; @@ -5380,27 +6659,28 @@ class VideoHandler { // ABOUT - this.votAboutHeader = ui.createHeader(localizationProvider/* localizationProvider */.j.get("about")); + this.votAboutHeader = ui.createHeader(localizationProvider.get("about")); this.votSettingsDialog.bodyContainer.appendChild(this.votAboutHeader); this.votLanguageSelect = ui.createVOTSelect( - localizationProvider/* localizationProvider */.j.get("langs")[ - storage/* votStorage */.d.syncGet("locale-lang-override", "auto") + localizationProvider.get("langs")[ + // eslint-disable-next-line sonarjs/no-duplicate-string + votStorage.syncGet("locale-lang-override", "auto") ], - localizationProvider/* localizationProvider */.j.get("VOTMenuLanguage"), + localizationProvider.get("VOTMenuLanguage"), genOptionsByOBJ( - localizationProvider/* availableLocales */.z, - storage/* votStorage */.d.syncGet("locale-lang-override", "auto"), + availableLocales, + votStorage.syncGet("locale-lang-override", "auto"), ), { onSelectCb: async (e) => { - await storage/* votStorage */.d.set( + await votStorage.set( "locale-lang-override", e.target.dataset.votValue, ); }, labelElement: ui.createVOTSelectLabel( - localizationProvider/* localizationProvider */.j.get("VOTMenuLanguage"), + localizationProvider.get("VOTMenuLanguage"), ), }, ); @@ -5410,16 +6690,16 @@ class VideoHandler { ); this.votShowPiPButtonCheckbox = ui.createCheckbox( - localizationProvider/* localizationProvider */.j.get("VOTShowPiPButton"), + localizationProvider.get("VOTShowPiPButton"), this.data?.showPiPButton ?? false, ); - this.votShowPiPButtonCheckbox.container.hidden = !(0,utils/* isPiPAvailable */.Bs)(); + this.votShowPiPButtonCheckbox.container.hidden = !isPiPAvailable(); this.votSettingsDialog.bodyContainer.appendChild( this.votShowPiPButtonCheckbox.container, ); this.votVersionInfo = ui.createInformation( - `${localizationProvider/* localizationProvider */.j.get("VOTVersion")}:`, + `${localizationProvider.get("VOTVersion")}:`, true ? `cloudflare ${GM_info.script.version}` : 0, @@ -5429,7 +6709,7 @@ class VideoHandler { ); this.votAuthorsInfo = ui.createInformation( - `${localizationProvider/* localizationProvider */.j.get("VOTAuthors")}:`, + `${localizationProvider.get("VOTAuthors")}:`, GM_info.script.author, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5437,7 +6717,7 @@ class VideoHandler { ); this.votLoaderInfo = ui.createInformation( - `${localizationProvider/* localizationProvider */.j.get("VOTLoader")}:`, + `${localizationProvider.get("VOTLoader")}:`, `${GM_info.scriptHandler} v${GM_info.version}`, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5445,7 +6725,7 @@ class VideoHandler { ); this.votBrowserInfo = ui.createInformation( - `${localizationProvider/* localizationProvider */.j.get("VOTBrowser")}:`, + `${localizationProvider.get("VOTBrowser")}:`, `${browserInfo.browser.name} ${browserInfo.browser.version} (${browserInfo.os.name} ${browserInfo.os.version})`, ); this.votSettingsDialog.bodyContainer.appendChild( @@ -5453,7 +6733,7 @@ class VideoHandler { ); this.votResetSettingsButton = ui.createButton( - localizationProvider/* localizationProvider */.j.get("resetSettings"), + localizationProvider.get("resetSettings"), ); this.votSettingsDialog.bodyContainer.appendChild( this.votResetSettingsButton, @@ -5467,19 +6747,19 @@ class VideoHandler { this.votButton.translateButton.addEventListener("click", () => { (async () => { if (this.audio.src) { - debug/* default */.A.log("[click translationBtn] audio.src is not empty"); + utils_debug.log("[click translationBtn] audio.src is not empty"); this.stopTranslate(); return; } if (this.hls.url) { - debug/* default */.A.log("[click translationBtn] hls is not empty"); + utils_debug.log("[click translationBtn] hls is not empty"); this.stopTranslate(); return; } try { - debug/* default */.A.log("[click translationBtn] trying execute translation"); + utils_debug.log("[click translationBtn] trying execute translation"); if (!this.videoData.videoId) { throw new VOTLocalizedError("VOTNoVideoIDFound"); @@ -5539,15 +6819,15 @@ class VideoHandler { ? percentX <= 44 ? "left" : percentX >= 66 - ? "right" - : "default" + ? "right" + : "default" : "default"; this.votButton.container.dataset.direction = this.data.buttonPos === "default" ? "row" : "column"; this.votButton.container.dataset.position = this.data.buttonPos; this.votMenu.container.dataset.position = this.data.buttonPos; if (this.container.clientWidth && this.container.clientWidth > 550) { - await storage/* votStorage */.d.set("buttonPos", this.data.buttonPos); + await votStorage.set("buttonPos", this.data.buttonPos); } } }); @@ -5562,7 +6842,7 @@ class VideoHandler { }); this.votDownloadSubtitlesButton.addEventListener("click", async () => { - const srtContent = jsonToSrt(this.YandexSubtitles); + const srtContent = convertSubs(this.yandexSubtitles, "srt"); const blob = new Blob([srtContent], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); @@ -5583,8 +6863,9 @@ class VideoHandler { this.votVideoVolumeSlider.input.addEventListener("input", (e) => { const value = Number(e.target.value); - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = - `${value}%`; + this.votVideoVolumeSlider.label.querySelector( + "strong", + ).innerHTML = `${value}%`; this.setVideoVolume(value / 100); if (this.data.syncVolume) { this.syncVolumeWrapper("video", value); @@ -5596,7 +6877,7 @@ class VideoHandler { (e) => { (async () => { this.data.defaultVolume = Number(e.target.value); - await storage/* votStorage */.d.set("defaultVolume", this.data.defaultVolume); + await votStorage.set("defaultVolume", this.data.defaultVolume); this.votVideoTranslationVolumeSlider.label.querySelector( "strong", ).innerHTML = `${this.data.defaultVolume}%`; @@ -5623,9 +6904,9 @@ class VideoHandler { this.votAutoTranslateCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.autoTranslate = Number(e.target.checked); - await storage/* votStorage */.d.set("autoTranslate", this.data.autoTranslate); + await votStorage.set("autoTranslate", this.data.autoTranslate); await this.autoTranslate(); - debug/* default */.A.log( + utils_debug.log( "autoTranslate value changed. New value: ", this.data.autoTranslate, ); @@ -5637,11 +6918,11 @@ class VideoHandler { (e) => { (async () => { this.data.dontTranslateYourLang = Number(e.target.checked); - await storage/* votStorage */.d.set( + await votStorage.set( "dontTranslateYourLang", this.data.dontTranslateYourLang, ); - debug/* default */.A.log( + utils_debug.log( "dontTranslateYourLang value changed. New value: ", this.data.dontTranslateYourLang, ); @@ -5652,11 +6933,11 @@ class VideoHandler { this.votAutoSetVolumeCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.autoSetVolumeYandexStyle = Number(e.target.checked); - await storage/* votStorage */.d.set( + await votStorage.set( "autoSetVolumeYandexStyle", this.data.autoSetVolumeYandexStyle, ); - debug/* default */.A.log( + utils_debug.log( "autoSetVolumeYandexStyle value changed. New value: ", this.data.autoSetVolumeYandexStyle, ); @@ -5667,17 +6948,18 @@ class VideoHandler { (async () => { const presetAutoVolume = Number(e.target.value); this.data.autoVolume = (presetAutoVolume / 100).toFixed(2); - await storage/* votStorage */.d.set("autoVolume", this.data.autoVolume); - this.votAutoSetVolumeSlider.label.querySelector("strong").innerHTML = - `${presetAutoVolume}%`; + await votStorage.set("autoVolume", this.data.autoVolume); + this.votAutoSetVolumeSlider.label.querySelector( + "strong", + ).innerHTML = `${presetAutoVolume}%`; })(); }); this.votShowVideoSliderCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.showVideoSlider = Number(e.target.checked); - await storage/* votStorage */.d.set("showVideoSlider", this.data.showVideoSlider); - debug/* default */.A.log( + await votStorage.set("showVideoSlider", this.data.showVideoSlider); + utils_debug.log( "showVideoSlider value changed. New value: ", this.data.showVideoSlider, ); @@ -5690,8 +6972,8 @@ class VideoHandler { this.votAudioBoosterCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.audioBooster = Number(e.target.checked); - await storage/* votStorage */.d.set("audioBooster", this.data.audioBooster); - debug/* default */.A.log( + await votStorage.set("audioBooster", this.data.audioBooster); + utils_debug.log( "audioBooster value changed. New value: ", this.data.audioBooster, ); @@ -5700,7 +6982,7 @@ class VideoHandler { this.votVideoTranslationVolumeSlider.input.value; this.votVideoTranslationVolumeSlider.input.max = this.data .audioBooster - ? config/* maxAudioVolume */.T8 + ? maxAudioVolume : 100; if (!this.data.audioBooster) { this.votVideoTranslationVolumeSlider.input.value = @@ -5718,8 +7000,8 @@ class VideoHandler { accessToken: e.target.value, expires: new Date().getTime(), }; - await storage/* votStorage */.d.set("udemyData", this.data.udemyData); - debug/* default */.A.log( + await votStorage.set("udemyData", this.data.udemyData); + utils_debug.log( "udemyData value changed. New value: ", this.data.udemyData, ); @@ -5730,8 +7012,8 @@ class VideoHandler { this.votSyncVolumeCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.syncVolume = Number(e.target.checked); - await storage/* votStorage */.d.set("syncVolume", this.data.syncVolume); - debug/* default */.A.log( + await votStorage.set("syncVolume", this.data.syncVolume); + utils_debug.log( "syncVolume value changed. New value: ", this.data.syncVolume, ); @@ -5743,11 +7025,11 @@ class VideoHandler { (e) => { (async () => { this.data.translateAPIErrors = Number(e.target.checked); - await storage/* votStorage */.d.set( + await votStorage.set( "translateAPIErrors", this.data.translateAPIErrors, ); - debug/* default */.A.log( + utils_debug.log( "translateAPIErrors value changed. New value: ", this.data.translateAPIErrors, ); @@ -5760,7 +7042,7 @@ class VideoHandler { this.votSubtitlesMaxLengthSlider.input.addEventListener("input", (e) => { (async () => { this.data.subtitlesMaxLength = Number(e.target.value); - await storage/* votStorage */.d.set( + await votStorage.set( "subtitlesMaxLength", this.data.subtitlesMaxLength, ); @@ -5776,8 +7058,8 @@ class VideoHandler { (e) => { (async () => { this.data.highlightWords = Number(e.target.checked); - await storage/* votStorage */.d.set("highlightWords", this.data.highlightWords); - debug/* default */.A.log( + await votStorage.set("highlightWords", this.data.highlightWords); + utils_debug.log( "highlightWords value changed. New value: ", this.data.highlightWords, ); @@ -5789,15 +7071,15 @@ class VideoHandler { this.votShowPiPButtonCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.showPiPButton = Number(e.target.checked); - await storage/* votStorage */.d.set("showPiPButton", this.data.showPiPButton); - debug/* default */.A.log( + await votStorage.set("showPiPButton", this.data.showPiPButton); + utils_debug.log( "showPiPButton value changed. New value: ", this.data.showPiPButton, ); this.votButton.pipButton.hidden = - !(0,utils/* isPiPAvailable */.Bs)() || !this.data.showPiPButton; + !isPiPAvailable() || !this.data.showPiPButton; this.votButton.separator2.hidden = - !(0,utils/* isPiPAvailable */.Bs)() || !this.data.showPiPButton; + !isPiPAvailable() || !this.data.showPiPButton; })(); }); @@ -5805,9 +7087,9 @@ class VideoHandler { this.votM3u8ProxyHostTextfield.input.addEventListener("change", (e) => { (async () => { - this.data.m3u8ProxyHost = e.target.value || config/* m3u8ProxyHost */.se; - await storage/* votStorage */.d.set("m3u8ProxyHost", this.data.m3u8ProxyHost); - debug/* default */.A.log( + this.data.m3u8ProxyHost = e.target.value || m3u8ProxyHost; + await votStorage.set("m3u8ProxyHost", this.data.m3u8ProxyHost); + utils_debug.log( "m3u8ProxyHost value changed. New value: ", this.data.m3u8ProxyHost, ); @@ -5816,9 +7098,9 @@ class VideoHandler { this.votProxyWorkerHostTextfield.input.addEventListener("change", (e) => { (async () => { - this.data.proxyWorkerHost = e.target.value || config/* proxyWorkerHost */.Pm; - await storage/* votStorage */.d.set("proxyWorkerHost", this.data.proxyWorkerHost); - debug/* default */.A.log( + this.data.proxyWorkerHost = e.target.value || proxyWorkerHost; + await votStorage.set("proxyWorkerHost", this.data.proxyWorkerHost); + utils_debug.log( "proxyWorkerHost value changed. New value: ", this.data.proxyWorkerHost, ); @@ -5829,8 +7111,8 @@ class VideoHandler { this.votAudioProxyCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.audioProxy = Number(e.target.checked); - await storage/* votStorage */.d.set("audioProxy", this.data.audioProxy); - debug/* default */.A.log( + await votStorage.set("audioProxy", this.data.audioProxy); + utils_debug.log( "audioProxy value changed. New value: ", this.data.audioProxy, ); @@ -5839,12 +7121,12 @@ class VideoHandler { this.votResetSettingsButton.addEventListener("click", () => { (async () => { - localizationProvider/* localizationProvider */.j.reset(); - const valuesForClear = await storage/* votStorage */.d.list(); + localizationProvider.reset(); + const valuesForClear = await votStorage.list(); for (let i = 0; i < valuesForClear.length; i++) { const v = valuesForClear[i]; - if (!localizationProvider/* localizationProvider */.j.gmValues.includes(v)) { - storage/* votStorage */.d.syncDelete(v); + if (!localizationProvider.gmValues.includes(v)) { + votStorage.syncDelete(v); } } window.location.reload(); @@ -5977,7 +7259,7 @@ class VideoHandler { const isSettings = settings.contains(e); const isTempDialog = tempDialog?.contains(e) ?? false; - debug/* default */.A.log( + utils_debug.log( `[document click] ${isButton} ${isMenu} ${isVideo} ${isSettings} ${isTempDialog}`, ); if (!(!isButton && !isMenu && !isSettings && !isTempDialog)) return; @@ -6000,6 +7282,8 @@ class VideoHandler { eContainer = document.querySelector('div[data-testid="videoPlayer"]'); } else if (this.site.host === "yandexdisk") { eContainer = document.querySelector(".video-player__player"); + } else if (this.site.host === "reddit") { + eContainer = document.querySelector("shreddit-post"); } else { eContainer = this.container; } @@ -6043,25 +7327,29 @@ class VideoHandler { this.container.style.height = "100%"; } - addExtraEventListener(this.video, "canplaythrough", async () => { + addExtraEventListener(this.video, "canplay", async () => { // Временное решение if (this.site.host === "rutube" && this.video.src) { return; } - if ((0,utils/* getVideoId */.jI)(this.site.host, this.video) === this.videoData.videoId) + if ( + (await getVideoID(this.site, window.location.href)) === + this.videoData.videoId + ) return; await this.handleSrcChanged(); - debug/* default */.A.log("lipsync mode is loadeddata"); + utils_debug.log("lipsync mode is loadeddata"); await this.autoTranslate(); }); - addExtraEventListener(this.video, "emptied", () => { + addExtraEventListener(this.video, "emptied", async () => { if ( this.video.src && - (0,utils/* getVideoId */.jI)(this.site.host, this.video) === this.videoData.videoId + (await getVideoID(this.site, window.location.href)) === + this.videoData.videoId ) return; - debug/* default */.A.log("lipsync mode is emptied"); + utils_debug.log("lipsync mode is emptied"); this.videoData = ""; this.stopTranslation(); }); @@ -6093,46 +7381,46 @@ class VideoHandler { } async changeSubtitlesLang(subs) { - debug/* default */.A.log("[onchange] subtitles", subs); + utils_debug.log("[onchange] subtitles", subs); this.votSubtitlesSelect.setSelected(subs); if (subs === "disabled") { this.votSubtitlesSelect.setTitle( - localizationProvider/* localizationProvider */.j.get("VOTSubtitlesDisabled"), + localizationProvider.get("VOTSubtitlesDisabled"), ); this.subtitlesWidget.setContent(null); this.votDownloadSubtitlesButton.hidden = true; - this.YandexSubtitles = null; + this.yandexSubtitles = null; } else { const fetchedSubs = await fetchSubtitles( this.subtitlesList.at(parseInt(subs)), ); this.subtitlesWidget.setContent(fetchedSubs); this.votDownloadSubtitlesButton.hidden = false; - this.YandexSubtitles = fetchedSubs; + this.yandexSubtitles = fetchedSubs; } } async updateSubtitlesLangSelect() { const updatedOptions = [ { - label: localizationProvider/* localizationProvider */.j.get("VOTSubtitlesDisabled"), + label: localizationProvider.get("VOTSubtitlesDisabled"), value: "disabled", selected: true, disabled: false, }, ...this.subtitlesList.map((s, idx) => ({ label: - (localizationProvider/* localizationProvider */.j.get("langs")[s.language] ?? + (localizationProvider.get("langs")[s.language] ?? s.language.toUpperCase()) + (s.translatedFromLanguage - ? ` ${localizationProvider/* localizationProvider */.j.get("VOTTranslatedFrom")} ${ - localizationProvider/* localizationProvider */.j.get("langs")[s.translatedFromLanguage] ?? + ? ` ${localizationProvider.get("VOTTranslatedFrom")} ${ + localizationProvider.get("langs")[s.translatedFromLanguage] ?? s.translatedFromLanguage.toUpperCase() }` : "") + (s.source !== "yandex" ? ` ${s.source}` : "") + (s.isAutoGenerated - ? ` (${localizationProvider/* localizationProvider */.j.get("VOTAutogenerated")})` + ? ` (${localizationProvider.get("VOTAutogenerated")})` : ""), value: idx, selected: false, @@ -6150,7 +7438,7 @@ class VideoHandler { if (!this.videoData.videoId) { console.error( - `[VOT] ${localizationProvider/* localizationProvider */.j.getDefault("VOTNoVideoIDFound")}`, + `[VOT] ${localizationProvider.getDefault("VOTNoVideoIDFound")}`, ); this.subtitlesList = []; this.subtitlesListVideoId = null; @@ -6165,11 +7453,7 @@ class VideoHandler { return; } - this.subtitlesList = await getSubtitles( - this.site, - this.videoData.videoId, - this.videoData.detectedLanguage, - ); + this.subtitlesList = await subtitles_getSubtitles(this.votClient, this.videoData); if (!this.subtitlesList) { await this.changeSubtitlesLang("disabled"); } else { @@ -6182,7 +7466,7 @@ class VideoHandler { getVideoVolume() { let videoVolume = this.video?.volume; if (["youtube", "googledrive"].includes(this.site.host)) { - videoVolume = youtubeUtils/* default */.A.getVideoVolume() ?? videoVolume; + videoVolume = youtubeUtils.getVideoVolume() ?? videoVolume; } return videoVolume; } @@ -6190,7 +7474,7 @@ class VideoHandler { // Set video volume in 0.00-1.00 format setVideoVolume(volume) { if (["youtube", "googledrive"].includes(this.site.host)) { - const videoVolume = youtubeUtils/* default */.A.setVideoVolume(volume); + const videoVolume = youtubeUtils.setVideoVolume(volume); if (videoVolume) { return; } @@ -6200,7 +7484,7 @@ class VideoHandler { isMuted() { return ["youtube", "googledrive"].includes(this.site.host) - ? youtubeUtils/* default */.A.isMuted() + ? youtubeUtils.isMuted() : this.video?.muted; } @@ -6210,8 +7494,9 @@ class VideoHandler { const newSlidersVolume = Math.round(videoVolume); this.votVideoVolumeSlider.input.value = newSlidersVolume; - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = - `${newSlidersVolume}%`; + this.votVideoVolumeSlider.label.querySelector( + "strong", + ).innerHTML = `${newSlidersVolume}%`; ui.updateSlider(this.votVideoVolumeSlider.input); if (this.data.syncVolume === 1) { @@ -6221,10 +7506,10 @@ class VideoHandler { setSelectMenuValues(from, to) { this.votTranslationLanguageSelect.fromSelect.setTitle( - localizationProvider/* localizationProvider */.j.get("langs")[from], + localizationProvider.get("langs")[from], ); this.votTranslationLanguageSelect.toSelect.setTitle( - localizationProvider/* localizationProvider */.j.get("langs")[to], + localizationProvider.get("langs")[to], ); this.votTranslationLanguageSelect.fromSelect.setSelected(from); this.votTranslationLanguageSelect.toSelect.setSelected(to); @@ -6265,28 +7550,28 @@ class VideoHandler { } async getVideoData() { + // TODO: запатчить чтобы использовался уже существующий service? + const { duration, url, videoId, host } = await getVideoData( + window.location.href, + ); const videoData = { - // ! should be null for ALL websites except coursera and udemy ! - // else use direct link: `{url: xxx.mp4}` translationHelp: null, - isStream: false, // by default, we request the translation of the video - duration: this.video?.duration || 343, // ! if 0 - we get 400 error - videoId: (0,utils/* getVideoId */.jI)(this.site.host, this.video), + // by default, we request the translation of the video + isStream: false, + // ! if 0 - we get 400 error + duration: this.video?.duration || duration || config.defaultDuration, + videoId, + url, + host, detectedLanguage: this.translateFromLang, responseLanguage: this.translateToLang, }; - if (!videoData.videoId) { - this.ytData = {}; - return videoData; - } - if (this.site.host === "youtube") { - this.ytData = await youtubeUtils/* default */.A.getVideoData(); - videoData.isStream = this.ytData.isLive; - if (this.ytData.title) { - videoData.detectedLanguage = this.ytData.detectedLanguage; - videoData.responseLanguage = this.translateToLang; + const youtubeData = await youtubeUtils.getVideoData(); + videoData.isStream = youtubeData.isLive; + if (youtubeData.title) { + videoData.detectedLanguage = youtubeData.detectedLanguage; } } else if (["rutube", "ok.ru", "mail_ru"].includes(this.site.host)) { videoData.detectedLanguage = "ru"; @@ -6309,25 +7594,8 @@ class VideoHandler { url: coursehunterData.url, }; videoData.duration = coursehunterData.duration || videoData.duration; - } else if (this.site.host === "bannedvideo") { - const bannedvideoData = await bannedvideoUtils.getVideoData( - videoData.videoId, - ); - videoData.translationHelp = { - url: bannedvideoData.url, - }; - - videoData.duration = bannedvideoData.duration || videoData.duration; - videoData.isStream = bannedvideoData.live; } else if (this.site.host === "weverse") { - const weverseData = await weverseUtils.getVideoData(); videoData.detectedLanguage = "ko"; - if (weverseData) { - videoData.translationHelp = { - url: weverseData.url, - }; - videoData.duration = weverseData.duration || videoData.duration; - } } else if (this.site.host === "udemy") { const udemyData = await udemyUtils.getVideoData( this.data.udemyData, @@ -6336,11 +7604,6 @@ class VideoHandler { videoData.duration = udemyData.duration || videoData.duration; videoData.detectedLanguage = udemyData.detectedLanguage; videoData.translationHelp = udemyData.translationHelp; - } else if (this.site.host === "bitchute") { - // to avoid creating a separate file with the same functionality - videoData.translationHelp = { - url: videoData.videoId, - }; } else if ( [ "bilibili", @@ -6361,9 +7624,10 @@ class VideoHandler { } return videoData; } + videoValidator() { if (["youtube", "ok.ru", "vk"].includes(this.site.host)) { - debug/* default */.A.log("VideoValidator videoData: ", this.videoData); + utils_debug.log("VideoValidator videoData: ", this.videoData); if ( this.data.dontTranslateYourLang === 1 && this.videoData.detectedLanguage === this.data.dontTranslateLanguage @@ -6371,20 +7635,16 @@ class VideoHandler { throw new VOTLocalizedError("VOTDisableFromYourLang"); } } - // if (this.ytData.isPremiere) { - // throw new VOTLocalizedError("VOTPremiere"); - // } - // if (this.ytData.isLive) { - // throw new VOTLocalizedError("VOTLiveNotSupported"); - // } + if (!this.videoData.isStream && this.videoData.duration > 14_400) { throw new VOTLocalizedError("VOTVideoIsTooLong"); } + return true; } lipSync(mode = false) { - debug/* default */.A.log("lipsync video", this.video); + utils_debug.log("lipsync video", this.video); if (!this.video) { return; } @@ -6392,12 +7652,12 @@ class VideoHandler { this.audio.playbackRate = this.video.playbackRate; if (!mode) { - debug/* default */.A.log("lipsync mode is not set"); + utils_debug.log("lipsync mode is not set"); return; } if (mode == "play") { - debug/* default */.A.log("lipsync mode is play"); + utils_debug.log("lipsync mode is play"); const audioPromise = this.audio.play(); if (audioPromise !== undefined) { audioPromise.catch((e) => { @@ -6405,15 +7665,15 @@ class VideoHandler { if (e.name === "NotAllowedError") { this.transformBtn( "error", - localizationProvider/* localizationProvider */.j.get("grantPermissionToAutoPlay"), + localizationProvider.get("grantPermissionToAutoPlay"), ); throw new VOTLocalizedError("grantPermissionToAutoPlay"); } else if (e.name === "NotSupportedError") { this.transformBtn( "error", sitesChromiumBlocked.includes(window.location.hostname) - ? localizationProvider/* localizationProvider */.j.get("neededAdditionalExtension") - : localizationProvider/* localizationProvider */.j.get("audioFormatNotSupported"), + ? localizationProvider.get("neededAdditionalExtension") + : localizationProvider.get("audioFormatNotSupported"), ); throw sitesChromiumBlocked.includes(window.location.hostname) ? new VOTLocalizedError("neededAdditionalExtension") @@ -6425,19 +7685,19 @@ class VideoHandler { } // video is inactive if (["pause", "stop", "waiting"].includes(mode)) { - debug/* default */.A.log(`lipsync mode is ${mode}`); + utils_debug.log(`lipsync mode is ${mode}`); this.audio.pause(); } if (mode == "playing") { - debug/* default */.A.log("lipsync mode is playing"); + utils_debug.log("lipsync mode is playing"); this.audio.play(); } } // Define a function to handle common events handleVideoEvent(event) { - debug/* default */.A.log(`video ${event.type}`); + utils_debug.log(`video ${event.type}`); this.lipSync(event.type); } @@ -6453,8 +7713,8 @@ class VideoHandler { this.votVideoTranslationVolumeSlider.container.hidden = true; this.votDownloadButton.hidden = true; this.downloadTranslationUrl = null; - this.transformBtn("none", localizationProvider/* localizationProvider */.j.get("translateVideo")); - debug/* default */.A.log(`Volume on start: ${this.volumeOnStart}`); + this.transformBtn("none", localizationProvider.get("translateVideo")); + utils_debug.log(`Volume on start: ${this.volumeOnStart}`); if (this.volumeOnStart) { this.setVideoVolume(this.volumeOnStart); } @@ -6462,13 +7722,13 @@ class VideoHandler { clearInterval(this.streamPing); clearTimeout(this.autoRetry); this.hls?.destroy(); - this.hls = (0,utils/* initHls */.CK)(); + this.hls = initHls(); this.firstSyncVolume = true; } async translateExecutor(VIDEO_ID) { - debug/* default */.A.log("Run translateFunc", VIDEO_ID); - this.translateFunc( + utils_debug.log("Run translateFunc", VIDEO_ID); + await this.translateFunc( VIDEO_ID, this.videoData.isStream, this.videoData.detectedLanguage, @@ -6478,9 +7738,9 @@ class VideoHandler { } async updateTranslationErrorMsg(errorMessage) { - const translationTake = localizationProvider/* localizationProvider */.j.get("translationTake"); - const VOTTranslatingError = localizationProvider/* localizationProvider */.j.get("VOTTranslatingError"); - const lang = localizationProvider/* localizationProvider */.j.lang; + const translationTake = localizationProvider.get("translationTake"); + const VOTTranslatingError = localizationProvider.get("VOTTranslatingError"); + const lang = localizationProvider.lang; if (errorMessage?.name === "VOTLocalizedError") { this.transformBtn("error", errorMessage.localizedMessage); @@ -6489,7 +7749,7 @@ class VideoHandler { !errorMessage.includes(translationTake) && lang !== "ru" ) { - const translatedMessage = await (0,translateApis/* translate */.Tl)(errorMessage, "ru", lang); + const translatedMessage = await translate(errorMessage, "ru", lang); this.transformBtn("error", VOTTranslatingError); this.transformBtn("error", translatedMessage); } else { @@ -6506,8 +7766,9 @@ class VideoHandler { if (this.data.autoSetVolumeYandexStyle === 1) { this.votVideoVolumeSlider.input.value = this.data.autoVolume * 100; - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = - `${this.data.autoVolume * 100}%`; + this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = `${ + this.data.autoVolume * 100 + }%`; ui.updateSlider(this.votVideoVolumeSlider.input); } @@ -6535,17 +7796,7 @@ class VideoHandler { this.audio.src = proxiedAudioUrl; } - this.volumeOnStart = this.getVideoVolume(); - if (typeof this.data.defaultVolume === "number") { - this.gainNode.gain.value = this.data.defaultVolume / 100; - } - if ( - typeof this.data.autoSetVolumeYandexStyle === "number" && - this.data.autoSetVolumeYandexStyle - ) { - this.setVideoVolume(this.data.autoVolume); - } - + this.setupAudioSettings(); switch (this.site.host) { case "twitter": document @@ -6561,12 +7812,12 @@ class VideoHandler { for (const e of videoLipSyncEvents) { this.video.addEventListener(e, this.handleVideoEventBound); } - this.transformBtn("success", localizationProvider/* localizationProvider */.j.get("disableTranslate")); + this.transformBtn("success", localizationProvider.get("disableTranslate")); this.afterUpdateTranslation(audioUrl); } // Define a function to translate a video and handle the callback - translateFunc( + async translateFunc( VIDEO_ID, isStream, requestLang, @@ -6574,152 +7825,60 @@ class VideoHandler { translationHelp, ) { console.log("[VOT] Video Data: ", this.videoData); - const videoURL = translationHelp?.url - ? translationHelp.url - : `${this.site.url}${VIDEO_ID}`; - // fix enabling the old requested voiceover when changing the language to the native language (#414) - debug/* default */.A.log("Run videoValidator"); + utils_debug.log("Run videoValidator"); this.videoValidator(); if (isStream) { - debug/* default */.A.log("Executed stream translation"); - translateStream( - videoURL, + let translateRes = await this.translateStreamImpl( + this.videoData, requestLang, responseLang, - async (success, reqInterval, resOrError) => { - debug/* default */.A.log("[exec callback] translateStream callback"); - if ((0,utils/* getVideoId */.jI)(this.site.host, this.video) !== VIDEO_ID) return; - if (!success || !resOrError.translatedInfo) { - await this.updateTranslationErrorMsg(resOrError); - - if (reqInterval === 10) { - // if wait translating - clearTimeout(this.autoRetry); - this.autoRetry = setTimeout( - () => - this.translateFunc( - VIDEO_ID, - isStream, - requestLang, - responseLang, - translationHelp, - ), - reqInterval * 1000, - ); - } - - return; - } - - this.transformBtn( - "success", - localizationProvider/* localizationProvider */.j.get("disableTranslate"), - ); - - console.log(resOrError); - const pingId = resOrError.pingId; - debug/* default */.A.log(`Stream pingId: ${pingId}`); - // if you don't make ping requests, then the translation of the stream dies - this.streamPing = setInterval( - async () => - await rsp(pingId, (result) => - debug/* default */.A.log("Stream ping result: ", result), - ), - reqInterval * 1000, - ); - - debug/* default */.A.log(resOrError.translatedInfo.url); - const streamURL = `https://${ - this.data.m3u8ProxyHost - }/?all=yes&origin=${encodeURIComponent( - "https://strm.yandex.ru", - )}&referer=${encodeURIComponent( - "https://strm.yandex.ru", - )}&url=${encodeURIComponent(resOrError.translatedInfo.url)}`; - debug/* default */.A.log(streamURL); - - if (this.hls) { - this.hls.on(Hls.Events.MEDIA_ATTACHED, function () { - debug/* default */.A.log("audio and hls.js are now bound together !"); - }); - this.hls.on(Hls.Events.MANIFEST_PARSED, function (data) { - debug/* default */.A.log( - "manifest loaded, found " + - data?.levels?.length + - " quality level", - ); - }); - this.hls.loadSource(streamURL); - this.hls.attachMedia(this.audio); - this.hls.on(Hls.Events.ERROR, function (data) { - if (data.fatal) { - switch (data.type) { - case Hls.ErrorTypes.MEDIA_ERROR: - console.log( - "fatal media error encountered, try to recover", - ); - this.hls.recoverMediaError(); - break; - case Hls.ErrorTypes.NETWORK_ERROR: - console.error("fatal network error encountered", data); - // All retries and media options have been exhausted. - // Immediately trying to restart loading could cause loop loading. - // Consider modifying loading policies to best fit your asset and network - // conditions (manifestLoadPolicy, playlistLoadPolicy, fragLoadPolicy). - break; - default: - // cannot recover - this.hls.destroy(); - break; - } - } - }); - debug/* default */.A.log(this.hls); - } else if (this.audio.canPlayType("application/vnd.apple.mpegurl")) { - // safari - this.audio.src = streamURL; - } else { - // browser doesn't support m3u8 (hls unsupported and it's not a safari) - throw new VOTLocalizedError("audioFormatNotSupported"); - } + ); - if (this.site.host === "youtube") { - youtubeUtils/* default */.A.videoSeek(this.video, 10); // 10 is the most successful number for streaming. With it, the audio is not so far behind the original - } + if (!translateRes) { + utils_debug.log("Skip translation"); + return; + } - this.volumeOnStart = this.getVideoVolume(); - if (typeof this.data.defaultVolume === "number") { - this.gainNode.gain.value = this.data.defaultVolume / 100; - } + this.transformBtn( + "success", + localizationProvider.get("disableTranslate"), + ); - if ( - typeof this.data.autoSetVolumeYandexStyle === "number" && - this.data.autoSetVolumeYandexStyle - ) { - this.setVideoVolume(this.data.autoVolume); - } + const streamURL = `https://${ + this.data.m3u8ProxyHost + }/?all=yes&origin=${encodeURIComponent( + "https://strm.yandex.ru", + )}&referer=${encodeURIComponent( + "https://strm.yandex.ru", + )}&url=${encodeURIComponent(translateRes.result.url)}`; + + if (this.hls) { + this.setupHLS(streamURL); + } else if (this.audio.canPlayType("application/vnd.apple.mpegurl")) { + // safari + this.audio.src = streamURL; + } else { + // browser doesn't support m3u8 (hls unsupported and it isn't a safari) + throw new VOTLocalizedError("audioFormatNotSupported"); + } - if ( - !this.video.src && - !this.video.currentSrc && - !this.video.srcObject - ) { - this.stopTranslation(); - return; - } + if (this.site.host === "youtube") { + youtubeUtils.videoSeek(this.video, 10); // 10 is the most successful number for streaming. With it, the audio is not so far behind the original + } - if (this.video && !this.video.paused) this.lipSync("play"); - for (const e of videoLipSyncEvents) { - this.video.addEventListener(e, this.handleVideoEventBound); - } + this.setupAudioSettings(); + if (!this.video.src && !this.video.currentSrc && !this.video.srcObject) { + return this.stopTranslation(); + } - this.afterUpdateTranslation(streamURL); - }, - ); + if (this.video && !this.video.paused) this.lipSync("play"); + for (const e of videoLipSyncEvents) { + this.video.addEventListener(e, this.handleVideoEventBound); + } - return; + return this.afterUpdateTranslation(streamURL); } if (["udemy", "coursera"].includes(this.site.host) && !translationHelp) { @@ -6736,75 +7895,93 @@ class VideoHandler { if (cachedTranslation) { this.updateTranslation(cachedTranslation.url); - debug/* default */.A.log("[translateFunc] A cached translate was received"); + utils_debug.log("[translateFunc] Cached translation was received"); return; } - const timeoutDuration = this.subtitlesList.some( - (item) => item.source === "yandex", - ) - ? 20_000 - : 30_000; - - translateVideo( - videoURL, - this.videoData.duration, + let translateRes = await this.translateVideoImpl( + this.videoData, requestLang, responseLang, translationHelp, - async (success, urlOrError) => { - debug/* default */.A.log("[exec callback] translateVideo callback"); - if ((0,utils/* getVideoId */.jI)(this.site.host, this.video) !== VIDEO_ID) return; - if (!success) { - await this.updateTranslationErrorMsg(urlOrError); + ); - // if the error line contains information that the translation is being performed, then we wait - if ( - urlOrError.includes(localizationProvider/* localizationProvider */.j.get("translationTake")) - ) { - clearTimeout(this.autoRetry); - this.autoRetry = setTimeout( - () => - this.translateFunc( - VIDEO_ID, - isStream, - requestLang, - responseLang, - translationHelp, - ), - timeoutDuration, - ); - } - console.error("[VOT]", urlOrError); - return; - } + utils_debug.log("[translateRes]", translateRes); + if (!translateRes) { + utils_debug.log("Skip translation"); + return; + } - this.updateTranslation(urlOrError); - if ( - !this.subtitlesList.some( - (item) => - item.source === "yandex" && - item.translatedFromLanguage === this.videoData.detectedLanguage && - item.language === this.videoData.responseLanguage, - ) - ) { - this.subtitlesList = await getSubtitles( - this.site, - this.videoData.videoId, - this.videoData.detectedLanguage, - ); - await this.updateSubtitlesLangSelect(); + this.updateTranslation(translateRes.url); + if ( + !this.subtitlesList.some( + (item) => + item.source === "yandex" && + item.translatedFromLanguage === this.videoData.detectedLanguage && + item.language === this.videoData.responseLanguage, + ) + ) { + this.subtitlesList = await subtitles_getSubtitles(this.votClient, this.videoData); + await this.updateSubtitlesLangSelect(); + } + + this.videoTranslations.push({ + videoId: VIDEO_ID, + from: requestLang, + to: responseLang, + url: translateRes.url, + expires: Date.now() / 1000 + this.videoTranslationTTL, + }); + } + + // Вспомогательные методы + setupHLS(streamURL) { + this.hls.on(Hls.Events.MEDIA_ATTACHED, function () { + utils_debug.log("audio and hls.js are now bound together !"); + }); + this.hls.on(Hls.Events.MANIFEST_PARSED, function (data) { + utils_debug.log( + "manifest loaded, found " + data?.levels?.length + " quality level", + ); + }); + this.hls.loadSource(streamURL); + this.hls.attachMedia(this.audio); + this.hls.on(Hls.Events.ERROR, function (data) { + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.MEDIA_ERROR: + console.log("fatal media error encountered, try to recover"); + this.hls.recoverMediaError(); + break; + case Hls.ErrorTypes.NETWORK_ERROR: + console.error("fatal network error encountered", data); + // All retries and media options have been exhausted. + // Immediately trying to restart loading could cause loop loading. + // Consider modifying loading policies to best fit your asset and network + // conditions (manifestLoadPolicy, playlistLoadPolicy, fragLoadPolicy). + break; + default: + // cannot recover + this.hls.destroy(); + break; } + } + }); + utils_debug.log(this.hls); + } - this.videoTranslations.push({ - videoId: VIDEO_ID, - from: requestLang, - to: responseLang, - url: urlOrError, - expires: Date.now() / 1000 + this.videoTranslationTTL, - }); - }, - ); + setupAudioSettings() { + this.volumeOnStart = this.getVideoVolume(); + if (typeof this.data.defaultVolume === "number") { + this.gainNode.gain.value = this.data.defaultVolume / 100; + } + + if ( + typeof this.data.autoSetVolumeYandexStyle === "number" && + this.data.autoSetVolumeYandexStyle + ) { + this.setVideoVolume(this.data.autoVolume); + } } // Define a function to stop translation and clean up @@ -6814,7 +7991,7 @@ class VideoHandler { } async handleSrcChanged() { - debug/* default */.A.log("[VideoHandler] src changed", this); + utils_debug.log("[VideoHandler] src changed", this); this.firstPlay = true; @@ -6844,7 +8021,7 @@ class VideoHandler { } async release() { - debug/* default */.A.log("[VideoHandler] release"); + utils_debug.log("[VideoHandler] release"); this.initialized = false; this.releaseExtraEvents(); @@ -6869,7 +8046,7 @@ function getSites() { return false; }; - return config_sites.filter((e) => { + return sites.filter((e) => { return ( (Array.isArray(e.match) ? e.match.some(isMathes) : isMathes(e.match)) && e.host && @@ -6891,43 +8068,43 @@ function findContainer(site, video) { return container && container.shadowRoot ? container.parentElement : container; - } else { - const browserVersion = browserInfo.browser.version.split(".")[0]; - if ( - site.selector?.includes(":not") && - site.selector?.includes("*") && - browserVersion && - ((browserInfo.browser.name === "Chrome" && Number(browserVersion) < 88) || - (browserInfo.browser.name === "Firefox" && Number(browserVersion) < 84)) - ) { - const selector = site.selector.split(" *")[0]; - return selector - ? Array.from(document.querySelectorAll(selector)).find((e) => - e.contains(video), - ) - : video.parentElement; - } else { - return site.selector - ? Array.from(document.querySelectorAll(site.selector)).find((e) => - e.contains(video), - ) - : video.parentElement; - } } + + const browserVersion = browserInfo.browser.version.split(".")[0]; + if ( + site.selector?.includes(":not") && + site.selector?.includes("*") && + browserVersion && + ((browserInfo.browser.name === "Chrome" && Number(browserVersion) < 88) || + (browserInfo.browser.name === "Firefox" && Number(browserVersion) < 84)) + ) { + const selector = site.selector.split(" *")[0]; + return selector + ? Array.from(document.querySelectorAll(selector)).find((e) => + e.contains(video), + ) + : video.parentElement; + } + + return site.selector + ? Array.from(document.querySelectorAll(site.selector)).find((e) => + e.contains(video), + ) + : video.parentElement; } async function src_main() { - debug/* default */.A.log("Loading extension..."); + utils_debug.log("Loading extension..."); - await localizationProvider/* localizationProvider */.j.update(); + await localizationProvider.update(); - debug/* default */.A.log(`Selected menu language: ${localizationProvider/* localizationProvider */.j.lang}`); + utils_debug.log(`Selected menu language: ${localizationProvider.lang}`); if ( false ) {} - debug/* default */.A.log("Extension compatibility passed..."); + utils_debug.log("Extension compatibility passed..."); videoObserver.onVideoAdded.addListener((video) => { for (const site of getSites()) { @@ -6971,19 +8148,6 @@ src_main().catch((e) => { console.error("[VOT]", e); }); -// if (import.meta.webpackHot) { -// import.meta.webpackHot.monkeyReload(); -// import.meta.webpackHot.dispose(() => { -// for (const selector of [ -// ".vot-menu", -// ".vot-segmented-button", -// ".vot-subtitles-widget", -// ]) { -// document.querySelector(selector)?.remove(); -// } -// }); -// } - })(); /******/ })() diff --git a/dist/vot-min.user.js b/dist/vot-min.user.js index c30f4509..478eafaa 100644 --- a/dist/vot-min.user.js +++ b/dist/vot-min.user.js @@ -32,17 +32,49 @@ // @match *://*.pornhub.com/* // @match *://*.vk.com/* // @match *://*.vk.ru/* -// @match *://invidious.snopyta.org/* -// @match *://invidious.kavin.rocks/* -// @match *://vid.puffyan.us/* -// @match *://invidious.namazso.eu/* -// @match *://inv.riverside.rocks/* +// @match *://*.vimeo.com/* +// @match *://*.9gag.com/* +// @match *://*.twitter.com/* +// @match *://*.x.com/* +// @match *://*.facebook.com/* +// @match *://*.rutube.ru/* +// @match *://*.bilibili.com/* +// @match *://my.mail.ru/* +// @match *://*.bitchute.com/* +// @match *://*.coursera.org/learn/* +// @match *://*.udemy.com/course/* +// @match *://*.tiktok.com/* +// @match *://rumble.com/* +// @match *://*.eporner.com/* +// @match *://geo.dailymotion.com/* +// @match *://*.ok.ru/* +// @match *://trovo.live/* +// @match *://disk.yandex.ru/i/* +// @match *://coursehunter.net/* +// @match *://youtube.googleapis.com/embed/* +// @match *://*.banned.video/* +// @match *://*.weverse.io/* +// @match *://*.newgrounds.com/* +// @match *://*.egghead.io/* +// @match *://*.youku.com/* +// @match *://*.archive.org/* +// @match *://*.patreon.com/* +// @match *://*.reddit.com/* +// @match *://*.kodik.info/* +// @match *://*.kodik.biz/* +// @match *://*.kodik.cc/* +// @match *://*.kick.com/* +// @match *://developer.apple.com/* +// @match *://*/*.mp4* +// @match *://*.yewtu.be/* // @match *://yt.artemislena.eu/* // @match *://invidious.flokinet.to/* -// @match *://invidious.esmailelbob.xyz/* -// @match *://invidious.nerdvpn.de/* -// @match *://invidious.slipfox.xyz/* -// @match *://invidio.xamh.de/* +// @match *://iv.melmac.space/* +// @match *://inv.nadeko.net/* +// @match *://inv.tux.pizza/* +// @match *://invidious.private.coffee/* +// @match *://yt.drgnz.club/* +// @match *://vid.puffyan.us/* // @match *://invidious.dhusch.de/* // @match *://*.piped.video/* // @match *://piped.tokhmi.xyz/* @@ -72,19 +104,6 @@ // @match *://piped.jotoma.de/* // @match *://piped.pfcd.me/* // @match *://piped.frontendfriendly.xyz/* -// @match *://*.yewtu.be/* -// @match *://inv.vern.cc/* -// @match *://*.vimeo.com/* -// @match *://*.9gag.com/* -// @match *://*.twitter.com/* -// @match *://*.facebook.com/* -// @match *://*.rutube.ru/* -// @match *://*.bilibili.com/* -// @match *://my.mail.ru/* -// @match *://*.bitchute.com/* -// @match *://*.coursera.org/learn/* -// @match *://*.udemy.com/course/* -// @match *://*.tiktok.com/* // @match *://proxitok.pabloferreiro.es/* // @match *://proxitok.pussthecat.org/* // @match *://tok.habedieeh.re/* @@ -92,46 +111,32 @@ // @match *://proxitok.privacydev.net/* // @match *://tok.artemislena.eu/* // @match *://tok.adminforge.de/* -// @match *://tik.hostux.net/* // @match *://tt.vern.cc/* // @match *://cringe.whatever.social/* // @match *://proxitok.lunar.icu/* // @match *://proxitok.privacy.com.de/* -// @match *://rumble.com/* -// @match *://*.eporner.com/* // @match *://peertube.1312.media/* // @match *://tube.shanti.cafe/* -// @match *://bee-tube.fr/* +// @match *://*.bee-tube.fr/* // @match *://video.sadmin.io/* -// @match *://dalek.zone/* +// @match *://*.dalek.zone/* // @match *://review.peertube.biz/* -// @match *://peervideo.club/* +// @match *://*.peervideo.club/* // @match *://tube.la-dina.net/* // @match *://peertube.tmp.rcp.tf/* -// @match *://peertube.su/* -// @match *://geo.dailymotion.com/* -// @match *://*.ok.ru/* -// @match *://trovo.live/* -// @match *://disk.yandex.ru/i/* -// @match *://coursehunter.net/* -// @match *://youtube.googleapis.com/embed/* -// @match *://*.banned.video/* -// @match *://*.weverse.io/* -// @match *://*.newgrounds.com/* -// @match *://*.egghead.io/* -// @match *://*.youku.com/* -// @match *://*.archive.org/* -// @match *://*/*.mp4* +// @match *://*.peertube.su/* +// @match *://video.blender.org/* // @exclude file://*/*.mp4* // @connect yandex.ru // @connect yandex.net +// @connect timeweb.cloud // @connect raw.githubusercontent.com // @connect toil.cc // @connect deno.dev // @connect onrender.com // @connect workers.dev // @namespace vot-min -// @version 1.5.3.1 +// @version 1.6.0 // @icon https://translate.yandex.ru/icons/favicon.ico // @author sodapng, mynovelhost, Toil, SashaXser, MrSoczekXD // @homepageURL https://github.com/ilyhalight/voice-over-translation/issues @@ -140,4 +145,109 @@ // @supportURL https://github.com/ilyhalight/voice-over-translation/issues // ==/UserScript== -(()=>{var t={"./node_modules/bowser/es5.js":function(t){t.exports=function(t){var e={};function o(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,o),i.l=!0,i.exports}return o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)o.d(n,i,function(e){return t[e]}.bind(null,i));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=90)}({17:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n=o(18),i=function(){function t(){}return t.getFirstMatch=function(t,e){var o=e.match(t);return o&&o.length>0&&o[1]||""},t.getSecondMatch=function(t,e){var o=e.match(t);return o&&o.length>1&&o[2]||""},t.matchAndReturnConst=function(t,e,o){if(t.test(e))return o},t.getWindowsVersionName=function(t){switch(t){case"NT":return"NT";case"XP":case"NT 5.1":return"XP";case"NT 5.0":return"2000";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return}},t.getMacOSVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),10===e[0])switch(e[1]){case 5:return"Leopard";case 6:return"Snow Leopard";case 7:return"Lion";case 8:return"Mountain Lion";case 9:return"Mavericks";case 10:return"Yosemite";case 11:return"El Capitan";case 12:return"Sierra";case 13:return"High Sierra";case 14:return"Mojave";case 15:return"Catalina";default:return}},t.getAndroidVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),!(1===e[0]&&e[1]<5))return 1===e[0]&&e[1]<6?"Cupcake":1===e[0]&&e[1]>=6?"Donut":2===e[0]&&e[1]<2?"Eclair":2===e[0]&&2===e[1]?"Froyo":2===e[0]&&e[1]>2?"Gingerbread":3===e[0]?"Honeycomb":4===e[0]&&e[1]<1?"Ice Cream Sandwich":4===e[0]&&e[1]<4?"Jelly Bean":4===e[0]&&e[1]>=4?"KitKat":5===e[0]?"Lollipop":6===e[0]?"Marshmallow":7===e[0]?"Nougat":8===e[0]?"Oreo":9===e[0]?"Pie":void 0},t.getVersionPrecision=function(t){return t.split(".").length},t.compareVersions=function(e,o,n){void 0===n&&(n=!1);var i=t.getVersionPrecision(e),a=t.getVersionPrecision(o),r=Math.max(i,a),s=0,l=t.map([e,o],(function(e){var o=r-t.getVersionPrecision(e),n=e+new Array(o+1).join(".0");return t.map(n.split("."),(function(t){return new Array(20-t.length).join("0")+t})).reverse()}));for(n&&(s=r-Math.min(i,a)),r-=1;r>=s;){if(l[0][r]>l[1][r])return 1;if(l[0][r]===l[1][r]){if(r===s)return 0;r-=1}else if(l[0][r]1?i-1:0),r=1;r0){var r=Object.keys(o),l=s.default.find(r,(function(t){return e.isOS(t)}));if(l){var d=this.satisfies(o[l]);if(void 0!==d)return d}var c=s.default.find(r,(function(t){return e.isPlatform(t)}));if(c){var u=this.satisfies(o[c]);if(void 0!==u)return u}}if(a>0){var h=Object.keys(i),p=s.default.find(h,(function(t){return e.isBrowser(t,!0)}));if(void 0!==p)return this.compareVersion(i[p])}},e.isBrowser=function(t,e){void 0===e&&(e=!1);var o=this.getBrowserName().toLowerCase(),n=t.toLowerCase(),i=s.default.getBrowserTypeByAlias(n);return e&&i&&(n=i.toLowerCase()),n===o},e.compareVersion=function(t){var e=[0],o=t,n=!1,i=this.getBrowserVersion();if("string"==typeof i)return">"===t[0]||"<"===t[0]?(o=t.substr(1),"="===t[1]?(n=!0,o=t.substr(2)):e=[],">"===t[0]?e.push(1):e.push(-1)):"="===t[0]?o=t.substr(1):"~"===t[0]&&(n=!0,o=t.substr(1)),e.indexOf(s.default.compareVersions(i,o,n))>-1},e.isOS=function(t){return this.getOSName(!0)===String(t).toLowerCase()},e.isPlatform=function(t){return this.getPlatformType(!0)===String(t).toLowerCase()},e.isEngine=function(t){return this.getEngineName(!0)===String(t).toLowerCase()},e.is=function(t,e){return void 0===e&&(e=!1),this.isBrowser(t,e)||this.isOS(t)||this.isPlatform(t)},e.some=function(t){var e=this;return void 0===t&&(t=[]),t.some((function(t){return e.is(t)}))},t}();e.default=d,t.exports=e.default},92:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=/version\/(\d+(\.?_?\d+)+)/i,r=[{test:[/googlebot/i],describe:function(t){var e={name:"Googlebot"},o=i.default.getFirstMatch(/googlebot\/(\d+(\.\d+))/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/opera/i],describe:function(t){var e={name:"Opera"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opr\/|opios/i],describe:function(t){var e={name:"Opera"},o=i.default.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/SamsungBrowser/i],describe:function(t){var e={name:"Samsung Internet for Android"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Whale/i],describe:function(t){var e={name:"NAVER Whale Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MZBrowser/i],describe:function(t){var e={name:"MZ Browser"},o=i.default.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/focus/i],describe:function(t){var e={name:"Focus"},o=i.default.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/swing/i],describe:function(t){var e={name:"Swing"},o=i.default.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/coast/i],describe:function(t){var e={name:"Opera Coast"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opt\/\d+(?:.?_?\d+)+/i],describe:function(t){var e={name:"Opera Touch"},o=i.default.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/yabrowser/i],describe:function(t){var e={name:"Yandex Browser"},o=i.default.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/ucbrowser/i],describe:function(t){var e={name:"UC Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Maxthon|mxios/i],describe:function(t){var e={name:"Maxthon"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/epiphany/i],describe:function(t){var e={name:"Epiphany"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/puffin/i],describe:function(t){var e={name:"Puffin"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sleipnir/i],describe:function(t){var e={name:"Sleipnir"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/k-meleon/i],describe:function(t){var e={name:"K-Meleon"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/micromessenger/i],describe:function(t){var e={name:"WeChat"},o=i.default.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/qqbrowser/i],describe:function(t){var e={name:/qqbrowserlite/i.test(t)?"QQ Browser Lite":"QQ Browser"},o=i.default.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/msie|trident/i],describe:function(t){var e={name:"Internet Explorer"},o=i.default.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/\sedg\//i],describe:function(t){var e={name:"Microsoft Edge"},o=i.default.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/edg([ea]|ios)/i],describe:function(t){var e={name:"Microsoft Edge"},o=i.default.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/vivaldi/i],describe:function(t){var e={name:"Vivaldi"},o=i.default.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/seamonkey/i],describe:function(t){var e={name:"SeaMonkey"},o=i.default.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sailfish/i],describe:function(t){var e={name:"Sailfish"},o=i.default.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i,t);return o&&(e.version=o),e}},{test:[/silk/i],describe:function(t){var e={name:"Amazon Silk"},o=i.default.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/phantom/i],describe:function(t){var e={name:"PhantomJS"},o=i.default.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/slimerjs/i],describe:function(t){var e={name:"SlimerJS"},o=i.default.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e={name:"BlackBerry"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e={name:"WebOS Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/bada/i],describe:function(t){var e={name:"Bada"},o=i.default.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/tizen/i],describe:function(t){var e={name:"Tizen"},o=i.default.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/qupzilla/i],describe:function(t){var e={name:"QupZilla"},o=i.default.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/firefox|iceweasel|fxios/i],describe:function(t){var e={name:"Firefox"},o=i.default.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/electron/i],describe:function(t){var e={name:"Electron"},o=i.default.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MiuiBrowser/i],describe:function(t){var e={name:"Miui"},o=i.default.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/chromium/i],describe:function(t){var e={name:"Chromium"},o=i.default.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/chrome|crios|crmo/i],describe:function(t){var e={name:"Chrome"},o=i.default.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/GSA/i],describe:function(t){var e={name:"Google Search"},o=i.default.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e={name:"Android Browser"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/playstation 4/i],describe:function(t){var e={name:"PlayStation 4"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/safari|applewebkit/i],describe:function(t){var e={name:"Safari"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/.*/i],describe:function(t){var e=-1!==t.search("\\(")?/^(.*)\/(.*)[ \t]\((.*)/:/^(.*)\/(.*) /;return{name:i.default.getFirstMatch(e,t),version:i.default.getSecondMatch(e,t)}}}];e.default=r,t.exports=e.default},93:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:[/Roku\/DVP/],describe:function(t){var e=i.default.getFirstMatch(/Roku\/DVP-(\d+\.\d+)/i,t);return{name:a.OS_MAP.Roku,version:e}}},{test:[/windows phone/i],describe:function(t){var e=i.default.getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.WindowsPhone,version:e}}},{test:[/windows /i],describe:function(t){var e=i.default.getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i,t),o=i.default.getWindowsVersionName(e);return{name:a.OS_MAP.Windows,version:e,versionName:o}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(t){var e={name:a.OS_MAP.iOS},o=i.default.getSecondMatch(/(Version\/)(\d[\d.]+)/,t);return o&&(e.version=o),e}},{test:[/macintosh/i],describe:function(t){var e=i.default.getFirstMatch(/mac os x (\d+(\.?_?\d+)+)/i,t).replace(/[_\s]/g,"."),o=i.default.getMacOSVersionName(e),n={name:a.OS_MAP.MacOS,version:e};return o&&(n.versionName=o),n}},{test:[/(ipod|iphone|ipad)/i],describe:function(t){var e=i.default.getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i,t).replace(/[_\s]/g,".");return{name:a.OS_MAP.iOS,version:e}}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e=i.default.getFirstMatch(/android[\s/-](\d+(\.\d+)*)/i,t),o=i.default.getAndroidVersionName(e),n={name:a.OS_MAP.Android,version:e};return o&&(n.versionName=o),n}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e=i.default.getFirstMatch(/(?:web|hpw)[o0]s\/(\d+(\.\d+)*)/i,t),o={name:a.OS_MAP.WebOS};return e&&e.length&&(o.version=e),o}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e=i.default.getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i,t)||i.default.getFirstMatch(/blackberry\d+\/(\d+([_\s]\d+)*)/i,t)||i.default.getFirstMatch(/\bbb(\d+)/i,t);return{name:a.OS_MAP.BlackBerry,version:e}}},{test:[/bada/i],describe:function(t){var e=i.default.getFirstMatch(/bada\/(\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.Bada,version:e}}},{test:[/tizen/i],describe:function(t){var e=i.default.getFirstMatch(/tizen[/\s](\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.Tizen,version:e}}},{test:[/linux/i],describe:function(){return{name:a.OS_MAP.Linux}}},{test:[/CrOS/],describe:function(){return{name:a.OS_MAP.ChromeOS}}},{test:[/PlayStation 4/],describe:function(t){var e=i.default.getFirstMatch(/PlayStation 4[/\s](\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.PlayStation4,version:e}}}];e.default=r,t.exports=e.default},94:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:[/googlebot/i],describe:function(){return{type:"bot",vendor:"Google"}}},{test:[/huawei/i],describe:function(t){var e=i.default.getFirstMatch(/(can-l01)/i,t)&&"Nova",o={type:a.PLATFORMS_MAP.mobile,vendor:"Huawei"};return e&&(o.model=e),o}},{test:[/nexus\s*(?:7|8|9|10).*/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Nexus"}}},{test:[/ipad/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/kftt build/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Amazon",model:"Kindle Fire HD 7"}}},{test:[/silk/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Amazon"}}},{test:[/tablet(?! pc)/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet}}},{test:function(t){var e=t.test(/ipod|iphone/i),o=t.test(/like (ipod|iphone)/i);return e&&!o},describe:function(t){var e=i.default.getFirstMatch(/(ipod|iphone)/i,t);return{type:a.PLATFORMS_MAP.mobile,vendor:"Apple",model:e}}},{test:[/nexus\s*[0-6].*/i,/galaxy nexus/i],describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"Nexus"}}},{test:[/[^-]mobi/i],describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"blackberry"===t.getBrowserName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"BlackBerry"}}},{test:function(t){return"bada"===t.getBrowserName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"windows phone"===t.getBrowserName()},describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"Microsoft"}}},{test:function(t){var e=Number(String(t.getOSVersion()).split(".")[0]);return"android"===t.getOSName(!0)&&e>=3},describe:function(){return{type:a.PLATFORMS_MAP.tablet}}},{test:function(t){return"android"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"macos"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop,vendor:"Apple"}}},{test:function(t){return"windows"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop}}},{test:function(t){return"linux"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop}}},{test:function(t){return"playstation 4"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.tv}}},{test:function(t){return"roku"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.tv}}}];e.default=r,t.exports=e.default},95:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:function(t){return"microsoft edge"===t.getBrowserName(!0)},describe:function(t){if(/\sedg\//i.test(t))return{name:a.ENGINE_MAP.Blink};var e=i.default.getFirstMatch(/edge\/(\d+(\.?_?\d+)+)/i,t);return{name:a.ENGINE_MAP.EdgeHTML,version:e}}},{test:[/trident/i],describe:function(t){var e={name:a.ENGINE_MAP.Trident},o=i.default.getFirstMatch(/trident\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){return t.test(/presto/i)},describe:function(t){var e={name:a.ENGINE_MAP.Presto},o=i.default.getFirstMatch(/presto\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=t.test(/gecko/i),o=t.test(/like gecko/i);return e&&!o},describe:function(t){var e={name:a.ENGINE_MAP.Gecko},o=i.default.getFirstMatch(/gecko\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(apple)?webkit\/537\.36/i],describe:function(){return{name:a.ENGINE_MAP.Blink}}},{test:[/(apple)?webkit/i],describe:function(t){var e={name:a.ENGINE_MAP.WebKit},o=i.default.getFirstMatch(/webkit\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}}];e.default=r,t.exports=e.default}})},"./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss":(t,e,o)=>{"use strict";o.d(e,{A:()=>s});var n=o("./node_modules/css-loader/dist/runtime/noSourceMaps.js"),i=o.n(n),a=o("./node_modules/css-loader/dist/runtime/api.js"),r=o.n(a)()(i());r.push([t.id,'.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button:disabled{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button:disabled::before,.vot-button:disabled::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button:disabled::before,.vot-outlined-button:disabled::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button:disabled::before,.vot-text-button:disabled::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button:disabled::before,.vot-icon-button:disabled::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input,.vot-textfield:hover>textarea{border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input+span::before,.vot-textfield:hover>input+span::after,.vot-textfield:hover>textarea+span::before,.vot-textfield:hover>textarea+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:10000}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-translating=true] #vot-translating-icon{display:block !important}.vot-segmented-button[data-translating=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:10000;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog-container{visibility:visible;position:absolute;z-index:10000}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:10000;left:25%;top:75%;pointer-events:none}.vot-subtitles{position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:1rem;pointer-events:all;padding:1rem;font-size:2rem;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8);--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block}',""]);const s=r},"./node_modules/css-loader/dist/runtime/api.js":t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var o="",n=void 0!==e[5];return e[4]&&(o+="@supports (".concat(e[4],") {")),e[2]&&(o+="@media ".concat(e[2]," {")),n&&(o+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),o+=t(e),n&&(o+="}"),e[2]&&(o+="}"),e[4]&&(o+="}"),o})).join("")},e.i=function(t,o,n,i,a){"string"==typeof t&&(t=[[null,t,void 0]]);var r={};if(n)for(var s=0;s0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=a),o&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=o):c[2]=o),i&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=i):c[4]="".concat(i)),e.push(c))}},e}},"./node_modules/css-loader/dist/runtime/noSourceMaps.js":t=>{"use strict";t.exports=function(t){return t[1]}},"./node_modules/requestidlecallback-polyfill/index.js":()=>{window.requestIdleCallback=window.requestIdleCallback||function(t){var e=Date.now();return setTimeout((function(){t({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-e))}})}),1)},window.cancelIdleCallback=window.cancelIdleCallback||function(t){clearTimeout(t)}},"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":t=>{"use strict";var e=[];function o(t){for(var o=-1,n=0;n{"use strict";var e={};t.exports=function(t,o){var n=function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(t){o=null}e[t]=o}return e[t]}(t);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(o)}},"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":(t,e,o)=>{"use strict";t.exports=function(t){var e=o.nc;e&&t.setAttribute("nonce",e)}},"./node_modules/style-loader/dist/runtime/styleDomAPI.js":t=>{"use strict";t.exports=function(t){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var e=t.insertStyleElement(t);return{update:function(o){!function(t,e,o){var n="";o.supports&&(n+="@supports (".concat(o.supports,") {")),o.media&&(n+="@media ".concat(o.media," {"));var i=void 0!==o.layer;i&&(n+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),n+=o.css,i&&(n+="}"),o.media&&(n+="}"),o.supports&&(n+="}");var a=o.sourceMap;a&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),e.styleTagTransform(n,t,e.options)}(e,t,o)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},"./node_modules/style-loader/dist/runtime/styleTagTransform.js":t=>{"use strict";t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}},"./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js":t=>{t.exports=function(){return function(t){return t.styleTagTransform=function(t,e){e?.remove(),GM_addStyle(t)},document.createElement("style")}.apply(null,arguments)}},"./src/config/config.js":(t,e,o)=>{"use strict";o.d(e,{Cc:()=>s,JD:()=>l,K2:()=>u,Pm:()=>a,QL:()=>h,S7:()=>r,T8:()=>d,mE:()=>c,rl:()=>n,rw:()=>p,se:()=>i});const n="api.browser.yandex.ru",i="m3u8-proxy.toil.cc",a="vot-worker.toil.cc",r="bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf",s="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 YaBrowser/24.4.0.0 Safari/537.36",l=.15,d=900,c="yandex",u="yandex",h={yandex:"https://translate.toil.cc/detect",rustServer:"https://rust-server-531j.onrender.com/detect"},p={yandex:"https://translate.toil.cc/translate",deepl:"https://translate-deepl.toil.cc/translate"}},"./src/utils/debug.js":(t,e,o)=>{"use strict";o.d(e,{A:()=>i});const n={log:(...t)=>{}},i=n},"./src/yandexRequest.js":(t,e,o)=>{"use strict";o.r(e),o.d(e,{default:()=>a});var n=o("./src/config/config.js"),i=o("./src/utils/debug.js");const a=async function(t,e,o,a){try{i.A.log("yandexRequest:",t);const r={url:`https://${n.rl}${t}`,method:"POST",headers:{Accept:"application/x-protobuf","Accept-Language":"en","Content-Type":"application/x-protobuf","User-Agent":n.Cc,Pragma:"no-cache","Cache-Control":"no-cache","Sec-Fetch-Mode":"no-cors","sec-ch-ua":null,"sec-ch-ua-mobile":null,"sec-ch-ua-platform":null,...o},binary:!0,data:new Blob([e]),responseType:"arraybuffer"};GM_xmlhttpRequest({...r,onload:t=>{i.A.log("yandexRequest:",t.status,t),a(200===t.status,t.response)},onerror:t=>{console.error("[VOT]",t),a(!1)}})}catch(t){console.error("[VOT]",t),a(!1)}}}},e={};function o(n){var i=e[n];if(void 0!==i)return i.exports;var a=e[n]={id:n,exports:{}};return t[n].call(a.exports,a,a.exports,o),a.exports}o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.nc=void 0,(()=>{"use strict";const t=["invidious.snopyta.org","yewtu.be","invidious.kavin.rocks","vid.puffyan.us","invidious.namazso.eu","inv.riverside.rocks","yt.artemislena.eu","invidious.flokinet.to","invidious.esmailelbob.xyz","y.com.sb","invidious.nerdvpn.de","inv.vern.cc","invidious.slipfox.xyz","invidio.xamh.de","invidious.dhusch.de"],e=["piped.video","piped.tokhmi.xyz","piped.moomoo.me","piped.syncpundit.io","piped.mha.fi","watch.whatever.social","piped.garudalinux.org","efy.piped.pages.dev","watch.leptons.xyz","piped.lunar.icu","yt.dc09.ru","piped.mint.lgbt","il.ax","piped.privacy.com.de","piped.esmailelbob.xyz","piped.projectsegfau.lt","piped.in.projectsegfau.lt","piped.us.projectsegfau.lt","piped.privacydev.net","piped.palveluntarjoaja.eu","piped.smnz.de","piped.adminforge.de","piped.qdi.fi","piped.hostux.net","piped.chauvet.pro","piped.jotoma.de","piped.pfcd.me","piped.frontendfriendly.xyz"],n=["proxitok.pabloferreiro.es","proxitok.pussthecat.org","tok.habedieeh.re","proxitok.esmailelbob.xyz","proxitok.privacydev.net","tok.artemislena.eu","tok.adminforge.de","tik.hostux.net","tt.vern.cc","cringe.whatever.social","proxitok.lunar.icu","proxitok.privacy.com.de"],i=["peertube.1312.media","tube.shanti.cafe","bee-tube.fr","video.sadmin.io","dalek.zone","review.peertube.biz","peervideo.club","tube.la-dina.net","peertube.tmp.rcp.tf","peertube.su"];var a=o("./src/config/config.js");const r=["ru","en","zh","ko","lt","lv","ar","fr","it","es","de","ja"],s=["ru","en","kk"],l=["Violentmonkey","FireMonkey","Greasemonkey","AdGuard","OrangeMonkey"],d=JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","neededAdditionalExtension":"An additional extension is needed to support this site","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyAccessTokenExpired":"Your entered Udemy Access Token has expired","udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","enterUdemyAccessToken":"Enter Udemy Access Token","VOTUdemyData":"Udemy Data","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase"}');var c=o("./src/utils/debug.js");const u=new class{constructor(){this.gmSupport="function"==typeof GM_getValue,c.A.log(`GM Storage Status: ${this.gmSupport}`)}syncGet(t,e=void 0,o=!1){if(this.gmSupport)return GM_getValue(t,e);let n=window.localStorage.getItem(t);if("udemyData"===t&&"string"==typeof n)try{n=JSON.parse(n)}catch{n=e}return o?Number(n)??Number(e):n??e}async get(t,e=void 0,o=!1){return this.gmSupport?await GM_getValue(t,e):Promise.resolve(this.syncGet(t,e,o))}syncSet(t,e){return this.gmSupport?GM_setValue(t,e):("udemyData"===t&&(e=JSON.stringify(e)),window.localStorage.setItem(t,e))}async set(t,e){return this.gmSupport?await GM_setValue(t,e):Promise.resolve(this.syncSet(t,e))}syncDelete(t){return this.gmSupport?GM_deleteValue(t):window.localStorage.removeItem(t)}async delete(t){return this.gmSupport?await GM_deleteValue(t):Promise.resolve(this.syncDelete(t))}syncList(){return this.gmSupport?GM_listValues():["autoTranslate","dontTranslateLanguage","dontTranslateYourLang","autoSetVolumeYandexStyle","showVideoSlider","syncVolume","subtitlesMaxLength","highlightWords","responseLanguage","defaultVolume","udemyData","audioProxy","showPiPButton","locale-version","locale-lang","locale-phrases"]}async list(){return this.gmSupport?await GM_listValues():Promise.resolve(this.syncList())}};async function h(t,e={}){const o=new AbortController,n=setTimeout((()=>o.abort()),3e3);try{return await fetch(t,{...e,signal:o.signal})}catch(t){return console.error("Fetch timed-out. Error:",t),t}finally{clearTimeout(n)}}const p={async translate(t,e){try{const o=await h(`${a.rw.yandex}?${new URLSearchParams({text:t,lang:e})}`);if(o instanceof Error)throw o;const n=await o.json();if(200!==n.code)throw n.message;return n.text[0]}catch(e){return console.error("Error translating text:",e),t}},async detect(t){try{const e=await h(`${a.QL.yandex}?${new URLSearchParams({text:t})}`);if(e instanceof Error)throw e;const o=await e.json();if(200!==o.code)throw o.message;return o.lang??"en"}catch(t){return console.error("Error getting lang from text:",t),"en"}}},g={async detect(t){try{const e=await fetch(a.QL.rustServer,{method:"POST",body:t});if(e instanceof Error)throw e;return await e.text()}catch(t){return console.error("Error getting lang from text:",t),"en"}}},v={async translate(t,e="auto",o="ru"){try{const n=await h(a.rw.deepl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({text:t,source_lang:e,target_lang:o})});if(n instanceof Error)throw n;const i=await n.json();if(200!==i.code)throw i.message;return i.data}catch(e){return console.error("Error translating text:",e),t}}};const m=Object.keys(a.rw),b=Object.keys(a.QL).map((t=>"rustServer"===t?"rust-server":t));async function f(t,e,o,n){if(!window.location.hostname.includes("m.youtube.com")&&t?.getAudioTrack){const e=t.getAudioTrack(),o=e?.getLanguageInfo();if("und"!==o?.id)return L(o.id.split(".")[0])}const i=e?.captions?.playerCaptionsTracklistRenderer?.captionTracks;if(i?.length){const t=i.find((t=>"asr"===t.kind));if(t&&t.languageCode)return L(t.languageCode)}const r=function(t,e){const o=e?e.split("\n").filter((t=>!O.test(t))).join(" "):"",n=`${t} ${o}`.slice(0,450);return n.replace(/[^\p{L}\s]+|\s+/gu," ").trim()}(o,n);return c.A.log(`Detecting language text: ${r}`),async function(t){switch(await u.get("detectService",a.K2)){case"yandex":return await p.detect(t);case"rust-server":return await g.detect(t);default:return"en"}}(r)}function y(){return/^m\.youtube\.com$/.test(window.location.hostname)}function w(){return window.location.pathname.startsWith("/shorts/")?y()?document.querySelector("#movie_player"):document.querySelector("#shorts-player"):document.querySelector("#movie_player")}function x(){const t=w();return t?.getPlayerResponse?t?.getPlayerResponse?.call()??null:t?.data?.playerResponse??null}function S(){const t=w();return t?.getVideoData?t?.getVideoData?.call()??null:t?.data?.playerResponse?.videoDetails??null}const k={isMobile:y,getPlayer:w,getPlayerResponse:x,getPlayerData:S,getVideoVolume:function(){const t=w();return t?.getVolume?t.getVolume.call()/100:1},getSubtitles:function(){const t=x();let e=t?.captions?.playerCaptionsTracklistRenderer?.captionTracks??[];return e=e.reduce(((t,e)=>{if("languageCode"in e){const o=e?.languageCode?L(e?.languageCode):void 0,n=e?.url||e?.baseUrl;o&&n&&t.push({source:"youtube",language:o,isAutoGenerated:"asr"===e?.kind,url:`${n.startsWith("http")?n:`${window.location.origin}/${n}`}&fmt=json3`})}return t}),[]),c.A.log("youtube subtitles:",e),e},getVideoData:async function(){const t=w(),e=x(),o=S(),{title:n}=o??{},{shortDescription:i,isLive:a}=e?.videoDetails??{};let s=n?await f(t,e,n,i):"en";s=r.includes(s)?s:"en";const l={isLive:!!a,title:n,description:i,detectedLanguage:s};return c.A.log("youtube video data:",l),console.log("[VOT] Detected language: ",l.detectedLanguage),l},setVideoVolume:function(t){const e=w();if(e?.setVolume)return e.setVolume(Math.round(100*t)),!0},videoSeek:function(t,e){c.A.log("videoSeek",e);const o=(w()?.getProgressState()?.seekableEnd||t.currentTime)-e;t.currentTime=o},isMuted:function(){const t=w();return!!t?.isMuted&&t.isMuted.call()},isMusic:function(){const t=S().author,e=S().title.toUpperCase(),o=e.match(/\w+/g),n=document.body.querySelector("ytd-watch-flexy")?.playerData;return[e,document.URL,t,n?.microformat?.playerMicroformatRenderer.category,n?.title].some((t=>t?.toUpperCase().includes("MUSIC")))||document.body.querySelector("#upload-info #channel-name .badge-style-type-verified-artist")||t&&/(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(t)||t&&/(MUSIC|ROCK|SOUNDS|SONGS)/.test(t.toUpperCase())||o?.length&&["🎵","♫","SONG","SONGS","SOUNDTRACK","LYRIC","LYRICS","AMBIENT","MIX","VEVO","CLIP","KARAOKE","OPENING","COVER","COVERED","VOCAL","INSTRUMENTAL","ORCHESTRAL","DUBSTEP","DJ","DNB","BASS","BEAT","ALBUM","PLAYLIST","DUBSTEP","CHILL","RELAX","CLASSIC","CINEMATIC"].some((t=>o.includes(t)))||["OFFICIAL VIDEO","OFFICIAL AUDIO","FEAT.","FT.","LIVE RADIO","DANCE VER","HIP HOP","ROCK N ROLL","HOUR VER","HOURS VER","INTRO THEME"].some((t=>e.includes(t)))||o?.length&&["OP","ED","MV","OST","NCS","BGM","EDM","GMV","AMV","MMD","MAD"].some((t=>o.includes(t)))}},T=navigator.language||navigator.userLanguage,M=T?.substr(0,2)?.toLowerCase()??"en",V=(t,e)=>{let o=new URL(window.location.href);switch(t){case"piped":case"invidious":case"youtube":if(o.searchParams.has("enablejsapi")){const t=k.getPlayer().getVideoUrl();o=new URL(t)}return/(?:watch|embed|shorts|live)\/([^/]+)/.exec(o.pathname)?.[1]||o.searchParams.get("v");case"vk":{const t=/^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(o.pathname),e=o.searchParams.get("z"),n=o.searchParams.get("oid"),i=o.searchParams.get("id");return t?t[0].slice(1):e?e.split("/")[0]:n&&i?`video-${Math.abs(parseInt(n))}_${i}`:null}case"nine_gag":case"9gag":case"gag":return/gag\/([^/]+)/.exec(o.pathname)?.[1];case"twitch":{const t=/([^/]+)\/(?:clip)\/([^/]+)/.exec(o.pathname);if(/^m\.twitch\.tv$/.test(o.hostname))return/videos\/([^/]+)/.exec(o.href)?.[0]||o.pathname.slice(1);if(/^player\.twitch\.tv$/.test(o.hostname))return`videos/${o.searchParams.get("video")}`;if(/^clips\.twitch\.tv$/.test(o.hostname)){const t=document.querySelector("script[type='application/ld+json']"),e=o.pathname.slice(1);if(!t){const t="embed"===e,n=document.querySelector(t?".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']":".clips-player a:not([class])");if(!n)return;return`${n.href.replace("https://www.twitch.tv/","")}/clip/${t?o.searchParams.get("clip"):e}`}const n=JSON.parse(t.innerText),i=n["@graph"].find((t=>"VideoObject"===t["@type"]))?.creator.url;return`${i.replace("https://www.twitch.tv/","")}/clip/${e}`}return t?t[0]:/(?:videos)\/([^/]+)/.exec(o.pathname)?.[0]}case"proxitok":return/([^/]+)\/video\/([^/]+)/.exec(o.pathname)?.[0];case"tiktok":{let t=/([^/]+)\/video\/([^/]+)/.exec(o.pathname)?.[0];if(!t){const o=e.closest(".xgplayer-playing, .tiktok-web-player"),n=o?.closest('div[data-e2e="recommend-list-item-container"]'),i=n?.querySelector('a[data-e2e="video-author-avatar"]');if(o&&i){const e=o.id?.match(/^xgwrapper-\d+-(.*)$/)?.at(1),n=i.href?.match(/.*(@.*)$/)?.at(1);e&&n&&(t=`${n}/video/${e}`)}}return t}case"vimeo":{const t=o.searchParams.get("app_id"),e=/[^/]+\/[^/]+$/.exec(o.pathname)?.[0]||/[^/]+$/.exec(o.pathname)?.[0];return t?`${e}?app_id=${t}`:e}case"xvideos":return/[^/]+\/[^/]+$/.exec(o.pathname)?.[0];case"pornhub":return o.searchParams.get("viewkey")||/embed\/([^/]+)/.exec(o.pathname)?.[1];case"twitter":return/status\/([^/]+)/.exec(o.pathname)?.[1];case"udemy":case"rumble":case"facebook":return o.pathname.slice(1);case"rutube":return/(?:video|embed)\/([^/]+)/.exec(o.pathname)?.[1];case"coub":return/(?:view|embed)\/([^/]+)/.exec(o.pathname)?.[1]||document.querySelector(".coub.active")?.dataset?.permalink;case"bilibili":{const t=o.searchParams.get("bvid");if(t)return t;let e=/video\/([^/]+)/.exec(o.pathname)?.[1];return e&&null!==o.searchParams.get("p")&&(e+=`/?p=${o.searchParams.get("p")}`),e}case"mail_ru":{const t=o.pathname;if(t.startsWith("/v/")||t.startsWith("/mail/"))return t.slice(1);const e=/video\/embed\/([^/]+)/.exec(t)?.[1];if(!e)return null;const n=document.querySelector(".b-video-controls__mymail-link");return!!n&&n?.href.split("my.mail.ru")?.[1]}case"bitchute":return e.src?.startsWith("blob:")||!e.src?.includes(".mp4")?null:e.src;case"coursera":return/learn\/([^/]+)\/lecture\/([^/]+)/.exec(o.pathname)?.[0];case"eporner":return/video-([^/]+)\/([^/]+)/.exec(o.pathname)?.[0];case"peertube":return/\/w\/([^/]+)/.exec(o.pathname)?.[0];case"dailymotion":{const t=Array.from(document.querySelectorAll("*")).filter((t=>t.innerHTML.trim().includes(".m3u8")));try{let e=t[1].lastChild.src;return/\/video\/(\w+)\.m3u8/.exec(e)?.[1]}catch(t){return console.error("[VOT]",t),!1}}case"trovo":{const t=o.searchParams.get("vid");if(!t)return null;const e=/([^/]+)\/(\d+)/.exec(o.pathname)?.[0];return e?`${e}?vid=${t}`:null}case"yandexdisk":return/\/i\/([^/]+)/.exec(o.pathname)?.[1];case"coursehunter":{const t=/\/course\/([^/]+)/.exec(o.pathname)?.[1];return!!t&&t+o.search}case"ok.ru":return/\/video\/(\d+)/.exec(o.pathname)?.[1];case"googledrive":return o.searchParams.get("docid");case"bannedvideo":return o.searchParams.get("id");case"weverse":return/([^/]+)\/(live|media)\/([^/]+)/.exec(o.pathname)?.[0];case"newgrounds":return/([^/]+)\/(view)\/([^/]+)/.exec(o.pathname)?.[0];case"egghead":return o.pathname.slice(1);case"youku":return/v_show\/id_[\w=]+/.exec(o.pathname)?.[0];case"archive":return/(details|embed)\/([^/]+)/.exec(o.pathname)?.[2];case"directlink":return o.pathname+o.search;default:return!1}};function L(t){return t.toLowerCase().split(/[_;-]/)[0].trim()}function A(){return"pictureInPictureEnabled"in document&&document.pictureInPictureEnabled}function C(){return"undefined"!=typeof Hls&&Hls?.isSupported()?new Hls({debug:!1,lowLatencyMode:!0,backBufferLength:90}):void 0}const O=new RegExp([/(?:https?|ftp):\/\/\S+/g,/https?:\/\/\S+|www\.\S+/gm,/\b\S+\.\S+/gm,/#[^\s#]+/g,/Auto-generated by YouTube/g,/Provided to YouTube by/g,/Released on/g,/0x[a-fA-F0-9]{40}/g,/[13][a-km-zA-HJ-NP-Z1-9]{25,34}/g,/4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}/g,/Paypal/g].map((t=>t.source)).join("|"));async function P(t,e={}){try{return await fetch(t,e)}catch(o){return new Promise(((o,n)=>{GM_xmlhttpRequest({method:e.method||"GET",url:t,responseType:"blob",onload:t=>{o(new Response(t.response,{status:t.status,headers:Object.fromEntries(t.responseHeaders.trim().split("\r\n").map((t=>{let e=t.split(": ");if("set-cookie"!==e?.[0])return[e.shift(),e.join(": ")]})).filter((t=>t)))}))},ontimeout:()=>n(new Error("fetch timeout")),onerror:t=>n(t),onabort:()=>n(new Error("fetch abort"))})}))}}const E=["auto","en","ru","af","am","ar","az","bg","bn","bs","ca","cs","cy","da","de","el","es","et","eu","fa","fi","fr","gl","hi","hr","hu","hy","id","it","ja","jv","kk","km","kn","ko","lo","mk","ml","mn","ms","mt","my","ne","nl","pa","pl","pt","ro","si","sk","sl","sq","sr","su","sv","sw","tr","uk","ur","uz","vi","zh","zu"],B=new class{lang="en";locale={};gmValues=["locale-phrases","locale-lang","locale-version","locale-lang-override"];constructor(){const t=u.syncGet("locale-lang-override","auto");this.lang=t&&"auto"!==t?t:(navigator.language||navigator.userLanguage)?.substr(0,2)?.toLowerCase()??"en",this.setLocaleFromJsonString(u.syncGet("locale-phrases",""))}reset(){for(let t=0;t{if("object"==typeof t&&t)return t[e]}),t);return void 0===o&&console.warn("[VOT] [localizationProvider] locale",t,"doesn't contain key",e),o}getDefault(t){return this.getFromLocale(d,t)??t}get(t){return this.getFromLocale(this.locale,t)??this.getFromLocale(d,t)??t}};var R=o("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"),F=o.n(R),D=o("./node_modules/style-loader/dist/runtime/styleDomAPI.js"),_=o.n(D),q=o("./node_modules/style-loader/dist/runtime/insertBySelector.js"),z=o.n(q),I=o("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js"),N=o.n(I),$=o("./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js"),H=o.n($),j=o("./node_modules/style-loader/dist/runtime/styleTagTransform.js"),U=o.n(j),W=o("./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss"),G={};G.styleTagTransform=U(),G.setAttributes=N(),G.insert=z().bind(null,"head"),G.domAPI=_(),G.insertStyleElement=H();F()(W.A,G);W.A&&W.A.locals&&W.A.locals;function Y(t){const e=document.createElement("vot-block");return e.classList.add("vot-icon-button"),e.innerHTML=t,e}function Z(t){const e=parseFloat(t.value),o=""===t.min?0:parseFloat(t.min),n=(e-o)/((""===t.max?100:parseFloat(t.max))-o);t.parentElement.setAttribute("style",`--vot-progress: ${n}`)}function K(t,e="",o=" ",n=!1){const i=document.createElement("vot-block");i.classList.add("vot-textfield");const a=document.createElement(n?"textarea":"input");a.placeholder=o,a.value=e;const r=document.createElement("span");return r.innerHTML=t,i.appendChild(a),i.appendChild(r),{container:i,input:a,label:r}}function J(t){const e=document.createElement("vot-block");e.classList.add("vot-dialog-container"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-dialog-backdrop");const n=document.createElement("vot-block");n.classList.add("vot-dialog");const i=document.createElement("vot-block");i.classList.add("vot-dialog-content-wrapper");const a=document.createElement("vot-block");a.classList.add("vot-dialog-header-container");const r=document.createElement("vot-block");r.classList.add("vot-dialog-body-container");const s=document.createElement("vot-block");s.classList.add("vot-dialog-footer-container");const l=document.createElement("vot-block");l.classList.add("vot-dialog-title-container");const d=Y('');d.classList.add("vot-dialog-close-button"),o.onclick=d.onclick=()=>{e.hidden=!0};const c=document.createElement("vot-block");return c.classList.add("vot-dialog-title"),c.innerHTML=t,e.appendChild(o),e.appendChild(n),n.appendChild(i),i.appendChild(a),i.appendChild(r),i.appendChild(s),a.appendChild(l),a.appendChild(d),l.appendChild(c),{container:e,backdrop:o,dialog:n,contentWrapper:i,headerContainer:a,bodyContainer:r,footerContainer:s,titleContainer:l,closeButton:d,title:c}}function Q(t,e,o,n={}){const i=n.onSelectCb||function(){},a=n.labelElement||"";let r=[];const s=document.createElement("vot-block");s.classList.add("vot-select"),a&&s.appendChild(a);const l=document.createElement("vot-block");l.classList.add("vot-select-outer");const d=document.createElement("span");d.classList.add("vot-select-title"),d.innerText=t,void 0===t&&(d.innerText=o.find((t=>!0===t.selected))?.label);const c=document.createElement("vot-block");c.classList.add("vot-select-arrow-icon"),c.innerHTML='',l.append(d,c),l.onclick=()=>{const t=J(e);t.container.classList.add("vot-dialog-temp"),t.container.hidden=!1,document.documentElement.appendChild(t.container);const n=document.createElement("vot-block");n.classList.add("vot-select-content-list");for(const t of o){const e=document.createElement("vot-block");e.classList.add("vot-select-content-item"),e.innerText=t.label,e.dataset.votSelected=t.selected,e.dataset.votValue=t.value,t.disabled&&(e.inert=!0),e.onclick=async a=>{if(a.target.inert)return;const r=n.childNodes;for(let t of r)t.dataset.votSelected=!1;for(let e of o)e.selected=e.value===t.value;e.dataset.votSelected=!0,d.innerText=t.label,await i(a)},n.appendChild(e)}const a=K(B.get("searchField"));a.input.oninput=t=>{const e=t.target.value.toLowerCase();for(let t=0;t{t.container.remove(),r=[]}},s.append(l);return{container:s,title:d,arrowIcon:c,labelElement:a,setTitle:t=>{d.innerText=t},setSelected:t=>{const e=Array.from(r).filter((t=>!t.inert));for(let o=0;o{o=t}}}const X={createHeader:function(t,e=4){const o=document.createElement("vot-block");return o.classList.add("vot-header"),o.classList.add(`vot-header-level-${e}`),o.innerHTML=t,o},createInformation:function(t,e){const o=document.createElement("vot-block");o.classList.add("vot-info");const n=document.createElement("vot-block");n.innerHTML=t;const i=document.createElement("vot-block");return i.innerHTML=e,o.appendChild(n),o.appendChild(i),{container:o,header:n,value:i}},createButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-button"),e.innerHTML=t,e},createTextButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-text-button"),e.innerHTML=t,e},createOutlinedButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-outlined-button"),e.innerHTML=t,e},createIconButton:Y,createCheckbox:function(t,e=!1){const o=document.createElement("label");o.classList.add("vot-checkbox");const n=document.createElement("input");n.type="checkbox",n.checked=Boolean(e);const i=document.createElement("span");return i.innerHTML=t,o.appendChild(n),o.appendChild(i),{container:o,input:n,label:i}},createSlider:function(t,e=50,o=0,n=100){const i=document.createElement("vot-block");i.classList.add("vot-slider");const a=document.createElement("input");a.type="range",a.min=o,a.max=n,a.value=e;const r=document.createElement("span");return r.innerHTML=t,i.appendChild(a),i.appendChild(r),a.addEventListener("input",(t=>Z(t.target))),Z(a),{container:i,input:a,label:r}},createTextfield:K,createDialog:J,createVOTButton:function(t){const e=document.createElement("vot-block");e.classList.add("vot-segmented-button");const o=document.createElement("vot-block");o.classList.add("vot-segment"),o.classList.add("vot-translate-button"),o.innerHTML='';const n=document.createElement("vot-block");n.classList.add("vot-separator");const i=document.createElement("vot-block");i.classList.add("vot-segment-only-icon"),i.innerHTML='';const a=document.createElement("vot-block");a.classList.add("vot-separator");const r=document.createElement("vot-block");r.classList.add("vot-segment-only-icon"),r.innerHTML='';const s=document.createElement("span");return s.classList.add("vot-segment-label"),s.innerHTML=t,e.appendChild(o),e.appendChild(n),e.appendChild(i),e.appendChild(a),e.appendChild(r),o.appendChild(s),{container:e,translateButton:o,separator:n,pipButton:i,separator2:a,menuButton:r,label:s}},createVOTMenu:function(t){const e=document.createElement("vot-block");e.classList.add("vot-menu"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-menu-content-wrapper");const n=document.createElement("vot-block");n.classList.add("vot-menu-header-container");const i=document.createElement("vot-block");i.classList.add("vot-menu-body-container");const a=document.createElement("vot-block");a.classList.add("vot-menu-footer-container");const r=document.createElement("vot-block");r.classList.add("vot-menu-title-container");const s=document.createElement("vot-block");return s.classList.add("vot-menu-title"),s.innerHTML=t,e.appendChild(o),o.appendChild(n),o.appendChild(i),o.appendChild(a),n.appendChild(r),r.appendChild(s),{container:e,contentWrapper:o,headerContainer:n,bodyContainer:i,footerContainer:a,titleContainer:r,title:s}},createVOTSelectLabel:function(t){const e=document.createElement("span");return e.classList.add("vot-select-label"),e.innerText=t,e},createVOTSelect:Q,createVOTLanguageSelect:function(t){const e=t.fromTitle||"#UNDEFINED",o=t.fromDialogTitle||"#UNDEFINED",n=t.fromItems||[],i=t.fromOnSelectCB||function(){},a=t.toTitle||"#UNDEFINED",r=t.toDialogTitle||"#UNDEFINED",s=t.toItems||[],l=t.toOnSelectCB||function(){},d=document.createElement("vot-block");d.classList.add("vot-lang-select");const c=Q(e,o,n,{onSelectCb:i}),u=document.createElement("vot-block");u.classList.add("vot-lang-select-icon"),u.innerHTML='';const h=Q(a,r,s,{onSelectCb:l});return d.append(c.container,u,h.container),{container:d,fromSelect:c,icon:u,toSelect:h}},updateSlider:Z};class tt extends Error{constructor(t){super(B.getDefault(t)),this.name="VOTLocalizedError",this.unlocalizedMessage=t,this.localizedMessage=B.get(t)}}const et=new protobuf.Type("VideoTranslationHelpObject").add(new protobuf.Field("target",1,"string")).add(new protobuf.Field("targetUrl",2,"string")),ot=new protobuf.Type("VideoTranslationRequest").add(new protobuf.Field("url",3,"string")).add(new protobuf.Field("deviceId",4,"string")).add(new protobuf.Field("firstRequest",5,"bool")).add(new protobuf.Field("duration",6,"double")).add(new protobuf.Field("unknown2",7,"int32")).add(new protobuf.Field("language",8,"string")).add(new protobuf.Field("forceSourceLang",9,"bool")).add(new protobuf.Field("unknown4",10,"int32")).add(new protobuf.Field("translationHelp",11,"VideoTranslationHelpObject","repeated")).add(new protobuf.Field("responseLanguage",14,"string")).add(new protobuf.Field("unknown5",15,"int32")).add(new protobuf.Field("unknown6",16,"int32")).add(new protobuf.Field("bypassCache",17,"bool")),nt=new protobuf.Type("VideoSubtitlesRequest").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("language",2,"string")),it=new protobuf.Type("VideoStreamRequest").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("language",2,"string")).add(new protobuf.Field("responseLanguage",3,"string")),at=new protobuf.Type("VideoStreamPingRequest").add(new protobuf.Field("pingId",1,"int32")),rt=new protobuf.Type("VideoTranslationResponse").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("duration",2,"double")).add(new protobuf.Field("status",4,"int32")).add(new protobuf.Field("remainingTime",5,"int32")).add(new protobuf.Field("unknown0",6,"int32")).add(new protobuf.Field("translationId",7,"string")).add(new protobuf.Field("language",8,"string")).add(new protobuf.Field("message",9,"string")),st=new protobuf.Type("VideoSubtitlesObject").add(new protobuf.Field("language",1,"string")).add(new protobuf.Field("url",2,"string")).add(new protobuf.Field("unknown2",3,"int32")).add(new protobuf.Field("translatedLanguage",4,"string")).add(new protobuf.Field("translatedUrl",5,"string")).add(new protobuf.Field("unknown5",6,"int32")).add(new protobuf.Field("unknown6",7,"int32")),lt=new protobuf.Type("VideoSubtitlesResponse").add(new protobuf.Field("waiting",1,"int32")).add(new protobuf.Field("subtitles",2,"VideoSubtitlesObject","repeated")),dt=new protobuf.Type("VideoStreamObject").add(new protobuf.Field("url",1,"string")).add(new protobuf.Field("timestamp",2,"int64")),ct=new protobuf.Type("VideoStreamResponse").add(new protobuf.Field("interval",1,"int32")).add(new protobuf.Field("translatedInfo",2,"VideoStreamObject")).add(new protobuf.Field("pingId",3,"int32")),ut=new protobuf.Type("YandexSessionRequest").add(new protobuf.Field("uuid",1,"string")).add(new protobuf.Field("module",2,"string")),ht=new protobuf.Type("YandexSessionResponse").add(new protobuf.Field("sign",1,"string")).add(new protobuf.Field("expires",2,"int32")),pt=(new protobuf.Root).define("yandex").add(et).add(ot).add(rt).add(nt).add(st).add(lt).add(at).add(it).add(dt).add(ct).add(ut).add(ht),gt={encodeTranslationRequest:(t,e,o,n,i)=>pt.VideoTranslationRequest.encode({url:t,firstRequest:!0,duration:e,unknown2:1,language:o,forceSourceLang:!1,unknown4:0,translationHelp:i,responseLanguage:n,unknown5:0,unknown6:1,bypassCache:!1}).finish(),decodeTranslationResponse:t=>pt.VideoTranslationResponse.decode(new Uint8Array(t)),encodeSubtitlesRequest:(t,e)=>pt.VideoSubtitlesRequest.encode({url:t,language:e}).finish(),decodeSubtitlesResponse:t=>pt.VideoSubtitlesResponse.decode(new Uint8Array(t)),encodeStreamPingRequest:t=>pt.VideoStreamPingRequest.encode({pingId:t}).finish(),encodeStreamRequest:(t,e,o)=>pt.VideoStreamRequest.encode({url:t,language:e,responseLanguage:o}).finish(),decodeStreamResponse:t=>pt.VideoStreamResponse.decode(new Uint8Array(t)),encodeYandexSessionRequest:(t,e)=>pt.YandexSessionRequest.encode({uuid:t,module:e}).finish(),decodeYandexSessionResponse:t=>pt.YandexSessionResponse.decode(new Uint8Array(t))};var vt=o("./node_modules/bowser/es5.js");function mt(){let t="";for(let e=0;e<32;e++){t+="0123456789ABCDEF"[Math.floor(16*Math.random())]}return t}const bt=window.crypto.subtle.importKey("raw",(new TextEncoder).encode(a.S7),{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign","verify"]);async function ft(t){const e=await bt;return new Uint8Array(await window.crypto.subtle.sign("HMAC",e,t)).reduce(((t,e)=>t+e.toString(16).padStart(2,"0")),"")}const yt=async function(t,e){try{c.A.log("requestStreamPing");const n=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest.js"))).default;c.A.log("Inited yandexRequest...");const i=gt.encodeStreamPingRequest(t);await n("/stream-translation/ping-stream",i,{"Vtrans-Signature":await ft(i),"Sec-Vtrans-Token":mt()},e)}catch(t){console.error("[VOT]",t),e(!1)}};const wt=async function(t,e,n,i){try{c.A.log("requestStreamTranslation");const a=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest.js"))).default;c.A.log("Inited yandexRequest...");const r=gt.encodeStreamRequest(t,e,n);await a("/stream-translation/translate-stream",r,{"Vtrans-Signature":await ft(r),"Sec-Vtrans-Token":mt()},i)}catch(t){console.error("[VOT]",t),i(!1)}};const xt=async function(t,e,n,i,a,r){try{c.A.log("requestVideoTranslation");const s=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest.js"))).default;c.A.log("Inited yandexRequest...");const l=gt.encodeTranslationRequest(t,e,n,i,a);await s("/video-translation/translate",l,{"Vtrans-Signature":await ft(l),"Sec-Vtrans-Token":mt()},r)}catch(t){console.error("[VOT]",t),r(!1)}};const St=async function(t,e,n){try{c.A.log("requestVideoSubtitles");const i=(await Promise.resolve().then(o.bind(o,"./src/yandexRequest.js"))).default;c.A.log("Inited yandexRequest...");const a=gt.encodeSubtitlesRequest(t,e);await i("/video-subtitles/get-subtitles",a,{"Vsubs-Signature":await ft(a),"Sec-Vsubs-Token":mt()},n)}catch(t){console.error("[VOT]",t),n(!1)}};function kt(t){const e=t.startMs+t.durationMs;return t.tokens.reduce(((o,n,i)=>{const a=t.tokens[i+1];let r;o.length>0&&(r=o[o.length-1]);const s=r?.alignRange?.end??0,l=s+n.text.length;if(n.alignRange={start:s,end:l},o.push(n),a){const t=n.startMs+n.durationMs,i=a.startMs?a.startMs-t:e-t;o.push({text:" ",startMs:t,durationMs:i,alignRange:{start:l,end:l+1}})}return o}),[])}function Tt(t,e){const o=t.text.split(/([\n \t])/).reduce(((t,o)=>{if(o.length){const n=t[t.length-1]??e,i=n?.alignRange?.end??0,a=i+o.length;t.push({text:o,alignRange:{start:i,end:a}})}return t}),[]),n=Math.floor(t.durationMs/o.length),i=t.startMs+t.durationMs;return o.map(((e,a)=>{const r=a===o.length-1,s=t.startMs+n*a;return{...e,startMs:s,durationMs:r?i-s:n}}))}function Mt(t){let e=Math.floor(t/3600),o=Math.floor(t%3600/60),n=Math.floor(t%60),i=Math.floor(t%1*1e3);return`${e.toString().padStart(2,"0")}:${o.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")},${i.toString().padStart(3,"0")}`}async function Vt(t){const e=new Promise((t=>setTimeout((()=>t({containsTokens:!1,subtitles:[]})),5e3))),o=(async()=>{try{const e=await P(t.url);return await e.json()}catch(t){return console.error("[VOT] Failed to fetch subtitles. Reason:",t),{containsTokens:!1,subtitles:[]}}})();let n=await Promise.race([e,o]);return"youtube"===t.source&&(n=function(t){const e={containsTokens:!1,subtitles:[]};if("object"!=typeof t||!("events"in t)||!Array.isArray(t.events))return console.error("[VOT] Failed to format youtube subtitles",t),e;for(let o=0;ot.utf8.replace(/^( +| +)$/g,""))).join(" ");let i=t.events[o].dDurationMs;t.events[o+1]&&t.events[o].tStartMs+t.events[o].dDurationMs>t.events[o+1].tStartMs&&(i=t.events[o+1].tStartMs-t.events[o].tStartMs),"\n"!==n&&e.subtitles.push({text:n,startMs:t.events[o].tStartMs,durationMs:i})}return e}(n)),n.subtitles=function(t,e){const o=[];let n;for(let i=0;i{setTimeout((()=>{i||(console.error("[VOT] Failed get yandex subtitles. Reason: timeout"),t([]))}),5e3)})),new Promise((n=>{St(`${t.url}${e}`,o,((t,o)=>{c.A.log("[exec callback] Requesting video subtitles",e),t||(console.error("[VOT] Failed get yandex subtitles"),i=!0,n([]));const a=gt.decodeSubtitlesResponse(o);console.log("[VOT] Subtitles response: ",a);let r=a.subtitles??[];r=r.reduce(((t,e)=>(e.language&&!t.find((t=>{if("yandex"===t.source&&t.language===e.language&&!t.translatedFromLanguage)return t}))&&t.push({source:"yandex",language:e.language,url:e.url}),e.translatedLanguage&&t.push({source:"yandex",language:e.translatedLanguage,translatedFromLanguage:e.language,url:e.translatedUrl}),t)),[]),i=!0,n(r)}))}))]),r=[...a,...n].sort(((t,e)=>{if(t.source!==e.source)return"yandex"===t.source?-1:1;if(t.language!==e.language&&(t.language===M||e.language===M))return t.language===M?-1:1;if("yandex"===t.source){if(t.translatedFromLanguage!==e.translatedFromLanguage)return t.translatedFromLanguage&&e.translatedFromLanguage?t.translatedFromLanguage===o?-1:1:t.language===e.language?t.translatedFromLanguage?1:-1:t.translatedFromLanguage?-1:1;if(!t.translatedFromLanguage)return t.language===o?-1:1}return"youtube"===t.source&&t.isAutoGenerated!==e.isAutoGenerated?t.isAutoGenerated?1:-1:0}));return console.log("[VOT] subtitles list",r),r}class At{dragging=!1;subtitlesContainerRect=null;containerRect=null;offsetX=null;offsetY=null;lastContent=null;highlightWords=!1;subtitles=null;maxLength=300;maxLengthRegexp=/.{1,300}(?:\s|$)/g;constructor(t,e,o){this.site=o,this.video=t,"youtube"===this.site.host&&"mobile"!==this.site.additionalData?this.container=e.parentElement:this.container=e,this.votSubtitlesContainer=document.createElement("vot-block"),this.votSubtitlesContainer.classList.add("vot-subtitles-widget"),this.container.appendChild(this.votSubtitlesContainer),this.onMouseDownBound=this.onMouseDown.bind(this),this.onMouseUpBound=this.onMouseUp.bind(this),this.onMouseMoveBound=this.onMouseMove.bind(this),this.onTimeUpdateBound=this.onTimeUpdate.bind(this),document.addEventListener("mousedown",this.onMouseDownBound),document.addEventListener("mouseup",this.onMouseUpBound),document.addEventListener("mousemove",this.onMouseMoveBound),this.video?.addEventListener("timeupdate",this.onTimeUpdateBound)}release(){this.video?.removeEventListener("timeupdate",this.onTimeUpdateBound),document.removeEventListener("mousedown",this.onMouseDownBound),document.removeEventListener("mouseup",this.onMouseUpBound),document.removeEventListener("mousemove",this.onMouseMoveBound),this.votSubtitlesContainer.remove()}onMouseDown(t){this.votSubtitlesContainer.contains(t.target)&&(this.subtitlesContainerRect=this.votSubtitlesContainer.getBoundingClientRect(),this.containerRect=this.container.getBoundingClientRect(),this.offsetX=t.clientX-this.subtitlesContainerRect.x,this.offsetY=t.clientY-this.subtitlesContainerRect.y,this.dragging=!0)}onMouseUp(){this.dragging=!1}onMouseMove(t){if(this.dragging){t.preventDefault();const e=t.clientX-this.offsetX,o=t.clientY-this.offsetY,n=o>=this.containerRect.top,i=o+this.subtitlesContainerRect.height<=this.containerRect.bottom,a=e>=this.containerRect.left,r=e+this.subtitlesContainerRect.width<=this.containerRect.right;this.votSubtitlesContainer.style.top=n&&i?o-this.containerRect.y+"px":n?this.containerRect.height-this.subtitlesContainerRect.height+"px":"0px",this.votSubtitlesContainer.style.left=a&&r?e-this.containerRect.x+"px":a?this.containerRect.width-this.subtitlesContainerRect.width+"px":"0px"}}onTimeUpdate(){this.update()}setContent(t){t&&this.video?(this.subtitles=t,this.update()):(this.subtitles=null,this.votSubtitlesContainer.innerHTML="")}setMaxLength(t){"number"==typeof t&&t&&(this.maxLength=t,this.maxLengthRegexp=new RegExp(`.{1,${t}}(?:\\s|$)`,"g"),this.update())}setHighlightWords(t){this.highlightWords!==!!t&&(this.highlightWords=!!t,this.update())}update(){if(!this.video)return;let t="",e=this.highlightWords&&this.subtitles?.containsTokens;const o=1e3*this.video.currentTime,n=this.subtitles?.subtitles?.findLast((t=>t.startMsthis.maxLength){let t=[],e=0,n=0,a=0;for(let o=0;othis.maxLength){let r=i.slice(e,n+1);r.at(0)&&" "===r.at(0).text&&(r=r.slice(1)),r.at(-1)&&" "===r.at(-1).text&&(r=r.slice(0,r.length-1)),t.push({startMs:i[e].startMs,durationMs:i[n].startMs+i[n].durationMs-i[e].startMs,tokens:r}),e=o,a=0}n=o}for(let e=0;er||o>a.startMs-100&&r-o<275)?'class="passed"':""}>${a.text}`}}t!==this.lastContent&&(this.lastContent=t,this.votSubtitlesContainer.innerHTML=t?`${t.replace("\\n","
")}
`:"")}}const Ct={getVideoData:async function(){const t=window.course_id??document.querySelector('input[name="course_id"]')?.value,e=window.lessons??await async function(t){const e=await fetch(`https://coursehunter.net/api/v1/course/${t}/lessons`);return await e.json()}(t),o=parseInt(document.querySelector(".lessons-item_active")?.dataset?.index??1),n=e?.[o-1],{file:i,duration:a}=n;return c.A.log("coursehunter course data:",e),{url:i,duration:a}}};function Ot(){return Pt()?.player}function Pt(){return document.querySelector(".vjs-v6")}const Et={getPlayer:Pt,getPlayerData:Ot,getVideoData:async function(t="en"){let e=null;const o=Ot(),{duration:n}=o?.cache_||{},{courseId:i,tracks:a,sources:s}=o?.options_||{},l=function(t){const e=t?.find((t=>"video/mp4"===t.type));return e?.src}(s),d=await async function(t){const e=await fetch(`https://www.coursera.org/api/onDemandCourses.v1/${t}`),o=await e.json();return o?.elements?.[0]}(i);let u=d?.primaryLanguageCodes?.[0];u=u?L(u):"en",r.includes(u)||(u="en");const h=function(t,e,o){let n=t?.find((t=>L(t.srclang)===e));return n||(n=t?.find((t=>L(t.srclang)===o))||t?.[0]),n?.src}(a,u,t);console.log(`videoURL: ${l}, subtitlesURL: ${h}`),h&&l?e=[{target:"video_file_url",targetUrl:l},{target:"subtitles_file_url",targetUrl:`https://www.coursera.org${h}`}]:l&&!h?(console.warn("[VOT] Subtitles files not found. Using the link only to the video file."),e={url:l}):console.error(`Failed to find subtitlesURL or videoURL. videoURL: ${l}, subtitlesURL: ${h}`);const p={duration:n,detectedLanguage:u,translationHelp:e};return c.A.log("coursera video data:",p),console.log("[VOT] Detected language: ",p.detectedLanguage),p}},Bt="https://www.udemy.com/api-2.0",Rt=2592e6;async function Ft(t){const e=await fetch(`${Bt}/courses/${t}/?`+new URLSearchParams({"fields[course]":"locale",use_remote_version:"true",caching_intent:"true"}));return await e.json()}async function Dt(t,e,o){if(!(n=t.expires,n+Rt>(new Date).getTime()&&t.accessToken))return void console.error(B.get("udemyAccessTokenExpired"));var n;const i=`Bearer ${t.accessToken}`,a=await fetch(`${Bt}/users/me/subscribed-courses/${e}/lectures/${o}/?`+new URLSearchParams({"fields[lecture]":"asset","fields[asset]":"length,media_sources,captions"}),{headers:{"x-udemy-authorization":i,authorization:i}});return await a.json()}function _t(){return zt()?.player}function qt(){const t=document.querySelector(".ud-app-loader[data-module-id='course-taking']")?.dataset?.moduleArgs;return t?JSON.parse(t):(console.error(B.get("udemyModuleArgsNotFound")),{})}function zt(){return document.querySelector(".vjs-v7")}const It={getPlayer:zt,getPlayerData:_t,getVideoData:async function(t,e="en"){let o=null;const n=_t();c.A.log("udemyData",t);const i=qt();c.A.log("moduleData: ",i);const a=i.courseId,s=/learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1];c.A.log(`CourseId: ${a}, lectureId: ${s}`);const l=await Ft(a);c.A.log("courseLang Data:",l);const d=await Dt(t,a,s);console.log("lecture Data:",d);let u=l?.locale?.locale;u=u?L(u):"en",r.includes(u)||(u="en");const h=d?.asset?.length||n?.cache_?.duration,p=function(t){const e=t?.find((t=>"video/webm"===t.type||"video/mp4"===t.type));return e?.src}(d?.asset?.media_sources)||function(){const t=zt()?.querySelector("video")?.src;return!t?.startsWith("blob:")&&t}(),g=function(t,e,o){let n=t?.find((t=>L(t.locale_id)===e));return n||(n=t?.find((t=>L(t.locale_id)===o))||t?.[0]),n?.url}(d?.asset?.captions,u,e);console.log(`videoURL: ${p}, subtitlesURL: ${g}`),g&&p?o=[{target:"video_file_url",targetUrl:p},{target:"subtitles_file_url",targetUrl:g}]:p&&!g?(console.warn("[VOT] Subtitles files not found. Using the link only to the video file."),o={url:p}):console.error(`Failed to find subtitlesURL or videoURL. videoURL: ${p}, subtitlesURL: ${g}`);const v={duration:h,detectedLanguage:u,translationHelp:o};return c.A.log("udemy video data:",v),console.log("[VOT] Detected language: ",v.detectedLanguage),v},getModuleData:qt,getCourseLang:Ft,getLectureData:Dt};const Nt={getVideoData:async function(t){const e=await async function(t){return await fetch("https://api.banned.video/graphql",{method:"POST",body:JSON.stringify({operationName:"GetVideo",query:"query GetVideo($id: String!) {\n getVideo(id: $id) {\n ...DisplayVideoFields\n videoUrl: directUrl\n live\n }\n }\n\n fragment DisplayVideoFields on Video {\n title\n description: summary\n duration: videoDuration\n }",variables:{id:t}}),headers:{"User-Agent":"bannedVideoFrontEnd","apollographql-client-name":"banned-web","apollographql-client-version":"1.3","content-type":"application/json"}}).then((t=>t.json())).catch((t=>(console.error(t),{data:{getVideo:{}}})))}(t);c.A.log("banned.video video data:",e);const{videoUrl:o,duration:n,live:i,description:a,title:r}=e.data.getVideo;return{url:o,duration:n,live:i,title:r,description:a}}};const $t="https://global.apis.naver.com/weverse/wevweb",Ht="be4d79eb8fc7bd008ee82c8ec4ff6fd4",jt="1b9cb6378d959b45714bec49971ade22e6e24e42";async function Ut(t){const e=Date.now();let o=t.substring(0,Math.min(255,t.length))+e;const n=await async function(t,e){try{const o=new TextEncoder("utf-8");e=o.encode(e);const n=await window.crypto.subtle.importKey("raw",o.encode(t),{name:"HMAC",hash:{name:"SHA-1"}},!1,["sign","verify"]),i=await window.crypto.subtle.sign("HMAC",n,e);return btoa(String.fromCharCode(...new Uint8Array(i)))}catch(t){return console.error(t),!1}}(jt,o);return{wmsgpad:e,wmd:n}}function Wt(){return{appId:Ht,language:"en",os:"WEB",platform:"WEB",wpf:"pc"}}const Gt={getVideoData:async function(){const t=/([^/]+)\/(live|media)\/([^/]+)/.exec(new URL(window.location).pathname)?.[3],e=await async function(t){const e=`/post/v1.0/post-${t}/preview?`+new URLSearchParams({fieldSet:"postForPreview",...Wt()}),o=await Ut(e);try{const t=await fetch($t+e+"&"+new URLSearchParams(o));return await t.json()}catch(t){return console.error(t),!1}}(t);if(!e)return;c.A.log("weverse video preview data:",e);const{videoId:o,serviceId:n,infraVideoId:i}=e.extension.video;if(!(o&&n&&i))return!1;const a=await async function(t){const e=`/video/v1.1/vod/${t}/inKey?`+new URLSearchParams({gcc:"RU",...Wt()}),o=await Ut(e);try{const t=await fetch($t+e+"&"+new URLSearchParams(o),{method:"POST"});return await t.json()}catch(t){return console.error(t),!1}}(o);if(c.A.log("weverse video inKey data:",e),!a)return!1;const r=await async function(t,e,o){const n=Date.now();try{const i=await fetch(`https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${t}?`+new URLSearchParams({key:e,sid:o,nonce:n,devt:"html5_pc",prv:"N",aup:"N",stpb:"N",cpl:"en",env:"prod",lc:"en",adi:JSON.stringify([{adSystem:null}]),adu:"/"}));return await i.json()}catch(t){return console.error(t),!1}}(i,a.inKey,n);c.A.log("weverse video info:",r);const s=r.videos.list.find((t=>!1===t.useP2P&&t.source.includes(".mp4")));return!!s&&{url:s.source,duration:s.duration}}},Yt=[{additionalData:"mobile",host:"youtube",url:"https://youtu.be/",match:/^m.youtube.com$/,selector:"shorts-video #player"},{additionalData:"mobile",host:"youtube",url:"https://youtu.be/",match:/^m.youtube.com$/,selector:".player-container"},{host:"youtube",url:"https://youtu.be/",match:/^(www.)?youtube(-nocookie|kids)?.com$/,selector:".html5-video-container:not(#inline-player *)"},{host:"tiktok",url:"https://www.tiktok.com/",match:/^(www.)?tiktok.com$/,selector:null},{host:"proxitok",url:"https://www.tiktok.com/",match:n,selector:".column.has-text-centered"},{host:"twitch",url:"https://twitch.tv/",match:[/^m.twitch.tv$/,/^www.twitch.tv$/,/^clips.twitch.tv$/,/^player.twitch.tv$/],selector:".video-ref, main > div > section > div > div > div"},{host:"xvideos",url:"https://www.xvideos.com/",match:/^www.(xvideos|xv-ru).com$/,selector:".video-bg-pic"},{host:"pornhub",url:"https://rt.pornhub.com/view_video.php?viewkey=",match:/^[a-z]+.pornhub.com$/,selector:".mainPlayerDiv > .video-element-wrapper-js > div"},{additionalData:"embed",host:"pornhub",url:"https://rt.pornhub.com/view_video.php?viewkey=",match:t=>t.host.includes("pornhub.com")&&t.pathname.startsWith("/embed/"),selector:"#player"},{additionalData:"mobile",host:"vk",url:"https://vk.com/video?z=",match:/^m.vk.(com|ru)$/,selector:"vk-video-player",shadowRoot:!0},{additionalData:"clips",host:"vk",url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:'div[data-testid="clipcontainer-video"]'},{host:"vk",url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:".videoplayer_media"},{host:"vimeo",url:"https://vimeo.com/",match:/^vimeo.com$/,selector:".player"},{additionalData:"embed",host:"vimeo",url:"https://player.vimeo.com/",match:/^player.vimeo.com$/,selector:".player"},{host:"ok.ru",url:"https://ok.ru/video/",match:/^ok.ru$/,selector:".html5-vpl_vid"},{host:"nine_gag",url:"https://9gag.com/gag/",match:/^9gag.com$/,selector:".video-post"},{host:"bitchute",url:"https://www.bitchute.com/video/",match:/^(www.)?bitchute.com$/,selector:".video-js"},{host:"rutube",url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:".video-player > div > div > div:nth-child(2)"},{additionalData:"embed",host:"rutube",url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:"#app > div > div"},{host:"bilibili",url:"https://www.bilibili.com/video/",match:/^(www|m|player).bilibili.com$/,selector:".bpx-player-video-wrap"},{additionalData:"old",host:"bilibili",url:"https://www.bilibili.com/video/",match:/^(www|m).bilibili.com$/,selector:null},{host:"twitter",url:"https://twitter.com/i/status/",match:/^twitter.com$/,selector:'div[data-testid="videoComponent"] > div:nth-child(1) > div'},{host:"mail_ru",url:"https://my.mail.ru/",match:/^my.mail.ru$/,selector:"#b-video-wrapper"},{host:"coursera",url:"https://www.coursera.org/",match:/coursera.org$/,selector:".vjs-v6"},{host:"udemy",url:"https://www.udemy.com/",match:/udemy.com$/,selector:'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)'},{host:"invidious",url:"https://youtu.be/",match:t,selector:"#player"},{host:"piped",url:"https://youtu.be/",match:e,selector:".shaka-video-container"},{host:"rumble",url:"https://rumble.com/",match:/^rumble.com$/,selector:"#videoPlayer > .videoPlayer-Rumble-cls > div"},{host:"eporner",url:"https://www.eporner.com/",match:/^(www.)?eporner.com$/,selector:".vjs-v7"},{host:"peertube",url:"stub",match:i,selector:".vjs-v7"},{host:"dailymotion",url:"https://dai.ly/",match:/^geo.dailymotion.com$/,selector:".player"},{host:"trovo",url:"https://trovo.live/s/",match:/^trovo.live$/,selector:".player-video"},{host:"yandexdisk",url:"https://yadi.sk/i/",match:/^disk.yandex.ru$/,selector:".video-player__player > div:nth-child(1)"},{host:"coursehunter",url:"https://coursehunter.net/course/",match:/^coursehunter.net$/,selector:"#oframeplayer > pjsdiv:nth-of-type(1)"},{host:"googledrive",url:"https://drive.google.com/file/d/",match:/^youtube.googleapis.com$/,selector:".html5-video-container"},{host:"bannedvideo",url:"https://banned.video/watch?id=",match:/^(www.)?banned.video$/,selector:".vjs-v7"},{host:"facebook",url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/videos/"),selector:'div[role="main"] div[data-pagelet$="video" i]'},{additionalData:"reels",host:"facebook",url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/reel/"),selector:'div[role="main"]'},{host:"weverse",url:"https://weverse.io/",match:/^weverse.io$/,selector:".webplayer-internal-source-wrapper"},{host:"newgrounds",url:"https://www.newgrounds.com/",match:/^www.newgrounds.com$/,selector:".ng-video-player"},{host:"egghead",url:"https://egghead.io/",match:/^egghead.io$/,selector:".cueplayer-react-video-holder"},{host:"youku",url:"https://v.youku.com/",match:/^v.youku.com$/,selector:"#ykPlayer"},{host:"archive",url:"https://archive.org/details/",match:/^archive.org$/,selector:".jw-media"},{host:"directlink",url:"stub",match:t=>/([^.]+).mp4/.test(t.pathname),selector:null}];o("./node_modules/requestidlecallback-polyfill/index.js");class Zt{constructor(){this.listeners=new Set}hasListener(t){return this.listeners.has(t)}dispatchToListener(t,...e){try{t(...e)}catch(t){console.error("[VOT]",t)}}addListener(t){if(this.hasListener(t))throw new Error("[VOT] The listener has already been added.");this.listeners.add(t)}removeListener(t){if(!this.hasListener(t))throw new Error("[VOT] The listener has not been added yet.");this.listeners.delete(t)}dispatch(...t){for(const e of Array.from(this.listeners))this.dispatchToListener(e,...t)}}function Kt(t){return Array.from(t).flatMap((t=>t instanceof HTMLVideoElement?[t]:t instanceof HTMLElement?Array.from(t.querySelectorAll("video")):t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll("video")):[]))}const Jt=/advertise|promo|sponsor|banner|commercial|preroll|midroll|postroll|ad-container|sponsored/i;const Qt=vt.getParser(window.navigator.userAgent).getResult(),Xt=[...t,...e],te=["playing","ratechange","play","waiting","pause"];function ee(t,e){return t.map((t=>({label:B.get("langs")[t]??t.toUpperCase(),value:t,selected:e===t})))}function oe(t,e,o,n,i,a){c.A.log(`Translate video (url: ${t}, duration: ${e}, requestLang: ${o}, responseLang: ${n})`),c.A.log("translationHelp:",i),!0,xt(t,e,o,n,i,((t,e)=>{if(!1,c.A.log("[exec callback] Requesting video translation"),!t)return void a(!1,B.get("requestTranslationFailed"));const o=gt.decodeTranslationResponse(e);switch(console.log("[VOT] Translation response: ",o),o.status){case 0:a(!1,o.message);break;case 1:case 5:a(!!o.url,o.url||B.get("audioNotReceived"));break;case 2:a(!1,o.remainingTime?function(t){const e=Math.floor(t/60),o=Math.floor(t%60);return e>=60?B.get("translationTakeMoreThanHour"):1===e||0===e&&o>0?B.get("translationTakeAboutMinute"):11!==e&&e%10==1?B.get("translationTakeApproximatelyMinute2").replace("{0}",e):![12,13,14].includes(e)&&[2,3,4].includes(e%10)?B.get("translationTakeApproximatelyMinute").replace("{0}",e):B.get("translationTakeApproximatelyMinutes").replace("{0}",e)}(o.remainingTime):B.get("translationTakeFewMinutes"));break;case 3:case 6:a(!1,B.get("videoBeingTranslated"))}}))}class ne{translateFromLang="en";translateToLang=M;timer;ytData="";videoData="";firstPlay=!0;audio=new Audio;audioContext=new(window.AudioContext||window.webkitAudioContext);gainNode=this.audioContext.createGain();hls=C();videoTranslations=[];videoTranslationTTL=7200;downloadTranslationUrl=null;downloadSubtitlesUrl=null;autoRetry;streamPing;volumeOnStart;tempOriginalVolume;tempVolume;firstSyncVolume=!0;subtitlesList=[];subtitlesListVideoId=null;videoLastSrcObject=null;dragging;constructor(t,e,o){c.A.log("[VideoHandler] add video:",t,"container:",e,this),this.video=t,this.container=e,this.site=o,this.stopTranslationBound=this.stopTranslation.bind(this),this.handleVideoEventBound=this.handleVideoEvent.bind(this),this.changeOpacityOnEventBound=this.changeOpacityOnEvent.bind(this),this.resetTimerBound=this.resetTimer.bind(this),this.init()}async autoTranslate(){if(this.site.host,this.firstPlay&&1===this.data.autoTranslate&&this.videoData.videoId){this.firstPlay=!1;try{await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),this.transformBtn("error","VOTLocalizedError"===t?.name?t.localizedMessage:t)}}}async init(){if(this.initialized)return;const t={autoTranslate:u.get("autoTranslate",0,!0),dontTranslateLanguage:u.get("dontTranslateLanguage",M),dontTranslateYourLang:u.get("dontTranslateYourLang",1,!0),autoSetVolumeYandexStyle:u.get("autoSetVolumeYandexStyle",1,!0),autoVolume:u.get("autoVolume",a.JD,!0),buttonPos:u.get("buttonPos","default"),showVideoSlider:u.get("showVideoSlider",1,!0),syncVolume:u.get("syncVolume",0,!0),subtitlesMaxLength:u.get("subtitlesMaxLength",300,!0),highlightWords:u.get("highlightWords",0,!0),responseLanguage:u.get("responseLanguage",M),defaultVolume:u.get("defaultVolume",100,!0),udemyData:u.get("udemyData",{accessToken:"",expires:0}),audioProxy:u.get("audioProxy",0,!0),showPiPButton:u.get("showPiPButton",0,!0),translateAPIErrors:u.get("translateAPIErrors",1,!0),translationService:u.get("translationService",a.mE),detectService:u.get("detectService",a.K2),m3u8ProxyHost:u.get("m3u8ProxyHost",a.se),proxyWorkerHost:u.get("proxyWorkerHost",a.Pm),audioBooster:u.get("audioBooster",0,!0)};this.data=Object.fromEntries(await Promise.all(Object.entries(t).map((async([t,e])=>[t,await e])))),console.log("[db] data from db: ",this.data),this.subtitlesWidget=new At(this.video,this.container,this.site),this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength),this.subtitlesWidget.setHighlightWords(this.data.highlightWords),this.audio.crossOrigin="anonymous",this.gainNode.connect(this.audioContext.destination),this.audioSource=this.audioContext.createMediaElementSource(this.audio),this.audioSource.connect(this.gainNode),this.initUI(),this.initUIEvents();const e=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=e,e?this.votMenu.container.hidden=!0:(this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage??"ru")),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),await this.autoTranslate(),this.translateToLang=this.data.responseLanguage??"ru",this.initExtraEvents(),this.initialized=!0}transformBtn(t="none",e){this.votButton.container.dataset.status=t,this.votButton.container.dataset.translating="error"===t&&e.includes(B.get("translationTake")),this.votButton.label.innerHTML=e,this.votButton.container.title="error"===t?e:""}initUI(){this.votButton=X.createVOTButton(B.get("translateVideo")),this.data?.buttonPos&&"default"!==this.data?.buttonPos&&this.container.clientWidth&&this.container.clientWidth>550?(this.votButton.container.dataset.direction="column",this.votButton.container.dataset.position=this.data?.buttonPos):(this.votButton.container.dataset.direction="row",this.votButton.container.dataset.position="default"),this.container.appendChild(this.votButton.container),this.votButton.pipButton.hidden=!A()||!this.data?.showPiPButton,this.votButton.separator2.hidden=!A()||!this.data?.showPiPButton,this.votButton.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votMenu=X.createVOTMenu(B.get("VOTSettings")),this.votMenu.container.dataset.position=this.container.clientWidth&&this.container.clientWidth>550?this.data?.buttonPos:"default",this.container.appendChild(this.votMenu.container),this.votDownloadButton=X.createIconButton(''),this.votDownloadButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadButton),this.votDownloadSubtitlesButton=X.createIconButton(''),this.votDownloadSubtitlesButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadSubtitlesButton),this.votSettingsButton=X.createIconButton(''),this.votMenu.headerContainer.appendChild(this.votSettingsButton),this.votTranslationLanguageSelect=X.createVOTLanguageSelect({fromTitle:B.get("langs")[this.video.detectedLanguage],fromDialogTitle:B.get("videoLanguage"),fromItems:[{label:B.get("langs").auto,value:"auto",selected:""},...ee(r,this.videoData.detectedLanguage)],fromOnSelectCB:async t=>{c.A.log("[fromOnSelectCB] select from language",t.target.dataset.votValue),this.videoData=await this.getVideoData(),this.setSelectMenuValues(t.target.dataset.votValue,this.videoData.responseLanguage)},toTitle:B.get("langs")[this.video.responseLanguage],toDialogTitle:B.get("translationLanguage"),toItems:ee(s,this.videoData.responseLanguage),toOnSelectCB:async t=>{const e=t.target.dataset.votValue;c.A.log("[toOnSelectCB] select to language",e),this.data.responseLanguage=this.translateToLang=e,await u.set("responseLanguage",this.data.responseLanguage),c.A.log("Response Language value changed. New value: ",this.data.responseLanguage),this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage)}}),this.votMenu.bodyContainer.appendChild(this.votTranslationLanguageSelect.container),this.votSubtitlesSelect=X.createVOTSelect(B.get("VOTSubtitlesDisabled"),B.get("VOTSubtitles"),[{label:B.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1}],{onSelectCb:async t=>{await this.changeSubtitlesLang(t.target.dataset.votValue)},labelElement:X.createVOTSelectLabel(B.get("VOTSubtitles"))}),this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container),this.votVideoVolumeSlider=X.createSlider(`${B.get("VOTVolume")}: ${100*this.getVideoVolume()}%`,100*this.getVideoVolume()),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoVolumeSlider.container),this.votVideoTranslationVolumeSlider=X.createSlider(`${B.get("VOTVolumeTranslation")}: ${this.data?.defaultVolume??100}%`,this.data?.defaultVolume??100,0,this.data.audioBooster?a.T8:100),this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoTranslationVolumeSlider.container),this.votMenu.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votSettingsDialog=X.createDialog(B.get("VOTSettings")),document.documentElement.appendChild(this.votSettingsDialog.container),this.votTranslationHeader=X.createHeader(B.get("translationSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationHeader),this.votAutoTranslateCheckbox=X.createCheckbox(B.get("VOTAutoTranslate"),this.data?.autoTranslate??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoTranslateCheckbox.container),this.votDontTranslateYourLangSelect=X.createVOTSelect(B.get("langs")[u.syncGet("dontTranslateLanguage",M)],B.get("VOTDontTranslateYourLang"),ee(r,u.syncGet("dontTranslateLanguage",M)),{onSelectCb:async t=>{this.data.dontTranslateLanguage=t.target.dataset.votValue,await u.set("dontTranslateLanguage",this.data.dontTranslateLanguage)},labelElement:X.createCheckbox(B.get("VOTDontTranslateYourLang"),this.data?.dontTranslateYourLang??!0).container}),this.votSettingsDialog.bodyContainer.appendChild(this.votDontTranslateYourLangSelect.container),this.votAutoSetVolumeCheckbox=X.createCheckbox(`${B.get("VOTAutoSetVolume")}`,this.data?.autoSetVolumeYandexStyle??!0),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeCheckbox.container),this.votAutoSetVolumeSlider=X.createSlider(`${100*(this.data?.autoVolume??a.JD)}%`,100*(this.data?.autoVolume??a.JD),0,100),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeSlider.container),this.votShowVideoSliderCheckbox=X.createCheckbox(B.get("VOTShowVideoSlider"),this.data?.showVideoSlider??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votShowVideoSliderCheckbox.container),this.votAudioBoosterCheckbox=X.createCheckbox(B.get("VOTAudioBooster"),this.data?.audioBooster??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAudioBoosterCheckbox.container),this.votUdemyDataTextfield=X.createTextfield(B.get("VOTUdemyData"),this.data?.udemyData?.accessToken??""),this.votUdemyDataTextfield.container.hidden="udemy"!==this.site.host,this.votSettingsDialog.bodyContainer.appendChild(this.votUdemyDataTextfield.container),this.votSyncVolumeCheckbox=X.createCheckbox(B.get("VOTSyncVolume"),this.data?.syncVolume??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSyncVolumeCheckbox.container),this.votTranslationServiceSelect=X.createVOTSelect(u.syncGet("translationService",a.mE).toUpperCase(),B.get("VOTTranslationService"),ee(m,u.syncGet("translationService",a.mE)),{onSelectCb:async t=>{this.data.translationService=t.target.dataset.votValue,await u.set("translationService",this.data.translationService)},labelElement:X.createCheckbox(B.get("VOTTranslateAPIErrors"),this.data.translateAPIErrors??!0).container}),this.votTranslationServiceSelect.container.hidden="ru"===B.lang,this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationServiceSelect.container),this.votDetectServiceSelect=X.createVOTSelect(u.syncGet("detectService",a.K2).toUpperCase(),B.get("VOTDetectService"),ee(b,u.syncGet("detectService",a.K2)),{onSelectCb:async t=>{this.data.detectService=t.target.dataset.votValue,await u.set("detectService",this.data.detectService)},labelElement:X.createVOTSelectLabel(B.get("VOTDetectService"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votDetectServiceSelect.container),this.votSubtitlesHeader=X.createHeader(B.get("subtitlesSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader),this.votSubtitlesMaxLengthSlider=X.createSlider(`${B.get("VOTSubtitlesMaxLength")}: ${this.data?.subtitlesMaxLength??300}`,this.data?.subtitlesMaxLength??300,50,300),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesMaxLengthSlider.container),this.votSubtitlesHighlightWordsCheckbox=X.createCheckbox(B.get("VOTHighlightWords"),this.data?.highlightWords??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHighlightWordsCheckbox.container),this.votProxyHeader=X.createHeader(B.get("proxySettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votProxyHeader),this.votM3u8ProxyHostTextfield=X.createTextfield(B.get("VOTM3u8ProxyHost"),this.data?.m3u8ProxyHost,a.se),this.votSettingsDialog.bodyContainer.appendChild(this.votM3u8ProxyHostTextfield.container),this.votProxyWorkerHostTextfield=X.createTextfield(B.get("VOTProxyWorkerHost"),this.data?.proxyWorkerHost,a.Pm),this.votProxyWorkerHostTextfield.container.hidden=!0,this.votSettingsDialog.bodyContainer.appendChild(this.votProxyWorkerHostTextfield.container),this.votAudioProxyCheckbox=X.createCheckbox(B.get("VOTAudioProxy"),this.data?.audioProxy??!1),this.votAudioProxyCheckbox.container.hidden=!0,this.votSettingsDialog.bodyContainer.appendChild(this.votAudioProxyCheckbox.container),this.votAboutHeader=X.createHeader(B.get("about")),this.votSettingsDialog.bodyContainer.appendChild(this.votAboutHeader),this.votLanguageSelect=X.createVOTSelect(B.get("langs")[u.syncGet("locale-lang-override","auto")],B.get("VOTMenuLanguage"),ee(E,u.syncGet("locale-lang-override","auto")),{onSelectCb:async t=>{await u.set("locale-lang-override",t.target.dataset.votValue)},labelElement:X.createVOTSelectLabel(B.get("VOTMenuLanguage"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votLanguageSelect.container),this.votShowPiPButtonCheckbox=X.createCheckbox(B.get("VOTShowPiPButton"),this.data?.showPiPButton??!1),this.votShowPiPButtonCheckbox.container.hidden=!A(),this.votSettingsDialog.bodyContainer.appendChild(this.votShowPiPButtonCheckbox.container),this.votVersionInfo=X.createInformation(`${B.get("VOTVersion")}:`,GM_info.script.version),this.votSettingsDialog.bodyContainer.appendChild(this.votVersionInfo.container),this.votAuthorsInfo=X.createInformation(`${B.get("VOTAuthors")}:`,GM_info.script.author),this.votSettingsDialog.bodyContainer.appendChild(this.votAuthorsInfo.container),this.votLoaderInfo=X.createInformation(`${B.get("VOTLoader")}:`,`${GM_info.scriptHandler} v${GM_info.version}`),this.votSettingsDialog.bodyContainer.appendChild(this.votLoaderInfo.container),this.votBrowserInfo=X.createInformation(`${B.get("VOTBrowser")}:`,`${Qt.browser.name} ${Qt.browser.version} (${Qt.os.name} ${Qt.os.version})`),this.votSettingsDialog.bodyContainer.appendChild(this.votBrowserInfo.container),this.votResetSettingsButton=X.createButton(B.get("resetSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votResetSettingsButton)}initUIEvents(){this.votButton.translateButton.addEventListener("click",(()=>{(async()=>{if(this.audio.src)return c.A.log("[click translationBtn] audio.src is not empty"),void this.stopTranslate();if(this.hls.url)return c.A.log("[click translationBtn] hls is not empty"),void this.stopTranslate();try{if(c.A.log("[click translationBtn] trying execute translation"),!this.videoData.videoId)throw new tt("VOTNoVideoIDFound");"vk"===this.site.host&&"clips"===this.site.additionalData&&(this.videoData=await this.getVideoData()),await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),"VOTLocalizedError"===t?.name?this.transformBtn("error",t.localizedMessage):this.transformBtn("error",t)}})()})),this.votButton.pipButton.addEventListener("click",(()=>{(async()=>{this.video!==document.pictureInPictureElement?await this.video.requestPictureInPicture():await document.exitPictureInPicture()})()})),this.votButton.menuButton.addEventListener("click",(()=>{this.votMenu.container.hidden=!this.votMenu.container.hidden})),this.votButton.container.addEventListener("mousedown",(()=>{this.dragging=!0})),this.container.addEventListener("mouseup",(()=>{this.dragging=!1})),this.container.addEventListener("mousemove",(async t=>{if(this.dragging){t.preventDefault();const e=t.clientX/this.container.clientWidth*100;this.data.buttonPos=this.container.clientWidth&&this.container.clientWidth>550?e<=44?"left":e>=66?"right":"default":"default",this.votButton.container.dataset.direction="default"===this.data.buttonPos?"row":"column",this.votButton.container.dataset.position=this.data.buttonPos,this.votMenu.container.dataset.position=this.data.buttonPos,this.container.clientWidth&&this.container.clientWidth>550&&await u.set("buttonPos",this.data.buttonPos)}})),this.votDownloadButton.addEventListener("click",(()=>{this.downloadTranslationUrl&&window.open(this.downloadTranslationUrl,"_blank").focus()})),this.votDownloadSubtitlesButton.addEventListener("click",(async()=>{const t=function(t){let e="",o=1;for(const n of t.subtitles){let t=n.startMs/1e3,i=(n.startMs+n.durationMs)/1e3;e+=`${o}\n`,e+=`${Mt(t)} --\x3e ${Mt(i)}\n`,e+=`${n.text}\n\n`,o++}return e.trim()}(this.YandexSubtitles),e=new Blob([t],{type:"text/plain"}),o=URL.createObjectURL(e),n=document.createElement("a");n.href=o,n.download=`subtitles_${this.videoData.videoId}.srt`,n.click(),URL.revokeObjectURL(o)})),this.votSettingsButton.addEventListener("click",(()=>{this.votSettingsDialog.container.hidden=!this.votSettingsDialog.container.hidden,(document.fullscreenElement||document.webkitFullscreenElement)&&(document.webkitExitFullscreen&&document.webkitExitFullscreen(),document.exitFullscreen&&document.exitFullscreen())})),this.votVideoVolumeSlider.input.addEventListener("input",(t=>{const e=Number(t.target.value);this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`,this.setVideoVolume(e/100),this.data.syncVolume&&this.syncVolumeWrapper("video",e)})),this.votVideoTranslationVolumeSlider.input.addEventListener("input",(t=>{(async()=>{this.data.defaultVolume=Number(t.target.value),await u.set("defaultVolume",this.data.defaultVolume),this.votVideoTranslationVolumeSlider.label.querySelector("strong").innerHTML=`${this.data.defaultVolume}%`,this.gainNode.gain.value=this.data.defaultVolume/100,this.data.syncVolume&&(this.syncVolumeWrapper("translation",this.data.defaultVolume),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData&&this.setVideoVolume(this.tempOriginalVolume/100))})()})),this.votAutoTranslateCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoTranslate=Number(t.target.checked),await u.set("autoTranslate",this.data.autoTranslate),await this.autoTranslate(),c.A.log("autoTranslate value changed. New value: ",this.data.autoTranslate)})()})),this.votDontTranslateYourLangSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.dontTranslateYourLang=Number(t.target.checked),await u.set("dontTranslateYourLang",this.data.dontTranslateYourLang),c.A.log("dontTranslateYourLang value changed. New value: ",this.data.dontTranslateYourLang)})()})),this.votAutoSetVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoSetVolumeYandexStyle=Number(t.target.checked),await u.set("autoSetVolumeYandexStyle",this.data.autoSetVolumeYandexStyle),c.A.log("autoSetVolumeYandexStyle value changed. New value: ",this.data.autoSetVolumeYandexStyle)})()})),this.votAutoSetVolumeSlider.input.addEventListener("input",(t=>{(async()=>{const e=Number(t.target.value);this.data.autoVolume=(e/100).toFixed(2),await u.set("autoVolume",this.data.autoVolume),this.votAutoSetVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`})()})),this.votShowVideoSliderCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showVideoSlider=Number(t.target.checked),await u.set("showVideoSlider",this.data.showVideoSlider),c.A.log("showVideoSlider value changed. New value: ",this.data.showVideoSlider),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status})()})),this.votAudioBoosterCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioBooster=Number(t.target.checked),await u.set("audioBooster",this.data.audioBooster),c.A.log("audioBooster value changed. New value: ",this.data.audioBooster);const e=this.votVideoTranslationVolumeSlider.input.value;this.votVideoTranslationVolumeSlider.input.max=this.data.audioBooster?a.T8:100,this.data.audioBooster||(this.votVideoTranslationVolumeSlider.input.value=e>100?100:e,this.votVideoTranslationVolumeSlider.input.dispatchEvent(new Event("input")))})()})),this.votUdemyDataTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.udemyData={accessToken:t.target.value,expires:(new Date).getTime()},await u.set("udemyData",this.data.udemyData),c.A.log("udemyData value changed. New value: ",this.data.udemyData),window.location.reload()})()})),this.votSyncVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.syncVolume=Number(t.target.checked),await u.set("syncVolume",this.data.syncVolume),c.A.log("syncVolume value changed. New value: ",this.data.syncVolume)})()})),this.votTranslationServiceSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.translateAPIErrors=Number(t.target.checked),await u.set("translateAPIErrors",this.data.translateAPIErrors),c.A.log("translateAPIErrors value changed. New value: ",this.data.translateAPIErrors)})()})),this.votSubtitlesMaxLengthSlider.input.addEventListener("input",(t=>{(async()=>{this.data.subtitlesMaxLength=Number(t.target.value),await u.set("subtitlesMaxLength",this.data.subtitlesMaxLength),this.votSubtitlesMaxLengthSlider.label.querySelector("strong").innerHTML=`${this.data.subtitlesMaxLength}`,this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength)})()})),this.votSubtitlesHighlightWordsCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.highlightWords=Number(t.target.checked),await u.set("highlightWords",this.data.highlightWords),c.A.log("highlightWords value changed. New value: ",this.data.highlightWords),this.subtitlesWidget.setHighlightWords(this.data.highlightWords)})()})),this.votShowPiPButtonCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showPiPButton=Number(t.target.checked),await u.set("showPiPButton",this.data.showPiPButton),c.A.log("showPiPButton value changed. New value: ",this.data.showPiPButton),this.votButton.pipButton.hidden=!A()||!this.data.showPiPButton,this.votButton.separator2.hidden=!A()||!this.data.showPiPButton})()})),this.votM3u8ProxyHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.m3u8ProxyHost=t.target.value||a.se,await u.set("m3u8ProxyHost",this.data.m3u8ProxyHost),c.A.log("m3u8ProxyHost value changed. New value: ",this.data.m3u8ProxyHost)})()})),this.votProxyWorkerHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.proxyWorkerHost=t.target.value||a.Pm,await u.set("proxyWorkerHost",this.data.proxyWorkerHost),c.A.log("proxyWorkerHost value changed. New value: ",this.data.proxyWorkerHost),window.location.reload()})()})),this.votAudioProxyCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioProxy=Number(t.target.checked),await u.set("audioProxy",this.data.audioProxy),c.A.log("audioProxy value changed. New value: ",this.data.audioProxy)})()})),this.votResetSettingsButton.addEventListener("click",(()=>{(async()=>{B.reset();const t=await u.list();for(let e=0;e{this.extraEvents.push({element:t,event:e,handler:o}),t.addEventListener(e,o)},e=(e,o,n)=>{for(const i of o)t(e,i,n)};if(this.resizeObserver=new ResizeObserver((t=>{for(let e=0;e550;this.votButton.container.dataset.position=this.votMenu.container.dataset.position=e?this.data?.buttonPos:"default",this.votButton.container.dataset.direction=this.data?.buttonPos&&"default"!==this.data?.buttonPos&&e?"column":"row"})),this.resizeObserver.observe(this.video),this.votMenu.container.setAttribute("style",`--vot-container-height: ${this.video.getBoundingClientRect().height}px`),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData){this.syncVolumeObserver=new MutationObserver((t=>{if(this.audio.src&&this.data.syncVolume)for(let e=0;e{const e=t.target,o=this.votButton.container,n=this.votMenu.container,i=this.container,a=this.votSettingsDialog.container,r=document.querySelector(".vot-dialog-temp"),s=o.contains(e),l=n.contains(e),d=i.contains(e),u=a.contains(e),h=r?.contains(e)??!1;c.A.log(`[document click] ${s} ${l} ${d} ${u} ${h}`),s||l||u||h||(d||this.logout(0),this.votMenu.container.hidden=!0)})),o="pornhub"===this.site.host?"embed"===this.site.additionalData?document.querySelector("#player"):this.container.querySelector(".video-element-wrapper-js > div"):"twitter"===this.site.host?document.querySelector('div[data-testid="videoPlayer"]'):"yandexdisk"===this.site.host?document.querySelector(".video-player__player"):this.container,o&&e(o,["mousemove","mouseout"],this.resetTimerBound),t(this.votButton.container,"mousemove",this.changeOpacityOnEventBound),t(this.votMenu.container,"mousemove",this.changeOpacityOnEventBound),e(document,["touchstart","touchmove","touchend"],this.changeOpacityOnEventBound),t(this.votButton.container,"mousedown",(t=>{t.stopImmediatePropagation()})),t(this.votMenu.container,"mousedown",(t=>{t.stopImmediatePropagation()})),"youtube"===this.site.host&&(this.container.draggable=!1),"googledrive"===this.site.host&&(this.container.style.height="100%"),t(this.video,"canplaythrough",(async()=>{"rutube"===this.site.host&&this.video.src||V(this.site.host,this.video)!==this.videoData.videoId&&(await this.handleSrcChanged(),c.A.log("lipsync mode is loadeddata"),await this.autoTranslate())})),t(this.video,"emptied",(()=>{this.video.src&&V(this.site.host,this.video)===this.videoData.videoId||(c.A.log("lipsync mode is emptied"),this.videoData="",this.stopTranslation())})),["rutube","ok"].includes(this.site.host)||t(this.video,"volumechange",(()=>{this.syncVideoVolumeSlider()}))}logout(t){this.votMenu.container.hidden&&(this.votButton.container.style.opacity=t)}resetTimer(){clearTimeout(this.timer),this.logout(1),this.timer=setTimeout((()=>{this.logout(0)}),1e3)}changeOpacityOnEvent(t){clearTimeout(this.timer),this.logout(1),t.stopPropagation()}async changeSubtitlesLang(t){if(c.A.log("[onchange] subtitles",t),this.votSubtitlesSelect.setSelected(t),"disabled"===t)this.votSubtitlesSelect.setTitle(B.get("VOTSubtitlesDisabled")),this.subtitlesWidget.setContent(null),this.votDownloadSubtitlesButton.hidden=!0,this.YandexSubtitles=null;else{const e=await Vt(this.subtitlesList.at(parseInt(t)));this.subtitlesWidget.setContent(e),this.votDownloadSubtitlesButton.hidden=!1,this.YandexSubtitles=e}}async updateSubtitlesLangSelect(){const t=[{label:B.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1},...this.subtitlesList.map(((t,e)=>({label:(B.get("langs")[t.language]??t.language.toUpperCase())+(t.translatedFromLanguage?` ${B.get("VOTTranslatedFrom")} ${B.get("langs")[t.translatedFromLanguage]??t.translatedFromLanguage.toUpperCase()}`:"")+("yandex"!==t.source?` ${t.source}`:"")+(t.isAutoGenerated?` (${B.get("VOTAutogenerated")})`:""),value:e,selected:!1,disabled:!1})))];this.votSubtitlesSelect.updateItems(t),await this.changeSubtitlesLang(t[0].value)}async updateSubtitles(){if(await this.changeSubtitlesLang("disabled"),!this.videoData.videoId)return console.error(`[VOT] ${B.getDefault("VOTNoVideoIDFound")}`),this.subtitlesList=[],this.subtitlesListVideoId=null,this.votButton.container.hidden=!0,void await this.updateSubtitlesLangSelect();this.votButton.container.hidden=!1,this.subtitlesListVideoId!==this.videoData.videoId&&(this.subtitlesList=await Lt(this.site,this.videoData.videoId,this.videoData.detectedLanguage),this.subtitlesList?this.subtitlesListVideoId=this.videoData.videoId:await this.changeSubtitlesLang("disabled"),await this.updateSubtitlesLangSelect())}getVideoVolume(){let t=this.video?.volume;return["youtube","googledrive"].includes(this.site.host)&&(t=k.getVideoVolume()??t),t}setVideoVolume(t){if(["youtube","googledrive"].includes(this.site.host)){if(k.setVideoVolume(t))return}this.video.volume=t}isMuted(){return["youtube","googledrive"].includes(this.site.host)?k.isMuted():this.video?.muted}syncVideoVolumeSlider(){const t=this.isMuted()?0:100*this.getVideoVolume(),e=Math.round(t);this.votVideoVolumeSlider.input.value=e,this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=`${e}%`,X.updateSlider(this.votVideoVolumeSlider.input),1===this.data.syncVolume&&(this.tempOriginalVolume=Number(e))}setSelectMenuValues(t,e){this.votTranslationLanguageSelect.fromSelect.setTitle(B.get("langs")[t]),this.votTranslationLanguageSelect.toSelect.setTitle(B.get("langs")[e]),this.votTranslationLanguageSelect.fromSelect.setSelected(t),this.votTranslationLanguageSelect.toSelect.setSelected(e),console.log(`[VOT] Set translation from ${t} to ${e}`),this.videoData.detectedLanguage=t,this.videoData.responseLanguage=e}syncVolumeWrapper(t,e){const o="translation"===t?this.votVideoVolumeSlider:this.votVideoTranslationVolumeSlider,n=Number(o.input.value),i=function(t,e,o,n){let i=e;return e>n?(i=o+(e-n),i=i>100?100:Math.max(i,0),t.volume=i/100):e100?100:Math.max(i,0),t.volume=i/100),i}("translation"===t?this.video:this.audio,e,n,"translation"===t?this.tempVolume:this.tempOriginalVolume);o.input.value=i,o.label.querySelector("strong").innerHTML=`${i}%`,X.updateSlider(o.input),this.tempOriginalVolume="translation"===t?i:e,this.tempVolume="translation"===t?e:i}async getVideoData(){const t={translationHelp:null,isStream:!1,duration:this.video?.duration||343,videoId:V(this.site.host,this.video),detectedLanguage:this.translateFromLang,responseLanguage:this.translateToLang};if(!t.videoId)return this.ytData={},t;if("youtube"===this.site.host)this.ytData=await k.getVideoData(),t.isStream=this.ytData.isLive,this.ytData.title&&(t.detectedLanguage=this.ytData.detectedLanguage,t.responseLanguage=this.translateToLang);else if(["rutube","ok.ru","mail_ru"].includes(this.site.host))t.detectedLanguage="ru";else if("youku"===this.site.host)t.detectedLanguage="zh";else if("vk"===this.site.host){const e=document.getElementsByTagName("track")?.[0]?.srclang;t.detectedLanguage=e||"auto"}else if("coursera"===this.site.host){const e=await Et.getVideoData(this.translateToLang);t.duration=e.duration||t.duration,t.detectedLanguage=e.detectedLanguage,t.translationHelp=e.translationHelp}else if("coursehunter"===this.site.host){const e=await Ct.getVideoData();t.translationHelp={url:e.url},t.duration=e.duration||t.duration}else if("bannedvideo"===this.site.host){const e=await Nt.getVideoData(t.videoId);t.translationHelp={url:e.url},t.duration=e.duration||t.duration,t.isStream=e.live}else if("weverse"===this.site.host){const e=await Gt.getVideoData();t.detectedLanguage="ko",e&&(t.translationHelp={url:e.url},t.duration=e.duration||t.duration)}else if("udemy"===this.site.host){const e=await It.getVideoData(this.data.udemyData,this.translateToLang);t.duration=e.duration||t.duration,t.detectedLanguage=e.detectedLanguage,t.translationHelp=e.translationHelp}else"bitchute"===this.site.host?t.translationHelp={url:t.videoId}:["bilibili","piped","invidious","bitchute","rumble","peertube","dailymotion","trovo","yandexdisk","coursehunter","archive","directlink"].includes(this.site.host)&&(t.detectedLanguage="auto");return t}videoValidator(){if(["youtube","ok.ru","vk"].includes(this.site.host)&&(c.A.log("VideoValidator videoData: ",this.videoData),1===this.data.dontTranslateYourLang&&this.videoData.detectedLanguage===this.data.dontTranslateLanguage))throw new tt("VOTDisableFromYourLang");if(!this.videoData.isStream&&this.videoData.duration>14400)throw new tt("VOTVideoIsTooLong");return!0}lipSync(t=!1){if(c.A.log("lipsync video",this.video),this.video)if(this.audio.currentTime=this.video.currentTime,this.audio.playbackRate=this.video.playbackRate,t)if("play"!=t)["pause","stop","waiting"].includes(t)&&(c.A.log(`lipsync mode is ${t}`),this.audio.pause()),"playing"==t&&(c.A.log("lipsync mode is playing"),this.audio.play());else{c.A.log("lipsync mode is play");const t=this.audio.play();void 0!==t&&t.catch((t=>{if(console.error("[VOT]",t),"NotAllowedError"===t.name)throw this.transformBtn("error",B.get("grantPermissionToAutoPlay")),new tt("grantPermissionToAutoPlay");if("NotSupportedError"===t.name)throw this.transformBtn("error",Xt.includes(window.location.hostname)?B.get("neededAdditionalExtension"):B.get("audioFormatNotSupported")),Xt.includes(window.location.hostname)?new tt("neededAdditionalExtension"):new tt("audioFormatNotSupported")}))}else c.A.log("lipsync mode is not set")}handleVideoEvent(t){c.A.log(`video ${t.type}`),this.lipSync(t.type)}stopTranslate(){for(const t of te)this.video.removeEventListener(t,this.handleVideoEventBound);this.audio.pause(),this.audio.src="",this.audio.removeAttribute("src"),this.votVideoVolumeSlider.container.hidden=!0,this.votVideoTranslationVolumeSlider.container.hidden=!0,this.votDownloadButton.hidden=!0,this.downloadTranslationUrl=null,this.transformBtn("none",B.get("translateVideo")),c.A.log(`Volume on start: ${this.volumeOnStart}`),this.volumeOnStart&&this.setVideoVolume(this.volumeOnStart),this.volumeOnStart="",clearInterval(this.streamPing),clearTimeout(this.autoRetry),this.hls?.destroy(),this.hls=C(),this.firstSyncVolume=!0}async translateExecutor(t){c.A.log("Run translateFunc",t),this.translateFunc(t,this.videoData.isStream,this.videoData.detectedLanguage,this.videoData.responseLanguage,this.videoData.translationHelp)}async updateTranslationErrorMsg(t){const e=B.get("translationTake"),o=B.get("VOTTranslatingError"),n=B.lang;if("VOTLocalizedError"===t?.name)this.transformBtn("error",t.localizedMessage);else if(1!==this.data.translateAPIErrors||t.includes(e)||"ru"===n)this.transformBtn("error",t);else{const e=await async function(t,e="",o="ru"){switch(await u.get("translationService",a.mE)){case"yandex":{const n=e&&o?`${e}-${o}`:o;return await p.translate(t,n)}case"deepl":return await v.translate(t,e,o);default:return t}}(t,"ru",n);this.transformBtn("error",o),this.transformBtn("error",e)}}afterUpdateTranslation(t){this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,1===this.data.autoSetVolumeYandexStyle&&(this.votVideoVolumeSlider.input.value=100*this.data.autoVolume,this.votVideoVolumeSlider.label.querySelector("strong").innerHTML=100*this.data.autoVolume+"%",X.updateSlider(this.votVideoVolumeSlider.input)),this.votDownloadButton.hidden=!1,this.downloadTranslationUrl=t}updateTranslation(t){if(this.audio.src=t,this.volumeOnStart=this.getVideoVolume(),"number"==typeof this.data.defaultVolume&&(this.gainNode.gain.value=this.data.defaultVolume/100),"number"==typeof this.data.autoSetVolumeYandexStyle&&this.data.autoSetVolumeYandexStyle&&this.setVideoVolume(this.data.autoVolume),"twitter"===this.site.host)document.querySelector('button[data-testid="app-bar-back"][role="button"]').addEventListener("click",this.stopTranslationBound);this.video&&!this.video.paused&&this.lipSync("play");for(const t of te)this.video.addEventListener(t,this.handleVideoEventBound);this.transformBtn("success",B.get("disableTranslate")),this.afterUpdateTranslation(t)}translateFunc(t,e,o,n,i){console.log("[VOT] Video Data: ",this.videoData);const a=i?.url?i.url:`${this.site.url}${t}`;if(c.A.log("Run videoValidator"),this.videoValidator(),e)return c.A.log("Executed stream translation"),void function(t,e,o,n){c.A.log(`Translate stream (url: ${t}, requestLang: ${e}, responseLang: ${o})`),wt(t,e,o,((t,e)=>{if(c.A.log("[exec callback] Requesting stream translation"),!t)return void n(!1,B.get("requestTranslationFailed"));const o=gt.decodeStreamResponse(e);switch(console.log("[VOT] Stream Translation response: ",o),o.interval){case 10:n(!1,o.interval,B.get("translationTakeFewMinutes"));break;case 20:n(!0,o.interval,o||B.get("audioNotReceived"));break;case 0:n(!1,o.interval,B.get("streamNoConnectionToServer"))}}))}(a,o,n,(async(a,r,s)=>{if(c.A.log("[exec callback] translateStream callback"),V(this.site.host,this.video)!==t)return;if(!a||!s.translatedInfo)return await this.updateTranslationErrorMsg(s),void(10===r&&(clearTimeout(this.autoRetry),this.autoRetry=setTimeout((()=>this.translateFunc(t,e,o,n,i)),1e3*r)));this.transformBtn("success",B.get("disableTranslate")),console.log(s);const l=s.pingId;c.A.log(`Stream pingId: ${l}`),this.streamPing=setInterval((async()=>await yt(l,(t=>c.A.log("Stream ping result: ",t)))),1e3*r),c.A.log(s.translatedInfo.url);const d=`https://${this.data.m3u8ProxyHost}/?all=yes&origin=${encodeURIComponent("https://strm.yandex.ru")}&referer=${encodeURIComponent("https://strm.yandex.ru")}&url=${encodeURIComponent(s.translatedInfo.url)}`;if(c.A.log(d),this.hls)this.hls.on(Hls.Events.MEDIA_ATTACHED,(function(){c.A.log("audio and hls.js are now bound together !")})),this.hls.on(Hls.Events.MANIFEST_PARSED,(function(t){c.A.log("manifest loaded, found "+t?.levels?.length+" quality level")})),this.hls.loadSource(d),this.hls.attachMedia(this.audio),this.hls.on(Hls.Events.ERROR,(function(t){if(t.fatal)switch(t.type){case Hls.ErrorTypes.MEDIA_ERROR:console.log("fatal media error encountered, try to recover"),this.hls.recoverMediaError();break;case Hls.ErrorTypes.NETWORK_ERROR:console.error("fatal network error encountered",t);break;default:this.hls.destroy()}})),c.A.log(this.hls);else{if(!this.audio.canPlayType("application/vnd.apple.mpegurl"))throw new tt("audioFormatNotSupported");this.audio.src=d}if("youtube"===this.site.host&&k.videoSeek(this.video,10),this.volumeOnStart=this.getVideoVolume(),"number"==typeof this.data.defaultVolume&&(this.gainNode.gain.value=this.data.defaultVolume/100),"number"==typeof this.data.autoSetVolumeYandexStyle&&this.data.autoSetVolumeYandexStyle&&this.setVideoVolume(this.data.autoVolume),this.video.src||this.video.currentSrc||this.video.srcObject){this.video&&!this.video.paused&&this.lipSync("play");for(const t of te)this.video.addEventListener(t,this.handleVideoEventBound);this.afterUpdateTranslation(d)}else this.stopTranslation()}));if(["udemy","coursera"].includes(this.site.host)&&!i)throw new tt("VOTTranslationHelpNull");const r=this.videoTranslations.find((e=>e.videoId===t&&e.expires>Date.now()/1e3&&e.from===o&&e.to===n));if(r)return this.updateTranslation(r.url),void c.A.log("[translateFunc] A cached translate was received");const s=this.subtitlesList.some((t=>"yandex"===t.source))?2e4:3e4;oe(a,this.videoData.duration,o,n,i,(async(a,r)=>{if(c.A.log("[exec callback] translateVideo callback"),V(this.site.host,this.video)===t){if(!a)return await this.updateTranslationErrorMsg(r),r.includes(B.get("translationTake"))&&(clearTimeout(this.autoRetry),this.autoRetry=setTimeout((()=>this.translateFunc(t,e,o,n,i)),s)),void console.error("[VOT]",r);this.updateTranslation(r),this.subtitlesList.some((t=>"yandex"===t.source&&t.translatedFromLanguage===this.videoData.detectedLanguage&&t.language===this.videoData.responseLanguage))||(this.subtitlesList=await Lt(this.site,this.videoData.videoId,this.videoData.detectedLanguage),await this.updateSubtitlesLangSelect()),this.videoTranslations.push({videoId:t,from:o,to:n,url:r,expires:Date.now()/1e3+this.videoTranslationTTL})}}))}stopTranslation(){this.stopTranslate(),this.syncVideoVolumeSlider()}async handleSrcChanged(){c.A.log("[VideoHandler] src changed",this),this.firstPlay=!0,this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.videoData.responseLanguage);const t=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=t,t&&(this.votMenu.container.hidden=t),this.site.selector||(this.container=this.video.parentElement),this.container.contains(this.votButton.container)||(this.container.appendChild(this.votButton.container),this.container.appendChild(this.votMenu.container)),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),this.translateToLang=this.data.responseLanguage??"ru"}async release(){c.A.log("[VideoHandler] release"),this.initialized=!1,this.releaseExtraEvents(),this.subtitlesWidget.release(),this.votButton.container.remove(),this.votMenu.container.remove()}}const ie=new class{constructor(){this.videoCache=new Set,this.onVideoAdded=new Zt,this.onVideoRemoved=new Zt,this.observer=new MutationObserver((t=>{window.requestIdleCallback((()=>{for(let e=0;e{for(let e=0;e=3}(t)?requestAnimationFrame(o):e(t)}()}(t,(t=>{this.handleVideoAdded(t)}))}handleVideoAdded=t=>{this.onVideoAdded.dispatch(t)};handleVideoRemoved=t=>{document.contains(t)||(this.videoCache.delete(t),this.onVideoRemoved.dispatch(t))}},ae=new WeakMap;function re(t,e){if(t.shadowRoot){let o=t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.shadowRoot.contains(e))):e.parentElement;return o&&o.shadowRoot?o.parentElement:o}{const o=Qt.browser.version.split(".")[0];if(t.selector?.includes(":not")&&t.selector?.includes("*")&&o&&("Chrome"===Qt.browser.name&&Number(o)<88||"Firefox"===Qt.browser.name&&Number(o)<84)){const o=t.selector.split(" *")[0];return o?Array.from(document.querySelectorAll(o)).find((t=>t.contains(e))):e.parentElement}return t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.contains(e))):e.parentElement}}(async function(){if(c.A.log("Loading extension..."),await B.update(),c.A.log(`Selected menu language: ${B.lang}`),GM_info?.scriptHandler&&l.includes(GM_info.scriptHandler))return console.error(`[VOT] ${B.getDefault("unSupportedExtensionError").replace("{0}",GM_info.scriptHandler)}`),alert(`[VOT] ${B.get("unSupportedExtensionError").replace("{0}",GM_info.scriptHandler)}`);c.A.log("Extension compatibility passed..."),ie.onVideoAdded.addListener((t=>{for(const e of function(){const t=window.location.hostname,e=new URL(window.location),o=o=>o instanceof RegExp?o.test(t):"string"==typeof o?t.includes(o):"function"==typeof o&&o(e);return Yt.filter((t=>(Array.isArray(t.match)?t.match.some(o):o(t.match))&&t.host&&t.url))}()){if(!e)continue;let o=re(e,t);if(o&&(("rumble"!==e.host||t.style.display)&&(["peertube","directlink"].includes(e.host)&&(e.url=window.location.origin),!ae.has(t)))){ae.set(t,new ne(t,o,e));break}}})),ie.onVideoRemoved.addListener((async t=>{ae.has(t)&&(await ae.get(t).release(),ae.delete(t))})),ie.enable()})().catch((t=>{console.error("[VOT]",t)}))})()})(); \ No newline at end of file +/*! For license information please see vot-min.user.js.LICENSE.txt */ +(()=>{var t={"./node_modules/bowser/es5.js":function(t){t.exports=function(t){var e={};function o(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,o),i.l=!0,i.exports}return o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)o.d(n,i,function(e){return t[e]}.bind(null,i));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=90)}({17:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n=o(18),i=function(){function t(){}return t.getFirstMatch=function(t,e){var o=e.match(t);return o&&o.length>0&&o[1]||""},t.getSecondMatch=function(t,e){var o=e.match(t);return o&&o.length>1&&o[2]||""},t.matchAndReturnConst=function(t,e,o){if(t.test(e))return o},t.getWindowsVersionName=function(t){switch(t){case"NT":return"NT";case"XP":case"NT 5.1":return"XP";case"NT 5.0":return"2000";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return}},t.getMacOSVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),10===e[0])switch(e[1]){case 5:return"Leopard";case 6:return"Snow Leopard";case 7:return"Lion";case 8:return"Mountain Lion";case 9:return"Mavericks";case 10:return"Yosemite";case 11:return"El Capitan";case 12:return"Sierra";case 13:return"High Sierra";case 14:return"Mojave";case 15:return"Catalina";default:return}},t.getAndroidVersionName=function(t){var e=t.split(".").splice(0,2).map((function(t){return parseInt(t,10)||0}));if(e.push(0),!(1===e[0]&&e[1]<5))return 1===e[0]&&e[1]<6?"Cupcake":1===e[0]&&e[1]>=6?"Donut":2===e[0]&&e[1]<2?"Eclair":2===e[0]&&2===e[1]?"Froyo":2===e[0]&&e[1]>2?"Gingerbread":3===e[0]?"Honeycomb":4===e[0]&&e[1]<1?"Ice Cream Sandwich":4===e[0]&&e[1]<4?"Jelly Bean":4===e[0]&&e[1]>=4?"KitKat":5===e[0]?"Lollipop":6===e[0]?"Marshmallow":7===e[0]?"Nougat":8===e[0]?"Oreo":9===e[0]?"Pie":void 0},t.getVersionPrecision=function(t){return t.split(".").length},t.compareVersions=function(e,o,n){void 0===n&&(n=!1);var i=t.getVersionPrecision(e),a=t.getVersionPrecision(o),r=Math.max(i,a),s=0,l=t.map([e,o],(function(e){var o=r-t.getVersionPrecision(e),n=e+new Array(o+1).join(".0");return t.map(n.split("."),(function(t){return new Array(20-t.length).join("0")+t})).reverse()}));for(n&&(s=r-Math.min(i,a)),r-=1;r>=s;){if(l[0][r]>l[1][r])return 1;if(l[0][r]===l[1][r]){if(r===s)return 0;r-=1}else if(l[0][r]1?i-1:0),r=1;r0){var r=Object.keys(o),l=s.default.find(r,(function(t){return e.isOS(t)}));if(l){var d=this.satisfies(o[l]);if(void 0!==d)return d}var u=s.default.find(r,(function(t){return e.isPlatform(t)}));if(u){var c=this.satisfies(o[u]);if(void 0!==c)return c}}if(a>0){var h=Object.keys(i),p=s.default.find(h,(function(t){return e.isBrowser(t,!0)}));if(void 0!==p)return this.compareVersion(i[p])}},e.isBrowser=function(t,e){void 0===e&&(e=!1);var o=this.getBrowserName().toLowerCase(),n=t.toLowerCase(),i=s.default.getBrowserTypeByAlias(n);return e&&i&&(n=i.toLowerCase()),n===o},e.compareVersion=function(t){var e=[0],o=t,n=!1,i=this.getBrowserVersion();if("string"==typeof i)return">"===t[0]||"<"===t[0]?(o=t.substr(1),"="===t[1]?(n=!0,o=t.substr(2)):e=[],">"===t[0]?e.push(1):e.push(-1)):"="===t[0]?o=t.substr(1):"~"===t[0]&&(n=!0,o=t.substr(1)),e.indexOf(s.default.compareVersions(i,o,n))>-1},e.isOS=function(t){return this.getOSName(!0)===String(t).toLowerCase()},e.isPlatform=function(t){return this.getPlatformType(!0)===String(t).toLowerCase()},e.isEngine=function(t){return this.getEngineName(!0)===String(t).toLowerCase()},e.is=function(t,e){return void 0===e&&(e=!1),this.isBrowser(t,e)||this.isOS(t)||this.isPlatform(t)},e.some=function(t){var e=this;return void 0===t&&(t=[]),t.some((function(t){return e.is(t)}))},t}();e.default=d,t.exports=e.default},92:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=/version\/(\d+(\.?_?\d+)+)/i,r=[{test:[/googlebot/i],describe:function(t){var e={name:"Googlebot"},o=i.default.getFirstMatch(/googlebot\/(\d+(\.\d+))/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/opera/i],describe:function(t){var e={name:"Opera"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:opera)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opr\/|opios/i],describe:function(t){var e={name:"Opera"},o=i.default.getFirstMatch(/(?:opr|opios)[\s/](\S+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/SamsungBrowser/i],describe:function(t){var e={name:"Samsung Internet for Android"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:SamsungBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Whale/i],describe:function(t){var e={name:"NAVER Whale Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:whale)[\s/](\d+(?:\.\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MZBrowser/i],describe:function(t){var e={name:"MZ Browser"},o=i.default.getFirstMatch(/(?:MZBrowser)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/focus/i],describe:function(t){var e={name:"Focus"},o=i.default.getFirstMatch(/(?:focus)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/swing/i],describe:function(t){var e={name:"Swing"},o=i.default.getFirstMatch(/(?:swing)[\s/](\d+(?:\.\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/coast/i],describe:function(t){var e={name:"Opera Coast"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:coast)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/opt\/\d+(?:.?_?\d+)+/i],describe:function(t){var e={name:"Opera Touch"},o=i.default.getFirstMatch(/(?:opt)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/yabrowser/i],describe:function(t){var e={name:"Yandex Browser"},o=i.default.getFirstMatch(/(?:yabrowser)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/ucbrowser/i],describe:function(t){var e={name:"UC Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:ucbrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/Maxthon|mxios/i],describe:function(t){var e={name:"Maxthon"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:Maxthon|mxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/epiphany/i],describe:function(t){var e={name:"Epiphany"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:epiphany)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/puffin/i],describe:function(t){var e={name:"Puffin"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:puffin)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sleipnir/i],describe:function(t){var e={name:"Sleipnir"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:sleipnir)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/k-meleon/i],describe:function(t){var e={name:"K-Meleon"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/(?:k-meleon)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/micromessenger/i],describe:function(t){var e={name:"WeChat"},o=i.default.getFirstMatch(/(?:micromessenger)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/qqbrowser/i],describe:function(t){var e={name:/qqbrowserlite/i.test(t)?"QQ Browser Lite":"QQ Browser"},o=i.default.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/msie|trident/i],describe:function(t){var e={name:"Internet Explorer"},o=i.default.getFirstMatch(/(?:msie |rv:)(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/\sedg\//i],describe:function(t){var e={name:"Microsoft Edge"},o=i.default.getFirstMatch(/\sedg\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/edg([ea]|ios)/i],describe:function(t){var e={name:"Microsoft Edge"},o=i.default.getSecondMatch(/edg([ea]|ios)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/vivaldi/i],describe:function(t){var e={name:"Vivaldi"},o=i.default.getFirstMatch(/vivaldi\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/seamonkey/i],describe:function(t){var e={name:"SeaMonkey"},o=i.default.getFirstMatch(/seamonkey\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/sailfish/i],describe:function(t){var e={name:"Sailfish"},o=i.default.getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i,t);return o&&(e.version=o),e}},{test:[/silk/i],describe:function(t){var e={name:"Amazon Silk"},o=i.default.getFirstMatch(/silk\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/phantom/i],describe:function(t){var e={name:"PhantomJS"},o=i.default.getFirstMatch(/phantomjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/slimerjs/i],describe:function(t){var e={name:"SlimerJS"},o=i.default.getFirstMatch(/slimerjs\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e={name:"BlackBerry"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/blackberry[\d]+\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e={name:"WebOS Browser"},o=i.default.getFirstMatch(a,t)||i.default.getFirstMatch(/w(?:eb)?[o0]sbrowser\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/bada/i],describe:function(t){var e={name:"Bada"},o=i.default.getFirstMatch(/dolfin\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/tizen/i],describe:function(t){var e={name:"Tizen"},o=i.default.getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/qupzilla/i],describe:function(t){var e={name:"QupZilla"},o=i.default.getFirstMatch(/(?:qupzilla)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/firefox|iceweasel|fxios/i],describe:function(t){var e={name:"Firefox"},o=i.default.getFirstMatch(/(?:firefox|iceweasel|fxios)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/electron/i],describe:function(t){var e={name:"Electron"},o=i.default.getFirstMatch(/(?:electron)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/MiuiBrowser/i],describe:function(t){var e={name:"Miui"},o=i.default.getFirstMatch(/(?:MiuiBrowser)[\s/](\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/chromium/i],describe:function(t){var e={name:"Chromium"},o=i.default.getFirstMatch(/(?:chromium)[\s/](\d+(\.?_?\d+)+)/i,t)||i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/chrome|crios|crmo/i],describe:function(t){var e={name:"Chrome"},o=i.default.getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/GSA/i],describe:function(t){var e={name:"Google Search"},o=i.default.getFirstMatch(/(?:GSA)\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e={name:"Android Browser"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/playstation 4/i],describe:function(t){var e={name:"PlayStation 4"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/safari|applewebkit/i],describe:function(t){var e={name:"Safari"},o=i.default.getFirstMatch(a,t);return o&&(e.version=o),e}},{test:[/.*/i],describe:function(t){var e=-1!==t.search("\\(")?/^(.*)\/(.*)[ \t]\((.*)/:/^(.*)\/(.*) /;return{name:i.default.getFirstMatch(e,t),version:i.default.getSecondMatch(e,t)}}}];e.default=r,t.exports=e.default},93:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:[/Roku\/DVP/],describe:function(t){var e=i.default.getFirstMatch(/Roku\/DVP-(\d+\.\d+)/i,t);return{name:a.OS_MAP.Roku,version:e}}},{test:[/windows phone/i],describe:function(t){var e=i.default.getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.WindowsPhone,version:e}}},{test:[/windows /i],describe:function(t){var e=i.default.getFirstMatch(/Windows ((NT|XP)( \d\d?.\d)?)/i,t),o=i.default.getWindowsVersionName(e);return{name:a.OS_MAP.Windows,version:e,versionName:o}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(t){var e={name:a.OS_MAP.iOS},o=i.default.getSecondMatch(/(Version\/)(\d[\d.]+)/,t);return o&&(e.version=o),e}},{test:[/macintosh/i],describe:function(t){var e=i.default.getFirstMatch(/mac os x (\d+(\.?_?\d+)+)/i,t).replace(/[_\s]/g,"."),o=i.default.getMacOSVersionName(e),n={name:a.OS_MAP.MacOS,version:e};return o&&(n.versionName=o),n}},{test:[/(ipod|iphone|ipad)/i],describe:function(t){var e=i.default.getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i,t).replace(/[_\s]/g,".");return{name:a.OS_MAP.iOS,version:e}}},{test:function(t){var e=!t.test(/like android/i),o=t.test(/android/i);return e&&o},describe:function(t){var e=i.default.getFirstMatch(/android[\s/-](\d+(\.\d+)*)/i,t),o=i.default.getAndroidVersionName(e),n={name:a.OS_MAP.Android,version:e};return o&&(n.versionName=o),n}},{test:[/(web|hpw)[o0]s/i],describe:function(t){var e=i.default.getFirstMatch(/(?:web|hpw)[o0]s\/(\d+(\.\d+)*)/i,t),o={name:a.OS_MAP.WebOS};return e&&e.length&&(o.version=e),o}},{test:[/blackberry|\bbb\d+/i,/rim\stablet/i],describe:function(t){var e=i.default.getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i,t)||i.default.getFirstMatch(/blackberry\d+\/(\d+([_\s]\d+)*)/i,t)||i.default.getFirstMatch(/\bbb(\d+)/i,t);return{name:a.OS_MAP.BlackBerry,version:e}}},{test:[/bada/i],describe:function(t){var e=i.default.getFirstMatch(/bada\/(\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.Bada,version:e}}},{test:[/tizen/i],describe:function(t){var e=i.default.getFirstMatch(/tizen[/\s](\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.Tizen,version:e}}},{test:[/linux/i],describe:function(){return{name:a.OS_MAP.Linux}}},{test:[/CrOS/],describe:function(){return{name:a.OS_MAP.ChromeOS}}},{test:[/PlayStation 4/],describe:function(t){var e=i.default.getFirstMatch(/PlayStation 4[/\s](\d+(\.\d+)*)/i,t);return{name:a.OS_MAP.PlayStation4,version:e}}}];e.default=r,t.exports=e.default},94:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:[/googlebot/i],describe:function(){return{type:"bot",vendor:"Google"}}},{test:[/huawei/i],describe:function(t){var e=i.default.getFirstMatch(/(can-l01)/i,t)&&"Nova",o={type:a.PLATFORMS_MAP.mobile,vendor:"Huawei"};return e&&(o.model=e),o}},{test:[/nexus\s*(?:7|8|9|10).*/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Nexus"}}},{test:[/ipad/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/Macintosh(.*?) FxiOS(.*?)\//],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Apple",model:"iPad"}}},{test:[/kftt build/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Amazon",model:"Kindle Fire HD 7"}}},{test:[/silk/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet,vendor:"Amazon"}}},{test:[/tablet(?! pc)/i],describe:function(){return{type:a.PLATFORMS_MAP.tablet}}},{test:function(t){var e=t.test(/ipod|iphone/i),o=t.test(/like (ipod|iphone)/i);return e&&!o},describe:function(t){var e=i.default.getFirstMatch(/(ipod|iphone)/i,t);return{type:a.PLATFORMS_MAP.mobile,vendor:"Apple",model:e}}},{test:[/nexus\s*[0-6].*/i,/galaxy nexus/i],describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"Nexus"}}},{test:[/[^-]mobi/i],describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"blackberry"===t.getBrowserName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"BlackBerry"}}},{test:function(t){return"bada"===t.getBrowserName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"windows phone"===t.getBrowserName()},describe:function(){return{type:a.PLATFORMS_MAP.mobile,vendor:"Microsoft"}}},{test:function(t){var e=Number(String(t.getOSVersion()).split(".")[0]);return"android"===t.getOSName(!0)&&e>=3},describe:function(){return{type:a.PLATFORMS_MAP.tablet}}},{test:function(t){return"android"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.mobile}}},{test:function(t){return"macos"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop,vendor:"Apple"}}},{test:function(t){return"windows"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop}}},{test:function(t){return"linux"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.desktop}}},{test:function(t){return"playstation 4"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.tv}}},{test:function(t){return"roku"===t.getOSName(!0)},describe:function(){return{type:a.PLATFORMS_MAP.tv}}}];e.default=r,t.exports=e.default},95:function(t,e,o){"use strict";e.__esModule=!0,e.default=void 0;var n,i=(n=o(17))&&n.__esModule?n:{default:n},a=o(18),r=[{test:function(t){return"microsoft edge"===t.getBrowserName(!0)},describe:function(t){if(/\sedg\//i.test(t))return{name:a.ENGINE_MAP.Blink};var e=i.default.getFirstMatch(/edge\/(\d+(\.?_?\d+)+)/i,t);return{name:a.ENGINE_MAP.EdgeHTML,version:e}}},{test:[/trident/i],describe:function(t){var e={name:a.ENGINE_MAP.Trident},o=i.default.getFirstMatch(/trident\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){return t.test(/presto/i)},describe:function(t){var e={name:a.ENGINE_MAP.Presto},o=i.default.getFirstMatch(/presto\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:function(t){var e=t.test(/gecko/i),o=t.test(/like gecko/i);return e&&!o},describe:function(t){var e={name:a.ENGINE_MAP.Gecko},o=i.default.getFirstMatch(/gecko\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}},{test:[/(apple)?webkit\/537\.36/i],describe:function(){return{name:a.ENGINE_MAP.Blink}}},{test:[/(apple)?webkit/i],describe:function(t){var e={name:a.ENGINE_MAP.WebKit},o=i.default.getFirstMatch(/webkit\/(\d+(\.?_?\d+)+)/i,t);return o&&(e.version=o),e}}];e.default=r,t.exports=e.default}})},"./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss":(t,e,o)=>{"use strict";o.d(e,{A:()=>s});var n=o("./node_modules/css-loader/dist/runtime/noSourceMaps.js"),i=o.n(n),a=o("./node_modules/css-loader/dist/runtime/api.js"),r=o.n(a)()(i());r.push([t.id,'.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button[disabled=true]{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button[disabled=true]::before,.vot-button[disabled=true]::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button[disabled=true]{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button[disabled=true]::before,.vot-outlined-button[disabled=true]::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button[disabled=true]{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button[disabled=true]::before,.vot-text-button[disabled=true]::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button[disabled=true]{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button[disabled=true]::before,.vot-icon-button[disabled=true]::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):not(.vot-show-placeholer)::-moz-placeholder,.vot-textfield>textarea:not(:focus):not(.vot-show-placeholer)::-moz-placeholder{color:rgba(0,0,0,0) !important}.vot-textfield>input:not(:focus):not(.vot-show-placeholer)::-ms-input-placeholder,.vot-textfield>textarea:not(:focus):not(.vot-show-placeholer)::-ms-input-placeholder{color:rgba(0,0,0,0) !important}.vot-textfield>input:not(:focus):not(.vot-show-placeholer)::-webkit-input-placeholder,.vot-textfield>textarea:not(:focus):not(.vot-show-placeholer)::-webkit-input-placeholder{color:rgba(0,0,0,0) !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input.vot-show-placeholer+span::before,.vot-textfield>textarea.vot-show-placeholer+span::before{margin-right:0 !important}.vot-textfield>input.vot-show-placeholer+span::after,.vot-textfield>textarea.vot-show-placeholer+span::after{margin-left:0 !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input:not(:disabled),.vot-textfield:hover>textarea:not(:disabled){border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:disabled)+span::before,.vot-textfield:hover>input:not(:disabled)+span::after,.vot-textfield:hover>textarea:not(:disabled)+span::before,.vot-textfield:hover>textarea:not(:disabled)+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:disabled):not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:disabled):not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-details{display:flex;justify-content:space-between;align-items:center;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;border-radius:.5em;padding:.5em;margin:0 -0.5em;line-height:1.5;text-align:start;cursor:pointer;transition:background .5s ease}.vot-details[hidden]{display:none !important}.vot-details-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center;transform:scale(1.25) rotate(-90deg);fill:rgba(var(--vot-helper-onsurface-rgb), 0.87)}.vot-details:hover{background:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.04)}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:2147483647}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-loading=true] #vot-loading-icon{display:block !important}.vot-segmented-button[data-loading=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:2147483647;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-container{visibility:visible;position:absolute;z-index:2147483647}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:2147483647;left:25%;top:75%;pointer-events:none}.vot-subtitles{--vot-subtitles-background: rgba( var(--vot-surface-rgb, 46, 47, 52), var(--vot-subtitles-opacity, 0.8) );position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:.5em;pointer-events:all;padding:.5em;font-size:20px;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block;visibility:visible !important}',""]);const s=r},"./node_modules/css-loader/dist/runtime/api.js":t=>{"use strict";t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var o="",n=void 0!==e[5];return e[4]&&(o+="@supports (".concat(e[4],") {")),e[2]&&(o+="@media ".concat(e[2]," {")),n&&(o+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),o+=t(e),n&&(o+="}"),e[2]&&(o+="}"),e[4]&&(o+="}"),o})).join("")},e.i=function(t,o,n,i,a){"string"==typeof t&&(t=[[null,t,void 0]]);var r={};if(n)for(var s=0;s0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=a),o&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=o):u[2]=o),i&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=i):u[4]="".concat(i)),e.push(u))}},e}},"./node_modules/css-loader/dist/runtime/noSourceMaps.js":t=>{"use strict";t.exports=function(t){return t[1]}},"./node_modules/requestidlecallback-polyfill/index.js":()=>{window.requestIdleCallback=window.requestIdleCallback||function(t){var e=Date.now();return setTimeout((function(){t({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-e))}})}),1)},window.cancelIdleCallback=window.cancelIdleCallback||function(t){clearTimeout(t)}},"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":t=>{"use strict";var e=[];function o(t){for(var o=-1,n=0;n{"use strict";var e={};t.exports=function(t,o){var n=function(t){if(void 0===e[t]){var o=document.querySelector(t);if(window.HTMLIFrameElement&&o instanceof window.HTMLIFrameElement)try{o=o.contentDocument.head}catch(t){o=null}e[t]=o}return e[t]}(t);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(o)}},"./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":(t,e,o)=>{"use strict";t.exports=function(t){var e=o.nc;e&&t.setAttribute("nonce",e)}},"./node_modules/style-loader/dist/runtime/styleDomAPI.js":t=>{"use strict";t.exports=function(t){if("undefined"==typeof document)return{update:function(){},remove:function(){}};var e=t.insertStyleElement(t);return{update:function(o){!function(t,e,o){var n="";o.supports&&(n+="@supports (".concat(o.supports,") {")),o.media&&(n+="@media ".concat(o.media," {"));var i=void 0!==o.layer;i&&(n+="@layer".concat(o.layer.length>0?" ".concat(o.layer):""," {")),n+=o.css,i&&(n+="}"),o.media&&(n+="}"),o.supports&&(n+="}");var a=o.sourceMap;a&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),e.styleTagTransform(n,t,e.options)}(e,t,o)},remove:function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(e)}}}},"./node_modules/style-loader/dist/runtime/styleTagTransform.js":t=>{"use strict";t.exports=function(t,e){if(e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}},"./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js":t=>{t.exports=function(){return function(t){return t.styleTagTransform=function(t,e){e?.remove(),GM_addStyle(t)},document.createElement("style")}.apply(null,arguments)}}},e={};function o(n){var i=e[n];if(void 0!==i)return i.exports;var a=e[n]={id:n,exports:{}};return t[n].call(a.exports,a,a.exports,o),a.exports}o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.nc=void 0,(()=>{"use strict";var t=o("./node_modules/bowser/es5.js");const e=protobuf;var n;function i(t){switch(t){case 0:case"NO_CONNECTION":return n.NO_CONNECTION;case 10:case"TRANSLATING":return n.TRANSLATING;case 20:case"STREAMING":return n.STREAMING;default:return n.UNRECOGNIZED}}!function(t){t[t.NO_CONNECTION=0]="NO_CONNECTION",t[t.TRANSLATING=10]="TRANSLATING",t[t.STREAMING=20]="STREAMING",t[t.UNRECOGNIZED=-1]="UNRECOGNIZED"}(n||(n={}));const a={encode:(t,o=e.Writer.create())=>(""!==t.target&&o.uint32(10).string(t.target),""!==t.targetUrl&&o.uint32(18).string(t.targetUrl),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={target:"",targetUrl:""};for(;n.pos>>3){case 1:if(10!==t)break;a.target=n.string();continue;case 2:if(18!==t)break;a.targetUrl=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({target:b(t.target)?globalThis.String(t.target):"",targetUrl:b(t.targetUrl)?globalThis.String(t.targetUrl):""}),toJSON(t){const e={};return""!==t.target&&(e.target=t.target),""!==t.targetUrl&&(e.targetUrl=t.targetUrl),e},create:t=>a.fromPartial(t??{}),fromPartial(t){const e={target:"",targetUrl:""};return e.target=t.target??"",e.targetUrl=t.targetUrl??"",e}};const r={encode(t,o=e.Writer.create()){""!==t.url&&o.uint32(26).string(t.url),void 0!==t.deviceId&&o.uint32(34).string(t.deviceId),!1!==t.firstRequest&&o.uint32(40).bool(t.firstRequest),0!==t.duration&&o.uint32(49).double(t.duration),0!==t.unknown0&&o.uint32(56).int32(t.unknown0),""!==t.language&&o.uint32(66).string(t.language),!1!==t.forceSourceLang&&o.uint32(72).bool(t.forceSourceLang),0!==t.unknown1&&o.uint32(80).int32(t.unknown1);for(const e of t.translationHelp)a.encode(e,o.uint32(90).fork()).ldelim();return""!==t.responseLanguage&&o.uint32(114).string(t.responseLanguage),0!==t.unknown2&&o.uint32(120).int32(t.unknown2),0!==t.unknown3&&o.uint32(128).int32(t.unknown3),!1!==t.bypassCache&&o.uint32(136).bool(t.bypassCache),o},decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const r={url:"",deviceId:void 0,firstRequest:!1,duration:0,unknown0:0,language:"",forceSourceLang:!1,unknown1:0,translationHelp:[],responseLanguage:"",unknown2:0,unknown3:0,bypassCache:!1};for(;n.pos>>3){case 3:if(26!==t)break;r.url=n.string();continue;case 4:if(34!==t)break;r.deviceId=n.string();continue;case 5:if(40!==t)break;r.firstRequest=n.bool();continue;case 6:if(49!==t)break;r.duration=n.double();continue;case 7:if(56!==t)break;r.unknown0=n.int32();continue;case 8:if(66!==t)break;r.language=n.string();continue;case 9:if(72!==t)break;r.forceSourceLang=n.bool();continue;case 10:if(80!==t)break;r.unknown1=n.int32();continue;case 11:if(90!==t)break;r.translationHelp.push(a.decode(n,n.uint32()));continue;case 14:if(114!==t)break;r.responseLanguage=n.string();continue;case 15:if(120!==t)break;r.unknown2=n.int32();continue;case 16:if(128!==t)break;r.unknown3=n.int32();continue;case 17:if(136!==t)break;r.bypassCache=n.bool();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return r},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",deviceId:b(t.deviceId)?globalThis.String(t.deviceId):void 0,firstRequest:!!b(t.firstRequest)&&globalThis.Boolean(t.firstRequest),duration:b(t.duration)?globalThis.Number(t.duration):0,unknown0:b(t.unknown0)?globalThis.Number(t.unknown0):0,language:b(t.language)?globalThis.String(t.language):"",forceSourceLang:!!b(t.forceSourceLang)&&globalThis.Boolean(t.forceSourceLang),unknown1:b(t.unknown1)?globalThis.Number(t.unknown1):0,translationHelp:globalThis.Array.isArray(t?.translationHelp)?t.translationHelp.map((t=>a.fromJSON(t))):[],responseLanguage:b(t.responseLanguage)?globalThis.String(t.responseLanguage):"",unknown2:b(t.unknown2)?globalThis.Number(t.unknown2):0,unknown3:b(t.unknown3)?globalThis.Number(t.unknown3):0,bypassCache:!!b(t.bypassCache)&&globalThis.Boolean(t.bypassCache)}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),void 0!==t.deviceId&&(e.deviceId=t.deviceId),!1!==t.firstRequest&&(e.firstRequest=t.firstRequest),0!==t.duration&&(e.duration=t.duration),0!==t.unknown0&&(e.unknown0=Math.round(t.unknown0)),""!==t.language&&(e.language=t.language),!1!==t.forceSourceLang&&(e.forceSourceLang=t.forceSourceLang),0!==t.unknown1&&(e.unknown1=Math.round(t.unknown1)),t.translationHelp?.length&&(e.translationHelp=t.translationHelp.map((t=>a.toJSON(t)))),""!==t.responseLanguage&&(e.responseLanguage=t.responseLanguage),0!==t.unknown2&&(e.unknown2=Math.round(t.unknown2)),0!==t.unknown3&&(e.unknown3=Math.round(t.unknown3)),!1!==t.bypassCache&&(e.bypassCache=t.bypassCache),e},create:t=>r.fromPartial(t??{}),fromPartial(t){const e={url:"",deviceId:void 0,firstRequest:!1,duration:0,unknown0:0,language:"",forceSourceLang:!1,unknown1:0,translationHelp:[],responseLanguage:"",unknown2:0,unknown3:0,bypassCache:!1};return e.url=t.url??"",e.deviceId=t.deviceId??void 0,e.firstRequest=t.firstRequest??!1,e.duration=t.duration??0,e.unknown0=t.unknown0??0,e.language=t.language??"",e.forceSourceLang=t.forceSourceLang??!1,e.unknown1=t.unknown1??0,e.translationHelp=t.translationHelp?.map((t=>a.fromPartial(t)))||[],e.responseLanguage=t.responseLanguage??"",e.unknown2=t.unknown2??0,e.unknown3=t.unknown3??0,e.bypassCache=t.bypassCache??!1,e}};const s={encode:(t,o=e.Writer.create())=>(void 0!==t.url&&o.uint32(10).string(t.url),void 0!==t.duration&&o.uint32(17).double(t.duration),0!==t.status&&o.uint32(32).int32(t.status),void 0!==t.remainingTime&&o.uint32(40).int32(t.remainingTime),void 0!==t.unknown0&&o.uint32(48).int32(t.unknown0),""!==t.translationId&&o.uint32(58).string(t.translationId),void 0!==t.language&&o.uint32(66).string(t.language),void 0!==t.message&&o.uint32(74).string(t.message),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={url:void 0,duration:void 0,status:0,remainingTime:void 0,unknown0:void 0,translationId:"",language:void 0,message:void 0};for(;n.pos>>3){case 1:if(10!==t)break;a.url=n.string();continue;case 2:if(17!==t)break;a.duration=n.double();continue;case 4:if(32!==t)break;a.status=n.int32();continue;case 5:if(40!==t)break;a.remainingTime=n.int32();continue;case 6:if(48!==t)break;a.unknown0=n.int32();continue;case 7:if(58!==t)break;a.translationId=n.string();continue;case 8:if(66!==t)break;a.language=n.string();continue;case 9:if(74!==t)break;a.message=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):void 0,duration:b(t.duration)?globalThis.Number(t.duration):void 0,status:b(t.status)?globalThis.Number(t.status):0,remainingTime:b(t.remainingTime)?globalThis.Number(t.remainingTime):void 0,unknown0:b(t.unknown0)?globalThis.Number(t.unknown0):void 0,translationId:b(t.translationId)?globalThis.String(t.translationId):"",language:b(t.language)?globalThis.String(t.language):void 0,message:b(t.message)?globalThis.String(t.message):void 0}),toJSON(t){const e={};return void 0!==t.url&&(e.url=t.url),void 0!==t.duration&&(e.duration=t.duration),0!==t.status&&(e.status=Math.round(t.status)),void 0!==t.remainingTime&&(e.remainingTime=Math.round(t.remainingTime)),void 0!==t.unknown0&&(e.unknown0=Math.round(t.unknown0)),""!==t.translationId&&(e.translationId=t.translationId),void 0!==t.language&&(e.language=t.language),void 0!==t.message&&(e.message=t.message),e},create:t=>s.fromPartial(t??{}),fromPartial(t){const e={url:void 0,duration:void 0,status:0,remainingTime:void 0,unknown0:void 0,translationId:"",language:void 0,message:void 0};return e.url=t.url??void 0,e.duration=t.duration??void 0,e.status=t.status??0,e.remainingTime=t.remainingTime??void 0,e.unknown0=t.unknown0??void 0,e.translationId=t.translationId??"",e.language=t.language??void 0,e.message=t.message??void 0,e}};const l={encode:(t,o=e.Writer.create())=>(""!==t.language&&o.uint32(10).string(t.language),""!==t.url&&o.uint32(18).string(t.url),0!==t.unknown0&&o.uint32(24).int32(t.unknown0),""!==t.translatedLanguage&&o.uint32(34).string(t.translatedLanguage),""!==t.translatedUrl&&o.uint32(42).string(t.translatedUrl),0!==t.unknown1&&o.uint32(48).int32(t.unknown1),0!==t.unknown2&&o.uint32(56).int32(t.unknown2),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={language:"",url:"",unknown0:0,translatedLanguage:"",translatedUrl:"",unknown1:0,unknown2:0};for(;n.pos>>3){case 1:if(10!==t)break;a.language=n.string();continue;case 2:if(18!==t)break;a.url=n.string();continue;case 3:if(24!==t)break;a.unknown0=n.int32();continue;case 4:if(34!==t)break;a.translatedLanguage=n.string();continue;case 5:if(42!==t)break;a.translatedUrl=n.string();continue;case 6:if(48!==t)break;a.unknown1=n.int32();continue;case 7:if(56!==t)break;a.unknown2=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({language:b(t.language)?globalThis.String(t.language):"",url:b(t.url)?globalThis.String(t.url):"",unknown0:b(t.unknown0)?globalThis.Number(t.unknown0):0,translatedLanguage:b(t.translatedLanguage)?globalThis.String(t.translatedLanguage):"",translatedUrl:b(t.translatedUrl)?globalThis.String(t.translatedUrl):"",unknown1:b(t.unknown1)?globalThis.Number(t.unknown1):0,unknown2:b(t.unknown2)?globalThis.Number(t.unknown2):0}),toJSON(t){const e={};return""!==t.language&&(e.language=t.language),""!==t.url&&(e.url=t.url),0!==t.unknown0&&(e.unknown0=Math.round(t.unknown0)),""!==t.translatedLanguage&&(e.translatedLanguage=t.translatedLanguage),""!==t.translatedUrl&&(e.translatedUrl=t.translatedUrl),0!==t.unknown1&&(e.unknown1=Math.round(t.unknown1)),0!==t.unknown2&&(e.unknown2=Math.round(t.unknown2)),e},create:t=>l.fromPartial(t??{}),fromPartial(t){const e={language:"",url:"",unknown0:0,translatedLanguage:"",translatedUrl:"",unknown1:0,unknown2:0};return e.language=t.language??"",e.url=t.url??"",e.unknown0=t.unknown0??0,e.translatedLanguage=t.translatedLanguage??"",e.translatedUrl=t.translatedUrl??"",e.unknown1=t.unknown1??0,e.unknown2=t.unknown2??0,e}};const d={encode:(t,o=e.Writer.create())=>(""!==t.url&&o.uint32(10).string(t.url),""!==t.language&&o.uint32(18).string(t.language),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={url:"",language:""};for(;n.pos>>3){case 1:if(10!==t)break;a.url=n.string();continue;case 2:if(18!==t)break;a.language=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",language:b(t.language)?globalThis.String(t.language):""}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),""!==t.language&&(e.language=t.language),e},create:t=>d.fromPartial(t??{}),fromPartial(t){const e={url:"",language:""};return e.url=t.url??"",e.language=t.language??"",e}};const u={encode(t,o=e.Writer.create()){!1!==t.waiting&&o.uint32(8).bool(t.waiting);for(const e of t.subtitles)l.encode(e,o.uint32(18).fork()).ldelim();return o},decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={waiting:!1,subtitles:[]};for(;n.pos>>3){case 1:if(8!==t)break;a.waiting=n.bool();continue;case 2:if(18!==t)break;a.subtitles.push(l.decode(n,n.uint32()));continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({waiting:!!b(t.waiting)&&globalThis.Boolean(t.waiting),subtitles:globalThis.Array.isArray(t?.subtitles)?t.subtitles.map((t=>l.fromJSON(t))):[]}),toJSON(t){const e={};return!1!==t.waiting&&(e.waiting=t.waiting),t.subtitles?.length&&(e.subtitles=t.subtitles.map((t=>l.toJSON(t)))),e},create:t=>u.fromPartial(t??{}),fromPartial(t){const e={waiting:!1,subtitles:[]};return e.waiting=t.waiting??!1,e.subtitles=t.subtitles?.map((t=>l.fromPartial(t)))||[],e}};const c={encode:(t,o=e.Writer.create())=>(""!==t.url&&o.uint32(10).string(t.url),""!==t.timestamp&&o.uint32(18).string(t.timestamp),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={url:"",timestamp:""};for(;n.pos>>3){case 1:if(10!==t)break;a.url=n.string();continue;case 2:if(18!==t)break;a.timestamp=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",timestamp:b(t.timestamp)?globalThis.String(t.timestamp):""}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),""!==t.timestamp&&(e.timestamp=t.timestamp),e},create:t=>c.fromPartial(t??{}),fromPartial(t){const e={url:"",timestamp:""};return e.url=t.url??"",e.timestamp=t.timestamp??"",e}};const h={encode:(t,o=e.Writer.create())=>(""!==t.url&&o.uint32(10).string(t.url),""!==t.language&&o.uint32(18).string(t.language),""!==t.responseLanguage&&o.uint32(26).string(t.responseLanguage),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={url:"",language:"",responseLanguage:""};for(;n.pos>>3){case 1:if(10!==t)break;a.url=n.string();continue;case 2:if(18!==t)break;a.language=n.string();continue;case 3:if(26!==t)break;a.responseLanguage=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({url:b(t.url)?globalThis.String(t.url):"",language:b(t.language)?globalThis.String(t.language):"",responseLanguage:b(t.responseLanguage)?globalThis.String(t.responseLanguage):""}),toJSON(t){const e={};return""!==t.url&&(e.url=t.url),""!==t.language&&(e.language=t.language),""!==t.responseLanguage&&(e.responseLanguage=t.responseLanguage),e},create:t=>h.fromPartial(t??{}),fromPartial(t){const e={url:"",language:"",responseLanguage:""};return e.url=t.url??"",e.language=t.language??"",e.responseLanguage=t.responseLanguage??"",e}};const p={encode:(t,o=e.Writer.create())=>(0!==t.interval&&o.uint32(8).int32(t.interval),void 0!==t.translatedInfo&&c.encode(t.translatedInfo,o.uint32(18).fork()).ldelim(),void 0!==t.pingId&&o.uint32(24).int32(t.pingId),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={interval:0,translatedInfo:void 0,pingId:void 0};for(;n.pos>>3){case 1:if(8!==t)break;a.interval=n.int32();continue;case 2:if(18!==t)break;a.translatedInfo=c.decode(n,n.uint32());continue;case 3:if(24!==t)break;a.pingId=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({interval:b(t.interval)?i(t.interval):0,translatedInfo:b(t.translatedInfo)?c.fromJSON(t.translatedInfo):void 0,pingId:b(t.pingId)?globalThis.Number(t.pingId):void 0}),toJSON(t){const e={};return 0!==t.interval&&(e.interval=function(t){switch(t){case n.NO_CONNECTION:return"NO_CONNECTION";case n.TRANSLATING:return"TRANSLATING";case n.STREAMING:return"STREAMING";case n.UNRECOGNIZED:default:return"UNRECOGNIZED"}}(t.interval)),void 0!==t.translatedInfo&&(e.translatedInfo=c.toJSON(t.translatedInfo)),void 0!==t.pingId&&(e.pingId=Math.round(t.pingId)),e},create:t=>p.fromPartial(t??{}),fromPartial(t){const e={interval:0,translatedInfo:void 0,pingId:void 0};return e.interval=t.interval??0,e.translatedInfo=void 0!==t.translatedInfo&&null!==t.translatedInfo?c.fromPartial(t.translatedInfo):void 0,e.pingId=t.pingId??void 0,e}};const g={encode:(t,o=e.Writer.create())=>(0!==t.pingId&&o.uint32(8).int32(t.pingId),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={pingId:0};for(;n.pos>>3){case 1:if(8!==t)break;a.pingId=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({pingId:b(t.pingId)?globalThis.Number(t.pingId):0}),toJSON(t){const e={};return 0!==t.pingId&&(e.pingId=Math.round(t.pingId)),e},create:t=>g.fromPartial(t??{}),fromPartial(t){const e={pingId:0};return e.pingId=t.pingId??0,e}};const v={encode:(t,o=e.Writer.create())=>(""!==t.uuid&&o.uint32(10).string(t.uuid),""!==t.module&&o.uint32(18).string(t.module),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={uuid:"",module:""};for(;n.pos>>3){case 1:if(10!==t)break;a.uuid=n.string();continue;case 2:if(18!==t)break;a.module=n.string();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({uuid:b(t.uuid)?globalThis.String(t.uuid):"",module:b(t.module)?globalThis.String(t.module):""}),toJSON(t){const e={};return""!==t.uuid&&(e.uuid=t.uuid),""!==t.module&&(e.module=t.module),e},create:t=>v.fromPartial(t??{}),fromPartial(t){const e={uuid:"",module:""};return e.uuid=t.uuid??"",e.module=t.module??"",e}};const m={encode:(t,o=e.Writer.create())=>(""!==t.secretKey&&o.uint32(10).string(t.secretKey),0!==t.expires&&o.uint32(16).int32(t.expires),o),decode(t,o){const n=t instanceof e.Reader?t:e.Reader.create(t);let i=void 0===o?n.len:n.pos+o;const a={secretKey:"",expires:0};for(;n.pos>>3){case 1:if(10!==t)break;a.secretKey=n.string();continue;case 2:if(16!==t)break;a.expires=n.int32();continue}if(4==(7&t)||0===t)break;n.skipType(7&t)}return a},fromJSON:t=>({secretKey:b(t.secretKey)?globalThis.String(t.secretKey):"",expires:b(t.expires)?globalThis.Number(t.expires):0}),toJSON(t){const e={};return""!==t.secretKey&&(e.secretKey=t.secretKey),0!==t.expires&&(e.expires=Math.round(t.expires)),e},create:t=>m.fromPartial(t??{}),fromPartial(t){const e={secretKey:"",expires:0};return e.secretKey=t.secretKey??"",e.expires=t.expires??0,e}};function b(t){return null!=t}const f={encodeTranslationRequest:(t,e,o,n,i)=>r.encode({url:t,firstRequest:!0,duration:e,unknown0:1,language:o,forceSourceLang:!1,unknown1:0,translationHelp:i||[],responseLanguage:n,unknown2:0,unknown3:1,bypassCache:!1}).finish(),decodeTranslationResponse:t=>s.decode(new Uint8Array(t)),encodeSubtitlesRequest:(t,e)=>d.encode({url:t,language:e}).finish(),decodeSubtitlesResponse:t=>u.decode(new Uint8Array(t)),encodeStreamPingRequest:t=>g.encode({pingId:t}).finish(),encodeStreamRequest:(t,e,o)=>h.encode({url:t,language:e,responseLanguage:o}).finish(),decodeStreamResponse:t=>p.decode(new Uint8Array(t)),encodeYandexSessionRequest:(t,e)=>v.encode({uuid:t,module:e}).finish(),decodeYandexSessionResponse:t=>m.decode(new Uint8Array(t))},y={host:"api.browser.yandex.ru",hostVOT:"vot-api.toil.cc/v1",userAgent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 YaBrowser/24.6.0.0 Safari/537.36",componentVersion:"24.6.4.580",hmac:"bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf",defaultDuration:343},w=new TextEncoder;async function x(t,e,o){const n=await crypto.subtle.importKey("raw",w.encode(e),{name:"HMAC",hash:{name:t}},!1,["sign","verify"]);return await crypto.subtle.sign("HMAC",n,o)}async function S(t){const e=await x("SHA-256",y.hmac,t);return new Uint8Array(e).reduce(((t,e)=>t+e.toString(16).padStart(2,"0")),"")}var k,T;async function L(t,e={headers:{"User-Agent":y.userAgent}}){const{timeout:o=3e3}=e,n=new AbortController,i=setTimeout((()=>n.abort()),o),a=await fetch(t,{...e,signal:n.signal});return clearTimeout(i),a}!function(t){t.custom="custom",t.directlink="custom",t.youtube="youtube",t.piped="piped",t.invidious="invidious",t.vk="vk",t.nine_gag="nine_gag",t.gag="nine_gag",t.twitch="twitch",t.proxitok="proxitok",t.tiktok="tiktok",t.vimeo="vimeo",t.xvideos="xvideos",t.pornhub="pornhub",t.twitter="twitter",t.rumble="rumble",t.facebook="facebook",t.rutube="rutube",t.coub="coub",t.bilibili="bilibili",t.mail_ru="mailru",t.mailru="mailru",t.bitchute="bitchute",t.coursera="coursera",t.udemy="udemy",t.eporner="eporner",t.peertube="peertube",t.dailymotion="dailymotion",t.trovo="trovo",t.yandexdisk="yandexdisk",t.coursehunter="coursehunter",t.ok_ru="okru",t.okru="okru",t.googledrive="googledrive",t.bannedvideo="bannedvideo",t.weverse="weverse",t.newgrounds="newgrounds",t.egghead="egghead",t.youku="youku",t.archive="archive",t.kodik="kodik",t.patreon="patreon",t.reddit="reddit",t.kick="kick",t.apple_developer="apple_developer",t.appledeveloper="apple_developer"}(k||(k={})),function(t){t[t.FAILED=0]="FAILED",t[t.FINISHED=1]="FINISHED",t[t.WAITING=2]="WAITING",t[t.LONG_WAITING=3]="LONG_WAITING",t[t.PART_CONTENT=5]="PART_CONTENT",t[t.LONG_WAITING_2=6]="LONG_WAITING_2"}(T||(T={}));const V=["yewtu.be","yt.artemislena.eu","invidious.flokinet.to","iv.melmac.space","inv.nadeko.net","inv.tux.pizza","invidious.private.coffee","yt.drgnz.club","vid.puffyan.us","invidious.dhusch.de"],M=["piped.video","piped.tokhmi.xyz","piped.moomoo.me","piped.syncpundit.io","piped.mha.fi","watch.whatever.social","piped.garudalinux.org","efy.piped.pages.dev","watch.leptons.xyz","piped.lunar.icu","yt.dc09.ru","piped.mint.lgbt","il.ax","piped.privacy.com.de","piped.esmailelbob.xyz","piped.projectsegfau.lt","piped.in.projectsegfau.lt","piped.us.projectsegfau.lt","piped.privacydev.net","piped.palveluntarjoaja.eu","piped.smnz.de","piped.adminforge.de","piped.qdi.fi","piped.hostux.net","piped.chauvet.pro","piped.jotoma.de","piped.pfcd.me","piped.frontendfriendly.xyz"],A=[{additionalData:"mobile",host:k.youtube,url:"https://youtu.be/",match:/^m.youtube.com$/,selector:"shorts-video #player"},{additionalData:"mobile",host:k.youtube,url:"https://youtu.be/",match:/^m.youtube.com$/,selector:".player-container"},{host:k.youtube,url:"https://youtu.be/",match:/^(www.)?youtube(-nocookie|kids)?.com$/,selector:".html5-video-container:not(#inline-player *)"},{host:k.invidious,url:"https://youtu.be/",match:V,selector:"#player"},{host:k.piped,url:"https://youtu.be/",match:M,selector:".shaka-video-container"},{additionalData:"mobile",host:k.vk,url:"https://vk.com/video?z=",match:/^m.vk.(com|ru)$/,selector:"vk-video-player",shadowRoot:!0},{additionalData:"clips",host:k.vk,url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:'div[data-testid="clipcontainer-video"]'},{host:k.vk,url:"https://vk.com/video?z=",match:/^(www.|m.)?vk.(com|ru)$/,selector:".videoplayer_media"},{host:k.nine_gag,url:"https://9gag.com/gag/",match:/^9gag.com$/,selector:".video-post"},{host:k.twitch,url:"https://twitch.tv/",match:[/^m.twitch.tv$/,/^(www.)?twitch.tv$/,/^clips.twitch.tv$/,/^player.twitch.tv$/],selector:".video-ref, main > div > section > div > div > div"},{host:k.proxitok,url:"https://www.tiktok.com/",match:["proxitok.pabloferreiro.es","proxitok.pussthecat.org","tok.habedieeh.re","proxitok.esmailelbob.xyz","proxitok.privacydev.net","tok.artemislena.eu","tok.adminforge.de","tt.vern.cc","cringe.whatever.social","proxitok.lunar.icu","proxitok.privacy.com.de"],selector:".column.has-text-centered"},{host:k.tiktok,url:"https://www.tiktok.com/",match:/^(www.)?tiktok.com$/,selector:null},{host:k.vimeo,url:"https://vimeo.com/",match:/^vimeo.com$/,selector:".player"},{host:k.vimeo,url:"https://player.vimeo.com/",match:/^player.vimeo.com$/,selector:".player"},{host:k.xvideos,url:"https://www.xvideos.com/",match:/^(www.)?(xvideos|xv-ru).com$/,selector:".video-bg-pic"},{host:k.pornhub,url:"https://rt.pornhub.com/view_video.php?viewkey=",match:/^[a-z]+.pornhub.com$/,selector:".mainPlayerDiv > .video-element-wrapper-js > div"},{additionalData:"embed",host:k.pornhub,url:"https://rt.pornhub.com/view_video.php?viewkey=",match:t=>t.host.includes("pornhub.com")&&t.pathname.startsWith("/embed/"),selector:"#player"},{host:k.twitter,url:"https://twitter.com/i/status/",match:/^(twitter|x).com$/,selector:'div[data-testid="videoComponent"] > div:nth-child(1) > div'},{host:k.rumble,url:"https://rumble.com/",match:/^rumble.com$/,selector:"#videoPlayer > .videoPlayer-Rumble-cls > div"},{host:k.facebook,url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/videos/"),selector:'div[role="main"] div[data-pagelet$="video" i]'},{additionalData:"reels",host:k.facebook,url:"https://facebook.com/",match:t=>t.host.includes("facebook.com")&&t.pathname.includes("/reel/"),selector:'div[role="main"]'},{host:k.rutube,url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:".video-player > div > div > div:nth-child(2)"},{additionalData:"embed",host:k.rutube,url:"https://rutube.ru/video/",match:/^rutube.ru$/,selector:"#app > div > div"},{host:k.bilibili,url:"https://www.bilibili.com/video/",match:/^(www|m|player).bilibili.com$/,selector:".bpx-player-video-wrap"},{additionalData:"old",host:k.bilibili,url:"https://www.bilibili.com/video/",match:/^(www|m).bilibili.com$/,selector:null},{host:k.mailru,url:"https://my.mail.ru/",match:/^my.mail.ru$/,selector:"#b-video-wrapper"},{host:k.bitchute,url:"https://www.bitchute.com/video/",match:/^(www.)?bitchute.com$/,selector:".video-js"},{host:k.coursera,url:"https://www.coursera.org/",match:/coursera.org$/,selector:".vjs-v6",needExtraData:!0},{host:k.udemy,url:"https://www.udemy.com/",match:/udemy.com$/,selector:'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)',needExtraData:!0},{host:k.eporner,url:"https://www.eporner.com/",match:/^(www.)?eporner.com$/,selector:".vjs-v7"},{host:k.peertube,url:"stub",match:["peertube.1312.media","tube.shanti.cafe","bee-tube.fr","video.sadmin.io","dalek.zone","review.peertube.biz","peervideo.club","tube.la-dina.net","peertube.tmp.rcp.tf","peertube.su","video.blender.org"],selector:".vjs-v7"},{host:k.dailymotion,url:"https://dai.ly/",match:/^geo.dailymotion.com$/,selector:".player"},{host:k.trovo,url:"https://trovo.live/s/",match:/^trovo.live$/,selector:".player-video"},{host:k.yandexdisk,url:"https://yadi.sk/i/",match:/^disk.yandex.ru$/,selector:".video-player__player > div:nth-child(1)"},{host:k.coursehunter,url:"https://coursehunter.net/course/",match:/^coursehunter.net$/,selector:"#oframeplayer > pjsdiv:nth-of-type(1)",needExtraData:!0},{host:k.okru,url:"https://ok.ru/video/",match:/^ok.ru$/,selector:".html5-vpl_vid"},{host:k.googledrive,url:"https://drive.google.com/file/d/",match:/^youtube.googleapis.com$/,selector:".html5-video-container"},{host:k.bannedvideo,url:"https://madmaxworld.tv/watch?id=",match:/^(www.)?banned.video|madmaxworld.tv$/,selector:".vjs-v7",needExtraData:!0},{host:k.weverse,url:"https://weverse.io/",match:/^weverse.io$/,selector:".webplayer-internal-source-wrapper",needExtraData:!0},{host:k.newgrounds,url:"https://www.newgrounds.com/",match:/^(www.)?newgrounds.com$/,selector:".ng-video-player"},{host:k.egghead,url:"https://egghead.io/",match:/^egghead.io$/,selector:".cueplayer-react-video-holder"},{host:k.youku,url:"https://v.youku.com/",match:/^v.youku.com$/,selector:"#ykPlayer"},{host:k.archive,url:"https://archive.org/details/",match:/^archive.org$/,selector:".jw-media"},{host:k.kodik,url:"stub",match:/^kodik.(info|biz|cc)$/,selector:".fp-player",needExtraData:!0},{host:k.patreon,url:"stub",match:/^(www.)?patreon.com$/,selector:'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div',needExtraData:!0},{host:k.reddit,url:"stub",match:/^(www.)?reddit.com$/,selector:"shreddit-player",shadowRoot:!0,needExtraData:!0},{host:k.kick,url:"https://kick.com/",match:/^kick.com$/,selector:".vjs-v8",needExtraData:!0},{host:k.appledeveloper,url:"https://developer.apple.com/",match:/^developer.apple.com$/,selector:".developer-video-player",needExtraData:!0},{host:k.custom,url:"stub",match:t=>/([^.]+).mp4/.test(t.pathname),rawResult:!0}],O=["auto","ru","en","zh","ko","lt","lv","ar","fr","it","es","de","ja"],C=["ru","en","kk"],P=JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase","VOTMediaCSPError":"Failed to load audio (media csp error)","VOTSubtitlesDesign":"Subtitles design","VOTSubtitlesFontSize":"Font size of subtitles","VOTSubtitlesOpacity":"Transparency of the subtitle background"}'),E={log:(...t)=>console.log("%c[VOT DEBUG]","background: #F2452D; color: #fff; padding: 5px;",...t)},I=E,_=new class{constructor(){this.gmSupport="function"==typeof GM_getValue,I.log(`GM Storage Status: ${this.gmSupport}`)}syncGet(t,e=void 0,o=!1){if(this.gmSupport)return GM_getValue(t,e);let n=window.localStorage.getItem(t);if("udemyData"===t&&"string"==typeof n)try{n=JSON.parse(n)}catch{n=e}const i=n??e;return o?Number(i):i}async get(t,e=void 0,o=!1){return this.gmSupport?await GM_getValue(t,e):Promise.resolve(this.syncGet(t,e,o))}syncSet(t,e){return this.gmSupport?GM_setValue(t,e):("udemyData"===t&&(e=JSON.stringify(e)),window.localStorage.setItem(t,e))}async set(t,e){return this.gmSupport?await GM_setValue(t,e):Promise.resolve(this.syncSet(t,e))}syncDelete(t){return this.gmSupport?GM_deleteValue(t):window.localStorage.removeItem(t)}async delete(t){return this.gmSupport?await GM_deleteValue(t):Promise.resolve(this.syncDelete(t))}syncList(){return this.gmSupport?GM_listValues():["autoTranslate","dontTranslateLanguage","dontTranslateYourLang","autoSetVolumeYandexStyle","autoVolume","buttonPos","showVideoSlider","syncVolume","subtitlesMaxLength","highlightWords","responseLanguage","defaultVolume","audioProxy","showPiPButton","translateAPIErrors","translationService","detectService","m3u8ProxyHost","translateProxyEnabled","proxyWorkerHost","audioBooster","locale-version","locale-lang","locale-phrases"]}async list(){return this.gmSupport?await GM_listValues():Promise.resolve(this.syncList())}},N=navigator.language||navigator.userLanguage,R=N?.substr(0,2)?.toLowerCase()??"en";function $(t){return t.toLowerCase().split(/[_;-]/)[0].trim()}function B(){return"pictureInPictureEnabled"in document&&document.pictureInPictureEnabled}function D(){return"undefined"!=typeof Hls&&Hls?.isSupported()?new Hls({debug:!0,lowLatencyMode:!0,backBufferLength:90}):void 0}const F=new RegExp([/(?:https?|ftp):\/\/\S+/g,/https?:\/\/\S+|www\.\S+/gm,/\b\S+\.\S+/gm,/#[^\s#]+/g,/Auto-generated by YouTube/g,/Provided to YouTube by/g,/Released on/g,/0x[a-fA-F0-9]{40}/g,/[13][a-km-zA-HJ-NP-Z1-9]{25,34}/g,/4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}/g,/Paypal/g].map((t=>t.source)).join("|"));async function q(t,e={}){const{timeout:o=15e3,...n}=e,i=new AbortController,a=setTimeout((()=>i.abort()),o);try{if(t.includes("api.browser.yandex.ru"))throw new Error("Preventing yandex cors");const e=await fetch(t,{signal:i.signal,...n});return clearTimeout(a),e}catch(e){return I.log("GM_fetch preventing cors by GM_xmlhttpRequest",e.message),new Promise(((e,i)=>{clearTimeout(a),GM_xmlhttpRequest({method:n.method||"GET",url:t,responseType:"blob",...n,data:n.body,timeout:o,onload:t=>{const o=Object.fromEntries(t.responseHeaders.trim().split(/\r?\n/).map((t=>t.split(/: (.+)/))).filter((([t])=>t&&/^[\w-]+$/.test(t))));e(new Response(t.response,{status:t.status,headers:o}))},ontimeout:()=>i(new Error("Timeout")),onerror:t=>i(t),onabort:()=>i(new Error("AbortError"))})}))}}const z=["auto","en","ru","af","am","ar","az","bg","bn","bs","ca","cs","cy","da","de","el","es","et","eu","fa","fi","fr","gl","hi","hr","hu","hy","id","it","ja","jv","kk","km","kn","ko","lo","mk","ml","mn","ms","mt","my","ne","nl","pa","pl","pt","ro","si","sk","sl","sq","sr","su","sv","sw","tr","uk","ur","uz","vi","zh","zu"],H=new class{lang="en";locale={};gmValues=["locale-phrases","locale-lang","locale-version","locale-lang-override"];constructor(){const t=_.syncGet("locale-lang-override","auto");this.lang=t&&"auto"!==t?t:(navigator.language||navigator.userLanguage)?.substr(0,2)?.toLowerCase()??"en",this.setLocaleFromJsonString(_.syncGet("locale-phrases",""))}reset(){for(let t=0;t{if("object"==typeof t&&t)return t[e]}),t);return void 0===o&&console.warn("[VOT] [localizationProvider] locale",t,"doesn't contain key",e),o}getDefault(t){return this.getFromLocale(P,t)??t}get(t){return this.getFromLocale(this.locale,t)??this.getFromLocale(P,t)??t}};class U extends Error{constructor(t){super(t),this.name="VideoHelper",this.message=t}}class j{async getVideoData(t){try{const e=await L(`https://my.mail.ru/+/video/meta/${t}?xemail=&ajax_call=1&func_name=&mna=&mnb=&ext=1&_=${(new Date).getTime()}`);return await e.json()}catch(t){return void console.error("Failed to get mail.ru video info",t.message)}}}class W{API_ORIGIN="https://global.apis.naver.com/weverse/wevweb";API_APP_ID="be4d79eb8fc7bd008ee82c8ec4ff6fd4";API_HMAC_KEY="1b9cb6378d959b45714bec49971ade22e6e24e42";HEADERS={Accept:"application/json, text/plain, */*",Origin:"https://weverse.io",Referer:"https://weverse.io/"};getURLData(){return{appId:this.API_APP_ID,language:"en",os:"WEB",platform:"WEB",wpf:"pc"}}async createHash(t){const e=Date.now(),o=t.substring(0,Math.min(255,t.length))+e,n=await async function(t,e){try{const o=w.encode(e),n=await x("SHA-1",t,o);return btoa(String.fromCharCode(...new Uint8Array(n)))}catch(t){return console.error(t),!1}}(this.API_HMAC_KEY,o);if(!n)throw new U("Failed to get weverse HMAC signature");return{wmsgpad:e.toString(),wmd:n}}async getHashURLParams(t){const e=await this.createHash(t);return new URLSearchParams(e).toString()}async getPostPreview(t){const e=`/post/v1.0/post-${t}/preview?`+new URLSearchParams({fieldSet:"postForPreview",...this.getURLData()}).toString();try{const t=await this.getHashURLParams(e),o=await L(this.API_ORIGIN+e+"&"+t,{headers:this.HEADERS});return await o.json()}catch(e){return console.error(`Failed to get weverse post preview by postId: ${t}`,e.message),!1}}async getVideoInKey(t){const e=`/video/v1.1/vod/${t}/inKey?`+new URLSearchParams({gcc:"RU",...this.getURLData()}).toString();try{const t=await this.getHashURLParams(e),o=await L(this.API_ORIGIN+e+"&"+t,{method:"POST",headers:this.HEADERS});return await o.json()}catch(e){return console.error(`Failed to get weverse InKey by videoId: ${t}`,e.message),!1}}async getVideoInfo(t,e,o){const n=Date.now();try{const i=new URLSearchParams({key:e,sid:o,nonce:n.toString(),devt:"html5_pc",prv:"N",aup:"N",stpb:"N",cpl:"en",env:"prod",lc:"en",adi:JSON.stringify([{adSystem:null}]),adu:"/"}).toString(),a=await L(`https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${t}?`+i,{headers:this.HEADERS});return await a.json()}catch(n){return console.error(`Failed to get weverse video info (infraVideoId: ${t}, inkey: ${e}, serviceId: ${o}`,n.message),!1}}extractVideoInfo(t){return t.find((t=>!1===t.useP2P&&t.source.includes(".mp4")))}async getVideoData(t){const e=await this.getPostPreview(t);if(!e)return;const{videoId:o,serviceId:n,infraVideoId:i}=e.extension.video;if(!(o&&n&&i))return;const a=await this.getVideoInKey(o);if(!a)return;const r=await this.getVideoInfo(i,a.inKey,n);if(!r)return;const s=this.extractVideoInfo(r.videos.list);return s?{url:s.source,duration:s.duration}:void 0}}class G{API_ORIGIN=window.location.origin;async getSecureData(t){try{const e=this.API_ORIGIN+t,o=await L(e,{headers:{"User-Agent":y.userAgent,Origin:this.API_ORIGIN,Referer:this.API_ORIGIN}}),n=await o.text(),[i,a,r]=t.split("/").filter((t=>t)),s=(new DOMParser).parseFromString(n,"text/html"),l=Array.from(s.getElementsByTagName("script")).filter((t=>t.innerHTML.includes(`videoId = "${a}"`)));if(!l.length)throw new U("Failed to find secure script");const d=/'{[^']+}'/.exec(l[0].textContent.trim())?.[0];if(!d)throw new U("Secure json wasn't found in secure script");const u=JSON.parse(d.replaceAll("'",""));return{videoType:i,videoId:a,hash:r,...u}}catch(e){return console.error(`Failed to get kodik secure data by videoPath: ${t}.`,e.message),!1}}async getFtor(t){const{videoType:e,videoId:o,hash:n,d:i,d_sign:a,pd:r,pd_sign:s,ref:l,ref_sign:d}=t;try{const t=await L(this.API_ORIGIN+"/ftor",{method:"POST",headers:{"User-Agent":y.userAgent,Origin:this.API_ORIGIN,Referer:`${this.API_ORIGIN}/${e}/${o}/${n}/360p`},body:new URLSearchParams({d:i,d_sign:a,pd:r,pd_sign:s,ref:decodeURIComponent(l),ref_sign:d,bad_user:"false",cdn_is_working:"true",info:"{}",type:e,hash:n,id:o})});return await t.json()}catch(t){return console.error(`Failed to get kodik video data (type: ${e}, id: ${o}, hash: ${n})`,t.message),!1}}decryptUrl(t){return"https:"+atob(t.replace(/[a-zA-Z]/g,(function(t){const e=t.charCodeAt(0)+13;return String.fromCharCode((t<="Z"?90:122)>=e?e:e-26)})))}async getVideoData(t){const e=await this.getSecureData(t);if(!e)return;const o=await this.getFtor(e);if(!o)return;const n=Object.entries(o.links[o.default.toString()]),i=n.find((([t,e])=>"application/x-mpegURL"===e.type))?.[1];return i?{url:this.decryptUrl(i.src)}:void 0}}class J{async getPosts(t){try{const e=await L(`https://www.patreon.com/api/posts/${t}?json-api-use-default-includes=false`);return await e.json()}catch(e){return console.error(`Failed to get patreon posts by postId: ${t}.`,e.message),!1}}async getVideoData(t){const e=await this.getPosts(t);if(!e)return;const o=e.data.attributes.post_file.url;return o?{url:o}:void 0}}class Y{async getVideoData(){const t=document.querySelector("source[type='application/vnd.apple.mpegURL']")?.src?.replaceAll("&","&");if(t)return{url:decodeURIComponent(t)}}}class K{async getVideoInfo(t){try{const e=await L("https://api.banned.video/graphql",{method:"POST",body:JSON.stringify({operationName:"GetVideo",query:"query GetVideo($id: String!) {\n getVideo(id: $id) {\n title\n description: summary\n duration: videoDuration\n videoUrl: directUrl\n isStream: live\n }\n }",variables:{id:t}}),headers:{"User-Agent":"bannedVideoFrontEnd","apollographql-client-name":"banned-web","apollographql-client-version":"1.3","content-type":"application/json"}});return await e.json()}catch(e){return console.error(`Failed to get bannedvideo video info by videoId: ${t}.`,e.message),!1}}async getVideoData(t){const e=await this.getVideoInfo(t);if(!e)return!1;const{videoUrl:o,duration:n,isStream:i,description:a,title:r}=e.data.getVideo;return{url:o,duration:n,isStream:i,title:r,description:a}}}class Z{async getClipInfo(t){try{const e=await L(`https://kick.com/api/v2/clips/${t}`);return await e.json()}catch(e){return console.error(`Failed to get kick clip info by clipId: ${t}.`,e.message),!1}}async getVideoData(t){if(!t.startsWith("clip_"))return{url:A.find((t=>t.host===k.kick)).url+t};const e=await this.getClipInfo(t);if(!e)return!1;const{clip_url:o,duration:n,title:i}=e.clip;return{url:o,duration:n,title:i}}}class Q{API_ORIGIN="https://www.udemy.com/api-2.0";getModuleData(){const t=document.querySelector(".ud-app-loader[data-module-id='course-taking']")?.dataset?.moduleArgs;return t?JSON.parse(t):(console.error(H.get("udemyModuleArgsNotFound")),{})}getLectureId(){return/learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1]}async getLectureData(t,e){const o=await q(`${this.API_ORIGIN}/users/me/subscribed-courses/${t}/lectures/${e}/?`+new URLSearchParams({"fields[lecture]":"title,description,asset","fields[asset]":"length,media_sources,captions"}));return await o.json()}async getCourseLang(t){const e=await q(`${this.API_ORIGIN}/users/me/subscribed-courses/${t}?`+new URLSearchParams({"fields[course]":"locale"}));return await e.json()}findVideoUrl(t){return t?.find((t=>"video/mp4"===t.type))?.src}findSubtitleUrl(t,e){let o=t?.find((t=>$(t.locale_id)===e));return o||(o=t?.find((t=>"en"===$(t.locale_id)))??t?.[0]),o?.url}async getVideoData(t){const{courseId:e}=this.getModuleData();if(!e)return!1;const o=this.getLectureId();if(I.log(`[Udemy] courseId: ${e}, lectureId: ${o}`),!o)return!1;const{title:n,description:i,asset:a}=await this.getLectureData(e,o),{length:r,media_sources:s,captions:l}=a,d=this.findVideoUrl(s);if(!d)return console.log("Failed to find .mp4 video file in media_sources",s),!1;const u=await this.getCourseLang(e);let{locale:{locale:c}}=u;c=c?$(c):"en",O.includes(c)||(c="en");const h=this.findSubtitleUrl(l,c);return h||console.log("Failed to find subtitle file in captions",l),{...h?{url:A.find((t=>t.host===k.udemy)).url+t,translationHelp:[{target:"subtitles_file_url",targetUrl:h},{target:"video_file_url",targetUrl:d}],detectedLanguage:c}:{url:d,translationHelp:null},duration:r,title:n,description:i}}}class X{API_ORIGIN="https://coursehunter.net/api/v1";async getLessonsData(t){const e=await q(`${this.API_ORIGIN}/course/${t}/lessons`);return await e.json()}async getVideoData(){const t=window.course_id??+document.querySelector('input[name="course_id"]')?.value,e=window.lessons??await this.getLessonsData(t),o=+document.querySelector(".lessons-item_active")?.dataset?.index||1,n=e?.[o-1],{file:i,duration:a,title:r}=n;return!!i&&{url:i,duration:a,title:r}}}class tt{async getVideoData(t){const e=document.querySelector("meta[property='og:video']")?.content;if(e)return{url:e}}}class et{API_ORIGIN="https://www.coursera.org/api";async getCourseData(t){const e=await q(`${this.API_ORIGIN}/onDemandCourses.v1/${t}`),o=await e.json();return o?.elements?.[0]}getPlayer(){return document.querySelector(".vjs-v6")}getPlayerData(){return this.getPlayer()?.player}findVideoUrl(t){return t?.find((t=>"video/mp4"===t.type))?.src}findSubtitleUrl(t,e){let o=t?.find((t=>$(t.srclang)===e));return o||(o=t?.find((t=>"en"===$(t.srclang)))||t?.[0]),o?.src}async getVideoData(t){const e=this.getPlayerData(),{duration:o}=e?.cache_||{},{courseId:n,tracks:i,sources:a}=e?.options_||{},r=this.findVideoUrl(a);if(!r)return console.log("Failed to find .mp4 video file in sources",a),!1;const{primaryLanguageCodes:s}=await this.getCourseData(n);let l=s?.[0];l=l?$(l):"en",O.includes(l)||(l="en");const d=this.findSubtitleUrl(i,l);return d||console.log("Failed to find subtitle file in tracks",i),{...d?{url:A.find((t=>t.host===k.coursera)).url+t,translationHelp:[{target:"subtitles_file_url",targetUrl:d},{target:"video_file_url",targetUrl:r}],detectedLanguage:l}:{url:r,translationHelp:null},duration:o}}}class ot{static[k.mailru]=new j;static[k.weverse]=new W;static[k.kodik]=new G;static[k.patreon]=new J;static[k.reddit]=new Y;static[k.bannedvideo]=new K;static[k.kick]=new Z;static[k.udemy]=new Q;static[k.coursehunter]=new X;static[k.coursera]=new et;static[k.appledeveloper]=new tt}class nt extends Error{constructor(t){super(t),this.name="VideoDataError",this.message=t}}async function it(t,e){const o=new URL(window.location.href);switch(t.host){case k.custom:return o.href;case k.piped:case k.invidious:case k.youtube:return"youtu.be"===o.hostname&&(o.search=`?v=${o.pathname.replace("/","")}`,o.pathname="/watch"),/(?:watch|embed|shorts|live)\/([^/]+)/.exec(o.pathname)?.[1]??o.searchParams.get("v");case k.vk:{const t=/^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(o.pathname),n=o.searchParams.get("z"),i=o.searchParams.get("oid"),a=o.searchParams.get("id");if(t)return t[0].slice(1);if(n)return n.split("/")[0];if(i&&a)return`video-${Math.abs(parseInt(i))}_${a}`;const r=e.parentElement?.closest(".video_box_wrap");return r?r.id.replace("video_box_wrap","video"):null}case k.nine_gag:case k.gag:return/gag\/([^/]+)/.exec(o.pathname)?.[1];case k.twitch:{const t=/([^/]+)\/(?:clip)\/([^/]+)/.exec(o.pathname),e=/^clips\.twitch\.tv$/.test(o.hostname);if(/^m\.twitch\.tv$/.test(o.hostname))return/videos\/([^/]+)/.exec(o.href)?.[0]??o.pathname.slice(1);if(/^player\.twitch\.tv$/.test(o.hostname))return`videos/${o.searchParams.get("video")}`;if(e){const t=document.querySelector("script[type='application/ld+json']"),e=o.pathname.slice(1);if(t){const o=JSON.parse(t.innerText),n=o["@graph"].find((t=>"VideoObject"===t["@type"]))?.creator.url;return`${n.replace("https://www.twitch.tv/","")}/clip/${e}`}const n="embed"===e,i=document.querySelector(n?".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']":".clips-player a:not([class])");if(!i)return;return`${i.href.replace("https://www.twitch.tv/","")}/clip/${n?o.searchParams.get("clip"):e}`}return t?t[0]:/(?:videos)\/([^/]+)/.exec(o.pathname)?.[0]}case k.proxitok:case k.tiktok:return/([^/]+)\/video\/([^/]+)/.exec(o.pathname)?.[0];case k.vimeo:{const t=o.searchParams.get("app_id"),e=/[^/]+\/[^/]+$/.exec(o.pathname)?.[0]??/[^/]+$/.exec(o.pathname)?.[0];return t?`${e}?app_id=${t}`:e}case k.xvideos:return/[^/]+\/[^/]+$/.exec(o.pathname)?.[0];case k.pornhub:return o.searchParams.get("viewkey")??/embed\/([^/]+)/.exec(o.pathname)?.[1];case k.twitter:return/status\/([^/]+)/.exec(o.pathname)?.[1];case k.rumble:case k.facebook:return o.pathname.slice(1);case k.rutube:return/(?:video|embed)\/([^/]+)/.exec(o.pathname)?.[1];case k.bilibili:{const t=o.searchParams.get("bvid");if(t)return t;let e=/video\/([^/]+)/.exec(o.pathname)?.[1];return e&&null!==o.searchParams.get("p")&&(e+=`/?p=${o.searchParams.get("p")}`),e}case k.mailru:{const t=o.pathname;if(t.startsWith("/v/")||t.startsWith("/mail/"))return t.slice(1);const e=/video\/embed\/([^/]+)/.exec(t)?.[1];if(!e)return null;const n=await ot.mailru.getVideoData(e);return n?n.meta.url.replace("//my.mail.ru/",""):null}case k.bitchute:return/(video|embed)\/([^/]+)/.exec(o.pathname)?.[2];case k.coursera:return/learn\/([^/]+)\/lecture\/([^/]+)/.exec(o.pathname)?.[0];case k.udemy:return o.pathname.slice(1);case k.eporner:return/video-([^/]+)\/([^/]+)/.exec(o.pathname)?.[0];case k.peertube:return/\/w\/([^/]+)/.exec(o.pathname)?.[0];case k.dailymotion:{const t=Array.from(document.querySelectorAll("*")).filter((t=>t.innerHTML.trim().includes(".m3u8")));try{let e=t[1].lastChild.src;return/\/video\/(\w+)\.m3u8/.exec(e)?.[1]}catch(t){return console.error("[VOT]",t),!1}}case k.trovo:{const t=o.searchParams.get("vid");if(!t)return null;const e=/([^/]+)\/([\d]+)/.exec(o.pathname)?.[0];return e?`${e}?vid=${t}`:null}case k.yandexdisk:return/\/i\/([^/]+)/.exec(o.pathname)?.[1];case k.coursehunter:{const t=/\/course\/([^/]+)/.exec(o.pathname)?.[1];return!!t&&t+o.search}case k.okru:return/\/video\/(\d+)/.exec(o.pathname)?.[1];case k.googledrive:return/\/file\/d\/([^/]+)/.exec(o.pathname)?.[1];case k.bannedvideo:return o.searchParams.get("id");case k.weverse:return/([^/]+)\/(live|media)\/([^/]+)/.exec(o.pathname)?.[3];case k.newgrounds:return/([^/]+)\/(view)\/([^/]+)/.exec(o.pathname)?.[0];case k.egghead:return o.pathname.slice(1);case k.youku:return/v_show\/id_[\w=]+/.exec(o.pathname)?.[0];case k.archive:return/(details|embed)\/([^/]+)/.exec(o.pathname)?.[2];case k.kodik:return/\/(seria|video)\/([^/]+)\/([^/]+)\/([\d]+)p/.exec(o.pathname)?.[0];case k.patreon:{const t=/posts\/([^/]+)/.exec(o.pathname)?.[1];if(!t)return;return t.replace(/[^\d.]/g,"")}case k.reddit:return/\/r\/(([^/]+)\/([^/]+)\/([^/]+)\/([^/]+))/.exec(o.pathname)?.[1];case k.kick:{const t=/video\/([^/]+)/.exec(o.pathname)?.[0];if(t)return t;const e=o.searchParams.get("clip");if(e)return e;const n=document.getElementById("clip-video-player");return/clip_([^/]+)/.exec(n?.getAttribute("poster"))?.[0]}case k.appledeveloper:return/videos\/play\/([^/]+)\/([\d]+)/.exec(o.pathname)?.[0];default:return}}async function at(t,e){const o=await it(t,e);if(!o)throw new nt(`Entered unsupported link: "${t.host}"`);if(t.host===k.peertube&&(t.url=new URL(url).origin),t.rawResult)return{url:o,videoId:o,host:t.host,duration:void 0};if(!t.needExtraData)return{url:t.url+o,videoId:o,host:t.host,duration:void 0};const n=await ot[t.host].getVideoData(o);if(!n)throw new nt(`Failed to get video raw url for ${t.host}`);return{...n,videoId:o,host:t.host}}class rt extends Error{constructor(t){super(H.getDefault(t)),this.name="VOTLocalizedError",this.unlocalizedMessage=t,this.localizedMessage=H.get(t)}}const{rE:st}={rE:"0.7.2"};class lt extends Error{data;constructor(t,e=void 0){super(t),this.data=e,this.name="VOTJSError",this.message=t}}class dt{host;hostVOT;schema;schemaVOT;fetch;fetchOpts;getVideoDataFn;sessions={};requestLang;responseLang;userAgent=y.userAgent;componentVersion=y.componentVersion;paths={videoTranslation:"/video-translation/translate",videoSubtitles:"/video-subtitles/get-subtitles",streamPing:"/stream-translation/ping-stream",streamTranslation:"/stream-translation/translate-stream",createSession:"/session/create"};isCustomFormat(t){return/\.(m3u8|m4(a|v)|mpd)/.exec(t)}headers={"User-Agent":this.userAgent,Accept:"application/x-protobuf","Accept-Language":"en","Content-Type":"application/x-protobuf",Pragma:"no-cache","Cache-Control":"no-cache","Sec-Fetch-Mode":"no-cors"};headersVOT={"User-Agent":`vot.js/${st}`,"Content-Type":"application/json",Pragma:"no-cache","Cache-Control":"no-cache"};constructor({host:t=y.host,hostVOT:e=y.hostVOT,fetchFn:o=L,fetchOpts:n={},getVideoDataFn:i=at,requestLang:a="en",responseLang:r="ru",headers:s={}}={}){const l=/(http(s)?):\/\//,d=l.exec(t)?.[1];this.host=d?t.replace(`${d}://`,""):t,this.schema=d??"https";const u=l.exec(e)?.[1];this.hostVOT=u?e.replace(`${u}://`,""):e,this.schemaVOT=u??"https",this.fetch=o,this.fetchOpts=n,this.getVideoDataFn=i,this.requestLang=a,this.responseLang=r,this.headers={...this.headers,...s}}getOpts(t,e={}){return{method:"POST",headers:{...this.headers,...e},body:t,...this.fetchOpts}}async request(t,e,o={}){const n=this.getOpts(new Blob([e]),o);try{const e=await this.fetch(`${this.schema}://${this.host}${t}`,n),o=await e.arrayBuffer();return{success:200===e.status,data:o}}catch(t){return console.error("[vot.js]",t.message),{success:!1,data:null}}}async requestVOT(t,e,o={}){const n=this.getOpts(JSON.stringify(e),{...this.headersVOT,...o});try{console.log(`${this.schemaVOT}://${this.hostVOT}${t}`);const e=await this.fetch(`${this.schemaVOT}://${this.hostVOT}${t}`,n),o=await e.json();return{success:200===e.status,data:o}}catch(t){return console.error("[vot.js]",t.message),{success:!1,data:null}}}async getSession(t){const e=Math.floor(Date.now()/1e3),o=this.sessions[t];if(o&&o.timestamp+o.expires>e)return o;const{secretKey:n,expires:i,uuid:a}=await this.createSession(t);return this.sessions[t]={secretKey:n,expires:i,timestamp:e,uuid:a},this.sessions[t]}async translateVideoYAImpl({videoData:t,requestLang:e=this.requestLang,responseLang:o=this.responseLang,translationHelp:n=null,headers:i={}}){const{url:a,duration:r=y.defaultDuration}=t,{secretKey:s,uuid:l}=await this.getSession("video-translation"),d=f.encodeTranslationRequest(a,r,e,o,n),u=await S(d),c=await this.request(this.paths.videoTranslation,d,{"Vtrans-Signature":u,"Sec-Vtrans-Sk":s,"Sec-Vtrans-Token":`${u}:${l}:${this.paths.videoTranslation}:${this.componentVersion}`,...i});if(!c.success)throw new rt("requestTranslationFailed");const h=f.decodeTranslationResponse(c.data);switch(h.status){case T.FAILED:throw h?.message?new lt("Yandex couldn't translate video",h):new rt("requestTranslationFailed");case T.FINISHED:case T.PART_CONTENT:if(!h.url)throw new rt("audioNotReceived");return{translated:!0,url:h.url,remainingTime:h.remainingTime??-1};case T.WAITING:return{translated:!1,remainingTime:h.remainingTime};case T.LONG_WAITING:case T.LONG_WAITING_2:return{translated:!1,remainingTime:h.remainingTime??-1};default:throw console.error("[vot.js] Unknown response",h),new lt("Unknown response from Yandex",h)}}async translateVideoVOTImpl({url:t,videoId:e,service:o,requestLang:n=this.requestLang,responseLang:i=this.responseLang,headers:a={}}){const r=function(t,e,o){return t===k.patreon?{service:"mux",videoId:new URL(o).pathname.slice(1)}:{service:t,videoId:e}}(o,e,t),s=await this.requestVOT(this.paths.videoTranslation,{provider:"yandex",service:r.service,videoId:r.videoId,fromLang:n,toLang:i,rawVideo:t},a);if(!s.success)throw new rt("requestTranslationFailed",s);const l=s.data;switch(l.status){case"failed":throw new lt("Yandex couldn't translate video",l);case"success":if(!l.translatedUrl)throw new rt("audioNotReceived");return{translated:!0,url:l.translatedUrl,remainingTime:-1};case"waiting":return{translated:!1,remainingTime:l.remainingTime,message:l.message}}}async translateVideo({videoData:t,requestLang:e=this.requestLang,responseLang:o=this.responseLang,translationHelp:n=null,headers:i={}}){const{url:a,videoId:r,host:s}=t;return this.isCustomFormat(a)?await this.translateVideoVOTImpl({url:a,videoId:r,service:s,requestLang:e,responseLang:o,headers:i}):await this.translateVideoYAImpl({videoData:t,requestLang:e,responseLang:o,translationHelp:n,headers:i})}async getSubtitles({videoData:t,requestLang:e=this.requestLang,headers:o={}}){const{url:n}=t;if(this.isCustomFormat(n))throw new rt("Unsupported video URL for getting subtitles");const{secretKey:i,uuid:a}=await this.getSession("video-translation"),r=f.encodeSubtitlesRequest(n,e),s=await S(r),l=await this.request(this.paths.videoSubtitles,r,{"Vsubs-Signature":await S(r),"Sec-Vsubs-Sk":i,"Sec-Vsubs-Token":`${s}:${a}:${this.paths.videoSubtitles}:${this.componentVersion}`,...o});if(!l.success)throw new lt("Failed to request video subtitles",l);return f.decodeSubtitlesResponse(l.data)}async pingStream({pingId:t,headers:e={}}){const{secretKey:o,uuid:n}=await this.getSession("video-translation"),i=f.encodeStreamPingRequest(t),a=await S(i),r=await this.request(this.paths.streamPing,i,{"Vtrans-Signature":await S(i),"Sec-Vtrans-Sk":o,"Sec-Vtrans-Token":`${a}:${n}:${this.paths.streamPing}:${this.componentVersion}`,...e});if(!r.success)throw new lt("Failed to request stream ping",r);return!0}async translateStream({videoData:t,requestLang:e=this.requestLang,responseLang:o=this.responseLang,headers:i={}}){const{url:a}=t;if(this.isCustomFormat(a))throw new rt("Unsupported video URL for getting stream translation");const{secretKey:r,uuid:s}=await this.getSession("video-translation"),l=f.encodeStreamRequest(a,e,o),d=await S(l),u=await this.request(this.paths.streamTranslation,l,{"Vtrans-Signature":await S(l),"Sec-Vtrans-Sk":r,"Sec-Vtrans-Token":`${d}:${s}:${this.paths.streamTranslation}:${this.componentVersion}`,...i});if(!u.success)throw new lt("Failed to request stream translation",u);const c=f.decodeStreamResponse(u.data),h=c.interval;switch(h){case n.NO_CONNECTION:case n.TRANSLATING:return{translated:!1,interval:h,message:h===n.NO_CONNECTION?"streamNoConnectionToServer":"translationTakeFewMinutes"};case n.STREAMING:return{translated:!0,interval:h,pingId:c.pingId,result:c.translatedInfo};default:throw console.error("[vot.js] Unknown response",c),new lt("Unknown response from Yandex",c)}}async createSession(t){const e=function(){let t="";for(let e=0;e<32;e++)t+="0123456789ABCDEF"[Math.floor(16*Math.random())];return t}(),o=f.encodeYandexSessionRequest(e,t),n=await this.request(this.paths.createSession,o,{"Vtrans-Signature":await S(o)});if(!n.success)throw new lt("Failed to request create session",n);return{...f.decodeYandexSessionResponse(n.data),uuid:e}}}class ut extends dt{async request(t,e,o={}){const n=this.getOpts(JSON.stringify({headers:{...this.headers,...o},body:Array.from(e)}),{"Content-Type":"application/json"});try{const e=await this.fetch(`${this.schema}://${this.host}${t}`,n),o=await e.arrayBuffer();return{success:200===e.status,data:o}}catch(t){return console.error("[vot.js]",t.message),{success:!1,data:null}}}}function ct(t){const e=Math.floor(t/3600),o=Math.floor(t%3600/60),n=Math.floor(t%60),i=Math.floor(t%1*1e3);return`${e.toString().padStart(2,"0")}:${o.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")},${i.toString().padStart(3,"0")}`}const ht=globalThis,pt=ht.trustedTypes,gt=pt?pt.createPolicy("lit-html",{createHTML:t=>t}):void 0,vt="$lit$",mt=`lit$${Math.random().toFixed(9).slice(2)}$`,bt="?"+mt,ft=`<${bt}>`,yt=document,wt=()=>yt.createComment(""),xt=t=>null===t||"object"!=typeof t&&"function"!=typeof t,St=Array.isArray,kt=t=>St(t)||"function"==typeof t?.[Symbol.iterator],Tt="[ \t\n\f\r]",Lt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Vt=/-->/g,Mt=/>/g,At=RegExp(`>|${Tt}(?:([^\\s"'>=/]+)(${Tt}*=${Tt}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Ot=/'/g,Ct=/"/g,Pt=/^(?:script|style|textarea|title)$/i,Et=t=>(e,...o)=>({_$litType$:t,strings:e,values:o}),It=Et(1),_t=Et(2),Nt=(Et(3),Symbol.for("lit-noChange")),Rt=Symbol.for("lit-nothing"),$t=new WeakMap,Bt=yt.createTreeWalker(yt,129);function Dt(t,e){if(!St(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==gt?gt.createHTML(e):e}const Ft=(t,e)=>{const o=t.length-1,n=[];let i,a=2===e?"":3===e?"":"",r=Lt;for(let e=0;e"===l[0]?(r=i??Lt,d=-1):void 0===l[1]?d=-2:(d=r.lastIndex-l[2].length,s=l[1],r=void 0===l[3]?At:'"'===l[3]?Ct:Ot):r===Ct||r===Ot?r=At:r===Vt||r===Mt?r=Lt:(r=At,i=void 0);const c=r===At&&t[e+1].startsWith("/>")?" ":"";a+=r===Lt?o+ft:d>=0?(n.push(s),o.slice(0,d)+vt+o.slice(d)+mt+c):o+mt+(-2===d?e:c)}return[Dt(t,a+(t[o]||"")+(2===e?"":3===e?"":"")),n]};class qt{constructor({strings:t,_$litType$:e},o){let n;this.parts=[];let i=0,a=0;const r=t.length-1,s=this.parts,[l,d]=Ft(t,e);if(this.el=qt.createElement(l,o),Bt.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(n=Bt.nextNode())&&s.length0){n.textContent=pt?pt.emptyScript:"";for(let o=0;o2||""!==o[0]||""!==o[1]?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=Rt}_$AI(t,e=this,o,n){const i=this.strings;let a=!1;if(void 0===i)t=zt(this,t,e,0),a=!xt(t)||t!==this._$AH&&t!==Nt,a&&(this._$AH=t);else{const n=t;let r,s;for(t=i[0],r=0;r{const n=o?.renderBefore??e;let i=n._$litPart$;if(void 0===i){const t=o?.renderBefore??null;n._$litPart$=i=new Ut(e.insertBefore(wt(),t),t,void 0,o??{})}return i._$AI(t),i},Qt="m3u8-proxy.toil.cc",Xt="vot-worker.toil.cc",te="https://vot-api.toil.cc/v1",ee=.15,oe="yandex",ne="yandex",ie={yandex:"https://translate.toil.cc/detect",rustServer:"https://rust-server-531j.onrender.com/detect"},ae={yandex:"https://translate.toil.cc/translate",deepl:"https://translate-deepl.toil.cc/translate"};var re=o("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"),se=o.n(re),le=o("./node_modules/style-loader/dist/runtime/styleDomAPI.js"),de=o.n(le),ue=o("./node_modules/style-loader/dist/runtime/insertBySelector.js"),ce=o.n(ue),he=o("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js"),pe=o.n(he),ge=o("./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js"),ve=o.n(ge),me=o("./node_modules/style-loader/dist/runtime/styleTagTransform.js"),be=o.n(me),fe=o("./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss"),ye={};ye.styleTagTransform=be(),ye.setAttributes=pe(),ye.insert=ce().bind(null,"head"),ye.domAPI=de(),ye.insertStyleElement=ve();se()(fe.A,ye);fe.A&&fe.A.locals&&fe.A.locals;const we="#UNDEFINED",xe=_t` + +`;function Se(t){const e=document.createElement("vot-block");return e.classList.add("vot-icon-button"),Zt(t,e),e}function ke(t){const e=+t.value,o=+t.min,n=(e-o)/(+t.max-o);t.parentElement.setAttribute("style",`--vot-progress: ${n}`)}function Te(t,e="",o=" ",n=!1){const i=document.createElement("vot-block");i.classList.add("vot-textfield");const a=document.createElement(n?"textarea":"input");a.placeholder=o,a.value=e,t||a.classList.add("vot-show-placeholer");const r=document.createElement("span");return r.append(t),i.append(a,r),{container:i,input:a,label:r}}function Le(t){const e=document.createElement("vot-block");e.classList.add("vot-dialog-container"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-dialog-backdrop");const n=document.createElement("vot-block");n.classList.add("vot-dialog");const i=document.createElement("vot-block");i.classList.add("vot-dialog-content-wrapper");const a=document.createElement("vot-block");a.classList.add("vot-dialog-header-container");const r=document.createElement("vot-block");r.classList.add("vot-dialog-body-container");const s=document.createElement("vot-block");s.classList.add("vot-dialog-footer-container");const l=document.createElement("vot-block");l.classList.add("vot-dialog-title-container");const d=Se(_t` + + `);d.classList.add("vot-dialog-close-button"),o.onclick=d.onclick=()=>{e.hidden=!0};const u=document.createElement("vot-block");return u.classList.add("vot-dialog-title"),u.append(t),e.append(o,n),n.append(i),i.append(a,r,s),a.append(l,d),l.append(u),{container:e,backdrop:o,dialog:n,contentWrapper:i,headerContainer:a,bodyContainer:r,footerContainer:s,titleContainer:l,closeButton:d,title:u}}function Ve(t,e,o,n={}){const{onSelectCb:i=function(){},labelElement:a=""}=n;let r=[];const s=document.createElement("vot-block");s.classList.add("vot-select"),a&&s.append(a);const l=document.createElement("vot-block");l.classList.add("vot-select-outer");const d=document.createElement("span");d.classList.add("vot-select-title"),d.textContent=t,void 0===t&&(d.textContent=o.find((t=>!0===t.selected))?.label);const u=document.createElement("vot-block");u.classList.add("vot-select-arrow-icon"),Zt(xe,u),l.append(d,u),l.onclick=()=>{const t=Le(e);t.container.classList.add("vot-dialog-temp"),t.container.hidden=!1,document.documentElement.appendChild(t.container);const n=document.createElement("vot-block");n.classList.add("vot-select-content-list");for(const t of o){const e=document.createElement("vot-block");e.classList.add("vot-select-content-item"),e.textContent=t.label,e.dataset.votSelected=t.selected,e.dataset.votValue=t.value,t.disabled&&(e.inert=!0),e.onclick=async a=>{if(a.target.inert)return;const r=n.childNodes;for(let t of r)t.dataset.votSelected=!1;for(let e of o)e.selected=e.value===t.value;e.dataset.votSelected=!0,d.textContent=t.label,await i(a)},n.appendChild(e)}const a=Te(H.get("searchField"));a.input.oninput=t=>{const e=t.target.value.toLowerCase();for(let t=0;t{t.container.remove(),r=[]}},s.append(l);return{container:s,title:d,arrowIcon:u,labelElement:a,setTitle:t=>{d.textContent=t},setSelected:t=>{const e=Array.from(r).filter((t=>!t.inert));for(let o=0;o{o=t}}}const Me={createHeader:function(t,e=4){const o=document.createElement("vot-block");return o.classList.add("vot-header"),o.classList.add(`vot-header-level-${e}`),o.append(t),o},createInformation:function(t,e){const o=document.createElement("vot-block");o.classList.add("vot-info");const n=document.createElement("vot-block");n.append(t);const i=document.createElement("vot-block");return i.append(e),o.append(n,i),{container:o,header:n,value:i}},createButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-button"),e.append(t),e},createTextButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-text-button"),e.append(t),e},createOutlinedButton:function(t){const e=document.createElement("vot-block");return e.classList.add("vot-outlined-button"),e.append(t),e},createIconButton:Se,createCheckbox:function(t,e=!1){const o=document.createElement("label");o.classList.add("vot-checkbox");const n=document.createElement("input");n.type="checkbox",n.checked=Boolean(e);const i=document.createElement("span");return i.append(t),o.append(n,i),{container:o,input:n,label:i}},createSlider:function(t,e=50,o=0,n=100){const i=document.createElement("vot-block");i.classList.add("vot-slider");const a=document.createElement("input");a.type="range",a.min=o,a.max=n,a.value=e;const r=document.createElement("span");return Zt(t,r),i.append(a,r),a.addEventListener("input",(t=>ke(t.target))),ke(a),{container:i,input:a,label:r}},createTextfield:Te,createDialog:Le,createVOTButton:function(t){const e=document.createElement("vot-block");e.classList.add("vot-segmented-button");const o=document.createElement("vot-block");o.classList.add("vot-segment"),o.classList.add("vot-translate-button"),Zt(_t` + + + + `,o);const n=document.createElement("vot-block");n.classList.add("vot-separator");const i=document.createElement("vot-block");i.classList.add("vot-segment-only-icon"),Zt(_t` + + `,i);const a=document.createElement("vot-block");a.classList.add("vot-separator");const r=document.createElement("vot-block");r.classList.add("vot-segment-only-icon"),Zt(_t` + + `,r);const s=document.createElement("span");return s.classList.add("vot-segment-label"),s.append(t),e.append(o,n,i,a,r),o.append(s),{container:e,translateButton:o,separator:n,pipButton:i,separator2:a,menuButton:r,label:s}},createVOTMenu:function(t){const e=document.createElement("vot-block");e.classList.add("vot-menu"),e.hidden=!0;const o=document.createElement("vot-block");o.classList.add("vot-menu-content-wrapper");const n=document.createElement("vot-block");n.classList.add("vot-menu-header-container");const i=document.createElement("vot-block");i.classList.add("vot-menu-body-container");const a=document.createElement("vot-block");a.classList.add("vot-menu-footer-container");const r=document.createElement("vot-block");r.classList.add("vot-menu-title-container");const s=document.createElement("vot-block");return s.classList.add("vot-menu-title"),s.append(t),e.append(o),o.append(n,i,a),n.append(r),r.append(s),{container:e,contentWrapper:o,headerContainer:n,bodyContainer:i,footerContainer:a,titleContainer:r,title:s}},createVOTSelectLabel:function(t){const e=document.createElement("span");return e.classList.add("vot-select-label"),e.textContent=t,e},createVOTSelect:Ve,createVOTLanguageSelect:function(t){const{fromTitle:e=we,fromDialogTitle:o=we,fromItems:n=[],fromOnSelectCB:i=null,toTitle:a=we,toDialogTitle:r=we,toItems:s=[],toOnSelectCB:l=null}=t,d=document.createElement("vot-block");d.classList.add("vot-lang-select");const u=Ve(e,o,n,{onSelectCb:i}),c=document.createElement("vot-block");c.classList.add("vot-lang-select-icon"),Zt(_t` + + `,c);const h=Ve(a,r,s,{onSelectCb:l});return d.append(u.container,c,h.container),{container:d,fromSelect:u,icon:c,toSelect:h}},updateSlider:ke,createDetails:function(t){const e=document.createElement("vot-block");e.classList.add("vot-details");const o=document.createElement("vot-block");o.append(t);const n=document.createElement("vot-block");return n.classList.add("vot-details-arrow-icon"),Zt(xe,n),e.append(o,n),{container:e,header:o,arrowIcon:n}}};const Ae={async translate(t,e){try{const o=await q(`${ae.yandex}?${new URLSearchParams({text:t,lang:e})}`,{timeout:3e3});if(o instanceof Error)throw o;const n=await o.json();if(200!==n.code)throw n.message;return n.text[0]}catch(e){return console.error("Error translating text:",e),t}},async detect(t){try{const e=await q(`${ie.yandex}?${new URLSearchParams({text:t})}`,{timeout:3e3});if(e instanceof Error)throw e;const o=await e.json();if(200!==o.code)throw o.message;return o.lang??"en"}catch(t){return console.error("Error getting lang from text:",t),"en"}}},Oe={async detect(t){try{const e=await q(ie.rustServer,{method:"POST",body:t});if(e instanceof Error)throw e;return await e.text()}catch(t){return console.error("Error getting lang from text:",t),"en"}}},Ce={async translate(t,e="auto",o="ru"){try{const n=await q(ae.deepl,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({text:t,source_lang:e,target_lang:o})});if(n instanceof Error)throw n;const i=await n.json();if(200!==i.code)throw i.message;return i.data}catch(e){return console.error("Error translating text:",e),t}}};const Pe=Object.keys(ae),Ee=Object.keys(ie).map((t=>"rustServer"===t?"rust-server":t));async function Ie(t,e,o,n){if(!window.location.hostname.includes("m.youtube.com")&&t?.getAudioTrack){const e=t.getAudioTrack(),o=e?.getLanguageInfo();if("und"!==o?.id)return $(o.id.split(".")[0])}const i=e?.captions?.playerCaptionsTracklistRenderer?.captionTracks;if(i?.length){const t=i.find((t=>"asr"===t.kind));if(t&&t.languageCode)return $(t.languageCode)}const a=function(t,e){return`${t} ${e?e.split("\n").filter((t=>!F.test(t))).join(" "):""}`.slice(0,450).replace(/[^\p{L}\s]+|\s+/gu," ").trim()}(o,n);return I.log(`Detecting language text: ${a}`),async function(t){switch(await _.get("detectService",ne)){case"yandex":return await Ae.detect(t);case"rust-server":return await Oe.detect(t);default:return"en"}}(a)}function _e(){return/^m\.youtube\.com$/.test(window.location.hostname)}function Ne(){return window.location.pathname.startsWith("/shorts/")?_e()?document.querySelector("#movie_player"):document.querySelector("#shorts-player"):document.querySelector("#movie_player")}function Re(){const t=Ne();return t?.getPlayerResponse?t?.getPlayerResponse?.call()??null:t?.data?.playerResponse??null}function $e(){const t=Ne();return t?.getVideoData?t?.getVideoData?.call()??null:t?.data?.playerResponse?.videoDetails??null}const Be={isMobile:_e,getPlayer:Ne,getPlayerResponse:Re,getPlayerData:$e,getVideoVolume:function(){const t=Ne();return t?.getVolume?t.getVolume.call()/100:1},getSubtitles:function(){const t=Re();let e=t?.captions?.playerCaptionsTracklistRenderer?.captionTracks??[];return e=e.reduce(((t,e)=>{if("languageCode"in e){const o=e?.languageCode?$(e?.languageCode):void 0,n=e?.url||e?.baseUrl;o&&n&&t.push({source:"youtube",language:o,isAutoGenerated:"asr"===e?.kind,url:`${n.startsWith("http")?n:`${window.location.origin}/${n}`}&fmt=json3`})}return t}),[]),I.log("youtube subtitles:",e),e},getVideoData:async function(){const t=Ne(),e=Re(),o=$e(),{title:n}=o??{},{shortDescription:i,isLive:a}=e?.videoDetails??{};let r=n?await Ie(t,e,n,i):"en";r=O.includes(r)?r:"en";const s={isLive:!!a,title:n,description:i,detectedLanguage:r};return I.log("youtube video data:",s),console.log("[VOT] Detected language: ",s.detectedLanguage),s},setVideoVolume:function(t){const e=Ne();if(e?.setVolume)return e.setVolume(Math.round(100*t)),!0},videoSeek:function(t,e){I.log("videoSeek",e);const o=(Ne()?.getProgressState()?.seekableEnd||t.currentTime)-e;t.currentTime=o},isMuted:function(){const t=Ne();return!!t?.isMuted&&t.isMuted.call()},isMusic:function(){const t=$e().author,e=$e().title.toUpperCase(),o=e.match(/\w+/g),n=document.body.querySelector("ytd-watch-flexy")?.playerData;return[e,document.URL,t,n?.microformat?.playerMicroformatRenderer.category,n?.title].some((t=>t?.toUpperCase().includes("MUSIC")))||document.body.querySelector("#upload-info #channel-name .badge-style-type-verified-artist")||t&&/(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(t)||t&&/(MUSIC|ROCK|SOUNDS|SONGS)/.test(t.toUpperCase())||o?.length&&["🎵","♫","SONG","SONGS","SOUNDTRACK","LYRIC","LYRICS","AMBIENT","MIX","VEVO","CLIP","KARAOKE","OPENING","COVER","COVERED","VOCAL","INSTRUMENTAL","ORCHESTRAL","DUBSTEP","DJ","DNB","BASS","BEAT","ALBUM","PLAYLIST","DUBSTEP","CHILL","RELAX","CLASSIC","CINEMATIC"].some((t=>o.includes(t)))||["OFFICIAL VIDEO","OFFICIAL AUDIO","FEAT.","FT.","LIVE RADIO","DANCE VER","HIP HOP","ROCK N ROLL","HOUR VER","HOURS VER","INTRO THEME"].some((t=>e.includes(t)))||o?.length&&["OP","ED","MV","OST","NCS","BGM","EDM","GMV","AMV","MMD","MAD"].some((t=>o.includes(t)))}};function De(t){const e=t.startMs+t.durationMs;return t.tokens.reduce(((o,n,i)=>{const a=t.tokens[i+1];let r;o.length>0&&(r=o[o.length-1]);const s=r?.alignRange?.end??0,l=s+n.text.length;if(n.alignRange={start:s,end:l},o.push(n),a){const t=n.startMs+n.durationMs,i=a.startMs?a.startMs-t:e-t;o.push({text:" ",startMs:t,durationMs:i,alignRange:{start:l,end:l+1}})}return o}),[])}function Fe(t,e){const o=t.text.split(/([\n \t])/).reduce(((t,o)=>{if(o.length){const n=t[t.length-1]??e,i=n?.alignRange?.end??0,a=i+o.length;t.push({text:o,alignRange:{start:i,end:a}})}return t}),[]),n=Math.floor(t.durationMs/o.length),i=t.startMs+t.durationMs;return o.map(((e,a)=>{const r=a===o.length-1,s=t.startMs+n*a;return{...e,startMs:s,durationMs:r?i-s:n}}))}async function qe(t){const e=(async()=>{try{const e=await q(t.url,{timeout:5e3});return await e.json()}catch(t){return console.error("[VOT] Failed to fetch subtitles.",t),{containsTokens:!1,subtitles:[]}}})();let o=await e;return"youtube"===t.source&&(o=function(t){const e={containsTokens:!1,subtitles:[]};if("object"!=typeof t||!("events"in t)||!Array.isArray(t.events))return console.error("[VOT] Failed to format youtube subtitles",t),e;for(let o=0;ot.utf8.replace(/^( +| +)$/g,""))).join("");let i=t.events[o].dDurationMs;t.events[o+1]&&t.events[o].tStartMs+t.events[o].dDurationMs>t.events[o+1].tStartMs&&(i=t.events[o+1].tStartMs-t.events[o].tStartMs),"\n"!==n&&e.subtitles.push({text:n,startMs:t.events[o].tStartMs,durationMs:i})}return e}(o)),o.subtitles=function(t,e){const o=[];let n;for(let i=0;isetTimeout((()=>e(new Error("Timeout"))),5e3)));try{const e=await Promise.race([t.getSubtitles({videoData:{host:o,url:n,videoId:a,duration:r},requestLang:i}),l]);console.log("[VOT] Subtitles response: ",e),e.waiting&&console.error("[VOT] Failed to get yandex subtitles");let d=e.subtitles??[];return d=d.reduce(((t,e)=>(e.language&&!t.find((t=>"yandex"===t.source&&t.language===e.language&&!t.translatedFromLanguage))&&t.push({source:"yandex",language:e.language,url:e.url}),e.translatedLanguage&&t.push({source:"yandex",language:e.translatedLanguage,translatedFromLanguage:e.language,url:e.translatedUrl}),t)),[]),[...d,...s].sort(((t,e)=>{if(t.source!==e.source)return"yandex"===t.source?-1:1;if(t.language!==e.language&&(t.language===R||e.language===R))return t.language===R?-1:1;if("yandex"===t.source){if(t.translatedFromLanguage!==e.translatedFromLanguage)return t.translatedFromLanguage&&e.translatedFromLanguage?t.translatedFromLanguage===i?-1:1:t.language===e.language?t.translatedFromLanguage?1:-1:t.translatedFromLanguage?-1:1;if(!t.translatedFromLanguage)return t.language===i?-1:1}return"youtube"===t.source&&t.isAutoGenerated!==e.isAutoGenerated?t.isAutoGenerated?1:-1:0}))}catch(t){throw"Timeout"===t.message?console.error("[VOT] Failed to get yandex subtitles. Reason: timeout"):console.error("[VOT] Error in getSubtitles function",t),t}}class He{constructor(t,e,o){this.video=t,this.container="youtube"===o.host&&"mobile"!==o.additionalData?e.parentElement:e,this.site=o,this.subtitlesContainer=this.createSubtitlesContainer(),this.position={left:25,top:75},this.dragging={active:!1,offset:{x:0,y:0}},this.subtitles=null,this.lastContent=null,this.highlightWords=!1,this.fontSize=20,this.opacity=.2,this.maxLength=300,this.maxLengthRegexp=/.{1,300}(?:\s|$)/g,this.bindEvents(),this.updateContainerRect(),this.applySubtitlePosition()}createSubtitlesContainer(){const t=document.createElement("vot-block");return t.classList.add("vot-subtitles-widget"),this.container.appendChild(t),t}bindEvents(){this.onMouseDownBound=this.onMouseDown.bind(this),this.onMouseUpBound=this.onMouseUp.bind(this),this.onMouseMoveBound=this.onMouseMove.bind(this),this.onTimeUpdateBound=this.debounce(this.update.bind(this),100),document.addEventListener("mousedown",this.onMouseDownBound),document.addEventListener("mouseup",this.onMouseUpBound),document.addEventListener("mousemove",this.onMouseMoveBound),this.video?.addEventListener("timeupdate",this.onTimeUpdateBound),this.resizeObserver=new ResizeObserver(this.onResize.bind(this)),this.resizeObserver.observe(this.container)}onMouseDown(t){if(this.subtitlesContainer.contains(t.target)){const e=this.subtitlesContainer.getBoundingClientRect(),o=this.container.getBoundingClientRect();this.dragging={active:!0,offset:{x:t.clientX-e.left,y:t.clientY-e.top},containerOffset:{x:o.left,y:o.top}}}}onMouseUp(){this.dragging.active=!1}onMouseMove(t){if(this.dragging.active){t.preventDefault();const{width:e,height:o}=this.container.getBoundingClientRect(),n=this.dragging.containerOffset;this.position={left:(t.clientX-this.dragging.offset.x-n.x)/e*100,top:(t.clientY-this.dragging.offset.y-n.y)/o*100},this.applySubtitlePosition()}}onResize(){this.updateContainerRect()}updateContainerRect(){this.containerRect=this.container.getBoundingClientRect(),this.applySubtitlePosition()}applySubtitlePosition(){const{width:t,height:e}=this.containerRect,{offsetWidth:o,offsetHeight:n}=this.subtitlesContainer,i=(t-o)/t*100,a=(e-n)/e*100;this.position.left=Math.max(0,Math.min(this.position.left,i)),this.position.top=Math.max(0,Math.min(this.position.top,a)),this.subtitlesContainer.style.left=`${this.position.left}%`,this.subtitlesContainer.style.top=`${this.position.top}%`}setContent(t){t&&this.video?(this.subtitles=t,this.update()):(this.subtitles=null,Zt(null,this.subtitlesContainer))}setMaxLength(t){"number"==typeof t&&t&&(this.maxLength=t,this.maxLengthRegexp=new RegExp(`.{1,${t}}(?:\\s|$)`,"g"),this.update())}setHighlightWords(t){this.highlightWords=Boolean(t),this.update()}setFontSize(t){this.fontSize=t;const e=this.subtitlesContainer?.querySelector(".vot-subtitles");e&&(e.style.fontSize=`${this.fontSize}px`)}setOpacity(t){this.opacity=((100-+t)/100).toFixed(2);const e=this.subtitlesContainer?.querySelector(".vot-subtitles");e&&e.style.setProperty("--vot-subtitles-opacity",this.opacity)}update(){if(!this.video||!this.subtitles)return;const t=1e3*this.video.currentTime,e=this.subtitles.subtitles?.findLast((e=>e.startMs${n}`,this.subtitlesContainer))}processTokens(t){if(t.at(-1).alignRange.end<=this.maxLength)return t;let e=[],o=[],n=0;for(const i of t)n+=i.text.length,o.push(i),n>this.maxLength&&(e.push(this.trimChunk(o)),o=[],n=0);o.length&&e.push(this.trimChunk(o));const i=1e3*this.video.currentTime;return e.find((t=>t[0].startMs{const o=this.highlightWords&&(e>t.startMs+t.durationMs/2||e>t.startMs-100&&t.startMs+t.durationMs/2-e<275);return It`${t.text.replace("\\n","
")}
`}))}debounce(t,e){let o;return(...n)=>{clearTimeout(o),o=setTimeout((()=>t.apply(this,n)),e)}}release(){document.removeEventListener("mousedown",this.onMouseDownBound),document.removeEventListener("mouseup",this.onMouseUpBound),document.removeEventListener("mousemove",this.onMouseMoveBound),this.video?.removeEventListener("timeupdate",this.onTimeUpdateBound),this.resizeObserver.disconnect(),this.subtitlesContainer.remove()}}o("./node_modules/requestidlecallback-polyfill/index.js");class Ue{constructor(){this.listeners=new Set}hasListener(t){return this.listeners.has(t)}dispatchToListener(t,...e){try{t(...e)}catch(t){console.error("[VOT]",t)}}addListener(t){if(this.hasListener(t))throw new Error("[VOT] The listener has already been added.");this.listeners.add(t)}removeListener(t){if(!this.hasListener(t))throw new Error("[VOT] The listener has not been added yet.");this.listeners.delete(t)}dispatch(...t){for(const e of Array.from(this.listeners))this.dispatchToListener(e,...t)}}function je(t){return Array.from(t).flatMap((t=>t instanceof HTMLVideoElement?[t]:t instanceof HTMLElement?Array.from(t.querySelectorAll("video")):t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll("video")):[]))}const We=/advertise|promo|sponsor|banner|commercial|preroll|midroll|postroll|ad-container|sponsored/i;const Ge=t.getParser(window.navigator.userAgent).getResult(),Je=["Violentmonkey","FireMonkey","Greasemonkey","AdGuard","OrangeMonkey"],Ye=["playing","ratechange","play","waiting","pause"];function Ke(t,e){return t.map((t=>({label:H.get("langs")[t]??t.toUpperCase(),value:t,selected:e===t})))}class Ze{translateFromLang="en";translateToLang=R;timer;videoData="";firstPlay=!0;audio=new Audio;audioContext=new(window.AudioContext||window.webkitAudioContext);gainNode=this.audioContext.createGain();hls=D();votClient;videoTranslations=[];videoTranslationTTL=7200;cachedTranslation;downloadTranslationUrl=null;downloadSubtitlesUrl=null;autoRetry;streamPing;votOpts;volumeOnStart;tempOriginalVolume;tempVolume;firstSyncVolume=!0;subtitlesList=[];subtitlesListVideoId=null;videoLastSrcObject=null;dragging;constructor(t,e,o){I.log("[VideoHandler] add video:",t,"container:",e,this),this.video=t,this.container=e,this.site=o,this.stopTranslationBound=this.stopTranslation.bind(this),this.handleVideoEventBound=this.handleVideoEvent.bind(this),this.changeOpacityOnEventBound=this.changeOpacityOnEvent.bind(this),this.resetTimerBound=this.resetTimer.bind(this),this.init()}async translateVideoImpl(t,e,o,n=null){if(clearTimeout(this.autoRetry),I.log(t,`Translate video (requestLang: ${e}, responseLang: ${o})`),await it(this.site,this.video)!==t.videoId)return null;try{const i=await this.votClient.translateVideo({videoData:t,requestLang:e,responseLang:o,translationHelp:n});if(I.log("Translate video result",i),i.translated&&i.remainingTime<1)return I.log("Video translation finished with this data: ",i),i;await this.updateTranslationErrorMsg(i.remainingTime>0?function(t){const e=Math.floor(t/60),o=Math.floor(t%60);return e>=60?H.get("translationTakeMoreThanHour"):1===e||0===e&&o>0?H.get("translationTakeAboutMinute"):11!==e&&e%10==1?H.get("translationTakeApproximatelyMinute2").replace("{0}",e):![12,13,14].includes(e)&&[2,3,4].includes(e%10)?H.get("translationTakeApproximatelyMinute").replace("{0}",e):H.get("translationTakeApproximatelyMinutes").replace("{0}",e)}(i.remainingTime):i.message??H.get("translationTakeFewMinutes"))}catch(t){return console.error("[VOT] Failed to translate video",t),await this.updateTranslationErrorMsg(t.data?.message??t),null}return new Promise((i=>{const a=this.subtitlesList.some((t=>"yandex"===t.source))?2e4:3e4;this.autoRetry=setTimeout((async()=>{const a=await this.translateVideoImpl(t,e,o,n);(!a||a.translated&&a.remainingTime<1)&&i(a)}),a)}))}async translateStreamImpl(t,e,o){if(clearTimeout(this.autoRetry),I.log(t,`Translate stream (requestLang: ${e}, responseLang: ${o})`),await it(this.site,this.video)!==t.videoId)return null;try{const n=await this.votClient.translateStream({videoData:t,requestLang:e,responseLang:o});if(I.log("Translate stream result",n),!n.translated&&10===n.interval)return await this.updateTranslationErrorMsg(H.get("translationTakeFewMinutes")),new Promise((i=>{this.autoRetry=setTimeout((async()=>{const n=await this.translateStreamImpl(t,e,o);n&&!n.translated&&10===n.interval||i(n)}),1e3*n.interval)}));if(n.message)throw I.log(`Stream translation aborted! Message: ${n.message}`),new rt("streamNoConnectionToServer");if(!n.result)throw I.log("Failed to find translation result! Data:",n),new rt("audioNotReceived");return I.log("Stream translated successfully. Running...",n),this.streamPing=setInterval((async()=>{I.log("Ping stream translation",n.pingId),this.votClient.pingStream({pingId:n.pingId})}),1e3*n.interval),n}catch(t){return console.error("[VOT] Failed to translate stream",t),await this.updateTranslationErrorMsg(t.data?.message??t),null}}async autoTranslate(){if(this.site.host,this.firstPlay&&1===this.data.autoTranslate&&this.videoData.videoId){this.firstPlay=!1;try{await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),this.transformBtn("error","VOTLocalizedError"===t?.name?t.localizedMessage:t)}}}async init(){if(this.initialized)return;const t={autoTranslate:_.get("autoTranslate",0,!0),dontTranslateLanguage:_.get("dontTranslateLanguage",R),dontTranslateYourLang:_.get("dontTranslateYourLang",1,!0),autoSetVolumeYandexStyle:_.get("autoSetVolumeYandexStyle",1,!0),autoVolume:_.get("autoVolume",ee,!0),buttonPos:_.get("buttonPos","default"),showVideoSlider:_.get("showVideoSlider",1,!0),syncVolume:_.get("syncVolume",0,!0),subtitlesMaxLength:_.get("subtitlesMaxLength",300,!0),highlightWords:_.get("highlightWords",0,!0),subtitlesFontSize:_.get("subtitlesFontSize",20,!0),subtitlesOpacity:_.get("subtitlesOpacity",20,!0),responseLanguage:_.get("responseLanguage",R),defaultVolume:_.get("defaultVolume",100,!0),audioProxy:_.get("audioProxy",0,!0),showPiPButton:_.get("showPiPButton",0,!0),translateAPIErrors:_.get("translateAPIErrors",1,!0),translationService:_.get("translationService",oe),detectService:_.get("detectService",ne),m3u8ProxyHost:_.get("m3u8ProxyHost",Qt),translateProxyEnabled:_.get("translateProxyEnabled",0,!0),proxyWorkerHost:_.get("proxyWorkerHost",Xt),audioBooster:_.get("audioBooster",0,!0)};this.data=Object.fromEntries(await Promise.all(Object.entries(t).map((async([t,e])=>[t,await e])))),console.log("[db] data from db: ",this.data),0===this.data.translateProxyEnabled&&GM_info?.scriptHandler&&Je.includes(GM_info.scriptHandler)&&(this.data.translateProxyEnabled=1,await _.set("translateProxyEnabled",1),I.log("translateProxyEnabled",this.data.translateProxyEnabled)),I.log("Extension compatibility passed..."),this.votOpts={headers:1===this.data.translateProxyEnabled?{}:{"sec-ch-ua":null,"sec-ch-ua-mobile":null,"sec-ch-ua-platform":null},fetchFn:q,hostVOT:te,host:1===this.data.translateProxyEnabled?this.data.proxyWorkerHost:"api.browser.yandex.ru"},this.votClient=new(1===this.data.translateProxyEnabled?ut:dt)(this.votOpts),this.subtitlesWidget=new He(this.video,this.container,this.site),this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength),this.subtitlesWidget.setHighlightWords(this.data.highlightWords),this.subtitlesWidget.setFontSize(this.data.subtitlesFontSize),this.subtitlesWidget.setOpacity(this.data.subtitlesOpacity),this.audio.crossOrigin="anonymous",this.gainNode.connect(this.audioContext.destination),this.audioSource=this.audioContext.createMediaElementSource(this.audio),this.audioSource.connect(this.gainNode),this.initUI(),this.initUIEvents();const e=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=e,e?this.votMenu.container.hidden=!0:(this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage??"ru")),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),await this.autoTranslate(),this.translateToLang=this.data.responseLanguage??"ru",this.initExtraEvents(),this.initialized=!0}transformBtn(t="none",e){this.votButton.container.dataset.status=t;const o="error"===t&&e.includes(H.get("translationTake"));this.setLoadingBtn(o),this.votButton.label.textContent=e,this.votButton.container.title="error"===t?e:""}setLoadingBtn(t=!1){this.votButton.container.dataset.loading=t}initUI(){this.votButton=Me.createVOTButton(H.get("translateVideo")),this.data?.buttonPos&&"default"!==this.data?.buttonPos&&this.container.clientWidth&&this.container.clientWidth>550?(this.votButton.container.dataset.direction="column",this.votButton.container.dataset.position=this.data?.buttonPos):(this.votButton.container.dataset.direction="row",this.votButton.container.dataset.position="default"),this.container.appendChild(this.votButton.container),this.votButton.pipButton.hidden=!B()||!this.data?.showPiPButton,this.votButton.separator2.hidden=!B()||!this.data?.showPiPButton,this.votButton.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votMenu=Me.createVOTMenu(H.get("VOTSettings")),this.votMenu.container.dataset.position=this.container.clientWidth&&this.container.clientWidth>550?this.data?.buttonPos:"default",this.container.appendChild(this.votMenu.container),this.votDownloadButton=Me.createIconButton(_t` + + `),this.votDownloadButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadButton),this.votDownloadSubtitlesButton=Me.createIconButton(_t` + + `),this.votDownloadSubtitlesButton.hidden=!0,this.votMenu.headerContainer.appendChild(this.votDownloadSubtitlesButton),this.votSettingsButton=Me.createIconButton(_t` + + `),this.votMenu.headerContainer.appendChild(this.votSettingsButton),this.votTranslationLanguageSelect=Me.createVOTLanguageSelect({fromTitle:H.get("langs")[this.video.detectedLanguage],fromDialogTitle:H.get("videoLanguage"),fromItems:Ke(O,this.videoData.detectedLanguage),fromOnSelectCB:async t=>{I.log("[fromOnSelectCB] select from language",t.target.dataset.votValue),this.videoData=await this.getVideoData(),this.setSelectMenuValues(t.target.dataset.votValue,this.videoData.responseLanguage)},toTitle:H.get("langs")[this.video.responseLanguage],toDialogTitle:H.get("translationLanguage"),toItems:Ke(C,this.videoData.responseLanguage),toOnSelectCB:async t=>{const e=t.target.dataset.votValue;I.log("[toOnSelectCB] select to language",e),this.data.responseLanguage=this.translateToLang=e,await _.set("responseLanguage",this.data.responseLanguage),I.log("Response Language value changed. New value: ",this.data.responseLanguage),this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.data.responseLanguage)}}),this.votMenu.bodyContainer.appendChild(this.votTranslationLanguageSelect.container),this.votSubtitlesSelect=Me.createVOTSelect(H.get("VOTSubtitlesDisabled"),H.get("VOTSubtitles"),[{label:H.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1}],{onSelectCb:async t=>{await this.changeSubtitlesLang(t.target.dataset.votValue)},labelElement:Me.createVOTSelectLabel(H.get("VOTSubtitles"))}),this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container),this.votVideoVolumeSlider=Me.createSlider(It`${H.get("VOTVolume")}: + ${100*this.getVideoVolume()}%`,100*this.getVideoVolume()),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoVolumeSlider.container),this.votVideoTranslationVolumeSlider=Me.createSlider(It`${H.get("VOTVolumeTranslation")}: + ${this.data?.defaultVolume??100}%`,this.data?.defaultVolume??100,0,this.data.audioBooster?900:100),this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,this.votMenu.bodyContainer.appendChild(this.votVideoTranslationVolumeSlider.container),this.votMenu.container.addEventListener("click",(t=>{t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()})),this.votSettingsDialog=Me.createDialog(H.get("VOTSettings")),document.documentElement.appendChild(this.votSettingsDialog.container),this.votTranslationHeader=Me.createHeader(H.get("translationSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationHeader),this.votAutoTranslateCheckbox=Me.createCheckbox(H.get("VOTAutoTranslate"),this.data?.autoTranslate??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoTranslateCheckbox.container),this.votDontTranslateYourLangSelect=Me.createVOTSelect(H.get("langs")[_.syncGet("dontTranslateLanguage",R)],H.get("VOTDontTranslateYourLang"),Ke(O,_.syncGet("dontTranslateLanguage",R)),{onSelectCb:async t=>{this.data.dontTranslateLanguage=t.target.dataset.votValue,await _.set("dontTranslateLanguage",this.data.dontTranslateLanguage)},labelElement:Me.createCheckbox(H.get("VOTDontTranslateYourLang"),this.data?.dontTranslateYourLang??!0).container}),this.votSettingsDialog.bodyContainer.appendChild(this.votDontTranslateYourLangSelect.container),this.votAutoSetVolumeCheckbox=Me.createCheckbox(`${H.get("VOTAutoSetVolume")}`,this.data?.autoSetVolumeYandexStyle??!0),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeCheckbox.container),this.votAutoSetVolumeSlider=Me.createSlider(It`${100*(this.data?.autoVolume??ee)}%`,100*(this.data?.autoVolume??ee),0,100),this.votSettingsDialog.bodyContainer.appendChild(this.votAutoSetVolumeSlider.container),this.votShowVideoSliderCheckbox=Me.createCheckbox(H.get("VOTShowVideoSlider"),this.data?.showVideoSlider??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votShowVideoSliderCheckbox.container),this.votAudioBoosterCheckbox=Me.createCheckbox(H.get("VOTAudioBooster"),this.data?.audioBooster??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAudioBoosterCheckbox.container),this.votSyncVolumeCheckbox=Me.createCheckbox(H.get("VOTSyncVolume"),this.data?.syncVolume??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votSyncVolumeCheckbox.container),this.votTranslationServiceSelect=Me.createVOTSelect(_.syncGet("translationService",oe).toUpperCase(),H.get("VOTTranslationService"),Ke(Pe,_.syncGet("translationService",oe)),{onSelectCb:async t=>{this.data.translationService=t.target.dataset.votValue,await _.set("translationService",this.data.translationService)},labelElement:Me.createCheckbox(H.get("VOTTranslateAPIErrors"),this.data.translateAPIErrors??!0).container}),this.votTranslationServiceSelect.container.hidden="ru"===H.lang,this.votSettingsDialog.bodyContainer.appendChild(this.votTranslationServiceSelect.container),this.votDetectServiceSelect=Me.createVOTSelect(_.syncGet("detectService",ne).toUpperCase(),H.get("VOTDetectService"),Ke(Ee,_.syncGet("detectService",ne)),{onSelectCb:async t=>{this.data.detectService=t.target.dataset.votValue,await _.set("detectService",this.data.detectService)},labelElement:Me.createVOTSelectLabel(H.get("VOTDetectService"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votDetectServiceSelect.container),this.votSubtitlesHeader=Me.createHeader(H.get("subtitlesSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader),this.votSubtitlesDetails=Me.createDetails(H.get("VOTSubtitlesDesign")),this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesDetails.container),this.votProxyHeader=Me.createHeader(H.get("proxySettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votProxyHeader),this.votM3u8ProxyHostTextfield=Me.createTextfield(H.get("VOTM3u8ProxyHost"),this.data?.m3u8ProxyHost,Qt),this.votSettingsDialog.bodyContainer.appendChild(this.votM3u8ProxyHostTextfield.container),this.votProxyWorkerHostTextfield=Me.createTextfield(H.get("VOTProxyWorkerHost"),this.data?.proxyWorkerHost,Xt),this.votSettingsDialog.bodyContainer.appendChild(this.votProxyWorkerHostTextfield.container),this.votAudioProxyCheckbox=Me.createCheckbox(H.get("VOTAudioProxy"),this.data?.audioProxy??!1),this.votSettingsDialog.bodyContainer.appendChild(this.votAudioProxyCheckbox.container),this.votAboutHeader=Me.createHeader(H.get("about")),this.votSettingsDialog.bodyContainer.appendChild(this.votAboutHeader),this.votLanguageSelect=Me.createVOTSelect(H.get("langs")[_.syncGet("locale-lang-override","auto")],H.get("VOTMenuLanguage"),Ke(z,_.syncGet("locale-lang-override","auto")),{onSelectCb:async t=>{await _.set("locale-lang-override",t.target.dataset.votValue)},labelElement:Me.createVOTSelectLabel(H.get("VOTMenuLanguage"))}),this.votSettingsDialog.bodyContainer.appendChild(this.votLanguageSelect.container),this.votShowPiPButtonCheckbox=Me.createCheckbox(H.get("VOTShowPiPButton"),this.data?.showPiPButton??!1),this.votShowPiPButtonCheckbox.container.hidden=!B(),this.votSettingsDialog.bodyContainer.appendChild(this.votShowPiPButtonCheckbox.container),this.votVersionInfo=Me.createInformation(`${H.get("VOTVersion")}:`,GM_info.script.version),this.votSettingsDialog.bodyContainer.appendChild(this.votVersionInfo.container),this.votAuthorsInfo=Me.createInformation(`${H.get("VOTAuthors")}:`,GM_info.script.author),this.votSettingsDialog.bodyContainer.appendChild(this.votAuthorsInfo.container),this.votLoaderInfo=Me.createInformation(`${H.get("VOTLoader")}:`,`${GM_info.scriptHandler} v${GM_info.version}`),this.votSettingsDialog.bodyContainer.appendChild(this.votLoaderInfo.container),this.votBrowserInfo=Me.createInformation(`${H.get("VOTBrowser")}:`,`${Ge.browser.name} ${Ge.browser.version} (${Ge.os.name} ${Ge.os.version})`),this.votSettingsDialog.bodyContainer.appendChild(this.votBrowserInfo.container),this.votResetSettingsButton=Me.createButton(H.get("resetSettings")),this.votSettingsDialog.bodyContainer.appendChild(this.votResetSettingsButton)}initUIEvents(){this.votButton.translateButton.addEventListener("click",(()=>{(async()=>{if(this.audio.src)return I.log("[click translationBtn] audio.src is not empty"),void this.stopTranslate();if(this.hls.url)return I.log("[click translationBtn] hls is not empty"),void this.stopTranslate();try{if(I.log("[click translationBtn] trying execute translation"),!this.videoData.videoId)throw new rt("VOTNoVideoIDFound");"vk"===this.site.host&&"clips"===this.site.additionalData&&(this.videoData=await this.getVideoData()),await this.translateExecutor(this.videoData.videoId)}catch(t){console.error("[VOT]",t),"VOTLocalizedError"===t?.name?this.transformBtn("error",t.localizedMessage):this.transformBtn("error",t)}})()})),this.votButton.pipButton.addEventListener("click",(()=>{(async()=>{this.video!==document.pictureInPictureElement?await this.video.requestPictureInPicture():await document.exitPictureInPicture()})()})),this.votButton.menuButton.addEventListener("click",(()=>{this.votMenu.container.hidden=!this.votMenu.container.hidden})),this.votButton.container.addEventListener("mousedown",(()=>{this.dragging=!0})),this.container.addEventListener("mouseup",(()=>{this.dragging=!1})),this.container.addEventListener("mousemove",(async t=>{if(this.dragging){t.preventDefault();const e=t.clientX/this.container.clientWidth*100;this.data.buttonPos=this.container.clientWidth&&this.container.clientWidth>550?e<=44?"left":e>=66?"right":"default":"default",this.votButton.container.dataset.direction="default"===this.data.buttonPos?"row":"column",this.votButton.container.dataset.position=this.data.buttonPos,this.votMenu.container.dataset.position=this.data.buttonPos,this.container.clientWidth&&this.container.clientWidth>550&&await _.set("buttonPos",this.data.buttonPos)}})),this.votDownloadButton.addEventListener("click",(()=>{this.downloadTranslationUrl&&window.open(this.downloadTranslationUrl,"_blank").focus()})),this.votDownloadSubtitlesButton.addEventListener("click",(async()=>{const t=function(t,e="srt"){const o=t.subtitles.map(((t,o)=>{const n=t.startMs/1e3,i=(t.startMs+t.durationMs)/1e3;return("srt"===e?`${o+1}\n`:"")+`${ct(n)} --\x3e ${ct(i)}\n${t.text}\n\n`})).join("").trim();return"vtt"===e?`WEBVTT\n\n${o}`:o}(this.yandexSubtitles,"srt"),e=new Blob([t],{type:"text/plain"}),o=URL.createObjectURL(e),n=document.createElement("a");n.href=o,n.download=`subtitles_${this.videoData.videoId}.srt`,n.click(),URL.revokeObjectURL(o)})),this.votSettingsButton.addEventListener("click",(()=>{this.votSettingsDialog.container.hidden=!this.votSettingsDialog.container.hidden,(document.fullscreenElement||document.webkitFullscreenElement)&&(document.webkitExitFullscreen&&document.webkitExitFullscreen(),document.exitFullscreen&&document.exitFullscreen())})),this.votVideoVolumeSlider.input.addEventListener("input",(t=>{const e=Number(t.target.value);this.votVideoVolumeSlider.label.querySelector("strong").textContent=`${e}%`,this.setVideoVolume(e/100),this.data.syncVolume&&this.syncVolumeWrapper("video",e)})),this.votVideoTranslationVolumeSlider.input.addEventListener("input",(t=>{(async()=>{this.data.defaultVolume=Number(t.target.value),await _.set("defaultVolume",this.data.defaultVolume),this.votVideoTranslationVolumeSlider.label.querySelector("strong").textContent=`${this.data.defaultVolume}%`,this.gainNode.gain.value=this.data.defaultVolume/100,this.data.syncVolume&&(this.syncVolumeWrapper("translation",this.data.defaultVolume),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData&&this.setVideoVolume(this.tempOriginalVolume/100))})()})),this.votAutoTranslateCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoTranslate=Number(t.target.checked),await _.set("autoTranslate",this.data.autoTranslate),await this.autoTranslate(),I.log("autoTranslate value changed. New value: ",this.data.autoTranslate)})()})),this.votDontTranslateYourLangSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.dontTranslateYourLang=Number(t.target.checked),await _.set("dontTranslateYourLang",this.data.dontTranslateYourLang),I.log("dontTranslateYourLang value changed. New value: ",this.data.dontTranslateYourLang)})()})),this.votAutoSetVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.autoSetVolumeYandexStyle=Number(t.target.checked),await _.set("autoSetVolumeYandexStyle",this.data.autoSetVolumeYandexStyle),I.log("autoSetVolumeYandexStyle value changed. New value: ",this.data.autoSetVolumeYandexStyle)})()})),this.votAutoSetVolumeSlider.input.addEventListener("input",(t=>{(async()=>{const e=Number(t.target.value);this.data.autoVolume=(e/100).toFixed(2),await _.set("autoVolume",this.data.autoVolume),this.votAutoSetVolumeSlider.label.querySelector("strong").textContent=`${e}%`})()})),this.votShowVideoSliderCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showVideoSlider=Number(t.target.checked),await _.set("showVideoSlider",this.data.showVideoSlider),I.log("showVideoSlider value changed. New value: ",this.data.showVideoSlider),this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status})()})),this.votAudioBoosterCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioBooster=Number(t.target.checked),await _.set("audioBooster",this.data.audioBooster),I.log("audioBooster value changed. New value: ",this.data.audioBooster);const e=this.votVideoTranslationVolumeSlider.input.value;this.votVideoTranslationVolumeSlider.input.max=this.data.audioBooster?900:100,this.data.audioBooster||(this.votVideoTranslationVolumeSlider.input.value=e>100?100:e,this.votVideoTranslationVolumeSlider.input.dispatchEvent(new Event("input")))})()})),this.votSyncVolumeCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.syncVolume=Number(t.target.checked),await _.set("syncVolume",this.data.syncVolume),I.log("syncVolume value changed. New value: ",this.data.syncVolume)})()})),this.votTranslationServiceSelect.labelElement.addEventListener("change",(t=>{(async()=>{this.data.translateAPIErrors=Number(t.target.checked),await _.set("translateAPIErrors",this.data.translateAPIErrors),I.log("translateAPIErrors value changed. New value: ",this.data.translateAPIErrors)})()})),this.votSubtitlesDetails.container.addEventListener("click",(()=>{this.votSubtitlesDialog=Me.createDialog(H.get("VOTSubtitlesDesign")),this.votSubtitlesDialog.container.classList.add("vot-dialog-temp"),this.votSubtitlesDialog.container.hidden=!1,this.votSubtitlesDialog.backdrop.onclick=this.votSubtitlesDialog.closeButton.onclick=()=>{this.votSubtitlesDialog.container.remove()},this.votSubtitlesHighlightWordsCheckbox=Me.createCheckbox(H.get("VOTHighlightWords"),this.data?.highlightWords??!1),this.votSubtitlesDialog.bodyContainer.appendChild(this.votSubtitlesHighlightWordsCheckbox.container),this.votSubtitlesMaxLengthSlider=Me.createSlider(It`${H.get("VOTSubtitlesMaxLength")}: + ${this.data?.subtitlesMaxLength??300}`,this.data?.subtitlesMaxLength??300,50,300),this.votSubtitlesDialog.bodyContainer.appendChild(this.votSubtitlesMaxLengthSlider.container),this.votSubtitlesFontSizeSlider=Me.createSlider(It`${H.get("VOTSubtitlesFontSize")}: + ${this.data?.subtitlesFontSize??20}`,this.data?.subtitlesFontSize??20,8,50),this.votSubtitlesDialog.bodyContainer.appendChild(this.votSubtitlesFontSizeSlider.container),this.votSubtitlesOpacitySlider=Me.createSlider(It`${H.get("VOTSubtitlesOpacity")}: + ${this.data?.subtitlesOpacity??20}`,this.data?.subtitlesOpacity??20,0,100),this.votSubtitlesDialog.bodyContainer.appendChild(this.votSubtitlesOpacitySlider.container),this.votSubtitlesHighlightWordsCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.highlightWords=Number(t.target.checked),await _.set("highlightWords",this.data.highlightWords),I.log("highlightWords value changed. New value: ",this.data.highlightWords),this.subtitlesWidget.setHighlightWords(this.data.highlightWords)})()})),this.votSubtitlesMaxLengthSlider.input.addEventListener("input",(t=>{(async()=>{this.data.subtitlesMaxLength=Number(t.target.value),await _.set("subtitlesMaxLength",this.data.subtitlesMaxLength),this.votSubtitlesMaxLengthSlider.label.querySelector("strong").textContent=`${this.data.subtitlesMaxLength}`,this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength)})()})),this.votSubtitlesFontSizeSlider.input.addEventListener("input",(t=>{(async()=>{this.data.subtitlesFontSize=Number(t.target.value),await _.set("subtitlesFontSize",this.data.subtitlesFontSize),this.votSubtitlesFontSizeSlider.label.querySelector("strong").textContent=`${this.data.subtitlesFontSize}`,this.subtitlesWidget.setFontSize(this.data.subtitlesFontSize)})()})),this.votSubtitlesOpacitySlider.input.addEventListener("input",(t=>{(async()=>{this.data.subtitlesOpacity=Number(t.target.value),await _.set("subtitlesOpacity",this.data.subtitlesOpacity),this.votSubtitlesOpacitySlider.label.querySelector("strong").textContent=`${this.data.subtitlesOpacity}`,this.subtitlesWidget.setOpacity(this.data.subtitlesOpacity)})()})),document.documentElement.appendChild(this.votSubtitlesDialog.container)})),this.votShowPiPButtonCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.showPiPButton=Number(t.target.checked),await _.set("showPiPButton",this.data.showPiPButton),I.log("showPiPButton value changed. New value: ",this.data.showPiPButton),this.votButton.pipButton.hidden=!B()||!this.data.showPiPButton,this.votButton.separator2.hidden=!B()||!this.data.showPiPButton})()})),this.votM3u8ProxyHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.m3u8ProxyHost=t.target.value||Qt,await _.set("m3u8ProxyHost",this.data.m3u8ProxyHost),I.log("m3u8ProxyHost value changed. New value: ",this.data.m3u8ProxyHost)})()})),this.votProxyWorkerHostTextfield.input.addEventListener("change",(t=>{(async()=>{this.data.proxyWorkerHost=t.target.value||Xt,await _.set("proxyWorkerHost",this.data.proxyWorkerHost),I.log("proxyWorkerHost value changed. New value: ",this.data.proxyWorkerHost),this.votClient.host=this.data.proxyWorkerHost})()})),this.votAudioProxyCheckbox.input.addEventListener("change",(t=>{(async()=>{this.data.audioProxy=Number(t.target.checked),await _.set("audioProxy",this.data.audioProxy),I.log("audioProxy value changed. New value: ",this.data.audioProxy)})()})),this.votResetSettingsButton.addEventListener("click",(()=>{(async()=>{H.reset();const t=await _.list();for(let e=0;e{this.extraEvents.push({element:t,event:e,handler:o}),t.addEventListener(e,o)},e=(e,o,n)=>{for(const i of o)t(e,i,n)};if(this.resizeObserver=new ResizeObserver((t=>{for(let e=0;e550;this.votButton.container.dataset.position=this.votMenu.container.dataset.position=e?this.data?.buttonPos:"default",this.votButton.container.dataset.direction=this.data?.buttonPos&&"default"!==this.data?.buttonPos&&e?"column":"row"})),this.resizeObserver.observe(this.video),this.votMenu.container.setAttribute("style",`--vot-container-height: ${this.video.getBoundingClientRect().height}px`),["youtube","googledrive"].includes(this.site.host)&&"mobile"!==this.site.additionalData){this.syncVolumeObserver=new MutationObserver((t=>{if(this.audio.src&&this.data.syncVolume)for(let e=0;e{const e=t.target,o=this.votButton.container,n=this.votMenu.container,i=this.container,a=this.votSettingsDialog.container,r=document.querySelector(".vot-dialog-temp"),s=o.contains(e),l=n.contains(e),d=i.contains(e),u=a.contains(e),c=r?.contains(e)??!1;I.log(`[document click] ${s} ${l} ${d} ${u} ${c}`),s||l||u||c||(d||this.logout(0),this.votMenu.container.hidden=!0)})),o="pornhub"===this.site.host?"embed"===this.site.additionalData?document.querySelector("#player"):this.container.querySelector(".video-element-wrapper-js > div"):"twitter"===this.site.host?document.querySelector('div[data-testid="videoPlayer"]'):"yandexdisk"===this.site.host?document.querySelector(".video-player__player"):"reddit"===this.site.host?document.querySelector("shreddit-post"):this.container,o&&e(o,["mousemove","mouseout"],this.resetTimerBound),t(this.votButton.container,"mousemove",this.changeOpacityOnEventBound),t(this.votMenu.container,"mousemove",this.changeOpacityOnEventBound),e(document,["touchstart","touchmove","touchend"],this.changeOpacityOnEventBound),t(this.votButton.container,"mousedown",(t=>{t.stopImmediatePropagation()})),t(this.votMenu.container,"mousedown",(t=>{t.stopImmediatePropagation()})),"youtube"===this.site.host&&(this.container.draggable=!1),"googledrive"===this.site.host&&(this.container.style.height="100%"),t(this.video,"canplay",(async()=>{"rutube"===this.site.host&&this.video.src||await it(this.site,this.video)!==this.videoData.videoId&&(await this.handleSrcChanged(),I.log("lipsync mode is loadeddata"),await this.autoTranslate())})),t(this.video,"emptied",(async()=>{this.video.src&&await it(this.site,this.video)===this.videoData.videoId||(I.log("lipsync mode is emptied"),this.videoData="",this.stopTranslation())})),["rutube","ok"].includes(this.site.host)||t(this.video,"volumechange",(()=>{this.syncVideoVolumeSlider()}))}logout(t){this.votMenu.container.hidden&&(this.votButton.container.style.opacity=t)}resetTimer(){clearTimeout(this.timer),this.logout(1),this.timer=setTimeout((()=>{this.logout(0)}),1e3)}changeOpacityOnEvent(t){clearTimeout(this.timer),this.logout(1),t.stopPropagation()}async changeSubtitlesLang(t){I.log("[onchange] subtitles",t),this.votSubtitlesSelect.setSelected(t),"disabled"===t?(this.votSubtitlesSelect.setTitle(H.get("VOTSubtitlesDisabled")),this.subtitlesWidget.setContent(null),this.votDownloadSubtitlesButton.hidden=!0,this.yandexSubtitles=null):(this.yandexSubtitles=await qe(this.subtitlesList.at(parseInt(t))),this.subtitlesWidget.setContent(this.yandexSubtitles),this.votDownloadSubtitlesButton.hidden=!1)}async updateSubtitlesLangSelect(){const t=[{label:H.get("VOTSubtitlesDisabled"),value:"disabled",selected:!0,disabled:!1},...this.subtitlesList.map(((t,e)=>({label:(H.get("langs")[t.language]??t.language.toUpperCase())+(t.translatedFromLanguage?` ${H.get("VOTTranslatedFrom")} ${H.get("langs")[t.translatedFromLanguage]??t.translatedFromLanguage.toUpperCase()}`:"")+("yandex"!==t.source?` ${t.source}`:"")+(t.isAutoGenerated?` (${H.get("VOTAutogenerated")})`:""),value:e,selected:!1,disabled:!1})))];this.votSubtitlesSelect.updateItems(t),await this.changeSubtitlesLang(t[0].value)}async updateSubtitles(){if(await this.changeSubtitlesLang("disabled"),!this.videoData.videoId)return console.error(`[VOT] ${H.getDefault("VOTNoVideoIDFound")}`),this.subtitlesList=[],this.subtitlesListVideoId=null,this.votButton.container.hidden=!0,void await this.updateSubtitlesLangSelect();if(this.votButton.container.hidden=!1,this.subtitlesListVideoId!==this.videoData.videoId){try{this.subtitlesList=await ze(this.votClient,this.videoData)}catch(t){I.log("Error with yandex server, try auto-fix...",t),this.votOpts={fetchFn:q,hostVOT:te,host:this.data.proxyWorkerHost},this.votClient=new ut(this.votOpts),this.subtitlesList=await ze(this.votClient,this.videoData),await _.set("translateProxyEnabled",1)}this.subtitlesList?this.subtitlesListVideoId=this.videoData.videoId:await this.changeSubtitlesLang("disabled"),await this.updateSubtitlesLangSelect()}}getVideoVolume(){let t=this.video?.volume;return["youtube","googledrive"].includes(this.site.host)&&(t=Be.getVideoVolume()??t),t}setVideoVolume(t){if(["youtube","googledrive"].includes(this.site.host)){if(Be.setVideoVolume(t))return}this.video.volume=t}isMuted(){return["youtube","googledrive"].includes(this.site.host)?Be.isMuted():this.video?.muted}syncVideoVolumeSlider(){const t=this.isMuted()?0:100*this.getVideoVolume(),e=Math.round(t);this.votVideoVolumeSlider.input.value=e,this.votVideoVolumeSlider.label.querySelector("strong").textContent=`${e}%`,Me.updateSlider(this.votVideoVolumeSlider.input),1===this.data.syncVolume&&(this.tempOriginalVolume=Number(e))}setSelectMenuValues(t,e){this.votTranslationLanguageSelect.fromSelect.setTitle(H.get("langs")[t]),this.votTranslationLanguageSelect.toSelect.setTitle(H.get("langs")[e]),this.votTranslationLanguageSelect.fromSelect.setSelected(t),this.votTranslationLanguageSelect.toSelect.setSelected(e),console.log(`[VOT] Set translation from ${t} to ${e}`),this.videoData.detectedLanguage=t,this.videoData.responseLanguage=e}syncVolumeWrapper(t,e){const o="translation"===t?this.votVideoVolumeSlider:this.votVideoTranslationVolumeSlider,n=Number(o.input.value),i=function(t,e,o,n){let i=e;return e>n?(i=o+(e-n),i=i>100?100:Math.max(i,0),t.volume=i/100):e100?100:Math.max(i,0),t.volume=i/100),i}("translation"===t?this.video:this.audio,e,n,"translation"===t?this.tempVolume:this.tempOriginalVolume);o.input.value=i,o.label.querySelector("strong").textContent=`${i}%`,Me.updateSlider(o.input),this.tempOriginalVolume="translation"===t?i:e,this.tempVolume="translation"===t?e:i}async getVideoData(){const{duration:t,url:e,videoId:o,host:n,translationHelp:i,detectedLanguage:a}=await at(this.site,this.video),r={translationHelp:i??null,isStream:!1,duration:this.video?.duration||t||y.defaultDuration,videoId:o,url:e,host:n,detectedLanguage:a??this.translateFromLang,responseLanguage:this.translateToLang};if("youtube"===this.site.host){const t=await Be.getVideoData();r.isStream=t.isLive,t.title&&(r.detectedLanguage=t.detectedLanguage)}else if(["rutube","ok.ru","mail_ru"].includes(this.site.host))r.detectedLanguage="ru";else if("youku"===this.site.host)r.detectedLanguage="zh";else if("vk"===this.site.host){const t=document.getElementsByTagName("track")?.[0]?.srclang;r.detectedLanguage=t||"auto"}else"weverse"===this.site.host?r.detectedLanguage="ko":["bilibili","piped","invidious","bitchute","rumble","peertube","dailymotion","trovo","yandexdisk","coursehunter","archive","directlink"].includes(this.site.host)&&(r.detectedLanguage="auto");return r}videoValidator(){if(["youtube","ok.ru","vk"].includes(this.site.host)&&(I.log("VideoValidator videoData: ",this.videoData),1===this.data.dontTranslateYourLang&&this.videoData.detectedLanguage===this.data.dontTranslateLanguage))throw new rt("VOTDisableFromYourLang");if(!this.videoData.isStream&&this.videoData.duration>14400)throw new rt("VOTVideoIsTooLong");return!0}lipSync(t=!1){if(I.log("lipsync video",this.video),this.video)if(this.audio.currentTime=this.video.currentTime,this.audio.playbackRate=this.video.playbackRate,t)if("play"!=t)["pause","stop","waiting"].includes(t)&&(I.log(`lipsync mode is ${t}`),this.audio.pause()),"playing"==t&&(I.log("lipsync mode is playing"),this.audio.play());else{I.log("lipsync mode is play");const t=this.audio.play();void 0!==t&&t.catch((async t=>{if(console.error("[VOT]",t),"NotAllowedError"===t.name)throw this.transformBtn("error",H.get("grantPermissionToAutoPlay")),new rt("grantPermissionToAutoPlay")}))}else I.log("lipsync mode is not set")}handleVideoEvent(t){I.log(`video ${t.type}`),this.lipSync(t.type)}stopTranslate(){for(const t of Ye)this.video.removeEventListener(t,this.handleVideoEventBound);this.audio.pause(),this.audio.src="",this.audio.removeAttribute("src"),this.votVideoVolumeSlider.container.hidden=!0,this.votVideoTranslationVolumeSlider.container.hidden=!0,this.votDownloadButton.hidden=!0,this.downloadTranslationUrl=null,this.transformBtn("none",H.get("translateVideo")),I.log(`Volume on start: ${this.volumeOnStart}`),this.volumeOnStart&&this.setVideoVolume(this.volumeOnStart),this.volumeOnStart="",clearInterval(this.streamPing),clearTimeout(this.autoRetry),this.hls?.destroy(),this.hls=D(),this.firstSyncVolume=!0}async translateExecutor(t){I.log("Run translateFunc",t),await this.translateFunc(t,this.videoData.isStream,this.videoData.detectedLanguage,this.videoData.responseLanguage,this.videoData.translationHelp)}async updateTranslationErrorMsg(t){const e=H.get("translationTake"),o=H.lang;if(["Подготавливаем перевод","Видео передано в обработку","Ожидаем перевод видео","Загружаем переведенное аудио"].includes(t)&&this.setLoadingBtn(!0),"VOTLocalizedError"===t?.name)this.transformBtn("error",t.localizedMessage);else if(t instanceof Error)this.transformBtn("error",t?.message);else if(1!==this.data.translateAPIErrors||t.includes(e)||"ru"===o)this.transformBtn("error",t);else{this.setLoadingBtn(!0);const e=await async function(t,e="",o="ru"){switch(await _.get("translationService",oe)){case"yandex":{const n=e&&o?`${e}-${o}`:o;return await Ae.translate(t,n)}case"deepl":return await Ce.translate(t,e,o);default:return t}}(t,"ru",o);this.transformBtn("error",e)}}afterUpdateTranslation(t){this.votVideoVolumeSlider.container.hidden=1!==this.data.showVideoSlider||"success"!==this.votButton.container.dataset.status,this.votVideoTranslationVolumeSlider.container.hidden="success"!==this.votButton.container.dataset.status,1===this.data.autoSetVolumeYandexStyle&&(this.votVideoVolumeSlider.input.value=100*this.data.autoVolume,this.votVideoVolumeSlider.label.querySelector("strong").textContent=100*this.data.autoVolume+"%",Me.updateSlider(this.votVideoVolumeSlider.input)),this.votDownloadButton.hidden=!1,this.downloadTranslationUrl=t}async updateTranslation(t){if(this.cachedTranslation?.url===this.audio.currentSrc)I.log("[translateFunc] Audio src is the same"),this.audio.src=t;else{try{const e=await q(t,{method:"HEAD",timeout:5e3});if(I.log("Test audio response",e),404===e.status){I.log("Yandex returned not valid audio, trying to fix...");let e=await this.translateVideoImpl(this.videoData,this.videoData.detectedLanguage="auto",this.videoData.responseLanguage,this.videoData.translationHelp);this.setSelectMenuValues(this.videoData.detectedLanguage,this.videoData.responseLanguage),t=e.url,I.log("Fixed audio audioUrl",t)}else I.log("Valid audioUrl",t)}catch(t){"Timeout"===t.message?(I.log("Request timed out. Handling timeout error..."),this.data.audioProxy=1,await _.set("audioProxy",1)):I.log("Test audio error:",t)}this.audio.src=t;try{await this.audio.play()}catch(t){if(console.error("[VOT]",t),"NotSupportedError"===t.name){if([...V,...M].includes(window.location.hostname))throw new rt("VOTMediaCSPError");this.data.audioProxy=1,await _.set("audioProxy",1)}}}if(1===this.data.audioProxy&&t.startsWith("https://vtrans.s3-private.mds.yandex.net/tts/prod/")){const e=t.replace("https://vtrans.s3-private.mds.yandex.net/tts/prod/","");t=`https://${this.data.proxyWorkerHost}/video-translation/audio-proxy/${e}`,console.log(`[VOT] Audio proxied via ${t}`)}if(this.audio.src=t,this.volumeOnStart||(this.volumeOnStart=this.getVideoVolume()),this.setupAudioSettings(),"twitter"===this.site.host)document.querySelector('button[data-testid="app-bar-back"][role="button"]').addEventListener("click",this.stopTranslationBound);this.video&&!this.video.paused&&this.lipSync("play");for(const t of Ye)this.video.addEventListener(t,this.handleVideoEventBound);this.transformBtn("success",H.get("disableTranslate")),this.afterUpdateTranslation(t)}async translateFunc(t,e,o,n,i){if(console.log("[VOT] Video Data: ",this.videoData),I.log("Run videoValidator"),this.videoValidator(),this.setLoadingBtn(!0),e){let t=await this.translateStreamImpl(this.videoData,o,n);if(!t)return void I.log("Skip translation");this.transformBtn("success",H.get("disableTranslate"));const e=`https://${this.data.m3u8ProxyHost}/?all=yes&origin=${encodeURIComponent("https://strm.yandex.ru")}&referer=${encodeURIComponent("https://strm.yandex.ru")}&url=${encodeURIComponent(t.result.url)}`;if(this.hls)this.setupHLS(e);else{if(!this.audio.canPlayType("application/vnd.apple.mpegurl"))throw new rt("audioFormatNotSupported");this.audio.src=e}if("youtube"===this.site.host&&Be.videoSeek(this.video,10),this.setupAudioSettings(),!this.video.src&&!this.video.currentSrc&&!this.video.srcObject)return this.stopTranslation();this.video&&!this.video.paused&&this.lipSync("play");for(const t of Ye)this.video.addEventListener(t,this.handleVideoEventBound);return this.afterUpdateTranslation(e)}if(this.cachedTranslation=this.videoTranslations.find((e=>e.videoId===t&&e.expires>Date.now()/1e3&&e.from===o&&e.to===n)),this.cachedTranslation)return await this.updateTranslation(this.cachedTranslation.url),void I.log("[translateFunc] Cached translation was received");let a=await this.translateVideoImpl(this.videoData,o,n,i);I.log("[translateRes]",a),a?(await this.updateTranslation(a.url),[k.kick,k.reddit,k.patreon,k.kodik,k.appledeveloper].includes(this.site.host)||this.subtitlesList.some((t=>"yandex"===t.source&&t.translatedFromLanguage===this.videoData.detectedLanguage&&t.language===this.videoData.responseLanguage))||(this.subtitlesList=await ze(this.votClient,this.videoData),await this.updateSubtitlesLangSelect()),this.videoTranslations.push({videoId:t,from:o,to:n,url:this.downloadTranslationUrl,expires:Date.now()/1e3+this.videoTranslationTTL})):I.log("Skip translation")}setupHLS(t){this.hls.on(Hls.Events.MEDIA_ATTACHED,(function(){I.log("audio and hls.js are now bound together !")})),this.hls.on(Hls.Events.MANIFEST_PARSED,(function(t){I.log("manifest loaded, found "+t?.levels?.length+" quality level")})),this.hls.loadSource(t),this.hls.attachMedia(this.audio),this.hls.on(Hls.Events.ERROR,(function(t){if(t.fatal)switch(t.type){case Hls.ErrorTypes.MEDIA_ERROR:console.log("fatal media error encountered, try to recover"),this.hls.recoverMediaError();break;case Hls.ErrorTypes.NETWORK_ERROR:console.error("fatal network error encountered",t);break;default:this.hls.destroy()}})),I.log(this.hls)}setupAudioSettings(){"number"==typeof this.data.defaultVolume&&(this.gainNode.gain.value=this.data.defaultVolume/100),"number"==typeof this.data.autoSetVolumeYandexStyle&&this.data.autoSetVolumeYandexStyle&&this.setVideoVolume(this.data.autoVolume)}stopTranslation(){this.stopTranslate(),this.syncVideoVolumeSlider()}async handleSrcChanged(){I.log("[VideoHandler] src changed",this),this.firstPlay=!0,this.videoData=await this.getVideoData(),this.setSelectMenuValues(this.videoData.detectedLanguage,this.videoData.responseLanguage);const t=!this.video.src&&!this.video.currentSrc&&!this.video.srcObject;this.votButton.container.hidden=t,t&&(this.votMenu.container.hidden=t),this.site.selector||(this.container=this.video.parentElement),this.container.contains(this.votButton.container)||(this.container.appendChild(this.votButton.container),this.container.appendChild(this.votMenu.container)),await this.updateSubtitles(),await this.changeSubtitlesLang("disabled"),this.translateToLang=this.data.responseLanguage??"ru"}async release(){I.log("[VideoHandler] release"),this.initialized=!1,this.releaseExtraEvents(),this.subtitlesWidget.release(),this.votButton.container.remove(),this.votMenu.container.remove()}}const Qe=new class{constructor(){this.videoCache=new Set,this.onVideoAdded=new Ue,this.onVideoRemoved=new Ue,this.observer=new MutationObserver((t=>{window.requestIdleCallback((()=>{for(let e=0;e{for(let e=0;e{o.has(t)||o.add(t)})),t.querySelectorAll("*").forEach((t=>{t.shadowRoot&&n(t.shadowRoot)})))}for(let t=0;t=3}(t)?requestAnimationFrame(o):e(t)}()}(t,(t=>{this.handleVideoAdded(t)}))}handleVideoAdded=t=>{this.onVideoAdded.dispatch(t)};handleVideoRemoved=t=>{document.contains(t)||(this.videoCache.delete(t),this.onVideoRemoved.dispatch(t))}},Xe=new WeakMap;function to(t,e){if(t.shadowRoot){let o=t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.shadowRoot.contains(e))):e.parentElement;return o&&o.shadowRoot?o.parentElement:o}const o=Ge.browser.version.split(".")[0];if(t.selector?.includes(":not")&&t.selector?.includes("*")&&o&&("Chrome"===Ge.browser.name&&Number(o)<88||"Firefox"===Ge.browser.name&&Number(o)<84)){const o=t.selector.split(" *")[0];return o?Array.from(document.querySelectorAll(o)).find((t=>t.contains(e))):e.parentElement}return t.selector?Array.from(document.querySelectorAll(t.selector)).find((t=>t.contains(e))):e.parentElement}(async function(){I.log("Loading extension..."),await H.update(),I.log(`Selected menu language: ${H.lang}`),Qe.onVideoAdded.addListener((t=>{for(const e of function(){if(/(http(s)?:\/\/)(127\.0\.0\.1|localhost)/.exec(window.location.href))return[];const t=window.location.hostname,e=new URL(window.location),o=o=>o instanceof RegExp?o.test(t):"string"==typeof o?t.includes(o):"function"==typeof o&&o(e);return A.filter((t=>(Array.isArray(t.match)?t.match.some(o):o(t.match))&&t.host&&t.url))}()){if(!e)continue;let o=to(e,t);if(o&&(("rumble"!==e.host||t.style.display)&&(["peertube","directlink"].includes(e.host)&&(e.url=window.location.origin),!Xe.has(t)))){Xe.set(t,new Ze(t,o,e));break}}})),Qe.onVideoRemoved.addListener((async t=>{Xe.has(t)&&(await Xe.get(t).release(),Xe.delete(t))})),Qe.enable()})().catch((t=>{console.error("[VOT]",t)}))})()})(); \ No newline at end of file diff --git a/dist/vot.user.js b/dist/vot.user.js index 2097595a..a5fe6292 100644 --- a/dist/vot.user.js +++ b/dist/vot.user.js @@ -32,17 +32,49 @@ // @match *://*.pornhub.com/* // @match *://*.vk.com/* // @match *://*.vk.ru/* -// @match *://invidious.snopyta.org/* -// @match *://invidious.kavin.rocks/* -// @match *://vid.puffyan.us/* -// @match *://invidious.namazso.eu/* -// @match *://inv.riverside.rocks/* +// @match *://*.vimeo.com/* +// @match *://*.9gag.com/* +// @match *://*.twitter.com/* +// @match *://*.x.com/* +// @match *://*.facebook.com/* +// @match *://*.rutube.ru/* +// @match *://*.bilibili.com/* +// @match *://my.mail.ru/* +// @match *://*.bitchute.com/* +// @match *://*.coursera.org/learn/* +// @match *://*.udemy.com/course/* +// @match *://*.tiktok.com/* +// @match *://rumble.com/* +// @match *://*.eporner.com/* +// @match *://geo.dailymotion.com/* +// @match *://*.ok.ru/* +// @match *://trovo.live/* +// @match *://disk.yandex.ru/i/* +// @match *://coursehunter.net/* +// @match *://youtube.googleapis.com/embed/* +// @match *://*.banned.video/* +// @match *://*.weverse.io/* +// @match *://*.newgrounds.com/* +// @match *://*.egghead.io/* +// @match *://*.youku.com/* +// @match *://*.archive.org/* +// @match *://*.patreon.com/* +// @match *://*.reddit.com/* +// @match *://*.kodik.info/* +// @match *://*.kodik.biz/* +// @match *://*.kodik.cc/* +// @match *://*.kick.com/* +// @match *://developer.apple.com/* +// @match *://*/*.mp4* +// @match *://*.yewtu.be/* // @match *://yt.artemislena.eu/* // @match *://invidious.flokinet.to/* -// @match *://invidious.esmailelbob.xyz/* -// @match *://invidious.nerdvpn.de/* -// @match *://invidious.slipfox.xyz/* -// @match *://invidio.xamh.de/* +// @match *://iv.melmac.space/* +// @match *://inv.nadeko.net/* +// @match *://inv.tux.pizza/* +// @match *://invidious.private.coffee/* +// @match *://yt.drgnz.club/* +// @match *://vid.puffyan.us/* // @match *://invidious.dhusch.de/* // @match *://*.piped.video/* // @match *://piped.tokhmi.xyz/* @@ -72,19 +104,6 @@ // @match *://piped.jotoma.de/* // @match *://piped.pfcd.me/* // @match *://piped.frontendfriendly.xyz/* -// @match *://*.yewtu.be/* -// @match *://inv.vern.cc/* -// @match *://*.vimeo.com/* -// @match *://*.9gag.com/* -// @match *://*.twitter.com/* -// @match *://*.facebook.com/* -// @match *://*.rutube.ru/* -// @match *://*.bilibili.com/* -// @match *://my.mail.ru/* -// @match *://*.bitchute.com/* -// @match *://*.coursera.org/learn/* -// @match *://*.udemy.com/course/* -// @match *://*.tiktok.com/* // @match *://proxitok.pabloferreiro.es/* // @match *://proxitok.pussthecat.org/* // @match *://tok.habedieeh.re/* @@ -92,46 +111,32 @@ // @match *://proxitok.privacydev.net/* // @match *://tok.artemislena.eu/* // @match *://tok.adminforge.de/* -// @match *://tik.hostux.net/* // @match *://tt.vern.cc/* // @match *://cringe.whatever.social/* // @match *://proxitok.lunar.icu/* // @match *://proxitok.privacy.com.de/* -// @match *://rumble.com/* -// @match *://*.eporner.com/* // @match *://peertube.1312.media/* // @match *://tube.shanti.cafe/* -// @match *://bee-tube.fr/* +// @match *://*.bee-tube.fr/* // @match *://video.sadmin.io/* -// @match *://dalek.zone/* +// @match *://*.dalek.zone/* // @match *://review.peertube.biz/* -// @match *://peervideo.club/* +// @match *://*.peervideo.club/* // @match *://tube.la-dina.net/* // @match *://peertube.tmp.rcp.tf/* -// @match *://peertube.su/* -// @match *://geo.dailymotion.com/* -// @match *://*.ok.ru/* -// @match *://trovo.live/* -// @match *://disk.yandex.ru/i/* -// @match *://coursehunter.net/* -// @match *://youtube.googleapis.com/embed/* -// @match *://*.banned.video/* -// @match *://*.weverse.io/* -// @match *://*.newgrounds.com/* -// @match *://*.egghead.io/* -// @match *://*.youku.com/* -// @match *://*.archive.org/* -// @match *://*/*.mp4* +// @match *://*.peertube.su/* +// @match *://video.blender.org/* // @exclude file://*/*.mp4* // @connect yandex.ru // @connect yandex.net +// @connect timeweb.cloud // @connect raw.githubusercontent.com // @connect toil.cc // @connect deno.dev // @connect onrender.com // @connect workers.dev // @namespace vot -// @version 1.5.3.1 +// @version 1.6.0 // @icon https://translate.yandex.ru/icons/favicon.ico // @author sodapng, mynovelhost, Toil, SashaXser, MrSoczekXD // @homepageURL https://github.com/ilyhalight/voice-over-translation/issues @@ -166,7 +171,7 @@ var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default())); // Module -___CSS_LOADER_EXPORT___.push([module.id, `.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button:disabled{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button:disabled::before,.vot-button:disabled::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button:disabled::before,.vot-outlined-button:disabled::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button:disabled::before,.vot-text-button:disabled::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button:disabled{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button:disabled::before,.vot-icon-button:disabled::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input,.vot-textfield:hover>textarea{border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input+span::before,.vot-textfield:hover>input+span::after,.vot-textfield:hover>textarea+span::before,.vot-textfield:hover>textarea+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:10000}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-translating=true] #vot-translating-icon{display:block !important}.vot-segmented-button[data-translating=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:10000;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog-container{visibility:visible;position:absolute;z-index:10000}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:10000;left:25%;top:75%;pointer-events:none}.vot-subtitles{position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:1rem;pointer-events:all;padding:1rem;font-size:2rem;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8);--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block}`, ""]); +___CSS_LOADER_EXPORT___.push([module.id, `.vot-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-ontheme));background-color:rgb(var(--vot-helper-theme));box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer;transition:box-shadow .2s}.vot-button[hidden]{display:none !important}.vot-button::-moz-focus-inner{border:none}.vot-button::before,.vot-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-button::before{background-color:rgb(var(--vot-helper-ontheme));transition:opacity .2s}.vot-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-button:hover{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px 0 rgba(0,0,0,.14),0 1px 10px 0 rgba(0,0,0,.12)}.vot-button:hover::before{opacity:.08}.vot-button:active{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.vot-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s}.vot-button[disabled=true]{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);box-shadow:none;cursor:initial}.vot-button[disabled=true]::before,.vot-button[disabled=true]::after{opacity:0}.vot-outlined-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:solid 1px;border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.24);border-radius:4px;padding:0 16px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:34px;outline:none;cursor:pointer}.vot-outlined-button[hidden]{display:none !important}.vot-outlined-button::-moz-focus-inner{border:none}.vot-outlined-button::before,.vot-outlined-button::after{content:"";position:absolute;border-radius:3px;top:0;right:0;bottom:0;left:0;opacity:0}.vot-outlined-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-outlined-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-outlined-button:hover::before{opacity:.04}.vot-outlined-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-outlined-button[disabled=true]{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-outlined-button[disabled=true]::before,.vot-outlined-button[disabled=true]::after{opacity:0}.vot-text-button{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:4px;padding:0 8px;min-width:64px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;color:rgb(var(--vot-helper-theme));background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-text-button[hidden]{display:none !important}.vot-text-button::-moz-focus-inner{border:none}.vot-text-button::before,.vot-text-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-text-button::before{background-color:rgb(var(--vot-helper-theme));transition:opacity .2s}.vot-text-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-text-button:hover::before{opacity:.04}.vot-text-button:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-text-button[disabled=true]{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-text-button[disabled=true]::before,.vot-text-button[disabled=true]::after{opacity:0}.vot-icon-button{--vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);position:relative;display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;border:none;border-radius:50%;padding:0;width:36px;height:36px;vertical-align:middle;text-align:center;text-overflow:ellipsis;fill:var(--vot-helper-onsurface);color:var(--vot-helper-onsurface);background-color:rgba(0,0,0,0);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:500;line-height:36px;outline:none;cursor:pointer}.vot-icon-button[hidden]{display:none !important}.vot-icon-button::-moz-focus-inner{border:none}.vot-icon-button::before,.vot-icon-button::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-icon-button::before{background-color:var(--vot-helper-onsurface);transition:opacity .2s}.vot-icon-button::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity .3s,background-size .4s}.vot-icon-button:hover::before{opacity:.04}.vot-icon-button:active::after{opacity:.32;background-size:100% 100%;transition:background-size 0s,opacity 0s}.vot-icon-button[disabled=true]{background-color:rgba(0,0,0,0);color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);fill:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-icon-button[disabled=true]::before,.vot-icon-button[disabled=true]::after{opacity:0}.vot-textfield{--vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important;--vot-helper-safari1: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.38 ) !important;--vot-helper-safari2: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari3: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;position:relative !important;display:inline-block;padding-top:6px !important;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-textfield[hidden]{display:none !important}.vot-textfield>input,.vot-textfield>textarea{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:0 !important;border-style:solid !important;border-width:1px !important;border-color:rgba(0,0,0,0) var(--vot-helper-safari2) var(--vot-helper-safari2) !important;border-radius:4px !important;padding:15px 13px 15px !important;width:100% !important;height:inherit !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;-webkit-text-fill-color:currentColor !important;background-color:rgba(0,0,0,0) !important;box-shadow:inset 1px 0 rgba(0,0,0,0),inset -1px 0 rgba(0,0,0,0),inset 0 -1px rgba(0,0,0,0) !important;font-family:inherit !important;font-size:inherit !important;line-height:inherit !important;caret-color:var(--vot-helper-theme) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input:not(:focus):not(.vot-show-placeholer)::-moz-placeholder,.vot-textfield>textarea:not(:focus):not(.vot-show-placeholer)::-moz-placeholder{color:rgba(0,0,0,0) !important}.vot-textfield>input:not(:focus):not(.vot-show-placeholer)::-ms-input-placeholder,.vot-textfield>textarea:not(:focus):not(.vot-show-placeholer)::-ms-input-placeholder{color:rgba(0,0,0,0) !important}.vot-textfield>input:not(:focus):not(.vot-show-placeholer)::-webkit-input-placeholder,.vot-textfield>textarea:not(:focus):not(.vot-show-placeholer)::-webkit-input-placeholder{color:rgba(0,0,0,0) !important}.vot-textfield>input:not(:focus):placeholder-shown,.vot-textfield>textarea:not(:focus):placeholder-shown{border-top-color:var(--vot-helper-safari2) !important}.vot-textfield>input+span,.vot-textfield>textarea+span{position:absolute !important;top:0 !important;left:0 !important;display:flex !important;width:100% !important;max-height:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;font-size:75% !important;line-height:15px !important;cursor:text !important;transition:color .2s,font-size .2s,line-height .2s !important;pointer-events:none !important}.vot-textfield>input:not(:focus):placeholder-shown+span,.vot-textfield>textarea:not(:focus):placeholder-shown+span{font-size:inherit !important;line-height:68px !important}.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{content:"" !important;display:block !important;-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin-top:6px !important;border-top:solid 1px var(--vot-helper-safari2) !important;min-width:10px !important;height:8px !important;pointer-events:none !important;box-shadow:inset 0 1px rgba(0,0,0,0) !important;transition:border .2s,box-shadow .2s !important}.vot-textfield>input+span::before,.vot-textfield>textarea+span::before{margin-right:4px !important;border-left:solid 1px rgba(0,0,0,0) !important;border-radius:4px 0 !important}.vot-textfield>input+span::after,.vot-textfield>textarea+span::after{flex-grow:1 !important;margin-left:4px !important;border-right:solid 1px rgba(0,0,0,0) !important;border-radius:0 4px !important}.vot-textfield>input.vot-show-placeholer+span::before,.vot-textfield>textarea.vot-show-placeholer+span::before{margin-right:0 !important}.vot-textfield>input.vot-show-placeholer+span::after,.vot-textfield>textarea.vot-show-placeholer+span::after{margin-left:0 !important}.vot-textfield>input:not(:focus):placeholder-shown+span::before,.vot-textfield>input:not(:focus):placeholder-shown+span::after,.vot-textfield>textarea:not(:focus):placeholder-shown+span::before,.vot-textfield>textarea:not(:focus):placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}.vot-textfield:hover>input:not(:disabled),.vot-textfield:hover>textarea:not(:disabled){border-color:rgba(0,0,0,0) var(--vot-helper-safari3) var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:disabled)+span::before,.vot-textfield:hover>input:not(:disabled)+span::after,.vot-textfield:hover>textarea:not(:disabled)+span::before,.vot-textfield:hover>textarea:not(:disabled)+span::after{border-top-color:var(--vot-helper-safari3) !important}.vot-textfield:hover>input:not(:disabled):not(:focus):placeholder-shown,.vot-textfield:hover>textarea:not(:disabled):not(:focus):placeholder-shown{border-color:var(--vot-helper-safari3) !important}.vot-textfield>input:focus,.vot-textfield>textarea:focus{border-color:rgba(0,0,0,0) var(--vot-helper-theme) var(--vot-helper-theme) !important;box-shadow:inset 1px 0 var(--vot-helper-theme),inset -1px 0 var(--vot-helper-theme),inset 0 -1px var(--vot-helper-theme) !important;outline:none !important}.vot-textfield>input:focus+span,.vot-textfield>textarea:focus+span{color:var(--vot-helper-theme) !important}.vot-textfield>input:focus+span::before,.vot-textfield>input:focus+span::after,.vot-textfield>textarea:focus+span::before,.vot-textfield>textarea:focus+span::after{border-top-color:var(--vot-helper-theme) !important;box-shadow:inset 0 1px var(--vot-helper-theme) !important}.vot-textfield>input:disabled,.vot-textfield>input:disabled+span,.vot-textfield>textarea:disabled,.vot-textfield>textarea:disabled+span{border-color:rgba(0,0,0,0) var(--vot-helper-safari1) var(--vot-helper-safari1) !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;pointer-events:none !important}.vot-textfield>input:disabled+span::before,.vot-textfield>input:disabled+span::after,.vot-textfield>textarea:disabled+span::before,.vot-textfield>textarea:disabled+span::after{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown,.vot-textfield>input:disabled:placeholder-shown+span,.vot-textfield>textarea:disabled:placeholder-shown,.vot-textfield>textarea:disabled:placeholder-shown+span{border-top-color:var(--vot-helper-safari1) !important}.vot-textfield>input:disabled:placeholder-shown+span::before,.vot-textfield>input:disabled:placeholder-shown+span::after,.vot-textfield>textarea:disabled:placeholder-shown+span::before,.vot-textfield>textarea:disabled:placeholder-shown+span::after{border-top-color:rgba(0,0,0,0) !important}@media not all and (min-resolution: 0.001dpcm){@supports(-webkit-appearance: none){.vot-textfield>input,.vot-textfield>input+span,.vot-textfield>textarea,.vot-textfield>textarea+span,.vot-textfield>input+span::before,.vot-textfield>input+span::after,.vot-textfield>textarea+span::before,.vot-textfield>textarea+span::after{transition-duration:.1s !important}}}.vot-checkbox{--vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) );--vot-helper-ontheme: var( --vot-ontheme-rgb, var(--vot-onprimary-rgb, 255, 255, 255) );z-index:0;position:relative;display:inline-block;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-checkbox[hidden]{display:none !important}.vot-checkbox>input{appearance:none;-moz-appearance:none;-webkit-appearance:none;z-index:10000;position:absolute;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:3px 1px;border:solid 2px;background:rgba(0,0,0,0);border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6);border-radius:2px;width:18px;height:18px;outline:none;cursor:pointer;transition:border-color .2s,background-color .2s}.vot-checkbox>input+span{display:inline-block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-left:30px;width:inherit;cursor:pointer;font-weight:normal}.vot-checkbox>input+span::before{content:"";position:absolute;left:-10px;top:-8px;display:block;border-radius:50%;width:40px;height:40px;background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0));opacity:0;transform:scale(1);pointer-events:none;transition:opacity .3s,transform .2s}.vot-checkbox>input+span::after{content:"";z-index:10000;display:block;position:absolute;top:3px;left:1px;-webkit-box-sizing:content-box !important;-moz-box-sizing:content-box !important;box-sizing:content-box !important;width:10px;height:5px;border:solid 2px rgba(0,0,0,0);border-right-width:0;border-top-width:0;pointer-events:none;transform:translate(3px, 4px) rotate(-45deg);transition:border-color .2s}.vot-checkbox>input:checked,.vot-checkbox>input:indeterminate{border-color:rgb(var(--vot-helper-theme));background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::before,.vot-checkbox>input:indeterminate+span::before{background-color:rgb(var(--vot-helper-theme))}.vot-checkbox>input:checked+span::after,.vot-checkbox>input:indeterminate+span::after{border-color:rgb(var(--vot-helper-ontheme, 255, 255, 255))}.vot-checkbox>input:indeterminate+span::after{border-left-width:0;transform:translate(4px, 3px)}.vot-checkbox:hover>input+span::before{opacity:.04}.vot-checkbox:active>input,.vot-checkbox:active:hover>input{border-color:rgb(var(--vot-helper-theme))}.vot-checkbox:active>input:checked{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6)}.vot-checkbox:active>input+span::before{opacity:1;transform:scale(0);transition:transform 0s,opacity 0s}.vot-checkbox>input:disabled{border-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled:checked,.vot-checkbox>input:disabled:indeterminate{border-color:rgba(0,0,0,0);background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38)}.vot-checkbox>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38);cursor:initial}.vot-checkbox>input:disabled+span::before{opacity:0;transform:scale(0)}.vot-slider{--vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 ) !important;--vot-safari-helper2: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.12 ) !important;--vot-safari-helper3: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.16 ) !important;--vot-safari-helper4: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.24 ) !important;display:inline-block;width:100% !important;color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important;font-family:var(--vot-font, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system) !important;font-size:16px !important;line-height:1.5 !important;text-align:start !important}.vot-slider[hidden]{display:none !important}.vot-slider>input{-webkit-appearance:none !important;appearance:none !important;position:relative !important;top:24px !important;display:block !important;margin:0 0 -36px !important;width:100% !important;height:36px !important;background-color:rgba(0,0,0,0) !important;cursor:pointer !important}.vot-slider>input:last-child{position:static !important;margin:0 !important}.vot-slider>span{display:inline-block !important;margin-bottom:36px !important}.vot-slider>input:disabled{cursor:default !important;opacity:.38 !important}.vot-slider>input:disabled+span{color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input::-webkit-slider-runnable-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-webkit-slider-thumb{margin:0 !important;appearance:none !important;-webkit-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper1) !important}.vot-slider>input:active::-webkit-slider-thumb{box-shadow:0 0 0 2px var(--vot-safari-helper4) !important}.vot-slider>input:disabled::-webkit-slider-runnable-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-webkit-slider-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;color:rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-range-track{margin:17px 0 !important;border-radius:1px !important;width:100% !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-moz-range-thumb{appearance:none !important;-moz-appearance:none !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider>input::-moz-range-progress{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider:hover>input:hover::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-moz-range-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-moz-range-track{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-moz-range-progress{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87) !important}.vot-slider>input:disabled::-moz-range-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::-moz-focus-outer{border:none !important}.vot-slider>input:focus{outline:none !important}.vot-slider>input::-ms-track{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important;margin:17px 0 !important;border:none !important;border-radius:1px !important;padding:0 17px !important;width:100% !important;height:2px !important;background-color:rgba(0,0,0,0) !important}.vot-slider>input::-ms-fill-lower{border-radius:1px !important;height:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-slider>input::-ms-fill-upper{border-radius:1px !important;height:2px !important;background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input::-ms-thumb{appearance:none !important;margin:0 17px !important;border:none !important;border-radius:50% !important;height:2px !important;width:2px !important;background-color:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important;transform:scale(6, 6) !important;transition:box-shadow .2s !important}.vot-slider:hover>input::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.04) !important}.vot-slider>input:active::-ms-thumb{box-shadow:0 0 0 2px rgba(var(--vot-primary-rgb, 33, 150, 243), 0.24) !important}.vot-slider>input:disabled::-ms-fill-lower{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important}.vot-slider>input:disabled::-ms-fill-upper{background-color:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38) !important;opacity:.38 !important}.vot-slider>input:disabled::-ms-thumb{background-color:rgb(var(--vot-onsurface-rgb, 0, 0, 0)) !important;box-shadow:0 0 0 1px rgb(var(--vot-surface-rgb, 255, 255, 255)) !important;transform:scale(4, 4) !important}.vot-slider>input::before{content:"" !important;display:block !important;position:absolute !important;width:calc(100%*var(--vot-progress, 0)) !important;height:2px !important;top:calc(50% - 1px) !important;background:rgb(var(--vot-primary-rgb, 33, 150, 243)) !important}.vot-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important;--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important;--vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important;--vot-helper-safari2: rgba( var(--vot-onsurface-rgb, 0, 0, 0), 0.87 ) !important;display:flex;align-items:center;justify-content:space-between;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:14px;font-weight:normal;line-height:1.5;text-align:start;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-select[hidden]{display:none !important}.vot-select-label{font-size:16px}.vot-select-outer{display:flex;align-items:center;justify-content:space-between;max-width:120px;width:120px;padding:0 5px;border-style:solid !important;border-width:1px !important;border-color:var(--vot-helper-safari1) !important;border-radius:4px !important;cursor:pointer;transition:border .2s !important}.vot-select-outer:hover{border-color:var(--vot-helper-safari2) !important}.vot-select-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vot-select-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center}.vot-select-content-list{display:flex;flex-direction:column}.vot-select-content-list .vot-select-content-item{padding:5px 10px;border-radius:8px;cursor:pointer}.vot-select-content-list .vot-select-content-item:not([inert]):hover{background-color:#2a2c31}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]{color:rgb(var(--vot-primary-rgb, 33, 150, 243));background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.2)}.vot-select-content-list .vot-select-content-item[data-vot-selected=true]:hover{background-color:rgba(var(--vot-primary-rgb, 33, 150, 243), 0.1) !important}.vot-select-content-list .vot-select-content-item[data-vot-disabled=true]{cursor:default}.vot-select-content-list .vot-select-content-item[hidden]{display:none !important}.vot-header{color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-weight:bold;line-height:1.5;text-align:start}.vot-header[hidden]{display:none !important}.vot-header:not(:first-child){padding-top:8px}.vot-header-level-1{font-size:2em}.vot-header-level-2{font-size:1.5em}.vot-header-level-3{font-size:1.17em}.vot-header-level-4{font-size:1em}.vot-header-level-5{font-size:.83em}.vot-header-level-6{font-size:.67em}.vot-info{display:flex;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;text-align:start}.vot-info[hidden]{display:none !important}.vot-info>:not(:first-child){color:rgba(var(--vot-helper-onsurface-rgb), 0.5);flex:1;margin-left:8px}.vot-details{display:flex;justify-content:space-between;align-items:center;color:rgba(var(--vot-helper-onsurface-rgb), 0.87);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;border-radius:.5em;padding:.5em;margin:0 -0.5em;line-height:1.5;text-align:start;cursor:pointer;transition:background .5s ease}.vot-details[hidden]{display:none !important}.vot-details-arrow-icon{width:20px;height:32px;display:flex;justify-content:center;align-items:center;transform:scale(1.25) rotate(-90deg);fill:rgba(var(--vot-helper-onsurface-rgb), 0.87)}.vot-details:hover{background:rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.04)}.vot-lang-select{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);display:flex;align-items:center;justify-content:space-between;color:var(--vot-helper-theme);fill:var(--vot-helper-theme)}.vot-lang-select[hidden]{display:none !important}.vot-lang-select-icon{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.vot-segmented-button{--vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:5rem;transform:translate(-50%);user-select:none;display:flex;align-items:center;height:32px;max-width:100vw;background:rgb(var(--vot-surface-rgb, 255, 255, 255));color:var(--vot-helper-theme);fill:var(--vot-helper-theme);border-radius:4px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;cursor:default;transition:opacity .5s;z-index:2147483647}.vot-segmented-button[hidden]{display:none !important}.vot-segmented-button *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-segmented-button .vot-separator{width:1px;height:50%;background:rgba(var(--vot-helper-theme-rgb), 0.1)}.vot-segmented-button .vot-separator[hidden]{display:none !important}.vot-segmented-button .vot-segment,.vot-segmented-button .vot-segment-only-icon{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:center;height:100%;padding:0 8px;background-color:rgba(0,0,0,0);color:inherit;transition:background-color 100ms ease-in-out;border:none}.vot-segmented-button .vot-segment[hidden],.vot-segmented-button [hidden].vot-segment-only-icon{display:none !important}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before,.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{content:"";position:absolute;border-radius:inherit;top:0;right:0;bottom:0;left:0;opacity:0}.vot-segmented-button .vot-segment::before,.vot-segmented-button .vot-segment-only-icon::before{background-color:rgb(var(--vot-helper-theme-rgb));transition:opacity .2s}.vot-segmented-button .vot-segment::after,.vot-segmented-button .vot-segment-only-icon::after{background:radial-gradient(circle at center, currentColor 1%, transparent 1%) center/10000% 10000% no-repeat;transition:opacity 1s,background-size .5s}.vot-segmented-button .vot-segment:hover::before,.vot-segmented-button .vot-segment-only-icon:hover::before{opacity:.04}.vot-segmented-button .vot-segment:active::after,.vot-segmented-button .vot-segment-only-icon:active::after{opacity:.16;background-size:100% 100%;transition:background-size 0s}.vot-segmented-button .vot-segment-only-icon{min-width:32px;padding:0}.vot-segmented-button .vot-segment-label{margin-left:8px;white-space:nowrap}.vot-segmented-button[data-status=success] .vot-translate-button{color:rgb(var(--vot-primary-rgb, 33, 150, 243));fill:rgb(var(--vot-primary-rgb, 33, 150, 243))}.vot-segmented-button[data-status=error] .vot-translate-button{color:#f28b82;fill:#f28b82}.vot-segmented-button[data-loading=true] #vot-loading-icon{display:block !important}.vot-segmented-button[data-loading=true] #vot-translate-icon{display:none !important}.vot-segmented-button[data-direction=column]{flex-direction:column;height:fit-content}.vot-segmented-button[data-direction=column] .vot-segment-label{display:none}.vot-segmented-button[data-direction=column]>.vot-segment-only-icon,.vot-segmented-button[data-direction=column]>.vot-segment{padding:8px}.vot-segmented-button[data-direction=column] .vot-separator{height:1px;width:50%}.vot-segmented-button[data-position=left]{left:50px;top:12.5vh}.vot-segmented-button[data-position=right]{left:auto;right:0;top:12.5vh}.vot-segmented-button svg{width:fit-content}.vot-menu{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);overflow:hidden;position:absolute;left:50%;top:calc(5rem + 32px + 16px);user-select:none;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);border-radius:8px;font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;min-width:300px;cursor:default;z-index:2147483647;visibility:visible;opacity:1;transform-origin:top;transform:translate(-50%) scale(1);transition:opacity .3s,transform .1s}.vot-menu *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-menu[hidden]{pointer-events:none;display:block !important;visibility:hidden;opacity:0;transform:translate(-50%) scale(0)}.vot-menu-content-wrapper{display:flex;flex-direction:column;min-height:100px;max-height:calc(var(--vot-container-height, 75vh) - (5rem + 32px + 16px)*2);overflow:auto}.vot-menu-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-menu-header-container:empty{padding:0 0 16px 0}.vot-menu-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-menu-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0;text-align:start}.vot-menu-title{flex:1;font-size:16px;line-height:1;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 16px;gap:8px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar,.vot-menu-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-menu-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-menu-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-menu-footer-container:empty{padding:16px 0 0 0}.vot-menu[data-position=left]{left:240px;top:12.5vh}.vot-menu[data-position=right]{right:-80px;left:auto;top:12.5vh}.vot-dialog{--vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255);--vot-helper-surface: rgb(var(--vot-helper-surface-rgb));--vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0);--vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87);display:block;position:fixed;top:50%;bottom:50%;max-width:initial;max-height:initial;width:min(var(--vot-dialog-width, 512px),100%);height:fit-content;inset-inline-start:0px;inset-inline-end:0px;inset-block-start:0px;inset-block-end:0px;border-radius:8px;margin:auto;padding:0;background-color:var(--vot-helper-surface);color:var(--vot-helper-onsurface);box-shadow:0 0 16px rgba(0,0,0,.12),0 16px 16px rgba(0,0,0,.24);font-family:var(--vot-font-family, "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system);font-size:16px;line-height:1.5;user-select:none;visibility:visible;overflow:auto;overflow-y:hidden;opacity:1;transform-origin:center;transform:scale(1);transition:opacity .3s,transform .1s}[hidden]>.vot-dialog{pointer-events:none;opacity:0;transform:scale(0.5);transition:opacity .1s,transform .2s}.vot-dialog-container{visibility:visible;position:absolute;z-index:2147483647}.vot-dialog-container[hidden]{display:block !important;pointer-events:none;visibility:hidden}.vot-dialog-container *{-webkit-box-sizing:border-box !important;-moz-box-sizing:border-box !important;box-sizing:border-box !important}.vot-dialog-backdrop{background-color:rgba(0,0,0,.6);position:fixed;top:0;right:0;bottom:0;left:0;opacity:1;transition:opacity .3s}[hidden]>.vot-dialog-backdrop{pointer-events:none;opacity:0}.vot-dialog-content-wrapper{display:flex;flex-direction:column;max-height:75vh;overflow:auto}.vot-dialog-header-container{flex-shrink:0;align-items:flex-start;display:flex;min-height:31px}.vot-dialog-header-container:empty{padding:0 0 20px 0}.vot-dialog-header-container>.vot-icon-button{margin-inline-end:4px;margin-top:4px}.vot-dialog-title-container{display:flex;flex:1;font-size:inherit;font-weight:inherit;margin:0;outline:0}.vot-dialog-title{flex:1;font-size:115.3846153846%;font-weight:bold;line-height:1;padding-bottom:16px;padding-inline-end:20px;padding-inline-start:20px;padding-top:20px}.vot-dialog-body-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;flex-direction:column;min-height:1.375rem;overflow:auto;padding:0 20px;gap:16px;overscroll-behavior:contain;scrollbar-color:rgba(var(--vot-helper-onsurface-rgb), 0.1) var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar,.vot-dialog-body-container::-webkit-scrollbar-track{height:12px !important;width:12px !important;background:var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb{background:rgba(var(--vot-helper-onsurface-rgb), 0.1) !important;-webkit-border-radius:1ex !important;border:5px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-thumb:hover{border:3px solid var(--vot-helper-surface) !important}.vot-dialog-body-container::-webkit-scrollbar-corner{background:var(--vot-helper-surface) !important}.vot-dialog-footer-container{flex-shrink:0;display:flex;justify-content:flex-end;padding-bottom:16px;padding-inline-end:16px;padding-inline-start:16px;padding-top:16px}.vot-dialog-footer-container:empty{padding:20px 0 0 0}.vot-subtitles-widget{display:flex;justify-content:center;align-items:center;position:absolute;width:50%;max-height:100%;min-height:20%;z-index:2147483647;left:25%;top:75%;pointer-events:none}.vot-subtitles{--vot-subtitles-background: rgba( var(--vot-surface-rgb, 46, 47, 52), var(--vot-subtitles-opacity, 0.8) );position:relative;max-width:100%;max-height:100%;width:max-content;background:var(--vot-subtitles-background, rgba(46, 47, 52, 0.8));color:var(--vot-subtitles-color, rgb(227, 227, 227));border-radius:.5em;pointer-events:all;padding:.5em;font-size:20px;line-height:normal;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.vot-subtitles span{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.vot-subtitles .passed{color:var(--vot-subtitles-passed-color, rgb(33, 150, 243))}:root{--vot-font-family: "Roboto", "Segoe UI", BlinkMacSystemFont, system-ui, -apple-system;--vot-primary-rgb: 139, 180, 245;--vot-onprimary-rgb: 32, 33, 36;--vot-surface-rgb: 32, 33, 36;--vot-onsurface-rgb: 227, 227, 227;--vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227));--vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243))}vot-block{display:block;visibility:visible !important}`, ""]); // Exports /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); @@ -555,137 +560,6 @@ module.exports = function () { } -/***/ }), - -/***/ "./src/config/config.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ Cc: () => (/* binding */ yandexUserAgent), -/* harmony export */ JD: () => (/* binding */ defaultAutoVolume), -/* harmony export */ K2: () => (/* binding */ defaultDetectService), -/* harmony export */ Pm: () => (/* binding */ proxyWorkerHost), -/* harmony export */ QL: () => (/* binding */ detectUrls), -/* harmony export */ S7: () => (/* binding */ yandexHmacKey), -/* harmony export */ T8: () => (/* binding */ maxAudioVolume), -/* harmony export */ mE: () => (/* binding */ defaultTranslationService), -/* harmony export */ rl: () => (/* binding */ workerHost), -/* harmony export */ rw: () => (/* binding */ translateUrls), -/* harmony export */ se: () => (/* binding */ m3u8ProxyHost) -/* harmony export */ }); -// CONFIGURATION -const workerHost = "api.browser.yandex.ru"; -const m3u8ProxyHost = "m3u8-proxy.toil.cc"; // used for streaming -const proxyWorkerHost = "vot-worker.toil.cc"; // used for cloudflare version -const yandexHmacKey = "bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf"; -const yandexUserAgent = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 YaBrowser/24.4.0.0 Safari/537.36"; -const defaultAutoVolume = 0.15; // 0.0 - 1.0 (0% - 100%) - default volume of the video with the translation -const maxAudioVolume = 900; -const defaultTranslationService = "yandex"; -const defaultDetectService = "yandex"; - -const detectUrls = { - yandex: "https://translate.toil.cc/detect", - rustServer: "https://rust-server-531j.onrender.com/detect", -}; - -const translateUrls = { - yandex: "https://translate.toil.cc/translate", - deepl: "https://translate-deepl.toil.cc/translate", -}; - - - - -/***/ }), - -/***/ "./src/utils/debug.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -const debug = {}; -debug.log = (...text) => { - if (true) { - return; - } - return console.log( - "%c[VOT DEBUG]", - "background: #F2452D; color: #fff; padding: 5px;", - ...text, - ); -}; - -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (debug); - - -/***/ }), - -/***/ "./src/yandexRequest.js": -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _config_config_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/config/config.js"); -/* harmony import */ var _utils_debug_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/utils/debug.js"); - - - -async function yandexRequest(path, body, headers, callback) { - try { - _utils_debug_js__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.log("yandexRequest:", path); - // Create a fetch options object with headers and body - const options = { - url: `https://${_config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .workerHost */ .rl}${path}`, - method: "POST", - headers: { - ...{ - Accept: "application/x-protobuf", - "Accept-Language": "en", - "Content-Type": "application/x-protobuf", - "User-Agent": _config_config_js__WEBPACK_IMPORTED_MODULE_0__/* .yandexUserAgent */ .Cc, - Pragma: "no-cache", - "Cache-Control": "no-cache", - "Sec-Fetch-Mode": "no-cors", - "sec-ch-ua": null, - "sec-ch-ua-mobile": null, - "sec-ch-ua-platform": null, - }, - ...headers, - }, - binary: true, - data: new Blob([body]), - responseType: "arraybuffer", - }; - // Send the request using GM_xmlhttpRequest - GM_xmlhttpRequest({ - ...options, - onload: (http) => { - _utils_debug_js__WEBPACK_IMPORTED_MODULE_1__/* ["default"] */ .A.log("yandexRequest:", http.status, http); - callback(http.status === 200, http.response); - }, - onerror: (error) => { - console.error("[VOT]", error); - callback(false); - }, - }); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} - -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (yandexRequest); - - /***/ }) /******/ }); @@ -744,17 +618,6 @@ async function yandexRequest(path, body, headers, callback) { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ /******/ /* webpack/runtime/nonce */ /******/ (() => { /******/ __webpack_require__.nc = undefined; @@ -766,937 +629,1935 @@ var __webpack_exports__ = {}; (() => { "use strict"; -;// CONCATENATED MODULE: ./src/config/alternativeUrls.js -// Sites host Invidious. I tested the performance only on invidious.kevin.rocks, youtu.be and inv.vern.cc -const sitesInvidious = [ - "invidious.snopyta.org", - "yewtu.be", - "invidious.kavin.rocks", - "vid.puffyan.us", - "invidious.namazso.eu", - "inv.riverside.rocks", - "yt.artemislena.eu", - "invidious.flokinet.to", - "invidious.esmailelbob.xyz", - "y.com.sb", - "invidious.nerdvpn.de", - "inv.vern.cc", - "invidious.slipfox.xyz", - "invidio.xamh.de", - "invidious.dhusch.de", -]; - -// Sites host Piped. I tested the performance only on piped.video -const sitesPiped = [ - "piped.video", - "piped.tokhmi.xyz", - "piped.moomoo.me", - "piped.syncpundit.io", - "piped.mha.fi", - "watch.whatever.social", - "piped.garudalinux.org", - "efy.piped.pages.dev", - "watch.leptons.xyz", - "piped.lunar.icu", - "yt.dc09.ru", - "piped.mint.lgbt", - "il.ax", - "piped.privacy.com.de", - "piped.esmailelbob.xyz", - "piped.projectsegfau.lt", - "piped.in.projectsegfau.lt", - "piped.us.projectsegfau.lt", - "piped.privacydev.net", - "piped.palveluntarjoaja.eu", - "piped.smnz.de", - "piped.adminforge.de", - "piped.qdi.fi", - "piped.hostux.net", - "piped.chauvet.pro", - "piped.jotoma.de", - "piped.pfcd.me", - "piped.frontendfriendly.xyz", -]; - -const sitesProxiTok = [ - "proxitok.pabloferreiro.es", - "proxitok.pussthecat.org", - "tok.habedieeh.re", - "proxitok.esmailelbob.xyz", - "proxitok.privacydev.net", - "tok.artemislena.eu", - "tok.adminforge.de", - "tik.hostux.net", // maybe instance doesn't working - "tt.vern.cc", - "cringe.whatever.social", - "proxitok.lunar.icu", - "proxitok.privacy.com.de", // maybe instance doesn't working -]; - -// Sites host Peertube. I tested the performance only on dalek.zone and tube.shanti.cafe -const sitesPeertube = [ - "peertube.1312.media", - "tube.shanti.cafe", - "bee-tube.fr", - "video.sadmin.io", - "dalek.zone", - "review.peertube.biz", - "peervideo.club", - "tube.la-dina.net", - "peertube.tmp.rcp.tf", - "peertube.su", -]; - - - -// EXTERNAL MODULE: ./src/config/config.js -var config = __webpack_require__("./src/config/config.js"); -;// CONCATENATED MODULE: ./src/config/constants.js -// available languages for translation -const availableLangs = [ - "ru", - "en", - "zh", - "ko", - "lt", - "lv", - "ar", - "fr", - "it", - "es", - "de", - "ja", -]; - -// up-to-date list of TTS working languages -const actualTTS = ["ru", "en", "kk"]; - -const cfOnlyExtensions = [ - "Violentmonkey", - "FireMonkey", - "Greasemonkey", - "AdGuard", - "OrangeMonkey", -]; - - - -;// CONCATENATED MODULE: ./src/localization/locales/en.json -const en_namespaceObject = /*#__PURE__*/JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","neededAdditionalExtension":"An additional extension is needed to support this site","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyAccessTokenExpired":"Your entered Udemy Access Token has expired","udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","enterUdemyAccessToken":"Enter Udemy Access Token","VOTUdemyData":"Udemy Data","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase"}'); -// EXTERNAL MODULE: ./src/utils/debug.js -var debug = __webpack_require__("./src/utils/debug.js"); -;// CONCATENATED MODULE: ./src/utils/storage.js - - -const votStorage = new (class { - constructor() { - this.gmSupport = typeof GM_getValue === "function"; - debug/* default */.A.log(`GM Storage Status: ${this.gmSupport}`); - } - - syncGet(name, def = undefined, toNumber = false) { - if (this.gmSupport) { - return GM_getValue(name, def); - } - - let val = window.localStorage.getItem(name); - if (name === "udemyData" && typeof val === "string") { - try { - val = JSON.parse(val); - } catch { - val = def; - } - } - - return toNumber ? Number(val) ?? Number(def) : val ?? def; - } - - async get(name, def = undefined, toNumber = false) { - if (this.gmSupport) { - return await GM_getValue(name, def); - } - - return Promise.resolve(this.syncGet(name, def, toNumber)); - } - - syncSet(name, value) { - if (this.gmSupport) { - return GM_setValue(name, value); - } - - if (name === "udemyData") { - value = JSON.stringify(value); - } - - return window.localStorage.setItem(name, value); - } - - async set(name, value) { - if (this.gmSupport) { - return await GM_setValue(name, value); - } - - return Promise.resolve(this.syncSet(name, value)); - } - - syncDelete(name) { - if (this.gmSupport) { - return GM_deleteValue(name); - } - - return window.localStorage.removeItem(name); - } - - async delete(name) { - if (this.gmSupport) { - return await GM_deleteValue(name); +// EXTERNAL MODULE: ./node_modules/bowser/es5.js +var es5 = __webpack_require__("./node_modules/bowser/es5.js"); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/protos/yandex.js +const _m0 = protobuf; +const protobufPackage = ""; +var StreamInterval; +(function (StreamInterval) { + StreamInterval[StreamInterval["NO_CONNECTION"] = 0] = "NO_CONNECTION"; + StreamInterval[StreamInterval["TRANSLATING"] = 10] = "TRANSLATING"; + StreamInterval[StreamInterval["STREAMING"] = 20] = "STREAMING"; + StreamInterval[StreamInterval["UNRECOGNIZED"] = -1] = "UNRECOGNIZED"; +})(StreamInterval || (StreamInterval = {})); +function streamIntervalFromJSON(object) { + switch (object) { + case 0: + case "NO_CONNECTION": + return StreamInterval.NO_CONNECTION; + case 10: + case "TRANSLATING": + return StreamInterval.TRANSLATING; + case 20: + case "STREAMING": + return StreamInterval.STREAMING; + case -1: + case "UNRECOGNIZED": + default: + return StreamInterval.UNRECOGNIZED; } - - return Promise.resolve(this.syncDelete(name)); - } - - syncList() { - if (this.gmSupport) { - return GM_listValues(); +} +function streamIntervalToJSON(object) { + switch (object) { + case StreamInterval.NO_CONNECTION: + return "NO_CONNECTION"; + case StreamInterval.TRANSLATING: + return "TRANSLATING"; + case StreamInterval.STREAMING: + return "STREAMING"; + case StreamInterval.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; } - - return [ - "autoTranslate", - "dontTranslateLanguage", - "dontTranslateYourLang", - "autoSetVolumeYandexStyle", - "showVideoSlider", - "syncVolume", - "subtitlesMaxLength", - "highlightWords", - "responseLanguage", - "defaultVolume", - "udemyData", - "audioProxy", - "showPiPButton", - "locale-version", - "locale-lang", - "locale-phrases", - ]; - } - - async list() { - if (this.gmSupport) { - return await GM_listValues(); - } - - return Promise.resolve(this.syncList()); - } -})(); - -;// CONCATENATED MODULE: ./src/utils/translateApis.js - - - -const HTTP_TIMEOUT = 3000; - -async function fetchWithTimeout(url, options = {}) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), HTTP_TIMEOUT); - - try { - return await fetch(url, { - ...options, - signal: controller.signal, - }); - } catch (error) { - console.error("Fetch timed-out. Error:", error); - return error; - } finally { - clearTimeout(timeoutId); - } } - -const YandexTranslateAPI = { - async translate(text, lang) { - // Limit: 10k symbols - // - // Lang examples: - // en-ru, uk-ru, ru-en... - // ru, en (instead of auto-ru, auto-en) - - try { - const response = await fetchWithTimeout( - `${config/* translateUrls */.rw.yandex}?${new URLSearchParams({ - text, - lang, - })}`, - ); - - if (response instanceof Error) { - throw response; - } - - const content = await response.json(); - - if (content.code !== 200) { - throw content.message; - } - - return content.text[0]; - } catch (error) { - console.error("Error translating text:", error); - return text; - } - }, - - async detect(text) { - // Limit: 10k symbols - try { - const response = await fetchWithTimeout( - `${config/* detectUrls */.QL.yandex}?${new URLSearchParams({ - text, - })}`, - ); - - if (response instanceof Error) { - throw response; - } - - const content = await response.json(); - if (content.code !== 200) { - throw content.message; - } - - return content.lang ?? "en"; - } catch (error) { - console.error("Error getting lang from text:", error); - return "en"; - } - }, +function createBaseVideoTranslationHelpObject() { + return { target: "", targetUrl: "" }; +} +const VideoTranslationHelpObject = { + encode(message, writer = _m0.Writer.create()) { + if (message.target !== "") { + writer.uint32(10).string(message.target); + } + if (message.targetUrl !== "") { + writer.uint32(18).string(message.targetUrl); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTranslationHelpObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.target = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.targetUrl = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + target: isSet(object.target) ? globalThis.String(object.target) : "", + targetUrl: isSet(object.targetUrl) ? globalThis.String(object.targetUrl) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.target !== "") { + obj.target = message.target; + } + if (message.targetUrl !== "") { + obj.targetUrl = message.targetUrl; + } + return obj; + }, + create(base) { + return VideoTranslationHelpObject.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseVideoTranslationHelpObject(); + message.target = object.target ?? ""; + message.targetUrl = object.targetUrl ?? ""; + return message; + }, +}; +function createBaseVideoTranslationRequest() { + return { + url: "", + deviceId: undefined, + firstRequest: false, + duration: 0, + unknown0: 0, + language: "", + forceSourceLang: false, + unknown1: 0, + translationHelp: [], + responseLanguage: "", + unknown2: 0, + unknown3: 0, + bypassCache: false, + }; +} +const VideoTranslationRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(26).string(message.url); + } + if (message.deviceId !== undefined) { + writer.uint32(34).string(message.deviceId); + } + if (message.firstRequest !== false) { + writer.uint32(40).bool(message.firstRequest); + } + if (message.duration !== 0) { + writer.uint32(49).double(message.duration); + } + if (message.unknown0 !== 0) { + writer.uint32(56).int32(message.unknown0); + } + if (message.language !== "") { + writer.uint32(66).string(message.language); + } + if (message.forceSourceLang !== false) { + writer.uint32(72).bool(message.forceSourceLang); + } + if (message.unknown1 !== 0) { + writer.uint32(80).int32(message.unknown1); + } + for (const v of message.translationHelp) { + VideoTranslationHelpObject.encode(v, writer.uint32(90).fork()).ldelim(); + } + if (message.responseLanguage !== "") { + writer.uint32(114).string(message.responseLanguage); + } + if (message.unknown2 !== 0) { + writer.uint32(120).int32(message.unknown2); + } + if (message.unknown3 !== 0) { + writer.uint32(128).int32(message.unknown3); + } + if (message.bypassCache !== false) { + writer.uint32(136).bool(message.bypassCache); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTranslationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 3: + if (tag !== 26) { + break; + } + message.url = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + message.deviceId = reader.string(); + continue; + case 5: + if (tag !== 40) { + break; + } + message.firstRequest = reader.bool(); + continue; + case 6: + if (tag !== 49) { + break; + } + message.duration = reader.double(); + continue; + case 7: + if (tag !== 56) { + break; + } + message.unknown0 = reader.int32(); + continue; + case 8: + if (tag !== 66) { + break; + } + message.language = reader.string(); + continue; + case 9: + if (tag !== 72) { + break; + } + message.forceSourceLang = reader.bool(); + continue; + case 10: + if (tag !== 80) { + break; + } + message.unknown1 = reader.int32(); + continue; + case 11: + if (tag !== 90) { + break; + } + message.translationHelp.push(VideoTranslationHelpObject.decode(reader, reader.uint32())); + continue; + case 14: + if (tag !== 114) { + break; + } + message.responseLanguage = reader.string(); + continue; + case 15: + if (tag !== 120) { + break; + } + message.unknown2 = reader.int32(); + continue; + case 16: + if (tag !== 128) { + break; + } + message.unknown3 = reader.int32(); + continue; + case 17: + if (tag !== 136) { + break; + } + message.bypassCache = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + deviceId: isSet(object.deviceId) ? globalThis.String(object.deviceId) : undefined, + firstRequest: isSet(object.firstRequest) ? globalThis.Boolean(object.firstRequest) : false, + duration: isSet(object.duration) ? globalThis.Number(object.duration) : 0, + unknown0: isSet(object.unknown0) ? globalThis.Number(object.unknown0) : 0, + language: isSet(object.language) ? globalThis.String(object.language) : "", + forceSourceLang: isSet(object.forceSourceLang) ? globalThis.Boolean(object.forceSourceLang) : false, + unknown1: isSet(object.unknown1) ? globalThis.Number(object.unknown1) : 0, + translationHelp: globalThis.Array.isArray(object?.translationHelp) + ? object.translationHelp.map((e) => VideoTranslationHelpObject.fromJSON(e)) + : [], + responseLanguage: isSet(object.responseLanguage) ? globalThis.String(object.responseLanguage) : "", + unknown2: isSet(object.unknown2) ? globalThis.Number(object.unknown2) : 0, + unknown3: isSet(object.unknown3) ? globalThis.Number(object.unknown3) : 0, + bypassCache: isSet(object.bypassCache) ? globalThis.Boolean(object.bypassCache) : false, + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.deviceId !== undefined) { + obj.deviceId = message.deviceId; + } + if (message.firstRequest !== false) { + obj.firstRequest = message.firstRequest; + } + if (message.duration !== 0) { + obj.duration = message.duration; + } + if (message.unknown0 !== 0) { + obj.unknown0 = Math.round(message.unknown0); + } + if (message.language !== "") { + obj.language = message.language; + } + if (message.forceSourceLang !== false) { + obj.forceSourceLang = message.forceSourceLang; + } + if (message.unknown1 !== 0) { + obj.unknown1 = Math.round(message.unknown1); + } + if (message.translationHelp?.length) { + obj.translationHelp = message.translationHelp.map((e) => VideoTranslationHelpObject.toJSON(e)); + } + if (message.responseLanguage !== "") { + obj.responseLanguage = message.responseLanguage; + } + if (message.unknown2 !== 0) { + obj.unknown2 = Math.round(message.unknown2); + } + if (message.unknown3 !== 0) { + obj.unknown3 = Math.round(message.unknown3); + } + if (message.bypassCache !== false) { + obj.bypassCache = message.bypassCache; + } + return obj; + }, + create(base) { + return VideoTranslationRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseVideoTranslationRequest(); + message.url = object.url ?? ""; + message.deviceId = object.deviceId ?? undefined; + message.firstRequest = object.firstRequest ?? false; + message.duration = object.duration ?? 0; + message.unknown0 = object.unknown0 ?? 0; + message.language = object.language ?? ""; + message.forceSourceLang = object.forceSourceLang ?? false; + message.unknown1 = object.unknown1 ?? 0; + message.translationHelp = object.translationHelp?.map((e) => VideoTranslationHelpObject.fromPartial(e)) || []; + message.responseLanguage = object.responseLanguage ?? ""; + message.unknown2 = object.unknown2 ?? 0; + message.unknown3 = object.unknown3 ?? 0; + message.bypassCache = object.bypassCache ?? false; + return message; + }, +}; +function createBaseVideoTranslationResponse() { + return { + url: undefined, + duration: undefined, + status: 0, + remainingTime: undefined, + unknown0: undefined, + translationId: "", + language: undefined, + message: undefined, + }; +} +const VideoTranslationResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== undefined) { + writer.uint32(10).string(message.url); + } + if (message.duration !== undefined) { + writer.uint32(17).double(message.duration); + } + if (message.status !== 0) { + writer.uint32(32).int32(message.status); + } + if (message.remainingTime !== undefined) { + writer.uint32(40).int32(message.remainingTime); + } + if (message.unknown0 !== undefined) { + writer.uint32(48).int32(message.unknown0); + } + if (message.translationId !== "") { + writer.uint32(58).string(message.translationId); + } + if (message.language !== undefined) { + writer.uint32(66).string(message.language); + } + if (message.message !== undefined) { + writer.uint32(74).string(message.message); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVideoTranslationResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + message.duration = reader.double(); + continue; + case 4: + if (tag !== 32) { + break; + } + message.status = reader.int32(); + continue; + case 5: + if (tag !== 40) { + break; + } + message.remainingTime = reader.int32(); + continue; + case 6: + if (tag !== 48) { + break; + } + message.unknown0 = reader.int32(); + continue; + case 7: + if (tag !== 58) { + break; + } + message.translationId = reader.string(); + continue; + case 8: + if (tag !== 66) { + break; + } + message.language = reader.string(); + continue; + case 9: + if (tag !== 74) { + break; + } + message.message = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : undefined, + duration: isSet(object.duration) ? globalThis.Number(object.duration) : undefined, + status: isSet(object.status) ? globalThis.Number(object.status) : 0, + remainingTime: isSet(object.remainingTime) ? globalThis.Number(object.remainingTime) : undefined, + unknown0: isSet(object.unknown0) ? globalThis.Number(object.unknown0) : undefined, + translationId: isSet(object.translationId) ? globalThis.String(object.translationId) : "", + language: isSet(object.language) ? globalThis.String(object.language) : undefined, + message: isSet(object.message) ? globalThis.String(object.message) : undefined, + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== undefined) { + obj.url = message.url; + } + if (message.duration !== undefined) { + obj.duration = message.duration; + } + if (message.status !== 0) { + obj.status = Math.round(message.status); + } + if (message.remainingTime !== undefined) { + obj.remainingTime = Math.round(message.remainingTime); + } + if (message.unknown0 !== undefined) { + obj.unknown0 = Math.round(message.unknown0); + } + if (message.translationId !== "") { + obj.translationId = message.translationId; + } + if (message.language !== undefined) { + obj.language = message.language; + } + if (message.message !== undefined) { + obj.message = message.message; + } + return obj; + }, + create(base) { + return VideoTranslationResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseVideoTranslationResponse(); + message.url = object.url ?? undefined; + message.duration = object.duration ?? undefined; + message.status = object.status ?? 0; + message.remainingTime = object.remainingTime ?? undefined; + message.unknown0 = object.unknown0 ?? undefined; + message.translationId = object.translationId ?? ""; + message.language = object.language ?? undefined; + message.message = object.message ?? undefined; + return message; + }, +}; +function createBaseSubtitlesObject() { + return { language: "", url: "", unknown0: 0, translatedLanguage: "", translatedUrl: "", unknown1: 0, unknown2: 0 }; +} +const SubtitlesObject = { + encode(message, writer = _m0.Writer.create()) { + if (message.language !== "") { + writer.uint32(10).string(message.language); + } + if (message.url !== "") { + writer.uint32(18).string(message.url); + } + if (message.unknown0 !== 0) { + writer.uint32(24).int32(message.unknown0); + } + if (message.translatedLanguage !== "") { + writer.uint32(34).string(message.translatedLanguage); + } + if (message.translatedUrl !== "") { + writer.uint32(42).string(message.translatedUrl); + } + if (message.unknown1 !== 0) { + writer.uint32(48).int32(message.unknown1); + } + if (message.unknown2 !== 0) { + writer.uint32(56).int32(message.unknown2); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSubtitlesObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.language = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.url = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + message.unknown0 = reader.int32(); + continue; + case 4: + if (tag !== 34) { + break; + } + message.translatedLanguage = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + message.translatedUrl = reader.string(); + continue; + case 6: + if (tag !== 48) { + break; + } + message.unknown1 = reader.int32(); + continue; + case 7: + if (tag !== 56) { + break; + } + message.unknown2 = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + language: isSet(object.language) ? globalThis.String(object.language) : "", + url: isSet(object.url) ? globalThis.String(object.url) : "", + unknown0: isSet(object.unknown0) ? globalThis.Number(object.unknown0) : 0, + translatedLanguage: isSet(object.translatedLanguage) ? globalThis.String(object.translatedLanguage) : "", + translatedUrl: isSet(object.translatedUrl) ? globalThis.String(object.translatedUrl) : "", + unknown1: isSet(object.unknown1) ? globalThis.Number(object.unknown1) : 0, + unknown2: isSet(object.unknown2) ? globalThis.Number(object.unknown2) : 0, + }; + }, + toJSON(message) { + const obj = {}; + if (message.language !== "") { + obj.language = message.language; + } + if (message.url !== "") { + obj.url = message.url; + } + if (message.unknown0 !== 0) { + obj.unknown0 = Math.round(message.unknown0); + } + if (message.translatedLanguage !== "") { + obj.translatedLanguage = message.translatedLanguage; + } + if (message.translatedUrl !== "") { + obj.translatedUrl = message.translatedUrl; + } + if (message.unknown1 !== 0) { + obj.unknown1 = Math.round(message.unknown1); + } + if (message.unknown2 !== 0) { + obj.unknown2 = Math.round(message.unknown2); + } + return obj; + }, + create(base) { + return SubtitlesObject.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseSubtitlesObject(); + message.language = object.language ?? ""; + message.url = object.url ?? ""; + message.unknown0 = object.unknown0 ?? 0; + message.translatedLanguage = object.translatedLanguage ?? ""; + message.translatedUrl = object.translatedUrl ?? ""; + message.unknown1 = object.unknown1 ?? 0; + message.unknown2 = object.unknown2 ?? 0; + return message; + }, +}; +function createBaseSubtitlesRequest() { + return { url: "", language: "" }; +} +const SubtitlesRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + if (message.language !== "") { + writer.uint32(18).string(message.language); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSubtitlesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.language = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + language: isSet(object.language) ? globalThis.String(object.language) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.language !== "") { + obj.language = message.language; + } + return obj; + }, + create(base) { + return SubtitlesRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseSubtitlesRequest(); + message.url = object.url ?? ""; + message.language = object.language ?? ""; + return message; + }, +}; +function createBaseSubtitlesResponse() { + return { waiting: false, subtitles: [] }; +} +const SubtitlesResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.waiting !== false) { + writer.uint32(8).bool(message.waiting); + } + for (const v of message.subtitles) { + SubtitlesObject.encode(v, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSubtitlesResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + message.waiting = reader.bool(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.subtitles.push(SubtitlesObject.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + waiting: isSet(object.waiting) ? globalThis.Boolean(object.waiting) : false, + subtitles: globalThis.Array.isArray(object?.subtitles) + ? object.subtitles.map((e) => SubtitlesObject.fromJSON(e)) + : [], + }; + }, + toJSON(message) { + const obj = {}; + if (message.waiting !== false) { + obj.waiting = message.waiting; + } + if (message.subtitles?.length) { + obj.subtitles = message.subtitles.map((e) => SubtitlesObject.toJSON(e)); + } + return obj; + }, + create(base) { + return SubtitlesResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseSubtitlesResponse(); + message.waiting = object.waiting ?? false; + message.subtitles = object.subtitles?.map((e) => SubtitlesObject.fromPartial(e)) || []; + return message; + }, +}; +function createBaseStreamTranslationObject() { + return { url: "", timestamp: "" }; +} +const StreamTranslationObject = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + if (message.timestamp !== "") { + writer.uint32(18).string(message.timestamp); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamTranslationObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.timestamp = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + timestamp: isSet(object.timestamp) ? globalThis.String(object.timestamp) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.timestamp !== "") { + obj.timestamp = message.timestamp; + } + return obj; + }, + create(base) { + return StreamTranslationObject.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamTranslationObject(); + message.url = object.url ?? ""; + message.timestamp = object.timestamp ?? ""; + return message; + }, +}; +function createBaseStreamTranslationRequest() { + return { url: "", language: "", responseLanguage: "" }; +} +const StreamTranslationRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.url !== "") { + writer.uint32(10).string(message.url); + } + if (message.language !== "") { + writer.uint32(18).string(message.language); + } + if (message.responseLanguage !== "") { + writer.uint32(26).string(message.responseLanguage); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamTranslationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.url = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.language = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + message.responseLanguage = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + url: isSet(object.url) ? globalThis.String(object.url) : "", + language: isSet(object.language) ? globalThis.String(object.language) : "", + responseLanguage: isSet(object.responseLanguage) ? globalThis.String(object.responseLanguage) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.url !== "") { + obj.url = message.url; + } + if (message.language !== "") { + obj.language = message.language; + } + if (message.responseLanguage !== "") { + obj.responseLanguage = message.responseLanguage; + } + return obj; + }, + create(base) { + return StreamTranslationRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamTranslationRequest(); + message.url = object.url ?? ""; + message.language = object.language ?? ""; + message.responseLanguage = object.responseLanguage ?? ""; + return message; + }, +}; +function createBaseStreamTranslationResponse() { + return { interval: 0, translatedInfo: undefined, pingId: undefined }; +} +const StreamTranslationResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.interval !== 0) { + writer.uint32(8).int32(message.interval); + } + if (message.translatedInfo !== undefined) { + StreamTranslationObject.encode(message.translatedInfo, writer.uint32(18).fork()).ldelim(); + } + if (message.pingId !== undefined) { + writer.uint32(24).int32(message.pingId); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamTranslationResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + message.interval = reader.int32(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.translatedInfo = StreamTranslationObject.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 24) { + break; + } + message.pingId = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + interval: isSet(object.interval) ? streamIntervalFromJSON(object.interval) : 0, + translatedInfo: isSet(object.translatedInfo) + ? StreamTranslationObject.fromJSON(object.translatedInfo) + : undefined, + pingId: isSet(object.pingId) ? globalThis.Number(object.pingId) : undefined, + }; + }, + toJSON(message) { + const obj = {}; + if (message.interval !== 0) { + obj.interval = streamIntervalToJSON(message.interval); + } + if (message.translatedInfo !== undefined) { + obj.translatedInfo = StreamTranslationObject.toJSON(message.translatedInfo); + } + if (message.pingId !== undefined) { + obj.pingId = Math.round(message.pingId); + } + return obj; + }, + create(base) { + return StreamTranslationResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamTranslationResponse(); + message.interval = object.interval ?? 0; + message.translatedInfo = (object.translatedInfo !== undefined && object.translatedInfo !== null) + ? StreamTranslationObject.fromPartial(object.translatedInfo) + : undefined; + message.pingId = object.pingId ?? undefined; + return message; + }, +}; +function createBaseStreamPingRequest() { + return { pingId: 0 }; +} +const StreamPingRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.pingId !== 0) { + writer.uint32(8).int32(message.pingId); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStreamPingRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + message.pingId = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { pingId: isSet(object.pingId) ? globalThis.Number(object.pingId) : 0 }; + }, + toJSON(message) { + const obj = {}; + if (message.pingId !== 0) { + obj.pingId = Math.round(message.pingId); + } + return obj; + }, + create(base) { + return StreamPingRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseStreamPingRequest(); + message.pingId = object.pingId ?? 0; + return message; + }, }; - -const RustServerAPI = { - async detect(text) { - try { - const response = await fetch(config/* detectUrls */.QL.rustServer, { - method: "POST", - body: text, - }); - - if (response instanceof Error) { - throw response; - } - - return await response.text(); - } catch (error) { - console.error("Error getting lang from text:", error); - return "en"; - } - }, +function createBaseYandexSessionRequest() { + return { uuid: "", module: "" }; +} +const YandexSessionRequest = { + encode(message, writer = _m0.Writer.create()) { + if (message.uuid !== "") { + writer.uint32(10).string(message.uuid); + } + if (message.module !== "") { + writer.uint32(18).string(message.module); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseYandexSessionRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.uuid = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + message.module = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + uuid: isSet(object.uuid) ? globalThis.String(object.uuid) : "", + module: isSet(object.module) ? globalThis.String(object.module) : "", + }; + }, + toJSON(message) { + const obj = {}; + if (message.uuid !== "") { + obj.uuid = message.uuid; + } + if (message.module !== "") { + obj.module = message.module; + } + return obj; + }, + create(base) { + return YandexSessionRequest.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseYandexSessionRequest(); + message.uuid = object.uuid ?? ""; + message.module = object.module ?? ""; + return message; + }, +}; +function createBaseYandexSessionResponse() { + return { secretKey: "", expires: 0 }; +} +const YandexSessionResponse = { + encode(message, writer = _m0.Writer.create()) { + if (message.secretKey !== "") { + writer.uint32(10).string(message.secretKey); + } + if (message.expires !== 0) { + writer.uint32(16).int32(message.expires); + } + return writer; + }, + decode(input, length) { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseYandexSessionResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + message.secretKey = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + message.expires = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + fromJSON(object) { + return { + secretKey: isSet(object.secretKey) ? globalThis.String(object.secretKey) : "", + expires: isSet(object.expires) ? globalThis.Number(object.expires) : 0, + }; + }, + toJSON(message) { + const obj = {}; + if (message.secretKey !== "") { + obj.secretKey = message.secretKey; + } + if (message.expires !== 0) { + obj.expires = Math.round(message.expires); + } + return obj; + }, + create(base) { + return YandexSessionResponse.fromPartial(base ?? {}); + }, + fromPartial(object) { + const message = createBaseYandexSessionResponse(); + message.secretKey = object.secretKey ?? ""; + message.expires = object.expires ?? 0; + return message; + }, }; +function isSet(value) { + return value !== null && value !== undefined; +} -const DeeplServerAPI = { - async translate(text, fromLang = "auto", toLang = "ru") { - try { - const response = await fetchWithTimeout(config/* translateUrls */.rw.deepl, { - method: "POST", - headers: { - "content-type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams({ - text, - source_lang: fromLang, - target_lang: toLang, - }), - }); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/protobuf.js - if (response instanceof Error) { - throw response; - } +const yandexProtobuf = { + encodeTranslationRequest(url, duration, requestLang, responseLang, translationHelp) { + return VideoTranslationRequest.encode({ + url, + firstRequest: true, + duration, + unknown0: 1, + language: requestLang, + forceSourceLang: false, + unknown1: 0, + translationHelp: translationHelp ? translationHelp : [], + responseLanguage: responseLang, + unknown2: 0, + unknown3: 1, + bypassCache: false, + }).finish(); + }, + decodeTranslationResponse(response) { + return VideoTranslationResponse.decode(new Uint8Array(response)); + }, + encodeSubtitlesRequest(url, requestLang) { + return SubtitlesRequest.encode({ + url, + language: requestLang, + }).finish(); + }, + decodeSubtitlesResponse(response) { + return SubtitlesResponse.decode(new Uint8Array(response)); + }, + encodeStreamPingRequest(pingId) { + return StreamPingRequest.encode({ + pingId, + }).finish(); + }, + encodeStreamRequest(url, requestLang, responseLang) { + return StreamTranslationRequest.encode({ + url, + language: requestLang, + responseLanguage: responseLang, + }).finish(); + }, + decodeStreamResponse(response) { + return StreamTranslationResponse.decode(new Uint8Array(response)); + }, + encodeYandexSessionRequest(uuid, module) { + return YandexSessionRequest.encode({ + uuid, + module, + }).finish(); + }, + decodeYandexSessionResponse(response) { + return YandexSessionResponse.decode(new Uint8Array(response)); + }, +}; - const content = await response.json(); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/config/config.js +/* harmony default export */ const config = ({ + host: "api.browser.yandex.ru", + hostVOT: "vot-api.toil.cc/v1", + userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 YaBrowser/24.6.0.0 Safari/537.36", + componentVersion: "24.6.4.580", + hmac: "bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf", + defaultDuration: 343, +}); - if (content.code !== 200) { - throw content.message; - } +;// CONCATENATED MODULE: ./node_modules/vot.js/package.json +const package_namespaceObject = {"rE":"0.7.2"}; +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/secure.js - return content.data; - } catch (error) { - console.error("Error translating text:", error); - return text; +const utf8Encoder = new TextEncoder(); +async function signHMAC(hashName, hmac, data) { + const key = await crypto.subtle.importKey("raw", utf8Encoder.encode(hmac), { name: "HMAC", hash: { name: hashName } }, false, ["sign", "verify"]); + return await crypto.subtle.sign("HMAC", key, data); +} +async function getSignature(body) { + const signature = await signHMAC("SHA-256", config.hmac, body); + return new Uint8Array(signature).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); +} +function getUUID() { + const hexDigits = "0123456789ABCDEF"; + let uuid = ""; + for (let i = 0; i < 32; i++) { + const randomDigit = Math.floor(Math.random() * 16); + uuid += hexDigits[randomDigit]; } - }, -}; - -async function translate(text, fromLang = "", toLang = "ru") { - const service = await votStorage.get( - "translationService", - config/* defaultTranslationService */.mE, - ); - switch (service) { - case "yandex": { - const langPair = fromLang && toLang ? `${fromLang}-${toLang}` : toLang; - return await YandexTranslateAPI.translate(text, langPair); + return uuid; +} +async function getHmacSha1(hmacKey, salt) { + try { + const hmacSalt = utf8Encoder.encode(salt); + const signature = await signHMAC("SHA-1", hmacKey, hmacSalt); + return btoa(String.fromCharCode(...new Uint8Array(signature))); } - case "deepl": { - return await DeeplServerAPI.translate(text, fromLang, toLang); + catch (err) { + console.error(err); + return false; } - default: - return text; - } } -async function detect(text) { - const service = await votStorage.get("detectService", config/* defaultDetectService */.K2); - switch (service) { - case "yandex": - return await YandexTranslateAPI.detect(text); - case "rust-server": - return await RustServerAPI.detect(text); - default: - return "en"; - } +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/types/yandex.js +var VideoService; +(function (VideoService) { + VideoService["custom"] = "custom"; + VideoService["directlink"] = "custom"; + VideoService["youtube"] = "youtube"; + VideoService["piped"] = "piped"; + VideoService["invidious"] = "invidious"; + VideoService["vk"] = "vk"; + VideoService["nine_gag"] = "nine_gag"; + VideoService["gag"] = "nine_gag"; + VideoService["twitch"] = "twitch"; + VideoService["proxitok"] = "proxitok"; + VideoService["tiktok"] = "tiktok"; + VideoService["vimeo"] = "vimeo"; + VideoService["xvideos"] = "xvideos"; + VideoService["pornhub"] = "pornhub"; + VideoService["twitter"] = "twitter"; + VideoService["rumble"] = "rumble"; + VideoService["facebook"] = "facebook"; + VideoService["rutube"] = "rutube"; + VideoService["coub"] = "coub"; + VideoService["bilibili"] = "bilibili"; + VideoService["mail_ru"] = "mailru"; + VideoService["mailru"] = "mailru"; + VideoService["bitchute"] = "bitchute"; + VideoService["coursera"] = "coursera"; + VideoService["udemy"] = "udemy"; + VideoService["eporner"] = "eporner"; + VideoService["peertube"] = "peertube"; + VideoService["dailymotion"] = "dailymotion"; + VideoService["trovo"] = "trovo"; + VideoService["yandexdisk"] = "yandexdisk"; + VideoService["coursehunter"] = "coursehunter"; + VideoService["ok_ru"] = "okru"; + VideoService["okru"] = "okru"; + VideoService["googledrive"] = "googledrive"; + VideoService["bannedvideo"] = "bannedvideo"; + VideoService["weverse"] = "weverse"; + VideoService["newgrounds"] = "newgrounds"; + VideoService["egghead"] = "egghead"; + VideoService["youku"] = "youku"; + VideoService["archive"] = "archive"; + VideoService["kodik"] = "kodik"; + VideoService["patreon"] = "patreon"; + VideoService["reddit"] = "reddit"; + VideoService["kick"] = "kick"; + VideoService["apple_developer"] = "apple_developer"; + VideoService["appledeveloper"] = "apple_developer"; +})(VideoService || (VideoService = {})); +var VideoTranslationStatus; +(function (VideoTranslationStatus) { + VideoTranslationStatus[VideoTranslationStatus["FAILED"] = 0] = "FAILED"; + VideoTranslationStatus[VideoTranslationStatus["FINISHED"] = 1] = "FINISHED"; + VideoTranslationStatus[VideoTranslationStatus["WAITING"] = 2] = "WAITING"; + VideoTranslationStatus[VideoTranslationStatus["LONG_WAITING"] = 3] = "LONG_WAITING"; + VideoTranslationStatus[VideoTranslationStatus["PART_CONTENT"] = 5] = "PART_CONTENT"; + VideoTranslationStatus[VideoTranslationStatus["LONG_WAITING_2"] = 6] = "LONG_WAITING_2"; +})(VideoTranslationStatus || (VideoTranslationStatus = {})); + +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/utils.js + +async function fetchWithTimeout(url, options = { + headers: { + "User-Agent": config.userAgent, + }, +}) { + const { timeout = 3000 } = options; + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + const response = await fetch(url, { + ...options, + signal: controller.signal, + }); + clearTimeout(id); + return response; +} +function getTimestamp() { + return Math.floor(Date.now() / 1000); } -const translateServices = Object.keys(config/* translateUrls */.rw); -const detectServices = Object.keys(config/* detectUrls */.QL).map((k) => - k === "rustServer" ? "rust-server" : k, -); - - - -;// CONCATENATED MODULE: ./src/utils/youtubeUtils.js - - - - - -// Get the language code from the response or the text -async function getLanguage(player, response, title, description) { - if ( - !window.location.hostname.includes("m.youtube.com") && - player?.getAudioTrack - ) { - // ! Experimental ! get lang from selected audio track if availabled - const audioTracks = player.getAudioTrack(); - const trackInfo = audioTracks?.getLanguageInfo(); // get selected track info (id === "und" if tracks are not available) - if (trackInfo?.id !== "und") { - return langTo6391(trackInfo.id.split(".")[0]); - } - } +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/config/alternativeUrls.js +const sitesInvidious = [ + "yewtu.be", + "yt.artemislena.eu", + "invidious.flokinet.to", + "iv.melmac.space", + "inv.nadeko.net", + "inv.tux.pizza", + "invidious.private.coffee", + "yt.drgnz.club", + "vid.puffyan.us", + "invidious.dhusch.de", +]; +const sitesPiped = [ + "piped.video", + "piped.tokhmi.xyz", + "piped.moomoo.me", + "piped.syncpundit.io", + "piped.mha.fi", + "watch.whatever.social", + "piped.garudalinux.org", + "efy.piped.pages.dev", + "watch.leptons.xyz", + "piped.lunar.icu", + "yt.dc09.ru", + "piped.mint.lgbt", + "il.ax", + "piped.privacy.com.de", + "piped.esmailelbob.xyz", + "piped.projectsegfau.lt", + "piped.in.projectsegfau.lt", + "piped.us.projectsegfau.lt", + "piped.privacydev.net", + "piped.palveluntarjoaja.eu", + "piped.smnz.de", + "piped.adminforge.de", + "piped.qdi.fi", + "piped.hostux.net", + "piped.chauvet.pro", + "piped.jotoma.de", + "piped.pfcd.me", + "piped.frontendfriendly.xyz", +]; +const sitesProxiTok = [ + "proxitok.pabloferreiro.es", + "proxitok.pussthecat.org", + "tok.habedieeh.re", + "proxitok.esmailelbob.xyz", + "proxitok.privacydev.net", + "tok.artemislena.eu", + "tok.adminforge.de", + "tt.vern.cc", + "cringe.whatever.social", + "proxitok.lunar.icu", + "proxitok.privacy.com.de", +]; +const sitesPeertube = [ + "peertube.1312.media", + "tube.shanti.cafe", + "bee-tube.fr", + "video.sadmin.io", + "dalek.zone", + "review.peertube.biz", + "peervideo.club", + "tube.la-dina.net", + "peertube.tmp.rcp.tf", + "peertube.su", + "video.blender.org", +]; - // TODO: If the audio tracks will work fine, transfer the receipt of captions to the audioTracks variable - // Check if there is an automatic caption track in the response - const captionTracks = - response?.captions?.playerCaptionsTracklistRenderer?.captionTracks; - if (captionTracks?.length) { - const autoCaption = captionTracks.find((caption) => caption.kind === "asr"); - if (autoCaption && autoCaption.languageCode) { - return langTo6391(autoCaption.languageCode); - } - } - // If there is no caption track, use detect to get the language code from the description +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/config/sites.js - const text = cleanText(title, description); - debug/* default */.A.log(`Detecting language text: ${text}`); +/* harmony default export */ const sites = ([ + { + additionalData: "mobile", + host: VideoService.youtube, + url: "https://youtu.be/", + match: /^m.youtube.com$/, + selector: "shorts-video #player", + }, + { + additionalData: "mobile", + host: VideoService.youtube, + url: "https://youtu.be/", + match: /^m.youtube.com$/, + selector: ".player-container", + }, + { + host: VideoService.youtube, + url: "https://youtu.be/", + match: /^(www.)?youtube(-nocookie|kids)?.com$/, + selector: ".html5-video-container:not(#inline-player *)", + }, + { + host: VideoService.invidious, + url: "https://youtu.be/", + match: sitesInvidious, + selector: "#player", + }, + { + host: VideoService.piped, + url: "https://youtu.be/", + match: sitesPiped, + selector: ".shaka-video-container", + }, + { + additionalData: "mobile", + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^m.vk.(com|ru)$/, + selector: "vk-video-player", + shadowRoot: true, + }, + { + additionalData: "clips", + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^(www.|m.)?vk.(com|ru)$/, + selector: 'div[data-testid="clipcontainer-video"]', + }, + { + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^(www.|m.)?vk.(com|ru)$/, + selector: ".videoplayer_media", + }, + { + host: VideoService.nine_gag, + url: "https://9gag.com/gag/", + match: /^9gag.com$/, + selector: ".video-post", + }, + { + host: VideoService.twitch, + url: "https://twitch.tv/", + match: [ + /^m.twitch.tv$/, + /^(www.)?twitch.tv$/, + /^clips.twitch.tv$/, + /^player.twitch.tv$/, + ], + selector: ".video-ref, main > div > section > div > div > div", + }, + { + host: VideoService.proxitok, + url: "https://www.tiktok.com/", + match: sitesProxiTok, + selector: ".column.has-text-centered", + }, + { + host: VideoService.tiktok, + url: "https://www.tiktok.com/", + match: /^(www.)?tiktok.com$/, + selector: null, + }, + { + host: VideoService.vimeo, + url: "https://vimeo.com/", + match: /^vimeo.com$/, + selector: ".player", + }, + { + host: VideoService.vimeo, + url: "https://player.vimeo.com/", + match: /^player.vimeo.com$/, + selector: ".player", + }, + { + host: VideoService.xvideos, + url: "https://www.xvideos.com/", + match: /^(www.)?(xvideos|xv-ru).com$/, + selector: ".video-bg-pic", + }, + { + host: VideoService.pornhub, + url: "https://rt.pornhub.com/view_video.php?viewkey=", + match: /^[a-z]+.pornhub.com$/, + selector: ".mainPlayerDiv > .video-element-wrapper-js > div", + }, + { + additionalData: "embed", + host: VideoService.pornhub, + url: "https://rt.pornhub.com/view_video.php?viewkey=", + match: (url) => + url.host.includes("pornhub.com") && url.pathname.startsWith("/embed/"), + selector: "#player", + }, + { + host: VideoService.twitter, + url: "https://twitter.com/i/status/", + match: /^(twitter|x).com$/, + selector: 'div[data-testid="videoComponent"] > div:nth-child(1) > div', + }, + { + host: VideoService.rumble, + url: "https://rumble.com/", + match: /^rumble.com$/, + selector: "#videoPlayer > .videoPlayer-Rumble-cls > div", + }, + { + host: VideoService.facebook, + url: "https://facebook.com/", + match: (url) => + url.host.includes("facebook.com") && url.pathname.includes("/videos/"), + selector: 'div[role="main"] div[data-pagelet$="video" i]', + }, + { + additionalData: "reels", + host: VideoService.facebook, + url: "https://facebook.com/", + match: (url) => + url.host.includes("facebook.com") && url.pathname.includes("/reel/"), + selector: 'div[role="main"]', + }, + { + host: VideoService.rutube, + url: "https://rutube.ru/video/", + match: /^rutube.ru$/, + selector: ".video-player > div > div > div:nth-child(2)", + }, + { + additionalData: "embed", + host: VideoService.rutube, + url: "https://rutube.ru/video/", + match: /^rutube.ru$/, + selector: "#app > div > div", + }, + { + host: VideoService.bilibili, + url: "https://www.bilibili.com/video/", + match: /^(www|m|player).bilibili.com$/, + selector: ".bpx-player-video-wrap", + }, + // Добавляет лишние видео в обработчик + { + additionalData: "old", // /blackboard/webplayer/embed-old.html + host: VideoService.bilibili, + url: "https://www.bilibili.com/video/", + match: /^(www|m).bilibili.com$/, + selector: null, + }, + { + host: VideoService.mailru, + url: "https://my.mail.ru/", + match: /^my.mail.ru$/, + selector: "#b-video-wrapper", + }, + { + host: VideoService.bitchute, + url: "https://www.bitchute.com/video/", + match: /^(www.)?bitchute.com$/, + selector: ".video-js", + }, + { + // ONLY IF YOU LOGINED TO COURSERA /learn/NAME/lecture/XXXX + host: VideoService.coursera, + url: "https://www.coursera.org/", + match: /coursera.org$/, + selector: ".vjs-v6", + needExtraData: true, + }, + { + // ONLY IF YOU LOGINED TO UDEMY /course/NAME/learn/lecture/XXXX + host: VideoService.udemy, + url: "https://www.udemy.com/", + match: /udemy.com$/, + selector: + 'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)', + needExtraData: true, + }, + { + host: VideoService.eporner, + url: "https://www.eporner.com/", + match: /^(www.)?eporner.com$/, + selector: ".vjs-v7", + }, + { + host: VideoService.peertube, + url: "stub", + match: sitesPeertube, + selector: ".vjs-v7", + }, + { + host: VideoService.dailymotion, + url: "https://dai.ly/", + match: /^geo.dailymotion.com$/, + selector: ".player", + }, + { + host: VideoService.trovo, + url: "https://trovo.live/s/", + match: /^trovo.live$/, + selector: ".player-video", + }, + { + host: VideoService.yandexdisk, + url: "https://yadi.sk/i/", + match: /^disk.yandex.ru$/, + selector: ".video-player__player > div:nth-child(1)", + }, + { + host: VideoService.coursehunter, + url: "https://coursehunter.net/course/", + match: /^coursehunter.net$/, + selector: "#oframeplayer > pjsdiv:nth-of-type(1)", + needExtraData: true, + }, + { + host: VideoService.okru, + url: "https://ok.ru/video/", + match: /^ok.ru$/, + selector: ".html5-vpl_vid", + }, + { + host: VideoService.googledrive, + url: "https://drive.google.com/file/d/", + match: /^youtube.googleapis.com$/, + selector: ".html5-video-container", + }, + { + host: VideoService.bannedvideo, + url: "https://madmaxworld.tv/watch?id=", + match: /^(www.)?banned.video|madmaxworld.tv$/, + selector: ".vjs-v7", + needExtraData: true, + }, + { + host: VideoService.weverse, + url: "https://weverse.io/", + match: /^weverse.io$/, + selector: ".webplayer-internal-source-wrapper", + needExtraData: true, + }, + { + host: VideoService.newgrounds, + url: "https://www.newgrounds.com/", + match: /^(www.)?newgrounds.com$/, + selector: ".ng-video-player", + }, + { + host: VideoService.egghead, + url: "https://egghead.io/", + match: /^egghead.io$/, + selector: ".cueplayer-react-video-holder", + }, + { + host: VideoService.youku, + url: "https://v.youku.com/", + match: /^v.youku.com$/, + selector: "#ykPlayer", + }, + { + host: VideoService.archive, + url: "https://archive.org/details/", + match: /^archive.org$/, + selector: ".jw-media", + }, + { + host: VideoService.kodik, + url: "stub", + match: /^kodik.(info|biz|cc)$/, + selector: ".fp-player", + needExtraData: true, + }, + { + host: VideoService.patreon, + url: "stub", + match: /^(www.)?patreon.com$/, + selector: + 'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div', + needExtraData: true, + }, + { + host: VideoService.reddit, + url: "stub", + match: /^(www.)?reddit.com$/, + selector: "shreddit-player", + shadowRoot: true, + needExtraData: true, + }, + { + host: VideoService.kick, + url: "https://kick.com/", + match: /^kick.com$/, + selector: ".vjs-v8", + needExtraData: true, + }, + { + host: VideoService.appledeveloper, + url: "https://developer.apple.com/", + match: /^developer.apple.com$/, + selector: ".developer-video-player", + needExtraData: true, + }, + { + host: VideoService.custom, + url: "stub", + match: (url) => /([^.]+).mp4/.test(url.pathname), + rawResult: true, + }, +]); - return detect(text); -} +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/consts.js +const availableLangs = [ + "auto", + "ru", + "en", + "zh", + "ko", + "lt", + "lv", + "ar", + "fr", + "it", + "es", + "de", + "ja", +]; +const availableTTS = ["ru", "en", "kk"]; +const subtitlesFormats = (/* unused pure expression or super */ null && (["srt", "vtt", "json"])); -function isMobile() { - return /^m\.youtube\.com$/.test(window.location.hostname); -} -function getPlayer() { - if (window.location.pathname.startsWith("/shorts/")) { - return isMobile() - ? document.querySelector("#movie_player") - : document.querySelector("#shorts-player"); - } +;// CONCATENATED MODULE: ./src/localization/locales/en.json +const en_namespaceObject = /*#__PURE__*/JSON.parse('{"__version__":4,"recommended":"recommended","translateVideo":"Translate video","disableTranslate":"Turn off","translationSettings":"Translation settings","subtitlesSettings":"Subtitles settings","about":"About extension","resetSettings":"Reset settings","videoBeingTranslated":"The video is being translated","videoLanguage":"Video language","translationLanguage":"Translation language","translationTake":"The translation will take","translationTakeMoreThanHour":"The translation will take more than an hour","translationTakeAboutMinute":"The translation will take about a minute","translationTakeFewMinutes":"The translation will take a few minutes","translationTakeApproximatelyMinutes":"The translation will take approximately {0} minutes","translationTakeApproximatelyMinute":"The translation will take approximately {0} minutes","unSupportedExtensionError":"Error! {0} is not supported by this version of the extension!\\n\\nPlease use the cloudflare version of the VOT extension.","requestTranslationFailed":"Failed to request video translation","audioNotReceived":"Audio link not received","grantPermissionToAutoPlay":"Grant permission to autoplay","audioFormatNotSupported":"The audio format is not supported","VOTAutoTranslate":"Translate on open","VOTDontTranslateYourLang":"Do not translate from my language","VOTVolume":"Video volume","VOTVolumeTranslation":"Translation Volume","VOTAutoSetVolume":"Reduce video volume to ","VOTShowVideoSlider":"Video volume slider","VOTSyncVolume":"Link translation and video volume","VOTAudioProxy":"Proxy received audio","VOTDisableFromYourLang":"You have disabled the translation of the video in your language","VOTLiveNotSupported":"Translation of live streams is not supported","VOTPremiere":"Wait for the premiere to end before translating","VOTVideoIsTooLong":"Video is too long","VOTNoVideoIDFound":"No video ID found","VOTSubtitles":"Subtitles","VOTSubtitlesDisabled":"Disabled","VOTSubtitlesMaxLength":"Subtitles max length","VOTHighlightWords":"Highlight words","VOTTranslatedFrom":"translated from","VOTAutogenerated":"autogenerated","VOTSettings":"VOT Settings","VOTMenuLanguage":"Menu language","VOTAuthors":"Authors","VOTVersion":"Version","VOTLoader":"Loader","VOTBrowser":"Browser","VOTShowPiPButton":"Show PiP button","langs":{"auto":"Auto","af":"Afrikaans","ak":"Akan","sq":"Albanian","am":"Amharic","ar":"Arabic","hy":"Armenian","as":"Assamese","ay":"Aymara","az":"Azerbaijani","bn":"Bangla","eu":"Basque","be":"Belarusian","bho":"Bhojpuri","bs":"Bosnian","bg":"Bulgarian","my":"Burmese","ca":"Catalan","ceb":"Cebuano","zh":"Chinese","zh-Hans":"Chinese (Simplified)","zh-Hant":"Chinese (Traditional)","co":"Corsican","hr":"Croatian","cs":"Czech","da":"Danish","dv":"Divehi","nl":"Dutch","en":"English","eo":"Esperanto","et":"Estonian","ee":"Ewe","fil":"Filipino","fi":"Finnish","fr":"French","gl":"Galician","lg":"Ganda","ka":"Georgian","de":"German","el":"Greek","gn":"Guarani","gu":"Gujarati","ht":"Haitian Creole","ha":"Hausa","haw":"Hawaiian","iw":"Hebrew","hi":"Hindi","hmn":"Hmong","hu":"Hungarian","is":"Icelandic","ig":"Igbo","id":"Indonesian","ga":"Irish","it":"Italian","ja":"Japanese","jv":"Javanese","kn":"Kannada","kk":"Kazakh","km":"Khmer","rw":"Kinyarwanda","ko":"Korean","kri":"Krio","ku":"Kurdish","ky":"Kyrgyz","lo":"Lao","la":"Latin","lv":"Latvian","ln":"Lingala","lt":"Lithuanian","lb":"Luxembourgish","mk":"Macedonian","mg":"Malagasy","ms":"Malay","ml":"Malayalam","mt":"Maltese","mi":"Māori","mr":"Marathi","mn":"Mongolian","ne":"Nepali","nso":"Northern Sotho","no":"Norwegian","ny":"Nyanja","or":"Odia","om":"Oromo","ps":"Pashto","fa":"Persian","pl":"Polish","pt":"Portuguese","pa":"Punjabi","qu":"Quechua","ro":"Romanian","ru":"Russian","sm":"Samoan","sa":"Sanskrit","gd":"Scottish Gaelic","sr":"Serbian","sn":"Shona","sd":"Sindhi","si":"Sinhala","sk":"Slovak","sl":"Slovenian","so":"Somali","st":"Southern Sotho","es":"Spanish","su":"Sundanese","sw":"Swahili","sv":"Swedish","tg":"Tajik","ta":"Tamil","tt":"Tatar","te":"Telugu","th":"Thai","ti":"Tigrinya","ts":"Tsonga","tr":"Turkish","tk":"Turkmen","uk":"Ukrainian","ur":"Urdu","ug":"Uyghur","uz":"Uzbek","vi":"Vietnamese","cy":"Welsh","fy":"Western Frisian","xh":"Xhosa","yi":"Yiddish","yo":"Yoruba","zu":"Zulu"},"udemyModuleArgsNotFound":"Could not get udemy module data due to the fact that ModuleArgs was not found","VOTTranslationHelpNull":"Could not get the data required for the translate","streamNoConnectionToServer":"There is no connection to the server","searchField":"Search...","VOTTranslateAPIErrors":"Translate errors from the API","VOTTranslationService":"Translation Service","VOTDetectService":"Detect Service","VOTTranslatingError":"Translating the error","VOTProxyWorkerHost":"Enter the proxy worker address","VOTM3u8ProxyHost":"Enter the address of the m3u8 proxy worker","proxySettings":"Proxy Settings","translationTakeApproximatelyMinute2":"The translation will take approximately {0} minutes","VOTAudioBooster":"Extended translation volume increase","VOTMediaCSPError":"Failed to load audio (media csp error)","VOTSubtitlesDesign":"Subtitles design","VOTSubtitlesFontSize":"Font size of subtitles","VOTSubtitlesOpacity":"Transparency of the subtitle background"}'); +;// CONCATENATED MODULE: ./src/utils/debug.js +const debug = {}; +debug.log = (...text) => { + if (false) {} + return console.log( + "%c[VOT DEBUG]", + "background: #F2452D; color: #fff; padding: 5px;", + ...text, + ); +}; - return document.querySelector("#movie_player"); -} +/* harmony default export */ const utils_debug = (debug); -function getPlayerResponse() { - const player = getPlayer(); - if (player?.getPlayerResponse) - return player?.getPlayerResponse?.call() ?? null; - return player?.data?.playerResponse ?? null; -} +;// CONCATENATED MODULE: ./src/utils/storage.js -function getPlayerData() { - const player = getPlayer(); - if (player?.getVideoData) return player?.getVideoData?.call() ?? null; - return player?.data?.playerResponse?.videoDetails ?? null; -} -function getVideoVolume() { - const player = getPlayer(); - if (player?.getVolume) { - return player.getVolume.call() / 100; +const votStorage = new (class { + constructor() { + this.gmSupport = typeof GM_getValue === "function"; + utils_debug.log(`GM Storage Status: ${this.gmSupport}`); } - return 1; -} + syncGet(name, def = undefined, toNumber = false) { + if (this.gmSupport) { + return GM_getValue(name, def); + } -function setVideoVolume(volume) { - const player = getPlayer(); - if (player?.setVolume) { - player.setVolume(Math.round(volume * 100)); - return true; - } -} + let val = window.localStorage.getItem(name); + if (name === "udemyData" && typeof val === "string") { + try { + val = JSON.parse(val); + } catch { + val = def; + } + } -function isMuted() { - const player = getPlayer(); - if (player?.isMuted) { - return player.isMuted.call(); + const result = val ?? def; + return toNumber ? Number(result) : result; } - return false; -} - -function videoSeek(video, time) { - // * TIME IN MS - debug/* default */.A.log("videoSeek", time); - const preTime = - getPlayer()?.getProgressState()?.seekableEnd || video.currentTime; - const finalTime = preTime - time; // we always throw it to the end of the stream - time - video.currentTime = finalTime; -} - -function isMusic() { - // Нужно доработать логику - const channelName = getPlayerData().author, - titleStr = getPlayerData().title.toUpperCase(), - titleWordsList = titleStr.match(/\w+/g), - playerData = document.body.querySelector("ytd-watch-flexy")?.playerData; - - return ( - [ - titleStr, - document.URL, - channelName, - playerData?.microformat?.playerMicroformatRenderer.category, - playerData?.title, - ].some((i) => i?.toUpperCase().includes("MUSIC")) || - document.body.querySelector( - "#upload-info #channel-name .badge-style-type-verified-artist", - ) || - (channelName && - /(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(channelName)) || - (channelName && - /(MUSIC|ROCK|SOUNDS|SONGS)/.test(channelName.toUpperCase())) || - (titleWordsList?.length && - [ - "🎵", - "♫", - "SONG", - "SONGS", - "SOUNDTRACK", - "LYRIC", - "LYRICS", - "AMBIENT", - "MIX", - "VEVO", - "CLIP", - "KARAOKE", - "OPENING", - "COVER", - "COVERED", - "VOCAL", - "INSTRUMENTAL", - "ORCHESTRAL", - "DUBSTEP", - "DJ", - "DNB", - "BASS", - "BEAT", - "ALBUM", - "PLAYLIST", - "DUBSTEP", - "CHILL", - "RELAX", - "CLASSIC", - "CINEMATIC", - ].some((i) => titleWordsList.includes(i))) || - [ - "OFFICIAL VIDEO", - "OFFICIAL AUDIO", - "FEAT.", - "FT.", - "LIVE RADIO", - "DANCE VER", - "HIP HOP", - "ROCK N ROLL", - "HOUR VER", - "HOURS VER", - "INTRO THEME", - ].some((i) => titleStr.includes(i)) || - (titleWordsList?.length && - [ - "OP", - "ED", - "MV", - "OST", - "NCS", - "BGM", - "EDM", - "GMV", - "AMV", - "MMD", - "MAD", - ].some((i) => titleWordsList.includes(i))) - ); -} - -function getSubtitles() { - const response = getPlayerResponse(); - let captionTracks = - response?.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? []; - captionTracks = captionTracks.reduce((result, captionTrack) => { - if ("languageCode" in captionTrack) { - const language = captionTrack?.languageCode - ? langTo6391(captionTrack?.languageCode) - : undefined; - const url = captionTrack?.url || captionTrack?.baseUrl; - language && - url && - result.push({ - source: "youtube", - language, - isAutoGenerated: captionTrack?.kind === "asr", - url: `${ - url.startsWith("http") ? url : `${window.location.origin}/${url}` - }&fmt=json3`, - }); + async get(name, def = undefined, toNumber = false) { + if (this.gmSupport) { + return await GM_getValue(name, def); } - return result; - }, []); - debug/* default */.A.log("youtube subtitles:", captionTracks); - return captionTracks; -} - -// Get the video data from the player -async function getVideoData() { - const player = getPlayer(); - const response = getPlayerResponse(); // null in /embed - const data = getPlayerData(); - const { title } = data ?? {}; - const { shortDescription: description, isLive } = - response?.videoDetails ?? {}; - let detectedLanguage = title - ? await getLanguage(player, response, title, description) - : "en"; - detectedLanguage = availableLangs.includes(detectedLanguage) - ? detectedLanguage - : "en"; - const videoData = { - isLive: !!isLive, - title, - description, - detectedLanguage, - }; - debug/* default */.A.log("youtube video data:", videoData); - console.log("[VOT] Detected language: ", videoData.detectedLanguage); - return videoData; -} - -/* harmony default export */ const youtubeUtils = ({ - isMobile, - getPlayer, - getPlayerResponse, - getPlayerData, - getVideoVolume, - getSubtitles, - getVideoData, - setVideoVolume, - videoSeek, - isMuted, - isMusic, -}); - -;// CONCATENATED MODULE: ./src/utils/utils.js - - - -const userlang = navigator.language || navigator.userLanguage; -const lang = userlang?.substr(0, 2)?.toLowerCase() ?? "en"; -// not used -// function waitForElm(selector) { -// // https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists -// return new Promise((resolve) => { -// const element = document.querySelector(selector); -// if (element) { -// return resolve(element); -// } - -// const observer = new MutationObserver(() => { -// const element = document.querySelector(selector); -// if (element) { -// resolve(element); -// observer.disconnect(); -// } -// }); - -// observer.observe(document.body, { -// childList: true, -// subtree: true, -// once: true, -// }); -// }); -// } - -// not used -// const sleep = (m) => new Promise((r) => setTimeout(r, m)); - -const getVideoId = (service, video) => { - let url = new URL(window.location.href); - - switch (service) { - case "piped": - case "invidious": - case "youtube": { - if (url.searchParams.has("enablejsapi")) { - const videoUrl = youtubeUtils.getPlayer().getVideoUrl(); - url = new URL(videoUrl); - } + return Promise.resolve(this.syncGet(name, def, toNumber)); + } - return ( - /(?:watch|embed|shorts|live)\/([^/]+)/.exec(url.pathname)?.[1] || - url.searchParams.get("v") - ); + syncSet(name, value) { + if (this.gmSupport) { + return GM_setValue(name, value); } - case "vk": { - const pathID = /^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(url.pathname); - const paramZ = url.searchParams.get("z"); - const paramOID = url.searchParams.get("oid"); - const paramID = url.searchParams.get("id"); - if (pathID) { - return pathID[0].slice(1); - } else if (paramZ) { - return paramZ.split("/")[0]; - } else if (paramOID && paramID) { - return `video-${Math.abs(parseInt(paramOID))}_${paramID}`; - } - return null; + if (name === "udemyData") { + value = JSON.stringify(value); } - case "nine_gag": - case "9gag": - case "gag": - return /gag\/([^/]+)/.exec(url.pathname)?.[1]; - case "twitch": { - const clipPath = /([^/]+)\/(?:clip)\/([^/]+)/.exec(url.pathname); - if (/^m\.twitch\.tv$/.test(url.hostname)) { - return /videos\/([^/]+)/.exec(url.href)?.[0] || url.pathname.slice(1); - } else if (/^player\.twitch\.tv$/.test(url.hostname)) { - return `videos/${url.searchParams.get("video")}`; - } else if (/^clips\.twitch\.tv$/.test(url.hostname)) { - // https://clips.twitch.tv/clipId - const schema = document.querySelector( - "script[type='application/ld+json']", - ); - const pathname = url.pathname.slice(1); - if (!schema) { - // иногда из-за не прогрузов твича это не работает, но пусть лучше будет (можно переделать все в async и ждать элемента, но нужно ли это ради 1 сайта) - // ссылки вида https://clips.twitch.tv/embed?clip=clipId грузятся нормально - const isEmbed = pathname === "embed"; - const channelLink = document.querySelector( - isEmbed - ? ".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']" - : ".clips-player a:not([class])", - ); - if (!channelLink) { - return; - } + return window.localStorage.setItem(name, value); + } - const channelName = channelLink.href.replace( - "https://www.twitch.tv/", - "", - ); + async set(name, value) { + if (this.gmSupport) { + return await GM_setValue(name, value); + } - return `${channelName}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; - } + return Promise.resolve(this.syncSet(name, value)); + } - const schemaJSON = JSON.parse(schema.innerText); - const channelLink = schemaJSON["@graph"].find( - (obj) => obj["@type"] === "VideoObject", - )?.creator.url; + syncDelete(name) { + if (this.gmSupport) { + return GM_deleteValue(name); + } - const channelName = channelLink.replace("https://www.twitch.tv/", ""); - return `${channelName}/clip/${pathname}`; - } else if (clipPath) { - return clipPath[0]; - } + return window.localStorage.removeItem(name); + } - return /(?:videos)\/([^/]+)/.exec(url.pathname)?.[0]; + async delete(name) { + if (this.gmSupport) { + return await GM_deleteValue(name); } - case "proxitok": - return /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; - case "tiktok": { - let id = /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; - if (!id) { - const playerEl = video.closest(".xgplayer-playing, .tiktok-web-player"); - const itemEl = playerEl?.closest( - 'div[data-e2e="recommend-list-item-container"]', - ); - const authorEl = itemEl?.querySelector( - 'a[data-e2e="video-author-avatar"]', - ); - if (playerEl && authorEl) { - const videoId = playerEl.id?.match(/^xgwrapper-\d+-(.*)$/)?.at(1); - const author = authorEl.href?.match(/.*(@.*)$/)?.at(1); - if (videoId && author) { - id = `${author}/video/${videoId}`; - } - } - } - return id; - } - case "vimeo": { - const appId = url.searchParams.get("app_id"); - const videoId = - /[^/]+\/[^/]+$/.exec(url.pathname)?.[0] || - /[^/]+$/.exec(url.pathname)?.[0]; - - return appId ? `${videoId}?app_id=${appId}` : videoId; - } - case "xvideos": - return /[^/]+\/[^/]+$/.exec(url.pathname)?.[0]; - case "pornhub": - return ( - url.searchParams.get("viewkey") || - /embed\/([^/]+)/.exec(url.pathname)?.[1] - ); - case "twitter": - return /status\/([^/]+)/.exec(url.pathname)?.[1]; - case "udemy": - case "rumble": - case "facebook": - return url.pathname.slice(1); - case "rutube": - return /(?:video|embed)\/([^/]+)/.exec(url.pathname)?.[1]; - case "coub": - return ( - /(?:view|embed)\/([^/]+)/.exec(url.pathname)?.[1] || - document.querySelector(".coub.active")?.dataset?.permalink - ); - case "bilibili": { - const bvid = url.searchParams.get("bvid"); - if (bvid) { - return bvid; - } - let vid = /video\/([^/]+)/.exec(url.pathname)?.[1]; - if (vid && url.searchParams.get("p") !== null) { - vid += `/?p=${url.searchParams.get("p")}`; - } + return Promise.resolve(this.syncDelete(name)); + } - return vid; + syncList() { + if (this.gmSupport) { + return GM_listValues(); } - case "mail_ru": { - const pathname = url.pathname; - if (pathname.startsWith("/v/") || pathname.startsWith("/mail/")) { - return pathname.slice(1); - } - - const videoId = /video\/embed\/([^/]+)/.exec(pathname)?.[1]; - if (!videoId) { - return null; - } - const referer = document.querySelector(".b-video-controls__mymail-link"); - if (!referer) { - return false; - } + return [ + "autoTranslate", + "dontTranslateLanguage", + "dontTranslateYourLang", + "autoSetVolumeYandexStyle", + "autoVolume", + "buttonPos", + "showVideoSlider", + "syncVolume", + "subtitlesMaxLength", + "highlightWords", + "responseLanguage", + "defaultVolume", + "audioProxy", + "showPiPButton", + "translateAPIErrors", + "translationService", + "detectService", + "m3u8ProxyHost", + "translateProxyEnabled", + "proxyWorkerHost", + "audioBooster", + "locale-version", + "locale-lang", + "locale-phrases", + ]; + } - return referer?.href.split("my.mail.ru")?.[1]; + async list() { + if (this.gmSupport) { + return await GM_listValues(); } - case "bitchute": { - if (video.src?.startsWith("blob:") || !video.src?.includes(".mp4")) { - return null; - } - return video.src; - // doesn't want to translate using a bitchute link - // return /([^/]+)\/([^/]+).mp4/.exec(videoUrl.pathname)?.[2]; - } - case "coursera": - // ! LINK SHOULD BE LIKE THIS https://www.coursera.org/learn/learning-how-to-learn/lecture/75EsZ - // return url.pathname.match(/lecture\/([^/]+)\/([^/]+)/)?.[1]; // <--- COURSE PREVIEW - return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <--- COURSE PASSING (IF YOU LOGINED TO COURSERA) - case "eporner": - // ! LINK SHOULD BE LIKE THIS eporner.com/video-XXXXXXXXX/isdfsd-dfjsdfjsdf-dsfsdf-dsfsda-dsad-ddsd - return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; - case "peertube": - return /\/w\/([^/]+)/.exec(url.pathname)?.[0]; - case "dailymotion": { - // we work in the context of the player - // geo.dailymotion.com - const plainPlayerConfig = Array.from( - document.querySelectorAll("*"), - ).filter((s) => s.innerHTML.trim().includes(".m3u8")); - try { - let videoUrl = plainPlayerConfig[1].lastChild.src; - return /\/video\/(\w+)\.m3u8/.exec(videoUrl)?.[1]; - } catch (e) { - console.error("[VOT]", e); - return false; - } - } - case "trovo": { - const vid = url.searchParams.get("vid"); - if (!vid) { - return null; - } + return Promise.resolve(this.syncList()); + } +})(); + +;// CONCATENATED MODULE: ./src/utils/utils.js - const path = /([^/]+)\/(\d+)/.exec(url.pathname)?.[0]; - if (!path) { - return null; - } - return `${path}?vid=${vid}`; - } - case "yandexdisk": - return /\/i\/([^/]+)/.exec(url.pathname)?.[1]; - case "coursehunter": { - const courseId = /\/course\/([^/]+)/.exec(url.pathname)?.[1]; - return courseId ? courseId + url.search : false; - } - case "ok.ru": { - return /\/video\/(\d+)/.exec(url.pathname)?.[1]; - } - case "googledrive": - return url.searchParams.get("docid"); - case "bannedvideo": - return url.searchParams.get("id"); - case "weverse": - return /([^/]+)\/(live|media)\/([^/]+)/.exec(url.pathname)?.[0]; - case "newgrounds": - return /([^/]+)\/(view)\/([^/]+)/.exec(url.pathname)?.[0]; - case "egghead": - return url.pathname.slice(1); - case "youku": - return /v_show\/id_[\w=]+/.exec(url.pathname)?.[0]; - case "archive": - return /(details|embed)\/([^/]+)/.exec(url.pathname)?.[2]; - // case "sibnet": { - // const videoId = url.searchParams.get("videoid"); - // if (videoId) { - // return `video${videoId}`; - // } - - // return /video([^/]+)/.exec(url.pathname)?.[0]; - // } - // case "patreon": - // return /posts\/([^/]+)/.exec(url.pathname)?.[0]; - case "directlink": - return url.pathname + url.search; - default: - return false; - } -}; + +const userlang = navigator.language || navigator.userLanguage; +const lang = userlang?.substr(0, 2)?.toLowerCase() ?? "en"; function secsToStrTime(secs) { const minutes = Math.floor(secs / 60); @@ -1737,7 +2598,7 @@ function isPiPAvailable() { function initHls() { return typeof Hls != "undefined" && Hls?.isSupported() ? new Hls({ - debug: false, // turn it on manually if necessary + debug: true, // turn it on manually if necessary lowLatencyMode: true, backBufferLength: 90, }) @@ -1774,45 +2635,56 @@ function cleanText(title, description) { return fullText.replace(/[^\p{L}\s]+|\s+/gu, " ").trim(); } -async function GM_fetch(url, opt = {}) { +async function GM_fetch(url, opts = {}) { + const { timeout = 15000, ...fetchOptions } = opts; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + try { - // Попытка выполнить запрос с помощью fetch - const response = await fetch(url, opt); + if (url.includes("api.browser.yandex.ru")) { + throw new Error("Preventing yandex cors"); + } + + const response = await fetch(url, { + signal: controller.signal, + ...fetchOptions, + }); + clearTimeout(timeoutId); return response; - } catch (error) { + } catch (err) { // Если fetch завершился ошибкой, используем GM_xmlhttpRequest // https://greasyfork.org/ru/scripts/421384-gm-fetch/code + utils_debug.log("GM_fetch preventing cors by GM_xmlhttpRequest", err.message); return new Promise((resolve, reject) => { - // https://www.tampermonkey.net/documentation.php?ext=dhdg#GM_xmlhttpRequest - // https://violentmonkey.github.io/api/gm/#gm_xmlhttprequest + clearTimeout(timeoutId); GM_xmlhttpRequest({ - method: opt.method || "GET", + method: fetchOptions.method || "GET", url: url, responseType: "blob", + ...fetchOptions, + data: fetchOptions.body, + timeout: timeout, onload: (resp) => { + // chrome \n and ":", firefox \r\n and ": " (Only in GM_xmlhttpRequest) + // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#examples + const headers = Object.fromEntries( + resp.responseHeaders + .trim() + .split(/\r?\n/) + .map((line) => line.split(/: (.+)/)) + .filter(([key]) => key && /^[\w-]+$/.test(key)), + ); + resolve( new Response(resp.response, { status: resp.status, - // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#examples - headers: Object.fromEntries( - resp.responseHeaders - .trim() - .split("\r\n") - .map((line) => { - let parts = line.split(": "); - if (parts?.[0] === "set-cookie") { - return; - } - return [parts.shift(), parts.join(": ")]; - }) - .filter((key) => key), - ), + headers: headers, }), ); }, - ontimeout: () => reject(new Error("fetch timeout")), + ontimeout: () => reject(new Error("Timeout")), onerror: (error) => reject(error), - onabort: () => reject(new Error("fetch abort")), + onabort: () => reject(new Error("AbortError")), }); }); } @@ -1821,6 +2693,7 @@ async function GM_fetch(url, opt = {}) { ;// CONCATENATED MODULE: ./src/localization/localizationProvider.js +/* eslint-disable sonarjs/no-duplicate-string */ @@ -1828,7 +2701,7 @@ async function GM_fetch(url, opt = {}) { const localesVersion = 4; const localesUrl = `https://raw.githubusercontent.com/ilyhalight/voice-over-translation/${ - false ? 0 : "master" + true ? "dev" : 0 }/src/localization/locales`; const availableLocales = [ @@ -1918,2429 +2791,3200 @@ const localizationProvider = new (class { ?.substr(0, 2) ?.toLowerCase() ?? "en"; } - this.setLocaleFromJsonString(votStorage.syncGet("locale-phrases", "")); - } - - reset() { - for (let i = 0; i < this.gmValues.length; i++) { - votStorage.syncDelete(this.gmValues[i]); + this.setLocaleFromJsonString(votStorage.syncGet("locale-phrases", "")); + } + + reset() { + for (let i = 0; i < this.gmValues.length; i++) { + votStorage.syncDelete(this.gmValues[i]); + } + } + + async update(force = false) { + const localeVersion = await votStorage.get("locale-version", 0, true); + const localeLang = await votStorage.get("locale-lang"); + if ( + !force && + localeVersion === localesVersion && + localeLang === this.lang + ) { + return; + } + + utils_debug.log("Updating locale..."); + + try { + const response = await GM_fetch(`${localesUrl}/${this.lang}.json`); + if (response.status !== 200) throw response.status; + const text = await response.text(); + await votStorage.set("locale-phrases", text); + this.setLocaleFromJsonString(text); + const version = this.getFromLocale(this.locale, "__version__"); + if (typeof version === "number") + await votStorage.set("locale-version", version); + await votStorage.set("locale-lang", this.lang); + } catch (error) { + console.error( + "[VOT] [localizationProvider] failed get locale, cause:", + error, + ); + this.setLocaleFromJsonString(await votStorage.get("locale-phrases", "")); + } + } + + setLocaleFromJsonString(json) { + try { + this.locale = JSON.parse(json) ?? {}; + } catch (exception) { + console.error("[VOT] [localizationProvider]", exception); + this.locale = {}; + } + } + + getFromLocale(locale, key) { + const result = key.split(".").reduce((locale, key) => { + if (typeof locale === "object" && locale) return locale[key]; + return undefined; + }, locale); + if (result === undefined) { + console.warn( + "[VOT] [localizationProvider] locale", + locale, + "doesn't contain key", + key, + ); + } + return result; + } + + getDefault(key) { + return this.getFromLocale(en_namespaceObject, key) ?? key; + } + + get(key) { + return ( + this.getFromLocale(this.locale, key) ?? + this.getFromLocale(en_namespaceObject, key) ?? + key + ); + } +})(); + +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/helper.js + + + + + + + + + +class VideoHelperError extends Error { + constructor(message) { + super(message); + this.name = "VideoHelper"; + this.message = message; + } +} +class MailRuHelper { + async getVideoData(videoId) { + try { + const res = await fetchWithTimeout(`https://my.mail.ru/+/video/meta/${videoId}?xemail=&ajax_call=1&func_name=&mna=&mnb=&ext=1&_=${new Date().getTime()}`); + return (await res.json()); + } + catch (err) { + console.error("Failed to get mail.ru video info", err.message); + return undefined; + } + } +} +class WeverseHelper { + API_ORIGIN = "https://global.apis.naver.com/weverse/wevweb"; + API_APP_ID = "be4d79eb8fc7bd008ee82c8ec4ff6fd4"; + API_HMAC_KEY = "1b9cb6378d959b45714bec49971ade22e6e24e42"; + HEADERS = { + Accept: "application/json, text/plain, */*", + Origin: "https://weverse.io", + Referer: "https://weverse.io/", + }; + getURLData() { + return { + appId: this.API_APP_ID, + language: "en", + os: "WEB", + platform: "WEB", + wpf: "pc", + }; + } + async createHash(pathname) { + const timestamp = Date.now(); + const salt = pathname.substring(0, Math.min(255, pathname.length)) + timestamp; + const sign = await getHmacSha1(this.API_HMAC_KEY, salt); + if (!sign) { + throw new VideoHelperError("Failed to get weverse HMAC signature"); + } + return { + wmsgpad: timestamp.toString(), + wmd: sign, + }; + } + async getHashURLParams(pathname) { + const hash = await this.createHash(pathname); + return new URLSearchParams(hash).toString(); + } + async getPostPreview(postId) { + const pathname = `/post/v1.0/post-${postId}/preview?` + + new URLSearchParams({ + fieldSet: "postForPreview", + ...this.getURLData(), + }).toString(); + try { + const urlParams = await this.getHashURLParams(pathname); + const res = await fetchWithTimeout(this.API_ORIGIN + pathname + "&" + urlParams, { + headers: this.HEADERS, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get weverse post preview by postId: ${postId}`, err.message); + return false; + } + } + async getVideoInKey(videoId) { + const pathname = `/video/v1.1/vod/${videoId}/inKey?` + + new URLSearchParams({ + gcc: "RU", + ...this.getURLData(), + }).toString(); + try { + const urlParams = await this.getHashURLParams(pathname); + const res = await fetchWithTimeout(this.API_ORIGIN + pathname + "&" + urlParams, { + method: "POST", + headers: this.HEADERS, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get weverse InKey by videoId: ${videoId}`, err.message); + return false; + } + } + async getVideoInfo(infraVideoId, inkey, serviceId) { + const timestamp = Date.now(); + try { + const urlParams = new URLSearchParams({ + key: inkey, + sid: serviceId, + nonce: timestamp.toString(), + devt: "html5_pc", + prv: "N", + aup: "N", + stpb: "N", + cpl: "en", + env: "prod", + lc: "en", + adi: JSON.stringify([ + { + adSystem: null, + }, + ]), + adu: "/", + }).toString(); + const res = await fetchWithTimeout(`https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${infraVideoId}?` + + urlParams, { + headers: this.HEADERS, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get weverse video info (infraVideoId: ${infraVideoId}, inkey: ${inkey}, serviceId: ${serviceId}`, err.message); + return false; + } + } + extractVideoInfo(videoList) { + return videoList.find((video) => video.useP2P === false && video.source.includes(".mp4")); + } + async getVideoData(postId) { + const videoPreview = await this.getPostPreview(postId); + if (!videoPreview) { + return undefined; + } + const { videoId, serviceId, infraVideoId } = videoPreview.extension.video; + if (!(videoId && serviceId && infraVideoId)) { + return undefined; + } + const inkeyData = await this.getVideoInKey(videoId); + if (!inkeyData) { + return undefined; + } + const videoInfo = await this.getVideoInfo(infraVideoId, inkeyData.inKey, serviceId); + if (!videoInfo) { + return undefined; + } + const videoItem = this.extractVideoInfo(videoInfo.videos.list); + if (!videoItem) { + return undefined; + } + return { + url: videoItem.source, + duration: videoItem.duration, + }; + } +} +class KodikHelper { + API_ORIGIN = window.location.origin; + async getSecureData(videoPath) { + try { + const url = this.API_ORIGIN + videoPath; + const res = await fetchWithTimeout(url, { + headers: { + "User-Agent": config.userAgent, + Origin: this.API_ORIGIN, + Referer: this.API_ORIGIN, + }, + }); + const content = await res.text(); + const [videoType, videoId, hash] = videoPath.split("/").filter((a) => a); + const parser = new DOMParser(); + const doc = parser.parseFromString(content, "text/html"); + const secureScript = Array.from(doc.getElementsByTagName("script")).filter((s) => s.innerHTML.includes(`videoId = "${videoId}"`)); + if (!secureScript.length) { + throw new VideoHelperError("Failed to find secure script"); + } + const secureContent = /'{[^']+}'/.exec(secureScript[0].textContent.trim())?.[0]; + if (!secureContent) { + throw new VideoHelperError("Secure json wasn't found in secure script"); + } + const secureJSON = JSON.parse(secureContent.replaceAll("'", "")); + return { + videoType: videoType, + videoId, + hash, + ...secureJSON, + }; + } + catch (err) { + console.error(`Failed to get kodik secure data by videoPath: ${videoPath}.`, err.message); + return false; + } + } + async getFtor(secureData) { + const { videoType, videoId: id, hash, d, d_sign, pd, pd_sign, ref, ref_sign, } = secureData; + try { + const res = await fetchWithTimeout(this.API_ORIGIN + "/ftor", { + method: "POST", + headers: { + "User-Agent": config.userAgent, + Origin: this.API_ORIGIN, + Referer: `${this.API_ORIGIN}/${videoType}/${id}/${hash}/360p`, + }, + body: new URLSearchParams({ + d, + d_sign, + pd, + pd_sign, + ref: decodeURIComponent(ref), + ref_sign, + bad_user: "false", + cdn_is_working: "true", + info: "{}", + type: videoType, + hash, + id, + }), + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get kodik video data (type: ${videoType}, id: ${id}, hash: ${hash})`, err.message); + return false; + } + } + decryptUrl(encryptedUrl) { + const decryptedUrl = atob(encryptedUrl.replace(/[a-zA-Z]/g, function (e) { + const charCode = e.charCodeAt(0) + 13; + return String.fromCharCode((e <= "Z" ? 90 : 122) >= charCode ? charCode : charCode - 26); + })); + return "https:" + decryptedUrl; + } + async getVideoData(videoPath) { + const secureData = await this.getSecureData(videoPath); + if (!secureData) { + return undefined; + } + const videoData = await this.getFtor(secureData); + if (!videoData) { + return undefined; + } + const videoDataLinks = Object.entries(videoData.links[videoData.default.toString()]); + const videoLink = videoDataLinks.find(([_, data]) => data.type === "application/x-mpegURL")?.[1]; + if (!videoLink) { + return undefined; + } + return { + url: this.decryptUrl(videoLink.src), + }; + } +} +class PatreonHelper { + async getPosts(postId) { + try { + const res = await fetchWithTimeout(`https://www.patreon.com/api/posts/${postId}?json-api-use-default-includes=false`); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get patreon posts by postId: ${postId}.`, err.message); + return false; + } + } + async getVideoData(postId) { + const postData = await this.getPosts(postId); + if (!postData) { + return undefined; + } + const postFileUrl = postData.data.attributes.post_file.url; + if (!postFileUrl) { + return undefined; + } + return { + url: postFileUrl, + }; + } +} +class RedditHelper { + async getVideoData() { + const contentUrl = document + .querySelector("source[type='application/vnd.apple.mpegURL']") + ?.src + ?.replaceAll("&", "&"); + if (!contentUrl) { + return undefined; + } + return { + url: decodeURIComponent(contentUrl), + }; + } +} +class BannedVideoHelper { + async getVideoInfo(videoId) { + try { + const res = await fetchWithTimeout(`https://api.banned.video/graphql`, { + method: "POST", + body: JSON.stringify({ + operationName: "GetVideo", + query: `query GetVideo($id: String!) { + getVideo(id: $id) { + title + description: summary + duration: videoDuration + videoUrl: directUrl + isStream: live + } + }`, + variables: { + id: videoId, + }, + }), + headers: { + "User-Agent": "bannedVideoFrontEnd", + "apollographql-client-name": "banned-web", + "apollographql-client-version": "1.3", + "content-type": "application/json", + }, + }); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get bannedvideo video info by videoId: ${videoId}.`, err.message); + return false; + } + } + async getVideoData(videoId) { + const videoInfo = await this.getVideoInfo(videoId); + if (!videoInfo) { + return false; + } + const { videoUrl, duration, isStream, description, title } = videoInfo.data.getVideo; + return { + url: videoUrl, + duration, + isStream, + title, + description, + }; } - } - - async update(force = false) { - const localeVersion = await votStorage.get("locale-version", 0, true); - const localeLang = await votStorage.get("locale-lang"); - if ( - !force && - localeVersion === localesVersion && - localeLang === this.lang - ) { - return; +} +class KickHelper { + async getClipInfo(clipId) { + try { + const res = await fetchWithTimeout(`https://kick.com/api/v2/clips/${clipId}`); + return (await res.json()); + } + catch (err) { + console.error(`Failed to get kick clip info by clipId: ${clipId}.`, err.message); + return false; + } } - - debug/* default */.A.log("Updating locale..."); - - try { - const response = await GM_fetch(`${localesUrl}/${this.lang}.json`); - if (response.status !== 200) throw response.status; - const text = await response.text(); - await votStorage.set("locale-phrases", text); - this.setLocaleFromJsonString(text); - const version = this.getFromLocale(this.locale, "__version__"); - if (typeof version === "number") - await votStorage.set("locale-version", version); - await votStorage.set("locale-lang", this.lang); - } catch (error) { - console.error( - "[VOT] [localizationProvider] failed get locale, cause:", - error, - ); - this.setLocaleFromJsonString(await votStorage.get("locale-phrases", "")); + async getVideoData(videoId) { + if (!videoId.startsWith("clip_")) { + return { + url: sites.find((s) => s.host === VideoService.kick).url + videoId, + }; + } + const clipInfo = await this.getClipInfo(videoId); + if (!clipInfo) { + return false; + } + const { clip_url, duration, title } = clipInfo.clip; + return { + url: clip_url, + duration, + title, + }; + } +} +class UdemyHelper { + API_ORIGIN = "https://www.udemy.com/api-2.0"; + + getModuleData() { + const moduleArgs = document.querySelector( + ".ud-app-loader[data-module-id='course-taking']", + )?.dataset?.moduleArgs; + if (!moduleArgs) { + console.error(localizationProvider.get("udemyModuleArgsNotFound")); + return {}; + } + return JSON.parse(moduleArgs); } - } - setLocaleFromJsonString(json) { - try { - this.locale = JSON.parse(json) ?? {}; - } catch (exception) { - console.error("[VOT] [localizationProvider]", exception); - this.locale = {}; + getLectureId() { + return /learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1]; } - } - getFromLocale(locale, key) { - const result = key.split(".").reduce((locale, key) => { - if (typeof locale === "object" && locale) return locale[key]; - return undefined; - }, locale); - if (result === undefined) { - console.warn( - "[VOT] [localizationProvider] locale", - locale, - "doesn't contain key", - key, - ); + async getLectureData(courseId, lectureId) { + const res = await GM_fetch( + `${this.API_ORIGIN}/users/me/subscribed-courses/${courseId}/lectures/${lectureId}/?` + + new URLSearchParams({ + "fields[lecture]": "title,description,asset", + "fields[asset]": "length,media_sources,captions", + }) + ); + return await res.json(); } - return result; - } - getDefault(key) { - return this.getFromLocale(en_namespaceObject, key) ?? key; - } + async getCourseLang(courseId) { + const res = await GM_fetch( + `${this.API_ORIGIN}/users/me/subscribed-courses/${courseId}?` + + new URLSearchParams({ + "fields[course]": "locale", + }) + ); + return await res.json(); + } - get(key) { - return ( - this.getFromLocale(this.locale, key) ?? - this.getFromLocale(en_namespaceObject, key) ?? - key - ); - } -})(); + findVideoUrl(sources) { + return sources?.find((src) => src.type === "video/mp4")?.src; + } -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js -var injectStylesIntoStyleTag = __webpack_require__("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"); -var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleDomAPI.js -var styleDomAPI = __webpack_require__("./node_modules/style-loader/dist/runtime/styleDomAPI.js"); -var styleDomAPI_default = /*#__PURE__*/__webpack_require__.n(styleDomAPI); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertBySelector.js -var insertBySelector = __webpack_require__("./node_modules/style-loader/dist/runtime/insertBySelector.js"); -var insertBySelector_default = /*#__PURE__*/__webpack_require__.n(insertBySelector); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js -var setAttributesWithoutAttributes = __webpack_require__("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js"); -var setAttributesWithoutAttributes_default = /*#__PURE__*/__webpack_require__.n(setAttributesWithoutAttributes); -// EXTERNAL MODULE: ./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js -var style_loader_insertStyleElement = __webpack_require__("./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js"); -var style_loader_insertStyleElement_default = /*#__PURE__*/__webpack_require__.n(style_loader_insertStyleElement); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleTagTransform.js -var styleTagTransform = __webpack_require__("./node_modules/style-loader/dist/runtime/styleTagTransform.js"); -var styleTagTransform_default = /*#__PURE__*/__webpack_require__.n(styleTagTransform); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss -var main = __webpack_require__("./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss"); -;// CONCATENATED MODULE: ./src/styles/main.scss + findSubtitleUrl(captions, detectedLanguage) { + let subtitle = captions?.find( + (caption) => langTo6391(caption.locale_id) === detectedLanguage, + ); - - - - - - - - - + if (!subtitle) { + subtitle = captions?.find( + (caption) => langTo6391(caption.locale_id) === "en", + ) ?? captions?.[0]; + } -var options = {}; + return subtitle?.url; + } -options.styleTagTransform = (styleTagTransform_default()); -options.setAttributes = (setAttributesWithoutAttributes_default()); -options.insert = insertBySelector_default().bind(null, "head"); -options.domAPI = (styleDomAPI_default()); -options.insertStyleElement = (style_loader_insertStyleElement_default()); + async getVideoData(videoId) { + const { courseId } = this.getModuleData(); + if (!courseId) { + return false; + } -var update = injectStylesIntoStyleTag_default()(main/* default */.A, options); + const lectureId = this.getLectureId(); + utils_debug.log(`[Udemy] courseId: ${courseId}, lectureId: ${lectureId}`) + if (!lectureId) { + return false; + } + const { title, description, asset } = await this.getLectureData(courseId, lectureId); + const { length: duration, media_sources, captions } = asset; + const videoUrl = this.findVideoUrl(media_sources); + if (!videoUrl) { + console.log("Failed to find .mp4 video file in media_sources", media_sources); + return false; + } + const courseLangData = await this.getCourseLang(courseId); + let { locale: { locale: courseLang } } = courseLangData; + courseLang = courseLang ? langTo6391(courseLang) : "en"; + if (!availableLangs.includes(courseLang)) { + courseLang = "en"; + } - /* harmony default export */ const styles_main = (main/* default */.A && main/* default */.A.locals ? main/* default */.A.locals : undefined); + const subtitleUrl = this.findSubtitleUrl(captions, courseLang); + if (!subtitleUrl) { + console.log("Failed to find subtitle file in captions", captions) + } -;// CONCATENATED MODULE: ./src/ui.js + return { + ...subtitleUrl ? { + url: sites.find((s) => s.host === VideoService.udemy).url + videoId, + translationHelp: [ + { + target: "subtitles_file_url", + targetUrl: subtitleUrl, + }, + { + target: "video_file_url", + targetUrl: videoUrl, + }, + ], + detectedLanguage: courseLang, + } : { + url: videoUrl, + translationHelp: null, + }, + duration, + title, + description, + }; + } +} +class CoursehunterHelper { + API_ORIGIN = "https://coursehunter.net/api/v1"; + async getLessonsData(courseId) { + const response = await GM_fetch( + `${this.API_ORIGIN}/course/${courseId}/lessons`, + ); + return await response.json(); + } -function createHeader(html, level = 4) { - const header = document.createElement("vot-block"); - header.classList.add("vot-header"); - header.classList.add(`vot-header-level-${level}`); - header.innerHTML = html; + async getVideoData() { + const courseId = window.course_id ?? +document.querySelector('input[name="course_id"]')?.value; + const lessonsData = window.lessons ?? (await this.getLessonsData(courseId)); + const lessonId = +document.querySelector(".lessons-item_active")?.dataset?.index || 1; + const currentLesson = lessonsData?.[lessonId - 1]; + const { file: videoUrl, duration, title } = currentLesson; + if (!videoUrl) { + return false; + } - return header; + return { + url: videoUrl, + duration, + title + }; + } +} +class AppleDeveloperHelper { + async getVideoData(videoId) { + const contentUrl = document.querySelector("meta[property='og:video']")?.content + if (!contentUrl) { + return undefined; + } + return { + url: contentUrl, + }; + } } +class CourseraHelper { + API_ORIGIN = "https://www.coursera.org/api"; -function createInformation(html, valueHtml) { - const container = document.createElement("vot-block"); - container.classList.add("vot-info"); + async getCourseData(courseId) { + const response = await GM_fetch( + `${this.API_ORIGIN}/onDemandCourses.v1/${courseId}`, + ); + const resJSON = await response.json(); + return resJSON?.elements?.[0]; + } - const header = document.createElement("vot-block"); - header.innerHTML = html; + getPlayer() { + return document.querySelector(".vjs-v6"); + } - const value = document.createElement("vot-block"); - value.innerHTML = valueHtml; + getPlayerData() { + return this.getPlayer()?.player; + } - container.appendChild(header); - container.appendChild(value); + findVideoUrl(sources) { + return sources?.find((src) => src.type === "video/mp4")?.src; + } - return { - container, - header, - value, - }; -} + findSubtitleUrl(captions, detectedLanguage) { + let subtitle = captions?.find( + (caption) => langTo6391(caption.srclang) === detectedLanguage, + ); -function createButton(html) { - const button = document.createElement("vot-block"); - button.classList.add("vot-button"); - button.innerHTML = html; + if (!subtitle) { + subtitle = captions?.find( + (caption) => langTo6391(caption.srclang) === "en", + ) || captions?.[0]; + } - return button; -} + return subtitle?.src; + } -function createTextButton(html) { - const button = document.createElement("vot-block"); - button.classList.add("vot-text-button"); - button.innerHTML = html; + async getVideoData(videoId) { + const data = this.getPlayerData(); - return button; -} + const { duration } = data?.cache_ || {}; + const { courseId, tracks, sources } = data?.options_ || {}; -function createOutlinedButton(html) { - const button = document.createElement("vot-block"); - button.classList.add("vot-outlined-button"); - button.innerHTML = html; + const videoUrl = this.findVideoUrl(sources); + if (!videoUrl) { + console.log("Failed to find .mp4 video file in sources", sources); + return false; + } - return button; -} + const { primaryLanguageCodes } = await this.getCourseData(courseId); + let courseLang = primaryLanguageCodes?.[0]; + courseLang = courseLang ? langTo6391(courseLang) : "en"; -function createIconButton(html) { - const button = document.createElement("vot-block"); - button.classList.add("vot-icon-button"); - button.innerHTML = html; + if (!availableLangs.includes(courseLang)) { + courseLang = "en"; + } - return button; -} + const subtitleUrl = this.findSubtitleUrl(tracks, courseLang); + if (!subtitleUrl) { + console.log("Failed to find subtitle file in tracks", tracks) + } -function createCheckbox(html, value = false) { - const container = document.createElement("label"); - container.classList.add("vot-checkbox"); + return { + ...subtitleUrl ? { + url: sites.find((s) => s.host === VideoService.coursera).url + videoId, + translationHelp: [ + { + target: "subtitles_file_url", + targetUrl: subtitleUrl, + }, + { + target: "video_file_url", + targetUrl: videoUrl, + }, + ], + detectedLanguage: courseLang, + } : { + url: videoUrl, + translationHelp: null, + }, + duration, + }; + } +} +class VideoHelper { + static [VideoService.mailru] = new MailRuHelper(); + static [VideoService.weverse] = new WeverseHelper(); + static [VideoService.kodik] = new KodikHelper(); + static [VideoService.patreon] = new PatreonHelper(); + static [VideoService.reddit] = new RedditHelper(); + static [VideoService.bannedvideo] = new BannedVideoHelper(); + static [VideoService.kick] = new KickHelper(); + static [VideoService.udemy] = new UdemyHelper(); + static [VideoService.coursehunter] = new CoursehunterHelper(); + static [VideoService.coursera] = new CourseraHelper(); + static [VideoService.appledeveloper] = new AppleDeveloperHelper(); +} - const input = document.createElement("input"); - input.type = "checkbox"; - input.checked = Boolean(value); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/videoData.js - const label = document.createElement("span"); - label.innerHTML = html; - container.appendChild(input); - container.appendChild(label); - return { container, input, label }; +class VideoDataError extends Error { + constructor(message) { + super(message); + this.name = "VideoDataError"; + this.message = message; + } } - -function updateSlider(input) { - const value = parseFloat(input.value); - const min = input.min === "" ? 0 : parseFloat(input.min); - const max = input.max === "" ? 100 : parseFloat(input.max); - const progress = (value - min) / (max - min); - input.parentElement.setAttribute("style", `--vot-progress: ${progress}`); +function getService() { + if (/(http(s)?:\/\/)(127\.0\.0\.1|localhost)/.exec(window.location.href)) + return []; + const hostname = window.location.hostname; + const enteredURL = new URL(window.location); + const isMathes = (match) => { + if (match instanceof RegExp) { + return match.test(hostname); + } + else if (typeof match === "string") { + return hostname.includes(match); + } + else if (typeof match === "function") { + return match(enteredURL); + } + return false; + }; + return sites.filter((e) => { + return ((Array.isArray(e.match) ? e.match.some(isMathes) : isMathes(e.match)) && + e.host && + e.url); + }); } +async function getVideoID(service, video) { + const url = new URL(window.location.href); + switch (service.host) { + case VideoService.custom: + return url.href; + case VideoService.piped: + case VideoService.invidious: + case VideoService.youtube: + if (url.hostname === "youtu.be") { + url.search = `?v=${url.pathname.replace("/", "")}`; + url.pathname = "/watch"; + } + return (/(?:watch|embed|shorts|live)\/([^/]+)/.exec(url.pathname)?.[1] ?? + url.searchParams.get("v")); + case VideoService.vk: { + const pathID = /^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(url.pathname); + const paramZ = url.searchParams.get("z"); + const paramOID = url.searchParams.get("oid"); + const paramID = url.searchParams.get("id"); + if (pathID) { + return pathID[0].slice(1); + } + else if (paramZ) { + return paramZ.split("/")[0]; + } + else if (paramOID && paramID) { + return `video-${Math.abs(parseInt(paramOID))}_${paramID}`; + } -function createSlider(html, value = 50, min = 0, max = 100) { - const container = document.createElement("vot-block"); - container.classList.add("vot-slider"); + const videoBox = video.parentElement?.closest(".video_box_wrap"); + if (videoBox) { + return videoBox.id.replace("video_box_wrap", "video"); + } - const input = document.createElement("input"); - input.type = "range"; - input.min = min; - input.max = max; - input.value = value; + return null; + } + case VideoService.nine_gag: + case VideoService.gag: + return /gag\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.twitch: { + const clipPath = /([^/]+)\/(?:clip)\/([^/]+)/.exec(url.pathname); + const isClipsDomain = /^clips\.twitch\.tv$/.test(url.hostname); + if (/^m\.twitch\.tv$/.test(url.hostname)) { + return /videos\/([^/]+)/.exec(url.href)?.[0] ?? url.pathname.slice(1); + } + else if (/^player\.twitch\.tv$/.test(url.hostname)) { + return `videos/${url.searchParams.get("video")}`; + } + else if (isClipsDomain) { + const schema = document.querySelector( + "script[type='application/ld+json']", + ); + const pathname = url.pathname.slice(1); + if (schema) { + const schemaJSON = JSON.parse(schema.innerText); + const channelLink = schemaJSON["@graph"].find( + (obj) => obj["@type"] === "VideoObject", + )?.creator.url; + + const channelName = channelLink.replace("https://www.twitch.tv/", ""); + return `${channelName}/clip/${pathname}`; + } - const label = document.createElement("span"); - label.innerHTML = html; + const isEmbed = pathname === "embed"; + const channelLink = document.querySelector( + isEmbed + ? ".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']" + : ".clips-player a:not([class])", + ); - container.appendChild(input); - container.appendChild(label); + if (!channelLink) { + return; + } - input.addEventListener("input", (e) => updateSlider(e.target)); - updateSlider(input); + const channelName = channelLink.href.replace( + "https://www.twitch.tv/", + "", + ); + + return `${channelName}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; + } + else if (clipPath) { + return clipPath[0]; + } + return /(?:videos)\/([^/]+)/.exec(url.pathname)?.[0]; + } + case VideoService.proxitok: + case VideoService.tiktok: + return /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.vimeo: { + const appId = url.searchParams.get("app_id"); + const videoId = /[^/]+\/[^/]+$/.exec(url.pathname)?.[0] ?? + /[^/]+$/.exec(url.pathname)?.[0]; + return appId ? `${videoId}?app_id=${appId}` : videoId; + } + case VideoService.xvideos: + return /[^/]+\/[^/]+$/.exec(url.pathname)?.[0]; + case VideoService.pornhub: + return (url.searchParams.get("viewkey") ?? + /embed\/([^/]+)/.exec(url.pathname)?.[1]); + case VideoService.twitter: + return /status\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.rumble: + case VideoService.facebook: + return url.pathname.slice(1); + case VideoService.rutube: + return /(?:video|embed)\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.bilibili: { + const bvid = url.searchParams.get("bvid"); + if (bvid) { + return bvid; + } + let vid = /video\/([^/]+)/.exec(url.pathname)?.[1]; + if (vid && url.searchParams.get("p") !== null) { + vid += `/?p=${url.searchParams.get("p")}`; + } + return vid; + } + case VideoService.mailru: { + const pathname = url.pathname; + if (pathname.startsWith("/v/") || pathname.startsWith("/mail/")) { + return pathname.slice(1); + } + const videoId = /video\/embed\/([^/]+)/.exec(pathname)?.[1]; + if (!videoId) { + return null; + } + const videoData = await VideoHelper.mailru.getVideoData(videoId); + if (!videoData) { + return null; + } + return videoData.meta.url.replace("//my.mail.ru/", ""); + } + case VideoService.bitchute: + return /(video|embed)\/([^/]+)/.exec(url.pathname)?.[2]; + case VideoService.coursera: + return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <-- COURSE PASSING (IF YOU LOGINED TO COURSERA) + case VideoService.udemy: + return url.pathname.slice(1); + case VideoService.eporner: + return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.peertube: + return /\/w\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.dailymotion: { + // we work in the context of the player + // geo.dailymotion.com + const plainPlayerConfig = Array.from( + document.querySelectorAll("*"), + ).filter((s) => s.innerHTML.trim().includes(".m3u8")); + try { + let videoUrl = plainPlayerConfig[1].lastChild.src; + return /\/video\/(\w+)\.m3u8/.exec(videoUrl)?.[1]; + } catch (e) { + console.error("[VOT]", e); + return false; + } + } + case VideoService.trovo: { + const vid = url.searchParams.get("vid"); + if (!vid) { + return null; + } + const path = /([^/]+)\/([\d]+)/.exec(url.pathname)?.[0]; + if (!path) { + return null; + } + return `${path}?vid=${vid}`; + } + case VideoService.yandexdisk: + return /\/i\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.coursehunter: { + const courseId = /\/course\/([^/]+)/.exec(url.pathname)?.[1]; + return courseId ? courseId + url.search : false; + } + case VideoService.okru: { + return /\/video\/(\d+)/.exec(url.pathname)?.[1]; + } + case VideoService.googledrive: + return /\/file\/d\/([^/]+)/.exec(url.pathname)?.[1]; + case VideoService.bannedvideo: { + return url.searchParams.get("id"); + } + case VideoService.weverse: + return /([^/]+)\/(live|media)\/([^/]+)/.exec(url.pathname)?.[3]; + case VideoService.newgrounds: + return /([^/]+)\/(view)\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.egghead: + return url.pathname.slice(1); + case VideoService.youku: + return /v_show\/id_[\w=]+/.exec(url.pathname)?.[0]; + case VideoService.archive: + return /(details|embed)\/([^/]+)/.exec(url.pathname)?.[2]; + case VideoService.kodik: + return /\/(seria|video)\/([^/]+)\/([^/]+)\/([\d]+)p/.exec(url.pathname)?.[0]; + case VideoService.patreon: { + const fullPostId = /posts\/([^/]+)/.exec(url.pathname)?.[1]; + if (!fullPostId) { + return undefined; + } + return fullPostId.replace(/[^\d.]/g, ""); + } + case VideoService.reddit: + return /\/r\/(([^/]+)\/([^/]+)\/([^/]+)\/([^/]+))/.exec(url.pathname)?.[1]; + case VideoService.kick: { + const videoId = /video\/([^/]+)/.exec(url.pathname)?.[0]; + if (videoId) { + return videoId; + } + const clipId = url.searchParams.get("clip"); + if (clipId) { + return clipId; + } - return { - container, - input, - label, - }; + const player = document.getElementById("clip-video-player"); + return /clip_([^/]+)/.exec(player?.getAttribute("poster"))?.[0] + } + case VideoService.appledeveloper: { + return /videos\/play\/([^/]+)\/([\d]+)/.exec(url.pathname)?.[0]; + } + default: + return undefined; + } +} +async function getVideoData(service, video) { + const videoId = await getVideoID(service, video); + if (!videoId) { + throw new VideoDataError(`Entered unsupported link: "${service.host}"`); + } + if (service.host === VideoService.peertube) { + service.url = new URL(url).origin; + } + if (service.rawResult) { + return { + url: videoId, + videoId, + host: service.host, + duration: undefined, + }; + } + if (!service.needExtraData) { + return { + url: service.url + videoId, + videoId, + host: service.host, + duration: undefined, + }; + } + const result = await VideoHelper[service.host].getVideoData(videoId); + if (!result) { + throw new VideoDataError(`Failed to get video raw url for ${service.host}`); + } + return { + ...result, + videoId, + host: service.host, + }; } -function createTextfield( - html, - value = "", - placeholder = " ", - multiline = false, -) { - const container = document.createElement("vot-block"); - container.classList.add("vot-textfield"); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/vot.js - const input = document.createElement(multiline ? "textarea" : "input"); - input.placeholder = placeholder; - input.value = value; +function convertVOT(service, videoId, url) { + if (service === VideoService.patreon) { + return { + service: "mux", + videoId: new URL(url).pathname.slice(1), + }; + } + return { + service, + videoId, + }; +} - const label = document.createElement("span"); - label.innerHTML = html; +;// CONCATENATED MODULE: ./src/utils/VOTLocalizedError.js - container.appendChild(input); - container.appendChild(label); - return { - container, - input, - label, - }; +class VOTLocalizedError extends Error { + constructor(message) { + super(localizationProvider.getDefault(message)); + this.name = "VOTLocalizedError"; + this.unlocalizedMessage = message; + this.localizedMessage = localizationProvider.get(message); + } } -function createDialog(html) { - const container = document.createElement("vot-block"); - container.classList.add("vot-dialog-container"); - container.hidden = true; - - const backdrop = document.createElement("vot-block"); - backdrop.classList.add("vot-dialog-backdrop"); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/client.js - const dialog = document.createElement("vot-block"); - dialog.classList.add("vot-dialog"); - const contentWrapper = document.createElement("vot-block"); - contentWrapper.classList.add("vot-dialog-content-wrapper"); - const headerContainer = document.createElement("vot-block"); - headerContainer.classList.add("vot-dialog-header-container"); - const bodyContainer = document.createElement("vot-block"); - bodyContainer.classList.add("vot-dialog-body-container"); - const footerContainer = document.createElement("vot-block"); - footerContainer.classList.add("vot-dialog-footer-container"); - const titleContainer = document.createElement("vot-block"); - titleContainer.classList.add("vot-dialog-title-container"); - const closeButton = createIconButton( - ``, - ); - closeButton.classList.add("vot-dialog-close-button"); - backdrop.onclick = closeButton.onclick = () => { - container.hidden = true; - }; - const title = document.createElement("vot-block"); - title.classList.add("vot-dialog-title"); - title.innerHTML = html; - - container.appendChild(backdrop); - container.appendChild(dialog); - dialog.appendChild(contentWrapper); - contentWrapper.appendChild(headerContainer); - contentWrapper.appendChild(bodyContainer); - contentWrapper.appendChild(footerContainer); - headerContainer.appendChild(titleContainer); - headerContainer.appendChild(closeButton); - titleContainer.appendChild(title); - return { - container, - backdrop, - dialog, - contentWrapper, - headerContainer, - bodyContainer, - footerContainer, - titleContainer, - closeButton, - title, - }; +const { /* version */ "rE": version } = package_namespaceObject; +class VOTJSError extends Error { + data; + constructor(message, data = undefined) { + super(message); + this.data = data; + this.name = "VOTJSError"; + this.message = message; + } +} +class VOTClient { + host; + hostVOT; + schema; + schemaVOT; + fetch; + fetchOpts; + getVideoDataFn; + sessions = {}; + requestLang; + responseLang; + userAgent = config.userAgent; + componentVersion = config.componentVersion; + paths = { + videoTranslation: "/video-translation/translate", + videoSubtitles: "/video-subtitles/get-subtitles", + streamPing: "/stream-translation/ping-stream", + streamTranslation: "/stream-translation/translate-stream", + createSession: "/session/create", + }; + isCustomFormat(url) { + return /\.(m3u8|m4(a|v)|mpd)/.exec(url); + } + headers = { + "User-Agent": this.userAgent, + Accept: "application/x-protobuf", + "Accept-Language": "en", + "Content-Type": "application/x-protobuf", + Pragma: "no-cache", + "Cache-Control": "no-cache", + "Sec-Fetch-Mode": "no-cors", + }; + headersVOT = { + "User-Agent": `vot.js/${version}`, + "Content-Type": "application/json", + Pragma: "no-cache", + "Cache-Control": "no-cache", + }; + constructor({ host = config.host, hostVOT = config.hostVOT, fetchFn = fetchWithTimeout, fetchOpts = {}, getVideoDataFn = getVideoData, requestLang = "en", responseLang = "ru", headers = {}, } = {}) { + const schemaRe = /(http(s)?):\/\//; + const schema = schemaRe.exec(host)?.[1]; + this.host = schema ? host.replace(`${schema}://`, "") : host; + this.schema = schema ?? "https"; + const schemaVOT = schemaRe.exec(hostVOT)?.[1]; + this.hostVOT = schemaVOT ? hostVOT.replace(`${schemaVOT}://`, "") : hostVOT; + this.schemaVOT = schemaVOT ?? "https"; + this.fetch = fetchFn; + this.fetchOpts = fetchOpts; + this.getVideoDataFn = getVideoDataFn; + this.requestLang = requestLang; + this.responseLang = responseLang; + this.headers = { ...this.headers, ...headers }; + } + getOpts(body, headers = {}) { + return { + method: "POST", + headers: { + ...this.headers, + ...headers, + }, + body, + ...this.fetchOpts, + }; + } + async request(path, body, headers = {}) { + const options = this.getOpts(new Blob([body]), headers); + try { + const res = await this.fetch(`${this.schema}://${this.host}${path}`, options); + const data = await res.arrayBuffer(); + return { + success: res.status === 200, + data, + }; + } + catch (err) { + console.error("[vot.js]", err.message); + return { + success: false, + data: null, + }; + } + } + async requestVOT(path, body, headers = {}) { + const options = this.getOpts(JSON.stringify(body), { + ...this.headersVOT, + ...headers, + }); + try { + console.log(`${this.schemaVOT}://${this.hostVOT}${path}`); + const res = await this.fetch(`${this.schemaVOT}://${this.hostVOT}${path}`, options); + const data = (await res.json()); + return { + success: res.status === 200, + data, + }; + } + catch (err) { + console.error("[vot.js]", err.message); + return { + success: false, + data: null, + }; + } + } + async getSession(module) { + const timestamp = getTimestamp(); + const session = this.sessions[module]; + if (session && session.timestamp + session.expires > timestamp) { + return session; + } + const { secretKey, expires, uuid } = await this.createSession(module); + this.sessions[module] = { + secretKey, + expires, + timestamp, + uuid, + }; + return this.sessions[module]; + } + async translateVideoYAImpl({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, translationHelp = null, headers = {}, }) { + const { url, duration = config.defaultDuration } = videoData; + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeTranslationRequest(url, duration, requestLang, responseLang, translationHelp); + const sign = await getSignature(body); + const res = await this.request(this.paths.videoTranslation, body, { + "Vtrans-Signature": sign, + "Sec-Vtrans-Sk": secretKey, + "Sec-Vtrans-Token": `${sign}:${uuid}:${this.paths.videoTranslation}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTLocalizedError("requestTranslationFailed"); + } + const translationData = yandexProtobuf.decodeTranslationResponse(res.data); + switch (translationData.status) { + case VideoTranslationStatus.FAILED: + throw translationData?.message ? new VOTJSError("Yandex couldn't translate video", translationData) : new VOTLocalizedError("requestTranslationFailed"); + case VideoTranslationStatus.FINISHED: + case VideoTranslationStatus.PART_CONTENT: + if (!translationData.url) { + throw new VOTLocalizedError("audioNotReceived"); + } + return { + translated: true, + url: translationData.url, + remainingTime: translationData.remainingTime ?? -1, + }; + case VideoTranslationStatus.WAITING: + return { + translated: false, + remainingTime: translationData.remainingTime, + }; + case VideoTranslationStatus.LONG_WAITING: + case VideoTranslationStatus.LONG_WAITING_2: + return { + translated: false, + remainingTime: translationData.remainingTime ?? -1, + }; + default: + console.error("[vot.js] Unknown response", translationData); + throw new VOTJSError("Unknown response from Yandex", translationData); + } + } + async translateVideoVOTImpl({ url, videoId, service, requestLang = this.requestLang, responseLang = this.responseLang, headers = {}, }) { + const votData = convertVOT(service, videoId, url); + const res = await this.requestVOT(this.paths.videoTranslation, { + provider: "yandex", + service: votData.service, + videoId: votData.videoId, + fromLang: requestLang, + toLang: responseLang, + rawVideo: url, + }, headers); + if (!res.success) { + throw new VOTLocalizedError("requestTranslationFailed", res); + } + const translationData = res.data; + switch (translationData.status) { + case "failed": + throw new VOTJSError("Yandex couldn't translate video", translationData); + case "success": + if (!translationData.translatedUrl) { + throw new VOTLocalizedError("audioNotReceived"); + } + return { + translated: true, + url: translationData.translatedUrl, + remainingTime: -1, + }; + case "waiting": + return { + translated: false, + remainingTime: translationData.remainingTime, + message: translationData.message, + }; + } + } + async translateVideo({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, translationHelp = null, headers = {}, }) { + const { url, videoId, host } = videoData; + return this.isCustomFormat(url) + ? await this.translateVideoVOTImpl({ + url, + videoId, + service: host, + requestLang, + responseLang, + headers, + }) + : await this.translateVideoYAImpl({ + videoData, + requestLang, + responseLang, + translationHelp, + headers, + }); + } + async getSubtitles({ videoData, requestLang = this.requestLang, headers = {}, }) { + const { url } = videoData; + if (this.isCustomFormat(url)) { + throw new VOTLocalizedError("Unsupported video URL for getting subtitles"); // add translation + } + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeSubtitlesRequest(url, requestLang); + const sign = await getSignature(body); + const res = await this.request(this.paths.videoSubtitles, body, { + "Vsubs-Signature": await getSignature(body), + "Sec-Vsubs-Sk": secretKey, + "Sec-Vsubs-Token": `${sign}:${uuid}:${this.paths.videoSubtitles}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTJSError("Failed to request video subtitles", res); + } + return yandexProtobuf.decodeSubtitlesResponse(res.data); + } + async pingStream({ pingId, headers = {} }) { + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeStreamPingRequest(pingId); + const sign = await getSignature(body); + const res = await this.request(this.paths.streamPing, body, { + "Vtrans-Signature": await getSignature(body), + "Sec-Vtrans-Sk": secretKey, + "Sec-Vtrans-Token": `${sign}:${uuid}:${this.paths.streamPing}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTJSError("Failed to request stream ping", res); + } + return true; + } + async translateStream({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, headers = {}, }) { + const { url } = videoData; + if (this.isCustomFormat(url)) { + throw new VOTLocalizedError("Unsupported video URL for getting stream translation"); // add translation + } + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeStreamRequest(url, requestLang, responseLang); + const sign = await getSignature(body); + const res = await this.request(this.paths.streamTranslation, body, { + "Vtrans-Signature": await getSignature(body), + "Sec-Vtrans-Sk": secretKey, + "Sec-Vtrans-Token": `${sign}:${uuid}:${this.paths.streamTranslation}:${this.componentVersion}`, + ...headers, + }); + if (!res.success) { + throw new VOTJSError("Failed to request stream translation", res); + } + const translateResponse = yandexProtobuf.decodeStreamResponse(res.data); + const interval = translateResponse.interval; + switch (interval) { + case StreamInterval.NO_CONNECTION: + case StreamInterval.TRANSLATING: + return { + translated: false, + interval, + message: interval === StreamInterval.NO_CONNECTION + ? "streamNoConnectionToServer" + : "translationTakeFewMinutes", + }; + case StreamInterval.STREAMING: { + return { + translated: true, + interval, + pingId: translateResponse.pingId, + result: translateResponse.translatedInfo, + }; + } + default: + console.error("[vot.js] Unknown response", translateResponse); + throw new VOTJSError("Unknown response from Yandex", translateResponse); + } + } + async createSession(module) { + const uuid = getUUID(); + const body = yandexProtobuf.encodeYandexSessionRequest(uuid, module); + const res = await this.request(this.paths.createSession, body, { + "Vtrans-Signature": await getSignature(body), + }); + if (!res.success) { + throw new VOTJSError("Failed to request create session", res); + } + const subtitlesResponse = yandexProtobuf.decodeYandexSessionResponse(res.data); + return { + ...subtitlesResponse, + uuid, + }; + } +} +class VOTWorkerClient extends VOTClient { + async request(path, body, headers = {}) { + const options = this.getOpts(JSON.stringify({ + headers: { + ...this.headers, + ...headers, + }, + body: Array.from(body), + }), { + "Content-Type": "application/json", + }); + try { + const res = await this.fetch(`${this.schema}://${this.host}${path}`, options); + const data = await res.arrayBuffer(); + return { + success: res.status === 200, + data, + }; + } + catch (err) { + console.error("[vot.js]", err.message); + return { + success: false, + data: null, + }; + } + } } -function createVOTButton(html) { - const container = document.createElement("vot-block"); - container.classList.add("vot-segmented-button"); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/types/index.js - const translateButton = document.createElement("vot-block"); - translateButton.classList.add("vot-segment"); - translateButton.classList.add("vot-translate-button"); - translateButton.innerHTML = ``; - const separator = document.createElement("vot-block"); - separator.classList.add("vot-separator"); - const pipButton = document.createElement("vot-block"); - pipButton.classList.add("vot-segment-only-icon"); - pipButton.innerHTML = ``; - const separator2 = document.createElement("vot-block"); - separator2.classList.add("vot-separator"); - const menuButton = document.createElement("vot-block"); - menuButton.classList.add("vot-segment-only-icon"); - menuButton.innerHTML = ``; - const label = document.createElement("span"); - label.classList.add("vot-segment-label"); - label.innerHTML = html; - container.appendChild(translateButton); - container.appendChild(separator); - container.appendChild(pipButton); - container.appendChild(separator2); - container.appendChild(menuButton); - translateButton.appendChild(label); - return { - container, - translateButton, - separator, - pipButton, - separator2, - menuButton, - label, - }; -} -function createVOTMenu(html) { - const container = document.createElement("vot-block"); - container.classList.add("vot-menu"); - container.hidden = true; - const contentWrapper = document.createElement("vot-block"); - contentWrapper.classList.add("vot-menu-content-wrapper"); - const headerContainer = document.createElement("vot-block"); - headerContainer.classList.add("vot-menu-header-container"); - const bodyContainer = document.createElement("vot-block"); - bodyContainer.classList.add("vot-menu-body-container"); +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/index.js - const footerContainer = document.createElement("vot-block"); - footerContainer.classList.add("vot-menu-footer-container"); - const titleContainer = document.createElement("vot-block"); - titleContainer.classList.add("vot-menu-title-container"); - const title = document.createElement("vot-block"); - title.classList.add("vot-menu-title"); - title.innerHTML = html; - container.appendChild(contentWrapper); - contentWrapper.appendChild(headerContainer); - contentWrapper.appendChild(bodyContainer); - contentWrapper.appendChild(footerContainer); - headerContainer.appendChild(titleContainer); - titleContainer.appendChild(title); - return { - container, - contentWrapper, - headerContainer, - bodyContainer, - footerContainer, - titleContainer, - title, - }; +;// CONCATENATED MODULE: ./node_modules/vot.js/dist/utils/subs.js +function convertToSrtTimeFormat(seconds) { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainingSeconds = Math.floor(seconds % 60); + const milliseconds = Math.floor((seconds % 1) * 1000); + return `${hours.toString().padStart(2, "0")}:${minutes + .toString() + .padStart(2, "0")}:${remainingSeconds + .toString() + .padStart(2, "0")},${milliseconds.toString().padStart(3, "0")}`; } - -function createVOTSelectLabel(text) { - const label = document.createElement("span"); - label.classList.add("vot-select-label"); - label.innerText = text; - return label; +function convertSubs(data, output = "srt") { + const subs = data.subtitles + .map((sub, idx) => { + const startTime = sub.startMs / 1000.0; + const endTime = (sub.startMs + sub.durationMs) / 1000.0; + const result = output === "srt" ? `${idx + 1}\n` : ""; + return (result + + `${convertToSrtTimeFormat(startTime)} --> ${convertToSrtTimeFormat(endTime)}\n${sub.text}\n\n`); + }) + .join("") + .trim(); + return output === "vtt" ? `WEBVTT\n\n${subs}` : subs; } -function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { - const onSelectCb = options.onSelectCb || function () {}; - const labelElement = options.labelElement || ""; - let selectedItems = []; +;// CONCATENATED MODULE: ./node_modules/lit-html/lit-html.js +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const n=globalThis,c=n.trustedTypes,h=c?c.createPolicy("lit-html",{createHTML:t=>t}):void 0,f="$lit$",v=`lit$${Math.random().toFixed(9).slice(2)}$`,m="?"+v,_=`<${m}>`,w=document,lt=()=>w.createComment(""),st=t=>null===t||"object"!=typeof t&&"function"!=typeof t,g=Array.isArray,$=t=>g(t)||"function"==typeof t?.[Symbol.iterator],x="[ \t\n\f\r]",T=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,k=/>/g,O=RegExp(`>|${x}(?:([^\\s"'>=/]+)(${x}*=${x}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),S=/'/g,j=/"/g,M=/^(?:script|style|textarea|title)$/i,P=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),ke=P(1),Oe=P(2),Se=P(3),R=Symbol.for("lit-noChange"),D=Symbol.for("lit-nothing"),V=new WeakMap,I=w.createTreeWalker(w,129);function N(t,i){if(!g(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==h?h.createHTML(i):i}const U=(t,i)=>{const s=t.length-1,e=[];let h,o=2===i?"":3===i?"":"",n=T;for(let i=0;i"===l[0]?(n=h??T,c=-1):void 0===l[1]?c=-2:(c=n.lastIndex-l[2].length,r=l[1],n=void 0===l[3]?O:'"'===l[3]?j:S):n===j||n===S?n=O:n===E||n===k?n=T:(n=O,h=void 0);const u=n===O&&t[i+1].startsWith("/>")?" ":"";o+=n===T?s+_:c>=0?(e.push(r),s.slice(0,c)+f+s.slice(c)+v+u):s+v+(-2===c?i:u)}return[N(t,o+(t[s]||"")+(2===i?"":3===i?"":"")),e]};class B{constructor({strings:t,_$litType$:i},s){let e;this.parts=[];let h=0,o=0;const n=t.length-1,r=this.parts,[l,a]=U(t,i);if(this.el=B.createElement(l,s),I.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(e=I.nextNode())&&r.length0){e.textContent=c?c.emptyScript:"";for(let s=0;s2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=D}_$AI(t,i=this,s,e){const h=this.strings;let o=!1;if(void 0===h)t=z(this,t,i,0),o=!st(t)||t!==this._$AH&&t!==R,o&&(this._$AH=t);else{const e=t;let n,r;for(t=h[0],n=0;n{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new et(i.insertBefore(lt(),t),t,void 0,s??{})}return h._$AI(t),h}; +//# sourceMappingURL=lit-html.js.map - const container = document.createElement("vot-block"); - container.classList.add("vot-select"); +;// CONCATENATED MODULE: ./src/config/config.js +// CONFIGURATION +const workerHost = "api.browser.yandex.ru"; +const m3u8ProxyHost = "m3u8-proxy.toil.cc"; // used for streaming +const proxyWorkerHost = "vot-worker.toil.cc"; // used for cloudflare version +const votBackendUrl = "https://vot-api.toil.cc/v1"; - if (labelElement) { - container.appendChild(labelElement); - } +const defaultAutoVolume = 0.15; // 0.0 - 1.0 (0% - 100%) - default volume of the video with the translation +const maxAudioVolume = 900; +const defaultTranslationService = "yandex"; +const defaultDetectService = "yandex"; - const outer = document.createElement("vot-block"); - outer.classList.add("vot-select-outer"); +const detectUrls = { + yandex: "https://translate.toil.cc/detect", + rustServer: "https://rust-server-531j.onrender.com/detect", +}; - const title = document.createElement("span"); - title.classList.add("vot-select-title"); - title.innerText = selectTitle; +const translateUrls = { + yandex: "https://translate.toil.cc/translate", + deepl: "https://translate-deepl.toil.cc/translate", +}; - if (selectTitle === undefined) { - title.innerText = items.find((i) => i.selected === true)?.label; - } - const arrowIcon = document.createElement("vot-block"); - arrowIcon.classList.add("vot-select-arrow-icon"); - arrowIcon.innerHTML = ``; - outer.append(title, arrowIcon); - outer.onclick = () => { - const votSelectDialog = createDialog(dialogTitle); - votSelectDialog.container.classList.add("vot-dialog-temp"); - votSelectDialog.container.hidden = false; - document.documentElement.appendChild(votSelectDialog.container); +// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js +var injectStylesIntoStyleTag = __webpack_require__("./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js"); +var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag); +// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleDomAPI.js +var styleDomAPI = __webpack_require__("./node_modules/style-loader/dist/runtime/styleDomAPI.js"); +var styleDomAPI_default = /*#__PURE__*/__webpack_require__.n(styleDomAPI); +// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertBySelector.js +var insertBySelector = __webpack_require__("./node_modules/style-loader/dist/runtime/insertBySelector.js"); +var insertBySelector_default = /*#__PURE__*/__webpack_require__.n(insertBySelector); +// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js +var setAttributesWithoutAttributes = __webpack_require__("./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js"); +var setAttributesWithoutAttributes_default = /*#__PURE__*/__webpack_require__.n(setAttributesWithoutAttributes); +// EXTERNAL MODULE: ./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js +var style_loader_insertStyleElement = __webpack_require__("./node_modules/webpack-monkey/lib/node/deps/style-loader-insertStyleElement.js"); +var style_loader_insertStyleElement_default = /*#__PURE__*/__webpack_require__.n(style_loader_insertStyleElement); +// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleTagTransform.js +var styleTagTransform = __webpack_require__("./node_modules/style-loader/dist/runtime/styleTagTransform.js"); +var styleTagTransform_default = /*#__PURE__*/__webpack_require__.n(styleTagTransform); +// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss +var main = __webpack_require__("./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/styles/main.scss"); +;// CONCATENATED MODULE: ./src/styles/main.scss + + + + + + + + + + - const contentList = document.createElement("vot-block"); - contentList.classList.add("vot-select-content-list"); +var options = {}; - for (const item of items) { - const contentItem = document.createElement("vot-block"); - contentItem.classList.add("vot-select-content-item"); - contentItem.innerText = item.label; - contentItem.dataset.votSelected = item.selected; - contentItem.dataset.votValue = item.value; - if (item.disabled) { - contentItem.inert = true; - } +options.styleTagTransform = (styleTagTransform_default()); +options.setAttributes = (setAttributesWithoutAttributes_default()); +options.insert = insertBySelector_default().bind(null, "head"); +options.domAPI = (styleDomAPI_default()); +options.insertStyleElement = (style_loader_insertStyleElement_default()); - contentItem.onclick = async (e) => { - if (e.target.inert) return; +var update = injectStylesIntoStyleTag_default()(main/* default */.A, options); - // removing the selected value for updating - const contentItems = contentList.childNodes; - for (let ci of contentItems) { - ci.dataset.votSelected = false; - } - // fixed selection after closing the modal and opening again - for (let i of items) { - i.selected = i.value === item.value; - } - contentItem.dataset.votSelected = true; - title.innerText = item.label; - // !!! use e.target.dataset.votValue instead of e.target.value !!! - await onSelectCb(e); - }; - contentList.appendChild(contentItem); - } - // search logic - const votSearchLangTextfield = createTextfield( - localizationProvider.get("searchField"), - ); + /* harmony default export */ const styles_main = (main/* default */.A && main/* default */.A.locals ? main/* default */.A.locals : undefined); - votSearchLangTextfield.input.oninput = (e) => { - const searchText = e.target.value.toLowerCase(); - // check if there are lovercase characters in the string. used for smarter search - for (let i = 0; i < selectedItems.length; i++) { - const ci = selectedItems[i]; - ci.hidden = !ci.innerText.toLowerCase().includes(searchText); - } - }; +;// CONCATENATED MODULE: ./src/ui.js - votSelectDialog.bodyContainer.append( - votSearchLangTextfield.container, - contentList, - ); - selectedItems = contentList.childNodes; - // remove the modal so that they do not accumulate - votSelectDialog.backdrop.onclick = votSelectDialog.closeButton.onclick = - () => { - votSelectDialog.container.remove(); - selectedItems = []; - }; - }; - container.append(outer); - const setTitle = (newTitle) => { - title.innerText = newTitle; - }; - const setSelected = (val) => { - const selectedItemsArray = Array.from(selectedItems).filter( - (ci) => !ci.inert, - ); - for (let i = 0; i < selectedItemsArray.length; i++) { - const ci = selectedItemsArray[i]; - ci.dataset.votSelected = ci.dataset.votValue === val; - } - for (let i = 0; i < items.length; i++) { - const currentItem = items[i]; - currentItem.selected = String(currentItem.value) === val; - } - }; +const undefinedPhrase = "#UNDEFINED"; +const arrowIconRaw = Oe` + +`; - const updateItems = (newItems) => { - items = newItems; - }; +/** + * Create header element + * + * @param {HTMLElement|string} html - header content + * @param {1|2|3|4|5|6} level - header level + * @return {HTMLElement} HTML header element + */ +function createHeader(html, level = 4) { + const header = document.createElement("vot-block"); + header.classList.add("vot-header"); + header.classList.add(`vot-header-level-${level}`); + header.append(html); - return { - container, - title, - arrowIcon, - labelElement, - setTitle, - setSelected, - updateItems, - }; + return header; } -function createVOTLanguageSelect(options) { - const fromTitle = options.fromTitle || "#UNDEFINED"; - const fromDialogTitle = options.fromDialogTitle || "#UNDEFINED"; - const fromItems = options.fromItems || []; - const fromOnSelectCB = options.fromOnSelectCB || function () {}; - const toTitle = options.toTitle || "#UNDEFINED"; - const toDialogTitle = options.toDialogTitle || "#UNDEFINED"; - const toItems = options.toItems || []; - const toOnSelectCB = options.toOnSelectCB || function () {}; - +/** + * Create information element + * + * @param {HTMLElement|string} html - label content + * @param {HTMLElement|string} valueHtml - value content + * @return {{ + * container: HTMLElement, + * header: HTMLElement, + * value: HTMLElement + * }} information elements + */ +function createInformation(html, valueHtml) { const container = document.createElement("vot-block"); - container.classList.add("vot-lang-select"); - - const fromSelect = createVOTSelect(fromTitle, fromDialogTitle, fromItems, { - onSelectCb: fromOnSelectCB, - }); + container.classList.add("vot-info"); - const icon = document.createElement("vot-block"); - icon.classList.add("vot-lang-select-icon"); - icon.innerHTML = ``; + const header = document.createElement("vot-block"); + header.append(html); - const toSelect = createVOTSelect(toTitle, toDialogTitle, toItems, { - onSelectCb: toOnSelectCB, - }); + const value = document.createElement("vot-block"); + value.append(valueHtml); - container.append(fromSelect.container, icon, toSelect.container); + container.append(header, value); return { container, - fromSelect, - icon, - toSelect, + header, + value, }; } -/* harmony default export */ const ui = ({ - createHeader, - createInformation, - createButton, - createTextButton, - createOutlinedButton, - createIconButton, - createCheckbox, - createSlider, - createTextfield, - createDialog, - createVOTButton, - createVOTMenu, - createVOTSelectLabel, - createVOTSelect, - createVOTLanguageSelect, - updateSlider, -}); +/** + * Create button + * + * @param {HTMLElement|string} html - button content + * @return {HTMLElement} HTML button element + */ +function createButton(html) { + const button = document.createElement("vot-block"); + button.classList.add("vot-button"); + button.append(html); -;// CONCATENATED MODULE: ./src/utils/VOTLocalizedError.js + return button; +} +/** + * Create text button + * + * @param {HTMLElement|string} html - button content + * @return {HTMLElement} HTML text button element + */ +function createTextButton(html) { + const button = document.createElement("vot-block"); + button.classList.add("vot-text-button"); + button.append(html); -class VOTLocalizedError extends Error { - constructor(message) { - super(localizationProvider.getDefault(message)); - this.name = "VOTLocalizedError"; - this.unlocalizedMessage = message; - this.localizedMessage = localizationProvider.get(message); - } + return button; } -;// CONCATENATED MODULE: ./src/utils/volume.js -// element - audio / video element -function syncVolume(element, sliderVolume, otherSliderVolume, tempVolume) { - let finalValue = sliderVolume; - if (sliderVolume > tempVolume) { - // sliderVolume = 100 - // tempVolume = 69 - // volume = 15 - // 100 - 69 = 31 - // 15 + 31 = 46 - final video volume - finalValue = otherSliderVolume + (sliderVolume - tempVolume); - finalValue = finalValue > 100 ? 100 : Math.max(finalValue, 0); +/** + * Create outlined button + * + * @param {HTMLElement|string} html - button content + * @return {HTMLElement} HTML outlined button element + */ +function createOutlinedButton(html) { + const button = document.createElement("vot-block"); + button.classList.add("vot-outlined-button"); + button.append(html); - element.volume = finalValue / 100; - } else if (sliderVolume < tempVolume) { - // sliderVolume = 69 - // tempVolume = 100 - // volume = 15 - // 100 - 69 = 31 - // 15 - 31 = 0 - final video volume - finalValue = otherSliderVolume - (tempVolume - sliderVolume); - finalValue = finalValue > 100 ? 100 : Math.max(finalValue, 0); + return button; +} - element.volume = finalValue / 100; - } +/** + * Create icon button + * + * @param {TemplateResult} templateHtml - icon svg lit template + * @return {HTMLElement} HTML icon button element + */ +function createIconButton(templateHtml) { + const button = document.createElement("vot-block"); + button.classList.add("vot-icon-button"); + Q(templateHtml, button); - return finalValue; + return button; } +/** + * Create checkbox + * + * @param {string|HTMLElement} html - label content + * @param {boolean} value - checkbox state + * @return {{ + * container: HTMLElement, + * input: HTMLInputElement, + * label: HTMLSpanElement + * }} checkbox elements + */ +function createCheckbox(html, value = false) { + const container = document.createElement("label"); + container.classList.add("vot-checkbox"); + + const input = document.createElement("input"); + input.type = "checkbox"; + input.checked = Boolean(value); + const label = document.createElement("span"); + label.append(html); -;// CONCATENATED MODULE: ./src/yandexProtobuf.js -// coursera & udemy translation help object -const VideoTranslationHelpObject = new protobuf.Type( - "VideoTranslationHelpObject", -) - .add(new protobuf.Field("target", 1, "string")) // video_file_url or subtitles_file_url - .add(new protobuf.Field("targetUrl", 2, "string")); // url to video_file or url to subtitles - -const VideoTranslationRequest = new protobuf.Type("VideoTranslationRequest") - .add(new protobuf.Field("url", 3, "string")) - .add(new protobuf.Field("deviceId", 4, "string")) // used in mobile version - .add(new protobuf.Field("firstRequest", 5, "bool")) // true for the first request, false for subsequent ones - .add(new protobuf.Field("duration", 6, "double")) - .add(new protobuf.Field("unknown2", 7, "int32")) // 1 1 - .add(new protobuf.Field("language", 8, "string")) // source language code - .add(new protobuf.Field("forceSourceLang", 9, "bool")) // 0 - auto detected by yabrowser, 1 - user set his own lang by dropdown - .add(new protobuf.Field("unknown4", 10, "int32")) // 0 0 - .add( - new protobuf.Field( - "translationHelp", - 11, - "VideoTranslationHelpObject", - "repeated", - ), - ) // array for translation assistance ([0] -> {2: link to video, 1: "video_file_url"}, [1] -> {2: link to subtitles, 1: "subtitles_file_url"}) - .add(new protobuf.Field("responseLanguage", 14, "string")) - .add(new protobuf.Field("unknown5", 15, "int32")) // 0 - .add(new protobuf.Field("unknown6", 16, "int32")) // 1 - .add(new protobuf.Field("bypassCache", 17, "bool")); // ? maybe they have some kind of bypass limiter from one IP, because after one such request it stopped working - -const VideoSubtitlesRequest = new protobuf.Type("VideoSubtitlesRequest") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("language", 2, "string")); // source language code - -const VideoStreamRequest = new protobuf.Type("VideoStreamRequest") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("language", 2, "string")) - .add(new protobuf.Field("responseLanguage", 3, "string")); - -const VideoStreamPingRequest = new protobuf.Type("VideoStreamPingRequest").add( - new protobuf.Field("pingId", 1, "int32"), -); + container.append(input, label); -const VideoTranslationResponse = new protobuf.Type("VideoTranslationResponse") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("duration", 2, "double")) - .add(new protobuf.Field("status", 4, "int32")) - .add(new protobuf.Field("remainingTime", 5, "int32")) // secs before translation (used as interval before next request in yaBrowser) - .add(new protobuf.Field("unknown0", 6, "int32")) // unknown 0 (1st request) -> 10 (2nd, 3th and etc requests). (if status is 0) - .add(new protobuf.Field("translationId", 7, "string")) - .add(new protobuf.Field("language", 8, "string")) // detected language (if the wrong one is set) - .add(new protobuf.Field("message", 9, "string")); - -const VideoSubtitlesObject = new protobuf.Type("VideoSubtitlesObject") - .add(new protobuf.Field("language", 1, "string")) - .add(new protobuf.Field("url", 2, "string")) - .add(new protobuf.Field("unknown2", 3, "int32")) - .add(new protobuf.Field("translatedLanguage", 4, "string")) - .add(new protobuf.Field("translatedUrl", 5, "string")) - .add(new protobuf.Field("unknown5", 6, "int32")) - .add(new protobuf.Field("unknown6", 7, "int32")); - -const VideoSubtitlesResponse = new protobuf.Type("VideoSubtitlesResponse") - .add(new protobuf.Field("waiting", 1, "int32")) // 0 - finished/failed | 1 - waiting result (1 - ~10min, maybe more) - .add(new protobuf.Field("subtitles", 2, "VideoSubtitlesObject", "repeated")); - -const VideoStreamObject = new protobuf.Type("VideoStreamObject") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("timestamp", 2, "int64")); // timestamp in ms (probably means the time of 1 request to translate the stream) - -const VideoStreamResponse = new protobuf.Type("VideoStreamResponse") - .add(new protobuf.Field("interval", 1, "int32")) // 20s - streaming, 10s - translating, 0s - there is no connection with the server (the broadcast is finished or deleted) - .add(new protobuf.Field("translatedInfo", 2, "VideoStreamObject")) - .add(new protobuf.Field("pingId", 3, "int32")); - -// /session/create -const YandexSessionRequest = new protobuf.Type("YandexSessionRequest") - .add(new protobuf.Field("uuid", 1, "string")) - .add(new protobuf.Field("module", 2, "string")); - -const YandexSessionResponse = new protobuf.Type("YandexSessionResponse") - .add(new protobuf.Field("sign", 1, "string")) - .add(new protobuf.Field("expires", 2, "int32")); - -// * Yandex has been skipping any translation streams for a long time (whitelist always return true) -// * Most likely, it is already outdated and will not be used -// const VideoWhitelistStreamRequest = new protobuf.Type("VideoWhitelistStreamRequest") -// .add(new protobuf.Field("url", 1, "string")) -// .add(new protobuf.Field("deviceId", 4, "string")) - -// const VideoWhitelistStreamResponse = new protobuf.Type("VideoWhitelistStreamResponse") -// .add(new protobuf.Field("inWhitelist", 1, "bool")) - -// Create a root namespace and add the types -const root = new protobuf.Root() - .define("yandex") - .add(VideoTranslationHelpObject) - .add(VideoTranslationRequest) - .add(VideoTranslationResponse) - .add(VideoSubtitlesRequest) - .add(VideoSubtitlesObject) - .add(VideoSubtitlesResponse) - .add(VideoStreamPingRequest) - .add(VideoStreamRequest) - .add(VideoStreamObject) - .add(VideoStreamResponse) - .add(YandexSessionRequest) - .add(YandexSessionResponse); - -// Export the encoding and decoding functions -const yandexProtobuf = { - encodeTranslationRequest( - url, - duration, - requestLang, - responseLang, - translationHelp, - ) { - return root.VideoTranslationRequest.encode({ - url, - firstRequest: true, - duration, - unknown2: 1, - language: requestLang, - forceSourceLang: false, - unknown4: 0, - translationHelp, - responseLanguage: responseLang, - unknown5: 0, - unknown6: 1, - bypassCache: false, - }).finish(); - }, - decodeTranslationResponse(response) { - return root.VideoTranslationResponse.decode(new Uint8Array(response)); - }, - encodeSubtitlesRequest(url, requestLang) { - return root.VideoSubtitlesRequest.encode({ - url, - language: requestLang, - }).finish(); - }, - decodeSubtitlesResponse(response) { - return root.VideoSubtitlesResponse.decode(new Uint8Array(response)); - }, - encodeStreamPingRequest(pingId) { - return root.VideoStreamPingRequest.encode({ - pingId, - }).finish(); - }, - encodeStreamRequest(url, requestLang, responseLang) { - return root.VideoStreamRequest.encode({ - url, - language: requestLang, - responseLanguage: responseLang, - }).finish(); - }, - decodeStreamResponse(response) { - return root.VideoStreamResponse.decode(new Uint8Array(response)); - }, - encodeYandexSessionRequest(uuid, module) { - return root.YandexSessionRequest.encode({ - uuid, - module, - }).finish(); - }, - decodeYandexSessionResponse(response) { - return root.YandexSessionResponse.decode(new Uint8Array(response)); - }, -}; + return { container, input, label }; +} -// EXTERNAL MODULE: ./node_modules/bowser/es5.js -var es5 = __webpack_require__("./node_modules/bowser/es5.js"); -;// CONCATENATED MODULE: ./src/getUUID.js -function getUUID() { - const hexDigits = "0123456789ABCDEF"; - let uuid = ""; - for (let i = 0; i < 32; i++) { - const randomDigit = Math.floor(Math.random() * 16); - uuid += hexDigits[randomDigit]; - } - return uuid; +/** + * Update slider value + * + * @param {HTMLInputElement} input - slider input element + */ +function updateSlider(input) { + const value = +input.value; + const min = +input.min; + const max = +input.max; + const progress = (value - min) / (max - min); + input.parentElement.setAttribute("style", `--vot-progress: ${progress}`); } +/** + * Create slider + * + * @param {string|HTMLElement} html - label content + * @param {number} value - default value + * @param {number} min - min value + * @param {number} max - max value + * @return {{ + * container: HTMLElement, + * input: HTMLInputElement, + * label: HTMLSpanElement + * }} slider elements + */ +function createSlider(labelHtml, value = 50, min = 0, max = 100) { + const container = document.createElement("vot-block"); + container.classList.add("vot-slider"); + const input = document.createElement("input"); + input.type = "range"; + input.min = min; + input.max = max; + input.value = value; -;// CONCATENATED MODULE: ./src/getSignature.js + const label = document.createElement("span"); + Q(labelHtml, label); + container.append(input, label); -// Create a key from the HMAC secret -const CryptoKey = window.crypto.subtle.importKey( - "raw", - new TextEncoder().encode(config/* yandexHmacKey */.S7), - { name: "HMAC", hash: { name: "SHA-256" } }, - false, - ["sign", "verify"], -); + input.addEventListener("input", (e) => updateSlider(e.target)); + updateSlider(input); -async function getSignature(body) { - const key = await CryptoKey; - return new Uint8Array( - // Sign the body with the key - await window.crypto.subtle.sign("HMAC", key, body), - // Convert the signature to a hex string - ).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); + return { + container, + input, + label, + }; } +/** + * Create textfield + * + * @param {string|HTMLElement} html - label content + * @param {string} value - default value + * @param {string} placeholder - textfield placeholder + * @param {boolean} multiline - multiline textfield + * @return {{ + * container: HTMLElement, + * input: HTMLInputElement, + * label: HTMLSpanElement + * }} textfield elements + */ +function createTextfield( + html, + value = "", + placeholder = " ", + multiline = false, +) { + const container = document.createElement("vot-block"); + container.classList.add("vot-textfield"); + const input = document.createElement(multiline ? "textarea" : "input"); + input.placeholder = placeholder; + input.value = value; + if (!html) { + input.classList.add("vot-show-placeholer"); + } -;// CONCATENATED MODULE: ./src/rsp.js - - - + const label = document.createElement("span"); + label.append(html); + container.append(input, label); -// Request stream ping from Yandex API -async function requestStreamPing(pingId, callback) { - try { - debug/* default */.A.log("requestStreamPing"); - // ! CURRENT CLOUDFLARE WORKER DOESN'T SUPPORT STREAM TRANSLATIONS - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeStreamPingRequest(pingId); - // Send the request - await yandexRequest( - "/stream-translation/ping-stream", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } + return { + container, + input, + label, + }; } -/* harmony default export */ const rsp = (requestStreamPing); - -;// CONCATENATED MODULE: ./src/rst.js +/** + * Create dialog + * + * @param {string|HTMLElement} html - title content + * @return {{ + * container: HTMLElement, + * backdrop: HTMLElement, + * dialog: HTMLElement, + * contentWrapper: HTMLElement, + * headerContainer: HTMLElement, + * bodyContainer: HTMLElement, + * footerContainer: HTMLElement, + * titleContainer: HTMLElement, + * closeButton: HTMLElement, + * title: HTMLElement, + * }} dialog elements + */ +function createDialog(html) { + const container = document.createElement("vot-block"); + container.classList.add("vot-dialog-container"); + container.hidden = true; + const backdrop = document.createElement("vot-block"); + backdrop.classList.add("vot-dialog-backdrop"); + const dialog = document.createElement("vot-block"); + dialog.classList.add("vot-dialog"); + const contentWrapper = document.createElement("vot-block"); + contentWrapper.classList.add("vot-dialog-content-wrapper"); + const headerContainer = document.createElement("vot-block"); + headerContainer.classList.add("vot-dialog-header-container"); -// Request stream translation from Yandex API -async function requestStreamTranslation( - url, - requestLang, - responseLang, - callback, -) { - try { - debug/* default */.A.log("requestStreamTranslation"); - // ! CURRENT CLOUDFLARE WORKER DOESN'T SUPPORT STREAM TRANSLATIONS - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeStreamRequest( - url, - requestLang, - responseLang, - ); - // Send the request - await yandexRequest( - "/stream-translation/translate-stream", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} + const bodyContainer = document.createElement("vot-block"); + bodyContainer.classList.add("vot-dialog-body-container"); -/* harmony default export */ const rst = (requestStreamTranslation); + const footerContainer = document.createElement("vot-block"); + footerContainer.classList.add("vot-dialog-footer-container"); -;// CONCATENATED MODULE: ./src/rvt.js + const titleContainer = document.createElement("vot-block"); + titleContainer.classList.add("vot-dialog-title-container"); + const closeButton = createIconButton( + Oe` + + `, + ); + closeButton.classList.add("vot-dialog-close-button"); + backdrop.onclick = closeButton.onclick = () => { + container.hidden = true; + }; + const title = document.createElement("vot-block"); + title.classList.add("vot-dialog-title"); + title.append(html); + container.append(backdrop, dialog); + dialog.append(contentWrapper); + contentWrapper.append(headerContainer, bodyContainer, footerContainer); + headerContainer.append(titleContainer, closeButton); + titleContainer.append(title); -// Request video translation from Yandex API -async function requestVideoTranslation( - url, - duration, - requestLang, - responseLang, - translationHelp, - callback, -) { - try { - debug/* default */.A.log("requestVideoTranslation"); - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeTranslationRequest( - url, - duration, - requestLang, - responseLang, - translationHelp, - ); - // Send the request - await yandexRequest( - // "/stream-translation/whitelist-stream", - // "/stream-translation/translate-stream", - "/video-translation/translate", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } + return { + container, + backdrop, + dialog, + contentWrapper, + headerContainer, + bodyContainer, + footerContainer, + titleContainer, + closeButton, + title, + }; } -/* harmony default export */ const rvt = (requestVideoTranslation); - -;// CONCATENATED MODULE: ./src/rvs.js - +/** + * Create VOTButton + * + * @param {string|HTMLElement} label - label content + * @return {{ + * container: HTMLElement, + * translateButton: HTMLElement, + * separator: HTMLElement, + * pipButton: HTMLElement, + * separator2: HTMLElement, + * menuButton: HTMLElement, + * label: HTMLSpanElement, + * }} VOTButton elements + */ +function createVOTButton(labelHtml) { + const container = document.createElement("vot-block"); + container.classList.add("vot-segmented-button"); + const translateButton = document.createElement("vot-block"); + translateButton.classList.add("vot-segment"); + translateButton.classList.add("vot-translate-button"); + Q( + Oe` + + + + `, + translateButton, + ); + const separator = document.createElement("vot-block"); + separator.classList.add("vot-separator"); + const pipButton = document.createElement("vot-block"); + pipButton.classList.add("vot-segment-only-icon"); + Q( + Oe` + + `, + pipButton, + ); -// Request video subtitles from Yandex API -async function requestVideoSubtitles(url, requestLang, callback) { - try { - debug/* default */.A.log("requestVideoSubtitles"); - const yar = await Promise.resolve(/* import() */).then(__webpack_require__.bind(__webpack_require__, "./src/yandexRequest.js")); - const yandexRequest = yar.default; - debug/* default */.A.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeSubtitlesRequest(url, requestLang); - // Send the request - await yandexRequest( - "/video-subtitles/get-subtitles", - body, - { - "Vsubs-Signature": await getSignature(body), - "Sec-Vsubs-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} + const separator2 = document.createElement("vot-block"); + separator2.classList.add("vot-separator"); -/* harmony default export */ const rvs = (requestVideoSubtitles); + const menuButton = document.createElement("vot-block"); + menuButton.classList.add("vot-segment-only-icon"); + Q( + Oe` + + `, + menuButton, + ); -;// CONCATENATED MODULE: ./src/subtitles.js + const label = document.createElement("span"); + label.classList.add("vot-segment-label"); + label.append(labelHtml); + container.append( + translateButton, + separator, + pipButton, + separator2, + menuButton, + ); + translateButton.append(label); + return { + container, + translateButton, + separator, + pipButton, + separator2, + menuButton, + label, + }; +} +/** + * Create VOTMenu + * + * @param {string|HTMLElement} html - title content + * @return {{ + * container: HTMLElement, + * contentWrapper: HTMLElement, + * headerContainer: HTMLElement, + * bodyContainer: HTMLElement, + * footerContainer: HTMLElement, + * titleContainer: HTMLElement, + * title: HTMLSpanElement, + * }} VOTMenu elements + */ +function createVOTMenu(html) { + const container = document.createElement("vot-block"); + container.classList.add("vot-menu"); + container.hidden = true; + const contentWrapper = document.createElement("vot-block"); + contentWrapper.classList.add("vot-menu-content-wrapper"); + const headerContainer = document.createElement("vot-block"); + headerContainer.classList.add("vot-menu-header-container"); -function formatYandexSubtitlesTokens(line) { - const lineEndMs = line.startMs + line.durationMs; - return line.tokens.reduce((result, token, index) => { - const nextToken = line.tokens[index + 1]; - let lastToken; - if (result.length > 0) { - lastToken = result[result.length - 1]; - } - const alignRangeEnd = lastToken?.alignRange?.end ?? 0; - const newAlignRangeEnd = alignRangeEnd + token.text.length; - token.alignRange = { - start: alignRangeEnd, - end: newAlignRangeEnd, - }; - result.push(token); - if (nextToken) { - const endMs = token.startMs + token.durationMs; - const durationMs = nextToken.startMs - ? nextToken.startMs - endMs - : lineEndMs - endMs; - result.push({ - text: " ", - startMs: endMs, - durationMs, - alignRange: { - start: newAlignRangeEnd, - end: newAlignRangeEnd + 1, - }, - }); - } - return result; - }, []); -} + const bodyContainer = document.createElement("vot-block"); + bodyContainer.classList.add("vot-menu-body-container"); -function createSubtitlesTokens(line, previousLineLastToken) { - const tokens = line.text.split(/([\n \t])/).reduce((result, tokenText) => { - if (tokenText.length) { - const lastToken = result[result.length - 1] ?? previousLineLastToken; - const alignRangeStart = lastToken?.alignRange?.end ?? 0; - const alignRangeEnd = alignRangeStart + tokenText.length; - result.push({ - text: tokenText, - alignRange: { - start: alignRangeStart, - end: alignRangeEnd, - }, - }); - } - return result; - }, []); - const tokenDurationMs = Math.floor(line.durationMs / tokens.length); - const lineEndMs = line.startMs + line.durationMs; - return tokens.map((token, index) => { - const isLastToken = index === tokens.length - 1; - const startMs = line.startMs + tokenDurationMs * index; - const durationMs = isLastToken ? lineEndMs - startMs : tokenDurationMs; - return { - ...token, - startMs, - durationMs, - }; - }); -} + const footerContainer = document.createElement("vot-block"); + footerContainer.classList.add("vot-menu-footer-container"); -function getSubtitlesTokens(subtitles, source) { - const result = []; - let lastToken; - for (let i = 0; i < subtitles.subtitles.length; i++) { - const line = subtitles.subtitles[i]; - let tokens; - if (line?.tokens?.length) { - if (source === "yandex") { - tokens = formatYandexSubtitlesTokens(line); - } else { - console.warn("[VOT] Unsupported subtitles tokens type: ", source); - subtitles.containsTokens = false; - return null; - } - } else { - tokens = createSubtitlesTokens(line, lastToken); - } - lastToken = tokens[tokens.length - 1]; - result.push({ - ...line, - tokens, - }); - } - subtitles.containsTokens = true; - return result; -} + const titleContainer = document.createElement("vot-block"); + titleContainer.classList.add("vot-menu-title-container"); -function formatYoutubeSubtitles(subtitles) { - const result = { - containsTokens: false, - subtitles: [], - }; - if ( - typeof subtitles !== "object" || - !("events" in subtitles) || - !Array.isArray(subtitles.events) - ) { - console.error("[VOT] Failed to format youtube subtitles", subtitles); - return result; - } - for (let i = 0; i < subtitles.events.length; i++) { - if (!subtitles.events[i].segs) continue; - const text = subtitles.events[i].segs - .map((e) => e.utf8.replace(/^( +| +)$/g, "")) - .join(" "); - let durationMs = subtitles.events[i].dDurationMs; - if ( - subtitles.events[i + 1] && - subtitles.events[i].tStartMs + subtitles.events[i].dDurationMs > - subtitles.events[i + 1].tStartMs - ) { - durationMs = - subtitles.events[i + 1].tStartMs - subtitles.events[i].tStartMs; - } - if (text !== "\n") { - result.subtitles.push({ - text, - startMs: subtitles.events[i].tStartMs, - durationMs, - }); - } - } - return result; -} + const title = document.createElement("vot-block"); + title.classList.add("vot-menu-title"); + title.append(html); -function convertToSrtTimeFormat(seconds) { - let hours = Math.floor(seconds / 3600); - let minutes = Math.floor((seconds % 3600) / 60); - let remainingSeconds = Math.floor(seconds % 60); - let milliseconds = Math.floor((seconds % 1) * 1000); + container.append(contentWrapper); + contentWrapper.append(headerContainer, bodyContainer, footerContainer); + headerContainer.append(titleContainer); + titleContainer.append(title); - return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")},${milliseconds.toString().padStart(3, "0")}`; + return { + container, + contentWrapper, + headerContainer, + bodyContainer, + footerContainer, + titleContainer, + title, + }; } -function jsonToSrt(jsonData) { - let srtContent = ""; - let index = 1; - for (const entry of jsonData.subtitles) { - let startTime = entry.startMs / 1000.0; - let endTime = (entry.startMs + entry.durationMs) / 1000.0; - - srtContent += `${index}\n`; - srtContent += `${convertToSrtTimeFormat(startTime)} --> ${convertToSrtTimeFormat(endTime)}\n`; - srtContent += `${entry.text}\n\n`; - index++; - } - - return srtContent.trim(); +/** + * Create VOTSelectLabel + * + * @param {string} text - label text + * @return {HTMLSpanElement} VOTSelectLabel element + */ +function createVOTSelectLabel(text) { + const label = document.createElement("span"); + label.classList.add("vot-select-label"); + label.textContent = text; + return label; } -async function fetchSubtitles(subtitlesObject) { - const timeoutPromise = new Promise((resolve) => - setTimeout( - () => - resolve({ - containsTokens: false, - subtitles: [], - }), - 5000, - ), - ); +/** + * Create VOTSelect + * + * @param {string} selectTitle - select title + * @param {string} dialogTitle - dialog title + * @param {{label: string, value: string, selected: boolean}[]} items - items to select + * @param {{onSelectCb: function, labelElement: string}} options - items to select + * @return {{ + * container: HTMLElement, + * title: HTMLSpanElement, + * arrowIcon: HTMLElement, + * labelElement: HTMLElement, + * setTitle: (newTitle: string) => void, + * setSelected: (val: string) => void, + * updateItems: (newItems: {label: string, value: string, selected: boolean}[]) => void, + * }} VOTSelect elements + */ +function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { + const { onSelectCb = function () {}, labelElement = "" } = options; + let selectedItems = []; - const fetchPromise = (async () => { - try { - const response = await GM_fetch(subtitlesObject.url); - return await response.json(); - } catch (error) { - console.error("[VOT] Failed to fetch subtitles. Reason:", error); - return { - containsTokens: false, - subtitles: [], - }; - } - })(); + const container = document.createElement("vot-block"); + container.classList.add("vot-select"); - let subtitles = await Promise.race([timeoutPromise, fetchPromise]); + if (labelElement) { + container.append(labelElement); + } - if (subtitlesObject.source === "youtube") { - subtitles = formatYoutubeSubtitles(subtitles); + const outer = document.createElement("vot-block"); + outer.classList.add("vot-select-outer"); + + const title = document.createElement("span"); + title.classList.add("vot-select-title"); + title.textContent = selectTitle; + + if (selectTitle === undefined) { + title.textContent = items.find((i) => i.selected === true)?.label; } - subtitles.subtitles = getSubtitlesTokens(subtitles, subtitlesObject.source); - console.log("[VOT] subtitles:", subtitles); - return subtitles; -} + const arrowIcon = document.createElement("vot-block"); + arrowIcon.classList.add("vot-select-arrow-icon"); + Q(arrowIconRaw, arrowIcon); -async function subtitles_getSubtitles(site, videoId, requestLang) { - const ytSubtitles = - site.host === "youtube" ? youtubeUtils.getSubtitles() : []; - let resolved = false; - const yaSubtitles = await Promise.race([ - new Promise((resolve) => { - setTimeout(() => { - if (!resolved) { - console.error("[VOT] Failed get yandex subtitles. Reason: timeout"); - resolve([]); - } - }, 5000); - }), - new Promise((resolve) => { - rvs( - `${site.url}${videoId}`, - requestLang, - (success, response) => { - debug/* default */.A.log("[exec callback] Requesting video subtitles", videoId); + outer.append(title, arrowIcon); + outer.onclick = () => { + const votSelectDialog = createDialog(dialogTitle); + votSelectDialog.container.classList.add("vot-dialog-temp"); + votSelectDialog.container.hidden = false; + document.documentElement.appendChild(votSelectDialog.container); - if (!success) { - console.error("[VOT] Failed get yandex subtitles"); - resolved = true; - resolve([]); - } + const contentList = document.createElement("vot-block"); + contentList.classList.add("vot-select-content-list"); + + for (const item of items) { + const contentItem = document.createElement("vot-block"); + contentItem.classList.add("vot-select-content-item"); + contentItem.textContent = item.label; + contentItem.dataset.votSelected = item.selected; + contentItem.dataset.votValue = item.value; + if (item.disabled) { + contentItem.inert = true; + } - const subtitlesResponse = - yandexProtobuf.decodeSubtitlesResponse(response); - console.log("[VOT] Subtitles response: ", subtitlesResponse); + contentItem.onclick = async (e) => { + if (e.target.inert) return; - let subtitles = subtitlesResponse.subtitles ?? []; - subtitles = subtitles.reduce((result, yaSubtitlesObject) => { - if ( - yaSubtitlesObject.language && - !result.find((e) => { - if ( - e.source === "yandex" && - e.language === yaSubtitlesObject.language && - !e.translatedFromLanguage - ) { - return e; - } - }) - ) { - result.push({ - source: "yandex", - language: yaSubtitlesObject.language, - url: yaSubtitlesObject.url, - }); - } - if (yaSubtitlesObject.translatedLanguage) { - result.push({ - source: "yandex", - language: yaSubtitlesObject.translatedLanguage, - translatedFromLanguage: yaSubtitlesObject.language, - url: yaSubtitlesObject.translatedUrl, - }); - } - return result; - }, []); - resolved = true; - resolve(subtitles); - }, - ); - }), - ]); - const subtitles = [...yaSubtitles, ...ytSubtitles].sort((a, b) => { - if (a.source !== b.source) { - // sort by source - return a.source === "yandex" ? -1 : 1; - } - if ( - a.language !== b.language && - (a.language === lang || b.language === lang) - ) { - // sort by user language - return a.language === lang ? -1 : 1; - } - if (a.source === "yandex") { - // sort by translation - if (a.translatedFromLanguage !== b.translatedFromLanguage) { - // sort by translatedFromLanguage - if (!a.translatedFromLanguage || !b.translatedFromLanguage) { - // sort by isTranslated - if (a.language === b.language) { - return a.translatedFromLanguage ? 1 : -1; - } - return !a.translatedFromLanguage ? 1 : -1; + // removing the selected value for updating + const contentItems = contentList.childNodes; + for (let ci of contentItems) { + ci.dataset.votSelected = false; } - return a.translatedFromLanguage === requestLang ? -1 : 1; - } - if (!a.translatedFromLanguage) { - // sort non translated by language - return a.language === requestLang ? -1 : 1; + // fixed selection after closing the modal and opening again + for (let i of items) { + i.selected = i.value === item.value; + } + + contentItem.dataset.votSelected = true; + title.textContent = item.label; + + // !!! use e.target.dataset.votValue instead of e.target.value !!! + await onSelectCb(e); + }; + contentList.appendChild(contentItem); + } + + // search logic + const votSearchLangTextfield = createTextfield( + localizationProvider.get("searchField"), + ); + + votSearchLangTextfield.input.oninput = (e) => { + const searchText = e.target.value.toLowerCase(); + // check if there are lovercase characters in the string. used for smarter search + for (let i = 0; i < selectedItems.length; i++) { + const ci = selectedItems[i]; + ci.hidden = !ci.textContent.toLowerCase().includes(searchText); } + }; + + votSelectDialog.bodyContainer.append( + votSearchLangTextfield.container, + contentList, + ); + selectedItems = contentList.childNodes; + + // remove the modal so that they do not accumulate + votSelectDialog.backdrop.onclick = votSelectDialog.closeButton.onclick = + () => { + votSelectDialog.container.remove(); + selectedItems = []; + }; + }; + + container.append(outer); + + const setTitle = (newTitle) => { + title.textContent = newTitle; + }; + + const setSelected = (val) => { + const selectedItemsArray = Array.from(selectedItems).filter( + (ci) => !ci.inert, + ); + for (let i = 0; i < selectedItemsArray.length; i++) { + const ci = selectedItemsArray[i]; + ci.dataset.votSelected = ci.dataset.votValue === val; } - if (a.source === "youtube" && a.isAutoGenerated !== b.isAutoGenerated) { - // sort by isAutoGenerated - return a.isAutoGenerated ? 1 : -1; + + for (let i = 0; i < items.length; i++) { + const currentItem = items[i]; + currentItem.selected = String(currentItem.value) === val; } - return 0; + }; + + const updateItems = (newItems) => { + items = newItems; + }; + + return { + container, + title, + arrowIcon, + labelElement, + setTitle, + setSelected, + updateItems, + }; +} + +function createVOTLanguageSelect(options) { + const { + fromTitle = undefinedPhrase, + fromDialogTitle = undefinedPhrase, + fromItems = [], + fromOnSelectCB = null, + toTitle = undefinedPhrase, + toDialogTitle = undefinedPhrase, + toItems = [], + toOnSelectCB = null, + } = options; + + const container = document.createElement("vot-block"); + container.classList.add("vot-lang-select"); + + const fromSelect = createVOTSelect(fromTitle, fromDialogTitle, fromItems, { + onSelectCb: fromOnSelectCB, }); - console.log("[VOT] subtitles list", subtitles); - return subtitles; + + const icon = document.createElement("vot-block"); + icon.classList.add("vot-lang-select-icon"); + Q( + Oe` + + `, + icon, + ); + + const toSelect = createVOTSelect(toTitle, toDialogTitle, toItems, { + onSelectCb: toOnSelectCB, + }); + + container.append(fromSelect.container, icon, toSelect.container); + + return { + container, + fromSelect, + icon, + toSelect, + }; } -class SubtitlesWidget { - dragging = false; - subtitlesContainerRect = null; - containerRect = null; - offsetX = null; - offsetY = null; - - lastContent = null; - highlightWords = false; - subtitles = null; - maxLength = 300; - maxLengthRegexp = /.{1,300}(?:\s|$)/g; +function createDetails(titleHtml) { + const container = document.createElement("vot-block"); + container.classList.add("vot-details"); - constructor(video, container, site) { - this.site = site; - this.video = video; - if (this.site.host === "youtube" && this.site.additionalData !== "mobile") { - this.container = container.parentElement; - } else { - this.container = container; - } + const header = document.createElement("vot-block"); + header.append(titleHtml); - this.votSubtitlesContainer = document.createElement("vot-block"); - this.votSubtitlesContainer.classList.add("vot-subtitles-widget"); - this.container.appendChild(this.votSubtitlesContainer); + const arrowIcon = document.createElement("vot-block"); + arrowIcon.classList.add("vot-details-arrow-icon"); + Q(arrowIconRaw, arrowIcon); - this.onMouseDownBound = this.onMouseDown.bind(this); - this.onMouseUpBound = this.onMouseUp.bind(this); - this.onMouseMoveBound = this.onMouseMove.bind(this); - this.onTimeUpdateBound = this.onTimeUpdate.bind(this); + container.append(header, arrowIcon); - document.addEventListener("mousedown", this.onMouseDownBound); - document.addEventListener("mouseup", this.onMouseUpBound); - document.addEventListener("mousemove", this.onMouseMoveBound); + return { + container, + header, + arrowIcon, + }; +} - this.video?.addEventListener("timeupdate", this.onTimeUpdateBound); - } +/* harmony default export */ const ui = ({ + createHeader, + createInformation, + createButton, + createTextButton, + createOutlinedButton, + createIconButton, + createCheckbox, + createSlider, + createTextfield, + createDialog, + createVOTButton, + createVOTMenu, + createVOTSelectLabel, + createVOTSelect, + createVOTLanguageSelect, + updateSlider, + createDetails, +}); - release() { - this.video?.removeEventListener("timeupdate", this.onTimeUpdateBound); +;// CONCATENATED MODULE: ./src/utils/volume.js +// element - audio / video element +function syncVolume(element, sliderVolume, otherSliderVolume, tempVolume) { + let finalValue = sliderVolume; + if (sliderVolume > tempVolume) { + // sliderVolume = 100 + // tempVolume = 69 + // volume = 15 + // 100 - 69 = 31 + // 15 + 31 = 46 - final video volume + finalValue = otherSliderVolume + (sliderVolume - tempVolume); + finalValue = finalValue > 100 ? 100 : Math.max(finalValue, 0); - document.removeEventListener("mousedown", this.onMouseDownBound); - document.removeEventListener("mouseup", this.onMouseUpBound); - document.removeEventListener("mousemove", this.onMouseMoveBound); + element.volume = finalValue / 100; + } else if (sliderVolume < tempVolume) { + // sliderVolume = 69 + // tempVolume = 100 + // volume = 15 + // 100 - 69 = 31 + // 15 - 31 = 0 - final video volume + finalValue = otherSliderVolume - (tempVolume - sliderVolume); + finalValue = finalValue > 100 ? 100 : Math.max(finalValue, 0); - this.votSubtitlesContainer.remove(); + element.volume = finalValue / 100; } - onMouseDown(e) { - if (this.votSubtitlesContainer.contains(e.target)) { - this.subtitlesContainerRect = - this.votSubtitlesContainer.getBoundingClientRect(); - this.containerRect = this.container.getBoundingClientRect(); - this.offsetX = e.clientX - this.subtitlesContainerRect.x; - this.offsetY = e.clientY - this.subtitlesContainerRect.y; - this.dragging = true; - } - } + return finalValue; +} - onMouseUp() { - this.dragging = false; - } - onMouseMove(e) { - if (this.dragging) { - e.preventDefault(); - const x = e.clientX - this.offsetX; - const y = e.clientY - this.offsetY; - const top = y >= this.containerRect.top; - const bottom = - y + this.subtitlesContainerRect.height <= this.containerRect.bottom; - const left = x >= this.containerRect.left; - const right = - x + this.subtitlesContainerRect.width <= this.containerRect.right; - - if (top && bottom) { - this.votSubtitlesContainer.style.top = `${y - this.containerRect.y}px`; - } else if (!top) { - this.votSubtitlesContainer.style.top = `${0}px`; - } else { - this.votSubtitlesContainer.style.top = `${ - this.containerRect.height - this.subtitlesContainerRect.height - }px`; - } - if (left && right) { - this.votSubtitlesContainer.style.left = `${x - this.containerRect.x}px`; - } else if (!left) { - this.votSubtitlesContainer.style.left = `${0}px`; - } else { - this.votSubtitlesContainer.style.left = `${ - this.containerRect.width - this.subtitlesContainerRect.width - }px`; - } - } - } +;// CONCATENATED MODULE: ./src/utils/translateApis.js - onTimeUpdate() { - this.update(); - } - setContent(subtitles) { - if (subtitles && this.video) { - this.subtitles = subtitles; - this.update(); - } else { - this.subtitles = null; - this.votSubtitlesContainer.innerHTML = ""; - } - } - setMaxLength(len) { - if (typeof len === "number" && len) { - this.maxLength = len; - this.maxLengthRegexp = new RegExp(`.{1,${len}}(?:\\s|$)`, "g"); - this.update(); - } - } - setHighlightWords(value) { - if (this.highlightWords !== !!value) { - this.highlightWords = !!value; - this.update(); - } - } +const YandexTranslateAPI = { + async translate(text, lang) { + // Limit: 10k symbols + // + // Lang examples: + // en-ru, uk-ru, ru-en... + // ru, en (instead of auto-ru, auto-en) - update() { - if (!this.video) return; + try { + const response = await GM_fetch( + `${translateUrls.yandex}?${new URLSearchParams({ + text, + lang, + })}`, + { timeout: 3000 }, + ); - let content = ""; - let highlightWords = this.highlightWords && this.subtitles?.containsTokens; - const time = this.video.currentTime * 1000; - const line = this.subtitles?.subtitles?.findLast((e) => { - return e.startMs < time && time < e.startMs + e.durationMs; - }); - if (line) { - let { tokens } = line; - if (tokens.at(-1).alignRange.end > this.maxLength) { - let chunks = []; - let chunkStartIndex = 0; - let chunkEndIndex = 0; - let length = 0; - for (let i = 0; i < tokens.length + 1; i++) { - length += tokens[i]?.text?.length ?? 0; - if (!tokens[i] || length > this.maxLength) { - let t = tokens.slice(chunkStartIndex, chunkEndIndex + 1); - if (t.at(0) && t.at(0).text === " ") t = t.slice(1); - if (t.at(-1) && t.at(-1).text === " ") t = t.slice(0, t.length - 1); - chunks.push({ - startMs: tokens[chunkStartIndex].startMs, - durationMs: - tokens[chunkEndIndex].startMs + - tokens[chunkEndIndex].durationMs - - tokens[chunkStartIndex].startMs, - tokens: t, - }); - chunkStartIndex = i; - length = 0; - } - chunkEndIndex = i; - } - for (let index = 0; index < chunks.length; index++) { - const chunk = chunks[index]; - if (chunk.startMs < time && time < chunk.startMs + chunk.durationMs) { - tokens = chunk.tokens; - break; - } - } - } - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - const passedMs = token.startMs + token.durationMs / 2; - content += ` passedMs || - (time > token.startMs - 100 && passedMs - time < 275)) - ? 'class="passed"' - : "" - }>${token.text}`; + if (response instanceof Error) { + throw response; } - } - if (content !== this.lastContent) { - this.lastContent = content; - this.votSubtitlesContainer.innerHTML = content - ? `${content.replace( - "\\n", - "
", - )}
` - : ""; - } - } -} -;// CONCATENATED MODULE: ./src/utils/coursehunterUtils.js + const content = await response.json(); + if (content.code !== 200) { + throw content.message; + } -async function getCourseData(courseId) { - const response = await fetch( - `https://coursehunter.net/api/v1/course/${courseId}/lessons`, - ); - return await response.json(); -} + return content.text[0]; + } catch (error) { + console.error("Error translating text:", error); + return text; + } + }, + + async detect(text) { + // Limit: 10k symbols + try { + const response = await GM_fetch( + `${detectUrls.yandex}?${new URLSearchParams({ + text, + })}`, + { timeout: 3000 }, + ); -async function coursehunterUtils_getVideoData() { - const courseId = - window.course_id ?? - document.querySelector('input[name="course_id"]')?.value; + if (response instanceof Error) { + throw response; + } - const courseData = window.lessons ?? (await getCourseData(courseId)); + const content = await response.json(); + if (content.code !== 200) { + throw content.message; + } - const lessonId = parseInt( - document.querySelector(".lessons-item_active")?.dataset?.index ?? 1, - ); + return content.lang ?? "en"; + } catch (error) { + console.error("Error getting lang from text:", error); + return "en"; + } + }, +}; - const lessonData = courseData?.[lessonId - 1]; +const RustServerAPI = { + async detect(text) { + try { + const response = await GM_fetch( + detectUrls.rustServer, + { + method: "POST", + body: text, + }, + { timeout: 3000 }, + ); - const { file: videoUrl, duration } = lessonData; + if (response instanceof Error) { + throw response; + } - debug/* default */.A.log("coursehunter course data:", courseData); - return { - url: videoUrl, - duration, - }; -} + return await response.text(); + } catch (error) { + console.error("Error getting lang from text:", error); + return "en"; + } + }, +}; -/* harmony default export */ const coursehunterUtils = ({ - getVideoData: coursehunterUtils_getVideoData, -}); +const DeeplServerAPI = { + async translate(text, fromLang = "auto", toLang = "ru") { + try { + const response = await GM_fetch( + translateUrls.deepl, + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + text, + source_lang: fromLang, + target_lang: toLang, + }), + }, + { timeout: 3000 }, + ); -;// CONCATENATED MODULE: ./src/utils/courseraUtils.js + if (response instanceof Error) { + throw response; + } + const content = await response.json(); + if (content.code !== 200) { + throw content.message; + } + return content.data; + } catch (error) { + console.error("Error translating text:", error); + return text; + } + }, +}; -async function courseraUtils_getCourseData(courseId) { - const response = await fetch( - `https://www.coursera.org/api/onDemandCourses.v1/${courseId}`, +async function translate(text, fromLang = "", toLang = "ru") { + const service = await votStorage.get( + "translationService", + defaultTranslationService, ); - const resJSON = await response.json(); - return resJSON?.elements?.[0]; + switch (service) { + case "yandex": { + const langPair = fromLang && toLang ? `${fromLang}-${toLang}` : toLang; + return await YandexTranslateAPI.translate(text, langPair); + } + case "deepl": { + return await DeeplServerAPI.translate(text, fromLang, toLang); + } + default: + return text; + } } -function getSubtitlesFileURL(captions, detectedLanguage, responseLang) { - let subtitle = captions?.find( - (caption) => langTo6391(caption.srclang) === detectedLanguage, - ); - - if (!subtitle) { - subtitle = - captions?.find( - (caption) => langTo6391(caption.srclang) === responseLang, - ) || captions?.[0]; +async function detect(text) { + const service = await votStorage.get("detectService", defaultDetectService); + switch (service) { + case "yandex": + return await YandexTranslateAPI.detect(text); + case "rust-server": + return await RustServerAPI.detect(text); + default: + return "en"; } - - return subtitle?.src; } -function getVideoFileURL(sources) { - // const source = sources?.find((src) => src.type === "video/webm" || src.type === "video/mp4", - const source = sources?.find((src) => src.type === "video/mp4"); - - return source?.src; -} +const translateServices = Object.keys(translateUrls); +const detectServices = Object.keys(detectUrls).map((k) => + k === "rustServer" ? "rust-server" : k, +); -function courseraUtils_getPlayerData() { - return courseraUtils_getPlayer()?.player; -} -function courseraUtils_getPlayer() { - return document.querySelector(".vjs-v6"); -} -// Get the video data from the player -async function courseraUtils_getVideoData(responseLang = "en") { - let translationHelp = null; - const data = courseraUtils_getPlayerData(); +;// CONCATENATED MODULE: ./src/utils/youtubeUtils.js - const { duration } = data?.cache_ || {}; - const { courseId, tracks, sources } = data?.options_ || {}; - const videoURL = getVideoFileURL(sources); - const courseData = await courseraUtils_getCourseData(courseId); - let detectedLanguage = courseData?.primaryLanguageCodes?.[0]; - detectedLanguage = detectedLanguage ? langTo6391(detectedLanguage) : "en"; - if (!availableLangs.includes(detectedLanguage)) { - detectedLanguage = "en"; - } - const subtitlesURL = getSubtitlesFileURL( - tracks, - detectedLanguage, - responseLang, - ); - console.log(`videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`); - if (subtitlesURL && videoURL) { - translationHelp = [ - { - target: "video_file_url", - targetUrl: videoURL, - }, - { - target: "subtitles_file_url", - targetUrl: `https://www.coursera.org${subtitlesURL}`, - }, - ]; - } else if (videoURL && !subtitlesURL) { - console.warn( - "[VOT] Subtitles files not found. Using the link only to the video file.", - ); - translationHelp = { - url: videoURL, - }; - } else { - console.error( - `Failed to find subtitlesURL or videoURL. videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`, - ); +// Get the language code from the response or the text +async function getLanguage(player, response, title, description) { + if ( + !window.location.hostname.includes("m.youtube.com") && + player?.getAudioTrack + ) { + // ! Experimental ! get lang from selected audio track if availabled + const audioTracks = player.getAudioTrack(); + const trackInfo = audioTracks?.getLanguageInfo(); // get selected track info (id === "und" if tracks are not available) + if (trackInfo?.id !== "und") { + return langTo6391(trackInfo.id.split(".")[0]); + } } - const videoData = { - duration, - detectedLanguage, - translationHelp, - }; - - debug/* default */.A.log("coursera video data:", videoData); - console.log("[VOT] Detected language: ", videoData.detectedLanguage); - return videoData; -} - -/* harmony default export */ const courseraUtils = ({ - getPlayer: courseraUtils_getPlayer, - getPlayerData: courseraUtils_getPlayerData, - getVideoData: courseraUtils_getVideoData, -}); - -;// CONCATENATED MODULE: ./src/utils/udemyUtils.js - - + // TODO: If the audio tracks will work fine, transfer the receipt of captions to the audioTracks variable + // Check if there is an automatic caption track in the response + const captionTracks = + response?.captions?.playerCaptionsTracklistRenderer?.captionTracks; + if (captionTracks?.length) { + const autoCaption = captionTracks.find((caption) => caption.kind === "asr"); + if (autoCaption && autoCaption.languageCode) { + return langTo6391(autoCaption.languageCode); + } + } + // If there is no caption track, use detect to get the language code from the description + const text = cleanText(title, description); -const udemyAPIURL = "https://www.udemy.com/api-2.0"; -const accessTokenLife = 2_592_000_000; // 30 days + utils_debug.log(`Detecting language text: ${text}`); -async function getCourseLang(courseId) { - const response = await fetch( - `${udemyAPIURL}/courses/${courseId}/?` + - new URLSearchParams({ - "fields[course]": "locale", - use_remote_version: "true", - caching_intent: "true", - }), - ); - return await response.json(); + return detect(text); } -function checkUdemyTokenExpire(expires) { - return expires + accessTokenLife > new Date().getTime(); +function isMobile() { + return /^m\.youtube\.com$/.test(window.location.hostname); } -async function getLectureData(udemyData, courseId, lectureId) { - // reference: https://greasyfork.org/ru/scripts/422576-udemy-subtitle-downloader-v3/code - if (!checkUdemyTokenExpire(udemyData.expires) || !udemyData.accessToken) { - console.error(localizationProvider.get("udemyAccessTokenExpired")); - return undefined; +function getPlayer() { + if (window.location.pathname.startsWith("/shorts/")) { + return isMobile() + ? document.querySelector("#movie_player") + : document.querySelector("#shorts-player"); } - const bearerToken = `Bearer ${udemyData.accessToken}`; - const response = await fetch( - `${udemyAPIURL}/users/me/subscribed-courses/${courseId}/lectures/${lectureId}/?` + - new URLSearchParams({ - "fields[lecture]": "asset", - "fields[asset]": "length,media_sources,captions", - }), - { - headers: { - "x-udemy-authorization": bearerToken, - authorization: bearerToken, - }, - }, - ); - return await response.json(); + return document.querySelector("#movie_player"); } -function udemyUtils_getSubtitlesFileURL(captions, detectedLanguage, responseLang) { - let subtitle = captions?.find( - (caption) => langTo6391(caption.locale_id) === detectedLanguage, - ); - - if (!subtitle) { - subtitle = - captions?.find( - (caption) => langTo6391(caption.locale_id) === responseLang, - ) || captions?.[0]; - } - - return subtitle?.url; +function getPlayerResponse() { + const player = getPlayer(); + if (player?.getPlayerResponse) + return player?.getPlayerResponse?.call() ?? null; + return player?.data?.playerResponse ?? null; } -function getVideoFileURLFromAPI(sources) { - const source = sources?.find( - (src) => src.type === "video/webm" || src.type === "video/mp4", - ); - - return source?.src; +function getPlayerData() { + const player = getPlayer(); + if (player?.getVideoData) return player?.getVideoData?.call() ?? null; + return player?.data?.playerResponse?.videoDetails ?? null; } -function udemyUtils_getPlayerData() { - return udemyUtils_getPlayer()?.player; +function getVideoVolume() { + const player = getPlayer(); + if (player?.getVolume) { + return player.getVolume.call() / 100; + } + + return 1; } -function getModuleData() { - const moduleArgs = document.querySelector( - ".ud-app-loader[data-module-id='course-taking']", - )?.dataset?.moduleArgs; - if (!moduleArgs) { - console.error(localizationProvider.get("udemyModuleArgsNotFound")); - return {}; +function setVideoVolume(volume) { + const player = getPlayer(); + if (player?.setVolume) { + player.setVolume(Math.round(volume * 100)); + return true; } - return JSON.parse(moduleArgs); } -function getLectureId() { - return /learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1]; -} +function isMuted() { + const player = getPlayer(); + if (player?.isMuted) { + return player.isMuted.call(); + } -function udemyUtils_getPlayer() { - return document.querySelector(".vjs-v7"); + return false; } -function getVideoURLFromPlayer() { - const src = udemyUtils_getPlayer()?.querySelector("video")?.src; - return src?.startsWith("blob:") ? false : src; +function videoSeek(video, time) { + // * TIME IN MS + utils_debug.log("videoSeek", time); + const preTime = + getPlayer()?.getProgressState()?.seekableEnd || video.currentTime; + const finalTime = preTime - time; // we always throw it to the end of the stream - time + video.currentTime = finalTime; } -// Get the video data from the player -async function udemyUtils_getVideoData(udemyData, responseLang = "en") { - let translationHelp = null; - const data = udemyUtils_getPlayerData(); - debug/* default */.A.log("udemyData", udemyData); - - const moduleData = getModuleData(); - debug/* default */.A.log("moduleData: ", moduleData); - - const courseId = moduleData.courseId; - const lectureId = getLectureId(); - debug/* default */.A.log(`CourseId: ${courseId}, lectureId: ${lectureId}`); - - const courseLang = await getCourseLang(courseId); - debug/* default */.A.log("courseLang Data:", courseLang); - const lectureData = await getLectureData(udemyData, courseId, lectureId); - console.log("lecture Data:", lectureData); - - let detectedLanguage = courseLang?.locale?.locale; - detectedLanguage = detectedLanguage ? langTo6391(detectedLanguage) : "en"; - - if (!availableLangs.includes(detectedLanguage)) { - detectedLanguage = "en"; - } +function isMusic() { + // Нужно доработать логику + const channelName = getPlayerData().author, + titleStr = getPlayerData().title.toUpperCase(), + titleWordsList = titleStr.match(/\w+/g), + playerData = document.body.querySelector("ytd-watch-flexy")?.playerData; - const duration = lectureData?.asset?.length || data?.cache_?.duration; - const videoURL = - getVideoFileURLFromAPI(lectureData?.asset?.media_sources) || - getVideoURLFromPlayer(); - const subtitlesURL = udemyUtils_getSubtitlesFileURL( - lectureData?.asset?.captions, - detectedLanguage, - responseLang, + return ( + [ + titleStr, + document.URL, + channelName, + playerData?.microformat?.playerMicroformatRenderer.category, + playerData?.title, + ].some((i) => i?.toUpperCase().includes("MUSIC")) || + document.body.querySelector( + "#upload-info #channel-name .badge-style-type-verified-artist", + ) || + (channelName && + /(VEVO|Topic|Records|RECORDS|Recordings|AMV)$/.test(channelName)) || + (channelName && + /(MUSIC|ROCK|SOUNDS|SONGS)/.test(channelName.toUpperCase())) || + (titleWordsList?.length && + [ + "🎵", + "♫", + "SONG", + "SONGS", + "SOUNDTRACK", + "LYRIC", + "LYRICS", + "AMBIENT", + "MIX", + "VEVO", + "CLIP", + "KARAOKE", + "OPENING", + "COVER", + "COVERED", + "VOCAL", + "INSTRUMENTAL", + "ORCHESTRAL", + "DUBSTEP", + "DJ", + "DNB", + "BASS", + "BEAT", + "ALBUM", + "PLAYLIST", + "DUBSTEP", + "CHILL", + "RELAX", + "CLASSIC", + "CINEMATIC", + ].some((i) => titleWordsList.includes(i))) || + [ + "OFFICIAL VIDEO", + "OFFICIAL AUDIO", + "FEAT.", + "FT.", + "LIVE RADIO", + "DANCE VER", + "HIP HOP", + "ROCK N ROLL", + "HOUR VER", + "HOURS VER", + "INTRO THEME", + ].some((i) => titleStr.includes(i)) || + (titleWordsList?.length && + [ + "OP", + "ED", + "MV", + "OST", + "NCS", + "BGM", + "EDM", + "GMV", + "AMV", + "MMD", + "MAD", + ].some((i) => titleWordsList.includes(i))) ); +} - console.log(`videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`); - - if (subtitlesURL && videoURL) { - translationHelp = [ - { - target: "video_file_url", - targetUrl: videoURL, - }, - { - target: "subtitles_file_url", - targetUrl: subtitlesURL, - }, - ]; - } else if (videoURL && !subtitlesURL) { - console.warn( - "[VOT] Subtitles files not found. Using the link only to the video file.", - ); - translationHelp = { - url: videoURL, - }; - } else { - console.error( - `Failed to find subtitlesURL or videoURL. videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`, - ); - } +function getSubtitles() { + const response = getPlayerResponse(); + let captionTracks = + response?.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? []; + captionTracks = captionTracks.reduce((result, captionTrack) => { + if ("languageCode" in captionTrack) { + const language = captionTrack?.languageCode + ? langTo6391(captionTrack?.languageCode) + : undefined; + const url = captionTrack?.url || captionTrack?.baseUrl; + language && + url && + result.push({ + source: "youtube", + language, + isAutoGenerated: captionTrack?.kind === "asr", + url: `${ + url.startsWith("http") ? url : `${window.location.origin}/${url}` + }&fmt=json3`, + }); + } + return result; + }, []); + utils_debug.log("youtube subtitles:", captionTracks); + return captionTracks; +} +// Get the video data from the player +async function youtubeUtils_getVideoData() { + const player = getPlayer(); + const response = getPlayerResponse(); // null in /embed + const data = getPlayerData(); + const { title } = data ?? {}; + const { shortDescription: description, isLive } = + response?.videoDetails ?? {}; + let detectedLanguage = title + ? await getLanguage(player, response, title, description) + : "en"; + detectedLanguage = availableLangs.includes(detectedLanguage) + ? detectedLanguage + : "en"; const videoData = { - duration, + isLive: !!isLive, + title, + description, detectedLanguage, - translationHelp, }; - - debug/* default */.A.log("udemy video data:", videoData); + utils_debug.log("youtube video data:", videoData); console.log("[VOT] Detected language: ", videoData.detectedLanguage); return videoData; } -/* harmony default export */ const udemyUtils = ({ - getPlayer: udemyUtils_getPlayer, - getPlayerData: udemyUtils_getPlayerData, - getVideoData: udemyUtils_getVideoData, - getModuleData, - getCourseLang, - getLectureData, +/* harmony default export */ const youtubeUtils = ({ + isMobile, + getPlayer, + getPlayerResponse, + getPlayerData, + getVideoVolume, + getSubtitles, + getVideoData: youtubeUtils_getVideoData, + setVideoVolume, + videoSeek, + isMuted, + isMusic, }); -;// CONCATENATED MODULE: ./src/utils/bannedvideoUtils.js +;// CONCATENATED MODULE: ./src/subtitles.js -async function getVideoInfo(videoId) { - return await fetch(`https://api.banned.video/graphql`, { - method: "POST", - body: JSON.stringify({ - operationName: "GetVideo", - query: `query GetVideo($id: String!) { - getVideo(id: $id) { - ...DisplayVideoFields - videoUrl: directUrl - live - } - } - fragment DisplayVideoFields on Video { - title - description: summary - duration: videoDuration - }`, - variables: { - id: videoId, - }, - }), - headers: { - "User-Agent": "bannedVideoFrontEnd", - "apollographql-client-name": "banned-web", - "apollographql-client-version": "1.3", - "content-type": "application/json", - }, - }) - .then((res) => res.json()) - .catch((err) => { - console.error(err); - return { - data: { - getVideo: {}, + + +function formatYandexSubtitlesTokens(line) { + const lineEndMs = line.startMs + line.durationMs; + return line.tokens.reduce((result, token, index) => { + const nextToken = line.tokens[index + 1]; + let lastToken; + if (result.length > 0) { + lastToken = result[result.length - 1]; + } + const alignRangeEnd = lastToken?.alignRange?.end ?? 0; + const newAlignRangeEnd = alignRangeEnd + token.text.length; + token.alignRange = { + start: alignRangeEnd, + end: newAlignRangeEnd, + }; + result.push(token); + if (nextToken) { + const endMs = token.startMs + token.durationMs; + const durationMs = nextToken.startMs + ? nextToken.startMs - endMs + : lineEndMs - endMs; + result.push({ + text: " ", + startMs: endMs, + durationMs, + alignRange: { + start: newAlignRangeEnd, + end: newAlignRangeEnd + 1, }, - }; + }); + } + return result; + }, []); +} + +function createSubtitlesTokens(line, previousLineLastToken) { + const tokens = line.text.split(/([\n \t])/).reduce((result, tokenText) => { + if (tokenText.length) { + const lastToken = result[result.length - 1] ?? previousLineLastToken; + const alignRangeStart = lastToken?.alignRange?.end ?? 0; + const alignRangeEnd = alignRangeStart + tokenText.length; + result.push({ + text: tokenText, + alignRange: { + start: alignRangeStart, + end: alignRangeEnd, + }, + }); + } + return result; + }, []); + const tokenDurationMs = Math.floor(line.durationMs / tokens.length); + const lineEndMs = line.startMs + line.durationMs; + return tokens.map((token, index) => { + const isLastToken = index === tokens.length - 1; + const startMs = line.startMs + tokenDurationMs * index; + const durationMs = isLastToken ? lineEndMs - startMs : tokenDurationMs; + return { + ...token, + startMs, + durationMs, + }; + }); +} + +function getSubtitlesTokens(subtitles, source) { + const result = []; + let lastToken; + for (let i = 0; i < subtitles.subtitles.length; i++) { + const line = subtitles.subtitles[i]; + let tokens; + if (line?.tokens?.length) { + if (source === "yandex") { + tokens = formatYandexSubtitlesTokens(line); + } else { + console.warn("[VOT] Unsupported subtitles tokens type: ", source); + subtitles.containsTokens = false; + return null; + } + } else { + tokens = createSubtitlesTokens(line, lastToken); + } + lastToken = tokens[tokens.length - 1]; + result.push({ + ...line, + tokens, }); + } + subtitles.containsTokens = true; + return result; } -async function bannedvideoUtils_getVideoData(videoId) { - const videoData = await getVideoInfo(videoId); - - debug/* default */.A.log("banned.video video data:", videoData); - - const { videoUrl, duration, live, description, title } = - videoData.data.getVideo; - - // TODO: Add detect language from title + description - - return { - url: videoUrl, - duration, - live, - title, - description, +function formatYoutubeSubtitles(subtitles) { + const result = { + containsTokens: false, + subtitles: [], }; -} - -/* harmony default export */ const bannedvideoUtils = ({ - getVideoData: bannedvideoUtils_getVideoData, -}); - -;// CONCATENATED MODULE: ./src/utils/crypto.js -async function getHmacSha1(hmacKey, salt) { - try { - const utf8Encoder = new TextEncoder("utf-8"); - salt = utf8Encoder.encode(salt); - const key = await window.crypto.subtle.importKey( - "raw", - utf8Encoder.encode(hmacKey), - { name: "HMAC", hash: { name: "SHA-1" } }, - false, - ["sign", "verify"], - ); - const signature = await window.crypto.subtle.sign("HMAC", key, salt); - return btoa(String.fromCharCode(...new Uint8Array(signature))); - } catch (err) { - console.error(err); - return false; + if ( + typeof subtitles !== "object" || + !("events" in subtitles) || + !Array.isArray(subtitles.events) + ) { + console.error("[VOT] Failed to format youtube subtitles", subtitles); + return result; + } + for (let i = 0; i < subtitles.events.length; i++) { + if (!subtitles.events[i].segs) continue; + const text = subtitles.events[i].segs + .map((e) => e.utf8.replace(/^( +| +)$/g, "")) + .join(""); + let durationMs = subtitles.events[i].dDurationMs; + if ( + subtitles.events[i + 1] && + subtitles.events[i].tStartMs + subtitles.events[i].dDurationMs > + subtitles.events[i + 1].tStartMs + ) { + durationMs = + subtitles.events[i + 1].tStartMs - subtitles.events[i].tStartMs; + } + if (text !== "\n") { + result.subtitles.push({ + text, + startMs: subtitles.events[i].tStartMs, + durationMs, + }); + } } + return result; } -;// CONCATENATED MODULE: ./src/utils/weverseUtils.js - - - -const API_ORIGIN = "https://global.apis.naver.com/weverse/wevweb"; // find as REACT_APP_API_GW_ORIGIN in main..js -const API_APP_ID = "be4d79eb8fc7bd008ee82c8ec4ff6fd4"; // find as REACT_APP_API_APP_ID in main..js -const API_HMAC_KEY = "1b9cb6378d959b45714bec49971ade22e6e24e42"; // find as c.active near `createHmac('sha1'...` in main..js - -async function createHash(pathname) { - // pathname example: /post/v1.0/post-3-142049908/preview?fieldSet=postForPreview... - const timestamp = Date.now(); - - // example salt is /video/v1.1/vod/67134/inKey?gcc=RU&appId=be4d79eb8fc7bd008ee82c8ec4ff6fd4&language=en&os=WEB&platform=WEB&wpf=pc1707527163588 - let salt = pathname.substring(0, Math.min(255, pathname.length)) + timestamp; +async function fetchSubtitles(subtitlesObject) { + const fetchPromise = (async () => { + try { + const response = await GM_fetch(subtitlesObject.url, { timeout: 5000 }); + return await response.json(); + } catch (error) { + console.error("[VOT] Failed to fetch subtitles.", error); + return { + containsTokens: false, + subtitles: [], + }; + } + })(); - const sign = await getHmacSha1(API_HMAC_KEY, salt); + let subtitles = await fetchPromise; - return { - wmsgpad: timestamp, - wmd: sign, - }; -} + if (subtitlesObject.source === "youtube") { + subtitles = formatYoutubeSubtitles(subtitles); + } -function getURLData() { - return { - appId: API_APP_ID, - language: "en", - os: "WEB", - platform: "WEB", - wpf: "pc", - }; + subtitles.subtitles = getSubtitlesTokens(subtitles, subtitlesObject.source); + console.log("[VOT] subtitles:", subtitles); + return subtitles; } -async function getVideoPreview(postId) { - const pathname = - `/post/v1.0/post-${postId}/preview?` + - new URLSearchParams({ - fieldSet: "postForPreview", - ...getURLData(), - }); // ! DON'T EDIT ME +async function subtitles_getSubtitles(client, videoData) { + const { host, url, requestLang, videoId, duration } = videoData; + const ytSubtitles = host === "youtube" ? youtubeUtils.getSubtitles() : []; - const hash = await createHash(pathname); + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error("Timeout")), 5000), + ); try { - const res = await fetch( - API_ORIGIN + pathname + "&" + new URLSearchParams(hash), - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -async function getVideoInKey(videoId) { - const pathname = - `/video/v1.1/vod/${videoId}/inKey?` + - new URLSearchParams({ - gcc: "RU", - ...getURLData(), - }); // ! DON'T EDIT ME - const hash = await createHash(pathname); + const res = await Promise.race([ + client.getSubtitles({ + videoData: { host, url, videoId, duration }, + requestLang, + }), + timeoutPromise, + ]); - try { - const res = await fetch( - API_ORIGIN + pathname + "&" + new URLSearchParams(hash), - { - method: "POST", - }, - ); + console.log("[VOT] Subtitles response: ", res); - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} + if (res.waiting) { + console.error("[VOT] Failed to get yandex subtitles"); + } -async function weverseUtils_getVideoInfo(infraVideoId, inkey, serviceId) { - const timestamp = Date.now(); - try { - const res = await fetch( - `https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${infraVideoId}?` + - new URLSearchParams({ - key: inkey, - sid: serviceId, - nonce: timestamp, - devt: "html5_pc", - prv: "N", - aup: "N", - stpb: "N", - cpl: "en", - env: "prod", - lc: "en", - adi: JSON.stringify([ - { - adSystem: null, - }, - ]), - adu: "/", - }), - ); + // Обработка субтитров + let subtitles = res.subtitles ?? []; + subtitles = subtitles.reduce((result, yaSubtitlesObject) => { + if ( + yaSubtitlesObject.language && + !result.find( + (e) => + e.source === "yandex" && + e.language === yaSubtitlesObject.language && + !e.translatedFromLanguage, + ) + ) { + result.push({ + source: "yandex", + language: yaSubtitlesObject.language, + url: yaSubtitlesObject.url, + }); + } + if (yaSubtitlesObject.translatedLanguage) { + result.push({ + source: "yandex", + language: yaSubtitlesObject.translatedLanguage, + translatedFromLanguage: yaSubtitlesObject.language, + url: yaSubtitlesObject.translatedUrl, + }); + } + return result; + }, []); - return await res.json(); - } catch (err) { - console.error(err); - return false; + return [...subtitles, ...ytSubtitles].sort((a, b) => { + if (a.source !== b.source) return a.source === "yandex" ? -1 : 1; + if ( + a.language !== b.language && + (a.language === lang || b.language === lang) + ) + return a.language === lang ? -1 : 1; + if (a.source === "yandex") { + // sort by translation + if (a.translatedFromLanguage !== b.translatedFromLanguage) { + // sort by translatedFromLanguage + if (!a.translatedFromLanguage || !b.translatedFromLanguage) { + // sort by isTranslated + if (a.language === b.language) + return a.translatedFromLanguage ? 1 : -1; + return !a.translatedFromLanguage ? 1 : -1; + } + return a.translatedFromLanguage === requestLang ? -1 : 1; + } + // sort non translated by language + if (!a.translatedFromLanguage) + return a.language === requestLang ? -1 : 1; + } + // sort by isAutoGenerated + if (a.source === "youtube" && a.isAutoGenerated !== b.isAutoGenerated) + return a.isAutoGenerated ? 1 : -1; + return 0; + }); + } catch (error) { + if (error.message === "Timeout") { + console.error("[VOT] Failed to get yandex subtitles. Reason: timeout"); + } else { + console.error("[VOT] Error in getSubtitles function", error); + } + // на сайтах, где нет сабов всегда красит кнопку + throw error; } } -function extractVideoInfo(videoList) { - return videoList.find( - (video) => video.useP2P === false && video.source.includes(".mp4"), - ); -} - -async function weverseUtils_getVideoData() { - // ! When translating using a regular link (like this https://weverse.io/aespa/live/3-142049908), - // ! we will get an error, so we have to do this - - const postId = /([^/]+)\/(live|media)\/([^/]+)/.exec( - new URL(window.location).pathname, - )?.[3]; - - const videoPreview = await getVideoPreview(postId); - if (!videoPreview) { - return undefined; - } +class SubtitlesWidget { + constructor(video, container, site) { + this.video = video; + this.container = + site.host === "youtube" && site.additionalData !== "mobile" + ? container.parentElement + : container; + this.site = site; - debug/* default */.A.log("weverse video preview data:", videoPreview); + this.subtitlesContainer = this.createSubtitlesContainer(); + this.position = { left: 25, top: 75 }; + this.dragging = { active: false, offset: { x: 0, y: 0 } }; - const { videoId, serviceId, infraVideoId } = videoPreview.extension.video; - if (!(videoId && serviceId && infraVideoId)) { - return false; - } + this.subtitles = null; + this.lastContent = null; + this.highlightWords = false; + this.fontSize = 20; + this.opacity = 0.2; + this.maxLength = 300; + this.maxLengthRegexp = /.{1,300}(?:\s|$)/g; - const inkeyData = await getVideoInKey(videoId); - debug/* default */.A.log("weverse video inKey data:", videoPreview); - if (!inkeyData) { - return false; + this.bindEvents(); + this.updateContainerRect(); + this.applySubtitlePosition(); } - const videoInfo = await weverseUtils_getVideoInfo( - infraVideoId, - inkeyData.inKey, - serviceId, - ); - debug/* default */.A.log("weverse video info:", videoInfo); - - const videoItem = extractVideoInfo(videoInfo.videos.list); - if (!videoItem) { - return false; + createSubtitlesContainer() { + const container = document.createElement("vot-block"); + container.classList.add("vot-subtitles-widget"); + this.container.appendChild(container); + return container; } - return { - url: videoItem.source, - duration: videoItem.duration, - }; -} - -/* harmony default export */ const weverseUtils = ({ - getVideoData: weverseUtils_getVideoData, -}); - -;// CONCATENATED MODULE: ./src/config/sites.js - + bindEvents() { + this.onMouseDownBound = this.onMouseDown.bind(this); + this.onMouseUpBound = this.onMouseUp.bind(this); + this.onMouseMoveBound = this.onMouseMove.bind(this); + this.onTimeUpdateBound = this.debounce(this.update.bind(this), 100); -const sites = () => { - return [ - { - additionalData: "mobile", - host: "youtube", - url: "https://youtu.be/", - match: /^m.youtube.com$/, - selector: "shorts-video #player", - }, - { - additionalData: "mobile", - host: "youtube", - url: "https://youtu.be/", - match: /^m.youtube.com$/, - selector: ".player-container", - }, - { - host: "youtube", - url: "https://youtu.be/", - match: /^(www.)?youtube(-nocookie|kids)?.com$/, - selector: ".html5-video-container:not(#inline-player *)", - }, - { - host: "tiktok", - url: "https://www.tiktok.com/", - match: /^(www.)?tiktok.com$/, - selector: null, - }, - { - host: "proxitok", - url: "https://www.tiktok.com/", - match: sitesProxiTok, - selector: ".column.has-text-centered", - }, - { - host: "twitch", - url: "https://twitch.tv/", - match: [ - /^m.twitch.tv$/, - /^www.twitch.tv$/, - /^clips.twitch.tv$/, - /^player.twitch.tv$/, - ], - selector: ".video-ref, main > div > section > div > div > div", - }, - { - host: "xvideos", - url: "https://www.xvideos.com/", - match: /^www.(xvideos|xv-ru).com$/, - selector: ".video-bg-pic", - }, - { - host: "pornhub", - url: "https://rt.pornhub.com/view_video.php?viewkey=", - match: /^[a-z]+.pornhub.com$/, - selector: ".mainPlayerDiv > .video-element-wrapper-js > div", - }, - { - additionalData: "embed", - host: "pornhub", - url: "https://rt.pornhub.com/view_video.php?viewkey=", - match: (url) => - url.host.includes("pornhub.com") && url.pathname.startsWith("/embed/"), - selector: "#player", - }, - { - additionalData: "mobile", - host: "vk", - url: "https://vk.com/video?z=", - match: /^m.vk.(com|ru)$/, - selector: "vk-video-player", - shadowRoot: true, - }, - { - additionalData: "clips", - host: "vk", - url: "https://vk.com/video?z=", - match: /^(www.|m.)?vk.(com|ru)$/, - selector: 'div[data-testid="clipcontainer-video"]', - }, - { - host: "vk", - url: "https://vk.com/video?z=", - match: /^(www.|m.)?vk.(com|ru)$/, - selector: ".videoplayer_media", - }, - { - host: "vimeo", - url: "https://vimeo.com/", - match: /^vimeo.com$/, - selector: ".player", - }, - { - additionalData: "embed", - host: "vimeo", - url: "https://player.vimeo.com/", - match: /^player.vimeo.com$/, - selector: ".player", - }, - { - host: "ok.ru", - url: "https://ok.ru/video/", - match: /^ok.ru$/, - selector: ".html5-vpl_vid", - }, - { - host: "nine_gag", - url: "https://9gag.com/gag/", - match: /^9gag.com$/, - selector: ".video-post", - }, - // { - // host: "coub", - // url: "https://coub.com/view/", - // match: /^coub.com$/, - // selector: ".viewer__player", - // }, - { - host: "bitchute", - url: "https://www.bitchute.com/video/", - match: /^(www.)?bitchute.com$/, - selector: ".video-js", - }, - { - host: "rutube", - url: "https://rutube.ru/video/", - match: /^rutube.ru$/, - selector: ".video-player > div > div > div:nth-child(2)", - }, - { - additionalData: "embed", - host: "rutube", - url: "https://rutube.ru/video/", - match: /^rutube.ru$/, - selector: "#app > div > div", - }, - { - host: "bilibili", - url: "https://www.bilibili.com/video/", - match: /^(www|m|player).bilibili.com$/, - selector: ".bpx-player-video-wrap", - }, - // Добавляет лишние видео в обработчик - { - additionalData: "old", // /blackboard/webplayer/embed-old.html - host: "bilibili", - url: "https://www.bilibili.com/video/", - match: /^(www|m).bilibili.com$/, - selector: null, - }, - { - host: "twitter", - url: "https://twitter.com/i/status/", - match: /^twitter.com$/, - selector: 'div[data-testid="videoComponent"] > div:nth-child(1) > div', - }, - { - host: "mail_ru", - url: "https://my.mail.ru/", - match: /^my.mail.ru$/, - selector: "#b-video-wrapper", - }, - { - // ONLY IF YOU LOGINED TO COURSERA /learn/NAME/lecture/XXXX - host: "coursera", - url: "https://www.coursera.org/", - match: /coursera.org$/, - selector: ".vjs-v6", - }, - { - // ONLY IF YOU LOGINED TO UDEMY /course/NAME/learn/lecture/XXXX - host: "udemy", - url: "https://www.udemy.com/", - match: /udemy.com$/, - selector: - 'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)', - }, - { - // Sites host Invidious. I tested the performance only on invidious.kevin.rocks, youtu.be and inv.vern.cc - host: "invidious", - url: "https://youtu.be/", - match: sitesInvidious, - selector: "#player", - }, - { - // Sites host Piped. I tested the performance only on piped.video - host: "piped", - url: "https://youtu.be/", - match: sitesPiped, - selector: ".shaka-video-container", - }, - { - host: "rumble", - url: "https://rumble.com/", - match: /^rumble.com$/, - selector: "#videoPlayer > .videoPlayer-Rumble-cls > div", - }, - { - host: "eporner", - url: "https://www.eporner.com/", - match: /^(www.)?eporner.com$/, - selector: ".vjs-v7", - }, - { - host: "peertube", - url: "stub", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: sitesPeertube, - selector: ".vjs-v7", - }, - { - host: "dailymotion", - url: "https://dai.ly/", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: /^geo.dailymotion.com$/, - selector: ".player", - }, - { - host: "trovo", - url: "https://trovo.live/s/", - match: /^trovo.live$/, - selector: ".player-video", - }, - { - host: "yandexdisk", - url: "https://yadi.sk/i/", - match: /^disk.yandex.ru$/, - selector: ".video-player__player > div:nth-child(1)", - }, - { - host: "coursehunter", - url: "https://coursehunter.net/course/", - match: /^coursehunter.net$/, - selector: "#oframeplayer > pjsdiv:nth-of-type(1)", - }, - { - host: "googledrive", - url: "https://drive.google.com/file/d/", - match: /^youtube.googleapis.com$/, - selector: ".html5-video-container", - }, - { - host: "bannedvideo", - url: "https://banned.video/watch?id=", - match: /^(www.)?banned.video$/, - selector: ".vjs-v7", - }, - { - host: "facebook", - url: "https://facebook.com/", - match: (url) => - url.host.includes("facebook.com") && url.pathname.includes("/videos/"), - selector: 'div[role="main"] div[data-pagelet$="video" i]', - }, - { - additionalData: "reels", - host: "facebook", - url: "https://facebook.com/", - match: (url) => - url.host.includes("facebook.com") && url.pathname.includes("/reel/"), - selector: 'div[role="main"]', - }, - { - host: "weverse", - url: "https://weverse.io/", - match: /^weverse.io$/, - selector: ".webplayer-internal-source-wrapper", - }, - { - host: "newgrounds", - url: "https://www.newgrounds.com/", - match: /^www.newgrounds.com$/, - selector: ".ng-video-player", - }, - { - // TODO: Добавить поддержку tips (сделать через m3u8 т.к. обычная ссылка не принимается) и платных курсов - host: "egghead", - url: "https://egghead.io/", - match: /^egghead.io$/, - selector: ".cueplayer-react-video-holder", - }, - { - host: "youku", - url: "https://v.youku.com/", - match: /^v.youku.com$/, - selector: "#ykPlayer", - }, - { - host: "archive", - url: "https://archive.org/details/", - match: /^archive.org$/, - selector: ".jw-media", - }, - { - host: "directlink", - url: "stub", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: (url) => /([^.]+).mp4/.test(url.pathname), - selector: null, - }, - // пока рано - // { - // host: "patreon", - // url: "https://www.patreon.com/", - // match: /^www.patreon.com$/, - // selector: - // 'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div', - // }, - // { - // host: "sibnet", - // url: "https://video.sibnet.ru/", - // match: /^video.sibnet.ru$/, - // selector: ".video-js", // #video_html5_wrapper - // }, - // Нужно куда-то заливать данные о плейлисте - // { - // host: "epicgames", - // url: "https://dev.epicgames.com/community/learning/tutorials/", - // match: /^dev.epicgames.com$/, - // selector: "#vjs_video_3", - // }, - ]; -}; + document.addEventListener("mousedown", this.onMouseDownBound); + document.addEventListener("mouseup", this.onMouseUpBound); + document.addEventListener("mousemove", this.onMouseMoveBound); + this.video?.addEventListener("timeupdate", this.onTimeUpdateBound); + + this.resizeObserver = new ResizeObserver(this.onResize.bind(this)); + this.resizeObserver.observe(this.container); + } + + onMouseDown(e) { + if (this.subtitlesContainer.contains(e.target)) { + const rect = this.subtitlesContainer.getBoundingClientRect(); + const containerRect = this.container.getBoundingClientRect(); + this.dragging = { + active: true, + offset: { + x: e.clientX - rect.left, + y: e.clientY - rect.top, + }, + containerOffset: { + x: containerRect.left, + y: containerRect.top, + }, + }; + } + } + + onMouseUp() { + this.dragging.active = false; + } + + onMouseMove(e) { + if (this.dragging.active) { + e.preventDefault(); + const { width, height } = this.container.getBoundingClientRect(); + const containerOffset = this.dragging.containerOffset; + this.position = { + left: + ((e.clientX - this.dragging.offset.x - containerOffset.x) / width) * + 100, + top: + ((e.clientY - this.dragging.offset.y - containerOffset.y) / height) * + 100, + }; + this.applySubtitlePosition(); + } + } + + onResize() { + this.updateContainerRect(); + } + + updateContainerRect() { + this.containerRect = this.container.getBoundingClientRect(); + this.applySubtitlePosition(); + } + + applySubtitlePosition() { + const { width, height } = this.containerRect; + const { offsetWidth, offsetHeight } = this.subtitlesContainer; + + const maxLeft = ((width - offsetWidth) / width) * 100; + const maxTop = ((height - offsetHeight) / height) * 100; + + this.position.left = Math.max(0, Math.min(this.position.left, maxLeft)); + this.position.top = Math.max(0, Math.min(this.position.top, maxTop)); + + this.subtitlesContainer.style.left = `${this.position.left}%`; + this.subtitlesContainer.style.top = `${this.position.top}%`; + } + + setContent(subtitles) { + if (subtitles && this.video) { + this.subtitles = subtitles; + this.update(); + } else { + this.subtitles = null; + Q(null, this.subtitlesContainer); + } + } + + setMaxLength(len) { + if (typeof len === "number" && len) { + this.maxLength = len; + this.maxLengthRegexp = new RegExp(`.{1,${len}}(?:\\s|$)`, "g"); + this.update(); + } + } + + setHighlightWords(value) { + this.highlightWords = Boolean(value); + this.update(); + } + + setFontSize(size) { + this.fontSize = size; + const subtitlesEl = + this.subtitlesContainer?.querySelector(".vot-subtitles"); + if (subtitlesEl) { + subtitlesEl.style.fontSize = `${this.fontSize}px`; + } + } + + /** + * Set subtitles opacity by percentage where 100 - full transparent, 0 - not transparent + * + * @param {number} rate - 0-100 percent of opacity + */ + setOpacity(rate) { + this.opacity = ((100 - +rate) / 100).toFixed(2); + const subtitlesEl = + this.subtitlesContainer?.querySelector(".vot-subtitles"); + if (subtitlesEl) { + subtitlesEl.style.setProperty("--vot-subtitles-opacity", this.opacity); + } + } + + update() { + if (!this.video || !this.subtitles) return; + + const time = this.video.currentTime * 1000; + const line = this.subtitles.subtitles?.findLast( + (e) => e.startMs < time && time < e.startMs + e.durationMs, + ); + + if (!line) { + Q(null, this.subtitlesContainer); + return; + } + + let tokens = this.processTokens(line.tokens); + const content = this.renderTokens(tokens, time); + const stringContent = JSON.stringify(content); + if (stringContent !== this.lastContent) { + this.lastContent = stringContent; + Q( + ke`${content}`, + this.subtitlesContainer, + ); + } + } + + processTokens(tokens) { + if (tokens.at(-1).alignRange.end <= this.maxLength) return tokens; + + let chunks = []; + let chunkTokens = []; + let length = 0; + + for (const token of tokens) { + length += token.text.length; + chunkTokens.push(token); + + if (length > this.maxLength) { + chunks.push(this.trimChunk(chunkTokens)); + chunkTokens = []; + length = 0; + } + } + + if (chunkTokens.length) chunks.push(this.trimChunk(chunkTokens)); + + const time = this.video.currentTime * 1000; + return ( + chunks.find( + (chunk) => + chunk[0].startMs < time && + time < chunk.at(-1).startMs + chunk.at(-1).durationMs, + ) || chunks[0] + ); + } + + trimChunk(tokens) { + if (tokens[0].text === " ") tokens.shift(); + if (tokens.at(-1).text === " ") tokens.pop(); + return tokens; + } + + renderTokens(tokens, time) { + return tokens.map((token) => { + const passed = + this.highlightWords && + (time > token.startMs + token.durationMs / 2 || + (time > token.startMs - 100 && + token.startMs + token.durationMs / 2 - time < 275)); + return ke`${token.text.replace("\\n", "
")}
`; + }); + } + + debounce(func, wait) { + let timeout; + return (...args) => { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; + } -/* harmony default export */ const config_sites = (sites()); + release() { + document.removeEventListener("mousedown", this.onMouseDownBound); + document.removeEventListener("mouseup", this.onMouseUpBound); + document.removeEventListener("mousemove", this.onMouseMoveBound); + this.video?.removeEventListener("timeupdate", this.onTimeUpdateBound); + this.resizeObserver.disconnect(); + this.subtitlesContainer.remove(); + } +} // EXTERNAL MODULE: ./node_modules/requestidlecallback-polyfill/index.js var requestidlecallback_polyfill = __webpack_require__("./node_modules/requestidlecallback-polyfill/index.js"); @@ -4349,9 +5993,11 @@ class EventImpl { constructor() { this.listeners = new Set(); } + hasListener(e) { return this.listeners.has(e); } + dispatchToListener(handler, ...args) { try { handler(...args); @@ -4359,18 +6005,21 @@ class EventImpl { console.error("[VOT]", exception); } } + addListener(handler) { if (this.hasListener(handler)) { throw new Error("[VOT] The listener has already been added."); } this.listeners.add(handler); } + removeListener(handler) { if (!this.hasListener(handler)) { throw new Error("[VOT] The listener has not been added yet."); } this.listeners.delete(handler); } + dispatch(...args) { for (const handler of Array.from(this.listeners)) { this.dispatchToListener(handler, ...args); @@ -4477,7 +6126,7 @@ class VideoObserver { childList: true, subtree: true, }); - const videos = document.querySelectorAll("video"); + const videos = this.getAllVideoEls(); for (let i = 0; i < videos.length; i++) { this.checkAndHandleVideo(videos[i]); } @@ -4488,6 +6137,42 @@ class VideoObserver { this.intersectionObserver.disconnect(); } + getAllVideoEls() { + const videos = document.querySelectorAll("video"); + if (videos.length) { + return videos; + } + + // Use it only if we don't find videos + // It takes a long time to complete + const els = document.querySelectorAll("*"); + const videoElements = new Set(); + function traverseShadowRoot(root) { + if (!root) return; + root.querySelectorAll("video").forEach((video) => { + if (videoElements.has(video)) { + return; + } + videoElements.add(video); + }); + + root.querySelectorAll("*").forEach((element) => { + if (element.shadowRoot) { + traverseShadowRoot(element.shadowRoot); + } + }); + } + + for (let i = 0; i < els.length; i++) { + const el = els[i]; + if (el.shadowRoot) { + traverseShadowRoot(el); + } + } + + return Array.from(videoElements); + } + checkAndHandleVideo(video) { if (this.videoCache.has(video)) { return; @@ -4499,7 +6184,7 @@ class VideoObserver { handleIntersectingVideo(video) { this.intersectionObserver.unobserve(video); if (isAdVideo(video)) { - debug/* default */.A.log("The promotional video was ignored", video); + utils_debug.log("The promotional video was ignored", video); return; } waitForVideoReady(video, (readyVideo) => { @@ -4540,11 +6225,6 @@ class VideoObserver { - - - - - @@ -4552,11 +6232,14 @@ class VideoObserver { const browserInfo = es5.getParser(window.navigator.userAgent).getResult(); - const dontTranslateMusic = false; // Пока не придумал как стоит реализовать - -const sitesChromiumBlocked = [...sitesInvidious, ...sitesPiped]; - +const cfOnlyExtensions = [ + "Violentmonkey", + "FireMonkey", + "Greasemonkey", + "AdGuard", + "OrangeMonkey", +]; const videoLipSyncEvents = [ "playing", "ratechange", @@ -4573,140 +6256,6 @@ function genOptionsByOBJ(obj, conditionString) { })); } -if (false) { var translationPanding; } - -function translateVideo( - url, - duration, - requestLang, - responseLang, - translationHelp, - callback, -) { - debug/* default */.A.log( - `Translate video (url: ${url}, duration: ${duration}, requestLang: ${requestLang}, responseLang: ${responseLang})`, - ); - - debug/* default */.A.log("translationHelp:", translationHelp); - - if (false) {} - - translationPanding = true; - - rvt( - url, - duration, - requestLang, - responseLang, - translationHelp, - (success, response) => { - translationPanding = false; - - debug/* default */.A.log("[exec callback] Requesting video translation"); - if (!success) { - callback(false, localizationProvider.get("requestTranslationFailed")); - return; - } - - const translateResponse = - yandexProtobuf.decodeTranslationResponse(response); - console.log("[VOT] Translation response: ", translateResponse); - - switch (translateResponse.status) { - case 0: - callback(false, translateResponse.message); - break; - case 1: - case 5: - // status 5 (dzen) - // Отдает частичный контент т.е. аудио не для всего видео, а только для части (~10min) - // так же возвращается оставшееся время перевода (remainingTime) через которое нужно сделать повторный запрос, - // в котором будет возвращено полное аудио перевода и status 1. - // Если включена часть видео без перевода, то пишет "Эта часть видео еще не переведена" - callback( - !!translateResponse.url, - translateResponse.url || - localizationProvider.get("audioNotReceived"), - ); - break; - case 2: - callback( - false, - translateResponse.remainingTime - ? secsToStrTime(translateResponse.remainingTime) - : localizationProvider.get("translationTakeFewMinutes"), - ); - break; - case 3: - case 6: - /* - status: 3 - Иногда, в ответе приходит статус код 3, но видео всё, так же, ожидает перевода. - В конечном итоге, это занимает слишком много времени, - как-будто сервер не понимает, что данное видео уже недавно было переведено - и заместо возвращения готовой ссылки на перевод начинает переводить видео заново - при чём у него это получается за очень длительное время. - - status: 6 - Случайно встретил 6 статус код при котором видео так же продолжается перевод, - но после него ничего сверхъестественного не происходит. - Он появляется при первом запросе с 17=1, но не исключено, - что может появится и просто так - */ - callback(false, localizationProvider.get("videoBeingTranslated")); - break; - } - }, - ); -} - -function translateStream(url, requestLang, responseLang, callback) { - debug/* default */.A.log( - `Translate stream (url: ${url}, requestLang: ${requestLang}, responseLang: ${responseLang})`, - ); - - rst( - url, - requestLang, - responseLang, - (success, response) => { - debug/* default */.A.log("[exec callback] Requesting stream translation"); - if (!success) { - callback(false, localizationProvider.get("requestTranslationFailed")); - return; - } - - const streamResponse = yandexProtobuf.decodeStreamResponse(response); - console.log("[VOT] Stream Translation response: ", streamResponse); - - switch (streamResponse.interval) { - case 10: - callback( - false, - streamResponse.interval, - localizationProvider.get("translationTakeFewMinutes"), - ); - break; - case 20: - callback( - true, - streamResponse.interval, - streamResponse || localizationProvider.get("audioNotReceived"), - ); - break; - case 0: - // stream removed or ended - callback( - false, - streamResponse.interval, - localizationProvider.get("streamNoConnectionToServer"), - ); - break; - } - }, - ); -} - class VideoHandler { // translate properties translateFromLang = "en"; // default language of video @@ -4714,7 +6263,6 @@ class VideoHandler { timer; - ytData = ""; videoData = ""; firstPlay = true; audio = new Audio(); @@ -4722,15 +6270,18 @@ class VideoHandler { gainNode = this.audioContext.createGain(); hls = initHls(); // debug enabled only in dev mode + votClient; videoTranslations = []; videoTranslationTTL = 7200; + cachedTranslation; downloadTranslationUrl = null; downloadSubtitlesUrl = null; autoRetry; streamPing; + votOpts; volumeOnStart; tempOriginalVolume; // temp video volume for syncing tempVolume; // temp translation volume for syncing @@ -4744,8 +6295,15 @@ class VideoHandler { // button move dragging; + /** + * Constructor function for VideoHandler class. + * + * @param {Object} video - The video element to handle. + * @param {Object} container - The container element for the video. + * @param {Object} site - The site object associated with the video. + */ constructor(video, container, site) { - debug/* default */.A.log( + utils_debug.log( "[VideoHandler] add video:", video, "container:", @@ -4762,6 +6320,147 @@ class VideoHandler { this.init(); } + /** + * Translate a video based on the specified languages. + * + * @param {Object} videoData - The data of the video to be translated. + * @param {string} requestLang - The language code for the requested translation. + * @param {string} responseLang - The language code for the desired translated output. + * @param {Object} [translationHelp=null] - Additional translation help data (optional). + * @return {Promise} A Promise that resolves to the translated video data. + */ + async translateVideoImpl( + videoData, + requestLang, + responseLang, + translationHelp = null, + ) { + clearTimeout(this.autoRetry); + utils_debug.log( + videoData, + `Translate video (requestLang: ${requestLang}, responseLang: ${responseLang})`, + ); + + if ((await getVideoID(this.site, this.video)) !== videoData.videoId) { + return null; + } + + try { + const res = await this.votClient.translateVideo({ + videoData, + requestLang, + responseLang, + translationHelp, + }); + utils_debug.log("Translate video result", res); + if (res.translated && res.remainingTime < 1) { + utils_debug.log("Video translation finished with this data: ", res); + return res; + } + + await this.updateTranslationErrorMsg( + res.remainingTime > 0 + ? secsToStrTime(res.remainingTime) + : (res.message ?? + localizationProvider.get("translationTakeFewMinutes")), + ); + } catch (err) { + console.error("[VOT] Failed to translate video", err); + await this.updateTranslationErrorMsg(err.data?.message ?? err); + return null; + } + + return new Promise((resolve) => { + const timeoutDuration = this.subtitlesList.some( + (item) => item.source === "yandex", + ) + ? 20_000 + : 30_000; + this.autoRetry = setTimeout(async () => { + const res = await this.translateVideoImpl( + videoData, + requestLang, + responseLang, + translationHelp, + ); + if (!res || (res.translated && res.remainingTime < 1)) { + resolve(res); + } + }, timeoutDuration); + }); + } + + /** + * Translate a video stream based on the specified languages. + * + * @param {Object} videoData - The data of the video stream to be translated. + * @param {string} requestLang - The language code for the requested translation. + * @param {string} responseLang - The language code for the desired translated output. + * @return {Promise} A Promise that resolves to the translated video stream data. + */ + async translateStreamImpl(videoData, requestLang, responseLang) { + clearTimeout(this.autoRetry); + utils_debug.log( + videoData, + `Translate stream (requestLang: ${requestLang}, responseLang: ${responseLang})`, + ); + + if ((await getVideoID(this.site, this.video)) !== videoData.videoId) { + return null; + } + + try { + const res = await this.votClient.translateStream({ + videoData, + requestLang, + responseLang, + }); + utils_debug.log("Translate stream result", res); + if (!res.translated && res.interval === 10) { + await this.updateTranslationErrorMsg( + localizationProvider.get("translationTakeFewMinutes"), + ); + return new Promise((resolve) => { + this.autoRetry = setTimeout(async () => { + const res = await this.translateStreamImpl( + videoData, + requestLang, + responseLang, + ); + if (!res || !(!res.translated && res.interval === 10)) { + resolve(res); + } + }, res.interval * 1000); + }); + } + + if (res.message) { + utils_debug.log(`Stream translation aborted! Message: ${res.message}`); + throw new VOTLocalizedError("streamNoConnectionToServer"); + } + + if (!res.result) { + utils_debug.log("Failed to find translation result! Data:", res); + throw new VOTLocalizedError("audioNotReceived"); + } + + utils_debug.log("Stream translated successfully. Running...", res); + + this.streamPing = setInterval(async () => { + utils_debug.log("Ping stream translation", res.pingId); + this.votClient.pingStream({ + pingId: res.pingId, + }); + }, res.interval * 1000); + + return res; + } catch (err) { + console.error("[VOT] Failed to translate stream", err); + await this.updateTranslationErrorMsg(err.data?.message ?? err); + return null; + } + } + async autoTranslate() { if ( this.site.host === "youtube" && @@ -4790,12 +6489,12 @@ class VideoHandler { } } + /** + * Initializes the VideoHandler class by setting up data promises, fetching data, initializing UI elements, and setting up event listeners. + */ async init() { if (this.initialized) return; - const audioProxyDefault = - lang === "uk" && undefined === "cloudflare" ? 0 : 0; - const dataPromises = { autoTranslate: votStorage.get("autoTranslate", 0, true), dontTranslateLanguage: votStorage.get("dontTranslateLanguage", lang), @@ -4805,25 +6504,27 @@ class VideoHandler { 1, true, ), - autoVolume: votStorage.get("autoVolume", config/* defaultAutoVolume */.JD, true), + autoVolume: votStorage.get("autoVolume", defaultAutoVolume, true), buttonPos: votStorage.get("buttonPos", "default"), showVideoSlider: votStorage.get("showVideoSlider", 1, true), syncVolume: votStorage.get("syncVolume", 0, true), subtitlesMaxLength: votStorage.get("subtitlesMaxLength", 300, true), highlightWords: votStorage.get("highlightWords", 0, true), + subtitlesFontSize: votStorage.get("subtitlesFontSize", 20, true), + subtitlesOpacity: votStorage.get("subtitlesOpacity", 20, true), responseLanguage: votStorage.get("responseLanguage", lang), defaultVolume: votStorage.get("defaultVolume", 100, true), - udemyData: votStorage.get("udemyData", { accessToken: "", expires: 0 }), - audioProxy: votStorage.get("audioProxy", audioProxyDefault, true), + audioProxy: votStorage.get("audioProxy", 0, true), showPiPButton: votStorage.get("showPiPButton", 0, true), translateAPIErrors: votStorage.get("translateAPIErrors", 1, true), translationService: votStorage.get( "translationService", - config/* defaultTranslationService */.mE, + defaultTranslationService, ), - detectService: votStorage.get("detectService", config/* defaultDetectService */.K2), - m3u8ProxyHost: votStorage.get("m3u8ProxyHost", config/* m3u8ProxyHost */.se), - proxyWorkerHost: votStorage.get("proxyWorkerHost", config/* proxyWorkerHost */.Pm), + detectService: votStorage.get("detectService", defaultDetectService), + m3u8ProxyHost: votStorage.get("m3u8ProxyHost", m3u8ProxyHost), + translateProxyEnabled: votStorage.get("translateProxyEnabled", 0, true), + proxyWorkerHost: votStorage.get("proxyWorkerHost", proxyWorkerHost), audioBooster: votStorage.get("audioBooster", 0, true), }; @@ -4838,6 +6539,46 @@ class VideoHandler { console.log("[db] data from db: ", this.data); + if ( + this.data.translateProxyEnabled === 0 && + GM_info?.scriptHandler && + cfOnlyExtensions.includes(GM_info.scriptHandler) + ) { + this.data.translateProxyEnabled = 1; + await votStorage.set("translateProxyEnabled", 1); + utils_debug.log("translateProxyEnabled", this.data.translateProxyEnabled); + } + + utils_debug.log("Extension compatibility passed..."); + + this.votOpts = { + headers: + this.data.translateProxyEnabled === 1 + ? {} + : { + "sec-ch-ua": null, + "sec-ch-ua-mobile": null, + "sec-ch-ua-platform": null, + // "sec-ch-ua-model": null, + // "sec-ch-ua-platform-version": null, + // "sec-ch-ua-wow64": null, + // "sec-ch-ua-arch": null, + // "sec-ch-ua-bitness": null, + // "sec-ch-ua-full-version": null, + // "sec-ch-ua-full-version-list": null, + }, + fetchFn: GM_fetch, + hostVOT: votBackendUrl, + host: + this.data.translateProxyEnabled === 1 + ? this.data.proxyWorkerHost + : workerHost, + }; + + this.votClient = new ( + this.data.translateProxyEnabled === 1 ? VOTWorkerClient : VOTClient + )(this.votOpts); + this.subtitlesWidget = new SubtitlesWidget( this.video, this.container, @@ -4845,6 +6586,8 @@ class VideoHandler { ); this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength); this.subtitlesWidget.setHighlightWords(this.data.highlightWords); + this.subtitlesWidget.setFontSize(this.data.subtitlesFontSize); + this.subtitlesWidget.setOpacity(this.data.subtitlesOpacity); // audio booster this.audio.crossOrigin = "anonymous"; @@ -4878,24 +6621,39 @@ class VideoHandler { this.initialized = true; } + /** + * Set translation button status and text + * + * @param {"none"|"success"|"error"} status - button status + * @param {string} text - visible button text + */ transformBtn(status = "none", text) { this.votButton.container.dataset.status = status; - this.votButton.container.dataset.translating = - status === "error" - ? text.includes(localizationProvider.get("translationTake")) - : false; - this.votButton.label.innerHTML = text; + const isLoading = + status === "error" && + text.includes(localizationProvider.get("translationTake")); + this.setLoadingBtn(isLoading); + this.votButton.label.textContent = text; this.votButton.container.title = status === "error" ? text : ""; } + /** + * Set loading icon to translation button + * + * @param {boolean} loading + */ + setLoadingBtn(loading = false) { + this.votButton.container.dataset.loading = loading; + } + initUI() { // VOT Button { this.votButton = ui.createVOTButton( localizationProvider.get("translateVideo"), ); - // use an additional check because sometimes this.video.clientWidth = 0 + // use an additional check because sometimes this.video.clientWidth = 0 if ( this.data?.buttonPos && this.data?.buttonPos !== "default" && @@ -4932,19 +6690,46 @@ class VideoHandler { this.container.appendChild(this.votMenu.container); this.votDownloadButton = ui.createIconButton( - ``, + Oe` + + `, ); this.votDownloadButton.hidden = true; this.votMenu.headerContainer.appendChild(this.votDownloadButton); this.votDownloadSubtitlesButton = ui.createIconButton( - ``, + Oe` + + `, ); this.votDownloadSubtitlesButton.hidden = true; this.votMenu.headerContainer.appendChild(this.votDownloadSubtitlesButton); this.votSettingsButton = ui.createIconButton( - ``, + Oe` + + `, ); this.votMenu.headerContainer.appendChild(this.votSettingsButton); @@ -4952,16 +6737,12 @@ class VideoHandler { fromTitle: localizationProvider.get("langs")[this.video.detectedLanguage], fromDialogTitle: localizationProvider.get("videoLanguage"), - fromItems: [ - { - label: localizationProvider.get("langs")["auto"], - value: "auto", - selected: "", - }, - ...genOptionsByOBJ(availableLangs, this.videoData.detectedLanguage), - ], + fromItems: genOptionsByOBJ( + availableLangs, + this.videoData.detectedLanguage, + ), fromOnSelectCB: async (e) => { - debug/* default */.A.log( + utils_debug.log( "[fromOnSelectCB] select from language", e.target.dataset.votValue, ); @@ -4973,13 +6754,13 @@ class VideoHandler { }, toTitle: localizationProvider.get("langs")[this.video.responseLanguage], toDialogTitle: localizationProvider.get("translationLanguage"), - toItems: genOptionsByOBJ(actualTTS, this.videoData.responseLanguage), + toItems: genOptionsByOBJ(availableTTS, this.videoData.responseLanguage), toOnSelectCB: async (e) => { const newLang = e.target.dataset.votValue; - debug/* default */.A.log("[toOnSelectCB] select to language", newLang); + utils_debug.log("[toOnSelectCB] select to language", newLang); this.data.responseLanguage = this.translateToLang = newLang; await votStorage.set("responseLanguage", this.data.responseLanguage); - debug/* default */.A.log( + utils_debug.log( "Response Language value changed. New value: ", this.data.responseLanguage, ); @@ -5019,9 +6800,8 @@ class VideoHandler { this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container); this.votVideoVolumeSlider = ui.createSlider( - `${localizationProvider.get("VOTVolume")}: ${ - this.getVideoVolume() * 100 - }%`, + ke`${localizationProvider.get("VOTVolume")}: + ${this.getVideoVolume() * 100}%`, this.getVideoVolume() * 100, ); this.votVideoVolumeSlider.container.hidden = @@ -5032,12 +6812,11 @@ class VideoHandler { ); this.votVideoTranslationVolumeSlider = ui.createSlider( - `${localizationProvider.get("VOTVolumeTranslation")}: ${ - this.data?.defaultVolume ?? 100 - }%`, + ke`${localizationProvider.get("VOTVolumeTranslation")}: + ${this.data?.defaultVolume ?? 100}%`, this.data?.defaultVolume ?? 100, 0, - this.data.audioBooster ? config/* maxAudioVolume */.T8 : 100, + this.data.audioBooster ? maxAudioVolume : 100, ); this.votVideoTranslationVolumeSlider.container.hidden = this.votButton.container.dataset.status !== "success"; @@ -5110,8 +6889,10 @@ class VideoHandler { this.votAutoSetVolumeCheckbox.container, ); this.votAutoSetVolumeSlider = ui.createSlider( - `${(this.data?.autoVolume ?? config/* defaultAutoVolume */.JD) * 100}%`, - (this.data?.autoVolume ?? config/* defaultAutoVolume */.JD) * 100, + ke`${(this.data?.autoVolume ?? defaultAutoVolume) * 100}%`, + (this.data?.autoVolume ?? defaultAutoVolume) * 100, 0, 100, ); @@ -5135,16 +6916,6 @@ class VideoHandler { this.votAudioBoosterCheckbox.container, ); - // udemy only - this.votUdemyDataTextfield = ui.createTextfield( - localizationProvider.get("VOTUdemyData"), - this.data?.udemyData?.accessToken ?? "", - ); - this.votUdemyDataTextfield.container.hidden = this.site.host !== "udemy"; - this.votSettingsDialog.bodyContainer.appendChild( - this.votUdemyDataTextfield.container, - ); - this.votSyncVolumeCheckbox = ui.createCheckbox( localizationProvider.get("VOTSyncVolume"), this.data?.syncVolume ?? false, @@ -5155,12 +6926,12 @@ class VideoHandler { this.votTranslationServiceSelect = ui.createVOTSelect( votStorage - .syncGet("translationService", config/* defaultTranslationService */.mE) + .syncGet("translationService", defaultTranslationService) .toUpperCase(), localizationProvider.get("VOTTranslationService"), genOptionsByOBJ( translateServices, - votStorage.syncGet("translationService", config/* defaultTranslationService */.mE), + votStorage.syncGet("translationService", defaultTranslationService), ), { onSelectCb: async (e) => { @@ -5183,11 +6954,11 @@ class VideoHandler { ); this.votDetectServiceSelect = ui.createVOTSelect( - votStorage.syncGet("detectService", config/* defaultDetectService */.K2).toUpperCase(), + votStorage.syncGet("detectService", defaultDetectService).toUpperCase(), localizationProvider.get("VOTDetectService"), genOptionsByOBJ( detectServices, - votStorage.syncGet("detectService", config/* defaultDetectService */.K2), + votStorage.syncGet("detectService", defaultDetectService), ), { onSelectCb: async (e) => { @@ -5210,24 +6981,11 @@ class VideoHandler { ); this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader); - this.votSubtitlesMaxLengthSlider = ui.createSlider( - `${localizationProvider.get("VOTSubtitlesMaxLength")}: ${ - this.data?.subtitlesMaxLength ?? 300 - }`, - this.data?.subtitlesMaxLength ?? 300, - 50, - 300, - ); - this.votSettingsDialog.bodyContainer.appendChild( - this.votSubtitlesMaxLengthSlider.container, - ); - - this.votSubtitlesHighlightWordsCheckbox = ui.createCheckbox( - localizationProvider.get("VOTHighlightWords"), - this.data?.highlightWords ?? false, + this.votSubtitlesDetails = ui.createDetails( + localizationProvider.get("VOTSubtitlesDesign"), ); this.votSettingsDialog.bodyContainer.appendChild( - this.votSubtitlesHighlightWordsCheckbox.container, + this.votSubtitlesDetails.container, ); // PROXY @@ -5240,30 +6998,29 @@ class VideoHandler { this.votM3u8ProxyHostTextfield = ui.createTextfield( localizationProvider.get("VOTM3u8ProxyHost"), this.data?.m3u8ProxyHost, - config/* m3u8ProxyHost */.se, + m3u8ProxyHost, ); this.votSettingsDialog.bodyContainer.appendChild( this.votM3u8ProxyHostTextfield.container, ); - // cf version only this.votProxyWorkerHostTextfield = ui.createTextfield( localizationProvider.get("VOTProxyWorkerHost"), this.data?.proxyWorkerHost, - config/* proxyWorkerHost */.Pm, + proxyWorkerHost, ); - this.votProxyWorkerHostTextfield.container.hidden = - undefined !== "cloudflare"; + // this.votProxyWorkerHostTextfield.container.hidden = + // this.data.translateProxyEnabled !== 1; this.votSettingsDialog.bodyContainer.appendChild( this.votProxyWorkerHostTextfield.container, ); - // cf version only this.votAudioProxyCheckbox = ui.createCheckbox( localizationProvider.get("VOTAudioProxy"), this.data?.audioProxy ?? false, ); - this.votAudioProxyCheckbox.container.hidden = undefined !== "cloudflare"; + // this.votAudioProxyCheckbox.container.hidden = + // this.data.translateProxyEnabled !== 1; this.votSettingsDialog.bodyContainer.appendChild( this.votAudioProxyCheckbox.container, ); @@ -5275,6 +7032,7 @@ class VideoHandler { this.votLanguageSelect = ui.createVOTSelect( localizationProvider.get("langs")[ + // eslint-disable-next-line sonarjs/no-duplicate-string votStorage.syncGet("locale-lang-override", "auto") ], localizationProvider.get("VOTMenuLanguage"), @@ -5310,9 +7068,7 @@ class VideoHandler { this.votVersionInfo = ui.createInformation( `${localizationProvider.get("VOTVersion")}:`, - false - ? 0 - : GM_info.script.version, + GM_info.script.version, ); this.votSettingsDialog.bodyContainer.appendChild( this.votVersionInfo.container, @@ -5357,19 +7113,19 @@ class VideoHandler { this.votButton.translateButton.addEventListener("click", () => { (async () => { if (this.audio.src) { - debug/* default */.A.log("[click translationBtn] audio.src is not empty"); + utils_debug.log("[click translationBtn] audio.src is not empty"); this.stopTranslate(); return; } if (this.hls.url) { - debug/* default */.A.log("[click translationBtn] hls is not empty"); + utils_debug.log("[click translationBtn] hls is not empty"); this.stopTranslate(); return; } try { - debug/* default */.A.log("[click translationBtn] trying execute translation"); + utils_debug.log("[click translationBtn] trying execute translation"); if (!this.videoData.videoId) { throw new VOTLocalizedError("VOTNoVideoIDFound"); @@ -5452,7 +7208,7 @@ class VideoHandler { }); this.votDownloadSubtitlesButton.addEventListener("click", async () => { - const srtContent = jsonToSrt(this.YandexSubtitles); + const srtContent = convertSubs(this.yandexSubtitles, "srt"); const blob = new Blob([srtContent], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); @@ -5473,7 +7229,7 @@ class VideoHandler { this.votVideoVolumeSlider.input.addEventListener("input", (e) => { const value = Number(e.target.value); - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = + this.votVideoVolumeSlider.label.querySelector("strong").textContent = `${value}%`; this.setVideoVolume(value / 100); if (this.data.syncVolume) { @@ -5489,7 +7245,7 @@ class VideoHandler { await votStorage.set("defaultVolume", this.data.defaultVolume); this.votVideoTranslationVolumeSlider.label.querySelector( "strong", - ).innerHTML = `${this.data.defaultVolume}%`; + ).textContent = `${this.data.defaultVolume}%`; this.gainNode.gain.value = this.data.defaultVolume / 100; if (!this.data.syncVolume) { return; @@ -5515,7 +7271,7 @@ class VideoHandler { this.data.autoTranslate = Number(e.target.checked); await votStorage.set("autoTranslate", this.data.autoTranslate); await this.autoTranslate(); - debug/* default */.A.log( + utils_debug.log( "autoTranslate value changed. New value: ", this.data.autoTranslate, ); @@ -5531,7 +7287,7 @@ class VideoHandler { "dontTranslateYourLang", this.data.dontTranslateYourLang, ); - debug/* default */.A.log( + utils_debug.log( "dontTranslateYourLang value changed. New value: ", this.data.dontTranslateYourLang, ); @@ -5546,7 +7302,7 @@ class VideoHandler { "autoSetVolumeYandexStyle", this.data.autoSetVolumeYandexStyle, ); - debug/* default */.A.log( + utils_debug.log( "autoSetVolumeYandexStyle value changed. New value: ", this.data.autoSetVolumeYandexStyle, ); @@ -5558,8 +7314,9 @@ class VideoHandler { const presetAutoVolume = Number(e.target.value); this.data.autoVolume = (presetAutoVolume / 100).toFixed(2); await votStorage.set("autoVolume", this.data.autoVolume); - this.votAutoSetVolumeSlider.label.querySelector("strong").innerHTML = - `${presetAutoVolume}%`; + this.votAutoSetVolumeSlider.label.querySelector( + "strong", + ).textContent = `${presetAutoVolume}%`; })(); }); @@ -5567,7 +7324,7 @@ class VideoHandler { (async () => { this.data.showVideoSlider = Number(e.target.checked); await votStorage.set("showVideoSlider", this.data.showVideoSlider); - debug/* default */.A.log( + utils_debug.log( "showVideoSlider value changed. New value: ", this.data.showVideoSlider, ); @@ -5581,7 +7338,7 @@ class VideoHandler { (async () => { this.data.audioBooster = Number(e.target.checked); await votStorage.set("audioBooster", this.data.audioBooster); - debug/* default */.A.log( + utils_debug.log( "audioBooster value changed. New value: ", this.data.audioBooster, ); @@ -5590,7 +7347,7 @@ class VideoHandler { this.votVideoTranslationVolumeSlider.input.value; this.votVideoTranslationVolumeSlider.input.max = this.data .audioBooster - ? config/* maxAudioVolume */.T8 + ? maxAudioVolume : 100; if (!this.data.audioBooster) { this.votVideoTranslationVolumeSlider.input.value = @@ -5602,26 +7359,11 @@ class VideoHandler { })(); }); - this.votUdemyDataTextfield.input.addEventListener("change", (e) => { - (async () => { - this.data.udemyData = { - accessToken: e.target.value, - expires: new Date().getTime(), - }; - await votStorage.set("udemyData", this.data.udemyData); - debug/* default */.A.log( - "udemyData value changed. New value: ", - this.data.udemyData, - ); - window.location.reload(); - })(); - }); - this.votSyncVolumeCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.syncVolume = Number(e.target.checked); await votStorage.set("syncVolume", this.data.syncVolume); - debug/* default */.A.log( + utils_debug.log( "syncVolume value changed. New value: ", this.data.syncVolume, ); @@ -5637,7 +7379,7 @@ class VideoHandler { "translateAPIErrors", this.data.translateAPIErrors, ); - debug/* default */.A.log( + utils_debug.log( "translateAPIErrors value changed. New value: ", this.data.translateAPIErrors, ); @@ -5647,40 +7389,131 @@ class VideoHandler { // SUBTITLES - this.votSubtitlesMaxLengthSlider.input.addEventListener("input", (e) => { - (async () => { - this.data.subtitlesMaxLength = Number(e.target.value); - await votStorage.set( - "subtitlesMaxLength", - this.data.subtitlesMaxLength, - ); - this.votSubtitlesMaxLengthSlider.label.querySelector( - "strong", - ).innerHTML = `${this.data.subtitlesMaxLength}`; - this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength); - })(); - }); + this.votSubtitlesDetails.container.addEventListener("click", () => { + this.votSubtitlesDialog = ui.createDialog( + localizationProvider.get("VOTSubtitlesDesign"), + ); + this.votSubtitlesDialog.container.classList.add("vot-dialog-temp"); + this.votSubtitlesDialog.container.hidden = false; + // remove the modal so that they do not accumulate + this.votSubtitlesDialog.backdrop.onclick = + this.votSubtitlesDialog.closeButton.onclick = () => { + this.votSubtitlesDialog.container.remove(); + }; - this.votSubtitlesHighlightWordsCheckbox.input.addEventListener( - "change", - (e) => { + // subtitles elements + this.votSubtitlesHighlightWordsCheckbox = ui.createCheckbox( + localizationProvider.get("VOTHighlightWords"), + this.data?.highlightWords ?? false, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesHighlightWordsCheckbox.container, + ); + + this.votSubtitlesMaxLengthSlider = ui.createSlider( + ke`${localizationProvider.get("VOTSubtitlesMaxLength")}: + ${this.data?.subtitlesMaxLength ?? 300}`, + this.data?.subtitlesMaxLength ?? 300, + 50, + 300, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesMaxLengthSlider.container, + ); + + this.votSubtitlesFontSizeSlider = ui.createSlider( + ke`${localizationProvider.get("VOTSubtitlesFontSize")}: + ${this.data?.subtitlesFontSize ?? 20}`, + this.data?.subtitlesFontSize ?? 20, + 8, + 50, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesFontSizeSlider.container, + ); + + this.votSubtitlesOpacitySlider = ui.createSlider( + ke`${localizationProvider.get("VOTSubtitlesOpacity")}: + ${this.data?.subtitlesOpacity ?? 20}`, + this.data?.subtitlesOpacity ?? 20, + 0, + 100, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesOpacitySlider.container, + ); + + // subtitles events + this.votSubtitlesHighlightWordsCheckbox.input.addEventListener( + "change", + (e) => { + (async () => { + this.data.highlightWords = Number(e.target.checked); + await votStorage.set("highlightWords", this.data.highlightWords); + utils_debug.log( + "highlightWords value changed. New value: ", + this.data.highlightWords, + ); + this.subtitlesWidget.setHighlightWords(this.data.highlightWords); + })(); + }, + ); + + this.votSubtitlesMaxLengthSlider.input.addEventListener( + "input", + (e) => { + (async () => { + this.data.subtitlesMaxLength = Number(e.target.value); + await votStorage.set( + "subtitlesMaxLength", + this.data.subtitlesMaxLength, + ); + this.votSubtitlesMaxLengthSlider.label.querySelector( + "strong", + ).textContent = `${this.data.subtitlesMaxLength}`; + this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength); + })(); + }, + ); + + this.votSubtitlesFontSizeSlider.input.addEventListener("input", (e) => { + (async () => { + this.data.subtitlesFontSize = Number(e.target.value); + await votStorage.set( + "subtitlesFontSize", + this.data.subtitlesFontSize, + ); + this.votSubtitlesFontSizeSlider.label.querySelector( + "strong", + ).textContent = `${this.data.subtitlesFontSize}`; + this.subtitlesWidget.setFontSize(this.data.subtitlesFontSize); + })(); + }); + + this.votSubtitlesOpacitySlider.input.addEventListener("input", (e) => { (async () => { - this.data.highlightWords = Number(e.target.checked); - await votStorage.set("highlightWords", this.data.highlightWords); - debug/* default */.A.log( - "highlightWords value changed. New value: ", - this.data.highlightWords, + this.data.subtitlesOpacity = Number(e.target.value); + await votStorage.set( + "subtitlesOpacity", + this.data.subtitlesOpacity, ); - this.subtitlesWidget.setHighlightWords(this.data.highlightWords); + this.votSubtitlesOpacitySlider.label.querySelector( + "strong", + ).textContent = `${this.data.subtitlesOpacity}`; + this.subtitlesWidget.setOpacity(this.data.subtitlesOpacity); })(); - }, - ); + }); + + document.documentElement.appendChild(this.votSubtitlesDialog.container); + }); + + // OTHER this.votShowPiPButtonCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.showPiPButton = Number(e.target.checked); await votStorage.set("showPiPButton", this.data.showPiPButton); - debug/* default */.A.log( + utils_debug.log( "showPiPButton value changed. New value: ", this.data.showPiPButton, ); @@ -5695,9 +7528,9 @@ class VideoHandler { this.votM3u8ProxyHostTextfield.input.addEventListener("change", (e) => { (async () => { - this.data.m3u8ProxyHost = e.target.value || config/* m3u8ProxyHost */.se; + this.data.m3u8ProxyHost = e.target.value || m3u8ProxyHost; await votStorage.set("m3u8ProxyHost", this.data.m3u8ProxyHost); - debug/* default */.A.log( + utils_debug.log( "m3u8ProxyHost value changed. New value: ", this.data.m3u8ProxyHost, ); @@ -5706,13 +7539,13 @@ class VideoHandler { this.votProxyWorkerHostTextfield.input.addEventListener("change", (e) => { (async () => { - this.data.proxyWorkerHost = e.target.value || config/* proxyWorkerHost */.Pm; + this.data.proxyWorkerHost = e.target.value || proxyWorkerHost; await votStorage.set("proxyWorkerHost", this.data.proxyWorkerHost); - debug/* default */.A.log( + utils_debug.log( "proxyWorkerHost value changed. New value: ", this.data.proxyWorkerHost, ); - window.location.reload(); + this.votClient.host = this.data.proxyWorkerHost; })(); }); @@ -5720,7 +7553,7 @@ class VideoHandler { (async () => { this.data.audioProxy = Number(e.target.checked); await votStorage.set("audioProxy", this.data.audioProxy); - debug/* default */.A.log( + utils_debug.log( "audioProxy value changed. New value: ", this.data.audioProxy, ); @@ -5867,7 +7700,7 @@ class VideoHandler { const isSettings = settings.contains(e); const isTempDialog = tempDialog?.contains(e) ?? false; - debug/* default */.A.log( + utils_debug.log( `[document click] ${isButton} ${isMenu} ${isVideo} ${isSettings} ${isTempDialog}`, ); if (!(!isButton && !isMenu && !isSettings && !isTempDialog)) return; @@ -5890,6 +7723,8 @@ class VideoHandler { eContainer = document.querySelector('div[data-testid="videoPlayer"]'); } else if (this.site.host === "yandexdisk") { eContainer = document.querySelector(".video-player__player"); + } else if (this.site.host === "reddit") { + eContainer = document.querySelector("shreddit-post"); } else { eContainer = this.container; } @@ -5933,25 +7768,25 @@ class VideoHandler { this.container.style.height = "100%"; } - addExtraEventListener(this.video, "canplaythrough", async () => { + addExtraEventListener(this.video, "canplay", async () => { // Временное решение if (this.site.host === "rutube" && this.video.src) { return; } - if (getVideoId(this.site.host, this.video) === this.videoData.videoId) + if ((await getVideoID(this.site, this.video)) === this.videoData.videoId) return; await this.handleSrcChanged(); - debug/* default */.A.log("lipsync mode is loadeddata"); + utils_debug.log("lipsync mode is loadeddata"); await this.autoTranslate(); }); - addExtraEventListener(this.video, "emptied", () => { + addExtraEventListener(this.video, "emptied", async () => { if ( this.video.src && - getVideoId(this.site.host, this.video) === this.videoData.videoId + (await getVideoID(this.site, this.video)) === this.videoData.videoId ) return; - debug/* default */.A.log("lipsync mode is emptied"); + utils_debug.log("lipsync mode is emptied"); this.videoData = ""; this.stopTranslation(); }); @@ -5983,7 +7818,7 @@ class VideoHandler { } async changeSubtitlesLang(subs) { - debug/* default */.A.log("[onchange] subtitles", subs); + utils_debug.log("[onchange] subtitles", subs); this.votSubtitlesSelect.setSelected(subs); if (subs === "disabled") { this.votSubtitlesSelect.setTitle( @@ -5991,14 +7826,13 @@ class VideoHandler { ); this.subtitlesWidget.setContent(null); this.votDownloadSubtitlesButton.hidden = true; - this.YandexSubtitles = null; + this.yandexSubtitles = null; } else { - const fetchedSubs = await fetchSubtitles( + this.yandexSubtitles = await fetchSubtitles( this.subtitlesList.at(parseInt(subs)), ); - this.subtitlesWidget.setContent(fetchedSubs); + this.subtitlesWidget.setContent(this.yandexSubtitles); this.votDownloadSubtitlesButton.hidden = false; - this.YandexSubtitles = fetchedSubs; } } @@ -6055,11 +7889,20 @@ class VideoHandler { return; } - this.subtitlesList = await subtitles_getSubtitles( - this.site, - this.videoData.videoId, - this.videoData.detectedLanguage, - ); + try { + this.subtitlesList = await subtitles_getSubtitles(this.votClient, this.videoData); + } catch (err) { + utils_debug.log("Error with yandex server, try auto-fix...", err); + this.votOpts = { + fetchFn: GM_fetch, + hostVOT: votBackendUrl, + host: this.data.proxyWorkerHost, + }; + this.votClient = new VOTWorkerClient(this.votOpts); + this.subtitlesList = await subtitles_getSubtitles(this.votClient, this.videoData); + await votStorage.set("translateProxyEnabled", 1); + } + if (!this.subtitlesList) { await this.changeSubtitlesLang("disabled"); } else { @@ -6100,7 +7943,7 @@ class VideoHandler { const newSlidersVolume = Math.round(videoVolume); this.votVideoVolumeSlider.input.value = newSlidersVolume; - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = + this.votVideoVolumeSlider.label.querySelector("strong").textContent = `${newSlidersVolume}%`; ui.updateSlider(this.votVideoVolumeSlider.input); @@ -6145,7 +7988,7 @@ class VideoHandler { ); slider.input.value = finalValue; - slider.label.querySelector("strong").innerHTML = `${finalValue}%`; + slider.label.querySelector("strong").textContent = `${finalValue}%`; ui.updateSlider(slider.input); // Update the temp variables for future syncing @@ -6154,29 +7997,34 @@ class VideoHandler { this.tempVolume = fromType === "translation" ? newVolume : finalValue; } + /** + * Asynchronously retrieves video data from the current page's URL. + * If the video is hosted on YouTube, it also retrieves additional data. + * + * @return {Promise} An object containing the video's duration, URL, video ID, host, + * detected language, response language, and translation help. + */ async getVideoData() { + const { duration, url, videoId, host, translationHelp, detectedLanguage } = + await getVideoData(this.site, this.video); const videoData = { - // ! should be null for ALL websites except coursera and udemy ! - // else use direct link: `{url: xxx.mp4}` - translationHelp: null, - isStream: false, // by default, we request the translation of the video - duration: this.video?.duration || 343, // ! if 0 - we get 400 error - videoId: getVideoId(this.site.host, this.video), - detectedLanguage: this.translateFromLang, + translationHelp: translationHelp ?? null, + // by default, we request the translation of the video + isStream: false, + // ! if 0 - we get 400 error + duration: this.video?.duration || duration || config.defaultDuration, + videoId, + url, + host, + detectedLanguage: detectedLanguage ?? this.translateFromLang, responseLanguage: this.translateToLang, }; - if (!videoData.videoId) { - this.ytData = {}; - return videoData; - } - if (this.site.host === "youtube") { - this.ytData = await youtubeUtils.getVideoData(); - videoData.isStream = this.ytData.isLive; - if (this.ytData.title) { - videoData.detectedLanguage = this.ytData.detectedLanguage; - videoData.responseLanguage = this.translateToLang; + const youtubeData = await youtubeUtils.getVideoData(); + videoData.isStream = youtubeData.isLive; + if (youtubeData.title) { + videoData.detectedLanguage = youtubeData.detectedLanguage; } } else if (["rutube", "ok.ru", "mail_ru"].includes(this.site.host)) { videoData.detectedLanguage = "ru"; @@ -6185,52 +8033,8 @@ class VideoHandler { } else if (this.site.host === "vk") { const trackLang = document.getElementsByTagName("track")?.[0]?.srclang; videoData.detectedLanguage = trackLang || "auto"; - } else if (this.site.host === "coursera") { - const courseraData = await courseraUtils.getVideoData( - this.translateToLang, - ); - videoData.duration = courseraData.duration || videoData.duration; // courseraData.duration sometimes it can be equal to NaN - videoData.detectedLanguage = courseraData.detectedLanguage; - videoData.translationHelp = courseraData.translationHelp; - } else if (this.site.host === "coursehunter") { - const coursehunterData = await coursehunterUtils.getVideoData(); - videoData.translationHelp = { - // use direct link - url: coursehunterData.url, - }; - videoData.duration = coursehunterData.duration || videoData.duration; - } else if (this.site.host === "bannedvideo") { - const bannedvideoData = await bannedvideoUtils.getVideoData( - videoData.videoId, - ); - videoData.translationHelp = { - url: bannedvideoData.url, - }; - - videoData.duration = bannedvideoData.duration || videoData.duration; - videoData.isStream = bannedvideoData.live; } else if (this.site.host === "weverse") { - const weverseData = await weverseUtils.getVideoData(); videoData.detectedLanguage = "ko"; - if (weverseData) { - videoData.translationHelp = { - url: weverseData.url, - }; - videoData.duration = weverseData.duration || videoData.duration; - } - } else if (this.site.host === "udemy") { - const udemyData = await udemyUtils.getVideoData( - this.data.udemyData, - this.translateToLang, - ); - videoData.duration = udemyData.duration || videoData.duration; - videoData.detectedLanguage = udemyData.detectedLanguage; - videoData.translationHelp = udemyData.translationHelp; - } else if (this.site.host === "bitchute") { - // to avoid creating a separate file with the same functionality - videoData.translationHelp = { - url: videoData.videoId, - }; } else if ( [ "bilibili", @@ -6251,9 +8055,10 @@ class VideoHandler { } return videoData; } + videoValidator() { if (["youtube", "ok.ru", "vk"].includes(this.site.host)) { - debug/* default */.A.log("VideoValidator videoData: ", this.videoData); + utils_debug.log("VideoValidator videoData: ", this.videoData); if ( this.data.dontTranslateYourLang === 1 && this.videoData.detectedLanguage === this.data.dontTranslateLanguage @@ -6261,20 +8066,22 @@ class VideoHandler { throw new VOTLocalizedError("VOTDisableFromYourLang"); } } - // if (this.ytData.isPremiere) { - // throw new VOTLocalizedError("VOTPremiere"); - // } - // if (this.ytData.isLive) { - // throw new VOTLocalizedError("VOTLiveNotSupported"); - // } + if (!this.videoData.isStream && this.videoData.duration > 14_400) { throw new VOTLocalizedError("VOTVideoIsTooLong"); } + return true; } + /** + * Synchronizes the lip sync of the video and audio elements. + * + * @param {boolean} [mode=false] - The lip sync mode. + * @return {void} + */ lipSync(mode = false) { - debug/* default */.A.log("lipsync video", this.video); + utils_debug.log("lipsync video", this.video); if (!this.video) { return; } @@ -6282,15 +8089,15 @@ class VideoHandler { this.audio.playbackRate = this.video.playbackRate; if (!mode) { - debug/* default */.A.log("lipsync mode is not set"); + utils_debug.log("lipsync mode is not set"); return; } if (mode == "play") { - debug/* default */.A.log("lipsync mode is play"); + utils_debug.log("lipsync mode is play"); const audioPromise = this.audio.play(); if (audioPromise !== undefined) { - audioPromise.catch((e) => { + audioPromise.catch(async (e) => { console.error("[VOT]", e); if (e.name === "NotAllowedError") { this.transformBtn( @@ -6298,16 +8105,6 @@ class VideoHandler { localizationProvider.get("grantPermissionToAutoPlay"), ); throw new VOTLocalizedError("grantPermissionToAutoPlay"); - } else if (e.name === "NotSupportedError") { - this.transformBtn( - "error", - sitesChromiumBlocked.includes(window.location.hostname) - ? localizationProvider.get("neededAdditionalExtension") - : localizationProvider.get("audioFormatNotSupported"), - ); - throw sitesChromiumBlocked.includes(window.location.hostname) - ? new VOTLocalizedError("neededAdditionalExtension") - : new VOTLocalizedError("audioFormatNotSupported"); } }); } @@ -6315,19 +8112,19 @@ class VideoHandler { } // video is inactive if (["pause", "stop", "waiting"].includes(mode)) { - debug/* default */.A.log(`lipsync mode is ${mode}`); + utils_debug.log(`lipsync mode is ${mode}`); this.audio.pause(); } if (mode == "playing") { - debug/* default */.A.log("lipsync mode is playing"); + utils_debug.log("lipsync mode is playing"); this.audio.play(); } } // Define a function to handle common events handleVideoEvent(event) { - debug/* default */.A.log(`video ${event.type}`); + utils_debug.log(`video ${event.type}`); this.lipSync(event.type); } @@ -6344,7 +8141,7 @@ class VideoHandler { this.votDownloadButton.hidden = true; this.downloadTranslationUrl = null; this.transformBtn("none", localizationProvider.get("translateVideo")); - debug/* default */.A.log(`Volume on start: ${this.volumeOnStart}`); + utils_debug.log(`Volume on start: ${this.volumeOnStart}`); if (this.volumeOnStart) { this.setVideoVolume(this.volumeOnStart); } @@ -6357,8 +8154,8 @@ class VideoHandler { } async translateExecutor(VIDEO_ID) { - debug/* default */.A.log("Run translateFunc", VIDEO_ID); - this.translateFunc( + utils_debug.log("Run translateFunc", VIDEO_ID); + await this.translateFunc( VIDEO_ID, this.videoData.isStream, this.videoData.detectedLanguage, @@ -6369,18 +8166,32 @@ class VideoHandler { async updateTranslationErrorMsg(errorMessage) { const translationTake = localizationProvider.get("translationTake"); - const VOTTranslatingError = localizationProvider.get("VOTTranslatingError"); const lang = localizationProvider.lang; + if ( + [ + "Подготавливаем перевод", + "Видео передано в обработку", + "Ожидаем перевод видео", + "Загружаем переведенное аудио", + ].includes(errorMessage) + ) { + this.setLoadingBtn(true); + } + if (errorMessage?.name === "VOTLocalizedError") { this.transformBtn("error", errorMessage.localizedMessage); + } else if (errorMessage instanceof Error) { + // to prevent pass Error as text + this.transformBtn("error", errorMessage?.message); } else if ( this.data.translateAPIErrors === 1 && !errorMessage.includes(translationTake) && lang !== "ru" ) { + // adds a stub text until a text translation is received to avoid a long delay with long text + this.setLoadingBtn(true); const translatedMessage = await translate(errorMessage, "ru", lang); - this.transformBtn("error", VOTTranslatingError); this.transformBtn("error", translatedMessage); } else { this.transformBtn("error", errorMessage); @@ -6396,8 +8207,9 @@ class VideoHandler { if (this.data.autoSetVolumeYandexStyle === 1) { this.votVideoVolumeSlider.input.value = this.data.autoVolume * 100; - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = - `${this.data.autoVolume * 100}%`; + this.votVideoVolumeSlider.label.querySelector("strong").textContent = `${ + this.data.autoVolume * 100 + }%`; ui.updateSlider(this.votVideoVolumeSlider.input); } @@ -6406,26 +8218,84 @@ class VideoHandler { } // update translation audio src - updateTranslation(audioUrl) { - // ! Don't use this function for streams - this.audio.src = audioUrl; - - // cf version only - if ( - false - ) {} + async updateTranslation(audioUrl) { + //debug.log("cachedTranslation", this.cachedTranslation?.url, this.audio.currentSrc); + if (this.cachedTranslation?.url === this.audio.currentSrc) { + utils_debug.log("[translateFunc] Audio src is the same"); + this.audio.src = audioUrl; + } else { + try { + const response = await GM_fetch(audioUrl, { + method: "HEAD", + timeout: 5000, + }); + utils_debug.log("Test audio response", response); + if (response.status === 404) { + utils_debug.log("Yandex returned not valid audio, trying to fix..."); + let translateRes = await this.translateVideoImpl( + this.videoData, + (this.videoData.detectedLanguage = "auto"), + this.videoData.responseLanguage, + this.videoData.translationHelp, + ); + this.setSelectMenuValues( + this.videoData.detectedLanguage, + this.videoData.responseLanguage, + ); + audioUrl = translateRes.url; + utils_debug.log("Fixed audio audioUrl", audioUrl); + } else { + utils_debug.log("Valid audioUrl", audioUrl); + } + } catch (err) { + if (err.message === "Timeout") { + utils_debug.log("Request timed out. Handling timeout error..."); + this.data.audioProxy = 1; + await votStorage.set("audioProxy", 1); + } else { + utils_debug.log("Test audio error:", err); + } + } - this.volumeOnStart = this.getVideoVolume(); - if (typeof this.data.defaultVolume === "number") { - this.gainNode.gain.value = this.data.defaultVolume / 100; + this.audio.src = audioUrl; + try { + await this.audio.play(); + } catch (e) { + console.error("[VOT]", e); + if (e.name === "NotSupportedError") { + if ( + [...sitesInvidious, ...sitesPiped].includes( + window.location.hostname, + ) + ) { + throw new VOTLocalizedError("VOTMediaCSPError"); + } + this.data.audioProxy = 1; + await votStorage.set("audioProxy", 1); + } + } } + if ( - typeof this.data.autoSetVolumeYandexStyle === "number" && - this.data.autoSetVolumeYandexStyle + this.data.audioProxy === 1 && + audioUrl.startsWith("https://vtrans.s3-private.mds.yandex.net/tts/prod/") ) { - this.setVideoVolume(this.data.autoVolume); + const audioPath = audioUrl.replace( + "https://vtrans.s3-private.mds.yandex.net/tts/prod/", + "", + ); + audioUrl = `https://${this.data.proxyWorkerHost}/video-translation/audio-proxy/${audioPath}`; + console.log(`[VOT] Audio proxied via ${audioUrl}`); + } + + // ! Don't use this function for streams + this.audio.src = audioUrl; + + if (!this.volumeOnStart) { + this.volumeOnStart = this.getVideoVolume(); } + this.setupAudioSettings(); switch (this.site.host) { case "twitter": document @@ -6446,7 +8316,7 @@ class VideoHandler { } // Define a function to translate a video and handle the callback - translateFunc( + async translateFunc( VIDEO_ID, isStream, requestLang, @@ -6454,159 +8324,63 @@ class VideoHandler { translationHelp, ) { console.log("[VOT] Video Data: ", this.videoData); - const videoURL = translationHelp?.url - ? translationHelp.url - : `${this.site.url}${VIDEO_ID}`; - // fix enabling the old requested voiceover when changing the language to the native language (#414) - debug/* default */.A.log("Run videoValidator"); + utils_debug.log("Run videoValidator"); this.videoValidator(); + this.setLoadingBtn(true); if (isStream) { - debug/* default */.A.log("Executed stream translation"); - translateStream( - videoURL, + let translateRes = await this.translateStreamImpl( + this.videoData, requestLang, responseLang, - async (success, reqInterval, resOrError) => { - debug/* default */.A.log("[exec callback] translateStream callback"); - if (getVideoId(this.site.host, this.video) !== VIDEO_ID) return; - if (!success || !resOrError.translatedInfo) { - await this.updateTranslationErrorMsg(resOrError); - - if (reqInterval === 10) { - // if wait translating - clearTimeout(this.autoRetry); - this.autoRetry = setTimeout( - () => - this.translateFunc( - VIDEO_ID, - isStream, - requestLang, - responseLang, - translationHelp, - ), - reqInterval * 1000, - ); - } - - return; - } - - this.transformBtn( - "success", - localizationProvider.get("disableTranslate"), - ); - - console.log(resOrError); - const pingId = resOrError.pingId; - debug/* default */.A.log(`Stream pingId: ${pingId}`); - // if you don't make ping requests, then the translation of the stream dies - this.streamPing = setInterval( - async () => - await rsp(pingId, (result) => - debug/* default */.A.log("Stream ping result: ", result), - ), - reqInterval * 1000, - ); - - debug/* default */.A.log(resOrError.translatedInfo.url); - const streamURL = `https://${ - this.data.m3u8ProxyHost - }/?all=yes&origin=${encodeURIComponent( - "https://strm.yandex.ru", - )}&referer=${encodeURIComponent( - "https://strm.yandex.ru", - )}&url=${encodeURIComponent(resOrError.translatedInfo.url)}`; - debug/* default */.A.log(streamURL); - - if (this.hls) { - this.hls.on(Hls.Events.MEDIA_ATTACHED, function () { - debug/* default */.A.log("audio and hls.js are now bound together !"); - }); - this.hls.on(Hls.Events.MANIFEST_PARSED, function (data) { - debug/* default */.A.log( - "manifest loaded, found " + - data?.levels?.length + - " quality level", - ); - }); - this.hls.loadSource(streamURL); - this.hls.attachMedia(this.audio); - this.hls.on(Hls.Events.ERROR, function (data) { - if (data.fatal) { - switch (data.type) { - case Hls.ErrorTypes.MEDIA_ERROR: - console.log( - "fatal media error encountered, try to recover", - ); - this.hls.recoverMediaError(); - break; - case Hls.ErrorTypes.NETWORK_ERROR: - console.error("fatal network error encountered", data); - // All retries and media options have been exhausted. - // Immediately trying to restart loading could cause loop loading. - // Consider modifying loading policies to best fit your asset and network - // conditions (manifestLoadPolicy, playlistLoadPolicy, fragLoadPolicy). - break; - default: - // cannot recover - this.hls.destroy(); - break; - } - } - }); - debug/* default */.A.log(this.hls); - } else if (this.audio.canPlayType("application/vnd.apple.mpegurl")) { - // safari - this.audio.src = streamURL; - } else { - // browser doesn't support m3u8 (hls unsupported and it's not a safari) - throw new VOTLocalizedError("audioFormatNotSupported"); - } - - if (this.site.host === "youtube") { - youtubeUtils.videoSeek(this.video, 10); // 10 is the most successful number for streaming. With it, the audio is not so far behind the original - } + ); - this.volumeOnStart = this.getVideoVolume(); - if (typeof this.data.defaultVolume === "number") { - this.gainNode.gain.value = this.data.defaultVolume / 100; - } + if (!translateRes) { + utils_debug.log("Skip translation"); + return; + } - if ( - typeof this.data.autoSetVolumeYandexStyle === "number" && - this.data.autoSetVolumeYandexStyle - ) { - this.setVideoVolume(this.data.autoVolume); - } + this.transformBtn( + "success", + localizationProvider.get("disableTranslate"), + ); - if ( - !this.video.src && - !this.video.currentSrc && - !this.video.srcObject - ) { - this.stopTranslation(); - return; - } + const streamURL = `https://${ + this.data.m3u8ProxyHost + }/?all=yes&origin=${encodeURIComponent( + "https://strm.yandex.ru", + )}&referer=${encodeURIComponent( + "https://strm.yandex.ru", + )}&url=${encodeURIComponent(translateRes.result.url)}`; + if (this.hls) { + this.setupHLS(streamURL); + } else if (this.audio.canPlayType("application/vnd.apple.mpegurl")) { + // safari + this.audio.src = streamURL; + } else { + // browser doesn't support m3u8 (hls unsupported and it isn't a safari) + throw new VOTLocalizedError("audioFormatNotSupported"); + } - if (this.video && !this.video.paused) this.lipSync("play"); - for (const e of videoLipSyncEvents) { - this.video.addEventListener(e, this.handleVideoEventBound); - } + if (this.site.host === "youtube") { + youtubeUtils.videoSeek(this.video, 10); // 10 is the most successful number for streaming. With it, the audio is not so far behind the original + } - this.afterUpdateTranslation(streamURL); - }, - ); + this.setupAudioSettings(); + if (!this.video.src && !this.video.currentSrc && !this.video.srcObject) { + return this.stopTranslation(); + } - return; - } + if (this.video && !this.video.paused) this.lipSync("play"); + for (const e of videoLipSyncEvents) { + this.video.addEventListener(e, this.handleVideoEventBound); + } - if (["udemy", "coursera"].includes(this.site.host) && !translationHelp) { - throw new VOTLocalizedError("VOTTranslationHelpNull"); + return this.afterUpdateTranslation(streamURL); } - const cachedTranslation = this.videoTranslations.find( + this.cachedTranslation = this.videoTranslations.find( (t) => t.videoId === VIDEO_ID && t.expires > Date.now() / 1000 && @@ -6614,77 +8388,102 @@ class VideoHandler { t.to === responseLang, ); - if (cachedTranslation) { - this.updateTranslation(cachedTranslation.url); - debug/* default */.A.log("[translateFunc] A cached translate was received"); + if (this.cachedTranslation) { + await this.updateTranslation(this.cachedTranslation.url); + utils_debug.log("[translateFunc] Cached translation was received"); return; } - const timeoutDuration = this.subtitlesList.some( - (item) => item.source === "yandex", - ) - ? 20_000 - : 30_000; - - translateVideo( - videoURL, - this.videoData.duration, + let translateRes = await this.translateVideoImpl( + this.videoData, requestLang, responseLang, translationHelp, - async (success, urlOrError) => { - debug/* default */.A.log("[exec callback] translateVideo callback"); - if (getVideoId(this.site.host, this.video) !== VIDEO_ID) return; - if (!success) { - await this.updateTranslationErrorMsg(urlOrError); + ); - // if the error line contains information that the translation is being performed, then we wait - if ( - urlOrError.includes(localizationProvider.get("translationTake")) - ) { - clearTimeout(this.autoRetry); - this.autoRetry = setTimeout( - () => - this.translateFunc( - VIDEO_ID, - isStream, - requestLang, - responseLang, - translationHelp, - ), - timeoutDuration, - ); - } - console.error("[VOT]", urlOrError); - return; - } + utils_debug.log("[translateRes]", translateRes); + if (!translateRes) { + utils_debug.log("Skip translation"); + return; + } - this.updateTranslation(urlOrError); - if ( - !this.subtitlesList.some( - (item) => - item.source === "yandex" && - item.translatedFromLanguage === this.videoData.detectedLanguage && - item.language === this.videoData.responseLanguage, - ) - ) { - this.subtitlesList = await subtitles_getSubtitles( - this.site, - this.videoData.videoId, - this.videoData.detectedLanguage, - ); - await this.updateSubtitlesLangSelect(); + await this.updateTranslation(translateRes.url); + + if ( + ![ + VideoService.kick, + VideoService.reddit, + VideoService.patreon, + VideoService.kodik, + VideoService.appledeveloper, + ].includes(this.site.host) && + !this.subtitlesList.some( + (item) => + item.source === "yandex" && + item.translatedFromLanguage === this.videoData.detectedLanguage && + item.language === this.videoData.responseLanguage, + ) + ) { + this.subtitlesList = await subtitles_getSubtitles(this.votClient, this.videoData); + await this.updateSubtitlesLangSelect(); + } + + this.videoTranslations.push({ + videoId: VIDEO_ID, + from: requestLang, + to: responseLang, + url: this.downloadTranslationUrl, + expires: Date.now() / 1000 + this.videoTranslationTTL, + }); + } + + // Вспомогательные методы + setupHLS(streamURL) { + this.hls.on(Hls.Events.MEDIA_ATTACHED, function () { + utils_debug.log("audio and hls.js are now bound together !"); + }); + this.hls.on(Hls.Events.MANIFEST_PARSED, function (data) { + utils_debug.log( + "manifest loaded, found " + data?.levels?.length + " quality level", + ); + }); + this.hls.loadSource(streamURL); + this.hls.attachMedia(this.audio); + this.hls.on(Hls.Events.ERROR, function (data) { + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.MEDIA_ERROR: + console.log("fatal media error encountered, try to recover"); + this.hls.recoverMediaError(); + break; + case Hls.ErrorTypes.NETWORK_ERROR: + console.error("fatal network error encountered", data); + // All retries and media options have been exhausted. + // Immediately trying to restart loading could cause loop loading. + // Consider modifying loading policies to best fit your asset and network + // conditions (manifestLoadPolicy, playlistLoadPolicy, fragLoadPolicy). + break; + default: + // cannot recover + this.hls.destroy(); + break; } + } + }); + utils_debug.log(this.hls); + } - this.videoTranslations.push({ - videoId: VIDEO_ID, - from: requestLang, - to: responseLang, - url: urlOrError, - expires: Date.now() / 1000 + this.videoTranslationTTL, - }); - }, - ); + setupAudioSettings() { + if (typeof this.data.defaultVolume === "number") { + this.gainNode.gain.value = this.data.defaultVolume / 100; + } + + if ( + typeof this.data.autoSetVolumeYandexStyle === "number" && + this.data.autoSetVolumeYandexStyle + ) { + this.setVideoVolume(this.data.autoVolume); + } } // Define a function to stop translation and clean up @@ -6694,7 +8493,7 @@ class VideoHandler { } async handleSrcChanged() { - debug/* default */.A.log("[VideoHandler] src changed", this); + utils_debug.log("[VideoHandler] src changed", this); this.firstPlay = true; @@ -6724,7 +8523,7 @@ class VideoHandler { } async release() { - debug/* default */.A.log("[VideoHandler] release"); + utils_debug.log("[VideoHandler] release"); this.initialized = false; this.releaseExtraEvents(); @@ -6734,33 +8533,16 @@ class VideoHandler { } } -function getSites() { - const hostname = window.location.hostname; - const currentURL = new URL(window.location); - - const isMathes = (match) => { - if (match instanceof RegExp) { - return match.test(hostname); - } else if (typeof match === "string") { - return hostname.includes(match); - } else if (typeof match === "function") { - return match(currentURL); - } - return false; - }; - - return config_sites.filter((e) => { - return ( - (Array.isArray(e.match) ? e.match.some(isMathes) : isMathes(e.match)) && - e.host && - e.url - ); - }); -} - const videoObserver = new VideoObserver(); const videosWrappers = new WeakMap(); +/** + * Finds the container element for a given video element and site object. + * + * @param {Object} site - The site object. + * @param {Object} video - The video element. + * @return {Object|null} The container element or null if not found. + */ function findContainer(site, video) { if (site.shadowRoot) { let container = site.selector @@ -6771,55 +8553,40 @@ function findContainer(site, video) { return container && container.shadowRoot ? container.parentElement : container; - } else { - const browserVersion = browserInfo.browser.version.split(".")[0]; - if ( - site.selector?.includes(":not") && - site.selector?.includes("*") && - browserVersion && - ((browserInfo.browser.name === "Chrome" && Number(browserVersion) < 88) || - (browserInfo.browser.name === "Firefox" && Number(browserVersion) < 84)) - ) { - const selector = site.selector.split(" *")[0]; - return selector - ? Array.from(document.querySelectorAll(selector)).find((e) => - e.contains(video), - ) - : video.parentElement; - } else { - return site.selector - ? Array.from(document.querySelectorAll(site.selector)).find((e) => - e.contains(video), - ) - : video.parentElement; - } } + + const browserVersion = browserInfo.browser.version.split(".")[0]; + if ( + site.selector?.includes(":not") && + site.selector?.includes("*") && + browserVersion && + ((browserInfo.browser.name === "Chrome" && Number(browserVersion) < 88) || + (browserInfo.browser.name === "Firefox" && Number(browserVersion) < 84)) + ) { + const selector = site.selector.split(" *")[0]; + return selector + ? Array.from(document.querySelectorAll(selector)).find((e) => + e.contains(video), + ) + : video.parentElement; + } + + return site.selector + ? Array.from(document.querySelectorAll(site.selector)).find((e) => + e.contains(video), + ) + : video.parentElement; } async function src_main() { - debug/* default */.A.log("Loading extension..."); + utils_debug.log("Loading extension..."); await localizationProvider.update(); - debug/* default */.A.log(`Selected menu language: ${localizationProvider.lang}`); - - if ( - true && - GM_info?.scriptHandler && - cfOnlyExtensions.includes(GM_info.scriptHandler) - ) { - console.error( - `[VOT] ${localizationProvider.getDefault("unSupportedExtensionError").replace("{0}", GM_info.scriptHandler)}`, - ); - return alert( - `[VOT] ${localizationProvider.get("unSupportedExtensionError").replace("{0}", GM_info.scriptHandler)}`, - ); - } - - debug/* default */.A.log("Extension compatibility passed..."); + utils_debug.log(`Selected menu language: ${localizationProvider.lang}`); videoObserver.onVideoAdded.addListener((video) => { - for (const site of getSites()) { + for (const site of getService()) { if (!site) continue; let container = findContainer(site, video); @@ -6860,19 +8627,6 @@ src_main().catch((e) => { console.error("[VOT]", e); }); -// if (import.meta.webpackHot) { -// import.meta.webpackHot.monkeyReload(); -// import.meta.webpackHot.dispose(() => { -// for (const selector of [ -// ".vot-menu", -// ".vot-segmented-button", -// ".vot-subtitles-widget", -// ]) { -// document.querySelector(selector)?.remove(); -// } -// }); -// } - })(); /******/ })() diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..bc91c291 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,49 @@ +import js from "@eslint/js"; +import globals from "globals"; +import oxlint from "eslint-plugin-oxlint"; +import sonarjs from "eslint-plugin-sonarjs"; +import github from "eslint-plugin-github"; + +export default [ + { + ignores: ["dist/*", "wiki/*"], + }, + js.configs.recommended, + sonarjs.configs.recommended, + { + plugins: { + github: github, + }, + rules: { + "no-control-regex": 0, + "no-async-promise-executor": 0, + "sonarjs/max-switch-cases": 0, + "github/no-innerText": "error", + "github/no-inner-html": "error", + "github/no-useless-passive": "error", + "github/prefer-observers": "error", + "github/require-passive-events": "error", + // "github/unescaped-html-literal": "error", + }, + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { + ...globals.browser, + ...globals.node, + ...globals.greasemonkey, + // IMPORTED SCRIPTS + protobuf: "readonly", + Hls: "readonly", + // WEBPACK ENVIRONMENT + BUILD_MODE: "readonly", + DEBUG_MODE: "readonly", + IS_BETA_VERSION: "readonly", + __MK_GLOBAL__: "readonly", + // YOUTUBE PAGE API + ytplayer: "readonly", + }, + }, + }, + oxlint.configs["flat/recommended"], // oxlint should be the last one +]; diff --git a/integration-list.md b/integration-list.md new file mode 100644 index 00000000..847ec5e7 --- /dev/null +++ b/integration-list.md @@ -0,0 +1,46 @@ +| Статус | Действие | +| ------ | ------------------------------------------------------------------------ | +| ⠀✅ | Не дублируются запросы при смене языка | +| ⠀✅ | Отображение сообщений перевода от VOT Backend, если нет времени ожидания | +| ⠀✅ | Работа в Firefox Dev | +| ⠀✅ | Работа в Chrome | +| ⠀✅ | Работа в Arc | +| ⠀✅ | Работа в Brave | +| ⠀✅ | Работа в Opera GX | +| ⠀✅ | Работа в расширение Tampermonkey | +| ⠀✅ | Работа в расширение User Javascript and CSS | +| ⠀⚠️ | Работа в расширение OrangeMonkey | +| ⠀✅ | Работа в расширение GreaseMonkey | +| ⠀✅ | Работа в расширение FireMonkey | +| ⠀✅ | Работа в расширение VioletMonkey | +| ⠀✅ | Субтитры (везде) | +| ⠀✅ | Перевод YouTube | +| ⠀✅ | Перевод Weverse | +| ⠀✅ | Перевод Mail.ru | +| ⠀✅ | Перевод Patreon | +| ⠀✅ | Перевод Reddit | +| ⠀✅ | Перевод Kodik | +| ⠀✅ | Перевод Udemy | +| ⠀✅ | Перевод Coursehunter | +| ⠀✅ | Перевод Directlink | +| ⠀✅ | Перевод Vimeo | +| ⠀✅ | Перевод Yandex Disk | +| ⠀✅ | Перевод VK | +| ✅ | Перевод Archive.org | +| ✅ | Перевод Invidious | +| ✅ | Перевод Piped | +| ✅ | Перевод Twitch | +| ✅ | Перевод стримов на YouTube + пинг перевода | +| ✅ | Объединить уже существующий функционал | +| ✅ | Объединить конфиги | +| ✅ | Отображение ошибок из YaAPI, VOT Backend | +| ✅ | Добавить инструкцию по патчингу vot.js | +| WIP | Добавить переводы для всех ошибок из vot.js | +| ✅ | Добавить получение данных для Udemy | +| ✅ | Добавить получение данных для COURSERA | +| ✅ | Добавить получение данных для Coursehunter | +| WIP | Добавить отображение субтитров курса для Udemy | + +- проверить другие сайты + +DOCS: https://foswly.github.io/vot.js/ diff --git a/package.json b/package.json index 7ebfbe58..66b228d6 100644 --- a/package.json +++ b/package.json @@ -16,45 +16,47 @@ "voice-over-translation" ], "devDependencies": { - "ajv": "^8.16.0", - "bun-types": "^1.1.13", + "ajv": "^8.17.1", + "bun-types": "^1.1.20", "css-loader": "^7.1.2", - "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", + "eslint": "^9.7.0", + "eslint-plugin-github": "^5.0.1", + "eslint-plugin-oxlint": "^0.5.0", + "eslint-plugin-sonarjs": "^1.0.4", "eslint-webpack-plugin": "^4.2.0", - "husky": "^9.0.11", + "husky": "^9.1.1", "lint-staged": "^15.2.7", - "npm-run-all2": "^6.2.0", - "prettier": "^3.3.2", - "sass": "^1.77.6", - "sass-loader": "^14.2.1", + "npm-run-all2": "^6.2.2", + "oxlint": "0.6.1", + "patch-package": "^8.0.0", + "postinstall-postinstall": "^2.1.0", + "sass": "^1.77.8", + "sass-loader": "16.0.0", "style-loader": "^4.0.0", "tslib": "^2.6.3", - "webpack": "^5.92.0", + "webpack": "^5.93.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4", "webpack-monkey": "^0.2.1" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build:cloudflare": "webpack build -c webpack.config.js --env build_mode=cloudflare", - "build:default": "webpack build -c webpack.config.js", - "build:cloudflare-min": "webpack build -c webpack.config.js --env build_mode=cloudflare --env build_type=minify", - "build:default-min": "webpack build -c webpack.config.js --env build_type=minify", - "build": "run-p build:default build:cloudflare", - "build:min": "run-p build:default-min build:cloudflare-min", + "test": "webpack build -c .webpack/config.test.js", + "build:default": "webpack build -c .webpack/webpack.config.js", + "build:default-min": "webpack build -c .webpack/webpack.config.js --env build_type=minify", + "build": "run-p build:default", + "build:min": "run-p build:default-min", "build:all": "run-p build build:min", + "build:bun": "bun run build:all", "dev": "webpack serve --node-env development", - "dev:cloudflare": "webpack serve --node-env development --env build_mode=cloudflare", - "lint": "npx eslint .", - "lint-fix": "npx eslint . --fix", - "format": "prettier --write --ignore-unknown \"src/**/*.{html,css,scss,cjs,js,ts,json}\"", + "lint": "npx oxlint --ignore-path=.oxlintignore && npx eslint", "prepare": "husky", - "gen:wiki": "bun run ./wiki/gen-sites.js" + "gen:wiki": "bun run ./wiki/gen-sites.js", + "postinstall": "patch-package" }, "dependencies": { "bowser": "^2.11.0", - "requestidlecallback-polyfill": "^1.0.2" + "lit": "^3.1.4", + "requestidlecallback-polyfill": "^1.0.2", + "vot.js": "^0.7.2" } } diff --git a/patches/vot.js+0.7.2+001+vot-localized-errors.patch b/patches/vot.js+0.7.2+001+vot-localized-errors.patch new file mode 100644 index 00000000..7c3474fe --- /dev/null +++ b/patches/vot.js+0.7.2+001+vot-localized-errors.patch @@ -0,0 +1,50 @@ +diff --git a/node_modules/vot.js/dist/client.js b/node_modules/vot.js/dist/client.js +index 5d7421c..ffa6115 100644 +--- a/node_modules/vot.js/dist/client.js ++++ b/node_modules/vot.js/dist/client.js +@@ -7,6 +7,7 @@ import { fetchWithTimeout, getTimestamp } from "./utils/utils.js"; + import { getVideoData } from "./utils/videoData.js"; + import { convertVOT } from "./utils/vot.js"; + import { StreamInterval } from "./protos/yandex.js"; ++import { VOTLocalizedError } from "../../../src/utils/VOTLocalizedError.js"; + const { version } = packageInfo; + class VOTJSError extends Error { + data; +@@ -148,16 +149,16 @@ export default class VOTClient { + ...headers, + }); + if (!res.success) { +- throw new VOTJSError("Failed to request video translation", res); ++ throw new VOTLocalizedError("requestTranslationFailed"); + } + const translationData = yandexProtobuf.decodeTranslationResponse(res.data); + switch (translationData.status) { + case VideoTranslationStatus.FAILED: +- throw new VOTJSError("Yandex couldn't translate video", translationData); ++ throw translationData?.message ? new VOTJSError("Yandex couldn't translate video", translationData) : new VOTLocalizedError("requestTranslationFailed"); + case VideoTranslationStatus.FINISHED: + case VideoTranslationStatus.PART_CONTENT: + if (!translationData.url) { +- throw new VOTJSError("Audio link wasn't received from Yandex response", translationData); ++ throw new VOTLocalizedError("audioNotReceived"); + } + return { + translated: true, +@@ -191,7 +192,7 @@ export default class VOTClient { + rawVideo: url, + }, headers); + if (!res.success) { +- throw new VOTJSError("Failed to request video translation", res); ++ throw new VOTLocalizedError("requestTranslationFailed", res); + } + const translationData = res.data; + switch (translationData.status) { +@@ -199,7 +200,7 @@ export default class VOTClient { + throw new VOTJSError("Yandex couldn't translate video", translationData); + case "success": + if (!translationData.translatedUrl) { +- throw new VOTJSError("Audio link wasn't received from VOT response", translationData); ++ throw new VOTLocalizedError("audioNotReceived"); + } + return { + translated: true, diff --git a/patches/vot.js+0.7.2+002+sites-selectors.patch b/patches/vot.js+0.7.2+002+sites-selectors.patch new file mode 100644 index 00000000..1b5083a7 --- /dev/null +++ b/patches/vot.js+0.7.2+002+sites-selectors.patch @@ -0,0 +1,324 @@ +diff --git a/node_modules/vot.js/dist/config/sites.js b/node_modules/vot.js/dist/config/sites.js +index 99d23b1..8843c66 100644 +--- a/node_modules/vot.js/dist/config/sites.js ++++ b/node_modules/vot.js/dist/config/sites.js +@@ -2,29 +2,63 @@ import { VideoService } from "../types/yandex.js"; + import { sitesInvidious, sitesPiped, sitesProxiTok, sitesPeertube, } from "./alternativeUrls.js"; + export default [ + { ++ additionalData: "mobile", + host: VideoService.youtube, + url: "https://youtu.be/", +- match: /^((www.|m.)?youtube(-nocookie|kids)?.com)|(youtu.be)$/, ++ match: /^m.youtube.com$/, ++ selector: "shorts-video #player", ++ }, ++ { ++ additionalData: "mobile", ++ host: VideoService.youtube, ++ url: "https://youtu.be/", ++ match: /^m.youtube.com$/, ++ selector: ".player-container", ++ }, ++ { ++ host: VideoService.youtube, ++ url: "https://youtu.be/", ++ match: /^(www.)?youtube(-nocookie|kids)?.com$/, ++ selector: ".html5-video-container:not(#inline-player *)", + }, + { + host: VideoService.invidious, + url: "https://youtu.be/", + match: sitesInvidious, ++ selector: "#player", + }, + { + host: VideoService.piped, + url: "https://youtu.be/", + match: sitesPiped, ++ selector: ".shaka-video-container", ++ }, ++ { ++ additionalData: "mobile", ++ host: VideoService.vk, ++ url: "https://vk.com/video?z=", ++ match: /^m.vk.(com|ru)$/, ++ selector: "vk-video-player", ++ shadowRoot: true, ++ }, ++ { ++ additionalData: "clips", ++ host: VideoService.vk, ++ url: "https://vk.com/video?z=", ++ match: /^(www.|m.)?vk.(com|ru)$/, ++ selector: 'div[data-testid="clipcontainer-video"]', + }, + { + host: VideoService.vk, + url: "https://vk.com/video?z=", + match: /^(www.|m.)?vk.(com|ru)$/, ++ selector: ".videoplayer_media", + }, + { + host: VideoService.nine_gag, + url: "https://9gag.com/gag/", + match: /^9gag.com$/, ++ selector: ".video-post", + }, + { + host: VideoService.twitch, +@@ -35,162 +69,249 @@ export default [ + /^clips.twitch.tv$/, + /^player.twitch.tv$/, + ], ++ selector: ".video-ref, main > div > section > div > div > div", + }, + { + host: VideoService.proxitok, + url: "https://www.tiktok.com/", + match: sitesProxiTok, ++ selector: ".column.has-text-centered", + }, + { + host: VideoService.tiktok, + url: "https://www.tiktok.com/", + match: /^(www.)?tiktok.com$/, ++ selector: null, + }, + { + host: VideoService.vimeo, + url: "https://vimeo.com/", + match: /^vimeo.com$/, ++ selector: ".player", + }, + { + host: VideoService.vimeo, + url: "https://player.vimeo.com/", + match: /^player.vimeo.com$/, ++ selector: ".player", + }, + { + host: VideoService.xvideos, + url: "https://www.xvideos.com/", + match: /^(www.)?(xvideos|xv-ru).com$/, ++ selector: ".video-bg-pic", + }, + { + host: VideoService.pornhub, + url: "https://rt.pornhub.com/view_video.php?viewkey=", + match: /^[a-z]+.pornhub.com$/, ++ selector: ".mainPlayerDiv > .video-element-wrapper-js > div", ++ }, ++ { ++ additionalData: "embed", ++ host: VideoService.pornhub, ++ url: "https://rt.pornhub.com/view_video.php?viewkey=", ++ match: (url) => ++ url.host.includes("pornhub.com") && url.pathname.startsWith("/embed/"), ++ selector: "#player", + }, + { + host: VideoService.twitter, + url: "https://twitter.com/i/status/", + match: /^(twitter|x).com$/, ++ selector: 'div[data-testid="videoComponent"] > div:nth-child(1) > div', + }, + { + host: VideoService.rumble, + url: "https://rumble.com/", + match: /^rumble.com$/, ++ selector: "#videoPlayer > .videoPlayer-Rumble-cls > div", + }, + { + host: VideoService.facebook, + url: "https://facebook.com/", +- match: (url) => url.host.includes("facebook.com") && +- (url.pathname.includes("/videos/") || url.pathname.includes("/reel/")), ++ match: (url) => ++ url.host.includes("facebook.com") && url.pathname.includes("/videos/"), ++ selector: 'div[role="main"] div[data-pagelet$="video" i]', + }, + { ++ additionalData: "reels", ++ host: VideoService.facebook, ++ url: "https://facebook.com/", ++ match: (url) => ++ url.host.includes("facebook.com") && url.pathname.includes("/reel/"), ++ selector: 'div[role="main"]', ++ }, ++ { ++ host: VideoService.rutube, ++ url: "https://rutube.ru/video/", ++ match: /^rutube.ru$/, ++ selector: ".video-player > div > div > div:nth-child(2)", ++ }, ++ { ++ additionalData: "embed", + host: VideoService.rutube, + url: "https://rutube.ru/video/", + match: /^rutube.ru$/, ++ selector: "#app > div > div", + }, + { + host: VideoService.bilibili, + url: "https://www.bilibili.com/video/", + match: /^(www|m|player).bilibili.com$/, ++ selector: ".bpx-player-video-wrap", ++ }, ++ // Добавляет лишние видео в обработчик ++ { ++ additionalData: "old", // /blackboard/webplayer/embed-old.html ++ host: VideoService.bilibili, ++ url: "https://www.bilibili.com/video/", ++ match: /^(www|m).bilibili.com$/, ++ selector: null, + }, + { + host: VideoService.mailru, + url: "https://my.mail.ru/", + match: /^my.mail.ru$/, ++ selector: "#b-video-wrapper", + }, + { + host: VideoService.bitchute, + url: "https://www.bitchute.com/video/", + match: /^(www.)?bitchute.com$/, ++ selector: ".video-js", ++ }, ++ { ++ // ONLY IF YOU LOGINED TO COURSERA /learn/NAME/lecture/XXXX ++ host: VideoService.coursera, ++ url: "https://www.coursera.org/", ++ match: /coursera.org$/, ++ selector: ".vjs-v6", ++ needExtraData: true, ++ }, ++ { ++ // ONLY IF YOU LOGINED TO UDEMY /course/NAME/learn/lecture/XXXX ++ host: VideoService.udemy, ++ url: "https://www.udemy.com/", ++ match: /udemy.com$/, ++ selector: ++ 'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)', ++ needExtraData: true, + }, + { + host: VideoService.eporner, + url: "https://www.eporner.com/", + match: /^(www.)?eporner.com$/, ++ selector: ".vjs-v7", + }, + { + host: VideoService.peertube, + url: "stub", + match: sitesPeertube, ++ selector: ".vjs-v7", + }, + { + host: VideoService.dailymotion, + url: "https://dai.ly/", +- match: /^(www.)?dailymotion.com|dai.ly$/, ++ match: /^geo.dailymotion.com$/, ++ selector: ".player", + }, + { + host: VideoService.trovo, + url: "https://trovo.live/s/", + match: /^trovo.live$/, ++ selector: ".player-video", + }, + { + host: VideoService.yandexdisk, + url: "https://yadi.sk/i/", +- match: /^disk.yandex.ru|yadi.sk$/, ++ match: /^disk.yandex.ru$/, ++ selector: ".video-player__player > div:nth-child(1)", ++ }, ++ { ++ host: VideoService.coursehunter, ++ url: "https://coursehunter.net/course/", ++ match: /^coursehunter.net$/, ++ selector: "#oframeplayer > pjsdiv:nth-of-type(1)", + }, + { + host: VideoService.okru, + url: "https://ok.ru/video/", + match: /^ok.ru$/, ++ selector: ".html5-vpl_vid", + }, + { + host: VideoService.googledrive, + url: "https://drive.google.com/file/d/", +- match: /^drive.google.com$/, ++ match: /^youtube.googleapis.com$/, ++ selector: ".html5-video-container", + }, + { + host: VideoService.bannedvideo, + url: "https://madmaxworld.tv/watch?id=", + match: /^(www.)?banned.video|madmaxworld.tv$/, ++ selector: ".vjs-v7", + needExtraData: true, + }, + { + host: VideoService.weverse, + url: "https://weverse.io/", + match: /^weverse.io$/, ++ selector: ".webplayer-internal-source-wrapper", + needExtraData: true, + }, + { + host: VideoService.newgrounds, + url: "https://www.newgrounds.com/", + match: /^(www.)?newgrounds.com$/, ++ selector: ".ng-video-player", + }, + { + host: VideoService.egghead, + url: "https://egghead.io/", + match: /^egghead.io$/, ++ selector: ".cueplayer-react-video-holder", + }, + { + host: VideoService.youku, + url: "https://v.youku.com/", + match: /^v.youku.com$/, ++ selector: "#ykPlayer", + }, + { + host: VideoService.archive, + url: "https://archive.org/details/", + match: /^archive.org$/, ++ selector: ".jw-media", + }, + { + host: VideoService.kodik, + url: "stub", + match: /^kodik.(info|biz|cc)$/, ++ selector: ".fp-player", + needExtraData: true, + }, + { + host: VideoService.patreon, + url: "stub", + match: /^(www.)?patreon.com$/, ++ selector: ++ 'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div', + needExtraData: true, + }, + { + host: VideoService.reddit, + url: "stub", + match: /^(www.)?reddit.com$/, ++ selector: "shreddit-player", ++ shadowRoot: true, + needExtraData: true, + }, + { + host: VideoService.kick, + url: "https://kick.com/", + match: /^kick.com$/, ++ selector: ".vjs-v8", + needExtraData: true, + }, + { diff --git a/patches/vot.js+0.7.2+003+change-imports.patch b/patches/vot.js+0.7.2+003+change-imports.patch new file mode 100644 index 00000000..15412337 --- /dev/null +++ b/patches/vot.js+0.7.2+003+change-imports.patch @@ -0,0 +1,29 @@ +diff --git a/node_modules/vot.js/dist/protos/yandex.d.ts b/node_modules/vot.js/dist/protos/yandex.d.ts +index 683e776..57ed99f 100644 +--- a/node_modules/vot.js/dist/protos/yandex.d.ts ++++ b/node_modules/vot.js/dist/protos/yandex.d.ts +@@ -1,4 +1,4 @@ +-import _m0 from "protobufjs/minimal.js"; ++export type _m0 = typeof protobuf; + export declare const protobufPackage = ""; + export declare enum StreamInterval { + NO_CONNECTION = 0, +diff --git a/node_modules/vot.js/dist/protos/yandex.js b/node_modules/vot.js/dist/protos/yandex.js +index a05784f..3cc49f6 100644 +--- a/node_modules/vot.js/dist/protos/yandex.js ++++ b/node_modules/vot.js/dist/protos/yandex.js +@@ -1,4 +1,4 @@ +-import _m0 from "protobufjs/minimal.js"; ++const _m0 = protobuf; + export const protobufPackage = ""; + export var StreamInterval; + (function (StreamInterval) { +diff --git a/node_modules/vot.js/dist/secure.js b/node_modules/vot.js/dist/secure.js +index 531d1ba..6f2ea63 100644 +--- a/node_modules/vot.js/dist/secure.js ++++ b/node_modules/vot.js/dist/secure.js +@@ -1,4 +1,3 @@ +-import crypto from "node:crypto"; + import config from "./config/config.js"; + const utf8Encoder = new TextEncoder(); + async function signHMAC(hashName, hmac, data) { diff --git a/patches/vot.js+0.7.2+004+some-fixes.patch b/patches/vot.js+0.7.2+004+some-fixes.patch new file mode 100644 index 00000000..55fa4c6a --- /dev/null +++ b/patches/vot.js+0.7.2+004+some-fixes.patch @@ -0,0 +1,115 @@ +diff --git a/node_modules/vot.js/dist/types/yandex.d.ts b/node_modules/vot.js/dist/types/yandex.d.ts +index 5d80f07..e729cfa 100644 +--- a/node_modules/vot.js/dist/types/yandex.d.ts ++++ b/node_modules/vot.js/dist/types/yandex.d.ts +@@ -33,11 +33,14 @@ export declare enum VideoService { + mail_ru = "mailru", + mailru = "mailru", + bitchute = "bitchute", ++ coursera = "coursera", ++ udemy = "udemy", + eporner = "eporner", + peertube = "peertube", + dailymotion = "dailymotion", + trovo = "trovo", + yandexdisk = "yandexdisk", ++ coursehunter = "coursehunter", + ok_ru = "okru", + okru = "okru", + googledrive = "googledrive", +diff --git a/node_modules/vot.js/dist/types/yandex.js b/node_modules/vot.js/dist/types/yandex.js +index 59d35c3..bf4fe61 100644 +--- a/node_modules/vot.js/dist/types/yandex.js ++++ b/node_modules/vot.js/dist/types/yandex.js +@@ -23,11 +23,14 @@ export var VideoService; + VideoService["mail_ru"] = "mailru"; + VideoService["mailru"] = "mailru"; + VideoService["bitchute"] = "bitchute"; ++ VideoService["coursera"] = "coursera"; ++ VideoService["udemy"] = "udemy"; + VideoService["eporner"] = "eporner"; + VideoService["peertube"] = "peertube"; + VideoService["dailymotion"] = "dailymotion"; + VideoService["trovo"] = "trovo"; + VideoService["yandexdisk"] = "yandexdisk"; ++ VideoService["coursehunter"] = "coursehunter"; + VideoService["ok_ru"] = "okru"; + VideoService["okru"] = "okru"; + VideoService["googledrive"] = "googledrive"; +diff --git a/node_modules/vot.js/dist/utils/helper.js b/node_modules/vot.js/dist/utils/helper.js +index 8c73ca9..2bb4ff2 100644 +--- a/node_modules/vot.js/dist/utils/helper.js ++++ b/node_modules/vot.js/dist/utils/helper.js +@@ -1,4 +1,3 @@ +-import { parseFromString } from "dom-parser"; + import { getHmacSha1 } from "../secure.js"; + import sites from "../config/sites.js"; + import { fetchWithTimeout } from "./utils.js"; +@@ -157,7 +156,7 @@ export class WeverseHelper { + } + } + export class KodikHelper { +- API_ORIGIN = "https://kodik.biz"; ++ API_ORIGIN = window.location.origin; + async getSecureData(videoPath) { + try { + const url = this.API_ORIGIN + videoPath; +@@ -170,7 +169,8 @@ export class KodikHelper { + }); + const content = await res.text(); + const [videoType, videoId, hash] = videoPath.split("/").filter((a) => a); +- const doc = parseFromString(content); ++ const parser = new DOMParser(); ++ const doc = parser.parseFromString(content, "text/html"); + const secureScript = Array.from(doc.getElementsByTagName("script")).filter((s) => s.innerHTML.includes(`videoId = "${videoId}"`)); + if (!secureScript.length) { + throw new VideoHelperError("Failed to find secure script"); +@@ -276,11 +276,10 @@ export class PatreonHelper { + } + } + export class RedditHelper { +- async getVideoData(videoId) { +- const res = await fetchWithTimeout(`https://www.reddit.com/r/${videoId}`); +- const content = await res.text(); +- const contentUrl = /https:\/\/v\.redd\.it\/([^/]+)\/HLSPlaylist\.m3u8\?([^"]+)/ +- .exec(content)?.[0] ++ async getVideoData() { ++ const contentUrl = document ++ .querySelector("source[type='application/vnd.apple.mpegURL']") ++ ?.src + ?.replaceAll("&", "&"); + if (!contentUrl) { + return undefined; +diff --git a/node_modules/vot.js/dist/utils/videoData.js b/node_modules/vot.js/dist/utils/videoData.js +index cedfa97..9ab1c62 100644 +--- a/node_modules/vot.js/dist/utils/videoData.js ++++ b/node_modules/vot.js/dist/utils/videoData.js +@@ -150,14 +150,25 @@ export async function getVideoID(service, videoURL) { + } + case VideoService.bitchute: + return /(video|embed)\/([^/]+)/.exec(url.pathname)?.[2]; ++ case VideoService.coursera: ++ return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <-- COURSE PASSING (IF YOU LOGINED TO COURSERA) + case VideoService.eporner: + return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.peertube: + return /\/w\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.dailymotion: { +- return url.hostname === "dai.ly" +- ? url.pathname.slice(1) +- : /video\/([^/]+)/.exec(url.pathname)?.[1]; ++ // we work in the context of the player ++ // geo.dailymotion.com ++ const plainPlayerConfig = Array.from( ++ document.querySelectorAll("*"), ++ ).filter((s) => s.innerHTML.trim().includes(".m3u8")); ++ try { ++ let videoUrl = plainPlayerConfig[1].lastChild.src; ++ return /\/video\/(\w+)\.m3u8/.exec(videoUrl)?.[1]; ++ } catch (e) { ++ console.error("[VOT]", e); ++ return false; ++ } + } + case VideoService.trovo: { + const vid = url.searchParams.get("vid"); diff --git a/patches/vot.js+0.7.2+005+improve-videodata-logic.patch b/patches/vot.js+0.7.2+005+improve-videodata-logic.patch new file mode 100644 index 00000000..76a99954 --- /dev/null +++ b/patches/vot.js+0.7.2+005+improve-videodata-logic.patch @@ -0,0 +1,96 @@ +diff --git a/node_modules/vot.js/dist/client.js b/node_modules/vot.js/dist/client.js +index ffa6115..903b604 100644 +--- a/node_modules/vot.js/dist/client.js ++++ b/node_modules/vot.js/dist/client.js +@@ -237,7 +237,7 @@ export default class VOTClient { + async getSubtitles({ videoData, requestLang = this.requestLang, headers = {}, }) { + const { url } = videoData; + if (this.isCustomFormat(url)) { +- throw new VOTJSError("Unsupported video URL for getting subtitles"); ++ throw new VOTLocalizedError("Unsupported video URL for getting subtitles"); // add translation + } + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeSubtitlesRequest(url, requestLang); +@@ -271,7 +271,7 @@ export default class VOTClient { + async translateStream({ videoData, requestLang = this.requestLang, responseLang = this.responseLang, headers = {}, }) { + const { url } = videoData; + if (this.isCustomFormat(url)) { +- throw new VOTJSError("Unsupported video URL for getting stream translation"); ++ throw new VOTLocalizedError("Unsupported video URL for getting stream translation"); // add translation + } + const { secretKey, uuid } = await this.getSession("video-translation"); + const body = yandexProtobuf.encodeStreamRequest(url, requestLang, responseLang); +diff --git a/node_modules/vot.js/dist/utils/videoData.js b/node_modules/vot.js/dist/utils/videoData.js +index 9ab1c62..2532b39 100644 +--- a/node_modules/vot.js/dist/utils/videoData.js ++++ b/node_modules/vot.js/dist/utils/videoData.js +@@ -9,18 +9,11 @@ class VideoDataError extends Error { + this.message = message; + } + } +-export function getService(videoUrl) { +- if (/(file:\/\/|(http(s)?:\/\/)(127\.0\.0\.1|localhost))/.exec(videoUrl)) +- return false; +- let enteredURL; +- try { +- enteredURL = new URL(videoUrl); +- } +- catch (e) { +- console.error(`Invalid URL: ${videoUrl}. Have you forgotten https?`); +- return false; +- } +- const hostname = enteredURL.hostname; ++export function getService() { ++ if (/(http(s)?:\/\/)(127\.0\.0\.1|localhost)/.exec(window.location.href)) ++ return []; ++ const hostname = window.location.hostname; ++ const enteredURL = new URL(window.location); + const isMathes = (match) => { + if (match instanceof RegExp) { + return match.test(hostname); +@@ -33,14 +26,14 @@ export function getService(videoUrl) { + } + return false; + }; +- return sites.find((e) => { ++ return sites.filter((e) => { + return ((Array.isArray(e.match) ? e.match.some(isMathes) : isMathes(e.match)) && + e.host && + e.url); + }); + } +-export async function getVideoID(service, videoURL) { +- const url = new URL(videoURL); ++export async function getVideoID(service, video) { ++ const url = new URL(window.location.href); + switch (service.host) { + case VideoService.custom: + return url.href; +@@ -67,6 +60,12 @@ export async function getVideoID(service, videoURL) { + else if (paramOID && paramID) { + return `video-${Math.abs(parseInt(paramOID))}_${paramID}`; + } ++ ++ const videoBox = video.parentElement?.closest(".video_box_wrap"); ++ if (videoBox) { ++ return videoBox.id.replace("video_box_wrap", "video"); ++ } ++ + return null; + } + case VideoService.nine_gag: +@@ -223,12 +222,8 @@ export async function getVideoID(service, videoURL) { + return undefined; + } + } +-export async function getVideoData(url) { +- const service = getService(url); +- if (!service) { +- throw new VideoDataError(`URL: "${url}" is unknown service`); +- } +- const videoId = await getVideoID(service, url); ++export async function getVideoData(service, video) { ++ const videoId = await getVideoID(service, video); + if (!videoId) { + throw new VideoDataError(`Entered unsupported link: "${url}"`); + } diff --git a/patches/vot.js+0.7.2+006+fix-udemy.patch b/patches/vot.js+0.7.2+006+fix-udemy.patch new file mode 100644 index 00000000..b9ca51f4 --- /dev/null +++ b/patches/vot.js+0.7.2+006+fix-udemy.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/vot.js/dist/utils/videoData.js b/node_modules/vot.js/dist/utils/videoData.js +index 2532b39..7c1acae 100644 +--- a/node_modules/vot.js/dist/utils/videoData.js ++++ b/node_modules/vot.js/dist/utils/videoData.js +@@ -151,6 +151,8 @@ export async function getVideoID(service, video) { + return /(video|embed)\/([^/]+)/.exec(url.pathname)?.[2]; + case VideoService.coursera: + return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <-- COURSE PASSING (IF YOU LOGINED TO COURSERA) ++ case VideoService.udemy: ++ return url.pathname.slice(1); + case VideoService.eporner: + return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; + case VideoService.peertube: +@@ -225,7 +227,7 @@ export async function getVideoID(service, video) { + export async function getVideoData(service, video) { + const videoId = await getVideoID(service, video); + if (!videoId) { +- throw new VideoDataError(`Entered unsupported link: "${url}"`); ++ throw new VideoDataError(`Entered unsupported link: "${service.host}"`); + } + if (service.host === VideoService.peertube) { + service.url = new URL(url).origin; diff --git a/patches/vot.js+0.7.2+007+added-udemy-helper.patch b/patches/vot.js+0.7.2+007+added-udemy-helper.patch new file mode 100644 index 00000000..edfed690 --- /dev/null +++ b/patches/vot.js+0.7.2+007+added-udemy-helper.patch @@ -0,0 +1,259 @@ +diff --git a/node_modules/vot.js/dist/types/helpers/udemy.d.ts b/node_modules/vot.js/dist/types/helpers/udemy.d.ts +new file mode 100644 +index 0000000..1be4a49 +--- /dev/null ++++ b/node_modules/vot.js/dist/types/helpers/udemy.d.ts +@@ -0,0 +1,51 @@ ++import { ISODate } from "../utils.js"; ++ ++export type Caption = { ++ _class: "caption"; ++ asset_id: number; ++ created: ISODate; ++ file_name: string; ++ id: number; ++ locale_id: string; ++ source: string; ++ status: number; ++ title: string; ++ url: string; ++ video_label: string; ++}; ++ ++export type MediaSource = { ++ label: string; ++ src: string; ++ type: string; ++}; ++ ++export type Asset = { ++ _class: "asset"; ++ captions: Caption[]; ++ id: number; ++ length: number; ++ media_sources: MediaSource[]; ++} ++ ++export type Lecture = { ++ _class: "lecture"; ++ asset: Asset[]; ++ description: string; ++ id: number; ++ title: string; ++} ++ ++export type Locale = { ++ _class: "locale"; ++ english_title: string; ++ locale: string; ++ simple_english_title: string ++​​ title: string; ++} ++ ++export type Course = { ++ _class: "course"; ++ id: number; ++ locale: Locale; ++} +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/types/helpers/udemy.js b/node_modules/vot.js/dist/types/helpers/udemy.js +new file mode 100644 +index 0000000..693da49 +--- /dev/null ++++ b/node_modules/vot.js/dist/types/helpers/udemy.js +@@ -0,0 +1 @@ ++export {} +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/utils/helper.d.ts b/node_modules/vot.js/dist/utils/helper.d.ts +index c2f9f80..0b4416c 100644 +--- a/node_modules/vot.js/dist/utils/helper.d.ts ++++ b/node_modules/vot.js/dist/utils/helper.d.ts +@@ -4,7 +4,8 @@ import * as Kodik from "../types/helpers/kodik.js"; + import * as Patreon from "../types/helpers/patreon.js"; + import * as BannedVideo from "../types/helpers/bannedvideo.js"; + import * as Kick from "../types/helpers/kick.js"; +-import { VideoService } from "../types/yandex.js"; ++import * as Udemy from "../types/helpers/udemy.js"; ++import { VideoService, RequestLang, TranslationHelp } from "../types/yandex.js"; + export declare class MailRuHelper { + getVideoData(videoId: string): Promise; + } +@@ -80,6 +81,24 @@ export declare class KickHelper { + title: string; + }>; + } ++export declare class UdemyHelper { ++ getModuleData(): { ++ courseId?: number; ++ } ++ getLectureId(): null | string; ++ getLectureData(courseId: number|string, lectureId: number|string): Promise; ++ getCourseLang(courseId: number|string): Promise; ++ findVideoUrl(sources: Udemy.MediaSource[]): string|null; ++ findSubtitleUrl(captions: Udemy.Caption[], detectedLanguage: RequestLang): string|null; ++ getVideoData(videoId: string): Promise; ++} + export declare class AppleDeveloperHelper { + getVideoData(videoId: string): Promise<{ + url: string; +@@ -93,6 +112,7 @@ export default class VideoHelper { + static [VideoService.reddit]: RedditHelper; + static [VideoService.bannedvideo]: BannedVideoHelper; + static [VideoService.kick]: KickHelper; ++ static [VideoService.udemy]: UdemyHelper; + static [VideoService.appledeveloper]: AppleDeveloperHelper; + } + //# sourceMappingURL=helper.d.ts.map +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/utils/helper.js b/node_modules/vot.js/dist/utils/helper.js +index 96234ed..a6dd08e 100644 +--- a/node_modules/vot.js/dist/utils/helper.js ++++ b/node_modules/vot.js/dist/utils/helper.js +@@ -1,8 +1,12 @@ + import { getHmacSha1 } from "../secure.js"; + import sites from "../config/sites.js"; + import { fetchWithTimeout } from "./utils.js"; ++import { availableLangs } from "../consts"; + import config from "../config/config.js"; + import { VideoService } from "../types/yandex.js"; ++import { localizationProvider } from "../../../../src/localization/localizationProvider.js"; ++import debug from "../../../../src/utils/debug.js"; ++import { GM_fetch, langTo6391 } from "../../../../src/utils/utils.js"; + class VideoHelperError extends Error { + constructor(message) { + super(message); +@@ -367,6 +371,120 @@ export class KickHelper { + }; + } + } ++export class UdemyHelper { ++ API_ORIGIN = "https://www.udemy.com/api-2.0"; ++ ++ getModuleData() { ++ const moduleArgs = document.querySelector( ++ ".ud-app-loader[data-module-id='course-taking']", ++ )?.dataset?.moduleArgs; ++ if (!moduleArgs) { ++ console.error(localizationProvider.get("udemyModuleArgsNotFound")); ++ return {}; ++ } ++ return JSON.parse(moduleArgs); ++ } ++ ++ getLectureId() { ++ return /learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1]; ++ } ++ ++ async getLectureData(courseId, lectureId) { ++ const res = await GM_fetch( ++ `${this.API_ORIGIN}/users/me/subscribed-courses/${courseId}/lectures/${lectureId}/?` + ++ new URLSearchParams({ ++ "fields[lecture]": "title,description,asset", ++ "fields[asset]": "length,media_sources,captions", ++ }) ++ ); ++ return await res.json(); ++ } ++ ++ async getCourseLang(courseId) { ++ const res = await GM_fetch( ++ `${this.API_ORIGIN}/users/me/subscribed-courses/${courseId}?` + ++ new URLSearchParams({ ++ "fields[course]": "locale", ++ }) ++ ); ++ return await res.json(); ++ } ++ ++ findVideoUrl(sources) { ++ return sources?.find((src) => src.type === "video/mp4")?.src; ++ } ++ ++ findSubtitleUrl(captions, detectedLanguage) { ++ let subtitle = captions?.find( ++ (caption) => langTo6391(caption.locale_id) === detectedLanguage, ++ ); ++ ++ if (!subtitle) { ++ subtitle = captions?.find( ++ (caption) => langTo6391(caption.locale_id) === "en", ++ ) ?? captions?.[0]; ++ } ++ ++ return subtitle?.url; ++ } ++ ++ async getVideoData(videoId) { ++ const { courseId } = this.getModuleData(); ++ if (!courseId) { ++ return false; ++ } ++ ++ const lectureId = this.getLectureId(); ++ debug.log(`[Udemy] courseId: ${courseId}, lectureId: ${lectureId}`) ++ if (!lectureId) { ++ return false; ++ } ++ ++ const { title, description, asset } = await this.getLectureData(courseId, lectureId); ++ const { length: duration, media_sources, captions } = asset; ++ ++ const videoUrl = this.findVideoUrl(media_sources); ++ if (!videoUrl) { ++ console.log("Failed to find .mp4 video file in media_sources", media_sources); ++ return false; ++ } ++ ++ const courseLangData = await this.getCourseLang(courseId); ++ let { locale: { locale } } = courseLangData; ++ locale = locale ? langTo6391(locale) : "en"; ++ if (!availableLangs.includes(locale)) { ++ locale = "en"; ++ } ++ ++ const subtitleUrl = this.findSubtitleUrl(captions, locale); ++ if (!subtitleUrl) { ++ console.log("Failed to find subtitle file in captions", captions) ++ } ++ ++ return { ++ ...subtitleUrl ? { ++ url: sites.find((s) => s.host === VideoService.udemy).url + videoId, ++ translationHelp: [ ++ { ++ target: "subtitles_file_url", ++ targetUrl: subtitleUrl, ++ }, ++ { ++ target: "video_file_url", ++ targetUrl: videoUrl, ++ }, ++ ], ++ detectedLanguage: locale, ++ } : { ++ url: videoUrl, ++ translationHelp: null, ++ }, ++ duration, ++ title, ++ description, ++ }; ++ } ++} + export class AppleDeveloperHelper { + async getVideoData(videoId) { + const res = await fetchWithTimeout(`https://developer.apple.com/${videoId}`); +@@ -388,5 +506,6 @@ export default class VideoHelper { + static [VideoService.reddit] = new RedditHelper(); + static [VideoService.bannedvideo] = new BannedVideoHelper(); + static [VideoService.kick] = new KickHelper(); ++ static [VideoService.udemy] = new UdemyHelper(); + static [VideoService.appledeveloper] = new AppleDeveloperHelper(); + } diff --git a/patches/vot.js+0.7.2+008+added-coursehunter-helper.patch b/patches/vot.js+0.7.2+008+added-coursehunter-helper.patch new file mode 100644 index 00000000..23b068ab --- /dev/null +++ b/patches/vot.js+0.7.2+008+added-coursehunter-helper.patch @@ -0,0 +1,118 @@ +diff --git a/node_modules/vot.js/dist/types/helpers/coursehunter.d.ts b/node_modules/vot.js/dist/types/helpers/coursehunter.d.ts +new file mode 100644 +index 0000000..9f08d77 +--- /dev/null ++++ b/node_modules/vot.js/dist/types/helpers/coursehunter.d.ts +@@ -0,0 +1,7 @@ ++export type Lesson = { ++ duration: number; ++ file: string; ++ id: string; ++ subtitle: string; ++ title: string; ++} +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/types/helpers/coursehunter.js b/node_modules/vot.js/dist/types/helpers/coursehunter.js +new file mode 100644 +index 0000000..693da49 +--- /dev/null ++++ b/node_modules/vot.js/dist/types/helpers/coursehunter.js +@@ -0,0 +1 @@ ++export {} +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/utils/helper.d.ts b/node_modules/vot.js/dist/utils/helper.d.ts +index 0b4416c..64f4eab 100644 +--- a/node_modules/vot.js/dist/utils/helper.d.ts ++++ b/node_modules/vot.js/dist/utils/helper.d.ts +@@ -5,6 +5,7 @@ import * as Patreon from "../types/helpers/patreon.js"; + import * as BannedVideo from "../types/helpers/bannedvideo.js"; + import * as Kick from "../types/helpers/kick.js"; + import * as Udemy from "../types/helpers/udemy.js"; ++import * as Coursehunter from "../types/helpers/coursehunter.js"; + import { VideoService, RequestLang, TranslationHelp } from "../types/yandex.js"; + export declare class MailRuHelper { + getVideoData(videoId: string): Promise; +@@ -99,6 +100,14 @@ export declare class UdemyHelper { + detectedLanguage?: RequestLang + }>; + } ++export declare class CoursehunterHelper { ++ getLessonsData(courseId: number|string): Promise; ++ getVideoData(): Promise; ++} + export declare class AppleDeveloperHelper { + getVideoData(videoId: string): Promise<{ + url: string; +@@ -113,6 +122,7 @@ export default class VideoHelper { + static [VideoService.bannedvideo]: BannedVideoHelper; + static [VideoService.kick]: KickHelper; + static [VideoService.udemy]: UdemyHelper; ++ static [VideoService.coursehunter]: CoursehunterHelper; + static [VideoService.appledeveloper]: AppleDeveloperHelper; + } + //# sourceMappingURL=helper.d.ts.map +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/utils/helper.js b/node_modules/vot.js/dist/utils/helper.js +index a6dd08e..0939c0a 100644 +--- a/node_modules/vot.js/dist/utils/helper.js ++++ b/node_modules/vot.js/dist/utils/helper.js +@@ -485,6 +485,33 @@ export class UdemyHelper { + }; + } + } ++export class CoursehunterHelper { ++ API_ORIGIN = "https://coursehunter.net/api/v1"; ++ ++ async getLessonsData(courseId) { ++ const response = await GM_fetch( ++ `${this.API_ORIGIN}/course/${courseId}/lessons`, ++ ); ++ return await response.json(); ++ } ++ ++ async getVideoData() { ++ const courseId = window.course_id ?? +document.querySelector('input[name="course_id"]')?.value; ++ const lessonsData = window.lessons ?? (await this.getLessonsData(courseId)); ++ const lessonId = +document.querySelector(".lessons-item_active")?.dataset?.index || 1; ++ const currentLesson = lessonsData?.[lessonId - 1]; ++ const { file: videoUrl, duration, title } = currentLesson; ++ if (!videoUrl) { ++ return false; ++ } ++ ++ return { ++ url: videoUrl, ++ duration, ++ title ++ }; ++ } ++} + export class AppleDeveloperHelper { + async getVideoData(videoId) { + const res = await fetchWithTimeout(`https://developer.apple.com/${videoId}`); +@@ -507,5 +534,6 @@ export default class VideoHelper { + static [VideoService.bannedvideo] = new BannedVideoHelper(); + static [VideoService.kick] = new KickHelper(); + static [VideoService.udemy] = new UdemyHelper(); ++ static [VideoService.coursehunter] = new CoursehunterHelper(); + static [VideoService.appledeveloper] = new AppleDeveloperHelper(); + } +diff --git a/node_modules/vot.js/dist/utils/videoData.js b/node_modules/vot.js/dist/utils/videoData.js +index aec4c51..c676760 100644 +--- a/node_modules/vot.js/dist/utils/videoData.js ++++ b/node_modules/vot.js/dist/utils/videoData.js +@@ -184,6 +184,10 @@ export async function getVideoID(service, video) { + } + case VideoService.yandexdisk: + return /\/i\/([^/]+)/.exec(url.pathname)?.[1]; ++ case VideoService.coursehunter: { ++ const courseId = /\/course\/([^/]+)/.exec(url.pathname)?.[1]; ++ return courseId ? courseId + url.search : false; ++ } + case VideoService.okru: { + return /\/video\/(\d+)/.exec(url.pathname)?.[1]; + } diff --git a/patches/vot.js+0.7.2+009+added-coursera-helper.patch b/patches/vot.js+0.7.2+009+added-coursera-helper.patch new file mode 100644 index 00000000..4145f41c --- /dev/null +++ b/patches/vot.js+0.7.2+009+added-coursera-helper.patch @@ -0,0 +1,268 @@ +diff --git a/node_modules/vot.js/dist/config/sites.js b/node_modules/vot.js/dist/config/sites.js +index c1a178c..29bfeeb 100644 +--- a/node_modules/vot.js/dist/config/sites.js ++++ b/node_modules/vot.js/dist/config/sites.js +@@ -233,6 +233,7 @@ export default [ + url: "https://coursehunter.net/course/", + match: /^coursehunter.net$/, + selector: "#oframeplayer > pjsdiv:nth-of-type(1)", ++ needExtraData: true, + }, + { + host: VideoService.okru, +diff --git a/node_modules/vot.js/dist/types/helpers/coursera.d.ts b/node_modules/vot.js/dist/types/helpers/coursera.d.ts +new file mode 100644 +index 0000000..2803fc9 +--- /dev/null ++++ b/node_modules/vot.js/dist/types/helpers/coursera.d.ts +@@ -0,0 +1,52 @@ ++export type Course = { ++ categoryIds: unknown[]; ++ courseStatus: string; ++ description: string; ++ enrollableSiteUserRoles: unknown[]; ++ estimatedWorkload: string; ++ id: string; ++ instructorIds: string[]; ++ isReal: boolean; ++ isRestrictedMembership: boolean; ++ isSubtitleTranslationEnabled: boolean; ++ isVerificationEnabled: boolean; ++ launchedAt: number; ++ name: string; ++ partnerIds: string[]; ++ previewUserIds: unknown[]; ++ primaryLanguageCodes: string[]; ++ promoPhoto: string; ++ slug: string; ++ subtitleLanguageCodes: string[]; ++ verificationEnabledAt: number; ++} ++ ++// not fully typed ++export type Cache = { ++ duration: number; ++} ++ ++export type Source = { ++ src: string; ++ type: string; ++} ++ ++export type Track = { ++ kind: "captions"; ++ label: string; ++ src: string; ++ srclang: string; ++} ++ ++// not fully typed ++export type Options = { ++ courseId: string; ++ sources: Source[]; ++ tracks: Track[] ++} ++ ++// not fully typed ++export type PlayerData = { ++ cache_: Cache; ++ options_: Options; ++} +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/types/helpers/coursera.js b/node_modules/vot.js/dist/types/helpers/coursera.js +new file mode 100644 +index 0000000..693da49 +--- /dev/null ++++ b/node_modules/vot.js/dist/types/helpers/coursera.js +@@ -0,0 +1 @@ ++export {} +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/utils/helper.d.ts b/node_modules/vot.js/dist/utils/helper.d.ts +index 64f4eab..67e5edf 100644 +--- a/node_modules/vot.js/dist/utils/helper.d.ts ++++ b/node_modules/vot.js/dist/utils/helper.d.ts +@@ -6,6 +6,7 @@ import * as BannedVideo from "../types/helpers/bannedvideo.js"; + import * as Kick from "../types/helpers/kick.js"; + import * as Udemy from "../types/helpers/udemy.js"; + import * as Coursehunter from "../types/helpers/coursehunter.js"; ++import * as Coursera from "../types/helpers/coursera.js"; + import { VideoService, RequestLang, TranslationHelp } from "../types/yandex.js"; + export declare class MailRuHelper { + getVideoData(videoId: string): Promise; +@@ -97,7 +98,7 @@ export declare class UdemyHelper { + title: string; + description: string; + translationHelp: TranslationHelp[] | null; +- detectedLanguage?: RequestLang ++ detectedLanguage?: RequestLang; + }>; + } + export declare class CoursehunterHelper { +@@ -108,6 +109,19 @@ export declare class CoursehunterHelper { + title: string; + }>; + } ++export declare class CourseraHelper { ++ getCourseData(courseId: number|string): Promise; ++ getPlayer(): HTMLDivElement | null; ++ getPlayerData(): Coursera.PlayerData; ++ findVideoUrl(sources: Coursera.Source[]): string|null; ++ findSubtitleUrl(captions: Coursera.Track[], detectedLanguage: RequestLang): string|null; ++ getVideoData(videoId: string): Promise; ++} + export declare class AppleDeveloperHelper { + getVideoData(videoId: string): Promise<{ + url: string; +@@ -123,6 +137,7 @@ export default class VideoHelper { + static [VideoService.kick]: KickHelper; + static [VideoService.udemy]: UdemyHelper; + static [VideoService.coursehunter]: CoursehunterHelper; ++ static [VideoService.coursera]: CourseraHelper; + static [VideoService.appledeveloper]: AppleDeveloperHelper; + } + //# sourceMappingURL=helper.d.ts.map +\ No newline at end of file +diff --git a/node_modules/vot.js/dist/utils/helper.js b/node_modules/vot.js/dist/utils/helper.js +index 0939c0a..cb1fda5 100644 +--- a/node_modules/vot.js/dist/utils/helper.js ++++ b/node_modules/vot.js/dist/utils/helper.js +@@ -1,7 +1,7 @@ + import { getHmacSha1 } from "../secure.js"; + import sites from "../config/sites.js"; + import { fetchWithTimeout } from "./utils.js"; +-import { availableLangs } from "../consts"; ++import { availableLangs } from "../consts.js"; + import config from "../config/config.js"; + import { VideoService } from "../types/yandex.js"; + import { localizationProvider } from "../../../../src/localization/localizationProvider.js"; +@@ -450,13 +450,13 @@ export class UdemyHelper { + } + + const courseLangData = await this.getCourseLang(courseId); +- let { locale: { locale } } = courseLangData; +- locale = locale ? langTo6391(locale) : "en"; +- if (!availableLangs.includes(locale)) { +- locale = "en"; ++ let { locale: { locale: courseLang } } = courseLangData; ++ courseLang = courseLang ? langTo6391(courseLang) : "en"; ++ if (!availableLangs.includes(courseLang)) { ++ courseLang = "en"; + } + +- const subtitleUrl = this.findSubtitleUrl(captions, locale); ++ const subtitleUrl = this.findSubtitleUrl(captions, courseLang); + if (!subtitleUrl) { + console.log("Failed to find subtitle file in captions", captions) + } +@@ -474,7 +474,7 @@ export class UdemyHelper { + targetUrl: videoUrl, + }, + ], +- detectedLanguage: locale, ++ detectedLanguage: courseLang, + } : { + url: videoUrl, + translationHelp: null, +@@ -525,6 +525,90 @@ export class AppleDeveloperHelper { + }; + } + } ++export class CourseraHelper { ++ API_ORIGIN = "https://www.coursera.org/api"; ++ ++ async getCourseData(courseId) { ++ const response = await GM_fetch( ++ `${this.API_ORIGIN}/onDemandCourses.v1/${courseId}`, ++ ); ++ const resJSON = await response.json(); ++ return resJSON?.elements?.[0]; ++ } ++ ++ getPlayer() { ++ return document.querySelector(".vjs-v6"); ++ } ++ ++ getPlayerData() { ++ return this.getPlayer()?.player; ++ } ++ ++ findVideoUrl(sources) { ++ return sources?.find((src) => src.type === "video/mp4")?.src; ++ } ++ ++ findSubtitleUrl(captions, detectedLanguage) { ++ let subtitle = captions?.find( ++ (caption) => langTo6391(caption.srclang) === detectedLanguage, ++ ); ++ ++ if (!subtitle) { ++ subtitle = captions?.find( ++ (caption) => langTo6391(caption.srclang) === "en", ++ ) || captions?.[0]; ++ } ++ ++ return subtitle?.src; ++ } ++ ++ async getVideoData(videoId) { ++ const data = this.getPlayerData(); ++ ++ const { duration } = data?.cache_ || {}; ++ const { courseId, tracks, sources } = data?.options_ || {}; ++ ++ const videoUrl = this.findVideoUrl(sources); ++ if (!videoUrl) { ++ console.log("Failed to find .mp4 video file in sources", sources); ++ return false; ++ } ++ ++ const { primaryLanguageCodes } = await this.getCourseData(courseId); ++ let courseLang = primaryLanguageCodes?.[0]; ++ courseLang = courseLang ? langTo6391(courseLang) : "en"; ++ ++ if (!availableLangs.includes(courseLang)) { ++ courseLang = "en"; ++ } ++ ++ const subtitleUrl = this.findSubtitleUrl(tracks, courseLang); ++ if (!subtitleUrl) { ++ console.log("Failed to find subtitle file in tracks", tracks) ++ } ++ ++ return { ++ ...subtitleUrl ? { ++ url: sites.find((s) => s.host === VideoService.coursera).url + videoId, ++ translationHelp: [ ++ { ++ target: "subtitles_file_url", ++ targetUrl: subtitleUrl, ++ }, ++ { ++ target: "video_file_url", ++ targetUrl: videoUrl, ++ }, ++ ], ++ detectedLanguage: courseLang, ++ } : { ++ url: videoUrl, ++ translationHelp: null, ++ }, ++ duration, ++ }; ++ } ++} + export default class VideoHelper { + static [VideoService.mailru] = new MailRuHelper(); + static [VideoService.weverse] = new WeverseHelper(); +@@ -535,5 +619,6 @@ export default class VideoHelper { + static [VideoService.kick] = new KickHelper(); + static [VideoService.udemy] = new UdemyHelper(); + static [VideoService.coursehunter] = new CoursehunterHelper(); ++ static [VideoService.coursera] = new CourseraHelper(); + static [VideoService.appledeveloper] = new AppleDeveloperHelper(); + } diff --git a/patches/vot.js+0.7.2+010+updated-twitch-clips-method.patch b/patches/vot.js+0.7.2+010+updated-twitch-clips-method.patch new file mode 100644 index 00000000..fe50786b --- /dev/null +++ b/patches/vot.js+0.7.2+010+updated-twitch-clips-method.patch @@ -0,0 +1,59 @@ +diff --git a/node_modules/vot.js/dist/utils/videoData.js b/node_modules/vot.js/dist/utils/videoData.js +index eaa6086..8cbae24 100644 +--- a/node_modules/vot.js/dist/utils/videoData.js ++++ b/node_modules/vot.js/dist/utils/videoData.js +@@ -1,7 +1,6 @@ + import sites from "../config/sites.js"; + import { VideoService } from "../types/yandex.js"; + import VideoHelper from "./helper.js"; +-import { fetchWithTimeout } from "./utils.js"; + class VideoDataError extends Error { + constructor(message) { + super(message); +@@ -81,19 +80,37 @@ export async function getVideoID(service, video) { + return `videos/${url.searchParams.get("video")}`; + } + else if (isClipsDomain) { ++ const schema = document.querySelector( ++ "script[type='application/ld+json']", ++ ); + const pathname = url.pathname.slice(1); ++ if (schema) { ++ const schemaJSON = JSON.parse(schema.innerText); ++ const channelLink = schemaJSON["@graph"].find( ++ (obj) => obj["@type"] === "VideoObject", ++ )?.creator.url; ++ ++ const channelName = channelLink.replace("https://www.twitch.tv/", ""); ++ return `${channelName}/clip/${pathname}`; ++ } ++ + const isEmbed = pathname === "embed"; +- const res = await fetchWithTimeout(`https://clips.twitch.tv/${isEmbed ? url.searchParams.get("clip") : url.pathname.slice(1)}`, { +- headers: { +- "User-Agent": "Googlebot/2.1 (+http://www.googlebot.com/bot.html)", +- }, +- }); +- const content = await res.text(); +- const channelLink = /"url":"https:\/\/www\.twitch\.tv\/([^"]+)"/.exec(content); ++ const channelLink = document.querySelector( ++ isEmbed ++ ? ".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']" ++ : ".clips-player a:not([class])", ++ ); ++ + if (!channelLink) { +- return null; ++ return; + } +- return `${channelLink[1]}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; ++ ++ const channelName = channelLink.href.replace( ++ "https://www.twitch.tv/", ++ "", ++ ); ++ ++ return `${channelName}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; + } + else if (clipPath) { + return clipPath[0]; diff --git a/patches/vot.js+0.7.2+011+added-apple-developer.patch b/patches/vot.js+0.7.2+011+added-apple-developer.patch new file mode 100644 index 00000000..cb029504 --- /dev/null +++ b/patches/vot.js+0.7.2+011+added-apple-developer.patch @@ -0,0 +1,27 @@ +diff --git a/node_modules/vot.js/dist/config/sites.js b/node_modules/vot.js/dist/config/sites.js +index 29bfeeb..fde00aa 100644 +--- a/node_modules/vot.js/dist/config/sites.js ++++ b/node_modules/vot.js/dist/config/sites.js +@@ -319,6 +319,7 @@ export default [ + host: VideoService.appledeveloper, + url: "https://developer.apple.com/", + match: /^developer.apple.com$/, ++ selector: ".developer-video-player", + needExtraData: true, + }, + { +diff --git a/node_modules/vot.js/dist/utils/helper.js b/node_modules/vot.js/dist/utils/helper.js +index cb1fda5..2ae535b 100644 +--- a/node_modules/vot.js/dist/utils/helper.js ++++ b/node_modules/vot.js/dist/utils/helper.js +@@ -514,9 +514,7 @@ export class CoursehunterHelper { + } + export class AppleDeveloperHelper { + async getVideoData(videoId) { +- const res = await fetchWithTimeout(`https://developer.apple.com/${videoId}`); +- const content = await res.text(); +- const contentUrl = /https:\/\/devstreaming-cdn\.apple\.com\/videos\/([^.]+)\/(cmaf\.m3u8)/.exec(content)?.[0]; ++ const contentUrl = document.querySelector("meta[property='og:video']")?.content + if (!contentUrl) { + return undefined; + } diff --git a/patches/vot.js+0.7.2+012+fix-kick-clips.patch b/patches/vot.js+0.7.2+012+fix-kick-clips.patch new file mode 100644 index 00000000..27bbd913 --- /dev/null +++ b/patches/vot.js+0.7.2+012+fix-kick-clips.patch @@ -0,0 +1,19 @@ +diff --git a/node_modules/vot.js/dist/utils/videoData.js b/node_modules/vot.js/dist/utils/videoData.js +index 55365ac..3c16dd8 100644 +--- a/node_modules/vot.js/dist/utils/videoData.js ++++ b/node_modules/vot.js/dist/utils/videoData.js +@@ -239,7 +239,13 @@ export async function getVideoID(service, video) { + if (videoId) { + return videoId; + } +- return url.searchParams.get("clip"); ++ const clipId = url.searchParams.get("clip"); ++ if (clipId) { ++ return clipId; ++ } ++ ++ const player = document.getElementById("clip-video-player"); ++ return /clip_([^/]+)/.exec(player?.getAttribute("poster"))?.[0] + } + case VideoService.appledeveloper: { + return /videos\/play\/([^/]+)\/([\d]+)/.exec(url.pathname)?.[0]; diff --git a/src/config/alternativeUrls.js b/src/config/alternativeUrls.js deleted file mode 100644 index 9a2d270e..00000000 --- a/src/config/alternativeUrls.js +++ /dev/null @@ -1,81 +0,0 @@ -// Sites host Invidious. I tested the performance only on invidious.kevin.rocks, youtu.be and inv.vern.cc -const sitesInvidious = [ - "invidious.snopyta.org", - "yewtu.be", - "invidious.kavin.rocks", - "vid.puffyan.us", - "invidious.namazso.eu", - "inv.riverside.rocks", - "yt.artemislena.eu", - "invidious.flokinet.to", - "invidious.esmailelbob.xyz", - "y.com.sb", - "invidious.nerdvpn.de", - "inv.vern.cc", - "invidious.slipfox.xyz", - "invidio.xamh.de", - "invidious.dhusch.de", -]; - -// Sites host Piped. I tested the performance only on piped.video -const sitesPiped = [ - "piped.video", - "piped.tokhmi.xyz", - "piped.moomoo.me", - "piped.syncpundit.io", - "piped.mha.fi", - "watch.whatever.social", - "piped.garudalinux.org", - "efy.piped.pages.dev", - "watch.leptons.xyz", - "piped.lunar.icu", - "yt.dc09.ru", - "piped.mint.lgbt", - "il.ax", - "piped.privacy.com.de", - "piped.esmailelbob.xyz", - "piped.projectsegfau.lt", - "piped.in.projectsegfau.lt", - "piped.us.projectsegfau.lt", - "piped.privacydev.net", - "piped.palveluntarjoaja.eu", - "piped.smnz.de", - "piped.adminforge.de", - "piped.qdi.fi", - "piped.hostux.net", - "piped.chauvet.pro", - "piped.jotoma.de", - "piped.pfcd.me", - "piped.frontendfriendly.xyz", -]; - -const sitesProxiTok = [ - "proxitok.pabloferreiro.es", - "proxitok.pussthecat.org", - "tok.habedieeh.re", - "proxitok.esmailelbob.xyz", - "proxitok.privacydev.net", - "tok.artemislena.eu", - "tok.adminforge.de", - "tik.hostux.net", // maybe instance doesn't working - "tt.vern.cc", - "cringe.whatever.social", - "proxitok.lunar.icu", - "proxitok.privacy.com.de", // maybe instance doesn't working -]; - -// Sites host Peertube. I tested the performance only on dalek.zone and tube.shanti.cafe -const sitesPeertube = [ - "peertube.1312.media", - "tube.shanti.cafe", - "bee-tube.fr", - "video.sadmin.io", - "dalek.zone", - "review.peertube.biz", - "peervideo.club", - "tube.la-dina.net", - "peertube.tmp.rcp.tf", - "peertube.su", -]; - -export { sitesInvidious, sitesPiped, sitesProxiTok, sitesPeertube }; diff --git a/src/config/config.js b/src/config/config.js index df571eed..fdde008b 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -2,9 +2,8 @@ const workerHost = "api.browser.yandex.ru"; const m3u8ProxyHost = "m3u8-proxy.toil.cc"; // used for streaming const proxyWorkerHost = "vot-worker.toil.cc"; // used for cloudflare version -const yandexHmacKey = "bt8xH3VOlb4mqf0nqAibnDOoiPlXsisf"; -const yandexUserAgent = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 YaBrowser/24.4.0.0 Safari/537.36"; +const votBackendUrl = "https://vot-api.toil.cc/v1"; + const defaultAutoVolume = 0.15; // 0.0 - 1.0 (0% - 100%) - default volume of the video with the translation const maxAudioVolume = 900; const defaultTranslationService = "yandex"; @@ -25,11 +24,10 @@ export { m3u8ProxyHost, proxyWorkerHost, detectUrls, + votBackendUrl, translateUrls, defaultTranslationService, defaultDetectService, - yandexHmacKey, - yandexUserAgent, defaultAutoVolume, maxAudioVolume, }; diff --git a/src/config/constants.js b/src/config/constants.js deleted file mode 100644 index befe46f2..00000000 --- a/src/config/constants.js +++ /dev/null @@ -1,28 +0,0 @@ -// available languages for translation -const availableLangs = [ - "ru", - "en", - "zh", - "ko", - "lt", - "lv", - "ar", - "fr", - "it", - "es", - "de", - "ja", -]; - -// up-to-date list of TTS working languages -const actualTTS = ["ru", "en", "kk"]; - -const cfOnlyExtensions = [ - "Violentmonkey", - "FireMonkey", - "Greasemonkey", - "AdGuard", - "OrangeMonkey", -]; - -export { availableLangs, actualTTS, cfOnlyExtensions }; diff --git a/src/config/sites.js b/src/config/sites.js deleted file mode 100644 index 8851769b..00000000 --- a/src/config/sites.js +++ /dev/null @@ -1,329 +0,0 @@ -import { - sitesInvidious, - sitesPiped, - sitesProxiTok, - sitesPeertube, -} from "./alternativeUrls.js"; - -const sites = () => { - return [ - { - additionalData: "mobile", - host: "youtube", - url: "https://youtu.be/", - match: /^m.youtube.com$/, - selector: "shorts-video #player", - }, - { - additionalData: "mobile", - host: "youtube", - url: "https://youtu.be/", - match: /^m.youtube.com$/, - selector: ".player-container", - }, - { - host: "youtube", - url: "https://youtu.be/", - match: /^(www.)?youtube(-nocookie|kids)?.com$/, - selector: ".html5-video-container:not(#inline-player *)", - }, - { - host: "tiktok", - url: "https://www.tiktok.com/", - match: /^(www.)?tiktok.com$/, - selector: null, - }, - { - host: "proxitok", - url: "https://www.tiktok.com/", - match: sitesProxiTok, - selector: ".column.has-text-centered", - }, - { - host: "twitch", - url: "https://twitch.tv/", - match: [ - /^m.twitch.tv$/, - /^www.twitch.tv$/, - /^clips.twitch.tv$/, - /^player.twitch.tv$/, - ], - selector: ".video-ref, main > div > section > div > div > div", - }, - { - host: "xvideos", - url: "https://www.xvideos.com/", - match: /^www.(xvideos|xv-ru).com$/, - selector: ".video-bg-pic", - }, - { - host: "pornhub", - url: "https://rt.pornhub.com/view_video.php?viewkey=", - match: /^[a-z]+.pornhub.com$/, - selector: ".mainPlayerDiv > .video-element-wrapper-js > div", - }, - { - additionalData: "embed", - host: "pornhub", - url: "https://rt.pornhub.com/view_video.php?viewkey=", - match: (url) => - url.host.includes("pornhub.com") && url.pathname.startsWith("/embed/"), - selector: "#player", - }, - { - additionalData: "mobile", - host: "vk", - url: "https://vk.com/video?z=", - match: /^m.vk.(com|ru)$/, - selector: "vk-video-player", - shadowRoot: true, - }, - { - additionalData: "clips", - host: "vk", - url: "https://vk.com/video?z=", - match: /^(www.|m.)?vk.(com|ru)$/, - selector: 'div[data-testid="clipcontainer-video"]', - }, - { - host: "vk", - url: "https://vk.com/video?z=", - match: /^(www.|m.)?vk.(com|ru)$/, - selector: ".videoplayer_media", - }, - { - host: "vimeo", - url: "https://vimeo.com/", - match: /^vimeo.com$/, - selector: ".player", - }, - { - additionalData: "embed", - host: "vimeo", - url: "https://player.vimeo.com/", - match: /^player.vimeo.com$/, - selector: ".player", - }, - { - host: "ok.ru", - url: "https://ok.ru/video/", - match: /^ok.ru$/, - selector: ".html5-vpl_vid", - }, - { - host: "nine_gag", - url: "https://9gag.com/gag/", - match: /^9gag.com$/, - selector: ".video-post", - }, - // { - // host: "coub", - // url: "https://coub.com/view/", - // match: /^coub.com$/, - // selector: ".viewer__player", - // }, - { - host: "bitchute", - url: "https://www.bitchute.com/video/", - match: /^(www.)?bitchute.com$/, - selector: ".video-js", - }, - { - host: "rutube", - url: "https://rutube.ru/video/", - match: /^rutube.ru$/, - selector: ".video-player > div > div > div:nth-child(2)", - }, - { - additionalData: "embed", - host: "rutube", - url: "https://rutube.ru/video/", - match: /^rutube.ru$/, - selector: "#app > div > div", - }, - { - host: "bilibili", - url: "https://www.bilibili.com/video/", - match: /^(www|m|player).bilibili.com$/, - selector: ".bpx-player-video-wrap", - }, - // Добавляет лишние видео в обработчик - { - additionalData: "old", // /blackboard/webplayer/embed-old.html - host: "bilibili", - url: "https://www.bilibili.com/video/", - match: /^(www|m).bilibili.com$/, - selector: null, - }, - { - host: "twitter", - url: "https://twitter.com/i/status/", - match: /^twitter.com$/, - selector: 'div[data-testid="videoComponent"] > div:nth-child(1) > div', - }, - { - host: "mail_ru", - url: "https://my.mail.ru/", - match: /^my.mail.ru$/, - selector: "#b-video-wrapper", - }, - { - // ONLY IF YOU LOGINED TO COURSERA /learn/NAME/lecture/XXXX - host: "coursera", - url: "https://www.coursera.org/", - match: /coursera.org$/, - selector: ".vjs-v6", - }, - { - // ONLY IF YOU LOGINED TO UDEMY /course/NAME/learn/lecture/XXXX - host: "udemy", - url: "https://www.udemy.com/", - match: /udemy.com$/, - selector: - 'div[data-purpose="curriculum-item-viewer-content"] > section > div > div > div > div:nth-of-type(2)', - }, - { - // Sites host Invidious. I tested the performance only on invidious.kevin.rocks, youtu.be and inv.vern.cc - host: "invidious", - url: "https://youtu.be/", - match: sitesInvidious, - selector: "#player", - }, - { - // Sites host Piped. I tested the performance only on piped.video - host: "piped", - url: "https://youtu.be/", - match: sitesPiped, - selector: ".shaka-video-container", - }, - { - host: "rumble", - url: "https://rumble.com/", - match: /^rumble.com$/, - selector: "#videoPlayer > .videoPlayer-Rumble-cls > div", - }, - { - host: "eporner", - url: "https://www.eporner.com/", - match: /^(www.)?eporner.com$/, - selector: ".vjs-v7", - }, - { - host: "peertube", - url: "stub", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: sitesPeertube, - selector: ".vjs-v7", - }, - { - host: "dailymotion", - url: "https://dai.ly/", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: /^geo.dailymotion.com$/, - selector: ".player", - }, - { - host: "trovo", - url: "https://trovo.live/s/", - match: /^trovo.live$/, - selector: ".player-video", - }, - { - host: "yandexdisk", - url: "https://yadi.sk/i/", - match: /^disk.yandex.ru$/, - selector: ".video-player__player > div:nth-child(1)", - }, - { - host: "coursehunter", - url: "https://coursehunter.net/course/", - match: /^coursehunter.net$/, - selector: "#oframeplayer > pjsdiv:nth-of-type(1)", - }, - { - host: "googledrive", - url: "https://drive.google.com/file/d/", - match: /^youtube.googleapis.com$/, - selector: ".html5-video-container", - }, - { - host: "bannedvideo", - url: "https://banned.video/watch?id=", - match: /^(www.)?banned.video$/, - selector: ".vjs-v7", - }, - { - host: "facebook", - url: "https://facebook.com/", - match: (url) => - url.host.includes("facebook.com") && url.pathname.includes("/videos/"), - selector: 'div[role="main"] div[data-pagelet$="video" i]', - }, - { - additionalData: "reels", - host: "facebook", - url: "https://facebook.com/", - match: (url) => - url.host.includes("facebook.com") && url.pathname.includes("/reel/"), - selector: 'div[role="main"]', - }, - { - host: "weverse", - url: "https://weverse.io/", - match: /^weverse.io$/, - selector: ".webplayer-internal-source-wrapper", - }, - { - host: "newgrounds", - url: "https://www.newgrounds.com/", - match: /^www.newgrounds.com$/, - selector: ".ng-video-player", - }, - { - // TODO: Добавить поддержку tips (сделать через m3u8 т.к. обычная ссылка не принимается) и платных курсов - host: "egghead", - url: "https://egghead.io/", - match: /^egghead.io$/, - selector: ".cueplayer-react-video-holder", - }, - { - host: "youku", - url: "https://v.youku.com/", - match: /^v.youku.com$/, - selector: "#ykPlayer", - }, - { - host: "archive", - url: "https://archive.org/details/", - match: /^archive.org$/, - selector: ".jw-media", - }, - { - host: "directlink", - url: "stub", // This is a stub. The present value is set using window.location.origin. Check "src/index.js:videoObserver.onVideoAdded.addListener" to get more info - match: (url) => /([^.]+).mp4/.test(url.pathname), - selector: null, - }, - // пока рано - // { - // host: "patreon", - // url: "https://www.patreon.com/", - // match: /^www.patreon.com$/, - // selector: - // 'div[data-tag="post-card"] div[elevation="subtle"] > div > div > div > div', - // }, - // { - // host: "sibnet", - // url: "https://video.sibnet.ru/", - // match: /^video.sibnet.ru$/, - // selector: ".video-js", // #video_html5_wrapper - // }, - // Нужно куда-то заливать данные о плейлисте - // { - // host: "epicgames", - // url: "https://dev.epicgames.com/community/learning/tutorials/", - // match: /^dev.epicgames.com$/, - // selector: "#vjs_video_3", - // }, - ]; -}; - -export default sites(); diff --git a/src/getSignature.js b/src/getSignature.js deleted file mode 100644 index cedf1f08..00000000 --- a/src/getSignature.js +++ /dev/null @@ -1,21 +0,0 @@ -import { yandexHmacKey } from "./config/config.js"; - -// Create a key from the HMAC secret -const CryptoKey = window.crypto.subtle.importKey( - "raw", - new TextEncoder().encode(yandexHmacKey), - { name: "HMAC", hash: { name: "SHA-256" } }, - false, - ["sign", "verify"], -); - -async function getSignature(body) { - const key = await CryptoKey; - return new Uint8Array( - // Sign the body with the key - await window.crypto.subtle.sign("HMAC", key, body), - // Convert the signature to a hex string - ).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), ""); -} - -export { getSignature }; diff --git a/src/getUUID.js b/src/getUUID.js deleted file mode 100644 index ec71ba78..00000000 --- a/src/getUUID.js +++ /dev/null @@ -1,11 +0,0 @@ -function getUUID() { - const hexDigits = "0123456789ABCDEF"; - let uuid = ""; - for (let i = 0; i < 32; i++) { - const randomDigit = Math.floor(Math.random() * 16); - uuid += hexDigits[randomDigit]; - } - return uuid; -} - -export { getUUID }; diff --git a/src/headers.json b/src/headers.json index fa2aeb14..3bbc91fe 100644 --- a/src/headers.json +++ b/src/headers.json @@ -1,7 +1,7 @@ { "name": "[VOT] - Voice Over Translation", "description": "A small extension that adds a Yandex Browser video translation to other browsers", - "version": "1.5.3.1", + "version": "1.6.0", "author": "sodapng, mynovelhost, Toil, SashaXser, MrSoczekXD", "namespace": "vot", "icon": "https://translate.yandex.ru/icons/favicon.ico", @@ -19,51 +19,10 @@ "*://*.pornhub.com/*", "*://*.vk.com/*", "*://*.vk.ru/*", - "*://invidious.snopyta.org/*", - "*://invidious.kavin.rocks/*", - "*://vid.puffyan.us/*", - "*://invidious.namazso.eu/*", - "*://inv.riverside.rocks/*", - "*://yt.artemislena.eu/*", - "*://invidious.flokinet.to/*", - "*://invidious.esmailelbob.xyz/*", - "*://invidious.nerdvpn.de/*", - "*://invidious.slipfox.xyz/*", - "*://invidio.xamh.de/*", - "*://invidious.dhusch.de/*", - "*://*.piped.video/*", - "*://piped.tokhmi.xyz/*", - "*://piped.moomoo.me/*", - "*://piped.syncpundit.io/*", - "*://piped.mha.fi/*", - "*://watch.whatever.social/*", - "*://piped.garudalinux.org/*", - "*://efy.piped.pages.dev/*", - "*://watch.leptons.xyz/*", - "*://piped.lunar.icu/*", - "*://yt.dc09.ru/*", - "*://piped.mint.lgbt/*", - "*://*.il.ax/*", - "*://piped.privacy.com.de/*", - "*://piped.esmailelbob.xyz/*", - "*://piped.projectsegfau.lt/*", - "*://piped.in.projectsegfau.lt/*", - "*://piped.us.projectsegfau.lt/*", - "*://piped.privacydev.net/*", - "*://piped.palveluntarjoaja.eu/*", - "*://piped.smnz.de/*", - "*://piped.adminforge.de/*", - "*://piped.qdi.fi/*", - "*://piped.hostux.net/*", - "*://piped.chauvet.pro/*", - "*://piped.jotoma.de/*", - "*://piped.pfcd.me/*", - "*://piped.frontendfriendly.xyz/*", - "*://*.yewtu.be/*", - "*://inv.vern.cc/*", "*://*.vimeo.com/*", "*://*.9gag.com/*", "*://*.twitter.com/*", + "*://*.x.com/*", "*://*.facebook.com/*", "*://*.rutube.ru/*", "*://*.bilibili.com/*", @@ -72,30 +31,8 @@ "*://*.coursera.org/learn/*", "*://*.udemy.com/course/*", "*://*.tiktok.com/*", - "*://proxitok.pabloferreiro.es/*", - "*://proxitok.pussthecat.org/*", - "*://tok.habedieeh.re/*", - "*://proxitok.esmailelbob.xyz/*", - "*://proxitok.privacydev.net/*", - "*://tok.artemislena.eu/*", - "*://tok.adminforge.de/*", - "*://tik.hostux.net/*", - "*://tt.vern.cc/*", - "*://cringe.whatever.social/*", - "*://proxitok.lunar.icu/*", - "*://proxitok.privacy.com.de/*", "*://rumble.com/*", "*://*.eporner.com/*", - "*://peertube.1312.media/*", - "*://tube.shanti.cafe/*", - "*://bee-tube.fr/*", - "*://video.sadmin.io/*", - "*://dalek.zone/*", - "*://review.peertube.biz/*", - "*://peervideo.club/*", - "*://tube.la-dina.net/*", - "*://peertube.tmp.rcp.tf/*", - "*://peertube.su/*", "*://geo.dailymotion.com/*", "*://*.ok.ru/*", "*://trovo.live/*", @@ -108,6 +45,13 @@ "*://*.egghead.io/*", "*://*.youku.com/*", "*://*.archive.org/*", + "*://*.patreon.com/*", + "*://*.reddit.com/*", + "*://*.kodik.info/*", + "*://*.kodik.biz/*", + "*://*.kodik.cc/*", + "*://*.kick.com/*", + "*://developer.apple.com/*", "*://*/*.mp4*" ], "exclude": ["file://*/*.mp4*"], @@ -119,6 +63,7 @@ "connect": [ "yandex.ru", "yandex.net", + "timeweb.cloud", "raw.githubusercontent.com", "toil.cc", "deno.dev", diff --git a/src/index.js b/src/index.js index 2e6fa42c..ef4eef2f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,12 @@ -import { sitesInvidious, sitesPiped } from "./config/alternativeUrls.js"; +import Bowser from "bowser"; +import VOTClient, { VOTWorkerClient } from "vot.js"; +import votConfig from "vot.js/config"; +import { getVideoData, getVideoID, getService } from "vot.js/utils/videoData"; +import { YandexType } from "vot.js/types"; +import { availableLangs, availableTTS } from "vot.js/consts"; +import { convertSubs } from "vot.js/utils/subs"; +import { svg, html } from "lit-html"; + import { defaultAutoVolume, defaultDetectService, @@ -6,50 +14,29 @@ import { m3u8ProxyHost, proxyWorkerHost, maxAudioVolume, + votBackendUrl, + workerHost, } from "./config/config.js"; -import { - actualTTS, - availableLangs, - cfOnlyExtensions, -} from "./config/constants.js"; import { availableLocales, localizationProvider, } from "./localization/localizationProvider.js"; -import "./styles/main.scss"; import ui from "./ui.js"; import { VOTLocalizedError } from "./utils/VOTLocalizedError.js"; import debug from "./utils/debug.js"; import { - getVideoId, + GM_fetch, initHls, isPiPAvailable, lang, secsToStrTime, } from "./utils/utils.js"; import { syncVolume } from "./utils/volume.js"; -import { yandexProtobuf } from "./yandexProtobuf.js"; -import Bowser from "bowser"; - -import requestStreamPing from "./rsp.js"; -import requestStreamTranslation from "./rst.js"; -import requestVideoTranslation from "./rvt.js"; -import { - SubtitlesWidget, - fetchSubtitles, - getSubtitles, - jsonToSrt, -} from "./subtitles.js"; +import { SubtitlesWidget, fetchSubtitles, getSubtitles } from "./subtitles.js"; import youtubeUtils from "./utils/youtubeUtils.js"; -import coursehunterUtils from "./utils/coursehunterUtils.js"; -import courseraUtils from "./utils/courseraUtils.js"; -import udemyUtils from "./utils/udemyUtils.js"; -import bannedvideoUtils from "./utils/bannedvideoUtils.js"; -import weverseUtils from "./utils/weverseUtils.js"; -import sites from "./config/sites.js"; import { VideoObserver } from "./utils/VideoObserver.js"; import { votStorage } from "./utils/storage.js"; import { @@ -57,13 +44,17 @@ import { translate, translateServices, } from "./utils/translateApis.js"; +import { sitesInvidious, sitesPiped } from "vot.js/alternativeUrls"; const browserInfo = Bowser.getParser(window.navigator.userAgent).getResult(); - const dontTranslateMusic = false; // Пока не придумал как стоит реализовать - -const sitesChromiumBlocked = [...sitesInvidious, ...sitesPiped]; - +const cfOnlyExtensions = [ + "Violentmonkey", + "FireMonkey", + "Greasemonkey", + "AdGuard", + "OrangeMonkey", +]; const videoLipSyncEvents = [ "playing", "ratechange", @@ -80,145 +71,6 @@ function genOptionsByOBJ(obj, conditionString) { })); } -if (BUILD_MODE === "cloudflare") { - var translationPanding = false; -} - -function translateVideo( - url, - duration, - requestLang, - responseLang, - translationHelp, - callback, -) { - debug.log( - `Translate video (url: ${url}, duration: ${duration}, requestLang: ${requestLang}, responseLang: ${responseLang})`, - ); - - debug.log("translationHelp:", translationHelp); - - if (BUILD_MODE === "cloudflare" && translationPanding) { - debug.log("translationPanding return"); - return; - } - - translationPanding = true; - - requestVideoTranslation( - url, - duration, - requestLang, - responseLang, - translationHelp, - (success, response) => { - translationPanding = false; - - debug.log("[exec callback] Requesting video translation"); - if (!success) { - callback(false, localizationProvider.get("requestTranslationFailed")); - return; - } - - const translateResponse = - yandexProtobuf.decodeTranslationResponse(response); - console.log("[VOT] Translation response: ", translateResponse); - - switch (translateResponse.status) { - case 0: - callback(false, translateResponse.message); - break; - case 1: - case 5: - // status 5 (dzen) - // Отдает частичный контент т.е. аудио не для всего видео, а только для части (~10min) - // так же возвращается оставшееся время перевода (remainingTime) через которое нужно сделать повторный запрос, - // в котором будет возвращено полное аудио перевода и status 1. - // Если включена часть видео без перевода, то пишет "Эта часть видео еще не переведена" - callback( - !!translateResponse.url, - translateResponse.url || - localizationProvider.get("audioNotReceived"), - ); - break; - case 2: - callback( - false, - translateResponse.remainingTime - ? secsToStrTime(translateResponse.remainingTime) - : localizationProvider.get("translationTakeFewMinutes"), - ); - break; - case 3: - case 6: - /* - status: 3 - Иногда, в ответе приходит статус код 3, но видео всё, так же, ожидает перевода. - В конечном итоге, это занимает слишком много времени, - как-будто сервер не понимает, что данное видео уже недавно было переведено - и заместо возвращения готовой ссылки на перевод начинает переводить видео заново - при чём у него это получается за очень длительное время. - - status: 6 - Случайно встретил 6 статус код при котором видео так же продолжается перевод, - но после него ничего сверхъестественного не происходит. - Он появляется при первом запросе с 17=1, но не исключено, - что может появится и просто так - */ - callback(false, localizationProvider.get("videoBeingTranslated")); - break; - } - }, - ); -} - -function translateStream(url, requestLang, responseLang, callback) { - debug.log( - `Translate stream (url: ${url}, requestLang: ${requestLang}, responseLang: ${responseLang})`, - ); - - requestStreamTranslation( - url, - requestLang, - responseLang, - (success, response) => { - debug.log("[exec callback] Requesting stream translation"); - if (!success) { - callback(false, localizationProvider.get("requestTranslationFailed")); - return; - } - - const streamResponse = yandexProtobuf.decodeStreamResponse(response); - console.log("[VOT] Stream Translation response: ", streamResponse); - - switch (streamResponse.interval) { - case 10: - callback( - false, - streamResponse.interval, - localizationProvider.get("translationTakeFewMinutes"), - ); - break; - case 20: - callback( - true, - streamResponse.interval, - streamResponse || localizationProvider.get("audioNotReceived"), - ); - break; - case 0: - // stream removed or ended - callback( - false, - streamResponse.interval, - localizationProvider.get("streamNoConnectionToServer"), - ); - break; - } - }, - ); -} - class VideoHandler { // translate properties translateFromLang = "en"; // default language of video @@ -226,7 +78,6 @@ class VideoHandler { timer; - ytData = ""; videoData = ""; firstPlay = true; audio = new Audio(); @@ -234,15 +85,18 @@ class VideoHandler { gainNode = this.audioContext.createGain(); hls = initHls(); // debug enabled only in dev mode + votClient; videoTranslations = []; videoTranslationTTL = 7200; + cachedTranslation; downloadTranslationUrl = null; downloadSubtitlesUrl = null; autoRetry; streamPing; + votOpts; volumeOnStart; tempOriginalVolume; // temp video volume for syncing tempVolume; // temp translation volume for syncing @@ -256,6 +110,13 @@ class VideoHandler { // button move dragging; + /** + * Constructor function for VideoHandler class. + * + * @param {Object} video - The video element to handle. + * @param {Object} container - The container element for the video. + * @param {Object} site - The site object associated with the video. + */ constructor(video, container, site) { debug.log( "[VideoHandler] add video:", @@ -274,6 +135,147 @@ class VideoHandler { this.init(); } + /** + * Translate a video based on the specified languages. + * + * @param {Object} videoData - The data of the video to be translated. + * @param {string} requestLang - The language code for the requested translation. + * @param {string} responseLang - The language code for the desired translated output. + * @param {Object} [translationHelp=null] - Additional translation help data (optional). + * @return {Promise} A Promise that resolves to the translated video data. + */ + async translateVideoImpl( + videoData, + requestLang, + responseLang, + translationHelp = null, + ) { + clearTimeout(this.autoRetry); + debug.log( + videoData, + `Translate video (requestLang: ${requestLang}, responseLang: ${responseLang})`, + ); + + if ((await getVideoID(this.site, this.video)) !== videoData.videoId) { + return null; + } + + try { + const res = await this.votClient.translateVideo({ + videoData, + requestLang, + responseLang, + translationHelp, + }); + debug.log("Translate video result", res); + if (res.translated && res.remainingTime < 1) { + debug.log("Video translation finished with this data: ", res); + return res; + } + + await this.updateTranslationErrorMsg( + res.remainingTime > 0 + ? secsToStrTime(res.remainingTime) + : res.message ?? + localizationProvider.get("translationTakeFewMinutes"), + ); + } catch (err) { + console.error("[VOT] Failed to translate video", err); + await this.updateTranslationErrorMsg(err.data?.message ?? err); + return null; + } + + return new Promise((resolve) => { + const timeoutDuration = this.subtitlesList.some( + (item) => item.source === "yandex", + ) + ? 20_000 + : 30_000; + this.autoRetry = setTimeout(async () => { + const res = await this.translateVideoImpl( + videoData, + requestLang, + responseLang, + translationHelp, + ); + if (!res || (res.translated && res.remainingTime < 1)) { + resolve(res); + } + }, timeoutDuration); + }); + } + + /** + * Translate a video stream based on the specified languages. + * + * @param {Object} videoData - The data of the video stream to be translated. + * @param {string} requestLang - The language code for the requested translation. + * @param {string} responseLang - The language code for the desired translated output. + * @return {Promise} A Promise that resolves to the translated video stream data. + */ + async translateStreamImpl(videoData, requestLang, responseLang) { + clearTimeout(this.autoRetry); + debug.log( + videoData, + `Translate stream (requestLang: ${requestLang}, responseLang: ${responseLang})`, + ); + + if ((await getVideoID(this.site, this.video)) !== videoData.videoId) { + return null; + } + + try { + const res = await this.votClient.translateStream({ + videoData, + requestLang, + responseLang, + }); + debug.log("Translate stream result", res); + if (!res.translated && res.interval === 10) { + await this.updateTranslationErrorMsg( + localizationProvider.get("translationTakeFewMinutes"), + ); + return new Promise((resolve) => { + this.autoRetry = setTimeout(async () => { + const res = await this.translateStreamImpl( + videoData, + requestLang, + responseLang, + ); + if (!res || !(!res.translated && res.interval === 10)) { + resolve(res); + } + }, res.interval * 1000); + }); + } + + if (res.message) { + debug.log(`Stream translation aborted! Message: ${res.message}`); + throw new VOTLocalizedError("streamNoConnectionToServer"); + } + + if (!res.result) { + debug.log("Failed to find translation result! Data:", res); + throw new VOTLocalizedError("audioNotReceived"); + } + + debug.log("Stream translated successfully. Running...", res); + + this.streamPing = setInterval(async () => { + debug.log("Ping stream translation", res.pingId); + this.votClient.pingStream({ + pingId: res.pingId, + }); + }, res.interval * 1000); + + return res; + } catch (err) { + console.error("[VOT] Failed to translate stream", err); + await this.updateTranslationErrorMsg(err.data?.message ?? err); + return null; + } + } + async autoTranslate() { if ( this.site.host === "youtube" && @@ -302,12 +304,12 @@ class VideoHandler { } } + /** + * Initializes the VideoHandler class by setting up data promises, fetching data, initializing UI elements, and setting up event listeners. + */ async init() { if (this.initialized) return; - const audioProxyDefault = - lang === "uk" && BUILD_MODE === "cloudflare" ? 1 : 0; - const dataPromises = { autoTranslate: votStorage.get("autoTranslate", 0, true), dontTranslateLanguage: votStorage.get("dontTranslateLanguage", lang), @@ -323,10 +325,11 @@ class VideoHandler { syncVolume: votStorage.get("syncVolume", 0, true), subtitlesMaxLength: votStorage.get("subtitlesMaxLength", 300, true), highlightWords: votStorage.get("highlightWords", 0, true), + subtitlesFontSize: votStorage.get("subtitlesFontSize", 20, true), + subtitlesOpacity: votStorage.get("subtitlesOpacity", 20, true), responseLanguage: votStorage.get("responseLanguage", lang), defaultVolume: votStorage.get("defaultVolume", 100, true), - udemyData: votStorage.get("udemyData", { accessToken: "", expires: 0 }), - audioProxy: votStorage.get("audioProxy", audioProxyDefault, true), + audioProxy: votStorage.get("audioProxy", 0, true), showPiPButton: votStorage.get("showPiPButton", 0, true), translateAPIErrors: votStorage.get("translateAPIErrors", 1, true), translationService: votStorage.get( @@ -335,6 +338,7 @@ class VideoHandler { ), detectService: votStorage.get("detectService", defaultDetectService), m3u8ProxyHost: votStorage.get("m3u8ProxyHost", m3u8ProxyHost), + translateProxyEnabled: votStorage.get("translateProxyEnabled", 0, true), proxyWorkerHost: votStorage.get("proxyWorkerHost", proxyWorkerHost), audioBooster: votStorage.get("audioBooster", 0, true), }; @@ -350,6 +354,46 @@ class VideoHandler { console.log("[db] data from db: ", this.data); + if ( + this.data.translateProxyEnabled === 0 && + GM_info?.scriptHandler && + cfOnlyExtensions.includes(GM_info.scriptHandler) + ) { + this.data.translateProxyEnabled = 1; + await votStorage.set("translateProxyEnabled", 1); + debug.log("translateProxyEnabled", this.data.translateProxyEnabled); + } + + debug.log("Extension compatibility passed..."); + + this.votOpts = { + headers: + this.data.translateProxyEnabled === 1 + ? {} + : { + "sec-ch-ua": null, + "sec-ch-ua-mobile": null, + "sec-ch-ua-platform": null, + // "sec-ch-ua-model": null, + // "sec-ch-ua-platform-version": null, + // "sec-ch-ua-wow64": null, + // "sec-ch-ua-arch": null, + // "sec-ch-ua-bitness": null, + // "sec-ch-ua-full-version": null, + // "sec-ch-ua-full-version-list": null, + }, + fetchFn: GM_fetch, + hostVOT: votBackendUrl, + host: + this.data.translateProxyEnabled === 1 + ? this.data.proxyWorkerHost + : workerHost, + }; + + this.votClient = new ( + this.data.translateProxyEnabled === 1 ? VOTWorkerClient : VOTClient + )(this.votOpts); + this.subtitlesWidget = new SubtitlesWidget( this.video, this.container, @@ -357,6 +401,8 @@ class VideoHandler { ); this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength); this.subtitlesWidget.setHighlightWords(this.data.highlightWords); + this.subtitlesWidget.setFontSize(this.data.subtitlesFontSize); + this.subtitlesWidget.setOpacity(this.data.subtitlesOpacity); // audio booster this.audio.crossOrigin = "anonymous"; @@ -390,24 +436,39 @@ class VideoHandler { this.initialized = true; } + /** + * Set translation button status and text + * + * @param {"none"|"success"|"error"} status - button status + * @param {string} text - visible button text + */ transformBtn(status = "none", text) { this.votButton.container.dataset.status = status; - this.votButton.container.dataset.translating = - status === "error" - ? text.includes(localizationProvider.get("translationTake")) - : false; - this.votButton.label.innerHTML = text; + const isLoading = + status === "error" && + text.includes(localizationProvider.get("translationTake")); + this.setLoadingBtn(isLoading); + this.votButton.label.textContent = text; this.votButton.container.title = status === "error" ? text : ""; } + /** + * Set loading icon to translation button + * + * @param {boolean} loading + */ + setLoadingBtn(loading = false) { + this.votButton.container.dataset.loading = loading; + } + initUI() { // VOT Button { this.votButton = ui.createVOTButton( localizationProvider.get("translateVideo"), ); - // use an additional check because sometimes this.video.clientWidth = 0 + // use an additional check because sometimes this.video.clientWidth = 0 if ( this.data?.buttonPos && this.data?.buttonPos !== "default" && @@ -444,19 +505,46 @@ class VideoHandler { this.container.appendChild(this.votMenu.container); this.votDownloadButton = ui.createIconButton( - ``, + svg` + + `, ); this.votDownloadButton.hidden = true; this.votMenu.headerContainer.appendChild(this.votDownloadButton); this.votDownloadSubtitlesButton = ui.createIconButton( - ``, + svg` + + `, ); this.votDownloadSubtitlesButton.hidden = true; this.votMenu.headerContainer.appendChild(this.votDownloadSubtitlesButton); this.votSettingsButton = ui.createIconButton( - ``, + svg` + + `, ); this.votMenu.headerContainer.appendChild(this.votSettingsButton); @@ -464,14 +552,10 @@ class VideoHandler { fromTitle: localizationProvider.get("langs")[this.video.detectedLanguage], fromDialogTitle: localizationProvider.get("videoLanguage"), - fromItems: [ - { - label: localizationProvider.get("langs")["auto"], - value: "auto", - selected: "", - }, - ...genOptionsByOBJ(availableLangs, this.videoData.detectedLanguage), - ], + fromItems: genOptionsByOBJ( + availableLangs, + this.videoData.detectedLanguage, + ), fromOnSelectCB: async (e) => { debug.log( "[fromOnSelectCB] select from language", @@ -485,7 +569,7 @@ class VideoHandler { }, toTitle: localizationProvider.get("langs")[this.video.responseLanguage], toDialogTitle: localizationProvider.get("translationLanguage"), - toItems: genOptionsByOBJ(actualTTS, this.videoData.responseLanguage), + toItems: genOptionsByOBJ(availableTTS, this.videoData.responseLanguage), toOnSelectCB: async (e) => { const newLang = e.target.dataset.votValue; debug.log("[toOnSelectCB] select to language", newLang); @@ -531,9 +615,8 @@ class VideoHandler { this.votMenu.bodyContainer.appendChild(this.votSubtitlesSelect.container); this.votVideoVolumeSlider = ui.createSlider( - `${localizationProvider.get("VOTVolume")}: ${ - this.getVideoVolume() * 100 - }%`, + html`${localizationProvider.get("VOTVolume")}: + ${this.getVideoVolume() * 100}%`, this.getVideoVolume() * 100, ); this.votVideoVolumeSlider.container.hidden = @@ -544,9 +627,8 @@ class VideoHandler { ); this.votVideoTranslationVolumeSlider = ui.createSlider( - `${localizationProvider.get("VOTVolumeTranslation")}: ${ - this.data?.defaultVolume ?? 100 - }%`, + html`${localizationProvider.get("VOTVolumeTranslation")}: + ${this.data?.defaultVolume ?? 100}%`, this.data?.defaultVolume ?? 100, 0, this.data.audioBooster ? maxAudioVolume : 100, @@ -622,7 +704,9 @@ class VideoHandler { this.votAutoSetVolumeCheckbox.container, ); this.votAutoSetVolumeSlider = ui.createSlider( - `${(this.data?.autoVolume ?? defaultAutoVolume) * 100}%`, + html`${(this.data?.autoVolume ?? defaultAutoVolume) * 100}%`, (this.data?.autoVolume ?? defaultAutoVolume) * 100, 0, 100, @@ -647,16 +731,6 @@ class VideoHandler { this.votAudioBoosterCheckbox.container, ); - // udemy only - this.votUdemyDataTextfield = ui.createTextfield( - localizationProvider.get("VOTUdemyData"), - this.data?.udemyData?.accessToken ?? "", - ); - this.votUdemyDataTextfield.container.hidden = this.site.host !== "udemy"; - this.votSettingsDialog.bodyContainer.appendChild( - this.votUdemyDataTextfield.container, - ); - this.votSyncVolumeCheckbox = ui.createCheckbox( localizationProvider.get("VOTSyncVolume"), this.data?.syncVolume ?? false, @@ -722,24 +796,11 @@ class VideoHandler { ); this.votSettingsDialog.bodyContainer.appendChild(this.votSubtitlesHeader); - this.votSubtitlesMaxLengthSlider = ui.createSlider( - `${localizationProvider.get("VOTSubtitlesMaxLength")}: ${ - this.data?.subtitlesMaxLength ?? 300 - }`, - this.data?.subtitlesMaxLength ?? 300, - 50, - 300, - ); - this.votSettingsDialog.bodyContainer.appendChild( - this.votSubtitlesMaxLengthSlider.container, - ); - - this.votSubtitlesHighlightWordsCheckbox = ui.createCheckbox( - localizationProvider.get("VOTHighlightWords"), - this.data?.highlightWords ?? false, + this.votSubtitlesDetails = ui.createDetails( + localizationProvider.get("VOTSubtitlesDesign"), ); this.votSettingsDialog.bodyContainer.appendChild( - this.votSubtitlesHighlightWordsCheckbox.container, + this.votSubtitlesDetails.container, ); // PROXY @@ -758,24 +819,23 @@ class VideoHandler { this.votM3u8ProxyHostTextfield.container, ); - // cf version only this.votProxyWorkerHostTextfield = ui.createTextfield( localizationProvider.get("VOTProxyWorkerHost"), this.data?.proxyWorkerHost, proxyWorkerHost, ); - this.votProxyWorkerHostTextfield.container.hidden = - BUILD_MODE !== "cloudflare"; + // this.votProxyWorkerHostTextfield.container.hidden = + // this.data.translateProxyEnabled !== 1; this.votSettingsDialog.bodyContainer.appendChild( this.votProxyWorkerHostTextfield.container, ); - // cf version only this.votAudioProxyCheckbox = ui.createCheckbox( localizationProvider.get("VOTAudioProxy"), this.data?.audioProxy ?? false, ); - this.votAudioProxyCheckbox.container.hidden = BUILD_MODE !== "cloudflare"; + // this.votAudioProxyCheckbox.container.hidden = + // this.data.translateProxyEnabled !== 1; this.votSettingsDialog.bodyContainer.appendChild( this.votAudioProxyCheckbox.container, ); @@ -787,6 +847,7 @@ class VideoHandler { this.votLanguageSelect = ui.createVOTSelect( localizationProvider.get("langs")[ + // eslint-disable-next-line sonarjs/no-duplicate-string votStorage.syncGet("locale-lang-override", "auto") ], localizationProvider.get("VOTMenuLanguage"), @@ -822,9 +883,7 @@ class VideoHandler { this.votVersionInfo = ui.createInformation( `${localizationProvider.get("VOTVersion")}:`, - BUILD_MODE === "cloudflare" - ? `cloudflare ${GM_info.script.version}` - : GM_info.script.version, + GM_info.script.version, ); this.votSettingsDialog.bodyContainer.appendChild( this.votVersionInfo.container, @@ -964,7 +1023,7 @@ class VideoHandler { }); this.votDownloadSubtitlesButton.addEventListener("click", async () => { - const srtContent = jsonToSrt(this.YandexSubtitles); + const srtContent = convertSubs(this.yandexSubtitles, "srt"); const blob = new Blob([srtContent], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); @@ -985,7 +1044,7 @@ class VideoHandler { this.votVideoVolumeSlider.input.addEventListener("input", (e) => { const value = Number(e.target.value); - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = + this.votVideoVolumeSlider.label.querySelector("strong").textContent = `${value}%`; this.setVideoVolume(value / 100); if (this.data.syncVolume) { @@ -1001,7 +1060,7 @@ class VideoHandler { await votStorage.set("defaultVolume", this.data.defaultVolume); this.votVideoTranslationVolumeSlider.label.querySelector( "strong", - ).innerHTML = `${this.data.defaultVolume}%`; + ).textContent = `${this.data.defaultVolume}%`; this.gainNode.gain.value = this.data.defaultVolume / 100; if (!this.data.syncVolume) { return; @@ -1070,8 +1129,9 @@ class VideoHandler { const presetAutoVolume = Number(e.target.value); this.data.autoVolume = (presetAutoVolume / 100).toFixed(2); await votStorage.set("autoVolume", this.data.autoVolume); - this.votAutoSetVolumeSlider.label.querySelector("strong").innerHTML = - `${presetAutoVolume}%`; + this.votAutoSetVolumeSlider.label.querySelector( + "strong", + ).textContent = `${presetAutoVolume}%`; })(); }); @@ -1114,21 +1174,6 @@ class VideoHandler { })(); }); - this.votUdemyDataTextfield.input.addEventListener("change", (e) => { - (async () => { - this.data.udemyData = { - accessToken: e.target.value, - expires: new Date().getTime(), - }; - await votStorage.set("udemyData", this.data.udemyData); - debug.log( - "udemyData value changed. New value: ", - this.data.udemyData, - ); - window.location.reload(); - })(); - }); - this.votSyncVolumeCheckbox.input.addEventListener("change", (e) => { (async () => { this.data.syncVolume = Number(e.target.checked); @@ -1159,34 +1204,125 @@ class VideoHandler { // SUBTITLES - this.votSubtitlesMaxLengthSlider.input.addEventListener("input", (e) => { - (async () => { - this.data.subtitlesMaxLength = Number(e.target.value); - await votStorage.set( - "subtitlesMaxLength", - this.data.subtitlesMaxLength, - ); - this.votSubtitlesMaxLengthSlider.label.querySelector( - "strong", - ).innerHTML = `${this.data.subtitlesMaxLength}`; - this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength); - })(); - }); + this.votSubtitlesDetails.container.addEventListener("click", () => { + this.votSubtitlesDialog = ui.createDialog( + localizationProvider.get("VOTSubtitlesDesign"), + ); + this.votSubtitlesDialog.container.classList.add("vot-dialog-temp"); + this.votSubtitlesDialog.container.hidden = false; + // remove the modal so that they do not accumulate + this.votSubtitlesDialog.backdrop.onclick = + this.votSubtitlesDialog.closeButton.onclick = () => { + this.votSubtitlesDialog.container.remove(); + }; - this.votSubtitlesHighlightWordsCheckbox.input.addEventListener( - "change", - (e) => { + // subtitles elements + this.votSubtitlesHighlightWordsCheckbox = ui.createCheckbox( + localizationProvider.get("VOTHighlightWords"), + this.data?.highlightWords ?? false, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesHighlightWordsCheckbox.container, + ); + + this.votSubtitlesMaxLengthSlider = ui.createSlider( + html`${localizationProvider.get("VOTSubtitlesMaxLength")}: + ${this.data?.subtitlesMaxLength ?? 300}`, + this.data?.subtitlesMaxLength ?? 300, + 50, + 300, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesMaxLengthSlider.container, + ); + + this.votSubtitlesFontSizeSlider = ui.createSlider( + html`${localizationProvider.get("VOTSubtitlesFontSize")}: + ${this.data?.subtitlesFontSize ?? 20}`, + this.data?.subtitlesFontSize ?? 20, + 8, + 50, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesFontSizeSlider.container, + ); + + this.votSubtitlesOpacitySlider = ui.createSlider( + html`${localizationProvider.get("VOTSubtitlesOpacity")}: + ${this.data?.subtitlesOpacity ?? 20}`, + this.data?.subtitlesOpacity ?? 20, + 0, + 100, + ); + this.votSubtitlesDialog.bodyContainer.appendChild( + this.votSubtitlesOpacitySlider.container, + ); + + // subtitles events + this.votSubtitlesHighlightWordsCheckbox.input.addEventListener( + "change", + (e) => { + (async () => { + this.data.highlightWords = Number(e.target.checked); + await votStorage.set("highlightWords", this.data.highlightWords); + debug.log( + "highlightWords value changed. New value: ", + this.data.highlightWords, + ); + this.subtitlesWidget.setHighlightWords(this.data.highlightWords); + })(); + }, + ); + + this.votSubtitlesMaxLengthSlider.input.addEventListener( + "input", + (e) => { + (async () => { + this.data.subtitlesMaxLength = Number(e.target.value); + await votStorage.set( + "subtitlesMaxLength", + this.data.subtitlesMaxLength, + ); + this.votSubtitlesMaxLengthSlider.label.querySelector( + "strong", + ).textContent = `${this.data.subtitlesMaxLength}`; + this.subtitlesWidget.setMaxLength(this.data.subtitlesMaxLength); + })(); + }, + ); + + this.votSubtitlesFontSizeSlider.input.addEventListener("input", (e) => { (async () => { - this.data.highlightWords = Number(e.target.checked); - await votStorage.set("highlightWords", this.data.highlightWords); - debug.log( - "highlightWords value changed. New value: ", - this.data.highlightWords, + this.data.subtitlesFontSize = Number(e.target.value); + await votStorage.set( + "subtitlesFontSize", + this.data.subtitlesFontSize, ); - this.subtitlesWidget.setHighlightWords(this.data.highlightWords); + this.votSubtitlesFontSizeSlider.label.querySelector( + "strong", + ).textContent = `${this.data.subtitlesFontSize}`; + this.subtitlesWidget.setFontSize(this.data.subtitlesFontSize); })(); - }, - ); + }); + + this.votSubtitlesOpacitySlider.input.addEventListener("input", (e) => { + (async () => { + this.data.subtitlesOpacity = Number(e.target.value); + await votStorage.set( + "subtitlesOpacity", + this.data.subtitlesOpacity, + ); + this.votSubtitlesOpacitySlider.label.querySelector( + "strong", + ).textContent = `${this.data.subtitlesOpacity}`; + this.subtitlesWidget.setOpacity(this.data.subtitlesOpacity); + })(); + }); + + document.documentElement.appendChild(this.votSubtitlesDialog.container); + }); + + // OTHER this.votShowPiPButtonCheckbox.input.addEventListener("change", (e) => { (async () => { @@ -1224,7 +1360,7 @@ class VideoHandler { "proxyWorkerHost value changed. New value: ", this.data.proxyWorkerHost, ); - window.location.reload(); + this.votClient.host = this.data.proxyWorkerHost; })(); }); @@ -1402,6 +1538,8 @@ class VideoHandler { eContainer = document.querySelector('div[data-testid="videoPlayer"]'); } else if (this.site.host === "yandexdisk") { eContainer = document.querySelector(".video-player__player"); + } else if (this.site.host === "reddit") { + eContainer = document.querySelector("shreddit-post"); } else { eContainer = this.container; } @@ -1445,22 +1583,22 @@ class VideoHandler { this.container.style.height = "100%"; } - addExtraEventListener(this.video, "canplaythrough", async () => { + addExtraEventListener(this.video, "canplay", async () => { // Временное решение if (this.site.host === "rutube" && this.video.src) { return; } - if (getVideoId(this.site.host, this.video) === this.videoData.videoId) + if ((await getVideoID(this.site, this.video)) === this.videoData.videoId) return; await this.handleSrcChanged(); debug.log("lipsync mode is loadeddata"); await this.autoTranslate(); }); - addExtraEventListener(this.video, "emptied", () => { + addExtraEventListener(this.video, "emptied", async () => { if ( this.video.src && - getVideoId(this.site.host, this.video) === this.videoData.videoId + (await getVideoID(this.site, this.video)) === this.videoData.videoId ) return; debug.log("lipsync mode is emptied"); @@ -1503,14 +1641,13 @@ class VideoHandler { ); this.subtitlesWidget.setContent(null); this.votDownloadSubtitlesButton.hidden = true; - this.YandexSubtitles = null; + this.yandexSubtitles = null; } else { - const fetchedSubs = await fetchSubtitles( + this.yandexSubtitles = await fetchSubtitles( this.subtitlesList.at(parseInt(subs)), ); - this.subtitlesWidget.setContent(fetchedSubs); + this.subtitlesWidget.setContent(this.yandexSubtitles); this.votDownloadSubtitlesButton.hidden = false; - this.YandexSubtitles = fetchedSubs; } } @@ -1567,11 +1704,20 @@ class VideoHandler { return; } - this.subtitlesList = await getSubtitles( - this.site, - this.videoData.videoId, - this.videoData.detectedLanguage, - ); + try { + this.subtitlesList = await getSubtitles(this.votClient, this.videoData); + } catch (err) { + debug.log("Error with yandex server, try auto-fix...", err); + this.votOpts = { + fetchFn: GM_fetch, + hostVOT: votBackendUrl, + host: this.data.proxyWorkerHost, + }; + this.votClient = new VOTWorkerClient(this.votOpts); + this.subtitlesList = await getSubtitles(this.votClient, this.videoData); + await votStorage.set("translateProxyEnabled", 1); + } + if (!this.subtitlesList) { await this.changeSubtitlesLang("disabled"); } else { @@ -1612,7 +1758,7 @@ class VideoHandler { const newSlidersVolume = Math.round(videoVolume); this.votVideoVolumeSlider.input.value = newSlidersVolume; - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = + this.votVideoVolumeSlider.label.querySelector("strong").textContent = `${newSlidersVolume}%`; ui.updateSlider(this.votVideoVolumeSlider.input); @@ -1657,7 +1803,7 @@ class VideoHandler { ); slider.input.value = finalValue; - slider.label.querySelector("strong").innerHTML = `${finalValue}%`; + slider.label.querySelector("strong").textContent = `${finalValue}%`; ui.updateSlider(slider.input); // Update the temp variables for future syncing @@ -1666,29 +1812,34 @@ class VideoHandler { this.tempVolume = fromType === "translation" ? newVolume : finalValue; } + /** + * Asynchronously retrieves video data from the current page's URL. + * If the video is hosted on YouTube, it also retrieves additional data. + * + * @return {Promise} An object containing the video's duration, URL, video ID, host, + * detected language, response language, and translation help. + */ async getVideoData() { + const { duration, url, videoId, host, translationHelp, detectedLanguage } = + await getVideoData(this.site, this.video); const videoData = { - // ! should be null for ALL websites except coursera and udemy ! - // else use direct link: `{url: xxx.mp4}` - translationHelp: null, - isStream: false, // by default, we request the translation of the video - duration: this.video?.duration || 343, // ! if 0 - we get 400 error - videoId: getVideoId(this.site.host, this.video), - detectedLanguage: this.translateFromLang, + translationHelp: translationHelp ?? null, + // by default, we request the translation of the video + isStream: false, + // ! if 0 - we get 400 error + duration: this.video?.duration || duration || votConfig.defaultDuration, + videoId, + url, + host, + detectedLanguage: detectedLanguage ?? this.translateFromLang, responseLanguage: this.translateToLang, }; - if (!videoData.videoId) { - this.ytData = {}; - return videoData; - } - if (this.site.host === "youtube") { - this.ytData = await youtubeUtils.getVideoData(); - videoData.isStream = this.ytData.isLive; - if (this.ytData.title) { - videoData.detectedLanguage = this.ytData.detectedLanguage; - videoData.responseLanguage = this.translateToLang; + const youtubeData = await youtubeUtils.getVideoData(); + videoData.isStream = youtubeData.isLive; + if (youtubeData.title) { + videoData.detectedLanguage = youtubeData.detectedLanguage; } } else if (["rutube", "ok.ru", "mail_ru"].includes(this.site.host)) { videoData.detectedLanguage = "ru"; @@ -1697,52 +1848,8 @@ class VideoHandler { } else if (this.site.host === "vk") { const trackLang = document.getElementsByTagName("track")?.[0]?.srclang; videoData.detectedLanguage = trackLang || "auto"; - } else if (this.site.host === "coursera") { - const courseraData = await courseraUtils.getVideoData( - this.translateToLang, - ); - videoData.duration = courseraData.duration || videoData.duration; // courseraData.duration sometimes it can be equal to NaN - videoData.detectedLanguage = courseraData.detectedLanguage; - videoData.translationHelp = courseraData.translationHelp; - } else if (this.site.host === "coursehunter") { - const coursehunterData = await coursehunterUtils.getVideoData(); - videoData.translationHelp = { - // use direct link - url: coursehunterData.url, - }; - videoData.duration = coursehunterData.duration || videoData.duration; - } else if (this.site.host === "bannedvideo") { - const bannedvideoData = await bannedvideoUtils.getVideoData( - videoData.videoId, - ); - videoData.translationHelp = { - url: bannedvideoData.url, - }; - - videoData.duration = bannedvideoData.duration || videoData.duration; - videoData.isStream = bannedvideoData.live; } else if (this.site.host === "weverse") { - const weverseData = await weverseUtils.getVideoData(); videoData.detectedLanguage = "ko"; - if (weverseData) { - videoData.translationHelp = { - url: weverseData.url, - }; - videoData.duration = weverseData.duration || videoData.duration; - } - } else if (this.site.host === "udemy") { - const udemyData = await udemyUtils.getVideoData( - this.data.udemyData, - this.translateToLang, - ); - videoData.duration = udemyData.duration || videoData.duration; - videoData.detectedLanguage = udemyData.detectedLanguage; - videoData.translationHelp = udemyData.translationHelp; - } else if (this.site.host === "bitchute") { - // to avoid creating a separate file with the same functionality - videoData.translationHelp = { - url: videoData.videoId, - }; } else if ( [ "bilibili", @@ -1763,6 +1870,7 @@ class VideoHandler { } return videoData; } + videoValidator() { if (["youtube", "ok.ru", "vk"].includes(this.site.host)) { debug.log("VideoValidator videoData: ", this.videoData); @@ -1773,18 +1881,20 @@ class VideoHandler { throw new VOTLocalizedError("VOTDisableFromYourLang"); } } - // if (this.ytData.isPremiere) { - // throw new VOTLocalizedError("VOTPremiere"); - // } - // if (this.ytData.isLive) { - // throw new VOTLocalizedError("VOTLiveNotSupported"); - // } + if (!this.videoData.isStream && this.videoData.duration > 14_400) { throw new VOTLocalizedError("VOTVideoIsTooLong"); } + return true; } + /** + * Synchronizes the lip sync of the video and audio elements. + * + * @param {boolean} [mode=false] - The lip sync mode. + * @return {void} + */ lipSync(mode = false) { debug.log("lipsync video", this.video); if (!this.video) { @@ -1802,7 +1912,7 @@ class VideoHandler { debug.log("lipsync mode is play"); const audioPromise = this.audio.play(); if (audioPromise !== undefined) { - audioPromise.catch((e) => { + audioPromise.catch(async (e) => { console.error("[VOT]", e); if (e.name === "NotAllowedError") { this.transformBtn( @@ -1810,16 +1920,6 @@ class VideoHandler { localizationProvider.get("grantPermissionToAutoPlay"), ); throw new VOTLocalizedError("grantPermissionToAutoPlay"); - } else if (e.name === "NotSupportedError") { - this.transformBtn( - "error", - sitesChromiumBlocked.includes(window.location.hostname) - ? localizationProvider.get("neededAdditionalExtension") - : localizationProvider.get("audioFormatNotSupported"), - ); - throw sitesChromiumBlocked.includes(window.location.hostname) - ? new VOTLocalizedError("neededAdditionalExtension") - : new VOTLocalizedError("audioFormatNotSupported"); } }); } @@ -1870,7 +1970,7 @@ class VideoHandler { async translateExecutor(VIDEO_ID) { debug.log("Run translateFunc", VIDEO_ID); - this.translateFunc( + await this.translateFunc( VIDEO_ID, this.videoData.isStream, this.videoData.detectedLanguage, @@ -1881,18 +1981,32 @@ class VideoHandler { async updateTranslationErrorMsg(errorMessage) { const translationTake = localizationProvider.get("translationTake"); - const VOTTranslatingError = localizationProvider.get("VOTTranslatingError"); const lang = localizationProvider.lang; + if ( + [ + "Подготавливаем перевод", + "Видео передано в обработку", + "Ожидаем перевод видео", + "Загружаем переведенное аудио", + ].includes(errorMessage) + ) { + this.setLoadingBtn(true); + } + if (errorMessage?.name === "VOTLocalizedError") { this.transformBtn("error", errorMessage.localizedMessage); + } else if (errorMessage instanceof Error) { + // to prevent pass Error as text + this.transformBtn("error", errorMessage?.message); } else if ( this.data.translateAPIErrors === 1 && !errorMessage.includes(translationTake) && lang !== "ru" ) { + // adds a stub text until a text translation is received to avoid a long delay with long text + this.setLoadingBtn(true); const translatedMessage = await translate(errorMessage, "ru", lang); - this.transformBtn("error", VOTTranslatingError); this.transformBtn("error", translatedMessage); } else { this.transformBtn("error", errorMessage); @@ -1908,8 +2022,9 @@ class VideoHandler { if (this.data.autoSetVolumeYandexStyle === 1) { this.votVideoVolumeSlider.input.value = this.data.autoVolume * 100; - this.votVideoVolumeSlider.label.querySelector("strong").innerHTML = - `${this.data.autoVolume * 100}%`; + this.votVideoVolumeSlider.label.querySelector("strong").textContent = `${ + this.data.autoVolume * 100 + }%`; ui.updateSlider(this.votVideoVolumeSlider.input); } @@ -1918,13 +2033,65 @@ class VideoHandler { } // update translation audio src - updateTranslation(audioUrl) { - // ! Don't use this function for streams - this.audio.src = audioUrl; + async updateTranslation(audioUrl) { + //debug.log("cachedTranslation", this.cachedTranslation?.url, this.audio.currentSrc); + if (this.cachedTranslation?.url === this.audio.currentSrc) { + debug.log("[translateFunc] Audio src is the same"); + this.audio.src = audioUrl; + } else { + try { + const response = await GM_fetch(audioUrl, { + method: "HEAD", + timeout: 5000, + }); + debug.log("Test audio response", response); + if (response.status === 404) { + debug.log("Yandex returned not valid audio, trying to fix..."); + let translateRes = await this.translateVideoImpl( + this.videoData, + (this.videoData.detectedLanguage = "auto"), + this.videoData.responseLanguage, + this.videoData.translationHelp, + ); + this.setSelectMenuValues( + this.videoData.detectedLanguage, + this.videoData.responseLanguage, + ); + audioUrl = translateRes.url; + debug.log("Fixed audio audioUrl", audioUrl); + } else { + debug.log("Valid audioUrl", audioUrl); + } + } catch (err) { + if (err.message === "Timeout") { + debug.log("Request timed out. Handling timeout error..."); + this.data.audioProxy = 1; + await votStorage.set("audioProxy", 1); + } else { + debug.log("Test audio error:", err); + } + } + + this.audio.src = audioUrl; + try { + await this.audio.play(); + } catch (e) { + console.error("[VOT]", e); + if (e.name === "NotSupportedError") { + if ( + [...sitesInvidious, ...sitesPiped].includes( + window.location.hostname, + ) + ) { + throw new VOTLocalizedError("VOTMediaCSPError"); + } + this.data.audioProxy = 1; + await votStorage.set("audioProxy", 1); + } + } + } - // cf version only if ( - BUILD_MODE === "cloudflare" && this.data.audioProxy === 1 && audioUrl.startsWith("https://vtrans.s3-private.mds.yandex.net/tts/prod/") ) { @@ -1932,22 +2099,18 @@ class VideoHandler { "https://vtrans.s3-private.mds.yandex.net/tts/prod/", "", ); - const proxiedAudioUrl = `https://${this.data.proxyWorkerHost}/video-translation/audio-proxy/${audioPath}`; - console.log(`[VOT] Audio proxied via ${proxiedAudioUrl}`); - this.audio.src = proxiedAudioUrl; + audioUrl = `https://${this.data.proxyWorkerHost}/video-translation/audio-proxy/${audioPath}`; + console.log(`[VOT] Audio proxied via ${audioUrl}`); } - this.volumeOnStart = this.getVideoVolume(); - if (typeof this.data.defaultVolume === "number") { - this.gainNode.gain.value = this.data.defaultVolume / 100; - } - if ( - typeof this.data.autoSetVolumeYandexStyle === "number" && - this.data.autoSetVolumeYandexStyle - ) { - this.setVideoVolume(this.data.autoVolume); + // ! Don't use this function for streams + this.audio.src = audioUrl; + + if (!this.volumeOnStart) { + this.volumeOnStart = this.getVideoVolume(); } + this.setupAudioSettings(); switch (this.site.host) { case "twitter": document @@ -1968,7 +2131,7 @@ class VideoHandler { } // Define a function to translate a video and handle the callback - translateFunc( + async translateFunc( VIDEO_ID, isStream, requestLang, @@ -1976,159 +2139,63 @@ class VideoHandler { translationHelp, ) { console.log("[VOT] Video Data: ", this.videoData); - const videoURL = translationHelp?.url - ? translationHelp.url - : `${this.site.url}${VIDEO_ID}`; - // fix enabling the old requested voiceover when changing the language to the native language (#414) debug.log("Run videoValidator"); this.videoValidator(); + this.setLoadingBtn(true); if (isStream) { - debug.log("Executed stream translation"); - translateStream( - videoURL, + let translateRes = await this.translateStreamImpl( + this.videoData, requestLang, responseLang, - async (success, reqInterval, resOrError) => { - debug.log("[exec callback] translateStream callback"); - if (getVideoId(this.site.host, this.video) !== VIDEO_ID) return; - if (!success || !resOrError.translatedInfo) { - await this.updateTranslationErrorMsg(resOrError); - - if (reqInterval === 10) { - // if wait translating - clearTimeout(this.autoRetry); - this.autoRetry = setTimeout( - () => - this.translateFunc( - VIDEO_ID, - isStream, - requestLang, - responseLang, - translationHelp, - ), - reqInterval * 1000, - ); - } - - return; - } - - this.transformBtn( - "success", - localizationProvider.get("disableTranslate"), - ); - - console.log(resOrError); - const pingId = resOrError.pingId; - debug.log(`Stream pingId: ${pingId}`); - // if you don't make ping requests, then the translation of the stream dies - this.streamPing = setInterval( - async () => - await requestStreamPing(pingId, (result) => - debug.log("Stream ping result: ", result), - ), - reqInterval * 1000, - ); - - debug.log(resOrError.translatedInfo.url); - const streamURL = `https://${ - this.data.m3u8ProxyHost - }/?all=yes&origin=${encodeURIComponent( - "https://strm.yandex.ru", - )}&referer=${encodeURIComponent( - "https://strm.yandex.ru", - )}&url=${encodeURIComponent(resOrError.translatedInfo.url)}`; - debug.log(streamURL); - - if (this.hls) { - this.hls.on(Hls.Events.MEDIA_ATTACHED, function () { - debug.log("audio and hls.js are now bound together !"); - }); - this.hls.on(Hls.Events.MANIFEST_PARSED, function (data) { - debug.log( - "manifest loaded, found " + - data?.levels?.length + - " quality level", - ); - }); - this.hls.loadSource(streamURL); - this.hls.attachMedia(this.audio); - this.hls.on(Hls.Events.ERROR, function (data) { - if (data.fatal) { - switch (data.type) { - case Hls.ErrorTypes.MEDIA_ERROR: - console.log( - "fatal media error encountered, try to recover", - ); - this.hls.recoverMediaError(); - break; - case Hls.ErrorTypes.NETWORK_ERROR: - console.error("fatal network error encountered", data); - // All retries and media options have been exhausted. - // Immediately trying to restart loading could cause loop loading. - // Consider modifying loading policies to best fit your asset and network - // conditions (manifestLoadPolicy, playlistLoadPolicy, fragLoadPolicy). - break; - default: - // cannot recover - this.hls.destroy(); - break; - } - } - }); - debug.log(this.hls); - } else if (this.audio.canPlayType("application/vnd.apple.mpegurl")) { - // safari - this.audio.src = streamURL; - } else { - // browser doesn't support m3u8 (hls unsupported and it's not a safari) - throw new VOTLocalizedError("audioFormatNotSupported"); - } - - if (this.site.host === "youtube") { - youtubeUtils.videoSeek(this.video, 10); // 10 is the most successful number for streaming. With it, the audio is not so far behind the original - } - - this.volumeOnStart = this.getVideoVolume(); - if (typeof this.data.defaultVolume === "number") { - this.gainNode.gain.value = this.data.defaultVolume / 100; - } + ); - if ( - typeof this.data.autoSetVolumeYandexStyle === "number" && - this.data.autoSetVolumeYandexStyle - ) { - this.setVideoVolume(this.data.autoVolume); - } + if (!translateRes) { + debug.log("Skip translation"); + return; + } - if ( - !this.video.src && - !this.video.currentSrc && - !this.video.srcObject - ) { - this.stopTranslation(); - return; - } + this.transformBtn( + "success", + localizationProvider.get("disableTranslate"), + ); + + const streamURL = `https://${ + this.data.m3u8ProxyHost + }/?all=yes&origin=${encodeURIComponent( + "https://strm.yandex.ru", + )}&referer=${encodeURIComponent( + "https://strm.yandex.ru", + )}&url=${encodeURIComponent(translateRes.result.url)}`; + if (this.hls) { + this.setupHLS(streamURL); + } else if (this.audio.canPlayType("application/vnd.apple.mpegurl")) { + // safari + this.audio.src = streamURL; + } else { + // browser doesn't support m3u8 (hls unsupported and it isn't a safari) + throw new VOTLocalizedError("audioFormatNotSupported"); + } - if (this.video && !this.video.paused) this.lipSync("play"); - for (const e of videoLipSyncEvents) { - this.video.addEventListener(e, this.handleVideoEventBound); - } + if (this.site.host === "youtube") { + youtubeUtils.videoSeek(this.video, 10); // 10 is the most successful number for streaming. With it, the audio is not so far behind the original + } - this.afterUpdateTranslation(streamURL); - }, - ); + this.setupAudioSettings(); + if (!this.video.src && !this.video.currentSrc && !this.video.srcObject) { + return this.stopTranslation(); + } - return; - } + if (this.video && !this.video.paused) this.lipSync("play"); + for (const e of videoLipSyncEvents) { + this.video.addEventListener(e, this.handleVideoEventBound); + } - if (["udemy", "coursera"].includes(this.site.host) && !translationHelp) { - throw new VOTLocalizedError("VOTTranslationHelpNull"); + return this.afterUpdateTranslation(streamURL); } - const cachedTranslation = this.videoTranslations.find( + this.cachedTranslation = this.videoTranslations.find( (t) => t.videoId === VIDEO_ID && t.expires > Date.now() / 1000 && @@ -2136,77 +2203,102 @@ class VideoHandler { t.to === responseLang, ); - if (cachedTranslation) { - this.updateTranslation(cachedTranslation.url); - debug.log("[translateFunc] A cached translate was received"); + if (this.cachedTranslation) { + await this.updateTranslation(this.cachedTranslation.url); + debug.log("[translateFunc] Cached translation was received"); return; } - const timeoutDuration = this.subtitlesList.some( - (item) => item.source === "yandex", - ) - ? 20_000 - : 30_000; - - translateVideo( - videoURL, - this.videoData.duration, + let translateRes = await this.translateVideoImpl( + this.videoData, requestLang, responseLang, translationHelp, - async (success, urlOrError) => { - debug.log("[exec callback] translateVideo callback"); - if (getVideoId(this.site.host, this.video) !== VIDEO_ID) return; - if (!success) { - await this.updateTranslationErrorMsg(urlOrError); + ); - // if the error line contains information that the translation is being performed, then we wait - if ( - urlOrError.includes(localizationProvider.get("translationTake")) - ) { - clearTimeout(this.autoRetry); - this.autoRetry = setTimeout( - () => - this.translateFunc( - VIDEO_ID, - isStream, - requestLang, - responseLang, - translationHelp, - ), - timeoutDuration, - ); - } - console.error("[VOT]", urlOrError); - return; - } + debug.log("[translateRes]", translateRes); + if (!translateRes) { + debug.log("Skip translation"); + return; + } - this.updateTranslation(urlOrError); - if ( - !this.subtitlesList.some( - (item) => - item.source === "yandex" && - item.translatedFromLanguage === this.videoData.detectedLanguage && - item.language === this.videoData.responseLanguage, - ) - ) { - this.subtitlesList = await getSubtitles( - this.site, - this.videoData.videoId, - this.videoData.detectedLanguage, - ); - await this.updateSubtitlesLangSelect(); + await this.updateTranslation(translateRes.url); + + if ( + ![ + YandexType.VideoService.kick, + YandexType.VideoService.reddit, + YandexType.VideoService.patreon, + YandexType.VideoService.kodik, + YandexType.VideoService.appledeveloper, + ].includes(this.site.host) && + !this.subtitlesList.some( + (item) => + item.source === "yandex" && + item.translatedFromLanguage === this.videoData.detectedLanguage && + item.language === this.videoData.responseLanguage, + ) + ) { + this.subtitlesList = await getSubtitles(this.votClient, this.videoData); + await this.updateSubtitlesLangSelect(); + } + + this.videoTranslations.push({ + videoId: VIDEO_ID, + from: requestLang, + to: responseLang, + url: this.downloadTranslationUrl, + expires: Date.now() / 1000 + this.videoTranslationTTL, + }); + } + + // Вспомогательные методы + setupHLS(streamURL) { + this.hls.on(Hls.Events.MEDIA_ATTACHED, function () { + debug.log("audio and hls.js are now bound together !"); + }); + this.hls.on(Hls.Events.MANIFEST_PARSED, function (data) { + debug.log( + "manifest loaded, found " + data?.levels?.length + " quality level", + ); + }); + this.hls.loadSource(streamURL); + this.hls.attachMedia(this.audio); + this.hls.on(Hls.Events.ERROR, function (data) { + if (data.fatal) { + switch (data.type) { + case Hls.ErrorTypes.MEDIA_ERROR: + console.log("fatal media error encountered, try to recover"); + this.hls.recoverMediaError(); + break; + case Hls.ErrorTypes.NETWORK_ERROR: + console.error("fatal network error encountered", data); + // All retries and media options have been exhausted. + // Immediately trying to restart loading could cause loop loading. + // Consider modifying loading policies to best fit your asset and network + // conditions (manifestLoadPolicy, playlistLoadPolicy, fragLoadPolicy). + break; + default: + // cannot recover + this.hls.destroy(); + break; } + } + }); + debug.log(this.hls); + } - this.videoTranslations.push({ - videoId: VIDEO_ID, - from: requestLang, - to: responseLang, - url: urlOrError, - expires: Date.now() / 1000 + this.videoTranslationTTL, - }); - }, - ); + setupAudioSettings() { + if (typeof this.data.defaultVolume === "number") { + this.gainNode.gain.value = this.data.defaultVolume / 100; + } + + if ( + typeof this.data.autoSetVolumeYandexStyle === "number" && + this.data.autoSetVolumeYandexStyle + ) { + this.setVideoVolume(this.data.autoVolume); + } } // Define a function to stop translation and clean up @@ -2256,33 +2348,16 @@ class VideoHandler { } } -function getSites() { - const hostname = window.location.hostname; - const currentURL = new URL(window.location); - - const isMathes = (match) => { - if (match instanceof RegExp) { - return match.test(hostname); - } else if (typeof match === "string") { - return hostname.includes(match); - } else if (typeof match === "function") { - return match(currentURL); - } - return false; - }; - - return sites.filter((e) => { - return ( - (Array.isArray(e.match) ? e.match.some(isMathes) : isMathes(e.match)) && - e.host && - e.url - ); - }); -} - const videoObserver = new VideoObserver(); const videosWrappers = new WeakMap(); +/** + * Finds the container element for a given video element and site object. + * + * @param {Object} site - The site object. + * @param {Object} video - The video element. + * @return {Object|null} The container element or null if not found. + */ function findContainer(site, video) { if (site.shadowRoot) { let container = site.selector @@ -2293,29 +2368,29 @@ function findContainer(site, video) { return container && container.shadowRoot ? container.parentElement : container; - } else { - const browserVersion = browserInfo.browser.version.split(".")[0]; - if ( - site.selector?.includes(":not") && - site.selector?.includes("*") && - browserVersion && - ((browserInfo.browser.name === "Chrome" && Number(browserVersion) < 88) || - (browserInfo.browser.name === "Firefox" && Number(browserVersion) < 84)) - ) { - const selector = site.selector.split(" *")[0]; - return selector - ? Array.from(document.querySelectorAll(selector)).find((e) => - e.contains(video), - ) - : video.parentElement; - } else { - return site.selector - ? Array.from(document.querySelectorAll(site.selector)).find((e) => - e.contains(video), - ) - : video.parentElement; - } } + + const browserVersion = browserInfo.browser.version.split(".")[0]; + if ( + site.selector?.includes(":not") && + site.selector?.includes("*") && + browserVersion && + ((browserInfo.browser.name === "Chrome" && Number(browserVersion) < 88) || + (browserInfo.browser.name === "Firefox" && Number(browserVersion) < 84)) + ) { + const selector = site.selector.split(" *")[0]; + return selector + ? Array.from(document.querySelectorAll(selector)).find((e) => + e.contains(video), + ) + : video.parentElement; + } + + return site.selector + ? Array.from(document.querySelectorAll(site.selector)).find((e) => + e.contains(video), + ) + : video.parentElement; } async function main() { @@ -2325,23 +2400,8 @@ async function main() { debug.log(`Selected menu language: ${localizationProvider.lang}`); - if ( - BUILD_MODE !== "cloudflare" && - GM_info?.scriptHandler && - cfOnlyExtensions.includes(GM_info.scriptHandler) - ) { - console.error( - `[VOT] ${localizationProvider.getDefault("unSupportedExtensionError").replace("{0}", GM_info.scriptHandler)}`, - ); - return alert( - `[VOT] ${localizationProvider.get("unSupportedExtensionError").replace("{0}", GM_info.scriptHandler)}`, - ); - } - - debug.log("Extension compatibility passed..."); - videoObserver.onVideoAdded.addListener((video) => { - for (const site of getSites()) { + for (const site of getService()) { if (!site) continue; let container = findContainer(site, video); @@ -2381,16 +2441,3 @@ async function main() { main().catch((e) => { console.error("[VOT]", e); }); - -// if (import.meta.webpackHot) { -// import.meta.webpackHot.monkeyReload(); -// import.meta.webpackHot.dispose(() => { -// for (const selector of [ -// ".vot-menu", -// ".vot-segmented-button", -// ".vot-subtitles-widget", -// ]) { -// document.querySelector(selector)?.remove(); -// } -// }); -// } diff --git a/src/localization/locales/af.json b/src/localization/locales/af.json index f89ccba7..559272a7 100644 --- a/src/localization/locales/af.json +++ b/src/localization/locales/af.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Kon nie video-vertaling aanvra nie", "audioNotReceived": "Oudio skakel nie ontvang nie", "grantPermissionToAutoPlay": "Verleen toestemming om autoplay", - "neededAdditionalExtension": "'n bykomende uitbreiding is nodig om hierdie webwerf te ondersteun", "audioFormatNotSupported": "Die oudioformaat word nie ondersteun nie", "VOTAutoTranslate": "Vertaal op open", "VOTDontTranslateYourLang": "Moenie uit my taal vertaal nie", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zoeloe" }, - "udemyAccessTokenExpired": "Jou ingevoerde Udemy-Toegangsteken het verstryk", "udemyModuleArgsNotFound": "Kon nie udemy module data kry nie as gevolg van Die feit dat ModuleArgs nie gevind is nie", "VOTTranslationHelpNull": "Kon nie die nodige data vir die vertaling kry nie", - "enterUdemyAccessToken": "Tik Udemy Toegang Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Daar is geen verbinding met die bediener nie", "searchField": "Soek...", "VOTTranslateAPIErrors": "Vertaal foute UIT DIE API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Voer die adres van die m3u8-volmagwerker in", "proxySettings": "Instaaninstellings", "translationTakeApproximatelyMinute2": "Die vertaling sal ongeveer {0} minute neem", - "VOTAudioBooster": "Uitgebreide vertaalvolume-verhoging" + "VOTAudioBooster": "Uitgebreide vertaalvolume-verhoging", + "VOTMediaCSPError": "Kon nie klank laai nie (media csp-fout)", + "VOTSubtitlesDesign": "Onderskrifte ontwerp", + "VOTSubtitlesFontSize": "Lettergrootte van onderskrifte", + "VOTSubtitlesOpacity": "Deursigtigheid van die ondertitel agtergrond" } diff --git a/src/localization/locales/am.json b/src/localization/locales/am.json index 1608b632..7ca8e41a 100644 --- a/src/localization/locales/am.json +++ b/src/localization/locales/am.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "የቪዲዮ ትርጉም መጠየቅ አልተሳካም", "audioNotReceived": "የድምፅ አገናኝ አልተቀበለም", "grantPermissionToAutoPlay": "ለአውቶፕሌይ ፈቃድ ይስጡ", - "neededAdditionalExtension": "ይህንን ጣቢያ ለመደገፍ ተጨማሪ ቅጥያ ያስፈልጋል ። ", "audioFormatNotSupported": "የድምጽ ቅርጸት አይደገፍም", "VOTAutoTranslate": "ክፈት ላይ መተርጎም", "VOTDontTranslateYourLang": "ከቋንቋዬ አትበል", @@ -177,11 +176,8 @@ "yo": "ዮሩባ", "zu": "ዙሉ" }, - "udemyAccessTokenExpired": "የኢትዮ ቴሌኮም ዋና ሥራ አስፈጻሚ ከኃላፊነታቸው ለቀቁ", "udemyModuleArgsNotFound": "ሞጁሎች ባለመገኘታቸው ምክንያት የኡዲሚ ሞዱል ውሂብ ማግኘት አልተቻለም ። ", "VOTTranslationHelpNull": "ለመተርጎም የሚያስፈልገውን መረጃ ማግኘት አልተቻለም", - "enterUdemyAccessToken": "የኡዲሚ መዳረሻ ማስመሰያ ያስገቡ", - "VOTUdemyData": "የተጠቃሚዎች በይነ-ተገናኝነት", "streamNoConnectionToServer": "ከአገልጋዩ ጋር ምንም ግንኙነት የለም ። ", "searchField": "Search።..", "VOTTranslateAPIErrors": "ከ API ስህተቶችን ይተረጉሙ", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "የ m3u8 ተኪ ሰራተኛ አድራሻ ያስገቡ", "proxySettings": "የተኪ ቅንብሮች", "translationTakeApproximatelyMinute2": "ትርጉሙ በግምት ይወስዳል {0} ደቂቃዎች", - "VOTAudioBooster": "የተራዘመ የትርጉም መጠን መጨመር" + "VOTAudioBooster": "የተራዘመ የትርጉም መጠን መጨመር", + "VOTMediaCSPError": "ኦዲዮ መጫን አልተሳካም (ሚዲያ csp ስህተት)", + "VOTSubtitlesDesign": "ንዑስ ርዕሶች ንድፍ", + "VOTSubtitlesFontSize": "የትርጉም ጽሑፎች ቅርጸ-ቁምፊ መጠን", + "VOTSubtitlesOpacity": "የትርጉም ዳራ ግልፅነት" } diff --git a/src/localization/locales/ar.json b/src/localization/locales/ar.json index 03c5f209..46a7711e 100644 --- a/src/localization/locales/ar.json +++ b/src/localization/locales/ar.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "فشل طلب ترجمة الفيديو", "audioNotReceived": "لم يتم استلام رابط الصوت", "grantPermissionToAutoPlay": "السماح بالتشغيل التلقائي", - "neededAdditionalExtension": "هناك حاجة إلى امتداد إضافي لدعم هذا الموقع", "audioFormatNotSupported": "تنسيق الصوت غير مدعوم", "VOTAutoTranslate": "الترجمة عند الفتح", "VOTDontTranslateYourLang": "لا تترجم من لغتي", @@ -177,11 +176,8 @@ "haw": "لغة هاواي", "kri": "Kri" }, - "udemyAccessTokenExpired": "انتهت صلاحية رمز الوصول الخاص بك الذي تم إدخاله", "udemyModuleArgsNotFound": "لا يمكن الحصول على بيانات وحدة أوديمي يرجع ذلك إلى حقيقة أن مودوليرجس لم يتم العثور على", "VOTTranslationHelpNull": "تعذر الحصول على البيانات المطلوبة للترجمة", - "enterUdemyAccessToken": "أدخل رمز الوصول أوديمي", - "VOTUdemyData": "بيانات أوديمي", "streamNoConnectionToServer": "لا يوجد اتصال بالخادم", "searchField": "بحث...", "VOTTranslateAPIErrors": "ترجمة الأخطاء من واجهة برمجة التطبيقات", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "أدخل عنوان عامل الوكيل إم 3 يو 8", "proxySettings": "إعدادات الوكيل", "translationTakeApproximatelyMinute2": "ستستغرق الترجمة حوالي {0} دقيقة", - "VOTAudioBooster": "زيادة حجم الترجمة الموسعة" + "VOTAudioBooster": "زيادة حجم الترجمة الموسعة", + "VOTMediaCSPError": "فشل تحميل الصوت (خطأ في الطاقة الشمسية المركزة في الوسائط)", + "VOTSubtitlesDesign": "تصميم الترجمة", + "VOTSubtitlesFontSize": "حجم الخط من ترجمات", + "VOTSubtitlesOpacity": "شفافية خلفية العنوان الفرعي" } diff --git a/src/localization/locales/az.json b/src/localization/locales/az.json index c17d52d4..a1826742 100644 --- a/src/localization/locales/az.json +++ b/src/localization/locales/az.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Videonun tərcüməsini tələb etmək mümkün olmadı", "audioNotReceived": "Səs bağlantısı alınmadı", "grantPermissionToAutoPlay": "Avtomatik oynatmağa icazə verin", - "neededAdditionalExtension": "Bu saytı dəstəkləmək üçün əlavə bir uzantı lazımdır", "audioFormatNotSupported": "Audio format dəstəklənmir", "VOTAutoTranslate": "Açıq tərcümə", "VOTDontTranslateYourLang": "Dilimdən tərcümə etmə", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Daxil etdiyiniz Udemy giriş tokeninin müddəti bitdi", "udemyModuleArgsNotFound": "Moduleargs tapılmadığı üçün udemy modulunun məlumatlarını əldə etmək mümkün olmadı", "VOTTranslationHelpNull": "Tərcümə üçün lazım olan məlumatları əldə etmək mümkün olmadı", - "enterUdemyAccessToken": "Udemy giriş tokenini daxil edin", - "VOTUdemyData": "Udemy Məlumatları", "streamNoConnectionToServer": "Server bağlantısı yoxdur", "searchField": "Axtarış...", "VOTTranslateAPIErrors": "API-dən səhvlərin tərcüməsi", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 işləyən proxy server ünvanını daxil edin", "proxySettings": "Proxy server parametrləri", "translationTakeApproximatelyMinute2": "Tərcümə təxminən {0} dəqiqə çəkəcək", - "VOTAudioBooster": "Genişləndirilmiş tərcümə həcminin artırılması" + "VOTAudioBooster": "Genişləndirilmiş tərcümə həcminin artırılması", + "VOTMediaCSPError": "Audio yükləyə bilmədi (media csp xətası)", + "VOTSubtitlesDesign": "Altyazı dizaynı", + "VOTSubtitlesFontSize": "Altyazı şrift ölçüsü", + "VOTSubtitlesOpacity": "Altyazı fon şəffaflığı" } diff --git a/src/localization/locales/bg.json b/src/localization/locales/bg.json index b78c345e..a230f37c 100644 --- a/src/localization/locales/bg.json +++ b/src/localization/locales/bg.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Неуспешно искане за превод на видео", "audioNotReceived": "Аудио връзката не е получена", "grantPermissionToAutoPlay": "Даване на разрешение за автоматично изпълнение", - "neededAdditionalExtension": "Необходимо е допълнително разширение за поддръжка на този сайт", "audioFormatNotSupported": "Аудио форматът не се поддържа", "VOTAutoTranslate": "Превод на отворен", "VOTDontTranslateYourLang": "Не превеждайте от моя език", @@ -177,11 +176,8 @@ "yo": "Йоруба", "zu": "Зулу" }, - "udemyAccessTokenExpired": "Вашият въведен жетон за достъп е изтекъл", "udemyModuleArgsNotFound": "Не може да се получи информация за модула поради факта, че модулът не е намерен", "VOTTranslationHelpNull": "Грешка при получаване на данните, необходими за превода", - "enterUdemyAccessToken": "Въведете Жетон За Достъп", - "VOTUdemyData": "Данни Udemy", "streamNoConnectionToServer": "Няма връзка със сървъра", "searchField": "Търси...", "VOTTranslateAPIErrors": "Грешки при превод от АПИ", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Въведете адреса на прокси служителя м3у8", "proxySettings": "Настройки На Прокси Сървъра", "translationTakeApproximatelyMinute2": "Преводът ще отнеме около {0} минути", - "VOTAudioBooster": "Увеличаване на обема на превода" + "VOTAudioBooster": "Увеличаване на обема на превода", + "VOTMediaCSPError": "Грешка при зареждане на аудиото (грешка при създаване на аудио файл)", + "VOTSubtitlesDesign": "Субтитри дизайн", + "VOTSubtitlesFontSize": "Размер на шрифта на субтитрите", + "VOTSubtitlesOpacity": "Прозрачност на фона на субтитрите" } diff --git a/src/localization/locales/bn.json b/src/localization/locales/bn.json index 9f17fb79..65de90f7 100644 --- a/src/localization/locales/bn.json +++ b/src/localization/locales/bn.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "ভিডিও অনুবাদের অনুরোধ ব্যর্থ হয়েছে", "audioNotReceived": "অডিও লিঙ্ক পাওয়া যায়নি", "grantPermissionToAutoPlay": "অটো প্লে এর অনুমতি দিন", - "neededAdditionalExtension": "এই সাইটটি সমর্থনের জন্য অতিরিক্ত এক্সটেনশান প্রয়োজন", "audioFormatNotSupported": "অডিও ফরম্যাটটি সমর্থিত নয়", "VOTAutoTranslate": "খোলার সময় অনুবাদ করুন", "VOTDontTranslateYourLang": "নিজের ভাষা অনুবাদ না করুন", @@ -177,11 +176,8 @@ "ja": "জাপানি", "kri": "ক্রি" }, - "udemyAccessTokenExpired": "আপনার প্রবেশ করা উডেমি অ্যাক্সেস টোকেনের মেয়াদ শেষ হয়ে গেছে", "udemyModuleArgsNotFound": "মডিউলআর্গস পাওয়া যায়নি বলে উডেমি মডিউল ডেটা পাওয়া যায়নি", "VOTTranslationHelpNull": "অনুবাদের জন্য প্রয়োজনীয় তথ্য পাওয়া যায়নি", - "enterUdemyAccessToken": "উদেমি অ্যাক্সেস টোকেন লিখুন", - "VOTUdemyData": "উদেমি ডেটা", "streamNoConnectionToServer": "সার্ভারের সাথে কোন সংযোগ নেই", "searchField": "অনুসন্ধান...", "VOTTranslateAPIErrors": "এপিআই থেকে ত্রুটিগুলি অনুবাদ করুন", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "এম 3 ইউ 8 প্রক্সি কর্মীর ঠিকানা লিখুন", "proxySettings": "প্রক্সি সেটিংস", "translationTakeApproximatelyMinute2": "অনুবাদটি প্রায় {0} মিনিট সময় নেবে", - "VOTAudioBooster": "বর্ধিত অনুবাদ ভলিউম বৃদ্ধি" + "VOTAudioBooster": "বর্ধিত অনুবাদ ভলিউম বৃদ্ধি", + "VOTMediaCSPError": "অডিও লোড করতে ব্যর্থ (মিডিয়া সিএসপি ত্রুটি)", + "VOTSubtitlesDesign": "সাবটাইটেল ডিজাইন", + "VOTSubtitlesFontSize": "সাবটাইটেলের ফন্টের আকার", + "VOTSubtitlesOpacity": "সাবটাইটেল পটভূমির স্বচ্ছতা" } diff --git a/src/localization/locales/bs.json b/src/localization/locales/bs.json index 84c69689..5fbbd5aa 100644 --- a/src/localization/locales/bs.json +++ b/src/localization/locales/bs.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Neuspjela prijava.", "audioNotReceived": "Audio link nije primljen", "grantPermissionToAutoPlay": "Grant dozvolu za autoplay", - "neededAdditionalExtension": "Za podršku ovoj web stranici potrebno je dodatno proširenje", "audioFormatNotSupported": "Audio format nije podržan", "VOTAutoTranslate": "Prevedi na open", "VOTDontTranslateYourLang": "Ne prevodite sa mog jezika", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Vaš uneseni Udemy Token za pristup je istekao", "udemyModuleArgsNotFound": "Nisam mogao dobaviti podatke udemy modula zbog činjenice da ModuleArgs nije pronađen", "VOTTranslationHelpNull": "Nisam mogao dobaviti podatke potrebne za prijevod", - "enterUdemyAccessToken": "Unesite Udemy Token Za Pristup", - "VOTUdemyData": "Udemy Podaci", "streamNoConnectionToServer": "Nema veze sa serverom", "searchField": "Traži...", "VOTTranslateAPIErrors": "Prevedi greške iz API-ja", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Unesite adresu m3u8 proxy radnika", "proxySettings": "_postavke ... ", "translationTakeApproximatelyMinute2": "Prevod će trajati otprilike {0} minuta", - "VOTAudioBooster": "Prošireno povećanje obima prevođenja" + "VOTAudioBooster": "Prošireno povećanje obima prevođenja", + "VOTMediaCSPError": "Neuspješno učitavanje zvuka (media csp greška)", + "VOTSubtitlesDesign": "Subtitles design", + "VOTSubtitlesFontSize": "Veličina titlova", + "VOTSubtitlesOpacity": "Transparentnost pozadine titlova" } diff --git a/src/localization/locales/ca.json b/src/localization/locales/ca.json index 38f2c3b7..1cd04aa7 100644 --- a/src/localization/locales/ca.json +++ b/src/localization/locales/ca.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Ha fallat en demanar la traducció de vídeo", "audioNotReceived": "No s 'ha rebut l' enllaç d ' àudio", "grantPermissionToAutoPlay": "Concediu permís per reproduir automàticament", - "neededAdditionalExtension": "Es necessita una extensió addicional per donar suport a aquest lloc", "audioFormatNotSupported": "El format d'àudio no és compatible", "VOTAutoTranslate": "Tradueix en obert", "VOTDontTranslateYourLang": "No tradueixis de la meva llengua", @@ -177,11 +176,8 @@ "yo": "Ioruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "El Vostre Testimoni d'Accés a Udemy introduït ha caducat", "udemyModuleArgsNotFound": "No s'han pogut obtenir dades del mòdul udemy a causa del Fet Que No S'ha trobat ModuleArgs", "VOTTranslationHelpNull": "No s ' han pogut obtenir les dades necessàries per a la traducció", - "enterUdemyAccessToken": "Introduïu El Testimoni D ' Accés A Udemy", - "VOTUdemyData": "Dades Udemy", "streamNoConnectionToServer": "No hi ha connexió amb el servidor", "searchField": "Cerca...", "VOTTranslateAPIErrors": "Traduir errors des DE L'API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Introduïu l'adreça del treballador intermediari m3u8", "proxySettings": "Arranjament Del Servidor Intermediari", "translationTakeApproximatelyMinute2": "La traducció trigarà aproximadament {0} minuts", - "VOTAudioBooster": "Ampliació del volum de traducció" + "VOTAudioBooster": "Ampliació del volum de traducció", + "VOTMediaCSPError": "Ha fallat en carregar l ' àudio (error media csp)", + "VOTSubtitlesDesign": "Disseny de subtítols", + "VOTSubtitlesFontSize": "Mida del tipus de lletra dels subtítols", + "VOTSubtitlesOpacity": "Transparència del fons dels subtítols" } diff --git a/src/localization/locales/cs.json b/src/localization/locales/cs.json index 3a7d6557..3a11914d 100644 --- a/src/localization/locales/cs.json +++ b/src/localization/locales/cs.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nepodařilo se požádat o překlad videa", "audioNotReceived": "Nepřišel odkaz na zvuk", "grantPermissionToAutoPlay": "Povolte automatické přehrávání", - "neededAdditionalExtension": "Pro podporu této stránky je potřeba další rozšíření", "audioFormatNotSupported": "Formát zvuku není podporován", "VOTAutoTranslate": "Překládat automaticky při otevření", "VOTDontTranslateYourLang": "Nepřekládat z vašeho jazyka", @@ -177,11 +176,8 @@ "ja": "Japonština", "kri": "Krio" }, - "udemyAccessTokenExpired": "Platnost zadaného přístupového tokenu Udemy vypršela", "udemyModuleArgsNotFound": "Nelze získat data Udemy modulu Vzhledem k tomu, že ModuleArgs nebyl nalezen", "VOTTranslationHelpNull": "Nelze získat data potřebná pro překlad", - "enterUdemyAccessToken": "Zadejte Udemy Přístupový Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Neexistuje žádné připojení k serveru", "searchField": "Vyhledávání...", "VOTTranslateAPIErrors": "Přeložit chyby z API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Zadejte adresu pracovníka proxy m3u8", "proxySettings": "Nastavení Serveru Proxy", "translationTakeApproximatelyMinute2": "Překlad bude trvat přibližně {0} minut", - "VOTAudioBooster": "Rozšíření objemu překladu" + "VOTAudioBooster": "Rozšíření objemu překladu", + "VOTMediaCSPError": "Nepodařilo se načíst zvuk (chyba media CSP)", + "VOTSubtitlesDesign": "Titulky design", + "VOTSubtitlesFontSize": "Velikost písma titulků", + "VOTSubtitlesOpacity": "Průhlednost pozadí titulků" } diff --git a/src/localization/locales/cy.json b/src/localization/locales/cy.json index b76e4bde..3849cee8 100644 --- a/src/localization/locales/cy.json +++ b/src/localization/locales/cy.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Methu gofyn am gyfieithiad fideo", "audioNotReceived": "Ni dderbyniwyd y cyswllt sain", "grantPermissionToAutoPlay": "Rhoi caniatâd i awto-chwarae", - "neededAdditionalExtension": "Mae angen estyniad ychwanegol i gefnogi'r safle hwn", "audioFormatNotSupported": "Ni chynhelir y fformat sain", "VOTAutoTranslate": "Cyfieithu ar agor", "VOTDontTranslateYourLang": "Peidiwch â chyfieithu o fy iaith", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Mae Eich Tocyn Mynediad Udemy a gofnodwyd wedi dod i ben", "udemyModuleArgsNotFound": "Methwyd cael data modiwl udemy oherwydd y ffaith na chanfuwyd ModuleArgs", "VOTTranslationHelpNull": "Methu cael y data sydd ei angen ar gyfer y cyfieithiad", - "enterUdemyAccessToken": "Rhowch Docyn Mynediad Udemy", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Nid oes cysylltiad i'r defnyddiwr", "searchField": "Chwilio...", "VOTTranslateAPIErrors": "Cyfieithu gwallau O'R API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Rhowch gyfeiriad y gweithiwr dirprwy m3u8", "proxySettings": "Gosodiadau Dirprwy", "translationTakeApproximatelyMinute2": "Bydd y cyfieithiad yn cymryd oddeutu {0} munud", - "VOTAudioBooster": "Cynyddu cyfaint cyfieithu estynedig" + "VOTAudioBooster": "Cynyddu cyfaint cyfieithu estynedig", + "VOTMediaCSPError": "Methu llwytho sain (gwall csp cyfrwng)", + "VOTSubtitlesDesign": "Isdeitlau dylunio", + "VOTSubtitlesFontSize": "Maint ffont isdeitlau", + "VOTSubtitlesOpacity": "Tryloywder cefndir yr is-deitl" } diff --git a/src/localization/locales/da.json b/src/localization/locales/da.json index 0719558b..d5855e72 100644 --- a/src/localization/locales/da.json +++ b/src/localization/locales/da.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Kunne ikke anmode om videooversættelse", "audioNotReceived": "Audio link ikke modtaget", "grantPermissionToAutoPlay": "Giv tilladelse til autoplay", - "neededAdditionalExtension": "En ekstra udvidelse er nødvendig for at understøtte dette site", "audioFormatNotSupported": "Lydformatet understøttes ikke", "VOTAutoTranslate": "Oversæt på åben", "VOTDontTranslateYourLang": "Oversæt ikke fra mit sprog", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Sulu" }, - "udemyAccessTokenExpired": "Dit indtastede Udemy Access Token er udløbet", "udemyModuleArgsNotFound": "Kunne ikke få Udemy-moduldata på grund af det faktum, at ModuleArgs ikke blev fundet", "VOTTranslationHelpNull": "Kunne ikke få de nødvendige data til oversættelsen", - "enterUdemyAccessToken": "Indtast Udemy Access Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Der er ingen forbindelse til serveren", "searchField": "Søge...", "VOTTranslateAPIErrors": "Oversæt fejl fra API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Indtast adressen på M3U8 fuldmægtig", "proxySettings": "Fuldmagtsindstillinger", "translationTakeApproximatelyMinute2": "Oversættelsen tager cirka {0} minutter", - "VOTAudioBooster": "Udvidet oversættelse volumen stigning" + "VOTAudioBooster": "Udvidet oversættelse volumen stigning", + "VOTMediaCSPError": "Kunne ikke indlæse lyd (media CSP-fejl)", + "VOTSubtitlesDesign": "Undertekster design", + "VOTSubtitlesFontSize": "Skriftstørrelse på undertekster", + "VOTSubtitlesOpacity": "Gennemsigtighed i undertekstbaggrunden" } diff --git a/src/localization/locales/de.json b/src/localization/locales/de.json index e7869715..8a3ed867 100644 --- a/src/localization/locales/de.json +++ b/src/localization/locales/de.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Videoübersetzung konnte nicht angefordert werden", "audioNotReceived": "Audiolink nicht empfangen", "grantPermissionToAutoPlay": "Erteilen Sie die Berechtigung zur automatischen Wiedergabe", - "neededAdditionalExtension": "Eine zusätzliche Erweiterung ist erforderlich, um diese Website zu unterstützen", "audioFormatNotSupported": "Das Audioformat wird nicht unterstützt", "VOTAutoTranslate": "Beim Öffnen übersetzen", "VOTDontTranslateYourLang": "Nicht aus Ihrer Sprache übersetzen", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Dein eingegebenes Udemy-Zugriffstoken ist abgelaufen", "udemyModuleArgsNotFound": "Udemy-Moduldaten konnten nicht abgerufen werden, da Modulargs nicht gefunden wurde", "VOTTranslationHelpNull": "Die für die Übersetzung erforderlichen Daten konnten nicht abgerufen werden", - "enterUdemyAccessToken": "Geben Sie das Udemy-Zugriffstoken ein", - "VOTUdemyData": "Udemy-Daten", "streamNoConnectionToServer": "Es besteht keine Verbindung zum Server", "searchField": "Suche...", "VOTTranslateAPIErrors": "Fehler aus der API übersetzen", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Geben Sie die Adresse des m3u8-Proxy-Mitarbeiters ein", "proxySettings": "Proxyeinstellungen", "translationTakeApproximatelyMinute2": "Die Übersetzung dauert ungefähr {0} Minuten", - "VOTAudioBooster": "Erhöhung des erweiterten Übersetzungsvolumens" + "VOTAudioBooster": "Erhöhung des erweiterten Übersetzungsvolumens", + "VOTMediaCSPError": "Audio konnte nicht geladen werden (Medien-CSP-Fehler)", + "VOTSubtitlesDesign": "Untertitel-Design", + "VOTSubtitlesFontSize": "Schriftgröße der Untertitel", + "VOTSubtitlesOpacity": "Transparenz des Untertitelhintergrunds" } diff --git a/src/localization/locales/el.json b/src/localization/locales/el.json index 2384b2a3..5744f26e 100644 --- a/src/localization/locales/el.json +++ b/src/localization/locales/el.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Αποτυχία αίτησης μετάφρασης βίντεο", "audioNotReceived": "Δεν έχει ληφθεί σύνδεσμος ήχου", "grantPermissionToAutoPlay": "Χορήγηση άδειας για αυτόματη αναπαραγωγή", - "neededAdditionalExtension": "Απαιτείται πρόσθετη επέκταση για την υποστήριξη αυτού του ιστότοπου", "audioFormatNotSupported": "Η μορφή ήχου δεν υποστηρίζεται", "VOTAutoTranslate": "Μετάφραση σε ανοιχτό", "VOTDontTranslateYourLang": "Μην μεταφράζετε από τη γλώσσα μου", @@ -177,11 +176,8 @@ "yo": "Γιορούμπα", "zu": "Ζουλού" }, - "udemyAccessTokenExpired": "Το διακριτικό πρόσβασης Udemy που έχετε εισαγάγει έχει λήξει", "udemyModuleArgsNotFound": "Δεν ήταν δυνατή η λήψη δεδομένων μονάδας udemy λόγω του γεγονότος ότι το ModuleArgs δεν βρέθηκε", "VOTTranslationHelpNull": "Δεν ήταν δυνατή η λήψη των δεδομένων που απαιτούνται για τη μετάφραση", - "enterUdemyAccessToken": "Εισαγάγετε Το Διακριτικό Πρόσβασης Udemy", - "VOTUdemyData": "Δεδομένα Udemy", "streamNoConnectionToServer": "Δεν υπάρχει σύνδεση με το διακομιστή", "searchField": "Αναζήτηση...", "VOTTranslateAPIErrors": "Μετάφραση σφαλμάτων από το API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Εισαγάγετε τη διεύθυνση του διακομιστή μεσολάβησης m3u8", "proxySettings": "Ρυθμίσεις Διακομιστή Μεσολάβησης", "translationTakeApproximatelyMinute2": "Η μετάφραση θα διαρκέσει περίπου {0} λεπτά", - "VOTAudioBooster": "Εκτεταμένη αύξηση όγκου μετάφρασης" + "VOTAudioBooster": "Εκτεταμένη αύξηση όγκου μετάφρασης", + "VOTMediaCSPError": "Αποτυχία φόρτωσης ήχου (σφάλμα CSP πολυμέσων)", + "VOTSubtitlesDesign": "Σχεδιασμός υπότιτλων", + "VOTSubtitlesFontSize": "Μέγεθος γραμματοσειράς υπότιτλων", + "VOTSubtitlesOpacity": "Διαφάνεια του φόντου υποτίτλων" } diff --git a/src/localization/locales/en.json b/src/localization/locales/en.json index e3fa5300..703eb1e2 100644 --- a/src/localization/locales/en.json +++ b/src/localization/locales/en.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Failed to request video translation", "audioNotReceived": "Audio link not received", "grantPermissionToAutoPlay": "Grant permission to autoplay", - "neededAdditionalExtension": "An additional extension is needed to support this site", "audioFormatNotSupported": "The audio format is not supported", "VOTAutoTranslate": "Translate on open", "VOTDontTranslateYourLang": "Do not translate from my language", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Your entered Udemy Access Token has expired", "udemyModuleArgsNotFound": "Could not get udemy module data due to the fact that ModuleArgs was not found", "VOTTranslationHelpNull": "Could not get the data required for the translate", - "enterUdemyAccessToken": "Enter Udemy Access Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "There is no connection to the server", "searchField": "Search...", "VOTTranslateAPIErrors": "Translate errors from the API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Enter the address of the m3u8 proxy worker", "proxySettings": "Proxy Settings", "translationTakeApproximatelyMinute2": "The translation will take approximately {0} minutes", - "VOTAudioBooster": "Extended translation volume increase" + "VOTAudioBooster": "Extended translation volume increase", + "VOTMediaCSPError": "Failed to load audio (media csp error)", + "VOTSubtitlesDesign": "Subtitles design", + "VOTSubtitlesFontSize": "Font size of subtitles", + "VOTSubtitlesOpacity": "Transparency of the subtitle background" } diff --git a/src/localization/locales/es.json b/src/localization/locales/es.json index 505176f2..a68ac85d 100644 --- a/src/localization/locales/es.json +++ b/src/localization/locales/es.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Error al solicitar la traducción de vídeo", "audioNotReceived": "Enlace de audio no recibido", "grantPermissionToAutoPlay": "Conceder permiso para la reproducción automática", - "neededAdditionalExtension": "Se necesita una extensión adicional para admitir este sitio", "audioFormatNotSupported": "El formato de audio no es compatible", "VOTAutoTranslate": "Traducir en abierto", "VOTDontTranslateYourLang": "No traduzcas de mi idioma", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulú" }, - "udemyAccessTokenExpired": "El Token de acceso de Udemy que has introducido ha caducado", "udemyModuleArgsNotFound": "No se pudieron obtener los datos del módulo Udemy debido a que no se encontraron ModuleArgs", "VOTTranslationHelpNull": "No se pudieron obtener los datos necesarios para la traducción", - "enterUdemyAccessToken": "Introduce el Token de Acceso de Udemy", - "VOTUdemyData": "Datos de Udemy", "streamNoConnectionToServer": "No hay conexión con el servidor", "searchField": "Búsqueda...", "VOTTranslateAPIErrors": "Traducir errores de la API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Ingrese la dirección del trabajador proxy m3u8", "proxySettings": "Configuración de Proxy", "translationTakeApproximatelyMinute2": "La traducción tardará aproximadamente {0} minutos", - "VOTAudioBooster": "Aumento del volumen de traducción extendida" + "VOTAudioBooster": "Aumento del volumen de traducción extendida", + "VOTMediaCSPError": "Error al cargar audio (error de csp de medios)", + "VOTSubtitlesDesign": "Diseño de subtítulos", + "VOTSubtitlesFontSize": "Tamaño de fuente de los subtítulos", + "VOTSubtitlesOpacity": "Transparencia del fondo de los subtítulos" } diff --git a/src/localization/locales/et.json b/src/localization/locales/et.json index 1eeca0d2..c297299b 100644 --- a/src/localization/locales/et.json +++ b/src/localization/locales/et.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Videotõlke taotlemine nurjus", "audioNotReceived": "Audio link ei saanud", "grantPermissionToAutoPlay": "Andke luba automaatseks esitamiseks", - "neededAdditionalExtension": "Selle saidi toetamiseks on vaja täiendavat laiendust", "audioFormatNotSupported": "Helivormingut ei toetata", "VOTAutoTranslate": "Tõlgi avatud", "VOTDontTranslateYourLang": "Ära tõlgi minu keelest", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Teie sisestatud Udemy juurdepääsuluba on aegunud", "udemyModuleArgsNotFound": "Udemy mooduli andmeid ei õnnestunud saada, kuna Moduleargsi ei leitud", "VOTTranslationHelpNull": "Tõlkimiseks vajalikke andmeid ei õnnestunud saada", - "enterUdemyAccessToken": "Sisestage Udemy Juurdepääsuluba", - "VOTUdemyData": "Udemy Andmed", "streamNoConnectionToServer": "Serveriga pole ühendust", "searchField": "Otsing...", "VOTTranslateAPIErrors": "Tõlgi vead API-st", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Sisestage m3u8 puhverserveri töötaja aadress", "proxySettings": "Puhverserveri Seaded", "translationTakeApproximatelyMinute2": "Tõlge võtab umbes {0} minutit", - "VOTAudioBooster": "Laiendatud tõlke mahu suurenemine" + "VOTAudioBooster": "Laiendatud tõlke mahu suurenemine", + "VOTMediaCSPError": "Heli laadimine nurjus (media csp viga)", + "VOTSubtitlesDesign": "Subtiitrite kujundus", + "VOTSubtitlesFontSize": "Subtiitrite fondi suurus", + "VOTSubtitlesOpacity": "Subtiitrite tausta läbipaistvus" } diff --git a/src/localization/locales/eu.json b/src/localization/locales/eu.json index c6b7252e..31a2191e 100644 --- a/src/localization/locales/eu.json +++ b/src/localization/locales/eu.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Huts egin du bideoaren itzulpena eskatzean", "audioNotReceived": "Audio esteka ez da jaso", "grantPermissionToAutoPlay": "Autoprodukziorako baimena ematea", - "neededAdditionalExtension": "Beste luzapen bat behar da gune hau eusteko", "audioFormatNotSupported": "Audio formatua ez dago onartuta", "VOTAutoTranslate": "Itzuli irekita", "VOTDontTranslateYourLang": "Ez itzuli nire hizkuntzatik", @@ -177,11 +176,8 @@ "yo": "Joruba izena", "zu": "Zuluak" }, - "udemyAccessTokenExpired": "Sartu duzun Udemy Sarbide-Tokena iraungi da", "udemyModuleArgsNotFound": "Ezin izan da udemy moduluaren daturik lortu ModuleArgs aurkitu ez delako", "VOTTranslationHelpNull": "Ezin izan dira itzulpenerako behar diren datuak lortu", - "enterUdemyAccessToken": "Sartu Udemy Sarbide-Tokena", - "VOTUdemyData": "Udemy Datuak", "streamNoConnectionToServer": "Ez dago konexiorik zerbitzariarekin", "searchField": "Bilatu...", "VOTTranslateAPIErrors": "Itzuli ERROREAK APITIK", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Sartu m3u8 ordezkariaren helbidea", "proxySettings": "Ordezko Ezarpenak", "translationTakeApproximatelyMinute2": "Itzulpenak gutxi gorabehera {0} minutu hartuko ditu", - "VOTAudioBooster": "Luzatu itzulpen bolumena handitzea" + "VOTAudioBooster": "Luzatu itzulpen bolumena handitzea", + "VOTMediaCSPError": "Huts egin du audioa kargatzean (euskarriaren ksp errorea)", + "VOTSubtitlesDesign": "Azpitituluen diseinua", + "VOTSubtitlesFontSize": "Azpitituluen letra-tamaina", + "VOTSubtitlesOpacity": "Azpitituluen atzeko planoaren gardentasuna" } diff --git a/src/localization/locales/fa.json b/src/localization/locales/fa.json index 9075440a..3438b49f 100644 --- a/src/localization/locales/fa.json +++ b/src/localization/locales/fa.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "امکان درخواست ترجمه ویدیو وجود ندارد", "audioNotReceived": "پیدا نشدن پیوند صوتی", "grantPermissionToAutoPlay": "اجازه پخش خودکار را بدهید", - "neededAdditionalExtension": "برای پشتیبانی از این سایت، نیاز به یک افزونه اضافی است", "audioFormatNotSupported": "قالب صوتی پشتیبانی نمی شود", "VOTAutoTranslate": "ترجمه خودکار", "VOTDontTranslateYourLang": "از زبان مادری خود ترجمه نکنید", @@ -177,11 +176,8 @@ "ja": "ژاپنی", "kri": "کری" }, - "udemyAccessTokenExpired": "رمز ورود Udemy Access شما منقضی شده است", "udemyModuleArgsNotFound": "نتوانست داده های ماژول udemy را به دلیل این واقعیت که ModuleArgs یافت نشد ، دریافت کند", "VOTTranslationHelpNull": "نتونستم اطلاعات مورد نیاز برای ترجمه رو بدست بیارم", - "enterUdemyAccessToken": "توکن دسترسی Udemy را وارد کنید", - "VOTUdemyData": "داده های Udemy", "streamNoConnectionToServer": "هیچ ارتباطی با سرور وجود ندارد", "searchField": "جستجو کن..", "VOTTranslateAPIErrors": "ترجمه خطاها از API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "آدرس کارگر پروکسی m3u8 را وارد کنید", "proxySettings": "تنظیمات پروکسی", "translationTakeApproximatelyMinute2": "ترجمه تقریبا {0} دقیقه طول خواهد کشید", - "VOTAudioBooster": "افزایش حجم ترجمه گسترده" + "VOTAudioBooster": "افزایش حجم ترجمه گسترده", + "VOTMediaCSPError": "نتوانست صدا را بارگذاری کند (خطای csp رسانه ای)", + "VOTSubtitlesDesign": "طراحی زیرنویس", + "VOTSubtitlesFontSize": "اندازه فونت زیرنویس", + "VOTSubtitlesOpacity": "شفافیت پس زمینه زیرنویس" } diff --git a/src/localization/locales/fi.json b/src/localization/locales/fi.json index 6dfb7170..ea0e82c7 100644 --- a/src/localization/locales/fi.json +++ b/src/localization/locales/fi.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Videon käännöksen pyyntö epäonnistui", "audioNotReceived": "Äänilinkkiä ei vastaanotettu", "grantPermissionToAutoPlay": "Myönnä lupa automaattipeliin", - "neededAdditionalExtension": "Tämän sivuston tueksi tarvitaan lisälaajennus", "audioFormatNotSupported": "Äänimuotoa ei tueta", "VOTAutoTranslate": "Käännä auki", "VOTDontTranslateYourLang": "Älä käännä kielestäni", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Syötetty Udemy Access Token on vanhentunut", "udemyModuleArgsNotFound": "Udemy-moduulin tietoja ei saatu, koska Moduleargsia ei löytynyt", "VOTTranslationHelpNull": "Ei voitu saada tarvittavat tiedot kääntää", - "enterUdemyAccessToken": "Enter Udemy Access Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Palvelimeen ei ole yhteyttä", "searchField": "Etsiä...", "VOTTranslateAPIErrors": "Käännä virheet API: sta", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Anna M3U8-valtakirjan työntekijän osoite", "proxySettings": "Välityspalvelimen Asetukset", "translationTakeApproximatelyMinute2": "Käännös kestää noin {0} minuuttia", - "VOTAudioBooster": "Laajennettu käännösmäärä kasvaa" + "VOTAudioBooster": "Laajennettu käännösmäärä kasvaa", + "VOTMediaCSPError": "Äänen lataaminen epäonnistui (median csp-virhe)", + "VOTSubtitlesDesign": "Tekstitysten suunnittelu", + "VOTSubtitlesFontSize": "Tekstityksen kirjasinkoko", + "VOTSubtitlesOpacity": "Tekstityksen Taustan läpinäkyvyys" } diff --git a/src/localization/locales/fr.json b/src/localization/locales/fr.json index 182ef6b6..158f86aa 100644 --- a/src/localization/locales/fr.json +++ b/src/localization/locales/fr.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Impossible de demander la traduction de la vidéo", "audioNotReceived": "Lien audio non reçu", "grantPermissionToAutoPlay": "Accorder l'autorisation de lecture automatique", - "neededAdditionalExtension": "Une extension supplémentaire est nécessaire pour prendre en charge ce site", "audioFormatNotSupported": "Format audio non pris en charge", "VOTAutoTranslate": "Traduire à l'ouverture", "VOTDontTranslateYourLang": "Ne pas traduire à partir de votre langue", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zoulou" }, - "udemyAccessTokenExpired": "Votre jeton d'accès Udemy saisi a expiré", "udemyModuleArgsNotFound": "Impossible d'obtenir les données du module udemy en raison du fait que ModuleArgs n'a pas été trouvé", "VOTTranslationHelpNull": "Impossible d'obtenir les données requises pour la traduction", - "enterUdemyAccessToken": "Entrez le Jeton d'accès Udemy", - "VOTUdemyData": "Données Udemy", "streamNoConnectionToServer": "Il n'y a pas de connexion au serveur", "searchField": "Chercher...", "VOTTranslateAPIErrors": "Traduire les erreurs de l'API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Entrez l'adresse du travailleur proxy m3u8", "proxySettings": "Paramètres du Proxy", "translationTakeApproximatelyMinute2": "La traduction prendra environ {0} minutes", - "VOTAudioBooster": "Augmentation du volume de traduction étendu" + "VOTAudioBooster": "Augmentation du volume de traduction étendu", + "VOTMediaCSPError": "Échec du chargement de l'audio (erreur csp multimédia)", + "VOTSubtitlesDesign": "Conception des sous-titres", + "VOTSubtitlesFontSize": "Taille de la police des sous-titres", + "VOTSubtitlesOpacity": "Transparence de l'arrière-plan des sous-titres" } diff --git a/src/localization/locales/gl.json b/src/localization/locales/gl.json index 9e13c61d..fb255c5b 100644 --- a/src/localization/locales/gl.json +++ b/src/localization/locales/gl.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Non foi posíbel solicitar a tradución de vídeo", "audioNotReceived": "Enlace de Audio non recibido", "grantPermissionToAutoPlay": "Autorización para autoplay", - "neededAdditionalExtension": "É necesaria unha extensión adicional para apoiar este sitio", "audioFormatNotSupported": "O formato de audio non é compatible", "VOTAutoTranslate": "Traducir en aberto", "VOTDontTranslateYourLang": "Non traducir do meu idioma", @@ -177,11 +176,8 @@ "yo": "Iorubá", "zu": "Zulú" }, - "udemyAccessTokenExpired": "O Teu Token De Acceso Udemy caducou", "udemyModuleArgsNotFound": "Non puido obter datos do módulo udemy debido ao feito de Que ModuleArgs non foi atopado", "VOTTranslationHelpNull": "Non foi posíbel obter os datos necesarios para a tradución", - "enterUdemyAccessToken": "Introduza O Token De Acceso Udemy", - "VOTUdemyData": "Datos De Udemy", "streamNoConnectionToServer": "Non hai conexión co servidor", "searchField": "Busca...", "VOTTranslateAPIErrors": "Traducir erros DA API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Introduza o enderezo do traballador proxy m3u8", "proxySettings": "Configuración Do Proxy", "translationTakeApproximatelyMinute2": "A tradución levará aproximadamente {0} minutos", - "VOTAudioBooster": "Aumento do volume de tradución estendido" + "VOTAudioBooster": "Aumento do volume de tradución estendido", + "VOTMediaCSPError": "Fallou a carga do audio (erro do csp dos medios)", + "VOTSubtitlesDesign": "Deseño de subtítulos", + "VOTSubtitlesFontSize": "Tamaño da letra dos subtítulos", + "VOTSubtitlesOpacity": "Transparencia do fondo do subtítulo" } diff --git a/src/localization/locales/hi.json b/src/localization/locales/hi.json index d41e6a50..69e49230 100644 --- a/src/localization/locales/hi.json +++ b/src/localization/locales/hi.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "वीडियो अनुवाद का अनुरोध विफल रहा", "audioNotReceived": "ऑडियो का लिंक प्राप्त नहीं हुआ", "grantPermissionToAutoPlay": "ऑटो प्ले करने की अनुमति दें", - "neededAdditionalExtension": "इस साइट के समर्थन के लिए एक अतिरिक्त एक्सटेंशन आवश्यक है", "audioFormatNotSupported": "ऑडियो फ़ॉर्मेट समर्थित नहीं है", "VOTAutoTranslate": "खोलने पर अनुवाद करें", "VOTDontTranslateYourLang": "अपनी भाषा से अनुवाद न करें", @@ -177,11 +176,8 @@ "ja": "जापानी", "kri": "क्री" }, - "udemyAccessTokenExpired": "आपका दर्ज किया गया उदमी एक्सेस टोकन समाप्त हो गया है", "udemyModuleArgsNotFound": "इस तथ्य के कारण उदमी मॉड्यूल डेटा प्राप्त नहीं कर सका कि मॉड्यूलएआरजीएस नहीं मिला था", "VOTTranslationHelpNull": "अनुवाद के लिए आवश्यक डेटा प्राप्त नहीं कर सका", - "enterUdemyAccessToken": "उदमी एक्सेस टोकन दर्ज करें", - "VOTUdemyData": "उदमी डेटा", "streamNoConnectionToServer": "सर्वर से कोई संबंध नहीं है", "searchField": "खोज...", "VOTTranslateAPIErrors": "एपीआई से त्रुटियों का अनुवाद करें", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "एम 3 यू 8 प्रॉक्सी कार्यकर्ता का पता दर्ज करें", "proxySettings": "प्रॉक्सी सेटिंग्स", "translationTakeApproximatelyMinute2": "अनुवाद में लगभग {0} मिनट लगेंगे", - "VOTAudioBooster": "विस्तारित अनुवाद की मात्रा में वृद्धि" + "VOTAudioBooster": "विस्तारित अनुवाद की मात्रा में वृद्धि", + "VOTMediaCSPError": "ऑडियो लोड करने में विफल (मीडिया सीएसपी त्रुटि)", + "VOTSubtitlesDesign": "उपशीर्षक डिजाइन", + "VOTSubtitlesFontSize": "उपशीर्षक का फ़ॉन्ट आकार", + "VOTSubtitlesOpacity": "उपशीर्षक पृष्ठभूमि की पारदर्शिता" } diff --git a/src/localization/locales/hr.json b/src/localization/locales/hr.json index eb2965c4..3225f984 100644 --- a/src/localization/locales/hr.json +++ b/src/localization/locales/hr.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nije moguće zatražiti prijevod videozapisa", "audioNotReceived": "Audio veza nije primljena", "grantPermissionToAutoPlay": "Odobrite dopuštenje za automatsku reprodukciju", - "neededAdditionalExtension": "Za podršku ove stranice potrebno je dodatno proširenje", "audioFormatNotSupported": "Audio format nije podržan", "VOTAutoTranslate": "Prevedi na otvoreno", "VOTDontTranslateYourLang": "Nemojte prevoditi s mog jezika", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zuluski" }, - "udemyAccessTokenExpired": "Access token koji ste unijeli istekao je", "udemyModuleArgsNotFound": "Nije moguće dohvatiti podatke modula A. M. jer nije pronađen nijedan A. M.", "VOTTranslationHelpNull": "Nije moguće dohvatiti podatke potrebne za prijevod", - "enterUdemyAccessToken": "Unesite pristupni token", - "VOTUdemyData": "Podaci Udemy", "streamNoConnectionToServer": "Nema veze s poslužiteljem", "searchField": "Traži...", "VOTTranslateAPIErrors": "Prevođenje pogrešaka iz", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Unesite adresu radnog opunomoćenika od 93 do 8", "proxySettings": "Postavke opunomoćenika", "translationTakeApproximatelyMinute2": "Prijevod će trajati približno {0} minuta", - "VOTAudioBooster": "Povećanje opsega proširenog prijevoda" + "VOTAudioBooster": "Povećanje opsega proširenog prijevoda", + "VOTMediaCSPError": "Preuzimanje zvuka nije uspjelo (pogreška u NDR-u)", + "VOTSubtitlesDesign": "Dizajn titlova", + "VOTSubtitlesFontSize": "Veličina fonta titla", + "VOTSubtitlesOpacity": "Transparentnost pozadine titlova" } diff --git a/src/localization/locales/hu.json b/src/localization/locales/hu.json index 8bf6e5b4..68ff147a 100644 --- a/src/localization/locales/hu.json +++ b/src/localization/locales/hu.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nem sikerült a videó fordítását kérni", "audioNotReceived": "Az Audio link nem érkezett meg", "grantPermissionToAutoPlay": "Engedély megadása az automatikus lejátszáshoz", - "neededAdditionalExtension": "További bővítményre van szükség a webhely támogatásához", "audioFormatNotSupported": "Az audio formátum nem támogatott", "VOTAutoTranslate": "Fordítás nyitva", "VOTDontTranslateYourLang": "Ne fordítson az én nyelvemről", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "A megadott Udemy hozzáférési Token lejárt", "udemyModuleArgsNotFound": "Nem sikerült megszerezni az udemy modul adatait, mivel a ModuleArgs nem található", "VOTTranslationHelpNull": "Nem sikerült megszerezni a fordításhoz szükséges adatokat", - "enterUdemyAccessToken": "Adja Meg Az Udemy Hozzáférési Tokent", - "VOTUdemyData": "Udemy Adatok", "streamNoConnectionToServer": "Nincs kapcsolat a szerverrel", "searchField": "Keresés...", "VOTTranslateAPIErrors": "Hibák lefordítása az API - ból", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Írja be az m3u8 proxy munkás címét", "proxySettings": "Proxy Beállítások", "translationTakeApproximatelyMinute2": "A fordítás körülbelül {0} percet vesz igénybe", - "VOTAudioBooster": "Bővített fordítási volumen növekedés" + "VOTAudioBooster": "Bővített fordítási volumen növekedés", + "VOTMediaCSPError": "Nem sikerült betölteni a hangot (media csp hiba)", + "VOTSubtitlesDesign": "Feliratok tervezése", + "VOTSubtitlesFontSize": "A feliratok betűmérete", + "VOTSubtitlesOpacity": "A felirat hátterének átláthatósága" } diff --git a/src/localization/locales/hy.json b/src/localization/locales/hy.json index 68c28801..c41d978b 100644 --- a/src/localization/locales/hy.json +++ b/src/localization/locales/hy.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Չհաջողվեց պահանջել տեսանյութի թարգմանություն", "audioNotReceived": "Աուդիո հաղորդակցություն չի ստացվել", "grantPermissionToAutoPlay": "Տրամադրեք ավտոմատ նվագարկման թույլտվություն", - "neededAdditionalExtension": "Այս կայքին աջակցելու համար անհրաժեշտ է լրացուցիչ ընդլայնում", "audioFormatNotSupported": "Աուդիո Ձևաչափը չի ապահովվում", "VOTAutoTranslate": "Թարգմանել բաց", "VOTDontTranslateYourLang": "Մի թարգմանիր իմ լեզվից", @@ -177,11 +176,8 @@ "yo": "Յորուբա", "zu": "Զուլուսերեն" }, - "udemyAccessTokenExpired": "Ձեր մուտքագրած Udemy մուտքի նշանի ժամկետը լրացել է", "udemyModuleArgsNotFound": "Udemy մոդուլի տվյալները հնարավոր չէ ստանալ, քանի որ ModuleArgs-ը չի գտնվել", "VOTTranslationHelpNull": "Հնարավոր չէ ստանալ փոխանցման համար անհրաժեշտ տվյալները", - "enterUdemyAccessToken": "Մուտքագրեք Udemy մուտքի նշանը", - "VOTUdemyData": "Udemy-Ի Տվյալները", "streamNoConnectionToServer": "Սերվերի միացում չկա", "searchField": "Որոնում..", "VOTTranslateAPIErrors": "API-ից սխալների փոխանցում", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Մուտքագրեք աշխատանքային m3u8 վստահված անձի հասցեն", "proxySettings": "Վստահված սերվերի կարգավորումներ", "translationTakeApproximatelyMinute2": "Թարգմանությունը կտեւի մոտավորապես {0} րոպե", - "VOTAudioBooster": "Ընդլայնված թարգմանության ծավալի ավելացում" + "VOTAudioBooster": "Ընդլայնված թարգմանության ծավալի ավելացում", + "VOTMediaCSPError": "Չհաջողվեց բեռնել աուդիո (media csp սխալ)", + "VOTSubtitlesDesign": "Ենթագրերի ձևավորում", + "VOTSubtitlesFontSize": "Ենթագրերի տառատեսակի չափը", + "VOTSubtitlesOpacity": "Ենթագրերի ֆոնի թափանցիկություն" } diff --git a/src/localization/locales/id.json b/src/localization/locales/id.json index 64f689d9..794b24c2 100644 --- a/src/localization/locales/id.json +++ b/src/localization/locales/id.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Tidak dapat meminta terjemahan video", "audioNotReceived": "Tidak ada tautan audio yang diterima", "grantPermissionToAutoPlay": "Berikan izin untuk autoplay", - "neededAdditionalExtension": "Untuk mendukung situs ini, diperlukan ekstensi tambahan", "audioFormatNotSupported": "Format audio tidak didukung", "VOTAutoTranslate": "Terjemahkan saat membuka", "VOTDontTranslateYourLang": "Jangan terjemahkan dari bahasa asli Anda", @@ -177,11 +176,8 @@ "ja": "Jepang", "kri": "Kri" }, - "udemyAccessTokenExpired": "Token Akses Udemy yang Anda masukkan telah kedaluwarsa", "udemyModuleArgsNotFound": "Tidak dapat memperoleh data modul udemy karena fakta bahwa ModuleArgs tidak ditemukan", "VOTTranslationHelpNull": "Tidak dapat memperoleh data yang diperlukan untuk terjemahan", - "enterUdemyAccessToken": "Masukkan Token Akses Udemy", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Tidak ada koneksi ke server", "searchField": "Cari...", "VOTTranslateAPIErrors": "Terjemahkan kesalahan dari API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Masukkan alamat pekerja proksi m3u8", "proxySettings": "Pengaturan Proksi", "translationTakeApproximatelyMinute2": "Terjemahan akan memakan waktu sekitar {0} menit", - "VOTAudioBooster": "Peningkatan volume terjemahan yang diperpanjang" + "VOTAudioBooster": "Peningkatan volume terjemahan yang diperpanjang", + "VOTMediaCSPError": "Gagal memuat audio (kesalahan csp media)", + "VOTSubtitlesDesign": "Desain subtitle", + "VOTSubtitlesFontSize": "Ukuran font subtitle", + "VOTSubtitlesOpacity": "Transparansi latar belakang subtitle" } diff --git a/src/localization/locales/it.json b/src/localization/locales/it.json index 441c654e..38fef195 100644 --- a/src/localization/locales/it.json +++ b/src/localization/locales/it.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Richiesta di traduzione video non riuscita", "audioNotReceived": "Collegamento audio non ricevuto", "grantPermissionToAutoPlay": "Concedere il permesso di autoplay", - "neededAdditionalExtension": "È necessaria un'estensione aggiuntiva per supportare questo sito", "audioFormatNotSupported": "Il formato audio non è supportato", "VOTAutoTranslate": "Traduci su apri", "VOTDontTranslateYourLang": "Non tradurre dalla mia lingua", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulù" }, - "udemyAccessTokenExpired": "Il token di accesso Udemy inserito è scaduto", "udemyModuleArgsNotFound": "Impossibile ottenere i dati del modulo udemy a causa del fatto che ModuleArgs non è stato trovato", "VOTTranslationHelpNull": "Impossibile ottenere i dati richiesti per la traduzione", - "enterUdemyAccessToken": "Inserisci il token di accesso Udemy", - "VOTUdemyData": "Dati Udemy", "streamNoConnectionToServer": "Non c'è connessione al server", "searchField": "Ricerca...", "VOTTranslateAPIErrors": "Tradurre gli errori dall'API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Immettere l'indirizzo del proxy worker m3u8", "proxySettings": "Impostazioni proxy", "translationTakeApproximatelyMinute2": "La traduzione richiederà circa {0} minuti", - "VOTAudioBooster": "Aumento del volume di traduzione esteso" + "VOTAudioBooster": "Aumento del volume di traduzione esteso", + "VOTMediaCSPError": "Caricamento audio non riuscito (errore media csp)", + "VOTSubtitlesDesign": "Sottotitoli design", + "VOTSubtitlesFontSize": "Dimensione del carattere dei sottotitoli", + "VOTSubtitlesOpacity": "Trasparenza dello sfondo dei sottotitoli" } diff --git a/src/localization/locales/ja.json b/src/localization/locales/ja.json index 1aadf88a..a974ea77 100644 --- a/src/localization/locales/ja.json +++ b/src/localization/locales/ja.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "動画の翻訳リクエストに失敗しました", "audioNotReceived": "音声リンクが受信されませんでした", "grantPermissionToAutoPlay": "自動再生の権限を付与する", - "neededAdditionalExtension": "このサイトをサポートするために追加の拡張機能が必要です", "audioFormatNotSupported": "音声形式がサポートされていません", "VOTAutoTranslate": "開いたときに翻訳する", "VOTDontTranslateYourLang": "自分の言語からは翻訳しない", @@ -177,11 +176,8 @@ "ja": "日本語", "nso": "北部ソト語" }, - "udemyAccessTokenExpired": "入力したUdemyアクセストークンの有効期限が切れています", "udemyModuleArgsNotFound": "ModuleArgsが見つからなかったため、udemyモジュールのデータを取得できませんでした", "VOTTranslationHelpNull": "翻訳に必要なデータを取得できませんでした", - "enterUdemyAccessToken": "Udemyアクセストークンを入力します", - "VOTUdemyData": "Udemyデータ", "streamNoConnectionToServer": "サーバーへの接続がありません", "searchField": "検索。..", "VOTTranslateAPIErrors": "APIからのエラーの翻訳", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8プロキシワーカーのアドレスを入力します", "proxySettings": "プロキシ設定", "translationTakeApproximatelyMinute2": "翻訳には約{0}分かかります", - "VOTAudioBooster": "翻訳量の増加を拡大しました" + "VOTAudioBooster": "翻訳量の増加を拡大しました", + "VOTMediaCSPError": "オーディオのロードに失敗しました(メディアcspエラー)", + "VOTSubtitlesDesign": "字幕デザイン", + "VOTSubtitlesFontSize": "字幕のフォントサイズ", + "VOTSubtitlesOpacity": "字幕の背景の透明度" } diff --git a/src/localization/locales/jv.json b/src/localization/locales/jv.json index 4cce4ad6..aafaf688 100644 --- a/src/localization/locales/jv.json +++ b/src/localization/locales/jv.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Gagal kanggo njaluk terjemahan video", "audioNotReceived": "Link Audio ora ditampa", "grantPermissionToAutoPlay": "Ijin ijin kanggo autoplay", - "neededAdditionalExtension": "Ekstensi tambahan dibutuhake kanggo ndhukung situs iki", "audioFormatNotSupported": "Format audio ora didhukung", "VOTAutoTranslate": "Nerjemahake ing mbukak", "VOTDontTranslateYourLang": "Aja nerjemahké saka basanku", @@ -177,11 +176,8 @@ "yo": "Basa Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Wkwkwkwkwkwkwkwkwkwkwk Ngakak abis dah", "udemyModuleArgsNotFound": "Ora bisa entuk data modul udemy amarga Kasunyatan Manawa ModuleArgs ora ditemokake", "VOTTranslationHelpNull": "Ora bisa njaluk data sing dibutuhake kanggo translate", - "enterUdemyAccessToken": "Masuk Token Akses Udemy", - "VOTUdemyData": "Data Udemy", "streamNoConnectionToServer": "Ora ana sambungan menyang server", "searchField": "Nggolèki...", "VOTTranslateAPIErrors": "Translate kasalahan SAKA API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Pilih alamat m3u8 pekerja proxy", "proxySettings": "Setelan Proxy", "translationTakeApproximatelyMinute2": "Terjemahan bakal njupuk kira-kira {0} menit", - "VOTAudioBooster": "Tambah volume terjemahan sing ditambahi" + "VOTAudioBooster": "Tambah volume terjemahan sing ditambahi", + "VOTMediaCSPError": "Gagal kanggo mbukak audio (kesalahan csp media)", + "VOTSubtitlesDesign": "Subtitle desain", + "VOTSubtitlesFontSize": "Ukuran Font saka subtitles", + "VOTSubtitlesOpacity": "Transparansi latar mburi subtitle" } diff --git a/src/localization/locales/kk.json b/src/localization/locales/kk.json index a4e72f20..887cd8b7 100644 --- a/src/localization/locales/kk.json +++ b/src/localization/locales/kk.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Бейнені аудару талап етуден кейін сәтсіз аяқталды", "audioNotReceived": "Аудиоға сілтеме алынған жоқ", "grantPermissionToAutoPlay": "Автоматты түрде ойнатуды рұқсат ету", - "neededAdditionalExtension": "Бұл сайтты қолдау үшін көбірек өзге толтырушы қосымша қажет", "audioFormatNotSupported": "Аудио пішімі қолдау көрсетмейді", "VOTAutoTranslate": "Ашылада переводдау", "VOTDontTranslateYourLang": "Өз тіліңнен таратпау", @@ -177,11 +176,8 @@ "ja": "Жапон", "kri": "Кри" }, - "udemyAccessTokenExpired": "Сіз енгізген Udemy кіру таңбалауышының мерзімі аяқталды", "udemyModuleArgsNotFound": "Moduleargs табылмағандықтан udemy Модулінің деректерін алу мүмкін болмады", "VOTTranslationHelpNull": "Аударуға қажетті деректерді алу мүмкін болмады", - "enterUdemyAccessToken": "Udemy кіру таңбалауышын енгізіңіз", - "VOTUdemyData": "Udemy Деректері", "streamNoConnectionToServer": "Серверге қосылу жоқ", "searchField": "Іздеу...", "VOTTranslateAPIErrors": "API-ден қателерді аудару", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 жұмыс прокси мекенжайын енгізіңіз", "proxySettings": "Прокси параметрлері", "translationTakeApproximatelyMinute2": "Аударма шамамен {0} минутты алады", - "VOTAudioBooster": "Аударма көлемінің ұлғаюы" + "VOTAudioBooster": "Аударма көлемінің ұлғаюы", + "VOTMediaCSPError": "Аудио жүктелмеді (медиа csp қатесі)", + "VOTSubtitlesDesign": "Субтитрлер дизайны", + "VOTSubtitlesFontSize": "Субтитрлердің қаріп өлшемі", + "VOTSubtitlesOpacity": "Субтитр фонының мөлдірлігі" } diff --git a/src/localization/locales/km.json b/src/localization/locales/km.json index 6e3f87dd..50ccd809 100644 --- a/src/localization/locales/km.json +++ b/src/localization/locales/km.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "បរាជ័យក្នុងការស្នើសុំបកប្រែវីដេអូ", "audioNotReceived": "មិនបានទទួលតំណអូឌីយ៉ូ", "grantPermissionToAutoPlay": "ផ្តល់សិទ្ធិឲ្យចាក់ដោយស្វ័យប្រវត្តិ", - "neededAdditionalExtension": "ផ្នែកបន្ថែមបន្ថែមទៀតគឺត្រូវបានត្រូវការដើម្បីគាំទ្រដល់តំបន់បណ្តាញនេះ", "audioFormatNotSupported": "ទ្រង់ទ្រាយអូឌីយ៉ូមិនត្រូវបានគាំទ្រទេ", "VOTAutoTranslate": "បកប្រែបើក", "VOTDontTranslateYourLang": "កុំបកប្រែពីភាសារបស់ខ្ញុំ", @@ -177,11 +176,8 @@ "yo": "អ៊ីម៉ែល", "zu": "ស៊ុយលូ" }, - "udemyAccessTokenExpired": "សញ្ញាចូលដំណើរការ Udemy ដែលបានបញ្ចូលរបស់អ្នកបានផុតកំណត់", "udemyModuleArgsNotFound": "មិនអាចទទួលបានទិន្នន័យម៉ូឌុល udemy ដោយសារតែការពិតដែលថា moduleargs មិនត្រូវបានរកឃើញ", "VOTTranslationHelpNull": "មិនអាចទទួលបានទិន្នន័យដែលបានទាមទារសម្រាប់ការបកប្រែ", - "enterUdemyAccessToken": "បញ្ចូលសញ្ញាសម្ងាត់ចូលដំណើរការ Udemy", - "VOTUdemyData": "ទិន្នន័យ Udemy", "streamNoConnectionToServer": "មិនមានការតភ្ជាប់ទៅម៉ាស៊ីនបម្រើទេ", "searchField": "ស្វែងរក...", "VOTTranslateAPIErrors": "បកប្រែកំហុសពី API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "បញ្ចូលអាសយដ្ឋានរបស់កម្មករប្រូកស៊ី m3u8", "proxySettings": "ការកំណត់ប្រូកស៊ី", "translationTakeApproximatelyMinute2": "ការបកប្រែនេះនឹងចំណាយពេលប្រមាណ{0}នាទី", - "VOTAudioBooster": "បង្កើនកម្រិតសំឡេងបកប្រែដែលបានពង្រីក" + "VOTAudioBooster": "បង្កើនកម្រិតសំឡេងបកប្រែដែលបានពង្រីក", + "VOTMediaCSPError": "បានបរាជ័យក្នុងការផ្ទុកអូឌីយ៉ូ(កំហុសប្រព័ន្ធផ្សព្វផ្សាយ csp)", + "VOTSubtitlesDesign": "រចនាអក្សររត់ពីក្រោម", + "VOTSubtitlesFontSize": "ទំហំពុម្ពអក្សរនៃចំណងជើងរង", + "VOTSubtitlesOpacity": "តម្លាភាពនៃផ្ទៃខាងក្រោយចំណងជើងរង" } diff --git a/src/localization/locales/kn.json b/src/localization/locales/kn.json index 37016ee7..f8027db6 100644 --- a/src/localization/locales/kn.json +++ b/src/localization/locales/kn.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "ವೀಡಿಯೊ ಅನುವಾದವನ್ನು ಕೋರಲು ವಿಫಲಗೊಂಡಿದೆ", "audioNotReceived": "ಆಡಿಯೋ ಲಿಂಕ್ ಬಂದಿಲ್ಲ", "grantPermissionToAutoPlay": "ಸ್ವಯಂಪ್ಲೇ ಅನುಮತಿ ನೀಡಿ", - "neededAdditionalExtension": "ಈ ಸೈಟ್ ಅನ್ನು ಬೆಂಬಲಿಸಲು ಹೆಚ್ಚುವರಿ ವಿಸ್ತರಣೆ ಅಗತ್ಯವಿದೆ", "audioFormatNotSupported": "ಆಡಿಯೋ ಸ್ವರೂಪ ಬೆಂಬಲವಿಲ್ಲ", "VOTAutoTranslate": "ತೆರೆದ ಮೇಲೆ ಭಾಷಾಂತರಿಸಿ", "VOTDontTranslateYourLang": "'ನನ್ನ ಭಾಷೆಯಿಂದ ಭಾಷಾಂತರಿಸಬೇಡಿ'", @@ -177,11 +176,8 @@ "yo": "ಯೊರುಬಾ", "zu": "ಜುಲು" }, - "udemyAccessTokenExpired": "ನಿಮ್ಮ ನಮೂದಿಸಿದ ಉಡೆಮಿ ಪ್ರವೇಶ ಟೋಕನ್ ಅವಧಿ ಮುಗಿದಿದೆ", "udemyModuleArgsNotFound": "ModuleArgs ಕಂಡುಬಂದಿಲ್ಲ ಎಂಬ ಕಾರಣದಿಂದಾಗಿ udemy ಮಾಡ್ಯೂಲ್ ಡೇಟಾವನ್ನು ಪಡೆಯಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ", "VOTTranslationHelpNull": "ಭಾಷಾಂತರಕ್ಕೆ ಅಗತ್ಯವಿರುವ ದತ್ತಾಂಶವನ್ನು ಪಡೆಯಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ", - "enterUdemyAccessToken": "Udemy ಪ್ರವೇಶ ಟೋಕನ್ ಅನ್ನು ನಮೂದಿಸಿ", - "VOTUdemyData": "Udemy ಡೇಟಾ", "streamNoConnectionToServer": "ಸರ್ವರ್ಗೆ ಸಂಪರ್ಕವಿಲ್ಲ", "searchField": "ಹುಡುಕಿ...", "VOTTranslateAPIErrors": "API ನಿಂದ ದೋಷಗಳನ್ನು ಭಾಷಾಂತರಿಸಿ", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 ಪ್ರಾಕ್ಸಿ ಕೆಲಸಗಾರನ ವಿಳಾಸವನ್ನು ನಮೂದಿಸಿ", "proxySettings": "ಪ್ರಾಕ್ಸಿ ಸೆಟ್ಟಿಂಗ್ಗಳು", "translationTakeApproximatelyMinute2": "ಅನುವಾದವು ಸರಿಸುಮಾರು {0} ನಿಮಿಷಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ", - "VOTAudioBooster": "ವಿಸ್ತೃತ ಅನುವಾದ ಪರಿಮಾಣ ಹೆಚ್ಚಳ" + "VOTAudioBooster": "ವಿಸ್ತೃತ ಅನುವಾದ ಪರಿಮಾಣ ಹೆಚ್ಚಳ", + "VOTMediaCSPError": "ಆಡಿಯೋ ಲೋಡ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ (ಮೀಡಿಯಾ csp ದೋಷ)", + "VOTSubtitlesDesign": "ಉಪಶೀರ್ಷಿಕೆಗಳು ವಿನ್ಯಾಸ", + "VOTSubtitlesFontSize": "ಉಪಶೀರ್ಷಿಕೆಗಳ ಫಾಂಟ್ ಗಾತ್ರ", + "VOTSubtitlesOpacity": "ಉಪಶೀರ್ಷಿಕೆ ಹಿನ್ನೆಲೆ ಪಾರದರ್ಶಕತೆ" } diff --git a/src/localization/locales/ko.json b/src/localization/locales/ko.json index 9b02bfc8..448813a6 100644 --- a/src/localization/locales/ko.json +++ b/src/localization/locales/ko.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "비디오 번역 요청 실패", "audioNotReceived": "오디오 링크를 받지 못했습니다", "grantPermissionToAutoPlay": "자동 재생 권한 부여", - "neededAdditionalExtension": "이 사이트를 지원하기 위해 추가 확장 프로그램이 필요합니다", "audioFormatNotSupported": "오디오 형식이 지원되지 않습니다", "VOTAutoTranslate": "열 때 번역하기", "VOTDontTranslateYourLang": "내 언어에서 번역하지 않기", @@ -177,11 +176,8 @@ "hi": "힌디어", "kri": "Kri" }, - "udemyAccessTokenExpired": "입력한 액세스 토큰이 만료되었습니다.", "udemyModuleArgsNotFound": "오류 코드에 관한 문제로 트리거됩니다.", "VOTTranslationHelpNull": "번역에 필요한 데이터를 가져올 수 없습니다", - "enterUdemyAccessToken": "액세스 토큰 입력", - "VOTUdemyData": "데이터", "streamNoConnectionToServer": "서버에 연결이 없습니다", "searchField": "검색...", "VOTTranslateAPIErrors": "번역 오류", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "프록시 작업자의 주소를 입력합니다", "proxySettings": "프록시 설정", "translationTakeApproximatelyMinute2": "번역은 약{0}분이 소요됩니다", - "VOTAudioBooster": "확장 번역 볼륨 증가" + "VOTAudioBooster": "확장 번역 볼륨 증가", + "VOTMediaCSPError": "오디오 로드 실패", + "VOTSubtitlesDesign": "자막 디자인", + "VOTSubtitlesFontSize": "자막의 글꼴 크기", + "VOTSubtitlesOpacity": "자막 배경의 투명도" } diff --git a/src/localization/locales/lo.json b/src/localization/locales/lo.json index 23de1184..99fb5438 100644 --- a/src/localization/locales/lo.json +++ b/src/localization/locales/lo.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "ລົ້ມເຫລວໃນການຮ້ອງຂໍການແປພາສາວິດີໂອ", "audioNotReceived": "ການເຊື່ອມຕໍ່ສຽງບໍ່ໄດ້ຮັບ", "grantPermissionToAutoPlay": "ໃຫ້ການອະນຸຍາດໃຫ້ autoplay", - "neededAdditionalExtension": "ຕ້ອງມີການຂະຫຍາຍເພີ່ມເຕີມເພື່ອສະໜັບສະໜູນເວັບໄຊທ໌ນີ້", "audioFormatNotSupported": "ຮູບແບບສຽງບໍ່ໄດ້ຮັບການສະໜັບສະໜູນ", "VOTAutoTranslate": "ແປພາສາເປີດ", "VOTDontTranslateYourLang": "ຢ່າແປຈາກພາສາຂອງຂ້ອຍ", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "ຊູລູ" }, - "udemyAccessTokenExpired": "Token ການເຂົ້າເຖິງ Udemy ຂອງທ່ານໄດ້ຫມົດອາຍຸ", "udemyModuleArgsNotFound": "ບໍ່ສາມາດໄດ້ຮັບຂໍ້ມູນໂມດູນ udemy ເນື່ອງຈາກຄວາມຈິງທີ່ວ່າ ModuleArgs ບໍ່ພົບ", "VOTTranslationHelpNull": "ບໍ່ສາມາດໄດ້ຮັບຂໍ້ມູນທີ່ຕ້ອງການສໍາລັບການແປພາສາ", - "enterUdemyAccessToken": "ກະລຸນາໃສ່ Token ການເຂົ້າເຖິງ Udemy", - "VOTUdemyData": "ຂໍ້ມູນ Udemy", "streamNoConnectionToServer": "ບໍ່ມີການເຊື່ອມຕໍ່ກັບ server", "searchField": "ຄົ້ນຫາ...", "VOTTranslateAPIErrors": "ແປຂໍ້ຜິດພາດຈາກ API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "ໃສ່ທີ່ຢູ່ຂອງພະນັກງານຕົວແທນ m3u8", "proxySettings": "ການຕັ້ງຄ່າຕົວແທນ", "translationTakeApproximatelyMinute2": "ການແປພາສາຈະໃຊ້ເວລາປະມານ{0}ນາທີ", - "VOTAudioBooster": "ການແປພາສາຂະຫຍາຍປະລິມານເພີ່ມຂຶ້ນ" + "VOTAudioBooster": "ການແປພາສາຂະຫຍາຍປະລິມານເພີ່ມຂຶ້ນ", + "VOTMediaCSPError": "ລົ້ມເຫລວໃນການໂຫຼດສຽງ(media csp error)", + "VOTSubtitlesDesign": "ການອອກແບບຄໍາບັນຍາຍ", + "VOTSubtitlesFontSize": "ຂະໜາດຕົວອັກສອນຂອງຄຳບັນຍາຍ", + "VOTSubtitlesOpacity": "ຄວາມໂປ່ງໃສຂອງຄວາມເປັນມາ subtitle ໄດ້" } diff --git a/src/localization/locales/mk.json b/src/localization/locales/mk.json index 6dff8118..92519c9b 100644 --- a/src/localization/locales/mk.json +++ b/src/localization/locales/mk.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Не успеа да побара видео превод", "audioNotReceived": "Аудио врската не е примена", "grantPermissionToAutoPlay": "Дајте дозвола за автоматско репродукција", - "neededAdditionalExtension": "Потребна е дополнителна екстензија за поддршка на оваа страница", "audioFormatNotSupported": "Аудио форматот не е поддржан", "VOTAutoTranslate": "Преведи на отворено", "VOTDontTranslateYourLang": "Не преведувајте од мојот јазик", @@ -177,11 +176,8 @@ "yo": "Јоруба", "zu": "Зулу" }, - "udemyAccessTokenExpired": "Вашиот внесен Знак За Пристап До Удеми истече", "udemyModuleArgsNotFound": "Не можев да добијам податоци за модулот удеми поради фактот Што Модулоте пронајдени", "VOTTranslationHelpNull": "Не можев да ги добијам податоците потребни за преводот", - "enterUdemyAccessToken": "Внесете Удеми Пристап Токен", - "VOTUdemyData": "Удеми Податоци", "streamNoConnectionToServer": "Нема врска со серверот", "searchField": "Барај...", "VOTTranslateAPIErrors": "Преведи грешки ОД АПИ", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Внесете ја адресата на прокси-работникот м3у8", "proxySettings": "Поставки За Прокси", "translationTakeApproximatelyMinute2": "Преводот ќе трае приближно {0} минути", - "VOTAudioBooster": "Зголемен обем на превод" + "VOTAudioBooster": "Зголемен обем на превод", + "VOTMediaCSPError": "Не успеа да вчита аудио (грешка во медиумскиот цсп)", + "VOTSubtitlesDesign": "Преводи дизајн", + "VOTSubtitlesFontSize": "Големина на фонт на преводи", + "VOTSubtitlesOpacity": "Транспарентност на позадината на титлот" } diff --git a/src/localization/locales/ml.json b/src/localization/locales/ml.json index 6605f07f..87ffcec6 100644 --- a/src/localization/locales/ml.json +++ b/src/localization/locales/ml.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "വീഡിയോ പരിഭാഷ ആവശ്യപ്പെടുന്നതില് പരാജയം", "audioNotReceived": "ഓഡിയോ ലിങ്ക് ലഭ്യമല്ല", "grantPermissionToAutoPlay": "ഓട്ടോപ്ലേയ്ക്ക് അനുമതി നല്കുക", - "neededAdditionalExtension": "ഈ സൈറ്റിനെ പിന്തുണയ്ക്കുന്നതിന് ഒരു അധിക വിപുലീകരണം ആവശ്യമാണ്.", "audioFormatNotSupported": "ഓഡിയോ ഫോർമാറ്റ് പിന്തുണയ്ക്കുന്നില്ല", "VOTAutoTranslate": "ഓപ്പണിൽ വിവർത്തനം ചെയ്യുക", "VOTDontTranslateYourLang": "എന്റെ ഭാഷയിൽ നിന്ന് വിവർത്തനം ചെയ്യരുത്", @@ -177,11 +176,8 @@ "yo": "യോരുബ", "zu": "സുലു" }, - "udemyAccessTokenExpired": "നിങ്ങളുടെ ഉഡെമി ആക്സസ് ടോക്കൺ കാലഹരണപ്പെട്ടു", "udemyModuleArgsNotFound": "മോഡുലാർഗുകൾ കണ്ടെത്തിയില്ലാത്തതിനാൽ ഉഡെമി മൊഡ്യൂൾ ഡാറ്റ ലഭ്യമാക്കാൻ കഴിഞ്ഞില്ല", "VOTTranslationHelpNull": "പരിഭാഷയ്ക്ക് ആവശ്യമായ ഡാറ്റ ലഭ്യമാക്കുവാന് സാധ്യമായില്ല", - "enterUdemyAccessToken": "ഉഡെമി ആക്സസ് ടോക്കൺ നൽകുക", - "VOTUdemyData": "ഉഡെമി ഡാറ്റ", "streamNoConnectionToServer": "സെർവറുമായി ബന്ധമില്ല", "searchField": "തിരയൂ...", "VOTTranslateAPIErrors": "എപിഐയിൽ നിന്ന് പിശകുകൾ വിവർത്തനം ചെയ്യുക", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "എം 3 യു 8 പ്രോക്സി തൊഴിലാളിയുടെ വിലാസം നൽകുക", "proxySettings": "പ്രോക്സി ക്രമീകരണങ്ങൾ", "translationTakeApproximatelyMinute2": "പരിഭാഷ ഏകദേശം {0} മിനിറ്റ് എടുക്കും", - "VOTAudioBooster": "വിപുലീകരിച്ച പരിഭാഷ വോള്യം വർദ്ധിപ്പിക്കാൻ" + "VOTAudioBooster": "വിപുലീകരിച്ച പരിഭാഷ വോള്യം വർദ്ധിപ്പിക്കാൻ", + "VOTMediaCSPError": "ഓഡിയോ ലോഡ് ചെയ്യുന്നതില് പരാജയം (മീഡിയ സിഎസ്പി പിശക്)", + "VOTSubtitlesDesign": "സബ്ടൈറ്റിലുകൾ ഡിസൈൻ", + "VOTSubtitlesFontSize": "സബ്ടൈറ്റിലുകളുടെ അക്ഷരസഞ്ചയം", + "VOTSubtitlesOpacity": "സബ്ടൈറ്റിൽ പശ്ചാത്തലത്തിന്റെ സുതാര്യത" } diff --git a/src/localization/locales/mn.json b/src/localization/locales/mn.json index a44504ac..8f4f0ded 100644 --- a/src/localization/locales/mn.json +++ b/src/localization/locales/mn.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Видео орчуулга хийх хүсэлт гаргаагүй", "audioNotReceived": "Аудио холбоос хүлээн аваагүй", "grantPermissionToAutoPlay": "Autoplay зөвшөөрөл олгох", - "neededAdditionalExtension": "Энэ сайтыг дэмжихийн тулд нэмэлт өргөтгөл шаардлагатай байна", "audioFormatNotSupported": "Аудио форматыг дэмждэггүй", "VOTAutoTranslate": "Нээлттэй дээр орчуулах", "VOTDontTranslateYourLang": "Миний хэлнээс битгий орчуулаарай", @@ -177,11 +176,8 @@ "yo": "Еруба", "zu": "Зулу" }, - "udemyAccessTokenExpired": "Таны оруулсан Udemy хандалтын токен дууссан", "udemyModuleArgsNotFound": "Moduleargs олдоогүй тул udemy модулийн өгөгдлийг авч чадсангүй", "VOTTranslationHelpNull": "Орчуулахад шаардлагатай мэдээллийг авч чадсангүй", - "enterUdemyAccessToken": "Udemy Хандалтын Токен Оруулна Уу", - "VOTUdemyData": "Udemy Мэдээлэл", "streamNoConnectionToServer": "Сервертэй холбогдох боломжгүй", "searchField": "Хайх...", "VOTTranslateAPIErrors": "API - аас алдаа орчуулах", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 прокси ажилтны хаягийг оруулна уу", "proxySettings": "Прокси Тохиргоо", "translationTakeApproximatelyMinute2": "Орчуулга нь ойролцоогоор {0} минут болно", - "VOTAudioBooster": "Өргөтгөсөн орчуулгын хэмжээ нэмэгдэх" + "VOTAudioBooster": "Өргөтгөсөн орчуулгын хэмжээ нэмэгдэх", + "VOTMediaCSPError": "Аудио ачаалж чадаагүй (медиа csp алдаа)", + "VOTSubtitlesDesign": "Хадмалтай дизайн", + "VOTSubtitlesFontSize": "Хадмалтай үсгийн хэмжээ", + "VOTSubtitlesOpacity": "Subtitle background-ийн ил тод байдал" } diff --git a/src/localization/locales/ms.json b/src/localization/locales/ms.json index 20d758ae..c3145cd4 100644 --- a/src/localization/locales/ms.json +++ b/src/localization/locales/ms.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Gagal meminta terjemahan video", "audioNotReceived": "Link audio tidak diterima", "grantPermissionToAutoPlay": "Berikan kebenaran untuk main automatik", - "neededAdditionalExtension": "Sambungan tambahan diperlukan untuk menyokong laman web ini", "audioFormatNotSupported": "Format audio tidak disokong", "VOTAutoTranslate": "Terjemahkan semasa pembukaan", "VOTDontTranslateYourLang": "Jangan terjemahkan dari bahasa asal anda", @@ -177,11 +176,8 @@ "ja": "Jepun", "kri": "Kri" }, - "udemyAccessTokenExpired": "Token akses Udemy yang anda masukkan telah tamat tempoh", "udemyModuleArgsNotFound": "Tidak dapat mendapatkan Data Modul udemy kerana fakta bahawa ModuleArgs tidak dijumpai", "VOTTranslationHelpNull": "Tidak dapat mendapatkan data yang diperlukan untuk Terjemah", - "enterUdemyAccessToken": "Masukkan Token Akses Udemy", - "VOTUdemyData": "Data Udemy", "streamNoConnectionToServer": "Tiada sambungan ke pelayan", "searchField": "Cari...", "VOTTranslateAPIErrors": "Terjemahkan ralat dari API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Masukkan alamat pekerja proksi m3u8", "proxySettings": "Tetapan Proksi", "translationTakeApproximatelyMinute2": "Terjemahan akan mengambil masa lebih kurang {0} minit", - "VOTAudioBooster": "Peningkatan jumlah terjemahan lanjutan" + "VOTAudioBooster": "Peningkatan jumlah terjemahan lanjutan", + "VOTMediaCSPError": "Gagal memuatkan audio (ralat media CSP)", + "VOTSubtitlesDesign": "Reka bentuk sarikata", + "VOTSubtitlesFontSize": "Saiz fon sari kata", + "VOTSubtitlesOpacity": "Ketelusan latar belakang sari kata" } diff --git a/src/localization/locales/mt.json b/src/localization/locales/mt.json index 8c5b1db5..09a1e955 100644 --- a/src/localization/locales/mt.json +++ b/src/localization/locales/mt.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Naqas milli jitlob traduzzjoni bil-vidjo", "audioNotReceived": "Link awdjo mhux riċevut", "grantPermissionToAutoPlay": "Agħti permess biex tilgħab awtomatikament", - "neededAdditionalExtension": "Hija meħtieġa estensjoni addizzjonali biex tappoġġja dan is sit", "audioFormatNotSupported": "Il-format tal-awdjo mhuwiex appoġġjat", "VOTAutoTranslate": "Ittraduċi fuq miftuħ", "VOTDontTranslateYourLang": "Tittraduċix mil lingwa tiegħi", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Żulu" }, - "udemyAccessTokenExpired": "It-Token ta'aċċess udemy imdaħħal tiegħek skada", "udemyModuleArgsNotFound": "Ma setgħetx tikseb dejta tal modulu udemy minħabba l fatt Li Modulargs ma nstabx", "VOTTranslationHelpNull": "Ma setgħetx tikseb id-dejta meħtieġa għat-traduzzjoni", - "enterUdemyAccessToken": "Daħħal It-Token Tal-Aċċess Udemy", - "VOTUdemyData": "Dejta Udemy", "streamNoConnectionToServer": "M'hemm l ebda konnessjoni mas server", "searchField": "Fittex...", "VOTTranslateAPIErrors": "Ittraduċi żbalji mill-API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Daħħal l indirizz tal ħaddiem tal prokura m3u8", "proxySettings": "Settings Tal-Prokura", "translationTakeApproximatelyMinute2": "It-traduzzjoni tieħu madwar {0} minuti", - "VOTAudioBooster": "Żieda estiża fil-volum tat-traduzzjoni" + "VOTAudioBooster": "Żieda estiża fil-volum tat-traduzzjoni", + "VOTMediaCSPError": "Naqas milli jgħabbi l-awdjo (żball tal-psp tal-midja)", + "VOTSubtitlesDesign": "Disinn tas-sottotitoli", + "VOTSubtitlesFontSize": "Daqs tat-tipa tas-sottotitoli", + "VOTSubtitlesOpacity": "Trasparenza tal-isfond tas-sottotitolu" } diff --git a/src/localization/locales/my.json b/src/localization/locales/my.json index 59084716..143a0104 100644 --- a/src/localization/locales/my.json +++ b/src/localization/locales/my.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "ဗီဒီယိုဘာသာပြန်မှုကိုတောင်းဆိုရန်ပျက်ကွက်", "audioNotReceived": "အသံလင့်ခ်ကိုမရရှိသေးပါ။", "grantPermissionToAutoPlay": "အလိုအလျောက်ကစားရန်ခွင့်ပြုချက်", - "neededAdditionalExtension": "ဤဆိုဒ်ကိုထောက်ပံ့ရန်အပိုဆောင်း extension တစ်ခုလိုအပ်သည်။", "audioFormatNotSupported": "အသံပုံစံကိုမထောက်ပံ့ပါ။", "VOTAutoTranslate": "ပွင့်လင်းအပေါ်ဘာသာပြန်ဆို", "VOTDontTranslateYourLang": "ငါ့ဘာသာစကားကနေဘာသာပြန်မနေနဲ့။", @@ -177,11 +176,8 @@ "yo": "ယိုရူးဘား", "zu": "ဇူလူး" }, - "udemyAccessTokenExpired": "သင်ထည့်သွင်းထားသော Udemy Access Token သည်သက်တမ်းကုန်ဆုံးသွားပြီဖြစ်သည်။", "udemyModuleArgsNotFound": "ModuleArgs ကိုမတွေ့ရှိခဲ့သောကြောင့် udemy module data ကိုမရရှိနိုင်ခဲ့ပါ။", "VOTTranslationHelpNull": "ဘာသာပြန်ဖို့လိုအပ်တဲ့ဒေတာကိုမရနိုင်ခဲ့ဘူး။", - "enterUdemyAccessToken": "Udemy Access Token ကိုထည့်သွင်းပါ", - "VOTUdemyData": "Udemy ဒေတာ", "streamNoConnectionToServer": "ဆာဗာနှင့်ချိတ်ဆက်မှုမရှိပါ။", "searchField": "ရှာဖွေပါ။..", "VOTTranslateAPIErrors": "API မှအမှားများကိုဘာသာပြန်ပါ", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 proxy worker၏လိပ်စာကိုရိုက်ထည့်ပါ", "proxySettings": "Proxy Setting များ", "translationTakeApproximatelyMinute2": "ဘာသာပြန်ဆိုမှုသည်{0}မိနစ်ခန့်ကြာလိမ့်မည်။", - "VOTAudioBooster": "ဘာသာပြန်မှုပမာဏတိုးမြှင့်" + "VOTAudioBooster": "ဘာသာပြန်မှုပမာဏတိုးမြှင့်", + "VOTMediaCSPError": "အသံတင်ရန်ပျက်ကွက်ခြင်း(မီဒီယာ csp အမှား)", + "VOTSubtitlesDesign": "စာတန်းထိုးဒီဇိုင်း", + "VOTSubtitlesFontSize": "စာတန်းထိုးများ၏စာလုံးအရွယ်အစား", + "VOTSubtitlesOpacity": "စာတန်းထိုးနောက်ခံ၏ပွင့်လင်းမြင်သာမှု" } diff --git a/src/localization/locales/ne.json b/src/localization/locales/ne.json index 7423f815..4a508c78 100644 --- a/src/localization/locales/ne.json +++ b/src/localization/locales/ne.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "भिडियो अनुवाद अनुरोध गर्न असफल भयो", "audioNotReceived": "अडियो लिङ्क प्राप्त भएन", "grantPermissionToAutoPlay": "स्वतः प्ले गर्न अनुमति दिनुहोस्", - "neededAdditionalExtension": "यस साइटलाई समर्थन गर्न थप विस्तार आवश्यक छ", "audioFormatNotSupported": "अडियो ढाँचा समर्थित छैन", "VOTAutoTranslate": "खुला मा अनुवाद गर्नुहोस्", "VOTDontTranslateYourLang": "मेरो भाषाबाट अनुवाद नगर्नुहोस्", @@ -177,11 +176,8 @@ "yo": "योरुबा", "zu": "जुलु" }, - "udemyAccessTokenExpired": "तपाईंको प्रविष्ट गरिएको उडेमी पहुँच टोकनको म्याद सकिएको छ", "udemyModuleArgsNotFound": "मोड्युल आर्ग फेला नपरेको कारणले उडेमी मोड्युल डेटा प्राप्त गर्न सकेन", "VOTTranslationHelpNull": "अनुवादका लागि आवश्यक डेटा प्राप्त गर्न सकेन", - "enterUdemyAccessToken": "उडेमी पहुँच टोकन प्रविष्ट गर्नुहोस्", - "VOTUdemyData": "उडेमी डाटा", "streamNoConnectionToServer": "सर्भरसँग कुनै सम्बन्ध छैन", "searchField": "खोजी गर्नुहोस्।..", "VOTTranslateAPIErrors": "एपीआईबाट त्रुटिहरू अनुवाद गर्नुहोस्", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 प्रोक्सी कार्यकर्ताको ठेगाना प्रविष्ट गर्नुहोस्", "proxySettings": "प्रोक्सी सेटिङ", "translationTakeApproximatelyMinute2": "अनुवाद लगभग {0} मिनेट लाग्नेछ", - "VOTAudioBooster": "विस्तारित अनुवाद मात्रा वृद्धि" + "VOTAudioBooster": "विस्तारित अनुवाद मात्रा वृद्धि", + "VOTMediaCSPError": "अडियो लोड गर्न असफल भयो (मिडिया सीएसपी त्रुटि)", + "VOTSubtitlesDesign": "उपशीर्षक डिजाइन", + "VOTSubtitlesFontSize": "उपशीर्षकहरूको फन्ट साइज", + "VOTSubtitlesOpacity": "उपशीर्षक पृष्ठभूमिको पारदर्शिता" } diff --git a/src/localization/locales/nl.json b/src/localization/locales/nl.json index bf7ac3a2..41c8d7a1 100644 --- a/src/localization/locales/nl.json +++ b/src/localization/locales/nl.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Verzoek om videovertaling is mislukt", "audioNotReceived": "Audioverbinding niet ontvangen", "grantPermissionToAutoPlay": "Geef toestemming voor autoplay", - "neededAdditionalExtension": "Een extra extensie is nodig om deze site te ondersteunen", "audioFormatNotSupported": "Het audioformaat wordt niet ondersteund", "VOTAutoTranslate": "Vertalen op open", "VOTDontTranslateYourLang": "Niet vertalen vanuit mijn taal", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Uw ingevoerde Udemy Access Token is verlopen", "udemyModuleArgsNotFound": "Kon Udemy-modulegegevens niet verkrijgen vanwege het feit dat ModuleArgs niet werd gevonden", "VOTTranslationHelpNull": "Kon de gegevens die nodig zijn voor de vertaling niet krijgen", - "enterUdemyAccessToken": "Enter Udemy Access Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Er is geen verbinding met de server", "searchField": "Zoeken...", "VOTTranslateAPIErrors": "Vertaal fouten uit de API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Voer het adres van de m3u8 proxy worker in", "proxySettings": "Proxy-Instellingen", "translationTakeApproximatelyMinute2": "De vertaling duurt ongeveer {0} minuten", - "VOTAudioBooster": "Uitbreiding van het vertaalvolume" + "VOTAudioBooster": "Uitbreiding van het vertaalvolume", + "VOTMediaCSPError": "Laden van audio is mislukt (media csp-fout)", + "VOTSubtitlesDesign": "Ondertitels ontwerp", + "VOTSubtitlesFontSize": "Lettergrootte van ondertitels", + "VOTSubtitlesOpacity": "Transparantie van de ondertitelingsachtergrond" } diff --git a/src/localization/locales/pa.json b/src/localization/locales/pa.json index be1265e9..a654271a 100644 --- a/src/localization/locales/pa.json +++ b/src/localization/locales/pa.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "ਵੀਡੀਓ ਅਨੁਵਾਦ ਲਈ ਬੇਨਤੀ ਕਰਨ ਲਈ ਫੇਲ੍ਹ ਹੈ", "audioNotReceived": "ਆਡੀਓ ਲਿੰਕ ਨਹੀਂ ਮਿਲਿਆ", "grantPermissionToAutoPlay": "ਆਟੋਪਲੇ ਲਈ ਇਜਾਜ਼ਤ ਦਿਓ", - "neededAdditionalExtension": "ਇਸ ਸਾਈਟ ਦਾ ਸਮਰਥਨ ਕਰਨ ਲਈ ਇੱਕ ਵਾਧੂ ਐਕਸਟੈਂਸ਼ਨ ਦੀ ਲੋੜ ਹੈ", "audioFormatNotSupported": "ਆਡੀਓ ਫਾਰਮੈਟ ਸਹਾਇਕ ਨਹੀਂ ਹੈ", "VOTAutoTranslate": "ਓਪਨ ਉੱਤੇ ਅਨੁਵਾਦ ਕਰੋ", "VOTDontTranslateYourLang": "ਮੇਰੀ ਭਾਸ਼ਾ ਤੋਂ ਅਨੁਵਾਦ ਨਾ ਕਰੋ", @@ -177,11 +176,8 @@ "yo": "ਯੋਰੂਬਾ", "zu": "ਜ਼ੂਲੂ" }, - "udemyAccessTokenExpired": "ਤੁਹਾਡਾ ਦਿੱਤਾ ਗਿਆ ਉਡੇਮੀ ਐਕਸੈਸ ਟੋਕਨ ਖਤਮ ਹੋ ਗਿਆ ਹੈ", "udemyModuleArgsNotFound": "ਉਡੇਮੀ ਮੋਡੀਊਲ ਡਾਟਾ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ ਕਿਉਂਕਿ ਮੋਡੀਊਲ ਆਰਜੀਐਸ ਨਹੀਂ ਮਿਲਿਆ ਸੀ", "VOTTranslationHelpNull": "ਅਨੁਵਾਦ ਲਈ ਲੋੜੀਂਦਾ ਡਾਟਾ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ", - "enterUdemyAccessToken": "ਉਡੇਮੀ ਐਕਸੈਸ ਟੋਕਨ ਦਰਜ ਕਰੋ", - "VOTUdemyData": "ਉਡੇਮੀ ਡਾਟਾ", "streamNoConnectionToServer": "ਸਰਵਰ ਨਾਲ ਕੋਈ ਕੁਨੈਕਸ਼ਨ ਨਹੀਂ", "searchField": "ਖੋਜ ਕਰੋ...", "VOTTranslateAPIErrors": "ਏਪੀਆਈ ਤੋਂ ਗਲਤੀਆਂ ਅਨੁਵਾਦ ਕਰੋ", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "ਐਮ 3 ਯੂ 8 ਪ੍ਰੌਕਸੀ ਵਰਕਰ ਦਾ ਪਤਾ ਦਰਜ ਕਰੋ", "proxySettings": "ਪਰਾਕਸੀ ਸੈਟਿੰਗ", "translationTakeApproximatelyMinute2": "ਅਨੁਵਾਦ ਲਗਭਗ {0} ਮਿੰਟ ਲਵੇਗਾ", - "VOTAudioBooster": "ਵਿਸਤ੍ਰਿਤ ਅਨੁਵਾਦ ਵਾਲੀਅਮ ਵਾਧਾ" + "VOTAudioBooster": "ਵਿਸਤ੍ਰਿਤ ਅਨੁਵਾਦ ਵਾਲੀਅਮ ਵਾਧਾ", + "VOTMediaCSPError": "ਆਡੀਓ ਲੋਡ ਕਰਨ ਲਈ ਫੇਲ੍ਹ (ਮੀਡਿਆ ਸੀਐਸਪੀ ਗਲਤੀ)", + "VOTSubtitlesDesign": "ਉਪਸਿਰਲੇਖ ਡਿਜ਼ਾਈਨ", + "VOTSubtitlesFontSize": "ਉਪਸਿਰਲੇਖਾਂ ਦਾ ਫੋਂਟ ਸਾਈਜ਼", + "VOTSubtitlesOpacity": "ਉਪਸਿਰਲੇਖ ਪਿਛੋਕੜ ਦੀ ਪਾਰਦਰਸ਼ਤਾ" } diff --git a/src/localization/locales/pl.json b/src/localization/locales/pl.json index b6f469cf..0114ea59 100644 --- a/src/localization/locales/pl.json +++ b/src/localization/locales/pl.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nie udało się poprosić o tłumaczenie wideo", "audioNotReceived": "Łącze Audio nie zostało odebrane", "grantPermissionToAutoPlay": "Udziel pozwolenia na Autoodtwarzanie", - "neededAdditionalExtension": "Do obsługi tej witryny potrzebne jest dodatkowe rozszerzenie", "audioFormatNotSupported": "Format audio nie jest obsługiwany", "VOTAutoTranslate": "Translate on open", "VOTDontTranslateYourLang": "Nie tłumacz z mojego języka", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Wprowadzony Token dostępu Udemy wygasł", "udemyModuleArgsNotFound": "Nie można uzyskać danych modułu udemy ze względu na fakt, że ModuleArgs nie został znaleziony", "VOTTranslationHelpNull": "Nie można uzyskać danych wymaganych do tłumaczenia", - "enterUdemyAccessToken": "Wprowadź Token Dostępu Udemy", - "VOTUdemyData": "Dane Udemy", "streamNoConnectionToServer": "Nie ma połączenia z serwerem", "searchField": "Szukaj...", "VOTTranslateAPIErrors": "Przetłumacz błędy z API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Wprowadź adres pracownika proxy m3u8", "proxySettings": "Ustawienia Proxy", "translationTakeApproximatelyMinute2": "Tłumaczenie zajmie około {0} minut", - "VOTAudioBooster": "Rozszerzony wzrost objętości tłumaczenia" + "VOTAudioBooster": "Rozszerzony wzrost objętości tłumaczenia", + "VOTMediaCSPError": "Nie udało się załadować dźwięku (błąd media csp)", + "VOTSubtitlesDesign": "Projektowanie napisów", + "VOTSubtitlesFontSize": "Rozmiar czcionki napisów", + "VOTSubtitlesOpacity": "Przezroczystość tła napisów" } diff --git a/src/localization/locales/pt.json b/src/localization/locales/pt.json index 287161d1..5b523147 100644 --- a/src/localization/locales/pt.json +++ b/src/localization/locales/pt.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Falha ao solicitar a tradução do vídeo", "audioNotReceived": "Link de áudio não recebido", "grantPermissionToAutoPlay": "Conceda permissão para reprodução automática", - "neededAdditionalExtension": "Uma extensão adicional é necessária para suportar este site", "audioFormatNotSupported": "Formato de áudio não suportado", "VOTAutoTranslate": "Traduzir automaticamente ao abrir", "VOTDontTranslateYourLang": "Não traduzir do seu idioma nativo", @@ -176,11 +175,8 @@ "ja": "Japonês", "kri": "Crioulo" }, - "udemyAccessTokenExpired": "O Token de acesso da Udemy inserido expirou", "udemyModuleArgsNotFound": "Não foi possível obter dados do módulo udemy devido ao fato de que ModuleArgs não foi encontrado", "VOTTranslationHelpNull": "Não foi possível obter os dados necessários para a tradução", - "enterUdemyAccessToken": "Insira O Token De Acesso Da Udemy", - "VOTUdemyData": "Dados Udemy", "streamNoConnectionToServer": "Não há conexão com o servidor", "searchField": "Procura...", "VOTTranslateAPIErrors": "Traduzir erros da API", @@ -191,5 +187,9 @@ "VOTM3u8ProxyHost": "Insira o endereço do trabalhador proxy m3u8", "proxySettings": "Definições De Proxy", "translationTakeApproximatelyMinute2": "A tradução demora cerca de {0} minutos", - "VOTAudioBooster": "Aumento do volume de tradução alargado" + "VOTAudioBooster": "Aumento do volume de tradução alargado", + "VOTMediaCSPError": "Falha ao carregar áudio (erro de csp de mídia)", + "VOTSubtitlesDesign": "Design de Legendas", + "VOTSubtitlesFontSize": "Tamanho da fonte das legendas", + "VOTSubtitlesOpacity": "Transparência do fundo do subtítulo" } diff --git a/src/localization/locales/ro.json b/src/localization/locales/ro.json index 8375a586..28a8fac6 100644 --- a/src/localization/locales/ro.json +++ b/src/localization/locales/ro.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nu s-a solicitat traducerea video", "audioNotReceived": "Link-ul Audio nu a fost primit", "grantPermissionToAutoPlay": "Acordați permisiunea de redare automată", - "neededAdditionalExtension": "Este necesară o extensie suplimentară pentru a sprijini acest site", "audioFormatNotSupported": "Formatul audio nu este acceptat", "VOTAutoTranslate": "Traduceți pe deschis", "VOTDontTranslateYourLang": "Nu Traduceți din limba mea", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Jetonul dvs. de acces Udemy introdus a expirat", "udemyModuleArgsNotFound": "Nu s-au putut obține datele modulului udemy datorită faptului că ModuleArgs nu a fost găsit", "VOTTranslationHelpNull": "Nu s - au putut obține datele necesare pentru traducere", - "enterUdemyAccessToken": "Introduceți Jetonul De Acces Udemy", - "VOTUdemyData": "Datele Udemy", "streamNoConnectionToServer": "Nu există nicio conexiune la server", "searchField": "Căutare...", "VOTTranslateAPIErrors": "Traduceți erorile din API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Introduceți adresa lucrătorului proxy m3u8", "proxySettings": "Setări Proxy", "translationTakeApproximatelyMinute2": "Traducerea va dura aproximativ {0} minute", - "VOTAudioBooster": "Creșterea extinsă a volumului de traducere" + "VOTAudioBooster": "Creșterea extinsă a volumului de traducere", + "VOTMediaCSPError": "Nu s-a încărcat audio (eroare media csp)", + "VOTSubtitlesDesign": "Design subtitrari", + "VOTSubtitlesFontSize": "Dimensiunea fontului subtitrărilor", + "VOTSubtitlesOpacity": "Transparența fundalului subtitrării" } diff --git a/src/localization/locales/ru.json b/src/localization/locales/ru.json index 02499154..40ea0f86 100644 --- a/src/localization/locales/ru.json +++ b/src/localization/locales/ru.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Не удалось запросить перевод видео", "audioNotReceived": "Не получена ссылка на аудио", "grantPermissionToAutoPlay": "Предоставьте разрешение на автовоспроизведение", - "neededAdditionalExtension": "Для поддержки этого сайта необходимо дополнительное расширение", "audioFormatNotSupported": "Формат аудио не поддерживается", "VOTAutoTranslate": "Переводить при открытии", "VOTDontTranslateYourLang": "Не переводить с родного языка", @@ -177,11 +176,8 @@ "ja": "Японский", "kri": "Кри" }, - "udemyAccessTokenExpired": "Срок действия введённого вами токена доступа к Udemy истёк", "udemyModuleArgsNotFound": "Не удалось получить данные модуля udemy из-за того, что ModuleArgs не найден", "VOTTranslationHelpNull": "Не удалось получить данные, необходимые для перевода", - "enterUdemyAccessToken": "Введите токен доступа Udemy", - "VOTUdemyData": "Данные Udemy", "streamNoConnectionToServer": "Нет подключения к серверу", "searchField": "Поиск...", "VOTTranslateAPIErrors": "Перевод ошибок из API", @@ -191,6 +187,10 @@ "VOTProxyWorkerHost": "Введите адрес worker прокси-сервера", "VOTM3u8ProxyHost": "Введите адрес m3u8 прокси-сервера", "proxySettings": "Настройки прокси-сервера", - "translationTakeApproximatelyMinute2": "Перевод займет примерно {0} минуту", - "VOTAudioBooster": "Расширенное увеличение громкости перевода" + "translationTakeApproximatelyMinute2": "Перевод займёт примерно {0} минуту", + "VOTAudioBooster": "Расширенное увеличение громкости перевода", + "VOTMediaCSPError": "Не удалось загрузить аудио (ошибка media csp)", + "VOTSubtitlesDesign": "Оформление субтитров", + "VOTSubtitlesFontSize": "Размер шрифта субтитров", + "VOTSubtitlesOpacity": "Прозрачность фона субтитров" } diff --git a/src/localization/locales/si.json b/src/localization/locales/si.json index 21643b8d..09c5a09d 100644 --- a/src/localization/locales/si.json +++ b/src/localization/locales/si.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "වීඩියෝ පරිවර්තනය ඉල්ලා සිටීමට අසමත්", "audioNotReceived": "ශ්රව්ය සබැඳිය ලැබී නැත", "grantPermissionToAutoPlay": "ස්වයංක් රීයව ක් රීඩා කිරීමට අවසර ලබා දෙන්න", - "neededAdditionalExtension": "මෙම වෙබ් අඩවියට සහාය වීම සඳහා අතිරේක දිගුවක් අවශ් ය වේ", "audioFormatNotSupported": "ශ් රව් ය ආකෘතිය සහාය නොදක්වයි", "VOTAutoTranslate": "විවෘත මත පරිවර්තනය කරන්න", "VOTDontTranslateYourLang": "මගේ භාෂාවෙන් පරිවර්තනය කරන්න එපා", @@ -177,11 +176,8 @@ "yo": "යෝරුබා", "zu": "සුලු" }, - "udemyAccessTokenExpired": "ඔබගේ ඇතුලත් Udemy ප්රවේශ ටෝකනය කල් ඉකුත් වී ඇත", "udemyModuleArgsNotFound": "ModuleArgs සොයාගත නොහැකි වීම නිසා udemy මොඩියුල දත්ත ලබා ගැනීමට නොහැකි විය", "VOTTranslationHelpNull": "පරිවර්තනය සඳහා අවශ්ය දත්ත ලබා ගැනීමට නොහැකි විය", - "enterUdemyAccessToken": "Udemy ප්රවේශ සංකේතය ඇතුලත් කරන්න", - "VOTUdemyData": "උඩෙමි දත්ත", "streamNoConnectionToServer": "සර්වර් එකට කිසිම සම්බන්ධයක් නෑ", "searchField": "හොයන්න...", "VOTTranslateAPIErrors": "API වෙතින් දෝෂ පරිවර්තනය කරන්න", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 ප්රොක්සි සේවකයාගේ ලිපිනය ඇතුලත් කරන්න", "proxySettings": "ප්රොක්සි සැකසුම්", "translationTakeApproximatelyMinute2": "පරිවර්තනය සඳහා ආසන්න වශයෙන් {0} විනාඩි ගතවනු ඇත", - "VOTAudioBooster": "දීර්ඝ පරිවර්තන පරිමාව වැඩි කිරීම" + "VOTAudioBooster": "දීර්ඝ පරිවර්තන පරිමාව වැඩි කිරීම", + "VOTMediaCSPError": "ශ්රව්ය පූරණය කිරීමට අසමත් (මාධ්ය csp දෝෂය)", + "VOTSubtitlesDesign": "උපසිරැසි නිර්මාණය", + "VOTSubtitlesFontSize": "උපසිරැසි වල අකුරු ප්රමාණය", + "VOTSubtitlesOpacity": "උපසිරැසි පසුබිමේ විනිවිදභාවය" } diff --git a/src/localization/locales/sk.json b/src/localization/locales/sk.json index 22b8a8fe..92e0fc5a 100644 --- a/src/localization/locales/sk.json +++ b/src/localization/locales/sk.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nepodarilo sa požiadať o preklad videa", "audioNotReceived": "Audio odkaz nebol prijatý", "grantPermissionToAutoPlay": "Udeliť povolenie na automatické prehrávanie", - "neededAdditionalExtension": "Na podporu tejto stránky je potrebné ďalšie rozšírenie", "audioFormatNotSupported": "Formát zvuku nie je podporovaný", "VOTAutoTranslate": "Preložiť na open", "VOTDontTranslateYourLang": "Neprekladajte z môjho jazyka", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Platnosť zadaného prístupového tokenu Udemy vypršala", "udemyModuleArgsNotFound": "Nepodarilo sa získať udemy modul dáta vzhľadom k tomu, že ModuleArgs nebol nájdený", "VOTTranslationHelpNull": "Nepodarilo sa získať údaje potrebné pre preklad", - "enterUdemyAccessToken": "Zadajte Prístupový Token Udemy", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Neexistuje žiadne pripojenie k serveru", "searchField": "Vyhľadávanie...", "VOTTranslateAPIErrors": "Preklad chýb z API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Zadajte adresu pracovníka proxy m3u8", "proxySettings": "Nastavenia Servera Proxy", "translationTakeApproximatelyMinute2": "Preklad bude trvať približne {0} minút", - "VOTAudioBooster": "Rozšírené zvýšenie objemu prekladu" + "VOTAudioBooster": "Rozšírené zvýšenie objemu prekladu", + "VOTMediaCSPError": "Nepodarilo sa načítať zvuk (chyba media csp)", + "VOTSubtitlesDesign": "Dizajn titulkov", + "VOTSubtitlesFontSize": "Veľkosť písma titulkov", + "VOTSubtitlesOpacity": "Transparentnosť pozadia titulkov" } diff --git a/src/localization/locales/sl.json b/src/localization/locales/sl.json index 02ad2fa9..801166b6 100644 --- a/src/localization/locales/sl.json +++ b/src/localization/locales/sl.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Ni bilo mogoče zahtevati prevoda videa", "audioNotReceived": "Zvočna povezava ni prejeta", "grantPermissionToAutoPlay": "Podelite dovoljenje za samodejno predvajanje", - "neededAdditionalExtension": "Za podporo tega spletnega mesta je potrebna dodatna razširitev", "audioFormatNotSupported": "Zvočni format ni podprt", "VOTAutoTranslate": "Prevedi na odprto", "VOTDontTranslateYourLang": "Ne prevajaj iz mojega jezika", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Vaš vneseni žeton za dostop je potekel", "udemyModuleArgsNotFound": "Ni bilo mogoče dobiti podatkov o modulu zaradi dejstva, da ModuleArgs ni bilo mogoče najti", "VOTTranslationHelpNull": "Ni bilo mogoče dobiti podatkov, potrebnih za prevajanje", - "enterUdemyAccessToken": "Vnesite Žeton Za Dostop", - "VOTUdemyData": "Podatki O Udemiju", "streamNoConnectionToServer": "Ni povezave s strežnikom", "searchField": "Išči...", "VOTTranslateAPIErrors": "Prevedi napake iz API-ja", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Vnesite naslov pooblaščenca m3u8", "proxySettings": "Nastavitve Posrednika", "translationTakeApproximatelyMinute2": "Prevod bo trajal približno {0} minut", - "VOTAudioBooster": "Povečanje obsega razširjenega prevajanja" + "VOTAudioBooster": "Povečanje obsega razširjenega prevajanja", + "VOTMediaCSPError": "Ni bilo mogoče naložiti zvoka (napaka media CSP)", + "VOTSubtitlesDesign": "Oblikovanje podnapisov", + "VOTSubtitlesFontSize": "Velikost pisave podnapisov", + "VOTSubtitlesOpacity": "Preglednost ozadja podnapisov" } diff --git a/src/localization/locales/sq.json b/src/localization/locales/sq.json index a4b0b0dd..a0452cf8 100644 --- a/src/localization/locales/sq.json +++ b/src/localization/locales/sq.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Nuk arriti të kërkojë përkthim video", "audioNotReceived": "Lidhja Audio nuk është marrë", "grantPermissionToAutoPlay": "Jepni leje për autoplay", - "neededAdditionalExtension": "Një shtesë shtesë është e nevojshme për të mbështetur këtë faqe", "audioFormatNotSupported": "Formati audio nuk mbështetet", "VOTAutoTranslate": "Përktheni në open", "VOTDontTranslateYourLang": "Mos përktheni nga gjuha ime", @@ -177,11 +176,8 @@ "yo": "Joruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Hyrja Juaj Në Udemy Access Token ka skaduar", "udemyModuleArgsNotFound": "Nuk mund të marrë të dhënat e modulit udemy për shkak të faktit Se ModuleArgs nuk u gjet", "VOTTranslationHelpNull": "Nuk mund të merrte të dhënat e kërkuara për përkthimin", - "enterUdemyAccessToken": "Shkruani Udemy Access Token", - "VOTUdemyData": "Të Dhënat E Udemy", "streamNoConnectionToServer": "Nuk ka lidhje me serverin", "searchField": "Kërko...", "VOTTranslateAPIErrors": "Përktheni gabimet NGA API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Vendosni adresën e punonjësit të përfaqësuesit m3u8", "proxySettings": "Cilësimet E Përfaqësuesit", "translationTakeApproximatelyMinute2": "Përkthimi do të zgjasë afërsisht {0} minuta", - "VOTAudioBooster": "Rritja e vëllimit të përkthimit të zgjeruar" + "VOTAudioBooster": "Rritja e vëllimit të përkthimit të zgjeruar", + "VOTMediaCSPError": "Nuk arriti të ngarkojë audio (gabim media csp)", + "VOTSubtitlesDesign": "Dizajni i titrave", + "VOTSubtitlesFontSize": "Madhësia e shkronjave të titrave", + "VOTSubtitlesOpacity": "Transparenca e sfondit të titrave" } diff --git a/src/localization/locales/sr.json b/src/localization/locales/sr.json index 029ccd1a..ddc9f506 100644 --- a/src/localization/locales/sr.json +++ b/src/localization/locales/sr.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Захтев за превод видео записа није успео", "audioNotReceived": "Аудио комуникација није примљена", "grantPermissionToAutoPlay": "Дајте дозволу за аутоматску репродукцију", - "neededAdditionalExtension": "За подршку овој веб локацији потребно је додатно проширење", "audioFormatNotSupported": "Аудио формат није подржан", "VOTAutoTranslate": "Преведи на отворено", "VOTDontTranslateYourLang": "Не преводите са мог језика", @@ -177,11 +176,8 @@ "yo": "Јоруба", "zu": "Зулуски" }, - "udemyAccessTokenExpired": "Удеми приступни токен који сте унели је истекао", "udemyModuleArgsNotFound": "Није могуће добити податке о Удеми модулу јер Модулеаргс није пронађен", "VOTTranslationHelpNull": "Није могуће добити податке потребне за превод", - "enterUdemyAccessToken": "Унесите Удеми приступни токен", - "VOTUdemyData": "Удеми Подаци", "streamNoConnectionToServer": "Нема везе са сервером", "searchField": "Претрага...", "VOTTranslateAPIErrors": "Превођење грешака из АПИ-ја", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Унесите адресу радног проки сервера м3у8", "proxySettings": "Подешавања проксија", "translationTakeApproximatelyMinute2": "Превод ће трајати приближно {0} минута", - "VOTAudioBooster": "Повећање обима проширеног трансфера" + "VOTAudioBooster": "Повећање обима проширеног трансфера", + "VOTMediaCSPError": "Преузимање звука није успело (Медиа ЦСП грешка)", + "VOTSubtitlesDesign": "Дизајн титлова", + "VOTSubtitlesFontSize": "Величина фонта титлова", + "VOTSubtitlesOpacity": "Транспарентност позадине титлова" } diff --git a/src/localization/locales/su.json b/src/localization/locales/su.json index 2068e6de..f0963df2 100644 --- a/src/localization/locales/su.json +++ b/src/localization/locales/su.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Gagal menta tarjamahan video", "audioNotReceived": "Link Audio teu narima", "grantPermissionToAutoPlay": "Méré idin pikeun autoplay", - "neededAdditionalExtension": "Hiji extension tambahan diperlukeun pikeun ngarojong situs ieu", "audioFormatNotSupported": "Format audio teu dirojong", "VOTAutoTranslate": "Tarjamahkeun dina buka", "VOTDontTranslateYourLang": "Ulah narjamahkeun tina basa kuring", @@ -177,11 +176,8 @@ "yo": "Basa Yoruba", "zu": "Basa Zulu" }, - "udemyAccessTokenExpired": "Anjeun asup Udemy Aksés Token geus tamat", "udemyModuleArgsNotFound": "Teu bisa meunangkeun data modul udemy alatan kanyataan Yén ModuleArgs teu kapanggih", "VOTTranslationHelpNull": "Teu bisa meunangkeun data diperlukeun pikeun narjamahkeun", - "enterUdemyAccessToken": "Asupkeun Udemy Aksés Token", - "VOTUdemyData": "Data Udemy", "streamNoConnectionToServer": "Teu aya sambungan ka server", "searchField": "Néangan...", "VOTTranslateAPIErrors": "Narjamahkeun kasalahan TINA API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Tuliskeun alamat worker proxy m3u8", "proxySettings": "Setélan Proxy", "translationTakeApproximatelyMinute2": "Tarjamahan bakal nyandak kira-kira {0} menit", - "VOTAudioBooster": "Paningkatan volume tarjamahan anu diperpanjang" + "VOTAudioBooster": "Paningkatan volume tarjamahan anu diperpanjang", + "VOTMediaCSPError": "Gagal pikeun muka audio (media csp error)", + "VOTSubtitlesDesign": "Desain subtitle", + "VOTSubtitlesFontSize": "Ukuran font tina subtitles", + "VOTSubtitlesOpacity": "Transparansi tina latar subjudul" } diff --git a/src/localization/locales/sv.json b/src/localization/locales/sv.json index df1440a2..33fa88de 100644 --- a/src/localization/locales/sv.json +++ b/src/localization/locales/sv.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Misslyckades med att begära videoöversättning", "audioNotReceived": "Ljudlänk inte mottagen", "grantPermissionToAutoPlay": "Ge tillstånd till autoplay", - "neededAdditionalExtension": "En ytterligare förlängning behövs för att stödja denna webbplats", "audioFormatNotSupported": "Ljudformatet stöds inte", "VOTAutoTranslate": "Översätt på öppna", "VOTDontTranslateYourLang": "Översätt inte från mitt språk", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Din inmatade Udemy Access Token har gått ut", "udemyModuleArgsNotFound": "Kunde inte få Udemy moduldata på grund av att ModuleArgs inte hittades", "VOTTranslationHelpNull": "Kunde inte få de uppgifter som krävs för översättningen", - "enterUdemyAccessToken": "Ange Udemy Access Token", - "VOTUdemyData": "Udemy Data", "streamNoConnectionToServer": "Det finns ingen anslutning till servern", "searchField": "Söka...", "VOTTranslateAPIErrors": "Översätt fel från API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Ange adressen till M3U8-proxyarbetaren", "proxySettings": "Proxyinställningar", "translationTakeApproximatelyMinute2": "Översättningen tar ungefär {0} minuter", - "VOTAudioBooster": "Utökad volymökning av översättning" + "VOTAudioBooster": "Utökad volymökning av översättning", + "VOTMediaCSPError": "Det gick inte att ladda ljud (media csp-fel)", + "VOTSubtitlesDesign": "Textning design", + "VOTSubtitlesFontSize": "Teckenstorlek på undertexter", + "VOTSubtitlesOpacity": "Öppenhet i undertextens bakgrund" } diff --git a/src/localization/locales/sw.json b/src/localization/locales/sw.json index 14e1a941..bb9a7b08 100644 --- a/src/localization/locales/sw.json +++ b/src/localization/locales/sw.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Imeshindwa kuomba tafsiri ya video", "audioNotReceived": "Kiungo cha sauti hakijapokelewa", "grantPermissionToAutoPlay": "Ruzuku ruhusa ya kucheza kiotomatiki", - "neededAdditionalExtension": "Upanuzi wa ziada unahitajika kusaidia tovuti hii", "audioFormatNotSupported": "Umbizo la sauti halitumiki", "VOTAutoTranslate": "Tafsiri juu ya wazi", "VOTDontTranslateYourLang": "Usitafsiri kutoka kwa lugha yangu", @@ -177,11 +176,8 @@ "yo": "Wayoruba", "zu": "Kizulu" }, - "udemyAccessTokenExpired": "Ishara yako ya Ufikiaji Wa Udemy imeisha muda wake", "udemyModuleArgsNotFound": "Haikuweza kupata data ya moduli ya udemy kutokana na ukweli Kwamba ModuleArgs haikupatikana", "VOTTranslationHelpNull": "Haikuweza kupata data inayohitajika kwa tafsiri", - "enterUdemyAccessToken": "Ingiza Ishara Ya Ufikiaji Wa Udemy", - "VOTUdemyData": "Takwimu Za Udemy", "streamNoConnectionToServer": "Hakuna uhusiano na server", "searchField": "Tafuta...", "VOTTranslateAPIErrors": "Tafsiri makosa KUTOKA API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Ingiza anwani ya mfanyakazi wa wakala wa m3u8", "proxySettings": "Mipangilio Ya Wakala", "translationTakeApproximatelyMinute2": "Tafsiri itachukua takriban {0} dakika", - "VOTAudioBooster": "Kuongezeka kwa kiasi cha tafsiri" + "VOTAudioBooster": "Kuongezeka kwa kiasi cha tafsiri", + "VOTMediaCSPError": "Imeshindwa kupakia sauti (kosa la media csp)", + "VOTSubtitlesDesign": "Subtitles kubuni", + "VOTSubtitlesFontSize": "Ukubwa wa fonti ya manukuu", + "VOTSubtitlesOpacity": "Uwazi wa mandharinyuma ya manukuu" } diff --git a/src/localization/locales/tr.json b/src/localization/locales/tr.json index b9f63daf..650d0508 100644 --- a/src/localization/locales/tr.json +++ b/src/localization/locales/tr.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Video çevirisi isteği başarısız oldu", "audioNotReceived": "Ses bağlantısı alınamadı", "grantPermissionToAutoPlay": "Otomatik oynatma için izin verin", - "neededAdditionalExtension": "Bu siteyi desteklemek için ek bir uzantıya ihtiyaç var", "audioFormatNotSupported": "Ses formatı desteklenmiyor", "VOTAutoTranslate": "Açılışta çevir", "VOTDontTranslateYourLang": "Ana dilinizden çevirme", @@ -177,11 +176,8 @@ "ja": "Japonca", "kri": "Krio" }, - "udemyAccessTokenExpired": "Girdiğiniz Udemy Erişim Belirtecinizin süresi doldu", "udemyModuleArgsNotFound": "ModuleArgs bulunamadığı için udemy modül verileri alınamadı", "VOTTranslationHelpNull": "Çeviri için gereken veriler alınamadı", - "enterUdemyAccessToken": "Udemy Erişim Belirtecini Girin", - "VOTUdemyData": "Udemy Verileri", "streamNoConnectionToServer": "Sunucuya bağlantı yok", "searchField": "Aramak...", "VOTTranslateAPIErrors": "API'DEN hataları çevir", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 proxy çalışanının adresini girin", "proxySettings": "Proxy Ayarları", "translationTakeApproximatelyMinute2": "Çeviri yaklaşık {0} dakika sürecektir", - "VOTAudioBooster": "Genişletilmiş çeviri hacmi artışı" + "VOTAudioBooster": "Genişletilmiş çeviri hacmi artışı", + "VOTMediaCSPError": "Ses yüklenemedi (ortam csp hatası)", + "VOTSubtitlesDesign": "Altyazı tasarımı", + "VOTSubtitlesFontSize": "Altyazıların yazı tipi boyutu", + "VOTSubtitlesOpacity": "Altyazı arka planının şeffaflığı" } diff --git a/src/localization/locales/uk.json b/src/localization/locales/uk.json index 4c289215..d06f895c 100644 --- a/src/localization/locales/uk.json +++ b/src/localization/locales/uk.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Не вдалося запросити Переклад відео", "audioNotReceived": "Аудіосвязь не отримана", "grantPermissionToAutoPlay": "Надайте дозвіл на автоматичне відтворення", - "neededAdditionalExtension": "Для підтримки цього сайту необхідно додаткове розширення", "audioFormatNotSupported": "Аудіоформат не підтримується", "VOTAutoTranslate": "Перевести на відкритий", "VOTDontTranslateYourLang": "Не перекладайте з моєї мови", @@ -177,11 +176,8 @@ "yo": "Йоруба", "zu": "Зулуський" }, - "udemyAccessTokenExpired": "Термін дії введеного вами токена доступу Udemy закінчився", "udemyModuleArgsNotFound": "Не вдалося отримати дані модуля udemy через те, що ModuleArgs не знайдено", "VOTTranslationHelpNull": "Не вдалося отримати дані, необхідні для перекладу", - "enterUdemyAccessToken": "Введіть маркер доступу Udemy", - "VOTUdemyData": "Дані Udemy", "streamNoConnectionToServer": "Немає підключення до сервера", "searchField": "Пошук...", "VOTTranslateAPIErrors": "Переклад помилок з API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Введіть адресу m3u8 проксі-сервера", "proxySettings": "Налаштування проксі-сервера", "translationTakeApproximatelyMinute2": "Переклад займе приблизно {0} хвилину", - "VOTAudioBooster": "Збільшення обсягу розширеного перекладу" + "VOTAudioBooster": "Збільшення обсягу розширеного перекладу", + "VOTMediaCSPError": "Не вдалося завантажити аудіо (помилка media csp)", + "VOTSubtitlesDesign": "Дизайн субтитрів", + "VOTSubtitlesFontSize": "Розмір шрифту субтитрів", + "VOTSubtitlesOpacity": "Прозорість фону субтитрів" } diff --git a/src/localization/locales/ur.json b/src/localization/locales/ur.json index 5d9c4a90..acb8b9c4 100644 --- a/src/localization/locales/ur.json +++ b/src/localization/locales/ur.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "ویڈیو ترجمہ کرنے میں ناکامی", "audioNotReceived": "آڈیو کا لنک حاصل نہیں ہوا", "grantPermissionToAutoPlay": "خودکار پلے کرنے کی اجازت دیں", - "neededAdditionalExtension": "اس ویب سائٹ کو حمایت کرنے کے لیے اضافی ایکسٹینشن کی ضرورت ہے", "audioFormatNotSupported": "آڈیو فارمیٹ کی حمایت نہیں کی جاتی", "VOTAutoTranslate": "کھولنے پر ترجمہ کریں", "VOTDontTranslateYourLang": "اپنی زبان سے ترجمہ نہ کریں", @@ -177,11 +176,8 @@ "ja": "جاپانی", "kri": "کری" }, - "udemyAccessTokenExpired": "آپ کے داخل کردہ اڈیمی رسائی ٹوکن کی میعاد ختم ہوگئی ہے", "udemyModuleArgsNotFound": "حقیقت یہ ہے کہ ModuleArgs نہیں ملا تھا کی وجہ سے udemy ماڈیول ڈیٹا حاصل نہیں کر سکا", "VOTTranslationHelpNull": "ترجمہ کے لیے درکار ڈیٹا حاصل نہیں کر سکا ۔ ", - "enterUdemyAccessToken": "Udemy رسائی ٹوکن درج کریں", - "VOTUdemyData": "اڈیمی ڈیٹا", "streamNoConnectionToServer": "سرور سے کوئی تعلق نہیں ہے", "searchField": "تلاش...", "VOTTranslateAPIErrors": "API سے غلطیوں کا ترجمہ کریں", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 پراکسی ورکر کا پتہ درج کریں ۔ ", "proxySettings": "پراکسی کی ترتیبات", "translationTakeApproximatelyMinute2": "ترجمہ میں تقریبا {0} منٹ لگیں گے", - "VOTAudioBooster": "توسیع شدہ ترجمہ حجم میں اضافہ" + "VOTAudioBooster": "توسیع شدہ ترجمہ حجم میں اضافہ", + "VOTMediaCSPError": "آڈیو لوڈ کرنے میں ناکام (میڈیا سی ایس پی کی خرابی)", + "VOTSubtitlesDesign": "سب ٹائٹلز ڈیزائن", + "VOTSubtitlesFontSize": "سب ٹائٹلز کا فونٹ سائز", + "VOTSubtitlesOpacity": "ذیلی عنوان کے پس منظر کی شفافیت" } diff --git a/src/localization/locales/uz.json b/src/localization/locales/uz.json index 51c4eb9d..a8ddb3a7 100644 --- a/src/localization/locales/uz.json +++ b/src/localization/locales/uz.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Video tarjimasini talab qilib bo'lmadi", "audioNotReceived": "Audio havola qabul qilinmadi", "grantPermissionToAutoPlay": "Autoplay-ga ruxsat bering", - "neededAdditionalExtension": "Ushbu saytni qo'llab-quvvatlash uchun qo'shimcha kengaytma kerak", "audioFormatNotSupported": "Audio format mavjud emas", "VOTAutoTranslate": "Ochiq tarjima qiling", "VOTDontTranslateYourLang": "Mening tilimdan tarjima qilmang", @@ -177,11 +176,8 @@ "yo": "Yoruba", "zu": "Zulu" }, - "udemyAccessTokenExpired": "Sizning kiritilgan Udemy kirish Tokeningiz muddati tugadi", "udemyModuleArgsNotFound": "ModuleArgs topilmagani uchun udemy moduli ma'lumotlarini ololmadi", "VOTTranslationHelpNull": "Tarjima uchun zarur ma'lumotlarni olish bo'lmadi", - "enterUdemyAccessToken": "Udemy Kirish Tokenini Kiriting", - "VOTUdemyData": "Udemy Ma'lumotlari", "streamNoConnectionToServer": "Serverga ulanish yo'q", "searchField": "Qidirish...", "VOTTranslateAPIErrors": "API-dan xatolarni tarjima qiling", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "M3u8 proksi-serverining manzilini kiriting", "proxySettings": "Proksi Moslamalari", "translationTakeApproximatelyMinute2": "Tarjima taxminan {0} daqiqa davom etadi", - "VOTAudioBooster": "Kengaytirilgan tarjima hajmini oshirish" + "VOTAudioBooster": "Kengaytirilgan tarjima hajmini oshirish", + "VOTMediaCSPError": "Ovozni yuklab bo'lmadi (media CSP xatosi)", + "VOTSubtitlesDesign": "Subtitrlar dizayni", + "VOTSubtitlesFontSize": "Subtitrlarning shrift o'lchami", + "VOTSubtitlesOpacity": "Subtitr fonining shaffofligi" } diff --git a/src/localization/locales/vi.json b/src/localization/locales/vi.json index 83c2ff05..3f35a1e4 100644 --- a/src/localization/locales/vi.json +++ b/src/localization/locales/vi.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Không thể yêu cầu dịch video", "audioNotReceived": "Không nhận được liên kết âm thanh", "grantPermissionToAutoPlay": "Cấp quyền tự động phát", - "neededAdditionalExtension": "Phải cài đặt tiện ích mở rộng bổ sung để hỗ trợ trang web này", "audioFormatNotSupported": "Định dạng âm thanh không được hỗ trợ", "VOTAutoTranslate": "Tự động dịch khi mở", "VOTDontTranslateYourLang": "Không dịch từ ngôn ngữ gốc của bạn", @@ -177,11 +176,8 @@ "ja": "Nhật", "kri": "Kri" }, - "udemyAccessTokenExpired": "Mã Thông Báo Truy Cập udemy đã nhập của bạn đã hết hạn", "udemyModuleArgsNotFound": "Không thể lấy dữ liệu mô-đun udemy do Thực tế Là Không tìm Thấy Mô-Đun", "VOTTranslationHelpNull": "Không thể nhận được dữ liệu cần thiết cho bản dịch", - "enterUdemyAccessToken": "Nhập Mã Thông Báo Truy Cập Udemy", - "VOTUdemyData": "Dữ Liệu Udemy", "streamNoConnectionToServer": "Không có kết nối đến máy chủ", "searchField": "Tìm kiếm...", "VOTTranslateAPIErrors": "Dịch lỗi TỪ API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Nhập địa chỉ của nhân viên proxy m3u8", "proxySettings": "Cài Đặt Proxy", "translationTakeApproximatelyMinute2": "Bản dịch sẽ mất khoảng {0} phút", - "VOTAudioBooster": "Mở rộng khối lượng dịch tăng" + "VOTAudioBooster": "Mở rộng khối lượng dịch tăng", + "VOTMediaCSPError": "Không tải được âm thanh (lỗi media csp)", + "VOTSubtitlesDesign": "Thiết kế phụ đề", + "VOTSubtitlesFontSize": "Kích thước phông chữ của phụ đề", + "VOTSubtitlesOpacity": "Độ trong suốt của nền phụ đề" } diff --git a/src/localization/locales/zh.json b/src/localization/locales/zh.json index 0535e30c..eb1ffb61 100644 --- a/src/localization/locales/zh.json +++ b/src/localization/locales/zh.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "请求视频翻译失败", "audioNotReceived": "未收到音频链接", "grantPermissionToAutoPlay": "授予自动播放权限", - "neededAdditionalExtension": "需要一个额外的扩展来支持这个网站", "audioFormatNotSupported": "不支持音频格式", "VOTAutoTranslate": "打开翻译", "VOTDontTranslateYourLang": "不要从我的语言翻译", @@ -177,11 +176,8 @@ "yo": "约鲁巴", "zu": "祖鲁" }, - "udemyAccessTokenExpired": "您输入的Udemy访问令牌已过期", "udemyModuleArgsNotFound": "由于找不到ModuleArgs,无法获取udemy模块数据", "VOTTranslationHelpNull": "无法获得翻译所需的数据", - "enterUdemyAccessToken": "输入Udemy访问令牌", - "VOTUdemyData": "Udemy数据", "streamNoConnectionToServer": "没有连接到服务器", "searchField": "搜索。..", "VOTTranslateAPIErrors": "从API翻译错误", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "输入m3u8代理worker的地址", "proxySettings": "代理设置", "translationTakeApproximatelyMinute2": "翻译大约需要{0}分钟", - "VOTAudioBooster": "扩展翻译量增加" + "VOTAudioBooster": "扩展翻译量增加", + "VOTMediaCSPError": "加载音频失败(媒体csp错误)", + "VOTSubtitlesDesign": "字幕设计", + "VOTSubtitlesFontSize": "字幕的字体大小", + "VOTSubtitlesOpacity": "字幕背景的透明度" } diff --git a/src/localization/locales/zu.json b/src/localization/locales/zu.json index 9b526ff4..f03ec519 100644 --- a/src/localization/locales/zu.json +++ b/src/localization/locales/zu.json @@ -20,7 +20,6 @@ "requestTranslationFailed": "Yehlulekile ukucela ukuhunyushwa kwevidiyo", "audioNotReceived": "Isixhumanisi somsindo asitholakalanga", "grantPermissionToAutoPlay": "Imvume yokudlala ngokuzenzakalelayo", - "neededAdditionalExtension": "Kudingeka isandiso esengeziwe ukuze kusekelwe le ndawo", "audioFormatNotSupported": "Ifomethi yomsindo ayisekelwa", "VOTAutoTranslate": "Ukuhumusha ku-open", "VOTDontTranslateYourLang": "Ungahumushi kusuka olimini lwami", @@ -177,11 +176,8 @@ "yo": "Isiyoruba", "zu": "Isizulu" }, - "udemyAccessTokenExpired": "Udemy Wakho wangena Ukufinyelela Ithokheni uphelelwe yisikhathi", "udemyModuleArgsNotFound": "Ayikwazanga ukuthola idatha ye udemy module ngenxa yokuthi I ModuleArgs ayitholakalanga", "VOTTranslationHelpNull": "Ayikwazanga ukuthola imininingwane edingekayo ukuze kuhunyushwe", - "enterUdemyAccessToken": "Faka Udemy Ukufinyelela Ithokheni", - "VOTUdemyData": "Idatha Ye-Udemy", "streamNoConnectionToServer": "Akukho ukuxhumana neseva", "searchField": "Ukucinga...", "VOTTranslateAPIErrors": "Humusha amaphutha KUSUKA API", @@ -192,5 +188,9 @@ "VOTM3u8ProxyHost": "Faka ikheli le-m3u8 proxy worker", "proxySettings": "Izilungiselelo Ze-Proxy", "translationTakeApproximatelyMinute2": "Ukuhumusha kuzothatha cishe imizuzu {0}", - "VOTAudioBooster": "Ukwanda kwevolumu yokuhumusha okwandisiwe" + "VOTAudioBooster": "Ukwanda kwevolumu yokuhumusha okwandisiwe", + "VOTMediaCSPError": "Yehlulekile ukulayisha audio (media csp iphutha)", + "VOTSubtitlesDesign": "Umklamo wemibhalo engezansi", + "VOTSubtitlesFontSize": "Usayizi wefonti yemibhalo engezansi", + "VOTSubtitlesOpacity": "Ukungafihli lutho kwesizinda sesihlokwana" } diff --git a/src/localization/localizationProvider.js b/src/localization/localizationProvider.js index 005eed76..2d010d31 100644 --- a/src/localization/localizationProvider.js +++ b/src/localization/localizationProvider.js @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import defaultLocale from "./locales/en.json"; import debug from "../utils/debug.js"; import { votStorage } from "../utils/storage.js"; diff --git a/src/rsp.js b/src/rsp.js deleted file mode 100644 index 6d45a176..00000000 --- a/src/rsp.js +++ /dev/null @@ -1,35 +0,0 @@ -import { getUUID } from "./getUUID.js"; -import { getSignature } from "./getSignature.js"; -import { yandexProtobuf } from "./yandexProtobuf.js"; -import debug from "./utils/debug.js"; - -// Request stream ping from Yandex API -async function requestStreamPing(pingId, callback) { - try { - debug.log("requestStreamPing"); - // ! CURRENT CLOUDFLARE WORKER DOESN'T SUPPORT STREAM TRANSLATIONS - const yar = await import( - `./yandexRequest${BUILD_MODE === "cloudflare" ? "-cloudflare" : ""}.js` - ); - const yandexRequest = yar.default; - debug.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeStreamPingRequest(pingId); - // Send the request - await yandexRequest( - "/stream-translation/ping-stream", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} - -export default requestStreamPing; diff --git a/src/rst.js b/src/rst.js deleted file mode 100644 index ca41336f..00000000 --- a/src/rst.js +++ /dev/null @@ -1,44 +0,0 @@ -import { getUUID } from "./getUUID.js"; -import { getSignature } from "./getSignature.js"; -import { yandexProtobuf } from "./yandexProtobuf.js"; -import debug from "./utils/debug.js"; - -// Request stream translation from Yandex API -async function requestStreamTranslation( - url, - requestLang, - responseLang, - callback, -) { - try { - debug.log("requestStreamTranslation"); - // ! CURRENT CLOUDFLARE WORKER DOESN'T SUPPORT STREAM TRANSLATIONS - const yar = await import( - `./yandexRequest${BUILD_MODE === "cloudflare" ? "-cloudflare" : ""}.js` - ); - const yandexRequest = yar.default; - debug.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeStreamRequest( - url, - requestLang, - responseLang, - ); - // Send the request - await yandexRequest( - "/stream-translation/translate-stream", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} - -export default requestStreamTranslation; diff --git a/src/rvs.js b/src/rvs.js deleted file mode 100644 index d358699a..00000000 --- a/src/rvs.js +++ /dev/null @@ -1,34 +0,0 @@ -import { getUUID } from "./getUUID.js"; -import { getSignature } from "./getSignature.js"; -import { yandexProtobuf } from "./yandexProtobuf.js"; -import debug from "./utils/debug.js"; - -// Request video subtitles from Yandex API -async function requestVideoSubtitles(url, requestLang, callback) { - try { - debug.log("requestVideoSubtitles"); - const yar = await import( - `./yandexRequest${BUILD_MODE === "cloudflare" ? "-cloudflare" : ""}.js` - ); - const yandexRequest = yar.default; - debug.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeSubtitlesRequest(url, requestLang); - // Send the request - await yandexRequest( - "/video-subtitles/get-subtitles", - body, - { - "Vsubs-Signature": await getSignature(body), - "Sec-Vsubs-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} - -export default requestVideoSubtitles; diff --git a/src/rvt.js b/src/rvt.js deleted file mode 100644 index 5befe5a5..00000000 --- a/src/rvt.js +++ /dev/null @@ -1,49 +0,0 @@ -import { getUUID } from "./getUUID.js"; -import { getSignature } from "./getSignature.js"; -import { yandexProtobuf } from "./yandexProtobuf.js"; -import debug from "./utils/debug.js"; - -// Request video translation from Yandex API -async function requestVideoTranslation( - url, - duration, - requestLang, - responseLang, - translationHelp, - callback, -) { - try { - debug.log("requestVideoTranslation"); - const yar = await import( - `./yandexRequest${BUILD_MODE === "cloudflare" ? "-cloudflare" : ""}.js` - ); - const yandexRequest = yar.default; - debug.log("Inited yandexRequest..."); - // Initialize variables - const body = yandexProtobuf.encodeTranslationRequest( - url, - duration, - requestLang, - responseLang, - translationHelp, - ); - // Send the request - await yandexRequest( - // "/stream-translation/whitelist-stream", - // "/stream-translation/translate-stream", - "/video-translation/translate", - body, - { - "Vtrans-Signature": await getSignature(body), - "Sec-Vtrans-Token": getUUID(), - }, - callback, - ); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} - -export default requestVideoTranslation; diff --git a/src/styles/components/buttons/contained.scss b/src/styles/components/buttons/contained.scss index 101477b2..03dcef9b 100644 --- a/src/styles/components/buttons/contained.scss +++ b/src/styles/components/buttons/contained.scss @@ -1,8 +1,4 @@ .vot-button { - &[hidden] { - display: none !important; - } - --vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) @@ -45,6 +41,10 @@ cursor: pointer; transition: box-shadow 0.2s; + &[hidden] { + display: none !important; + } + &::-moz-focus-inner { border: none; } @@ -102,7 +102,7 @@ } } - &:disabled { + &[disabled="true"] { background-color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.12); color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38); box-shadow: none; diff --git a/src/styles/components/buttons/icon.scss b/src/styles/components/buttons/icon.scss index 4271f380..bb267979 100644 --- a/src/styles/components/buttons/icon.scss +++ b/src/styles/components/buttons/icon.scss @@ -1,8 +1,4 @@ .vot-icon-button { - &[hidden] { - display: none !important; - } - --vot-helper-onsurface: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.87); position: relative; display: inline-block; @@ -35,6 +31,10 @@ outline: none; cursor: pointer; + &[hidden] { + display: none !important; + } + &::-moz-focus-inner { border: none; } @@ -84,7 +84,7 @@ } } - &:disabled { + &[disabled="true"] { background-color: transparent; color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38); fill: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38); diff --git a/src/styles/components/buttons/outlined.scss b/src/styles/components/buttons/outlined.scss index a482556d..32f0200d 100644 --- a/src/styles/components/buttons/outlined.scss +++ b/src/styles/components/buttons/outlined.scss @@ -1,8 +1,4 @@ .vot-outlined-button { - &[hidden] { - display: none !important; - } - --vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) @@ -38,6 +34,10 @@ outline: none; cursor: pointer; + &[hidden] { + display: none !important; + } + &::-moz-focus-inner { border: none; } @@ -85,7 +85,7 @@ } } - &:disabled { + &[disabled="true"] { background-color: transparent; color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38); cursor: initial; diff --git a/src/styles/components/buttons/text.scss b/src/styles/components/buttons/text.scss index 0385260e..e3519303 100644 --- a/src/styles/components/buttons/text.scss +++ b/src/styles/components/buttons/text.scss @@ -1,8 +1,4 @@ .vot-text-button { - &[hidden] { - display: none !important; - } - --vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) @@ -37,6 +33,10 @@ outline: none; cursor: pointer; + &[hidden] { + display: none !important; + } + &::-moz-focus-inner { border: none; } @@ -84,7 +84,7 @@ } } - &:disabled { + &[disabled="true"] { background-color: transparent; color: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.38); cursor: initial; diff --git a/src/styles/components/checkbox.scss b/src/styles/components/checkbox.scss index 34b79d14..b124a20c 100644 --- a/src/styles/components/checkbox.scss +++ b/src/styles/components/checkbox.scss @@ -1,8 +1,4 @@ .vot-checkbox { - &[hidden] { - display: none !important; - } - --vot-helper-theme: var( --vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243) @@ -27,6 +23,10 @@ line-height: 1.5; text-align: start; + &[hidden] { + display: none !important; + } + & > input { appearance: none; -moz-appearance: none; diff --git a/src/styles/components/details.scss b/src/styles/components/details.scss new file mode 100644 index 00000000..063dc0dc --- /dev/null +++ b/src/styles/components/details.scss @@ -0,0 +1,40 @@ +.vot-details { + display: flex; + justify-content: space-between; + align-items: center; + color: rgba(var(--vot-helper-onsurface-rgb), 0.87); + font-family: var( + --vot-font-family, + "Roboto", + "Segoe UI", + BlinkMacSystemFont, + system-ui, + -apple-system + ); + font-size: 16px; + border-radius: 0.5em; + padding: 0.5em; + margin: 0 -0.5em; + line-height: 1.5; + text-align: start; + cursor: pointer; + transition: background 0.5s ease; + + &[hidden] { + display: none !important; + } + + &-arrow-icon { + width: 20px; + height: 32px; + display: flex; + justify-content: center; + align-items: center; + transform: scale(1.25) rotate(-90deg); + fill: rgba(var(--vot-helper-onsurface-rgb), 0.87); + } + + &:hover { + background: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.04); + } +} diff --git a/src/styles/components/dialog.scss b/src/styles/components/dialog.scss index 3e0e0f3e..982701a1 100644 --- a/src/styles/components/dialog.scss +++ b/src/styles/components/dialog.scss @@ -1,8 +1,63 @@ .vot-dialog { + --vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255); + --vot-helper-surface: rgb(var(--vot-helper-surface-rgb)); + --vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0); + --vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87); + display: block; + position: fixed; + top: 50%; + bottom: 50%; + max-width: initial; + max-height: initial; + width: calc(min(var(--vot-dialog-width, 512px), 100%)); + height: fit-content; + inset-inline-start: 0px; + inset-inline-end: 0px; + inset-block-start: 0px; + inset-block-end: 0px; + border-radius: 8px; + margin: auto; + padding: 0; + background-color: var(--vot-helper-surface); + color: var(--vot-helper-onsurface); + box-shadow: + 0 0 16px rgba(0, 0, 0, 0.12), + 0 16px 16px rgba(0, 0, 0, 0.24); + font-family: var( + --vot-font-family, + "Roboto", + "Segoe UI", + BlinkMacSystemFont, + system-ui, + -apple-system + ); + font-size: 16px; + line-height: 1.5; + user-select: none; + visibility: visible; + overflow: auto; + overflow-y: hidden; + + opacity: 1; + transform-origin: center; + transform: scale(1); + transition: + opacity 0.3s, + transform 0.1s; + + [hidden] > & { + pointer-events: none; + opacity: 0; + transform: scale(0.5); + transition: + opacity 0.1s, + transform 0.2s; + } + &-container { visibility: visible; position: absolute; - z-index: 10000; + z-index: 2147483647; &[hidden] { display: block !important; @@ -34,63 +89,6 @@ } } - & { - --vot-helper-surface-rgb: var(--vot-surface-rgb, 255, 255, 255); - --vot-helper-surface: rgb(var(--vot-helper-surface-rgb)); - --vot-helper-onsurface-rgb: var(--vot-onsurface-rgb, 0, 0, 0); - --vot-helper-onsurface: rgba(var(--vot-helper-onsurface-rgb), 0.87); - display: block; - position: fixed; - top: 50%; - bottom: 50%; - max-width: initial; - max-height: initial; - width: calc(min(var(--vot-dialog-width, 512px), 100%)); - height: fit-content; - inset-inline-start: 0px; - inset-inline-end: 0px; - inset-block-start: 0px; - inset-block-end: 0px; - border-radius: 8px; - margin: auto; - padding: 0; - background-color: var(--vot-helper-surface); - color: var(--vot-helper-onsurface); - box-shadow: - 0 0 16px rgba(0, 0, 0, 0.12), - 0 16px 16px rgba(0, 0, 0, 0.24); - font-family: var( - --vot-font-family, - "Roboto", - "Segoe UI", - BlinkMacSystemFont, - system-ui, - -apple-system - ); - font-size: 16px; - line-height: 1.5; - user-select: none; - visibility: visible; - overflow: auto; - overflow-y: hidden; - - opacity: 1; - transform-origin: center; - transform: scale(1); - transition: - opacity 0.3s, - transform 0.1s; - - [hidden] > & { - pointer-events: none; - opacity: 0; - transform: scale(0.5); - transition: - opacity 0.1s, - transform 0.2s; - } - } - &-content-wrapper { display: flex; flex-direction: column; diff --git a/src/styles/components/header.scss b/src/styles/components/header.scss index fd72106c..ded735a5 100644 --- a/src/styles/components/header.scss +++ b/src/styles/components/header.scss @@ -1,8 +1,4 @@ .vot-header { - &[hidden] { - display: none !important; - } - color: rgba(var(--vot-helper-onsurface-rgb), 0.87); font-family: var( --vot-font-family, @@ -16,6 +12,10 @@ line-height: 1.5; text-align: start; + &[hidden] { + display: none !important; + } + &:not(:first-child) { padding-top: 8px; } diff --git a/src/styles/components/info.scss b/src/styles/components/info.scss index ec555686..7b809833 100644 --- a/src/styles/components/info.scss +++ b/src/styles/components/info.scss @@ -1,8 +1,4 @@ .vot-info { - &[hidden] { - display: none !important; - } - display: flex; color: rgba(var(--vot-helper-onsurface-rgb), 0.87); font-family: var( @@ -17,6 +13,10 @@ line-height: 1.5; text-align: start; + &[hidden] { + display: none !important; + } + & > :not(:first-child) { color: rgba(var(--vot-helper-onsurface-rgb), 0.5); flex: 1; diff --git a/src/styles/components/lang-select.scss b/src/styles/components/lang-select.scss index 31ea2107..ea45d9eb 100644 --- a/src/styles/components/lang-select.scss +++ b/src/styles/components/lang-select.scss @@ -1,8 +1,4 @@ .vot-lang-select { - &[hidden] { - display: none !important; - } - --vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0); --vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87); display: flex; @@ -11,6 +7,10 @@ color: var(--vot-helper-theme); fill: var(--vot-helper-theme); + &[hidden] { + display: none !important; + } + &-icon { width: 32px; height: 32px; diff --git a/src/styles/components/menu.scss b/src/styles/components/menu.scss index f50dd105..4d66e6b1 100644 --- a/src/styles/components/menu.scss +++ b/src/styles/components/menu.scss @@ -23,7 +23,7 @@ line-height: 1.5; min-width: 300px; cursor: default; - z-index: 10000; + z-index: 2147483647; visibility: visible; opacity: 1; diff --git a/src/styles/components/segmented-button.scss b/src/styles/components/segmented-button.scss index 92ddcf83..e11bdd2d 100644 --- a/src/styles/components/segmented-button.scss +++ b/src/styles/components/segmented-button.scss @@ -1,14 +1,4 @@ .vot-segmented-button { - &[hidden] { - display: none !important; - } - - & * { - -webkit-box-sizing: border-box !important; - -moz-box-sizing: border-box !important; - box-sizing: border-box !important; - } - --vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0); --vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87); overflow: hidden; @@ -37,23 +27,28 @@ line-height: 1.5; cursor: default; transition: opacity 0.5s; - z-index: 10000; + z-index: 2147483647; - & .vot-separator { - &[hidden] { - display: none !important; - } + &[hidden] { + display: none !important; + } + & * { + -webkit-box-sizing: border-box !important; + -moz-box-sizing: border-box !important; + box-sizing: border-box !important; + } + + & .vot-separator { width: 1px; height: 50%; background: rgba(var(--vot-helper-theme-rgb), 0.1); - } - - & .vot-segment { &[hidden] { display: none !important; } + } + & .vot-segment { position: relative; overflow: hidden; display: flex; @@ -66,6 +61,10 @@ transition: background-color 100ms ease-in-out; border: none; + &[hidden] { + display: none !important; + } + &::before, &::after { content: ""; @@ -131,8 +130,8 @@ fill: #f28b82; } - &[data-translating="true"] { - #vot-translating-icon { + &[data-loading="true"] { + #vot-loading-icon { display: block !important; } diff --git a/src/styles/components/select.scss b/src/styles/components/select.scss index 76024e3f..b56d1639 100644 --- a/src/styles/components/select.scss +++ b/src/styles/components/select.scss @@ -1,8 +1,4 @@ .vot-select { - &[hidden] { - display: none !important; - } - --vot-helper-theme-rgb: var(--vot-onsurface-rgb, 0, 0, 0) !important; --vot-helper-theme: rgba(var(--vot-helper-theme-rgb), 0.87) !important; --vot-helper-safari1: rgba(var(--vot-onsurface-rgb, 0, 0, 0), 0.6) !important; @@ -29,6 +25,10 @@ color: var(--vot-helper-theme); fill: var(--vot-helper-theme); + &[hidden] { + display: none !important; + } + &-label { font-size: 16px; } diff --git a/src/styles/components/slider.scss b/src/styles/components/slider.scss index 6e4f61b5..3911e3c5 100644 --- a/src/styles/components/slider.scss +++ b/src/styles/components/slider.scss @@ -1,8 +1,4 @@ .vot-slider { - &[hidden] { - display: none !important; - } - --vot-safari-helper1: rgba( var(--vot-primary-rgb, 33, 150, 243), 0.04 @@ -33,6 +29,10 @@ font-size: 16px !important; line-height: 1.5 !important; text-align: start !important; + + &[hidden] { + display: none !important; + } } /* Input */ diff --git a/src/styles/components/textfield.scss b/src/styles/components/textfield.scss index 74e2ba9c..6999c540 100644 --- a/src/styles/components/textfield.scss +++ b/src/styles/components/textfield.scss @@ -1,8 +1,4 @@ .vot-textfield { - &[hidden] { - display: none !important; - } - --vot-helper-theme: rgb( var(--vot-theme-rgb, var(--vot-primary-rgb, 33, 150, 243)) ) !important; @@ -30,6 +26,10 @@ line-height: 1.5 !important; text-align: start !important; + &[hidden] { + display: none !important; + } + & > input, & > textarea { -webkit-box-sizing: border-box !important; @@ -59,8 +59,24 @@ border 0.2s, box-shadow 0.2s !important; - &:not(:focus):placeholder-shown { - border-top-color: var(--vot-helper-safari2) !important; + &:not(:focus) { + &:not(.vot-show-placeholer) { + &::-moz-placeholder { + color: transparent !important; + } + + &::-ms-input-placeholder { + color: transparent !important; + } + + &::-webkit-input-placeholder { + color: transparent !important; + } + } + + &:placeholder-shown { + border-top-color: var(--vot-helper-safari2) !important; + } } & + span { @@ -121,6 +137,16 @@ border-radius: 0 4px !important; } + &.vot-show-placeholer + span { + &::before { + margin-right: 0 !important; + } + + &::after { + margin-left: 0 !important; + } + } + &:not(:focus):placeholder-shown { & + span::before, & + span::after { @@ -130,8 +156,8 @@ } &:hover { - & > input, - & > textarea { + & > input:not(:disabled), + & > textarea:not(:disabled) { border-color: transparent var(--vot-helper-safari3) var(--vot-helper-safari3) !important; diff --git a/src/styles/main.scss b/src/styles/main.scss index a5aed84d..6f7e97fc 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -12,6 +12,7 @@ @import "./components/select.scss"; @import "./components/header.scss"; @import "./components/info.scss"; +@import "./components/details.scss"; @import "./components/lang-select.scss"; @import "./components/segmented-button.scss"; @@ -28,11 +29,12 @@ --vot-surface-rgb: 32, 33, 36; --vot-onsurface-rgb: 227, 227, 227; - --vot-subtitles-background: rgba(var(--vot-surface-rgb, 46, 47, 52), 0.8); --vot-subtitles-color: rgb(var(--vot-onsurface-rgb, 227, 227, 227)); --vot-subtitles-passed-color: rgb(var(--vot-primary-rgb, 33, 150, 243)); } vot-block { display: block; + // for reddit + visibility: visible !important; } diff --git a/src/styles/subtitles.scss b/src/styles/subtitles.scss index 50fa41f6..0c2c3b33 100644 --- a/src/styles/subtitles.scss +++ b/src/styles/subtitles.scss @@ -7,23 +7,27 @@ width: 50%; max-height: 100%; min-height: 20%; - z-index: 10000; + z-index: 2147483647; left: 25%; top: 75%; pointer-events: none; } & { + --vot-subtitles-background: rgba( + var(--vot-surface-rgb, 46, 47, 52), + var(--vot-subtitles-opacity, 0.8) + ); position: relative; max-width: 100%; max-height: 100%; width: max-content; background: var(--vot-subtitles-background, rgba(46, 47, 52, 0.8)); color: var(--vot-subtitles-color, rgb(227, 227, 227)); - border-radius: 1rem; + border-radius: 0.5em; pointer-events: all; - padding: 1rem; - font-size: 2rem; + padding: 0.5em; + font-size: 20px; line-height: normal; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; diff --git a/src/subtitles.js b/src/subtitles.js index 588bfedc..29d47cc5 100644 --- a/src/subtitles.js +++ b/src/subtitles.js @@ -1,8 +1,7 @@ +import { html, render, nothing } from "lit-html"; + import youtubeUtils from "./utils/youtubeUtils.js"; import { lang, GM_fetch } from "./utils/utils.js"; -import { yandexProtobuf } from "./yandexProtobuf.js"; -import requestVideoSubtitles from "./rvs.js"; -import debug from "./utils/debug.js"; function formatYandexSubtitlesTokens(line) { const lineEndMs = line.startMs + line.durationMs; @@ -112,7 +111,7 @@ function formatYoutubeSubtitles(subtitles) { if (!subtitles.events[i].segs) continue; const text = subtitles.events[i].segs .map((e) => e.utf8.replace(/^( +| +)$/g, "")) - .join(" "); + .join(""); let durationMs = subtitles.events[i].dDurationMs; if ( subtitles.events[i + 1] && @@ -133,49 +132,13 @@ function formatYoutubeSubtitles(subtitles) { return result; } -function convertToSrtTimeFormat(seconds) { - let hours = Math.floor(seconds / 3600); - let minutes = Math.floor((seconds % 3600) / 60); - let remainingSeconds = Math.floor(seconds % 60); - let milliseconds = Math.floor((seconds % 1) * 1000); - - return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")},${milliseconds.toString().padStart(3, "0")}`; -} - -export function jsonToSrt(jsonData) { - let srtContent = ""; - let index = 1; - for (const entry of jsonData.subtitles) { - let startTime = entry.startMs / 1000.0; - let endTime = (entry.startMs + entry.durationMs) / 1000.0; - - srtContent += `${index}\n`; - srtContent += `${convertToSrtTimeFormat(startTime)} --> ${convertToSrtTimeFormat(endTime)}\n`; - srtContent += `${entry.text}\n\n`; - index++; - } - - return srtContent.trim(); -} - export async function fetchSubtitles(subtitlesObject) { - const timeoutPromise = new Promise((resolve) => - setTimeout( - () => - resolve({ - containsTokens: false, - subtitles: [], - }), - 5000, - ), - ); - const fetchPromise = (async () => { try { - const response = await GM_fetch(subtitlesObject.url); + const response = await GM_fetch(subtitlesObject.url, { timeout: 5000 }); return await response.json(); } catch (error) { - console.error("[VOT] Failed to fetch subtitles. Reason:", error); + console.error("[VOT] Failed to fetch subtitles.", error); return { containsTokens: false, subtitles: [], @@ -183,7 +146,7 @@ export async function fetchSubtitles(subtitlesObject) { } })(); - let subtitles = await Promise.race([timeoutPromise, fetchPromise]); + let subtitles = await fetchPromise; if (subtitlesObject.source === "youtube") { subtitles = formatYoutubeSubtitles(subtitles); @@ -194,211 +157,205 @@ export async function fetchSubtitles(subtitlesObject) { return subtitles; } -export async function getSubtitles(site, videoId, requestLang) { - const ytSubtitles = - site.host === "youtube" ? youtubeUtils.getSubtitles() : []; - let resolved = false; - const yaSubtitles = await Promise.race([ - new Promise((resolve) => { - setTimeout(() => { - if (!resolved) { - console.error("[VOT] Failed get yandex subtitles. Reason: timeout"); - resolve([]); - } - }, 5000); - }), - new Promise((resolve) => { - requestVideoSubtitles( - `${site.url}${videoId}`, +export async function getSubtitles(client, videoData) { + const { host, url, requestLang, videoId, duration } = videoData; + const ytSubtitles = host === "youtube" ? youtubeUtils.getSubtitles() : []; + + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error("Timeout")), 5000), + ); + + try { + const res = await Promise.race([ + client.getSubtitles({ + videoData: { host, url, videoId, duration }, requestLang, - (success, response) => { - debug.log("[exec callback] Requesting video subtitles", videoId); + }), + timeoutPromise, + ]); - if (!success) { - console.error("[VOT] Failed get yandex subtitles"); - resolved = true; - resolve([]); - } + console.log("[VOT] Subtitles response: ", res); - const subtitlesResponse = - yandexProtobuf.decodeSubtitlesResponse(response); - console.log("[VOT] Subtitles response: ", subtitlesResponse); - - let subtitles = subtitlesResponse.subtitles ?? []; - subtitles = subtitles.reduce((result, yaSubtitlesObject) => { - if ( - yaSubtitlesObject.language && - !result.find((e) => { - if ( - e.source === "yandex" && - e.language === yaSubtitlesObject.language && - !e.translatedFromLanguage - ) { - return e; - } - }) - ) { - result.push({ - source: "yandex", - language: yaSubtitlesObject.language, - url: yaSubtitlesObject.url, - }); - } - if (yaSubtitlesObject.translatedLanguage) { - result.push({ - source: "yandex", - language: yaSubtitlesObject.translatedLanguage, - translatedFromLanguage: yaSubtitlesObject.language, - url: yaSubtitlesObject.translatedUrl, - }); - } - return result; - }, []); - resolved = true; - resolve(subtitles); - }, - ); - }), - ]); - const subtitles = [...yaSubtitles, ...ytSubtitles].sort((a, b) => { - if (a.source !== b.source) { - // sort by source - return a.source === "yandex" ? -1 : 1; + if (res.waiting) { + console.error("[VOT] Failed to get yandex subtitles"); } - if ( - a.language !== b.language && - (a.language === lang || b.language === lang) - ) { - // sort by user language - return a.language === lang ? -1 : 1; - } - if (a.source === "yandex") { - // sort by translation - if (a.translatedFromLanguage !== b.translatedFromLanguage) { - // sort by translatedFromLanguage - if (!a.translatedFromLanguage || !b.translatedFromLanguage) { - // sort by isTranslated - if (a.language === b.language) { - return a.translatedFromLanguage ? 1 : -1; + + // Обработка субтитров + let subtitles = res.subtitles ?? []; + subtitles = subtitles.reduce((result, yaSubtitlesObject) => { + if ( + yaSubtitlesObject.language && + !result.find( + (e) => + e.source === "yandex" && + e.language === yaSubtitlesObject.language && + !e.translatedFromLanguage, + ) + ) { + result.push({ + source: "yandex", + language: yaSubtitlesObject.language, + url: yaSubtitlesObject.url, + }); + } + if (yaSubtitlesObject.translatedLanguage) { + result.push({ + source: "yandex", + language: yaSubtitlesObject.translatedLanguage, + translatedFromLanguage: yaSubtitlesObject.language, + url: yaSubtitlesObject.translatedUrl, + }); + } + return result; + }, []); + + return [...subtitles, ...ytSubtitles].sort((a, b) => { + if (a.source !== b.source) return a.source === "yandex" ? -1 : 1; + if ( + a.language !== b.language && + (a.language === lang || b.language === lang) + ) + return a.language === lang ? -1 : 1; + if (a.source === "yandex") { + // sort by translation + if (a.translatedFromLanguage !== b.translatedFromLanguage) { + // sort by translatedFromLanguage + if (!a.translatedFromLanguage || !b.translatedFromLanguage) { + // sort by isTranslated + if (a.language === b.language) + return a.translatedFromLanguage ? 1 : -1; + return !a.translatedFromLanguage ? 1 : -1; } - return !a.translatedFromLanguage ? 1 : -1; + return a.translatedFromLanguage === requestLang ? -1 : 1; } - return a.translatedFromLanguage === requestLang ? -1 : 1; - } - if (!a.translatedFromLanguage) { // sort non translated by language - return a.language === requestLang ? -1 : 1; + if (!a.translatedFromLanguage) + return a.language === requestLang ? -1 : 1; } - } - if (a.source === "youtube" && a.isAutoGenerated !== b.isAutoGenerated) { // sort by isAutoGenerated - return a.isAutoGenerated ? 1 : -1; + if (a.source === "youtube" && a.isAutoGenerated !== b.isAutoGenerated) + return a.isAutoGenerated ? 1 : -1; + return 0; + }); + } catch (error) { + if (error.message === "Timeout") { + console.error("[VOT] Failed to get yandex subtitles. Reason: timeout"); + } else { + console.error("[VOT] Error in getSubtitles function", error); } - return 0; - }); - console.log("[VOT] subtitles list", subtitles); - return subtitles; + // на сайтах, где нет сабов всегда красит кнопку + throw error; + } } export class SubtitlesWidget { - dragging = false; - subtitlesContainerRect = null; - containerRect = null; - offsetX = null; - offsetY = null; - - lastContent = null; - highlightWords = false; - subtitles = null; - maxLength = 300; - maxLengthRegexp = /.{1,300}(?:\s|$)/g; - constructor(video, container, site) { - this.site = site; this.video = video; - if (this.site.host === "youtube" && this.site.additionalData !== "mobile") { - this.container = container.parentElement; - } else { - this.container = container; - } + this.container = + site.host === "youtube" && site.additionalData !== "mobile" + ? container.parentElement + : container; + this.site = site; + + this.subtitlesContainer = this.createSubtitlesContainer(); + this.position = { left: 25, top: 75 }; + this.dragging = { active: false, offset: { x: 0, y: 0 } }; + + this.subtitles = null; + this.lastContent = null; + this.highlightWords = false; + this.fontSize = 20; + this.opacity = 0.2; + this.maxLength = 300; + this.maxLengthRegexp = /.{1,300}(?:\s|$)/g; + + this.bindEvents(); + this.updateContainerRect(); + this.applySubtitlePosition(); + } - this.votSubtitlesContainer = document.createElement("vot-block"); - this.votSubtitlesContainer.classList.add("vot-subtitles-widget"); - this.container.appendChild(this.votSubtitlesContainer); + createSubtitlesContainer() { + const container = document.createElement("vot-block"); + container.classList.add("vot-subtitles-widget"); + this.container.appendChild(container); + return container; + } + bindEvents() { this.onMouseDownBound = this.onMouseDown.bind(this); this.onMouseUpBound = this.onMouseUp.bind(this); this.onMouseMoveBound = this.onMouseMove.bind(this); - this.onTimeUpdateBound = this.onTimeUpdate.bind(this); + this.onTimeUpdateBound = this.debounce(this.update.bind(this), 100); document.addEventListener("mousedown", this.onMouseDownBound); document.addEventListener("mouseup", this.onMouseUpBound); document.addEventListener("mousemove", this.onMouseMoveBound); - this.video?.addEventListener("timeupdate", this.onTimeUpdateBound); - } - release() { - this.video?.removeEventListener("timeupdate", this.onTimeUpdateBound); - - document.removeEventListener("mousedown", this.onMouseDownBound); - document.removeEventListener("mouseup", this.onMouseUpBound); - document.removeEventListener("mousemove", this.onMouseMoveBound); - - this.votSubtitlesContainer.remove(); + this.resizeObserver = new ResizeObserver(this.onResize.bind(this)); + this.resizeObserver.observe(this.container); } onMouseDown(e) { - if (this.votSubtitlesContainer.contains(e.target)) { - this.subtitlesContainerRect = - this.votSubtitlesContainer.getBoundingClientRect(); - this.containerRect = this.container.getBoundingClientRect(); - this.offsetX = e.clientX - this.subtitlesContainerRect.x; - this.offsetY = e.clientY - this.subtitlesContainerRect.y; - this.dragging = true; + if (this.subtitlesContainer.contains(e.target)) { + const rect = this.subtitlesContainer.getBoundingClientRect(); + const containerRect = this.container.getBoundingClientRect(); + this.dragging = { + active: true, + offset: { + x: e.clientX - rect.left, + y: e.clientY - rect.top, + }, + containerOffset: { + x: containerRect.left, + y: containerRect.top, + }, + }; } } onMouseUp() { - this.dragging = false; + this.dragging.active = false; } onMouseMove(e) { - if (this.dragging) { + if (this.dragging.active) { e.preventDefault(); - const x = e.clientX - this.offsetX; - const y = e.clientY - this.offsetY; - const top = y >= this.containerRect.top; - const bottom = - y + this.subtitlesContainerRect.height <= this.containerRect.bottom; - const left = x >= this.containerRect.left; - const right = - x + this.subtitlesContainerRect.width <= this.containerRect.right; - - if (top && bottom) { - this.votSubtitlesContainer.style.top = `${y - this.containerRect.y}px`; - } else if (!top) { - this.votSubtitlesContainer.style.top = `${0}px`; - } else { - this.votSubtitlesContainer.style.top = `${ - this.containerRect.height - this.subtitlesContainerRect.height - }px`; - } - - if (left && right) { - this.votSubtitlesContainer.style.left = `${x - this.containerRect.x}px`; - } else if (!left) { - this.votSubtitlesContainer.style.left = `${0}px`; - } else { - this.votSubtitlesContainer.style.left = `${ - this.containerRect.width - this.subtitlesContainerRect.width - }px`; - } + const { width, height } = this.container.getBoundingClientRect(); + const containerOffset = this.dragging.containerOffset; + this.position = { + left: + ((e.clientX - this.dragging.offset.x - containerOffset.x) / width) * + 100, + top: + ((e.clientY - this.dragging.offset.y - containerOffset.y) / height) * + 100, + }; + this.applySubtitlePosition(); } } - onTimeUpdate() { - this.update(); + onResize() { + this.updateContainerRect(); + } + + updateContainerRect() { + this.containerRect = this.container.getBoundingClientRect(); + this.applySubtitlePosition(); + } + + applySubtitlePosition() { + const { width, height } = this.containerRect; + const { offsetWidth, offsetHeight } = this.subtitlesContainer; + + const maxLeft = ((width - offsetWidth) / width) * 100; + const maxTop = ((height - offsetHeight) / height) * 100; + + this.position.left = Math.max(0, Math.min(this.position.left, maxLeft)); + this.position.top = Math.max(0, Math.min(this.position.top, maxTop)); + + this.subtitlesContainer.style.left = `${this.position.left}%`; + this.subtitlesContainer.style.top = `${this.position.top}%`; } setContent(subtitles) { @@ -407,7 +364,7 @@ export class SubtitlesWidget { this.update(); } else { this.subtitles = null; - this.votSubtitlesContainer.innerHTML = ""; + render(null, this.subtitlesContainer); } } @@ -420,75 +377,126 @@ export class SubtitlesWidget { } setHighlightWords(value) { - if (this.highlightWords !== !!value) { - this.highlightWords = !!value; - this.update(); + this.highlightWords = Boolean(value); + this.update(); + } + + setFontSize(size) { + this.fontSize = size; + const subtitlesEl = + this.subtitlesContainer?.querySelector(".vot-subtitles"); + if (subtitlesEl) { + subtitlesEl.style.fontSize = `${this.fontSize}px`; + } + } + + /** + * Set subtitles opacity by percentage where 100 - full transparent, 0 - not transparent + * + * @param {number} rate - 0-100 percent of opacity + */ + setOpacity(rate) { + this.opacity = ((100 - +rate) / 100).toFixed(2); + const subtitlesEl = + this.subtitlesContainer?.querySelector(".vot-subtitles"); + if (subtitlesEl) { + subtitlesEl.style.setProperty("--vot-subtitles-opacity", this.opacity); } } update() { - if (!this.video) return; + if (!this.video || !this.subtitles) return; - let content = ""; - let highlightWords = this.highlightWords && this.subtitles?.containsTokens; const time = this.video.currentTime * 1000; - const line = this.subtitles?.subtitles?.findLast((e) => { - return e.startMs < time && time < e.startMs + e.durationMs; - }); - if (line) { - let { tokens } = line; - if (tokens.at(-1).alignRange.end > this.maxLength) { - let chunks = []; - let chunkStartIndex = 0; - let chunkEndIndex = 0; - let length = 0; - for (let i = 0; i < tokens.length + 1; i++) { - length += tokens[i]?.text?.length ?? 0; - if (!tokens[i] || length > this.maxLength) { - let t = tokens.slice(chunkStartIndex, chunkEndIndex + 1); - if (t.at(0) && t.at(0).text === " ") t = t.slice(1); - if (t.at(-1) && t.at(-1).text === " ") t = t.slice(0, t.length - 1); - chunks.push({ - startMs: tokens[chunkStartIndex].startMs, - durationMs: - tokens[chunkEndIndex].startMs + - tokens[chunkEndIndex].durationMs - - tokens[chunkStartIndex].startMs, - tokens: t, - }); - chunkStartIndex = i; - length = 0; - } - chunkEndIndex = i; - } - for (let index = 0; index < chunks.length; index++) { - const chunk = chunks[index]; - if (chunk.startMs < time && time < chunk.startMs + chunk.durationMs) { - tokens = chunk.tokens; - break; - } - } - } - for (let i = 0; i < tokens.length; i++) { - const token = tokens[i]; - const passedMs = token.startMs + token.durationMs / 2; - content += ` passedMs || - (time > token.startMs - 100 && passedMs - time < 275)) - ? 'class="passed"' - : "" - }>${token.text}`; - } + const line = this.subtitles.subtitles?.findLast( + (e) => e.startMs < time && time < e.startMs + e.durationMs, + ); + + if (!line) { + render(null, this.subtitlesContainer); + return; } - if (content !== this.lastContent) { - this.lastContent = content; - this.votSubtitlesContainer.innerHTML = content - ? `${content.replace( - "\\n", - "
", - )}
` - : ""; + + let tokens = this.processTokens(line.tokens); + const content = this.renderTokens(tokens, time); + const stringContent = JSON.stringify(content); + if (stringContent !== this.lastContent) { + this.lastContent = stringContent; + render( + html`${content}`, + this.subtitlesContainer, + ); } } + + processTokens(tokens) { + if (tokens.at(-1).alignRange.end <= this.maxLength) return tokens; + + let chunks = []; + let chunkTokens = []; + let length = 0; + + for (const token of tokens) { + length += token.text.length; + chunkTokens.push(token); + + if (length > this.maxLength) { + chunks.push(this.trimChunk(chunkTokens)); + chunkTokens = []; + length = 0; + } + } + + if (chunkTokens.length) chunks.push(this.trimChunk(chunkTokens)); + + const time = this.video.currentTime * 1000; + return ( + chunks.find( + (chunk) => + chunk[0].startMs < time && + time < chunk.at(-1).startMs + chunk.at(-1).durationMs, + ) || chunks[0] + ); + } + + trimChunk(tokens) { + if (tokens[0].text === " ") tokens.shift(); + if (tokens.at(-1).text === " ") tokens.pop(); + return tokens; + } + + renderTokens(tokens, time) { + return tokens.map((token) => { + const passed = + this.highlightWords && + (time > token.startMs + token.durationMs / 2 || + (time > token.startMs - 100 && + token.startMs + token.durationMs / 2 - time < 275)); + return html`${token.text.replace("\\n", "
")}
`; + }); + } + + debounce(func, wait) { + let timeout; + return (...args) => { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; + } + + release() { + document.removeEventListener("mousedown", this.onMouseDownBound); + document.removeEventListener("mouseup", this.onMouseUpBound); + document.removeEventListener("mousemove", this.onMouseMoveBound); + this.video?.removeEventListener("timeupdate", this.onTimeUpdateBound); + this.resizeObserver.disconnect(); + this.subtitlesContainer.remove(); + } } diff --git a/src/ui.js b/src/ui.js index 4ff1486f..ebb79be2 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,26 +1,59 @@ +import { svg, render } from "lit-html"; + +import "./styles/main.scss"; + import { localizationProvider } from "./localization/localizationProvider.js"; +const undefinedPhrase = "#UNDEFINED"; +const arrowIconRaw = svg` + +`; + +/** + * Create header element + * + * @param {HTMLElement|string} html - header content + * @param {1|2|3|4|5|6} level - header level + * @return {HTMLElement} HTML header element + */ export function createHeader(html, level = 4) { const header = document.createElement("vot-block"); header.classList.add("vot-header"); header.classList.add(`vot-header-level-${level}`); - header.innerHTML = html; + header.append(html); return header; } +/** + * Create information element + * + * @param {HTMLElement|string} html - label content + * @param {HTMLElement|string} valueHtml - value content + * @return {{ + * container: HTMLElement, + * header: HTMLElement, + * value: HTMLElement + * }} information elements + */ export function createInformation(html, valueHtml) { const container = document.createElement("vot-block"); container.classList.add("vot-info"); const header = document.createElement("vot-block"); - header.innerHTML = html; + header.append(html); const value = document.createElement("vot-block"); - value.innerHTML = valueHtml; + value.append(valueHtml); - container.appendChild(header); - container.appendChild(value); + container.append(header, value); return { container, @@ -29,38 +62,73 @@ export function createInformation(html, valueHtml) { }; } +/** + * Create button + * + * @param {HTMLElement|string} html - button content + * @return {HTMLElement} HTML button element + */ export function createButton(html) { const button = document.createElement("vot-block"); button.classList.add("vot-button"); - button.innerHTML = html; + button.append(html); return button; } +/** + * Create text button + * + * @param {HTMLElement|string} html - button content + * @return {HTMLElement} HTML text button element + */ export function createTextButton(html) { const button = document.createElement("vot-block"); button.classList.add("vot-text-button"); - button.innerHTML = html; + button.append(html); return button; } +/** + * Create outlined button + * + * @param {HTMLElement|string} html - button content + * @return {HTMLElement} HTML outlined button element + */ export function createOutlinedButton(html) { const button = document.createElement("vot-block"); button.classList.add("vot-outlined-button"); - button.innerHTML = html; + button.append(html); return button; } -export function createIconButton(html) { +/** + * Create icon button + * + * @param {TemplateResult} templateHtml - icon svg lit template + * @return {HTMLElement} HTML icon button element + */ +export function createIconButton(templateHtml) { const button = document.createElement("vot-block"); button.classList.add("vot-icon-button"); - button.innerHTML = html; + render(templateHtml, button); return button; } +/** + * Create checkbox + * + * @param {string|HTMLElement} html - label content + * @param {boolean} value - checkbox state + * @return {{ + * container: HTMLElement, + * input: HTMLInputElement, + * label: HTMLSpanElement + * }} checkbox elements + */ export function createCheckbox(html, value = false) { const container = document.createElement("label"); container.classList.add("vot-checkbox"); @@ -70,23 +138,40 @@ export function createCheckbox(html, value = false) { input.checked = Boolean(value); const label = document.createElement("span"); - label.innerHTML = html; + label.append(html); - container.appendChild(input); - container.appendChild(label); + container.append(input, label); return { container, input, label }; } +/** + * Update slider value + * + * @param {HTMLInputElement} input - slider input element + */ export function updateSlider(input) { - const value = parseFloat(input.value); - const min = input.min === "" ? 0 : parseFloat(input.min); - const max = input.max === "" ? 100 : parseFloat(input.max); + const value = +input.value; + const min = +input.min; + const max = +input.max; const progress = (value - min) / (max - min); input.parentElement.setAttribute("style", `--vot-progress: ${progress}`); } -export function createSlider(html, value = 50, min = 0, max = 100) { +/** + * Create slider + * + * @param {string|HTMLElement} html - label content + * @param {number} value - default value + * @param {number} min - min value + * @param {number} max - max value + * @return {{ + * container: HTMLElement, + * input: HTMLInputElement, + * label: HTMLSpanElement + * }} slider elements + */ +export function createSlider(labelHtml, value = 50, min = 0, max = 100) { const container = document.createElement("vot-block"); container.classList.add("vot-slider"); @@ -97,10 +182,9 @@ export function createSlider(html, value = 50, min = 0, max = 100) { input.value = value; const label = document.createElement("span"); - label.innerHTML = html; + render(labelHtml, label); - container.appendChild(input); - container.appendChild(label); + container.append(input, label); input.addEventListener("input", (e) => updateSlider(e.target)); updateSlider(input); @@ -112,6 +196,19 @@ export function createSlider(html, value = 50, min = 0, max = 100) { }; } +/** + * Create textfield + * + * @param {string|HTMLElement} html - label content + * @param {string} value - default value + * @param {string} placeholder - textfield placeholder + * @param {boolean} multiline - multiline textfield + * @return {{ + * container: HTMLElement, + * input: HTMLInputElement, + * label: HTMLSpanElement + * }} textfield elements + */ export function createTextfield( html, value = "", @@ -124,12 +221,14 @@ export function createTextfield( const input = document.createElement(multiline ? "textarea" : "input"); input.placeholder = placeholder; input.value = value; + if (!html) { + input.classList.add("vot-show-placeholer"); + } const label = document.createElement("span"); - label.innerHTML = html; + label.append(html); - container.appendChild(input); - container.appendChild(label); + container.append(input, label); return { container, @@ -138,6 +237,23 @@ export function createTextfield( }; } +/** + * Create dialog + * + * @param {string|HTMLElement} html - title content + * @return {{ + * container: HTMLElement, + * backdrop: HTMLElement, + * dialog: HTMLElement, + * contentWrapper: HTMLElement, + * headerContainer: HTMLElement, + * bodyContainer: HTMLElement, + * footerContainer: HTMLElement, + * titleContainer: HTMLElement, + * closeButton: HTMLElement, + * title: HTMLElement, + * }} dialog elements + */ export function createDialog(html) { const container = document.createElement("vot-block"); container.classList.add("vot-dialog-container"); @@ -165,7 +281,16 @@ export function createDialog(html) { titleContainer.classList.add("vot-dialog-title-container"); const closeButton = createIconButton( - ``, + svg` + + `, ); closeButton.classList.add("vot-dialog-close-button"); @@ -175,17 +300,13 @@ export function createDialog(html) { const title = document.createElement("vot-block"); title.classList.add("vot-dialog-title"); - title.innerHTML = html; - - container.appendChild(backdrop); - container.appendChild(dialog); - dialog.appendChild(contentWrapper); - contentWrapper.appendChild(headerContainer); - contentWrapper.appendChild(bodyContainer); - contentWrapper.appendChild(footerContainer); - headerContainer.appendChild(titleContainer); - headerContainer.appendChild(closeButton); - titleContainer.appendChild(title); + title.append(html); + + container.append(backdrop, dialog); + dialog.append(contentWrapper); + contentWrapper.append(headerContainer, bodyContainer, footerContainer); + headerContainer.append(titleContainer, closeButton); + titleContainer.append(title); return { container, @@ -201,39 +322,101 @@ export function createDialog(html) { }; } -export function createVOTButton(html) { +/** + * Create VOTButton + * + * @param {string|HTMLElement} label - label content + * @return {{ + * container: HTMLElement, + * translateButton: HTMLElement, + * separator: HTMLElement, + * pipButton: HTMLElement, + * separator2: HTMLElement, + * menuButton: HTMLElement, + * label: HTMLSpanElement, + * }} VOTButton elements + */ +export function createVOTButton(labelHtml) { const container = document.createElement("vot-block"); container.classList.add("vot-segmented-button"); const translateButton = document.createElement("vot-block"); translateButton.classList.add("vot-segment"); translateButton.classList.add("vot-translate-button"); - translateButton.innerHTML = ``; + render( + svg` + + + + `, + translateButton, + ); const separator = document.createElement("vot-block"); separator.classList.add("vot-separator"); const pipButton = document.createElement("vot-block"); pipButton.classList.add("vot-segment-only-icon"); - pipButton.innerHTML = ``; + render( + svg` + + `, + pipButton, + ); const separator2 = document.createElement("vot-block"); separator2.classList.add("vot-separator"); const menuButton = document.createElement("vot-block"); menuButton.classList.add("vot-segment-only-icon"); - menuButton.innerHTML = ``; + render( + svg` + + `, + menuButton, + ); const label = document.createElement("span"); label.classList.add("vot-segment-label"); - label.innerHTML = html; + label.append(labelHtml); - container.appendChild(translateButton); - container.appendChild(separator); - container.appendChild(pipButton); - container.appendChild(separator2); - container.appendChild(menuButton); - translateButton.appendChild(label); + container.append( + translateButton, + separator, + pipButton, + separator2, + menuButton, + ); + translateButton.append(label); return { container, @@ -246,6 +429,20 @@ export function createVOTButton(html) { }; } +/** + * Create VOTMenu + * + * @param {string|HTMLElement} html - title content + * @return {{ + * container: HTMLElement, + * contentWrapper: HTMLElement, + * headerContainer: HTMLElement, + * bodyContainer: HTMLElement, + * footerContainer: HTMLElement, + * titleContainer: HTMLElement, + * title: HTMLSpanElement, + * }} VOTMenu elements + */ export function createVOTMenu(html) { const container = document.createElement("vot-block"); container.classList.add("vot-menu"); @@ -268,14 +465,12 @@ export function createVOTMenu(html) { const title = document.createElement("vot-block"); title.classList.add("vot-menu-title"); - title.innerHTML = html; + title.append(html); - container.appendChild(contentWrapper); - contentWrapper.appendChild(headerContainer); - contentWrapper.appendChild(bodyContainer); - contentWrapper.appendChild(footerContainer); - headerContainer.appendChild(titleContainer); - titleContainer.appendChild(title); + container.append(contentWrapper); + contentWrapper.append(headerContainer, bodyContainer, footerContainer); + headerContainer.append(titleContainer); + titleContainer.append(title); return { container, @@ -288,23 +483,45 @@ export function createVOTMenu(html) { }; } +/** + * Create VOTSelectLabel + * + * @param {string} text - label text + * @return {HTMLSpanElement} VOTSelectLabel element + */ export function createVOTSelectLabel(text) { const label = document.createElement("span"); label.classList.add("vot-select-label"); - label.innerText = text; + label.textContent = text; return label; } +/** + * Create VOTSelect + * + * @param {string} selectTitle - select title + * @param {string} dialogTitle - dialog title + * @param {{label: string, value: string, selected: boolean}[]} items - items to select + * @param {{onSelectCb: function, labelElement: string}} options - items to select + * @return {{ + * container: HTMLElement, + * title: HTMLSpanElement, + * arrowIcon: HTMLElement, + * labelElement: HTMLElement, + * setTitle: (newTitle: string) => void, + * setSelected: (val: string) => void, + * updateItems: (newItems: {label: string, value: string, selected: boolean}[]) => void, + * }} VOTSelect elements + */ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { - const onSelectCb = options.onSelectCb || function () {}; - const labelElement = options.labelElement || ""; + const { onSelectCb = function () {}, labelElement = "" } = options; let selectedItems = []; const container = document.createElement("vot-block"); container.classList.add("vot-select"); if (labelElement) { - container.appendChild(labelElement); + container.append(labelElement); } const outer = document.createElement("vot-block"); @@ -312,15 +529,15 @@ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { const title = document.createElement("span"); title.classList.add("vot-select-title"); - title.innerText = selectTitle; + title.textContent = selectTitle; if (selectTitle === undefined) { - title.innerText = items.find((i) => i.selected === true)?.label; + title.textContent = items.find((i) => i.selected === true)?.label; } const arrowIcon = document.createElement("vot-block"); arrowIcon.classList.add("vot-select-arrow-icon"); - arrowIcon.innerHTML = ``; + render(arrowIconRaw, arrowIcon); outer.append(title, arrowIcon); outer.onclick = () => { @@ -335,7 +552,7 @@ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { for (const item of items) { const contentItem = document.createElement("vot-block"); contentItem.classList.add("vot-select-content-item"); - contentItem.innerText = item.label; + contentItem.textContent = item.label; contentItem.dataset.votSelected = item.selected; contentItem.dataset.votValue = item.value; if (item.disabled) { @@ -356,7 +573,7 @@ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { } contentItem.dataset.votSelected = true; - title.innerText = item.label; + title.textContent = item.label; // !!! use e.target.dataset.votValue instead of e.target.value !!! await onSelectCb(e); @@ -374,7 +591,7 @@ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { // check if there are lovercase characters in the string. used for smarter search for (let i = 0; i < selectedItems.length; i++) { const ci = selectedItems[i]; - ci.hidden = !ci.innerText.toLowerCase().includes(searchText); + ci.hidden = !ci.textContent.toLowerCase().includes(searchText); } }; @@ -395,7 +612,7 @@ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { container.append(outer); const setTitle = (newTitle) => { - title.innerText = newTitle; + title.textContent = newTitle; }; const setSelected = (val) => { @@ -429,14 +646,16 @@ export function createVOTSelect(selectTitle, dialogTitle, items, options = {}) { } export function createVOTLanguageSelect(options) { - const fromTitle = options.fromTitle || "#UNDEFINED"; - const fromDialogTitle = options.fromDialogTitle || "#UNDEFINED"; - const fromItems = options.fromItems || []; - const fromOnSelectCB = options.fromOnSelectCB || function () {}; - const toTitle = options.toTitle || "#UNDEFINED"; - const toDialogTitle = options.toDialogTitle || "#UNDEFINED"; - const toItems = options.toItems || []; - const toOnSelectCB = options.toOnSelectCB || function () {}; + const { + fromTitle = undefinedPhrase, + fromDialogTitle = undefinedPhrase, + fromItems = [], + fromOnSelectCB = null, + toTitle = undefinedPhrase, + toDialogTitle = undefinedPhrase, + toItems = [], + toOnSelectCB = null, + } = options; const container = document.createElement("vot-block"); container.classList.add("vot-lang-select"); @@ -447,7 +666,19 @@ export function createVOTLanguageSelect(options) { const icon = document.createElement("vot-block"); icon.classList.add("vot-lang-select-icon"); - icon.innerHTML = ``; + render( + svg` + + `, + icon, + ); const toSelect = createVOTSelect(toTitle, toDialogTitle, toItems, { onSelectCb: toOnSelectCB, @@ -463,6 +694,26 @@ export function createVOTLanguageSelect(options) { }; } +export function createDetails(titleHtml) { + const container = document.createElement("vot-block"); + container.classList.add("vot-details"); + + const header = document.createElement("vot-block"); + header.append(titleHtml); + + const arrowIcon = document.createElement("vot-block"); + arrowIcon.classList.add("vot-details-arrow-icon"); + render(arrowIconRaw, arrowIcon); + + container.append(header, arrowIcon); + + return { + container, + header, + arrowIcon, + }; +} + export default { createHeader, createInformation, @@ -480,4 +731,5 @@ export default { createVOTSelect, createVOTLanguageSelect, updateSlider, + createDetails, }; diff --git a/src/utils/EventImpl.js b/src/utils/EventImpl.js index 02b786d0..120b3c21 100644 --- a/src/utils/EventImpl.js +++ b/src/utils/EventImpl.js @@ -2,9 +2,11 @@ export class EventImpl { constructor() { this.listeners = new Set(); } + hasListener(e) { return this.listeners.has(e); } + dispatchToListener(handler, ...args) { try { handler(...args); @@ -12,18 +14,21 @@ export class EventImpl { console.error("[VOT]", exception); } } + addListener(handler) { if (this.hasListener(handler)) { throw new Error("[VOT] The listener has already been added."); } this.listeners.add(handler); } + removeListener(handler) { if (!this.hasListener(handler)) { throw new Error("[VOT] The listener has not been added yet."); } this.listeners.delete(handler); } + dispatch(...args) { for (const handler of Array.from(this.listeners)) { this.dispatchToListener(handler, ...args); diff --git a/src/utils/VideoObserver.js b/src/utils/VideoObserver.js index ee4c930a..bc412cb4 100644 --- a/src/utils/VideoObserver.js +++ b/src/utils/VideoObserver.js @@ -96,7 +96,7 @@ export class VideoObserver { childList: true, subtree: true, }); - const videos = document.querySelectorAll("video"); + const videos = this.getAllVideoEls(); for (let i = 0; i < videos.length; i++) { this.checkAndHandleVideo(videos[i]); } @@ -107,6 +107,42 @@ export class VideoObserver { this.intersectionObserver.disconnect(); } + getAllVideoEls() { + const videos = document.querySelectorAll("video"); + if (videos.length) { + return videos; + } + + // Use it only if we don't find videos + // It takes a long time to complete + const els = document.querySelectorAll("*"); + const videoElements = new Set(); + function traverseShadowRoot(root) { + if (!root) return; + root.querySelectorAll("video").forEach((video) => { + if (videoElements.has(video)) { + return; + } + videoElements.add(video); + }); + + root.querySelectorAll("*").forEach((element) => { + if (element.shadowRoot) { + traverseShadowRoot(element.shadowRoot); + } + }); + } + + for (let i = 0; i < els.length; i++) { + const el = els[i]; + if (el.shadowRoot) { + traverseShadowRoot(el); + } + } + + return Array.from(videoElements); + } + checkAndHandleVideo(video) { if (this.videoCache.has(video)) { return; diff --git a/src/utils/bannedvideoUtils.js b/src/utils/bannedvideoUtils.js deleted file mode 100644 index 6ec9a40e..00000000 --- a/src/utils/bannedvideoUtils.js +++ /dev/null @@ -1,64 +0,0 @@ -import debug from "./debug.js"; - -async function getVideoInfo(videoId) { - return await fetch(`https://api.banned.video/graphql`, { - method: "POST", - body: JSON.stringify({ - operationName: "GetVideo", - query: `query GetVideo($id: String!) { - getVideo(id: $id) { - ...DisplayVideoFields - videoUrl: directUrl - live - } - } - - fragment DisplayVideoFields on Video { - title - description: summary - duration: videoDuration - }`, - variables: { - id: videoId, - }, - }), - headers: { - "User-Agent": "bannedVideoFrontEnd", - "apollographql-client-name": "banned-web", - "apollographql-client-version": "1.3", - "content-type": "application/json", - }, - }) - .then((res) => res.json()) - .catch((err) => { - console.error(err); - return { - data: { - getVideo: {}, - }, - }; - }); -} - -async function getVideoData(videoId) { - const videoData = await getVideoInfo(videoId); - - debug.log("banned.video video data:", videoData); - - const { videoUrl, duration, live, description, title } = - videoData.data.getVideo; - - // TODO: Add detect language from title + description - - return { - url: videoUrl, - duration, - live, - title, - description, - }; -} - -export default { - getVideoData, -}; diff --git a/src/utils/coursehunterUtils.js b/src/utils/coursehunterUtils.js deleted file mode 100644 index d7e62b40..00000000 --- a/src/utils/coursehunterUtils.js +++ /dev/null @@ -1,34 +0,0 @@ -import debug from "./debug.js"; - -async function getCourseData(courseId) { - const response = await fetch( - `https://coursehunter.net/api/v1/course/${courseId}/lessons`, - ); - return await response.json(); -} - -async function getVideoData() { - const courseId = - window.course_id ?? - document.querySelector('input[name="course_id"]')?.value; - - const courseData = window.lessons ?? (await getCourseData(courseId)); - - const lessonId = parseInt( - document.querySelector(".lessons-item_active")?.dataset?.index ?? 1, - ); - - const lessonData = courseData?.[lessonId - 1]; - - const { file: videoUrl, duration } = lessonData; - - debug.log("coursehunter course data:", courseData); - return { - url: videoUrl, - duration, - }; -} - -export default { - getVideoData, -}; diff --git a/src/utils/courseraUtils.js b/src/utils/courseraUtils.js deleted file mode 100644 index 69ef4244..00000000 --- a/src/utils/courseraUtils.js +++ /dev/null @@ -1,107 +0,0 @@ -import debug from "./debug.js"; -import { availableLangs } from "../config/constants.js"; -import { langTo6391 } from "./utils.js"; - -async function getCourseData(courseId) { - const response = await fetch( - `https://www.coursera.org/api/onDemandCourses.v1/${courseId}`, - ); - const resJSON = await response.json(); - return resJSON?.elements?.[0]; -} - -function getSubtitlesFileURL(captions, detectedLanguage, responseLang) { - let subtitle = captions?.find( - (caption) => langTo6391(caption.srclang) === detectedLanguage, - ); - - if (!subtitle) { - subtitle = - captions?.find( - (caption) => langTo6391(caption.srclang) === responseLang, - ) || captions?.[0]; - } - - return subtitle?.src; -} - -function getVideoFileURL(sources) { - // const source = sources?.find((src) => src.type === "video/webm" || src.type === "video/mp4", - const source = sources?.find((src) => src.type === "video/mp4"); - - return source?.src; -} - -function getPlayerData() { - return getPlayer()?.player; -} - -function getPlayer() { - return document.querySelector(".vjs-v6"); -} - -// Get the video data from the player -async function getVideoData(responseLang = "en") { - let translationHelp = null; - const data = getPlayerData(); - - const { duration } = data?.cache_ || {}; - const { courseId, tracks, sources } = data?.options_ || {}; - - const videoURL = getVideoFileURL(sources); - const courseData = await getCourseData(courseId); - - let detectedLanguage = courseData?.primaryLanguageCodes?.[0]; - detectedLanguage = detectedLanguage ? langTo6391(detectedLanguage) : "en"; - - if (!availableLangs.includes(detectedLanguage)) { - detectedLanguage = "en"; - } - - const subtitlesURL = getSubtitlesFileURL( - tracks, - detectedLanguage, - responseLang, - ); - console.log(`videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`); - - if (subtitlesURL && videoURL) { - translationHelp = [ - { - target: "video_file_url", - targetUrl: videoURL, - }, - { - target: "subtitles_file_url", - targetUrl: `https://www.coursera.org${subtitlesURL}`, - }, - ]; - } else if (videoURL && !subtitlesURL) { - console.warn( - "[VOT] Subtitles files not found. Using the link only to the video file.", - ); - translationHelp = { - url: videoURL, - }; - } else { - console.error( - `Failed to find subtitlesURL or videoURL. videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`, - ); - } - - const videoData = { - duration, - detectedLanguage, - translationHelp, - }; - - debug.log("coursera video data:", videoData); - console.log("[VOT] Detected language: ", videoData.detectedLanguage); - return videoData; -} - -export default { - getPlayer, - getPlayerData, - getVideoData, -}; diff --git a/src/utils/crypto.js b/src/utils/crypto.js deleted file mode 100644 index 9c6bc195..00000000 --- a/src/utils/crypto.js +++ /dev/null @@ -1,18 +0,0 @@ -export async function getHmacSha1(hmacKey, salt) { - try { - const utf8Encoder = new TextEncoder("utf-8"); - salt = utf8Encoder.encode(salt); - const key = await window.crypto.subtle.importKey( - "raw", - utf8Encoder.encode(hmacKey), - { name: "HMAC", hash: { name: "SHA-1" } }, - false, - ["sign", "verify"], - ); - const signature = await window.crypto.subtle.sign("HMAC", key, salt); - return btoa(String.fromCharCode(...new Uint8Array(signature))); - } catch (err) { - console.error(err); - return false; - } -} diff --git a/src/utils/storage.js b/src/utils/storage.js index 511bfd80..92e67bf2 100644 --- a/src/utils/storage.js +++ b/src/utils/storage.js @@ -20,7 +20,8 @@ export const votStorage = new (class { } } - return toNumber ? Number(val) ?? Number(def) : val ?? def; + const result = val ?? def; + return toNumber ? Number(result) : result; } async get(name, def = undefined, toNumber = false) { @@ -77,15 +78,23 @@ export const votStorage = new (class { "dontTranslateLanguage", "dontTranslateYourLang", "autoSetVolumeYandexStyle", + "autoVolume", + "buttonPos", "showVideoSlider", "syncVolume", "subtitlesMaxLength", "highlightWords", "responseLanguage", "defaultVolume", - "udemyData", "audioProxy", "showPiPButton", + "translateAPIErrors", + "translationService", + "detectService", + "m3u8ProxyHost", + "translateProxyEnabled", + "proxyWorkerHost", + "audioBooster", "locale-version", "locale-lang", "locale-phrases", diff --git a/src/utils/translateApis.js b/src/utils/translateApis.js index b33e51b2..34876d39 100644 --- a/src/utils/translateApis.js +++ b/src/utils/translateApis.js @@ -5,25 +5,7 @@ import { defaultTranslationService, } from "../config/config.js"; import { votStorage } from "./storage.js"; - -const HTTP_TIMEOUT = 3000; - -async function fetchWithTimeout(url, options = {}) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), HTTP_TIMEOUT); - - try { - return await fetch(url, { - ...options, - signal: controller.signal, - }); - } catch (error) { - console.error("Fetch timed-out. Error:", error); - return error; - } finally { - clearTimeout(timeoutId); - } -} +import { GM_fetch } from "./utils.js"; const YandexTranslateAPI = { async translate(text, lang) { @@ -34,11 +16,12 @@ const YandexTranslateAPI = { // ru, en (instead of auto-ru, auto-en) try { - const response = await fetchWithTimeout( + const response = await GM_fetch( `${translateUrls.yandex}?${new URLSearchParams({ text, lang, })}`, + { timeout: 3000 }, ); if (response instanceof Error) { @@ -61,10 +44,11 @@ const YandexTranslateAPI = { async detect(text) { // Limit: 10k symbols try { - const response = await fetchWithTimeout( + const response = await GM_fetch( `${detectUrls.yandex}?${new URLSearchParams({ text, })}`, + { timeout: 3000 }, ); if (response instanceof Error) { @@ -87,10 +71,14 @@ const YandexTranslateAPI = { const RustServerAPI = { async detect(text) { try { - const response = await fetch(detectUrls.rustServer, { - method: "POST", - body: text, - }); + const response = await GM_fetch( + detectUrls.rustServer, + { + method: "POST", + body: text, + }, + { timeout: 3000 }, + ); if (response instanceof Error) { throw response; @@ -107,17 +95,21 @@ const RustServerAPI = { const DeeplServerAPI = { async translate(text, fromLang = "auto", toLang = "ru") { try { - const response = await fetchWithTimeout(translateUrls.deepl, { - method: "POST", - headers: { - "content-type": "application/x-www-form-urlencoded", + const response = await GM_fetch( + translateUrls.deepl, + { + method: "POST", + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + text, + source_lang: fromLang, + target_lang: toLang, + }), }, - body: new URLSearchParams({ - text, - source_lang: fromLang, - target_lang: toLang, - }), - }); + { timeout: 3000 }, + ); if (response instanceof Error) { throw response; diff --git a/src/utils/udemyUtils.js b/src/utils/udemyUtils.js deleted file mode 100644 index a84720b1..00000000 --- a/src/utils/udemyUtils.js +++ /dev/null @@ -1,179 +0,0 @@ -import debug from "./debug.js"; -import { availableLangs } from "../config/constants.js"; -import { langTo6391 } from "./utils.js"; -import { localizationProvider } from "../localization/localizationProvider.js"; - -const udemyAPIURL = "https://www.udemy.com/api-2.0"; -const accessTokenLife = 2_592_000_000; // 30 days - -async function getCourseLang(courseId) { - const response = await fetch( - `${udemyAPIURL}/courses/${courseId}/?` + - new URLSearchParams({ - "fields[course]": "locale", - use_remote_version: "true", - caching_intent: "true", - }), - ); - return await response.json(); -} - -function checkUdemyTokenExpire(expires) { - return expires + accessTokenLife > new Date().getTime(); -} - -async function getLectureData(udemyData, courseId, lectureId) { - // reference: https://greasyfork.org/ru/scripts/422576-udemy-subtitle-downloader-v3/code - if (!checkUdemyTokenExpire(udemyData.expires) || !udemyData.accessToken) { - console.error(localizationProvider.get("udemyAccessTokenExpired")); - return undefined; - } - - const bearerToken = `Bearer ${udemyData.accessToken}`; - const response = await fetch( - `${udemyAPIURL}/users/me/subscribed-courses/${courseId}/lectures/${lectureId}/?` + - new URLSearchParams({ - "fields[lecture]": "asset", - "fields[asset]": "length,media_sources,captions", - }), - { - headers: { - "x-udemy-authorization": bearerToken, - authorization: bearerToken, - }, - }, - ); - return await response.json(); -} - -function getSubtitlesFileURL(captions, detectedLanguage, responseLang) { - let subtitle = captions?.find( - (caption) => langTo6391(caption.locale_id) === detectedLanguage, - ); - - if (!subtitle) { - subtitle = - captions?.find( - (caption) => langTo6391(caption.locale_id) === responseLang, - ) || captions?.[0]; - } - - return subtitle?.url; -} - -function getVideoFileURLFromAPI(sources) { - const source = sources?.find( - (src) => src.type === "video/webm" || src.type === "video/mp4", - ); - - return source?.src; -} - -function getPlayerData() { - return getPlayer()?.player; -} - -function getModuleData() { - const moduleArgs = document.querySelector( - ".ud-app-loader[data-module-id='course-taking']", - )?.dataset?.moduleArgs; - if (!moduleArgs) { - console.error(localizationProvider.get("udemyModuleArgsNotFound")); - return {}; - } - return JSON.parse(moduleArgs); -} - -function getLectureId() { - return /learn\/lecture\/([^/]+)/.exec(window.location.pathname)?.[1]; -} - -function getPlayer() { - return document.querySelector(".vjs-v7"); -} - -function getVideoURLFromPlayer() { - const src = getPlayer()?.querySelector("video")?.src; - return src?.startsWith("blob:") ? false : src; -} - -// Get the video data from the player -async function getVideoData(udemyData, responseLang = "en") { - let translationHelp = null; - const data = getPlayerData(); - debug.log("udemyData", udemyData); - - const moduleData = getModuleData(); - debug.log("moduleData: ", moduleData); - - const courseId = moduleData.courseId; - const lectureId = getLectureId(); - debug.log(`CourseId: ${courseId}, lectureId: ${lectureId}`); - - const courseLang = await getCourseLang(courseId); - debug.log("courseLang Data:", courseLang); - const lectureData = await getLectureData(udemyData, courseId, lectureId); - console.log("lecture Data:", lectureData); - - let detectedLanguage = courseLang?.locale?.locale; - detectedLanguage = detectedLanguage ? langTo6391(detectedLanguage) : "en"; - - if (!availableLangs.includes(detectedLanguage)) { - detectedLanguage = "en"; - } - - const duration = lectureData?.asset?.length || data?.cache_?.duration; - const videoURL = - getVideoFileURLFromAPI(lectureData?.asset?.media_sources) || - getVideoURLFromPlayer(); - const subtitlesURL = getSubtitlesFileURL( - lectureData?.asset?.captions, - detectedLanguage, - responseLang, - ); - - console.log(`videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`); - - if (subtitlesURL && videoURL) { - translationHelp = [ - { - target: "video_file_url", - targetUrl: videoURL, - }, - { - target: "subtitles_file_url", - targetUrl: subtitlesURL, - }, - ]; - } else if (videoURL && !subtitlesURL) { - console.warn( - "[VOT] Subtitles files not found. Using the link only to the video file.", - ); - translationHelp = { - url: videoURL, - }; - } else { - console.error( - `Failed to find subtitlesURL or videoURL. videoURL: ${videoURL}, subtitlesURL: ${subtitlesURL}`, - ); - } - - const videoData = { - duration, - detectedLanguage, - translationHelp, - }; - - debug.log("udemy video data:", videoData); - console.log("[VOT] Detected language: ", videoData.detectedLanguage); - return videoData; -} - -export default { - getPlayer, - getPlayerData, - getVideoData, - getModuleData, - getCourseLang, - getLectureData, -}; diff --git a/src/utils/utils.js b/src/utils/utils.js index 757d0f40..2f2ed746 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,286 +1,9 @@ import { localizationProvider } from "../localization/localizationProvider.js"; -import youtubeUtils from "./youtubeUtils.js"; +import debug from "./debug.js"; const userlang = navigator.language || navigator.userLanguage; export const lang = userlang?.substr(0, 2)?.toLowerCase() ?? "en"; -// not used -// function waitForElm(selector) { -// // https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists -// return new Promise((resolve) => { -// const element = document.querySelector(selector); -// if (element) { -// return resolve(element); -// } - -// const observer = new MutationObserver(() => { -// const element = document.querySelector(selector); -// if (element) { -// resolve(element); -// observer.disconnect(); -// } -// }); - -// observer.observe(document.body, { -// childList: true, -// subtree: true, -// once: true, -// }); -// }); -// } - -// not used -// const sleep = (m) => new Promise((r) => setTimeout(r, m)); - -const getVideoId = (service, video) => { - let url = new URL(window.location.href); - - switch (service) { - case "piped": - case "invidious": - case "youtube": { - if (url.searchParams.has("enablejsapi")) { - const videoUrl = youtubeUtils.getPlayer().getVideoUrl(); - url = new URL(videoUrl); - } - - return ( - /(?:watch|embed|shorts|live)\/([^/]+)/.exec(url.pathname)?.[1] || - url.searchParams.get("v") - ); - } - case "vk": { - const pathID = /^\/(video|clip)-?\d{8,9}_\d{9}$/.exec(url.pathname); - const paramZ = url.searchParams.get("z"); - const paramOID = url.searchParams.get("oid"); - const paramID = url.searchParams.get("id"); - if (pathID) { - return pathID[0].slice(1); - } else if (paramZ) { - return paramZ.split("/")[0]; - } else if (paramOID && paramID) { - return `video-${Math.abs(parseInt(paramOID))}_${paramID}`; - } - - return null; - } - case "nine_gag": - case "9gag": - case "gag": - return /gag\/([^/]+)/.exec(url.pathname)?.[1]; - case "twitch": { - const clipPath = /([^/]+)\/(?:clip)\/([^/]+)/.exec(url.pathname); - if (/^m\.twitch\.tv$/.test(url.hostname)) { - return /videos\/([^/]+)/.exec(url.href)?.[0] || url.pathname.slice(1); - } else if (/^player\.twitch\.tv$/.test(url.hostname)) { - return `videos/${url.searchParams.get("video")}`; - } else if (/^clips\.twitch\.tv$/.test(url.hostname)) { - // https://clips.twitch.tv/clipId - const schema = document.querySelector( - "script[type='application/ld+json']", - ); - const pathname = url.pathname.slice(1); - if (!schema) { - // иногда из-за не прогрузов твича это не работает, но пусть лучше будет (можно переделать все в async и ждать элемента, но нужно ли это ради 1 сайта) - // ссылки вида https://clips.twitch.tv/embed?clip=clipId грузятся нормально - const isEmbed = pathname === "embed"; - const channelLink = document.querySelector( - isEmbed - ? ".tw-link[data-test-selector='stream-info-card-component__stream-avatar-link']" - : ".clips-player a:not([class])", - ); - - if (!channelLink) { - return; - } - - const channelName = channelLink.href.replace( - "https://www.twitch.tv/", - "", - ); - - return `${channelName}/clip/${isEmbed ? url.searchParams.get("clip") : pathname}`; - } - - const schemaJSON = JSON.parse(schema.innerText); - const channelLink = schemaJSON["@graph"].find( - (obj) => obj["@type"] === "VideoObject", - )?.creator.url; - - const channelName = channelLink.replace("https://www.twitch.tv/", ""); - return `${channelName}/clip/${pathname}`; - } else if (clipPath) { - return clipPath[0]; - } - - return /(?:videos)\/([^/]+)/.exec(url.pathname)?.[0]; - } - case "proxitok": - return /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; - case "tiktok": { - let id = /([^/]+)\/video\/([^/]+)/.exec(url.pathname)?.[0]; - if (!id) { - const playerEl = video.closest(".xgplayer-playing, .tiktok-web-player"); - const itemEl = playerEl?.closest( - 'div[data-e2e="recommend-list-item-container"]', - ); - const authorEl = itemEl?.querySelector( - 'a[data-e2e="video-author-avatar"]', - ); - if (playerEl && authorEl) { - const videoId = playerEl.id?.match(/^xgwrapper-\d+-(.*)$/)?.at(1); - const author = authorEl.href?.match(/.*(@.*)$/)?.at(1); - if (videoId && author) { - id = `${author}/video/${videoId}`; - } - } - } - return id; - } - case "vimeo": { - const appId = url.searchParams.get("app_id"); - const videoId = - /[^/]+\/[^/]+$/.exec(url.pathname)?.[0] || - /[^/]+$/.exec(url.pathname)?.[0]; - - return appId ? `${videoId}?app_id=${appId}` : videoId; - } - case "xvideos": - return /[^/]+\/[^/]+$/.exec(url.pathname)?.[0]; - case "pornhub": - return ( - url.searchParams.get("viewkey") || - /embed\/([^/]+)/.exec(url.pathname)?.[1] - ); - case "twitter": - return /status\/([^/]+)/.exec(url.pathname)?.[1]; - case "udemy": - case "rumble": - case "facebook": - return url.pathname.slice(1); - case "rutube": - return /(?:video|embed)\/([^/]+)/.exec(url.pathname)?.[1]; - case "coub": - return ( - /(?:view|embed)\/([^/]+)/.exec(url.pathname)?.[1] || - document.querySelector(".coub.active")?.dataset?.permalink - ); - case "bilibili": { - const bvid = url.searchParams.get("bvid"); - if (bvid) { - return bvid; - } - - let vid = /video\/([^/]+)/.exec(url.pathname)?.[1]; - if (vid && url.searchParams.get("p") !== null) { - vid += `/?p=${url.searchParams.get("p")}`; - } - - return vid; - } - case "mail_ru": { - const pathname = url.pathname; - if (pathname.startsWith("/v/") || pathname.startsWith("/mail/")) { - return pathname.slice(1); - } - - const videoId = /video\/embed\/([^/]+)/.exec(pathname)?.[1]; - if (!videoId) { - return null; - } - - const referer = document.querySelector(".b-video-controls__mymail-link"); - if (!referer) { - return false; - } - - return referer?.href.split("my.mail.ru")?.[1]; - } - case "bitchute": { - if (video.src?.startsWith("blob:") || !video.src?.includes(".mp4")) { - return null; - } - - return video.src; - // doesn't want to translate using a bitchute link - // return /([^/]+)\/([^/]+).mp4/.exec(videoUrl.pathname)?.[2]; - } - case "coursera": - // ! LINK SHOULD BE LIKE THIS https://www.coursera.org/learn/learning-how-to-learn/lecture/75EsZ - // return url.pathname.match(/lecture\/([^/]+)\/([^/]+)/)?.[1]; // <--- COURSE PREVIEW - return /learn\/([^/]+)\/lecture\/([^/]+)/.exec(url.pathname)?.[0]; // <--- COURSE PASSING (IF YOU LOGINED TO COURSERA) - case "eporner": - // ! LINK SHOULD BE LIKE THIS eporner.com/video-XXXXXXXXX/isdfsd-dfjsdfjsdf-dsfsdf-dsfsda-dsad-ddsd - return /video-([^/]+)\/([^/]+)/.exec(url.pathname)?.[0]; - case "peertube": - return /\/w\/([^/]+)/.exec(url.pathname)?.[0]; - case "dailymotion": { - // we work in the context of the player - // geo.dailymotion.com - const plainPlayerConfig = Array.from( - document.querySelectorAll("*"), - ).filter((s) => s.innerHTML.trim().includes(".m3u8")); - try { - let videoUrl = plainPlayerConfig[1].lastChild.src; - return /\/video\/(\w+)\.m3u8/.exec(videoUrl)?.[1]; - } catch (e) { - console.error("[VOT]", e); - return false; - } - } - case "trovo": { - const vid = url.searchParams.get("vid"); - if (!vid) { - return null; - } - - const path = /([^/]+)\/(\d+)/.exec(url.pathname)?.[0]; - if (!path) { - return null; - } - - return `${path}?vid=${vid}`; - } - case "yandexdisk": - return /\/i\/([^/]+)/.exec(url.pathname)?.[1]; - case "coursehunter": { - const courseId = /\/course\/([^/]+)/.exec(url.pathname)?.[1]; - return courseId ? courseId + url.search : false; - } - case "ok.ru": { - return /\/video\/(\d+)/.exec(url.pathname)?.[1]; - } - case "googledrive": - return url.searchParams.get("docid"); - case "bannedvideo": - return url.searchParams.get("id"); - case "weverse": - return /([^/]+)\/(live|media)\/([^/]+)/.exec(url.pathname)?.[0]; - case "newgrounds": - return /([^/]+)\/(view)\/([^/]+)/.exec(url.pathname)?.[0]; - case "egghead": - return url.pathname.slice(1); - case "youku": - return /v_show\/id_[\w=]+/.exec(url.pathname)?.[0]; - case "archive": - return /(details|embed)\/([^/]+)/.exec(url.pathname)?.[2]; - // case "sibnet": { - // const videoId = url.searchParams.get("videoid"); - // if (videoId) { - // return `video${videoId}`; - // } - - // return /video([^/]+)/.exec(url.pathname)?.[0]; - // } - // case "patreon": - // return /posts\/([^/]+)/.exec(url.pathname)?.[0]; - case "directlink": - return url.pathname + url.search; - default: - return false; - } -}; - function secsToStrTime(secs) { const minutes = Math.floor(secs / 60); const seconds = Math.floor(secs % 60); @@ -357,52 +80,62 @@ function cleanText(title, description) { return fullText.replace(/[^\p{L}\s]+|\s+/gu, " ").trim(); } -async function GM_fetch(url, opt = {}) { +async function GM_fetch(url, opts = {}) { + const { timeout = 15000, ...fetchOptions } = opts; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + try { - // Попытка выполнить запрос с помощью fetch - const response = await fetch(url, opt); + if (url.includes("api.browser.yandex.ru")) { + throw new Error("Preventing yandex cors"); + } + + const response = await fetch(url, { + signal: controller.signal, + ...fetchOptions, + }); + clearTimeout(timeoutId); return response; - } catch (error) { + } catch (err) { // Если fetch завершился ошибкой, используем GM_xmlhttpRequest // https://greasyfork.org/ru/scripts/421384-gm-fetch/code + debug.log("GM_fetch preventing cors by GM_xmlhttpRequest", err.message); return new Promise((resolve, reject) => { - // https://www.tampermonkey.net/documentation.php?ext=dhdg#GM_xmlhttpRequest - // https://violentmonkey.github.io/api/gm/#gm_xmlhttprequest + clearTimeout(timeoutId); GM_xmlhttpRequest({ - method: opt.method || "GET", + method: fetchOptions.method || "GET", url: url, responseType: "blob", + ...fetchOptions, + data: fetchOptions.body, + timeout: timeout, onload: (resp) => { + // chrome \n and ":", firefox \r\n and ": " (Only in GM_xmlhttpRequest) + // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#examples + const headers = Object.fromEntries( + resp.responseHeaders + .trim() + .split(/\r?\n/) + .map((line) => line.split(/: (.+)/)) + .filter(([key]) => key && /^[\w-]+$/.test(key)), + ); + resolve( new Response(resp.response, { status: resp.status, - // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#examples - headers: Object.fromEntries( - resp.responseHeaders - .trim() - .split("\r\n") - .map((line) => { - let parts = line.split(": "); - if (parts?.[0] === "set-cookie") { - return; - } - return [parts.shift(), parts.join(": ")]; - }) - .filter((key) => key), - ), + headers: headers, }), ); }, - ontimeout: () => reject(new Error("fetch timeout")), + ontimeout: () => reject(new Error("Timeout")), onerror: (error) => reject(error), - onabort: () => reject(new Error("fetch abort")), + onabort: () => reject(new Error("AbortError")), }); }); } } export { - getVideoId, secsToStrTime, langTo6391, isPiPAvailable, diff --git a/src/utils/weverseUtils.js b/src/utils/weverseUtils.js deleted file mode 100644 index 9aa4b97b..00000000 --- a/src/utils/weverseUtils.js +++ /dev/null @@ -1,163 +0,0 @@ -import debug from "./debug.js"; -import { getHmacSha1 } from "./crypto.js"; - -const API_ORIGIN = "https://global.apis.naver.com/weverse/wevweb"; // find as REACT_APP_API_GW_ORIGIN in main..js -const API_APP_ID = "be4d79eb8fc7bd008ee82c8ec4ff6fd4"; // find as REACT_APP_API_APP_ID in main..js -const API_HMAC_KEY = "1b9cb6378d959b45714bec49971ade22e6e24e42"; // find as c.active near `createHmac('sha1'...` in main..js - -async function createHash(pathname) { - // pathname example: /post/v1.0/post-3-142049908/preview?fieldSet=postForPreview... - const timestamp = Date.now(); - - // example salt is /video/v1.1/vod/67134/inKey?gcc=RU&appId=be4d79eb8fc7bd008ee82c8ec4ff6fd4&language=en&os=WEB&platform=WEB&wpf=pc1707527163588 - let salt = pathname.substring(0, Math.min(255, pathname.length)) + timestamp; - - const sign = await getHmacSha1(API_HMAC_KEY, salt); - - return { - wmsgpad: timestamp, - wmd: sign, - }; -} - -function getURLData() { - return { - appId: API_APP_ID, - language: "en", - os: "WEB", - platform: "WEB", - wpf: "pc", - }; -} - -async function getVideoPreview(postId) { - const pathname = - `/post/v1.0/post-${postId}/preview?` + - new URLSearchParams({ - fieldSet: "postForPreview", - ...getURLData(), - }); // ! DON'T EDIT ME - - const hash = await createHash(pathname); - - try { - const res = await fetch( - API_ORIGIN + pathname + "&" + new URLSearchParams(hash), - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -async function getVideoInKey(videoId) { - const pathname = - `/video/v1.1/vod/${videoId}/inKey?` + - new URLSearchParams({ - gcc: "RU", - ...getURLData(), - }); // ! DON'T EDIT ME - const hash = await createHash(pathname); - - try { - const res = await fetch( - API_ORIGIN + pathname + "&" + new URLSearchParams(hash), - { - method: "POST", - }, - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -async function getVideoInfo(infraVideoId, inkey, serviceId) { - const timestamp = Date.now(); - try { - const res = await fetch( - `https://global.apis.naver.com/rmcnmv/rmcnmv/vod/play/v2.0/${infraVideoId}?` + - new URLSearchParams({ - key: inkey, - sid: serviceId, - nonce: timestamp, - devt: "html5_pc", - prv: "N", - aup: "N", - stpb: "N", - cpl: "en", - env: "prod", - lc: "en", - adi: JSON.stringify([ - { - adSystem: null, - }, - ]), - adu: "/", - }), - ); - - return await res.json(); - } catch (err) { - console.error(err); - return false; - } -} - -function extractVideoInfo(videoList) { - return videoList.find( - (video) => video.useP2P === false && video.source.includes(".mp4"), - ); -} - -async function getVideoData() { - // ! When translating using a regular link (like this https://weverse.io/aespa/live/3-142049908), - // ! we will get an error, so we have to do this - - const postId = /([^/]+)\/(live|media)\/([^/]+)/.exec( - new URL(window.location).pathname, - )?.[3]; - - const videoPreview = await getVideoPreview(postId); - if (!videoPreview) { - return undefined; - } - - debug.log("weverse video preview data:", videoPreview); - - const { videoId, serviceId, infraVideoId } = videoPreview.extension.video; - if (!(videoId && serviceId && infraVideoId)) { - return false; - } - - const inkeyData = await getVideoInKey(videoId); - debug.log("weverse video inKey data:", videoPreview); - if (!inkeyData) { - return false; - } - - const videoInfo = await getVideoInfo( - infraVideoId, - inkeyData.inKey, - serviceId, - ); - debug.log("weverse video info:", videoInfo); - - const videoItem = extractVideoInfo(videoInfo.videos.list); - if (!videoItem) { - return false; - } - - return { - url: videoItem.source, - duration: videoItem.duration, - }; -} - -export default { - getVideoData, -}; diff --git a/src/utils/youtubeUtils.js b/src/utils/youtubeUtils.js index 5e50b158..411cdc4a 100644 --- a/src/utils/youtubeUtils.js +++ b/src/utils/youtubeUtils.js @@ -1,5 +1,6 @@ +import { availableLangs } from "vot.js/consts"; + import debug from "./debug.js"; -import { availableLangs } from "../config/constants.js"; import { langTo6391, cleanText } from "./utils.js"; import { detect } from "./translateApis.js"; diff --git a/src/yandexProtobuf.js b/src/yandexProtobuf.js deleted file mode 100644 index 42437f55..00000000 --- a/src/yandexProtobuf.js +++ /dev/null @@ -1,169 +0,0 @@ -// coursera & udemy translation help object -const VideoTranslationHelpObject = new protobuf.Type( - "VideoTranslationHelpObject", -) - .add(new protobuf.Field("target", 1, "string")) // video_file_url or subtitles_file_url - .add(new protobuf.Field("targetUrl", 2, "string")); // url to video_file or url to subtitles - -const VideoTranslationRequest = new protobuf.Type("VideoTranslationRequest") - .add(new protobuf.Field("url", 3, "string")) - .add(new protobuf.Field("deviceId", 4, "string")) // used in mobile version - .add(new protobuf.Field("firstRequest", 5, "bool")) // true for the first request, false for subsequent ones - .add(new protobuf.Field("duration", 6, "double")) - .add(new protobuf.Field("unknown2", 7, "int32")) // 1 1 - .add(new protobuf.Field("language", 8, "string")) // source language code - .add(new protobuf.Field("forceSourceLang", 9, "bool")) // 0 - auto detected by yabrowser, 1 - user set his own lang by dropdown - .add(new protobuf.Field("unknown4", 10, "int32")) // 0 0 - .add( - new protobuf.Field( - "translationHelp", - 11, - "VideoTranslationHelpObject", - "repeated", - ), - ) // array for translation assistance ([0] -> {2: link to video, 1: "video_file_url"}, [1] -> {2: link to subtitles, 1: "subtitles_file_url"}) - .add(new protobuf.Field("responseLanguage", 14, "string")) - .add(new protobuf.Field("unknown5", 15, "int32")) // 0 - .add(new protobuf.Field("unknown6", 16, "int32")) // 1 - .add(new protobuf.Field("bypassCache", 17, "bool")); // ? maybe they have some kind of bypass limiter from one IP, because after one such request it stopped working - -const VideoSubtitlesRequest = new protobuf.Type("VideoSubtitlesRequest") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("language", 2, "string")); // source language code - -const VideoStreamRequest = new protobuf.Type("VideoStreamRequest") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("language", 2, "string")) - .add(new protobuf.Field("responseLanguage", 3, "string")); - -const VideoStreamPingRequest = new protobuf.Type("VideoStreamPingRequest").add( - new protobuf.Field("pingId", 1, "int32"), -); - -const VideoTranslationResponse = new protobuf.Type("VideoTranslationResponse") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("duration", 2, "double")) - .add(new protobuf.Field("status", 4, "int32")) - .add(new protobuf.Field("remainingTime", 5, "int32")) // secs before translation (used as interval before next request in yaBrowser) - .add(new protobuf.Field("unknown0", 6, "int32")) // unknown 0 (1st request) -> 10 (2nd, 3th and etc requests). (if status is 0) - .add(new protobuf.Field("translationId", 7, "string")) - .add(new protobuf.Field("language", 8, "string")) // detected language (if the wrong one is set) - .add(new protobuf.Field("message", 9, "string")); - -const VideoSubtitlesObject = new protobuf.Type("VideoSubtitlesObject") - .add(new protobuf.Field("language", 1, "string")) - .add(new protobuf.Field("url", 2, "string")) - .add(new protobuf.Field("unknown2", 3, "int32")) - .add(new protobuf.Field("translatedLanguage", 4, "string")) - .add(new protobuf.Field("translatedUrl", 5, "string")) - .add(new protobuf.Field("unknown5", 6, "int32")) - .add(new protobuf.Field("unknown6", 7, "int32")); - -const VideoSubtitlesResponse = new protobuf.Type("VideoSubtitlesResponse") - .add(new protobuf.Field("waiting", 1, "int32")) // 0 - finished/failed | 1 - waiting result (1 - ~10min, maybe more) - .add(new protobuf.Field("subtitles", 2, "VideoSubtitlesObject", "repeated")); - -const VideoStreamObject = new protobuf.Type("VideoStreamObject") - .add(new protobuf.Field("url", 1, "string")) - .add(new protobuf.Field("timestamp", 2, "int64")); // timestamp in ms (probably means the time of 1 request to translate the stream) - -const VideoStreamResponse = new protobuf.Type("VideoStreamResponse") - .add(new protobuf.Field("interval", 1, "int32")) // 20s - streaming, 10s - translating, 0s - there is no connection with the server (the broadcast is finished or deleted) - .add(new protobuf.Field("translatedInfo", 2, "VideoStreamObject")) - .add(new protobuf.Field("pingId", 3, "int32")); - -// /session/create -const YandexSessionRequest = new protobuf.Type("YandexSessionRequest") - .add(new protobuf.Field("uuid", 1, "string")) - .add(new protobuf.Field("module", 2, "string")); - -const YandexSessionResponse = new protobuf.Type("YandexSessionResponse") - .add(new protobuf.Field("sign", 1, "string")) - .add(new protobuf.Field("expires", 2, "int32")); - -// * Yandex has been skipping any translation streams for a long time (whitelist always return true) -// * Most likely, it is already outdated and will not be used -// const VideoWhitelistStreamRequest = new protobuf.Type("VideoWhitelistStreamRequest") -// .add(new protobuf.Field("url", 1, "string")) -// .add(new protobuf.Field("deviceId", 4, "string")) - -// const VideoWhitelistStreamResponse = new protobuf.Type("VideoWhitelistStreamResponse") -// .add(new protobuf.Field("inWhitelist", 1, "bool")) - -// Create a root namespace and add the types -const root = new protobuf.Root() - .define("yandex") - .add(VideoTranslationHelpObject) - .add(VideoTranslationRequest) - .add(VideoTranslationResponse) - .add(VideoSubtitlesRequest) - .add(VideoSubtitlesObject) - .add(VideoSubtitlesResponse) - .add(VideoStreamPingRequest) - .add(VideoStreamRequest) - .add(VideoStreamObject) - .add(VideoStreamResponse) - .add(YandexSessionRequest) - .add(YandexSessionResponse); - -// Export the encoding and decoding functions -export const yandexProtobuf = { - encodeTranslationRequest( - url, - duration, - requestLang, - responseLang, - translationHelp, - ) { - return root.VideoTranslationRequest.encode({ - url, - firstRequest: true, - duration, - unknown2: 1, - language: requestLang, - forceSourceLang: false, - unknown4: 0, - translationHelp, - responseLanguage: responseLang, - unknown5: 0, - unknown6: 1, - bypassCache: false, - }).finish(); - }, - decodeTranslationResponse(response) { - return root.VideoTranslationResponse.decode(new Uint8Array(response)); - }, - encodeSubtitlesRequest(url, requestLang) { - return root.VideoSubtitlesRequest.encode({ - url, - language: requestLang, - }).finish(); - }, - decodeSubtitlesResponse(response) { - return root.VideoSubtitlesResponse.decode(new Uint8Array(response)); - }, - encodeStreamPingRequest(pingId) { - return root.VideoStreamPingRequest.encode({ - pingId, - }).finish(); - }, - encodeStreamRequest(url, requestLang, responseLang) { - return root.VideoStreamRequest.encode({ - url, - language: requestLang, - responseLanguage: responseLang, - }).finish(); - }, - decodeStreamResponse(response) { - return root.VideoStreamResponse.decode(new Uint8Array(response)); - }, - encodeYandexSessionRequest(uuid, module) { - return root.YandexSessionRequest.encode({ - uuid, - module, - }).finish(); - }, - decodeYandexSessionResponse(response) { - return root.YandexSessionResponse.decode(new Uint8Array(response)); - }, -}; diff --git a/src/yandexRequest-cloudflare.js b/src/yandexRequest-cloudflare.js deleted file mode 100644 index 2e574705..00000000 --- a/src/yandexRequest-cloudflare.js +++ /dev/null @@ -1,54 +0,0 @@ -import { yandexUserAgent, proxyWorkerHost } from "./config/config.js"; -import debug from "./utils/debug.js"; -import { votStorage } from "./utils/storage.js"; -import { GM_fetch } from "./utils/utils.js"; - -async function yandexRequest(path, body, headers, callback) { - let response; - let responseBody; - try { - debug.log("yandexRequest:", path); - // Create a fetch options object with headers and body - const options = { - method: "POST", - mode: "cors", - cache: "no-cache", - headers: { - "Content-Type": "application/json", - }, - redirect: "follow", - referrerPolicy: "no-referrer", - body: JSON.stringify({ - headers: { - ...{ - Accept: "application/x-protobuf", - "Accept-Language": "en", - "Content-Type": "application/x-protobuf", - "User-Agent": yandexUserAgent, - Pragma: "no-cache", - "Cache-Control": "no-cache", - "Sec-Fetch-Mode": "no-cors", - }, - ...headers, - }, - body: Array.from(body), - }), - }; - const workerHost = await votStorage.get("proxyWorkerHost", proxyWorkerHost); - // Fetch the translation from the worker host - response = await GM_fetch(`https://${workerHost}${path}`, options); - debug.log("yandexRequest:", response.status, response); - // Get the response body as an array buffer - responseBody = await response.arrayBuffer(); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - response = { status: -1 }; - responseBody = exception; - } - - // Call the callback function with the result - callback(response.status == 200, responseBody); -} - -export default yandexRequest; diff --git a/src/yandexRequest.js b/src/yandexRequest.js deleted file mode 100644 index 3d528bd9..00000000 --- a/src/yandexRequest.js +++ /dev/null @@ -1,49 +0,0 @@ -import { workerHost, yandexUserAgent } from "./config/config.js"; -import debug from "./utils/debug.js"; - -async function yandexRequest(path, body, headers, callback) { - try { - debug.log("yandexRequest:", path); - // Create a fetch options object with headers and body - const options = { - url: `https://${workerHost}${path}`, - method: "POST", - headers: { - ...{ - Accept: "application/x-protobuf", - "Accept-Language": "en", - "Content-Type": "application/x-protobuf", - "User-Agent": yandexUserAgent, - Pragma: "no-cache", - "Cache-Control": "no-cache", - "Sec-Fetch-Mode": "no-cors", - "sec-ch-ua": null, - "sec-ch-ua-mobile": null, - "sec-ch-ua-platform": null, - }, - ...headers, - }, - binary: true, - data: new Blob([body]), - responseType: "arraybuffer", - }; - // Send the request using GM_xmlhttpRequest - GM_xmlhttpRequest({ - ...options, - onload: (http) => { - debug.log("yandexRequest:", http.status, http); - callback(http.status === 200, http.response); - }, - onerror: (error) => { - console.error("[VOT]", error); - callback(false); - }, - }); - } catch (exception) { - console.error("[VOT]", exception); - // Handle errors - callback(false); - } -} - -export default yandexRequest; diff --git a/tests/headers.json b/tests/headers.json new file mode 100644 index 00000000..eb51e7a9 --- /dev/null +++ b/tests/headers.json @@ -0,0 +1,12 @@ +{ + "name": "[VOT] - Test UI", + "description": "Test ui", + "version": "1.0.0", + "author": "sodapng, mynovelhost, Toil, SashaXser, MrSoczekXD", + "namespace": "vot-test-ui", + "icon": "https://translate.yandex.ru/icons/favicon.ico", + "match": ["*://*.toil.cc/*"], + "require": [ + "https://gist.githubusercontent.com/ilyhalight/6eb5bb4dffc7ca9e3c57d6933e2452f3/raw/7ab38af2228d0bed13912e503bc8a9ee4b11828d/gm-addstyle-polyfill.js" + ] +} diff --git a/tests/ui.js b/tests/ui.js new file mode 100644 index 00000000..9eef5b1b --- /dev/null +++ b/tests/ui.js @@ -0,0 +1,234 @@ +import { svg } from "lit-html"; +import ui from "../src/ui.js"; + +class TestUI { + initUI() { + this.testDialog = ui.createDialog("Test"); + // Header + this.h1 = ui.createHeader("H1 Lorem ipsum dolor", 1); + this.h2 = ui.createHeader("H2 Lorem ipsum dolor", 2); + this.h3 = ui.createHeader("H3 Lorem ipsum dolor", 3); + this.h4 = ui.createHeader("H4 Lorem ipsum dolor", 4); + this.h5 = ui.createHeader("H5 Lorem ipsum dolor", 5); + this.h6 = ui.createHeader("H6 Lorem ipsum dolor", 6); + this.testHTML = document.createElement("vot-block"); + this.testHTML.style = "padding: 4px; color: skyblue;"; + this.testHTML.textContent = "Lorem ipsum dolor"; + this.h3WithHTML = ui.createHeader(this.testHTML.cloneNode(true), 3); + console.log(this.h3WithHTML.outerHTML); + this.testDialog.bodyContainer.append( + this.h1, + this.h2, + this.h3, + this.h4, + this.h5, + this.h6, + this.h3WithHTML, + ); + + // Information + this.info = ui.createInformation( + "Information Lorem ipsum dolor:", + "Neque porro quisquam est qui", + ); + this.infoWithHTML = ui.createInformation( + this.testHTML.cloneNode(true), + this.testHTML.cloneNode(true), + ); + + this.testDialog.bodyContainer.append( + this.info.container, + this.infoWithHTML.container, + ); + + // Button + this.button = ui.createButton("Button Lorem ipsum dolor"); + this.buttonDisabled = ui.createButton("Button Disabled Lorem ipsum dolor"); + this.buttonDisabled.setAttribute("disabled", "true"); + this.buttonWithHTML = ui.createButton(this.testHTML.cloneNode(true)); + this.testDialog.bodyContainer.append( + this.button, + this.buttonDisabled, + this.buttonWithHTML, + ); + + // TextButton + this.textButton = ui.createTextButton("TextButton Lorem ipsum dolor"); + this.textButtonDisabled = ui.createTextButton( + "TextButton Disabled Lorem ipsum dolor", + ); + this.textButtonDisabled.setAttribute("disabled", "true"); + this.textButtonWithHTML = ui.createTextButton( + this.testHTML.cloneNode(true), + ); + this.testDialog.bodyContainer.append( + this.textButton, + this.textButtonDisabled, + this.textButtonWithHTML, + ); + + // OutlinedButton + this.outlinedButton = ui.createOutlinedButton( + "OutlinedButton Lorem ipsum dolor", + ); + this.outlinedButtonDisabled = ui.createOutlinedButton( + "OutlinedButton Disabled Lorem ipsum dolor", + ); + this.outlinedButtonDisabled.setAttribute("disabled", "true"); + this.outlinedButtonWithHTML = ui.createOutlinedButton( + this.testHTML.cloneNode(true), + ); + this.testDialog.bodyContainer.append( + this.outlinedButton, + this.outlinedButtonDisabled, + this.outlinedButtonWithHTML, + ); + + // IconButton + this.iconButton = ui.createIconButton( + svg` + + `, + ); + this.iconButtonDisabled = ui.createIconButton( + svg` + + `, + ); + this.iconButtonDisabled.setAttribute("disabled", "true"); + this.testDialog.bodyContainer.append( + this.iconButton, + this.iconButtonDisabled, + ); + + // Checkbox + this.checkbox = ui.createCheckbox("Checkbox Lorem ipsum dolor", true); + this.checkboxDisabled = ui.createCheckbox( + "Checkbox Disabled Lorem ipsum dolor", + true, + ); + this.checkboxDisabled.input.disabled = true; + this.checkboxWithHTML = ui.createCheckbox( + this.testHTML.cloneNode(true), + false, + ); + + this.testDialog.bodyContainer.append( + this.checkbox.container, + this.checkboxDisabled.container, + this.checkboxWithHTML.container, + ); + + // Slider + this.slider = ui.createSlider("Slider Lorem ipsum dolor"); + this.sliderDisabled = ui.createSlider("Slider Disabled Lorem ipsum dolor"); + this.sliderDisabled.input.disabled = true; + this.sliderWithHTML = ui.createSlider( + this.testHTML.cloneNode(true), + 2, + 0, + 300, + ); + + this.testDialog.bodyContainer.append( + this.slider.container, + this.sliderDisabled.container, + this.sliderWithHTML.container, + ); + + // Textfield + this.textfield = ui.createTextfield( + "Textfield Lorem ipsum dolor", + "", + "test", + ); + this.textfieldWithoutLabel = ui.createTextfield("", "", "test"); + this.textfieldWithoutValue = ui.createTextfield( + "Textfield Without Value", + "", + "", + ); + this.textfieldDisabled = ui.createTextfield( + "Textfield Disabled Lorem ipsum dolor", + "", + "", + ); + this.textfieldDisabled.input.disabled = true; + this.textfieldWithHTML = ui.createTextfield( + this.testHTML.cloneNode(true), + "lorem ipsum dolor", + "lorem ipsum 1231", + true, + ); + + this.testDialog.bodyContainer.append( + this.textfield.container, + this.textfieldWithoutLabel.container, + this.textfieldWithoutValue.container, + this.textfieldDisabled.container, + this.textfieldWithHTML.container, + ); + + // VOTButton + this.votButton = ui.createVOTButton("Translate"); + this.votButton.container.style.left = "20%"; + this.votButtonWithHTML = ui.createVOTButton(this.testHTML.cloneNode(true)); + this.votButtonWithHTML.container.style.marginTop = "100px"; + this.votButtonWithHTML.container.style.left = "20%"; + + // VOTMenu + this.votMenu = ui.createVOTMenu("VOTMenu Lorem ipsum dolor"); + this.votMenu.container.hidden = false; + this.votMenu.container.style.marginTop = "100px"; + this.votMenu.container.style.left = "20%"; + this.votMenuWithHTML = ui.createVOTMenu(this.testHTML.cloneNode(true)); + this.votMenuWithHTML.container.hidden = false; + this.votMenuWithHTML.container.style.marginTop = "250px"; + this.votMenuWithHTML.container.style.left = "20%"; + + // VOTSelectLabel + this.votSelectLabel = ui.createVOTSelectLabel( + "VOTSelectLabel Lorem ipsum dolor", + ); + this.testDialog.bodyContainer.append(this.votSelectLabel); + + // VOTSelect + this.votSelect = ui.createVOTSelect( + "VOTSelect Lorem ipsum dolor", + "VOTSelect Dialog Lorem ipsum dolor", + [ + { + label: "label Lorem ipsum dolor", + value: "value Lorem ipsum dolor", + selected: false, + }, + ], + ); + this.testDialog.bodyContainer.append(this.votSelect.container); + + this.testDialog.container.hidden = false; + document.documentElement.append( + this.testDialog.container, + this.votButton.container, + this.votButtonWithHTML.container, + this.votMenu.container, + this.votMenuWithHTML.container, + ); + } +} + +new TestUI().initUI(); diff --git a/wiki/README.md b/wiki/README.md index 3e352b94..e99fe5f1 100644 --- a/wiki/README.md +++ b/wiki/README.md @@ -2,7 +2,7 @@ Автогенерируемый markdown для Wiki. -Полностью автоматически обновляется, только, список сайтов. Все пути и ограничения должны быть прописаны вручную в `gen-sites.js` +Список поддерживаемых сайтов обновляется полностью автоматически. Все пути и ограничения должны быть прописаны вручную в `gen-sites.js` ### Запуск: diff --git a/wiki/SITES-EN.md b/wiki/SITES-EN.md index 64aa7f6f..dd265a91 100644 --- a/wiki/SITES-EN.md +++ b/wiki/SITES-EN.md @@ -9,8 +9,37 @@ Available (sub)domains: - `(www.)?youtubekids.com` - `m.youtube.com` +Available paths: + +- /watch +- /embed +- /shorts +- /live +- ?v=VIDEO_ID + +Limitations: + +- Doesn't work in the video preview + +## Invidious + +Status: [✅] Working + Available (sub)domains: +- `yewtu.be` +- `yt.artemislena.eu` +- `invidious.flokinet.to` +- `iv.melmac.space` +- `inv.nadeko.net` +- `inv.tux.pizza` +- `invidious.private.coffee` +- `yt.drgnz.club` +- `vid.puffyan.us` +- `invidious.dhusch.de` + +Available paths: + - /watch - /embed - /shorts @@ -20,23 +49,113 @@ Available (sub)domains: Limitations: - Doesn't work in the video preview +- To work, you must completely remove [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) with pages -## Tiktok +## Piped Status: [✅] Working Available (sub)domains: -- `(www.)?tiktok.com` +- `piped.video` +- `piped.tokhmi.xyz` +- `piped.moomoo.me` +- `piped.syncpundit.io` +- `piped.mha.fi` +- `watch.whatever.social` +- `piped.garudalinux.org` +- `efy.piped.pages.dev` +- `watch.leptons.xyz` +- `piped.lunar.icu` +- `yt.dc09.ru` +- `piped.mint.lgbt` +- `il.ax` +- `piped.privacy.com.de` +- `piped.esmailelbob.xyz` +- `piped.projectsegfau.lt` +- `piped.in.projectsegfau.lt` +- `piped.us.projectsegfau.lt` +- `piped.privacydev.net` +- `piped.palveluntarjoaja.eu` +- `piped.smnz.de` +- `piped.adminforge.de` +- `piped.qdi.fi` +- `piped.hostux.net` +- `piped.chauvet.pro` +- `piped.jotoma.de` +- `piped.pfcd.me` +- `piped.frontendfriendly.xyz` + +Available paths: + +- /watch +- /embed +- /shorts +- /live +- ?v=VIDEO_ID + +Limitations: + +- Doesn't work in the video preview +- To work, you must completely remove [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) with pages + +## Vk + +Status: [✅] Working Available (sub)domains: -- /@NICKNAME/video/VIDEO_ID +- `vk.com` +- `vk.ru` +- `www.vk.com` +- `www.vk.ru` +- `m.vk.com` +- `m.vk.ru` + +Available paths: + +- /video-xxxxxxxxx_xxxxxxxxx +- ?z=VIDEO_ID +- /video_ext.php?oid=VIDEO_ID_PART_ONE&id=VIDEO_ID_PART_TWO + +## 9GAG + +Status: [✅] Working + +Available (sub)domains: + +- `9gag.com` + +Available paths: + +- /gag/VIDEO_ID Limitations: - The translation in the feed doesn't work (It only works in open videos) +## Twitch + +Status: [✅] Working + +Available (sub)domains: + +- `m.twitch.tv` +- `(www.)?twitch.tv` +- `clips.twitch.tv` +- `player.twitch.tv` + +Available paths: + +- /videos +- /embed +- /NICKNAME/clip +- ?v=VIDEO_ID (player.twitch.tv) + +Limitations: + +- Translation of live broadcasts is not available + ## Proxitok Status: [✅] Working @@ -50,49 +169,56 @@ Available (sub)domains: - `proxitok.privacydev.net` - `tok.artemislena.eu` - `tok.adminforge.de` -- `tik.hostux.net` - `tt.vern.cc` - `cringe.whatever.social` - `proxitok.lunar.icu` - `proxitok.privacy.com.de` -Available (sub)domains: +Available paths: - /@NICKNAME/video/VIDEO_ID -## Twitch +## Tiktok Status: [✅] Working Available (sub)domains: -- `m.twitch.tv` -- `www.twitch.tv` -- `clips.twitch.tv` -- `player.twitch.tv` +- `(www.)?tiktok.com` -Available (sub)domains: +Available paths: -- /videos -- /embed -- /NICKNAME/clip -- ?v=VIDEO_ID (player.twitch.tv) +- /@NICKNAME/video/VIDEO_ID Limitations: -- Translation of live broadcasts is not available +- The translation in the feed doesn't work (It only works in open videos) -## Xvideos +## Vimeo Status: [✅] Working Available (sub)domains: -- `www.xvideos.com` -- `www.xv-ru.com` +- `player.vimeo.com` +- `vimeo.com` + +Available paths: + +- /CHANNEL_ID/VIDEO_ID +- /VIDEO_ID + +## Xvideos + +Status: [✅] Working Available (sub)domains: +- `(www.)?xvideos.com` +- `(www.)?xv-ru.com` + +Available paths: + - /VIDEO_ID/VIDEO_NAME Limitations: @@ -108,7 +234,7 @@ Available (sub)domains: - `pornhub.com` - `[a-z]+.pornhub.com` -Available (sub)domains: +Available paths: - /view_video.php?viewkey=VIDEO_ID - /embed/VIDEO_ID @@ -117,79 +243,56 @@ Limitations: - Translation is not available for PH Premium -## Vk - -Status: [✅] Working - -Available (sub)domains: +## Twitter -- `vk.com` -- `vk.ru` -- `www.vk.com` -- `www.vk.ru` -- `m.vk.com` -- `m.vk.ru` +Status: [⚠️] Works with limitations Available (sub)domains: -- /video-xxxxxxxxx_xxxxxxxxx -- ?z=VIDEO_ID -- /video_ext.php?oid=VIDEO_ID_PART_ONE&id=VIDEO_ID_PART_TWO - -## Vimeo - -Status: [✅] Working +- `twitter.com` +- `x.com` -Available (sub)domains: +Available paths: -- `player.vimeo.com` -- `vimeo.com` +- /NAME/status/VIDEO_ID -Available (sub)domains: +Limitations: -- /CHANNEL_ID/VIDEO_ID -- /VIDEO_ID +- To work, you need to add a script to the [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) +- The translation in the feed doesn't work (It only works in open videos) -## Ok.ru +## Rumble Status: [✅] Working Available (sub)domains: -- `ok.ru` +- `rumble.com` -Available (sub)domains: +Available paths: -- /video/VIDEO_ID +- /VIDEO_NAME -## 9GAG +Limitations: -Status: [✅] Working +- Translation of live broadcasts is not available -Available (sub)domains: +## Facebook -- `9gag.com` +Status: [⚠️] Works with limitations Available (sub)domains: -- /gag/VIDEO_ID - -Limitations: - -- The translation in the feed doesn't work (It only works in open videos) - -## Bitchute - -Status: [✅] Working +- `facebook.com` -Available (sub)domains: +Available paths: -- `(www.)?bitchute.com` +- /reel/VIDEO_ID +- /videos/VIDEO_ID -Available (sub)domains: +Limitations: -- /video/VIDEO_ID -- /embed/VIDEO_ID +- To work, you need to add a script to the [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) ## Rutube @@ -199,7 +302,7 @@ Available (sub)domains: - `rutube.ru` -Available (sub)domains: +Available paths: - /video/VIDEO_ID - /?bvid=VIDEO_ID @@ -214,41 +317,31 @@ Available (sub)domains: - `m.bilibili.com` - `player.bilibili.com` -Available (sub)domains: +Available paths: - /video/VIDEO_ID - /?bvid=VIDEO_ID -## Twitter - -Status: [⚠️] Works with limitations - -Available (sub)domains: +## Mail.ru -- `twitter.com` +Status: [✅] Working Available (sub)domains: -- /NAME/status/VIDEO_ID - -Limitations: - -- To work, you need to add a script to the [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) -- The translation in the feed doesn't work (It only works in open videos) +- `my.mail.ru` -## Mail.ru +## Bitchute Status: [✅] Working Available (sub)domains: -- `my.mail.ru` +- `(www.)?bitchute.com` -Available (sub)domains: +Available paths: -- /v/NICKNAME/video/... -- /mail/NICKNAME/video/... -- video/embed/VIDEO_ID +- /video/VIDEO_ID +- /embed/VIDEO_ID ## Coursera @@ -258,7 +351,7 @@ Available (sub)domains: - `coursera.org` -Available (sub)domains: +Available paths: - /learn/NAME/lecture/XXXX @@ -275,112 +368,13 @@ Available (sub)domains: - `udemy.com` -Available (sub)domains: +Available paths: - /course/NAME/learn/lecture/LECTURE_ID Limitations: -- To work, you need to [set an Access Token](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-Where-to-get-Udemy-Access-Token%3F) - You must be logged in to the site -- If the video doesn't have subtitles in your language, then the translation will not be performed - -## Invidious - -Status: [✅] Working - -Available (sub)domains: - -- `invidious.snopyta.org` -- `yewtu.be` -- `invidious.kavin.rocks` -- `vid.puffyan.us` -- `invidious.namazso.eu` -- `inv.riverside.rocks` -- `yt.artemislena.eu` -- `invidious.flokinet.to` -- `invidious.esmailelbob.xyz` -- `y.com.sb` -- `invidious.nerdvpn.de` -- `inv.vern.cc` -- `invidious.slipfox.xyz` -- `invidio.xamh.de` -- `invidious.dhusch.de` - -Available (sub)domains: - -- /watch -- /embed -- /shorts -- /live -- ?v=VIDEO_ID - -Limitations: - -- Doesn't work in the video preview - -## Piped - -Status: [✅] Working - -Available (sub)domains: - -- `piped.video` -- `piped.tokhmi.xyz` -- `piped.moomoo.me` -- `piped.syncpundit.io` -- `piped.mha.fi` -- `watch.whatever.social` -- `piped.garudalinux.org` -- `efy.piped.pages.dev` -- `watch.leptons.xyz` -- `piped.lunar.icu` -- `yt.dc09.ru` -- `piped.mint.lgbt` -- `il.ax` -- `piped.privacy.com.de` -- `piped.esmailelbob.xyz` -- `piped.projectsegfau.lt` -- `piped.in.projectsegfau.lt` -- `piped.us.projectsegfau.lt` -- `piped.privacydev.net` -- `piped.palveluntarjoaja.eu` -- `piped.smnz.de` -- `piped.adminforge.de` -- `piped.qdi.fi` -- `piped.hostux.net` -- `piped.chauvet.pro` -- `piped.jotoma.de` -- `piped.pfcd.me` -- `piped.frontendfriendly.xyz` - -Available (sub)domains: - -- /watch -- /embed -- /shorts -- /live -- ?v=VIDEO_ID - -Limitations: - -- Doesn't work in the video preview - -## Rumble - -Status: [✅] Working - -Available (sub)domains: - -- `rumble.com` - -Available (sub)domains: - -- /VIDEO_NAME - -Limitations: - -- Translation of live broadcasts is not available ## Eporner @@ -390,7 +384,7 @@ Available (sub)domains: - `(www.)?eporner.com` -Available (sub)domains: +Available paths: - /video-VIDEO_ID/NAME @@ -410,8 +404,9 @@ Available (sub)domains: - `tube.la-dina.net` - `peertube.tmp.rcp.tf` - `peertube.su` +- `video.blender.org` -Available (sub)domains: +Available paths: - /w/VIDEO_ID @@ -427,7 +422,7 @@ Available (sub)domains: - `geo.dailymotion.com (embedded player, on www.dailymotion.com it works)` -Available (sub)domains: +Available paths: - /video/VIDEO_ID @@ -439,7 +434,7 @@ Available (sub)domains: - `trovo.live` -Available (sub)domains: +Available paths: - /s/NICK/VIDEO_ID @@ -455,7 +450,7 @@ Available (sub)domains: - `disk.yandex.ru` -Available (sub)domains: +Available paths: - /i/FILE_ID @@ -472,10 +467,18 @@ Available (sub)domains: - `coursehunter.net` -Available (sub)domains: +Available paths: - /course/COURSE_ID +## OK.ru + +Status: [✅] Working + +Available (sub)domains: + +- `ok.ru` + ## Google Drive Status: [✅] Working @@ -484,7 +487,7 @@ Available (sub)domains: - `youtube.googleapis.com` -Available (sub)domains: +Available paths: - /file/d/FILE_ID @@ -498,29 +501,12 @@ Status: [✅] Working Available (sub)domains: -- `(www.)?banned.video` +- `(www.)?banned.video|madmaxworld.tv` -Available (sub)domains: +Available paths: - /watch?id=VIDEO_ID -## Facebook - -Status: [⚠️] Works with limitations - -Available (sub)domains: - -- `facebook.com` - -Available (sub)domains: - -- /reel/VIDEO_ID -- /videos/VIDEO_ID - -Limitations: - -- To work, you need to add a script to the [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) - ## Weverse Status: [✅] Working @@ -529,7 +515,7 @@ Available (sub)domains: - `weverse.io` -Available (sub)domains: +Available paths: - /CHANNEL_NAME/media/VIDEO_ID - /CHANNEL_NAME/live/VIDEO_ID @@ -544,9 +530,9 @@ Status: [✅] Working Available (sub)domains: -- `www.newgrounds.com` +- `(www.)?newgrounds.com` -Available (sub)domains: +Available paths: - /XXX/view/XXX @@ -558,7 +544,7 @@ Available (sub)domains: - `egghead.io` -Available (sub)domains: +Available paths: - /lessons/LESSON_NAME @@ -570,7 +556,7 @@ Available (sub)domains: - `v.youku.com` -Available (sub)domains: +Available paths: - /v_show/VIDEO_ID @@ -582,23 +568,84 @@ Available (sub)domains: - `archive.org` -Available (sub)domains: +Available paths: - /details/VIDEO_ID - /embed/VIDEO_ID -## Directlink +## Kodik Status: [✅] Working Available (sub)domains: -- `any` +- `kodik.info` +- `kodik.biz` +- `kodik.cc` + +## Patreon + +Status: [✅] Working Available (sub)domains: -- /\*.mp4 +- `(www.)?patreon.com` + +Available paths: + +- /posts/POST_ID Limitations: -- Local videos cannot be translated +- There are no subtitles + +## Reddit + +Status: [✅] Working + +Available (sub)domains: + +- `(www.)?reddit.com` + +Available paths: + +- /r/SUB_REDDIT/comments/VIDEO_ID/VIDEO_NAME + +Limitations: + +- There are no subtitles +- To work, you must completely remove [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) with pages + +## Kick + +Status: [✅] Working + +Available (sub)domains: + +- `kick.com` + +Available paths: + +- /video/VIDEO_ID +- /NICKNAME?clip=clip_CLIPID + +Limitations: + +- There are no subtitles +- Translation of live broadcasts is not available + +## Apple_developer + +Status: [✅] Working + +Available (sub)domains: + +- `developer.apple.com` + +## Direct link to MP4 + +Status: [✅] Working + +Available (sub)domains: + +- `any` diff --git a/wiki/SITES-RU.md b/wiki/SITES-RU.md index 390824f1..e6ac743a 100644 --- a/wiki/SITES-RU.md +++ b/wiki/SITES-RU.md @@ -9,8 +9,37 @@ - `(www.)?youtubekids.com` - `m.youtube.com` +Доступные пути: + +- /watch +- /embed +- /shorts +- /live +- ?v=VIDEO_ID + +Ограничения: + +- Не работает в предпросмотре видео + +## Invidious + +Статус: [✅] Работает + Доступные (под)домены: +- `yewtu.be` +- `yt.artemislena.eu` +- `invidious.flokinet.to` +- `iv.melmac.space` +- `inv.nadeko.net` +- `inv.tux.pizza` +- `invidious.private.coffee` +- `yt.drgnz.club` +- `vid.puffyan.us` +- `invidious.dhusch.de` + +Доступные пути: + - /watch - /embed - /shorts @@ -20,23 +49,113 @@ Ограничения: - Не работает в предпросмотре видео +- Для работы необходимо полностью удалить [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) со страницы -## Tiktok +## Piped Статус: [✅] Работает Доступные (под)домены: -- `(www.)?tiktok.com` +- `piped.video` +- `piped.tokhmi.xyz` +- `piped.moomoo.me` +- `piped.syncpundit.io` +- `piped.mha.fi` +- `watch.whatever.social` +- `piped.garudalinux.org` +- `efy.piped.pages.dev` +- `watch.leptons.xyz` +- `piped.lunar.icu` +- `yt.dc09.ru` +- `piped.mint.lgbt` +- `il.ax` +- `piped.privacy.com.de` +- `piped.esmailelbob.xyz` +- `piped.projectsegfau.lt` +- `piped.in.projectsegfau.lt` +- `piped.us.projectsegfau.lt` +- `piped.privacydev.net` +- `piped.palveluntarjoaja.eu` +- `piped.smnz.de` +- `piped.adminforge.de` +- `piped.qdi.fi` +- `piped.hostux.net` +- `piped.chauvet.pro` +- `piped.jotoma.de` +- `piped.pfcd.me` +- `piped.frontendfriendly.xyz` + +Доступные пути: + +- /watch +- /embed +- /shorts +- /live +- ?v=VIDEO_ID + +Ограничения: + +- Не работает в предпросмотре видео +- Для работы необходимо полностью удалить [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) со страницы + +## Vk + +Статус: [✅] Работает Доступные (под)домены: -- /@NICKNAME/video/VIDEO_ID +- `vk.com` +- `vk.ru` +- `www.vk.com` +- `www.vk.ru` +- `m.vk.com` +- `m.vk.ru` + +Доступные пути: + +- /video-xxxxxxxxx_xxxxxxxxx +- ?z=VIDEO_ID +- /video_ext.php?oid=VIDEO_ID_PART_ONE&id=VIDEO_ID_PART_TWO + +## 9GAG + +Статус: [✅] Работает + +Доступные (под)домены: + +- `9gag.com` + +Доступные пути: + +- /gag/VIDEO_ID Ограничения: - Не работает перевод в ленте (Работает только в открытых видео) +## Twitch + +Статус: [✅] Работает + +Доступные (под)домены: + +- `m.twitch.tv` +- `(www.)?twitch.tv` +- `clips.twitch.tv` +- `player.twitch.tv` + +Доступные пути: + +- /videos +- /embed +- /NICKNAME/clip +- ?v=VIDEO_ID (player.twitch.tv) + +Ограничения: + +- Не доступен перевод прямых трансляций + ## Proxitok Статус: [✅] Работает @@ -50,49 +169,56 @@ - `proxitok.privacydev.net` - `tok.artemislena.eu` - `tok.adminforge.de` -- `tik.hostux.net` - `tt.vern.cc` - `cringe.whatever.social` - `proxitok.lunar.icu` - `proxitok.privacy.com.de` -Доступные (под)домены: +Доступные пути: - /@NICKNAME/video/VIDEO_ID -## Twitch +## Tiktok Статус: [✅] Работает Доступные (под)домены: -- `m.twitch.tv` -- `www.twitch.tv` -- `clips.twitch.tv` -- `player.twitch.tv` +- `(www.)?tiktok.com` -Доступные (под)домены: +Доступные пути: -- /videos -- /embed -- /NICKNAME/clip -- ?v=VIDEO_ID (player.twitch.tv) +- /@NICKNAME/video/VIDEO_ID Ограничения: -- Не доступен перевод прямых трансляций +- Не работает перевод в ленте (Работает только в открытых видео) -## Xvideos +## Vimeo Статус: [✅] Работает Доступные (под)домены: -- `www.xvideos.com` -- `www.xv-ru.com` +- `player.vimeo.com` +- `vimeo.com` + +Доступные пути: + +- /CHANNEL_ID/VIDEO_ID +- /VIDEO_ID + +## Xvideos + +Статус: [✅] Работает Доступные (под)домены: +- `(www.)?xvideos.com` +- `(www.)?xv-ru.com` + +Доступные пути: + - /VIDEO_ID/VIDEO_NAME Ограничения: @@ -108,7 +234,7 @@ - `pornhub.com` - `[a-z]+.pornhub.com` -Доступные (под)домены: +Доступные пути: - /view_video.php?viewkey=VIDEO_ID - /embed/VIDEO_ID @@ -117,79 +243,56 @@ - Недоступен перевод для PH Premium -## Vk - -Статус: [✅] Работает - -Доступные (под)домены: +## Twitter -- `vk.com` -- `vk.ru` -- `www.vk.com` -- `www.vk.ru` -- `m.vk.com` -- `m.vk.ru` +Статус: [⚠️] Работает с ограничениями Доступные (под)домены: -- /video-xxxxxxxxx_xxxxxxxxx -- ?z=VIDEO_ID -- /video_ext.php?oid=VIDEO_ID_PART_ONE&id=VIDEO_ID_PART_TWO - -## Vimeo - -Статус: [✅] Работает +- `twitter.com` +- `x.com` -Доступные (под)домены: +Доступные пути: -- `player.vimeo.com` -- `vimeo.com` +- /NAME/status/VIDEO_ID -Доступные (под)домены: +Ограничения: -- /CHANNEL_ID/VIDEO_ID -- /VIDEO_ID +- Для работы необходимо добавить скрипт в [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) +- Не работает перевод в ленте (Работает только в открытых видео) -## Ok.ru +## Rumble Статус: [✅] Работает Доступные (под)домены: -- `ok.ru` +- `rumble.com` -Доступные (под)домены: +Доступные пути: -- /video/VIDEO_ID +- /VIDEO_NAME -## 9GAG +Ограничения: -Статус: [✅] Работает +- Не доступен перевод прямых трансляций -Доступные (под)домены: +## Facebook -- `9gag.com` +Статус: [⚠️] Работает с ограничениями Доступные (под)домены: -- /gag/VIDEO_ID - -Ограничения: - -- Не работает перевод в ленте (Работает только в открытых видео) - -## Bitchute - -Статус: [✅] Работает +- `facebook.com` -Доступные (под)домены: +Доступные пути: -- `(www.)?bitchute.com` +- /reel/VIDEO_ID +- /videos/VIDEO_ID -Доступные (под)домены: +Ограничения: -- /video/VIDEO_ID -- /embed/VIDEO_ID +- Для работы необходимо добавить скрипт в [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) ## Rutube @@ -199,7 +302,7 @@ - `rutube.ru` -Доступные (под)домены: +Доступные пути: - /video/VIDEO_ID - /?bvid=VIDEO_ID @@ -214,41 +317,31 @@ - `m.bilibili.com` - `player.bilibili.com` -Доступные (под)домены: +Доступные пути: - /video/VIDEO_ID - /?bvid=VIDEO_ID -## Twitter - -Статус: [⚠️] Работает с ограничениями - -Доступные (под)домены: +## Mail.ru -- `twitter.com` +Статус: [✅] Работает Доступные (под)домены: -- /NAME/status/VIDEO_ID - -Ограничения: - -- Для работы необходимо добавить скрипт в [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) -- Не работает перевод в ленте (Работает только в открытых видео) +- `my.mail.ru` -## Mail.ru +## Bitchute Статус: [✅] Работает Доступные (под)домены: -- `my.mail.ru` +- `(www.)?bitchute.com` -Доступные (под)домены: +Доступные пути: -- /v/NICKNAME/video/... -- /mail/NICKNAME/video/... -- video/embed/VIDEO_ID +- /video/VIDEO_ID +- /embed/VIDEO_ID ## Coursera @@ -258,7 +351,7 @@ - `coursera.org` -Доступные (под)домены: +Доступные пути: - /learn/NAME/lecture/XXXX @@ -275,112 +368,13 @@ - `udemy.com` -Доступные (под)домены: +Доступные пути: - /course/NAME/learn/lecture/LECTURE_ID Ограничения: -- Для работы необходимо [установить Access Token](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-Where-to-get-Udemy-Access-Token%3F) - Необходимо быть авторизованным на сайте -- Если у видео нет субтитров на вашем языке, то перевод не будет выполнен - -## Invidious - -Статус: [✅] Работает - -Доступные (под)домены: - -- `invidious.snopyta.org` -- `yewtu.be` -- `invidious.kavin.rocks` -- `vid.puffyan.us` -- `invidious.namazso.eu` -- `inv.riverside.rocks` -- `yt.artemislena.eu` -- `invidious.flokinet.to` -- `invidious.esmailelbob.xyz` -- `y.com.sb` -- `invidious.nerdvpn.de` -- `inv.vern.cc` -- `invidious.slipfox.xyz` -- `invidio.xamh.de` -- `invidious.dhusch.de` - -Доступные (под)домены: - -- /watch -- /embed -- /shorts -- /live -- ?v=VIDEO_ID - -Ограничения: - -- Не работает в предпросмотре видео - -## Piped - -Статус: [✅] Работает - -Доступные (под)домены: - -- `piped.video` -- `piped.tokhmi.xyz` -- `piped.moomoo.me` -- `piped.syncpundit.io` -- `piped.mha.fi` -- `watch.whatever.social` -- `piped.garudalinux.org` -- `efy.piped.pages.dev` -- `watch.leptons.xyz` -- `piped.lunar.icu` -- `yt.dc09.ru` -- `piped.mint.lgbt` -- `il.ax` -- `piped.privacy.com.de` -- `piped.esmailelbob.xyz` -- `piped.projectsegfau.lt` -- `piped.in.projectsegfau.lt` -- `piped.us.projectsegfau.lt` -- `piped.privacydev.net` -- `piped.palveluntarjoaja.eu` -- `piped.smnz.de` -- `piped.adminforge.de` -- `piped.qdi.fi` -- `piped.hostux.net` -- `piped.chauvet.pro` -- `piped.jotoma.de` -- `piped.pfcd.me` -- `piped.frontendfriendly.xyz` - -Доступные (под)домены: - -- /watch -- /embed -- /shorts -- /live -- ?v=VIDEO_ID - -Ограничения: - -- Не работает в предпросмотре видео - -## Rumble - -Статус: [✅] Работает - -Доступные (под)домены: - -- `rumble.com` - -Доступные (под)домены: - -- /VIDEO_NAME - -Ограничения: - -- Не доступен перевод прямых трансляций ## Eporner @@ -390,7 +384,7 @@ - `(www.)?eporner.com` -Доступные (под)домены: +Доступные пути: - /video-VIDEO_ID/NAME @@ -410,8 +404,9 @@ - `tube.la-dina.net` - `peertube.tmp.rcp.tf` - `peertube.su` +- `video.blender.org` -Доступные (под)домены: +Доступные пути: - /w/VIDEO_ID @@ -427,7 +422,7 @@ - `geo.dailymotion.com (встраиваемый плеер, на www.dailymotion.com работает)` -Доступные (под)домены: +Доступные пути: - /video/VIDEO_ID @@ -439,7 +434,7 @@ - `trovo.live` -Доступные (под)домены: +Доступные пути: - /s/NICK/VIDEO_ID @@ -455,7 +450,7 @@ - `disk.yandex.ru` -Доступные (под)домены: +Доступные пути: - /i/FILE_ID @@ -472,10 +467,18 @@ - `coursehunter.net` -Доступные (под)домены: +Доступные пути: - /course/COURSE_ID +## OK.ru + +Статус: [✅] Работает + +Доступные (под)домены: + +- `ok.ru` + ## Google Drive Статус: [✅] Работает @@ -484,7 +487,7 @@ - `youtube.googleapis.com` -Доступные (под)домены: +Доступные пути: - /file/d/FILE_ID @@ -498,29 +501,12 @@ Доступные (под)домены: -- `(www.)?banned.video` +- `(www.)?banned.video|madmaxworld.tv` -Доступные (под)домены: +Доступные пути: - /watch?id=VIDEO_ID -## Facebook - -Статус: [⚠️] Работает с ограничениями - -Доступные (под)домены: - -- `facebook.com` - -Доступные (под)домены: - -- /reel/VIDEO_ID -- /videos/VIDEO_ID - -Ограничения: - -- Для работы необходимо добавить скрипт в [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) - ## Weverse Статус: [✅] Работает @@ -529,7 +515,7 @@ - `weverse.io` -Доступные (под)домены: +Доступные пути: - /CHANNEL_NAME/media/VIDEO_ID - /CHANNEL_NAME/live/VIDEO_ID @@ -544,9 +530,9 @@ Доступные (под)домены: -- `www.newgrounds.com` +- `(www.)?newgrounds.com` -Доступные (под)домены: +Доступные пути: - /XXX/view/XXX @@ -558,7 +544,7 @@ - `egghead.io` -Доступные (под)домены: +Доступные пути: - /lessons/LESSON_NAME @@ -570,7 +556,7 @@ - `v.youku.com` -Доступные (под)домены: +Доступные пути: - /v_show/VIDEO_ID @@ -582,23 +568,84 @@ - `archive.org` -Доступные (под)домены: +Доступные пути: - /details/VIDEO_ID - /embed/VIDEO_ID -## Directlink +## Kodik Статус: [✅] Работает Доступные (под)домены: -- `any` +- `kodik.info` +- `kodik.biz` +- `kodik.cc` + +## Patreon + +Статус: [✅] Работает Доступные (под)домены: -- /\*.mp4 +- `(www.)?patreon.com` + +Доступные пути: + +- /posts/POST_ID Ограничения: -- Нельзя переводить локальные видео +- Нет субтитров + +## Reddit + +Статус: [✅] Работает + +Доступные (под)домены: + +- `(www.)?reddit.com` + +Доступные пути: + +- /r/SUB_REDDIT/comments/VIDEO_ID/VIDEO_NAME + +Ограничения: + +- Нет субтитров +- Для работы необходимо полностью удалить [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) со страницы + +## Kick + +Статус: [✅] Работает + +Доступные (под)домены: + +- `kick.com` + +Доступные пути: + +- /video/VIDEO_ID +- /NICKNAME?clip=clip_CLIPID + +Ограничения: + +- Нет субтитров +- Не доступен перевод прямых трансляций + +## Apple_developer + +Статус: [✅] Работает + +Доступные (под)домены: + +- `developer.apple.com` + +## Direct link to MP4 + +Статус: [✅] Работает + +Доступные (под)домены: + +- `any` diff --git a/wiki/gen-sites.js b/wiki/gen-sites.js index f916bc80..003c17a3 100644 --- a/wiki/gen-sites.js +++ b/wiki/gen-sites.js @@ -2,7 +2,7 @@ import Bun from "bun"; import * as path from "node:path"; -import sites from "../src/config/sites"; +import sites from "vot.js/sites"; const i18n = { limitations: { @@ -49,6 +49,10 @@ const i18n = { ru: "Для работы необходимо добавить скрипт в [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ)", en: "To work, you need to add a script to the [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ)", }, + needRemoveCSP: { + ru: "Для работы необходимо полностью удалить [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BRU%5D-FAQ) со страницы", + en: "To work, you must completely remove [CSP](https://github.com/ilyhalight/voice-over-translation/wiki/%5BEN%5D-FAQ) with pages", + }, cantTranslatePHPremium: { ru: "Недоступен перевод для PH Premium", en: "Translation is not available for PH Premium", @@ -81,6 +85,10 @@ const i18n = { ru: "Нельзя переводить локальные видео", en: "Local videos cannot be translated", }, + noSubtitles: { + ru: "Нет субтитров", + en: "There are no subtitles", + }, }; const youtubeSiteData = { @@ -88,10 +96,15 @@ const youtubeSiteData = { limits: [i18n.noPreviewVideos], }; +const youtubeAltSiteData = { + paths: youtubeSiteData.paths, + limits: [...youtubeSiteData.limits, i18n.needRemoveCSP], +}; + const siteData = { youtube: youtubeSiteData, - invidious: youtubeSiteData, - piped: youtubeSiteData, + invidious: youtubeAltSiteData, + piped: youtubeAltSiteData, vk: { paths: [ "/video-xxxxxxxxx_xxxxxxxxx", @@ -136,11 +149,7 @@ const siteData = { }, udemy: { paths: ["/course/NAME/learn/lecture/LECTURE_ID"], - limits: [ - i18n.needSetAccessToken, - i18n.needBeLoggedIn, - i18n.videoWithoutSubs, - ], + limits: [i18n.needBeLoggedIn], }, rumble: { paths: ["/VIDEO_NAME"], @@ -217,6 +226,22 @@ const siteData = { archive: { paths: ["/details/VIDEO_ID", "/embed/VIDEO_ID"], }, + patreon: { + paths: ["/posts/POST_ID"], + limits: [i18n.noSubtitles], + }, + reddit: { + paths: ["/r/SUB_REDDIT/comments/VIDEO_ID/VIDEO_NAME"], + limits: [i18n.noSubtitles, i18n.needRemoveCSP], + }, + kick: { + paths: ["/video/VIDEO_ID", "/NICKNAME?clip=clip_CLIPID"], + limits: [i18n.noSubtitles, i18n.noStreams], + }, + apple_developer: { + paths: ["/videos/play/XXX/XXX"], + limits: [i18n.noSubtitles], + }, directlink: { paths: ["/*.mp4"], limits: [i18n.noLocalLinks], @@ -347,7 +372,7 @@ function genMarkdown(sites, lang = "ru") { const pathsData = hasData ? Array.from(siteData[site.host].paths) : []; let paths = ""; if (pathsData.length) { - paths = `\n\n${i18n.availabledDomains[lang]}:\n\n- ${pathsData.join("\n- ")}`; + paths = `\n\n${i18n.availabledPaths[lang]}:\n\n- ${pathsData.join("\n- ")}`; } return `## ${ucFirst(site.host)} @@ -367,7 +392,7 @@ async function main() { return { host, - match: host === "directlink" ? "any" : site.match, + match: host === "custom" ? "any" : site.match, status: extra ? extraData[host].status : "✅", statusPhrase: extra ? extraData[host].statusPhrase : i18n.working, additionalData: site.additionalData, @@ -379,9 +404,11 @@ async function main() { const mdText = genMarkdown(supportedSites, lang) .join("\n\n") .replace("Nine_gag", "9GAG") - .replace("Mail_ru", "Mail.ru") + .replace("Mailru", "Mail.ru") .replace("Yandexdisk", "Yandex Disk") .replace("Googledrive", "Google Drive") + .replace("Okru", "OK.ru") + .replace("Custom", "Direct link to MP4") .replace("Bannedvideo", "Banned.Video") .replace( "geo.dailymotion.com", diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..0dec8701 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,5481 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 +# bun ./bun.lockb --hash: F762E541E0FF6E1C-eae194f429fbde4b-061F686514E6E029-30e8128906d4e96a + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0": + version "4.11.0" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/config-array@^0.17.0": + version "0.17.0" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz" + integrity sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-array@^0.17.1": + version "0.17.1" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz" + integrity sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@eslint/js@9.7.0": + version "9.7.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz" + integrity sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng== + +"@eslint/js@9.8.0": + version "9.8.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz" + integrity sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + +"@github/browserslist-config@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@github/browserslist-config/-/browserslist-config-1.0.0.tgz" + integrity sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz" + integrity sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.23" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz" + integrity sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jsonjoy.com/base64@^1.1.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz" + integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== + +"@jsonjoy.com/json-pack@^1.0.3": + version "1.0.4" + resolved "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz" + integrity sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg== + dependencies: + "@jsonjoy.com/base64" "^1.1.1" + "@jsonjoy.com/util" "^1.1.2" + hyperdyperid "^1.2.0" + thingies "^1.20.0" + +"@jsonjoy.com/util@^1.1.2": + version "1.1.3" + resolved "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.1.3.tgz" + integrity sha512-g//kkF4kOwUjemValCtOc/xiYzmwMRmWq3Bn+YnzOzuZLHq2PpMOxxIayN3cKbo7Ko2Np65t6D9H81IvXbXhqg== + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@lit-labs/ssr-dom-shim@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz" + integrity sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g== + +"@lit/reactive-element@^2.0.4": + version "2.0.4" + resolved "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz" + integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.2.0" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@oxlint/darwin-arm64@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-0.6.1.tgz" + integrity sha512-qnPtdpX4a2wZJYHo/7pO5oHGFfA1t47M+gN9d/ds2+C7UzfZyp1kgE6cjeBidnkpSnFWUPfOmuoOOrFs8Ig+Sw== + +"@oxlint/darwin-x64@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/darwin-x64/-/darwin-x64-0.6.1.tgz" + integrity sha512-/M126TTjEfzNRwYgopJ1qMxbJMUzzrMY64PjbXTID7ig7nLyxcMSAp454Znf8Sh/3uulPODynmkCdkReiGqPvw== + +"@oxlint/linux-arm64-gnu@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/linux-arm64-gnu/-/linux-arm64-gnu-0.6.1.tgz" + integrity sha512-120Qbek2yJz59iIzS0bwoM2717UTd6MRggDBgRw70rN0zdzkAZhORSywuC+EPq8U6ltIqYeMfcl+sGq7ZyhPyg== + +"@oxlint/linux-arm64-musl@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/linux-arm64-musl/-/linux-arm64-musl-0.6.1.tgz" + integrity sha512-wT0T+YOW8Ng2WGcLnWCylEpHwUDRun2HomTNslRed4RQzTWOisyp6hNB8sS6JZlRGYoCGUePgwBy7GM2P0IQYw== + +"@oxlint/linux-x64-gnu@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/linux-x64-gnu/-/linux-x64-gnu-0.6.1.tgz" + integrity sha512-4qUcfTDXGOrm3/ohiCG09ZvjpCZImg1yI/IaOo25ij9VXLoaWUYJ6vbxY1GqPQegqZhli7fbFXQ5FP6AwjhBtQ== + +"@oxlint/linux-x64-musl@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/linux-x64-musl/-/linux-x64-musl-0.6.1.tgz" + integrity sha512-v0O46xwi0EW+J3QxIZAU1h+2voIsq+BZc6/mPKzkSlnyYSPECBDFOG6IJF/rf7qzsjvIeFqdYB+sGGCxoxM5mg== + +"@oxlint/win32-arm64@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/win32-arm64/-/win32-arm64-0.6.1.tgz" + integrity sha512-51NQgkd/XDQzAvFHKanjhFyKoPXvomNBHJnQF3iEYKmAGT2+VOOF9N6ism/FTLn+xGdzLBVU51xwRlmpCXt3kQ== + +"@oxlint/win32-x64@0.6.1": + version "0.6.1" + resolved "https://registry.npmjs.org/@oxlint/win32-x64/-/win32-x64-0.6.1.tgz" + integrity sha512-T8uLmIxbs1X2wbiZzIjxHJgtYEOGl7d+wZTzj701JsKHsY8DWtOCvMMgmd3EoO0XX8PPxpJYdZDWupdiIlYQ4Q== + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@types/body-parser@*": + version "1.19.4" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz" + integrity sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.13": + version "3.5.13" + resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect@*": + version "3.4.37" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz" + integrity sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.5.4": + version "1.5.4" + resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/eslint@*", "@types/eslint@^8.56.10": + version "8.56.10" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz" + integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.6" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.6.tgz" + integrity sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.17.21": + version "4.17.21" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.39" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz" + integrity sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/http-errors@*": + version "2.0.3" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz" + integrity sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA== + +"@types/http-proxy@^1.17.8": + version "1.17.13" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.13.tgz" + integrity sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.5" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz" + integrity sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ== + +"@types/istanbul-lib-report@*": + version "3.0.2" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz" + integrity sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.3" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz" + integrity sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.14" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz" + integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/mime@*", "@types/mime@^1": + version "1.3.4" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz" + integrity sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw== + +"@types/node@*": + version "20.8.9" + resolved "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz" + integrity sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg== + dependencies: + undici-types "~5.26.4" + +"@types/node@>=13.7.0", "@types/node@~20.12.8": + version "20.12.14" + resolved "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz" + integrity sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg== + dependencies: + undici-types "~5.26.4" + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + +"@types/qs@*": + version "6.9.9" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz" + integrity sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg== + +"@types/range-parser@*": + version "1.2.6" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz" + integrity sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA== + +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== + +"@types/send@*": + version "0.17.3" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz" + integrity sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.4": + version "1.9.4" + resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.15.5": + version "1.15.5" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz" + integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.36": + version "0.3.36" + resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/trusted-types@^2.0.2": + version "2.0.7" + resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +"@types/ws@^8.5.10", "@types/ws@~8.5.10": + version "8.5.10" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + +"@types/yargs@^17.0.8": + version "17.0.29" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz" + integrity sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs-parser@*": + version "21.0.2" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz" + integrity sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw== + +"@typescript-eslint/eslint-plugin@^7.0.1": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz" + integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw== + dependencies: + ignore "^5.3.1" + graphemer "^1.4.0" + ts-api-utils "^1.3.0" + natural-compare "^1.4.0" + "@typescript-eslint/utils" "7.18.0" + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/type-utils" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/scope-manager" "7.18.0" + +"@typescript-eslint/parser@^7.0.0", "@typescript-eslint/parser@^7.0.1": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz" + integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== + dependencies: + debug "^4.3.4" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + +"@typescript-eslint/scope-manager@7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz" + integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA== + dependencies: + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + +"@typescript-eslint/type-utils@7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz" + integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA== + dependencies: + debug "^4.3.4" + ts-api-utils "^1.3.0" + "@typescript-eslint/utils" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + +"@typescript-eslint/types@7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz" + integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== + +"@typescript-eslint/typescript-estree@7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz" + integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA== + dependencies: + debug "^4.3.4" + globby "^11.1.0" + semver "^7.6.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + ts-api-utils "^1.3.0" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + +"@typescript-eslint/utils@7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz" + integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw== + dependencies: + "@typescript-eslint/types" "7.18.0" + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + +"@typescript-eslint/visitor-keys@7.18.0": + version "7.18.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz" + integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg== + dependencies: + eslint-visitor-keys "^3.4.3" + "@typescript-eslint/types" "7.18.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +acorn@^8.12.0, acorn@^8.7.1: + version "8.12.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.8.2, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@^8.0.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ansi-escapes@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz" + integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== + dependencies: + environment "^1.0.0" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +ansi-styles@^6.0.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@~5.1.3: + version "5.1.3" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-includes@^3.1.6, array-includes@^3.1.7, array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlastindex@^1.2.3: + version "1.2.5" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +ast-types-flow@^0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz" + integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axe-core@^4.9.1: + version "4.10.0" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz" + integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== + +axobject-query@~3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== + dependencies: + deep-equal "^2.0.5" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-extension-url-match@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/browser-extension-url-match/-/browser-extension-url-match-0.3.3.tgz" + integrity sha512-x7uphFW6fxHF8/RfNqUQomZL4KsKgNb3f8ZrLsYsxSVTaOyeg9I3w/8iuQ2I51vVmrtiYiv37Tfq5gQyMN3j2Q== + dependencies: + fancy-regex "^0.4.3" + +"browserslist@>= 4.21.0", browserslist@^4.21.10: + version "4.22.1" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +"browserslist@>= 4.21.0", browserslist@^4.23.1: + version "4.23.2" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz" + integrity sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA== + dependencies: + caniuse-lite "^1.0.30001640" + electron-to-chromium "^1.4.820" + node-releases "^2.0.14" + update-browserslist-db "^1.1.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bun-types@^1.1.20: + version "1.1.21" + resolved "https://registry.npmjs.org/bun-types/-/bun-types-1.1.21.tgz" + integrity sha512-oz9q2KGiuQZciV8oBCYfTTwJMWEFFYBF0gOyO0dsV06yQvJIiFMcUx5PDk580UJ2Po5amtwjgRJ8ksxSiCueHQ== + dependencies: + "@types/ws" "~8.5.10" + "@types/node" "~20.12.8" + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001541: + version "1.0.30001554" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz" + integrity sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ== + +caniuse-lite@^1.0.30001640: + version "1.0.30001645" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001645.tgz" + integrity sha512-GFtY2+qt91kzyMk6j48dJcwJVq5uTkk71XxE3RtScx7XWRLsO7bU44LOFkOZYR8w9YMS0UhPSYpN/6rAMImmLw== + +chalk@^4.0.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@~5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^3.2.0, ci-info@^3.7.0: + version "3.9.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== + dependencies: + restore-cursor "^5.0.0" + +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + dependencies: + slice-ansi "^5.0.0" + string-width "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.0, colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@~12.1.0: + version "12.1.0" + resolved "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-loader@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz" + integrity sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@~4.3.6: + version "4.3.6" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-parser@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/dom-parser/-/dom-parser-1.1.5.tgz" + integrity sha512-lCiFG48ZUzGXjKN0qhSkxD/i3ndyV6I37zQ3W2VFYLjF1ob8A+QgSsM7Ps2UT0d3LpJxLMmMHiJJ34z5hkKLiA== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.535: + version "1.4.567" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.567.tgz" + integrity sha512-8KR114CAYQ4/r5EIEsOmOMqQ9j0MRbJZR3aXD/KFA8RuKzyoUB4XrUCg+l8RUGqTVQgKNIgTpjaG8YHRPAbX2w== + +electron-to-chromium@^1.4.820: + version "1.5.4" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz" + integrity sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +enhanced-resolve@^5.16.0: + version "5.16.1" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz" + integrity sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enhanced-resolve@^5.17.0: + version "5.17.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz" + integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.10.0" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz" + integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== + +environment@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz" + integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== + +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: + version "1.23.3" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.19: + version "1.0.19" + resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz" + integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.2" + safe-array-concat "^1.1.2" + +es-module-lexer@^1.2.1: + version "1.3.1" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz" + integrity sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +"eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.0.0 || ^9.0.0", eslint@^8.0.1, eslint@^8.56.0: + version "8.57.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +"eslint@^8.0.0 || ^9.0.0": + version "9.7.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz" + integrity sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw== + dependencies: + ajv "^6.12.4" + levn "^0.4.1" + chalk "^4.0.0" + debug "^4.3.2" + espree "^10.1.0" + ignore "^5.2.0" + esquery "^1.5.0" + esutils "^2.0.2" + find-up "^5.0.0" + is-glob "^4.0.0" + minimatch "^3.1.2" + "@eslint/js" "9.7.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + cross-spawn "^7.0.2" + glob-parent "^6.0.2" + imurmurhash "^0.1.4" + eslint-scope "^8.0.2" + lodash.merge "^4.6.2" + is-path-inside "^3.0.3" + fast-deep-equal "^3.1.3" + natural-compare "^1.4.0" + "@eslint/eslintrc" "^3.1.0" + "@nodelib/fs.walk" "^1.2.8" + file-entry-cache "^8.0.0" + eslint-visitor-keys "^4.0.0" + "@eslint/config-array" "^0.17.0" + "@humanwhocodes/retry" "^0.3.0" + escape-string-regexp "^4.0.0" + "@eslint-community/regexpp" "^4.11.0" + "@eslint-community/eslint-utils" "^4.2.0" + "@humanwhocodes/module-importer" "^1.0.1" + json-stable-stringify-without-jsonify "^1.0.1" + +eslint@*, eslint@>=4.19.1, eslint@>=5.0.0, eslint@>=5.14.1, eslint@>=7.0.0, eslint@>=8.0.0, eslint@^9.7.0: + version "9.8.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz" + integrity sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.11.0" + "@eslint/config-array" "^0.17.1" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.8.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.0.2" + eslint-visitor-keys "^4.0.0" + espree "^10.1.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +eslint-config-prettier@*, eslint-config-prettier@>=8.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.8.0: + version "2.8.1" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz" + integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== + dependencies: + debug "^3.2.7" + +eslint-plugin-escompat@^3.3.3: + version "3.11.0" + resolved "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.11.0.tgz" + integrity sha512-kSTb1wxBRW4aL43Yu23Ula5lSFd9KVVwxyZ4zkG2feBFoj/o4mmgqkN12DXYv3VclZ559ePpBG6b9UjAeYeUyA== + dependencies: + browserslist "^4.23.1" + +eslint-plugin-eslint-comments@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz" + integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== + dependencies: + escape-string-regexp "^1.0.5" + ignore "^5.0.5" + +eslint-plugin-filenames@^1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz" + integrity sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w== + dependencies: + lodash.camelcase "4.3.0" + lodash.kebabcase "4.1.1" + lodash.snakecase "4.1.1" + lodash.upperfirst "4.3.1" + +eslint-plugin-github@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-5.0.1.tgz" + integrity sha512-qbXG3wL5Uh2JB92EKeX2hPtO9c/t75qVxQjVLYuTFfhHifLZzv9CBvLCvoaBhLrAC/xTMVht7DK/NofYK8X4Dg== + dependencies: + "@github/browserslist-config" "^1.0.0" + "@typescript-eslint/eslint-plugin" "^7.0.1" + "@typescript-eslint/parser" "^7.0.1" + aria-query "^5.3.0" + eslint-config-prettier ">=8.0.0" + eslint-plugin-escompat "^3.3.3" + eslint-plugin-eslint-comments "^3.2.0" + eslint-plugin-filenames "^1.3.2" + eslint-plugin-i18n-text "^1.0.1" + eslint-plugin-import "^2.25.2" + eslint-plugin-jsx-a11y "^6.7.1" + eslint-plugin-no-only-tests "^3.0.0" + eslint-plugin-prettier "^5.0.0" + eslint-rule-documentation ">=1.0.0" + jsx-ast-utils "^3.3.2" + prettier "^3.0.0" + svg-element-attributes "^1.3.1" + +eslint-plugin-i18n-text@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz" + integrity sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA== + +eslint-plugin-import@^2.25.2: + version "2.29.1" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-jsx-a11y@^6.7.1: + version "6.9.0" + resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz" + integrity sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g== + dependencies: + aria-query "~5.1.3" + array-includes "^3.1.8" + array.prototype.flatmap "^1.3.2" + ast-types-flow "^0.0.8" + axe-core "^4.9.1" + axobject-query "~3.1.1" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + es-iterator-helpers "^1.0.19" + hasown "^2.0.2" + jsx-ast-utils "^3.3.5" + language-tags "^1.0.9" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + safe-regex-test "^1.0.3" + string.prototype.includes "^2.0.0" + +eslint-plugin-no-only-tests@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.1.0.tgz" + integrity sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw== + +eslint-plugin-oxlint@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/eslint-plugin-oxlint/-/eslint-plugin-oxlint-0.5.0.tgz" + integrity sha512-DFPxJTUSe8XdvbOsvpAyzUVNmLHnqpNLQFAH2bJo+Mxkb59s50/EKn1nCukrzMAG34nq02Iu4D+0MuUr9oHWhw== + dependencies: + scule "^1.3.0" + +eslint-plugin-prettier@^5.0.0: + version "5.2.1" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz" + integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.9.1" + +eslint-plugin-sonarjs@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-1.0.4.tgz" + integrity sha512-jF0eGCUsq/HzMub4ExAyD8x1oEgjOyB9XVytYGyWgSFvdiJQJp6IuP7RmtauCf06o6N/kZErh+zW4b10y1WZ+Q== + +eslint-rule-documentation@>=1.0.0: + version "1.0.23" + resolved "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz" + integrity sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-scope@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz" + integrity sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + +eslint-webpack-plugin@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.2.0.tgz" + integrity sha512-rsfpFQ01AWQbqtjgPRr2usVRxhWDuG0YDYcG8DJOteD3EFnpeuYuOwk0PQiN7PRBTqS6ElNdtPZPggj8If9WnA== + dependencies: + "@types/eslint" "^8.56.10" + jest-worker "^29.7.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + schema-utils "^4.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +espree@^10.0.1, espree@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz" + integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== + dependencies: + acorn "^8.12.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.0.0" + +esquery@^1.4.2, esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@~8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fancy-regex@^0.4.3: + version "0.4.3" + resolved "https://registry.npmjs.org/fancy-regex/-/fancy-regex-0.4.3.tgz" + integrity sha512-c9R6Q7cM8pX5i0ZF3sAjYWPFsjGOUthPWPnkuZgDP8i6bzRtTnSXX5PJeWc34w/C0tbGj/bO3GSmdnEEj4K5PQ== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flat-cache@^3.0.4: + version "3.1.1" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz" + integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +follow-redirects@^1.0.0: + version "1.15.5" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^10.3.7: + version "10.3.10" + resolved "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz" + integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.5" + minimatch "^9.0.1" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-scurry "^1.10.1" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + slash "^3.0.0" + ignore "^5.2.0" + merge2 "^1.4.1" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + array-union "^2.1.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz" + integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +husky@^9.1.1: + version "9.1.4" + resolved "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz" + integrity sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA== + +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.0.5, ignore@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +immutable@^4.0.0: + version "4.3.4" + resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +internal-slot@^1.0.4, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz" + integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== + dependencies: + get-east-asian-width "^1.0.0" + +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-map@^2.0.2, is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-network-error@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/is-network-error/-/is-network-error-1.0.1.tgz" + integrity sha512-OwQXkwBJeESyhFw+OumbJVD58BFBJJI5OM5S1+eyrDKlgDZPX2XNT5gXS56GSD3NPbbwUuMlR1Q71SRp5SobuQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2, is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + +jackspeak@^2.3.5: + version "2.3.6" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-parse-even-better-errors@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz" + integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify@^1.0.2: + version "1.1.1" + resolved "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + +jsx-ast-utils@^3.3.2, jsx-ast-utils@^3.3.5: + version "3.3.5" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keyv@^4.5.3, keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + +language-subtag-registry@^0.3.20: + version "0.3.23" + resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== + +language-tags@^1.0.9: + version "1.0.9" + resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz" + integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== + dependencies: + language-subtag-registry "^0.3.20" + +launch-editor@^2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz" + integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@~3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== + +lint-staged@^15.2.7: + version "15.2.8" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.8.tgz" + integrity sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ== + dependencies: + chalk "~5.3.0" + commander "~12.1.0" + debug "~4.3.6" + execa "~8.0.1" + lilconfig "~3.1.2" + listr2 "~8.2.4" + micromatch "~4.0.7" + pidtree "~0.6.0" + string-argv "~0.3.2" + yaml "~2.5.0" + +listr2@~8.2.4: + version "8.2.4" + resolved "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz" + integrity sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g== + dependencies: + cli-truncate "^4.0.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^6.1.0" + rfdc "^1.4.1" + wrap-ansi "^9.0.0" + +lit@^3.1.4: + version "3.2.0" + resolved "https://registry.npmjs.org/lit/-/lit-3.2.0.tgz" + integrity sha512-s6tI33Lf6VpDu7u4YqsSX78D28bYQulM+VAzsGch4fx2H0eLZnJsUBsPWmGYSGoKDNbjtRv02rio1o+UdPVwvw== + dependencies: + "@lit/reactive-element" "^2.0.4" + lit-element "^4.1.0" + lit-html "^3.2.0" + +lit-element@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/lit-element/-/lit-element-4.1.0.tgz" + integrity sha512-gSejRUQJuMQjV2Z59KAS/D4iElUhwKpIyJvZ9w+DIagIQjfJnhR20h2Q5ddpzXGS+fF0tMZ/xEYGMnKmaI/iww== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.2.0" + "@lit/reactive-element" "^2.0.4" + lit-html "^3.2.0" + +lit-html@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/lit-html/-/lit-html-3.2.0.tgz" + integrity sha512-pwT/HwoxqI9FggTrYVarkBKFN9MlTUpLrDHubTmW4SrkL3kkqW5gxwbxMMUnbbRHBC0WTZnYHcjDSCM559VyfA== + dependencies: + "@types/trusted-types" "^2.0.2" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lodash.camelcase@4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.kebabcase@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.snakecase@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.upperfirst@4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +log-update@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz" + integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== + dependencies: + ansi-escapes "^7.0.0" + cli-cursor "^5.0.0" + slice-ansi "^7.1.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + +long@^5.0.0: + version "5.2.3" + resolved "https://registry.npmjs.org/long/-/long-5.2.3.tgz" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +"lru-cache@^9.1.1 || ^10.0.0": + version "10.2.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^4.6.0: + version "4.9.2" + resolved "https://registry.npmjs.org/memfs/-/memfs-4.9.2.tgz" + integrity sha512-f16coDZlTG1jskq3mxarwB+fGRrd0uXWt+o1WIhRfOwbXQZqUDsTVxQBFK9JjRQHblg8eAG2JSbprDXKjc7ijQ== + dependencies: + "@jsonjoy.com/json-pack" "^1.0.3" + "@jsonjoy.com/util" "^1.1.2" + sonic-forest "^1.0.0" + tslib "^2.0.0" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@~4.0.7: + version "4.0.7" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.0, minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": + version "7.0.4" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-forge@^1: + version "1.3.1" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +node-releases@^2.0.14: + version "2.0.18" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== + +npm-run-all2@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.2.2.tgz" + integrity sha512-Q+alQAGIW7ZhKcxLt8GcSi3h3ryheD6xnmXahkMRVM5LYmajcUrSITm8h+OPC9RYWMV2GR0Q1ntTUCfxaNoOJw== + dependencies: + ansi-styles "^6.2.1" + cross-spawn "^7.0.3" + memorystream "^0.3.1" + minimatch "^9.0.0" + pidtree "^0.6.0" + read-package-json-fast "^3.0.2" + shell-quote "^1.7.3" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4, object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.fromentries@^2.0.7, object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.1.6, object.values@^1.1.7: + version "1.2.0" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1, on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + +open@^7.4.2: + version "7.4.2" + resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +open@^10.0.3: + version "10.0.3" + resolved "https://registry.npmjs.org/open/-/open-10.0.3.tgz" + integrity sha512-dtbI5oW7987hwC9qjJTyABldTaa19SuyJse1QboWv3b0qCcrrLNVDqBx1XgELAjh9QTVQaP/C5b1nhQebd1H2A== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +oxlint@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/oxlint/-/oxlint-0.6.1.tgz" + integrity sha512-V66/akd9Gu2+KE6zP/QNTqlmBChL2Q9tPYR9CguKML1SaR/URXmgpvx+0ZfFIA+pDz/VzOOM0w79ozfakbW82w== + optionalDependencies: + "@oxlint/win32-x64" "0.6.1" + "@oxlint/win32-arm64" "0.6.1" + "@oxlint/linux-x64-gnu" "0.6.1" + "@oxlint/linux-arm64-gnu" "0.6.1" + "@oxlint/linux-x64-musl" "0.6.1" + "@oxlint/linux-arm64-musl" "0.6.1" + "@oxlint/darwin-x64" "0.6.1" + "@oxlint/darwin-arm64" "0.6.1" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-retry@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz" + integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== + dependencies: + "@types/retry" "0.12.2" + is-network-error "^1.0.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +patch-package@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz" + integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" + find-yarn-workspace-root "^2.0.0" + fs-extra "^9.0.0" + json-stable-stringify "^1.0.2" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + rimraf "^2.6.3" + semver "^7.5.3" + slash "^2.0.0" + tmp "^0.0.33" + yaml "^2.2.2" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.1: + version "1.10.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz" + integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== + dependencies: + lru-cache "^9.1.1 || ^10.0.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss@^8.1.0, postcss@^8.4.33: + version "8.4.35" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz" + integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + +postcss-modules-local-by-default@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.16" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz" + integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@>=3.0.0, prettier@^3.0.0: + version "3.3.3" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +protobufjs@^7.3.2: + version "7.3.2" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz" + integrity sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +read-package-json-fast@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== + dependencies: + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^3.0.0" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +reflect.getprototypeof@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz" + integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +requestidlecallback-polyfill@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/requestidlecallback-polyfill/-/requestidlecallback-polyfill-1.0.2.tgz" + integrity sha512-zzkRzvMe7UdV0M7AIU70vl2fh4rFnNYDL8U0ISwWiOX/5MowBV1ESYCWSQP/KsgJNUOC/AS6X3DApOmxoyE6MA== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve@^1.20.0, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== + dependencies: + onetime "^7.0.0" + signal-exit "^4.1.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@^5.0.5: + version "5.0.5" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz" + integrity sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A== + dependencies: + glob "^10.3.7" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sass@^1.77.8: + version "1.77.8" + resolved "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz" + integrity sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +sass-loader@16.0.0: + version "16.0.0" + resolved "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz" + integrity sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw== + dependencies: + neo-async "^2.6.2" + +schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0, schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +scule@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz" + integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g== + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +semver@^7.6.0: + version "7.6.3" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.7.3, shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +slice-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== + dependencies: + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +sonic-forest@^1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/sonic-forest/-/sonic-forest-1.0.3.tgz" + integrity sha512-dtwajos6IWMEWXdEbW1IkEkyL2gztCAgDplRIX+OT5aRKnEd5e7r7YCxRgXZdhRP1FBdOBf8axeTPhzDv8T4wQ== + dependencies: + tree-dump "^1.0.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +string-argv@~0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz" + integrity sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string-width@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz" + integrity sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string.prototype.includes@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz" + integrity sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +"strip-ansi@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz" + integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-element-attributes@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.1.tgz" + integrity sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA== + +synckit@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz" + integrity sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A== + dependencies: + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser@^5.26.0: + version "5.28.1" + resolved "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz" + integrity sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +terser-webpack-plugin@^5.0.0, terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thingies@^1.20.0: + version "1.21.0" + resolved "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz" + integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tree-dump@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.1.tgz" + integrity sha512-WCkcRBVPSlHHq1dc/px9iOfqklvzCbdRwvlNfxGZsrHqf6aZttfPrd7DJTt6oR10dwUfpFFQeVTkPbBIZxX/YA== + +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + "@types/json5" "^0.0.29" + +tslib@2, tslib@^2: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@^2.6.2, tslib@^2.6.3: + version "2.6.3" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +type-check@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typescript@>=4.2.0, typescript@^5.0.0: + version "5.5.4" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vot.js@^0.7.2: + version "0.7.2" + resolved "https://registry.npmjs.org/vot.js/-/vot.js-0.7.2.tgz" + integrity sha512-AoLd6ddDl4U93LUPY97lurg22FDzMpMSyPuikN/8IYCseCQublcZV+6BKCrmlH0d+HhtSe3pXhEcBCXjlptZHQ== + dependencies: + dom-parser "^1.1.5" + protobufjs "^7.3.2" + +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webpack@5.x.x, webpack@^5.0.0, webpack@^5.1.0, webpack@^5.27.0: + version "5.91.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz" + integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.16.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +webpack@^5.0.0, webpack@^5.27.0: + version "5.92.1" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz" + integrity sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +webpack@^5.93.0: + version "5.93.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz" + integrity sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA== + dependencies: + acorn "^8.7.1" + events "^3.2.0" + tapable "^2.1.1" + neo-async "^2.6.2" + watchpack "^2.4.1" + mime-types "^2.1.27" + graceful-fs "^4.2.11" + browserslist "^4.21.10" + eslint-scope "5.1.1" + schema-utils "^3.2.0" + "@types/estree" "^1.0.5" + loader-runner "^4.2.0" + glob-to-regexp "^0.4.1" + es-module-lexer "^1.2.1" + webpack-sources "^3.2.3" + enhanced-resolve "^5.17.0" + "@webassemblyjs/ast" "^1.12.1" + chrome-trace-event "^1.0.2" + "@types/eslint-scope" "^3.7.3" + terser-webpack-plugin "^5.3.10" + acorn-import-attributes "^1.9.5" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + json-parse-even-better-errors "^2.3.1" + +webpack-cli@5.x.x, webpack-cli@^5.1.4: + version "5.1.4" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^7.1.0: + version "7.2.1" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz" + integrity sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA== + dependencies: + colorette "^2.0.10" + memfs "^4.6.0" + mime-types "^2.1.31" + on-finished "^2.4.1" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^5.0.0, webpack-dev-server@^5.0.4: + version "5.0.4" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz" + integrity sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" + ansi-html-community "^0.0.8" + bonjour-service "^1.2.1" + chokidar "^3.6.0" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.4.0" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + rimraf "^5.0.5" + schema-utils "^4.2.0" + selfsigned "^2.4.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^7.1.0" + ws "^8.16.0" + +webpack-merge@^5.7.3, webpack-merge@^5.9.0: + version "5.10.0" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-monkey@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/webpack-monkey/-/webpack-monkey-0.2.1.tgz" + integrity sha512-BFhOyl/2DGE9HzlOzTlqM8GUhb6CDsNY4d8lnhM85DQzzDV+USijD/Y+BKEP//I5IxRedBUTdSZBUnp12CighQ== + dependencies: + browser-extension-url-match "^0.3.3" + lodash "^4.17.21" + mitt "^3.0.1" + webpack-merge "^5.9.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.4" + resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz" + integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== + dependencies: + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.2" + which-typed-array "^1.1.15" + +which-collection@^1.0.1, which-collection@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== + dependencies: + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" + +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +"wrap-ansi@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.2.2: + version "2.4.5" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz" + integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== + +yaml@~2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz" + integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==