]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/v1/videos.js
2edb3112273d2e63eb65fb98ef026b56a96b1157
[github/Chocobozzz/PeerTube.git] / server / controllers / api / v1 / videos.js
1 'use strict'
2
3 const async = require('async')
4 const config = require('config')
5 const express = require('express')
6 const multer = require('multer')
7
8 const constants = require('../../../initializers/constants')
9 const logger = require('../../../helpers/logger')
10 const friends = require('../../../lib/friends')
11 const middlewares = require('../../../middlewares')
12 const oAuth2 = middlewares.oauth2
13 const pagination = middlewares.pagination
14 const reqValidator = middlewares.reqValidators
15 const reqValidatorPagination = reqValidator.pagination
16 const reqValidatorSort = reqValidator.sort
17 const reqValidatorVideos = reqValidator.videos
18 const search = middlewares.search
19 const sort = middlewares.sort
20 const utils = require('../../../helpers/utils')
21 const Videos = require('../../../models/videos') // model
22 const videos = require('../../../lib/videos')
23 const webtorrent = require('../../../lib/webtorrent')
24
25 const router = express.Router()
26 const uploads = config.get('storage.uploads')
27
28 // multer configuration
29 const storage = multer.diskStorage({
30 destination: function (req, file, cb) {
31 cb(null, uploads)
32 },
33
34 filename: function (req, file, cb) {
35 let extension = ''
36 if (file.mimetype === 'video/webm') extension = 'webm'
37 else if (file.mimetype === 'video/mp4') extension = 'mp4'
38 else if (file.mimetype === 'video/ogg') extension = 'ogv'
39 utils.generateRandomString(16, function (err, randomString) {
40 const fieldname = err ? undefined : randomString
41 cb(null, fieldname + '.' + extension)
42 })
43 }
44 })
45
46 const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
47
48 router.get('/',
49 reqValidatorPagination.pagination,
50 reqValidatorSort.videosSort,
51 sort.setVideosSort,
52 pagination.setPagination,
53 listVideos
54 )
55 router.post('/',
56 oAuth2.authenticate,
57 reqFiles,
58 reqValidatorVideos.videosAdd,
59 addVideo
60 )
61 router.get('/:id',
62 reqValidatorVideos.videosGet,
63 getVideo
64 )
65 router.delete('/:id',
66 oAuth2.authenticate,
67 reqValidatorVideos.videosRemove,
68 removeVideo
69 )
70 router.get('/search/:value',
71 reqValidatorVideos.videosSearch,
72 reqValidatorPagination.pagination,
73 reqValidatorSort.videosSort,
74 sort.setVideosSort,
75 pagination.setPagination,
76 search.setVideosSearch,
77 searchVideos
78 )
79
80 // ---------------------------------------------------------------------------
81
82 module.exports = router
83
84 // ---------------------------------------------------------------------------
85
86 function addVideo (req, res, next) {
87 const videoFile = req.files.videofile[0]
88 const videoInfos = req.body
89
90 async.waterfall([
91 function seedTheVideo (callback) {
92 videos.seed(videoFile.path, callback)
93 },
94
95 function createThumbnail (torrent, callback) {
96 videos.createVideoThumbnail(videoFile.path, function (err, thumbnailName) {
97 if (err) {
98 // TODO: unseed the video
99 logger.error('Cannot make a thumbnail of the video file.')
100 return callback(err)
101 }
102
103 callback(null, torrent, thumbnailName)
104 })
105 },
106
107 function insertIntoDB (torrent, thumbnailName, callback) {
108 const videoData = {
109 name: videoInfos.name,
110 namePath: videoFile.filename,
111 description: videoInfos.description,
112 magnetUri: torrent.magnetURI,
113 author: res.locals.oauth.token.user.username,
114 duration: videoFile.duration,
115 thumbnail: thumbnailName,
116 tags: videoInfos.tags
117 }
118
119 Videos.add(videoData, function (err, insertedVideo) {
120 if (err) {
121 // TODO unseed the video
122 // TODO remove thumbnail
123 logger.error('Cannot insert this video in the database.')
124 return callback(err)
125 }
126
127 return callback(null, insertedVideo)
128 })
129 },
130
131 function sendToFriends (insertedVideo, callback) {
132 videos.convertVideoToRemote(insertedVideo, function (err, remoteVideo) {
133 if (err) {
134 // TODO unseed the video
135 // TODO remove thumbnail
136 // TODO delete from DB
137 logger.error('Cannot convert video to remote.')
138 return callback(err)
139 }
140
141 // Now we'll add the video's meta data to our friends
142 friends.addVideoToFriends(remoteVideo)
143
144 return callback(null)
145 })
146 }
147
148 ], function andFinally (err) {
149 if (err) {
150 logger.error('Cannot insert the video.')
151 return next(err)
152 }
153
154 // TODO : include Location of the new video -> 201
155 return res.type('json').status(204).end()
156 })
157 }
158
159 function getVideo (req, res, next) {
160 Videos.get(req.params.id, function (err, videoObj) {
161 if (err) return next(err)
162
163 const state = videos.getVideoState(videoObj)
164 if (state.exist === false) {
165 return res.type('json').status(204).end()
166 }
167
168 res.json(getFormatedVideo(videoObj))
169 })
170 }
171
172 function listVideos (req, res, next) {
173 Videos.list(req.query.start, req.query.count, req.query.sort, function (err, videosList, totalVideos) {
174 if (err) return next(err)
175
176 res.json(getFormatedVideos(videosList, totalVideos))
177 })
178 }
179
180 function removeVideo (req, res, next) {
181 const videoId = req.params.id
182
183 async.waterfall([
184 function getVideo (callback) {
185 Videos.get(videoId, callback)
186 },
187
188 function removeVideoTorrent (video, callback) {
189 removeTorrent(video.magnetUri, function () {
190 return callback(null, video)
191 })
192 },
193
194 function removeFromDB (video, callback) {
195 Videos.removeOwned(req.params.id, function (err) {
196 if (err) return callback(err)
197
198 return callback(null, video)
199 })
200 },
201
202 function removeVideoData (video, callback) {
203 videos.removeVideosDataFromDisk([ video ], function (err) {
204 if (err) logger.error('Cannot remove video data from disk.', { video: video })
205
206 return callback(null, video)
207 })
208 },
209
210 function sendInformationToFriends (video, callback) {
211 const params = {
212 name: video.name,
213 magnetUri: video.magnetUri
214 }
215
216 friends.removeVideoToFriends(params)
217
218 return callback(null)
219 }
220 ], function andFinally (err) {
221 if (err) {
222 logger.error('Errors when removed the video.', { error: err })
223 return next(err)
224 }
225
226 return res.type('json').status(204).end()
227 })
228 }
229
230 function searchVideos (req, res, next) {
231 Videos.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
232 function (err, videosList, totalVideos) {
233 if (err) return next(err)
234
235 res.json(getFormatedVideos(videosList, totalVideos))
236 })
237 }
238
239 // ---------------------------------------------------------------------------
240
241 function getFormatedVideo (videoObj) {
242 const formatedVideo = {
243 id: videoObj._id,
244 name: videoObj.name,
245 description: videoObj.description,
246 podUrl: videoObj.podUrl.replace(/^https?:\/\//, ''),
247 isLocal: videos.getVideoState(videoObj).owned,
248 magnetUri: videoObj.magnetUri,
249 author: videoObj.author,
250 duration: videoObj.duration,
251 tags: videoObj.tags,
252 thumbnailPath: constants.THUMBNAILS_STATIC_PATH + '/' + videoObj.thumbnail,
253 createdDate: videoObj.createdDate
254 }
255
256 return formatedVideo
257 }
258
259 function getFormatedVideos (videosObj, totalVideos) {
260 const formatedVideos = []
261
262 videosObj.forEach(function (videoObj) {
263 formatedVideos.push(getFormatedVideo(videoObj))
264 })
265
266 return {
267 total: totalVideos,
268 data: formatedVideos
269 }
270 }
271
272 // Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
273 function removeTorrent (magnetUri, callback) {
274 try {
275 webtorrent.remove(magnetUri, callback)
276 } catch (err) {
277 logger.warn('Cannot remove the torrent from WebTorrent', { err: err })
278 return callback(null)
279 }
280 }