Merge branch 'release/2.1.0' into develop
[oweals/peertube.git] / client / src / app / +admin / config / edit-custom-config / edit-custom-config.component.ts
1 import { Component, OnInit } from '@angular/core'
2 import { ConfigService } from '@app/+admin/config/shared/config.service'
3 import { ServerService } from '@app/core/server/server.service'
4 import { CustomConfigValidatorsService, FormReactive, UserValidatorsService } from '@app/shared'
5 import { Notifier } from '@app/core'
6 import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
7 import { I18n } from '@ngx-translate/i18n-polyfill'
8 import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
9 import { SelectItem } from 'primeng/api'
10 import { forkJoin } from 'rxjs'
11 import { ServerConfig } from '@shared/models'
12
13 @Component({
14   selector: 'my-edit-custom-config',
15   templateUrl: './edit-custom-config.component.html',
16   styleUrls: [ './edit-custom-config.component.scss' ]
17 })
18 export class EditCustomConfigComponent extends FormReactive implements OnInit {
19   customConfig: CustomConfig
20
21   resolutions: { id: string, label: string, description?: string }[] = []
22   transcodingThreadOptions: { label: string, value: number }[] = []
23
24   languageItems: SelectItem[] = []
25   categoryItems: SelectItem[] = []
26
27   private serverConfig: ServerConfig
28
29   constructor (
30     protected formValidatorService: FormValidatorService,
31     private customConfigValidatorsService: CustomConfigValidatorsService,
32     private userValidatorsService: UserValidatorsService,
33     private notifier: Notifier,
34     private configService: ConfigService,
35     private serverService: ServerService,
36     private i18n: I18n
37   ) {
38     super()
39
40     this.resolutions = [
41       {
42         id: '0p',
43         label: this.i18n('Audio-only'),
44         description: this.i18n('A <code>.mp4</code> that keeps the original audio track, with no video')
45       },
46       {
47         id: '240p',
48         label: this.i18n('240p')
49       },
50       {
51         id: '360p',
52         label: this.i18n('360p')
53       },
54       {
55         id: '480p',
56         label: this.i18n('480p')
57       },
58       {
59         id: '720p',
60         label: this.i18n('720p')
61       },
62       {
63         id: '1080p',
64         label: this.i18n('1080p')
65       },
66       {
67         id: '2160p',
68         label: this.i18n('2160p')
69       }
70     ]
71
72     this.transcodingThreadOptions = [
73       { value: 0, label: this.i18n('Auto (via ffmpeg)') },
74       { value: 1, label: '1' },
75       { value: 2, label: '2' },
76       { value: 4, label: '4' },
77       { value: 8, label: '8' }
78     ]
79   }
80
81   get videoQuotaOptions () {
82     return this.configService.videoQuotaOptions
83   }
84
85   get videoQuotaDailyOptions () {
86     return this.configService.videoQuotaDailyOptions
87   }
88
89   get availableThemes () {
90     return this.serverConfig.theme.registered
91       .map(t => t.name)
92   }
93
94   getResolutionKey (resolution: string) {
95     return 'transcoding.resolutions.' + resolution
96   }
97
98   ngOnInit () {
99     this.serverConfig = this.serverService.getTmpConfig()
100     this.serverService.getConfig()
101         .subscribe(config => this.serverConfig = config)
102
103     const formGroupData: { [key in keyof CustomConfig ]: any } = {
104       instance: {
105         name: this.customConfigValidatorsService.INSTANCE_NAME,
106         shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
107         description: null,
108
109         isNSFW: false,
110         defaultNSFWPolicy: null,
111
112         terms: null,
113         codeOfConduct: null,
114
115         creationReason: null,
116         moderationInformation: null,
117         administrator: null,
118         maintenanceLifetime: null,
119         businessModel: null,
120
121         hardwareInformation: null,
122
123         categories: null,
124         languages: null,
125
126         defaultClientRoute: null,
127
128         customizations: {
129           javascript: null,
130           css: null
131         }
132       },
133       theme: {
134         default: null
135       },
136       services: {
137         twitter: {
138           username: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
139           whitelisted: null
140         }
141       },
142       cache: {
143         previews: {
144           size: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE
145         },
146         captions: {
147           size: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE
148         }
149       },
150       signup: {
151         enabled: null,
152         limit: this.customConfigValidatorsService.SIGNUP_LIMIT,
153         requiresEmailVerification: null
154       },
155       import: {
156         videos: {
157           http: {
158             enabled: null
159           },
160           torrent: {
161             enabled: null
162           }
163         }
164       },
165       admin: {
166         email: this.customConfigValidatorsService.ADMIN_EMAIL
167       },
168       contactForm: {
169         enabled: null
170       },
171       user: {
172         videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
173         videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY
174       },
175       transcoding: {
176         enabled: null,
177         threads: this.customConfigValidatorsService.TRANSCODING_THREADS,
178         allowAdditionalExtensions: null,
179         allowAudioFiles: null,
180         resolutions: {},
181         hls: {
182           enabled: null
183         },
184         webtorrent: {
185           enabled: null
186         }
187       },
188       autoBlacklist: {
189         videos: {
190           ofUsers: {
191             enabled: null
192           }
193         }
194       },
195       followers: {
196         instance: {
197           enabled: null,
198           manualApproval: null
199         }
200       },
201       followings: {
202         instance: {
203           autoFollowBack: {
204             enabled: null
205           },
206           autoFollowIndex: {
207             enabled: null,
208             indexUrl: this.customConfigValidatorsService.INDEX_URL
209           }
210         }
211       }
212     }
213
214     const defaultValues = {
215       transcoding: {
216         resolutions: {}
217       }
218     }
219     for (const resolution of this.resolutions) {
220       defaultValues.transcoding.resolutions[resolution.id] = 'false'
221       formGroupData.transcoding.resolutions[resolution.id] = null
222     }
223
224     this.buildForm(formGroupData)
225     this.loadForm()
226     this.checkTranscodingFields()
227   }
228
229   isTranscodingEnabled () {
230     return this.form.value['transcoding']['enabled'] === true
231   }
232
233   isSignupEnabled () {
234     return this.form.value['signup']['enabled'] === true
235   }
236
237   isAutoFollowIndexEnabled () {
238     return this.form.value['followings']['instance']['autoFollowIndex']['enabled'] === true
239   }
240
241   async formValidated () {
242     this.configService.updateCustomConfig(this.form.getRawValue())
243       .subscribe(
244         res => {
245           this.customConfig = res
246
247           // Reload general configuration
248           this.serverService.resetConfig()
249
250           this.updateForm()
251
252           this.notifier.success(this.i18n('Configuration updated.'))
253         },
254
255         err => this.notifier.error(err.message)
256       )
257   }
258
259   getSelectedLanguageLabel () {
260     return this.i18n('{{\'{0} languages selected')
261   }
262
263   getDefaultLanguageLabel () {
264     return this.i18n('No language')
265   }
266
267   getSelectedCategoryLabel () {
268     return this.i18n('{{\'{0} categories selected')
269   }
270
271   getDefaultCategoryLabel () {
272     return this.i18n('No category')
273   }
274
275   private updateForm () {
276     this.form.patchValue(this.customConfig)
277   }
278
279   private loadForm () {
280     forkJoin([
281       this.configService.getCustomConfig(),
282       this.serverService.getVideoLanguages(),
283       this.serverService.getVideoCategories()
284     ]).subscribe(
285       ([ config, languages, categories ]) => {
286         this.customConfig = config
287
288         this.languageItems = languages.map(l => ({ label: l.label, value: l.id }))
289         this.categoryItems = categories.map(l => ({ label: l.label, value: l.id }))
290
291         this.updateForm()
292         // Force form validation
293         this.forceCheck()
294       },
295
296       err => this.notifier.error(err.message)
297     )
298   }
299
300   private checkTranscodingFields () {
301     const hlsControl = this.form.get('transcoding.hls.enabled')
302     const webtorrentControl = this.form.get('transcoding.webtorrent.enabled')
303
304     webtorrentControl.valueChanges
305                      .subscribe(newValue => {
306                        if (newValue === false && !hlsControl.disabled) {
307                          hlsControl.disable()
308                        }
309
310                        if (newValue === true && !hlsControl.enabled) {
311                          hlsControl.enable()
312                        }
313                      })
314
315     hlsControl.valueChanges
316               .subscribe(newValue => {
317                 if (newValue === false && !webtorrentControl.disabled) {
318                   webtorrentControl.disable()
319                 }
320
321                 if (newValue === true && !webtorrentControl.enabled) {
322                   webtorrentControl.enable()
323                 }
324               })
325   }
326 }