]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/remote/videos.js
Server: add video abuse support
[github/Chocobozzz/PeerTube.git] / server / controllers / api / remote / videos.js
CommitLineData
528a9efa
C
1'use strict'
2
1a42c9e2 3const eachSeries = require('async/eachSeries')
528a9efa 4const express = require('express')
feb4bdfd 5const waterfall = require('async/waterfall')
528a9efa 6
a6fd2b30
C
7const db = require('../../../initializers/database')
8const middlewares = require('../../../middlewares')
528a9efa 9const secureMiddleware = middlewares.secure
55fa55a9
C
10const videosValidators = middlewares.validators.remote.videos
11const signatureValidators = middlewares.validators.remote.signature
a6fd2b30 12const logger = require('../../../helpers/logger')
528a9efa
C
13
14const router = express.Router()
15
a6fd2b30 16router.post('/',
55fa55a9 17 signatureValidators.signature,
0eb78d53 18 secureMiddleware.checkSignature,
55fa55a9 19 videosValidators.remoteVideos,
528a9efa
C
20 remoteVideos
21)
22
23// ---------------------------------------------------------------------------
24
25module.exports = router
26
27// ---------------------------------------------------------------------------
28
29function remoteVideos (req, res, next) {
30 const requests = req.body.data
4ff0d862 31 const fromPod = res.locals.secure.pod
528a9efa
C
32
33 // We need to process in the same order to keep consistency
34 // TODO: optimization
1a42c9e2 35 eachSeries(requests, function (request, callbackEach) {
55fa55a9 36 const data = request.data
528a9efa 37
3d118fb5
C
38 switch (request.type) {
39 case 'add':
55fa55a9 40 addRemoteVideo(data, fromPod, callbackEach)
3d118fb5
C
41 break
42
43 case 'update':
55fa55a9 44 updateRemoteVideo(data, fromPod, callbackEach)
3d118fb5
C
45 break
46
47 case 'remove':
55fa55a9
C
48 removeRemoteVideo(data, fromPod, callbackEach)
49 break
50
51 case 'report-abuse':
52 reportAbuseRemoteVideo(data, fromPod, callbackEach)
3d118fb5
C
53 break
54
55 default:
56 logger.error('Unkown remote request type %s.', request.type)
528a9efa 57 }
aaf61f38
C
58 }, function (err) {
59 if (err) logger.error('Error managing remote videos.', { error: err })
528a9efa
C
60 })
61
62 // We don't need to keep the other pod waiting
63 return res.type('json').status(204).end()
64}
65
4ff0d862 66function addRemoteVideo (videoToCreateData, fromPod, finalCallback) {
a078c155 67 logger.debug('Adding remote video "%s".', videoToCreateData.name)
6666aad4 68
feb4bdfd
C
69 waterfall([
70
7920c273
C
71 function startTransaction (callback) {
72 db.sequelize.transaction().asCallback(function (err, t) {
73 return callback(err, t)
74 })
75 },
76
4ff0d862
C
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
feb4bdfd 82
4ff0d862
C
83 db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
84 return callback(err, t, authorInstance)
feb4bdfd
C
85 })
86 },
87
7920c273
C
88 function findOrCreateTags (t, author, callback) {
89 const tags = videoToCreateData.tags
7920c273 90
4ff0d862 91 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
7920c273
C
92 return callback(err, t, author, tagInstances)
93 })
94 },
95
96 function createVideoObject (t, author, tagInstances, callback) {
feb4bdfd
C
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,
124648d7 104 duration: videoToCreateData.duration,
79066fdf
C
105 createdAt: videoToCreateData.createdAt,
106 updatedAt: videoToCreateData.updatedAt
feb4bdfd
C
107 }
108
109 const video = db.Video.build(videoData)
110
7920c273 111 return callback(null, t, tagInstances, video)
feb4bdfd
C
112 },
113
7920c273 114 function generateThumbnail (t, tagInstances, video, callback) {
4d324488 115 db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
feb4bdfd 116 if (err) {
4d324488 117 logger.error('Cannot generate thumbnail from data.', { error: err })
feb4bdfd
C
118 return callback(err)
119 }
120
7920c273 121 return callback(err, t, tagInstances, video)
feb4bdfd
C
122 })
123 },
124
7920c273
C
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 })
c77fa067
C
141 }
142
7920c273
C
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 })
528a9efa
C
158}
159
3d118fb5
C
160function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) {
161 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name)
feb4bdfd 162
3d118fb5
C
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) {
55fa55a9
C
172 fetchVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
173 return callback(err, t, videoInstance)
3d118fb5
C
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)
79066fdf 193 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
3d118fb5
C
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 })
528a9efa
C
207 }
208
3d118fb5
C
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)
6666aad4
C
217 }
218
3d118fb5
C
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)
55fa55a9
C
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) {
3d118fb5 238 if (err || !video) {
55fa55a9
C
239 if (!err) err = new Error('video not found')
240
241 logger.error('Cannot load video from host and remote id.', { error: err })
3d118fb5
C
242 return callback(err)
243 }
6666aad4 244
55fa55a9
C
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)
528a9efa
C
268 })
269}