]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/videos.js
Move tags in another table
[github/Chocobozzz/PeerTube.git] / server / controllers / api / videos.js
1 'use strict'
2
3 const each = require('async/each')
4 const express = require('express')
5 const fs = require('fs')
6 const multer = require('multer')
7 const path = require('path')
8 const waterfall = require('async/waterfall')
9
10 const constants = require('../../initializers/constants')
11 const db = require('../../initializers/database')
12 const logger = require('../../helpers/logger')
13 const friends = require('../../lib/friends')
14 const middlewares = require('../../middlewares')
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('/',
48 validatorsPagination.pagination,
49 validatorsSort.videosSort,
50 sort.setVideosSort,
51 pagination.setPagination,
52 listVideos
53 )
54 router.post('/',
55 oAuth.authenticate,
56 reqFiles,
57 validatorsVideos.videosAdd,
58 addVideo
59 )
60 router.get('/:id',
61 validatorsVideos.videosGet,
62 getVideo
63 )
64 router.delete('/:id',
65 oAuth.authenticate,
66 validatorsVideos.videosRemove,
67 removeVideo
68 )
69 router.get('/search/:value',
70 validatorsVideos.videosSearch,
71 validatorsPagination.pagination,
72 validatorsSort.videosSort,
73 sort.setVideosSort,
74 pagination.setPagination,
75 search.setVideosSearch,
76 searchVideos
77 )
78
79 // ---------------------------------------------------------------------------
80
81 module.exports = router
82
83 // ---------------------------------------------------------------------------
84
85 function addVideo (req, res, next) {
86 const videoFile = req.files.videofile[0]
87 const videoInfos = req.body
88
89 waterfall([
90
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) {
98 const username = res.locals.oauth.token.user.username
99
100 const query = {
101 where: {
102 name: username,
103 podId: null
104 },
105 defaults: {
106 name: username,
107 podId: null // null because it is OUR pod
108 },
109 transaction: t
110 }
111
112 db.Author.findOrCreate(query).asCallback(function (err, result) {
113 // [ instance, wasCreated ]
114 return callback(err, t, result[0])
115 })
116 },
117
118 function findOrCreateTags (t, author, callback) {
119 const tags = videoInfos.tags
120 const tagInstances = []
121
122 each(tags, function (tag, callbackEach) {
123 const query = {
124 where: {
125 name: tag
126 },
127 defaults: {
128 name: tag
129 },
130 transaction: t
131 }
132
133 db.Tag.findOrCreate(query).asCallback(function (err, res) {
134 if (err) return callbackEach(err)
135
136 // res = [ tag, isCreated ]
137 const tag = res[0]
138 tagInstances.push(tag)
139 return callbackEach()
140 })
141 }, function (err) {
142 return callback(err, t, author, tagInstances)
143 })
144 },
145
146 function createVideoObject (t, author, tagInstances, callback) {
147 const videoData = {
148 name: videoInfos.name,
149 remoteId: null,
150 extname: path.extname(videoFile.filename),
151 description: videoInfos.description,
152 duration: videoFile.duration,
153 authorId: author.id
154 }
155
156 const video = db.Video.build(videoData)
157
158 return callback(null, t, author, tagInstances, video)
159 },
160
161 // Set the videoname the same as the id
162 function renameVideoFile (t, author, tagInstances, video, callback) {
163 const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR
164 const source = path.join(videoDir, videoFile.filename)
165 const destination = path.join(videoDir, video.getVideoFilename())
166
167 fs.rename(source, destination, function (err) {
168 return callback(err, t, author, tagInstances, video)
169 })
170 },
171
172 function insertVideoIntoDB (t, author, tagInstances, video, callback) {
173 const options = { transaction: t }
174
175 // Add tags association
176 video.save(options).asCallback(function (err, videoCreated) {
177 if (err) return callback(err)
178
179 // Do not forget to add Author informations to the created video
180 videoCreated.Author = author
181
182 return callback(err, t, tagInstances, videoCreated)
183 })
184 },
185
186 function associateTagsToVideo (t, tagInstances, video, callback) {
187 const options = { transaction: t }
188
189 video.setTags(tagInstances, options).asCallback(function (err) {
190 video.Tags = tagInstances
191
192 return callback(err, t, video)
193 })
194 },
195
196 function sendToFriends (t, video, callback) {
197 video.toRemoteJSON(function (err, remoteVideo) {
198 if (err) return callback(err)
199
200 // Now we'll add the video's meta data to our friends
201 friends.addVideoToFriends(remoteVideo)
202
203 return callback(null, t)
204 })
205 }
206
207 ], function andFinally (err, t) {
208 if (err) {
209 logger.error('Cannot insert the video.')
210
211 // Abort transaction?
212 if (t) t.rollback()
213
214 return next(err)
215 }
216
217 // Commit transaction
218 t.commit()
219
220 // TODO : include Location of the new video -> 201
221 return res.type('json').status(204).end()
222 })
223 }
224
225 function getVideo (req, res, next) {
226 db.Video.loadAndPopulateAuthorAndPodAndTags(req.params.id, function (err, video) {
227 if (err) return next(err)
228
229 if (!video) {
230 return res.type('json').status(204).end()
231 }
232
233 res.json(video.toFormatedJSON())
234 })
235 }
236
237 function listVideos (req, res, next) {
238 db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
239 if (err) return next(err)
240
241 res.json(getFormatedVideos(videosList, videosTotal))
242 })
243 }
244
245 function removeVideo (req, res, next) {
246 const videoId = req.params.id
247
248 waterfall([
249 function getVideo (callback) {
250 db.Video.load(videoId, callback)
251 },
252
253 function removeFromDB (video, callback) {
254 video.destroy().asCallback(function (err) {
255 if (err) return callback(err)
256
257 return callback(null, video)
258 })
259 },
260
261 function sendInformationToFriends (video, callback) {
262 const params = {
263 name: video.name,
264 remoteId: video.id
265 }
266
267 friends.removeVideoToFriends(params)
268
269 return callback(null)
270 }
271 ], function andFinally (err) {
272 if (err) {
273 logger.error('Errors when removed the video.', { error: err })
274 return next(err)
275 }
276
277 return res.type('json').status(204).end()
278 })
279 }
280
281 function searchVideos (req, res, next) {
282 db.Video.searchAndPopulateAuthorAndPodAndTags(
283 req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
284 function (err, videosList, videosTotal) {
285 if (err) return next(err)
286
287 res.json(getFormatedVideos(videosList, videosTotal))
288 }
289 )
290 }
291
292 // ---------------------------------------------------------------------------
293
294 function getFormatedVideos (videos, videosTotal) {
295 const formatedVideos = []
296
297 videos.forEach(function (video) {
298 formatedVideos.push(video.toFormatedJSON())
299 })
300
301 return {
302 total: videosTotal,
303 data: formatedVideos
304 }
305 }