diff options
Diffstat (limited to 'client/src/standalone/videos')
-rw-r--r-- | client/src/standalone/videos/embed-api.ts | 34 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 46 | ||||
-rw-r--r-- | client/src/standalone/videos/test-embed.html | 6 | ||||
-rw-r--r-- | client/src/standalone/videos/test-embed.ts | 36 |
4 files changed, 94 insertions, 28 deletions
diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts index 61e5d0b9a..a9263555d 100644 --- a/client/src/standalone/videos/embed-api.ts +++ b/client/src/standalone/videos/embed-api.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import './embed.scss' | 1 | import './embed.scss' |
2 | 2 | ||
3 | import * as Channel from 'jschannel' | 3 | import * as Channel from 'jschannel' |
4 | import { PeerTubeResolution } from '../player/definitions' | 4 | import { PeerTubeResolution, PeerTubeTextTrack } from '../player/definitions' |
5 | import { PeerTubeEmbed } from './embed' | 5 | import { PeerTubeEmbed } from './embed' |
6 | 6 | ||
7 | /** | 7 | /** |
@@ -44,6 +44,9 @@ export class PeerTubeEmbedApi { | |||
44 | channel.bind('setResolution', (txn, resolutionId) => this.setResolution(resolutionId)) | 44 | channel.bind('setResolution', (txn, resolutionId) => this.setResolution(resolutionId)) |
45 | channel.bind('getResolutions', (txn, params) => this.resolutions) | 45 | channel.bind('getResolutions', (txn, params) => this.resolutions) |
46 | 46 | ||
47 | channel.bind('getCaptions', (txn, params) => this.getCaptions()) | ||
48 | channel.bind('setCaption', (txn, id) => this.setCaption(id)), | ||
49 | |||
47 | channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate)) | 50 | channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate)) |
48 | channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate()) | 51 | channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate()) |
49 | channel.bind('getPlaybackRates', (txn, params) => this.embed.player.options_.playbackRates) | 52 | channel.bind('getPlaybackRates', (txn, params) => this.embed.player.options_.playbackRates) |
@@ -71,6 +74,26 @@ export class PeerTubeEmbedApi { | |||
71 | this.embed.player.p2pMediaLoader().getHLSJS().nextLevel = resolutionId | 74 | this.embed.player.p2pMediaLoader().getHLSJS().nextLevel = resolutionId |
72 | } | 75 | } |
73 | 76 | ||
77 | private getCaptions (): PeerTubeTextTrack[] { | ||
78 | return this.embed.player.textTracks().tracks_.map(t => { | ||
79 | return { | ||
80 | id: t.id, | ||
81 | src: t.src, | ||
82 | label: t.label, | ||
83 | mode: t.mode as any | ||
84 | } | ||
85 | }) | ||
86 | } | ||
87 | |||
88 | private setCaption (id: string) { | ||
89 | const tracks = this.embed.player.textTracks().tracks_ | ||
90 | |||
91 | for (const track of tracks) { | ||
92 | if (track.id === id) track.mode = 'showing' | ||
93 | else track.mode = 'disabled' | ||
94 | } | ||
95 | } | ||
96 | |||
74 | /** | 97 | /** |
75 | * Let the host know that we're ready to go! | 98 | * Let the host know that we're ready to go! |
76 | */ | 99 | */ |
@@ -80,17 +103,19 @@ export class PeerTubeEmbedApi { | |||
80 | } | 103 | } |
81 | 104 | ||
82 | private setupStateTracking () { | 105 | private setupStateTracking () { |
83 | let currentState: 'playing' | 'paused' | 'unstarted' = 'unstarted' | 106 | let currentState: 'playing' | 'paused' | 'unstarted' | 'ended' = 'unstarted' |
84 | 107 | ||
85 | setInterval(() => { | 108 | setInterval(() => { |
86 | const position = this.element.currentTime | 109 | const position = this.element.currentTime |
87 | const volume = this.element.volume | 110 | const volume = this.element.volume |
111 | const duration = this.element.duration | ||
88 | 112 | ||
89 | this.channel.notify({ | 113 | this.channel.notify({ |
90 | method: 'playbackStatusUpdate', | 114 | method: 'playbackStatusUpdate', |
91 | params: { | 115 | params: { |
92 | position, | 116 | position, |
93 | volume, | 117 | volume, |
118 | duration: this.embed.player.duration(), | ||
94 | playbackState: currentState | 119 | playbackState: currentState |
95 | } | 120 | } |
96 | }) | 121 | }) |
@@ -106,6 +131,11 @@ export class PeerTubeEmbedApi { | |||
106 | this.channel.notify({ method: 'playbackStatusChange', params: 'paused' }) | 131 | this.channel.notify({ method: 'playbackStatusChange', params: 'paused' }) |
107 | }) | 132 | }) |
108 | 133 | ||
134 | this.element.addEventListener('ended', ev => { | ||
135 | currentState = 'ended' | ||
136 | this.channel.notify({ method: 'playbackStatusChange', params: 'ended' }) | ||
137 | }) | ||
138 | |||
109 | // PeerTube specific capabilities | 139 | // PeerTube specific capabilities |
110 | 140 | ||
111 | if (this.isWebtorrent()) { | 141 | if (this.isWebtorrent()) { |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 5213443fc..286757e5e 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -1,15 +1,11 @@ | |||
1 | import './embed.scss' | 1 | import './embed.scss' |
2 | 2 | ||
3 | import { | 3 | import { |
4 | getCompleteLocale, | ||
5 | is18nLocale, | ||
6 | isDefaultLocale, | ||
7 | peertubeTranslate, | 4 | peertubeTranslate, |
8 | ResultList, | 5 | ResultList, |
9 | ServerConfig, | 6 | ServerConfig, |
10 | VideoDetails | 7 | VideoDetails |
11 | } from '../../../../shared' | 8 | } from '../../../../shared' |
12 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | ||
13 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 9 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' |
14 | import { | 10 | import { |
15 | P2PMediaLoaderOptions, | 11 | P2PMediaLoaderOptions, |
@@ -19,10 +15,14 @@ import { | |||
19 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' | 15 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' |
20 | import { PeerTubeEmbedApi } from './embed-api' | 16 | import { PeerTubeEmbedApi } from './embed-api' |
21 | import { TranslationsManager } from '../../assets/player/translations-manager' | 17 | import { TranslationsManager } from '../../assets/player/translations-manager' |
18 | import videojs from 'video.js' | ||
19 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | ||
20 | |||
21 | type Translations = { [ id: string ]: string } | ||
22 | 22 | ||
23 | export class PeerTubeEmbed { | 23 | export class PeerTubeEmbed { |
24 | videoElement: HTMLVideoElement | 24 | videoElement: HTMLVideoElement |
25 | player: any | 25 | player: videojs.Player |
26 | api: PeerTubeEmbedApi = null | 26 | api: PeerTubeEmbedApi = null |
27 | autoplay: boolean | 27 | autoplay: boolean |
28 | controls: boolean | 28 | controls: boolean |
@@ -71,7 +71,7 @@ export class PeerTubeEmbed { | |||
71 | element.parentElement.removeChild(element) | 71 | element.parentElement.removeChild(element) |
72 | } | 72 | } |
73 | 73 | ||
74 | displayError (text: string, translations?: { [ id: string ]: string }) { | 74 | displayError (text: string, translations?: Translations) { |
75 | // Remove video element | 75 | // Remove video element |
76 | if (this.videoElement) this.removeElement(this.videoElement) | 76 | if (this.videoElement) this.removeElement(this.videoElement) |
77 | 77 | ||
@@ -90,12 +90,12 @@ export class PeerTubeEmbed { | |||
90 | errorText.innerHTML = translatedText | 90 | errorText.innerHTML = translatedText |
91 | } | 91 | } |
92 | 92 | ||
93 | videoNotFound (translations?: { [ id: string ]: string }) { | 93 | videoNotFound (translations?: Translations) { |
94 | const text = 'This video does not exist.' | 94 | const text = 'This video does not exist.' |
95 | this.displayError(text, translations) | 95 | this.displayError(text, translations) |
96 | } | 96 | } |
97 | 97 | ||
98 | videoFetchError (translations?: { [ id: string ]: string }) { | 98 | videoFetchError (translations?: Translations) { |
99 | const text = 'We cannot fetch the video. Please try again later.' | 99 | const text = 'We cannot fetch the video. Please try again later.' |
100 | this.displayError(text, translations) | 100 | this.displayError(text, translations) |
101 | } | 101 | } |
@@ -129,7 +129,7 @@ export class PeerTubeEmbed { | |||
129 | 129 | ||
130 | this.autoplay = this.getParamToggle(params, 'autoplay', false) | 130 | this.autoplay = this.getParamToggle(params, 'autoplay', false) |
131 | this.controls = this.getParamToggle(params, 'controls', true) | 131 | this.controls = this.getParamToggle(params, 'controls', true) |
132 | this.muted = this.getParamToggle(params, 'muted', false) | 132 | this.muted = this.getParamToggle(params, 'muted', undefined) |
133 | this.loop = this.getParamToggle(params, 'loop', false) | 133 | this.loop = this.getParamToggle(params, 'loop', false) |
134 | this.title = this.getParamToggle(params, 'title', true) | 134 | this.title = this.getParamToggle(params, 'title', true) |
135 | this.enableApi = this.getParamToggle(params, 'api', this.enableApi) | 135 | this.enableApi = this.getParamToggle(params, 'api', this.enableApi) |
@@ -237,7 +237,7 @@ export class PeerTubeEmbed { | |||
237 | }) | 237 | }) |
238 | } | 238 | } |
239 | 239 | ||
240 | this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: any) => this.player = player) | 240 | this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: videojs.Player) => this.player = player) |
241 | this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) | 241 | this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) |
242 | 242 | ||
243 | window[ 'videojsPlayer' ] = this.player | 243 | window[ 'videojsPlayer' ] = this.player |
@@ -261,22 +261,22 @@ export class PeerTubeEmbed { | |||
261 | } | 261 | } |
262 | 262 | ||
263 | private async buildDock (videoInfo: VideoDetails, configResponse: Response) { | 263 | private async buildDock (videoInfo: VideoDetails, configResponse: Response) { |
264 | if (this.controls) { | 264 | if (!this.controls) return |
265 | // On webtorrent fallback, player may have been disposed | ||
266 | if (!this.player.player_) return | ||
267 | 265 | ||
268 | const title = this.title ? videoInfo.name : undefined | 266 | // On webtorrent fallback, player may have been disposed |
267 | if (!this.player.player_) return | ||
269 | 268 | ||
270 | const config: ServerConfig = await configResponse.json() | 269 | const title = this.title ? videoInfo.name : undefined |
271 | const description = config.tracker.enabled && this.warningTitle | ||
272 | ? '<span class="text">' + this.player.localize('Watching this video may reveal your IP address to others.') + '</span>' | ||
273 | : undefined | ||
274 | 270 | ||
275 | this.player.dock({ | 271 | const config: ServerConfig = await configResponse.json() |
276 | title, | 272 | const description = config.tracker.enabled && this.warningTitle |
277 | description | 273 | ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>' |
278 | }) | 274 | : undefined |
279 | } | 275 | |
276 | this.player.dock({ | ||
277 | title, | ||
278 | description | ||
279 | }) | ||
280 | } | 280 | } |
281 | 281 | ||
282 | private buildCSS () { | 282 | private buildCSS () { |
diff --git a/client/src/standalone/videos/test-embed.html b/client/src/standalone/videos/test-embed.html index 20cdbdc5f..9e1d6fc61 100644 --- a/client/src/standalone/videos/test-embed.html +++ b/client/src/standalone/videos/test-embed.html | |||
@@ -38,6 +38,12 @@ | |||
38 | </fieldset> | 38 | </fieldset> |
39 | 39 | ||
40 | <fieldset> | 40 | <fieldset> |
41 | <legend>Captions:</legend> | ||
42 | <div id="caption-list"></div> | ||
43 | <br/> | ||
44 | </fieldset> | ||
45 | |||
46 | <fieldset> | ||
41 | <legend>Rates:</legend> | 47 | <legend>Rates:</legend> |
42 | <div id="rate-list"></div> | 48 | <div id="rate-list"></div> |
43 | </fieldset> | 49 | </fieldset> |
diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts index e5e6365dc..24cb62230 100644 --- a/client/src/standalone/videos/test-embed.ts +++ b/client/src/standalone/videos/test-embed.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import './test-embed.scss' | 1 | import './test-embed.scss' |
2 | import { PeerTubePlayer } from '../player/player' | 2 | import { PeerTubePlayer } from '../player/player' |
3 | import { PeerTubeResolution, PlayerEventType } from '../player/definitions' | 3 | import { PeerTubeResolution, PlayerEventType, PeerTubeTextTrack } from '../player/definitions' |
4 | 4 | ||
5 | window.addEventListener('load', async () => { | 5 | window.addEventListener('load', async () => { |
6 | const urlParts = window.location.href.split('/') | 6 | const urlParts = window.location.href.split('/') |
@@ -8,7 +8,7 @@ window.addEventListener('load', async () => { | |||
8 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] | 8 | const videoId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ] |
9 | 9 | ||
10 | const 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}?api=1` |
12 | 12 | ||
13 | const mainElement = document.querySelector('#host') | 13 | const mainElement = document.querySelector('#host') |
14 | mainElement.appendChild(iframe) | 14 | mainElement.appendChild(iframe) |
@@ -30,7 +30,7 @@ window.addEventListener('load', async () => { | |||
30 | ] | 30 | ] |
31 | 31 | ||
32 | monitoredEvents.forEach(e => { | 32 | monitoredEvents.forEach(e => { |
33 | player.addEventListener(e as PlayerEventType, () => console.log(`PLAYER: event '${e}' received`)) | 33 | player.addEventListener(e as PlayerEventType, (param) => console.log(`PLAYER: event '${e}' received`, param)) |
34 | console.log(`PLAYER: now listening for event '${e}'`) | 34 | console.log(`PLAYER: now listening for event '${e}'`) |
35 | }) | 35 | }) |
36 | 36 | ||
@@ -67,6 +67,36 @@ window.addEventListener('load', async () => { | |||
67 | updateRates() | 67 | updateRates() |
68 | }) | 68 | }) |
69 | 69 | ||
70 | const updateCaptions = async () => { | ||
71 | const captions = await player.getCaptions() | ||
72 | |||
73 | const captionEl = document.querySelector('#caption-list') | ||
74 | captionEl.innerHTML = '' | ||
75 | |||
76 | captions.forEach(c => { | ||
77 | console.log(c) | ||
78 | |||
79 | if (c.mode === 'showing') { | ||
80 | const itemEl = document.createElement('strong') | ||
81 | itemEl.innerText = `${c.label} (active)` | ||
82 | itemEl.style.display = 'block' | ||
83 | captionEl.appendChild(itemEl) | ||
84 | } else { | ||
85 | const itemEl = document.createElement('a') | ||
86 | itemEl.href = 'javascript:;' | ||
87 | itemEl.innerText = c.label | ||
88 | itemEl.addEventListener('click', () => { | ||
89 | player.setCaption(c.id) | ||
90 | updateCaptions() | ||
91 | }) | ||
92 | itemEl.style.display = 'block' | ||
93 | captionEl.appendChild(itemEl) | ||
94 | } | ||
95 | }) | ||
96 | } | ||
97 | |||
98 | updateCaptions() | ||
99 | |||
70 | const updateResolutions = ((resolutions: PeerTubeResolution[]) => { | 100 | const updateResolutions = ((resolutions: PeerTubeResolution[]) => { |
71 | const resolutionListEl = document.querySelector('#resolution-list') | 101 | const resolutionListEl = document.querySelector('#resolution-list') |
72 | resolutionListEl.innerHTML = '' | 102 | resolutionListEl.innerHTML = '' |