diff options
Diffstat (limited to 'server/controllers/api/videos.js')
-rw-r--r-- | server/controllers/api/videos.js | 114 |
1 files changed, 77 insertions, 37 deletions
diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 6829804ec..4d45c11c0 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js | |||
@@ -70,13 +70,13 @@ router.put('/:id', | |||
70 | oAuth.authenticate, | 70 | oAuth.authenticate, |
71 | reqFiles, | 71 | reqFiles, |
72 | validatorsVideos.videosUpdate, | 72 | validatorsVideos.videosUpdate, |
73 | updateVideo | 73 | updateVideoRetryWrapper |
74 | ) | 74 | ) |
75 | router.post('/', | 75 | router.post('/', |
76 | oAuth.authenticate, | 76 | oAuth.authenticate, |
77 | reqFiles, | 77 | reqFiles, |
78 | validatorsVideos.videosAdd, | 78 | validatorsVideos.videosAdd, |
79 | addVideo | 79 | addVideoRetryWrapper |
80 | ) | 80 | ) |
81 | router.get('/:id', | 81 | router.get('/:id', |
82 | validatorsVideos.videosGet, | 82 | validatorsVideos.videosGet, |
@@ -103,19 +103,37 @@ module.exports = router | |||
103 | 103 | ||
104 | // --------------------------------------------------------------------------- | 104 | // --------------------------------------------------------------------------- |
105 | 105 | ||
106 | function addVideo (req, res, next) { | 106 | // Wrapper to video add that retry the function if there is a database error |
107 | const videoFile = req.files.videofile[0] | 107 | // We need this because we run the transaction in SERIALIZABLE isolation that can fail |
108 | function addVideoRetryWrapper (req, res, next) { | ||
109 | utils.transactionRetryer( | ||
110 | function (callback) { | ||
111 | return addVideo(req, res, req.files.videofile[0], callback) | ||
112 | }, | ||
113 | function (err) { | ||
114 | if (err) { | ||
115 | logger.error('Cannot insert the video with many retries.', { error: err }) | ||
116 | return next(err) | ||
117 | } | ||
118 | |||
119 | // TODO : include Location of the new video -> 201 | ||
120 | return res.type('json').status(204).end() | ||
121 | } | ||
122 | ) | ||
123 | } | ||
124 | |||
125 | function addVideo (req, res, videoFile, callback) { | ||
108 | const videoInfos = req.body | 126 | const videoInfos = req.body |
109 | 127 | ||
110 | waterfall([ | 128 | waterfall([ |
111 | 129 | ||
112 | function startTransaction (callback) { | 130 | function startTransaction (callbackWaterfall) { |
113 | db.sequelize.transaction().asCallback(function (err, t) { | 131 | db.sequelize.transaction({ isolationLevel: 'SERIALIZABLE' }).asCallback(function (err, t) { |
114 | return callback(err, t) | 132 | return callbackWaterfall(err, t) |
115 | }) | 133 | }) |
116 | }, | 134 | }, |
117 | 135 | ||
118 | function findOrCreateAuthor (t, callback) { | 136 | function findOrCreateAuthor (t, callbackWaterfall) { |
119 | const user = res.locals.oauth.token.User | 137 | const user = res.locals.oauth.token.User |
120 | 138 | ||
121 | const name = user.username | 139 | const name = user.username |
@@ -124,19 +142,19 @@ function addVideo (req, res, next) { | |||
124 | const userId = user.id | 142 | const userId = user.id |
125 | 143 | ||
126 | db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { | 144 | db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { |
127 | return callback(err, t, authorInstance) | 145 | return callbackWaterfall(err, t, authorInstance) |
128 | }) | 146 | }) |
129 | }, | 147 | }, |
130 | 148 | ||
131 | function findOrCreateTags (t, author, callback) { | 149 | function findOrCreateTags (t, author, callbackWaterfall) { |
132 | const tags = videoInfos.tags | 150 | const tags = videoInfos.tags |
133 | 151 | ||
134 | db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { | 152 | db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { |
135 | return callback(err, t, author, tagInstances) | 153 | return callbackWaterfall(err, t, author, tagInstances) |
136 | }) | 154 | }) |
137 | }, | 155 | }, |
138 | 156 | ||
139 | function createVideoObject (t, author, tagInstances, callback) { | 157 | function createVideoObject (t, author, tagInstances, callbackWaterfall) { |
140 | const videoData = { | 158 | const videoData = { |
141 | name: videoInfos.name, | 159 | name: videoInfos.name, |
142 | remoteId: null, | 160 | remoteId: null, |
@@ -148,74 +166,97 @@ function addVideo (req, res, next) { | |||
148 | 166 | ||
149 | const video = db.Video.build(videoData) | 167 | const video = db.Video.build(videoData) |
150 | 168 | ||
151 | return callback(null, t, author, tagInstances, video) | 169 | return callbackWaterfall(null, t, author, tagInstances, video) |
152 | }, | 170 | }, |
153 | 171 | ||
154 | // Set the videoname the same as the id | 172 | // Set the videoname the same as the id |
155 | function renameVideoFile (t, author, tagInstances, video, callback) { | 173 | function renameVideoFile (t, author, tagInstances, video, callbackWaterfall) { |
156 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR | 174 | const videoDir = constants.CONFIG.STORAGE.VIDEOS_DIR |
157 | const source = path.join(videoDir, videoFile.filename) | 175 | const source = path.join(videoDir, videoFile.filename) |
158 | const destination = path.join(videoDir, video.getVideoFilename()) | 176 | const destination = path.join(videoDir, video.getVideoFilename()) |
159 | 177 | ||
160 | fs.rename(source, destination, function (err) { | 178 | fs.rename(source, destination, function (err) { |
161 | return callback(err, t, author, tagInstances, video) | 179 | if (err) return callbackWaterfall(err) |
180 | |||
181 | // This is important in case if there is another attempt | ||
182 | videoFile.filename = video.getVideoFilename() | ||
183 | return callbackWaterfall(null, t, author, tagInstances, video) | ||
162 | }) | 184 | }) |
163 | }, | 185 | }, |
164 | 186 | ||
165 | function insertVideoIntoDB (t, author, tagInstances, video, callback) { | 187 | function insertVideoIntoDB (t, author, tagInstances, video, callbackWaterfall) { |
166 | const options = { transaction: t } | 188 | const options = { transaction: t } |
167 | 189 | ||
168 | // Add tags association | 190 | // Add tags association |
169 | video.save(options).asCallback(function (err, videoCreated) { | 191 | video.save(options).asCallback(function (err, videoCreated) { |
170 | if (err) return callback(err) | 192 | if (err) return callbackWaterfall(err) |
171 | 193 | ||
172 | // Do not forget to add Author informations to the created video | 194 | // Do not forget to add Author informations to the created video |
173 | videoCreated.Author = author | 195 | videoCreated.Author = author |
174 | 196 | ||
175 | return callback(err, t, tagInstances, videoCreated) | 197 | return callbackWaterfall(err, t, tagInstances, videoCreated) |
176 | }) | 198 | }) |
177 | }, | 199 | }, |
178 | 200 | ||
179 | function associateTagsToVideo (t, tagInstances, video, callback) { | 201 | function associateTagsToVideo (t, tagInstances, video, callbackWaterfall) { |
180 | const options = { transaction: t } | 202 | const options = { transaction: t } |
181 | 203 | ||
182 | video.setTags(tagInstances, options).asCallback(function (err) { | 204 | video.setTags(tagInstances, options).asCallback(function (err) { |
183 | video.Tags = tagInstances | 205 | video.Tags = tagInstances |
184 | 206 | ||
185 | return callback(err, t, video) | 207 | return callbackWaterfall(err, t, video) |
186 | }) | 208 | }) |
187 | }, | 209 | }, |
188 | 210 | ||
189 | function sendToFriends (t, video, callback) { | 211 | function sendToFriends (t, video, callbackWaterfall) { |
190 | video.toAddRemoteJSON(function (err, remoteVideo) { | 212 | video.toAddRemoteJSON(function (err, remoteVideo) { |
191 | if (err) return callback(err) | 213 | if (err) return callbackWaterfall(err) |
192 | 214 | ||
193 | // Now we'll add the video's meta data to our friends | 215 | // Now we'll add the video's meta data to our friends |
194 | friends.addVideoToFriends(remoteVideo) | 216 | friends.addVideoToFriends(remoteVideo, t, function (err) { |
195 | 217 | return callbackWaterfall(err, t) | |
196 | return callback(null, t) | 218 | }) |
197 | }) | 219 | }) |
198 | } | 220 | } |
199 | 221 | ||
200 | ], function andFinally (err, t) { | 222 | ], function andFinally (err, t) { |
201 | if (err) { | 223 | if (err) { |
202 | logger.error('Cannot insert the video.') | 224 | // This is just a debug because we will retry the insert |
225 | logger.debug('Cannot insert the video.', { error: err }) | ||
203 | 226 | ||
204 | // Abort transaction? | 227 | // Abort transaction? |
205 | if (t) t.rollback() | 228 | if (t) t.rollback() |
206 | 229 | ||
207 | return next(err) | 230 | return callback(err) |
208 | } | 231 | } |
209 | 232 | ||
210 | // Commit transaction | 233 | // Commit transaction |
211 | t.commit() | 234 | t.commit() |
212 | 235 | ||
213 | // TODO : include Location of the new video -> 201 | 236 | logger.info('Video with name %s created.', videoInfos.name) |
214 | return res.type('json').status(204).end() | 237 | |
238 | return callback(null) | ||
215 | }) | 239 | }) |
216 | } | 240 | } |
217 | 241 | ||
218 | function updateVideo (req, res, next) { | 242 | function updateVideoRetryWrapper (req, res, next) { |
243 | utils.transactionRetryer( | ||
244 | function (callback) { | ||
245 | return updateVideo(req, res, callback) | ||
246 | }, | ||
247 | function (err) { | ||
248 | if (err) { | ||
249 | logger.error('Cannot update the video with many retries.', { error: err }) | ||
250 | return next(err) | ||
251 | } | ||
252 | |||
253 | // TODO : include Location of the new video -> 201 | ||
254 | return res.type('json').status(204).end() | ||
255 | } | ||
256 | ) | ||
257 | } | ||
258 | |||
259 | function updateVideo (req, res, finalCallback) { | ||
219 | const videoInstance = res.locals.video | 260 | const videoInstance = res.locals.video |
220 | const videoInfosToUpdate = req.body | 261 | const videoInfosToUpdate = req.body |
221 | 262 | ||
@@ -267,26 +308,25 @@ function updateVideo (req, res, next) { | |||
267 | const json = videoInstance.toUpdateRemoteJSON() | 308 | const json = videoInstance.toUpdateRemoteJSON() |
268 | 309 | ||
269 | // Now we'll update the video's meta data to our friends | 310 | // Now we'll update the video's meta data to our friends |
270 | friends.updateVideoToFriends(json) | 311 | friends.updateVideoToFriends(json, t, function (err) { |
271 | 312 | return callback(err, t) | |
272 | return callback(null, t) | 313 | }) |
273 | } | 314 | } |
274 | 315 | ||
275 | ], function andFinally (err, t) { | 316 | ], function andFinally (err, t) { |
276 | if (err) { | 317 | if (err) { |
277 | logger.error('Cannot insert the video.') | 318 | logger.debug('Cannot update the video.', { error: err }) |
278 | 319 | ||
279 | // Abort transaction? | 320 | // Abort transaction? |
280 | if (t) t.rollback() | 321 | if (t) t.rollback() |
281 | 322 | ||
282 | return next(err) | 323 | return finalCallback(err) |
283 | } | 324 | } |
284 | 325 | ||
285 | // Commit transaction | 326 | // Commit transaction |
286 | t.commit() | 327 | t.commit() |
287 | 328 | ||
288 | // TODO : include Location of the new video -> 201 | 329 | return finalCallback(null) |
289 | return res.type('json').status(204).end() | ||
290 | }) | 330 | }) |
291 | } | 331 | } |
292 | 332 | ||