diff options
author | Chocobozzz <me@florianbigard.com> | 2019-12-17 11:20:24 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-12-18 10:14:22 +0100 |
commit | 3f9c4955af81702591a6eeb2069f99faf0d2814d (patch) | |
tree | 8f23b5e79bde4a46dbc2318c0500576c35712486 /client | |
parent | f88ee4a9523bf3c4a61a45832963c558aed4d0b1 (diff) | |
download | PeerTube-3f9c4955af81702591a6eeb2069f99faf0d2814d.tar.gz PeerTube-3f9c4955af81702591a6eeb2069f99faf0d2814d.tar.zst PeerTube-3f9c4955af81702591a6eeb2069f99faf0d2814d.zip |
Speedup embed first paint
Diffstat (limited to 'client')
-rw-r--r-- | client/src/app/videos/+video-watch/comment/video-comment-add.component.ts | 8 | ||||
-rw-r--r-- | client/src/assets/player/peertube-player-manager.ts | 54 | ||||
-rw-r--r-- | client/src/assets/player/translations-manager.ts | 52 | ||||
-rw-r--r-- | client/src/standalone/videos/embed-api.ts | 1 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.html | 2 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.scss | 10 | ||||
-rw-r--r-- | client/src/standalone/videos/embed.ts | 54 |
7 files changed, 116 insertions, 65 deletions
diff --git a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts index 083509b83..1be96ad9e 100644 --- a/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts +++ b/client/src/app/videos/+video-watch/comment/video-comment-add.component.ts | |||
@@ -137,6 +137,10 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit { | |||
137 | this.router.navigate([ '/login' ]) | 137 | this.router.navigate([ '/login' ]) |
138 | } | 138 | } |
139 | 139 | ||
140 | cancelCommentReply () { | ||
141 | this.cancel.emit(null) | ||
142 | } | ||
143 | |||
140 | private addCommentReply (commentCreate: VideoCommentCreate) { | 144 | private addCommentReply (commentCreate: VideoCommentCreate) { |
141 | return this.videoCommentService | 145 | return this.videoCommentService |
142 | .addCommentReply(this.video.id, this.parentComment.id, commentCreate) | 146 | .addCommentReply(this.video.id, this.parentComment.id, commentCreate) |
@@ -146,8 +150,4 @@ export class VideoCommentAddComponent extends FormReactive implements OnInit { | |||
146 | return this.videoCommentService | 150 | return this.videoCommentService |
147 | .addCommentThread(this.video.id, commentCreate) | 151 | .addCommentThread(this.video.id, commentCreate) |
148 | } | 152 | } |
149 | |||
150 | private cancelCommentReply () { | ||
151 | this.cancel.emit(null) | ||
152 | } | ||
153 | } | 153 | } |
diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 2f4e0ac1a..b1551185a 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts | |||
@@ -15,11 +15,12 @@ import './videojs-components/peertube-load-progress-bar' | |||
15 | import './videojs-components/theater-button' | 15 | import './videojs-components/theater-button' |
16 | import { P2PMediaLoaderPluginOptions, UserWatching, VideoJSCaption, VideoJSPluginOptions, videojsUntyped } from './peertube-videojs-typings' | 16 | import { P2PMediaLoaderPluginOptions, UserWatching, VideoJSCaption, VideoJSPluginOptions, videojsUntyped } from './peertube-videojs-typings' |
17 | import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from './utils' | 17 | import { buildVideoEmbed, buildVideoLink, copyToClipboard, getRtcConfig } from './utils' |
18 | import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models/i18n/i18n' | 18 | import { isDefaultLocale } from '../../../../shared/models/i18n/i18n' |
19 | import { segmentValidatorFactory } from './p2p-media-loader/segment-validator' | 19 | import { segmentValidatorFactory } from './p2p-media-loader/segment-validator' |
20 | import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder' | 20 | import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder' |
21 | import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager' | 21 | import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager' |
22 | import { getStoredP2PEnabled } from './peertube-player-local-storage' | 22 | import { getStoredP2PEnabled } from './peertube-player-local-storage' |
23 | import { TranslationsManager } from './translations-manager' | ||
23 | 24 | ||
24 | // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) | 25 | // Change 'Playback Rate' to 'Speed' (smaller for our settings menu) |
25 | videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' | 26 | videojsUntyped.getComponent('PlaybackRateMenuButton').prototype.controlText_ = 'Speed' |
@@ -86,24 +87,9 @@ export type PeertubePlayerManagerOptions = { | |||
86 | } | 87 | } |
87 | 88 | ||
88 | export class PeertubePlayerManager { | 89 | export class PeertubePlayerManager { |
89 | |||
90 | private static videojsLocaleCache: { [ path: string ]: any } = {} | ||
91 | private static playerElementClassName: string | 90 | private static playerElementClassName: string |
92 | private static onPlayerChange: (player: any) => void | 91 | private static onPlayerChange: (player: any) => void |
93 | 92 | ||
94 | static getServerTranslations (serverUrl: string, locale: string) { | ||
95 | const path = PeertubePlayerManager.getLocalePath(serverUrl, locale) | ||
96 | // It is the default locale, nothing to translate | ||
97 | if (!path) return Promise.resolve(undefined) | ||
98 | |||
99 | return fetch(path + '/server.json') | ||
100 | .then(res => res.json()) | ||
101 | .catch(err => { | ||
102 | console.error('Cannot get server translations', err) | ||
103 | return undefined | ||
104 | }) | ||
105 | } | ||
106 | |||
107 | static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: any) => void) { | 93 | static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: any) => void) { |
108 | let p2pMediaLoader: any | 94 | let p2pMediaLoader: any |
109 | 95 | ||
@@ -120,7 +106,7 @@ export class PeertubePlayerManager { | |||
120 | 106 | ||
121 | const videojsOptions = this.getVideojsOptions(mode, options, p2pMediaLoader) | 107 | const videojsOptions = this.getVideojsOptions(mode, options, p2pMediaLoader) |
122 | 108 | ||
123 | await this.loadLocaleInVideoJS(options.common.serverUrl, options.common.language) | 109 | await TranslationsManager.loadLocaleInVideoJS(options.common.serverUrl, options.common.language, videojs) |
124 | 110 | ||
125 | const self = this | 111 | const self = this |
126 | return new Promise(res => { | 112 | return new Promise(res => { |
@@ -181,32 +167,6 @@ export class PeertubePlayerManager { | |||
181 | }) | 167 | }) |
182 | } | 168 | } |
183 | 169 | ||
184 | private static loadLocaleInVideoJS (serverUrl: string, locale: string) { | ||
185 | const path = PeertubePlayerManager.getLocalePath(serverUrl, locale) | ||
186 | // It is the default locale, nothing to translate | ||
187 | if (!path) return Promise.resolve(undefined) | ||
188 | |||
189 | let p: Promise<any> | ||
190 | |||
191 | if (PeertubePlayerManager.videojsLocaleCache[path]) { | ||
192 | p = Promise.resolve(PeertubePlayerManager.videojsLocaleCache[path]) | ||
193 | } else { | ||
194 | p = fetch(path + '/player.json') | ||
195 | .then(res => res.json()) | ||
196 | .then(json => { | ||
197 | PeertubePlayerManager.videojsLocaleCache[path] = json | ||
198 | return json | ||
199 | }) | ||
200 | .catch(err => { | ||
201 | console.error('Cannot get player translations', err) | ||
202 | return undefined | ||
203 | }) | ||
204 | } | ||
205 | |||
206 | const completeLocale = getCompleteLocale(locale) | ||
207 | return p.then(json => videojs.addLanguage(getShortLocale(completeLocale), json)) | ||
208 | } | ||
209 | |||
210 | private static getVideojsOptions (mode: PlayerMode, options: PeertubePlayerManagerOptions, p2pMediaLoaderModule?: any) { | 170 | private static getVideojsOptions (mode: PlayerMode, options: PeertubePlayerManagerOptions, p2pMediaLoaderModule?: any) { |
211 | const commonOptions = options.common | 171 | const commonOptions = options.common |
212 | 172 | ||
@@ -519,14 +479,6 @@ export class PeertubePlayerManager { | |||
519 | } | 479 | } |
520 | }) | 480 | }) |
521 | } | 481 | } |
522 | |||
523 | private static getLocalePath (serverUrl: string, locale: string) { | ||
524 | const completeLocale = getCompleteLocale(locale) | ||
525 | |||
526 | if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return undefined | ||
527 | |||
528 | return serverUrl + '/client/locales/' + completeLocale | ||
529 | } | ||
530 | } | 482 | } |
531 | 483 | ||
532 | // ############################################################################ | 484 | // ############################################################################ |
diff --git a/client/src/assets/player/translations-manager.ts b/client/src/assets/player/translations-manager.ts new file mode 100644 index 000000000..e9f300ce7 --- /dev/null +++ b/client/src/assets/player/translations-manager.ts | |||
@@ -0,0 +1,52 @@ | |||
1 | import { getCompleteLocale, getShortLocale, is18nLocale, isDefaultLocale } from '../../../../shared/models' | ||
2 | |||
3 | export class TranslationsManager { | ||
4 | private static videojsLocaleCache: { [ path: string ]: any } = {} | ||
5 | |||
6 | static getServerTranslations (serverUrl: string, locale: string) { | ||
7 | const path = TranslationsManager.getLocalePath(serverUrl, locale) | ||
8 | // It is the default locale, nothing to translate | ||
9 | if (!path) return Promise.resolve(undefined) | ||
10 | |||
11 | return fetch(path + '/server.json') | ||
12 | .then(res => res.json()) | ||
13 | .catch(err => { | ||
14 | console.error('Cannot get server translations', err) | ||
15 | return undefined | ||
16 | }) | ||
17 | } | ||
18 | |||
19 | static loadLocaleInVideoJS (serverUrl: string, locale: string, videojs: any) { | ||
20 | const path = TranslationsManager.getLocalePath(serverUrl, locale) | ||
21 | // It is the default locale, nothing to translate | ||
22 | if (!path) return Promise.resolve(undefined) | ||
23 | |||
24 | let p: Promise<any> | ||
25 | |||
26 | if (TranslationsManager.videojsLocaleCache[ path ]) { | ||
27 | p = Promise.resolve(TranslationsManager.videojsLocaleCache[ path ]) | ||
28 | } else { | ||
29 | p = fetch(path + '/player.json') | ||
30 | .then(res => res.json()) | ||
31 | .then(json => { | ||
32 | TranslationsManager.videojsLocaleCache[ path ] = json | ||
33 | return json | ||
34 | }) | ||
35 | .catch(err => { | ||
36 | console.error('Cannot get player translations', err) | ||
37 | return undefined | ||
38 | }) | ||
39 | } | ||
40 | |||
41 | const completeLocale = getCompleteLocale(locale) | ||
42 | return p.then(json => videojs.addLanguage(getShortLocale(completeLocale), json)) | ||
43 | } | ||
44 | |||
45 | private static getLocalePath (serverUrl: string, locale: string) { | ||
46 | const completeLocale = getCompleteLocale(locale) | ||
47 | |||
48 | if (!is18nLocale(completeLocale) || isDefaultLocale(completeLocale)) return undefined | ||
49 | |||
50 | return serverUrl + '/client/locales/' + completeLocale | ||
51 | } | ||
52 | } | ||
diff --git a/client/src/standalone/videos/embed-api.ts b/client/src/standalone/videos/embed-api.ts index 169e371da..259113215 100644 --- a/client/src/standalone/videos/embed-api.ts +++ b/client/src/standalone/videos/embed-api.ts | |||
@@ -43,7 +43,6 @@ export class PeerTubeEmbedApi { | |||
43 | channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate)) | 43 | channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate)) |
44 | channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate()) | 44 | channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate()) |
45 | channel.bind('getPlaybackRates', (txn, params) => this.embed.playerOptions.playbackRates) | 45 | channel.bind('getPlaybackRates', (txn, params) => this.embed.playerOptions.playbackRates) |
46 | |||
47 | this.channel = channel | 46 | this.channel = channel |
48 | } | 47 | } |
49 | 48 | ||
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html index 5a15bf552..6edf71f48 100644 --- a/client/src/standalone/videos/embed.html +++ b/client/src/standalone/videos/embed.html | |||
@@ -22,5 +22,7 @@ | |||
22 | <video playsinline="true" id="video-container" class="video-js vjs-peertube-skin"> | 22 | <video playsinline="true" id="video-container" class="video-js vjs-peertube-skin"> |
23 | </video> | 23 | </video> |
24 | 24 | ||
25 | <div id="placeholder-preview" /> | ||
26 | |||
25 | </body> | 27 | </body> |
26 | </html> | 28 | </html> |
diff --git a/client/src/standalone/videos/embed.scss b/client/src/standalone/videos/embed.scss index c40ea1208..95573dabe 100644 --- a/client/src/standalone/videos/embed.scss +++ b/client/src/standalone/videos/embed.scss | |||
@@ -79,6 +79,16 @@ html, body { | |||
79 | } | 79 | } |
80 | } | 80 | } |
81 | 81 | ||
82 | #placeholder-preview { | ||
83 | position: absolute; | ||
84 | top: 0; | ||
85 | left: 0; | ||
86 | background-size: 100% auto; | ||
87 | width: 100%; | ||
88 | height: 100%; | ||
89 | background-position: 50% 50%; | ||
90 | } | ||
91 | |||
82 | @media screen and (max-width: 300px) { | 92 | @media screen and (max-width: 300px) { |
83 | #error-block { | 93 | #error-block { |
84 | font-size: 36px; | 94 | font-size: 36px; |
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index bd012f506..f33dd8869 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -1,16 +1,24 @@ | |||
1 | import './embed.scss' | 1 | import './embed.scss' |
2 | 2 | ||
3 | import { peertubeTranslate, ResultList, ServerConfig, VideoDetails } from '../../../../shared' | 3 | import { |
4 | getCompleteLocale, | ||
5 | is18nLocale, | ||
6 | isDefaultLocale, | ||
7 | peertubeTranslate, | ||
8 | ResultList, | ||
9 | ServerConfig, | ||
10 | VideoDetails | ||
11 | } from '../../../../shared' | ||
4 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | 12 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' |
5 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 13 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' |
6 | import { | 14 | import { |
7 | P2PMediaLoaderOptions, | 15 | P2PMediaLoaderOptions, |
8 | PeertubePlayerManager, | ||
9 | PeertubePlayerManagerOptions, | 16 | PeertubePlayerManagerOptions, |
10 | PlayerMode | 17 | PlayerMode |
11 | } from '../../assets/player/peertube-player-manager' | 18 | } from '../../assets/player/peertube-player-manager' |
12 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' | 19 | import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type' |
13 | import { PeerTubeEmbedApi } from './embed-api' | 20 | import { PeerTubeEmbedApi } from './embed-api' |
21 | import { TranslationsManager } from '../../assets/player/translations-manager' | ||
14 | 22 | ||
15 | export class PeerTubeEmbed { | 23 | export class PeerTubeEmbed { |
16 | videoElement: HTMLVideoElement | 24 | videoElement: HTMLVideoElement |
@@ -154,20 +162,30 @@ export class PeerTubeEmbed { | |||
154 | const urlParts = window.location.pathname.split('/') | 162 | const urlParts = window.location.pathname.split('/') |
155 | const videoId = urlParts[ urlParts.length - 1 ] | 163 | const videoId = urlParts[ urlParts.length - 1 ] |
156 | 164 | ||
157 | const [ serverTranslations, videoResponse, captionsResponse, configResponse ] = await Promise.all([ | 165 | const videoPromise = this.loadVideoInfo(videoId) |
158 | PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language), | 166 | const captionsPromise = this.loadVideoCaptions(videoId) |
159 | this.loadVideoInfo(videoId), | 167 | const configPromise = this.loadConfig() |
160 | this.loadVideoCaptions(videoId), | 168 | |
161 | this.loadConfig() | 169 | const translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language) |
162 | ]) | 170 | const videoResponse = await videoPromise |
163 | 171 | ||
164 | if (!videoResponse.ok) { | 172 | if (!videoResponse.ok) { |
173 | const serverTranslations = await translationsPromise | ||
174 | |||
165 | if (videoResponse.status === 404) return this.videoNotFound(serverTranslations) | 175 | if (videoResponse.status === 404) return this.videoNotFound(serverTranslations) |
166 | 176 | ||
167 | return this.videoFetchError(serverTranslations) | 177 | return this.videoFetchError(serverTranslations) |
168 | } | 178 | } |
169 | 179 | ||
170 | const videoInfo: VideoDetails = await videoResponse.json() | 180 | const videoInfo: VideoDetails = await videoResponse.json() |
181 | this.loadPlaceholder(videoInfo) | ||
182 | |||
183 | const PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager') | ||
184 | |||
185 | const promises = [ translationsPromise, captionsPromise, configPromise, PeertubePlayerManagerModulePromise ] | ||
186 | const [ serverTranslations, captionsResponse, configResponse, PeertubePlayerManagerModule ] = await Promise.all(promises) | ||
187 | |||
188 | const PeertubePlayerManager = PeertubePlayerManagerModule.PeertubePlayerManager | ||
171 | const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse) | 189 | const videoCaptions = await this.buildCaptions(serverTranslations, captionsResponse) |
172 | 190 | ||
173 | this.loadParams(videoInfo) | 191 | this.loadParams(videoInfo) |
@@ -220,7 +238,7 @@ export class PeerTubeEmbed { | |||
220 | }) | 238 | }) |
221 | } | 239 | } |
222 | 240 | ||
223 | this.player = await PeertubePlayerManager.initialize(this.mode, options, player => this.player = player) | 241 | this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: any) => this.player = player) |
224 | this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) | 242 | this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) |
225 | 243 | ||
226 | window[ 'videojsPlayer' ] = this.player | 244 | window[ 'videojsPlayer' ] = this.player |
@@ -230,6 +248,8 @@ export class PeerTubeEmbed { | |||
230 | await this.buildDock(videoInfo, configResponse) | 248 | await this.buildDock(videoInfo, configResponse) |
231 | 249 | ||
232 | this.initializeApi() | 250 | this.initializeApi() |
251 | |||
252 | this.removePlaceholder() | ||
233 | } | 253 | } |
234 | 254 | ||
235 | private handleError (err: Error, translations?: { [ id: string ]: string }) { | 255 | private handleError (err: Error, translations?: { [ id: string ]: string }) { |
@@ -282,6 +302,22 @@ export class PeerTubeEmbed { | |||
282 | 302 | ||
283 | return [] | 303 | return [] |
284 | } | 304 | } |
305 | |||
306 | private loadPlaceholder (video: VideoDetails) { | ||
307 | const placeholder = this.getPlaceholderElement() | ||
308 | |||
309 | const url = window.location.origin + video.previewPath | ||
310 | placeholder.style.backgroundImage = `url("${url}")` | ||
311 | } | ||
312 | |||
313 | private removePlaceholder () { | ||
314 | const placeholder = this.getPlaceholderElement() | ||
315 | placeholder.parentElement.removeChild(placeholder) | ||
316 | } | ||
317 | |||
318 | private getPlaceholderElement () { | ||
319 | return document.getElementById('placeholder-preview') | ||
320 | } | ||
285 | } | 321 | } |
286 | 322 | ||
287 | PeerTubeEmbed.main() | 323 | PeerTubeEmbed.main() |