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