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 5f0a5ff6c..844620ca2 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
@@ -445,13 +445,11 @@
allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart.
-
-
+
+
+ jobs in parallel
+
{{ formErrors.import.concurrency }}
@@ -892,13 +890,13 @@
will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding
-
-
-
+
+
{{ formErrors.transcoding.threads }}
@@ -908,13 +906,11 @@
allows to transcode multiple files in parallel. ⚠️ Requires a PeerTube restart.
-
-
+
+
+ jobs in parallel
+
{{ formErrors.transcoding.concurrency }}
@@ -922,7 +918,7 @@
new transcoding profiles can be added by PeerTube plugins
- x264, targeting maximum device compatibility
-
+
{{ formErrors.transcoding.profile }}
@@ -1007,10 +1003,10 @@
-
+ bindLabel="label" bindValue="value" [clearable]="false" [searchable]="true"
+ >
@@ -1069,13 +1065,12 @@
will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding
-
-
-
+
{{ formErrors.live.transcoding.threads }}
@@ -1083,7 +1078,7 @@
new live transcoding profiles can be added by PeerTube plugins
- x264, targeting maximum device compatibility
-
+
{{ formErrors.live.transcoding.profile }}
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
index 665247368..c12465d45 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
+++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss
@@ -42,7 +42,8 @@ input[type=checkbox] {
@include peertube-select-container($form-base-input-width);
}
-ng-select,
+my-select-options,
+my-select-custom-value,
my-select-checkbox {
@include responsive-width($form-base-input-width);
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 48fb86968..a9f72d7db 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
@@ -37,12 +37,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
resolutions: { id: string, label: string, description?: string }[] = []
liveResolutions: { id: string, label: string, description?: string }[] = []
- concurrencyOptions: number[] = []
- transcodingThreadOptions: { label: string, value: number }[] = []
- liveMaxDurationOptions: { label: string, value: number }[] = []
- vodTranscodingProfileOptions: string[] = []
- liveTranscodingProfileOptions: string[] = []
+ transcodingThreadOptions: SelectOptionsItem[] = []
+ liveMaxDurationOptions: SelectOptionsItem[] = []
languageItems: SelectOptionsItem[] = []
categoryItems: SelectOptionsItem[] = []
@@ -99,23 +96,22 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
this.liveResolutions = this.resolutions.filter(r => r.id !== '0p')
this.transcodingThreadOptions = [
- { value: 0, label: $localize`Auto (via ffmpeg)` },
- { value: 1, label: '1' },
- { value: 2, label: '2' },
- { value: 4, label: '4' },
- { value: 8, label: '8' }
+ { 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.concurrencyOptions = [ 1, 2, 3, 4, 5, 6 ]
-
- this.vodTranscodingProfileOptions = [ 'default' ]
- this.liveTranscodingProfileOptions = [ 'default' ]
this.liveMaxDurationOptions = [
- { value: -1, label: $localize`No limit` },
- { value: 1000 * 3600, label: $localize`1 hour` },
- { value: 1000 * 3600 * 3, label: $localize`3 hours` },
- { value: 1000 * 3600 * 5, label: $localize`5 hours` },
- { value: 1000 * 3600 * 10, label: $localize`10 hours` }
+ { 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` }
]
}
@@ -137,11 +133,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
}
getAvailableTranscodingProfile (type: 'live' | 'vod') {
- if (type === 'live') {
- return this.serverConfig.live.transcoding.availableProfiles
- }
+ const profiles = type === 'live'
+ ? this.serverConfig.live.transcoding.availableProfiles
+ : this.serverConfig.transcoding.availableProfiles
- return this.serverConfig.transcoding.availableProfiles
+ return profiles.map(p => ({ id: p, label: p }))
}
getTotalTranscodingThreads () {
diff --git a/client/src/app/shared/shared-forms/select/index.ts b/client/src/app/shared/shared-forms/select/index.ts
index 33459b23b..e387e1f48 100644
--- a/client/src/app/shared/shared-forms/select/index.ts
+++ b/client/src/app/shared/shared-forms/select/index.ts
@@ -1,4 +1,5 @@
export * from './select-channel.component'
+export * from './select-checkbox.component'
+export * from './select-custom-value.component'
export * from './select-options.component'
export * from './select-tags.component'
-export * from './select-checkbox.component'
diff --git a/client/src/app/shared/shared-forms/select/select-custom-input.component.html b/client/src/app/shared/shared-forms/select/select-custom-input.component.html
deleted file mode 100644
index 84fa15c3d..000000000
--- a/client/src/app/shared/shared-forms/select/select-custom-input.component.html
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- {{ channel.label }}
-
-
-
-
diff --git a/client/src/app/shared/shared-forms/select/select-custom-input.component.scss b/client/src/app/shared/shared-forms/select/select-custom-input.component.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/client/src/app/shared/shared-forms/select/select-custom-input.component.ts b/client/src/app/shared/shared-forms/select/select-custom-input.component.ts
deleted file mode 100644
index ba6fef8ad..000000000
--- a/client/src/app/shared/shared-forms/select/select-custom-input.component.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Component, forwardRef, Input } from '@angular/core'
-import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
-
-@Component({
- selector: 'my-select-custom-input',
- styleUrls: [ './select-custom-input.component.scss' ],
- templateUrl: './select-custom-input.component.html',
- providers: [
- {
- provide: NG_VALUE_ACCESSOR,
- useExisting: forwardRef(() => SelectCustomInputComponent),
- multi: true
- }
- ]
-})
-export class SelectCustomInputComponent implements ControlValueAccessor {
- @Input() items: any[] = []
-
- selectedId: number
-
- // ng-select options
- bindLabel = 'label'
- bindValue = 'id'
- clearable = false
- searchable = false
-
- propagateChange = (_: any) => { /* empty */ }
-
- writeValue (id: number) {
- this.selectedId = id
- }
-
- registerOnChange (fn: (_: any) => void) {
- this.propagateChange = fn
- }
-
- registerOnTouched () {
- // Unused
- }
-
- onModelChange () {
- this.propagateChange(this.selectedId)
- }
-}
diff --git a/client/src/app/shared/shared-forms/select/select-custom-value.component.html b/client/src/app/shared/shared-forms/select/select-custom-value.component.html
new file mode 100644
index 000000000..5fdf432ff
--- /dev/null
+++ b/client/src/app/shared/shared-forms/select/select-custom-value.component.html
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/client/src/app/shared/shared-forms/select/select-custom-value.component.ts b/client/src/app/shared/shared-forms/select/select-custom-value.component.ts
new file mode 100644
index 000000000..a8e5ad0d3
--- /dev/null
+++ b/client/src/app/shared/shared-forms/select/select-custom-value.component.ts
@@ -0,0 +1,76 @@
+import { Component, forwardRef, Input, OnChanges } from '@angular/core'
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
+import { SelectOptionsItem } from './select-options.component'
+
+@Component({
+ selector: 'my-select-custom-value',
+ styleUrls: [ './select-shared.component.scss' ],
+ templateUrl: './select-custom-value.component.html',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SelectCustomValueComponent),
+ multi: true
+ }
+ ]
+})
+export class SelectCustomValueComponent implements ControlValueAccessor, OnChanges {
+ @Input() items: SelectOptionsItem[] = []
+ @Input() clearable = false
+ @Input() searchable = false
+ @Input() groupBy: string
+ @Input() labelForId: string
+
+ customValue: number | string = ''
+ selectedId: number | string
+
+ itemsWithCustom: SelectOptionsItem[] = []
+
+ ngOnChanges () {
+ this.itemsWithCustom = this.getItems()
+ }
+
+ propagateChange = (_: any) => { /* empty */ }
+
+ writeValue (id: number | string) {
+ this.selectedId = id
+
+ if (this.isSelectedIdInItems() !== true) {
+ this.selectedId = 'other'
+ this.customValue = id
+ }
+ }
+
+ registerOnChange (fn: (_: any) => void) {
+ this.propagateChange = fn
+ }
+
+ registerOnTouched () {
+ // Unused
+ }
+
+ onModelChange () {
+ if (this.selectedId === 'other') {
+ return this.propagateChange(this.customValue)
+ }
+
+ return this.propagateChange(this.selectedId)
+ }
+
+ isSelectedIdInItems () {
+ return !!this.items.find(i => i.id === this.selectedId)
+ }
+
+ getItems () {
+ const other: SelectOptionsItem = {
+ id: 'other',
+ label: $localize`Custom value...`
+ }
+
+ return this.items.concat([ other ])
+ }
+
+ isCustomValue () {
+ return this.selectedId === 'other'
+ }
+}
diff --git a/client/src/app/shared/shared-forms/select/select-options.component.html b/client/src/app/shared/shared-forms/select/select-options.component.html
index 6d0d7e925..3b1761255 100644
--- a/client/src/app/shared/shared-forms/select/select-options.component.html
+++ b/client/src/app/shared/shared-forms/select/select-options.component.html
@@ -6,6 +6,7 @@
[clearable]="clearable"
[labelForId]="labelForId"
[searchable]="searchable"
+ [searchFn]="searchFn"
bindLabel="label"
bindValue="id"
diff --git a/client/src/app/shared/shared-forms/select/select-options.component.ts b/client/src/app/shared/shared-forms/select/select-options.component.ts
index f0abd1a68..51a3f515d 100644
--- a/client/src/app/shared/shared-forms/select/select-options.component.ts
+++ b/client/src/app/shared/shared-forms/select/select-options.component.ts
@@ -27,6 +27,7 @@ export class SelectOptionsComponent implements ControlValueAccessor {
@Input() searchable = false
@Input() groupBy: string
@Input() labelForId: string
+ @Input() searchFn: any
selectedId: number | string
diff --git a/client/src/app/shared/shared-forms/select/select-shared.component.scss b/client/src/app/shared/shared-forms/select/select-shared.component.scss
index 0b4c6b784..1a4192b55 100644
--- a/client/src/app/shared/shared-forms/select/select-shared.component.scss
+++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss
@@ -30,3 +30,18 @@ ng-select ::ng-deep {
width: 20px;
}
}
+
+.root {
+ display:flex;
+
+ > my-select-options {
+ flex-grow: 1;
+ }
+}
+
+input[type=text] {
+ margin-left: 5px;
+
+ @include peertube-input-text($form-base-input-width);
+ display: block;
+}
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts
index 22e8dd05a..9bdd138a1 100644
--- a/client/src/app/shared/shared-forms/shared-form.module.ts
+++ b/client/src/app/shared/shared-forms/shared-form.module.ts
@@ -7,13 +7,19 @@ import { SharedGlobalIconModule } from '../shared-icons'
import { SharedMainModule } from '../shared-main/shared-main.module'
import { DynamicFormFieldComponent } from './dynamic-form-field.component'
import { FormValidatorService } from './form-validator.service'
-import { InputToggleHiddenComponent } from './input-toggle-hidden.component'
import { InputSwitchComponent } from './input-switch.component'
+import { InputToggleHiddenComponent } from './input-toggle-hidden.component'
import { MarkdownTextareaComponent } from './markdown-textarea.component'
import { PeertubeCheckboxComponent } from './peertube-checkbox.component'
import { PreviewUploadComponent } from './preview-upload.component'
import { ReactiveFileComponent } from './reactive-file.component'
-import { SelectChannelComponent, SelectCheckboxComponent, SelectOptionsComponent, SelectTagsComponent } from './select'
+import {
+ SelectChannelComponent,
+ SelectCheckboxComponent,
+ SelectCustomValueComponent,
+ SelectOptionsComponent,
+ SelectTagsComponent
+} from './select'
import { TextareaAutoResizeDirective } from './textarea-autoresize.directive'
import { TimestampInputComponent } from './timestamp-input.component'
@@ -44,6 +50,7 @@ import { TimestampInputComponent } from './timestamp-input.component'
SelectOptionsComponent,
SelectTagsComponent,
SelectCheckboxComponent,
+ SelectCustomValueComponent,
DynamicFormFieldComponent
],
@@ -69,6 +76,7 @@ import { TimestampInputComponent } from './timestamp-input.component'
SelectOptionsComponent,
SelectTagsComponent,
SelectCheckboxComponent,
+ SelectCustomValueComponent,
DynamicFormFieldComponent
],