diff options
author | Chocobozzz <me@florianbigard.com> | 2019-08-15 11:53:26 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-08-19 17:26:35 +0200 |
commit | 453e83ea5d81d203ba34bc43cd5c2c750ba40568 (patch) | |
tree | 604e02f4343d13a4ba42e1fb7527ba6ab9111712 /server/lib/activitypub | |
parent | 13176a07a95984a53cc59aec5217f2ce9806d1bc (diff) | |
download | PeerTube-453e83ea5d81d203ba34bc43cd5c2c750ba40568.tar.gz PeerTube-453e83ea5d81d203ba34bc43cd5c2c750ba40568.tar.zst PeerTube-453e83ea5d81d203ba34bc43cd5c2c750ba40568.zip |
Stronger model typings
Diffstat (limited to 'server/lib/activitypub')
35 files changed, 412 insertions, 329 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 9f5d12eb4..7862b0f00 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -22,13 +22,25 @@ import { JobQueue } from '../job-queue' | |||
22 | import { getServerActor } from '../../helpers/utils' | 22 | import { getServerActor } from '../../helpers/utils' |
23 | import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' | 23 | import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' |
24 | import { sequelizeTypescript } from '../../initializers/database' | 24 | import { sequelizeTypescript } from '../../initializers/database' |
25 | import { | ||
26 | MAccount, | ||
27 | MActor, | ||
28 | MActorAccountChannelId, | ||
29 | MActorAccountId, | ||
30 | MActorDefault, | ||
31 | MActorFull, | ||
32 | MActorId, | ||
33 | MActorAccountChannelIdActor, | ||
34 | MChannel, | ||
35 | MActorFullActor, MAccountActorDefault, MChannelActorDefault, MChannelActorAccountDefault | ||
36 | } from '../../typings/models' | ||
25 | 37 | ||
26 | // Set account keys, this could be long so process after the account creation and do not block the client | 38 | // Set account keys, this could be long so process after the account creation and do not block the client |
27 | function setAsyncActorKeys (actor: ActorModel) { | 39 | function setAsyncActorKeys (actor: MActor) { |
28 | return createPrivateAndPublicKeys() | 40 | return createPrivateAndPublicKeys() |
29 | .then(({ publicKey, privateKey }) => { | 41 | .then(({ publicKey, privateKey }) => { |
30 | actor.set('publicKey', publicKey) | 42 | actor.publicKey = publicKey |
31 | actor.set('privateKey', privateKey) | 43 | actor.privateKey = privateKey |
32 | return actor.save() | 44 | return actor.save() |
33 | }) | 45 | }) |
34 | .catch(err => { | 46 | .catch(err => { |
@@ -37,12 +49,26 @@ function setAsyncActorKeys (actor: ActorModel) { | |||
37 | }) | 49 | }) |
38 | } | 50 | } |
39 | 51 | ||
52 | function getOrCreateActorAndServerAndModel ( | ||
53 | activityActor: string | ActivityPubActor, | ||
54 | fetchType: 'all', | ||
55 | recurseIfNeeded?: boolean, | ||
56 | updateCollections?: boolean | ||
57 | ): Promise<MActorFullActor> | ||
58 | |||
59 | function getOrCreateActorAndServerAndModel ( | ||
60 | activityActor: string | ActivityPubActor, | ||
61 | fetchType?: 'association-ids', | ||
62 | recurseIfNeeded?: boolean, | ||
63 | updateCollections?: boolean | ||
64 | ): Promise<MActorAccountChannelId> | ||
65 | |||
40 | async function getOrCreateActorAndServerAndModel ( | 66 | async function getOrCreateActorAndServerAndModel ( |
41 | activityActor: string | ActivityPubActor, | 67 | activityActor: string | ActivityPubActor, |
42 | fetchType: ActorFetchByUrlType = 'actor-and-association-ids', | 68 | fetchType: ActorFetchByUrlType = 'association-ids', |
43 | recurseIfNeeded = true, | 69 | recurseIfNeeded = true, |
44 | updateCollections = false | 70 | updateCollections = false |
45 | ) { | 71 | ): Promise<MActorFullActor | MActorAccountChannelId> { |
46 | const actorUrl = getAPId(activityActor) | 72 | const actorUrl = getAPId(activityActor) |
47 | let created = false | 73 | let created = false |
48 | let accountPlaylistsUrl: string | 74 | let accountPlaylistsUrl: string |
@@ -61,7 +87,7 @@ async function getOrCreateActorAndServerAndModel ( | |||
61 | 87 | ||
62 | // Create the attributed to actor | 88 | // Create the attributed to actor |
63 | // In PeerTube a video channel is owned by an account | 89 | // In PeerTube a video channel is owned by an account |
64 | let ownerActor: ActorModel = undefined | 90 | let ownerActor: MActorFullActor |
65 | if (recurseIfNeeded === true && result.actor.type === 'Group') { | 91 | if (recurseIfNeeded === true && result.actor.type === 'Group') { |
66 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') | 92 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') |
67 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) | 93 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) |
@@ -85,8 +111,8 @@ async function getOrCreateActorAndServerAndModel ( | |||
85 | accountPlaylistsUrl = result.playlists | 111 | accountPlaylistsUrl = result.playlists |
86 | } | 112 | } |
87 | 113 | ||
88 | if (actor.Account) actor.Account.Actor = actor | 114 | if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor |
89 | if (actor.VideoChannel) actor.VideoChannel.Actor = actor | 115 | if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor |
90 | 116 | ||
91 | const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) | 117 | const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) |
92 | if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') | 118 | if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') |
@@ -140,7 +166,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
140 | actorInstance.followingUrl = attributes.following | 166 | actorInstance.followingUrl = attributes.following |
141 | } | 167 | } |
142 | 168 | ||
143 | async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { | 169 | type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string } |
170 | async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) { | ||
144 | if (info.name !== undefined) { | 171 | if (info.name !== undefined) { |
145 | if (actor.avatarId) { | 172 | if (actor.avatarId) { |
146 | try { | 173 | try { |
@@ -212,14 +239,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) { | |||
212 | return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) | 239 | return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) |
213 | } | 240 | } |
214 | 241 | ||
215 | async function refreshActorIfNeeded ( | 242 | async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> ( |
216 | actorArg: ActorModel, | 243 | actorArg: T, |
217 | fetchedType: ActorFetchByUrlType | 244 | fetchedType: ActorFetchByUrlType |
218 | ): Promise<{ actor: ActorModel, refreshed: boolean }> { | 245 | ): Promise<{ actor: T | MActorFull, refreshed: boolean }> { |
219 | if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } | 246 | if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } |
220 | 247 | ||
221 | // We need more attributes | 248 | // We need more attributes |
222 | const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) | 249 | const actor = fetchedType === 'all' |
250 | ? actorArg as MActorFull | ||
251 | : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) | ||
223 | 252 | ||
224 | try { | 253 | try { |
225 | let actorUrl: string | 254 | let actorUrl: string |
@@ -297,9 +326,9 @@ export { | |||
297 | 326 | ||
298 | function saveActorAndServerAndModelIfNotExist ( | 327 | function saveActorAndServerAndModelIfNotExist ( |
299 | result: FetchRemoteActorResult, | 328 | result: FetchRemoteActorResult, |
300 | ownerActor?: ActorModel, | 329 | ownerActor?: MActorFullActor, |
301 | t?: Transaction | 330 | t?: Transaction |
302 | ): Bluebird<ActorModel> | Promise<ActorModel> { | 331 | ): Bluebird<MActorFullActor> | Promise<MActorFullActor> { |
303 | let actor = result.actor | 332 | let actor = result.actor |
304 | 333 | ||
305 | if (t !== undefined) return save(t) | 334 | if (t !== undefined) return save(t) |
@@ -336,7 +365,7 @@ function saveActorAndServerAndModelIfNotExist ( | |||
336 | 365 | ||
337 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists | 366 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists |
338 | // (which could be false in a retried query) | 367 | // (which could be false in a retried query) |
339 | const [ actorCreated ] = await ActorModel.findOrCreate({ | 368 | const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({ |
340 | defaults: actor.toJSON(), | 369 | defaults: actor.toJSON(), |
341 | where: { | 370 | where: { |
342 | url: actor.url | 371 | url: actor.url |
@@ -345,10 +374,10 @@ function saveActorAndServerAndModelIfNotExist ( | |||
345 | }) | 374 | }) |
346 | 375 | ||
347 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { | 376 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { |
348 | actorCreated.Account = await saveAccount(actorCreated, result, t) | 377 | actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountActorDefault |
349 | actorCreated.Account.Actor = actorCreated | 378 | actorCreated.Account.Actor = actorCreated |
350 | } else if (actorCreated.type === 'Group') { // Video channel | 379 | } else if (actorCreated.type === 'Group') { // Video channel |
351 | actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) | 380 | actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) as MChannelActorAccountDefault |
352 | actorCreated.VideoChannel.Actor = actorCreated | 381 | actorCreated.VideoChannel.Actor = actorCreated |
353 | actorCreated.VideoChannel.Account = ownerActor.Account | 382 | actorCreated.VideoChannel.Account = ownerActor.Account |
354 | } | 383 | } |
@@ -360,7 +389,7 @@ function saveActorAndServerAndModelIfNotExist ( | |||
360 | } | 389 | } |
361 | 390 | ||
362 | type FetchRemoteActorResult = { | 391 | type FetchRemoteActorResult = { |
363 | actor: ActorModel | 392 | actor: MActor |
364 | name: string | 393 | name: string |
365 | summary: string | 394 | summary: string |
366 | support?: string | 395 | support?: string |
@@ -429,7 +458,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
429 | } | 458 | } |
430 | } | 459 | } |
431 | 460 | ||
432 | async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { | 461 | async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) { |
433 | const [ accountCreated ] = await AccountModel.findOrCreate({ | 462 | const [ accountCreated ] = await AccountModel.findOrCreate({ |
434 | defaults: { | 463 | defaults: { |
435 | name: result.name, | 464 | name: result.name, |
@@ -442,10 +471,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t | |||
442 | transaction: t | 471 | transaction: t |
443 | }) | 472 | }) |
444 | 473 | ||
445 | return accountCreated | 474 | return accountCreated as MAccount |
446 | } | 475 | } |
447 | 476 | ||
448 | async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { | 477 | async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) { |
449 | const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ | 478 | const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ |
450 | defaults: { | 479 | defaults: { |
451 | name: result.name, | 480 | name: result.name, |
@@ -460,5 +489,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu | |||
460 | transaction: t | 489 | transaction: t |
461 | }) | 490 | }) |
462 | 491 | ||
463 | return videoChannelCreated | 492 | return videoChannelCreated as MChannel |
464 | } | 493 | } |
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts index 0e3d78590..f2ab54cf7 100644 --- a/server/lib/activitypub/audience.ts +++ b/server/lib/activitypub/audience.ts | |||
@@ -3,11 +3,10 @@ import { ActivityAudience } from '../../../shared/models/activitypub' | |||
3 | import { ACTIVITY_PUB } from '../../initializers/constants' | 3 | import { ACTIVITY_PUB } from '../../initializers/constants' |
4 | import { ActorModel } from '../../models/activitypub/actor' | 4 | import { ActorModel } from '../../models/activitypub/actor' |
5 | import { VideoModel } from '../../models/video/video' | 5 | import { VideoModel } from '../../models/video/video' |
6 | import { VideoCommentModel } from '../../models/video/video-comment' | ||
7 | import { VideoShareModel } from '../../models/video/video-share' | 6 | import { VideoShareModel } from '../../models/video/video-share' |
8 | import { ActorModelOnly } from '../../typings/models' | 7 | import { MActorFollowersUrl, MActorLight, MCommentOwner, MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../../typings/models' |
9 | 8 | ||
10 | function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience { | 9 | function getRemoteVideoAudience (video: MVideoAccountLight, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience { |
11 | return { | 10 | return { |
12 | to: [ video.VideoChannel.Account.Actor.url ], | 11 | to: [ video.VideoChannel.Account.Actor.url ], |
13 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) | 12 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) |
@@ -15,9 +14,9 @@ function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor | |||
15 | } | 14 | } |
16 | 15 | ||
17 | function getVideoCommentAudience ( | 16 | function getVideoCommentAudience ( |
18 | videoComment: VideoCommentModel, | 17 | videoComment: MCommentOwnerVideo, |
19 | threadParentComments: VideoCommentModel[], | 18 | threadParentComments: MCommentOwner[], |
20 | actorsInvolvedInVideo: ActorModel[], | 19 | actorsInvolvedInVideo: MActorFollowersUrl[], |
21 | isOrigin = false | 20 | isOrigin = false |
22 | ): ActivityAudience { | 21 | ): ActivityAudience { |
23 | const to = [ ACTIVITY_PUB.PUBLIC ] | 22 | const to = [ ACTIVITY_PUB.PUBLIC ] |
@@ -42,26 +41,28 @@ function getVideoCommentAudience ( | |||
42 | } | 41 | } |
43 | } | 42 | } |
44 | 43 | ||
45 | function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience { | 44 | function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience { |
46 | return { | 45 | return { |
47 | to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), | 46 | to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), |
48 | cc: [] | 47 | cc: [] |
49 | } | 48 | } |
50 | } | 49 | } |
51 | 50 | ||
52 | async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { | 51 | async function getActorsInvolvedInVideo (video: MVideo, t: Transaction) { |
53 | const actors = await VideoShareModel.loadActorsByShare(video.id, t) | 52 | const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) |
54 | 53 | ||
55 | const videoActor = video.VideoChannel && video.VideoChannel.Account | 54 | const videoAll = video as VideoModel |
56 | ? video.VideoChannel.Account.Actor | 55 | |
57 | : await ActorModel.loadAccountActorByVideoId(video.id, t) | 56 | const videoActor = videoAll.VideoChannel && videoAll.VideoChannel.Account |
57 | ? videoAll.VideoChannel.Account.Actor | ||
58 | : await ActorModel.loadFromAccountByVideoId(video.id, t) | ||
58 | 59 | ||
59 | actors.push(videoActor) | 60 | actors.push(videoActor) |
60 | 61 | ||
61 | return actors | 62 | return actors |
62 | } | 63 | } |
63 | 64 | ||
64 | function getAudience (actorSender: ActorModelOnly, isPublic = true) { | 65 | function getAudience (actorSender: MActorFollowersUrl, isPublic = true) { |
65 | return buildAudience([ actorSender.followersUrl ], isPublic) | 66 | return buildAudience([ actorSender.followersUrl ], isPublic) |
66 | } | 67 | } |
67 | 68 | ||
diff --git a/server/lib/activitypub/cache-file.ts b/server/lib/activitypub/cache-file.ts index de5cc54ac..65b2dcb49 100644 --- a/server/lib/activitypub/cache-file.ts +++ b/server/lib/activitypub/cache-file.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { CacheFileObject } from '../../../shared/index' | 1 | import { CacheFileObject } from '../../../shared/index' |
2 | import { VideoModel } from '../../models/video/video' | ||
3 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 2 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
4 | import { Transaction } from 'sequelize' | 3 | import { Transaction } from 'sequelize' |
5 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 4 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
5 | import { MActorId, MVideoRedundancy, MVideoWithAllFiles } from '@server/typings/models' | ||
6 | 6 | ||
7 | function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) { | 7 | function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId) { |
8 | 8 | ||
9 | if (cacheFileObject.url.mediaType === 'application/x-mpegURL') { | 9 | if (cacheFileObject.url.mediaType === 'application/x-mpegURL') { |
10 | const url = cacheFileObject.url | 10 | const url = cacheFileObject.url |
@@ -39,7 +39,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject | |||
39 | } | 39 | } |
40 | } | 40 | } |
41 | 41 | ||
42 | async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { | 42 | async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) { |
43 | const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) | 43 | const redundancyModel = await VideoRedundancyModel.loadByUrl(cacheFileObject.id, t) |
44 | 44 | ||
45 | if (!redundancyModel) { | 45 | if (!redundancyModel) { |
@@ -49,7 +49,7 @@ async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: | |||
49 | } | 49 | } |
50 | } | 50 | } |
51 | 51 | ||
52 | function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { | 52 | function createCacheFile (cacheFileObject: CacheFileObject, video: MVideoWithAllFiles, byActor: MActorId, t: Transaction) { |
53 | const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) | 53 | const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) |
54 | 54 | ||
55 | return VideoRedundancyModel.create(attributes, { transaction: t }) | 55 | return VideoRedundancyModel.create(attributes, { transaction: t }) |
@@ -57,9 +57,9 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b | |||
57 | 57 | ||
58 | function updateCacheFile ( | 58 | function updateCacheFile ( |
59 | cacheFileObject: CacheFileObject, | 59 | cacheFileObject: CacheFileObject, |
60 | redundancyModel: VideoRedundancyModel, | 60 | redundancyModel: MVideoRedundancy, |
61 | video: VideoModel, | 61 | video: MVideoWithAllFiles, |
62 | byActor: { id?: number }, | 62 | byActor: MActorId, |
63 | t: Transaction | 63 | t: Transaction |
64 | ) { | 64 | ) { |
65 | if (redundancyModel.actorId !== byActor.id) { | 65 | if (redundancyModel.actorId !== byActor.id) { |
diff --git a/server/lib/activitypub/playlist.ts b/server/lib/activitypub/playlist.ts index c2e2a3283..c52b715ef 100644 --- a/server/lib/activitypub/playlist.ts +++ b/server/lib/activitypub/playlist.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' | 1 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' |
2 | import { crawlCollectionPage } from './crawl' | 2 | import { crawlCollectionPage } from './crawl' |
3 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 3 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
4 | import { AccountModel } from '../../models/account/account' | ||
5 | import { isArray } from '../../helpers/custom-validators/misc' | 4 | import { isArray } from '../../helpers/custom-validators/misc' |
6 | import { getOrCreateActorAndServerAndModel } from './actor' | 5 | import { getOrCreateActorAndServerAndModel } from './actor' |
7 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
@@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object | |||
13 | import { getOrCreateVideoAndAccountAndChannel } from './videos' | 12 | import { getOrCreateVideoAndAccountAndChannel } from './videos' |
14 | import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' | 13 | import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' |
15 | import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' | 14 | import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' |
16 | import { VideoModel } from '../../models/video/video' | ||
17 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 15 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
18 | import { sequelizeTypescript } from '../../initializers/database' | 16 | import { sequelizeTypescript } from '../../initializers/database' |
19 | import { createPlaylistMiniatureFromUrl } from '../thumbnail' | 17 | import { createPlaylistMiniatureFromUrl } from '../thumbnail' |
20 | import { FilteredModelAttributes } from '../../typings/sequelize' | 18 | import { FilteredModelAttributes } from '../../typings/sequelize' |
21 | import { AccountModelId } from '../../typings/models' | 19 | import { MAccountDefault, MAccountId, MVideoId } from '../../typings/models' |
20 | import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../typings/models/video/video-playlist' | ||
22 | 21 | ||
23 | function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { | 22 | function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { |
24 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED | 23 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPlaylistPrivacy.PUBLIC : VideoPlaylistPrivacy.UNLISTED |
25 | 24 | ||
26 | return { | 25 | return { |
@@ -36,7 +35,7 @@ function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount | |||
36 | } | 35 | } |
37 | } | 36 | } |
38 | 37 | ||
39 | function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) { | 38 | function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: MVideoPlaylistId, video: MVideoId) { |
40 | return { | 39 | return { |
41 | position: elementObject.position, | 40 | position: elementObject.position, |
42 | url: elementObject.id, | 41 | url: elementObject.id, |
@@ -47,7 +46,7 @@ function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObje | |||
47 | } | 46 | } |
48 | } | 47 | } |
49 | 48 | ||
50 | async function createAccountPlaylists (playlistUrls: string[], account: AccountModel) { | 49 | async function createAccountPlaylists (playlistUrls: string[], account: MAccountDefault) { |
51 | await Bluebird.map(playlistUrls, async playlistUrl => { | 50 | await Bluebird.map(playlistUrls, async playlistUrl => { |
52 | try { | 51 | try { |
53 | const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) | 52 | const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) |
@@ -75,7 +74,7 @@ async function createAccountPlaylists (playlistUrls: string[], account: AccountM | |||
75 | }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) | 74 | }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) |
76 | } | 75 | } |
77 | 76 | ||
78 | async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { | 77 | async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { |
79 | const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) | 78 | const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) |
80 | 79 | ||
81 | if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) { | 80 | if (isArray(playlistObject.attributedTo) && playlistObject.attributedTo.length === 1) { |
@@ -88,7 +87,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc | |||
88 | } | 87 | } |
89 | } | 88 | } |
90 | 89 | ||
91 | const [ playlist ] = await VideoPlaylistModel.upsert<VideoPlaylistModel>(playlistAttributes, { returning: true }) | 90 | const [ playlist ] = await VideoPlaylistModel.upsert<MVideoPlaylist>(playlistAttributes, { returning: true }) |
92 | 91 | ||
93 | let accItems: string[] = [] | 92 | let accItems: string[] = [] |
94 | await crawlCollectionPage<string>(playlistObject.id, items => { | 93 | await crawlCollectionPage<string>(playlistObject.id, items => { |
@@ -114,7 +113,7 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc | |||
114 | return resetVideoPlaylistElements(accItems, refreshedPlaylist) | 113 | return resetVideoPlaylistElements(accItems, refreshedPlaylist) |
115 | } | 114 | } |
116 | 115 | ||
117 | async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> { | 116 | async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> { |
118 | if (!videoPlaylist.isOutdated()) return videoPlaylist | 117 | if (!videoPlaylist.isOutdated()) return videoPlaylist |
119 | 118 | ||
120 | try { | 119 | try { |
@@ -157,7 +156,7 @@ export { | |||
157 | 156 | ||
158 | // --------------------------------------------------------------------------- | 157 | // --------------------------------------------------------------------------- |
159 | 158 | ||
160 | async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { | 159 | async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) { |
161 | const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] | 160 | const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] |
162 | 161 | ||
163 | await Bluebird.map(elementUrls, async elementUrl => { | 162 | await Bluebird.map(elementUrls, async elementUrl => { |
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index cf27e6c32..86f7c764d 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
2 | import { ActorModel } from '../../../models/activitypub/actor' | ||
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
4 | import { addFetchOutboxJob } from '../actor' | 3 | import { addFetchOutboxJob } from '../actor' |
5 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 4 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
6 | import { SignatureActorModel } from '../../../typings/models' | 5 | import { MActorDefault, MActorSignature } from '../../../typings/models' |
7 | 6 | ||
8 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { | 7 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { |
9 | const { byActor: targetActor, inboxActor } = options | 8 | const { byActor: targetActor, inboxActor } = options |
@@ -20,7 +19,7 @@ export { | |||
20 | 19 | ||
21 | // --------------------------------------------------------------------------- | 20 | // --------------------------------------------------------------------------- |
22 | 21 | ||
23 | async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) { | 22 | async function processAccept (actor: MActorDefault, targetActor: MActorSignature) { |
24 | const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) | 23 | const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) |
25 | if (!follow) throw new Error('Cannot find associated follow.') | 24 | if (!follow) throw new Error('Cannot find associated follow.') |
26 | 25 | ||
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index b3cdc4441..91a9ad72c 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts | |||
@@ -5,10 +5,9 @@ import { VideoShareModel } from '../../../models/video/video-share' | |||
5 | import { forwardVideoRelatedActivity } from '../send/utils' | 5 | import { forwardVideoRelatedActivity } from '../send/utils' |
6 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' | 6 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' |
7 | import { Notifier } from '../../notifier' | 7 | import { Notifier } from '../../notifier' |
8 | import { VideoModel } from '../../../models/video/video' | ||
9 | import { logger } from '../../../helpers/logger' | 8 | import { logger } from '../../../helpers/logger' |
10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
11 | import { SignatureActorModel } from '../../../typings/models' | 10 | import { MActorSignature, MVideoAccountAllFiles } from '../../../typings/models' |
12 | 11 | ||
13 | async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) { | 12 | async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) { |
14 | const { activity, byActor: actorAnnouncer } = options | 13 | const { activity, byActor: actorAnnouncer } = options |
@@ -26,10 +25,10 @@ export { | |||
26 | 25 | ||
27 | // --------------------------------------------------------------------------- | 26 | // --------------------------------------------------------------------------- |
28 | 27 | ||
29 | async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) { | 28 | async function processVideoShare (actorAnnouncer: MActorSignature, activity: ActivityAnnounce, notify: boolean) { |
30 | const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id | 29 | const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id |
31 | 30 | ||
32 | let video: VideoModel | 31 | let video: MVideoAccountAllFiles |
33 | let videoCreated: boolean | 32 | let videoCreated: boolean |
34 | 33 | ||
35 | try { | 34 | try { |
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 6815c6997..c45f09f52 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -10,10 +10,8 @@ import { createOrUpdateCacheFile } from '../cache-file' | |||
10 | import { Notifier } from '../../notifier' | 10 | import { Notifier } from '../../notifier' |
11 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | 11 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' |
12 | import { createOrUpdateVideoPlaylist } from '../playlist' | 12 | import { createOrUpdateVideoPlaylist } from '../playlist' |
13 | import { VideoModel } from '../../../models/video/video' | ||
14 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 13 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
15 | import { VideoCommentModel } from '../../../models/video/video-comment' | 14 | import { MActorSignature, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../../typings/models' |
16 | import { SignatureActorModel } from '../../../typings/models' | ||
17 | 15 | ||
18 | async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { | 16 | async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { |
19 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
@@ -61,7 +59,7 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) { | |||
61 | return video | 59 | return video |
62 | } | 60 | } |
63 | 61 | ||
64 | async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) { | 62 | async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) { |
65 | const cacheFile = activity.object as CacheFileObject | 63 | const cacheFile = activity.object as CacheFileObject |
66 | 64 | ||
67 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object }) | 65 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFile.object }) |
@@ -77,15 +75,15 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: Signat | |||
77 | } | 75 | } |
78 | } | 76 | } |
79 | 77 | ||
80 | async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) { | 78 | async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) { |
81 | const commentObject = activity.object as VideoCommentObject | 79 | const commentObject = activity.object as VideoCommentObject |
82 | const byAccount = byActor.Account | 80 | const byAccount = byActor.Account |
83 | 81 | ||
84 | if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) | 82 | if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) |
85 | 83 | ||
86 | let video: VideoModel | 84 | let video: MVideoAccountAllFiles |
87 | let created: boolean | 85 | let created: boolean |
88 | let comment: VideoCommentModel | 86 | let comment: MCommentOwnerVideo |
89 | try { | 87 | try { |
90 | const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) | 88 | const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) |
91 | video = resolveThreadResult.video | 89 | video = resolveThreadResult.video |
@@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig | |||
110 | if (created && notify) Notifier.Instance.notifyOnNewComment(comment) | 108 | if (created && notify) Notifier.Instance.notifyOnNewComment(comment) |
111 | } | 109 | } |
112 | 110 | ||
113 | async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) { | 111 | async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorSignature) { |
114 | const playlistObject = activity.object as PlaylistObject | 112 | const playlistObject = activity.object as PlaylistObject |
115 | const byAccount = byActor.Account | 113 | const byAccount = byActor.Account |
116 | 114 | ||
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 344d14322..79d0e0d79 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -2,15 +2,13 @@ import { ActivityDelete } from '../../../../shared/models/activitypub' | |||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { sequelizeTypescript } from '../../../initializers' | 4 | import { sequelizeTypescript } from '../../../initializers' |
5 | import { AccountModel } from '../../../models/account/account' | ||
6 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
7 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
9 | import { VideoCommentModel } from '../../../models/video/video-comment' | 7 | import { VideoCommentModel } from '../../../models/video/video-comment' |
10 | import { forwardVideoRelatedActivity } from '../send/utils' | 8 | import { forwardVideoRelatedActivity } from '../send/utils' |
11 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | 9 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' |
12 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
13 | import { SignatureActorModel } from '../../../typings/models' | 11 | import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor } from '../../../typings/models' |
14 | 12 | ||
15 | async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { | 13 | async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { |
16 | const { activity, byActor } = options | 14 | const { activity, byActor } = options |
@@ -24,13 +22,17 @@ async function processDeleteActivity (options: APProcessorOptions<ActivityDelete | |||
24 | if (byActorFull.type === 'Person') { | 22 | if (byActorFull.type === 'Person') { |
25 | if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.') | 23 | if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.') |
26 | 24 | ||
27 | byActorFull.Account.Actor = await byActorFull.Account.$get('Actor') as ActorModel | 25 | const accountToDelete = byActorFull.Account as MAccountActor |
28 | return retryTransactionWrapper(processDeleteAccount, byActorFull.Account) | 26 | accountToDelete.Actor = byActorFull |
27 | |||
28 | return retryTransactionWrapper(processDeleteAccount, accountToDelete) | ||
29 | } else if (byActorFull.type === 'Group') { | 29 | } else if (byActorFull.type === 'Group') { |
30 | if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') | 30 | if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') |
31 | 31 | ||
32 | byActorFull.VideoChannel.Actor = await byActorFull.VideoChannel.$get('Actor') as ActorModel | 32 | const channelToDelete = byActorFull.VideoChannel as MChannelActorAccountActor |
33 | return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel) | 33 | channelToDelete.Actor = byActorFull |
34 | |||
35 | return retryTransactionWrapper(processDeleteVideoChannel, channelToDelete) | ||
34 | } | 36 | } |
35 | } | 37 | } |
36 | 38 | ||
@@ -70,7 +72,7 @@ export { | |||
70 | 72 | ||
71 | // --------------------------------------------------------------------------- | 73 | // --------------------------------------------------------------------------- |
72 | 74 | ||
73 | async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) { | 75 | async function processDeleteVideo (actor: MActor, videoToDelete: VideoModel) { |
74 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) | 76 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) |
75 | 77 | ||
76 | await sequelizeTypescript.transaction(async t => { | 78 | await sequelizeTypescript.transaction(async t => { |
@@ -84,7 +86,7 @@ async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) | |||
84 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) | 86 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) |
85 | } | 87 | } |
86 | 88 | ||
87 | async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: VideoPlaylistModel) { | 89 | async function processDeleteVideoPlaylist (actor: MActor, playlistToDelete: VideoPlaylistModel) { |
88 | logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid) | 90 | logger.debug('Removing remote video playlist "%s".', playlistToDelete.uuid) |
89 | 91 | ||
90 | await sequelizeTypescript.transaction(async t => { | 92 | await sequelizeTypescript.transaction(async t => { |
@@ -98,7 +100,7 @@ async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: | |||
98 | logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid) | 100 | logger.info('Remote video playlist with uuid %s removed.', playlistToDelete.uuid) |
99 | } | 101 | } |
100 | 102 | ||
101 | async function processDeleteAccount (accountToRemove: AccountModel) { | 103 | async function processDeleteAccount (accountToRemove: MAccountActor) { |
102 | logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) | 104 | logger.debug('Removing remote account "%s".', accountToRemove.Actor.url) |
103 | 105 | ||
104 | await sequelizeTypescript.transaction(async t => { | 106 | await sequelizeTypescript.transaction(async t => { |
@@ -108,7 +110,7 @@ async function processDeleteAccount (accountToRemove: AccountModel) { | |||
108 | logger.info('Remote account %s removed.', accountToRemove.Actor.url) | 110 | logger.info('Remote account %s removed.', accountToRemove.Actor.url) |
109 | } | 111 | } |
110 | 112 | ||
111 | async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { | 113 | async function processDeleteVideoChannel (videoChannelToRemove: MChannelActor) { |
112 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) | 114 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.url) |
113 | 115 | ||
114 | await sequelizeTypescript.transaction(async t => { | 116 | await sequelizeTypescript.transaction(async t => { |
@@ -118,7 +120,7 @@ async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelMode | |||
118 | logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) | 120 | logger.info('Remote video channel %s removed.', videoChannelToRemove.Actor.url) |
119 | } | 121 | } |
120 | 122 | ||
121 | function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { | 123 | function processDeleteVideoComment (byActor: MActorSignature, videoComment: VideoCommentModel, activity: ActivityDelete) { |
122 | logger.debug('Removing remote video comment "%s".', videoComment.url) | 124 | logger.debug('Removing remote video comment "%s".', videoComment.url) |
123 | 125 | ||
124 | return sequelizeTypescript.transaction(async t => { | 126 | return sequelizeTypescript.transaction(async t => { |
diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts index 727fcfee0..debd8a67c 100644 --- a/server/lib/activitypub/process/process-dislike.ts +++ b/server/lib/activitypub/process/process-dislike.ts | |||
@@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' | |||
7 | import { forwardVideoRelatedActivity } from '../send/utils' | 7 | import { forwardVideoRelatedActivity } from '../send/utils' |
8 | import { getVideoDislikeActivityPubUrl } from '../url' | 8 | import { getVideoDislikeActivityPubUrl } from '../url' |
9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
10 | import { SignatureActorModel } from '../../../typings/models' | 10 | import { MActorSignature } from '../../../typings/models' |
11 | 11 | ||
12 | async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { | 12 | async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { |
13 | const { activity, byActor } = options | 13 | const { activity, byActor } = options |
@@ -22,7 +22,7 @@ export { | |||
22 | 22 | ||
23 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
24 | 24 | ||
25 | async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) { | 25 | async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) { |
26 | const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object | 26 | const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object |
27 | const byAccount = byActor.Account | 27 | const byAccount = byActor.Account |
28 | 28 | ||
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts index 1f8a80c14..422386540 100644 --- a/server/lib/activitypub/process/process-flag.ts +++ b/server/lib/activitypub/process/process-flag.ts | |||
@@ -8,7 +8,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' | |||
8 | import { Notifier } from '../../notifier' | 8 | import { Notifier } from '../../notifier' |
9 | import { getAPId } from '../../../helpers/activitypub' | 9 | import { getAPId } from '../../../helpers/activitypub' |
10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 10 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
11 | import { SignatureActorModel } from '../../../typings/models' | 11 | import { MActorSignature, MVideoAbuseVideo } from '../../../typings/models' |
12 | 12 | ||
13 | async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { | 13 | async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { |
14 | const { activity, byActor } = options | 14 | const { activity, byActor } = options |
@@ -23,7 +23,7 @@ export { | |||
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
25 | 25 | ||
26 | async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) { | 26 | async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) { |
27 | const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) | 27 | const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) |
28 | 28 | ||
29 | logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object)) | 29 | logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object)) |
@@ -41,7 +41,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, | |||
41 | state: VideoAbuseState.PENDING | 41 | state: VideoAbuseState.PENDING |
42 | } | 42 | } |
43 | 43 | ||
44 | const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) | 44 | const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo |
45 | videoAbuseInstance.Video = video | 45 | videoAbuseInstance.Video = video |
46 | 46 | ||
47 | logger.info('Remote abuse for video uuid %s created', flag.object) | 47 | logger.info('Remote abuse for video uuid %s created', flag.object) |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 240aa5799..bc5660395 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -10,8 +10,7 @@ import { getAPId } from '../../../helpers/activitypub' | |||
10 | import { getServerActor } from '../../../helpers/utils' | 10 | import { getServerActor } from '../../../helpers/utils' |
11 | import { CONFIG } from '../../../initializers/config' | 11 | import { CONFIG } from '../../../initializers/config' |
12 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 12 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
13 | import { SignatureActorModel } from '../../../typings/models' | 13 | import { MAccount, MActorFollowActors, MActorFollowFull, MActorSignature } from '../../../typings/models' |
14 | import { ActorFollowModelLight } from '../../../typings/models/actor-follow' | ||
15 | 14 | ||
16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { | 15 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { |
17 | const { activity, byActor } = options | 16 | const { activity, byActor } = options |
@@ -28,7 +27,7 @@ export { | |||
28 | 27 | ||
29 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
30 | 29 | ||
31 | async function processFollow (byActor: SignatureActorModel, targetActorURL: string) { | 30 | async function processFollow (byActor: MActorSignature, targetActorURL: string) { |
32 | const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { | 31 | const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { |
33 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) | 32 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) |
34 | 33 | ||
@@ -43,10 +42,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
43 | 42 | ||
44 | await sendReject(byActor, targetActor) | 43 | await sendReject(byActor, targetActor) |
45 | 44 | ||
46 | return { actorFollow: undefined } | 45 | return { actorFollow: undefined as MActorFollowActors } |
47 | } | 46 | } |
48 | 47 | ||
49 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ | 48 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({ |
50 | where: { | 49 | where: { |
51 | actorId: byActor.id, | 50 | actorId: byActor.id, |
52 | targetActorId: targetActor.id | 51 | targetActorId: targetActor.id |
@@ -57,7 +56,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
57 | state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' | 56 | state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' |
58 | }, | 57 | }, |
59 | transaction: t | 58 | transaction: t |
60 | }) as [ ActorFollowModelLight, boolean ] | 59 | }) |
61 | 60 | ||
62 | if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { | 61 | if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { |
63 | actorFollow.state = 'accepted' | 62 | actorFollow.state = 'accepted' |
@@ -77,8 +76,14 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
77 | if (!actorFollow) return | 76 | if (!actorFollow) return |
78 | 77 | ||
79 | if (created) { | 78 | if (created) { |
80 | if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) | 79 | if (isFollowingInstance) { |
81 | else Notifier.Instance.notifyOfNewUserFollow(actorFollow) | 80 | Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) |
81 | } else { | ||
82 | const actorFollowFull = actorFollow as MActorFollowFull | ||
83 | actorFollowFull.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as MAccount | ||
84 | |||
85 | Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) | ||
86 | } | ||
82 | } | 87 | } |
83 | 88 | ||
84 | logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) | 89 | logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) |
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts index cf559af72..62be0de42 100644 --- a/server/lib/activitypub/process/process-like.ts +++ b/server/lib/activitypub/process/process-like.ts | |||
@@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' | |||
7 | import { getVideoLikeActivityPubUrl } from '../url' | 7 | import { getVideoLikeActivityPubUrl } from '../url' |
8 | import { getAPId } from '../../../helpers/activitypub' | 8 | import { getAPId } from '../../../helpers/activitypub' |
9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
10 | import { SignatureActorModel } from '../../../typings/models' | 10 | import { MActorSignature } from '../../../typings/models' |
11 | 11 | ||
12 | async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { | 12 | async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { |
13 | const { activity, byActor } = options | 13 | const { activity, byActor } = options |
@@ -22,7 +22,7 @@ export { | |||
22 | 22 | ||
23 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
24 | 24 | ||
25 | async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) { | 25 | async function processLikeVideo (byActor: MActorSignature, activity: ActivityLike) { |
26 | const videoUrl = getAPId(activity.object) | 26 | const videoUrl = getAPId(activity.object) |
27 | 27 | ||
28 | const byAccount = byActor.Account | 28 | const byAccount = byActor.Account |
diff --git a/server/lib/activitypub/process/process-reject.ts b/server/lib/activitypub/process/process-reject.ts index 22e311ceb..00e9afa10 100644 --- a/server/lib/activitypub/process/process-reject.ts +++ b/server/lib/activitypub/process/process-reject.ts | |||
@@ -2,7 +2,7 @@ import { ActivityReject } from '../../../../shared/models/activitypub/activity' | |||
2 | import { sequelizeTypescript } from '../../../initializers' | 2 | import { sequelizeTypescript } from '../../../initializers' |
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
4 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 4 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
5 | import { ActorModelOnly } from '../../../typings/models' | 5 | import { MActor } from '../../../typings/models' |
6 | 6 | ||
7 | async function processRejectActivity (options: APProcessorOptions<ActivityReject>) { | 7 | async function processRejectActivity (options: APProcessorOptions<ActivityReject>) { |
8 | const { byActor: targetActor, inboxActor } = options | 8 | const { byActor: targetActor, inboxActor } = options |
@@ -19,7 +19,7 @@ export { | |||
19 | 19 | ||
20 | // --------------------------------------------------------------------------- | 20 | // --------------------------------------------------------------------------- |
21 | 21 | ||
22 | async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) { | 22 | async function processReject (follower: MActor, targetActor: MActor) { |
23 | return sequelizeTypescript.transaction(async t => { | 23 | return sequelizeTypescript.transaction(async t => { |
24 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t) | 24 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, targetActor.id, t) |
25 | 25 | ||
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index c37ee38bb..10643b2e9 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts | |||
@@ -11,7 +11,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos' | |||
11 | import { VideoShareModel } from '../../../models/video/video-share' | 11 | import { VideoShareModel } from '../../../models/video/video-share' |
12 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | 12 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' |
13 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 13 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
14 | import { SignatureActorModel } from '../../../typings/models' | 14 | import { MActorSignature } from '../../../typings/models' |
15 | 15 | ||
16 | async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { | 16 | async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { |
17 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
@@ -54,7 +54,7 @@ export { | |||
54 | 54 | ||
55 | // --------------------------------------------------------------------------- | 55 | // --------------------------------------------------------------------------- |
56 | 56 | ||
57 | async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) { | 57 | async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) { |
58 | const likeActivity = activity.object as ActivityLike | 58 | const likeActivity = activity.object as ActivityLike |
59 | 59 | ||
60 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object }) | 60 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: likeActivity.object }) |
@@ -77,7 +77,7 @@ async function processUndoLike (byActor: SignatureActorModel, activity: Activity | |||
77 | }) | 77 | }) |
78 | } | 78 | } |
79 | 79 | ||
80 | async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) { | 80 | async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo) { |
81 | const dislike = activity.object.type === 'Dislike' | 81 | const dislike = activity.object.type === 'Dislike' |
82 | ? activity.object | 82 | ? activity.object |
83 | : activity.object.object as DislikeObject | 83 | : activity.object.object as DislikeObject |
@@ -102,7 +102,7 @@ async function processUndoDislike (byActor: SignatureActorModel, activity: Activ | |||
102 | }) | 102 | }) |
103 | } | 103 | } |
104 | 104 | ||
105 | async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) { | 105 | async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) { |
106 | const cacheFileObject = activity.object.object as CacheFileObject | 106 | const cacheFileObject = activity.object.object as CacheFileObject |
107 | 107 | ||
108 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) | 108 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) |
@@ -127,7 +127,7 @@ async function processUndoCacheFile (byActor: SignatureActorModel, activity: Act | |||
127 | }) | 127 | }) |
128 | } | 128 | } |
129 | 129 | ||
130 | function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) { | 130 | function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) { |
131 | return sequelizeTypescript.transaction(async t => { | 131 | return sequelizeTypescript.transaction(async t => { |
132 | const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) | 132 | const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t) |
133 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) | 133 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) |
@@ -140,7 +140,7 @@ function processUndoFollow (follower: SignatureActorModel, followActivity: Activ | |||
140 | }) | 140 | }) |
141 | } | 141 | } |
142 | 142 | ||
143 | function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) { | 143 | function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) { |
144 | return sequelizeTypescript.transaction(async t => { | 144 | return sequelizeTypescript.transaction(async t => { |
145 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) | 145 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) |
146 | if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) | 146 | if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 414f9e375..9f80a0ce9 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -15,7 +15,7 @@ import { forwardVideoRelatedActivity } from '../send/utils' | |||
15 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | 15 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' |
16 | import { createOrUpdateVideoPlaylist } from '../playlist' | 16 | import { createOrUpdateVideoPlaylist } from '../playlist' |
17 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 17 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
18 | import { SignatureActorModel } from '../../../typings/models' | 18 | import { MActorSignature } from '../../../typings/models' |
19 | 19 | ||
20 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { | 20 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { |
21 | const { activity, byActor } = options | 21 | const { activity, byActor } = options |
@@ -53,7 +53,7 @@ export { | |||
53 | 53 | ||
54 | // --------------------------------------------------------------------------- | 54 | // --------------------------------------------------------------------------- |
55 | 55 | ||
56 | async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) { | 56 | async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) { |
57 | const videoObject = activity.object as VideoTorrentObject | 57 | const videoObject = activity.object as VideoTorrentObject |
58 | 58 | ||
59 | if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { | 59 | if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { |
@@ -61,20 +61,20 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit | |||
61 | return undefined | 61 | return undefined |
62 | } | 62 | } |
63 | 63 | ||
64 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false }) | 64 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false, fetchType: 'all' }) |
65 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 65 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
66 | 66 | ||
67 | const updateOptions = { | 67 | const updateOptions = { |
68 | video, | 68 | video, |
69 | videoObject, | 69 | videoObject, |
70 | account: actor.Account, | 70 | account: channelActor.VideoChannel.Account, |
71 | channel: channelActor.VideoChannel, | 71 | channel: channelActor.VideoChannel, |
72 | overrideTo: activity.to | 72 | overrideTo: activity.to |
73 | } | 73 | } |
74 | return updateVideoFromAP(updateOptions) | 74 | return updateVideoFromAP(updateOptions) |
75 | } | 75 | } |
76 | 76 | ||
77 | async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) { | 77 | async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) { |
78 | const cacheFileObject = activity.object as CacheFileObject | 78 | const cacheFileObject = activity.object as CacheFileObject |
79 | 79 | ||
80 | if (!isCacheFileObjectValid(cacheFileObject)) { | 80 | if (!isCacheFileObjectValid(cacheFileObject)) { |
@@ -150,7 +150,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
150 | } | 150 | } |
151 | } | 151 | } |
152 | 152 | ||
153 | async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) { | 153 | async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) { |
154 | const playlistObject = activity.object as PlaylistObject | 154 | const playlistObject = activity.object as PlaylistObject |
155 | const byAccount = byActor.Account | 155 | const byAccount = byActor.Account |
156 | 156 | ||
diff --git a/server/lib/activitypub/process/process-view.ts b/server/lib/activitypub/process/process-view.ts index e4997b828..df29ee968 100644 --- a/server/lib/activitypub/process/process-view.ts +++ b/server/lib/activitypub/process/process-view.ts | |||
@@ -3,7 +3,7 @@ import { forwardVideoRelatedActivity } from '../send/utils' | |||
3 | import { Redis } from '../../redis' | 3 | import { Redis } from '../../redis' |
4 | import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' | 4 | import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' |
5 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 5 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
6 | import { SignatureActorModel } from '../../../typings/models' | 6 | import { MActorSignature } from '../../../typings/models' |
7 | 7 | ||
8 | async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) { | 8 | async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) { |
9 | const { activity, byActor } = options | 9 | const { activity, byActor } = options |
@@ -18,11 +18,11 @@ export { | |||
18 | 18 | ||
19 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
20 | 20 | ||
21 | async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) { | 21 | async function processCreateView (activity: ActivityView | ActivityCreate, byActor: MActorSignature) { |
22 | const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object | 22 | const videoObject = activity.type === 'View' ? activity.object : (activity.object as ViewObject).object |
23 | 23 | ||
24 | const options = { | 24 | const options = { |
25 | videoObject: videoObject, | 25 | videoObject, |
26 | fetchType: 'only-video' as 'only-video' | 26 | fetchType: 'only-video' as 'only-video' |
27 | } | 27 | } |
28 | const { video } = await getOrCreateVideoAndAccountAndChannel(options) | 28 | const { video } = await getOrCreateVideoAndAccountAndChannel(options) |
diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts index d108fe321..c602bf218 100644 --- a/server/lib/activitypub/process/process.ts +++ b/server/lib/activitypub/process/process.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { Activity, ActivityType } from '../../../../shared/models/activitypub' | 1 | import { Activity, ActivityType } from '../../../../shared/models/activitypub' |
2 | import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' | 2 | import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { processAcceptActivity } from './process-accept' | 4 | import { processAcceptActivity } from './process-accept' |
6 | import { processAnnounceActivity } from './process-announce' | 5 | import { processAnnounceActivity } from './process-announce' |
7 | import { processCreateActivity } from './process-create' | 6 | import { processCreateActivity } from './process-create' |
@@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike' | |||
16 | import { processFlagActivity } from './process-flag' | 15 | import { processFlagActivity } from './process-flag' |
17 | import { processViewActivity } from './process-view' | 16 | import { processViewActivity } from './process-view' |
18 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' | 17 | import { APProcessorOptions } from '../../../typings/activitypub-processor.model' |
19 | import { SignatureActorModel } from '../../../typings/models' | 18 | import { MActorDefault, MActorSignature } from '../../../typings/models' |
20 | 19 | ||
21 | const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = { | 20 | const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = { |
22 | Create: processCreateActivity, | 21 | Create: processCreateActivity, |
@@ -36,15 +35,15 @@ const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Act | |||
36 | async function processActivities ( | 35 | async function processActivities ( |
37 | activities: Activity[], | 36 | activities: Activity[], |
38 | options: { | 37 | options: { |
39 | signatureActor?: SignatureActorModel | 38 | signatureActor?: MActorSignature |
40 | inboxActor?: ActorModel | 39 | inboxActor?: MActorDefault |
41 | outboxUrl?: string | 40 | outboxUrl?: string |
42 | fromFetch?: boolean | 41 | fromFetch?: boolean |
43 | } = {} | 42 | } = {} |
44 | ) { | 43 | ) { |
45 | const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options | 44 | const { outboxUrl, signatureActor, inboxActor, fromFetch = false } = options |
46 | 45 | ||
47 | const actorsCache: { [ url: string ]: SignatureActorModel } = {} | 46 | const actorsCache: { [ url: string ]: MActorSignature } = {} |
48 | 47 | ||
49 | for (const activity of activities) { | 48 | for (const activity of activities) { |
50 | if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) { | 49 | if (!signatureActor && [ 'Create', 'Announce', 'Like' ].includes(activity.type) === false) { |
@@ -75,7 +74,7 @@ async function processActivities ( | |||
75 | } | 74 | } |
76 | 75 | ||
77 | try { | 76 | try { |
78 | await activityProcessor({ activity, byActor, inboxActor: inboxActor, fromFetch }) | 77 | await activityProcessor({ activity, byActor, inboxActor, fromFetch }) |
79 | } catch (err) { | 78 | } catch (err) { |
80 | logger.warn('Cannot process activity %s.', activity.type, { err }) | 79 | logger.warn('Cannot process activity %s.', activity.type, { err }) |
81 | } | 80 | } |
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 813c42e15..9f0225b64 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts | |||
@@ -3,10 +3,9 @@ import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from | |||
3 | import { unicastTo } from './utils' | 3 | import { unicastTo } from './utils' |
4 | import { buildFollowActivity } from './send-follow' | 4 | import { buildFollowActivity } from './send-follow' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { ActorFollowModelLight } from '../../../typings/models/actor-follow' | 6 | import { MActor, MActorFollowActors } from '../../../typings/models' |
7 | import { ActorModelOnly } from '../../../typings/models' | ||
8 | 7 | ||
9 | async function sendAccept (actorFollow: ActorFollowModelLight) { | 8 | async function sendAccept (actorFollow: MActorFollowActors) { |
10 | const follower = actorFollow.ActorFollower | 9 | const follower = actorFollow.ActorFollower |
11 | const me = actorFollow.ActorFollowing | 10 | const me = actorFollow.ActorFollowing |
12 | 11 | ||
@@ -34,7 +33,7 @@ export { | |||
34 | 33 | ||
35 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
36 | 35 | ||
37 | function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept { | 36 | function buildAcceptActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityAccept { |
38 | return { | 37 | return { |
39 | type: 'Accept', | 38 | type: 'Accept', |
40 | id: url, | 39 | id: url, |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index 7fe4ca180..a0f33852c 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -1,16 +1,15 @@ | |||
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/activitypub' |
3 | import { VideoModel } from '../../../models/video/video' | ||
4 | import { broadcastToFollowers } from './utils' | 3 | import { broadcastToFollowers } from './utils' |
5 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' | 4 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' |
6 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
7 | import { ActorModelOnly } from '../../../typings/models' | 6 | import { MActorLight, MVideo } from '../../../typings/models' |
8 | import { VideoShareModelOnly } from '../../../typings/models/video-share' | 7 | import { MVideoShare } from '../../../typings/models/video' |
9 | 8 | ||
10 | async function buildAnnounceWithVideoAudience ( | 9 | async function buildAnnounceWithVideoAudience ( |
11 | byActor: ActorModelOnly, | 10 | byActor: MActorLight, |
12 | videoShare: VideoShareModelOnly, | 11 | videoShare: MVideoShare, |
13 | video: VideoModel, | 12 | video: MVideo, |
14 | t: Transaction | 13 | t: Transaction |
15 | ) { | 14 | ) { |
16 | const announcedObject = video.url | 15 | const announcedObject = video.url |
@@ -23,7 +22,7 @@ async function buildAnnounceWithVideoAudience ( | |||
23 | return { activity, actorsInvolvedInVideo } | 22 | return { activity, actorsInvolvedInVideo } |
24 | } | 23 | } |
25 | 24 | ||
26 | async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShareModelOnly, video: VideoModel, t: Transaction) { | 25 | async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { |
27 | const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) | 26 | const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t) |
28 | 27 | ||
29 | logger.info('Creating job to send announce %s.', videoShare.url) | 28 | logger.info('Creating job to send announce %s.', videoShare.url) |
@@ -32,7 +31,7 @@ async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShar | |||
32 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException) | 31 | return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException) |
33 | } | 32 | } |
34 | 33 | ||
35 | function buildAnnounceActivity (url: string, byActor: ActorModelOnly, object: string, audience?: ActivityAudience): ActivityAnnounce { | 34 | function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce { |
36 | if (!audience) audience = getAudience(byActor) | 35 | if (!audience) audience = getAudience(byActor) |
37 | 36 | ||
38 | return audiencify({ | 37 | return audiencify({ |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 9c21149f2..26ec3e948 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -1,19 +1,23 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { VideoModel } from '../../../models/video/video' | ||
6 | import { VideoCommentModel } from '../../../models/video/video-comment' | 4 | import { VideoCommentModel } from '../../../models/video/video-comment' |
7 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 5 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' |
8 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' | 6 | import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' |
9 | import { logger } from '../../../helpers/logger' | 7 | import { logger } from '../../../helpers/logger' |
10 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
11 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
12 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | 8 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' |
13 | import { getServerActor } from '../../../helpers/utils' | 9 | import { getServerActor } from '../../../helpers/utils' |
14 | import * as Bluebird from 'bluebird' | 10 | import { |
15 | 11 | MActorLight, | |
16 | async function sendCreateVideo (video: VideoModel, t: Transaction) { | 12 | MCommentOwnerVideo, |
13 | MVideoAccountLight, | ||
14 | MVideoAP, | ||
15 | MVideoPlaylistFull, | ||
16 | MVideoRedundancyFileVideo, | ||
17 | MVideoRedundancyStreamingPlaylistVideo | ||
18 | } from '../../../typings/models' | ||
19 | |||
20 | async function sendCreateVideo (video: MVideoAP, t: Transaction) { | ||
17 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 21 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
18 | 22 | ||
19 | logger.info('Creating job to send video creation of %s.', video.url) | 23 | logger.info('Creating job to send video creation of %s.', video.url) |
@@ -27,7 +31,11 @@ async function sendCreateVideo (video: VideoModel, t: Transaction) { | |||
27 | return broadcastToFollowers(createActivity, byActor, [ byActor ], t) | 31 | return broadcastToFollowers(createActivity, byActor, [ byActor ], t) |
28 | } | 32 | } |
29 | 33 | ||
30 | async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, fileRedundancy: VideoRedundancyModel) { | 34 | async function sendCreateCacheFile ( |
35 | byActor: MActorLight, | ||
36 | video: MVideoAccountLight, | ||
37 | fileRedundancy: MVideoRedundancyStreamingPlaylistVideo | MVideoRedundancyFileVideo | ||
38 | ) { | ||
31 | logger.info('Creating job to send file cache of %s.', fileRedundancy.url) | 39 | logger.info('Creating job to send file cache of %s.', fileRedundancy.url) |
32 | 40 | ||
33 | return sendVideoRelatedCreateActivity({ | 41 | return sendVideoRelatedCreateActivity({ |
@@ -38,7 +46,7 @@ async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, file | |||
38 | }) | 46 | }) |
39 | } | 47 | } |
40 | 48 | ||
41 | async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transaction) { | 49 | async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transaction) { |
42 | if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined | 50 | if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined |
43 | 51 | ||
44 | logger.info('Creating job to send create video playlist of %s.', playlist.url) | 52 | logger.info('Creating job to send create video playlist of %s.', playlist.url) |
@@ -57,7 +65,7 @@ async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transac | |||
57 | return broadcastToFollowers(createActivity, byActor, toFollowersOf, t) | 65 | return broadcastToFollowers(createActivity, byActor, toFollowersOf, t) |
58 | } | 66 | } |
59 | 67 | ||
60 | async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) { | 68 | async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transaction) { |
61 | logger.info('Creating job to send comment %s.', comment.url) | 69 | logger.info('Creating job to send comment %s.', comment.url) |
62 | 70 | ||
63 | const isOrigin = comment.Video.isOwned() | 71 | const isOrigin = comment.Video.isOwned() |
@@ -95,7 +103,7 @@ async function sendCreateVideoComment (comment: VideoCommentModel, t: Transactio | |||
95 | t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) | 103 | t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) |
96 | } | 104 | } |
97 | 105 | ||
98 | function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { | 106 | function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate { |
99 | if (!audience) audience = getAudience(byActor) | 107 | if (!audience) audience = getAudience(byActor) |
100 | 108 | ||
101 | return audiencify( | 109 | return audiencify( |
@@ -122,8 +130,8 @@ export { | |||
122 | // --------------------------------------------------------------------------- | 130 | // --------------------------------------------------------------------------- |
123 | 131 | ||
124 | async function sendVideoRelatedCreateActivity (options: { | 132 | async function sendVideoRelatedCreateActivity (options: { |
125 | byActor: ActorModel, | 133 | byActor: MActorLight, |
126 | video: VideoModel, | 134 | video: MVideoAccountLight, |
127 | url: string, | 135 | url: string, |
128 | object: any, | 136 | object: any, |
129 | transaction?: Transaction | 137 | transaction?: Transaction |
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index 6c7fb8449..4b1ff8dc5 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -1,17 +1,17 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | ||
5 | import { VideoCommentModel } from '../../../models/video/video-comment' | 4 | import { VideoCommentModel } from '../../../models/video/video-comment' |
6 | import { VideoShareModel } from '../../../models/video/video-share' | 5 | import { VideoShareModel } from '../../../models/video/video-share' |
7 | import { getDeleteActivityPubUrl } from '../url' | 6 | import { getDeleteActivityPubUrl } from '../url' |
8 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 7 | import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' |
9 | import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' | 8 | import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' |
10 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
11 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
12 | import { getServerActor } from '../../../helpers/utils' | 10 | import { getServerActor } from '../../../helpers/utils' |
11 | import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video' | ||
12 | import { MActorUrl } from '../../../typings/models' | ||
13 | 13 | ||
14 | async function sendDeleteVideo (video: VideoModel, transaction: Transaction) { | 14 | async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) { |
15 | logger.info('Creating job to broadcast delete of video %s.', video.url) | 15 | logger.info('Creating job to broadcast delete of video %s.', video.url) |
16 | 16 | ||
17 | const byActor = video.VideoChannel.Account.Actor | 17 | const byActor = video.VideoChannel.Account.Actor |
@@ -42,7 +42,7 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) { | |||
42 | return broadcastToFollowers(activity, byActor, actorsInvolved, t) | 42 | return broadcastToFollowers(activity, byActor, actorsInvolved, t) |
43 | } | 43 | } |
44 | 44 | ||
45 | async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { | 45 | async function sendDeleteVideoComment (videoComment: MCommentOwnerVideoReply, t: Transaction) { |
46 | logger.info('Creating job to send delete of comment %s.', videoComment.url) | 46 | logger.info('Creating job to send delete of comment %s.', videoComment.url) |
47 | 47 | ||
48 | const isVideoOrigin = videoComment.Video.isOwned() | 48 | const isVideoOrigin = videoComment.Video.isOwned() |
@@ -74,7 +74,7 @@ async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Trans | |||
74 | t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) | 74 | t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)) |
75 | } | 75 | } |
76 | 76 | ||
77 | async function sendDeleteVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { | 77 | async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) { |
78 | logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) | 78 | logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url) |
79 | 79 | ||
80 | const byActor = videoPlaylist.OwnerAccount.Actor | 80 | const byActor = videoPlaylist.OwnerAccount.Actor |
@@ -101,7 +101,7 @@ export { | |||
101 | 101 | ||
102 | // --------------------------------------------------------------------------- | 102 | // --------------------------------------------------------------------------- |
103 | 103 | ||
104 | function buildDeleteActivity (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { | 104 | function buildDeleteActivity (url: string, object: string, byActor: MActorUrl, audience?: ActivityAudience): ActivityDelete { |
105 | const activity = { | 105 | const activity = { |
106 | type: 'Delete' as 'Delete', | 106 | type: 'Delete' as 'Delete', |
107 | id: url, | 107 | id: url, |
diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts index a88436f2c..6e41f241f 100644 --- a/server/lib/activitypub/send/send-dislike.ts +++ b/server/lib/activitypub/send/send-dislike.ts | |||
@@ -1,13 +1,12 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActorModel } from '../../../models/activitypub/actor' | ||
3 | import { VideoModel } from '../../../models/video/video' | ||
4 | import { getVideoDislikeActivityPubUrl } from '../url' | 2 | import { getVideoDislikeActivityPubUrl } from '../url' |
5 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
6 | import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' | 4 | import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' |
7 | import { sendVideoRelatedActivity } from './utils' | 5 | import { sendVideoRelatedActivity } from './utils' |
8 | import { audiencify, getAudience } from '../audience' | 6 | import { audiencify, getAudience } from '../audience' |
7 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' | ||
9 | 8 | ||
10 | async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 9 | async function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
11 | logger.info('Creating job to dislike %s.', video.url) | 10 | logger.info('Creating job to dislike %s.', video.url) |
12 | 11 | ||
13 | const activityBuilder = (audience: ActivityAudience) => { | 12 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -19,7 +18,7 @@ async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transacti | |||
19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) |
20 | } | 19 | } |
21 | 20 | ||
22 | function buildDislikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityDislike { | 21 | function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { |
23 | if (!audience) audience = getAudience(byActor) | 22 | if (!audience) audience = getAudience(byActor) |
24 | 23 | ||
25 | return audiencify( | 24 | return audiencify( |
diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts index 61ee389a6..5ae1614ab 100644 --- a/server/lib/activitypub/send/send-flag.ts +++ b/server/lib/activitypub/send/send-flag.ts | |||
@@ -1,14 +1,13 @@ | |||
1 | import { ActorModel } from '../../../models/activitypub/actor' | ||
2 | import { VideoModel } from '../../../models/video/video' | ||
3 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | ||
4 | import { getVideoAbuseActivityPubUrl } from '../url' | 1 | import { getVideoAbuseActivityPubUrl } from '../url' |
5 | import { unicastTo } from './utils' | 2 | import { unicastTo } from './utils' |
6 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
7 | import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' | 4 | import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' |
8 | import { audiencify, getAudience } from '../audience' | 5 | import { audiencify, getAudience } from '../audience' |
9 | import { Transaction } from 'sequelize' | 6 | import { Transaction } from 'sequelize' |
7 | import { MActor, MVideoFullLight } from '../../../typings/models' | ||
8 | import { MVideoAbuseVideo } from '../../../typings/models/video' | ||
10 | 9 | ||
11 | async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { | 10 | async function sendVideoAbuse (byActor: MActor, videoAbuse: MVideoAbuseVideo, video: MVideoFullLight, t: Transaction) { |
12 | if (!video.VideoChannel.Account.Actor.serverId) return // Local user | 11 | if (!video.VideoChannel.Account.Actor.serverId) return // Local user |
13 | 12 | ||
14 | const url = getVideoAbuseActivityPubUrl(videoAbuse) | 13 | const url = getVideoAbuseActivityPubUrl(videoAbuse) |
@@ -22,7 +21,7 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, | |||
22 | t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)) | 21 | t.afterCommit(() => unicastTo(flagActivity, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)) |
23 | } | 22 | } |
24 | 23 | ||
25 | function buildFlagActivity (url: string, byActor: ActorModel, videoAbuse: VideoAbuseModel, audience: ActivityAudience): ActivityFlag { | 24 | function buildFlagActivity (url: string, byActor: MActor, videoAbuse: MVideoAbuseVideo, audience: ActivityAudience): ActivityFlag { |
26 | if (!audience) audience = getAudience(byActor) | 25 | if (!audience) audience = getAudience(byActor) |
27 | 26 | ||
28 | const activity = Object.assign( | 27 | const activity = Object.assign( |
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index a59ed50cf..6b17b25da 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -4,9 +4,9 @@ import { getActorFollowActivityPubUrl } from '../url' | |||
4 | import { unicastTo } from './utils' | 4 | import { unicastTo } from './utils' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { Transaction } from 'sequelize' | 6 | import { Transaction } from 'sequelize' |
7 | import { ActorModelOnly } from '../../../typings/models' | 7 | import { MActor, MActorFollowActors } from '../../../typings/models' |
8 | 8 | ||
9 | function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { | 9 | function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { |
10 | const me = actorFollow.ActorFollower | 10 | const me = actorFollow.ActorFollower |
11 | const following = actorFollow.ActorFollowing | 11 | const following = actorFollow.ActorFollowing |
12 | 12 | ||
@@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { | |||
21 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) | 21 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) |
22 | } | 22 | } |
23 | 23 | ||
24 | function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow { | 24 | function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow { |
25 | return { | 25 | return { |
26 | type: 'Follow', | 26 | type: 'Follow', |
27 | id: url, | 27 | id: url, |
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 35227887a..e84a6f98b 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts | |||
@@ -1,13 +1,12 @@ | |||
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/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | ||
4 | import { VideoModel } from '../../../models/video/video' | ||
5 | import { getVideoLikeActivityPubUrl } from '../url' | 3 | import { getVideoLikeActivityPubUrl } from '../url' |
6 | import { sendVideoRelatedActivity } from './utils' | 4 | import { sendVideoRelatedActivity } from './utils' |
7 | import { audiencify, getAudience } from '../audience' | 5 | import { audiencify, getAudience } from '../audience' |
8 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
7 | import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models' | ||
9 | 8 | ||
10 | async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 9 | async function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
11 | logger.info('Creating job to like %s.', video.url) | 10 | logger.info('Creating job to like %s.', video.url) |
12 | 11 | ||
13 | const activityBuilder = (audience: ActivityAudience) => { | 12 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -19,7 +18,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) | |||
19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 18 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) |
20 | } | 19 | } |
21 | 20 | ||
22 | function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { | 21 | function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { |
23 | if (!audience) audience = getAudience(byActor) | 22 | if (!audience) audience = getAudience(byActor) |
24 | 23 | ||
25 | return audiencify( | 24 | return audiencify( |
diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts index 63110b433..4258a3c36 100644 --- a/server/lib/activitypub/send/send-reject.ts +++ b/server/lib/activitypub/send/send-reject.ts | |||
@@ -1,12 +1,11 @@ | |||
1 | import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' | 1 | import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' |
2 | import { ActorModel } from '../../../models/activitypub/actor' | ||
3 | import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' | 2 | import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' |
4 | import { unicastTo } from './utils' | 3 | import { unicastTo } from './utils' |
5 | import { buildFollowActivity } from './send-follow' | 4 | import { buildFollowActivity } from './send-follow' |
6 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
7 | import { SignatureActorModel } from '../../../typings/models' | 6 | import { MActor } from '../../../typings/models' |
8 | 7 | ||
9 | async function sendReject (follower: SignatureActorModel, following: ActorModel) { | 8 | async function sendReject (follower: MActor, following: MActor) { |
10 | if (!follower.serverId) { // This should never happen | 9 | if (!follower.serverId) { // This should never happen |
11 | logger.warn('Do not sending reject to local follower.') | 10 | logger.warn('Do not sending reject to local follower.') |
12 | return | 11 | return |
@@ -31,7 +30,7 @@ export { | |||
31 | 30 | ||
32 | // --------------------------------------------------------------------------- | 31 | // --------------------------------------------------------------------------- |
33 | 32 | ||
34 | function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject { | 33 | function buildRejectActivity (url: string, byActor: MActor, followActivityData: ActivityFollow): ActivityReject { |
35 | return { | 34 | return { |
36 | type: 'Reject', | 35 | type: 'Reject', |
37 | id: url, | 36 | id: url, |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 8fcbbac5c..e9ab5b3c5 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -2,13 +2,12 @@ import { Transaction } from 'sequelize' | |||
2 | import { | 2 | import { |
3 | ActivityAnnounce, | 3 | ActivityAnnounce, |
4 | ActivityAudience, | 4 | ActivityAudience, |
5 | ActivityCreate, ActivityDislike, | 5 | ActivityCreate, |
6 | ActivityDislike, | ||
6 | ActivityFollow, | 7 | ActivityFollow, |
7 | ActivityLike, | 8 | ActivityLike, |
8 | ActivityUndo | 9 | ActivityUndo |
9 | } from '../../../../shared/models/activitypub' | 10 | } from '../../../../shared/models/activitypub' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | ||
11 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
12 | import { VideoModel } from '../../../models/video/video' | 11 | import { VideoModel } from '../../../models/video/video' |
13 | import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' | 12 | import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' |
14 | import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' | 13 | import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' |
@@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience' | |||
16 | import { buildCreateActivity } from './send-create' | 15 | import { buildCreateActivity } from './send-create' |
17 | import { buildFollowActivity } from './send-follow' | 16 | import { buildFollowActivity } from './send-follow' |
18 | import { buildLikeActivity } from './send-like' | 17 | import { buildLikeActivity } from './send-like' |
19 | import { VideoShareModel } from '../../../models/video/video-share' | ||
20 | import { buildAnnounceWithVideoAudience } from './send-announce' | 18 | import { buildAnnounceWithVideoAudience } from './send-announce' |
21 | import { logger } from '../../../helpers/logger' | 19 | import { logger } from '../../../helpers/logger' |
22 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
23 | import { buildDislikeActivity } from './send-dislike' | 20 | import { buildDislikeActivity } from './send-dislike' |
24 | 21 | import { | |
25 | async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { | 22 | MActor, MActorAudience, |
23 | MActorFollowActors, | ||
24 | MActorLight, | ||
25 | MVideo, | ||
26 | MVideoAccountLight, | ||
27 | MVideoRedundancyVideo, | ||
28 | MVideoShare | ||
29 | } from '../../../typings/models' | ||
30 | |||
31 | async function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { | ||
26 | const me = actorFollow.ActorFollower | 32 | const me = actorFollow.ActorFollower |
27 | const following = actorFollow.ActorFollowing | 33 | const following = actorFollow.ActorFollowing |
28 | 34 | ||
@@ -40,7 +46,7 @@ async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { | |||
40 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) | 46 | t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) |
41 | } | 47 | } |
42 | 48 | ||
43 | async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { | 49 | async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { |
44 | logger.info('Creating job to undo announce %s.', videoShare.url) | 50 | logger.info('Creating job to undo announce %s.', videoShare.url) |
45 | 51 | ||
46 | const undoUrl = getUndoActivityPubUrl(videoShare.url) | 52 | const undoUrl = getUndoActivityPubUrl(videoShare.url) |
@@ -52,7 +58,7 @@ async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareMode | |||
52 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) | 58 | return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) |
53 | } | 59 | } |
54 | 60 | ||
55 | async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 61 | async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
56 | logger.info('Creating job to undo a like of video %s.', video.url) | 62 | logger.info('Creating job to undo a like of video %s.', video.url) |
57 | 63 | ||
58 | const likeUrl = getVideoLikeActivityPubUrl(byActor, video) | 64 | const likeUrl = getVideoLikeActivityPubUrl(byActor, video) |
@@ -61,7 +67,7 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact | |||
61 | return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) | 67 | return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) |
62 | } | 68 | } |
63 | 69 | ||
64 | async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { | 70 | async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { |
65 | logger.info('Creating job to undo a dislike of video %s.', video.url) | 71 | logger.info('Creating job to undo a dislike of video %s.', video.url) |
66 | 72 | ||
67 | const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) | 73 | const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) |
@@ -70,7 +76,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans | |||
70 | return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) | 76 | return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) |
71 | } | 77 | } |
72 | 78 | ||
73 | async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) { | 79 | async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { |
74 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) | 80 | logger.info('Creating job to undo cache file %s.', redundancyModel.url) |
75 | 81 | ||
76 | const videoId = redundancyModel.getVideo().id | 82 | const videoId = redundancyModel.getVideo().id |
@@ -94,7 +100,7 @@ export { | |||
94 | 100 | ||
95 | function undoActivityData ( | 101 | function undoActivityData ( |
96 | url: string, | 102 | url: string, |
97 | byActor: ActorModel, | 103 | byActor: MActorAudience, |
98 | object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, | 104 | object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, |
99 | audience?: ActivityAudience | 105 | audience?: ActivityAudience |
100 | ): ActivityUndo { | 106 | ): ActivityUndo { |
@@ -112,8 +118,8 @@ function undoActivityData ( | |||
112 | } | 118 | } |
113 | 119 | ||
114 | async function sendUndoVideoRelatedActivity (options: { | 120 | async function sendUndoVideoRelatedActivity (options: { |
115 | byActor: ActorModel, | 121 | byActor: MActor, |
116 | video: VideoModel, | 122 | video: MVideoAccountLight, |
117 | url: string, | 123 | url: string, |
118 | activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, | 124 | activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, |
119 | transaction: Transaction | 125 | transaction: Transaction |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 5bf092894..3a5cc1853 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -2,21 +2,29 @@ import { Transaction } from 'sequelize' | |||
2 | import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' |
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { AccountModel } from '../../../models/account/account' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
6 | import { VideoModel } from '../../../models/video/video' | 5 | import { VideoModel } from '../../../models/video/video' |
7 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
8 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
9 | import { getUpdateActivityPubUrl } from '../url' | 7 | import { getUpdateActivityPubUrl } from '../url' |
10 | import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' | 8 | import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' |
11 | import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' | 9 | import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' |
12 | import { logger } from '../../../helpers/logger' | 10 | import { logger } from '../../../helpers/logger' |
13 | import { VideoCaptionModel } from '../../../models/video/video-caption' | 11 | import { VideoCaptionModel } from '../../../models/video/video-caption' |
14 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | ||
15 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
16 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' | 12 | import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' |
17 | import { getServerActor } from '../../../helpers/utils' | 13 | import { getServerActor } from '../../../helpers/utils' |
14 | import { | ||
15 | MAccountActor, | ||
16 | MActor, | ||
17 | MActorLight, | ||
18 | MChannelActor, | ||
19 | MVideoAP, | ||
20 | MVideoAPWithoutCaption, | ||
21 | MVideoPlaylistFull, | ||
22 | MVideoRedundancyVideo | ||
23 | } from '../../../typings/models' | ||
24 | |||
25 | async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { | ||
26 | const video = videoArg as MVideoAP | ||
18 | 27 | ||
19 | async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByActor?: ActorModel) { | ||
20 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 28 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
21 | 29 | ||
22 | logger.info('Creating job to update video %s.', video.url) | 30 | logger.info('Creating job to update video %s.', video.url) |
@@ -41,7 +49,7 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction, overrodeByAct | |||
41 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) | 49 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) |
42 | } | 50 | } |
43 | 51 | ||
44 | async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) { | 52 | async function sendUpdateActor (accountOrChannel: MAccountActor | MChannelActor, t: Transaction) { |
45 | const byActor = accountOrChannel.Actor | 53 | const byActor = accountOrChannel.Actor |
46 | 54 | ||
47 | logger.info('Creating job to update actor %s.', byActor.url) | 55 | logger.info('Creating job to update actor %s.', byActor.url) |
@@ -51,7 +59,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod | |||
51 | const audience = getAudience(byActor) | 59 | const audience = getAudience(byActor) |
52 | const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) | 60 | const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) |
53 | 61 | ||
54 | let actorsInvolved: ActorModel[] | 62 | let actorsInvolved: MActor[] |
55 | if (accountOrChannel instanceof AccountModel) { | 63 | if (accountOrChannel instanceof AccountModel) { |
56 | // Actors that shared my videos are involved too | 64 | // Actors that shared my videos are involved too |
57 | actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) | 65 | actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) |
@@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod | |||
65 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) | 73 | return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) |
66 | } | 74 | } |
67 | 75 | ||
68 | async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) { | 76 | async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) { |
69 | logger.info('Creating job to update cache file %s.', redundancyModel.url) | 77 | logger.info('Creating job to update cache file %s.', redundancyModel.url) |
70 | 78 | ||
71 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id) | 79 | const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(redundancyModel.getVideo().id) |
@@ -80,7 +88,7 @@ async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoR | |||
80 | return sendVideoRelatedActivity(activityBuilder, { byActor, video }) | 88 | return sendVideoRelatedActivity(activityBuilder, { byActor, video }) |
81 | } | 89 | } |
82 | 90 | ||
83 | async function sendUpdateVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { | 91 | async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) { |
84 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined | 92 | if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined |
85 | 93 | ||
86 | const byActor = videoPlaylist.OwnerAccount.Actor | 94 | const byActor = videoPlaylist.OwnerAccount.Actor |
@@ -113,7 +121,7 @@ export { | |||
113 | 121 | ||
114 | // --------------------------------------------------------------------------- | 122 | // --------------------------------------------------------------------------- |
115 | 123 | ||
116 | function buildUpdateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate { | 124 | function buildUpdateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityUpdate { |
117 | if (!audience) audience = getAudience(byActor) | 125 | if (!audience) audience = getAudience(byActor) |
118 | 126 | ||
119 | return audiencify( | 127 | return audiencify( |
@@ -121,8 +129,7 @@ function buildUpdateActivity (url: string, byActor: ActorModel, object: any, aud | |||
121 | type: 'Update' as 'Update', | 129 | type: 'Update' as 'Update', |
122 | id: url, | 130 | id: url, |
123 | actor: byActor.url, | 131 | actor: byActor.url, |
124 | object: audiencify(object, audience | 132 | object: audiencify(object, audience) |
125 | ) | ||
126 | }, | 133 | }, |
127 | audience | 134 | audience |
128 | ) | 135 | ) |
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index 8ad126be0..8809417f9 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | ||
5 | import { getVideoLikeActivityPubUrl } from '../url' | 4 | import { getVideoLikeActivityPubUrl } from '../url' |
6 | import { sendVideoRelatedActivity } from './utils' | 5 | import { sendVideoRelatedActivity } from './utils' |
7 | import { audiencify, getAudience } from '../audience' | 6 | import { audiencify, getAudience } from '../audience' |
8 | import { logger } from '../../../helpers/logger' | 7 | import { logger } from '../../../helpers/logger' |
8 | import { MActorAudience, MVideoAccountLight, MVideoUrl } from '@server/typings/models' | ||
9 | 9 | ||
10 | async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) { | 10 | async function sendView (byActor: ActorModel, video: MVideoAccountLight, t: Transaction) { |
11 | logger.info('Creating job to send view of %s.', video.url) | 11 | logger.info('Creating job to send view of %s.', video.url) |
12 | 12 | ||
13 | const activityBuilder = (audience: ActivityAudience) => { | 13 | const activityBuilder = (audience: ActivityAudience) => { |
@@ -19,7 +19,7 @@ async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) | |||
19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) | 19 | return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) |
20 | } | 20 | } |
21 | 21 | ||
22 | function buildViewActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityView { | 22 | function buildViewActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityView { |
23 | if (!audience) audience = getAudience(byActor) | 23 | if (!audience) audience = getAudience(byActor) |
24 | 24 | ||
25 | return audiencify( | 25 | return audiencify( |
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts index 4f69afb00..8129ab32a 100644 --- a/server/lib/activitypub/send/utils.ts +++ b/server/lib/activitypub/send/utils.ts | |||
@@ -4,15 +4,14 @@ import { logger } from '../../../helpers/logger' | |||
4 | import { ActorModel } from '../../../models/activitypub/actor' | 4 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
6 | import { JobQueue } from '../../job-queue' | 6 | import { JobQueue } from '../../job-queue' |
7 | import { VideoModel } from '../../../models/video/video' | ||
8 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' | 7 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' |
9 | import { getServerActor } from '../../../helpers/utils' | 8 | import { getServerActor } from '../../../helpers/utils' |
10 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | 9 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' |
11 | import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models' | 10 | import { MActorFollowerException, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models' |
12 | 11 | ||
13 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 12 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
14 | byActor: ActorModelOnly, | 13 | byActor: MActorLight, |
15 | video: VideoModel, | 14 | video: MVideoAccountLight, |
16 | transaction?: Transaction | 15 | transaction?: Transaction |
17 | }) { | 16 | }) { |
18 | const { byActor, video, transaction } = options | 17 | const { byActor, video, transaction } = options |
@@ -41,8 +40,8 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud | |||
41 | async function forwardVideoRelatedActivity ( | 40 | async function forwardVideoRelatedActivity ( |
42 | activity: Activity, | 41 | activity: Activity, |
43 | t: Transaction, | 42 | t: Transaction, |
44 | followersException: ActorFollowerException[] = [], | 43 | followersException: MActorFollowerException[] = [], |
45 | video: VideoModel | 44 | video: MVideo |
46 | ) { | 45 | ) { |
47 | // Mastodon does not add our announces in audience, so we forward to them manually | 46 | // Mastodon does not add our announces in audience, so we forward to them manually |
48 | const additionalActors = await getActorsInvolvedInVideo(video, t) | 47 | const additionalActors = await getActorsInvolvedInVideo(video, t) |
@@ -54,7 +53,7 @@ async function forwardVideoRelatedActivity ( | |||
54 | async function forwardActivity ( | 53 | async function forwardActivity ( |
55 | activity: Activity, | 54 | activity: Activity, |
56 | t: Transaction, | 55 | t: Transaction, |
57 | followersException: ActorFollowerException[] = [], | 56 | followersException: MActorFollowerException[] = [], |
58 | additionalFollowerUrls: string[] = [] | 57 | additionalFollowerUrls: string[] = [] |
59 | ) { | 58 | ) { |
60 | logger.info('Forwarding activity %s.', activity.id) | 59 | logger.info('Forwarding activity %s.', activity.id) |
@@ -88,10 +87,10 @@ async function forwardActivity ( | |||
88 | 87 | ||
89 | async function broadcastToFollowers ( | 88 | async function broadcastToFollowers ( |
90 | data: any, | 89 | data: any, |
91 | byActor: ActorModelId, | 90 | byActor: MActorId, |
92 | toFollowersOf: ActorModelId[], | 91 | toFollowersOf: MActorId[], |
93 | t: Transaction, | 92 | t: Transaction, |
94 | actorsException: ActorFollowerException[] = [] | 93 | actorsException: MActorFollowerException[] = [] |
95 | ) { | 94 | ) { |
96 | const uris = await computeFollowerUris(toFollowersOf, actorsException, t) | 95 | const uris = await computeFollowerUris(toFollowersOf, actorsException, t) |
97 | 96 | ||
@@ -100,16 +99,16 @@ async function broadcastToFollowers ( | |||
100 | 99 | ||
101 | async function broadcastToActors ( | 100 | async function broadcastToActors ( |
102 | data: any, | 101 | data: any, |
103 | byActor: ActorModelId, | 102 | byActor: MActorId, |
104 | toActors: ActorModelOnly[], | 103 | toActors: MActor[], |
105 | t?: Transaction, | 104 | t?: Transaction, |
106 | actorsException: ActorFollowerException[] = [] | 105 | actorsException: MActorFollowerException[] = [] |
107 | ) { | 106 | ) { |
108 | const uris = await computeUris(toActors, actorsException) | 107 | const uris = await computeUris(toActors, actorsException) |
109 | return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor)) | 108 | return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor)) |
110 | } | 109 | } |
111 | 110 | ||
112 | function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { | 111 | function broadcastTo (uris: string[], data: any, byActor: MActorId) { |
113 | if (uris.length === 0) return undefined | 112 | if (uris.length === 0) return undefined |
114 | 113 | ||
115 | logger.debug('Creating broadcast job.', { uris }) | 114 | logger.debug('Creating broadcast job.', { uris }) |
@@ -123,7 +122,7 @@ function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { | |||
123 | return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) | 122 | return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) |
124 | } | 123 | } |
125 | 124 | ||
126 | function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) { | 125 | function unicastTo (data: any, byActor: MActorId, toActorUrl: string) { |
127 | logger.debug('Creating unicast job.', { uri: toActorUrl }) | 126 | logger.debug('Creating unicast job.', { uri: toActorUrl }) |
128 | 127 | ||
129 | const payload = { | 128 | const payload = { |
@@ -148,7 +147,7 @@ export { | |||
148 | 147 | ||
149 | // --------------------------------------------------------------------------- | 148 | // --------------------------------------------------------------------------- |
150 | 149 | ||
151 | async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) { | 150 | async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorFollowerException[], t: Transaction) { |
152 | const toActorFollowerIds = toFollowersOf.map(a => a.id) | 151 | const toActorFollowerIds = toFollowersOf.map(a => a.id) |
153 | 152 | ||
154 | const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) | 153 | const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) |
@@ -157,7 +156,7 @@ async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsExcepti | |||
157 | return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) | 156 | return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) |
158 | } | 157 | } |
159 | 158 | ||
160 | async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) { | 159 | async function computeUris (toActors: MActor[], actorsException: MActorFollowerException[] = []) { |
161 | const serverActor = await getServerActor() | 160 | const serverActor = await getServerActor() |
162 | const targetUrls = toActors | 161 | const targetUrls = toActors |
163 | .filter(a => a.id !== serverActor.id) // Don't send to ourselves | 162 | .filter(a => a.id !== serverActor.id) // Don't send to ourselves |
@@ -170,7 +169,7 @@ async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFo | |||
170 | .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) | 169 | .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1) |
171 | } | 170 | } |
172 | 171 | ||
173 | async function buildSharedInboxesException (actorsException: ActorFollowerException[]) { | 172 | async function buildSharedInboxesException (actorsException: MActorFollowerException[]) { |
174 | const serverActor = await getServerActor() | 173 | const serverActor = await getServerActor() |
175 | 174 | ||
176 | return actorsException | 175 | return actorsException |
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 7f38402b6..fdca9bed7 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts | |||
@@ -1,19 +1,18 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { VideoPrivacy } from '../../../shared/models/videos' | 2 | import { VideoPrivacy } from '../../../shared/models/videos' |
3 | import { getServerActor } from '../../helpers/utils' | 3 | import { getServerActor } from '../../helpers/utils' |
4 | import { VideoModel } from '../../models/video/video' | ||
5 | import { VideoShareModel } from '../../models/video/video-share' | 4 | import { VideoShareModel } from '../../models/video/video-share' |
6 | import { sendUndoAnnounce, sendVideoAnnounce } from './send' | 5 | import { sendUndoAnnounce, sendVideoAnnounce } from './send' |
7 | import { getVideoAnnounceActivityPubUrl } from './url' | 6 | import { getVideoAnnounceActivityPubUrl } from './url' |
8 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
9 | import * as Bluebird from 'bluebird' | 7 | import * as Bluebird from 'bluebird' |
10 | import { doRequest } from '../../helpers/requests' | 8 | import { doRequest } from '../../helpers/requests' |
11 | import { getOrCreateActorAndServerAndModel } from './actor' | 9 | import { getOrCreateActorAndServerAndModel } from './actor' |
12 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
13 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 11 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
14 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 12 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
13 | import { MChannelActor, MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video' | ||
15 | 14 | ||
16 | async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { | 15 | async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) { |
17 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 16 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
18 | 17 | ||
19 | return Promise.all([ | 18 | return Promise.all([ |
@@ -22,7 +21,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) | |||
22 | ]) | 21 | ]) |
23 | } | 22 | } |
24 | 23 | ||
25 | async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { | 24 | async function changeVideoChannelShare ( |
25 | video: MVideoAccountLight, | ||
26 | oldVideoChannel: MChannelActorLight, | ||
27 | t: Transaction | ||
28 | ) { | ||
26 | logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name) | 29 | logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name) |
27 | 30 | ||
28 | await undoShareByVideoChannel(video, oldVideoChannel, t) | 31 | await undoShareByVideoChannel(video, oldVideoChannel, t) |
@@ -30,7 +33,7 @@ async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: Vide | |||
30 | await shareByVideoChannel(video, t) | 33 | await shareByVideoChannel(video, t) |
31 | } | 34 | } |
32 | 35 | ||
33 | async function addVideoShares (shareUrls: string[], instance: VideoModel) { | 36 | async function addVideoShares (shareUrls: string[], video: MVideoId) { |
34 | await Bluebird.map(shareUrls, async shareUrl => { | 37 | await Bluebird.map(shareUrls, async shareUrl => { |
35 | try { | 38 | try { |
36 | // Fetch url | 39 | // Fetch url |
@@ -50,7 +53,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) { | |||
50 | 53 | ||
51 | const entry = { | 54 | const entry = { |
52 | actorId: actor.id, | 55 | actorId: actor.id, |
53 | videoId: instance.id, | 56 | videoId: video.id, |
54 | url: shareUrl | 57 | url: shareUrl |
55 | } | 58 | } |
56 | 59 | ||
@@ -69,7 +72,7 @@ export { | |||
69 | 72 | ||
70 | // --------------------------------------------------------------------------- | 73 | // --------------------------------------------------------------------------- |
71 | 74 | ||
72 | async function shareByServer (video: VideoModel, t: Transaction) { | 75 | async function shareByServer (video: MVideo, t: Transaction) { |
73 | const serverActor = await getServerActor() | 76 | const serverActor = await getServerActor() |
74 | 77 | ||
75 | const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) | 78 | const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video) |
@@ -88,7 +91,7 @@ async function shareByServer (video: VideoModel, t: Transaction) { | |||
88 | return sendVideoAnnounce(serverActor, serverShare, video, t) | 91 | return sendVideoAnnounce(serverActor, serverShare, video, t) |
89 | } | 92 | } |
90 | 93 | ||
91 | async function shareByVideoChannel (video: VideoModel, t: Transaction) { | 94 | async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) { |
92 | const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) | 95 | const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video) |
93 | const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ | 96 | const [ videoChannelShare ] = await VideoShareModel.findOrCreate({ |
94 | defaults: { | 97 | defaults: { |
@@ -105,7 +108,7 @@ async function shareByVideoChannel (video: VideoModel, t: Transaction) { | |||
105 | return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) | 108 | return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) |
106 | } | 109 | } |
107 | 110 | ||
108 | async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { | 111 | async function undoShareByVideoChannel (video: MVideo, oldVideoChannel: MChannelActorLight, t: Transaction) { |
109 | // Load old share | 112 | // Load old share |
110 | const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) | 113 | const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) |
111 | if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id) | 114 | if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id) |
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index dfcb3c668..6290af34b 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts | |||
@@ -1,36 +1,42 @@ | |||
1 | import { WEBSERVER } from '../../initializers/constants' | 1 | import { WEBSERVER } from '../../initializers/constants' |
2 | import { VideoModel } from '../../models/video/video' | 2 | import { |
3 | import { VideoAbuseModel } from '../../models/video/video-abuse' | 3 | MActor, |
4 | import { VideoCommentModel } from '../../models/video/video-comment' | 4 | MActorFollowActors, |
5 | import { VideoFileModel } from '../../models/video/video-file' | 5 | MActorId, |
6 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | 6 | MActorUrl, |
7 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 7 | MCommentId, |
8 | import { ActorModelOnly, ActorModelUrl } from '../../typings/models' | 8 | MVideoAbuseId, |
9 | import { ActorFollowModelLight } from '../../typings/models/actor-follow' | 9 | MVideoId, |
10 | 10 | MVideoUrl, | |
11 | function getVideoActivityPubUrl (video: VideoModel) { | 11 | MVideoUUID |
12 | } from '../../typings/models' | ||
13 | import { MVideoPlaylist, MVideoPlaylistUUID } from '../../typings/models/video/video-playlist' | ||
14 | import { MVideoFileVideoUUID } from '../../typings/models/video/video-file' | ||
15 | import { MStreamingPlaylist } from '../../typings/models/video/video-streaming-playlist' | ||
16 | |||
17 | function getVideoActivityPubUrl (video: MVideoUUID) { | ||
12 | return WEBSERVER.URL + '/videos/watch/' + video.uuid | 18 | return WEBSERVER.URL + '/videos/watch/' + video.uuid |
13 | } | 19 | } |
14 | 20 | ||
15 | function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) { | 21 | function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) { |
16 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid | 22 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid |
17 | } | 23 | } |
18 | 24 | ||
19 | function getVideoPlaylistElementActivityPubUrl (videoPlaylist: VideoPlaylistModel, video: VideoModel) { | 25 | function getVideoPlaylistElementActivityPubUrl (videoPlaylist: MVideoPlaylistUUID, video: MVideoUUID) { |
20 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid | 26 | return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid + '/' + video.uuid |
21 | } | 27 | } |
22 | 28 | ||
23 | function getVideoCacheFileActivityPubUrl (videoFile: VideoFileModel) { | 29 | function getVideoCacheFileActivityPubUrl (videoFile: MVideoFileVideoUUID) { |
24 | const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : '' | 30 | const suffixFPS = videoFile.fps && videoFile.fps !== -1 ? '-' + videoFile.fps : '' |
25 | 31 | ||
26 | return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}` | 32 | return `${WEBSERVER.URL}/redundancy/videos/${videoFile.Video.uuid}/${videoFile.resolution}${suffixFPS}` |
27 | } | 33 | } |
28 | 34 | ||
29 | function getVideoCacheStreamingPlaylistActivityPubUrl (video: VideoModel, playlist: VideoStreamingPlaylistModel) { | 35 | function getVideoCacheStreamingPlaylistActivityPubUrl (video: MVideoUUID, playlist: MStreamingPlaylist) { |
30 | return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}` | 36 | return `${WEBSERVER.URL}/redundancy/streaming-playlists/${playlist.getStringType()}/${video.uuid}` |
31 | } | 37 | } |
32 | 38 | ||
33 | function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) { | 39 | function getVideoCommentActivityPubUrl (video: MVideoUUID, videoComment: MCommentId) { |
34 | return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id | 40 | return WEBSERVER.URL + '/videos/watch/' + video.uuid + '/comments/' + videoComment.id |
35 | } | 41 | } |
36 | 42 | ||
@@ -42,54 +48,54 @@ function getAccountActivityPubUrl (accountName: string) { | |||
42 | return WEBSERVER.URL + '/accounts/' + accountName | 48 | return WEBSERVER.URL + '/accounts/' + accountName |
43 | } | 49 | } |
44 | 50 | ||
45 | function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { | 51 | function getVideoAbuseActivityPubUrl (videoAbuse: MVideoAbuseId) { |
46 | return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id | 52 | return WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id |
47 | } | 53 | } |
48 | 54 | ||
49 | function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) { | 55 | function getVideoViewActivityPubUrl (byActor: MActorUrl, video: MVideoId) { |
50 | return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString() | 56 | return byActor.url + '/views/videos/' + video.id + '/' + new Date().toISOString() |
51 | } | 57 | } |
52 | 58 | ||
53 | function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { | 59 | function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) { |
54 | return byActor.url + '/likes/' + video.id | 60 | return byActor.url + '/likes/' + video.id |
55 | } | 61 | } |
56 | 62 | ||
57 | function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { | 63 | function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) { |
58 | return byActor.url + '/dislikes/' + video.id | 64 | return byActor.url + '/dislikes/' + video.id |
59 | } | 65 | } |
60 | 66 | ||
61 | function getVideoSharesActivityPubUrl (video: VideoModel) { | 67 | function getVideoSharesActivityPubUrl (video: MVideoUrl) { |
62 | return video.url + '/announces' | 68 | return video.url + '/announces' |
63 | } | 69 | } |
64 | 70 | ||
65 | function getVideoCommentsActivityPubUrl (video: VideoModel) { | 71 | function getVideoCommentsActivityPubUrl (video: MVideoUrl) { |
66 | return video.url + '/comments' | 72 | return video.url + '/comments' |
67 | } | 73 | } |
68 | 74 | ||
69 | function getVideoLikesActivityPubUrl (video: VideoModel) { | 75 | function getVideoLikesActivityPubUrl (video: MVideoUrl) { |
70 | return video.url + '/likes' | 76 | return video.url + '/likes' |
71 | } | 77 | } |
72 | 78 | ||
73 | function getVideoDislikesActivityPubUrl (video: VideoModel) { | 79 | function getVideoDislikesActivityPubUrl (video: MVideoUrl) { |
74 | return video.url + '/dislikes' | 80 | return video.url + '/dislikes' |
75 | } | 81 | } |
76 | 82 | ||
77 | function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { | 83 | function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) { |
78 | return follower.url + '/follows/' + following.id | 84 | return follower.url + '/follows/' + following.id |
79 | } | 85 | } |
80 | 86 | ||
81 | function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) { | 87 | function getActorFollowAcceptActivityPubUrl (actorFollow: MActorFollowActors) { |
82 | const follower = actorFollow.ActorFollower | 88 | const follower = actorFollow.ActorFollower |
83 | const me = actorFollow.ActorFollowing | 89 | const me = actorFollow.ActorFollowing |
84 | 90 | ||
85 | return follower.url + '/accepts/follows/' + me.id | 91 | return follower.url + '/accepts/follows/' + me.id |
86 | } | 92 | } |
87 | 93 | ||
88 | function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { | 94 | function getActorFollowRejectActivityPubUrl (follower: MActorUrl, following: MActorId) { |
89 | return follower.url + '/rejects/follows/' + following.id | 95 | return follower.url + '/rejects/follows/' + following.id |
90 | } | 96 | } |
91 | 97 | ||
92 | function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) { | 98 | function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) { |
93 | return video.url + '/announces/' + byActor.id | 99 | return video.url + '/announces/' + byActor.id |
94 | } | 100 | } |
95 | 101 | ||
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts index 8d2c1ade3..375ac0aad 100644 --- a/server/lib/activitypub/video-comments.ts +++ b/server/lib/activitypub/video-comments.ts | |||
@@ -2,20 +2,20 @@ import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validat | |||
2 | import { logger } from '../../helpers/logger' | 2 | import { logger } from '../../helpers/logger' |
3 | import { doRequest } from '../../helpers/requests' | 3 | import { doRequest } from '../../helpers/requests' |
4 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 4 | import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
5 | import { VideoModel } from '../../models/video/video' | ||
6 | import { VideoCommentModel } from '../../models/video/video-comment' | 5 | import { VideoCommentModel } from '../../models/video/video-comment' |
7 | import { getOrCreateActorAndServerAndModel } from './actor' | 6 | import { getOrCreateActorAndServerAndModel } from './actor' |
8 | import { getOrCreateVideoAndAccountAndChannel } from './videos' | 7 | import { getOrCreateVideoAndAccountAndChannel } from './videos' |
9 | import * as Bluebird from 'bluebird' | 8 | import * as Bluebird from 'bluebird' |
10 | import { checkUrlsSameHost } from '../../helpers/activitypub' | 9 | import { checkUrlsSameHost } from '../../helpers/activitypub' |
10 | import { MCommentOwner, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../typings/models/video' | ||
11 | 11 | ||
12 | type ResolveThreadParams = { | 12 | type ResolveThreadParams = { |
13 | url: string, | 13 | url: string, |
14 | comments?: VideoCommentModel[], | 14 | comments?: MCommentOwner[], |
15 | isVideo?: boolean, | 15 | isVideo?: boolean, |
16 | commentCreated?: boolean | 16 | commentCreated?: boolean |
17 | } | 17 | } |
18 | type ResolveThreadResult = Promise<{ video: VideoModel, comment: VideoCommentModel, commentCreated: boolean }> | 18 | type ResolveThreadResult = Promise<{ video: MVideoAccountAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }> |
19 | 19 | ||
20 | async function addVideoComments (commentUrls: string[]) { | 20 | async function addVideoComments (commentUrls: string[]) { |
21 | return Bluebird.map(commentUrls, commentUrl => { | 21 | return Bluebird.map(commentUrls, commentUrl => { |
@@ -85,9 +85,9 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { | |||
85 | const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } | 85 | const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false } |
86 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam }) | 86 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: url, syncParam }) |
87 | 87 | ||
88 | let resultComment: VideoCommentModel | 88 | let resultComment: MCommentOwnerVideo |
89 | if (comments.length !== 0) { | 89 | if (comments.length !== 0) { |
90 | const firstReply = comments[ comments.length - 1 ] | 90 | const firstReply = comments[ comments.length - 1 ] as MCommentOwnerVideo |
91 | firstReply.inReplyToCommentId = null | 91 | firstReply.inReplyToCommentId = null |
92 | firstReply.originCommentId = null | 92 | firstReply.originCommentId = null |
93 | firstReply.videoId = video.id | 93 | firstReply.videoId = video.id |
@@ -97,7 +97,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { | |||
97 | comments[comments.length - 1] = await firstReply.save() | 97 | comments[comments.length - 1] = await firstReply.save() |
98 | 98 | ||
99 | for (let i = comments.length - 2; i >= 0; i--) { | 99 | for (let i = comments.length - 2; i >= 0; i--) { |
100 | const comment = comments[ i ] | 100 | const comment = comments[ i ] as MCommentOwnerVideo |
101 | comment.originCommentId = firstReply.id | 101 | comment.originCommentId = firstReply.id |
102 | comment.inReplyToCommentId = comments[ i + 1 ].id | 102 | comment.inReplyToCommentId = comments[ i + 1 ].id |
103 | comment.videoId = video.id | 103 | comment.videoId = video.id |
@@ -107,7 +107,7 @@ async function tryResolveThreadFromVideo (params: ResolveThreadParams) { | |||
107 | comments[i] = await comment.save() | 107 | comments[i] = await comment.save() |
108 | } | 108 | } |
109 | 109 | ||
110 | resultComment = comments[0] | 110 | resultComment = comments[0] as MCommentOwnerVideo |
111 | } | 111 | } |
112 | 112 | ||
113 | return { video, comment: resultComment, commentCreated } | 113 | return { video, comment: resultComment, commentCreated } |
@@ -151,7 +151,7 @@ async function resolveParentComment (params: ResolveThreadParams) { | |||
151 | originCommentId: null, | 151 | originCommentId: null, |
152 | createdAt: new Date(body.published), | 152 | createdAt: new Date(body.published), |
153 | updatedAt: new Date(body.updated) | 153 | updatedAt: new Date(body.updated) |
154 | }) | 154 | }) as MCommentOwner |
155 | comment.Account = actor.Account | 155 | comment.Account = actor.Account |
156 | 156 | ||
157 | return resolveThread({ | 157 | return resolveThread({ |
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts index cda5b2981..6bd46bb58 100644 --- a/server/lib/activitypub/video-rates.ts +++ b/server/lib/activitypub/video-rates.ts | |||
@@ -1,6 +1,4 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { AccountModel } from '../../models/account/account' | ||
3 | import { VideoModel } from '../../models/video/video' | ||
4 | import { sendLike, sendUndoDislike, sendUndoLike } from './send' | 2 | import { sendLike, sendUndoDislike, sendUndoLike } from './send' |
5 | import { VideoRateType } from '../../../shared/models/videos' | 3 | import { VideoRateType } from '../../../shared/models/videos' |
6 | import * as Bluebird from 'bluebird' | 4 | import * as Bluebird from 'bluebird' |
@@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger' | |||
10 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' | 8 | import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' |
11 | import { doRequest } from '../../helpers/requests' | 9 | import { doRequest } from '../../helpers/requests' |
12 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 10 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
13 | import { ActorModel } from '../../models/activitypub/actor' | ||
14 | import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' | 11 | import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' |
15 | import { sendDislike } from './send/send-dislike' | 12 | import { sendDislike } from './send/send-dislike' |
13 | import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models' | ||
16 | 14 | ||
17 | async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRateType) { | 15 | async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) { |
18 | let rateCounts = 0 | 16 | let rateCounts = 0 |
19 | 17 | ||
20 | await Bluebird.map(ratesUrl, async rateUrl => { | 18 | await Bluebird.map(ratesUrl, async rateUrl => { |
@@ -64,11 +62,13 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa | |||
64 | return | 62 | return |
65 | } | 63 | } |
66 | 64 | ||
67 | async function sendVideoRateChange (account: AccountModel, | 65 | async function sendVideoRateChange ( |
68 | video: VideoModel, | 66 | account: MAccountActor, |
69 | likes: number, | 67 | video: MVideoAccountLight, |
70 | dislikes: number, | 68 | likes: number, |
71 | t: Transaction) { | 69 | dislikes: number, |
70 | t: Transaction | ||
71 | ) { | ||
72 | const actor = account.Actor | 72 | const actor = account.Actor |
73 | 73 | ||
74 | // Keep the order: first we undo and then we create | 74 | // Keep the order: first we undo and then we create |
@@ -84,8 +84,10 @@ async function sendVideoRateChange (account: AccountModel, | |||
84 | if (dislikes > 0) await sendDislike(actor, video, t) | 84 | if (dislikes > 0) await sendDislike(actor, video, t) |
85 | } | 85 | } |
86 | 86 | ||
87 | function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) { | 87 | function getRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) { |
88 | return rateType === 'like' ? getVideoLikeActivityPubUrl(actor, video) : getVideoDislikeActivityPubUrl(actor, video) | 88 | return rateType === 'like' |
89 | ? getVideoLikeActivityPubUrl(actor, video) | ||
90 | : getVideoDislikeActivityPubUrl(actor, video) | ||
89 | } | 91 | } |
90 | 92 | ||
91 | export { | 93 | export { |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 3a8451a32..5c10f9764 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -24,7 +24,6 @@ import { | |||
24 | REMOTE_SCHEME, | 24 | REMOTE_SCHEME, |
25 | STATIC_PATHS | 25 | STATIC_PATHS |
26 | } from '../../initializers/constants' | 26 | } from '../../initializers/constants' |
27 | import { ActorModel } from '../../models/activitypub/actor' | ||
28 | import { TagModel } from '../../models/video/tag' | 27 | import { TagModel } from '../../models/video/tag' |
29 | import { VideoModel } from '../../models/video/video' | 28 | import { VideoModel } from '../../models/video/video' |
30 | import { VideoFileModel } from '../../models/video/video-file' | 29 | import { VideoFileModel } from '../../models/video/video-file' |
@@ -38,7 +37,6 @@ import { JobQueue } from '../job-queue' | |||
38 | import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' | 37 | import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' |
39 | import { createRates } from './video-rates' | 38 | import { createRates } from './video-rates' |
40 | import { addVideoShares, shareVideoByServerAndChannel } from './share' | 39 | import { addVideoShares, shareVideoByServerAndChannel } from './share' |
41 | import { AccountModel } from '../../models/account/account' | ||
42 | import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' | 40 | import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' |
43 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' | 41 | import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' |
44 | import { Notifier } from '../notifier' | 42 | import { Notifier } from '../notifier' |
@@ -49,15 +47,33 @@ import { VideoShareModel } from '../../models/video/video-share' | |||
49 | import { VideoCommentModel } from '../../models/video/video-comment' | 47 | import { VideoCommentModel } from '../../models/video/video-comment' |
50 | import { sequelizeTypescript } from '../../initializers/database' | 48 | import { sequelizeTypescript } from '../../initializers/database' |
51 | import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' | 49 | import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' |
52 | import { ThumbnailModel } from '../../models/video/thumbnail' | ||
53 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 50 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
54 | import { join } from 'path' | 51 | import { join } from 'path' |
55 | import { FilteredModelAttributes } from '../../typings/sequelize' | 52 | import { FilteredModelAttributes } from '../../typings/sequelize' |
56 | import { autoBlacklistVideoIfNeeded } from '../video-blacklist' | 53 | import { autoBlacklistVideoIfNeeded } from '../video-blacklist' |
57 | import { ActorFollowScoreCache } from '../files-cache' | 54 | import { ActorFollowScoreCache } from '../files-cache' |
58 | import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models' | 55 | import { |
56 | MAccountActor, | ||
57 | MChannelAccountLight, | ||
58 | MChannelDefault, | ||
59 | MChannelId, | ||
60 | MVideo, | ||
61 | MVideoAccountAllFiles, | ||
62 | MVideoAccountLight, | ||
63 | MVideoAP, | ||
64 | MVideoAPWithoutCaption, | ||
65 | MVideoFile, | ||
66 | MVideoFullLight, | ||
67 | MVideoId, | ||
68 | MVideoTag, | ||
69 | MVideoThumbnail, | ||
70 | MVideoWithAllFiles | ||
71 | } from '../../typings/models' | ||
72 | import { MThumbnail } from '../../typings/models/video/thumbnail' | ||
73 | |||
74 | async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
75 | const video = videoArg as MVideoAP | ||
59 | 76 | ||
60 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
61 | if ( | 77 | if ( |
62 | // Check this is not a blacklisted video, or unfederated blacklisted video | 78 | // Check this is not a blacklisted video, or unfederated blacklisted video |
63 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && | 79 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && |
@@ -102,7 +118,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request. | |||
102 | return { response, videoObject: body } | 118 | return { response, videoObject: body } |
103 | } | 119 | } |
104 | 120 | ||
105 | async function fetchRemoteVideoDescription (video: VideoModel) { | 121 | async function fetchRemoteVideoDescription (video: MVideoAccountLight) { |
106 | const host = video.VideoChannel.Account.Actor.Server.host | 122 | const host = video.VideoChannel.Account.Actor.Server.host |
107 | const path = video.getDescriptionAPIPath() | 123 | const path = video.getDescriptionAPIPath() |
108 | const options = { | 124 | const options = { |
@@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) { | |||
114 | return body.description ? body.description : '' | 130 | return body.description ? body.description : '' |
115 | } | 131 | } |
116 | 132 | ||
117 | function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { | 133 | function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) { |
118 | const url = buildRemoteBaseUrl(video, path) | 134 | const url = buildRemoteBaseUrl(video, path) |
119 | 135 | ||
120 | // We need to provide a callback, if no we could have an uncaught exception | 136 | // We need to provide a callback, if no we could have an uncaught exception |
121 | return doRequestAndSaveToFile({ uri: url }, destPath) | 137 | return doRequestAndSaveToFile({ uri: url }, destPath) |
122 | } | 138 | } |
123 | 139 | ||
124 | function buildRemoteBaseUrl (video: VideoModel, path: string) { | 140 | function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) { |
125 | const host = video.VideoChannel.Account.Actor.Server.host | 141 | const host = video.VideoChannel.Account.Actor.Server.host |
126 | 142 | ||
127 | return REMOTE_SCHEME.HTTP + '://' + host + path | 143 | return REMOTE_SCHEME.HTTP + '://' + host + path |
@@ -146,7 +162,7 @@ type SyncParam = { | |||
146 | thumbnail: boolean | 162 | thumbnail: boolean |
147 | refreshVideo?: boolean | 163 | refreshVideo?: boolean |
148 | } | 164 | } |
149 | async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { | 165 | async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { |
150 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) | 166 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) |
151 | 167 | ||
152 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] | 168 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] |
@@ -194,12 +210,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
194 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) | 210 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) |
195 | } | 211 | } |
196 | 212 | ||
213 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
214 | videoObject: { id: string } | string, | ||
215 | syncParam?: SyncParam, | ||
216 | fetchType?: 'all', | ||
217 | allowRefresh?: boolean | ||
218 | }): Promise<{ video: MVideoAccountAllFiles, created: boolean, autoBlacklisted?: boolean }> | ||
219 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
220 | videoObject: { id: string } | string, | ||
221 | syncParam?: SyncParam, | ||
222 | fetchType?: VideoFetchByUrlType, | ||
223 | allowRefresh?: boolean | ||
224 | }): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> | ||
197 | async function getOrCreateVideoAndAccountAndChannel (options: { | 225 | async function getOrCreateVideoAndAccountAndChannel (options: { |
198 | videoObject: { id: string } | string, | 226 | videoObject: { id: string } | string, |
199 | syncParam?: SyncParam, | 227 | syncParam?: SyncParam, |
200 | fetchType?: VideoFetchByUrlType, | 228 | fetchType?: VideoFetchByUrlType, |
201 | allowRefresh?: boolean // true by default | 229 | allowRefresh?: boolean // true by default |
202 | }) { | 230 | }): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> { |
203 | // Default params | 231 | // Default params |
204 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 232 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
205 | const fetchType = options.fetchType || 'all' | 233 | const fetchType = options.fetchType || 'all' |
@@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
227 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) | 255 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) |
228 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | 256 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) |
229 | 257 | ||
230 | const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) | 258 | const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) |
231 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) | 259 | const videoChannel = actor.VideoChannel |
260 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) | ||
232 | 261 | ||
233 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) | 262 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) |
234 | 263 | ||
@@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
236 | } | 265 | } |
237 | 266 | ||
238 | async function updateVideoFromAP (options: { | 267 | async function updateVideoFromAP (options: { |
239 | video: VideoModel, | 268 | video: MVideoAccountAllFiles, |
240 | videoObject: VideoTorrentObject, | 269 | videoObject: VideoTorrentObject, |
241 | account: AccountModelIdActor, | 270 | account: MAccountActor, |
242 | channel: VideoChannelModelIdActor, | 271 | channel: MChannelDefault, |
243 | overrideTo?: string[] | 272 | overrideTo?: string[] |
244 | }) { | 273 | }) { |
245 | const { video, videoObject, account, channel, overrideTo } = options | 274 | const { video, videoObject, account, channel, overrideTo } = options |
246 | 275 | ||
247 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) | 276 | logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel }) |
248 | 277 | ||
249 | let videoFieldsSave: any | 278 | let videoFieldsSave: any |
250 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE | 279 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE |
251 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED | 280 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED |
252 | 281 | ||
253 | try { | 282 | try { |
254 | let thumbnailModel: ThumbnailModel | 283 | let thumbnailModel: MThumbnail |
255 | 284 | ||
256 | try { | 285 | try { |
257 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 286 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
@@ -259,7 +288,7 @@ async function updateVideoFromAP (options: { | |||
259 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) | 288 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) |
260 | } | 289 | } |
261 | 290 | ||
262 | await sequelizeTypescript.transaction(async t => { | 291 | const videoUpdated = await sequelizeTypescript.transaction(async t => { |
263 | const sequelizeOptions = { transaction: t } | 292 | const sequelizeOptions = { transaction: t } |
264 | 293 | ||
265 | videoFieldsSave = video.toJSON() | 294 | videoFieldsSave = video.toJSON() |
@@ -293,21 +322,21 @@ async function updateVideoFromAP (options: { | |||
293 | video.channelId = videoData.channelId | 322 | video.channelId = videoData.channelId |
294 | video.views = videoData.views | 323 | video.views = videoData.views |
295 | 324 | ||
296 | await video.save(sequelizeOptions) | 325 | const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight |
297 | 326 | ||
298 | if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) | 327 | if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) |
299 | 328 | ||
300 | // FIXME: use icon URL instead | 329 | // FIXME: use icon URL instead |
301 | const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) | 330 | const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename)) |
302 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) | 331 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) |
303 | await video.addAndSaveThumbnail(previewModel, t) | 332 | await videoUpdated.addAndSaveThumbnail(previewModel, t) |
304 | 333 | ||
305 | { | 334 | { |
306 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) | 335 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject) |
307 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) | 336 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) |
308 | 337 | ||
309 | // Remove video files that do not exist anymore | 338 | // Remove video files that do not exist anymore |
310 | const destroyTasks = video.VideoFiles | 339 | const destroyTasks = videoUpdated.VideoFiles |
311 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) | 340 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) |
312 | .map(f => f.destroy(sequelizeOptions)) | 341 | .map(f => f.destroy(sequelizeOptions)) |
313 | await Promise.all(destroyTasks) | 342 | await Promise.all(destroyTasks) |
@@ -318,15 +347,15 @@ async function updateVideoFromAP (options: { | |||
318 | .then(([ file ]) => file) | 347 | .then(([ file ]) => file) |
319 | }) | 348 | }) |
320 | 349 | ||
321 | video.VideoFiles = await Promise.all(upsertTasks) | 350 | videoUpdated.VideoFiles = await Promise.all(upsertTasks) |
322 | } | 351 | } |
323 | 352 | ||
324 | { | 353 | { |
325 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) | 354 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles) |
326 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) | 355 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) |
327 | 356 | ||
328 | // Remove video files that do not exist anymore | 357 | // Remove video files that do not exist anymore |
329 | const destroyTasks = video.VideoStreamingPlaylists | 358 | const destroyTasks = videoUpdated.VideoStreamingPlaylists |
330 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) | 359 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) |
331 | .map(f => f.destroy(sequelizeOptions)) | 360 | .map(f => f.destroy(sequelizeOptions)) |
332 | await Promise.all(destroyTasks) | 361 | await Promise.all(destroyTasks) |
@@ -337,38 +366,42 @@ async function updateVideoFromAP (options: { | |||
337 | .then(([ streamingPlaylist ]) => streamingPlaylist) | 366 | .then(([ streamingPlaylist ]) => streamingPlaylist) |
338 | }) | 367 | }) |
339 | 368 | ||
340 | video.VideoStreamingPlaylists = await Promise.all(upsertTasks) | 369 | videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks) |
341 | } | 370 | } |
342 | 371 | ||
343 | { | 372 | { |
344 | // Update Tags | 373 | // Update Tags |
345 | const tags = videoObject.tag.map(tag => tag.name) | 374 | const tags = videoObject.tag.map(tag => tag.name) |
346 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 375 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
347 | await video.$set('Tags', tagInstances, sequelizeOptions) | 376 | await videoUpdated.$set('Tags', tagInstances, sequelizeOptions) |
348 | } | 377 | } |
349 | 378 | ||
350 | { | 379 | { |
351 | // Update captions | 380 | // Update captions |
352 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) | 381 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t) |
353 | 382 | ||
354 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 383 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
355 | return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) | 384 | return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t) |
356 | }) | 385 | }) |
357 | video.VideoCaptions = await Promise.all(videoCaptionsPromises) | 386 | await Promise.all(videoCaptionsPromises) |
358 | } | 387 | } |
388 | |||
389 | return videoUpdated | ||
359 | }) | 390 | }) |
360 | 391 | ||
361 | await autoBlacklistVideoIfNeeded({ | 392 | await autoBlacklistVideoIfNeeded({ |
362 | video, | 393 | video: videoUpdated, |
363 | user: undefined, | 394 | user: undefined, |
364 | isRemote: true, | 395 | isRemote: true, |
365 | isNew: false, | 396 | isNew: false, |
366 | transaction: undefined | 397 | transaction: undefined |
367 | }) | 398 | }) |
368 | 399 | ||
369 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? | 400 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users? |
370 | 401 | ||
371 | logger.info('Remote video with uuid %s updated', videoObject.uuid) | 402 | logger.info('Remote video with uuid %s updated', videoObject.uuid) |
403 | |||
404 | return videoUpdated | ||
372 | } catch (err) { | 405 | } catch (err) { |
373 | if (video !== undefined && videoFieldsSave !== undefined) { | 406 | if (video !== undefined && videoFieldsSave !== undefined) { |
374 | resetSequelizeInstance(video, videoFieldsSave) | 407 | resetSequelizeInstance(video, videoFieldsSave) |
@@ -381,15 +414,15 @@ async function updateVideoFromAP (options: { | |||
381 | } | 414 | } |
382 | 415 | ||
383 | async function refreshVideoIfNeeded (options: { | 416 | async function refreshVideoIfNeeded (options: { |
384 | video: VideoModel, | 417 | video: MVideoThumbnail, |
385 | fetchedType: VideoFetchByUrlType, | 418 | fetchedType: VideoFetchByUrlType, |
386 | syncParam: SyncParam | 419 | syncParam: SyncParam |
387 | }): Promise<VideoModel> { | 420 | }): Promise<MVideoThumbnail> { |
388 | if (!options.video.isOutdated()) return options.video | 421 | if (!options.video.isOutdated()) return options.video |
389 | 422 | ||
390 | // We need more attributes if the argument video was fetched with not enough joints | 423 | // We need more attributes if the argument video was fetched with not enough joints |
391 | const video = options.fetchedType === 'all' | 424 | const video = options.fetchedType === 'all' |
392 | ? options.video | 425 | ? options.video as MVideoAccountAllFiles |
393 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) | 426 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) |
394 | 427 | ||
395 | try { | 428 | try { |
@@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: { | |||
410 | } | 443 | } |
411 | 444 | ||
412 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 445 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
413 | const account = await AccountModel.load(channelActor.VideoChannel.accountId) | ||
414 | 446 | ||
415 | const updateOptions = { | 447 | const updateOptions = { |
416 | video, | 448 | video, |
417 | videoObject, | 449 | videoObject, |
418 | account, | 450 | account: channelActor.VideoChannel.Account, |
419 | channel: channelActor.VideoChannel | 451 | channel: channelActor.VideoChannel |
420 | } | 452 | } |
421 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) | 453 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) |
@@ -467,15 +499,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS | |||
467 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' | 499 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' |
468 | } | 500 | } |
469 | 501 | ||
470 | async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { | 502 | async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { |
471 | logger.debug('Adding remote video %s.', videoObject.id) | 503 | logger.debug('Adding remote video %s.', videoObject.id) |
472 | 504 | ||
473 | const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) | 505 | const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to) |
474 | const video = VideoModel.build(videoData) | 506 | const video = VideoModel.build(videoData) as MVideoThumbnail |
475 | 507 | ||
476 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 508 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
477 | 509 | ||
478 | let thumbnailModel: ThumbnailModel | 510 | let thumbnailModel: MThumbnail |
479 | if (waitThumbnail === true) { | 511 | if (waitThumbnail === true) { |
480 | thumbnailModel = await promiseThumbnail | 512 | thumbnailModel = await promiseThumbnail |
481 | } | 513 | } |
@@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
483 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { | 515 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { |
484 | const sequelizeOptions = { transaction: t } | 516 | const sequelizeOptions = { transaction: t } |
485 | 517 | ||
486 | const videoCreated = await video.save(sequelizeOptions) | 518 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight |
487 | videoCreated.VideoChannel = channelActor.VideoChannel | 519 | videoCreated.VideoChannel = channel |
488 | 520 | ||
489 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 521 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
490 | 522 | ||
@@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
517 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 549 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
518 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) | 550 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) |
519 | }) | 551 | }) |
520 | const captions = await Promise.all(videoCaptionsPromises) | 552 | await Promise.all(videoCaptionsPromises) |
521 | 553 | ||
522 | video.VideoFiles = videoFiles | 554 | videoCreated.VideoFiles = videoFiles |
523 | video.VideoStreamingPlaylists = streamingPlaylists | 555 | videoCreated.VideoStreamingPlaylists = streamingPlaylists |
524 | video.Tags = tagInstances | 556 | videoCreated.Tags = tagInstances |
525 | video.VideoCaptions = captions | ||
526 | 557 | ||
527 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ | 558 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ |
528 | video, | 559 | video: videoCreated, |
529 | user: undefined, | 560 | user: undefined, |
530 | isRemote: true, | 561 | isRemote: true, |
531 | isNew: true, | 562 | isNew: true, |
@@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
548 | return { autoBlacklisted, videoCreated } | 579 | return { autoBlacklisted, videoCreated } |
549 | } | 580 | } |
550 | 581 | ||
551 | async function videoActivityObjectToDBAttributes ( | 582 | async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) { |
552 | videoChannel: VideoChannelModelId, | ||
553 | videoObject: VideoTorrentObject, | ||
554 | to: string[] = [] | ||
555 | ) { | ||
556 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED | 583 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED |
557 | const duration = videoObject.duration.replace(/[^\d]+/, '') | 584 | const duration = videoObject.duration.replace(/[^\d]+/, '') |
558 | 585 | ||
@@ -603,7 +630,7 @@ async function videoActivityObjectToDBAttributes ( | |||
603 | } | 630 | } |
604 | } | 631 | } |
605 | 632 | ||
606 | function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { | 633 | function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) { |
607 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] | 634 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] |
608 | 635 | ||
609 | if (fileUrls.length === 0) { | 636 | if (fileUrls.length === 0) { |
@@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
641 | return attributes | 668 | return attributes |
642 | } | 669 | } |
643 | 670 | ||
644 | function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { | 671 | function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) { |
645 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] | 672 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] |
646 | if (playlistUrls.length === 0) return [] | 673 | if (playlistUrls.length === 0) return [] |
647 | 674 | ||