diff options
Diffstat (limited to 'client/src/standalone/videos/embed.ts')
-rw-r--r-- | client/src/standalone/videos/embed.ts | 180 |
1 files changed, 103 insertions, 77 deletions
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 54b8fb543..707f04253 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -1,33 +1,18 @@ | |||
1 | import './embed.scss' | 1 | import './embed.scss' |
2 | 2 | ||
3 | import 'core-js/es6/symbol' | ||
4 | import 'core-js/es6/object' | ||
5 | import 'core-js/es6/function' | ||
6 | import 'core-js/es6/parse-int' | ||
7 | import 'core-js/es6/parse-float' | ||
8 | import 'core-js/es6/number' | ||
9 | import 'core-js/es6/math' | ||
10 | import 'core-js/es6/string' | ||
11 | import 'core-js/es6/date' | ||
12 | import 'core-js/es6/array' | ||
13 | import 'core-js/es6/regexp' | ||
14 | import 'core-js/es6/map' | ||
15 | import 'core-js/es6/weak-map' | ||
16 | import 'core-js/es6/set' | ||
17 | // For google bot that uses Chrome 41 and does not understand fetch | ||
18 | import 'whatwg-fetch' | ||
19 | |||
20 | // FIXME: something weird with our path definition in tsconfig and typings | ||
21 | // @ts-ignore | ||
22 | import * as vjs from 'video.js' | ||
23 | |||
24 | import * as Channel from 'jschannel' | 3 | import * as Channel from 'jschannel' |
25 | 4 | ||
26 | import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared' | 5 | import { peertubeTranslate, ResultList, ServerConfig, VideoDetails } from '../../../../shared' |
27 | import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player' | ||
28 | import { PeerTubeResolution } from '../player/definitions' | 6 | import { PeerTubeResolution } from '../player/definitions' |
29 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | 7 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' |
30 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 8 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' |
9 | import { | ||
10 | P2PMediaLoaderOptions, | ||
11 | PeertubePlayerManager, | ||
12 | PeertubePlayerManagerOptions, | ||
13 | PlayerMode | ||
14 | } from '../../assets/player/peertube-player-manager' | ||
15 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' | ||
31 | 16 | ||
32 | /** | 17 | /** |
33 | * Embed API exposes control of the embed player to the outside world via | 18 | * Embed API exposes control of the embed player to the outside world via |
@@ -55,7 +40,7 @@ class PeerTubeEmbedApi { | |||
55 | } | 40 | } |
56 | 41 | ||
57 | private constructChannel () { | 42 | private constructChannel () { |
58 | let channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope }) | 43 | const channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope }) |
59 | 44 | ||
60 | channel.bind('play', (txn, params) => this.embed.player.play()) | 45 | channel.bind('play', (txn, params) => this.embed.player.play()) |
61 | channel.bind('pause', (txn, params) => this.embed.player.pause()) | 46 | channel.bind('pause', (txn, params) => this.embed.player.pause()) |
@@ -73,16 +58,16 @@ class PeerTubeEmbedApi { | |||
73 | } | 58 | } |
74 | 59 | ||
75 | private setResolution (resolutionId: number) { | 60 | private setResolution (resolutionId: number) { |
76 | if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return | 61 | if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionForbidden()) return |
77 | 62 | ||
78 | // Auto resolution | 63 | // Auto resolution |
79 | if (resolutionId === -1) { | 64 | if (resolutionId === -1) { |
80 | this.embed.player.peertube().enableAutoResolution() | 65 | this.embed.player.webtorrent().enableAutoResolution() |
81 | return | 66 | return |
82 | } | 67 | } |
83 | 68 | ||
84 | this.embed.player.peertube().disableAutoResolution() | 69 | this.embed.player.webtorrent().disableAutoResolution() |
85 | this.embed.player.peertube().updateResolution(resolutionId) | 70 | this.embed.player.webtorrent().updateResolution(resolutionId) |
86 | } | 71 | } |
87 | 72 | ||
88 | /** | 73 | /** |
@@ -97,8 +82,8 @@ class PeerTubeEmbedApi { | |||
97 | let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted' | 82 | let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted' |
98 | 83 | ||
99 | setInterval(() => { | 84 | setInterval(() => { |
100 | let position = this.element.currentTime | 85 | const position = this.element.currentTime |
101 | let volume = this.element.volume | 86 | const volume = this.element.volume |
102 | 87 | ||
103 | this.channel.notify({ | 88 | this.channel.notify({ |
104 | method: 'playbackStatusUpdate', | 89 | method: 'playbackStatusUpdate', |
@@ -122,15 +107,17 @@ class PeerTubeEmbedApi { | |||
122 | 107 | ||
123 | // PeerTube specific capabilities | 108 | // PeerTube specific capabilities |
124 | 109 | ||
125 | this.embed.player.peertube().on('autoResolutionUpdate', () => this.loadResolutions()) | 110 | if (this.embed.player.webtorrent) { |
126 | this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) | 111 | this.embed.player.webtorrent().on('autoResolutionUpdate', () => this.loadWebTorrentResolutions()) |
112 | this.embed.player.webtorrent().on('videoFileUpdate', () => this.loadWebTorrentResolutions()) | ||
113 | } | ||
127 | } | 114 | } |
128 | 115 | ||
129 | private loadResolutions () { | 116 | private loadWebTorrentResolutions () { |
130 | let resolutions = [] | 117 | const resolutions = [] |
131 | let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() | 118 | const currentResolutionId = this.embed.player.webtorrent().getCurrentResolutionId() |
132 | 119 | ||
133 | for (const videoFile of this.embed.player.peertube().videoFiles) { | 120 | for (const videoFile of this.embed.player.webtorrent().videoFiles) { |
134 | let label = videoFile.resolution.label | 121 | let label = videoFile.resolution.label |
135 | if (videoFile.fps && videoFile.fps >= 50) { | 122 | if (videoFile.fps && videoFile.fps >= 50) { |
136 | label += videoFile.fps | 123 | label += videoFile.fps |
@@ -164,6 +151,8 @@ class PeerTubeEmbed { | |||
164 | subtitle: string | 151 | subtitle: string |
165 | enableApi = false | 152 | enableApi = false |
166 | startTime: number | string = 0 | 153 | startTime: number | string = 0 |
154 | stopTime: number | string | ||
155 | mode: PlayerMode | ||
167 | scope = 'peertube' | 156 | scope = 'peertube' |
168 | 157 | ||
169 | static async main () { | 158 | static async main () { |
@@ -188,6 +177,10 @@ class PeerTubeEmbed { | |||
188 | return fetch(this.getVideoUrl(videoId) + '/captions') | 177 | return fetch(this.getVideoUrl(videoId) + '/captions') |
189 | } | 178 | } |
190 | 179 | ||
180 | loadConfig (): Promise<Response> { | ||
181 | return fetch('/api/v1/config') | ||
182 | } | ||
183 | |||
191 | removeElement (element: HTMLElement) { | 184 | removeElement (element: HTMLElement) { |
192 | element.parentElement.removeChild(element) | 185 | element.parentElement.removeChild(element) |
193 | } | 186 | } |
@@ -246,17 +239,20 @@ class PeerTubeEmbed { | |||
246 | 239 | ||
247 | private loadParams () { | 240 | private loadParams () { |
248 | try { | 241 | try { |
249 | let params = new URL(window.location.toString()).searchParams | 242 | const params = new URL(window.location.toString()).searchParams |
250 | 243 | ||
251 | this.autoplay = this.getParamToggle(params, 'autoplay') | 244 | this.autoplay = this.getParamToggle(params, 'autoplay', false) |
252 | this.controls = this.getParamToggle(params, 'controls') | 245 | this.controls = this.getParamToggle(params, 'controls', true) |
253 | this.muted = this.getParamToggle(params, 'muted') | 246 | this.muted = this.getParamToggle(params, 'muted', false) |
254 | this.loop = this.getParamToggle(params, 'loop') | 247 | this.loop = this.getParamToggle(params, 'loop', false) |
255 | this.enableApi = this.getParamToggle(params, 'api', this.enableApi) | 248 | this.enableApi = this.getParamToggle(params, 'api', this.enableApi) |
256 | 249 | ||
257 | this.scope = this.getParamString(params, 'scope', this.scope) | 250 | this.scope = this.getParamString(params, 'scope', this.scope) |
258 | this.subtitle = this.getParamString(params, 'subtitle') | 251 | this.subtitle = this.getParamString(params, 'subtitle') |
259 | this.startTime = this.getParamString(params, 'start') | 252 | this.startTime = this.getParamString(params, 'start') |
253 | this.stopTime = this.getParamString(params, 'stop') | ||
254 | |||
255 | this.mode = this.getParamString(params, 'mode') === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent' | ||
260 | } catch (err) { | 256 | } catch (err) { |
261 | console.error('Cannot get params from URL.', err) | 257 | console.error('Cannot get params from URL.', err) |
262 | } | 258 | } |
@@ -266,11 +262,11 @@ class PeerTubeEmbed { | |||
266 | const urlParts = window.location.pathname.split('/') | 262 | const urlParts = window.location.pathname.split('/') |
267 | const videoId = urlParts[ urlParts.length - 1 ] | 263 | const videoId = urlParts[ urlParts.length - 1 ] |
268 | 264 | ||
269 | const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([ | 265 | const [ serverTranslations, videoResponse, captionsResponse, configResponse ] = await Promise.all([ |
270 | loadLocaleInVideoJS(window.location.origin, vjs, navigator.language), | 266 | PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language), |
271 | getServerTranslations(window.location.origin, navigator.language), | ||
272 | this.loadVideoInfo(videoId), | 267 | this.loadVideoInfo(videoId), |
273 | this.loadVideoCaptions(videoId) | 268 | this.loadVideoCaptions(videoId), |
269 | this.loadConfig() | ||
274 | ]) | 270 | ]) |
275 | 271 | ||
276 | if (!videoResponse.ok) { | 272 | if (!videoResponse.ok) { |
@@ -292,43 +288,73 @@ class PeerTubeEmbed { | |||
292 | 288 | ||
293 | this.loadParams() | 289 | this.loadParams() |
294 | 290 | ||
295 | const videojsOptions = getVideojsOptions({ | 291 | const options: PeertubePlayerManagerOptions = { |
296 | autoplay: this.autoplay, | 292 | common: { |
297 | controls: this.controls, | 293 | autoplay: this.autoplay, |
298 | muted: this.muted, | 294 | controls: this.controls, |
299 | loop: this.loop, | 295 | muted: this.muted, |
300 | startTime: this.startTime, | 296 | loop: this.loop, |
301 | subtitle: this.subtitle, | 297 | captions: videoCaptions.length !== 0, |
302 | 298 | startTime: this.startTime, | |
303 | videoCaptions, | 299 | stopTime: this.stopTime, |
304 | inactivityTimeout: 1500, | 300 | subtitle: this.subtitle, |
305 | videoViewUrl: this.getVideoUrl(videoId) + '/views', | 301 | |
306 | playerElement: this.videoElement, | 302 | videoCaptions, |
307 | videoFiles: videoInfo.files, | 303 | inactivityTimeout: 1500, |
308 | videoDuration: videoInfo.duration, | 304 | videoViewUrl: this.getVideoUrl(videoId) + '/views', |
309 | enableHotkeys: true, | 305 | |
310 | peertubeLink: true, | 306 | playerElement: this.videoElement, |
311 | poster: window.location.origin + videoInfo.previewPath, | 307 | onPlayerElementChange: (element: HTMLVideoElement) => this.videoElement = element, |
312 | theaterMode: false | 308 | |
313 | }) | 309 | videoDuration: videoInfo.duration, |
310 | enableHotkeys: true, | ||
311 | peertubeLink: true, | ||
312 | poster: window.location.origin + videoInfo.previewPath, | ||
313 | theaterMode: false, | ||
314 | |||
315 | serverUrl: window.location.origin, | ||
316 | language: navigator.language, | ||
317 | embedUrl: window.location.origin + videoInfo.embedPath | ||
318 | }, | ||
319 | |||
320 | webtorrent: { | ||
321 | videoFiles: videoInfo.files | ||
322 | } | ||
323 | } | ||
314 | 324 | ||
315 | this.playerOptions = videojsOptions | 325 | if (this.mode === 'p2p-media-loader') { |
316 | this.player = vjs(this.videoContainerId, videojsOptions, () => { | 326 | const hlsPlaylist = videoInfo.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS) |
317 | this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) | 327 | |
328 | Object.assign(options, { | ||
329 | p2pMediaLoader: { | ||
330 | playlistUrl: hlsPlaylist.playlistUrl, | ||
331 | segmentsSha256Url: hlsPlaylist.segmentsSha256Url, | ||
332 | redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl), | ||
333 | trackerAnnounce: videoInfo.trackerUrls, | ||
334 | videoFiles: videoInfo.files | ||
335 | } as P2PMediaLoaderOptions | ||
336 | }) | ||
337 | } | ||
318 | 338 | ||
319 | window[ 'videojsPlayer' ] = this.player | 339 | this.player = await PeertubePlayerManager.initialize(this.mode, options) |
320 | 340 | ||
321 | if (this.controls) { | 341 | this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) |
322 | this.player.dock({ | ||
323 | title: videoInfo.name, | ||
324 | description: this.player.localize('Uses P2P, others may know your IP is downloading this video.') | ||
325 | }) | ||
326 | } | ||
327 | 342 | ||
328 | addContextMenu(this.player, window.location.origin + videoInfo.embedPath) | 343 | window[ 'videojsPlayer' ] = this.player |
329 | 344 | ||
330 | this.initializeApi() | 345 | if (this.controls) { |
331 | }) | 346 | const config: ServerConfig = await configResponse.json() |
347 | const description = config.tracker.enabled | ||
348 | ? '<span class="text">' + this.player.localize('Uses P2P, others may know your IP is downloading this video.') + '</span>' | ||
349 | : undefined | ||
350 | |||
351 | this.player.dock({ | ||
352 | title: videoInfo.name, | ||
353 | description | ||
354 | }) | ||
355 | } | ||
356 | |||
357 | this.initializeApi() | ||
332 | } | 358 | } |
333 | 359 | ||
334 | private handleError (err: Error, translations?: { [ id: string ]: string }) { | 360 | private handleError (err: Error, translations?: { [ id: string ]: string }) { |