From 7294aab0c879ef96c0fde15c389a2c4c1463d3c7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 20 Aug 2020 16:18:16 +0200 Subject: [PATCH] Add ability to set custom field to video form --- .../plugin-show-installed.component.html | 33 +----------- .../plugin-show-installed.component.scss | 16 ------ .../shared/video-edit.component.html | 15 ++++++ .../shared/video-edit.component.scss | 3 +- .../shared/video-edit.component.ts | 51 ++++++++++++++++--- .../video-import-torrent.component.html | 1 + .../video-import-url.component.html | 1 + .../video-upload.component.html | 1 + .../+video-edit/video-update.component.html | 3 +- .../+video-edit/video-update.component.ts | 8 +++ client/src/app/core/plugins/plugin.service.ts | 17 ++++++- .../dynamic-form-field.component.html | 35 +++++++++++++ .../dynamic-form-field.component.scss | 18 +++++++ .../dynamic-form-field.component.ts | 15 ++++++ .../shared/shared-forms/shared-form.module.ts | 9 +++- .../shared-main/video/video-edit.model.ts | 6 ++- .../shared/shared-main/video/video.model.ts | 4 ++ .../shared/shared-main/video/video.service.ts | 1 + client/src/root-helpers/plugins.ts | 41 +++++++++++++-- client/src/standalone/videos/embed.ts | 6 ++- .../src/types/register-client-option.model.ts | 3 ++ server/controllers/api/videos/index.ts | 2 +- server/models/video/video-format-utils.ts | 5 +- shared/models/plugins/client-hook.model.ts | 3 ++ shared/models/plugins/index.ts | 1 + .../plugins/plugin-client-scope.type.ts | 2 +- .../register-client-form-field.model.ts | 12 +++++ .../plugins/register-server-setting.model.ts | 9 +--- shared/models/videos/video-update.model.ts | 2 + shared/models/videos/video.model.ts | 2 + 30 files changed, 249 insertions(+), 76 deletions(-) create mode 100644 client/src/app/shared/shared-forms/dynamic-form-field.component.html create mode 100644 client/src/app/shared/shared-forms/dynamic-form-field.component.scss create mode 100644 client/src/app/shared/shared-forms/dynamic-form-field.component.ts create mode 100644 shared/models/plugins/register-client-form-field.model.ts diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html index f3fc429ff..cb2894568 100644 --- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html @@ -7,38 +7,7 @@
- - - - - - - - - - - - - - - - -
- {{ formErrors[setting.name] }} -
+
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss index cc35aec57..5ab6e5f1b 100644 --- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.scss @@ -5,22 +5,6 @@ h2 { margin-bottom: 20px; } -input:not([type=submit]) { - @include peertube-input-text(340px); - - display: block; -} - -textarea { - @include peertube-textarea(340px, 200px); - - display: block; -} - -.peertube-select-container { - @include peertube-select-container(340px); -} - input[type=submit], button { @include peertube-button; @include orange-button; diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.html b/client/src/app/+videos/+video-edit/shared/video-edit.component.html index ae3413e79..842997b20 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.html @@ -265,6 +265,21 @@ + + Plugin settings + + +
+ +
+
+ +
+
+ +
+
+
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss index 9caf009c5..3082a4f72 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.scss @@ -7,7 +7,8 @@ @import 'variables'; @import 'mixins'; -label { +label, +my-dynamic-form-field ::ng-deep label { font-weight: $font-regular; font-size: 100%; } 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..f04111e69 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,8 @@ import { forkJoin } from 'rxjs' import { map } from 'rxjs/operators' -import { Component, Input, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' import { FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms' -import { ServerService } from '@app/core' +import { HooksService, PluginService, ServerService } from '@app/core' import { removeElementFromArray } from '@app/helpers' import { VIDEO_CATEGORY_VALIDATOR, @@ -21,6 +21,7 @@ import { FormReactiveValidationMessages, FormValidatorService, SelectChannelItem import { InstanceService } from '@app/shared/shared-instance' import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' import { ServerConfig, VideoConstant, VideoPrivacy } from '@shared/models' +import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' @@ -39,9 +40,12 @@ export class VideoEditComponent implements OnInit, OnDestroy { @Input() schedulePublicationPossible = true @Input() videoCaptions: (VideoCaptionEdit & { captionPath?: string })[] = [] @Input() waitTranscodingEnabled = true + @Input() type: 'import-url' | 'import-torrent' | 'upload' | 'update' @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 @@ -53,6 +57,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { tagValidators: ValidatorFn[] tagValidatorsMessages: { [ name: string ]: string } + pluginDataFormGroup: FormGroup + schedulePublicationEnabled = false calendarLocale: any = {} @@ -64,6 +70,11 @@ export class VideoEditComponent implements OnInit, OnDestroy { serverConfig: ServerConfig + pluginFields: { + commonOptions: RegisterClientFormFieldOptions + videoFormOptions: RegisterClientVideoFieldOptions + }[] = [] + private schedulerInterval: any private firstPatchDone = false private initialVideoCaptions: string[] = [] @@ -72,9 +83,11 @@ 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() @@ -136,19 +149,26 @@ export class VideoEditComponent implements OnInit, OnDestroy { 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) }) @@ -173,6 +193,8 @@ export class VideoEditComponent implements OnInit, OnDestroy { 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 () { @@ -223,6 +245,23 @@ 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' ] diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html index 825cb6df4..785514c76 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html @@ -58,6 +58,7 @@
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.html index 2107dc9d0..3e4eb5fbc 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.html +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.html @@ -54,6 +54,7 @@
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html index ed697c25b..677fa1197 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html @@ -69,6 +69,7 @@ [form]="form" [formErrors]="formErrors" [videoCaptions]="videoCaptions" [validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels" [waitTranscodingEnabled]="waitTranscodingEnabled" + type="upload" >
diff --git a/client/src/app/+videos/+video-edit/video-update.component.html b/client/src/app/+videos/+video-edit/video-update.component.html index 6c1239395..b37596399 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.html +++ b/client/src/app/+videos/+video-edit/video-update.component.html @@ -10,11 +10,12 @@ [form]="form" [formErrors]="formErrors" [schedulePublicationPossible]="schedulePublicationPossible" [validationMessages]="validationMessages" [userVideoChannels]="userVideoChannels" [videoCaptions]="videoCaptions" [waitTranscodingEnabled]="waitTranscodingEnabled" + type="update" (pluginFieldsAdded)="hydratePluginFieldsFromVideo()" >
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts index 2e1d0f89d..20438a2d3 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.ts +++ b/client/src/app/+videos/+video-edit/video-update.component.ts @@ -126,6 +126,14 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { ) } + hydratePluginFieldsFromVideo () { + if (!this.video.pluginData) return + + this.form.patchValue({ + pluginData: this.video.pluginData + }) + } + private hydrateFormFromVideo () { this.form.patchValue(this.video.toFormPatch()) diff --git a/client/src/app/core/plugins/plugin.service.ts b/client/src/app/core/plugins/plugin.service.ts index 871613b89..4e44a1865 100644 --- a/client/src/app/core/plugins/plugin.service.ts +++ b/client/src/app/core/plugins/plugin.service.ts @@ -9,7 +9,7 @@ import { RestExtractor } from '@app/core/rest' import { ServerService } from '@app/core/server/server.service' import { getDevLocale, isOnDevLocale } from '@app/helpers' import { CustomModalComponent } from '@app/modal/custom-modal.component' -import { Hooks, loadPlugin, PluginInfo, runHook } from '@root-helpers/plugins' +import { FormFields, Hooks, loadPlugin, PluginInfo, runHook } from '@root-helpers/plugins' import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' import { ClientHook, @@ -36,6 +36,7 @@ export class PluginService implements ClientHook { 'video-watch': new ReplaySubject(1), signup: new ReplaySubject(1), login: new ReplaySubject(1), + 'video-edit': new ReplaySubject(1), embed: new ReplaySubject(1) } @@ -50,6 +51,9 @@ export class PluginService implements ClientHook { private loadingScopes: { [id in PluginClientScope]?: boolean } = {} private hooks: Hooks = {} + private formFields: FormFields = { + video: [] + } constructor ( private authService: AuthService, @@ -188,9 +192,18 @@ export class PluginService implements ClientHook { : PluginType.THEME } + getRegisteredVideoFormFields (type: 'import-url' | 'import-torrent' | 'upload' | 'update') { + return this.formFields.video.filter(f => f.videoFormOptions.type === type) + } + private loadPlugin (pluginInfo: PluginInfo) { return this.zone.runOutsideAngular(() => { - return loadPlugin(this.hooks, pluginInfo, pluginInfo => this.buildPeerTubeHelpers(pluginInfo)) + return loadPlugin({ + hooks: this.hooks, + formFields: this.formFields, + pluginInfo, + peertubeHelpersFactory: pluginInfo => this.buildPeerTubeHelpers(pluginInfo) + }) }) } diff --git a/client/src/app/shared/shared-forms/dynamic-form-field.component.html b/client/src/app/shared/shared-forms/dynamic-form-field.component.html new file mode 100644 index 000000000..c111ea7df --- /dev/null +++ b/client/src/app/shared/shared-forms/dynamic-form-field.component.html @@ -0,0 +1,35 @@ +
+ + + + + + + + + + + + + + + + +
+ {{ formErrors[setting.name] }} +
+ +
diff --git a/client/src/app/shared/shared-forms/dynamic-form-field.component.scss b/client/src/app/shared/shared-forms/dynamic-form-field.component.scss new file mode 100644 index 000000000..70b3cf6c3 --- /dev/null +++ b/client/src/app/shared/shared-forms/dynamic-form-field.component.scss @@ -0,0 +1,18 @@ +@import '_variables'; +@import '_mixins'; + +input:not([type=submit]) { + @include peertube-input-text(340px); + + display: block; +} + +textarea { + @include peertube-textarea(340px, 200px); + + display: block; +} + +.peertube-select-container { + @include peertube-select-container(340px); +} diff --git a/client/src/app/shared/shared-forms/dynamic-form-field.component.ts b/client/src/app/shared/shared-forms/dynamic-form-field.component.ts new file mode 100644 index 000000000..b63890797 --- /dev/null +++ b/client/src/app/shared/shared-forms/dynamic-form-field.component.ts @@ -0,0 +1,15 @@ +import { Component, Input } from '@angular/core' +import { FormGroup } from '@angular/forms' +import { RegisterClientFormFieldOptions } from '@shared/models' + +@Component({ + selector: 'my-dynamic-form-field', + templateUrl: './dynamic-form-field.component.html', + styleUrls: [ './dynamic-form-field.component.scss' ] +}) + +export class DynamicFormFieldComponent { + @Input() form: FormGroup + @Input() formErrors: any + @Input() setting: RegisterClientFormFieldOptions +} 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 1946ac21f..a28988f87 100644 --- a/client/src/app/shared/shared-forms/shared-form.module.ts +++ b/client/src/app/shared/shared-forms/shared-form.module.ts @@ -15,6 +15,7 @@ import { ReactiveFileComponent } from './reactive-file.component' import { SelectChannelComponent, SelectCheckboxComponent, SelectOptionsComponent, SelectTagsComponent } from './select' import { TextareaAutoResizeDirective } from './textarea-autoresize.directive' import { TimestampInputComponent } from './timestamp-input.component' +import { DynamicFormFieldComponent } from './dynamic-form-field.component' @NgModule({ imports: [ @@ -41,7 +42,9 @@ import { TimestampInputComponent } from './timestamp-input.component' SelectChannelComponent, SelectOptionsComponent, SelectTagsComponent, - SelectCheckboxComponent + SelectCheckboxComponent, + + DynamicFormFieldComponent ], exports: [ @@ -63,7 +66,9 @@ import { TimestampInputComponent } from './timestamp-input.component' SelectChannelComponent, SelectOptionsComponent, SelectTagsComponent, - SelectCheckboxComponent + SelectCheckboxComponent, + + DynamicFormFieldComponent ], providers: [ diff --git a/client/src/app/shared/shared-main/video/video-edit.model.ts b/client/src/app/shared/shared-main/video/video-edit.model.ts index 6a529e052..757b686c0 100644 --- a/client/src/app/shared/shared-main/video/video-edit.model.ts +++ b/client/src/app/shared/shared-main/video/video-edit.model.ts @@ -25,6 +25,8 @@ export class VideoEdit implements VideoUpdate { scheduleUpdate?: VideoScheduleUpdate originallyPublishedAt?: Date | string + pluginData?: any + constructor ( video?: Video & { tags: string[], @@ -55,10 +57,12 @@ export class VideoEdit implements VideoUpdate { this.scheduleUpdate = video.scheduledUpdate this.originallyPublishedAt = video.originallyPublishedAt ? new Date(video.originallyPublishedAt) : null + + this.pluginData = video.pluginData } } - patch (values: { [ id: string ]: string }) { + patch (values: { [ id: string ]: any }) { Object.keys(values).forEach((key) => { this[ key ] = values[ key ] }) diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts index 73f0198e2..0dca3da0d 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts @@ -84,6 +84,8 @@ export class Video implements VideoServerModel { currentTime: number } + pluginData?: any + static buildClientUrl (videoUUID: string) { return '/videos/watch/' + videoUUID } @@ -152,6 +154,8 @@ export class Video implements VideoServerModel { this.originInstanceHost = this.account.host this.originInstanceUrl = 'https://' + this.originInstanceHost + + this.pluginData = hash.pluginData } isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { diff --git a/client/src/app/shared/shared-main/video/video.service.ts b/client/src/app/shared/shared-main/video/video.service.ts index 48aff82b4..8a688c8ed 100644 --- a/client/src/app/shared/shared-main/video/video.service.ts +++ b/client/src/app/shared/shared-main/video/video.service.ts @@ -96,6 +96,7 @@ export class VideoService implements VideosProvider { downloadEnabled: video.downloadEnabled, thumbnailfile: video.thumbnailfile, previewfile: video.previewfile, + pluginData: video.pluginData, scheduleUpdate, originallyPublishedAt } diff --git a/client/src/root-helpers/plugins.ts b/client/src/root-helpers/plugins.ts index 011721761..4bc2c8eb2 100644 --- a/client/src/root-helpers/plugins.ts +++ b/client/src/root-helpers/plugins.ts @@ -1,6 +1,14 @@ -import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' -import { ClientHookName, ClientScript, RegisterClientHookOptions, ServerConfigPlugin, PluginType, clientHookObject } from '../../../shared/models' import { RegisterClientHelpers } from 'src/types/register-client-option.model' +import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' +import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' +import { + ClientHookName, + clientHookObject, + ClientScript, + PluginType, + RegisterClientHookOptions, + ServerConfigPlugin +} from '../../../shared/models' import { ClientScript as ClientScriptModule } from '../types/client-script.model' import { importModule } from './utils' @@ -18,6 +26,13 @@ type PluginInfo = { isTheme: boolean } +type FormFields = { + video: { + commonOptions: RegisterClientFormFieldOptions + videoFormOptions: RegisterClientVideoFieldOptions + }[] +} + async function runHook (hooks: Hooks, hookName: ClientHookName, result?: T, params?: any) { if (!hooks[hookName]) return result @@ -34,7 +49,13 @@ async function runHook (hooks: Hooks, hookName: ClientHookName, result?: T, p return result } -function loadPlugin (hooks: Hooks, pluginInfo: PluginInfo, peertubeHelpersFactory: (pluginInfo: PluginInfo) => RegisterClientHelpers) { +function loadPlugin (options: { + hooks: Hooks + pluginInfo: PluginInfo + peertubeHelpersFactory: (pluginInfo: PluginInfo) => RegisterClientHelpers + formFields?: FormFields +}) { + const { hooks, pluginInfo, peertubeHelpersFactory, formFields } = options const { plugin, clientScript } = pluginInfo const registerHook = (options: RegisterClientHookOptions) => { @@ -54,12 +75,23 @@ function loadPlugin (hooks: Hooks, pluginInfo: PluginInfo, peertubeHelpersFactor }) } + const registerVideoField = (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => { + if (!formFields) { + throw new Error('Video field registration is not supported') + } + + formFields.video.push({ + commonOptions, + videoFormOptions + }) + } + const peertubeHelpers = peertubeHelpersFactory(pluginInfo) console.log('Loading script %s of plugin %s.', clientScript.script, plugin.name) return importModule(clientScript.script) - .then((script: ClientScriptModule) => script.register({ registerHook, peertubeHelpers })) + .then((script: ClientScriptModule) => script.register({ registerHook, registerVideoField, peertubeHelpers })) .then(() => sortHooksByPriority(hooks)) .catch(err => console.error('Cannot import or register plugin %s.', pluginInfo.plugin.name, err)) } @@ -68,6 +100,7 @@ export { HookStructValue, Hooks, PluginInfo, + FormFields, loadPlugin, runHook } diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index fe65794f7..c79471005 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -750,7 +750,11 @@ export class PeerTubeEmbed { isTheme: false } - await loadPlugin(this.peertubeHooks, pluginInfo, _ => this.buildPeerTubeHelpers(translations)) + await loadPlugin({ + hooks: this.peertubeHooks, + pluginInfo, + peertubeHelpersFactory: _ => this.buildPeerTubeHelpers(translations) + }) } } } diff --git a/client/src/types/register-client-option.model.ts b/client/src/types/register-client-option.model.ts index dff00e9dd..e3c6d803d 100644 --- a/client/src/types/register-client-option.model.ts +++ b/client/src/types/register-client-option.model.ts @@ -1,8 +1,11 @@ +import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' import { RegisterClientHookOptions } from '@shared/models/plugins/register-client-hook.model' export type RegisterClientOptions = { registerHook: (options: RegisterClientHookOptions) => void + registerVideoField: (commonOptions: RegisterClientFormFieldOptions, videoFormOptions: RegisterClientVideoFieldOptions) => void + peertubeHelpers: RegisterClientHelpers } diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index c05acfd2f..15b6f214f 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -414,7 +414,7 @@ async function updateVideo (req: express.Request, res: express.Response) { Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) } - Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated }) + Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body }) } catch (err) { // Force fields we want to update // If the transaction is retried, sequelize will think the object has not changed diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index 9b6509dfd..7a17c839f 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts @@ -78,7 +78,10 @@ function videoModelToFormattedJSON (video: MVideoFormattable, options?: VideoFor userHistory: userHistory ? { currentTime: userHistory.currentTime - } : undefined + } : undefined, + + // Can be added by external plugins + pluginData: (video as any).pluginData } if (options) { diff --git a/shared/models/plugins/client-hook.model.ts b/shared/models/plugins/client-hook.model.ts index 193a3f646..7b7144676 100644 --- a/shared/models/plugins/client-hook.model.ts +++ b/shared/models/plugins/client-hook.model.ts @@ -70,6 +70,9 @@ export const clientActionHookObject = { // Fired when a user click on 'View x replies' and they're loaded 'action:video-watch.video-thread-replies.loaded': true, + // Fired when the video edit page (upload, URL/torrent import, update) is being initialized + 'action:video-edit.init': true, + // Fired when the login page is being initialized 'action:login.init': true, diff --git a/shared/models/plugins/index.ts b/shared/models/plugins/index.ts index 209fca791..83ed6f583 100644 --- a/shared/models/plugins/index.ts +++ b/shared/models/plugins/index.ts @@ -19,6 +19,7 @@ export * from './plugin-video-privacy-manager.model' export * from './plugin.type' export * from './public-server.setting' export * from './register-client-hook.model' +export * from './register-client-form-field.model' export * from './register-server-hook.model' export * from './register-server-setting.model' export * from './server-hook.model' diff --git a/shared/models/plugins/plugin-client-scope.type.ts b/shared/models/plugins/plugin-client-scope.type.ts index a3c669fe7..e188ce100 100644 --- a/shared/models/plugins/plugin-client-scope.type.ts +++ b/shared/models/plugins/plugin-client-scope.type.ts @@ -1 +1 @@ -export type PluginClientScope = 'common' | 'video-watch' | 'search' | 'signup' | 'login' | 'embed' +export type PluginClientScope = 'common' | 'video-watch' | 'search' | 'signup' | 'login' | 'embed' | 'video-edit' diff --git a/shared/models/plugins/register-client-form-field.model.ts b/shared/models/plugins/register-client-form-field.model.ts new file mode 100644 index 000000000..df24339c6 --- /dev/null +++ b/shared/models/plugins/register-client-form-field.model.ts @@ -0,0 +1,12 @@ +export interface RegisterClientFormFieldOptions { + name: string + label: string + type: 'input' | 'input-checkbox' | 'input-textarea' | 'markdown-text' | 'markdown-enhanced' + + // Default setting value + default?: string | boolean +} + +export interface RegisterClientVideoFieldOptions { + type: 'import-url' | 'import-torrent' | 'update' | 'upload' +} diff --git a/shared/models/plugins/register-server-setting.model.ts b/shared/models/plugins/register-server-setting.model.ts index 920c3480f..6872dc53e 100644 --- a/shared/models/plugins/register-server-setting.model.ts +++ b/shared/models/plugins/register-server-setting.model.ts @@ -1,15 +1,10 @@ -export interface RegisterServerSettingOptions { - name: string - label: string - type: 'input' | 'input-checkbox' | 'input-textarea' | 'markdown-text' | 'markdown-enhanced' +import { RegisterClientFormFieldOptions } from './register-client-form-field.model' +export interface RegisterServerSettingOptions extends RegisterClientFormFieldOptions { // If the setting is not private, anyone can view its value (client code included) // If the setting is private, only server-side hooks can access it // Mainly used by the PeerTube client to get admin config private: boolean - - // Default setting value - default?: string | boolean } export interface RegisteredServerSettings { diff --git a/shared/models/videos/video-update.model.ts b/shared/models/videos/video-update.model.ts index 4ef904156..86653b959 100644 --- a/shared/models/videos/video-update.model.ts +++ b/shared/models/videos/video-update.model.ts @@ -19,4 +19,6 @@ export interface VideoUpdate { previewfile?: Blob scheduleUpdate?: VideoScheduleUpdate originallyPublishedAt?: Date | string + + pluginData?: any } diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts index 557e66e09..158ee8f05 100644 --- a/shared/models/videos/video.model.ts +++ b/shared/models/videos/video.model.ts @@ -53,6 +53,8 @@ export interface Video { userHistory?: { currentTime: number } + + pluginData?: any } export interface VideoDetails extends Video { -- 2.41.0