]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/videos.js
Client: upgrade angular dep'
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos.js
CommitLineData
9f10b292
C
1'use strict'
2
7920c273 3const each = require('async/each')
f0f5567b 4const express = require('express')
558d7c23 5const fs = require('fs')
f0f5567b 6const multer = require('multer')
558d7c23 7const path = require('path')
1a42c9e2 8const waterfall = require('async/waterfall')
f0f5567b 9
f253b1c1 10const constants = require('../../initializers/constants')
feb4bdfd 11const db = require('../../initializers/database')
f253b1c1
C
12const logger = require('../../helpers/logger')
13const friends = require('../../lib/friends')
14const middlewares = require('../../middlewares')
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
fbf1134e 47router.get('/',
fc51fde0
C
48 validatorsPagination.pagination,
49 validatorsSort.videosSort,
a877d5ac 50 sort.setVideosSort,
fbf1134e
C
51 pagination.setPagination,
52 listVideos
53)
54router.post('/',
69b0a27c 55 oAuth.authenticate,
fbf1134e 56 reqFiles,
fc51fde0 57 validatorsVideos.videosAdd,
fbf1134e
C
58 addVideo
59)
60router.get('/:id',
fc51fde0 61 validatorsVideos.videosGet,
68ce3ae0 62 getVideo
fbf1134e
C
63)
64router.delete('/:id',
69b0a27c 65 oAuth.authenticate,
fc51fde0 66 validatorsVideos.videosRemove,
fbf1134e
C
67 removeVideo
68)
46246b5f 69router.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 81module.exports = router
c45f7f84 82
9f10b292 83// ---------------------------------------------------------------------------
c45f7f84 84
9f10b292 85function 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 228function 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 240function 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 248function 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 272function 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 285function 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}