Add theatre mode
authorChocobozzz <me@florianbigard.com>
Mon, 11 Jun 2018 14:49:56 +0000 (16:49 +0200)
committerChocobozzz <me@florianbigard.com>
Mon, 11 Jun 2018 14:49:56 +0000 (16:49 +0200)
client/src/app/videos/+video-watch/video-watch.component.scss
client/src/app/videos/+video-watch/video-watch.component.ts
client/src/assets/player/images/theater.svg [new file with mode: 0644]
client/src/assets/player/peertube-player.ts
client/src/assets/player/peertube-videojs-plugin.ts
client/src/assets/player/theater-button.ts [new file with mode: 0644]
client/src/assets/player/utils.ts
client/src/sass/include/_variables.scss
client/src/sass/player/peertube-skin.scss
client/src/standalone/videos/embed.ts

index d1f8409375a527a2837325757cccb428302a2084..00e776a69a0f232d24fe3fed404c60194c973547 100644 (file)
       position: relative !important;
     }
   }
+
+  /deep/ .video-js.vjs-theater-enabled {
+    width: 100%;
+    height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
+  }
 }
 
 #video-not-found {
index 0f4f5ce892c52ab407a1d484a3b51acf8857f0d8..eefa43a73bde51ae4d543e1ccc7cd7627dbd4a51 100644 (file)
@@ -368,7 +368,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
       enableHotkeys: true,
       peertubeLink: false,
       poster: this.video.previewUrl,
