diff options
author | Chocobozzz <me@florianbigard.com> | 2022-03-18 11:17:35 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-03-18 11:21:50 +0100 |
commit | 57e4e1c1a95c3a81a967f54ecc2a510d8b0e129c (patch) | |
tree | fcf12670d643ec4a3b5eccdfa834227c0417d988 /server | |
parent | 2e3f7a5a6fbae276d3ba1cb1b08289917ec7604b (diff) | |
download | PeerTube-57e4e1c1a95c3a81a967f54ecc2a510d8b0e129c.tar.gz PeerTube-57e4e1c1a95c3a81a967f54ecc2a510d8b0e129c.tar.zst PeerTube-57e4e1c1a95c3a81a967f54ecc2a510d8b0e129c.zip |
Don't store remote rates of remote videos
In the future we'll stop to expose all available rates to improve users
privacy
Diffstat (limited to 'server')
41 files changed, 382 insertions, 320 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index c4d1be121..fc27ebbe8 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -66,11 +66,13 @@ activityPubClientRouter.get('/accounts?/:name/playlists', | |||
66 | ) | 66 | ) |
67 | activityPubClientRouter.get('/accounts?/:name/likes/:videoId', | 67 | activityPubClientRouter.get('/accounts?/:name/likes/:videoId', |
68 | executeIfActivityPub, | 68 | executeIfActivityPub, |
69 | cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), | ||
69 | asyncMiddleware(getAccountVideoRateValidatorFactory('like')), | 70 | asyncMiddleware(getAccountVideoRateValidatorFactory('like')), |
70 | getAccountVideoRateFactory('like') | 71 | getAccountVideoRateFactory('like') |
71 | ) | 72 | ) |
72 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', | 73 | activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', |
73 | executeIfActivityPub, | 74 | executeIfActivityPub, |
75 | cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS), | ||
74 | asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')), | 76 | asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')), |
75 | getAccountVideoRateFactory('dislike') | 77 | getAccountVideoRateFactory('dislike') |
76 | ) | 78 | ) |
diff --git a/server/controllers/api/search/search-videos.ts b/server/controllers/api/search/search-videos.ts index 68428d766..1d7a7b7bc 100644 --- a/server/controllers/api/search/search-videos.ts +++ b/server/controllers/api/search/search-videos.ts | |||
@@ -134,8 +134,7 @@ async function searchVideoURI (url: string, res: express.Response) { | |||
134 | if (isUserAbleToSearchRemoteURI(res)) { | 134 | if (isUserAbleToSearchRemoteURI(res)) { |
135 | try { | 135 | try { |
136 | const syncParam = { | 136 | const syncParam = { |
137 | likes: false, | 137 | rates: false, |
138 | dislikes: false, | ||
139 | shares: false, | 138 | shares: false, |
140 | comments: false, | 139 | comments: false, |
141 | thumbnail: true, | 140 | thumbnail: true, |
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index d0bcc6785..9d6d8b2fa 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts | |||
@@ -154,7 +154,9 @@ async function activityPubCollectionPagination ( | |||
154 | id: baseUrl, | 154 | id: baseUrl, |
155 | type: 'OrderedCollectionPage', | 155 | type: 'OrderedCollectionPage', |
156 | totalItems: result.total, | 156 | totalItems: result.total, |
157 | first: baseUrl + '?page=1' | 157 | first: result.data.length === 0 |
158 | ? undefined | ||
159 | : baseUrl + '?page=1' | ||
158 | } | 160 | } |
159 | } | 161 | } |
160 | 162 | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index e0f6f2bd2..aaf39e6ec 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
24 | 24 | ||
25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
26 | 26 | ||
27 | const LAST_MIGRATION_VERSION = 690 | 27 | const LAST_MIGRATION_VERSION = 695 |
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
diff --git a/server/initializers/migrations/0695-remove-remote-rates.ts b/server/initializers/migrations/0695-remove-remote-rates.ts new file mode 100644 index 000000000..f5c394bae --- /dev/null +++ b/server/initializers/migrations/0695-remove-remote-rates.ts | |||
@@ -0,0 +1,28 @@ | |||
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 | db: any | ||
8 | }): Promise<void> { | ||
9 | const query = 'DELETE FROM "accountVideoRate" ' + | ||
10 | 'WHERE "accountVideoRate".id IN (' + | ||
11 | 'SELECT "accountVideoRate".id FROM "accountVideoRate" ' + | ||
12 | 'INNER JOIN account ON account.id = "accountVideoRate"."accountId" ' + | ||
13 | 'INNER JOIN actor ON actor.id = account."actorId" ' + | ||
14 | 'INNER JOIN video ON video.id = "accountVideoRate"."videoId" ' + | ||
15 | 'WHERE actor."serverId" IS NOT NULL AND video.remote IS TRUE' + | ||
16 | ')' | ||
17 | |||
18 | await utils.sequelize.query(query, { type: Sequelize.QueryTypes.BULKDELETE, transaction: utils.transaction }) | ||
19 | } | ||
20 | |||
21 | function down () { | ||
22 | throw new Error('Not implemented.') | ||
23 | } | ||
24 | |||
25 | export { | ||
26 | up, | ||
27 | down | ||
28 | } | ||
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts index d0558f191..2bd5bb066 100644 --- a/server/lib/activitypub/audience.ts +++ b/server/lib/activitypub/audience.ts | |||
@@ -1,68 +1,6 @@ | |||
1 | import { Transaction } from 'sequelize' | ||
2 | import { ActivityAudience } from '../../../shared/models/activitypub' | 1 | import { ActivityAudience } from '../../../shared/models/activitypub' |
3 | import { ACTIVITY_PUB } from '../../initializers/constants' | 2 | import { ACTIVITY_PUB } from '../../initializers/constants' |
4 | import { ActorModel } from '../../models/actor/actor' | 3 | import { MActorFollowersUrl } from '../../types/models' |
5 | import { VideoModel } from '../../models/video/video' | ||
6 | import { VideoShareModel } from '../../models/video/video-share' | ||
7 | import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '../../types/models' | ||
8 | |||
9 | function getRemoteVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience { | ||
10 | return { | ||
11 | to: [ accountActor.url ], | ||
12 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) | ||
13 | } | ||
14 | } | ||
15 | |||
16 | function getVideoCommentAudience ( | ||
17 | videoComment: MCommentOwnerVideo, | ||
18 | threadParentComments: MCommentOwner[], | ||
19 | actorsInvolvedInVideo: MActorFollowersUrl[], | ||
20 | isOrigin = false | ||
21 | ): ActivityAudience { | ||
22 | const to = [ ACTIVITY_PUB.PUBLIC ] | ||
23 | const cc: string[] = [] | ||
24 | |||
25 | // Owner of the video we comment | ||
26 | if (isOrigin === false) { | ||
27 | cc.push(videoComment.Video.VideoChannel.Account.Actor.url) | ||
28 | } | ||
29 | |||
30 | // Followers of the poster | ||
31 | cc.push(videoComment.Account.Actor.followersUrl) | ||
32 | |||
33 | // Send to actors we reply to | ||
34 | for (const parentComment of threadParentComments) { | ||
35 | if (parentComment.isDeleted()) continue | ||
36 | |||
37 | cc.push(parentComment.Account.Actor.url) | ||
38 | } | ||
39 | |||
40 | return { | ||
41 | to, | ||
42 | cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl)) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience { | ||
47 | return { | ||
48 | to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), | ||
49 | cc: [] | ||
50 | } | ||
51 | } | ||
52 | |||
53 | async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) { | ||
54 | const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) | ||
55 | |||
56 | const videoAll = video as VideoModel | ||
57 | |||
58 | const videoActor = videoAll.VideoChannel?.Account | ||
59 | ? videoAll.VideoChannel.Account.Actor | ||
60 | : await ActorModel.loadFromAccountByVideoId(video.id, t) | ||
61 | |||
62 | actors.push(videoActor) | ||
63 | |||
64 | return actors | ||
65 | } | ||
66 | 4 | ||
67 | function getAudience (actorSender: MActorFollowersUrl, isPublic = true) { | 5 | function getAudience (actorSender: MActorFollowersUrl, isPublic = true) { |
68 | return buildAudience([ actorSender.followersUrl ], isPublic) | 6 | return buildAudience([ actorSender.followersUrl ], isPublic) |
@@ -92,9 +30,5 @@ function audiencify<T> (object: T, audience: ActivityAudience) { | |||
92 | export { | 30 | export { |
93 | buildAudience, | 31 | buildAudience, |
94 | getAudience, | 32 | getAudience, |
95 | getRemoteVideoAudience, | 33 | audiencify |
96 | getActorsInvolvedInVideo, | ||
97 | getAudienceFromFollowersOf, | ||
98 | audiencify, | ||
99 | getVideoCommentAudience | ||
100 | } | 34 | } |
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index 2619d9754..200f8ce11 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts | |||
@@ -2,7 +2,7 @@ import { ActivityAnnounce } from '../../../../shared/models/activitypub' | |||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { sequelizeTypescript } from '../../../initializers/database' | 3 | import { sequelizeTypescript } from '../../../initializers/database' |
4 | import { VideoShareModel } from '../../../models/video/video-share' | 4 | import { VideoShareModel } from '../../../models/video/video-share' |
5 | import { forwardVideoRelatedActivity } from '../send/utils' | 5 | import { forwardVideoRelatedActivity } from '../send/shared/send-utils' |
6 | import { getOrCreateAPVideo } from '../videos' | 6 | import { getOrCreateAPVideo } from '../videos' |
7 | import { Notifier } from '../../notifier' | 7 | import { Notifier } from '../../notifier' |
8 | import { logger } from '../../../helpers/logger' | 8 | import { logger } from '../../../helpers/logger' |
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 3e8ad184c..b5b1a0feb 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -9,7 +9,7 @@ import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFile | |||
9 | import { Notifier } from '../../notifier' | 9 | import { Notifier } from '../../notifier' |
10 | import { createOrUpdateCacheFile } from '../cache-file' | 10 | import { createOrUpdateCacheFile } from '../cache-file' |
11 | import { createOrUpdateVideoPlaylist } from '../playlists' | 11 | import { createOrUpdateVideoPlaylist } from '../playlists' |
12 | import { forwardVideoRelatedActivity } from '../send/utils' | 12 | import { forwardVideoRelatedActivity } from '../send/shared/send-utils' |
13 | import { resolveThread } from '../video-comments' | 13 | import { resolveThread } from '../video-comments' |
14 | import { getOrCreateAPVideo } from '../videos' | 14 | import { getOrCreateAPVideo } from '../videos' |
15 | 15 | ||
@@ -55,7 +55,7 @@ export { | |||
55 | async function processCreateVideo (activity: ActivityCreate, notify: boolean) { | 55 | async function processCreateVideo (activity: ActivityCreate, notify: boolean) { |
56 | const videoToCreateData = activity.object as VideoObject | 56 | const videoToCreateData = activity.object as VideoObject |
57 | 57 | ||
58 | const syncParam = { likes: false, dislikes: false, shares: false, comments: false, thumbnail: true, refreshVideo: false } | 58 | const syncParam = { rates: false, shares: false, comments: false, thumbnail: true, refreshVideo: false } |
59 | const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam }) | 59 | const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam }) |
60 | 60 | ||
61 | if (created && notify) Notifier.Instance.notifyOnNewVideoIfNeeded(video) | 61 | if (created && notify) Notifier.Instance.notifyOnNewVideoIfNeeded(video) |
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 1d2279df5..ac0e7e235 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -16,7 +16,7 @@ import { | |||
16 | MChannelActor, | 16 | MChannelActor, |
17 | MCommentOwnerVideo | 17 | MCommentOwnerVideo |
18 | } from '../../../types/models' | 18 | } from '../../../types/models' |
19 | import { forwardVideoRelatedActivity } from '../send/utils' | 19 | import { forwardVideoRelatedActivity } from '../send/shared/send-utils' |
20 | 20 | ||
21 | async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { | 21 | async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { |
22 | const { activity, byActor } = options | 22 | const { activity, byActor } = options |
diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts index 2f46b83d1..97a994e94 100644 --- a/server/lib/activitypub/process/process-dislike.ts +++ b/server/lib/activitypub/process/process-dislike.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { VideoModel } from '@server/models/video/video' | ||
1 | import { ActivityCreate, ActivityDislike, DislikeObject } from '@shared/models' | 2 | import { ActivityCreate, ActivityDislike, DislikeObject } from '@shared/models' |
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { sequelizeTypescript } from '../../../initializers/database' | 4 | import { sequelizeTypescript } from '../../../initializers/database' |
4 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 5 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
5 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 6 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
6 | import { MActorSignature } from '../../../types/models' | 7 | import { MActorSignature } from '../../../types/models' |
7 | import { forwardVideoRelatedActivity } from '../send/utils' | 8 | import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos' |
8 | import { getOrCreateAPVideo } from '../videos' | ||
9 | 9 | ||
10 | async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { | 10 | async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { |
11 | const { activity, byActor } = options | 11 | const { activity, byActor } = options |
@@ -29,16 +29,23 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct | |||
29 | 29 | ||
30 | if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url) | 30 | if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url) |
31 | 31 | ||
32 | const { video } = await getOrCreateAPVideo({ videoObject: dislikeObject }) | 32 | const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: dislikeObject, fetchType: 'only-video' }) |
33 | |||
34 | // We don't care about dislikes of remote videos | ||
35 | if (!onlyVideo.isOwned()) return | ||
33 | 36 | ||
34 | return sequelizeTypescript.transaction(async t => { | 37 | return sequelizeTypescript.transaction(async t => { |
38 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t) | ||
39 | |||
35 | const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t) | 40 | const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t) |
36 | if (existingRate && existingRate.type === 'dislike') return | 41 | if (existingRate && existingRate.type === 'dislike') return |
37 | 42 | ||
38 | await video.increment('dislikes', { transaction: t }) | 43 | await video.increment('dislikes', { transaction: t }) |
44 | video.dislikes++ | ||
39 | 45 | ||
40 | if (existingRate && existingRate.type === 'like') { | 46 | if (existingRate && existingRate.type === 'like') { |
41 | await video.decrement('likes', { transaction: t }) | 47 | await video.decrement('likes', { transaction: t }) |
48 | video.likes-- | ||
42 | } | 49 | } |
43 | 50 | ||
44 | const rate = existingRate || new AccountVideoRateModel() | 51 | const rate = existingRate || new AccountVideoRateModel() |
@@ -49,11 +56,6 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct | |||
49 | 56 | ||
50 | await rate.save({ transaction: t }) | 57 | await rate.save({ transaction: t }) |
51 | 58 | ||
52 | if (video.isOwned()) { | 59 | await federateVideoIfNeeded(video, false, t) |
53 | // Don't resend the activity to the sender | ||
54 | const exceptions = [ byActor ] | ||
55 | |||
56 | await forwardVideoRelatedActivity(activity, t, exceptions, video) | ||
57 | } | ||
58 | }) | 60 | }) |
59 | } | 61 | } |
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts index cd4e86cbb..93afb5edf 100644 --- a/server/lib/activitypub/process/process-like.ts +++ b/server/lib/activitypub/process/process-like.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { VideoModel } from '@server/models/video/video' | ||
1 | import { ActivityLike } from '../../../../shared/models/activitypub' | 2 | import { ActivityLike } from '../../../../shared/models/activitypub' |
2 | import { getAPId } from '../../../helpers/activitypub' | 3 | import { getAPId } from '../../../helpers/activitypub' |
3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
@@ -5,11 +6,11 @@ import { sequelizeTypescript } from '../../../initializers/database' | |||
5 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
6 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 7 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
7 | import { MActorSignature } from '../../../types/models' | 8 | import { MActorSignature } from '../../../types/models' |
8 | import { forwardVideoRelatedActivity } from '../send/utils' | 9 | import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos' |
9 | import { getOrCreateAPVideo } from '../videos' | ||
10 | 10 | ||
11 | async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { | 11 | async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { |
12 | const { activity, byActor } = options | 12 | const { activity, byActor } = options |
13 | |||
13 | return retryTransactionWrapper(processLikeVideo, byActor, activity) | 14 | return retryTransactionWrapper(processLikeVideo, byActor, activity) |
14 | } | 15 | } |
15 | 16 | ||
@@ -27,17 +28,24 @@ async function processLikeVideo (byActor: MActorSignature, activity: ActivityLik | |||
27 | const byAccount = byActor.Account | 28 | const byAccount = byActor.Account |
28 | if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url) | 29 | if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url) |
29 | 30 | ||
30 | const { video } = await getOrCreateAPVideo({ videoObject: videoUrl }) | 31 | const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: videoUrl, fetchType: 'only-video' }) |
32 | |||
33 | // We don't care about likes of remote videos | ||
34 | if (!onlyVideo.isOwned()) return | ||
31 | 35 | ||
32 | return sequelizeTypescript.transaction(async t => { | 36 | return sequelizeTypescript.transaction(async t => { |
37 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t) | ||
38 | |||
33 | const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t) | 39 | const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t) |
34 | if (existingRate && existingRate.type === 'like') return | 40 | if (existingRate && existingRate.type === 'like') return |
35 | 41 | ||
36 | if (existingRate && existingRate.type === 'dislike') { | 42 | if (existingRate && existingRate.type === 'dislike') { |
37 | await video.decrement('dislikes', { transaction: t }) | 43 | await video.decrement('dislikes', { transaction: t }) |
44 | video.dislikes-- | ||
38 | } | 45 | } |
39 | 46 | ||
40 | await video.increment('likes', { transaction: t }) | 47 | await video.increment('likes', { transaction: t }) |
48 | video.likes++ | ||
41 | 49 | ||
42 | const rate = existingRate || new AccountVideoRateModel() | 50 | const rate = existingRate || new AccountVideoRateModel() |
43 | rate.type = 'like' | 51 | rate.type = 'like' |
@@ -47,11 +55,6 @@ async function processLikeVideo (byActor: MActorSignature, activity: ActivityLik | |||
47 | 55 | ||
48 | await rate.save({ transaction: t }) | 56 | await rate.save({ transaction: t }) |
49 | 57 | ||
50 | if (video.isOwned()) { | 58 | await federateVideoIfNeeded(video, false, t) |
51 | // Don't resend the activity to the sender | ||
52 | const exceptions = [ byActor ] | ||
53 | |||
54 | await forwardVideoRelatedActivity(activity, t, exceptions, video) | ||
55 | } | ||
56 | }) | 59 | }) |
57 | } | 60 | } |
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index d4b2a795f..257eb6c2b 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { VideoModel } from '@server/models/video/video' | ||
1 | import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo, CacheFileObject } from '../../../../shared/models/activitypub' | 2 | import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo, CacheFileObject } from '../../../../shared/models/activitypub' |
2 | import { DislikeObject } from '../../../../shared/models/activitypub/objects' | 3 | import { DislikeObject } from '../../../../shared/models/activitypub/objects' |
3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
@@ -10,8 +11,8 @@ import { VideoRedundancyModel } from '../../../models/redundancy/video-redundanc | |||
10 | import { VideoShareModel } from '../../../models/video/video-share' | 11 | import { VideoShareModel } from '../../../models/video/video-share' |
11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 12 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
12 | import { MActorSignature } from '../../../types/models' | 13 | import { MActorSignature } from '../../../types/models' |
13 | import { forwardVideoRelatedActivity } from '../send/utils' | 14 | import { forwardVideoRelatedActivity } from '../send/shared/send-utils' |
14 | import { getOrCreateAPVideo } from '../videos' | 15 | import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos' |
15 | 16 | ||
16 | async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { | 17 | async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { |
17 | const { activity, byActor } = options | 18 | const { activity, byActor } = options |
@@ -55,23 +56,22 @@ export { | |||
55 | async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) { | 56 | async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) { |
56 | const likeActivity = activity.object as ActivityLike | 57 | const likeActivity = activity.object as ActivityLike |
57 | 58 | ||
58 | const { video } = await getOrCreateAPVideo({ videoObject: likeActivity.object }) | 59 | const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: likeActivity.object }) |
60 | // We don't care about likes of remote videos | ||
61 | if (!onlyVideo.isOwned()) return | ||
59 | 62 | ||
60 | return sequelizeTypescript.transaction(async t => { | 63 | return sequelizeTypescript.transaction(async t => { |
61 | if (!byActor.Account) throw new Error('Unknown account ' + byActor.url) | 64 | if (!byActor.Account) throw new Error('Unknown account ' + byActor.url) |
62 | 65 | ||
66 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t) | ||
63 | const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, likeActivity.id, t) | 67 | const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, likeActivity.id, t) |
64 | if (!rate || rate.type !== 'like') throw new Error(`Unknown like by account ${byActor.Account.id} for video ${video.id}.`) | 68 | if (!rate || rate.type !== 'like') throw new Error(`Unknown like by account ${byActor.Account.id} for video ${video.id}.`) |
65 | 69 | ||
66 | await rate.destroy({ transaction: t }) | 70 | await rate.destroy({ transaction: t }) |
67 | await video.decrement('likes', { transaction: t }) | 71 | await video.decrement('likes', { transaction: t }) |
68 | 72 | ||
69 | if (video.isOwned()) { | 73 | video.likes-- |
70 | // Don't resend the activity to the sender | 74 | await federateVideoIfNeeded(video, false, t) |
71 | const exceptions = [ byActor ] | ||
72 | |||
73 | await forwardVideoRelatedActivity(activity, t, exceptions, video) | ||
74 | } | ||
75 | }) | 75 | }) |
76 | } | 76 | } |
77 | 77 | ||
@@ -80,26 +80,27 @@ async function processUndoDislike (byActor: MActorSignature, activity: ActivityU | |||
80 | ? activity.object | 80 | ? activity.object |
81 | : activity.object.object as DislikeObject | 81 | : activity.object.object as DislikeObject |
82 | 82 | ||
83 | const { video } = await getOrCreateAPVideo({ videoObject: dislike.object }) | 83 | const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: dislike.object }) |
84 | // We don't care about likes of remote videos | ||
85 | if (!onlyVideo.isOwned()) return | ||
84 | 86 | ||
85 | return sequelizeTypescript.transaction(async t => { | 87 | return sequelizeTypescript.transaction(async t => { |
86 | if (!byActor.Account) throw new Error('Unknown account ' + byActor.url) | 88 | if (!byActor.Account) throw new Error('Unknown account ' + byActor.url) |
87 | 89 | ||
90 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t) | ||
88 | const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, dislike.id, t) | 91 | const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, dislike.id, t) |
89 | if (!rate || rate.type !== 'dislike') throw new Error(`Unknown dislike by account ${byActor.Account.id} for video ${video.id}.`) | 92 | if (!rate || rate.type !== 'dislike') throw new Error(`Unknown dislike by account ${byActor.Account.id} for video ${video.id}.`) |
90 | 93 | ||
91 | await rate.destroy({ transaction: t }) | 94 | await rate.destroy({ transaction: t }) |
92 | await video.decrement('dislikes', { transaction: t }) | 95 | await video.decrement('dislikes', { transaction: t }) |
96 | video.dislikes-- | ||
93 | 97 | ||
94 | if (video.isOwned()) { | 98 | await federateVideoIfNeeded(video, false, t) |
95 | // Don't resend the activity to the sender | ||
96 | const exceptions = [ byActor ] | ||
97 | |||
98 | await forwardVideoRelatedActivity(activity, t, exceptions, video) | ||
99 | } | ||
100 | }) | 99 | }) |
101 | } | 100 | } |
102 | 101 | ||
102 | // --------------------------------------------------------------------------- | ||
103 | |||
103 | async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) { | 104 | async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) { |
104 | const cacheFileObject = activity.object.object as CacheFileObject | 105 | const cacheFileObject = activity.object.object as CacheFileObject |
105 | 106 | ||
@@ -125,19 +126,6 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit | |||
125 | }) | 126 | }) |
126 | } | 127 | } |
127 | 128 | ||
128 | function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) { | ||
129 | return sequelizeTypescript.transaction(async t => { | ||
130 | const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) | ||
131 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) | ||
132 | |||
133 | if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`) | ||
134 | |||
135 | await actorFollow.destroy({ transaction: t }) | ||
136 | |||
137 | return undefined | ||
138 | }) | ||
139 | } | ||
140 | |||
141 | function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) { | 129 | function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) { |
142 | return sequelizeTypescript.transaction(async t => { | 130 | return sequelizeTypescript.transaction(async t => { |
143 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) | 131 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) |
@@ -155,3 +143,18 @@ function processUndoAnnounce (byActor: MActorSignature, announceActivity: Activi | |||
155 | } | 143 | } |
156 | }) | 144 | }) |
157 | } | 145 | } |
146 | |||
147 | // --------------------------------------------------------------------------- | ||
148 | |||
149 | function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) { | ||
150 | return sequelizeTypescript.transaction(async t => { | ||
151 | const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) | ||
152 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) | ||
153 | |||
154 | if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`) | ||
155 | |||
156 | await actorFollow.destroy({ transaction: t }) | ||
157 | |||
158 | return undefined | ||
159 | }) | ||
160 | } | ||
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index f40008a6b..4afdbd430 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -13,7 +13,7 @@ import { MActorFull, MActorSignature } from '../../../types/models' | |||
13 | import { APActorUpdater } from '../actors/updater' | 13 | import { APActorUpdater } from '../actors/updater' |
14 | import { createOrUpdateCacheFile } from '../cache-file' | 14 | import { createOrUpdateCacheFile } from '../cache-file' |
15 | import { createOrUpdateVideoPlaylist } from '../playlists' | 15 | import { createOrUpdateVideoPlaylist } from '../playlists' |
16 | import { forwardVideoRelatedActivity } from '../send/utils' | 16 | import { forwardVideoRelatedActivity } from '../send/shared/send-utils' |
17 | import { APVideoUpdater, getOrCreateAPVideo } from '../videos' | 17 | import { APVideoUpdater, getOrCreateAPVideo } from '../videos' |
18 | 18 | ||
19 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { | 19 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { |
diff --git a/server/lib/activitypub/process/process-view.ts b/server/lib/activitypub/process/process-view.ts index 720385f9b..c59940164 100644 --- a/server/lib/activitypub/process/process-view.ts +++ b/server/lib/activitypub/process/process-view.ts | |||
@@ -2,7 +2,7 @@ import { VideoViews } from '@server/lib/video-views' | |||
2 | import { ActivityView } from '../../../../shared/models/activitypub' | 2 | import { ActivityView } from '../../../../shared/models/activitypub' |
3 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 3 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
4 | import { MActorSignature } from '../../../types/models' | 4 | import { MActorSignature } from '../../../types/models' |
5 | import { forwardVideoRelatedActivity } from '../send/utils' | 5 | import { forwardVideoRelatedActivity } from '../send/shared/send-utils' |
6 | import { getOrCreateAPVideo } from '../videos' | 6 | import { getOrCreateAPVideo } from '../videos' |
7 | 7 | ||
8 | async function processViewActivity (options: APProcessorOptions<ActivityView>) { | 8 | async function processViewActivity (options: APProcessorOptions<ActivityView>) { |
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index bb387e2c0..939f06d9e 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept, ActivityFollow } from '@shared/models' |
2 | import { logger } from '../../../helpers/logger' | 2 | import { logger } from '../../../helpers/logger' |
3 | import { MActor, MActorFollowActors } from '../../../types/models' | 3 | import { MActor, MActorFollowActors } from '../../../types/models' |
4 | import { getLocalActorFollowAcceptActivityPubUrl } from '../url' | 4 | import { getLocalActorFollowAcceptActivityPubUrl } from '../url' |
5 | import { buildFollowActivity } from './send-follow' | 5 | import { buildFollowActivity } from './send-follow' |
6 | import { unicastTo } from './utils' | 6 | import { unicastTo } from './shared/send-utils' |
7 | 7 | ||
8 | function sendAccept (actorFollow: MActorFollowActors) { | 8 | function sendAccept (actorFollow: MActorFollowActors) { |
9 | const follower = actorFollow.ActorFollower | 9 | const follower = actorFollow.ActorFollower |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index 471dcfa77..7897beb75 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' | 2 | import { ActivityAnnounce, ActivityAudience } from '@shared/models' |
3 | import { broadcastToFollowers } from './utils' | ||
4 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' | ||
5 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
6 | import { MActorLight, MVideo } from '../../../types/models' | 4 | import { MActorLight, MVideo } from '../../../types/models' |
7 | import { MVideoShare } from '../../../types/models/video' | 5 | import { MVideoShare } from '../../../types/models/video' |
6 | import { audiencify, getAudience } from '../audience' | ||
7 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf } from './shared' | ||
8 | import { broadcastToFollowers } from './shared/send-utils' | ||
8 | 9 | ||
9 | async function buildAnnounceWithVideoAudience ( | 10 | async function buildAnnounceWithVideoAudience ( |
10 | byActor: MActorLight, | 11 | byActor: MActorLight, |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index baded642a..f6d897220 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -1,11 +1,8 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' | 2 | import { getServerActor } from '@server/models/application/application' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { ActivityAudience, ActivityCreate, ContextType, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' |
4 | import { VideoCommentModel } from '../../../models/video/video-comment' | ||
5 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | ||
6 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' | ||
7 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | 4 | import { logger, loggerTagsFactory } from '../../../helpers/logger' |
8 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | 5 | import { VideoCommentModel } from '../../../models/video/video-comment' |
9 | import { | 6 | import { |
10 | MActorLight, | 7 | MActorLight, |
11 | MCommentOwnerVideo, | 8 | MCommentOwnerVideo, |
@@ -15,8 +12,16 @@ import { | |||
15 | MVideoRedundancyFileVideo, | 12 | MVideoRedundancyFileVideo, |
16 | MVideoRedundancyStreamingPlaylistVideo | 13 | MVideoRedundancyStreamingPlaylistVideo |
17 | } from '../../../types/models' | 14 | } from '../../../types/models' |
18 | import { getServerActor } from '@server/models/application/application' | 15 | import { audiencify, getAudience } from '../audience' |
19 | import { ContextType } from '@shared/models/activitypub/context' | 16 | import { |
17 | broadcastToActors, | ||
18 | broadcastToFollowers, | ||
19 | getActorsInvolvedInVideo, | ||
20 | getAudienceFromFollowersOf, | ||
21 | getVideoCommentAudience, | ||
22 | sendVideoRelatedActivity, | ||
23 | unicastTo | ||
24 | } from './shared' | ||
20 | 25 | ||
21 | const lTags = loggerTagsFactory('ap', 'create') | 26 | const lTags = loggerTagsFactory('ap', 'create') |
22 | 27 | ||
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index d31f8c10b..39216cdeb 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -1,15 +1,16 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { getServerActor } from '@server/models/application/application' | 2 | import { getServerActor } from '@server/models/application/application' |
3 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' | 3 | import { ActivityAudience, ActivityDelete } from '@shared/models' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { ActorModel } from '../../../models/actor/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { VideoCommentModel } from '../../../models/video/video-comment' | 6 | import { VideoCommentModel } from '../../../models/video/video-comment' |
7 | import { VideoShareModel } from '../../../models/video/video-share' | 7 | import { VideoShareModel } from '../../../models/video/video-share' |
8 | import { MActorUrl } from '../../../types/models' | 8 | import { MActorUrl } from '../../../types/models' |
9 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../types/models/video' | 9 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../types/models/video' |
10 | import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' | 10 | import { audiencify } from '../audience' |
11 | import { getDeleteActivityPubUrl } from '../url' | 11 | import { getDeleteActivityPubUrl } from '../url' |
12 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 12 | import { getActorsInvolvedInVideo, getVideoCommentAudience } from './shared' |
13 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './shared/send-utils' | ||
13 | 14 | ||
14 | async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) { | 15 | async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) { |
15 | logger.info('Creating job to broadcast delete of video %s.', video.url) | 16 | logger.info('Creating job to broadcast delete of video %s.', video.url) |
diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts index 274230535..ecb11e9bf 100644 --- a/server/lib/activitypub/send/send-dislike.ts +++ b/server/lib/activitypub/send/send-dislike.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { getVideoDislikeActivityPubUrlByLocalActor } from '../url' | 2 | import { ActivityAudience, ActivityDislike } from '@shared/models' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' | ||
5 | import { sendVideoRelatedActivity } from './utils' | ||
6 | import { audiencify, getAudience } from '../audience' | ||
7 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models' | 4 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models' |
5 | import { audiencify, getAudience } from '../audience' | ||
6 | import { getVideoDislikeActivityPubUrlByLocalActor } from '../url' | ||
7 | import { sendVideoActivityToOrigin } from './shared/send-utils' | ||
8 | 8 | ||
9 | function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 9 | function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
10 | logger.info('Creating job to dislike %s.', video.url) | 10 | logger.info('Creating job to dislike %s.', video.url) |
@@ -15,7 +15,7 @@ function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction | |||
15 | return buildDislikeActivity(url, byActor, video, audience) | 15 | return buildDislikeActivity(url, byActor, video, audience) |
16 | } | 16 | } |
17 | 17 | ||
18 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t }) |
19 | } | 19 | } |
20 | 20 | ||
21 | function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { | 21 | function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { |
diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts index b0483b5a0..6df4e7eb8 100644 --- a/server/lib/activitypub/send/send-flag.ts +++ b/server/lib/activitypub/send/send-flag.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityFlag } from '@shared/models' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { MAbuseAP, MAccountLight, MActor } from '../../../types/models' | 4 | import { MAbuseAP, MAccountLight, MActor } from '../../../types/models' |
5 | import { audiencify, getAudience } from '../audience' | 5 | import { audiencify, getAudience } from '../audience' |
6 | import { getLocalAbuseActivityPubUrl } from '../url' | 6 | import { getLocalAbuseActivityPubUrl } from '../url' |
7 | import { unicastTo } from './utils' | 7 | import { unicastTo } from './shared/send-utils' |
8 | 8 | ||
9 | function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLight, t: Transaction) { | 9 | function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLight, t: Transaction) { |
10 | if (!flaggedAccount.Actor.serverId) return // Local user | 10 | if (!flaggedAccount.Actor.serverId) return // Local user |
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index 9219640dd..aeeb50a2a 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 2 | import { ActivityFollow } from '@shared/models' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { MActor, MActorFollowActors } from '../../../types/models' | 4 | import { MActor, MActorFollowActors } from '../../../types/models' |
5 | import { unicastTo } from './utils' | 5 | import { unicastTo } from './shared/send-utils' |
6 | 6 | ||
7 | function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { | 7 | function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { |
8 | const me = actorFollow.ActorFollower | 8 | const me = actorFollow.ActorFollower |
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index ed6dfcf56..a5fe95e0a 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityLike } from '@shared/models' |
3 | import { getVideoLikeActivityPubUrlByLocalActor } from '../url' | ||
4 | import { sendVideoRelatedActivity } from './utils' | ||
5 | import { audiencify, getAudience } from '../audience' | ||
6 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
7 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models' | 4 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models' |
5 | import { audiencify, getAudience } from '../audience' | ||
6 | import { getVideoLikeActivityPubUrlByLocalActor } from '../url' | ||
7 | import { sendVideoActivityToOrigin } from './shared/send-utils' | ||
8 | 8 | ||
9 | function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 9 | function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
10 | logger.info('Creating job to like %s.', video.url) | 10 | logger.info('Creating job to like %s.', video.url) |
@@ -15,7 +15,7 @@ function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | |||
15 | return buildLikeActivity(url, byActor, video, audience) | 15 | return buildLikeActivity(url, byActor, video, audience) |
16 | } | 16 | } |
17 | 17 | ||
18 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t }) |
19 | } | 19 | } |
20 | 20 | ||
21 | function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { | 21 | function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { |
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts index 8d74a7848..01b8f743b 100644 --- a/server/lib/activitypub/send/send-reject.ts +++ b/server/lib/activitypub/send/send-reject.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' | 1 | import { ActivityFollow, ActivityReject } from '@shared/models' |
2 | import { logger } from '../../../helpers/logger' | 2 | import { logger } from '../../../helpers/logger' |
3 | import { MActor } from '../../../types/models' | 3 | import { MActor } from '../../../types/models' |
4 | import { getLocalActorFollowRejectActivityPubUrl } from '../url' | 4 | import { getLocalActorFollowRejectActivityPubUrl } from '../url' |
5 | import { buildFollowActivity } from './send-follow' | 5 | import { buildFollowActivity } from './send-follow' |
6 | import { unicastTo } from './utils' | 6 | import { unicastTo } from './shared/send-utils' |
7 | 7 | ||
8 | function sendReject (followUrl: string, follower: MActor, following: MActor) { | 8 | function sendReject (followUrl: string, follower: MActor, following: MActor) { |
9 | if (!follower.serverId) { // This should never happen | 9 | if (!follower.serverId) { // This should never happen |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index d2b738bef..948ca0d7a 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -7,7 +7,7 @@ import { | |||
7 | ActivityFollow, | 7 | ActivityFollow, |
8 | ActivityLike, | 8 | ActivityLike, |
9 | ActivityUndo | 9 | ActivityUndo |
10 | } from '../../../../shared/models/activitypub' | 10 | } from '@shared/models' |
11 | import { logger } from '../../../helpers/logger' | 11 | import { logger } from '../../../helpers/logger' |
12 | import { VideoModel } from '../../../models/video/video' | 12 | import { VideoModel } from '../../../models/video/video' |
13 | import { | 13 | import { |
@@ -27,7 +27,7 @@ import { buildCreateActivity } from './send-create' | |||
27 | import { buildDislikeActivity } from './send-dislike' | 27 | import { buildDislikeActivity } from './send-dislike' |
28 | import { buildFollowActivity } from './send-follow' | 28 | import { buildFollowActivity } from './send-follow' |
29 | import { buildLikeActivity } from './send-like' | 29 | import { buildLikeActivity } from './send-like' |
30 | import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 30 | import { broadcastToFollowers, sendVideoActivityToOrigin, sendVideoRelatedActivity, unicastTo } from './shared/send-utils' |
31 | 31 | ||
32 | function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { | 32 | function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { |
33 | const me = actorFollow.ActorFollower | 33 | const me = actorFollow.ActorFollower |
@@ -46,6 +46,8 @@ function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { | |||
46 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) | 46 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) |
47 | } | 47 | } |
48 | 48 | ||
49 | // --------------------------------------------------------------------------- | ||
50 | |||
49 | async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { | 51 | async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { |
50 | logger.info('Creating job to undo announce %s.', videoShare.url) | 52 | logger.info('Creating job to undo announce %s.', videoShare.url) |
51 | 53 | ||
@@ -58,13 +60,30 @@ async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, | |||
58 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) | 60 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) |
59 | } | 61 | } |
60 | 62 | ||
63 | async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { | ||
64 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) | ||
65 | |||
66 | const associatedVideo = redundancyModel.getVideo() | ||
67 | if (!associatedVideo) { | ||
68 | logger.warn('Cannot send undo activity for redundancy %s: no video files associated.', redundancyModel.url) | ||
69 | return | ||
70 | } | ||
71 | |||
72 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id) | ||
73 | const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) | ||
74 | |||
75 | return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t }) | ||
76 | } | ||
77 | |||
78 | // --------------------------------------------------------------------------- | ||
79 | |||
61 | async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 80 | async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
62 | logger.info('Creating job to undo a like of video %s.', video.url) | 81 | logger.info('Creating job to undo a like of video %s.', video.url) |
63 | 82 | ||
64 | const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video) | 83 | const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video) |
65 | const likeActivity = buildLikeActivity(likeUrl, byActor, video) | 84 | const likeActivity = buildLikeActivity(likeUrl, byActor, video) |
66 | 85 | ||
67 | return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) | 86 | return sendUndoVideoToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) |
68 | } | 87 | } |
69 | 88 | ||
70 | async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { | 89 | async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
@@ -73,22 +92,7 @@ async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: T | |||
73 | const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video) | 92 | const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video) |
74 | const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video) | 93 | const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video) |
75 | 94 | ||
76 | return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) | 95 | return sendUndoVideoToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) |
77 | } | ||
78 | |||
79 | async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { | ||
80 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) | ||
81 | |||
82 | const associatedVideo = redundancyModel.getVideo() | ||
83 | if (!associatedVideo) { | ||
84 | logger.warn('Cannot send undo activity for redundancy %s: no video files associated.', redundancyModel.url) | ||
85 | return | ||
86 | } | ||
87 | |||
88 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id) | ||
89 | const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) | ||
90 | |||
91 | return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t }) | ||
92 | } | 96 | } |
93 | 97 | ||
94 | // --------------------------------------------------------------------------- | 98 | // --------------------------------------------------------------------------- |
@@ -126,7 +130,7 @@ async function sendUndoVideoRelatedActivity (options: { | |||
126 | byActor: MActor | 130 | byActor: MActor |
127 | video: MVideoAccountLight | 131 | video: MVideoAccountLight |
128 | url: string | 132 | url: string |
129 | activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce | 133 | activity: ActivityFollow | ActivityCreate | ActivityAnnounce |
130 | transaction: Transaction | 134 | transaction: Transaction |
131 | }) { | 135 | }) { |
132 | const activityBuilder = (audience: ActivityAudience) => { | 136 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -137,3 +141,19 @@ async function sendUndoVideoRelatedActivity (options: { | |||
137 | 141 | ||
138 | return sendVideoRelatedActivity(activityBuilder, options) | 142 | return sendVideoRelatedActivity(activityBuilder, options) |
139 | } | 143 | } |
144 | |||
145 | async function sendUndoVideoToOriginActivity (options: { | ||
146 | byActor: MActor | ||
147 | video: MVideoAccountLight | ||
148 | url: string | ||
149 | activity: ActivityLike | ActivityDislike | ||
150 | transaction: Transaction | ||
151 | }) { | ||
152 | const activityBuilder = (audience: ActivityAudience) => { | ||
153 | const undoUrl = getUndoActivityPubUrl(options.url) | ||
154 | |||
155 | return undoActivityData(undoUrl, options.byActor, options.activity, audience) | ||
156 | } | ||
157 | |||
158 | return sendVideoActivityToOrigin(activityBuilder, options) | ||
159 | } | ||
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index bcf6e1569..7c9e72cbc 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -1,14 +1,10 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' | 2 | import { getServerActor } from '@server/models/application/application' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { ActivityAudience, ActivityUpdate, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' |
4 | import { logger } from '../../../helpers/logger' | ||
4 | import { AccountModel } from '../../../models/account/account' | 5 | import { AccountModel } from '../../../models/account/account' |
5 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
6 | import { VideoShareModel } from '../../../models/video/video-share' | 7 | import { VideoShareModel } from '../../../models/video/video-share' |
7 | import { getUpdateActivityPubUrl } from '../url' | ||
8 | import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' | ||
9 | import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' | ||
10 | import { logger } from '../../../helpers/logger' | ||
11 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | ||
12 | import { | 8 | import { |
13 | MAccountDefault, | 9 | MAccountDefault, |
14 | MActor, | 10 | MActor, |
@@ -19,7 +15,10 @@ import { | |||
19 | MVideoPlaylistFull, | 15 | MVideoPlaylistFull, |
20 | MVideoRedundancyVideo | 16 | MVideoRedundancyVideo |
21 | } from '../../../types/models' | 17 | } from '../../../types/models' |
22 | import { getServerActor } from '@server/models/application/application' | 18 | import { audiencify, getAudience } from '../audience' |
19 | import { getUpdateActivityPubUrl } from '../url' | ||
20 | import { getActorsInvolvedInVideo } from './shared' | ||
21 | import { broadcastToFollowers, sendVideoRelatedActivity } from './shared/send-utils' | ||
23 | 22 | ||
24 | async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { | 23 | async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { |
25 | const video = videoArg as MVideoAP | 24 | const video = videoArg as MVideoAP |
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index b12583e26..1f97307b9 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { VideoViews } from '@server/lib/video-views' | 2 | import { VideoViews } from '@server/lib/video-views' |
3 | import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' | 3 | import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' |
4 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' | 4 | import { ActivityAudience, ActivityView } from '@shared/models' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { ActorModel } from '../../../models/actor/actor' | 6 | import { ActorModel } from '../../../models/actor/actor' |
7 | import { audiencify, getAudience } from '../audience' | 7 | import { audiencify, getAudience } from '../audience' |
8 | import { getLocalVideoViewActivityPubUrl } from '../url' | 8 | import { getLocalVideoViewActivityPubUrl } from '../url' |
9 | import { sendVideoRelatedActivity } from './utils' | 9 | import { sendVideoRelatedActivity } from './shared/send-utils' |
10 | 10 | ||
11 | async function sendView (byActor: ActorModel, video: MVideoImmutable, t: Transaction) { | 11 | async function sendView (byActor: ActorModel, video: MVideoImmutable, t: Transaction) { |
12 | logger.info('Creating job to send view of %s.', video.url) | 12 | logger.info('Creating job to send view of %s.', video.url) |
diff --git a/server/lib/activitypub/send/shared/audience-utils.ts b/server/lib/activitypub/send/shared/audience-utils.ts new file mode 100644 index 000000000..a5f64a08d --- /dev/null +++ b/server/lib/activitypub/send/shared/audience-utils.ts | |||
@@ -0,0 +1,74 @@ | |||
1 | import { Transaction } from 'sequelize/dist' | ||
2 | import { ACTIVITY_PUB } from '@server/initializers/constants' | ||
3 | import { ActorModel } from '@server/models/actor/actor' | ||
4 | import { VideoModel } from '@server/models/video/video' | ||
5 | import { VideoShareModel } from '@server/models/video/video-share' | ||
6 | import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '@server/types/models' | ||
7 | import { ActivityAudience } from '@shared/models' | ||
8 | |||
9 | function getOriginVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[] = []): ActivityAudience { | ||
10 | return { | ||
11 | to: [ accountActor.url ], | ||
12 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) | ||
13 | } | ||
14 | } | ||
15 | |||
16 | function getVideoCommentAudience ( | ||
17 | videoComment: MCommentOwnerVideo, | ||
18 | threadParentComments: MCommentOwner[], | ||
19 | actorsInvolvedInVideo: MActorFollowersUrl[], | ||
20 | isOrigin = false | ||
21 | ): ActivityAudience { | ||
22 | const to = [ ACTIVITY_PUB.PUBLIC ] | ||
23 | const cc: string[] = [] | ||
24 | |||
25 | // Owner of the video we comment | ||
26 | if (isOrigin === false) { | ||
27 | cc.push(videoComment.Video.VideoChannel.Account.Actor.url) | ||
28 | } | ||
29 | |||
30 | // Followers of the poster | ||
31 | cc.push(videoComment.Account.Actor.followersUrl) | ||
32 | |||
33 | // Send to actors we reply to | ||
34 | for (const parentComment of threadParentComments) { | ||
35 | if (parentComment.isDeleted()) continue | ||
36 | |||
37 | cc.push(parentComment.Account.Actor.url) | ||
38 | } | ||
39 | |||
40 | return { | ||
41 | to, | ||
42 | cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl)) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience { | ||
47 | return { | ||
48 | to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), | ||
49 | cc: [] | ||
50 | } | ||
51 | } | ||
52 | |||
53 | async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) { | ||
54 | const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) | ||
55 | |||
56 | const videoAll = video as VideoModel | ||
57 | |||
58 | const videoActor = videoAll.VideoChannel?.Account | ||
59 | ? videoAll.VideoChannel.Account.Actor | ||
60 | : await ActorModel.loadFromAccountByVideoId(video.id, t) | ||
61 | |||
62 | actors.push(videoActor) | ||
63 | |||
64 | return actors | ||
65 | } | ||
66 | |||
67 | // --------------------------------------------------------------------------- | ||
68 | |||
69 | export { | ||
70 | getOriginVideoAudience, | ||
71 | getActorsInvolvedInVideo, | ||
72 | getAudienceFromFollowersOf, | ||
73 | getVideoCommentAudience | ||
74 | } | ||
diff --git a/server/lib/activitypub/send/shared/index.ts b/server/lib/activitypub/send/shared/index.ts new file mode 100644 index 000000000..bda579115 --- /dev/null +++ b/server/lib/activitypub/send/shared/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './audience-utils' | ||
2 | export * from './send-utils' | ||
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/shared/send-utils.ts index 7729703b8..9e8f12fa8 100644 --- a/server/lib/activitypub/send/utils.ts +++ b/server/lib/activitypub/send/shared/send-utils.ts | |||
@@ -1,15 +1,15 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' | 2 | import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' |
3 | import { getServerActor } from '@server/models/application/application' | 3 | import { getServerActor } from '@server/models/application/application' |
4 | import { Activity, ActivityAudience } from '@shared/models' | ||
4 | import { ContextType } from '@shared/models/activitypub/context' | 5 | import { ContextType } from '@shared/models/activitypub/context' |
5 | import { Activity, ActivityAudience } from '../../../../shared/models/activitypub' | 6 | import { afterCommitIfTransaction } from '../../../../helpers/database-utils' |
6 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | 7 | import { logger } from '../../../../helpers/logger' |
7 | import { logger } from '../../../helpers/logger' | 8 | import { ActorModel } from '../../../../models/actor/actor' |
8 | import { ActorModel } from '../../../models/actor/actor' | 9 | import { ActorFollowModel } from '../../../../models/actor/actor-follow' |
9 | import { ActorFollowModel } from '../../../models/actor/actor-follow' | 10 | import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../../types/models' |
10 | import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models' | 11 | import { JobQueue } from '../../../job-queue' |
11 | import { JobQueue } from '../../job-queue' | 12 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getOriginVideoAudience } from './audience-utils' |
12 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' | ||
13 | 13 | ||
14 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 14 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
15 | byActor: MActorLight | 15 | byActor: MActorLight |
@@ -23,16 +23,7 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud | |||
23 | 23 | ||
24 | // Send to origin | 24 | // Send to origin |
25 | if (video.isOwned() === false) { | 25 | if (video.isOwned() === false) { |
26 | let accountActor: MActorLight = (video as MVideoAccountLight).VideoChannel?.Account?.Actor | 26 | return sendVideoActivityToOrigin(activityBuilder, options) |
27 | |||
28 | if (!accountActor) accountActor = await ActorModel.loadAccountActorByVideoId(video.id, transaction) | ||
29 | |||
30 | const audience = getRemoteVideoAudience(accountActor, actorsInvolvedInVideo) | ||
31 | const activity = activityBuilder(audience) | ||
32 | |||
33 | return afterCommitIfTransaction(transaction, () => { | ||
34 | return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType) | ||
35 | }) | ||
36 | } | 27 | } |
37 | 28 | ||
38 | // Send to followers | 29 | // Send to followers |
@@ -44,6 +35,30 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud | |||
44 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType) | 35 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType) |
45 | } | 36 | } |
46 | 37 | ||
38 | async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAudience) => Activity, options: { | ||
39 | byActor: MActorLight | ||
40 | video: MVideoImmutable | MVideoAccountLight | ||
41 | actorsInvolvedInVideo?: MActorLight[] | ||
42 | transaction?: Transaction | ||
43 | contextType?: ContextType | ||
44 | }) { | ||
45 | const { byActor, video, actorsInvolvedInVideo, transaction, contextType } = options | ||
46 | |||
47 | if (video.isOwned()) throw new Error('Cannot send activity to owned video origin ' + video.url) | ||
48 | |||
49 | let accountActor: MActorLight = (video as MVideoAccountLight).VideoChannel?.Account?.Actor | ||
50 | if (!accountActor) accountActor = await ActorModel.loadAccountActorByVideoId(video.id, transaction) | ||
51 | |||
52 | const audience = getOriginVideoAudience(accountActor, actorsInvolvedInVideo) | ||
53 | const activity = activityBuilder(audience) | ||
54 | |||
55 | return afterCommitIfTransaction(transaction, () => { | ||
56 | return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType) | ||
57 | }) | ||
58 | } | ||
59 | |||
60 | // --------------------------------------------------------------------------- | ||
61 | |||
47 | async function forwardVideoRelatedActivity ( | 62 | async function forwardVideoRelatedActivity ( |
48 | activity: Activity, | 63 | activity: Activity, |
49 | t: Transaction, | 64 | t: Transaction, |
@@ -92,6 +107,8 @@ async function forwardActivity ( | |||
92 | return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })) | 107 | return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })) |
93 | } | 108 | } |
94 | 109 | ||
110 | // --------------------------------------------------------------------------- | ||
111 | |||
95 | async function broadcastToFollowers ( | 112 | async function broadcastToFollowers ( |
96 | data: any, | 113 | data: any, |
97 | byActor: MActorId, | 114 | byActor: MActorId, |
@@ -177,6 +194,7 @@ export { | |||
177 | unicastTo, | 194 | unicastTo, |
178 | forwardActivity, | 195 | forwardActivity, |
179 | broadcastToActors, | 196 | broadcastToActors, |
197 | sendVideoActivityToOrigin, | ||
180 | forwardVideoRelatedActivity, | 198 | forwardVideoRelatedActivity, |
181 | sendVideoRelatedActivity | 199 | sendVideoRelatedActivity |
182 | } | 200 | } |
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts index 2a14790fe..2c7da3e00 100644 --- a/server/lib/activitypub/video-comments.ts +++ b/server/lib/activitypub/video-comments.ts | |||
@@ -87,7 +87,7 @@ async function tryToResolveThreadFromVideo (params: ResolveThreadParams) { | |||
87 | 87 | ||
88 | // Maybe it's a reply to a video? | 88 | // Maybe it's a reply to a video? |
89 | // If yes, it's done: we resolved all the thread | 89 | // If yes, it's done: we resolved all the thread |
90 | const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } | 90 | const syncParam = { rates: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } |
91 | const { video } = await getOrCreateAPVideo({ videoObject: url, syncParam }) | 91 | const { video } = await getOrCreateAPVideo({ videoObject: url, syncParam }) |
92 | 92 | ||
93 | if (video.isOwned() && !video.hasPrivacyForFederation()) { | 93 | if (video.isOwned() && !video.hasPrivacyForFederation()) { |
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts index 04aa5eae9..2e7920f4e 100644 --- a/server/lib/activitypub/video-rates.ts +++ b/server/lib/activitypub/video-rates.ts | |||
@@ -1,49 +1,21 @@ | |||
1 | import { map } from 'bluebird' | ||
2 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
3 | import { doJSONRequest } from '@server/helpers/requests' | ||
4 | import { VideoRateType } from '../../../shared/models/videos' | 2 | import { VideoRateType } from '../../../shared/models/videos' |
5 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 3 | import { MAccountActor, MActorUrl, MVideoAccountLight, MVideoFullLight, MVideoId } from '../../types/models' |
6 | import { logger, loggerTagsFactory } from '../../helpers/logger' | ||
7 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | ||
8 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | ||
9 | import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../types/models' | ||
10 | import { getOrCreateAPActor } from './actors' | ||
11 | import { sendLike, sendUndoDislike, sendUndoLike } from './send' | 4 | import { sendLike, sendUndoDislike, sendUndoLike } from './send' |
12 | import { sendDislike } from './send/send-dislike' | 5 | import { sendDislike } from './send/send-dislike' |
13 | import { getVideoDislikeActivityPubUrlByLocalActor, getVideoLikeActivityPubUrlByLocalActor } from './url' | 6 | import { getVideoDislikeActivityPubUrlByLocalActor, getVideoLikeActivityPubUrlByLocalActor } from './url' |
14 | 7 | import { federateVideoIfNeeded } from './videos' | |
15 | const lTags = loggerTagsFactory('ap', 'video-rate', 'create') | ||
16 | |||
17 | async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) { | ||
18 | await map(ratesUrl, async rateUrl => { | ||
19 | try { | ||
20 | await createRate(rateUrl, video, rate) | ||
21 | } catch (err) { | ||
22 | logger.info('Cannot add rate %s.', rateUrl, { err, ...lTags(rateUrl, video.uuid, video.url) }) | ||
23 | } | ||
24 | }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) | ||
25 | } | ||
26 | 8 | ||
27 | async function sendVideoRateChange ( | 9 | async function sendVideoRateChange ( |
28 | account: MAccountActor, | 10 | account: MAccountActor, |
29 | video: MVideoAccountLight, | 11 | video: MVideoFullLight, |
30 | likes: number, | 12 | likes: number, |
31 | dislikes: number, | 13 | dislikes: number, |
32 | t: Transaction | 14 | t: Transaction |
33 | ) { | 15 | ) { |
34 | const actor = account.Actor | 16 | if (video.isOwned()) return federateVideoIfNeeded(video, false, t) |
35 | 17 | ||
36 | // Keep the order: first we undo and then we create | 18 | return sendVideoRateChangeToOrigin(account, video, likes, dislikes, t) |
37 | |||
38 | // Undo Like | ||
39 | if (likes < 0) await sendUndoLike(actor, video, t) | ||
40 | // Undo Dislike | ||
41 | if (dislikes < 0) await sendUndoDislike(actor, video, t) | ||
42 | |||
43 | // Like | ||
44 | if (likes > 0) await sendLike(actor, video, t) | ||
45 | // Dislike | ||
46 | if (dislikes > 0) await sendDislike(actor, video, t) | ||
47 | } | 19 | } |
48 | 20 | ||
49 | function getLocalRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) { | 21 | function getLocalRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) { |
@@ -56,35 +28,32 @@ function getLocalRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVid | |||
56 | 28 | ||
57 | export { | 29 | export { |
58 | getLocalRateUrl, | 30 | getLocalRateUrl, |
59 | createRates, | ||
60 | sendVideoRateChange | 31 | sendVideoRateChange |
61 | } | 32 | } |
62 | 33 | ||
63 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
64 | 35 | ||
65 | async function createRate (rateUrl: string, video: MVideo, rate: VideoRateType) { | 36 | async function sendVideoRateChangeToOrigin ( |
66 | // Fetch url | 37 | account: MAccountActor, |
67 | const { body } = await doJSONRequest<any>(rateUrl, { activityPub: true }) | 38 | video: MVideoAccountLight, |
68 | if (!body || !body.actor) throw new Error('Body or body actor is invalid') | 39 | likes: number, |
69 | 40 | dislikes: number, | |
70 | const actorUrl = getAPId(body.actor) | 41 | t: Transaction |
71 | if (checkUrlsSameHost(actorUrl, rateUrl) !== true) { | 42 | ) { |
72 | throw new Error(`Rate url ${rateUrl} has not the same host than actor url ${actorUrl}`) | 43 | // Local video, we don't need to send like |
73 | } | 44 | if (video.isOwned()) return |
74 | 45 | ||
75 | if (checkUrlsSameHost(body.id, rateUrl) !== true) { | 46 | const actor = account.Actor |
76 | throw new Error(`Rate url ${rateUrl} host is different from the AP object id ${body.id}`) | ||
77 | } | ||
78 | 47 | ||
79 | const actor = await getOrCreateAPActor(actorUrl) | 48 | // Keep the order: first we undo and then we create |
80 | 49 | ||
81 | const entry = { | 50 | // Undo Like |
82 | videoId: video.id, | 51 | if (likes < 0) await sendUndoLike(actor, video, t) |
83 | accountId: actor.Account.id, | 52 | // Undo Dislike |
84 | type: rate, | 53 | if (dislikes < 0) await sendUndoDislike(actor, video, t) |
85 | url: body.id | ||
86 | } | ||
87 | 54 | ||
88 | // Video "likes"/"dislikes" will be updated by the caller | 55 | // Like |
89 | await AccountVideoRateModel.upsert(entry) | 56 | if (likes > 0) await sendLike(actor, video, t) |
57 | // Dislike | ||
58 | if (dislikes > 0) await sendDislike(actor, video, t) | ||
90 | } | 59 | } |
diff --git a/server/lib/activitypub/videos/get.ts b/server/lib/activitypub/videos/get.ts index f3e2f0625..b13c6ceeb 100644 --- a/server/lib/activitypub/videos/get.ts +++ b/server/lib/activitypub/videos/get.ts | |||
@@ -42,7 +42,7 @@ async function getOrCreateAPVideo ( | |||
42 | options: GetVideoParamAll | GetVideoParamImmutable | GetVideoParamOther | 42 | options: GetVideoParamAll | GetVideoParamImmutable | GetVideoParamOther |
43 | ): GetVideoResult<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail | MVideoImmutable> { | 43 | ): GetVideoResult<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail | MVideoImmutable> { |
44 | // Default params | 44 | // Default params |
45 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 45 | const syncParam = options.syncParam || { rates: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
46 | const fetchType = options.fetchType || 'all' | 46 | const fetchType = options.fetchType || 'all' |
47 | const allowRefresh = options.allowRefresh !== false | 47 | const allowRefresh = options.allowRefresh !== false |
48 | 48 | ||
diff --git a/server/lib/activitypub/videos/shared/video-sync-attributes.ts b/server/lib/activitypub/videos/shared/video-sync-attributes.ts index c4e101005..8cf0c87a6 100644 --- a/server/lib/activitypub/videos/shared/video-sync-attributes.ts +++ b/server/lib/activitypub/videos/shared/video-sync-attributes.ts | |||
@@ -1,20 +1,20 @@ | |||
1 | import { runInReadCommittedTransaction } from '@server/helpers/database-utils' | ||
1 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | 2 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
3 | import { doJSONRequest } from '@server/helpers/requests' | ||
2 | import { JobQueue } from '@server/lib/job-queue' | 4 | import { JobQueue } from '@server/lib/job-queue' |
3 | import { AccountVideoRateModel } from '@server/models/account/account-video-rate' | 5 | import { VideoModel } from '@server/models/video/video' |
4 | import { VideoCommentModel } from '@server/models/video/video-comment' | 6 | import { VideoCommentModel } from '@server/models/video/video-comment' |
5 | import { VideoShareModel } from '@server/models/video/video-share' | 7 | import { VideoShareModel } from '@server/models/video/video-share' |
6 | import { MVideo } from '@server/types/models' | 8 | import { MVideo } from '@server/types/models' |
7 | import { ActivitypubHttpFetcherPayload, VideoObject } from '@shared/models' | 9 | import { ActivitypubHttpFetcherPayload, ActivityPubOrderedCollection, VideoObject } from '@shared/models' |
8 | import { crawlCollectionPage } from '../../crawl' | 10 | import { crawlCollectionPage } from '../../crawl' |
9 | import { addVideoShares } from '../../share' | 11 | import { addVideoShares } from '../../share' |
10 | import { addVideoComments } from '../../video-comments' | 12 | import { addVideoComments } from '../../video-comments' |
11 | import { createRates } from '../../video-rates' | ||
12 | 13 | ||
13 | const lTags = loggerTagsFactory('ap', 'video') | 14 | const lTags = loggerTagsFactory('ap', 'video') |
14 | 15 | ||
15 | type SyncParam = { | 16 | type SyncParam = { |
16 | likes: boolean | 17 | rates: boolean |
17 | dislikes: boolean | ||
18 | shares: boolean | 18 | shares: boolean |
19 | comments: boolean | 19 | comments: boolean |
20 | thumbnail: boolean | 20 | thumbnail: boolean |
@@ -24,45 +24,57 @@ type SyncParam = { | |||
24 | async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoObject, syncParam: SyncParam) { | 24 | async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoObject, syncParam: SyncParam) { |
25 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) | 25 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) |
26 | 26 | ||
27 | await syncRates('like', video, fetchedVideo, syncParam.likes) | 27 | const ratePromise = updateVideoRates(video, fetchedVideo) |
28 | await syncRates('dislike', video, fetchedVideo, syncParam.dislikes) | 28 | if (syncParam.rates) await ratePromise |
29 | 29 | ||
30 | await syncShares(video, fetchedVideo, syncParam.shares) | 30 | await syncShares(video, fetchedVideo, syncParam.shares) |
31 | 31 | ||
32 | await syncComments(video, fetchedVideo, syncParam.comments) | 32 | await syncComments(video, fetchedVideo, syncParam.comments) |
33 | } | 33 | } |
34 | 34 | ||
35 | async function updateVideoRates (video: MVideo, fetchedVideo: VideoObject) { | ||
36 | const [ likes, dislikes ] = await Promise.all([ | ||
37 | getRatesCount('like', video, fetchedVideo), | ||
38 | getRatesCount('dislike', video, fetchedVideo) | ||
39 | ]) | ||
40 | |||
41 | return runInReadCommittedTransaction(async t => { | ||
42 | await VideoModel.updateRatesOf(video.id, 'like', likes, t) | ||
43 | await VideoModel.updateRatesOf(video.id, 'dislike', dislikes, t) | ||
44 | }) | ||
45 | } | ||
46 | |||
35 | // --------------------------------------------------------------------------- | 47 | // --------------------------------------------------------------------------- |
36 | 48 | ||
37 | export { | 49 | export { |
38 | SyncParam, | 50 | SyncParam, |
39 | syncVideoExternalAttributes | 51 | syncVideoExternalAttributes, |
52 | updateVideoRates | ||
40 | } | 53 | } |
41 | 54 | ||
42 | // --------------------------------------------------------------------------- | 55 | // --------------------------------------------------------------------------- |
43 | 56 | ||
44 | function createJob (payload: ActivitypubHttpFetcherPayload) { | 57 | async function getRatesCount (type: 'like' | 'dislike', video: MVideo, fetchedVideo: VideoObject) { |
45 | return JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload }) | ||
46 | } | ||
47 | |||
48 | function syncRates (type: 'like' | 'dislike', video: MVideo, fetchedVideo: VideoObject, isSync: boolean) { | ||
49 | const uri = type === 'like' | 58 | const uri = type === 'like' |
50 | ? fetchedVideo.likes | 59 | ? fetchedVideo.likes |
51 | : fetchedVideo.dislikes | 60 | : fetchedVideo.dislikes |
52 | 61 | ||
53 | if (!isSync) { | 62 | logger.info('Sync %s of video %s', type, video.url) |
54 | const jobType = type === 'like' | 63 | const options = { activityPub: true } |
55 | ? 'video-likes' | 64 | |
56 | : 'video-dislikes' | 65 | const response = await doJSONRequest<ActivityPubOrderedCollection<any>>(uri, options) |
66 | const totalItems = response.body.totalItems | ||
57 | 67 | ||
58 | return createJob({ uri, videoId: video.id, type: jobType }) | 68 | if (isNaN(totalItems)) { |
69 | logger.error('Cannot sync %s of video %s, totalItems is not a number', type, video.url, { body: response.body }) | ||
70 | return | ||
59 | } | 71 | } |
60 | 72 | ||
61 | const handler = items => createRates(items, video, type) | 73 | return totalItems |
62 | const cleaner = crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, type, crawlStartDate) | 74 | } |
63 | 75 | ||
64 | return crawlCollectionPage<string>(uri, handler, cleaner) | 76 | function createJob (payload: ActivitypubHttpFetcherPayload) { |
65 | .catch(err => logger.error('Cannot add rate of video %s.', video.uuid, { err, rootUrl: uri, ...lTags(video.uuid, video.url) })) | 77 | return JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload }) |
66 | } | 78 | } |
67 | 79 | ||
68 | function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) { | 80 | function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) { |
diff --git a/server/lib/activitypub/videos/updater.ts b/server/lib/activitypub/videos/updater.ts index f786bb196..32cbf7e07 100644 --- a/server/lib/activitypub/videos/updater.ts +++ b/server/lib/activitypub/videos/updater.ts | |||
@@ -7,7 +7,7 @@ import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist' | |||
7 | import { VideoLiveModel } from '@server/models/video/video-live' | 7 | import { VideoLiveModel } from '@server/models/video/video-live' |
8 | import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models' | 8 | import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models' |
9 | import { VideoObject, VideoPrivacy } from '@shared/models' | 9 | import { VideoObject, VideoPrivacy } from '@shared/models' |
10 | import { APVideoAbstractBuilder, getVideoAttributesFromObject } from './shared' | 10 | import { APVideoAbstractBuilder, getVideoAttributesFromObject, updateVideoRates } from './shared' |
11 | 11 | ||
12 | export class APVideoUpdater extends APVideoAbstractBuilder { | 12 | export class APVideoUpdater extends APVideoAbstractBuilder { |
13 | private readonly wasPrivateVideo: boolean | 13 | private readonly wasPrivateVideo: boolean |
@@ -74,6 +74,8 @@ export class APVideoUpdater extends APVideoAbstractBuilder { | |||
74 | transaction: undefined | 74 | transaction: undefined |
75 | }) | 75 | }) |
76 | 76 | ||
77 | await updateVideoRates(videoUpdated, this.videoObject) | ||
78 | |||
77 | // Notify our users? | 79 | // Notify our users? |
78 | if (this.wasPrivateVideo || this.wasUnlistedVideo) { | 80 | if (this.wasPrivateVideo || this.wasUnlistedVideo) { |
79 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) | 81 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) |
diff --git a/server/lib/job-queue/handlers/activitypub-cleaner.ts b/server/lib/job-queue/handlers/activitypub-cleaner.ts index 509dd1cb5..07dd908cd 100644 --- a/server/lib/job-queue/handlers/activitypub-cleaner.ts +++ b/server/lib/job-queue/handlers/activitypub-cleaner.ts | |||
@@ -34,7 +34,7 @@ async function processActivityPubCleaner (_job: Job) { | |||
34 | if (result?.status === 'deleted') { | 34 | if (result?.status === 'deleted') { |
35 | const { videoId, type } = result.data | 35 | const { videoId, type } = result.data |
36 | 36 | ||
37 | await VideoModel.updateRatesOf(videoId, type, undefined) | 37 | await VideoModel.syncLocalRates(videoId, type, undefined) |
38 | } | 38 | } |
39 | }, { concurrency: AP_CLEANER.CONCURRENCY }) | 39 | }, { concurrency: AP_CLEANER.CONCURRENCY }) |
40 | } | 40 | } |
diff --git a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts index 46016a0a7..128e14f94 100644 --- a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts +++ b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { Job } from 'bull' | 1 | import { Job } from 'bull' |
2 | import { ActivitypubHttpFetcherPayload, FetchType } from '@shared/models' | 2 | import { ActivitypubHttpFetcherPayload, FetchType } from '@shared/models' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | ||
5 | import { VideoModel } from '../../../models/video/video' | 4 | import { VideoModel } from '../../../models/video/video' |
6 | import { VideoCommentModel } from '../../../models/video/video-comment' | 5 | import { VideoCommentModel } from '../../../models/video/video-comment' |
7 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
@@ -11,7 +10,6 @@ import { createAccountPlaylists } from '../../activitypub/playlists' | |||
11 | import { processActivities } from '../../activitypub/process' | 10 | import { processActivities } from '../../activitypub/process' |
12 | import { addVideoShares } from '../../activitypub/share' | 11 | import { addVideoShares } from '../../activitypub/share' |
13 | import { addVideoComments } from '../../activitypub/video-comments' | 12 | import { addVideoComments } from '../../activitypub/video-comments' |
14 | import { createRates } from '../../activitypub/video-rates' | ||
15 | 13 | ||
16 | async function processActivityPubHttpFetcher (job: Job) { | 14 | async function processActivityPubHttpFetcher (job: Job) { |
17 | logger.info('Processing ActivityPub fetcher in job %d.', job.id) | 15 | logger.info('Processing ActivityPub fetcher in job %d.', job.id) |
@@ -23,16 +21,12 @@ async function processActivityPubHttpFetcher (job: Job) { | |||
23 | 21 | ||
24 | const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { | 22 | const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { |
25 | 'activity': items => processActivities(items, { outboxUrl: payload.uri, fromFetch: true }), | 23 | 'activity': items => processActivities(items, { outboxUrl: payload.uri, fromFetch: true }), |
26 | 'video-likes': items => createRates(items, video, 'like'), | ||
27 | 'video-dislikes': items => createRates(items, video, 'dislike'), | ||
28 | 'video-shares': items => addVideoShares(items, video), | 24 | 'video-shares': items => addVideoShares(items, video), |
29 | 'video-comments': items => addVideoComments(items), | 25 | 'video-comments': items => addVideoComments(items), |
30 | 'account-playlists': items => createAccountPlaylists(items) | 26 | 'account-playlists': items => createAccountPlaylists(items) |
31 | } | 27 | } |
32 | 28 | ||
33 | const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Promise<any> } = { | 29 | const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Promise<any> } = { |
34 | 'video-likes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'like' as 'like', crawlStartDate), | ||
35 | 'video-dislikes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'dislike' as 'dislike', crawlStartDate), | ||
36 | 'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate), | 30 | 'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate), |
37 | 'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate) | 31 | 'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate) |
38 | } | 32 | } |
diff --git a/server/lib/job-queue/handlers/activitypub-refresher.ts b/server/lib/job-queue/handlers/activitypub-refresher.ts index 5037992d2..92ceed180 100644 --- a/server/lib/job-queue/handlers/activitypub-refresher.ts +++ b/server/lib/job-queue/handlers/activitypub-refresher.ts | |||
@@ -28,7 +28,7 @@ export { | |||
28 | 28 | ||
29 | async function refreshVideo (videoUrl: string) { | 29 | async function refreshVideo (videoUrl: string) { |
30 | const fetchType = 'all' as 'all' | 30 | const fetchType = 'all' as 'all' |
31 | const syncParam = { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true } | 31 | const syncParam = { rates: true, shares: true, comments: true, thumbnail: true } |
32 | 32 | ||
33 | const videoFromDatabase = await loadVideoByUrl(videoUrl, fetchType) | 33 | const videoFromDatabase = await loadVideoByUrl(videoUrl, fetchType) |
34 | if (videoFromDatabase) { | 34 | if (videoFromDatabase) { |
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts index 16562ad0b..91c217615 100644 --- a/server/lib/schedulers/videos-redundancy-scheduler.ts +++ b/server/lib/schedulers/videos-redundancy-scheduler.ts | |||
@@ -352,7 +352,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
352 | // We need more attributes and check if the video still exists | 352 | // We need more attributes and check if the video still exists |
353 | const getVideoOptions = { | 353 | const getVideoOptions = { |
354 | videoObject: videoUrl, | 354 | videoObject: videoUrl, |
355 | syncParam: { likes: false, dislikes: false, shares: false, comments: false, thumbnail: false, refreshVideo: true }, | 355 | syncParam: { rates: false, shares: false, comments: false, thumbnail: false, refreshVideo: true }, |
356 | fetchType: 'all' as 'all' | 356 | fetchType: 'all' as 'all' |
357 | } | 357 | } |
358 | const { video } = await getOrCreateAPVideo(getVideoOptions) | 358 | const { video } = await getOrCreateAPVideo(getVideoOptions) |
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts index 7303651eb..5c7d9cfc0 100644 --- a/server/models/account/account-video-rate.ts +++ b/server/models/account/account-video-rate.ts | |||
@@ -12,7 +12,7 @@ import { AttributesOnly } from '@shared/typescript-utils' | |||
12 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 12 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
13 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' | 13 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' |
14 | import { ActorModel } from '../actor/actor' | 14 | import { ActorModel } from '../actor/actor' |
15 | import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' | 15 | import { getSort, throwIfNotValid } from '../utils' |
16 | import { VideoModel } from '../video/video' | 16 | import { VideoModel } from '../video/video' |
17 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' | 17 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' |
18 | import { AccountModel } from './account' | 18 | import { AccountModel } from './account' |
@@ -249,28 +249,6 @@ export class AccountVideoRateModel extends Model<Partial<AttributesOnly<AccountV | |||
249 | ]).then(([ total, data ]) => ({ total, data })) | 249 | ]).then(([ total, data ]) => ({ total, data })) |
250 | } | 250 | } |
251 | 251 | ||
252 | static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) { | ||
253 | return AccountVideoRateModel.sequelize.transaction(async t => { | ||
254 | const query = { | ||
255 | where: { | ||
256 | updatedAt: { | ||
257 | [Op.lt]: beforeUpdatedAt | ||
258 | }, | ||
259 | videoId, | ||
260 | type, | ||
261 | accountId: { | ||
262 | [Op.notIn]: buildLocalAccountIdsIn() | ||
263 | } | ||
264 | }, | ||
265 | transaction: t | ||
266 | } | ||
267 | |||
268 | await AccountVideoRateModel.destroy(query) | ||
269 | |||
270 | return VideoModel.updateRatesOf(videoId, type, t) | ||
271 | }) | ||
272 | } | ||
273 | |||
274 | toFormattedJSON (this: MAccountVideoRateFormattable): AccountVideoRate { | 252 | toFormattedJSON (this: MAccountVideoRateFormattable): AccountVideoRate { |
275 | return { | 253 | return { |
276 | video: this.Video.toFormattedJSON(), | 254 | video: this.Video.toFormattedJSON(), |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 4147b3d62..8bad2a01e 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1402,7 +1402,21 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { | |||
1402 | }) | 1402 | }) |
1403 | } | 1403 | } |
1404 | 1404 | ||
1405 | static updateRatesOf (videoId: number, type: VideoRateType, t: Transaction) { | 1405 | static updateRatesOf (videoId: number, type: VideoRateType, count: number, t: Transaction) { |
1406 | const field = type === 'like' | ||
1407 | ? 'likes' | ||
1408 | : 'dislikes' | ||
1409 | |||
1410 | const rawQuery = `UPDATE "video" SET "${field}" = :count WHERE "video"."id" = :videoId` | ||
1411 | |||
1412 | return AccountVideoRateModel.sequelize.query(rawQuery, { | ||
1413 | transaction: t, | ||
1414 | replacements: { videoId, rateType: type, count }, | ||
1415 | type: QueryTypes.UPDATE | ||
1416 | }) | ||
1417 | } | ||
1418 | |||
1419 | static syncLocalRates (videoId: number, type: VideoRateType, t: Transaction) { | ||
1406 | const field = type === 'like' | 1420 | const field = type === 'like' |
1407 | ? 'likes' | 1421 | ? 'likes' |
1408 | : 'dislikes' | 1422 | : 'dislikes' |
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 05ccee8ad..a9df262dc 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts | |||
@@ -606,8 +606,8 @@ describe('Test multiple servers', function () { | |||
606 | 606 | ||
607 | for (const baseVideo of baseVideos) { | 607 | for (const baseVideo of baseVideos) { |
608 | const sameVideo = data.find(video => video.name === baseVideo.name) | 608 | const sameVideo = data.find(video => video.name === baseVideo.name) |
609 | expect(baseVideo.likes).to.equal(sameVideo.likes) | 609 | expect(baseVideo.likes).to.equal(sameVideo.likes, `Likes of ${sameVideo.uuid} do not correspond`) |
610 | expect(baseVideo.dislikes).to.equal(sameVideo.dislikes) | 610 | expect(baseVideo.dislikes).to.equal(sameVideo.dislikes, `Dislikes of ${sameVideo.uuid} do not correspond`) |
611 | } | 611 | } |
612 | } | 612 | } |
613 | }) | 613 | }) |