]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Automatically adapt player ratio
authorChocobozzz <me@florianbigard.com>
Fri, 18 Aug 2023 07:48:45 +0000 (09:48 +0200)
committerChocobozzz <me@florianbigard.com>
Fri, 18 Aug 2023 07:48:45 +0000 (09:48 +0200)
12 files changed:
client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.scss
client/src/app/+videos/+video-watch/video-watch.component.scss
client/src/app/+videos/+video-watch/video-watch.component.ts
client/src/assets/player/peertube-player.ts
client/src/assets/player/shared/metrics/metrics-plugin.ts
client/src/assets/player/shared/p2p-media-loader/p2p-media-loader-plugin.ts
client/src/assets/player/shared/peertube/peertube-plugin.ts
client/src/assets/player/shared/web-video/web-video-plugin.ts
client/src/assets/player/types/peertube-player-options.ts
client/src/assets/player/types/peertube-videojs-typings.ts
client/src/sass/include/_variables.scss
packages/tests/src/api/notifications/notifications-api.ts

index 0f0ac19798207651b6a9616aca713f35e4c62268..2d43ffe4041d4cd2975851ebae72385dcd32b7ec 100644 (file)
@@ -6,6 +6,7 @@
 .playlist {
   position: relative;
   min-width: 200px;
+  width: 25vw;
   max-width: 470px;
   height: 66vh;
   background-color: pvar(--mainBackgroundColor);
index d438facd32870de80edff0b4e5ac869119e8c321..e535ecb9cf5b1c5a2316edb73c349dbf648d93ac 100644 (file)
@@ -4,15 +4,8 @@
 @use '_bootstrap-variables';
 @use '_miniature' as *;
 
-$video-height: 66vh;
-
-@function getPlayerHeight ($width) {
-  @return calc(#{$width} / #{$video-watch-player-factor});
-}
-
-@function getPlayerWidth ($height) {
-  @return calc(#{$height} * #{$video-watch-player-factor});
-}
+$video-default-height: 66vh;
+$video-theater-height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
 
 @mixin playlist-below-player {
   width: 100% !important;
@@ -38,20 +31,16 @@ $video-height: 66vh;
 
 .root {
   &.theater-enabled #video-wrapper {
-    $height: calc(100vh - #{$header-height} - #{$theater-bottom-space});
-
     flex-direction: column;
     justify-content: center;
 
     #videojs-wrapper {
       width: 100%;
-      height: $height;
+      height: $video-theater-height;
     }
 
     ::ng-deep .video-js {
-      height: $height;
-      width: 100%;
-      max-width: initial;
+      --player-height: #{$video-theater-height};
     }
 
     my-video-watch-playlist ::ng-deep .playlist {
@@ -65,10 +54,24 @@ $video-height: 66vh;
   display: flex;
   justify-content: center;
 
+  #videojs-wrapper {
+    display: flex;
+    justify-content: center;
+    flex-grow: 1;
+    height: $video-default-height;
+  }
+
   ::ng-deep .video-js {
+    --player-height: #{$video-default-height};
+    --player-portrait-mode: 0;
+    // Default player ratio, redefined by the player to automatically adapt player size
+    --player-ratio: #{math.div(16, 9)};
+
     width: 100%;
-    max-width: getPlayerWidth($video-height);
-    height: $video-height;
+    height: var(--player-height);
+
+    // Can be recalculated by the player depending on video ratio
+    max-width: calc(var(--player-height) * var(--player-ratio));
 
     // VideoJS create an inner video player
     video {
@@ -78,13 +81,6 @@ $video-height: 66vh;
   }
 }
 
-#videojs-wrapper {
-  display: flex;
-  justify-content: center;
-  flex-grow: 1;
-  height: $video-height;
-}
-
 .remote-server-down {
   color: #fff;
   display: flex;
@@ -253,12 +249,16 @@ my-video-comments {
 
 @media screen and (max-width: 600px) {
   #videojs-wrapper {
-    height: getPlayerHeight(100vw) !important;
+    // Reset height
+    height: initial !important;
 
     .remote-server-down,
     ::ng-deep .video-js {
-      width: 100vw;
-      height: getPlayerHeight(100vw) !important;
+      --player-portrait-mode: 1;
+
+      // Can be recalculated by the player depending on video ratio
+      height: calc(100vw / var(--player-ratio)) !important;
+      max-height: $video-theater-height;
     }
   }
 
index f109427ccdf62b3a68df68d739a8bb998b71b30c..febb3c82896e40d11de8706c45d5fd8ad0e50c01 100644 (file)
@@ -623,7 +623,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
       peertubeLink: () => false,
 
-      pluginsManager: this.pluginService.getPluginsManager()
+      pluginsManager: this.pluginService.getPluginsManager(),
+
+      autoPlayerRatio: {
+        cssRatioVariable: '--player-ratio',
+        cssPlayerPortraitModeVariable: '--player-portrait-mode'
+      }
     }
   }
 
index 4da681a086d0edcfcf7d033f952fa341598fad4d..111b4645b525bcc032fa5e9ff0cb6ba19d9a688c 100644 (file)
@@ -364,7 +364,9 @@ export class PeerTubePlayer {
         videoUUID: () => this.currentLoadOptions.videoUUID,
         subtitle: () => this.currentLoadOptions.subtitle,
 
-        poster: () => this.currentLoadOptions.poster
+        poster: () => this.currentLoadOptions.poster,
+
+        autoPlayerRatio: this.options.autoPlayerRatio
       },
       metrics: {
         mode: () => this.currentLoadOptions.mode,
index 0ad16338c5e2c6f8b86c7df5a4ef681e995834d8..a581d793158c787cc95889980d3dca2f71855cac 100644 (file)
@@ -140,10 +140,10 @@ class MetricsPlugin extends Plugin {
 
   private trackBytes () {
     this.player.on('network-info', (_event, data: PlayerNetworkInfo) => {
-      this.downloadedBytesHTTP += Math.round(data.http.downloaded - (this.lastPlayerNetworkInfo?.http.downloaded || 0))
-      this.downloadedBytesP2P += Math.round((data.p2p?.downloaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.downloaded || 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.uploadedBytesP2P += Math.round((data.p2p?.uploaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.uploaded || 0))
+      this.uploadedBytesP2P += Math.max(Math.round((data.p2p?.uploaded || 0) - (this.lastPlayerNetworkInfo?.p2p?.uploaded || 0)), 0)
 
       this.p2pPeers = data.p2p?.peersP2POnly
       this.p2pEnabled = !!data.p2p
index 1e47fe48688fbc6cdbc800f0b52a442495c1ec3f..ee617ce8a6e0c54461ff79e90946828f5a5f0e51 100644 (file)
@@ -119,6 +119,14 @@ class P2pMediaLoaderPlugin extends Plugin {
     this.runStats()
 
     this.hlsjs.on(Hlsjs.Events.LEVEL_SWITCHED, () => this.player.trigger('engine-resolution-change'))
+
+    this.hlsjs.on(Hlsjs.Events.MANIFEST_PARSED, (_event, data) => {
+      if (Array.isArray(data.levels) && data.levels.length > 1) {
+        const level = data.levels[0]
+
+        this.player.trigger('video-ratio-changed', { ratio: level.width / level.height })
+      }
+    })
   }
 
   private runStats () {
index cf866723c6aa52b8b458b17f3e10d04ea8a1244d..7aeae26704a1aaca63ba0c503132ffac0880f172 100644 (file)
@@ -115,6 +115,8 @@ class PeerTubePlugin extends Plugin {
         this.hideFatalError()
       })
     })
+
+    this.initOnRatioChange()
   }
 
   dispose () {
@@ -210,6 +212,25 @@ class PeerTubePlugin extends Plugin {
     this.runUserViewing()
   }
 
+  private initOnRatioChange () {
+    if (!this.options.autoPlayerRatio) return
+
+    const defaultRatio = getComputedStyle(this.player.el()).getPropertyValue(this.options.autoPlayerRatio.cssRatioVariable)
+
+    this.player.on('video-ratio-changed', (_event, data: { ratio: number }) => {
+      const el = this.player.el() as HTMLElement
+
+      // In portrait screen mode, we allow player with bigger height size than width
+      const portraitMode = getComputedStyle(el).getPropertyValue(this.options.autoPlayerRatio.cssPlayerPortraitModeVariable) === '1'
+
+      const currentRatio = !portraitMode && data.ratio < 1
+        ? defaultRatio
+        : data.ratio
+
+      el.style.setProperty('--player-ratio', currentRatio + '')
+    })
+  }
+
   // ---------------------------------------------------------------------------
 
   private runUserViewing () {
index 18d9111083fd36afa681984172d7cea016b42eb4..8f4db0680ac989da41cdd567cfa5607b6f1822b4 100644 (file)
@@ -19,6 +19,7 @@ class WebVideoPlugin extends Plugin {
 
   private onErrorHandler: () => void
   private onPlayHandler: () => void
+  private onLoadedMetadata: () => void
 
   constructor (player: videojs.Player, options?: WebVideoPluginOptions) {
     super(player, options)
@@ -28,6 +29,12 @@ class WebVideoPlugin extends Plugin {
 
     this.updateVideoFile({ videoFile: this.pickAverageVideoFile(), isUserResolutionChange: false })
 
+    this.onLoadedMetadata = () => {
+      player.trigger('video-ratio-changed', { ratio: this.player.videoWidth() / this.player.videoHeight() })
+    }
+
+    player.on('loadedmetadata', this.onLoadedMetadata)
+
     player.ready(() => {
       this.buildQualities()
 
@@ -43,6 +50,7 @@ class WebVideoPlugin extends Plugin {
   dispose () {
     clearInterval(this.networkInfoInterval)
 
+    if (this.onLoadedMetadata) this.player.off('loadedmetadata', this.onLoadedMetadata)
     if (this.onErrorHandler) this.player.off('error', this.onErrorHandler)
     if (this.onPlayHandler) this.player.off('canplay', this.onPlayHandler)
 
index 352f7d8ddb2ac0a5fd95a917126a93758c40f743..6fb2f7913729b4b6724550270547ee83aef1c10b 100644 (file)
@@ -38,6 +38,11 @@ export type PeerTubePlayerContructorOptions = {
   language: string
 
   pluginsManager: PluginsManager
+
+  autoPlayerRatio?: {
+    cssRatioVariable: string
+    cssPlayerPortraitModeVariable: string
+  }
 }
 
 export type PeerTubePlayerLoadOptions = {
index dae9e14c8ad3c2457631ae824a6aee4dcc3ad0ba..27fbda31dcea8e86a91e80220e6cd48c95ee910c 100644 (file)
@@ -104,6 +104,11 @@ type VideoJSStoryboard = {
 }
 
 type PeerTubePluginOptions = {
+  autoPlayerRatio: {
+    cssRatioVariable: string
+    cssPlayerPortraitModeVariable: string
+  }
+
   hasAutoplay: () => videojs.Autoplay
 
   videoViewUrl: () => string
index 43a0057748d958320c64f9f5063542d0f9ce10cb..f414cf185666380c2dd8604c0fed72df36a4ffc0 100644 (file)
@@ -97,7 +97,6 @@ $activated-action-button-color: #212529;
 $focus-box-shadow-form: 0 0 0 .2rem;
 $form-input-font-size: 15px;
 
-$video-watch-player-factor: math.div(16, 9);
 $video-watch-info-margin-left: 44px;
 
 $primeng-breakpoint: 960px;
index 1c746155370ce5531e4f5a708fe3c7e9869e9c90..f99b81e94d6ed7a3d6729369040a151028e3e747 100644 (file)
@@ -18,7 +18,7 @@ describe('Test notifications API', function () {
   let emails: object[] = []
 
   before(async function () {
-    this.timeout(120000)
+    this.timeout(240000)
 
     const res = await prepareNotificationsTest(1)
     emails = res.emails