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