From f1a0555a88db9ade2b073a2e4dc73c4a6176c8a0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 11 Jan 2022 11:26:35 +0100 Subject: [PATCH] Add player controls on mobile --- .../src/assets/player/bezels/pause-bezel.ts | 4 ++ .../player/mobile/peertube-mobile-buttons.ts | 42 +++++++++++ .../player/mobile/peertube-mobile-plugin.ts | 16 +++++ .../assets/player/peertube-player-manager.ts | 6 +- client/src/assets/player/peertube-plugin.ts | 7 +- .../assets/player/peertube-videojs-typings.ts | 2 + .../next-previous-video-button.ts | 1 - client/src/sass/player/_player-variables.scss | 3 + client/src/sass/player/control-bar.scss | 4 +- client/src/sass/player/mobile.scss | 71 +++++++++++++++++++ client/src/sass/player/settings-menu.scss | 1 + 11 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 client/src/assets/player/mobile/peertube-mobile-buttons.ts create mode 100644 client/src/assets/player/mobile/peertube-mobile-plugin.ts diff --git a/client/src/assets/player/bezels/pause-bezel.ts b/client/src/assets/player/bezels/pause-bezel.ts index 886574380..315964311 100644 --- a/client/src/assets/player/bezels/pause-bezel.ts +++ b/client/src/assets/player/bezels/pause-bezel.ts @@ -1,4 +1,5 @@ import videojs from 'video.js' +import { isMobile } from '../utils' function getPauseBezel () { return ` @@ -37,6 +38,9 @@ class PauseBezel extends Component { constructor (player: videojs.Player, options?: videojs.ComponentOptions) { super(player, options) + // Hide bezels on mobile since we already have our mobile overlay + if (isMobile()) return + player.on('pause', (_: any) => { if (player.seeking() || player.ended()) return this.container.innerHTML = getPauseBezel() diff --git a/client/src/assets/player/mobile/peertube-mobile-buttons.ts b/client/src/assets/player/mobile/peertube-mobile-buttons.ts new file mode 100644 index 000000000..d6f8f35e3 --- /dev/null +++ b/client/src/assets/player/mobile/peertube-mobile-buttons.ts @@ -0,0 +1,42 @@ +import videojs from 'video.js' + +import debug from 'debug' + +const logger = debug('peertube:player:mobile') + +const Component = videojs.getComponent('Component') +class PeerTubeMobileButtons extends Component { + + createEl () { + const container = super.createEl('div', { + className: 'vjs-mobile-buttons-overlay' + }) as HTMLDivElement + + container.addEventListener('click', () => { + logger('Set user as inactive') + + this.player_.userActive(false) + }) + + const mainButton = super.createEl('div', { + className: 'main-button' + }) as HTMLDivElement + + mainButton.addEventListener('click', e => { + e.stopPropagation() + + if (this.player_.paused() || this.player_.ended()) { + this.player_.play() + return + } + + this.player_.pause() + }) + + container.appendChild(mainButton) + + return container + } +} + +videojs.registerComponent('PeerTubeMobileButtons', PeerTubeMobileButtons) diff --git a/client/src/assets/player/mobile/peertube-mobile-plugin.ts b/client/src/assets/player/mobile/peertube-mobile-plugin.ts new file mode 100644 index 000000000..b3834e20d --- /dev/null +++ b/client/src/assets/player/mobile/peertube-mobile-plugin.ts @@ -0,0 +1,16 @@ +import videojs from 'video.js' +import './peertube-mobile-buttons' + +const Plugin = videojs.getPlugin('plugin') + +class PeerTubeMobilePlugin extends Plugin { + + constructor (player: videojs.Player, options: videojs.PlayerOptions) { + super(player, options) + + player.addChild('PeerTubeMobileButtons') + } +} + +videojs.registerPlugin('peertubeMobile', PeerTubeMobilePlugin) +export { PeerTubeMobilePlugin } diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index ac8134fa8..6b6c1e581 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts @@ -21,6 +21,7 @@ import './videojs-components/settings-panel' import './videojs-components/settings-panel-child' import './videojs-components/theater-button' import './playlist/playlist-plugin' +import './mobile/peertube-mobile-plugin' import videojs from 'video.js' import { HlsJsEngineSettings } from '@peertube/p2p-media-loader-hlsjs' import { PluginsManager } from '@root-helpers/plugins-manager' @@ -43,7 +44,7 @@ import { VideoJSPluginOptions } from './peertube-videojs-typings' import { TranslationsManager } from './translations-manager' -import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isSafari } from './utils' +import { buildVideoOrPlaylistEmbed, getRtcConfig, isIOS, isMobile, isSafari } from './utils' // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) (videojs.getComponent('PlaybackRateMenuButton') as any).prototype.controlText_ = 'Speed' @@ -189,7 +190,10 @@ export class PeertubePlayerManager { videoEmbedTitle: options.common.embedTitle }) + if (isMobile()) player.peertubeMobile() + player.bezels() + player.stats({ videoUUID: options.common.videoUUID, videoIsLive: options.common.isLive, diff --git a/client/src/assets/player/peertube-plugin.ts b/client/src/assets/player/peertube-plugin.ts index 451b4a161..272f5353d 100644 --- a/client/src/assets/player/peertube-plugin.ts +++ b/client/src/assets/player/peertube-plugin.ts @@ -12,6 +12,9 @@ import { import { PeerTubePluginOptions, UserWatching, VideoJSCaption } from './peertube-videojs-typings' import { isMobile } from './utils' import { SettingsButton } from './videojs-components/settings-menu-button' +import debug from 'debug' + +const logger = debug('peertube:player:peertube') const Plugin = videojs.getPlugin('plugin') @@ -233,7 +236,7 @@ class PeerTubePlugin extends Plugin { } private alterInactivity () { - if (this.menuOpened || this.mouseInSettings || this.mouseInControlBar || this.isTouchEnabled()) { + if (this.menuOpened || this.mouseInSettings || this.mouseInControlBar) { this.setInactivityTimeout(0) return } @@ -245,6 +248,8 @@ class PeerTubePlugin extends Plugin { private setInactivityTimeout (timeout: number) { (this.player as any).cache_.inactivityTimeout = timeout this.player.options_.inactivityTimeout = timeout + + logger('Set player inactivity to ' + timeout) } private isTouchEnabled () { diff --git a/client/src/assets/player/peertube-videojs-typings.ts b/client/src/assets/player/peertube-videojs-typings.ts index f97d7a208..e4013d815 100644 --- a/client/src/assets/player/peertube-videojs-typings.ts +++ b/client/src/assets/player/peertube-videojs-typings.ts @@ -42,6 +42,8 @@ declare module 'video.js' { bezels (): void + peertubeMobile (): void + stats (options?: StatsCardOptions): StatsForNerdsPlugin textTracks (): TextTrackList & { diff --git a/client/src/assets/player/videojs-components/next-previous-video-button.ts b/client/src/assets/player/videojs-components/next-previous-video-button.ts index 228231a22..fe17ce2ce 100644 --- a/client/src/assets/player/videojs-components/next-previous-video-button.ts +++ b/client/src/assets/player/videojs-components/next-previous-video-button.ts @@ -40,7 +40,6 @@ class NextPreviousVideoButton extends Button { update () { const disabled = this.nextPreviousVideoButtonOptions.isDisabled() - console.log(disabled) if (disabled) this.addClass('vjs-disabled') else this.removeClass('vjs-disabled') diff --git a/client/src/sass/player/_player-variables.scss b/client/src/sass/player/_player-variables.scss index 2625576d4..dec3fe911 100644 --- a/client/src/sass/player/_player-variables.scss +++ b/client/src/sass/player/_player-variables.scss @@ -8,11 +8,14 @@ $font-size: 13px; $control-bar-height: 38px; $control-bar-icon-size: 26px; $control-bar-volume-slider-height: 14px; +$control-bar-slider-top: -10px; $control-bar-font-size: 14px; $control-bar-play-font-size: 34px; $control-bar-next-previous-play-font-size: 14px; $control-bar-button-width: 38px; +$control-bar-total-height: $control-bar-height - $control-bar-slider-top; + $slider-bg-color: lighten($primary-background-color, 33%); $progress-margin: 10px; diff --git a/client/src/sass/player/control-bar.scss b/client/src/sass/player/control-bar.scss index b3a96510a..c4e1283f8 100644 --- a/client/src/sass/player/control-bar.scss +++ b/client/src/sass/player/control-bar.scss @@ -4,6 +4,8 @@ @use './_player-variables' as *; .video-js.vjs-peertube-skin .vjs-control-bar { + z-index: 100; + height: $control-bar-height; background: linear-gradient(rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.6)); box-shadow: 0 -15px 40px 10px rgba(0, 0, 0, 0.2); @@ -49,7 +51,7 @@ @include margin-left($progress-margin); position: absolute; - top: -10px; + top: $control-bar-slider-top; z-index: 100; // On top of the progress bar width: calc(100% - (2 * #{$progress-margin})); height: 14px; diff --git a/client/src/sass/player/mobile.scss b/client/src/sass/player/mobile.scss index 86c090200..d72dc41df 100644 --- a/client/src/sass/player/mobile.scss +++ b/client/src/sass/player/mobile.scss @@ -1,5 +1,7 @@ +@use 'sass:math'; @use '_variables' as *; @use '_mixins' as *; +@use './_player-variables' as *; /* Special mobile style */ @@ -14,3 +16,72 @@ } } } + +.vjs-mobile-buttons-overlay { + display: none; + + position: absolute; + background-color: rgba(0, 0, 0, 0.4); + width: 100%; + height: 100%; + top: 0; + + .vjs-user-active, + .vjs-paused { + display: block; + } + + .main-button { + font-family: VideoJS; + font-weight: normal; + font-style: normal; + font-size: 5em; + width: fit-content; + margin: auto; + position: relative; + top: calc(50% - 10px); + transform: translateY(-50%); + } +} + +.vjs-paused { + .main-button { + &:before { + content: '\f101'; + } + } +} + +.vjs-playing { + .main-button { + &:before { + content: '\f103'; + } + } +} + +.vjs-ended { + .main-button { + &:before { + content: '\f116'; + } + } +} + +.vjs-has-started { + + &.vjs-user-active, + &.vjs-paused { + .vjs-mobile-buttons-overlay { + display: block; + } + } + + &.vjs-seeking, + &.vjs-scrubbing, + &.vjs-waiting { + .vjs-mobile-buttons-overlay { + display: none; + } + } +} diff --git a/client/src/sass/player/settings-menu.scss b/client/src/sass/player/settings-menu.scss index 5a476259e..8aa2c2ac3 100644 --- a/client/src/sass/player/settings-menu.scss +++ b/client/src/sass/player/settings-menu.scss @@ -22,6 +22,7 @@ $setting-transition-easing: ease-out; opacity: $primary-foreground-opacity; margin: 0 auto; font-size: $font-size !important; + z-index: 100; width: auto; overflow: hidden;