-import { Component, OnInit, AfterViewChecked, ViewChild } from '@angular/core'
+
+import omit from 'lodash-es/omit'
+import { forkJoin } from 'rxjs'
+import { SelectOptionsItem } from 'src/types/select-options-item.model'
+import { Component, OnInit } from '@angular/core'
+import { ActivatedRoute, Router } from '@angular/router'
import { ConfigService } from '@app/+admin/config/shared/config.service'
-import { ServerService } from '@app/core/server/server.service'
-import { CustomConfigValidatorsService, FormReactive, UserValidatorsService } from '@app/shared'
import { Notifier } from '@app/core'
-import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
-import { I18n } from '@ngx-translate/i18n-polyfill'
-import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
-import { SelectItem } from 'primeng/api'
-import { forkJoin } from 'rxjs'
-import { ServerConfig } from '@shared/models'
-import { ViewportScroller } from '@angular/common'
-import { NgbTabset } from '@ng-bootstrap/ng-bootstrap'
+import { ServerService } from '@app/core/server/server.service'
+import {
+ ADMIN_EMAIL_VALIDATOR,
+ CACHE_CAPTIONS_SIZE_VALIDATOR,
+ CACHE_PREVIEWS_SIZE_VALIDATOR,
+ CONCURRENCY_VALIDATOR,
+ INDEX_URL_VALIDATOR,
+ INSTANCE_NAME_VALIDATOR,
+ INSTANCE_SHORT_DESCRIPTION_VALIDATOR,
+ MAX_INSTANCE_LIVES_VALIDATOR,
+ MAX_LIVE_DURATION_VALIDATOR,
+ MAX_USER_LIVES_VALIDATOR,
+ SEARCH_INDEX_URL_VALIDATOR,
+ SERVICES_TWITTER_USERNAME_VALIDATOR,
+ SIGNUP_LIMIT_VALIDATOR,
+ SIGNUP_MINIMUM_AGE_VALIDATOR,
+ TRANSCODING_THREADS_VALIDATOR
+} from '@app/shared/form-validators/custom-config-validators'
+import { USER_VIDEO_QUOTA_DAILY_VALIDATOR, USER_VIDEO_QUOTA_VALIDATOR } from '@app/shared/form-validators/user-validators'
+import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
+import { CustomPageService } from '@app/shared/shared-main/custom-page'
+import { CustomConfig, CustomPage, HTMLServerConfig } from '@shared/models'
+import { EditConfigurationService } from './edit-configuration.service'
+
+type ComponentCustomConfig = CustomConfig & {
+ instanceCustomHomepage: CustomPage
+}
@Component({
selector: 'my-edit-custom-config',
templateUrl: './edit-custom-config.component.html',
styleUrls: [ './edit-custom-config.component.scss' ]
})
-export class EditCustomConfigComponent extends FormReactive implements OnInit, AfterViewChecked {
- @ViewChild('tabs') private tabs: NgbTabset
-
- initDone = false
- customConfig: CustomConfig
+export class EditCustomConfigComponent extends FormReactive implements OnInit {
+ activeNav: string
- resolutions: { id: string, label: string, description?: string }[] = []
- transcodingThreadOptions: { label: string, value: number }[] = []
+ customConfig: ComponentCustomConfig
+ serverConfig: HTMLServerConfig
- languageItems: SelectItem[] = []
- categoryItems: SelectItem[] = []
+ homepage: CustomPage
- private serverConfig: ServerConfig
+ languageItems: SelectOptionsItem[] = []
+ categoryItems: SelectOptionsItem[] = []
constructor (
- private viewportScroller: ViewportScroller,
+ private router: Router,
+ private route: ActivatedRoute,
protected formValidatorService: FormValidatorService,
- private customConfigValidatorsService: CustomConfigValidatorsService,
- private userValidatorsService: UserValidatorsService,
private notifier: Notifier,
private configService: ConfigService,
+ private customPage: CustomPageService,
private serverService: ServerService,
- private i18n: I18n
+ private editConfigurationService: EditConfigurationService
) {
super()
-
- this.resolutions = [
- {
- id: '0p',
- label: this.i18n('Audio-only'),
- description: this.i18n('A <code>.mp4</code> that keeps the original audio track, with no video')
- },
- {
- id: '240p',
- label: this.i18n('240p')
- },
- {
- id: '360p',
- label: this.i18n('360p')
- },
- {
- id: '480p',
- label: this.i18n('480p')
- },
- {
- id: '720p',
- label: this.i18n('720p')
- },
- {
- id: '1080p',
- label: this.i18n('1080p')
- },
- {
- id: '2160p',
- label: this.i18n('2160p')
- }
- ]
-
- this.transcodingThreadOptions = [
- { value: 0, label: this.i18n('Auto (via ffmpeg)') },
- { value: 1, label: '1' },
- { value: 2, label: '2' },
- { value: 4, label: '4' },
- { value: 8, label: '8' }
- ]
- }
-
- get videoQuotaOptions () {
- return this.configService.videoQuotaOptions
- }
-
- get videoQuotaDailyOptions () {
- return this.configService.videoQuotaDailyOptions
- }
-
- get availableThemes () {
- return this.serverConfig.theme.registered
- .map(t => t.name)
- }
-
- getResolutionKey (resolution: string) {
- return 'transcoding.resolutions.' + resolution
}
ngOnInit () {
- this.serverConfig = this.serverService.getTmpConfig()
- this.serverService.getConfig()
- .subscribe(config => this.serverConfig = config)
+ this.serverConfig = this.serverService.getHTMLConfig()
- const formGroupData: { [key in keyof CustomConfig ]: any } = {
+ const formGroupData: { [key in keyof ComponentCustomConfig ]: any } = {
instance: {
- name: this.customConfigValidatorsService.INSTANCE_NAME,
- shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
+ name: INSTANCE_NAME_VALIDATOR,
+ shortDescription: INSTANCE_SHORT_DESCRIPTION_VALIDATOR,
description: null,
isNSFW: false,
},
services: {
twitter: {
- username: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
+ username: SERVICES_TWITTER_USERNAME_VALIDATOR,
whitelisted: null
}
},
cache: {
previews: {
- size: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE
+ size: CACHE_PREVIEWS_SIZE_VALIDATOR
},
captions: {
- size: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE
+ size: CACHE_CAPTIONS_SIZE_VALIDATOR
+ },
+ torrents: {
+ size: CACHE_CAPTIONS_SIZE_VALIDATOR
}
},
signup: {
enabled: null,
- limit: this.customConfigValidatorsService.SIGNUP_LIMIT,
- requiresEmailVerification: null
+ limit: SIGNUP_LIMIT_VALIDATOR,
+ requiresEmailVerification: null,
+ minimumAge: SIGNUP_MINIMUM_AGE_VALIDATOR
},
import: {
videos: {
+ concurrency: CONCURRENCY_VALIDATOR,
http: {
enabled: null
},
}
}
},
+ trending: {
+ videos: {
+ algorithms: {
+ enabled: null,
+ default: null
+ }
+ }
+ },
admin: {
- email: this.customConfigValidatorsService.ADMIN_EMAIL
+ email: ADMIN_EMAIL_VALIDATOR
},
contactForm: {
enabled: null
},
user: {
- videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
- videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY
+ videoQuota: USER_VIDEO_QUOTA_VALIDATOR,
+ videoQuotaDaily: USER_VIDEO_QUOTA_DAILY_VALIDATOR
},
transcoding: {
enabled: null,
- threads: this.customConfigValidatorsService.TRANSCODING_THREADS,
+ threads: TRANSCODING_THREADS_VALIDATOR,
allowAdditionalExtensions: null,
allowAudioFiles: null,
+ profile: null,
+ concurrency: CONCURRENCY_VALIDATOR,
resolutions: {},
hls: {
enabled: null
enabled: null
}
},
+ live: {
+ enabled: null,
+
+ maxDuration: MAX_LIVE_DURATION_VALIDATOR,
+ maxInstanceLives: MAX_INSTANCE_LIVES_VALIDATOR,
+ maxUserLives: MAX_USER_LIVES_VALIDATOR,
+ allowReplay: null,
+
+ transcoding: {
+ enabled: null,
+ threads: TRANSCODING_THREADS_VALIDATOR,
+ profile: null,
+ resolutions: {}
+ }
+ },
autoBlacklist: {
videos: {
ofUsers: {
},
autoFollowIndex: {
enabled: null,
- indexUrl: this.customConfigValidatorsService.INDEX_URL
+ indexUrl: INDEX_URL_VALIDATOR
}
}
+ },
+ broadcastMessage: {
+ enabled: null,
+ level: null,
+ dismissable: null,
+ message: null
+ },
+ search: {
+ remoteUri: {
+ users: null,
+ anonymous: null
+ },
+ searchIndex: {
+ enabled: null,
+ url: SEARCH_INDEX_URL_VALIDATOR,
+ disableLocalSearch: null,
+ isDefaultSearch: null
+ }
+ },
+
+ instanceCustomHomepage: {
+ content: null
}
}
const defaultValues = {
transcoding: {
resolutions: {}
+ },
+ live: {
+ transcoding: {
+ resolutions: {}
+ }
}
}
- for (const resolution of this.resolutions) {
+
+ for (const resolution of this.editConfigurationService.getVODResolutions()) {
defaultValues.transcoding.resolutions[resolution.id] = 'false'
formGroupData.transcoding.resolutions[resolution.id] = null
}
+ for (const resolution of this.editConfigurationService.getLiveResolutions()) {
+ defaultValues.live.transcoding.resolutions[resolution.id] = 'false'
+ formGroupData.live.transcoding.resolutions[resolution.id] = null
+ }
+
this.buildForm(formGroupData)
- this.loadForm()
- this.checkTranscodingFields()
- }
- ngAfterViewChecked () {
- if (!this.initDone) {
- this.initDone = true
- this.gotoAnchor()
+ if (this.route.snapshot.fragment) {
+ this.onNavChange(this.route.snapshot.fragment)
}
- }
- isTranscodingEnabled () {
- return this.form.value['transcoding']['enabled'] === true
+ this.loadConfigAndUpdateForm()
+ this.loadCategoriesAndLanguages()
+ if (!this.serverConfig.allowEdits) {
+ this.form.disable()
+ }
}
- isSignupEnabled () {
- return this.form.value['signup']['enabled'] === true
- }
+ formValidated () {
+ const value: ComponentCustomConfig = this.form.getRawValue()
- isAutoFollowIndexEnabled () {
- return this.form.value['followings']['instance']['autoFollowIndex']['enabled'] === true
- }
+ forkJoin([
+ this.configService.updateCustomConfig(omit(value, 'instanceCustomHomepage')),
+ this.customPage.updateInstanceHomepage(value.instanceCustomHomepage.content)
+ ])
+ .subscribe({
+ next: ([ resConfig ]) => {
+ const instanceCustomHomepage = {
+ content: value.instanceCustomHomepage.content
+ }
- async formValidated () {
- this.configService.updateCustomConfig(this.form.getRawValue())
- .subscribe(
- res => {
- this.customConfig = res
+ this.customConfig = { ...resConfig, instanceCustomHomepage }
// Reload general configuration
this.serverService.resetConfig()
+ .subscribe(config => {
+ this.serverConfig = config
+ })
this.updateForm()
- this.notifier.success(this.i18n('Configuration updated.'))
+ this.notifier.success($localize`Configuration updated.`)
},
- err => this.notifier.error(err.message)
- )
+ error: err => this.notifier.error(err.message)
+ })
}
- getSelectedLanguageLabel () {
- return this.i18n('{{\'{0} languages selected')
- }
+ hasConsistentOptions () {
+ if (this.hasLiveAllowReplayConsistentOptions()) return true
- getDefaultLanguageLabel () {
- return this.i18n('No language')
+ return false
}
- getSelectedCategoryLabel () {
- return this.i18n('{{\'{0} categories selected')
+ hasLiveAllowReplayConsistentOptions () {
+ if (
+ this.editConfigurationService.isTranscodingEnabled(this.form) === false &&
+ this.editConfigurationService.isLiveEnabled(this.form) &&
+ this.form.value['live']['allowReplay'] === true
+ ) {
+ return false
+ }
+
+ return true
}
- getDefaultCategoryLabel () {
- return this.i18n('No category')
+ onNavChange (newActiveNav: string) {
+ this.activeNav = newActiveNav
+
+ this.router.navigate([], { fragment: this.activeNav })
}
- gotoAnchor () {
- const hashToTab = {
- 'customizations': 'advanced-configuration'
- }
- const hash = window.location.hash.replace('#', '')
+ grabAllErrors (errorObjectArg?: any) {
+ const errorObject = errorObjectArg || this.formErrors
+
+ let acc: string[] = []
- if (hash && Object.keys(hashToTab).includes(hash)) {
- this.tabs.select(hashToTab[hash])
- setTimeout(() => this.viewportScroller.scrollToAnchor(hash), 100)
+ for (const key of Object.keys(errorObject)) {
+ const value = errorObject[key]
+ if (!value) continue
+
+ if (typeof value === 'string') {
+ acc.push(value)
+ } else {
+ acc = acc.concat(this.grabAllErrors(value))
+ }
}
+
+ return acc
}
private updateForm () {
this.form.patchValue(this.customConfig)
}
- private loadForm () {
+ private loadConfigAndUpdateForm () {
forkJoin([
this.configService.getCustomConfig(),
- this.serverService.getVideoLanguages(),
- this.serverService.getVideoCategories()
- ]).subscribe(
- ([ config, languages, categories ]) => {
- this.customConfig = config
-
- this.languageItems = languages.map(l => ({ label: l.label, value: l.id }))
- this.categoryItems = categories.map(l => ({ label: l.label, value: l.id }))
+ this.customPage.getInstanceHomepage()
+ ]).subscribe({
+ next: ([ config, homepage ]) => {
+ this.customConfig = { ...config, instanceCustomHomepage: homepage }
this.updateForm()
// Force form validation
this.forceCheck()
},
- err => this.notifier.error(err.message)
- )
+ error: err => this.notifier.error(err.message)
+ })
}
- private checkTranscodingFields () {
- const hlsControl = this.form.get('transcoding.hls.enabled')
- const webtorrentControl = this.form.get('transcoding.webtorrent.enabled')
-
- webtorrentControl.valueChanges
- .subscribe(newValue => {
- if (newValue === false && !hlsControl.disabled) {
- hlsControl.disable()
- }
-
- if (newValue === true && !hlsControl.enabled) {
- hlsControl.enable()
- }
- })
-
- hlsControl.valueChanges
- .subscribe(newValue => {
- if (newValue === false && !webtorrentControl.disabled) {
- webtorrentControl.disable()
- }
-
- if (newValue === true && !webtorrentControl.enabled) {
- webtorrentControl.enable()
- }
- })
+ private loadCategoriesAndLanguages () {
+ forkJoin([
+ this.serverService.getVideoLanguages(),
+ this.serverService.getVideoCategories()
+ ]).subscribe({
+ next: ([ languages, categories ]) => {
+ this.languageItems = languages.map(l => ({ label: l.label, id: l.id }))
+ this.categoryItems = categories.map(l => ({ label: l.label, id: l.id + '' }))
+ },
+
+ error: err => this.notifier.error(err.message)
+ })
}
}