diff options
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.component.scss | 5 | ||||
-rw-r--r-- | client/src/app/videos/+video-watch/video-watch.component.ts | 3 | ||||
-rw-r--r-- | client/src/assets/player/images/theater.svg | 13 | ||||
-rw-r--r-- | client/src/assets/player/peertube-player.ts | 9 | ||||
-rw-r--r-- | client/src/assets/player/peertube-videojs-plugin.ts | 30 | ||||
-rw-r--r-- | client/src/assets/player/theater-button.ts | 46 | ||||
-rw-r--r-- | client/src/assets/player/utils.ts | 13 | ||||
-rw-r--r-- | client/src/sass/include/_variables.scss | 2 | ||||
-rw-r--r-- | client/src/sass/player/peertube-skin.scss | 25 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 3 |
10 files changed, 135 insertions, 14 deletions
diff --git a/client/src/app/videos/+video-watch/video-watch.component.scss b/client/src/app/videos/+video-watch/video-watch.component.scss index d1f840937..00e776a69 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.scss +++ b/client/src/app/videos/+video-watch/video-watch.component.scss | |||
@@ -21,6 +21,11 @@ | |||
21 | position: relative !important; | 21 | position: relative !important; |
22 | } | 22 | } |
23 | } | 23 | } |
24 | |||
25 | /deep/ .video-js.vjs-theater-enabled { | ||
26 | width: 100%; | ||
27 | height: calc(100vh - #{$header-height} - #{$theater-bottom-space}); | ||
28 | } | ||
24 | } | 29 | } |
25 | 30 | ||
26 | #video-not-found { | 31 | #video-not-found { |
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 0f4f5ce89..eefa43a73 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts | |||
@@ -368,7 +368,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
368 | enableHotkeys: true, | 368 | enableHotkeys: true, |
369 | peertubeLink: false, | 369 | peertubeLink: false, |
370 | poster: this.video.previewUrl, | 370 | poster: this.video.previewUrl, |
371 | startTime | 371 | startTime, |
372 | theaterMode: true | ||
372 | }) | 373 | }) |
373 | 374 | ||
374 | if (this.videojsLocaleLoaded === false) { | 375 | 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 index 000000000..d7086c214 --- /dev/null +++ b/client/src/assets/player/images/theater.svg | |||
@@ -0,0 +1,13 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <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"> | ||
3 | <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> | ||
4 | <title>theater</title> | ||
5 | <defs></defs> | ||
6 | <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | ||
7 | <g id="Artboard-4" transform="translate(-576.000000, -159.000000)" stroke="#fff" stroke-width="2"> | ||
8 | <g id="33" transform="translate(576.000000, 159.000000)"> | ||
9 | <rect id="Rectangle-433" x="1" y="6" width="22" height="12"></rect> | ||
10 | </g> | ||
11 | </g> | ||
12 | </g> | ||
13 | </svg> | ||
diff --git a/client/src/assets/player/peertube-player.ts b/client/src/assets/player/peertube-player.ts index eb75091de..afc8e0881 100644 --- a/client/src/assets/player/peertube-player.ts +++ b/client/src/assets/player/peertube-player.ts | |||
@@ -10,6 +10,7 @@ import './settings-menu-button' | |||
10 | import './webtorrent-info-button' | 10 | import './webtorrent-info-button' |
11 | import './peertube-videojs-plugin' | 11 | import './peertube-videojs-plugin' |
12 | import './peertube-load-progress-bar' | 12 | import './peertube-load-progress-bar' |
13 | import './theater-button' | ||
13 | import { videojsUntyped } from './peertube-videojs-typings' | 14 | import { videojsUntyped } from './peertube-videojs-typings' |
14 | import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils' | 15 | import { buildVideoEmbed, buildVideoLink, copyToClipboard } from './utils' |
15 | import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n' | 16 | import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n' |
@@ -28,6 +29,7 @@ function getVideojsOptions (options: { | |||
28 | peertubeLink: boolean, | 29 | peertubeLink: boolean, |
29 | poster: string, | 30 | poster: string, |
30 | startTime: number | 31 | startTime: number |
32 | theaterMode: boolean | ||
31 | }) { | 33 | }) { |
32 | const videojsOptions = { | 34 | const videojsOptions = { |
33 | controls: true, | 35 | controls: true, |
@@ -63,6 +65,7 @@ function getVideojsOptions (options: { | |||
63 | 65 | ||
64 | function getControlBarChildren (options: { | 66 | function getControlBarChildren (options: { |
65 | peertubeLink: boolean | 67 | peertubeLink: boolean |
68 | theaterMode: boolean | ||
66 | }) { | 69 | }) { |
67 | const children = { | 70 | const children = { |
68 | 'playToggle': {}, | 71 | 'playToggle': {}, |
@@ -105,6 +108,12 @@ function getControlBarChildren (options: { | |||
105 | }) | 108 | }) |
106 | } | 109 | } |
107 | 110 | ||
111 | if (options.theaterMode === true) { | ||
112 | Object.assign(children, { | ||
113 | 'theaterButton': {} | ||
114 | }) | ||
115 | } | ||
116 | |||
108 | Object.assign(children, { | 117 | Object.assign(children, { |
109 | 'fullscreenToggle': {} | 118 | 'fullscreenToggle': {} |
110 | }) | 119 | }) |
diff --git a/client/src/assets/player/peertube-videojs-plugin.ts b/client/src/assets/player/peertube-videojs-plugin.ts index 47288c842..d3ae7b137 100644 --- a/client/src/assets/player/peertube-videojs-plugin.ts +++ b/client/src/assets/player/peertube-videojs-plugin.ts | |||
@@ -55,6 +55,7 @@ class PeerTubePlugin extends Plugin { | |||
55 | private player: any | 55 | private player: any |
56 | private currentVideoFile: VideoFile | 56 | private currentVideoFile: VideoFile |
57 | private torrent: WebTorrent.Torrent | 57 | private torrent: WebTorrent.Torrent |
58 | private fakeRenderer | ||
58 | private autoResolution = true | 59 | private autoResolution = true |
59 | private isAutoResolutionObservation = false | 60 | private isAutoResolutionObservation = false |
60 | 61 | ||
@@ -123,6 +124,8 @@ class PeerTubePlugin extends Plugin { | |||
123 | 124 | ||
124 | // Don't need to destroy renderer, video player will be destroyed | 125 | // Don't need to destroy renderer, video player will be destroyed |
125 | this.flushVideoFile(this.currentVideoFile, false) | 126 | this.flushVideoFile(this.currentVideoFile, false) |
127 | |||
128 | this.destroyFakeRenderer() | ||
126 | } | 129 | } |
127 | 130 | ||
128 | getCurrentResolutionId () { | 131 | getCurrentResolutionId () { |
@@ -185,7 +188,6 @@ class PeerTubePlugin extends Plugin { | |||
185 | console.log('Adding ' + magnetOrTorrentUrl + '.') | 188 | console.log('Adding ' + magnetOrTorrentUrl + '.') |
186 | 189 | ||
187 | const oldTorrent = this.torrent | 190 | const oldTorrent = this.torrent |
188 | let fakeRenderer | ||
189 | const torrentOptions = { | 191 | const torrentOptions = { |
190 | store: (chunkLength, storeOpts) => new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), { | 192 | store: (chunkLength, storeOpts) => new CacheChunkStore(new PeertubeChunkStore(chunkLength, storeOpts), { |
191 | max: 100 | 193 | max: 100 |
@@ -205,7 +207,7 @@ class PeerTubePlugin extends Plugin { | |||
205 | if (options.delay) { | 207 | if (options.delay) { |
206 | const fakeVideoElem = document.createElement('video') | 208 | const fakeVideoElem = document.createElement('video') |
207 | renderVideo(torrent.files[0], fakeVideoElem, { autoplay: false, controls: false }, (err, renderer) => { | 209 | renderVideo(torrent.files[0], fakeVideoElem, { autoplay: false, controls: false }, (err, renderer) => { |
208 | fakeRenderer = renderer | 210 | this.fakeRenderer = renderer |
209 | 211 | ||
210 | if (err) console.error('Cannot render new torrent in fake video element.', err) | 212 | if (err) console.error('Cannot render new torrent in fake video element.', err) |
211 | 213 | ||
@@ -217,16 +219,7 @@ class PeerTubePlugin extends Plugin { | |||
217 | 219 | ||
218 | // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution) | 220 | // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution) |
219 | this.addTorrentDelay = setTimeout(() => { | 221 | this.addTorrentDelay = setTimeout(() => { |
220 | if (fakeRenderer) { | 222 | this.destroyFakeRenderer() |
221 | if (fakeRenderer.destroy) { | ||
222 | try { | ||
223 | fakeRenderer.destroy() | ||
224 | } catch (err) { | ||
225 | console.log('Cannot destroy correctly fake renderer.', err) | ||
226 | } | ||
227 | } | ||
228 | fakeRenderer = undefined | ||
229 | } | ||
230 | 223 | ||
231 | const paused = this.player.paused() | 224 | const paused = this.player.paused() |
232 | 225 | ||
@@ -567,6 +560,19 @@ class PeerTubePlugin extends Plugin { | |||
567 | return this.videoFiles[Math.floor(this.videoFiles.length / 2)] | 560 | return this.videoFiles[Math.floor(this.videoFiles.length / 2)] |
568 | } | 561 | } |
569 | 562 | ||
563 | private destroyFakeRenderer () { | ||
564 | if (this.fakeRenderer) { | ||
565 | if (this.fakeRenderer.destroy) { | ||
566 | try { | ||
567 | this.fakeRenderer.destroy() | ||
568 | } catch (err) { | ||
569 | console.log('Cannot destroy correctly fake renderer.', err) | ||
570 | } | ||
571 | } | ||
572 | this.fakeRenderer = undefined | ||
573 | } | ||
574 | } | ||
575 | |||
570 | // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657 | 576 | // Thanks: https://github.com/videojs/video.js/issues/4460#issuecomment-312861657 |
571 | private initSmoothProgressBar () { | 577 | private initSmoothProgressBar () { |
572 | const SeekBar = videojsUntyped.getComponent('SeekBar') | 578 | 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 index 000000000..1d330e08f --- /dev/null +++ b/client/src/assets/player/theater-button.ts | |||
@@ -0,0 +1,46 @@ | |||
1 | import { VideoJSComponentInterface, videojsUntyped } from './peertube-videojs-typings' | ||
2 | import { getStoredTheater, saveTheaterInStore } from './utils' | ||
3 | |||
4 | const Button: VideoJSComponentInterface = videojsUntyped.getComponent('Button') | ||
5 | class TheaterButton extends Button { | ||
6 | |||
7 | private static readonly THEATER_MODE_CLASS = 'vjs-theater-enabled' | ||
8 | |||
9 | constructor (player, options) { | ||
10 | super(player, options) | ||
11 | |||
12 | const enabled = getStoredTheater() | ||
13 | if (enabled === true) { | ||
14 | this.player_.addClass(TheaterButton.THEATER_MODE_CLASS) | ||
15 | this.handleTheaterChange() | ||
16 | } | ||
17 | } | ||
18 | |||
19 | buildCSSClass () { | ||
20 | return `vjs-theater-control ${super.buildCSSClass()}` | ||
21 | } | ||
22 | |||
23 | handleTheaterChange () { | ||
24 | if (this.isTheaterEnabled()) { | ||
25 | this.controlText('Normal mode') | ||
26 | } else { | ||
27 | this.controlText('Theater mode') | ||
28 | } | ||
29 | |||
30 | saveTheaterInStore(this.isTheaterEnabled()) | ||
31 | } | ||
32 | |||
33 | handleClick () { | ||
34 | this.player_.toggleClass(TheaterButton.THEATER_MODE_CLASS) | ||
35 | |||
36 | this.handleTheaterChange() | ||
37 | } | ||
38 | |||
39 | private isTheaterEnabled () { | ||
40 | return this.player_.hasClass(TheaterButton.THEATER_MODE_CLASS) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | TheaterButton.prototype.controlText_ = 'Theater mode' | ||
45 | |||
46 | TheaterButton.registerComponent('TheaterButton', TheaterButton) | ||
diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts index 4eaf53720..b7cd40aa2 100644 --- a/client/src/assets/player/utils.ts +++ b/client/src/assets/player/utils.ts | |||
@@ -51,6 +51,13 @@ function getAverageBandwidth () { | |||
51 | return undefined | 51 | return undefined |
52 | } | 52 | } |
53 | 53 | ||
54 | function getStoredTheater () { | ||
55 | const value = getLocalStorage('theater-enabled') | ||
56 | if (value !== null && value !== undefined) return value === 'true' | ||
57 | |||
58 | return undefined | ||
59 | } | ||
60 | |||
54 | function saveVolumeInStore (value: number) { | 61 | function saveVolumeInStore (value: number) { |
55 | return setLocalStorage('volume', value.toString()) | 62 | return setLocalStorage('volume', value.toString()) |
56 | } | 63 | } |
@@ -59,6 +66,10 @@ function saveMuteInStore (value: boolean) { | |||
59 | return setLocalStorage('mute', value.toString()) | 66 | return setLocalStorage('mute', value.toString()) |
60 | } | 67 | } |
61 | 68 | ||
69 | function saveTheaterInStore (enabled: boolean) { | ||
70 | return setLocalStorage('theater-enabled', enabled.toString()) | ||
71 | } | ||
72 | |||
62 | function saveAverageBandwidth (value: number) { | 73 | function saveAverageBandwidth (value: number) { |
63 | return setLocalStorage('average-bandwidth', value.toString()) | 74 | return setLocalStorage('average-bandwidth', value.toString()) |
64 | } | 75 | } |
@@ -133,6 +144,8 @@ export { | |||
133 | videoFileMaxByResolution, | 144 | videoFileMaxByResolution, |
134 | videoFileMinByResolution, | 145 | videoFileMinByResolution, |
135 | copyToClipboard, | 146 | copyToClipboard, |
147 | getStoredTheater, | ||
148 | saveTheaterInStore, | ||
136 | isMobile, | 149 | isMobile, |
137 | bytes | 150 | bytes |
138 | } | 151 | } |
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index 95b4b166e..092f8ed24 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss | |||
@@ -31,3 +31,5 @@ $footer-border-color: $header-border-color; | |||
31 | 31 | ||
32 | $video-thumbnail-height: 110px; | 32 | $video-thumbnail-height: 110px; |
33 | $video-thumbnail-width: 200px; | 33 | $video-thumbnail-width: 200px; |
34 | |||
35 | $theater-bottom-space: 85px; | ||
diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss index de6501962..e60a854d4 100644 --- a/client/src/sass/player/peertube-skin.scss +++ b/client/src/sass/player/peertube-skin.scss | |||
@@ -135,6 +135,7 @@ | |||
135 | .vjs-resolution-control, | 135 | .vjs-resolution-control, |
136 | .vjs-fullscreen-control, | 136 | .vjs-fullscreen-control, |
137 | .vjs-peertube-link, | 137 | .vjs-peertube-link, |
138 | .vjs-theater-control, | ||
138 | .vjs-settings | 139 | .vjs-settings |
139 | { | 140 | { |
140 | color: $primary-foreground-color !important; | 141 | color: $primary-foreground-color !important; |
@@ -394,6 +395,26 @@ | |||
394 | padding: 0 5px; | 395 | padding: 0 5px; |
395 | } | 396 | } |
396 | 397 | ||
398 | .vjs-theater-control { | ||
399 | @include disable-outline; | ||
400 | |||
401 | width: 37px; | ||
402 | margin-right: 1px; | ||
403 | |||
404 | .vjs-icon-placeholder { | ||
405 | display: inline-block; | ||
406 | width: 22px; | ||
407 | height: 22px; | ||
408 | vertical-align: middle; | ||
409 | background: url('#{$assets-path}/player/images/theater.svg') no-repeat; | ||
410 | background-size: contain; | ||
411 | |||
412 | &::before { | ||
413 | content: ''; | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
397 | .vjs-fullscreen-control { | 418 | .vjs-fullscreen-control { |
398 | @include disable-outline; | 419 | @include disable-outline; |
399 | 420 | ||
@@ -440,6 +461,10 @@ | |||
440 | } | 461 | } |
441 | 462 | ||
442 | @media screen and (max-width: 750px) { | 463 | @media screen and (max-width: 750px) { |
464 | .vjs-theater-control { | ||
465 | display: none; | ||
466 | } | ||
467 | |||
443 | .vjs-dock-text { | 468 | .vjs-dock-text { |
444 | font-size: 16px; | 469 | font-size: 16px; |
445 | } | 470 | } |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index e28d964de..bf0eb484e 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -99,7 +99,8 @@ loadLocale(window.location.origin, videojs, navigator.language) | |||
99 | enableHotkeys: true, | 99 | enableHotkeys: true, |
100 | peertubeLink: true, | 100 | peertubeLink: true, |
101 | poster: window.location.origin + videoInfo.previewPath, | 101 | poster: window.location.origin + videoInfo.previewPath, |
102 | startTime | 102 | startTime, |
103 | theaterMode: false | ||
103 | }) | 104 | }) |
104 | videojs(videoContainerId, videojsOptions, function () { | 105 | videojs(videoContainerId, videojsOptions, function () { |
105 | const player = this | 106 | const player = this |