]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/videos.js
Server: add video abuse support
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos.js
1 'use strict'
2
3 const express = require('express')
4 const fs = require('fs')
5 const multer = require('multer')
6 const path = require('path')
7 const waterfall = require('async/waterfall')
8
9 const constants = require('../../initializers/constants')
10 const db = require('../../initializers/database')
11 const logger = require('../../helpers/logger')
12 const friends = require('../../lib/friends')
13 const middlewares = require('../../middlewares')
14 const admin = middlewares.admin
15 const oAuth = middlewares.oauth
16 const pagination = middlewares.pagination
17 const validators = middlewares.validators
18 const validatorsPagination = validators.pagination
19 const validatorsSort = validators.sort
20 const validatorsVideos = validators.videos
21 const search = middlewares.search
22 const sort = middlewares.sort
23 const utils = require('../../helpers/utils')
24
25 const router = express.Router()
26
27 // multer configuration
28 const storage = multer.diskStorage({
29 destination: function (req, file, cb) {
30 cb(null, constants.CONFIG.STORAGE.VIDEOS_DIR)
31 },
32
33 filename: function (req, file, cb) {
34 let extension = ''
35 if (file.mimetype === 'video/webm') extension = 'webm'
36 else if (file.mimetype === 'video/mp4') extension = 'mp4'
37 else if (file.mimetype === 'video/ogg') extension = 'ogv'
38 utils.generateRandomString(16, function (err, randomString) {
39 const fieldname = err ? undefined : randomString
40 cb(null, fieldname + '.' + extension)
41 })
42 }
43 })
44
45 const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
46
47 router.get('/abuse',
48 oAuth.authenticate,
49 admin.ensureIsAdmin,
50 validatorsPagination.pagination,
51 validatorsSort.videoAbusesSort,
52 sort.setVideoAbusesSort,
53 pagination.setPagination,
54 listVideoAbuses
55 )
56 router.post('/:id/abuse',
57 oAuth.authenticate,
58 validatorsVideos.videoAbuseReport,
59 reportVideoAbuse
60 )
61
62 router.get('/',
63 validatorsPagination.pagination,
64 validatorsSort.videosSort,
65 sort.setVideosSort,
66 pagination.setPagination,
67 listVideos
68 )
69 router.put('/:id',
70 oAuth.authenticate,
71 reqFiles,
72 validatorsVideos.videosUpdate,
73 updateVideo
74 )
75 router.post('/',
76 oAuth.authenticate,
77 reqFiles,
78 validatorsVideos.videosAdd,
79 addVideo
80 )
81 router.get('/:id',
82 validatorsVideos.videosGet,
83 getVideo
84 )
85 router.delete('/:id',
86 oAuth.authenticate,
87 validatorsVideos.videosRemove,
88 removeVideo
89 )
90 router.get('/search/:value',
91 validatorsVideos.videosSearch,
92 validatorsPagination.pagination,
93 validatorsSort.videosSort,
94 sort.setVideosSort,
95 pagination.setPagination,
96 search.setVideosSearch,
97 searchVideos
98 )
99
100 // ---------------------------------------------------------------------------
101
102 module.exports = router
103
104 // ---------------------------------------------------------------------------
105
106 function addVideo (req, res, next) {
107 const videoFile = req.files.videofile[0]
108 const videoInfos = req.body
109
110 waterfall([
111
112 function startTransaction (callback) {
113 db.sequelize.transaction().asCallback(function (err, t) {
114 return callback(err, t)
115 })
116 },
117
118 function findOrCreateAuthor (t, callback) {
119 const user = res.locals.oauth.token.User
120
121 const name = user.username
122 // null because it is OUR pod
123 const podId = null
124 const userId = user.id
125
126 db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
127 return callback(err, t, authorInstance)
128 })
129 },
130
131 function findOrCreateTags (t, author, callback) {
132 const tags = videoInfos.tags
133
134 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
135 return callback(err, t, author, tagInstances)
136 })
137 },
138
139 function createVideoObject (t, author, tagInstances, callback) {
140 const videoData = {
141 name: videoInfos.name,
142 remoteId: null,
143 extname: path.extname(videoFile.filename),
144 description: videoInfos.description,
145 duration: videoFile.duration,
146 authorId: author.id
147 }
148
149 const video = db.Video.build(videoData)
150
151 return callback(null, t, author, tagInstances, video)
152 },
153
154 // Set the videoname the same as the id
155 function renameVideoFile (t, author, tagInstances, video, callback) {
156 const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR
157 const source = path.join(videoDir, videoFile.filename)
158 const destination = path.join(videoDir, video.getVideoFilename())
159
160 fs.rename(source, destination, function (err) {
161 return callback(err, t, author, tagInstances, video)
162 })
163 },
164
165 function insertVideoIntoDB (t, author, tagInstances, video, callback) {
166 const options = { transaction: t }
167
168 // Add tags association
169 video.save(options).asCallback(function (err, videoCreated) {
170 if (err) return callback(err)
171
172 // Do not forget to add Author informations to the created video
173 videoCreated.Author = author
174
175 return callback(err, t, tagInstances, videoCreated)
176 })
177 },
178
179 function associateTagsToVideo (t, tagInstances, video, callback) {
180 const options = { transaction: t }
181
182 video.setTags(tagInstances, options).asCallback(function (err) {
183 video.Tags = tagInstances
184
185 return callback(err, t, video)
186 })
187 },
188
189 function sendToFriends (t, video, callback) {
190 video.toAddRemoteJSON(function (err, remoteVideo) {
191 if (err) return callback(err)
192
193 // Now we'll add the video's meta data to our friends
194 friends.addVideoToFriends(remoteVideo)
195
196 return callback(null, t)
197 })
198 }
199
200 ], function andFinally (err, t) {
201 if (err) {
202 logger.error('Cannot insert the video.')
203
204 // Abort transaction?
205 if (t) t.rollback()
206
207 return next(err)
208 }
209
210 // Commit transaction
211 t.commit()
212
213 // TODO : include Location of the new video -> 201
214 return res.type('json').status(204).end()
215 })
216 }
217
218 function updateVideo (req, res, next) {
219 const videoInstance = res.locals.video
220 const videoInfosToUpdate = req.body
221
222 waterfall([
223
224 function startTransaction (callback) {
225 db.sequelize.transaction().asCallback(function (err, t) {
226 return callback(err, t)
227 })
228 },
229
230 function findOrCreateTags (t, callback) {
231 if (videoInfosToUpdate.tags) {
232 db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) {
233 return callback(err, t, tagInstances)
234 })
235 } else {
236 return callback(null, t, null)
237 }
238 },
239
240 function updateVideoIntoDB (t, tagInstances, callback) {
241 const options = { transaction: t }
242
243 if (videoInfosToUpdate.name) videoInstance.set('name', videoInfosToUpdate.name)
244 if (videoInfosToUpdate.description) videoInstance.set('description', videoInfosToUpdate.description)
245
246 // Add tags association
247 videoInstance.save(options).asCallback(function (err) {
248 return callback(err, t, tagInstances)
249 })
250 },
251
252 function associateTagsToVideo (t, tagInstances, callback) {
253 if (tagInstances) {
254 const options = { transaction: t }
255
256 videoInstance.setTags(tagInstances, options).asCallback(function (err) {
257 videoInstance.Tags = tagInstances
258
259 return callback(err, t)
260 })
261 } else {
262 return callback(null, t)
263 }
264 },
265
266 function sendToFriends (t, callback) {
267 const json = videoInstance.toUpdateRemoteJSON()
268
269 // Now we'll update the video's meta data to our friends
270 friends.updateVideoToFriends(json)
271
272 return callback(null, t)
273 }
274
275 ], function andFinally (err, t) {
276 if (err) {
277 logger.error('Cannot insert the video.')
278
279 // Abort transaction?
280 if (t) t.rollback()
281
282 return next(err)
283 }
284
285 // Commit transaction
286 t.commit()
287
288 // TODO : include Location of the new video -> 201
289 return res.type('json').status(204).end()
290 })
291 }
292
293 function getVideo (req, res, next) {
294 const videoInstance = res.locals.video
295 res.json(videoInstance.toFormatedJSON())
296 }
297
298 function listVideos (req, res, next) {
299 db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
300 if (err) return next(err)
301
302 res.json(utils.getFormatedObjects(videosList, videosTotal))
303 })
304 }
305
306 function removeVideo (req, res, next) {
307 const videoInstance = res.locals.video
308
309 videoInstance.destroy().asCallback(function (err) {
310 if (err) {
311 logger.error('Errors when removed the video.', { error: err })
312 return next(err)
313 }
314
315 return res.type('json').status(204).end()
316 })
317 }
318
319 function searchVideos (req, res, next) {
320 db.Video.searchAndPopulateAuthorAndPodAndTags(
321 req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
322 function (err, videosList, videosTotal) {
323 if (err) return next(err)
324
325 res.json(utils.getFormatedObjects(videosList, videosTotal))
326 }
327 )
328 }
329
330 function listVideoAbuses (req, res, next) {
331 db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort, function (err, abusesList, abusesTotal) {
332 if (err) return next(err)
333
334 res.json(utils.getFormatedObjects(abusesList, abusesTotal))
335 })
336 }
337
338 function reportVideoAbuse (req, res, next) {
339 const videoInstance = res.locals.video
340 const reporterUsername = res.locals.oauth.token.User.username
341
342 const abuse = {
343 reporterUsername,
344 reason: req.body.reason,
345 videoId: videoInstance.id,
346 reporterPodId: null // This is our pod that reported this abuse
347 }
348
349 db.VideoAbuse.create(abuse).asCallback(function (err) {
350 if (err) return next(err)
351
352 // We send the information to the destination pod
353 if (videoInstance.isOwned() === false) {
354 const reportData = {
355 reporterUsername,
356 reportReason: abuse.reason,
357 videoRemoteId: videoInstance.remoteId
358 }
359
360 friends.reportAbuseVideoToFriend(reportData, videoInstance)
361 }
362
363 return res.type('json').status(204).end()
364 })
365 }
366