diff options
author | Chocobozzz <me@florianbigard.com> | 2022-12-30 15:54:08 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-12-30 15:54:08 +0100 |
commit | 66b73484c719659d213cab225ae3b63a3aae5228 (patch) | |
tree | abb57afc023eb188e97ac7adc5a3fe9871c320d3 | |
parent | b27bce21f0e3002b81327c4120123f258d88b700 (diff) | |
download | PeerTube-66b73484c719659d213cab225ae3b63a3aae5228.tar.gz PeerTube-66b73484c719659d213cab225ae3b63a3aae5228.tar.zst PeerTube-66b73484c719659d213cab225ae3b63a3aae5228.zip |
Prevent hotkeys playback rate/seek with lives
4 files changed, 80 insertions, 39 deletions
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index fa0417832..84548de97 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -133,8 +133,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
133 | this.loadRouteParams() | 133 | this.loadRouteParams() |
134 | this.loadRouteQuery() | 134 | this.loadRouteQuery() |
135 | 135 | ||
136 | this.initHotkeys() | ||
137 | |||
138 | this.theaterEnabled = getStoredTheater() | 136 | this.theaterEnabled = getStoredTheater() |
139 | 137 | ||
140 | this.hooks.runAction('action:video-watch.init', 'video-watch') | 138 | this.hooks.runAction('action:video-watch.init', 'video-watch') |
@@ -407,6 +405,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
407 | if (res === false) return this.location.back() | 405 | if (res === false) return this.location.back() |
408 | } | 406 | } |
409 | 407 | ||
408 | this.buildHotkeysHelp(video) | ||
409 | |||
410 | this.buildPlayer({ urlOptions, loggedInOrAnonymousUser, forceAutoplay }) | 410 | this.buildPlayer({ urlOptions, loggedInOrAnonymousUser, forceAutoplay }) |
411 | .catch(err => logger.error('Cannot build the player', err)) | 411 | .catch(err => logger.error('Cannot build the player', err)) |
412 | 412 | ||
@@ -787,33 +787,43 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
787 | this.video.viewers = newViewers | 787 | this.video.viewers = newViewers |
788 | } | 788 | } |
789 | 789 | ||
790 | private initHotkeys () { | 790 | private buildHotkeysHelp (video: Video) { |
791 | if (this.hotkeys.length !== 0) { | ||
792 | this.hotkeysService.remove(this.hotkeys) | ||
793 | } | ||
794 | |||
791 | this.hotkeys = [ | 795 | this.hotkeys = [ |
792 | // These hotkeys are managed by the player | 796 | // These hotkeys are managed by the player |
793 | new Hotkey('f', e => e, undefined, $localize`Enter/exit fullscreen`), | 797 | new Hotkey('f', e => e, undefined, $localize`Enter/exit fullscreen`), |
794 | new Hotkey('space', e => e, undefined, $localize`Play/Pause the video`), | 798 | new Hotkey('space', e => e, undefined, $localize`Play/Pause the video`), |
795 | new Hotkey('m', e => e, undefined, $localize`Mute/unmute the video`), | 799 | new Hotkey('m', e => e, undefined, $localize`Mute/unmute the video`), |
796 | 800 | ||
797 | new Hotkey('0-9', e => e, undefined, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90%`), | ||
798 | |||
799 | new Hotkey('up', e => e, undefined, $localize`Increase the volume`), | 801 | new Hotkey('up', e => e, undefined, $localize`Increase the volume`), |
800 | new Hotkey('down', e => e, undefined, $localize`Decrease the volume`), | 802 | new Hotkey('down', e => e, undefined, $localize`Decrease the volume`), |
801 | 803 | ||
802 | new Hotkey('right', e => e, undefined, $localize`Seek the video forward`), | ||
803 | new Hotkey('left', e => e, undefined, $localize`Seek the video backward`), | ||
804 | |||
805 | new Hotkey('>', e => e, undefined, $localize`Increase playback rate`), | ||
806 | new Hotkey('<', e => e, undefined, $localize`Decrease playback rate`), | ||
807 | |||
808 | new Hotkey(',', e => e, undefined, $localize`Navigate in the video to the previous frame`), | ||
809 | new Hotkey('.', e => e, undefined, $localize`Navigate in the video to the next frame`), | ||
810 | |||
811 | new Hotkey('t', e => { | 804 | new Hotkey('t', e => { |
812 | this.theaterEnabled = !this.theaterEnabled | 805 | this.theaterEnabled = !this.theaterEnabled |
813 | return false | 806 | return false |
814 | }, undefined, $localize`Toggle theater mode`) | 807 | }, undefined, $localize`Toggle theater mode`) |
815 | ] | 808 | ] |
816 | 809 | ||
810 | if (!video.isLive) { | ||
811 | this.hotkeys = this.hotkeys.concat([ | ||
812 | // These hotkeys are also managed by the player but only for VOD | ||
813 | |||
814 | new Hotkey('0-9', e => e, undefined, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90%`), | ||
815 | |||
816 | new Hotkey('right', e => e, undefined, $localize`Seek the video forward`), | ||
817 | new Hotkey('left', e => e, undefined, $localize`Seek the video backward`), | ||
818 | |||
819 | new Hotkey('>', e => e, undefined, $localize`Increase playback rate`), | ||
820 | new Hotkey('<', e => e, undefined, $localize`Decrease playback rate`), | ||
821 | |||
822 | new Hotkey(',', e => e, undefined, $localize`Navigate in the video to the previous frame`), | ||
823 | new Hotkey('.', e => e, undefined, $localize`Navigate in the video to the next frame`) | ||
824 | ]) | ||
825 | } | ||
826 | |||
817 | if (this.isUserLoggedIn()) { | 827 | if (this.isUserLoggedIn()) { |
818 | this.hotkeys = this.hotkeys.concat([ | 828 | this.hotkeys = this.hotkeys.concat([ |
819 | new Hotkey('shift+s', () => { | 829 | new Hotkey('shift+s', () => { |
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 413ec2b80..2781850b9 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -123,7 +123,7 @@ export class PeertubePlayerManager { | |||
123 | self.addContextMenu(videojsOptionsBuilder, player, options.common) | 123 | self.addContextMenu(videojsOptionsBuilder, player, options.common) |
124 | 124 | ||
125 | if (isMobile()) player.peertubeMobile() | 125 | if (isMobile()) player.peertubeMobile() |
126 | if (options.common.enableHotkeys === true) player.peerTubeHotkeysPlugin() | 126 | if (options.common.enableHotkeys === true) player.peerTubeHotkeysPlugin({ isLive: options.common.isLive }) |
127 | if (options.common.controlBar === false) player.controlBar.addClass('control-bar-hidden') | 127 | if (options.common.controlBar === false) player.controlBar.addClass('control-bar-hidden') |
128 | 128 | ||
129 | player.bezels() | 129 | player.bezels() |
diff --git a/client/src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts b/client/src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts index ec1e1038b..f5b4b3919 100644 --- a/client/src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts +++ b/client/src/assets/player/shared/hotkeys/peertube-hotkeys-plugin.ts | |||
@@ -4,6 +4,10 @@ type KeyHandler = { accept: (event: KeyboardEvent) => boolean, cb: (e: KeyboardE | |||
4 | 4 | ||
5 | const Plugin = videojs.getPlugin('plugin') | 5 | const Plugin = videojs.getPlugin('plugin') |
6 | 6 | ||
7 | export type HotkeysOptions = { | ||
8 | isLive: boolean | ||
9 | } | ||
10 | |||
7 | class PeerTubeHotkeysPlugin extends Plugin { | 11 | class PeerTubeHotkeysPlugin extends Plugin { |
8 | private static readonly VOLUME_STEP = 0.1 | 12 | private static readonly VOLUME_STEP = 0.1 |
9 | private static readonly SEEK_STEP = 5 | 13 | private static readonly SEEK_STEP = 5 |
@@ -12,9 +16,13 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
12 | 16 | ||
13 | private readonly handlers: KeyHandler[] | 17 | private readonly handlers: KeyHandler[] |
14 | 18 | ||
15 | constructor (player: videojs.Player, options: videojs.PlayerOptions) { | 19 | private readonly isLive: boolean |
20 | |||
21 | constructor (player: videojs.Player, options: videojs.PlayerOptions & HotkeysOptions) { | ||
16 | super(player, options) | 22 | super(player, options) |
17 | 23 | ||
24 | this.isLive = options.isLive | ||
25 | |||
18 | this.handlers = this.buildHandlers() | 26 | this.handlers = this.buildHandlers() |
19 | 27 | ||
20 | this.handleKeyFunction = (event: KeyboardEvent) => this.onKeyDown(event) | 28 | this.handleKeyFunction = (event: KeyboardEvent) => this.onKeyDown(event) |
@@ -68,28 +76,6 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
68 | } | 76 | } |
69 | }, | 77 | }, |
70 | 78 | ||
71 | // Rewind | ||
72 | { | ||
73 | accept: e => this.isNaked(e, 'ArrowLeft') || this.isNaked(e, 'MediaRewind'), | ||
74 | cb: e => { | ||
75 | e.preventDefault() | ||
76 | |||
77 | const target = Math.max(0, this.player.currentTime() - PeerTubeHotkeysPlugin.SEEK_STEP) | ||
78 | this.player.currentTime(target) | ||
79 | } | ||
80 | }, | ||
81 | |||
82 | // Forward | ||
83 | { | ||
84 | accept: e => this.isNaked(e, 'ArrowRight') || this.isNaked(e, 'MediaForward'), | ||
85 | cb: e => { | ||
86 | e.preventDefault() | ||
87 | |||
88 | const target = Math.min(this.player.duration(), this.player.currentTime() + PeerTubeHotkeysPlugin.SEEK_STEP) | ||
89 | this.player.currentTime(target) | ||
90 | } | ||
91 | }, | ||
92 | |||
93 | // Fullscreen | 79 | // Fullscreen |
94 | { | 80 | { |
95 | // f key or Ctrl + Enter | 81 | // f key or Ctrl + Enter |
@@ -116,6 +102,8 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
116 | { | 102 | { |
117 | accept: e => e.key === '>', | 103 | accept: e => e.key === '>', |
118 | cb: () => { | 104 | cb: () => { |
105 | if (this.isLive) return | ||
106 | |||
119 | const target = Math.min(this.player.playbackRate() + 0.1, 5) | 107 | const target = Math.min(this.player.playbackRate() + 0.1, 5) |
120 | 108 | ||
121 | this.player.playbackRate(parseFloat(target.toFixed(2))) | 109 | this.player.playbackRate(parseFloat(target.toFixed(2))) |
@@ -126,6 +114,8 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
126 | { | 114 | { |
127 | accept: e => e.key === '<', | 115 | accept: e => e.key === '<', |
128 | cb: () => { | 116 | cb: () => { |
117 | if (this.isLive) return | ||
118 | |||
129 | const target = Math.max(this.player.playbackRate() - 0.1, 0.10) | 119 | const target = Math.max(this.player.playbackRate() - 0.1, 0.10) |
130 | 120 | ||
131 | this.player.playbackRate(parseFloat(target.toFixed(2))) | 121 | this.player.playbackRate(parseFloat(target.toFixed(2))) |
@@ -136,6 +126,8 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
136 | { | 126 | { |
137 | accept: e => e.key === ',', | 127 | accept: e => e.key === ',', |
138 | cb: () => { | 128 | cb: () => { |
129 | if (this.isLive) return | ||
130 | |||
139 | this.player.pause() | 131 | this.player.pause() |
140 | 132 | ||
141 | // Calculate movement distance (assuming 30 fps) | 133 | // Calculate movement distance (assuming 30 fps) |
@@ -148,6 +140,8 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
148 | { | 140 | { |
149 | accept: e => e.key === '.', | 141 | accept: e => e.key === '.', |
150 | cb: () => { | 142 | cb: () => { |
143 | if (this.isLive) return | ||
144 | |||
151 | this.player.pause() | 145 | this.player.pause() |
152 | 146 | ||
153 | // Calculate movement distance (assuming 30 fps) | 147 | // Calculate movement distance (assuming 30 fps) |
@@ -157,11 +151,47 @@ class PeerTubeHotkeysPlugin extends Plugin { | |||
157 | } | 151 | } |
158 | ] | 152 | ] |
159 | 153 | ||
154 | if (this.isLive) return handlers | ||
155 | |||
156 | return handlers.concat(this.buildVODHandlers()) | ||
157 | } | ||
158 | |||
159 | private buildVODHandlers () { | ||
160 | const handlers: KeyHandler[] = [ | ||
161 | // Rewind | ||
162 | { | ||
163 | accept: e => this.isNaked(e, 'ArrowLeft') || this.isNaked(e, 'MediaRewind'), | ||
164 | cb: e => { | ||
165 | if (this.isLive) return | ||
166 | |||
167 | e.preventDefault() | ||
168 | |||
169 | const target = Math.max(0, this.player.currentTime() - PeerTubeHotkeysPlugin.SEEK_STEP) | ||
170 | this.player.currentTime(target) | ||
171 | } | ||
172 | }, | ||
173 | |||
174 | // Forward | ||
175 | { | ||
176 | accept: e => this.isNaked(e, 'ArrowRight') || this.isNaked(e, 'MediaForward'), | ||
177 | cb: e => { | ||
178 | if (this.isLive) return | ||
179 | |||
180 | e.preventDefault() | ||
181 | |||
182 | const target = Math.min(this.player.duration(), this.player.currentTime() + PeerTubeHotkeysPlugin.SEEK_STEP) | ||
183 | this.player.currentTime(target) | ||
184 | } | ||
185 | } | ||
186 | ] | ||
187 | |||
160 | // 0-9 key handlers | 188 | // 0-9 key handlers |
161 | for (let i = 0; i < 10; i++) { | 189 | for (let i = 0; i < 10; i++) { |
162 | handlers.push({ | 190 | handlers.push({ |
163 | accept: e => this.isNakedOrShift(e, i + ''), | 191 | accept: e => this.isNakedOrShift(e, i + ''), |
164 | cb: e => { | 192 | cb: e => { |
193 | if (this.isLive) return | ||
194 | |||
165 | e.preventDefault() | 195 | e.preventDefault() |
166 | 196 | ||
167 | this.player.currentTime(this.player.duration() * i * 0.1) | 197 | this.player.currentTime(this.player.duration() * i * 0.1) |
diff --git a/client/src/assets/player/types/peertube-videojs-typings.ts b/client/src/assets/player/types/peertube-videojs-typings.ts index c60154f3b..5674f78cb 100644 --- a/client/src/assets/player/types/peertube-videojs-typings.ts +++ b/client/src/assets/player/types/peertube-videojs-typings.ts | |||
@@ -3,6 +3,7 @@ import videojs from 'video.js' | |||
3 | import { Engine } from '@peertube/p2p-media-loader-hlsjs' | 3 | import { Engine } from '@peertube/p2p-media-loader-hlsjs' |
4 | import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@shared/models' | 4 | import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@shared/models' |
5 | import { PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin' | 5 | import { PeerTubeDockPluginOptions } from '../shared/dock/peertube-dock-plugin' |
6 | import { HotkeysOptions } from '../shared/hotkeys/peertube-hotkeys-plugin' | ||
6 | import { Html5Hlsjs } from '../shared/p2p-media-loader/hls-plugin' | 7 | import { Html5Hlsjs } from '../shared/p2p-media-loader/hls-plugin' |
7 | import { P2pMediaLoaderPlugin } from '../shared/p2p-media-loader/p2p-media-loader-plugin' | 8 | import { P2pMediaLoaderPlugin } from '../shared/p2p-media-loader/p2p-media-loader-plugin' |
8 | import { RedundancyUrlManager } from '../shared/p2p-media-loader/redundancy-url-manager' | 9 | import { RedundancyUrlManager } from '../shared/p2p-media-loader/redundancy-url-manager' |
@@ -44,7 +45,7 @@ declare module 'video.js' { | |||
44 | 45 | ||
45 | bezels (): void | 46 | bezels (): void |
46 | peertubeMobile (): void | 47 | peertubeMobile (): void |
47 | peerTubeHotkeysPlugin (): void | 48 | peerTubeHotkeysPlugin (options?: HotkeysOptions): void |
48 | 49 | ||
49 | stats (options?: StatsCardOptions): StatsForNerdsPlugin | 50 | stats (options?: StatsCardOptions): StatsForNerdsPlugin |
50 | 51 | ||