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.ts528
1 files changed, 204 insertions, 324 deletions
diff --git a/server/controllers/api/remote/videos.ts b/server/controllers/api/remote/videos.ts
index d9cc08fb4..ebe4eca36 100644
--- a/server/controllers/api/remote/videos.ts
+++ b/server/controllers/api/remote/videos.ts
@@ -1,6 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as Sequelize from 'sequelize' 2import * as Promise from 'bluebird'
3import { eachSeries, waterfall } from 'async'
4 3
5import { database as db } from '../../../initializers/database' 4import { database as db } from '../../../initializers/database'
6import { 5import {
@@ -16,20 +15,14 @@ import {
16 remoteQaduVideosValidator, 15 remoteQaduVideosValidator,
17 remoteEventsVideosValidator 16 remoteEventsVideosValidator
18} from '../../../middlewares' 17} from '../../../middlewares'
19import { 18import { logger, retryTransactionWrapper } from '../../../helpers'
20 logger,
21 commitTransaction,
22 retryTransactionWrapper,
23 rollbackTransaction,
24 startSerializableTransaction
25} from '../../../helpers'
26import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib' 19import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
27import { PodInstance, VideoInstance } from '../../../models' 20import { PodInstance, VideoInstance } from '../../../models'
28 21
29const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] 22const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
30 23
31// Functions to call when processing a remote request 24// Functions to call when processing a remote request
32const functionsHash = {} 25const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
33functionsHash[ENDPOINT_ACTIONS.ADD] = addRemoteVideoRetryWrapper 26functionsHash[ENDPOINT_ACTIONS.ADD] = addRemoteVideoRetryWrapper
34functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper 27functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper
35functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo 28functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo
@@ -72,20 +65,19 @@ function remoteVideos (req: express.Request, res: express.Response, next: expres
72 65
73 // We need to process in the same order to keep consistency 66 // We need to process in the same order to keep consistency
74 // TODO: optimization 67 // TODO: optimization
75 eachSeries(requests, function (request: any, callbackEach) { 68 Promise.mapSeries(requests, (request: any) => {
76 const data = request.data 69 const data = request.data
77 70
78 // Get the function we need to call in order to process the request 71 // Get the function we need to call in order to process the request
79 const fun = functionsHash[request.type] 72 const fun = functionsHash[request.type]
80 if (fun === undefined) { 73 if (fun === undefined) {
81 logger.error('Unkown remote request type %s.', request.type) 74 logger.error('Unkown remote request type %s.', request.type)
82 return callbackEach(null) 75 return
83 } 76 }
84 77
85 fun.call(this, data, fromPod, callbackEach) 78 return fun.call(this, data, fromPod)
86 }, function (err) {
87 if (err) logger.error('Error managing remote videos.', { error: err })
88 }) 79 })
80 .catch(err => logger.error('Error managing remote videos.', { error: err }))
89 81
90 // We don't need to keep the other pod waiting 82 // We don't need to keep the other pod waiting
91 return res.type('json').status(204).end() 83 return res.type('json').status(204).end()
@@ -95,13 +87,12 @@ function remoteVideosQadu (req: express.Request, res: express.Response, next: ex
95 const requests = req.body.data 87 const requests = req.body.data
96 const fromPod = res.locals.secure.pod 88 const fromPod = res.locals.secure.pod
97 89
98 eachSeries(requests, function (request: any, callbackEach) { 90 Promise.mapSeries(requests, (request: any) => {
99 const videoData = request.data 91 const videoData = request.data
100 92
101 quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod, callbackEach) 93 return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
102 }, function (err) {
103 if (err) logger.error('Error managing remote videos.', { error: err })
104 }) 94 })
95 .catch(err => logger.error('Error managing remote videos.', { error: err }))
105 96
106 return res.type('json').status(204).end() 97 return res.type('json').status(204).end()
107} 98}
@@ -110,414 +101,303 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
110 const requests = req.body.data 101 const requests = req.body.data
111 const fromPod = res.locals.secure.pod 102 const fromPod = res.locals.secure.pod
112 103
113 eachSeries(requests, function (request: any, callbackEach) { 104 Promise.mapSeries(requests, (request: any) => {
114 const eventData = request.data 105 const eventData = request.data
115 106
116 processVideosEventsRetryWrapper(eventData, fromPod, callbackEach) 107 return processVideosEventsRetryWrapper(eventData, fromPod)
117 }, function (err) {
118 if (err) logger.error('Error managing remote videos.', { error: err })
119 }) 108 })
109 .catch(err => logger.error('Error managing remote videos.', { error: err }))
120 110
121 return res.type('json').status(204).end() 111 return res.type('json').status(204).end()
122} 112}
123 113
124function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 114function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance) {
125 const options = { 115 const options = {
126 arguments: [ eventData, fromPod ], 116 arguments: [ eventData, fromPod ],
127 errorMessage: 'Cannot process videos events with many retries.' 117 errorMessage: 'Cannot process videos events with many retries.'
128 } 118 }
129 119
130 retryTransactionWrapper(processVideosEvents, options, finalCallback) 120 return retryTransactionWrapper(processVideosEvents, options)
131} 121}
132 122
133function processVideosEvents (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 123function processVideosEvents (eventData: any, fromPod: PodInstance) {
134 waterfall([
135 startSerializableTransaction,
136
137 function findVideo (t, callback) {
138 fetchOwnedVideo(eventData.remoteId, function (err, videoInstance) {
139 return callback(err, t, videoInstance)
140 })
141 },
142 124
143 function updateVideoIntoDB (t, videoInstance, callback) { 125 return db.sequelize.transaction(t => {
144 const options = { transaction: t } 126 return fetchOwnedVideo(eventData.remoteId)
127 .then(videoInstance => {
128 const options = { transaction: t }
145 129
146 let columnToUpdate 130 let columnToUpdate
147 let qaduType 131 let qaduType
148 132
149 switch (eventData.eventType) { 133 switch (eventData.eventType) {
150 case REQUEST_VIDEO_EVENT_TYPES.VIEWS: 134 case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
151 columnToUpdate = 'views' 135 columnToUpdate = 'views'
152 qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS 136 qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
153 break 137 break
154 138
155 case REQUEST_VIDEO_EVENT_TYPES.LIKES: 139 case REQUEST_VIDEO_EVENT_TYPES.LIKES:
156 columnToUpdate = 'likes' 140 columnToUpdate = 'likes'
157 qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES 141 qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
158 break 142 break
159 143
160 case REQUEST_VIDEO_EVENT_TYPES.DISLIKES: 144 case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
161 columnToUpdate = 'dislikes' 145 columnToUpdate = 'dislikes'
162 qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES 146 qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
163 break 147 break
164 148
165 default: 149 default:
166 return callback(new Error('Unknown video event type.')) 150 throw new Error('Unknown video event type.')
167 } 151 }
168 152
169 const query = {} 153 const query = {}
170 query[columnToUpdate] = eventData.count 154 query[columnToUpdate] = eventData.count
171 155
172 videoInstance.increment(query, options).asCallback(function (err) { 156 return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
173 return callback(err, t, videoInstance, qaduType)
174 }) 157 })
175 }, 158 .then(({ videoInstance, qaduType }) => {
176 159 const qadusParams = [
177 function sendQaduToFriends (t, videoInstance, qaduType, callback) { 160 {
178 const qadusParams = [ 161 videoId: videoInstance.id,
179 { 162 type: qaduType
180 videoId: videoInstance.id, 163 }
181 type: qaduType 164 ]
182 } 165
183 ] 166 return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
184
185 quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
186 return callback(err, t)
187 }) 167 })
188 }, 168 })
189 169 .then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId))
190 commitTransaction 170 .catch(err => {
191 171 logger.debug('Cannot process a video event.', { error: err })
192 ], function (err: Error, t: Sequelize.Transaction) { 172 throw err
193 if (err) {
194 logger.debug('Cannot process a video event.', { error: err })
195 return rollbackTransaction(err, t, finalCallback)
196 }
197
198 logger.info('Remote video event processed for video %s.', eventData.remoteId)
199 return finalCallback(null)
200 }) 173 })
201} 174}
202 175
203function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 176function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance) {
204 const options = { 177 const options = {
205 arguments: [ videoData, fromPod ], 178 arguments: [ videoData, fromPod ],
206 errorMessage: 'Cannot update quick and dirty the remote video with many retries.' 179 errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
207 } 180 }
208 181
209 retryTransactionWrapper(quickAndDirtyUpdateVideo, options, finalCallback) 182 return retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
210} 183}
211 184
212function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 185function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance) {
213 let videoName 186 let videoName
214 187
215 waterfall([ 188 return db.sequelize.transaction(t => {
216 startSerializableTransaction, 189 return fetchRemoteVideo(fromPod.host, videoData.remoteId)
217 190 .then(videoInstance => {
218 function findVideo (t, callback) { 191 const options = { transaction: t }
219 fetchRemoteVideo(fromPod.host, videoData.remoteId, function (err, videoInstance) {
220 return callback(err, t, videoInstance)
221 })
222 },
223
224 function updateVideoIntoDB (t, videoInstance, callback) {
225 const options = { transaction: t }
226 192
227 videoName = videoInstance.name 193 videoName = videoInstance.name
228 194
229 if (videoData.views) { 195 if (videoData.views) {
230 videoInstance.set('views', videoData.views) 196 videoInstance.set('views', videoData.views)
231 } 197 }
232 198
233 if (videoData.likes) { 199 if (videoData.likes) {
234 videoInstance.set('likes', videoData.likes) 200 videoInstance.set('likes', videoData.likes)
235 } 201 }
236 202
237 if (videoData.dislikes) { 203 if (videoData.dislikes) {
238 videoInstance.set('dislikes', videoData.dislikes) 204 videoInstance.set('dislikes', videoData.dislikes)
239 } 205 }
240 206
241 videoInstance.save(options).asCallback(function (err) { 207 return videoInstance.save(options)
242 return callback(err, t)
243 }) 208 })
244 },
245
246 commitTransaction
247
248 ], function (err: Error, t: Sequelize.Transaction) {
249 if (err) {
250 logger.debug('Cannot quick and dirty update the remote video.', { error: err })
251 return rollbackTransaction(err, t, finalCallback)
252 }
253
254 logger.info('Remote video %s quick and dirty updated', videoName)
255 return finalCallback(null)
256 }) 209 })
210 .then(() => logger.info('Remote video %s quick and dirty updated', videoName))
211 .catch(err => logger.debug('Cannot quick and dirty update the remote video.', { error: err }))
257} 212}
258 213
259// Handle retries on fail 214// Handle retries on fail
260function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 215function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance) {
261 const options = { 216 const options = {
262 arguments: [ videoToCreateData, fromPod ], 217 arguments: [ videoToCreateData, fromPod ],
263 errorMessage: 'Cannot insert the remote video with many retries.' 218 errorMessage: 'Cannot insert the remote video with many retries.'
264 } 219 }
265 220
266 retryTransactionWrapper(addRemoteVideo, options, finalCallback) 221 return retryTransactionWrapper(addRemoteVideo, options)
267} 222}
268 223
269function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 224function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance) {
270 logger.debug('Adding remote video "%s".', videoToCreateData.remoteId) 225 logger.debug('Adding remote video "%s".', videoToCreateData.remoteId)
271 226
272 waterfall([ 227 return db.sequelize.transaction(t => {
273 228 return db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId)
274 startSerializableTransaction, 229 .then(video => {
275 230 if (video) throw new Error('RemoteId and host pair is not unique.')
276 function assertRemoteIdAndHostUnique (t, callback) {
277 db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId, function (err, video) {
278 if (err) return callback(err)
279 231
280 if (video) return callback(new Error('RemoteId and host pair is not unique.')) 232 return undefined
281
282 return callback(null, t)
283 }) 233 })
284 }, 234 .then(() => {
285 235 const name = videoToCreateData.author
286 function findOrCreateAuthor (t, callback) { 236 const podId = fromPod.id
287 const name = videoToCreateData.author 237 // This author is from another pod so we do not associate a user
288 const podId = fromPod.id 238 const userId = null
289 // This author is from another pod so we do not associate a user
290 const userId = null
291 239
292 db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) { 240 return db.Author.findOrCreateAuthor(name, podId, userId, t)
293 return callback(err, t, authorInstance)
294 }) 241 })
295 }, 242 .then(author => {
243 const tags = videoToCreateData.tags
296 244
297 function findOrCreateTags (t, author, callback) { 245 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
298 const tags = videoToCreateData.tags
299
300 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
301 return callback(err, t, author, tagInstances)
302 }) 246 })
303 }, 247 .then(({ author, tagInstances }) => {
304 248 const videoData = {
305 function createVideoObject (t, author, tagInstances, callback) { 249 name: videoToCreateData.name,
306 const videoData = { 250 remoteId: videoToCreateData.remoteId,
307 name: videoToCreateData.name, 251 extname: videoToCreateData.extname,
308 remoteId: videoToCreateData.remoteId, 252 infoHash: videoToCreateData.infoHash,
309 extname: videoToCreateData.extname, 253 category: videoToCreateData.category,
310 infoHash: videoToCreateData.infoHash, 254 licence: videoToCreateData.licence,
311 category: videoToCreateData.category, 255 language: videoToCreateData.language,
312 licence: videoToCreateData.licence, 256 nsfw: videoToCreateData.nsfw,
313 language: videoToCreateData.language, 257 description: videoToCreateData.description,
314 nsfw: videoToCreateData.nsfw, 258 authorId: author.id,
315 description: videoToCreateData.description, 259 duration: videoToCreateData.duration,
316 authorId: author.id, 260 createdAt: videoToCreateData.createdAt,
317 duration: videoToCreateData.duration, 261 // FIXME: updatedAt does not seems to be considered by Sequelize
318 createdAt: videoToCreateData.createdAt, 262 updatedAt: videoToCreateData.updatedAt,
319 // FIXME: updatedAt does not seems to be considered by Sequelize 263 views: videoToCreateData.views,
320 updatedAt: videoToCreateData.updatedAt, 264 likes: videoToCreateData.likes,
321 views: videoToCreateData.views, 265 dislikes: videoToCreateData.dislikes
322 likes: videoToCreateData.likes,
323 dislikes: videoToCreateData.dislikes
324 }
325
326 const video = db.Video.build(videoData)
327
328 return callback(null, t, tagInstances, video)
329 },
330
331 function generateThumbnail (t, tagInstances, video, callback) {
332 db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
333 if (err) {
334 logger.error('Cannot generate thumbnail from data.', { error: err })
335 return callback(err)
336 } 266 }
337 267
338 return callback(err, t, tagInstances, video) 268 const video = db.Video.build(videoData)
269 return { tagInstances, video }
339 }) 270 })
340 }, 271 .then(({ tagInstances, video }) => {
341 272 return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
342 function insertVideoIntoDB (t, tagInstances, video, callback) {
343 const options = {
344 transaction: t
345 }
346
347 video.save(options).asCallback(function (err, videoCreated) {
348 return callback(err, t, tagInstances, videoCreated)
349 }) 273 })
350 }, 274 .then(({ tagInstances, video }) => {
351 275 const options = {
352 function associateTagsToVideo (t, tagInstances, video, callback) { 276 transaction: t
353 const options = { 277 }
354 transaction: t
355 }
356 278
357 video.setTags(tagInstances, options).asCallback(function (err) { 279 return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
358 return callback(err, t)
359 }) 280 })
360 }, 281 .then(({ tagInstances, videoCreated }) => {
361 282 const options = {
362 commitTransaction 283 transaction: t
363 284 }
364 ], function (err: Error, t: Sequelize.Transaction) {
365 if (err) {
366 // This is just a debug because we will retry the insert
367 logger.debug('Cannot insert the remote video.', { error: err })
368 return rollbackTransaction(err, t, finalCallback)
369 }
370 285
371 logger.info('Remote video %s inserted.', videoToCreateData.name) 286 return videoCreated.setTags(tagInstances, options)
372 return finalCallback(null) 287 })
288 })
289 .then(() => logger.info('Remote video %s inserted.', videoToCreateData.name))
290 .catch(err => {
291 logger.debug('Cannot insert the remote video.', { error: err })
292 throw err
373 }) 293 })
374} 294}
375 295
376// Handle retries on fail 296// Handle retries on fail
377function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 297function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance) {
378 const options = { 298 const options = {
379 arguments: [ videoAttributesToUpdate, fromPod ], 299 arguments: [ videoAttributesToUpdate, fromPod ],
380 errorMessage: 'Cannot update the remote video with many retries' 300 errorMessage: 'Cannot update the remote video with many retries'
381 } 301 }
382 302
383 retryTransactionWrapper(updateRemoteVideo, options, finalCallback) 303 return retryTransactionWrapper(updateRemoteVideo, options)
384} 304}
385 305
386function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) { 306function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance) {
387 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId) 307 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId)
388 308
389 waterfall([ 309 return db.sequelize.transaction(t => {
310 return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId)
311 .then(videoInstance => {
312 const tags = videoAttributesToUpdate.tags
390 313
391 startSerializableTransaction, 314 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
392
393 function findVideo (t, callback) {
394 fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
395 return callback(err, t, videoInstance)
396 }) 315 })
397 }, 316 .then(({ videoInstance, tagInstances }) => {
398 317 const options = { transaction: t }
399 function findOrCreateTags (t, videoInstance, callback) { 318
400 const tags = videoAttributesToUpdate.tags 319 videoInstance.set('name', videoAttributesToUpdate.name)
401 320 videoInstance.set('category', videoAttributesToUpdate.category)
402 db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) { 321 videoInstance.set('licence', videoAttributesToUpdate.licence)
403 return callback(err, t, videoInstance, tagInstances) 322 videoInstance.set('language', videoAttributesToUpdate.language)
404 }) 323 videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
405 }, 324 videoInstance.set('description', videoAttributesToUpdate.description)
406 325 videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
407 function updateVideoIntoDB (t, videoInstance, tagInstances, callback) { 326 videoInstance.set('duration', videoAttributesToUpdate.duration)
408 const options = { transaction: t } 327 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
409 328 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
410 videoInstance.set('name', videoAttributesToUpdate.name) 329 videoInstance.set('extname', videoAttributesToUpdate.extname)
411 videoInstance.set('category', videoAttributesToUpdate.category) 330 videoInstance.set('views', videoAttributesToUpdate.views)
412 videoInstance.set('licence', videoAttributesToUpdate.licence) 331 videoInstance.set('likes', videoAttributesToUpdate.likes)
413 videoInstance.set('language', videoAttributesToUpdate.language) 332 videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
414 videoInstance.set('nsfw', videoAttributesToUpdate.nsfw) 333
415 videoInstance.set('description', videoAttributesToUpdate.description) 334 return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
416 videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
417 videoInstance.set('duration', videoAttributesToUpdate.duration)
418 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
419 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
420 videoInstance.set('extname', videoAttributesToUpdate.extname)
421 videoInstance.set('views', videoAttributesToUpdate.views)
422 videoInstance.set('likes', videoAttributesToUpdate.likes)
423 videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
424
425 videoInstance.save(options).asCallback(function (err) {
426 return callback(err, t, videoInstance, tagInstances)
427 }) 335 })
428 }, 336 .then(({ videoInstance, tagInstances }) => {
429 337 const options = { transaction: t }
430 function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
431 const options = { transaction: t }
432 338
433 videoInstance.setTags(tagInstances, options).asCallback(function (err) { 339 return videoInstance.setTags(tagInstances, options)
434 return callback(err, t)
435 }) 340 })
436 }, 341 })
437 342 .then(() => logger.info('Remote video %s updated', videoAttributesToUpdate.name))
438 commitTransaction 343 .catch(err => {
439 344 // This is just a debug because we will retry the insert
440 ], function (err: Error, t: Sequelize.Transaction) { 345 logger.debug('Cannot update the remote video.', { error: err })
441 if (err) { 346 throw err
442 // This is just a debug because we will retry the insert
443 logger.debug('Cannot update the remote video.', { error: err })
444 return rollbackTransaction(err, t, finalCallback)
445 }
446
447 logger.info('Remote video %s updated', videoAttributesToUpdate.name)
448 return finalCallback(null)
449 }) 347 })
450} 348}
451 349
452function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance, callback: (err: Error) => void) { 350function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance) {
453 // We need the instance because we have to remove some other stuffs (thumbnail etc) 351 // We need the instance because we have to remove some other stuffs (thumbnail etc)
454 fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) { 352 return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId)
455 // Do not return the error, continue the process 353 .then(video => {
456 if (err) return callback(null) 354 logger.debug('Removing remote video %s.', video.remoteId)
457 355 return video.destroy()
458 logger.debug('Removing remote video %s.', video.remoteId) 356 })
459 video.destroy().asCallback(function (err) { 357 .catch(err => {
460 // Do not return the error, continue the process 358 logger.debug('Could not fetch remote video.', { host: fromPod.host, remoteId: videoToRemoveData.remoteId, error: err })
461 if (err) {
462 logger.error('Cannot remove remote video with id %s.', videoToRemoveData.remoteId, { error: err })
463 }
464
465 return callback(null)
466 }) 359 })
467 })
468} 360}
469 361
470function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance, callback: (err: Error) => void) { 362function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance) {
471 fetchOwnedVideo(reportData.videoRemoteId, function (err, video) { 363 return fetchOwnedVideo(reportData.videoRemoteId)
472 if (err || !video) { 364 .then(video => {
473 if (!err) err = new Error('video not found') 365 logger.debug('Reporting remote abuse for video %s.', video.id)
474
475 logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId })
476 // Do not return the error, continue the process
477 return callback(null)
478 }
479
480 logger.debug('Reporting remote abuse for video %s.', video.id)
481
482 const videoAbuseData = {
483 reporterUsername: reportData.reporterUsername,
484 reason: reportData.reportReason,
485 reporterPodId: fromPod.id,
486 videoId: video.id
487 }
488 366
489 db.VideoAbuse.create(videoAbuseData).asCallback(function (err) { 367 const videoAbuseData = {
490 if (err) { 368 reporterUsername: reportData.reporterUsername,
491 logger.error('Cannot create remote abuse video.', { error: err }) 369 reason: reportData.reportReason,
370 reporterPodId: fromPod.id,
371 videoId: video.id
492 } 372 }
493 373
494 return callback(null) 374 return db.VideoAbuse.create(videoAbuseData)
495 }) 375 })
496 }) 376 .catch(err => logger.error('Cannot create remote abuse video.', { error: err }))
497} 377}
498 378
499function fetchOwnedVideo (id: string, callback: (err: Error, video?: VideoInstance) => void) { 379function fetchOwnedVideo (id: string) {
500 db.Video.load(id, function (err, video) { 380 return db.Video.load(id)
501 if (err || !video) { 381 .then(video => {
502 if (!err) err = new Error('video not found') 382 if (!video) throw new Error('Video not found')
503 383
384 return video
385 })
386 .catch(err => {
504 logger.error('Cannot load owned video from id.', { error: err, id }) 387 logger.error('Cannot load owned video from id.', { error: err, id })
505 return callback(err) 388 throw err
506 } 389 })
507
508 return callback(null, video)
509 })
510} 390}
511 391
512function fetchRemoteVideo (podHost: string, remoteId: string, callback: (err: Error, video?: VideoInstance) => void) { 392function fetchRemoteVideo (podHost: string, remoteId: string) {
513 db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) { 393 return db.Video.loadByHostAndRemoteId(podHost, remoteId)
514 if (err || !video) { 394 .then(video => {
515 if (!err) err = new Error('video not found') 395 if (!video) throw new Error('Video not found')
516 396
397 return video
398 })
399 .catch(err => {
517 logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId }) 400 logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId })
518 return callback(err) 401 throw err
519 } 402 })
520
521 return callback(null, video)
522 })
523} 403}