]>
Commit | Line | Data |
---|---|---|
1 | import * as express from 'express' | |
2 | import * as Bluebird from 'bluebird' | |
3 | import * as Sequelize from 'sequelize' | |
4 | ||
5 | import { database as db } from '../../../initializers/database' | |
6 | import { | |
7 | REQUEST_ENDPOINT_ACTIONS, | |
8 | REQUEST_ENDPOINTS, | |
9 | REQUEST_VIDEO_EVENT_TYPES, | |
10 | REQUEST_VIDEO_QADU_TYPES | |
11 | } from '../../../initializers' | |
12 | import { | |
13 | checkSignature, | |
14 | signatureValidator, | |
15 | remoteVideosValidator, | |
16 | remoteQaduVideosValidator, | |
17 | remoteEventsVideosValidator | |
18 | } from '../../../middlewares' | |
19 | import { logger, retryTransactionWrapper, resetSequelizeInstance } from '../../../helpers' | |
20 | import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib' | |
21 | import { PodInstance, VideoFileInstance } from '../../../models' | |
22 | import { | |
23 | RemoteVideoRequest, | |
24 | RemoteVideoCreateData, | |
25 | RemoteVideoUpdateData, | |
26 | RemoteVideoRemoveData, | |
27 | RemoteVideoReportAbuseData, | |
28 | RemoteQaduVideoRequest, | |
29 | RemoteQaduVideoData, | |
30 | RemoteVideoEventRequest, | |
31 | RemoteVideoEventData, | |
32 | RemoteVideoChannelCreateData, | |
33 | RemoteVideoChannelUpdateData, | |
34 | RemoteVideoChannelRemoveData, | |
35 | RemoteVideoAuthorRemoveData, | |
36 | RemoteVideoAuthorCreateData | |
37 | } from '../../../../shared' | |
38 | import { VideoInstance } from '../../../models/video/video-interface' | |
39 | ||
40 | const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] | |
41 | ||
42 | // Functions to call when processing a remote request | |
43 | // FIXME: use RemoteVideoRequestType as id type | |
44 | const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {} | |
45 | functionsHash[ENDPOINT_ACTIONS.ADD_VIDEO] = addRemoteVideoRetryWrapper | |
46 | functionsHash[ENDPOINT_ACTIONS.UPDATE_VIDEO] = updateRemoteVideoRetryWrapper | |
47 | functionsHash[ENDPOINT_ACTIONS.REMOVE_VIDEO] = removeRemoteVideoRetryWrapper | |
48 | functionsHash[ENDPOINT_ACTIONS.ADD_CHANNEL] = addRemoteVideoChannelRetryWrapper | |
49 | functionsHash[ENDPOINT_ACTIONS.UPDATE_CHANNEL] = updateRemoteVideoChannelRetryWrapper | |
50 | functionsHash[ENDPOINT_ACTIONS.REMOVE_CHANNEL] = removeRemoteVideoChannelRetryWrapper | |
51 | functionsHash[ENDPOINT_ACTIONS.REPORT_ABUSE] = reportAbuseRemoteVideoRetryWrapper | |
52 | functionsHash[ENDPOINT_ACTIONS.ADD_AUTHOR] = addRemoteVideoAuthorRetryWrapper | |
53 | functionsHash[ENDPOINT_ACTIONS.REMOVE_AUTHOR] = removeRemoteVideoAuthorRetryWrapper | |
54 | ||
55 | const remoteVideosRouter = express.Router() | |
56 | ||
57 | remoteVideosRouter.post('/', | |
58 | signatureValidator, | |
59 | checkSignature, | |
60 | remoteVideosValidator, | |
61 | remoteVideos | |
62 | ) | |
63 | ||
64 | remoteVideosRouter.post('/qadu', | |
65 | signatureValidator, | |
66 | checkSignature, | |
67 | remoteQaduVideosValidator, | |
68 | remoteVideosQadu | |
69 | ) | |
70 | ||
71 | remoteVideosRouter.post('/events', | |
72 | signatureValidator, | |
73 | checkSignature, | |
74 | remoteEventsVideosValidator, | |
75 | remoteVideosEvents | |
76 | ) | |
77 | ||
78 | // --------------------------------------------------------------------------- | |
79 | ||
80 | export { | |
81 | remoteVideosRouter | |
82 | } | |
83 | ||
84 | // --------------------------------------------------------------------------- | |
85 | ||
86 | function remoteVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | |
87 | const requests: RemoteVideoRequest[] = req.body.data | |
88 | const fromPod = res.locals.secure.pod | |
89 | ||
90 | // We need to process in the same order to keep consistency | |
91 | Bluebird.each(requests, request => { | |
92 | const data = request.data | |
93 | ||
94 | // Get the function we need to call in order to process the request | |
95 | const fun = functionsHash[request.type] | |
96 | if (fun === undefined) { | |
97 | logger.error('Unknown remote request type %s.', request.type) | |
98 | return | |
99 | } | |
100 | ||
101 | return fun.call(this, data, fromPod) | |
102 | }) | |
103 | .catch(err => logger.error('Error managing remote videos.', err)) | |
104 | ||
105 | // Don't block the other pod | |
106 | return res.type('json').status(204).end() | |
107 | } | |
108 | ||
109 | function remoteVideosQadu (req: express.Request, res: express.Response, next: express.NextFunction) { | |
110 | const requests: RemoteQaduVideoRequest[] = req.body.data | |
111 | const fromPod = res.locals.secure.pod | |
112 | ||
113 | Bluebird.each(requests, request => { | |
114 | const videoData = request.data | |
115 | ||
116 | return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod) | |
117 | }) | |
118 | .catch(err => logger.error('Error managing remote videos.', err)) | |
119 | ||
120 | return res.type('json').status(204).end() | |
121 | } | |
122 | ||
123 | function remoteVideosEvents (req: express.Request, res: express.Response, next: express.NextFunction) { | |
124 | const requests: RemoteVideoEventRequest[] = req.body.data | |
125 | const fromPod = res.locals.secure.pod | |
126 | ||
127 | Bluebird.each(requests, request => { | |
128 | const eventData = request.data | |
129 | ||
130 | return processVideosEventsRetryWrapper(eventData, fromPod) | |
131 | }) | |
132 | .catch(err => logger.error('Error managing remote videos.', err)) | |
133 | ||
134 | return res.type('json').status(204).end() | |
135 | } | |
136 | ||
137 | async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) { | |
138 | const options = { | |
139 | arguments: [ eventData, fromPod ], | |
140 | errorMessage: 'Cannot process videos events with many retries.' | |
141 | } | |
142 | ||
143 | await retryTransactionWrapper(processVideosEvents, options) | |
144 | } | |
145 | ||
146 | async function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { | |
147 | await db.sequelize.transaction(async t => { | |
148 | const sequelizeOptions = { transaction: t } | |
149 | const videoInstance = await fetchLocalVideoByUUID(eventData.uuid, t) | |
150 | ||
151 | let columnToUpdate | |
152 | let qaduType | |
153 | ||
154 | switch (eventData.eventType) { | |
155 | case REQUEST_VIDEO_EVENT_TYPES.VIEWS: | |
156 | columnToUpdate = 'views' | |
157 | qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS | |
158 | break | |
159 | ||
160 | case REQUEST_VIDEO_EVENT_TYPES.LIKES: | |
161 | columnToUpdate = 'likes' | |
162 | qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES | |
163 | break | |
164 | ||
165 | case REQUEST_VIDEO_EVENT_TYPES.DISLIKES: | |
166 | columnToUpdate = 'dislikes' | |
167 | qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES | |
168 | break | |
169 | ||
170 | default: | |
171 | throw new Error('Unknown video event type.') | |
172 | } | |
173 | ||
174 | const query = {} | |
175 | query[columnToUpdate] = eventData.count | |
176 | ||
177 | await videoInstance.increment(query, sequelizeOptions) | |
178 | ||
179 | const qadusParams = [ | |
180 | { | |
181 | videoId: videoInstance.id, | |
182 | type: qaduType | |
183 | } | |
184 | ] | |
185 | await quickAndDirtyUpdatesVideoToFriends(qadusParams, t) | |
186 | }) | |
187 | ||
188 | logger.info('Remote video event processed for video with uuid %s.', eventData.uuid) | |
189 | } | |
190 | ||
191 | async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) { | |
192 | const options = { | |
193 | arguments: [ videoData, fromPod ], | |
194 | errorMessage: 'Cannot update quick and dirty the remote video with many retries.' | |
195 | } | |
196 | ||
197 | await retryTransactionWrapper(quickAndDirtyUpdateVideo, options) | |
198 | } | |
199 | ||
200 | async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) { | |
201 | let videoUUID = '' | |
202 | ||
203 | await db.sequelize.transaction(async t => { | |
204 | const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t) | |
205 | const sequelizeOptions = { transaction: t } | |
206 | ||
207 | videoUUID = videoInstance.uuid | |
208 | ||
209 | if (videoData.views) { | |
210 | videoInstance.set('views', videoData.views) | |
211 | } | |
212 | ||
213 | if (videoData.likes) { | |
214 | videoInstance.set('likes', videoData.likes) | |
215 | } | |
216 | ||
217 | if (videoData.dislikes) { | |
218 | videoInstance.set('dislikes', videoData.dislikes) | |
219 | } | |
220 | ||
221 | await videoInstance.save(sequelizeOptions) | |
222 | }) | |
223 | ||
224 | logger.info('Remote video with uuid %s quick and dirty updated', videoUUID) | |
225 | } | |
226 | ||
227 | // Handle retries on fail | |
228 | async function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | |
229 | const options = { | |
230 | arguments: [ videoToCreateData, fromPod ], | |
231 | errorMessage: 'Cannot insert the remote video with many retries.' | |
232 | } | |
233 | ||
234 | await retryTransactionWrapper(addRemoteVideo, options) | |
235 | } | |
236 | ||
237 | async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | |
238 | logger.debug('Adding remote video "%s".', videoToCreateData.uuid) | |
239 | ||
240 | await db.sequelize.transaction(async t => { | |
241 | const sequelizeOptions = { | |
242 | transaction: t | |
243 | } | |
244 | ||
245 | const videoFromDatabase = await db.Video.loadByUUID(videoToCreateData.uuid) | |
246 | if (videoFromDatabase) throw new Error('UUID already exists.') | |
247 | ||
248 | const videoChannel = await db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t) | |
249 | if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.') | |
250 | ||
251 | const tags = videoToCreateData.tags | |
252 | const tagInstances = await db.Tag.findOrCreateTags(tags, t) | |
253 | ||
254 | const videoData = { | |
255 | name: videoToCreateData.name, | |
256 | uuid: videoToCreateData.uuid, | |
257 | category: videoToCreateData.category, | |
258 | licence: videoToCreateData.licence, | |
259 | language: videoToCreateData.language, | |
260 | nsfw: videoToCreateData.nsfw, | |
261 | description: videoToCreateData.truncatedDescription, | |
262 | channelId: videoChannel.id, | |
263 | duration: videoToCreateData.duration, | |
264 | createdAt: videoToCreateData.createdAt, | |
265 | // FIXME: updatedAt does not seems to be considered by Sequelize | |
266 | updatedAt: videoToCreateData.updatedAt, | |
267 | views: videoToCreateData.views, | |
268 | likes: videoToCreateData.likes, | |
269 | dislikes: videoToCreateData.dislikes, | |
270 | remote: true | |
271 | } | |
272 | ||
273 | const video = db.Video.build(videoData) | |
274 | await db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData) | |
275 | const videoCreated = await video.save(sequelizeOptions) | |
276 | ||
277 | const tasks = [] | |
278 | for (const fileData of videoToCreateData.files) { | |
279 | const videoFileInstance = db.VideoFile.build({ | |
280 | extname: fileData.extname, | |
281 | infoHash: fileData.infoHash, | |
282 | resolution: fileData.resolution, | |
283 | size: fileData.size, | |
284 | videoId: videoCreated.id | |
285 | }) | |
286 | ||
287 | tasks.push(videoFileInstance.save(sequelizeOptions)) | |
288 | } | |
289 | ||
290 | await Promise.all(tasks) | |
291 | ||
292 | await videoCreated.setTags(tagInstances, sequelizeOptions) | |
293 | }) | |
294 | ||
295 | logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid) | |
296 | } | |
297 | ||
298 | // Handle retries on fail | |
299 | async function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | |
300 | const options = { | |
301 | arguments: [ videoAttributesToUpdate, fromPod ], | |
302 | errorMessage: 'Cannot update the remote video with many retries' | |
303 | } | |
304 | ||
305 | await retryTransactionWrapper(updateRemoteVideo, options) | |
306 | } | |
307 | ||
308 | async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | |
309 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) | |
310 | let videoInstance: VideoInstance | |
311 | let videoFieldsSave: object | |
312 | ||
313 | try { | |
314 | await db.sequelize.transaction(async t => { | |
315 | const sequelizeOptions = { | |
316 | transaction: t | |
317 | } | |
318 | ||
319 | const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t) | |
320 | videoFieldsSave = videoInstance.toJSON() | |
321 | const tags = videoAttributesToUpdate.tags | |
322 | ||
323 | const tagInstances = await db.Tag.findOrCreateTags(tags, t) | |
324 | ||
325 | videoInstance.set('name', videoAttributesToUpdate.name) | |
326 | videoInstance.set('category', videoAttributesToUpdate.category) | |
327 | videoInstance.set('licence', videoAttributesToUpdate.licence) | |
328 | videoInstance.set('language', videoAttributesToUpdate.language) | |
329 | videoInstance.set('nsfw', videoAttributesToUpdate.nsfw) | |
330 | videoInstance.set('description', videoAttributesToUpdate.truncatedDescription) | |
331 | videoInstance.set('duration', videoAttributesToUpdate.duration) | |
332 | videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) | |
333 | videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt) | |
334 | videoInstance.set('views', videoAttributesToUpdate.views) | |
335 | videoInstance.set('likes', videoAttributesToUpdate.likes) | |
336 | videoInstance.set('dislikes', videoAttributesToUpdate.dislikes) | |
337 | ||
338 | await videoInstance.save(sequelizeOptions) | |
339 | ||
340 | // Remove old video files | |
341 | const videoFileDestroyTasks: Bluebird<void>[] = [] | |
342 | for (const videoFile of videoInstance.VideoFiles) { | |
343 | videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions)) | |
344 | } | |
345 | await Promise.all(videoFileDestroyTasks) | |
346 | ||
347 | const videoFileCreateTasks: Bluebird<VideoFileInstance>[] = [] | |
348 | for (const fileData of videoAttributesToUpdate.files) { | |
349 | const videoFileInstance = db.VideoFile.build({ | |
350 | extname: fileData.extname, | |
351 | infoHash: fileData.infoHash, | |
352 | resolution: fileData.resolution, | |
353 | size: fileData.size, | |
354 | videoId: videoInstance.id | |
355 | }) | |
356 | ||
357 | videoFileCreateTasks.push(videoFileInstance.save(sequelizeOptions)) | |
358 | } | |
359 | ||
360 | await Promise.all(videoFileCreateTasks) | |
361 | ||
362 | await videoInstance.setTags(tagInstances, sequelizeOptions) | |
363 | }) | |
364 | ||
365 | logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid) | |
366 | } catch (err) { | |
367 | if (videoInstance !== undefined && videoFieldsSave !== undefined) { | |
368 | resetSequelizeInstance(videoInstance, videoFieldsSave) | |
369 | } | |
370 | ||
371 | // This is just a debug because we will retry the insert | |
372 | logger.debug('Cannot update the remote video.', err) | |
373 | throw err | |
374 | } | |
375 | } | |
376 | ||
377 | async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | |
378 | const options = { | |
379 | arguments: [ videoToRemoveData, fromPod ], | |
380 | errorMessage: 'Cannot remove the remote video channel with many retries.' | |
381 | } | |
382 | ||
383 | await retryTransactionWrapper(removeRemoteVideo, options) | |
384 | } | |
385 | ||
386 | async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | |
387 | logger.debug('Removing remote video "%s".', videoToRemoveData.uuid) | |
388 | ||
389 | await db.sequelize.transaction(async t => { | |
390 | // We need the instance because we have to remove some other stuffs (thumbnail etc) | |
391 | const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t) | |
392 | await videoInstance.destroy({ transaction: t }) | |
393 | }) | |
394 | ||
395 | logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid) | |
396 | } | |
397 | ||
398 | async function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { | |
399 | const options = { | |
400 | arguments: [ authorToCreateData, fromPod ], | |
401 | errorMessage: 'Cannot insert the remote video author with many retries.' | |
402 | } | |
403 | ||
404 | await retryTransactionWrapper(addRemoteVideoAuthor, options) | |
405 | } | |
406 | ||
407 | async function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { | |
408 | logger.debug('Adding remote video author "%s".', authorToCreateData.uuid) | |
409 | ||
410 | await db.sequelize.transaction(async t => { | |
411 | const authorInDatabase = await db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t) | |
412 | if (authorInDatabase) throw new Error('Author with UUID ' + authorToCreateData.uuid + ' already exists.') | |
413 | ||
414 | const videoAuthorData = { | |
415 | name: authorToCreateData.name, | |
416 | uuid: authorToCreateData.uuid, | |
417 | userId: null, // Not on our pod | |
418 | podId: fromPod.id | |
419 | } | |
420 | ||
421 | const author = db.Author.build(videoAuthorData) | |
422 | await author.save({ transaction: t }) | |
423 | }) | |
424 | ||
425 | logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid) | |
426 | } | |
427 | ||
428 | async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { | |
429 | const options = { | |
430 | arguments: [ authorAttributesToRemove, fromPod ], | |
431 | errorMessage: 'Cannot remove the remote video author with many retries.' | |
432 | } | |
433 | ||
434 | await retryTransactionWrapper(removeRemoteVideoAuthor, options) | |
435 | } | |
436 | ||
437 | async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { | |
438 | logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid) | |
439 | ||
440 | await db.sequelize.transaction(async t => { | |
441 | const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t) | |
442 | await videoAuthor.destroy({ transaction: t }) | |
443 | }) | |
444 | ||
445 | logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid) | |
446 | } | |
447 | ||
448 | async function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { | |
449 | const options = { | |
450 | arguments: [ videoChannelToCreateData, fromPod ], | |
451 | errorMessage: 'Cannot insert the remote video channel with many retries.' | |
452 | } | |
453 | ||
454 | await retryTransactionWrapper(addRemoteVideoChannel, options) | |
455 | } | |
456 | ||
457 | async function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { | |
458 | logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) | |
459 | ||
460 | await db.sequelize.transaction(async t => { | |
461 | const videoChannelInDatabase = await db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid) | |
462 | if (videoChannelInDatabase) { | |
463 | throw new Error('Video channel with UUID ' + videoChannelToCreateData.uuid + ' already exists.') | |
464 | } | |
465 | ||
466 | const authorUUID = videoChannelToCreateData.ownerUUID | |
467 | const podId = fromPod.id | |
468 | ||
469 | const author = await db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t) | |
470 | if (!author) throw new Error('Unknown author UUID' + authorUUID + '.') | |
471 | ||
472 | const videoChannelData = { | |
473 | name: videoChannelToCreateData.name, | |
474 | description: videoChannelToCreateData.description, | |
475 | uuid: videoChannelToCreateData.uuid, | |
476 | createdAt: videoChannelToCreateData.createdAt, | |
477 | updatedAt: videoChannelToCreateData.updatedAt, | |
478 | remote: true, | |
479 | authorId: author.id | |
480 | } | |
481 | ||
482 | const videoChannel = db.VideoChannel.build(videoChannelData) | |
483 | await videoChannel.save({ transaction: t }) | |
484 | }) | |
485 | ||
486 | logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid) | |
487 | } | |
488 | ||
489 | async function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { | |
490 | const options = { | |
491 | arguments: [ videoChannelAttributesToUpdate, fromPod ], | |
492 | errorMessage: 'Cannot update the remote video channel with many retries.' | |
493 | } | |
494 | ||
495 | await retryTransactionWrapper(updateRemoteVideoChannel, options) | |
496 | } | |
497 | ||
498 | async function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { | |
499 | logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid) | |
500 | ||
501 | await db.sequelize.transaction(async t => { | |
502 | const sequelizeOptions = { transaction: t } | |
503 | ||
504 | const videoChannelInstance = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t) | |
505 | videoChannelInstance.set('name', videoChannelAttributesToUpdate.name) | |
506 | videoChannelInstance.set('description', videoChannelAttributesToUpdate.description) | |
507 | videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt) | |
508 | videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt) | |
509 | ||
510 | await videoChannelInstance.save(sequelizeOptions) | |
511 | }) | |
512 | ||
513 | logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid) | |
514 | } | |
515 | ||
516 | async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | |
517 | const options = { | |
518 | arguments: [ videoChannelAttributesToRemove, fromPod ], | |
519 | errorMessage: 'Cannot remove the remote video channel with many retries.' | |
520 | } | |
521 | ||
522 | await retryTransactionWrapper(removeRemoteVideoChannel, options) | |
523 | } | |
524 | ||
525 | async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | |
526 | logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid) | |
527 | ||
528 | await db.sequelize.transaction(async t => { | |
529 | const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t) | |
530 | await videoChannel.destroy({ transaction: t }) | |
531 | }) | |
532 | ||
533 | logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid) | |
534 | } | |
535 | ||
536 | async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | |
537 | const options = { | |
538 | arguments: [ reportData, fromPod ], | |
539 | errorMessage: 'Cannot create remote abuse video with many retries.' | |
540 | } | |
541 | ||
542 | await retryTransactionWrapper(reportAbuseRemoteVideo, options) | |
543 | } | |
544 | ||
545 | async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | |
546 | logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID) | |
547 | ||
548 | await db.sequelize.transaction(async t => { | |
549 | const videoInstance = await fetchLocalVideoByUUID(reportData.videoUUID, t) | |
550 | const videoAbuseData = { | |
551 | reporterUsername: reportData.reporterUsername, | |
552 | reason: reportData.reportReason, | |
553 | reporterPodId: fromPod.id, | |
554 | videoId: videoInstance.id | |
555 | } | |
556 | ||
557 | await db.VideoAbuse.create(videoAbuseData) | |
558 | ||
559 | }) | |
560 | ||
561 | logger.info('Remote abuse for video uuid %s created', reportData.videoUUID) | |
562 | } | |
563 | ||
564 | async function fetchLocalVideoByUUID (id: string, t: Sequelize.Transaction) { | |
565 | try { | |
566 | const video = await db.Video.loadLocalVideoByUUID(id, t) | |
567 | ||
568 | if (!video) throw new Error('Video ' + id + ' not found') | |
569 | ||
570 | return video | |
571 | } catch (err) { | |
572 | logger.error('Cannot load owned video from id.', { error: err.stack, id }) | |
573 | throw err | |
574 | } | |
575 | } | |
576 | ||
577 | async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { | |
578 | try { | |
579 | const video = await db.Video.loadByHostAndUUID(podHost, uuid, t) | |
580 | if (!video) throw new Error('Video not found') | |
581 | ||
582 | return video | |
583 | } catch (err) { | |
584 | logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid }) | |
585 | throw err | |
586 | } | |
587 | } |