aboutsummaryrefslogblamecommitdiffhomepage
path: root/server/controllers/api/remote/videos.ts
blob: cba47f0a13061db84dd00b18356112dba88dc019 (plain) (tree)
1
2
3
4
5
                                  
                                    
                                      
 
                                                               












                              
                                                                                          
                                                                                                 
                                                                








                             





                               
                           
                                                                     

                                                                           

                                                     
                                               
                                                                       








                                                                                     
 
                                           
 



                            


              



                                


                  



                                  


                    

                                                                              


                    


                                                                              
                                                                                                 
                                                      
                                       

                                                             
                                      
                             
 


                                                                       
                                                                   
            
     
 
                                        
    
                                                                   
 
                              


                                           
                                                                                                     
                                                          

                                       
                                      

                                  
                                                                   
    
                                                                   



                                           
                                                                                                       
                                                           

                                       
                                      

                                  
                                                              
    
                                                                   



                                           
                                                                                                        




                                                                   
                                                             

 


                                                                                            
                                                                        
 

                      
 




                                               
 



                                               
 



                                                  
 


                                                  
 

                                           
 
                                                          
 






                                                            
    

                                                                                     

 
                                                                                                            




                                                                                     
                                                                  

 
                                                                                                
                    
 


                                                                                        
 
                                  
 


                                                 
 


                                                 
 


                                                       
 
                                              
    

                                                                             

 
                         
                                                                                                            



                                                                     
 
                                                        

 
                                                                                                
                                                                   
 



                                             
 















                                                                                                                
                                                          







                                                                      

                                        
     
 











                                                                                    
        
 

                                                          
 
                            
 
                                                              
    

                                                                            

 
                         
                                                                                                                     
                   
                                                    

                                                                    
 
                                                           

 
                                                                                                         
                                                                           

                                  
 






                                                                                                        
                                              








                                                                     
                                                                                    





                                                                       
                                                                   

















                                                                       

          

                                                                           
 
                                             
 




                                                                                  



                                                                       
                                                            
                                                        
             
   

 
                                                                                                               




                                                                             
                                                           

 
                                                                                                   

                                                                     
                                             
                                                                                       

                                                                                                
    

                                                                           

 
                                                                                                                         




                                                                            
                                                              

 
                                                                                                             

                                                                           


                                                                                                             
 








                                                   
    

                                                                                    

 
                                                                                                                                  




                                                                            
                                                                 

 
                                                                                                                      

                                                                                   


                                                                                                            
    

                                                                                         

 
                                                                                                                                 




                                                                             
                                                               

 
                                                                                                                     

                                                                                  




                                                                                                      
 

                                                         
 














                                                                               
    

                                                                                           

 
                                                                                                                                          




                                                                             
                                                                  

 
                                                                                                                              

                                                                                          

                                               
 




                                                                                                                           
 
                                                     
    

                                                                                               

 
                                                                                                                                          




                                                                             
                                                                  

 
                                                                                                                              

                                                                                          


                                                                                                                   
    

                                                                                                

 
                                                                                                                  




                                                                       
                                                                

 
                                                                                                      
                                                                            
 
                                             
                                                                              





                                                    
 
                                              
 


                                                                             

 
                                                                             
       
                                                            
 






                                                                              
 
 



                                                                                                  
 




                                                                                              
 
import * as express from 'express'
import * as Bluebird from 'bluebird'
import * as Sequelize from 'sequelize'

import { database as db } from '../../../initializers/database'
import {
  REQUEST_ENDPOINT_ACTIONS,
  REQUEST_ENDPOINTS,
  REQUEST_VIDEO_EVENT_TYPES,
  REQUEST_VIDEO_QADU_TYPES
} from '../../../initializers'
import {
  checkSignature,
  signatureValidator,
  remoteVideosValidator,
  remoteQaduVideosValidator,
  remoteEventsVideosValidator
} from '../../../middlewares'
import { logger, retryTransactionWrapper, resetSequelizeInstance } from '../../../helpers'
import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib'
import { PodInstance, VideoFileInstance } from '../../../models'
import {
  RemoteVideoRequest,
  RemoteVideoCreateData,
  RemoteVideoUpdateData,
  RemoteVideoRemoveData,
  RemoteVideoReportAbuseData,
  RemoteQaduVideoRequest,
  RemoteQaduVideoData,
  RemoteVideoEventRequest,
  RemoteVideoEventData,
  RemoteVideoChannelCreateData,
  RemoteVideoChannelUpdateData,
  RemoteVideoChannelRemoveData,
  RemoteVideoAuthorRemoveData,
  RemoteVideoAuthorCreateData
} from '../../../../shared'
import { VideoInstance } from '../../../models/video/video-interface'

