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