diff options
-rw-r--r-- | server/controllers/api/videos/index.ts | 19 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/migrations/0315-user-notifications.ts | 41 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 22 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-file.ts | 18 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-import.ts | 7 | ||||
-rw-r--r-- | server/tests/api/users/user-notifications.ts | 5 |
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 | ||
19 | const LAST_MIGRATION_VERSION = 310 | 19 | const 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction, | ||
5 | queryInterface: Sequelize.QueryInterface, | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | |||
9 | { | ||
10 | const query = ` | ||
11 | CREATE 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, | ||
19 | PRIMARY 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 | |||
34 | function down (options) { | ||
35 | throw new Error('Not implemented.') | ||
36 | } | ||
37 | |||
38 | export { | ||
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 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { VideoResolution, VideoState, Job } from '../../../../shared' | 2 | import { VideoResolution, VideoState } from '../../../../shared' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { VideoModel } from '../../../models/video/video' | 4 | import { VideoModel } from '../../../models/video/video' |
5 | import { JobQueue } from '../job-queue' | 5 | import { JobQueue } from '../job-queue' |
@@ -8,7 +8,7 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils' | |||
8 | import { sequelizeTypescript } from '../../../initializers' | 8 | import { sequelizeTypescript } from '../../../initializers' |
9 | import * as Bluebird from 'bluebird' | 9 | import * as Bluebird from 'bluebird' |
10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' | 10 | import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' |
11 | import { importVideoFile, transcodeOriginalVideofile, optimizeVideofile } from '../../video-transcoding' | 11 | import { importVideoFile, optimizeVideofile, transcodeOriginalVideofile } from '../../video-transcoding' |
12 | import { Notifier } from '../../notifier' | 12 | import { Notifier } from '../../notifier' |
13 | 13 | ||
14 | export type VideoFilePayload = { | 14 | export type VideoFilePayload = { |
@@ -68,7 +68,7 @@ async function processVideoFile (job: Bull.Job) { | |||
68 | async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) { | 68 | async 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 | ||
96 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, isNewVideo: boolean) { | 97 | async 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 | ||