diff options
Diffstat (limited to 'server/controllers/api/remote/videos.ts')
-rw-r--r-- | server/controllers/api/remote/videos.ts | 654 |
1 files changed, 280 insertions, 374 deletions
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 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as Promise from 'bluebird' | 2 | import * as Bluebird from 'bluebird' |
3 | import * as Sequelize from 'sequelize' | 3 | import * as Sequelize from 'sequelize' |
4 | 4 | ||
5 | import { database as db } from '../../../initializers/database' | 5 | import { database as db } from '../../../initializers/database' |
@@ -17,7 +17,7 @@ import { | |||
17 | remoteEventsVideosValidator | 17 | remoteEventsVideosValidator |
18 | } from '../../../middlewares' | 18 | } from '../../../middlewares' |
19 | import { logger, retryTransactionWrapper } from '../../../helpers' | 19 | import { logger, retryTransactionWrapper } from '../../../helpers' |
20 | import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib' | 20 | import { quickAndDirtyUpdatesVideoToFriends, fetchVideoChannelByHostAndUUID } from '../../../lib' |
21 | import { PodInstance, VideoFileInstance } from '../../../models' | 21 | import { PodInstance, VideoFileInstance } from '../../../models' |
22 | import { | 22 | import { |
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 | ||
136 | function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromPod: PodInstance) { | 136 | async 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 | ||
145 | function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { | 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) | ||
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 | ||
198 | function quickAndDirtyUpdateVideoRetryWrapper (videoData: RemoteQaduVideoData, fromPod: PodInstance) { | 190 | async 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 | ||
207 | function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodInstance) { | 199 | async 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 |
237 | function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | 227 | async 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 | ||
246 | function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | 236 | async 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 |
332 | function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | 298 | async 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 | ||
341 | function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | 307 | async 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 | ||
413 | function removeRemoteVideoRetryWrapper (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | 369 | async 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 | ||
422 | function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | 378 | async 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 | ||
437 | function addRemoteVideoAuthorRetryWrapper (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { | 390 | async 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 | ||
446 | function addRemoteVideoAuthor (authorToCreateData: RemoteVideoAuthorCreateData, fromPod: PodInstance) { | 399 | async 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 | ||
475 | function removeRemoteVideoAuthorRetryWrapper (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { | 420 | async 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 | ||
484 | function removeRemoteVideoAuthor (authorAttributesToRemove: RemoteVideoAuthorRemoveData, fromPod: PodInstance) { | 429 | async 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 | ||
498 | function addRemoteVideoChannelRetryWrapper (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { | 440 | async 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 | ||
507 | function addRemoteVideoChannel (videoChannelToCreateData: RemoteVideoChannelCreateData, fromPod: PodInstance) { | 449 | async 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 | ||
547 | function updateRemoteVideoChannelRetryWrapper (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { | 481 | async 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 | ||
556 | function updateRemoteVideoChannel (videoChannelAttributesToUpdate: RemoteVideoChannelUpdateData, fromPod: PodInstance) { | 490 | async 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 | ||
580 | function removeRemoteVideoChannelRetryWrapper (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | 508 | async 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 | ||
589 | function removeRemoteVideoChannel (videoChannelAttributesToRemove: RemoteVideoChannelRemoveData, fromPod: PodInstance) { | 517 | async 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 | ||
603 | function reportAbuseRemoteVideoRetryWrapper (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | 528 | async 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 | ||
612 | function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | 537 | async 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 | ||
636 | function 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 | ||
649 | function fetchVideoByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { | 556 | async 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 | ||
662 | function fetchVideoChannelByHostAndUUID (podHost: string, uuid: string, t: Sequelize.Transaction) { | 569 | async 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 | } |