aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-12-18 15:31:54 +0100
committerChocobozzz <me@florianbigard.com>2019-12-18 15:40:59 +0100
commitba430d7516bc5b1324b60571ba7594460969b7fb (patch)
treedf5c6952c82f49a94c0a884bbc97d4a0cbd9f867 /client/src
parent5dfb7c1dec8222b0bbccac5b56ad46da1438747e (diff)
downloadPeerTube-ba430d7516bc5b1324b60571ba7594460969b7fb.tar.gz
PeerTube-ba430d7516bc5b1324b60571ba7594460969b7fb.tar.zst
PeerTube-ba430d7516bc5b1324b60571ba7594460969b7fb.zip
Lazy load static objects
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/+about/about-instance/about-instance.component.ts38
-rw-r--r--client/src/app/+about/about-instance/contact-admin-modal.component.ts8
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts23
-rw-r--r--client/src/app/+admin/moderation/moderation.component.ts14
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts19
-rw-r--r--client/src/app/+admin/users/user-edit/user-create.component.ts2
-rw-r--r--client/src/app/+admin/users/user-edit/user-edit.ts17
-rw-r--r--client/src/app/+admin/users/user-edit/user-update.component.ts2
-rw-r--r--client/src/app/+admin/users/user-list/user-list.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts43
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-interface/my-account-interface-settings.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts6
-rw-r--r--client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts10
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts13
-rw-r--r--client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts14
-rw-r--r--client/src/app/+my-account/my-account.component.ts16
-rw-r--r--client/src/app/+my-account/shared/actor-avatar-info.component.ts17
-rw-r--r--client/src/app/+signup/+register/register-routing.module.ts2
-rw-r--r--client/src/app/+signup/+register/register.component.ts10
-rw-r--r--client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts8
-rw-r--r--client/src/app/app.component.ts46
-rw-r--r--client/src/app/app.module.ts6
-rw-r--r--client/src/app/core/plugins/plugin.service.ts6
-rw-r--r--client/src/app/core/routing/redirect.service.ts12
-rw-r--r--client/src/app/core/routing/server-config-resolver.service.ts12
-rw-r--r--client/src/app/core/server/server.service.ts205
-rw-r--r--client/src/app/core/theme/theme.service.ts15
-rw-r--r--client/src/app/login/login-routing.module.ts2
-rw-r--r--client/src/app/login/login.component.ts13
-rw-r--r--client/src/app/menu/menu.component.ts10
-rw-r--r--client/src/app/search/search-filters.component.ts16
-rw-r--r--client/src/app/shared/images/preview-upload.component.ts10
-rw-r--r--client/src/app/shared/instance/instance-features-table.component.html16
-rw-r--r--client/src/app/shared/instance/instance-features-table.component.ts23
-rw-r--r--client/src/app/shared/instance/instance.service.ts45
-rw-r--r--client/src/app/shared/moderation/user-moderation-dropdown.component.ts15
-rw-r--r--client/src/app/shared/overview/overview.service.ts2
-rw-r--r--client/src/app/shared/video-caption/video-caption.service.ts2
-rw-r--r--client/src/app/shared/video-import/video-import.service.ts2
-rw-r--r--client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts19
-rw-r--r--client/src/app/shared/video-playlist/video-playlist.service.ts6
-rw-r--r--client/src/app/shared/video/abstract-video-list.ts10
-rw-r--r--client/src/app/shared/video/video-miniature.component.ts9
-rw-r--r--client/src/app/shared/video/video.service.ts4
-rw-r--r--client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts10
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.html2
-rw-r--r--client/src/app/videos/+video-edit/shared/video-edit.component.ts21
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-send.ts13
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts5
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.ts17
-rw-r--r--client/src/app/videos/+video-watch/video-watch.component.ts16
-rw-r--r--client/src/app/videos/video-list/video-most-liked.component.ts7
-rw-r--r--client/src/app/videos/video-list/video-trending.component.ts6
54 files changed, 542 insertions, 353 deletions
diff --git a/client/src/app/+about/about-instance/about-instance.component.ts b/client/src/app/+about/about-instance/about-instance.component.ts
index 16ccae2e2..87beb13da 100644
--- a/client/src/app/+about/about-instance/about-instance.component.ts
+++ b/client/src/app/+about/about-instance/about-instance.component.ts
@@ -5,7 +5,8 @@ import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-a
5import { InstanceService } from '@app/shared/instance/instance.service' 5import { InstanceService } from '@app/shared/instance/instance.service'
6import { MarkdownService } from '@app/shared/renderer' 6import { MarkdownService } from '@app/shared/renderer'
7import { forkJoin } from 'rxjs' 7import { forkJoin } from 'rxjs'
8import { first } from 'rxjs/operators' 8import { map, switchMap } from 'rxjs/operators'
9import { ServerConfig } from '@shared/models'
9 10
10@Component({ 11@Component({
11 selector: 'my-about-instance', 12 selector: 'my-about-instance',
@@ -33,6 +34,8 @@ export class AboutInstanceComponent implements OnInit {
33 languages: string[] = [] 34 languages: string[] = []
34 categories: string[] = [] 35 categories: string[] = []
35 36
37 serverConfig: ServerConfig
38
36 constructor ( 39 constructor (
37 private notifier: Notifier, 40 private notifier: Notifier,
38 private serverService: ServerService, 41 private serverService: ServerService,
@@ -42,25 +45,35 @@ export class AboutInstanceComponent implements OnInit {
42 ) {} 45 ) {}
43 46
44 get instanceName () { 47 get instanceName () {
45 return this.serverService.getConfig().instance.name 48 return this.serverConfig.instance.name
46 } 49 }
47 50
48 get isContactFormEnabled () { 51 get isContactFormEnabled () {
49 return this.serverService.getConfig().email.enabled && this.serverService.getConfig().contactForm.enabled 52 return this.serverConfig.email.enabled && this.serverConfig.contactForm.enabled
50 } 53 }
51 54
52 get isNSFW () { 55 get isNSFW () {
53 return this.serverService.getConfig().instance.isNSFW 56 return this.serverConfig.instance.isNSFW
54 } 57 }
55 58
56 ngOnInit () { 59 ngOnInit () {
57 forkJoin([ 60 this.serverConfig = this.serverService.getTmpConfig()
58 this.instanceService.getAbout(), 61 this.serverService.getConfig()
59 this.serverService.localeObservable.pipe(first()), 62 .subscribe(config => this.serverConfig = config)
60 this.serverService.videoLanguagesLoaded.pipe(first()), 63
61 this.serverService.videoCategoriesLoaded.pipe(first()) 64 this.instanceService.getAbout()
62 ]).subscribe( 65 .pipe(
63 async ([ about, translations ]) => { 66 switchMap(about => {
67 return forkJoin([
68 this.instanceService.buildTranslatedLanguages(about),
69 this.instanceService.buildTranslatedCategories(about)
70 ]).pipe(map(([ languages, categories ]) => ({ about, languages, categories })))
71 })
72 ).subscribe(
73 async ({ about, languages, categories }) => {
74 this.languages = languages
75 this.categories = categories
76
64 this.shortDescription = about.instance.shortDescription 77 this.shortDescription = about.instance.shortDescription
65 78
66 this.creationReason = about.instance.creationReason 79 this.creationReason = about.instance.creationReason
@@ -68,9 +81,6 @@ export class AboutInstanceComponent implements OnInit {
68 this.businessModel = about.instance.businessModel 81 this.businessModel = about.instance.businessModel
69 82
70 this.html = await this.instanceService.buildHtml(about) 83 this.html = await this.instanceService.buildHtml(about)
71
72 this.languages = this.instanceService.buildTranslatedLanguages(about, translations)
73 this.categories = this.instanceService.buildTranslatedCategories(about, translations)
74 }, 84 },
75 85
76 () => this.notifier.error(this.i18n('Cannot get about information from server')) 86 () => this.notifier.error(this.i18n('Cannot get about information from server'))
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
index 878d49b55..2ed41e741 100644
--- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts
+++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts
@@ -6,6 +6,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' 6import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
7import { FormReactive, InstanceValidatorsService } from '@app/shared' 7import { FormReactive, InstanceValidatorsService } from '@app/shared'
8import { InstanceService } from '@app/shared/instance/instance.service' 8import { InstanceService } from '@app/shared/instance/instance.service'
9import { ServerConfig } from '@shared/models'
9 10
10@Component({ 11@Component({
11 selector: 'my-contact-admin-modal', 12 selector: 'my-contact-admin-modal',
@@ -18,6 +19,7 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
18 error: string 19 error: string
19 20
20 private openedModal: NgbModalRef 21 private openedModal: NgbModalRef
22 private serverConfig: ServerConfig
21 23
22 constructor ( 24 constructor (
23 protected formValidatorService: FormValidatorService, 25 protected formValidatorService: FormValidatorService,
@@ -32,10 +34,14 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
32 } 34 }
33 35
34 get instanceName () { 36 get instanceName () {
35 return this.serverService.getConfig().instance.name 37 return this.serverConfig.instance.name
36 } 38 }
37 39
38 ngOnInit () { 40 ngOnInit () {
41 this.serverConfig = this.serverService.getTmpConfig()
42 this.serverService.getConfig()
43 .subscribe(config => this.serverConfig = config)
44
39 this.buildForm({ 45 this.buildForm({
40 fromName: this.instanceValidatorsService.FROM_NAME, 46 fromName: this.instanceValidatorsService.FROM_NAME,
41 fromEmail: this.instanceValidatorsService.FROM_EMAIL, 47 fromEmail: this.instanceValidatorsService.FROM_EMAIL,
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
index 1f6751297..25e06d8a1 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
@@ -8,7 +8,7 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
8import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 8import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
9import { SelectItem } from 'primeng/api' 9import { SelectItem } from 'primeng/api'
10import { forkJoin } from 'rxjs' 10import { forkJoin } from 'rxjs'
11import { first } from 'rxjs/operators' 11import { ServerConfig } from '@shared/models'
12 12
13@Component({ 13@Component({
14 selector: 'my-edit-custom-config', 14 selector: 'my-edit-custom-config',
@@ -24,6 +24,8 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
24 languageItems: SelectItem[] = [] 24 languageItems: SelectItem[] = []
25 categoryItems: SelectItem[] = [] 25 categoryItems: SelectItem[] = []
26 26
27 private serverConfig: ServerConfig
28
27 constructor ( 29 constructor (
28 protected formValidatorService: FormValidatorService, 30 protected formValidatorService: FormValidatorService,
29 private customConfigValidatorsService: CustomConfigValidatorsService, 31 private customConfigValidatorsService: CustomConfigValidatorsService,
@@ -84,7 +86,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
84 } 86 }
85 87
86 get availableThemes () { 88 get availableThemes () {
87 return this.serverService.getConfig().theme.registered 89 return this.serverConfig.theme.registered
88 .map(t => t.name) 90 .map(t => t.name)
89 } 91 }
90 92
@@ -93,6 +95,10 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
93 } 95 }
94 96
95 ngOnInit () { 97 ngOnInit () {
98 this.serverConfig = this.serverService.getTmpConfig()
99 this.serverService.getConfig()
100 .subscribe(config => this.serverConfig = config)
101
96 const formGroupData: { [key in keyof CustomConfig ]: any } = { 102 const formGroupData: { [key in keyof CustomConfig ]: any } = {
97 instance: { 103 instance: {
98 name: this.customConfigValidatorsService.INSTANCE_NAME, 104 name: this.customConfigValidatorsService.INSTANCE_NAME,
@@ -218,16 +224,13 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
218 224
219 forkJoin([ 225 forkJoin([
220 this.configService.getCustomConfig(), 226 this.configService.getCustomConfig(),
221 this.serverService.videoLanguagesLoaded.pipe(first()), // First so the observable completes 227 this.serverService.getVideoLanguages(),
222 this.serverService.videoCategoriesLoaded.pipe(first()) 228 this.serverService.getVideoCategories()
223 ]).subscribe( 229 ]).subscribe(
224 ([ config ]) => { 230 ([ config, languages, categories ]) => {
225 this.customConfig = config 231 this.customConfig = config
226 232
227 const languages = this.serverService.getVideoLanguages()
228 this.languageItems = languages.map(l => ({ label: l.label, value: l.id })) 233 this.languageItems = languages.map(l => ({ label: l.label, value: l.id }))
229
230 const categories = this.serverService.getVideoCategories()
231 this.categoryItems = categories.map(l => ({ label: l.label, value: l.id })) 234 this.categoryItems = categories.map(l => ({ label: l.label, value: l.id }))
232 235
233 this.updateForm() 236 this.updateForm()
@@ -249,12 +252,14 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
249 252
250 async formValidated () { 253 async formValidated () {
251 this.configService.updateCustomConfig(this.form.value) 254 this.configService.updateCustomConfig(this.form.value)
255 .pipe(
256 )
252 .subscribe( 257 .subscribe(
253 res => { 258 res => {
254 this.customConfig = res 259 this.customConfig = res
255 260
256 // Reload general configuration 261 // Reload general configuration
257 this.serverService.loadConfig() 262 this.serverService.resetConfig()
258 263
259 this.updateForm() 264 this.updateForm()
260 265
diff --git a/client/src/app/+admin/moderation/moderation.component.ts b/client/src/app/+admin/moderation/moderation.component.ts
index 47154af3f..7744deb06 100644
--- a/client/src/app/+admin/moderation/moderation.component.ts
+++ b/client/src/app/+admin/moderation/moderation.component.ts
@@ -1,4 +1,4 @@
1import { Component } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { UserRight } from '../../../../../shared' 2import { UserRight } from '../../../../../shared'
3import { AuthService, ServerService } from '@app/core' 3import { AuthService, ServerService } from '@app/core'
4 4
@@ -6,14 +6,18 @@ import { AuthService, ServerService } from '@app/core'
6 templateUrl: './moderation.component.html', 6 templateUrl: './moderation.component.html',
7 styleUrls: [ './moderation.component.scss' ] 7 styleUrls: [ './moderation.component.scss' ]
8}) 8})
9export class ModerationComponent { 9export class ModerationComponent implements OnInit {
10 autoBlacklistVideosEnabled: boolean 10 autoBlacklistVideosEnabled = false
11 11
12 constructor ( 12 constructor (
13 private auth: AuthService, 13 private auth: AuthService,
14 private serverService: ServerService 14 private serverService: ServerService
15 ) { 15 ) { }
16 this.autoBlacklistVideosEnabled = this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled 16
17 ngOnInit (): void {
18 this.serverService.getConfig()
19 .subscribe(config => this.autoBlacklistVideosEnabled = config.autoBlacklist.videos.ofUsers.enabled)
20
17 } 21 }
18 22
19 hasVideoAbusesRight () { 23 hasVideoAbusesRight () {
diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
index f4bce7c48..5876f658b 100644
--- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
+++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts
@@ -33,11 +33,18 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
33 private i18n: I18n 33 private i18n: I18n
34 ) { 34 ) {
35 super() 35 super()
36 }
36 37
37 // don't filter if auto-blacklist not enabled as this will be only list 38 ngOnInit () {
38 if (this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled) { 39 this.serverService.getConfig()
39 this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL 40 .subscribe(config => {
40 } 41 // don't filter if auto-blacklist not enabled as this will be only list
42 if (config.autoBlacklist.videos.ofUsers.enabled) {
43 this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL
44 }
45 })
46
47 this.initialize()
41 48
42 this.videoBlacklistActions = [ 49 this.videoBlacklistActions = [
43 { 50 {
@@ -47,10 +54,6 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
47 ] 54 ]
48 } 55 }
49 56
50 ngOnInit () {
51 this.initialize()
52 }
53
54 getVideoUrl (videoBlacklist: VideoBlacklist) { 57 getVideoUrl (videoBlacklist: VideoBlacklist) {
55 return Video.buildClientUrl(videoBlacklist.video.uuid) 58 return Video.buildClientUrl(videoBlacklist.video.uuid)
56 } 59 }
diff --git a/client/src/app/+admin/users/user-edit/user-create.component.ts b/client/src/app/+admin/users/user-edit/user-create.component.ts
index 3b57a49c6..e726ec4d7 100644
--- a/client/src/app/+admin/users/user-edit/user-create.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-create.component.ts
@@ -34,6 +34,8 @@ export class UserCreateComponent extends UserEdit implements OnInit {
34 } 34 }
35 35
36 ngOnInit () { 36 ngOnInit () {
37 super.ngOnInit()
38
37 const defaultValues = { 39 const defaultValues = {
38 role: UserRole.USER.toString(), 40 role: UserRole.USER.toString(),
39 videoQuota: '-1', 41 videoQuota: '-1',
diff --git a/client/src/app/+admin/users/user-edit/user-edit.ts b/client/src/app/+admin/users/user-edit/user-edit.ts
index 6625d65d6..02f1dcd42 100644
--- a/client/src/app/+admin/users/user-edit/user-edit.ts
+++ b/client/src/app/+admin/users/user-edit/user-edit.ts
@@ -1,21 +1,30 @@
1import { AuthService, ServerService } from '../../../core' 1import { AuthService, ServerService } from '../../../core'
2import { FormReactive } from '../../../shared' 2import { FormReactive } from '../../../shared'
3import { USER_ROLE_LABELS, UserRole, VideoResolution } from '../../../../../../shared' 3import { ServerConfig, USER_ROLE_LABELS, UserRole, VideoResolution } from '../../../../../../shared'
4import { ConfigService } from '@app/+admin/config/shared/config.service' 4import { ConfigService } from '@app/+admin/config/shared/config.service'
5import { UserAdminFlag } from '@shared/models/users/user-flag.model' 5import { UserAdminFlag } from '@shared/models/users/user-flag.model'
6import { OnInit } from '@angular/core'
6 7
7export abstract class UserEdit extends FormReactive { 8export abstract class UserEdit extends FormReactive implements OnInit {
8 videoQuotaOptions: { value: string, label: string }[] = [] 9 videoQuotaOptions: { value: string, label: string }[] = []
9 videoQuotaDailyOptions: { value: string, label: string }[] = [] 10 videoQuotaDailyOptions: { value: string, label: string }[] = []
10 username: string 11 username: string
11 userId: number 12 userId: number
12 13
14 protected serverConfig: ServerConfig
15
13 protected abstract serverService: ServerService 16 protected abstract serverService: ServerService
14 protected abstract configService: ConfigService 17 protected abstract configService: ConfigService
15 protected abstract auth: AuthService 18 protected abstract auth: AuthService
16 abstract isCreation (): boolean 19 abstract isCreation (): boolean
17 abstract getFormButtonTitle (): string 20 abstract getFormButtonTitle (): string
18 21
22 ngOnInit (): void {
23 this.serverConfig = this.serverService.getTmpConfig()
24 this.serverService.getConfig()
25 .subscribe(config => this.serverConfig = config)
26 }
27
19 getRoles () { 28 getRoles () {
20 const authUser = this.auth.getUser() 29 const authUser = this.auth.getUser()
21 30
@@ -32,12 +41,12 @@ export abstract class UserEdit extends FormReactive {
32 isTranscodingInformationDisplayed () { 41 isTranscodingInformationDisplayed () {
33 const formVideoQuota = parseInt(this.form.value['videoQuota'], 10) 42 const formVideoQuota = parseInt(this.form.value['videoQuota'], 10)
34 43
35 return this.serverService.getConfig().transcoding.enabledResolutions.length !== 0 && 44 return this.serverConfig.transcoding.enabledResolutions.length !== 0 &&
36 formVideoQuota > 0 45 formVideoQuota > 0
37 } 46 }
38 47
39 computeQuotaWithTranscoding () { 48 computeQuotaWithTranscoding () {
40 const transcodingConfig = this.serverService.getConfig().transcoding 49 const transcodingConfig = this.serverConfig.transcoding
41 50
42 const resolutions = transcodingConfig.enabledResolutions 51 const resolutions = transcodingConfig.enabledResolutions
43 const higherResolution = VideoResolution.H_4K 52 const higherResolution = VideoResolution.H_4K
diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts
index c7052a925..d1682a99d 100644
--- a/client/src/app/+admin/users/user-edit/user-update.component.ts
+++ b/client/src/app/+admin/users/user-edit/user-update.component.ts
@@ -43,6 +43,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
43 } 43 }
44 44
45 ngOnInit () { 45 ngOnInit () {
46 super.ngOnInit()
47
46 const defaultValues = { videoQuota: '-1', videoQuotaDaily: '-1' } 48 const defaultValues = { videoQuota: '-1', videoQuotaDaily: '-1' }
47 this.buildForm({ 49 this.buildForm({
48 email: this.userValidatorsService.USER_EMAIL, 50 email: this.userValidatorsService.USER_EMAIL,
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index ab82713b2..1083ba291 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -4,7 +4,7 @@ import { SortMeta } from 'primeng/components/common/sortmeta'
4import { ConfirmService, ServerService } from '../../../core' 4import { ConfirmService, ServerService } from '../../../core'
5import { RestPagination, RestTable, UserService } from '../../../shared' 5import { RestPagination, RestTable, UserService } from '../../../shared'
6import { I18n } from '@ngx-translate/i18n-polyfill' 6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { User } from '../../../../../../shared' 7import { ServerConfig, User } from '../../../../../../shared'
8import { UserBanModalComponent } from '@app/shared/moderation' 8import { UserBanModalComponent } from '@app/shared/moderation'
9import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' 9import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
10 10
@@ -25,6 +25,8 @@ export class UserListComponent extends RestTable implements OnInit {
25 selectedUsers: User[] = [] 25 selectedUsers: User[] = []
26 bulkUserActions: DropdownAction<User[]>[] = [] 26 bulkUserActions: DropdownAction<User[]>[] = []
27 27
28 private serverConfig: ServerConfig
29
28 constructor ( 30 constructor (
29 private notifier: Notifier, 31 private notifier: Notifier,
30 private confirmService: ConfirmService, 32 private confirmService: ConfirmService,
@@ -41,10 +43,14 @@ export class UserListComponent extends RestTable implements OnInit {
41 } 43 }
42 44
43 get requiresEmailVerification () { 45 get requiresEmailVerification () {
44 return this.serverService.getConfig().signup.requiresEmailVerification 46 return this.serverConfig.signup.requiresEmailVerification
45 } 47 }
46 48
47 ngOnInit () { 49 ngOnInit () {
50 this.serverConfig = this.serverService.getTmpConfig()
51 this.serverService.getConfig()
52 .subscribe(config => this.serverConfig = config)
53
48 this.initialize() 54 this.initialize()
49 55
50 this.bulkUserActions = [ 56 this.bulkUserActions = [
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
index ec7cf935c..9d406805f 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
@@ -6,6 +6,7 @@ import { FormValidatorService } from '@app/shared/forms/form-validators/form-val
6import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service' 6import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
7import { User } from '../../../../../../shared' 7import { User } from '../../../../../../shared'
8import { tap } from 'rxjs/operators' 8import { tap } from 'rxjs/operators'
9import { forkJoin } from 'rxjs'
9 10
10@Component({ 11@Component({
11 selector: 'my-account-change-email', 12 selector: 'my-account-change-email',
@@ -45,29 +46,29 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
45 const password = this.form.value[ 'password' ] 46 const password = this.form.value[ 'password' ]
46 const email = this.form.value[ 'new-email' ] 47 const email = this.form.value[ 'new-email' ]
47 48
48 this.userService.changeEmail(password, email) 49 forkJoin([
49 .pipe( 50 this.serverService.getConfig(),
50 tap(() => this.authService.refreshUserInformation()) 51 this.userService.changeEmail(password, email)
51 ) 52 ]).pipe(tap(() => this.authService.refreshUserInformation()))
52 .subscribe( 53 .subscribe(
53 () => { 54 ([ config ]) => {
54 this.form.reset() 55 this.form.reset()
55 56
56 if (this.serverService.getConfig().signup.requiresEmailVerification) { 57 if (config.signup.requiresEmailVerification) {
57 this.success = this.i18n('Please check your emails to verify your new email.') 58 this.success = this.i18n('Please check your emails to verify your new email.')
58 } else { 59 } else {
59 this.success = this.i18n('Email updated.') 60 this.success = this.i18n('Email updated.')
60 } 61 }
61 }, 62 },
62
63 err => {
64 if (err.status === 401) {
65 this.error = this.i18n('You current password is invalid.')
66 return
67 }
68 63
69 this.error = err.message 64 err => {
65 if (err.status === 401) {
66 this.error = this.i18n('You current password is invalid.')
67 return
70 } 68 }
71 ) 69
70 this.error = err.message
71 }
72 )
72 } 73 }
73} 74}
diff --git a/client/src/app/+my-account/my-account-settings/my-account-interface/my-account-interface-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-interface/my-account-interface-settings.component.ts
index 5ec1c9f8f..441f89f10 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-interface/my-account-interface-settings.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-interface/my-account-interface-settings.component.ts
@@ -1,6 +1,6 @@
1import { Component, Input, OnInit } from '@angular/core' 1import { Component, Input, OnInit } from '@angular/core'
2import { Notifier, ServerService } from '@app/core' 2import { Notifier, ServerService } from '@app/core'
3import { UserUpdateMe } from '../../../../../../shared' 3import { ServerConfig, UserUpdateMe } from '../../../../../../shared'
4import { AuthService } from '../../../core' 4import { AuthService } from '../../../core'
5import { FormReactive, User, UserService } from '../../../shared' 5import { FormReactive, User, UserService } from '../../../shared'
6import { I18n } from '@ngx-translate/i18n-polyfill' 6import { I18n } from '@ngx-translate/i18n-polyfill'
@@ -16,6 +16,8 @@ export class MyAccountInterfaceSettingsComponent extends FormReactive implements
16 @Input() user: User = null 16 @Input() user: User = null
17 @Input() userInformationLoaded: Subject<any> 17 @Input() userInformationLoaded: Subject<any>
18 18
19 private serverConfig: ServerConfig
20
19 constructor ( 21 constructor (
20 protected formValidatorService: FormValidatorService, 22 protected formValidatorService: FormValidatorService,
21 private authService: AuthService, 23 private authService: AuthService,
@@ -28,11 +30,15 @@ export class MyAccountInterfaceSettingsComponent extends FormReactive implements
28 } 30 }
29 31
30 get availableThemes () { 32 get availableThemes () {
31 return this.serverService.getConfig().theme.registered 33 return this.serverConfig.theme.registered
32 .map(t => t.name) 34 .map(t => t.name)
33 } 35 }
34 36
35 ngOnInit () { 37 ngOnInit () {
38 this.serverConfig = this.serverService.getTmpConfig()
39 this.serverService.getConfig()
40 .subscribe(config => this.serverConfig = config)
41
36 this.buildForm({ 42 this.buildForm({
37 theme: null 43 theme: null
38 }) 44 })
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
index 76fabb19d..6ba1a1020 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
@@ -21,7 +21,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
21 webNotifications: { [ id in keyof UserNotificationSetting ]: boolean } = {} as any 21 webNotifications: { [ id in keyof UserNotificationSetting ]: boolean } = {} as any
22 labelNotifications: { [ id in keyof UserNotificationSetting ]: string } = {} as any 22 labelNotifications: { [ id in keyof UserNotificationSetting ]: string } = {} as any
23 rightNotifications: { [ id in keyof Partial<UserNotificationSetting> ]: UserRight } = {} as any 23 rightNotifications: { [ id in keyof Partial<UserNotificationSetting> ]: UserRight } = {} as any
24 emailEnabled: boolean 24 emailEnabled = false
25 25
26 private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500) 26 private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500)
27 27
@@ -31,7 +31,6 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
31 private serverService: ServerService, 31 private serverService: ServerService,
32 private notifier: Notifier 32 private notifier: Notifier
33 ) { 33 ) {
34
35 this.labelNotifications = { 34 this.labelNotifications = {
36 newVideoFromSubscription: this.i18n('New video from your subscriptions'), 35 newVideoFromSubscription: this.i18n('New video from your subscriptions'),
37 newCommentOnMyVideo: this.i18n('New comment on your video'), 36 newCommentOnMyVideo: this.i18n('New comment on your video'),
@@ -55,11 +54,14 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
55 newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW, 54 newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW,
56 autoInstanceFollowing: UserRight.MANAGE_CONFIGURATION 55 autoInstanceFollowing: UserRight.MANAGE_CONFIGURATION
57 } 56 }
58
59 this.emailEnabled = this.serverService.getConfig().email.enabled
60 } 57 }
61 58
62 ngOnInit () { 59 ngOnInit () {
60 this.serverService.getConfig()
61 .subscribe(config => {
62 this.emailEnabled = config.email.enabled
63 })
64
63 this.userInformationLoaded.subscribe(() => this.loadNotificationSettings()) 65 this.userInformationLoaded.subscribe(() => this.loadNotificationSettings())
64 } 66 }
65 67
diff --git a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts
index 99eee23b8..a66159b3f 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-video-settings/my-account-video-settings.component.ts
@@ -41,11 +41,9 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
41 }) 41 })
42 42
43 forkJoin([ 43 forkJoin([
44 this.serverService.videoLanguagesLoaded.pipe(first()), 44 this.serverService.getVideoLanguages(),
45 this.userInformationLoaded.pipe(first()) 45 this.userInformationLoaded.pipe(first())
46 ]).subscribe(() => { 46 ]).subscribe(([ languages ]) => {
47 const languages = this.serverService.getVideoLanguages()
48
49 this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ] 47 this.languageItems = [ { label: this.i18n('Unknown language'), value: '_unknown' } ]
50 this.languageItems = this.languageItems 48 this.languageItems = this.languageItems
51 .concat(languages.map(l => ({ label: l.label, value: l.id }))) 49 .concat(languages.map(l => ({ label: l.label, value: l.id })))
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts
index 081e956d2..9c948b367 100644
--- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts
+++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-update.component.ts
@@ -9,6 +9,7 @@ import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
9import { I18n } from '@ngx-translate/i18n-polyfill' 9import { I18n } from '@ngx-translate/i18n-polyfill'
10import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 10import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
11import { VideoChannelValidatorsService } from '@app/shared/forms/form-validators/video-channel-validators.service' 11import { VideoChannelValidatorsService } from '@app/shared/forms/form-validators/video-channel-validators.service'
12import { ServerConfig } from '@shared/models'
12 13
13@Component({ 14@Component({
14 selector: 'my-account-video-channel-update', 15 selector: 'my-account-video-channel-update',
@@ -21,6 +22,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
21 22
22 private paramsSub: Subscription 23 private paramsSub: Subscription
23 private oldSupportField: string 24 private oldSupportField: string
25 private serverConfig: ServerConfig
24 26
25 constructor ( 27 constructor (
26 protected formValidatorService: FormValidatorService, 28 protected formValidatorService: FormValidatorService,
@@ -37,6 +39,10 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
37 } 39 }
38 40
39 ngOnInit () { 41 ngOnInit () {
42 this.serverConfig = this.serverService.getTmpConfig()
43 this.serverService.getConfig()
44 .subscribe(config => this.serverConfig = config)
45
40 this.buildForm({ 46 this.buildForm({
41 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME, 47 'display-name': this.videoChannelValidatorsService.VIDEO_CHANNEL_DISPLAY_NAME,
42 description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION, 48 description: this.videoChannelValidatorsService.VIDEO_CHANNEL_DESCRIPTION,
@@ -109,11 +115,11 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
109 } 115 }
110 116
111 get maxAvatarSize () { 117 get maxAvatarSize () {
112 return this.serverService.getConfig().avatar.file.size.max 118 return this.serverConfig.avatar.file.size.max
113 } 119 }
114 120
115 get avatarExtensions () { 121 get avatarExtensions () {
116 return this.serverService.getConfig().avatar.file.extensions.join(',') 122 return this.serverConfig.avatar.file.extensions.join(',')
117 } 123 }
118 124
119 isCreation () { 125 isCreation () {
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
index 8aed8b513..e47e5f980 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-create.component.ts
@@ -47,15 +47,14 @@ export class MyAccountVideoPlaylistCreateComponent extends MyAccountVideoPlaylis
47 populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) 47 populateAsyncUserVideoChannels(this.authService, this.userVideoChannels)
48 .catch(err => console.error('Cannot populate user video channels.', err)) 48 .catch(err => console.error('Cannot populate user video channels.', err))
49 49
50 this.serverService.videoPlaylistPrivaciesLoaded.subscribe( 50 this.serverService.getVideoPlaylistPrivacies()
51 () => { 51 .subscribe(videoPlaylistPrivacies => {
52 this.videoPlaylistPrivacies = this.serverService.getVideoPlaylistPrivacies() 52 this.videoPlaylistPrivacies = videoPlaylistPrivacies
53 53
54 this.form.patchValue({ 54 this.form.patchValue({
55 privacy: VideoPlaylistPrivacy.PRIVATE 55 privacy: VideoPlaylistPrivacy.PRIVATE
56 })
56 }) 57 })
57 }
58 )
59 } 58 }
60 59
61 formValidated () { 60 formValidated () {
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
index 917ad7258..2f85cdd96 100644
--- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
+++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-update.component.ts
@@ -1,7 +1,7 @@
1import { Component, OnDestroy, OnInit } from '@angular/core' 1import { Component, OnDestroy, OnInit } from '@angular/core'
2import { ActivatedRoute, Router } from '@angular/router' 2import { ActivatedRoute, Router } from '@angular/router'
3import { AuthService, Notifier, ServerService } from '@app/core' 3import { AuthService, Notifier, ServerService } from '@app/core'
4import { Subscription } from 'rxjs' 4import { forkJoin, Subscription } from 'rxjs'
5import { I18n } from '@ngx-translate/i18n-polyfill' 5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
7import { MyAccountVideoPlaylistEdit } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-edit' 7import { MyAccountVideoPlaylistEdit } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-edit'
@@ -56,13 +56,17 @@ export class MyAccountVideoPlaylistUpdateComponent extends MyAccountVideoPlaylis
56 this.paramsSub = this.route.params 56 this.paramsSub = this.route.params
57 .pipe( 57 .pipe(
58 map(routeParams => routeParams['videoPlaylistId']), 58 map(routeParams => routeParams['videoPlaylistId']),
59 switchMap(videoPlaylistId => this.videoPlaylistService.getVideoPlaylist(videoPlaylistId)), 59 switchMap(videoPlaylistId => {
60 delayWhen(() => this.serverService.videoPlaylistPrivaciesLoaded) 60 return forkJoin([
61 this.videoPlaylistService.getVideoPlaylist(videoPlaylistId),
62 this.serverService.getVideoPlaylistPrivacies()
63 ])
64 })
61 ) 65 )
62 .subscribe( 66 .subscribe(
63 videoPlaylistToUpdate => { 67 ([ videoPlaylistToUpdate, videoPlaylistPrivacies]) => {
64 this.videoPlaylistPrivacies = this.serverService.getVideoPlaylistPrivacies()
65 this.videoPlaylistToUpdate = videoPlaylistToUpdate 68 this.videoPlaylistToUpdate = videoPlaylistToUpdate
69 this.videoPlaylistPrivacies = videoPlaylistPrivacies
66 70
67 this.hydrateFormFromPlaylist() 71 this.hydrateFormFromPlaylist()
68 }, 72 },
diff --git a/client/src/app/+my-account/my-account.component.ts b/client/src/app/+my-account/my-account.component.ts
index d98d06f8e..05dcf522d 100644
--- a/client/src/app/+my-account/my-account.component.ts
+++ b/client/src/app/+my-account/my-account.component.ts
@@ -1,20 +1,28 @@
1import { Component } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { ServerService } from '@app/core' 2import { ServerService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill' 3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { TopMenuDropdownParam } from '@app/shared/menu/top-menu-dropdown.component' 4import { TopMenuDropdownParam } from '@app/shared/menu/top-menu-dropdown.component'
5import { ServerConfig } from '@shared/models'
5 6
6@Component({ 7@Component({
7 selector: 'my-my-account', 8 selector: 'my-my-account',
8 templateUrl: './my-account.component.html', 9 templateUrl: './my-account.component.html',
9 styleUrls: [ './my-account.component.scss' ] 10 styleUrls: [ './my-account.component.scss' ]
10}) 11})
11export class MyAccountComponent { 12export class MyAccountComponent implements OnInit {
12 menuEntries: TopMenuDropdownParam[] = [] 13 menuEntries: TopMenuDropdownParam[] = []
13 14
15 private serverConfig: ServerConfig
16
14 constructor ( 17 constructor (
15 private serverService: ServerService, 18 private serverService: ServerService,
16 private i18n: I18n 19 private i18n: I18n
17 ) { 20 ) { }
21
22 ngOnInit (): void {
23 this.serverConfig = this.serverService.getTmpConfig()
24 this.serverService.getConfig()
25 .subscribe(config => this.serverConfig = config)
18 26
19 const libraryEntries: TopMenuDropdownParam = { 27 const libraryEntries: TopMenuDropdownParam = {
20 label: this.i18n('My library'), 28 label: this.i18n('My library'),
@@ -91,7 +99,7 @@ export class MyAccountComponent {
91 } 99 }
92 100
93 isVideoImportEnabled () { 101 isVideoImportEnabled () {
94 const importConfig = this.serverService.getConfig().import.videos 102 const importConfig = this.serverConfig.import.videos
95 103
96 return importConfig.http.enabled || importConfig.torrent.enabled 104 return importConfig.http.enabled || importConfig.torrent.enabled
97 } 105 }
diff --git a/client/src/app/+my-account/shared/actor-avatar-info.component.ts b/client/src/app/+my-account/shared/actor-avatar-info.component.ts
index 0289a66c3..101dfa556 100644
--- a/client/src/app/+my-account/shared/actor-avatar-info.component.ts
+++ b/client/src/app/+my-account/shared/actor-avatar-info.component.ts
@@ -1,26 +1,35 @@
1import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core' 1import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { ServerService } from '../../core/server' 2import { ServerService } from '../../core/server'
3import { VideoChannel } from '@app/shared/video-channel/video-channel.model' 3import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
4import { Account } from '@app/shared/account/account.model' 4import { Account } from '@app/shared/account/account.model'
5import { Notifier } from '@app/core' 5import { Notifier } from '@app/core'
6import { ServerConfig } from '@shared/models'
6 7
7@Component({ 8@Component({
8 selector: 'my-actor-avatar-info', 9 selector: 'my-actor-avatar-info',
9 templateUrl: './actor-avatar-info.component.html', 10 templateUrl: './actor-avatar-info.component.html',
10 styleUrls: [ './actor-avatar-info.component.scss' ] 11 styleUrls: [ './actor-avatar-info.component.scss' ]
11}) 12})
12export class ActorAvatarInfoComponent { 13export class ActorAvatarInfoComponent implements OnInit {
13 @ViewChild('avatarfileInput', { static: false }) avatarfileInput: ElementRef<HTMLInputElement> 14 @ViewChild('avatarfileInput', { static: false }) avatarfileInput: ElementRef<HTMLInputElement>
14 15
15 @Input() actor: VideoChannel | Account 16 @Input() actor: VideoChannel | Account
16 17
17 @Output() avatarChange = new EventEmitter<FormData>() 18 @Output() avatarChange = new EventEmitter<FormData>()
18 19
20 private serverConfig: ServerConfig
21
19 constructor ( 22 constructor (
20 private serverService: ServerService, 23 private serverService: ServerService,
21 private notifier: Notifier 24 private notifier: Notifier
22 ) {} 25 ) {}
23 26
27 ngOnInit (): void {
28 this.serverConfig = this.serverService.getTmpConfig()
29 this.serverService.getConfig()
30 .subscribe(config => this.serverConfig = config)
31 }
32
24 onAvatarChange () { 33 onAvatarChange () {
25 const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ] 34 const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ]
26 if (avatarfile.size > this.maxAvatarSize) { 35 if (avatarfile.size > this.maxAvatarSize) {
@@ -35,10 +44,10 @@ export class ActorAvatarInfoComponent {
35 } 44 }
36 45
37 get maxAvatarSize () { 46 get maxAvatarSize () {
38 return this.serverService.getConfig().avatar.file.size.max 47 return this.serverConfig.avatar.file.size.max
39 } 48 }
40 49
41 get avatarExtensions () { 50 get avatarExtensions () {
42 return this.serverService.getConfig().avatar.file.extensions.join(',') 51 return this.serverConfig.avatar.file.extensions.join(',')
43 } 52 }
44} 53}
diff --git a/client/src/app/+signup/+register/register-routing.module.ts b/client/src/app/+signup/+register/register-routing.module.ts
index e3a5001dc..f47e80755 100644
--- a/client/src/app/+signup/+register/register-routing.module.ts
+++ b/client/src/app/+signup/+register/register-routing.module.ts
@@ -16,7 +16,7 @@ const registerRoutes: Routes = [
16 } 16 }
17 }, 17 },
18 resolve: { 18 resolve: {
19 serverConfigLoaded: ServerConfigResolver 19 serverConfig: ServerConfigResolver
20 } 20 }
21 } 21 }
22] 22]
diff --git a/client/src/app/+signup/+register/register.component.ts b/client/src/app/+signup/+register/register.component.ts
index acec56f04..ae944ec15 100644
--- a/client/src/app/+signup/+register/register.component.ts
+++ b/client/src/app/+signup/+register/register.component.ts
@@ -4,10 +4,11 @@ import { UserService, UserValidatorsService } from '@app/shared'
4import { I18n } from '@ngx-translate/i18n-polyfill' 4import { I18n } from '@ngx-translate/i18n-polyfill'
5import { UserRegister } from '@shared/models/users/user-register.model' 5import { UserRegister } from '@shared/models/users/user-register.model'
6import { FormGroup } from '@angular/forms' 6import { FormGroup } from '@angular/forms'
7import { About } from '@shared/models/server' 7import { About, ServerConfig } from '@shared/models/server'
8import { InstanceService } from '@app/shared/instance/instance.service' 8import { InstanceService } from '@app/shared/instance/instance.service'
9import { HooksService } from '@app/core/plugins/hooks.service' 9import { HooksService } from '@app/core/plugins/hooks.service'
10import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap' 10import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
11import { ActivatedRoute } from '@angular/router'
11 12
12@Component({ 13@Component({
13 selector: 'my-register', 14 selector: 'my-register',
@@ -34,7 +35,10 @@ export class RegisterComponent implements OnInit {
34 formStepUser: FormGroup 35 formStepUser: FormGroup
35 formStepChannel: FormGroup 36 formStepChannel: FormGroup
36 37
38 private serverConfig: ServerConfig
39
37 constructor ( 40 constructor (
41 private route: ActivatedRoute,
38 private authService: AuthService, 42 private authService: AuthService,
39 private userValidatorsService: UserValidatorsService, 43 private userValidatorsService: UserValidatorsService,
40 private notifier: Notifier, 44 private notifier: Notifier,
@@ -48,10 +52,12 @@ export class RegisterComponent implements OnInit {
48 } 52 }
49 53
50 get requiresEmailVerification () { 54 get requiresEmailVerification () {
51 return this.serverService.getConfig().signup.requiresEmailVerification 55 return this.serverConfig.signup.requiresEmailVerification
52 } 56 }
53 57
54 ngOnInit (): void { 58 ngOnInit (): void {
59 this.serverConfig = this.route.snapshot.data.serverConfig
60
55 this.instanceService.getAbout() 61 this.instanceService.getAbout()
56 .subscribe( 62 .subscribe(
57 async about => { 63 async about => {
diff --git a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
index cfd471fa4..3bd604b66 100644
--- a/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
+++ b/client/src/app/+signup/+verify-account/verify-account-ask-send-email/verify-account-ask-send-email.component.ts
@@ -5,6 +5,7 @@ import { ServerService } from '@app/core/server'
5import { FormReactive, UserService } from '@app/shared' 5import { FormReactive, UserService } from '@app/shared'
6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 6import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
7import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service' 7import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
8import { ServerConfig } from '@shared/models'
8 9
9@Component({ 10@Component({
10 selector: 'my-verify-account-ask-send-email', 11 selector: 'my-verify-account-ask-send-email',
@@ -13,6 +14,7 @@ import { UserValidatorsService } from '@app/shared/forms/form-validators/user-va
13}) 14})
14 15
15export class VerifyAccountAskSendEmailComponent extends FormReactive implements OnInit { 16export class VerifyAccountAskSendEmailComponent extends FormReactive implements OnInit {
17 private serverConfig: ServerConfig
16 18
17 constructor ( 19 constructor (
18 protected formValidatorService: FormValidatorService, 20 protected formValidatorService: FormValidatorService,
@@ -27,10 +29,14 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
27 } 29 }
28 30
29 get requiresEmailVerification () { 31 get requiresEmailVerification () {
30 return this.serverService.getConfig().signup.requiresEmailVerification 32 return this.serverConfig.signup.requiresEmailVerification
31 } 33 }
32 34
33 ngOnInit () { 35 ngOnInit () {
36 this.serverConfig = this.serverService.getTmpConfig()
37 this.serverService.getConfig()
38 .subscribe(config => this.serverConfig = config)
39
34 this.buildForm({ 40 this.buildForm({
35 'verify-email-email': this.userValidatorsService.USER_EMAIL 41 'verify-email-email': this.userValidatorsService.USER_EMAIL
36 }) 42 })
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts
index 351620c59..883f36514 100644
--- a/client/src/app/app.component.ts
+++ b/client/src/app/app.component.ts
@@ -15,7 +15,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
15import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants' 15import { POP_STATE_MODAL_DISMISS } from '@app/shared/misc/constants'
16import { WelcomeModalComponent } from '@app/modal/welcome-modal.component' 16import { WelcomeModalComponent } from '@app/modal/welcome-modal.component'
17import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component' 17import { InstanceConfigWarningModalComponent } from '@app/modal/instance-config-warning-modal.component'
18import { UserRole } from '@shared/models' 18import { ServerConfig, UserRole } from '@shared/models'
19import { User } from '@app/shared' 19import { User } from '@app/shared'
20import { InstanceService } from '@app/shared/instance/instance.service' 20import { InstanceService } from '@app/shared/instance/instance.service'
21 21
@@ -33,6 +33,8 @@ export class AppComponent implements OnInit {
33 33
34 customCSS: SafeHtml 34 customCSS: SafeHtml
35 35
36 private serverConfig: ServerConfig
37
36 constructor ( 38 constructor (
37 private i18n: I18n, 39 private i18n: I18n,
38 private viewportScroller: ViewportScroller, 40 private viewportScroller: ViewportScroller,
@@ -52,7 +54,7 @@ export class AppComponent implements OnInit {
52 ) { } 54 ) { }
53 55
54 get instanceName () { 56 get instanceName () {
55 return this.serverService.getConfig().instance.name 57 return this.serverConfig.instance.name
56 } 58 }
57 59
58 get defaultRoute () { 60 get defaultRoute () {
@@ -62,6 +64,10 @@ export class AppComponent implements OnInit {
62 ngOnInit () { 64 ngOnInit () {
63 document.getElementById('incompatible-browser').className += ' browser-ok' 65 document.getElementById('incompatible-browser').className += ' browser-ok'
64 66
67 this.serverConfig = this.serverService.getTmpConfig()
68 this.serverService.getConfig()
69 .subscribe(config => this.serverConfig = config)
70
65 this.loadPlugins() 71 this.loadPlugins()
66 this.themeService.initialize() 72 this.themeService.initialize()
67 73
@@ -72,14 +78,6 @@ export class AppComponent implements OnInit {
72 this.authService.refreshUserInformation() 78 this.authService.refreshUserInformation()
73 } 79 }
74 80
75 // Load custom data from server
76 this.serverService.loadConfig()
77 this.serverService.loadVideoCategories()
78 this.serverService.loadVideoLanguages()
79 this.serverService.loadVideoLicences()
80 this.serverService.loadVideoPrivacies()
81 this.serverService.loadVideoPlaylistPrivacies()
82
83 // Do not display menu on small screens 81 // Do not display menu on small screens
84 if (this.screenService.isInSmallView()) { 82 if (this.screenService.isInSmallView()) {
85 this.isMenuDisplayed = false 83 this.isMenuDisplayed = false
@@ -187,10 +185,8 @@ export class AppComponent implements OnInit {
187 185
188 private injectJS () { 186 private injectJS () {
189 // Inject JS 187 // Inject JS
190 this.serverService.configLoaded 188 this.serverService.getConfig()
191 .subscribe(() => { 189 .subscribe(config => {
192 const config = this.serverService.getConfig()
193
194 if (config.instance.customizations.javascript) { 190 if (config.instance.customizations.javascript) {
195 try { 191 try {
196 // tslint:disable:no-eval 192 // tslint:disable:no-eval
@@ -204,17 +200,14 @@ export class AppComponent implements OnInit {
204 200
205 private injectCSS () { 201 private injectCSS () {
206 // Inject CSS if modified (admin config settings) 202 // Inject CSS if modified (admin config settings)
207 this.serverService.configLoaded 203 this.serverService.configReloaded
208 .pipe(skip(1)) // We only want to subscribe to reloads, because the CSS is already injected by the server
209 .subscribe(() => { 204 .subscribe(() => {
210 const headStyle = document.querySelector('style.custom-css-style') 205 const headStyle = document.querySelector('style.custom-css-style')
211 if (headStyle) headStyle.parentNode.removeChild(headStyle) 206 if (headStyle) headStyle.parentNode.removeChild(headStyle)
212 207
213 const config = this.serverService.getConfig()
214
215 // We test customCSS if the admin removed the css 208 // We test customCSS if the admin removed the css
216 if (this.customCSS || config.instance.customizations.css) { 209 if (this.customCSS || this.serverConfig.instance.customizations.css) {
217 const styleTag = '<style>' + config.instance.customizations.css + '</style>' 210 const styleTag = '<style>' + this.serverConfig.instance.customizations.css + '</style>'
218 this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag) 211 this.customCSS = this.domSanitizer.bypassSecurityTrustHtml(styleTag)
219 } 212 }
220 }) 213 })
@@ -227,25 +220,22 @@ export class AppComponent implements OnInit {
227 } 220 }
228 221
229 private async openModalsIfNeeded () { 222 private async openModalsIfNeeded () {
230 this.serverService.configLoaded 223 this.authService.userInformationLoaded
231 .pipe( 224 .pipe(
232 first(),
233 switchMap(() => this.authService.userInformationLoaded),
234 map(() => this.authService.getUser()), 225 map(() => this.authService.getUser()),
235 filter(user => user.role === UserRole.ADMINISTRATOR) 226 filter(user => user.role === UserRole.ADMINISTRATOR)
236 ).subscribe(user => setTimeout(() => this.openAdminModals(user))) // setTimeout because of ngIf in template 227 ).subscribe(user => setTimeout(() => this._openAdminModalsIfNeeded(user))) // setTimeout because of ngIf in template
237 } 228 }
238 229
239 private async openAdminModals (user: User) { 230 private async _openAdminModalsIfNeeded (user: User) {
240 if (user.noWelcomeModal !== true) return this.welcomeModal.show() 231 if (user.noWelcomeModal !== true) return this.welcomeModal.show()
241 232
242 const config = this.serverService.getConfig() 233 if (user.noInstanceConfigWarningModal === true || !this.serverConfig.signup.allowed) return
243 if (user.noInstanceConfigWarningModal === true || !config.signup.allowed) return
244 234
245 this.instanceService.getAbout() 235 this.instanceService.getAbout()
246 .subscribe(about => { 236 .subscribe(about => {
247 if ( 237 if (
248 config.instance.name.toLowerCase() === 'peertube' || 238 this.serverConfig.instance.name.toLowerCase() === 'peertube' ||
249 !about.instance.terms || 239 !about.instance.terms ||
250 !about.instance.administrator || 240 !about.instance.administrator ||
251 !about.instance.maintenanceLifetime 241 !about.instance.maintenanceLifetime
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 38b7328e2..dda705811 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -25,10 +25,10 @@ export function metaFactory (serverService: ServerService): MetaLoader {
25 return new MetaStaticLoader({ 25 return new MetaStaticLoader({
26 pageTitlePositioning: PageTitlePositioning.PrependPageTitle, 26 pageTitlePositioning: PageTitlePositioning.PrependPageTitle,
27 pageTitleSeparator: ' - ', 27 pageTitleSeparator: ' - ',
28 get applicationName () { return serverService.getConfig().instance.name }, 28 get applicationName () { return serverService.getTmpConfig().instance.name },
29 defaults: { 29 defaults: {
30 get title () { return serverService.getConfig().instance.name }, 30 get title () { return serverService.getTmpConfig().instance.name },
31 get description () { return serverService.getConfig().instance.shortDescription } 31 get description () { return serverService.getTmpConfig().instance.shortDescription }
32 } 32 }
33 }) 33 })
34} 34}
diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts
index e24468da5..da5114048 100644
--- a/client/src/app/core/plugins/plugin.service.ts
+++ b/client/src/app/core/plugins/plugin.service.ts
@@ -70,9 +70,9 @@ export class PluginService implements ClientHook {
70 } 70 }
71 71
72 initializePlugins () { 72 initializePlugins () {
73 this.server.configLoaded 73 this.server.getConfig()
74 .subscribe(() => { 74 .subscribe(config => {
75 this.plugins = this.server.getConfig().plugin.registered 75 this.plugins = config.plugin.registered
76 76
77 this.buildScopeStruct() 77 this.buildScopeStruct()
78 78
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts
index 43b89f08d..3982cf36f 100644
--- a/client/src/app/core/routing/redirect.service.ts
+++ b/client/src/app/core/routing/redirect.service.ts
@@ -16,15 +16,15 @@ export class RedirectService {
16 private serverService: ServerService 16 private serverService: ServerService
17 ) { 17 ) {
18 // The config is first loaded from the cache so try to get the default route 18 // The config is first loaded from the cache so try to get the default route
19 const config = this.serverService.getConfig() 19 const tmpConfig = this.serverService.getTmpConfig()
20 if (config && config.instance && config.instance.defaultClientRoute) { 20 if (tmpConfig && tmpConfig.instance && tmpConfig.instance.defaultClientRoute) {
21 RedirectService.DEFAULT_ROUTE = config.instance.defaultClientRoute 21 RedirectService.DEFAULT_ROUTE = tmpConfig.instance.defaultClientRoute
22 } 22 }
23 23
24 // Load default route 24 // Load default route
25 this.serverService.configLoaded 25 this.serverService.getConfig()
26 .subscribe(() => { 26 .subscribe(config => {
27 const defaultRouteConfig = this.serverService.getConfig().instance.defaultClientRoute 27 const defaultRouteConfig = config.instance.defaultClientRoute
28 28
29 if (defaultRouteConfig) { 29 if (defaultRouteConfig) {
30 RedirectService.DEFAULT_ROUTE = defaultRouteConfig 30 RedirectService.DEFAULT_ROUTE = defaultRouteConfig
diff --git a/client/src/app/core/routing/server-config-resolver.service.ts b/client/src/app/core/routing/server-config-resolver.service.ts
index ec7d6428f..3b7ed99bf 100644
--- a/client/src/app/core/routing/server-config-resolver.service.ts
+++ b/client/src/app/core/routing/server-config-resolver.service.ts
@@ -1,17 +1,13 @@
1import { Injectable } from '@angular/core' 1import { Injectable } from '@angular/core'
2import { Resolve } from '@angular/router' 2import { Resolve } from '@angular/router'
3import { ServerService } from '@app/core/server' 3import { ServerService } from '@app/core/server'
4import { ServerConfig } from '@shared/models'
4 5
5@Injectable() 6@Injectable()
6export class ServerConfigResolver implements Resolve<boolean> { 7export class ServerConfigResolver implements Resolve<ServerConfig> {
7 constructor ( 8 constructor (private server: ServerService) {}
8 private server: ServerService
9 ) {}
10 9
11 resolve () { 10 resolve () {
12 // FIXME: directly returning this.server.configLoaded does not seem to work 11 return this.server.getConfig()
13 return new Promise<boolean>(res => {
14 return this.server.configLoaded.subscribe(() => res(true))
15 })
16 } 12 }
17} 13}
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index fdcc51cc5..ec904bf57 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -1,34 +1,36 @@
1import { map, shareReplay, switchMap, tap } from 'rxjs/operators' 1import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient } from '@angular/common/http'
3import { Inject, Injectable, LOCALE_ID } from '@angular/core' 3import { Inject, Injectable, LOCALE_ID } from '@angular/core'
4import { peertubeLocalStorage } from '@app/shared/misc/peertube-web-storage' 4import { peertubeLocalStorage } from '@app/shared/misc/peertube-web-storage'
5import { Observable, of, ReplaySubject } from 'rxjs' 5import { Observable, of, Subject } from 'rxjs'
6import { getCompleteLocale, ServerConfig } from '../../../../../shared' 6import { getCompleteLocale, ServerConfig } from '../../../../../shared'
7import { environment } from '../../../environments/environment' 7import { environment } from '../../../environments/environment'
8import { VideoConstant, VideoPrivacy } from '../../../../../shared/models/videos' 8import { VideoConstant } from '../../../../../shared/models/videos'
9import { isDefaultLocale, peertubeTranslate } from '../../../../../shared/models/i18n' 9import { isDefaultLocale, peertubeTranslate } from '../../../../../shared/models/i18n'
10import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils' 10import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
11import { sortBy } from '@app/shared/misc/utils' 11import { sortBy } from '@app/shared/misc/utils'
12import { VideoPlaylistPrivacy } from '@shared/models/videos/playlist/video-playlist-privacy.model'
13import { cloneDeep } from 'lodash-es'
14 12
15@Injectable() 13@Injectable()
16export class ServerService { 14export class ServerService {
17 private static BASE_SERVER_URL = environment.apiUrl + '/api/v1/server/'
18 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/' 15 private static BASE_CONFIG_URL = environment.apiUrl + '/api/v1/config/'
19 private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/' 16 private static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
20 private static BASE_VIDEO_PLAYLIST_URL = environment.apiUrl + '/api/v1/video-playlists/' 17 private static BASE_VIDEO_PLAYLIST_URL = environment.apiUrl + '/api/v1/video-playlists/'
21 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' 18 private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/'
22 private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' 19 private static CONFIG_LOCAL_STORAGE_KEY = 'server-config'
23 20
24 configLoaded = new ReplaySubject<boolean>(1) 21 configReloaded = new Subject<void>()
25 videoPrivaciesLoaded = new ReplaySubject<boolean>(1)
26 videoPlaylistPrivaciesLoaded = new ReplaySubject<boolean>(1)
27 videoCategoriesLoaded = new ReplaySubject<boolean>(1)
28 videoLicencesLoaded = new ReplaySubject<boolean>(1)
29 videoLanguagesLoaded = new ReplaySubject<boolean>(1)
30 localeObservable: Observable<any>
31 22
23 private localeObservable: Observable<any>
24 private videoLicensesObservable: Observable<VideoConstant<number>[]>
25 private videoCategoriesObservable: Observable<VideoConstant<number>[]>
26 private videoPrivaciesObservable: Observable<VideoConstant<number>[]>
27 private videoPlaylistPrivaciesObservable: Observable<VideoConstant<number>[]>
28 private videoLanguagesObservable: Observable<VideoConstant<string>[]>
29 private configObservable: Observable<ServerConfig>
30
31 private configReset = false
32
33 private configLoaded = false
32 private config: ServerConfig = { 34 private config: ServerConfig = {
33 instance: { 35 instance: {
34 name: 'PeerTube', 36 name: 'PeerTube',
@@ -121,132 +123,141 @@ export class ServerService {
121 enabled: true 123 enabled: true
122 } 124 }
123 } 125 }
124 private videoCategories: Array<VideoConstant<number>> = []
125 private videoLicences: Array<VideoConstant<number>> = []
126 private videoLanguages: Array<VideoConstant<string>> = []
127 private videoPrivacies: Array<VideoConstant<VideoPrivacy>> = []
128 private videoPlaylistPrivacies: Array<VideoConstant<VideoPlaylistPrivacy>> = []
129 126
130 constructor ( 127 constructor (
131 private http: HttpClient, 128 private http: HttpClient,
132 @Inject(LOCALE_ID) private localeId: string 129 @Inject(LOCALE_ID) private localeId: string
133 ) { 130 ) {
134 this.loadServerLocale()
135 this.loadConfigLocally() 131 this.loadConfigLocally()
136 } 132 }
137 133
138 loadConfig () { 134 getServerVersionAndCommit () {
139 this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) 135 const serverVersion = this.config.serverVersion
140 .pipe(tap(this.saveConfigLocally)) 136 const commit = this.config.serverCommit || ''
141 .subscribe(data => {
142 this.config = data
143 137
144 this.configLoaded.next(true) 138 let result = serverVersion
145 }) 139 if (commit) result += '...' + commit
146 }
147 140
148 loadVideoCategories () { 141 return result
149 return this.loadAttributeEnum(ServerService.BASE_VIDEO_URL, 'categories', this.videoCategories, this.videoCategoriesLoaded, true)
150 } 142 }
151 143
152 loadVideoLicences () { 144 resetConfig () {
153 return this.loadAttributeEnum(ServerService.BASE_VIDEO_URL, 'licences', this.videoLicences, this.videoLicencesLoaded) 145 this.configLoaded = false
146 this.configReset = true
154 } 147 }
155 148
156 loadVideoLanguages () { 149 getConfig () {
157 return this.loadAttributeEnum(ServerService.BASE_VIDEO_URL, 'languages', this.videoLanguages, this.videoLanguagesLoaded, true) 150 if (this.configLoaded) return of(this.config)
158 }
159 151
160 loadVideoPrivacies () { 152 if (!this.configObservable) {
161 return this.loadAttributeEnum(ServerService.BASE_VIDEO_URL, 'privacies', this.videoPrivacies, this.videoPrivaciesLoaded) 153 this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL)
162 } 154 .pipe(
155 tap(this.saveConfigLocally),
156 tap(() => this.configLoaded = true),
157 tap(() => {
158 if (this.configReset) {
159 this.configReloaded.next()
160 this.configReset = false
161 }
162 }),
163 share()
164 )
165 }
163 166
164 loadVideoPlaylistPrivacies () { 167 return this.configObservable
165 return this.loadAttributeEnum(
166 ServerService.BASE_VIDEO_PLAYLIST_URL,
167 'privacies',
168 this.videoPlaylistPrivacies,
169 this.videoPlaylistPrivaciesLoaded
170 )
171 } 168 }
172 169
173 getConfig () { 170 getTmpConfig () {
174 return cloneDeep(this.config) 171 return this.config
175 }
176
177 getServerVersionAndCommit () {
178 const serverVersion = this.config.serverVersion
179 const commit = this.config.serverCommit || ''
180
181 let result = `v${serverVersion}`
182 if (commit) result += '...' + commit
183
184 return result
185 } 172 }
186 173
187 getVideoCategories () { 174 getVideoCategories () {
188 return cloneDeep(this.videoCategories) 175 if (!this.videoCategoriesObservable) {
176 this.videoCategoriesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'categories', true)
177 }
178
179 return this.videoCategoriesObservable.pipe(first())
189 } 180 }
190 181
191 getVideoLicences () { 182 getVideoLicences () {
192 return cloneDeep(this.videoLicences) 183 if (!this.videoLicensesObservable) {
184 this.videoLicensesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'licences')
185 }
186
187 return this.videoLicensesObservable.pipe(first())
193 } 188 }
194 189
195 getVideoLanguages () { 190 getVideoLanguages () {
196 return cloneDeep(this.videoLanguages) 191 if (!this.videoLanguagesObservable) {
192 this.videoLanguagesObservable = this.loadAttributeEnum<string>(ServerService.BASE_VIDEO_URL, 'languages', true)
193 }
194
195 return this.videoLanguagesObservable.pipe(first())
197 } 196 }
198 197
199 getVideoPrivacies () { 198 getVideoPrivacies () {
200 return cloneDeep(this.videoPrivacies) 199 if (!this.videoPrivaciesObservable) {
200 this.videoPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_URL, 'privacies')
201 }
202
203 return this.videoPrivaciesObservable.pipe(first())
201 } 204 }
202 205
203 getVideoPlaylistPrivacies () { 206 getVideoPlaylistPrivacies () {
204 return cloneDeep(this.videoPlaylistPrivacies) 207 if (!this.videoPlaylistPrivaciesObservable) {
208 this.videoPlaylistPrivaciesObservable = this.loadAttributeEnum<number>(ServerService.BASE_VIDEO_PLAYLIST_URL, 'privacies')
209 }
210
211 return this.videoPlaylistPrivaciesObservable.pipe(first())
212 }
213
214 getServerLocale () {
215 if (!this.localeObservable) {
216 const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId)
217
218 // Default locale, nothing to translate
219 if (isDefaultLocale(completeLocale)) {
220 this.localeObservable = of({}).pipe(shareReplay())
221 } else {
222 this.localeObservable = this.http
223 .get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json')
224 .pipe(shareReplay())
225 }
226 }
227
228 return this.localeObservable.pipe(first())
205 } 229 }
206 230
207 private loadAttributeEnum ( 231 private loadAttributeEnum <T extends string | number> (
208 baseUrl: string, 232 baseUrl: string,
209 attributeName: 'categories' | 'licences' | 'languages' | 'privacies', 233 attributeName: 'categories' | 'licences' | 'languages' | 'privacies',
210 hashToPopulate: VideoConstant<string | number>[],
211 notifier: ReplaySubject<boolean>,
212 sort = false 234 sort = false
213 ) { 235 ) {
214 this.localeObservable 236 return this.getServerLocale()
215 .pipe( 237 .pipe(
216 switchMap(translations => { 238 switchMap(translations => {
217 return this.http.get<{ [id: string]: string }>(baseUrl + attributeName) 239 return this.http.get<{ [ id: string ]: string }>(baseUrl + attributeName)
218 .pipe(map(data => ({ data, translations }))) 240 .pipe(map(data => ({ data, translations })))
219 }) 241 }),
220 ) 242 map(({ data, translations }) => {
221 .subscribe(({ data, translations }) => { 243 const hashToPopulate: VideoConstant<T>[] = []
222 Object.keys(data)
223 .forEach(dataKey => {
224 const label = data[ dataKey ]
225
226 hashToPopulate.push({
227 id: attributeName === 'languages' ? dataKey : parseInt(dataKey, 10),
228 label: peertubeTranslate(label, translations)
229 })
230 })
231
232 if (sort === true) sortBy(hashToPopulate, 'label')
233
234 notifier.next(true)
235 })
236 }
237 244
238 private loadServerLocale () { 245 Object.keys(data)
239 const completeLocale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId) 246 .forEach(dataKey => {
247 const label = data[ dataKey ]
240 248
241 // Default locale, nothing to translate 249 hashToPopulate.push({
242 if (isDefaultLocale(completeLocale)) { 250 id: (attributeName === 'languages' ? dataKey : parseInt(dataKey, 10)) as T,
243 this.localeObservable = of({}).pipe(shareReplay()) 251 label: peertubeTranslate(label, translations)
244 return 252 })
245 } 253 })
254
255 if (sort === true) sortBy(hashToPopulate, 'label')
246 256
247 this.localeObservable = this.http 257 return hashToPopulate
248 .get(ServerService.BASE_LOCALE_URL + completeLocale + '/server.json') 258 }),
249 .pipe(shareReplay()) 259 shareReplay()
260 )
250 } 261 }
251 262
252 private saveConfigLocally (config: ServerConfig) { 263 private saveConfigLocally (config: ServerConfig) {
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts
index 3eebc1acc..2c5873cb3 100644
--- a/client/src/app/core/theme/theme.service.ts
+++ b/client/src/app/core/theme/theme.service.ts
@@ -3,7 +3,7 @@ import { AuthService } from '@app/core/auth'
3import { ServerService } from '@app/core/server' 3import { ServerService } from '@app/core/server'
4import { environment } from '../../../environments/environment' 4import { environment } from '../../../environments/environment'
5import { PluginService } from '@app/core/plugins/plugin.service' 5import { PluginService } from '@app/core/plugins/plugin.service'
6import { ServerConfigTheme } from '@shared/models' 6import { ServerConfig, ServerConfigTheme } from '@shared/models'
7import { peertubeLocalStorage } from '@app/shared/misc/peertube-web-storage' 7import { peertubeLocalStorage } from '@app/shared/misc/peertube-web-storage'
8import { first } from 'rxjs/operators' 8import { first } from 'rxjs/operators'
9 9
@@ -20,6 +20,8 @@ export class ThemeService {
20 private themeFromLocalStorage: ServerConfigTheme 20 private themeFromLocalStorage: ServerConfigTheme
21 private themeDOMLinksFromLocalStorage: HTMLLinkElement[] = [] 21 private themeDOMLinksFromLocalStorage: HTMLLinkElement[] = []
22 22
23 private serverConfig: ServerConfig
24
23 constructor ( 25 constructor (
24 private auth: AuthService, 26 private auth: AuthService,
25 private pluginService: PluginService, 27 private pluginService: PluginService,
@@ -30,9 +32,12 @@ export class ThemeService {
30 // Try to load from local storage first, so we don't have to wait network requests 32 // Try to load from local storage first, so we don't have to wait network requests
31 this.loadAndSetFromLocalStorage() 33 this.loadAndSetFromLocalStorage()
32 34
33 this.server.configLoaded 35 this.serverConfig = this.server.getTmpConfig()
34 .subscribe(() => { 36 this.server.getConfig()
35 const themes = this.server.getConfig().theme.registered 37 .subscribe(config => {
38 this.serverConfig = config
39
40 const themes = this.serverConfig.theme.registered
36 41
37 this.removeThemeFromLocalStorageIfNeeded(themes) 42 this.removeThemeFromLocalStorageIfNeeded(themes)
38 this.injectThemes(themes) 43 this.injectThemes(themes)
@@ -77,7 +82,7 @@ export class ThemeService {
77 if (theme !== 'instance-default') return theme 82 if (theme !== 'instance-default') return theme
78 } 83 }
79 84
80 return this.server.getConfig().theme.default 85 return this.serverConfig.theme.default
81 } 86 }
82 87
83 private loadTheme (name: string) { 88 private loadTheme (name: string) {
diff --git a/client/src/app/login/login-routing.module.ts b/client/src/app/login/login-routing.module.ts
index 5a41f4e7e..22f59b4d9 100644
--- a/client/src/app/login/login-routing.module.ts
+++ b/client/src/app/login/login-routing.module.ts
@@ -15,7 +15,7 @@ const loginRoutes: Routes = [
15 } 15 }
16 }, 16 },
17 resolve: { 17 resolve: {
18 serverConfigLoaded: ServerConfigResolver 18 serverConfig: ServerConfigResolver
19 } 19 }
20 } 20 }
21] 21]
diff --git a/client/src/app/login/login.component.ts b/client/src/app/login/login.component.ts
index 911b9982f..cf923492a 100644
--- a/client/src/app/login/login.component.ts
+++ b/client/src/app/login/login.component.ts
@@ -7,7 +7,8 @@ import { I18n } from '@ngx-translate/i18n-polyfill'
7import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' 7import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
8import { LoginValidatorsService } from '@app/shared/forms/form-validators/login-validators.service' 8import { LoginValidatorsService } from '@app/shared/forms/form-validators/login-validators.service'
9import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 9import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
10import { Router } from '@angular/router' 10import { ActivatedRoute, Router } from '@angular/router'
11import { ServerConfig } from '@shared/models'
11 12
12@Component({ 13@Component({
13 selector: 'my-login', 14 selector: 'my-login',
@@ -23,10 +24,12 @@ export class LoginComponent extends FormReactive implements OnInit {
23 forgotPasswordEmail = '' 24 forgotPasswordEmail = ''
24 25
25 private openedForgotPasswordModal: NgbModalRef 26 private openedForgotPasswordModal: NgbModalRef
27 private serverConfig: ServerConfig
26 28
27 constructor ( 29 constructor (
28 public router: Router,
29 protected formValidatorService: FormValidatorService, 30 protected formValidatorService: FormValidatorService,
31 private router: Router,
32 private route: ActivatedRoute,
30 private modalService: NgbModal, 33 private modalService: NgbModal,
31 private loginValidatorsService: LoginValidatorsService, 34 private loginValidatorsService: LoginValidatorsService,
32 private authService: AuthService, 35 private authService: AuthService,
@@ -40,14 +43,16 @@ export class LoginComponent extends FormReactive implements OnInit {
40 } 43 }
41 44
42 get signupAllowed () { 45 get signupAllowed () {
43 return this.serverService.getConfig().signup.allowed === true 46 return this.serverConfig.signup.allowed === true
44 } 47 }
45 48
46 isEmailDisabled () { 49 isEmailDisabled () {
47 return this.serverService.getConfig().email.enabled === false 50 return this.serverConfig.email.enabled === false
48 } 51 }
49 52
50 ngOnInit () { 53 ngOnInit () {
54 this.serverConfig = this.route.snapshot.data.serverConfig
55
51 this.buildForm({ 56 this.buildForm({
52 username: this.loginValidatorsService.LOGIN_USERNAME, 57 username: this.loginValidatorsService.LOGIN_USERNAME,
53 password: this.loginValidatorsService.LOGIN_PASSWORD 58 password: this.loginValidatorsService.LOGIN_PASSWORD
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index c7c31577a..2d522b521 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -4,6 +4,7 @@ import { AuthService, AuthStatus, RedirectService, ServerService, ThemeService }
4import { User } from '../shared/users/user.model' 4import { User } from '../shared/users/user.model'
5import { LanguageChooserComponent } from '@app/menu/language-chooser.component' 5import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
6import { HotkeysService } from 'angular2-hotkeys' 6import { HotkeysService } from 'angular2-hotkeys'
7import { ServerConfig } from '@shared/models'
7 8
8@Component({ 9@Component({
9 selector: 'my-menu', 10 selector: 'my-menu',
@@ -18,6 +19,7 @@ export class MenuComponent implements OnInit {
18 userHasAdminAccess = false 19 userHasAdminAccess = false
19 helpVisible = false 20 helpVisible = false
20 21
22 private serverConfig: ServerConfig
21 private routesPerRight: { [ role in UserRight ]?: string } = { 23 private routesPerRight: { [ role in UserRight ]?: string } = {
22 [UserRight.MANAGE_USERS]: '/admin/users', 24 [UserRight.MANAGE_USERS]: '/admin/users',
23 [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends', 25 [UserRight.MANAGE_SERVER_FOLLOW]: '/admin/friends',
@@ -36,6 +38,10 @@ export class MenuComponent implements OnInit {
36 ) {} 38 ) {}
37 39
38 ngOnInit () { 40 ngOnInit () {
41 this.serverConfig = this.serverService.getTmpConfig()
42 this.serverService.getConfig()
43 .subscribe(config => this.serverConfig = config)
44
39 this.isLoggedIn = this.authService.isLoggedIn() 45 this.isLoggedIn = this.authService.isLoggedIn()
40 if (this.isLoggedIn === true) this.user = this.authService.getUser() 46 if (this.isLoggedIn === true) this.user = this.authService.getUser()
41 this.computeIsUserHasAdminAccess() 47 this.computeIsUserHasAdminAccess()
@@ -64,8 +70,8 @@ export class MenuComponent implements OnInit {
64 } 70 }
65 71
66 isRegistrationAllowed () { 72 isRegistrationAllowed () {
67 return this.serverService.getConfig().signup.allowed && 73 return this.serverConfig.signup.allowed &&
68 this.serverService.getConfig().signup.allowedForCurrentIP 74 this.serverConfig.signup.allowedForCurrentIP
69 } 75 }
70 76
71 getFirstAdminRightAvailable () { 77 getFirstAdminRightAvailable () {
diff --git a/client/src/app/search/search-filters.component.ts b/client/src/app/search/search-filters.component.ts
index 57131fcac..344a260df 100644
--- a/client/src/app/search/search-filters.component.ts
+++ b/client/src/app/search/search-filters.component.ts
@@ -1,10 +1,10 @@
1import { Component, EventEmitter, Input, OnInit, Output, SimpleChanges } from '@angular/core' 1import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
2import { ValidatorFn } from '@angular/forms' 2import { ValidatorFn } from '@angular/forms'
3import { VideoValidatorsService } from '@app/shared' 3import { VideoValidatorsService } from '@app/shared'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
5import { I18n } from '@ngx-translate/i18n-polyfill' 5import { I18n } from '@ngx-translate/i18n-polyfill'
6import { AdvancedSearch } from '@app/search/advanced-search.model' 6import { AdvancedSearch } from '@app/search/advanced-search.model'
7import { VideoConstant } from '../../../../shared' 7import { ServerConfig, VideoConstant } from '../../../../shared'
8 8
9@Component({ 9@Component({
10 selector: 'my-search-filters', 10 selector: 'my-search-filters',
@@ -33,6 +33,8 @@ export class SearchFiltersComponent implements OnInit {
33 originallyPublishedStartYear: string 33 originallyPublishedStartYear: string
34 originallyPublishedEndYear: string 34 originallyPublishedEndYear: string
35 35
36 private serverConfig: ServerConfig
37
36 constructor ( 38 constructor (
37 private i18n: I18n, 39 private i18n: I18n,
38 private videoValidatorsService: VideoValidatorsService, 40 private videoValidatorsService: VideoValidatorsService,
@@ -99,9 +101,13 @@ export class SearchFiltersComponent implements OnInit {
99 } 101 }
100 102
101 ngOnInit () { 103 ngOnInit () {
102 this.serverService.videoCategoriesLoaded.subscribe(() => this.videoCategories = this.serverService.getVideoCategories()) 104 this.serverConfig = this.serverService.getTmpConfig()
103 this.serverService.videoLicencesLoaded.subscribe(() => this.videoLicences = this.serverService.getVideoLicences()) 105 this.serverService.getConfig()
104 this.serverService.videoLanguagesLoaded.subscribe(() => this.videoLanguages = this.serverService.getVideoLanguages()) 106 .subscribe(config => this.serverConfig = config)
107
108 this.serverService.getVideoCategories().subscribe(categories => this.videoCategories = categories)
109 this.serverService.getVideoLicences().subscribe(licences => this.videoLicences = licences)
110 this.serverService.getVideoLanguages().subscribe(languages => this.videoLanguages = languages)
105 111
106 this.loadFromDurationRange() 112 this.loadFromDurationRange()
107 this.loadFromPublishedRange() 113 this.loadFromPublishedRange()
diff --git a/client/src/app/shared/images/preview-upload.component.ts b/client/src/app/shared/images/preview-upload.component.ts
index 44b78866e..f56f5b1f8 100644
--- a/client/src/app/shared/images/preview-upload.component.ts
+++ b/client/src/app/shared/images/preview-upload.component.ts
@@ -2,6 +2,7 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'
2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' 2import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
3import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser' 3import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
5import { ServerConfig } from '@shared/models'
5 6
6@Component({ 7@Component({
7 selector: 'my-preview-upload', 8 selector: 'my-preview-upload',
@@ -24,6 +25,7 @@ export class PreviewUploadComponent implements OnInit, ControlValueAccessor {
24 imageSrc: SafeResourceUrl 25 imageSrc: SafeResourceUrl
25 allowedExtensionsMessage = '' 26 allowedExtensionsMessage = ''
26 27
28 private serverConfig: ServerConfig
27 private file: File 29 private file: File
28 30
29 constructor ( 31 constructor (
@@ -32,14 +34,18 @@ export class PreviewUploadComponent implements OnInit, ControlValueAccessor {
32 ) {} 34 ) {}
33 35
34 get videoImageExtensions () { 36 get videoImageExtensions () {
35 return this.serverService.getConfig().video.image.extensions 37 return this.serverConfig.video.image.extensions
36 } 38 }
37 39
38 get maxVideoImageSize () { 40 get maxVideoImageSize () {
39 return this.serverService.getConfig().video.image.size.max 41 return this.serverConfig.video.image.size.max
40 } 42 }
41 43
42 ngOnInit () { 44 ngOnInit () {
45 this.serverConfig = this.serverService.getTmpConfig()
46 this.serverService.getConfig()
47 .subscribe(config => this.serverConfig = config)
48
43 this.allowedExtensionsMessage = this.videoImageExtensions.join(', ') 49 this.allowedExtensionsMessage = this.videoImageExtensions.join(', ')
44 } 50 }
45 51
diff --git a/client/src/app/shared/instance/instance-features-table.component.html b/client/src/app/shared/instance/instance-features-table.component.html
index f880a886f..fd8b3354f 100644
--- a/client/src/app/shared/instance/instance-features-table.component.html
+++ b/client/src/app/shared/instance/instance-features-table.component.html
@@ -1,6 +1,6 @@
1<div class="feature-table"> 1<div class="feature-table">
2 2
3 <table class="table" *ngIf="config"> 3 <table class="table" *ngIf="serverConfig">
4 <tr> 4 <tr>
5 <td i18n class="label">PeerTube version</td> 5 <td i18n class="label">PeerTube version</td>
6 6
@@ -19,7 +19,7 @@
19 <tr> 19 <tr>
20 <td i18n class="label">User registration allowed</td> 20 <td i18n class="label">User registration allowed</td>
21 <td> 21 <td>
22 <my-feature-boolean [value]="config.signup.allowed"></my-feature-boolean> 22 <my-feature-boolean [value]="serverConfig.signup.allowed"></my-feature-boolean>
23 </td> 23 </td>
24 </tr> 24 </tr>
25 25
@@ -30,15 +30,15 @@
30 <tr> 30 <tr>
31 <td i18n class="sub-label">Transcoding in multiple resolutions</td> 31 <td i18n class="sub-label">Transcoding in multiple resolutions</td>
32 <td> 32 <td>
33 <my-feature-boolean [value]="config.transcoding.enabledResolutions.length !== 0"></my-feature-boolean> 33 <my-feature-boolean [value]="serverConfig.transcoding.enabledResolutions.length !== 0"></my-feature-boolean>
34 </td> 34 </td>
35 </tr> 35 </tr>
36 36
37 <tr> 37 <tr>
38 <td i18n class="sub-label">Video uploads</td> 38 <td i18n class="sub-label">Video uploads</td>
39 <td> 39 <td>
40 <span *ngIf="config.autoBlacklist.videos.ofUsers.enabled">Requires manual validation by moderators</span> 40 <span *ngIf="serverConfig.autoBlacklist.videos.ofUsers.enabled">Requires manual validation by moderators</span>
41 <span *ngIf="!config.autoBlacklist.videos.ofUsers.enabled">Automatically published</span> 41 <span *ngIf="!serverConfig.autoBlacklist.videos.ofUsers.enabled">Automatically published</span>
42 </td> 42 </td>
43 </tr> 43 </tr>
44 44
@@ -69,14 +69,14 @@
69 <tr> 69 <tr>
70 <td i18n class="sub-label">HTTP import (YouTube, Vimeo, direct URL...)</td> 70 <td i18n class="sub-label">HTTP import (YouTube, Vimeo, direct URL...)</td>
71 <td> 71 <td>
72 <my-feature-boolean [value]="config.import.videos.http.enabled"></my-feature-boolean> 72 <my-feature-boolean [value]="serverConfig.import.videos.http.enabled"></my-feature-boolean>
73 </td> 73 </td>
74 </tr> 74 </tr>
75 75
76 <tr> 76 <tr>
77 <td i18n class="sub-label">Torrent import</td> 77 <td i18n class="sub-label">Torrent import</td>
78 <td> 78 <td>
79 <my-feature-boolean [value]="config.import.videos.torrent.enabled"></my-feature-boolean> 79 <my-feature-boolean [value]="serverConfig.import.videos.torrent.enabled"></my-feature-boolean>
80 </td> 80 </td>
81 </tr> 81 </tr>
82 82
@@ -88,7 +88,7 @@
88 <tr> 88 <tr>
89 <td i18n class="sub-label">P2P enabled</td> 89 <td i18n class="sub-label">P2P enabled</td>
90 <td> 90 <td>
91 <my-feature-boolean [value]="config.tracker.enabled"></my-feature-boolean> 91 <my-feature-boolean [value]="serverConfig.tracker.enabled"></my-feature-boolean>
92 </td> 92 </td>
93 </tr> 93 </tr>
94 </table> 94 </table>
diff --git a/client/src/app/shared/instance/instance-features-table.component.ts b/client/src/app/shared/instance/instance-features-table.component.ts
index 1661f1efe..8fd15ebad 100644
--- a/client/src/app/shared/instance/instance-features-table.component.ts
+++ b/client/src/app/shared/instance/instance-features-table.component.ts
@@ -10,7 +10,7 @@ import { ServerConfig } from '@shared/models'
10}) 10})
11export class InstanceFeaturesTableComponent implements OnInit { 11export class InstanceFeaturesTableComponent implements OnInit {
12 quotaHelpIndication = '' 12 quotaHelpIndication = ''
13 config: ServerConfig 13 serverConfig: ServerConfig
14 14
15 constructor ( 15 constructor (
16 private i18n: I18n, 16 private i18n: I18n,
@@ -19,29 +19,34 @@ export class InstanceFeaturesTableComponent implements OnInit {
19 } 19 }
20 20
21 get initialUserVideoQuota () { 21 get initialUserVideoQuota () {
22 return this.serverService.getConfig().user.videoQuota 22 return this.serverConfig.user.videoQuota
23 } 23 }
24 24
25 get dailyUserVideoQuota () { 25 get dailyUserVideoQuota () {
26 return Math.min(this.initialUserVideoQuota, this.serverService.getConfig().user.videoQuotaDaily) 26 return Math.min(this.initialUserVideoQuota, this.serverConfig.user.videoQuotaDaily)
27 } 27 }
28 28
29 ngOnInit () { 29 ngOnInit () {
30 this.serverService.configLoaded 30 this.serverConfig = this.serverService.getTmpConfig()
31 .subscribe(() => { 31 this.serverService.getConfig()
32 this.config = this.serverService.getConfig() 32 .subscribe(config => {
33 this.serverConfig = config
33 this.buildQuotaHelpIndication() 34 this.buildQuotaHelpIndication()
34 }) 35 })
35 } 36 }
36 37
37 buildNSFWLabel () { 38 buildNSFWLabel () {
38 const policy = this.serverService.getConfig().instance.defaultNSFWPolicy 39 const policy = this.serverConfig.instance.defaultNSFWPolicy
39 40
40 if (policy === 'do_not_list') return this.i18n('Hidden') 41 if (policy === 'do_not_list') return this.i18n('Hidden')
41 if (policy === 'blur') return this.i18n('Blurred with confirmation request') 42 if (policy === 'blur') return this.i18n('Blurred with confirmation request')
42 if (policy === 'display') return this.i18n('Displayed') 43 if (policy === 'display') return this.i18n('Displayed')
43 } 44 }
44 45
46 getServerVersionAndCommit () {
47 return this.serverService.getServerVersionAndCommit()
48 }
49
45 private getApproximateTime (seconds: number) { 50 private getApproximateTime (seconds: number) {
46 const hours = Math.floor(seconds / 3600) 51 const hours = Math.floor(seconds / 3600)
47 let pluralSuffix = '' 52 let pluralSuffix = ''
@@ -53,10 +58,6 @@ export class InstanceFeaturesTableComponent implements OnInit {
53 return this.i18n('~ {{minutes}} {minutes, plural, =1 {minute} other {minutes}}', { minutes }) 58 return this.i18n('~ {{minutes}} {minutes, plural, =1 {minute} other {minutes}}', { minutes })
54 } 59 }
55 60
56 getServerVersionAndCommit () {
57 return this.serverService.getServerVersionAndCommit()
58 }
59
60 private buildQuotaHelpIndication () { 61 private buildQuotaHelpIndication () {
61 if (this.initialUserVideoQuota === -1) return 62 if (this.initialUserVideoQuota === -1) return
62 63
diff --git a/client/src/app/shared/instance/instance.service.ts b/client/src/app/shared/instance/instance.service.ts
index 44b413fa4..8b26063fb 100644
--- a/client/src/app/shared/instance/instance.service.ts
+++ b/client/src/app/shared/instance/instance.service.ts
@@ -1,4 +1,4 @@
1import { catchError } from 'rxjs/operators' 1import { catchError, map } from 'rxjs/operators'
2import { HttpClient } from '@angular/common/http' 2import { HttpClient } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { environment } from '../../../environments/environment' 4import { environment } from '../../../environments/environment'
@@ -7,6 +7,7 @@ import { About } from '../../../../../shared/models/server'
7import { MarkdownService } from '@app/shared/renderer' 7import { MarkdownService } from '@app/shared/renderer'
8import { peertubeTranslate } from '@shared/models' 8import { peertubeTranslate } from '@shared/models'
9import { ServerService } from '@app/core' 9import { ServerService } from '@app/core'
10import { forkJoin } from 'rxjs'
10 11
11@Injectable() 12@Injectable()
12export class InstanceService { 13export class InstanceService {
@@ -57,25 +58,35 @@ export class InstanceService {
57 return html 58 return html
58 } 59 }
59 60
60 buildTranslatedLanguages (about: About, translations: any) { 61 buildTranslatedLanguages (about: About) {
61 const languagesArray = this.serverService.getVideoLanguages() 62 return forkJoin([
63 this.serverService.getVideoLanguages(),
64 this.serverService.getServerLocale()
65 ]).pipe(
66 map(([ languagesArray, translations ]) => {
67 return about.instance.languages
68 .map(l => {
69 const languageObj = languagesArray.find(la => la.id === l)
62 70
63 return about.instance.languages 71 return peertubeTranslate(languageObj.label, translations)
64 .map(l => { 72 })
65 const languageObj = languagesArray.find(la => la.id === l) 73 })
66 74 )
67 return peertubeTranslate(languageObj.label, translations)
68 })
69 } 75 }
70 76
71 buildTranslatedCategories (about: About, translations: any) { 77 buildTranslatedCategories (about: About) {
72 const categoriesArray = this.serverService.getVideoCategories() 78 return forkJoin([
73 79 this.serverService.getVideoCategories(),
74 return about.instance.categories 80 this.serverService.getServerLocale()
75 .map(c => { 81 ]).pipe(
76 const categoryObj = categoriesArray.find(ca => ca.id === c) 82 map(([ categoriesArray, translations ]) => {
83 return about.instance.categories
84 .map(c => {
85 const categoryObj = categoriesArray.find(ca => ca.id === c)
77 86
78 return peertubeTranslate(categoryObj.label, translations) 87 return peertubeTranslate(categoryObj.label, translations)
79 }) 88 })
89 })
90 )
80 } 91 }
81} 92}
diff --git a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
index e9d4c1437..d82dc3d94 100644
--- a/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
+++ b/client/src/app/shared/moderation/user-moderation-dropdown.component.ts
@@ -1,4 +1,4 @@
1import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core' 1import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'
2import { I18n } from '@ngx-translate/i18n-polyfill' 2import { I18n } from '@ngx-translate/i18n-polyfill'
3import { DropdownAction } from '@app/shared/buttons/action-dropdown.component' 3import { DropdownAction } from '@app/shared/buttons/action-dropdown.component'
4import { UserBanModalComponent } from '@app/shared/moderation/user-ban-modal.component' 4import { UserBanModalComponent } from '@app/shared/moderation/user-ban-modal.component'
@@ -7,12 +7,13 @@ import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core'
7import { User, UserRight } from '../../../../../shared/models/users' 7import { User, UserRight } from '../../../../../shared/models/users'
8import { Account } from '@app/shared/account/account.model' 8import { Account } from '@app/shared/account/account.model'
9import { BlocklistService } from '@app/shared/blocklist' 9import { BlocklistService } from '@app/shared/blocklist'
10import { ServerConfig } from '@shared/models'
10 11
11@Component({ 12@Component({
12 selector: 'my-user-moderation-dropdown', 13 selector: 'my-user-moderation-dropdown',
13 templateUrl: './user-moderation-dropdown.component.html' 14 templateUrl: './user-moderation-dropdown.component.html'
14}) 15})
15export class UserModerationDropdownComponent implements OnChanges { 16export class UserModerationDropdownComponent implements OnInit, OnChanges {
16 @ViewChild('userBanModal', { static: false }) userBanModal: UserBanModalComponent 17 @ViewChild('userBanModal', { static: false }) userBanModal: UserBanModalComponent
17 18
18 @Input() user: User 19 @Input() user: User
@@ -26,6 +27,8 @@ export class UserModerationDropdownComponent implements OnChanges {
26 27
27 userActions: DropdownAction<{ user: User, account: Account }>[][] = [] 28 userActions: DropdownAction<{ user: User, account: Account }>[][] = []
28 29
30 private serverConfig: ServerConfig
31
29 constructor ( 32 constructor (
30 private authService: AuthService, 33 private authService: AuthService,
31 private notifier: Notifier, 34 private notifier: Notifier,
@@ -38,7 +41,13 @@ export class UserModerationDropdownComponent implements OnChanges {
38 ) { } 41 ) { }
39 42
40 get requiresEmailVerification () { 43 get requiresEmailVerification () {
41 return this.serverService.getConfig().signup.requiresEmailVerification 44 return this.serverConfig.signup.requiresEmailVerification
45 }
46
47 ngOnInit (): void {
48 this.serverConfig = this.serverService.getTmpConfig()
49 this.serverService.getConfig()
50 .subscribe(config => this.serverConfig = config)
42 } 51 }
43 52
44 ngOnChanges () { 53 ngOnChanges () {
diff --git a/client/src/app/shared/overview/overview.service.ts b/client/src/app/shared/overview/overview.service.ts
index bd4068925..79cb781f7 100644
--- a/client/src/app/shared/overview/overview.service.ts
+++ b/client/src/app/shared/overview/overview.service.ts
@@ -60,7 +60,7 @@ export class OverviewService {
60 .pipe( 60 .pipe(
61 // Translate categories 61 // Translate categories
62 switchMap(() => { 62 switchMap(() => {
63 return this.serverService.localeObservable 63 return this.serverService.getServerLocale()
64 .pipe( 64 .pipe(
65 tap(translations => { 65 tap(translations => {
66 for (const c of videosOverviewResult.categories) { 66 for (const c of videosOverviewResult.categories) {
diff --git a/client/src/app/shared/video-caption/video-caption.service.ts b/client/src/app/shared/video-caption/video-caption.service.ts
index 977f6253a..6bfe67435 100644
--- a/client/src/app/shared/video-caption/video-caption.service.ts
+++ b/client/src/app/shared/video-caption/video-caption.service.ts
@@ -22,7 +22,7 @@ export class VideoCaptionService {
22 return this.authHttp.get<ResultList<VideoCaption>>(VideoService.BASE_VIDEO_URL + videoId + '/captions') 22 return this.authHttp.get<ResultList<VideoCaption>>(VideoService.BASE_VIDEO_URL + videoId + '/captions')
23 .pipe( 23 .pipe(
24 switchMap(captionsResult => { 24 switchMap(captionsResult => {
25 return this.serverService.localeObservable 25 return this.serverService.getServerLocale()
26 .pipe(map(translations => ({ captionsResult, translations }))) 26 .pipe(map(translations => ({ captionsResult, translations })))
27 }), 27 }),
28 map(({ captionsResult, translations }) => { 28 map(({ captionsResult, translations }) => {
diff --git a/client/src/app/shared/video-import/video-import.service.ts b/client/src/app/shared/video-import/video-import.service.ts
index 7ae13154d..3e3fb7dfb 100644
--- a/client/src/app/shared/video-import/video-import.service.ts
+++ b/client/src/app/shared/video-import/video-import.service.ts
@@ -91,7 +91,7 @@ export class VideoImportService {
91 } 91 }
92 92
93 private extractVideoImports (result: ResultList<VideoImport>): Observable<ResultList<VideoImport>> { 93 private extractVideoImports (result: ResultList<VideoImport>): Observable<ResultList<VideoImport>> {
94 return this.serverService.localeObservable 94 return this.serverService.getServerLocale()
95 .pipe( 95 .pipe(
96 map(translations => { 96 map(translations => {
97 result.data.forEach(d => 97 result.data.forEach(d =>
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts
index a8e5a4885..cd592eab0 100644
--- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts
+++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts
@@ -1,6 +1,6 @@
1import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' 1import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'
2import { Video } from '@app/shared/video/video.model' 2import { Video } from '@app/shared/video/video.model'
3import { VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models' 3import { ServerConfig, VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models'
4import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' 4import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core'
5import { ActivatedRoute } from '@angular/router' 5import { ActivatedRoute } from '@angular/router'
6import { I18n } from '@ngx-translate/i18n-polyfill' 6import { I18n } from '@ngx-translate/i18n-polyfill'
@@ -17,7 +17,7 @@ import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-
17 templateUrl: './video-playlist-element-miniature.component.html', 17 templateUrl: './video-playlist-element-miniature.component.html',
18 changeDetection: ChangeDetectionStrategy.OnPush 18 changeDetection: ChangeDetectionStrategy.OnPush
19}) 19})
20export class VideoPlaylistElementMiniatureComponent { 20export class VideoPlaylistElementMiniatureComponent implements OnInit {
21 @ViewChild('moreDropdown', { static: false }) moreDropdown: NgbDropdown 21 @ViewChild('moreDropdown', { static: false }) moreDropdown: NgbDropdown
22 22
23 @Input() playlist: VideoPlaylist 23 @Input() playlist: VideoPlaylist
@@ -39,6 +39,8 @@ export class VideoPlaylistElementMiniatureComponent {
39 stopTimestamp: number 39 stopTimestamp: number
40 } = {} as any 40 } = {} as any
41 41
42 private serverConfig: ServerConfig
43
42 constructor ( 44 constructor (
43 private authService: AuthService, 45 private authService: AuthService,
44 private serverService: ServerService, 46 private serverService: ServerService,
@@ -51,6 +53,15 @@ export class VideoPlaylistElementMiniatureComponent {
51 private cdr: ChangeDetectorRef 53 private cdr: ChangeDetectorRef
52 ) {} 54 ) {}
53 55
56 ngOnInit (): void {
57 this.serverConfig = this.serverService.getTmpConfig()
58 this.serverService.getConfig()
59 .subscribe(config => {
60 this.serverConfig = config
61 this.cdr.detectChanges()
62 })
63 }
64
54 isUnavailable (e: VideoPlaylistElement) { 65 isUnavailable (e: VideoPlaylistElement) {
55 return e.type === VideoPlaylistElementType.UNAVAILABLE 66 return e.type === VideoPlaylistElementType.UNAVAILABLE
56 } 67 }
@@ -80,7 +91,7 @@ export class VideoPlaylistElementMiniatureComponent {
80 } 91 }
81 92
82 isVideoBlur (video: Video) { 93 isVideoBlur (video: Video) {
83 return video.isVideoNSFWForUser(this.authService.getUser(), this.serverService.getConfig()) 94 return video.isVideoNSFWForUser(this.authService.getUser(), this.serverConfig)
84 } 95 }
85 96
86 removeFromPlaylist (playlistElement: VideoPlaylistElement) { 97 removeFromPlaylist (playlistElement: VideoPlaylistElement) {
diff --git a/client/src/app/shared/video-playlist/video-playlist.service.ts b/client/src/app/shared/video-playlist/video-playlist.service.ts
index 42791af86..2945b4959 100644
--- a/client/src/app/shared/video-playlist/video-playlist.service.ts
+++ b/client/src/app/shared/video-playlist/video-playlist.service.ts
@@ -173,7 +173,7 @@ export class VideoPlaylistService {
173 } 173 }
174 174
175 extractPlaylists (result: ResultList<VideoPlaylistServerModel>) { 175 extractPlaylists (result: ResultList<VideoPlaylistServerModel>) {
176 return this.serverService.localeObservable 176 return this.serverService.getServerLocale()
177 .pipe( 177 .pipe(
178 map(translations => { 178 map(translations => {
179 const playlistsJSON = result.data 179 const playlistsJSON = result.data
@@ -190,12 +190,12 @@ export class VideoPlaylistService {
190 } 190 }
191 191
192 extractPlaylist (playlist: VideoPlaylistServerModel) { 192 extractPlaylist (playlist: VideoPlaylistServerModel) {
193 return this.serverService.localeObservable 193 return this.serverService.getServerLocale()
194 .pipe(map(translations => new VideoPlaylist(playlist, translations))) 194 .pipe(map(translations => new VideoPlaylist(playlist, translations)))
195 } 195 }
196 196
197 extractVideoPlaylistElements (result: ResultList<ServerVideoPlaylistElement>) { 197 extractVideoPlaylistElements (result: ResultList<ServerVideoPlaylistElement>) {
198 return this.serverService.localeObservable 198 return this.serverService.getServerLocale()
199 .pipe( 199 .pipe(
200 map(translations => { 200 map(translations => {
201 const elementsJson = result.data 201 const elementsJson = result.data
diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts
index 2926b179b..faeea27d9 100644
--- a/client/src/app/shared/video/abstract-video-list.ts
+++ b/client/src/app/shared/video/abstract-video-list.ts
@@ -13,7 +13,7 @@ import { Notifier, ServerService } from '@app/core'
13import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' 13import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
14import { I18n } from '@ngx-translate/i18n-polyfill' 14import { I18n } from '@ngx-translate/i18n-polyfill'
15import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' 15import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date'
16import { ResultList } from '@shared/models' 16import { ResultList, ServerConfig } from '@shared/models'
17 17
18enum GroupDate { 18enum GroupDate {
19 UNKNOWN = 0, 19 UNKNOWN = 0,
@@ -61,6 +61,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
61 61
62 onDataSubject = new Subject<any[]>() 62 onDataSubject = new Subject<any[]>()
63 63
64 protected serverConfig: ServerConfig
65
64 protected abstract notifier: Notifier 66 protected abstract notifier: Notifier
65 protected abstract authService: AuthService 67 protected abstract authService: AuthService
66 protected abstract route: ActivatedRoute 68 protected abstract route: ActivatedRoute
@@ -85,6 +87,10 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
85 } 87 }
86 88
87 ngOnInit () { 89 ngOnInit () {
90 this.serverConfig = this.serverService.getTmpConfig()
91 this.serverService.getConfig()
92 .subscribe(config => this.serverConfig = config)
93
88 this.groupedDateLabels = { 94 this.groupedDateLabels = {
89 [GroupDate.UNKNOWN]: null, 95 [GroupDate.UNKNOWN]: null,
90 [GroupDate.TODAY]: this.i18n('Today'), 96 [GroupDate.TODAY]: this.i18n('Today'),
@@ -251,7 +257,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
251 } 257 }
252 258
253 let path = this.router.url 259 let path = this.router.url
254 if (!path || path === '/') path = this.serverService.getConfig().instance.defaultClientRoute 260 if (!path || path === '/') path = this.serverConfig.instance.defaultClientRoute
255 261
256 this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' }) 262 this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' })
257 } 263 }
diff --git a/client/src/app/shared/video/video-miniature.component.ts b/client/src/app/shared/video/video-miniature.component.ts
index d5c7dfd9b..9fffc7ddb 100644
--- a/client/src/app/shared/video/video-miniature.component.ts
+++ b/client/src/app/shared/video/video-miniature.component.ts
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Input, LOCALE
2import { User } from '../users' 2import { User } from '../users'
3import { Video } from './video.model' 3import { Video } from './video.model'
4import { ServerService } from '@app/core' 4import { ServerService } from '@app/core'
5import { VideoPrivacy, VideoState } from '../../../../../shared' 5import { ServerConfig, VideoPrivacy, VideoState } from '../../../../../shared'
6import { I18n } from '@ngx-translate/i18n-polyfill' 6import { I18n } from '@ngx-translate/i18n-polyfill'
7import { VideoActionsDisplayType } from '@app/shared/video/video-actions-dropdown.component' 7import { VideoActionsDisplayType } from '@app/shared/video/video-actions-dropdown.component'
8import { ScreenService } from '@app/shared/misc/screen.service' 8import { ScreenService } from '@app/shared/misc/screen.service'
@@ -55,6 +55,7 @@ export class VideoMiniatureComponent implements OnInit {
55 report: true 55 report: true
56 } 56 }
57 showActions = false 57 showActions = false
58 serverConfig: ServerConfig
58 59
59 private ownerDisplayTypeChosen: 'account' | 'videoChannel' 60 private ownerDisplayTypeChosen: 'account' | 'videoChannel'
60 61
@@ -66,10 +67,14 @@ export class VideoMiniatureComponent implements OnInit {
66 ) { } 67 ) { }
67 68
68 get isVideoBlur () { 69 get isVideoBlur () {
69 return this.video.isVideoNSFWForUser(this.user, this.serverService.getConfig()) 70 return this.video.isVideoNSFWForUser(this.user, this.serverConfig)
70 } 71 }
71 72
72 ngOnInit () { 73 ngOnInit () {
74 this.serverConfig = this.serverService.getTmpConfig()
75 this.serverService.getConfig()
76 .subscribe(config => this.serverConfig = config)
77
73 this.setUpBy() 78 this.setUpBy()
74 79
75 // We rely on mouseenter to lazy load actions 80 // We rely on mouseenter to lazy load actions
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts
index b0fa55966..9adf46495 100644
--- a/client/src/app/shared/video/video.service.ts
+++ b/client/src/app/shared/video/video.service.ts
@@ -64,7 +64,7 @@ export class VideoService implements VideosProvider {
64 } 64 }
65 65
66 getVideo (options: { videoId: string }): Observable<VideoDetails> { 66 getVideo (options: { videoId: string }): Observable<VideoDetails> {
67 return this.serverService.localeObservable 67 return this.serverService.getServerLocale()
68 .pipe( 68 .pipe(
69 switchMap(translations => { 69 switchMap(translations => {
70 return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + options.videoId) 70 return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + options.videoId)
@@ -315,7 +315,7 @@ export class VideoService implements VideosProvider {
315 } 315 }
316 316
317 extractVideos (result: ResultList<VideoServerModel>) { 317 extractVideos (result: ResultList<VideoServerModel>) {
318 return this.serverService.localeObservable 318 return this.serverService.getServerLocale()
319 .pipe( 319 .pipe(
320 map(translations => { 320 map(translations => {
321 const videosJson = result.data 321 const videosJson = result.data
diff --git a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts
index 86c6e03e7..1a9bf5171 100644
--- a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts
+++ b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts
@@ -5,7 +5,7 @@ import { VideoCaptionsValidatorsService } from '@app/shared/forms/form-validator
5import { ServerService } from '@app/core' 5import { ServerService } from '@app/core'
6import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' 6import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
7import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' 7import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
8import { VideoConstant } from '../../../../../../shared' 8import { ServerConfig, VideoConstant } from '../../../../../../shared'
9 9
10@Component({ 10@Component({
11 selector: 'my-video-caption-add-modal', 11 selector: 'my-video-caption-add-modal',
@@ -15,6 +15,7 @@ import { VideoConstant } from '../../../../../../shared'
15 15
16export class VideoCaptionAddModalComponent extends FormReactive implements OnInit { 16export class VideoCaptionAddModalComponent extends FormReactive implements OnInit {
17 @Input() existingCaptions: string[] 17 @Input() existingCaptions: string[]
18 @Input() serverConfig: ServerConfig
18 19
19 @Output() captionAdded = new EventEmitter<VideoCaptionEdit>() 20 @Output() captionAdded = new EventEmitter<VideoCaptionEdit>()
20 21
@@ -35,15 +36,16 @@ export class VideoCaptionAddModalComponent extends FormReactive implements OnIni
35 } 36 }
36 37
37 get videoCaptionExtensions () { 38 get videoCaptionExtensions () {
38 return this.serverService.getConfig().videoCaption.file.extensions 39 return this.serverConfig.videoCaption.file.extensions
39 } 40 }
40 41
41 get videoCaptionMaxSize () { 42 get videoCaptionMaxSize () {
42 return this.serverService.getConfig().videoCaption.file.size.max 43 return this.serverConfig.videoCaption.file.size.max
43 } 44 }
44 45
45 ngOnInit () { 46 ngOnInit () {
46 this.videoCaptionLanguages = this.serverService.getVideoLanguages() 47 this.serverService.getVideoLanguages()
48 .subscribe(languages => this.videoCaptionLanguages = languages)
47 49
48 this.buildForm({ 50 this.buildForm({
49 language: this.videoCaptionsValidatorsService.VIDEO_CAPTION_LANGUAGE, 51 language: this.videoCaptionsValidatorsService.VIDEO_CAPTION_LANGUAGE,
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html
index e2a222037..e40649d95 100644
--- a/client/src/app/videos/+video-edit/shared/video-edit.component.html
+++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html
@@ -269,5 +269,5 @@
269</div> 269</div>
270 270
271<my-video-caption-add-modal 271<my-video-caption-add-modal
272 #videoCaptionAddModal [existingCaptions]="existingCaptions" (captionAdded)="onCaptionAdded($event)" 272 #videoCaptionAddModal [existingCaptions]="existingCaptions" [serverConfig]="serverConfig" (captionAdded)="onCaptionAdded($event)"
273></my-video-caption-add-modal> 273></my-video-caption-add-modal>
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 d0d5e2a2b..982e071ad 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
@@ -12,7 +12,7 @@ import { VideoCaptionService } from '@app/shared/video-caption'
12import { VideoCaptionAddModalComponent } from '@app/videos/+video-edit/shared/video-caption-add-modal.component' 12import { VideoCaptionAddModalComponent } from '@app/videos/+video-edit/shared/video-caption-add-modal.component'
13import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' 13import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
14import { removeElementFromArray } from '@app/shared/misc/utils' 14import { removeElementFromArray } from '@app/shared/misc/utils'
15import { VideoConstant, VideoPrivacy } from '../../../../../../shared' 15import { ServerConfig, VideoConstant, VideoPrivacy } from '../../../../../../shared'
16import { VideoService } from '@app/shared/video/video.service' 16import { VideoService } from '@app/shared/video/video.service'
17 17
18@Component({ 18@Component({
@@ -51,6 +51,8 @@ export class VideoEditComponent implements OnInit, OnDestroy {
51 calendarTimezone: string 51 calendarTimezone: string
52 calendarDateFormat: string 52 calendarDateFormat: string
53 53
54 serverConfig: ServerConfig
55
54 private schedulerInterval: any 56 private schedulerInterval: any
55 private firstPatchDone = false 57 private firstPatchDone = false
56 private initialVideoCaptions: string[] = [] 58 private initialVideoCaptions: string[] = []
@@ -130,12 +132,19 @@ export class VideoEditComponent implements OnInit, OnDestroy {
130 ngOnInit () { 132 ngOnInit () {
131 this.updateForm() 133 this.updateForm()
132 134
133 this.videoCategories = this.serverService.getVideoCategories() 135 this.serverService.getVideoCategories()
134 this.videoLicences = this.serverService.getVideoLicences() 136 .subscribe(res => this.videoCategories = res)
135 this.videoLanguages = this.serverService.getVideoLanguages() 137 this.serverService.getVideoLicences()
138 .subscribe(res => this.videoLicences = res)
139 this.serverService.getVideoLanguages()
140 .subscribe(res => this.videoLanguages = res)
141
142 this.serverService.getVideoPrivacies()
143 .subscribe(privacies => this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies))
136 144
137 const privacies = this.serverService.getVideoPrivacies() 145 this.serverConfig = this.serverService.getTmpConfig()
138 this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies) 146 this.serverService.getConfig()
147 .subscribe(config => this.serverConfig = config)
139 148
140 this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) 149 this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id)
141 150
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-send.ts b/client/src/app/videos/+video-edit/video-add-components/video-send.ts
index 580c123a0..b32f16950 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-send.ts
+++ b/client/src/app/videos/+video-edit/video-add-components/video-send.ts
@@ -3,7 +3,7 @@ import { LoadingBarService } from '@ngx-loading-bar/core'
3import { AuthService, Notifier, ServerService } from '@app/core' 3import { AuthService, Notifier, ServerService } from '@app/core'
4import { catchError, switchMap, tap } from 'rxjs/operators' 4import { catchError, switchMap, tap } from 'rxjs/operators'
5import { FormReactive } from '@app/shared' 5import { FormReactive } from '@app/shared'
6import { VideoConstant, VideoPrivacy } from '../../../../../../shared' 6import { ServerConfig, VideoConstant, VideoPrivacy } from '../../../../../../shared'
7import { VideoService } from '@app/shared/video/video.service' 7import { VideoService } from '@app/shared/video/video.service'
8import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' 8import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model'
9import { VideoCaptionService } from '@app/shared/video-caption' 9import { VideoCaptionService } from '@app/shared/video-caption'
@@ -29,6 +29,7 @@ export abstract class VideoSend extends FormReactive implements OnInit {
29 protected serverService: ServerService 29 protected serverService: ServerService
30 protected videoService: VideoService 30 protected videoService: VideoService
31 protected videoCaptionService: VideoCaptionService 31 protected videoCaptionService: VideoCaptionService
32 protected serverConfig: ServerConfig
32 33
33 abstract canDeactivate (): CanComponentDeactivateResult 34 abstract canDeactivate (): CanComponentDeactivateResult
34 35
@@ -38,10 +39,14 @@ export abstract class VideoSend extends FormReactive implements OnInit {
38 populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) 39 populateAsyncUserVideoChannels(this.authService, this.userVideoChannels)
39 .then(() => this.firstStepChannelId = this.userVideoChannels[ 0 ].id) 40 .then(() => this.firstStepChannelId = this.userVideoChannels[ 0 ].id)
40 41
41 this.serverService.videoPrivaciesLoaded 42 this.serverConfig = this.serverService.getTmpConfig()
43 this.serverService.getConfig()
44 .subscribe(config => this.serverConfig = config)
45
46 this.serverService.getVideoPrivacies()
42 .subscribe( 47 .subscribe(
43 () => { 48 privacies => {
44 this.videoPrivacies = this.serverService.getVideoPrivacies() 49 this.videoPrivacies = privacies
45 50
46 this.firstStepPrivacyId = this.DEFAULT_VIDEO_PRIVACY 51 this.firstStepPrivacyId = this.DEFAULT_VIDEO_PRIVACY
47 }) 52 })
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts
index 23b79edd3..28e10e562 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts
+++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts
@@ -14,6 +14,7 @@ import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.
14import { FormValidatorService, UserService } from '@app/shared' 14import { FormValidatorService, UserService } from '@app/shared'
15import { VideoCaptionService } from '@app/shared/video-caption' 15import { VideoCaptionService } from '@app/shared/video-caption'
16import { scrollToTop } from '@app/shared/misc/utils' 16import { scrollToTop } from '@app/shared/misc/utils'
17import { ServerConfig } from '@shared/models'
17 18
18@Component({ 19@Component({
19 selector: 'my-video-upload', 20 selector: 'my-video-upload',
@@ -70,7 +71,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
70 } 71 }
71 72
72 get videoExtensions () { 73 get videoExtensions () {
73 return this.serverService.getConfig().video.file.extensions.join(',') 74 return this.serverConfig.video.file.extensions.join(',')
74 } 75 }
75 76
76 ngOnInit () { 77 ngOnInit () {
@@ -155,7 +156,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
155 } 156 }
156 157
157 const privacy = this.firstStepPrivacyId.toString() 158 const privacy = this.firstStepPrivacyId.toString()
158 const nsfw = this.serverService.getConfig().instance.isNSFW 159 const nsfw = this.serverConfig.instance.isNSFW
159 const waitTranscoding = true 160 const waitTranscoding = true
160 const commentsEnabled = true 161 const commentsEnabled = true
161 const downloadEnabled = true 162 const downloadEnabled = true
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts
index 911bc884e..401d8a08f 100644
--- a/client/src/app/videos/+video-edit/video-add.component.ts
+++ b/client/src/app/videos/+video-edit/video-add.component.ts
@@ -1,28 +1,37 @@
1import { Component, HostListener, ViewChild } from '@angular/core' 1import { Component, HostListener, OnInit, ViewChild } from '@angular/core'
2import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' 2import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service'
3import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component' 3import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component'
4import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component' 4import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component'
5import { AuthService, ServerService } from '@app/core' 5import { AuthService, ServerService } from '@app/core'
6import { VideoImportTorrentComponent } from '@app/videos/+video-edit/video-add-components/video-import-torrent.component' 6import { VideoImportTorrentComponent } from '@app/videos/+video-edit/video-add-components/video-import-torrent.component'
7import { ServerConfig } from '@shared/models'
7 8
8@Component({ 9@Component({
9 selector: 'my-videos-add', 10 selector: 'my-videos-add',
10 templateUrl: './video-add.component.html', 11 templateUrl: './video-add.component.html',
11 styleUrls: [ './video-add.component.scss' ] 12 styleUrls: [ './video-add.component.scss' ]
12}) 13})
13export class VideoAddComponent implements CanComponentDeactivate { 14export class VideoAddComponent implements OnInit, CanComponentDeactivate {
14 @ViewChild('videoUpload', { static: false }) videoUpload: VideoUploadComponent 15 @ViewChild('videoUpload', { static: false }) videoUpload: VideoUploadComponent
15 @ViewChild('videoImportUrl', { static: false }) videoImportUrl: VideoImportUrlComponent 16 @ViewChild('videoImportUrl', { static: false }) videoImportUrl: VideoImportUrlComponent
16 @ViewChild('videoImportTorrent', { static: false }) videoImportTorrent: VideoImportTorrentComponent 17 @ViewChild('videoImportTorrent', { static: false }) videoImportTorrent: VideoImportTorrentComponent
17 18
18 secondStepType: 'upload' | 'import-url' | 'import-torrent' 19 secondStepType: 'upload' | 'import-url' | 'import-torrent'
19 videoName: string 20 videoName: string
21 serverConfig: ServerConfig
20 22
21 constructor ( 23 constructor (
22 private auth: AuthService, 24 private auth: AuthService,
23 private serverService: ServerService 25 private serverService: ServerService
24 ) {} 26 ) {}
25 27
28 ngOnInit () {
29 this.serverConfig = this.serverService.getTmpConfig()
30
31 this.serverService.getConfig()
32 .subscribe(config => this.serverConfig = config)
33 }
34
26 onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) { 35 onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) {
27 this.secondStepType = type 36 this.secondStepType = type
28 this.videoName = videoName 37 this.videoName = videoName
@@ -52,11 +61,11 @@ export class VideoAddComponent implements CanComponentDeactivate {
52 } 61 }
53 62
54 isVideoImportHttpEnabled () { 63 isVideoImportHttpEnabled () {
55 return this.serverService.getConfig().import.videos.http.enabled 64 return this.serverConfig.import.videos.http.enabled
56 } 65 }
57 66
58 isVideoImportTorrentEnabled () { 67 isVideoImportTorrentEnabled () {
59 return this.serverService.getConfig().import.videos.torrent.enabled 68 return this.serverConfig.import.videos.torrent.enabled
60 } 69 }
61 70
62 isInSecondStep () { 71 isInSecondStep () {
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 8cc1e8b58..626d0ca07 100644
--- a/client/src/app/videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/videos/+video-watch/video-watch.component.ts
@@ -8,7 +8,7 @@ import { MetaService } from '@ngx-meta/core'
8import { AuthUser, Notifier, ServerService } from '@app/core' 8import { AuthUser, Notifier, ServerService } from '@app/core'
9import { forkJoin, Observable, Subscription } from 'rxjs' 9import { forkJoin, Observable, Subscription } from 'rxjs'
10import { Hotkey, HotkeysService } from 'angular2-hotkeys' 10import { Hotkey, HotkeysService } from 'angular2-hotkeys'
11import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared' 11import { ServerConfig, UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared'
12import { AuthService, ConfirmService } from '../../core' 12import { AuthService, ConfirmService } from '../../core'
13import { RestExtractor, VideoBlacklistService } from '../../shared' 13import { RestExtractor, VideoBlacklistService } from '../../shared'
14import { VideoDetails } from '../../shared/video/video-details.model' 14import { VideoDetails } from '../../shared/video/video-details.model'
@@ -84,6 +84,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
84 private queryParamsSub: Subscription 84 private queryParamsSub: Subscription
85 private configSub: Subscription 85 private configSub: Subscription
86 86
87 private serverConfig: ServerConfig
88
87 constructor ( 89 constructor (
88 private elementRef: ElementRef, 90 private elementRef: ElementRef,
89 private changeDetector: ChangeDetectorRef, 91 private changeDetector: ChangeDetectorRef,
@@ -120,11 +122,15 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
120 } 122 }
121 123
122 async ngOnInit () { 124 async ngOnInit () {
123 this.configSub = this.serverService.configLoaded 125 this.serverConfig = this.serverService.getTmpConfig()
124 .subscribe(() => { 126
127 this.configSub = this.serverService.getConfig()
128 .subscribe(config => {
129 this.serverConfig = config
130
125 if ( 131 if (
126 isWebRTCDisabled() || 132 isWebRTCDisabled() ||
127 this.serverService.getConfig().tracker.enabled === false || 133 this.serverConfig.tracker.enabled === false ||
128 peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true' 134 peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
129 ) { 135 ) {
130 this.hasAlreadyAcceptedPrivacyConcern = true 136 this.hasAlreadyAcceptedPrivacyConcern = true
@@ -280,7 +286,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
280 } 286 }
281 287
282 isVideoBlur (video: Video) { 288 isVideoBlur (video: Video) {
283 return video.isVideoNSFWForUser(this.user, this.serverService.getConfig()) 289 return video.isVideoNSFWForUser(this.user, this.serverConfig)
284 } 290 }
285 291
286 isAutoPlayEnabled () { 292 isAutoPlayEnabled () {
diff --git a/client/src/app/videos/video-list/video-most-liked.component.ts b/client/src/app/videos/video-list/video-most-liked.component.ts
index aff8413eb..f94a7da04 100644
--- a/client/src/app/videos/video-list/video-most-liked.component.ts
+++ b/client/src/app/videos/video-list/video-most-liked.component.ts
@@ -40,11 +40,8 @@ export class VideoMostLikedComponent extends AbstractVideoList implements OnInit
40 40
41 this.generateSyndicationList() 41 this.generateSyndicationList()
42 42
43 this.serverService.configLoaded.subscribe( 43 this.titlePage = this.i18n('Most liked videos')
44 () => { 44 this.titleTooltip = this.i18n('Videos that have the higher number of likes.')
45 this.titlePage = this.i18n('Most liked videos')
46 this.titleTooltip = this.i18n('Videos that have the higher number of likes.')
47 })
48 } 45 }
49 46
50 getVideosObservable (page: number) { 47 getVideosObservable (page: number) {
diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts
index 19324da63..bc88679fa 100644
--- a/client/src/app/videos/video-list/video-trending.component.ts
+++ b/client/src/app/videos/video-list/video-trending.component.ts
@@ -40,9 +40,9 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
40 40
41 this.generateSyndicationList() 41 this.generateSyndicationList()
42 42
43 this.serverService.configLoaded.subscribe( 43 this.serverService.getConfig().subscribe(
44 () => { 44 config => {
45 const trendingDays = this.serverService.getConfig().trending.videos.intervalDays 45 const trendingDays = config.trending.videos.intervalDays
46 46
47 if (trendingDays === 1) { 47 if (trendingDays === 1) {
48 this.titlePage = this.i18n('Trending for the last 24 hours') 48 this.titlePage = this.i18n('Trending for the last 24 hours')