aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/remote.js
blob: 929ade555872504e959f66e0728d4629f38fa574 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
'use strict'

const each = require('async/each')
const eachSeries = require('async/eachSeries')
const express = require('express')
const waterfall = require('async/waterfall')

const db = require('../../initializers/database')
const middlewares = require('../../middlewares')
const secureMiddleware = middlewares.secure
const validators = middlewares.validators.remote
const logger = require('../../helpers/logger')

const router = express.Router()

router.post('/videos',
  validators.signature,
  secureMiddleware.checkSignature,
  validators.remoteVideos,
  remoteVideos
)

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

module.exports = router

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

function remoteVideos (req, res, next) {
  const requests = req.body.data
  const fromPod = res.locals.secure.pod

  // We need to process in the same order to keep consistency
  // TODO: optimization
  eachSeries(requests, function (request, callbackEach) {
    const videoData = request.data

    if (request.type === 'add') {
      addRemoteVideo(videoData, fromPod, callbackEach)
    } else if (request.type === 'remove') {
      removeRemoteVideo(videoData, fromPod, callbackEach)
    } else {
      logger.error('Unkown remote request type %s.', request.type)
    }
  }, function (err) {
    if (err) logger.error('Error managing remote videos.', { error: err })
  })

  // We don't need to keep the other pod waiting
  return res.type('json').status(204).end()
}

function addRemoteVideo (videoToCreateData, fromPod, finalCallback) {
  logger.debug('Adding remote video "%s".', videoToCreateData.name)

  waterfall([

    function startTransaction (callback) {
      db.sequelize.transaction().asCallback(function (err, t) {
        return callback(err, t)
      })
    },

    function findOrCreateAuthor (t, callback) {
      const name = videoToCreateData.author
      const podId = fromPod.id
      // This author is from another pod so we do not associate a user
      const userId = null

      db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
        return callback(err, t, authorInstance)
      })
    },

    function findOrCreateTags (t, author, callback) {
      const tags = videoToCreateData.tags

      db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
        return callback(err, t, author, tagInstances)
      })
    },

    function createVideoObject (t, author, tagInstances, callback) {
      const videoData = {
        name: videoToCreateData.name,
        remoteId: videoToCreateData.remoteId,
        extname: videoToCreateData.extname,
        infoHash: videoToCreateData.infoHash,
        description: videoToCreateData.description,
        authorId: author.id,
        duration: videoToCreateData.duration,
        createdAt: videoToCreateData.createdAt
      }

      const video = db.Video.build(videoData)

      return callback(null, t, tagInstances, video)
    },

    function generateThumbnail (t, tagInstances, video, callback) {
      db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
        if (err) {
          logger.error('Cannot generate thumbnail from data.', { error: err })
          return callback(err)
        }

        return callback(err, t, tagInstances, video)
      })
    },

    function insertVideoIntoDB (t, tagInstances, video, callback) {
      const options = {
        transaction: t
      }

      video.save(options).asCallback(function (err, videoCreated) {
        return callback(err, t, tagInstances, videoCreated)
      })
    },

    function associateTagsToVideo (t, tagInstances, video, callback) {
      const options = { transaction: t }

      video.setTags(tagInstances, options).asCallback(function (err) {
        return callback(err, t)
      })
    }

  ], function (err, t) {
    if (err) {
      logger.error('Cannot insert the remote video.')

      // Abort transaction?
      if (t) t.rollback()

      return finalCallback(err)
    }

    // Commit transaction
    t.commit()

    return finalCallback()
  })
}

function removeRemoteVideo (videoToRemoveData, fromPod, callback) {
  // TODO: use bulkDestroy?

  // We need the list because we have to remove some other stuffs (thumbnail etc)
  db.Video.listByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, videosList) {
    if (err) {
      logger.error('Cannot list videos from host and remote id.', { error: err.message })
      return callback(err)
    }

    if (videosList.length === 0) {
      logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromPod.host })
    }

    each(videosList, function (video, callbackEach) {
      logger.debug('Removing remote video %s.', video.remoteId)

      video.destroy().asCallback(callbackEach)
    }, callback)
  })
}