aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/videos/index.ts19
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0315-user-notifications.ts41
-rw-r--r--server/lib/activitypub/videos.ts22
-rw-r--r--server/lib/job-queue/handlers/video-file.ts18
-rw-r--r--server/lib/job-queue/handlers/video-import.ts7
-rw-r--r--server/tests/api/users/user-notifications.ts5
7 files changed, 81 insertions, 33 deletions
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 94ed08fed..33521a8c1 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -264,7 +264,6 @@ async function addVideo (req: express.Request, res: express.Response) {
264 } 264 }
265 265
266 await federateVideoIfNeeded(video, true, t) 266 await federateVideoIfNeeded(video, true, t)
267 Notifier.Instance.notifyOnNewVideo(video)
268 267
269 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) 268 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON()))
270 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) 269 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid)
@@ -272,6 +271,8 @@ async function addVideo (req: express.Request, res: express.Response) {
272 return videoCreated 271 return videoCreated
273 }) 272 })
274 273
274 Notifier.Instance.notifyOnNewVideo(videoCreated)
275
275 if (video.state === VideoState.TO_TRANSCODE) { 276 if (video.state === VideoState.TO_TRANSCODE) {
276 // Put uuid because we don't have id auto incremented for now 277 // Put uuid because we don't have id auto incremented for now
277 const dataInput = { 278 const dataInput = {
@@ -311,10 +312,8 @@ async function updateVideo (req: express.Request, res: express.Response) {
311 } 312 }
312 313
313 try { 314 try {
314 await sequelizeTypescript.transaction(async t => { 315 const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => {
315 const sequelizeOptions = { 316 const sequelizeOptions = { transaction: t }
316 transaction: t
317 }
318 const oldVideoChannel = videoInstance.VideoChannel 317 const oldVideoChannel = videoInstance.VideoChannel
319 318
320 if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name) 319 if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name)
@@ -367,17 +366,19 @@ async function updateVideo (req: express.Request, res: express.Response) {
367 const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE 366 const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE
368 await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) 367 await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t)
369 368
370 if (wasUnlistedVideo || wasPrivateVideo) {
371 Notifier.Instance.notifyOnNewVideo(videoInstanceUpdated)
372 }
373
374 auditLogger.update( 369 auditLogger.update(
375 getAuditIdFromRes(res), 370 getAuditIdFromRes(res),
376 new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), 371 new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()),
377 oldVideoAuditView 372 oldVideoAuditView
378 ) 373 )
379 logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid) 374 logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid)
375
376 return videoInstanceUpdated
380 }) 377 })
378
379 if (wasUnlistedVideo || wasPrivateVideo) {
380 Notifier.Instance.notifyOnNewVideo(videoInstanceUpdated)
381 }
381 } catch (err) { 382 } catch (err) {
382 // Force fields we want to update 383 // Force fields we want to update
383 // If the transaction is retried, sequelize will think the object has not changed 384 // If the transaction is retried, sequelize will think the object has not changed
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index fcfaf71a0..91e74f6c7 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -16,7 +16,7 @@ let config: IConfig = require('config')
16 16
17// --------------------------------------------------------------------------- 17// ---------------------------------------------------------------------------
18 18
19const LAST_MIGRATION_VERSION = 310 19const LAST_MIGRATION_VERSION = 315
20 20
21// --------------------------------------------------------------------------- 21// ---------------------------------------------------------------------------
22 22
diff --git a/server/initializers/migrations/0315-user-notifications.ts b/server/initializers/migrations/0315-user-notifications.ts
new file mode 100644
index 000000000..2bd9c657d
--- /dev/null
+++ b/server/initializers/migrations/0315-user-notifications.ts
@@ -0,0 +1,41 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction,
5 queryInterface: Sequelize.QueryInterface,
6 sequelize: Sequelize.Sequelize
7}): Promise<void> {
8
9 {
10 const query = `
11CREATE TABLE IF NOT EXISTS "userNotificationSetting" ("id" SERIAL,
12"newVideoFromSubscription" INTEGER NOT NULL DEFAULT NULL,
13"newCommentOnMyVideo" INTEGER NOT NULL DEFAULT NULL,
14"videoAbuseAsModerator" INTEGER NOT NULL DEFAULT NULL,
15"blacklistOnMyVideo" INTEGER NOT NULL DEFAULT NULL,
16"userId" INTEGER REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
17"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
18"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
19PRIMARY KEY ("id"))
20`
21 await utils.sequelize.query(query)
22 }
23
24 {
25 const query = 'INSERT INTO "userNotificationSetting" ' +
26 '("newVideoFromSubscription", "newCommentOnMyVideo", "videoAbuseAsModerator", "blacklistOnMyVideo", ' +
27 '"userId", "createdAt", "updatedAt") ' +
28 '(SELECT 2, 2, 4, 4, id, NOW(), NOW() FROM "user")'
29
30 await utils.sequelize.query(query)
31 }
32}
33
34function down (options) {
35 throw new Error('Not implemented.')
36}
37
38export {
39 up,
40 down
41}
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 5794988a5..893768769 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -204,19 +204,17 @@ async function updateVideoFromAP (options: {
204 overrideTo?: string[] 204 overrideTo?: string[]
205}) { 205}) {
206 logger.debug('Updating remote video "%s".', options.videoObject.uuid) 206 logger.debug('Updating remote video "%s".', options.videoObject.uuid)
207
207 let videoFieldsSave: any 208 let videoFieldsSave: any
209 const wasPrivateVideo = options.video.privacy === VideoPrivacy.PRIVATE
210 const wasUnlistedVideo = options.video.privacy === VideoPrivacy.UNLISTED
208 211
209 try { 212 try {
210 await sequelizeTypescript.transaction(async t => { 213 await sequelizeTypescript.transaction(async t => {
211 const sequelizeOptions = { 214 const sequelizeOptions = { transaction: t }
212 transaction: t
213 }
214 215
215 videoFieldsSave = options.video.toJSON() 216 videoFieldsSave = options.video.toJSON()
216 217
217 const wasPrivateVideo = options.video.privacy === VideoPrivacy.PRIVATE
218 const wasUnlistedVideo = options.video.privacy === VideoPrivacy.UNLISTED
219
220 // Check actor has the right to update the video 218 // Check actor has the right to update the video
221 const videoChannel = options.video.VideoChannel 219 const videoChannel = options.video.VideoChannel
222 if (videoChannel.Account.id !== options.account.id) { 220 if (videoChannel.Account.id !== options.account.id) {
@@ -281,15 +279,13 @@ async function updateVideoFromAP (options: {
281 }) 279 })
282 options.video.VideoCaptions = await Promise.all(videoCaptionsPromises) 280 options.video.VideoCaptions = await Promise.all(videoCaptionsPromises)
283 } 281 }
284
285 {
286 // Notify our users?
287 if (wasPrivateVideo || wasUnlistedVideo) {
288 Notifier.Instance.notifyOnNewVideo(options.video)
289 }
290 }
291 }) 282 })
292 283
284 // Notify our users?
285 if (wasPrivateVideo || wasUnlistedVideo) {
286 Notifier.Instance.notifyOnNewVideo(options.video)
287 }
288
293 logger.info('Remote video with uuid %s updated', options.videoObject.uuid) 289 logger.info('Remote video with uuid %s updated', options.videoObject.uuid)
294 } catch (err) { 290 } catch (err) {
295 if (options.video !== undefined && videoFieldsSave !== undefined) { 291 if (options.video !== undefined && videoFieldsSave !== undefined) {
diff --git a/server/lib/job-queue/handlers/video-file.ts b/server/lib/job-queue/handlers/video-file.ts
index 959cc04fa..480d324dc 100644
--- a/server/lib/job-queue/handlers/video-file.ts
+++ b/server/lib/job-queue/handlers/video-file.ts
@@ -1,5 +1,5 @@
1import * as Bull from 'bull' 1import * as Bull from 'bull'
2import { VideoResolution, VideoState, Job } from '../../../../shared' 2import { VideoResolution, VideoState } from '../../../../shared'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { JobQueue } from '../job-queue' 5import { JobQueue } from '../job-queue'
@@ -8,7 +8,7 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils'
8import { sequelizeTypescript } from '../../../initializers' 8import { sequelizeTypescript } from '../../../initializers'
9import * as Bluebird from 'bluebird' 9import * as Bluebird from 'bluebird'
10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' 10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
11import { importVideoFile, transcodeOriginalVideofile, optimizeVideofile } from '../../video-transcoding' 11import { importVideoFile, optimizeVideofile, transcodeOriginalVideofile } from '../../video-transcoding'
12import { Notifier } from '../../notifier' 12import { Notifier } from '../../notifier'
13 13
14export type VideoFilePayload = { 14export type VideoFilePayload = {
@@ -68,7 +68,7 @@ async function processVideoFile (job: Bull.Job) {
68async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { 68async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) {
69 if (video === undefined) return undefined 69 if (video === undefined) return undefined
70 70
71 return sequelizeTypescript.transaction(async t => { 71 const { videoDatabase, isNewVideo } = await sequelizeTypescript.transaction(async t => {
72 // Maybe the video changed in database, refresh it 72 // Maybe the video changed in database, refresh it
73 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) 73 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
74 // Video does not exist anymore 74 // Video does not exist anymore
@@ -87,10 +87,11 @@ async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) {
87 87
88 // If the video was not published, we consider it is a new one for other instances 88 // If the video was not published, we consider it is a new one for other instances
89 await federateVideoIfNeeded(videoDatabase, isNewVideo, t) 89 await federateVideoIfNeeded(videoDatabase, isNewVideo, t)
90 if (isNewVideo) Notifier.Instance.notifyOnNewVideo(video)
91 90
92 return undefined 91 return { videoDatabase, isNewVideo }
93 }) 92 })
93
94 if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
94} 95}
95 96
96async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) { 97async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) {
@@ -99,7 +100,7 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo
99 // Outside the transaction (IO on disk) 100 // Outside the transaction (IO on disk)
100 const { videoFileResolution } = await videoArg.getOriginalFileResolution() 101 const { videoFileResolution } = await videoArg.getOriginalFileResolution()
101 102
102 return sequelizeTypescript.transaction(async t => { 103 const videoDatabase = await sequelizeTypescript.transaction(async t => {
103 // Maybe the video changed in database, refresh it 104 // Maybe the video changed in database, refresh it
104 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t) 105 let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoArg.uuid, t)
105 // Video does not exist anymore 106 // Video does not exist anymore
@@ -137,8 +138,11 @@ async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: bo
137 } 138 }
138 139
139 await federateVideoIfNeeded(videoDatabase, isNewVideo, t) 140 await federateVideoIfNeeded(videoDatabase, isNewVideo, t)
140 if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase) 141
142 return videoDatabase
141 }) 143 })
144
145 if (isNewVideo) Notifier.Instance.notifyOnNewVideo(videoDatabase)
142} 146}
143 147
144// --------------------------------------------------------------------------- 148// ---------------------------------------------------------------------------
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 82edb8d5c..29cd1198c 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -180,12 +180,11 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
180 // Update video DB object 180 // Update video DB object
181 video.duration = duration 181 video.duration = duration
182 video.state = CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED 182 video.state = CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED
183 const videoUpdated = await video.save({ transaction: t }) 183 await video.save({ transaction: t })
184 184
185 // Now we can federate the video (reload from database, we need more attributes) 185 // Now we can federate the video (reload from database, we need more attributes)
186 const videoForFederation = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) 186 const videoForFederation = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
187 await federateVideoIfNeeded(videoForFederation, true, t) 187 await federateVideoIfNeeded(videoForFederation, true, t)
188 Notifier.Instance.notifyOnNewVideo(videoForFederation)
189 188
190 // Update video import object 189 // Update video import object
191 videoImport.state = VideoImportState.SUCCESS 190 videoImport.state = VideoImportState.SUCCESS
@@ -193,10 +192,12 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
193 192
194 logger.info('Video %s imported.', video.uuid) 193 logger.info('Video %s imported.', video.uuid)
195 194
196 videoImportUpdated.Video = videoUpdated 195 videoImportUpdated.Video = videoForFederation
197 return videoImportUpdated 196 return videoImportUpdated
198 }) 197 })
199 198
199 Notifier.Instance.notifyOnNewVideo(videoImportUpdated.Video)
200
200 // Create transcoding jobs? 201 // Create transcoding jobs?
201 if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { 202 if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) {
202 // Put uuid because we don't have id auto incremented for now 203 // Put uuid because we don't have id auto incremented for now
diff --git a/server/tests/api/users/user-notifications.ts b/server/tests/api/users/user-notifications.ts
index ea35e6390..09c0479fd 100644
--- a/server/tests/api/users/user-notifications.ts
+++ b/server/tests/api/users/user-notifications.ts
@@ -152,6 +152,8 @@ describe('Test users notifications', function () {
152 const videoName = 'remote video ' + videoNameId 152 const videoName = 'remote video ' + videoNameId
153 153
154 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId) 154 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId)
155 await waitJobs(servers)
156
155 await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') 157 await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
156 }) 158 })
157 159
@@ -194,6 +196,7 @@ describe('Test users notifications', function () {
194 } 196 }
195 } 197 }
196 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data) 198 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
199 await waitJobs(servers)
197 200
198 await wait(6000) 201 await wait(6000)
199 await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence') 202 await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'presence')
@@ -245,6 +248,7 @@ describe('Test users notifications', function () {
245 248
246 const data = { privacy: VideoPrivacy.PRIVATE } 249 const data = { privacy: VideoPrivacy.PRIVATE }
247 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data) 250 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
251 await waitJobs(servers)
248 252
249 await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence') 253 await checkNewVideoFromSubscription(baseParams, videoName, uuid, 'absence')
250 254
@@ -276,6 +280,7 @@ describe('Test users notifications', function () {
276 280
277 const data = { privacy: VideoPrivacy.PRIVATE } 281 const data = { privacy: VideoPrivacy.PRIVATE }
278 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data) 282 const uuid = await uploadVideoByRemoteAccount(servers, videoNameId, data)
283 await waitJobs(servers)
279 284
280 await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED }) 285 await updateVideo(servers[1].url, servers[1].accessToken, uuid, { privacy: VideoPrivacy.UNLISTED })
281 286