const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]

// Functions to call when processing a remote request
// FIXME: use RemoteVideoRequestType as id type
const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.ADD_CHANNEL] = addRemoteVideoChannelRetryWrapper
functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper
functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper
functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper
functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper

const remoteVideosRouter = express.Router()

remoteVideosRouter.post('/',
  signatureValidator,
  checkSignature,
  remoteVideosValidator,
  remoteVideos
)

remoteVideosRouter.post('/qadu',
  signatureValidator,
  checkSignature,
  remoteQaduVideosValidator,
  remoteVideosQadu
)

remoteVideosRouter.post('/events',
  signatureValidator,
  checkSignature,
  remoteEventsVideosValidator,
  remoteVideosEvents
)

// ---------------------------------------------------------------------------

export {
  remoteVideosRouter
}

// ---------------------------------------------------------------------------

function remoteVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
  const requests: RemoteVideoRequest[] = req.body.data
  const fromPod = res.locals.secure.pod

  // We need to process in the same order to keep consistency
  Bluebird.each(requests, request => {
    const data = request.data

    // Get the function we need to call in order to process the request
    const fun = functionsHash[request.type]
    if (fun === undefined) {
      logger.error('Unknown remote request type %s.', request.type)
      return
    }

    return fun.call(this, data, fromPod)
  })
  .catch(err => logger.error('Error managing remote videos.', err))

  // Don't block the other pod
  return res.type('json').status(204).end()
}

function remoteVideosQadu (req: express.Request, res: express.Response, next: express.NextFunction) {
  const requests: RemoteQaduVideoRequest[] = req.body.data
  const fromPod = res.locals.secure.pod

  Bluebird.each(requests, request => {
    const videoData = request.data

    return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
  })
  .catch(err => logger.error('Error managing remote videos.', err))

  return res.type('json').status(204).end()
}

function remoteVideosEvents (req: express.Request, res: express.Response, next: express.NextFunction) {
  const requests: RemoteVideoEventRequest[] = req.body.data
  const fromPod = res.locals.secure.pod

  Bluebird.each(requests, request => {
    const eventData = request.data

    return processVideosEventsRetryWrapper(eventData, fromPod)
  })
  .catch(err => logger.error('Error managing remote videos.', err))

  return res.type('json').status(204).end()
}

async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) {
  const options = {
    arguments: [ eventData, fromPod ],
    errorMessage: 'Cannot process videos events with many retries.'
  }

  await retryTransactionWrapper(processVideosEvents, options)
}

async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
  await db.sequelize.transaction(async t => {
    const sequelizeOptions = { transaction: t }
    const videoInstance = await fetchLocalVideoByUUID(eventData.uuid, t)

    let columnToUpdate
    let qaduType

    switch (eventData.eventType) {
    case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
      columnToUpdate = 'views'
      qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
      break

    case REQUEST_VIDEO_EVENT_TYPES.LIKES:
      columnToUpdate = 'likes'
      qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
      break

    case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
      columnToUpdate = 'dislikes'
      qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
      break

    default:
      throw new Error('Unknown video event type.')
    }

    const query = {}
    query[columnToUpdate] = eventData.count

    await videoInstance.increment(query, sequelizeOptions)

    const qadusParams = [
      {
        videoId: videoInstance.id,
        type: qaduType
      }
    ]
    await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
  })

  logger.info('Remote video event processed for video with uuid %s.', eventData.uuid)
}

async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoData, fromPod ],
    errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
  }

  await retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
}

async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
  let videoUUID = ''

  await db.sequelize.transaction(async t => {
    const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
    const sequelizeOptions = { transaction: t }

    videoUUID = videoInstance.uuid

    if (videoData.views) {
      videoInstance.set('views', videoData.views)
    }

    if (videoData.likes) {
      videoInstance.set('likes', videoData.likes)
    }

    if (videoData.dislikes) {
      videoInstance.set('dislikes', videoData.dislikes)
    }

    await videoInstance.save(sequelizeOptions)
  })

  logger.info('Remote video with uuid %s quick and dirty updated', videoUUID)
}

// Handle retries on fail
async function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoToCreateData, fromPod ],
    errorMessage: 'Cannot insert the remote video with many retries.'
  }

  await retryTransactionWrapper(addRemoteVideo, options)
}

