"intl": "^1.2.4",
"json-loader": "^0.5.4",
"ng-router-loader": "^2.0.0",
- "ng2-file-upload": "^1.1.4-2",
"ngc-webpack": "3.2.2",
"ngx-bootstrap": "1.9.1",
"ngx-chips": "1.5.3",
setTimeout(() => this.router.navigate([ '/admin/friends/list' ]), 1000)
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
)
this.loadData()
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
)
this.loadData()
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
)
this.friends = resultList.data
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
}
this.requestService.getStats().subscribe(
stats => this.stats = stats,
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
this.loadData()
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
)
this.totalRecords = resultList.total
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
}
this.totalRecords = resultList.total
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
}
private authService: AuthService,
private configService: ConfigService,
private userService: UserService,
- private videoService: VideoService,
- viewContainerRef: ViewContainerRef
+ private videoService: VideoService
) {}
ngOnInit () {
'maxlength': 'A tag should be less than 10 characters long.'
}
}
+
+export const VIDEO_FILE = {
+ VALIDATORS: [ Validators.required ],
+ MESSAGES: {
+ 'required': 'Video file is required.'
+ }
+}
handleError (err: HttpErrorResponse) {
let errorMessage
+ console.log(err)
+
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
errorMessage = err.error.message
console.error('An error occurred:', errorMessage)
} else if (err.status !== undefined) {
- // The backend returned an unsuccessful response code.
- // The response body may contain clues as to what went wrong,
- errorMessage = err.error
+ const body = err.error
+ errorMessage = body.error
console.error(`Backend returned code ${err.status}, body was: ${errorMessage}`)
} else {
errorMessage = err
}
- return Observable.throw(errorMessage)
+ const errorObj = {
+ message: errorMessage,
+ status: undefined
+ }
+
+ if (err.status) {
+ errorObj.status = err.status
+ }
+
+ return Observable.throw(errorObj)
}
}
import { ProgressbarModule } from 'ngx-bootstrap/progressbar'
import { PaginationModule } from 'ngx-bootstrap/pagination'
import { ModalModule } from 'ngx-bootstrap/modal'
-import { FileUploadModule } from 'ng2-file-upload/ng2-file-upload'
import { DataTableModule, SharedModule as PrimeSharedModule } from 'primeng/primeng'
import { AUTH_INTERCEPTOR_PROVIDER } from './auth'
PaginationModule.forRoot(),
ProgressbarModule.forRoot(),
- FileUploadModule,
-
DataTableModule,
PrimeSharedModule
],
HttpClientModule,
BsDropdownModule,
- FileUploadModule,
ModalModule,
PaginationModule,
ProgressbarModule,
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/operator/catch'
import 'rxjs/add/operator/map'
-import { HttpClient, HttpParams } from '@angular/common/http'
+import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'
import { Search } from '../../shared'
import { SortField } from './sort-field.type'
import { Video } from './video.model'
import { VideoPagination } from './video-pagination.model'
import {
-UserVideoRate,
-VideoRateType,
-VideoUpdate,
-VideoAbuseCreate,
-UserVideoRateUpdate,
-Video as VideoServerModel,
-ResultList
+ VideoCreate,
+ UserVideoRate,
+ VideoRateType,
+ VideoUpdate,
+ VideoAbuseCreate,
+ UserVideoRateUpdate,
+ Video as VideoServerModel,
+ ResultList
} from '../../../../../shared'
@Injectable()
.catch(this.restExtractor.handleError)
}
+ // uploadVideo (video: VideoCreate) {
+ uploadVideo (video: any) {
+ const req = new HttpRequest('POST', `${VideoService.BASE_VIDEO_URL}/upload`, video, { reportProgress: true })
+
+ return this.authHttp.request(req)
+ .catch(this.restExtractor.handleError)
+ }
+
getVideos (videoPagination: VideoPagination, sort: SortField) {
const pagination = this.videoPaginationToRestPagination(videoPagination)
<h3>Upload a video</h3>
- <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
+ <div *ngIf="error !== undefined" class="alert alert-danger">{{ error }}</div>
<form novalidate [formGroup]="form">
<div class="form-group">
</div>
<div class="form-group">
- <label for="tags" class="label-tags">Tags</label> <span class="little-information">(press enter to add the tag)</span>
+ <label class="label-tags">Tags</label> <span class="little-information">(press enter to add the tag)</span>
<tag-input
[ngModel]="tags" [validators]="tagValidators" [errorMessages]="tagValidatorsMessages"
formControlName="tags" maxItems="3" modelAsStrings="true"
<div class="form-group">
<label for="videofile">File</label>
- <div class="btn btn-default btn-file" [ngClass]="{ 'disabled': filename !== null }" >
+ <div class="btn btn-default btn-file">
<span>Select the video...</span>
- <input
- type="file" name="videofile" id="videofile"
- ng2FileSelect [uploader]="uploader" [disabled]="filename !== null"
- (change)="fileChanged()"
- >
+ <input #videofileInput type="file" name="videofile" id="videofile" (change)="fileChange($event)" />
+ <input type="hidden" name="videofileHidden" formControlName="videofile"/>
</div>
</div>
<div class="file-to-upload">
- <div class="file" *ngIf="uploader.queue.length > 0">
+ <div class="file" *ngIf="filename">
<span class="filename">{{ filename }}</span>
<span class="glyphicon glyphicon-remove" (click)="removeFile()"></span>
</div>
</div>
- <div *ngIf="fileError" class="alert alert-danger">
- {{ fileError }}
+ <div *ngIf="formErrors.videofile" class="alert alert-danger">
+ {{ formErrors.videofile }}
</div>
<div class="form-group">
</div>
<div class="progress">
- <progressbar [value]="uploader.progress" max="100"></progressbar>
+ <progressbar [value]="progressPercent" max="100"></progressbar>
</div>
<div class="form-group">
-import { Component, ElementRef, OnInit } from '@angular/core'
+import { Component, OnInit, ViewChild } from '@angular/core'
import { FormBuilder, FormGroup } from '@angular/forms'
import { Router } from '@angular/router'
-import { FileUploader } from 'ng2-file-upload/ng2-file-upload'
import { NotificationsService } from 'angular2-notifications'
-import { AuthService } from '../../core'
import {
FormReactive,
VIDEO_NAME,
} from '../../shared'
import { VideoService } from '../shared'
import { VideoCreate } from '../../../../../shared'
+import { HttpEventType, HttpResponse } from '@angular/common/http'
+import { VIDEO_FILE } from '../../shared/forms/form-validators/video'
@Component({
selector: 'my-videos-add',
})
export class VideoAddComponent extends FormReactive implements OnInit {
+ @ViewChild('videofileInput') videofileInput
+
+ progressPercent = 0
tags: string[] = []
- uploader: FileUploader
videoCategories = []
videoLicences = []
videoLanguages = []
tagValidators = VIDEO_TAGS.VALIDATORS
tagValidatorsMessages = VIDEO_TAGS.MESSAGES
- error: string = null
+ error: string
form: FormGroup
formErrors = {
name: '',
category: '',
licence: '',
language: '',
- description: ''
+ description: '',
+ videofile: ''
}
validationMessages = {
name: VIDEO_NAME.MESSAGES,
category: VIDEO_CATEGORY.MESSAGES,
licence: VIDEO_LICENCE.MESSAGES,
language: VIDEO_LANGUAGE.MESSAGES,
- description: VIDEO_DESCRIPTION.MESSAGES
+ description: VIDEO_DESCRIPTION.MESSAGES,
+ videofile: VIDEO_FILE.MESSAGES
}
- // Special error messages
- fileError = ''
-
constructor (
- private authService: AuthService,
- private elementRef: ElementRef,
private formBuilder: FormBuilder,
private router: Router,
private notificationsService: NotificationsService,
}
get filename () {
- if (this.uploader.queue.length === 0) {
- return null
- }
-
- return this.uploader.queue[0].file.name
+ return this.form.value['videofile']
}
buildForm () {
licence: [ '', VIDEO_LICENCE.VALIDATORS ],
language: [ '', VIDEO_LANGUAGE.VALIDATORS ],
description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
- tags: [ '']
+ videofile: [ '', VIDEO_FILE.VALIDATORS ],
+ tags: [ '' ]
})
this.form.valueChanges.subscribe(data => this.onValueChanged(data))
this.videoLicences = this.videoService.videoLicences
this.videoLanguages = this.videoService.videoLanguages
- this.uploader = new FileUploader({
- authToken: this.authService.getRequestHeaderValue(),
- queueLimit: 1,
- url: API_URL + '/api/v1/videos/upload',
- removeAfterUpload: true
- })
-
- this.uploader.onBuildItemForm = (item, form: FormData) => {
- const formValue: VideoCreate = this.form.value
-
- const name = formValue.name
- const nsfw = formValue.nsfw
- const category = formValue.category
- const licence = formValue.licence
- const language = formValue.language
- const description = formValue.description
- const tags = formValue.tags
-
- form.append('name', name)
- form.append('category', '' + category)
- form.append('nsfw', '' + nsfw)
- form.append('licence', '' + licence)
-
- // Language is optional
- if (language) {
- form.append('language', '' + language)
- }
-
- form.append('description', description)
-
- for (let i = 0; i < tags.length; i++) {
- form.append(`tags[${i}]`, tags[i])
- }
- }
-
this.buildForm()
}
- checkForm () {
- this.forceCheck()
-
- if (this.filename === null) {
- this.fileError = 'You did not add a file.'
- }
-
- return this.form.valid === true && this.fileError === ''
+ // The goal is to keep reactive form validation (required field)
+ // https://stackoverflow.com/a/44238894
+ fileChange ($event) {
+ this.form.controls['videofile'].setValue($event.target.files[0].name)
}
- fileChanged () {
- this.fileError = ''
+ removeFile () {
+ this.videofileInput.nativeElement.value = ''
+ this.form.controls['videofile'].setValue('')
}
- removeFile () {
- this.uploader.clearQueue()
+ checkForm () {
+ this.forceCheck()
+
+ return this.form.valid
}
upload () {
return
}
- const item = this.uploader.queue[0]
- // TODO: wait for https://github.com/valor-software/ng2-file-upload/pull/242
- item.alias = 'videofile'
-
- item.onSuccess = () => {
- console.log('Video uploaded.')
- this.notificationsService.success('Success', 'Video uploaded.')
-
- // Print all the videos once it's finished
- this.router.navigate(['/videos/list'])
+ const formValue: VideoCreate = this.form.value
+
+ const name = formValue.name
+ const nsfw = formValue.nsfw
+ const category = formValue.category
+ const licence = formValue.licence
+ const language = formValue.language
+ const description = formValue.description
+ const tags = formValue.tags
+ const videofile = this.videofileInput.nativeElement.files[0]
+
+ const formData = new FormData()
+ formData.append('name', name)
+ formData.append('category', '' + category)
+ formData.append('nsfw', '' + nsfw)
+ formData.append('licence', '' + licence)
+ formData.append('videofile', videofile)
+
+ // Language is optional
+ if (language) {
+ formData.append('language', '' + language)
}
- item.onError = (response: string, status: number) => {
- // We need to handle manually these cases because we use the FileUpload component
- if (status === 400) {
- this.error = response
- } else if (status === 401) {
- this.error = 'Access token was expired, refreshing token...'
- this.authService.refreshAccessToken().subscribe(
- () => {
- // Update the uploader request header
- this.uploader.authToken = this.authService.getRequestHeaderValue()
- this.error += ' access token refreshed. Please retry your request.'
- }
- )
- } else if (status === 403) {
- this.error = 'Your video quota is reached, you can\'t upload this video.'
- } else {
- this.error = 'Unknown error'
- console.error(this.error)
- }
+ formData.append('description', description)
+
+ for (let i = 0; i < tags.length; i++) {
+ formData.append(`tags[${i}]`, tags[i])
}
- this.uploader.uploadAll()
+ this.videoService.uploadVideo(formData).subscribe(
+ event => {
+ if (event.type === HttpEventType.UploadProgress) {
+ this.progressPercent = Math.round(100 * event.loaded / event.total)
+ } else if (event instanceof HttpResponse) {
+ console.log('Video uploaded.')
+ this.notificationsService.success('Success', 'Video uploaded.')
+
+ // Display all the videos once it's finished
+ this.router.navigate([ '/videos/list '])
+ }
+ },
+
+ err => this.error = err.message
+ )
}
}
import { FormBuilder, FormGroup } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router'
-import { FileUploader } from 'ng2-file-upload/ng2-file-upload'
import { NotificationsService } from 'angular2-notifications'
import { AuthService } from '../../core'
this.hide()
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
}
this.userRating = 'like'
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
this.userRating = 'dislike'
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
}
},
- err => this.notificationsService.error('Error', err)
+ err => this.notificationsService.error('Error', err.message)
)
}
loader-utils "^0.2.16"
recast "^0.11.20"
-ng2-file-upload@^1.1.4-2:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/ng2-file-upload/-/ng2-file-upload-1.2.1.tgz#5563c5dfd6f43fbfbe815c206e343464a0a6a197"
-
ng2-material-dropdown@0.7.10:
version "0.7.10"
resolved "https://registry.yarnpkg.com/ng2-material-dropdown/-/ng2-material-dropdown-0.7.10.tgz#093471f2a9cadd726cbcb120b0ad7818a54fa5ed"
function makeFriendsValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
// Force https if the administrator wants to make friends
if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') {
- return res.status(400).send('Cannot make friends with a non HTTPS web server.')
+ return res.status(400)
+ .json({
+ error: 'Cannot make friends with a non HTTPS web server.'
+ })
+ .end()
}
req.checkBody('hosts', 'Should have an array of unique hosts').isEachUniqueHostValid()
return res.sendStatus(500)
}
- if (user.username === 'root') return res.status(400).send('Cannot remove the root user')
+ if (user.username === 'root') {
+ return res.status(400)
+ .send({ error: 'Cannot remove the root user' })
+ .end()
+ }
- next()
+ return next()
})
})
}
videoPromise
.then(video => {
- if (!video) return res.status(404).send('Video not found')
+ if (!video) {
+ return res.status(404)
+ .json({ error: 'Video not found' })
+ .end()
+ }
- next()
+ return next()
})
.catch(err => {
logger.error('Error in user request validator.', err)
function ensureUserRegistrationAllowed (req: express.Request, res: express.Response, next: express.NextFunction) {
isSignupAllowed().then(allowed => {
if (allowed === false) {
- return res.status(403).send('User registration is not enabled or user limit is reached.')
+ return res.status(403)
+ .send({ error: 'User registration is not enabled or user limit is reached.' })
+ .end()
}
return next()
function checkUserExists (id: number, res: express.Response, callback: (err: Error, user: UserInstance) => void) {
db.User.loadById(id)
.then(user => {
- if (!user) return res.status(404).send('User not found')
+ if (!user) {
+ return res.status(404)
+ .send({ error: 'User not found' })
+ .end()
+ }
res.locals.user = user
- callback(null, user)
+ return callback(null, user)
})
.catch(err => {
logger.error('Error in user request validator.', err)
function checkUserDoesNotAlreadyExist (username: string, email: string, res: express.Response, callback: () => void) {
db.User.loadByUsernameOrEmail(username, email)
.then(user => {
- if (user) return res.status(409).send('User already exists.')
+ if (user) {
+ return res.status(409)
+ .send({ error: 'User already exists.' })
+ .end()
+ }
- callback()
+ return callback()
})
.catch(err => {
logger.error('Error in usersAdd request validator.', err)
user.isAbleToUploadVideo(videoFile)
.then(isAble => {
if (isAble === false) {
- res.status(403).send('The user video quota is exceeded with this video.')
+ res.status(403)
+ .json({ error: 'The user video quota is exceeded with this video.' })
+ .end()
return undefined
}
return db.Video.getDurationFromFile(videoFile.path)
.catch(err => {
logger.error('Invalid input file in videosAddValidator.', err)
- res.status(400).send('Invalid input file.')
+ res.status(400)
+ .json({ error: 'Invalid input file.' })
+ .end()
return undefined
})
})
.then(duration => {
// Previous test failed, abort
- if (duration === undefined) return undefined
+ if (duration === undefined) return
if (!isVideoDurationValid('' + duration)) {
- return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
+ return res.status(400)
+ .json({
+ error: 'Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).'
+ })
+ .end()
}
videoFile['duration'] = duration
checkVideoExists(req.params.id, res, () => {
// We need to make additional checks
if (res.locals.video.isOwned() === false) {
- return res.status(403).send('Cannot update video of another pod')
+ return res.status(403)
+ .json({ error: 'Cannot update video of another pod' })
+ .end()
}
if (res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
- return res.status(403).send('Cannot update video of another user')
+ return res.status(403)
+ .json({ error: 'Cannot update video of another user' })
+ .end()
}
next()
}
promise.then(video => {
- if (!video) return res.status(404).send('Video not found')
+ if (!video) {
+ return res.status(404)
+ .json({ error: 'Video not found' })
+ .end()
+ }
res.locals.video = video
callback()
db.User.loadById(userId)
.then(user => {
if (res.locals.video.isOwned() === false) {
- return res.status(403).send('Cannot remove video of another pod, blacklist it')
+ return res.status(403)
+ .json({ error: 'Cannot remove video of another pod, blacklist it' })
+ .end()
}
// Check if the user can delete the video
// The user can delete it if s/he is an admin
// Or if s/he is the video's author
if (user.isAdmin() === false && res.locals.video.Author.userId !== res.locals.oauth.token.User.id) {
- return res.status(403).send('Cannot remove video of another user')
+ return res.status(403)
+ .json({ error: 'Cannot remove video of another user' })
+ .end()
}
// If we reach this comment, we can delete the video
function checkVideoIsBlacklistable (req: express.Request, res: express.Response, callback: () => void) {
if (res.locals.video.isOwned() === true) {
- return res.status(403).send('Cannot blacklist a local video')
+ return res.status(403)
+ .json({ error: 'Cannot blacklist a local video' })
+ .end()
}
callback()