1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-03 09:49:20 +02:00

Upgrade to videojs v8

This commit is contained in:
Chocobozzz 2025-08-11 08:30:00 +02:00
parent 906b5f7f2c
commit 48ea20c9e4
No known key found for this signature in database
GPG key ID: 583A612D890159BE
57 changed files with 570 additions and 438 deletions

View file

@ -68,7 +68,6 @@
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/sanitize-html": "2.11.0", "@types/sanitize-html": "2.11.0",
"@types/sha.js": "^2.4.0", "@types/sha.js": "^2.4.0",
"@types/video.js": "^7.3.40",
"@wdio/browserstack-service": "^9.12.7", "@wdio/browserstack-service": "^9.12.7",
"@wdio/cli": "^9.12.7", "@wdio/cli": "^9.12.7",
"@wdio/globals": "^9.17.0", "@wdio/globals": "^9.17.0",
@ -111,7 +110,7 @@
"type-fest": "^4.37.0", "type-fest": "^4.37.0",
"typescript": "~5.7.3", "typescript": "~5.7.3",
"ua-parser-js": "^2.0.3", "ua-parser-js": "^2.0.3",
"video.js": "^7.19.2", "video.js": "^8.23.4",
"vite": "^6.0.11", "vite": "^6.0.11",
"vite-plugin-checker": "^0.9.3", "vite-plugin-checker": "^0.9.3",
"vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-node-polyfills": "^0.24.0",

View file

@ -52,7 +52,8 @@ import {
PeerTubePlayerConstructorOptions, PeerTubePlayerConstructorOptions,
PeerTubePlayerLoadOptions, PeerTubePlayerLoadOptions,
PlayerMode, PlayerMode,
videojs videojs,
VideojsPlayer
} from '@peertube/player' } from '@peertube/player'
import { logger } from '@root-helpers/logger' import { logger } from '@root-helpers/logger'
import { isP2PEnabled, videoRequiresFileToken, videoRequiresUserAuth } from '@root-helpers/video' import { isP2PEnabled, videoRequiresFileToken, videoRequiresUserAuth } from '@root-helpers/video'
@ -885,7 +886,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
return loggedInOrAnonymousUser?.autoPlayNextVideo return loggedInOrAnonymousUser?.autoPlayNextVideo
}, },
isSuspended: (player: videojs.Player) => { isSuspended: (player: VideojsPlayer) => {
return !isXPercentInViewport(player.el() as HTMLElement, 80) return !isXPercentInViewport(player.el() as HTMLElement, 80)
}, },

View file

