diff options
Diffstat (limited to 'server')
26 files changed, 341 insertions, 305 deletions
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 8c9b0aa50..0d114dcd2 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -3,7 +3,6 @@ import * as multer from 'multer' | |||
3 | import { extname, join } from 'path' | 3 | import { extname, join } from 'path' |
4 | import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared' | 4 | import { VideoCreate, VideoPrivacy, VideoUpdate } from '../../../../shared' |
5 | import { | 5 | import { |
6 | fetchRemoteVideoDescription, | ||
7 | generateRandomString, | 6 | generateRandomString, |
8 | getFormattedObjects, | 7 | getFormattedObjects, |
9 | getVideoFileHeight, | 8 | getVideoFileHeight, |
@@ -12,7 +11,6 @@ import { | |||
12 | resetSequelizeInstance, | 11 | resetSequelizeInstance, |
13 | retryTransactionWrapper | 12 | retryTransactionWrapper |
14 | } from '../../../helpers' | 13 | } from '../../../helpers' |
15 | import { getVideoActivityPubUrl, shareVideoByServer } from '../../../helpers/activitypub' | ||
16 | import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers' | 14 | import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers' |
17 | import { database as db } from '../../../initializers/database' | 15 | import { database as db } from '../../../initializers/database' |
18 | import { sendAddVideo } from '../../../lib/activitypub/send/send-add' | 16 | import { sendAddVideo } from '../../../lib/activitypub/send/send-add' |
@@ -37,6 +35,9 @@ import { abuseVideoRouter } from './abuse' | |||
37 | import { blacklistRouter } from './blacklist' | 35 | import { blacklistRouter } from './blacklist' |
38 | import { videoChannelRouter } from './channel' | 36 | import { videoChannelRouter } from './channel' |
39 | import { rateVideoRouter } from './rate' | 37 | import { rateVideoRouter } from './rate' |
38 | import { getVideoActivityPubUrl } from '../../../lib/activitypub/url' | ||
39 | import { shareVideoByServer } from '../../../lib/activitypub/share' | ||
40 | import { fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' | ||
40 | 41 | ||
41 | const videosRouter = express.Router() | 42 | const videosRouter = express.Router() |
42 | 43 | ||
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index 9622a1801..5c577bb61 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts | |||
@@ -1,244 +1,7 @@ | |||
1 | import { join } from 'path' | ||
2 | import * as request from 'request' | ||
3 | import * as Sequelize from 'sequelize' | ||
4 | import * as url from 'url' | ||
5 | import { ActivityIconObject } from '../../shared/index' | ||
6 | import { Activity } from '../../shared/models/activitypub/activity' | 1 | import { Activity } from '../../shared/models/activitypub/activity' |
7 | import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' | ||
8 | import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object' | ||
9 | import { ResultList } from '../../shared/models/result-list.model' | 2 | import { ResultList } from '../../shared/models/result-list.model' |
10 | import { database as db, REMOTE_SCHEME } from '../initializers' | ||
11 | import { ACTIVITY_PUB, CONFIG, STATIC_PATHS } from '../initializers/constants' | ||
12 | import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/process/misc' | ||
13 | import { sendVideoAnnounce } from '../lib/activitypub/send/send-announce' | ||
14 | import { sendVideoChannelAnnounce } from '../lib/index' | ||
15 | import { AccountFollowInstance } from '../models/account/account-follow-interface' | ||
16 | import { AccountInstance } from '../models/account/account-interface' | 3 | import { AccountInstance } from '../models/account/account-interface' |
17 | import { VideoAbuseInstance } from '../models/video/video-abuse-interface' | ||
18 | import { VideoChannelInstance } from '../models/video/video-channel-interface' | ||
19 | import { VideoInstance } from '../models/video/video-interface' | ||
20 | import { isRemoteAccountValid } from './custom-validators' | ||
21 | import { logger } from './logger' | ||
22 | import { signObject } from './peertube-crypto' | 4 | import { signObject } from './peertube-crypto' |
23 | import { doRequest, doRequestAndSaveToFile } from './requests' | ||
24 | import { getServerAccount } from './utils' | ||
25 | import { isVideoChannelObjectValid } from './custom-validators/activitypub/video-channels' | ||
26 | |||
27 | function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) { | ||
28 | const thumbnailName = video.getThumbnailName() | ||
29 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName) | ||
30 | |||
31 | const options = { | ||
32 | method: 'GET', | ||
33 | uri: icon.url | ||
34 | } | ||
35 | return doRequestAndSaveToFile(options, thumbnailPath) | ||
36 | } | ||
37 | |||
38 | async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { | ||
39 | const serverAccount = await getServerAccount() | ||
40 | |||
41 | await db.VideoChannelShare.create({ | ||
42 | accountId: serverAccount.id, | ||
43 | videoChannelId: videoChannel.id | ||
44 | }, { transaction: t }) | ||
45 | |||
46 | return sendVideoChannelAnnounce(serverAccount, videoChannel, t) | ||
47 | } | ||
48 | |||
49 | async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) { | ||
50 | const serverAccount = await getServerAccount() | ||
51 | |||
52 | await db.VideoShare.create({ | ||
53 | accountId: serverAccount.id, | ||
54 | videoId: video.id | ||
55 | }, { transaction: t }) | ||
56 | |||
57 | return sendVideoAnnounce(serverAccount, video, t) | ||
58 | } | ||
59 | |||
60 | function getVideoActivityPubUrl (video: VideoInstance) { | ||
61 | return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid | ||
62 | } | ||
63 | |||
64 | function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) { | ||
65 | return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid | ||
66 | } | ||
67 | |||
68 | function getAccountActivityPubUrl (accountName: string) { | ||
69 | return CONFIG.WEBSERVER.URL + '/account/' + accountName | ||
70 | } | ||
71 | |||
72 | function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) { | ||
73 | return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id | ||
74 | } | ||
75 | |||
76 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { | ||
77 | const me = accountFollow.AccountFollower | ||
78 | const following = accountFollow.AccountFollowing | ||
79 | |||
80 | return me.url + '#follows/' + following.id | ||
81 | } | ||
82 | |||
83 | function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) { | ||
84 | const follower = accountFollow.AccountFollower | ||
85 | const me = accountFollow.AccountFollowing | ||
86 | |||
87 | return follower.url + '#accepts/follows/' + me.id | ||
88 | } | ||
89 | |||
90 | function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) { | ||
91 | return originalUrl + '#announces/' + byAccount.id | ||
92 | } | ||
93 | |||
94 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { | ||
95 | return originalUrl + '#updates/' + updatedAt | ||
96 | } | ||
97 | |||
98 | function getUndoActivityPubUrl (originalUrl: string) { | ||
99 | return originalUrl + '/undo' | ||
100 | } | ||
101 | |||
102 | async function getOrCreateAccount (accountUrl: string) { | ||
103 | let account = await db.Account.loadByUrl(accountUrl) | ||
104 | |||
105 | // We don't have this account in our database, fetch it on remote | ||
106 | if (!account) { | ||
107 | const res = await fetchRemoteAccountAndCreateServer(accountUrl) | ||
108 | if (res === undefined) throw new Error('Cannot fetch remote account.') | ||
109 | |||
110 | // Save our new account in database | ||
111 | account = await res.account.save() | ||
112 | } | ||
113 | |||
114 | return account | ||
115 | } | ||
116 | |||
117 | async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
118 | let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl) | ||
119 | |||
120 | // We don't have this account in our database, fetch it on remote | ||
121 | if (!videoChannel) { | ||
122 | videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl) | ||
123 | if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.') | ||
124 | |||
125 | // Save our new video channel in database | ||
126 | await videoChannel.save() | ||
127 | } | ||
128 | |||
129 | return videoChannel | ||
130 | } | ||
131 | |||
132 | async function fetchRemoteAccountAndCreateServer (accountUrl: string) { | ||
133 | const options = { | ||
134 | uri: accountUrl, | ||
135 | method: 'GET', | ||
136 | headers: { | ||
137 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
138 | } | ||
139 | } | ||
140 | |||
141 | logger.info('Fetching remote account %s.', accountUrl) | ||
142 | |||
143 | let requestResult | ||
144 | try { | ||
145 | requestResult = await doRequest(options) | ||
146 | } catch (err) { | ||
147 | logger.warn('Cannot fetch remote account %s.', accountUrl, err) | ||
148 | return undefined | ||
149 | } | ||
150 | |||
151 | const accountJSON: ActivityPubActor = JSON.parse(requestResult.body) | ||
152 | if (isRemoteAccountValid(accountJSON) === false) { | ||
153 | logger.debug('Remote account JSON is not valid.', { accountJSON }) | ||
154 | return undefined | ||
155 | } | ||
156 | |||
157 | const followersCount = await fetchAccountCount(accountJSON.followers) | ||
158 | const followingCount = await fetchAccountCount(accountJSON.following) | ||
159 | |||
160 | const account = db.Account.build({ | ||
161 | uuid: accountJSON.uuid, | ||
162 | name: accountJSON.preferredUsername, | ||
163 | url: accountJSON.url, | ||
164 | publicKey: accountJSON.publicKey.publicKeyPem, | ||
165 | privateKey: null, | ||
166 | followersCount: followersCount, | ||
167 | followingCount: followingCount, | ||
168 | inboxUrl: accountJSON.inbox, | ||
169 | outboxUrl: accountJSON.outbox, | ||
170 | sharedInboxUrl: accountJSON.endpoints.sharedInbox, | ||
171 | followersUrl: accountJSON.followers, | ||
172 | followingUrl: accountJSON.following | ||
173 | }) | ||
174 | |||
175 | const accountHost = url.parse(account.url).host | ||
176 | const serverOptions = { | ||
177 | where: { | ||
178 | host: accountHost | ||
179 | }, | ||
180 | defaults: { | ||
181 | host: accountHost | ||
182 | } | ||
183 | } | ||
184 | const [ server ] = await db.Server.findOrCreate(serverOptions) | ||
185 | account.set('serverId', server.id) | ||
186 | |||
187 | return { account, server } | ||
188 | } | ||
189 | |||
190 | async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
191 | const options = { | ||
192 | uri: videoChannelUrl, | ||
193 | method: 'GET', | ||
194 | headers: { | ||
195 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
196 | } | ||
197 | } | ||
198 | |||
199 | logger.info('Fetching remote video channel %s.', videoChannelUrl) | ||
200 | |||
201 | let requestResult | ||
202 | try { | ||
203 | requestResult = await doRequest(options) | ||
204 | } catch (err) { | ||
205 | logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err) | ||
206 | return undefined | ||
207 | } | ||
208 | |||
209 | const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body) | ||
210 | if (isVideoChannelObjectValid(videoChannelJSON) === false) { | ||
211 | logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON }) | ||
212 | return undefined | ||
213 | } | ||
214 | |||
215 | const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount) | ||
216 | const videoChannel = db.VideoChannel.build(videoChannelAttributes) | ||
217 | videoChannel.Account = ownerAccount | ||
218 | |||
219 | return videoChannel | ||
220 | } | ||
221 | |||
222 | function fetchRemoteVideoPreview (video: VideoInstance) { | ||
223 | // FIXME: use url | ||
224 | const host = video.VideoChannel.Account.Server.host | ||
225 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) | ||
226 | |||
227 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) | ||
228 | } | ||
229 | |||
230 | async function fetchRemoteVideoDescription (video: VideoInstance) { | ||
231 | // FIXME: use url | ||
232 | const host = video.VideoChannel.Account.Server.host | ||
233 | const path = video.getDescriptionPath() | ||
234 | const options = { | ||
235 | uri: REMOTE_SCHEME.HTTP + '://' + host + path, | ||
236 | json: true | ||
237 | } | ||
238 | |||
239 | const { body } = await doRequest(options) | ||
240 | return body.description ? body.description : '' | ||
241 | } | ||
242 | 5 | ||
243 | function activityPubContextify <T> (data: T) { | 6 | function activityPubContextify <T> (data: T) { |
244 | return Object.assign(data,{ | 7 | return Object.assign(data,{ |
@@ -289,43 +52,7 @@ function buildSignedActivity (byAccount: AccountInstance, data: Object) { | |||
289 | // --------------------------------------------------------------------------- | 52 | // --------------------------------------------------------------------------- |
290 | 53 | ||
291 | export { | 54 | export { |
292 | fetchRemoteAccountAndCreateServer, | ||
293 | activityPubContextify, | 55 | activityPubContextify, |
294 | activityPubCollectionPagination, | 56 | activityPubCollectionPagination, |
295 | generateThumbnailFromUrl, | 57 | buildSignedActivity |
296 | getOrCreateAccount, | ||
297 | fetchRemoteVideoPreview, | ||
298 | fetchRemoteVideoDescription, | ||
299 | shareVideoChannelByServer, | ||
300 | shareVideoByServer, | ||
301 | getOrCreateVideoChannel, | ||
302 | buildSignedActivity, | ||
303 | getVideoActivityPubUrl, | ||
304 | getVideoChannelActivityPubUrl, | ||
305 | getAccountActivityPubUrl, | ||
306 | getVideoAbuseActivityPubUrl, | ||
307 | getAccountFollowActivityPubUrl, | ||
308 | getAccountFollowAcceptActivityPubUrl, | ||
309 | getAnnounceActivityPubUrl, | ||
310 | getUpdateActivityPubUrl, | ||
311 | getUndoActivityPubUrl | ||
312 | } | ||
313 | |||
314 | // --------------------------------------------------------------------------- | ||
315 | |||
316 | async function fetchAccountCount (url: string) { | ||
317 | const options = { | ||
318 | uri: url, | ||
319 | method: 'GET' | ||
320 | } | ||
321 | |||
322 | let requestResult | ||
323 | try { | ||
324 | requestResult = await doRequest(options) | ||
325 | } catch (err) { | ||
326 | logger.warn('Cannot fetch remote account count %s.', url, err) | ||
327 | return undefined | ||
328 | } | ||
329 | |||
330 | return requestResult.totalItems ? requestResult.totalItems : 0 | ||
331 | } | 58 | } |
diff --git a/server/helpers/webfinger.ts b/server/helpers/webfinger.ts index 31417e728..b7408c845 100644 --- a/server/helpers/webfinger.ts +++ b/server/helpers/webfinger.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import * as WebFinger from 'webfinger.js' | 1 | import * as WebFinger from 'webfinger.js' |
2 | import { WebFingerData } from '../../shared' | ||
2 | 3 | ||
3 | import { isTestInstance } from './core-utils' | 4 | import { isTestInstance } from './core-utils' |
4 | import { isActivityPubUrlValid } from './custom-validators' | 5 | import { isActivityPubUrlValid } from './custom-validators' |
5 | import { WebFingerData } from '../../shared' | 6 | import { fetchRemoteAccountAndCreateServer } from '../lib/activitypub/account' |
6 | import { fetchRemoteAccountAndCreateServer } from './activitypub' | ||
7 | 7 | ||
8 | const webfinger = new WebFinger({ | 8 | const webfinger = new WebFinger({ |
9 | webfist_fallback: false, | 9 | webfist_fallback: false, |
diff --git a/server/lib/activitypub/account.ts b/server/lib/activitypub/account.ts new file mode 100644 index 000000000..704a92e13 --- /dev/null +++ b/server/lib/activitypub/account.ts | |||
@@ -0,0 +1,104 @@ | |||
1 | import * as url from 'url' | ||
2 | import { ActivityPubActor } from '../../../shared/models/activitypub/activitypub-actor' | ||
3 | import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub/account' | ||
4 | import { logger } from '../../helpers/logger' | ||
5 | import { doRequest } from '../../helpers/requests' | ||
6 | import { ACTIVITY_PUB } from '../../initializers/constants' | ||
7 | import { database as db } from '../../initializers/database' | ||
8 | |||
9 | async function getOrCreateAccount (accountUrl: string) { | ||
10 | let account = await db.Account.loadByUrl(accountUrl) | ||
11 | |||
12 | // We don't have this account in our database, fetch it on remote | ||
13 | if (!account) { | ||
14 | const res = await fetchRemoteAccountAndCreateServer(accountUrl) | ||
15 | if (res === undefined) throw new Error('Cannot fetch remote account.') | ||
16 | |||
17 | // Save our new account in database | ||
18 | account = await res.account.save() | ||
19 | } | ||
20 | |||
21 | return account | ||
22 | } | ||
23 | |||
24 | async function fetchRemoteAccountAndCreateServer (accountUrl: string) { | ||
25 | const options = { | ||
26 | uri: accountUrl, | ||
27 | method: 'GET', | ||
28 | headers: { | ||
29 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
30 | } | ||
31 | } | ||
32 | |||
33 | logger.info('Fetching remote account %s.', accountUrl) | ||
34 | |||
35 | let requestResult | ||
36 | try { | ||
37 | requestResult = await doRequest(options) | ||
38 | } catch (err) { | ||
39 | logger.warn('Cannot fetch remote account %s.', accountUrl, err) | ||
40 | return undefined | ||
41 | } | ||
42 | |||
43 | const accountJSON: ActivityPubActor = JSON.parse(requestResult.body) | ||
44 | if (isRemoteAccountValid(accountJSON) === false) { | ||
45 | logger.debug('Remote account JSON is not valid.', { accountJSON }) | ||
46 | return undefined | ||
47 | } | ||
48 | |||
49 | const followersCount = await fetchAccountCount(accountJSON.followers) | ||
50 | const followingCount = await fetchAccountCount(accountJSON.following) | ||
51 | |||
52 | const account = db.Account.build({ | ||
53 | uuid: accountJSON.uuid, | ||
54 | name: accountJSON.preferredUsername, | ||
55 | url: accountJSON.url, | ||
56 | publicKey: accountJSON.publicKey.publicKeyPem, | ||
57 | privateKey: null, | ||
58 | followersCount: followersCount, | ||
59 | followingCount: followingCount, | ||
60 | inboxUrl: accountJSON.inbox, | ||
61 | outboxUrl: accountJSON.outbox, | ||
62 | sharedInboxUrl: accountJSON.endpoints.sharedInbox, | ||
63 | followersUrl: accountJSON.followers, | ||
64 | followingUrl: accountJSON.following | ||
65 | }) | ||
66 | |||
67 | const accountHost = url.parse(account.url).host | ||
68 | const serverOptions = { | ||
69 | where: { | ||
70 | host: accountHost | ||
71 | }, | ||
72 | defaults: { | ||
73 | host: accountHost | ||
74 | } | ||
75 | } | ||
76 | const [ server ] = await db.Server.findOrCreate(serverOptions) | ||
77 | account.set('serverId', server.id) | ||
78 | |||
79 | return { account, server } | ||
80 | } | ||
81 | |||
82 | export { | ||
83 | getOrCreateAccount, | ||
84 | fetchRemoteAccountAndCreateServer | ||
85 | } | ||
86 | |||
87 | // --------------------------------------------------------------------------- | ||
88 | |||
89 | async function fetchAccountCount (url: string) { | ||
90 | const options = { | ||
91 | uri: url, | ||
92 | method: 'GET' | ||
93 | } | ||
94 | |||
95 | let requestResult | ||
96 | try { | ||
97 | requestResult = await doRequest(options) | ||
98 | } catch (err) { | ||
99 | logger.warn('Cannot fetch remote account count %s.', url, err) | ||
100 | return undefined | ||
101 | } | ||
102 | |||
103 | return requestResult.totalItems ? requestResult.totalItems : 0 | ||
104 | } | ||
diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts index 1bea0a412..b1daa70bb 100644 --- a/server/lib/activitypub/index.ts +++ b/server/lib/activitypub/index.ts | |||
@@ -1,2 +1,7 @@ | |||
1 | export * from './process' | 1 | export * from './process' |
2 | export * from './send' | 2 | export * from './send' |
3 | export * from './account' | ||
4 | export * from './share' | ||
5 | export * from './video-channels' | ||
6 | export * from './videos' | ||
7 | export * from './url' | ||
diff --git a/server/lib/activitypub/process/process-add.ts b/server/lib/activitypub/process/process-add.ts index f064c1ab6..281036228 100644 --- a/server/lib/activitypub/process/process-add.ts +++ b/server/lib/activitypub/process/process-add.ts | |||
@@ -1,11 +1,14 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { VideoTorrentObject } from '../../../../shared' | 2 | import { VideoTorrentObject } from '../../../../shared' |
3 | import { ActivityAdd } from '../../../../shared/models/activitypub/activity' | 3 | import { ActivityAdd } from '../../../../shared/models/activitypub/activity' |
4 | import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../../helpers' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
5 | import { getOrCreateVideoChannel } from '../../../helpers/activitypub' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { database as db } from '../../../initializers' | 6 | import { database as db } from '../../../initializers' |
7 | import { AccountInstance } from '../../../models/account/account-interface' | 7 | import { AccountInstance } from '../../../models/account/account-interface' |
8 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 8 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
9 | import { getOrCreateAccount } from '../account' | ||
10 | import { getOrCreateVideoChannel } from '../video-channels' | ||
11 | import { generateThumbnailFromUrl } from '../videos' | ||
9 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 12 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
10 | 13 | ||
11 | async function processAddActivity (activity: ActivityAdd) { | 14 | async function processAddActivity (activity: ActivityAdd) { |
@@ -41,12 +44,10 @@ function processAddVideo (account: AccountInstance, activity: ActivityAdd, video | |||
41 | return retryTransactionWrapper(addRemoteVideo, options) | 44 | return retryTransactionWrapper(addRemoteVideo, options) |
42 | } | 45 | } |
43 | 46 | ||
44 | function addRemoteVideo ( | 47 | function addRemoteVideo (account: AccountInstance, |
45 | account: AccountInstance, | 48 | activity: ActivityAdd, |
46 | activity: ActivityAdd, | 49 | videoChannel: VideoChannelInstance, |
47 | videoChannel: VideoChannelInstance, | 50 | videoToCreateData: VideoTorrentObject) { |
48 | videoToCreateData: VideoTorrentObject | ||
49 | ) { | ||
50 | logger.debug('Adding remote video %s.', videoToCreateData.url) | 51 | logger.debug('Adding remote video %s.', videoToCreateData.url) |
51 | 52 | ||
52 | return db.sequelize.transaction(async t => { | 53 | return db.sequelize.transaction(async t => { |
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index 656db08a9..40712ef03 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity' |
2 | import { getOrCreateAccount } from '../../../helpers/activitypub' | ||
3 | import { logger } from '../../../helpers/logger' | 2 | import { logger } from '../../../helpers/logger' |
4 | import { database as db } from '../../../initializers/index' | 3 | import { database as db } from '../../../initializers/index' |
5 | import { VideoInstance } from '../../../models/index' | 4 | import { VideoInstance } from '../../../models/index' |
6 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 5 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
7 | import { processAddActivity } from './process-add' | 6 | import { processAddActivity } from './process-add' |
8 | import { processCreateActivity } from './process-create' | 7 | import { processCreateActivity } from './process-create' |
8 | import { getOrCreateAccount } from '../account' | ||
9 | 9 | ||
10 | async function processAnnounceActivity (activity: ActivityAnnounce) { | 10 | async function processAnnounceActivity (activity: ActivityAnnounce) { |
11 | const announcedActivity = activity.object | 11 | const announcedActivity = activity.object |
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index aac941a6c..fc635eb1f 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' | 1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' |
2 | import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' | 2 | import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' |
3 | import { logger, retryTransactionWrapper } from '../../../helpers' | 3 | import { logger, retryTransactionWrapper } from '../../../helpers' |
4 | import { getOrCreateAccount, getVideoChannelActivityPubUrl } from '../../../helpers/activitypub' | ||
5 | import { database as db } from '../../../initializers' | 4 | import { database as db } from '../../../initializers' |
6 | import { AccountInstance } from '../../../models/account/account-interface' | 5 | import { AccountInstance } from '../../../models/account/account-interface' |
6 | import { getOrCreateAccount } from '../account' | ||
7 | import { getVideoChannelActivityPubUrl } from '../url' | ||
7 | import { videoChannelActivityObjectToDBAttributes } from './misc' | 8 | import { videoChannelActivityObjectToDBAttributes } from './misc' |
8 | 9 | ||
9 | async function processCreateActivity (activity: ActivityCreate) { | 10 | async function processCreateActivity (activity: ActivityCreate) { |
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index af5d964d4..0328d1a7d 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { ActivityDelete } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityDelete } from '../../../../shared/models/activitypub/activity' |
2 | import { getOrCreateAccount } from '../../../helpers/activitypub' | ||
3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
4 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
5 | import { database as db } from '../../../initializers' | 4 | import { database as db } from '../../../initializers' |
6 | import { AccountInstance } from '../../../models/account/account-interface' | 5 | import { AccountInstance } from '../../../models/account/account-interface' |
7 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 6 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
8 | import { VideoInstance } from '../../../models/video/video-interface' | 7 | import { VideoInstance } from '../../../models/video/video-interface' |
8 | import { getOrCreateAccount } from '../account' | ||
9 | 9 | ||
10 | async function processDeleteActivity (activity: ActivityDelete) { | 10 | async function processDeleteActivity (activity: ActivityDelete) { |
11 | const account = await getOrCreateAccount(activity.actor) | 11 | const account = await getOrCreateAccount(activity.actor) |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 553639580..41b38828c 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' |
2 | import { getOrCreateAccount, retryTransactionWrapper } from '../../../helpers' | 2 | import { retryTransactionWrapper } from '../../../helpers' |
3 | import { database as db } from '../../../initializers' | 3 | import { database as db } from '../../../initializers' |
4 | import { AccountInstance } from '../../../models/account/account-interface' | 4 | import { AccountInstance } from '../../../models/account/account-interface' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { sendAccept } from '../send/send-accept' | 6 | import { sendAccept } from '../send/send-accept' |
7 | import { getOrCreateAccount } from '../account' | ||
7 | 8 | ||
8 | async function processFollowActivity (activity: ActivityFollow) { | 9 | async function processFollowActivity (activity: ActivityFollow) { |
9 | const activityObject = activity.object | 10 | const activityObject = activity.object |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index a3bfb1baf..4876735b8 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { VideoChannelObject, VideoTorrentObject } from '../../../../shared' | 1 | import { VideoChannelObject, VideoTorrentObject } from '../../../../shared' |
2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' | 2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' |
3 | import { getOrCreateAccount } from '../../../helpers/activitypub' | ||
4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
5 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
6 | import { resetSequelizeInstance } from '../../../helpers/utils' | 5 | import { resetSequelizeInstance } from '../../../helpers/utils' |
@@ -9,6 +8,7 @@ import { AccountInstance } from '../../../models/account/account-interface' | |||
9 | import { VideoInstance } from '../../../models/video/video-interface' | 8 | import { VideoInstance } from '../../../models/video/video-interface' |
10 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 9 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
11 | import Bluebird = require('bluebird') | 10 | import Bluebird = require('bluebird') |
11 | import { getOrCreateAccount } from '../account' | ||
12 | 12 | ||
13 | async function processUpdateActivity (activity: ActivityUpdate) { | 13 | async function processUpdateActivity (activity: ActivityUpdate) { |
14 | const account = await getOrCreateAccount(activity.actor) | 14 | const account = await getOrCreateAccount(activity.actor) |
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 0324a30fa..eeab19ed0 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts | |||
@@ -3,7 +3,7 @@ import { ActivityAccept } from '../../../../shared/models/activitypub/activity' | |||
3 | import { AccountInstance } from '../../../models' | 3 | import { AccountInstance } from '../../../models' |
4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
5 | import { unicastTo } from './misc' | 5 | import { unicastTo } from './misc' |
6 | import { getAccountFollowAcceptActivityPubUrl } from '../../../helpers/activitypub' | 6 | import { getAccountFollowAcceptActivityPubUrl } from '../url' |
7 | 7 | ||
8 | async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) { | 8 | async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) { |
9 | const follower = accountFollow.AccountFollower | 9 | const follower = accountFollow.AccountFollower |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index b9217e4f6..4b3a4ef75 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -4,7 +4,7 @@ import { VideoChannelInstance } from '../../../models/video/video-channel-interf | |||
4 | import { broadcastToFollowers } from './misc' | 4 | import { broadcastToFollowers } from './misc' |
5 | import { addActivityData } from './send-add' | 5 | import { addActivityData } from './send-add' |
6 | import { createActivityData } from './send-create' | 6 | import { createActivityData } from './send-create' |
7 | import { getAnnounceActivityPubUrl } from '../../../helpers/activitypub' | 7 | import { getAnnounceActivityPubUrl } from '../url' |
8 | 8 | ||
9 | async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { | 9 | async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { |
10 | const url = getAnnounceActivityPubUrl(video.url, byAccount) | 10 | const url = getAnnounceActivityPubUrl(video.url, byAccount) |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 66bfeee89..14b666fc9 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -3,7 +3,7 @@ import { ActivityCreate } from '../../../../shared/models/activitypub/activity' | |||
3 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' | 3 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' |
4 | import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' | 4 | import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' |
5 | import { broadcastToFollowers, getAudience, unicastTo } from './misc' | 5 | import { broadcastToFollowers, getAudience, unicastTo } from './misc' |
6 | import { getVideoAbuseActivityPubUrl } from '../../../helpers/activitypub' | 6 | import { getVideoAbuseActivityPubUrl } from '../url' |
7 | 7 | ||
8 | async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { | 8 | async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { |
9 | const byAccount = videoChannel.Account | 9 | const byAccount = videoChannel.Account |
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index 48d641c22..3c01fb77c 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -2,8 +2,8 @@ import { Transaction } from 'sequelize' | |||
2 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' | 2 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' |
3 | import { AccountInstance } from '../../../models' | 3 | import { AccountInstance } from '../../../models' |
4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
5 | import { getAccountFollowActivityPubUrl } from '../url' | ||
5 | import { unicastTo } from './misc' | 6 | import { unicastTo } from './misc' |
6 | import { getAccountFollowActivityPubUrl } from '../../../helpers/activitypub' | ||
7 | 7 | ||
8 | async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) { | 8 | async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) { |
9 | const me = accountFollow.AccountFollower | 9 | const me = accountFollow.AccountFollower |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 39da824f3..77bee6639 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -3,8 +3,8 @@ import { ActivityFollow, ActivityUndo } from '../../../../shared/models/activity | |||
3 | import { AccountInstance } from '../../../models' | 3 | import { AccountInstance } from '../../../models' |
4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
5 | import { unicastTo } from './misc' | 5 | import { unicastTo } from './misc' |
6 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../../../helpers/activitypub' | ||
7 | import { followActivityData } from './send-follow' | 6 | import { followActivityData } from './send-follow' |
7 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../url' | ||
8 | 8 | ||
9 | async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) { | 9 | async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) { |
10 | const me = accountFollow.AccountFollower | 10 | const me = accountFollow.AccountFollower |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 42738f973..32cada06e 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' | 2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' |
3 | import { getUpdateActivityPubUrl } from '../../../helpers/activitypub' | ||
4 | import { database as db } from '../../../initializers' | 3 | import { database as db } from '../../../initializers' |
5 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' | 4 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' |
5 | import { getUpdateActivityPubUrl } from '../url' | ||
6 | import { broadcastToFollowers, getAudience } from './misc' | 6 | import { broadcastToFollowers, getAudience } from './misc' |
7 | 7 | ||
8 | async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { | 8 | async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { |
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts new file mode 100644 index 000000000..689e200a6 --- /dev/null +++ b/server/lib/activitypub/share.ts | |||
@@ -0,0 +1,33 @@ | |||
1 | import { Transaction } from 'sequelize' | ||
2 | import { getServerAccount } from '../../helpers/utils' | ||
3 | import { database as db } from '../../initializers' | ||
4 | import { VideoChannelInstance } from '../../models/index' | ||
5 | import { VideoInstance } from '../../models/video/video-interface' | ||
6 | import { sendVideoAnnounce, sendVideoChannelAnnounce } from './send/send-announce' | ||
7 | |||
8 | async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) { | ||
9 | const serverAccount = await getServerAccount() | ||
10 | |||
11 | await db.VideoChannelShare.create({ | ||
12 | accountId: serverAccount.id, | ||
13 | videoChannelId: videoChannel.id | ||
14 | }, { transaction: t }) | ||
15 | |||
16 | return sendVideoChannelAnnounce(serverAccount, videoChannel, t) | ||
17 | } | ||
18 | |||
19 | async function shareVideoByServer (video: VideoInstance, t: Transaction) { | ||
20 | const serverAccount = await getServerAccount() | ||
21 | |||
22 | await db.VideoShare.create({ | ||
23 | accountId: serverAccount.id, | ||
24 | videoId: video.id | ||
25 | }, { transaction: t }) | ||
26 | |||
27 | return sendVideoAnnounce(serverAccount, video, t) | ||
28 | } | ||
29 | |||
30 | export { | ||
31 | shareVideoChannelByServer, | ||
32 | shareVideoByServer | ||
33 | } | ||
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts new file mode 100644 index 000000000..41ac0f9a8 --- /dev/null +++ b/server/lib/activitypub/url.ts | |||
@@ -0,0 +1,60 @@ | |||
1 | import { CONFIG } from '../../initializers/constants' | ||
2 | import { VideoInstance } from '../../models/video/video-interface' | ||
3 | import { VideoChannelInstance } from '../../models/video/video-channel-interface' | ||
4 | import { VideoAbuseInstance } from '../../models/video/video-abuse-interface' | ||
5 | import { AccountFollowInstance } from '../../models/account/account-follow-interface' | ||
6 | import { AccountInstance } from '../../models/account/account-interface' | ||
7 | |||
8 | function getVideoActivityPubUrl (video: VideoInstance) { | ||
9 | return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid | ||
10 | } | ||
11 | |||
12 | function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) { | ||
13 | return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid | ||
14 | } | ||
15 | |||
16 | function getAccountActivityPubUrl (accountName: string) { | ||
17 | return CONFIG.WEBSERVER.URL + '/account/' + accountName | ||
18 | } | ||
19 | |||
20 | function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) { | ||
21 | return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id | ||
22 | } | ||
23 | |||
24 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { | ||
25 | const me = accountFollow.AccountFollower | ||
26 | const following = accountFollow.AccountFollowing | ||
27 | |||
28 | return me.url + '#follows/' + following.id | ||
29 | } | ||
30 | |||
31 | function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) { | ||
32 | const follower = accountFollow.AccountFollower | ||
33 | const me = accountFollow.AccountFollowing | ||
34 | |||
35 | return follower.url + '#accepts/follows/' + me.id | ||
36 | } | ||
37 | |||
38 | function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) { | ||
39 | return originalUrl + '#announces/' + byAccount.id | ||
40 | } | ||
41 | |||
42 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { | ||
43 | return originalUrl + '#updates/' + updatedAt | ||
44 | } | ||
45 | |||
46 | function getUndoActivityPubUrl (originalUrl: string) { | ||
47 | return originalUrl + '/undo' | ||
48 | } | ||
49 | |||
50 | export { | ||
51 | getVideoActivityPubUrl, | ||
52 | getVideoChannelActivityPubUrl, | ||
53 | getAccountActivityPubUrl, | ||
54 | getVideoAbuseActivityPubUrl, | ||
55 | getAccountFollowActivityPubUrl, | ||
56 | getAccountFollowAcceptActivityPubUrl, | ||
57 | getAnnounceActivityPubUrl, | ||
58 | getUpdateActivityPubUrl, | ||
59 | getUndoActivityPubUrl | ||
60 | } | ||
diff --git a/server/lib/activitypub/video-channels.ts b/server/lib/activitypub/video-channels.ts new file mode 100644 index 000000000..7339d79f9 --- /dev/null +++ b/server/lib/activitypub/video-channels.ts | |||
@@ -0,0 +1,60 @@ | |||
1 | import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' | ||
2 | import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub/video-channels' | ||
3 | import { logger } from '../../helpers/logger' | ||
4 | import { doRequest } from '../../helpers/requests' | ||
5 | import { database as db } from '../../initializers' | ||
6 | import { ACTIVITY_PUB } from '../../initializers/constants' | ||
7 | import { AccountInstance } from '../../models/account/account-interface' | ||
8 | import { videoChannelActivityObjectToDBAttributes } from './process/misc' | ||
9 | |||
10 | async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
11 | let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl) | ||
12 | |||
13 | // We don't have this account in our database, fetch it on remote | ||
14 | if (!videoChannel) { | ||
15 | videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl) | ||
16 | if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.') | ||
17 | |||
18 | // Save our new video channel in database | ||
19 | await videoChannel.save() | ||
20 | } | ||
21 | |||
22 | return videoChannel | ||
23 | } | ||
24 | |||
25 | async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
26 | const options = { | ||
27 | uri: videoChannelUrl, | ||
28 | method: 'GET', | ||
29 | headers: { | ||
30 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
31 | } | ||
32 | } | ||
33 | |||
34 | logger.info('Fetching remote video channel %s.', videoChannelUrl) | ||
35 | |||
36 | let requestResult | ||
37 | try { | ||
38 | requestResult = await doRequest(options) | ||
39 | } catch (err) { | ||
40 | logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err) | ||
41 | return undefined | ||
42 | } | ||
43 | |||
44 | const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body) | ||
45 | if (isVideoChannelObjectValid(videoChannelJSON) === false) { | ||
46 | logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON }) | ||
47 | return undefined | ||
48 | } | ||
49 | |||
50 | const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount) | ||
51 | const videoChannel = db.VideoChannel.build(videoChannelAttributes) | ||
52 | videoChannel.Account = ownerAccount | ||
53 | |||
54 | return videoChannel | ||
55 | } | ||
56 | |||
57 | export { | ||
58 | getOrCreateVideoChannel, | ||
59 | fetchRemoteVideoChannel | ||
60 | } | ||
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts new file mode 100644 index 000000000..944244893 --- /dev/null +++ b/server/lib/activitypub/videos.ts | |||
@@ -0,0 +1,44 @@ | |||
1 | import { join } from 'path' | ||
2 | import * as request from 'request' | ||
3 | import { ActivityIconObject } from '../../../shared/index' | ||
4 | import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests' | ||
5 | import { CONFIG, REMOTE_SCHEME, STATIC_PATHS } from '../../initializers/constants' | ||
6 | import { VideoInstance } from '../../models/video/video-interface' | ||
7 | |||
8 | function fetchRemoteVideoPreview (video: VideoInstance) { | ||
9 | // FIXME: use url | ||
10 | const host = video.VideoChannel.Account.Server.host | ||
11 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) | ||
12 | |||
13 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) | ||
14 | } | ||
15 | |||
16 | async function fetchRemoteVideoDescription (video: VideoInstance) { | ||
17 | // FIXME: use url | ||
18 | const host = video.VideoChannel.Account.Server.host | ||
19 | const path = video.getDescriptionPath() | ||
20 | const options = { | ||
21 | uri: REMOTE_SCHEME.HTTP + '://' + host + path, | ||
22 | json: true | ||
23 | } | ||
24 | |||
25 | const { body } = await doRequest(options) | ||
26 | return body.description ? body.description : '' | ||
27 | } | ||
28 | |||
29 | function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) { | ||
30 | const thumbnailName = video.getThumbnailName() | ||
31 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName) | ||
32 | |||
33 | const options = { | ||
34 | method: 'GET', | ||
35 | uri: icon.url | ||
36 | } | ||
37 | return doRequestAndSaveToFile(options, thumbnailPath) | ||
38 | } | ||
39 | |||
40 | export { | ||
41 | fetchRemoteVideoPreview, | ||
42 | fetchRemoteVideoDescription, | ||
43 | generateThumbnailFromUrl | ||
44 | } | ||
diff --git a/server/lib/cache/videos-preview-cache.ts b/server/lib/cache/videos-preview-cache.ts index 0570f51e8..7f352f361 100644 --- a/server/lib/cache/videos-preview-cache.ts +++ b/server/lib/cache/videos-preview-cache.ts | |||
@@ -3,8 +3,9 @@ import { join } from 'path' | |||
3 | import { createWriteStream } from 'fs' | 3 | import { createWriteStream } from 'fs' |
4 | 4 | ||
5 | import { database as db, CONFIG, CACHE } from '../../initializers' | 5 | import { database as db, CONFIG, CACHE } from '../../initializers' |
6 | import { logger, unlinkPromise, fetchRemoteVideoPreview } from '../../helpers' | 6 | import { logger, unlinkPromise } from '../../helpers' |
7 | import { VideoInstance } from '../../models' | 7 | import { VideoInstance } from '../../models' |
8 | import { fetchRemoteVideoPreview } from '../activitypub/videos' | ||
8 | 9 | ||
9 | class VideosPreviewCache { | 10 | class VideosPreviewCache { |
10 | 11 | ||
diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts index f26110973..e65ab3ee1 100644 --- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts +++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts | |||
@@ -1,13 +1,11 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { computeResolutionsToTranscode, logger } from '../../../helpers' | 2 | import { computeResolutionsToTranscode, logger } from '../../../helpers' |
3 | |||
4 | import { database as db } from '../../../initializers/database' | 3 | import { database as db } from '../../../initializers/database' |
5 | import { VideoInstance } from '../../../models' | 4 | import { VideoInstance } from '../../../models' |
6 | 5 | import { sendAddVideo } from '../../activitypub/send/send-add' | |
7 | import { JobScheduler } from '../job-scheduler' | 6 | import { JobScheduler } from '../job-scheduler' |
8 | import { TranscodingJobPayload } from './transcoding-job-scheduler' | 7 | import { TranscodingJobPayload } from './transcoding-job-scheduler' |
9 | import { shareVideoByServer } from '../../../helpers/activitypub' | 8 | import { shareVideoByServer } from '../../activitypub/share' |
10 | import { sendAddVideo } from '../../activitypub/send/send-add' | ||
11 | 9 | ||
12 | async function process (data: TranscodingJobPayload, jobId: number) { | 10 | async function process (data: TranscodingJobPayload, jobId: number) { |
13 | const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID) | 11 | const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID) |
diff --git a/server/lib/user.ts b/server/lib/user.ts index d54ffc916..5653d8e65 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -5,7 +5,7 @@ import { CONFIG } from '../initializers/constants' | |||
5 | import { UserInstance } from '../models' | 5 | import { UserInstance } from '../models' |
6 | import { createVideoChannel } from './video-channel' | 6 | import { createVideoChannel } from './video-channel' |
7 | import { logger } from '../helpers/logger' | 7 | import { logger } from '../helpers/logger' |
8 | import { getAccountActivityPubUrl } from '../helpers/activitypub' | 8 | import { getAccountActivityPubUrl } from './activitypub/url' |
9 | 9 | ||
10 | async function createUserAccountAndChannel (user: UserInstance, validateUser = true) { | 10 | async function createUserAccountAndChannel (user: UserInstance, validateUser = true) { |
11 | const { account, videoChannel } = await db.sequelize.transaction(async t => { | 11 | const { account, videoChannel } = await db.sequelize.transaction(async t => { |
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 5235d9cb5..a939a23d5 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -3,7 +3,7 @@ import { VideoChannelCreate } from '../../shared/models' | |||
3 | import { logger } from '../helpers' | 3 | import { logger } from '../helpers' |
4 | import { database as db } from '../initializers' | 4 | import { database as db } from '../initializers' |
5 | import { AccountInstance } from '../models' | 5 | import { AccountInstance } from '../models' |
6 | import { getVideoChannelActivityPubUrl } from '../helpers/activitypub' | 6 | import { getVideoChannelActivityPubUrl } from './activitypub/url' |
7 | 7 | ||
8 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { | 8 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { |
9 | const videoChannelData = { | 9 | const videoChannelData = { |
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts index 8e8a3961b..29bec0c97 100644 --- a/server/middlewares/activitypub.ts +++ b/server/middlewares/activitypub.ts | |||
@@ -2,9 +2,9 @@ import { eachSeries } from 'async' | |||
2 | import { NextFunction, Request, RequestHandler, Response } from 'express' | 2 | import { NextFunction, Request, RequestHandler, Response } from 'express' |
3 | import { ActivityPubSignature } from '../../shared' | 3 | import { ActivityPubSignature } from '../../shared' |
4 | import { isSignatureVerified, logger } from '../helpers' | 4 | import { isSignatureVerified, logger } from '../helpers' |
5 | import { fetchRemoteAccountAndCreateServer } from '../helpers/activitypub' | ||
6 | import { database as db } from '../initializers' | 5 | import { database as db } from '../initializers' |
7 | import { ACTIVITY_PUB } from '../initializers/constants' | 6 | import { ACTIVITY_PUB } from '../initializers/constants' |
7 | import { fetchRemoteAccountAndCreateServer } from '../lib/activitypub/account' | ||
8 | 8 | ||
9 | async function checkSignature (req: Request, res: Response, next: NextFunction) { | 9 | async function checkSignature (req: Request, res: Response, next: NextFunction) { |
10 | const signatureObject: ActivityPubSignature = req.body.signature | 10 | const signatureObject: ActivityPubSignature = req.body.signature |