From 92e66e04f7f51d37b465cff442ce47f6d6d7cadd Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 22 Mar 2022 16:58:49 +0100 Subject: Rename studio to editor --- .../edit-custom-config.component.ts | 2 +- .../edit-vod-transcoding.component.html | 12 +- .../edit-vod-transcoding.component.ts | 4 +- ...y-account-notification-preferences.component.ts | 4 +- .../+my-library/my-videos/my-videos.component.ts | 6 +- client/src/app/+video-editor/edit/index.ts | 2 - .../edit/video-editor-edit.component.html | 88 --------- .../edit/video-editor-edit.component.scss | 76 -------- .../edit/video-editor-edit.component.ts | 202 -------------------- .../edit/video-editor-edit.resolver.ts | 18 -- client/src/app/+video-editor/index.ts | 1 - client/src/app/+video-editor/shared/index.ts | 1 - .../+video-editor/shared/video-editor.service.ts | 28 --- .../+video-editor/video-editor-routing.module.ts | 30 --- .../src/app/+video-editor/video-editor.module.ts | 27 --- client/src/app/+video-studio/edit/index.ts | 2 + .../edit/video-studio-edit.component.html | 88 +++++++++ .../edit/video-studio-edit.component.scss | 76 ++++++++ .../edit/video-studio-edit.component.ts | 204 +++++++++++++++++++++ .../edit/video-studio-edit.resolver.ts | 18 ++ client/src/app/+video-studio/index.ts | 1 + client/src/app/+video-studio/shared/index.ts | 1 + .../+video-studio/shared/video-studio.service.ts | 28 +++ .../+video-studio/video-studio-routing.module.ts | 29 +++ .../src/app/+video-studio/video-studio.module.ts | 27 +++ .../action-buttons/action-buttons.component.ts | 2 +- client/src/app/app-routing.module.ts | 4 +- .../shared-main/users/user-notification.model.ts | 2 +- .../users/user-notifications.component.html | 2 +- .../app/shared/shared-main/video/video.model.ts | 4 +- .../video-actions-dropdown.component.ts | 12 +- 31 files changed, 501 insertions(+), 500 deletions(-) delete mode 100644 client/src/app/+video-editor/edit/index.ts delete mode 100644 client/src/app/+video-editor/edit/video-editor-edit.component.html delete mode 100644 client/src/app/+video-editor/edit/video-editor-edit.component.scss delete mode 100644 client/src/app/+video-editor/edit/video-editor-edit.component.ts delete mode 100644 client/src/app/+video-editor/edit/video-editor-edit.resolver.ts delete mode 100644 client/src/app/+video-editor/index.ts delete mode 100644 client/src/app/+video-editor/shared/index.ts delete mode 100644 client/src/app/+video-editor/shared/video-editor.service.ts delete mode 100644 client/src/app/+video-editor/video-editor-routing.module.ts delete mode 100644 client/src/app/+video-editor/video-editor.module.ts create mode 100644 client/src/app/+video-studio/edit/index.ts create mode 100644 client/src/app/+video-studio/edit/video-studio-edit.component.html create mode 100644 client/src/app/+video-studio/edit/video-studio-edit.component.scss create mode 100644 client/src/app/+video-studio/edit/video-studio-edit.component.ts create mode 100644 client/src/app/+video-studio/edit/video-studio-edit.resolver.ts create mode 100644 client/src/app/+video-studio/index.ts create mode 100644 client/src/app/+video-studio/shared/index.ts create mode 100644 client/src/app/+video-studio/shared/video-studio.service.ts create mode 100644 client/src/app/+video-studio/video-studio-routing.module.ts create mode 100644 client/src/app/+video-studio/video-studio.module.ts (limited to 'client/src/app') 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 94f1021bf..eb892bbfd 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 @@ -200,7 +200,7 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { resolutions: {} } }, - videoEditor: { + videoStudio: { enabled: null }, autoBlacklist: { 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 index 52d6c79f6..5c0bea4a5 100644 --- 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 @@ -193,9 +193,9 @@ -
+
-
VIDEO EDITOR
+
VIDEO STUDIO
Allows your users to edit their video (cut, add intro/outro, add a watermark etc)
@@ -203,14 +203,14 @@
- +
- ⚠️ You need to enable transcoding first to enable video editor + ⚠️ You need to enable transcoding first to enable video studio
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 index 948c10b69..a38438e3a 100644 --- 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 @@ -72,7 +72,7 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges { private checkTranscodingFields () { const transcodingControl = this.form.get('transcoding.enabled') - const videoEditorControl = this.form.get('videoEditor.enabled') + const videoStudioControl = this.form.get('videoStudio.enabled') const hlsControl = this.form.get('transcoding.hls.enabled') const webtorrentControl = this.form.get('transcoding.webtorrent.enabled') @@ -101,7 +101,7 @@ export class EditVODTranscodingComponent implements OnInit, OnChanges { transcodingControl.valueChanges .subscribe(newValue => { if (newValue === false) { - videoEditorControl.setValue(false) + videoStudioControl.setValue(false) } }) } diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts index 187a3818a..7c13282fa 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts @@ -45,7 +45,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { abuseStateChange: $localize`One of your abuse reports has been accepted or rejected by moderators`, newPeerTubeVersion: $localize`A new PeerTube version is available`, newPluginVersion: $localize`One of your plugin/theme has a new available version`, - myVideoEditionFinished: $localize`Video edition finished` + myVideoStudioEditionFinished: $localize`Video studio edition has finished` } this.notificationSettingGroups = [ { @@ -64,7 +64,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { 'blacklistOnMyVideo', 'myVideoPublished', 'myVideoImportFinished', - 'myVideoEditionFinished' + 'myVideoStudioEditionFinished' ] }, diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts index 45a586981..a364b9b6a 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.ts +++ b/client/src/app/+my-library/my-videos/my-videos.component.ts @@ -205,9 +205,9 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { private buildActions () { this.videoActions = [ { - label: $localize`Editor`, - linkBuilder: ({ video }) => [ '/video-editor/edit', video.uuid ], - isDisplayed: ({ video }) => video.isEditableBy(this.authService.getUser(), this.serverService.getHTMLConfig().videoEditor.enabled), + label: $localize`Studio`, + linkBuilder: ({ video }) => [ '/studio/edit', video.uuid ], + isDisplayed: ({ video }) => video.isEditableBy(this.authService.getUser(), this.serverService.getHTMLConfig().videoStudio.enabled), iconName: 'film' }, { diff --git a/client/src/app/+video-editor/edit/index.ts b/client/src/app/+video-editor/edit/index.ts deleted file mode 100644 index 390ca80fc..000000000 --- a/client/src/app/+video-editor/edit/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './video-editor-edit.component' -export * from './video-editor-edit.resolver' diff --git a/client/src/app/+video-editor/edit/video-editor-edit.component.html b/client/src/app/+video-editor/edit/video-editor-edit.component.html deleted file mode 100644 index d33dfaf18..000000000 --- a/client/src/app/+video-editor/edit/video-editor-edit.component.html +++ /dev/null @@ -1,88 +0,0 @@ -
-

Edit {{ video.name }}

- -
-
- -
-

CUT VIDEO

- -
Set a new start/end.
- -
- - -
- -
- - -
-
- -
-

ADD INTRO

- -
Concatenate a file at the beginning of the video.
- -
- -
-
- -
-

ADD OUTRO

- -
Concatenate a file at the end of the video.
- -
- -
-
- -
-

ADD WATERMARK

- -
Add a watermark image to the video.
- -
- -
-
- - -
- - -
-
- - -
- -
- - -
    -
  1. {{ task }}
  2. -
-
-
-
-
diff --git a/client/src/app/+video-editor/edit/video-editor-edit.component.scss b/client/src/app/+video-editor/edit/video-editor-edit.component.scss deleted file mode 100644 index 43f336f59..000000000 --- a/client/src/app/+video-editor/edit/video-editor-edit.component.scss +++ /dev/null @@ -1,76 +0,0 @@ -@use '_variables' as *; -@use '_mixins' as *; - -.columns { - display: flex; - - .information { - width: 100%; - margin-left: 50px; - - > div { - margin-bottom: 30px; - } - - @media screen and (max-width: $small-view) { - display: none; - } - } -} - -h1 { - font-size: 20px; -} - -h2 { - font-weight: $font-bold; - font-size: 16px; - color: pvar(--mainColor); - background-color: pvar(--mainBackgroundColor); - padding: 0 5px; - width: fit-content; - margin: -8px 0 0; -} - -.section { - $min-width: 600px; - - @include padding-left(10px); - - min-width: $min-width; - - margin-bottom: 50px; - border: 1px solid $separator-border-color; - border-radius: 5px; - width: fit-content; - - .form-group, - .description { - @include margin-left(5px); - } - - .description { - color: pvar(--greyForegroundColor); - margin-top: 5px; - margin-bottom: 15px; - } - - @media screen and (max-width: $min-width) { - min-width: none; - } -} - -my-timestamp-input { - display: block; -} - -my-embed { - display: block; - max-width: 500px; - width: 100%; -} - -my-reactive-file { - display: block; - width: fit-content; -} diff --git a/client/src/app/+video-editor/edit/video-editor-edit.component.ts b/client/src/app/+video-editor/edit/video-editor-edit.component.ts deleted file mode 100644 index 0adf93335..000000000 --- a/client/src/app/+video-editor/edit/video-editor-edit.component.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' -import { ConfirmService, Notifier, ServerService } from '@app/core' -import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' -import { Video, VideoDetails } from '@app/shared/shared-main' -import { LoadingBarService } from '@ngx-loading-bar/core' -import { secondsToTime } from '@shared/core-utils' -import { VideoEditorTask, VideoEditorTaskCut } from '@shared/models' -import { VideoEditorService } from '../shared' - -@Component({ - selector: 'my-video-editor-edit', - templateUrl: './video-editor-edit.component.html', - styleUrls: [ './video-editor-edit.component.scss' ] -}) -export class VideoEditorEditComponent extends FormReactive implements OnInit { - isRunningEdition = false - - video: VideoDetails - - constructor ( - protected formValidatorService: FormValidatorService, - private serverService: ServerService, - private notifier: Notifier, - private router: Router, - private route: ActivatedRoute, - private videoEditorService: VideoEditorService, - private loadingBar: LoadingBarService, - private confirmService: ConfirmService - ) { - super() - } - - ngOnInit () { - this.video = this.route.snapshot.data.video - - const defaultValues = { - cut: { - start: 0, - end: this.video.duration - } - } - - this.buildForm({ - cut: { - start: null, - end: null - }, - 'add-intro': { - file: null - }, - 'add-outro': { - file: null - }, - 'add-watermark': { - file: null - } - }, defaultValues) - } - - get videoExtensions () { - return this.serverService.getHTMLConfig().video.file.extensions - } - - get imageExtensions () { - return this.serverService.getHTMLConfig().video.image.extensions - } - - async runEdition () { - if (this.isRunningEdition) return - - const title = $localize`Are you sure you want to edit "${this.video.name}"?` - const listHTML = this.getTasksSummary().map(t => `
  • ${t}
  • `).join('') - - // eslint-disable-next-line max-len - const confirmHTML = $localize`The current video will be overwritten by this edited video and you won't be able to recover it.

    ` + - $localize`As a reminder, the following tasks will be executed:
      ${listHTML}
    ` - - if (await this.confirmService.confirm(confirmHTML, title) !== true) return - - this.isRunningEdition = true - - const tasks = this.buildTasks() - - this.loadingBar.useRef().start() - - return this.videoEditorService.editVideo(this.video.uuid, tasks) - .subscribe({ - next: () => { - this.notifier.success($localize`Video updated.`) - this.router.navigateByUrl(Video.buildWatchUrl(this.video)) - }, - - error: err => { - this.loadingBar.useRef().complete() - this.isRunningEdition = false - this.notifier.error(err.message) - console.error(err) - } - }) - } - - getIntroOutroTooltip () { - return $localize`(extensions: ${this.videoExtensions.join(', ')})` - } - - getWatermarkTooltip () { - return $localize`(extensions: ${this.imageExtensions.join(', ')})` - } - - noEdition () { - return this.buildTasks().length === 0 - } - - getTasksSummary () { - const tasks = this.buildTasks() - - return tasks.map(t => { - if (t.name === 'add-intro') { - return $localize`"${this.getFilename(t.options.file)}" will be added at the beginning of the video` - } - - if (t.name === 'add-outro') { - return $localize`"${this.getFilename(t.options.file)}" will be added at the end of the video` - } - - if (t.name === 'add-watermark') { - return $localize`"${this.getFilename(t.options.file)}" image watermark will be added to the video` - } - - if (t.name === 'cut') { - const { start, end } = t.options - - if (start !== undefined && end !== undefined) { - return $localize`Video will begin at ${secondsToTime(start)} and stop at ${secondsToTime(end)}` - } - - if (start !== undefined) { - return $localize`Video will begin at ${secondsToTime(start)}` - } - - if (end !== undefined) { - return $localize`Video will stop at ${secondsToTime(end)}` - } - } - - return '' - }) - } - - private getFilename (obj: any) { - return obj.name - } - - private buildTasks () { - const tasks: VideoEditorTask[] = [] - const value = this.form.value - - const cut = value['cut'] - if (cut['start'] !== 0 || cut['end'] !== this.video.duration) { - - const options: VideoEditorTaskCut['options'] = {} - if (cut['start'] !== 0) options.start = cut['start'] - if (cut['end'] !== this.video.duration) options.end = cut['end'] - - tasks.push({ - name: 'cut', - options - }) - } - - if (value['add-intro']?.['file']) { - tasks.push({ - name: 'add-intro', - options: { - file: value['add-intro']['file'] - } - }) - } - - if (value['add-outro']?.['file']) { - tasks.push({ - name: 'add-outro', - options: { - file: value['add-outro']['file'] - } - }) - } - - if (value['add-watermark']?.['file']) { - tasks.push({ - name: 'add-watermark', - options: { - file: value['add-watermark']['file'] - } - }) - } - - return tasks - } - -} diff --git a/client/src/app/+video-editor/edit/video-editor-edit.resolver.ts b/client/src/app/+video-editor/edit/video-editor-edit.resolver.ts deleted file mode 100644 index 7b95ae834..000000000 --- a/client/src/app/+video-editor/edit/video-editor-edit.resolver.ts +++ /dev/null @@ -1,18 +0,0 @@ - -import { Injectable } from '@angular/core' -import { ActivatedRouteSnapshot, Resolve } from '@angular/router' -import { VideoService } from '@app/shared/shared-main' - -@Injectable() -export class VideoEditorEditResolver implements Resolve { - constructor ( - private videoService: VideoService - ) { - } - - resolve (route: ActivatedRouteSnapshot) { - const videoId: string = route.params['videoId'] - - return this.videoService.getVideo({ videoId }) - } -} diff --git a/client/src/app/+video-editor/index.ts b/client/src/app/+video-editor/index.ts deleted file mode 100644 index 5a9e9fdd0..000000000 --- a/client/src/app/+video-editor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './video-editor.module' diff --git a/client/src/app/+video-editor/shared/index.ts b/client/src/app/+video-editor/shared/index.ts deleted file mode 100644 index eaf88b6f4..000000000 --- a/client/src/app/+video-editor/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './video-editor.service' diff --git a/client/src/app/+video-editor/shared/video-editor.service.ts b/client/src/app/+video-editor/shared/video-editor.service.ts deleted file mode 100644 index 5b7053039..000000000 --- a/client/src/app/+video-editor/shared/video-editor.service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { catchError } from 'rxjs' -import { HttpClient } from '@angular/common/http' -import { Injectable } from '@angular/core' -import { RestExtractor } from '@app/core' -import { objectToFormData } from '@app/helpers' -import { VideoService } from '@app/shared/shared-main' -import { VideoEditorCreateEdition, VideoEditorTask } from '@shared/models' - -@Injectable() -export class VideoEditorService { - - constructor ( - private authHttp: HttpClient, - private restExtractor: RestExtractor - ) {} - - editVideo (videoId: number | string, tasks: VideoEditorTask[]) { - const url = VideoService.BASE_VIDEO_URL + '/' + videoId + '/editor/edit' - const body: VideoEditorCreateEdition = { - tasks - } - - const data = objectToFormData(body) - - return this.authHttp.post(url, data) - .pipe(catchError(err => this.restExtractor.handleError(err))) - } -} diff --git a/client/src/app/+video-editor/video-editor-routing.module.ts b/client/src/app/+video-editor/video-editor-routing.module.ts deleted file mode 100644 index 9f37a0dae..000000000 --- a/client/src/app/+video-editor/video-editor-routing.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NgModule } from '@angular/core' -import { RouterModule, Routes } from '@angular/router' -import { VideoEditorEditResolver } from './edit' -import { VideoEditorEditComponent } from './edit/video-editor-edit.component' - -const videoEditorRoutes: Routes = [ - { - path: '', - children: [ - { - path: 'edit/:videoId', - component: VideoEditorEditComponent, - data: { - meta: { - title: $localize`Edit video` - } - }, - resolve: { - video: VideoEditorEditResolver - } - } - ] - } -] - -@NgModule({ - imports: [ RouterModule.forChild(videoEditorRoutes) ], - exports: [ RouterModule ] -}) -export class VideoEditorRoutingModule {} diff --git a/client/src/app/+video-editor/video-editor.module.ts b/client/src/app/+video-editor/video-editor.module.ts deleted file mode 100644 index 7bbebc17b..000000000 --- a/client/src/app/+video-editor/video-editor.module.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NgModule } from '@angular/core' -import { SharedFormModule } from '@app/shared/shared-forms' -import { SharedMainModule } from '@app/shared/shared-main' -import { VideoEditorEditComponent, VideoEditorEditResolver } from './edit' -import { VideoEditorService } from './shared' -import { VideoEditorRoutingModule } from './video-editor-routing.module' - -@NgModule({ - imports: [ - VideoEditorRoutingModule, - - SharedMainModule, - SharedFormModule - ], - - declarations: [ - VideoEditorEditComponent - ], - - exports: [], - - providers: [ - VideoEditorService, - VideoEditorEditResolver - ] -}) -export class VideoEditorModule { } diff --git a/client/src/app/+video-studio/edit/index.ts b/client/src/app/+video-studio/edit/index.ts new file mode 100644 index 000000000..ff1d77fc0 --- /dev/null +++ b/client/src/app/+video-studio/edit/index.ts @@ -0,0 +1,2 @@ +export * from './video-studio-edit.component' +export * from './video-studio-edit.resolver' diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.html b/client/src/app/+video-studio/edit/video-studio-edit.component.html new file mode 100644 index 000000000..a9f34811f --- /dev/null +++ b/client/src/app/+video-studio/edit/video-studio-edit.component.html @@ -0,0 +1,88 @@ +
    +

    Studio for {{ video.name }}

    + +
    +
    + +
    +

    CUT VIDEO

    + +
    Set a new start/end.
    + +
    + + +
    + +
    + + +
    +
    + +
    +

    ADD INTRO

    + +
    Concatenate a file at the beginning of the video.
    + +
    + +
    +
    + +
    +

    ADD OUTRO

    + +
    Concatenate a file at the end of the video.
    + +
    + +
    +
    + +
    +

    ADD WATERMARK

    + +
    Add a watermark image to the video.
    + +
    + +
    +
    + + +
    + + +
    +
    + + +
    + +
    + + +
      +
    1. {{ task }}
    2. +
    +
    +
    +
    +
    diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.scss b/client/src/app/+video-studio/edit/video-studio-edit.component.scss new file mode 100644 index 000000000..43f336f59 --- /dev/null +++ b/client/src/app/+video-studio/edit/video-studio-edit.component.scss @@ -0,0 +1,76 @@ +@use '_variables' as *; +@use '_mixins' as *; + +.columns { + display: flex; + + .information { + width: 100%; + margin-left: 50px; + + > div { + margin-bottom: 30px; + } + + @media screen and (max-width: $small-view) { + display: none; + } + } +} + +h1 { + font-size: 20px; +} + +h2 { + font-weight: $font-bold; + font-size: 16px; + color: pvar(--mainColor); + background-color: pvar(--mainBackgroundColor); + padding: 0 5px; + width: fit-content; + margin: -8px 0 0; +} + +.section { + $min-width: 600px; + + @include padding-left(10px); + + min-width: $min-width; + + margin-bottom: 50px; + border: 1px solid $separator-border-color; + border-radius: 5px; + width: fit-content; + + .form-group, + .description { + @include margin-left(5px); + } + + .description { + color: pvar(--greyForegroundColor); + margin-top: 5px; + margin-bottom: 15px; + } + + @media screen and (max-width: $min-width) { + min-width: none; + } +} + +my-timestamp-input { + display: block; +} + +my-embed { + display: block; + max-width: 500px; + width: 100%; +} + +my-reactive-file { + display: block; + width: fit-content; +} diff --git a/client/src/app/+video-studio/edit/video-studio-edit.component.ts b/client/src/app/+video-studio/edit/video-studio-edit.component.ts new file mode 100644 index 000000000..392b65767 --- /dev/null +++ b/client/src/app/+video-studio/edit/video-studio-edit.component.ts @@ -0,0 +1,204 @@ +import { Component, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { ConfirmService, Notifier, ServerService } from '@app/core' +import { FormReactive, FormValidatorService } from '@app/shared/shared-forms' +import { VideoDetails } from '@app/shared/shared-main' +import { LoadingBarService } from '@ngx-loading-bar/core' +import { secondsToTime } from '@shared/core-utils' +import { VideoStudioTask, VideoStudioTaskCut } from '@shared/models' +import { VideoStudioService } from '../shared' + +@Component({ + selector: 'my-video-studio-edit', + templateUrl: './video-studio-edit.component.html', + styleUrls: [ './video-studio-edit.component.scss' ] +}) +export class VideoStudioEditComponent extends FormReactive implements OnInit { + isRunningEdition = false + + video: VideoDetails + + constructor ( + protected formValidatorService: FormValidatorService, + private serverService: ServerService, + private notifier: Notifier, + private router: Router, + private route: ActivatedRoute, + private videoStudioService: VideoStudioService, + private loadingBar: LoadingBarService, + private confirmService: ConfirmService + ) { + super() + } + + ngOnInit () { + this.video = this.route.snapshot.data.video + + const defaultValues = { + cut: { + start: 0, + end: this.video.duration + } + } + + this.buildForm({ + cut: { + start: null, + end: null + }, + 'add-intro': { + file: null + }, + 'add-outro': { + file: null + }, + 'add-watermark': { + file: null + } + }, defaultValues) + } + + get videoExtensions () { + return this.serverService.getHTMLConfig().video.file.extensions + } + + get imageExtensions () { + return this.serverService.getHTMLConfig().video.image.extensions + } + + async runEdition () { + if (this.isRunningEdition) return + + const title = $localize`Are you sure you want to edit "${this.video.name}"?` + const listHTML = this.getTasksSummary().map(t => `
  • ${t}
  • `).join('') + + // eslint-disable-next-line max-len + const confirmHTML = $localize`The current video will be overwritten by this edited video and you won't be able to recover it.

    ` + + $localize`As a reminder, the following tasks will be executed:
      ${listHTML}
    ` + + if (await this.confirmService.confirm(confirmHTML, title) !== true) return + + this.isRunningEdition = true + + const tasks = this.buildTasks() + + this.loadingBar.useRef().start() + + return this.videoStudioService.editVideo(this.video.uuid, tasks) + .subscribe({ + next: () => { + this.notifier.success($localize`Edition tasks created.`) + + // Don't redirect to old video version watch page that could be confusing for users + this.router.navigateByUrl('/my-library/videos') + }, + + error: err => { + this.loadingBar.useRef().complete() + this.isRunningEdition = false + this.notifier.error(err.message) + console.error(err) + } + }) + } + + getIntroOutroTooltip () { + return $localize`(extensions: ${this.videoExtensions.join(', ')})` + } + + getWatermarkTooltip () { + return $localize`(extensions: ${this.imageExtensions.join(', ')})` + } + + noEdition () { + return this.buildTasks().length === 0 + } + + getTasksSummary () { + const tasks = this.buildTasks() + + return tasks.map(t => { + if (t.name === 'add-intro') { + return $localize`"${this.getFilename(t.options.file)}" will be added at the beginning of the video` + } + + if (t.name === 'add-outro') { + return $localize`"${this.getFilename(t.options.file)}" will be added at the end of the video` + } + + if (t.name === 'add-watermark') { + return $localize`"${this.getFilename(t.options.file)}" image watermark will be added to the video` + } + + if (t.name === 'cut') { + const { start, end } = t.options + + if (start !== undefined && end !== undefined) { + return $localize`Video will begin at ${secondsToTime(start)} and stop at ${secondsToTime(end)}` + } + + if (start !== undefined) { + return $localize`Video will begin at ${secondsToTime(start)}` + } + + if (end !== undefined) { + return $localize`Video will stop at ${secondsToTime(end)}` + } + } + + return '' + }) + } + + private getFilename (obj: any) { + return obj.name + } + + private buildTasks () { + const tasks: VideoStudioTask[] = [] + const value = this.form.value + + const cut = value['cut'] + if (cut['start'] !== 0 || cut['end'] !== this.video.duration) { + + const options: VideoStudioTaskCut['options'] = {} + if (cut['start'] !== 0) options.start = cut['start'] + if (cut['end'] !== this.video.duration) options.end = cut['end'] + + tasks.push({ + name: 'cut', + options + }) + } + + if (value['add-intro']?.['file']) { + tasks.push({ + name: 'add-intro', + options: { + file: value['add-intro']['file'] + } + }) + } + + if (value['add-outro']?.['file']) { + tasks.push({ + name: 'add-outro', + options: { + file: value['add-outro']['file'] + } + }) + } + + if (value['add-watermark']?.['file']) { + tasks.push({ + name: 'add-watermark', + options: { + file: value['add-watermark']['file'] + } + }) + } + + return tasks + } + +} diff --git a/client/src/app/+video-studio/edit/video-studio-edit.resolver.ts b/client/src/app/+video-studio/edit/video-studio-edit.resolver.ts new file mode 100644 index 000000000..c658be50b --- /dev/null +++ b/client/src/app/+video-studio/edit/video-studio-edit.resolver.ts @@ -0,0 +1,18 @@ + +import { Injectable } from '@angular/core' +import { ActivatedRouteSnapshot, Resolve } from '@angular/router' +import { VideoService } from '@app/shared/shared-main' + +@Injectable() +export class VideoStudioEditResolver implements Resolve { + constructor ( + private videoService: VideoService + ) { + } + + resolve (route: ActivatedRouteSnapshot) { + const videoId: string = route.params['videoId'] + + return this.videoService.getVideo({ videoId }) + } +} diff --git a/client/src/app/+video-studio/index.ts b/client/src/app/+video-studio/index.ts new file mode 100644 index 000000000..d50c21cdc --- /dev/null +++ b/client/src/app/+video-studio/index.ts @@ -0,0 +1 @@ +export * from './video-studio.module' diff --git a/client/src/app/+video-studio/shared/index.ts b/client/src/app/+video-studio/shared/index.ts new file mode 100644 index 000000000..9940ac6a9 --- /dev/null +++ b/client/src/app/+video-studio/shared/index.ts @@ -0,0 +1 @@ +export * from './video-studio.service' diff --git a/client/src/app/+video-studio/shared/video-studio.service.ts b/client/src/app/+video-studio/shared/video-studio.service.ts new file mode 100644 index 000000000..8d8b2f0e5 --- /dev/null +++ b/client/src/app/+video-studio/shared/video-studio.service.ts @@ -0,0 +1,28 @@ +import { catchError } from 'rxjs' +import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { RestExtractor } from '@app/core' +import { objectToFormData } from '@app/helpers' +import { VideoService } from '@app/shared/shared-main' +import { VideoStudioCreateEdition, VideoStudioTask } from '@shared/models' + +@Injectable() +export class VideoStudioService { + + constructor ( + private authHttp: HttpClient, + private restExtractor: RestExtractor + ) {} + + editVideo (videoId: number | string, tasks: VideoStudioTask[]) { + const url = VideoService.BASE_VIDEO_URL + '/' + videoId + '/studio/edit' + const body: VideoStudioCreateEdition = { + tasks + } + + const data = objectToFormData(body) + + return this.authHttp.post(url, data) + .pipe(catchError(err => this.restExtractor.handleError(err))) + } +} diff --git a/client/src/app/+video-studio/video-studio-routing.module.ts b/client/src/app/+video-studio/video-studio-routing.module.ts new file mode 100644 index 000000000..bcd9b79a5 --- /dev/null +++ b/client/src/app/+video-studio/video-studio-routing.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core' +import { RouterModule, Routes } from '@angular/router' +import { VideoStudioEditComponent, VideoStudioEditResolver } from './edit' + +const videoStudioRoutes: Routes = [ + { + path: '', + children: [ + { + path: 'edit/:videoId', + component: VideoStudioEditComponent, + data: { + meta: { + title: $localize`Studio` + } + }, + resolve: { + video: VideoStudioEditResolver + } + } + ] + } +] + +@NgModule({ + imports: [ RouterModule.forChild(videoStudioRoutes) ], + exports: [ RouterModule ] +}) +export class VideoStudioRoutingModule {} diff --git a/client/src/app/+video-studio/video-studio.module.ts b/client/src/app/+video-studio/video-studio.module.ts new file mode 100644 index 000000000..1a8763539 --- /dev/null +++ b/client/src/app/+video-studio/video-studio.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core' +import { SharedFormModule } from '@app/shared/shared-forms' +import { SharedMainModule } from '@app/shared/shared-main' +import { VideoStudioEditComponent, VideoStudioEditResolver } from './edit' +import { VideoStudioService } from './shared' +import { VideoStudioRoutingModule } from './video-studio-routing.module' + +@NgModule({ + imports: [ + VideoStudioRoutingModule, + + SharedMainModule, + SharedFormModule + ], + + declarations: [ + VideoStudioEditComponent + ], + + exports: [], + + providers: [ + VideoStudioService, + VideoStudioEditResolver + ] +}) +export class VideoStudioModule { } diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts index 6e8a64f46..af26ea04d 100644 --- a/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts +++ b/client/src/app/+videos/+video-watch/shared/action-buttons/action-buttons.component.ts @@ -35,7 +35,7 @@ export class ActionButtonsComponent implements OnInit, OnChanges { playlist: false, download: true, update: true, - editor: true, + studio: true, blacklist: true, delete: true, report: true, diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index cd499845b..a831da099 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -144,8 +144,8 @@ const routes: Routes = [ }, { - path: 'video-editor', - loadChildren: () => import('./+video-editor/video-editor.module').then(m => m.VideoEditorModule), + path: 'studio', + loadChildren: () => import('./+video-studio/video-studio.module').then(m => m.VideoStudioModule), canActivateChild: [ MetaGuard ] }, diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts index d1b36f347..a2367166e 100644 --- a/client/src/app/shared/shared-main/users/user-notification.model.ts +++ b/client/src/app/shared/shared-main/users/user-notification.model.ts @@ -228,7 +228,7 @@ export class UserNotification implements UserNotificationServer { this.pluginQueryParams.pluginType = this.plugin.type + '' break - case UserNotificationType.MY_VIDEO_EDITION_FINISHED: + case UserNotificationType.MY_VIDEO_STUDIO_EDITION_FINISHED: this.videoUrl = this.buildVideoUrl(this.video) break } diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.html b/client/src/app/shared/shared-main/users/user-notifications.component.html index ff1259fb8..e7cdb0183 100644 --- a/client/src/app/shared/shared-main/users/user-notifications.component.html +++ b/client/src/app/shared/shared-main/users/user-notifications.component.html @@ -207,7 +207,7 @@
    - +
    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 612fcf16c..2d4db9a28 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts @@ -228,8 +228,8 @@ export class Video implements VideoServerModel { return user && this.isLocal === true && (this.account.name === user.username || user.hasRight(UserRight.UPDATE_ANY_VIDEO)) } - isEditableBy (user: AuthUser, videoEditorEnabled: boolean) { - return videoEditorEnabled && + isEditableBy (user: AuthUser, videoStudioEnabled: boolean) { + return videoStudioEnabled && this.state?.id === VideoState.PUBLISHED && this.isUpdatableBy(user) } diff --git a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts index 29a711378..5eef96145 100644 --- a/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-actions-dropdown.component.ts @@ -29,7 +29,7 @@ export type VideoActionsDisplayType = { liveInfo?: boolean removeFiles?: boolean transcoding?: boolean - editor?: boolean + studio?: boolean } @Component({ @@ -61,7 +61,7 @@ export class VideoActionsDropdownComponent implements OnChanges { liveInfo: false, removeFiles: false, transcoding: false, - editor: true + studio: true } @Input() placement = 'left' @@ -153,7 +153,7 @@ export class VideoActionsDropdownComponent implements OnChanges { } isVideoEditable () { - return this.video.isEditableBy(this.user, this.serverService.getHTMLConfig().videoEditor.enabled) + return this.video.isEditableBy(this.user, this.serverService.getHTMLConfig().videoStudio.enabled) } isVideoRemovable () { @@ -337,10 +337,10 @@ export class VideoActionsDropdownComponent implements OnChanges { isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.update && this.isVideoUpdatable() }, { - label: $localize`Editor`, - linkBuilder: ({ video }) => [ '/video-editor/edit', video.uuid ], + label: $localize`Studio`, + linkBuilder: ({ video }) => [ '/studio/edit', video.uuid ], iconName: 'film', - isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.editor && this.isVideoEditable() + isDisplayed: () => this.authService.isLoggedIn() && this.displayOptions.studio && this.isVideoEditable() }, { label: $localize`Block`, -- cgit v1.2.3