@ -5,7 +5,7 @@ import { TranslationsManager } from '@root-helpers/translations-manager'
import { copyToClipboard } from '@root-helpers/utils' import { copyToClipboard } from '@root-helpers/utils'
import { buildVideoOrPlaylistEmbed } from '@root-helpers/video' import { buildVideoOrPlaylistEmbed } from '@root-helpers/video'
import { isMobile } from '@root-helpers/web-browser' 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 { saveAverageBandwidth } from './peertube-player-local-storage'
import './shared/bezels/bezels-plugin' import './shared/bezels/bezels-plugin'
import './shared/context-menu' import './shared/context-menu'
@ -20,12 +20,12 @@ import './shared/control-bar/theater-button'
import './shared/control-bar/time-tooltip' import './shared/control-bar/time-tooltip'
import './shared/dock/peertube-dock-component' import './shared/dock/peertube-dock-component'
import './shared/dock/peertube-dock-plugin' 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/hotkeys/peertube-hotkeys-plugin'
import './shared/metrics/metrics-plugin' import './shared/metrics/metrics-plugin'
import './shared/mobile/peertube-mobile-buttons' import './shared/mobile/peertube-mobile-buttons'
import './shared/mobile/peertube-mobile-plugin' 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/hls-plugin'
import './shared/p2p-media-loader/p2p-media-loader-plugin' import './shared/p2p-media-loader/p2p-media-loader-plugin'
import './shared/peertube/peertube-plugin' import './shared/peertube/peertube-plugin'
@ -45,7 +45,15 @@ import './shared/stats/stats-plugin'
import './shared/upnext/end-card' import './shared/upnext/end-card'
import './shared/upnext/upnext-plugin' import './shared/upnext/upnext-plugin'
import './shared/web-video/web-video-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 const CaptionsButton = videojs.getComponent('CaptionsButton') as any
// Change Captions to Subtitles/CC // Change Captions to Subtitles/CC
@ -70,7 +78,7 @@ export class PeerTubePlayer {
private videojsDecodeErrors = 0 private videojsDecodeErrors = 0
private player: VideoJsPlayer private player: VideojsPlayer
private currentLoadOptions: PeerTubePlayerLoadOptions private currentLoadOptions: PeerTubePlayerLoadOptions
@ -210,7 +218,7 @@ export class PeerTubePlayer {
this.getVideojsOptions() this.getVideojsOptions()
) )
this.player = videojs(this.options.playerElement(), videojsOptions) this.player = videojs(this.options.playerElement(), videojsOptions) as VideojsPlayer
this.player.ready(() => { this.player.ready(() => {
if (!isNaN(+this.options.playbackRate)) { if (!isNaN(+this.options.playbackRate)) {
@ -233,7 +241,7 @@ export class PeerTubePlayer {
this.player.on('video-change', () => alreadyFallback = false) this.player.on('video-change', () => alreadyFallback = false)
this.player.on('error', () => handleError()) 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 if (data.source !== 'p2p-media-loader' || isNaN(data.bandwidthEstimate)) return
saveAverageBandwidth(data.bandwidthEstimate) saveAverageBandwidth(data.bandwidthEstimate)
@ -366,7 +374,7 @@ export class PeerTubePlayer {
}) })
} }
getVideojsOptions (): videojs.PlayerOptions { getVideojsOptions (): VideojsPlayerOptions {
const html5 = { const html5 = {
preloadTextTracks: false, preloadTextTracks: false,
// Prevent a bug on iOS where the text tracks added by peertube plugin are removed on play // 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, html5,
// We don't use text track settings for now // 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, controls: this.options.controls !== undefined ? this.options.controls : true,
loop: this.options.loop !== undefined ? this.options.loop : false, loop: this.options.loop !== undefined ? this.options.loop : false,
@ -441,18 +449,20 @@ export class PeerTubePlayer {
plugins, plugins,
controlBar: { controlBar: {
children: controlBarOptionsBuilder.getChildrenOptions() as any // FIXME: typings children: controlBarOptionsBuilder.getChildrenOptions()
}, },
language: this.options.language && !isDefaultLocale(this.options.language) language: this.options.language && !isDefaultLocale(this.options.language)
? this.options.language ? this.options.language
: undefined : undefined,
}
enableSmoothSeeking: true
} satisfies VideojsPlayerOptions
return videojsOptions return videojsOptions
} }
private getAutoPlayValue (autoplay: boolean): videojs.Autoplay { private getAutoPlayValue (autoplay: boolean): VideojsAutoplay {
if (autoplay !== true) return false if (autoplay !== true) return false
return this.currentLoadOptions.forceAutoplay return this.currentLoadOptions.forceAutoplay
@ -500,14 +510,14 @@ export class PeerTubePlayer {
const player = this.player const player = this.player
const shortUUID = self.currentLoadOptions.videoShortUUID const shortUUID = self.currentLoadOptions.videoShortUUID
const isLoopEnabled = player.options_['loop'] const isLoopEnabled = player.options_.loop
const items = [ const items = [
{ {
icon: 'repeat', icon: 'repeat',
label: player.localize('Play in loop') + (isLoopEnabled ? '<span class="vjs-icon-tick-white"></span>' : ''), label: player.localize('Play in loop') + (isLoopEnabled ? '<span class="vjs-icon-tick-white"></span>' : ''),
listener: function () { listener: function () {
player.options_['loop'] = !isLoopEnabled player.options_.loop = !isLoopEnabled
} }
}, },
{ {

View file

@ -23,8 +23,8 @@ $context-menu-width: 350px;
cursor: pointer; cursor: pointer;
font-size: 1em; font-size: 1em;
padding: 8px 16px; padding: 8px 16px;
text-align: start;
text-transform: none; text-transform: none;
justify-content: flex-start;
&:hover { &:hover {
background-color: rgba(255, 255, 255, 0.2); background-color: rgba(255, 255, 255, 0.2);

View file

@ -40,7 +40,6 @@ $setting-transition-easing: ease-out;
} }
.vjs-settings-sub-menu-title { .vjs-settings-sub-menu-title {
text-align: start;
font-weight: $font-semibold; font-weight: $font-semibold;
} }
@ -118,16 +117,19 @@ $setting-transition-easing: ease-out;
.vjs-menu-item { .vjs-menu-item {
outline: 0; outline: 0;
font-weight: $font-semibold; font-weight: $font-semibold;
text-align: end; justify-content: flex-end;
padding: 8px 15px; padding: 8px 15px;
&.vjs-back-button { &.vjs-back-button {
padding: 12px 15px; padding: 12px 15px;
margin-bottom: 5px; margin-bottom: 5px;
border-bottom: 1px solid #808080; border-bottom: 1px solid #808080;
text-align: start; justify-content: flex-start;
align-items: center;
&::before { &::before {
top: -1px;
@include chevron-left(9px, 2px); @include chevron-left(9px, 2px);
@include margin-right(10px); @include margin-right(10px);
} }

View file

@ -1,11 +1,11 @@
import videojs from 'video.js' import videojs from 'video.js'
import type { VideojsComponentOptions, VideojsPlayer, VideojsPlugin } from '../../types/peertube-videojs-typings'
import { PauseBezel } from './pause-bezel' import { PauseBezel } from './pause-bezel'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class BezelsPlugin extends Plugin { class BezelsPlugin extends Plugin {
constructor (player: VideojsPlayer, options?: VideojsComponentOptions) {
constructor (player: videojs.Player, options?: videojs.ComponentOptions) {
super(player) super(player)
this.player.ready(() => { this.player.ready(() => {

View file

@ -1,5 +1,6 @@
import videojs from 'video.js'
import { isMobile } from '@root-helpers/web-browser' import { isMobile } from '@root-helpers/web-browser'
import videojs from 'video.js'
import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types'
function getPauseBezel () { function getPauseBezel () {
return ` 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 { export class PauseBezel extends Component {
declare container: HTMLDivElement declare container: HTMLDivElement
@ -42,7 +44,7 @@ export class PauseBezel extends Component {
declare private playerPlayHandler: () => void declare private playerPlayHandler: () => void
declare private videoChangeHandler: () => void declare private videoChangeHandler: () => void
constructor (player: videojs.Player, options?: videojs.ComponentOptions) { constructor (player: VideojsPlayer, options?: VideojsComponentOptions) {
super(player, options) super(player, options)
// Hide bezels on mobile since we already have our mobile overlay // Hide bezels on mobile since we already have our mobile overlay

View file

@ -1,17 +1,17 @@
import videojs from 'video.js' 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 { class ContextMenuItem extends MenuItem {
declare options_: ContextMenuItemOptions declare options_: ContextMenuItemOptions
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor (player: videojs.Player, options: ContextMenuItemOptions) { constructor (player: VideojsPlayer, options: VideojsComponentOptions & ContextMenuItemOptions) {
super(player, options) super(player, options)
} }
handleClick (e: videojs.EventTarget.Event) { handleClick (e: Event) {
super.handleClick(e) super.handleClick(e)
this.options_.listener(e) this.options_.listener(e)

View file

@ -1,18 +1,18 @@
import videojs, { VideoJsPlayer } from 'video.js' import videojs from 'video.js'
import { ContextMenuPluginOptions } from '../../types' import { ContextMenuPluginOptions, VideojsMenuItemOptions, VideojsPlayer, VideojsPlugin } from '../../types'
import { ContextMenu } from './context-menu' import { ContextMenu } from './context-menu'
import { getPointerPosition } from './util' import { getPointerPosition } from './util'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class ContextMenuPlugin extends Plugin { class ContextMenuPlugin extends Plugin {
declare options_: ContextMenuPluginOptions & videojs.MenuOptions declare options_: ContextMenuPluginOptions & VideojsMenuItemOptions
declare menu: ContextMenu declare menu: ContextMenu
declare private onContextMenuBind: (e: TouchEvent & MouseEvent) => void declare private onContextMenuBind: (e: TouchEvent & MouseEvent) => void
constructor (player: videojs.Player, options: ContextMenuPluginOptions & videojs.MenuOptions) { constructor (player: VideojsPlayer, options: ContextMenuPluginOptions & VideojsMenuItemOptions) {
super(player, options) super(player)
this.options_ = options this.options_ = options
@ -96,7 +96,7 @@ export { ContextMenuPlugin }
// Private // Private
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function hasMenu (player: VideoJsPlayer) { function hasMenu (player: VideojsPlayer) {
if (!player.usingPlugin('contextMenu')) return false if (!player.usingPlugin('contextMenu')) return false
return !!player.contextMenu().menu?.el() return !!player.contextMenu().menu?.el()

View file

@ -1,33 +1,32 @@
import videojs from 'video.js' import videojs from 'video.js'
import { ContextMenuPluginOptions, VideojsMenu, VideojsMenuOptions, VideojsPlayer } from '../../types'
import { ContextMenuItem } from './context-menu-item' 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 } } type ContextMenuOptions = ContextMenuPluginOptions & { position: { left: number, top: number } }
class ContextMenu extends Menu { 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 }) super(player, { ...options, menuButton: undefined })
// Each menu component has its own `dispose` method that can be // TODO: explain why we need this (I don't understand it)
// safely bound and unbound to events while maintaining its context. this.dispose = this.dispose.bind(this)
this.dispose = videojs.bind(this, this.dispose)
for (const c of options.content()) { for (const c of options.content()) {
this.addItem( this.addItem(
new ContextMenuItem(player, { new ContextMenuItem(player, {
label: c.label, label: c.label,
listener: videojs.bind(player, c.listener) listener: c.listener.bind(player)
}) })
) )
} }
} }
createEl () { createEl () {
const el = super.createEl() const el = super.createEl() as HTMLElement
videojs.dom.addClass(el, 'vjs-contextmenu-ui-menu') videojs.dom.addClass(el, 'vjs-contextmenu-ui-menu')
el.style.left = this.options_.position.left + 'px' el.style.left = this.options_.position.left + 'px'

View file

@ -1,11 +1,11 @@
import videojs from 'video.js' import videojs from 'video.js'
import { TheaterButtonOptions } from '../../types'
import { getStoredPreferredSubtitle } from '../../peertube-player-local-storage' 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 { class CaptionToggleButton extends Button {
constructor (player: VideojsPlayer, options: TheaterButtonOptions & VideojsButtonOptions) {
constructor (player: videojs.Player, options: TheaterButtonOptions & videojs.ComponentOptions) {
super(player, options) super(player, options)
player.on('texttrackchange', () => this.update()) player.on('texttrackchange', () => this.update())

View file

@ -1,9 +1,9 @@
import { VideoChapter } from '@peertube/peertube-models' import { VideoChapter } from '@peertube/peertube-models'
import videojs from 'video.js' import videojs from 'video.js'
import { ChaptersOptions } from '../../types' import { ChaptersOptions, VideojsPlayer, VideojsPlugin } from '../../types'
import { ProgressBarMarkerComponent } from './progress-bar-marker-component' import { ProgressBarMarkerComponent } from './progress-bar-marker-component'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class ChaptersPlugin extends Plugin { class ChaptersPlugin extends Plugin {
declare private chapters: VideoChapter[] declare private chapters: VideoChapter[]
@ -11,8 +11,8 @@ class ChaptersPlugin extends Plugin {
private activeChapter: VideoChapter private activeChapter: VideoChapter
constructor (player: videojs.Player, options: videojs.ComponentOptions & ChaptersOptions) { constructor (player: VideojsPlayer, options: ChaptersOptions) {
super(player, options) super(player)
this.markers = [] this.markers = []
this.chapters = options.chapters this.chapters = options.chapters
@ -25,8 +25,13 @@ class ChaptersPlugin extends Plugin {
const marker = new ProgressBarMarkerComponent(player, { timecode: chapter.timecode }) const marker = new ProgressBarMarkerComponent(player, { timecode: chapter.timecode })
marker.on('mouseenter', () => this.activeChapter = chapter) marker.on('mouseenter', () => {
marker.on('mouseleave', () => this.activeChapter = undefined) this.activeChapter = chapter
})
marker.on('mouseleave', () => {
this.activeChapter = undefined
})
this.markers.push(marker) this.markers.push(marker)
this.getSeekBar().addChild(marker) this.getSeekBar().addChild(marker)

View file

@ -1,12 +1,12 @@
import videojs from 'video.js' 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 { 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) super(player, options)
this.player().on('video-change', () => { this.player().on('video-change', () => {

View file

@ -1,8 +1,9 @@
import videojs from 'video.js' import videojs from 'video.js'
import { PlayerNetworkInfo } from '../../types' import { PlayerNetworkInfo, VideojsButton } from '../../types'
import { bytes } from '../common' import { bytes } from '../common'
const Button = videojs.getComponent('Button') const Button = videojs.getComponent('Button') as typeof VideojsButton
class P2PInfoButton extends Button { class P2PInfoButton extends Button {
createEl () { createEl () {
const div = videojs.dom.createEl('div', { className: 'vjs-peertube' }) const div = videojs.dom.createEl('div', { className: 'vjs-peertube' })
@ -87,7 +88,7 @@ class P2PInfoButton extends Button {
subDivP2P.className = 'vjs-peertube-displayed' 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.p2p) return
if (data.source === 'web-video') subDivHttpText.textContent = 'HTTP' if (data.source === 'web-video') subDivHttpText.textContent = 'HTTP'

View file

@ -1,16 +1,16 @@
import videojs from 'video.js'
import { buildVideoLink, decorateVideoLink } from '@peertube/peertube-core-utils' 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 { class PeerTubeLinkButton extends Component {
declare private mouseEnterHandler: () => void declare private mouseEnterHandler: () => void
declare private clickHandler: () => 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) super(player, options)
this.updateShowing() this.updateShowing()

View file

@ -1,15 +1,15 @@
import videojs from 'video.js' 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 { class PeerTubeLiveDisplay extends ClickableComponent {
declare private interval: any declare private interval: any
declare private contentEl_: any declare private contentEl_: HTMLElement
constructor (player: videojs.Player, options?: PeerTubeLinkButtonOptions) { constructor (player: VideojsPlayer, options?: VideojsClickableComponentOptions & PeerTubeLinkButtonOptions) {
super(player, options as any) super(player, options)
this.interval = this.setInterval(() => this.updateClass(), 1000) this.interval = this.setInterval(() => this.updateClass(), 1000)
@ -36,7 +36,7 @@ class PeerTubeLiveDisplay extends ClickableComponent {
className: 'vjs-live-display' className: 'vjs-live-display'
}, { }, {
'aria-live': 'off' 'aria-live': 'off'
}) }) as HTMLElement
this.contentEl_.appendChild(videojs.dom.createEl('span', { this.contentEl_.appendChild(videojs.dom.createEl('span', {
className: 'vjs-control-text', className: 'vjs-control-text',

View file

@ -1,12 +1,12 @@
import videojs from 'video.js' 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 { 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) super(player, options)
const updateMarker = () => { const updateMarker = () => {

View file

@ -1,10 +1,11 @@
import videojs from 'video.js' 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 // Big thanks to this beautiful plugin: https://github.com/phloxic/videojs-sprite-thumbnails
// Adapted to respect peertube player style // Adapted to respect peertube player style
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class StoryboardPlugin extends Plugin { class StoryboardPlugin extends Plugin {
declare private url: string declare private url: string
@ -14,9 +15,9 @@ class StoryboardPlugin extends Plugin {
declare private cached: boolean declare private cached: boolean
declare private mouseTimeTooltip: videojs.MouseTimeDisplay declare private mouseTimeTooltip: VideojsPlayer['controlBar']['progressControl']['seekBar']['mouseTimeDisplay']['timeTooltip']
declare private seekBar: { el(): HTMLElement, mouseTimeDisplay: any, playProgressBar: any } declare private seekBar: VideojsPlayer['controlBar']['progressControl']['seekBar']
declare private progress: any declare private progress: VideojsPlayer['controlBar']['progressControl']
declare private spritePlaceholder: HTMLElement declare private spritePlaceholder: HTMLElement
@ -26,8 +27,8 @@ class StoryboardPlugin extends Plugin {
declare private onReadyOrLoadstartHandler: (event: { type: 'ready' }) => void declare private onReadyOrLoadstartHandler: (event: { type: 'ready' }) => void
constructor (player: videojs.Player, options: videojs.ComponentOptions & StoryboardOptions) { constructor (player: VideojsPlayer, options: StoryboardOptions) {
super(player, options) super(player)
this.url = options.url this.url = options.url
this.height = options.height this.height = options.height
@ -46,7 +47,7 @@ class StoryboardPlugin extends Plugin {
} }
init () { init () {
const controls = this.player.controlBar as any const controls = this.player.controlBar
// default control bar component tree is expected // default control bar component tree is expected
// https://docs.videojs.com/tutorial-components.html#default-component-tree // https://docs.videojs.com/tutorial-components.html#default-component-tree
@ -60,7 +61,8 @@ class StoryboardPlugin extends Plugin {
this.onReadyOrLoadstartHandler = event => { this.onReadyOrLoadstartHandler = event => {
if (event.type !== 'ready') { 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') return Object.prototype.hasOwnProperty.call(source, 'storyboard')
}) as any }) as any
const spriteOpts = spriteSource?.['storyboard'] as StoryboardOptions const spriteOpts = spriteSource?.['storyboard'] as StoryboardOptions
@ -97,7 +99,7 @@ class StoryboardPlugin extends Plugin {
if (!this.cached) { if (!this.cached) {
this.sprites[this.url] = videojs.dom.createEl('img', { this.sprites[this.url] = videojs.dom.createEl('img', {
src: this.url src: this.url
}) }) as HTMLImageElement
} }
this.progress.on(spriteEvents, this.boundedHijackMouseTooltip) this.progress.on(spriteEvents, this.boundedHijackMouseTooltip)
} else { } else {

View file

@ -1,15 +1,15 @@
import videojs from 'video.js' import videojs from 'video.js'
import { getStoredTheater, saveTheaterInStore } from '../../peertube-player-local-storage' 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 { class TheaterButton extends Button {
private static readonly THEATER_MODE_CLASS = 'vjs-theater-enabled' private static readonly THEATER_MODE_CLASS = 'vjs-theater-enabled'
declare private theaterButtonOptions: TheaterButtonOptions declare private theaterButtonOptions: TheaterButtonOptions
constructor (player: videojs.Player, options: TheaterButtonOptions & videojs.ComponentOptions) { constructor (player: VideojsPlayer, options: TheaterButtonOptions & VideojsButtonOptions) {
super(player, options) super(player, options)
this.theaterButtonOptions = options this.theaterButtonOptions = options

View file

@ -1,13 +1,14 @@
import { timeToInt } from '@peertube/peertube-core-utils' 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 { class TimeTooltip extends TimeToolTip {
declare private currentTimecode: string declare private currentTimecode: string
write (timecode: string) { write (timecode: string) {
const player: VideoJsPlayer = this.player() const player = this.player()
if (player.usingPlugin('chapters')) { if (player.usingPlugin('chapters')) {
if (timecode === this.currentTimecode) return if (timecode === this.currentTimecode) return

View file

@ -1,6 +1,7 @@
import videojs from 'video.js' 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 = { export type PeerTubeDockComponentOptions = {
title?: string title?: string
@ -9,10 +10,10 @@ export type PeerTubeDockComponentOptions = {
} }
class PeerTubeDockComponent extends Component { class PeerTubeDockComponent extends Component {
declare options_: videojs.ComponentOptions & PeerTubeDockComponentOptions declare options_: VideojsComponentOptions & PeerTubeDockComponentOptions
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // 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) super(player, options)
} }

View file

@ -1,7 +1,8 @@
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsPlayer, VideojsPlugin } from '../../types'
import { PeerTubeDockComponent } from './peertube-dock-component' import { PeerTubeDockComponent } from './peertube-dock-component'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
export type PeerTubeDockPluginOptions = { export type PeerTubeDockPluginOptions = {
title?: string title?: string
@ -12,8 +13,8 @@ export type PeerTubeDockPluginOptions = {
class PeerTubeDockPlugin extends Plugin { class PeerTubeDockPlugin extends Plugin {
declare private dockComponent: PeerTubeDockComponent declare private dockComponent: PeerTubeDockComponent
constructor (player: videojs.Player, options: videojs.PlayerOptions & PeerTubeDockPluginOptions) { constructor (player: VideojsPlayer, options: PeerTubeDockPluginOptions) {
super(player, options) super(player)
player.ready(() => { player.ready(() => {
player.addClass('peertube-dock') player.addClass('peertube-dock')

View file

@ -1,8 +1,9 @@
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsPlayer, VideojsPlugin } from '../../types'
type KeyHandler = { accept: (event: KeyboardEvent) => boolean, cb: (e: KeyboardEvent) => void } 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 = { export type HotkeysOptions = {
isLive: boolean isLive: boolean
@ -18,8 +19,8 @@ class PeerTubeHotkeysPlugin extends Plugin {
declare private readonly isLive: boolean declare private readonly isLive: boolean
constructor (player: videojs.Player, options: videojs.PlayerOptions & HotkeysOptions) { constructor (player: VideojsPlayer, options: HotkeysOptions) {
super(player, options) super(player)
this.isLive = options.isLive this.isLive = options.isLive

View file

@ -1,12 +1,12 @@
import debug from 'debug'
import videojs from 'video.js'
import { PlaybackMetricCreate, VideoResolutionType } from '@peertube/peertube-models' import { PlaybackMetricCreate, VideoResolutionType } from '@peertube/peertube-models'
import { logger } from '@root-helpers/logger' 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 debugLogger = debug('peertube:player:metrics')
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class MetricsPlugin extends Plugin { class MetricsPlugin extends Plugin {
declare options_: MetricsPluginOptions declare options_: MetricsPluginOptions
@ -27,7 +27,7 @@ class MetricsPlugin extends Plugin {
declare private metricsInterval: any declare private metricsInterval: any
constructor (player: videojs.Player, options: MetricsPluginOptions) { constructor (player: VideojsPlayer, options: MetricsPluginOptions) {
super(player) super(player)
this.options_ = options this.options_ = options
@ -154,7 +154,7 @@ class MetricsPlugin extends Plugin {
} }
private trackBytes () { 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.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) this.downloadedBytesP2P += Math.max(Math.round((data.p2p?.downloaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.downloaded || 0)), 0)

View file

@ -1,6 +1,8 @@
import videojs from 'video.js' 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 { class PeerTubeMobileButtons extends Component {
declare private mainButton: HTMLDivElement declare private mainButton: HTMLDivElement

View file

@ -1,11 +1,14 @@
import { logger } from '@root-helpers/logger'
import debug from 'debug' import debug from 'debug'
import videojs from 'video.js' import videojs from 'video.js'
import { logger } from '@root-helpers/logger' import { VideojsPlayer, VideojsPlugin } from '../../types'
import { PeerTubeMobileButtons } from './peertube-mobile-buttons' import { PeerTubeMobileButtons } from './peertube-mobile-buttons'
const debugLogger = debug('peertube:player:mobile') 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 { class PeerTubeMobilePlugin extends Plugin {
private static readonly DOUBLE_TAP_DELAY_MS = 250 private static readonly DOUBLE_TAP_DELAY_MS = 250
@ -29,10 +32,10 @@ class PeerTubeMobilePlugin extends Plugin {
declare private sliderActiveHandler: () => void declare private sliderActiveHandler: () => void
declare private sliderInactiveHandler: () => void declare private sliderInactiveHandler: () => void
declare private seekBar: videojs.Component declare private seekBar: SeekBar
constructor (player: videojs.Player, options: videojs.PlayerOptions) { constructor (player: VideojsPlayer) {
super(player, options) super(player)
this.seekAmount = 0 this.seekAmount = 0
@ -45,16 +48,14 @@ class PeerTubeMobilePlugin extends Plugin {
this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons this.peerTubeMobileButtons = player.addChild('PeerTubeMobileButtons', { reportTouchActivity: false }) as PeerTubeMobileButtons
if (!this.player.options_.userActions) this.player.options_.userActions = {}; if (!this.player.options_.userActions) this.player.options_.userActions = {}
this.player.options_.userActions.click = false
// FIXME: typings
(this.player.options_.userActions as any).click = false
this.player.options_.userActions.doubleClick = false this.player.options_.userActions.doubleClick = false
this.onPlayHandler = () => this.initTouchStartEvents() this.onPlayHandler = () => this.initTouchStartEvents()
this.player.one('play', this.onPlayHandler) 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.sliderActiveHandler = () => this.player.addClass('vjs-mobile-sliding')
this.sliderInactiveHandler = () => this.player.removeClass('vjs-mobile-sliding') this.sliderInactiveHandler = () => this.player.removeClass('vjs-mobile-sliding')
@ -76,10 +77,9 @@ class PeerTubeMobilePlugin extends Plugin {
private handleFullscreenRotation () { private handleFullscreenRotation () {
this.onFullScreenChangeHandler = () => { this.onFullScreenChangeHandler = () => {
if (!this.player.isFullscreen() || this.isPortraitVideo()) return;
// https://github.com/microsoft/TypeScript-DOM-lib-generator/issues/1615 // 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)) .catch((err: Error) => logger.error('Cannot lock screen to landscape.', err))
} }
@ -151,7 +151,7 @@ class PeerTubeMobilePlugin extends Plugin {
private onDoubleTap (event: TouchEvent) { private onDoubleTap (event: TouchEvent) {
const playerWidth = this.player.currentWidth() 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 const offsetX = event.targetTouches[0].pageX - rect.left
debugLogger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX) debugLogger('Calculating double tap zone (player width: %d, offset X: %d)', playerWidth, offsetX)

View file

@ -1,14 +1,15 @@
import { NSFWFlag } from '@peertube/peertube-models' import { NSFWFlag } from '@peertube/peertube-models'
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types'
import { type PeerTubeNSFWPluginOptions } from './peertube-nsfw-plugin' import { type PeerTubeNSFWPluginOptions } from './peertube-nsfw-plugin'
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
class PeerTubeNSFWDetailsComponent extends Component { class PeerTubeNSFWDetailsComponent extends Component {
declare options_: videojs.ComponentOptions & PeerTubeNSFWPluginOptions declare options_: VideojsComponentOptions & PeerTubeNSFWPluginOptions
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // 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) super(player, options)
} }

View file

@ -1,13 +1,14 @@
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types'
import { type PeerTubeNSFWPluginOptions } from './peertube-nsfw-plugin' import { type PeerTubeNSFWPluginOptions } from './peertube-nsfw-plugin'
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
class PeerTubeNSFWInfoComponent extends Component { class PeerTubeNSFWInfoComponent extends Component {
declare options_: videojs.ComponentOptions & PeerTubeNSFWPluginOptions declare options_: VideojsComponentOptions & PeerTubeNSFWPluginOptions
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // 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) super(player, options)
} }

View file

@ -1,8 +1,9 @@
import videojs from 'video.js' 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 { 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 = { export type PeerTubeNSFWPluginOptions = {
summary: string summary: string
@ -13,8 +14,8 @@ class PeerTubeNSFWPlugin extends Plugin {
declare private nsfwInfoComponent: PeerTubeNSFWInfoComponent declare private nsfwInfoComponent: PeerTubeNSFWInfoComponent
declare private nsfwDetailsComponent: PeerTubeNSFWDetailsComponent declare private nsfwDetailsComponent: PeerTubeNSFWDetailsComponent
constructor (player: videojs.Player, options: videojs.PlayerOptions & PeerTubeNSFWPluginOptions) { constructor (player: VideojsPlayer, options: PeerTubeNSFWPluginOptions) {
super(player, options) super(player)
player.ready(() => { player.ready(() => {
player.addClass('peertube-nsfw') player.addClass('peertube-nsfw')

View file

@ -1,12 +1,13 @@
// Thanks https://github.com/streamroot/videojs-hlsjs-plugin // 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 // 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 { logger } from '@root-helpers/logger'
import Hlsjs, { ErrorData, Level, LevelSwitchingData, ManifestParsedData } from 'hls.js' 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 { 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) const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hlsjs)
@ -37,7 +38,7 @@ const registerSourceHandler = function (vjs: typeof videojs) {
alreadyRegistered = true // FIXME: typings alreadyRegistered = true // FIXME: typings
;(html5 as any).registerSourceHandler({ ;(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 hlsTypeRE = /^application\/x-mpegURL|application\/vnd\.apple\.mpegurl$/i
const hlsExtRE = /\.m3u8/i const hlsExtRE = /\.m3u8/i
@ -47,7 +48,7 @@ const registerSourceHandler = function (vjs: typeof videojs) {
return '' return ''
}, },
handleSource: function (source: videojs.Tech.SourceObject, tech: VideoJSTechHLS) { handleSource: function (source: SourceObject, tech: VideoJSTechHLS) {
if (tech.hlsProvider) { if (tech.hlsProvider) {
tech.hlsProvider.dispose() tech.hlsProvider.dispose()
} }
@ -64,11 +65,11 @@ const registerSourceHandler = function (vjs: typeof videojs) {
// HLS options plugin // HLS options plugin
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class HLSJSConfigHandler extends Plugin { class HLSJSConfigHandler extends Plugin {
constructor (player: videojs.Player, options: HlsjsConfigHandlerOptions) { constructor (player: VideojsPlayer, options: HlsjsConfigHandlerOptions) {
super(player, options) super(player)
if (!options) return if (!options) return
@ -109,9 +110,9 @@ videojs.registerPlugin('hlsjs', HLSJSConfigHandler)
export class Html5Hlsjs { export class Html5Hlsjs {
private readonly videoElement: HTMLVideoElement private readonly videoElement: HTMLVideoElement
private readonly errorCounts: ErrorCounts = {} private readonly errorCounts: ErrorCounts = {}
private readonly player: videojs.Player private readonly player: VideojsPlayer
private readonly tech: videojs.Tech private readonly tech: Tech
private readonly source: videojs.Tech.SourceObject private readonly source: SourceObject
private readonly vjs: typeof videojs private readonly vjs: typeof videojs
private maxNetworkErrorRecovery = 5 private maxNetworkErrorRecovery = 5
@ -134,7 +135,7 @@ export class Html5Hlsjs {
private audioMode = false 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.vjs = vjs
this.source = source this.source = source
@ -142,7 +143,7 @@ export class Html5Hlsjs {
;(this.tech as any).name_ = 'Hlsjs' ;(this.tech as any).name_ = 'Hlsjs'
this.videoElement = tech.el() as HTMLVideoElement 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 => { this.handlers.error = event => {
let errorTxt: string let errorTxt: string

View file

@ -5,12 +5,13 @@ import { FragLoadedData, default as Hlsjs } from 'hls.js'
import type { DownloadSource, SegmentErrorDetails, SegmentLoadDetails } from 'p2p-media-loader-core' import type { DownloadSource, SegmentErrorDetails, SegmentLoadDetails } from 'p2p-media-loader-core'
import type { HlsWithP2PInstance } from 'p2p-media-loader-hlsjs' import type { HlsWithP2PInstance } from 'p2p-media-loader-hlsjs'
import videojs from 'video.js' import videojs from 'video.js'
import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo } from '../../types' import { P2PMediaLoaderPluginOptions, PlayerNetworkInfo, VideojsPlayer, VideojsPlugin } from '../../types'
import { SettingsButton } from '../settings/settings-menu-button' import { SettingsButton } from '../settings/settings-menu-button'
const debugLogger = debug('peertube:player:p2p-media-loader') 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 { class P2pMediaLoaderPlugin extends Plugin {
declare private readonly options: P2PMediaLoaderPluginOptions declare private readonly options: P2PMediaLoaderPluginOptions
@ -35,7 +36,7 @@ class P2pMediaLoaderPlugin extends Plugin {
declare private connectedPeers: Set<string> declare private connectedPeers: Set<string>
declare private totalHTTPPeers: number declare private totalHTTPPeers: number
constructor (player: videojs.Player, options?: P2PMediaLoaderPluginOptions) { constructor (player: VideojsPlayer, options?: P2PMediaLoaderPluginOptions) {
super(player) super(player)
this.options = options this.options = options

View file

@ -5,6 +5,8 @@ import { isIOS, isMobile, isSafari } from '@root-helpers/web-browser'
import debug from 'debug' import debug from 'debug'
import { UAParser } from 'ua-parser-js' import { UAParser } from 'ua-parser-js'
import videojs from 'video.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 { import {
getPlayerSessionId, getPlayerSessionId,
getStoredLastSubtitle, getStoredLastSubtitle,
@ -16,19 +18,26 @@ import {
saveVideoWatchHistory, saveVideoWatchHistory,
saveVolumeInStore saveVolumeInStore
} from '../../peertube-player-local-storage' } 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' import { SettingsButton } from '../settings/settings-menu-button'
const debugLogger = debug('peertube:player:peertube') const debugLogger = debug('peertube:player:peertube')
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class PeerTubePlugin extends Plugin { class PeerTubePlugin extends Plugin {
declare private readonly videoViewUrl: () => string declare private readonly videoViewUrl: () => string
declare private readonly authorizationHeader: () => string declare private readonly authorizationHeader: () => string
declare private readonly initialInactivityTimeout: number declare private readonly initialInactivityTimeout: number
declare private readonly hasAutoplay: () => videojs.Autoplay declare private readonly hasAutoplay: () => VideojsAutoplay
declare private currentSubtitle: string declare private currentSubtitle: string
declare private currentPlaybackRate: number declare private currentPlaybackRate: number
@ -39,7 +48,7 @@ class PeerTubePlugin extends Plugin {
declare private mouseInControlBar: boolean declare private mouseInControlBar: boolean
declare private mouseInSettings: boolean declare private mouseInSettings: boolean
declare private errorModal: videojs.ModalDialog declare private errorModal: ModalDialog
declare private hasInitialSeek: boolean declare private hasInitialSeek: boolean
@ -56,7 +65,7 @@ class PeerTubePlugin extends Plugin {
os: string os: string
} }
constructor (player: videojs.Player, private readonly options: PeerTubePluginOptions) { constructor (player: VideojsPlayer, private readonly options: PeerTubePluginOptions) {
super(player) super(player)
this.setUserAgentInfo() this.setUserAgentInfo()
@ -99,7 +108,7 @@ class PeerTubePlugin extends Plugin {
}) })
this.player.one('canplay', () => { this.player.one('canplay', () => {
const playerOptions = this.player.options_ const playerOptions = this.player.options_ as VideojsPlayerOptions
const volume = getStoredVolume() const volume = getStoredVolume()
if (volume !== undefined) this.player.volume(volume) 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-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 if (this.options.videoRatio()) return
this.adaptPlayerFromRatio({ ratio: data.ratio, defaultRatio }) this.adaptPlayerFromRatio({ ratio: data.ratio, defaultRatio })
@ -497,7 +506,7 @@ class PeerTubePlugin extends Plugin {
private listenControlBarMouse () { private listenControlBarMouse () {
const controlBar = this.player.controlBar const controlBar = this.player.controlBar
const settingsButton: SettingsButton = (controlBar as any).settingsButton const settingsButton = controlBar.settingsButton
controlBar.on('mouseenter', () => { controlBar.on('mouseenter', () => {
this.mouseInControlBar = true this.mouseInControlBar = true
@ -635,24 +644,6 @@ class PeerTubePlugin extends Plugin {
// Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657 // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
private initSmoothProgressBar () { 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 () { private patchMenuEscapeKey () {
@ -688,7 +679,7 @@ class PeerTubePlugin extends Plugin {
private getCaptionsButton () { private getCaptionsButton () {
const settingsButton = this.player.controlBar.getDescendant([ 'settingsButton' ]) as SettingsButton 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 () { private getPlaybackRateButton () {

View file

@ -7,8 +7,8 @@ import {
} from '../../types' } from '../../types'
type ControlBarOptionsBuilderConstructorOptions = type ControlBarOptionsBuilderConstructorOptions =
Pick<PeerTubePlayerConstructorOptions, 'peertubeLink' | 'instanceName' | 'theaterButton'> & & Pick<PeerTubePlayerConstructorOptions, 'peertubeLink' | 'instanceName' | 'theaterButton'>
{ & {
videoShortUUID: () => string videoShortUUID: () => string
p2pEnabled: () => boolean p2pEnabled: () => boolean
@ -17,7 +17,6 @@ type ControlBarOptionsBuilderConstructorOptions =
} }
export class ControlBarOptionsBuilder { export class ControlBarOptionsBuilder {
constructor (private options: ControlBarOptionsBuilderConstructorOptions) { constructor (private options: ControlBarOptionsBuilderConstructorOptions) {
} }
@ -84,11 +83,7 @@ export class ControlBarOptionsBuilder {
progressControl: { progressControl: {
children: { children: {
seekBar: { seekBar: {
children: { children: [ 'loadProgressBar', 'mouseTimeDisplay', 'playProgressBar' ]
loadProgressBar: {},
mouseTimeDisplay: {},
playProgressBar: {}
}
} }
} }
} }

View file

@ -7,7 +7,13 @@ import debug from 'debug'
import { Level } from 'hls.js' import { Level } from 'hls.js'
import type { CoreConfig, StreamConfig } from 'p2p-media-loader-core' import type { CoreConfig, StreamConfig } from 'p2p-media-loader-core'
import { getAverageBandwidthInStore } from '../../peertube-player-local-storage' 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 { getRtcConfig } from '../common'
import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager' import { RedundancyUrlManager } from '../p2p-media-loader/redundancy-url-manager'
import { SegmentValidator } from '../p2p-media-loader/segment-validator' import { SegmentValidator } from '../p2p-media-loader/segment-validator'
@ -67,7 +73,7 @@ export class HLSOptionsBuilder {
const hlsjs = { const hlsjs = {
hlsjsConfig: this.getHLSJSOptions(p2pMediaLoaderConfig), 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 resolution = Math.min(level.height || 0, level.width || 0)
const file = this.options.hls.videoFiles.find(f => f.resolution.id === resolution) const file = this.options.hls.videoFiles.find(f => f.resolution.id === resolution)

View file

@ -1,20 +1,20 @@
import videojs from 'video.js' import videojs from 'video.js'
import { PlaylistPluginOptions } from '../../types' import { PlaylistPluginOptions, VideojsClickableComponent, VideojsClickableComponentOptions, VideojsPlayer } from '../../types'
import { PlaylistMenu } from './playlist-menu' import { PlaylistMenu } from './playlist-menu'
const ClickableComponent = videojs.getComponent('ClickableComponent') const ClickableComponent = videojs.getComponent('ClickableComponent') as typeof VideojsClickableComponent
class PlaylistButton extends ClickableComponent { class PlaylistButton extends ClickableComponent {
declare private playlistInfoElement: HTMLElement declare private playlistInfoElement: HTMLElement
declare private wrapper: 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 // FIXME: eslint -> it's not a useless constructor, we need to extend constructor options typings
// eslint-disable-next-line @typescript-eslint/no-useless-constructor // eslint-disable-next-line @typescript-eslint/no-useless-constructor
constructor ( constructor (
player: videojs.Player, player: VideojsPlayer,
options?: PlaylistPluginOptions & { playlistMenu: PlaylistMenu } & videojs.ClickableComponentOptions options?: PlaylistPluginOptions & { playlistMenu: PlaylistMenu } & VideojsClickableComponentOptions
) { ) {
super(player, options) super(player, options)
} }

View file

@ -1,9 +1,9 @@
import videojs from 'video.js'
import { secondsToTime } from '@peertube/peertube-core-utils' import { secondsToTime } from '@peertube/peertube-core-utils'
import { VideoPlaylistElement } from '@peertube/peertube-models' 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 { class PlaylistMenuItem extends Component {
declare private element: VideoPlaylistElement declare private element: VideoPlaylistElement
@ -11,10 +11,10 @@ class PlaylistMenuItem extends Component {
declare private clickHandler: () => void declare private clickHandler: () => void
declare private keyDownHandler: (event: KeyboardEvent) => void declare private keyDownHandler: (event: KeyboardEvent) => void
declare options_: videojs.ComponentOptions & PlaylistItemOptions declare options_: VideojsComponentOptions & PlaylistItemOptions
constructor (player: videojs.Player, options?: PlaylistItemOptions) { constructor (player: VideojsPlayer, options?: VideojsComponentOptions & PlaylistItemOptions) {
super(player, options as any) super(player, options)
this.emitTapEvents() this.emitTapEvents()
@ -130,7 +130,8 @@ class PlaylistMenuItem extends Component {
li.appendChild(block) 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') { if (event.code === 'Space' || event.code === 'Enter') {
this.switchPlaylistItem() this.switchPlaylistItem()
} }
@ -141,6 +142,6 @@ class PlaylistMenuItem extends Component {
} }
} }
Component.registerComponent('PlaylistMenuItem', PlaylistMenuItem) videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem)
export { PlaylistMenuItem } export { PlaylistMenuItem }

View file

@ -1,9 +1,9 @@
import videojs from 'video.js'
import { VideoPlaylistElement } from '@peertube/peertube-models' 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' import { PlaylistMenuItem } from './playlist-menu-item'
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
class PlaylistMenu extends Component { class PlaylistMenu extends Component {
declare private menuItems: PlaylistMenuItem[] declare private menuItems: PlaylistMenuItem[]
@ -14,9 +14,9 @@ class PlaylistMenu extends Component {
declare private readonly onPlayerCick: (event: Event) => void 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) super(player, options)
this.menuItems = [] this.menuItems = []
@ -152,6 +152,6 @@ class PlaylistMenu extends Component {
} }
} }
Component.registerComponent('PlaylistMenu', PlaylistMenu) videojs.registerComponent('PlaylistMenu', PlaylistMenu)
export { PlaylistMenu } export { PlaylistMenu }

View file

@ -1,16 +1,16 @@
import videojs from 'video.js' import videojs from 'video.js'
import { PlaylistPluginOptions } from '../../types' import { PlaylistPluginOptions, VideojsPlayer, VideojsPlugin } from '../../types'
import { PlaylistButton } from './playlist-button' import { PlaylistButton } from './playlist-button'
import { PlaylistMenu } from './playlist-menu' import { PlaylistMenu } from './playlist-menu'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class PlaylistPlugin extends Plugin { class PlaylistPlugin extends Plugin {
declare private playlistMenu: PlaylistMenu declare private playlistMenu: PlaylistMenu
declare private playlistButton: PlaylistButton declare private playlistButton: PlaylistButton
constructor (player: videojs.Player, options?: PlaylistPluginOptions) { constructor (player: VideojsPlayer, options?: PlaylistPluginOptions) {
super(player, options) super(player)
this.player.addClass('vjs-playlist') this.player.addClass('vjs-playlist')

View file

@ -1,7 +1,7 @@
import videojs from 'video.js' 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 { class PeerTubeResolutionsPlugin extends Plugin {
declare private currentSelection: PeerTubeResolution declare private currentSelection: PeerTubeResolution
@ -9,7 +9,7 @@ class PeerTubeResolutionsPlugin extends Plugin {
declare private autoResolutionChosenId: number declare private autoResolutionChosenId: number
constructor (player: videojs.Player) { constructor (player: VideojsPlayer) {
super(player) super(player)
this.resolutions = [] this.resolutions = []
@ -85,7 +85,6 @@ class PeerTubeResolutionsPlugin extends Plugin {
return 1 return 1
}) })
} }
} }
videojs.registerPlugin('peertubeResolutions', PeerTubeResolutionsPlugin) videojs.registerPlugin('peertubeResolutions', PeerTubeResolutionsPlugin)

View file

@ -1,12 +1,12 @@
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsMenu } from '../../types'
const Menu = videojs.getComponent('Menu') const Menu = videojs.getComponent('Menu') as typeof VideojsMenu
const Component = videojs.getComponent('Component')
// Default menu doesn't check if the child is disabled/hidden // Default menu doesn't check if the child is disabled/hidden
class MenuFocusFixed extends Menu { class MenuFocusFixed extends Menu {
declare private focusedChild_: number declare focusedChild_: number
stepForward () { stepForward () {
let stepChild = 0 let stepChild = 0
@ -72,5 +72,5 @@ class MenuFocusFixed extends Menu {
} }
} }
Component.registerComponent('MenuFocusFixed', MenuFocusFixed) videojs.registerComponent('MenuFocusFixed', MenuFocusFixed)
export { MenuFocusFixed } export { MenuFocusFixed }

View file

@ -1,12 +1,14 @@
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsMenu, VideojsMenuButton, VideojsMenuButtonOptions, VideojsPlayer } from '../../types'
import { ResolutionMenuItem } from './resolution-menu-item' import { ResolutionMenuItem } from './resolution-menu-item'
const Menu = videojs.getComponent('Menu') const Menu = videojs.getComponent('Menu') as typeof VideojsMenu
const MenuButton = videojs.getComponent('MenuButton') const MenuButton = videojs.getComponent('MenuButton') as typeof VideojsMenuButton
class ResolutionMenuButton extends MenuButton { class ResolutionMenuButton extends MenuButton {
declare labelEl_: HTMLElement declare labelEl_: HTMLElement
constructor (player: videojs.Player, options?: videojs.MenuButtonOptions) { constructor (player: VideojsPlayer, options?: VideojsMenuButtonOptions) {
super(player, options) super(player, options)
this.controlText('Quality') this.controlText('Quality')
@ -37,7 +39,7 @@ class ResolutionMenuButton extends MenuButton {
} }
createMenu () { 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() const resolutions = this.player().peertubeResolutions().getResolutions()
for (const r of resolutions) { for (const r of resolutions) {

View file

@ -1,9 +1,10 @@
import videojs from 'video.js' 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 { export interface ResolutionMenuItemOptions extends VideojsMenuItemOptions {
resolutionId: number resolutionId?: number
} }
class ResolutionMenuItem extends MenuItem { class ResolutionMenuItem extends MenuItem {
@ -14,7 +15,7 @@ class ResolutionMenuItem extends MenuItem {
declare private updateSelectionHandler: () => void declare private updateSelectionHandler: () => void
constructor (player: videojs.Player, options?: ResolutionMenuItemOptions) { constructor (player: VideojsPlayer, options?: ResolutionMenuItemOptions) {
super(player, { ...options, selectable: true }) super(player, { ...options, selectable: true })
this.autoResolutionChosen = '' this.autoResolutionChosen = ''

View file

@ -1,9 +1,10 @@
import videojs from 'video.js' 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 { class SettingsDialog extends Component {
constructor (player: videojs.Player) { constructor (player: VideojsPlayer) {
super(player) super(player)
this.hide() this.hide()
@ -41,6 +42,6 @@ class SettingsDialog extends Component {
} }
} }
Component.registerComponent('SettingsDialog', SettingsDialog) videojs.registerComponent('SettingsDialog', SettingsDialog)
export { SettingsDialog } export { SettingsDialog }

View file

@ -1,5 +1,6 @@
import debug from 'debug' import debug from 'debug'
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsButton, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types'
import { toTitleCase } from '../common' import { toTitleCase } from '../common'
import { MenuFocusFixed } from './menu-focus-fixed' import { MenuFocusFixed } from './menu-focus-fixed'
import { SettingsDialog } from './settings-dialog' import { SettingsDialog } from './settings-dialog'
@ -9,10 +10,10 @@ import { SettingsPanelChild } from './settings-panel-child'
const debugLogger = debug('peertube:player:settings') const debugLogger = debug('peertube:player:settings')
const Button = videojs.getComponent('Button') const Button = videojs.getComponent('Button') as typeof VideojsButton
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
export interface SettingsButtonOptions extends videojs.ComponentOptions { export interface SettingsButtonOptions extends VideojsComponentOptions {
entries: any[] entries: any[]
setup?: { setup?: {
maxHeightOffset: number maxHeightOffset: number
@ -33,18 +34,18 @@ class SettingsButton extends Button {
declare private settingsButtonOptions: SettingsButtonOptions declare private settingsButtonOptions: SettingsButtonOptions
constructor (player: videojs.Player, options?: SettingsButtonOptions) { constructor (player: VideojsPlayer, options?: SettingsButtonOptions) {
super(player, options) super(player, options)
this.settingsButtonOptions = options this.settingsButtonOptions = options
this.controlText('Settings') 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.dialogEl = this.dialog.el() as HTMLElement
this.menu = null this.menu = null
this.panel = this.dialog.addChild('settingsPanel') this.panel = this.dialog.addChild('settingsPanel') as SettingsPanel
this.panelChild = this.panel.addChild('settingsPanelChild') this.panelChild = this.panel.addChild('settingsPanelChild') as SettingsPanelChild
this.addClass('vjs-settings') this.addClass('vjs-settings')
this.setAttribute('aria-controls', 'vjs-settings-dialog-' + this.player().id()) this.setAttribute('aria-controls', 'vjs-settings-dialog-' + this.player().id())
@ -170,7 +171,7 @@ class SettingsButton extends Button {
this.resetChildren() this.resetChildren()
} }
getComponentSize (element: videojs.Component | HTMLElement) { getComponentSize (element: VideojsComponent | HTMLElement) {
let width: number = null let width: number = null
let height: number = null let height: number = null
@ -217,7 +218,7 @@ class SettingsButton extends Button {
this.focus() this.focus()
}) })
this.menu.on('arrow-right', (_, el) => { this.menu.on('arrow-right', (_: any, el: HTMLElement) => {
debugLogger('Detected arrow right on menu item', el) debugLogger('Detected arrow right on menu item', el)
el.click() el.click()
@ -257,7 +258,7 @@ class SettingsButton extends Button {
// Hide children to avoid sub menus stacking on top of each other // Hide children to avoid sub menus stacking on top of each other
// or having multiple menus open // 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 // Whether to add or remove selected class on the settings sub menu element
settingsMenuItem.on('click', openSubMenu) settingsMenuItem.on('click', openSubMenu)
@ -288,6 +289,6 @@ class SettingsButton extends Button {
} }
} }
Component.registerComponent('SettingsButton', SettingsButton) videojs.registerComponent('SettingsButton', SettingsButton)
export { SettingsButton } export { SettingsButton }

View file

@ -1,37 +1,40 @@
import debug from 'debug' import debug from 'debug'
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsComponent, VideojsMenu, VideojsMenuItem, VideojsMenuItemOptions, VideojsPlayer } from '../../types'
import { toTitleCase } from '../common' import { toTitleCase } from '../common'
import { SettingsDialog } from './settings-dialog' import { SettingsDialog } from './settings-dialog'
import { SettingsButton } from './settings-menu-button' import { SettingsButton } from './settings-menu-button'
import { SettingsPanel } from './settings-panel' import { SettingsPanel } from './settings-panel'
import { SettingsPanelChild } from './settings-panel-child' 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 debugLogger = debug('peertube:player:settings')
const MenuItem = videojs.getComponent('MenuItem') const MenuItem = videojs.getComponent('MenuItem') as typeof VideojsMenuItem
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
interface MenuItemExtended extends videojs.MenuItem { interface MenuItemExtended extends VideojsMenuItem {
isSelected_: boolean isSelected_: boolean
getLabel?: () => string getLabel?: () => string
} }
export interface SettingsMenuItemOptions extends videojs.MenuItemOptions { export interface SettingsMenuItemOptions extends VideojsMenuItemOptions {
entry: string entry?: string
menuButton: SettingsButton menuButton?: SettingsButton
} }
class SettingsMenuItem extends MenuItem { class SettingsMenuItem extends MenuItem {
declare settingsButton: SettingsButton declare settingsButton: SettingsButton
declare dialog: SettingsDialog declare dialog: SettingsDialog
declare mainMenu: videojs.Menu declare mainMenu: VideojsMenu
declare panel: SettingsPanel declare panel: SettingsPanel
declare panelChild: SettingsPanelChild declare panelChild: SettingsPanelChild
declare panelChildEl: HTMLElement declare panelChildEl: HTMLElement
declare size: number[] declare size: number[]
declare menuToLoad: string declare menuToLoad: string
declare subMenu: SettingsButton declare subMenu: MenuButton
declare submenuClickHandler: typeof SettingsMenuItem.prototype.onSubmenuClick declare submenuClickHandler: typeof SettingsMenuItem.prototype.onSubmenuClick
declare transitionEndHandler: typeof SettingsMenuItem.prototype.onTransitionEnd declare transitionEndHandler: typeof SettingsMenuItem.prototype.onTransitionEnd
@ -40,14 +43,14 @@ class SettingsMenuItem extends MenuItem {
declare settingsSubMenuValueEl_: HTMLElement declare settingsSubMenuValueEl_: HTMLElement
declare settingsSubMenuEl_: HTMLElement declare settingsSubMenuEl_: HTMLElement
constructor (player: videojs.Player, options?: SettingsMenuItemOptions) { constructor (player: VideojsPlayer, options?: SettingsMenuItemOptions) {
super(player, options) super(player, options)
this.settingsButton = options.menuButton this.settingsButton = options.menuButton
this.dialog = this.settingsButton.dialog this.dialog = this.settingsButton.dialog
this.mainMenu = this.settingsButton.menu this.mainMenu = this.settingsButton.menu
this.panel = this.dialog.getChild('settingsPanel') this.panel = this.dialog.getChild('settingsPanel') as SettingsPanel
this.panelChild = this.panel.getChild('settingsPanelChild') this.panelChild = this.panel.getChild('settingsPanelChild') as SettingsPanelChild
this.panelChildEl = this.panelChild.el() as HTMLElement this.panelChildEl = this.panelChild.el() as HTMLElement
this.size = null this.size = null
@ -56,15 +59,15 @@ class SettingsMenuItem extends MenuItem {
this.menuToLoad = 'mainmenu' this.menuToLoad = 'mainmenu'
const subMenuName = toTitleCase(options.entry) const subMenuName = toTitleCase(options.entry)
const SubMenuComponent = videojs.getComponent(subMenuName) const SubMenuComponent = videojs.getComponent(subMenuName) as typeof MenuButton
console.log(options.entry)
if (!SubMenuComponent) { if (!SubMenuComponent) {
throw new Error(`Component ${subMenuName} does not exist`) throw new Error(`Component ${subMenuName} does not exist`)
} }
const newOptions = Object.assign({}, options, { entry: options.menuButton, menuButton: this }) this.subMenu = new SubMenuComponent(this.player(), { ...options })
this.subMenu = new SubMenuComponent(this.player(), newOptions) as SettingsButton
const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0] const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0]
this.settingsSubMenuEl_.className += ' ' + subMenuClass this.settingsSubMenuEl_.className += ' ' + subMenuClass
@ -174,7 +177,7 @@ class SettingsMenuItem extends MenuItem {
* *
* @method handleClick * @method handleClick
*/ */
handleClick (event: videojs.EventTarget.Event) { handleClick (event: Event) {
this.menuToLoad = 'submenu' this.menuToLoad = 'submenu'
// Remove open class to ensure only the open submenu gets this class // Remove open class to ensure only the open submenu gets this class
videojs.dom.removeClass(this.el(), 'open') videojs.dom.removeClass(this.el(), 'open')
@ -239,8 +242,7 @@ class SettingsMenuItem extends MenuItem {
mainMenuEl.style.opacity = '0' mainMenuEl.style.opacity = '0'
// back button will always take you to main menu, so set dialog sizes // back button will always take you to main menu, so set dialog sizes
const mainMenuAny = this.mainMenu this.settingsButton.setDialogSize([ this.mainMenu.width(), this.mainMenu.height() ])
this.settingsButton.setDialogSize([ mainMenuAny.width() as number, mainMenuAny.height() as number ])
// animation not triggered without timeout (some async stuff ?!?) // animation not triggered without timeout (some async stuff ?!?)
setTimeout(() => { setTimeout(() => {
@ -376,7 +378,6 @@ class SettingsMenuItem extends MenuItem {
} }
} }
;(SettingsMenuItem as any).prototype.contentElType = 'button'
videojs.registerComponent('SettingsMenuItem', SettingsMenuItem) videojs.registerComponent('SettingsMenuItem', SettingsMenuItem)
export { SettingsMenuItem } export { SettingsMenuItem }

View file

@ -1,9 +1,9 @@
import videojs from 'video.js' 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 { class SettingsPanelChild extends Component {
createEl () { createEl () {
return super.createEl('div', { return super.createEl('div', {
className: 'vjs-settings-panel-child', className: 'vjs-settings-panel-child',
@ -12,6 +12,6 @@ class SettingsPanelChild extends Component {
} }
} }
Component.registerComponent('SettingsPanelChild', SettingsPanelChild) videojs.registerComponent('SettingsPanelChild', SettingsPanelChild)
export { SettingsPanelChild } export { SettingsPanelChild }

View file

@ -1,9 +1,9 @@
import videojs from 'video.js' 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 { class SettingsPanel extends Component {
createEl () { createEl () {
return super.createEl('div', { return super.createEl('div', {
className: 'vjs-settings-panel', className: 'vjs-settings-panel',
@ -12,6 +12,6 @@ class SettingsPanel extends Component {
} }
} }
Component.registerComponent('SettingsPanel', SettingsPanel) videojs.registerComponent('SettingsPanel', SettingsPanel)
export { SettingsPanel } export { SettingsPanel }

View file

@ -1,10 +1,11 @@
import videojs from 'video.js'
import { logger } from '@root-helpers/logger'
import { secondsToTime } from '@peertube/peertube-core-utils' 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' import { bytes } from '../common'
interface StatsCardOptions extends videojs.ComponentOptions { interface StatsCardOptions extends VideojsComponentOptions {
videoUUID: string videoUUID: string
videoIsLive: boolean videoIsLive: boolean
mode: 'web-video' | 'p2p-media-loader' mode: 'web-video' | 'p2p-media-loader'
@ -28,7 +29,8 @@ interface InfoElement {
value: HTMLElement value: HTMLElement
} }
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
class StatsCard extends Component { class StatsCard extends Component {
declare options_: StatsCardOptions declare options_: StatsCardOptions
@ -65,7 +67,7 @@ class StatsCard extends Component {
declare private onNetworkInfoHandler: (_event: any, data: EventPlayerNetworkInfo) => void declare private onNetworkInfoHandler: (_event: any, data: EventPlayerNetworkInfo) => void
constructor (player: videojs.Player, options?: StatsCardOptions) { constructor (player: VideojsPlayer, options?: StatsCardOptions) {
super(player, options) super(player, options)
this.metadataStore = {} this.metadataStore = {}
@ -365,7 +367,7 @@ class StatsCard extends Component {
return { root, value } return { root, value }
} }
private timeRangesToString (r: videojs.TimeRange) { private timeRangesToString (r: TimeRange) {
let result = '' let result = ''
for (let i = 0; i < r.length; i++) { for (let i = 0; i < r.length; i++) {

View file

@ -1,12 +1,13 @@
import videojs from 'video.js' import videojs from 'video.js'
import { VideojsPlayer, VideojsPlugin } from '../../types'
import { StatsCard, StatsCardOptions } from './stats-card' import { StatsCard, StatsCardOptions } from './stats-card'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class StatsForNerdsPlugin extends Plugin { class StatsForNerdsPlugin extends Plugin {
declare private statsCard: StatsCard declare private statsCard: StatsCard
constructor (player: videojs.Player, options: StatsCardOptions) { constructor (player: VideojsPlayer, options: StatsCardOptions) {
super(player) super(player)
this.player.ready(() => { this.player.ready(() => {

View file

@ -1,5 +1,5 @@
import videojs from 'video.js' import videojs from 'video.js'
import { UpNextPluginOptions } from '../../types' import { UpNextPluginOptions, VideojsComponent, VideojsComponentOptions, VideojsPlayer } from '../../types'
function getMainTemplate (options: EndCardOptions) { function getMainTemplate (options: EndCardOptions) {
return ` return `
@ -24,13 +24,14 @@ function getMainTemplate (options: EndCardOptions) {
` `
} }
export interface EndCardOptions extends videojs.ComponentOptions, UpNextPluginOptions { export interface EndCardOptions extends VideojsComponentOptions, UpNextPluginOptions {
cancelText: string cancelText: string
headText: string headText: string
suspendedText: string suspendedText: string
} }
const Component = videojs.getComponent('Component') const Component = videojs.getComponent('Component') as typeof VideojsComponent
export class EndCard extends Component { export class EndCard extends Component {
declare options_: EndCardOptions declare options_: EndCardOptions
@ -53,7 +54,7 @@ export class EndCard extends Component {
declare private onEndedHandler: () => void declare private onEndedHandler: () => void
declare private onPlayingHandler: () => void declare private onPlayingHandler: () => void
constructor (player: videojs.Player, options: EndCardOptions) { constructor (player: VideojsPlayer, options: EndCardOptions) {
super(player, options) super(player, options)
this.dashOffsetTotal = 586 this.dashOffsetTotal = 586

View file

@ -1,12 +1,11 @@
import videojs from 'video.js' import videojs from 'video.js'
import { UpNextPluginOptions } from '../../types' import { UpNextPluginOptions, VideojsPlayer, VideojsPlugin } from '../../types'
import { EndCard, EndCardOptions } from './end-card' import { EndCard, EndCardOptions } from './end-card'
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class UpNextPlugin extends Plugin { class UpNextPlugin extends Plugin {
constructor (player: VideojsPlayer, options: UpNextPluginOptions) {
constructor (player: videojs.Player, options: UpNextPluginOptions) {
super(player) super(player)
const settings: EndCardOptions = { const settings: EndCardOptions = {

View file

@ -3,11 +3,11 @@ import { VideoFile } from '@peertube/peertube-models'
import { logger } from '@root-helpers/logger' import { logger } from '@root-helpers/logger'
import debug from 'debug' import debug from 'debug'
import videojs from 'video.js' 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 debugLogger = debug('peertube:player:web-video-plugin')
const Plugin = videojs.getPlugin('plugin') const Plugin = videojs.getPlugin('plugin') as typeof VideojsPlugin
class WebVideoPlugin extends Plugin { class WebVideoPlugin extends Plugin {
declare private readonly videoFiles: VideoFile[] declare private readonly videoFiles: VideoFile[]
@ -21,8 +21,8 @@ class WebVideoPlugin extends Plugin {
declare private onPlayHandler: () => void declare private onPlayHandler: () => void
declare private onLoadedMetadata: () => void declare private onLoadedMetadata: () => void
constructor (player: videojs.Player, options?: WebVideoPluginOptions) { constructor (player: VideojsPlayer, options?: WebVideoPluginOptions) {
super(player, options) super(player)
this.videoFiles = options.videoFiles this.videoFiles = options.videoFiles
this.videoFileToken = options.videoFileToken this.videoFileToken = options.videoFileToken
@ -109,7 +109,8 @@ class WebVideoPlugin extends Plugin {
.then(() => { .then(() => {
if (paused) this.player.pause() if (paused) this.player.pause()
this.player.autoplay(oldAutoplayValue) // FIXME: typings
this.player.autoplay(oldAutoplayValue as any)
}) })
} }
} }

View file

@ -1,7 +1,7 @@
import { LiveVideoLatencyModeType, VideoChapter, VideoFile } from '@peertube/peertube-models' import { LiveVideoLatencyModeType, VideoChapter, VideoFile } from '@peertube/peertube-models'
import { PluginsManager } from '@root-helpers/plugins-manager' import { PluginsManager } from '@root-helpers/plugins-manager'
import { PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin' 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' export type PlayerMode = 'web-video' | 'p2p-media-loader'
@ -106,7 +106,7 @@ export type PeerTubePlayerLoadOptions = {
upnext?: { upnext?: {
isEnabled: () => boolean isEnabled: () => boolean
isSuspended: (player: videojs.VideoJsPlayer) => boolean isSuspended: (player: VideojsPlayer) => boolean
timeout: number timeout: number
} }

View file

@ -3,6 +3,22 @@ import type { HlsConfig, Level, Loader, LoaderContext } from 'hls.js'
import type { CoreConfig } from 'p2p-media-loader-core' import type { CoreConfig } from 'p2p-media-loader-core'
import type { HlsJsP2PEngine } from 'p2p-media-loader-hlsjs' import type { HlsJsP2PEngine } from 'p2p-media-loader-hlsjs'
import videojs from 'video.js' 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 { BezelsPlugin } from '../shared/bezels/bezels-plugin'
import { ContextMenuPlugin } from '../shared/context-menu' import { ContextMenuPlugin } from '../shared/context-menu'
import { ChaptersPlugin } from '../shared/control-bar/chapters-plugin' 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 { UpNextPlugin } from '../shared/upnext/upnext-plugin'
import { WebVideoPlugin } from '../shared/web-video/web-video-plugin' import { WebVideoPlugin } from '../shared/web-video/web-video-plugin'
import { PlayerMode } from './peertube-player-options' import { PlayerMode } from './peertube-player-options'
import { SettingsButton } from '../shared/settings/settings-menu-button'
declare module 'video.js' { declare module 'video.js' {
export interface VideoJsPlayer { export interface VideoJsPlayer {
srOptions_: HlsjsConfigHandlerOptions
theaterEnabled: boolean
// FIXME: add it to upstream typings // FIXME: add it to upstream typings
posterImage: {
show(): void
hide(): void
}
handleTechSeeked_(): void handleTechSeeked_(): void
textTracks(): TextTrackList & { textTracks(): TextTrackList & {
tracks_: (TextTrack & { id: string, label: string, src: string })[] 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 hlsProvider: Html5Hlsjs
} }
export interface HlsjsConfigHandlerOptions { export interface HlsjsConfigHandlerOptions {
hlsjsConfig?: HlsConfig hlsjsConfig?: HlsConfig
levelLabelHandler?: (level: Level, player: videojs.Player) => string levelLabelHandler?: (level: Level, player: VideojsPlayer) => string
} }
export type PeerTubeResolution = { export type PeerTubeResolution = {
@ -116,7 +95,7 @@ export type PeerTubePluginOptions = {
cssPlayerPortraitModeVariable: string cssPlayerPortraitModeVariable: string
} }
hasAutoplay: () => videojs.Autoplay hasAutoplay: () => VideojsAutoplay
videoViewUrl: () => string videoViewUrl: () => string
videoViewIntervalMs: number videoViewIntervalMs: number
@ -154,7 +133,7 @@ export type ContextMenuPluginOptions = {
} }
export type ContextMenuItemOptions = { export type ContextMenuItemOptions = {
listener: (e: videojs.EventTarget.Event) => void listener: (e?: Event) => void
label: string label: string
} }
@ -215,7 +194,7 @@ export type WebVideoPluginOptions = {
} }
export type HLSLoaderClass = { export type HLSLoaderClass = {
new(confg: HlsConfig): Loader<LoaderContext> new(config: HlsConfig): Loader<LoaderContext>
getEngine(): HlsJsP2PEngine getEngine(): HlsJsP2PEngine
} }
@ -300,3 +279,150 @@ export type PlaylistItemOptions = {
onClicked: () => void 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<typeof videojs.getPlayer> & {
options_: ReturnType<typeof videojs.getPlayer>['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<typeof Component>[1]
export type VideojsMenuItemOptions = ConstructorParameters<typeof MenuItem>[1]
export type VideojsMenuOptions = ConstructorParameters<typeof Menu>[1]
export type VideojsButtonOptions = ConstructorParameters<typeof Button>[1]
export type VideojsClickableComponentOptions = ConstructorParameters<typeof ClickableComponent>[1]
export type VideojsMenuButtonOptions = ConstructorParameters<typeof MenuButton>[1]
export type VideojsAutoplay = boolean | 'muted' | 'play' | 'any'
export type VideojsPlayerOptions = Partial<VideojsPlayer['options_']> & {
muted?: boolean
controls?: boolean
autoplay?: VideojsAutoplay
poster?: string
preload?: 'none'
plugins?: VideoJSPluginOptions
controlBar?: {
children: Record<string, any>
}
textTrackSettings?: boolean
}
export type VideojsCaptionsButton = CaptionButton & Component

View file

@ -7,7 +7,7 @@ import {
VideoPlaylistElement, VideoPlaylistElement,
VideoState VideoState
} from '@peertube/peertube-models' } 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 { TranslationsManager } from '@root-helpers/translations-manager'
import { PeerTubeServerError } from 'src/types' import { PeerTubeServerError } from 'src/types'
import type videojs from 'video.js' import type videojs from 'video.js'
@ -29,7 +29,7 @@ import {
import { PlayerHTML } from './shared/player-html' import { PlayerHTML } from './shared/player-html'
export class PeerTubeEmbed { export class PeerTubeEmbed {
player: videojs.Player player: VideojsPlayer
api: PeerTubeEmbedApi = null api: PeerTubeEmbedApi = null
config: HTMLServerConfig config: HTMLServerConfig

View file

172
pnpm-lock.yaml generated
View file

@ -693,18 +693,15 @@ importers:
'@types/sha.js': '@types/sha.js':
specifier: ^2.4.0 specifier: ^2.4.0
version: 2.4.4 version: 2.4.4
'@types/video.js':
specifier: ^7.3.40
version: 7.3.58
'@wdio/browserstack-service': '@wdio/browserstack-service':
specifier: ^9.12.7 specifier: ^9.12.7
version: 9.19.2(@wdio/cli@9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5))(bufferutil@4.0.9)(debug@4.4.1)(utf-8-validate@6.0.5) version: 9.19.2(@wdio/cli@9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5))(bufferutil@4.0.9)(debug@4.4.1)(utf-8-validate@6.0.5)
'@wdio/cli': '@wdio/cli':
specifier: ^9.12.7 specifier: ^9.12.7
version: 9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5) version: 9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5)
'@wdio/globals': '@wdio/globals':
specifier: ^9.17.0 specifier: ^9.17.0
version: 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) version: 9.17.0(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
'@wdio/local-runner': '@wdio/local-runner':
specifier: ^9.12.7 specifier: ^9.12.7
version: 9.19.2(@wdio/globals@9.17.0)(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) version: 9.19.2(@wdio/globals@9.17.0)(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
@ -823,8 +820,8 @@ importers:
specifier: ^2.0.3 specifier: ^2.0.3
version: 2.0.4(encoding@0.1.13) version: 2.0.4(encoding@0.1.13)
video.js: video.js:
specifier: ^7.19.2 specifier: ^8.23.4
version: 7.21.7 version: 8.23.4
vite: vite:
specifier: ^6.0.11 specifier: ^6.0.11
version: 6.3.5(@types/node@20.19.13)(sass-embedded@1.92.0)(sass@1.85.0)(tsx@4.20.5) version: 6.3.5(@types/node@20.19.13)(sass-embedded@1.92.0)(sass@1.85.0)(tsx@4.20.5)
@ -4257,9 +4254,6 @@ packages:
'@types/validator@13.15.2': '@types/validator@13.15.2':
resolution: {integrity: sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==} resolution: {integrity: sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==}
'@types/video.js@7.3.58':
resolution: {integrity: sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==}
'@types/webtorrent@0.110.0': '@types/webtorrent@0.110.0':
resolution: {integrity: sha512-f2Moh9KWdHqYT1N5hxbaCw213SyYyyHhxOoxJI4c53nwAiq4JCytpySV8cM5FC9oPOE7Y/4nPYbnreJNl9lJRA==} resolution: {integrity: sha512-f2Moh9KWdHqYT1N5hxbaCw213SyYyyHhxOoxJI4c53nwAiq4JCytpySV8cM5FC9oPOE7Y/4nPYbnreJNl9lJRA==}
@ -4378,18 +4372,18 @@ packages:
resolution: {integrity: sha512-e1swq86y3+88vXk0cA+gc+wztDZ7boLtbAUVeooxsipIVRMRrc7xU5vKAyoXHnciTA7s28J7W8xyq/HCJN9r0w==} resolution: {integrity: sha512-e1swq86y3+88vXk0cA+gc+wztDZ7boLtbAUVeooxsipIVRMRrc7xU5vKAyoXHnciTA7s28J7W8xyq/HCJN9r0w==}
engines: {node: '>=14.18.20'} engines: {node: '>=14.18.20'}
'@videojs/http-streaming@2.16.3': '@videojs/http-streaming@3.17.2':
resolution: {integrity: sha512-91CJv5PnFBzNBvyEjt+9cPzTK/xoVixARj2g7ZAvItA+5bx8VKdk5RxCz/PP2kdzz9W+NiDUMPkdmTsosmy69Q==} resolution: {integrity: sha512-VBQ3W4wnKnVKb/limLdtSD2rAd5cmHN70xoMf4OmuDd0t2kfJX04G+sfw6u2j8oOm2BXYM9E1f4acHruqKnM1g==}
engines: {node: '>=8', npm: '>=5'} engines: {node: '>=8', npm: '>=5'}
peerDependencies: peerDependencies:
video.js: ^6 || ^7 video.js: ^8.19.0
'@videojs/vhs-utils@3.0.5': '@videojs/vhs-utils@4.1.1':
resolution: {integrity: sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==} resolution: {integrity: sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA==}
engines: {node: '>=8', npm: '>=5'} engines: {node: '>=8', npm: '>=5'}
'@videojs/xhr@2.6.0': '@videojs/xhr@2.7.0':
resolution: {integrity: sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==} resolution: {integrity: sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==}
'@vitejs/plugin-basic-ssl@1.2.0': '@vitejs/plugin-basic-ssl@1.2.0':
resolution: {integrity: sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==} resolution: {integrity: sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==}
@ -4560,8 +4554,8 @@ packages:
addressparser@1.0.1: addressparser@1.0.1:
resolution: {integrity: sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==} resolution: {integrity: sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==}
aes-decrypter@3.1.3: aes-decrypter@4.0.2:
resolution: {integrity: sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==} resolution: {integrity: sha512-lc+/9s6iJvuaRe5qDlMTpCFjnwpkeOXp8qP3oiZ5jsj1MRg+SBVUmmICrhxHvc8OELSmc+fEyyxAuppY6hrWzw==}
agent-base@6.0.2: agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
@ -7146,9 +7140,6 @@ packages:
indexof@0.0.1: indexof@0.0.1:
resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==} resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==}
individual@2.0.0:
resolution: {integrity: sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g==}
inflection@1.13.4: inflection@1.13.4:
resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==} resolution: {integrity: sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==}
engines: {'0': node >= 0.4.0} engines: {'0': node >= 0.4.0}
@ -7639,9 +7630,6 @@ packages:
k-rpc@5.1.0: k-rpc@5.1.0:
resolution: {integrity: sha512-FGc+n70Hcjoa/X2JTwP+jMIOpBz+pkRffHnSl9yrYiwUxg3FIgD50+u1ePfJUOnRCnx6pbjmVk5aAeB1wIijuQ==} resolution: {integrity: sha512-FGc+n70Hcjoa/X2JTwP+jMIOpBz+pkRffHnSl9yrYiwUxg3FIgD50+u1ePfJUOnRCnx6pbjmVk5aAeB1wIijuQ==}
keycode@2.2.1:
resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==}
keyv@4.5.4: keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
@ -7891,8 +7879,8 @@ packages:
resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==} resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==}
engines: {node: '>=12'} engines: {node: '>=12'}
m3u8-parser@4.8.0: m3u8-parser@7.2.0:
resolution: {integrity: sha512-UqA2a/Pw3liR6Df3gwxrqghCP17OpPlQj6RBPLYygf/ZSQ4MoSgvdvhvt35qV+3NaaA0FSZx93Ix+2brT1U7cA==} resolution: {integrity: sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==}
magic-string@0.30.17: magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@ -8206,8 +8194,8 @@ packages:
resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
mpd-parser@0.22.1: mpd-parser@1.3.1:
resolution: {integrity: sha512-fwBebvpyPUU8bOzvhX0VQZgSohncbgYwUyJJoTSNpmy7ccD2ryiCvM7oRkn/xQH5cv73/xU7rJSNCLjdGFor0Q==} resolution: {integrity: sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==}
hasBin: true hasBin: true
mri@1.1.4: mri@1.1.4:
@ -8258,8 +8246,8 @@ packages:
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
engines: {node: ^18.17.0 || >=20.5.0} engines: {node: ^18.17.0 || >=20.5.0}
mux.js@6.0.1: mux.js@7.1.0:
resolution: {integrity: sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w==} resolution: {integrity: sha512-NTxawK/BBELJrYsZThEulyUMDVlLizKdxyAsMuzoCD1eFj97BVaA8D/CvKsKu6FOLYkFojN5CbM9h++ZTZtknA==}
engines: {node: '>=8', npm: '>=5'} engines: {node: '>=8', npm: '>=5'}
hasBin: true hasBin: true
@ -9513,9 +9501,6 @@ packages:
run-series@1.1.9: run-series@1.1.9:
resolution: {integrity: sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==} resolution: {integrity: sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==}
rust-result@1.0.0:
resolution: {integrity: sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==}
rxjs@7.8.1: rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
@ -9539,9 +9524,6 @@ packages:
safe-identifier@0.4.2: safe-identifier@0.4.2:
resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==}
safe-json-parse@4.0.0:
resolution: {integrity: sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==}
safe-push-apply@1.0.0: safe-push-apply@1.0.0:
resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -10646,9 +10628,6 @@ packages:
url-parse@1.5.10: url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
url-toolkit@2.2.5:
resolution: {integrity: sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==}
url@0.11.4: url@0.11.4:
resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -10745,11 +10724,17 @@ packages:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0} engines: {'0': node >=0.6.0}
video.js@7.21.7: video.js@8.23.4:
resolution: {integrity: sha512-T2s3WFAht7Zjr2OSJamND9x9Dn2O+Z5WuHGdh8jI5SYh5mkMdVTQ7vSRmA5PYpjXJ2ycch6jpMjkJEIEU2xxqw==} resolution: {integrity: sha512-qI0VTlYmKzEqRsz1Nppdfcaww4RSxZAq77z2oNSl3cNg2h6do5C8Ffl0KqWQ1OpD8desWXsCrde7tKJ9gGTEyQ==}
videojs-font@3.2.0: videojs-contrib-quality-levels@4.1.0:
resolution: {integrity: sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==} resolution: {integrity: sha512-TfrXJJg1Bv4t6TOCMEVMwF/CoS8iENYsWNKip8zfhB5kTcegiFYezEA0eHAJPU64ZC8NQbxQgOwAsYU8VXbOWA==}
engines: {node: '>=16', npm: '>=8'}
peerDependencies:
video.js: ^8
videojs-font@4.2.0:
resolution: {integrity: sha512-YPq+wiKoGy2/M7ccjmlvwi58z2xsykkkfNMyIg4xb7EZQQNwB71hcSsB3o75CqQV7/y5lXkXhI/rsGAS7jfEmQ==}
videojs-vtt.js@0.15.5: videojs-vtt.js@0.15.5:
resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==} resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==}
@ -14860,8 +14845,6 @@ snapshots:
'@types/validator@13.15.2': {} '@types/validator@13.15.2': {}
'@types/video.js@7.3.58': {}
'@types/webtorrent@0.110.0': '@types/webtorrent@0.110.0':
dependencies: dependencies:
'@types/bittorrent-protocol': 3.1.6 '@types/bittorrent-protocol': 3.1.6
@ -15116,24 +15099,23 @@ snapshots:
bytes: 3.1.2 bytes: 3.1.2
multiparty: 4.2.3 multiparty: 4.2.3
'@videojs/http-streaming@2.16.3(video.js@7.21.7)': '@videojs/http-streaming@3.17.2(video.js@8.23.4)':
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
'@videojs/vhs-utils': 3.0.5 '@videojs/vhs-utils': 4.1.1
aes-decrypter: 3.1.3 aes-decrypter: 4.0.2
global: 4.4.0 global: 4.4.0
m3u8-parser: 4.8.0 m3u8-parser: 7.2.0
mpd-parser: 0.22.1 mpd-parser: 1.3.1
mux.js: 6.0.1 mux.js: 7.1.0
video.js: 7.21.7 video.js: 8.23.4
'@videojs/vhs-utils@3.0.5': '@videojs/vhs-utils@4.1.1':
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
global: 4.4.0 global: 4.4.0
url-toolkit: 2.2.5
'@videojs/xhr@2.6.0': '@videojs/xhr@2.7.0':
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
global: 4.4.0 global: 4.4.0
@ -15195,13 +15177,13 @@ snapshots:
'@vue/shared@3.5.18': {} '@vue/shared@3.5.18': {}
'@wdio/browserstack-service@9.19.2(@wdio/cli@9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5))(bufferutil@4.0.9)(debug@4.4.1)(utf-8-validate@6.0.5)': '@wdio/browserstack-service@9.19.2(@wdio/cli@9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5))(bufferutil@4.0.9)(debug@4.4.1)(utf-8-validate@6.0.5)':
dependencies: dependencies:
'@browserstack/ai-sdk-node': 1.5.17(debug@4.4.1) '@browserstack/ai-sdk-node': 1.5.17(debug@4.4.1)
'@percy/appium-app': 2.1.0 '@percy/appium-app': 2.1.0
'@percy/selenium-webdriver': 2.2.3 '@percy/selenium-webdriver': 2.2.3
'@types/gitconfiglocal': 2.0.3 '@types/gitconfiglocal': 2.0.3
'@wdio/cli': 9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5) '@wdio/cli': 9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5)
'@wdio/logger': 9.18.0 '@wdio/logger': 9.18.0
'@wdio/reporter': 9.19.2 '@wdio/reporter': 9.19.2
'@wdio/types': 9.19.2 '@wdio/types': 9.19.2
@ -15224,11 +15206,11 @@ snapshots:
- supports-color - supports-color
- utf-8-validate - utf-8-validate
'@wdio/cli@9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5)': '@wdio/cli@9.19.2(@types/node@20.19.13)(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5)':
dependencies: dependencies:
'@vitest/snapshot': 2.1.9 '@vitest/snapshot': 2.1.9
'@wdio/config': 9.19.2 '@wdio/config': 9.19.2
'@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
'@wdio/logger': 9.18.0 '@wdio/logger': 9.18.0
'@wdio/protocols': 9.16.2 '@wdio/protocols': 9.16.2
'@wdio/types': 9.19.2 '@wdio/types': 9.19.2
@ -15273,7 +15255,7 @@ snapshots:
'@wdio/types': 9.19.2 '@wdio/types': 9.19.2
chalk: 5.6.0 chalk: 5.6.0
'@wdio/globals@9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))': '@wdio/globals@9.17.0(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))':
dependencies: dependencies:
expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) expect-webdriverio: 5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
webdriverio: 9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5) webdriverio: 9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)
@ -15283,7 +15265,7 @@ snapshots:
'@types/node': 20.19.13 '@types/node': 20.19.13
'@wdio/logger': 9.18.0 '@wdio/logger': 9.18.0
'@wdio/repl': 9.16.2 '@wdio/repl': 9.16.2
'@wdio/runner': 9.19.2(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) '@wdio/runner': 9.19.2(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
'@wdio/types': 9.19.2 '@wdio/types': 9.19.2
'@wdio/xvfb': 9.19.2 '@wdio/xvfb': 9.19.2
exit-hook: 4.0.0 exit-hook: 4.0.0
@ -15332,12 +15314,12 @@ snapshots:
diff: 8.0.2 diff: 8.0.2
object-inspect: 1.13.4 object-inspect: 1.13.4
'@wdio/runner@9.19.2(bufferutil@4.0.9)(expect-webdriverio@5.4.2)(utf-8-validate@6.0.5)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))': '@wdio/runner@9.19.2(bufferutil@4.0.9)(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(utf-8-validate@6.0.5)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))':
dependencies: dependencies:
'@types/node': 20.19.13 '@types/node': 20.19.13
'@wdio/config': 9.19.2 '@wdio/config': 9.19.2
'@wdio/dot-reporter': 9.19.2 '@wdio/dot-reporter': 9.19.2
'@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
'@wdio/logger': 9.18.0 '@wdio/logger': 9.18.0
'@wdio/types': 9.19.2 '@wdio/types': 9.19.2
'@wdio/utils': 9.19.2 '@wdio/utils': 9.19.2
@ -15446,10 +15428,10 @@ snapshots:
addressparser@1.0.1: {} addressparser@1.0.1: {}
aes-decrypter@3.1.3: aes-decrypter@4.0.2:
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
'@videojs/vhs-utils': 3.0.5 '@videojs/vhs-utils': 4.1.1
global: 4.4.0 global: 4.4.0
pkcs7: 1.0.4 pkcs7: 1.0.4
@ -17869,7 +17851,7 @@ snapshots:
expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)): expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)):
dependencies: dependencies:
'@vitest/snapshot': 3.2.4 '@vitest/snapshot': 3.2.4
'@wdio/globals': 9.17.0(expect-webdriverio@5.4.2)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)) '@wdio/globals': 9.17.0(expect-webdriverio@5.4.2(@wdio/globals@9.17.0)(@wdio/logger@9.18.0)(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)))(webdriverio@9.19.2(bufferutil@4.0.9)(utf-8-validate@6.0.5))
'@wdio/logger': 9.18.0 '@wdio/logger': 9.18.0
deep-eql: 5.0.2 deep-eql: 5.0.2
expect: 30.1.2 expect: 30.1.2
@ -18769,7 +18751,7 @@ snapshots:
i18next@24.2.3(typescript@5.8.3): i18next@24.2.3(typescript@5.8.3):
dependencies: dependencies:
'@babel/runtime': 7.28.2 '@babel/runtime': 7.28.3
optionalDependencies: optionalDependencies:
typescript: 5.8.3 typescript: 5.8.3
@ -18839,8 +18821,6 @@ snapshots:
indexof@0.0.1: {} indexof@0.0.1: {}
individual@2.0.0: {}
inflection@1.13.4: {} inflection@1.13.4: {}
inflight@1.0.6: inflight@1.0.6:
@ -19380,8 +19360,6 @@ snapshots:
randombytes: 2.1.0 randombytes: 2.1.0
optional: true optional: true
keycode@2.2.1: {}
keyv@4.5.4: keyv@4.5.4:
dependencies: dependencies:
json-buffer: 3.0.1 json-buffer: 3.0.1
@ -19635,10 +19613,10 @@ snapshots:
luxon@3.7.1: {} luxon@3.7.1: {}
m3u8-parser@4.8.0: m3u8-parser@7.2.0:
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
'@videojs/vhs-utils': 3.0.5 '@videojs/vhs-utils': 4.1.1
global: 4.4.0 global: 4.4.0
magic-string@0.30.17: magic-string@0.30.17:
@ -20002,10 +19980,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
mpd-parser@0.22.1: mpd-parser@1.3.1:
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
'@videojs/vhs-utils': 3.0.5 '@videojs/vhs-utils': 4.1.1
'@xmldom/xmldom': 0.8.11 '@xmldom/xmldom': 0.8.11
global: 4.4.0 global: 4.4.0
@ -20065,7 +20043,7 @@ snapshots:
mute-stream@2.0.0: {} mute-stream@2.0.0: {}
mux.js@6.0.1: mux.js@7.1.0:
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
global: 4.4.0 global: 4.4.0
@ -21562,10 +21540,6 @@ snapshots:
run-series@1.1.9: {} run-series@1.1.9: {}
rust-result@1.0.0:
dependencies:
individual: 2.0.0
rxjs@7.8.1: rxjs@7.8.1:
dependencies: dependencies:
tslib: 2.8.1 tslib: 2.8.1
@ -21590,10 +21564,6 @@ snapshots:
safe-identifier@0.4.2: {} safe-identifier@0.4.2: {}
safe-json-parse@4.0.0:
dependencies:
rust-result: 1.0.0
safe-push-apply@1.0.0: safe-push-apply@1.0.0:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
@ -22876,8 +22846,6 @@ snapshots:
querystringify: 2.2.0 querystringify: 2.2.0
requires-port: 1.0.0 requires-port: 1.0.0
url-toolkit@2.2.5: {}
url@0.11.4: url@0.11.4:
dependencies: dependencies:
punycode: 1.4.1 punycode: 1.4.1
@ -22974,23 +22942,27 @@ snapshots:
core-util-is: 1.0.2 core-util-is: 1.0.2
extsprintf: 1.4.1 extsprintf: 1.4.1
video.js@7.21.7: video.js@8.23.4:
dependencies: dependencies:
'@babel/runtime': 7.28.3 '@babel/runtime': 7.28.3
'@videojs/http-streaming': 2.16.3(video.js@7.21.7) '@videojs/http-streaming': 3.17.2(video.js@8.23.4)
'@videojs/vhs-utils': 3.0.5 '@videojs/vhs-utils': 4.1.1
'@videojs/xhr': 2.6.0 '@videojs/xhr': 2.7.0
aes-decrypter: 3.1.3 aes-decrypter: 4.0.2
global: 4.4.0 global: 4.4.0
keycode: 2.2.1 m3u8-parser: 7.2.0
m3u8-parser: 4.8.0 mpd-parser: 1.3.1
mpd-parser: 0.22.1 mux.js: 7.1.0
mux.js: 6.0.1 videojs-contrib-quality-levels: 4.1.0(video.js@8.23.4)
safe-json-parse: 4.0.0 videojs-font: 4.2.0
videojs-font: 3.2.0
videojs-vtt.js: 0.15.5 videojs-vtt.js: 0.15.5
videojs-font@3.2.0: {} videojs-contrib-quality-levels@4.1.0(video.js@8.23.4):
dependencies:
global: 4.4.0
video.js: 8.23.4
videojs-font@4.2.0: {}
videojs-vtt.js@0.15.5: videojs-vtt.js@0.15.5:
dependencies: dependencies: