aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/activity.ts14
-rw-r--r--server/lib/activitypub/playlists/get.ts4
-rw-r--r--server/lib/activitypub/process/process-create.ts58
-rw-r--r--server/lib/activitypub/process/process-dislike.ts11
-rw-r--r--server/lib/activitypub/process/process-flag.ts8
-rw-r--r--server/lib/activitypub/process/process-undo.ts43
-rw-r--r--server/lib/activitypub/process/process-update.ts36
-rw-r--r--server/lib/activitypub/send/send-create.ts23
-rw-r--r--server/lib/activitypub/send/send-undo.ts19
-rw-r--r--server/lib/activitypub/send/send-update.ts9
-rw-r--r--server/lib/activitypub/videos/get.ts8
11 files changed, 141 insertions, 92 deletions
diff --git a/server/lib/activitypub/activity.ts b/server/lib/activitypub/activity.ts
index 1f6ec221e..0fed3e8fd 100644
--- a/server/lib/activitypub/activity.ts
+++ b/server/lib/activitypub/activity.ts
@@ -1,4 +1,5 @@
1import { ActivityType } from '@shared/models' 1import { doJSONRequest } from '@server/helpers/requests'
2import { APObjectId, ActivityObject, ActivityPubActor, ActivityType } from '@shared/models'
2 3
3function getAPId (object: string | { id: string }) { 4function getAPId (object: string | { id: string }) {
4 if (typeof object === 'string') return object 5 if (typeof object === 'string') return object
@@ -32,8 +33,19 @@ function buildAvailableActivities (): ActivityType[] {
32 ] 33 ]
33} 34}
34 35
36async function fetchAPObject <T extends (ActivityObject | ActivityPubActor)> (object: APObjectId) {
37 if (typeof object === 'string') {
38 const { body } = await doJSONRequest<Exclude<T, string>>(object, { activityPub: true })
39
40 return body
41 }
42
43 return object as Exclude<T, string>
44}
45
35export { 46export {
36 getAPId, 47 getAPId,
48 fetchAPObject,
37 getActivityStreamDuration, 49 getActivityStreamDuration,
38 buildAvailableActivities, 50 buildAvailableActivities,
39 getDurationFromActivityStream 51 getDurationFromActivityStream
diff --git a/server/lib/activitypub/playlists/get.ts b/server/lib/activitypub/playlists/get.ts
index bfaf52cc9..c34554d69 100644
--- a/server/lib/activitypub/playlists/get.ts
+++ b/server/lib/activitypub/playlists/get.ts
@@ -1,12 +1,12 @@
1import { VideoPlaylistModel } from '@server/models/video/video-playlist' 1import { VideoPlaylistModel } from '@server/models/video/video-playlist'
2import { MVideoPlaylistFullSummary } from '@server/types/models' 2import { MVideoPlaylistFullSummary } from '@server/types/models'
3import { APObject } from '@shared/models' 3import { APObjectId } from '@shared/models'
4import { getAPId } from '../activity' 4import { getAPId } from '../activity'
5import { createOrUpdateVideoPlaylist } from './create-update' 5import { createOrUpdateVideoPlaylist } from './create-update'
6import { scheduleRefreshIfNeeded } from './refresh' 6import { scheduleRefreshIfNeeded } from './refresh'
7import { fetchRemoteVideoPlaylist } from './shared' 7import { fetchRemoteVideoPlaylist } from './shared'
8 8
9async function getOrCreateAPVideoPlaylist (playlistObjectArg: APObject): Promise<MVideoPlaylistFullSummary> { 9async function getOrCreateAPVideoPlaylist (playlistObjectArg: APObjectId): Promise<MVideoPlaylistFullSummary> {
10 const playlistUrl = getAPId(playlistObjectArg) 10 const playlistUrl = getAPId(playlistObjectArg)
11 11
12 const playlistFromDatabase = await VideoPlaylistModel.loadByUrlWithAccountAndChannelSummary(playlistUrl) 12 const playlistFromDatabase = await VideoPlaylistModel.loadByUrlWithAccountAndChannelSummary(playlistUrl)
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 1e6e8956c..e89d1ab45 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -1,13 +1,24 @@
1import { isBlockedByServerOrAccount } from '@server/lib/blocklist' 1import { isBlockedByServerOrAccount } from '@server/lib/blocklist'
2import { isRedundancyAccepted } from '@server/lib/redundancy' 2import { isRedundancyAccepted } from '@server/lib/redundancy'
3import { VideoModel } from '@server/models/video/video' 3import { VideoModel } from '@server/models/video/video'
4import { ActivityCreate, CacheFileObject, PlaylistObject, VideoCommentObject, VideoObject, WatchActionObject } from '@shared/models' 4import {
5 AbuseObject,
6 ActivityCreate,
7 ActivityCreateObject,
8 ActivityObject,
9 CacheFileObject,
10 PlaylistObject,
11 VideoCommentObject,
12 VideoObject,
13 WatchActionObject
14} from '@shared/models'
5import { retryTransactionWrapper } from '../../../helpers/database-utils' 15import { retryTransactionWrapper } from '../../../helpers/database-utils'
6import { logger } from '../../../helpers/logger' 16import { logger } from '../../../helpers/logger'
7import { sequelizeTypescript } from '../../../initializers/database' 17import { sequelizeTypescript } from '../../../initializers/database'
8import { APProcessorOptions } from '../../../types/activitypub-processor.model' 18import { APProcessorOptions } from '../../../types/activitypub-processor.model'
9import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../types/models' 19import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../types/models'
10import { Notifier } from '../../notifier' 20import { Notifier } from '../../notifier'
21import { fetchAPObject } from '../activity'
11import { createOrUpdateCacheFile } from '../cache-file' 22import { createOrUpdateCacheFile } from '../cache-file'
12import { createOrUpdateLocalVideoViewer } from '../local-video-viewer' 23import { createOrUpdateLocalVideoViewer } from '../local-video-viewer'
13import { createOrUpdateVideoPlaylist } from '../playlists' 24import { createOrUpdateVideoPlaylist } from '../playlists'
@@ -15,35 +26,35 @@ import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
15import { resolveThread } from '../video-comments' 26import { resolveThread } from '../video-comments'
16import { getOrCreateAPVideo } from '../videos' 27import { getOrCreateAPVideo } from '../videos'
17 28
18async function processCreateActivity (options: APProcessorOptions<ActivityCreate>) { 29async function processCreateActivity (options: APProcessorOptions<ActivityCreate<ActivityCreateObject>>) {
19 const { activity, byActor } = options 30 const { activity, byActor } = options
20 31
21 // Only notify if it is not from a fetcher job 32 // Only notify if it is not from a fetcher job
22 const notify = options.fromFetch !== true 33 const notify = options.fromFetch !== true
23 const activityObject = activity.object 34 const activityObject = await fetchAPObject<Exclude<ActivityObject, AbuseObject>>(activity.object)
24 const activityType = activityObject.type 35 const activityType = activityObject.type
25 36
26 if (activityType === 'Video') { 37 if (activityType === 'Video') {
27 return processCreateVideo(activity, notify) 38 return processCreateVideo(activityObject, notify)
28 } 39 }
29 40
30 if (activityType === 'Note') { 41 if (activityType === 'Note') {
31 // Comments will be fetched from videos 42 // Comments will be fetched from videos
32 if (options.fromFetch) return 43 if (options.fromFetch) return
33 44
34 return retryTransactionWrapper(processCreateVideoComment, activity, byActor, notify) 45 return retryTransactionWrapper(processCreateVideoComment, activity, activityObject, byActor, notify)
35 } 46 }
36 47
37 if (activityType === 'WatchAction') { 48 if (activityType === 'WatchAction') {
38 return retryTransactionWrapper(processCreateWatchAction, activity) 49 return retryTransactionWrapper(processCreateWatchAction, activityObject)
39 } 50 }
40 51
41 if (activityType === 'CacheFile') { 52 if (activityType === 'CacheFile') {
42 return retryTransactionWrapper(processCreateCacheFile, activity, byActor) 53 return retryTransactionWrapper(processCreateCacheFile, activity, activityObject, byActor)
43 } 54 }
44 55
45 if (activityType === 'Playlist') { 56 if (activityType === 'Playlist') {
46 return retryTransactionWrapper(processCreatePlaylist, activity, byActor) 57 return retryTransactionWrapper(processCreatePlaylist, activity, activityObject, byActor)
47 } 58 }
48 59
49 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) 60 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@@ -58,9 +69,7 @@ export {
58 69
59// --------------------------------------------------------------------------- 70// ---------------------------------------------------------------------------
60 71
61async function processCreateVideo (activity: ActivityCreate, notify: boolean) { 72async function processCreateVideo (videoToCreateData: VideoObject, notify: boolean) {
62 const videoToCreateData = activity.object as VideoObject
63
64 const syncParam = { rates: false, shares: false, comments: false, thumbnail: true, refreshVideo: false } 73 const syncParam = { rates: false, shares: false, comments: false, thumbnail: true, refreshVideo: false }
65 const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam }) 74 const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam })
66 75
@@ -69,11 +78,13 @@ async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
69 return video 78 return video
70} 79}
71 80
72async function processCreateCacheFile (activity: ActivityCreate, byActor: MActorSignature) { 81async function processCreateCacheFile (
82 activity: ActivityCreate<CacheFileObject | string>,
83 cacheFile: CacheFileObject,
84 byActor: MActorSignature
85) {
73 if (await isRedundancyAccepted(activity, byActor) !== true) return 86 if (await isRedundancyAccepted(activity, byActor) !== true) return
74 87
75 const cacheFile = activity.object as CacheFileObject
76
77 const { video } = await getOrCreateAPVideo({ videoObject: cacheFile.object }) 88 const { video } = await getOrCreateAPVideo({ videoObject: cacheFile.object })
78 89
79 await sequelizeTypescript.transaction(async t => { 90 await sequelizeTypescript.transaction(async t => {
@@ -87,9 +98,7 @@ async function processCreateCacheFile (activity: ActivityCreate, byActor: MActor
87 } 98 }
88} 99}
89 100
90async function processCreateWatchAction (activity: ActivityCreate) { 101async function processCreateWatchAction (watchAction: WatchActionObject) {
91 const watchAction = activity.object as WatchActionObject
92
93 if (watchAction.actionStatus !== 'CompletedActionStatus') return 102 if (watchAction.actionStatus !== 'CompletedActionStatus') return
94 103
95 const video = await VideoModel.loadByUrl(watchAction.object) 104 const video = await VideoModel.loadByUrl(watchAction.object)
@@ -100,8 +109,12 @@ async function processCreateWatchAction (activity: ActivityCreate) {
100 }) 109 })
101} 110}
102 111
103async function processCreateVideoComment (activity: ActivityCreate, byActor: MActorSignature, notify: boolean) { 112async function processCreateVideoComment (
104 const commentObject = activity.object as VideoCommentObject 113 activity: ActivityCreate<VideoCommentObject | string>,
114 commentObject: VideoCommentObject,
115 byActor: MActorSignature,
116 notify: boolean
117) {
105 const byAccount = byActor.Account 118 const byAccount = byActor.Account
106 119
107 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) 120 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
@@ -144,8 +157,11 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: MAc
144 if (created && notify) Notifier.Instance.notifyOnNewComment(comment) 157 if (created && notify) Notifier.Instance.notifyOnNewComment(comment)
145} 158}
146 159
147async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorSignature) { 160async function processCreatePlaylist (
148 const playlistObject = activity.object as PlaylistObject 161 activity: ActivityCreate<PlaylistObject | string>,
162 playlistObject: PlaylistObject,
163 byActor: MActorSignature
164) {
149 const byAccount = byActor.Account 165 const byAccount = byActor.Account
150 166
151 if (!byAccount) throw new Error('Cannot create video playlist with the non account actor ' + byActor.url) 167 if (!byAccount) throw new Error('Cannot create video playlist with the non account actor ' + byActor.url)
diff --git a/server/lib/activitypub/process/process-dislike.ts b/server/lib/activitypub/process/process-dislike.ts
index 44e349b22..4e270f917 100644
--- a/server/lib/activitypub/process/process-dislike.ts
+++ b/server/lib/activitypub/process/process-dislike.ts
@@ -1,5 +1,5 @@
1import { VideoModel } from '@server/models/video/video' 1import { VideoModel } from '@server/models/video/video'
2import { ActivityCreate, ActivityDislike, DislikeObject } from '@shared/models' 2import { ActivityDislike } from '@shared/models'
3import { retryTransactionWrapper } from '../../../helpers/database-utils' 3import { retryTransactionWrapper } from '../../../helpers/database-utils'
4import { sequelizeTypescript } from '../../../initializers/database' 4import { sequelizeTypescript } from '../../../initializers/database'
5import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 5import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
@@ -7,7 +7,7 @@ import { APProcessorOptions } from '../../../types/activitypub-processor.model'
7import { MActorSignature } from '../../../types/models' 7import { MActorSignature } from '../../../types/models'
8import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos' 8import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
9 9
10async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) { 10async function processDislikeActivity (options: APProcessorOptions<ActivityDislike>) {
11 const { activity, byActor } = options 11 const { activity, byActor } = options
12 return retryTransactionWrapper(processDislike, activity, byActor) 12 return retryTransactionWrapper(processDislike, activity, byActor)
13} 13}
@@ -20,11 +20,8 @@ export {
20 20
21// --------------------------------------------------------------------------- 21// ---------------------------------------------------------------------------
22 22
23async function processDislike (activity: ActivityCreate | ActivityDislike, byActor: MActorSignature) { 23async function processDislike (activity: ActivityDislike, byActor: MActorSignature) {
24 const dislikeObject = activity.type === 'Dislike' 24 const dislikeObject = activity.object
25 ? activity.object
26 : (activity.object as DislikeObject).object
27
28 const byAccount = byActor.Account 25 const byAccount = byActor.Account
29 26
30 if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url) 27 if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts
index 10f58ef27..bea285670 100644
--- a/server/lib/activitypub/process/process-flag.ts
+++ b/server/lib/activitypub/process/process-flag.ts
@@ -3,7 +3,7 @@ import { AccountModel } from '@server/models/account/account'
3import { VideoModel } from '@server/models/video/video' 3import { VideoModel } from '@server/models/video/video'
4import { VideoCommentModel } from '@server/models/video/video-comment' 4import { VideoCommentModel } from '@server/models/video/video-comment'
5import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 5import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
6import { AbuseObject, AbuseState, ActivityCreate, ActivityFlag } from '@shared/models' 6import { AbuseState, ActivityFlag } from '@shared/models'
7import { retryTransactionWrapper } from '../../../helpers/database-utils' 7import { retryTransactionWrapper } from '../../../helpers/database-utils'
8import { logger } from '../../../helpers/logger' 8import { logger } from '../../../helpers/logger'
9import { sequelizeTypescript } from '../../../initializers/database' 9import { sequelizeTypescript } from '../../../initializers/database'
@@ -11,7 +11,7 @@ import { getAPId } from '../../../lib/activitypub/activity'
11import { APProcessorOptions } from '../../../types/activitypub-processor.model' 11import { APProcessorOptions } from '../../../types/activitypub-processor.model'
12import { MAccountDefault, MActorSignature, MCommentOwnerVideo } from '../../../types/models' 12import { MAccountDefault, MActorSignature, MCommentOwnerVideo } from '../../../types/models'
13 13
14async function processFlagActivity (options: APProcessorOptions<ActivityCreate | ActivityFlag>) { 14async function processFlagActivity (options: APProcessorOptions<ActivityFlag>) {
15 const { activity, byActor } = options 15 const { activity, byActor } = options
16 16
17 return retryTransactionWrapper(processCreateAbuse, activity, byActor) 17 return retryTransactionWrapper(processCreateAbuse, activity, byActor)
@@ -25,9 +25,7 @@ export {
25 25
26// --------------------------------------------------------------------------- 26// ---------------------------------------------------------------------------
27 27
28async function processCreateAbuse (activity: ActivityCreate | ActivityFlag, byActor: MActorSignature) { 28async function processCreateAbuse (flag: ActivityFlag, byActor: MActorSignature) {
29 const flag = activity.type === 'Flag' ? activity : (activity.object as AbuseObject)
30
31 const account = byActor.Account 29 const account = byActor.Account
32 if (!account) throw new Error('Cannot create abuse with the non account actor ' + byActor.url) 30 if (!account) throw new Error('Cannot create abuse with the non account actor ' + byActor.url)
33 31
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index 99423a72b..25f68724d 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -1,6 +1,14 @@
1import { VideoModel } from '@server/models/video/video' 1import { VideoModel } from '@server/models/video/video'
2import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo, CacheFileObject } from '../../../../shared/models/activitypub' 2import {
3import { DislikeObject } from '../../../../shared/models/activitypub/objects' 3 ActivityAnnounce,
4 ActivityCreate,
5 ActivityDislike,
6 ActivityFollow,
7 ActivityLike,
8 ActivityUndo,
9 ActivityUndoObject,
10 CacheFileObject
11} from '../../../../shared/models/activitypub'
4import { retryTransactionWrapper } from '../../../helpers/database-utils' 12import { retryTransactionWrapper } from '../../../helpers/database-utils'
5import { logger } from '../../../helpers/logger' 13import { logger } from '../../../helpers/logger'
6import { sequelizeTypescript } from '../../../initializers/database' 14import { sequelizeTypescript } from '../../../initializers/database'
@@ -11,10 +19,11 @@ import { VideoRedundancyModel } from '../../../models/redundancy/video-redundanc
11import { VideoShareModel } from '../../../models/video/video-share' 19import { VideoShareModel } from '../../../models/video/video-share'
12import { APProcessorOptions } from '../../../types/activitypub-processor.model' 20import { APProcessorOptions } from '../../../types/activitypub-processor.model'
13import { MActorSignature } from '../../../types/models' 21import { MActorSignature } from '../../../types/models'
22import { fetchAPObject } from '../activity'
14import { forwardVideoRelatedActivity } from '../send/shared/send-utils' 23import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
15import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos' 24import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
16 25
17async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) { 26async function processUndoActivity (options: APProcessorOptions<ActivityUndo<ActivityUndoObject>>) {
18 const { activity, byActor } = options 27 const { activity, byActor } = options
19 const activityToUndo = activity.object 28 const activityToUndo = activity.object
20 29
@@ -23,8 +32,10 @@ async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
23 } 32 }
24 33
25 if (activityToUndo.type === 'Create') { 34 if (activityToUndo.type === 'Create') {
26 if (activityToUndo.object.type === 'CacheFile') { 35 const objectToUndo = await fetchAPObject<CacheFileObject>(activityToUndo.object)
27 return retryTransactionWrapper(processUndoCacheFile, byActor, activity) 36
37 if (objectToUndo.type === 'CacheFile') {
38 return retryTransactionWrapper(processUndoCacheFile, byActor, activity, objectToUndo)
28 } 39 }
29 } 40 }
30 41
@@ -53,8 +64,8 @@ export {
53 64
54// --------------------------------------------------------------------------- 65// ---------------------------------------------------------------------------
55 66
56async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) { 67async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo<ActivityLike>) {
57 const likeActivity = activity.object as ActivityLike 68 const likeActivity = activity.object
58 69
59 const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: likeActivity.object }) 70 const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: likeActivity.object })
60 // We don't care about likes of remote videos 71 // We don't care about likes of remote videos
@@ -78,12 +89,10 @@ async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo
78 }) 89 })
79} 90}
80 91
81async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo) { 92async function processUndoDislike (byActor: MActorSignature, activity: ActivityUndo<ActivityDislike>) {
82 const dislike = activity.object.type === 'Dislike' 93 const dislikeActivity = activity.object
83 ? activity.object
84 : activity.object.object as DislikeObject
85 94
86 const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: dislike.object }) 95 const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: dislikeActivity.object })
87 // We don't care about likes of remote videos 96 // We don't care about likes of remote videos
88 if (!onlyVideo.isOwned()) return 97 if (!onlyVideo.isOwned()) return
89 98
@@ -91,7 +100,7 @@ async function processUndoDislike (byActor: MActorSignature, activity: ActivityU
91 if (!byActor.Account) throw new Error('Unknown account ' + byActor.url) 100 if (!byActor.Account) throw new Error('Unknown account ' + byActor.url)
92 101
93 const video = await VideoModel.loadFull(onlyVideo.id, t) 102 const video = await VideoModel.loadFull(onlyVideo.id, t)
94 const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, dislike.id, t) 103 const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, dislikeActivity.id, t)
95 if (!rate || rate.type !== 'dislike') { 104 if (!rate || rate.type !== 'dislike') {
96 logger.warn(`Unknown dislike by account %d for video %d.`, byActor.Account.id, video.id) 105 logger.warn(`Unknown dislike by account %d for video %d.`, byActor.Account.id, video.id)
97 return 106 return
@@ -107,9 +116,11 @@ async function processUndoDislike (byActor: MActorSignature, activity: ActivityU
107 116
108// --------------------------------------------------------------------------- 117// ---------------------------------------------------------------------------
109 118
110async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) { 119async function processUndoCacheFile (
111 const cacheFileObject = activity.object.object as CacheFileObject 120 byActor: MActorSignature,
112 121 activity: ActivityUndo<ActivityCreate<CacheFileObject>>,
122 cacheFileObject: CacheFileObject
123) {
113 const { video } = await getOrCreateAPVideo({ videoObject: cacheFileObject.object }) 124 const { video } = await getOrCreateAPVideo({ videoObject: cacheFileObject.object })
114 125
115 return sequelizeTypescript.transaction(async t => { 126 return sequelizeTypescript.transaction(async t => {
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 4afdbd430..9caa74e04 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -1,5 +1,5 @@
1import { isRedundancyAccepted } from '@server/lib/redundancy' 1import { isRedundancyAccepted } from '@server/lib/redundancy'
2import { ActivityUpdate, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub' 2import { ActivityUpdate, ActivityUpdateObject, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub'
3import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' 3import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
4import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' 4import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
5import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' 5import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file'
@@ -10,16 +10,18 @@ import { sequelizeTypescript } from '../../../initializers/database'
10import { ActorModel } from '../../../models/actor/actor' 10import { ActorModel } from '../../../models/actor/actor'
11import { APProcessorOptions } from '../../../types/activitypub-processor.model' 11import { APProcessorOptions } from '../../../types/activitypub-processor.model'
12import { MActorFull, MActorSignature } from '../../../types/models' 12import { MActorFull, MActorSignature } from '../../../types/models'
13import { fetchAPObject } from '../activity'
13import { APActorUpdater } from '../actors/updater' 14import { APActorUpdater } from '../actors/updater'
14import { createOrUpdateCacheFile } from '../cache-file' 15import { createOrUpdateCacheFile } from '../cache-file'
15import { createOrUpdateVideoPlaylist } from '../playlists' 16import { createOrUpdateVideoPlaylist } from '../playlists'
16import { forwardVideoRelatedActivity } from '../send/shared/send-utils' 17import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
17import { APVideoUpdater, getOrCreateAPVideo } from '../videos' 18import { APVideoUpdater, getOrCreateAPVideo } from '../videos'
18 19
19async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { 20async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate<ActivityUpdateObject>>) {
20 const { activity, byActor } = options 21 const { activity, byActor } = options
21 22
22 const objectType = activity.object.type 23 const object = await fetchAPObject(activity.object)
24 const objectType = object.type
23 25
24 if (objectType === 'Video') { 26 if (objectType === 'Video') {
25 return retryTransactionWrapper(processUpdateVideo, activity) 27 return retryTransactionWrapper(processUpdateVideo, activity)
@@ -28,17 +30,17 @@ async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate
28 if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') { 30 if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
29 // We need more attributes 31 // We need more attributes
30 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url) 32 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
31 return retryTransactionWrapper(processUpdateActor, byActorFull, activity) 33 return retryTransactionWrapper(processUpdateActor, byActorFull, object)
32 } 34 }
33 35
34 if (objectType === 'CacheFile') { 36 if (objectType === 'CacheFile') {
35 // We need more attributes 37 // We need more attributes
36 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url) 38 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
37 return retryTransactionWrapper(processUpdateCacheFile, byActorFull, activity) 39 return retryTransactionWrapper(processUpdateCacheFile, byActorFull, activity, object)
38 } 40 }
39 41
40 if (objectType === 'Playlist') { 42 if (objectType === 'Playlist') {
41 return retryTransactionWrapper(processUpdatePlaylist, byActor, activity) 43 return retryTransactionWrapper(processUpdatePlaylist, byActor, activity, object)
42 } 44 }
43 45
44 return undefined 46 return undefined
@@ -52,7 +54,7 @@ export {
52 54
53// --------------------------------------------------------------------------- 55// ---------------------------------------------------------------------------
54 56
55async function processUpdateVideo (activity: ActivityUpdate) { 57async function processUpdateVideo (activity: ActivityUpdate<VideoObject | string>) {
56 const videoObject = activity.object as VideoObject 58 const videoObject = activity.object as VideoObject
57 59
58 if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) { 60 if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
@@ -72,11 +74,13 @@ async function processUpdateVideo (activity: ActivityUpdate) {
72 return updater.update(activity.to) 74 return updater.update(activity.to)
73} 75}
74 76
75async function processUpdateCacheFile (byActor: MActorSignature, activity: ActivityUpdate) { 77async function processUpdateCacheFile (
78 byActor: MActorSignature,
79 activity: ActivityUpdate<CacheFileObject | string>,
80 cacheFileObject: CacheFileObject
81) {
76 if (await isRedundancyAccepted(activity, byActor) !== true) return 82 if (await isRedundancyAccepted(activity, byActor) !== true) return
77 83
78 const cacheFileObject = activity.object as CacheFileObject
79
80 if (!isCacheFileObjectValid(cacheFileObject)) { 84 if (!isCacheFileObjectValid(cacheFileObject)) {
81 logger.debug('Cache file object sent by update is not valid.', { cacheFileObject }) 85 logger.debug('Cache file object sent by update is not valid.', { cacheFileObject })
82 return undefined 86 return undefined
@@ -96,19 +100,19 @@ async function processUpdateCacheFile (byActor: MActorSignature, activity: Activ
96 } 100 }
97} 101}
98 102
99async function processUpdateActor (actor: MActorFull, activity: ActivityUpdate) { 103async function processUpdateActor (actor: MActorFull, actorObject: ActivityPubActor) {
100 const actorObject = activity.object as ActivityPubActor
101
102 logger.debug('Updating remote account "%s".', actorObject.url) 104 logger.debug('Updating remote account "%s".', actorObject.url)
103 105
104 const updater = new APActorUpdater(actorObject, actor) 106 const updater = new APActorUpdater(actorObject, actor)
105 return updater.update() 107 return updater.update()
106} 108}
107 109
108async function processUpdatePlaylist (byActor: MActorSignature, activity: ActivityUpdate) { 110async function processUpdatePlaylist (
109 const playlistObject = activity.object as PlaylistObject 111 byActor: MActorSignature,
112 activity: ActivityUpdate<PlaylistObject | string>,
113 playlistObject: PlaylistObject
114) {
110 const byAccount = byActor.Account 115 const byAccount = byActor.Account
111
112 if (!byAccount) throw new Error('Cannot update video playlist with the non account actor ' + byActor.url) 116 if (!byAccount) throw new Error('Cannot update video playlist with the non account actor ' + byActor.url)
113 117
114 await createOrUpdateVideoPlaylist(playlistObject, activity.to) 118 await createOrUpdateVideoPlaylist(playlistObject, activity.to)
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 0e996ab80..2cd4db14d 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -1,6 +1,14 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { getServerActor } from '@server/models/application/application' 2import { getServerActor } from '@server/models/application/application'
3import { ActivityAudience, ActivityCreate, ContextType, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' 3import {
4 ActivityAudience,
5 ActivityCreate,
6 ActivityCreateObject,
7 ContextType,
8 VideoCommentObject,
9 VideoPlaylistPrivacy,
10 VideoPrivacy
11} from '@shared/models'
4import { logger, loggerTagsFactory } from '../../../helpers/logger' 12import { logger, loggerTagsFactory } from '../../../helpers/logger'
5import { VideoCommentModel } from '../../../models/video/video-comment' 13import { VideoCommentModel } from '../../../models/video/video-comment'
6import { 14import {
@@ -107,7 +115,7 @@ async function sendCreateVideoComment (comment: MCommentOwnerVideo, transaction:
107 115
108 const byActor = comment.Account.Actor 116 const byActor = comment.Account.Actor
109 const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, transaction) 117 const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, transaction)
110 const commentObject = comment.toActivityPubObject(threadParentComments) 118 const commentObject = comment.toActivityPubObject(threadParentComments) as VideoCommentObject
111 119
112 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, transaction) 120 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, transaction)
113 // Add the actor that commented too 121 // Add the actor that commented too
@@ -168,7 +176,12 @@ async function sendCreateVideoComment (comment: MCommentOwnerVideo, transaction:
168 }) 176 })
169} 177}
170 178
171function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate { 179function buildCreateActivity <T extends ActivityCreateObject> (
180 url: string,
181 byActor: MActorLight,
182 object: T,
183 audience?: ActivityAudience
184): ActivityCreate<T> {
172 if (!audience) audience = getAudience(byActor) 185 if (!audience) audience = getAudience(byActor)
173 186
174 return audiencify( 187 return audiencify(
@@ -176,7 +189,9 @@ function buildCreateActivity (url: string, byActor: MActorLight, object: any, au
176 type: 'Create' as 'Create', 189 type: 'Create' as 'Create',
177 id: url + '/activity', 190 id: url + '/activity',
178 actor: byActor.url, 191 actor: byActor.url,
179 object: audiencify(object, audience) 192 object: typeof object === 'string'
193 ? object
194 : audiencify(object, audience)
180 }, 195 },
181 audience 196 audience
182 ) 197 )
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
index b8eb47ff6..b0b48c9c4 100644
--- a/server/lib/activitypub/send/send-undo.ts
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -1,14 +1,5 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { 2import { ActivityAudience, ActivityDislike, ActivityLike, ActivityUndo, ActivityUndoObject, ContextType } from '@shared/models'
3 ActivityAnnounce,
4 ActivityAudience,
5 ActivityCreate,
6 ActivityDislike,
7 ActivityFollow,
8 ActivityLike,
9 ActivityUndo,
10 ContextType
11} from '@shared/models'
12import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
13import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
14import { 5import {
@@ -128,12 +119,12 @@ export {
128 119
129// --------------------------------------------------------------------------- 120// ---------------------------------------------------------------------------
130 121
131function undoActivityData ( 122function undoActivityData <T extends ActivityUndoObject> (
132 url: string, 123 url: string,
133 byActor: MActorAudience, 124 byActor: MActorAudience,
134 object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce, 125 object: T,
135 audience?: ActivityAudience 126 audience?: ActivityAudience
136): ActivityUndo { 127): ActivityUndo<T> {
137 if (!audience) audience = getAudience(byActor) 128 if (!audience) audience = getAudience(byActor)
138 129
139 return audiencify( 130 return audiencify(
@@ -151,7 +142,7 @@ async function sendUndoVideoRelatedActivity (options: {
151 byActor: MActor 142 byActor: MActor
152 video: MVideoAccountLight 143 video: MVideoAccountLight
153 url: string 144 url: string
154 activity: ActivityFollow | ActivityCreate | ActivityAnnounce 145 activity: ActivityUndoObject
155 contextType: ContextType 146 contextType: ContextType
156 transaction: Transaction 147 transaction: Transaction
157}) { 148}) {
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index 3d2b437e4..f3fb741c6 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -1,6 +1,6 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { getServerActor } from '@server/models/application/application' 2import { getServerActor } from '@server/models/application/application'
3import { ActivityAudience, ActivityUpdate, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' 3import { ActivityAudience, ActivityUpdate, ActivityUpdateObject, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
4import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
5import { AccountModel } from '../../../models/account/account' 5import { AccountModel } from '../../../models/account/account'
6import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
@@ -137,7 +137,12 @@ export {
137 137
138// --------------------------------------------------------------------------- 138// ---------------------------------------------------------------------------
139 139
140function buildUpdateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityUpdate { 140function buildUpdateActivity (
141 url: string,
142 byActor: MActorLight,
143 object: ActivityUpdateObject,
144 audience?: ActivityAudience
145): ActivityUpdate<ActivityUpdateObject> {
141 if (!audience) audience = getAudience(byActor) 146 if (!audience) audience = getAudience(byActor)
142 147
143 return audiencify( 148 return audiencify(
diff --git a/server/lib/activitypub/videos/get.ts b/server/lib/activitypub/videos/get.ts
index 14ba55034..92387c5d4 100644
--- a/server/lib/activitypub/videos/get.ts
+++ b/server/lib/activitypub/videos/get.ts
@@ -3,7 +3,7 @@ import { logger } from '@server/helpers/logger'
3import { JobQueue } from '@server/lib/job-queue' 3import { JobQueue } from '@server/lib/job-queue'
4import { loadVideoByUrl, VideoLoadByUrlType } from '@server/lib/model-loaders' 4import { loadVideoByUrl, VideoLoadByUrlType } from '@server/lib/model-loaders'
5import { MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models' 5import { MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models'
6import { APObject } from '@shared/models' 6import { APObjectId } from '@shared/models'
7import { getAPId } from '../activity' 7import { getAPId } from '../activity'
8import { refreshVideoIfNeeded } from './refresh' 8import { refreshVideoIfNeeded } from './refresh'
9import { APVideoCreator, fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared' 9import { APVideoCreator, fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared'
@@ -15,21 +15,21 @@ type GetVideoResult <T> = Promise<{
15}> 15}>
16 16
17type GetVideoParamAll = { 17type GetVideoParamAll = {
18 videoObject: APObject 18 videoObject: APObjectId
19 syncParam?: SyncParam 19 syncParam?: SyncParam
20 fetchType?: 'all' 20 fetchType?: 'all'
21 allowRefresh?: boolean 21 allowRefresh?: boolean
22} 22}
23 23
24type GetVideoParamImmutable = { 24type GetVideoParamImmutable = {
25 videoObject: APObject 25 videoObject: APObjectId
26 syncParam?: SyncParam 26 syncParam?: SyncParam
27 fetchType: 'only-immutable-attributes' 27 fetchType: 'only-immutable-attributes'
28 allowRefresh: false 28 allowRefresh: false
29} 29}
30 30
31type GetVideoParamOther = { 31type GetVideoParamOther = {
32 videoObject: APObject 32 videoObject: APObjectId
33 syncParam?: SyncParam 33 syncParam?: SyncParam
34 fetchType?: 'all' | 'only-video' 34 fetchType?: 'all' | 'only-video'
35 allowRefresh?: boolean 35 allowRefresh?: boolean