aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-06-11 15:59:10 +0200
committerChocobozzz <me@florianbigard.com>2019-06-11 16:05:03 +0200
commit5efab5467cacb4cce584e2d36e4133a701d8c983 (patch)
treef0f2289e2e88df598404b115c719ac05f3e2e170
parent0ba5f5baade94a051710d9d9d408ac8083c14ac6 (diff)
downloadPeerTube-5efab5467cacb4cce584e2d36e4133a701d8c983.tar.gz
PeerTube-5efab5467cacb4cce584e2d36e4133a701d8c983.tar.zst
PeerTube-5efab5467cacb4cce584e2d36e4133a701d8c983.zip
Add more embed parameters
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts24
-rw-r--r--client/src/assets/player/peertube-player-manager.ts21
-rw-r--r--client/src/sass/include/_variables.scss4
-rw-r--r--client/src/sass/player/_player-variables.scss8
-rw-r--r--client/src/sass/player/context-menu.scss4
-rw-r--r--client/src/sass/player/peertube-skin.scss12
-rw-r--r--client/src/sass/player/settings-menu.scss2
-rw-r--r--client/src/standalone/videos/embed-api.ts130
-rw-r--r--client/src/standalone/videos/embed.html2
-rw-r--r--client/src/standalone/videos/embed.ts205
-rw-r--r--server/tests/api/check-params/users.ts3
-rw-r--r--server/tests/api/users/users-verification.ts3
12 files changed, 240 insertions, 178 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 2d13f1b58..cf9dc8f9c 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -20,6 +20,7 @@ import { environment } from '../../../environments/environment'
20import { VideoCaptionService } from '@app/shared/video-caption' 20import { VideoCaptionService } from '@app/shared/video-caption'
21import { MarkdownService } from '@app/shared/renderer' 21import { MarkdownService } from '@app/shared/renderer'
22import { 22import {
23 CustomizationOptions,
23 P2PMediaLoaderOptions, 24 P2PMediaLoaderOptions,
24 PeertubePlayerManager, 25 PeertubePlayerManager,
25 PeertubePlayerManagerOptions, 26 PeertubePlayerManagerOptions,
@@ -28,7 +29,7 @@ import {
28import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' 29import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
29import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' 30import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
30import { Video } from '@app/shared/video/video.model' 31import { Video } from '@app/shared/video/video.model'
31import { isWebRTCDisabled } from '../../../assets/player/utils' 32import { isWebRTCDisabled, timeToInt } from '../../../assets/player/utils'
32import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component' 33import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component'
33 34
34@Component({ 35@Component({
@@ -249,8 +250,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
249 const urlOptions = { 250 const urlOptions = {
250 startTime: queryParams.start, 251 startTime: queryParams.start,
251 stopTime: queryParams.stop, 252 stopTime: queryParams.stop,
253
254 muted: queryParams.muted,
255 loop: queryParams.loop,
252 subtitle: queryParams.subtitle, 256 subtitle: queryParams.subtitle,
253 playerMode: queryParams.mode 257
258 playerMode: queryParams.mode,
259 peertubeLink: false
254 } 260 }
255 261
256 this.onVideoFetched(video, captionsResult.data, urlOptions) 262 this.onVideoFetched(video, captionsResult.data, urlOptions)
@@ -327,7 +333,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
327 private async onVideoFetched ( 333 private async onVideoFetched (
328 video: VideoDetails, 334 video: VideoDetails,
329 videoCaptions: VideoCaption[], 335 videoCaptions: VideoCaption[],
330 urlOptions: { startTime?: number, stopTime?: number, subtitle?: string, playerMode?: string } 336 urlOptions: CustomizationOptions & { playerMode: PlayerMode }
331 ) { 337 ) {
332 this.video = video 338 this.video = video
333 339
@@ -339,7 +345,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
339 345
340 this.videoWatchPlaylist.updatePlaylistIndex(video) 346 this.videoWatchPlaylist.updatePlaylistIndex(video)
341 347
342 let startTime = urlOptions.startTime || (this.video.userHistory ? this.video.userHistory.currentTime : 0) 348 let startTime = timeToInt(urlOptions.startTime) || (this.video.userHistory ? this.video.userHistory.currentTime : 0)
343 // If we are at the end of the video, reset the timer 349 // If we are at the end of the video, reset the timer
344 if (this.video.duration - startTime <= 1) startTime = 0 350 if (this.video.duration - startTime <= 1) startTime = 0
345 351
@@ -378,12 +384,18 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
378 enableHotkeys: true, 384 enableHotkeys: true,
379 inactivityTimeout: 2500, 385 inactivityTimeout: 2500,
380 poster: this.video.previewUrl, 386 poster: this.video.previewUrl,
387
381 startTime, 388 startTime,
382 stopTime: urlOptions.stopTime, 389 stopTime: urlOptions.stopTime,
390 controls: urlOptions.controls,
391 muted: urlOptions.muted,
392 loop: urlOptions.loop,
393 subtitle: urlOptions.subtitle,
394
395 peertubeLink: urlOptions.peertubeLink,
383 396
384 theaterMode: true, 397 theaterMode: true,
385 captions: videoCaptions.length !== 0, 398 captions: videoCaptions.length !== 0,
386 peertubeLink: false,
387 399
388 videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE 400 videoViewUrl: this.video.privacy.id !== VideoPrivacy.PRIVATE
389 ? this.videoService.getVideoViewUrl(this.video.uuid) 401 ? this.videoService.getVideoViewUrl(this.video.uuid)
@@ -392,8 +404,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
392 404
393 language: this.localeId, 405 language: this.localeId,
394 406
395 subtitle: urlOptions.subtitle,
396
397 userWatching: this.user && this.user.videosHistoryEnabled === true ? { 407 userWatching: this.user && this.user.videosHistoryEnabled === true ? {
398 url: this.videoService.getUserWatchingVideoUrl(this.video.uuid), 408 url: this.videoService.getUserWatchingVideoUrl(this.video.uuid),
399 authorizationHeader: this.authService.getRequestHeaderValue() 409 authorizationHeader: this.authService.getRequestHeaderValue()
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts
index 31cbc7dfd..8f6237326 100644
--- a/client/src/assets/player/peertube-player-manager.ts
+++ b/client/src/assets/player/peertube-player-manager.ts
@@ -39,7 +39,19 @@ export type P2PMediaLoaderOptions = {
39 videoFiles: VideoFile[] 39 videoFiles: VideoFile[]
40} 40}
41 41
42export type CommonOptions = { 42export interface CustomizationOptions {
43 startTime: number | string
44 stopTime: number | string
45
46 controls?: boolean
47 muted?: boolean
48 loop?: boolean
49 subtitle?: string
50
51 peertubeLink: boolean
52}
53
54export interface CommonOptions extends CustomizationOptions {
43 playerElement: HTMLVideoElement 55 playerElement: HTMLVideoElement
44 onPlayerElementChange: (element: HTMLVideoElement) => void 56 onPlayerElementChange: (element: HTMLVideoElement) => void
45 57
@@ -48,21 +60,14 @@ export type CommonOptions = {
48 enableHotkeys: boolean 60 enableHotkeys: boolean
49 inactivityTimeout: number 61 inactivityTimeout: number
50 poster: string 62 poster: string
51 startTime: number | string
52 stopTime: number | string
53 63
54 theaterMode: boolean 64 theaterMode: boolean
55 captions: boolean 65 captions: boolean
56 peertubeLink: boolean
57 66
58 videoViewUrl: string 67 videoViewUrl: string
59 embedUrl: string 68 embedUrl: string
60 69
61 language?: string 70 language?: string
62 controls?: boolean
63 muted?: boolean
64 loop?: boolean
65 subtitle?: string
66 71
67 videoCaptions: VideoJSCaption[] 72 videoCaptions: VideoJSCaption[]
68 73
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss
index c7b205b11..aafeda257 100644
--- a/client/src/sass/include/_variables.scss
+++ b/client/src/sass/include/_variables.scss
@@ -71,7 +71,9 @@ $variables: (
71 --menuForegroundColor: var(--menuForegroundColor), 71 --menuForegroundColor: var(--menuForegroundColor),
72 --submenuColor: var(--submenuColor), 72 --submenuColor: var(--submenuColor),
73 --inputColor: var(--inputColor), 73 --inputColor: var(--inputColor),
74 --inputPlaceholderColor: var(--inputPlaceholderColor) 74 --inputPlaceholderColor: var(--inputPlaceholderColor),
75 --embedForegroundColor: var(--embedForegroundColor),
76 --embedBigPlayBackgroundColor: var(--embedBigPlayBackgroundColor)
75); 77);
76 78
77/*** theme helper ***/ 79/*** theme helper ***/
diff --git a/client/src/sass/player/_player-variables.scss b/client/src/sass/player/_player-variables.scss
index 110129790..4e9e8736c 100644
--- a/client/src/sass/player/_player-variables.scss
+++ b/client/src/sass/player/_player-variables.scss
@@ -10,4 +10,10 @@ $slider-bg-color: lighten($primary-background-color, 33%);
10 10
11$progress-margin: 10px; 11$progress-margin: 10px;
12 12
13$assets-path: '../../assets/' !default; \ No newline at end of file 13$assets-path: '../../assets/' !default;
14
15body {
16 --embedForegroundColor: #{$primary-foreground-color};
17
18 --embedBigPlayBackgroundColor: #{$primary-background-color};
19}
diff --git a/client/src/sass/player/context-menu.scss b/client/src/sass/player/context-menu.scss
index 71d6d1b1d..eeab0ccdf 100644
--- a/client/src/sass/player/context-menu.scss
+++ b/client/src/sass/player/context-menu.scss
@@ -14,7 +14,7 @@ $context-menu-width: 350px;
14 14
15 .vjs-menu-content { 15 .vjs-menu-content {
16 opacity: $primary-foreground-opacity; 16 opacity: $primary-foreground-opacity;
17 color: $primary-foreground-color; 17 color: var(--embedForegroundCsolor);
18 font-size: $font-size !important; 18 font-size: $font-size !important;
19 font-weight: $font-semibold; 19 font-weight: $font-semibold;
20 } 20 }
@@ -30,4 +30,4 @@ $context-menu-width: 350px;
30 background-color: rgba(255, 255, 255, 0.2); 30 background-color: rgba(255, 255, 255, 0.2);
31 } 31 }
32 } 32 }
33} \ No newline at end of file 33}
diff --git a/client/src/sass/player/peertube-skin.scss b/client/src/sass/player/peertube-skin.scss
index e63a2875c..996024ade 100644
--- a/client/src/sass/player/peertube-skin.scss
+++ b/client/src/sass/player/peertube-skin.scss
@@ -10,9 +10,8 @@
10} 10}
11 11
12.video-js.vjs-peertube-skin { 12.video-js.vjs-peertube-skin {
13
14 font-size: $font-size; 13 font-size: $font-size;
15 color: $primary-foreground-color; 14 color: var(--embedForegroundColor);
16 15
17 .vjs-dock-text { 16 .vjs-dock-text {
18 padding-right: 10px; 17 padding-right: 10px;
@@ -114,7 +113,7 @@
114 .vjs-control-bar, 113 .vjs-control-bar,
115 .vjs-big-play-button, 114 .vjs-big-play-button,
116 .vjs-settings-dialog { 115 .vjs-settings-dialog {
117 background-color: rgba($primary-background-color, 0.5); 116 background-color: var(--embedBigPlayBackgroundColor);
118 } 117 }
119 118
120 .vjs-poster { 119 .vjs-poster {
@@ -139,7 +138,8 @@
139 .vjs-theater-control, 138 .vjs-theater-control,
140 .vjs-settings 139 .vjs-settings
141 { 140 {
142 color: $primary-foreground-color !important; 141 color: var(--embedForegroundColor) !important;
142
143 opacity: $primary-foreground-opacity; 143 opacity: $primary-foreground-opacity;
144 transition: opacity .1s; 144 transition: opacity .1s;
145 145
@@ -151,7 +151,7 @@
151 .vjs-current-time, 151 .vjs-current-time,
152 .vjs-duration, 152 .vjs-duration,
153 .vjs-peertube { 153 .vjs-peertube {
154 color: $primary-foreground-color; 154 color: var(--embedForegroundColor);
155 opacity: $primary-foreground-opacity; 155 opacity: $primary-foreground-opacity;
156 } 156 }
157 157
@@ -171,7 +171,7 @@
171 transition: none; 171 transition: none;
172 172
173 .vjs-play-progress { 173 .vjs-play-progress {
174 background: $primary-foreground-color; 174 background: var(--embedForegroundColor);
175 175
176 // Not display the circle if the progress is not hovered 176 // Not display the circle if the progress is not hovered
177 &::before { 177 &::before {
diff --git a/client/src/sass/player/settings-menu.scss b/client/src/sass/player/settings-menu.scss
index 61965c85e..83407b445 100644
--- a/client/src/sass/player/settings-menu.scss
+++ b/client/src/sass/player/settings-menu.scss
@@ -38,7 +38,7 @@ $setting-transition-easing: ease-out;
38 position: absolute; 38 position: absolute;
39 right: .5em; 39 right: .5em;
40 bottom: 3.5em; 40 bottom: 3.5em;
41 color: $primary-foreground-color; 41 color: var(--embedForegroundColor);
42 opacity: $primary-foreground-opacity; 42 opacity: $primary-foreground-opacity;
43 margin: 0 auto; 43 margin: 0 auto;
44 font-size: $font-size !important; 44 font-size: $font-size !important;
diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts
new file mode 100644
index 000000000..169e371da
--- /dev/null
+++ b/client/src/standalone/videos/embed-api.ts
@@ -0,0 +1,130 @@
1import './embed.scss'
2
3import * as Channel from 'jschannel'
4import { PeerTubeResolution } from '../player/definitions'
5import { PeerTubeEmbed } from './embed'
6
7/**
8 * Embed API exposes control of the embed player to the outside world via
9 * JSChannels and window.postMessage
10 */
11export class PeerTubeEmbedApi {
12 private channel: Channel.MessagingChannel
13 private isReady = false
14 private resolutions: PeerTubeResolution[] = null
15
16 constructor (private embed: PeerTubeEmbed) {
17 }
18
19 initialize () {
20 this.constructChannel()
21 this.setupStateTracking()
22
23 // We're ready!
24
25 this.notifyReady()
26 }
27
28 private get element () {
29 return this.embed.videoElement
30 }
31
32 private constructChannel () {
33 const channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope })
34
35 channel.bind('play', (txn, params) => this.embed.player.play())
36 channel.bind('pause', (txn, params) => this.embed.player.pause())
37 channel.bind('seek', (txn, time) => this.embed.player.currentTime(time))
38 channel.bind('setVolume', (txn, value) => this.embed.player.volume(value))
39 channel.bind('getVolume', (txn, value) => this.embed.player.volume())
40 channel.bind('isReady', (txn, params) => this.isReady)
41 channel.bind('setResolution', (txn, resolutionId) => this.setResolution(resolutionId))
42 channel.bind('getResolutions', (txn, params) => this.resolutions)
43 channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate))
44 channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate())
45 channel.bind('getPlaybackRates', (txn, params) => this.embed.playerOptions.playbackRates)
46
47 this.channel = channel
48 }
49
50 private setResolution (resolutionId: number) {
51 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionForbidden()) return
52
53 // Auto resolution
54 if (resolutionId === -1) {
55 this.embed.player.webtorrent().enableAutoResolution()
56 return
57 }
58
59 this.embed.player.webtorrent().disableAutoResolution()
60 this.embed.player.webtorrent().updateResolution(resolutionId)
61 }
62
63 /**
64 * Let the host know that we're ready to go!
65 */
66 private notifyReady () {
67 this.isReady = true
68 this.channel.notify({ method: 'ready', params: true })
69 }
70
71 private setupStateTracking () {
72 let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted'
73
74 setInterval(() => {
75 const position = this.element.currentTime
76 const volume = this.element.volume
77
78 this.channel.notify({
79 method: 'playbackStatusUpdate',
80 params: {
81 position,
82 volume,
83 playbackState: currentState
84 }
85 })
86 }, 500)
87
88 this.element.addEventListener('play', ev => {
89 currentState = 'playing'
90 this.channel.notify({ method: 'playbackStatusChange', params: 'playing' })
91 })
92
93 this.element.addEventListener('pause', ev => {
94 currentState = 'paused'
95 this.channel.notify({ method: 'playbackStatusChange', params: 'paused' })
96 })
97
98 // PeerTube specific capabilities
99
100 if (this.embed.player.webtorrent) {
101 this.embed.player.webtorrent().on('autoResolutionUpdate', () => this.loadWebTorrentResolutions())
102 this.embed.player.webtorrent().on('videoFileUpdate', () => this.loadWebTorrentResolutions())
103 }
104 }
105
106 private loadWebTorrentResolutions () {
107 const resolutions = []
108 const currentResolutionId = this.embed.player.webtorrent().getCurrentResolutionId()
109
110 for (const videoFile of this.embed.player.webtorrent().videoFiles) {
111 let label = videoFile.resolution.label
112 if (videoFile.fps && videoFile.fps >= 50) {
113 label += videoFile.fps
114 }
115
116 resolutions.push({
117 id: videoFile.resolution.id,
118 label,
119 src: videoFile.magnetUri,
120 active: videoFile.resolution.id === currentResolutionId
121 })
122 }
123
124 this.resolutions = resolutions
125 this.channel.notify({
126 method: 'resolutionUpdate',
127 params: this.resolutions
128 })
129 }
130}
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html
index c3b6e08ca..5a15bf552 100644
--- a/client/src/standalone/videos/embed.html
+++ b/client/src/standalone/videos/embed.html
@@ -11,7 +11,7 @@
11 <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" /> 11 <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" />
12 </head> 12 </head>
13 13
14 <body> 14 <body id="custom-css">
15 15
16 <div id="error-block"> 16 <div id="error-block">
17 <h1 id="error-title"></h1> 17 <h1 id="error-title"></h1>
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index 707f04253..cfe8e94b1 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -1,9 +1,6 @@
1import './embed.scss' 1import './embed.scss'
2 2
3import * as Channel from 'jschannel'
4
5import { peertubeTranslate, ResultList, ServerConfig, VideoDetails } from '../../../../shared' 3import { peertubeTranslate, ResultList, ServerConfig, VideoDetails } from '../../../../shared'
6import { PeerTubeResolution } from '../player/definitions'
7import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 4import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
8import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' 5import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
9import { 6import {
@@ -13,133 +10,9 @@ import {
13 PlayerMode 10 PlayerMode
14} from '../../assets/player/peertube-player-manager' 11} from '../../assets/player/peertube-player-manager'
15import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' 12import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
13import { PeerTubeEmbedApi } from './embed-api'
16 14
17/** 15export class PeerTubeEmbed {
18 * Embed API exposes control of the embed player to the outside world via
19 * JSChannels and window.postMessage
20 */
21class PeerTubeEmbedApi {
22 private channel: Channel.MessagingChannel
23 private isReady = false
24 private resolutions: PeerTubeResolution[] = null
25
26 constructor (private embed: PeerTubeEmbed) {
27 }
28
29 initialize () {
30 this.constructChannel()
31 this.setupStateTracking()
32
33 // We're ready!
34
35 this.notifyReady()
36 }
37
38 private get element () {
39 return this.embed.videoElement
40 }
41
42 private constructChannel () {
43 const channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope })
44
45 channel.bind('play', (txn, params) => this.embed.player.play())
46 channel.bind('pause', (txn, params) => this.embed.player.pause())
47 channel.bind('seek', (txn, time) => this.embed.player.currentTime(time))
48 channel.bind('setVolume', (txn, value) => this.embed.player.volume(value))
49 channel.bind('getVolume', (txn, value) => this.embed.player.volume())
50 channel.bind('isReady', (txn, params) => this.isReady)
51 channel.bind('setResolution', (txn, resolutionId) => this.setResolution(resolutionId))
52 channel.bind('getResolutions', (txn, params) => this.resolutions)
53 channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate))
54 channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate())
55 channel.bind('getPlaybackRates', (txn, params) => this.embed.playerOptions.playbackRates)
56
57 this.channel = channel
58 }
59
60 private setResolution (resolutionId: number) {
61 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionForbidden()) return
62
63 // Auto resolution
64 if (resolutionId === -1) {
65 this.embed.player.webtorrent().enableAutoResolution()
66 return
67 }
68
69 this.embed.player.webtorrent().disableAutoResolution()
70 this.embed.player.webtorrent().updateResolution(resolutionId)
71 }
72
73 /**
74 * Let the host know that we're ready to go!
75 */
76 private notifyReady () {
77 this.isReady = true
78 this.channel.notify({ method: 'ready', params: true })
79 }
80
81 private setupStateTracking () {
82 let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted'
83
84 setInterval(() => {
85 const position = this.element.currentTime
86 const volume = this.element.volume
87
88 this.channel.notify({
89 method: 'playbackStatusUpdate',
90 params: {
91 position,
92 volume,
93 playbackState: currentState
94 }
95 })
96 }, 500)
97
98 this.element.addEventListener('play', ev => {
99 currentState = 'playing'
100 this.channel.notify({ method: 'playbackStatusChange', params: 'playing' })
101 })
102
103 this.element.addEventListener('pause', ev => {
104 currentState = 'paused'
105 this.channel.notify({ method: 'playbackStatusChange', params: 'paused' })
106 })
107
108 // PeerTube specific capabilities
109
110 if (this.embed.player.webtorrent) {
111 this.embed.player.webtorrent().on('autoResolutionUpdate', () => this.loadWebTorrentResolutions())
112 this.embed.player.webtorrent().on('videoFileUpdate', () => this.loadWebTorrentResolutions())
113 }
114 }
115
116 private loadWebTorrentResolutions () {
117 const resolutions = []
118 const currentResolutionId = this.embed.player.webtorrent().getCurrentResolutionId()
119
120 for (const videoFile of this.embed.player.webtorrent().videoFiles) {
121 let label = videoFile.resolution.label
122 if (videoFile.fps && videoFile.fps >= 50) {
123 label += videoFile.fps
124 }
125
126 resolutions.push({
127 id: videoFile.resolution.id,
128 label,
129 src: videoFile.magnetUri,
130 active: videoFile.resolution.id === currentResolutionId
131 })
132 }
133
134 this.resolutions = resolutions
135 this.channel.notify({
136 method: 'resolutionUpdate',
137 params: this.resolutions
138 })
139 }
140}
141
142class PeerTubeEmbed {
143 videoElement: HTMLVideoElement 16 videoElement: HTMLVideoElement
144 player: any 17 player: any
145 playerOptions: any 18 playerOptions: any
@@ -152,6 +25,12 @@ class PeerTubeEmbed {
152 enableApi = false 25 enableApi = false
153 startTime: number | string = 0 26 startTime: number | string = 0
154 stopTime: number | string 27 stopTime: number | string
28
29 title: boolean
30 warningTitle: boolean
31 bigPlayBackgroundColor: string
32 foregroundColor: string
33
155 mode: PlayerMode 34 mode: PlayerMode
156 scope = 'peertube' 35 scope = 'peertube'
157 36
@@ -245,13 +124,18 @@ class PeerTubeEmbed {
245 this.controls = this.getParamToggle(params, 'controls', true) 124 this.controls = this.getParamToggle(params, 'controls', true)
246 this.muted = this.getParamToggle(params, 'muted', false) 125 this.muted = this.getParamToggle(params, 'muted', false)
247 this.loop = this.getParamToggle(params, 'loop', false) 126 this.loop = this.getParamToggle(params, 'loop', false)
127 this.title = this.getParamToggle(params, 'title', true)
248 this.enableApi = this.getParamToggle(params, 'api', this.enableApi) 128 this.enableApi = this.getParamToggle(params, 'api', this.enableApi)
129 this.warningTitle = this.getParamToggle(params, 'warningTitle', true)
249 130
250 this.scope = this.getParamString(params, 'scope', this.scope) 131 this.scope = this.getParamString(params, 'scope', this.scope)
251 this.subtitle = this.getParamString(params, 'subtitle') 132 this.subtitle = this.getParamString(params, 'subtitle')
252 this.startTime = this.getParamString(params, 'start') 133 this.startTime = this.getParamString(params, 'start')
253 this.stopTime = this.getParamString(params, 'stop') 134 this.stopTime = this.getParamString(params, 'stop')
254 135
136 this.bigPlayBackgroundColor = this.getParamString(params, 'bigPlayBackgroundColor')
137 this.foregroundColor = this.getParamString(params, 'foregroundColor')
138
255 this.mode = this.getParamString(params, 'mode') === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent' 139 this.mode = this.getParamString(params, 'mode') === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent'
256 } catch (err) { 140 } catch (err) {
257 console.error('Cannot get params from URL.', err) 141 console.error('Cannot get params from URL.', err)
@@ -276,15 +160,7 @@ class PeerTubeEmbed {
276 } 160 }
277 161
278 const videoInfo: VideoDetails = await videoResponse.json() 162 const videoInfo: VideoDetails = await videoResponse.json()
279 let videoCaptions: VideoJSCaption[] = [] 163 const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse)
280 if (captionsResponse.ok) {
281 const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
282 videoCaptions = data.map(c => ({
283 label: peertubeTranslate(c.language.label, serverTranslations),
284 language: c.language.id,
285 src: window.location.origin + c.captionPath
286 }))
287 }
288 164
289 this.loadParams() 165 this.loadParams()
290 166
@@ -337,33 +213,66 @@ class PeerTubeEmbed {
337 } 213 }
338 214
339 this.player = await PeertubePlayerManager.initialize(this.mode, options) 215 this.player = await PeertubePlayerManager.initialize(this.mode, options)
340
341 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) 216 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations))
342 217
343 window[ 'videojsPlayer' ] = this.player 218 window[ 'videojsPlayer' ] = this.player
344 219
220 this.buildCSS()
221
222 await this.buildDock(videoInfo, configResponse)
223
224 this.initializeApi()
225 }
226
227 private handleError (err: Error, translations?: { [ id: string ]: string }) {
228 if (err.message.indexOf('from xs param') !== -1) {
229 this.player.dispose()
230 this.videoElement = null
231 this.displayError('This video is not available because the remote instance is not responding.', translations)
232 return
233 }
234 }
235
236 private async buildDock (videoInfo: VideoDetails, configResponse: Response) {
345 if (this.controls) { 237 if (this.controls) {
238 const title = this.title ? videoInfo.name : undefined
239
346 const config: ServerConfig = await configResponse.json() 240 const config: ServerConfig = await configResponse.json()
347 const description = config.tracker.enabled 241 const description = config.tracker.enabled && this.warningTitle
348 ? '<span class="text">' + this.player.localize('Uses P2P, others may know your IP is downloading this video.') + '</span>' 242 ? '<span class="text">' + this.player.localize('Uses P2P, others may know your IP is downloading this video.') + '</span>'
349 : undefined 243 : undefined
350 244
351 this.player.dock({ 245 this.player.dock({
352 title: videoInfo.name, 246 title,
353 description 247 description
354 }) 248 })
355 } 249 }
250 }
356 251
357 this.initializeApi() 252 private buildCSS () {
253 const body = document.getElementById('custom-css')
254
255 if (this.bigPlayBackgroundColor) {
256 body.style.setProperty('--embedBigPlayBackgroundColor', this.bigPlayBackgroundColor)
257 }
258
259 if (this.foregroundColor) {
260 body.style.setProperty('--embedForegroundColor', this.foregroundColor)
261 }
358 } 262 }
359 263
360 private handleError (err: Error, translations?: { [ id: string ]: string }) { 264 private async buildCaptions (serverTranslations: any, captionsResponse: Response): Promise<VideoJSCaption[]> {
361 if (err.message.indexOf('from xs param') !== -1) { 265 if (captionsResponse.ok) {
362 this.player.dispose() 266 const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
363 this.videoElement = null 267
364 this.displayError('This video is not available because the remote instance is not responding.', translations) 268 return data.map(c => ({
365 return 269 label: peertubeTranslate(c.language.label, serverTranslations),
270 language: c.language.id,
271 src: window.location.origin + c.captionPath
272 }))
366 } 273 }
274
275 return []
367 } 276 }
368} 277}
369 278
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 3268f8c90..2316033a1 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -379,8 +379,7 @@ describe('Test users API validators', function () {
379 it('Should succeed without password change with the correct params', async function () { 379 it('Should succeed without password change with the correct params', async function () {
380 const fields = { 380 const fields = {
381 nsfwPolicy: 'blur', 381 nsfwPolicy: 'blur',
382 autoPlayVideo: false, 382 autoPlayVideo: false
383 email: 'super_email@example.com'
384 } 383 }
385 384
386 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 }) 385 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields, statusCodeExpected: 204 })
diff --git a/server/tests/api/users/users-verification.ts b/server/tests/api/users/users-verification.ts
index b8fa1430b..7cd61f539 100644
--- a/server/tests/api/users/users-verification.ts
+++ b/server/tests/api/users/users-verification.ts
@@ -109,7 +109,8 @@ describe('Test users account verification', function () {
109 await updateMyUser({ 109 await updateMyUser({
110 url: server.url, 110 url: server.url,
111 accessToken: userAccessToken, 111 accessToken: userAccessToken,
112 email: 'updated@example.com' 112 email: 'updated@example.com',
113 currentPassword: user1.password
113 }) 114 })
114 115
115 await waitJobs(server) 116 await waitJobs(server)