async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
  logger.debug('Adding remote video "%s".', videoToCreateData.uuid)

  await db.sequelize.transaction(async t => {
    const sequelizeOptions = {
      transaction: t
    }

    const videoFromDatabase = await db.Video.loadByUUID(videoToCreateData.uuid)
    if (videoFromDatabase) throw new Error('UUID already exists.')

    const videoChannel = await db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
    if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')

    const tags = videoToCreateData.tags
    const tagInstances = await db.Tag.findOrCreateTags(tags, t)

    const videoData = {
      name: videoToCreateData.name,
      uuid: videoToCreateData.uuid,
      category: videoToCreateData.category,
      licence: videoToCreateData.licence,
      language: videoToCreateData.language,
      nsfw: videoToCreateData.nsfw,
      description: videoToCreateData.truncatedDescription,
      channelId: videoChannel.id,
      duration: videoToCreateData.duration,
      createdAt: videoToCreateData.createdAt,
      // FIXME: updatedAt does not seems to be considered by Sequelize
      updatedAt: videoToCreateData.updatedAt,
      views: videoToCreateData.views,
      likes: videoToCreateData.likes,
      dislikes: videoToCreateData.dislikes,
      remote: true,
      privacy: videoToCreateData.privacy
    }

    const video = db.Video.build(videoData)
    await db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData)
    const videoCreated = await video.save(sequelizeOptions)

    const tasks = []
    for (const fileData of videoToCreateData.files) {
      const videoFileInstance = db.VideoFile.build({
        extname: fileData.extname,
        infoHash: fileData.infoHash,
        resolution: fileData.resolution,
        size: fileData.size,
        videoId: videoCreated.id
      })

      tasks.push(videoFileInstance.save(sequelizeOptions))
    }

    await Promise.all(tasks)

    await videoCreated.setTags(tagInstances, sequelizeOptions)
  })

  logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
}

// Handle retries on fail
async function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoAttributesToUpdate, fromPod ],
    errorMessage: 'Cannot update the remote video with many retries'
  }

  await retryTransactionWrapper(updateRemoteVideo, options)
}

async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
  logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
  let videoInstance: VideoInstance
  let videoFieldsSave: object

  try {
    await db.sequelize.transaction(async t => {
      const sequelizeOptions = {
        transaction: t
      }

      const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
      videoFieldsSave = videoInstance.toJSON()
      const tags = videoAttributesToUpdate.tags

      const tagInstances = await db.Tag.findOrCreateTags(tags, t)

      videoInstance.set('name', videoAttributesToUpdate.name)
      videoInstance.set('category', videoAttributesToUpdate.category)
      videoInstance.set('licence', videoAttributesToUpdate.licence)
      videoInstance.set('language', videoAttributesToUpdate.language)
      videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
      videoInstance.set('description', videoAttributesToUpdate.truncatedDescription)
      videoInstance.set('duration', videoAttributesToUpdate.duration)
      videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
      videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
      videoInstance.set('views', videoAttributesToUpdate.views)
      videoInstance.set('likes', videoAttributesToUpdate.likes)
      videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
      videoInstance.set('privacy', videoAttributesToUpdate.privacy)

      await videoInstance.save(sequelizeOptions)

      // Remove old video files
      const videoFileDestroyTasks: Bluebird<void>[] = []
      for (const videoFile of videoInstance.VideoFiles) {
        videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
      }
      await Promise.all(videoFileDestroyTasks)

      const videoFileCreateTasks: Bluebird<VideoFileInstance>[] = []
      for (const fileData of videoAttributesToUpdate.files) {
        const videoFileInstance = db.VideoFile.build({
          extname: fileData.extname,
          infoHash: fileData.infoHash,
          resolution: fileData.resolution,
          size: fileData.size,
          videoId: videoInstance.id
        })

        videoFileCreateTasks.push(videoFileInstance.save(sequelizeOptions))
      }

      await Promise.all(videoFileCreateTasks)

      await videoInstance.setTags(tagInstances, sequelizeOptions)
    })

    logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
  } catch (err) {
    if (videoInstance !== undefined && videoFieldsSave !== undefined) {
      resetSequelizeInstance(videoInstance, videoFieldsSave)
    }

    // This is just a debug because we will retry the insert
    logger.debug('Cannot update the remote video.', err)
    throw err
  }
}

async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoToRemoveData, fromPod ],
    errorMessage: 'Cannot remove the remote video channel with many retries.'
  }

  await retryTransactionWrapper(removeRemoteVideo, options)
}

async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
  logger.debug('Removing remote video "%s".', videoToRemoveData.uuid)

  await db.sequelize.transaction(async t => {
    // We need the instance because we have to remove some other stuffs (thumbnail etc)
    const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
    await videoInstance.destroy({ transaction: t })
  })

  logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid)
}

