]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/controllers/api/remote/videos.ts
Add video channels
[github/Chocobozzz/PeerTube.git] / server / controllers / api / remote / videos.ts
CommitLineData
4d4e5cd4 1import * as express from 'express'
6fcd19ba 2import * as Promise 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'
65fcc311 20import { quickAndDirtyUpdatesVideoToFriends } 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
4771e000 90 Promise.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
4771e000 112 Promise.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
4771e000 126 Promise.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
4771e000 136function 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
6fcd19ba 142 return retryTransactionWrapper(processVideosEvents, options)
e4c87ec2
C
143}
144
4771e000 145function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
e4c87ec2 146
6fcd19ba 147 return db.sequelize.transaction(t => {
72c7248b 148 return fetchVideoByUUID(eventData.uuid, t)
6fcd19ba
C
149 .then(videoInstance => {
150 const options = { transaction: t }
e4c87ec2 151
6fcd19ba
C
152 let columnToUpdate
153 let qaduType
e4c87ec2 154
6fcd19ba 155 switch (eventData.eventType) {
980246ea
C
156 case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
157 columnToUpdate = 'views'
158 qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
159 break
160
161 case REQUEST_VIDEO_EVENT_TYPES.LIKES:
162 columnToUpdate = 'likes'
163 qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
164 break
165
166 case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
167 columnToUpdate = 'dislikes'
168 qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
169 break
170
171 default:
172 throw new Error('Unknown video event type.')
6fcd19ba 173 }
e4c87ec2 174
6fcd19ba
C
175 const query = {}
176 query[columnToUpdate] = eventData.count
e4c87ec2 177
6fcd19ba 178 return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
d38b8281 179 })
6fcd19ba
C
180 .then(({ videoInstance, qaduType }) => {
181 const qadusParams = [
182 {
183 videoId: videoInstance.id,
184 type: qaduType
185 }
186 ]
187
188 return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
e4c87ec2 189 })
6fcd19ba 190 })
6d33593a 191 .then(() => logger.info('Remote video event processed for video with uuid %s.', eventData.uuid))
6fcd19ba 192 .catch(err => {
ad0997ad 193 logger.debug('Cannot process a video event.', err)
6fcd19ba 194 throw err
e4c87ec2
C
195 })
196}
197
4771e000 198function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
9e167724
C
199 const options = {
200 arguments: [ videoData, fromPod ],
201 errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
202 }
203
6fcd19ba 204 return retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
9e167724
C
205}
206
4771e000 207function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
6d33593a 208 let videoUUID = ''
f148e5ed 209
6fcd19ba 210 return db.sequelize.transaction(t => {
72c7248b 211 return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
6fcd19ba
C
212 .then(videoInstance => {
213 const options = { transaction: t }
9e167724 214
6d33593a 215 videoUUID = videoInstance.uuid
f148e5ed 216
6fcd19ba
C
217 if (videoData.views) {
218 videoInstance.set('views', videoData.views)
219 }
9e167724 220
6fcd19ba
C
221 if (videoData.likes) {
222 videoInstance.set('likes', videoData.likes)
223 }
9e167724 224
6fcd19ba
C
225 if (videoData.dislikes) {
226 videoInstance.set('dislikes', videoData.dislikes)
227 }
9e167724 228
6fcd19ba 229 return videoInstance.save(options)
9e167724 230 })
9e167724 231 })
6d33593a 232 .then(() => logger.info('Remote video with uuid %s quick and dirty updated', videoUUID))
ad0997ad 233 .catch(err => logger.debug('Cannot quick and dirty update the remote video.', err))
9e167724
C
234}
235
ed04d94f 236// Handle retries on fail
4771e000 237function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
d6a5b018
C
238 const options = {
239 arguments: [ videoToCreateData, fromPod ],
240 errorMessage: 'Cannot insert the remote video with many retries.'
241 }
ed04d94f 242
6fcd19ba 243 return retryTransactionWrapper(addRemoteVideo, options)
ed04d94f
C
244}
245
4771e000 246function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
0a6658fd 247 logger.debug('Adding remote video "%s".', videoToCreateData.uuid)
6666aad4 248
6fcd19ba 249 return db.sequelize.transaction(t => {
0a6658fd 250 return db.Video.loadByUUID(videoToCreateData.uuid)
6fcd19ba 251 .then(video => {
0a6658fd 252 if (video) throw new Error('UUID already exists.')
cddadde8 253
72c7248b 254 return db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
cddadde8 255 })
72c7248b
C
256 .then(videoChannel => {
257 if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')
feb4bdfd 258
6fcd19ba 259 const tags = videoToCreateData.tags
feb4bdfd 260
72c7248b 261 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoChannel, tagInstances }))
7920c273 262 })
72c7248b 263 .then(({ videoChannel, tagInstances }) => {
6fcd19ba
C
264 const videoData = {
265 name: videoToCreateData.name,
0a6658fd 266 uuid: videoToCreateData.uuid,
6fcd19ba
C
267 category: videoToCreateData.category,
268 licence: videoToCreateData.licence,
269 language: videoToCreateData.language,
270 nsfw: videoToCreateData.nsfw,
271 description: videoToCreateData.description,
72c7248b 272 channelId: videoChannel.id,
6fcd19ba
C
273 duration: videoToCreateData.duration,
274 createdAt: videoToCreateData.createdAt,
275 // FIXME: updatedAt does not seems to be considered by Sequelize
276 updatedAt: videoToCreateData.updatedAt,
277 views: videoToCreateData.views,
278 likes: videoToCreateData.likes,
0a6658fd
C
279 dislikes: videoToCreateData.dislikes,
280 remote: true
feb4bdfd
C
281 }
282
6fcd19ba
C
283 const video = db.Video.build(videoData)
284 return { tagInstances, video }
feb4bdfd 285 })
6fcd19ba
C
286 .then(({ tagInstances, video }) => {
287 return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
7920c273 288 })
6fcd19ba
C
289 .then(({ tagInstances, video }) => {
290 const options = {
291 transaction: t
292 }
7920c273 293
6fcd19ba 294 return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
7920c273 295 })
93e1258c
C
296 .then(({ tagInstances, videoCreated }) => {
297 const tasks = []
298 const options = {
299 transaction: t
300 }
301
302 videoToCreateData.files.forEach(fileData => {
303 const videoFileInstance = db.VideoFile.build({
304 extname: fileData.extname,
305 infoHash: fileData.infoHash,
306 resolution: fileData.resolution,
307 size: fileData.size,
308 videoId: videoCreated.id
309 })
310
311 tasks.push(videoFileInstance.save(options))
312 })
313
314 return Promise.all(tasks).then(() => ({ tagInstances, videoCreated }))
315 })
6fcd19ba
C
316 .then(({ tagInstances, videoCreated }) => {
317 const options = {
318 transaction: t
319 }
7920c273 320
6fcd19ba
C
321 return videoCreated.setTags(tagInstances, options)
322 })
323 })
6d33593a 324 .then(() => logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid))
6fcd19ba 325 .catch(err => {
ad0997ad 326 logger.debug('Cannot insert the remote video.', err)
6fcd19ba 327 throw err
7920c273 328 })
528a9efa
C
329}
330
ed04d94f 331// Handle retries on fail
4771e000 332function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
d6a5b018 333 const options = {
fbc22d79 334 arguments: [ videoAttributesToUpdate, fromPod ],
d6a5b018
C
335 errorMessage: 'Cannot update the remote video with many retries'
336 }
ed04d94f 337
6fcd19ba 338 return retryTransactionWrapper(updateRemoteVideo, options)
ed04d94f
C
339}
340
4771e000 341function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
0a6658fd 342 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
feb4bdfd 343
6fcd19ba 344 return db.sequelize.transaction(t => {
72c7248b 345 return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
6fcd19ba
C
346 .then(videoInstance => {
347 const tags = videoAttributesToUpdate.tags
3d118fb5 348
6fcd19ba 349 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
3d118fb5 350 })
6fcd19ba
C
351 .then(({ videoInstance, tagInstances }) => {
352 const options = { transaction: t }
353
354 videoInstance.set('name', videoAttributesToUpdate.name)
355 videoInstance.set('category', videoAttributesToUpdate.category)
356 videoInstance.set('licence', videoAttributesToUpdate.licence)
357 videoInstance.set('language', videoAttributesToUpdate.language)
358 videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
359 videoInstance.set('description', videoAttributesToUpdate.description)
6fcd19ba
C
360 videoInstance.set('duration', videoAttributesToUpdate.duration)
361 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
362 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
6fcd19ba
C
363 videoInstance.set('views', videoAttributesToUpdate.views)
364 videoInstance.set('likes', videoAttributesToUpdate.likes)
365 videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
366
367 return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
3d118fb5 368 })
93e1258c 369 .then(({ tagInstances, videoInstance }) => {
6d33593a
C
370 const tasks: Promise<void>[] = []
371
372 // Remove old video files
373 videoInstance.VideoFiles.forEach(videoFile => {
72c7248b 374 tasks.push(videoFile.destroy({ transaction: t }))
6d33593a
C
375 })
376
377 return Promise.all(tasks).then(() => ({ tagInstances, videoInstance }))
378 })
379 .then(({ tagInstances, videoInstance }) => {
380 const tasks: Promise<VideoFileInstance>[] = []
93e1258c
C
381 const options = {
382 transaction: t
383 }
384
385 videoAttributesToUpdate.files.forEach(fileData => {
386 const videoFileInstance = db.VideoFile.build({
387 extname: fileData.extname,
388 infoHash: fileData.infoHash,
389 resolution: fileData.resolution,
390 size: fileData.size,
391 videoId: videoInstance.id
392 })
393
394 tasks.push(videoFileInstance.save(options))
395 })
396
397 return Promise.all(tasks).then(() => ({ tagInstances, videoInstance }))
398 })
6fcd19ba
C
399 .then(({ videoInstance, tagInstances }) => {
400 const options = { transaction: t }
3d118fb5 401
6fcd19ba 402 return videoInstance.setTags(tagInstances, options)
3d118fb5 403 })
6fcd19ba 404 })
6d33593a 405 .then(() => logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid))
6fcd19ba
C
406 .catch(err => {
407 // This is just a debug because we will retry the insert
ad0997ad 408 logger.debug('Cannot update the remote video.', err)
6fcd19ba 409 throw err
3d118fb5
C
410 })
411}
412
72c7248b
C
413function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
414 const options = {
415 arguments: [ videoToRemoveData, fromPod ],
416 errorMessage: 'Cannot remove the remote video channel with many retries.'
417 }
418
419 return retryTransactionWrapper(removeRemoteVideo, options)
420}
421
4771e000 422function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
72c7248b
C
423 logger.debug('Removing remote video "%s".', videoToRemoveData.uuid)
424
425 return db.sequelize.transaction(t => {
426 // We need the instance because we have to remove some other stuffs (thumbnail etc)
427 return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
428 .then(video => video.destroy({ transaction: t }))
429 })
430 .then(() => logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid))
431 .catch(err => {
432 logger.debug('Cannot remove the remote video.', err)
433 throw err
434 })
435}
436
437function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
438 const options = {
439 arguments: [ authorToCreateData, fromPod ],
440 errorMessage: 'Cannot insert the remote video author with many retries.'
441 }
442
443 return retryTransactionWrapper(addRemoteVideoAuthor, options)
444}
445
446function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
447 logger.debug('Adding remote video author "%s".', authorToCreateData.uuid)
448
449 return db.sequelize.transaction(t => {
450 return db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t)
451 .then(author => {
452 if (author) throw new Error('UUID already exists.')
453
454 return undefined
455 })
456 .then(() => {
457 const videoAuthorData = {
458 name: authorToCreateData.name,
459 uuid: authorToCreateData.uuid,
460 userId: null, // Not on our pod
461 podId: fromPod.id
462 }
463
464 const author = db.Author.build(videoAuthorData)
465 return author.save({ transaction: t })
466 })
467 })
468 .then(() => logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid))
6fcd19ba 469 .catch(err => {
72c7248b
C
470 logger.debug('Cannot insert the remote video author.', err)
471 throw err
d8cc063e 472 })
55fa55a9
C
473}
474
72c7248b
C
475function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
476 const options = {
477 arguments: [ authorAttributesToRemove, fromPod ],
478 errorMessage: 'Cannot remove the remote video author with many retries.'
479 }
480
481 return retryTransactionWrapper(removeRemoteVideoAuthor, options)
482}
483
484function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
485 logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid)
486
487 return db.sequelize.transaction(t => {
488 return db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
489 .then(videoAuthor => videoAuthor.destroy({ transaction: t }))
490 })
491 .then(() => logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid))
492 .catch(err => {
493 logger.debug('Cannot remove the remote video author.', err)
494 throw err
495 })
496}
497
498function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
499 const options = {
500 arguments: [ videoChannelToCreateData, fromPod ],
501 errorMessage: 'Cannot insert the remote video channel with many retries.'
502 }
503
504 return retryTransactionWrapper(addRemoteVideoChannel, options)
505}
506
507function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
508 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
509
510 return db.sequelize.transaction(t => {
511 return db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid)
512 .then(videoChannel => {
513 if (videoChannel) throw new Error('UUID already exists.')
514
515 return undefined
516 })
517 .then(() => {
518 const authorUUID = videoChannelToCreateData.ownerUUID
519 const podId = fromPod.id
520
521 return db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t)
522 })
523 .then(author => {
524 if (!author) throw new Error('Unknown author UUID.')
525
526 const videoChannelData = {
527 name: videoChannelToCreateData.name,
528 description: videoChannelToCreateData.description,
529 uuid: videoChannelToCreateData.uuid,
530 createdAt: videoChannelToCreateData.createdAt,
531 updatedAt: videoChannelToCreateData.updatedAt,
532 remote: true,
533 authorId: author.id
534 }
535
536 const videoChannel = db.VideoChannel.build(videoChannelData)
537 return videoChannel.save({ transaction: t })
538 })
539 })
540 .then(() => logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid))
541 .catch(err => {
542 logger.debug('Cannot insert the remote video channel.', err)
543 throw err
544 })
545}
546
547function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
548 const options = {
549 arguments: [ videoChannelAttributesToUpdate, fromPod ],
550 errorMessage: 'Cannot update the remote video channel with many retries.'
551 }
552
553 return retryTransactionWrapper(updateRemoteVideoChannel, options)
554}
555
556function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
557 logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid)
558
559 return db.sequelize.transaction(t => {
560 return fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t)
561 .then(videoChannelInstance => {
562 const options = { transaction: t }
563
564 videoChannelInstance.set('name', videoChannelAttributesToUpdate.name)
565 videoChannelInstance.set('description', videoChannelAttributesToUpdate.description)
566 videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt)
567 videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt)
568
569 return videoChannelInstance.save(options)
570 })
571 })
572 .then(() => logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid))
573 .catch(err => {
574 // This is just a debug because we will retry the insert
575 logger.debug('Cannot update the remote video channel.', err)
576 throw err
577 })
578}
579
580function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
581 const options = {
582 arguments: [ videoChannelAttributesToRemove, fromPod ],
583 errorMessage: 'Cannot remove the remote video channel with many retries.'
584 }
585
586 return retryTransactionWrapper(removeRemoteVideoChannel, options)
587}
588
589function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
590 logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid)
591
592 return db.sequelize.transaction(t => {
593 return fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t)
594 .then(videoChannel => videoChannel.destroy({ transaction: t }))
595 })
596 .then(() => logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid))
597 .catch(err => {
598 logger.debug('Cannot remove the remote video channel.', err)
599 throw err
600 })
601}
602
603function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
604 const options = {
605 arguments: [ reportData, fromPod ],
606 errorMessage: 'Cannot create remote abuse video with many retries.'
607 }
608
609 return retryTransactionWrapper(reportAbuseRemoteVideo, options)
610}
611
4771e000 612function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
72c7248b 613 logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)
55fa55a9 614
72c7248b
C
615 return db.sequelize.transaction(t => {
616 return fetchVideoByUUID(reportData.videoUUID, t)
617 .then(video => {
618 const videoAbuseData = {
619 reporterUsername: reportData.reporterUsername,
620 reason: reportData.reportReason,
621 reporterPodId: fromPod.id,
622 videoId: video.id
623 }
d8cc063e 624
72c7248b
C
625 return db.VideoAbuse.create(videoAbuseData)
626 })
627 })
628 .then(() => logger.info('Remote abuse for video uuid %s created', reportData.videoUUID))
629 .catch(err => {
630 // This is just a debug because we will retry the insert
631 logger.debug('Cannot create remote abuse video', err)
632 throw err
633 })
55fa55a9
C
634}
635
72c7248b
C
636function fetchVideoByUUID (id: string, t: Sequelize.Transaction) {
637 return db.Video.loadByUUID(id, t)
6fcd19ba
C
638 .then(video => {
639 if (!video) throw new Error('Video not found')
e4c87ec2 640
6fcd19ba
C
641 return video
642 })
643 .catch(err => {
ad0997ad 644 logger.error('Cannot load owned video from id.', { error: err.stack, id })
6fcd19ba
C
645 throw err
646 })
e4c87ec2
C
647}
648
72c7248b
C
649function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
650 return db.Video.loadByHostAndUUID(podHost, uuid, t)
6fcd19ba
C
651 .then(video => {
652 if (!video) throw new Error('Video not found')
55fa55a9 653
6fcd19ba
C
654 return video
655 })
656 .catch(err => {
0a6658fd 657 logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
6fcd19ba
C
658 throw err
659 })
528a9efa 660}
72c7248b
C
661
662function fetchVideoChannelByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
663 return db.VideoChannel.loadByHostAndUUID(podHost, uuid, t)
664 .then(videoChannel => {
665 if (!videoChannel) throw new Error('Video channel not found')
666
667 return videoChannel
668 })
669 .catch(err => {
670 logger.error('Cannot load video channel from host and uuid.', { error: err.stack, podHost, uuid })
671 throw err
672 })
673}