Skip to content

Commit

Permalink
Add video category support
Browse files Browse the repository at this point in the history
  • Loading branch information
Chocobozzz committed Mar 22, 2017
1 parent 2d7653d commit 6e07c3d
Show file tree
Hide file tree
Showing 28 changed files with 324 additions and 30 deletions.
4 changes: 4 additions & 0 deletions client/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, OnInit, ViewContainerRef } from '@angular/core';
import { Router } from '@angular/router';

import { AuthService } from './core';
import { VideoService } from './videos';
import { UserService } from './shared';

@Component({
Expand All @@ -27,6 +28,7 @@ export class AppComponent implements OnInit {
private router: Router,
private authService: AuthService,
private userService: UserService,
private videoService: VideoService,
viewContainerRef: ViewContainerRef
) {}

Expand All @@ -35,6 +37,8 @@ export class AppComponent implements OnInit {
// The service will automatically redirect to the login page if the token is not valid anymore
this.userService.checkTokenValidity();
}

this.videoService.loadVideoCategories();
}

isInAdmin() {
Expand Down
6 changes: 6 additions & 0 deletions client/src/app/shared/forms/form-validators/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export const VIDEO_NAME = {
'maxlength': 'Video name cannot be more than 50 characters long.'
}
};
export const VIDEO_CATEGORY = {
VALIDATORS: [ Validators.required ],
MESSAGES: {
'required': 'Video category is required.'
}
};
export const VIDEO_DESCRIPTION = {
VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(250) ],
MESSAGES: {
Expand Down
3 changes: 3 additions & 0 deletions client/src/app/videos/shared/video.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export class Video {
author: string;
by: string;
createdAt: Date;
categoryLabel: string;
description: string;
duration: string;
id: string;
Expand Down Expand Up @@ -31,6 +32,7 @@ export class Video {
constructor(hash: {
author: string,
createdAt: string,
categoryLabel: string,
description: string,
duration: number;
id: string,
Expand All @@ -46,6 +48,7 @@ export class Video {
}) {
this.author = hash.author;
this.createdAt = new Date(hash.createdAt);
this.categoryLabel = hash.categoryLabel;
this.description = hash.description;
this.duration = Video.createDurationString(hash.duration);
this.id = hash.id;
Expand Down
15 changes: 15 additions & 0 deletions client/src/app/videos/shared/video.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { Video } from './video.model';
export class VideoService {
private static BASE_VIDEO_URL = '/api/v1/videos/';

videoCategories: Array<{ id: number, label: string }> = [];

constructor(
private authService: AuthService,
private authHttp: AuthHttp,
Expand All @@ -30,6 +32,19 @@ export class VideoService {
private restService: RestService
) {}

loadVideoCategories() {
return this.http.get(VideoService.BASE_VIDEO_URL + 'categories')
.map(this.restExtractor.extractDataGet)
.subscribe(data => {
Object.keys(data).forEach(categoryKey => {
this.videoCategories.push({
id: parseInt(categoryKey),
label: data[categoryKey]
});
});
});
}

getVideo(id: string): Observable<Video> {
return this.http.get(VideoService.BASE_VIDEO_URL + id)
.map(this.restExtractor.extractDataGet)
Expand Down
12 changes: 12 additions & 0 deletions client/src/app/videos/video-add/video-add.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ <h3>Upload a video</h3>
</div>
</div>

<div class="form-group">
<label for="category">Category</label>
<select class="form-control" id="category" formControlName="category">
<option></option>
<option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
</select>

<div *ngIf="formErrors.category" class="alert alert-danger">
{{ formErrors.category }}
</div>
</div>

<div class="form-group">
<label for="tags">Tags</label> <span class="little-information">(press enter to add the tag)</span>
<input
Expand Down
20 changes: 18 additions & 2 deletions client/src/app/videos/video-add/video-add.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import { FileUploader } from 'ng2-file-upload/ng2-file-upload';
import { NotificationsService } from 'angular2-notifications';

import { AuthService } from '../../core';
import { FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../shared';
import {
FormReactive,
VIDEO_NAME,
VIDEO_CATEGORY,
VIDEO_DESCRIPTION,
VIDEO_TAGS
} from '../../shared';
import { VideoService } from '../shared';

@Component({
selector: 'my-videos-add',
Expand All @@ -17,16 +24,19 @@ import { FormReactive, VIDEO_NAME, VIDEO_DESCRIPTION, VIDEO_TAGS } from '../../s
export class VideoAddComponent extends FormReactive implements OnInit {
tags: string[] = [];
uploader: FileUploader;
videoCategories = [];

error: string = null;
form: FormGroup;
formErrors = {
name: '',
category: '',
description: '',
currentTag: ''
};
validationMessages = {
name: VIDEO_NAME.MESSAGES,
category: VIDEO_CATEGORY.MESSAGES,
description: VIDEO_DESCRIPTION.MESSAGES,
currentTag: VIDEO_TAGS.MESSAGES
};
Expand All @@ -40,7 +50,8 @@ export class VideoAddComponent extends FormReactive implements OnInit {
private elementRef: ElementRef,
private formBuilder: FormBuilder,
private router: Router,
private notificationsService: NotificationsService
private notificationsService: NotificationsService,
private videoService: VideoService
) {
super();
}
Expand All @@ -56,6 +67,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
buildForm() {
this.form = this.formBuilder.group({
name: [ '', VIDEO_NAME.VALIDATORS ],
category: [ '', VIDEO_CATEGORY.VALIDATORS ],
description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
currentTag: [ '', VIDEO_TAGS.VALIDATORS ]
});
Expand All @@ -64,6 +76,8 @@ export class VideoAddComponent extends FormReactive implements OnInit {
}

ngOnInit() {
this.videoCategories = this.videoService.videoCategories;

this.uploader = new FileUploader({
authToken: this.authService.getRequestHeaderValue(),
queueLimit: 1,
Expand All @@ -73,9 +87,11 @@ export class VideoAddComponent extends FormReactive implements OnInit {

this.uploader.onBuildItemForm = (item, form) => {
const name = this.form.value['name'];
const category = this.form.value['category'];
const description = this.form.value['description'];

form.append('name', name);
form.append('category', category);
form.append('description', description);

for (let i = 0; i < this.tags.length; i++) {
Expand Down
7 changes: 7 additions & 0 deletions client/src/app/videos/video-watch/video-watch.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@
</div>
</div>

<div id="video-category" class="row">
<div class="col-md-12">
<span id="category-label">Category:</span>
{{ video.categoryLabel }}
</div>
</div>

<div id="video-description" class="row">
<div class="col-md-12">
<div id="description-label">Description</div>
Expand Down
10 changes: 9 additions & 1 deletion client/src/app/videos/video-watch/video-watch.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,16 @@
}
}

#video-category {
margin-top: 10px;

#category-label {
font-weight: bold;
}
}

#video-description {
margin-top: 15px;
margin-top: 10px;

#description-label {
font-weight: bold;
Expand Down
2 changes: 2 additions & 0 deletions server/controllers/api/remote/videos.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) {
remoteId: videoToCreateData.remoteId,
extname: videoToCreateData.extname,
infoHash: videoToCreateData.infoHash,
category: videoToCreateData.category,
description: videoToCreateData.description,
authorId: author.id,
duration: videoToCreateData.duration,
Expand Down Expand Up @@ -390,6 +391,7 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) {
const options = { transaction: t }

videoInstance.set('name', videoAttributesToUpdate.name)
videoInstance.set('category', videoAttributesToUpdate.category)
videoInstance.set('description', videoAttributesToUpdate.description)
videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
videoInstance.set('duration', videoAttributesToUpdate.duration)
Expand Down
8 changes: 8 additions & 0 deletions server/controllers/api/videos.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const storage = multer.diskStorage({

const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])

router.get('/categories', listVideoCategories)

router.get('/abuse',
oAuth.authenticate,
admin.ensureIsAdmin,
Expand Down Expand Up @@ -110,6 +112,10 @@ module.exports = router

// ---------------------------------------------------------------------------

function listVideoCategories (req, res, next) {
res.json(constants.VIDEO_CATEGORIES)
}

function rateVideoRetryWrapper (req, res, next) {
const options = {
arguments: [ req, res ],
Expand Down Expand Up @@ -300,6 +306,7 @@ function addVideo (req, res, videoFile, finalCallback) {
name: videoInfos.name,
remoteId: null,
extname: path.extname(videoFile.filename),
category: videoInfos.category,
description: videoInfos.description,
duration: videoFile.duration,
authorId: author.id
Expand Down Expand Up @@ -413,6 +420,7 @@ function updateVideo (req, res, finalCallback) {
}

if (videoInfosToUpdate.name) videoInstance.set('name', videoInfosToUpdate.name)
if (videoInfosToUpdate.category) videoInstance.set('category', videoInfosToUpdate.category)
if (videoInfosToUpdate.description) videoInstance.set('description', videoInfosToUpdate.description)

videoInstance.save(options).asCallback(function (err) {
Expand Down
1 change: 1 addition & 0 deletions server/helpers/custom-validators/remote/videos.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ module.exports = remoteVideosValidators
function isCommonVideoAttributesValid (video) {
return videosValidators.isVideoDateValid(video.createdAt) &&
videosValidators.isVideoDateValid(video.updatedAt) &&
videosValidators.isVideoCategoryValid(video.category) &&
videosValidators.isVideoDescriptionValid(video.description) &&
videosValidators.isVideoDurationValid(video.duration) &&
videosValidators.isVideoInfoHashValid(video.infoHash) &&
Expand Down
5 changes: 5 additions & 0 deletions server/helpers/custom-validators/videos.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const VIDEO_EVENTS_CONSTRAINTS_FIELDS = constants.CONSTRAINTS_FIELDS.VIDEO_EVENT
const videosValidators = {
isVideoAuthorValid,
isVideoDateValid,
isVideoCategoryValid,
isVideoDescriptionValid,
isVideoDurationValid,
isVideoInfoHashValid,
Expand Down Expand Up @@ -40,6 +41,10 @@ function isVideoDateValid (value) {
return validator.isDate(value)
}

function isVideoCategoryValid (value) {
return constants.VIDEO_CATEGORIES[value] !== undefined
}

function isVideoDescriptionValid (value) {
return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)
}
Expand Down
24 changes: 23 additions & 1 deletion server/initializers/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const path = require('path')

// ---------------------------------------------------------------------------

const LAST_MIGRATION_VERSION = 25
const LAST_MIGRATION_VERSION = 30

// ---------------------------------------------------------------------------

Expand Down Expand Up @@ -103,6 +103,27 @@ const VIDEO_RATE_TYPES = {
DISLIKE: 'dislike'
}

const VIDEO_CATEGORIES = {
1: 'Music',
2: 'Films',
3: 'Vehicles',
4: 'Art',
5: 'Sports',
6: 'Travels',
7: 'Gaming',
8: 'People',
9: 'Comedy',
10: 'Entertainment',
11: 'News',
12: 'Howto',
13: 'Education',
14: 'Activism',
15: 'Science & Technology',
16: 'Animals',
17: 'Kids',
18: 'Food'
}

// ---------------------------------------------------------------------------

// Score a pod has when we create it as a friend
Expand Down Expand Up @@ -258,6 +279,7 @@ module.exports = {
STATIC_PATHS,
THUMBNAILS_SIZE,
USER_ROLES,
VIDEO_CATEGORIES,
VIDEO_RATE_TYPES
}

Expand Down
34 changes: 34 additions & 0 deletions server/initializers/migrations/0030-video-category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict'

const waterfall = require('async/waterfall')

// utils = { transaction, queryInterface, sequelize, Sequelize }
exports.up = function (utils, finalCallback) {
const q = utils.queryInterface
const Sequelize = utils.Sequelize

const data = {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0
}

waterfall([

function addCategoryColumn (callback) {
q.addColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(function (err) {
return callback(err)
})
},

function nullOnDefault (callback) {
data.defaultValue = null

q.changeColumn('Videos', 'category', data, { transaction: utils.transaction }).asCallback(callback)
}
], finalCallback)
}

exports.down = function (options, callback) {
throw new Error('Not implemented.')
}
2 changes: 2 additions & 0 deletions server/middlewares/validators/videos.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const validatorsVideos = {
function videosAdd (req, res, next) {
req.checkBody('videofile', 'Should have a valid file').isVideoFile(req.files)
req.checkBody('name', 'Should have a valid name').isVideoNameValid()
req.checkBody('category', 'Should have a valid category').isVideoCategoryValid()
req.checkBody('description', 'Should have a valid description').isVideoDescriptionValid()
req.checkBody('tags', 'Should have correct tags').isVideoTagsValid()

Expand All @@ -47,6 +48,7 @@ function videosAdd (req, res, next) {
function videosUpdate (req, res, next) {
req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid()
req.checkBody('category', 'Should have a valid category').optional().isVideoCategoryValid()
req.checkBody('description', 'Should have a valid description').optional().isVideoDescriptionValid()
req.checkBody('tags', 'Should have correct tags').optional().isVideoTagsValid()

Expand Down

0 comments on commit 6e07c3d

Please sign in to comment.