aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/remote/videos.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api/remote/videos.ts')
-rw-r--r--server/controllers/api/remote/videos.ts301
1 files changed, 257 insertions, 44 deletions
diff --git a/server/controllers/api/remote/videos.ts b/server/controllers/api/remote/videos.ts
index 23023211f..c8f531490 100644
--- a/server/controllers/api/remote/videos.ts
+++ b/server/controllers/api/remote/videos.ts
@@ -1,5 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as Promise from 'bluebird' 2import * as Promise from 'bluebird'
3import * as Sequelize from 'sequelize'
3 4
4import { database as db } from '../../../initializers/database' 5import { database as db } from '../../../initializers/database'
5import { 6import {
@@ -27,17 +28,28 @@ import {
27 RemoteQaduVideoRequest, 28 RemoteQaduVideoRequest,
28 RemoteQaduVideoData, 29 RemoteQaduVideoData,
29 RemoteVideoEventRequest, 30 RemoteVideoEventRequest,
30 RemoteVideoEventData 31 RemoteVideoEventData,
32 RemoteVideoChannelCreateData,
33 RemoteVideoChannelUpdateData,
34 RemoteVideoChannelRemoveData,
35 RemoteVideoAuthorRemoveData,
36 RemoteVideoAuthorCreateData
31} from '../../../../shared' 37} from '../../../../shared'
32 38
33const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] 39const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
34 40
35// Functions to call when processing a remote request 41// Functions to call when processing a remote request
42// FIXME: use RemoteVideoRequestType as id type
36const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {} 43const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
37functionsHash[ENDPOINT_ACTIONS.ADD] = addRemoteVideoRetryWrapper 44functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper
38functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper 45functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper
39functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo 46functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper
40functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideo 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
41 53
42const remoteVideosRouter = express.Router() 54const remoteVideosRouter = express.Router()
43 55
@@ -133,7 +145,7 @@ function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromP
133function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { 145function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
134 146
135 return db.sequelize.transaction(t => { 147 return db.sequelize.transaction(t => {
136 return fetchVideoByUUID(eventData.uuid) 148 return fetchVideoByUUID(eventData.uuid, t)
137 .then(videoInstance => { 149 .then(videoInstance => {
138 const options = { transaction: t } 150 const options = { transaction: t }
139 151
@@ -196,7 +208,7 @@ function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodI
196 let videoUUID = '' 208 let videoUUID = ''
197 209
198 return db.sequelize.transaction(t => { 210 return db.sequelize.transaction(t => {
199 return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid) 211 return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
200 .then(videoInstance => { 212 .then(videoInstance => {
201 const options = { transaction: t } 213 const options = { transaction: t }
202 214
@@ -239,22 +251,16 @@ function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodI
239 .then(video => { 251 .then(video => {
240 if (video) throw new Error('UUID already exists.') 252 if (video) throw new Error('UUID already exists.')
241 253
242 return undefined 254 return db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
243 }) 255 })
244 .then(() => { 256 .then(videoChannel => {
245 const name = videoToCreateData.author 257 if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')
246 const podId = fromPod.id
247 // This author is from another pod so we do not associate a user
248 const userId = null
249 258
250 return db.Author.findOrCreateAuthor(name, podId, userId, t)
251 })
252 .then(author => {
253 const tags = videoToCreateData.tags 259 const tags = videoToCreateData.tags
254 260
255 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances })) 261 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoChannel, tagInstances }))
256 }) 262 })
257 .then(({ author, tagInstances }) => { 263 .then(({ videoChannel, tagInstances }) => {
258 const videoData = { 264 const videoData = {
259 name: videoToCreateData.name, 265 name: videoToCreateData.name,
260 uuid: videoToCreateData.uuid, 266 uuid: videoToCreateData.uuid,
@@ -263,7 +269,7 @@ function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodI
263 language: videoToCreateData.language, 269 language: videoToCreateData.language,
264 nsfw: videoToCreateData.nsfw, 270 nsfw: videoToCreateData.nsfw,
265 description: videoToCreateData.description, 271 description: videoToCreateData.description,
266 authorId: author.id, 272 channelId: videoChannel.id,
267 duration: videoToCreateData.duration, 273 duration: videoToCreateData.duration,
268 createdAt: videoToCreateData.createdAt, 274 createdAt: videoToCreateData.createdAt,
269 // FIXME: updatedAt does not seems to be considered by Sequelize 275 // FIXME: updatedAt does not seems to be considered by Sequelize
@@ -336,7 +342,7 @@ function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, from
336 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) 342 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
337 343
338 return db.sequelize.transaction(t => { 344 return db.sequelize.transaction(t => {
339 return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid) 345 return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
340 .then(videoInstance => { 346 .then(videoInstance => {
341 const tags = videoAttributesToUpdate.tags 347 const tags = videoAttributesToUpdate.tags
342 348
@@ -365,7 +371,7 @@ function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, from
365 371
366 // Remove old video files 372 // Remove old video files
367 videoInstance.VideoFiles.forEach(videoFile => { 373 videoInstance.VideoFiles.forEach(videoFile => {
368 tasks.push(videoFile.destroy()) 374 tasks.push(videoFile.destroy({ transaction: t }))
369 }) 375 })
370 376
371 return Promise.all(tasks).then(() => ({ tagInstances, videoInstance })) 377 return Promise.all(tasks).then(() => ({ tagInstances, videoInstance }))
@@ -404,37 +410,231 @@ function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, from
404 }) 410 })
405} 411}
406 412
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
407function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { 422function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
408 // We need the instance because we have to remove some other stuffs (thumbnail etc) 423 logger.debug('Removing remote video "%s".', videoToRemoveData.uuid)
409 return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid) 424
410 .then(video => { 425 return db.sequelize.transaction(t => {
411 logger.debug('Removing remote video with uuid %s.', video.uuid) 426 // We need the instance because we have to remove some other stuffs (thumbnail etc)
412 return video.destroy() 427 return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
413 }) 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))
414 .catch(err => { 469 .catch(err => {
415 logger.debug('Could not fetch remote video.', { host: fromPod.host, uuid: videoToRemoveData.uuid, error: err.stack }) 470 logger.debug('Cannot insert the remote video author.', err)
471 throw err
416 }) 472 })
417} 473}
418 474
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
419function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { 612function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
420 return fetchVideoByUUID(reportData.videoUUID) 613 logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)
421 .then(video => {
422 logger.debug('Reporting remote abuse for video %s.', video.id)
423 614
424 const videoAbuseData = { 615 return db.sequelize.transaction(t => {
425 reporterUsername: reportData.reporterUsername, 616 return fetchVideoByUUID(reportData.videoUUID, t)
426 reason: reportData.reportReason, 617 .then(video => {
427 reporterPodId: fromPod.id, 618 const videoAbuseData = {
428 videoId: video.id 619 reporterUsername: reportData.reporterUsername,
429 } 620 reason: reportData.reportReason,
621 reporterPodId: fromPod.id,
622 videoId: video.id
623 }
430 624
431 return db.VideoAbuse.create(videoAbuseData) 625 return db.VideoAbuse.create(videoAbuseData)
432 }) 626 })
433 .catch(err => logger.error('Cannot create remote abuse video.', err)) 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 })
434} 634}
435 635
436function fetchVideoByUUID (id: string) { 636function fetchVideoByUUID (id: string, t: Sequelize.Transaction) {
437 return db.Video.loadByUUID(id) 637 return db.Video.loadByUUID(id, t)
438 .then(video => { 638 .then(video => {
439 if (!video) throw new Error('Video not found') 639 if (!video) throw new Error('Video not found')
440 640
@@ -446,8 +646,8 @@ function fetchVideoByUUID (id: string) {
446 }) 646 })
447} 647}
448 648
449function fetchVideoByHostAndUUID (podHost: string, uuid: string) { 649function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
450 return db.Video.loadByHostAndUUID(podHost, uuid) 650 return db.Video.loadByHostAndUUID(podHost, uuid, t)
451 .then(video => { 651 .then(video => {
452 if (!video) throw new Error('Video not found') 652 if (!video) throw new Error('Video not found')
453 653
@@ -458,3 +658,16 @@ function fetchVideoByHostAndUUID (podHost: string, uuid: string) {
458 throw err 658 throw err
459 }) 659 })
460} 660}
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}