aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/standalone
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/standalone')
-rw-r--r--client/src/standalone/videos/embed.html3
-rw-r--r--client/src/standalone/videos/embed.ts156
2 files changed, 98 insertions, 61 deletions
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html
index b7cf13ec2..c3b6e08ca 100644
--- a/client/src/standalone/videos/embed.html
+++ b/client/src/standalone/videos/embed.html
@@ -6,6 +6,7 @@
6 <meta charset="UTF-8"> 6 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <meta name="viewport" content="width=device-width, initial-scale=1">
8 <meta name="robots" content="noindex"> 8 <meta name="robots" content="noindex">
9 <meta property="og:platform" content="PeerTube" />
9 10
10 <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" />
11 </head> 12 </head>
@@ -13,7 +14,7 @@
13 <body> 14 <body>
14 15
15 <div id="error-block"> 16 <div id="error-block">
16 <h1 id="error-title">Sorry</h1> 17 <h1 id="error-title"></h1>
17 18
18 <div id="error-content"></div> 19 <div id="error-content"></div>
19 </div> 20 </div>
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts
index 3a09f285e..32bf42e12 100644
--- a/client/src/standalone/videos/embed.ts
+++ b/client/src/standalone/videos/embed.ts
@@ -17,17 +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
20// FIXME: something weird with our path definition in tsconfig and typings
21// @ts-ignore
22import * as vjs from 'video.js'
23
24import * as Channel from 'jschannel' 20import * as Channel from 'jschannel'
25 21
26import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared' 22import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared'
27import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player'
28import { PeerTubeResolution } from '../player/definitions' 23import { PeerTubeResolution } from '../player/definitions'
29import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 24import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
30import { 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'
31 33
32/** 34/**
33 * 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
@@ -73,16 +75,16 @@ class PeerTubeEmbedApi {
73 } 75 }
74 76
75 private setResolution (resolutionId: number) { 77 private setResolution (resolutionId: number) {
76 if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return 78 if (resolutionId === -1 && this.embed.player.webtorrent().isAutoResolutionForbidden()) return
77 79
78 // Auto resolution 80 // Auto resolution
79 if (resolutionId === -1) { 81 if (resolutionId === -1) {
80 this.embed.player.peertube().enableAutoResolution() 82 this.embed.player.webtorrent().enableAutoResolution()
81 return 83 return
82 } 84 }
83 85
84 this.embed.player.peertube().disableAutoResolution() 86 this.embed.player.webtorrent().disableAutoResolution()
85 this.embed.player.peertube().updateResolution(resolutionId) 87 this.embed.player.webtorrent().updateResolution(resolutionId)
86 } 88 }
87 89
88 /** 90 /**
@@ -122,15 +124,17 @@ class PeerTubeEmbedApi {
122 124
123 // PeerTube specific capabilities 125 // PeerTube specific capabilities
124 126
125 this.embed.player.peertube().on('autoResolutionUpdate', () => this.loadResolutions()) 127 if (this.embed.player.webtorrent) {
126 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 }
127 } 131 }
128 132
129 private loadResolutions () { 133 private loadWebTorrentResolutions () {
130 let resolutions = [] 134 let resolutions = []
131 let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() 135 let currentResolutionId = this.embed.player.webtorrent().getCurrentResolutionId()
132 136
133 for (const videoFile of this.embed.player.peertube().videoFiles) { 137 for (const videoFile of this.embed.player.webtorrent().videoFiles) {
134 let label = videoFile.resolution.label 138 let label = videoFile.resolution.label
135 if (videoFile.fps && videoFile.fps >= 50) { 139 if (videoFile.fps && videoFile.fps >= 50) {
136 label += videoFile.fps 140 label += videoFile.fps
@@ -164,6 +168,7 @@ class PeerTubeEmbed {
164 subtitle: string 168 subtitle: string
165 enableApi = false 169 enableApi = false
166 startTime: number | string = 0 170 startTime: number | string = 0
171 mode: PlayerMode
167 scope = 'peertube' 172 scope = 'peertube'
168 173
169 static async main () { 174 static async main () {
@@ -192,27 +197,33 @@ class PeerTubeEmbed {
192 element.parentElement.removeChild(element) 197 element.parentElement.removeChild(element)
193 } 198 }
194 199
195 displayError (text: string) { 200 displayError (text: string, translations?: { [ id: string ]: string }) {
196 // Remove video element 201 // Remove video element
197 if (this.videoElement) this.removeElement(this.videoElement) 202 if (this.videoElement) this.removeElement(this.videoElement)
198 203
199 document.title = 'Sorry - ' + text 204 const translatedText = peertubeTranslate(text, translations)
205 const translatedSorry = peertubeTranslate('Sorry', translations)
206
207 document.title = translatedSorry + ' - ' + translatedText
200 208
201 const errorBlock = document.getElementById('error-block') 209 const errorBlock = document.getElementById('error-block')
202 errorBlock.style.display = 'flex' 210 errorBlock.style.display = 'flex'
203 211
212 const errorTitle = document.getElementById('error-title')
213 errorTitle.innerHTML = peertubeTranslate('Sorry', translations)
214
204 const errorText = document.getElementById('error-content') 215 const errorText = document.getElementById('error-content')
205 errorText.innerHTML = text 216 errorText.innerHTML = translatedText
206 } 217 }
207 218
208 videoNotFound () { 219 videoNotFound (translations?: { [ id: string ]: string }) {
209 const text = 'This video does not exist.' 220 const text = 'This video does not exist.'
210 this.displayError(text) 221 this.displayError(text, translations)
211 } 222 }
212 223
213 videoFetchError () { 224 videoFetchError (translations?: { [ id: string ]: string }) {
214 const text = 'We cannot fetch the video. Please try again later.' 225 const text = 'We cannot fetch the video. Please try again later.'
215 this.displayError(text) 226 this.displayError(text, translations)
216 } 227 }
217 228
218 getParamToggle (params: URLSearchParams, name: string, defaultValue?: boolean) { 229 getParamToggle (params: URLSearchParams, name: string, defaultValue?: boolean) {
@@ -251,6 +262,8 @@ class PeerTubeEmbed {
251 this.scope = this.getParamString(params, 'scope', this.scope) 262 this.scope = this.getParamString(params, 'scope', this.scope)
252 this.subtitle = this.getParamString(params, 'subtitle') 263 this.subtitle = this.getParamString(params, 'subtitle')
253 this.startTime = this.getParamString(params, 'start') 264 this.startTime = this.getParamString(params, 'start')
265
266 this.mode = this.getParamString(params, 'mode') === 'p2p-media-loader' ? 'p2p-media-loader' : 'webtorrent'
254 } catch (err) { 267 } catch (err) {
255 console.error('Cannot get params from URL.', err) 268 console.error('Cannot get params from URL.', err)
256 } 269 }
@@ -260,17 +273,16 @@ class PeerTubeEmbed {
260 const urlParts = window.location.pathname.split('/') 273 const urlParts = window.location.pathname.split('/')
261 const videoId = urlParts[ urlParts.length - 1 ] 274 const videoId = urlParts[ urlParts.length - 1 ]
262 275
263 const [ , serverTranslations, videoResponse, captionsResponse ] = await Promise.all([ 276 const [ serverTranslations, videoResponse, captionsResponse ] = await Promise.all([
264 loadLocaleInVideoJS(window.location.origin, vjs, navigator.language), 277 PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language),
265 getServerTranslations(window.location.origin, navigator.language),
266 this.loadVideoInfo(videoId), 278 this.loadVideoInfo(videoId),
267 this.loadVideoCaptions(videoId) 279 this.loadVideoCaptions(videoId)
268 ]) 280 ])
269 281
270 if (!videoResponse.ok) { 282 if (!videoResponse.ok) {
271 if (videoResponse.status === 404) return this.videoNotFound() 283 if (videoResponse.status === 404) return this.videoNotFound(serverTranslations)
272 284
273 return this.videoFetchError() 285 return this.videoFetchError(serverTranslations)
274 } 286 }
275 287
276 const videoInfo: VideoDetails = await videoResponse.json() 288 const videoInfo: VideoDetails = await videoResponse.json()
@@ -286,50 +298,74 @@ class PeerTubeEmbed {
286 298
287 this.loadParams() 299 this.loadParams()
288 300
289 const videojsOptions = getVideojsOptions({ 301 const options: PeertubePlayerManagerOptions = {
290 autoplay: this.autoplay, 302 common: {
291 controls: this.controls, 303 autoplay: this.autoplay,
292 muted: this.muted, 304 controls: this.controls,
293 loop: this.loop, 305 muted: this.muted,
294 startTime: this.startTime, 306 loop: this.loop,
295 subtitle: this.subtitle, 307 captions: videoCaptions.length !== 0,
296 308 startTime: this.startTime,
297 videoCaptions, 309 subtitle: this.subtitle,
298 inactivityTimeout: 1500, 310
299 videoViewUrl: this.getVideoUrl(videoId) + '/views', 311 videoCaptions,
300 playerElement: this.videoElement, 312 inactivityTimeout: 1500,
301 videoFiles: videoInfo.files, 313 videoViewUrl: this.getVideoUrl(videoId) + '/views',
302 videoDuration: videoInfo.duration, 314
303 enableHotkeys: true, 315 playerElement: this.videoElement,
304 peertubeLink: true, 316 onPlayerElementChange: (element: HTMLVideoElement) => this.videoElement = element,
305 poster: window.location.origin + videoInfo.previewPath, 317
306 theaterMode: false 318 videoDuration: videoInfo.duration,
307 }) 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 }
308 333
309 this.playerOptions = videojsOptions 334 if (this.mode === 'p2p-media-loader') {
310 this.player = vjs(this.videoContainerId, videojsOptions, () => { 335 const hlsPlaylist = videoInfo.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
311 this.player.on('customError', (event: any, data: any) => 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 }
312 347
313 window[ 'videojsPlayer' ] = this.player 348 this.player = await PeertubePlayerManager.initialize(this.mode, options)
314 349
315 if (this.controls) { 350 this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations))
316 this.player.dock({
317 title: videoInfo.name,
318 description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
319 })
320 }
321 351
322 addContextMenu(this.player, window.location.origin + videoInfo.embedPath) 352 window[ 'videojsPlayer' ] = this.player
323 353
324 this.initializeApi() 354 if (this.controls) {
325 }) 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()
326 } 362 }
327 363
328 private handleError (err: Error) { 364 private handleError (err: Error, translations?: { [ id: string ]: string }) {
329 if (err.message.indexOf('from xs param') !== -1) { 365 if (err.message.indexOf('from xs param') !== -1) {
330 this.player.dispose() 366 this.player.dispose()
331 this.videoElement = null 367 this.videoElement = null
332 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)
333 return 369 return
334 } 370 }
335 } 371 }