]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/standalone/videos/embed.ts
Fix circular dependencies
[github/Chocobozzz/PeerTube.git] / client / src / standalone / videos / embed.ts
CommitLineData
202e7223 1import './embed.scss'
583eb04b 2import videojs from 'video.js'
a4ff3100
C
3import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
4import { Tokens } from '@root-helpers/users'
bd45d503 5import { peertubeTranslate } from '../../../../shared/core-utils/i18n'
3f9c4955 6import {
3f9c4955
C
7 ResultList,
8 ServerConfig,
583eb04b
C
9 UserRefreshToken,
10 VideoCaption,
4504f09f 11 VideoDetails,
5abc96fc
C
12 VideoPlaylist,
13 VideoPlaylistElement,
583eb04b
C
14 VideoStreamingPlaylistType
15} from '../../../../shared/models'
16import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
abb3097e 17import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
583eb04b
C
18import { TranslationsManager } from '../../assets/player/translations-manager'
19import { PeerTubeEmbedApi } from './embed-api'
abb3097e
C
20
21type Translations = { [ id: string ]: string }
202e7223 22
5efab546 23export class PeerTubeEmbed {
5abc96fc 24 playerElement: HTMLVideoElement
7e37e111 25 player: videojs.Player
902aa3a0 26 api: PeerTubeEmbedApi = null
5abc96fc 27
3b019808
C
28 autoplay: boolean
29 controls: boolean
30 muted: boolean
31 loop: boolean
32 subtitle: string
902aa3a0 33 enableApi = false
1f6824c9 34 startTime: number | string = 0
f0a39880 35 stopTime: number | string
5efab546
C
36
37 title: boolean
38 warningTitle: boolean
08d9ba0f 39 peertubeLink: boolean
5efab546
C
40 bigPlayBackgroundColor: string
41 foregroundColor: string
42
3b6f205c 43 mode: PlayerMode
902aa3a0
C
44 scope = 'peertube'
45
a4ff3100 46 userTokens: Tokens
71ab65d0 47 headers = new Headers()
4504f09f
RK
48 LOCAL_STORAGE_OAUTH_CLIENT_KEYS = {
49 CLIENT_ID: 'client_id',
50 CLIENT_SECRET: 'client_secret'
51 }
71ab65d0 52
5abc96fc
C
53 private translationsPromise: Promise<{ [id: string]: string }>
54 private configPromise: Promise<ServerConfig>
55 private PeertubePlayerManagerModulePromise: Promise<any>
56
57 private playlist: VideoPlaylist
58 private playlistElements: VideoPlaylistElement[]
59 private currentPlaylistElement: VideoPlaylistElement
60
61 private wrapperElement: HTMLElement
62
902aa3a0 63 static async main () {
5abc96fc 64 const videoContainerId = 'video-wrapper'
99941732
WL
65 const embed = new PeerTubeEmbed(videoContainerId)
66 await embed.init()
67 }
902aa3a0 68
5abc96fc
C
69 constructor (private videoWrapperId: string) {
70 this.wrapperElement = document.getElementById(this.videoWrapperId)
902aa3a0
C
71 }
72
99941732
WL
73 getVideoUrl (id: string) {
74 return window.location.origin + '/api/v1/videos/' + id
75 }
d4f3fea6 76
207612df 77 refreshFetch (url: string, options?: RequestInit) {
4504f09f
RK
78 return fetch(url, options)
79 .then((res: Response) => {
80 if (res.status !== 401) return res
81
82 // 401 unauthorized is not catch-ed, but then-ed
83 const error = res
84
85 const refreshingTokenPromise = new Promise((resolve, reject) => {
86 const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID)
87 const clientSecret: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET)
207612df 88
4504f09f
RK
89 const headers = new Headers()
90 headers.set('Content-Type', 'application/x-www-form-urlencoded')
207612df 91
4504f09f 92 const data = {
a4ff3100 93 refresh_token: this.userTokens.refreshToken,
4504f09f
RK
94 client_id: clientId,
95 client_secret: clientSecret,
96 response_type: 'code',
97 grant_type: 'refresh_token'
98 }
99
100 fetch('/api/v1/users/token', {
101 headers,
102 method: 'POST',
103 body: objectToUrlEncoded(data)
207612df 104 }).then(res => res.json())
4504f09f 105 .then((obj: UserRefreshToken) => {
a4ff3100
C
106 this.userTokens.accessToken = obj.access_token
107 this.userTokens.refreshToken = obj.refresh_token
108 this.userTokens.save()
109
110 this.setHeadersFromTokens()
111
4504f09f
RK
112 resolve()
113 })
114 .catch((refreshTokenError: any) => {
115 reject(refreshTokenError)
116 })
117 })
118
119 return refreshingTokenPromise
207612df 120 .catch(() => this.removeTokensFromHeaders())
4504f09f
RK
121 .then(() => fetch(url, {
122 ...options,
123 headers: this.headers
124 }))
125 })
126 }
127
5abc96fc
C
128 getPlaylistUrl (id: string) {
129 return window.location.origin + '/api/v1/video-playlists/' + id
130 }
131
99941732 132 loadVideoInfo (videoId: string): Promise<Response> {
4504f09f 133 return this.refreshFetch(this.getVideoUrl(videoId), { headers: this.headers })
99941732 134 }
d4f3fea6 135
16f7022b 136 loadVideoCaptions (videoId: string): Promise<Response> {
be59656c 137 return this.refreshFetch(this.getVideoUrl(videoId) + '/captions', { headers: this.headers })
16f7022b
C
138 }
139
5abc96fc 140 loadPlaylistInfo (playlistId: string): Promise<Response> {
be59656c 141 return this.refreshFetch(this.getPlaylistUrl(playlistId), { headers: this.headers })
5abc96fc
C
142 }
143
fb13852d
C
144 loadPlaylistElements (playlistId: string, start = 0): Promise<Response> {
145 const url = new URL(this.getPlaylistUrl(playlistId) + '/videos')
146 url.search = new URLSearchParams({ start: '' + start, count: '100' }).toString()
147
be59656c 148 return this.refreshFetch(url.toString(), { headers: this.headers })
5abc96fc
C
149 }
150
151 loadConfig (): Promise<ServerConfig> {
be59656c 152 return this.refreshFetch('/api/v1/config')
5abc96fc 153 .then(res => res.json())
31b6ddf8
C
154 }
155
99941732
WL
156 removeElement (element: HTMLElement) {
157 element.parentElement.removeChild(element)
158 }
d4f3fea6 159
abb3097e 160 displayError (text: string, translations?: Translations) {
99941732 161 // Remove video element
5abc96fc
C
162 if (this.playerElement) {
163 this.removeElement(this.playerElement)
164 this.playerElement = undefined
165 }
99941732 166
ad3fa0c5
C
167 const translatedText = peertubeTranslate(text, translations)
168 const translatedSorry = peertubeTranslate('Sorry', translations)
169
170 document.title = translatedSorry + ' - ' + translatedText
99941732
WL
171
172 const errorBlock = document.getElementById('error-block')
173 errorBlock.style.display = 'flex'
174
ad3fa0c5
C
175 const errorTitle = document.getElementById('error-title')
176 errorTitle.innerHTML = peertubeTranslate('Sorry', translations)
177
99941732 178 const errorText = document.getElementById('error-content')
ad3fa0c5 179 errorText.innerHTML = translatedText
2a71d286
C
180
181 this.wrapperElement.style.display = 'none'
99941732
WL
182 }
183
abb3097e 184 videoNotFound (translations?: Translations) {
99941732 185 const text = 'This video does not exist.'
ad3fa0c5 186 this.displayError(text, translations)
99941732
WL
187 }
188
abb3097e 189 videoFetchError (translations?: Translations) {
99941732 190 const text = 'We cannot fetch the video. Please try again later.'
ad3fa0c5 191 this.displayError(text, translations)
99941732
WL
192 }
193
5abc96fc
C
194 playlistNotFound (translations?: Translations) {
195 const text = 'This playlist does not exist.'
196 this.displayError(text, translations)
197 }
198
199 playlistFetchError (translations?: Translations) {
200 const text = 'We cannot fetch the playlist. Please try again later.'
201 this.displayError(text, translations)
202 }
203
3b019808 204 getParamToggle (params: URLSearchParams, name: string, defaultValue?: boolean) {
99941732
WL
205 return params.has(name) ? (params.get(name) === '1' || params.get(name) === 'true') : defaultValue
206 }
d4f3fea6 207
3b019808 208 getParamString (params: URLSearchParams, name: string, defaultValue?: string) {
99941732
WL
209 return params.has(name) ? params.get(name) : defaultValue
210 }
da99ccf2 211
9054a8b6
C
212 async playNextVideo () {
213 const next = this.getNextPlaylistElement()
214 if (!next) {
215 console.log('Next element not found in playlist.')
216 return
217 }
218
219 this.currentPlaylistElement = next
220
221 return this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
222 }
223
224 async playPreviousVideo () {
225 const previous = this.getPreviousPlaylistElement()
226 if (!previous) {
227 console.log('Previous element not found in playlist.')
228 return
229 }
230
231 this.currentPlaylistElement = previous
232
233 await this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
234 }
235
236 getCurrentPosition () {
237 if (!this.currentPlaylistElement) return -1
238
239 return this.currentPlaylistElement.position
240 }
241
902aa3a0 242 async init () {
99941732 243 try {
a4ff3100 244 this.userTokens = Tokens.load()
99941732
WL
245 await this.initCore()
246 } catch (e) {
247 console.error(e)
248 }
249 }
250
902aa3a0
C
251 private initializeApi () {
252 if (!this.enableApi) return
253
254 this.api = new PeerTubeEmbedApi(this)
255 this.api.initialize()
256 }
257
0f2f274c 258 private loadParams (video: VideoDetails) {
da99ccf2 259 try {
c4710631 260 const params = new URL(window.location.toString()).searchParams
99941732 261
31b6ddf8
C
262 this.autoplay = this.getParamToggle(params, 'autoplay', false)
263 this.controls = this.getParamToggle(params, 'controls', true)
64645512 264 this.muted = this.getParamToggle(params, 'muted', undefined)
31b6ddf8 265 this.loop = this.getParamToggle(params, 'loop', false)
5efab546 266 this.title = this.getParamToggle(params, 'title', true)
99941732 267 this.enableApi = this.getParamToggle(params, 'api', this.enableApi)
5efab546 268 this.warningTitle = this.getParamToggle(params, 'warningTitle', true)
08d9ba0f 269 this.peertubeLink = this.getParamToggle(params, 'peertubeLink', true)
f37bad63 270
3b019808
C
271 this.scope = this.getParamString(params, 'scope', this.scope)
272 this.subtitle = this.getParamString(params, 'subtitle')
273 this.startTime = this.getParamString(params, 'start')
f0a39880 274 this.stopTime = this.getParamString(params, 'stop')
3b6f205c 275
5efab546
C
276 this.bigPlayBackgroundColor = this.getParamString(params, 'bigPlayBackgroundColor')
277 this.foregroundColor = this.getParamString(params, 'foregroundColor')
278
0f2f274c
C
279 const modeParam = this.getParamString(params, 'mode')
280
281 if (modeParam) {
282 if (modeParam === 'p2p-media-loader') this.mode = 'p2p-media-loader'
283 else this.mode = 'webtorrent'
284 } else {
285 if (Array.isArray(video.streamingPlaylists) && video.streamingPlaylists.length !== 0) this.mode = 'p2p-media-loader'
286 else this.mode = 'webtorrent'
287 }
da99ccf2
C
288 } catch (err) {
289 console.error('Cannot get params from URL.', err)
290 }
99941732
WL
291 }
292
fb13852d
C
293 private async loadAllPlaylistVideos (playlistId: string, baseResult: ResultList<VideoPlaylistElement>) {
294 let elements = baseResult.data
295 let total = baseResult.total
296 let i = 0
297
298 while (total > elements.length && i < 10) {
299 const result = await this.loadPlaylistElements(playlistId, elements.length)
300
301 const json = await result.json() as ResultList<VideoPlaylistElement>
302 total = json.total
303
304 elements = elements.concat(json.data)
305 i++
306 }
307
308 if (i === 10) {
309 console.error('Cannot fetch all playlists elements, there are too many!')
310 }
311
312 return elements
313 }
314
5abc96fc
C
315 private async loadPlaylist (playlistId: string) {
316 const playlistPromise = this.loadPlaylistInfo(playlistId)
317 const playlistElementsPromise = this.loadPlaylistElements(playlistId)
99941732 318
be59656c
C
319 let playlistResponse: Response
320 let isResponseOk: boolean
5abc96fc 321
be59656c
C
322 try {
323 playlistResponse = await playlistPromise
324 isResponseOk = true
325 } catch (err) {
326 console.error(err)
327 isResponseOk = false
328 }
329
330 if (!isResponseOk) {
5abc96fc 331 const serverTranslations = await this.translationsPromise
71ab65d0 332
be59656c 333 if (playlistResponse?.status === 404) {
5abc96fc
C
334 this.playlistNotFound(serverTranslations)
335 return undefined
336 }
337
338 this.playlistFetchError(serverTranslations)
339 return undefined
340 }
341
342 return { playlistResponse, videosResponse: await playlistElementsPromise }
343 }
344
345 private async loadVideo (videoId: string) {
3f9c4955 346 const videoPromise = this.loadVideoInfo(videoId)
3f9c4955 347
be59656c
C
348 let videoResponse: Response
349 let isResponseOk: boolean
350
351 try {
352 videoResponse = await videoPromise
353 isResponseOk = true
354 } catch (err) {
355 console.error(err)
356
357 isResponseOk = false
358 }
99941732 359
be59656c 360 if (!isResponseOk) {
5abc96fc
C
361 const serverTranslations = await this.translationsPromise
362
be59656c 363 if (videoResponse?.status === 404) {
5abc96fc
C
364 this.videoNotFound(serverTranslations)
365 return undefined
366 }
367
368 this.videoFetchError(serverTranslations)
369 return undefined
370 }
371
372 const captionsPromise = this.loadVideoCaptions(videoId)
373
374 return { captionsPromise, videoResponse }
375 }
3f9c4955 376
5abc96fc
C
377 private async buildPlaylistManager () {
378 const translations = await this.translationsPromise
379
380 this.player.upnext({
381 timeout: 10000, // 10s
382 headText: peertubeTranslate('Up Next', translations),
383 cancelText: peertubeTranslate('Cancel', translations),
384 suspendedText: peertubeTranslate('Autoplay is suspended', translations),
385 getTitle: () => this.nextVideoTitle(),
a950e4c8 386 next: () => this.playNextVideo(),
5abc96fc
C
387 condition: () => !!this.getNextPlaylistElement(),
388 suspended: () => false
389 })
390 }
99941732 391
4572c3d0
C
392 private async loadVideoAndBuildPlayer (uuid: string) {
393 const res = await this.loadVideo(uuid)
5abc96fc
C
394 if (res === undefined) return
395
396 return this.buildVideoPlayer(res.videoResponse, res.captionsPromise)
397 }
3f9c4955 398
5abc96fc
C
399 private nextVideoTitle () {
400 const next = this.getNextPlaylistElement()
401 if (!next) return ''
402
403 return next.video.name
404 }
405
406 private getNextPlaylistElement (position?: number): VideoPlaylistElement {
407 if (!position) position = this.currentPlaylistElement.position + 1
408
409 if (position > this.playlist.videosLength) {
410 return undefined
411 }
412
413 const next = this.playlistElements.find(e => e.position === position)
414
415 if (!next || !next.video) {
416 return this.getNextPlaylistElement(position + 1)
417 }
418
419 return next
420 }
421
a950e4c8 422 private getPreviousPlaylistElement (position?: number): VideoPlaylistElement {
2a71d286 423 if (!position) position = this.currentPlaylistElement.position - 1
a950e4c8
C
424
425 if (position < 1) {
426 return undefined
427 }
428
429 const prev = this.playlistElements.find(e => e.position === position)
430
431 if (!prev || !prev.video) {
432 return this.getNextPlaylistElement(position - 1)
433 }
434
435 return prev
436 }
437
5abc96fc
C
438 private async buildVideoPlayer (videoResponse: Response, captionsPromise: Promise<Response>) {
439 let alreadyHadPlayer = false
440
441 if (this.player) {
442 this.player.dispose()
443 alreadyHadPlayer = true
444 }
445
446 this.playerElement = document.createElement('video')
447 this.playerElement.className = 'video-js vjs-peertube-skin'
448 this.playerElement.setAttribute('playsinline', 'true')
449 this.wrapperElement.appendChild(this.playerElement)
450
451 const videoInfoPromise = videoResponse.json()
452 .then((videoInfo: VideoDetails) => {
453 if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo)
454
455 return videoInfo
456 })
457
6fad8e51 458 const [ videoInfoTmp, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([
5abc96fc
C
459 videoInfoPromise,
460 this.translationsPromise,
461 captionsPromise,
462 this.configPromise,
463 this.PeertubePlayerManagerModulePromise
464 ])
3f9c4955 465
6fad8e51
C
466 const videoInfo: VideoDetails = videoInfoTmp
467
3f9c4955 468 const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager
5efab546 469 const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse)
99941732 470
0f2f274c 471 this.loadParams(videoInfo)
da99ccf2 472
4572c3d0
C
473 const playlistPlugin = this.currentPlaylistElement
474 ? {
475 elements: this.playlistElements,
476 playlist: this.playlist,
477
478 getCurrentPosition: () => this.currentPlaylistElement.position,
479
480 onItemClicked: (videoPlaylistElement: VideoPlaylistElement) => {
481 this.currentPlaylistElement = videoPlaylistElement
482
483 this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
484 .catch(err => console.error(err))
485 }
486 }
487 : undefined
488
2adfc7ea
C
489 const options: PeertubePlayerManagerOptions = {
490 common: {
5abc96fc
C
491 // Autoplay in playlist mode
492 autoplay: alreadyHadPlayer ? true : this.autoplay,
2adfc7ea
C
493 controls: this.controls,
494 muted: this.muted,
495 loop: this.loop,
1a8c2d74 496
2adfc7ea 497 captions: videoCaptions.length !== 0,
2adfc7ea
C
498 subtitle: this.subtitle,
499
1a8c2d74
C
500 startTime: this.playlist ? this.currentPlaylistElement.startTimestamp : this.startTime,
501 stopTime: this.playlist ? this.currentPlaylistElement.stopTimestamp : this.stopTime,
502
a950e4c8
C
503 nextVideo: this.playlist ? () => this.playNextVideo() : undefined,
504 hasNextVideo: this.playlist ? () => !!this.getNextPlaylistElement() : undefined,
505
506 previousVideo: this.playlist ? () => this.playPreviousVideo() : undefined,
507 hasPreviousVideo: this.playlist ? () => !!this.getPreviousPlaylistElement() : undefined,
508
4572c3d0 509 playlist: playlistPlugin,
5abc96fc 510
2adfc7ea 511 videoCaptions,
35f0a5e6 512 inactivityTimeout: 2500,
5abc96fc 513 videoViewUrl: this.getVideoUrl(videoInfo.uuid) + '/views',
6ec0b75b 514
5abc96fc
C
515 playerElement: this.playerElement,
516 onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
6ec0b75b 517
2adfc7ea
C
518 videoDuration: videoInfo.duration,
519 enableHotkeys: true,
08d9ba0f 520 peertubeLink: this.peertubeLink,
2adfc7ea 521 poster: window.location.origin + videoInfo.previewPath,
3d9a63d3 522 theaterButton: false,
2adfc7ea
C
523
524 serverUrl: window.location.origin,
525 language: navigator.language,
526 embedUrl: window.location.origin + videoInfo.embedPath
6ec0b75b
C
527 },
528
529 webtorrent: {
530 videoFiles: videoInfo.files
2adfc7ea 531 }
3b6f205c 532 }
2adfc7ea 533
3b6f205c 534 if (this.mode === 'p2p-media-loader') {
09209296
C
535 const hlsPlaylist = videoInfo.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
536
3b6f205c
C
537 Object.assign(options, {
538 p2pMediaLoader: {
09209296
C
539 playlistUrl: hlsPlaylist.playlistUrl,
540 segmentsSha256Url: hlsPlaylist.segmentsSha256Url,
541 redundancyBaseUrls: hlsPlaylist.redundancies.map(r => r.baseUrl),
542 trackerAnnounce: videoInfo.trackerUrls,
5a71acd2 543 videoFiles: hlsPlaylist.files
09209296 544 } as P2PMediaLoaderOptions
3b6f205c 545 })
2adfc7ea 546 }
202e7223 547
7e37e111 548 this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: videojs.Player) => this.player = player)
2adfc7ea 549 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations))
99941732 550
2adfc7ea 551 window[ 'videojsPlayer' ] = this.player
902aa3a0 552
5efab546
C
553 this.buildCSS()
554
5abc96fc 555 await this.buildDock(videoInfo, config)
5efab546
C
556
557 this.initializeApi()
3f9c4955
C
558
559 this.removePlaceholder()
5abc96fc
C
560
561 if (this.isPlaylistEmbed()) {
562 await this.buildPlaylistManager()
1a8c2d74 563
4572c3d0 564 this.player.playlist().updateSelected()
1a8c2d74
C
565
566 this.player.on('stopped', () => {
567 this.playNextVideo()
568 })
5abc96fc
C
569 }
570 }
571
572 private async initCore () {
573 if (this.userTokens) this.setHeadersFromTokens()
574
575 this.configPromise = this.loadConfig()
576 this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language)
577 this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager')
578
579 let videoId: string
580
581 if (this.isPlaylistEmbed()) {
582 const playlistId = this.getResourceId()
583 const res = await this.loadPlaylist(playlistId)
584 if (!res) return undefined
585
586 this.playlist = await res.playlistResponse.json()
587
588 const playlistElementResult = await res.videosResponse.json()
fb13852d 589 this.playlistElements = await this.loadAllPlaylistVideos(playlistId, playlistElementResult)
5abc96fc 590
2a71d286
C
591 const params = new URL(window.location.toString()).searchParams
592 const playlistPositionParam = this.getParamString(params, 'playlistPosition')
593
594 let position = 1
595
596 if (playlistPositionParam) {
597 position = parseInt(playlistPositionParam + '', 10)
598 }
599
600 this.currentPlaylistElement = this.playlistElements.find(e => e.position === position)
601 if (!this.currentPlaylistElement || !this.currentPlaylistElement.video) {
602 console.error('Current playlist element is not valid.', this.currentPlaylistElement)
603 this.currentPlaylistElement = this.getNextPlaylistElement()
604 }
605
606 if (!this.currentPlaylistElement) {
607 console.error('This playlist does not have any valid element.')
608 const serverTranslations = await this.translationsPromise
609 this.playlistFetchError(serverTranslations)
610 return
611 }
612
5abc96fc
C
613 videoId = this.currentPlaylistElement.video.uuid
614 } else {
615 videoId = this.getResourceId()
616 }
617
4572c3d0 618 return this.loadVideoAndBuildPlayer(videoId)
5efab546
C
619 }
620
621 private handleError (err: Error, translations?: { [ id: string ]: string }) {
622 if (err.message.indexOf('from xs param') !== -1) {
623 this.player.dispose()
5abc96fc 624 this.playerElement = null
5efab546
C
625 this.displayError('This video is not available because the remote instance is not responding.', translations)
626 return
627 }
628 }
629
5abc96fc 630 private async buildDock (videoInfo: VideoDetails, config: ServerConfig) {
abb3097e 631 if (!this.controls) return
5efab546 632
818c449b
C
633 // On webtorrent fallback, player may have been disposed
634 if (!this.player.player_) return
5efab546 635
abb3097e 636 const title = this.title ? videoInfo.name : undefined
31b6ddf8 637
abb3097e
C
638 const description = config.tracker.enabled && this.warningTitle
639 ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
640 : undefined
641
642 this.player.dock({
643 title,
644 description
645 })
5efab546 646 }
16f7022b 647
5efab546
C
648 private buildCSS () {
649 const body = document.getElementById('custom-css')
650
651 if (this.bigPlayBackgroundColor) {
652 body.style.setProperty('--embedBigPlayBackgroundColor', this.bigPlayBackgroundColor)
653 }
654
655 if (this.foregroundColor) {
656 body.style.setProperty('--embedForegroundColor', this.foregroundColor)
657 }
99941732 658 }
6d88de72 659
5efab546
C
660 private async buildCaptions (serverTranslations: any, captionsResponse: Response): Promise<VideoJSCaption[]> {
661 if (captionsResponse.ok) {
662 const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
663
664 return data.map(c => ({
665 label: peertubeTranslate(c.language.label, serverTranslations),
666 language: c.language.id,
667 src: window.location.origin + c.captionPath
668 }))
6d88de72 669 }
5efab546
C
670
671 return []
6d88de72 672 }
3f9c4955
C
673
674 private loadPlaceholder (video: VideoDetails) {
675 const placeholder = this.getPlaceholderElement()
676
677 const url = window.location.origin + video.previewPath
678 placeholder.style.backgroundImage = `url("${url}")`
5abc96fc 679 placeholder.style.display = 'block'
3f9c4955
C
680 }
681
682 private removePlaceholder () {
683 const placeholder = this.getPlaceholderElement()
5abc96fc 684 placeholder.style.display = 'none'
3f9c4955
C
685 }
686
687 private getPlaceholderElement () {
688 return document.getElementById('placeholder-preview')
689 }
a4ff3100
C
690
691 private setHeadersFromTokens () {
692 this.headers.set('Authorization', `${this.userTokens.tokenType} ${this.userTokens.accessToken}`)
693 }
5abc96fc 694
207612df
C
695 private removeTokensFromHeaders () {
696 this.headers.delete('Authorization')
697 }
698
5abc96fc
C
699 private getResourceId () {
700 const urlParts = window.location.pathname.split('/')
701 return urlParts[ urlParts.length - 1 ]
702 }
703
704 private isPlaylistEmbed () {
705 return window.location.pathname.split('/')[1] === 'video-playlists'
706 }
99941732
WL
707}
708
709PeerTubeEmbed.main()
902aa3a0 710 .catch(err => console.error('Cannot init embed.', err))