Server: propagate video update to other pods
authorChocobozzz <florian.bigard@gmail.com>
Fri, 30 Dec 2016 10:27:42 +0000 (11:27 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Fri, 30 Dec 2016 10:27:42 +0000 (11:27 +0100)
server/controllers/api/remote.js
server/controllers/api/videos.js
server/helpers/custom-validators/videos.js
server/models/video.js
server/tests/api/multiple-pods.js

index 929ade555872504e959f66e0728d4629f38fa574..254ae56d52b0fb63a075ba2b6276755c4bc78c89 100644 (file)
@@ -35,12 +35,21 @@ function remoteVideos (req, res, next) {
   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)
+    switch (request.type) {
+      case 'add':
+        addRemoteVideo(videoData, fromPod, callbackEach)
+        break
+
+      case 'update':
+        updateRemoteVideo(videoData, fromPod, callbackEach)
+        break
+
+      case 'remove':
+        removeRemoteVideo(videoData, fromPod, callbackEach)
+        break
+
+      default:
+        logger.error('Unkown remote request type %s.', request.type)
     }
   }, function (err) {
     if (err) logger.error('Error managing remote videos.', { error: err })
@@ -143,24 +152,85 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) {
   })
 }
 
-function removeRemoteVideo (videoToRemoveData, fromPod, callback) {
-  // TODO: use bulkDestroy?
+function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) {
+  logger.debug('Updating remote video "%s".', videoAttributesToUpdate.name)
 
-  // 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)
+  waterfall([
+
+    function startTransaction (callback) {
+      db.sequelize.transaction().asCallback(function (err, t) {
+        return callback(err, t)
+      })
+    },
+
+    function findVideo (t, callback) {
+      db.Video.loadByHostAndRemoteId(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
+        if (err || !videoInstance) {
+          logger.error('Cannot load video from host and remote id.', { error: err.message })
+          return callback(err)
+        }
+
+        return callback(null, t, videoInstance)
+      })
+    },
+
+    function findOrCreateTags (t, videoInstance, callback) {
+      const tags = videoAttributesToUpdate.tags
+
+      db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
+        return callback(err, t, videoInstance, tagInstances)
+      })
+    },
+
+    function updateVideoIntoDB (t, videoInstance, tagInstances, callback) {
+      const options = { transaction: t }
+
+      videoInstance.set('name', videoAttributesToUpdate.name)
+      videoInstance.set('description', videoAttributesToUpdate.description)
+      videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
+      videoInstance.set('duration', videoAttributesToUpdate.duration)
+      videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
+      videoInstance.set('extname', videoAttributesToUpdate.extname)
+
+      videoInstance.save(options).asCallback(function (err) {
+        return callback(err, t, videoInstance, tagInstances)
+      })
+    },
+
+    function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
+      const options = { transaction: t }
+
+      videoInstance.setTags(tagInstances, options).asCallback(function (err) {
+        return callback(err, t)
+      })
     }
 
-    if (videosList.length === 0) {
-      logger.error('No remote video was found for this pod.', { remoteId: videoToRemoveData.remoteId, podHost: fromPod.host })
+  ], function (err, t) {
+    if (err) {
+      logger.error('Cannot update the remote video.')
+
+      // Abort transaction?
+      if (t) t.rollback()
+
+      return finalCallback(err)
     }
 
-    each(videosList, function (video, callbackEach) {
-      logger.debug('Removing remote video %s.', video.remoteId)
+    // Commit transaction
+    t.commit()
+
+    return finalCallback()
+  })
+}
+
+function removeRemoteVideo (videoToRemoveData, fromPod, callback) {
+  // We need the instance because we have to remove some other stuffs (thumbnail etc)
+  db.Video.loadByHostAndRemoteId(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
+    if (err || !video) {
+      logger.error('Cannot load video from host and remote id.', { error: err.message })
+      return callback(err)
+    }
 
-      video.destroy().asCallback(callbackEach)
-    }, callback)
+    logger.debug('Removing remote video %s.', video.remoteId)
+    video.destroy().asCallback(callback)
   })
 }
index 1b306d1cf038644f09f77adcacb37c35e14f9f13..e5c52a87b4f0c9ba670b7f6f88efc8f2e39328b2 100644 (file)
@@ -229,8 +229,6 @@ function updateVideo (req, res, next) {
 
       // Add tags association
       videoInstance.save(options).asCallback(function (err) {
-        if (err) return callback(err)
-
         return callback(err, t, tagInstances)
       })
     },
index 4aaa6aaa99c37454457309b3e8651cc8ecef3117..b76eec1b5b72a8795697e70776766b435c1263ce 100644 (file)
@@ -37,6 +37,17 @@ function isEachRemoteVideosValid (requests) {
         isVideoRemoteIdValid(video.remoteId) &&
         isVideoExtnameValid(video.extname)
       ) ||
