]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/controllers/api/remote/videos.js
Server: add video abuse support
[github/Chocobozzz/PeerTube.git] / server / controllers / api / remote / videos.js
... / ...
CommitLineData
1'use strict'
2
3const eachSeries = require('async/eachSeries')
4const express = require('express')
5const waterfall = require('async/waterfall')
6
7const db = require('../../../initializers/database')
8const middlewares = require('../../../middlewares')
9const secureMiddleware = middlewares.secure
10const videosValidators = middlewares.validators.remote.videos
11const signatureValidators = middlewares.validators.remote.signature
12const logger = require('../../../helpers/logger')
13
14const router = express.Router()
15
16router.post('/',
17 signatureValidators.signature,
18 secureMiddleware.checkSignature,
19 videosValidators.remoteVideos,
20 remoteVideos
21)
22
23// ---------------------------------------------------------------------------
24
25module.exports = router
26
27// ---------------------------------------------------------------------------
28
29function remoteVideos (req, res, next) {
30 const requests = req.body.data
31 const fromPod = res.locals.secure.pod
32
33 // We need to process in the same order to keep consistency
34 // TODO: optimization
35 eachSeries(requests, function (request, callbackEach) {
36 const data = request.data
37
38 switch (request.type) {
39 case 'add':
40 addRemoteVideo(data, fromPod, callbackEach)
41 break
42
43 case 'update':
44 updateRemoteVideo(data, fromPod, callbackEach)
45 break
46
47 case 'remove':
48 removeRemoteVideo(data, fromPod, callbackEach)
49 break
50
51 case 'report-abuse':
52 reportAbuseRemoteVideo(data, fromPod, callbackEach)
53 break
54
55 default:
56 logger.error('Unkown remote request type %s.', request.type)
57 }
58 }, function (err) {
59 if (err) logger.error('Error managing remote videos.', { error: err })
60 })
61
62 // We don't need to keep the other pod waiting
63 return res.type('json').status(204).end()
64}
65
66function addRemoteVideo (videoToCreateData, fromPod, finalCallback) {
67 logger.debug('Adding remote video "%s".', videoToCreateData.name)
68
69 waterfall([
70
71 function startTransaction (callback) {
72 db.sequelize.transaction().asCallback(function (err, t) {
73 return callback(err, t)
74 })
75 },
76
77 function findOrCreateAuthor (t, callback) {
78 const name = videoToCreateData.author
79 const podId = fromPod.id
80 // This author is from another pod so we do not associate a user
81 const userId = null
82
83 db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
84 return callback(err, t, authorInstance)
85 })
86 },
87
88 function findOrCreateTags (t, author, callback) {
89 const tags = videoToCreateData.tags
90
91 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
92 return callback(err, t, author, tagInstances)
93 })
94 },
95
96 function createVideoObject (t, author, tagInstances, callback) {
97 const videoData = {
98 name: videoToCreateData.name,
99 remoteId: videoToCreateData.remoteId,
100 extname: videoToCreateData.extname,
101 infoHash: videoToCreateData.infoHash,
102 description: videoToCreateData.description,
103 authorId: author.id,
104 duration: videoToCreateData.duration,
105 createdAt: videoToCreateData.createdAt,
106 updatedAt: videoToCreateData.updatedAt
107 }
108
109 const video = db.Video.build(videoData)
110
111 return callback(null, t, tagInstances, video)
112 },
113
114 function generateThumbnail (t, tagInstances, video, callback) {
115 db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
116 if (err) {
117 logger.error('Cannot generate thumbnail from data.', { error: err })
118 return callback(err)
119 }
120
121 return callback(err, t, tagInstances, video)
122 })
123 },
124
125 function insertVideoIntoDB (t, tagInstances, video, callback) {
126 const options = {
127 transaction: t
128 }
129
130 video.save(options).asCallback(function (err, videoCreated) {
131 return callback(err, t, tagInstances, videoCreated)
132 })
133 },
134
135 function associateTagsToVideo (t, tagInstances, video, callback) {
136 const options = { transaction: t }
137
138 video.setTags(tagInstances, options).asCallback(function (err) {
139 return callback(err, t)
140 })
141 }
142
143 ], function (err, t) {
144 if (err) {
145 logger.error('Cannot insert the remote video.')
146
147 // Abort transaction?
148 if (t) t.rollback()
149
150 return finalCallback(err)
151 }
152
153 // Commit transaction
154 t.commit()
155
156 return finalCallback()
157 })
158}
159
160function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) {
161 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name)
162
163 waterfall([
164
165 function startTransaction (callback) {
166 db.sequelize.transaction().asCallback(function (err, t) {
167 return callback(err, t)
168 })
169 },
170
171 function findVideo (t, callback) {
172 fetchVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
173 return callback(err, t, videoInstance)
174 })
175 },
176
177 function findOrCreateTags (t, videoInstance, callback) {
178 const tags = videoAttributesToUpdate.tags
179
180 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
181 return callback(err, t, videoInstance, tagInstances)
182 })
183 },
184
185 function updateVideoIntoDB (t, videoInstance, tagInstances, callback) {
186 const options = { transaction: t }
187
188 videoInstance.set('name', videoAttributesToUpdate.name)
189 videoInstance.set('description', videoAttributesToUpdate.description)
190 videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
191 videoInstance.set('duration', videoAttributesToUpdate.duration)
192 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
193 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
194 videoInstance.set('extname', videoAttributesToUpdate.extname)
195
196 videoInstance.save(options).asCallback(function (err) {
197 return callback(err, t, videoInstance, tagInstances)
198 })
199 },
200
201 function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
202 const options = { transaction: t }
203
204 videoInstance.setTags(tagInstances, options).asCallback(function (err) {
205 return callback(err, t)
206 })
207 }
208
209 ], function (err, t) {
210 if (err) {
211 logger.error('Cannot update the remote video.')
212
213 // Abort transaction?
214 if (t) t.rollback()
215
216 return finalCallback(err)
217 }
218
219 // Commit transaction
220 t.commit()
221
222 return finalCallback()
223 })
224}
225
226function removeRemoteVideo (videoToRemoveData, fromPod, callback) {
227 // We need the instance because we have to remove some other stuffs (thumbnail etc)
228 fetchVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
229 if (err) return callback(err)
230
231 logger.debug('Removing remote video %s.', video.remoteId)
232 video.destroy().asCallback(callback)
233 })
234}
235
236function reportAbuseRemoteVideo (reportData, fromPod, callback) {
237 db.Video.load(reportData.videoRemoteId, function (err, video) {
238 if (err || !video) {
239 if (!err) err = new Error('video not found')
240
241 logger.error('Cannot load video from host and remote id.', { error: err })
242 return callback(err)
243 }
244
245 logger.debug('Reporting remote abuse for video %s.', video.id)
246
247 const videoAbuseData = {
248 reporterUsername: reportData.reporterUsername,
249 reason: reportData.reportReason,
250 reporterPodId: fromPod.id,
251 videoId: video.id
252 }
253
254 db.VideoAbuse.create(videoAbuseData).asCallback(callback)
255 })
256}
257
258function fetchVideo (podHost, remoteId, callback) {
259 db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) {
260 if (err || !video) {
261 if (!err) err = new Error('video not found')
262
263 logger.error('Cannot load video from host and remote id.', { error: err })
264 return callback(err)
265 }
266
267 return callback(null, video)
268 })
269}