diff options
Diffstat (limited to 'client/src/standalone/videos')
-rw-r--r-- | client/src/standalone/videos/embed.ts | 120 | ||||
-rw-r--r-- | client/src/standalone/videos/test-embed.ts | 32 |
2 files changed, 72 insertions, 80 deletions
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index e9baf64d0..a4196600a 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -22,23 +22,21 @@ import * as Channel from 'jschannel' | |||
22 | 22 | ||
23 | import { VideoDetails } from '../../../../shared' | 23 | import { VideoDetails } from '../../../../shared' |
24 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player' | 24 | import { addContextMenu, getVideojsOptions, loadLocale } from '../../assets/player/peertube-player' |
25 | import { PeerTubeResolution } from '../player/definitions'; | 25 | import { PeerTubeResolution } from '../player/definitions' |
26 | 26 | ||
27 | /** | 27 | /** |
28 | * Embed API exposes control of the embed player to the outside world via | 28 | * Embed API exposes control of the embed player to the outside world via |
29 | * JSChannels and window.postMessage | 29 | * JSChannels and window.postMessage |
30 | */ | 30 | */ |
31 | class PeerTubeEmbedApi { | 31 | class PeerTubeEmbedApi { |
32 | constructor( | 32 | private channel: Channel.MessagingChannel |
33 | private embed : PeerTubeEmbed | ||
34 | ) { | ||
35 | } | ||
36 | |||
37 | private channel : Channel.MessagingChannel | ||
38 | private isReady = false | 33 | private isReady = false |
39 | private resolutions : PeerTubeResolution[] = null | 34 | private resolutions: PeerTubeResolution[] = null |
35 | |||
36 | constructor (private embed: PeerTubeEmbed) { | ||
37 | } | ||
40 | 38 | ||
41 | initialize() { | 39 | initialize () { |
42 | this.constructChannel() | 40 | this.constructChannel() |
43 | this.setupStateTracking() | 41 | this.setupStateTracking() |
44 | 42 | ||
@@ -46,14 +44,14 @@ class PeerTubeEmbedApi { | |||
46 | 44 | ||
47 | this.notifyReady() | 45 | this.notifyReady() |
48 | } | 46 | } |
49 | 47 | ||
50 | private get element() { | 48 | private get element () { |
51 | return this.embed.videoElement | 49 | return this.embed.videoElement |
52 | } | 50 | } |
53 | 51 | ||
54 | private constructChannel() { | 52 | private constructChannel () { |
55 | let channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope }) | 53 | let channel = Channel.build({ window: window.parent, origin: '*', scope: this.embed.scope }) |
56 | 54 | ||
57 | channel.bind('play', (txn, params) => this.embed.player.play()) | 55 | channel.bind('play', (txn, params) => this.embed.player.play()) |
58 | channel.bind('pause', (txn, params) => this.embed.player.pause()) | 56 | channel.bind('pause', (txn, params) => this.embed.player.pause()) |
59 | channel.bind('seek', (txn, time) => this.embed.player.currentTime(time)) | 57 | channel.bind('seek', (txn, time) => this.embed.player.currentTime(time)) |
@@ -69,9 +67,8 @@ class PeerTubeEmbedApi { | |||
69 | this.channel = channel | 67 | this.channel = channel |
70 | } | 68 | } |
71 | 69 | ||
72 | private setResolution(resolutionId : number) { | 70 | private setResolution (resolutionId: number) { |
73 | if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) | 71 | if (resolutionId === -1 && this.embed.player.peertube().isAutoResolutionForbidden()) return |
74 | return | ||
75 | 72 | ||
76 | // Auto resolution | 73 | // Auto resolution |
77 | if (resolutionId === -1) { | 74 | if (resolutionId === -1) { |
@@ -86,14 +83,13 @@ class PeerTubeEmbedApi { | |||
86 | /** | 83 | /** |
87 | * Let the host know that we're ready to go! | 84 | * Let the host know that we're ready to go! |
88 | */ | 85 | */ |
89 | private notifyReady() { | 86 | private notifyReady () { |
90 | this.isReady = true | 87 | this.isReady = true |
91 | this.channel.notify({ method: 'ready', params: true }) | 88 | this.channel.notify({ method: 'ready', params: true }) |
92 | } | 89 | } |
93 | 90 | ||
94 | private setupStateTracking() { | 91 | private setupStateTracking () { |
95 | 92 | let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted' | |
96 | let currentState : 'playing' | 'paused' | 'unstarted' = 'unstarted' | ||
97 | 93 | ||
98 | setInterval(() => { | 94 | setInterval(() => { |
99 | let position = this.element.currentTime | 95 | let position = this.element.currentTime |
@@ -104,7 +100,7 @@ class PeerTubeEmbedApi { | |||
104 | params: { | 100 | params: { |
105 | position, | 101 | position, |
106 | volume, | 102 | volume, |
107 | playbackState: currentState, | 103 | playbackState: currentState |
108 | } | 104 | } |
109 | }) | 105 | }) |
110 | }, 500) | 106 | }, 500) |
@@ -125,7 +121,7 @@ class PeerTubeEmbedApi { | |||
125 | this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) | 121 | this.embed.player.peertube().on('videoFileUpdate', () => this.loadResolutions()) |
126 | } | 122 | } |
127 | 123 | ||
128 | private loadResolutions() { | 124 | private loadResolutions () { |
129 | let resolutions = [] | 125 | let resolutions = [] |
130 | let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() | 126 | let currentResolutionId = this.embed.player.peertube().getCurrentResolutionId() |
131 | 127 | ||
@@ -152,30 +148,28 @@ class PeerTubeEmbedApi { | |||
152 | } | 148 | } |
153 | 149 | ||
154 | class PeerTubeEmbed { | 150 | class PeerTubeEmbed { |
155 | constructor( | 151 | videoElement: HTMLVideoElement |
156 | private videoContainerId : string | 152 | player: any |
157 | ) { | 153 | playerOptions: any |
158 | this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement | 154 | api: PeerTubeEmbedApi = null |
159 | } | 155 | autoplay = false |
160 | 156 | controls = true | |
161 | videoElement : HTMLVideoElement | 157 | muted = false |
162 | player : any | 158 | loop = false |
163 | playerOptions : any | 159 | enableApi = false |
164 | api : PeerTubeEmbedApi = null | 160 | startTime = 0 |
165 | autoplay : boolean = false | 161 | scope = 'peertube' |
166 | controls : boolean = true | 162 | |
167 | muted : boolean = false | 163 | static async main () { |
168 | loop : boolean = false | ||
169 | enableApi : boolean = false | ||
170 | startTime : number = 0 | ||
171 | scope : string = 'peertube' | ||
172 | |||
173 | static async main() { | ||
174 | const videoContainerId = 'video-container' | 164 | const videoContainerId = 'video-container' |
175 | const embed = new PeerTubeEmbed(videoContainerId) | 165 | const embed = new PeerTubeEmbed(videoContainerId) |
176 | await embed.init() | 166 | await embed.init() |
177 | } | 167 | } |
178 | 168 | ||
169 | constructor (private videoContainerId: string) { | ||
170 | this.videoElement = document.getElementById(videoContainerId) as HTMLVideoElement | ||
171 | } | ||
172 | |||
179 | getVideoUrl (id: string) { | 173 | getVideoUrl (id: string) { |
180 | return window.location.origin + '/api/v1/videos/' + id | 174 | return window.location.origin + '/api/v1/videos/' + id |
181 | } | 175 | } |
@@ -219,15 +213,7 @@ class PeerTubeEmbed { | |||
219 | return params.has(name) ? params.get(name) : defaultValue | 213 | return params.has(name) ? params.get(name) : defaultValue |
220 | } | 214 | } |
221 | 215 | ||
222 | private initializeApi() { | 216 | async init () { |
223 | if (!this.enableApi) | ||
224 | return | ||
225 | |||
226 | this.api = new PeerTubeEmbedApi(this) | ||
227 | this.api.initialize() | ||
228 | } | ||
229 | |||
230 | async init() { | ||
231 | try { | 217 | try { |
232 | await this.initCore() | 218 | await this.initCore() |
233 | } catch (e) { | 219 | } catch (e) { |
@@ -235,7 +221,14 @@ class PeerTubeEmbed { | |||
235 | } | 221 | } |
236 | } | 222 | } |
237 | 223 | ||
238 | private loadParams() { | 224 | private initializeApi () { |
225 | if (!this.enableApi) return | ||
226 | |||
227 | this.api = new PeerTubeEmbedApi(this) | ||
228 | this.api.initialize() | ||
229 | } | ||
230 | |||
231 | private loadParams () { | ||
239 | try { | 232 | try { |
240 | let params = new URL(window.location.toString()).searchParams | 233 | let params = new URL(window.location.toString()).searchParams |
241 | 234 | ||
@@ -248,24 +241,23 @@ class PeerTubeEmbed { | |||
248 | 241 | ||
249 | const startTimeParamString = params.get('start') | 242 | const startTimeParamString = params.get('start') |
250 | const startTimeParamNumber = parseInt(startTimeParamString, 10) | 243 | const startTimeParamNumber = parseInt(startTimeParamString, 10) |
251 | if (isNaN(startTimeParamNumber) === false) | 244 | |
252 | this.startTime = startTimeParamNumber | 245 | if (isNaN(startTimeParamNumber) === false) this.startTime = startTimeParamNumber |
253 | } catch (err) { | 246 | } catch (err) { |
254 | console.error('Cannot get params from URL.', err) | 247 | console.error('Cannot get params from URL.', err) |
255 | } | 248 | } |
256 | } | 249 | } |
257 | 250 | ||
258 | private async initCore() { | 251 | private async initCore () { |
259 | const urlParts = window.location.href.split('/') | 252 | const urlParts = window.location.href.split('/') |
260 | const lastPart = urlParts[urlParts.length - 1] | 253 | const lastPart = urlParts[ urlParts.length - 1 ] |
261 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[0] | 254 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] |
262 | 255 | ||
263 | await loadLocale(window.location.origin, vjs, navigator.language) | 256 | await loadLocale(window.location.origin, vjs, navigator.language) |
264 | let response = await this.loadVideoInfo(videoId) | 257 | let response = await this.loadVideoInfo(videoId) |
265 | 258 | ||
266 | if (!response.ok) { | 259 | if (!response.ok) { |
267 | if (response.status === 404) | 260 | if (response.status === 404) return this.videoNotFound(this.videoElement) |
268 | return this.videoNotFound(this.videoElement) | ||
269 | 261 | ||
270 | return this.videoFetchError(this.videoElement) | 262 | return this.videoFetchError(this.videoElement) |
271 | } | 263 | } |
@@ -279,7 +271,7 @@ class PeerTubeEmbed { | |||
279 | controls: this.controls, | 271 | controls: this.controls, |
280 | muted: this.muted, | 272 | muted: this.muted, |
281 | loop: this.loop, | 273 | loop: this.loop, |
282 | startTime : this.startTime, | 274 | startTime: this.startTime, |
283 | 275 | ||
284 | inactivityTimeout: 1500, | 276 | inactivityTimeout: 1500, |
285 | videoViewUrl: this.getVideoUrl(videoId) + '/views', | 277 | videoViewUrl: this.getVideoUrl(videoId) + '/views', |
@@ -295,14 +287,15 @@ class PeerTubeEmbed { | |||
295 | this.playerOptions = videojsOptions | 287 | this.playerOptions = videojsOptions |
296 | this.player = vjs(this.videoContainerId, videojsOptions, () => { | 288 | this.player = vjs(this.videoContainerId, videojsOptions, () => { |
297 | 289 | ||
298 | window['videojsPlayer'] = this.player | 290 | window[ 'videojsPlayer' ] = this.player |
299 | 291 | ||
300 | if (this.controls) { | 292 | if (this.controls) { |
301 | (this.player as any).dock({ | 293 | this.player.dock({ |
302 | title: videoInfo.name, | 294 | title: videoInfo.name, |
303 | description: this.player.localize('Uses P2P, others may know your IP is downloading this video.') | 295 | description: this.player.localize('Uses P2P, others may know your IP is downloading this video.') |
304 | }) | 296 | }) |
305 | } | 297 | } |
298 | |||
306 | addContextMenu(this.player, window.location.origin + videoInfo.embedPath) | 299 | addContextMenu(this.player, window.location.origin + videoInfo.embedPath) |
307 | this.initializeApi() | 300 | this.initializeApi() |
308 | }) | 301 | }) |
@@ -310,3 +303,4 @@ class PeerTubeEmbed { | |||
310 | } | 303 | } |
311 | 304 | ||
312 | PeerTubeEmbed.main() | 305 | PeerTubeEmbed.main() |
306 | .catch(err => console.error('Cannot init embed.', err)) | ||
diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts index 721514488..c0181ed28 100644 --- a/client/src/standalone/videos/test-embed.ts +++ b/client/src/standalone/videos/test-embed.ts | |||
@@ -1,49 +1,47 @@ | |||
1 | import './test-embed.scss' | 1 | import './test-embed.scss' |
2 | import { PeerTubePlayer } from '../player/player'; | 2 | import { PeerTubePlayer } from '../player/player' |
3 | import { PlayerEventType } from '../player/definitions'; | 3 | import { PlayerEventType } from '../player/definitions' |
4 | 4 | ||
5 | window.addEventListener('load', async () => { | 5 | window.addEventListener('load', async () => { |
6 | |||
7 | const urlParts = window.location.href.split('/') | 6 | const urlParts = window.location.href.split('/') |
8 | const lastPart = urlParts[urlParts.length - 1] | 7 | const lastPart = urlParts[ urlParts.length - 1 ] |
9 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[0] | 8 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] |
10 | 9 | ||
11 | let iframe = document.createElement('iframe') | 10 | let iframe = document.createElement('iframe') |
12 | iframe.src = `/videos/embed/${videoId}?autoplay=1&controls=0&api=1` | 11 | iframe.src = `/videos/embed/${videoId}?autoplay=1&controls=0&api=1` |
13 | let mainElement = document.querySelector('#host') | 12 | let mainElement = document.querySelector('#host') |
14 | mainElement.appendChild(iframe); | 13 | mainElement.appendChild(iframe) |
15 | 14 | ||
16 | console.log(`Document finished loading.`) | 15 | console.log(`Document finished loading.`) |
17 | let player = new PeerTubePlayer(document.querySelector('iframe')) | 16 | let player = new PeerTubePlayer(document.querySelector('iframe')) |
18 | 17 | ||
19 | window['player'] = player | 18 | window[ 'player' ] = player |
20 | 19 | ||
21 | console.log(`Awaiting player ready...`) | 20 | console.log(`Awaiting player ready...`) |
22 | await player.ready | 21 | await player.ready |
23 | console.log(`Player is ready.`) | 22 | console.log(`Player is ready.`) |
24 | 23 | ||
25 | let monitoredEvents = [ | 24 | let monitoredEvents = [ |
26 | 'pause', 'play', | 25 | 'pause', |
27 | 'playbackStatusUpdate', | 26 | 'play', |
27 | 'playbackStatusUpdate', | ||
28 | 'playbackStatusChange' | 28 | 'playbackStatusChange' |
29 | ] | 29 | ] |
30 | 30 | ||
31 | monitoredEvents.forEach(e => { | 31 | monitoredEvents.forEach(e => { |
32 | player.addEventListener(<PlayerEventType>e, () => console.log(`PLAYER: event '${e}' received`)) | 32 | player.addEventListener(e as PlayerEventType, () => console.log(`PLAYER: event '${e}' received`)) |
33 | console.log(`PLAYER: now listening for event '${e}'`) | 33 | console.log(`PLAYER: now listening for event '${e}'`) |
34 | }) | 34 | }) |
35 | 35 | ||
36 | let playbackRates = [] | 36 | let playbackRates: number[] = [] |
37 | let activeRate = 1 | ||
38 | let currentRate = await player.getPlaybackRate() | 37 | let currentRate = await player.getPlaybackRate() |
39 | 38 | ||
40 | let updateRates = async () => { | 39 | let updateRates = async () => { |
41 | |||
42 | let rateListEl = document.querySelector('#rate-list') | 40 | let rateListEl = document.querySelector('#rate-list') |
43 | rateListEl.innerHTML = '' | 41 | rateListEl.innerHTML = '' |
44 | 42 | ||
45 | playbackRates.forEach(rate => { | 43 | playbackRates.forEach(rate => { |
46 | if (currentRate == rate) { | 44 | if (currentRate === rate) { |
47 | let itemEl = document.createElement('strong') | 45 | let itemEl = document.createElement('strong') |
48 | itemEl.innerText = `${rate} (active)` | 46 | itemEl.innerText = `${rate} (active)` |
49 | itemEl.style.display = 'block' | 47 | itemEl.style.display = 'block' |
@@ -93,6 +91,6 @@ window.addEventListener('load', async () => { | |||
93 | 91 | ||
94 | player.getResolutions().then( | 92 | player.getResolutions().then( |
95 | resolutions => updateResolutions(resolutions)) | 93 | resolutions => updateResolutions(resolutions)) |
96 | player.addEventListener('resolutionUpdate', | 94 | player.addEventListener('resolutionUpdate', |
97 | resolutions => updateResolutions(resolutions)) | 95 | resolutions => updateResolutions(resolutions)) |
98 | }) \ No newline at end of file | 96 | }) |