diff options
Diffstat (limited to 'server/controllers/api/videos/index.ts')
-rw-r--r-- | server/controllers/api/videos/index.ts | 72 |
1 files changed, 46 insertions, 26 deletions
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 7d55f06b6..76a318d13 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -8,14 +8,13 @@ import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../ | |||
8 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' | 8 | import { getFormattedObjects, getServerActor } from '../../../helpers/utils' |
9 | import { | 9 | import { |
10 | CONFIG, | 10 | CONFIG, |
11 | IMAGE_MIMETYPE_EXT, | 11 | MIMETYPES, |
12 | PREVIEWS_SIZE, | 12 | PREVIEWS_SIZE, |
13 | sequelizeTypescript, | 13 | sequelizeTypescript, |
14 | THUMBNAILS_SIZE, | 14 | THUMBNAILS_SIZE, |
15 | VIDEO_CATEGORIES, | 15 | VIDEO_CATEGORIES, |
16 | VIDEO_LANGUAGES, | 16 | VIDEO_LANGUAGES, |
17 | VIDEO_LICENCES, | 17 | VIDEO_LICENCES, |
18 | VIDEO_MIMETYPE_EXT, | ||
19 | VIDEO_PRIVACIES | 18 | VIDEO_PRIVACIES |
20 | } from '../../../initializers' | 19 | } from '../../../initializers' |
21 | import { | 20 | import { |
@@ -24,19 +23,20 @@ import { | |||
24 | fetchRemoteVideoDescription, | 23 | fetchRemoteVideoDescription, |
25 | getVideoActivityPubUrl | 24 | getVideoActivityPubUrl |
26 | } from '../../../lib/activitypub' | 25 | } from '../../../lib/activitypub' |
27 | import { sendCreateView } from '../../../lib/activitypub/send' | ||
28 | import { JobQueue } from '../../../lib/job-queue' | 26 | import { JobQueue } from '../../../lib/job-queue' |
29 | import { Redis } from '../../../lib/redis' | 27 | import { Redis } from '../../../lib/redis' |
30 | import { | 28 | import { |
31 | asyncMiddleware, | 29 | asyncMiddleware, |
32 | asyncRetryTransactionMiddleware, | 30 | asyncRetryTransactionMiddleware, |
33 | authenticate, | 31 | authenticate, |
32 | checkVideoFollowConstraints, | ||
34 | commonVideosFiltersValidator, | 33 | commonVideosFiltersValidator, |
35 | optionalAuthenticate, | 34 | optionalAuthenticate, |
36 | paginationValidator, | 35 | paginationValidator, |
37 | setDefaultPagination, | 36 | setDefaultPagination, |
38 | setDefaultSort, | 37 | setDefaultSort, |
39 | videosAddValidator, | 38 | videosAddValidator, |
39 | videosCustomGetValidator, | ||
40 | videosGetValidator, | 40 | videosGetValidator, |
41 | videosRemoveValidator, | 41 | videosRemoveValidator, |
42 | videosSortValidator, | 42 | videosSortValidator, |
@@ -56,27 +56,29 @@ import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-u | |||
56 | import { videoCaptionsRouter } from './captions' | 56 | import { videoCaptionsRouter } from './captions' |
57 | import { videoImportsRouter } from './import' | 57 | import { videoImportsRouter } from './import' |
58 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | 58 | import { resetSequelizeInstance } from '../../../helpers/database-utils' |
59 | import { rename } from 'fs-extra' | 59 | import { move } from 'fs-extra' |
60 | import { watchingRouter } from './watching' | 60 | import { watchingRouter } from './watching' |
61 | import { Notifier } from '../../../lib/notifier' | ||
62 | import { sendView } from '../../../lib/activitypub/send/send-view' | ||
61 | 63 | ||
62 | const auditLogger = auditLoggerFactory('videos') | 64 | const auditLogger = auditLoggerFactory('videos') |
63 | const videosRouter = express.Router() | 65 | const videosRouter = express.Router() |
64 | 66 | ||
65 | const reqVideoFileAdd = createReqFiles( | 67 | const reqVideoFileAdd = createReqFiles( |
66 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | 68 | [ 'videofile', 'thumbnailfile', 'previewfile' ], |
67 | Object.assign({}, VIDEO_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), | 69 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), |
68 | { | 70 | { |
69 | videofile: CONFIG.STORAGE.VIDEOS_DIR, | 71 | videofile: CONFIG.STORAGE.TMP_DIR, |
70 | thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR, | 72 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, |
71 | previewfile: CONFIG.STORAGE.PREVIEWS_DIR | 73 | previewfile: CONFIG.STORAGE.TMP_DIR |
72 | } | 74 | } |
73 | ) | 75 | ) |
74 | const reqVideoFileUpdate = createReqFiles( | 76 | const reqVideoFileUpdate = createReqFiles( |
75 | [ 'thumbnailfile', 'previewfile' ], | 77 | [ 'thumbnailfile', 'previewfile' ], |
76 | IMAGE_MIMETYPE_EXT, | 78 | MIMETYPES.IMAGE.MIMETYPE_EXT, |
77 | { | 79 | { |
78 | thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR, | 80 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, |
79 | previewfile: CONFIG.STORAGE.PREVIEWS_DIR | 81 | previewfile: CONFIG.STORAGE.TMP_DIR |
80 | } | 82 | } |
81 | ) | 83 | ) |
82 | 84 | ||
@@ -122,8 +124,9 @@ videosRouter.get('/:id/description', | |||
122 | ) | 124 | ) |
123 | videosRouter.get('/:id', | 125 | videosRouter.get('/:id', |
124 | optionalAuthenticate, | 126 | optionalAuthenticate, |
125 | asyncMiddleware(videosGetValidator), | 127 | asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), |
126 | getVideo | 128 | asyncMiddleware(checkVideoFollowConstraints), |
129 | asyncMiddleware(getVideo) | ||
127 | ) | 130 | ) |
128 | videosRouter.post('/:id/views', | 131 | videosRouter.post('/:id/views', |
129 | asyncMiddleware(videosGetValidator), | 132 | asyncMiddleware(videosGetValidator), |
@@ -207,7 +210,7 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
207 | // Move physical file | 210 | // Move physical file |
208 | const videoDir = CONFIG.STORAGE.VIDEOS_DIR | 211 | const videoDir = CONFIG.STORAGE.VIDEOS_DIR |
209 | const destination = join(videoDir, video.getVideoFilename(videoFile)) | 212 | const destination = join(videoDir, video.getVideoFilename(videoFile)) |
210 | await rename(videoPhysicalFile.path, destination) | 213 | await move(videoPhysicalFile.path, destination) |
211 | // This is important in case if there is another attempt in the retry process | 214 | // This is important in case if there is another attempt in the retry process |
212 | videoPhysicalFile.filename = video.getVideoFilename(videoFile) | 215 | videoPhysicalFile.filename = video.getVideoFilename(videoFile) |
213 | videoPhysicalFile.path = destination | 216 | videoPhysicalFile.path = destination |
@@ -270,6 +273,8 @@ async function addVideo (req: express.Request, res: express.Response) { | |||
270 | return videoCreated | 273 | return videoCreated |
271 | }) | 274 | }) |
272 | 275 | ||
276 | Notifier.Instance.notifyOnNewVideo(videoCreated) | ||
277 | |||
273 | if (video.state === VideoState.TO_TRANSCODE) { | 278 | if (video.state === VideoState.TO_TRANSCODE) { |
274 | // Put uuid because we don't have id auto incremented for now | 279 | // Put uuid because we don't have id auto incremented for now |
275 | const dataInput = { | 280 | const dataInput = { |
@@ -294,6 +299,7 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
294 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) | 299 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) |
295 | const videoInfoToUpdate: VideoUpdate = req.body | 300 | const videoInfoToUpdate: VideoUpdate = req.body |
296 | const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE | 301 | const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE |
302 | const wasUnlistedVideo = videoInstance.privacy === VideoPrivacy.UNLISTED | ||
297 | 303 | ||
298 | // Process thumbnail or create it from the video | 304 | // Process thumbnail or create it from the video |
299 | if (req.files && req.files['thumbnailfile']) { | 305 | if (req.files && req.files['thumbnailfile']) { |
@@ -308,10 +314,8 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
308 | } | 314 | } |
309 | 315 | ||
310 | try { | 316 | try { |
311 | await sequelizeTypescript.transaction(async t => { | 317 | const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { |
312 | const sequelizeOptions = { | 318 | const sequelizeOptions = { transaction: t } |
313 | transaction: t | ||
314 | } | ||
315 | const oldVideoChannel = videoInstance.VideoChannel | 319 | const oldVideoChannel = videoInstance.VideoChannel |
316 | 320 | ||
317 | if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name) | 321 | if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name) |
@@ -363,7 +367,11 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
363 | } | 367 | } |
364 | 368 | ||
365 | const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE | 369 | const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE |
366 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | 370 | |
371 | // Don't send update if the video was unfederated | ||
372 | if (!videoInstanceUpdated.VideoBlacklist || videoInstanceUpdated.VideoBlacklist.unfederated === false) { | ||
373 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | ||
374 | } | ||
367 | 375 | ||
368 | auditLogger.update( | 376 | auditLogger.update( |
369 | getAuditIdFromRes(res), | 377 | getAuditIdFromRes(res), |
@@ -371,7 +379,13 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
371 | oldVideoAuditView | 379 | oldVideoAuditView |
372 | ) | 380 | ) |
373 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid) | 381 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid) |
382 | |||
383 | return videoInstanceUpdated | ||
374 | }) | 384 | }) |
385 | |||
386 | if (wasUnlistedVideo || wasPrivateVideo) { | ||
387 | Notifier.Instance.notifyOnNewVideo(videoInstanceUpdated) | ||
388 | } | ||
375 | } catch (err) { | 389 | } catch (err) { |
376 | // Force fields we want to update | 390 | // Force fields we want to update |
377 | // If the transaction is retried, sequelize will think the object has not changed | 391 | // If the transaction is retried, sequelize will think the object has not changed |
@@ -384,10 +398,17 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
384 | return res.type('json').status(204).end() | 398 | return res.type('json').status(204).end() |
385 | } | 399 | } |
386 | 400 | ||
387 | function getVideo (req: express.Request, res: express.Response) { | 401 | async function getVideo (req: express.Request, res: express.Response) { |
388 | const videoInstance = res.locals.video | 402 | // We need more attributes |
403 | const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null | ||
404 | const video: VideoModel = await VideoModel.loadForGetAPI(res.locals.video.id, undefined, userId) | ||
389 | 405 | ||
390 | return res.json(videoInstance.toFormattedDetailsJSON()) | 406 | if (video.isOutdated()) { |
407 | JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) | ||
408 | .catch(err => logger.error('Cannot create AP refresher job for video %s.', video.url, { err })) | ||
409 | } | ||
410 | |||
411 | return res.json(video.toFormattedDetailsJSON()) | ||
391 | } | 412 | } |
392 | 413 | ||
393 | async function viewVideo (req: express.Request, res: express.Response) { | 414 | async function viewVideo (req: express.Request, res: express.Response) { |
@@ -406,8 +427,7 @@ async function viewVideo (req: express.Request, res: express.Response) { | |||
406 | ]) | 427 | ]) |
407 | 428 | ||
408 | const serverActor = await getServerActor() | 429 | const serverActor = await getServerActor() |
409 | 430 | await sendView(serverActor, videoInstance, undefined) | |
410 | await sendCreateView(serverActor, videoInstance, undefined) | ||
411 | 431 | ||
412 | return res.status(204).end() | 432 | return res.status(204).end() |
413 | } | 433 | } |
@@ -425,7 +445,7 @@ async function getVideoDescription (req: express.Request, res: express.Response) | |||
425 | return res.json({ description }) | 445 | return res.json({ description }) |
426 | } | 446 | } |
427 | 447 | ||
428 | async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { | 448 | async function listVideos (req: express.Request, res: express.Response) { |
429 | const resultList = await VideoModel.listForApi({ | 449 | const resultList = await VideoModel.listForApi({ |
430 | start: req.query.start, | 450 | start: req.query.start, |
431 | count: req.query.count, | 451 | count: req.query.count, |
@@ -439,7 +459,7 @@ async function listVideos (req: express.Request, res: express.Response, next: ex | |||
439 | nsfw: buildNSFWFilter(res, req.query.nsfw), | 459 | nsfw: buildNSFWFilter(res, req.query.nsfw), |
440 | filter: req.query.filter as VideoFilter, | 460 | filter: req.query.filter as VideoFilter, |
441 | withFiles: false, | 461 | withFiles: false, |
442 | userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined | 462 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined |
443 | }) | 463 | }) |
444 | 464 | ||
445 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 465 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |