aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+admin/system/jobs/jobs.component.ts1
-rw-r--r--scripts/simulate-many-viewers.ts89
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/lib/activitypub/local-video-viewer.ts6
-rw-r--r--server/lib/activitypub/send/send-view.ts2
-rw-r--r--server/lib/activitypub/send/shared/audience-utils.ts6
-rw-r--r--server/lib/activitypub/send/shared/send-utils.ts23
-rw-r--r--server/lib/job-queue/job-queue.ts3
-rw-r--r--server/lib/views/shared/video-viewer-counters.ts1
-rw-r--r--server/models/actor/actor.ts61
-rw-r--r--server/models/video/video-share.ts25
-rw-r--r--shared/models/server/job.model.ts1
12 files changed, 154 insertions, 69 deletions
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts
index f7e10fd04..42f503be6 100644
--- a/client/src/app/+admin/system/jobs/jobs.component.ts
+++ b/client/src/app/+admin/system/jobs/jobs.component.ts
@@ -25,6 +25,7 @@ export class JobsComponent extends RestTable implements OnInit {
25 25
26 'activitypub-follow', 26 'activitypub-follow',
27 'activitypub-http-broadcast', 27 'activitypub-http-broadcast',
28 'activitypub-http-broadcast-parallel',
28 'activitypub-http-fetcher', 29 'activitypub-http-fetcher',
29 'activitypub-http-unicast', 30 'activitypub-http-unicast',
30 'activitypub-refresher', 31 'activitypub-refresher',
diff --git a/scripts/simulate-many-viewers.ts b/scripts/simulate-many-viewers.ts
new file mode 100644
index 000000000..fb666c318
--- /dev/null
+++ b/scripts/simulate-many-viewers.ts
@@ -0,0 +1,89 @@
1import Bluebird from 'bluebird'
2import { wait } from '@shared/core-utils'
3import {
4 createSingleServer,
5 doubleFollow,
6 killallServers,
7 PeerTubeServer,
8 setAccessTokensToServers,
9 waitJobs
10} from '@shared/server-commands'
11
12let servers: PeerTubeServer[]
13const viewers: { xForwardedFor: string }[] = []
14let videoId: string
15
16run()
17 .then(() => process.exit(0))
18 .catch(err => console.error(err))
19 .finally(() => killallServers(servers))
20
21async function run () {
22 await prepare()
23
24 while (true) {
25 await runViewers()
26 }
27}
28
29async function prepare () {
30 console.log('Preparing servers...')
31
32 const config = {
33 log: {
34 level: 'info'
35 },
36 rates_limit: {
37 api: {
38 max: 5_000_000
39 }
40 },
41 views: {
42 videos: {
43 local_buffer_update_interval: '30 minutes',
44 ip_view_expiration: '1 hour'
45 }
46 }
47 }
48
49 servers = await Promise.all([
50 createSingleServer(1, config, { nodeArgs: [ '--inspect' ] }),
51 createSingleServer(2, config),
52 createSingleServer(3, config)
53 ])
54
55 await setAccessTokensToServers(servers)
56 await doubleFollow(servers[0], servers[1])
57 await doubleFollow(servers[0], servers[2])
58
59 const { uuid } = await servers[0].videos.quickUpload({ name: 'video' })
60 videoId = uuid
61
62 await waitJobs(servers)
63
64 const THOUSAND_VIEWERS = 2
65
66 for (let i = 2; i < 252; i++) {
67 for (let j = 2; j < 6; j++) {
68 for (let k = 2; k < THOUSAND_VIEWERS + 2; k++) {
69 viewers.push({ xForwardedFor: `0.${k}.${j}.${i},127.0.0.1` })
70 }
71 }
72 }
73
74 console.log('Servers preparation finished.')
75}
76
77async function runViewers () {
78 console.log('Will run views of %d viewers.', viewers.length)
79
80 const before = new Date().getTime()
81
82 await Bluebird.map(viewers, viewer => {
83 return servers[0].views.simulateView({ id: videoId, xForwardedFor: viewer.xForwardedFor })
84 }, { concurrency: 100 })
85
86 console.log('Finished to run views in %d seconds.', (new Date().getTime() - before) / 1000)
87
88 await wait(5000)
89}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 2d324d1eb..75ccbc458 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -139,6 +139,7 @@ const REMOTE_SCHEME = {
139 139
140const JOB_ATTEMPTS: { [id in JobType]: number } = { 140const JOB_ATTEMPTS: { [id in JobType]: number } = {
141 'activitypub-http-broadcast': 1, 141 'activitypub-http-broadcast': 1,
142 'activitypub-http-broadcast-parallel': 1,
142 'activitypub-http-unicast': 1, 143 'activitypub-http-unicast': 1,
143 'activitypub-http-fetcher': 2, 144 'activitypub-http-fetcher': 2,
144 'activitypub-follow': 5, 145 'activitypub-follow': 5,
@@ -159,6 +160,7 @@ const JOB_ATTEMPTS: { [id in JobType]: number } = {
159// Excluded keys are jobs that can be configured by admins 160// Excluded keys are jobs that can be configured by admins
160const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-import'>]: number } = { 161const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-import'>]: number } = {
161 'activitypub-http-broadcast': 1, 162 'activitypub-http-broadcast': 1,
163 'activitypub-http-broadcast-parallel': 30,
162 'activitypub-http-unicast': 10, 164 'activitypub-http-unicast': 10,
163 'activitypub-http-fetcher': 3, 165 'activitypub-http-fetcher': 3,
164 'activitypub-cleaner': 1, 166 'activitypub-cleaner': 1,
@@ -176,6 +178,7 @@ const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-im
176} 178}
177const JOB_TTL: { [id in JobType]: number } = { 179const JOB_TTL: { [id in JobType]: number } = {
178 'activitypub-http-broadcast': 60000 * 10, // 10 minutes 180 'activitypub-http-broadcast': 60000 * 10, // 10 minutes
181 'activitypub-http-broadcast-parallel': 60000 * 10, // 10 minutes
179 'activitypub-http-unicast': 60000 * 10, // 10 minutes 182 'activitypub-http-unicast': 60000 * 10, // 10 minutes
180 'activitypub-http-fetcher': 1000 * 3600 * 10, // 10 hours 183 'activitypub-http-fetcher': 1000 * 3600 * 10, // 10 hours
181 'activitypub-follow': 60000 * 10, // 10 minutes 184 'activitypub-follow': 60000 * 10, // 10 minutes
@@ -371,7 +374,7 @@ const VIEW_LIFETIME = {
371 VIEWER_STATS: 60000 * 60 // 1 hour 374 VIEWER_STATS: 60000 * 60 // 1 hour
372} 375}
373 376
374const MAX_LOCAL_VIEWER_WATCH_SECTIONS = 10 377const MAX_LOCAL_VIEWER_WATCH_SECTIONS = 100
375 378
376let CONTACT_FORM_LIFETIME = 60000 * 60 // 1 hour 379let CONTACT_FORM_LIFETIME = 60000 * 60 // 1 hour
377 380
diff --git a/server/lib/activitypub/local-video-viewer.ts b/server/lib/activitypub/local-video-viewer.ts
index 738083adc..bdd746791 100644
--- a/server/lib/activitypub/local-video-viewer.ts
+++ b/server/lib/activitypub/local-video-viewer.ts
@@ -23,7 +23,7 @@ async function createOrUpdateLocalVideoViewer (watchAction: WatchActionObject, v
23 : null, 23 : null,
24 24
25 videoId: video.id 25 videoId: video.id
26 }) 26 }, { transaction: t })
27 27
28 await LocalVideoViewerWatchSectionModel.bulkCreateSections({ 28 await LocalVideoViewerWatchSectionModel.bulkCreateSections({
29 localVideoViewerId: localVideoViewer.id, 29 localVideoViewerId: localVideoViewer.id,
@@ -31,7 +31,9 @@ async function createOrUpdateLocalVideoViewer (watchAction: WatchActionObject, v
31 watchSections: watchAction.watchSections.map(s => ({ 31 watchSections: watchAction.watchSections.map(s => ({
32 start: s.startTimestamp, 32 start: s.startTimestamp,
33 end: s.endTimestamp 33 end: s.endTimestamp
34 })) 34 })),
35
36 transaction: t
35 }) 37 })
36} 38}
37 39
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts
index 25a20ec6d..bf3451603 100644
--- a/server/lib/activitypub/send/send-view.ts
+++ b/server/lib/activitypub/send/send-view.ts
@@ -26,7 +26,7 @@ async function sendView (options: {
26 return buildViewActivity({ url, byActor, video, audience, type }) 26 return buildViewActivity({ url, byActor, video, audience, type })
27 } 27 }
28 28
29 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction, contextType: 'View' }) 29 return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction, contextType: 'View', parallelizable: true })
30} 30}
31 31
32// --------------------------------------------------------------------------- 32// ---------------------------------------------------------------------------
diff --git a/server/lib/activitypub/send/shared/audience-utils.ts b/server/lib/activitypub/send/shared/audience-utils.ts
index ba4be487c..2f6b0741d 100644
--- a/server/lib/activitypub/send/shared/audience-utils.ts
+++ b/server/lib/activitypub/send/shared/audience-utils.ts
@@ -3,7 +3,7 @@ import { ACTIVITY_PUB } from '@server/initializers/constants'
3import { ActorModel } from '@server/models/actor/actor' 3import { ActorModel } from '@server/models/actor/actor'
4import { VideoModel } from '@server/models/video/video' 4import { VideoModel } from '@server/models/video/video'
5import { VideoShareModel } from '@server/models/video/video-share' 5import { VideoShareModel } from '@server/models/video/video-share'
6import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '@server/types/models' 6import { MActorFollowersUrl, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '@server/types/models'
7import { ActivityAudience } from '@shared/models' 7import { ActivityAudience } from '@shared/models'
8 8
9function getOriginVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[] = []): ActivityAudience { 9function getOriginVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[] = []): ActivityAudience {
@@ -51,13 +51,13 @@ function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[
51} 51}
52 52
53async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) { 53async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) {
54 const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) 54 const actors = await VideoShareModel.listActorIdsAndFollowerUrlsByShare(video.id, t)
55 55
56 const videoAll = video as VideoModel 56 const videoAll = video as VideoModel
57 57
58 const videoActor = videoAll.VideoChannel?.Account 58 const videoActor = videoAll.VideoChannel?.Account
59 ? videoAll.VideoChannel.Account.Actor 59 ? videoAll.VideoChannel.Account.Actor
60 : await ActorModel.loadFromAccountByVideoId(video.id, t) 60 : await ActorModel.loadAccountActorFollowerUrlByVideoId(video.id, t)
61 61
62 actors.push(videoActor) 62 actors.push(videoActor)
63 63
diff --git a/server/lib/activitypub/send/shared/send-utils.ts b/server/lib/activitypub/send/shared/send-utils.ts
index dbcde91ee..fcec63991 100644
--- a/server/lib/activitypub/send/shared/send-utils.ts
+++ b/server/lib/activitypub/send/shared/send-utils.ts
@@ -15,17 +15,18 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
15 byActor: MActorLight 15 byActor: MActorLight
16 video: MVideoImmutable | MVideoAccountLight 16 video: MVideoImmutable | MVideoAccountLight
17 contextType: ContextType 17 contextType: ContextType
18 parallelizable?: boolean
18 transaction?: Transaction 19 transaction?: Transaction
19}) { 20}) {
20 const { byActor, video, transaction, contextType } = options 21 const { byActor, video, transaction, contextType, parallelizable } = options
21
22 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction)
23 22
24 // Send to origin 23 // Send to origin
25 if (video.isOwned() === false) { 24 if (video.isOwned() === false) {
26 return sendVideoActivityToOrigin(activityBuilder, options) 25 return sendVideoActivityToOrigin(activityBuilder, options)
27 } 26 }
28 27
28 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction)
29
29 // Send to followers 30 // Send to followers
30 const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo) 31 const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
31 const activity = activityBuilder(audience) 32 const activity = activityBuilder(audience)
@@ -38,6 +39,7 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
38 toFollowersOf: actorsInvolvedInVideo, 39 toFollowersOf: actorsInvolvedInVideo,
39 transaction, 40 transaction,
40 actorsException, 41 actorsException,
42 parallelizable,
41 contextType 43 contextType
42 }) 44 })
43} 45}
@@ -130,9 +132,10 @@ async function broadcastToFollowers (options: {
130 transaction: Transaction 132 transaction: Transaction
131 contextType: ContextType 133 contextType: ContextType
132 134
135 parallelizable?: boolean
133 actorsException?: MActorWithInboxes[] 136 actorsException?: MActorWithInboxes[]
134}) { 137}) {
135 const { data, byActor, toFollowersOf, transaction, contextType, actorsException = [] } = options 138 const { data, byActor, toFollowersOf, transaction, contextType, actorsException = [], parallelizable } = options
136 139
137 const uris = await computeFollowerUris(toFollowersOf, actorsException, transaction) 140 const uris = await computeFollowerUris(toFollowersOf, actorsException, transaction)
138 141
@@ -141,6 +144,7 @@ async function broadcastToFollowers (options: {
141 uris, 144 uris,
142 data, 145 data,
143 byActor, 146 byActor,
147 parallelizable,
144 contextType 148 contextType
145 }) 149 })
146 }) 150 })
@@ -173,8 +177,9 @@ function broadcastTo (options: {
173 data: any 177 data: any
174 byActor: MActorId 178 byActor: MActorId
175 contextType: ContextType 179 contextType: ContextType
180 parallelizable?: boolean // default to false
176}) { 181}) {
177 const { uris, data, byActor, contextType } = options 182 const { uris, data, byActor, contextType, parallelizable } = options
178 183
179 if (uris.length === 0) return undefined 184 if (uris.length === 0) return undefined
180 185
@@ -200,7 +205,13 @@ function broadcastTo (options: {
200 contextType 205 contextType
201 } 206 }
202 207
203 JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) 208 JobQueue.Instance.createJob({
209 type: parallelizable
210 ? 'activitypub-http-broadcast-parallel'
211 : 'activitypub-http-broadcast',
212
213 payload
214 })
204 } 215 }
205 216
206 for (const unicastUri of unicastUris) { 217 for (const unicastUri of unicastUris) {
diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts
index 61e41fb0f..ce24763f1 100644
--- a/server/lib/job-queue/job-queue.ts
+++ b/server/lib/job-queue/job-queue.ts
@@ -43,6 +43,7 @@ import { processVideosViewsStats } from './handlers/video-views-stats'
43 43
44type CreateJobArgument = 44type CreateJobArgument =
45 { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } | 45 { type: 'activitypub-http-broadcast', payload: ActivitypubHttpBroadcastPayload } |
46 { type: 'activitypub-http-broadcast-parallel', payload: ActivitypubHttpBroadcastPayload } |
46 { type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } | 47 { type: 'activitypub-http-unicast', payload: ActivitypubHttpUnicastPayload } |
47 { type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } | 48 { type: 'activitypub-http-fetcher', payload: ActivitypubHttpFetcherPayload } |
48 { type: 'activitypub-http-cleaner', payload: {} } | 49 { type: 'activitypub-http-cleaner', payload: {} } |
@@ -68,6 +69,7 @@ export type CreateJobOptions = {
68 69
69const handlers: { [id in JobType]: (job: Job) => Promise<any> } = { 70const handlers: { [id in JobType]: (job: Job) => Promise<any> } = {
70 'activitypub-http-broadcast': processActivityPubHttpBroadcast, 71 'activitypub-http-broadcast': processActivityPubHttpBroadcast,
72 'activitypub-http-broadcast-parallel': processActivityPubHttpBroadcast,
71 'activitypub-http-unicast': processActivityPubHttpUnicast, 73 'activitypub-http-unicast': processActivityPubHttpUnicast,
72 'activitypub-http-fetcher': processActivityPubHttpFetcher, 74 'activitypub-http-fetcher': processActivityPubHttpFetcher,
73 'activitypub-cleaner': processActivityPubCleaner, 75 'activitypub-cleaner': processActivityPubCleaner,
@@ -93,6 +95,7 @@ const errorHandlers: { [id in JobType]?: (job: Job, err: any) => Promise<any> }
93const jobTypes: JobType[] = [ 95const jobTypes: JobType[] = [
94 'activitypub-follow', 96 'activitypub-follow',
95 'activitypub-http-broadcast', 97 'activitypub-http-broadcast',
98 'activitypub-http-broadcast-parallel',
96 'activitypub-http-fetcher', 99 'activitypub-http-fetcher',
97 'activitypub-http-unicast', 100 'activitypub-http-unicast',
98 'activitypub-cleaner', 101 'activitypub-cleaner',
diff --git a/server/lib/views/shared/video-viewer-counters.ts b/server/lib/views/shared/video-viewer-counters.ts
index 941b62ed7..999ab7d8d 100644
--- a/server/lib/views/shared/video-viewer-counters.ts
+++ b/server/lib/views/shared/video-viewer-counters.ts
@@ -1,4 +1,3 @@
1
2import { isTestInstance } from '@server/helpers/core-utils' 1import { isTestInstance } from '@server/helpers/core-utils'
3import { logger, loggerTagsFactory } from '@server/helpers/logger' 2import { logger, loggerTagsFactory } from '@server/helpers/logger'
4import { VIEW_LIFETIME } from '@server/initializers/constants' 3import { VIEW_LIFETIME } from '@server/initializers/constants'
diff --git a/server/models/actor/actor.ts b/server/models/actor/actor.ts
index 93145b8ae..943b7364f 100644
--- a/server/models/actor/actor.ts
+++ b/server/models/actor/actor.ts
@@ -1,5 +1,5 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import { literal, Op, Transaction } from 'sequelize' 2import { literal, Op, QueryTypes, Transaction } from 'sequelize'
3import { 3import {
4 AllowNull, 4 AllowNull,
5 BelongsTo, 5 BelongsTo,
@@ -43,15 +43,18 @@ import {
43 MActorAccountChannelId, 43 MActorAccountChannelId,
44 MActorAPAccount, 44 MActorAPAccount,
45 MActorAPChannel, 45 MActorAPChannel,
46 MActorFollowersUrl,
46 MActorFormattable, 47 MActorFormattable,
47 MActorFull, 48 MActorFull,
48 MActorHost, 49 MActorHost,
50 MActorId,
49 MActorServer, 51 MActorServer,
50 MActorSummaryFormattable, 52 MActorSummaryFormattable,
51 MActorUrl, 53 MActorUrl,
52 MActorWithInboxes 54 MActorWithInboxes
53} from '../../types/models' 55} from '../../types/models'
54import { AccountModel } from '../account/account' 56import { AccountModel } from '../account/account'
57import { getServerActor } from '../application/application'
55import { ServerModel } from '../server/server' 58import { ServerModel } from '../server/server'
56import { isOutdated, throwIfNotValid } from '../utils' 59import { isOutdated, throwIfNotValid } from '../utils'
57import { VideoModel } from '../video/video' 60import { VideoModel } from '../video/video'
@@ -304,7 +307,10 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
304 }) 307 })
305 VideoChannel: VideoChannelModel 308 VideoChannel: VideoChannelModel
306 309
307 static load (id: number): Promise<MActor> { 310 static async load (id: number): Promise<MActor> {
311 const actorServer = await getServerActor()
312 if (id === actorServer.id) return actorServer
313
308 return ActorModel.unscoped().findByPk(id) 314 return ActorModel.unscoped().findByPk(id)
309 } 315 }
310 316
@@ -312,48 +318,21 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
312 return ActorModel.scope(ScopeNames.FULL).findByPk(id) 318 return ActorModel.scope(ScopeNames.FULL).findByPk(id)
313 } 319 }
314 320
315 static loadFromAccountByVideoId (videoId: number, transaction: Transaction): Promise<MActor> { 321 static loadAccountActorFollowerUrlByVideoId (videoId: number, transaction: Transaction) {
316 const query = { 322 const query = `SELECT "actor"."id" AS "id", "actor"."followersUrl" AS "followersUrl" ` +
317 include: [ 323 `FROM "actor" ` +
318 { 324 `INNER JOIN "account" ON "actor"."id" = "account"."actorId" ` +
319 attributes: [ 'id' ], 325 `INNER JOIN "videoChannel" ON "videoChannel"."accountId" = "account"."id" ` +
320 model: AccountModel.unscoped(), 326 `INNER JOIN "video" ON "video"."channelId" = "videoChannel"."id" AND "video"."id" = :videoId`
321 required: true, 327
322 include: [ 328 const options = {
323 { 329 type: QueryTypes.SELECT as QueryTypes.SELECT,
324 attributes: [ 'id' ], 330 replacements: { videoId },
325 model: VideoChannelModel.unscoped(), 331 plain: true as true,
326 required: true,
327 include: [
328 {
329 attributes: [ 'id' ],
330 model: VideoModel.unscoped(),
331 required: true,
332 where: {
333 id: videoId
334 }
335 }
336 ]
337 }
338 ]
339 }
340 ],
341 transaction 332 transaction
342 } 333 }
343 334
344 return ActorModel.unscoped().findOne(query) 335 return ActorModel.sequelize.query<MActorId & MActorFollowersUrl>(query, options)
345 }
346
347 static isActorUrlExist (url: string) {
348 const query = {
349 raw: true,
350 where: {
351 url
352 }
353 }
354
355 return ActorModel.unscoped().findOne(query)
356 .then(a => !!a)
357 } 336 }
358 337
359 static listByFollowersUrls (followersUrls: string[], transaction?: Transaction): Promise<MActorFull[]> { 338 static listByFollowersUrls (followersUrls: string[], transaction?: Transaction): Promise<MActorFull[]> {
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index ad95dec6e..ca63bb2d9 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -3,7 +3,7 @@ import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Mode
3import { AttributesOnly } from '@shared/typescript-utils' 3import { AttributesOnly } from '@shared/typescript-utils'
4import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 4import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
5import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 5import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
6import { MActorDefault } from '../../types/models' 6import { MActorDefault, MActorFollowersUrl, MActorId } from '../../types/models'
7import { MVideoShareActor, MVideoShareFull } from '../../types/models/video' 7import { MVideoShareActor, MVideoShareFull } from '../../types/models/video'
8import { ActorModel } from '../actor/actor' 8import { ActorModel } from '../actor/actor'
9import { buildLocalActorIdsIn, throwIfNotValid } from '../utils' 9import { buildLocalActorIdsIn, throwIfNotValid } from '../utils'
@@ -107,22 +107,19 @@ export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareMode
107 }) 107 })
108 } 108 }
109 109
110 static loadActorsByShare (videoId: number, t: Transaction): Promise<MActorDefault[]> { 110 static listActorIdsAndFollowerUrlsByShare (videoId: number, t: Transaction) {
111 const query = { 111 const query = `SELECT "actor"."id" AS "id", "actor"."followersUrl" AS "followersUrl" ` +
112 where: { 112 `FROM "videoShare" ` +
113 videoId 113 `INNER JOIN "actor" ON "actor"."id" = "videoShare"."actorId" ` +
114 }, 114 `WHERE "videoShare"."videoId" = :videoId`
115 include: [ 115
116 { 116 const options = {
117 model: ActorModel, 117 type: QueryTypes.SELECT as QueryTypes.SELECT,
118 required: true 118 replacements: { videoId },
119 }
120 ],
121 transaction: t 119 transaction: t
122 } 120 }
123 121
124 return VideoShareModel.scope(ScopeNames.FULL).findAll(query) 122 return VideoShareModel.sequelize.query<MActorId & MActorFollowersUrl>(query, options)
125 .then((res: MVideoShareFull[]) => res.map(r => r.Actor))
126 } 123 }
127 124
128 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Promise<MActorDefault[]> { 125 static loadActorsWhoSharedVideosOf (actorOwnerId: number, t: Transaction): Promise<MActorDefault[]> {
diff --git a/shared/models/server/job.model.ts b/shared/models/server/job.model.ts
index 073f15872..4633ab769 100644
--- a/shared/models/server/job.model.ts
+++ b/shared/models/server/job.model.ts
@@ -9,6 +9,7 @@ export type JobState = 'active' | 'completed' | 'failed' | 'waiting' | 'delayed'
9export type JobType = 9export type JobType =
10 | 'activitypub-http-unicast' 10 | 'activitypub-http-unicast'
11 | 'activitypub-http-broadcast' 11 | 'activitypub-http-broadcast'
12 | 'activitypub-http-broadcast-parallel'
12 | 'activitypub-http-fetcher' 13 | 'activitypub-http-fetcher'
13 | 'activitypub-cleaner' 14 | 'activitypub-cleaner'
14 | 'activitypub-follow' 15 | 'activitypub-follow'