]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/videos.js
Server: optimization for videoGet and videoRemove
[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')
69b0a27c 14const oAuth = middlewares.oauth
fbf1134e 15const pagination = middlewares.pagination
fc51fde0
C
16const validators = middlewares.validators
17const validatorsPagination = validators.pagination
18const validatorsSort = validators.sort
19const validatorsVideos = validators.videos
46246b5f 20const search = middlewares.search
a877d5ac 21const sort = middlewares.sort
f253b1c1 22const utils = require('../../helpers/utils')
f0f5567b
C
23
24const router = express.Router()
9f10b292
C
25
26// multer configuration
f0f5567b 27const storage = multer.diskStorage({
9f10b292 28 destination: function (req, file, cb) {
b3d92510 29 cb(null, constants.CONFIG.STORAGE.VIDEOS_DIR)
9f10b292
C
30 },
31
32 filename: function (req, file, cb) {
f0f5567b 33 let extension = ''
9f10b292
C
34 if (file.mimetype === 'video/webm') extension = 'webm'
35 else if (file.mimetype === 'video/mp4') extension = 'mp4'
36 else if (file.mimetype === 'video/ogg') extension = 'ogv'
bc503c2a
C
37 utils.generateRandomString(16, function (err, randomString) {
38 const fieldname = err ? undefined : randomString
9f10b292
C
39 cb(null, fieldname + '.' + extension)
40 })
41 }
42})
43
8c9c1942 44const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
8c308c2b 45
fbf1134e 46router.get('/',
fc51fde0
C
47 validatorsPagination.pagination,
48 validatorsSort.videosSort,
a877d5ac 49 sort.setVideosSort,
fbf1134e
C
50 pagination.setPagination,
51 listVideos
52)
7b1f49de
C
53router.put('/:id',
54 oAuth.authenticate,
55 reqFiles,
56 validatorsVideos.videosUpdate,
57 updateVideo
58)
fbf1134e 59router.post('/',
69b0a27c 60 oAuth.authenticate,
fbf1134e 61 reqFiles,
fc51fde0 62 validatorsVideos.videosAdd,
fbf1134e
C
63 addVideo
64)
65router.get('/:id',
fc51fde0 66 validatorsVideos.videosGet,
68ce3ae0 67 getVideo
fbf1134e
C
68)
69router.delete('/:id',
69b0a27c 70 oAuth.authenticate,
fc51fde0 71 validatorsVideos.videosRemove,
fbf1134e
C
72 removeVideo
73)
46246b5f 74router.get('/search/:value',
fc51fde0
C
75 validatorsVideos.videosSearch,
76 validatorsPagination.pagination,
77 validatorsSort.videosSort,
a877d5ac 78 sort.setVideosSort,
fbf1134e 79 pagination.setPagination,
46246b5f 80 search.setVideosSearch,
fbf1134e
C
81 searchVideos
82)
8c308c2b 83
9f10b292 84// ---------------------------------------------------------------------------
c45f7f84 85
9f10b292 86module.exports = router
c45f7f84 87
9f10b292 88// ---------------------------------------------------------------------------
c45f7f84 89
9f10b292 90function addVideo (req, res, next) {
bc503c2a
C
91 const videoFile = req.files.videofile[0]
92 const videoInfos = req.body
9f10b292 93
1a42c9e2 94 waterfall([
807df9e6 95
7920c273
C
96 function startTransaction (callback) {
97 db.sequelize.transaction().asCallback(function (err, t) {
98 return callback(err, t)
99 })
100 },
101
102 function findOrCreateAuthor (t, callback) {
4712081f 103 const user = res.locals.oauth.token.User
feb4bdfd 104
4ff0d862
C
105 const name = user.username
106 // null because it is OUR pod
107 const podId = null
108 const userId = user.id
4712081f 109
4ff0d862 110 db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
4712081f 111 return callback(err, t, authorInstance)
7920c273
C
112 })
113 },
114
115 function findOrCreateTags (t, author, callback) {
116 const tags = videoInfos.tags
4ff0d862
C
117
118 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
7920c273 119 return callback(err, t, author, tagInstances)
feb4bdfd
C
120 })
121 },
122
7920c273 123 function createVideoObject (t, author, tagInstances, callback) {
807df9e6
C
124 const videoData = {
125 name: videoInfos.name,
558d7c23
C
126 remoteId: null,
127 extname: path.extname(videoFile.filename),
807df9e6 128 description: videoInfos.description,
67100f1f 129 duration: videoFile.duration,
feb4bdfd 130 authorId: author.id
807df9e6
C
131 }
132
feb4bdfd 133 const video = db.Video.build(videoData)
558d7c23 134
7920c273 135 return callback(null, t, author, tagInstances, video)
558d7c23
C
136 },
137
feb4bdfd 138 // Set the videoname the same as the id
7920c273 139 function renameVideoFile (t, author, tagInstances, video, callback) {
558d7c23
C
140 const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR
141 const source = path.join(videoDir, videoFile.filename)
f285faa0 142 const destination = path.join(videoDir, video.getVideoFilename())
558d7c23
C
143
144 fs.rename(source, destination, function (err) {
7920c273 145 return callback(err, t, author, tagInstances, video)
558d7c23
C
146 })
147 },
148
7920c273
C
149 function insertVideoIntoDB (t, author, tagInstances, video, callback) {
150 const options = { transaction: t }
151
152 // Add tags association
153 video.save(options).asCallback(function (err, videoCreated) {
154 if (err) return callback(err)
155
feb4bdfd
C
156 // Do not forget to add Author informations to the created video
157 videoCreated.Author = author
158
7920c273 159 return callback(err, t, tagInstances, videoCreated)
3a8a8b51 160 })
807df9e6
C
161 },
162
7920c273
C
163 function associateTagsToVideo (t, tagInstances, video, callback) {
164 const options = { transaction: t }
165
166 video.setTags(tagInstances, options).asCallback(function (err) {
167 video.Tags = tagInstances
168
169 return callback(err, t, video)
170 })
171 },
172
173 function sendToFriends (t, video, callback) {
7b1f49de 174 video.toAddRemoteJSON(function (err, remoteVideo) {
aaf61f38 175 if (err) return callback(err)
807df9e6 176
528a9efa
C
177 // Now we'll add the video's meta data to our friends
178 friends.addVideoToFriends(remoteVideo)
807df9e6 179
7920c273 180 return callback(null, t)
528a9efa 181 })
807df9e6
C
182 }
183
7b1f49de
C
184 ], function andFinally (err, t) {
185 if (err) {
186 logger.error('Cannot insert the video.')
187
188 // Abort transaction?
189 if (t) t.rollback()
190
191 return next(err)
192 }
193
194 // Commit transaction
195 t.commit()
196
197 // TODO : include Location of the new video -> 201
198 return res.type('json').status(204).end()
199 })
200}
201
202function updateVideo (req, res, next) {
818f7987 203 const videoInstance = res.locals.video
7b1f49de
C
204 const videoInfosToUpdate = req.body
205
206 waterfall([
207
208 function startTransaction (callback) {
209 db.sequelize.transaction().asCallback(function (err, t) {
210 return callback(err, t)
211 })
212 },
213
214 function findOrCreateTags (t, callback) {
215 if (videoInfosToUpdate.tags) {
216 db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) {
217 return callback(err, t, tagInstances)
218 })
219 } else {
220 return callback(null, t, null)
221 }
222 },
223
224 function updateVideoIntoDB (t, tagInstances, callback) {
225 const options = { transaction: t }
226
227 if (videoInfosToUpdate.name) videoInstance.set('name', videoInfosToUpdate.name)
228 if (videoInfosToUpdate.description) videoInstance.set('description', videoInfosToUpdate.description)
229
230 // Add tags association
231 videoInstance.save(options).asCallback(function (err) {
7b1f49de
C
232 return callback(err, t, tagInstances)
233 })
234 },
235
236 function associateTagsToVideo (t, tagInstances, callback) {
237 if (tagInstances) {
238 const options = { transaction: t }
239
240 videoInstance.setTags(tagInstances, options).asCallback(function (err) {
241 videoInstance.Tags = tagInstances
242
243 return callback(err, t)
244 })
245 } else {
246 return callback(null, t)
247 }
248 },
249
250 function sendToFriends (t, callback) {
251 const json = videoInstance.toUpdateRemoteJSON()
252
253 // Now we'll update the video's meta data to our friends
254 friends.updateVideoToFriends(json)
255
256 return callback(null, t)
257 }
258
7920c273 259 ], function andFinally (err, t) {
807df9e6
C
260 if (err) {
261 logger.error('Cannot insert the video.')
7920c273
C
262
263 // Abort transaction?
264 if (t) t.rollback()
265
807df9e6
C
266 return next(err)
267 }
268
7920c273
C
269 // Commit transaction
270 t.commit()
271
807df9e6
C
272 // TODO : include Location of the new video -> 201
273 return res.type('json').status(204).end()
9f10b292
C
274 })
275}
8c308c2b 276
68ce3ae0 277function getVideo (req, res, next) {
818f7987
C
278 const videoInstance = res.locals.video
279 res.json(videoInstance.toFormatedJSON())
9f10b292 280}
8c308c2b 281
9f10b292 282function listVideos (req, res, next) {
feb4bdfd 283 db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
9f10b292 284 if (err) return next(err)
c45f7f84 285
aaf61f38 286 res.json(getFormatedVideos(videosList, videosTotal))
9f10b292
C
287 })
288}
c45f7f84 289
9f10b292 290function removeVideo (req, res, next) {
818f7987 291 const videoInstance = res.locals.video
8c308c2b 292
818f7987 293 videoInstance.destroy().asCallback(function (err) {
807df9e6
C
294 if (err) {
295 logger.error('Errors when removed the video.', { error: err })
296 return next(err)
297 }
298
299 return res.type('json').status(204).end()
9f10b292
C
300 })
301}
8c308c2b 302
9f10b292 303function searchVideos (req, res, next) {
7920c273
C
304 db.Video.searchAndPopulateAuthorAndPodAndTags(
305 req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
306 function (err, videosList, videosTotal) {
307 if (err) return next(err)
8c308c2b 308
7920c273
C
309 res.json(getFormatedVideos(videosList, videosTotal))
310 }
311 )
9f10b292 312}
c173e565 313
9f10b292 314// ---------------------------------------------------------------------------
c173e565 315
aaf61f38 316function getFormatedVideos (videos, videosTotal) {
bc503c2a 317 const formatedVideos = []
2df82d42 318
aaf61f38
C
319 videos.forEach(function (video) {
320 formatedVideos.push(video.toFormatedJSON())
2df82d42
C
321 })
322
68ce3ae0 323 return {
aaf61f38 324 total: videosTotal,
68ce3ae0
C
325 data: formatedVideos
326 }
2df82d42 327}