]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/videos.js
Server: add video abuse support
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos.js
CommitLineData
9f10b292
C
1'use strict'
2
f0f5567b 3const express = require('express')
558d7c23 4const fs = require('fs')
f0f5567b 5const multer = require('multer')
558d7c23 6const path = require('path')
1a42c9e2 7const waterfall = require('async/waterfall')
f0f5567b 8
f253b1c1 9const constants = require('../../initializers/constants')
feb4bdfd 10const db = require('../../initializers/database')
f253b1c1
C
11const logger = require('../../helpers/logger')
12const friends = require('../../lib/friends')
13const middlewares = require('../../middlewares')
55fa55a9 14const admin = middlewares.admin
69b0a27c 15const oAuth = middlewares.oauth
fbf1134e 16const pagination = middlewares.pagination
fc51fde0
C
17const validators = middlewares.validators
18const validatorsPagination = validators.pagination
19const validatorsSort = validators.sort
20const validatorsVideos = validators.videos
46246b5f 21const search = middlewares.search
a877d5ac 22const sort = middlewares.sort
f253b1c1 23const utils = require('../../helpers/utils')
f0f5567b
C
24
25const router = express.Router()
9f10b292
C
26
27// multer configuration
f0f5567b 28const storage = multer.diskStorage({
9f10b292 29 destination: function (req, file, cb) {
b3d92510 30 cb(null, constants.CONFIG.STORAGE.VIDEOS_DIR)
9f10b292
C
31 },
32
33 filename: function (req, file, cb) {
f0f5567b 34 let extension = ''
9f10b292
C
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'
bc503c2a
C
38 utils.generateRandomString(16, function (err, randomString) {
39 const fieldname = err ? undefined : randomString
9f10b292
C
40 cb(null, fieldname + '.' + extension)
41 })
42 }
43})
44
8c9c1942 45const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
8c308c2b 46
55fa55a9
C
47router.get('/abuse',
48 oAuth.authenticate,
49 admin.ensureIsAdmin,
50 validatorsPagination.pagination,
51 validatorsSort.videoAbusesSort,
52 sort.setVideoAbusesSort,
53 pagination.setPagination,
54 listVideoAbuses
55)
56router.post('/:id/abuse',
57 oAuth.authenticate,
58 validatorsVideos.videoAbuseReport,
59 reportVideoAbuse
60)
61
fbf1134e 62router.get('/',
fc51fde0
C
63 validatorsPagination.pagination,
64 validatorsSort.videosSort,
a877d5ac 65 sort.setVideosSort,
fbf1134e
C
66 pagination.setPagination,
67 listVideos
68)
7b1f49de
C
69router.put('/:id',
70 oAuth.authenticate,
71 reqFiles,
72 validatorsVideos.videosUpdate,
73 updateVideo
74)
fbf1134e 75router.post('/',
69b0a27c 76 oAuth.authenticate,
fbf1134e 77 reqFiles,
fc51fde0 78 validatorsVideos.videosAdd,
fbf1134e
C
79 addVideo
80)
81router.get('/:id',
fc51fde0 82 validatorsVideos.videosGet,
68ce3ae0 83 getVideo
fbf1134e
C
84)
85router.delete('/:id',
69b0a27c 86 oAuth.authenticate,
fc51fde0 87 validatorsVideos.videosRemove,
fbf1134e
C
88 removeVideo
89)
46246b5f 90router.get('/search/:value',
fc51fde0
C
91 validatorsVideos.videosSearch,
92 validatorsPagination.pagination,
93 validatorsSort.videosSort,
a877d5ac 94 sort.setVideosSort,
fbf1134e 95 pagination.setPagination,
46246b5f 96 search.setVideosSearch,
fbf1134e
C
97 searchVideos
98)
8c308c2b 99
9f10b292 100// ---------------------------------------------------------------------------
c45f7f84 101
9f10b292 102module.exports = router
c45f7f84 103
9f10b292 104// ---------------------------------------------------------------------------
c45f7f84 105
9f10b292 106function addVideo (req, res, next) {
bc503c2a
C
107 const videoFile = req.files.videofile[0]
108 const videoInfos = req.body
9f10b292 109
1a42c9e2 110 waterfall([
807df9e6 111
7920c273
C
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) {
4712081f 119 const user = res.locals.oauth.token.User
feb4bdfd 120
4ff0d862
C
121 const name = user.username
122 // null because it is OUR pod
123 const podId = null
124 const userId = user.id
4712081f 125
4ff0d862 126 db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
4712081f 127 return callback(err, t, authorInstance)
7920c273
C
128 })
129 },
130
131 function findOrCreateTags (t, author, callback) {
132 const tags = videoInfos.tags
4ff0d862
C
133
134 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
7920c273 135 return callback(err, t, author, tagInstances)
feb4bdfd
C
136 })
137 },
138
7920c273 139 function createVideoObject (t, author, tagInstances, callback) {
807df9e6
C
140 const videoData = {
141 name: videoInfos.name,
558d7c23
C
142 remoteId: null,
143 extname: path.extname(videoFile.filename),
807df9e6 144 description: videoInfos.description,
67100f1f 145 duration: videoFile.duration,
feb4bdfd 146 authorId: author.id
807df9e6
C
147 }
148
feb4bdfd 149 const video = db.Video.build(videoData)
558d7c23 150
7920c273 151 return callback(null, t, author, tagInstances, video)
558d7c23
C
152 },
153
feb4bdfd 154 // Set the videoname the same as the id
7920c273 155 function renameVideoFile (t, author, tagInstances, video, callback) {
558d7c23
C
156 const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR
157 const source = path.join(videoDir, videoFile.filename)
f285faa0 158 const destination = path.join(videoDir, video.getVideoFilename())
558d7c23
C
159
160 fs.rename(source, destination, function (err) {
7920c273 161 return callback(err, t, author, tagInstances, video)
558d7c23
C
162 })
163 },
164
7920c273
C
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
feb4bdfd
C
172 // Do not forget to add Author informations to the created video
173 videoCreated.Author = author
174
7920c273 175 return callback(err, t, tagInstances, videoCreated)
3a8a8b51 176 })
807df9e6
C
177 },
178
7920c273
C
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) {
7b1f49de 190 video.toAddRemoteJSON(function (err, remoteVideo) {
aaf61f38 191 if (err) return callback(err)
807df9e6 192
528a9efa
C
193 // Now we'll add the video's meta data to our friends
194 friends.addVideoToFriends(remoteVideo)
807df9e6 195
7920c273 196 return callback(null, t)
528a9efa 197 })
807df9e6
C
198 }
199
7b1f49de
C
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
218function updateVideo (req, res, next) {
818f7987 219 const videoInstance = res.locals.video
7b1f49de
C
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) {
7b1f49de
C
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
7920c273 275 ], function andFinally (err, t) {
807df9e6
C
276 if (err) {
277 logger.error('Cannot insert the video.')
7920c273
C
278
279 // Abort transaction?
280 if (t) t.rollback()
281
807df9e6
C
282 return next(err)
283 }
284
7920c273
C
285 // Commit transaction
286 t.commit()
287
807df9e6
C
288 // TODO : include Location of the new video -> 201
289 return res.type('json').status(204).end()
9f10b292
C
290 })
291}
8c308c2b 292
68ce3ae0 293function getVideo (req, res, next) {
818f7987
C
294 const videoInstance = res.locals.video
295 res.json(videoInstance.toFormatedJSON())
9f10b292 296}
8c308c2b 297
9f10b292 298function listVideos (req, res, next) {
feb4bdfd 299 db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
9f10b292 300 if (err) return next(err)
c45f7f84 301
55fa55a9 302 res.json(utils.getFormatedObjects(videosList, videosTotal))
9f10b292
C
303 })
304}
c45f7f84 305
9f10b292 306function removeVideo (req, res, next) {
818f7987 307 const videoInstance = res.locals.video
8c308c2b 308
818f7987 309 videoInstance.destroy().asCallback(function (err) {
807df9e6
C
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()
9f10b292
C
316 })
317}
8c308c2b 318
9f10b292 319function searchVideos (req, res, next) {
7920c273
C
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)
8c308c2b 324
55fa55a9 325 res.json(utils.getFormatedObjects(videosList, videosTotal))
7920c273
C
326 }
327 )
9f10b292 328}
c173e565 329
55fa55a9
C
330function 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)
2df82d42 333
55fa55a9 334 res.json(utils.getFormatedObjects(abusesList, abusesTotal))
2df82d42 335 })
55fa55a9 336}
2df82d42 337
55fa55a9
C
338function 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
68ce3ae0 347 }
55fa55a9
C
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 })
2df82d42 365}
55fa55a9 366