async function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
  const options = {
    arguments: [ authorToCreateData, fromPod ],
    errorMessage: 'Cannot insert the remote video author with many retries.'
  }

  await retryTransactionWrapper(addRemoteVideoAuthor, options)
}

async function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
  logger.debug('Adding remote video author "%s".', authorToCreateData.uuid)

  await db.sequelize.transaction(async t => {
    const authorInDatabase = await db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t)
    if (authorInDatabase) throw new Error('Author with UUID ' + authorToCreateData.uuid + ' already exists.')

    const videoAuthorData = {
      name: authorToCreateData.name,
      uuid: authorToCreateData.uuid,
      userId: null, // Not on our pod
      podId: fromPod.id
    }

    const author = db.Author.build(videoAuthorData)
    await author.save({ transaction: t })
  })

  logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid)
}

async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
  const options = {
    arguments: [ authorAttributesToRemove, fromPod ],
    errorMessage: 'Cannot remove the remote video author with many retries.'
  }

  await retryTransactionWrapper(removeRemoteVideoAuthor, options)
}

async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
  logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid)

  await db.sequelize.transaction(async t => {
    const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
    await videoAuthor.destroy({ transaction: t })
  })

  logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid)
}

async function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoChannelToCreateData, fromPod ],
    errorMessage: 'Cannot insert the remote video channel with many retries.'
  }

  await retryTransactionWrapper(addRemoteVideoChannel, options)
}

async function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
  logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)

  await db.sequelize.transaction(async t => {
    const videoChannelInDatabase = await db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid)
    if (videoChannelInDatabase) {
      throw new Error('Video channel with UUID ' + videoChannelToCreateData.uuid + ' already exists.')
    }

    const authorUUID = videoChannelToCreateData.ownerUUID
    const podId = fromPod.id

    const author = await db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t)
    if (!author) throw new Error('Unknown author UUID' + authorUUID + '.')

    const videoChannelData = {
      name: videoChannelToCreateData.name,
      description: videoChannelToCreateData.description,
      uuid: videoChannelToCreateData.uuid,
      createdAt: videoChannelToCreateData.createdAt,
      updatedAt: videoChannelToCreateData.updatedAt,
      remote: true,
      authorId: author.id
    }

    const videoChannel = db.VideoChannel.build(videoChannelData)
    await videoChannel.save({ transaction: t })
  })

  logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
}

async function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoChannelAttributesToUpdate, fromPod ],
    errorMessage: 'Cannot update the remote video channel with many retries.'
  }

  await retryTransactionWrapper(updateRemoteVideoChannel, options)
}

async function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
  logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid)

  await db.sequelize.transaction(async t => {
    const sequelizeOptions = { transaction: t }

    const videoChannelInstance = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t)
    videoChannelInstance.set('name', videoChannelAttributesToUpdate.name)
    videoChannelInstance.set('description', videoChannelAttributesToUpdate.description)
    videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt)
    videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt)

    await videoChannelInstance.save(sequelizeOptions)
  })

  logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid)
}

async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
  const options = {
    arguments: [ videoChannelAttributesToRemove, fromPod ],
    errorMessage: 'Cannot remove the remote video channel with many retries.'
  }

  await retryTransactionWrapper(removeRemoteVideoChannel, options)
}

async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
  logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid)

  await db.sequelize.transaction(async t => {
    const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t)
    await videoChannel.destroy({ transaction: t })
  })

  logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid)
}

async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
  const options = {
    arguments: [ reportData, fromPod ],
    errorMessage: 'Cannot create remote abuse video with many retries.'
  }

  await retryTransactionWrapper(reportAbuseRemoteVideo, options)
}

async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
  logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)

  await db.sequelize.transaction(async t => {
    const videoInstance = await fetchLocalVideoByUUID(reportData.videoUUID, t)
    const videoAbuseData = {
      reporterUsername: reportData.reporterUsername,
      reason: reportData.reportReason,
      reporterPodId: fromPod.id,
      videoId: videoInstance.id
    }

    await db.VideoAbuse.create(videoAbuseData)

  })

  logger.info('Remote abuse for video uuid %s created', reportData.videoUUID)
}

async function fetchLocalVideoByUUID (id: string, t: Sequelize.Transaction) {
  try {
    const video = await db.Video.loadLocalVideoByUUID(id, t)

    if (!video) throw new Error('Video ' + id + ' not found')

    return video
  } catch (err) {
    logger.error('Cannot load owned video from id.', { error: err.stack, id })
    throw err
  }
}

async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
  try {
    const video = await db.Video.loadByHostAndUUID(podHost, uuid, t)
    if (!video) throw new Error('Video not found')

    return video
  } catch (err) {
    logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
    throw err
  }
}