aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/actor.ts75
-rw-r--r--server/lib/activitypub/audience.ts27
-rw-r--r--server/lib/activitypub/cache-file.ts14
-rw-r--r--server/lib/activitypub/playlist.ts19
-rw-r--r--server/lib/activitypub/process/process-accept.ts5
-rw-r--r--server/lib/activitypub/process/process-announce.ts7
-rw-r--r--server/lib/activitypub/process/process-create.ts14
-rw-r--r--server/lib/activitypub/process/process-delete.ts26
-rw-r--r--server/lib/activitypub/process/process-dislike.ts4
-rw-r--r--server/lib/activitypub/process/process-flag.ts6
-rw-r--r--server/lib/activitypub/process/process-follow.ts21
-rw-r--r--server/lib/activitypub/process/process-like.ts4
-rw-r--r--server/lib/activitypub/process/process-reject.ts4
-rw-r--r--server/lib/activitypub/process/process-undo.ts12
-rw-r--r--server/lib/activitypub/process/process-update.ts12
-rw-r--r--server/lib/activitypub/process/process-view.ts6
-rw-r--r--server/lib/activitypub/process/process.ts11
-rw-r--r--server/lib/activitypub/send/send-accept.ts7
-rw-r--r--server/lib/activitypub/send/send-announce.ts15
-rw-r--r--server/lib/activitypub/send/send-create.ts34
-rw-r--r--server/lib/activitypub/send/send-delete.ts12
-rw-r--r--server/lib/activitypub/send/send-dislike.ts7
-rw-r--r--server/lib/activitypub/send/send-flag.ts9
-rw-r--r--server/lib/activitypub/send/send-follow.ts6
-rw-r--r--server/lib/activitypub/send/send-like.ts7
-rw-r--r--server/lib/activitypub/send/send-reject.ts7
-rw-r--r--server/lib/activitypub/send/send-undo.ts34
-rw-r--r--server/lib/activitypub/send/send-update.ts31
-rw-r--r--server/lib/activitypub/send/send-view.ts6
-rw-r--r--server/lib/activitypub/send/utils.ts35
-rw-r--r--server/lib/activitypub/share.ts21
-rw-r--r--server/lib/activitypub/url.ts60
-rw-r--r--server/lib/activitypub/video-comments.ts16
-rw-r--r--server/lib/activitypub/video-rates.ts24
-rw-r--r--server/lib/activitypub/videos.ts143
-rw-r--r--server/lib/avatar.ts8
-rw-r--r--server/lib/blocklist.ts5
-rw-r--r--server/lib/client-html.ts7
-rw-r--r--server/lib/emailer.ts36
-rw-r--r--server/lib/hls.ts6
-rw-r--r--server/lib/job-queue/handlers/activitypub-follow.ts9
-rw-r--r--server/lib/job-queue/handlers/activitypub-http-fetcher.ts5
-rw-r--r--server/lib/job-queue/handlers/utils/activitypub-http-utils.ts4
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts11
-rw-r--r--server/lib/job-queue/handlers/video-import.ts38
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts7
-rw-r--r--server/lib/notifier.ts101
-rw-r--r--server/lib/oauth-model.ts3
-rw-r--r--server/lib/peertube-socket.ts4
-rw-r--r--server/lib/redundancy.ts3
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts52
-rw-r--r--server/lib/thumbnail.ts26
-rw-r--r--server/lib/user.ts26
-rw-r--r--server/lib/video-blacklist.ts13
-rw-r--r--server/lib/video-channel.ts17
-rw-r--r--server/lib/video-comment.ts11
-rw-r--r--server/lib/video-playlist.ts7
-rw-r--r--server/lib/video-transcoding.ts16
58 files changed, 643 insertions, 513 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index 9f5d12eb4..7862b0f00 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -22,13 +22,25 @@ import { JobQueue } from '../job-queue'
22import { getServerActor } from '../../helpers/utils' 22import { getServerActor } from '../../helpers/utils'
23import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' 23import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
24import { sequelizeTypescript } from '../../initializers/database' 24import { sequelizeTypescript } from '../../initializers/database'
25import {
26 MAccount,
27 MActor,
28 MActorAccountChannelId,
29 MActorAccountId,
30 MActorDefault,
31 MActorFull,
32 MActorId,
33 MActorAccountChannelIdActor,
34 MChannel,
35 MActorFullActor, MAccountActorDefault, MChannelActorDefault, MChannelActorAccountDefault
36} from '../../typings/models'
25 37
26// Set account keys, this could be long so process after the account creation and do not block the client 38// Set account keys, this could be long so process after the account creation and do not block the client
27function setAsyncActorKeys (actor: ActorModel) { 39function setAsyncActorKeys (actor: MActor) {
28 return createPrivateAndPublicKeys() 40 return createPrivateAndPublicKeys()
29 .then(({ publicKey, privateKey }) => { 41 .then(({ publicKey, privateKey }) => {
30 actor.set('publicKey', publicKey) 42 actor.publicKey = publicKey
31 actor.set('privateKey', privateKey) 43 actor.privateKey = privateKey
32 return actor.save() 44 return actor.save()
33 }) 45 })
34 .catch(err => { 46 .catch(err => {
@@ -37,12 +49,26 @@ function setAsyncActorKeys (actor: ActorModel) {
37 }) 49 })
38} 50}
39 51
52function getOrCreateActorAndServerAndModel (
53 activityActor: string | ActivityPubActor,
54 fetchType: 'all',
55 recurseIfNeeded?: boolean,
56 updateCollections?: boolean
57): Promise<MActorFullActor>
58
59function getOrCreateActorAndServerAndModel (
60 activityActor: string | ActivityPubActor,
61 fetchType?: 'association-ids',
62 recurseIfNeeded?: boolean,
63 updateCollections?: boolean
64): Promise<MActorAccountChannelId>
65
40async function getOrCreateActorAndServerAndModel ( 66async function getOrCreateActorAndServerAndModel (
41 activityActor: string | ActivityPubActor, 67 activityActor: string | ActivityPubActor,
42 fetchType: ActorFetchByUrlType = 'actor-and-association-ids', 68 fetchType: ActorFetchByUrlType = 'association-ids',
43 recurseIfNeeded = true, 69 recurseIfNeeded = true,
44 updateCollections = false 70 updateCollections = false
45) { 71): Promise<MActorFullActor | MActorAccountChannelId> {
46 const actorUrl = getAPId(activityActor) 72 const actorUrl = getAPId(activityActor)
47 let created = false 73 let created = false
48 let accountPlaylistsUrl: string 74 let accountPlaylistsUrl: string
@@ -61,7 +87,7 @@ async function getOrCreateActorAndServerAndModel (
61 87
62 // Create the attributed to actor 88 // Create the attributed to actor
63 // In PeerTube a video channel is owned by an account 89 // In PeerTube a video channel is owned by an account
64 let ownerActor: ActorModel = undefined 90 let ownerActor: MActorFullActor
65 if (recurseIfNeeded === true && result.actor.type === 'Group') { 91 if (recurseIfNeeded === true && result.actor.type === 'Group') {
66 const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') 92 const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person')
67 if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) 93 if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url)
@@ -85,8 +111,8 @@ async function getOrCreateActorAndServerAndModel (
85 accountPlaylistsUrl = result.playlists 111 accountPlaylistsUrl = result.playlists
86 } 112 }
87 113
88 if (actor.Account) actor.Account.Actor = actor 114 if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
89 if (actor.VideoChannel) actor.VideoChannel.Actor = actor 115 if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
90 116
91 const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType) 117 const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
92 if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') 118 if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.')
@@ -140,7 +166,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ
140 actorInstance.followingUrl = attributes.following 166 actorInstance.followingUrl = attributes.following
141} 167}
142 168
143async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { 169type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string }
170async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) {
144 if (info.name !== undefined) { 171 if (info.name !== undefined) {
145 if (actor.avatarId) { 172 if (actor.avatarId) {
146 try { 173 try {
@@ -212,14 +239,16 @@ async function addFetchOutboxJob (actor: Pick<ActorModel, 'id' | 'outboxUrl'>) {
212 return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }) 239 return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
213} 240}
214 241
215async function refreshActorIfNeeded ( 242async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannelId> (
216 actorArg: ActorModel, 243 actorArg: T,
217 fetchedType: ActorFetchByUrlType 244 fetchedType: ActorFetchByUrlType
218): Promise<{ actor: ActorModel, refreshed: boolean }> { 245): Promise<{ actor: T | MActorFull, refreshed: boolean }> {
219 if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } 246 if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
220 247
221 // We need more attributes 248 // We need more attributes
222 const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url) 249 const actor = fetchedType === 'all'
250 ? actorArg as MActorFull
251 : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url)
223 252
224 try { 253 try {
225 let actorUrl: string 254 let actorUrl: string
@@ -297,9 +326,9 @@ export {
297 326
298function saveActorAndServerAndModelIfNotExist ( 327function saveActorAndServerAndModelIfNotExist (
299 result: FetchRemoteActorResult, 328 result: FetchRemoteActorResult,
300 ownerActor?: ActorModel, 329 ownerActor?: MActorFullActor,
301 t?: Transaction 330 t?: Transaction
302): Bluebird<ActorModel> | Promise<ActorModel> { 331): Bluebird<MActorFullActor> | Promise<MActorFullActor> {
303 let actor = result.actor 332 let actor = result.actor
304 333
305 if (t !== undefined) return save(t) 334 if (t !== undefined) return save(t)
@@ -336,7 +365,7 @@ function saveActorAndServerAndModelIfNotExist (
336 365
337 // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists 366 // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists
338 // (which could be false in a retried query) 367 // (which could be false in a retried query)
339 const [ actorCreated ] = await ActorModel.findOrCreate({ 368 const [ actorCreated ] = await ActorModel.findOrCreate<MActorFullActor>({
340 defaults: actor.toJSON(), 369 defaults: actor.toJSON(),
341 where: { 370 where: {
342 url: actor.url 371 url: actor.url
@@ -345,10 +374,10 @@ function saveActorAndServerAndModelIfNotExist (
345 }) 374 })
346 375
347 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { 376 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
348 actorCreated.Account = await saveAccount(actorCreated, result, t) 377 actorCreated.Account = await saveAccount(actorCreated, result, t) as MAccountActorDefault
349 actorCreated.Account.Actor = actorCreated 378 actorCreated.Account.Actor = actorCreated
350 } else if (actorCreated.type === 'Group') { // Video channel 379 } else if (actorCreated.type === 'Group') { // Video channel
351 actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) 380 actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) as MChannelActorAccountDefault
352 actorCreated.VideoChannel.Actor = actorCreated 381 actorCreated.VideoChannel.Actor = actorCreated
353 actorCreated.VideoChannel.Account = ownerActor.Account 382 actorCreated.VideoChannel.Account = ownerActor.Account
354 } 383 }
@@ -360,7 +389,7 @@ function saveActorAndServerAndModelIfNotExist (
360} 389}
361 390
362type FetchRemoteActorResult = { 391type FetchRemoteActorResult = {
363 actor: ActorModel 392 actor: MActor
364 name: string 393 name: string
365 summary: string 394 summary: string
366 support?: string 395 support?: string
@@ -429,7 +458,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe
429 } 458 }
430} 459}
431 460
432async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { 461async function saveAccount (actor: MActorId, result: FetchRemoteActorResult, t: Transaction) {
433 const [ accountCreated ] = await AccountModel.findOrCreate({ 462 const [ accountCreated ] = await AccountModel.findOrCreate({
434 defaults: { 463 defaults: {
435 name: result.name, 464 name: result.name,
@@ -442,10 +471,10 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t
442 transaction: t 471 transaction: t
443 }) 472 })
444 473
445 return accountCreated 474 return accountCreated as MAccount
446} 475}
447 476
448async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { 477async function saveVideoChannel (actor: MActorId, result: FetchRemoteActorResult, ownerActor: MActorAccountId, t: Transaction) {
449 const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({ 478 const [ videoChannelCreated ] = await VideoChannelModel.findOrCreate({
450 defaults: { 479 defaults: {
451 name: result.name, 480 name: result.name,
@@ -460,5 +489,5 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
460 transaction: t 489 transaction: t
461 }) 490 })
462 491
463 return videoChannelCreated 492 return videoChannelCreated as MChannel
464} 493}
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts
index 0e3d78590..f2ab54cf7 100644
--- a/server/lib/activitypub/audience.ts
+++ b/server/lib/activitypub/audience.ts
@@ -3,11 +3,10 @@ import { ActivityAudience } from '../../../shared/models/activitypub'
3import { ACTIVITY_PUB } from '../../initializers/constants' 3import { ACTIVITY_PUB } from '../../initializers/constants'
4import { ActorModel } from '../../models/activitypub/actor' 4import { ActorModel } from '../../models/activitypub/actor'
5import { VideoModel } from '../../models/video/video' 5import { VideoModel } from '../../models/video/video'
6import { VideoCommentModel } from '../../models/video/video-comment'
7import { VideoShareModel } from '../../models/video/video-share' 6import { VideoShareModel } from '../../models/video/video-share'
8import { ActorModelOnly } from '../../typings/models' 7import { MActorFollowersUrl, MActorLight, MCommentOwner, MCommentOwnerVideo, MVideo, MVideoAccountLight } from '../../typings/models'
9 8
10function getRemoteVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]): ActivityAudience { 9function 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
17function getVideoCommentAudience ( 16function 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
45function getAudienceFromFollowersOf (actorsInvolvedInObject: ActorModel[]): ActivityAudience { 44function 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
52async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { 51async 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
64function getAudience (actorSender: ActorModelOnly, isPublic = true) { 65function 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 @@
1import { CacheFileObject } from '../../../shared/index' 1import { CacheFileObject } from '../../../shared/index'
2import { VideoModel } from '../../models/video/video'
3import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 2import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
4import { Transaction } from 'sequelize' 3import { Transaction } from 'sequelize'
5import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' 4import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
5import { MActorId, MVideoRedundancy, MVideoWithAllFiles } from '@server/typings/models'
6 6
7function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) { 7function 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
42async function createOrUpdateCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { 42async 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
52function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }, t: Transaction) { 52function 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
58function updateCacheFile ( 58function updateCacheFile (
59 cacheFileObject: CacheFileObject, 59 cacheFileObject: CacheFileObject,
60 redundancyModel: VideoRedundancyModel, 60 redundancyModel: MVideoRedundancy,
61 video: VideoModel, 61 video: MVideoWithAllFiles,
62 byActor: { id?: number }, 62 byActor: MActorId,
63 t: Transaction 63 t: Transaction
64) { 64) {
65 if (redundancyModel.actorId !== byActor.id) { 65 if (redundancyModel.actorId !== byActor.id) {
diff --git a/server/lib/activitypub/playlist.ts b/server/lib/activitypub/playlist.ts
index c2e2a3283..c52b715ef 100644
--- a/server/lib/activitypub/playlist.ts
+++ b/server/lib/activitypub/playlist.ts
@@ -1,7 +1,6 @@
1import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' 1import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
2import { crawlCollectionPage } from './crawl' 2import { crawlCollectionPage } from './crawl'
3import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 3import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
4import { AccountModel } from '../../models/account/account'
5import { isArray } from '../../helpers/custom-validators/misc' 4import { isArray } from '../../helpers/custom-validators/misc'
6import { getOrCreateActorAndServerAndModel } from './actor' 5import { getOrCreateActorAndServerAndModel } from './actor'
7import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
@@ -13,14 +12,14 @@ import { PlaylistElementObject } from '../../../shared/models/activitypub/object
13import { getOrCreateVideoAndAccountAndChannel } from './videos' 12import { getOrCreateVideoAndAccountAndChannel } from './videos'
14import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist' 13import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '../../helpers/custom-validators/activitypub/playlist'
15import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element' 14import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
16import { VideoModel } from '../../models/video/video'
17import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 15import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
18import { sequelizeTypescript } from '../../initializers/database' 16import { sequelizeTypescript } from '../../initializers/database'
19import { createPlaylistMiniatureFromUrl } from '../thumbnail' 17import { createPlaylistMiniatureFromUrl } from '../thumbnail'
20import { FilteredModelAttributes } from '../../typings/sequelize' 18import { FilteredModelAttributes } from '../../typings/sequelize'
21import { AccountModelId } from '../../typings/models' 19import { MAccountDefault, MAccountId, MVideoId } from '../../typings/models'
20import { MVideoPlaylist, MVideoPlaylistId, MVideoPlaylistOwner } from '../../typings/models/video/video-playlist'
22 21
23function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { 22function 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
39function playlistElementObjectToDBAttributes (elementObject: PlaylistElementObject, videoPlaylist: VideoPlaylistModel, video: VideoModel) { 38function 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
50async function createAccountPlaylists (playlistUrls: string[], account: AccountModel) { 49async 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
78async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: AccountModelId, to: string[]) { 77async 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
117async function refreshVideoPlaylistIfNeeded (videoPlaylist: VideoPlaylistModel): Promise<VideoPlaylistModel> { 116async 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
160async function resetVideoPlaylistElements (elementUrls: string[], playlist: VideoPlaylistModel) { 159async function resetVideoPlaylistElements (elementUrls: string[], playlist: MVideoPlaylist) {
161 const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = [] 160 const elementsToCreate: FilteredModelAttributes<VideoPlaylistElementModel>[] = []
162 161
163 await Bluebird.map(elementUrls, async elementUrl => { 162 await Bluebird.map(elementUrls, async elementUrl => {
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts
index cf27e6c32..86f7c764d 100644
--- a/server/lib/activitypub/process/process-accept.ts
+++ b/server/lib/activitypub/process/process-accept.ts
@@ -1,9 +1,8 @@
1import { ActivityAccept } from '../../../../shared/models/activitypub' 1import { ActivityAccept } from '../../../../shared/models/activitypub'
2import { ActorModel } from '../../../models/activitypub/actor'
3import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 2import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
4import { addFetchOutboxJob } from '../actor' 3import { addFetchOutboxJob } from '../actor'
5import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 4import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
6import { SignatureActorModel } from '../../../typings/models' 5import { MActorDefault, MActorSignature } from '../../../typings/models'
7 6
8async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { 7async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) {
9 const { byActor: targetActor, inboxActor } = options 8 const { byActor: targetActor, inboxActor } = options
@@ -20,7 +19,7 @@ export {
20 19
21// --------------------------------------------------------------------------- 20// ---------------------------------------------------------------------------
22 21
23async function processAccept (actor: ActorModel, targetActor: SignatureActorModel) { 22async function processAccept (actor: MActorDefault, targetActor: MActorSignature) {
24 const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) 23 const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id)
25 if (!follow) throw new Error('Cannot find associated follow.') 24 if (!follow) throw new Error('Cannot find associated follow.')
26 25
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts
index b3cdc4441..91a9ad72c 100644
--- a/server/lib/activitypub/process/process-announce.ts
+++ b/server/lib/activitypub/process/process-announce.ts
@@ -5,10 +5,9 @@ import { VideoShareModel } from '../../../models/video/video-share'
5import { forwardVideoRelatedActivity } from '../send/utils' 5import { forwardVideoRelatedActivity } from '../send/utils'
6import { getOrCreateVideoAndAccountAndChannel } from '../videos' 6import { getOrCreateVideoAndAccountAndChannel } from '../videos'
7import { Notifier } from '../../notifier' 7import { Notifier } from '../../notifier'
8import { VideoModel } from '../../../models/video/video'
9import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
10import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 9import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
11import { SignatureActorModel } from '../../../typings/models' 10import { MActorSignature, MVideoAccountAllFiles } from '../../../typings/models'
12 11
13async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) { 12async 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
29async function processVideoShare (actorAnnouncer: SignatureActorModel, activity: ActivityAnnounce, notify: boolean) { 28async function processVideoShare (actorAnnouncer: MActorSignature, activity: ActivityAnnounce, notify: boolean) {
30 const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id 29 const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
31 30
32 let video: VideoModel 31 let video: MVideoAccountAllFiles
33 let videoCreated: boolean 32 let videoCreated: boolean
34 33
35 try { 34 try {
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 6815c6997..c45f09f52 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -10,10 +10,8 @@ import { createOrUpdateCacheFile } from '../cache-file'
10import { Notifier } from '../../notifier' 10import { Notifier } from '../../notifier'
11import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' 11import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
12import { createOrUpdateVideoPlaylist } from '../playlist' 12import { createOrUpdateVideoPlaylist } from '../playlist'
13import { VideoModel } from '../../../models/video/video'
14import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 13import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
15import { VideoCommentModel } from '../../../models/video/video-comment' 14import { MActorSignature, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../../typings/models'
16import { SignatureActorModel } from '../../../typings/models'
17 15
18async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { 16async 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
64async function processCreateCacheFile (activity: ActivityCreate, byActor: SignatureActorModel) { 62async 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
80async function processCreateVideoComment (activity: ActivityCreate, byActor: SignatureActorModel, notify: boolean) { 78async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) {
81 const commentObject = activity.object as VideoCommentObject 79 const commentObject = activity.object as VideoCommentObject
82 const byAccount = byActor.Account 80 const byAccount = byActor.Account
83 81
84 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) 82 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
85 83
86 let video: VideoModel 84 let video: MVideoAccountAllFiles
87 let created: boolean 85 let created: boolean
88 let comment: VideoCommentModel 86 let comment: MCommentOwnerVideo
89 try { 87 try {
90 const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false }) 88 const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false })
91 video = resolveThreadResult.video 89 video = resolveThreadResult.video
@@ -110,7 +108,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Sig
110 if (created && notify) Notifier.Instance.notifyOnNewComment(comment) 108 if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
111} 109}
112 110
113async function processCreatePlaylist (activity: ActivityCreate, byActor: SignatureActorModel) { 111async 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'
2import { retryTransactionWrapper } from '../../../helpers/database-utils' 2import { retryTransactionWrapper } from '../../../helpers/database-utils'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { sequelizeTypescript } from '../../../initializers' 4import { sequelizeTypescript } from '../../../initializers'
5import { AccountModel } from '../../../models/account/account'
6import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
7import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
8import { VideoChannelModel } from '../../../models/video/video-channel'
9import { VideoCommentModel } from '../../../models/video/video-comment' 7import { VideoCommentModel } from '../../../models/video/video-comment'
10import { forwardVideoRelatedActivity } from '../send/utils' 8import { forwardVideoRelatedActivity } from '../send/utils'
11import { VideoPlaylistModel } from '../../../models/video/video-playlist' 9import { VideoPlaylistModel } from '../../../models/video/video-playlist'
12import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 10import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
13import { SignatureActorModel } from '../../../typings/models' 11import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor } from '../../../typings/models'
14 12
15async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) { 13async 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
73async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) { 75async 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
87async function processDeleteVideoPlaylist (actor: ActorModel, playlistToDelete: VideoPlaylistModel) { 89async 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
101async function processDeleteAccount (accountToRemove: AccountModel) { 103async 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
111async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { 113async 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
121function processDeleteVideoComment (byActor: SignatureActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { 123function 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'
7import { forwardVideoRelatedActivity } from '../send/utils' 7import { forwardVideoRelatedActivity } from '../send/utils'
8import { getVideoDislikeActivityPubUrl } from '../url' 8import { getVideoDislikeActivityPubUrl } from '../url'
9import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 9import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
10import { SignatureActorModel } from '../../../typings/models' 10import { MActorSignature } from '../../../typings/models'
11 11
12async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { 12async 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
25async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: SignatureActorModel) { 25async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) {
26 const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object 26 const dislikeObject = activity.type === 'Dislike' ? activity.object : (activity.object as DislikeObject).object
27 const byAccount = byActor.Account 27 const byAccount = byActor.Account
28 28
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts
index 1f8a80c14..422386540 100644
--- a/server/lib/activitypub/process/process-flag.ts
+++ b/server/lib/activitypub/process/process-flag.ts
@@ -8,7 +8,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
8import { Notifier } from '../../notifier' 8import { Notifier } from '../../notifier'
9import { getAPId } from '../../../helpers/activitypub' 9import { getAPId } from '../../../helpers/activitypub'
10import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 10import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
11import { SignatureActorModel } from '../../../typings/models' 11import { MActorSignature, MVideoAbuseVideo } from '../../../typings/models'
12 12
13async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { 13async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) {
14 const { activity, byActor } = options 14 const { activity, byActor } = options
@@ -23,7 +23,7 @@ export {
23 23
24// --------------------------------------------------------------------------- 24// ---------------------------------------------------------------------------
25 25
26async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: SignatureActorModel) { 26async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) {
27 const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject) 27 const flag = activity.type === 'Flag' ? activity : (activity.object as VideoAbuseObject)
28 28
29 logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object)) 29 logger.debug('Reporting remote abuse for video %s.', getAPId(flag.object))
@@ -41,7 +41,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag,
41 state: VideoAbuseState.PENDING 41 state: VideoAbuseState.PENDING
42 } 42 }
43 43
44 const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) 44 const videoAbuseInstance = await VideoAbuseModel.create(videoAbuseData, { transaction: t }) as MVideoAbuseVideo
45 videoAbuseInstance.Video = video 45 videoAbuseInstance.Video = video
46 46
47 logger.info('Remote abuse for video uuid %s created', flag.object) 47 logger.info('Remote abuse for video uuid %s created', flag.object)
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index 240aa5799..bc5660395 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -10,8 +10,7 @@ import { getAPId } from '../../../helpers/activitypub'
10import { getServerActor } from '../../../helpers/utils' 10import { getServerActor } from '../../../helpers/utils'
11import { CONFIG } from '../../../initializers/config' 11import { CONFIG } from '../../../initializers/config'
12import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 12import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
13import { SignatureActorModel } from '../../../typings/models' 13import { MAccount, MActorFollowActors, MActorFollowFull, MActorSignature } from '../../../typings/models'
14import { ActorFollowModelLight } from '../../../typings/models/actor-follow'
15 14
16async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { 15async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) {
17 const { activity, byActor } = options 16 const { activity, byActor } = options
@@ -28,7 +27,7 @@ export {
28 27
29// --------------------------------------------------------------------------- 28// ---------------------------------------------------------------------------
30 29
31async function processFollow (byActor: SignatureActorModel, targetActorURL: string) { 30async function processFollow (byActor: MActorSignature, targetActorURL: string) {
32 const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => { 31 const { actorFollow, created, isFollowingInstance } = await sequelizeTypescript.transaction(async t => {
33 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t) 32 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
34 33
@@ -43,10 +42,10 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
43 42
44 await sendReject(byActor, targetActor) 43 await sendReject(byActor, targetActor)
45 44
46 return { actorFollow: undefined } 45 return { actorFollow: undefined as MActorFollowActors }
47 } 46 }
48 47
49 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate({ 48 const [ actorFollow, created ] = await ActorFollowModel.findOrCreate<MActorFollowActors>({
50 where: { 49 where: {
51 actorId: byActor.id, 50 actorId: byActor.id,
52 targetActorId: targetActor.id 51 targetActorId: targetActor.id
@@ -57,7 +56,7 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
57 state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted' 56 state: CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL ? 'pending' : 'accepted'
58 }, 57 },
59 transaction: t 58 transaction: t
60 }) as [ ActorFollowModelLight, boolean ] 59 })
61 60
62 if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) { 61 if (actorFollow.state !== 'accepted' && CONFIG.FOLLOWERS.INSTANCE.MANUAL_APPROVAL === false) {
63 actorFollow.state = 'accepted' 62 actorFollow.state = 'accepted'
@@ -77,8 +76,14 @@ async function processFollow (byActor: SignatureActorModel, targetActorURL: stri
77 if (!actorFollow) return 76 if (!actorFollow) return
78 77
79 if (created) { 78 if (created) {
80 if (isFollowingInstance) Notifier.Instance.notifyOfNewInstanceFollow(actorFollow) 79 if (isFollowingInstance) {
81 else Notifier.Instance.notifyOfNewUserFollow(actorFollow) 80 Notifier.Instance.notifyOfNewInstanceFollow(actorFollow)
81 } else {
82 const actorFollowFull = actorFollow as MActorFollowFull
83 actorFollowFull.ActorFollower.Account = await actorFollow.ActorFollower.$get('Account') as MAccount
84
85 Notifier.Instance.notifyOfNewUserFollow(actorFollowFull)
86 }
82 } 87 }
83 88
84 logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url) 89 logger.info('Actor %s is followed by actor %s.', targetActorURL, byActor.url)
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts
index cf559af72..62be0de42 100644
--- a/server/lib/activitypub/process/process-like.ts
+++ b/server/lib/activitypub/process/process-like.ts
@@ -7,7 +7,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
7import { getVideoLikeActivityPubUrl } from '../url' 7import { getVideoLikeActivityPubUrl } from '../url'
8import { getAPId } from '../../../helpers/activitypub' 8import { getAPId } from '../../../helpers/activitypub'
9import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 9import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
10import { SignatureActorModel } from '../../../typings/models' 10import { MActorSignature } from '../../../typings/models'
11 11
12async function processLikeActivity (options: APProcessorOptions<ActivityLike>) { 12async 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
25async function processLikeVideo (byActor: SignatureActorModel, activity: ActivityLike) { 25async 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'
2import { sequelizeTypescript } from '../../../initializers' 2import { sequelizeTypescript } from '../../../initializers'
3import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 3import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
4import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 4import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
5import { ActorModelOnly } from '../../../typings/models' 5import { MActor } from '../../../typings/models'
6 6
7async function processRejectActivity (options: APProcessorOptions<ActivityReject>) { 7async 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
22async function processReject (follower: ActorModelOnly, targetActor: ActorModelOnly) { 22async 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'
11import { VideoShareModel } from '../../../models/video/video-share' 11import { VideoShareModel } from '../../../models/video/video-share'
12import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' 12import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
13import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 13import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
14import { SignatureActorModel } from '../../../typings/models' 14import { MActorSignature } from '../../../typings/models'
15 15
16async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { 16async 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
57async function processUndoLike (byActor: SignatureActorModel, activity: ActivityUndo) { 57async 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
80async function processUndoDislike (byActor: SignatureActorModel, activity: ActivityUndo) { 80async 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
105async function processUndoCacheFile (byActor: SignatureActorModel, activity: ActivityUndo) { 105async 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
130function processUndoFollow (follower: SignatureActorModel, followActivity: ActivityFollow) { 130function 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
143function processUndoAnnounce (byActor: SignatureActorModel, announceActivity: ActivityAnnounce) { 143function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) {
144 return sequelizeTypescript.transaction(async t => { 144 return sequelizeTypescript.transaction(async t => {
145 const share = await VideoShareModel.loadByUrl(announceActivity.id, t) 145 const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
146 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) 146 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 414f9e375..9f80a0ce9 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -15,7 +15,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
15import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' 15import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
16import { createOrUpdateVideoPlaylist } from '../playlist' 16import { createOrUpdateVideoPlaylist } from '../playlist'
17import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 17import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
18import { SignatureActorModel } from '../../../typings/models' 18import { MActorSignature } from '../../../typings/models'
19 19
20async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { 20async 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
56async function processUpdateVideo (actor: SignatureActorModel, activity: ActivityUpdate) { 56async function processUpdateVideo (actor: MActorSignature, activity: ActivityUpdate) {
57 const videoObject = activity.object as VideoTorrentObject 57 const videoObject = activity.object as VideoTorrentObject
58 58
59 if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { 59 if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
@@ -61,20 +61,20 @@ async function processUpdateVideo (actor: SignatureActorModel, activity: Activit
61 return undefined 61 return undefined
62 } 62 }
63 63
64 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false }) 64 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false, fetchType: 'all' })
65 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) 65 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
66 66
67 const updateOptions = { 67 const updateOptions = {
68 video, 68 video,
69 videoObject, 69 videoObject,
70 account: actor.Account, 70 account: channelActor.VideoChannel.Account,
71 channel: channelActor.VideoChannel, 71 channel: channelActor.VideoChannel,
72 overrideTo: activity.to 72 overrideTo: activity.to
73 } 73 }
74 return updateVideoFromAP(updateOptions) 74 return updateVideoFromAP(updateOptions)
75} 75}
76 76
77async function processUpdateCacheFile (byActor: SignatureActorModel, activity: ActivityUpdate) { 77async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) {
78 const cacheFileObject = activity.object as CacheFileObject 78 const cacheFileObject = activity.object as CacheFileObject
79 79
80 if (!isCacheFileObjectValid(cacheFileObject)) { 80 if (!isCacheFileObjectValid(cacheFileObject)) {
@@ -150,7 +150,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate)
150 } 150 }
151} 151}
152 152
153async function processUpdatePlaylist (byActor: SignatureActorModel, activity: ActivityUpdate) { 153async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) {
154 const playlistObject = activity.object as PlaylistObject 154 const playlistObject = activity.object as PlaylistObject
155 const byAccount = byActor.Account 155 const byAccount = byActor.Account
156 156
diff --git a/server/lib/activitypub/process/process-view.ts b/server/lib/activitypub/process/process-view.ts
index e4997b828..df29ee968 100644
--- a/server/lib/activitypub/process/process-view.ts
+++ b/server/lib/activitypub/process/process-view.ts
@@ -3,7 +3,7 @@ import { forwardVideoRelatedActivity } from '../send/utils'
3import { Redis } from '../../redis' 3import { Redis } from '../../redis'
4import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub' 4import { ActivityCreate, ActivityView, ViewObject } from '../../../../shared/models/activitypub'
5import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 5import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
6import { SignatureActorModel } from '../../../typings/models' 6import { MActorSignature } from '../../../typings/models'
7 7
8async function processViewActivity (options: APProcessorOptions<ActivityCreate | ActivityView>) { 8async 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
21async function processCreateView (activity: ActivityView | ActivityCreate, byActor: SignatureActorModel) { 21async 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 @@
1import { Activity, ActivityType } from '../../../../shared/models/activitypub' 1import { Activity, ActivityType } from '../../../../shared/models/activitypub'
2import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub' 2import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { ActorModel } from '../../../models/activitypub/actor'
5import { processAcceptActivity } from './process-accept' 4import { processAcceptActivity } from './process-accept'
6import { processAnnounceActivity } from './process-announce' 5import { processAnnounceActivity } from './process-announce'
7import { processCreateActivity } from './process-create' 6import { processCreateActivity } from './process-create'
@@ -16,7 +15,7 @@ import { processDislikeActivity } from './process-dislike'
16import { processFlagActivity } from './process-flag' 15import { processFlagActivity } from './process-flag'
17import { processViewActivity } from './process-view' 16import { processViewActivity } from './process-view'
18import { APProcessorOptions } from '../../../typings/activitypub-processor.model' 17import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
19import { SignatureActorModel } from '../../../typings/models' 18import { MActorDefault, MActorSignature } from '../../../typings/models'
20 19
21const processActivity: { [ P in ActivityType ]: (options: APProcessorOptions<Activity>) => Promise<any> } = { 20const 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
36async function processActivities ( 35async 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
3import { unicastTo } from './utils' 3import { unicastTo } from './utils'
4import { buildFollowActivity } from './send-follow' 4import { buildFollowActivity } from './send-follow'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { ActorFollowModelLight } from '../../../typings/models/actor-follow' 6import { MActor, MActorFollowActors } from '../../../typings/models'
7import { ActorModelOnly } from '../../../typings/models'
8 7
9async function sendAccept (actorFollow: ActorFollowModelLight) { 8async 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
37function buildAcceptActivity (url: string, byActor: ActorModelOnly, followActivityData: ActivityFollow): ActivityAccept { 36function 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' 2import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
3import { VideoModel } from '../../../models/video/video'
4import { broadcastToFollowers } from './utils' 3import { broadcastToFollowers } from './utils'
5import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' 4import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
6import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
7import { ActorModelOnly } from '../../../typings/models' 6import { MActorLight, MVideo } from '../../../typings/models'
8import { VideoShareModelOnly } from '../../../typings/models/video-share' 7import { MVideoShare } from '../../../typings/models/video'
9 8
10async function buildAnnounceWithVideoAudience ( 9async 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
26async function sendVideoAnnounce (byActor: ActorModelOnly, videoShare: VideoShareModelOnly, video: VideoModel, t: Transaction) { 25async 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
35function buildAnnounceActivity (url: string, byActor: ActorModelOnly, object: string, audience?: ActivityAudience): ActivityAnnounce { 34function 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video'
6import { VideoCommentModel } from '../../../models/video/video-comment' 4import { VideoCommentModel } from '../../../models/video/video-comment'
7import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' 5import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
8import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' 6import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
9import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
10import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
11import { VideoPlaylistModel } from '../../../models/video/video-playlist'
12import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 8import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
13import { getServerActor } from '../../../helpers/utils' 9import { getServerActor } from '../../../helpers/utils'
14import * as Bluebird from 'bluebird' 10import {
15 11 MActorLight,
16async function sendCreateVideo (video: VideoModel, t: Transaction) { 12 MCommentOwnerVideo,
13 MVideoAccountLight,
14 MVideoAP,
15 MVideoPlaylistFull,
16 MVideoRedundancyFileVideo,
17 MVideoRedundancyStreamingPlaylistVideo
18} from '../../../typings/models'
19
20async 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
30async function sendCreateCacheFile (byActor: ActorModel, video: VideoModel, fileRedundancy: VideoRedundancyModel) { 34async 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
41async function sendCreateVideoPlaylist (playlist: VideoPlaylistModel, t: Transaction) { 49async 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
60async function sendCreateVideoComment (comment: VideoCommentModel, t: Transaction) { 68async 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
98function buildCreateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityCreate { 106function 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
124async function sendVideoRelatedCreateActivity (options: { 132async 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video'
5import { VideoCommentModel } from '../../../models/video/video-comment' 4import { VideoCommentModel } from '../../../models/video/video-comment'
6import { VideoShareModel } from '../../../models/video/video-share' 5import { VideoShareModel } from '../../../models/video/video-share'
7import { getDeleteActivityPubUrl } from '../url' 6import { getDeleteActivityPubUrl } from '../url'
8import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' 7import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
9import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' 8import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
10import { logger } from '../../../helpers/logger' 9import { logger } from '../../../helpers/logger'
11import { VideoPlaylistModel } from '../../../models/video/video-playlist'
12import { getServerActor } from '../../../helpers/utils' 10import { getServerActor } from '../../../helpers/utils'
11import { MCommentOwnerVideoReply, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../typings/models/video'
12import { MActorUrl } from '../../../typings/models'
13 13
14async function sendDeleteVideo (video: VideoModel, transaction: Transaction) { 14async 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
45async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { 45async 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
77async function sendDeleteVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { 77async 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
104function buildDeleteActivity (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { 104function 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActorModel } from '../../../models/activitypub/actor'
3import { VideoModel } from '../../../models/video/video'
4import { getVideoDislikeActivityPubUrl } from '../url' 2import { getVideoDislikeActivityPubUrl } from '../url'
5import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
6import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' 4import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
7import { sendVideoRelatedActivity } from './utils' 5import { sendVideoRelatedActivity } from './utils'
8import { audiencify, getAudience } from '../audience' 6import { audiencify, getAudience } from '../audience'
7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
9 8
10async function sendDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { 9async 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
22function buildDislikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityDislike { 21function 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 @@
1import { ActorModel } from '../../../models/activitypub/actor'
2import { VideoModel } from '../../../models/video/video'
3import { VideoAbuseModel } from '../../../models/video/video-abuse'
4import { getVideoAbuseActivityPubUrl } from '../url' 1import { getVideoAbuseActivityPubUrl } from '../url'
5import { unicastTo } from './utils' 2import { unicastTo } from './utils'
6import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
7import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' 4import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub'
8import { audiencify, getAudience } from '../audience' 5import { audiencify, getAudience } from '../audience'
9import { Transaction } from 'sequelize' 6import { Transaction } from 'sequelize'
7import { MActor, MVideoFullLight } from '../../../typings/models'
8import { MVideoAbuseVideo } from '../../../typings/models/video'
10 9
11async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { 10async 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
25function buildFlagActivity (url: string, byActor: ActorModel, videoAbuse: VideoAbuseModel, audience: ActivityAudience): ActivityFlag { 24function buildFlagActivity (url: string, byActor: MActor, videoAbuse: MVideoAbuseVideo, audience: ActivityAudience): ActivityFlag {
26 if (!audience) audience = getAudience(byActor) 25 if (!audience) audience = getAudience(byActor)
27 26
28 const activity = Object.assign( 27 const activity = Object.assign(
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts
index a59ed50cf..6b17b25da 100644
--- a/server/lib/activitypub/send/send-follow.ts
+++ b/server/lib/activitypub/send/send-follow.ts
@@ -4,9 +4,9 @@ import { getActorFollowActivityPubUrl } from '../url'
4import { unicastTo } from './utils' 4import { unicastTo } from './utils'
5import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { Transaction } from 'sequelize' 6import { Transaction } from 'sequelize'
7import { ActorModelOnly } from '../../../typings/models' 7import { MActor, MActorFollowActors } from '../../../typings/models'
8 8
9function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { 9function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
10 const me = actorFollow.ActorFollower 10 const me = actorFollow.ActorFollower
11 const following = actorFollow.ActorFollowing 11 const following = actorFollow.ActorFollowing
12 12
@@ -21,7 +21,7 @@ function sendFollow (actorFollow: ActorFollowModel, t: Transaction) {
21 t.afterCommit(() => unicastTo(data, me, following.inboxUrl)) 21 t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
22} 22}
23 23
24function buildFollowActivity (url: string, byActor: ActorModelOnly, targetActor: ActorModelOnly): ActivityFollow { 24function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow {
25 return { 25 return {
26 type: 'Follow', 26 type: 'Follow',
27 id: url, 27 id: url,
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts
index 35227887a..e84a6f98b 100644
--- a/server/lib/activitypub/send/send-like.ts
+++ b/server/lib/activitypub/send/send-like.ts
@@ -1,13 +1,12 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video'
5import { getVideoLikeActivityPubUrl } from '../url' 3import { getVideoLikeActivityPubUrl } from '../url'
6import { sendVideoRelatedActivity } from './utils' 4import { sendVideoRelatedActivity } from './utils'
7import { audiencify, getAudience } from '../audience' 5import { audiencify, getAudience } from '../audience'
8import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
7import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../typings/models'
9 8
10async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) { 9async 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
22function buildLikeActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityLike { 21function 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 @@
1import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' 1import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
2import { ActorModel } from '../../../models/activitypub/actor'
3import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url' 2import { getActorFollowActivityPubUrl, getActorFollowRejectActivityPubUrl } from '../url'
4import { unicastTo } from './utils' 3import { unicastTo } from './utils'
5import { buildFollowActivity } from './send-follow' 4import { buildFollowActivity } from './send-follow'
6import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
7import { SignatureActorModel } from '../../../typings/models' 6import { MActor } from '../../../typings/models'
8 7
9async function sendReject (follower: SignatureActorModel, following: ActorModel) { 8async 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
34function buildRejectActivity (url: string, byActor: ActorModel, followActivityData: ActivityFollow): ActivityReject { 33function 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'
2import { 2import {
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'
10import { ActorModel } from '../../../models/activitypub/actor'
11import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
12import { VideoModel } from '../../../models/video/video' 11import { VideoModel } from '../../../models/video/video'
13import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' 12import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
14import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' 13import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
@@ -16,13 +15,20 @@ import { audiencify, getAudience } from '../audience'
16import { buildCreateActivity } from './send-create' 15import { buildCreateActivity } from './send-create'
17import { buildFollowActivity } from './send-follow' 16import { buildFollowActivity } from './send-follow'
18import { buildLikeActivity } from './send-like' 17import { buildLikeActivity } from './send-like'
19import { VideoShareModel } from '../../../models/video/video-share'
20import { buildAnnounceWithVideoAudience } from './send-announce' 18import { buildAnnounceWithVideoAudience } from './send-announce'
21import { logger } from '../../../helpers/logger' 19import { logger } from '../../../helpers/logger'
22import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
23import { buildDislikeActivity } from './send-dislike' 20import { buildDislikeActivity } from './send-dislike'
24 21import {
25async 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
31async 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
43async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { 49async 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
55async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transaction) { 61async 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
64async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Transaction) { 70async 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
73async function sendUndoCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel, t: Transaction) { 79async 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
95function undoActivityData ( 101function 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
114async function sendUndoVideoRelatedActivity (options: { 120async function sendUndoVideoRelatedActivity (options: {
115 byActor: ActorModel, 121 byActor: MActor,
116 video: VideoModel, 122 video: MVideoAccountLight,
117 url: string, 123 url: string,
118 activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, 124 activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce,
119 transaction: Transaction 125 transaction: Transaction
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 5bf092894..3a5cc1853 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -2,21 +2,29 @@ import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { AccountModel } from '../../../models/account/account' 4import { AccountModel } from '../../../models/account/account'
5import { ActorModel } from '../../../models/activitypub/actor'
6import { VideoModel } from '../../../models/video/video' 5import { VideoModel } from '../../../models/video/video'
7import { VideoChannelModel } from '../../../models/video/video-channel'
8import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
9import { getUpdateActivityPubUrl } from '../url' 7import { getUpdateActivityPubUrl } from '../url'
10import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' 8import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
11import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' 9import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
12import { logger } from '../../../helpers/logger' 10import { logger } from '../../../helpers/logger'
13import { VideoCaptionModel } from '../../../models/video/video-caption' 11import { VideoCaptionModel } from '../../../models/video/video-caption'
14import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
15import { VideoPlaylistModel } from '../../../models/video/video-playlist'
16import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' 12import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
17import { getServerActor } from '../../../helpers/utils' 13import { getServerActor } from '../../../helpers/utils'
14import {
15 MAccountActor,
16 MActor,
17 MActorLight,
18 MChannelActor,
19 MVideoAP,
20 MVideoAPWithoutCaption,
21 MVideoPlaylistFull,
22 MVideoRedundancyVideo
23} from '../../../typings/models'
24
25async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
26 const video = videoArg as MVideoAP
18 27
19async 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
44async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) { 52async function sendUpdateActor (accountOrChannel: MAccountActor | MChannelActor, t: Transaction) {
45 const byActor = accountOrChannel.Actor 53 const byActor = accountOrChannel.Actor
46 54
47 logger.info('Creating job to update actor %s.', byActor.url) 55 logger.info('Creating job to update actor %s.', byActor.url)
@@ -51,7 +59,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
51 const audience = getAudience(byActor) 59 const audience = getAudience(byActor)
52 const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience) 60 const updateActivity = buildUpdateActivity(url, byActor, accountOrChannelObject, audience)
53 61
54 let actorsInvolved: ActorModel[] 62 let actorsInvolved: MActor[]
55 if (accountOrChannel instanceof AccountModel) { 63 if (accountOrChannel instanceof AccountModel) {
56 // Actors that shared my videos are involved too 64 // Actors that shared my videos are involved too
57 actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t) 65 actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
@@ -65,7 +73,7 @@ async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelMod
65 return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t) 73 return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
66} 74}
67 75
68async function sendUpdateCacheFile (byActor: ActorModel, redundancyModel: VideoRedundancyModel) { 76async 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
83async function sendUpdateVideoPlaylist (videoPlaylist: VideoPlaylistModel, t: Transaction) { 91async 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
116function buildUpdateActivity (url: string, byActor: ActorModel, object: any, audience?: ActivityAudience): ActivityUpdate { 124function 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video'
5import { getVideoLikeActivityPubUrl } from '../url' 4import { getVideoLikeActivityPubUrl } from '../url'
6import { sendVideoRelatedActivity } from './utils' 5import { sendVideoRelatedActivity } from './utils'
7import { audiencify, getAudience } from '../audience' 6import { audiencify, getAudience } from '../audience'
8import { logger } from '../../../helpers/logger' 7import { logger } from '../../../helpers/logger'
8import { MActorAudience, MVideoAccountLight, MVideoUrl } from '@server/typings/models'
9 9
10async function sendView (byActor: ActorModel, video: VideoModel, t: Transaction) { 10async 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
22function buildViewActivity (url: string, byActor: ActorModel, video: VideoModel, audience?: ActivityAudience): ActivityView { 22function 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'
4import { ActorModel } from '../../../models/activitypub/actor' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 5import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
6import { JobQueue } from '../../job-queue' 6import { JobQueue } from '../../job-queue'
7import { VideoModel } from '../../../models/video/video'
8import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' 7import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
9import { getServerActor } from '../../../helpers/utils' 8import { getServerActor } from '../../../helpers/utils'
10import { afterCommitIfTransaction } from '../../../helpers/database-utils' 9import { afterCommitIfTransaction } from '../../../helpers/database-utils'
11import { ActorFollowerException, ActorModelId, ActorModelOnly } from '../../../typings/models' 10import { MActorFollowerException, MActor, MActorId, MActorLight, MVideo, MVideoAccountLight } from '../../../typings/models'
12 11
13async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { 12async 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
41async function forwardVideoRelatedActivity ( 40async 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 (
54async function forwardActivity ( 53async 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
89async function broadcastToFollowers ( 88async 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
101async function broadcastToActors ( 100async 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
112function broadcastTo (uris: string[], data: any, byActor: ActorModelId) { 111function 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
126function unicastTo (data: any, byActor: ActorModelId, toActorUrl: string) { 125function 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
151async function computeFollowerUris (toFollowersOf: ActorModelId[], actorsException: ActorFollowerException[], t: Transaction) { 150async 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
160async function computeUris (toActors: ActorModelOnly[], actorsException: ActorFollowerException[] = []) { 159async 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
173async function buildSharedInboxesException (actorsException: ActorFollowerException[]) { 172async 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { VideoPrivacy } from '../../../shared/models/videos' 2import { VideoPrivacy } from '../../../shared/models/videos'
3import { getServerActor } from '../../helpers/utils' 3import { getServerActor } from '../../helpers/utils'
4import { VideoModel } from '../../models/video/video'
5import { VideoShareModel } from '../../models/video/video-share' 4import { VideoShareModel } from '../../models/video/video-share'
6import { sendUndoAnnounce, sendVideoAnnounce } from './send' 5import { sendUndoAnnounce, sendVideoAnnounce } from './send'
7import { getVideoAnnounceActivityPubUrl } from './url' 6import { getVideoAnnounceActivityPubUrl } from './url'
8import { VideoChannelModel } from '../../models/video/video-channel'
9import * as Bluebird from 'bluebird' 7import * as Bluebird from 'bluebird'
10import { doRequest } from '../../helpers/requests' 8import { doRequest } from '../../helpers/requests'
11import { getOrCreateActorAndServerAndModel } from './actor' 9import { getOrCreateActorAndServerAndModel } from './actor'
12import { logger } from '../../helpers/logger' 10import { logger } from '../../helpers/logger'
13import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 11import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
14import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 12import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
13import { MChannelActor, MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models/video'
15 14
16async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { 15async 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
25async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { 24async 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
33async function addVideoShares (shareUrls: string[], instance: VideoModel) { 36async 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
72async function shareByServer (video: VideoModel, t: Transaction) { 75async 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
91async function shareByVideoChannel (video: VideoModel, t: Transaction) { 94async 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
108async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { 111async 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 @@
1import { WEBSERVER } from '../../initializers/constants' 1import { WEBSERVER } from '../../initializers/constants'
2import { VideoModel } from '../../models/video/video' 2import {
3import { VideoAbuseModel } from '../../models/video/video-abuse' 3 MActor,
4import { VideoCommentModel } from '../../models/video/video-comment' 4 MActorFollowActors,
5import { VideoFileModel } from '../../models/video/video-file' 5 MActorId,
6import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' 6 MActorUrl,
7import { VideoPlaylistModel } from '../../models/video/video-playlist' 7 MCommentId,
8import { ActorModelOnly, ActorModelUrl } from '../../typings/models' 8 MVideoAbuseId,
9import { ActorFollowModelLight } from '../../typings/models/actor-follow' 9 MVideoId,
10 10 MVideoUrl,
11function getVideoActivityPubUrl (video: VideoModel) { 11 MVideoUUID
12} from '../../typings/models'
13import { MVideoPlaylist, MVideoPlaylistUUID } from '../../typings/models/video/video-playlist'
14import { MVideoFileVideoUUID } from '../../typings/models/video/video-file'
15import { MStreamingPlaylist } from '../../typings/models/video/video-streaming-playlist'
16
17function getVideoActivityPubUrl (video: MVideoUUID) {
12 return WEBSERVER.URL + '/videos/watch/' + video.uuid 18 return WEBSERVER.URL + '/videos/watch/' + video.uuid
13} 19}
14 20
15function getVideoPlaylistActivityPubUrl (videoPlaylist: VideoPlaylistModel) { 21function getVideoPlaylistActivityPubUrl (videoPlaylist: MVideoPlaylist) {
16 return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid 22 return WEBSERVER.URL + '/video-playlists/' + videoPlaylist.uuid
17} 23}
18 24
19function getVideoPlaylistElementActivityPubUrl (videoPlaylist: VideoPlaylistModel, video: VideoModel) { 25function 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
23function getVideoCacheFileActivityPubUrl (videoFile: VideoFileModel) { 29function 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
29function getVideoCacheStreamingPlaylistActivityPubUrl (video: VideoModel, playlist: VideoStreamingPlaylistModel) { 35function 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
33function getVideoCommentActivityPubUrl (video: VideoModel, videoComment: VideoCommentModel) { 39function 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
45function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { 51function 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
49function getVideoViewActivityPubUrl (byActor: ActorModelUrl, video: VideoModel) { 55function 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
53function getVideoLikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { 59function getVideoLikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
54 return byActor.url + '/likes/' + video.id 60 return byActor.url + '/likes/' + video.id
55} 61}
56 62
57function getVideoDislikeActivityPubUrl (byActor: ActorModelUrl, video: VideoModel | { id: number }) { 63function getVideoDislikeActivityPubUrl (byActor: MActorUrl, video: MVideoId) {
58 return byActor.url + '/dislikes/' + video.id 64 return byActor.url + '/dislikes/' + video.id
59} 65}
60 66
61function getVideoSharesActivityPubUrl (video: VideoModel) { 67function getVideoSharesActivityPubUrl (video: MVideoUrl) {
62 return video.url + '/announces' 68 return video.url + '/announces'
63} 69}
64 70
65function getVideoCommentsActivityPubUrl (video: VideoModel) { 71function getVideoCommentsActivityPubUrl (video: MVideoUrl) {
66 return video.url + '/comments' 72 return video.url + '/comments'
67} 73}
68 74
69function getVideoLikesActivityPubUrl (video: VideoModel) { 75function getVideoLikesActivityPubUrl (video: MVideoUrl) {
70 return video.url + '/likes' 76 return video.url + '/likes'
71} 77}
72 78
73function getVideoDislikesActivityPubUrl (video: VideoModel) { 79function getVideoDislikesActivityPubUrl (video: MVideoUrl) {
74 return video.url + '/dislikes' 80 return video.url + '/dislikes'
75} 81}
76 82
77function getActorFollowActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { 83function getActorFollowActivityPubUrl (follower: MActor, following: MActorId) {
78 return follower.url + '/follows/' + following.id 84 return follower.url + '/follows/' + following.id
79} 85}
80 86
81function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModelLight) { 87function 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
88function getActorFollowRejectActivityPubUrl (follower: ActorModelOnly, following: ActorModelOnly) { 94function 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
92function getVideoAnnounceActivityPubUrl (byActor: ActorModelOnly, video: VideoModel) { 98function getVideoAnnounceActivityPubUrl (byActor: MActorId, video: MVideoUrl) {
93 return video.url + '/announces/' + byActor.id 99 return video.url + '/announces/' + byActor.id
94} 100}
95 101
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 8d2c1ade3..375ac0aad 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -2,20 +2,20 @@ import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validat
2import { logger } from '../../helpers/logger' 2import { logger } from '../../helpers/logger'
3import { doRequest } from '../../helpers/requests' 3import { doRequest } from '../../helpers/requests'
4import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 4import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
5import { VideoModel } from '../../models/video/video'
6import { VideoCommentModel } from '../../models/video/video-comment' 5import { VideoCommentModel } from '../../models/video/video-comment'
7import { getOrCreateActorAndServerAndModel } from './actor' 6import { getOrCreateActorAndServerAndModel } from './actor'
8import { getOrCreateVideoAndAccountAndChannel } from './videos' 7import { getOrCreateVideoAndAccountAndChannel } from './videos'
9import * as Bluebird from 'bluebird' 8import * as Bluebird from 'bluebird'
10import { checkUrlsSameHost } from '../../helpers/activitypub' 9import { checkUrlsSameHost } from '../../helpers/activitypub'
10import { MCommentOwner, MCommentOwnerVideo, MVideoAccountAllFiles } from '../../typings/models/video'
11 11
12type ResolveThreadParams = { 12type 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}
18type ResolveThreadResult = Promise<{ video: VideoModel, comment: VideoCommentModel, commentCreated: boolean }> 18type ResolveThreadResult = Promise<{ video: MVideoAccountAllFiles, comment: MCommentOwnerVideo, commentCreated: boolean }>
19 19
20async function addVideoComments (commentUrls: string[]) { 20async 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { AccountModel } from '../../models/account/account'
3import { VideoModel } from '../../models/video/video'
4import { sendLike, sendUndoDislike, sendUndoLike } from './send' 2import { sendLike, sendUndoDislike, sendUndoLike } from './send'
5import { VideoRateType } from '../../../shared/models/videos' 3import { VideoRateType } from '../../../shared/models/videos'
6import * as Bluebird from 'bluebird' 4import * as Bluebird from 'bluebird'
@@ -10,11 +8,11 @@ import { logger } from '../../helpers/logger'
10import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 8import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
11import { doRequest } from '../../helpers/requests' 9import { doRequest } from '../../helpers/requests'
12import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 10import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
13import { ActorModel } from '../../models/activitypub/actor'
14import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url' 11import { getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from './url'
15import { sendDislike } from './send/send-dislike' 12import { sendDislike } from './send/send-dislike'
13import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../typings/models'
16 14
17async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRateType) { 15async 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
67async function sendVideoRateChange (account: AccountModel, 65async 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
87function getRateUrl (rateType: VideoRateType, actor: ActorModel, video: VideoModel) { 87function 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
91export { 93export {
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 3a8451a32..5c10f9764 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -24,7 +24,6 @@ import {
24 REMOTE_SCHEME, 24 REMOTE_SCHEME,
25 STATIC_PATHS 25 STATIC_PATHS
26} from '../../initializers/constants' 26} from '../../initializers/constants'
27import { ActorModel } from '../../models/activitypub/actor'
28import { TagModel } from '../../models/video/tag' 27import { TagModel } from '../../models/video/tag'
29import { VideoModel } from '../../models/video/video' 28import { VideoModel } from '../../models/video/video'
30import { VideoFileModel } from '../../models/video/video-file' 29import { VideoFileModel } from '../../models/video/video-file'
@@ -38,7 +37,6 @@ import { JobQueue } from '../job-queue'
38import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher' 37import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher'
39import { createRates } from './video-rates' 38import { createRates } from './video-rates'
40import { addVideoShares, shareVideoByServerAndChannel } from './share' 39import { addVideoShares, shareVideoByServerAndChannel } from './share'
41import { AccountModel } from '../../models/account/account'
42import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video' 40import { fetchVideoByUrl, VideoFetchByUrlType } from '../../helpers/video'
43import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub' 41import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
44import { Notifier } from '../notifier' 42import { Notifier } from '../notifier'
@@ -49,15 +47,33 @@ import { VideoShareModel } from '../../models/video/video-share'
49import { VideoCommentModel } from '../../models/video/video-comment' 47import { VideoCommentModel } from '../../models/video/video-comment'
50import { sequelizeTypescript } from '../../initializers/database' 48import { sequelizeTypescript } from '../../initializers/database'
51import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' 49import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
52import { ThumbnailModel } from '../../models/video/thumbnail'
53import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 50import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
54import { join } from 'path' 51import { join } from 'path'
55import { FilteredModelAttributes } from '../../typings/sequelize' 52import { FilteredModelAttributes } from '../../typings/sequelize'
56import { autoBlacklistVideoIfNeeded } from '../video-blacklist' 53import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
57import { ActorFollowScoreCache } from '../files-cache' 54import { ActorFollowScoreCache } from '../files-cache'
58import { AccountModelIdActor, VideoChannelModelId, VideoChannelModelIdActor } from '../../typings/models' 55import {
56 MAccountActor,
57 MChannelAccountLight,
58 MChannelDefault,
59 MChannelId,
60 MVideo,
61 MVideoAccountAllFiles,
62 MVideoAccountLight,
63 MVideoAP,
64 MVideoAPWithoutCaption,
65 MVideoFile,
66 MVideoFullLight,
67 MVideoId,
68 MVideoTag,
69 MVideoThumbnail,
70 MVideoWithAllFiles
71} from '../../typings/models'
72import { MThumbnail } from '../../typings/models/video/thumbnail'
73
74async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVideo: boolean, transaction?: sequelize.Transaction) {
75 const video = videoArg as MVideoAP
59 76
60async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
61 if ( 77 if (
62 // Check this is not a blacklisted video, or unfederated blacklisted video 78 // Check this is not a blacklisted video, or unfederated blacklisted video
63 (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) && 79 (video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) &&
@@ -102,7 +118,7 @@ async function fetchRemoteVideo (videoUrl: string): Promise<{ response: request.
102 return { response, videoObject: body } 118 return { response, videoObject: body }
103} 119}
104 120
105async function fetchRemoteVideoDescription (video: VideoModel) { 121async function fetchRemoteVideoDescription (video: MVideoAccountLight) {
106 const host = video.VideoChannel.Account.Actor.Server.host 122 const host = video.VideoChannel.Account.Actor.Server.host
107 const path = video.getDescriptionAPIPath() 123 const path = video.getDescriptionAPIPath()
108 const options = { 124 const options = {
@@ -114,14 +130,14 @@ async function fetchRemoteVideoDescription (video: VideoModel) {
114 return body.description ? body.description : '' 130 return body.description ? body.description : ''
115} 131}
116 132
117function fetchRemoteVideoStaticFile (video: VideoModel, path: string, destPath: string) { 133function fetchRemoteVideoStaticFile (video: MVideoAccountLight, path: string, destPath: string) {
118 const url = buildRemoteBaseUrl(video, path) 134 const url = buildRemoteBaseUrl(video, path)
119 135
120 // We need to provide a callback, if no we could have an uncaught exception 136 // We need to provide a callback, if no we could have an uncaught exception
121 return doRequestAndSaveToFile({ uri: url }, destPath) 137 return doRequestAndSaveToFile({ uri: url }, destPath)
122} 138}
123 139
124function buildRemoteBaseUrl (video: VideoModel, path: string) { 140function buildRemoteBaseUrl (video: MVideoAccountLight, path: string) {
125 const host = video.VideoChannel.Account.Actor.Server.host 141 const host = video.VideoChannel.Account.Actor.Server.host
126 142
127 return REMOTE_SCHEME.HTTP + '://' + host + path 143 return REMOTE_SCHEME.HTTP + '://' + host + path
@@ -146,7 +162,7 @@ type SyncParam = {
146 thumbnail: boolean 162 thumbnail: boolean
147 refreshVideo?: boolean 163 refreshVideo?: boolean
148} 164}
149async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) { 165async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoTorrentObject, syncParam: SyncParam) {
150 logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid) 166 logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
151 167
152 const jobPayloads: ActivitypubHttpFetcherPayload[] = [] 168 const jobPayloads: ActivitypubHttpFetcherPayload[] = []
@@ -194,12 +210,24 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid
194 await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) 210 await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }))
195} 211}
196 212
213function getOrCreateVideoAndAccountAndChannel (options: {
214 videoObject: { id: string } | string,
215 syncParam?: SyncParam,
216 fetchType?: 'all',
217 allowRefresh?: boolean
218}): Promise<{ video: MVideoAccountAllFiles, created: boolean, autoBlacklisted?: boolean }>
219function getOrCreateVideoAndAccountAndChannel (options: {
220 videoObject: { id: string } | string,
221 syncParam?: SyncParam,
222 fetchType?: VideoFetchByUrlType,
223 allowRefresh?: boolean
224}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }>
197async function getOrCreateVideoAndAccountAndChannel (options: { 225async function getOrCreateVideoAndAccountAndChannel (options: {
198 videoObject: { id: string } | string, 226 videoObject: { id: string } | string,
199 syncParam?: SyncParam, 227 syncParam?: SyncParam,
200 fetchType?: VideoFetchByUrlType, 228 fetchType?: VideoFetchByUrlType,
201 allowRefresh?: boolean // true by default 229 allowRefresh?: boolean // true by default
202}) { 230}): Promise<{ video: MVideoAccountAllFiles | MVideoThumbnail, created: boolean, autoBlacklisted?: boolean }> {
203 // Default params 231 // Default params
204 const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false } 232 const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
205 const fetchType = options.fetchType || 'all' 233 const fetchType = options.fetchType || 'all'
@@ -227,8 +255,9 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
227 const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl) 255 const { videoObject: fetchedVideo } = await fetchRemoteVideo(videoUrl)
228 if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl) 256 if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
229 257
230 const channelActor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo) 258 const actor = await getOrCreateVideoChannelFromVideoObject(fetchedVideo)
231 const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, channelActor, syncParam.thumbnail) 259 const videoChannel = actor.VideoChannel
260 const { autoBlacklisted, videoCreated } = await retryTransactionWrapper(createVideo, fetchedVideo, videoChannel, syncParam.thumbnail)
232 261
233 await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam) 262 await syncVideoExternalAttributes(videoCreated, fetchedVideo, syncParam)
234 263
@@ -236,22 +265,22 @@ async function getOrCreateVideoAndAccountAndChannel (options: {
236} 265}
237 266
238async function updateVideoFromAP (options: { 267async function updateVideoFromAP (options: {
239 video: VideoModel, 268 video: MVideoAccountAllFiles,
240 videoObject: VideoTorrentObject, 269 videoObject: VideoTorrentObject,
241 account: AccountModelIdActor, 270 account: MAccountActor,
242 channel: VideoChannelModelIdActor, 271 channel: MChannelDefault,
243 overrideTo?: string[] 272 overrideTo?: string[]
244}) { 273}) {
245 const { video, videoObject, account, channel, overrideTo } = options 274 const { video, videoObject, account, channel, overrideTo } = options
246 275
247 logger.debug('Updating remote video "%s".', options.videoObject.uuid) 276 logger.debug('Updating remote video "%s".', options.videoObject.uuid, { account, channel })
248 277
249 let videoFieldsSave: any 278 let videoFieldsSave: any
250 const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE 279 const wasPrivateVideo = video.privacy === VideoPrivacy.PRIVATE
251 const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED 280 const wasUnlistedVideo = video.privacy === VideoPrivacy.UNLISTED
252 281
253 try { 282 try {
254 let thumbnailModel: ThumbnailModel 283 let thumbnailModel: MThumbnail
255 284
256 try { 285 try {
257 thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) 286 thumbnailModel = await createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
@@ -259,7 +288,7 @@ async function updateVideoFromAP (options: {
259 logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }) 288 logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err })
260 } 289 }
261 290
262 await sequelizeTypescript.transaction(async t => { 291 const videoUpdated = await sequelizeTypescript.transaction(async t => {
263 const sequelizeOptions = { transaction: t } 292 const sequelizeOptions = { transaction: t }
264 293
265 videoFieldsSave = video.toJSON() 294 videoFieldsSave = video.toJSON()
@@ -293,21 +322,21 @@ async function updateVideoFromAP (options: {
293 video.channelId = videoData.channelId 322 video.channelId = videoData.channelId
294 video.views = videoData.views 323 video.views = videoData.views
295 324
296 await video.save(sequelizeOptions) 325 const videoUpdated = await video.save(sequelizeOptions) as MVideoFullLight
297 326
298 if (thumbnailModel) await video.addAndSaveThumbnail(thumbnailModel, t) 327 if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t)
299 328
300 // FIXME: use icon URL instead 329 // FIXME: use icon URL instead
301 const previewUrl = buildRemoteBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.getPreview().filename)) 330 const previewUrl = buildRemoteBaseUrl(videoUpdated, join(STATIC_PATHS.PREVIEWS, videoUpdated.getPreview().filename))
302 const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE) 331 const previewModel = createPlaceholderThumbnail(previewUrl, video, ThumbnailType.PREVIEW, PREVIEWS_SIZE)
303 await video.addAndSaveThumbnail(previewModel, t) 332 await videoUpdated.addAndSaveThumbnail(previewModel, t)
304 333
305 { 334 {
306 const videoFileAttributes = videoFileActivityUrlToDBAttributes(video, videoObject) 335 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoUpdated, videoObject)
307 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) 336 const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
308 337
309 // Remove video files that do not exist anymore 338 // Remove video files that do not exist anymore
310 const destroyTasks = video.VideoFiles 339 const destroyTasks = videoUpdated.VideoFiles
311 .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f))) 340 .filter(f => !newVideoFiles.find(newFile => newFile.hasSameUniqueKeysThan(f)))
312 .map(f => f.destroy(sequelizeOptions)) 341 .map(f => f.destroy(sequelizeOptions))
313 await Promise.all(destroyTasks) 342 await Promise.all(destroyTasks)
@@ -318,15 +347,15 @@ async function updateVideoFromAP (options: {
318 .then(([ file ]) => file) 347 .then(([ file ]) => file)
319 }) 348 })
320 349
321 video.VideoFiles = await Promise.all(upsertTasks) 350 videoUpdated.VideoFiles = await Promise.all(upsertTasks)
322 } 351 }
323 352
324 { 353 {
325 const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(video, videoObject, video.VideoFiles) 354 const streamingPlaylistAttributes = streamingPlaylistActivityUrlToDBAttributes(videoUpdated, videoObject, videoUpdated.VideoFiles)
326 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) 355 const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a))
327 356
328 // Remove video files that do not exist anymore 357 // Remove video files that do not exist anymore
329 const destroyTasks = video.VideoStreamingPlaylists 358 const destroyTasks = videoUpdated.VideoStreamingPlaylists
330 .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f))) 359 .filter(f => !newStreamingPlaylists.find(newPlaylist => newPlaylist.hasSameUniqueKeysThan(f)))
331 .map(f => f.destroy(sequelizeOptions)) 360 .map(f => f.destroy(sequelizeOptions))
332 await Promise.all(destroyTasks) 361 await Promise.all(destroyTasks)
@@ -337,38 +366,42 @@ async function updateVideoFromAP (options: {
337 .then(([ streamingPlaylist ]) => streamingPlaylist) 366 .then(([ streamingPlaylist ]) => streamingPlaylist)
338 }) 367 })
339 368
340 video.VideoStreamingPlaylists = await Promise.all(upsertTasks) 369 videoUpdated.VideoStreamingPlaylists = await Promise.all(upsertTasks)
341 } 370 }
342 371
343 { 372 {
344 // Update Tags 373 // Update Tags
345 const tags = videoObject.tag.map(tag => tag.name) 374 const tags = videoObject.tag.map(tag => tag.name)
346 const tagInstances = await TagModel.findOrCreateTags(tags, t) 375 const tagInstances = await TagModel.findOrCreateTags(tags, t)
347 await video.$set('Tags', tagInstances, sequelizeOptions) 376 await videoUpdated.$set('Tags', tagInstances, sequelizeOptions)
348 } 377 }
349 378
350 { 379 {
351 // Update captions 380 // Update captions
352 await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(video.id, t) 381 await VideoCaptionModel.deleteAllCaptionsOfRemoteVideo(videoUpdated.id, t)
353 382
354 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { 383 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
355 return VideoCaptionModel.insertOrReplaceLanguage(video.id, c.identifier, t) 384 return VideoCaptionModel.insertOrReplaceLanguage(videoUpdated.id, c.identifier, t)
356 }) 385 })
357 video.VideoCaptions = await Promise.all(videoCaptionsPromises) 386 await Promise.all(videoCaptionsPromises)
358 } 387 }
388
389 return videoUpdated
359 }) 390 })
360 391
361 await autoBlacklistVideoIfNeeded({ 392 await autoBlacklistVideoIfNeeded({
362 video, 393 video: videoUpdated,
363 user: undefined, 394 user: undefined,
364 isRemote: true, 395 isRemote: true,
365 isNew: false, 396 isNew: false,
366 transaction: undefined 397 transaction: undefined
367 }) 398 })
368 399
369 if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video) // Notify our users? 400 if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users?
370 401
371 logger.info('Remote video with uuid %s updated', videoObject.uuid) 402 logger.info('Remote video with uuid %s updated', videoObject.uuid)
403
404 return videoUpdated
372 } catch (err) { 405 } catch (err) {
373 if (video !== undefined && videoFieldsSave !== undefined) { 406 if (video !== undefined && videoFieldsSave !== undefined) {
374 resetSequelizeInstance(video, videoFieldsSave) 407 resetSequelizeInstance(video, videoFieldsSave)
@@ -381,15 +414,15 @@ async function updateVideoFromAP (options: {
381} 414}
382 415
383async function refreshVideoIfNeeded (options: { 416async function refreshVideoIfNeeded (options: {
384 video: VideoModel, 417 video: MVideoThumbnail,
385 fetchedType: VideoFetchByUrlType, 418 fetchedType: VideoFetchByUrlType,
386 syncParam: SyncParam 419 syncParam: SyncParam
387}): Promise<VideoModel> { 420}): Promise<MVideoThumbnail> {
388 if (!options.video.isOutdated()) return options.video 421 if (!options.video.isOutdated()) return options.video
389 422
390 // We need more attributes if the argument video was fetched with not enough joints 423 // We need more attributes if the argument video was fetched with not enough joints
391 const video = options.fetchedType === 'all' 424 const video = options.fetchedType === 'all'
392 ? options.video 425 ? options.video as MVideoAccountAllFiles
393 : await VideoModel.loadByUrlAndPopulateAccount(options.video.url) 426 : await VideoModel.loadByUrlAndPopulateAccount(options.video.url)
394 427
395 try { 428 try {
@@ -410,12 +443,11 @@ async function refreshVideoIfNeeded (options: {
410 } 443 }
411 444
412 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject) 445 const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
413 const account = await AccountModel.load(channelActor.VideoChannel.accountId)
414 446
415 const updateOptions = { 447 const updateOptions = {
416 video, 448 video,
417 videoObject, 449 videoObject,
418 account, 450 account: channelActor.VideoChannel.Account,
419 channel: channelActor.VideoChannel 451 channel: channelActor.VideoChannel
420 } 452 }
421 await retryTransactionWrapper(updateVideoFromAP, updateOptions) 453 await retryTransactionWrapper(updateVideoFromAP, updateOptions)
@@ -467,15 +499,15 @@ function isAPPlaylistSegmentHashesUrlObject (tag: any): tag is ActivityPlaylistS
467 return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json' 499 return tag.name === 'sha256' && tag.type === 'Link' && urlMediaType === 'application/json'
468} 500}
469 501
470async function createVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) { 502async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) {
471 logger.debug('Adding remote video %s.', videoObject.id) 503 logger.debug('Adding remote video %s.', videoObject.id)
472 504
473 const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to) 505 const videoData = await videoActivityObjectToDBAttributes(channel, videoObject, videoObject.to)
474 const video = VideoModel.build(videoData) 506 const video = VideoModel.build(videoData) as MVideoThumbnail
475 507
476 const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE) 508 const promiseThumbnail = createVideoMiniatureFromUrl(videoObject.icon.url, video, ThumbnailType.MINIATURE)
477 509
478 let thumbnailModel: ThumbnailModel 510 let thumbnailModel: MThumbnail
479 if (waitThumbnail === true) { 511 if (waitThumbnail === true) {
480 thumbnailModel = await promiseThumbnail 512 thumbnailModel = await promiseThumbnail
481 } 513 }
@@ -483,8 +515,8 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
483 const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => { 515 const { autoBlacklisted, videoCreated } = await sequelizeTypescript.transaction(async t => {
484 const sequelizeOptions = { transaction: t } 516 const sequelizeOptions = { transaction: t }
485 517
486 const videoCreated = await video.save(sequelizeOptions) 518 const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight
487 videoCreated.VideoChannel = channelActor.VideoChannel 519 videoCreated.VideoChannel = channel
488 520
489 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) 521 if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t)
490 522
@@ -517,15 +549,14 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
517 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => { 549 const videoCaptionsPromises = videoObject.subtitleLanguage.map(c => {
518 return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t) 550 return VideoCaptionModel.insertOrReplaceLanguage(videoCreated.id, c.identifier, t)
519 }) 551 })
520 const captions = await Promise.all(videoCaptionsPromises) 552 await Promise.all(videoCaptionsPromises)
521 553
522 video.VideoFiles = videoFiles 554 videoCreated.VideoFiles = videoFiles
523 video.VideoStreamingPlaylists = streamingPlaylists 555 videoCreated.VideoStreamingPlaylists = streamingPlaylists
524 video.Tags = tagInstances 556 videoCreated.Tags = tagInstances
525 video.VideoCaptions = captions
526 557
527 const autoBlacklisted = await autoBlacklistVideoIfNeeded({ 558 const autoBlacklisted = await autoBlacklistVideoIfNeeded({
528 video, 559 video: videoCreated,
529 user: undefined, 560 user: undefined,
530 isRemote: true, 561 isRemote: true,
531 isNew: true, 562 isNew: true,
@@ -548,11 +579,7 @@ async function createVideo (videoObject: VideoTorrentObject, channelActor: Actor
548 return { autoBlacklisted, videoCreated } 579 return { autoBlacklisted, videoCreated }
549} 580}
550 581
551async function videoActivityObjectToDBAttributes ( 582async function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObject: VideoTorrentObject, to: string[] = []) {
552 videoChannel: VideoChannelModelId,
553 videoObject: VideoTorrentObject,
554 to: string[] = []
555) {
556 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED 583 const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
557 const duration = videoObject.duration.replace(/[^\d]+/, '') 584 const duration = videoObject.duration.replace(/[^\d]+/, '')
558 585
@@ -603,7 +630,7 @@ async function videoActivityObjectToDBAttributes (
603 } 630 }
604} 631}
605 632
606function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject) { 633function videoFileActivityUrlToDBAttributes (video: MVideo, videoObject: VideoTorrentObject) {
607 const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] 634 const fileUrls = videoObject.url.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[]
608 635
609 if (fileUrls.length === 0) { 636 if (fileUrls.length === 0) {
@@ -641,7 +668,7 @@ function videoFileActivityUrlToDBAttributes (video: VideoModel, videoObject: Vid
641 return attributes 668 return attributes
642} 669}
643 670
644function streamingPlaylistActivityUrlToDBAttributes (video: VideoModel, videoObject: VideoTorrentObject, videoFiles: VideoFileModel[]) { 671function streamingPlaylistActivityUrlToDBAttributes (video: MVideoId, videoObject: VideoTorrentObject, videoFiles: MVideoFile[]) {
645 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[] 672 const playlistUrls = videoObject.url.filter(u => isAPStreamingPlaylistUrlObject(u)) as ActivityPlaylistUrlObject[]
646 if (playlistUrls.length === 0) return [] 673 if (playlistUrls.length === 0) return []
647 674
diff --git a/server/lib/avatar.ts b/server/lib/avatar.ts
index 1b38e6cb5..9005b3e22 100644
--- a/server/lib/avatar.ts
+++ b/server/lib/avatar.ts
@@ -3,8 +3,6 @@ import { sendUpdateActor } from './activitypub/send'
3import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' 3import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
4import { updateActorAvatarInstance } from './activitypub' 4import { updateActorAvatarInstance } from './activitypub'
5import { processImage } from '../helpers/image-utils' 5import { processImage } from '../helpers/image-utils'
6import { AccountModel } from '../models/account/account'
7import { VideoChannelModel } from '../models/video/video-channel'
8import { extname, join } from 'path' 6import { extname, join } from 'path'
9import { retryTransactionWrapper } from '../helpers/database-utils' 7import { retryTransactionWrapper } from '../helpers/database-utils'
10import * as uuidv4 from 'uuid/v4' 8import * as uuidv4 from 'uuid/v4'
@@ -13,8 +11,12 @@ import { sequelizeTypescript } from '../initializers/database'
13import * as LRUCache from 'lru-cache' 11import * as LRUCache from 'lru-cache'
14import { queue } from 'async' 12import { queue } from 'async'
15import { downloadImage } from '../helpers/requests' 13import { downloadImage } from '../helpers/requests'
14import { MAccountActorDefault, MChannelActorDefault } from '../typings/models'
16 15
17async function updateActorAvatarFile (avatarPhysicalFile: Express.Multer.File, accountOrChannel: AccountModel | VideoChannelModel) { 16async function updateActorAvatarFile (
17 avatarPhysicalFile: Express.Multer.File,
18 accountOrChannel: MAccountActorDefault | MChannelActorDefault
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 @@
1import { sequelizeTypescript } from '../initializers' 1import { sequelizeTypescript } from '../initializers'
2import { AccountBlocklistModel } from '../models/account/account-blocklist' 2import { AccountBlocklistModel } from '../models/account/account-blocklist'
3import { ServerBlocklistModel } from '../models/server/server-blocklist' 3import { ServerBlocklistModel } from '../models/server/server-blocklist'
4import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models'
4 5
5function addAccountInBlocklist (byAccountId: number, targetAccountId: number) { 6function 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
23function removeAccountFromBlocklist (accountBlock: AccountBlocklistModel) { 24function 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
29function removeServerFromBlocklist (serverBlock: ServerBlocklistModel) { 30function 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..a33aef26d 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -13,6 +13,7 @@ import { VideoChannelModel } from '../models/video/video-channel'
13import * as Bluebird from 'bluebird' 13import * as Bluebird from 'bluebird'
14import { CONFIG } from '../initializers/config' 14import { CONFIG } from '../initializers/config'
15import { logger } from '../helpers/logger' 15import { logger } from '../helpers/logger'
16import { MAccountActor, MChannelActor, MVideo } from '../typings/models'
16 17
17export class ClientHtml { 18export class ClientHtml {
18 19
@@ -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 73c2bcb1b..fe57a3e4c 100644
--- a/server/lib/emailer.ts
+++ b/server/lib/emailer.ts
@@ -3,16 +3,14 @@ import { isTestInstance } from '../helpers/core-utils'
3import { bunyanLogger, logger } from '../helpers/logger' 3import { bunyanLogger, logger } from '../helpers/logger'
4import { CONFIG } from '../initializers/config' 4import { CONFIG } from '../initializers/config'
5import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
6import { VideoModel } from '../models/video/video'
7import { JobQueue } from './job-queue' 6import { JobQueue } from './job-queue'
8import { EmailPayload } from './job-queue/handlers/email' 7import { EmailPayload } from './job-queue/handlers/email'
9import { readFileSync } from 'fs-extra' 8import { readFileSync } from 'fs-extra'
10import { VideoCommentModel } from '../models/video/video-comment'
11import { VideoAbuseModel } from '../models/video/video-abuse'
12import { VideoBlacklistModel } from '../models/video/video-blacklist' 9import { VideoBlacklistModel } from '../models/video/video-blacklist'
13import { VideoImportModel } from '../models/video/video-import'
14import { ActorFollowModel } from '../models/activitypub/actor-follow'
15import { WEBSERVER } from '../initializers/constants' 10import { WEBSERVER } from '../initializers/constants'
11import { MCommentOwnerVideo, MVideo, MVideoAbuseVideo, MVideoAccountLight, MVideoBlacklistVideo } from '../typings/models/video'
12import { MActorFollowActors, MActorFollowFull, MUser } from '../typings/models'
13import { MVideoImport, MVideoImportVideo } from '@server/typings/models/video/video-import'
16 14
17type SendEmailOptions = { 15type SendEmailOptions = {
18 to: string[] 16 to: string[]
@@ -90,7 +88,7 @@ class Emailer {
90 } 88 }
91 } 89 }
92 90
93 addNewVideoFromSubscriberNotification (to: string[], video: VideoModel) { 91 addNewVideoFromSubscriberNotification (to: string[], video: MVideoAccountLight) {
94 const channelName = video.VideoChannel.getDisplayName() 92 const channelName = video.VideoChannel.getDisplayName()
95 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 93 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
96 94
@@ -111,7 +109,7 @@ class Emailer {
111 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 109 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
112 } 110 }
113 111
114 addNewFollowNotification (to: string[], actorFollow: ActorFollowModel, followType: 'account' | 'channel') { 112 addNewFollowNotification (to: string[], actorFollow: MActorFollowFull, followType: 'account' | 'channel') {
115 const followerName = actorFollow.ActorFollower.Account.getDisplayName() 113 const followerName = actorFollow.ActorFollower.Account.getDisplayName()
116 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName() 114 const followingName = (actorFollow.ActorFollowing.VideoChannel || actorFollow.ActorFollowing.Account).getDisplayName()
117 115
@@ -130,7 +128,7 @@ class Emailer {
130 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 128 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
131 } 129 }
132 130
133 addNewInstanceFollowerNotification (to: string[], actorFollow: ActorFollowModel) { 131 addNewInstanceFollowerNotification (to: string[], actorFollow: MActorFollowActors) {
134 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : '' 132 const awaitingApproval = actorFollow.state === 'pending' ? ' awaiting manual approval.' : ''
135 133
136 const text = `Hi dear admin,\n\n` + 134 const text = `Hi dear admin,\n\n` +
@@ -148,7 +146,7 @@ class Emailer {
148 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 146 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
149 } 147 }
150 148
151 myVideoPublishedNotification (to: string[], video: VideoModel) { 149 myVideoPublishedNotification (to: string[], video: MVideo) {
152 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 150 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
153 151
154 const text = `Hi dear user,\n\n` + 152 const text = `Hi dear user,\n\n` +
@@ -168,7 +166,7 @@ class Emailer {
168 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 166 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
169 } 167 }
170 168
171 myVideoImportSuccessNotification (to: string[], videoImport: VideoImportModel) { 169 myVideoImportSuccessNotification (to: string[], videoImport: MVideoImportVideo) {
172 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath() 170 const videoUrl = WEBSERVER.URL + videoImport.Video.getWatchStaticPath()
173 171
174 const text = `Hi dear user,\n\n` + 172 const text = `Hi dear user,\n\n` +
@@ -188,7 +186,7 @@ class Emailer {
188 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 186 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
189 } 187 }
190 188
191 myVideoImportErrorNotification (to: string[], videoImport: VideoImportModel) { 189 myVideoImportErrorNotification (to: string[], videoImport: MVideoImport) {
192 const importUrl = WEBSERVER.URL + '/my-account/video-imports' 190 const importUrl = WEBSERVER.URL + '/my-account/video-imports'
193 191
194 const text = `Hi dear user,\n\n` + 192 const text = `Hi dear user,\n\n` +
@@ -208,7 +206,7 @@ class Emailer {
208 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 206 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
209 } 207 }
210 208
211 addNewCommentOnMyVideoNotification (to: string[], comment: VideoCommentModel) { 209 addNewCommentOnMyVideoNotification (to: string[], comment: MCommentOwnerVideo) {
212 const accountName = comment.Account.getDisplayName() 210 const accountName = comment.Account.getDisplayName()
213 const video = comment.Video 211 const video = comment.Video
214 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() 212 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
@@ -230,7 +228,7 @@ class Emailer {
230 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 228 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
231 } 229 }
232 230
233 addNewCommentMentionNotification (to: string[], comment: VideoCommentModel) { 231 addNewCommentMentionNotification (to: string[], comment: MCommentOwnerVideo) {
234 const accountName = comment.Account.getDisplayName() 232 const accountName = comment.Account.getDisplayName()
235 const video = comment.Video 233 const video = comment.Video
236 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath() 234 const commentUrl = WEBSERVER.URL + comment.getCommentStaticPath()
@@ -252,7 +250,7 @@ class Emailer {
252 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 250 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
253 } 251 }
254 252
255 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: VideoAbuseModel) { 253 addVideoAbuseModeratorsNotification (to: string[], videoAbuse: MVideoAbuseVideo) {
256 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath() 254 const videoUrl = WEBSERVER.URL + videoAbuse.Video.getWatchStaticPath()
257 255
258 const text = `Hi,\n\n` + 256 const text = `Hi,\n\n` +
@@ -269,7 +267,7 @@ class Emailer {
269 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 267 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
270 } 268 }
271 269
272 addVideoAutoBlacklistModeratorsNotification (to: string[], video: VideoModel) { 270 addVideoAutoBlacklistModeratorsNotification (to: string[], video: MVideo) {
273 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' 271 const VIDEO_AUTO_BLACKLIST_URL = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list'
274 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 272 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
275 273
@@ -292,7 +290,7 @@ class Emailer {
292 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 290 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
293 } 291 }
294 292
295 addNewUserRegistrationNotification (to: string[], user: UserModel) { 293 addNewUserRegistrationNotification (to: string[], user: MUser) {
296 const text = `Hi,\n\n` + 294 const text = `Hi,\n\n` +
297 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` + 295 `User ${user.username} just registered on ${WEBSERVER.HOST} PeerTube instance.\n\n` +
298 `Cheers,\n` + 296 `Cheers,\n` +
@@ -307,7 +305,7 @@ class Emailer {
307 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 305 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
308 } 306 }
309 307
310 addVideoBlacklistNotification (to: string[], videoBlacklist: VideoBlacklistModel) { 308 addVideoBlacklistNotification (to: string[], videoBlacklist: MVideoBlacklistVideo) {
311 const videoName = videoBlacklist.Video.name 309 const videoName = videoBlacklist.Video.name
312 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() 310 const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath()
313 311
@@ -329,7 +327,7 @@ class Emailer {
329 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 327 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
330 } 328 }
331 329
332 addVideoUnblacklistNotification (to: string[], video: VideoModel) { 330 addVideoUnblacklistNotification (to: string[], video: MVideo) {
333 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath() 331 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
334 332
335 const text = 'Hi,\n\n' + 333 const text = 'Hi,\n\n' +
@@ -381,7 +379,7 @@ class Emailer {
381 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload }) 379 return JobQueue.Instance.createJob({ type: 'email', payload: emailPayload })
382 } 380 }
383 381
384 addUserBlockJob (user: UserModel, blocked: boolean, reason?: string) { 382 addUserBlockJob (user: MUser, blocked: boolean, reason?: string) {
385 const reasonString = reason ? ` for the following reason: ${reason}` : '' 383 const reasonString = reason ? ` for the following reason: ${reason}` : ''
386 const blockedWord = blocked ? 'blocked' : 'unblocked' 384 const blockedWord = blocked ? 'blocked' : 'unblocked'
387 const blockedString = `Your account ${user.username} on ${WEBSERVER.HOST} has been ${blockedWord}${reasonString}.` 385 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 @@
1import { VideoModel } from '../models/video/video'
2import { basename, dirname, join } from 'path' 1import { basename, dirname, join } from 'path'
3import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants' 2import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../initializers/constants'
4import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' 3import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra'
@@ -12,6 +11,7 @@ import { flatten, uniq } from 'lodash'
12import { VideoFileModel } from '../models/video/video-file' 11import { VideoFileModel } from '../models/video/video-file'
13import { CONFIG } from '../initializers/config' 12import { CONFIG } from '../initializers/config'
14import { sequelizeTypescript } from '../initializers/database' 13import { sequelizeTypescript } from '../initializers/database'
14import { MVideoWithFile } from '@server/typings/models'
15 15
16async function updateStreamingPlaylistsInfohashesIfNeeded () { 16async 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
31async function updateMasterHLSPlaylist (video: VideoModel) { 31async 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
58async function updateSha256Segments (video: VideoModel) { 58async 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..741b1ffde 100644
--- a/server/lib/job-queue/handlers/activitypub-follow.ts
+++ b/server/lib/job-queue/handlers/activitypub-follow.ts
@@ -10,6 +10,7 @@ import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
10import { ActorModel } from '../../../models/activitypub/actor' 10import { ActorModel } from '../../../models/activitypub/actor'
11import { Notifier } from '../../notifier' 11import { Notifier } from '../../notifier'
12import { sequelizeTypescript } from '../../../initializers/database' 12import { sequelizeTypescript } from '../../../initializers/database'
13import { MActorFollowFull, MActorFull } from '../../../typings/models'
13 14
14export type ActivitypubFollowPayload = { 15export type ActivitypubFollowPayload = {
15 followerActorId: number 16 followerActorId: number
@@ -23,13 +24,13 @@ async function processActivityPubFollow (job: Bull.Job) {
23 24
24 logger.info('Processing ActivityPub follow in job %d.', job.id) 25 logger.info('Processing ActivityPub follow in job %d.', job.id)
25 26
26 let targetActor: ActorModel 27 let targetActor: MActorFull
27 if (!host || host === WEBSERVER.HOST) { 28 if (!host || host === WEBSERVER.HOST) {
28 targetActor = await ActorModel.loadLocalByName(payload.name) 29 targetActor = await ActorModel.loadLocalByName(payload.name)
29 } else { 30 } else {
30 const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP) 31 const sanitizedHost = sanitizeHost(host, REMOTE_SCHEME.HTTP)
31 const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost) 32 const actorUrl = await loadActorUrlOrGetFromWebfinger(payload.name + '@' + sanitizedHost)
32 targetActor = await getOrCreateActorAndServerAndModel(actorUrl) 33 targetActor = await getOrCreateActorAndServerAndModel(actorUrl, 'all')
33 } 34 }
34 35
35 const fromActor = await ActorModel.load(payload.followerActorId) 36 const fromActor = await ActorModel.load(payload.followerActorId)
@@ -44,7 +45,7 @@ export {
44 45
45// --------------------------------------------------------------------------- 46// ---------------------------------------------------------------------------
46 47
47async function follow (fromActor: ActorModel, targetActor: ActorModel) { 48async function follow (fromActor: MActorFull, targetActor: MActorFull) {
48 if (fromActor.id === targetActor.id) { 49 if (fromActor.id === targetActor.id) {
49 throw new Error('Follower is the same than target actor.') 50 throw new Error('Follower is the same than target actor.')
50 } 51 }
@@ -53,7 +54,7 @@ async function follow (fromActor: ActorModel, targetActor: ActorModel) {
53 const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending' 54 const state = !fromActor.serverId && !targetActor.serverId ? 'accepted' : 'pending'
54 55
55 const actorFollow = await sequelizeTypescript.transaction(async t => { 56 const actorFollow = await sequelizeTypescript.transaction(async t => {
56 const [ actorFollow ] = await ActorFollowModel.findOrCreate({ 57 const [ actorFollow ] = await ActorFollowModel.findOrCreate<MActorFollowFull>({
57 where: { 58 where: {
58 actorId: fromActor.id, 59 actorId: fromActor.id,
59 targetActorId: targetActor.id 60 targetActorId: targetActor.id
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'
11import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 11import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
12import { VideoShareModel } from '../../../models/video/video-share' 12import { VideoShareModel } from '../../../models/video/video-share'
13import { VideoCommentModel } from '../../../models/video/video-comment' 13import { VideoCommentModel } from '../../../models/video/video-comment'
14import { MAccountDefault, MVideoFullLight } from '../../../typings/models'
14 15
15type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' 16type 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'
3import { ActorModel } from '../../../../models/activitypub/actor' 3import { ActorModel } from '../../../../models/activitypub/actor'
4import { sha256 } from '../../../../helpers/core-utils' 4import { sha256 } from '../../../../helpers/core-utils'
5import { HTTP_SIGNATURE } from '../../../../initializers/constants' 5import { HTTP_SIGNATURE } from '../../../../initializers/constants'
6import { MActor } from '../../../../typings/models'
6 7
7type Payload = { body: any, signatureActorId?: number } 8type Payload = { body: any, signatureActorId?: number }
8 9
@@ -19,7 +20,8 @@ async function computeBody (payload: Payload) {
19} 20}
20 21
21async function buildSignedRequestOptions (payload: Payload) { 22async 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
6import { copy, stat } from 'fs-extra' 6import { copy, stat } from 'fs-extra'
7import { VideoFileModel } from '../../../models/video/video-file' 7import { VideoFileModel } from '../../../models/video/video-file'
8import { extname } from 'path' 8import { extname } from 'path'
9import { MVideoFile, MVideoWithFile } from '@server/typings/models'
9 10
10export type VideoFileImportPayload = { 11export type VideoFileImportPayload = {
11 videoUUID: string, 12 videoUUID: string,
@@ -37,7 +38,7 @@ export {
37 38
38// --------------------------------------------------------------------------- 39// ---------------------------------------------------------------------------
39 40
40async function updateVideoFile (video: VideoModel, inputFilePath: string) { 41async 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..f9dda79f8 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -17,9 +17,10 @@ import { move, remove, stat } from 'fs-extra'
17import { Notifier } from '../../notifier' 17import { Notifier } from '../../notifier'
18import { CONFIG } from '../../../initializers/config' 18import { CONFIG } from '../../../initializers/config'
19import { sequelizeTypescript } from '../../../initializers/database' 19import { sequelizeTypescript } from '../../../initializers/database'
20import { ThumbnailModel } from '../../../models/video/thumbnail'
21import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail' 20import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail'
22import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' 21import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
22import { MThumbnail } from '../../../typings/models/video/thumbnail'
23import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import'
23 24
24type VideoImportYoutubeDLPayload = { 25type VideoImportYoutubeDLPayload = {
25 type: 'youtube-dl' 26 type: 'youtube-dl'
@@ -110,11 +111,13 @@ type ProcessFileOptions = {
110 generateThumbnail: boolean 111 generateThumbnail: boolean
111 generatePreview: boolean 112 generatePreview: boolean
112} 113}
113async function processFile (downloader: () => Promise<string>, videoImport: VideoImportModel, options: ProcessFileOptions) { 114async function processFile (downloader: () => Promise<string>, videoImportArg: MVideoImportDefault, options: ProcessFileOptions) {
114 let tempVideoPath: string 115 let tempVideoPath: string
115 let videoDestFile: string 116 let videoDestFile: string
116 let videoFile: VideoFileModel 117 let videoFile: VideoFileModel
117 118
119 const videoImport = videoImportArg as MVideoImportDefaultFiles
120
118 try { 121 try {
119 // Download video from youtubeDL 122 // Download video from youtubeDL
120 tempVideoPath = await downloader() 123 tempVideoPath = await downloader()
@@ -148,7 +151,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
148 tempVideoPath = null // This path is not used anymore 151 tempVideoPath = null // This path is not used anymore
149 152
150 // Process thumbnail 153 // Process thumbnail
151 let thumbnailModel: ThumbnailModel 154 let thumbnailModel: MThumbnail
152 if (options.downloadThumbnail && options.thumbnailUrl) { 155 if (options.downloadThumbnail && options.thumbnailUrl) {
153 thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE) 156 thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.MINIATURE)
154 } else if (options.generateThumbnail || options.downloadThumbnail) { 157 } else if (options.generateThumbnail || options.downloadThumbnail) {
@@ -156,7 +159,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
156 } 159 }
157 160
158 // Process preview 161 // Process preview
159 let previewModel: ThumbnailModel 162 let previewModel: MThumbnail
160 if (options.downloadPreview && options.thumbnailUrl) { 163 if (options.downloadPreview && options.thumbnailUrl) {
161 previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW) 164 previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImport.Video, ThumbnailType.PREVIEW)
162 } else if (options.generatePreview || options.downloadPreview) { 165 } else if (options.generatePreview || options.downloadPreview) {
@@ -166,14 +169,15 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
166 // Create torrent 169 // Create torrent
167 await videoImport.Video.createTorrentAndSetInfoHash(videoFile) 170 await videoImport.Video.createTorrentAndSetInfoHash(videoFile)
168 171
169 const videoImportUpdated: VideoImportModel = await sequelizeTypescript.transaction(async t => { 172 const { videoImportUpdated, video } = await sequelizeTypescript.transaction(async t => {
173 const videoImportToUpdate = videoImport as MVideoImportVideo
174
170 // Refresh video 175 // Refresh video
171 const video = await VideoModel.load(videoImport.videoId, t) 176 const video = await VideoModel.load(videoImportToUpdate.videoId, t)
172 if (!video) throw new Error('Video linked to import ' + videoImport.videoId + ' does not exist anymore.') 177 if (!video) throw new Error('Video linked to import ' + videoImportToUpdate.videoId + ' does not exist anymore.')
173 videoImport.Video = video
174 178
175 const videoFileCreated = await videoFile.save({ transaction: t }) 179 const videoFileCreated = await videoFile.save({ transaction: t })
176 video.VideoFiles = [ videoFileCreated ] 180 videoImportToUpdate.Video = Object.assign(video, { VideoFiles: [ videoFileCreated ] })
177 181
178 // Update video DB object 182 // Update video DB object
179 video.duration = duration 183 video.duration = duration
@@ -188,25 +192,25 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
188 await federateVideoIfNeeded(videoForFederation, true, t) 192 await federateVideoIfNeeded(videoForFederation, true, t)
189 193
190 // Update video import object 194 // Update video import object
191 videoImport.state = VideoImportState.SUCCESS 195 videoImportToUpdate.state = VideoImportState.SUCCESS
192 const videoImportUpdated = await videoImport.save({ transaction: t }) 196 const videoImportUpdated = await videoImportToUpdate.save({ transaction: t }) as MVideoImportVideo
197 videoImportUpdated.Video = video
193 198
194 logger.info('Video %s imported.', video.uuid) 199 logger.info('Video %s imported.', video.uuid)
195 200
196 videoImportUpdated.Video = videoForFederation 201 return { videoImportUpdated, video: videoForFederation }
197 return videoImportUpdated
198 }) 202 })
199 203
200 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true) 204 Notifier.Instance.notifyOnFinishedVideoImport(videoImportUpdated, true)
201 205
202 if (videoImportUpdated.Video.isBlacklisted()) { 206 if (video.isBlacklisted()) {
203 Notifier.Instance.notifyOnVideoAutoBlacklist(videoImportUpdated.Video) 207 Notifier.Instance.notifyOnVideoAutoBlacklist(video)
204 } else { 208 } else {
205 Notifier.Instance.notifyOnNewVideoIfNeeded(videoImportUpdated.Video) 209 Notifier.Instance.notifyOnNewVideoIfNeeded(video)
206 } 210 }
207 211
208 // Create transcoding jobs? 212 // Create transcoding jobs?
209 if (videoImportUpdated.Video.state === VideoState.TO_TRANSCODE) { 213 if (video.state === VideoState.TO_TRANSCODE) {
210 // Put uuid because we don't have id auto incremented for now 214 // Put uuid because we don't have id auto incremented for now
211 const dataInput = { 215 const dataInput = {
212 type: 'optimize' as 'optimize', 216 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'
11import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding' 11import { generateHlsPlaylist, optimizeVideofile, transcodeOriginalVideofile, mergeAudioVideofile } from '../../video-transcoding'
12import { Notifier } from '../../notifier' 12import { Notifier } from '../../notifier'
13import { CONFIG } from '../../../initializers/config' 13import { CONFIG } from '../../../initializers/config'
14import { MVideoUUID, MVideoWithFile } from '@server/typings/models'
14 15
15interface BaseTranscodingPayload { 16interface 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
76async function onHlsPlaylistGenerationSuccess (video: VideoModel) { 77async 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
90async function publishNewResolutionIfNeeded (video: VideoModel, payload?: NewResolutionTranscodingPayload | MergeAudioTranscodingPayload) { 91async 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
122async function onVideoFileOptimizerSuccess (videoArg: VideoModel, payload: OptimizeTranscodingPayload) { 123async 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..f01101b8e 100644
--- a/server/lib/notifier.ts
+++ b/server/lib/notifier.ts
@@ -8,13 +8,23 @@ import { UserModel } from '../models/account/user'
8import { PeerTubeSocket } from './peertube-socket' 8import { PeerTubeSocket } from './peertube-socket'
9import { CONFIG } from '../initializers/config' 9import { CONFIG } from '../initializers/config'
10import { VideoPrivacy, VideoState } from '../../shared/models/videos' 10import { VideoPrivacy, VideoState } from '../../shared/models/videos'
11import { VideoAbuseModel } from '../models/video/video-abuse'
12import { VideoBlacklistModel } from '../models/video/video-blacklist' 11import { VideoBlacklistModel } from '../models/video/video-blacklist'
13import * as Bluebird from 'bluebird' 12import * as Bluebird from 'bluebird'
14import { VideoImportModel } from '../models/video/video-import' 13import { VideoImportModel } from '../models/video/video-import'
15import { AccountBlocklistModel } from '../models/account/account-blocklist' 14import { AccountBlocklistModel } from '../models/account/account-blocklist'
15import {
16 MCommentOwnerVideo,
17 MVideo,
18 MVideoAbuseVideo,
19 MVideoAccountLight,
20 MVideoBlacklistVideo,
21 MVideoFullLight
22} from '../typings/models/video'
23import { MUser, MUserAccount, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/typings/models/user'
24import { MActorFollowActors, MActorFollowFull } from '../typings/models'
16import { ActorFollowModel } from '../models/activitypub/actor-follow' 25import { ActorFollowModel } from '../models/activitypub/actor-follow'
17import { AccountModel } from '../models/account/account' 26import { MVideoImportVideo } from '@server/typings/models/video/video-import'
27import { AccountModel } from '@server/models/account/account'
18 28
19class Notifier { 29class 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 (video: MVideo): void {
71 this.notifyModeratorsOfVideoAutoBlacklist(video) 81 this.notifyModeratorsOfVideoAutoBlacklist(video)
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.', 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: MVideo): 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: MUserAccount): 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,14 +114,14 @@ class Notifier {
104 }) 114 })
105 } 115 }
106 116
107 notifyOfNewInstanceFollow (actorFollow: ActorFollowModel): void { 117 notifyOfNewInstanceFollow (actorFollow: MActorFollowActors): 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 private async notifySubscribersOfNewVideo (video: MVideoAccountLight) {
115 // List all followers that are users 125 // List all followers that are users
116 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId) 126 const users = await UserModel.listUserSubscribersOf(video.VideoChannel.actorId)
117 127
@@ -127,7 +137,7 @@ class Notifier {
127 userId: user.id, 137 userId: user.id,
128 videoId: video.id 138 videoId: video.id
129 }) 139 })
130 notification.Video = video 140 notification.Video = video as VideoModel
131 141
132 return notification 142 return notification
133 } 143 }
@@ -139,7 +149,7 @@ class Notifier {
139 return this.notify({ users, settingGetter, notificationCreator, emailSender }) 149 return this.notify({ users, settingGetter, notificationCreator, emailSender })
140 } 150 }
141 151
142 private async notifyVideoOwnerOfNewComment (comment: VideoCommentModel) { 152 private async notifyVideoOwnerOfNewComment (comment: MCommentOwnerVideo) {
143 if (comment.Video.isOwned() === false) return 153 if (comment.Video.isOwned() === false) return
144 154
145 const user = await UserModel.loadByVideoId(comment.videoId) 155 const user = await UserModel.loadByVideoId(comment.videoId)
@@ -162,7 +172,7 @@ class Notifier {
162 userId: user.id, 172 userId: user.id,
163 commentId: comment.id 173 commentId: comment.id
164 }) 174 })
165 notification.Comment = comment 175 notification.Comment = comment as VideoCommentModel
166 176
167 return notification 177 return notification
168 } 178 }
@@ -174,7 +184,7 @@ class Notifier {
174 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 184 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
175 } 185 }
176 186
177 private async notifyOfCommentMention (comment: VideoCommentModel) { 187 private async notifyOfCommentMention (comment: MCommentOwnerVideo) {
178 const extractedUsernames = comment.extractMentions() 188 const extractedUsernames = comment.extractMentions()
179 logger.debug( 189 logger.debug(
180 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url, 190 'Extracted %d username from comment %s.', extractedUsernames.length, comment.url,
@@ -209,7 +219,7 @@ class Notifier {
209 userId: user.id, 219 userId: user.id,
210 commentId: comment.id 220 commentId: comment.id
211 }) 221 })
212 notification.Comment = comment 222 notification.Comment = comment as VideoCommentModel
213 223
214 return notification 224 return notification
215 } 225 }
@@ -221,7 +231,7 @@ class Notifier {
221 return this.notify({ users, settingGetter, notificationCreator, emailSender }) 231 return this.notify({ users, settingGetter, notificationCreator, emailSender })
222 } 232 }
223 233
224 private async notifyUserOfNewActorFollow (actorFollow: ActorFollowModel) { 234 private async notifyUserOfNewActorFollow (actorFollow: MActorFollowFull) {
225 if (actorFollow.ActorFollowing.isOwned() === false) return 235 if (actorFollow.ActorFollowing.isOwned() === false) return
226 236
227 // Account follows one of our account? 237 // Account follows one of our account?
@@ -236,9 +246,6 @@ class Notifier {
236 246
237 if (!user) return 247 if (!user) return
238 248
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 249 const followerAccount = actorFollow.ActorFollower.Account
243 250
244 const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id) 251 const accountMuted = await AccountBlocklistModel.isAccountMutedBy(user.Account.id, followerAccount.id)
@@ -256,7 +263,7 @@ class Notifier {
256 userId: user.id, 263 userId: user.id,
257 actorFollowId: actorFollow.id 264 actorFollowId: actorFollow.id
258 }) 265 })
259 notification.ActorFollow = actorFollow 266 notification.ActorFollow = actorFollow as ActorFollowModel
260 267
261 return notification 268 return notification
262 } 269 }
@@ -268,7 +275,7 @@ class Notifier {
268 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 275 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
269 } 276 }
270 277
271 private async notifyAdminsOfNewInstanceFollow (actorFollow: ActorFollowModel) { 278 private async notifyAdminsOfNewInstanceFollow (actorFollow: MActorFollowActors) {
272 const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW) 279 const admins = await UserModel.listWithRight(UserRight.MANAGE_SERVER_FOLLOW)
273 280
274 logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url) 281 logger.info('Notifying %d administrators of new instance follower: %s.', admins.length, actorFollow.ActorFollower.url)
@@ -283,7 +290,7 @@ class Notifier {
283 userId: user.id, 290 userId: user.id,
284 actorFollowId: actorFollow.id 291 actorFollowId: actorFollow.id
285 }) 292 })
286 notification.ActorFollow = actorFollow 293 notification.ActorFollow = actorFollow as ActorFollowModel
287 294
288 return notification 295 return notification
289 } 296 }
@@ -295,7 +302,7 @@ class Notifier {
295 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender }) 302 return this.notify({ users: admins, settingGetter, notificationCreator, emailSender })
296 } 303 }
297 304
298 private async notifyModeratorsOfNewVideoAbuse (videoAbuse: VideoAbuseModel) { 305 private async notifyModeratorsOfNewVideoAbuse (videoAbuse: MVideoAbuseVideo) {
299 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES) 306 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_ABUSES)
300 if (moderators.length === 0) return 307 if (moderators.length === 0) return
301 308
@@ -306,7 +313,7 @@ class Notifier {
306 } 313 }
307 314
308 async function notificationCreator (user: UserModel) { 315 async function notificationCreator (user: UserModel) {
309 const notification = await UserNotificationModel.create({ 316 const notification: UserNotificationModelForApi = await UserNotificationModel.create({
310 type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, 317 type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS,
311 userId: user.id, 318 userId: user.id,
312 videoAbuseId: videoAbuse.id 319 videoAbuseId: videoAbuse.id
@@ -323,7 +330,7 @@ class Notifier {
323 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 330 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
324 } 331 }
325 332
326 private async notifyModeratorsOfVideoAutoBlacklist (video: VideoModel) { 333 private async notifyModeratorsOfVideoAutoBlacklist (video: MVideo) {
327 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST) 334 const moderators = await UserModel.listWithRight(UserRight.MANAGE_VIDEO_BLACKLIST)
328 if (moderators.length === 0) return 335 if (moderators.length === 0) return
329 336
@@ -339,7 +346,7 @@ class Notifier {
339 userId: user.id, 346 userId: user.id,
340 videoId: video.id 347 videoId: video.id
341 }) 348 })
342 notification.Video = video 349 notification.Video = video as VideoModel
343 350
344 return notification 351 return notification
345 } 352 }
@@ -351,7 +358,7 @@ class Notifier {
351 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender }) 358 return this.notify({ users: moderators, settingGetter, notificationCreator, emailSender })
352 } 359 }
353 360
354 private async notifyVideoOwnerOfBlacklist (videoBlacklist: VideoBlacklistModel) { 361 private async notifyVideoOwnerOfBlacklist (videoBlacklist: MVideoBlacklistVideo) {
355 const user = await UserModel.loadByVideoId(videoBlacklist.videoId) 362 const user = await UserModel.loadByVideoId(videoBlacklist.videoId)
356 if (!user) return 363 if (!user) return
357 364
@@ -367,7 +374,7 @@ class Notifier {
367 userId: user.id, 374 userId: user.id,
368 videoBlacklistId: videoBlacklist.id 375 videoBlacklistId: videoBlacklist.id
369 }) 376 })
370 notification.VideoBlacklist = videoBlacklist 377 notification.VideoBlacklist = videoBlacklist as VideoBlacklistModel
371 378
372 return notification 379 return notification
373 } 380 }
@@ -379,7 +386,7 @@ class Notifier {
379 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 386 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
380 } 387 }
381 388
382 private async notifyVideoOwnerOfUnblacklist (video: VideoModel) { 389 private async notifyVideoOwnerOfUnblacklist (video: MVideo) {
383 const user = await UserModel.loadByVideoId(video.id) 390 const user = await UserModel.loadByVideoId(video.id)
384 if (!user) return 391 if (!user) return
385 392
@@ -395,7 +402,7 @@ class Notifier {
395 userId: user.id, 402 userId: user.id,
396 videoId: video.id 403 videoId: video.id
397 }) 404 })
398 notification.Video = video 405 notification.Video = video as VideoModel
399 406
400 return notification 407 return notification
401 } 408 }
@@ -407,7 +414,7 @@ class Notifier {
407 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 414 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
408 } 415 }
409 416
410 private async notifyOwnedVideoHasBeenPublished (video: VideoModel) { 417 private async notifyOwnedVideoHasBeenPublished (video: MVideoFullLight) {
411 const user = await UserModel.loadByVideoId(video.id) 418 const user = await UserModel.loadByVideoId(video.id)
412 if (!user) return 419 if (!user) return
413 420
@@ -423,7 +430,7 @@ class Notifier {
423 userId: user.id, 430 userId: user.id,
424 videoId: video.id 431 videoId: video.id
425 }) 432 })
426 notification.Video = video 433 notification.Video = video as VideoModel
427 434
428 return notification 435 return notification
429 } 436 }
@@ -435,7 +442,7 @@ class Notifier {
435 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 442 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
436 } 443 }
437 444
438 private async notifyOwnerVideoImportIsFinished (videoImport: VideoImportModel, success: boolean) { 445 private async notifyOwnerVideoImportIsFinished (videoImport: MVideoImportVideo, success: boolean) {
439 const user = await UserModel.loadByVideoImportId(videoImport.id) 446 const user = await UserModel.loadByVideoImportId(videoImport.id)
440 if (!user) return 447 if (!user) return
441 448
@@ -451,7 +458,7 @@ class Notifier {
451 userId: user.id, 458 userId: user.id,
452 videoImportId: videoImport.id 459 videoImportId: videoImport.id
453 }) 460 })
454 notification.VideoImport = videoImport 461 notification.VideoImport = videoImport as VideoImportModel
455 462
456 return notification 463 return notification
457 } 464 }
@@ -465,13 +472,13 @@ class Notifier {
465 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender }) 472 return this.notify({ users: [ user ], settingGetter, notificationCreator, emailSender })
466 } 473 }
467 474
468 private async notifyModeratorsOfNewUserRegistration (registeredUser: UserModel) { 475 private async notifyModeratorsOfNewUserRegistration (registeredUser: MUserAccount) {
469 const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) 476 const moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS)
470 if (moderators.length === 0) return 477 if (moderators.length === 0) return
471 478
472 logger.info( 479 logger.info(
473 'Notifying %s moderators of new user registration of %s.', 480 'Notifying %s moderators of new user registration of %s.',
474 moderators.length, registeredUser.Account.Actor.preferredUsername 481 moderators.length, registeredUser.username
475 ) 482 )
476 483
477 function settingGetter (user: UserModel) { 484 function settingGetter (user: UserModel) {
@@ -484,7 +491,7 @@ class Notifier {
484 userId: user.id, 491 userId: user.id,
485 accountId: registeredUser.Account.id 492 accountId: registeredUser.Account.id
486 }) 493 })
487 notification.Account = registeredUser.Account 494 notification.Account = registeredUser.Account as AccountModel
488 495
489 return notification 496 return notification
490 } 497 }
@@ -497,10 +504,10 @@ class Notifier {
497 } 504 }
498 505
499 private async notify (options: { 506 private async notify (options: {
500 users: UserModel[], 507 users: MUserWithNotificationSetting[],
501 notificationCreator: (user: UserModel) => Promise<UserNotificationModel>, 508 notificationCreator: (user: MUserWithNotificationSetting) => Promise<UserNotificationModelForApi>,
502 emailSender: (emails: string[]) => Promise<any> | Bluebird<any>, 509 emailSender: (emails: string[]) => Promise<any> | Bluebird<any>,
503 settingGetter: (user: UserModel) => UserNotificationSettingValue 510 settingGetter: (user: MUserWithNotificationSetting) => UserNotificationSettingValue
504 }) { 511 }) {
505 const emails: string[] = [] 512 const emails: string[] = []
506 513
@@ -521,7 +528,7 @@ class Notifier {
521 } 528 }
522 } 529 }
523 530
524 private isEmailEnabled (user: UserModel, value: UserNotificationSettingValue) { 531 private isEmailEnabled (user: MUser, value: UserNotificationSettingValue) {
525 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false 532 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION === true && user.emailVerified === false) return false
526 533
527 return value & UserNotificationSettingValue.EMAIL 534 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'
8import { Transaction } from 'sequelize' 8import { Transaction } from 'sequelize'
9import { CONFIG } from '../initializers/config' 9import { CONFIG } from '../initializers/config'
10import * as LRUCache from 'lru-cache' 10import * as LRUCache from 'lru-cache'
11import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
11 12
12type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } 13type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
13 14
14const accessTokenCache = new LRUCache<string, OAuthTokenModel>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) 15const accessTokenCache = new LRUCache<string, MOAuthTokenUser>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
15const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE }) 16const 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 eb84ecd4b..1c7b09175 100644
--- a/server/lib/peertube-socket.ts
+++ b/server/lib/peertube-socket.ts
@@ -1,8 +1,8 @@
1import * as SocketIO from 'socket.io' 1import * as SocketIO from 'socket.io'
2import { authenticateSocket } from '../middlewares' 2import { authenticateSocket } from '../middlewares'
3import { UserNotificationModel } from '../models/account/user-notification'
4import { logger } from '../helpers/logger' 3import { logger } from '../helpers/logger'
5import { Server } from 'http' 4import { Server } from 'http'
5import { UserNotificationModelForApi } from '@server/typings/models/user'
6 6
7class PeerTubeSocket { 7class PeerTubeSocket {
8 8
@@ -32,7 +32,7 @@ class PeerTubeSocket {
32 }) 32 })
33 } 33 }
34 34
35 sendNotification (userId: number, notification: UserNotificationModel) { 35 sendNotification (userId: number, notification: UserNotificationModelForApi) {
36 const socket = this.userNotificationSockets[userId] 36 const socket = this.userNotificationSockets[userId]
37 37
38 if (!socket) return 38 if (!socket) return
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'
2import { sendUndoCacheFile } from './activitypub/send' 2import { sendUndoCacheFile } from './activitypub/send'
3import { Transaction } from 'sequelize' 3import { Transaction } from 'sequelize'
4import { getServerActor } from '../helpers/utils' 4import { getServerActor } from '../helpers/utils'
5import { MVideoRedundancyVideo } from '@server/typings/models'
5 6
6async function removeVideoRedundancy (videoRedundancy: VideoRedundancyModel, t?: Transaction) { 7async 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/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index 04f601bfb..de8fc075b 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 }
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import { VideosRedundancy } from '../../../shared/models/redundancy' 4import { VideosRedundancy } from '../../../shared/models/redundancy'
5import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 5import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
6import { VideoFileModel } from '../../models/video/video-file'
7import { downloadWebTorrentVideo } from '../../helpers/webtorrent' 6import { downloadWebTorrentVideo } from '../../helpers/webtorrent'
8import { join } from 'path' 7import { join } from 'path'
9import { move } from 'fs-extra' 8import { move } from 'fs-extra'
@@ -12,16 +11,29 @@ import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
12import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' 11import { getVideoCacheFileActivityPubUrl, getVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
13import { removeVideoRedundancy } from '../redundancy' 12import { removeVideoRedundancy } from '../redundancy'
14import { getOrCreateVideoAndAccountAndChannel } from '../activitypub' 13import { getOrCreateVideoAndAccountAndChannel } from '../activitypub'
15import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
16import { VideoModel } from '../../models/video/video'
17import { downloadPlaylistSegments } from '../hls' 14import { downloadPlaylistSegments } from '../hls'
18import { CONFIG } from '../../initializers/config' 15import { CONFIG } from '../../initializers/config'
16import {
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
20type CandidateToDuplicate = { 28type 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
35function isMVideoRedundancyFileVideo (o: MVideoRedundancyVideo): o is MVideoRedundancyFileVideo {
36 return !!(o as MVideoRedundancyFileVideo).VideoFile
25} 37}
26 38
27export class VideosRedundancyScheduler extends AbstractScheduler { 39export class VideosRedundancyScheduler extends AbstractScheduler {
@@ -102,7 +114,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
102 } 114 }
103 } 115 }
104 116
105 private async extendsRedundancy (redundancyModel: VideoRedundancyModel) { 117 private async extendsRedundancy (redundancyModel: MVideoRedundancyVideo) {
106 const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy) 118 const redundancy = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.find(s => s.strategy === redundancyModel.strategy)
107 // Redundancy strategy disabled, remove our redundancy instead of extending expiration 119 // Redundancy strategy disabled, remove our redundancy instead of extending expiration
108 if (!redundancy) { 120 if (!redundancy) {
@@ -172,7 +184,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
172 } 184 }
173 } 185 }
174 186
175 private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: VideoModel, file: VideoFileModel) { 187 private async createVideoFileRedundancy (redundancy: VideosRedundancy, video: MVideoAccountLight, fileArg: MVideoFile) {
188 const file = fileArg as MVideoFileVideo
176 file.Video = video 189 file.Video = video
177 190
178 const serverActor = await getServerActor() 191 const serverActor = await getServerActor()
@@ -187,7 +200,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
187 const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file)) 200 const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, video.getVideoFilename(file))
188 await move(tmpPath, destPath) 201 await move(tmpPath, destPath)
189 202
190 const createdModel = await VideoRedundancyModel.create({ 203 const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
191 expiresOn: this.buildNewExpiration(redundancy.minLifetime), 204 expiresOn: this.buildNewExpiration(redundancy.minLifetime),
192 url: getVideoCacheFileActivityPubUrl(file), 205 url: getVideoCacheFileActivityPubUrl(file),
193 fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), 206 fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL),
@@ -203,7 +216,12 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
203 logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url) 216 logger.info('Duplicated %s - %d -> %s.', video.url, file.resolution, createdModel.url)
204 } 217 }
205 218
206 private async createStreamingPlaylistRedundancy (redundancy: VideosRedundancy, video: VideoModel, playlist: VideoStreamingPlaylistModel) { 219 private async createStreamingPlaylistRedundancy (
220 redundancy: VideosRedundancy,
221 video: MVideoAccountLight,
222 playlistArg: MStreamingPlaylist
223 ) {
224 const playlist = playlistArg as MStreamingPlaylistVideo
207 playlist.Video = video 225 playlist.Video = video
208 226
209 const serverActor = await getServerActor() 227 const serverActor = await getServerActor()
@@ -213,7 +231,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
213 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) 231 const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid)
214 await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) 232 await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT)
215 233
216 const createdModel = await VideoRedundancyModel.create({ 234 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
217 expiresOn: this.buildNewExpiration(redundancy.minLifetime), 235 expiresOn: this.buildNewExpiration(redundancy.minLifetime),
218 url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), 236 url: getVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
219 fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), 237 fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL),
@@ -229,7 +247,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
229 logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) 247 logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url)
230 } 248 }
231 249
232 private async extendsExpirationOf (redundancy: VideoRedundancyModel, expiresAfterMs: number) { 250 private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) {
233 logger.info('Extending expiration of %s.', redundancy.url) 251 logger.info('Extending expiration of %s.', redundancy.url)
234 252
235 const serverActor = await getServerActor() 253 const serverActor = await getServerActor()
@@ -243,7 +261,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
243 private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) { 261 private async purgeCacheIfNeeded (candidateToDuplicate: CandidateToDuplicate) {
244 while (this.isTooHeavy(candidateToDuplicate)) { 262 while (this.isTooHeavy(candidateToDuplicate)) {
245 const redundancy = candidateToDuplicate.redundancy 263 const redundancy = candidateToDuplicate.redundancy
246 const toDelete = await VideoRedundancyModel.loadOldestLocalThatAlreadyExpired(redundancy.strategy, redundancy.minLifetime) 264 const toDelete = await VideoRedundancyModel.loadOldestLocalExpired(redundancy.strategy, redundancy.minLifetime)
247 if (!toDelete) return 265 if (!toDelete) return
248 266
249 await removeVideoRedundancy(toDelete) 267 await removeVideoRedundancy(toDelete)
@@ -263,14 +281,14 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
263 return new Date(Date.now() + expiresAfterMs) 281 return new Date(Date.now() + expiresAfterMs)
264 } 282 }
265 283
266 private buildEntryLogId (object: VideoRedundancyModel) { 284 private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) {
267 if (object.VideoFile) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` 285 if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}`
268 286
269 return `${object.VideoStreamingPlaylist.playlistUrl}` 287 return `${object.VideoStreamingPlaylist.playlistUrl}`
270 } 288 }
271 289
272 private getTotalFileSizes (files: VideoFileModel[], playlists: VideoStreamingPlaylistModel[]) { 290 private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylist[]) {
273 const fileReducer = (previous: number, current: VideoFileModel) => previous + current.size 291 const fileReducer = (previous: number, current: MVideoFile) => previous + current.size
274 292
275 const totalSize = files.reduce(fileReducer, 0) 293 const totalSize = files.reduce(fileReducer, 0)
276 if (playlists.length === 0) return totalSize 294 if (playlists.length === 0) return totalSize
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 @@
1import { VideoFileModel } from '../models/video/video-file'
2import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' 1import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
3import { CONFIG } from '../initializers/config' 2import { CONFIG } from '../initializers/config'
4import { PREVIEWS_SIZE, THUMBNAILS_SIZE, ASSETS_PATH } from '../initializers/constants' 3import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants'
5import { VideoModel } from '../models/video/video'
6import { ThumbnailModel } from '../models/video/thumbnail' 4import { ThumbnailModel } from '../models/video/thumbnail'
7import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' 5import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
8import { processImage } from '../helpers/image-utils' 6import { processImage } from '../helpers/image-utils'
9import { join } from 'path' 7import { join } from 'path'
10import { downloadImage } from '../helpers/requests' 8import { downloadImage } from '../helpers/requests'
11import { VideoPlaylistModel } from '../models/video/video-playlist' 9import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist'
10import { MVideoFile, MVideoThumbnail } from '../typings/models'
11import { MThumbnail } from '../typings/models/video/thumbnail'
12 12
13type ImageSize = { height: number, width: number } 13type ImageSize = { height: number, width: number }
14 14
15function createPlaylistMiniatureFromExisting ( 15function 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
29function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: VideoPlaylistModel, size?: ImageSize) { 29function 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
37function createVideoMiniatureFromUrl (fileUrl: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { 37function 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
44function createVideoMiniatureFromExisting ( 44function 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
57function generateVideoMiniature (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { 57function 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
68function createPlaceholderThumbnail (fileUrl: string, video: VideoModel, type: ThumbnailType, size: ImageSize) { 68function 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
93function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) { 93function 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
107function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) { 107function 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..266974cac 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -5,7 +5,6 @@ import { AccountModel } from '../models/account/account'
5import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' 6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
7import { createVideoChannel } from './video-channel' 7import { createVideoChannel } from './video-channel'
8import { VideoChannelModel } from '../models/video/video-channel'
9import { ActorModel } from '../models/activitypub/actor' 8import { ActorModel } from '../models/activitypub/actor'
10import { UserNotificationSettingModel } from '../models/account/user-notification-setting' 9import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
11import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' 10import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
@@ -14,14 +13,17 @@ import { sequelizeTypescript } from '../initializers/database'
14import { Transaction } from 'sequelize/types' 13import { Transaction } from 'sequelize/types'
15import { Redis } from './redis' 14import { Redis } from './redis'
16import { Emailer } from './emailer' 15import { Emailer } from './emailer'
16import { MAccountActor, MActor, MChannelActor } from '../typings/models'
17import { MUser, MUserId, MUserNotifSettingAccount } from '../typings/models/user'
17 18
18type ChannelNames = { name: string, displayName: string } 19type ChannelNames = { name: string, displayName: string }
20
19async function createUserAccountAndChannelAndPlaylist (parameters: { 21async function createUserAccountAndChannelAndPlaylist (parameters: {
20 userToCreate: UserModel, 22 userToCreate: UserModel,
21 userDisplayName?: string, 23 userDisplayName?: string,
22 channelNames?: ChannelNames, 24 channelNames?: ChannelNames,
23 validateUser?: boolean 25 validateUser?: boolean
24}) { 26}): Promise<{ user: MUserNotifSettingAccount, account: MAccountActor, videoChannel: MChannelActor }> {
25 const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters 27 const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
26 28
27 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { 29 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
@@ -30,7 +32,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
30 validate: validateUser 32 validate: validateUser
31 } 33 }
32 34
33 const userCreated = await userToCreate.save(userOptions) 35 const userCreated: MUserNotifSettingAccount = await userToCreate.save(userOptions)
34 userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t) 36 userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
35 37
36 const accountCreated = await createLocalAccountWithoutKeys({ 38 const accountCreated = await createLocalAccountWithoutKeys({
@@ -50,15 +52,15 @@ async function createUserAccountAndChannelAndPlaylist (parameters: {
50 return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist } 52 return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
51 }) 53 })
52 54
53 const [ accountKeys, channelKeys ] = await Promise.all([ 55 const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([
54 setAsyncActorKeys(account.Actor), 56 setAsyncActorKeys(account.Actor),
55 setAsyncActorKeys(videoChannel.Actor) 57 setAsyncActorKeys(videoChannel.Actor)
56 ]) 58 ])
57 59
58 account.Actor = accountKeys 60 account.Actor = accountActorWithKeys
59 videoChannel.Actor = channelKeys 61 videoChannel.Actor = channelActorWithKeys
60 62
61 return { user, account, videoChannel } as { user: UserModel, account: AccountModel, videoChannel: VideoChannelModel } 63 return { user, account, videoChannel }
62} 64}
63 65
64async function createLocalAccountWithoutKeys (parameters: { 66async function createLocalAccountWithoutKeys (parameters: {
@@ -73,7 +75,7 @@ async function createLocalAccountWithoutKeys (parameters: {
73 const url = getAccountActivityPubUrl(name) 75 const url = getAccountActivityPubUrl(name)
74 76
75 const actorInstance = buildActorInstance(type, url, name) 77 const actorInstance = buildActorInstance(type, url, name)
76 const actorInstanceCreated = await actorInstance.save({ transaction: t }) 78 const actorInstanceCreated: MActor = await actorInstance.save({ transaction: t })
77 79
78 const accountInstance = new AccountModel({ 80 const accountInstance = new AccountModel({
79 name: displayName || name, 81 name: displayName || name,
@@ -82,7 +84,7 @@ async function createLocalAccountWithoutKeys (parameters: {
82 actorId: actorInstanceCreated.id 84 actorId: actorInstanceCreated.id
83 }) 85 })
84 86
85 const accountInstanceCreated = await accountInstance.save({ transaction: t }) 87 const accountInstanceCreated: MAccountActor = await accountInstance.save({ transaction: t })
86 accountInstanceCreated.Actor = actorInstanceCreated 88 accountInstanceCreated.Actor = actorInstanceCreated
87 89
88 return accountInstanceCreated 90 return accountInstanceCreated
@@ -102,7 +104,7 @@ async function createApplicationActor (applicationId: number) {
102 return accountCreated 104 return accountCreated
103} 105}
104 106
105async function sendVerifyUserEmail (user: UserModel, isPendingEmail = false) { 107async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
106 const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) 108 const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
107 let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString 109 let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString
108 110
@@ -124,7 +126,7 @@ export {
124 126
125// --------------------------------------------------------------------------- 127// ---------------------------------------------------------------------------
126 128
127function createDefaultUserNotificationSettings (user: UserModel, t: Transaction | undefined) { 129function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) {
128 const values: UserNotificationSetting & { userId: number } = { 130 const values: UserNotificationSetting & { userId: number } = {
129 userId: user.id, 131 userId: user.id,
130 newVideoFromSubscription: UserNotificationSettingValue.WEB, 132 newVideoFromSubscription: UserNotificationSettingValue.WEB,
@@ -143,7 +145,7 @@ function createDefaultUserNotificationSettings (user: UserModel, t: Transaction
143 return UserNotificationSettingModel.create(values, { transaction: t }) 145 return UserNotificationSettingModel.create(values, { transaction: t })
144} 146}
145 147
146async function buildChannelAttributes (user: UserModel, channelNames?: ChannelNames) { 148async 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..a0fc26e84 100644
--- a/server/lib/video-blacklist.ts
+++ b/server/lib/video-blacklist.ts
@@ -2,16 +2,15 @@ import { Transaction } from 'sequelize'
2import { CONFIG } from '../initializers/config' 2import { CONFIG } from '../initializers/config'
3import { UserRight, VideoBlacklistType } from '../../shared/models' 3import { UserRight, VideoBlacklistType } from '../../shared/models'
4import { VideoBlacklistModel } from '../models/video/video-blacklist' 4import { VideoBlacklistModel } from '../models/video/video-blacklist'
5import { UserModel } from '../models/account/user'
6import { VideoModel } from '../models/video/video'
7import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
8import { UserAdminFlag } from '../../shared/models/users/user-flag.model' 6import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
9import { Hooks } from './plugins/hooks' 7import { Hooks } from './plugins/hooks'
10import { Notifier } from './notifier' 8import { Notifier } from './notifier'
9import { MUser, MVideoBlacklist, MVideoWithBlacklistLight } from '@server/typings/models'
11 10
12async function autoBlacklistVideoIfNeeded (parameters: { 11async 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<MVideoBlacklist>({
36 where: { 35 where: {
37 videoId: video.id 36 videoId: video.id
38 }, 37 },
@@ -49,10 +48,10 @@ async function autoBlacklistVideoIfNeeded (parameters: {
49} 48}
50 49
51async function autoBlacklistNeeded (parameters: { 50async function autoBlacklistNeeded (parameters: {
52 video: VideoModel, 51 video: MVideoWithBlacklistLight,
53 isRemote: boolean, 52 isRemote: boolean,
54 isNew: boolean, 53 isNew: boolean,
55 user?: UserModel 54 user?: MUser
56}) { 55}) {
57 const { user, video, isRemote, isNew } = parameters 56 const { user, video, isRemote, isNew } = parameters
58 57
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index ee0482c36..ee8eb6568 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -1,12 +1,19 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as uuidv4 from 'uuid/v4' 2import * as uuidv4 from 'uuid/v4'
3import { VideoChannelCreate } from '../../shared/models' 3import { VideoChannelCreate } from '../../shared/models'
4import { AccountModel } from '../models/account/account'
5import { VideoChannelModel } from '../models/video/video-channel' 4import { VideoChannelModel } from '../models/video/video-channel'
6import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' 5import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
7import { VideoModel } from '../models/video/video' 6import { VideoModel } from '../models/video/video'
7import { MAccountId, MChannelActor, MChannelId } from '../typings/models'
8 8
9async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { 9type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelActor &
10 { Account?: T }
11
12async function createVideoChannel <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 MChannelActor
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
37async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) { 44async 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) {
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 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { ResultList } from '../../shared/models' 2import { ResultList } from '../../shared/models'
3import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' 3import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
4import { AccountModel } from '../models/account/account'
5import { VideoModel } from '../models/video/video'
6import { VideoCommentModel } from '../models/video/video-comment' 4import { VideoCommentModel } from '../models/video/video-comment'
7import { getVideoCommentActivityPubUrl } from './activitypub' 5import { getVideoCommentActivityPubUrl } from './activitypub'
8import { sendCreateVideoComment } from './activitypub/send' 6import { sendCreateVideoComment } from './activitypub/send'
7import { MAccountDefault, MComment, MCommentOwnerVideoReply, MVideoFullLight } from '../typings/models'
9 8
10async function createVideoComment (obj: { 9async 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 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { AccountModel } from '../models/account/account'
3import { VideoPlaylistModel } from '../models/video/video-playlist' 2import { VideoPlaylistModel } from '../models/video/video-playlist'
4import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' 3import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
5import { getVideoPlaylistActivityPubUrl } from './activitypub' 4import { getVideoPlaylistActivityPubUrl } from './activitypub'
6import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' 5import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
6import { MAccount } from '../typings/models'
7import { MVideoPlaylistOwner } from '../typings/models/video/video-playlist'
7 8
8async function createWatchLaterPlaylist (account: AccountModel, t: Sequelize.Transaction) { 9async 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'
5import { logger } from '../helpers/logger' 5import { logger } from '../helpers/logger'
6import { VideoResolution } from '../../shared/models/videos' 6import { VideoResolution } from '../../shared/models/videos'
7import { VideoFileModel } from '../models/video/video-file' 7import { VideoFileModel } from '../models/video/video-file'
8import { VideoModel } from '../models/video/video'
9import { updateMasterHLSPlaylist, updateSha256Segments } from './hls' 8import { updateMasterHLSPlaylist, updateSha256Segments } from './hls'
10import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 9import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
11import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' 10import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
12import { CONFIG } from '../initializers/config' 11import { CONFIG } from '../initializers/config'
12import { 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 */
17async function optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) { 17async 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 */
60async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortrait: boolean) { 60async 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
90async function mergeAudioVideofile (video: VideoModel, resolution: VideoResolution) { 90async 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
120async function generateHlsPlaylist (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) { 120async 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
168async function onVideoFileTranscoding (video: VideoModel, videoFile: VideoFileModel, transcodingPath: string, outputPath: string) { 168async 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