aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/standalone/videos/embed.ts
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/standalone/videos/embed.ts')
-rw-r--r--client/src/standalone/videos/embed.ts182
1 files changed, 111 insertions, 71 deletions
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index ea3436c7c..32bf42e12 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -17,14 +17,19 @@ import 'core-js/es6/set'
17// For google bot that uses Chrome 41 and does not understand fetch 17// For google bot that uses Chrome 41 and does not understand fetch
18import 'whatwg-fetch' 18import 'whatwg-fetch'
19 19
20import * as vjs from 'video.js'
21import * as Channel from 'jschannel' 20import * as Channel from 'jschannel'
22 21
23import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared' 22import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared'
24import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player'
25import { PeerTubeResolution } from '../player/definitions' 23import { PeerTubeResolution } from '../player/definitions'
26import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 24import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
27import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' 25import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
26import {
27 P2PMediaLoaderOptions,
28 PeertubePlayerManager,
29 PeertubePlayerManagerOptions,
30 PlayerMode
31} from '../../assets/player/peertube-player-manager'
32import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
28 33
29/** 34/**
30 * Embed API exposes control of the embed player to the outside world via 35 * Embed API exposes control of the embed player to the outside world via
@@ -70,16 +75,16 @@ class PeerTubeEmbedApi {
70 } 75 }
71 76
72 private setResolution (resolutionId: number) { 77 private setResolution (resolutionId: number) {
73 if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return 78 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionForbidden()) return
74 79
75 // Auto resolution 80 // Auto resolution
76 if (resolutionId === -1) { 81 if (resolutionId === -1) {
77 this.embed.player.peertube().enableAutoResolution() 82 this.embed.player.webtorrent().enableAutoResolution()
78 return 83 return
79 } 84 }
80 85
81 this.embed.player.peertube().disableAutoResolution() 86 this.embed.player.webtorrent().disableAutoResolution()
82 this.embed.player.peertube().updateResolution(resolutionId) 87 this.embed.player.webtorrent().updateResolution(resolutionId)
83 } 88 }
84 89
85 /** 90 /**
@@ -119,15 +124,17 @@ class PeerTubeEmbedApi {
119 124
120 // PeerTube specific capabilities 125 // PeerTube specific capabilities
121 126
122 this.embed.player.peertube().on('autoResolutionUpdate', () => this.loadResolutions()) 127 if (this.embed.player.webtorrent) {
123 this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) 128 this.embed.player.webtorrent().on('autoResolutionUpdate', () => this.loadWebTorrentResolutions())
129 this.embed.player.webtorrent().on('videoFileUpdate', () => this.loadWebTorrentResolutions())
130 }
124 } 131 }
125 132
126 private loadResolutions () { 133 private loadWebTorrentResolutions () {
127 let resolutions = [] 134 let resolutions = []
128 let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() 135 let currentResolutionId = this.embed.player.webtorrent().getCurrentResolutionId()
129 136
130 for (const videoFile of this.embed.player.peertube().videoFiles) { 137 for (const videoFile of this.embed.player.webtorrent().videoFiles) {
131 let label = videoFile.resolution.label 138 let label = videoFile.resolution.label
132 if (videoFile.fps && videoFile.fps >= 50) { 139 if (videoFile.fps && videoFile.fps >= 50) {
133 label += videoFile.fps 140 label += videoFile.fps
@@ -154,12 +161,14 @@ class PeerTubeEmbed {
154 player: any 161 player: any
155 playerOptions: any 162 playerOptions: any
156 api: PeerTubeEmbedApi = null 163 api: PeerTubeEmbedApi = null
157 autoplay = false 164 autoplay: boolean
158 controls = true 165 controls: boolean
159 muted = false 166 muted: boolean
160 loop = false 167 loop: boolean
168 subtitle: string
161 enableApi = false 169 enableApi = false
162 startTime: number | string = 0 170 startTime: number | string = 0
171 mode: PlayerMode
163 scope = 'peertube' 172 scope = 'peertube'
164 173
165 static async main () { 174 static async main () {
@@ -188,34 +197,40 @@ class PeerTubeEmbed {
188 element.parentElement.removeChild(element) 197 element.parentElement.removeChild(element)
189 } 198 }
190 199
191 displayError (text: string) { 200 displayError (text: string, translations?: { [ id: string ]: string }) {
192 // Remove video element 201 // Remove video element
193 if (this.videoElement) this.removeElement(this.videoElement) 202 if (this.videoElement) this.removeElement(this.videoElement)
194 203
195 document.title = 'Sorry - ' + text 204 const translatedText = peertubeTranslate(text, translations)
205 const translatedSorry = peertubeTranslate('Sorry', translations)
206
207 document.title = translatedSorry + ' - ' + translatedText
196 208
197 const errorBlock = document.getElementById('error-block') 209 const errorBlock = document.getElementById('error-block')
198 errorBlock.style.display = 'flex' 210 errorBlock.style.display = 'flex'
199 211
212 const errorTitle = document.getElementById('error-title')
213 errorTitle.innerHTML = peertubeTranslate('Sorry', translations)
214
200 const errorText = document.getElementById('error-content') 215 const errorText = document.getElementById('error-content')
201 errorText.innerHTML = text 216 errorText.innerHTML = translatedText
202 } 217 }
203 218
204 videoNotFound () { 219 videoNotFound (translations?: { [ id: string ]: string }) {
205 const text = 'This video does not exist.' 220 const text = 'This video does not exist.'
206 this.displayError(text) 221 this.displayError(text, translations)
207 } 222 }
208 223
209 videoFetchError () { 224 videoFetchError (translations?: { [ id: string ]: string }) {
210 const text = 'We cannot fetch the video. Please try again later.' 225 const text = 'We cannot fetch the video. Please try again later.'
211 this.displayError(text) 226 this.displayError(text, translations)
212 } 227 }
213 228
214 getParamToggle (params: URLSearchParams, name: string, defaultValue: boolean) { 229 getParamToggle (params: URLSearchParams, name: string, defaultValue?: boolean) {
215 return params.has(name) ? (params.get(name) === '1' || params.get(name) === 'true') : defaultValue 230 return params.has(name) ? (params.get(name) === '1' || params.get(name) === 'true') : defaultValue
216 } 231 }
217 232
218 getParamString (params: URLSearchParams, name: string, defaultValue: string) { 233 getParamString (params: URLSearchParams, name: string, defaultValue?: string) {
219 return params.has(name) ? params.get(name) : defaultValue 234 return params.has(name) ? params.get(name) : defaultValue
220 } 235 }
221 236
@@ -238,36 +253,36 @@ class PeerTubeEmbed {
238 try { 253 try {
239 let params = new URL(window.location.toString()).searchParams 254 let params = new URL(window.location.toString()).searchParams
240 255
241 this.autoplay = this.getParamToggle(params, 'autoplay', this.autoplay) 256 this.autoplay = this.getParamToggle(params, 'autoplay')
242 this.controls = this.getParamToggle(params, 'controls', this.controls) 257 this.controls = this.getParamToggle(params, 'controls')
243 this.muted = this.getParamToggle(params, 'muted', this.muted) 258 this.muted = this.getParamToggle(params, 'muted')
244 this.loop = this.getParamToggle(params, 'loop', this.loop) 259 this.loop = this.getParamToggle(params, 'loop')
245 this.enableApi = this.getParamToggle(params, 'api', this.enableApi) 260 this.enableApi = this.getParamToggle(params, 'api', this.enableApi)
261
246 this.scope = this.getParamString(params, 'scope', this.scope) 262 this.scope = this.getParamString(params, 'scope', this.scope)
263 this.subtitle = this.getParamString(params, 'subtitle')
264 this.startTime = this.getParamString(params, 'start')
247 265
248 const startTimeParamString = params.get('start') 266 this.mode = this.getParamString(params, 'mode') === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent'
249 if (startTimeParamString) this.startTime = startTimeParamString
250 } catch (err) { 267 } catch (err) {
251 console.error('Cannot get params from URL.', err) 268 console.error('Cannot get params from URL.', err)
252 } 269 }
253 } 270 }
254 271
255 private async initCore () { 272 private async initCore () {
256 const urlParts = window.location.href.split('/') 273 const urlParts = window.location.pathname.split('/')
257 const lastPart = urlParts[ urlParts.length - 1 ] 274 const videoId = urlParts[ urlParts.length - 1 ]
258 const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
259 275
260 const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([ 276 const [ serverTranslations, videoResponse, captionsResponse ] = await Promise.all([
261 loadLocaleInVideoJS(window.location.origin, vjs, navigator.language), 277 PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language),
262 getServerTranslations(window.location.origin, navigator.language),
263 this.loadVideoInfo(videoId), 278 this.loadVideoInfo(videoId),
264 this.loadVideoCaptions(videoId) 279 this.loadVideoCaptions(videoId)
265 ]) 280 ])
266 281
267 if (!videoResponse.ok) { 282 if (!videoResponse.ok) {
268 if (videoResponse.status === 404) return this.videoNotFound() 283 if (videoResponse.status === 404) return this.videoNotFound(serverTranslations)
269 284
270 return this.videoFetchError() 285 return this.videoFetchError(serverTranslations)
271 } 286 }
272 287
273 const videoInfo: VideoDetails = await videoResponse.json() 288 const videoInfo: VideoDetails = await videoResponse.json()
@@ -283,49 +298,74 @@ class PeerTubeEmbed {
283 298
284 this.loadParams() 299 this.loadParams()
285 300
286 const videojsOptions = getVideojsOptions({ 301 const options: PeertubePlayerManagerOptions = {
287 autoplay: this.autoplay, 302 common: {
288 controls: this.controls, 303 autoplay: this.autoplay,
289 muted: this.muted, 304 controls: this.controls,
290 loop: this.loop, 305 muted: this.muted,
291 startTime: this.startTime, 306 loop: this.loop,
292 307 captions: videoCaptions.length !== 0,
293 videoCaptions, 308 startTime: this.startTime,
294 inactivityTimeout: 1500, 309 subtitle: this.subtitle,
295 videoViewUrl: this.getVideoUrl(videoId) + '/views', 310
296 playerElement: this.videoElement, 311 videoCaptions,
297 videoFiles: videoInfo.files, 312 inactivityTimeout: 1500,
298 videoDuration: videoInfo.duration, 313 videoViewUrl: this.getVideoUrl(videoId) + '/views',
299 enableHotkeys: true, 314
300 peertubeLink: true, 315 playerElement: this.videoElement,
301 poster: window.location.origin + videoInfo.previewPath, 316 onPlayerElementChange: (element: HTMLVideoElement) => this.videoElement = element,
302 theaterMode: false 317
303 }) 318 videoDuration: videoInfo.duration,
319 enableHotkeys: true,
320 peertubeLink: true,
321 poster: window.location.origin + videoInfo.previewPath,
322 theaterMode: false,
323
324 serverUrl: window.location.origin,
325 language: navigator.language,
326 embedUrl: window.location.origin + videoInfo.embedPath
327 },
328
329 webtorrent: {
330 videoFiles: videoInfo.files
331 }
332 }
304 333
305 this.playerOptions = videojsOptions 334 if (this.mode === 'p2p-media-loader') {
306 this.player = vjs(this.videoContainerId, videojsOptions, () => { 335 const hlsPlaylist = videoInfo.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
307 this.player.on('customError', (event, data) => this.handleError(data.err)) 336
337 Object.assign(options, {
338 p2pMediaLoader: {
339 playlistUrl: hlsPlaylist.playlistUrl,
340 segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
341 redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl),
342 trackerAnnounce: videoInfo.trackerUrls,
343 videoFiles: videoInfo.files
344 } as P2PMediaLoaderOptions
345 })
346 }
308 347
309 window[ 'videojsPlayer' ] = this.player 348 this.player = await PeertubePlayerManager.initialize(this.mode, options)
310 349
311 if (this.controls) { 350 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations))
312 this.player.dock({
313 title: videoInfo.name,
314 description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
315 })
316 }
317 351
318 addContextMenu(this.player, window.location.origin + videoInfo.embedPath) 352 window[ 'videojsPlayer' ] = this.player
319 353
320 this.initializeApi() 354 if (this.controls) {
321 }) 355 this.player.dock({
356 title: videoInfo.name,
357 description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
358 })
359 }
360
361 this.initializeApi()
322 } 362 }
323 363
324 private handleError (err: Error) { 364 private handleError (err: Error, translations?: { [ id: string ]: string }) {
325 if (err.message.indexOf('from xs param') !== -1) { 365 if (err.message.indexOf('from xs param') !== -1) {
326 this.player.dispose() 366 this.player.dispose()
327 this.videoElement = null 367 this.videoElement = null
328 this.displayError('This video is not available because the remote instance is not responding.') 368 this.displayError('This video is not available because the remote instance is not responding.', translations)
329 return 369 return
330 } 370 }
331 } 371 }