Improving select displays, focus box-shadows for paginators, instructions for index url
[oweals/peertube.git] / client / src / app / +admin / config / edit-custom-config / edit-custom-config.component.html
1 <form role="form" [formGroup]="form">
2
3   <div ngbNav #nav="ngbNav" class="nav-tabs">
4
5     <ng-container ngbNavItem="instance-information">
6       <a ngbNavLink i18n>Instance information</a>
7
8       <ng-template ngbNavContent>
9
10         <ng-container formGroupName="instance">
11
12           <div class="form-row mt-5"> <!-- instance grid -->
13             <div class="form-group col-12 col-lg-4 col-xl-3">
14               <div i18n class="inner-form-title">INSTANCE</div>
15             </div>
16
17             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
18
19               <div class="form-group">
20                 <label i18n for="instanceName">Name</label>
21                 <input
22                   type="text" id="instanceName" class="form-control"
23                   formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }"
24                 >
25                 <div *ngIf="formErrors.instance.name" class="form-error">{{ formErrors.instance.name }}</div>
26               </div>
27
28               <div class="form-group">
29                 <label i18n for="instanceShortDescription">Short description</label>
30                 <textarea
31                   id="instanceShortDescription" formControlName="shortDescription" class="form-control small"
32                   [ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }"
33                 ></textarea>
34                 <div *ngIf="formErrors.instance.shortDescription" class="form-error">{{ formErrors.instance.shortDescription }}</div>
35               </div>
36
37               <div class="form-group">
38                 <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
39                 <my-markdown-textarea
40                   name="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true"
41                   [classes]="{ 'input-error': formErrors['instance.description'] }"
42                 ></my-markdown-textarea>
43                 <div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
44               </div>
45
46               <div class="form-group">
47                 <label i18n for="instanceCategories">Main instance categories</label>
48
49                 <div>
50                   <p-multiSelect
51                     inputId="instanceCategories" [options]="categoryItems" formControlName="categories" [showToggleAll]="false"
52                     [defaultLabel]="getDefaultCategoryLabel()" [selectedItemsLabel]="getSelectedCategoryLabel()"
53                     emptyFilterMessage="No results found" i18n-emptyFilterMessage
54                   ></p-multiSelect>
55                 </div>
56               </div>
57
58               <div class="form-group">
59                 <label i18n for="instanceLanguages">Main languages you/your moderators speak</label>
60
61                 <div>
62                   <p-multiSelect
63                     inputId="instanceLanguages" [options]="languageItems" formControlName="languages" [showToggleAll]="false"
64                     [defaultLabel]="getDefaultLanguageLabel()" [selectedItemsLabel]="getSelectedLanguageLabel()"
65                     emptyFilterMessage="No results found" i18n-emptyFilterMessage
66                   ></p-multiSelect>
67                 </div>
68               </div>
69
70             </div>
71           </div>
72
73           <div class="form-row mt-4"> <!-- moderation & nsfw grid -->
74             <div class="form-group col-12 col-lg-4 col-xl-3">
75               <div i18n class="inner-form-title">MODERATION & NSFW</div>
76               <div i18n class="inner-for-description">
77                 Manage <a routerLink="/admin/users">users</a> to build a moderation team.
78               </div>
79             </div>
80
81             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
82
83               <div class="form-group">
84                 <my-peertube-checkbox inputName="instanceIsNSFW" formControlName="isNSFW">
85                   <ng-template ptTemplate="label">
86                     <ng-container i18n>This instance is dedicated to sensitive or NSFW content</ng-container>
87                   </ng-template>
88
89                   <ng-template ptTemplate="help">
90                     <ng-container i18n>
91                       Enabling it will allow other administrators to know that you are mainly federating sensitive content.<br /><br />
92                       Moreover, the NSFW checkbox on video upload will be automatically checked by default.
93                     </ng-container>
94                   </ng-template>
95                 </my-peertube-checkbox>
96               </div>
97
98               <div class="form-group">
99                 <label i18n for="instanceDefaultNSFWPolicy">Policy on videos containing sensitive content</label>
100
101                 <my-help>
102                   <ng-template ptTemplate="customHtml">
103                     <ng-container i18n>
104                       With <strong>Do not list</strong> or <strong>Blur thumbnails</strong>, a confirmation will be requested to watch the video.
105                     </ng-container>
106                   </ng-template>
107                 </my-help>
108
109                 <div class="peertube-select-container">
110                   <select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy" class="form-control">
111                     <option i18n value="undefined" disabled>Policy for sensitive videos</option>
112                     <option i18n value="do_not_list">Do not list</option>
113                     <option i18n value="blur">Blur thumbnails</option>
114                     <option i18n value="display">Display</option>
115                   </select>
116                 </div>
117                 <div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div>
118               </div>
119
120               <div class="form-group">
121                 <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
122                 <my-markdown-textarea
123                   name="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true"
124                   [ngClass]="{ 'input-error': formErrors['instance.terms'] }"
125                 ></my-markdown-textarea>
126                 <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div>
127               </div>
128
129               <div class="form-group">
130                 <label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help>
131                 <my-markdown-textarea
132                   name="instanceCodeOfConduct" formControlName="codeOfConduct" textareaWidth="500px" [previewColumn]="true"
133                   [ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }"
134                 ></my-markdown-textarea>
135                 <div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div>
136               </div>
137
138               <div class="form-group">
139                 <label i18n for="instanceModerationInformation">Moderation information</label><my-help helpType="markdownText"></my-help>
140                 <div i18n class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div>
141
142                 <my-markdown-textarea
143                   name="instanceModerationInformation" formControlName="moderationInformation" textareaWidth="500px" [previewColumn]="true"
144                   [ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }"
145                 ></my-markdown-textarea>
146                 <div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div>
147               </div>
148
149             </div>
150           </div>
151
152           <div class="form-row mt-4"> <!-- you and your instance grid -->
153             <div class="form-group col-12 col-lg-4 col-xl-3">
154               <div i18n class="inner-form-title">YOU AND YOUR INSTANCE</div>
155             </div>
156
157             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
158
159               <div class="form-group">
160                 <label i18n for="instanceAdministrator">Who is behind the instance?</label>
161                 <div i18n class="label-small-info">A single person? A non-profit? A company?</div>
162
163                 <my-markdown-textarea
164                   name="instanceAdministrator" formControlName="administrator" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true"
165                   [classes]="{ 'input-error': formErrors['instance.administrator'] }"
166                 ></my-markdown-textarea>
167
168                 <div *ngIf="formErrors.instance.administrator" class="form-error">{{ formErrors.instance.administrator }}</div>
169               </div>
170
171               <div class="form-group">
172                 <label i18n for="instanceCreationReason">Why did you create this instance?</label>
173                 <div i18n class="label-small-info">To share your personal videos? To open registrations and allow people to upload what they want?</div>
174
175                 <textarea
176                   id="instanceCreationReason" formControlName="creationReason" class="small" class="form-control"
177                   [ngClass]="{ 'input-error': formErrors['instance.creationReason'] }"
178                 ></textarea>
179                 <div *ngIf="formErrors.instance.creationReason" class="form-error">{{ formErrors.instance.creationReason }}</div>
180               </div>
181
182               <div class="form-group">
183                 <label i18n for="instanceMaintenanceLifetime">How long do you plan to maintain this instance?</label>
184                 <div i18n class="label-small-info">It's important to know for users who want to register on your instance</div>
185
186                 <textarea
187                   id="instanceMaintenanceLifetime" formControlName="maintenanceLifetime" class="form-control small"
188                   [ngClass]="{ 'input-error': formErrors['instance.maintenanceLifetime'] }"
189                 ></textarea>
190                 <div *ngIf="formErrors.instance.maintenanceLifetime" class="form-error">{{ formErrors.instance.maintenanceLifetime }}</div>
191               </div>
192
193               <div class="form-group">
194                 <label i18n for="instanceBusinessModel">How will you finance the PeerTube server?</label>
195                 <div i18n class="label-small-info">With your own funds? With user donations? Advertising?</div>
196
197                 <textarea
198                   id="instanceBusinessModel" formControlName="businessModel" class="form-control small"
199                   [ngClass]="{ 'input-error': formErrors['instance.businessModel'] }"
200                 ></textarea>
201                 <div *ngIf="formErrors.instance.businessModel" class="form-error">{{ formErrors.instance.businessModel }}</div>
202               </div>
203
204             </div>
205           </div>
206
207           <div class="form-row mt-4"> <!-- other information grid -->
208             <div class="form-group col-12 col-lg-4 col-xl-3">
209               <div i18n class="inner-form-title">OTHER INFORMATION</div>
210             </div>
211
212             <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
213
214               <div class="form-group">
215                 <label i18n for="instanceHardwareInformation">What server/hardware does the instance run on?</label>
216                 <div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div>
217
218                 <my-markdown-textarea
219                   name="instanceHardwareInformation" formControlName="hardwareInformation" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true"
220                   [classes]="{ 'input-error': formErrors['instance.hardwareInformation'] }"
221                 ></my-markdown-textarea>
222
223                 <div *ngIf="formErrors.instance.hardwareInformation" class="form-error">{{ formErrors.instance.hardwareInformation }}</div>
224               </div>
225
226             </div>
227           </div>
228
229         </ng-container>
230       </ng-template>
231     </ng-container>
232
233     <ng-container ngbNavItem="basic-configuration">
234       <a ngbNavLink i18n>Basic configuration</a>
235
236       <ng-template ngbNavContent>
237
238         <div class="form-row mt-5"> <!-- appearance grid -->
239           <div class="form-group col-12 col-lg-4 col-xl-3">
240             <div i18n class="inner-form-title">APPEARANCE</div>
241             <div i18n class="inner-for-description">
242               Use <a routerLink="/admin/plugins">plugins & themes</a> for more involved changes, or <a routerLink="/admin/config/edit-custom" fragment="customizations" (click)="gotoAnchor()">add slight customizations</a>.
243             </div>
244           </div>
245
246           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
247
248             <ng-container formGroupName="theme">
249               <div class="form-group">
250                 <label i18n for="themeDefault">Theme</label>
251
252                 <div class="peertube-select-container">
253                   <select formControlName="default" id="themeDefault" class="form-control">
254                     <option i18n value="default">default</option>
255
256                     <option *ngFor="let theme of availableThemes" [value]="theme">{{ theme }}</option>
257                   </select>
258                 </div>
259               </div>
260             </ng-container>
261
262             <div class="form-group" formGroupName="instance">
263               <label i18n for="instanceDefaultClientRoute">Landing page</label>
264               <div class="peertube-select-container">
265                 <select id="instanceDefaultClientRoute" formControlName="defaultClientRoute" class="form-control">
266                   <option i18n value="/videos/overview">Discover videos</option>
267                   <option i18n value="/videos/trending">Trending videos</option>
268                   <option i18n value="/videos/most-liked">Most liked videos</option>
269                   <option i18n value="/videos/recently-added">Recently added videos</option>
270                   <option i18n value="/videos/local">Local videos</option>
271                 </select>
272               </div>
273               <div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div>
274             </div>
275
276           </div>
277         </div>
278
279         <div class="form-row mt-4"> <!-- new users grid -->
280           <div class="form-group col-12 col-lg-4 col-xl-3">
281             <div i18n class="inner-form-title">NEW USERS</div>
282             <div i18n class="inner-for-description">
283               Manage <a routerLink="/admin/users">users</a> to set their quota individually.
284             </div>
285           </div>
286
287           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
288
289             <ng-container formGroupName="signup">
290               <div class="form-group">
291                 <my-peertube-checkbox
292                   inputName="signupEnabled" formControlName="enabled"
293                   i18n-labelText labelText="Signup enabled"
294                 >
295                   <ng-container ngProjectAs="description">
296                     <span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span>
297                   </ng-container>
298                   <ng-container ngProjectAs="extra">
299                     <my-peertube-checkbox [ngClass]="{ 'disabled-checkbox-extra': !isSignupEnabled() }"
300                       inputName="signupRequiresEmailVerification" formControlName="requiresEmailVerification"
301                       i18n-labelText labelText="Signup requires email verification"
302                     ></my-peertube-checkbox>
303
304                     <div [ngClass]="{ 'disabled-checkbox-extra': !isSignupEnabled() }" class="mt-3">
305                       <label i18n for="signupLimit">Signup limit</label>
306                       <input
307                         type="number" min="-1" id="signupLimit" class="form-control"
308                         formControlName="limit" [ngClass]="{ 'input-error': formErrors['signup.limit'] }"
309                       >
310                       <div *ngIf="formErrors.signup.limit" class="form-error">{{ formErrors.signup.limit }}</div>
311                       <small *ngIf="form.value['signup']['limit'] === -1" class="text-muted">Signup won't be limited to a fixed number of users.</small>
312                     </div>
313                   </ng-container>
314                 </my-peertube-checkbox>
315               </div>
316             </ng-container>
317
318             <ng-container formGroupName="user">
319               <div class="form-group">
320                 <label i18n for="userVideoQuota">Default video quota per user</label>
321                 <div class="peertube-select-container">
322                   <select id="userVideoQuota" formControlName="videoQuota" class="form-control">
323                     <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value" [disabled]="videoQuotaOption.disabled">
324                       {{ videoQuotaOption.label }}
325                     </option>
326                   </select>
327                 </div>
328                 <div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
329               </div>
330
331               <div class="form-group">
332                 <label i18n for="userVideoQuotaDaily">Default daily upload limit per user</label>
333                 <div class="peertube-select-container">
334                   <select id="userVideoQuotaDaily" formControlName="videoQuotaDaily" class="form-control">
335                     <option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value" [disabled]="videoQuotaDailyOption.disabled">
336                       {{ videoQuotaDailyOption.label }}
337                     </option>
338                   </select>
339                 </div>
340                 <div *ngIf="formErrors.user.videoQuotaDaily" class="form-error">{{ formErrors.user.videoQuotaDaily }}</div>
341               </div>
342             </ng-container>
343
344           </div>
345         </div>
346
347         <div class="form-row mt-4"> <!-- new videos grid -->
348           <div class="form-group col-12 col-lg-4 col-xl-3">
349             <div i18n class="inner-form-title">NEW VIDEOS</div>
350           </div>
351
352           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
353
354             <ng-container formGroupName="import">
355               <ng-container formGroupName="videos">
356
357                 <div class="form-group" formGroupName="http">
358                   <my-peertube-checkbox
359                     inputName="importVideosHttpEnabled" formControlName="enabled"
360                     i18n-labelText labelText="Allow import with HTTP URL (i.e. YouTube)"
361                   ></my-peertube-checkbox>
362                 </div>
363
364                 <div class="form-group" formGroupName="torrent">
365                   <my-peertube-checkbox
366                     inputName="importVideosTorrentEnabled" formControlName="enabled"
367                     i18n-labelText labelText="Allow import with a torrent file or a magnet URI"
368                   ></my-peertube-checkbox>
369                 </div>
370
371               </ng-container>
372             </ng-container>
373
374             <ng-container formGroupName="autoBlacklist">
375               <ng-container formGroupName="videos">
376                 <ng-container formGroupName="ofUsers">
377
378                   <div class="form-group">
379                     <my-peertube-checkbox
380                       inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
381                       i18n-labelText labelText="Blacklist new videos automatically"
382                     >
383                     <ng-container ngProjectAs="description">
384                       <span i18n>Unless a user is marked as trusted, their videos will stay private until a moderator reviews them.</span>
385                     </ng-container>
386                   </my-peertube-checkbox>
387                   </div>
388
389                 </ng-container>
390               </ng-container>
391             </ng-container>
392
393           </div>
394         </div>
395
396         <div class="form-row mt-4"> <!-- federation grid -->
397           <div class="form-group col-12 col-lg-4 col-xl-3">
398             <div i18n class="inner-form-title">FEDERATION</div>
399             <div i18n class="inner-form-description">
400               Manage <a routerLink="/admin/follows">relations</a> with other instances.
401             </div>
402           </div>
403
404           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
405
406             <ng-container formGroupName="followers">
407               <ng-container formGroupName="instance">
408
409                 <div class="form-group">
410                   <my-peertube-checkbox
411                     inputName="followersInstanceEnabled" formControlName="enabled"
412                     i18n-labelText labelText="Other instances can follow yours"
413                   ></my-peertube-checkbox>
414                 </div>
415
416                 <div class="form-group">
417                   <my-peertube-checkbox
418                     inputName="followersInstanceManualApproval" formControlName="manualApproval"
419                     i18n-labelText labelText="Manually approve new instance followers"
420                   ></my-peertube-checkbox>
421                 </div>
422               </ng-container>
423             </ng-container>
424
425             <ng-container formGroupName="followings">
426               <ng-container formGroupName="instance">
427
428                 <ng-container formGroupName="autoFollowBack">
429                   <div class="form-group">
430                     <my-peertube-checkbox
431                       inputName="followingsInstanceAutoFollowBackEnabled" formControlName="enabled"
432                       i18n-labelText labelText="Automatically follow back instances"
433                     >
434                       <ng-container ngProjectAs="description">
435                         <span i18n>⚠️ This functionality requires a lot of attention and extra moderation.</span>
436                       </ng-container>
437                     </my-peertube-checkbox>
438                   </div>
439                 </ng-container>
440
441                 <ng-container formGroupName="autoFollowIndex">
442                   <div class="form-group">
443                     <my-peertube-checkbox
444                       inputName="followingsInstanceAutoFollowIndexEnabled" formControlName="enabled"
445                       i18n-labelText labelText="Automatically follow instances of a public index"
446                     >
447                       <ng-container ngProjectAs="description">
448                         <p i18n>⚠️ This functionality requires a lot of attention and extra moderation.</p>
449
450                         <span i18n>
451                           You should only follow indexes you trust, or <a href="https://framagit.org/framasoft/peertube/instances-peertube#peertube-auto-follow">host your own</a>.
452                         </span>
453                       </ng-container>
454
455                       <ng-container ngProjectAs="extra">
456                         <div [ngClass]="{ 'disabled-checkbox-extra': !isAutoFollowIndexEnabled() }">
457                           <label i18n for="followingsInstanceAutoFollowIndexUrl">Index URL</label>
458                           <input
459                             type="text" id="followingsInstanceAutoFollowIndexUrl" class="form-control"
460                             formControlName="indexUrl" [ngClass]="{ 'input-error': formErrors['followings.instance.autoFollowIndex.indexUrl'] }"
461                           >
462                           <div *ngIf="formErrors.followings.instance.autoFollowIndex.indexUrl" class="form-error">{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}</div>
463                         </div>
464                       </ng-container>
465                     </my-peertube-checkbox>
466                   </div>
467
468                 </ng-container>
469               </ng-container>
470             </ng-container>
471
472           </div>
473         </div>
474
475         <div class="form-row mt-4"> <!-- administrators grid -->
476           <div class="form-group col-12 col-lg-4 col-xl-3">
477             <div i18n class="inner-form-title">ADMINISTRATORS</div>
478           </div>
479
480           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
481
482             <div class="form-group" formGroupName="admin">
483               <label i18n for="adminEmail">Admin email</label>
484               <input
485                 type="text" id="adminEmail" class="form-control"
486                 formControlName="email" [ngClass]="{ 'input-error': formErrors['admin.email'] }"
487               >
488               <div *ngIf="formErrors.admin.email" class="form-error">{{ formErrors.admin.email }}</div>
489             </div>
490
491             <div class="form-group" formGroupName="contactForm">
492               <my-peertube-checkbox
493                 inputName="enableContactForm" formControlName="enabled"
494                 i18n-labelText labelText="Enable contact form"
495               ></my-peertube-checkbox>
496             </div>
497
498           </div>
499         </div>
500
501       </ng-template>
502     </ng-container>
503
504     <ng-container ngbNavItem="services">
505       <a ngbNavLink i18n>Services</a>
506
507       <ng-template ngbNavContent>
508
509         <div class="form-row mt-5"> <!-- twitter grid -->
510           <div class="form-group col-12 col-lg-4 col-xl-3">
511             <div i18n class="inner-form-title">TWITTER</div>
512             <div i18n class="inner-form-description">
513               Optional. If any, provide the Twitter account representing your instance to improve link previews.
514             </div>
515           </div>
516
517           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
518
519             <ng-container formGroupName="services">
520               <ng-container formGroupName="twitter">
521
522                 <div class="form-group">
523                   <label i18n for="signupLimit">Your Twitter username</label>
524
525                   <input
526                     type="text" id="servicesTwitterUsername" class="form-control"
527                     formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
528                   >
529                   <div *ngIf="formErrors.services.twitter.username" class="form-error">{{ formErrors.services.twitter.username }}</div>
530                 </div>
531
532                 <div class="form-group">
533                   <my-peertube-checkbox inputName="servicesTwitterWhitelisted" formControlName="whitelisted">
534                     <ng-template ptTemplate="label">
535                       <ng-container i18n>Instance whitelisted by Twitter</ng-container>
536                     </ng-template>
537
538                     <ng-template ptTemplate="help">
539                       <ng-container i18n>
540                         If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
541                         If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
542                         Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on
543                         <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a>
544                         to see if you instance is whitelisted.
545                       </ng-container>
546                     </ng-template>
547                   </my-peertube-checkbox>
548                 </div>
549
550               </ng-container>
551             </ng-container>
552
553           </div>
554         </div>
555       </ng-template>
556     </ng-container>
557
558     <ng-container ngbNavItem="advanced-configuration">
559       <a ngbNavLink i18n>Advanced configuration</a>
560
561       <ng-template ngbNavContent>
562
563         <div class="form-row mt-5"> <!-- transcoding grid -->
564           <div class="form-group col-12 col-lg-4 col-xl-3">
565             <div i18n class="inner-form-title">TRANSCODING</div>
566             <div i18n class="inner-form-description">
567               Process uploaded videos so that they are in a streamable form that any device can play. Though costly in
568               resources, this is a critical part of PeerTube, so tread carefully.
569             </div>
570           </div>
571
572           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
573
574             <ng-container formGroupName="transcoding">
575
576               <div class="form-group">
577                 <my-peertube-checkbox inputName="transcodingEnabled" formControlName="enabled">
578                   <ng-template ptTemplate="label">
579                     <ng-container i18n>Transcoding enabled</ng-container>
580                   </ng-template>
581
582                   <ng-template ptTemplate="help">
583                     <ng-container i18n>If you disable transcoding, many videos from your users will not work!</ng-container>
584                   </ng-template>
585
586                   <ng-container ngProjectAs="extra">
587
588                     <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
589                       <my-peertube-checkbox
590                         inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
591                         i18n-labelText labelText="Allow additional extensions"
592                       >
593                         <ng-container ngProjectAs="description">
594                           <span i18n>Allows users to upload .mkv, .mov, .avi and .flv videos.</span>
595                         </ng-container>
596                       </my-peertube-checkbox>
597                     </div>
598
599                     <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
600                       <my-peertube-checkbox
601                         inputName="transcodingAllowAudioFiles" formControlName="allowAudioFiles"
602                         i18n-labelText labelText="Allow audio files upload"
603                       >
604                         <ng-container ngProjectAs="description">
605                           <span i18n>Allows users to upload audio files that will be merged with the preview file on upload.</span>
606                         </ng-container>
607                       </my-peertube-checkbox>
608                     </div>
609
610                     <ng-container formGroupName="webtorrent">
611                       <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
612                         <my-peertube-checkbox
613                           inputName="transcodingWebTorrentEnabled" formControlName="enabled"
614                           i18n-labelText labelText="WebTorrent support enabled"
615                         >
616                           <ng-template ptTemplate="help">
617                             <ng-container i18n>
618                               <strong>Experimental, we suggest you to not disable webtorrent support for now</strong>
619
620                               <p>If you also enabled HLS support, it will multiply videos storage by 2</p>
621
622                               <br />
623
624                               <strong>If disabled, breaks federation with PeerTube instances < 2.1</strong>
625                             </ng-container>
626                           </ng-template>
627                         </my-peertube-checkbox>
628                       </div>
629                     </ng-container>
630
631                     <ng-container formGroupName="hls">
632                       <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
633                         <my-peertube-checkbox
634                           inputName="transcodingHlsEnabled" formControlName="enabled"
635                           i18n-labelText labelText="HLS support enabled"
636                         >
637                           <ng-template ptTemplate="help">
638                             <ng-container i18n>
639                               <strong>Requires ffmpeg >= 4.1</strong>
640
641                               <p>Generate HLS playlists and fragmented MP4 files resulting in a better playback than with the current default player:</p>
642                               <ul>
643                                 <li>Resolution change is smoother</li>
644                                 <li>Faster playback in particular with long videos</li>
645                                 <li>More stable playback (less bugs/infinite loading)</li>
646                               </ul>
647
648                               <p>If you also enabled WebTorrent support, it will multiply videos storage by 2</p>
649                             </ng-container>
650                           </ng-template>
651                         </my-peertube-checkbox>
652                       </div>
653                     </ng-container>
654
655                   </ng-container>
656                 </my-peertube-checkbox>
657               </div>
658
659               <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
660                 <label i18n for="transcodingThreads">Transcoding threads</label>
661                 <div class="peertube-select-container">
662                   <select id="transcodingThreads" formControlName="threads" class="form-control">
663                     <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
664                       {{ transcodingThreadOption.label }}
665                     </option>
666                   </select>
667                 </div>
668                 <div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
669               </div>
670
671               <div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isTranscodingEnabled() }">
672
673                 <label i18n for="transcodingThreads">Resolutions to generate</label>
674
675                 <div class="ml-2 mt-2 d-flex flex-column">
676                   <ng-container formGroupName="resolutions">
677                     <div class="form-group" *ngFor="let resolution of resolutions">
678                       <my-peertube-checkbox
679                         [inputName]="getResolutionKey(resolution.id)" [formControlName]="resolution.id"
680                         labelText="{{resolution.label}}"
681                       >
682                         <ng-template *ngIf="resolution.description" ptTemplate="help">
683                           <div [innerHTML]="resolution.description"></div>
684                         </ng-template>
685                       </my-peertube-checkbox>
686                     </div>
687                   </ng-container>
688                 </div>
689
690               </div>
691
692             </ng-container>
693
694           </div>
695         </div>
696
697         <div class="form-row mt-4"> <!-- cache grid -->
698           <div class="form-group col-12 col-lg-4 col-xl-3">
699             <div i18n class="inner-form-title">CACHE</div>
700             <div i18n class="inner-form-description">
701               Some files are not federated, and fetched when necessary. Define their caching policies.
702             </div>
703           </div>
704
705           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
706
707             <ng-container formGroupName="cache">
708               <div class="form-group" formGroupName="previews">
709                 <label i18n for="cachePreviewsSize">Number of previews to keep in cache</label>
710                 <input
711                   type="number" min="0" id="cachePreviewsSize" class="form-control"
712                   formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }"
713                 >
714                 <div *ngIf="formErrors.cache.previews.size" class="form-error">{{ formErrors.cache.previews.size }}</div>
715               </div>
716
717               <div class="form-group" formGroupName="captions">
718                 <label i18n for="cacheCaptionsSize">Number of video captions to keep in cache</label>
719                 <input
720                   type="number" min="0" id="cacheCaptionsSize" class="form-control"
721                   formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }"
722                 >
723                 <div *ngIf="formErrors.cache.captions.size" class="form-error">{{ formErrors.cache.captions.size }}</div>
724               </div>
725             </ng-container>
726
727           </div>
728         </div>
729
730         <div class="form-row mt-4"> <!-- cache grid -->
731           <div class="form-group col-12 col-lg-4 col-xl-3">
732             <div class="anchor" id="customizations"></div> <!-- customizations anchor -->
733             <div i18n class="inner-form-title">CUSTOMIZATIONS</div>
734             <div i18n class="inner-form-description">
735               Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill.
736             </div>
737           </div>
738
739           <div class="form-group form-group-right col-12 col-lg-8 col-xl-9">
740
741             <ng-container formGroupName="instance">
742               <ng-container formGroupName="customizations">
743                 <div class="form-group">
744                   <label i18n for="customizationJavascript">JavaScript</label>
745                   <my-help>
746                     <ng-template ptTemplate="customHtml">
747                       <ng-container i18n>
748                         Write JavaScript code directly.<br />Example: <pre>console.log('my instance is amazing');</pre>
749                       </ng-container>
750                     </ng-template>
751                   </my-help>
752
753                   <textarea
754                     id="customizationJavascript" formControlName="javascript" class="form-control"
755                     [ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
756                   ></textarea>
757
758                   <div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
759                 </div>
760
761                 <div class="form-group">
762                   <label for="customizationCSS">CSS</label>
763
764                   <my-help>
765                     <ng-template ptTemplate="customHtml">
766                       <ng-container i18n>
767                         Write CSS code directly. Example:<br /><br />
768     <pre>
769     #custom-css {{ '{' }}
770       color: red;
771     {{ '}' }}
772     </pre>
773                         Prepend with <em>#custom-css</em> to override styles. Example:<br /><br />
774     <pre>
775     #custom-css .logged-in-email {{ '{' }}
776       color: red;
777     {{ '}' }}
778     </pre>
779                       </ng-container>
780                     </ng-template>
781                   </my-help>
782
783                   <textarea
784                     id="customizationCSS" formControlName="css" class="form-control"
785                     [ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }"
786                   ></textarea>
787                   <div *ngIf="formErrors.instance.customizations.css" class="form-error">{{ formErrors.instance.customizations.css }}</div>
788                 </div>
789               </ng-container>
790             </ng-container>
791
792           </div>
793         </div>
794
795       </ng-template>
796     </ng-container>
797   </div>
798
799   <div [ngbNavOutlet]="nav"></div>
800
801   <div class="form-row mt-4"> <!-- submit placement block -->
802     <div class="col-md-7 col-xl-5"></div>
803     <div class="col-md-5 col-xl-5">
804       <input (click)="formValidated()" type="submit" i18n-value value="Update configuration" [disabled]="!form.valid">
805       <span class="form-error" i18n *ngIf="!form.valid">It seems like the configuration is invalid. Please search for potential errors in the different tabs.</span>
806     </div>
807   </div>
808 </form>