Speed up plugins loading

This commit is contained in:
Chocobozzz 2021-06-04 14:39:47 +02:00
parent 2989628b79
commit fc21ef5c62
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
16 changed files with 63 additions and 95 deletions

View File

@ -11,7 +11,6 @@ export class MyLibraryComponent implements OnInit {
menuEntries: TopMenuDropdownParam[] = []
user: AuthUser
private serverConfig: HTMLServerConfig
constructor (

View File

@ -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)

View File

@ -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)) {

View File

@ -194,7 +194,6 @@ export class VideoEditComponent implements OnInit, OnDestroy {
}
})
this.serverConfig = this.serverService.getHTMLConfig()
this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)

View File

@ -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)

View File

@ -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
})
}

View File

@ -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(

View File

@ -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 {}

View File

@ -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: [

View File

@ -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<boolean>(1)
pluginsLoaded: { [ scope in PluginClientScope ]: ReplaySubject<boolean> } = {
common: new ReplaySubject<boolean>(1),
'admin-plugin': new ReplaySubject<boolean>(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 ]

View File

@ -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)
}
}
}

View File

@ -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'

View File

@ -42,7 +42,6 @@ export class RedirectService {
return this.defaultRoute
}
getDefaultTrendingAlgorithm () {
return this.defaultTrendingAlgorithm
}

View File

@ -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)

View File

@ -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))

View File

@ -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<void>(res => {
setTimeout(() => res(), ms)
@ -64,7 +27,6 @@ function wait (ms: number) {
export {
copyToClipboard,
importModule,
objectToUrlEncoded,
wait
}