Improve video upload error handling
authorChocobozzz <me@florianbigard.com>
Fri, 16 Nov 2018 09:05:25 +0000 (10:05 +0100)
committerChocobozzz <me@florianbigard.com>
Fri, 16 Nov 2018 09:05:25 +0000 (10:05 +0100)
14 files changed:
client/src/app/shared/misc/utils.ts
client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html
client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss
client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts
client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html
client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss
client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
client/src/app/videos/+video-edit/video-add-components/video-send.ts
client/src/app/videos/+video-edit/video-add-components/video-upload.component.html
client/src/app/videos/+video-edit/video-add-components/video-upload.component.scss
client/src/app/videos/+video-edit/video-add-components/video-upload.component.ts
client/src/app/videos/+video-edit/video-add.component.html
client/src/app/videos/+video-edit/video-add.component.ts
server/middlewares/validators/videos/videos.ts

index 78be2e5dda6bd0435a757bd08b61b0bd13ee1df4..78e8e968250951bec4c61f5c291e2aa37328628f 100644 (file)
@@ -124,6 +124,10 @@ function sortBy (obj: any[], key1: string, key2?: string) {
   })
 }
 
+function scrollToTop () {
+  window.scroll(0, 0)
+}
+
 export {
   sortBy,
   durationToString,
@@ -135,5 +139,6 @@ export {
   immutableAssign,
   objectToFormData,
   lineFeedToHtml,
-  removeElementFromArray
+  removeElementFromArray,
+  scrollToTop
 }
index a933a64f0df25c08a2312b345560a0a24fc8c2f5..11a81ad66f74dfccfca572cd490a8f4b331eb830 100644 (file)
   </div>
 </div>
 
-<div *ngIf="hasImportedVideo" class="alert alert-info" i18n>
+<div *ngIf="error" class="alert alert-danger">
+  <div i18n>Sorry, but something went wrong</div>
+  {{ error }}
+</div>
+
+<div *ngIf="hasImportedVideo && !error" class="alert alert-info" i18n>
   Congratulations, the video will be imported with BitTorrent! You can already add information about this video.
 </div>
 
index 262b0b68e513c17cc999c23ca939924593f6019d..00626cd7b3ec601f9fb93c61bf252a59dbb83182 100644 (file)
@@ -7,6 +7,14 @@ $width-size: 190px;
   @include peertube-select-container($width-size);
 }
 
+.alert.alert-danger {
+  text-align: center;
+
+  & > div {
+    font-weight: $font-semibold;
+  }
+}
+
 .import-video-torrent {
   display: flex;
   flex-direction: column;
index e13c06ce99638b12df1c9452fbd80878428b55f8..13776ae36748161b90ff726aa5edb157c2cfafc2 100644 (file)
@@ -12,6 +12,7 @@ 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'
+import { scrollToTop } from '@app/shared/misc/utils'
 
 @Component({
   selector: 'my-video-import-torrent',
@@ -23,9 +24,9 @@ import { VideoImportService } from '@app/shared/video-import'
 })
 export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate {
   @Output() firstStepDone = new EventEmitter<string>()
+  @Output() firstStepError = new EventEmitter<void>()
   @ViewChild('torrentfileInput') torrentfileInput: ElementRef<HTMLInputElement>
 
-  videoFileName: string
   magnetUri = ''
 
   isImportingVideo = false
@@ -33,6 +34,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
   isUpdatingVideo = false
 
   video: VideoEdit
+  error: string
 
   protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC
 
@@ -104,6 +106,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
       err => {
         this.loadingBar.complete()
         this.isImportingVideo = false
+        this.firstStepError.emit()
         this.notificationsService.error(this.i18n('Error'), err.message)
       }
     )
@@ -129,8 +132,8 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Ca
           },
 
           err => {
-            this.isUpdatingVideo = false
-            this.notificationsService.error(this.i18n('Error'), err.message)
+            this.error = err.message
+            scrollToTop()
             console.error(err)
           }
         )
index 9f5fc6d22b6349d5a4293c923d4ece7dac3c5b78..533446672fd302bd82422f0cc295fc0752f8e211 100644 (file)
   </div>
 </div>
 
-<div *ngIf="hasImportedVideo" class="alert alert-info" i18n>
+
+<div *ngIf="error" class="alert alert-danger">
+  <div i18n>Sorry, but something went wrong</div>
+  {{ error }}
+</div>
+
+<div *ngIf="!error && hasImportedVideo" class="alert alert-info" i18n>
   Congratulations, the video behind {{ targetUrl }} will be imported! You can already add information about this video.
 </div>
 
