From: Chocobozzz Date: Fri, 4 Jun 2021 12:39:47 +0000 (+0200) Subject: Speed up plugins loading X-Git-Tag: v3.3.0-rc.1~139 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=fc21ef5c62d845576a916414468b3a57370a57b2;hp=2989628b7913383b39ac34c7db8666a21f8e5037;p=github%2FChocobozzz%2FPeerTube.git Speed up plugins loading --- diff --git a/client/src/app/+my-library/my-library.component.ts b/client/src/app/+my-library/my-library.component.ts index 939aa84c8..2ee3559a7 100644 --- a/client/src/app/+my-library/my-library.component.ts +++ b/client/src/app/+my-library/my-library.component.ts @@ -11,7 +11,6 @@ export class MyLibraryComponent implements OnInit { menuEntries: TopMenuDropdownParam[] = [] user: AuthUser - private serverConfig: HTMLServerConfig constructor ( diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts index f5f0c87ed..afa523b91 100644 --- a/client/src/app/+search/search-filters.component.ts +++ b/client/src/app/+search/search-filters.component.ts @@ -99,7 +99,6 @@ export class SearchFiltersComponent implements OnInit { ngOnInit () { this.serverConfig = this.serverService.getHTMLConfig() - this.serverService.getVideoCategories().subscribe(categories => this.videoCategories = categories) this.serverService.getVideoLicences().subscribe(licences => this.videoLicences = licences) this.serverService.getVideoLanguages().subscribe(languages => this.videoLanguages = languages) diff --git a/client/src/app/+search/search.component.ts b/client/src/app/+search/search.component.ts index a31096bb2..a4ab7a5b1 100644 --- a/client/src/app/+search/search.component.ts +++ b/client/src/app/+search/search.component.ts @@ -287,7 +287,7 @@ export class SearchComponent implements OnInit, OnDestroy { ) } - private getDefaultSearchTarget(): SearchTargetType { + private getDefaultSearchTarget (): SearchTargetType { const searchIndexConfig = this.serverConfig.search.searchIndex if (searchIndexConfig.enabled && (searchIndexConfig.isDefaultSearch || searchIndexConfig.disableLocalSearch)) { diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts index 52801802b..c3299cade 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts @@ -194,7 +194,6 @@ export class VideoEditComponent implements OnInit, OnDestroy { } }) - this.serverConfig = this.serverService.getHTMLConfig() this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index 51d486ea5..3fdbc0184 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts @@ -181,7 +181,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy { this.hasAlreadyAcceptedPrivacyConcern = true } - this.paramsSub = this.route.params.subscribe(routeParams => { const videoId = routeParams[ 'videoId' ] if (videoId) this.loadVideo(videoId) diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index b04e6a42b..1f98e9d2e 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -3,7 +3,7 @@ import { RouteReuseStrategy, RouterModule, Routes, UrlMatchResult, UrlSegment } import { CustomReuseStrategy } from '@app/core/routing/custom-reuse-strategy' import { MenuGuards } from '@app/core/routing/menu-guard.service' import { POSSIBLE_LOCALES } from '@shared/core-utils/i18n' -import { MetaGuard, PreloadSelectedModulesList } from './core' +import { HomepageRedirectComponent, MetaGuard, PreloadSelectedModulesList } from './core' import { EmptyComponent } from './empty.component' import { USER_USERNAME_REGEX_CHARACTERS } from './shared/form-validators/user-validators' import { ActorRedirectGuard } from './shared/shared-main' @@ -156,7 +156,7 @@ const routes: Routes = [ { path: '', - component: EmptyComponent // Avoid 404, app component will redirect dynamically + component: HomepageRedirectComponent } ] @@ -164,7 +164,7 @@ const routes: Routes = [ for (const locale of POSSIBLE_LOCALES) { routes.push({ path: locale, - component: EmptyComponent + component: HomepageRedirectComponent }) } diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 6884068f6..2056d6669 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -1,6 +1,5 @@ import { Hotkey, HotkeysService } from 'angular2-hotkeys' -import { concat } from 'rxjs' -import { filter, first, map, pairwise, switchMap } from 'rxjs/operators' +import { filter, map, pairwise, switchMap } from 'rxjs/operators' import { DOCUMENT, PlatformLocation, ViewportScroller } from '@angular/common' import { AfterViewInit, Component, Inject, LOCALE_ID, OnInit, ViewChild } from '@angular/core' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' @@ -14,7 +13,7 @@ import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' import { NgbConfig, NgbModal } from '@ng-bootstrap/ng-bootstrap' import { LoadingBarService } from '@ngx-loading-bar/core' import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' -import { getShortLocale, is18nPath } from '@shared/core-utils/i18n' +import { getShortLocale } from '@shared/core-utils/i18n' import { BroadcastMessageLevel, HTMLServerConfig, ServerConfig, UserRole } from '@shared/models' import { MenuService } from './core/menu/menu.service' import { POP_STATE_MODAL_DISMISS } from './helpers' @@ -75,7 +74,7 @@ export class AppComponent implements OnInit, AfterViewInit { this.serverConfig = this.serverService.getHTMLConfig() - this.loadPlugins() + this.hooks.runAction('action:application.init', 'common') this.themeService.initialize() this.authService.loadClientCredentials() @@ -190,12 +189,6 @@ export class AppComponent implements OnInit, AfterViewInit { } }) - // Homepage redirection - navigationEndEvent.pipe( - map(() => window.location.pathname), - filter(pathname => !pathname || pathname === '/' || is18nPath(pathname)) - ).subscribe(() => this.redirectService.redirectToHomepage(true)) - // Plugin hooks navigationEndEvent.subscribe(e => { this.hooks.runAction('action:router.navigation-end', 'common', { path: e.url }) @@ -268,12 +261,6 @@ export class AppComponent implements OnInit, AfterViewInit { } } - private async loadPlugins () { - this.pluginService.initializePlugins() - - this.hooks.runAction('action:application.init', 'common') - } - private async openModalsIfNeeded () { this.authService.userInformationLoaded .pipe( diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index ea53818e1..dfdccbe69 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -1,4 +1,5 @@ import 'focus-visible' +import { tap } from 'rxjs/operators' import { environment } from 'src/environments/environment' import { APP_BASE_HREF, registerLocaleData } from '@angular/common' import { APP_INITIALIZER, NgModule } from '@angular/core' @@ -7,7 +8,7 @@ import { ServiceWorkerModule } from '@angular/service-worker' import localeOc from '@app/helpers/locales/oc' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' -import { CoreModule, ServerService } from './core' +import { CoreModule, PluginService, ServerService } from './core' import { EmptyComponent } from './empty.component' import { HeaderComponent, SearchTypeaheadComponent, SuggestionComponent } from './header' import { HighlightPipe } from './header/highlight.pipe' @@ -26,8 +27,14 @@ import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings registerLocaleData(localeOc, 'oc') -export function loadConfigFactory (server: ServerService) { - return () => server.loadHTMLConfig() +export function loadConfigFactory (server: ServerService, pluginService: PluginService) { + return () => { + const result = server.loadHTMLConfig() + + if (result) return result.pipe(tap(() => pluginService.initializePlugins())) + + return pluginService.initializePlugins() + } } @NgModule({ @@ -75,9 +82,9 @@ export function loadConfigFactory (server: ServerService) { { provide: APP_INITIALIZER, useFactory: loadConfigFactory, - deps: [ ServerService ], + deps: [ ServerService, PluginService ], multi: true - } + } ] }) export class AppModule {} diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index de3274544..3e2056481 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -14,7 +14,7 @@ import { throwIfAlreadyLoaded } from './module-import-guard' import { Notifier } from './notification' import { HtmlRendererService, LinkifierService, MarkdownService } from './renderer' import { RestExtractor, RestService } from './rest' -import { LoginGuard, MetaGuard, MetaService, RedirectService, UnloggedGuard, UserRightGuard } from './routing' +import { HomepageRedirectComponent, LoginGuard, MetaGuard, MetaService, RedirectService, UnloggedGuard, UserRightGuard } from './routing' import { CanDeactivateGuard } from './routing/can-deactivate-guard.service' import { ServerConfigResolver } from './routing/server-config-resolver.service' import { ScopedTokensService } from './scoped-tokens' @@ -36,13 +36,15 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra ], declarations: [ - CheatSheetComponent + CheatSheetComponent, + HomepageRedirectComponent ], exports: [ ToastModule, - CheatSheetComponent + CheatSheetComponent, + HomepageRedirectComponent ], providers: [ diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts index dff8ad864..ccbfd3e4d 100644 --- a/client/src/app/core/plugins/plugin.service.ts +++ b/client/src/app/core/plugins/plugin.service.ts @@ -1,3 +1,4 @@ +import * as debug from 'debug' import { Observable, of, ReplaySubject } from 'rxjs' import { catchError, first, map, shareReplay } from 'rxjs/operators' import { HttpClient } from '@angular/common/http' @@ -24,7 +25,6 @@ import { } from '@shared/models' import { environment } from '../../../environments/environment' import { RegisterClientHelpers } from '../../../types/register-client-option.model' -import * as debug from 'debug' const logger = debug('peertube:plugins') @@ -33,8 +33,6 @@ export class PluginService implements ClientHook { private static BASE_PLUGIN_API_URL = environment.apiUrl + '/api/v1/plugins' private static BASE_PLUGIN_URL = environment.apiUrl + '/plugins' - pluginsBuilt = new ReplaySubject(1) - pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject } = { common: new ReplaySubject(1), 'admin-plugin': new ReplaySubject(1), @@ -79,30 +77,18 @@ export class PluginService implements ClientHook { } initializePlugins () { - logger('Building plugin configuration') + const config = this.server.getHTMLConfig() + this.plugins = config.plugin.registered - this.server.getConfig() - .subscribe(config => { - this.plugins = config.plugin.registered + this.buildScopeStruct() - this.buildScopeStruct() - - this.pluginsBuilt.next(true) - - logger('Plugin configuration built') - }) + this.ensurePluginsAreLoaded('common') } initializeCustomModal (customModal: CustomModalComponent) { this.customModal = customModal } - ensurePluginsAreBuilt () { - return this.pluginsBuilt.asObservable() - .pipe(first(), shareReplay()) - .toPromise() - } - ensurePluginsAreLoaded (scope: PluginClientScope) { this.loadPluginsByScope(scope) @@ -156,8 +142,6 @@ export class PluginService implements ClientHook { logger('Loading scope %s', scope) try { - await this.ensurePluginsAreBuilt() - if (!isReload) this.loadedScopes.push(scope) const toLoad = this.scopes[ scope ] diff --git a/client/src/app/core/routing/homepage-redirect.component.ts b/client/src/app/core/routing/homepage-redirect.component.ts new file mode 100644 index 000000000..9e3848038 --- /dev/null +++ b/client/src/app/core/routing/homepage-redirect.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit } from '@angular/core' +import { ActivatedRoute } from '@angular/router' +import { is18nPath } from '@shared/core-utils/i18n/i18n' +import { RedirectService } from './redirect.service' + +/* + * We have to use a component instead of an homepage because of a weird issue when using router.navigate in guard + * + * Since we also want to use the `skipLocationChange` option, we cannot use a guard that returns a UrlTree + * See https://github.com/angular/angular/issues/27148 +*/ + +@Component({ + template: '' +}) +export class HomepageRedirectComponent implements OnInit { + + constructor ( + private route: ActivatedRoute, + private redirectService: RedirectService + ) { } + + ngOnInit () { + const url = this.route.snapshot.url + + if (url.length === 0 || is18nPath('/' + url[0])) { + this.redirectService.redirectToHomepage(true) + } + } +} diff --git a/client/src/app/core/routing/index.ts b/client/src/app/core/routing/index.ts index 4314ea475..d0c688a2f 100644 --- a/client/src/app/core/routing/index.ts +++ b/client/src/app/core/routing/index.ts @@ -1,6 +1,7 @@ export * from './can-deactivate-guard.service' export * from './custom-reuse-strategy' export * from './disable-for-reuse-hook' +export * from './homepage-redirect.component' export * from './login-guard.service' export * from './menu-guard.service' export * from './meta-guard.service' diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index 198332b00..17d9d1358 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts @@ -42,7 +42,6 @@ export class RedirectService { return this.defaultRoute } - getDefaultTrendingAlgorithm () { return this.defaultTrendingAlgorithm } diff --git a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts index eec325a61..c3f10c055 100644 --- a/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts +++ b/client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts @@ -44,7 +44,6 @@ export class ActorBannerEditComponent implements OnInit { this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}` } - onBannerChange (input: HTMLInputElement) { this.bannerfileInput = new ElementRef(input) diff --git a/client/src/root-helpers/plugins.ts b/client/src/root-helpers/plugins.ts index 8c1c858b7..10c111a8c 100644 --- a/client/src/root-helpers/plugins.ts +++ b/client/src/root-helpers/plugins.ts @@ -11,8 +11,8 @@ import { RegisterClientVideoFieldOptions, ServerConfigPlugin } from '../../../shared/models' +import { environment } from '../environments/environment' import { ClientScript as ClientScriptModule } from '../types/client-script.model' -import { importModule } from './utils' interface HookStructValue extends RegisterClientHookOptions { plugin: ServerConfigPlugin @@ -101,7 +101,8 @@ function loadPlugin (options: { console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name) - return importModule(clientScript.script) + const absURL = (environment.apiUrl || window.location.origin) + clientScript.script + return import(/* webpackIgnore: true */ absURL) .then((script: ClientScriptModule) => script.register({ registerHook, registerVideoField, registerSettingsScript, peertubeHelpers })) .then(() => sortHooksByPriority(hooks)) .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err)) diff --git a/client/src/root-helpers/utils.ts b/client/src/root-helpers/utils.ts index 06591b95e..00bd92411 100644 --- a/client/src/root-helpers/utils.ts +++ b/client/src/root-helpers/utils.ts @@ -1,5 +1,3 @@ -import { environment } from '../environments/environment' - function objectToUrlEncoded (obj: any) { const str: string[] = [] for (const key of Object.keys(obj)) { @@ -21,41 +19,6 @@ function copyToClipboard (text: string) { document.body.removeChild(el) } -// Thanks: https://github.com/uupaa/dynamic-import-polyfill -function importModule (path: string) { - return new Promise((resolve, reject) => { - const vector = '$importModule$' + Math.random().toString(32).slice(2) - const script = document.createElement('script') - - const destructor = () => { - delete window[ vector ] - script.onerror = null - script.onload = null - script.remove() - URL.revokeObjectURL(script.src) - script.src = '' - } - - script.defer = true - script.type = 'module' - - script.onerror = () => { - reject(new Error(`Failed to import: ${path}`)) - destructor() - } - script.onload = () => { - resolve(window[ vector ]) - destructor() - } - const absURL = (environment.apiUrl || window.location.origin) + path - const loader = `import * as m from "${absURL}"; window.${vector} = m;` // export Module - const blob = new Blob([ loader ], { type: 'text/javascript' }) - script.src = URL.createObjectURL(blob) - - document.head.appendChild(script) - }) -} - function wait (ms: number) { return new Promise(res => { setTimeout(() => res(), ms) @@ -64,7 +27,6 @@ function wait (ms: number) { export { copyToClipboard, - importModule, objectToUrlEncoded, wait }