-      startTime
+      startTime,
+      theaterMode: true
     })
 
     if (this.videojsLocaleLoaded === false) {
diff --git a/client/src/assets/player/images/theater.svg b/client/src/assets/player/images/theater.svg
new file mode 100644 (file)
index 0000000..d7086c2
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
+    <title>theater</title>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Artboard-4" transform="translate(-576.000000, -159.000000)" stroke="#fff" stroke-width="2">
+            <g id="33" transform="translate(576.000000, 159.000000)">
+                <rect id="Rectangle-433" x="1" y="6" width="22" height="12"></rect>
+            </g>
+        </g>
+    </g>
+</svg>
index eb75091de24ede24127e8dff4258351723228b02..afc8e0881752292594644ed949dafaa229b68520 100644 (file)
@@ -10,6 +10,7 @@ import './settings-menu-button'
 import './webtorrent-info-button'
 import './peertube-videojs-plugin'
 import './peertube-load-progress-bar'
+import './theater-button'
 import { videojsUntyped } from './peertube-videojs-typings'
 import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils'
 import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n'
@@ -28,6 +29,7 @@ function getVideojsOptions (options: {
   peertubeLink: boolean,
   poster: string,
   startTime: number
+  theaterMode: boolean
 }) {
   const videojsOptions = {
     controls: true,
@@ -63,6 +65,7 @@ function getVideojsOptions (options: {
 
 function getControlBarChildren (options: {
   peertubeLink: boolean
+  theaterMode: boolean
 }) {
   const children = {
     'playToggle': {},
@@ -105,6 +108,12 @@ function getControlBarChildren (options: {
     })
   }
 
+  if (options.theaterMode === true) {
+    Object.assign(children, {
+      'theaterButton': {}
+    })
+  }
+
   Object.assign(children, {
     'fullscreenToggle': {}
   })
index 47288c842323b8d2eecfaf06b3fbc80baf5cd36c..d3ae7b1374a7d849a92ecf3f98ec27f81e4c3dc6 100644 (file)
@@ -55,6 +55,7 @@ class PeerTubePlugin extends Plugin {
   private player: any
   private currentVideoFile: VideoFile
   private torrent: WebTorrent.Torrent
+  private fakeRenderer
   private autoResolution = true
   private isAutoResolutionObservation = false
 
@@ -123,6 +124,8 @@ class PeerTubePlugin extends Plugin {
 
     // Don't need to destroy renderer, video player will be destroyed
     this.flushVideoFile(this.currentVideoFile, false)
+
+    this.destroyFakeRenderer()
   }
 
   getCurrentResolutionId () {
@@ -185,7 +188,6 @@ class PeerTubePlugin extends Plugin {
     console.log('Adding ' + magnetOrTorrentUrl + '.')
 
     const oldTorrent = this.torrent
-    let fakeRenderer
     const torrentOptions = {
       store: (chunkLength, storeOpts) => new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), {
         max: 100
@@ -205,7 +207,7 @@ class PeerTubePlugin extends Plugin {
         if (options.delay) {
           const fakeVideoElem = document.createElement('video')
           renderVideo(torrent.files[0], fakeVideoElem, { autoplay: false, controls: false }, (err, renderer) => {
-            fakeRenderer = renderer
+            this.fakeRenderer = renderer
 
             if (err) console.error('Cannot render new torrent in fake video element.', err)
 
@@ -217,16 +219,7 @@ class PeerTubePlugin extends Plugin {
 
       // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution)
       this.addTorrentDelay = setTimeout(() => {
-        if (fakeRenderer) {
-          if (fakeRenderer.destroy) {
-            try {
-              fakeRenderer.destroy()
-            } catch (err) {
-              console.log('Cannot destroy correctly fake renderer.', err)
-            }
-          }
-          fakeRenderer = undefined
-        }
+        this.destroyFakeRenderer()
 
         const paused = this.player.paused()
 
@@ -567,6 +560,19 @@ class PeerTubePlugin extends Plugin {
     return this.videoFiles[Math.floor(this.videoFiles.length / 2)]
   }
 
+  private destroyFakeRenderer () {
+    if (this.fakeRenderer) {
+      if (this.fakeRenderer.destroy) {
+        try {
+          this.fakeRenderer.destroy()
+        } catch (err) {
+          console.log('Cannot destroy correctly fake renderer.', err)
+        }
+      }
+      this.fakeRenderer = undefined
+    }
+  }
+
   // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657
   private initSmoothProgressBar () {
     const SeekBar = videojsUntyped.getComponent('SeekBar')
diff --git a/client/src/assets/player/theater-button.ts b/client/src/assets/player/theater-button.ts
new file mode 100644 (file)
index 0000000..1d330e0
--- /dev/null
@@ -0,0 +1,46 @@
+import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings'
+import { getStoredTheater, saveTheaterInStore } from './utils'
+
+const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button')
+class TheaterButton extends Button {
+
+  private static readonly THEATER_MODE_CLASS = 'vjs-theater-enabled'
+
+  constructor (player, options) {
+    super(player, options)
+
+    const enabled = getStoredTheater()
+    if (enabled === true) {
+      this.player_.addClass(TheaterButton.THEATER_MODE_CLASS)
+      this.handleTheaterChange()
+    }
+  }
+
+  buildCSSClass () {
+    return `vjs-theater-control ${super.buildCSSClass()}`
+  }
+
+  handleTheaterChange () {
+    if (this.isTheaterEnabled()) {
+      this.controlText('Normal mode')
+    } else {
+      this.controlText('Theater mode')
+    }
+
+    saveTheaterInStore(this.isTheaterEnabled())
+  }
+
+  handleClick () {
+    this.player_.toggleClass(TheaterButton.THEATER_MODE_CLASS)
+
+    this.handleTheaterChange()
+  }
+
+  private isTheaterEnabled () {
+    return this.player_.hasClass(TheaterButton.THEATER_MODE_CLASS)
+  }
+}
+
+TheaterButton.prototype.controlText_ = 'Theater mode'
+
+TheaterButton.registerComponent('TheaterButton', TheaterButton)
index 4eaf537209022d96770fe6f601a60de8ee79bb3b..b7cd40aa28e9155577ed49b1f270e1f3b92a33ec 100644 (file)
@@ -51,6 +51,13 @@ function getAverageBandwidth () {
   return undefined
 }
 
+function getStoredTheater () {
+  const value = getLocalStorage('theater-enabled')
+  if (value !== null && value !== undefined) return value === 'true'
+
+  return undefined
+}
+
 function saveVolumeInStore (value: number) {
   return setLocalStorage('volume', value.toString())
 }
@@ -59,6 +66,10 @@ function saveMuteInStore (value: boolean) {
   return setLocalStorage('mute', value.toString())
 }
 
+function saveTheaterInStore (enabled: boolean) {
+  return setLocalStorage('theater-enabled', enabled.toString())
+}
+
 function saveAverageBandwidth (value: number) {
   return setLocalStorage('average-bandwidth', value.toString())
 }
@@ -133,6 +144,8 @@ export {
   videoFileMaxByResolution,
   videoFileMinByResolution,
   copyToClipboard,
+  getStoredTheater,
+  saveTheaterInStore,
   isMobile,
   bytes
 }
index 95b4b166e548a8a9cc9c0074efa40d33d459cb08..092f8ed2422f183e9e879f86da7225c91345fec9 100644 (file)
@@ -31,3 +31,5 @@ $footer-border-color: $header-border-color;
 
 $video-thumbnail-height: 110px;
 $video-thumbnail-width: 200px;
+
+$theater-bottom-space: 85px;
index de6501962f1be234b5cbcb6519bc9d98f848931a..e60a854d4633cfe071bebc56e7faea468e0de3bd 100644 (file)
     .vjs-resolution-control,
     .vjs-fullscreen-control,
     .vjs-peertube-link,
+    .vjs-theater-control,
     .vjs-settings
     {
       color: $primary-foreground-color !important;
       padding: 0 5px;
     }
 
+    .vjs-theater-control {
+      @include disable-outline;
+
+      width: 37px;
+      margin-right: 1px;
+
+      .vjs-icon-placeholder {
+        display: inline-block;
+        width: 22px;
+        height: 22px;
+        vertical-align: middle;
+        background: url('#{$assets-path}/player/images/theater.svg') no-repeat;
+        background-size: contain;
+
+        &::before {
+          content: '';
+        }
+      }
+    }
+
     .vjs-fullscreen-control {
       @include disable-outline;
 
   }
 
   @media screen and (max-width: 750px) {
+    .vjs-theater-control {
+      display: none;
+    }
+
     .vjs-dock-text {
       font-size: 16px;
     }
index e28d964de64543f39ccfd94c838d68a2f6935c7d..bf0eb484ee70a3397954e74f8971fc88d07e84a9 100644 (file)
@@ -99,7 +99,8 @@ loadLocale(window.location.origin, videojs, navigator.language)
       enableHotkeys: true,
       peertubeLink: true,
       poster: window.location.origin + videoInfo.previewPath,
-      startTime
+      startTime,
+      theaterMode: false
     })
     videojs(videoContainerId, videojsOptions, function () {
       const player = this