index 7c6deda1d22fe542a9d09bd33505368c9374cb74..e907edc7035fc926b77fcf80f02ca58228e34084 100644 (file)
@@ -7,6 +7,14 @@ $width-size: 190px;
   @include peertube-select-container($width-size);
 }
 
+.alert.alert-danger {
+  text-align: center;
+
+  & > div {
+    font-weight: $font-semibold;
+  }
+}
+
 .import-video-url {
   display: flex;
   flex-direction: column;
index 031e557ed688b8dd6bb80c569ec19266a2a86d6e..9cdface7519dcf09e6730db2f1b3fc8a4ed97761 100644 (file)
@@ -12,6 +12,7 @@ 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'
+import { scrollToTop } from '@app/shared/misc/utils'
 
 @Component({
   selector: 'my-video-import-url',
@@ -23,15 +24,16 @@ import { VideoImportService } from '@app/shared/video-import'
 })
 export class VideoImportUrlComponent extends VideoSend implements OnInit, CanComponentDeactivate {
   @Output() firstStepDone = new EventEmitter<string>()
+  @Output() firstStepError = new EventEmitter<void>()
 
   targetUrl = ''
-  videoFileName: string
 
   isImportingVideo = false
   hasImportedVideo = false
   isUpdatingVideo = false
 
   video: VideoEdit
+  error: string
 
   protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC
 
@@ -96,6 +98,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
       err => {
         this.loadingBar.complete()
         this.isImportingVideo = false
+        this.firstStepError.emit()
         this.notificationsService.error(this.i18n('Error'), err.message)
       }
     )
@@ -121,8 +124,8 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
           },
 
           err => {
-            this.isUpdatingVideo = false
-            this.notificationsService.error(this.i18n('Error'), err.message)
+            this.error = err.message
+            scrollToTop()
             console.error(err)
           }
         )
index 1bf22e1a93aa4b39171916e0c80766f5e80d4fde..71d2544d812f4cad2a6cd162b5143f34399939bf 100644 (file)
@@ -21,6 +21,7 @@ export abstract class VideoSend extends FormReactive implements OnInit {
   firstStepChannelId = 0
 
   abstract firstStepDone: EventEmitter<string>
+  abstract firstStepError: EventEmitter<void>
   protected abstract readonly DEFAULT_VIDEO_PRIVACY: VideoPrivacy
 
   protected loadingBar: LoadingBarService
index fa57c8cb58f0e93056c47d701e897d657cbc05a8..a09f54dfcce48ad277e4a1fee4e12ebf8ccaadd8 100644 (file)
@@ -29,7 +29,7 @@
   </div>
 </div>
 
-<div *ngIf="isUploadingVideo" class="upload-progress-cancel">
+<div *ngIf="isUploadingVideo && !error" class="upload-progress-cancel">
   <p-progressBar
     [value]="videoUploadPercents"
     [ngClass]="{ processing: videoUploadPercents === 100 && videoUploaded === false }"
   <input *ngIf="videoUploaded === false" type="button" value="Cancel" (click)="cancelUpload()" />
 </div>
 
+<div *ngIf="error" class="alert alert-danger">
+  <div i18n>Sorry, but something went wrong</div>
+  {{ error }}
+</div>
+
 <!-- Hidden because we want to load the component -->
 <form [hidden]="!isUploadingVideo" novalidate [formGroup]="form">
   <my-video-edit
@@ -55,4 +60,4 @@
       <input [disabled]="isPublishingButtonDisabled()" type="button" i18n-value value="Publish" />
     </div>
   </div>
-</form>
\ No newline at end of file
+</form>
index dbae5230d0f7d955f5c437f9bdb5ab6b62dd6ca7..cf1725ef9ee22a37affb1311a8a8f9984bfee8b2 100644 (file)
@@ -5,6 +5,14 @@
   @include peertube-select-container(190px);
 }
 
+.alert.alert-danger {
+  text-align: center;
+
+  & > div {
+    font-weight: $font-semibold;
+  }
+}
+
 .upload-video {
   display: flex;
   flex-direction: column;
@@ -82,4 +90,4 @@
 
     margin-left: 10px;
   }
-}
\ No newline at end of file
+}
index 8e2d0deaff7b2c5290026a0633a0bc6fd37f998d..3fcb71ac3440f58e3a6b16bf30a63ee547bd26a5 100644 (file)
@@ -14,6 +14,7 @@ import { VideoSend } from '@app/videos/+video-edit/video-add-components/video-se
 import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service'
 import { FormValidatorService, UserService } from '@app/shared'
 import { VideoCaptionService } from '@app/shared/video-caption'
