From 8c83592d89ca32ba82832b6285e71b42f31fe814 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Aug 2025 08:30:00 +0200 Subject: [PATCH] Upgrade to videojs v8 --- client/package.json | 3 +- .../app/+video-watch/video-watch.component.ts | 5 +- .../standalone/player/src/peertube-player.ts | 40 ++-- .../player/src/sass/shared/context-menu.scss | 2 +- .../player/src/sass/shared/settings-menu.scss | 8 +- .../player/src/shared/bezels/bezels-plugin.ts | 6 +- .../player/src/shared/bezels/pause-bezel.ts | 8 +- .../shared/context-menu/context-menu-item.ts | 8 +- .../context-menu/context-menu-plugin.ts | 14 +- .../src/shared/context-menu/context-menu.ts | 17 +- .../control-bar/caption-toggle-button.ts | 8 +- .../src/shared/control-bar/chapters-plugin.ts | 17 +- .../control-bar/next-previous-video-button.ts | 8 +- .../src/shared/control-bar/p2p-info-button.ts | 7 +- .../control-bar/peertube-link-button.ts | 10 +- .../control-bar/peertube-live-display.ts | 12 +- .../progress-bar-marker-component.ts | 8 +- .../shared/control-bar/storyboard-plugin.ts | 22 +- .../src/shared/control-bar/theater-button.ts | 8 +- .../src/shared/control-bar/time-tooltip.ts | 7 +- .../shared/dock/peertube-dock-component.ts | 7 +- .../src/shared/dock/peertube-dock-plugin.ts | 7 +- .../shared/hotkeys/peertube-hotkeys-plugin.ts | 7 +- .../src/shared/metrics/metrics-plugin.ts | 12 +- .../shared/mobile/peertube-mobile-buttons.ts | 4 +- .../shared/mobile/peertube-mobile-plugin.ts | 28 +-- .../nsfw/peertube-nsfw-details-component.ts | 7 +- .../nsfw/peertube-nsfw-info-component.ts | 7 +- .../src/shared/nsfw/peertube-nsfw-plugin.ts | 9 +- .../src/shared/p2p-media-loader/hls-plugin.ts | 27 +-- .../p2p-media-loader-plugin.ts | 7 +- .../src/shared/peertube/peertube-plugin.ts | 45 ++-- .../control-bar-options-builder.ts | 11 +- .../hls-options-builder.ts | 10 +- .../src/shared/playlist/playlist-button.ts | 10 +- .../src/shared/playlist/playlist-menu-item.ts | 17 +- .../src/shared/playlist/playlist-menu.ts | 12 +- .../src/shared/playlist/playlist-plugin.ts | 8 +- .../peertube-resolutions-plugin.ts | 7 +- .../src/shared/settings/menu-focus-fixed.ts | 8 +- .../shared/settings/resolution-menu-button.ts | 10 +- .../shared/settings/resolution-menu-item.ts | 9 +- .../src/shared/settings/settings-dialog.ts | 7 +- .../shared/settings/settings-menu-button.ts | 23 +- .../src/shared/settings/settings-menu-item.ts | 39 ++-- .../shared/settings/settings-panel-child.ts | 6 +- .../src/shared/settings/settings-panel.ts | 6 +- .../player/src/shared/stats/stats-card.ts | 16 +- .../player/src/shared/stats/stats-plugin.ts | 5 +- .../player/src/shared/upnext/end-card.ts | 9 +- .../player/src/shared/upnext/upnext-plugin.ts | 7 +- .../src/shared/web-video/web-video-plugin.ts | 11 +- .../src/types/peertube-player-options.ts | 4 +- .../src/types/peertube-videojs-typings.ts | 212 ++++++++++++++---- client/src/standalone/videos/embed.ts | 4 +- client/yarn.lock | 190 +++++++--------- locales/fr-FR/translation.json | 0 57 files changed, 573 insertions(+), 453 deletions(-) create mode 100644 locales/fr-FR/translation.json diff --git a/client/package.json b/client/package.json index 04990d473..ca7236d66 100644 --- a/client/package.json +++ b/client/package.json @@ -67,7 +67,6 @@ "@types/qrcode": "^1.5.5", "@types/sanitize-html": "2.11.0", "@types/sha.js": "^2.4.0", - "@types/video.js": "^7.3.40", "@wdio/browserstack-service": "^9.12.7", "@wdio/cli": "^9.12.7", "@wdio/local-runner": "^9.12.7", @@ -109,7 +108,7 @@ "type-fest": "^4.37.0", "typescript": "~5.7.3", "ua-parser-js": "^2.0.3", - "video.js": "^7.19.2", + "video.js": "^8.23.4", "vite": "^6.0.11", "vite-plugin-checker": "^0.9.3", "vite-plugin-node-polyfills": "^0.23.0", diff --git a/client/src/app/+video-watch/video-watch.component.ts b/client/src/app/+video-watch/video-watch.component.ts index b6ba33443..e5b34857d 100644 --- a/client/src/app/+video-watch/video-watch.component.ts +++ b/client/src/app/+video-watch/video-watch.component.ts @@ -52,7 +52,8 @@ import { PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerMode, - videojs + videojs, + VideojsPlayer } from '@peertube/player' import { logger } from '@root-helpers/logger' import { isP2PEnabled, videoRequiresFileToken, videoRequiresUserAuth } from '@root-helpers/video' @@ -885,7 +886,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { return loggedInOrAnonymousUser?.autoPlayNextVideo }, - isSuspended: (player: videojs.Player) => { + isSuspended: (player: VideojsPlayer) => { return !isXPercentInViewport(player.el() as HTMLElement, 80) }, diff --git a/client/src/standalone/player/src/peertube-player.ts b/client/src/standalone/player/src/peertube-player.ts index f3d38ab39..f43426513 100644 --- a/client/src/standalone/player/src/peertube-player.ts +++ b/client/src/standalone/player/src/peertube-player.ts @@ -5,7 +5,7 @@ import { TranslationsManager } from '@root-helpers/translations-manager' import { copyToClipboard } from '@root-helpers/utils' import { buildVideoOrPlaylistEmbed } from '@root-helpers/video' import { isMobile } from '@root-helpers/web-browser' -import videojs, { VideoJsPlayer } from 'video.js' +import videojs from 'video.js' import { saveAverageBandwidth } from './peertube-player-local-storage' import './shared/bezels/bezels-plugin' import './shared/context-menu' @@ -20,12 +20,12 @@ import './shared/control-bar/theater-button' import './shared/control-bar/time-tooltip' import './shared/dock/peertube-dock-component' import './shared/dock/peertube-dock-plugin' -import './shared/nsfw/peertube-nsfw-info-component' -import './shared/nsfw/peertube-nsfw-plugin' import './shared/hotkeys/peertube-hotkeys-plugin' import './shared/metrics/metrics-plugin' import './shared/mobile/peertube-mobile-buttons' import './shared/mobile/peertube-mobile-plugin' +import './shared/nsfw/peertube-nsfw-info-component' +import './shared/nsfw/peertube-nsfw-plugin' import './shared/p2p-media-loader/hls-plugin' import './shared/p2p-media-loader/p2p-media-loader-plugin' import './shared/peertube/peertube-plugin' @@ -45,7 +45,15 @@ import './shared/stats/stats-plugin' import './shared/upnext/end-card' import './shared/upnext/upnext-plugin' import './shared/web-video/web-video-plugin' -import { PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions, PlayerNetworkInfo, VideoJSPluginOptions } from './types' +import { + PeerTubePlayerConstructorOptions, + PeerTubePlayerLoadOptions, + PlayerNetworkInfo, + VideojsAutoplay, + VideojsPlayer, + VideojsPlayerOptions, + VideoJSPluginOptions +} from './types' const CaptionsButton = videojs.getComponent('CaptionsButton') as any // Change Captions to Subtitles/CC @@ -70,7 +78,7 @@ export class PeerTubePlayer { private videojsDecodeErrors = 0 - private player: VideoJsPlayer + private player: VideojsPlayer private currentLoadOptions: PeerTubePlayerLoadOptions @@ -210,7 +218,7 @@ export class PeerTubePlayer { this.getVideojsOptions() ) - this.player = videojs(this.options.playerElement(), videojsOptions) + this.player = videojs(this.options.playerElement(), videojsOptions) as VideojsPlayer this.player.ready(() => { if (!isNaN(+this.options.playbackRate)) { @@ -233,7 +241,7 @@ export class PeerTubePlayer { this.player.on('video-change', () => alreadyFallback = false) this.player.on('error', () => handleError()) - this.player.on('network-info', (_, data: PlayerNetworkInfo) => { + this.player.on('network-info', (_: any, data: PlayerNetworkInfo) => { if (data.source !== 'p2p-media-loader' || isNaN(data.bandwidthEstimate)) return saveAverageBandwidth(data.bandwidthEstimate) @@ -366,7 +374,7 @@ export class PeerTubePlayer { }) } - getVideojsOptions (): videojs.PlayerOptions { + getVideojsOptions (): VideojsPlayerOptions { const html5 = { preloadTextTracks: false, // Prevent a bug on iOS where the text tracks added by peertube plugin are removed on play @@ -422,7 +430,7 @@ export class PeerTubePlayer { html5, // We don't use text track settings for now - textTrackSettings: false as any, // FIXME: typings + textTrackSettings: false, controls: this.options.controls !== undefined ? this.options.controls : true, loop: this.options.loop !== undefined ? this.options.loop : false, @@ -441,18 +449,20 @@ export class PeerTubePlayer { plugins, controlBar: { - children: controlBarOptionsBuilder.getChildrenOptions() as any // FIXME: typings + children: controlBarOptionsBuilder.getChildrenOptions() }, language: this.options.language && !isDefaultLocale(this.options.language) ? this.options.language - : undefined - } + : undefined, + + enableSmoothSeeking: true + } satisfies VideojsPlayerOptions return videojsOptions } - private getAutoPlayValue (autoplay: boolean): videojs.Autoplay { + private getAutoPlayValue (autoplay: boolean): VideojsAutoplay { if (autoplay !== true) return false return this.currentLoadOptions.forceAutoplay @@ -500,14 +510,14 @@ export class PeerTubePlayer { const player = this.player const shortUUID = self.currentLoadOptions.videoShortUUID - const isLoopEnabled = player.options_['loop'] + const isLoopEnabled = player.options_.loop const items = [ { icon: 'repeat', label: player.localize('Play in loop') + (isLoopEnabled ? '' : ''), listener: function () { - player.options_['loop'] = !isLoopEnabled + player.options_.loop = !isLoopEnabled } }, { diff --git a/client/src/standalone/player/src/sass/shared/context-menu.scss b/client/src/standalone/player/src/sass/shared/context-menu.scss index 21dd487df..d65568ac8 100644 --- a/client/src/standalone/player/src/sass/shared/context-menu.scss +++ b/client/src/standalone/player/src/sass/shared/context-menu.scss @@ -25,8 +25,8 @@ $context-menu-width: 350px; cursor: pointer; font-size: 1em; padding: 8px 16px; - text-align: start; text-transform: none; + justify-content: flex-start; &:hover { background-color: rgba(255, 255, 255, 0.2); diff --git a/client/src/standalone/player/src/sass/shared/settings-menu.scss b/client/src/standalone/player/src/sass/shared/settings-menu.scss index a808cce16..9beeb66d6 100644 --- a/client/src/standalone/player/src/sass/shared/settings-menu.scss +++ b/client/src/standalone/player/src/sass/shared/settings-menu.scss @@ -40,7 +40,6 @@ $setting-transition-easing: ease-out; } .vjs-settings-sub-menu-title { - text-align: start; font-weight: $font-semibold; } @@ -118,16 +117,19 @@ $setting-transition-easing: ease-out; .vjs-menu-item { outline: 0; font-weight: $font-semibold; - text-align: end; + justify-content: flex-end; padding: 8px 15px; &.vjs-back-button { padding: 12px 15px; margin-bottom: 5px; border-bottom: 1px solid #808080; - text-align: start; + justify-content: flex-start; + align-items: center; &::before { + top: -1px; + @include chevron-left(9px, 2px); @include margin-right(10px); } diff --git a/client/src/standalone/player/src/shared/bezels/bezels-plugin.ts b/client/src/standalone/player/src/shared/bezels/bezels-plugin.ts index cf07dbd2c..0de47c53c 100644 --- a/client/src/standalone/player/src/shared/bezels/bezels-plugin.ts +++ b/client/src/standalone/player/src/shared/bezels/bezels-plugin.ts @@ -1,11 +1,11 @@ import videojs from 'video.js' +import type { VideojsComponentOptions, VideojsPlayer, VideojsPlugin } from '../../types/peertube-videojs-typings' import { PauseBezel } from './pause-bezel' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class BezelsPlugin extends Plugin { - - constructor (player: videojs.Player, options?: videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options?: VideojsComponentOptions) { super(player) this.player.ready(() => { diff --git a/client/src/standalone/player/src/shared/bezels/pause-bezel.ts b/client/src/standalone/player/src/shared/bezels/pause-bezel.ts index d7515e3cc..c921ead4d 100644 --- a/client/src/standalone/player/src/shared/bezels/pause-bezel.ts +++ b/client/src/standalone/player/src/shared/bezels/pause-bezel.ts @@ -1,5 +1,6 @@ -import videojs from 'video.js' import { isMobile } from '@root-helpers/web-browser' +import videojs from 'video.js' +import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' function getPauseBezel () { return ` @@ -31,7 +32,8 @@ function getPlayBezel () { ` } -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent + export class PauseBezel extends Component { declare container: HTMLDivElement @@ -42,7 +44,7 @@ export class PauseBezel extends Component { declare private playerPlayHandler: () => void declare private videoChangeHandler: () => void - constructor (player: videojs.Player, options?: videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options?: VideojsComponentOptions) { super(player, options) // Hide bezels on mobile since we already have our mobile overlay diff --git a/client/src/standalone/player/src/shared/context-menu/context-menu-item.ts b/client/src/standalone/player/src/shared/context-menu/context-menu-item.ts index 65f728c46..8d6949419 100644 --- a/client/src/standalone/player/src/shared/context-menu/context-menu-item.ts +++ b/client/src/standalone/player/src/shared/context-menu/context-menu-item.ts @@ -1,17 +1,17 @@ import videojs from 'video.js' -import { ContextMenuItemOptions } from '../../types' +import { ContextMenuItemOptions, VideojsComponentOptions, VideojsMenuItem, VideojsPlayer } from '../../types' -const MenuItem = videojs.getComponent('MenuItem') +const MenuItem = videojs.getComponent('MenuItem') as typeof VideojsMenuItem class ContextMenuItem extends MenuItem { declare options_: ContextMenuItemOptions // eslint-disable-next-line @typescript-eslint/no-useless-constructor - constructor (player: videojs.Player, options: ContextMenuItemOptions) { + constructor (player: VideojsPlayer, options: VideojsComponentOptions & ContextMenuItemOptions) { super(player, options) } - handleClick (e: videojs.EventTarget.Event) { + handleClick (e: Event) { super.handleClick(e) this.options_.listener(e) diff --git a/client/src/standalone/player/src/shared/context-menu/context-menu-plugin.ts b/client/src/standalone/player/src/shared/context-menu/context-menu-plugin.ts index ee0732072..379a48c67 100644 --- a/client/src/standalone/player/src/shared/context-menu/context-menu-plugin.ts +++ b/client/src/standalone/player/src/shared/context-menu/context-menu-plugin.ts @@ -1,18 +1,18 @@ -import videojs, { VideoJsPlayer } from 'video.js' -import { ContextMenuPluginOptions } from '../../types' +import videojs from 'video.js' +import { ContextMenuPluginOptions, VideojsMenuItemOptions, VideojsPlayer, VideojsPlugin } from '../../types' import { ContextMenu } from './context-menu' import { getPointerPosition } from './util' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class ContextMenuPlugin extends Plugin { - declare options_: ContextMenuPluginOptions & videojs.MenuOptions + declare options_: ContextMenuPluginOptions & VideojsMenuItemOptions declare menu: ContextMenu declare private onContextMenuBind: (e: TouchEvent & MouseEvent) => void - constructor (player: videojs.Player, options: ContextMenuPluginOptions & videojs.MenuOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: ContextMenuPluginOptions & VideojsMenuItemOptions) { + super(player) this.options_ = options @@ -96,7 +96,7 @@ export { ContextMenuPlugin } // Private // --------------------------------------------------------------------------- -function hasMenu (player: VideoJsPlayer) { +function hasMenu (player: VideojsPlayer) { if (!player.usingPlugin('contextMenu')) return false return !!player.contextMenu().menu?.el() diff --git a/client/src/standalone/player/src/shared/context-menu/context-menu.ts b/client/src/standalone/player/src/shared/context-menu/context-menu.ts index 908ebbfd1..4446fa00c 100644 --- a/client/src/standalone/player/src/shared/context-menu/context-menu.ts +++ b/client/src/standalone/player/src/shared/context-menu/context-menu.ts @@ -1,33 +1,32 @@ import videojs from 'video.js' +import { ContextMenuPluginOptions, VideojsMenu, VideojsMenuOptions, VideojsPlayer } from '../../types' import { ContextMenuItem } from './context-menu-item' -import { ContextMenuPluginOptions } from '../../types' -const Menu = videojs.getComponent('Menu') +const Menu = videojs.getComponent('Menu') as typeof VideojsMenu type ContextMenuOptions = ContextMenuPluginOptions & { position: { left: number, top: number } } class ContextMenu extends Menu { - declare options_: ContextMenuOptions & videojs.MenuOptions + declare options_: ContextMenuOptions & VideojsMenuOptions - constructor (player: videojs.Player, options: ContextMenuOptions) { + constructor (player: VideojsPlayer, options: ContextMenuOptions) { super(player, { ...options, menuButton: undefined }) - // Each menu component has its own `dispose` method that can be - // safely bound and unbound to events while maintaining its context. - this.dispose = videojs.bind(this, this.dispose) + // TODO: explain why we need this (I don't understand it) + this.dispose = this.dispose.bind(this) for (const c of options.content()) { this.addItem( new ContextMenuItem(player, { label: c.label, - listener: videojs.bind(player, c.listener) + listener: c.listener.bind(player) }) ) } } createEl () { - const el = super.createEl() + const el = super.createEl() as HTMLElement videojs.dom.addClass(el, 'vjs-contextmenu-ui-menu') el.style.left = this.options_.position.left + 'px' diff --git a/client/src/standalone/player/src/shared/control-bar/caption-toggle-button.ts b/client/src/standalone/player/src/shared/control-bar/caption-toggle-button.ts index 0dbe5c610..58ba07986 100644 --- a/client/src/standalone/player/src/shared/control-bar/caption-toggle-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/caption-toggle-button.ts @@ -1,11 +1,11 @@ import videojs from 'video.js' -import { TheaterButtonOptions } from '../../types' import { getStoredPreferredSubtitle } from '../../peertube-player-local-storage' +import { TheaterButtonOptions, VideojsButton, VideojsButtonOptions, VideojsPlayer } from '../../types' + +const Button = videojs.getComponent('Button') as typeof VideojsButton -const Button = videojs.getComponent('Button') class CaptionToggleButton extends Button { - - constructor (player: videojs.Player, options: TheaterButtonOptions & videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options: TheaterButtonOptions & VideojsButtonOptions) { super(player, options) player.on('texttrackchange', () => this.update()) diff --git a/client/src/standalone/player/src/shared/control-bar/chapters-plugin.ts b/client/src/standalone/player/src/shared/control-bar/chapters-plugin.ts index d25e60264..1768bedd1 100644 --- a/client/src/standalone/player/src/shared/control-bar/chapters-plugin.ts +++ b/client/src/standalone/player/src/shared/control-bar/chapters-plugin.ts @@ -1,9 +1,9 @@ import { VideoChapter } from '@peertube/peertube-models' import videojs from 'video.js' -import { ChaptersOptions } from '../../types' +import { ChaptersOptions, VideojsPlayer, VideojsPlugin } from '../../types' import { ProgressBarMarkerComponent } from './progress-bar-marker-component' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class ChaptersPlugin extends Plugin { declare private chapters: VideoChapter[] @@ -11,8 +11,8 @@ class ChaptersPlugin extends Plugin { private activeChapter: VideoChapter - constructor (player: videojs.Player, options: videojs.ComponentOptions & ChaptersOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: ChaptersOptions) { + super(player) this.markers = [] this.chapters = options.chapters @@ -25,8 +25,13 @@ class ChaptersPlugin extends Plugin { const marker = new ProgressBarMarkerComponent(player, { timecode: chapter.timecode }) - marker.on('mouseenter', () => this.activeChapter = chapter) - marker.on('mouseleave', () => this.activeChapter = undefined) + marker.on('mouseenter', () => { + this.activeChapter = chapter + }) + + marker.on('mouseleave', () => { + this.activeChapter = undefined + }) this.markers.push(marker) this.getSeekBar().addChild(marker) diff --git a/client/src/standalone/player/src/shared/control-bar/next-previous-video-button.ts b/client/src/standalone/player/src/shared/control-bar/next-previous-video-button.ts index f2ff962fd..61eafaef5 100644 --- a/client/src/standalone/player/src/shared/control-bar/next-previous-video-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/next-previous-video-button.ts @@ -1,12 +1,12 @@ import videojs from 'video.js' -import { NextPreviousVideoButtonOptions } from '../../types' +import { NextPreviousVideoButtonOptions, VideojsButton, VideojsButtonOptions, VideojsPlayer } from '../../types' -const Button = videojs.getComponent('Button') +const Button = videojs.getComponent('Button') as typeof VideojsButton class NextPreviousVideoButton extends Button { - declare options_: NextPreviousVideoButtonOptions & videojs.ComponentOptions + declare options_: NextPreviousVideoButtonOptions & VideojsButtonOptions - constructor (player: videojs.Player, options?: NextPreviousVideoButtonOptions & videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options?: NextPreviousVideoButtonOptions & VideojsButtonOptions) { super(player, options) this.player().on('video-change', () => { diff --git a/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts index e521b8fea..3ddab6fbd 100644 --- a/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/p2p-info-button.ts @@ -1,8 +1,9 @@ import videojs from 'video.js' -import { PlayerNetworkInfo } from '../../types' +import { PlayerNetworkInfo, VideojsButton } from '../../types' import { bytes } from '../common' -const Button = videojs.getComponent('Button') +const Button = videojs.getComponent('Button') as typeof VideojsButton + class P2PInfoButton extends Button { createEl () { const div = videojs.dom.createEl('div', { className: 'vjs-peertube' }) @@ -87,7 +88,7 @@ class P2PInfoButton extends Button { subDivP2P.className = 'vjs-peertube-displayed' }) - this.player_.on('network-info', (_event, data: PlayerNetworkInfo) => { + this.player_.on('network-info', (_event: any, data: PlayerNetworkInfo) => { if (data.p2p) return if (data.source === 'web-video') subDivHttpText.textContent = 'HTTP' diff --git a/client/src/standalone/player/src/shared/control-bar/peertube-link-button.ts b/client/src/standalone/player/src/shared/control-bar/peertube-link-button.ts index f61945981..7e4079af9 100644 --- a/client/src/standalone/player/src/shared/control-bar/peertube-link-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/peertube-link-button.ts @@ -1,16 +1,16 @@ -import videojs from 'video.js' import { buildVideoLink, decorateVideoLink } from '@peertube/peertube-core-utils' -import { PeerTubeLinkButtonOptions } from '../../types' +import videojs from 'video.js' +import { PeerTubeLinkButtonOptions, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class PeerTubeLinkButton extends Component { declare private mouseEnterHandler: () => void declare private clickHandler: () => void - declare options_: PeerTubeLinkButtonOptions & videojs.ComponentOptions + declare options_: PeerTubeLinkButtonOptions & VideojsComponentOptions - constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions & videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options?: PeerTubeLinkButtonOptions & VideojsComponentOptions) { super(player, options) this.updateShowing() diff --git a/client/src/standalone/player/src/shared/control-bar/peertube-live-display.ts b/client/src/standalone/player/src/shared/control-bar/peertube-live-display.ts index ed77528e1..adb15dd26 100644 --- a/client/src/standalone/player/src/shared/control-bar/peertube-live-display.ts +++ b/client/src/standalone/player/src/shared/control-bar/peertube-live-display.ts @@ -1,15 +1,15 @@ import videojs from 'video.js' -import { PeerTubeLinkButtonOptions } from '../../types' +import { PeerTubeLinkButtonOptions, VideojsClickableComponent, VideojsClickableComponentOptions, VideojsPlayer } from '../../types' -const ClickableComponent = videojs.getComponent('ClickableComponent') +const ClickableComponent = videojs.getComponent('ClickableComponent') as typeof VideojsClickableComponent class PeerTubeLiveDisplay extends ClickableComponent { declare private interval: any - declare private contentEl_: any + declare private contentEl_: HTMLElement - constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions) { - super(player, options as any) + constructor (player: VideojsPlayer, options?: VideojsClickableComponentOptions & PeerTubeLinkButtonOptions) { + super(player, options) this.interval = this.setInterval(() => this.updateClass(), 1000) @@ -36,7 +36,7 @@ class PeerTubeLiveDisplay extends ClickableComponent { className: 'vjs-live-display' }, { 'aria-live': 'off' - }) + }) as HTMLElement this.contentEl_.appendChild(videojs.dom.createEl('span', { className: 'vjs-control-text', diff --git a/client/src/standalone/player/src/shared/control-bar/progress-bar-marker-component.ts b/client/src/standalone/player/src/shared/control-bar/progress-bar-marker-component.ts index 6476f087a..54dfd96cf 100644 --- a/client/src/standalone/player/src/shared/control-bar/progress-bar-marker-component.ts +++ b/client/src/standalone/player/src/shared/control-bar/progress-bar-marker-component.ts @@ -1,12 +1,12 @@ import videojs from 'video.js' -import { ProgressBarMarkerComponentOptions } from '../../types' +import { ProgressBarMarkerComponentOptions, VideojsClickableComponent, VideojsClickableComponentOptions, VideojsPlayer } from '../../types' -const ClickableComponent = videojs.getComponent('ClickableComponent') +const ClickableComponent = videojs.getComponent('ClickableComponent') as typeof VideojsClickableComponent export class ProgressBarMarkerComponent extends ClickableComponent { - declare options_: ProgressBarMarkerComponentOptions & videojs.ComponentOptions + declare options_: ProgressBarMarkerComponentOptions & VideojsClickableComponentOptions - constructor (player: videojs.Player, options?: ProgressBarMarkerComponentOptions & videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options?: ProgressBarMarkerComponentOptions & VideojsClickableComponentOptions) { super(player, options) const updateMarker = () => { diff --git a/client/src/standalone/player/src/shared/control-bar/storyboard-plugin.ts b/client/src/standalone/player/src/shared/control-bar/storyboard-plugin.ts index 914e5c164..d5e652751 100644 --- a/client/src/standalone/player/src/shared/control-bar/storyboard-plugin.ts +++ b/client/src/standalone/player/src/shared/control-bar/storyboard-plugin.ts @@ -1,10 +1,11 @@ import videojs from 'video.js' -import { StoryboardOptions } from '../../types' +import { StoryboardOptions, VideojsPlayer, VideojsPlugin } from '../../types' +import Tech from 'video.js/dist/types/tech/tech' // Big thanks to this beautiful plugin: https://github.com/phloxic/videojs-sprite-thumbnails // Adapted to respect peertube player style -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class StoryboardPlugin extends Plugin { declare private url: string @@ -14,9 +15,9 @@ class StoryboardPlugin extends Plugin { declare private cached: boolean - declare private mouseTimeTooltip: videojs.MouseTimeDisplay - declare private seekBar: { el(): HTMLElement, mouseTimeDisplay: any, playProgressBar: any } - declare private progress: any + declare private mouseTimeTooltip: VideojsPlayer['controlBar']['progressControl']['seekBar']['mouseTimeDisplay']['timeTooltip'] + declare private seekBar: VideojsPlayer['controlBar']['progressControl']['seekBar'] + declare private progress: VideojsPlayer['controlBar']['progressControl'] declare private spritePlaceholder: HTMLElement @@ -26,8 +27,8 @@ class StoryboardPlugin extends Plugin { declare private onReadyOrLoadstartHandler: (event: { type: 'ready' }) => void - constructor (player: videojs.Player, options: videojs.ComponentOptions & StoryboardOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: StoryboardOptions) { + super(player) this.url = options.url this.height = options.height @@ -46,7 +47,7 @@ class StoryboardPlugin extends Plugin { } init () { - const controls = this.player.controlBar as any + const controls = this.player.controlBar // default control bar component tree is expected // https://docs.videojs.com/tutorial-components.html#default-component-tree @@ -60,7 +61,8 @@ class StoryboardPlugin extends Plugin { this.onReadyOrLoadstartHandler = event => { if (event.type !== 'ready') { - const spriteSource = this.player.currentSources().find(source => { + // FIXME: typings + const spriteSource = (this.player.currentSources() as unknown as Tech[]).find(source => { return Object.prototype.hasOwnProperty.call(source, 'storyboard') }) as any const spriteOpts = spriteSource?.['storyboard'] as StoryboardOptions @@ -97,7 +99,7 @@ class StoryboardPlugin extends Plugin { if (!this.cached) { this.sprites[this.url] = videojs.dom.createEl('img', { src: this.url - }) + }) as HTMLImageElement } this.progress.on(spriteEvents, this.boundedHijackMouseTooltip) } else { diff --git a/client/src/standalone/player/src/shared/control-bar/theater-button.ts b/client/src/standalone/player/src/shared/control-bar/theater-button.ts index d721605dc..78ea1d744 100644 --- a/client/src/standalone/player/src/shared/control-bar/theater-button.ts +++ b/client/src/standalone/player/src/shared/control-bar/theater-button.ts @@ -1,15 +1,15 @@ import videojs from 'video.js' import { getStoredTheater, saveTheaterInStore } from '../../peertube-player-local-storage' -import { TheaterButtonOptions } from '../../types' +import { TheaterButtonOptions, VideojsButton, VideojsButtonOptions, VideojsPlayer } from '../../types' + +const Button = videojs.getComponent('Button') as typeof VideojsButton -const Button = videojs.getComponent('Button') class TheaterButton extends Button { - private static readonly THEATER_MODE_CLASS = 'vjs-theater-enabled' declare private theaterButtonOptions: TheaterButtonOptions - constructor (player: videojs.Player, options: TheaterButtonOptions & videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options: TheaterButtonOptions & VideojsButtonOptions) { super(player, options) this.theaterButtonOptions = options diff --git a/client/src/standalone/player/src/shared/control-bar/time-tooltip.ts b/client/src/standalone/player/src/shared/control-bar/time-tooltip.ts index 4c0dd0e99..8a11d1db6 100644 --- a/client/src/standalone/player/src/shared/control-bar/time-tooltip.ts +++ b/client/src/standalone/player/src/shared/control-bar/time-tooltip.ts @@ -1,13 +1,14 @@ import { timeToInt } from '@peertube/peertube-core-utils' -import videojs, { VideoJsPlayer } from 'video.js' +import videojs from 'video.js' +import { VideojsTimeTooltip } from '../../types' -const TimeToolTip = videojs.getComponent('TimeTooltip') as any // FIXME: typings don't have write method +const TimeToolTip = videojs.getComponent('TimeTooltip') as typeof VideojsTimeTooltip class TimeTooltip extends TimeToolTip { declare private currentTimecode: string write (timecode: string) { - const player: VideoJsPlayer = this.player() + const player = this.player() if (player.usingPlugin('chapters')) { if (timecode === this.currentTimecode) return diff --git a/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts b/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts index ad5a62bb1..bd2ed0477 100644 --- a/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts +++ b/client/src/standalone/player/src/shared/dock/peertube-dock-component.ts @@ -1,6 +1,7 @@ import videojs from 'video.js' +import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent export type PeerTubeDockComponentOptions = { title?: string @@ -9,10 +10,10 @@ export type PeerTubeDockComponentOptions = { } class PeerTubeDockComponent extends Component { - declare options_: videojs.ComponentOptions & PeerTubeDockComponentOptions + declare options_: VideojsComponentOptions & PeerTubeDockComponentOptions // eslint-disable-next-line @typescript-eslint/no-useless-constructor - constructor (player: videojs.Player, options: videojs.ComponentOptions & PeerTubeDockComponentOptions) { + constructor (player: VideojsPlayer, options: VideojsComponentOptions & PeerTubeDockComponentOptions) { super(player, options) } diff --git a/client/src/standalone/player/src/shared/dock/peertube-dock-plugin.ts b/client/src/standalone/player/src/shared/dock/peertube-dock-plugin.ts index 4ff9873fa..4abd2a7a9 100644 --- a/client/src/standalone/player/src/shared/dock/peertube-dock-plugin.ts +++ b/client/src/standalone/player/src/shared/dock/peertube-dock-plugin.ts @@ -1,7 +1,8 @@ import videojs from 'video.js' +import { VideojsPlayer, VideojsPlugin } from '../../types' import { PeerTubeDockComponent } from './peertube-dock-component' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin export type PeerTubeDockPluginOptions = { title?: string @@ -12,8 +13,8 @@ export type PeerTubeDockPluginOptions = { class PeerTubeDockPlugin extends Plugin { declare private dockComponent: PeerTubeDockComponent - constructor (player: videojs.Player, options: videojs.PlayerOptions & PeerTubeDockPluginOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: PeerTubeDockPluginOptions) { + super(player) player.ready(() => { player.addClass('peertube-dock') diff --git a/client/src/standalone/player/src/shared/hotkeys/peertube-hotkeys-plugin.ts b/client/src/standalone/player/src/shared/hotkeys/peertube-hotkeys-plugin.ts index d1bcf2810..3d91efcb6 100644 --- a/client/src/standalone/player/src/shared/hotkeys/peertube-hotkeys-plugin.ts +++ b/client/src/standalone/player/src/shared/hotkeys/peertube-hotkeys-plugin.ts @@ -1,8 +1,9 @@ import videojs from 'video.js' +import { VideojsPlayer, VideojsPlugin } from '../../types' type KeyHandler = { accept: (event: KeyboardEvent) => boolean, cb: (e: KeyboardEvent) => void } -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin export type HotkeysOptions = { isLive: boolean @@ -18,8 +19,8 @@ class PeerTubeHotkeysPlugin extends Plugin { declare private readonly isLive: boolean - constructor (player: videojs.Player, options: videojs.PlayerOptions & HotkeysOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: HotkeysOptions) { + super(player) this.isLive = options.isLive diff --git a/client/src/standalone/player/src/shared/metrics/metrics-plugin.ts b/client/src/standalone/player/src/shared/metrics/metrics-plugin.ts index b90db9097..b60bf780c 100644 --- a/client/src/standalone/player/src/shared/metrics/metrics-plugin.ts +++ b/client/src/standalone/player/src/shared/metrics/metrics-plugin.ts @@ -1,12 +1,12 @@ -import debug from 'debug' -import videojs from 'video.js' import { PlaybackMetricCreate, VideoResolutionType } from '@peertube/peertube-models' import { logger } from '@root-helpers/logger' -import { MetricsPluginOptions, PlayerNetworkInfo } from '../../types' +import debug from 'debug' +import videojs from 'video.js' +import { MetricsPluginOptions, PlayerNetworkInfo, VideojsPlayer, VideojsPlugin } from '../../types' const debugLogger = debug('peertube:player:metrics') -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class MetricsPlugin extends Plugin { declare options_: MetricsPluginOptions @@ -27,7 +27,7 @@ class MetricsPlugin extends Plugin { declare private metricsInterval: any - constructor (player: videojs.Player, options: MetricsPluginOptions) { + constructor (player: VideojsPlayer, options: MetricsPluginOptions) { super(player) this.options_ = options @@ -154,7 +154,7 @@ class MetricsPlugin extends Plugin { } private trackBytes () { - this.player.on('network-info', (_event, data: PlayerNetworkInfo) => { + this.player.on('network-info', (_event: any, data: PlayerNetworkInfo) => { this.downloadedBytesHTTP += Math.max(Math.round(data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0)), 0) this.downloadedBytesP2P += Math.max(Math.round((data.p2p?.downloaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.downloaded || 0)), 0) diff --git a/client/src/standalone/player/src/shared/mobile/peertube-mobile-buttons.ts b/client/src/standalone/player/src/shared/mobile/peertube-mobile-buttons.ts index 43316b9ad..0801967d3 100644 --- a/client/src/standalone/player/src/shared/mobile/peertube-mobile-buttons.ts +++ b/client/src/standalone/player/src/shared/mobile/peertube-mobile-buttons.ts @@ -1,6 +1,8 @@ import videojs from 'video.js' +import { VideojsComponent } from '../../types' + +const Component = videojs.getComponent('Component') as typeof VideojsComponent -const Component = videojs.getComponent('Component') class PeerTubeMobileButtons extends Component { declare private mainButton: HTMLDivElement diff --git a/client/src/standalone/player/src/shared/mobile/peertube-mobile-plugin.ts b/client/src/standalone/player/src/shared/mobile/peertube-mobile-plugin.ts index 008eab112..770cf55ef 100644 --- a/client/src/standalone/player/src/shared/mobile/peertube-mobile-plugin.ts +++ b/client/src/standalone/player/src/shared/mobile/peertube-mobile-plugin.ts @@ -1,11 +1,14 @@ +import { logger } from '@root-helpers/logger' import debug from 'debug' import videojs from 'video.js' -import { logger } from '@root-helpers/logger' +import { VideojsPlayer, VideojsPlugin } from '../../types' import { PeerTubeMobileButtons } from './peertube-mobile-buttons' const debugLogger = debug('peertube:player:mobile') -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin + +type SeekBar = VideojsPlayer['controlBar']['progressControl']['seekBar'] class PeerTubeMobilePlugin extends Plugin { private static readonly DOUBLE_TAP_DELAY_MS = 250 @@ -29,10 +32,10 @@ class PeerTubeMobilePlugin extends Plugin { declare private sliderActiveHandler: () => void declare private sliderInactiveHandler: () => void - declare private seekBar: videojs.Component + declare private seekBar: SeekBar - constructor (player: videojs.Player, options: videojs.PlayerOptions) { - super(player, options) + constructor (player: VideojsPlayer) { + super(player) this.seekAmount = 0 @@ -45,16 +48,14 @@ class PeerTubeMobilePlugin extends Plugin { this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons - if (!this.player.options_.userActions) this.player.options_.userActions = {}; - - // FIXME: typings - (this.player.options_.userActions as any).click = false + if (!this.player.options_.userActions) this.player.options_.userActions = {} + this.player.options_.userActions.click = false this.player.options_.userActions.doubleClick = false this.onPlayHandler = () => this.initTouchStartEvents() this.player.one('play', this.onPlayHandler) - this.seekBar = this.player.getDescendant([ 'controlBar', 'progressControl', 'seekBar' ]) + this.seekBar = this.player.getDescendant([ 'controlBar', 'progressControl', 'seekBar' ]) as SeekBar this.sliderActiveHandler = () => this.player.addClass('vjs-mobile-sliding') this.sliderInactiveHandler = () => this.player.removeClass('vjs-mobile-sliding') @@ -76,10 +77,9 @@ class PeerTubeMobilePlugin extends Plugin { private handleFullscreenRotation () { this.onFullScreenChangeHandler = () => { - if (!this.player.isFullscreen() || this.isPortraitVideo()) return; - // https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1615 - (screen.orientation as any).lock('landscape') + if (!this.player.isFullscreen() || this.isPortraitVideo()) return + ;(screen.orientation as any).lock('landscape') .catch((err: Error) => logger.error('Cannot lock screen to landscape.', err)) } @@ -151,7 +151,7 @@ class PeerTubeMobilePlugin extends Plugin { private onDoubleTap (event: TouchEvent) { const playerWidth = this.player.currentWidth() - const rect = this.findPlayerTarget((event.target as HTMLElement)).getBoundingClientRect() + const rect = this.findPlayerTarget(event.target as HTMLElement).getBoundingClientRect() const offsetX = event.targetTouches[0].pageX - rect.left debugLogger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX) diff --git a/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-details-component.ts b/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-details-component.ts index 03dbc4bf0..6dd79a74a 100644 --- a/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-details-component.ts +++ b/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-details-component.ts @@ -1,14 +1,15 @@ import { NSFWFlag } from '@peertube/peertube-models' import videojs from 'video.js' +import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' import { type PeerTubeNSFWPluginOptions } from './peertube-nsfw-plugin' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class PeerTubeNSFWDetailsComponent extends Component { - declare options_: videojs.ComponentOptions & PeerTubeNSFWPluginOptions + declare options_: VideojsComponentOptions & PeerTubeNSFWPluginOptions // eslint-disable-next-line @typescript-eslint/no-useless-constructor - constructor (player: videojs.Player, options: videojs.ComponentOptions & PeerTubeNSFWPluginOptions) { + constructor (player: VideojsPlayer, options: VideojsComponentOptions & PeerTubeNSFWPluginOptions) { super(player, options) } diff --git a/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-info-component.ts b/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-info-component.ts index d5a134eca..55f32992b 100644 --- a/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-info-component.ts +++ b/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-info-component.ts @@ -1,13 +1,14 @@ import videojs from 'video.js' +import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' import { type PeerTubeNSFWPluginOptions } from './peertube-nsfw-plugin' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class PeerTubeNSFWInfoComponent extends Component { - declare options_: videojs.ComponentOptions & PeerTubeNSFWPluginOptions + declare options_: VideojsComponentOptions & PeerTubeNSFWPluginOptions // eslint-disable-next-line @typescript-eslint/no-useless-constructor - constructor (player: videojs.Player, options: videojs.ComponentOptions & PeerTubeNSFWPluginOptions) { + constructor (player: VideojsPlayer, options: VideojsComponentOptions & PeerTubeNSFWPluginOptions) { super(player, options) } diff --git a/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-plugin.ts b/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-plugin.ts index d4d97e405..5102bc333 100644 --- a/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-plugin.ts +++ b/client/src/standalone/player/src/shared/nsfw/peertube-nsfw-plugin.ts @@ -1,8 +1,9 @@ import videojs from 'video.js' -import { PeerTubeNSFWInfoComponent } from './peertube-nsfw-info-component' +import { VideojsPlayer, VideojsPlugin } from '../../types' import { PeerTubeNSFWDetailsComponent } from './peertube-nsfw-details-component' +import { PeerTubeNSFWInfoComponent } from './peertube-nsfw-info-component' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin export type PeerTubeNSFWPluginOptions = { summary: string @@ -13,8 +14,8 @@ class PeerTubeNSFWPlugin extends Plugin { declare private nsfwInfoComponent: PeerTubeNSFWInfoComponent declare private nsfwDetailsComponent: PeerTubeNSFWDetailsComponent - constructor (player: videojs.Player, options: videojs.PlayerOptions & PeerTubeNSFWPluginOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: PeerTubeNSFWPluginOptions) { + super(player) player.ready(() => { player.addClass('peertube-nsfw') diff --git a/client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts b/client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts index 2bd094db4..f6ac83105 100644 --- a/client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/hls-plugin.ts @@ -1,12 +1,13 @@ // Thanks https://github.com/streamroot/videojs-hlsjs-plugin // We duplicated this plugin to choose the hls.js version we want, because streamroot only provide a bundled file +import { omit } from '@peertube/peertube-core-utils' 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' +import videojs from 'video.js' +import Tech, { SourceObject } from 'video.js/dist/types/tech/tech' +import { HLSPluginOptions, HlsjsConfigHandlerOptions, PeerTubeResolution, VideoJSTechHLS, VideojsPlayer, VideojsPlugin } from '../../types' const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hlsjs) @@ -37,7 +38,7 @@ const registerSourceHandler = function (vjs: typeof videojs) { alreadyRegistered = true // FIXME: typings ;(html5 as any).registerSourceHandler({ - canHandleSource: function (source: videojs.Tech.SourceObject) { + canHandleSource: function (source: SourceObject) { const hlsTypeRE = /^application\/x-mpegURL|application\/vnd\.apple\.mpegurl$/i const hlsExtRE = /\.m3u8/i @@ -47,7 +48,7 @@ const registerSourceHandler = function (vjs: typeof videojs) { return '' }, - handleSource: function (source: videojs.Tech.SourceObject, tech: VideoJSTechHLS) { + handleSource: function (source: SourceObject, tech: VideoJSTechHLS) { if (tech.hlsProvider) { tech.hlsProvider.dispose() } @@ -64,11 +65,11 @@ const registerSourceHandler = function (vjs: typeof videojs) { // HLS options plugin // --------------------------------------------------------------------------- -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class HLSJSConfigHandler extends Plugin { - constructor (player: videojs.Player, options: HlsjsConfigHandlerOptions) { - super(player, options) + constructor (player: VideojsPlayer, options: HlsjsConfigHandlerOptions) { + super(player) if (!options) return @@ -109,9 +110,9 @@ videojs.registerPlugin('hlsjs', HLSJSConfigHandler) export class Html5Hlsjs { private readonly videoElement: HTMLVideoElement private readonly errorCounts: ErrorCounts = {} - private readonly player: videojs.Player - private readonly tech: videojs.Tech - private readonly source: videojs.Tech.SourceObject + private readonly player: VideojsPlayer + private readonly tech: Tech + private readonly source: SourceObject private readonly vjs: typeof videojs private maxNetworkErrorRecovery = 5 @@ -134,7 +135,7 @@ export class Html5Hlsjs { private audioMode = false - constructor (vjs: typeof videojs, source: videojs.Tech.SourceObject, tech: videojs.Tech) { + constructor (vjs: typeof videojs, source: SourceObject, tech: Tech) { this.vjs = vjs this.source = source @@ -142,7 +143,7 @@ export class Html5Hlsjs { ;(this.tech as any).name_ = 'Hlsjs' this.videoElement = tech.el() as HTMLVideoElement - this.player = vjs((tech.options_ as any).playerId) + this.player = vjs(tech.options_.playerId) as VideojsPlayer this.handlers.error = event => { let errorTxt: string diff --git a/client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts b/client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts index 07a038722..b17ce60ca 100644 --- a/client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts +++ b/client/src/standalone/player/src/shared/p2p-media-loader/p2p-media-loader-plugin.ts @@ -5,12 +5,13 @@ 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 { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideojsPlayer, VideojsPlugin } from '../../types' import { SettingsButton } from '../settings/settings-menu-button' const debugLogger = debug('peertube:player:p2p-media-loader') -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin + class P2pMediaLoaderPlugin extends Plugin { declare private readonly options: P2PMediaLoaderPluginOptions @@ -35,7 +36,7 @@ class P2pMediaLoaderPlugin extends Plugin { declare private connectedPeers: Set declare private totalHTTPPeers: number - constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions) { + constructor (player: VideojsPlayer, options?: P2PMediaLoaderPluginOptions) { super(player) this.options = options diff --git a/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts index 7d731ca7f..6ae35ff35 100644 --- a/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts +++ b/client/src/standalone/player/src/shared/peertube/peertube-plugin.ts @@ -5,6 +5,8 @@ import { isIOS, isMobile, isSafari } from '@root-helpers/web-browser' import debug from 'debug' import { UAParser } from 'ua-parser-js' import videojs from 'video.js' +import type MediaError from 'video.js/dist/types/media-error' +import type ModalDialog from 'video.js/dist/types/modal-dialog' import { getPlayerSessionId, getStoredLastSubtitle, @@ -16,19 +18,26 @@ import { saveVideoWatchHistory, saveVolumeInStore } from '../../peertube-player-local-storage' -import { PeerTubePluginOptions } from '../../types' +import type { + PeerTubePluginOptions, + VideojsAutoplay, + VideojsCaptionsButton, + VideojsPlayer, + VideojsPlayerOptions, + VideojsPlugin +} from '../../types' import { SettingsButton } from '../settings/settings-menu-button' const debugLogger = debug('peertube:player:peertube') -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class PeerTubePlugin extends Plugin { declare private readonly videoViewUrl: () => string declare private readonly authorizationHeader: () => string declare private readonly initialInactivityTimeout: number - declare private readonly hasAutoplay: () => videojs.Autoplay + declare private readonly hasAutoplay: () => VideojsAutoplay declare private currentSubtitle: string declare private currentPlaybackRate: number @@ -39,7 +48,7 @@ class PeerTubePlugin extends Plugin { declare private mouseInControlBar: boolean declare private mouseInSettings: boolean - declare private errorModal: videojs.ModalDialog + declare private errorModal: ModalDialog declare private hasInitialSeek: boolean @@ -56,7 +65,7 @@ class PeerTubePlugin extends Plugin { os: string } - constructor (player: videojs.Player, private readonly options: PeerTubePluginOptions) { + constructor (player: VideojsPlayer, private readonly options: PeerTubePluginOptions) { super(player) this.setUserAgentInfo() @@ -99,7 +108,7 @@ class PeerTubePlugin extends Plugin { }) this.player.one('canplay', () => { - const playerOptions = this.player.options_ + const playerOptions = this.player.options_ as VideojsPlayerOptions const volume = getStoredVolume() if (volume !== undefined) this.player.volume(volume) @@ -298,7 +307,7 @@ class PeerTubePlugin extends Plugin { this.player.on('video-change', () => tryToUpdateRatioFromOptions()) - this.player.on('video-ratio-changed', (_event, data: { ratio: number }) => { + this.player.on('video-ratio-changed', (_event: any, data: { ratio: number }) => { if (this.options.videoRatio()) return this.adaptPlayerFromRatio({ ratio: data.ratio, defaultRatio }) @@ -497,7 +506,7 @@ class PeerTubePlugin extends Plugin { private listenControlBarMouse () { const controlBar = this.player.controlBar - const settingsButton: SettingsButton = (controlBar as any).settingsButton + const settingsButton = controlBar.settingsButton controlBar.on('mouseenter', () => { this.mouseInControlBar = true @@ -635,24 +644,6 @@ class PeerTubePlugin extends Plugin { // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657 private initSmoothProgressBar () { - const SeekBar = videojs.getComponent('SeekBar') as any - SeekBar.prototype.getPercent = function getPercent () { - // Allows for smooth scrubbing, when player can't keep up. - // const time = (this.player_.scrubbing()) ? - // this.player_.getCache().currentTime : - // this.player_.currentTime() - const time = this.player_.currentTime() - const percent = time / this.player_.duration() - return percent >= 1 ? 1 : percent - } - SeekBar.prototype.handleMouseMove = function handleMouseMove (event: any) { - let newTime = this.calculateDistance(event) * this.player_.duration() - if (newTime === this.player_.duration()) { - newTime = newTime - 0.1 - } - this.player_.currentTime(newTime) - this.update() - } } private patchMenuEscapeKey () { @@ -688,7 +679,7 @@ class PeerTubePlugin extends Plugin { private getCaptionsButton () { const settingsButton = this.player.controlBar.getDescendant([ 'settingsButton' ]) as SettingsButton - return settingsButton.menu.getChild('captionsButton') as videojs.CaptionsButton + return settingsButton.menu.getChild('captionsButton') as unknown as VideojsCaptionsButton } private getPlaybackRateButton () { diff --git a/client/src/standalone/player/src/shared/player-options-builder/control-bar-options-builder.ts b/client/src/standalone/player/src/shared/player-options-builder/control-bar-options-builder.ts index 42f472df4..55f89c50f 100644 --- a/client/src/standalone/player/src/shared/player-options-builder/control-bar-options-builder.ts +++ b/client/src/standalone/player/src/shared/player-options-builder/control-bar-options-builder.ts @@ -7,8 +7,8 @@ import { } from '../../types' type ControlBarOptionsBuilderConstructorOptions = - Pick & - { + & Pick + & { videoShortUUID: () => string p2pEnabled: () => boolean @@ -17,7 +17,6 @@ type ControlBarOptionsBuilderConstructorOptions = } export class ControlBarOptionsBuilder { - constructor (private options: ControlBarOptionsBuilderConstructorOptions) { } @@ -84,11 +83,7 @@ export class ControlBarOptionsBuilder { progressControl: { children: { seekBar: { - children: { - loadProgressBar: {}, - mouseTimeDisplay: {}, - playProgressBar: {} - } + children: [ 'loadProgressBar', 'mouseTimeDisplay', 'playProgressBar' ] } } } diff --git a/client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts b/client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts index a0ee03723..01fdba427 100644 --- a/client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts +++ b/client/src/standalone/player/src/shared/player-options-builder/hls-options-builder.ts @@ -7,7 +7,13 @@ import debug from 'debug' import { Level } from 'hls.js' import type { CoreConfig, StreamConfig } from 'p2p-media-loader-core' import { getAverageBandwidthInStore } from '../../peertube-player-local-storage' -import { HLSPluginOptions, P2PMediaLoaderPluginOptions, PeerTubePlayerConstructorOptions, PeerTubePlayerLoadOptions } from '../../types' +import { + HLSPluginOptions, + P2PMediaLoaderPluginOptions, + PeerTubePlayerConstructorOptions, + PeerTubePlayerLoadOptions, + VideojsPlayer +} from '../../types' import { getRtcConfig } from '../common' import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager' import { SegmentValidator } from '../p2p-media-loader/segment-validator' @@ -67,7 +73,7 @@ export class HLSOptionsBuilder { const hlsjs = { hlsjsConfig: this.getHLSJSOptions(p2pMediaLoaderConfig), - levelLabelHandler: (level: Level, player: videojs.VideoJsPlayer) => { + levelLabelHandler: (level: Level, player: VideojsPlayer) => { const resolution = Math.min(level.height || 0, level.width || 0) const file = this.options.hls.videoFiles.find(f => f.resolution.id === resolution) diff --git a/client/src/standalone/player/src/shared/playlist/playlist-button.ts b/client/src/standalone/player/src/shared/playlist/playlist-button.ts index b5bb63c7f..d788e3858 100644 --- a/client/src/standalone/player/src/shared/playlist/playlist-button.ts +++ b/client/src/standalone/player/src/shared/playlist/playlist-button.ts @@ -1,20 +1,20 @@ import videojs from 'video.js' -import { PlaylistPluginOptions } from '../../types' +import { PlaylistPluginOptions, VideojsClickableComponent, VideojsClickableComponentOptions, VideojsPlayer } from '../../types' import { PlaylistMenu } from './playlist-menu' -const ClickableComponent = videojs.getComponent('ClickableComponent') +const ClickableComponent = videojs.getComponent('ClickableComponent') as typeof VideojsClickableComponent class PlaylistButton extends ClickableComponent { declare private playlistInfoElement: HTMLElement declare private wrapper: HTMLElement - declare options_: PlaylistPluginOptions & { playlistMenu: PlaylistMenu } & videojs.ClickableComponentOptions + declare options_: PlaylistPluginOptions & { playlistMenu: PlaylistMenu } & VideojsClickableComponentOptions // FIXME: eslint -> it's not a useless constructor, we need to extend constructor options typings // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor ( - player: videojs.Player, - options?: PlaylistPluginOptions & { playlistMenu: PlaylistMenu } & videojs.ClickableComponentOptions + player: VideojsPlayer, + options?: PlaylistPluginOptions & { playlistMenu: PlaylistMenu } & VideojsClickableComponentOptions ) { super(player, options) } diff --git a/client/src/standalone/player/src/shared/playlist/playlist-menu-item.ts b/client/src/standalone/player/src/shared/playlist/playlist-menu-item.ts index 578950161..b159dae7c 100644 --- a/client/src/standalone/player/src/shared/playlist/playlist-menu-item.ts +++ b/client/src/standalone/player/src/shared/playlist/playlist-menu-item.ts @@ -1,9 +1,9 @@ -import videojs from 'video.js' import { secondsToTime } from '@peertube/peertube-core-utils' import { VideoPlaylistElement } from '@peertube/peertube-models' -import { PlaylistItemOptions } from '../../types' +import videojs from 'video.js' +import { PlaylistItemOptions, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class PlaylistMenuItem extends Component { declare private element: VideoPlaylistElement @@ -11,10 +11,10 @@ class PlaylistMenuItem extends Component { declare private clickHandler: () => void declare private keyDownHandler: (event: KeyboardEvent) => void - declare options_: videojs.ComponentOptions & PlaylistItemOptions + declare options_: VideojsComponentOptions & PlaylistItemOptions - constructor (player: videojs.Player, options?: PlaylistItemOptions) { - super(player, options as any) + constructor (player: VideojsPlayer, options?: VideojsComponentOptions & PlaylistItemOptions) { + super(player, options) this.emitTapEvents() @@ -130,7 +130,8 @@ class PlaylistMenuItem extends Component { li.appendChild(block) } - private handleKeyDown (event: KeyboardEvent) { + // Can't put it private because it's public in parent component + handleKeyDown (event: KeyboardEvent) { if (event.code === 'Space' || event.code === 'Enter') { this.switchPlaylistItem() } @@ -141,6 +142,6 @@ class PlaylistMenuItem extends Component { } } -Component.registerComponent('PlaylistMenuItem', PlaylistMenuItem) +videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem) export { PlaylistMenuItem } diff --git a/client/src/standalone/player/src/shared/playlist/playlist-menu.ts b/client/src/standalone/player/src/shared/playlist/playlist-menu.ts index b9c991e2a..f759611c7 100644 --- a/client/src/standalone/player/src/shared/playlist/playlist-menu.ts +++ b/client/src/standalone/player/src/shared/playlist/playlist-menu.ts @@ -1,9 +1,9 @@ -import videojs from 'video.js' import { VideoPlaylistElement } from '@peertube/peertube-models' -import { PlaylistPluginOptions } from '../../types' +import videojs from 'video.js' +import { PlaylistPluginOptions, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' import { PlaylistMenuItem } from './playlist-menu-item' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class PlaylistMenu extends Component { declare private menuItems: PlaylistMenuItem[] @@ -14,9 +14,9 @@ class PlaylistMenu extends Component { declare private readonly onPlayerCick: (event: Event) => void - declare options_: PlaylistPluginOptions & videojs.ComponentOptions + declare options_: PlaylistPluginOptions & VideojsComponentOptions - constructor (player: videojs.Player, options?: PlaylistPluginOptions & videojs.ComponentOptions) { + constructor (player: VideojsPlayer, options?: PlaylistPluginOptions & VideojsComponentOptions) { super(player, options) this.menuItems = [] @@ -152,6 +152,6 @@ class PlaylistMenu extends Component { } } -Component.registerComponent('PlaylistMenu', PlaylistMenu) +videojs.registerComponent('PlaylistMenu', PlaylistMenu) export { PlaylistMenu } diff --git a/client/src/standalone/player/src/shared/playlist/playlist-plugin.ts b/client/src/standalone/player/src/shared/playlist/playlist-plugin.ts index 4cb208032..9ea839fac 100644 --- a/client/src/standalone/player/src/shared/playlist/playlist-plugin.ts +++ b/client/src/standalone/player/src/shared/playlist/playlist-plugin.ts @@ -1,16 +1,16 @@ import videojs from 'video.js' -import { PlaylistPluginOptions } from '../../types' +import { PlaylistPluginOptions, VideojsPlayer, VideojsPlugin } from '../../types' import { PlaylistButton } from './playlist-button' import { PlaylistMenu } from './playlist-menu' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class PlaylistPlugin extends Plugin { declare private playlistMenu: PlaylistMenu declare private playlistButton: PlaylistButton - constructor (player: videojs.Player, options?: PlaylistPluginOptions) { - super(player, options) + constructor (player: VideojsPlayer, options?: PlaylistPluginOptions) { + super(player) this.player.addClass('vjs-playlist') diff --git a/client/src/standalone/player/src/shared/resolutions/peertube-resolutions-plugin.ts b/client/src/standalone/player/src/shared/resolutions/peertube-resolutions-plugin.ts index d5fb98d02..088470df7 100644 --- a/client/src/standalone/player/src/shared/resolutions/peertube-resolutions-plugin.ts +++ b/client/src/standalone/player/src/shared/resolutions/peertube-resolutions-plugin.ts @@ -1,7 +1,7 @@ import videojs from 'video.js' -import { PeerTubeResolution } from '../../types' +import { PeerTubeResolution, VideojsPlayer, VideojsPlugin } from '../../types' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class PeerTubeResolutionsPlugin extends Plugin { declare private currentSelection: PeerTubeResolution @@ -9,7 +9,7 @@ class PeerTubeResolutionsPlugin extends Plugin { declare private autoResolutionChosenId: number - constructor (player: videojs.Player) { + constructor (player: VideojsPlayer) { super(player) this.resolutions = [] @@ -85,7 +85,6 @@ class PeerTubeResolutionsPlugin extends Plugin { return 1 }) } - } videojs.registerPlugin('peertubeResolutions', PeerTubeResolutionsPlugin) diff --git a/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts index ad148e823..0bda7c5be 100644 --- a/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts +++ b/client/src/standalone/player/src/shared/settings/menu-focus-fixed.ts @@ -1,12 +1,12 @@ import videojs from 'video.js' +import { VideojsMenu } from '../../types' -const Menu = videojs.getComponent('Menu') -const Component = videojs.getComponent('Component') +const Menu = videojs.getComponent('Menu') as typeof VideojsMenu // Default menu doesn't check if the child is disabled/hidden class MenuFocusFixed extends Menu { - declare private focusedChild_: number + declare focusedChild_: number stepForward () { let stepChild = 0 @@ -72,5 +72,5 @@ class MenuFocusFixed extends Menu { } } -Component.registerComponent('MenuFocusFixed', MenuFocusFixed) +videojs.registerComponent('MenuFocusFixed', MenuFocusFixed) export { MenuFocusFixed } diff --git a/client/src/standalone/player/src/shared/settings/resolution-menu-button.ts b/client/src/standalone/player/src/shared/settings/resolution-menu-button.ts index b0f6dbc14..c48641581 100644 --- a/client/src/standalone/player/src/shared/settings/resolution-menu-button.ts +++ b/client/src/standalone/player/src/shared/settings/resolution-menu-button.ts @@ -1,12 +1,14 @@ import videojs from 'video.js' +import { VideojsMenu, VideojsMenuButton, VideojsMenuButtonOptions, VideojsPlayer } from '../../types' import { ResolutionMenuItem } from './resolution-menu-item' -const Menu = videojs.getComponent('Menu') -const MenuButton = videojs.getComponent('MenuButton') +const Menu = videojs.getComponent('Menu') as typeof VideojsMenu +const MenuButton = videojs.getComponent('MenuButton') as typeof VideojsMenuButton + class ResolutionMenuButton extends MenuButton { declare labelEl_: HTMLElement - constructor (player: videojs.Player, options?: videojs.MenuButtonOptions) { + constructor (player: VideojsPlayer, options?: VideojsMenuButtonOptions) { super(player, options) this.controlText('Quality') @@ -37,7 +39,7 @@ class ResolutionMenuButton extends MenuButton { } createMenu () { - const menu: videojs.Menu = new Menu(this.player_, { menuButton: this }) + const menu = new Menu(this.player_, { menuButton: this }) const resolutions = this.player().peertubeResolutions().getResolutions() for (const r of resolutions) { diff --git a/client/src/standalone/player/src/shared/settings/resolution-menu-item.ts b/client/src/standalone/player/src/shared/settings/resolution-menu-item.ts index 46999161e..f5990ad0a 100644 --- a/client/src/standalone/player/src/shared/settings/resolution-menu-item.ts +++ b/client/src/standalone/player/src/shared/settings/resolution-menu-item.ts @@ -1,9 +1,10 @@ import videojs from 'video.js' +import { VideojsMenuItem, VideojsMenuItemOptions, VideojsPlayer } from '../../types' -const MenuItem = videojs.getComponent('MenuItem') +const MenuItem = videojs.getComponent('MenuItem') as typeof VideojsMenuItem -export interface ResolutionMenuItemOptions extends videojs.MenuItemOptions { - resolutionId: number +export interface ResolutionMenuItemOptions extends VideojsMenuItemOptions { + resolutionId?: number } class ResolutionMenuItem extends MenuItem { @@ -14,7 +15,7 @@ class ResolutionMenuItem extends MenuItem { declare private updateSelectionHandler: () => void - constructor (player: videojs.Player, options?: ResolutionMenuItemOptions) { + constructor (player: VideojsPlayer, options?: ResolutionMenuItemOptions) { super(player, { ...options, selectable: true }) this.autoResolutionChosen = '' diff --git a/client/src/standalone/player/src/shared/settings/settings-dialog.ts b/client/src/standalone/player/src/shared/settings/settings-dialog.ts index 7c2fbd8c3..12d57a650 100644 --- a/client/src/standalone/player/src/shared/settings/settings-dialog.ts +++ b/client/src/standalone/player/src/shared/settings/settings-dialog.ts @@ -1,9 +1,10 @@ import videojs from 'video.js' +import { VideojsComponent, VideojsPlayer } from '../../types' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class SettingsDialog extends Component { - constructor (player: videojs.Player) { + constructor (player: VideojsPlayer) { super(player) this.hide() @@ -41,6 +42,6 @@ class SettingsDialog extends Component { } } -Component.registerComponent('SettingsDialog', SettingsDialog) +videojs.registerComponent('SettingsDialog', SettingsDialog) export { SettingsDialog } diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts index d7731709a..d7970da9f 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-button.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-button.ts @@ -1,5 +1,6 @@ import debug from 'debug' import videojs from 'video.js' +import { VideojsButton, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' import { toTitleCase } from '../common' import { MenuFocusFixed } from './menu-focus-fixed' import { SettingsDialog } from './settings-dialog' @@ -9,10 +10,10 @@ import { SettingsPanelChild } from './settings-panel-child' const debugLogger = debug('peertube:player:settings') -const Button = videojs.getComponent('Button') -const Component = videojs.getComponent('Component') +const Button = videojs.getComponent('Button') as typeof VideojsButton +const Component = videojs.getComponent('Component') as typeof VideojsComponent -export interface SettingsButtonOptions extends videojs.ComponentOptions { +export interface SettingsButtonOptions extends VideojsComponentOptions { entries: any[] setup?: { maxHeightOffset: number @@ -33,18 +34,18 @@ class SettingsButton extends Button { declare private settingsButtonOptions: SettingsButtonOptions - constructor (player: videojs.Player, options?: SettingsButtonOptions) { + constructor (player: VideojsPlayer, options?: SettingsButtonOptions) { super(player, options) this.settingsButtonOptions = options this.controlText('Settings') - this.dialog = this.player().addChild('settingsDialog') + this.dialog = this.player().addChild('settingsDialog') as SettingsDialog this.dialogEl = this.dialog.el() as HTMLElement this.menu = null - this.panel = this.dialog.addChild('settingsPanel') - this.panelChild = this.panel.addChild('settingsPanelChild') + this.panel = this.dialog.addChild('settingsPanel') as SettingsPanel + this.panelChild = this.panel.addChild('settingsPanelChild') as SettingsPanelChild this.addClass('vjs-settings') this.setAttribute('aria-controls', 'vjs-settings-dialog-' + this.player().id()) @@ -170,7 +171,7 @@ class SettingsButton extends Button { this.resetChildren() } - getComponentSize (element: videojs.Component | HTMLElement) { + getComponentSize (element: VideojsComponent | HTMLElement) { let width: number = null let height: number = null @@ -217,7 +218,7 @@ class SettingsButton extends Button { this.focus() }) - this.menu.on('arrow-right', (_, el) => { + this.menu.on('arrow-right', (_: any, el: HTMLElement) => { debugLogger('Detected arrow right on menu item', el) el.click() @@ -257,7 +258,7 @@ class SettingsButton extends Button { // Hide children to avoid sub menus stacking on top of each other // or having multiple menus open - settingsMenuItem.on('click', videojs.bind(this, this.hideChildren)) + settingsMenuItem.on('click', () => this.hideChildren()) // Whether to add or remove selected class on the settings sub menu element settingsMenuItem.on('click', openSubMenu) @@ -288,6 +289,6 @@ class SettingsButton extends Button { } } -Component.registerComponent('SettingsButton', SettingsButton) +videojs.registerComponent('SettingsButton', SettingsButton) export { SettingsButton } diff --git a/client/src/standalone/player/src/shared/settings/settings-menu-item.ts b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts index 91ab0c794..989cc2fe8 100644 --- a/client/src/standalone/player/src/shared/settings/settings-menu-item.ts +++ b/client/src/standalone/player/src/shared/settings/settings-menu-item.ts @@ -1,37 +1,40 @@ import debug from 'debug' import videojs from 'video.js' +import { VideojsComponent, VideojsMenu, VideojsMenuItem, VideojsMenuItemOptions, VideojsPlayer } from '../../types' import { toTitleCase } from '../common' import { SettingsDialog } from './settings-dialog' import { SettingsButton } from './settings-menu-button' import { SettingsPanel } from './settings-panel' import { SettingsPanelChild } from './settings-panel-child' +import Button from 'video.js/dist/types/button' +import MenuButton from 'video.js/dist/types/menu/menu-button' const debugLogger = debug('peertube:player:settings') -const MenuItem = videojs.getComponent('MenuItem') -const Component = videojs.getComponent('Component') +const MenuItem = videojs.getComponent('MenuItem') as typeof VideojsMenuItem +const Component = videojs.getComponent('Component') as typeof VideojsComponent -interface MenuItemExtended extends videojs.MenuItem { +interface MenuItemExtended extends VideojsMenuItem { isSelected_: boolean getLabel?: () => string } -export interface SettingsMenuItemOptions extends videojs.MenuItemOptions { - entry: string - menuButton: SettingsButton +export interface SettingsMenuItemOptions extends VideojsMenuItemOptions { + entry?: string + menuButton?: SettingsButton } class SettingsMenuItem extends MenuItem { declare settingsButton: SettingsButton declare dialog: SettingsDialog - declare mainMenu: videojs.Menu + declare mainMenu: VideojsMenu declare panel: SettingsPanel declare panelChild: SettingsPanelChild declare panelChildEl: HTMLElement declare size: number[] declare menuToLoad: string - declare subMenu: SettingsButton + declare subMenu: MenuButton declare submenuClickHandler: typeof SettingsMenuItem.prototype.onSubmenuClick declare transitionEndHandler: typeof SettingsMenuItem.prototype.onTransitionEnd @@ -40,14 +43,14 @@ class SettingsMenuItem extends MenuItem { declare settingsSubMenuValueEl_: HTMLElement declare settingsSubMenuEl_: HTMLElement - constructor (player: videojs.Player, options?: SettingsMenuItemOptions) { + constructor (player: VideojsPlayer, options?: SettingsMenuItemOptions) { super(player, options) this.settingsButton = options.menuButton this.dialog = this.settingsButton.dialog this.mainMenu = this.settingsButton.menu - this.panel = this.dialog.getChild('settingsPanel') - this.panelChild = this.panel.getChild('settingsPanelChild') + this.panel = this.dialog.getChild('settingsPanel') as SettingsPanel + this.panelChild = this.panel.getChild('settingsPanelChild') as SettingsPanelChild this.panelChildEl = this.panelChild.el() as HTMLElement this.size = null @@ -56,15 +59,15 @@ class SettingsMenuItem extends MenuItem { this.menuToLoad = 'mainmenu' const subMenuName = toTitleCase(options.entry) - const SubMenuComponent = videojs.getComponent(subMenuName) + const SubMenuComponent = videojs.getComponent(subMenuName) as typeof MenuButton + + console.log(options.entry) if (!SubMenuComponent) { throw new Error(`Component ${subMenuName} does not exist`) } - const newOptions = Object.assign({}, options, { entry: options.menuButton, menuButton: this }) - - this.subMenu = new SubMenuComponent(this.player(), newOptions) as SettingsButton + this.subMenu = new SubMenuComponent(this.player(), { ...options }) const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0] this.settingsSubMenuEl_.className += ' ' + subMenuClass @@ -174,7 +177,7 @@ class SettingsMenuItem extends MenuItem { * * @method handleClick */ - handleClick (event: videojs.EventTarget.Event) { + handleClick (event: Event) { this.menuToLoad = 'submenu' // Remove open class to ensure only the open submenu gets this class videojs.dom.removeClass(this.el(), 'open') @@ -239,8 +242,7 @@ class SettingsMenuItem extends MenuItem { mainMenuEl.style.opacity = '0' // back button will always take you to main menu, so set dialog sizes - const mainMenuAny = this.mainMenu - this.settingsButton.setDialogSize([ mainMenuAny.width() as number, mainMenuAny.height() as number ]) + this.settingsButton.setDialogSize([ this.mainMenu.width(), this.mainMenu.height() ]) // animation not triggered without timeout (some async stuff ?!?) setTimeout(() => { @@ -376,7 +378,6 @@ class SettingsMenuItem extends MenuItem { } } -;(SettingsMenuItem as any).prototype.contentElType = 'button' videojs.registerComponent('SettingsMenuItem', SettingsMenuItem) export { SettingsMenuItem } diff --git a/client/src/standalone/player/src/shared/settings/settings-panel-child.ts b/client/src/standalone/player/src/shared/settings/settings-panel-child.ts index d4ce5ce3f..41bad40c3 100644 --- a/client/src/standalone/player/src/shared/settings/settings-panel-child.ts +++ b/client/src/standalone/player/src/shared/settings/settings-panel-child.ts @@ -1,9 +1,9 @@ import videojs from 'video.js' +import { VideojsComponent } from '../../types' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class SettingsPanelChild extends Component { - createEl () { return super.createEl('div', { className: 'vjs-settings-panel-child', @@ -12,6 +12,6 @@ class SettingsPanelChild extends Component { } } -Component.registerComponent('SettingsPanelChild', SettingsPanelChild) +videojs.registerComponent('SettingsPanelChild', SettingsPanelChild) export { SettingsPanelChild } diff --git a/client/src/standalone/player/src/shared/settings/settings-panel.ts b/client/src/standalone/player/src/shared/settings/settings-panel.ts index d7c198bbf..1fe35372d 100644 --- a/client/src/standalone/player/src/shared/settings/settings-panel.ts +++ b/client/src/standalone/player/src/shared/settings/settings-panel.ts @@ -1,9 +1,9 @@ import videojs from 'video.js' +import { VideojsComponent } from '../../types' -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent class SettingsPanel extends Component { - createEl () { return super.createEl('div', { className: 'vjs-settings-panel', @@ -12,6 +12,6 @@ class SettingsPanel extends Component { } } -Component.registerComponent('SettingsPanel', SettingsPanel) +videojs.registerComponent('SettingsPanel', SettingsPanel) export { SettingsPanel } diff --git a/client/src/standalone/player/src/shared/stats/stats-card.ts b/client/src/standalone/player/src/shared/stats/stats-card.ts index a40bc37de..f75d4a3d8 100644 --- a/client/src/standalone/player/src/shared/stats/stats-card.ts +++ b/client/src/standalone/player/src/shared/stats/stats-card.ts @@ -1,10 +1,11 @@ -import videojs from 'video.js' -import { logger } from '@root-helpers/logger' import { secondsToTime } from '@peertube/peertube-core-utils' -import { PlayerNetworkInfo as EventPlayerNetworkInfo } from '../../types' +import { logger } from '@root-helpers/logger' +import videojs from 'video.js' +import { TimeRange } from 'video.js/dist/types/utils/time' +import { PlayerNetworkInfo as EventPlayerNetworkInfo, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' import { bytes } from '../common' -interface StatsCardOptions extends videojs.ComponentOptions { +interface StatsCardOptions extends VideojsComponentOptions { videoUUID: string videoIsLive: boolean mode: 'web-video' | 'p2p-media-loader' @@ -28,7 +29,8 @@ interface InfoElement { value: HTMLElement } -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent + class StatsCard extends Component { declare options_: StatsCardOptions @@ -65,7 +67,7 @@ class StatsCard extends Component { declare private onNetworkInfoHandler: (_event: any, data: EventPlayerNetworkInfo) => void - constructor (player: videojs.Player, options?: StatsCardOptions) { + constructor (player: VideojsPlayer, options?: StatsCardOptions) { super(player, options) this.metadataStore = {} @@ -365,7 +367,7 @@ class StatsCard extends Component { return { root, value } } - private timeRangesToString (r: videojs.TimeRange) { + private timeRangesToString (r: TimeRange) { let result = '' for (let i = 0; i < r.length; i++) { diff --git a/client/src/standalone/player/src/shared/stats/stats-plugin.ts b/client/src/standalone/player/src/shared/stats/stats-plugin.ts index c680b2659..9dd64e365 100644 --- a/client/src/standalone/player/src/shared/stats/stats-plugin.ts +++ b/client/src/standalone/player/src/shared/stats/stats-plugin.ts @@ -1,12 +1,13 @@ import videojs from 'video.js' +import { VideojsPlayer, VideojsPlugin } from '../../types' import { StatsCard, StatsCardOptions } from './stats-card' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class StatsForNerdsPlugin extends Plugin { declare private statsCard: StatsCard - constructor (player: videojs.Player, options: StatsCardOptions) { + constructor (player: VideojsPlayer, options: StatsCardOptions) { super(player) this.player.ready(() => { diff --git a/client/src/standalone/player/src/shared/upnext/end-card.ts b/client/src/standalone/player/src/shared/upnext/end-card.ts index b056f4b9b..03db2d303 100644 --- a/client/src/standalone/player/src/shared/upnext/end-card.ts +++ b/client/src/standalone/player/src/shared/upnext/end-card.ts @@ -1,5 +1,5 @@ import videojs from 'video.js' -import { UpNextPluginOptions } from '../../types' +import { UpNextPluginOptions, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types' function getMainTemplate (options: EndCardOptions) { return ` @@ -24,13 +24,14 @@ function getMainTemplate (options: EndCardOptions) { ` } -export interface EndCardOptions extends videojs.ComponentOptions, UpNextPluginOptions { +export interface EndCardOptions extends VideojsComponentOptions, UpNextPluginOptions { cancelText: string headText: string suspendedText: string } -const Component = videojs.getComponent('Component') +const Component = videojs.getComponent('Component') as typeof VideojsComponent + export class EndCard extends Component { declare options_: EndCardOptions @@ -53,7 +54,7 @@ export class EndCard extends Component { declare private onEndedHandler: () => void declare private onPlayingHandler: () => void - constructor (player: videojs.Player, options: EndCardOptions) { + constructor (player: VideojsPlayer, options: EndCardOptions) { super(player, options) this.dashOffsetTotal = 586 diff --git a/client/src/standalone/player/src/shared/upnext/upnext-plugin.ts b/client/src/standalone/player/src/shared/upnext/upnext-plugin.ts index 45688978c..24571b4a2 100644 --- a/client/src/standalone/player/src/shared/upnext/upnext-plugin.ts +++ b/client/src/standalone/player/src/shared/upnext/upnext-plugin.ts @@ -1,12 +1,11 @@ import videojs from 'video.js' -import { UpNextPluginOptions } from '../../types' +import { UpNextPluginOptions, VideojsPlayer, VideojsPlugin } from '../../types' import { EndCard, EndCardOptions } from './end-card' -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class UpNextPlugin extends Plugin { - - constructor (player: videojs.Player, options: UpNextPluginOptions) { + constructor (player: VideojsPlayer, options: UpNextPluginOptions) { super(player) const settings: EndCardOptions = { diff --git a/client/src/standalone/player/src/shared/web-video/web-video-plugin.ts b/client/src/standalone/player/src/shared/web-video/web-video-plugin.ts index 0d4ae7146..2f2a84e98 100644 --- a/client/src/standalone/player/src/shared/web-video/web-video-plugin.ts +++ b/client/src/standalone/player/src/shared/web-video/web-video-plugin.ts @@ -3,11 +3,11 @@ import { VideoFile } from '@peertube/peertube-models' import { logger } from '@root-helpers/logger' import debug from 'debug' import videojs from 'video.js' -import { PeerTubeResolution, PlayerNetworkInfo, WebVideoPluginOptions } from '../../types' +import { PeerTubeResolution, PlayerNetworkInfo, VideojsPlayer, VideojsPlugin, WebVideoPluginOptions } from '../../types' const debugLogger = debug('peertube:player:web-video-plugin') -const Plugin = videojs.getPlugin('plugin') +const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin class WebVideoPlugin extends Plugin { declare private readonly videoFiles: VideoFile[] @@ -21,8 +21,8 @@ class WebVideoPlugin extends Plugin { declare private onPlayHandler: () => void declare private onLoadedMetadata: () => void - constructor (player: videojs.Player, options?: WebVideoPluginOptions) { - super(player, options) + constructor (player: VideojsPlayer, options?: WebVideoPluginOptions) { + super(player) this.videoFiles = options.videoFiles this.videoFileToken = options.videoFileToken @@ -109,7 +109,8 @@ class WebVideoPlugin extends Plugin { .then(() => { if (paused) this.player.pause() - this.player.autoplay(oldAutoplayValue) + // FIXME: typings + this.player.autoplay(oldAutoplayValue as any) }) } } diff --git a/client/src/standalone/player/src/types/peertube-player-options.ts b/client/src/standalone/player/src/types/peertube-player-options.ts index f26dc05ab..b908f0e96 100644 --- a/client/src/standalone/player/src/types/peertube-player-options.ts +++ b/client/src/standalone/player/src/types/peertube-player-options.ts @@ -1,7 +1,7 @@ import { LiveVideoLatencyModeType, VideoChapter, VideoFile } from '@peertube/peertube-models' import { PluginsManager } from '@root-helpers/plugins-manager' import { PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin' -import { PlaylistPluginOptions, VideoJSCaption, VideoJSStoryboard } from './peertube-videojs-typings' +import { PlaylistPluginOptions, VideoJSCaption, VideojsPlayer, VideoJSStoryboard } from './peertube-videojs-typings' export type PlayerMode = 'web-video' | 'p2p-media-loader' @@ -106,7 +106,7 @@ export type PeerTubePlayerLoadOptions = { upnext?: { isEnabled: () => boolean - isSuspended: (player: videojs.VideoJsPlayer) => boolean + isSuspended: (player: VideojsPlayer) => boolean timeout: number } diff --git a/client/src/standalone/player/src/types/peertube-videojs-typings.ts b/client/src/standalone/player/src/types/peertube-videojs-typings.ts index b0607a14c..34e08fc64 100644 --- a/client/src/standalone/player/src/types/peertube-videojs-typings.ts +++ b/client/src/standalone/player/src/types/peertube-videojs-typings.ts @@ -3,6 +3,22 @@ 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 BigPlayButton from 'video.js/dist/types/big-play-button' +import Button from 'video.js/dist/types/button' +import ClickableComponent from 'video.js/dist/types/clickable-component' +import Component from 'video.js/dist/types/component' +import ControlBar from 'video.js/dist/types/control-bar/control-bar' +import MouseTimeDisplay from 'video.js/dist/types/control-bar/progress-control/mouse-time-display' +import ProgressControl from 'video.js/dist/types/control-bar/progress-control/progress-control' +import SeekBar from 'video.js/dist/types/control-bar/progress-control/seek-bar' +import TimeTooltip from 'video.js/dist/types/control-bar/progress-control/time-tooltip' +import type CaptionButton from 'video.js/dist/types/control-bar/text-track-controls/captions-button' +import LoadingSpinner from 'video.js/dist/types/loading-spinner' +import Menu from 'video.js/dist/types/menu/menu' +import MenuButton from 'video.js/dist/types/menu/menu-button' +import MenuItem from 'video.js/dist/types/menu/menu-item' +import type Plugin from 'video.js/dist/types/plugin' +import Tech from 'video.js/dist/types/tech/tech' import { BezelsPlugin } from '../shared/bezels/bezels-plugin' import { ContextMenuPlugin } from '../shared/context-menu' import { ChaptersPlugin } from '../shared/control-bar/chapters-plugin' @@ -23,65 +39,28 @@ import { StatsForNerdsPlugin } from '../shared/stats/stats-plugin' import { UpNextPlugin } from '../shared/upnext/upnext-plugin' import { WebVideoPlugin } from '../shared/web-video/web-video-plugin' import { PlayerMode } from './peertube-player-options' +import { SettingsButton } from '../shared/settings/settings-menu-button' declare module 'video.js' { export interface VideoJsPlayer { - srOptions_: HlsjsConfigHandlerOptions - - theaterEnabled: boolean - // FIXME: add it to upstream typings - posterImage: { - show(): void - hide(): void - } handleTechSeeked_(): void textTracks(): TextTrackList & { tracks_: (TextTrack & { id: string, label: string, src: string })[] } - - // Plugins - - peertube(): PeerTubePlugin - - webVideo(options?: any): WebVideoPlugin - - p2pMediaLoader(options?: any): P2pMediaLoaderPlugin - hlsjs(options?: any): any - - peertubeResolutions(): PeerTubeResolutionsPlugin - - contextMenu(options?: ContextMenuPluginOptions): ContextMenuPlugin - - bezels(): BezelsPlugin - peertubeMobile(): PeerTubeMobilePlugin - peerTubeHotkeysPlugin(options?: HotkeysOptions): PeerTubeHotkeysPlugin - - stats(options?: StatsCardOptions): StatsForNerdsPlugin - - storyboard(options?: StoryboardOptions): StoryboardPlugin - - peertubeDock(options?: PeerTubeDockPluginOptions): PeerTubeDockPlugin - peertubeNSFW(options?: PeerTubeNSFWPluginOptions): PeerTubeNSFWPlugin - - chapters(options?: ChaptersOptions): ChaptersPlugin - - upnext(options?: UpNextPluginOptions): UpNextPlugin - - playlist(options?: PlaylistPluginOptions): PlaylistPlugin } } -export interface VideoJSTechHLS extends videojs.Tech { +export interface VideoJSTechHLS extends Tech { hlsProvider: Html5Hlsjs } export interface HlsjsConfigHandlerOptions { hlsjsConfig?: HlsConfig - levelLabelHandler?: (level: Level, player: videojs.Player) => string + levelLabelHandler?: (level: Level, player: VideojsPlayer) => string } export type PeerTubeResolution = { @@ -116,7 +95,7 @@ export type PeerTubePluginOptions = { cssPlayerPortraitModeVariable: string } - hasAutoplay: () => videojs.Autoplay + hasAutoplay: () => VideojsAutoplay videoViewUrl: () => string videoViewIntervalMs: number @@ -154,7 +133,7 @@ export type ContextMenuPluginOptions = { } export type ContextMenuItemOptions = { - listener: (e: videojs.EventTarget.Event) => void + listener: (e?: Event) => void label: string } @@ -215,7 +194,7 @@ export type WebVideoPluginOptions = { } export type HLSLoaderClass = { - new(confg: HlsConfig): Loader + new(config: HlsConfig): Loader getEngine(): HlsJsP2PEngine } @@ -300,3 +279,150 @@ export type PlaylistItemOptions = { onClicked: () => void } + +type EventHandler = (event: any, data: any) => void + +export declare class VideojsPlugin extends Plugin { + player: VideojsPlayer + + on (event: string, handler: EventHandler): this + one (event: string, handler: EventHandler): void + off (event: string, handler: EventHandler): void +} + +export type VideojsPlayer = ReturnType & { + options_: ReturnType['options_'] & { + loop: boolean + + userActions: { + click?: boolean + doubleClick?: boolean + } + } + + posterImage: { + show(): void + hide(): void + } + + error: (err?: MediaError | string | number) => MediaError + + on(event: string, handler: EventHandler): void + + loadingSpinner: LoadingSpinner + controlBar: ControlBar & { + settingsButton: SettingsButton + + progressControl: ProgressControl & { + seekBar: SeekBar & { + mouseTimeDisplay: { + timeTooltip: MouseTimeDisplay + } + } + } + } + bigPlayButton: BigPlayButton + + srOptions_: HlsjsConfigHandlerOptions + + // --------------------------------------------------------------------------- + + peertube(): PeerTubePlugin + + webVideo(options?: any): WebVideoPlugin + + p2pMediaLoader(options?: any): P2pMediaLoaderPlugin + hlsjs(options?: any): any + + peertubeResolutions(): PeerTubeResolutionsPlugin + + contextMenu(options?: ContextMenuPluginOptions): ContextMenuPlugin + + bezels(): BezelsPlugin + peertubeMobile(): PeerTubeMobilePlugin + peerTubeHotkeysPlugin(options?: HotkeysOptions): PeerTubeHotkeysPlugin + + stats(options?: StatsCardOptions): StatsForNerdsPlugin + + storyboard(options?: StoryboardOptions): StoryboardPlugin + + peertubeDock(options?: PeerTubeDockPluginOptions): PeerTubeDockPlugin + peertubeNSFW(options?: PeerTubeNSFWPluginOptions): PeerTubeNSFWPlugin + + chapters(options?: ChaptersOptions): ChaptersPlugin + + upnext(options?: UpNextPluginOptions): UpNextPlugin + + playlist(options?: PlaylistPluginOptions): PlaylistPlugin + + // --------------------------------------------------------------------------- + + theaterEnabled: boolean +} + +export declare class VideojsComponent extends Component { + player: () => VideojsPlayer + player_: VideojsPlayer +} + +export declare class VideojsMenuItem extends MenuItem { + player: () => VideojsPlayer + player_: VideojsPlayer +} + +export declare class VideojsMenu extends Menu { + player: () => VideojsPlayer + player_: VideojsPlayer +} + +export declare class VideojsMenuButton extends MenuButton { + player: () => VideojsPlayer + player_: VideojsPlayer + + // FIXME: typings + setIcon: Component['setIcon'] +} + +export declare class VideojsButton extends Button { + player: () => VideojsPlayer + player_: VideojsPlayer +} + +export declare class VideojsClickableComponent extends ClickableComponent { + player: () => VideojsPlayer + player_: VideojsPlayer +} + +export declare class VideojsTimeTooltip extends TimeTooltip { + player: () => VideojsPlayer + player_: VideojsPlayer +} + +export type VideojsComponentOptions = ConstructorParameters[1] +export type VideojsMenuItemOptions = ConstructorParameters[1] +export type VideojsMenuOptions = ConstructorParameters[1] +export type VideojsButtonOptions = ConstructorParameters[1] +export type VideojsClickableComponentOptions = ConstructorParameters[1] +export type VideojsMenuButtonOptions = ConstructorParameters[1] + +export type VideojsAutoplay = boolean | 'muted' | 'play' | 'any' + +export type VideojsPlayerOptions = Partial & { + muted?: boolean + controls?: boolean + + autoplay?: VideojsAutoplay + + poster?: string + preload?: 'none' + + plugins?: VideoJSPluginOptions + + controlBar?: { + children: Record + } + + textTrackSettings?: boolean +} + +export type VideojsCaptionsButton = CaptionButton & Component diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 3bcf034bf..28d453aa4 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -7,7 +7,7 @@ import { VideoPlaylistElement, VideoState } from '@peertube/peertube-models' -import type { PeerTubePlayer } from '@peertube/player' +import type { PeerTubePlayer, VideojsPlayer } from '@peertube/player' import { TranslationsManager } from '@root-helpers/translations-manager' import { PeerTubeServerError } from 'src/types' import type videojs from 'video.js' @@ -29,7 +29,7 @@ import { import { PlayerHTML } from './shared/player-html' export class PeerTubeEmbed { - player: videojs.Player + player: VideojsPlayer api: PeerTubeEmbedApi = null config: HTMLServerConfig diff --git a/client/yarn.lock b/client/yarn.lock index 23374c599..f83b8c636 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2265,11 +2265,6 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/video.js@^7.3.40": - version "7.3.58" - resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.3.58.tgz#7e8cdafee25c75d6eb18f530b93ac52edff53c03" - integrity sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ== - "@types/which@^2.0.1": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/which/-/which-2.0.2.tgz#54541d02d6b1daee5ec01ac0d1b37cecf37db1ae" @@ -2387,33 +2382,41 @@ "@typescript-eslint/types" "8.32.0" eslint-visitor-keys "^4.2.0" -"@videojs/http-streaming@2.16.3": - version "2.16.3" - resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-2.16.3.tgz#d9b460c3716436327dbab4b1faeb2a767f05dcef" - integrity sha512-91CJv5PnFBzNBvyEjt+9cPzTK/xoVixARj2g7ZAvItA+5bx8VKdk5RxCz/PP2kdzz9W+NiDUMPkdmTsosmy69Q== +"@videojs/http-streaming@^3.17.2": + version "3.17.2" + resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-3.17.2.tgz#264eaf23980f4f0e3ad918a665ac60f178f01ff8" + integrity sha512-VBQ3W4wnKnVKb/limLdtSD2rAd5cmHN70xoMf4OmuDd0t2kfJX04G+sfw6u2j8oOm2BXYM9E1f4acHruqKnM1g== dependencies: "@babel/runtime" "^7.12.5" - "@videojs/vhs-utils" "3.0.5" - aes-decrypter "3.1.3" + "@videojs/vhs-utils" "^4.1.1" + aes-decrypter "^4.0.2" global "^4.4.0" - m3u8-parser "4.8.0" - mpd-parser "^0.22.1" - mux.js "6.0.1" - video.js "^6 || ^7" + m3u8-parser "^7.2.0" + mpd-parser "^1.3.1" + mux.js "7.1.0" + video.js "^7 || ^8" -"@videojs/vhs-utils@3.0.5", "@videojs/vhs-utils@^3.0.4", "@videojs/vhs-utils@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz#665ba70d78258ba1ab977364e2fe9f4d4799c46c" - integrity sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw== +"@videojs/vhs-utils@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-4.0.0.tgz#4d4dbf5d61a9fbd2da114b84ec747c3a483bc60d" + integrity sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg== dependencies: "@babel/runtime" "^7.12.5" global "^4.4.0" url-toolkit "^2.2.1" -"@videojs/xhr@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@videojs/xhr/-/xhr-2.6.0.tgz#cd897e0ad54faf497961bcce3fa16dc15a26bb80" - integrity sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q== +"@videojs/vhs-utils@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-4.1.1.tgz#44226fc5993f577490b5e08951ddc083714405cc" + integrity sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA== + dependencies: + "@babel/runtime" "^7.12.5" + global "^4.4.0" + +"@videojs/xhr@2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@videojs/xhr/-/xhr-2.7.0.tgz#e272af6e2b5448aeb400905a5c6f4818f6b6ad47" + integrity sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ== dependencies: "@babel/runtime" "^7.5.5" global "~4.4.0" @@ -2709,13 +2712,13 @@ addressparser@^1.0.1: resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" integrity sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg== -aes-decrypter@3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/aes-decrypter/-/aes-decrypter-3.1.3.tgz#65ff5f2175324d80c41083b0e135d1464b12ac35" - integrity sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A== +aes-decrypter@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/aes-decrypter/-/aes-decrypter-4.0.2.tgz#90648181c68878f54093920a3b44776ec2dc4914" + integrity sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw== dependencies: "@babel/runtime" "^7.12.5" - "@videojs/vhs-utils" "^3.0.5" + "@videojs/vhs-utils" "^4.1.1" global "^4.4.0" pkcs7 "^1.0.4" @@ -5402,7 +5405,7 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -global@^4.3.1, global@^4.4.0, global@~4.4.0: +global@4.4.0, global@^4.3.1, global@^4.4.0, global@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== @@ -5745,11 +5748,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -individual@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/individual/-/individual-2.0.0.tgz#833b097dad23294e76117a98fb38e0d9ad61bb97" - integrity sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -6322,11 +6320,6 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" -keycode@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.1.tgz#09c23b2be0611d26117ea2501c2c391a01f39eff" - integrity sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg== - keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -6575,13 +6568,13 @@ lru@^3.1.0: dependencies: inherits "^2.0.1" -m3u8-parser@4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-4.8.0.tgz#4a2d591fdf6f2579d12a327081198df8af83083d" - integrity sha512-UqA2a/Pw3liR6Df3gwxrqghCP17OpPlQj6RBPLYygf/ZSQ4MoSgvdvhvt35qV+3NaaA0FSZx93Ix+2brT1U7cA== +m3u8-parser@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-7.2.0.tgz#9e2eb50abb8349d248cd58842367da4acabdf297" + integrity sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ== dependencies: "@babel/runtime" "^7.12.5" - "@videojs/vhs-utils" "^3.0.5" + "@videojs/vhs-utils" "^4.1.1" global "^4.4.0" magic-string@0.30.17, magic-string@^0.30.12, magic-string@^0.30.3: @@ -6921,13 +6914,13 @@ mocha@^10.3.0: yargs-parser "^20.2.9" yargs-unparser "^2.0.0" -mpd-parser@0.22.1, mpd-parser@^0.22.1: - version "0.22.1" - resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.22.1.tgz#bc2bf7d3e56368e4b0121035b055675401871521" - integrity sha512-fwBebvpyPUU8bOzvhX0VQZgSohncbgYwUyJJoTSNpmy7ccD2ryiCvM7oRkn/xQH5cv73/xU7rJSNCLjdGFor0Q== +mpd-parser@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-1.3.1.tgz#557b6ac27411c2c177bb01e46e14440703a414a3" + integrity sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw== dependencies: "@babel/runtime" "^7.12.5" - "@videojs/vhs-utils" "^3.0.5" + "@videojs/vhs-utils" "^4.0.0" "@xmldom/xmldom" "^0.8.3" global "^4.4.0" @@ -6972,10 +6965,10 @@ mute-stream@^2.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== -mux.js@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-6.0.1.tgz#65ce0f7a961d56c006829d024d772902d28c7755" - integrity sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w== +mux.js@7.1.0, mux.js@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-7.1.0.tgz#aba5ed55a39cb790ef4b30b2c3ea0d2630b0264e" + integrity sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA== dependencies: "@babel/runtime" "^7.11.2" global "^4.4.0" @@ -8311,13 +8304,6 @@ run-series@^1.1.9: resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.9.tgz#15ba9cb90e6a6c054e67c98e1dc063df0ecc113a" integrity sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g== -rust-result@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rust-result/-/rust-result-1.0.0.tgz#34c75b2e6dc39fe5875e5bdec85b5e0f91536f72" - integrity sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA== - dependencies: - individual "^2.0.0" - rxjs@7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" @@ -8358,13 +8344,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-json-parse@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz#7c0f578cfccd12d33a71c0e05413e2eca171eaac" - integrity sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ== - dependencies: - rust-result "^1.0.0" - safe-push-apply@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" @@ -8946,16 +8925,7 @@ strict-event-emitter@^0.1.0: resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.1.0.tgz#fd742c1fb7e3852f0b964ecdae2d7666a6fb7ef8" integrity sha512-8hSYfU+WKLdNcHVXJ0VxRXiPESalzRe7w1l8dg9+/22Ry+iZQUoQuoJ27R30GMD1TiyYINWsIEGY05WrskhSKw== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9036,14 +9006,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9747,31 +9710,37 @@ vary@^1, vary@^1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -"video.js@^6 || ^7", video.js@^7.19.2: - version "7.21.7" - resolved "https://registry.yarnpkg.com/video.js/-/video.js-7.21.7.tgz#8feccac174c5f202144b2f84138352a81f263dcd" - integrity sha512-T2s3WFAht7Zjr2OSJamND9x9Dn2O+Z5WuHGdh8jI5SYh5mkMdVTQ7vSRmA5PYpjXJ2ycch6jpMjkJEIEU2xxqw== +"video.js@^7 || ^8", video.js@^8.23.4: + version "8.23.4" + resolved "https://registry.yarnpkg.com/video.js/-/video.js-8.23.4.tgz#65876174dfcee1057102a03a847fdaa8cf346c66" + integrity sha512-qI0VTlYmKzEqRsz1Nppdfcaww4RSxZAq77z2oNSl3cNg2h6do5C8Ffl0KqWQ1OpD8desWXsCrde7tKJ9gGTEyQ== dependencies: "@babel/runtime" "^7.12.5" - "@videojs/http-streaming" "2.16.3" - "@videojs/vhs-utils" "^3.0.4" - "@videojs/xhr" "2.6.0" - aes-decrypter "3.1.3" + "@videojs/http-streaming" "^3.17.2" + "@videojs/vhs-utils" "^4.1.1" + "@videojs/xhr" "2.7.0" + aes-decrypter "^4.0.2" + global "4.4.0" + m3u8-parser "^7.2.0" + mpd-parser "^1.3.1" + mux.js "^7.0.1" + videojs-contrib-quality-levels "4.1.0" + videojs-font "4.2.0" + videojs-vtt.js "0.15.5" + +videojs-contrib-quality-levels@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.1.0.tgz#44c2d2167114a5c8418548b10a25cb409d6cba51" + integrity sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA== + dependencies: global "^4.4.0" - keycode "^2.2.0" - m3u8-parser "4.8.0" - mpd-parser "0.22.1" - mux.js "6.0.1" - safe-json-parse "4.0.0" - videojs-font "3.2.0" - videojs-vtt.js "^0.15.5" -videojs-font@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-3.2.0.tgz#212c9d3f4e4ec3fa7345167d64316add35e92232" - integrity sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA== +videojs-font@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-4.2.0.tgz#fbce803d347c565816e296f527e208dc65c9f235" + integrity sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ== -videojs-vtt.js@^0.15.5: +videojs-vtt.js@0.15.5: version "0.15.5" resolved "https://registry.yarnpkg.com/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz#567776eaf2a7a928d88b148a8b401ade2406f2ca" integrity sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ== @@ -10058,7 +10027,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10076,15 +10045,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" diff --git a/locales/fr-FR/translation.json b/locales/fr-FR/translation.json new file mode 100644 index 000000000..e69de29bb