]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/v1/videos.js
Fix readme roadmap
[github/Chocobozzz/PeerTube.git] / server / controllers / api / v1 / videos.js
CommitLineData
9f10b292
C
1'use strict'
2
807df9e6 3const async = require('async')
f0f5567b 4const config = require('config')
f0f5567b
C
5const express = require('express')
6const multer = require('multer')
7
cbe2f7c3 8const constants = require('../../../initializers/constants')
f0f5567b
C
9const logger = require('../../../helpers/logger')
10const friends = require('../../../lib/friends')
b3b92647
C
11const middlewares = require('../../../middlewares')
12const oAuth2 = middlewares.oauth2
fbf1134e
C
13const pagination = middlewares.pagination
14const reqValidator = middlewares.reqValidators
15const reqValidatorPagination = reqValidator.pagination
a877d5ac 16const reqValidatorSort = reqValidator.sort
fbf1134e 17const reqValidatorVideos = reqValidator.videos
46246b5f 18const search = middlewares.search
a877d5ac 19const sort = middlewares.sort
cbe2f7c3 20const utils = require('../../../helpers/utils')
f0f5567b
C
21const Videos = require('../../../models/videos') // model
22const videos = require('../../../lib/videos')
23const webtorrent = require('../../../lib/webtorrent')
24
25const router = express.Router()
26const uploads = config.get('storage.uploads')
9f10b292
C
27
28// multer configuration
f0f5567b 29const storage = multer.diskStorage({
9f10b292
C
30 destination: function (req, file, cb) {
31 cb(null, uploads)
32 },
33
34 filename: function (req, file, cb) {
f0f5567b 35 let extension = ''
9f10b292
C
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'
bc503c2a
C
39 utils.generateRandomString(16, function (err, randomString) {
40 const fieldname = err ? undefined : randomString
9f10b292
C
41 cb(null, fieldname + '.' + extension)
42 })
43 }
44})
45
8c9c1942 46const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
8c308c2b 47
fbf1134e
C
48router.get('/',
49 reqValidatorPagination.pagination,
a877d5ac
C
50 reqValidatorSort.videosSort,
51 sort.setVideosSort,
fbf1134e
C
52 pagination.setPagination,
53 listVideos
54)
55router.post('/',
56 oAuth2.authenticate,
57 reqFiles,
58 reqValidatorVideos.videosAdd,
59 addVideo
60)
61router.get('/:id',
62 reqValidatorVideos.videosGet,
68ce3ae0 63 getVideo
fbf1134e
C
64)
65router.delete('/:id',
66 oAuth2.authenticate,
67 reqValidatorVideos.videosRemove,
68 removeVideo
69)
46246b5f 70router.get('/search/:value',
fbf1134e
C
71 reqValidatorVideos.videosSearch,
72 reqValidatorPagination.pagination,
a877d5ac
C
73 reqValidatorSort.videosSort,
74 sort.setVideosSort,
fbf1134e 75 pagination.setPagination,
46246b5f 76 search.setVideosSearch,
fbf1134e
C
77 searchVideos
78)
8c308c2b 79
9f10b292 80// ---------------------------------------------------------------------------
c45f7f84 81
9f10b292 82module.exports = router
c45f7f84 83
9f10b292 84// ---------------------------------------------------------------------------
c45f7f84 85
9f10b292 86function addVideo (req, res, next) {
bc503c2a
C
87 const videoFile = req.files.videofile[0]
88 const videoInfos = req.body
9f10b292 89
807df9e6 90 async.waterfall([
67100f1f 91 function seedTheVideo (callback) {
807df9e6
C
92 videos.seed(videoFile.path, callback)
93 },
8c308c2b 94
67100f1f 95 function createThumbnail (torrent, callback) {
57a56079 96 videos.createVideoThumbnail(videoFile.path, function (err, thumbnailName) {
3a8a8b51 97 if (err) {
cbe2f7c3
C
98 // TODO: unseed the video
99 logger.error('Cannot make a thumbnail of the video file.')
807df9e6 100 return callback(err)
3a8a8b51
C
101 }
102
67100f1f 103 callback(null, torrent, thumbnailName)
807df9e6
C
104 })
105 },
106
67100f1f 107 function insertIntoDB (torrent, thumbnailName, callback) {
807df9e6
C
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,
67100f1f 114 duration: videoFile.duration,
be587647
C
115 thumbnail: thumbnailName,
116 tags: videoInfos.tags
807df9e6
C
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)
cbe2f7c3 125 }
3a8a8b51 126
528a9efa 127 return callback(null, insertedVideo)
3a8a8b51 128 })
807df9e6
C
129 },
130
528a9efa
C
131 function sendToFriends (insertedVideo, callback) {
132 videos.convertVideoToRemote(insertedVideo, function (err, remoteVideo) {
807df9e6
C
133 if (err) {
134 // TODO unseed the video
135 // TODO remove thumbnail
528a9efa
C
136 // TODO delete from DB
137 logger.error('Cannot convert video to remote.')
807df9e6
C
138 return callback(err)
139 }
140
528a9efa
C
141 // Now we'll add the video's meta data to our friends
142 friends.addVideoToFriends(remoteVideo)
807df9e6 143
528a9efa
C
144 return callback(null)
145 })
807df9e6
C
146 }
147
be587647 148 ], function andFinally (err) {
807df9e6
C
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()
9f10b292
C
156 })
157}
8c308c2b 158
68ce3ae0 159function getVideo (req, res, next) {
bc503c2a 160 Videos.get(req.params.id, function (err, videoObj) {
9f10b292 161 if (err) return next(err)
8c308c2b 162
bc503c2a 163 const state = videos.getVideoState(videoObj)
2df82d42
C
164 if (state.exist === false) {
165 return res.type('json').status(204).end()
9f10b292 166 }
8c308c2b 167
bc503c2a 168 res.json(getFormatedVideo(videoObj))
9f10b292
C
169 })
170}
8c308c2b 171
9f10b292 172function listVideos (req, res, next) {
68ce3ae0 173 Videos.list(req.query.start, req.query.count, req.query.sort, function (err, videosList, totalVideos) {
9f10b292 174 if (err) return next(err)
c45f7f84 175
68ce3ae0 176 res.json(getFormatedVideos(videosList, totalVideos))
9f10b292
C
177 })
178}
c45f7f84 179
9f10b292 180function removeVideo (req, res, next) {
bc503c2a 181 const videoId = req.params.id
8c308c2b 182
807df9e6
C
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) {
9f10b292 195 Videos.removeOwned(req.params.id, function (err) {
807df9e6 196 if (err) return callback(err)
c173e565 197
807df9e6
C
198 return callback(null, video)
199 })
200 },
cbe2f7c3 201
807df9e6
C
202 function removeVideoData (video, callback) {
203 videos.removeVideosDataFromDisk([ video ], function (err) {
204 if (err) logger.error('Cannot remove video data from disk.', { video: video })
c173e565 205
807df9e6 206 return callback(null, video)
c173e565 207 })
807df9e6
C
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 }
be587647 220 ], function andFinally (err) {
807df9e6
C
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()
9f10b292
C
227 })
228}
8c308c2b 229
9f10b292 230function searchVideos (req, res, next) {
46246b5f
C
231 Videos.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
232 function (err, videosList, totalVideos) {
9f10b292 233 if (err) return next(err)
8c308c2b 234
68ce3ae0 235 res.json(getFormatedVideos(videosList, totalVideos))
9f10b292
C
236 })
237}
c173e565 238
9f10b292 239// ---------------------------------------------------------------------------
c173e565 240
bc503c2a
C
241function getFormatedVideo (videoObj) {
242 const formatedVideo = {
243 id: videoObj._id,
244 name: videoObj.name,
245 description: videoObj.description,
9e379c83 246 podUrl: videoObj.podUrl.replace(/^https?:\/\//, ''),
bc503c2a
C
247 isLocal: videos.getVideoState(videoObj).owned,
248 magnetUri: videoObj.magnetUri,
249 author: videoObj.author,
250 duration: videoObj.duration,
be587647 251 tags: videoObj.tags,
bb10240e
C
252 thumbnailPath: constants.THUMBNAILS_STATIC_PATH + '/' + videoObj.thumbnail,
253 createdDate: videoObj.createdDate
2df82d42
C
254 }
255
bc503c2a 256 return formatedVideo
2df82d42
C
257}
258
68ce3ae0 259function getFormatedVideos (videosObj, totalVideos) {
bc503c2a 260 const formatedVideos = []
2df82d42 261
bc503c2a
C
262 videosObj.forEach(function (videoObj) {
263 formatedVideos.push(getFormatedVideo(videoObj))
2df82d42
C
264 })
265
68ce3ae0
C
266 return {
267 total: totalVideos,
268 data: formatedVideos
269 }
2df82d42
C
270}
271
9f10b292
C
272// Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
273function 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)
c173e565 279 }
9f10b292 280}