diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index 5c0864f48..fd648a425 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts @@ -10,7 +10,16 @@ import { SharedModerationModule } from '@app/shared/shared-moderation' import { SharedVideoCommentModule } from '@app/shared/shared-video-comment' import { AdminRoutingModule } from './admin-routing.module' import { AdminComponent } from './admin.component' -import { ConfigComponent, EditCustomConfigComponent } from './config' +import { + ConfigComponent, + EditAdvancedConfigurationComponent, + EditBasicConfigurationComponent, + EditConfigurationService, + EditCustomConfigComponent, + EditInstanceInformationComponent, + EditLiveConfigurationComponent, + EditVODTranscodingComponent +} from './config' import { ConfigService } from './config/shared/config.service' import { FollowersListComponent, FollowsComponent, VideoRedundanciesListComponent } from './follows' import { FollowingListComponent } from './follows/following-list/following-list.component' @@ -81,7 +90,13 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom DebugComponent, ConfigComponent, - EditCustomConfigComponent + + EditCustomConfigComponent, + EditBasicConfigurationComponent, + EditVODTranscodingComponent, + EditLiveConfigurationComponent, + EditAdvancedConfigurationComponent, + EditInstanceInformationComponent ], exports: [ @@ -93,7 +108,8 @@ import { UserCreateComponent, UserListComponent, UserPasswordComponent, UsersCom LogsService, DebugService, ConfigService, - PluginApiService + PluginApiService, + EditConfigurationService ] }) export class AdminModule { } diff --git a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html new file mode 100644 index 000000000..db3036c4e --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.html @@ -0,0 +1,107 @@ + + +
+
+
CACHE
+
+ Some files are not federated, and fetched when necessary. Define their caching policies. +
+
+ +
+ + +
+ +
+ + {form.value['cache']['previews']['size'], plural, =1 {cached image} other {cached images}} +
+
{{ formErrors.cache.previews.size }}
+
+ +
+ +
+ + {form.value['cache']['captions']['size'], plural, =1 {cached image} other {cached images}} +
+
{{ formErrors.cache.captions.size }}
+
+
+ +
+
+ +
+
+
+
CUSTOMIZATIONS
+
+ Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill. +
+
+ +
+ + + +
+ + + + + Write JavaScript code directly.
Example:
console.log('my instance is amazing');
+
+
+
+ + + +
{{ formErrors.instance.customizations.javascript }}
+
+ +
+ + + + + + Write CSS code directly. Example:

+
+#custom-css {{ '{' }}
+color: red;
+{{ '}' }}
+
+ Prepend with #custom-css to override styles. Example:

+
+#custom-css .logged-in-email {{ '{' }}
+color: red;
+{{ '}' }}
+
+
+
+
+ + +
{{ formErrors.instance.customizations.css }}
+
+
+
+ +
+
+ +
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts new file mode 100644 index 000000000..a37b7b7d5 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-advanced-configuration.component.ts @@ -0,0 +1,14 @@ + +import { SelectOptionsItem } from 'src/types/select-options-item.model' +import { Component, Input } from '@angular/core' +import { FormGroup } from '@angular/forms' + +@Component({ + selector: 'my-edit-advanced-configuration', + templateUrl: './edit-advanced-configuration.component.html', + styleUrls: [ './edit-custom-config.component.scss' ] +}) +export class EditAdvancedConfigurationComponent { + @Input() form: FormGroup + @Input() formErrors: any +} diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html new file mode 100644 index 000000000..ac1a11b4d --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html @@ -0,0 +1,500 @@ + +
+
+
APPEARANCE
+
+ Use plugins & themes for more involved changes, or add slight customizations. +
+
+ +
+ + +
+ + +
+ +
+
+
+ +
+ +
+ +
+
{{ formErrors.instance.defaultClientRoute }}
+
+ +
+ + + +
+ +
+
{{ formErrors.trending.videos.algorithms.default }}
+
+
+
+ +
+
+ +
+
+
BROADCAST MESSAGE
+
+ Display a message on your instance +
+
+ +
+ + + +
+ +
+ +
+ +
+ +
+ +
+ +
+
{{ formErrors.broadcastMessage.level }}
+
+ +
+ + +
{{ formErrors.broadcastMessage.message }}
+
+ +
+ +
+
+ +
+
+
NEW USERS
+
+ Manage users to set their quota individually. +
+
+ +
+ + +
+ + + ⚠️ This functionality requires a lot of attention and extra moderation. + + + + + + + +
+ +
+ + {form.value['signup']['limit'], plural, =1 {user} other {users}} +
+
{{ formErrors.signup.limit }}
+ Signup won't be limited to a fixed number of users. +
+
+
+
+
+ + +
+ + + + +
{{ formErrors.user.videoQuota }}
+
+ +
+ + + + +
{{ formErrors.user.videoQuotaDaily }}
+
+
+ +
+
+ +
+
+
VIDEOS
+
+ +
+ + + + + +
+ + + allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart. + + +
+ + jobs in parallel +
+ +
{{ formErrors.import.concurrency }}
+
+ +
+ +
+ +
+ +
+ +
+
+ + + + + +
+ + + Unless a user is marked as trusted, their videos will stay private until a moderator reviews them. + + +
+ +
+
+
+ +
+
+ +
+
+
SEARCH
+
+ +
+ + + + +
+ + + Allow your users to look up remote videos/actors that may not be federated with your instance + + +
+ +
+ + + Allow anonymous users to look up remote videos/actors that may not be federated with your instance + + +
+ +
+ + +
+ + +

