diff options
Diffstat (limited to 'server/lib')
60 files changed, 920 insertions, 596 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 9f5d12eb4..13b73077e 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -22,13 +22,27 @@ 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 | MAccountDefault, | ||
28 | MActor, | ||
29 | MActorAccountChannelId, | ||
30 | MActorAccountChannelIdActor, | ||
31 | MActorAccountId, | ||
32 | MActorDefault, | ||
33 | MActorFull, | ||
34 | MActorFullActor, | ||
35 | MActorId, | ||
36 | MChannel, | ||
37 | MChannelAccountDefault | ||
38 | } from '../../typings/models' | ||
25 | 39 | ||
26 | // Set account keys, this could be long so process after the account creation and do not block the client | 40 | // Set account keys, this could be long so process after the account creation and do not block the client |
27 | function setAsyncActorKeys (actor: ActorModel) { | 41 | function setAsyncActorKeys <T extends MActor> (actor: T) { |
28 | return createPrivateAndPublicKeys() | 42 | return createPrivateAndPublicKeys() |
29 | .then(({ publicKey, privateKey }) => { | 43 | .then(({ publicKey, privateKey }) => { |
30 | actor.set('publicKey', publicKey) | 44 | actor.publicKey = publicKey |
31 | actor.set('privateKey', privateKey) | 45 | actor.privateKey = privateKey |
32 | return actor.save() | 46 | return actor.save() |
33 | }) | 47 | }) |
34 | .catch(err => { | 48 | .catch(err => { |
@@ -37,12 +51,26 @@ function setAsyncActorKeys (actor: ActorModel) { | |||
37 | }) | 51 | }) |
38 | } | 52 | } |
39 | 53 | ||
54 | function getOrCreateActorAndServerAndModel ( | ||
55 | activityActor: string | ActivityPubActor, | ||
56 | fetchType: 'all', | ||
57 | recurseIfNeeded?: boolean, | ||
58 | updateCollections?: boolean | ||
59 | ): Promise<MActorFullActor> | ||
60 | |||
61 | function getOrCreateActorAndServerAndModel ( | ||
62 | activityActor: string | ActivityPubActor, | ||
63 | fetchType?: 'association-ids', | ||
64 | recurseIfNeeded?: boolean, | ||
65 | updateCollections?: boolean | ||
66 | ): Promise<MActorAccountChannelId> | ||
67 | |||
40 | async function getOrCreateActorAndServerAndModel ( | 68 | async function getOrCreateActorAndServerAndModel ( |
41 | activityActor: string | ActivityPubActor, | 69 | activityActor: string | ActivityPubActor, |
42 | fetchType: ActorFetchByUrlType = 'actor-and-association-ids', | 70 | fetchType: ActorFetchByUrlType = 'association-ids', |
43 | recurseIfNeeded = true, | 71 | recurseIfNeeded = true, |
44 | updateCollections = false | 72 | updateCollections = false |
45 | ) { | 73 | ): Promise<MActorFullActor | MActorAccountChannelId> { |
46 | const actorUrl = getAPId(activityActor) | 74 | const actorUrl = getAPId(activityActor) |
47 | let created = false | 75 | let created = false |
48 | let accountPlaylistsUrl: string | 76 | let accountPlaylistsUrl: string |
@@ -61,7 +89,7 @@ async function getOrCreateActorAndServerAndModel ( | |||
61 | 89 | ||
62 | // Create the attributed to actor | 90 | // Create the attributed to actor |
63 | // In PeerTube a video channel is owned by an account | 91 | // In PeerTube a video channel is owned by an account |
64 | let ownerActor: ActorModel = undefined | 92 | let ownerActor: MActorFullActor |
65 | if (recurseIfNeeded === true && result.actor.type === 'Group') { | 93 | if (recurseIfNeeded === true && result.actor.type === 'Group') { |
66 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') | 94 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') |
67 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) | 95 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) |
@@ -85,8 +113,8 @@ async function getOrCreateActorAndServerAndModel ( | |||
85 | accountPlaylistsUrl = result.playlists | 113 | accountPlaylistsUrl = result.playlists |
86 | } | 114 | } |
87 | 115 | ||
88 | if (actor.Account) actor.Account.Actor = actor | 116 | if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor |
89 | if (actor.VideoChannel) actor.VideoChannel.Actor = actor | 117 | if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor |
90 | 118 | ||
91 | const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) | 119 | const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) |
92 | if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') | 120 | if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') |
@@ -120,7 +148,7 @@ function buildActorInstance (type: ActivityPubActorType, url: string, preferredU | |||
120 | sharedInboxUrl: WEBSERVER.URL + '/inbox', | 148 | sharedInboxUrl: WEBSERVER.URL + '/inbox', |
121 | followersUrl: url + '/followers', | 149 | followersUrl: url + '/followers', |
122 | followingUrl: url + '/following' | 150 | followingUrl: url + '/following' |
123 | }) | 151 | }) as MActor |
124 | } | 152 | } |
125 | 153 | ||
126 | async function updateActorInstance (actorInstance: ActorModel, attributes: ActivityPubActor) { | 154 | async function updateActorInstance (actorInstance: ActorModel, attributes: ActivityPubActor) { |
@@ -140,7 +168,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
140 | actorInstance.followingUrl = attributes.following | 168 | actorInstance.followingUrl = attributes.following |
141 | } | 169 | } |
142 | 170 | ||
143 | async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { | 171 | type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string } |
172 | async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) { | ||
144 | if (info.name !== undefined) { | 173 | if (info.name !== undefined) { |
145 | if (actor.avatarId) { | 174 | if (actor.avatarId) { |
146 | try { | 175 | try { |
@@ -212,14 +241,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) { | |||
212 | return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) | 241 | return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) |
213 | } | 242 | } |
214 | 243 | ||
215 | async function refreshActorIfNeeded ( | 244 | async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> ( |
216 | actorArg: ActorModel, | 245 | actorArg: T, |
217 | fetchedType: ActorFetchByUrlType | 246 | fetchedType: ActorFetchByUrlType |
218 | ): Promise<{ actor: ActorModel, refreshed: boolean }> { | 247 | ): Promise<{ actor: T | MActorFull, refreshed: boolean }> { |
219 | if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } | 248 | if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } |
220 | 249 | ||
221 | // We need more attributes | 250 | // We need more attributes |
222 | const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) | 251 | const actor = fetchedType === 'all' |
252 | ? actorArg as MActorFull | ||
253 | : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) | ||
223 | 254 | ||
224 | try { | 255 | try { |
225 | let actorUrl: string | 256 | let actorUrl: string |
@@ -297,9 +328,9 @@ export { | |||
297 | 328 | ||
298 | function saveActorAndServerAndModelIfNotExist ( | 329 | function saveActorAndServerAndModelIfNotExist ( |
299 | result: FetchRemoteActorResult, | 330 | result: FetchRemoteActorResult, |
300 | ownerActor?: ActorModel, | 331 | ownerActor?: MActorFullActor, |
301 | t?: Transaction | 332 | t?: Transaction |
302 | ): Bluebird<ActorModel> | Promise<ActorModel> { | 333 | ): Bluebird<MActorFullActor> | Promise<MActorFullActor> { |
303 | let actor = result.actor | 334 | let actor = result.actor |
304 | 335 | ||
305 | if (t !== undefined) return save(t) | 336 | if (t !== undefined) return save(t) |
@@ -336,7 +367,7 @@ function saveActorAndServerAndModelIfNotExist ( | |||
336 | 367 | ||
337 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists | 368 | // 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) | 369 | // (which could be false in a retried query) |
339 | const [ actorCreated ] = await ActorModel.findOrCreate({ | 370 | const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({ |
340 | defaults: actor.toJSON(), | 371 | defaults: actor.toJSON(), |
341 | where: { | 372 | where: { |
342 | url: actor.url | 373 | url: actor.url |
@@ -345,12 +376,11 @@ function saveActorAndServerAndModelIfNotExist ( | |||
345 | }) | 376 | }) |
346 | 377 | ||
347 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { | 378 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { |
348 | actorCreated.Account = await saveAccount(actorCreated, result, t) | 379 | actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountDefault |
349 | actorCreated.Account.Actor = actorCreated | 380 | actorCreated.Account.Actor = actorCreated |
350 | } else if (actorCreated.type === 'Group') { // Video channel | 381 | } else if (actorCreated.type === 'Group') { // Video channel |
351 | actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) | 382 | const channel = await saveVideoChannel(actorCreated, result, ownerActor, t) |
352 | actorCreated.VideoChannel.Actor = actorCreated | 383 | actorCreated.VideoChannel = Object.assign(channel, { Actor: actorCreated, Account: ownerActor.Account }) |
353 | actorCreated.VideoChannel.Account = ownerActor.Account | ||
354 | } | 384 | } |
355 | 385 | ||
356 | actorCreated.Server = server | 386 | actorCreated.Server = server |
@@ -360,7 +390,7 @@ function saveActorAndServerAndModelIfNotExist ( | |||
360 | } | 390 | } |
361 | 391 | ||
362 | type FetchRemoteActorResult = { | 392 | type FetchRemoteActorResult = { |
363 | actor: ActorModel | 393 | actor: MActor |
364 | name: string | 394 | name: string |
365 | summary: string | 395 | summary: string |
366 | support?: string | 396 | support?: string |
@@ -429,7 +459,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
429 | } | 459 | } |
430 | } | 460 | } |
431 | 461 | ||
432 | async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { | 462 | async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) { |
433 | const [ accountCreated ] = await AccountModel.findOrCreate({ | 463 | const [ accountCreated ] = await AccountModel.findOrCreate({ |
434 | defaults: { | 464 | defaults: { |
435 | name: result.name, | 465 | name: result.name, |
@@ -442,10 +472,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t | |||
442 | transaction: t | 472 | transaction: t |
443 | }) | 473 | }) |
444 | 474 | ||
445 | return accountCreated | 475 | return accountCreated as MAccount |
446 | } | 476 | } |
447 | 477 | ||
448 | async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { | 478 | async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) { |
449 | const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ | 479 | const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ |
450 | defaults: { | 480 | defaults: { |
451 | name: result.name, | 481 | name: result.name, |
@@ -460,5 +490,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu | |||
460 | transaction: t | 490 | transaction: t |
461 | }) | 491 | }) |
462 | 492 | ||
463 | return videoChannelCreated | 493 | return videoChannelCreated as MChannel |
464 | } | 494 | } |
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/follow.ts b/server/lib/activitypub/follow.ts new file mode 100644 index 000000000..1abf43cd4 --- /dev/null +++ b/server/lib/activitypub/follow.ts | |||
@@ -0,0 +1,36 @@ | |||
1 | import { MActorFollowActors } from '../../typings/models' | ||
2 | import { CONFIG } from '../../initializers/config' | ||
3 | import { SERVER_ACTOR_NAME } from '../../initializers/constants' | ||
4 | import { JobQueue } from '../job-queue' | ||
5 | import { logger } from '../../helpers/logger' | ||
6 | import { getServerActor } from '../../helpers/utils' | ||
7 | import { ServerModel } from '../../models/server/server' | ||
8 | |||
9 | async function autoFollowBackIfNeeded (actorFollow: MActorFollowActors) { | ||
10 | if (!CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_BACK.ENABLED) return | ||
11 | |||
12 | const follower = actorFollow.ActorFollower | ||
13 | |||
14 | if (follower.type === 'Application' && follower.preferredUsername === SERVER_ACTOR_NAME) { | ||
15 | logger.info('Auto follow back %s.', follower.url) | ||
16 | |||
17 | const me = await getServerActor() | ||
18 | |||
19 | const server = await ServerModel.load(follower.serverId) | ||
20 | const host = server.host | ||
21 | |||
22 | const payload = { | ||
23 | host, | ||
24 | name: SERVER_ACTOR_NAME, | ||
25 | followerActorId: me.id, | ||
26 | isAutoFollow: true | ||
27 | } | ||
28 | |||
29 | JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | ||
30 | .catch(err => logger.error('Cannot create auto follow back job for %s.', host, err)) | ||
31 | } | ||
32 | } | ||
33 | |||
34 | export { | ||
35 | autoFollowBackIfNeeded | ||
36 | } | ||
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..dcfbb2c84 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,12 +19,12 @@ 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 | ||
27 | if (follow.state !== 'accepted') { | 26 | if (follow.state !== 'accepted') { |
28 | follow.set('state', 'accepted') | 27 | follow.state = 'accepted' |
29 | await follow.save() | 28 | await follow.save() |
30 | 29 | ||
31 | await addFetchOutboxJob(targetActor) | 30 | await addFetchOutboxJob(targetActor) |
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index b3cdc4441..7e22125d5 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, MVideoAccountLightBlacklistAllFiles } 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: MVideoAccountLightBlacklistAllFiles |
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..bee853721 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, MVideoAccountLightBlacklistAllFiles } 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: MVideoAccountLightBlacklistAllFiles |
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..e6e9084de 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,31 +23,39 @@ 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)) | ||
30 | |||
31 | const account = byActor.Account | 29 | const account = byActor.Account |
32 | if (!account) throw new Error('Cannot create video abuse with the non account actor ' + byActor.url) | 30 | if (!account) throw new Error('Cannot create video abuse with the non account actor ' + byActor.url) |
33 | 31 | ||
34 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: flag.object }) | 32 | const objects = Array.isArray(flag.object) ? flag.object : [ flag.object ] |
35 | 33 | ||
36 | const videoAbuse = await sequelizeTypescript.transaction(async t => { | 34 | for (const object of objects) { |
37 | const videoAbuseData = { | 35 | try { |
38 | reporterAccountId: account.id, | 36 | logger.debug('Reporting remote abuse for video %s.', getAPId(object)) |
39 | reason: flag.content, | 37 | |
40 | videoId: video.id, | 38 | const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: object }) |
41 | state: VideoAbuseState.PENDING | ||
42 | } | ||
43 | 39 | ||
44 | const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) | 40 | const videoAbuse = await sequelizeTypescript.transaction(async t => { |
45 | videoAbuseInstance.Video = video | 41 | const videoAbuseData = { |
42 | reporterAccountId: account.id, | ||
43 | reason: flag.content, | ||
44 | videoId: video.id, | ||
45 | state: VideoAbuseState.PENDING | ||
46 | } | ||
46 | 47 | ||
47 | logger.info('Remote abuse for video uuid %s created', flag.object) | 48 | const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo |
49 | videoAbuseInstance.Video = video | ||
48 | 50 | ||
49 | return videoAbuseInstance | 51 | logger.info('Remote abuse for video uuid %s created', flag.object) |
50 | }) | ||
51 | 52 | ||
52 | Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse) | 53 | return videoAbuseInstance |
54 | }) | ||
55 | |||
56 | Notifier.Instance.notifyOnNewVideoAbuse(videoAbuse) | ||
57 | } catch (err) { | ||
58 | logger.debug('Cannot process report of %s. (Maybe not a video abuse).', getAPId(object), { err }) | ||
59 | } | ||
60 | } | ||
53 | } | 61 | } |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 240aa5799..85f22d654 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -10,8 +10,8 @@ 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 { MActorFollowActors, MActorSignature } from '../../../typings/models' |
14 | import { ActorFollowModelLight } from '../../../typings/models/actor-follow' | 14 | import { autoFollowBackIfNeeded } from '../follow' |
15 | 15 | ||
16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { | 16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { |
17 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
@@ -28,8 +28,8 @@ export { | |||
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
31 | async function processFollow (byActor: SignatureActorModel, targetActorURL: string) { | 31 | async function processFollow (byActor: MActorSignature, targetActorURL: string) { |
32 | const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { | 32 | const { actorFollow, created, isFollowingInstance, targetActor } = await sequelizeTypescript.transaction(async t => { |
33 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) | 33 | const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) |
34 | 34 | ||
35 | if (!targetActor) throw new Error('Unknown actor') | 35 | if (!targetActor) throw new Error('Unknown actor') |
@@ -43,10 +43,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
43 | 43 | ||
44 | await sendReject(byActor, targetActor) | 44 | await sendReject(byActor, targetActor) |
45 | 45 | ||
46 | return { actorFollow: undefined } | 46 | return { actorFollow: undefined as MActorFollowActors } |
47 | } | 47 | } |
48 | 48 | ||
49 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ | 49 | const [ actorFollow, created ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({ |
50 | where: { | 50 | where: { |
51 | actorId: byActor.id, | 51 | actorId: byActor.id, |
52 | targetActorId: targetActor.id | 52 | targetActorId: targetActor.id |
@@ -57,7 +57,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
57 | state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' | 57 | state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' |
58 | }, | 58 | }, |
59 | transaction: t | 59 | transaction: t |
60 | }) as [ ActorFollowModelLight, boolean ] | 60 | }) |
61 | 61 | ||
62 | if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { | 62 | if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { |
63 | actorFollow.state = 'accepted' | 63 | actorFollow.state = 'accepted' |
@@ -68,17 +68,26 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri | |||
68 | actorFollow.ActorFollowing = targetActor | 68 | actorFollow.ActorFollowing = targetActor |
69 | 69 | ||
70 | // Target sends to actor he accepted the follow request | 70 | // Target sends to actor he accepted the follow request |
71 | if (actorFollow.state === 'accepted') await sendAccept(actorFollow) | 71 | if (actorFollow.state === 'accepted') { |
72 | await sendAccept(actorFollow) | ||
73 | await autoFollowBackIfNeeded(actorFollow) | ||
74 | } | ||
72 | 75 | ||
73 | return { actorFollow, created, isFollowingInstance } | 76 | return { actorFollow, created, isFollowingInstance, targetActor } |
74 | }) | 77 | }) |
75 | 78 | ||
76 | // Rejected | 79 | // Rejected |
77 | if (!actorFollow) return | 80 | if (!actorFollow) return |
78 | 81 | ||
79 | if (created) { | 82 | if (created) { |
80 | if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) | 83 | const follower = await ActorModel.loadFull(byActor.id) |
81 | else Notifier.Instance.notifyOfNewUserFollow(actorFollow) | 84 | const actorFollowFull = Object.assign(actorFollow, { ActorFollowing: targetActor, ActorFollower: follower }) |
85 | |||
86 | if (isFollowingInstance) { | ||
87 | Notifier.Instance.notifyOfNewInstanceFollow(actorFollowFull) | ||
88 | } else { | ||
89 | Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) | ||
90 | } | ||
82 | } | 91 | } |
83 | 92 | ||
84 | logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) | 93 | 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..a47d605d8 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, MAccountIdActor } 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,23 @@ 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 account = actor.Account as MAccountIdActor | ||
68 | account.Actor = actor | ||
69 | |||
67 | const updateOptions = { | 70 | const updateOptions = { |
68 | video, | 71 | video, |
69 | videoObject, | 72 | videoObject, |
70 | account: actor.Account, | 73 | account, |
71 | channel: channelActor.VideoChannel, | 74 | channel: channelActor.VideoChannel, |
72 | overrideTo: activity.to | 75 | overrideTo: activity.to |
73 | } | 76 | } |
74 | return updateVideoFromAP(updateOptions) | 77 | return updateVideoFromAP(updateOptions) |
75 | } | 78 | } |
76 | 79 | ||
77 | async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) { | 80 | async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) { |
78 | const cacheFileObject = activity.object as CacheFileObject | 81 | const cacheFileObject = activity.object as CacheFileObject |
79 | 82 | ||
80 | if (!isCacheFileObjectValid(cacheFileObject)) { | 83 | if (!isCacheFileObjectValid(cacheFileObject)) { |
@@ -150,7 +153,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
150 | } | 153 | } |
151 | } | 154 | } |
152 | 155 | ||
153 | async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) { | 156 | async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) { |
154 | const playlistObject = activity.object as PlaylistObject | 157 | const playlistObject = activity.object as PlaylistObject |
155 | const byAccount = byActor.Account | 158 | const byAccount = byActor.Account |
156 | 159 | ||
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..ce400d8ff 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -1,12 +1,11 @@ | |||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 1 | import { ActivityFollow } from '../../../../shared/models/activitypub' |
2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
3 | import { getActorFollowActivityPubUrl } from '../url' | 2 | import { getActorFollowActivityPubUrl } from '../url' |
4 | import { unicastTo } from './utils' | 3 | import { unicastTo } from './utils' |
5 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
6 | import { Transaction } from 'sequelize' | 5 | import { Transaction } from 'sequelize' |
7 | import { ActorModelOnly } from '../../../typings/models' | 6 | import { MActor, MActorFollowActors } from '../../../typings/models' |
8 | 7 | ||
9 | function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { | 8 | function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { |
10 | const me = actorFollow.ActorFollower | 9 | const me = actorFollow.ActorFollower |
11 | const following = actorFollow.ActorFollowing | 10 | const following = actorFollow.ActorFollowing |
12 | 11 | ||
@@ -21,7 +20,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { | |||
21 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) | 20 | t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) |
22 | } | 21 | } |
23 | 22 | ||
24 | function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow { | 23 | function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow { |
25 | return { | 24 | return { |
26 | type: 'Follow', | 25 | type: 'Follow', |
27 | id: url, | 26 | 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..37517c2be 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 | MAccountDefault, | ||
16 | MActor, | ||
17 | MActorLight, | ||
18 | MChannelDefault, | ||
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: MChannelDefault | MAccountDefault, 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..3e8306fa4 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, MVideoAccountLightBlacklistAllFiles } 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: MVideoAccountLightBlacklistAllFiles, 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..c318978fd 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,31 @@ 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 | MAccountIdActor, | ||
57 | MChannelAccountLight, | ||
58 | MChannelDefault, | ||
59 | MChannelId, | ||
60 | MVideo, | ||
61 | MVideoAccountLight, | ||
62 | MVideoAccountLightBlacklistAllFiles, | ||
63 | MVideoAP, | ||
64 | MVideoAPWithoutCaption, | ||
65 | MVideoFile, | ||
66 | MVideoFullLight, | ||
67 | MVideoId, | ||
68 | MVideoThumbnail | ||
69 | } from '../../typings/models' | ||
70 | import { MThumbnail } from '../../typings/models/video/thumbnail' | ||
71 | |||
72 | async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
73 | const video = videoArg as MVideoAP | ||
59 | 74 | ||
60 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | ||
61 | if ( | 75 | if ( |
62 | // Check this is not a blacklisted video, or unfederated blacklisted video | 76 | // Check this is not a blacklisted video, or unfederated blacklisted video |
63 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && | 77 | (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && |
@@ -102,7 +116,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request. | |||
102 | return { response, videoObject: body } | 116 | return { response, videoObject: body } |
103 | } | 117 | } |
104 | 118 | ||
105 | async function fetchRemoteVideoDescription (video: VideoModel) { | 119 | async function fetchRemoteVideoDescription (video: MVideoAccountLight) { |
106 | const host = video.VideoChannel.Account.Actor.Server.host | 120 | const host = video.VideoChannel.Account.Actor.Server.host |
107 | const path = video.getDescriptionAPIPath() | 121 | const path = video.getDescriptionAPIPath() |
108 | const options = { | 122 | const options = { |
@@ -114,14 +128,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) { | |||
114 | return body.description ? body.description : '' | 128 | return body.description ? body.description : '' |
115 | } | 129 | } |
116 | 130 | ||
117 | function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { | 131 | function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) { |
118 | const url = buildRemoteBaseUrl(video, path) | 132 | const url = buildRemoteBaseUrl(video, path) |
119 | 133 | ||
120 | // We need to provide a callback, if no we could have an uncaught exception | 134 | // We need to provide a callback, if no we could have an uncaught exception |
121 | return doRequestAndSaveToFile({ uri: url }, destPath) | 135 | return doRequestAndSaveToFile({ uri: url }, destPath) |
122 | } | 136 | } |
123 | 137 | ||
124 | function buildRemoteBaseUrl (video: VideoModel, path: string) { | 138 | function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) { |
125 | const host = video.VideoChannel.Account.Actor.Server.host | 139 | const host = video.VideoChannel.Account.Actor.Server.host |
126 | 140 | ||
127 | return REMOTE_SCHEME.HTTP + '://' + host + path | 141 | return REMOTE_SCHEME.HTTP + '://' + host + path |
@@ -146,7 +160,7 @@ type SyncParam = { | |||
146 | thumbnail: boolean | 160 | thumbnail: boolean |
147 | refreshVideo?: boolean | 161 | refreshVideo?: boolean |
148 | } | 162 | } |
149 | async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { | 163 | async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { |
150 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) | 164 | logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) |
151 | 165 | ||
152 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] | 166 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] |
@@ -194,12 +208,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
194 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) | 208 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) |
195 | } | 209 | } |
196 | 210 | ||
211 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
212 | videoObject: { id: string } | string, | ||
213 | syncParam?: SyncParam, | ||
214 | fetchType?: 'all', | ||
215 | allowRefresh?: boolean | ||
216 | }): Promise<{ video: MVideoAccountLightBlacklistAllFiles, created: boolean, autoBlacklisted?: boolean }> | ||
217 | function getOrCreateVideoAndAccountAndChannel (options: { | ||
218 | videoObject: { id: string } | string, | ||
219 | syncParam?: SyncParam, | ||
220 | fetchType?: VideoFetchByUrlType, | ||
221 | allowRefresh?: boolean | ||
222 | }): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> | ||
197 | async function getOrCreateVideoAndAccountAndChannel (options: { | 223 | async function getOrCreateVideoAndAccountAndChannel (options: { |
198 | videoObject: { id: string } | string, | 224 | videoObject: { id: string } | string, |
199 | syncParam?: SyncParam, | 225 | syncParam?: SyncParam, |
200 | fetchType?: VideoFetchByUrlType, | 226 | fetchType?: VideoFetchByUrlType, |
201 | allowRefresh?: boolean // true by default | 227 | allowRefresh?: boolean // true by default |
202 | }) { | 228 | }): Promise<{ video: MVideoAccountLightBlacklistAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> { |
203 | // Default params | 229 | // Default params |
204 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } | 230 | const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } |
205 | const fetchType = options.fetchType || 'all' | 231 | const fetchType = options.fetchType || 'all' |
@@ -227,8 +253,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
227 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) | 253 | const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) |
228 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) | 254 | if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) |
229 | 255 | ||
230 | const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) | 256 | const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) |
231 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) | 257 | const videoChannel = actor.VideoChannel |
258 | const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail) | ||
232 | 259 | ||
233 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) | 260 | await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) |
234 | 261 | ||
@@ -236,22 +263,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: { | |||
236 | } | 263 | } |
237 | 264 | ||
238 | async function updateVideoFromAP (options: { | 265 | async function updateVideoFromAP (options: { |
239 | video: VideoModel, | 266 | video: MVideoAccountLightBlacklistAllFiles, |
240 | videoObject: VideoTorrentObject, | 267 | videoObject: VideoTorrentObject, |
241 | account: AccountModelIdActor, | 268 | account: MAccountIdActor, |
242 | channel: VideoChannelModelIdActor, | 269 | channel: MChannelDefault, |
243 | overrideTo?: string[] | 270 | overrideTo?: string[] |
244 | }) { | 271 | }) { |
245 | const { video, videoObject, account, channel, overrideTo } = options | 272 | const { video, videoObject, account, channel, overrideTo } = options |
246 | 273 | ||
247 | logger.debug('Updating remote video "%s".', options.videoObject.uuid) | 274 | logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel }) |
248 | 275 | ||
249 | let videoFieldsSave: any | 276 | let videoFieldsSave: any |
250 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE | 277 | const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE |
251 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED | 278 | const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED |
252 | 279 | ||
253 | try { | 280 | try { |
254 | let thumbnailModel: ThumbnailModel | 281 | let thumbnailModel: MThumbnail |
255 | 282 | ||
256 | try { | 283 | try { |
257 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 284 | thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
@@ -259,7 +286,7 @@ async function updateVideoFromAP (options: { | |||
259 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) | 286 | logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) |
260 | } | 287 | } |
261 | 288 | ||
262 | await sequelizeTypescript.transaction(async t => { | 289 | const videoUpdated = await sequelizeTypescript.transaction(async t => { |
263 | const sequelizeOptions = { transaction: t } | 290 | const sequelizeOptions = { transaction: t } |
264 | 291 | ||
265 | videoFieldsSave = video.toJSON() | 292 | videoFieldsSave = video.toJSON() |
@@ -293,21 +320,21 @@ async function updateVideoFromAP (options: { | |||
293 | video.channelId = videoData.channelId | 320 | video.channelId = videoData.channelId |
294 | video.views = videoData.views | 321 | video.views = videoData.views |
295 | 322 | ||
296 | await video.save(sequelizeOptions) | 323 | const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight |
297 | 324 | ||
298 | if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) | 325 | if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) |
299 | 326 | ||
300 | // FIXME: use icon URL instead | 327 | // FIXME: use icon URL instead |
301 | const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) | 328 | const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename)) |
302 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) | 329 | const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) |
303 | await video.addAndSaveThumbnail(previewModel, t) | 330 | await videoUpdated.addAndSaveThumbnail(previewModel, t) |
304 | 331 | ||
305 | { | 332 | { |
306 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) | 333 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject) |
307 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) | 334 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) |
308 | 335 | ||
309 | // Remove video files that do not exist anymore | 336 | // Remove video files that do not exist anymore |
310 | const destroyTasks = video.VideoFiles | 337 | const destroyTasks = videoUpdated.VideoFiles |
311 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) | 338 | .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) |
312 | .map(f => f.destroy(sequelizeOptions)) | 339 | .map(f => f.destroy(sequelizeOptions)) |
313 | await Promise.all(destroyTasks) | 340 | await Promise.all(destroyTasks) |
@@ -318,15 +345,15 @@ async function updateVideoFromAP (options: { | |||
318 | .then(([ file ]) => file) | 345 | .then(([ file ]) => file) |
319 | }) | 346 | }) |
320 | 347 | ||
321 | video.VideoFiles = await Promise.all(upsertTasks) | 348 | videoUpdated.VideoFiles = await Promise.all(upsertTasks) |
322 | } | 349 | } |
323 | 350 | ||
324 | { | 351 | { |
325 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) | 352 | const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles) |
326 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) | 353 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) |
327 | 354 | ||
328 | // Remove video files that do not exist anymore | 355 | // Remove video files that do not exist anymore |
329 | const destroyTasks = video.VideoStreamingPlaylists | 356 | const destroyTasks = videoUpdated.VideoStreamingPlaylists |
330 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) | 357 | .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) |
331 | .map(f => f.destroy(sequelizeOptions)) | 358 | .map(f => f.destroy(sequelizeOptions)) |
332 | await Promise.all(destroyTasks) | 359 | await Promise.all(destroyTasks) |
@@ -337,38 +364,42 @@ async function updateVideoFromAP (options: { | |||
337 | .then(([ streamingPlaylist ]) => streamingPlaylist) | 364 | .then(([ streamingPlaylist ]) => streamingPlaylist) |
338 | }) | 365 | }) |
339 | 366 | ||
340 | video.VideoStreamingPlaylists = await Promise.all(upsertTasks) | 367 | videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks) |
341 | } | 368 | } |
342 | 369 | ||
343 | { | 370 | { |
344 | // Update Tags | 371 | // Update Tags |
345 | const tags = videoObject.tag.map(tag => tag.name) | 372 | const tags = videoObject.tag.map(tag => tag.name) |
346 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | 373 | const tagInstances = await TagModel.findOrCreateTags(tags, t) |
347 | await video.$set('Tags', tagInstances, sequelizeOptions) | 374 | await videoUpdated.$set('Tags', tagInstances, sequelizeOptions) |
348 | } | 375 | } |
349 | 376 | ||
350 | { | 377 | { |
351 | // Update captions | 378 | // Update captions |
352 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) | 379 | await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t) |
353 | 380 | ||
354 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 381 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
355 | return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) | 382 | return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t) |
356 | }) | 383 | }) |
357 | video.VideoCaptions = await Promise.all(videoCaptionsPromises) | 384 | await Promise.all(videoCaptionsPromises) |
358 | } | 385 | } |
386 | |||
387 | return videoUpdated | ||
359 | }) | 388 | }) |
360 | 389 | ||
361 | await autoBlacklistVideoIfNeeded({ | 390 | await autoBlacklistVideoIfNeeded({ |
362 | video, | 391 | video: videoUpdated, |
363 | user: undefined, | 392 | user: undefined, |
364 | isRemote: true, | 393 | isRemote: true, |
365 | isNew: false, | 394 | isNew: false, |
366 | transaction: undefined | 395 | transaction: undefined |
367 | }) | 396 | }) |
368 | 397 | ||
369 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? | 398 | if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users? |
370 | 399 | ||
371 | logger.info('Remote video with uuid %s updated', videoObject.uuid) | 400 | logger.info('Remote video with uuid %s updated', videoObject.uuid) |
401 | |||
402 | return videoUpdated | ||
372 | } catch (err) { | 403 | } catch (err) { |
373 | if (video !== undefined && videoFieldsSave !== undefined) { | 404 | if (video !== undefined && videoFieldsSave !== undefined) { |
374 | resetSequelizeInstance(video, videoFieldsSave) | 405 | resetSequelizeInstance(video, videoFieldsSave) |
@@ -381,15 +412,15 @@ async function updateVideoFromAP (options: { | |||
381 | } | 412 | } |
382 | 413 | ||
383 | async function refreshVideoIfNeeded (options: { | 414 | async function refreshVideoIfNeeded (options: { |
384 | video: VideoModel, | 415 | video: MVideoThumbnail, |
385 | fetchedType: VideoFetchByUrlType, | 416 | fetchedType: VideoFetchByUrlType, |
386 | syncParam: SyncParam | 417 | syncParam: SyncParam |
387 | }): Promise<VideoModel> { | 418 | }): Promise<MVideoThumbnail> { |
388 | if (!options.video.isOutdated()) return options.video | 419 | if (!options.video.isOutdated()) return options.video |
389 | 420 | ||
390 | // We need more attributes if the argument video was fetched with not enough joints | 421 | // We need more attributes if the argument video was fetched with not enough joints |
391 | const video = options.fetchedType === 'all' | 422 | const video = options.fetchedType === 'all' |
392 | ? options.video | 423 | ? options.video as MVideoAccountLightBlacklistAllFiles |
393 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) | 424 | : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) |
394 | 425 | ||
395 | try { | 426 | try { |
@@ -410,12 +441,11 @@ async function refreshVideoIfNeeded (options: { | |||
410 | } | 441 | } |
411 | 442 | ||
412 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) | 443 | const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) |
413 | const account = await AccountModel.load(channelActor.VideoChannel.accountId) | ||
414 | 444 | ||
415 | const updateOptions = { | 445 | const updateOptions = { |
416 | video, | 446 | video, |
417 | videoObject, | 447 | videoObject, |
418 | account, | 448 | account: channelActor.VideoChannel.Account, |
419 | channel: channelActor.VideoChannel | 449 | channel: channelActor.VideoChannel |
420 | } | 450 | } |
421 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) | 451 | await retryTransactionWrapper(updateVideoFromAP, updateOptions) |
@@ -467,15 +497,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS | |||
467 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' | 497 | return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' |
468 | } | 498 | } |
469 | 499 | ||
470 | async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { | 500 | async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { |
471 | logger.debug('Adding remote video %s.', videoObject.id) | 501 | logger.debug('Adding remote video %s.', videoObject.id) |
472 | 502 | ||
473 | const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) | 503 | const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to) |
474 | const video = VideoModel.build(videoData) | 504 | const video = VideoModel.build(videoData) as MVideoThumbnail |
475 | 505 | ||
476 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) | 506 | const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) |
477 | 507 | ||
478 | let thumbnailModel: ThumbnailModel | 508 | let thumbnailModel: MThumbnail |
479 | if (waitThumbnail === true) { | 509 | if (waitThumbnail === true) { |
480 | thumbnailModel = await promiseThumbnail | 510 | thumbnailModel = await promiseThumbnail |
481 | } | 511 | } |
@@ -483,8 +513,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
483 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { | 513 | const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { |
484 | const sequelizeOptions = { transaction: t } | 514 | const sequelizeOptions = { transaction: t } |
485 | 515 | ||
486 | const videoCreated = await video.save(sequelizeOptions) | 516 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight |
487 | videoCreated.VideoChannel = channelActor.VideoChannel | 517 | videoCreated.VideoChannel = channel |
488 | 518 | ||
489 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 519 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
490 | 520 | ||
@@ -517,15 +547,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
517 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { | 547 | const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { |
518 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) | 548 | return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) |
519 | }) | 549 | }) |
520 | const captions = await Promise.all(videoCaptionsPromises) | 550 | await Promise.all(videoCaptionsPromises) |
521 | 551 | ||
522 | video.VideoFiles = videoFiles | 552 | videoCreated.VideoFiles = videoFiles |
523 | video.VideoStreamingPlaylists = streamingPlaylists | 553 | videoCreated.VideoStreamingPlaylists = streamingPlaylists |
524 | video.Tags = tagInstances | 554 | videoCreated.Tags = tagInstances |
525 | video.VideoCaptions = captions | ||
526 | 555 | ||
527 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ | 556 | const autoBlacklisted = await autoBlacklistVideoIfNeeded({ |
528 | video, | 557 | video: videoCreated, |
529 | user: undefined, | 558 | user: undefined, |
530 | isRemote: true, | 559 | isRemote: true, |
531 | isNew: true, | 560 | isNew: true, |
@@ -548,11 +577,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor | |||
548 | return { autoBlacklisted, videoCreated } | 577 | return { autoBlacklisted, videoCreated } |
549 | } | 578 | } |
550 | 579 | ||
551 | async function videoActivityObjectToDBAttributes ( | 580 | 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 | 581 | const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED |
557 | const duration = videoObject.duration.replace(/[^\d]+/, '') | 582 | const duration = videoObject.duration.replace(/[^\d]+/, '') |
558 | 583 | ||
@@ -603,7 +628,7 @@ async function videoActivityObjectToDBAttributes ( | |||
603 | } | 628 | } |
604 | } | 629 | } |
605 | 630 | ||
606 | function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { | 631 | function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) { |
607 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] | 632 | const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] |
608 | 633 | ||
609 | if (fileUrls.length === 0) { | 634 | if (fileUrls.length === 0) { |
@@ -641,7 +666,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid | |||
641 | return attributes | 666 | return attributes |
642 | } | 667 | } |
643 | 668 | ||
644 | function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { | 669 | function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) { |
645 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] | 670 | const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] |
646 | if (playlistUrls.length === 0) return [] | 671 | if (playlistUrls.length === 0) return [] |
647 | 672 | ||
diff --git a/server/lib/avatar.ts b/server/lib/avatar.ts index 1b38e6cb5..ad4cdd3ab 100644 --- a/server/lib/avatar.ts +++ b/server/lib/avatar.ts | |||
@@ -3,8 +3,6 @@ import { sendUpdateActor } from './activitypub/send' | |||
3 | import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' | 3 | import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' |
4 | import { updateActorAvatarInstance } from './activitypub' | 4 | import { updateActorAvatarInstance } from './activitypub' |
5 | import { processImage } from '../helpers/image-utils' | 5 | import { processImage } from '../helpers/image-utils' |
6 | import { AccountModel } from '../models/account/account' | ||
7 | import { VideoChannelModel } from '../models/video/video-channel' | ||
8 | import { extname, join } from 'path' | 6 | import { extname, join } from 'path' |
9 | import { retryTransactionWrapper } from '../helpers/database-utils' | 7 | import { retryTransactionWrapper } from '../helpers/database-utils' |
10 | import * as uuidv4 from 'uuid/v4' | 8 | import * as uuidv4 from 'uuid/v4' |
@@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database' | |||
13 | import * as LRUCache from 'lru-cache' | 11 | import * as LRUCache from 'lru-cache' |
14 | import { queue } from 'async' | 12 | import { queue } from 'async' |
15 | import { downloadImage } from '../helpers/requests' | 13 | import { downloadImage } from '../helpers/requests' |
14 | import { MAccountDefault, MChannelDefault } from '../typings/models' | ||
16 | 15 | ||
17 | async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) { | 16 | async function updateActorAvatarFile ( |
17 | avatarPhysicalFile: Express.Multer.File, | ||
18 | accountOrChannel: MAccountDefault | MChannelDefault | ||
19 | ) { | ||
18 | const extension = extname(avatarPhysicalFile.filename) | 20 | const extension = extname(avatarPhysicalFile.filename) |
19 | const avatarName = uuidv4() + extension | 21 | const avatarName = uuidv4() + extension |
20 | const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName) | 22 | const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName) |
diff --git a/server/lib/blocklist.ts b/server/lib/blocklist.ts index 1633e500c..28c69b46e 100644 --- a/server/lib/blocklist.ts +++ b/server/lib/blocklist.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { sequelizeTypescript } from '../initializers' | 1 | import { sequelizeTypescript } from '../initializers' |
2 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 2 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
3 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | 3 | import { ServerBlocklistModel } from '../models/server/server-blocklist' |
4 | import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models' | ||
4 | 5 | ||
5 | function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { | 6 | function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { |
6 | return sequelizeTypescript.transaction(async t => { | 7 | return sequelizeTypescript.transaction(async t => { |
@@ -20,13 +21,13 @@ function addServerInBlocklist (byAccountId: number, targetServerId: number) { | |||
20 | }) | 21 | }) |
21 | } | 22 | } |
22 | 23 | ||
23 | function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) { | 24 | function removeAccountFromBlocklist (accountBlock: MAccountBlocklist) { |
24 | return sequelizeTypescript.transaction(async t => { | 25 | return sequelizeTypescript.transaction(async t => { |
25 | return accountBlock.destroy({ transaction: t }) | 26 | return accountBlock.destroy({ transaction: t }) |
26 | }) | 27 | }) |
27 | } | 28 | } |
28 | 29 | ||
29 | function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) { | 30 | function removeServerFromBlocklist (serverBlock: MServerBlocklist) { |
30 | return sequelizeTypescript.transaction(async t => { | 31 | return sequelizeTypescript.transaction(async t => { |
31 | return serverBlock.destroy({ transaction: t }) | 32 | return serverBlock.destroy({ transaction: t }) |
32 | }) | 33 | }) |
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 8841dd2ac..a1f4ae858 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -13,6 +13,7 @@ import { VideoChannelModel } from '../models/video/video-channel' | |||
13 | import * as Bluebird from 'bluebird' | 13 | import * as Bluebird from 'bluebird' |
14 | import { CONFIG } from '../initializers/config' | 14 | import { CONFIG } from '../initializers/config' |
15 | import { logger } from '../helpers/logger' | 15 | import { logger } from '../helpers/logger' |
16 | import { MAccountActor, MChannelActor, MVideo } from '../typings/models' | ||
16 | 17 | ||
17 | export class ClientHtml { | 18 | export class ClientHtml { |
18 | 19 | ||
@@ -41,11 +42,11 @@ export class ClientHtml { | |||
41 | 42 | ||
42 | const [ html, video ] = await Promise.all([ | 43 | const [ html, video ] = await Promise.all([ |
43 | ClientHtml.getIndexHTML(req, res), | 44 | ClientHtml.getIndexHTML(req, res), |
44 | VideoModel.load(videoId) | 45 | VideoModel.loadWithBlacklist(videoId) |
45 | ]) | 46 | ]) |
46 | 47 | ||
47 | // Let Angular application handle errors | 48 | // Let Angular application handle errors |
48 | if (!video || video.privacy === VideoPrivacy.PRIVATE) { | 49 | if (!video || video.privacy === VideoPrivacy.PRIVATE || video.VideoBlacklist) { |
49 | return ClientHtml.getIndexHTML(req, res) | 50 | return ClientHtml.getIndexHTML(req, res) |
50 | } | 51 | } |
51 | 52 | ||
@@ -65,7 +66,7 @@ export class ClientHtml { | |||
65 | } | 66 | } |
66 | 67 | ||
67 | private static async getAccountOrChannelHTMLPage ( | 68 | private static async getAccountOrChannelHTMLPage ( |
68 | loader: () => Bluebird<AccountModel | VideoChannelModel>, | 69 | loader: () => Bluebird<MAccountActor | MChannelActor>, |
69 | req: express.Request, | 70 | req: express.Request, |
70 | res: express.Response | 71 | res: express.Response |
71 | ) { | 72 | ) { |
@@ -157,7 +158,7 @@ export class ClientHtml { | |||
157 | return htmlStringPage.replace('</head>', linkTag + '</head>') | 158 | return htmlStringPage.replace('</head>', linkTag + '</head>') |
158 | } | 159 | } |
159 | 160 | ||
160 | private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { | 161 | private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: MVideo) { |
161 | const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() | 162 | const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath() |
162 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 163 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
163 | 164 | ||
@@ -236,7 +237,7 @@ export class ClientHtml { | |||
236 | return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) | 237 | return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString) |
237 | } | 238 | } |
238 | 239 | ||
239 | private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) { | 240 | private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) { |
240 | // SEO, use origin account or channel URL | 241 | // SEO, use origin account or channel URL |
241 | const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />` | 242 | const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />` |
242 | 243 | ||
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 10e7d0479..bd3d4f252 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -2,17 +2,20 @@ import { createTransport, Transporter } from 'nodemailer' | |||
2 | import { isTestInstance } from '../helpers/core-utils' | 2 | import { isTestInstance } from '../helpers/core-utils' |
3 | import { bunyanLogger, logger } from '../helpers/logger' | 3 | import { bunyanLogger, logger } from '../helpers/logger' |
4 | import { CONFIG } from '../initializers/config' | 4 | import { CONFIG } from '../initializers/config' |
5 | import { UserModel } from '../models/account/user' | ||
6 | import { VideoModel } from '../models/video/video' | ||
7 | import { JobQueue } from './job-queue' | 5 | import { JobQueue } from './job-queue' |
8 | import { EmailPayload } from './job-queue/handlers/email' | 6 | import { EmailPayload } from './job-queue/handlers/email' |
9 | import { readFileSync } from 'fs-extra' | 7 | import { readFileSync } from 'fs-extra' |
10 | import { VideoCommentModel } from '../models/video/video-comment' | ||
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | ||
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | ||
13 | import { VideoImportModel } from '../models/video/video-import' | ||
14 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
15 | import { WEBSERVER } from '../initializers/constants' | 8 | import { WEBSERVER } from '../initializers/constants' |
9 | import { | ||
10 | MCommentOwnerVideo, | ||
11 | MVideo, | ||
12 | MVideoAbuseVideo, | ||
13 | MVideoAccountLight, | ||
14 | MVideoBlacklistLightVideo, | ||
15 | MVideoBlacklistVideo | ||
16 | } from '../typings/models/video' | ||
17 | import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models' | ||
18 | import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import' | ||
16 | 19 | ||
17 | type SendEmailOptions = { | 20 | type SendEmailOptions = { |
18 | to: string[] | 21 | to: string[] |
@@ -90,7 +93,7 @@ class Emailer { | |||
90 | } | 93 | } |
91 | } | 94 | } |
92 | 95 | ||
93 | addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) { | 96 | addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) { |
94 | const channelName = video.VideoChannel.getDisplayName() | 97 | const channelName = video.VideoChannel.getDisplayName() |
95 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 98 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
96 | 99 | ||
@@ -111,7 +114,7 @@ class Emailer { | |||
111 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 114 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
112 | } | 115 | } |
113 | 116 | ||
114 | addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { | 117 | addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') { |
115 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() | 118 | const followerName = actorFollow.ActorFollower.Account.getDisplayName() |
116 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() | 119 | const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() |
117 | 120 | ||
@@ -130,7 +133,7 @@ class Emailer { | |||
130 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 133 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
131 | } | 134 | } |
132 | 135 | ||
133 | addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) { | 136 | addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) { |
134 | const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' | 137 | const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' |
135 | 138 | ||
136 | const text = `Hi dear admin,\n\n` + | 139 | const text = `Hi dear admin,\n\n` + |
@@ -148,7 +151,23 @@ class Emailer { | |||
148 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 151 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
149 | } | 152 | } |
150 | 153 | ||
151 | myVideoPublishedNotification (to: string[], video: VideoModel) { | 154 | addAutoInstanceFollowingNotification (to: string[], actorFollow: MActorFollowActors) { |
155 | const text = `Hi dear admin,\n\n` + | ||
156 | `Your instance automatically followed a new instance: ${actorFollow.ActorFollowing.url}` + | ||
157 | `\n\n` + | ||
158 | `Cheers,\n` + | ||
159 | `${CONFIG.EMAIL.BODY.SIGNATURE}` | ||
160 | |||
161 | const emailPayload: EmailPayload = { | ||
162 | to, | ||
163 | subject: CONFIG.EMAIL.SUBJECT.PREFIX + 'Auto instance following', | ||
164 | text | ||
165 | } | ||
166 | |||
167 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | ||
168 | } | ||
169 | |||
170 | myVideoPublishedNotification (to: string[], video: MVideo) { | ||
152 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 171 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
153 | 172 | ||
154 | const text = `Hi dear user,\n\n` + | 173 | const text = `Hi dear user,\n\n` + |
@@ -168,7 +187,7 @@ class Emailer { | |||
168 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 187 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
169 | } | 188 | } |
170 | 189 | ||
171 | myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { | 190 | myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) { |
172 | const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() | 191 | const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() |
173 | 192 | ||
174 | const text = `Hi dear user,\n\n` + | 193 | const text = `Hi dear user,\n\n` + |
@@ -188,7 +207,7 @@ class Emailer { | |||
188 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 207 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
189 | } | 208 | } |
190 | 209 | ||
191 | myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { | 210 | myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) { |
192 | const importUrl = WEBSERVER.URL + '/my-account/video-imports' | 211 | const importUrl = WEBSERVER.URL + '/my-account/video-imports' |
193 | 212 | ||
194 | const text = `Hi dear user,\n\n` + | 213 | const text = `Hi dear user,\n\n` + |
@@ -208,7 +227,7 @@ class Emailer { | |||
208 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 227 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
209 | } | 228 | } |
210 | 229 | ||
211 | addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { | 230 | addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) { |
212 | const accountName = comment.Account.getDisplayName() | 231 | const accountName = comment.Account.getDisplayName() |
213 | const video = comment.Video | 232 | const video = comment.Video |
214 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | 233 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() |
@@ -230,7 +249,7 @@ class Emailer { | |||
230 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 249 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
231 | } | 250 | } |
232 | 251 | ||
233 | addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { | 252 | addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) { |
234 | const accountName = comment.Account.getDisplayName() | 253 | const accountName = comment.Account.getDisplayName() |
235 | const video = comment.Video | 254 | const video = comment.Video |
236 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() | 255 | const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() |
@@ -252,7 +271,7 @@ class Emailer { | |||
252 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 271 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
253 | } | 272 | } |
254 | 273 | ||
255 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { | 274 | addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) { |
256 | const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() | 275 | const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() |
257 | 276 | ||
258 | const text = `Hi,\n\n` + | 277 | const text = `Hi,\n\n` + |
@@ -269,9 +288,9 @@ class Emailer { | |||
269 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 288 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
270 | } | 289 | } |
271 | 290 | ||
272 | addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) { | 291 | addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) { |
273 | const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' | 292 | const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' |
274 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 293 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
275 | 294 | ||
276 | const text = `Hi,\n\n` + | 295 | const text = `Hi,\n\n` + |
277 | `A recently added video was auto-blacklisted and requires moderator review before publishing.` + | 296 | `A recently added video was auto-blacklisted and requires moderator review before publishing.` + |
@@ -292,7 +311,7 @@ class Emailer { | |||
292 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 311 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
293 | } | 312 | } |
294 | 313 | ||
295 | addNewUserRegistrationNotification (to: string[], user: UserModel) { | 314 | addNewUserRegistrationNotification (to: string[], user: MUser) { |
296 | const text = `Hi,\n\n` + | 315 | const text = `Hi,\n\n` + |
297 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + | 316 | `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + |
298 | `Cheers,\n` + | 317 | `Cheers,\n` + |
@@ -307,7 +326,7 @@ class Emailer { | |||
307 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 326 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
308 | } | 327 | } |
309 | 328 | ||
310 | addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { | 329 | addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) { |
311 | const videoName = videoBlacklist.Video.name | 330 | const videoName = videoBlacklist.Video.name |
312 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() | 331 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
313 | 332 | ||
@@ -329,7 +348,7 @@ class Emailer { | |||
329 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 348 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
330 | } | 349 | } |
331 | 350 | ||
332 | addVideoUnblacklistNotification (to: string[], video: VideoModel) { | 351 | addVideoUnblacklistNotification (to: string[], video: MVideo) { |
333 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() | 352 | const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() |
334 | 353 | ||
335 | const text = 'Hi,\n\n' + | 354 | const text = 'Hi,\n\n' + |
@@ -381,7 +400,7 @@ class Emailer { | |||
381 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) | 400 | return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) |
382 | } | 401 | } |
383 | 402 | ||
384 | addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) { | 403 | addUserBlockJob (user: MUser, blocked: boolean, reason?: string) { |
385 | const reasonString = reason ? ` for the following reason: ${reason}` : '' | 404 | const reasonString = reason ? ` for the following reason: ${reason}` : '' |
386 | const blockedWord = blocked ? 'blocked' : 'unblocked' | 405 | const blockedWord = blocked ? 'blocked' : 'unblocked' |
387 | const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.` | 406 | const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.` |
diff --git a/server/lib/hls.ts b/server/lib/hls.ts index 98da4dcd8..05136c21c 100644 --- a/server/lib/hls.ts +++ b/server/lib/hls.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | import { VideoModel } from '../models/video/video' | ||
2 | import { basename, dirname, join } from 'path' | 1 | import { basename, dirname, join } from 'path' |
3 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' | 2 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' |
4 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' | 3 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' |
@@ -12,6 +11,7 @@ import { flatten, uniq } from 'lodash' | |||
12 | import { VideoFileModel } from '../models/video/video-file' | 11 | import { VideoFileModel } from '../models/video/video-file' |
13 | import { CONFIG } from '../initializers/config' | 12 | import { CONFIG } from '../initializers/config' |
14 | import { sequelizeTypescript } from '../initializers/database' | 13 | import { sequelizeTypescript } from '../initializers/database' |
14 | import { MVideoWithFile } from '@server/typings/models' | ||
15 | 15 | ||
16 | async function updateStreamingPlaylistsInfohashesIfNeeded () { | 16 | async function updateStreamingPlaylistsInfohashesIfNeeded () { |
17 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() | 17 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() |
@@ -28,7 +28,7 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () { | |||
28 | } | 28 | } |
29 | } | 29 | } |
30 | 30 | ||
31 | async function updateMasterHLSPlaylist (video: VideoModel) { | 31 | async function updateMasterHLSPlaylist (video: MVideoWithFile) { |
32 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 32 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
33 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] | 33 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] |
34 | const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) | 34 | const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) |
@@ -55,7 +55,7 @@ async function updateMasterHLSPlaylist (video: VideoModel) { | |||
55 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') | 55 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') |
56 | } | 56 | } |
57 | 57 | ||
58 | async function updateSha256Segments (video: VideoModel) { | 58 | async function updateSha256Segments (video: MVideoWithFile) { |
59 | const json: { [filename: string]: { [range: string]: string } } = {} | 59 | const json: { [filename: string]: { [range: string]: string } } = {} |
60 | 60 | ||
61 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 61 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts index 4ae66cd01..af7c8a838 100644 --- a/server/lib/job-queue/handlers/activitypub-follow.ts +++ b/server/lib/job-queue/handlers/activitypub-follow.ts | |||
@@ -10,11 +10,13 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | |||
10 | import { ActorModel } from '../../../models/activitypub/actor' | 10 | import { ActorModel } from '../../../models/activitypub/actor' |
11 | import { Notifier } from '../../notifier' | 11 | import { Notifier } from '../../notifier' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
13 | import { MActor, MActorFollowActors, MActorFull } from '../../../typings/models' | ||
13 | 14 | ||
14 | export type ActivitypubFollowPayload = { | 15 | export type ActivitypubFollowPayload = { |
15 | followerActorId: number | 16 | followerActorId: number |
16 | name: string | 17 | name: string |
17 | host: string | 18 | host: string |
19 | isAutoFollow?: boolean | ||
18 | } | 20 | } |
19 | 21 | ||
20 | async function processActivityPubFollow (job: Bull.Job) { | 22 | async function processActivityPubFollow (job: Bull.Job) { |
@@ -23,18 +25,18 @@ async function processActivityPubFollow (job: Bull.Job) { | |||
23 | 25 | ||
24 | logger.info('Processing ActivityPub follow in job %d.', job.id) | 26 | logger.info('Processing ActivityPub follow in job %d.', job.id) |
25 | 27 | ||
26 | let targetActor: ActorModel | 28 | let targetActor: MActorFull |
27 | if (!host || host === WEBSERVER.HOST) { | 29 | if (!host || host === WEBSERVER.HOST) { |
28 | targetActor = await ActorModel.loadLocalByName(payload.name) | 30 | targetActor = await ActorModel.loadLocalByName(payload.name) |
29 | } else { | 31 | } else { |
30 | const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) | 32 | const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) |
31 | const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) | 33 | const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) |
32 | targetActor = await getOrCreateActorAndServerAndModel(actorUrl) | 34 | targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all') |
33 | } | 35 | } |
34 | 36 | ||
35 | const fromActor = await ActorModel.load(payload.followerActorId) | 37 | const fromActor = await ActorModel.load(payload.followerActorId) |
36 | 38 | ||
37 | return retryTransactionWrapper(follow, fromActor, targetActor) | 39 | return retryTransactionWrapper(follow, fromActor, targetActor, payload.isAutoFollow) |
38 | } | 40 | } |
39 | // --------------------------------------------------------------------------- | 41 | // --------------------------------------------------------------------------- |
40 | 42 | ||
@@ -44,7 +46,7 @@ export { | |||
44 | 46 | ||
45 | // --------------------------------------------------------------------------- | 47 | // --------------------------------------------------------------------------- |
46 | 48 | ||
47 | async function follow (fromActor: ActorModel, targetActor: ActorModel) { | 49 | async function follow (fromActor: MActor, targetActor: MActorFull, isAutoFollow = false) { |
48 | if (fromActor.id === targetActor.id) { | 50 | if (fromActor.id === targetActor.id) { |
49 | throw new Error('Follower is the same than target actor.') | 51 | throw new Error('Follower is the same than target actor.') |
50 | } | 52 | } |
@@ -53,7 +55,7 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) { | |||
53 | const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' | 55 | const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' |
54 | 56 | ||
55 | const actorFollow = await sequelizeTypescript.transaction(async t => { | 57 | const actorFollow = await sequelizeTypescript.transaction(async t => { |
56 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ | 58 | const [ actorFollow ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({ |
57 | where: { | 59 | where: { |
58 | actorId: fromActor.id, | 60 | actorId: fromActor.id, |
59 | targetActorId: targetActor.id | 61 | targetActorId: targetActor.id |
@@ -74,5 +76,15 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) { | |||
74 | return actorFollow | 76 | return actorFollow |
75 | }) | 77 | }) |
76 | 78 | ||
77 | if (actorFollow.state === 'accepted') Notifier.Instance.notifyOfNewUserFollow(actorFollow) | 79 | const followerFull = await ActorModel.loadFull(fromActor.id) |
80 | |||
81 | const actorFollowFull = Object.assign(actorFollow, { | ||
82 | ActorFollowing: targetActor, | ||
83 | ActorFollower: followerFull | ||
84 | }) | ||
85 | |||
86 | if (actorFollow.state === 'accepted') Notifier.Instance.notifyOfNewUserFollow(actorFollowFull) | ||
87 | if (isAutoFollow === true) Notifier.Instance.notifyOfAutoInstanceFollowing(actorFollowFull) | ||
88 | |||
89 | return actorFollow | ||
78 | } | 90 | } |
diff --git a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts index c3f59dc77..0182c5169 100644 --- a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts +++ b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts | |||
@@ -11,6 +11,7 @@ import { AccountModel } from '../../../models/account/account' | |||
11 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 11 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
12 | import { VideoShareModel } from '../../../models/video/video-share' | 12 | import { VideoShareModel } from '../../../models/video/video-share' |
13 | import { VideoCommentModel } from '../../../models/video/video-comment' | 13 | import { VideoCommentModel } from '../../../models/video/video-comment' |
14 | import { MAccountDefault, MVideoFullLight } from '../../../typings/models' | ||
14 | 15 | ||
15 | type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' | 16 | type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' |
16 | 17 | ||
@@ -26,10 +27,10 @@ async function processActivityPubHttpFetcher (job: Bull.Job) { | |||
26 | 27 | ||
27 | const payload = job.data as ActivitypubHttpFetcherPayload | 28 | const payload = job.data as ActivitypubHttpFetcherPayload |
28 | 29 | ||
29 | let video: VideoModel | 30 | let video: MVideoFullLight |
30 | if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) | 31 | if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId) |
31 | 32 | ||
32 | let account: AccountModel | 33 | let account: MAccountDefault |
33 | if (payload.accountId) account = await AccountModel.load(payload.accountId) | 34 | if (payload.accountId) account = await AccountModel.load(payload.accountId) |
34 | 35 | ||
35 | const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { | 36 | const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = { |
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts index cdee1f6fd..d3bde6e6a 100644 --- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts +++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts | |||
@@ -3,6 +3,7 @@ import { getServerActor } from '../../../../helpers/utils' | |||
3 | import { ActorModel } from '../../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../../models/activitypub/actor' |
4 | import { sha256 } from '../../../../helpers/core-utils' | 4 | import { sha256 } from '../../../../helpers/core-utils' |
5 | import { HTTP_SIGNATURE } from '../../../../initializers/constants' | 5 | import { HTTP_SIGNATURE } from '../../../../initializers/constants' |
6 | import { MActor } from '../../../../typings/models' | ||
6 | 7 | ||
7 | type Payload = { body: any, signatureActorId?: number } | 8 | type Payload = { body: any, signatureActorId?: number } |
8 | 9 | ||
@@ -19,7 +20,8 @@ async function computeBody (payload: Payload) { | |||
19 | } | 20 | } |
20 | 21 | ||
21 | async function buildSignedRequestOptions (payload: Payload) { | 22 | async function buildSignedRequestOptions (payload: Payload) { |
22 | let actor: ActorModel | null | 23 | let actor: MActor | null |
24 | |||
23 | if (payload.signatureActorId) { | 25 | if (payload.signatureActorId) { |
24 | actor = await ActorModel.load(payload.signatureActorId) | 26 | actor = await ActorModel.load(payload.signatureActorId) |
25 | if (!actor) throw new Error('Unknown signature actor id.') | 27 | if (!actor) throw new Error('Unknown signature actor id.') |
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 8cacb0ef3..5c5b7dccb 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -6,6 +6,7 @@ import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg | |||
6 | import { copy, stat } from 'fs-extra' | 6 | import { copy, stat } from 'fs-extra' |
7 | import { VideoFileModel } from '../../../models/video/video-file' | 7 | import { VideoFileModel } from '../../../models/video/video-file' |
8 | import { extname } from 'path' | 8 | import { extname } from 'path' |
9 | import { MVideoFile, MVideoWithFile } from '@server/typings/models' | ||
9 | 10 | ||
10 | export type VideoFileImportPayload = { | 11 | export type VideoFileImportPayload = { |
11 | videoUUID: string, | 12 | videoUUID: string, |
@@ -37,7 +38,7 @@ export { | |||
37 | 38 | ||
38 | // --------------------------------------------------------------------------- | 39 | // --------------------------------------------------------------------------- |
39 | 40 | ||
40 | async function updateVideoFile (video: VideoModel, inputFilePath: string) { | 41 | async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) { |
41 | const { videoFileResolution } = await getVideoFileResolution(inputFilePath) | 42 | const { videoFileResolution } = await getVideoFileResolution(inputFilePath) |
42 | const { size } = await stat(inputFilePath) | 43 | const { size } = await stat(inputFilePath) |
43 | const fps = await getVideoFileFPS(inputFilePath) | 44 | const fps = await getVideoFileFPS(inputFilePath) |
@@ -48,7 +49,7 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) { | |||
48 | size, | 49 | size, |
49 | fps, | 50 | fps, |
50 | videoId: video.id | 51 | videoId: video.id |
51 | }) | 52 | }) as MVideoFile |
52 | 53 | ||
53 | const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution) | 54 | const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution) |
54 | 55 | ||
@@ -60,9 +61,9 @@ async function updateVideoFile (video: VideoModel, inputFilePath: string) { | |||
60 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) | 61 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) |
61 | 62 | ||
62 | // Update the database | 63 | // Update the database |
63 | currentVideoFile.set('extname', updatedVideoFile.extname) | 64 | currentVideoFile.extname = updatedVideoFile.extname |
64 | currentVideoFile.set('size', updatedVideoFile.size) | 65 | currentVideoFile.size = updatedVideoFile.size |
65 | currentVideoFile.set('fps', updatedVideoFile.fps) | 66 | currentVideoFile.fps = updatedVideoFile.fps |
66 | 67 | ||
67 | updatedVideoFile = currentVideoFile | 68 | updatedVideoFile = currentVideoFile |
68 | } | 69 | } |
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index 13b741180..93a3e9d90 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -17,9 +17,11 @@ import { move, remove, stat } from 'fs-extra' | |||
17 | import { Notifier } from '../../notifier' | 17 | import { Notifier } from '../../notifier' |
18 | import { CONFIG } from '../../../initializers/config' | 18 | import { CONFIG } from '../../../initializers/config' |
19 | import { sequelizeTypescript } from '../../../initializers/database' | 19 | import { sequelizeTypescript } from '../../../initializers/database' |
20 | import { ThumbnailModel } from '../../../models/video/thumbnail' | ||
21 | import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' | 20 | import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' |
22 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
22 | import { MThumbnail } from '../../../typings/models/video/thumbnail' | ||
23 | import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import' | ||
24 | import { MVideoBlacklistVideo, MVideoBlacklist } from '@server/typings/models' | ||
23 | 25 | ||
24 | type VideoImportYoutubeDLPayload = { | 26 | type VideoImportYoutubeDLPayload = { |
25 | type: 'youtube-dl' | 27 | type: 'youtube-dl' |
@@ -110,7 +112,7 @@ type ProcessFileOptions = { | |||
110 | generateThumbnail: boolean | 112 | generateThumbnail: boolean |
111 | generatePreview: boolean | 113 | generatePreview: boolean |
112 | } | 114 | } |
113 | async function processFile (downloader: () => Promise<string>, videoImport: VideoImportModel, options: ProcessFileOptions) { | 115 | async function processFile (downloader: () => Promise<string>, videoImport: MVideoImportDefault, options: ProcessFileOptions) { |
114 | let tempVideoPath: string | 116 | let tempVideoPath: string |
115 | let videoDestFile: string | 117 | let videoDestFile: string |
116 | let videoFile: VideoFileModel | 118 | let videoFile: VideoFileModel |
@@ -139,41 +141,44 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
139 | videoId: videoImport.videoId | 141 | videoId: videoImport.videoId |
140 | } | 142 | } |
141 | videoFile = new VideoFileModel(videoFileData) | 143 | videoFile = new VideoFileModel(videoFileData) |
144 | |||
145 | const videoWithFiles = Object.assign(videoImport.Video, { VideoFiles: [ videoFile ] }) | ||
142 | // To clean files if the import fails | 146 | // To clean files if the import fails |
143 | videoImport.Video.VideoFiles = [ videoFile ] | 147 | const videoImportWithFiles: MVideoImportDefaultFiles = Object.assign(videoImport, { Video: videoWithFiles }) |
144 | 148 | ||
145 | // Move file | 149 | // Move file |
146 | videoDestFile = join(CONFIG.STORAGE.VIDEOS_DIR, videoImport.Video.getVideoFilename(videoFile)) | 150 | videoDestFile = join(CONFIG.STORAGE.VIDEOS_DIR, videoImportWithFiles.Video.getVideoFilename(videoFile)) |
147 | await move(tempVideoPath, videoDestFile) | 151 | await move(tempVideoPath, videoDestFile) |
148 | tempVideoPath = null // This path is not used anymore | 152 | tempVideoPath = null // This path is not used anymore |
149 | 153 | ||
150 | // Process thumbnail | 154 | // Process thumbnail |
151 | let thumbnailModel: ThumbnailModel | 155 | let thumbnailModel: MThumbnail |
152 | if (options.downloadThumbnail && options.thumbnailUrl) { | 156 | if (options.downloadThumbnail && options.thumbnailUrl) { |
153 | thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE) | 157 | thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.MINIATURE) |
154 | } else if (options.generateThumbnail || options.downloadThumbnail) { | 158 | } else if (options.generateThumbnail || options.downloadThumbnail) { |
155 | thumbnailModel = await generateVideoMiniature(videoImport.Video, videoFile, ThumbnailType.MINIATURE) | 159 | thumbnailModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.MINIATURE) |
156 | } | 160 | } |
157 | 161 | ||
158 | // Process preview | 162 | // Process preview |
159 | let previewModel: ThumbnailModel | 163 | let previewModel: MThumbnail |
160 | if (options.downloadPreview && options.thumbnailUrl) { | 164 | if (options.downloadPreview && options.thumbnailUrl) { |
161 | previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW) | 165 | previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.PREVIEW) |
162 | } else if (options.generatePreview || options.downloadPreview) { | 166 | } else if (options.generatePreview || options.downloadPreview) { |
163 | previewModel = await generateVideoMiniature(videoImport.Video, videoFile, ThumbnailType.PREVIEW) | 167 | previewModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.PREVIEW) |
164 | } | 168 | } |
165 | 169 | ||
166 | // Create torrent | 170 | // Create torrent |
167 | await videoImport.Video.createTorrentAndSetInfoHash(videoFile) | 171 | await videoImportWithFiles.Video.createTorrentAndSetInfoHash(videoFile) |
172 | |||
173 | const { videoImportUpdated, video } = await sequelizeTypescript.transaction(async t => { | ||
174 | const videoImportToUpdate = videoImportWithFiles as MVideoImportVideo | ||
168 | 175 | ||
169 | const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => { | ||
170 | // Refresh video | 176 | // Refresh video |
171 | const video = await VideoModel.load(videoImport.videoId, t) | 177 | const video = await VideoModel.load(videoImportToUpdate.videoId, t) |
172 | if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.') | 178 | if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.') |
173 | videoImport.Video = video | ||
174 | 179 | ||
175 | const videoFileCreated = await videoFile.save({ transaction: t }) | 180 | const videoFileCreated = await videoFile.save({ transaction: t }) |
176 | video.VideoFiles = [ videoFileCreated ] | 181 | videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] }) |
177 | 182 | ||
178 | // Update video DB object | 183 | // Update video DB object |
179 | video.duration = duration | 184 | video.duration = duration |
@@ -188,25 +193,27 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide | |||
188 | await federateVideoIfNeeded(videoForFederation, true, t) | 193 | await federateVideoIfNeeded(videoForFederation, true, t) |
189 | 194 | ||
190 | // Update video import object | 195 | // Update video import object |
191 | videoImport.state = VideoImportState.SUCCESS | 196 | videoImportToUpdate.state = VideoImportState.SUCCESS |
192 | const videoImportUpdated = await videoImport.save({ transaction: t }) | 197 | const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo |
198 | videoImportUpdated.Video = video | ||
193 | 199 | ||
194 | logger.info('Video %s imported.', video.uuid) | 200 | logger.info('Video %s imported.', video.uuid) |
195 | 201 | ||
196 | videoImportUpdated.Video = videoForFederation | 202 | return { videoImportUpdated, video: videoForFederation } |
197 | return videoImportUpdated | ||
198 | }) | 203 | }) |
199 | 204 | ||
200 | Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) | 205 | Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) |
201 | 206 | ||
202 | if (videoImportUpdated.Video.isBlacklisted()) { | 207 | if (video.isBlacklisted()) { |
203 | Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video) | 208 | const videoBlacklist = Object.assign(video.VideoBlacklist, { Video: video }) |
209 | |||
210 | Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist) | ||
204 | } else { | 211 | } else { |
205 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video) | 212 | Notifier.Instance.notifyOnNewVideoIfNeeded(video) |
206 | } | 213 | } |
207 | 214 | ||
208 | // Create transcoding jobs? | 215 | // Create transcoding jobs? |
209 | if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { | 216 | if (video.state === VideoState.TO_TRANSCODE) { |
210 | // Put uuid because we don't have id auto incremented for now | 217 | // Put uuid because we don't have id auto incremented for now |
211 | const dataInput = { | 218 | const dataInput = { |
212 | type: 'optimize' as 'optimize', | 219 | type: 'optimize' as 'optimize', |
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 981daf9a1..2ebe15bcb 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -11,6 +11,7 @@ import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' | |||
11 | import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' | 11 | import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' |
12 | import { Notifier } from '../../notifier' | 12 | import { Notifier } from '../../notifier' |
13 | import { CONFIG } from '../../../initializers/config' | 13 | import { CONFIG } from '../../../initializers/config' |
14 | import { MVideoUUID, MVideoWithFile } from '@server/typings/models' | ||
14 | 15 | ||
15 | interface BaseTranscodingPayload { | 16 | interface BaseTranscodingPayload { |
16 | videoUUID: string | 17 | videoUUID: string |
@@ -73,7 +74,7 @@ async function processVideoTranscoding (job: Bull.Job) { | |||
73 | return video | 74 | return video |
74 | } | 75 | } |
75 | 76 | ||
76 | async function onHlsPlaylistGenerationSuccess (video: VideoModel) { | 77 | async function onHlsPlaylistGenerationSuccess (video: MVideoUUID) { |
77 | if (video === undefined) return undefined | 78 | if (video === undefined) return undefined |
78 | 79 | ||
79 | await sequelizeTypescript.transaction(async t => { | 80 | await sequelizeTypescript.transaction(async t => { |
@@ -87,7 +88,7 @@ async function onHlsPlaylistGenerationSuccess (video: VideoModel) { | |||
87 | }) | 88 | }) |
88 | } | 89 | } |
89 | 90 | ||
90 | async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { | 91 | async function publishNewResolutionIfNeeded (video: MVideoUUID, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { |
91 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { | 92 | const { videoDatabase, videoPublished } = await sequelizeTypescript.transaction(async t => { |
92 | // Maybe the video changed in database, refresh it | 93 | // Maybe the video changed in database, refresh it |
93 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) | 94 | let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t) |
@@ -119,7 +120,7 @@ async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewRes | |||
119 | await createHlsJobIfEnabled(payload) | 120 | await createHlsJobIfEnabled(payload) |
120 | } | 121 | } |
121 | 122 | ||
122 | async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { | 123 | async function onVideoFileOptimizerSuccess (videoArg: MVideoWithFile, payload: OptimizeTranscodingPayload) { |
123 | if (videoArg === undefined) return undefined | 124 | if (videoArg === undefined) return undefined |
124 | 125 | ||
125 | // Outside the transaction (IO on disk) | 126 | // Outside the transaction (IO on disk) |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index a7dfb0979..b7cc2607d 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -1,20 +1,30 @@ | |||
1 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' | 1 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' |
2 | import { logger } from '../helpers/logger' | 2 | import { logger } from '../helpers/logger' |
3 | import { VideoModel } from '../models/video/video' | ||
4 | import { Emailer } from './emailer' | 3 | import { Emailer } from './emailer' |
5 | import { UserNotificationModel } from '../models/account/user-notification' | 4 | import { UserNotificationModel } from '../models/account/user-notification' |
6 | import { VideoCommentModel } from '../models/video/video-comment' | ||
7 | import { UserModel } from '../models/account/user' | 5 | import { UserModel } from '../models/account/user' |
8 | import { PeerTubeSocket } from './peertube-socket' | 6 | import { PeerTubeSocket } from './peertube-socket' |
9 | import { CONFIG } from '../initializers/config' | 7 | import { CONFIG } from '../initializers/config' |
10 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' | 8 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' |
11 | import { VideoAbuseModel } from '../models/video/video-abuse' | ||
12 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | ||
13 | import * as Bluebird from 'bluebird' | 9 | import * as Bluebird from 'bluebird' |
14 | import { VideoImportModel } from '../models/video/video-import' | ||
15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 10 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
16 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | 11 | import { |
17 | import { AccountModel } from '../models/account/account' | 12 | MCommentOwnerVideo, |
13 | MVideoAbuseVideo, | ||
14 | MVideoAccountLight, | ||
15 | MVideoBlacklistLightVideo, | ||
16 | MVideoBlacklistVideo, | ||
17 | MVideoFullLight | ||
18 | } from '../typings/models/video' | ||
19 | import { | ||
20 | MUser, | ||
21 | MUserDefault, | ||
22 | MUserNotifSettingAccount, | ||
23 | MUserWithNotificationSetting, | ||
24 | UserNotificationModelForApi | ||
25 | } from '@server/typings/models/user' | ||
26 | import { MActorFollowFull } from '../typings/models' | ||
27 | import { MVideoImportVideo } from '@server/typings/models/video/video-import' | ||
18 | 28 | ||
19 | class Notifier { | 29 | class Notifier { |
20 | 30 | ||
@@ -22,7 +32,7 @@ class Notifier { | |||
22 | 32 | ||
23 | private constructor () {} | 33 | private constructor () {} |
24 | 34 | ||
25 | notifyOnNewVideoIfNeeded (video: VideoModel): void { | 35 | notifyOnNewVideoIfNeeded (video: MVideoAccountLight): void { |
26 | // Only notify on public and published videos which are not blacklisted | 36 | // Only notify on public and published videos which are not blacklisted |
27 | if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return | 37 | if (video.privacy !== VideoPrivacy.PUBLIC || video.state !== VideoState.PUBLISHED || video.isBlacklisted()) return |
28 | 38 | ||
@@ -30,7 +40,7 @@ class Notifier { | |||
30 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) | 40 | .catch(err => logger.error('Cannot notify subscribers of new video %s.', video.url, { err })) |
31 | } | 41 | } |
32 | 42 | ||
33 | notifyOnVideoPublishedAfterTranscoding (video: VideoModel): void { | 43 | notifyOnVideoPublishedAfterTranscoding (video: MVideoFullLight): void { |
34 | // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update | 44 | // don't notify if didn't wait for transcoding or video is still blacklisted/waiting for scheduled update |
35 | if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return | 45 | if (!video.waitTranscoding || video.VideoBlacklist || video.ScheduleVideoUpdate) return |
36 | 46 | ||
@@ -38,7 +48,7 @@ class Notifier { | |||
38 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) | 48 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after transcoding.', video.url, { err })) |
39 | } | 49 | } |
40 | 50 | ||
41 | notifyOnVideoPublishedAfterScheduledUpdate (video: VideoModel): void { | 51 | notifyOnVideoPublishedAfterScheduledUpdate (video: MVideoFullLight): void { |
42 | // don't notify if video is still blacklisted or waiting for transcoding | 52 | // don't notify if video is still blacklisted or waiting for transcoding |
43 | if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return | 53 | if (video.VideoBlacklist || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return |
44 | 54 | ||
@@ -46,7 +56,7 @@ class Notifier { | |||
46 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) | 56 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after scheduled update.', video.url, { err })) |
47 | } | 57 | } |
48 | 58 | ||
49 | notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: VideoModel): void { | 59 | notifyOnVideoPublishedAfterRemovedFromAutoBlacklist (video: MVideoFullLight): void { |
50 | // don't notify if video is still waiting for transcoding or scheduled update | 60 | // don't notify if video is still waiting for transcoding or scheduled update |
51 | if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return | 61 | if (video.ScheduleVideoUpdate || (video.waitTranscoding && video.state !== VideoState.PUBLISHED)) return |
52 | 62 | ||
@@ -54,7 +64,7 @@ class Notifier { | |||
54 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length | 64 | .catch(err => logger.error('Cannot notify owner that its video %s has been published after removed from auto-blacklist.', video.url, { err })) // tslint:disable-line:max-line-length |
55 | } | 65 | } |
56 | 66 | ||
57 | notifyOnNewComment (comment: VideoCommentModel): void { | 67 | notifyOnNewComment (comment: MCommentOwnerVideo): void { |
58 | this.notifyVideoOwnerOfNewComment(comment) | 68 | this.notifyVideoOwnerOfNewComment(comment) |
59 | .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) | 69 | .catch(err => logger.error('Cannot notify video owner of new comment %s.', comment.url, { err })) |
60 | 70 | ||
@@ -62,37 +72,37 @@ class Notifier { | |||
62 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) | 72 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) |
63 | } | 73 | } |
64 | 74 | ||
65 | notifyOnNewVideoAbuse (videoAbuse: VideoAbuseModel): void { | 75 | notifyOnNewVideoAbuse (videoAbuse: MVideoAbuseVideo): void { |
66 | this.notifyModeratorsOfNewVideoAbuse(videoAbuse) | 76 | this.notifyModeratorsOfNewVideoAbuse(videoAbuse) |
67 | .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) | 77 | .catch(err => logger.error('Cannot notify of new video abuse of video %s.', videoAbuse.Video.url, { err })) |
68 | } | 78 | } |
69 | 79 | ||
70 | notifyOnVideoAutoBlacklist (video: VideoModel): void { | 80 | notifyOnVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo): void { |
71 | this.notifyModeratorsOfVideoAutoBlacklist(video) | 81 | this.notifyModeratorsOfVideoAutoBlacklist(videoBlacklist) |
72 | .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', video.url, { err })) | 82 | .catch(err => logger.error('Cannot notify of auto-blacklist of video %s.', videoBlacklist.Video.url, { err })) |
73 | } | 83 | } |
74 | 84 | ||
75 | notifyOnVideoBlacklist (videoBlacklist: VideoBlacklistModel): void { | 85 | notifyOnVideoBlacklist (videoBlacklist: MVideoBlacklistVideo): void { |
76 | this.notifyVideoOwnerOfBlacklist(videoBlacklist) | 86 | this.notifyVideoOwnerOfBlacklist(videoBlacklist) |
77 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) | 87 | .catch(err => logger.error('Cannot notify video owner of new video blacklist of %s.', videoBlacklist.Video.url, { err })) |
78 | } | 88 | } |
79 | 89 | ||
80 | notifyOnVideoUnblacklist (video: VideoModel): void { | 90 | notifyOnVideoUnblacklist (video: MVideoFullLight): void { |
81 | this.notifyVideoOwnerOfUnblacklist(video) | 91 | this.notifyVideoOwnerOfUnblacklist(video) |
82 | .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) | 92 | .catch(err => logger.error('Cannot notify video owner of unblacklist of %s.', video.url, { err })) |
83 | } | 93 | } |
84 | 94 | ||
85 | notifyOnFinishedVideoImport (videoImport: VideoImportModel, success: boolean): void { | 95 | notifyOnFinishedVideoImport (videoImport: MVideoImportVideo, success: boolean): void { |
86 | this.notifyOwnerVideoImportIsFinished(videoImport, success) | 96 | this.notifyOwnerVideoImportIsFinished(videoImport, success) |
87 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) | 97 | .catch(err => logger.error('Cannot notify owner that its video import %s is finished.', videoImport.getTargetIdentifier(), { err })) |
88 | } | 98 | } |
89 | 99 | ||
90 | notifyOnNewUserRegistration (user: UserModel): void { | 100 | notifyOnNewUserRegistration (user: MUserDefault): void { |
91 | this.notifyModeratorsOfNewUserRegistration(user) | 101 | this.notifyModeratorsOfNewUserRegistration(user) |
92 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) | 102 | .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) |
93 | } | 103 | } |
94 | 104 | ||
95 | notifyOfNewUserFollow (actorFollow: ActorFollowModel): void { | 105 | notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { |
96 | this.notifyUserOfNewActorFollow(actorFollow) | 106 | this.notifyUserOfNewActorFollow(actorFollow) |
97 | .catch(err => { | 107 | .catch(err => { |
98 | logger.error( | 108 | logger.error( |
@@ -104,25 +114,32 @@ class Notifier { | |||
104 | }) | 114 | }) |
105 | } | 115 | } |
106 | 116 | ||
107 | notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void { | 117 | notifyOfNewInstanceFollow (actorFollow: MActorFollowFull): void { |
108 | this.notifyAdminsOfNewInstanceFollow(actorFollow) | 118 | this.notifyAdminsOfNewInstanceFollow(actorFollow) |
109 | .catch(err => { | 119 | .catch(err => { |
110 | logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) | 120 | logger.error('Cannot notify administrators of new follower %s.', actorFollow.ActorFollower.url, { err }) |
111 | }) | 121 | }) |
112 | } | 122 | } |
113 | 123 | ||
114 | private async notifySubscribersOfNewVideo (video: VideoModel) { | 124 | notifyOfAutoInstanceFollowing (actorFollow: MActorFollowFull): void { |
125 | this.notifyAdminsOfAutoInstanceFollowing(actorFollow) | ||
126 | .catch(err => { | ||
127 | logger.error('Cannot notify administrators of auto instance following %s.', actorFollow.ActorFollowing.url, { err }) | ||
128 | }) | ||
129 | } | ||
130 | |||
131 | private async notifySubscribersOfNewVideo (video: MVideoAccountLight) { | ||
115 | // List all followers that are users | 132 | // List all followers that are users |
116 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) | 133 | const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) |
117 | 134 | ||
118 | logger.info('Notifying %d users of new video %s.', users.length, video.url) | 135 | logger.info('Notifying %d users of new video %s.', users.length, video.url) |
119 | 136 | ||
120 | function settingGetter (user: UserModel) { | 137 | function settingGetter (user: MUserWithNotificationSetting) { |
121 | return user.NotificationSetting.newVideoFromSubscription | 138 | return user.NotificationSetting.newVideoFromSubscription |
122 | } | 139 | } |
123 | 140 | ||
124 | async function notificationCreator (user: UserModel) { | 141 | async function notificationCreator (user: MUserWithNotificationSetting) { |
125 | const notification = await UserNotificationModel.create({ | 142 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
126 | type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, | 143 | type: UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION, |
127 | userId: user.id, | 144 | userId: user.id, |
128 | videoId: video.id | 145 | videoId: video.id |
@@ -139,7 +156,7 @@ class Notifier { | |||
139 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | 156 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) |
140 | } | 157 | } |
141 | 158 | ||
142 | private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { | 159 | private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) { |
143 | if (comment.Video.isOwned() === false) return | 160 | if (comment.Video.isOwned() === false) return |
144 | 161 | ||
145 | const user = await UserModel.loadByVideoId(comment.videoId) | 162 | const user = await UserModel.loadByVideoId(comment.videoId) |
@@ -152,12 +169,12 @@ class Notifier { | |||
152 | 169 | ||
153 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) | 170 | logger.info('Notifying user %s of new comment %s.', user.username, comment.url) |
154 | 171 | ||
155 | function settingGetter (user: UserModel) { | 172 | function settingGetter (user: MUserWithNotificationSetting) { |
156 | return user.NotificationSetting.newCommentOnMyVideo | 173 | return user.NotificationSetting.newCommentOnMyVideo |
157 | } | 174 | } |
158 | 175 | ||
159 | async function notificationCreator (user: UserModel) { | 176 | async function notificationCreator (user: MUserWithNotificationSetting) { |
160 | const notification = await UserNotificationModel.create({ | 177 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
161 | type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, | 178 | type: UserNotificationType.NEW_COMMENT_ON_MY_VIDEO, |
162 | userId: user.id, | 179 | userId: user.id, |
163 | commentId: comment.id | 180 | commentId: comment.id |
@@ -174,7 +191,7 @@ class Notifier { | |||
174 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 191 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
175 | } | 192 | } |
176 | 193 | ||
177 | private async notifyOfCommentMention (comment: VideoCommentModel) { | 194 | private async notifyOfCommentMention (comment: MCommentOwnerVideo) { |
178 | const extractedUsernames = comment.extractMentions() | 195 | const extractedUsernames = comment.extractMentions() |
179 | logger.debug( | 196 | logger.debug( |
180 | 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, | 197 | 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, |
@@ -197,14 +214,14 @@ class Notifier { | |||
197 | 214 | ||
198 | logger.info('Notifying %d users of new comment %s.', users.length, comment.url) | 215 | logger.info('Notifying %d users of new comment %s.', users.length, comment.url) |
199 | 216 | ||
200 | function settingGetter (user: UserModel) { | 217 | function settingGetter (user: MUserNotifSettingAccount) { |
201 | if (accountMutedHash[user.Account.id] === true) return UserNotificationSettingValue.NONE | 218 | if (accountMutedHash[user.Account.id] === true) return UserNotificationSettingValue.NONE |
202 | 219 | ||
203 | return user.NotificationSetting.commentMention | 220 | return user.NotificationSetting.commentMention |
204 | } | 221 | } |
205 | 222 | ||
206 | async function notificationCreator (user: UserModel) { | 223 | async function notificationCreator (user: MUserNotifSettingAccount) { |
207 | const notification = await UserNotificationModel.create({ | 224 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
208 | type: UserNotificationType.COMMENT_MENTION, | 225 | type: UserNotificationType.COMMENT_MENTION, |
209 | userId: user.id, | 226 | userId: user.id, |
210 | commentId: comment.id | 227 | commentId: comment.id |
@@ -221,7 +238,7 @@ class Notifier { | |||
221 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) | 238 | return this.notify({ users, settingGetter, notificationCreator, emailSender }) |
222 | } | 239 | } |
223 | 240 | ||
224 | private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) { | 241 | private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) { |
225 | if (actorFollow.ActorFollowing.isOwned() === false) return | 242 | if (actorFollow.ActorFollowing.isOwned() === false) return |
226 | 243 | ||
227 | // Account follows one of our account? | 244 | // Account follows one of our account? |
@@ -236,9 +253,6 @@ class Notifier { | |||
236 | 253 | ||
237 | if (!user) return | 254 | if (!user) return |
238 | 255 | ||
239 | if (!actorFollow.ActorFollower.Account || !actorFollow.ActorFollower.Account.name) { | ||
240 | actorFollow.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as AccountModel | ||
241 | } | ||
242 | const followerAccount = actorFollow.ActorFollower.Account | 256 | const followerAccount = actorFollow.ActorFollower.Account |
243 | 257 | ||
244 | const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) | 258 | const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) |
@@ -246,12 +260,12 @@ class Notifier { | |||
246 | 260 | ||
247 | logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName()) | 261 | logger.info('Notifying user %s of new follower: %s.', user.username, followerAccount.getDisplayName()) |
248 | 262 | ||
249 | function settingGetter (user: UserModel) { | 263 | function settingGetter (user: MUserWithNotificationSetting) { |
250 | return user.NotificationSetting.newFollow | 264 | return user.NotificationSetting.newFollow |
251 | } | 265 | } |
252 | 266 | ||
253 | async function notificationCreator (user: UserModel) { | 267 | async function notificationCreator (user: MUserWithNotificationSetting) { |
254 | const notification = await UserNotificationModel.create({ | 268 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
255 | type: UserNotificationType.NEW_FOLLOW, | 269 | type: UserNotificationType.NEW_FOLLOW, |
256 | userId: user.id, | 270 | userId: user.id, |
257 | actorFollowId: actorFollow.id | 271 | actorFollowId: actorFollow.id |
@@ -268,17 +282,17 @@ class Notifier { | |||
268 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 282 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
269 | } | 283 | } |
270 | 284 | ||
271 | private async notifyAdminsOfNewInstanceFollow (actorFollow: ActorFollowModel) { | 285 | private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowFull) { |
272 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | 286 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) |
273 | 287 | ||
274 | logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) | 288 | logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) |
275 | 289 | ||
276 | function settingGetter (user: UserModel) { | 290 | function settingGetter (user: MUserWithNotificationSetting) { |
277 | return user.NotificationSetting.newInstanceFollower | 291 | return user.NotificationSetting.newInstanceFollower |
278 | } | 292 | } |
279 | 293 | ||
280 | async function notificationCreator (user: UserModel) { | 294 | async function notificationCreator (user: MUserWithNotificationSetting) { |
281 | const notification = await UserNotificationModel.create({ | 295 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
282 | type: UserNotificationType.NEW_INSTANCE_FOLLOWER, | 296 | type: UserNotificationType.NEW_INSTANCE_FOLLOWER, |
283 | userId: user.id, | 297 | userId: user.id, |
284 | actorFollowId: actorFollow.id | 298 | actorFollowId: actorFollow.id |
@@ -295,18 +309,45 @@ class Notifier { | |||
295 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | 309 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) |
296 | } | 310 | } |
297 | 311 | ||
298 | private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { | 312 | private async notifyAdminsOfAutoInstanceFollowing (actorFollow: MActorFollowFull) { |
313 | const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) | ||
314 | |||
315 | logger.info('Notifying %d administrators of auto instance following: %s.', admins.length, actorFollow.ActorFollowing.url) | ||
316 | |||
317 | function settingGetter (user: MUserWithNotificationSetting) { | ||
318 | return user.NotificationSetting.autoInstanceFollowing | ||
319 | } | ||
320 | |||
321 | async function notificationCreator (user: MUserWithNotificationSetting) { | ||
322 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
323 | type: UserNotificationType.AUTO_INSTANCE_FOLLOWING, | ||
324 | userId: user.id, | ||
325 | actorFollowId: actorFollow.id | ||
326 | }) | ||
327 | notification.ActorFollow = actorFollow | ||
328 | |||
329 | return notification | ||
330 | } | ||
331 | |||
332 | function emailSender (emails: string[]) { | ||
333 | return Emailer.Instance.addAutoInstanceFollowingNotification(emails, actorFollow) | ||
334 | } | ||
335 | |||
336 | return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) | ||
337 | } | ||
338 | |||
339 | private async notifyModeratorsOfNewVideoAbuse (videoAbuse: MVideoAbuseVideo) { | ||
299 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) | 340 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) |
300 | if (moderators.length === 0) return | 341 | if (moderators.length === 0) return |
301 | 342 | ||
302 | logger.info('Notifying %s user/moderators of new video abuse %s.', moderators.length, videoAbuse.Video.url) | 343 | logger.info('Notifying %s user/moderators of new video abuse %s.', moderators.length, videoAbuse.Video.url) |
303 | 344 | ||
304 | function settingGetter (user: UserModel) { | 345 | function settingGetter (user: MUserWithNotificationSetting) { |
305 | return user.NotificationSetting.videoAbuseAsModerator | 346 | return user.NotificationSetting.videoAbuseAsModerator |
306 | } | 347 | } |
307 | 348 | ||
308 | async function notificationCreator (user: UserModel) { | 349 | async function notificationCreator (user: MUserWithNotificationSetting) { |
309 | const notification = await UserNotificationModel.create({ | 350 | const notification: UserNotificationModelForApi = await UserNotificationModel.create<UserNotificationModelForApi>({ |
310 | type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, | 351 | type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, |
311 | userId: user.id, | 352 | userId: user.id, |
312 | videoAbuseId: videoAbuse.id | 353 | videoAbuseId: videoAbuse.id |
@@ -323,46 +364,46 @@ class Notifier { | |||
323 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | 364 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
324 | } | 365 | } |
325 | 366 | ||
326 | private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) { | 367 | private async notifyModeratorsOfVideoAutoBlacklist (videoBlacklist: MVideoBlacklistLightVideo) { |
327 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) | 368 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) |
328 | if (moderators.length === 0) return | 369 | if (moderators.length === 0) return |
329 | 370 | ||
330 | logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, video.url) | 371 | logger.info('Notifying %s moderators of video auto-blacklist %s.', moderators.length, videoBlacklist.Video.url) |
331 | 372 | ||
332 | function settingGetter (user: UserModel) { | 373 | function settingGetter (user: MUserWithNotificationSetting) { |
333 | return user.NotificationSetting.videoAutoBlacklistAsModerator | 374 | return user.NotificationSetting.videoAutoBlacklistAsModerator |
334 | } | 375 | } |
335 | async function notificationCreator (user: UserModel) { | ||
336 | 376 | ||
337 | const notification = await UserNotificationModel.create({ | 377 | async function notificationCreator (user: MUserWithNotificationSetting) { |
378 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ | ||
338 | type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, | 379 | type: UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS, |
339 | userId: user.id, | 380 | userId: user.id, |
340 | videoId: video.id | 381 | videoBlacklistId: videoBlacklist.id |
341 | }) | 382 | }) |
342 | notification.Video = video | 383 | notification.VideoBlacklist = videoBlacklist |
343 | 384 | ||
344 | return notification | 385 | return notification |
345 | } | 386 | } |
346 | 387 | ||
347 | function emailSender (emails: string[]) { | 388 | function emailSender (emails: string[]) { |
348 | return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, video) | 389 | return Emailer.Instance.addVideoAutoBlacklistModeratorsNotification(emails, videoBlacklist) |
349 | } | 390 | } |
350 | 391 | ||
351 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | 392 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
352 | } | 393 | } |
353 | 394 | ||
354 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { | 395 | private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) { |
355 | const user = await UserModel.loadByVideoId(videoBlacklist.videoId) | 396 | const user = await UserModel.loadByVideoId(videoBlacklist.videoId) |
356 | if (!user) return | 397 | if (!user) return |
357 | 398 | ||
358 | logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url) | 399 | logger.info('Notifying user %s that its video %s has been blacklisted.', user.username, videoBlacklist.Video.url) |
359 | 400 | ||
360 | function settingGetter (user: UserModel) { | 401 | function settingGetter (user: MUserWithNotificationSetting) { |
361 | return user.NotificationSetting.blacklistOnMyVideo | 402 | return user.NotificationSetting.blacklistOnMyVideo |
362 | } | 403 | } |
363 | 404 | ||
364 | async function notificationCreator (user: UserModel) { | 405 | async function notificationCreator (user: MUserWithNotificationSetting) { |
365 | const notification = await UserNotificationModel.create({ | 406 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
366 | type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, | 407 | type: UserNotificationType.BLACKLIST_ON_MY_VIDEO, |
367 | userId: user.id, | 408 | userId: user.id, |
368 | videoBlacklistId: videoBlacklist.id | 409 | videoBlacklistId: videoBlacklist.id |
@@ -379,18 +420,18 @@ class Notifier { | |||
379 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 420 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
380 | } | 421 | } |
381 | 422 | ||
382 | private async notifyVideoOwnerOfUnblacklist (video: VideoModel) { | 423 | private async notifyVideoOwnerOfUnblacklist (video: MVideoFullLight) { |
383 | const user = await UserModel.loadByVideoId(video.id) | 424 | const user = await UserModel.loadByVideoId(video.id) |
384 | if (!user) return | 425 | if (!user) return |
385 | 426 | ||
386 | logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url) | 427 | logger.info('Notifying user %s that its video %s has been unblacklisted.', user.username, video.url) |
387 | 428 | ||
388 | function settingGetter (user: UserModel) { | 429 | function settingGetter (user: MUserWithNotificationSetting) { |
389 | return user.NotificationSetting.blacklistOnMyVideo | 430 | return user.NotificationSetting.blacklistOnMyVideo |
390 | } | 431 | } |
391 | 432 | ||
392 | async function notificationCreator (user: UserModel) { | 433 | async function notificationCreator (user: MUserWithNotificationSetting) { |
393 | const notification = await UserNotificationModel.create({ | 434 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
394 | type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, | 435 | type: UserNotificationType.UNBLACKLIST_ON_MY_VIDEO, |
395 | userId: user.id, | 436 | userId: user.id, |
396 | videoId: video.id | 437 | videoId: video.id |
@@ -407,18 +448,18 @@ class Notifier { | |||
407 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 448 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
408 | } | 449 | } |
409 | 450 | ||
410 | private async notifyOwnedVideoHasBeenPublished (video: VideoModel) { | 451 | private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) { |
411 | const user = await UserModel.loadByVideoId(video.id) | 452 | const user = await UserModel.loadByVideoId(video.id) |
412 | if (!user) return | 453 | if (!user) return |
413 | 454 | ||
414 | logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url) | 455 | logger.info('Notifying user %s of the publication of its video %s.', user.username, video.url) |
415 | 456 | ||
416 | function settingGetter (user: UserModel) { | 457 | function settingGetter (user: MUserWithNotificationSetting) { |
417 | return user.NotificationSetting.myVideoPublished | 458 | return user.NotificationSetting.myVideoPublished |
418 | } | 459 | } |
419 | 460 | ||
420 | async function notificationCreator (user: UserModel) { | 461 | async function notificationCreator (user: MUserWithNotificationSetting) { |
421 | const notification = await UserNotificationModel.create({ | 462 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
422 | type: UserNotificationType.MY_VIDEO_PUBLISHED, | 463 | type: UserNotificationType.MY_VIDEO_PUBLISHED, |
423 | userId: user.id, | 464 | userId: user.id, |
424 | videoId: video.id | 465 | videoId: video.id |
@@ -435,18 +476,18 @@ class Notifier { | |||
435 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 476 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
436 | } | 477 | } |
437 | 478 | ||
438 | private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) { | 479 | private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) { |
439 | const user = await UserModel.loadByVideoImportId(videoImport.id) | 480 | const user = await UserModel.loadByVideoImportId(videoImport.id) |
440 | if (!user) return | 481 | if (!user) return |
441 | 482 | ||
442 | logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier()) | 483 | logger.info('Notifying user %s its video import %s is finished.', user.username, videoImport.getTargetIdentifier()) |
443 | 484 | ||
444 | function settingGetter (user: UserModel) { | 485 | function settingGetter (user: MUserWithNotificationSetting) { |
445 | return user.NotificationSetting.myVideoImportFinished | 486 | return user.NotificationSetting.myVideoImportFinished |
446 | } | 487 | } |
447 | 488 | ||
448 | async function notificationCreator (user: UserModel) { | 489 | async function notificationCreator (user: MUserWithNotificationSetting) { |
449 | const notification = await UserNotificationModel.create({ | 490 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
450 | type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR, | 491 | type: success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR, |
451 | userId: user.id, | 492 | userId: user.id, |
452 | videoImportId: videoImport.id | 493 | videoImportId: videoImport.id |
@@ -465,21 +506,21 @@ class Notifier { | |||
465 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) | 506 | return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) |
466 | } | 507 | } |
467 | 508 | ||
468 | private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) { | 509 | private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserDefault) { |
469 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) | 510 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) |
470 | if (moderators.length === 0) return | 511 | if (moderators.length === 0) return |
471 | 512 | ||
472 | logger.info( | 513 | logger.info( |
473 | 'Notifying %s moderators of new user registration of %s.', | 514 | 'Notifying %s moderators of new user registration of %s.', |
474 | moderators.length, registeredUser.Account.Actor.preferredUsername | 515 | moderators.length, registeredUser.username |
475 | ) | 516 | ) |
476 | 517 | ||
477 | function settingGetter (user: UserModel) { | 518 | function settingGetter (user: MUserWithNotificationSetting) { |
478 | return user.NotificationSetting.newUserRegistration | 519 | return user.NotificationSetting.newUserRegistration |
479 | } | 520 | } |
480 | 521 | ||
481 | async function notificationCreator (user: UserModel) { | 522 | async function notificationCreator (user: MUserWithNotificationSetting) { |
482 | const notification = await UserNotificationModel.create({ | 523 | const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ |
483 | type: UserNotificationType.NEW_USER_REGISTRATION, | 524 | type: UserNotificationType.NEW_USER_REGISTRATION, |
484 | userId: user.id, | 525 | userId: user.id, |
485 | accountId: registeredUser.Account.id | 526 | accountId: registeredUser.Account.id |
@@ -496,11 +537,11 @@ class Notifier { | |||
496 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) | 537 | return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) |
497 | } | 538 | } |
498 | 539 | ||
499 | private async notify (options: { | 540 | private async notify <T extends MUserWithNotificationSetting> (options: { |
500 | users: UserModel[], | 541 | users: T[], |
501 | notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, | 542 | notificationCreator: (user: T) => Promise<UserNotificationModelForApi>, |
502 | emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, | 543 | emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, |
503 | settingGetter: (user: UserModel) => UserNotificationSettingValue | 544 | settingGetter: (user: T) => UserNotificationSettingValue |
504 | }) { | 545 | }) { |
505 | const emails: string[] = [] | 546 | const emails: string[] = [] |
506 | 547 | ||
@@ -521,7 +562,7 @@ class Notifier { | |||
521 | } | 562 | } |
522 | } | 563 | } |
523 | 564 | ||
524 | private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) { | 565 | private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) { |
525 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false | 566 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false |
526 | 567 | ||
527 | return value & UserNotificationSettingValue.EMAIL | 568 | return value & UserNotificationSettingValue.EMAIL |
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts index a1153e88a..086856f41 100644 --- a/server/lib/oauth-model.ts +++ b/server/lib/oauth-model.ts | |||
@@ -8,10 +8,11 @@ import { LRU_CACHE } from '../initializers/constants' | |||
8 | import { Transaction } from 'sequelize' | 8 | import { Transaction } from 'sequelize' |
9 | import { CONFIG } from '../initializers/config' | 9 | import { CONFIG } from '../initializers/config' |
10 | import * as LRUCache from 'lru-cache' | 10 | import * as LRUCache from 'lru-cache' |
11 | import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' | ||
11 | 12 | ||
12 | type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } | 13 | type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } |
13 | 14 | ||
14 | const accessTokenCache = new LRUCache<string, OAuthTokenModel>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) | 15 | const accessTokenCache = new LRUCache<string, MOAuthTokenUser>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) |
15 | const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) | 16 | const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) |
16 | 17 | ||
17 | // --------------------------------------------------------------------------- | 18 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/peertube-socket.ts b/server/lib/peertube-socket.ts index 17748fd18..26ced351f 100644 --- a/server/lib/peertube-socket.ts +++ b/server/lib/peertube-socket.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import * as SocketIO from 'socket.io' | 1 | import * as SocketIO from 'socket.io' |
2 | import { authenticateSocket } from '../middlewares' | 2 | import { authenticateSocket } from '../middlewares' |
3 | import { UserNotificationModel } from '../models/account/user-notification' | ||
4 | import { logger } from '../helpers/logger' | 3 | import { logger } from '../helpers/logger' |
5 | import { Server } from 'http' | 4 | import { Server } from 'http' |
5 | import { UserNotificationModelForApi } from '@server/typings/models/user' | ||
6 | 6 | ||
7 | class PeerTubeSocket { | 7 | class PeerTubeSocket { |
8 | 8 | ||
@@ -34,13 +34,14 @@ class PeerTubeSocket { | |||
34 | }) | 34 | }) |
35 | } | 35 | } |
36 | 36 | ||
37 | sendNotification (userId: number, notification: UserNotificationModel) { | 37 | sendNotification (userId: number, notification: UserNotificationModelForApi) { |
38 | const sockets = this.userNotificationSockets[userId] | 38 | const sockets = this.userNotificationSockets[userId] |
39 | 39 | ||
40 | if (!sockets) return | 40 | if (!sockets) return |
41 | 41 | ||
42 | const notificationMessage = notification.toFormattedJSON() | ||
42 | for (const socket of sockets) { | 43 | for (const socket of sockets) { |
43 | socket.emit('new-notification', notification.toFormattedJSON()) | 44 | socket.emit('new-notification', notificationMessage) |
44 | } | 45 | } |
45 | } | 46 | } |
46 | 47 | ||
diff --git a/server/lib/redundancy.ts b/server/lib/redundancy.ts index 04d3ded8f..1b4ecd7c0 100644 --- a/server/lib/redundancy.ts +++ b/server/lib/redundancy.ts | |||
@@ -2,8 +2,9 @@ import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | |||
2 | import { sendUndoCacheFile } from './activitypub/send' | 2 | import { sendUndoCacheFile } from './activitypub/send' |
3 | import { Transaction } from 'sequelize' | 3 | import { Transaction } from 'sequelize' |
4 | import { getServerActor } from '../helpers/utils' | 4 | import { getServerActor } from '../helpers/utils' |
5 | import { MVideoRedundancyVideo } from '@server/typings/models' | ||
5 | 6 | ||
6 | async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?: Transaction) { | 7 | async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { |
7 | const serverActor = await getServerActor() | 8 | const serverActor = await getServerActor() |
8 | 9 | ||
9 | // Local cache, send undo to remote instances | 10 | // Local cache, send undo to remote instances |
diff --git a/server/lib/schedulers/auto-follow-index-instances.ts b/server/lib/schedulers/auto-follow-index-instances.ts new file mode 100644 index 000000000..ef11fc87f --- /dev/null +++ b/server/lib/schedulers/auto-follow-index-instances.ts | |||
@@ -0,0 +1,72 @@ | |||
1 | import { logger } from '../../helpers/logger' | ||
2 | import { AbstractScheduler } from './abstract-scheduler' | ||
3 | import { INSTANCES_INDEX, SCHEDULER_INTERVALS_MS, SERVER_ACTOR_NAME } from '../../initializers/constants' | ||
4 | import { CONFIG } from '../../initializers/config' | ||
5 | import { chunk } from 'lodash' | ||
6 | import { doRequest } from '@server/helpers/requests' | ||
7 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | ||
8 | import { JobQueue } from '@server/lib/job-queue' | ||
9 | import { getServerActor } from '@server/helpers/utils' | ||
10 | |||
11 | export class AutoFollowIndexInstances extends AbstractScheduler { | ||
12 | |||
13 | private static instance: AbstractScheduler | ||
14 | |||
15 | protected schedulerIntervalMs = SCHEDULER_INTERVALS_MS.autoFollowIndexInstances | ||
16 | |||
17 | private lastCheck: Date | ||
18 | |||
19 | private constructor () { | ||
20 | super() | ||
21 | } | ||
22 | |||
23 | protected async internalExecute () { | ||
24 | return this.autoFollow() | ||
25 | } | ||
26 | |||
27 | private async autoFollow () { | ||
28 | if (CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.ENABLED === false) return | ||
29 | |||
30 | const indexUrl = CONFIG.FOLLOWINGS.INSTANCE.AUTO_FOLLOW_INDEX.INDEX_URL | ||
31 | |||
32 | logger.info('Auto follow instances of index %s.', indexUrl) | ||
33 | |||
34 | try { | ||
35 | const serverActor = await getServerActor() | ||
36 | |||
37 | const uri = indexUrl + INSTANCES_INDEX.HOSTS_PATH | ||
38 | |||
39 | const qs = this.lastCheck ? { since: this.lastCheck.toISOString() } : {} | ||
40 | this.lastCheck = new Date() | ||
41 | |||
42 | const { body } = await doRequest({ uri, qs, json: true }) | ||
43 | |||
44 | const hosts: string[] = body.data.map(o => o.host) | ||
45 | const chunks = chunk(hosts, 20) | ||
46 | |||
47 | for (const chunk of chunks) { | ||
48 | const unfollowedHosts = await ActorFollowModel.keepUnfollowedInstance(chunk) | ||
49 | |||
50 | for (const unfollowedHost of unfollowedHosts) { | ||
51 | const payload = { | ||
52 | host: unfollowedHost, | ||
53 | name: SERVER_ACTOR_NAME, | ||
54 | followerActorId: serverActor.id, | ||
55 | isAutoFollow: true | ||
56 | } | ||
57 | |||
58 | await JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) | ||
59 | .catch(err => logger.error('Cannot create follow job for %s.', unfollowedHost, err)) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | } catch (err) { | ||
64 | logger.error('Cannot auto follow hosts of index %s.', indexUrl, { err }) | ||
65 | } | ||
66 | |||
67 | } | ||
68 | |||
69 | static get Instance () { | ||
70 | return this.instance || (this.instance = new this()) | ||
71 | } | ||
72 | } | ||
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts index 5f4aad66e..1e30f6ebc 100644 --- a/server/lib/schedulers/videos-redundancy-scheduler.ts +++ b/server/lib/schedulers/videos-redundancy-scheduler.ts | |||
@@ -3,7 +3,6 @@ import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER } | |||
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import { VideosRedundancy } from '../../../shared/models/redundancy' | 4 | import { VideosRedundancy } from '../../../shared/models/redundancy' |
5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
6 | import { VideoFileModel } from '../../models/video/video-file' | ||
7 | import { downloadWebTorrentVideo } from '../../helpers/webtorrent' | 6 | import { downloadWebTorrentVideo } from '../../helpers/webtorrent' |
8 | import { join } from 'path' | 7 | import { join } from 'path' |
9 | import { move } from 'fs-extra' | 8 | import { move } from 'fs-extra' |
@@ -12,16 +11,31 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send' | |||
12 | import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' | 11 | import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' |
13 | import { removeVideoRedundancy } from '../redundancy' | 12 | import { removeVideoRedundancy } from '../redundancy' |
14 | import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' | 13 | import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' |
15 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | ||
16 | import { VideoModel } from '../../models/video/video' | ||
17 | import { downloadPlaylistSegments } from '../hls' | 14 | import { downloadPlaylistSegments } from '../hls' |
18 | import { CONFIG } from '../../initializers/config' | 15 | import { CONFIG } from '../../initializers/config' |
16 | import { | ||
17 | MStreamingPlaylist, | ||
18 | MStreamingPlaylistVideo, | ||
19 | MVideoAccountLight, | ||
20 | MVideoFile, | ||
21 | MVideoFileVideo, | ||
22 | MVideoRedundancyFileVideo, | ||
23 | MVideoRedundancyStreamingPlaylistVideo, | ||
24 | MVideoRedundancyVideo, | ||
25 | MVideoWithAllFiles | ||
26 | } from '@server/typings/models' | ||
19 | 27 | ||
20 | type CandidateToDuplicate = { | 28 | type CandidateToDuplicate = { |
21 | redundancy: VideosRedundancy, | 29 | redundancy: VideosRedundancy, |
22 | video: VideoModel, | 30 | video: MVideoWithAllFiles, |
23 | files: VideoFileModel[], | 31 | files: MVideoFile[], |
24 | streamingPlaylists: VideoStreamingPlaylistModel[] | 32 | streamingPlaylists: MStreamingPlaylist[] |
33 | } | ||
34 | |||
35 | function isMVideoRedundancyFileVideo ( | ||
36 | o: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo | ||
37 | ): o is MVideoRedundancyFileVideo { | ||
38 | return !!(o as MVideoRedundancyFileVideo).VideoFile | ||
25 | } | 39 | } |
26 | 40 | ||
27 | export class VideosRedundancyScheduler extends AbstractScheduler { | 41 | export class VideosRedundancyScheduler extends AbstractScheduler { |
@@ -102,7 +116,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
102 | } | 116 | } |
103 | } | 117 | } |
104 | 118 | ||
105 | private async extendsRedundancy (redundancyModel: VideoRedundancyModel) { | 119 | private async extendsRedundancy (redundancyModel: MVideoRedundancyVideo) { |
106 | const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) | 120 | const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) |
107 | // Redundancy strategy disabled, remove our redundancy instead of extending expiration | 121 | // Redundancy strategy disabled, remove our redundancy instead of extending expiration |
108 | if (!redundancy) { | 122 | if (!redundancy) { |
@@ -172,7 +186,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
172 | } | 186 | } |
173 | } | 187 | } |
174 | 188 | ||
175 | private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: VideoModel, file: VideoFileModel) { | 189 | private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: MVideoAccountLight, fileArg: MVideoFile) { |
190 | const file = fileArg as MVideoFileVideo | ||
176 | file.Video = video | 191 | file.Video = video |
177 | 192 | ||
178 | const serverActor = await getServerActor() | 193 | const serverActor = await getServerActor() |
@@ -187,7 +202,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
187 | const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) | 202 | const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) |
188 | await move(tmpPath, destPath, { overwrite: true }) | 203 | await move(tmpPath, destPath, { overwrite: true }) |
189 | 204 | ||
190 | const createdModel = await VideoRedundancyModel.create({ | 205 | const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({ |
191 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), | 206 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), |
192 | url: getVideoCacheFileActivityPubUrl(file), | 207 | url: getVideoCacheFileActivityPubUrl(file), |
193 | fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), | 208 | fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), |
@@ -203,7 +218,12 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
203 | logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) | 218 | logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) |
204 | } | 219 | } |
205 | 220 | ||
206 | private async createStreamingPlaylistRedundancy (redundancy: VideosRedundancy, video: VideoModel, playlist: VideoStreamingPlaylistModel) { | 221 | private async createStreamingPlaylistRedundancy ( |
222 | redundancy: VideosRedundancy, | ||
223 | video: MVideoAccountLight, | ||
224 | playlistArg: MStreamingPlaylist | ||
225 | ) { | ||
226 | const playlist = playlistArg as MStreamingPlaylistVideo | ||
207 | playlist.Video = video | 227 | playlist.Video = video |
208 | 228 | ||
209 | const serverActor = await getServerActor() | 229 | const serverActor = await getServerActor() |
@@ -213,7 +233,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
213 | const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) | 233 | const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) |
214 | await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) | 234 | await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) |
215 | 235 | ||
216 | const createdModel = await VideoRedundancyModel.create({ | 236 | const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ |
217 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), | 237 | expiresOn: this.buildNewExpiration(redundancy.minLifetime), |
218 | url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), | 238 | url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), |
219 | fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), | 239 | fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), |
@@ -229,7 +249,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
229 | logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) | 249 | logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) |
230 | } | 250 | } |
231 | 251 | ||
232 | private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) { | 252 | private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { |
233 | logger.info('Extending expiration of %s.', redundancy.url) | 253 | logger.info('Extending expiration of %s.', redundancy.url) |
234 | 254 | ||
235 | const serverActor = await getServerActor() | 255 | const serverActor = await getServerActor() |
@@ -243,7 +263,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
243 | private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { | 263 | private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { |
244 | while (await this.isTooHeavy(candidateToDuplicate)) { | 264 | while (await this.isTooHeavy(candidateToDuplicate)) { |
245 | const redundancy = candidateToDuplicate.redundancy | 265 | const redundancy = candidateToDuplicate.redundancy |
246 | const toDelete = await VideoRedundancyModel.loadOldestLocalThatAlreadyExpired(redundancy.strategy, redundancy.minLifetime) | 266 | const toDelete = await VideoRedundancyModel.loadOldestLocalExpired(redundancy.strategy, redundancy.minLifetime) |
247 | if (!toDelete) return | 267 | if (!toDelete) return |
248 | 268 | ||
249 | await removeVideoRedundancy(toDelete) | 269 | await removeVideoRedundancy(toDelete) |
@@ -263,19 +283,18 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
263 | return new Date(Date.now() + expiresAfterMs) | 283 | return new Date(Date.now() + expiresAfterMs) |
264 | } | 284 | } |
265 | 285 | ||
266 | private buildEntryLogId (object: VideoRedundancyModel) { | 286 | private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { |
267 | if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` | 287 | if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` |
268 | 288 | ||
269 | return `${object.VideoStreamingPlaylist.playlistUrl}` | 289 | return `${object.VideoStreamingPlaylist.playlistUrl}` |
270 | } | 290 | } |
271 | 291 | ||
272 | private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) { | 292 | private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) { |
273 | const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size | 293 | const fileReducer = (previous: number, current: MVideoFile) => previous + current.size |
274 | 294 | ||
275 | const totalSize = files.reduce(fileReducer, 0) | 295 | const totalSize = files.reduce(fileReducer, 0) |
276 | if (playlists.length === 0) return totalSize | ||
277 | 296 | ||
278 | return totalSize * playlists.length | 297 | return totalSize + (totalSize * playlists.length) |
279 | } | 298 | } |
280 | 299 | ||
281 | private async loadAndRefreshVideo (videoUrl: string) { | 300 | private async loadAndRefreshVideo (videoUrl: string) { |
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts index a59773f5a..84791955e 100644 --- a/server/lib/thumbnail.ts +++ b/server/lib/thumbnail.ts | |||
@@ -1,20 +1,20 @@ | |||
1 | import { VideoFileModel } from '../models/video/video-file' | ||
2 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' | 1 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' |
3 | import { CONFIG } from '../initializers/config' | 2 | import { CONFIG } from '../initializers/config' |
4 | import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' | 3 | import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' |
5 | import { VideoModel } from '../models/video/video' | ||
6 | import { ThumbnailModel } from '../models/video/thumbnail' | 4 | import { ThumbnailModel } from '../models/video/thumbnail' |
7 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' | 5 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' |
8 | import { processImage } from '../helpers/image-utils' | 6 | import { processImage } from '../helpers/image-utils' |
9 | import { join } from 'path' | 7 | import { join } from 'path' |
10 | import { downloadImage } from '../helpers/requests' | 8 | import { downloadImage } from '../helpers/requests' |
11 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 9 | import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist' |
10 | import { MVideoFile, MVideoThumbnail } from '../typings/models' | ||
11 | import { MThumbnail } from '../typings/models/video/thumbnail' | ||
12 | 12 | ||
13 | type ImageSize = { height: number, width: number } | 13 | type ImageSize = { height: number, width: number } |
14 | 14 | ||
15 | function createPlaylistMiniatureFromExisting ( | 15 | function createPlaylistMiniatureFromExisting ( |
16 | inputPath: string, | 16 | inputPath: string, |
17 | playlist: VideoPlaylistModel, | 17 | playlist: MVideoPlaylistThumbnail, |
18 | automaticallyGenerated: boolean, | 18 | automaticallyGenerated: boolean, |
19 | keepOriginal = false, | 19 | keepOriginal = false, |
20 | size?: ImageSize | 20 | size?: ImageSize |
@@ -26,7 +26,7 @@ function createPlaylistMiniatureFromExisting ( | |||
26 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) | 26 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) |
27 | } | 27 | } |
28 | 28 | ||
29 | function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylistModel, size?: ImageSize) { | 29 | function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: MVideoPlaylistThumbnail, size?: ImageSize) { |
30 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) | 30 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) |
31 | const type = ThumbnailType.MINIATURE | 31 | const type = ThumbnailType.MINIATURE |
32 | 32 | ||
@@ -34,7 +34,7 @@ function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylis | |||
34 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) | 34 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl }) |
35 | } | 35 | } |
36 | 36 | ||
37 | function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { | 37 | function createVideoMiniatureFromUrl (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) { |
38 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | 38 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) |
39 | const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height }) | 39 | const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height }) |
40 | 40 | ||
@@ -43,7 +43,7 @@ function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: | |||
43 | 43 | ||
44 | function createVideoMiniatureFromExisting ( | 44 | function createVideoMiniatureFromExisting ( |
45 | inputPath: string, | 45 | inputPath: string, |
46 | video: VideoModel, | 46 | video: MVideoThumbnail, |
47 | type: ThumbnailType, | 47 | type: ThumbnailType, |
48 | automaticallyGenerated: boolean, | 48 | automaticallyGenerated: boolean, |
49 | size?: ImageSize | 49 | size?: ImageSize |
@@ -54,7 +54,7 @@ function createVideoMiniatureFromExisting ( | |||
54 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) | 54 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail }) |
55 | } | 55 | } |
56 | 56 | ||
57 | function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { | 57 | function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile, type: ThumbnailType) { |
58 | const input = video.getVideoFilePath(videoFile) | 58 | const input = video.getVideoFilePath(videoFile) |
59 | 59 | ||
60 | const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) | 60 | const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type) |
@@ -65,7 +65,7 @@ function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, t | |||
65 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail }) | 65 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail }) |
66 | } | 66 | } |
67 | 67 | ||
68 | function createPlaceholderThumbnail (fileUrl: string, video: VideoModel, type: ThumbnailType, size: ImageSize) { | 68 | function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) { |
69 | const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | 69 | const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) |
70 | 70 | ||
71 | const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() | 71 | const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() |
@@ -90,7 +90,7 @@ export { | |||
90 | createPlaylistMiniatureFromExisting | 90 | createPlaylistMiniatureFromExisting |
91 | } | 91 | } |
92 | 92 | ||
93 | function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) { | 93 | function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) { |
94 | const filename = playlist.generateThumbnailName() | 94 | const filename = playlist.generateThumbnailName() |
95 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR | 95 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR |
96 | 96 | ||
@@ -104,7 +104,7 @@ function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSiz | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | 106 | ||
107 | function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) { | 107 | function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) { |
108 | const existingThumbnail = Array.isArray(video.Thumbnails) | 108 | const existingThumbnail = Array.isArray(video.Thumbnails) |
109 | ? video.Thumbnails.find(t => t.type === type) | 109 | ? video.Thumbnails.find(t => t.type === type) |
110 | : undefined | 110 | : undefined |
@@ -148,7 +148,7 @@ async function createThumbnailFromFunction (parameters: { | |||
148 | type: ThumbnailType, | 148 | type: ThumbnailType, |
149 | automaticallyGenerated?: boolean, | 149 | automaticallyGenerated?: boolean, |
150 | fileUrl?: string, | 150 | fileUrl?: string, |
151 | existingThumbnail?: ThumbnailModel | 151 | existingThumbnail?: MThumbnail |
152 | }) { | 152 | }) { |
153 | const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters | 153 | const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters |
154 | 154 | ||
diff --git a/server/lib/user.ts b/server/lib/user.ts index 0e4007770..c45438d95 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -2,10 +2,8 @@ import * as uuidv4 from 'uuid/v4' | |||
2 | import { ActivityPubActorType } from '../../shared/models/activitypub' | 2 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
3 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' | 3 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' |
4 | import { AccountModel } from '../models/account/account' | 4 | import { AccountModel } from '../models/account/account' |
5 | import { UserModel } from '../models/account/user' | ||
6 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' | 5 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' |
7 | import { createVideoChannel } from './video-channel' | 6 | import { createLocalVideoChannel } from './video-channel' |
8 | import { VideoChannelModel } from '../models/video/video-channel' | ||
9 | import { ActorModel } from '../models/activitypub/actor' | 7 | import { ActorModel } from '../models/activitypub/actor' |
10 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | 8 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' |
11 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' | 9 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' |
@@ -14,14 +12,17 @@ import { sequelizeTypescript } from '../initializers/database' | |||
14 | import { Transaction } from 'sequelize/types' | 12 | import { Transaction } from 'sequelize/types' |
15 | import { Redis } from './redis' | 13 | import { Redis } from './redis' |
16 | import { Emailer } from './emailer' | 14 | import { Emailer } from './emailer' |
15 | import { MAccountDefault, MActorDefault, MChannelActor } from '../typings/models' | ||
16 | import { MUser, MUserDefault, MUserId } from '../typings/models/user' | ||
17 | 17 | ||
18 | type ChannelNames = { name: string, displayName: string } | 18 | type ChannelNames = { name: string, displayName: string } |
19 | |||
19 | async function createUserAccountAndChannelAndPlaylist (parameters: { | 20 | async function createUserAccountAndChannelAndPlaylist (parameters: { |
20 | userToCreate: UserModel, | 21 | userToCreate: MUser, |
21 | userDisplayName?: string, | 22 | userDisplayName?: string, |
22 | channelNames?: ChannelNames, | 23 | channelNames?: ChannelNames, |
23 | validateUser?: boolean | 24 | validateUser?: boolean |
24 | }) { | 25 | }): Promise<{ user: MUserDefault, account: MAccountDefault, videoChannel: MChannelActor }> { |
25 | const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters | 26 | const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters |
26 | 27 | ||
27 | const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { | 28 | const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { |
@@ -30,7 +31,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { | |||
30 | validate: validateUser | 31 | validate: validateUser |
31 | } | 32 | } |
32 | 33 | ||
33 | const userCreated = await userToCreate.save(userOptions) | 34 | const userCreated: MUserDefault = await userToCreate.save(userOptions) |
34 | userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) | 35 | userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) |
35 | 36 | ||
36 | const accountCreated = await createLocalAccountWithoutKeys({ | 37 | const accountCreated = await createLocalAccountWithoutKeys({ |
@@ -43,22 +44,22 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { | |||
43 | userCreated.Account = accountCreated | 44 | userCreated.Account = accountCreated |
44 | 45 | ||
45 | const channelAttributes = await buildChannelAttributes(userCreated, channelNames) | 46 | const channelAttributes = await buildChannelAttributes(userCreated, channelNames) |
46 | const videoChannel = await createVideoChannel(channelAttributes, accountCreated, t) | 47 | const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t) |
47 | 48 | ||
48 | const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) | 49 | const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) |
49 | 50 | ||
50 | return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } | 51 | return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } |
51 | }) | 52 | }) |
52 | 53 | ||
53 | const [ accountKeys, channelKeys ] = await Promise.all([ | 54 | const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([ |
54 | setAsyncActorKeys(account.Actor), | 55 | setAsyncActorKeys(account.Actor), |
55 | setAsyncActorKeys(videoChannel.Actor) | 56 | setAsyncActorKeys(videoChannel.Actor) |
56 | ]) | 57 | ]) |
57 | 58 | ||
58 | account.Actor = accountKeys | 59 | account.Actor = accountActorWithKeys |
59 | videoChannel.Actor = channelKeys | 60 | videoChannel.Actor = channelActorWithKeys |
60 | 61 | ||
61 | return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel } | 62 | return { user, account, videoChannel } |
62 | } | 63 | } |
63 | 64 | ||
64 | async function createLocalAccountWithoutKeys (parameters: { | 65 | async function createLocalAccountWithoutKeys (parameters: { |
@@ -73,7 +74,7 @@ async function createLocalAccountWithoutKeys (parameters: { | |||
73 | const url = getAccountActivityPubUrl(name) | 74 | const url = getAccountActivityPubUrl(name) |
74 | 75 | ||
75 | const actorInstance = buildActorInstance(type, url, name) | 76 | const actorInstance = buildActorInstance(type, url, name) |
76 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) | 77 | const actorInstanceCreated: MActorDefault = await actorInstance.save({ transaction: t }) |
77 | 78 | ||
78 | const accountInstance = new AccountModel({ | 79 | const accountInstance = new AccountModel({ |
79 | name: displayName || name, | 80 | name: displayName || name, |
@@ -82,7 +83,7 @@ async function createLocalAccountWithoutKeys (parameters: { | |||
82 | actorId: actorInstanceCreated.id | 83 | actorId: actorInstanceCreated.id |
83 | }) | 84 | }) |
84 | 85 | ||
85 | const accountInstanceCreated = await accountInstance.save({ transaction: t }) | 86 | const accountInstanceCreated: MAccountDefault = await accountInstance.save({ transaction: t }) |
86 | accountInstanceCreated.Actor = actorInstanceCreated | 87 | accountInstanceCreated.Actor = actorInstanceCreated |
87 | 88 | ||
88 | return accountInstanceCreated | 89 | return accountInstanceCreated |
@@ -102,7 +103,7 @@ async function createApplicationActor (applicationId: number) { | |||
102 | return accountCreated | 103 | return accountCreated |
103 | } | 104 | } |
104 | 105 | ||
105 | async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) { | 106 | async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) { |
106 | const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) | 107 | const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) |
107 | let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString | 108 | let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString |
108 | 109 | ||
@@ -124,7 +125,7 @@ export { | |||
124 | 125 | ||
125 | // --------------------------------------------------------------------------- | 126 | // --------------------------------------------------------------------------- |
126 | 127 | ||
127 | function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) { | 128 | function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) { |
128 | const values: UserNotificationSetting & { userId: number } = { | 129 | const values: UserNotificationSetting & { userId: number } = { |
129 | userId: user.id, | 130 | userId: user.id, |
130 | newVideoFromSubscription: UserNotificationSettingValue.WEB, | 131 | newVideoFromSubscription: UserNotificationSettingValue.WEB, |
@@ -137,13 +138,14 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | |||
137 | newUserRegistration: UserNotificationSettingValue.WEB, | 138 | newUserRegistration: UserNotificationSettingValue.WEB, |
138 | commentMention: UserNotificationSettingValue.WEB, | 139 | commentMention: UserNotificationSettingValue.WEB, |
139 | newFollow: UserNotificationSettingValue.WEB, | 140 | newFollow: UserNotificationSettingValue.WEB, |
140 | newInstanceFollower: UserNotificationSettingValue.WEB | 141 | newInstanceFollower: UserNotificationSettingValue.WEB, |
142 | autoInstanceFollowing: UserNotificationSettingValue.WEB | ||
141 | } | 143 | } |
142 | 144 | ||
143 | return UserNotificationSettingModel.create(values, { transaction: t }) | 145 | return UserNotificationSettingModel.create(values, { transaction: t }) |
144 | } | 146 | } |
145 | 147 | ||
146 | async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) { | 148 | async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) { |
147 | if (channelNames) return channelNames | 149 | if (channelNames) return channelNames |
148 | 150 | ||
149 | let channelName = user.username + '_channel' | 151 | let channelName = user.username + '_channel' |
diff --git a/server/lib/video-blacklist.ts b/server/lib/video-blacklist.ts index bdaecd8e2..1dd45b76d 100644 --- a/server/lib/video-blacklist.ts +++ b/server/lib/video-blacklist.ts | |||
@@ -2,16 +2,15 @@ import { Transaction } from 'sequelize' | |||
2 | import { CONFIG } from '../initializers/config' | 2 | import { CONFIG } from '../initializers/config' |
3 | import { UserRight, VideoBlacklistType } from '../../shared/models' | 3 | import { UserRight, VideoBlacklistType } from '../../shared/models' |
4 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | 4 | import { VideoBlacklistModel } from '../models/video/video-blacklist' |
5 | import { UserModel } from '../models/account/user' | ||
6 | import { VideoModel } from '../models/video/video' | ||
7 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
8 | import { UserAdminFlag } from '../../shared/models/users/user-flag.model' | 6 | import { UserAdminFlag } from '../../shared/models/users/user-flag.model' |
9 | import { Hooks } from './plugins/hooks' | 7 | import { Hooks } from './plugins/hooks' |
10 | import { Notifier } from './notifier' | 8 | import { Notifier } from './notifier' |
9 | import { MUser, MVideoBlacklistVideo, MVideoWithBlacklistLight } from '@server/typings/models' | ||
11 | 10 | ||
12 | async function autoBlacklistVideoIfNeeded (parameters: { | 11 | async function autoBlacklistVideoIfNeeded (parameters: { |
13 | video: VideoModel, | 12 | video: MVideoWithBlacklistLight, |
14 | user?: UserModel, | 13 | user?: MUser, |
15 | isRemote: boolean, | 14 | isRemote: boolean, |
16 | isNew: boolean, | 15 | isNew: boolean, |
17 | notify?: boolean, | 16 | notify?: boolean, |
@@ -32,7 +31,7 @@ async function autoBlacklistVideoIfNeeded (parameters: { | |||
32 | reason: 'Auto-blacklisted. Moderator review required.', | 31 | reason: 'Auto-blacklisted. Moderator review required.', |
33 | type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED | 32 | type: VideoBlacklistType.AUTO_BEFORE_PUBLISHED |
34 | } | 33 | } |
35 | const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate({ | 34 | const [ videoBlacklist ] = await VideoBlacklistModel.findOrCreate<MVideoBlacklistVideo>({ |
36 | where: { | 35 | where: { |
37 | videoId: video.id | 36 | videoId: video.id |
38 | }, | 37 | }, |
@@ -41,7 +40,9 @@ async function autoBlacklistVideoIfNeeded (parameters: { | |||
41 | }) | 40 | }) |
42 | video.VideoBlacklist = videoBlacklist | 41 | video.VideoBlacklist = videoBlacklist |
43 | 42 | ||
44 | if (notify) Notifier.Instance.notifyOnVideoAutoBlacklist(video) | 43 | videoBlacklist.Video = video |
44 | |||
45 | if (notify) Notifier.Instance.notifyOnVideoAutoBlacklist(videoBlacklist) | ||
45 | 46 | ||
46 | logger.info('Video %s auto-blacklisted.', video.uuid) | 47 | logger.info('Video %s auto-blacklisted.', video.uuid) |
47 | 48 | ||
@@ -49,10 +50,10 @@ async function autoBlacklistVideoIfNeeded (parameters: { | |||
49 | } | 50 | } |
50 | 51 | ||
51 | async function autoBlacklistNeeded (parameters: { | 52 | async function autoBlacklistNeeded (parameters: { |
52 | video: VideoModel, | 53 | video: MVideoWithBlacklistLight, |
53 | isRemote: boolean, | 54 | isRemote: boolean, |
54 | isNew: boolean, | 55 | isNew: boolean, |
55 | user?: UserModel | 56 | user?: MUser |
56 | }) { | 57 | }) { |
57 | const { user, video, isRemote, isNew } = parameters | 58 | const { user, video, isRemote, isNew } = parameters |
58 | 59 | ||
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index ee0482c36..41eab456b 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -1,12 +1,19 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import * as uuidv4 from 'uuid/v4' | 2 | import * as uuidv4 from 'uuid/v4' |
3 | import { VideoChannelCreate } from '../../shared/models' | 3 | import { VideoChannelCreate } from '../../shared/models' |
4 | import { AccountModel } from '../models/account/account' | ||
5 | import { VideoChannelModel } from '../models/video/video-channel' | 4 | import { VideoChannelModel } from '../models/video/video-channel' |
6 | import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' | 5 | import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' |
7 | import { VideoModel } from '../models/video/video' | 6 | import { VideoModel } from '../models/video/video' |
7 | import { MAccountId, MChannelDefault, MChannelId } from '../typings/models' | ||
8 | 8 | ||
9 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { | 9 | type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & |
10 | { Account?: T } | ||
11 | |||
12 | async function createLocalVideoChannel <T extends MAccountId> ( | ||
13 | videoChannelInfo: VideoChannelCreate, | ||
14 | account: T, | ||
15 | t: Sequelize.Transaction | ||
16 | ): Promise<CustomVideoChannelModelAccount<T>> { | ||
10 | const uuid = uuidv4() | 17 | const uuid = uuidv4() |
11 | const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) | 18 | const url = getVideoChannelActivityPubUrl(videoChannelInfo.name) |
12 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) | 19 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) |
@@ -21,10 +28,10 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account | |||
21 | actorId: actorInstanceCreated.id | 28 | actorId: actorInstanceCreated.id |
22 | } | 29 | } |
23 | 30 | ||
24 | const videoChannel = VideoChannelModel.build(videoChannelData) | 31 | const videoChannel = new VideoChannelModel(videoChannelData) |
25 | 32 | ||
26 | const options = { transaction: t } | 33 | const options = { transaction: t } |
27 | const videoChannelCreated = await videoChannel.save(options) | 34 | const videoChannelCreated: CustomVideoChannelModelAccount<T> = await videoChannel.save(options) as MChannelDefault |
28 | 35 | ||
29 | // Do not forget to add Account/Actor information to the created video channel | 36 | // Do not forget to add Account/Actor information to the created video channel |
30 | videoChannelCreated.Account = account | 37 | videoChannelCreated.Account = account |
@@ -34,7 +41,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account | |||
34 | return videoChannelCreated | 41 | return videoChannelCreated |
35 | } | 42 | } |
36 | 43 | ||
37 | async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { | 44 | async function federateAllVideosOfChannel (videoChannel: MChannelId) { |
38 | const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) | 45 | const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel) |
39 | 46 | ||
40 | for (const videoId of videoIds) { | 47 | for (const videoId of videoIds) { |
@@ -47,6 +54,6 @@ async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { | |||
47 | // --------------------------------------------------------------------------- | 54 | // --------------------------------------------------------------------------- |
48 | 55 | ||
49 | export { | 56 | export { |
50 | createVideoChannel, | 57 | createLocalVideoChannel, |
51 | federateAllVideosOfChannel | 58 | federateAllVideosOfChannel |
52 | } | 59 | } |
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index 449aa74cb..bb811bd2c 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts | |||
@@ -1,17 +1,16 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { ResultList } from '../../shared/models' | 2 | import { ResultList } from '../../shared/models' |
3 | import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' | 3 | import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' |
4 | import { AccountModel } from '../models/account/account' | ||
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 { getVideoCommentActivityPubUrl } from './activitypub' | 5 | import { getVideoCommentActivityPubUrl } from './activitypub' |
8 | import { sendCreateVideoComment } from './activitypub/send' | 6 | import { sendCreateVideoComment } from './activitypub/send' |
7 | import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models' | ||
9 | 8 | ||
10 | async function createVideoComment (obj: { | 9 | async function createVideoComment (obj: { |
11 | text: string, | 10 | text: string, |
12 | inReplyToComment: VideoCommentModel | null, | 11 | inReplyToComment: MComment | null, |
13 | video: VideoModel | 12 | video: MVideoFullLight, |
14 | account: AccountModel | 13 | account: MAccountDefault |
15 | }, t: Sequelize.Transaction) { | 14 | }, t: Sequelize.Transaction) { |
16 | let originCommentId: number | null = null | 15 | let originCommentId: number | null = null |
17 | let inReplyToCommentId: number | null = null | 16 | let inReplyToCommentId: number | null = null |
@@ -32,7 +31,7 @@ async function createVideoComment (obj: { | |||
32 | 31 | ||
33 | comment.url = getVideoCommentActivityPubUrl(obj.video, comment) | 32 | comment.url = getVideoCommentActivityPubUrl(obj.video, comment) |
34 | 33 | ||
35 | const savedComment = await comment.save({ transaction: t }) | 34 | const savedComment: MCommentOwnerVideoReply = await comment.save({ transaction: t }) |
36 | savedComment.InReplyToVideoComment = obj.inReplyToComment | 35 | savedComment.InReplyToVideoComment = obj.inReplyToComment |
37 | savedComment.Video = obj.video | 36 | savedComment.Video = obj.video |
38 | savedComment.Account = obj.account | 37 | savedComment.Account = obj.account |
diff --git a/server/lib/video-playlist.ts b/server/lib/video-playlist.ts index 6e214e60f..29b70cfda 100644 --- a/server/lib/video-playlist.ts +++ b/server/lib/video-playlist.ts | |||
@@ -1,12 +1,13 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { AccountModel } from '../models/account/account' | ||
3 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 2 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
4 | import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' | 3 | import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' |
5 | import { getVideoPlaylistActivityPubUrl } from './activitypub' | 4 | import { getVideoPlaylistActivityPubUrl } from './activitypub' |
6 | import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' | 5 | import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' |
6 | import { MAccount } from '../typings/models' | ||
7 | import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist' | ||
7 | 8 | ||
8 | async function createWatchLaterPlaylist (account: AccountModel, t: Sequelize.Transaction) { | 9 | async function createWatchLaterPlaylist (account: MAccount, t: Sequelize.Transaction) { |
9 | const videoPlaylist = new VideoPlaylistModel({ | 10 | const videoPlaylist: MVideoPlaylistOwner = new VideoPlaylistModel({ |
10 | name: 'Watch later', | 11 | name: 'Watch later', |
11 | privacy: VideoPlaylistPrivacy.PRIVATE, | 12 | privacy: VideoPlaylistPrivacy.PRIVATE, |
12 | type: VideoPlaylistType.WATCH_LATER, | 13 | type: VideoPlaylistType.WATCH_LATER, |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index ba6b29163..a204c0c63 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -5,16 +5,16 @@ import { ensureDir, move, remove, stat } from 'fs-extra' | |||
5 | import { logger } from '../helpers/logger' | 5 | import { logger } from '../helpers/logger' |
6 | import { VideoResolution } from '../../shared/models/videos' | 6 | import { VideoResolution } from '../../shared/models/videos' |
7 | import { VideoFileModel } from '../models/video/video-file' | 7 | import { VideoFileModel } from '../models/video/video-file' |
8 | import { VideoModel } from '../models/video/video' | ||
9 | import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' | 8 | import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' |
10 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 9 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' |
11 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' | 10 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' |
12 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../initializers/config' |
12 | import { MVideoFile, MVideoWithFile, MVideoWithFileThumbnail } from '@server/typings/models' | ||
13 | 13 | ||
14 | /** | 14 | /** |
15 | * Optimize the original video file and replace it. The resolution is not changed. | 15 | * Optimize the original video file and replace it. The resolution is not changed. |
16 | */ | 16 | */ |
17 | async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { | 17 | async function optimizeVideofile (video: MVideoWithFile, inputVideoFileArg?: MVideoFile) { |
18 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 18 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
19 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 19 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
20 | const newExtname = '.mp4' | 20 | const newExtname = '.mp4' |
@@ -57,7 +57,7 @@ async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFi | |||
57 | /** | 57 | /** |
58 | * Transcode the original video file to a lower resolution. | 58 | * Transcode the original video file to a lower resolution. |
59 | */ | 59 | */ |
60 | async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { | 60 | async function transcodeOriginalVideofile (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean) { |
61 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 61 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
62 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 62 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
63 | const extname = '.mp4' | 63 | const extname = '.mp4' |
@@ -87,7 +87,7 @@ async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoR | |||
87 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) | 87 | return onVideoFileTranscoding(video, newVideoFile, videoTranscodedPath, videoOutputPath) |
88 | } | 88 | } |
89 | 89 | ||
90 | async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { | 90 | async function mergeAudioVideofile (video: MVideoWithFileThumbnail, resolution: VideoResolution) { |
91 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR | 91 | const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR |
92 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR | 92 | const transcodeDirectory = CONFIG.STORAGE.TMP_DIR |
93 | const newExtname = '.mp4' | 93 | const newExtname = '.mp4' |
@@ -117,7 +117,7 @@ async function mergeAudioVideofile (video: VideoModel, resolution: VideoResoluti | |||
117 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) | 117 | return onVideoFileTranscoding(video, inputVideoFile, videoTranscodedPath, videoOutputPath) |
118 | } | 118 | } |
119 | 119 | ||
120 | async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { | 120 | async function generateHlsPlaylist (video: MVideoWithFile, resolution: VideoResolution, isPortraitMode: boolean) { |
121 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 121 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
122 | await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) | 122 | await ensureDir(join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid)) |
123 | 123 | ||
@@ -165,14 +165,14 @@ export { | |||
165 | 165 | ||
166 | // --------------------------------------------------------------------------- | 166 | // --------------------------------------------------------------------------- |
167 | 167 | ||
168 | async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { | 168 | async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoFile, transcodingPath: string, outputPath: string) { |
169 | const stats = await stat(transcodingPath) | 169 | const stats = await stat(transcodingPath) |
170 | const fps = await getVideoFileFPS(transcodingPath) | 170 | const fps = await getVideoFileFPS(transcodingPath) |
171 | 171 | ||
172 | await move(transcodingPath, outputPath) | 172 | await move(transcodingPath, outputPath) |
173 | 173 | ||
174 | videoFile.set('size', stats.size) | 174 | videoFile.size = stats.size |
175 | videoFile.set('fps', fps) | 175 | videoFile.fps = fps |
176 | 176 | ||
177 | await video.createTorrentAndSetInfoHash(videoFile) | 177 | await video.createTorrentAndSetInfoHash(videoFile) |
178 | 178 | ||