]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/controllers/api/v1/videos.js
Refractoring and add thumbnails support (without tests)
[github/Chocobozzz/PeerTube.git] / server / controllers / api / v1 / videos.js
1 'use strict'
2
3 const config = require('config')
4 const express = require('express')
5 const fs = require('fs')
6 const path = require('path')
7 const multer = require('multer')
8
9 const constants = require('../../../initializers/constants')
10 const logger = require('../../../helpers/logger')
11 const friends = require('../../../lib/friends')
12 const middleware = require('../../../middlewares')
13 const oAuth2 = require('../../../middlewares/oauth2')
14 const cacheMiddleware = middleware.cache
15 const reqValidator = middleware.reqValidators.videos
16 const utils = require('../../../helpers/utils')
17 const Videos = require('../../../models/videos') // model
18 const videos = require('../../../lib/videos')
19 const webtorrent = require('../../../lib/webtorrent')
20
21 const router = express.Router()
22 const uploads = config.get('storage.uploads')
23
24 // multer configuration
25 const storage = multer.diskStorage({
26 destination: function (req, file, cb) {
27 cb(null, uploads)
28 },
29
30 filename: function (req, file, cb) {
31 let extension = ''
32 if (file.mimetype === 'video/webm') extension = 'webm'
33 else if (file.mimetype === 'video/mp4') extension = 'mp4'
34 else if (file.mimetype === 'video/ogg') extension = 'ogv'
35 utils.generateRandomString(16, function (err, random_string) {
36 const fieldname = err ? undefined : random_string
37 cb(null, fieldname + '.' + extension)
38 })
39 }
40 })
41
42 const reqFiles = multer({ storage: storage }).fields([{ name: 'videofile', maxCount: 1 }])
43 const thumbnailsDir = path.join(__dirname, '..', '..', '..', '..', config.get('storage.thumbnails'))
44
45 router.get('/', cacheMiddleware.cache(false), listVideos)
46 router.post('/', oAuth2.authenticate, reqFiles, reqValidator.videosAdd, cacheMiddleware.cache(false), addVideo)
47 router.get('/:id', reqValidator.videosGet, cacheMiddleware.cache(false), getVideos)
48 router.delete('/:id', oAuth2.authenticate, reqValidator.videosRemove, cacheMiddleware.cache(false), removeVideo)
49 router.get('/search/:name', reqValidator.videosSearch, cacheMiddleware.cache(false), searchVideos)
50
51 // ---------------------------------------------------------------------------
52
53 module.exports = router
54
55 // ---------------------------------------------------------------------------
56
57 function addVideo (req, res, next) {
58 const video_file = req.files.videofile[0]
59 const video_infos = req.body
60
61 videos.seed(video_file.path, function (err, torrent) {
62 if (err) {
63 logger.error('Cannot seed this video.')
64 return next(err)
65 }
66
67 videos.getVideoDuration(video_file.path, function (err, duration) {
68 if (err) {
69 // TODO: unseed the video
70 logger.error('Cannot retrieve metadata of the file.')
71 return next(err)
72 }
73
74 videos.getVideoThumbnail(video_file.path, function (err, thumbnail_name) {
75 if (err) {
76 // TODO: unseed the video
77 logger.error('Cannot make a thumbnail of the video file.')
78 return next(err)
79 }
80
81 const video_data = {
82 name: video_infos.name,
83 namePath: video_file.filename,
84 description: video_infos.description,
85 magnetUri: torrent.magnetURI,
86 author: res.locals.oauth.token.user.username,
87 duration: duration,
88 thumbnail: thumbnail_name
89 }
90
91 Videos.add(video_data, function (err) {
92 if (err) {
93 // TODO unseed the video
94 logger.error('Cannot insert this video in the database.')
95 return next(err)
96 }
97
98 fs.readFile(thumbnailsDir + thumbnail_name, function (err, data) {
99 if (err) {
100 // TODO: remove video?
101 logger.error('Cannot read the thumbnail of the video')
102 return next(err)
103 }
104
105 // Set the image in base64
106 video_data.thumbnail_base64 = new Buffer(data).toString('base64')
107 // Now we'll add the video's meta data to our friends
108 friends.addVideoToFriends(video_data)
109
110 // TODO : include Location of the new video -> 201
111 res.type('json').status(204).end()
112 })
113 })
114 })
115 })
116 })
117 }
118
119 function getVideos (req, res, next) {
120 Videos.get(req.params.id, function (err, video_obj) {
121 if (err) return next(err)
122
123 const state = videos.getVideoState(video_obj)
124 if (state.exist === false) {
125 return res.type('json').status(204).end()
126 }
127
128 res.json(getFormatedVideo(video_obj))
129 })
130 }
131
132 function listVideos (req, res, next) {
133 Videos.list(function (err, videos_list) {
134 if (err) return next(err)
135
136 res.json(getFormatedVideos(videos_list))
137 })
138 }
139
140 function removeVideo (req, res, next) {
141 const video_id = req.params.id
142 Videos.get(video_id, function (err, video) {
143 if (err) return next(err)
144
145 removeTorrent(video.magnetUri, function () {
146 Videos.removeOwned(req.params.id, function (err) {
147 if (err) return next(err)
148
149 videos.removeVideosDataFromDisk([ video ], function (err) {
150 if (err) logger.error('Cannot remove video data from disk.', { video: video })
151
152 const params = {
153 name: video.name,
154 magnetUri: video.magnetUri
155 }
156
157 friends.removeVideoToFriends(params)
158 res.type('json').status(204).end()
159 })
160 })
161 })
162 })
163 }
164
165 function searchVideos (req, res, next) {
166 Videos.search(req.params.name, function (err, videos_list) {
167 if (err) return next(err)
168
169 res.json(getFormatedVideos(videos_list))
170 })
171 }
172
173 // ---------------------------------------------------------------------------
174
175 function getFormatedVideo (video_obj) {
176 const formated_video = {
177 id: video_obj._id,
178 name: video_obj.name,
179 description: video_obj.description,
180 podUrl: video_obj.podUrl,
181 isLocal: videos.getVideoState(video_obj).owned,
182 magnetUri: video_obj.magnetUri,
183 author: video_obj.author,
184 duration: video_obj.duration,
185 thumbnail_path: constants.THUMBNAILS_STATIC_PATH + '/' + video_obj.thumbnail
186 }
187
188 return formated_video
189 }
190
191 function getFormatedVideos (videos_obj) {
192 const formated_videos = []
193
194 videos_obj.forEach(function (video_obj) {
195 formated_videos.push(getFormatedVideo(video_obj))
196 })
197
198 return formated_videos
199 }
200
201 // Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
202 function removeTorrent (magnetUri, callback) {
203 try {
204 webtorrent.remove(magnetUri, callback)
205 } catch (err) {
206 logger.warn('Cannot remove the torrent from WebTorrent', { err: err })
207 return callback(null)
208 }
209 }