⚠️ This functionality depends heavily on the moderation of instances followed by the search index you select.

+ + + You should only use moderated search indexes in production, or host your own. + +
+ + +
+ + +
{{ formErrors.search.searchIndex.url }}
+
+ +
+ +
+ +
+ + + Otherwise the local search stays used by default + + +
+ +
+
+
+ +
+ +
+ +
+
+ +
+
+
FEDERATION
+
+ Manage relations with other instances. +
+
+ +
+ + + + +
+ +
+ +
+ +
+
+
+ + + + + +
+ + + ⚠️ This functionality requires a lot of attention and extra moderation. + + +
+
+ + +
+ + +

⚠️ This functionality requires a lot of attention and extra moderation.

+ + + You should only follow moderated indexes in production, or host your own. + +
+ + +
+ + +
{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+
ADMINISTRATORS
+
+ +
+ +
+ + +
{{ formErrors.admin.email }}
+
+ +
+ +
+ +
+
+ +
+
+
TWITTER
+
+ Provide the Twitter account representing your instance to improve link previews. + If you don't have a Twitter account, just leave the default value. +
+
+ +
+ + + + +
+ + + +
{{ formErrors.services.twitter.username }}
+
+ +
+ + + Instance allowed by Twitter + + + + + If your instance is explicitly allowed by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.
+ If the instance is not, we use an image link card that will redirect to your PeerTube instance.

+ Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on + https://cards-dev.twitter.com/validator + to see if you instance is allowed. +
+
+
+
+ +
+
+ +
+
+
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts new file mode 100644 index 000000000..9a19c2913 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts @@ -0,0 +1,83 @@ + +import { pairwise } from 'rxjs/operators' +import { Component, Input, OnInit } from '@angular/core' +import { FormGroup } from '@angular/forms' +import { ServerConfig } from '@shared/models' +import { ConfigService } from '../shared/config.service' + +@Component({ + selector: 'my-edit-basic-configuration', + templateUrl: './edit-basic-configuration.component.html', + styleUrls: [ './edit-custom-config.component.scss' ] +}) +export class EditBasicConfigurationComponent implements OnInit { + @Input() form: FormGroup + @Input() formErrors: any + + @Input() serverConfig: ServerConfig + + signupAlertMessage: string + + constructor ( + private configService: ConfigService + ) { } + + ngOnInit () { + this.checkSignupField() + } + + getVideoQuotaOptions () { + return this.configService.videoQuotaOptions + } + + getVideoQuotaDailyOptions () { + return this.configService.videoQuotaDailyOptions + } + + getAvailableThemes () { + return this.serverConfig.theme.registered + .map(t => t.name) + } + + doesTrendingVideosAlgorithmsEnabledInclude (algorithm: string) { + const enabled = this.form.value['trending']['videos']['algorithms']['enabled'] + if (!Array.isArray(enabled)) return false + + return !!enabled.find((e: string) => e === algorithm) + } + + isSignupEnabled () { + return this.form.value['signup']['enabled'] === true + } + + isSearchIndexEnabled () { + return this.form.value['search']['searchIndex']['enabled'] === true + } + + isAutoFollowIndexEnabled () { + return this.form.value['followings']['instance']['autoFollowIndex']['enabled'] === true + } + + private checkSignupField () { + const signupControl = this.form.get('signup.enabled') + + signupControl.valueChanges + .pipe(pairwise()) + .subscribe(([ oldValue, newValue ]) => { + if (oldValue !== true && newValue === true) { + // tslint:disable:max-line-length + this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.` + + this.form.patchValue({ + autoBlacklist: { + videos: { + ofUsers: { + enabled: true + } + } + } + }) + } + }) + } +} diff --git a/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts b/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts new file mode 100644 index 000000000..63e0343fa --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts @@ -0,0 +1,90 @@ +import { Injectable } from '@angular/core' +import { FormGroup } from '@angular/forms' + +export type ResolutionOption = { + id: string + label: string + description?: string +} + +@Injectable() +export class EditConfigurationService { + + getVODResolutions () { + return [ + { + id: '0p', + label: $localize`Audio-only`, + description: $localize`A .mp4 that keeps the original audio track, with no video` + }, + { + id: '240p', + label: $localize`240p` + }, + { + id: '360p', + label: $localize`360p` + }, + { + id: '480p', + label: $localize`480p` + }, + { + id: '720p', + label: $localize`720p` + }, + { + id: '1080p', + label: $localize`1080p` + }, + { + id: '1440p', + label: $localize`1440p` + }, + { + id: '2160p', + label: $localize`2160p` + } + ] + } + + getLiveResolutions () { + return this.getVODResolutions().filter(r => r.id !== '0p') + } + + isTranscodingEnabled (form: FormGroup) { + return form.value['transcoding']['enabled'] === true + } + + isLiveEnabled (form: FormGroup) { + return form.value['live']['enabled'] === true + } + + isLiveTranscodingEnabled (form: FormGroup) { + return form.value['live']['transcoding']['enabled'] === true + } + + getTotalTranscodingThreads (form: FormGroup) { + const transcodingEnabled = form.value['transcoding']['enabled'] + const transcodingThreads = form.value['transcoding']['threads'] + const liveTranscodingEnabled = form.value['live']['transcoding']['enabled'] + const liveTranscodingThreads = form.value['live']['transcoding']['threads'] + + // checks whether all enabled method are on fixed values and not on auto (= 0) + let noneOnAuto = !transcodingEnabled || +transcodingThreads > 0 + noneOnAuto &&= !liveTranscodingEnabled || +liveTranscodingThreads > 0 + + // count total of fixed value, repalcing auto by a single thread (knowing it will display "at least") + let value = 0 + if (transcodingEnabled) value += +transcodingThreads || 1 + if (liveTranscodingEnabled) value += +liveTranscodingThreads || 1 + + return { + value, + atMost: noneOnAuto, // auto switches everything to a least estimation since ffmpeg will take as many threads as possible + unit: value > 1 + ? $localize`threads` + : $localize`thread` + } + } +} diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 135276192..534b03517 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html @@ -7,231 +7,8 @@ Instance information - - - -
-
-
INSTANCE
-
- -
- -
- - -
{{ formErrors.instance.name }}
-
- -
- - -
{{ formErrors.instance.shortDescription }}
-
- -
- - -
{{ formErrors.instance.description }}
-
- -
- - -
- - -
-
- -
- - -
- - -
-
- -
-
- -
-
-
MODERATION & NSFW
-
- Manage users to build a moderation team. -
-
- -
- -
- - - This instance is dedicated to sensitive or NSFW content - - - - - Enabling it will allow other administrators to know that you are mainly federating sensitive content.

- Moreover, the NSFW checkbox on video upload will be automatically checked by default. -
-
-
-
- -
- - - - - - With Do not list or Blur thumbnails, a confirmation will be requested to watch the video. - - - - -
- -
-
{{ formErrors.instance.defaultNSFWPolicy }}
-
- -
- - -
{{ formErrors.instance.terms }}
-
- -
- - -
{{ formErrors.instance.codeOfConduct }}
-
- -
- -
Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc
- - -
{{ formErrors.instance.moderationInformation }}
-
- -
-
- -
-
-
YOU AND YOUR INSTANCE
-
- -
- -
- -
A single person? A non-profit? A company?
- - - -
{{ formErrors.instance.administrator }}
-
- -
- -
To share your personal videos? To open registrations and allow people to upload what they want?
- - -
{{ formErrors.instance.creationReason }}
-
- -
- -
It's important to know for users who want to register on your instance
- - -
{{ formErrors.instance.maintenanceLifetime }}
-
- -
- -
With your own funds? With user donations? Advertising?
- - -
{{ formErrors.instance.businessModel }}
-
- -
-
- -
-
-
OTHER INFORMATION
-
- -
- -
- -
i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.
- - - -
{{ formErrors.instance.hardwareInformation }}
-
- -
-
- -
+ +
@@ -239,502 +16,8 @@ Basic configuration - -
-
-
APPEARANCE
-
- Use plugins & themes for more involved changes, or add slight customizations. -
-
- -
- - -
- - -
- -
-
-
- -
- -
- -
-
{{ formErrors.instance.defaultClientRoute }}
-
- -
- - - -
- -
-
{{ formErrors.trending.videos.algorithms.default }}
-
-
-
- -
-
- -
-
-
BROADCAST MESSAGE
-
- Display a message on your instance -
-
- -
- - - -
- -
- -
- -
- -
- -
- -
-
{{ formErrors.broadcastMessage.level }}
-
- -
- - -
{{ formErrors.broadcastMessage.message }}
-
- -
- -
-
- -
-
-
NEW USERS
-
- Manage users to set their quota individually. -
-
- -
- - -
- - - ⚠️ This functionality requires a lot of attention and extra moderation. - - - - - - -
- -
- - {form.value['signup']['limit'], plural, =1 {user} other {users}} -
-
{{ formErrors.signup.limit }}
- Signup won't be limited to a fixed number of users. -
-
-
-
-
- - -
- - - - -
{{ formErrors.user.videoQuota }}
-
- -
- - - - -
{{ formErrors.user.videoQuotaDaily }}
-
-
- -
-
- -
-
-
VIDEOS
-
- -
- - - - - -
- - - allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart. - - -
- - jobs in parallel -
- -
{{ formErrors.import.concurrency }}
-
- -
- -
- -
- -
- -
-
- - - - - -
- - - Unless a user is marked as trusted, their videos will stay private until a moderator reviews them. - - -
- -
-
-
- -
-
- -
-
-
SEARCH
-
- -
- - - - -
- - - Allow your users to look up remote videos/actors that may not be federated with your instance - - -
- -
- - - Allow anonymous users to look up remote videos/actors that may not be federated with your instance - - -
- -
- - -
- - -

⚠️ This functionality depends heavily on the moderation of instances followed by the search index you select.

- - - You should only use moderated search indexes in production, or host your own. - -
- - -
- - -
{{ formErrors.search.searchIndex.url }}
-
- -
- -
- -
- - - Otherwise the local search stays used by default - - -
- -
-
-
- -
- -
- -
-
- -
-
-
FEDERATION
-
- Manage relations with other instances. -
-
- -
- - - - -
- -
- -
- -
-
-
- - - - - -
- - - ⚠️ This functionality requires a lot of attention and extra moderation. - - -
-
- - -
- - -

⚠️ This functionality requires a lot of attention and extra moderation.

- - - You should only follow moderated indexes in production, or host your own. - -
- - -
- - -
{{ formErrors.followings.instance.autoFollowIndex.indexUrl }}
-
-
-
-
- -
-
-
- -
-
- -
-
-
ADMINISTRATORS
-
- -
- -
- - -
{{ formErrors.admin.email }}
-
- -
- -
- -
-
- -
-
-
TWITTER
-
- Provide the Twitter account representing your instance to improve link previews. - If you don't have a Twitter account, just leave the default value. -
-
- -
- - - - -
- - - -
{{ formErrors.services.twitter.username }}
-
- -
- - - Instance allowed by Twitter - - - - - If your instance is explicitly allowed by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.
- If the instance is not, we use an image link card that will redirect to your PeerTube instance.

- Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on - https://cards-dev.twitter.com/validator - to see if you instance is allowed. -
-
-
-
- -
-
- -
-
+ +
@@ -742,208 +25,8 @@ VOD Transcoding - -
-
-
- -
- - Estimating a server's capacity to transcode and stream videos isn't easy and we can't tune PeerTube automatically. - - - However, you may want to read our guidelines before tweaking the following values. - - - -
- - -
-
- -
-
-
TRANSCODING
-
- Process uploaded videos so that they are in a streamable form that any device can play. Though costly in - resources, this is a critical part of PeerTube, so tread carefully. -
-
- -
- - - -
- - - Transcoding enabled - - - - -
- - -
- - - Allows users to upload .mkv, .mov, .avi, .wmv, .flv, .f4v, .3g2, .3gp, .mts, .m2ts, .mxf, or .nut videos. - - -
- -
- - -
Allows users to upload .mp3, .ogg, .wma, .flac, .aac, or .ac3 audio files.
-
The file will be merged in a still image video with the preview file on upload.
-
-
-
-
- -
- - - -
- - - -

If you also enabled HLS support, it will multiply videos storage by 2

- -
- - If disabled, breaks federation with PeerTube instances < 2.1 -
-
-
-
-
- - -
- - - - Requires ffmpeg >= 4.1 - -

Generate HLS playlists and fragmented MP4 files resulting in a better playback than with plain WebTorrent:

-
    -
  • Resolution change is smoother
  • -
  • Faster playback especially with long videos
  • -
  • More stable playback (less bugs/infinite loading)
  • -
- -

If you also enabled WebTorrent support, it will multiply videos storage by 2

-
-
-
-
-
- -
- - -
- -
- - -
-
-
-
- - - The original file resolution will be the default target if no option is selected. - -
-
-
-
- -
-
-
- -
- - - will claim at most {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding - will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding - - - - -
{{ formErrors.transcoding.threads }}
-
- -
- - - allows to transcode multiple files in parallel. ⚠️ Requires a PeerTube restart. - - -
- - jobs in parallel -
- -
{{ formErrors.transcoding.concurrency }}
-
- -
- - new transcoding profiles can be added by PeerTube plugins - - - - {{ item }} - -
- x264, targeting maximum device compatibility -
-
-
-
{{ formErrors.transcoding.profile }}
-
- -
- -
-
- + +
@@ -951,160 +34,8 @@ Live streaming - -
-
-
LIVE
-
- Enable users of your instance to stream live. -
-
- -
- - - -
- - - Allow live streaming - - - -
⚠️ Enabling live streaming requires trust in your users and extra moderation work
-
If enabled, your server needs to accept incoming TCP traffic on port {{ liveRTMPPort }}
-
- - - -
- - - If the user quota is reached, PeerTube will automatically terminate the live streaming - - -
- -
- -
- - {form.value['live']['maxInstanceLives'], plural, =1 {live} other {lives}} -
-
- -
- -
- - {form.value['live']['maxUserLives'], plural, =1 {live} other {lives}} -
-
- -
- - - -
- -
-
-
-
-
-
- -
-
-
TRANSCODING
-
- Same as VOD transcoding, transcoding live streams so that they are in a streamable form that any device can play. Requires a beefy CPU, and then some. -
-
- -
- - - - -
- - - Transcoding enabled for live streams - - -
- -
- - -
- -
- - -
-
-
-
-
-
-
- -
- - - will claim at most {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding - will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding - - - -
{{ formErrors.live.transcoding.threads }}
-
- -
- - new live transcoding profiles can be added by PeerTube plugins - - - - {{ item }} - -
- x264, targeting maximum device compatibility -
-
-
-
{{ formErrors.live.transcoding.profile }}
-
- -
-
- -
-
- + +
@@ -1112,111 +43,8 @@ Advanced configuration - -
-
-
CACHE
-
- Some files are not federated, and fetched when necessary. Define their caching policies. -
-
- -
- - -
- -
- - {form.value['cache']['previews']['size'], plural, =1 {cached image} other {cached images}} -
-
{{ formErrors.cache.previews.size }}
-
- -
- -
- - {form.value['cache']['captions']['size'], plural, =1 {cached image} other {cached images}} -
-
{{ formErrors.cache.captions.size }}
-
-
- -
-
- -
-
-
-
CUSTOMIZATIONS
-
- Slight modifications to your PeerTube instance for when creating a plugin or theme is overkill. -
-
- -
- - - -
- - - - - Write JavaScript code directly.
Example:
console.log('my instance is amazing');
-
-
-
- - - -
{{ formErrors.instance.customizations.javascript }}
-
- -
- - - - - - Write CSS code directly. Example:

-
-    #custom-css {{ '{' }}
-      color: red;
-    {{ '}' }}
-    
- Prepend with #custom-css to override styles. Example:

-
-    #custom-css .logged-in-email {{ '{' }}
-      color: red;
-    {{ '}' }}
-    
-
-
-
- - -
{{ formErrors.instance.customizations.css }}
-
-
-
- -
-
- + +
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 29524fdd8..a5eddf6c2 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 @@ -1,8 +1,5 @@ -import { forkJoin } from 'rxjs' -import { pairwise } from 'rxjs/operators' -import { SelectOptionsItem } from 'src/types/select-options-item.model' -import { ViewportScroller } from '@angular/common' -import { AfterViewChecked, Component, OnInit, ViewChild } from '@angular/core' + +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { ConfigService } from '@app/+admin/config/shared/config.service' import { Notifier } from '@app/core' @@ -22,155 +19,35 @@ import { } 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 { NgbNav } from '@ng-bootstrap/ng-bootstrap' import { CustomConfig, ServerConfig } from '@shared/models' +import { forkJoin } from 'rxjs' +import { SelectOptionsItem } from 'src/types/select-options-item.model' +import { EditConfigurationService } from './edit-configuration.service' @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('nav') nav: NgbNav +export class EditCustomConfigComponent extends FormReactive implements OnInit { + activeNav: string - initDone = false customConfig: CustomConfig - - resolutions: { id: string, label: string, description?: string }[] = [] - liveResolutions: { id: string, label: string, description?: string }[] = [] - - transcodingThreadOptions: SelectOptionsItem[] = [] - liveMaxDurationOptions: SelectOptionsItem[] = [] + serverConfig: ServerConfig languageItems: SelectOptionsItem[] = [] categoryItems: SelectOptionsItem[] = [] - signupAlertMessage: string - - activeNav: string - - private serverConfig: ServerConfig - constructor ( private router: Router, private route: ActivatedRoute, - private viewportScroller: ViewportScroller, protected formValidatorService: FormValidatorService, private notifier: Notifier, private configService: ConfigService, - private serverService: ServerService + private serverService: ServerService, + private editConfigurationService: EditConfigurationService ) { super() - - this.resolutions = [ - { - id: '0p', - label: $localize`Audio-only`, - description: $localize`A .mp4 that keeps the original audio track, with no video` - }, - { - id: '240p', - label: $localize`240p` - }, - { - id: '360p', - label: $localize`360p` - }, - { - id: '480p', - label: $localize`480p` - }, - { - id: '720p', - label: $localize`720p` - }, - { - id: '1080p', - label: $localize`1080p` - }, - { - id: '1440p', - label: $localize`1440p` - }, - { - id: '2160p', - label: $localize`2160p` - } - ] - - this.liveResolutions = this.resolutions.filter(r => r.id !== '0p') - - this.transcodingThreadOptions = [ - { id: 0, label: $localize`Auto (via ffmpeg)` }, - { id: 1, label: '1' }, - { id: 2, label: '2' }, - { id: 4, label: '4' }, - { id: 8, label: '8' }, - { id: 12, label: '12' }, - { id: 16, label: '16' }, - { id: 32, label: '32' } - ] - - this.liveMaxDurationOptions = [ - { id: -1, label: $localize`No limit` }, - { id: 1000 * 3600, label: $localize`1 hour` }, - { id: 1000 * 3600 * 3, label: $localize`3 hours` }, - { id: 1000 * 3600 * 5, label: $localize`5 hours` }, - { id: 1000 * 3600 * 10, label: $localize`10 hours` } - ] - } - - get videoQuotaOptions () { - return this.configService.videoQuotaOptions - } - - get videoQuotaDailyOptions () { - return this.configService.videoQuotaDailyOptions - } - - get availableThemes () { - return this.serverConfig.theme.registered - .map(t => t.name) - } - - get liveRTMPPort () { - return this.serverConfig.live.rtmp.port - } - - getAvailableTranscodingProfile (type: 'live' | 'vod') { - const profiles = type === 'live' - ? this.serverConfig.live.transcoding.availableProfiles - : this.serverConfig.transcoding.availableProfiles - - return profiles.map(p => ({ id: p, label: p })) - } - - getTotalTranscodingThreads () { - const transcodingEnabled = this.form.value['transcoding']['enabled'] - const transcodingThreads = this.form.value['transcoding']['threads'] - const liveTranscodingEnabled = this.form.value['live']['transcoding']['enabled'] - const liveTranscodingThreads = this.form.value['live']['transcoding']['threads'] - - // checks whether all enabled method are on fixed values and not on auto (= 0) - let noneOnAuto = !transcodingEnabled || +transcodingThreads > 0 - noneOnAuto &&= !liveTranscodingEnabled || +liveTranscodingThreads > 0 - - // count total of fixed value, repalcing auto by a single thread (knowing it will display "at least") - let value = 0 - if (transcodingEnabled) value += +transcodingThreads || 1 - if (liveTranscodingEnabled) value += +liveTranscodingThreads || 1 - - return { - value, - atMost: noneOnAuto, // auto switches everything to a least estimation since ffmpeg will take as many threads as possible - unit: value > 1 - ? $localize`threads` - : $localize`thread` - } - } - - getResolutionKey (resolution: string) { - return 'transcoding.resolutions.' + resolution } ngOnInit () { @@ -346,60 +223,24 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A } } - 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.liveResolutions) { + for (const resolution of this.editConfigurationService.getLiveResolutions()) { defaultValues.live.transcoding.resolutions[resolution.id] = 'false' formGroupData.live.transcoding.resolutions[resolution.id] = null } + this.buildForm(formGroupData) + if (this.route.snapshot.fragment) { this.onNavChange(this.route.snapshot.fragment) } - this.buildForm(formGroupData) - this.loadForm() - - this.checkTranscodingFields() - this.checkSignupField() - } - - ngAfterViewChecked () { - if (!this.initDone) { - this.initDone = true - this.gotoAnchor() - } - } - - isTranscodingEnabled () { - return this.form.value['transcoding']['enabled'] === true - } - - isLiveEnabled () { - return this.form.value['live']['enabled'] === true - } - - isLiveTranscodingEnabled () { - return this.form.value['live']['transcoding']['enabled'] === true - } - - isSignupEnabled () { - return this.form.value['signup']['enabled'] === true - } - - isSearchIndexEnabled () { - return this.form.value['search']['searchIndex']['enabled'] === true - } - - isAutoFollowIndexEnabled () { - return this.form.value['followings']['instance']['autoFollowIndex']['enabled'] === true - } - - trendingVideosAlgorithmsEnabledIncludes (algorithm: string) { - return this.form.value['trending']['videos']['algorithms']['enabled'].find((e: string) => e === algorithm) + this.loadConfigAndUpdateForm() + this.loadCategoriesAndLanguages() } async formValidated () { @@ -422,18 +263,6 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A ) } - gotoAnchor () { - const hashToNav = { - 'customizations': 'advanced-configuration' - } - const hash = window.location.hash.replace('#', '') - - if (hash && Object.keys(hashToNav).includes(hash)) { - this.nav.select(hashToNav[hash]) - setTimeout(() => this.viewportScroller.scrollToAnchor(hash), 100) - } - } - hasConsistentOptions () { if (this.hasLiveAllowReplayConsistentOptions()) return true @@ -441,7 +270,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A } hasLiveAllowReplayConsistentOptions () { - if (this.isTranscodingEnabled() === false && this.isLiveEnabled() && this.form.value['live']['allowReplay'] === true) { + if ( + this.editConfigurationService.isTranscodingEnabled(this.form) === false && + this.editConfigurationService.isLiveEnabled(this.form) && + this.form.value['live']['allowReplay'] === true + ) { return false } @@ -458,18 +291,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A this.form.patchValue(this.customConfig) } - private loadForm () { - forkJoin([ - this.configService.getCustomConfig(), - this.serverService.getVideoLanguages(), - this.serverService.getVideoCategories() - ]).subscribe( - ([ config, languages, categories ]) => { + private loadConfigAndUpdateForm () { + this.configService.getCustomConfig() + .subscribe(config => { this.customConfig = config - this.languageItems = languages.map(l => ({ label: l.label, id: l.id })) - this.categoryItems = categories.map(l => ({ label: l.label, id: l.id + '' })) - this.updateForm() // Force form validation this.forceCheck() @@ -479,53 +305,17 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A ) } - private checkTranscodingFields () { - const hlsControl = this.form.get('transcoding.hls.enabled') - const webtorrentControl = this.form.get('transcoding.webtorrent.enabled') + private loadCategoriesAndLanguages () { + forkJoin([ + this.serverService.getVideoLanguages(), + this.serverService.getVideoCategories() + ]).subscribe( + ([ languages, categories ]) => { + this.languageItems = languages.map(l => ({ label: l.label, id: l.id })) + this.categoryItems = categories.map(l => ({ label: l.label, id: l.id + '' })) + }, - 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 checkSignupField () { - const signupControl = this.form.get('signup.enabled') - - signupControl.valueChanges - .pipe(pairwise()) - .subscribe(([ oldValue, newValue ]) => { - if (oldValue !== true && newValue === true) { - // tslint:disable:max-line-length - this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.` - - this.form.patchValue({ - autoBlacklist: { - videos: { - ofUsers: { - enabled: true - } - } - } - }) - } - }) + err => this.notifier.error(err.message) + ) } } diff --git a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html new file mode 100644 index 000000000..6f19ede0a --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.html @@ -0,0 +1,228 @@ + + + + +
+
+
INSTANCE
+
+ +
+ +
+ + +
{{ formErrors.instance.name }}
+
+ +
+ + +
{{ formErrors.instance.shortDescription }}
+
+ +
+ + +
{{ formErrors.instance.description }}
+
+ +
+ + +
+ + +
+
+ +
+ + +
+ + +
+
+ +
+
+ +
+
+
MODERATION & NSFW
+
+ Manage users to build a moderation team. +
+
+ +
+ +
+ + + This instance is dedicated to sensitive or NSFW content + + + + + Enabling it will allow other administrators to know that you are mainly federating sensitive content.

+ Moreover, the NSFW checkbox on video upload will be automatically checked by default. +
+
+
+
+ +
+ + + + + + With Do not list or Blur thumbnails, a confirmation will be requested to watch the video. + + + + +
+ +
+
{{ formErrors.instance.defaultNSFWPolicy }}
+
+ +
+ + +
{{ formErrors.instance.terms }}
+
+ +
+ + +
{{ formErrors.instance.codeOfConduct }}
+
+ +
+ +
Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc
+ + +
{{ formErrors.instance.moderationInformation }}
+
+ +
+
+ +
+
+
YOU AND YOUR INSTANCE
+
+ +
+ +
+ +
A single person? A non-profit? A company?
+ + + +
{{ formErrors.instance.administrator }}
+
+ +
+ +
To share your personal videos? To open registrations and allow people to upload what they want?
+ + +
{{ formErrors.instance.creationReason }}
+
+ +
+ +
It's important to know for users who want to register on your instance
+ + +
{{ formErrors.instance.maintenanceLifetime }}
+
+ +
+ +
With your own funds? With user donations? Advertising?
+ + +
{{ formErrors.instance.businessModel }}
+
+ +
+
+ +
+
+
OTHER INFORMATION
+
+ +
+ +
+ +
i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.
+ + + +
{{ formErrors.instance.hardwareInformation }}
+
+ +
+
+ +
+ +
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.ts new file mode 100644 index 000000000..26365e707 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-instance-information.component.ts @@ -0,0 +1,16 @@ +import { SelectOptionsItem } from 'src/types/select-options-item.model' +import { Component, Input } from '@angular/core' +import { FormGroup } from '@angular/forms' + +@Component({ + selector: 'my-edit-instance-information', + templateUrl: './edit-instance-information.component.html', + styleUrls: [ './edit-custom-config.component.scss' ] +}) +export class EditInstanceInformationComponent { + @Input() form: FormGroup + @Input() formErrors: any + + @Input() languageItems: SelectOptionsItem[] = [] + @Input() categoryItems: SelectOptionsItem[] = [] +} diff --git a/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html new file mode 100644 index 000000000..4b1a55245 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.html @@ -0,0 +1,155 @@ + + +
+
+
LIVE
+
+ Enable users of your instance to stream live. +
+
+ +
+ + + +
+ + + Allow live streaming + + + +
⚠️ Enabling live streaming requires trust in your users and extra moderation work
+
If enabled, your server needs to accept incoming TCP traffic on port {{ getLiveRTMPPort() }}
+
+ + + +
+ + + If the user quota is reached, PeerTube will automatically terminate the live streaming + + +
+ +
+ +
+ + {form.value['live']['maxInstanceLives'], plural, =1 {live} other {lives}} +
+
+ +
+ +
+ + {form.value['live']['maxUserLives'], plural, =1 {live} other {lives}} +
+
+ +
+ + + +
+ +
+
+
+
+
+
+ +
+
+
TRANSCODING
+
+ Same as VOD transcoding, transcoding live streams so that they are in a streamable form that any device can play. Requires a beefy CPU, and then some. +
+
+ +
+ + + + +
+ + + Transcoding enabled for live streams + + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+
+
+ +
+ + + will claim at most {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding + will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding + + + +
{{ formErrors.live.transcoding.threads }}
+
+ +
+ + new live transcoding profiles can be added by PeerTube plugins + + + + {{ item }} + +
+ x264, targeting maximum device compatibility +
+
+
+
{{ formErrors.live.transcoding.profile }}
+
+ +
+
+ +
+
+
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts new file mode 100644 index 000000000..a82a40a84 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-live-configuration.component.ts @@ -0,0 +1,67 @@ + +import { SelectOptionsItem } from 'src/types/select-options-item.model' +import { Component, Input, OnInit } from '@angular/core' +import { FormGroup } from '@angular/forms' +import { ServerConfig } from '@shared/models' +import { ConfigService } from '../shared/config.service' +import { EditConfigurationService, ResolutionOption } from './edit-configuration.service' + +@Component({ + selector: 'my-edit-live-configuration', + templateUrl: './edit-live-configuration.component.html', + styleUrls: [ './edit-custom-config.component.scss' ] +}) +export class EditLiveConfigurationComponent implements OnInit { + @Input() form: FormGroup + @Input() formErrors: any + @Input() serverConfig: ServerConfig + + transcodingThreadOptions: SelectOptionsItem[] = [] + liveMaxDurationOptions: SelectOptionsItem[] = [] + liveResolutions: ResolutionOption[] = [] + + constructor ( + private configService: ConfigService, + private editConfigurationService: EditConfigurationService + ) { } + + ngOnInit () { + this.transcodingThreadOptions = this.configService.transcodingThreadOptions + + this.liveMaxDurationOptions = [ + { id: -1, label: $localize`No limit` }, + { id: 1000 * 3600, label: $localize`1 hour` }, + { id: 1000 * 3600 * 3, label: $localize`3 hours` }, + { id: 1000 * 3600 * 5, label: $localize`5 hours` }, + { id: 1000 * 3600 * 10, label: $localize`10 hours` } + ] + + this.liveResolutions = this.editConfigurationService.getLiveResolutions() + } + + getAvailableTranscodingProfile () { + const profiles = this.serverConfig.live.transcoding.availableProfiles + + return profiles.map(p => ({ id: p, label: p })) + } + + getResolutionKey (resolution: string) { + return 'live.transcoding.resolutions.' + resolution + } + + getLiveRTMPPort () { + return this.serverConfig.live.rtmp.port + } + + isLiveEnabled () { + return this.editConfigurationService.isLiveEnabled(this.form) + } + + isLiveTranscodingEnabled () { + return this.editConfigurationService.isLiveTranscodingEnabled(this.form) + } + + getTotalTranscodingThreads () { + return this.editConfigurationService.getTotalTranscodingThreads(this.form) + } +} diff --git a/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html new file mode 100644 index 000000000..a51909865 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.html @@ -0,0 +1,201 @@ + + +
+
+
+ +
+ + Estimating a server's capacity to transcode and stream videos isn't easy and we can't tune PeerTube automatically. + + + However, you may want to read our guidelines before tweaking the following values. + + + +
+
+
+ +
+
+
TRANSCODING
+
+ Process uploaded videos so that they are in a streamable form that any device can play. Though costly in + resources, this is a critical part of PeerTube, so tread carefully. +
+
+ +
+ + + +
+ + + Transcoding enabled + + + + +
+ + +
+ + + Allows users to upload .mkv, .mov, .avi, .wmv, .flv, .f4v, .3g2, .3gp, .mts, .m2ts, .mxf, or .nut videos. + + +
+ +
+ + +
Allows users to upload .mp3, .ogg, .wma, .flac, .aac, or .ac3 audio files.
+
The file will be merged in a still image video with the preview file on upload.
+
+
+
+
+ +
+ + + +
+ + + +

If you also enabled HLS support, it will multiply videos storage by 2

+ +
+ + If disabled, breaks federation with PeerTube instances < 2.1 +
+
+
+
+
+ + +
+ + + + Requires ffmpeg >= 4.1 + +

Generate HLS playlists and fragmented MP4 files resulting in a better playback than with plain WebTorrent:

+
    +
  • Resolution change is smoother
  • +
  • Faster playback especially with long videos
  • +
  • More stable playback (less bugs/infinite loading)
  • +
+ +

If you also enabled WebTorrent support, it will multiply videos storage by 2

+
+
+
+
+
+ +
+ + +
+ +
+ + +
+
+
+
+ + + The original file resolution will be the default target if no option is selected. + +
+
+
+
+ +
+
+
+ +
+ + + will claim at most {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding + will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding + + + + +
{{ formErrors.transcoding.threads }}
+
+ +
+ + + allows to transcode multiple files in parallel. ⚠️ Requires a PeerTube restart. + + +
+ + jobs in parallel +
+ +
{{ formErrors.transcoding.concurrency }}
+
+ +
+ + new transcoding profiles can be added by PeerTube plugins + + + + {{ item }} + +
+ x264, targeting maximum device compatibility +
+
+
+
{{ formErrors.transcoding.profile }}
+
+ +
+ +
+
+
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.ts new file mode 100644 index 000000000..d745912a0 --- /dev/null +++ b/client/src/app/+admin/config/edit-custom-config/edit-vod-transcoding.component.ts @@ -0,0 +1,78 @@ + +import { SelectOptionsItem } from 'src/types/select-options-item.model' +import { Component, Input, OnInit } from '@angular/core' +import { FormGroup } from '@angular/forms' +import { ServerConfig } from '@shared/models' +import { ConfigService } from '../shared/config.service' +import { EditConfigurationService, ResolutionOption } from './edit-configuration.service' + +@Component({ + selector: 'my-edit-vod-transcoding', + templateUrl: './edit-vod-transcoding.component.html', + styleUrls: [ './edit-custom-config.component.scss' ] +}) +export class EditVODTranscodingComponent implements OnInit { + @Input() form: FormGroup + @Input() formErrors: any + @Input() serverConfig: ServerConfig + + transcodingThreadOptions: SelectOptionsItem[] = [] + resolutions: ResolutionOption[] = [] + + constructor ( + private configService: ConfigService, + private editConfigurationService: EditConfigurationService + ) { } + + ngOnInit () { + this.transcodingThreadOptions = this.configService.transcodingThreadOptions + this.resolutions = this.editConfigurationService.getVODResolutions() + + this.checkTranscodingFields() + } + + getAvailableTranscodingProfile () { + const profiles = this.serverConfig.transcoding.availableProfiles + + return profiles.map(p => ({ id: p, label: p })) + } + + getResolutionKey (resolution: string) { + return 'transcoding.resolutions.' + resolution + } + + isTranscodingEnabled () { + return this.editConfigurationService.isTranscodingEnabled(this.form) + } + + getTotalTranscodingThreads () { + return this.editConfigurationService.getTotalTranscodingThreads(this.form) + } + + 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() + } + }) + } +} diff --git a/client/src/app/+admin/config/edit-custom-config/index.ts b/client/src/app/+admin/config/edit-custom-config/index.ts index 1ec12631f..95fcc8f52 100644 --- a/client/src/app/+admin/config/edit-custom-config/index.ts +++ b/client/src/app/+admin/config/edit-custom-config/index.ts @@ -1 +1,7 @@ +export * from './edit-advanced-configuration.component' +export * from './edit-basic-configuration.component' +export * from './edit-configuration.service' export * from './edit-custom-config.component' +export * from './edit-instance-information.component' +export * from './edit-live-configuration.component' +export * from './edit-vod-transcoding.component' diff --git a/client/src/app/+admin/config/shared/config.service.ts b/client/src/app/+admin/config/shared/config.service.ts index d29b752f7..80f495b41 100644 --- a/client/src/app/+admin/config/shared/config.service.ts +++ b/client/src/app/+admin/config/shared/config.service.ts @@ -12,11 +12,12 @@ export class ConfigService { videoQuotaOptions: SelectOptionsItem[] = [] videoQuotaDailyOptions: SelectOptionsItem[] = [] + transcodingThreadOptions: SelectOptionsItem[] = [] constructor ( private authHttp: HttpClient, private restExtractor: RestExtractor - ) { + ) { this.videoQuotaOptions = [ { id: -1, label: $localize`Unlimited` }, { id: 0, label: $localize`None - no upload possible` }, @@ -44,6 +45,17 @@ export class ConfigService { { id: 20 * 1024 * 1024 * 1024, label: $localize`20GB` }, { id: 50 * 1024 * 1024 * 1024, label: $localize`50GB` } ] + + this.transcodingThreadOptions = [ + { id: 0, label: $localize`Auto (via ffmpeg)` }, + { id: 1, label: '1' }, + { id: 2, label: '2' }, + { id: 4, label: '4' }, + { id: 8, label: '8' }, + { id: 12, label: '12' }, + { id: 16, label: '16' }, + { id: 32, label: '32' } + ] } getCustomConfig () {