From: Chocobozzz Date: Mon, 6 Aug 2018 13:30:24 +0000 (+0200) Subject: Move send video components inside a dedicated directory X-Git-Tag: delete~47 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=788487140c500abeb69ca44daf3a9e26efa8d36f;p=oweals%2Fpeertube.git Move send video components inside a dedicated directory --- diff --git a/client/src/app/videos/+video-edit/shared/video-send.ts b/client/src/app/videos/+video-edit/shared/video-send.ts deleted file mode 100644 index bc1c7a45b..000000000 --- a/client/src/app/videos/+video-edit/shared/video-send.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { FormReactive } from '@app/shared' -import { OnInit } from '@angular/core' -import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' -import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils' -import { VideoConstant, VideoPrivacy } from '../../../../../../shared/models/videos' -import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' -import { LoadingBarService } from '@ngx-loading-bar/core' -import { NotificationsService } from 'angular2-notifications' -import { AuthService, ServerService } from '@app/core' -import { VideoService } from '@app/shared/video/video.service' -import { VideoCaptionService } from '@app/shared/video-caption' -import { catchError, switchMap, tap } from 'rxjs/operators' -import { VideoEdit } from '@app/shared/video/video-edit.model' - -export abstract class VideoSend extends FormReactive implements OnInit, CanComponentDeactivate { - - userVideoChannels: { id: number, label: string, support: string }[] = [] - videoPrivacies: VideoConstant[] = [] - videoCaptions: VideoCaptionEdit[] = [] - - firstStepPrivacyId = 0 - firstStepChannelId = 0 - - protected abstract readonly DEFAULT_VIDEO_PRIVACY: VideoPrivacy - - protected loadingBar: LoadingBarService - protected notificationsService: NotificationsService - protected authService: AuthService - protected serverService: ServerService - protected videoService: VideoService - protected videoCaptionService: VideoCaptionService - - abstract canDeactivate () - - ngOnInit () { - this.buildForm({}) - - populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) - .then(() => this.firstStepChannelId = this.userVideoChannels[ 0 ].id) - - this.serverService.videoPrivaciesLoaded - .subscribe( - () => { - this.videoPrivacies = this.serverService.getVideoPrivacies() - - this.firstStepPrivacyId = this.DEFAULT_VIDEO_PRIVACY - }) - } - - checkForm () { - this.forceCheck() - - return this.form.valid - } - - protected updateVideoAndCaptions (video: VideoEdit) { - this.loadingBar.start() - - return this.videoService.updateVideo(video) - .pipe( - // Then update captions - switchMap(() => this.videoCaptionService.updateCaptions(video.id, this.videoCaptions)), - tap(() => this.loadingBar.complete()), - catchError(err => { - this.loadingBar.complete() - throw err - }) - ) - } -} 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 new file mode 100644 index 000000000..6b431f6f6 --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html @@ -0,0 +1,60 @@ +
+
+
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+
+ +
+ Congratulations, the video behind {{ targetUrl }} will be imported! You can already add information about this video. +
+ + +
+ + +
+
+ + +
+
+
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss new file mode 100644 index 000000000..5e713ab97 --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss @@ -0,0 +1,37 @@ +@import 'variables'; +@import 'mixins'; + +$width-size: 190px; + +.peertube-select-container { + @include peertube-select-container($width-size); +} + +.import-video { + display: flex; + flex-direction: column; + align-items: center; + + .icon.icon-upload { + @include icon(90px); + margin-bottom: 25px; + cursor: default; + + background-image: url('../../../../assets/images/video/upload.svg'); + } + + input[type=text] { + @include peertube-input-text($width-size); + display: block; + } + + input[type=button] { + @include peertube-button; + @include orange-button; + + width: $width-size; + margin-top: 30px; + } +} + + diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts new file mode 100644 index 000000000..dbe69409f --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts @@ -0,0 +1,132 @@ +import { Component, EventEmitter, OnInit, Output } from '@angular/core' +import { Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { VideoPrivacy, VideoUpdate } from '../../../../../../shared/models/videos' +import { AuthService, ServerService } from '../../../core' +import { VideoService } from '../../../shared/video/video.service' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { LoadingBarService } from '@ngx-loading-bar/core' +import { VideoSend } from '@app/videos/+video-edit/video-add-components/video-send' +import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' +import { VideoEdit } from '@app/shared/video/video-edit.model' +import { FormValidatorService } from '@app/shared' +import { VideoCaptionService } from '@app/shared/video-caption' +import { VideoImportService } from '@app/shared/video-import' + +@Component({ + selector: 'my-video-import-url', + templateUrl: './video-import-url.component.html', + styleUrls: [ + '../shared/video-edit.component.scss', + './video-import-url.component.scss' + ] +}) +export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate { + @Output() firstStepDone = new EventEmitter() + + targetUrl = '' + videoFileName: string + + isImportingVideo = false + hasImportedVideo = false + isUpdatingVideo = false + + video: VideoEdit + + protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PRIVATE + + constructor ( + protected formValidatorService: FormValidatorService, + protected loadingBar: LoadingBarService, + protected notificationsService: NotificationsService, + protected authService: AuthService, + protected serverService: ServerService, + protected videoService: VideoService, + protected videoCaptionService: VideoCaptionService, + private router: Router, + private videoImportService: VideoImportService, + private i18n: I18n + ) { + super() + } + + ngOnInit () { + super.ngOnInit() + } + + canDeactivate () { + return { canDeactivate: true } + } + + isTargetUrlValid () { + return this.targetUrl && this.targetUrl.match(/https?:\/\//) + } + + importVideo () { + this.isImportingVideo = true + + const videoUpdate: VideoUpdate = { + privacy: this.firstStepPrivacyId, + waitTranscoding: false, + commentsEnabled: true, + channelId: this.firstStepChannelId + } + + this.loadingBar.start() + + this.videoImportService.importVideo(this.targetUrl, videoUpdate).subscribe( + res => { + this.loadingBar.complete() + this.firstStepDone.emit(res.video.name) + this.isImportingVideo = false + this.hasImportedVideo = true + + this.video = new VideoEdit(Object.assign(res.video, { + commentsEnabled: videoUpdate.commentsEnabled, + support: null, + thumbnailUrl: null, + previewUrl: null + })) + this.hydrateFormFromVideo() + }, + + err => { + this.loadingBar.complete() + this.isImportingVideo = false + this.notificationsService.error(this.i18n('Error'), err.message) + } + ) + } + + updateSecondStep () { + if (this.checkForm() === false) { + return + } + + this.video.patch(this.form.value) + + this.isUpdatingVideo = true + + // Update the video + this.updateVideoAndCaptions(this.video) + .subscribe( + () => { + this.isUpdatingVideo = false + this.notificationsService.success(this.i18n('Success'), this.i18n('Video to import updated.')) + + this.router.navigate([ '/my-account', 'video-imports' ]) + }, + + err => { + this.isUpdatingVideo = false + this.notificationsService.error(this.i18n('Error'), err.message) + console.error(err) + } + ) + + } + + private hydrateFormFromVideo () { + this.form.patchValue(this.video.toFormPatch()) + } +} diff --git a/client/src/app/videos/+video-edit/video-add-components/video-send.ts b/client/src/app/videos/+video-edit/video-add-components/video-send.ts new file mode 100644 index 000000000..efd182269 --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-send.ts @@ -0,0 +1,71 @@ +import { EventEmitter, OnInit } from '@angular/core' +import { LoadingBarService } from '@ngx-loading-bar/core' +import { NotificationsService } from 'angular2-notifications' +import { catchError, switchMap, tap } from 'rxjs/operators' +import { FormReactive } from '@app/shared' +import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' +import { VideoConstant, VideoPrivacy } from '../../../../../../shared' +import { AuthService, ServerService } from '@app/core' +import { VideoService } from '@app/shared/video/video.service' +import { VideoCaptionEdit } from '@app/shared/video-caption/video-caption-edit.model' +import { VideoCaptionService } from '@app/shared/video-caption' +import { VideoEdit } from '@app/shared/video/video-edit.model' +import { populateAsyncUserVideoChannels } from '@app/shared/misc/utils' + +export abstract class VideoSend extends FormReactive implements OnInit, CanComponentDeactivate { + + userVideoChannels: { id: number, label: string, support: string }[] = [] + videoPrivacies: VideoConstant[] = [] + videoCaptions: VideoCaptionEdit[] = [] + + firstStepPrivacyId = 0 + firstStepChannelId = 0 + + abstract firstStepDone: EventEmitter + protected abstract readonly DEFAULT_VIDEO_PRIVACY: VideoPrivacy + + protected loadingBar: LoadingBarService + protected notificationsService: NotificationsService + protected authService: AuthService + protected serverService: ServerService + protected videoService: VideoService + protected videoCaptionService: VideoCaptionService + + abstract canDeactivate () + + ngOnInit () { + this.buildForm({}) + + populateAsyncUserVideoChannels(this.authService, this.userVideoChannels) + .then(() => this.firstStepChannelId = this.userVideoChannels[ 0 ].id) + + this.serverService.videoPrivaciesLoaded + .subscribe( + () => { + this.videoPrivacies = this.serverService.getVideoPrivacies() + + this.firstStepPrivacyId = this.DEFAULT_VIDEO_PRIVACY + }) + } + + checkForm () { + this.forceCheck() + + return this.form.valid + } + + protected updateVideoAndCaptions (video: VideoEdit) { + this.loadingBar.start() + + return this.videoService.updateVideo(video) + .pipe( + // Then update captions + switchMap(() => this.videoCaptionService.updateCaptions(video.id, this.videoCaptions)), + tap(() => this.loadingBar.complete()), + catchError(err => { + this.loadingBar.complete() + throw err + }) + ) + } +} 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 new file mode 100644 index 000000000..8c0723155 --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.html @@ -0,0 +1,58 @@ +
+
+
+ +
+ Select the file to upload + +
+ (.mp4, .webm, .ogv) + +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+ + +
+ + +
+ + +
+
Publish will be available when upload is finished
+ +
+ + +
+
+
\ No newline at end of file diff --git a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss new file mode 100644 index 000000000..dbae5230d --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss @@ -0,0 +1,85 @@ +@import 'variables'; +@import 'mixins'; + +.peertube-select-container { + @include peertube-select-container(190px); +} + +.upload-video { + display: flex; + flex-direction: column; + align-items: center; + + .form-group-channel { + margin-bottom: 20px; + margin-top: 35px; + } + + .icon.icon-upload { + @include icon(90px); + margin-bottom: 25px; + cursor: default; + + background-image: url('../../../../assets/images/video/upload.svg'); + } + + .button-file { + @include peertube-button-file(auto); + + min-width: 190px; + } + + .button-file-extension { + display: block; + font-size: 12px; + margin-top: 5px; + } +} + +.upload-progress-cancel { + display: flex; + margin-top: 25px; + margin-bottom: 40px; + + p-progressBar { + flex-grow: 1; + + /deep/ .ui-progressbar { + font-size: 15px !important; + color: #fff !important; + height: 30px !important; + line-height: 30px !important; + border-radius: 3px !important; + background-color: rgba(11, 204, 41, 0.16) !important; + + .ui-progressbar-value { + background-color: #0BCC29 !important; + } + + .ui-progressbar-label { + text-align: left; + padding-left: 18px; + margin-top: 0 !important; + } + } + + &.processing { + /deep/ .ui-progressbar-label { + // Same color as background to hide "100%" + color: rgba(11, 204, 41, 0.16) !important; + + &::before { + content: 'Processing...'; + color: #fff; + } + } + } + } + + input { + @include peertube-button; + @include grey-button; + + margin-left: 10px; + } +} \ No newline at end of file diff --git a/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts new file mode 100644 index 000000000..3ec89ff62 --- /dev/null +++ b/client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts @@ -0,0 +1,219 @@ +import { HttpEventType, HttpResponse } from '@angular/common/http' +import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' +import { Router } from '@angular/router' +import { LoadingBarService } from '@ngx-loading-bar/core' +import { NotificationsService } from 'angular2-notifications' +import { BytesPipe } from 'ngx-pipes' +import { Subscription } from 'rxjs' +import { VideoPrivacy } from '../../../../../../shared/models/videos' +import { AuthService, ServerService } from '../../../core' +import { VideoEdit } from '../../../shared/video/video-edit.model' +import { VideoService } from '../../../shared/video/video.service' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { VideoSend } from '@app/videos/+video-edit/video-add-components/video-send' +import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' +import { FormValidatorService, UserService } from '@app/shared' +import { VideoCaptionService } from '@app/shared/video-caption' + +@Component({ + selector: 'my-video-upload', + templateUrl: './video-upload.component.html', + styleUrls: [ + '../shared/video-edit.component.scss', + './video-upload.component.scss' + ] +}) +export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate { + @Output() firstStepDone = new EventEmitter() + @ViewChild('videofileInput') videofileInput + + // So that it can be accessed in the template + readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY + + userVideoQuotaUsed = 0 + + isUploadingVideo = false + isUpdatingVideo = false + videoUploaded = false + videoUploadObservable: Subscription = null + videoUploadPercents = 0 + videoUploadedIds = { + id: 0, + uuid: '' + } + + protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC + + constructor ( + protected formValidatorService: FormValidatorService, + protected loadingBar: LoadingBarService, + protected notificationsService: NotificationsService, + protected authService: AuthService, + protected serverService: ServerService, + protected videoService: VideoService, + protected videoCaptionService: VideoCaptionService, + private userService: UserService, + private router: Router, + private i18n: I18n + ) { + super() + } + + get videoExtensions () { + return this.serverService.getConfig().video.file.extensions.join(',') + } + + ngOnInit () { + super.ngOnInit() + + this.userService.getMyVideoQuotaUsed() + .subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed) + } + + ngOnDestroy () { + if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe() + } + + canDeactivate () { + let text = '' + + if (this.videoUploaded === true) { + // FIXME: cannot concatenate strings inside i18n service :/ + text = this.i18n('Your video was uploaded to your account and is private.') + + this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?') + } else { + text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?') + } + + return { + canDeactivate: !this.isUploadingVideo, + text + } + } + + fileChange () { + this.uploadFirstStep() + } + + cancelUpload () { + if (this.videoUploadObservable !== null) { + this.videoUploadObservable.unsubscribe() + this.isUploadingVideo = false + this.videoUploadPercents = 0 + this.videoUploadObservable = null + this.notificationsService.info(this.i18n('Info'), this.i18n('Upload cancelled')) + } + } + + uploadFirstStep () { + const videofile = this.videofileInput.nativeElement.files[0] as File + if (!videofile) return + + // Cannot upload videos > 8GB for now + if (videofile.size > 8 * 1024 * 1024 * 1024) { + this.notificationsService.error(this.i18n('Error'), this.i18n('We are sorry but PeerTube cannot handle videos > 8GB')) + return + } + + const videoQuota = this.authService.getUser().videoQuota + if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { + const bytePipes = new BytesPipe() + + const msg = this.i18n( + 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})', + { + videoSize: bytePipes.transform(videofile.size, 0), + videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), + videoQuota: bytePipes.transform(videoQuota, 0) + } + ) + this.notificationsService.error(this.i18n('Error'), msg) + return + } + + const nameWithoutExtension = videofile.name.replace(/\.[^/.]+$/, '') + let name: string + + // If the name of the file is very small, keep the extension + if (nameWithoutExtension.length < 3) name = videofile.name + else name = nameWithoutExtension + + const privacy = this.firstStepPrivacyId.toString() + const nsfw = false + const waitTranscoding = true + const commentsEnabled = true + const channelId = this.firstStepChannelId.toString() + + const formData = new FormData() + formData.append('name', name) + // Put the video "private" -> we are waiting the user validation of the second step + formData.append('privacy', VideoPrivacy.PRIVATE.toString()) + formData.append('nsfw', '' + nsfw) + formData.append('commentsEnabled', '' + commentsEnabled) + formData.append('waitTranscoding', '' + waitTranscoding) + formData.append('channelId', '' + channelId) + formData.append('videofile', videofile) + + this.isUploadingVideo = true + this.firstStepDone.emit(name) + + this.form.patchValue({ + name, + privacy, + nsfw, + channelId + }) + + this.videoUploadObservable = this.videoService.uploadVideo(formData).subscribe( + event => { + if (event.type === HttpEventType.UploadProgress) { + this.videoUploadPercents = Math.round(100 * event.loaded / event.total) + } else if (event instanceof HttpResponse) { + this.videoUploaded = true + + this.videoUploadedIds = event.body.video + + this.videoUploadObservable = null + } + }, + + err => { + // Reset progress + this.isUploadingVideo = false + this.videoUploadPercents = 0 + this.videoUploadObservable = null + this.notificationsService.error(this.i18n('Error'), err.message) + } + ) + } + + updateSecondStep () { + if (this.checkForm() === false) { + return + } + + const video = new VideoEdit() + video.patch(this.form.value) + video.id = this.videoUploadedIds.id + video.uuid = this.videoUploadedIds.uuid + + this.isUpdatingVideo = true + + this.updateVideoAndCaptions(video) + .subscribe( + () => { + this.isUpdatingVideo = false + this.isUploadingVideo = false + + this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.')) + this.router.navigate([ '/videos/watch', video.uuid ]) + }, + + err => { + this.isUpdatingVideo = false + this.notificationsService.error(this.i18n('Error'), err.message) + console.error(err) + } + ) + } +} diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts index 377ea5dd2..e74fa1f15 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -1,7 +1,7 @@ import { Component, ViewChild } from '@angular/core' import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' -import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-import-url.component' -import { VideoUploadComponent } from '@app/videos/+video-edit/video-upload.component' +import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component' +import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component' import { ServerService } from '@app/core' @Component({ diff --git a/client/src/app/videos/+video-edit/video-add.module.ts b/client/src/app/videos/+video-edit/video-add.module.ts index dd1a3875d..a1324b397 100644 --- a/client/src/app/videos/+video-edit/video-add.module.ts +++ b/client/src/app/videos/+video-edit/video-add.module.ts @@ -5,8 +5,8 @@ import { VideoEditModule } from './shared/video-edit.module' import { VideoAddRoutingModule } from './video-add-routing.module' import { VideoAddComponent } from './video-add.component' import { CanDeactivateGuard } from '../../shared/guards/can-deactivate-guard.service' -import { VideoUploadComponent } from '@app/videos/+video-edit/video-upload.component' -import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-import-url.component' +import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component' +import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component' @NgModule({ imports: [ diff --git a/client/src/app/videos/+video-edit/video-import-url.component.html b/client/src/app/videos/+video-edit/video-import-url.component.html deleted file mode 100644 index 6b431f6f6..000000000 --- a/client/src/app/videos/+video-edit/video-import-url.component.html +++ /dev/null @@ -1,60 +0,0 @@ -
-
-
- -
- - - - -
- -
- -
- -
-
- -
- -
- -
-
- - -
-
- -
- Congratulations, the video behind {{ targetUrl }} will be imported! You can already add information about this video. -
- - -
- - -
-
- - -
-
-
diff --git a/client/src/app/videos/+video-edit/video-import-url.component.scss b/client/src/app/videos/+video-edit/video-import-url.component.scss deleted file mode 100644 index 9ada9db19..000000000 --- a/client/src/app/videos/+video-edit/video-import-url.component.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import '_variables'; -@import '_mixins'; - -$width-size: 190px; - -.peertube-select-container { - @include peertube-select-container($width-size); -} - -.import-video { - display: flex; - flex-direction: column; - align-items: center; - - .icon.icon-upload { - @include icon(90px); - margin-bottom: 25px; - cursor: default; - - background-image: url('../../../assets/images/video/upload.svg'); - } - - input[type=text] { - @include peertube-input-text($width-size); - display: block; - } - - input[type=button] { - @include peertube-button; - @include orange-button; - - width: $width-size; - margin-top: 30px; - } -} - - diff --git a/client/src/app/videos/+video-edit/video-import-url.component.ts b/client/src/app/videos/+video-edit/video-import-url.component.ts deleted file mode 100644 index 99fcb6f00..000000000 --- a/client/src/app/videos/+video-edit/video-import-url.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core' -import { Router } from '@angular/router' -import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' -import { NotificationsService } from 'angular2-notifications' -import { VideoPrivacy, VideoUpdate } from '../../../../../shared/models/videos' -import { AuthService, ServerService } from '../../core' -import { VideoService } from '../../shared/video/video.service' -import { I18n } from '@ngx-translate/i18n-polyfill' -import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' -import { VideoImportService } from '@app/shared/video-import' -import { VideoEdit } from '@app/shared/video/video-edit.model' -import { LoadingBarService } from '@ngx-loading-bar/core' -import { VideoCaptionService } from '@app/shared/video-caption' -import { VideoSend } from '@app/videos/+video-edit/shared/video-send' - -@Component({ - selector: 'my-video-import-url', - templateUrl: './video-import-url.component.html', - styleUrls: [ - './shared/video-edit.component.scss', - './video-import-url.component.scss' - ] -}) -export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate { - @Output() firstStepDone = new EventEmitter() - - targetUrl = '' - videoFileName: string - - isImportingVideo = false - hasImportedVideo = false - isUpdatingVideo = false - - video: VideoEdit - - protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PRIVATE - - constructor ( - protected formValidatorService: FormValidatorService, - protected loadingBar: LoadingBarService, - protected notificationsService: NotificationsService, - protected authService: AuthService, - protected serverService: ServerService, - protected videoService: VideoService, - protected videoCaptionService: VideoCaptionService, - private router: Router, - private videoImportService: VideoImportService, - private i18n: I18n - ) { - super() - } - - ngOnInit () { - super.ngOnInit() - } - - canDeactivate () { - return { canDeactivate: true } - } - - isTargetUrlValid () { - return this.targetUrl && this.targetUrl.match(/https?:\/\//) - } - - importVideo () { - this.isImportingVideo = true - - const videoUpdate: VideoUpdate = { - privacy: this.firstStepPrivacyId, - waitTranscoding: false, - commentsEnabled: true, - channelId: this.firstStepChannelId - } - - this.loadingBar.start() - - this.videoImportService.importVideo(this.targetUrl, videoUpdate).subscribe( - res => { - this.loadingBar.complete() - this.firstStepDone.emit(res.video.name) - this.isImportingVideo = false - this.hasImportedVideo = true - - this.video = new VideoEdit(Object.assign(res.video, { - commentsEnabled: videoUpdate.commentsEnabled, - support: null, - thumbnailUrl: null, - previewUrl: null - })) - this.hydrateFormFromVideo() - }, - - err => { - this.loadingBar.complete() - this.isImportingVideo = false - this.notificationsService.error(this.i18n('Error'), err.message) - } - ) - } - - updateSecondStep () { - if (this.checkForm() === false) { - return - } - - this.video.patch(this.form.value) - - this.isUpdatingVideo = true - - // Update the video - this.updateVideoAndCaptions(this.video) - .subscribe( - () => { - this.isUpdatingVideo = false - this.notificationsService.success(this.i18n('Success'), this.i18n('Video to import updated.')) - - this.router.navigate([ '/my-account', 'video-imports' ]) - }, - - err => { - this.isUpdatingVideo = false - this.notificationsService.error(this.i18n('Error'), err.message) - console.error(err) - } - ) - - } - - private hydrateFormFromVideo () { - this.form.patchValue(this.video.toFormPatch()) - } -} diff --git a/client/src/app/videos/+video-edit/video-upload.component.html b/client/src/app/videos/+video-edit/video-upload.component.html deleted file mode 100644 index 8c0723155..000000000 --- a/client/src/app/videos/+video-edit/video-upload.component.html +++ /dev/null @@ -1,58 +0,0 @@ -
-
-
- -
- Select the file to upload - -
- (.mp4, .webm, .ogv) - -
- -
- -
-
- -
- -
- -
-
-
-
- -
- - -
- - -
- - -
-
Publish will be available when upload is finished
- -
- - -
-
-
\ No newline at end of file diff --git a/client/src/app/videos/+video-edit/video-upload.component.scss b/client/src/app/videos/+video-edit/video-upload.component.scss deleted file mode 100644 index 015835672..000000000 --- a/client/src/app/videos/+video-edit/video-upload.component.scss +++ /dev/null @@ -1,85 +0,0 @@ -@import '_variables'; -@import '_mixins'; - -.peertube-select-container { - @include peertube-select-container(190px); -} - -.upload-video { - display: flex; - flex-direction: column; - align-items: center; - - .form-group-channel { - margin-bottom: 20px; - margin-top: 35px; - } - - .icon.icon-upload { - @include icon(90px); - margin-bottom: 25px; - cursor: default; - - background-image: url('../../../assets/images/video/upload.svg'); - } - - .button-file { - @include peertube-button-file(auto); - - min-width: 190px; - } - - .button-file-extension { - display: block; - font-size: 12px; - margin-top: 5px; - } -} - -.upload-progress-cancel { - display: flex; - margin-top: 25px; - margin-bottom: 40px; - - p-progressBar { - flex-grow: 1; - - /deep/ .ui-progressbar { - font-size: 15px !important; - color: #fff !important; - height: 30px !important; - line-height: 30px !important; - border-radius: 3px !important; - background-color: rgba(11, 204, 41, 0.16) !important; - - .ui-progressbar-value { - background-color: #0BCC29 !important; - } - - .ui-progressbar-label { - text-align: left; - padding-left: 18px; - margin-top: 0 !important; - } - } - - &.processing { - /deep/ .ui-progressbar-label { - // Same color as background to hide "100%" - color: rgba(11, 204, 41, 0.16) !important; - - &::before { - content: 'Processing...'; - color: #fff; - } - } - } - } - - input { - @include peertube-button; - @include grey-button; - - margin-left: 10px; - } -} \ No newline at end of file diff --git a/client/src/app/videos/+video-edit/video-upload.component.ts b/client/src/app/videos/+video-edit/video-upload.component.ts deleted file mode 100644 index 983af60ce..000000000 --- a/client/src/app/videos/+video-edit/video-upload.component.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { HttpEventType, HttpResponse } from '@angular/common/http' -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core' -import { Router } from '@angular/router' -import { UserService } from '@app/shared' -import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service' -import { LoadingBarService } from '@ngx-loading-bar/core' -import { NotificationsService } from 'angular2-notifications' -import { BytesPipe } from 'ngx-pipes' -import { Subscription } from 'rxjs' -import { VideoPrivacy } from '../../../../../shared/models/videos' -import { AuthService, ServerService } from '../../core' -import { VideoEdit } from '../../shared/video/video-edit.model' -import { VideoService } from '../../shared/video/video.service' -import { I18n } from '@ngx-translate/i18n-polyfill' -import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' -import { VideoCaptionService } from '@app/shared/video-caption' -import { VideoSend } from '@app/videos/+video-edit/shared/video-send' - -@Component({ - selector: 'my-video-upload', - templateUrl: './video-upload.component.html', - styleUrls: [ - './shared/video-edit.component.scss', - './video-upload.component.scss' - ] -}) -export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate { - @Output() firstStepDone = new EventEmitter() - @ViewChild('videofileInput') videofileInput - - // So that it can be accessed in the template - readonly SPECIAL_SCHEDULED_PRIVACY = VideoEdit.SPECIAL_SCHEDULED_PRIVACY - - userVideoQuotaUsed = 0 - - isUploadingVideo = false - isUpdatingVideo = false - videoUploaded = false - videoUploadObservable: Subscription = null - videoUploadPercents = 0 - videoUploadedIds = { - id: 0, - uuid: '' - } - - protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC - - constructor ( - protected formValidatorService: FormValidatorService, - protected loadingBar: LoadingBarService, - protected notificationsService: NotificationsService, - protected authService: AuthService, - protected serverService: ServerService, - protected videoService: VideoService, - protected videoCaptionService: VideoCaptionService, - private userService: UserService, - private router: Router, - private i18n: I18n - ) { - super() - } - - get videoExtensions () { - return this.serverService.getConfig().video.file.extensions.join(',') - } - - ngOnInit () { - super.ngOnInit() - - this.userService.getMyVideoQuotaUsed() - .subscribe(data => this.userVideoQuotaUsed = data.videoQuotaUsed) - } - - ngOnDestroy () { - if (this.videoUploadObservable) this.videoUploadObservable.unsubscribe() - } - - canDeactivate () { - let text = '' - - if (this.videoUploaded === true) { - // FIXME: cannot concatenate strings inside i18n service :/ - text = this.i18n('Your video was uploaded to your account and is private.') + - this.i18n('But associated data (tags, description...) will be lost, are you sure you want to leave this page?') - } else { - text = this.i18n('Your video is not uploaded yet, are you sure you want to leave this page?') - } - - return { - canDeactivate: !this.isUploadingVideo, - text - } - } - - fileChange () { - this.uploadFirstStep() - } - - cancelUpload () { - if (this.videoUploadObservable !== null) { - this.videoUploadObservable.unsubscribe() - this.isUploadingVideo = false - this.videoUploadPercents = 0 - this.videoUploadObservable = null - this.notificationsService.info(this.i18n('Info'), this.i18n('Upload cancelled')) - } - } - - uploadFirstStep () { - const videofile = this.videofileInput.nativeElement.files[0] as File - if (!videofile) return - - // Cannot upload videos > 8GB for now - if (videofile.size > 8 * 1024 * 1024 * 1024) { - this.notificationsService.error(this.i18n('Error'), this.i18n('We are sorry but PeerTube cannot handle videos > 8GB')) - return - } - - const videoQuota = this.authService.getUser().videoQuota - if (videoQuota !== -1 && (this.userVideoQuotaUsed + videofile.size) > videoQuota) { - const bytePipes = new BytesPipe() - - const msg = this.i18n( - 'Your video quota is exceeded with this video (video size: {{ videoSize }}, used: {{ videoQuotaUsed }}, quota: {{ videoQuota }})', - { - videoSize: bytePipes.transform(videofile.size, 0), - videoQuotaUsed: bytePipes.transform(this.userVideoQuotaUsed, 0), - videoQuota: bytePipes.transform(videoQuota, 0) - } - ) - this.notificationsService.error(this.i18n('Error'), msg) - return - } - - const nameWithoutExtension = videofile.name.replace(/\.[^/.]+$/, '') - let name: string - - // If the name of the file is very small, keep the extension - if (nameWithoutExtension.length < 3) name = videofile.name - else name = nameWithoutExtension - - const privacy = this.firstStepPrivacyId.toString() - const nsfw = false - const waitTranscoding = true - const commentsEnabled = true - const channelId = this.firstStepChannelId.toString() - - const formData = new FormData() - formData.append('name', name) - // Put the video "private" -> we are waiting the user validation of the second step - formData.append('privacy', VideoPrivacy.PRIVATE.toString()) - formData.append('nsfw', '' + nsfw) - formData.append('commentsEnabled', '' + commentsEnabled) - formData.append('waitTranscoding', '' + waitTranscoding) - formData.append('channelId', '' + channelId) - formData.append('videofile', videofile) - - this.isUploadingVideo = true - this.firstStepDone.emit(name) - - this.form.patchValue({ - name, - privacy, - nsfw, - channelId - }) - - this.videoUploadObservable = this.videoService.uploadVideo(formData).subscribe( - event => { - if (event.type === HttpEventType.UploadProgress) { - this.videoUploadPercents = Math.round(100 * event.loaded / event.total) - } else if (event instanceof HttpResponse) { - this.videoUploaded = true - - this.videoUploadedIds = event.body.video - - this.videoUploadObservable = null - } - }, - - err => { - // Reset progress - this.isUploadingVideo = false - this.videoUploadPercents = 0 - this.videoUploadObservable = null - this.notificationsService.error(this.i18n('Error'), err.message) - } - ) - } - - updateSecondStep () { - if (this.checkForm() === false) { - return - } - - const video = new VideoEdit() - video.patch(this.form.value) - video.id = this.videoUploadedIds.id - video.uuid = this.videoUploadedIds.uuid - - this.isUpdatingVideo = true - - this.updateVideoAndCaptions(video) - .subscribe( - () => { - this.isUpdatingVideo = false - this.isUploadingVideo = false - - this.notificationsService.success(this.i18n('Success'), this.i18n('Video published.')) - this.router.navigate([ '/videos/watch', video.uuid ]) - }, - - err => { - this.isUpdatingVideo = false - this.notificationsService.error(this.i18n('Error'), err.message) - console.error(err) - } - ) - } -}