]>
Commit | Line | Data |
---|---|---|
9f10b292 C |
1 | 'use strict' |
2 | ||
7920c273 | 3 | const each = require('async/each') |
f0f5567b | 4 | const express = require('express') |
558d7c23 | 5 | const fs = require('fs') |
f0f5567b | 6 | const multer = require('multer') |
558d7c23 | 7 | const path = require('path') |
1a42c9e2 | 8 | const waterfall = require('async/waterfall') |
f0f5567b | 9 | |
f253b1c1 | 10 | const constants = require('../../initializers/constants') |
feb4bdfd | 11 | const db = require('../../initializers/database') |
f253b1c1 C |
12 | const logger = require('../../helpers/logger') |
13 | const friends = require('../../lib/friends') | |
14 | const middlewares = require('../../middlewares') | |
69b0a27c | 15 | const oAuth = middlewares.oauth |
fbf1134e | 16 | const pagination = middlewares.pagination |
fc51fde0 C |
17 | const validators = middlewares.validators |
18 | const validatorsPagination = validators.pagination | |
19 | const validatorsSort = validators.sort | |
20 | const validatorsVideos = validators.videos | |
46246b5f | 21 | const search = middlewares.search |
a877d5ac | 22 | const sort = middlewares.sort |
f253b1c1 | 23 | const utils = require('../../helpers/utils') |
f0f5567b C |
24 | |
25 | const router = express.Router() | |
9f10b292 C |
26 | |
27 | // multer configuration | |
f0f5567b | 28 | const 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 | 45 | const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }]) |
8c308c2b | 46 | |
fbf1134e | 47 | router.get('/', |
fc51fde0 C |
48 | validatorsPagination.pagination, |
49 | validatorsSort.videosSort, | |
a877d5ac | 50 | sort.setVideosSort, |
fbf1134e C |
51 | pagination.setPagination, |
52 | listVideos | |
53 | ) | |
54 | router.post('/', | |
69b0a27c | 55 | oAuth.authenticate, |
fbf1134e | 56 | reqFiles, |
fc51fde0 | 57 | validatorsVideos.videosAdd, |
fbf1134e C |
58 | addVideo |
59 | ) | |
60 | router.get('/:id', | |
fc51fde0 | 61 | validatorsVideos.videosGet, |
68ce3ae0 | 62 | getVideo |
fbf1134e C |
63 | ) |
64 | router.delete('/:id', | |
69b0a27c | 65 | oAuth.authenticate, |
fc51fde0 | 66 | validatorsVideos.videosRemove, |
fbf1134e C |
67 | removeVideo |
68 | ) | |
46246b5f | 69 | router.get('/search/:value', |
fc51fde0 C |
70 | validatorsVideos.videosSearch, |
71 | validatorsPagination.pagination, | |
72 | validatorsSort.videosSort, | |
a877d5ac | 73 | sort.setVideosSort, |
fbf1134e | 74 | pagination.setPagination, |
46246b5f | 75 | search.setVideosSearch, |
fbf1134e C |
76 | searchVideos |
77 | ) | |
8c308c2b | 78 | |
9f10b292 | 79 | // --------------------------------------------------------------------------- |
c45f7f84 | 80 | |
9f10b292 | 81 | module.exports = router |
c45f7f84 | 82 | |
9f10b292 | 83 | // --------------------------------------------------------------------------- |
c45f7f84 | 84 | |
9f10b292 | 85 | function addVideo (req, res, next) { |
bc503c2a C |
86 | const videoFile = req.files.videofile[0] |
87 | const videoInfos = req.body | |
9f10b292 | 88 | |
1a42c9e2 | 89 | waterfall([ |
807df9e6 | 90 | |
7920c273 C |
91 | function startTransaction (callback) { |
92 | db.sequelize.transaction().asCallback(function (err, t) { | |
93 | return callback(err, t) | |
94 | }) | |
95 | }, | |
96 | ||
97 | function findOrCreateAuthor (t, callback) { | |
4712081f | 98 | const user = res.locals.oauth.token.User |
feb4bdfd C |
99 | |
100 | const query = { | |
101 | where: { | |
4712081f C |
102 | name: user.username, |
103 | podId: null, | |
104 | userId: user.id | |
feb4bdfd C |
105 | }, |
106 | defaults: { | |
4712081f C |
107 | name: user.username, |
108 | podId: null, // null because it is OUR pod | |
109 | userId: user.id | |
7920c273 C |
110 | }, |
111 | transaction: t | |
feb4bdfd C |
112 | } |
113 | ||
114 | db.Author.findOrCreate(query).asCallback(function (err, result) { | |
4712081f C |
115 | const authorInstance = result[0] |
116 | ||
117 | return callback(err, t, authorInstance) | |
7920c273 C |
118 | }) |
119 | }, | |
120 | ||
121 | function findOrCreateTags (t, author, callback) { | |
122 | const tags = videoInfos.tags | |
123 | const tagInstances = [] | |
124 | ||
125 | each(tags, function (tag, callbackEach) { | |
126 | const query = { | |
127 | where: { | |
128 | name: tag | |
129 | }, | |
130 | defaults: { | |
131 | name: tag | |
132 | }, | |
133 | transaction: t | |
134 | } | |
135 | ||
136 | db.Tag.findOrCreate(query).asCallback(function (err, res) { | |
137 | if (err) return callbackEach(err) | |
138 | ||
139 | // res = [ tag, isCreated ] | |
140 | const tag = res[0] | |
141 | tagInstances.push(tag) | |
142 | return callbackEach() | |
143 | }) | |
144 | }, function (err) { | |
145 | return callback(err, t, author, tagInstances) | |
feb4bdfd C |
146 | }) |
147 | }, | |
148 | ||
7920c273 | 149 | function createVideoObject (t, author, tagInstances, callback) { |
807df9e6 C |
150 | const videoData = { |
151 | name: videoInfos.name, | |
558d7c23 C |
152 | remoteId: null, |
153 | extname: path.extname(videoFile.filename), | |
807df9e6 | 154 | description: videoInfos.description, |
67100f1f | 155 | duration: videoFile.duration, |
feb4bdfd | 156 | authorId: author.id |
807df9e6 C |
157 | } |
158 | ||
feb4bdfd | 159 | const video = db.Video.build(videoData) |
558d7c23 | 160 | |
7920c273 | 161 | return callback(null, t, author, tagInstances, video) |
558d7c23 C |
162 | }, |
163 | ||
feb4bdfd | 164 | // Set the videoname the same as the id |
7920c273 | 165 | function renameVideoFile (t, author, tagInstances, video, callback) { |
558d7c23 C |
166 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR |
167 | const source = path.join(videoDir, videoFile.filename) | |
f285faa0 | 168 | const destination = path.join(videoDir, video.getVideoFilename()) |
558d7c23 C |
169 | |
170 | fs.rename(source, destination, function (err) { | |
7920c273 | 171 | return callback(err, t, author, tagInstances, video) |
558d7c23 C |
172 | }) |
173 | }, | |
174 | ||
7920c273 C |
175 | function insertVideoIntoDB (t, author, tagInstances, video, callback) { |
176 | const options = { transaction: t } | |
177 | ||
178 | // Add tags association | |
179 | video.save(options).asCallback(function (err, videoCreated) { | |
180 | if (err) return callback(err) | |
181 | ||
feb4bdfd C |
182 | // Do not forget to add Author informations to the created video |
183 | videoCreated.Author = author | |
184 | ||
7920c273 | 185 | return callback(err, t, tagInstances, videoCreated) |
3a8a8b51 | 186 | }) |
807df9e6 C |
187 | }, |
188 | ||
7920c273 C |
189 | function associateTagsToVideo (t, tagInstances, video, callback) { |
190 | const options = { transaction: t } | |
191 | ||
192 | video.setTags(tagInstances, options).asCallback(function (err) { | |
193 | video.Tags = tagInstances | |
194 | ||
195 | return callback(err, t, video) | |
196 | }) | |
197 | }, | |
198 | ||
199 | function sendToFriends (t, video, callback) { | |
aaf61f38 C |
200 | video.toRemoteJSON(function (err, remoteVideo) { |
201 | if (err) return callback(err) | |
807df9e6 | 202 | |
528a9efa C |
203 | // Now we'll add the video's meta data to our friends |
204 | friends.addVideoToFriends(remoteVideo) | |
807df9e6 | 205 | |
7920c273 | 206 | return callback(null, t) |
528a9efa | 207 | }) |
807df9e6 C |
208 | } |
209 | ||
7920c273 | 210 | ], function andFinally (err, t) { |
807df9e6 C |
211 | if (err) { |
212 | logger.error('Cannot insert the video.') | |
7920c273 C |
213 | |
214 | // Abort transaction? | |
215 | if (t) t.rollback() | |
216 | ||
807df9e6 C |
217 | return next(err) |
218 | } | |
219 | ||
7920c273 C |
220 | // Commit transaction |
221 | t.commit() | |
222 | ||
807df9e6 C |
223 | // TODO : include Location of the new video -> 201 |
224 | return res.type('json').status(204).end() | |
9f10b292 C |
225 | }) |
226 | } | |
8c308c2b | 227 | |
68ce3ae0 | 228 | function getVideo (req, res, next) { |
7920c273 | 229 | db.Video.loadAndPopulateAuthorAndPodAndTags(req.params.id, function (err, video) { |
9f10b292 | 230 | if (err) return next(err) |
8c308c2b | 231 | |
aaf61f38 | 232 | if (!video) { |
2df82d42 | 233 | return res.type('json').status(204).end() |
9f10b292 | 234 | } |
8c308c2b | 235 | |
aaf61f38 | 236 | res.json(video.toFormatedJSON()) |
9f10b292 C |
237 | }) |
238 | } | |
8c308c2b | 239 | |
9f10b292 | 240 | function listVideos (req, res, next) { |
feb4bdfd | 241 | db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) { |
9f10b292 | 242 | if (err) return next(err) |
c45f7f84 | 243 | |
aaf61f38 | 244 | res.json(getFormatedVideos(videosList, videosTotal)) |
9f10b292 C |
245 | }) |
246 | } | |
c45f7f84 | 247 | |
9f10b292 | 248 | function removeVideo (req, res, next) { |
bc503c2a | 249 | const videoId = req.params.id |
8c308c2b | 250 | |
1a42c9e2 | 251 | waterfall([ |
98ac898a C |
252 | function loadVideo (callback) { |
253 | db.Video.load(videoId, function (err, video) { | |
254 | return callback(err, video) | |
807df9e6 C |
255 | }) |
256 | }, | |
cbe2f7c3 | 257 | |
98ac898a C |
258 | function deleteVideo (video, callback) { |
259 | // Informations to other pods will be sent by the afterDestroy video hook | |
260 | video.destroy().asCallback(callback) | |
807df9e6 | 261 | } |
be587647 | 262 | ], function andFinally (err) { |
807df9e6 C |
263 | if (err) { |
264 | logger.error('Errors when removed the video.', { error: err }) | |
265 | return next(err) | |
266 | } | |
267 | ||
268 | return res.type('json').status(204).end() | |
9f10b292 C |
269 | }) |
270 | } | |
8c308c2b | 271 | |
9f10b292 | 272 | function searchVideos (req, res, next) { |
7920c273 C |
273 | db.Video.searchAndPopulateAuthorAndPodAndTags( |
274 | req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort, | |
275 | function (err, videosList, videosTotal) { | |
276 | if (err) return next(err) | |
8c308c2b | 277 | |
7920c273 C |
278 | res.json(getFormatedVideos(videosList, videosTotal)) |
279 | } | |
280 | ) | |
9f10b292 | 281 | } |
c173e565 | 282 | |
9f10b292 | 283 | // --------------------------------------------------------------------------- |
c173e565 | 284 | |
aaf61f38 | 285 | function getFormatedVideos (videos, videosTotal) { |
bc503c2a | 286 | const formatedVideos = [] |
2df82d42 | 287 | |
aaf61f38 C |
288 | videos.forEach(function (video) { |
289 | formatedVideos.push(video.toFormatedJSON()) | |
2df82d42 C |
290 | }) |
291 | ||
68ce3ae0 | 292 | return { |
aaf61f38 | 293 | total: videosTotal, |
68ce3ae0 C |
294 | data: formatedVideos |
295 | } | |
2df82d42 | 296 | } |