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