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