X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=client%2Fsrc%2Fapp%2F%2Bvideos%2F%2Bvideo-edit%2Fshared%2Fvideo-edit.component.ts;h=366c93a7936ac26d22185675e6ee31372df18308;hb=9df52d660feb722404be00a50f3c8a612bec1c15;hp=92d06aa12414c58f44948a0e03d2cd3d9bb70c27;hpb=7ed1edbbe4ffbef28093e4f5630751cb652814e4;p=github%2FChocobozzz%2FPeerTube.git diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts index 92d06aa12..366c93a79 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts @@ -1,8 +1,9 @@ import { forkJoin } from 'rxjs' import { map } from 'rxjs/operators' -import { Component, Input, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' -import { FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms' -import { ServerService } from '@app/core' +import { SelectChannelItem } from 'src/types/select-options-item.model' +import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' +import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms' +import { HooksService, PluginService, ServerService } from '@app/core' import { removeElementFromArray } from '@app/helpers' import { VIDEO_CATEGORY_VALIDATOR, @@ -17,14 +18,27 @@ import { VIDEO_SUPPORT_VALIDATOR, VIDEO_TAGS_ARRAY_VALIDATOR } from '@app/shared/form-validators/video-validators' -import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem } from '@app/shared/shared-forms' +import { FormReactiveValidationMessages, FormValidatorService } from '@app/shared/shared-forms' import { InstanceService } from '@app/shared/shared-instance' import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' -import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models' +import { + HTMLServerConfig, + LiveVideo, + RegisterClientFormFieldOptions, + RegisterClientVideoFieldOptions, + VideoConstant, + VideoDetails, + VideoPrivacy +} from '@shared/models' import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' +import { VideoEditType } from './video-edit.type' type VideoLanguages = VideoConstant & { group?: string } +type PluginField = { + commonOptions: RegisterClientFormFieldOptions + videoFormOptions: RegisterClientVideoFieldOptions +} @Component({ selector: 'my-video-edit', @@ -35,13 +49,22 @@ export class VideoEditComponent implements OnInit, OnDestroy { @Input() form: FormGroup @Input() formErrors: { [ id: string ]: string } = {} @Input() validationMessages: FormReactiveValidationMessages = {} + + @Input() videoToUpdate: VideoDetails + @Input() userVideoChannels: SelectChannelItem[] = [] @Input() schedulePublicationPossible = true + @Input() videoCaptions: (VideoCaptionEdit & { captionPath?: string })[] = [] + @Input() waitTranscodingEnabled = true + @Input() type: VideoEditType + @Input() liveVideo: LiveVideo @ViewChild('videoCaptionAddModal', { static: true }) videoCaptionAddModal: VideoCaptionAddModalComponent + @Output() pluginFieldsAdded = new EventEmitter() + // So that it can be accessed in the template readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY @@ -50,8 +73,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { videoLicences: VideoConstant[] = [] videoLanguages: VideoLanguages[] = [] - tagValidators: ValidatorFn[] - tagValidatorsMessages: { [ name: string ]: string } + pluginDataFormGroup: FormGroup schedulePublicationEnabled = false @@ -62,7 +84,9 @@ export class VideoEditComponent implements OnInit, OnDestroy { calendarTimezone: string calendarDateFormat: string - serverConfig: ServerConfig + serverConfig: HTMLServerConfig + + pluginFields: PluginField[] = [] private schedulerInterval: any private firstPatchDone = false @@ -72,21 +96,16 @@ export class VideoEditComponent implements OnInit, OnDestroy { private formValidatorService: FormValidatorService, private videoService: VideoService, private serverService: ServerService, + private pluginService: PluginService, private instanceService: InstanceService, private i18nPrimengCalendarService: I18nPrimengCalendarService, - private ngZone: NgZone + private ngZone: NgZone, + private hooks: HooksService ) { - this.calendarLocale = this.i18nPrimengCalendarService.getCalendarLocale() this.calendarTimezone = this.i18nPrimengCalendarService.getTimezone() this.calendarDateFormat = this.i18nPrimengCalendarService.getDateFormat() } - get existingCaptions () { - return this.videoCaptions - .filter(c => c.action !== 'REMOVE') - .map(c => c.language.id) - } - updateForm () { const defaultValues: any = { nsfw: 'false', @@ -111,7 +130,10 @@ export class VideoEditComponent implements OnInit, OnDestroy { previewfile: null, support: VIDEO_SUPPORT_VALIDATOR, schedulePublicationAt: VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR, - originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR + originallyPublishedAt: VIDEO_ORIGINALLY_PUBLISHED_AT_VALIDATOR, + liveStreamKey: null, + permanentLive: null, + saveReplay: null } this.formValidatorService.updateForm( @@ -131,30 +153,39 @@ export class VideoEditComponent implements OnInit, OnDestroy { this.trackChannelChange() this.trackPrivacyChange() + this.trackLivePermanentFieldChange() } ngOnInit () { this.updateForm() + this.pluginService.ensurePluginsAreLoaded('video-edit') + .then(() => this.updatePluginFields()) + this.serverService.getVideoCategories() .subscribe(res => this.videoCategories = res) + this.serverService.getVideoLicences() .subscribe(res => this.videoLicences = res) + forkJoin([ this.instanceService.getAbout(), this.serverService.getVideoLanguages() ]).pipe(map(([ about, languages ]) => ({ about, languages }))) .subscribe(res => { this.videoLanguages = res.languages - .map(l => res.about.instance.languages.includes(l.id) - ? { ...l, group: $localize`Instance languages`, groupOrder: 0 } - : { ...l, group: $localize`All languages`, groupOrder: 1 }) + .map(l => { + return res.about.instance.languages.includes(l.id) + ? { ...l, group: $localize`Instance languages`, groupOrder: 0 } + : { ...l, group: $localize`All languages`, groupOrder: 1 } + }) .sort((a, b) => a.groupOrder - b.groupOrder) }) this.serverService.getVideoPrivacies() .subscribe(privacies => { - this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies) + this.videoPrivacies = this.videoService.explainedPrivacyLabels(privacies).videoPrivacies + if (this.schedulePublicationPossible) { this.videoPrivacies.push({ id: this.SPECIAL_SCHEDULED_PRIVACY, @@ -164,21 +195,27 @@ export class VideoEditComponent implements OnInit, OnDestroy { } }) - this.serverConfig = this.serverService.getTmpConfig() - this.serverService.getConfig() - .subscribe(config => this.serverConfig = config) + this.serverConfig = this.serverService.getHTMLConfig() this.initialVideoCaptions = this.videoCaptions.map(c => c.language.id) this.ngZone.runOutsideAngular(() => { this.schedulerInterval = setInterval(() => this.minScheduledDate = new Date(), 1000 * 60) // Update every minute }) + + this.hooks.runAction('action:video-edit.init', 'video-edit', { type: this.type }) } ngOnDestroy () { if (this.schedulerInterval) clearInterval(this.schedulerInterval) } + getExistingCaptions () { + return this.videoCaptions + .filter(c => c.action !== 'REMOVE') + .map(c => c.language.id) + } + onCaptionAdded (caption: VideoCaptionEdit) { const existingCaption = this.videoCaptions.find(c => c.language.id === caption.language.id) @@ -196,7 +233,7 @@ export class VideoEditComponent implements OnInit, OnDestroy { async deleteCaption (caption: VideoCaptionEdit) { // Caption recovers his former state - if (caption.action && this.initialVideoCaptions.indexOf(caption.language.id) !== -1) { + if (caption.action && this.initialVideoCaptions.includes(caption.language.id)) { caption.action = undefined return } @@ -214,6 +251,24 @@ export class VideoEditComponent implements OnInit, OnDestroy { this.videoCaptionAddModal.show() } + isSaveReplayEnabled () { + return this.serverConfig.live.allowReplay + } + + isPermanentLiveEnabled () { + return this.form.value['permanentLive'] === true + } + + isPluginFieldHidden (pluginField: PluginField) { + if (typeof pluginField.commonOptions.hidden !== 'function') return false + + return pluginField.commonOptions.hidden({ + formValues: this.form.value, + videoToUpdate: this.videoToUpdate, + liveVideo: this.liveVideo + }) + } + private sortVideoCaptions () { this.videoCaptions.sort((v1, v2) => { if (v1.language.label < v2.language.label) return -1 @@ -223,9 +278,26 @@ export class VideoEditComponent implements OnInit, OnDestroy { }) } + private updatePluginFields () { + this.pluginFields = this.pluginService.getRegisteredVideoFormFields(this.type) + + if (this.pluginFields.length === 0) return + + const obj: any = {} + + for (const setting of this.pluginFields) { + obj[setting.commonOptions.name] = new FormControl(setting.commonOptions.default) + } + + this.pluginDataFormGroup = new FormGroup(obj) + this.form.addControl('pluginData', this.pluginDataFormGroup) + + this.pluginFieldsAdded.emit() + } + private trackPrivacyChange () { // We will update the schedule input and the wait transcoding checkbox validators - this.form.controls[ 'privacy' ] + this.form.controls['privacy'] .valueChanges .pipe(map(res => parseInt(res.toString(), 10))) .subscribe( @@ -264,12 +336,12 @@ export class VideoEditComponent implements OnInit, OnDestroy { private trackChannelChange () { // We will update the "support" field depending on the channel - this.form.controls[ 'channelId' ] + this.form.controls['channelId'] .valueChanges .pipe(map(res => parseInt(res.toString(), 10))) .subscribe( newChannelId => { - const oldChannelId = parseInt(this.form.value[ 'channelId' ], 10) + const oldChannelId = parseInt(this.form.value['channelId'], 10) // Not initialized yet if (isNaN(newChannelId)) return @@ -278,10 +350,15 @@ export class VideoEditComponent implements OnInit, OnDestroy { // Wait support field update setTimeout(() => { - const currentSupport = this.form.value[ 'support' ] + const currentSupport = this.form.value['support'] // First time we set the channel? - if (isNaN(oldChannelId) && !currentSupport) return this.updateSupportField(newChannel.support) + if (isNaN(oldChannelId)) { + // Fill support if it's empty + if (!currentSupport) this.updateSupportField(newChannel.support) + + return + } const oldChannel = this.userVideoChannels.find(c => c.id === oldChannelId) if (!newChannel || !oldChannel) { @@ -300,6 +377,24 @@ export class VideoEditComponent implements OnInit, OnDestroy { ) } + private trackLivePermanentFieldChange () { + // We will update the "support" field depending on the channel + this.form.controls['permanentLive'] + .valueChanges + .subscribe( + permanentLive => { + const saveReplayControl = this.form.controls['saveReplay'] + + if (permanentLive === true) { + saveReplayControl.setValue(false) + saveReplayControl.disable() + } else { + saveReplayControl.enable() + } + } + ) + } + private updateSupportField (support: string) { return this.form.patchValue({ support: support || '' }) }