aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api/videos/index.ts')
-rw-r--r--server/controllers/api/videos/index.ts72
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 '../../../
8import { getFormattedObjects, getServerActor } from '../../../helpers/utils' 8import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
9import { 9import {
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'
21import { 20import {
@@ -24,19 +23,20 @@ import {
24 fetchRemoteVideoDescription, 23 fetchRemoteVideoDescription,
25 getVideoActivityPubUrl 24 getVideoActivityPubUrl
26} from '../../../lib/activitypub' 25} from '../../../lib/activitypub'
27import { sendCreateView } from '../../../lib/activitypub/send'
28import { JobQueue } from '../../../lib/job-queue' 26import { JobQueue } from '../../../lib/job-queue'
29import { Redis } from '../../../lib/redis' 27import { Redis } from '../../../lib/redis'
30import { 28import {
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
56import { videoCaptionsRouter } from './captions' 56import { videoCaptionsRouter } from './captions'
57import { videoImportsRouter } from './import' 57import { videoImportsRouter } from './import'
58import { resetSequelizeInstance } from '../../../helpers/database-utils' 58import { resetSequelizeInstance } from '../../../helpers/database-utils'
59import { rename } from 'fs-extra' 59import { move } from 'fs-extra'
60import { watchingRouter } from './watching' 60import { watchingRouter } from './watching'
61import { Notifier } from '../../../lib/notifier'
62import { sendView } from '../../../lib/activitypub/send/send-view'
61 63
62const auditLogger = auditLoggerFactory('videos') 64const auditLogger = auditLoggerFactory('videos')
63const videosRouter = express.Router() 65const videosRouter = express.Router()
64 66
65const reqVideoFileAdd = createReqFiles( 67const 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)
74const reqVideoFileUpdate = createReqFiles( 76const 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)
123videosRouter.get('/:id', 125videosRouter.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)
128videosRouter.post('/:id/views', 131videosRouter.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
387function getVideo (req: express.Request, res: express.Response) { 401async 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
393async function viewVideo (req: express.Request, res: express.Response) { 414async 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
428async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { 448async 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))