diff --git a/client/.browserslistrc b/client/.browserslistrc index 9eb6a61de7c..cdc51d7ada5 100644 --- a/client/.browserslistrc +++ b/client/.browserslistrc @@ -1,4 +1,4 @@ last 1 Chrome version last 2 Edge major versions Firefox ESR -ios_saf >= 13.1 +ios_saf >= 14 diff --git a/client/.eslintrc.json b/client/.eslintrc.json index d00e6e7abbc..709aa79d1a8 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -163,7 +163,8 @@ "@typescript-eslint/unbound-method": [ "error", { "ignoreStatic": true } - ] + ], + "import/no-named-default": "off" } }, { diff --git a/client/.gitignore b/client/.gitignore index 6523ad810a5..04c0e93f135 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -12,6 +12,8 @@ /e2e/local.log /e2e/browserstack.err /e2e/screenshots +/src/standalone/player/build +/src/standalone/player/dist /src/standalone/embed-player-api/build /src/standalone/embed-player-api/dist /e2e/logs diff --git a/client/angular.json b/client/angular.json index 82c4ad21704..8c0d6f14481 100644 --- a/client/angular.json +++ b/client/angular.json @@ -183,7 +183,10 @@ "includePaths": [ "src/sass/include", "." - ] + ], + "sass": { + "silenceDeprecations": [ "import", "mixed-decls", "color-functions", "global-builtin" ] + } }, "assets": [ "src/assets/images", @@ -212,7 +215,9 @@ "is-plain-object", "parse-srcset", "deepmerge", - "core-js/features/reflect" + "core-js/features/reflect", + "hammerjs", + "jschannel" ], "scripts": [], "extractLicenses": false, @@ -241,7 +246,7 @@ { "type": "anyComponentStyle", "maximumWarning": "6kb", - "maximumError": "100kb" + "maximumError": "120kb" } ], "fileReplacements": [ diff --git a/client/e2e/tsconfig.json b/client/e2e/tsconfig.json index af776ebd32e..f5958a4b236 100644 --- a/client/e2e/tsconfig.json +++ b/client/e2e/tsconfig.json @@ -5,7 +5,7 @@ "noImplicitAny": false, "esModuleInterop": true, "module": "commonjs", - "target": "ES2015", + "target": "ES2018", "typeRoots": [ "../node_modules/@types", "../node_modules" diff --git a/client/package.json b/client/package.json index ae3b47d645b..d9fce706bc7 100644 --- a/client/package.json +++ b/client/package.json @@ -24,10 +24,13 @@ "net": false, "stream": false, "os": false, + "http": false, + "dgram": false, "util": false }, "workspaces": [ - "../packages/*" + "../packages/*", + "./src/standalone/player" ], "typings": "*.d.ts", "devDependencies": { @@ -55,8 +58,6 @@ "@ngx-loading-bar/http-client": "^7.0.0", "@ngx-loading-bar/router": "^7.0.0", "@peertube/maildev": "^1.2.0", - "@peertube/p2p-media-loader-core": "^1.0.20", - "@peertube/p2p-media-loader-hlsjs": "^1.0.20", "@peertube/xliffmerge": "^2.0.3", "@plussub/srt-vtt-parser": "^2.0.5", "@popperjs/core": "^2.11.5", @@ -103,8 +104,11 @@ "markdown-it": "14.1.0", "markdown-it-emoji": "^3.0.0", "ngx-uploadx": "^7.0.0", + "p2p-media-loader-core": "^2.1.2", + "p2p-media-loader-hlsjs": "^2.1.2", "primeng": "^17", "rxjs": "^7.3.0", + "sass-embedded": "^1.83.4", "sha.js": "^2.4.11", "socket.io-client": "^4.5.4", "stylelint": "^16.2.1", @@ -115,9 +119,9 @@ "tslib": "^2.4.0", "typescript": "~5.7.3", "video.js": "^7.19.2", - "vite": "^5.3.1", - "vite-plugin-checker": "^0.7.2", - "vite-plugin-node-polyfills": "^0.22.0", + "vite": "^6.0.11", + "vite-plugin-checker": "^0.8.0", + "vite-plugin-node-polyfills": "^0.23.0", "zone.js": "~0.15.0" }, "dependencies": {} diff --git a/client/src/app/+videos/+video-watch/player-styles.component.scss b/client/src/app/+videos/+video-watch/player-styles.component.scss index c0befc10a74..d2b386a5b47 100644 --- a/client/src/app/+videos/+video-watch/player-styles.component.scss +++ b/client/src/app/+videos/+video-watch/player-styles.component.scss @@ -1,5 +1 @@ -@use 'node_modules/video.js/dist/video-js'; - -$assets-path: '../../assets/'; - -@use '../../../sass/player/index'; +@use '../../../standalone/player/build/peertube-player.css'; diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index ca49564396d..6f4c3fa65c6 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts @@ -47,16 +47,18 @@ import { logger } from '@root-helpers/logger' import { isP2PEnabled, videoRequiresFileToken, videoRequiresUserAuth } from '@root-helpers/video' import debug from 'debug' import { forkJoin, map, Observable, of, Subscription, switchMap } from 'rxjs' +import { environment } from '../../../environments/environment' import { + cleanupVideoWatch, + getStoredTheater, + getStoredVideoWatchHistory, HLSOptions, PeerTubePlayer, PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerMode, videojs -} from '../../../assets/player' -import { cleanupVideoWatch, getStoredTheater, getStoredVideoWatchHistory } from '../../../assets/player/peertube-player-local-storage' -import { environment } from '../../../environments/environment' +} from '@peertube/player' import { DateToggleComponent } from '../../shared/shared-main/date/date-toggle.component' import { PluginPlaceholderComponent } from '../../shared/shared-main/plugins/plugin-placeholder.component' import { VideoViewsCounterComponent } from '../../shared/shared-video/video-views-counter.component' diff --git a/client/src/assets/player/index.ts b/client/src/assets/player/index.ts deleted file mode 100644 index d34188ea755..00000000000 --- a/client/src/assets/player/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './peertube-player' -export * from './types' diff --git a/client/src/root-helpers/plugins-manager.ts b/client/src/root-helpers/plugins-manager.ts index b11af1511f1..c82836f9fc3 100644 --- a/client/src/root-helpers/plugins-manager.ts +++ b/client/src/root-helpers/plugins-manager.ts @@ -2,7 +2,7 @@ import debug from 'debug' import { firstValueFrom, ReplaySubject } from 'rxjs' import { first, shareReplay } from 'rxjs/operators' -import { RegisterClientHelpers } from 'src/types/register-client-option.model' +import { RegisterClientHelpers } from '../types/register-client-option.model' import { getExternalAuthHref, getHookType, internalRunHook } from '@peertube/peertube-core-utils' import { ClientDoAction, @@ -83,6 +83,7 @@ class PluginsManager { private readonly onFormFields: OnFormFields private readonly onSettingsScripts: OnSettingsScripts private readonly onClientRoute: OnClientRoute + private readonly backendUrl: string constructor (options: { doAction?: ClientDoAction @@ -90,12 +91,14 @@ class PluginsManager { onFormFields?: OnFormFields onSettingsScripts?: OnSettingsScripts onClientRoute?: OnClientRoute + backendUrl?: string }) { this.doAction = options.doAction this.peertubeHelpersFactory = options.peertubeHelpersFactory this.onFormFields = options.onFormFields this.onSettingsScripts = options.onSettingsScripts this.onClientRoute = options.onClientRoute + this.backendUrl = options.backendUrl } static getPluginPathPrefix (isTheme: boolean) { @@ -281,7 +284,7 @@ class PluginsManager { logger.info(`Loading script ${clientScript.script} of plugin ${plugin.name}`) - const absURL = (environment.apiUrl || window.location.origin) + clientScript.script + const absURL = (this.backendUrl || environment.apiUrl || window.location.origin) + clientScript.script return dynamicImport(absURL) .then((script: ClientScript) => { return script.register({ diff --git a/client/src/assets/player/translations-manager.ts b/client/src/root-helpers/translations-manager.ts similarity index 100% rename from client/src/assets/player/translations-manager.ts rename to client/src/root-helpers/translations-manager.ts diff --git a/client/src/standalone/build-tools/vite-utils.ts b/client/src/standalone/build-tools/vite-utils.ts new file mode 100644 index 00000000000..bf366659cfd --- /dev/null +++ b/client/src/standalone/build-tools/vite-utils.ts @@ -0,0 +1,21 @@ +import { resolve } from 'path' + +export function getCSSConfig (root: string) { + return { + preprocessorOptions: { + scss: { + api: 'modern-compiler', + loadPaths: [ resolve(root, './src/sass/include') ], + // FIXME: Wait for bootstrap upgrade that fixes deprecated sass utils + silenceDeprecations: [ 'import', 'mixed-decls', 'color-functions', 'global-builtin' ] + } + } + } +} + +export function getAliasConfig (root: string) { + return [ + { find: /^video.js$/, replacement: resolve(root, './node_modules/video.js/core.js') }, + { find: '@root-helpers', replacement: resolve(root, './src/root-helpers') } + ] +} diff --git a/client/src/standalone/player/package.json b/client/src/standalone/player/package.json new file mode 100644 index 00000000000..940ccb746f9 --- /dev/null +++ b/client/src/standalone/player/package.json @@ -0,0 +1,31 @@ +{ + "name": "@peertube/player", + "private": false, + "version": "0.0.0", + "scripts": { + "build": "rm -rf ./build && ../../../node_modules/.bin/vite build --mode production --config ./vite.config.mjs", + "dev": "../../../node_modules/.bin/vite build --mode dev --watch --config ./vite.config.mjs" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Chocobozzz/PeerTube.git" + }, + "keywords": [ + "peertube", + "embed" + ], + "main": "./build/peertube-player.js", + "exports": { + ".": "./build/peertube-player.js" + }, + "types": "./src/index.ts", + "author": "Chocobozzz", + "license": "AGPL-3.0", + "type": "module", + "sideEffects": true, + "bugs": { + "url": "https://github.com/Chocobozzz/PeerTube/issues" + }, + "homepage": "https://github.com/Chocobozzz/PeerTube#readme", + "dependencies": {} +} diff --git a/client/src/standalone/player/src/index.ts b/client/src/standalone/player/src/index.ts new file mode 100644 index 00000000000..66e3e149795 --- /dev/null +++ b/client/src/standalone/player/src/index.ts @@ -0,0 +1,5 @@ +export * from './peertube-player' +export * from './peertube-player-local-storage' +export * from './types' + +import './sass/player.scss' diff --git a/client/src/assets/player/peertube-player-local-storage.ts b/client/src/standalone/player/src/peertube-player-local-storage.ts similarity index 100% rename from client/src/assets/player/peertube-player-local-storage.ts rename to client/src/standalone/player/src/peertube-player-local-storage.ts diff --git a/client/src/assets/player/peertube-player.ts b/client/src/standalone/player/src/peertube-player.ts similarity index 99% rename from client/src/assets/player/peertube-player.ts rename to client/src/standalone/player/src/peertube-player.ts index 2c4108bbb52..182eb47cccd 100644 --- a/client/src/assets/player/peertube-player.ts +++ b/client/src/standalone/player/src/peertube-player.ts @@ -31,6 +31,8 @@ import './shared/metrics/metrics-plugin' import './shared/p2p-media-loader/hls-plugin' import './shared/p2p-media-loader/p2p-media-loader-plugin' import './shared/web-video/web-video-plugin' +import './shared/dock/peertube-dock-component' +import './shared/dock/peertube-dock-plugin' import videojs, { VideoJsPlayer } from 'video.js' import { logger } from '@root-helpers/logger' import { PluginsManager } from '@root-helpers/plugins-manager' @@ -40,7 +42,7 @@ import { isMobile } from '@root-helpers/web-browser' import { buildVideoLink, decorateVideoLink, isDefaultLocale, pick } from '@peertube/peertube-core-utils' import { saveAverageBandwidth } from './peertube-player-local-storage' import { ControlBarOptionsBuilder, HLSOptionsBuilder, WebVideoOptionsBuilder } from './shared/player-options-builder' -import { TranslationsManager } from './translations-manager' +import { TranslationsManager } from '@root-helpers/translations-manager' import { PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerNetworkInfo, VideoJSPluginOptions } from './types' // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) diff --git a/client/src/standalone/player/src/sass/player.scss b/client/src/standalone/player/src/sass/player.scss new file mode 100644 index 00000000000..30d2493bca2 --- /dev/null +++ b/client/src/standalone/player/src/sass/player.scss @@ -0,0 +1,2 @@ +@use '../../../node_modules/video.js/dist/video-js'; +@use './shared/index.scss'; diff --git a/client/src/sass/player/_player-variables.scss b/client/src/standalone/player/src/sass/shared/_player-variables.scss similarity index 93% rename from client/src/sass/player/_player-variables.scss rename to client/src/standalone/player/src/sass/shared/_player-variables.scss index 8691bf6b2bd..9e0f41363fd 100644 --- a/client/src/sass/player/_player-variables.scss +++ b/client/src/standalone/player/src/sass/shared/_player-variables.scss @@ -22,5 +22,3 @@ $control-bar-total-height: $control-bar-height - $control-bar-slider-top; $progress-margin: 10px; $dock-padding: 20px; - -$assets-path: '../../assets/' !default; diff --git a/client/src/sass/player/bezels.scss b/client/src/standalone/player/src/sass/shared/bezels.scss similarity index 100% rename from client/src/sass/player/bezels.scss rename to client/src/standalone/player/src/sass/shared/bezels.scss diff --git a/client/src/sass/player/context-menu.scss b/client/src/standalone/player/src/sass/shared/context-menu.scss similarity index 89% rename from client/src/sass/player/context-menu.scss rename to client/src/standalone/player/src/sass/shared/context-menu.scss index f5b051be252..84f5928d169 100644 --- a/client/src/sass/player/context-menu.scss +++ b/client/src/standalone/player/src/sass/shared/context-menu.scss @@ -50,8 +50,8 @@ $context-menu-width: 350px; @each $icon in $icons { &[class$="-#{$icon}"] { - mask-image: url('#{$assets-path}/player/images/#{$icon}.svg'); - -webkit-mask-image: url('#{$assets-path}/player/images/#{$icon}.svg'); + mask-image: url('./svg/#{$icon}.svg'); + -webkit-mask-image: url('./svg/#{$icon}.svg'); } } diff --git a/client/src/sass/player/control-bar.scss b/client/src/standalone/player/src/sass/shared/control-bar.scss similarity index 94% rename from client/src/sass/player/control-bar.scss rename to client/src/standalone/player/src/sass/shared/control-bar.scss index af54d0ccc97..14d7b1c8b84 100644 --- a/client/src/sass/player/control-bar.scss +++ b/client/src/standalone/player/src/sass/shared/control-bar.scss @@ -270,11 +270,11 @@ $chapter-marker-size: 9px; @include margin-right(2px); &.icon-download { - background-image: url('#{$assets-path}/player/images/arrow-down.svg'); + background-image: url('./svg/arrow-down.svg'); } &.icon-upload { - background-image: url('#{$assets-path}/player/images/arrow-up.svg'); + background-image: url('./svg/arrow-up.svg'); } } } @@ -291,8 +291,8 @@ $chapter-marker-size: 9px; .icon { &.icon-next, &.icon-previous { - mask-image: url('#{$assets-path}/player/images/next.svg'); - -webkit-mask-image: url('#{$assets-path}/player/images/next.svg'); + mask-image: url('./svg/next.svg'); + -webkit-mask-image: url('./svg/next.svg'); mask-size: cover; -webkit-mask-size: cover; @@ -319,7 +319,7 @@ $chapter-marker-size: 9px; width: $control-bar-icon-size; height: $control-bar-icon-size; vertical-align: middle; - background: url('#{$assets-path}/player/images/volume.svg') no-repeat; + background: url('./svg/volume.svg') no-repeat; background-size: contain; &::before { @@ -328,7 +328,7 @@ $chapter-marker-size: 9px; } &.vjs-vol-0 .vjs-icon-placeholder { - background: url('#{$assets-path}/player/images/volume-mute.svg') no-repeat; + background: url('./svg/volume-mute.svg') no-repeat; background-size: contain; } } @@ -415,7 +415,7 @@ $chapter-marker-size: 9px; height: $control-bar-icon-size - 4px; width: $control-bar-icon-size - 4px; vertical-align: middle; - background: url('#{$assets-path}/player/images/settings.svg') no-repeat; + background: url('./svg/settings.svg') no-repeat; background-size: contain; &::before { @@ -448,7 +448,7 @@ $chapter-marker-size: 9px; width: $control-bar-icon-size - 4px; height: $control-bar-icon-size - 4px; vertical-align: middle; - background: url('#{$assets-path}/player/images/theater.svg') no-repeat; + background: url('./svg/theater.svg') no-repeat; background-size: contain; &::before { @@ -493,7 +493,7 @@ $chapter-marker-size: 9px; width: $control-bar-icon-size; height: $control-bar-icon-size; vertical-align: middle; - background: url('#{$assets-path}/player/images/fullscreen.svg') no-repeat; + background: url('./svg/fullscreen.svg') no-repeat; background-size: contain; &::before { @@ -515,14 +515,6 @@ $chapter-marker-size: 9px; .vjs-theater-control { display: none; } - - .vjs-peertube { - .icon, - .download-speed-text, - .upload-speed-text { - display: none !important; - } - } } .video-js.vjs-peertube-skin.vjs-size-570 .vjs-control-bar { @@ -540,6 +532,12 @@ $chapter-marker-size: 9px; .vjs-peertube-displayed { display: none !important; } + + .icon, + .download-speed-text, + .upload-speed-text { + display: none !important; + } } .vjs-peertube-link { diff --git a/client/src/sass/player/dock.scss b/client/src/standalone/player/src/sass/shared/dock.scss similarity index 100% rename from client/src/sass/player/dock.scss rename to client/src/standalone/player/src/sass/shared/dock.scss diff --git a/client/src/sass/player/index.scss b/client/src/standalone/player/src/sass/shared/index.scss similarity index 100% rename from client/src/sass/player/index.scss rename to client/src/standalone/player/src/sass/shared/index.scss diff --git a/client/src/sass/player/mobile.scss b/client/src/standalone/player/src/sass/shared/mobile.scss similarity index 100% rename from client/src/sass/player/mobile.scss rename to client/src/standalone/player/src/sass/shared/mobile.scss diff --git a/client/src/sass/player/offline-notification.scss b/client/src/standalone/player/src/sass/shared/offline-notification.scss similarity index 100% rename from client/src/sass/player/offline-notification.scss rename to client/src/standalone/player/src/sass/shared/offline-notification.scss diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/standalone/player/src/sass/shared/peertube-skin.scss similarity index 98% rename from client/src/sass/player/peertube-skin.scss rename to client/src/standalone/player/src/sass/shared/peertube-skin.scss index ea9f3e3354d..381bde83a08 100644 --- a/client/src/sass/player/peertube-skin.scss +++ b/client/src/standalone/player/src/sass/shared/peertube-skin.scss @@ -60,7 +60,7 @@ body { .vjs-icon-placeholder::before { content: ''; - background-image: url('#{$assets-path}/player/images/big-play-button.svg'); + background-image: url('./svg/big-play-button.svg'); @include big-play-button-triangle-size(45px); } diff --git a/client/src/sass/player/playlist.scss b/client/src/standalone/player/src/sass/shared/playlist.scss similarity index 91% rename from client/src/sass/player/playlist.scss rename to client/src/standalone/player/src/sass/shared/playlist.scss index c3e571b003a..f39a8463b1a 100644 --- a/client/src/sass/player/playlist.scss +++ b/client/src/standalone/player/src/sass/shared/playlist.scss @@ -44,8 +44,8 @@ $playlist-menu-width: 350px; } .cross { - mask-image: url('#{$assets-path}/images/feather/x.svg'); - -webkit-mask-image: url('#{$assets-path}/images/feather/x.svg'); + mask-image: url('./svg/x.svg'); + -webkit-mask-image: url('./svg/x.svg'); mask-size: cover; -webkit-mask-size: cover; @@ -92,8 +92,8 @@ $playlist-menu-width: 350px; } .vjs-playlist-icon { - mask-image: url('#{$assets-path}/images/feather/playlists.svg'); - -webkit-mask-image: url('#{$assets-path}/images/feather/playlists.svg'); + mask-image: url('./svg/playlists.svg'); + -webkit-mask-image: url('./svg/playlists.svg'); mask-size: cover; -webkit-mask-size: cover; diff --git a/client/src/sass/player/settings-menu.scss b/client/src/standalone/player/src/sass/shared/settings-menu.scss similarity index 97% rename from client/src/sass/player/settings-menu.scss rename to client/src/standalone/player/src/sass/shared/settings-menu.scss index d2095e85b62..0353c0b3121 100644 --- a/client/src/sass/player/settings-menu.scss +++ b/client/src/standalone/player/src/sass/shared/settings-menu.scss @@ -145,7 +145,7 @@ $setting-transition-easing: ease-out; position: absolute; content: ' '; margin-top: 1px; - background-image: url('#{$assets-path}/player/images/tick-white.svg'); + background-image: url('./svg/tick-white.svg'); @include icon(15px); @include left(15px); diff --git a/client/src/sass/player/spinner.scss b/client/src/standalone/player/src/sass/shared/spinner.scss similarity index 100% rename from client/src/sass/player/spinner.scss rename to client/src/standalone/player/src/sass/shared/spinner.scss diff --git a/client/src/sass/player/stats.scss b/client/src/standalone/player/src/sass/shared/stats.scss similarity index 100% rename from client/src/sass/player/stats.scss rename to client/src/standalone/player/src/sass/shared/stats.scss diff --git a/client/src/sass/player/storyboard.scss b/client/src/standalone/player/src/sass/shared/storyboard.scss similarity index 100% rename from client/src/sass/player/storyboard.scss rename to client/src/standalone/player/src/sass/shared/storyboard.scss diff --git a/client/src/sass/player/upnext.scss b/client/src/standalone/player/src/sass/shared/upnext.scss similarity index 100% rename from client/src/sass/player/upnext.scss rename to client/src/standalone/player/src/sass/shared/upnext.scss diff --git a/client/src/assets/player/images/arrow-down.svg b/client/src/standalone/player/src/sass/svg/arrow-down.svg similarity index 100% rename from client/src/assets/player/images/arrow-down.svg rename to client/src/standalone/player/src/sass/svg/arrow-down.svg diff --git a/client/src/assets/player/images/arrow-up.svg b/client/src/standalone/player/src/sass/svg/arrow-up.svg similarity index 100% rename from client/src/assets/player/images/arrow-up.svg rename to client/src/standalone/player/src/sass/svg/arrow-up.svg diff --git a/client/src/assets/player/images/big-play-button.svg b/client/src/standalone/player/src/sass/svg/big-play-button.svg similarity index 100% rename from client/src/assets/player/images/big-play-button.svg rename to client/src/standalone/player/src/sass/svg/big-play-button.svg diff --git a/client/src/assets/player/images/code.svg b/client/src/standalone/player/src/sass/svg/code.svg similarity index 100% rename from client/src/assets/player/images/code.svg rename to client/src/standalone/player/src/sass/svg/code.svg diff --git a/client/src/assets/player/images/fullscreen.svg b/client/src/standalone/player/src/sass/svg/fullscreen.svg similarity index 100% rename from client/src/assets/player/images/fullscreen.svg rename to client/src/standalone/player/src/sass/svg/fullscreen.svg diff --git a/client/src/assets/player/images/info.svg b/client/src/standalone/player/src/sass/svg/info.svg similarity index 100% rename from client/src/assets/player/images/info.svg rename to client/src/standalone/player/src/sass/svg/info.svg diff --git a/client/src/assets/player/images/link-2.svg b/client/src/standalone/player/src/sass/svg/link-2.svg similarity index 100% rename from client/src/assets/player/images/link-2.svg rename to client/src/standalone/player/src/sass/svg/link-2.svg diff --git a/client/src/assets/player/images/next.svg b/client/src/standalone/player/src/sass/svg/next.svg similarity index 100% rename from client/src/assets/player/images/next.svg rename to client/src/standalone/player/src/sass/svg/next.svg diff --git a/client/src/standalone/player/src/sass/svg/playlists.svg b/client/src/standalone/player/src/sass/svg/playlists.svg new file mode 100644 index 00000000000..247c073f0ec --- /dev/null +++ b/client/src/standalone/player/src/sass/svg/playlists.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + diff --git a/client/src/assets/player/images/repeat.svg b/client/src/standalone/player/src/sass/svg/repeat.svg similarity index 100% rename from client/src/assets/player/images/repeat.svg rename to client/src/standalone/player/src/sass/svg/repeat.svg diff --git a/client/src/assets/player/images/settings.svg b/client/src/standalone/player/src/sass/svg/settings.svg similarity index 100% rename from client/src/assets/player/images/settings.svg rename to client/src/standalone/player/src/sass/svg/settings.svg diff --git a/client/src/assets/player/images/theater.svg b/client/src/standalone/player/src/sass/svg/theater.svg similarity index 100% rename from client/src/assets/player/images/theater.svg rename to client/src/standalone/player/src/sass/svg/theater.svg diff --git a/client/src/assets/player/images/tick-white.svg b/client/src/standalone/player/src/sass/svg/tick-white.svg similarity index 100% rename from client/src/assets/player/images/tick-white.svg rename to client/src/standalone/player/src/sass/svg/tick-white.svg diff --git a/client/src/assets/player/images/volume-mute.svg b/client/src/standalone/player/src/sass/svg/volume-mute.svg similarity index 100% rename from client/src/assets/player/images/volume-mute.svg rename to client/src/standalone/player/src/sass/svg/volume-mute.svg diff --git a/client/src/assets/player/images/volume.svg b/client/src/standalone/player/src/sass/svg/volume.svg similarity index 100% rename from client/src/assets/player/images/volume.svg rename to client/src/standalone/player/src/sass/svg/volume.svg diff --git a/client/src/standalone/player/src/sass/svg/x.svg b/client/src/standalone/player/src/sass/svg/x.svg new file mode 100644 index 00000000000..7d5875ca8bc --- /dev/null +++ b/client/src/standalone/player/src/sass/svg/x.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/player/shared/bezels/bezels-plugin.ts b/client/src/standalone/player/src/shared/bezels/bezels-plugin.ts similarity index 100% rename from client/src/assets/player/shared/bezels/bezels-plugin.ts rename to client/src/standalone/player/src/shared/bezels/bezels-plugin.ts diff --git a/client/src/assets/player/shared/bezels/index.ts b/client/src/standalone/player/src/shared/bezels/index.ts similarity index 100% rename from client/src/assets/player/shared/bezels/index.ts rename to client/src/standalone/player/src/shared/bezels/index.ts diff --git a/client/src/assets/player/shared/bezels/pause-bezel.ts b/client/src/standalone/player/src/shared/bezels/pause-bezel.ts similarity index 100% rename from client/src/assets/player/shared/bezels/pause-bezel.ts rename to client/src/standalone/player/src/shared/bezels/pause-bezel.ts diff --git a/client/src/assets/player/shared/common/index.ts b/client/src/standalone/player/src/shared/common/index.ts similarity index 100% rename from client/src/assets/player/shared/common/index.ts rename to client/src/standalone/player/src/shared/common/index.ts diff --git a/client/src/assets/player/shared/common/utils.ts b/client/src/standalone/player/src/shared/common/utils.ts similarity index 100% rename from client/src/assets/player/shared/common/utils.ts rename to client/src/standalone/player/src/shared/common/utils.ts diff --git a/client/src/assets/player/shared/context-menu/context-menu-item.ts b/client/src/standalone/player/src/shared/context-menu/context-menu-item.ts similarity index 100% rename from client/src/assets/player/shared/context-menu/context-menu-item.ts rename to client/src/standalone/player/src/shared/context-menu/context-menu-item.ts diff --git a/client/src/assets/player/shared/context-menu/context-menu-plugin.ts b/client/src/standalone/player/src/shared/context-menu/context-menu-plugin.ts similarity index 100% rename from client/src/assets/player/shared/context-menu/context-menu-plugin.ts rename to client/src/standalone/player/src/shared/context-menu/context-menu-plugin.ts diff --git a/client/src/assets/player/shared/context-menu/context-menu.ts b/client/src/standalone/player/src/shared/context-menu/context-menu.ts similarity index 100% rename from client/src/assets/player/shared/context-menu/context-menu.ts rename to client/src/standalone/player/src/shared/context-menu/context-menu.ts diff --git a/client/src/assets/player/shared/context-menu/index.ts b/client/src/standalone/player/src/shared/context-menu/index.ts similarity index 100% rename from client/src/assets/player/shared/context-menu/index.ts rename to client/src/standalone/player/src/shared/context-menu/index.ts diff --git a/client/src/assets/player/shared/context-menu/util.ts b/client/src/standalone/player/src/shared/context-menu/util.ts similarity index 100% rename from client/src/assets/player/shared/context-menu/util.ts rename to client/src/standalone/player/src/shared/context-menu/util.ts diff --git a/client/src/assets/player/shared/control-bar/caption-toggle-button.ts b/client/src/standalone/player/src/shared/control-bar/caption-toggle-button.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/caption-toggle-button.ts rename to client/src/standalone/player/src/shared/control-bar/caption-toggle-button.ts diff --git a/client/src/assets/player/shared/control-bar/chapters-plugin.ts b/client/src/standalone/player/src/shared/control-bar/chapters-plugin.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/chapters-plugin.ts rename to client/src/standalone/player/src/shared/control-bar/chapters-plugin.ts diff --git a/client/src/assets/player/shared/control-bar/index.ts b/client/src/standalone/player/src/shared/control-bar/index.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/index.ts rename to client/src/standalone/player/src/shared/control-bar/index.ts diff --git a/client/src/assets/player/shared/control-bar/next-previous-video-button.ts b/client/src/standalone/player/src/shared/control-bar/next-previous-video-button.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/next-previous-video-button.ts rename to client/src/standalone/player/src/shared/control-bar/next-previous-video-button.ts diff --git a/client/src/assets/player/shared/control-bar/p2p-info-button.ts b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/p2p-info-button.ts rename to client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts diff --git a/client/src/assets/player/shared/control-bar/peertube-link-button.ts b/client/src/standalone/player/src/shared/control-bar/peertube-link-button.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/peertube-link-button.ts rename to client/src/standalone/player/src/shared/control-bar/peertube-link-button.ts diff --git a/client/src/assets/player/shared/control-bar/peertube-live-display.ts b/client/src/standalone/player/src/shared/control-bar/peertube-live-display.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/peertube-live-display.ts rename to client/src/standalone/player/src/shared/control-bar/peertube-live-display.ts diff --git a/client/src/assets/player/shared/control-bar/progress-bar-marker-component.ts b/client/src/standalone/player/src/shared/control-bar/progress-bar-marker-component.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/progress-bar-marker-component.ts rename to client/src/standalone/player/src/shared/control-bar/progress-bar-marker-component.ts diff --git a/client/src/assets/player/shared/control-bar/storyboard-plugin.ts b/client/src/standalone/player/src/shared/control-bar/storyboard-plugin.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/storyboard-plugin.ts rename to client/src/standalone/player/src/shared/control-bar/storyboard-plugin.ts diff --git a/client/src/assets/player/shared/control-bar/theater-button.ts b/client/src/standalone/player/src/shared/control-bar/theater-button.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/theater-button.ts rename to client/src/standalone/player/src/shared/control-bar/theater-button.ts diff --git a/client/src/assets/player/shared/control-bar/time-tooltip.ts b/client/src/standalone/player/src/shared/control-bar/time-tooltip.ts similarity index 100% rename from client/src/assets/player/shared/control-bar/time-tooltip.ts rename to client/src/standalone/player/src/shared/control-bar/time-tooltip.ts diff --git a/client/src/assets/player/shared/dock/index.ts b/client/src/standalone/player/src/shared/dock/index.ts similarity index 100% rename from client/src/assets/player/shared/dock/index.ts rename to client/src/standalone/player/src/shared/dock/index.ts diff --git a/client/src/assets/player/shared/dock/peertube-dock-component.ts b/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts similarity index 100% rename from client/src/assets/player/shared/dock/peertube-dock-component.ts rename to client/src/standalone/player/src/shared/dock/peertube-dock-component.ts diff --git a/client/src/assets/player/shared/dock/peertube-dock-plugin.ts b/client/src/standalone/player/src/shared/dock/peertube-dock-plugin.ts similarity index 100% rename from client/src/assets/player/shared/dock/peertube-dock-plugin.ts rename to client/src/standalone/player/src/shared/dock/peertube-dock-plugin.ts diff --git a/client/src/assets/player/shared/hotkeys/index.ts b/client/src/standalone/player/src/shared/hotkeys/index.ts similarity index 100% rename from client/src/assets/player/shared/hotkeys/index.ts rename to client/src/standalone/player/src/shared/hotkeys/index.ts diff --git a/client/src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts b/client/src/standalone/player/src/shared/hotkeys/peertube-hotkeys-plugin.ts similarity index 100% rename from client/src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts rename to client/src/standalone/player/src/shared/hotkeys/peertube-hotkeys-plugin.ts diff --git a/client/src/assets/player/shared/metrics/index.ts b/client/src/standalone/player/src/shared/metrics/index.ts similarity index 100% rename from client/src/assets/player/shared/metrics/index.ts rename to client/src/standalone/player/src/shared/metrics/index.ts diff --git a/client/src/assets/player/shared/metrics/metrics-plugin.ts b/client/src/standalone/player/src/shared/metrics/metrics-plugin.ts similarity index 100% rename from client/src/assets/player/shared/metrics/metrics-plugin.ts rename to client/src/standalone/player/src/shared/metrics/metrics-plugin.ts diff --git a/client/src/assets/player/shared/mobile/index.ts b/client/src/standalone/player/src/shared/mobile/index.ts similarity index 100% rename from client/src/assets/player/shared/mobile/index.ts rename to client/src/standalone/player/src/shared/mobile/index.ts diff --git a/client/src/assets/player/shared/mobile/peertube-mobile-buttons.ts b/client/src/standalone/player/src/shared/mobile/peertube-mobile-buttons.ts similarity index 100% rename from client/src/assets/player/shared/mobile/peertube-mobile-buttons.ts rename to client/src/standalone/player/src/shared/mobile/peertube-mobile-buttons.ts diff --git a/client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts b/client/src/standalone/player/src/shared/mobile/peertube-mobile-plugin.ts similarity index 100% rename from client/src/assets/player/shared/mobile/peertube-mobile-plugin.ts rename to client/src/standalone/player/src/shared/mobile/peertube-mobile-plugin.ts diff --git a/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts b/client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts similarity index 95% rename from client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts rename to client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts index fd5fb2525ec..0bd34f8df65 100644 --- a/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts @@ -5,6 +5,10 @@ import { logger } from '@root-helpers/logger' import Hlsjs, { ErrorData, Level, LevelSwitchingData, ManifestParsedData } from 'hls.js' import videojs from 'video.js' import { HLSPluginOptions, HlsjsConfigHandlerOptions, PeerTubeResolution, VideoJSTechHLS } from '../../types' +import { HlsJsP2PEngine, HlsWithP2PInstance } from 'p2p-media-loader-hlsjs' +import { omit } from '@peertube/peertube-core-utils' + +const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hlsjs) type ErrorCounts = { [ type: string ]: number @@ -14,8 +18,6 @@ type ErrorCounts = { // Source handler registration // --------------------------------------------------------------------------- -type HookFn = (player: videojs.Player, hljs: Hlsjs) => void - let alreadyRegistered = false const registerSourceHandler = function (vjs: typeof videojs) { @@ -110,8 +112,6 @@ videojs.registerPlugin('hlsjs', HLSJSConfigHandler) // --------------------------------------------------------------------------- export class Html5Hlsjs { - private static hooks: { [id: string]: HookFn[] } = {} - private readonly videoElement: HTMLVideoElement private readonly errorCounts: ErrorCounts = {} private readonly player: videojs.Player @@ -121,7 +121,7 @@ export class Html5Hlsjs { private maxNetworkErrorRecovery = 5 - private hls: Hlsjs + private hls: HlsWithP2PInstance private hlsjsConfig: HLSPluginOptions = null private _duration: number = null @@ -410,10 +410,15 @@ export class Html5Hlsjs { this.videoElement.addEventListener('play', this.handlers.play) } - const loader = this.hlsjsConfig.loaderBuilder() - this.hls = new Hlsjs({ ...this.hlsjsConfig, loader }) + this.hls = new HlsWithP2P({ + ...omit(this.hlsjsConfig, [ 'p2pMediaLoaderOptions' ]), - this.player.trigger('hlsjs-initialized', { hlsjs: this.hls, engine: loader.getEngine() }) + p2p: { + core: this.hlsjsConfig.p2pMediaLoaderOptions + } + }) + + this.player.trigger('hlsjs-initialized', { hlsjs: this.hls }) this.hls.on(Hlsjs.Events.ERROR, (event, data) => this._onError(event, data)) this.hls.on(Hlsjs.Events.MANIFEST_PARSED, (event, data) => this._onMetaData(event, data)) @@ -462,17 +467,21 @@ export class Html5Hlsjs { this.hlsjsConfig.autoStartLoad = true this.player.autoplay('play') - const loader = this.hlsjsConfig.loaderBuilder() - this.hls = new Hlsjs({ - ...this.hlsjsConfig, - loader, + this.hls = new HlsWithP2P({ + ...omit(this.hlsjsConfig, [ 'p2pMediaLoaderOptions' ]), + + p2p: { + core: this.hlsjsConfig.p2pMediaLoaderOptions + }, + startPosition: this.duration() === Infinity ? undefined : currentTime, + startLevel }) - this.player.trigger('hlsjs-initialized', { hlsjs: this.hls, engine: loader.getEngine() }) + this.player.trigger('hlsjs-initialized', { hlsjs: this.hls }) this.hls.on(Hlsjs.Events.ERROR, (event, data) => this._onError(event, data)) this.registerLevelEventSwitch() diff --git a/client/src/assets/player/shared/p2p-media-loader/index.ts b/client/src/standalone/player/src/shared/p2p-media-loader/index.ts similarity index 100% rename from client/src/assets/player/shared/p2p-media-loader/index.ts rename to client/src/standalone/player/src/shared/p2p-media-loader/index.ts diff --git a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts similarity index 74% rename from client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts rename to client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts index abcbd226189..af52503f021 100644 --- a/client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts @@ -1,9 +1,9 @@ -import { Events, Segment } from '@peertube/p2p-media-loader-core' -import { Engine, initHlsJsPlayer } from '@peertube/p2p-media-loader-hlsjs' import { addQueryParams } from '@peertube/peertube-core-utils' import { logger } from '@root-helpers/logger' import debug from 'debug' -import Hlsjs from 'hls.js' +import { FragLoadedData, default as Hlsjs } from 'hls.js' +import type { DownloadSource, SegmentErrorDetails, SegmentLoadDetails } from 'p2p-media-loader-core' +import type { HlsWithP2PInstance } from 'p2p-media-loader-hlsjs' import videojs from 'video.js' import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types' import { SettingsButton } from '../settings/settings-menu-button' @@ -14,8 +14,7 @@ const Plugin = videojs.getPlugin('plugin') class P2pMediaLoaderPlugin extends Plugin { declare private readonly options: P2PMediaLoaderPluginOptions - declare private hlsjs: Hlsjs - declare private p2pEngine: Engine + declare private hlsjs: HlsWithP2PInstance declare private statsP2PBytes: { pendingDownload: number[] pendingUpload: number[] @@ -33,6 +32,9 @@ class P2pMediaLoaderPlugin extends Plugin { declare private liveEnded: boolean + declare private connectedPeers: Set + declare private totalHTTPPeers: number + constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions) { super(player) @@ -76,15 +78,13 @@ class P2pMediaLoaderPlugin extends Plugin { } { - const onHLSJSInitialized = (_: any, { hlsjs, engine }: { hlsjs: Hlsjs, engine: Engine }) => { - this.p2pEngine?.removeAllListeners() - this.p2pEngine?.destroy() + const onHLSJSInitialized = (_: any, { hlsjs }: { hlsjs: HlsWithP2PInstance }) => { + this.hlsjs?.p2pEngine?.destroy() clearInterval(this.networkInfoInterval) this.hlsjs = hlsjs - this.p2pEngine = engine - debugLogger('hls.js initialized, initializing p2p-media-loader plugin', { hlsjs, engine }) + debugLogger('hls.js initialized, initializing p2p-media-loader plugin', { hlsjs }) player.ready(() => this.initializePlugin()) } @@ -116,8 +116,7 @@ class P2pMediaLoaderPlugin extends Plugin { } dispose () { - this.p2pEngine?.removeAllListeners() - this.p2pEngine?.destroy() + this.hlsjs?.p2pEngine?.destroy() this.hlsjs?.destroy() this.options.segmentValidator?.destroy() @@ -152,15 +151,22 @@ class P2pMediaLoaderPlugin extends Plugin { } private initializePlugin () { - initHlsJsPlayer(this.player, this.hlsjs) + this.hlsjs.p2pEngine.addEventListener('onSegmentError', (details: SegmentErrorDetails) => { + if (navigator.onLine === false || this.liveEnded || details.downloadSource !== 'http') return + + const segment = details.segment + logger.clientError(`Segment ${segment.runtimeId} error.`, details) - this.p2pEngine.on(Events.SegmentError, (segment: Segment, err) => { - if (navigator.onLine === false || this.liveEnded) return + if (this.options.redundancyUrlManager) { + this.options.redundancyUrlManager.onSegmentError(segment.url) + } + }) - logger.clientError(`Segment ${segment.id} error.`, err) + this.hlsjs.p2pEngine.addEventListener('onSegmentLoaded', (details: SegmentLoadDetails) => { + if (details.downloadSource !== 'http') return if (this.options.redundancyUrlManager) { - this.options.redundancyUrlManager.removeBySegmentUrl(segment.requestUrl) + this.options.redundancyUrlManager.onSegmentSuccess(details.segmentUrl) } }) @@ -168,7 +174,8 @@ class P2pMediaLoaderPlugin extends Plugin { ? this.options.redundancyUrlManager.countBaseUrls() : 0 - this.statsP2PBytes.peersWithWebSeed = 1 + redundancyUrlsCount + this.totalHTTPPeers = 1 + redundancyUrlsCount + this.statsP2PBytes.peersWithWebSeed = this.totalHTTPPeers this.runStats() @@ -200,30 +207,45 @@ class P2pMediaLoaderPlugin extends Plugin { } private runStats () { - this.p2pEngine.on(Events.PieceBytesDownloaded, (method: string, _segment, bytes: number) => { + this.connectedPeers = new Set() + + if (this.hlsjs.p2pEngine.getConfig().core.mainStream.isP2PDisabled) { + this.hlsjs.on(Hlsjs.Events.FRAG_LOADED, (e, data: FragLoadedData) => { + const bytes = data.frag.stats.loaded + console.log(bytes) + + this.statsHTTPBytes.pendingDownload.push(bytes) + this.statsHTTPBytes.totalDownload += bytes + }) + } + + this.hlsjs.p2pEngine.addEventListener('onChunkDownloaded', (bytes: number, method: DownloadSource) => { const elem = method === 'p2p' ? this.statsP2PBytes : this.statsHTTPBytes elem.pendingDownload.push(bytes) elem.totalDownload += bytes }) - this.p2pEngine.on(Events.PieceBytesUploaded, (method: string, _segment, bytes: number) => { - if (method !== 'p2p') { - logger.error(`Received upload from unknown method ${method}`) - return - } - + this.hlsjs.p2pEngine.addEventListener('onChunkUploaded', (bytes: number) => { this.statsP2PBytes.pendingUpload.push(bytes) this.statsP2PBytes.totalUpload += bytes }) - this.p2pEngine.on(Events.PeerConnect, () => { - this.statsP2PBytes.peersWithWebSeed++ - this.statsP2PBytes.peersP2POnly++ + this.hlsjs.p2pEngine.addEventListener('onPeerConnect', peer => { + if (peer.streamType !== 'main') return + + this.connectedPeers.add(peer.peerId) + this.statsP2PBytes.peersP2POnly = this.connectedPeers.size + + this.statsP2PBytes.peersWithWebSeed = this.totalHTTPPeers + this.statsP2PBytes.peersP2POnly }) - this.p2pEngine.on(Events.PeerClose, () => { - this.statsP2PBytes.peersWithWebSeed-- - this.statsP2PBytes.peersP2POnly-- + this.hlsjs.p2pEngine.addEventListener('onPeerClose', peer => { + if (peer.streamType !== 'main') return + + this.connectedPeers.delete(peer.peerId) + this.statsP2PBytes.peersP2POnly = this.connectedPeers.size + + this.statsP2PBytes.peersWithWebSeed = this.totalHTTPPeers + this.statsP2PBytes.peersP2POnly }) this.networkInfoInterval = setInterval(() => { diff --git a/client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts b/client/src/standalone/player/src/shared/p2p-media-loader/redundancy-url-manager.ts similarity index 67% rename from client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts rename to client/src/standalone/player/src/shared/p2p-media-loader/redundancy-url-manager.ts index ab051ca4450..ec7a219b7fc 100644 --- a/client/src/assets/player/shared/p2p-media-loader/redundancy-url-manager.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/redundancy-url-manager.ts @@ -1,22 +1,32 @@ import { logger } from '@root-helpers/logger' class RedundancyUrlManager { + private map = new Map() constructor (private baseUrls: string[] = []) { // empty } - removeBySegmentUrl (segmentUrl: string) { - const baseUrl = getBaseUrl(segmentUrl) + onSegmentError (segmentUrl: string) { + if (!this.map.has(segmentUrl)) return + + const customSegmentUrl = this.map.get(segmentUrl) + this.map.delete(segmentUrl) + + const baseUrl = getBaseUrl(customSegmentUrl) const oldLength = baseUrl.length this.baseUrls = this.baseUrls.filter(u => u !== baseUrl && u !== baseUrl + '/') if (oldLength !== this.baseUrls.length) { - logger.info(`Removed redundancy of segment URL ${segmentUrl}.`) + logger.info(`Removed redundancy of segment URL ${customSegmentUrl}.`) } } + onSegmentSuccess (segmentUrl: string) { + this.map.delete(segmentUrl) + } + buildUrl (url: string) { const max = this.baseUrls.length + 1 const i = this.getRandomInt(max) @@ -26,7 +36,11 @@ class RedundancyUrlManager { const newBaseUrl = this.baseUrls[i] const slashPart = newBaseUrl.endsWith('/') ? '' : '/' - return newBaseUrl + slashPart + getFilename(url) + const newUrl = newBaseUrl + slashPart + getFilename(url) + + this.map.set(url, newUrl) + + return newUrl } countBaseUrls () { diff --git a/client/src/assets/player/shared/p2p-media-loader/segment-url-builder.ts b/client/src/standalone/player/src/shared/p2p-media-loader/segment-url-builder.ts similarity index 80% rename from client/src/assets/player/shared/p2p-media-loader/segment-url-builder.ts rename to client/src/standalone/player/src/shared/p2p-media-loader/segment-url-builder.ts index 8875e5353c1..9cd1d979c11 100644 --- a/client/src/assets/player/shared/p2p-media-loader/segment-url-builder.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/segment-url-builder.ts @@ -1,8 +1,9 @@ -import { Segment } from '@peertube/p2p-media-loader-core' +import type { Segment } from 'p2p-media-loader-core' import { RedundancyUrlManager } from './redundancy-url-manager' export function segmentUrlBuilderFactory (redundancyUrlManager: RedundancyUrlManager | null) { return function segmentBuilder (segment: Segment) { + console.log(segment) if (!redundancyUrlManager) return segment.url return redundancyUrlManager.buildUrl(segment.url) diff --git a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts b/client/src/standalone/player/src/shared/p2p-media-loader/segment-validator.ts similarity index 76% rename from client/src/assets/player/shared/p2p-media-loader/segment-validator.ts rename to client/src/standalone/player/src/shared/p2p-media-loader/segment-validator.ts index e5f6631171c..d88ba5ebc48 100644 --- a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/segment-validator.ts @@ -1,9 +1,9 @@ -import { Segment } from '@peertube/p2p-media-loader-core' +import type { ByteRange } from 'p2p-media-loader-core' +import { removeQueryParams } from '@peertube/peertube-core-utils' import { logger } from '@root-helpers/logger' import { wait } from '@root-helpers/utils' -import { removeQueryParams } from '@peertube/peertube-core-utils' -import { isSameOrigin } from '../common' import debug from 'debug' +import { isSameOrigin } from '../common' const debugLogger = debug('peertube:player:segment-validator') @@ -12,9 +12,6 @@ type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string const maxRetries = 10 export class SegmentValidator { - - private readonly bytesRangeRegex = /bytes=(\d+)-(\d+)/ - private destroyed = false private segmentJSONPromise: Promise @@ -29,17 +26,18 @@ export class SegmentValidator { }) { } - async validate (segment: Segment, _method: string, _peerId: string, retry = 1) { - if (this.destroyed) return + async validate (url: string, byteRange: ByteRange | undefined, data: ArrayBuffer, retry = 1): Promise { + if (this.destroyed) return false this.loadSha256SegmentsPromiseIfNeeded() - const filename = removeQueryParams(segment.url).split('/').pop() + const filename = removeQueryParams(url).split('/').pop() const segmentValue = (await this.segmentJSONPromise)[filename] if (!segmentValue && retry > maxRetries) { - throw new Error(`Unknown segment name ${filename} in segment validator`) + logger.clientError(`Unknown segment name ${filename} in segment validator`) + return false } if (!segmentValue) { @@ -49,9 +47,7 @@ export class SegmentValidator { this.loadSha256SegmentsPromise() - await this.validate(segment, _method, _peerId, retry + 1) - - return + return this.validate(url, byteRange, data, retry + 1) } let hashShouldBe: string @@ -60,24 +56,25 @@ export class SegmentValidator { if (typeof segmentValue === 'string') { hashShouldBe = segmentValue } else { - const captured = this.bytesRangeRegex.exec(segment.range) - range = captured[1] + '-' + captured[2] + range = byteRange.start + '-' + byteRange.end hashShouldBe = segmentValue[range] } if (hashShouldBe === undefined) { - throw new Error(`Unknown segment name ${filename}/${range} in segment validator`) + logger.clientError(`Unknown segment name ${filename}/${range} in segment validator`) + return false } - debugLogger(`Validating ${filename}` + (segment.range ? ` range ${segment.range}` : '')) + debugLogger(`Validating ${filename}` + (range ? ` range ${range}` : '')) - const calculatedSha = await this.sha256Hex(segment.data) + const calculatedSha = await this.sha256Hex(data) if (calculatedSha !== hashShouldBe) { - throw new Error( - `Hashes does not correspond for segment ${filename}/${range}` + - `(expected: ${hashShouldBe} instead of ${calculatedSha})` + logger.clientError( + `Hashes does not correspond for segment ${filename}/${range} (expected: ${hashShouldBe} instead of ${calculatedSha})` ) + + return true } } @@ -106,7 +103,7 @@ export class SegmentValidator { return fetch(this.options.segmentsSha256Url, { headers }) .then(res => res.json() as Promise) .catch(err => { - logger.error('Cannot get sha256 segments', err) + logger.clientError('Cannot get sha256 segments', err) return {} }) } diff --git a/client/src/assets/player/shared/peertube/index.ts b/client/src/standalone/player/src/shared/peertube/index.ts similarity index 100% rename from client/src/assets/player/shared/peertube/index.ts rename to client/src/standalone/player/src/shared/peertube/index.ts diff --git a/client/src/assets/player/shared/peertube/peertube-plugin.ts b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts similarity index 100% rename from client/src/assets/player/shared/peertube/peertube-plugin.ts rename to client/src/standalone/player/src/shared/peertube/peertube-plugin.ts diff --git a/client/src/assets/player/shared/player-options-builder/control-bar-options-builder.ts b/client/src/standalone/player/src/shared/player-options-builder/control-bar-options-builder.ts similarity index 100% rename from client/src/assets/player/shared/player-options-builder/control-bar-options-builder.ts rename to client/src/standalone/player/src/shared/player-options-builder/control-bar-options-builder.ts diff --git a/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts b/client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts similarity index 65% rename from client/src/assets/player/shared/player-options-builder/hls-options-builder.ts rename to client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts index db33c06c3dc..7dfcb69ba04 100644 --- a/client/src/assets/player/shared/player-options-builder/hls-options-builder.ts +++ b/client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts @@ -1,13 +1,11 @@ -import { HybridLoaderSettings } from '@peertube/p2p-media-loader-core' -import { Engine, HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs' import { getResolutionAndFPSLabel, getResolutionLabel } from '@peertube/peertube-core-utils' import { LiveVideoLatencyMode } from '@peertube/peertube-models' import { logger } from '@root-helpers/logger' -import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' +import debug from 'debug' import { Level } from 'hls.js' +import type { ByteRange, CoreConfig, StreamConfig } from 'p2p-media-loader-core' import { getAverageBandwidthInStore } from '../../peertube-player-local-storage' import { - HLSLoaderClass, HLSPluginOptions, P2PMediaLoaderPluginOptions, PeerTubePlayerConstructorOptions, @@ -15,9 +13,8 @@ import { } from '../../types' import { getRtcConfig, isSameOrigin } from '../common' import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager' -import { segmentUrlBuilderFactory } from '../p2p-media-loader/segment-url-builder' import { SegmentValidator } from '../p2p-media-loader/segment-validator' -import debug from 'debug' +import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' const debugLogger = debug('peertube:player:hls') @@ -58,7 +55,6 @@ export class HLSOptionsBuilder { 'filter:internal.player.p2p-media-loader.options.result', this.getP2PMediaLoaderOptions({ redundancyUrlManager, segmentValidator }) ) - const loaderBuilder = () => new Engine(p2pMediaLoaderConfig).createLoaderClass() as unknown as HLSLoaderClass const p2pMediaLoader: P2PMediaLoaderPluginOptions = { requiresUserAuth: this.options.requiresUserAuth, @@ -73,7 +69,7 @@ export class HLSOptionsBuilder { } const hlsjs = { - hlsjsConfig: this.getHLSJSOptions(loaderBuilder), + hlsjsConfig: this.getHLSJSOptions(p2pMediaLoaderConfig), levelLabelHandler: (level: Level, player: videojs.VideoJsPlayer) => { const resolution = Math.min(level.height || 0, level.width || 0) @@ -99,65 +95,81 @@ export class HLSOptionsBuilder { private getP2PMediaLoaderOptions (options: { redundancyUrlManager: RedundancyUrlManager | null segmentValidator: SegmentValidator | null - }): HlsJsEngineSettings { + }) { const { redundancyUrlManager, segmentValidator } = options - let consumeOnly = false + let isP2PUploadDisabled = false if ( (navigator as any)?.connection?.type === 'cellular' || peertubeLocalStorage.getItem('peertube-videojs-p2p-consume-only') === 'true' // Use for E2E testing ) { logger.info('We are on a cellular connection: disabling seeding.') - consumeOnly = true + isP2PUploadDisabled = true } - const trackerAnnounce = this.options.hls.trackerAnnounce + const announceTrackers = this.options.hls.trackerAnnounce .filter(t => t.startsWith('ws')) const specificLiveOrVODOptions = this.options.isLive ? this.getP2PMediaLoaderLiveOptions() : this.getP2PMediaLoaderVODOptions() - return { - loader: { - trackerAnnounce, - rtcConfig: getRtcConfig(this.options.stunServers), + // TODO: remove validateHTTPSegment typing when p2p-media-loader-core is updated + const loaderOptions: Partial & { validateHTTPSegment: any } = { + announceTrackers, + rtcConfig: getRtcConfig(this.options.stunServers), - simultaneousHttpDownloads: 1, - httpFailedSegmentTimeout: 1000, + httpRequestSetup: (segmentUrlArg, segmentByteRange, requestAbortSignal, requestByteRange) => { + const { requiresUserAuth, requiresPassword } = this.options - xhrSetup: (xhr, url) => { - const { requiresUserAuth, requiresPassword } = this.options + const segmentUrl = redundancyUrlManager + ? redundancyUrlManager.buildUrl(segmentUrlArg) + : segmentUrlArg - if (!(requiresUserAuth || requiresPassword)) return + const headers = new Headers() - if (!isSameOrigin(this.options.serverUrl, url)) return + if (requestByteRange) { + headers.set('Range', `bytes=${requestByteRange.start}-${requestByteRange.end ?? ''}`) + } - if (requiresPassword) xhr.setRequestHeader('x-peertube-video-password', this.options.videoPassword()) - else xhr.setRequestHeader('Authorization', this.options.authorizationHeader()) - }, + if (isSameOrigin(this.options.serverUrl, segmentUrl)) { + if (requiresPassword) { + headers.set('x-peertube-video-password', this.options.videoPassword()) + } else if (requiresUserAuth) { + headers.set('Authorization', this.options.authorizationHeader()) + } + } - segmentValidator: segmentValidator - ? segmentValidator.validate.bind(segmentValidator) - : null, + return Promise.resolve( + new Request(segmentUrl, { + headers, + signal: requestAbortSignal + }) + ) + }, - segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager), + validateP2PSegment: segmentValidator + ? segmentValidator.validate.bind(segmentValidator) + : null, - useP2P: this.options.p2pEnabled, - consumeOnly, + validateHTTPSegment: segmentValidator + ? segmentValidator.validate.bind(segmentValidator) + : null, - ...specificLiveOrVODOptions - }, - segments: { - swarmId: this.options.hls.playlistUrl, - forwardSegmentCount: specificLiveOrVODOptions.p2pDownloadMaxPriority ?? 20 - } + isP2PDisabled: !this.options.p2pEnabled, + isP2PUploadDisabled, + + swarmId: this.options.hls.playlistUrl, + + ...specificLiveOrVODOptions } + + return { loader: loaderOptions } } - private getP2PMediaLoaderLiveOptions (): Partial { + private getP2PMediaLoaderLiveOptions (): Partial { const base = { - requiredSegmentsPriority: 1 + highDemandTimeWindow: 4 } const latencyMode = this.options.liveOptions.latencyMode @@ -167,8 +179,7 @@ export class HLSOptionsBuilder { return { ...base, - useP2P: false, - requiredSegmentsPriority: 10 + isP2PDisabled: true } case LiveVideoLatencyMode.HIGH_LATENCY: @@ -179,34 +190,39 @@ export class HLSOptionsBuilder { } } - private getP2PMediaLoaderVODOptions (): Partial { + private getP2PMediaLoaderVODOptions (): Partial { return { - requiredSegmentsPriority: 3, - skipSegmentBuilderPriority: 1, - - cachedSegmentExpiration: 86400000, - cachedSegmentsCount: 100, - - httpDownloadMaxPriority: 9, - httpDownloadProbability: 0.06, - httpDownloadProbabilitySkipIfNoPeers: true, + highDemandTimeWindow: 15, - p2pDownloadMaxPriority: 50 + segmentMemoryStorageLimit: 1024 } } // --------------------------------------------------------------------------- - private getHLSJSOptions (loaderBuilder: () => HLSLoaderClass): HLSPluginOptions { + private getHLSJSOptions (p2pMediaLoaderConfig: { loader: CoreConfig }): HLSPluginOptions { const specificLiveOrVODOptions = this.options.isLive ? this.getHLSLiveOptions() : this.getHLSVODOptions() - const base = { + const base: HLSPluginOptions = { capLevelToPlayerSize: true, autoStartLoad: false, - loaderBuilder, + p2pMediaLoaderOptions: p2pMediaLoaderConfig.loader, + + // p2p-media-loader uses hls.js loader to fetch m3u8 playlists + xhrSetup: (xhr, url) => { + const { requiresUserAuth, requiresPassword } = this.options + + if (isSameOrigin(this.options.serverUrl, url)) { + if (requiresPassword) { + xhr.setRequestHeader('x-peertube-video-password', this.options.videoPassword()) + } else if (requiresUserAuth) { + xhr.setRequestHeader('Authorization', this.options.authorizationHeader()) + } + } + }, ...specificLiveOrVODOptions } diff --git a/client/src/assets/player/shared/player-options-builder/index.ts b/client/src/standalone/player/src/shared/player-options-builder/index.ts similarity index 100% rename from client/src/assets/player/shared/player-options-builder/index.ts rename to client/src/standalone/player/src/shared/player-options-builder/index.ts diff --git a/client/src/assets/player/shared/player-options-builder/web-video-options-builder.ts b/client/src/standalone/player/src/shared/player-options-builder/web-video-options-builder.ts similarity index 100% rename from client/src/assets/player/shared/player-options-builder/web-video-options-builder.ts rename to client/src/standalone/player/src/shared/player-options-builder/web-video-options-builder.ts diff --git a/client/src/assets/player/shared/playlist/index.ts b/client/src/standalone/player/src/shared/playlist/index.ts similarity index 100% rename from client/src/assets/player/shared/playlist/index.ts rename to client/src/standalone/player/src/shared/playlist/index.ts diff --git a/client/src/assets/player/shared/playlist/playlist-button.ts b/client/src/standalone/player/src/shared/playlist/playlist-button.ts similarity index 100% rename from client/src/assets/player/shared/playlist/playlist-button.ts rename to client/src/standalone/player/src/shared/playlist/playlist-button.ts diff --git a/client/src/assets/player/shared/playlist/playlist-menu-item.ts b/client/src/standalone/player/src/shared/playlist/playlist-menu-item.ts similarity index 100% rename from client/src/assets/player/shared/playlist/playlist-menu-item.ts rename to client/src/standalone/player/src/shared/playlist/playlist-menu-item.ts diff --git a/client/src/assets/player/shared/playlist/playlist-menu.ts b/client/src/standalone/player/src/shared/playlist/playlist-menu.ts similarity index 100% rename from client/src/assets/player/shared/playlist/playlist-menu.ts rename to client/src/standalone/player/src/shared/playlist/playlist-menu.ts diff --git a/client/src/assets/player/shared/playlist/playlist-plugin.ts b/client/src/standalone/player/src/shared/playlist/playlist-plugin.ts similarity index 100% rename from client/src/assets/player/shared/playlist/playlist-plugin.ts rename to client/src/standalone/player/src/shared/playlist/playlist-plugin.ts diff --git a/client/src/assets/player/shared/resolutions/index.ts b/client/src/standalone/player/src/shared/resolutions/index.ts similarity index 100% rename from client/src/assets/player/shared/resolutions/index.ts rename to client/src/standalone/player/src/shared/resolutions/index.ts diff --git a/client/src/assets/player/shared/resolutions/peertube-resolutions-plugin.ts b/client/src/standalone/player/src/shared/resolutions/peertube-resolutions-plugin.ts similarity index 100% rename from client/src/assets/player/shared/resolutions/peertube-resolutions-plugin.ts rename to client/src/standalone/player/src/shared/resolutions/peertube-resolutions-plugin.ts diff --git a/client/src/assets/player/shared/settings/index.ts b/client/src/standalone/player/src/shared/settings/index.ts similarity index 100% rename from client/src/assets/player/shared/settings/index.ts rename to client/src/standalone/player/src/shared/settings/index.ts diff --git a/client/src/assets/player/shared/settings/menu-focus-fixed.ts b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts similarity index 100% rename from client/src/assets/player/shared/settings/menu-focus-fixed.ts rename to client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts diff --git a/client/src/assets/player/shared/settings/resolution-menu-button.ts b/client/src/standalone/player/src/shared/settings/resolution-menu-button.ts similarity index 100% rename from client/src/assets/player/shared/settings/resolution-menu-button.ts rename to client/src/standalone/player/src/shared/settings/resolution-menu-button.ts diff --git a/client/src/assets/player/shared/settings/resolution-menu-item.ts b/client/src/standalone/player/src/shared/settings/resolution-menu-item.ts similarity index 100% rename from client/src/assets/player/shared/settings/resolution-menu-item.ts rename to client/src/standalone/player/src/shared/settings/resolution-menu-item.ts diff --git a/client/src/assets/player/shared/settings/settings-dialog.ts b/client/src/standalone/player/src/shared/settings/settings-dialog.ts similarity index 100% rename from client/src/assets/player/shared/settings/settings-dialog.ts rename to client/src/standalone/player/src/shared/settings/settings-dialog.ts diff --git a/client/src/assets/player/shared/settings/settings-menu-button.ts b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts similarity index 100% rename from client/src/assets/player/shared/settings/settings-menu-button.ts rename to client/src/standalone/player/src/shared/settings/settings-menu-button.ts diff --git a/client/src/assets/player/shared/settings/settings-menu-item.ts b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts similarity index 100% rename from client/src/assets/player/shared/settings/settings-menu-item.ts rename to client/src/standalone/player/src/shared/settings/settings-menu-item.ts diff --git a/client/src/assets/player/shared/settings/settings-panel-child.ts b/client/src/standalone/player/src/shared/settings/settings-panel-child.ts similarity index 100% rename from client/src/assets/player/shared/settings/settings-panel-child.ts rename to client/src/standalone/player/src/shared/settings/settings-panel-child.ts diff --git a/client/src/assets/player/shared/settings/settings-panel.ts b/client/src/standalone/player/src/shared/settings/settings-panel.ts similarity index 100% rename from client/src/assets/player/shared/settings/settings-panel.ts rename to client/src/standalone/player/src/shared/settings/settings-panel.ts diff --git a/client/src/assets/player/shared/stats/index.ts b/client/src/standalone/player/src/shared/stats/index.ts similarity index 100% rename from client/src/assets/player/shared/stats/index.ts rename to client/src/standalone/player/src/shared/stats/index.ts diff --git a/client/src/assets/player/shared/stats/stats-card.ts b/client/src/standalone/player/src/shared/stats/stats-card.ts similarity index 99% rename from client/src/assets/player/shared/stats/stats-card.ts rename to client/src/standalone/player/src/shared/stats/stats-card.ts index de25cae35cf..01971c7d0f9 100644 --- a/client/src/assets/player/shared/stats/stats-card.ts +++ b/client/src/standalone/player/src/shared/stats/stats-card.ts @@ -138,6 +138,8 @@ class StatsCard extends Component { this.containerEl.style.display = 'block' this.updateInterval = setInterval(async () => { + if (!this.mode) return + try { const options = this.mode === 'p2p-media-loader' ? this.buildHLSOptions() diff --git a/client/src/assets/player/shared/stats/stats-plugin.ts b/client/src/standalone/player/src/shared/stats/stats-plugin.ts similarity index 100% rename from client/src/assets/player/shared/stats/stats-plugin.ts rename to client/src/standalone/player/src/shared/stats/stats-plugin.ts diff --git a/client/src/assets/player/shared/upnext/end-card.ts b/client/src/standalone/player/src/shared/upnext/end-card.ts similarity index 100% rename from client/src/assets/player/shared/upnext/end-card.ts rename to client/src/standalone/player/src/shared/upnext/end-card.ts diff --git a/client/src/assets/player/shared/upnext/index.ts b/client/src/standalone/player/src/shared/upnext/index.ts similarity index 100% rename from client/src/assets/player/shared/upnext/index.ts rename to client/src/standalone/player/src/shared/upnext/index.ts diff --git a/client/src/assets/player/shared/upnext/upnext-plugin.ts b/client/src/standalone/player/src/shared/upnext/upnext-plugin.ts similarity index 100% rename from client/src/assets/player/shared/upnext/upnext-plugin.ts rename to client/src/standalone/player/src/shared/upnext/upnext-plugin.ts diff --git a/client/src/assets/player/shared/web-video/web-video-plugin.ts b/client/src/standalone/player/src/shared/web-video/web-video-plugin.ts similarity index 100% rename from client/src/assets/player/shared/web-video/web-video-plugin.ts rename to client/src/standalone/player/src/shared/web-video/web-video-plugin.ts diff --git a/client/src/assets/player/types/index.ts b/client/src/standalone/player/src/types/index.ts similarity index 100% rename from client/src/assets/player/types/index.ts rename to client/src/standalone/player/src/types/index.ts diff --git a/client/src/assets/player/types/peertube-player-options.ts b/client/src/standalone/player/src/types/peertube-player-options.ts similarity index 100% rename from client/src/assets/player/types/peertube-player-options.ts rename to client/src/standalone/player/src/types/peertube-player-options.ts diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/standalone/player/src/types/peertube-videojs-typings.ts similarity index 96% rename from client/src/assets/player/types/peertube-videojs-typings.ts rename to client/src/standalone/player/src/types/peertube-videojs-typings.ts index 671d2f56951..b78038e271a 100644 --- a/client/src/assets/player/types/peertube-videojs-typings.ts +++ b/client/src/standalone/player/src/types/peertube-videojs-typings.ts @@ -1,6 +1,7 @@ -import { Engine } from '@peertube/p2p-media-loader-hlsjs' import { VideoChapter, VideoFile, VideoPlaylist, VideoPlaylistElement } from '@peertube/peertube-models' import type { HlsConfig, Level, Loader, LoaderContext } from 'hls.js' +import type { CoreConfig } from 'p2p-media-loader-core' +import type { HlsJsP2PEngine } from 'p2p-media-loader-hlsjs' import videojs from 'video.js' import { BezelsPlugin } from '../shared/bezels/bezels-plugin' import { ContextMenuPlugin } from '../shared/context-menu' @@ -215,9 +216,9 @@ export type WebVideoPluginOptions = { export type HLSLoaderClass = { new (confg: HlsConfig): Loader - getEngine(): Engine + getEngine(): HlsJsP2PEngine } -export type HLSPluginOptions = Partial HLSLoaderClass }> +export type HLSPluginOptions = Partial export type P2PMediaLoaderPluginOptions = { redundancyUrlManager: RedundancyUrlManager | null @@ -233,7 +234,7 @@ export type P2PMediaLoaderPluginOptions = { } export type P2PMediaLoader = { - getEngine(): Engine + getEngine(): HlsJsP2PEngine destroy: () => void } diff --git a/client/src/standalone/player/tsconfig.json b/client/src/standalone/player/tsconfig.json new file mode 100644 index 00000000000..c2187c8c04a --- /dev/null +++ b/client/src/standalone/player/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../tsconfig.json", + "include": [ + "./index.ts" + ] +} diff --git a/client/src/standalone/player/vite.config.mjs b/client/src/standalone/player/vite.config.mjs new file mode 100644 index 00000000000..1e9a2ae6d1c --- /dev/null +++ b/client/src/standalone/player/vite.config.mjs @@ -0,0 +1,47 @@ +import { nodePolyfills } from 'vite-plugin-node-polyfills' +import { dirname, resolve } from 'path' +import { fileURLToPath } from 'url' +import { defineConfig } from 'vite' +import checker from 'vite-plugin-checker' + +import { getCSSConfig, getAliasConfig } from '../build-tools/vite-utils.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, '../../../') + +export default defineConfig(() => { + return { + build: { + outDir: resolve(__dirname, 'build'), + emptyOutDir: false, + minify: false, + lib: { + formats: [ 'es' ], + entry: resolve(__dirname, './src/index.ts'), + name: 'PeerTubePlayer', + fileName: 'peertube-player', + cssFilename: 'peertube-player.css' + } + }, + + css: getCSSConfig(root), + + resolve: { + alias: getAliasConfig(root), + + conditions: [ + 'p2pml:core-as-bundle', 'defaultClientConditions' + ] + }, + + plugins: [ + checker({ + typescript: { + tsconfigPath: resolve(__dirname, 'tsconfig.json') + } + }), + + nodePolyfills() + ] + } +}) diff --git a/client/src/standalone/videos/embed.scss b/client/src/standalone/videos/embed.scss index 44082e4a031..8f25b750124 100644 --- a/client/src/standalone/videos/embed.scss +++ b/client/src/standalone/videos/embed.scss @@ -1,11 +1,7 @@ @use '_variables' as *; @use '_mixins' as *; @use '_css-variables' as *; -@use 'video.js/dist/video-js'; - -$assets-path: '../../assets/'; - -@use '../../sass/player/index'; +@use '../player/build/peertube-player'; [hidden] { display: none !important; diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index dbb9478fe5b..74af84059c4 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -1,8 +1,3 @@ -import './embed.scss' -import '../../assets/player/shared/dock/peertube-dock-component' -import '../../assets/player/shared/dock/peertube-dock-plugin' -import { PeerTubeServerError } from 'src/types' -import videojs from 'video.js' import { HTMLServerConfig, ResultList, @@ -12,10 +7,13 @@ import { VideoPlaylistElement, VideoState } from '@peertube/peertube-models' -import { PeerTubePlayer } from '../../assets/player/peertube-player' -import { TranslationsManager } from '../../assets/player/translations-manager' +import type { PeerTubePlayer } from '@peertube/player' +import { TranslationsManager } from '@root-helpers/translations-manager' +import { PeerTubeServerError } from 'src/types' +import type videojs from 'video.js' import { getParamString, logger, videoRequiresFileToken } from '../../root-helpers' import { PeerTubeEmbedApi } from './embed-api' +import './embed.scss' import { AuthHTTP, LiveManager, @@ -37,6 +35,7 @@ export class PeerTubeEmbed { private translationsPromise: Promise<{ [id: string]: string }> private PeerTubePlayerManagerModulePromise: Promise + private videojs: typeof videojs private readonly http: AuthHTTP private readonly videoFetcher: VideoFetcher @@ -94,7 +93,7 @@ export class PeerTubeEmbed { async init () { this.translationsPromise = TranslationsManager.getServerTranslations(getBackendUrl(), navigator.language) - this.PeerTubePlayerManagerModulePromise = import('../../assets/player/peertube-player') + this.PeerTubePlayerManagerModulePromise = import('@peertube/player') // Issue when we parsed config from HTML, fallback to API if (!this.config) { @@ -334,7 +333,11 @@ export class PeerTubeEmbed { } } - this.peertubePlugin.getPluginsManager().runHook('action:embed.player.loaded', undefined, { player: this.player, videojs, video }) + this.peertubePlugin.getPluginsManager().runHook( + 'action:embed.player.loaded', + undefined, + { player: this.player, videojs: this.videojs, video } + ) } private buildCSS () { @@ -430,11 +433,13 @@ export class PeerTubeEmbed { this.playerHTML.setInitVideoEl(playerElement) this.playerHTML.addInitVideoElToDOM() - const [ { PeerTubePlayer } ] = await Promise.all([ + const [ { PeerTubePlayer, videojs } ] = await Promise.all([ this.PeerTubePlayerManagerModulePromise, this.peertubePlugin.loadPlugins(this.config, await this.translationsPromise) ]) + this.videojs = videojs + const constructorOptions = this.playerOptionsBuilder.getPlayerConstructorOptions({ serverConfig: this.config, authorizationHeader: () => this.http.getHeaderTokenValue() diff --git a/client/src/standalone/videos/shared/peertube-plugin.ts b/client/src/standalone/videos/shared/peertube-plugin.ts index 1643ebc9be6..15459394611 100644 --- a/client/src/standalone/videos/shared/peertube-plugin.ts +++ b/client/src/standalone/videos/shared/peertube-plugin.ts @@ -19,7 +19,8 @@ export class PeerTubePlugin { peertubeHelpersFactory: pluginInfo => this.buildPeerTubeHelpers({ pluginInfo, translations - }) + }), + backendUrl: getBackendUrl() }) this.pluginsManager.loadPluginsList(config) diff --git a/client/src/standalone/videos/shared/player-options-builder.ts b/client/src/standalone/videos/shared/player-options-builder.ts index 4e3daec57ff..3bd0c199538 100644 --- a/client/src/standalone/videos/shared/player-options-builder.ts +++ b/client/src/standalone/videos/shared/player-options-builder.ts @@ -11,7 +11,6 @@ import { VideoState, VideoStreamingPlaylistType } from '@peertube/peertube-models' -import { HLSOptions, PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerMode, VideoJSCaption } from '../../../assets/player' import { getBoolOrDefault, getParamString, @@ -22,12 +21,13 @@ import { UserLocalStorageKeys, videoRequiresUserAuth } from '../../../root-helpers' +import { HLSOptions, PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerMode, VideoJSCaption } from '../../player' import { PeerTubePlugin } from './peertube-plugin' import { PlayerHTML } from './player-html' import { PlaylistTracker } from './playlist-tracker' import { Translations } from './translations' -import { VideoFetcher } from './video-fetcher' import { getBackendUrl } from './url' +import { VideoFetcher } from './video-fetcher' export class PlayerOptionsBuilder { private autoplay: boolean diff --git a/client/src/standalone/videos/vite.config.mjs b/client/src/standalone/videos/vite.config.mjs index 7c69fd1142e..f7423012800 100644 --- a/client/src/standalone/videos/vite.config.mjs +++ b/client/src/standalone/videos/vite.config.mjs @@ -5,8 +5,9 @@ import { dirname } from 'path' import { fileURLToPath } from 'url' import checker from 'vite-plugin-checker' -const __dirname = dirname(fileURLToPath(import.meta.url)) +import { getCSSConfig, getAliasConfig } from '../build-tools/vite-utils.js' +const __dirname = dirname(fileURLToPath(import.meta.url)) const root = resolve(__dirname, '../../../') export default defineConfig(({ mode }) => { @@ -41,26 +42,17 @@ export default defineConfig(({ mode }) => { }, resolve: { - alias: [ - { find: /^video.js$/, replacement: resolve(root, './node_modules/video.js/core.js') }, - { find: '@root-helpers', replacement: resolve(root, './src/root-helpers') } - ], + alias: getAliasConfig(root), }, - css: { - preprocessorOptions: { - scss: { - includePaths: [resolve(root, './src/sass/include')] - } - } - }, + css: getCSSConfig(root), build: { outDir: resolve(root, 'dist', 'standalone', 'videos'), emptyOutDir: true, sourcemap: mode === 'development', - target: [ 'firefox78', 'ios12' ], + target: [ 'firefox78', 'ios14' ], rollupOptions: { input: { diff --git a/client/yarn.lock b/client/yarn.lock index 0ef35537301..9d736b04a86 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -454,6 +454,11 @@ axios "^1.7.4" uuid "9.0.1" +"@bufbuild/protobuf@^2.0.0": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-2.2.3.tgz#9cd136f6b687e63e9b517b3a54211ece942897ee" + integrity sha512-tFQoXHJdkEOSwj5tRIZSPNUuXK3RaR7T1nUrPgbYX1pUbvqqaaZAsfo+NXBPsz5rZMSKVFrgK1WL8Q/MSLvprg== + "@colors/colors@1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" @@ -500,171 +505,86 @@ esquery "^1.6.0" jsdoc-type-pratt-parser "~4.0.0" -"@esbuild/aix-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" - integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== - "@esbuild/aix-ppc64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== - "@esbuild/android-arm64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== -"@esbuild/android-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" - integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== - "@esbuild/android-arm@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== -"@esbuild/android-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" - integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== - "@esbuild/android-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== -"@esbuild/darwin-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== - "@esbuild/darwin-arm64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== -"@esbuild/darwin-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" - integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== - "@esbuild/darwin-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== -"@esbuild/freebsd-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" - integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== - "@esbuild/freebsd-arm64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== -"@esbuild/freebsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" - integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== - "@esbuild/freebsd-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== - "@esbuild/linux-arm64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== -"@esbuild/linux-arm@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" - integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== - "@esbuild/linux-arm@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== -"@esbuild/linux-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" - integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== - "@esbuild/linux-ia32@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== -"@esbuild/linux-loong64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" - integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== - "@esbuild/linux-loong64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== -"@esbuild/linux-mips64el@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" - integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== - "@esbuild/linux-mips64el@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== -"@esbuild/linux-ppc64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" - integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== - "@esbuild/linux-ppc64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== -"@esbuild/linux-riscv64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" - integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== - "@esbuild/linux-riscv64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== -"@esbuild/linux-s390x@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" - integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== - "@esbuild/linux-s390x@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== -"@esbuild/linux-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" - integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== - "@esbuild/linux-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" @@ -675,11 +595,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== -"@esbuild/netbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" - integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== - "@esbuild/netbsd-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" @@ -690,51 +605,26 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== -"@esbuild/openbsd-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" - integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== - "@esbuild/openbsd-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== -"@esbuild/sunos-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" - integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== - "@esbuild/sunos-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== -"@esbuild/win32-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" - integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== - "@esbuild/win32-arm64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== -"@esbuild/win32-ia32@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" - integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== - "@esbuild/win32-ia32@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== -"@esbuild/win32-x64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" - integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== - "@esbuild/win32-x64@0.24.2": version "0.24.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" @@ -1495,29 +1385,6 @@ smtp-server "^3.9.0" wildstring "1.0.9" -"@peertube/p2p-media-loader-core@^1.0.20": - version "1.0.20" - resolved "https://registry.yarnpkg.com/@peertube/p2p-media-loader-core/-/p2p-media-loader-core-1.0.20.tgz#8e786dd52471a03fc00006d14b150b38fe7c8211" - integrity sha512-t6yYFcBTqDZSp3U0HqOI9fJzxFgb2C4PoiRI4FPGd28baUbsilO1PQBRwQzvu6wt8zwjzOE8FBpzYa+1gv1Sqg== - dependencies: - bittorrent-tracker "^11.1.0" - debug "^4.3.5" - esbuild "^0.21.5" - events "^3.3.0" - sha.js "^2.4.11" - simple-peer "^9.11.1" - -"@peertube/p2p-media-loader-hlsjs@^1.0.20": - version "1.0.20" - resolved "https://registry.yarnpkg.com/@peertube/p2p-media-loader-hlsjs/-/p2p-media-loader-hlsjs-1.0.20.tgz#b6330d5331a70fa3a65ef3c32546b8d88c065749" - integrity sha512-PZS9h+txV+BX3t5lsh5PZ0ZtOogPJv4GmheQ5etceQZHxRAx2UxcAchMBJsa/sQ5c4CSMsN61Megs9iZ3gWauQ== - dependencies: - "@peertube/p2p-media-loader-core" "^1.0.20" - debug "^4.3.5" - esbuild "^0.21.5" - events "^3.3.0" - m3u8-parser "~4.7.1" - "@peertube/xliffmerge@^2.0.3": version "2.0.4" resolved "https://registry.yarnpkg.com/@peertube/xliffmerge/-/xliffmerge-2.0.4.tgz#5a80c263ab6f1b15a8b8c3e54317077f0d98e611" @@ -1609,9 +1476,9 @@ magic-string "^0.30.3" "@rollup/pluginutils@^5.0.1": - version "5.1.4" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a" - integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== + version "5.1.3" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz#3001bf1a03f3ad24457591f2c259c8e514e0dbdf" + integrity sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A== dependencies: "@types/estree" "^1.0.0" estree-walker "^2.0.2" @@ -1989,7 +1856,7 @@ resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.8.tgz#d5c6ec44f2f3328653dce385ae586bd8261f8e85" integrity sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg== -"@types/debug@^4.1.5": +"@types/debug@^4.1.12", "@types/debug@^4.1.5": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== @@ -2997,7 +2864,7 @@ bittorrent-peerid@^1.3.6: resolved "https://registry.yarnpkg.com/bittorrent-peerid/-/bittorrent-peerid-1.3.6.tgz#3688705a64937a8176ac2ded1178fc7bd91b61db" integrity sha512-VyLcUjVMEOdSpHaCG/7odvCdLbAB1y3l9A2V6WIje24uV7FkJPrQrH/RrlFmKxP89pFVDEnE+YlHaFujlFIZsg== -bittorrent-tracker@^11.1.0: +bittorrent-tracker@^11.2.1: version "11.2.1" resolved "https://registry.yarnpkg.com/bittorrent-tracker/-/bittorrent-tracker-11.2.1.tgz#b45ff4bd70c2c582bc60d4a8bb6b5bdcb487df9a" integrity sha512-SffBgHzNrhn+HBwdRD2st+TYJOs2LhF3ljJFPCYGv592LpGtPxw41UZHTUeY5muWnQl+wopcU8qXM9UEk2WKrA== @@ -3184,6 +3051,11 @@ browserstack-local@^1.5.1: ps-tree "=1.2.0" temp-fs "^0.9.9" +buffer-builder@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/buffer-builder/-/buffer-builder-0.2.0.tgz#3322cd307d8296dab1f604618593b261a3fade8f" + integrity sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg== + buffer-crc32@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" @@ -3544,6 +3416,11 @@ colorette@^2.0.20: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colorjs.io@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/colorjs.io/-/colorjs.io-0.5.2.tgz#63b20139b007591ebc3359932bef84628eb3fcef" + integrity sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -3811,7 +3688,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -3832,7 +3709,7 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@~4.3.1, debug@~4.3.2: +debug@^4.3.7, debug@~4.3.1, debug@~4.3.2: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -4022,10 +3899,10 @@ dom-walk@^0.1.0: resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== -domain-browser@4.22.0: - version "4.22.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.22.0.tgz#6ddd34220ec281f9a65d3386d267ddd35c491f9f" - integrity sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw== +domain-browser@^4.22.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.23.0.tgz#427ebb91efcb070f05cffdfb8a4e9a6c25f8c94b" + integrity sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA== domelementtype@^2.3.0: version "2.3.0" @@ -4129,9 +4006,9 @@ electron-to-chromium@^1.5.73: integrity sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug== elliptic@^6.5.3, elliptic@^6.5.5: - version "6.6.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" - integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== + version "6.6.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210" + integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -4354,35 +4231,6 @@ esbuild@0.24.2, esbuild@^0.24.2: "@esbuild/win32-ia32" "0.24.2" "@esbuild/win32-x64" "0.24.2" -esbuild@^0.21.3, esbuild@^0.21.5: - version "0.21.5" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" - integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== - optionalDependencies: - "@esbuild/aix-ppc64" "0.21.5" - "@esbuild/android-arm" "0.21.5" - "@esbuild/android-arm64" "0.21.5" - "@esbuild/android-x64" "0.21.5" - "@esbuild/darwin-arm64" "0.21.5" - "@esbuild/darwin-x64" "0.21.5" - "@esbuild/freebsd-arm64" "0.21.5" - "@esbuild/freebsd-x64" "0.21.5" - "@esbuild/linux-arm" "0.21.5" - "@esbuild/linux-arm64" "0.21.5" - "@esbuild/linux-ia32" "0.21.5" - "@esbuild/linux-loong64" "0.21.5" - "@esbuild/linux-mips64el" "0.21.5" - "@esbuild/linux-ppc64" "0.21.5" - "@esbuild/linux-riscv64" "0.21.5" - "@esbuild/linux-s390x" "0.21.5" - "@esbuild/linux-x64" "0.21.5" - "@esbuild/netbsd-x64" "0.21.5" - "@esbuild/openbsd-x64" "0.21.5" - "@esbuild/sunos-x64" "0.21.5" - "@esbuild/win32-arm64" "0.21.5" - "@esbuild/win32-ia32" "0.21.5" - "@esbuild/win32-x64" "0.21.5" - escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -5031,11 +4879,6 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-browser-rtc@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/get-browser-rtc/-/get-browser-rtc-1.1.0.tgz#d1494e299b00f33fc8e9d6d3343ba4ba99711a2c" - integrity sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ== - get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -5356,12 +5199,12 @@ hash-base@^3.0.0: safe-buffer "^5.2.0" hash-base@~3.0, hash-base@~3.0.4: - version "3.0.5" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.5.tgz#52480e285395cf7fba17dc4c9e47acdc7f248a8a" - integrity sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg== + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow== dependencies: - inherits "^2.0.4" - safe-buffer "^5.2.1" + inherits "^2.0.1" + safe-buffer "^5.0.1" hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" @@ -6427,22 +6270,20 @@ m3u8-parser@4.8.0: "@videojs/vhs-utils" "^3.0.5" global "^4.4.0" -m3u8-parser@~4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.7.1.tgz#d6df2c940bb19a01112a04ccc4ff44886a945305" - integrity sha512-pbrQwiMiq+MmI9bl7UjtPT3AK603PV9bogNlr83uC+X9IoxqL5E4k7kU7fMQ0dpRgxgeSMygqUa0IMLQNXLBNA== - dependencies: - "@babel/runtime" "^7.12.5" - "@videojs/vhs-utils" "^3.0.5" - global "^4.4.0" - -magic-string@0.30.17, magic-string@^0.30.12, magic-string@^0.30.3: +magic-string@0.30.17, magic-string@^0.30.12: version "0.30.17" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +magic-string@^0.30.3: + version "0.30.12" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.12.tgz#9eb11c9d072b9bcb4940a5b2c2e1a217e4ee1a60" + integrity sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + mailparser-mit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mailparser-mit/-/mailparser-mit-1.0.0.tgz#19df8436c2a02e1d34a03ec518a2eb065e0a94a4" @@ -6855,6 +6696,11 @@ mux.js@6.0.1: "@babel/runtime" "^7.11.2" global "^4.4.0" +nano-md5@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nano-md5/-/nano-md5-1.0.5.tgz#99d155e95ccaadc4096a309292a0df1b5190514e" + integrity sha512-1VAOX0EiuwAdCMGpnglxp9r6ylm+gXwQi+UPAnc/Oj1tLLJ8D1I8rLZeiO4MWsUAqH8tuBAHweT1LYSrDfJlPg== + nanoid@^3.3.8: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -6888,9 +6734,9 @@ ngx-uploadx@^7.0.0: tslib "^2.3.0" node-abi@^3.3.0: - version "3.74.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.74.0.tgz#5bfb4424264eaeb91432d2adb9da23c63a301ed0" - integrity sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w== + version "3.71.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.71.0.tgz#52d84bbcd8575efb71468fbaa1f9a49b2c242038" + integrity sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw== dependencies: semver "^7.3.5" @@ -6946,9 +6792,9 @@ node-gyp-build-optional-packages@5.2.2: detect-libc "^2.0.1" node-gyp-build@^4.3.0: - version "4.8.4" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" - integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== + version "4.8.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.3.tgz#9187216d24dbee29e44eb20d2ebf62a296bbea1a" + integrity sha512-EMS95CMJzdoSKoIiXo8pxKoL8DYxwIZXYlLmgPb8KUv794abpnLK6ynsCAWNliOjREKruYKdzbh76HHYUHX7nw== node-gyp@^11.0.0: version "11.0.0" @@ -6982,9 +6828,9 @@ node-request-interceptor@^0.6.3: strict-event-emitter "^0.1.0" node-stdlib-browser@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/node-stdlib-browser/-/node-stdlib-browser-1.3.0.tgz#6e04d149f9abfc345a2271b2820a75df0f9cd7de" - integrity sha512-g/koYzOr9Fb1Jc+tHUHlFd5gODjGn48tHexUK8q6iqOVriEgSnd3/1T7myBYc+0KBVze/7F7n65ec9rW6OD7xw== + version "1.2.1" + resolved "https://registry.yarnpkg.com/node-stdlib-browser/-/node-stdlib-browser-1.2.1.tgz#888fa104914af94143ca4d8a8980fe0ed242d2d1" + integrity sha512-dZezG3D88Lg22DwyjsDuUs7cCT/XGr8WwJgg/S3ZnkcWuPet2Tt/W1d2Eytb1Z73JpZv+XVCDI5TWv6UMRq0Gg== dependencies: assert "^2.0.0" browser-resolve "^2.0.0" @@ -6994,7 +6840,7 @@ node-stdlib-browser@^1.2.0: constants-browserify "^1.0.0" create-require "^1.1.1" crypto-browserify "^3.11.0" - domain-browser "4.22.0" + domain-browser "^4.22.0" events "^3.0.0" https-browserify "^1.0.0" isomorphic-timers-promises "^1.0.1" @@ -7321,6 +7167,23 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +p2p-media-loader-core@2.1.2, p2p-media-loader-core@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/p2p-media-loader-core/-/p2p-media-loader-core-2.1.2.tgz#27bcb7b95cba76d00a681ce21bffaf41f418514e" + integrity sha512-fAeoXANKFRnHF17a706Bd6WtMdi5a9Ac78b39TBaGBWLFGfNWVj249ysuY8c5beLlgpGNnl5J4w9VH+I1UH2nQ== + dependencies: + "@types/debug" "^4.1.12" + bittorrent-tracker "^11.2.1" + debug "^4.4.0" + nano-md5 "^1.0.5" + +p2p-media-loader-hlsjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/p2p-media-loader-hlsjs/-/p2p-media-loader-hlsjs-2.1.2.tgz#a476cce7e196426b45c30a3669c62b0d21f1616e" + integrity sha512-97iCj6qlyHbBDTDHpbyuKejBdljA0gahq0Fa2B7GhiIJNqPN4nJe0KzycE1Yket8JpDKqGGE1747PpTSz2qLaA== + dependencies: + p2p-media-loader-core "2.1.2" + pac-proxy-agent@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz#da7c3b5c4cccc6655aaafb701ae140fb23f15df2" @@ -7620,7 +7483,7 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.32, postcss@^8.4.43, postcss@^8.4.49, postcss@^8.5.1: +postcss@^8.4.32, postcss@^8.4.49, postcss@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.1.tgz#e2272a1f8a807fafa413218245630b5db10a3214" integrity sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ== @@ -7812,6 +7675,11 @@ queue-microtask@^1.2.2, queue-microtask@^1.2.3: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -8119,7 +7987,7 @@ rollup@4.30.1: "@rollup/rollup-win32-x64-msvc" "4.30.1" fsevents "~2.3.2" -rollup@^4.20.0, rollup@^4.23.0: +rollup@^4.23.0: version "4.34.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.1.tgz#dcc318abee12e23dae4003af733c1987d7baca8e" integrity sha512-iYZ/+PcdLYSGfH3S+dGahlW/RWmsqDhLgj1BT9DH/xXJ0ggZN7xkdP9wipPNjjNLczI+fmMLmTB9pye+d2r4GQ== @@ -8171,7 +8039,7 @@ rust-result@^1.0.0: dependencies: individual "^2.0.0" -rxjs@7.8.1, rxjs@^7.3.0, rxjs@^7.8.1: +rxjs@7.8.1, rxjs@^7.3.0, rxjs@^7.4.0, rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -8238,6 +8106,141 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass-embedded-android-arm64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.83.4.tgz#60af9d787e74276af95e4a1a1507567435bc61d2" + integrity sha512-tgX4FzmbVqnQmD67ZxQDvI+qFNABrboOQgwsG05E5bA/US42zGajW9AxpECJYiMXVOHmg+d81ICbjb0fsVHskw== + +sass-embedded-android-arm@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-android-arm/-/sass-embedded-android-arm-1.83.4.tgz#960953d094bf28c3e10a2e0ebd14459d4ec6e2d2" + integrity sha512-9Z4pJAOgEkXa3VDY/o+U6l5XvV0mZTJcSl0l/mSPHihjAHSpLYnOW6+KOWeM8dxqrsqTYcd6COzhanI/a++5Gw== + +sass-embedded-android-ia32@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.83.4.tgz#2293cb9920181094edfa477ba503f1f187d21624" + integrity sha512-RsFOziFqPcfZXdFRULC4Ayzy9aK6R6FwQ411broCjlOBX+b0gurjRadkue3cfUEUR5mmy0KeCbp7zVKPLTK+5Q== + +sass-embedded-android-riscv64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.83.4.tgz#84f86f2e96955a415343a2f24bae1af7bde26e5f" + integrity sha512-EHwh0nmQarBBrMRU928eTZkFGx19k/XW2YwbPR4gBVdWLkbTgCA5aGe8hTE6/1zStyx++3nDGvTZ78+b/VvvLg== + +sass-embedded-android-x64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-android-x64/-/sass-embedded-android-x64-1.83.4.tgz#8db3bb08b941889918f8435a97487cd84e7fd748" + integrity sha512-0PgQNuPWYy1jEOEPDVsV89KfqOsMLIp9CSbjBY7jRcwRhyVAcigqrUG6bDeNtojHUYKA1kU+Eh/85WxOHUOgBw== + +sass-embedded-darwin-arm64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.83.4.tgz#d0f3d82eea999ab0ae7ec8abd7fa364f0defc75e" + integrity sha512-rp2ywymWc3nymnSnAFG5R/8hvxWCsuhK3wOnD10IDlmNB7o4rzKby1c+2ZfpQGowlYGWsWWTgz8FW2qzmZsQRw== + +sass-embedded-darwin-x64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.83.4.tgz#cd2ac7f209fe823a8a5fc1a064cdfe2833680034" + integrity sha512-kLkN2lXz9PCgGfDS8Ev5YVcl/V2173L6379en/CaFuJJi7WiyPgBymW7hOmfCt4uO4R1y7CP2Uc08DRtZsBlAA== + +sass-embedded-linux-arm64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.83.4.tgz#057adf6e337357787331d40714cb8bba4a96dafe" + integrity sha512-E0zjsZX2HgESwyqw31EHtI39DKa7RgK7nvIhIRco1d0QEw227WnoR9pjH3M/ZQy4gQj3GKilOFHM5Krs/omeIA== + +sass-embedded-linux-arm@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.83.4.tgz#aea8b56f3844633f0bfaf13e0694c79511218fc0" + integrity sha512-nL90ryxX2lNmFucr9jYUyHHx21AoAgdCL1O5Ltx2rKg2xTdytAGHYo2MT5S0LIeKLa/yKP/hjuSvrbICYNDvtA== + +sass-embedded-linux-ia32@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.83.4.tgz#2cedba9f41be61ded3cede5abd16f8ec163d7f46" + integrity sha512-ew5HpchSzgAYbQoriRh8QhlWn5Kw2nQ2jHoV9YLwGKe3fwwOWA0KDedssvDv7FWnY/FCqXyymhLd6Bxae4Xquw== + +sass-embedded-linux-musl-arm64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.83.4.tgz#1c5f50c9df93abce7d5ffb4d86eed65b8ffba2f4" + integrity sha512-IzMgalf6MZOxgp4AVCgsaWAFDP/IVWOrgVXxkyhw29fyAEoSWBJH4k87wyPhEtxSuzVHLxKNbc8k3UzdWmlBFg== + +sass-embedded-linux-musl-arm@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.83.4.tgz#00f241dbc750ee73242bfde1ec5d64ef2d5d7956" + integrity sha512-0RrJRwMrmm+gG0VOB5b5Cjs7Sd+lhqpQJa6EJNEaZHljJokEfpE5GejZsGMRMIQLxEvVphZnnxl6sonCGFE/QQ== + +sass-embedded-linux-musl-ia32@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.83.4.tgz#27537a309d39f8e35a7dba34a3edc29a3ee16adf" + integrity sha512-LLb4lYbcxPzX4UaJymYXC+WwokxUlfTJEFUv5VF0OTuSsHAGNRs/rslPtzVBTvMeG9TtlOQDhku1F7G6iaDotA== + +sass-embedded-linux-musl-riscv64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.83.4.tgz#a32edf2ddb7f7d9b526e971e80cadef1e025cce8" + integrity sha512-zoKlPzD5Z13HKin1UGR74QkEy+kZEk2AkGX5RelRG494mi+IWwRuWCppXIovor9+BQb9eDWPYPoMVahwN5F7VA== + +sass-embedded-linux-musl-x64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.83.4.tgz#668b90b80bf35830c2f1ea2a47557d5e60842598" + integrity sha512-hB8+/PYhfEf2zTIcidO5Bpof9trK6WJjZ4T8g2MrxQh8REVtdPcgIkoxczRynqybf9+fbqbUwzXtiUao2GV+vQ== + +sass-embedded-linux-riscv64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.83.4.tgz#b7718df2adf1cbcb4c26609215018dd2e8bab595" + integrity sha512-83fL4n+oeDJ0Y4KjASmZ9jHS1Vl9ESVQYHMhJE0i4xDi/P3BNarm2rsKljq/QtrwGpbqwn8ujzOu7DsNCMDSHA== + +sass-embedded-linux-x64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.83.4.tgz#52e61bd582dfc56b8f638f2b9cfdb8a53db1e57e" + integrity sha512-NlnGdvCmTD5PK+LKXlK3sAuxOgbRIEoZfnHvxd157imCm/s2SYF/R28D0DAAjEViyI8DovIWghgbcqwuertXsA== + +sass-embedded-win32-arm64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.83.4.tgz#b6ca8f65177e24770e87e43ffea5868fea34de27" + integrity sha512-J2BFKrEaeSrVazU2qTjyQdAk+MvbzJeTuCET0uAJEXSKtvQ3AzxvzndS7LqkDPbF32eXAHLw8GVpwcBwKbB3Uw== + +sass-embedded-win32-ia32@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.83.4.tgz#94f8da72e253532f8d857516b99e1caf61e7b08f" + integrity sha512-uPAe9T/5sANFhJS5dcfAOhOJy8/l2TRYG4r+UO3Wp4yhqbN7bggPvY9c7zMYS0OC8tU/bCvfYUDFHYMCl91FgA== + +sass-embedded-win32-x64@1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.83.4.tgz#2179d4e2fc2f9086aecd64209a2d84f7d8e9edbe" + integrity sha512-C9fkDY0jKITdJFij4UbfPFswxoXN9O/Dr79v17fJnstVwtUojzVJWKHUXvF0Zg2LIR7TCc4ju3adejKFxj7ueA== + +sass-embedded@^1.83.4: + version "1.83.4" + resolved "https://registry.yarnpkg.com/sass-embedded/-/sass-embedded-1.83.4.tgz#9b05cdc22ae71a1b27b5996a39054ba59bebc04a" + integrity sha512-Hf2burRA/y5PGxsg6jB9UpoK/xZ6g/pgrkOcdl6j+rRg1Zj8XhGKZ1MTysZGtTPUUmiiErqzkP5+Kzp95yv9GQ== + dependencies: + "@bufbuild/protobuf" "^2.0.0" + buffer-builder "^0.2.0" + colorjs.io "^0.5.0" + immutable "^5.0.2" + rxjs "^7.4.0" + supports-color "^8.1.1" + sync-child-process "^1.0.2" + varint "^6.0.0" + optionalDependencies: + sass-embedded-android-arm "1.83.4" + sass-embedded-android-arm64 "1.83.4" + sass-embedded-android-ia32 "1.83.4" + sass-embedded-android-riscv64 "1.83.4" + sass-embedded-android-x64 "1.83.4" + sass-embedded-darwin-arm64 "1.83.4" + sass-embedded-darwin-x64 "1.83.4" + sass-embedded-linux-arm "1.83.4" + sass-embedded-linux-arm64 "1.83.4" + sass-embedded-linux-ia32 "1.83.4" + sass-embedded-linux-musl-arm "1.83.4" + sass-embedded-linux-musl-arm64 "1.83.4" + sass-embedded-linux-musl-ia32 "1.83.4" + sass-embedded-linux-musl-riscv64 "1.83.4" + sass-embedded-linux-musl-x64 "1.83.4" + sass-embedded-linux-riscv64 "1.83.4" + sass-embedded-linux-x64 "1.83.4" + sass-embedded-win32-arm64 "1.83.4" + sass-embedded-win32-ia32 "1.83.4" + sass-embedded-win32-x64 "1.83.4" + sass@1.83.1: version "1.83.1" resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.1.tgz#dee1ab94b47a6f9993d3195d36f556bcbda64846" @@ -8415,19 +8418,6 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -simple-peer@^9.11.1: - version "9.11.1" - resolved "https://registry.yarnpkg.com/simple-peer/-/simple-peer-9.11.1.tgz#9814d5723f821b778b7fb011bdefcbd1e788e6cc" - integrity sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw== - dependencies: - buffer "^6.0.3" - debug "^4.3.2" - err-code "^3.0.1" - get-browser-rtc "^1.1.0" - queue-microtask "^1.2.3" - randombytes "^2.1.0" - readable-stream "^3.6.0" - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -8628,7 +8618,29 @@ stream-http@^3.2.0: readable-stream "^3.6.0" xtend "^4.0.2" -streamx@^2.15.0, streamx@^2.17.0, streamx@^2.20.1, streamx@^2.21.0: +streamx@^2.15.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.18.0.tgz#5bc1a51eb412a667ebfdcd4e6cf6a6fc65721ac7" + integrity sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + +streamx@^2.17.0, streamx@^2.20.1: + version "2.20.2" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.2.tgz#6a8911959d6f307c19781a1d19ecd94b5f042d78" + integrity sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + +streamx@^2.21.0: version "2.22.0" resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.22.0.tgz#cd7b5e57c95aaef0ff9b2aef7905afa62ec6e4a7" integrity sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw== @@ -8897,6 +8909,18 @@ symbol-observable@4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== +sync-child-process@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/sync-child-process/-/sync-child-process-1.0.2.tgz#45e7c72e756d1243e80b547ea2e17957ab9e367f" + integrity sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA== + dependencies: + sync-message-port "^1.0.0" + +sync-message-port@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sync-message-port/-/sync-message-port-1.1.3.tgz#6055c565ee8c81d2f9ee5aae7db757e6d9088c0c" + integrity sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg== + synckit@^0.9.1: version "0.9.2" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62" @@ -9406,6 +9430,11 @@ validate-npm-package-name@^6.0.0: resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-6.0.0.tgz#3add966c853cfe36e0e8e6a762edd72ae6f1d6ac" integrity sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg== +varint@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" + integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== + "video.js@^6 || ^7", video.js@^7.19.2: version "7.21.6" resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.21.6.tgz#a42d817c42d8d91538b72197a0874aeb01c76ce9" @@ -9437,10 +9466,10 @@ videojs-vtt.js@^0.15.5: dependencies: global "^4.3.1" -vite-plugin-checker@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.7.2.tgz#093ffdf9ccf51b2c9eab7101480bd0217ae99536" - integrity sha512-xeYeJbG0gaCaT0QcUC4B2Zo4y5NR8ZhYenc5gPbttrZvraRFwkEADCYwq+BfEHl9zYz7yf85TxsiGoYwyyIjhw== +vite-plugin-checker@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.8.0.tgz#33419857a623b35c9483e4f603d4ca8b6984acde" + integrity sha512-UA5uzOGm97UvZRTdZHiQVYFnd86AVn8EVaD4L3PoVzxH+IZSfaAw14WGFwX9QS23UW3lV/5bVKZn6l0w+q9P0g== dependencies: "@babel/code-frame" "^7.12.13" ansi-escapes "^4.3.0" @@ -9457,15 +9486,15 @@ vite-plugin-checker@^0.7.2: vscode-languageserver-textdocument "^1.0.1" vscode-uri "^3.0.2" -vite-plugin-node-polyfills@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.22.0.tgz#d0afcf82eb985fc02244620d7cec1ddd1c6e0864" - integrity sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA== +vite-plugin-node-polyfills@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.23.0.tgz#99d0d1524fa75ce5c7bb1fc8af30283379e9c684" + integrity sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w== dependencies: "@rollup/plugin-inject" "^5.0.5" node-stdlib-browser "^1.2.0" -vite@6.0.11: +vite@6.0.11, vite@^6.0.11: version "6.0.11" resolved "https://registry.yarnpkg.com/vite/-/vite-6.0.11.tgz#224497e93e940b34c3357c9ebf2ec20803091ed8" integrity sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg== @@ -9476,17 +9505,6 @@ vite@6.0.11: optionalDependencies: fsevents "~2.3.3" -vite@^5.3.1: - version "5.4.14" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.14.tgz#ff8255edb02134df180dcfca1916c37a6abe8408" - integrity sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA== - dependencies: - esbuild "^0.21.3" - postcss "^8.4.43" - rollup "^4.20.0" - optionalDependencies: - fsevents "~2.3.3" - vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" diff --git a/packages/node-utils/src/crypto.ts b/packages/node-utils/src/crypto.ts index 388d7557468..f4199f88416 100644 --- a/packages/node-utils/src/crypto.ts +++ b/packages/node-utils/src/crypto.ts @@ -7,3 +7,7 @@ export function sha256 (str: string | Buffer, encoding: BinaryToTextEncoding = ' export function sha1 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') { return createHash('sha1').update(str).digest(encoding) } + +export function md5 (str: string | Buffer, encoding: BinaryToTextEncoding = 'hex') { + return createHash('md5').update(str).digest(encoding) +} diff --git a/packages/tests/src/api/object-storage/videos.ts b/packages/tests/src/api/object-storage/videos.ts index bbfaab3ebe8..3c9e89e1aa9 100644 --- a/packages/tests/src/api/object-storage/videos.ts +++ b/packages/tests/src/api/object-storage/videos.ts @@ -20,7 +20,7 @@ import { generateHighBitrateVideo } from '@tests/shared/generate.js' import { MockObjectStorageProxy } from '@tests/shared/mock-servers/mock-object-storage.js' import { SQLCommand } from '@tests/shared/sql-command.js' import { checkPlaylistInfohash } from '@tests/shared/streaming-playlists.js' -import { checkWebTorrentWorks } from '@tests/shared/webtorrent.js' +import { checkWebTorrentWorks } from '@tests/shared/p2p.js' import bytes from 'bytes' import { expect } from 'chai' import { stat } from 'fs/promises' diff --git a/packages/tests/src/api/server/tracker.ts b/packages/tests/src/api/server/tracker.ts index 58e99ff65b7..9f703e6c32d 100644 --- a/packages/tests/src/api/server/tracker.ts +++ b/packages/tests/src/api/server/tracker.ts @@ -7,7 +7,7 @@ import { PeerTubeServer, setAccessTokensToServers } from '@peertube/peertube-server-commands' -import { magnetUriDecode, magnetUriEncode } from '@tests/shared/webtorrent.js' +import { magnetUriDecode, magnetUriEncode } from '@tests/shared/p2p.js' import WebTorrent from 'webtorrent' describe('Test tracker', function () { diff --git a/packages/tests/src/api/transcoding/transcoder.ts b/packages/tests/src/api/transcoding/transcoder.ts index fb8099fd862..e1f9e05e967 100644 --- a/packages/tests/src/api/transcoding/transcoder.ts +++ b/packages/tests/src/api/transcoding/transcoder.ts @@ -18,7 +18,7 @@ import { waitJobs } from '@peertube/peertube-server-commands' import { canDoQuickTranscode } from '@peertube/peertube-server/core/lib/transcoding/transcoding-quick-transcode.js' -import { checkWebTorrentWorks } from '@tests/shared/webtorrent.js' +import { checkWebTorrentWorks } from '@tests/shared/p2p.js' import { expect } from 'chai' describe('Test video transcoding', function () { diff --git a/packages/tests/src/api/videos/multiple-servers.ts b/packages/tests/src/api/videos/multiple-servers.ts index f2fd483a7da..de021dcf959 100644 --- a/packages/tests/src/api/videos/multiple-servers.ts +++ b/packages/tests/src/api/videos/multiple-servers.ts @@ -17,7 +17,7 @@ import { import { dateIsValid, testImageGeneratedByFFmpeg } from '@tests/shared/checks.js' import { checkTmpIsEmpty } from '@tests/shared/directories.js' import { checkVideoFilesWereRemoved, completeVideoCheck, saveVideoInServers } from '@tests/shared/videos.js' -import { checkWebTorrentWorks } from '@tests/shared/webtorrent.js' +import { checkWebTorrentWorks } from '@tests/shared/p2p.js' import Bluebird from 'bluebird' import { expect } from 'chai' import request from 'supertest' diff --git a/packages/tests/src/api/videos/video-static-file-privacy.ts b/packages/tests/src/api/videos/video-static-file-privacy.ts index 5577dfdb550..0b5c3f27be2 100644 --- a/packages/tests/src/api/videos/video-static-file-privacy.ts +++ b/packages/tests/src/api/videos/video-static-file-privacy.ts @@ -16,7 +16,7 @@ import { } from '@peertube/peertube-server-commands' import { expectStartWith } from '@tests/shared/checks.js' import { checkVideoFileTokenReinjection } from '@tests/shared/streaming-playlists.js' -import { magnetUriDecode, parseTorrentVideo } from '@tests/shared/webtorrent.js' +import { magnetUriDecode, parseTorrentVideo } from '@tests/shared/p2p.js' import { expect } from 'chai' describe('Test video static file privacy', function () { diff --git a/packages/tests/src/cli/update-host.ts b/packages/tests/src/cli/update-host.ts index 30a7b17e4cf..10022086fd3 100644 --- a/packages/tests/src/cli/update-host.ts +++ b/packages/tests/src/cli/update-host.ts @@ -12,7 +12,7 @@ import { setDefaultVideoChannel, waitJobs } from '@peertube/peertube-server-commands' -import { parseTorrentVideo } from '@tests/shared/webtorrent.js' +import { parseTorrentVideo } from '@tests/shared/p2p.js' import { VideoPlaylistPrivacy } from '@peertube/peertube-models' describe('Test update host CLI', function () { diff --git a/packages/tests/src/shared/live.ts b/packages/tests/src/shared/live.ts index 179e49336c5..7507098e8db 100644 --- a/packages/tests/src/shared/live.ts +++ b/packages/tests/src/shared/live.ts @@ -2,14 +2,13 @@ import { getVideoStreamDimensionsInfo, getVideoStreamFPS } from '@peertube/peertube-ffmpeg' import { LiveVideo, VideoResolution, VideoStreamingPlaylistType } from '@peertube/peertube-models' -import { sha1 } from '@peertube/peertube-node-utils' import { ObjectStorageCommand, PeerTubeServer } from '@peertube/peertube-server-commands' import { expect } from 'chai' import { pathExists } from 'fs-extra/esm' import { readdir } from 'fs/promises' import { join } from 'path' import { SQLCommand } from './sql-command.js' -import { checkLiveSegmentHash, checkResolutionsInMasterPlaylist } from './streaming-playlists.js' +import { checkLiveSegmentHash, checkPlaylistInfohash, checkResolutionsInMasterPlaylist } from './streaming-playlists.js' async function checkLiveCleanup (options: { server: PeerTubeServer @@ -169,13 +168,10 @@ async function testLiveVideoResolutions (options: { hlsPlaylist, withRetry: !!objectStorage // With object storage, the request may fail because of inconsistent data in S3 }) + } - if (originServer.internalServerNumber === server.internalServerNumber) { - const infohash = sha1(`2${hlsPlaylist.playlistUrl}+V${i}`) - const dbInfohashes = await sqlCommand.getPlaylistInfohash(hlsPlaylist.id) - - expect(dbInfohashes).to.include(infohash) - } + if (originServer.internalServerNumber === server.internalServerNumber) { + await checkPlaylistInfohash({ video, sqlCommand, files: resolutions.map(r => ({ resolution: { id: r } })) }) } } } diff --git a/packages/tests/src/shared/webtorrent.ts b/packages/tests/src/shared/p2p.ts similarity index 100% rename from packages/tests/src/shared/webtorrent.ts rename to packages/tests/src/shared/p2p.ts diff --git a/packages/tests/src/shared/streaming-playlists.ts b/packages/tests/src/shared/streaming-playlists.ts index 35229db86e7..83f51788c3d 100644 --- a/packages/tests/src/shared/streaming-playlists.ts +++ b/packages/tests/src/shared/streaming-playlists.ts @@ -10,14 +10,14 @@ import { VideoStreamingPlaylist, VideoStreamingPlaylistType } from '@peertube/peertube-models' -import { sha1, sha256 } from '@peertube/peertube-node-utils' +import { md5, sha256 } from '@peertube/peertube-node-utils' import { makeRawRequest, PeerTubeServer } from '@peertube/peertube-server-commands' import { expect } from 'chai' import { basename, dirname, join } from 'path' import { expectStartWith } from './checks.js' +import { checkWebTorrentWorks } from './p2p.js' import { SQLCommand } from './sql-command.js' -import { hlsInfohashExist } from './tracker.js' -import { checkWebTorrentWorks } from './webtorrent.js' +import { checkTrackerInfohash } from './tracker.js' export async function checkSegmentHash (options: { server: PeerTubeServer @@ -72,16 +72,25 @@ export async function checkLiveSegmentHash (options: { export async function checkPlaylistInfohash (options: { video: VideoDetails sqlCommand: SQLCommand - files: unknown[] + files: { resolution: { id: number } }[] }) { const { sqlCommand, video, files } = options const hls = getHLS(video) + const version = 2 + const hash = (input: string) => btoa(md5(input, 'binary').slice(1)) + for (let i = 0; i < files.length; i++) { - const infohash = sha1(`${2 + hls.playlistUrl}+V${i}`) + const str = files[0].resolution.id === VideoResolution.H_NOVIDEO && files.length !== 0 + ? `v${version}-${hls.playlistUrl}-secondary-0` + : `v${version}-${hls.playlistUrl}-main-${i}` + + const infohash = hash(str) const dbInfohashes = await sqlCommand.getPlaylistInfohash(hls.id) expect(dbInfohashes).to.include(infohash) + + await checkTrackerInfohash(video.account.host, infohash) } } @@ -298,40 +307,33 @@ export async function completeCheckHlsPlaylist (options: { const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl, token }) - let i = 0 for (const resolution of hlsResolutions) { expect(masterPlaylist).to.contain(`${resolution}.m3u8`) expect(masterPlaylist).to.contain(`${resolution}.m3u8`) - - const url = 'http://' + videoDetails.account.host - await hlsInfohashExist(url, hlsPlaylist.playlistUrl, i) - - i++ } } // Check resolution playlists - { - for (const resolution of hlsResolutions) { - const file = hlsFiles.find(f => f.resolution.id === resolution) - const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8' + for (const resolution of hlsResolutions) { + const file = hlsFiles.find(f => f.resolution.id === resolution) + const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8' - let url: string - if (objectStorageBaseUrl && requiresAuth) { - url = `${baseUrl}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}` - } else if (objectStorageBaseUrl) { - url = `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}` - } else { - url = `${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}` - } + let url: string + if (objectStorageBaseUrl && requiresAuth) { + url = `${baseUrl}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}` + } else if (objectStorageBaseUrl) { + url = `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}` + } else { + url = `${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}` + } - const subPlaylist = await server.streamingPlaylists.get({ url, token }) + const subPlaylist = await server.streamingPlaylists.get({ url, token }) - expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`)) - expect(subPlaylist).to.contain(basename(file.fileUrl)) - } + expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`)) + expect(subPlaylist).to.contain(basename(file.fileUrl)) } + // Segment hash { let baseUrlAndPath: string if (objectStorageBaseUrl && requiresAuth) { @@ -353,6 +355,14 @@ export async function completeCheckHlsPlaylist (options: { }) } } + + // Info hashed + if (isOrigin) { + const sqlCommand = new SQLCommand(server) + + await checkPlaylistInfohash({ video: videoDetails, sqlCommand, files: hlsFiles }) + await sqlCommand.cleanup() + } } } diff --git a/packages/tests/src/shared/tracker.ts b/packages/tests/src/shared/tracker.ts index 6ab43045623..30352fac87f 100644 --- a/packages/tests/src/shared/tracker.ts +++ b/packages/tests/src/shared/tracker.ts @@ -1,14 +1,11 @@ -import { expect } from 'chai' -import { sha1 } from '@peertube/peertube-node-utils' import { makeGetRequest } from '@peertube/peertube-server-commands' +import { expect } from 'chai' -async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, fileNumber: number) { +export async function checkTrackerInfohash (serverUrl: string, infohash: string) { const path = '/tracker/announce' - const infohash = sha1(`2${masterPlaylistUrl}+V${fileNumber}`) - // From bittorrent-tracker - const infohashBinary = escape(Buffer.from(infohash, 'hex').toString('binary')).replace(/[@*/+]/g, function (char) { + const infohashBinary = escape(Buffer.from(infohash, 'utf-8').toString('binary')).replace(/[@*/+]/g, function (char) { return '%' + char.charCodeAt(0).toString(16).toUpperCase() }) @@ -21,7 +18,3 @@ async function hlsInfohashExist (serverUrl: string, masterPlaylistUrl: string, f expect(res.text).to.not.contain('failure') } - -export { - hlsInfohashExist -} diff --git a/packages/tests/src/shared/videos.ts b/packages/tests/src/shared/videos.ts index 723e169bd63..6a66962d3b5 100644 --- a/packages/tests/src/shared/videos.ts +++ b/packages/tests/src/shared/videos.ts @@ -29,7 +29,7 @@ import { tmpdir } from 'os' import { basename, join } from 'path' import { dateIsValid, expectStartWith, testImageGeneratedByFFmpeg } from './checks.js' import { completeCheckHlsPlaylist } from './streaming-playlists.js' -import { checkWebTorrentWorks } from './webtorrent.js' +import { checkWebTorrentWorks } from './p2p.js' export async function completeWebVideoFilesCheck (options: { server: PeerTubeServer diff --git a/scripts/build/client.sh b/scripts/build/client.sh index 357925e7f6a..41ab8b84769 100755 --- a/scripts/build/client.sh +++ b/scripts/build/client.sh @@ -47,9 +47,12 @@ languages=( ["kab"]="kab" ) -cd client -rm -rf ./dist +rm -rf ./client/dist + +npm run build:embed + +cd client # Don't build other languages if --light arg is provided if [ -z ${1+x} ] || ([ "$1" != "--light" ] && [ "$1" != "--analyze-bundle" ]); then @@ -88,7 +91,5 @@ else --configuration production --stats-json $additionalParams fi -cd ../ && npm run build:embed && cd client/ - # Copy runtime locales cp -r "./src/locale" "./dist/locale" diff --git a/scripts/build/embed.sh b/scripts/build/embed.sh index 70c5fc4cfc0..6c88f750515 100755 --- a/scripts/build/embed.sh +++ b/scripts/build/embed.sh @@ -2,4 +2,6 @@ set -eu +(cd client/src/standalone/player && npm run build) + cd client && ./node_modules/.bin/vite -c ./src/standalone/videos/vite.config.mjs build --mode=production diff --git a/scripts/client-report.sh b/scripts/client-report.sh index c41e001503e..5ecb71124e8 100755 --- a/scripts/client-report.sh +++ b/scripts/client-report.sh @@ -3,5 +3,6 @@ set -eu npm run concurrently -- -k \ + "cd client/src/standalone/player/ && npx vite-bundle-visualizer" \ "cd client/src/standalone/videos/ && npx vite-bundle-visualizer" \ "cd client && npx esbuild-visualizer --metadata ./dist/en-US/stats.json --open" diff --git a/scripts/dev/client.sh b/scripts/dev/client.sh index 2f0b367d4bb..854bedec773 100755 --- a/scripts/dev/client.sh +++ b/scripts/dev/client.sh @@ -2,21 +2,27 @@ set -eu +(cd client/src/standalone/player && npm run build) + clientConfiguration="hmr" if [ ! -z ${2+x} ] && [ "$2" = "--ar-locale" ]; then clientConfiguration="ar-locale" fi +embedCommand="cd client/src/standalone/player && npm run dev" clientCommand="cd client && node --max_old_space_size=4096 node_modules/.bin/ng serve --proxy-config proxy.config.json --hmr --configuration $clientConfiguration --host 0.0.0.0 --port 3000" serverCommand="NODE_ENV=dev node dist/server" if [ ! -z ${1+x} ] && [ "$1" = "--skip-server" ]; then - eval $clientCommand + node node_modules/.bin/concurrently -k \ + "$embedCommand" \ + "$clientCommand" else npm run build:server node node_modules/.bin/concurrently -k \ + "$embedCommand" \ "$clientCommand" \ "$serverCommand" fi diff --git a/scripts/dev/embed.sh b/scripts/dev/embed.sh index 845036eff7a..b13db57aa75 100755 --- a/scripts/dev/embed.sh +++ b/scripts/dev/embed.sh @@ -3,7 +3,9 @@ set -eu npm run build:server -- --incremental +(cd client/src/standalone/player && npm run build) npm run concurrently -- -k \ + "cd client/src/standalone/player && npm run dev" \ "cd client && ./node_modules/.bin/vite -c ./src/standalone/videos/vite.config.mjs dev" \ "NODE_ENV=dev npm start" diff --git a/server/core/controllers/tracker.ts b/server/core/controllers/tracker.ts index dbcc884d29a..9f137b67acd 100644 --- a/server/core/controllers/tracker.ts +++ b/server/core/controllers/tracker.ts @@ -25,11 +25,21 @@ const trackerServer = new TrackerServer({ http: false, udp: false, ws: false, - filter: async function (infoHash, params, cb) { + filter: async function (infoHashArg, params, cb) { if (CONFIG.TRACKER.ENABLED === false) { return cb(new Error('Tracker is disabled on this instance.')) } + let infoHash: string + + try { + infoHash = Buffer.from(infoHashArg, 'hex').toString('binary') + } catch (err) { + logger.error('Cannot parse infohash in tracker filter', { err }) + + return cb(new Error('Invalid infoHash.')) + } + let ip: string if (params.type === 'ws') { @@ -126,8 +136,7 @@ function createWebsocketTrackerServer (app: express.Application) { // --------------------------------------------------------------------------- export { - trackerRouter, - createWebsocketTrackerServer + createWebsocketTrackerServer, trackerRouter } // --------------------------------------------------------------------------- diff --git a/server/core/initializers/constants.ts b/server/core/initializers/constants.ts index d748a1f1cb8..8c86281d203 100644 --- a/server/core/initializers/constants.ts +++ b/server/core/initializers/constants.ts @@ -423,7 +423,7 @@ export const CONSTRAINTS_FIELDS = { } }, EXTNAME: [] as string[], - INFO_HASH: { min: 40, max: 40 }, // Length, info hash is 20 bytes length but we represent it in hexadecimal so 20 * 2 + INFO_HASH: { min: 10, max: 100 }, DURATION: { min: 0 }, // Number TAGS: { min: 0, max: 5 }, // Number of total tags TAG: { min: 2, max: 30 }, // Length @@ -1112,7 +1112,9 @@ export const TRACKER_RATE_LIMITS = { BLOCK_IP_LIFETIME: parseDurationToMs('3 minutes') } -export const P2P_MEDIA_LOADER_PEER_VERSION = 2 +// We use -2 instead of 2 because of historical reason +// When p2p-media-loader bumps to v3, we'll be able to switch to it +export const P2P_MEDIA_LOADER_PEER_VERSION = -2 // --------------------------------------------------------------------------- diff --git a/server/core/lib/activitypub/videos/shared/object-to-model-attributes.ts b/server/core/lib/activitypub/videos/shared/object-to-model-attributes.ts index 7d6279b81b4..1e23216d07e 100644 --- a/server/core/lib/activitypub/videos/shared/object-to-model-attributes.ts +++ b/server/core/lib/activitypub/videos/shared/object-to-model-attributes.ts @@ -167,7 +167,7 @@ export function getStreamingPlaylistAttributesFromObject (video: MVideoId, video for (const playlistUrlObject of playlistUrls) { const segmentsSha256UrlObject = playlistUrlObject.tag.find(isAPPlaylistSegmentHashesUrlObject) - const files: unknown[] = playlistUrlObject.tag.filter(u => isAPVideoUrlObject(u)) + const files = playlistUrlObject.tag.filter(u => isAPVideoUrlObject(u)) const attribute = { type: VideoStreamingPlaylistType.HLS, diff --git a/server/core/lib/hls.ts b/server/core/lib/hls.ts index 87f503eeadf..7e3cb8b265b 100644 --- a/server/core/lib/hls.ts +++ b/server/core/lib/hls.ts @@ -24,18 +24,23 @@ import { VideoPathManager } from './video-path-manager.js' const lTags = loggerTagsFactory('hls') export async function updateStreamingPlaylistsInfohashesIfNeeded () { - const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() + const playlistsToUpdateIds = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() // Use separate SQL queries, because we could have many videos to update - for (const playlist of playlistsToUpdate) { - await sequelizeTypescript.transaction(async t => { - const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t) + for (const playlistId of playlistsToUpdateIds) { + try { + await sequelizeTypescript.transaction(async t => { + const playlist = await VideoStreamingPlaylistModel.loadWithVideo(playlistId, t) + const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlistId, t) - playlist.assignP2PMediaLoaderInfoHashes(playlist.Video, videoFiles) - playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION + playlist.assignP2PMediaLoaderInfoHashes(playlist.Video, videoFiles) + playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION - await playlist.save({ transaction: t }) - }) + await playlist.save({ transaction: t }) + }) + } catch (err) { + logger.error(`Cannot update streaming playlist infohash of playlist id ${playlistId}`, { err }) + } } } diff --git a/server/core/lib/live/shared/muxing-session.ts b/server/core/lib/live/shared/muxing-session.ts index ca6743103de..45d37cf5f93 100644 --- a/server/core/lib/live/shared/muxing-session.ts +++ b/server/core/lib/live/shared/muxing-session.ts @@ -227,7 +227,7 @@ class MuxingSession extends EventEmitter { this.streamingPlaylist.playlistUrl = url } - this.streamingPlaylist.assignP2PMediaLoaderInfoHashes(this.videoLive.Video, this.allResolutions) + this.streamingPlaylist.assignP2PMediaLoaderInfoHashes(this.videoLive.Video, this.allResolutions.map(r => ({ height: r }))) await this.streamingPlaylist.save() } catch (err) { diff --git a/server/core/models/video/video-file.ts b/server/core/models/video/video-file.ts index 9979078ec5b..2e9ce534fbf 100644 --- a/server/core/models/video/video-file.ts +++ b/server/core/models/video/video-file.ts @@ -354,23 +354,17 @@ export class VideoFileModel extends SequelizeModel { const query = { include: [ { - model: VideoModel.unscoped(), + model: VideoStreamingPlaylistModel.unscoped(), required: true, - include: [ - { - model: VideoStreamingPlaylistModel.unscoped(), - required: true, - where: { - id: streamingPlaylistId - } - } - ] + where: { + id: streamingPlaylistId + } } ], transaction } - return VideoFileModel.findAll(query) + return VideoFileModel.findAll(query) } static getStats () { diff --git a/server/core/models/video/video-streaming-playlist.ts b/server/core/models/video/video-streaming-playlist.ts index 0f463969b4c..8742e57900b 100644 --- a/server/core/models/video/video-streaming-playlist.ts +++ b/server/core/models/video/video-streaming-playlist.ts @@ -5,7 +5,7 @@ import { type FileStorageType, type VideoStreamingPlaylistType_Type } from '@peertube/peertube-models' -import { sha1 } from '@peertube/peertube-node-utils' +import { md5 } from '@peertube/peertube-node-utils' import { logger } from '@server/helpers/logger.js' import { CONFIG } from '@server/initializers/config.js' import { getHLSPrivateFileUrl, getObjectStoragePublicFileUrl } from '@server/lib/object-storage/index.js' @@ -141,12 +141,21 @@ export class VideoStreamingPlaylistModel extends SequelizeModel btoa(md5(input, 'binary').slice(1)) + // https://github.com/Novage/p2p-media-loader/blob/master/p2p-media-loader-core/lib/p2p-media-manager.ts#L115 for (let i = 0; i < files.length; i++) { - hashes.push(sha1(`${P2P_MEDIA_LOADER_PEER_VERSION}${playlistUrl}+V${i}`)) + hashes.push(hash(`v${version}-${playlistUrl}-main-${i}`)) + } + + // Audio only stream + if (files.some(f => f.height === 0)) { + hashes.push(hash(`v${version}-${playlistUrl}-secondary-0`)) } logger.debug('Assigned P2P Media Loader info hashes', { playlistUrl, hashes }) @@ -154,22 +163,18 @@ export class VideoStreamingPlaylistModel extends SequelizeModel r.id) } static loadWithVideoAndFiles (id: number) { @@ -188,14 +193,15 @@ export class VideoStreamingPlaylistModel extends SequelizeModel(id, options) } - static loadWithVideo (id: number) { + static loadWithVideo (id: number, transaction?: Transaction) { const options = { include: [ { model: VideoModel.unscoped(), required: true } - ] + ], + transaction } return VideoStreamingPlaylistModel.findByPk(id, options) @@ -242,7 +248,7 @@ export class VideoStreamingPlaylistModel extends SequelizeModel