<span i18n>allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart.</span>
</span>
- <div class="peertube-select-container">
- <select id="importConcurrency" formControlName="concurrency" class="form-control">
- <option *ngFor="let option of concurrencyOptions" [value]="option">
- {{ option }}
- </option>
- </select>
+ <div class="number-with-unit">
+ <input type="number" name="importConcurrency" formControlName="concurrency" />
+ <span i18n>jobs in parallel</span>
</div>
+
<div *ngIf="formErrors.import.concurrency" class="form-error">{{ formErrors.import.concurrency }}</div>
</div>
<ng-container *ngIf="!getTotalTranscodingThreads().atMost" i18n>will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding</ng-container>
</span>
- <div class="peertube-select-container">
- <select id="transcodingThreads" formControlName="threads" class="form-control">
- <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
- {{ transcodingThreadOption.label }} {transcodingThreadOption.value, plural, =0 {} =1 {thread} other {threads}}
- </option>
- </select>
- </div>
+ <my-select-custom-value
+ id="transcodingThreads"
+ [items]="transcodingThreadOptions"
+ formControlName="threads"
+ [clearable]="false"
+ ></my-select-custom-value>
+
<div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
</div>
<span i18n>allows to transcode multiple files in parallel. ⚠️ Requires a PeerTube restart.</span>
</span>
- <div class="peertube-select-container">
- <select id="transcodingConcurrency" formControlName="concurrency" class="form-control">
- <option *ngFor="let option of concurrencyOptions" [value]="option">
- {{ option }}
- </option>
- </select>
+ <div class="number-with-unit">
+ <input type="number" name="transcodingConcurrency" formControlName="concurrency" />
+ <span i18n>jobs in parallel</span>
</div>
+
<div *ngIf="formErrors.transcoding.concurrency" class="form-error">{{ formErrors.transcoding.concurrency }}</div>
</div>
<label i18n for="transcodingProfile">Transcoding profile</label>
<span class="text-muted ml-1" i18n>new transcoding profiles can be added by PeerTube plugins</span>
- <ng-select
+ <my-select-options
id="transcodingProfile"
formControlName="profile"
[items]="getAvailableTranscodingProfile('vod')"
<span class="text-muted" i18n>x264, targeting maximum device compatibility</span>
</ng-container>
</ng-template>
- </ng-select>
+ </my-select-options>
<div *ngIf="formErrors.transcoding.profile" class="form-error">{{ formErrors.transcoding.profile }}</div>
</div>
<div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isLiveEnabled() }">
<label i18n for="liveMaxDuration">Max live duration</label>
- <ng-select
+ <my-select-options
labelForId="liveMaxDuration" [items]="liveMaxDurationOptions" formControlName="maxDuration"
- bindLabel="label" bindValue="value" [clearable]="false" [searchable]="false"
- ></ng-select>
+ bindLabel="label" bindValue="value" [clearable]="false" [searchable]="true"
+ ></my-select-options>
</div>
</ng-container>
<ng-container *ngIf="!getTotalTranscodingThreads().atMost" i18n>will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding</ng-container>
</span>
- <div class="peertube-select-container">
- <select id="liveTranscodingThreads" formControlName="threads" class="form-control">
- <option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
- {{ transcodingThreadOption.label }} {transcodingThreadOption.value, plural, =0 {} =1 {thread} other {threads}}
- </option>
- </select>
- </div>
+ <my-select-custom-value
+ id="liveTranscodingThreads"
+ [items]="transcodingThreadOptions"
+ formControlName="threads"
+ [clearable]="false"
+ ></my-select-custom-value>
<div *ngIf="formErrors.live.transcoding.threads" class="form-error">{{ formErrors.live.transcoding.threads }}</div>
</div>
<label i18n for="liveTranscodingProfile">Live transcoding profile</label>
<span class="text-muted ml-1" i18n>new live transcoding profiles can be added by PeerTube plugins</span>
- <ng-select
+ <my-select-options
id="liveTranscodingProfile"
formControlName="profile"
[items]="getAvailableTranscodingProfile('live')"
<span class="text-muted" i18n>x264, targeting maximum device compatibility</span>
</ng-container>
</ng-template>
- </ng-select>
+ </my-select-options>
<div *ngIf="formErrors.live.transcoding.profile" class="form-error">{{ formErrors.live.transcoding.profile }}</div>
</div>
@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);
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[] = []
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` }
]
}
}
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 () {
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'
+++ /dev/null
-<ng-select
- [(ngModel)]="selectedId"
- (ngModelChange)="onModelChange()"
- [bindLabel]="bindLabel"
- [bindValue]="bindValue"
- [clearable]="clearable"
- [searchable]="searchable"
->
- <ng-option *ngFor="let item of items" [value]="item.id">
- {{ channel.label }}
- </ng-option>
-
- <ng-content></ng-content>
-</ng-select>
+++ /dev/null
-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)
- }
-}
--- /dev/null
+<div class="root">
+ <my-select-options
+ [items]="itemsWithCustom"
+ [clearable]="clearable"
+ [searchable]="searchable"
+ [groupBy]="groupBy"
+ [labelForId]="labelForId"
+
+ [(ngModel)]="selectedId"
+ (ngModelChange)="onModelChange()"
+ ></my-select-options>
+
+ <input *ngIf="isCustomValue()" [(ngModel)]="customValue" (ngModelChange)="onModelChange()" type="text" class="form-control" />
+</div>
--- /dev/null
+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'
+ }
+}
[clearable]="clearable"
[labelForId]="labelForId"
[searchable]="searchable"
+ [searchFn]="searchFn"
bindLabel="label"
bindValue="id"
@Input() searchable = false
@Input() groupBy: string
@Input() labelForId: string
+ @Input() searchFn: any
selectedId: number | string
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;
+}
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'
SelectOptionsComponent,
SelectTagsComponent,
SelectCheckboxComponent,
+ SelectCustomValueComponent,
DynamicFormFieldComponent
],
SelectOptionsComponent,
SelectTagsComponent,
SelectCheckboxComponent,
+ SelectCustomValueComponent,
DynamicFormFieldComponent
],