aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api/videos')
-rw-r--r--server/controllers/api/videos/abuse.ts5
-rw-r--r--server/controllers/api/videos/blacklist.ts31
-rw-r--r--server/controllers/api/videos/captions.ts4
-rw-r--r--server/controllers/api/videos/comment.ts15
-rw-r--r--server/controllers/api/videos/import.ts21
-rw-r--r--server/controllers/api/videos/index.ts72
-rw-r--r--server/controllers/api/videos/rate.ts17
7 files changed, 108 insertions, 57 deletions
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index d0c81804b..32f9c4793 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -3,7 +3,6 @@ import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { getFormattedObjects } from '../../../helpers/utils' 4import { getFormattedObjects } from '../../../helpers/utils'
5import { sequelizeTypescript } from '../../../initializers' 5import { sequelizeTypescript } from '../../../initializers'
6import { sendVideoAbuse } from '../../../lib/activitypub/send'
7import { 6import {
8 asyncMiddleware, 7 asyncMiddleware,
9 asyncRetryTransactionMiddleware, 8 asyncRetryTransactionMiddleware,
@@ -22,6 +21,8 @@ import { VideoModel } from '../../../models/video/video'
22import { VideoAbuseModel } from '../../../models/video/video-abuse' 21import { VideoAbuseModel } from '../../../models/video/video-abuse'
23import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' 22import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger'
24import { UserModel } from '../../../models/account/user' 23import { UserModel } from '../../../models/account/user'
24import { Notifier } from '../../../lib/notifier'
25import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
25 26
26const auditLogger = auditLoggerFactory('abuse') 27const auditLogger = auditLoggerFactory('abuse')
27const abuseVideoRouter = express.Router() 28const abuseVideoRouter = express.Router()
@@ -117,6 +118,8 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
117 await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance) 118 await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance)
118 } 119 }
119 120
121 Notifier.Instance.notifyOnNewVideoAbuse(videoAbuseInstance)
122
120 auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON())) 123 auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON()))
121 124
122 return videoAbuseInstance 125 return videoAbuseInstance
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index 7f803c8e9..43b0516e7 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -16,6 +16,10 @@ import {
16} from '../../../middlewares' 16} from '../../../middlewares'
17import { VideoBlacklistModel } from '../../../models/video/video-blacklist' 17import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
18import { sequelizeTypescript } from '../../../initializers' 18import { sequelizeTypescript } from '../../../initializers'
19import { Notifier } from '../../../lib/notifier'
20import { VideoModel } from '../../../models/video/video'
21import { sendCreateVideo, sendDeleteVideo, sendUpdateVideo } from '../../../lib/activitypub/send'
22import { federateVideoIfNeeded } from '../../../lib/activitypub'
19 23
20const blacklistRouter = express.Router() 24const blacklistRouter = express.Router()
21 25
@@ -64,16 +68,26 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response)
64 68
65 const toCreate = { 69 const toCreate = {
66 videoId: videoInstance.id, 70 videoId: videoInstance.id,
71 unfederated: body.unfederate === true,
67 reason: body.reason 72 reason: body.reason
68 } 73 }
69 74
70 await VideoBlacklistModel.create(toCreate) 75 const blacklist = await VideoBlacklistModel.create(toCreate)
76 blacklist.Video = videoInstance
77
78 if (body.unfederate === true) {
79 await sendDeleteVideo(videoInstance, undefined)
80 }
81
82 Notifier.Instance.notifyOnVideoBlacklist(blacklist)
83
84 logger.info('Video %s blacklisted.', res.locals.video.uuid)
85
71 return res.type('json').status(204).end() 86 return res.type('json').status(204).end()
72} 87}
73 88
74async function updateVideoBlacklistController (req: express.Request, res: express.Response) { 89async function updateVideoBlacklistController (req: express.Request, res: express.Response) {
75 const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel 90 const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel
76 logger.info(videoBlacklist)
77 91
78 if (req.body.reason !== undefined) videoBlacklist.reason = req.body.reason 92 if (req.body.reason !== undefined) videoBlacklist.reason = req.body.reason
79 93
@@ -92,11 +106,20 @@ async function listBlacklist (req: express.Request, res: express.Response, next:
92 106
93async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) { 107async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) {
94 const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel 108 const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel
109 const video: VideoModel = res.locals.video
95 110
96 await sequelizeTypescript.transaction(t => { 111 await sequelizeTypescript.transaction(async t => {
97 return videoBlacklist.destroy({ transaction: t }) 112 const unfederated = videoBlacklist.unfederated
113 await videoBlacklist.destroy({ transaction: t })
114
115 // Re federate the video
116 if (unfederated === true) {
117 await federateVideoIfNeeded(video, true, t)
118 }
98 }) 119 })
99 120
121 Notifier.Instance.notifyOnVideoUnblacklist(video)
122
100 logger.info('Video %s removed from blacklist.', res.locals.video.uuid) 123 logger.info('Video %s removed from blacklist.', res.locals.video.uuid)
101 124
102 return res.type('json').status(204).end() 125 return res.type('json').status(204).end()
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts
index 3ba918189..9b3661368 100644
--- a/server/controllers/api/videos/captions.ts
+++ b/server/controllers/api/videos/captions.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' 2import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares'
3import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators' 3import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators'
4import { createReqFiles } from '../../../helpers/express-utils' 4import { createReqFiles } from '../../../helpers/express-utils'
5import { CONFIG, sequelizeTypescript, VIDEO_CAPTIONS_MIMETYPE_EXT } from '../../../initializers' 5import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../../initializers'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { VideoCaptionModel } from '../../../models/video/video-caption' 7import { VideoCaptionModel } from '../../../models/video/video-caption'
8import { VideoModel } from '../../../models/video/video' 8import { VideoModel } from '../../../models/video/video'
@@ -12,7 +12,7 @@ import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
12 12
13const reqVideoCaptionAdd = createReqFiles( 13const reqVideoCaptionAdd = createReqFiles(
14 [ 'captionfile' ], 14 [ 'captionfile' ],
15 VIDEO_CAPTIONS_MIMETYPE_EXT, 15 MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT,
16 { 16 {
17 captionfile: CONFIG.STORAGE.CAPTIONS_DIR 17 captionfile: CONFIG.STORAGE.CAPTIONS_DIR
18 } 18 }
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index 4f2b4faee..70c1148ba 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -8,7 +8,7 @@ import { buildFormattedCommentTree, createVideoComment } from '../../../lib/vide
8import { 8import {
9 asyncMiddleware, 9 asyncMiddleware,
10 asyncRetryTransactionMiddleware, 10 asyncRetryTransactionMiddleware,
11 authenticate, 11 authenticate, optionalAuthenticate,
12 paginationValidator, 12 paginationValidator,
13 setDefaultPagination, 13 setDefaultPagination,
14 setDefaultSort 14 setDefaultSort
@@ -26,6 +26,7 @@ import { VideoCommentModel } from '../../../models/video/video-comment'
26import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' 26import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger'
27import { AccountModel } from '../../../models/account/account' 27import { AccountModel } from '../../../models/account/account'
28import { UserModel } from '../../../models/account/user' 28import { UserModel } from '../../../models/account/user'
29import { Notifier } from '../../../lib/notifier'
29 30
30const auditLogger = auditLoggerFactory('comments') 31const auditLogger = auditLoggerFactory('comments')
31const videoCommentRouter = express.Router() 32const videoCommentRouter = express.Router()
@@ -36,10 +37,12 @@ videoCommentRouter.get('/:videoId/comment-threads',
36 setDefaultSort, 37 setDefaultSort,
37 setDefaultPagination, 38 setDefaultPagination,
38 asyncMiddleware(listVideoCommentThreadsValidator), 39 asyncMiddleware(listVideoCommentThreadsValidator),
40 optionalAuthenticate,
39 asyncMiddleware(listVideoThreads) 41 asyncMiddleware(listVideoThreads)
40) 42)
41videoCommentRouter.get('/:videoId/comment-threads/:threadId', 43videoCommentRouter.get('/:videoId/comment-threads/:threadId',
42 asyncMiddleware(listVideoThreadCommentsValidator), 44 asyncMiddleware(listVideoThreadCommentsValidator),
45 optionalAuthenticate,
43 asyncMiddleware(listVideoThreadComments) 46 asyncMiddleware(listVideoThreadComments)
44) 47)
45 48
@@ -69,10 +72,12 @@ export {
69 72
70async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) { 73async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) {
71 const video = res.locals.video as VideoModel 74 const video = res.locals.video as VideoModel
75 const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined
76
72 let resultList: ResultList<VideoCommentModel> 77 let resultList: ResultList<VideoCommentModel>
73 78
74 if (video.commentsEnabled === true) { 79 if (video.commentsEnabled === true) {
75 resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort) 80 resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort, user)
76 } else { 81 } else {
77 resultList = { 82 resultList = {
78 total: 0, 83 total: 0,
@@ -85,10 +90,12 @@ async function listVideoThreads (req: express.Request, res: express.Response, ne
85 90
86async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) { 91async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) {
87 const video = res.locals.video as VideoModel 92 const video = res.locals.video as VideoModel
93 const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined
94
88 let resultList: ResultList<VideoCommentModel> 95 let resultList: ResultList<VideoCommentModel>
89 96
90 if (video.commentsEnabled === true) { 97 if (video.commentsEnabled === true) {
91 resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id) 98 resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id, user)
92 } else { 99 } else {
93 resultList = { 100 resultList = {
94 total: 0, 101 total: 0,
@@ -113,6 +120,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons
113 }, t) 120 }, t)
114 }) 121 })
115 122
123 Notifier.Instance.notifyOnNewComment(comment)
116 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON())) 124 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
117 125
118 return res.json({ 126 return res.json({
@@ -134,6 +142,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response
134 }, t) 142 }, t)
135 }) 143 })
136 144
145 Notifier.Instance.notifyOnNewComment(comment)
137 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON())) 146 auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON()))
138 147
139 return res.json({ comment: comment.toFormattedJSON() }).end() 148 return res.json({ comment: comment.toFormattedJSON() }).end()
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 9e51e2000..7053d5253 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -3,14 +3,7 @@ import * as magnetUtil from 'magnet-uri'
3import 'multer' 3import 'multer'
4import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' 4import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger'
5import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' 5import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares'
6import { 6import { CONFIG, MIMETYPES, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE } from '../../../initializers'
7 CONFIG,
8 IMAGE_MIMETYPE_EXT,
9 PREVIEWS_SIZE,
10 sequelizeTypescript,
11 THUMBNAILS_SIZE,
12 TORRENT_MIMETYPE_EXT
13} from '../../../initializers'
14import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl' 7import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl'
15import { createReqFiles } from '../../../helpers/express-utils' 8import { createReqFiles } from '../../../helpers/express-utils'
16import { logger } from '../../../helpers/logger' 9import { logger } from '../../../helpers/logger'
@@ -28,18 +21,18 @@ import { VideoChannelModel } from '../../../models/video/video-channel'
28import * as Bluebird from 'bluebird' 21import * as Bluebird from 'bluebird'
29import * as parseTorrent from 'parse-torrent' 22import * as parseTorrent from 'parse-torrent'
30import { getSecureTorrentName } from '../../../helpers/utils' 23import { getSecureTorrentName } from '../../../helpers/utils'
31import { readFile, rename } from 'fs-extra' 24import { readFile, move } from 'fs-extra'
32 25
33const auditLogger = auditLoggerFactory('video-imports') 26const auditLogger = auditLoggerFactory('video-imports')
34const videoImportsRouter = express.Router() 27const videoImportsRouter = express.Router()
35 28
36const reqVideoFileImport = createReqFiles( 29const reqVideoFileImport = createReqFiles(
37 [ 'thumbnailfile', 'previewfile', 'torrentfile' ], 30 [ 'thumbnailfile', 'previewfile', 'torrentfile' ],
38 Object.assign({}, TORRENT_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), 31 Object.assign({}, MIMETYPES.TORRENT.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT),
39 { 32 {
40 thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR, 33 thumbnailfile: CONFIG.STORAGE.TMP_DIR,
41 previewfile: CONFIG.STORAGE.PREVIEWS_DIR, 34 previewfile: CONFIG.STORAGE.TMP_DIR,
42 torrentfile: CONFIG.STORAGE.TORRENTS_DIR 35 torrentfile: CONFIG.STORAGE.TMP_DIR
43 } 36 }
44) 37)
45 38
@@ -78,7 +71,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
78 71
79 // Rename the torrent to a secured name 72 // Rename the torrent to a secured name
80 const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) 73 const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName))
81 await rename(torrentfile.path, newTorrentPath) 74 await move(torrentfile.path, newTorrentPath)
82 torrentfile.path = newTorrentPath 75 torrentfile.path = newTorrentPath
83 76
84 const buf = await readFile(torrentfile.path) 77 const buf = await readFile(torrentfile.path)
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))
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index dc322bb0c..53952a0a2 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -2,8 +2,8 @@ import * as express from 'express'
2import { UserVideoRateUpdate } from '../../../../shared' 2import { UserVideoRateUpdate } from '../../../../shared'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { sequelizeTypescript, VIDEO_RATE_TYPES } from '../../../initializers' 4import { sequelizeTypescript, VIDEO_RATE_TYPES } from '../../../initializers'
5import { sendVideoRateChange } from '../../../lib/activitypub' 5import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub'
6import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoRateValidator } from '../../../middlewares' 6import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares'
7import { AccountModel } from '../../../models/account/account' 7import { AccountModel } from '../../../models/account/account'
8import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 8import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
9import { VideoModel } from '../../../models/video/video' 9import { VideoModel } from '../../../models/video/video'
@@ -12,7 +12,7 @@ const rateVideoRouter = express.Router()
12 12
13rateVideoRouter.put('/:id/rate', 13rateVideoRouter.put('/:id/rate',
14 authenticate, 14 authenticate,
15 asyncMiddleware(videoRateValidator), 15 asyncMiddleware(videoUpdateRateValidator),
16 asyncRetryTransactionMiddleware(rateVideo) 16 asyncRetryTransactionMiddleware(rateVideo)
17) 17)
18 18
@@ -28,11 +28,12 @@ async function rateVideo (req: express.Request, res: express.Response) {
28 const body: UserVideoRateUpdate = req.body 28 const body: UserVideoRateUpdate = req.body
29 const rateType = body.rating 29 const rateType = body.rating
30 const videoInstance: VideoModel = res.locals.video 30 const videoInstance: VideoModel = res.locals.video
31 const userAccount: AccountModel = res.locals.oauth.token.User.Account
31 32
32 await sequelizeTypescript.transaction(async t => { 33 await sequelizeTypescript.transaction(async t => {
33 const sequelizeOptions = { transaction: t } 34 const sequelizeOptions = { transaction: t }
34 35
35 const accountInstance = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) 36 const accountInstance = await AccountModel.load(userAccount.id, t)
36 const previousRate = await AccountVideoRateModel.load(accountInstance.id, videoInstance.id, t) 37 const previousRate = await AccountVideoRateModel.load(accountInstance.id, videoInstance.id, t)
37 38
38 let likesToIncrement = 0 39 let likesToIncrement = 0
@@ -44,20 +45,22 @@ async function rateVideo (req: express.Request, res: express.Response) {
44 // There was a previous rate, update it 45 // There was a previous rate, update it
45 if (previousRate) { 46 if (previousRate) {
46 // We will remove the previous rate, so we will need to update the video count attribute 47 // We will remove the previous rate, so we will need to update the video count attribute
47 if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement-- 48 if (previousRate.type === 'like') likesToIncrement--
48 else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement-- 49 else if (previousRate.type === 'dislike') dislikesToIncrement--
49 50
50 if (rateType === 'none') { // Destroy previous rate 51 if (rateType === 'none') { // Destroy previous rate
51 await previousRate.destroy(sequelizeOptions) 52 await previousRate.destroy(sequelizeOptions)
52 } else { // Update previous rate 53 } else { // Update previous rate
53 previousRate.type = rateType 54 previousRate.type = rateType
55 previousRate.url = getRateUrl(rateType, userAccount.Actor, videoInstance)
54 await previousRate.save(sequelizeOptions) 56 await previousRate.save(sequelizeOptions)
55 } 57 }
56 } else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate 58 } else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate
57 const query = { 59 const query = {
58 accountId: accountInstance.id, 60 accountId: accountInstance.id,
59 videoId: videoInstance.id, 61 videoId: videoInstance.id,
60 type: rateType 62 type: rateType,
63 url: getRateUrl(rateType, userAccount.Actor, videoInstance)
61 } 64 }
62 65
63 await AccountVideoRateModel.create(query, sequelizeOptions) 66 await AccountVideoRateModel.create(query, sequelizeOptions)