+      (
+        isRequestTypeUpdateValid(request.type) &&
+        isVideoDateValid(video.createdAt) &&
+        isVideoDescriptionValid(video.description) &&
+        isVideoDurationValid(video.duration) &&
+        isVideoInfoHashValid(video.infoHash) &&
+        isVideoNameValid(video.name) &&
+        isVideoTagsValid(video.tags) &&
+        isVideoRemoteIdValid(video.remoteId) &&
+        isVideoExtnameValid(video.extname)
+      ) ||
       (
         isRequestTypeRemoveValid(request.type) &&
         isVideoNameValid(video.name) &&
@@ -104,6 +115,10 @@ function isRequestTypeAddValid (value) {
   return value === 'add'
 }
 
+function isRequestTypeUpdateValid (value) {
+  return value === 'update'
+}
+
 function isRequestTypeRemoveValid (value) {
   return value === 'remove'
 }
index 14fbe2f719ab2ca79dafdf28b3d2235217ce52e7..f51d08f06f4a4e430c9cbb811d881af6c80e4e78 100644 (file)
@@ -111,10 +111,10 @@ module.exports = function (sequelize, DataTypes) {
         getDurationFromFile,
         list,
         listForApi,
-        listByHostAndRemoteId,
         listOwnedAndPopulateAuthorAndTags,
         listOwnedByAuthor,
         load,
+        loadByHostAndRemoteId,
         loadAndPopulateAuthor,
         loadAndPopulateAuthorAndPodAndTags,
         searchAndPopulateAuthorAndPodAndTags
@@ -428,7 +428,7 @@ function listForApi (start, count, sort, callback) {
   })
 }
 
-function listByHostAndRemoteId (fromHost, remoteId, callback) {
+function loadByHostAndRemoteId (fromHost, remoteId, callback) {
   const query = {
     where: {
       remoteId: remoteId
@@ -449,7 +449,7 @@ function listByHostAndRemoteId (fromHost, remoteId, callback) {
     ]
   }
 
-  return this.findAll(query).asCallback(callback)
+  return this.findOne(query).asCallback(callback)
 }
 
 function listOwnedAndPopulateAuthorAndTags (callback) {
index f0fe59c5fefb09466a6d46677e2a9eb7417a8eed..6721870682b6a9e1450f8cf458af594d11714b27 100644 (file)
@@ -299,8 +299,8 @@ describe('Test multiple pods', function () {
         if (err) throw err
 
         const video = res.body.data[0]
-        toRemove.push(res.body.data[2].id)
-        toRemove.push(res.body.data[3].id)
+        toRemove.push(res.body.data[2])
+        toRemove.push(res.body.data[3])
 
         webtorrent.add(video.magnetUri, function (torrent) {
           expect(torrent.files).to.exist
@@ -368,16 +368,51 @@ describe('Test multiple pods', function () {
         })
       })
     })
+  })
+
+  describe('Should manipulate these videos', function () {
+    it('Should update the video 3 by asking pod 3', function (done) {
+      this.timeout(15000)
+
+      const name = 'my super video updated'
+      const description = 'my super description updated'
+      const tags = [ 'tagup1', 'tagup2' ]
+
+      videosUtils.updateVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, name, description, tags, function (err) {
+        if (err) throw err
+
+        setTimeout(done, 11000)
+      })
+    })
+
+    it('Should have the video 3 updated on each pod', function (done) {
+      each(servers, function (server, callback) {
+        videosUtils.getVideosList(server.url, function (err, res) {
+          if (err) throw err
+
+          const videos = res.body.data
+          const videoUpdated = videos.find(function (video) {
+            return video.name === 'my super video updated'
+          })
+
+          expect(!!videoUpdated).to.be.true
+          expect(videoUpdated.description).to.equal('my super description updated')
+          expect(videoUpdated.tags).to.deep.equal([ 'tagup1', 'tagup2' ])
+
+          callback()
+        })
+      }, done)
+    })
 
-    it('Should remove the file 3 and 3-2 by asking pod 3', function (done) {
+    it('Should remove the videos 3 and 3-2 by asking pod 3', function (done) {
       this.timeout(15000)
 
       series([
         function (next) {
-          videosUtils.removeVideo(servers[2].url, servers[2].accessToken, toRemove[0], next)
+          videosUtils.removeVideo(servers[2].url, servers[2].accessToken, toRemove[0].id, next)
         },
         function (next) {
-          videosUtils.removeVideo(servers[2].url, servers[2].accessToken, toRemove[1], next)
+          videosUtils.removeVideo(servers[2].url, servers[2].accessToken, toRemove[1].id, next)
         }],
         function (err) {
           if (err) throw err
@@ -394,11 +429,11 @@ describe('Test multiple pods', function () {
           const videos = res.body.data
           expect(videos).to.be.an('array')
           expect(videos.length).to.equal(2)
-          expect(videos[0].id).not.to.equal(videos[1].id)
-          expect(videos[0].id).not.to.equal(toRemove[0])
-          expect(videos[1].id).not.to.equal(toRemove[0])
-          expect(videos[0].id).not.to.equal(toRemove[1])
-          expect(videos[1].id).not.to.equal(toRemove[1])
+          expect(videos[0].name).not.to.equal(videos[1].name)
+          expect(videos[0].name).not.to.equal(toRemove[0].name)
+          expect(videos[1].name).not.to.equal(toRemove[0].name)
+          expect(videos[0].name).not.to.equal(toRemove[1].name)
+          expect(videos[1].name).not.to.equal(toRemove[1].name)
 
           callback()
         })