aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/standalone
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/standalone')
-rw-r--r--client/src/standalone/player/definitions.ts4
-rw-r--r--client/src/standalone/player/events.ts4
-rw-r--r--client/src/standalone/videos/embed.html1
-rw-r--r--client/src/standalone/videos/embed.ts180
-rw-r--r--client/src/standalone/videos/test-embed.ts24
5 files changed, 119 insertions, 94 deletions
diff --git a/client/src/standalone/player/definitions.ts b/client/src/standalone/player/definitions.ts
index 7f9ef9b6f..afd10541b 100644
--- a/client/src/standalone/player/definitions.ts
+++ b/client/src/standalone/player/definitions.ts
@@ -1,6 +1,4 @@
1export interface EventHandler<T> { 1export type EventHandler<T> = (ev: T) => void
2 (ev: T): void
3}
4 2
5export type PlayerEventType = 3export type PlayerEventType =
6 'pause' | 'play' | 4 'pause' | 'play' |
diff --git a/client/src/standalone/player/events.ts b/client/src/standalone/player/events.ts
index f1639ef19..28a13c727 100644
--- a/client/src/standalone/player/events.ts
+++ b/client/src/standalone/player/events.ts
@@ -13,13 +13,13 @@ export class EventRegistrar {
13 private eventRegistrations: PlayerEventRegistrationMap = {} 13 private eventRegistrations: PlayerEventRegistrationMap = {}
14 14
15 public bindToChannel (channel: Channel.MessagingChannel) { 15 public bindToChannel (channel: Channel.MessagingChannel) {
16 for (let name of Object.keys(this.eventRegistrations)) { 16 for (const name of Object.keys(this.eventRegistrations)) {
17 channel.bind(name, (txn, params) => this.fire(name, params)) 17 channel.bind(name, (txn, params) => this.fire(name, params))
18 } 18 }
19 } 19 }
20 20
21 public registerTypes (names: string[]) { 21 public registerTypes (names: string[]) {
22 for (let name of names) { 22 for (const name of names) {
23 this.eventRegistrations[ name ] = { registrations: [] } 23 this.eventRegistrations[ name ] = { registrations: [] }
24 } 24 }
25 } 25 }
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html
index f79cf68df..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>
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 @@
1import './embed.scss' 1import './embed.scss'
2 2
3import 'core-js/es6/symbol'
4import 'core-js/es6/object'
5import 'core-js/es6/function'
6import 'core-js/es6/parse-int'
7import 'core-js/es6/parse-float'
8import 'core-js/es6/number'
9import 'core-js/es6/math'
10import 'core-js/es6/string'
11import 'core-js/es6/date'
12import 'core-js/es6/array'
13import 'core-js/es6/regexp'
14import 'core-js/es6/map'
15import 'core-js/es6/weak-map'
16import 'core-js/es6/set'
17// For google bot that uses Chrome 41 and does not understand fetch
18import 'whatwg-fetch'
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' 3import * as Channel from 'jschannel'
25 4
26import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared' 5import { peertubeTranslate, ResultList, ServerConfig, VideoDetails } from '../../../../shared'
27import { addContextMenu, getServerTranslations, getVideojsOptions, loadLocaleInVideoJS } from '../../assets/player/peertube-player'
28import { PeerTubeResolution } from '../player/definitions' 6import { PeerTubeResolution } from '../player/definitions'
29import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' 7import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
30import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' 8import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
9import {
10 P2PMediaLoaderOptions,
11 PeertubePlayerManager,
12 PeertubePlayerManagerOptions,
13 PlayerMode
14} from '../../assets/player/peertube-player-manager'
15import { 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 }) {
diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts
index 30a298573..8e83d92a9 100644
--- a/client/src/standalone/videos/test-embed.ts
+++ b/client/src/standalone/videos/test-embed.ts
@@ -7,13 +7,13 @@ window.addEventListener('load', async () => {
7 const lastPart = urlParts[ urlParts.length - 1 ] 7 const lastPart = urlParts[ urlParts.length - 1 ]
8 const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] 8 const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
9 9
10 let iframe = document.createElement('iframe') 10 const iframe = document.createElement('iframe')
11 iframe.src = `/videos/embed/${videoId}?autoplay=1&controls=0&api=1` 11 iframe.src = `/videos/embed/${videoId}?autoplay=1&controls=0&api=1`
12 let mainElement = document.querySelector('#host') 12 const mainElement = document.querySelector('#host')
13 mainElement.appendChild(iframe) 13 mainElement.appendChild(iframe)
14 14
15 console.log(`Document finished loading.`) 15 console.log(`Document finished loading.`)
16 let player = new PeerTubePlayer(document.querySelector('iframe')) 16 const player = new PeerTubePlayer(document.querySelector('iframe'))
17 17
18 window[ 'player' ] = player 18 window[ 'player' ] = player
19 19
@@ -21,7 +21,7 @@ window.addEventListener('load', async () => {
21 await player.ready 21 await player.ready
22 console.log(`Player is ready.`) 22 console.log(`Player is ready.`)
23 23
24 let monitoredEvents = [ 24 const monitoredEvents = [
25 'pause', 25 'pause',
26 'play', 26 'play',
27 'playbackStatusUpdate', 27 'playbackStatusUpdate',
@@ -36,18 +36,18 @@ window.addEventListener('load', async () => {
36 let playbackRates: number[] = [] 36 let playbackRates: number[] = []
37 let currentRate = await player.getPlaybackRate() 37 let currentRate = await player.getPlaybackRate()
38 38
39 let updateRates = async () => { 39 const updateRates = async () => {
40 let rateListEl = document.querySelector('#rate-list') 40 const rateListEl = document.querySelector('#rate-list')
41 rateListEl.innerHTML = '' 41 rateListEl.innerHTML = ''
42 42
43 playbackRates.forEach(rate => { 43 playbackRates.forEach(rate => {
44 if (currentRate === rate) { 44 if (currentRate === rate) {
45 let itemEl = document.createElement('strong') 45 const itemEl = document.createElement('strong')
46 itemEl.innerText = `${rate} (active)` 46 itemEl.innerText = `${rate} (active)`
47 itemEl.style.display = 'block' 47 itemEl.style.display = 'block'
48 rateListEl.appendChild(itemEl) 48 rateListEl.appendChild(itemEl)
49 } else { 49 } else {
50 let itemEl = document.createElement('a') 50 const itemEl = document.createElement('a')
51 itemEl.href = 'javascript:;' 51 itemEl.href = 'javascript:;'
52 itemEl.innerText = rate.toString() 52 itemEl.innerText = rate.toString()
53 itemEl.addEventListener('click', () => { 53 itemEl.addEventListener('click', () => {
@@ -66,18 +66,18 @@ window.addEventListener('load', async () => {
66 updateRates() 66 updateRates()
67 }) 67 })
68 68
69 let updateResolutions = ((resolutions: PeerTubeResolution[]) => { 69 const updateResolutions = ((resolutions: PeerTubeResolution[]) => {
70 let resolutionListEl = document.querySelector('#resolution-list') 70 const resolutionListEl = document.querySelector('#resolution-list')
71 resolutionListEl.innerHTML = '' 71 resolutionListEl.innerHTML = ''
72 72
73 resolutions.forEach(resolution => { 73 resolutions.forEach(resolution => {
74 if (resolution.active) { 74 if (resolution.active) {
75 let itemEl = document.createElement('strong') 75 const itemEl = document.createElement('strong')
76 itemEl.innerText = `${resolution.label} (active)` 76 itemEl.innerText = `${resolution.label} (active)`
77 itemEl.style.display = 'block' 77 itemEl.style.display = 'block'
78 resolutionListEl.appendChild(itemEl) 78 resolutionListEl.appendChild(itemEl)
79 } else { 79 } else {
80 let itemEl = document.createElement('a') 80 const itemEl = document.createElement('a')
81 itemEl.href = 'javascript:;' 81 itemEl.href = 'javascript:;'
82 itemEl.innerText = resolution.label 82 itemEl.innerText = resolution.label
83 itemEl.addEventListener('click', () => { 83 itemEl.addEventListener('click', () => {