]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/remote/videos.ts
Be tolerant with remote requests
[github/Chocobozzz/PeerTube.git] / server / controllers / api / remote / videos.ts
CommitLineData
4d4e5cd4 1import * as express from 'express'
eb080476 2import * as Bluebird from 'bluebird'
72c7248b 3import * as Sequelize from 'sequelize'
528a9efa 4
e02643f3 5import { database as db } from '../../../initializers/database'
65fcc311
C
6import {
7 REQUEST_ENDPOINT_ACTIONS,
8 REQUEST_ENDPOINTS,
9 REQUEST_VIDEO_EVENT_TYPES,
10 REQUEST_VIDEO_QADU_TYPES
11} from '../../../initializers'
12import {
13 checkSignature,
14 signatureValidator,
15 remoteVideosValidator,
16 remoteQaduVideosValidator,
17 remoteEventsVideosValidator
18} from '../../../middlewares'
6fcd19ba 19import { logger, retryTransactionWrapper } from '../../../helpers'
eb080476 20import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib'
6d33593a 21import { PodInstance, VideoFileInstance } from '../../../models'
4771e000
C
22import {
23 RemoteVideoRequest,
24 RemoteVideoCreateData,
25 RemoteVideoUpdateData,
26 RemoteVideoRemoveData,
27 RemoteVideoReportAbuseData,
28 RemoteQaduVideoRequest,
29 RemoteQaduVideoData,
30 RemoteVideoEventRequest,
72c7248b
C
31 RemoteVideoEventData,
32 RemoteVideoChannelCreateData,
33 RemoteVideoChannelUpdateData,
34 RemoteVideoChannelRemoveData,
35 RemoteVideoAuthorRemoveData,
36 RemoteVideoAuthorCreateData
4771e000 37} from '../../../../shared'
65fcc311
C
38
39const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
62f4ef41
C
40
41// Functions to call when processing a remote request
72c7248b 42// FIXME: use RemoteVideoRequestType as id type
6fcd19ba 43const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
72c7248b
C
44functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper
45functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper
46functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper
47functionsHash[ENDPOINT_ACTIONS.ADD_CHANNEL] = addRemoteVideoChannelRetryWrapper
48functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper
49functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper
50functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper
51functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper
52functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper
62f4ef41 53
65fcc311 54const remoteVideosRouter = express.Router()
528a9efa 55
65fcc311
C
56remoteVideosRouter.post('/',
57 signatureValidator,
58 checkSignature,
59 remoteVideosValidator,
528a9efa
C
60 remoteVideos
61)
62
65fcc311
C
63remoteVideosRouter.post('/qadu',
64 signatureValidator,
65 checkSignature,
66 remoteQaduVideosValidator,
9e167724
C
67 remoteVideosQadu
68)
69
65fcc311
C
70remoteVideosRouter.post('/events',
71 signatureValidator,
72 checkSignature,
73 remoteEventsVideosValidator,
e4c87ec2
C
74 remoteVideosEvents
75)
76
528a9efa
C
77// ---------------------------------------------------------------------------
78
65fcc311
C
79export {
80 remoteVideosRouter
81}
528a9efa
C
82
83// ---------------------------------------------------------------------------
84
69818c93 85function remoteVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
4771e000 86 const requests: RemoteVideoRequest[] = req.body.data
4ff0d862 87 const fromPod = res.locals.secure.pod
528a9efa
C
88
89 // We need to process in the same order to keep consistency
eb080476 90 Bluebird.each(requests, request => {
55fa55a9 91 const data = request.data
528a9efa 92
62f4ef41
C
93 // Get the function we need to call in order to process the request
94 const fun = functionsHash[request.type]
95 if (fun === undefined) {
6d33593a 96 logger.error('Unknown remote request type %s.', request.type)
6fcd19ba 97 return
528a9efa 98 }
62f4ef41 99
6fcd19ba 100 return fun.call(this, data, fromPod)
528a9efa 101 })
ad0997ad 102 .catch(err => logger.error('Error managing remote videos.', err))
528a9efa 103
709756b8 104 // Don't block the other pod
528a9efa
C
105 return res.type('json').status(204).end()
106}
107
69818c93 108function remoteVideosQadu (req: express.Request, res: express.Response, next: express.NextFunction) {
4771e000 109 const requests: RemoteQaduVideoRequest[] = req.body.data
9e167724
C
110 const fromPod = res.locals.secure.pod
111
eb080476 112 Bluebird.each(requests, request => {
9e167724
C
113 const videoData = request.data
114
6fcd19ba 115 return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
9e167724 116 })
ad0997ad 117 .catch(err => logger.error('Error managing remote videos.', err))
9e167724
C
118
119 return res.type('json').status(204).end()
120}
121
69818c93 122function remoteVideosEvents (req: express.Request, res: express.Response, next: express.NextFunction) {
4771e000 123 const requests: RemoteVideoEventRequest[] = req.body.data
e4c87ec2
C
124 const fromPod = res.locals.secure.pod
125
eb080476 126 Bluebird.each(requests, request => {
e4c87ec2
C
127 const eventData = request.data
128
6fcd19ba 129 return processVideosEventsRetryWrapper(eventData, fromPod)
e4c87ec2 130 })
ad0997ad 131 .catch(err => logger.error('Error managing remote videos.', err))
e4c87ec2
C
132
133 return res.type('json').status(204).end()
134}
135
eb080476 136async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) {
e4c87ec2
C
137 const options = {
138 arguments: [ eventData, fromPod ],
139 errorMessage: 'Cannot process videos events with many retries.'
140 }
141
eb080476 142 await retryTransactionWrapper(processVideosEvents, options)
e4c87ec2
C
143}
144
eb080476
C
145async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
146 await db.sequelize.transaction(async t => {
147 const sequelizeOptions = { transaction: t }
148 const videoInstance = await fetchVideoByUUID(eventData.uuid, t)
e4c87ec2 149
eb080476
C
150 let columnToUpdate
151 let qaduType
e4c87ec2 152
eb080476
C
153 switch (eventData.eventType) {
154 case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
155 columnToUpdate = 'views'
156 qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
157 break
e4c87ec2 158
eb080476
C
159 case REQUEST_VIDEO_EVENT_TYPES.LIKES:
160 columnToUpdate = 'likes'
161 qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
162 break
980246ea 163
eb080476
C
164 case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
165 columnToUpdate = 'dislikes'
166 qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
167 break
980246ea 168
eb080476
C
169 default:
170 throw new Error('Unknown video event type.')
171 }
980246ea 172
eb080476
C
173 const query = {}
174 query[columnToUpdate] = eventData.count
e4c87ec2 175
eb080476 176 await videoInstance.increment(query, sequelizeOptions)
e4c87ec2 177
eb080476
C
178 const qadusParams = [
179 {
180 videoId: videoInstance.id,
181 type: qaduType
182 }
183 ]
184 await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
e4c87ec2 185 })
eb080476
C
186
187 logger.info('Remote video event processed for video with uuid %s.', eventData.uuid)
e4c87ec2
C
188}
189
eb080476 190async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
9e167724
C
191 const options = {
192 arguments: [ videoData, fromPod ],
193 errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
194 }
195
eb080476 196 await retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
9e167724
C
197}
198
eb080476 199async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
6d33593a 200 let videoUUID = ''
f148e5ed 201
eb080476
C
202 await db.sequelize.transaction(async t => {
203 const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
204 const sequelizeOptions = { transaction: t }
9e167724 205
eb080476 206 videoUUID = videoInstance.uuid
f148e5ed 207
eb080476
C
208 if (videoData.views) {
209 videoInstance.set('views', videoData.views)
210 }
9e167724 211
eb080476
C
212 if (videoData.likes) {
213 videoInstance.set('likes', videoData.likes)
214 }
9e167724 215
eb080476
C
216 if (videoData.dislikes) {
217 videoInstance.set('dislikes', videoData.dislikes)
218 }
9e167724 219
eb080476 220 await videoInstance.save(sequelizeOptions)
9e167724 221 })
eb080476
C
222
223 logger.info('Remote video with uuid %s quick and dirty updated', videoUUID)
9e167724
C
224}
225
ed04d94f 226// Handle retries on fail
eb080476 227async function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
d6a5b018
C
228 const options = {
229 arguments: [ videoToCreateData, fromPod ],
230 errorMessage: 'Cannot insert the remote video with many retries.'
231 }
ed04d94f 232
eb080476 233 await retryTransactionWrapper(addRemoteVideo, options)
ed04d94f
C
234}
235
eb080476 236async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
0a6658fd 237 logger.debug('Adding remote video "%s".', videoToCreateData.uuid)
6666aad4 238
eb080476
C
239 await db.sequelize.transaction(async t => {
240 const sequelizeOptions = {
241 transaction: t
242 }
feb4bdfd 243
eb080476
C
244 const videoFromDatabase = await db.Video.loadByUUID(videoToCreateData.uuid)
245 if (videoFromDatabase) throw new Error('UUID already exists.')
246
247 const videoChannel = await db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
248 if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')
249
250 const tags = videoToCreateData.tags
251 const tagInstances = await db.Tag.findOrCreateTags(tags, t)
252
253 const videoData = {
254 name: videoToCreateData.name,
255 uuid: videoToCreateData.uuid,
256 category: videoToCreateData.category,
257 licence: videoToCreateData.licence,
258 language: videoToCreateData.language,
259 nsfw: videoToCreateData.nsfw,
260 description: videoToCreateData.description,
261 channelId: videoChannel.id,
262 duration: videoToCreateData.duration,
263 createdAt: videoToCreateData.createdAt,
264 // FIXME: updatedAt does not seems to be considered by Sequelize
265 updatedAt: videoToCreateData.updatedAt,
266 views: videoToCreateData.views,
267 likes: videoToCreateData.likes,
268 dislikes: videoToCreateData.dislikes,
269 remote: true
270 }
feb4bdfd 271
eb080476
C
272 const video = db.Video.build(videoData)
273 await db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData)
274 const videoCreated = await video.save(sequelizeOptions)
275
276 const tasks = []
277 for (const fileData of videoToCreateData.files) {
278 const videoFileInstance = db.VideoFile.build({
279 extname: fileData.extname,
280 infoHash: fileData.infoHash,
281 resolution: fileData.resolution,
282 size: fileData.size,
283 videoId: videoCreated.id
7920c273 284 })
7920c273 285
eb080476
C
286 tasks.push(videoFileInstance.save(sequelizeOptions))
287 }
93e1258c 288
eb080476 289 await Promise.all(tasks)
7920c273 290
eb080476 291 await videoCreated.setTags(tagInstances, sequelizeOptions)
7920c273 292 })
eb080476
C
293
294 logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
528a9efa
C
295}
296
ed04d94f 297// Handle retries on fail
eb080476 298async function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
d6a5b018 299 const options = {
fbc22d79 300 arguments: [ videoAttributesToUpdate, fromPod ],
d6a5b018
C
301 errorMessage: 'Cannot update the remote video with many retries'
302 }
ed04d94f 303
eb080476 304 await retryTransactionWrapper(updateRemoteVideo, options)
ed04d94f
C
305}
306
eb080476 307async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
0a6658fd 308 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
feb4bdfd 309
eb080476
C
310 try {
311 await db.sequelize.transaction(async t => {
312 const sequelizeOptions = {
313 transaction: t
314 }
315
316 const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
317 const tags = videoAttributesToUpdate.tags
318
319 const tagInstances = await db.Tag.findOrCreateTags(tags, t)
320
321 videoInstance.set('name', videoAttributesToUpdate.name)
322 videoInstance.set('category', videoAttributesToUpdate.category)
323 videoInstance.set('licence', videoAttributesToUpdate.licence)
324 videoInstance.set('language', videoAttributesToUpdate.language)
325 videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
326 videoInstance.set('description', videoAttributesToUpdate.description)
327 videoInstance.set('duration', videoAttributesToUpdate.duration)
328 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
329 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
330 videoInstance.set('views', videoAttributesToUpdate.views)
331 videoInstance.set('likes', videoAttributesToUpdate.likes)
332 videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
333
334 await videoInstance.save(sequelizeOptions)
335
336 // Remove old video files
337 const videoFileDestroyTasks: Bluebird<void>[] = []
338 for (const videoFile of videoInstance.VideoFiles) {
339 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
340 }
341 await Promise.all(videoFileDestroyTasks)
342
343 const videoFileCreateTasks: Bluebird<VideoFileInstance>[] = []
344 for (const fileData of videoAttributesToUpdate.files) {
345 const videoFileInstance = db.VideoFile.build({
346 extname: fileData.extname,
347 infoHash: fileData.infoHash,
348 resolution: fileData.resolution,
349 size: fileData.size,
350 videoId: videoInstance.id
6d33593a
C
351 })
352
eb080476
C
353 videoFileCreateTasks.push(videoFileInstance.save(sequelizeOptions))
354 }
93e1258c 355
eb080476 356 await Promise.all(videoFileCreateTasks)
3d118fb5 357
eb080476
C
358 await videoInstance.setTags(tagInstances, sequelizeOptions)
359 })
360
361 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
362 } catch (err) {
6fcd19ba 363 // This is just a debug because we will retry the insert
ad0997ad 364 logger.debug('Cannot update the remote video.', err)
6fcd19ba 365 throw err
eb080476 366 }
3d118fb5
C
367}
368
eb080476 369async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
72c7248b
C
370 const options = {
371 arguments: [ videoToRemoveData, fromPod ],
372 errorMessage: 'Cannot remove the remote video channel with many retries.'
373 }
374
eb080476 375 await retryTransactionWrapper(removeRemoteVideo, options)
72c7248b
C
376}
377
eb080476 378async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
72c7248b
C
379 logger.debug('Removing remote video "%s".', videoToRemoveData.uuid)
380
eb080476 381 await db.sequelize.transaction(async t => {
72c7248b 382 // We need the instance because we have to remove some other stuffs (thumbnail etc)
eb080476
C
383 const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
384 await videoInstance.destroy({ transaction: t })
72c7248b 385 })
eb080476
C
386
387 logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid)
72c7248b
C
388}
389
eb080476 390async function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
72c7248b
C
391 const options = {
392 arguments: [ authorToCreateData, fromPod ],
393 errorMessage: 'Cannot insert the remote video author with many retries.'
394 }
395
eb080476 396 await retryTransactionWrapper(addRemoteVideoAuthor, options)
72c7248b
C
397}
398
eb080476 399async function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
72c7248b
C
400 logger.debug('Adding remote video author "%s".', authorToCreateData.uuid)
401
eb080476
C
402 await db.sequelize.transaction(async t => {
403 const authorInDatabase = await db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t)
404 if (authorInDatabase) throw new Error('Author with UUID ' + authorToCreateData.uuid + ' already exists.')
72c7248b 405
eb080476
C
406 const videoAuthorData = {
407 name: authorToCreateData.name,
408 uuid: authorToCreateData.uuid,
409 userId: null, // Not on our pod
410 podId: fromPod.id
411 }
412
413 const author = db.Author.build(videoAuthorData)
414 await author.save({ transaction: t })
72c7248b 415 })
eb080476
C
416
417 logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid)
55fa55a9
C
418}
419
eb080476 420async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
72c7248b
C
421 const options = {
422 arguments: [ authorAttributesToRemove, fromPod ],
423 errorMessage: 'Cannot remove the remote video author with many retries.'
424 }
425
eb080476 426 await retryTransactionWrapper(removeRemoteVideoAuthor, options)
72c7248b
C
427}
428
eb080476 429async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
72c7248b
C
430 logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid)
431
eb080476
C
432 await db.sequelize.transaction(async t => {
433 const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
434 await videoAuthor.destroy({ transaction: t })
72c7248b 435 })
eb080476
C
436
437 logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid)
72c7248b
C
438}
439
eb080476 440async function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
72c7248b
C
441 const options = {
442 arguments: [ videoChannelToCreateData, fromPod ],
443 errorMessage: 'Cannot insert the remote video channel with many retries.'
444 }
445
eb080476 446 await retryTransactionWrapper(addRemoteVideoChannel, options)
72c7248b
C
447}
448
eb080476 449async function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
72c7248b
C
450 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
451
eb080476
C
452 await db.sequelize.transaction(async t => {
453 const videoChannelInDatabase = await db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid)
454 if (videoChannelInDatabase) {
455 throw new Error('Video channel with UUID ' + videoChannelToCreateData.uuid + ' already exists.')
456 }
72c7248b 457
eb080476
C
458 const authorUUID = videoChannelToCreateData.ownerUUID
459 const podId = fromPod.id
72c7248b 460
eb080476
C
461 const author = await db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t)
462 if (!author) throw new Error('Unknown author UUID' + authorUUID + '.')
463
464 const videoChannelData = {
465 name: videoChannelToCreateData.name,
466 description: videoChannelToCreateData.description,
467 uuid: videoChannelToCreateData.uuid,
468 createdAt: videoChannelToCreateData.createdAt,
469 updatedAt: videoChannelToCreateData.updatedAt,
470 remote: true,
471 authorId: author.id
472 }
473
474 const videoChannel = db.VideoChannel.build(videoChannelData)
475 await videoChannel.save({ transaction: t })
72c7248b 476 })
eb080476
C
477
478 logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
72c7248b
C
479}
480
eb080476 481async function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
72c7248b
C
482 const options = {
483 arguments: [ videoChannelAttributesToUpdate, fromPod ],
484 errorMessage: 'Cannot update the remote video channel with many retries.'
485 }
486
eb080476 487 await retryTransactionWrapper(updateRemoteVideoChannel, options)
72c7248b
C
488}
489
eb080476 490async function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
72c7248b
C
491 logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid)
492
eb080476
C
493 await db.sequelize.transaction(async t => {
494 const sequelizeOptions = { transaction: t }
72c7248b 495
eb080476
C
496 const videoChannelInstance = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t)
497 videoChannelInstance.set('name', videoChannelAttributesToUpdate.name)
498 videoChannelInstance.set('description', videoChannelAttributesToUpdate.description)
499 videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt)
500 videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt)
72c7248b 501
eb080476 502 await videoChannelInstance.save(sequelizeOptions)
72c7248b 503 })
eb080476
C
504
505 logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid)
72c7248b
C
506}
507
eb080476 508async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
72c7248b
C
509 const options = {
510 arguments: [ videoChannelAttributesToRemove, fromPod ],
511 errorMessage: 'Cannot remove the remote video channel with many retries.'
512 }
513
eb080476 514 await retryTransactionWrapper(removeRemoteVideoChannel, options)
72c7248b
C
515}
516
eb080476 517async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
72c7248b
C
518 logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid)
519
eb080476
C
520 await db.sequelize.transaction(async t => {
521 const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t)
522 await videoChannel.destroy({ transaction: t })
72c7248b 523 })
eb080476
C
524
525 logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid)
72c7248b
C
526}
527
eb080476 528async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
72c7248b
C
529 const options = {
530 arguments: [ reportData, fromPod ],
531 errorMessage: 'Cannot create remote abuse video with many retries.'
532 }
533
eb080476 534 await retryTransactionWrapper(reportAbuseRemoteVideo, options)
72c7248b
C
535}
536
eb080476 537async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
72c7248b 538 logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)
55fa55a9 539
eb080476
C
540 await db.sequelize.transaction(async t => {
541 const videoInstance = await fetchVideoByUUID(reportData.videoUUID, t)
542 const videoAbuseData = {
543 reporterUsername: reportData.reporterUsername,
544 reason: reportData.reportReason,
545 reporterPodId: fromPod.id,
546 videoId: videoInstance.id
547 }
55fa55a9 548
eb080476 549 await db.VideoAbuse.create(videoAbuseData)
e4c87ec2 550
eb080476
C
551 })
552
553 logger.info('Remote abuse for video uuid %s created', reportData.videoUUID)
e4c87ec2
C
554}
555
eb080476
C
556async function fetchVideoByUUID (id: string, t: Sequelize.Transaction) {
557 try {
558 const video = await db.Video.loadByUUID(id, t)
55fa55a9 559
eb080476
C
560 if (!video) throw new Error('Video ' + id + ' not found')
561
562 return video
563 } catch (err) {
564 logger.error('Cannot load owned video from id.', { error: err.stack, id })
565 throw err
566 }
528a9efa 567}
72c7248b 568
eb080476
C
569async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
570 try {
571 const video = await db.Video.loadByHostAndUUID(podHost, uuid, t)
572 if (!video) throw new Error('Video not found')
72c7248b 573
eb080476
C
574 return video
575 } catch (err) {
576 logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
577 throw err
578 }
72c7248b 579}