+import { scrollToTop } from '@app/shared/misc/utils'
 
 @Component({
   selector: 'my-video-upload',
@@ -25,6 +26,7 @@ import { VideoCaptionService } from '@app/shared/video-caption'
 })
 export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy, CanComponentDeactivate {
   @Output() firstStepDone = new EventEmitter<string>()
+  @Output() firstStepError = new EventEmitter<void>()
   @ViewChild('videofileInput') videofileInput: ElementRef<HTMLInputElement>
 
   // So that it can be accessed in the template
@@ -43,6 +45,8 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
     uuid: ''
   }
 
+  error: string
+
   protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PUBLIC
 
   constructor (
@@ -201,6 +205,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
         this.isUploadingVideo = false
         this.videoUploadPercents = 0
         this.videoUploadObservable = null
+        this.firstStepError.emit()
         this.notificationsService.error(this.i18n('Error'), err.message)
       }
     )
@@ -235,8 +240,8 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
           },
 
           err => {
-            this.isUpdatingVideo = false
-            this.notificationsService.error(this.i18n('Error'), err.message)
+            this.error = err.message
+            scrollToTop()
             console.error(err)
           }
         )
index e14e23aeded894f6ffec55a68e2e2e11db278aff..72a233b722c1bf17e800bb4b1b8f97a905415590 100644 (file)
@@ -6,24 +6,24 @@
 
   <ngb-tabset class="video-add-tabset root-tabset bootstrap" [ngClass]="{ 'hide-nav': secondStepType !== undefined }">
 
-    <ngb-tab i18n-title title="">
+    <ngb-tab>
       <ng-template ngbTabTitle><span i18n>Upload a file</span></ng-template>
       <ng-template ngbTabContent>
-        <my-video-upload #videoUpload (firstStepDone)="onFirstStepDone('upload', $event)"></my-video-upload>
+        <my-video-upload #videoUpload (firstStepDone)="onFirstStepDone('upload', $event)" (firstStepError)="onError()"></my-video-upload>
       </ng-template>
     </ngb-tab>
 
     <ngb-tab *ngIf="isVideoImportHttpEnabled()">
       <ng-template ngbTabTitle><span i18n>Import with URL</span></ng-template>
       <ng-template ngbTabContent>
-        <my-video-import-url #videoImportUrl (firstStepDone)="onFirstStepDone('import-url', $event)"></my-video-import-url>
+        <my-video-import-url #videoImportUrl (firstStepDone)="onFirstStepDone('import-url', $event)" (firstStepError)="onError()"></my-video-import-url>
       </ng-template>
     </ngb-tab>
 
     <ngb-tab *ngIf="isVideoImportTorrentEnabled()">
       <ng-template ngbTabTitle><span i18n>Import with torrent</span></ng-template>
       <ng-template ngbTabContent>
-        <my-video-import-torrent #videoImportTorrent (firstStepDone)="onFirstStepDone('import-torrent', $event)"></my-video-import-torrent>
+        <my-video-import-torrent #videoImportTorrent (firstStepDone)="onFirstStepDone('import-torrent', $event)" (firstStepError)="onError()"></my-video-import-torrent>
       </ng-template>
     </ngb-tab>
   </ngb-tabset>
index 1a9247dbea4a34b4497186d5588fedb6a1237141..57a9d0ca7abd4d2d541f490a74b4ff44f50d9c84 100644 (file)
@@ -27,6 +27,11 @@ export class VideoAddComponent implements CanComponentDeactivate {
     this.videoName = videoName
   }
 
+  onError () {
+    this.videoName = undefined
+    this.secondStepType = undefined
+  }
+
   canDeactivate () {
     if (this.secondStepType === 'upload') return this.videoUpload.canDeactivate()
     if (this.secondStepType === 'import-url') return this.videoImportUrl.canDeactivate()
index 656d161d8ab5b14d731aa76e9f3326b696ed3557..bf21bca8c6ae1c3b151b0750206f12658fc8f739 100644 (file)
@@ -393,6 +393,8 @@ export {
 function areErrorsInScheduleUpdate (req: express.Request, res: express.Response) {
   if (req.body.scheduleUpdate) {
     if (!req.body.scheduleUpdate.updateAt) {
+      logger.warn('Invalid parameters: scheduleUpdate.updateAt is mandatory.')
+
       res.status(400)
          .json({ error: 'Schedule update at is mandatory.' })