aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/remote
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api/remote')
-rw-r--r--server/controllers/api/remote/pods.ts48
-rw-r--r--server/controllers/api/remote/videos.ts654
2 files changed, 303 insertions, 399 deletions
diff --git a/server/controllers/api/remote/pods.ts b/server/controllers/api/remote/pods.ts
index a62b9c684..326eb61ac 100644
--- a/server/controllers/api/remote/pods.ts
+++ b/server/controllers/api/remote/pods.ts
@@ -5,7 +5,8 @@ import {
5 checkSignature, 5 checkSignature,
6 signatureValidator, 6 signatureValidator,
7 setBodyHostPort, 7 setBodyHostPort,
8 remotePodsAddValidator 8 remotePodsAddValidator,
9 asyncMiddleware
9} from '../../../middlewares' 10} from '../../../middlewares'
10import { sendOwnedDataToPod } from '../../../lib' 11import { sendOwnedDataToPod } from '../../../lib'
11import { getMyPublicCert, getFormattedObjects } from '../../../helpers' 12import { getMyPublicCert, getFormattedObjects } from '../../../helpers'
@@ -18,15 +19,17 @@ const remotePodsRouter = express.Router()
18remotePodsRouter.post('/remove', 19remotePodsRouter.post('/remove',
19 signatureValidator, 20 signatureValidator,
20 checkSignature, 21 checkSignature,
21 removePods 22 asyncMiddleware(removePods)
22) 23)
23 24
24remotePodsRouter.post('/list', remotePodsList) 25remotePodsRouter.post('/list',
26 asyncMiddleware(remotePodsList)
27)
25 28
26remotePodsRouter.post('/add', 29remotePodsRouter.post('/add',
27 setBodyHostPort, // We need to modify the host before running the validator! 30 setBodyHostPort, // We need to modify the host before running the validator!
28 remotePodsAddValidator, 31 remotePodsAddValidator,
29 addPods 32 asyncMiddleware(addPods)
30) 33)
31 34
32// --------------------------------------------------------------------------- 35// ---------------------------------------------------------------------------
@@ -37,35 +40,30 @@ export {
37 40
38// --------------------------------------------------------------------------- 41// ---------------------------------------------------------------------------
39 42
40function addPods (req: express.Request, res: express.Response, next: express.NextFunction) { 43async function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
41 const information = req.body 44 const information = req.body
42 45
43 const pod = db.Pod.build(information) 46 const pod = db.Pod.build(information)
44 pod.save() 47 const podCreated = await pod.save()
45 .then(podCreated => { 48
46 return sendOwnedDataToPod(podCreated.id) 49 await sendOwnedDataToPod(podCreated.id)
47 }) 50
48 .then(() => { 51 const cert = await getMyPublicCert()
49 return getMyPublicCert() 52 return res.json({ cert, email: CONFIG.ADMIN.EMAIL })
50 })
51 .then(cert => {
52 return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
53 })
54 .catch(err => next(err))
55} 53}
56 54
57function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) { 55async function remotePodsList (req: express.Request, res: express.Response, next: express.NextFunction) {
58 db.Pod.list() 56 const pods = await db.Pod.list()
59 .then(podsList => res.json(getFormattedObjects<FormattedPod, PodInstance>(podsList, podsList.length))) 57
60 .catch(err => next(err)) 58 return res.json(getFormattedObjects<FormattedPod, PodInstance>(pods, pods.length))
61} 59}
62 60
63function removePods (req: express.Request, res: express.Response, next: express.NextFunction) { 61async function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
64 const signature: PodSignature = req.body.signature 62 const signature: PodSignature = req.body.signature
65 const host = signature.host 63 const host = signature.host
66 64
67 db.Pod.loadByHost(host) 65 const pod = await db.Pod.loadByHost(host)
68 .then(pod => pod.destroy()) 66 await pod.destroy()
69 .then(() => res.type('json').status(204).end()) 67
70 .catch(err => next(err)) 68 return res.type('json').status(204).end()
71} 69}
diff --git a/server/controllers/api/remote/videos.ts b/server/controllers/api/remote/videos.ts
index c8f531490..bf442c6e5 100644
--- a/server/controllers/api/remote/videos.ts
+++ b/server/controllers/api/remote/videos.ts
@@ -1,5 +1,5 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as Promise from 'bluebird' 2import * as Bluebird from 'bluebird'
3import * as Sequelize from 'sequelize' 3import * as Sequelize from 'sequelize'
4 4
5import { database as db } from '../../../initializers/database' 5import { database as db } from '../../../initializers/database'
@@ -17,7 +17,7 @@ import {
17 remoteEventsVideosValidator 17 remoteEventsVideosValidator
18} from '../../../middlewares' 18} from '../../../middlewares'
19import { logger, retryTransactionWrapper } from '../../../helpers' 19import { logger, retryTransactionWrapper } from '../../../helpers'
20import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib' 20import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib'
21import { PodInstance, VideoFileInstance } from '../../../models' 21import { PodInstance, VideoFileInstance } from '../../../models'
22import { 22import {
23 RemoteVideoRequest, 23 RemoteVideoRequest,
@@ -87,7 +87,7 @@ function remoteVideos (req: express.Request, res: express.Response, next: expres
87 const fromPod = res.locals.secure.pod 87 const fromPod = res.locals.secure.pod
88 88
89 // We need to process in the same order to keep consistency 89 // We need to process in the same order to keep consistency
90 Promise.each(requests, request => { 90 Bluebird.each(requests, request => {
91 const data = request.data 91 const data = request.data
92 92
93 // Get the function we need to call in order to process the request 93 // Get the function we need to call in order to process the request
@@ -109,7 +109,7 @@ function remoteVideosQadu (req: express.Request, res: express.Response, next: ex
109 const requests: RemoteQaduVideoRequest[] = req.body.data 109 const requests: RemoteQaduVideoRequest[] = req.body.data
110 const fromPod = res.locals.secure.pod 110 const fromPod = res.locals.secure.pod
111 111
112 Promise.each(requests, request => { 112 Bluebird.each(requests, request => {
113 const videoData = request.data 113 const videoData = request.data
114 114
115 return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod) 115 return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
@@ -123,7 +123,7 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
123 const requests: RemoteVideoEventRequest[] = req.body.data 123 const requests: RemoteVideoEventRequest[] = req.body.data
124 const fromPod = res.locals.secure.pod 124 const fromPod = res.locals.secure.pod
125 125
126 Promise.each(requests, request => { 126 Bluebird.each(requests, request => {
127 const eventData = request.data 127 const eventData = request.data
128 128
129 return processVideosEventsRetryWrapper(eventData, fromPod) 129 return processVideosEventsRetryWrapper(eventData, fromPod)
@@ -133,541 +133,447 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
133 return res.type('json').status(204).end() 133 return res.type('json').status(204).end()
134} 134}
135 135
136function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) { 136async function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) {
137 const options = { 137 const options = {
138 arguments: [ eventData, fromPod ], 138 arguments: [ eventData, fromPod ],
139 errorMessage: 'Cannot process videos events with many retries.' 139 errorMessage: 'Cannot process videos events with many retries.'
140 } 140 }
141 141
142 return retryTransactionWrapper(processVideosEvents, options) 142 await retryTransactionWrapper(processVideosEvents, options)
143} 143}
144 144
145function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { 145async 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)
146 149
147 return db.sequelize.transaction(t => { 150 let columnToUpdate
148 return fetchVideoByUUID(eventData.uuid, t) 151 let qaduType
149 .then(videoInstance => {
150 const options = { transaction: t }
151 152
152 let columnToUpdate 153 switch (eventData.eventType) {
153 let qaduType 154 case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
155 columnToUpdate = 'views'
156 qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
157 break
154 158
155 switch (eventData.eventType) { 159 case REQUEST_VIDEO_EVENT_TYPES.LIKES:
156 case REQUEST_VIDEO_EVENT_TYPES.VIEWS: 160 columnToUpdate = 'likes'
157 columnToUpdate = 'views' 161 qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
158 qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS 162 break
159 break
160 163
161 case REQUEST_VIDEO_EVENT_TYPES.LIKES: 164 case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
162 columnToUpdate = 'likes' 165 columnToUpdate = 'dislikes'
163 qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES 166 qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
164 break 167 break
165 168
166 case REQUEST_VIDEO_EVENT_TYPES.DISLIKES: 169 default:
167 columnToUpdate = 'dislikes' 170 throw new Error('Unknown video event type.')
168 qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES 171 }
169 break
170 172
171 default: 173 const query = {}
172 throw new Error('Unknown video event type.') 174 query[columnToUpdate] = eventData.count
173 }
174 175
175 const query = {} 176 await videoInstance.increment(query, sequelizeOptions)
176 query[columnToUpdate] = eventData.count
177 177
178 return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType })) 178 const qadusParams = [
179 }) 179 {
180 .then(({ videoInstance, qaduType }) => { 180 videoId: videoInstance.id,
181 const qadusParams = [ 181 type: qaduType
182 { 182 }
183 videoId: videoInstance.id, 183 ]
184 type: qaduType 184 await quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
185 }
186 ]
187
188 return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
189 })
190 })
191 .then(() => logger.info('Remote video event processed for video with uuid %s.', eventData.uuid))
192 .catch(err => {
193 logger.debug('Cannot process a video event.', err)
194 throw err
195 }) 185 })
186
187 logger.info('Remote video event processed for video with uuid %s.', eventData.uuid)
196} 188}
197 189
198function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) { 190async function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
199 const options = { 191 const options = {
200 arguments: [ videoData, fromPod ], 192 arguments: [ videoData, fromPod ],
201 errorMessage: 'Cannot update quick and dirty the remote video with many retries.' 193 errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
202 } 194 }
203 195
204 return retryTransactionWrapper(quickAndDirtyUpdateVideo, options) 196 await retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
205} 197}
206 198
207function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) { 199async function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) {
208 let videoUUID = '' 200 let videoUUID = ''
209 201
210 return db.sequelize.transaction(t => { 202 await db.sequelize.transaction(async t => {
211 return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t) 203 const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoData.uuid, t)
212 .then(videoInstance => { 204 const sequelizeOptions = { transaction: t }
213 const options = { transaction: t }
214 205
215 videoUUID = videoInstance.uuid 206 videoUUID = videoInstance.uuid
216 207
217 if (videoData.views) { 208 if (videoData.views) {
218 videoInstance.set('views', videoData.views) 209 videoInstance.set('views', videoData.views)
219 } 210 }
220 211
221 if (videoData.likes) { 212 if (videoData.likes) {
222 videoInstance.set('likes', videoData.likes) 213 videoInstance.set('likes', videoData.likes)
223 } 214 }
224 215
225 if (videoData.dislikes) { 216 if (videoData.dislikes) {
226 videoInstance.set('dislikes', videoData.dislikes) 217 videoInstance.set('dislikes', videoData.dislikes)
227 } 218 }
228 219
229 return videoInstance.save(options) 220 await videoInstance.save(sequelizeOptions)
230 })
231 }) 221 })
232 .then(() => logger.info('Remote video with uuid %s quick and dirty updated', videoUUID)) 222
233 .catch(err => logger.debug('Cannot quick and dirty update the remote video.', err)) 223 logger.info('Remote video with uuid %s quick and dirty updated', videoUUID)
234} 224}
235 225
236// Handle retries on fail 226// Handle retries on fail
237function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { 227async function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
238 const options = { 228 const options = {
239 arguments: [ videoToCreateData, fromPod ], 229 arguments: [ videoToCreateData, fromPod ],
240 errorMessage: 'Cannot insert the remote video with many retries.' 230 errorMessage: 'Cannot insert the remote video with many retries.'
241 } 231 }
242 232
243 return retryTransactionWrapper(addRemoteVideo, options) 233 await retryTransactionWrapper(addRemoteVideo, options)
244} 234}
245 235
246function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { 236async function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
247 logger.debug('Adding remote video "%s".', videoToCreateData.uuid) 237 logger.debug('Adding remote video "%s".', videoToCreateData.uuid)
248 238
249 return db.sequelize.transaction(t => { 239 await db.sequelize.transaction(async t => {
250 return db.Video.loadByUUID(videoToCreateData.uuid) 240 const sequelizeOptions = {
251 .then(video => { 241 transaction: t
252 if (video) throw new Error('UUID already exists.') 242 }
253
254 return db.VideoChannel.loadByHostAndUUID(fromPod.host, videoToCreateData.channelUUID, t)
255 })
256 .then(videoChannel => {
257 if (!videoChannel) throw new Error('Video channel ' + videoToCreateData.channelUUID + ' not found.')
258 243
259 const tags = videoToCreateData.tags 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 }
260 271
261 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoChannel, tagInstances })) 272 const video = db.Video.build(videoData)
262 }) 273 await db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData)
263 .then(({ videoChannel, tagInstances }) => { 274 const videoCreated = await video.save(sequelizeOptions)
264 const videoData = { 275
265 name: videoToCreateData.name, 276 const tasks = []
266 uuid: videoToCreateData.uuid, 277 for (const fileData of videoToCreateData.files) {
267 category: videoToCreateData.category, 278 const videoFileInstance = db.VideoFile.build({
268 licence: videoToCreateData.licence, 279 extname: fileData.extname,
269 language: videoToCreateData.language, 280 infoHash: fileData.infoHash,
270 nsfw: videoToCreateData.nsfw, 281 resolution: fileData.resolution,
271 description: videoToCreateData.description, 282 size: fileData.size,
272 channelId: videoChannel.id, 283 videoId: videoCreated.id
273 duration: videoToCreateData.duration,
274 createdAt: videoToCreateData.createdAt,
275 // FIXME: updatedAt does not seems to be considered by Sequelize
276 updatedAt: videoToCreateData.updatedAt,
277 views: videoToCreateData.views,
278 likes: videoToCreateData.likes,
279 dislikes: videoToCreateData.dislikes,
280 remote: true
281 }
282
283 const video = db.Video.build(videoData)
284 return { tagInstances, video }
285 })
286 .then(({ tagInstances, video }) => {
287 return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
288 }) 284 })
289 .then(({ tagInstances, video }) => {
290 const options = {
291 transaction: t
292 }
293 285
294 return video.save(options).then(videoCreated => ({ tagInstances, videoCreated })) 286 tasks.push(videoFileInstance.save(sequelizeOptions))
295 }) 287 }
296 .then(({ tagInstances, videoCreated }) => {
297 const tasks = []
298 const options = {
299 transaction: t
300 }
301
302 videoToCreateData.files.forEach(fileData => {
303 const videoFileInstance = db.VideoFile.build({
304 extname: fileData.extname,
305 infoHash: fileData.infoHash,
306 resolution: fileData.resolution,
307 size: fileData.size,
308 videoId: videoCreated.id
309 })
310
311 tasks.push(videoFileInstance.save(options))
312 })
313 288
314 return Promise.all(tasks).then(() => ({ tagInstances, videoCreated })) 289 await Promise.all(tasks)
315 })
316 .then(({ tagInstances, videoCreated }) => {
317 const options = {
318 transaction: t
319 }
320 290
321 return videoCreated.setTags(tagInstances, options) 291 await videoCreated.setTags(tagInstances, sequelizeOptions)
322 })
323 })
324 .then(() => logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid))
325 .catch(err => {
326 logger.debug('Cannot insert the remote video.', err)
327 throw err
328 }) 292 })
293
294 logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
329} 295}
330 296
331// Handle retries on fail 297// Handle retries on fail
332function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { 298async function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
333 const options = { 299 const options = {
334 arguments: [ videoAttributesToUpdate, fromPod ], 300 arguments: [ videoAttributesToUpdate, fromPod ],
335 errorMessage: 'Cannot update the remote video with many retries' 301 errorMessage: 'Cannot update the remote video with many retries'
336 } 302 }
337 303
338 return retryTransactionWrapper(updateRemoteVideo, options) 304 await retryTransactionWrapper(updateRemoteVideo, options)
339} 305}
340 306
341function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { 307async function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
342 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) 308 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
343 309
344 return db.sequelize.transaction(t => { 310 try {
345 return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t) 311 await db.sequelize.transaction(async t => {
346 .then(videoInstance => { 312 const sequelizeOptions = {
347 const tags = videoAttributesToUpdate.tags 313 transaction: t
348 314 }
349 return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances })) 315
350 }) 316 const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid, t)
351 .then(({ videoInstance, tagInstances }) => { 317 const tags = videoAttributesToUpdate.tags
352 const options = { transaction: t } 318
353 319 const tagInstances = await db.Tag.findOrCreateTags(tags, t)
354 videoInstance.set('name', videoAttributesToUpdate.name) 320
355 videoInstance.set('category', videoAttributesToUpdate.category) 321 videoInstance.set('name', videoAttributesToUpdate.name)
356 videoInstance.set('licence', videoAttributesToUpdate.licence) 322 videoInstance.set('category', videoAttributesToUpdate.category)
357 videoInstance.set('language', videoAttributesToUpdate.language) 323 videoInstance.set('licence', videoAttributesToUpdate.licence)
358 videoInstance.set('nsfw', videoAttributesToUpdate.nsfw) 324 videoInstance.set('language', videoAttributesToUpdate.language)
359 videoInstance.set('description', videoAttributesToUpdate.description) 325 videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
360 videoInstance.set('duration', videoAttributesToUpdate.duration) 326 videoInstance.set('description', videoAttributesToUpdate.description)
361 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt) 327 videoInstance.set('duration', videoAttributesToUpdate.duration)
362 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt) 328 videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
363 videoInstance.set('views', videoAttributesToUpdate.views) 329 videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
364 videoInstance.set('likes', videoAttributesToUpdate.likes) 330 videoInstance.set('views', videoAttributesToUpdate.views)
365 videoInstance.set('dislikes', videoAttributesToUpdate.dislikes) 331 videoInstance.set('likes', videoAttributesToUpdate.likes)
366 332 videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
367 return videoInstance.save(options).then(() => ({ videoInstance, tagInstances })) 333
368 }) 334 await videoInstance.save(sequelizeOptions)
369 .then(({ tagInstances, videoInstance }) => { 335
370 const tasks: Promise<void>[] = [] 336 // Remove old video files
371 337 const videoFileDestroyTasks: Bluebird<void>[] = []
372 // Remove old video files 338 for (const videoFile of videoInstance.VideoFiles) {
373 videoInstance.VideoFiles.forEach(videoFile => { 339 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
374 tasks.push(videoFile.destroy({ transaction: t })) 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
375 }) 351 })
376 352
377 return Promise.all(tasks).then(() => ({ tagInstances, videoInstance })) 353 videoFileCreateTasks.push(videoFileInstance.save(sequelizeOptions))
378 }) 354 }
379 .then(({ tagInstances, videoInstance }) => {
380 const tasks: Promise<VideoFileInstance>[] = []
381 const options = {
382 transaction: t
383 }
384
385 videoAttributesToUpdate.files.forEach(fileData => {
386 const videoFileInstance = db.VideoFile.build({
387 extname: fileData.extname,
388 infoHash: fileData.infoHash,
389 resolution: fileData.resolution,
390 size: fileData.size,
391 videoId: videoInstance.id
392 })
393
394 tasks.push(videoFileInstance.save(options))
395 })
396 355
397 return Promise.all(tasks).then(() => ({ tagInstances, videoInstance })) 356 await Promise.all(videoFileCreateTasks)
398 })
399 .then(({ videoInstance, tagInstances }) => {
400 const options = { transaction: t }
401 357
402 return videoInstance.setTags(tagInstances, options) 358 await videoInstance.setTags(tagInstances, sequelizeOptions)
403 }) 359 })
404 }) 360
405 .then(() => logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)) 361 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
406 .catch(err => { 362 } catch (err) {
407 // This is just a debug because we will retry the insert 363 // This is just a debug because we will retry the insert
408 logger.debug('Cannot update the remote video.', err) 364 logger.debug('Cannot update the remote video.', err)
409 throw err 365 throw err
410 }) 366 }
411} 367}
412 368
413function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { 369async function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
414 const options = { 370 const options = {
415 arguments: [ videoToRemoveData, fromPod ], 371 arguments: [ videoToRemoveData, fromPod ],
416 errorMessage: 'Cannot remove the remote video channel with many retries.' 372 errorMessage: 'Cannot remove the remote video channel with many retries.'
417 } 373 }
418 374
419 return retryTransactionWrapper(removeRemoteVideo, options) 375 await retryTransactionWrapper(removeRemoteVideo, options)
420} 376}
421 377
422function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { 378async function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
423 logger.debug('Removing remote video "%s".', videoToRemoveData.uuid) 379 logger.debug('Removing remote video "%s".', videoToRemoveData.uuid)
424 380
425 return db.sequelize.transaction(t => { 381 await db.sequelize.transaction(async t => {
426 // We need the instance because we have to remove some other stuffs (thumbnail etc) 382 // We need the instance because we have to remove some other stuffs (thumbnail etc)
427 return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t) 383 const videoInstance = await fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid, t)
428 .then(video => video.destroy({ transaction: t })) 384 await videoInstance.destroy({ transaction: t })
429 })
430 .then(() => logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid))
431 .catch(err => {
432 logger.debug('Cannot remove the remote video.', err)
433 throw err
434 }) 385 })
386
387 logger.info('Remote video with uuid %s removed.', videoToRemoveData.uuid)
435} 388}
436 389
437function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { 390async function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
438 const options = { 391 const options = {
439 arguments: [ authorToCreateData, fromPod ], 392 arguments: [ authorToCreateData, fromPod ],
440 errorMessage: 'Cannot insert the remote video author with many retries.' 393 errorMessage: 'Cannot insert the remote video author with many retries.'
441 } 394 }
442 395
443 return retryTransactionWrapper(addRemoteVideoAuthor, options) 396 await retryTransactionWrapper(addRemoteVideoAuthor, options)
444} 397}
445 398
446function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { 399async function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) {
447 logger.debug('Adding remote video author "%s".', authorToCreateData.uuid) 400 logger.debug('Adding remote video author "%s".', authorToCreateData.uuid)
448 401
449 return db.sequelize.transaction(t => { 402 await db.sequelize.transaction(async t => {
450 return db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t) 403 const authorInDatabase = await db.Author.loadAuthorByPodAndUUID(authorToCreateData.uuid, fromPod.id, t)
451 .then(author => { 404 if (authorInDatabase) throw new Error('Author with UUID ' + authorToCreateData.uuid + ' already exists.')
452 if (author) throw new Error('UUID already exists.')
453 405
454 return undefined 406 const videoAuthorData = {
455 }) 407 name: authorToCreateData.name,
456 .then(() => { 408 uuid: authorToCreateData.uuid,
457 const videoAuthorData = { 409 userId: null, // Not on our pod
458 name: authorToCreateData.name, 410 podId: fromPod.id
459 uuid: authorToCreateData.uuid, 411 }
460 userId: null, // Not on our pod 412
461 podId: fromPod.id 413 const author = db.Author.build(videoAuthorData)
462 } 414 await author.save({ transaction: t })
463
464 const author = db.Author.build(videoAuthorData)
465 return author.save({ transaction: t })
466 })
467 }) 415 })
468 .then(() => logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid)) 416
469 .catch(err => { 417 logger.info('Remote video author with uuid %s inserted.', authorToCreateData.uuid)
470 logger.debug('Cannot insert the remote video author.', err)
471 throw err
472 })
473} 418}
474 419
475function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { 420async function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
476 const options = { 421 const options = {
477 arguments: [ authorAttributesToRemove, fromPod ], 422 arguments: [ authorAttributesToRemove, fromPod ],
478 errorMessage: 'Cannot remove the remote video author with many retries.' 423 errorMessage: 'Cannot remove the remote video author with many retries.'
479 } 424 }
480 425
481 return retryTransactionWrapper(removeRemoteVideoAuthor, options) 426 await retryTransactionWrapper(removeRemoteVideoAuthor, options)
482} 427}
483 428
484function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { 429async function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) {
485 logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid) 430 logger.debug('Removing remote video author "%s".', authorAttributesToRemove.uuid)
486 431
487 return db.sequelize.transaction(t => { 432 await db.sequelize.transaction(async t => {
488 return db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t) 433 const videoAuthor = await db.Author.loadAuthorByPodAndUUID(authorAttributesToRemove.uuid, fromPod.id, t)
489 .then(videoAuthor => videoAuthor.destroy({ transaction: t })) 434 await videoAuthor.destroy({ transaction: t })
490 })
491 .then(() => logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid))
492 .catch(err => {
493 logger.debug('Cannot remove the remote video author.', err)
494 throw err
495 }) 435 })
436
437 logger.info('Remote video author with uuid %s removed.', authorAttributesToRemove.uuid)
496} 438}
497 439
498function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { 440async function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
499 const options = { 441 const options = {
500 arguments: [ videoChannelToCreateData, fromPod ], 442 arguments: [ videoChannelToCreateData, fromPod ],
501 errorMessage: 'Cannot insert the remote video channel with many retries.' 443 errorMessage: 'Cannot insert the remote video channel with many retries.'
502 } 444 }
503 445
504 return retryTransactionWrapper(addRemoteVideoChannel, options) 446 await retryTransactionWrapper(addRemoteVideoChannel, options)
505} 447}
506 448
507function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { 449async function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) {
508 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) 450 logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
509 451
510 return db.sequelize.transaction(t => { 452 await db.sequelize.transaction(async t => {
511 return db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid) 453 const videoChannelInDatabase = await db.VideoChannel.loadByUUID(videoChannelToCreateData.uuid)
512 .then(videoChannel => { 454 if (videoChannelInDatabase) {
513 if (videoChannel) throw new Error('UUID already exists.') 455 throw new Error('Video channel with UUID ' + videoChannelToCreateData.uuid + ' already exists.')
456 }
514 457
515 return undefined 458 const authorUUID = videoChannelToCreateData.ownerUUID
516 }) 459 const podId = fromPod.id
517 .then(() => {
518 const authorUUID = videoChannelToCreateData.ownerUUID
519 const podId = fromPod.id
520 460
521 return db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t) 461 const author = await db.Author.loadAuthorByPodAndUUID(authorUUID, podId, t)
522 }) 462 if (!author) throw new Error('Unknown author UUID' + authorUUID + '.')
523 .then(author => { 463
524 if (!author) throw new Error('Unknown author UUID.') 464 const videoChannelData = {
525 465 name: videoChannelToCreateData.name,
526 const videoChannelData = { 466 description: videoChannelToCreateData.description,
527 name: videoChannelToCreateData.name, 467 uuid: videoChannelToCreateData.uuid,
528 description: videoChannelToCreateData.description, 468 createdAt: videoChannelToCreateData.createdAt,
529 uuid: videoChannelToCreateData.uuid, 469 updatedAt: videoChannelToCreateData.updatedAt,
530 createdAt: videoChannelToCreateData.createdAt, 470 remote: true,
531 updatedAt: videoChannelToCreateData.updatedAt, 471 authorId: author.id
532 remote: true, 472 }
533 authorId: author.id 473
534 } 474 const videoChannel = db.VideoChannel.build(videoChannelData)
535 475 await videoChannel.save({ transaction: t })
536 const videoChannel = db.VideoChannel.build(videoChannelData)
537 return videoChannel.save({ transaction: t })
538 })
539 })
540 .then(() => logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid))
541 .catch(err => {
542 logger.debug('Cannot insert the remote video channel.', err)
543 throw err
544 }) 476 })
477
478 logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
545} 479}
546 480
547function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { 481async function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
548 const options = { 482 const options = {
549 arguments: [ videoChannelAttributesToUpdate, fromPod ], 483 arguments: [ videoChannelAttributesToUpdate, fromPod ],
550 errorMessage: 'Cannot update the remote video channel with many retries.' 484 errorMessage: 'Cannot update the remote video channel with many retries.'
551 } 485 }
552 486
553 return retryTransactionWrapper(updateRemoteVideoChannel, options) 487 await retryTransactionWrapper(updateRemoteVideoChannel, options)
554} 488}
555 489
556function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { 490async function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) {
557 logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid) 491 logger.debug('Updating remote video channel "%s".', videoChannelAttributesToUpdate.uuid)
558 492
559 return db.sequelize.transaction(t => { 493 await db.sequelize.transaction(async t => {
560 return fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t) 494 const sequelizeOptions = { transaction: t }
561 .then(videoChannelInstance => {
562 const options = { transaction: t }
563 495
564 videoChannelInstance.set('name', videoChannelAttributesToUpdate.name) 496 const videoChannelInstance = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToUpdate.uuid, t)
565 videoChannelInstance.set('description', videoChannelAttributesToUpdate.description) 497 videoChannelInstance.set('name', videoChannelAttributesToUpdate.name)
566 videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt) 498 videoChannelInstance.set('description', videoChannelAttributesToUpdate.description)
567 videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt) 499 videoChannelInstance.set('createdAt', videoChannelAttributesToUpdate.createdAt)
500 videoChannelInstance.set('updatedAt', videoChannelAttributesToUpdate.updatedAt)
568 501
569 return videoChannelInstance.save(options) 502 await videoChannelInstance.save(sequelizeOptions)
570 })
571 })
572 .then(() => logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid))
573 .catch(err => {
574 // This is just a debug because we will retry the insert
575 logger.debug('Cannot update the remote video channel.', err)
576 throw err
577 }) 503 })
504
505 logger.info('Remote video channel with uuid %s updated', videoChannelAttributesToUpdate.uuid)
578} 506}
579 507
580function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { 508async function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
581 const options = { 509 const options = {
582 arguments: [ videoChannelAttributesToRemove, fromPod ], 510 arguments: [ videoChannelAttributesToRemove, fromPod ],
583 errorMessage: 'Cannot remove the remote video channel with many retries.' 511 errorMessage: 'Cannot remove the remote video channel with many retries.'
584 } 512 }
585 513
586 return retryTransactionWrapper(removeRemoteVideoChannel, options) 514 await retryTransactionWrapper(removeRemoteVideoChannel, options)
587} 515}
588 516
589function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { 517async function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) {
590 logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid) 518 logger.debug('Removing remote video channel "%s".', videoChannelAttributesToRemove.uuid)
591 519
592 return db.sequelize.transaction(t => { 520 await db.sequelize.transaction(async t => {
593 return fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t) 521 const videoChannel = await fetchVideoChannelByHostAndUUID(fromPod.host, videoChannelAttributesToRemove.uuid, t)
594 .then(videoChannel => videoChannel.destroy({ transaction: t })) 522 await videoChannel.destroy({ transaction: t })
595 })
596 .then(() => logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid))
597 .catch(err => {
598 logger.debug('Cannot remove the remote video channel.', err)
599 throw err
600 }) 523 })
524
525 logger.info('Remote video channel with uuid %s removed.', videoChannelAttributesToRemove.uuid)
601} 526}
602 527
603function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { 528async function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
604 const options = { 529 const options = {
605 arguments: [ reportData, fromPod ], 530 arguments: [ reportData, fromPod ],
606 errorMessage: 'Cannot create remote abuse video with many retries.' 531 errorMessage: 'Cannot create remote abuse video with many retries.'
607 } 532 }
608 533
609 return retryTransactionWrapper(reportAbuseRemoteVideo, options) 534 await retryTransactionWrapper(reportAbuseRemoteVideo, options)
610} 535}
611 536
612function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { 537async function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
613 logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID) 538 logger.debug('Reporting remote abuse for video %s.', reportData.videoUUID)
614 539
615 return db.sequelize.transaction(t => { 540 await db.sequelize.transaction(async t => {
616 return fetchVideoByUUID(reportData.videoUUID, t) 541 const videoInstance = await fetchVideoByUUID(reportData.videoUUID, t)
617 .then(video => { 542 const videoAbuseData = {
618 const videoAbuseData = { 543 reporterUsername: reportData.reporterUsername,
619 reporterUsername: reportData.reporterUsername, 544 reason: reportData.reportReason,
620 reason: reportData.reportReason, 545 reporterPodId: fromPod.id,
621 reporterPodId: fromPod.id, 546 videoId: videoInstance.id
622 videoId: video.id 547 }
623 }
624
625 return db.VideoAbuse.create(videoAbuseData)
626 })
627 })
628 .then(() => logger.info('Remote abuse for video uuid %s created', reportData.videoUUID))
629 .catch(err => {
630 // This is just a debug because we will retry the insert
631 logger.debug('Cannot create remote abuse video', err)
632 throw err
633 })
634}
635 548
636function fetchVideoByUUID (id: string, t: Sequelize.Transaction) { 549 await db.VideoAbuse.create(videoAbuseData)
637 return db.Video.loadByUUID(id, t)
638 .then(video => {
639 if (!video) throw new Error('Video not found')
640 550
641 return video 551 })
642 }) 552
643 .catch(err => { 553 logger.info('Remote abuse for video uuid %s created', reportData.videoUUID)
644 logger.error('Cannot load owned video from id.', { error: err.stack, id })
645 throw err
646 })
647} 554}
648 555
649function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { 556async function fetchVideoByUUID (id: string, t: Sequelize.Transaction) {
650 return db.Video.loadByHostAndUUID(podHost, uuid, t) 557 try {
651 .then(video => { 558 const video = await db.Video.loadByUUID(id, t)
652 if (!video) throw new Error('Video not found')
653 559
654 return video 560 if (!video) throw new Error('Video ' + id + ' not found')
655 }) 561
656 .catch(err => { 562 return video
657 logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid }) 563 } catch (err) {
658 throw err 564 logger.error('Cannot load owned video from id.', { error: err.stack, id })
659 }) 565 throw err
566 }
660} 567}
661 568
662function fetchVideoChannelByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { 569async function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) {
663 return db.VideoChannel.loadByHostAndUUID(podHost, uuid, t) 570 try {
664 .then(videoChannel => { 571 const video = await db.Video.loadByHostAndUUID(podHost, uuid, t)
665 if (!videoChannel) throw new Error('Video channel not found') 572 if (!video) throw new Error('Video not found')
666 573
667 return videoChannel 574 return video
668 }) 575 } catch (err) {
669 .catch(err => { 576 logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
670 logger.error('Cannot load video channel from host and uuid.', { error: err.stack, podHost, uuid }) 577 throw err
671 throw err 578 }
672 })
673} 579}