diff options
20 files changed, 189 insertions, 163 deletions
diff --git a/client/package.json b/client/package.json index caec34e44..8a82a294e 100644 --- a/client/package.json +++ b/client/package.json | |||
@@ -61,7 +61,6 @@ | |||
61 | "intl": "^1.2.4", | 61 | "intl": "^1.2.4", |
62 | "json-loader": "^0.5.4", | 62 | "json-loader": "^0.5.4", |
63 | "ng-router-loader": "^2.0.0", | 63 | "ng-router-loader": "^2.0.0", |
64 | "ng2-file-upload": "^1.1.4-2", | ||
65 | "ngc-webpack": "3.2.2", | 64 | "ngc-webpack": "3.2.2", |
66 | "ngx-bootstrap": "1.9.1", | 65 | "ngx-bootstrap": "1.9.1", |
67 | "ngx-chips": "1.5.3", | 66 | "ngx-chips": "1.5.3", |
diff --git a/client/src/app/+admin/friends/friend-add/friend-add.component.ts b/client/src/app/+admin/friends/friend-add/friend-add.component.ts index e0b73dfa3..6580e1b88 100644 --- a/client/src/app/+admin/friends/friend-add/friend-add.component.ts +++ b/client/src/app/+admin/friends/friend-add/friend-add.component.ts | |||
@@ -98,7 +98,7 @@ export class FriendAddComponent implements OnInit { | |||
98 | setTimeout(() => this.router.navigate([ '/admin/friends/list' ]), 1000) | 98 | setTimeout(() => this.router.navigate([ '/admin/friends/list' ]), 1000) |
99 | }, | 99 | }, |
100 | 100 | ||
101 | err => this.notificationsService.error('Error', err) | 101 | err => this.notificationsService.error('Error', err.message) |
102 | ) | 102 | ) |
103 | } | 103 | } |
104 | ) | 104 | ) |
diff --git a/client/src/app/+admin/friends/friend-list/friend-list.component.ts b/client/src/app/+admin/friends/friend-list/friend-list.component.ts index 6a8bd492c..4af39c47e 100644 --- a/client/src/app/+admin/friends/friend-list/friend-list.component.ts +++ b/client/src/app/+admin/friends/friend-list/friend-list.component.ts | |||
@@ -40,7 +40,7 @@ export class FriendListComponent implements OnInit { | |||
40 | this.loadData() | 40 | this.loadData() |
41 | }, | 41 | }, |
42 | 42 | ||
43 | err => this.notificationsService.error('Error', err) | 43 | err => this.notificationsService.error('Error', err.message) |
44 | ) | 44 | ) |
45 | } | 45 | } |
46 | ) | 46 | ) |
@@ -59,7 +59,7 @@ export class FriendListComponent implements OnInit { | |||
59 | this.loadData() | 59 | this.loadData() |
60 | }, | 60 | }, |
61 | 61 | ||
62 | err => this.notificationsService.error('Error', err) | 62 | err => this.notificationsService.error('Error', err.message) |
63 | ) | 63 | ) |
64 | } | 64 | } |
65 | ) | 65 | ) |
@@ -72,7 +72,7 @@ export class FriendListComponent implements OnInit { | |||
72 | this.friends = resultList.data | 72 | this.friends = resultList.data |
73 | }, | 73 | }, |
74 | 74 | ||
75 | err => this.notificationsService.error('Error', err) | 75 | err => this.notificationsService.error('Error', err.message) |
76 | ) | 76 | ) |
77 | } | 77 | } |
78 | } | 78 | } |
diff --git a/client/src/app/+admin/request-schedulers/request-schedulers-stats/request-schedulers-stats.component.ts b/client/src/app/+admin/request-schedulers/request-schedulers-stats/request-schedulers-stats.component.ts index 1edeb31fe..1654827ab 100644 --- a/client/src/app/+admin/request-schedulers/request-schedulers-stats/request-schedulers-stats.component.ts +++ b/client/src/app/+admin/request-schedulers/request-schedulers-stats/request-schedulers-stats.component.ts | |||
@@ -57,7 +57,7 @@ export class RequestSchedulersStatsComponent implements OnInit, OnDestroy { | |||
57 | this.requestService.getStats().subscribe( | 57 | this.requestService.getStats().subscribe( |
58 | stats => this.stats = stats, | 58 | stats => this.stats = stats, |
59 | 59 | ||
60 | err => this.notificationsService.error('Error', err) | 60 | err => this.notificationsService.error('Error', err.message) |
61 | ) | 61 | ) |
62 | } | 62 | } |
63 | 63 | ||
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts index c3fa55825..73b5c0ce9 100644 --- a/client/src/app/+admin/users/user-list/user-list.component.ts +++ b/client/src/app/+admin/users/user-list/user-list.component.ts | |||
@@ -47,7 +47,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
47 | this.loadData() | 47 | this.loadData() |
48 | }, | 48 | }, |
49 | 49 | ||
50 | err => this.notificationsService.error('Error', err) | 50 | err => this.notificationsService.error('Error', err.message) |
51 | ) | 51 | ) |
52 | } | 52 | } |
53 | ) | 53 | ) |
@@ -65,7 +65,7 @@ export class UserListComponent extends RestTable implements OnInit { | |||
65 | this.totalRecords = resultList.total | 65 | this.totalRecords = resultList.total |
66 | }, | 66 | }, |
67 | 67 | ||
68 | err => this.notificationsService.error('Error', err) | 68 | err => this.notificationsService.error('Error', err.message) |
69 | ) | 69 | ) |
70 | } | 70 | } |
71 | } | 71 | } |
diff --git a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts index 5a0131a81..92de11f0e 100644 --- a/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/video-abuses/video-abuse-list/video-abuse-list.component.ts | |||
@@ -40,7 +40,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { | |||
40 | this.totalRecords = resultList.total | 40 | this.totalRecords = resultList.total |
41 | }, | 41 | }, |
42 | 42 | ||
43 | err => this.notificationsService.error('Error', err) | 43 | err => this.notificationsService.error('Error', err.message) |
44 | ) | 44 | ) |
45 | } | 45 | } |
46 | } | 46 | } |
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 57bf64f69..ae86bc96f 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -31,8 +31,7 @@ export class AppComponent implements OnInit { | |||
31 | private authService: AuthService, | 31 | private authService: AuthService, |
32 | private configService: ConfigService, | 32 | private configService: ConfigService, |
33 | private userService: UserService, | 33 | private userService: UserService, |
34 | private videoService: VideoService, | 34 | private videoService: VideoService |
35 | viewContainerRef: ViewContainerRef | ||
36 | ) {} | 35 | ) {} |
37 | 36 | ||
38 | ngOnInit () { | 37 | ngOnInit () { |
diff --git a/client/src/app/shared/forms/form-validators/video.ts b/client/src/app/shared/forms/form-validators/video.ts index 6542cf8f6..32b1f1c2e 100644 --- a/client/src/app/shared/forms/form-validators/video.ts +++ b/client/src/app/shared/forms/form-validators/video.ts | |||
@@ -44,3 +44,10 @@ export const VIDEO_TAGS = { | |||
44 | 'maxlength': 'A tag should be less than 10 characters long.' | 44 | 'maxlength': 'A tag should be less than 10 characters long.' |
45 | } | 45 | } |
46 | } | 46 | } |
47 | |||
48 | export const VIDEO_FILE = { | ||
49 | VALIDATORS: [ Validators.required ], | ||
50 | MESSAGES: { | ||
51 | 'required': 'Video file is required.' | ||
52 | } | ||
53 | } | ||
diff --git a/client/src/app/shared/rest/rest-extractor.service.ts b/client/src/app/shared/rest/rest-extractor.service.ts index 32dad5c73..1f6222da8 100644 --- a/client/src/app/shared/rest/rest-extractor.service.ts +++ b/client/src/app/shared/rest/rest-extractor.service.ts | |||
@@ -40,19 +40,29 @@ export class RestExtractor { | |||
40 | handleError (err: HttpErrorResponse) { | 40 | handleError (err: HttpErrorResponse) { |
41 | let errorMessage | 41 | let errorMessage |
42 | 42 | ||
43 | console.log(err) | ||
44 | |||
43 | if (err.error instanceof Error) { | 45 | if (err.error instanceof Error) { |
44 | // A client-side or network error occurred. Handle it accordingly. | 46 | // A client-side or network error occurred. Handle it accordingly. |
45 | errorMessage = err.error.message | 47 | errorMessage = err.error.message |
46 | console.error('An error occurred:', errorMessage) | 48 | console.error('An error occurred:', errorMessage) |
47 | } else if (err.status !== undefined) { | 49 | } else if (err.status !== undefined) { |
48 | // The backend returned an unsuccessful response code. | 50 | const body = err.error |
49 | // The response body may contain clues as to what went wrong, | 51 | errorMessage = body.error |
50 | errorMessage = err.error | ||
51 | console.error(`Backend returned code ${err.status}, body was: ${errorMessage}`) | 52 | console.error(`Backend returned code ${err.status}, body was: ${errorMessage}`) |
52 | } else { | 53 | } else { |
53 | errorMessage = err | 54 | errorMessage = err |
54 | } | 55 | } |
55 | 56 | ||
56 | return Observable.throw(errorMessage) | 57 | const errorObj = { |
58 | message: errorMessage, | ||
59 | status: undefined | ||
60 | } | ||
61 | |||
62 | if (err.status) { | ||
63 | errorObj.status = err.status | ||
64 | } | ||
65 | |||
66 | return Observable.throw(errorObj) | ||
57 | } | 67 | } |
58 | } | 68 | } |
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 118ce822d..a28ac322d 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts | |||
@@ -10,7 +10,6 @@ import { BsDropdownModule } from 'ngx-bootstrap/dropdown' | |||
10 | import { ProgressbarModule } from 'ngx-bootstrap/progressbar' | 10 | import { ProgressbarModule } from 'ngx-bootstrap/progressbar' |
11 | import { PaginationModule } from 'ngx-bootstrap/pagination' | 11 | import { PaginationModule } from 'ngx-bootstrap/pagination' |
12 | import { ModalModule } from 'ngx-bootstrap/modal' | 12 | import { ModalModule } from 'ngx-bootstrap/modal' |
13 | import { FileUploadModule } from 'ng2-file-upload/ng2-file-upload' | ||
14 | import { DataTableModule, SharedModule as PrimeSharedModule } from 'primeng/primeng' | 13 | import { DataTableModule, SharedModule as PrimeSharedModule } from 'primeng/primeng' |
15 | 14 | ||
16 | import { AUTH_INTERCEPTOR_PROVIDER } from './auth' | 15 | import { AUTH_INTERCEPTOR_PROVIDER } from './auth' |
@@ -32,8 +31,6 @@ import { VideoAbuseService } from './video-abuse' | |||
32 | PaginationModule.forRoot(), | 31 | PaginationModule.forRoot(), |
33 | ProgressbarModule.forRoot(), | 32 | ProgressbarModule.forRoot(), |
34 | 33 | ||
35 | FileUploadModule, | ||
36 | |||
37 | DataTableModule, | 34 | DataTableModule, |
38 | PrimeSharedModule | 35 | PrimeSharedModule |
39 | ], | 36 | ], |
@@ -52,7 +49,6 @@ import { VideoAbuseService } from './video-abuse' | |||
52 | HttpClientModule, | 49 | HttpClientModule, |
53 | 50 | ||
54 | BsDropdownModule, | 51 | BsDropdownModule, |
55 | FileUploadModule, | ||
56 | ModalModule, | 52 | ModalModule, |
57 | PaginationModule, | 53 | PaginationModule, |
58 | ProgressbarModule, | 54 | ProgressbarModule, |
diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index b6d2a0666..cfce4cb16 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts | |||
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core' | |||
2 | import { Observable } from 'rxjs/Observable' | 2 | import { Observable } from 'rxjs/Observable' |
3 | import 'rxjs/add/operator/catch' | 3 | import 'rxjs/add/operator/catch' |
4 | import 'rxjs/add/operator/map' | 4 | import 'rxjs/add/operator/map' |
5 | import { HttpClient, HttpParams } from '@angular/common/http' | 5 | import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' |
6 | 6 | ||
7 | import { Search } from '../../shared' | 7 | import { Search } from '../../shared' |
8 | import { SortField } from './sort-field.type' | 8 | import { SortField } from './sort-field.type' |
@@ -14,13 +14,14 @@ import { | |||
14 | import { Video } from './video.model' | 14 | import { Video } from './video.model' |
15 | import { VideoPagination } from './video-pagination.model' | 15 | import { VideoPagination } from './video-pagination.model' |
16 | import { | 16 | import { |
17 | UserVideoRate, | 17 | VideoCreate, |
18 | VideoRateType, | 18 | UserVideoRate, |
19 | VideoUpdate, | 19 | VideoRateType, |
20 | VideoAbuseCreate, | 20 | VideoUpdate, |
21 | UserVideoRateUpdate, | 21 | VideoAbuseCreate, |
22 | Video as VideoServerModel, | 22 | UserVideoRateUpdate, |
23 | ResultList | 23 | Video as VideoServerModel, |
24 | ResultList | ||
24 | } from '../../../../../shared' | 25 | } from '../../../../../shared' |
25 | 26 | ||
26 | @Injectable() | 27 | @Injectable() |
@@ -73,6 +74,14 @@ export class VideoService { | |||
73 | .catch(this.restExtractor.handleError) | 74 | .catch(this.restExtractor.handleError) |
74 | } | 75 | } |
75 | 76 | ||
77 | // uploadVideo (video: VideoCreate) { | ||
78 | uploadVideo (video: any) { | ||
79 | const req = new HttpRequest('POST', `${VideoService.BASE_VIDEO_URL}/upload`, video, { reportProgress: true }) | ||
80 | |||
81 | return this.authHttp.request(req) | ||
82 | .catch(this.restExtractor.handleError) | ||
83 | } | ||
84 | |||
76 | getVideos (videoPagination: VideoPagination, sort: SortField) { | 85 | getVideos (videoPagination: VideoPagination, sort: SortField) { |
77 | const pagination = this.videoPaginationToRestPagination(videoPagination) | 86 | const pagination = this.videoPaginationToRestPagination(videoPagination) |
78 | 87 | ||
diff --git a/client/src/app/videos/video-edit/video-add.component.html b/client/src/app/videos/video-edit/video-add.component.html index 7ad671ae7..cf8fc2b80 100644 --- a/client/src/app/videos/video-edit/video-add.component.html +++ b/client/src/app/videos/video-edit/video-add.component.html | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | <h3>Upload a video</h3> | 4 | <h3>Upload a video</h3> |
5 | 5 | ||
6 | <div *ngIf="error" class="alert alert-danger">{{ error }}</div> | 6 | <div *ngIf="error !== undefined" class="alert alert-danger">{{ error }}</div> |
7 | 7 | ||
8 | <form novalidate [formGroup]="form"> | 8 | <form novalidate [formGroup]="form"> |
9 | <div class="form-group"> | 9 | <div class="form-group"> |
@@ -62,7 +62,7 @@ | |||
62 | </div> | 62 | </div> |
63 | 63 | ||
64 | <div class="form-group"> | 64 | <div class="form-group"> |
65 | <label for="tags" class="label-tags">Tags</label> <span class="little-information">(press enter to add the tag)</span> | 65 | <label class="label-tags">Tags</label> <span class="little-information">(press enter to add the tag)</span> |
66 | <tag-input | 66 | <tag-input |
67 | [ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages" | 67 | [ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages" |
68 | formControlName="tags" maxItems="3" modelAsStrings="true" | 68 | formControlName="tags" maxItems="3" modelAsStrings="true" |
@@ -71,25 +71,22 @@ | |||
71 | 71 | ||
72 | <div class="form-group"> | 72 | <div class="form-group"> |
73 | <label for="videofile">File</label> | 73 | <label for="videofile">File</label> |
74 | <div class="btn btn-default btn-file" [ngClass]="{ 'disabled': filename !== null }" > | 74 | <div class="btn btn-default btn-file"> |
75 | <span>Select the video...</span> | 75 | <span>Select the video...</span> |
76 | <input | 76 | <input #videofileInput type="file" name="videofile" id="videofile" (change)="fileChange($event)" /> |
77 | type="file" name="videofile" id="videofile" | 77 | <input type="hidden" name="videofileHidden" formControlName="videofile"/> |
78 | ng2FileSelect [uploader]="uploader" [disabled]="filename !== null" | ||
79 | (change)="fileChanged()" | ||
80 | > | ||
81 | </div> | 78 | </div> |
82 | </div> | 79 | </div> |
83 | 80 | ||
84 | <div class="file-to-upload"> | 81 | <div class="file-to-upload"> |
85 | <div class="file" *ngIf="uploader.queue.length > 0"> | 82 | <div class="file" *ngIf="filename"> |
86 | <span class="filename">{{ filename }}</span> | 83 | <span class="filename">{{ filename }}</span> |
87 | <span class="glyphicon glyphicon-remove" (click)="removeFile()"></span> | 84 | <span class="glyphicon glyphicon-remove" (click)="removeFile()"></span> |
88 | </div> | 85 | </div> |
89 | </div> | 86 | </div> |
90 | 87 | ||
91 | <div *ngIf="fileError" class="alert alert-danger"> | 88 | <div *ngIf="formErrors.videofile" class="alert alert-danger"> |
92 | {{ fileError }} | 89 | {{ formErrors.videofile }} |
93 | </div> | 90 | </div> |
94 | 91 | ||
95 | <div class="form-group"> | 92 | <div class="form-group"> |
@@ -105,7 +102,7 @@ | |||
105 | </div> | 102 | </div> |
106 | 103 | ||
107 | <div class="progress"> | 104 | <div class="progress"> |
108 | <progressbar [value]="uploader.progress" max="100"></progressbar> | 105 | <progressbar [value]="progressPercent" max="100"></progressbar> |
109 | </div> | 106 | </div> |
110 | 107 | ||
111 | <div class="form-group"> | 108 | <div class="form-group"> |
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 42b11cd08..537ef9bc3 100644 --- a/client/src/app/videos/video-edit/video-add.component.ts +++ b/client/src/app/videos/video-edit/video-add.component.ts | |||
@@ -1,11 +1,9 @@ | |||
1 | import { Component, ElementRef, OnInit } from '@angular/core' | 1 | import { Component, OnInit, ViewChild } from '@angular/core' |
2 | import { FormBuilder, FormGroup } from '@angular/forms' | 2 | import { FormBuilder, FormGroup } from '@angular/forms' |
3 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
4 | 4 | ||
5 | import { FileUploader } from 'ng2-file-upload/ng2-file-upload' | ||
6 | import { NotificationsService } from 'angular2-notifications' | 5 | import { NotificationsService } from 'angular2-notifications' |
7 | 6 | ||
8 | import { AuthService } from '../../core' | ||
9 | import { | 7 | import { |
10 | FormReactive, | 8 | FormReactive, |
11 | VIDEO_NAME, | 9 | VIDEO_NAME, |
@@ -17,6 +15,8 @@ import { | |||
17 | } from '../../shared' | 15 | } from '../../shared' |
18 | import { VideoService } from '../shared' | 16 | import { VideoService } from '../shared' |
19 | import { VideoCreate } from '../../../../../shared' | 17 | import { VideoCreate } from '../../../../../shared' |
18 | import { HttpEventType, HttpResponse } from '@angular/common/http' | ||
19 | import { VIDEO_FILE } from '../../shared/forms/form-validators/video' | ||
20 | 20 | ||
21 | @Component({ | 21 | @Component({ |
22 | selector: 'my-videos-add', | 22 | selector: 'my-videos-add', |
@@ -25,8 +25,10 @@ import { VideoCreate } from '../../../../../shared' | |||
25 | }) | 25 | }) |
26 | 26 | ||
27 | export class VideoAddComponent extends FormReactive implements OnInit { | 27 | export class VideoAddComponent extends FormReactive implements OnInit { |
28 | @ViewChild('videofileInput') videofileInput | ||
29 | |||
30 | progressPercent = 0 | ||
28 | tags: string[] = [] | 31 | tags: string[] = [] |
29 | uploader: FileUploader | ||
30 | videoCategories = [] | 32 | videoCategories = [] |
31 | videoLicences = [] | 33 | videoLicences = [] |
32 | videoLanguages = [] | 34 | videoLanguages = [] |
@@ -34,29 +36,26 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
34 | tagValidators = VIDEO_TAGS.VALIDATORS | 36 | tagValidators = VIDEO_TAGS.VALIDATORS |
35 | tagValidatorsMessages = VIDEO_TAGS.MESSAGES | 37 | tagValidatorsMessages = VIDEO_TAGS.MESSAGES |
36 | 38 | ||
37 | error: string = null | 39 | error: string |
38 | form: FormGroup | 40 | form: FormGroup |
39 | formErrors = { | 41 | formErrors = { |
40 | name: '', | 42 | name: '', |
41 | category: '', | 43 | category: '', |
42 | licence: '', | 44 | licence: '', |
43 | language: '', | 45 | language: '', |
44 | description: '' | 46 | description: '', |
47 | videofile: '' | ||
45 | } | 48 | } |
46 | validationMessages = { | 49 | validationMessages = { |
47 | name: VIDEO_NAME.MESSAGES, | 50 | name: VIDEO_NAME.MESSAGES, |
48 | category: VIDEO_CATEGORY.MESSAGES, | 51 | category: VIDEO_CATEGORY.MESSAGES, |
49 | licence: VIDEO_LICENCE.MESSAGES, | 52 | licence: VIDEO_LICENCE.MESSAGES, |
50 | language: VIDEO_LANGUAGE.MESSAGES, | 53 | language: VIDEO_LANGUAGE.MESSAGES, |
51 | description: VIDEO_DESCRIPTION.MESSAGES | 54 | description: VIDEO_DESCRIPTION.MESSAGES, |
55 | videofile: VIDEO_FILE.MESSAGES | ||
52 | } | 56 | } |
53 | 57 | ||
54 | // Special error messages | ||
55 | fileError = '' | ||
56 | |||
57 | constructor ( | 58 | constructor ( |
58 | private authService: AuthService, | ||
59 | private elementRef: ElementRef, | ||
60 | private formBuilder: FormBuilder, | 59 | private formBuilder: FormBuilder, |
61 | private router: Router, | 60 | private router: Router, |
62 | private notificationsService: NotificationsService, | 61 | private notificationsService: NotificationsService, |
@@ -66,11 +65,7 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
66 | } | 65 | } |
67 | 66 | ||
68 | get filename () { | 67 | get filename () { |
69 | if (this.uploader.queue.length === 0) { | 68 | return this.form.value['videofile'] |
70 | return null | ||
71 | } | ||
72 | |||
73 | return this.uploader.queue[0].file.name | ||
74 | } | 69 | } |
75 | 70 | ||
76 | buildForm () { | 71 | buildForm () { |
@@ -81,7 +76,8 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
81 | licence: [ '', VIDEO_LICENCE.VALIDATORS ], | 76 | licence: [ '', VIDEO_LICENCE.VALIDATORS ], |
82 | language: [ '', VIDEO_LANGUAGE.VALIDATORS ], | 77 | language: [ '', VIDEO_LANGUAGE.VALIDATORS ], |
83 | description: [ '', VIDEO_DESCRIPTION.VALIDATORS ], | 78 | description: [ '', VIDEO_DESCRIPTION.VALIDATORS ], |
84 | tags: [ ''] | 79 | videofile: [ '', VIDEO_FILE.VALIDATORS ], |
80 | tags: [ '' ] | ||
85 | }) | 81 | }) |
86 | 82 | ||
87 | this.form.valueChanges.subscribe(data => this.onValueChanged(data)) | 83 | this.form.valueChanges.subscribe(data => this.onValueChanged(data)) |
@@ -92,60 +88,24 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
92 | this.videoLicences = this.videoService.videoLicences | 88 | this.videoLicences = this.videoService.videoLicences |
93 | this.videoLanguages = this.videoService.videoLanguages | 89 | this.videoLanguages = this.videoService.videoLanguages |
94 | 90 | ||
95 | this.uploader = new FileUploader({ | ||
96 | authToken: this.authService.getRequestHeaderValue(), | ||
97 | queueLimit: 1, | ||
98 | url: API_URL + '/api/v1/videos/upload', | ||
99 | removeAfterUpload: true | ||
100 | }) | ||
101 | |||
102 | this.uploader.onBuildItemForm = (item, form: FormData) => { | ||
103 | const formValue: VideoCreate = this.form.value | ||
104 | |||
105 | const name = formValue.name | ||
106 | const nsfw = formValue.nsfw | ||
107 | const category = formValue.category | ||
108 | const licence = formValue.licence | ||
109 | const language = formValue.language | ||
110 | const description = formValue.description | ||
111 | const tags = formValue.tags | ||
112 | |||
113 | form.append('name', name) | ||
114 | form.append('category', '' + category) | ||
115 | form.append('nsfw', '' + nsfw) | ||
116 | form.append('licence', '' + licence) | ||
117 | |||
118 | // Language is optional | ||
119 | if (language) { | ||
120 | form.append('language', '' + language) | ||
121 | } | ||
122 | |||
123 | form.append('description', description) | ||
124 | |||
125 | for (let i = 0; i < tags.length; i++) { | ||
126 | form.append(`tags[${i}]`, tags[i]) | ||
127 | } | ||
128 | } | ||
129 | |||
130 | this.buildForm() | 91 | this.buildForm() |
131 | } | 92 | } |
132 | 93 | ||
133 | checkForm () { | 94 | // The goal is to keep reactive form validation (required field) |
134 | this.forceCheck() | 95 | // https://stackoverflow.com/a/44238894 |
135 | 96 | fileChange ($event) { | |
136 | if (this.filename === null) { | 97 | this.form.controls['videofile'].setValue($event.target.files[0].name) |
137 | this.fileError = 'You did not add a file.' | ||
138 | } | ||
139 | |||
140 | return this.form.valid === true && this.fileError === '' | ||
141 | } | 98 | } |
142 | 99 | ||
143 | fileChanged () { | 100 | removeFile () { |
144 | this.fileError = '' | 101 | this.videofileInput.nativeElement.value = '' |
102 | this.form.controls['videofile'].setValue('') | ||
145 | } | 103 | } |
146 | 104 | ||
147 | removeFile () { | 105 | checkForm () { |
148 | this.uploader.clearQueue() | 106 | this.forceCheck() |
107 | |||
108 | return this.form.valid | ||
149 | } | 109 | } |
150 | 110 | ||
151 | upload () { | 111 | upload () { |
@@ -153,39 +113,49 @@ export class VideoAddComponent extends FormReactive implements OnInit { | |||
153 | return | 113 | return |
154 | } | 114 | } |
155 | 115 | ||
156 | const item = this.uploader.queue[0] | 116 | const formValue: VideoCreate = this.form.value |
157 | // TODO: wait for https://github.com/valor-software/ng2-file-upload/pull/242 | 117 | |
158 | item.alias = 'videofile' | 118 | const name = formValue.name |
159 | 119 | const nsfw = formValue.nsfw | |
160 | item.onSuccess = () => { | 120 | const category = formValue.category |
161 | console.log('Video uploaded.') | 121 | const licence = formValue.licence |
162 | this.notificationsService.success('Success', 'Video uploaded.') | 122 | const language = formValue.language |
163 | 123 | const description = formValue.description | |
164 | // Print all the videos once it's finished | 124 | const tags = formValue.tags |
165 | this.router.navigate(['/videos/list']) | 125 | const videofile = this.videofileInput.nativeElement.files[0] |
126 | |||
127 | const formData = new FormData() | ||
128 | formData.append('name', name) | ||
129 | formData.append('category', '' + category) | ||
130 | formData.append('nsfw', '' + nsfw) | ||
131 | formData.append('licence', '' + licence) | ||
132 | formData.append('videofile', videofile) | ||
133 | |||
134 | // Language is optional | ||
135 | if (language) { | ||
136 | formData.append('language', '' + language) | ||
166 | } | 137 | } |
167 | 138 | ||
168 | item.onError = (response: string, status: number) => { | 139 | formData.append('description', description) |
169 | // We need to handle manually these cases because we use the FileUpload component | 140 | |
170 | if (status === 400) { | 141 | for (let i = 0; i < tags.length; i++) { |
171 | this.error = response | 142 | formData.append(`tags[${i}]`, tags[i]) |
172 | } else if (status === 401) { | ||
173 | this.error = 'Access token was expired, refreshing token...' | ||
174 | this.authService.refreshAccessToken().subscribe( | ||
175 | () => { | ||
176 | // Update the uploader request header | ||
177 | this.uploader.authToken = this.authService.getRequestHeaderValue() | ||
178 | this.error += ' access token refreshed. Please retry your request.' | ||
179 | } | ||
180 | ) | ||
181 | } else if (status === 403) { | ||
182 | this.error = 'Your video quota is reached, you can\'t upload this video.' | ||
183 | } else { | ||
184 | this.error = 'Unknown error' | ||
185 | console.error(this.error) | ||
186 | } | ||
187 | } | 143 | } |
188 | 144 | ||
189 | this.uploader.uploadAll() | 145 | this.videoService.uploadVideo(formData).subscribe( |
146 | event => { | ||
147 | if (event.type === HttpEventType.UploadProgress) { | ||
148 | this.progressPercent = Math.round(100 * event.loaded / event.total) | ||
149 | } else if (event instanceof HttpResponse) { | ||
150 | console.log('Video uploaded.') | ||
151 | this.notificationsService.success('Success', 'Video uploaded.') | ||
152 | |||
153 | // Display all the videos once it's finished | ||
154 | this.router.navigate([ '/videos/list ']) | ||
155 | } | ||
156 | }, | ||
157 | |||
158 | err => this.error = err.message | ||
159 | ) | ||
190 | } | 160 | } |
191 | } | 161 | } |
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 6c57a23d8..141ed3522 100644 --- a/client/src/app/videos/video-edit/video-update.component.ts +++ b/client/src/app/videos/video-edit/video-update.component.ts | |||
@@ -2,7 +2,6 @@ import { Component, ElementRef, OnInit } from '@angular/core' | |||
2 | import { FormBuilder, FormGroup } from '@angular/forms' | 2 | import { FormBuilder, FormGroup } from '@angular/forms' |
3 | import { ActivatedRoute, Router } from '@angular/router' | 3 | import { ActivatedRoute, Router } from '@angular/router' |
4 | 4 | ||
5 | import { FileUploader } from 'ng2-file-upload/ng2-file-upload' | ||
6 | import { NotificationsService } from 'angular2-notifications' | 5 | import { NotificationsService } from 'angular2-notifications' |
7 | 6 | ||
8 | import { AuthService } from '../../core' | 7 | import { AuthService } from '../../core' |
diff --git a/client/src/app/videos/video-watch/video-report.component.ts b/client/src/app/videos/video-watch/video-report.component.ts index a5758060d..d9c83a640 100644 --- a/client/src/app/videos/video-watch/video-report.component.ts +++ b/client/src/app/videos/video-watch/video-report.component.ts | |||
@@ -63,7 +63,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
63 | this.hide() | 63 | this.hide() |
64 | }, | 64 | }, |
65 | 65 | ||
66 | err => this.notificationsService.error('Error', err) | 66 | err => this.notificationsService.error('Error', err.message) |
67 | ) | 67 | ) |
68 | } | 68 | } |
69 | } | 69 | } |
diff --git a/client/src/app/videos/video-watch/video-watch.component.ts b/client/src/app/videos/video-watch/video-watch.component.ts index 9cedc9c76..f5a47199d 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -158,7 +158,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
158 | this.userRating = 'like' | 158 | this.userRating = 'like' |
159 | }, | 159 | }, |
160 | 160 | ||
161 | err => this.notificationsService.error('Error', err) | 161 | err => this.notificationsService.error('Error', err.message) |
162 | ) | 162 | ) |
163 | } | 163 | } |
164 | 164 | ||
@@ -175,7 +175,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
175 | this.userRating = 'dislike' | 175 | this.userRating = 'dislike' |
176 | }, | 176 | }, |
177 | 177 | ||
178 | err => this.notificationsService.error('Error', err) | 178 | err => this.notificationsService.error('Error', err.message) |
179 | ) | 179 | ) |
180 | } | 180 | } |
181 | 181 | ||
@@ -275,7 +275,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
275 | } | 275 | } |
276 | }, | 276 | }, |
277 | 277 | ||
278 | err => this.notificationsService.error('Error', err) | 278 | err => this.notificationsService.error('Error', err.message) |
279 | ) | 279 | ) |
280 | } | 280 | } |
281 | 281 | ||
diff --git a/client/yarn.lock b/client/yarn.lock index 3552dbf99..b61da8636 100644 --- a/client/yarn.lock +++ b/client/yarn.lock | |||
@@ -4436,10 +4436,6 @@ ng-router-loader@^2.0.0: | |||
4436 | loader-utils "^0.2.16" | 4436 | loader-utils "^0.2.16" |
4437 | recast "^0.11.20" | 4437 | recast "^0.11.20" |
4438 | 4438 | ||
4439 | ng2-file-upload@^1.1.4-2: | ||
4440 | version "1.2.1" | ||
4441 | resolved "https://registry.yarnpkg.com/ng2-file-upload/-/ng2-file-upload-1.2.1.tgz#5563c5dfd6f43fbfbe815c206e343464a0a6a197" | ||
4442 | |||
4443 | ng2-material-dropdown@0.7.10: | 4439 | ng2-material-dropdown@0.7.10: |
4444 | version "0.7.10" | 4440 | version "0.7.10" |
4445 | resolved "https://registry.yarnpkg.com/ng2-material-dropdown/-/ng2-material-dropdown-0.7.10.tgz#093471f2a9cadd726cbcb120b0ad7818a54fa5ed" | 4441 | resolved "https://registry.yarnpkg.com/ng2-material-dropdown/-/ng2-material-dropdown-0.7.10.tgz#093471f2a9cadd726cbcb120b0ad7818a54fa5ed" |
diff --git a/server/middlewares/validators/pods.ts b/server/middlewares/validators/pods.ts index 4d0e054b0..3a0f56f6a 100644 --- a/server/middlewares/validators/pods.ts +++ b/server/middlewares/validators/pods.ts | |||
@@ -11,7 +11,11 @@ import { isTestInstance } from '../../helpers' | |||
11 | function makeFriendsValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 11 | function makeFriendsValidator (req: express.Request, res: express.Response, next: express.NextFunction) { |
12 | // Force https if the administrator wants to make friends | 12 | // Force https if the administrator wants to make friends |
13 | if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') { | 13 | if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') { |
14 | return res.status(400).send('Cannot make friends with a non HTTPS web server.') | 14 | return res.status(400) |
15 | .json({ | ||
16 | error: 'Cannot make friends with a non HTTPS web server.' | ||
17 | }) | ||
18 | .end() | ||
15 | } | 19 | } |
16 | 20 | ||
17 | req.checkBody('hosts', 'Should have an array of unique hosts').isEachUniqueHostValid() | 21 | req.checkBody('hosts', 'Should have an array of unique hosts').isEachUniqueHostValid() |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index aec6324bf..15c07c693 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -45,9 +45,13 @@ function usersRemoveValidator (req: express.Request, res: express.Response, next | |||
45 | return res.sendStatus(500) | 45 | return res.sendStatus(500) |
46 | } | 46 | } |
47 | 47 | ||
48 | if (user.username === 'root') return res.status(400).send('Cannot remove the root user') | 48 | if (user.username === 'root') { |
49 | return res.status(400) | ||
50 | .send({ error: 'Cannot remove the root user' }) | ||
51 | .end() | ||
52 | } | ||
49 | 53 | ||
50 | next() | 54 | return next() |
51 | }) | 55 | }) |
52 | }) | 56 | }) |
53 | } | 57 | } |
@@ -99,9 +103,13 @@ function usersVideoRatingValidator (req: express.Request, res: express.Response, | |||
99 | 103 | ||
100 | videoPromise | 104 | videoPromise |
101 | .then(video => { | 105 | .then(video => { |
102 | if (!video) return res.status(404).send('Video not found') | 106 | if (!video) { |
107 | return res.status(404) | ||
108 | .json({ error: 'Video not found' }) | ||
109 | .end() | ||
110 | } | ||
103 | 111 | ||
104 | next() | 112 | return next() |
105 | }) | 113 | }) |
106 | .catch(err => { | 114 | .catch(err => { |
107 | logger.error('Error in user request validator.', err) | 115 | logger.error('Error in user request validator.', err) |
@@ -113,7 +121,9 @@ function usersVideoRatingValidator (req: express.Request, res: express.Response, | |||
113 | function ensureUserRegistrationAllowed (req: express.Request, res: express.Response, next: express.NextFunction) { | 121 | function ensureUserRegistrationAllowed (req: express.Request, res: express.Response, next: express.NextFunction) { |
114 | isSignupAllowed().then(allowed => { | 122 | isSignupAllowed().then(allowed => { |
115 | if (allowed === false) { | 123 | if (allowed === false) { |
116 | return res.status(403).send('User registration is not enabled or user limit is reached.') | 124 | return res.status(403) |
125 | .send({ error: 'User registration is not enabled or user limit is reached.' }) | ||
126 | .end() | ||
117 | } | 127 | } |
118 | 128 | ||
119 | return next() | 129 | return next() |
@@ -138,10 +148,14 @@ export { | |||
138 | function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) { | 148 | function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) { |
139 | db.User.loadById(id) | 149 | db.User.loadById(id) |
140 | .then(user => { | 150 | .then(user => { |
141 | if (!user) return res.status(404).send('User not found') | 151 | if (!user) { |
152 | return res.status(404) | ||
153 | .send({ error: 'User not found' }) | ||
154 | .end() | ||
155 | } | ||
142 | 156 | ||
143 | res.locals.user = user | 157 | res.locals.user = user |
144 | callback(null, user) | 158 | return callback(null, user) |
145 | }) | 159 | }) |
146 | .catch(err => { | 160 | .catch(err => { |
147 | logger.error('Error in user request validator.', err) | 161 | logger.error('Error in user request validator.', err) |
@@ -152,9 +166,13 @@ function checkUserExists (id: number, res: express.Response, callback: (err: Err | |||
152 | function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) { | 166 | function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) { |
153 | db.User.loadByUsernameOrEmail(username, email) | 167 | db.User.loadByUsernameOrEmail(username, email) |
154 | .then(user => { | 168 | .then(user => { |
155 | if (user) return res.status(409).send('User already exists.') | 169 | if (user) { |
170 | return res.status(409) | ||
171 | .send({ error: 'User already exists.' }) | ||
172 | .end() | ||
173 | } | ||
156 | 174 | ||
157 | callback() | 175 | return callback() |
158 | }) | 176 | }) |
159 | .catch(err => { | 177 | .catch(err => { |
160 | logger.error('Error in usersAdd request validator.', err) | 178 | logger.error('Error in usersAdd request validator.', err) |
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index 519e3d46c..213b4c46b 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts | |||
@@ -30,7 +30,9 @@ function videosAddValidator (req: express.Request, res: express.Response, next: | |||
30 | user.isAbleToUploadVideo(videoFile) | 30 | user.isAbleToUploadVideo(videoFile) |
31 | .then(isAble => { | 31 | .then(isAble => { |
32 | if (isAble === false) { | 32 | if (isAble === false) { |
33 | res.status(403).send('The user video quota is exceeded with this video.') | 33 | res.status(403) |
34 | .json({ error: 'The user video quota is exceeded with this video.' }) | ||
35 | .end() | ||
34 | 36 | ||
35 | return undefined | 37 | return undefined |
36 | } | 38 | } |
@@ -38,17 +40,23 @@ function videosAddValidator (req: express.Request, res: express.Response, next: | |||
38 | return db.Video.getDurationFromFile(videoFile.path) | 40 | return db.Video.getDurationFromFile(videoFile.path) |
39 | .catch(err => { | 41 | .catch(err => { |
40 | logger.error('Invalid input file in videosAddValidator.', err) | 42 | logger.error('Invalid input file in videosAddValidator.', err) |
41 | res.status(400).send('Invalid input file.') | 43 | res.status(400) |
44 | .json({ error: 'Invalid input file.' }) | ||
45 | .end() | ||
42 | 46 | ||
43 | return undefined | 47 | return undefined |
44 | }) | 48 | }) |
45 | }) | 49 | }) |
46 | .then(duration => { | 50 | .then(duration => { |
47 | // Previous test failed, abort | 51 | // Previous test failed, abort |
48 | if (duration === undefined) return undefined | 52 | if (duration === undefined) return |
49 | 53 | ||
50 | if (!isVideoDurationValid('' + duration)) { | 54 | if (!isVideoDurationValid('' + duration)) { |
51 | return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).') | 55 | return res.status(400) |
56 | .json({ | ||
57 | error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).' | ||
58 | }) | ||
59 | .end() | ||
52 | } | 60 | } |
53 | 61 | ||
54 | videoFile['duration'] = duration | 62 | videoFile['duration'] = duration |
@@ -80,11 +88,15 @@ function videosUpdateValidator (req: express.Request, res: express.Response, nex | |||
80 | checkVideoExists(req.params.id, res, () => { | 88 | checkVideoExists(req.params.id, res, () => { |
81 | // We need to make additional checks | 89 | // We need to make additional checks |
82 | if (res.locals.video.isOwned() === false) { | 90 | if (res.locals.video.isOwned() === false) { |
83 | return res.status(403).send('Cannot update video of another pod') | 91 | return res.status(403) |
92 | .json({ error: 'Cannot update video of another pod' }) | ||
93 | .end() | ||
84 | } | 94 | } |
85 | 95 | ||
86 | if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { | 96 | if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { |
87 | return res.status(403).send('Cannot update video of another user') | 97 | return res.status(403) |
98 | .json({ error: 'Cannot update video of another user' }) | ||
99 | .end() | ||
88 | } | 100 | } |
89 | 101 | ||
90 | next() | 102 | next() |
@@ -188,7 +200,11 @@ function checkVideoExists (id: string, res: express.Response, callback: () => vo | |||
188 | } | 200 | } |
189 | 201 | ||
190 | promise.then(video => { | 202 | promise.then(video => { |
191 | if (!video) return res.status(404).send('Video not found') | 203 | if (!video) { |
204 | return res.status(404) | ||
205 | .json({ error: 'Video not found' }) | ||
206 | .end() | ||
207 | } | ||
192 | 208 | ||
193 | res.locals.video = video | 209 | res.locals.video = video |
194 | callback() | 210 | callback() |
@@ -204,14 +220,18 @@ function checkUserCanDeleteVideo (userId: number, res: express.Response, callbac | |||
204 | db.User.loadById(userId) | 220 | db.User.loadById(userId) |
205 | .then(user => { | 221 | .then(user => { |
206 | if (res.locals.video.isOwned() === false) { | 222 | if (res.locals.video.isOwned() === false) { |
207 | return res.status(403).send('Cannot remove video of another pod, blacklist it') | 223 | return res.status(403) |
224 | .json({ error: 'Cannot remove video of another pod, blacklist it' }) | ||
225 | .end() | ||
208 | } | 226 | } |
209 | 227 | ||
210 | // Check if the user can delete the video | 228 | // Check if the user can delete the video |
211 | // The user can delete it if s/he is an admin | 229 | // The user can delete it if s/he is an admin |
212 | // Or if s/he is the video's author | 230 | // Or if s/he is the video's author |
213 | if (user.isAdmin() === false && res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { | 231 | if (user.isAdmin() === false && res.locals.video.Author.userId !== res.locals.oauth.token.User.id) { |
214 | return res.status(403).send('Cannot remove video of another user') | 232 | return res.status(403) |
233 | .json({ error: 'Cannot remove video of another user' }) | ||
234 | .end() | ||
215 | } | 235 | } |
216 | 236 | ||
217 | // If we reach this comment, we can delete the video | 237 | // If we reach this comment, we can delete the video |
@@ -225,7 +245,9 @@ function checkUserCanDeleteVideo (userId: number, res: express.Response, callbac | |||
225 | 245 | ||
226 | function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) { | 246 | function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) { |
227 | if (res.locals.video.isOwned() === true) { | 247 | if (res.locals.video.isOwned() === true) { |
228 | return res.status(403).send('Cannot blacklist a local video') | 248 | return res.status(403) |
249 | .json({ error: 'Cannot blacklist a local video' }) | ||
250 | .end() | ||
229 | } | 251 | } |
230 | 252 | ||
231 | callback() | 253 | callback() |