diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/activitypub/client.ts | 27 | ||||
-rw-r--r-- | server/controllers/api/videos/channel.ts | 4 | ||||
-rw-r--r-- | server/helpers/activitypub.ts | 64 | ||||
-rw-r--r-- | server/helpers/custom-validators/activitypub/activity.ts | 6 | ||||
-rw-r--r-- | server/helpers/custom-validators/activitypub/misc.ts | 2 | ||||
-rw-r--r-- | server/helpers/custom-validators/activitypub/videos.ts | 42 | ||||
-rw-r--r-- | server/helpers/database-utils.ts | 8 | ||||
-rw-r--r-- | server/initializers/constants.ts | 12 | ||||
-rw-r--r-- | server/lib/activitypub/misc.ts | 46 | ||||
-rw-r--r-- | server/lib/activitypub/process-add.ts | 22 | ||||
-rw-r--r-- | server/lib/activitypub/process-announce.ts | 29 | ||||
-rw-r--r-- | server/lib/activitypub/process-create.ts | 16 | ||||
-rw-r--r-- | server/lib/activitypub/send-request.ts | 40 | ||||
-rw-r--r-- | server/middlewares/validators/video-channels.ts | 4 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 3 |
15 files changed, 214 insertions, 111 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 76049f496..7b3921770 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -4,10 +4,13 @@ import * as express from 'express' | |||
4 | import { database as db } from '../../initializers' | 4 | import { database as db } from '../../initializers' |
5 | import { executeIfActivityPub, localAccountValidator } from '../../middlewares' | 5 | import { executeIfActivityPub, localAccountValidator } from '../../middlewares' |
6 | import { pageToStartAndCount } from '../../helpers' | 6 | import { pageToStartAndCount } from '../../helpers' |
7 | import { AccountInstance } from '../../models' | 7 | import { AccountInstance, VideoChannelInstance } from '../../models' |
8 | import { activityPubCollectionPagination } from '../../helpers/activitypub' | 8 | import { activityPubCollectionPagination } from '../../helpers/activitypub' |
9 | import { ACTIVITY_PUB } from '../../initializers/constants' | 9 | import { ACTIVITY_PUB } from '../../initializers/constants' |
10 | import { asyncMiddleware } from '../../middlewares/async' | 10 | import { asyncMiddleware } from '../../middlewares/async' |
11 | import { videosGetValidator } from '../../middlewares/validators/videos' | ||
12 | import { VideoInstance } from '../../models/video/video-interface' | ||
13 | import { videoChannelsGetValidator } from '../../middlewares/validators/video-channels' | ||
11 | 14 | ||
12 | const activityPubClientRouter = express.Router() | 15 | const activityPubClientRouter = express.Router() |
13 | 16 | ||
@@ -26,6 +29,16 @@ activityPubClientRouter.get('/account/:name/following', | |||
26 | executeIfActivityPub(asyncMiddleware(accountFollowingController)) | 29 | executeIfActivityPub(asyncMiddleware(accountFollowingController)) |
27 | ) | 30 | ) |
28 | 31 | ||
32 | activityPubClientRouter.get('/videos/watch/:id', | ||
33 | executeIfActivityPub(videosGetValidator), | ||
34 | executeIfActivityPub(asyncMiddleware(videoController)) | ||
35 | ) | ||
36 | |||
37 | activityPubClientRouter.get('/video-channels/:id', | ||
38 | executeIfActivityPub(videoChannelsGetValidator), | ||
39 | executeIfActivityPub(asyncMiddleware(videoChannelController)) | ||
40 | ) | ||
41 | |||
29 | // --------------------------------------------------------------------------- | 42 | // --------------------------------------------------------------------------- |
30 | 43 | ||
31 | export { | 44 | export { |
@@ -63,3 +76,15 @@ async function accountFollowingController (req: express.Request, res: express.Re | |||
63 | 76 | ||
64 | return res.json(activityPubResult) | 77 | return res.json(activityPubResult) |
65 | } | 78 | } |
79 | |||
80 | async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
81 | const video: VideoInstance = res.locals.video | ||
82 | |||
83 | return res.json(video.toActivityPubObject()) | ||
84 | } | ||
85 | |||
86 | async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
87 | const videoChannel: VideoChannelInstance = res.locals.videoChannel | ||
88 | |||
89 | return res.json(videoChannel.toActivityPubObject()) | ||
90 | } | ||
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts index 656bc3129..8f3df2550 100644 --- a/server/controllers/api/videos/channel.ts +++ b/server/controllers/api/videos/channel.ts | |||
@@ -10,7 +10,7 @@ import { | |||
10 | paginationValidator, | 10 | paginationValidator, |
11 | setPagination, | 11 | setPagination, |
12 | setVideoChannelsSort, | 12 | setVideoChannelsSort, |
13 | videoChannelGetValidator, | 13 | videoChannelsGetValidator, |
14 | videoChannelsAddValidator, | 14 | videoChannelsAddValidator, |
15 | videoChannelsRemoveValidator, | 15 | videoChannelsRemoveValidator, |
16 | videoChannelsSortValidator, | 16 | videoChannelsSortValidator, |
@@ -53,7 +53,7 @@ videoChannelRouter.delete('/channels/:id', | |||
53 | ) | 53 | ) |
54 | 54 | ||
55 | videoChannelRouter.get('/channels/:id', | 55 | videoChannelRouter.get('/channels/:id', |
56 | videoChannelGetValidator, | 56 | videoChannelsGetValidator, |
57 | asyncMiddleware(getVideoChannel) | 57 | asyncMiddleware(getVideoChannel) |
58 | ) | 58 | ) |
59 | 59 | ||
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index b376b8ca2..c710117cd 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts | |||
@@ -4,13 +4,18 @@ import * as Sequelize from 'sequelize' | |||
4 | import * as url from 'url' | 4 | import * as url from 'url' |
5 | import { ActivityIconObject } from '../../shared/index' | 5 | import { ActivityIconObject } from '../../shared/index' |
6 | import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' | 6 | import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' |
7 | import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object' | ||
7 | import { ResultList } from '../../shared/models/result-list.model' | 8 | import { ResultList } from '../../shared/models/result-list.model' |
8 | import { database as db, REMOTE_SCHEME } from '../initializers' | 9 | import { database as db, REMOTE_SCHEME } from '../initializers' |
9 | import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' | 10 | import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' |
10 | import { sendAnnounce } from '../lib/activitypub/send-request' | 11 | import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc' |
12 | import { sendVideoAnnounce } from '../lib/activitypub/send-request' | ||
13 | import { sendVideoChannelAnnounce } from '../lib/index' | ||
14 | import { AccountInstance } from '../models/account/account-interface' | ||
11 | import { VideoChannelInstance } from '../models/video/video-channel-interface' | 15 | import { VideoChannelInstance } from '../models/video/video-channel-interface' |
12 | import { VideoInstance } from '../models/video/video-interface' | 16 | import { VideoInstance } from '../models/video/video-interface' |
13 | import { isRemoteAccountValid } from './custom-validators' | 17 | import { isRemoteAccountValid } from './custom-validators' |
18 | import { isVideoChannelObjectValid } from './custom-validators/activitypub/videos' | ||
14 | import { logger } from './logger' | 19 | import { logger } from './logger' |
15 | import { doRequest, doRequestAndSaveToFile } from './requests' | 20 | import { doRequest, doRequestAndSaveToFile } from './requests' |
16 | import { getServerAccount } from './utils' | 21 | import { getServerAccount } from './utils' |
@@ -34,7 +39,7 @@ async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: | |||
34 | videoChannelId: videoChannel.id | 39 | videoChannelId: videoChannel.id |
35 | }, { transaction: t }) | 40 | }, { transaction: t }) |
36 | 41 | ||
37 | return sendAnnounce(serverAccount, videoChannel, t) | 42 | return sendVideoChannelAnnounce(serverAccount, videoChannel, t) |
38 | } | 43 | } |
39 | 44 | ||
40 | async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) { | 45 | async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) { |
@@ -45,7 +50,7 @@ async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transactio | |||
45 | videoId: video.id | 50 | videoId: video.id |
46 | }, { transaction: t }) | 51 | }, { transaction: t }) |
47 | 52 | ||
48 | return sendAnnounce(serverAccount, video, t) | 53 | return sendVideoAnnounce(serverAccount, video, t) |
49 | } | 54 | } |
50 | 55 | ||
51 | function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) { | 56 | function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) { |
@@ -66,13 +71,27 @@ async function getOrCreateAccount (accountUrl: string) { | |||
66 | if (res === undefined) throw new Error('Cannot fetch remote account.') | 71 | if (res === undefined) throw new Error('Cannot fetch remote account.') |
67 | 72 | ||
68 | // Save our new account in database | 73 | // Save our new account in database |
69 | const account = res.account | 74 | account = await res.account.save() |
70 | await account.save() | ||
71 | } | 75 | } |
72 | 76 | ||
73 | return account | 77 | return account |
74 | } | 78 | } |
75 | 79 | ||
80 | async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
81 | let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl) | ||
82 | |||
83 | // We don't have this account in our database, fetch it on remote | ||
84 | if (!videoChannel) { | ||
85 | videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl) | ||
86 | if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.') | ||
87 | |||
88 | // Save our new video channel in database | ||
89 | await videoChannel.save() | ||
90 | } | ||
91 | |||
92 | return videoChannel | ||
93 | } | ||
94 | |||
76 | async function fetchRemoteAccountAndCreateServer (accountUrl: string) { | 95 | async function fetchRemoteAccountAndCreateServer (accountUrl: string) { |
77 | const options = { | 96 | const options = { |
78 | uri: accountUrl, | 97 | uri: accountUrl, |
@@ -131,6 +150,38 @@ async function fetchRemoteAccountAndCreateServer (accountUrl: string) { | |||
131 | return { account, server } | 150 | return { account, server } |
132 | } | 151 | } |
133 | 152 | ||
153 | async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
154 | const options = { | ||
155 | uri: videoChannelUrl, | ||
156 | method: 'GET', | ||
157 | headers: { | ||
158 | 'Accept': ACTIVITY_PUB_ACCEPT_HEADER | ||
159 | } | ||
160 | } | ||
161 | |||
162 | logger.info('Fetching remote video channel %s.', videoChannelUrl) | ||
163 | |||
164 | let requestResult | ||
165 | try { | ||
166 | requestResult = await doRequest(options) | ||
167 | } catch (err) { | ||
168 | logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err) | ||
169 | return undefined | ||
170 | } | ||
171 | |||
172 | const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body) | ||
173 | if (isVideoChannelObjectValid(videoChannelJSON) === false) { | ||
174 | logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON }) | ||
175 | return undefined | ||
176 | } | ||
177 | |||
178 | const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount) | ||
179 | const videoChannel = db.VideoChannel.build(videoChannelAttributes) | ||
180 | videoChannel.Account = ownerAccount | ||
181 | |||
182 | return videoChannel | ||
183 | } | ||
184 | |||
134 | function fetchRemoteVideoPreview (video: VideoInstance) { | 185 | function fetchRemoteVideoPreview (video: VideoInstance) { |
135 | // FIXME: use url | 186 | // FIXME: use url |
136 | const host = video.VideoChannel.Account.Server.host | 187 | const host = video.VideoChannel.Account.Server.host |
@@ -200,7 +251,8 @@ export { | |||
200 | fetchRemoteVideoPreview, | 251 | fetchRemoteVideoPreview, |
201 | fetchRemoteVideoDescription, | 252 | fetchRemoteVideoDescription, |
202 | shareVideoChannelByServer, | 253 | shareVideoChannelByServer, |
203 | shareVideoByServer | 254 | shareVideoByServer, |
255 | getOrCreateVideoChannel | ||
204 | } | 256 | } |
205 | 257 | ||
206 | // --------------------------------------------------------------------------- | 258 | // --------------------------------------------------------------------------- |
diff --git a/server/helpers/custom-validators/activitypub/activity.ts b/server/helpers/custom-validators/activitypub/activity.ts index 08e5ae0aa..8084cf7b0 100644 --- a/server/helpers/custom-validators/activitypub/activity.ts +++ b/server/helpers/custom-validators/activitypub/activity.ts | |||
@@ -2,8 +2,7 @@ import * as validator from 'validator' | |||
2 | import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account' | 2 | import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account' |
3 | import { isActivityPubUrlValid } from './misc' | 3 | import { isActivityPubUrlValid } from './misc' |
4 | import { | 4 | import { |
5 | isVideoAnnounceValid, | 5 | isAnnounceValid, |
6 | isVideoChannelAnnounceValid, | ||
7 | isVideoChannelCreateActivityValid, | 6 | isVideoChannelCreateActivityValid, |
8 | isVideoChannelDeleteActivityValid, | 7 | isVideoChannelDeleteActivityValid, |
9 | isVideoChannelUpdateActivityValid, | 8 | isVideoChannelUpdateActivityValid, |
@@ -37,8 +36,7 @@ function isActivityValid (activity: any) { | |||
37 | isAccountFollowActivityValid(activity) || | 36 | isAccountFollowActivityValid(activity) || |
38 | isAccountAcceptActivityValid(activity) || | 37 | isAccountAcceptActivityValid(activity) || |
39 | isVideoFlagValid(activity) || | 38 | isVideoFlagValid(activity) || |
40 | isVideoAnnounceValid(activity) || | 39 | isAnnounceValid(activity) |
41 | isVideoChannelAnnounceValid(activity) | ||
42 | } | 40 | } |
43 | 41 | ||
44 | // --------------------------------------------------------------------------- | 42 | // --------------------------------------------------------------------------- |
diff --git a/server/helpers/custom-validators/activitypub/misc.ts b/server/helpers/custom-validators/activitypub/misc.ts index 665a63a73..f09a764b6 100644 --- a/server/helpers/custom-validators/activitypub/misc.ts +++ b/server/helpers/custom-validators/activitypub/misc.ts | |||
@@ -21,7 +21,7 @@ function isActivityPubUrlValid (url: string) { | |||
21 | } | 21 | } |
22 | 22 | ||
23 | function isBaseActivityValid (activity: any, type: string) { | 23 | function isBaseActivityValid (activity: any, type: string) { |
24 | return Array.isArray(activity['@context']) && | 24 | return (activity['@context'] === undefined || Array.isArray(activity['@context'])) && |
25 | activity.type === type && | 25 | activity.type === type && |
26 | isActivityPubUrlValid(activity.id) && | 26 | isActivityPubUrlValid(activity.id) && |
27 | isActivityPubUrlValid(activity.actor) && | 27 | isActivityPubUrlValid(activity.actor) && |
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts index 89c49b0df..8486297ad 100644 --- a/server/helpers/custom-validators/activitypub/videos.ts +++ b/server/helpers/custom-validators/activitypub/videos.ts | |||
@@ -39,6 +39,7 @@ function isActivityPubVideoDurationValid (value: string) { | |||
39 | 39 | ||
40 | function isVideoTorrentObjectValid (video: any) { | 40 | function isVideoTorrentObjectValid (video: any) { |
41 | return video.type === 'Video' && | 41 | return video.type === 'Video' && |
42 | isActivityPubUrlValid(video.id) && | ||
42 | isVideoNameValid(video.name) && | 43 | isVideoNameValid(video.name) && |
43 | isActivityPubVideoDurationValid(video.duration) && | 44 | isActivityPubVideoDurationValid(video.duration) && |
44 | isUUIDValid(video.uuid) && | 45 | isUUIDValid(video.uuid) && |
@@ -62,14 +63,12 @@ function isVideoFlagValid (activity: any) { | |||
62 | isActivityPubUrlValid(activity.object) | 63 | isActivityPubUrlValid(activity.object) |
63 | } | 64 | } |
64 | 65 | ||
65 | function isVideoAnnounceValid (activity: any) { | 66 | function isAnnounceValid (activity: any) { |
66 | return isBaseActivityValid(activity, 'Announce') && | 67 | return isBaseActivityValid(activity, 'Announce') && |
67 | isVideoTorrentObjectValid(activity.object) | 68 | ( |
68 | } | 69 | isVideoChannelCreateActivityValid(activity.object) || |
69 | 70 | isVideoTorrentAddActivityValid(activity.object) | |
70 | function isVideoChannelAnnounceValid (activity: any) { | 71 | ) |
71 | return isBaseActivityValid(activity, 'Announce') && | ||
72 | isVideoChannelObjectValid(activity.object) | ||
73 | } | 72 | } |
74 | 73 | ||
75 | function isVideoChannelCreateActivityValid (activity: any) { | 74 | function isVideoChannelCreateActivityValid (activity: any) { |
@@ -88,8 +87,11 @@ function isVideoChannelDeleteActivityValid (activity: any) { | |||
88 | 87 | ||
89 | function isVideoChannelObjectValid (videoChannel: any) { | 88 | function isVideoChannelObjectValid (videoChannel: any) { |
90 | return videoChannel.type === 'VideoChannel' && | 89 | return videoChannel.type === 'VideoChannel' && |
90 | isActivityPubUrlValid(videoChannel.id) && | ||
91 | isVideoChannelNameValid(videoChannel.name) && | 91 | isVideoChannelNameValid(videoChannel.name) && |
92 | isVideoChannelDescriptionValid(videoChannel.description) && | 92 | isVideoChannelDescriptionValid(videoChannel.content) && |
93 | isDateValid(videoChannel.published) && | ||
94 | isDateValid(videoChannel.updated) && | ||
93 | isUUIDValid(videoChannel.uuid) | 95 | isUUIDValid(videoChannel.uuid) |
94 | } | 96 | } |
95 | 97 | ||
@@ -103,8 +105,8 @@ export { | |||
103 | isVideoChannelDeleteActivityValid, | 105 | isVideoChannelDeleteActivityValid, |
104 | isVideoTorrentDeleteActivityValid, | 106 | isVideoTorrentDeleteActivityValid, |
105 | isVideoFlagValid, | 107 | isVideoFlagValid, |
106 | isVideoAnnounceValid, | 108 | isAnnounceValid, |
107 | isVideoChannelAnnounceValid | 109 | isVideoChannelObjectValid |
108 | } | 110 | } |
109 | 111 | ||
110 | // --------------------------------------------------------------------------- | 112 | // --------------------------------------------------------------------------- |
@@ -148,8 +150,20 @@ function setValidRemoteVideoUrls (video: any) { | |||
148 | 150 | ||
149 | function isRemoteVideoUrlValid (url: any) { | 151 | function isRemoteVideoUrlValid (url: any) { |
150 | return url.type === 'Link' && | 152 | return url.type === 'Link' && |
151 | ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 && | 153 | ( |
152 | isVideoUrlValid(url.url) && | 154 | ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 && |
153 | validator.isInt(url.width + '', { min: 0 }) && | 155 | isVideoUrlValid(url.url) && |
154 | validator.isInt(url.size + '', { min: 0 }) | 156 | validator.isInt(url.width + '', { min: 0 }) && |
157 | validator.isInt(url.size + '', { min: 0 }) | ||
158 | ) || | ||
159 | ( | ||
160 | ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 && | ||
161 | isVideoUrlValid(url.url) && | ||
162 | validator.isInt(url.width + '', { min: 0 }) | ||
163 | ) || | ||
164 | ( | ||
165 | ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.indexOf(url.mimeType) !== -1 && | ||
166 | validator.isLength(url.url, { min: 5 }) && | ||
167 | validator.isInt(url.width + '', { min: 0 }) | ||
168 | ) | ||
155 | } | 169 | } |
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts index d62462d35..169b80065 100644 --- a/server/helpers/database-utils.ts +++ b/server/helpers/database-utils.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | // TODO: import from ES6 when retry typing file will include errorFilter function | 1 | // TODO: import from ES6 when retry typing file will include errorFilter function |
2 | import * as retry from 'async/retry' | 2 | import * as retry from 'async/retry' |
3 | 3 | import * as Bluebird from 'bluebird' | |
4 | import { logger } from './logger' | 4 | import { logger } from './logger' |
5 | 5 | ||
6 | type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] } | 6 | type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] } |
7 | function retryTransactionWrapper (functionToRetry: (...args) => Promise<any>, options: RetryTransactionWrapperOptions) { | 7 | function retryTransactionWrapper (functionToRetry: (...args) => Promise<any> | Bluebird<any>, options: RetryTransactionWrapperOptions) { |
8 | const args = options.arguments ? options.arguments : [] | 8 | const args = options.arguments ? options.arguments : [] |
9 | 9 | ||
10 | return transactionRetryer(callback => { | 10 | return transactionRetryer(callback => { |
@@ -13,8 +13,8 @@ function retryTransactionWrapper (functionToRetry: (...args) => Promise<any>, op | |||
13 | .catch(err => callback(err)) | 13 | .catch(err => callback(err)) |
14 | }) | 14 | }) |
15 | .catch(err => { | 15 | .catch(err => { |
16 | // Do not throw the error, continue the process | ||
17 | logger.error(options.errorMessage, err) | 16 | logger.error(options.errorMessage, err) |
17 | throw err | ||
18 | }) | 18 | }) |
19 | } | 19 | } |
20 | 20 | ||
@@ -28,7 +28,7 @@ function transactionRetryer (func: Function) { | |||
28 | logger.debug('Maybe retrying the transaction function.', { willRetry }) | 28 | logger.debug('Maybe retrying the transaction function.', { willRetry }) |
29 | return willRetry | 29 | return willRetry |
30 | } | 30 | } |
31 | }, func, err => err ? rej(err) : res()) | 31 | }, func, (err, data) => err ? rej(err) : res(data)) |
32 | }) | 32 | }) |
33 | } | 33 | } |
34 | 34 | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index dca223370..eeda8347d 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -227,13 +227,11 @@ const ACTIVITY_PUB_ACCEPT_HEADER = 'application/ld+json; profile="https://www.w3 | |||
227 | 227 | ||
228 | const ACTIVITY_PUB = { | 228 | const ACTIVITY_PUB = { |
229 | COLLECTION_ITEMS_PER_PAGE: 10, | 229 | COLLECTION_ITEMS_PER_PAGE: 10, |
230 | VIDEO_URL_MIME_TYPES: [ | 230 | URL_MIME_TYPES: { |
231 | 'video/mp4', | 231 | VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT |
232 | 'video/webm', | 232 | TORRENT: [ 'application/x-bittorrent' ], |
233 | 'video/ogg', | 233 | MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] |
234 | 'application/x-bittorrent', | 234 | } |
235 | 'application/x-bittorrent;x-scheme-handler/magnet' | ||
236 | ] | ||
237 | } | 235 | } |
238 | 236 | ||
239 | // --------------------------------------------------------------------------- | 237 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/activitypub/misc.ts b/server/lib/activitypub/misc.ts index 43d26c328..13838fc4c 100644 --- a/server/lib/activitypub/misc.ts +++ b/server/lib/activitypub/misc.ts | |||
@@ -7,6 +7,21 @@ import { VIDEO_MIMETYPE_EXT } from '../../initializers/constants' | |||
7 | import { VideoChannelInstance } from '../../models/video/video-channel-interface' | 7 | import { VideoChannelInstance } from '../../models/video/video-channel-interface' |
8 | import { VideoFileAttributes } from '../../models/video/video-file-interface' | 8 | import { VideoFileAttributes } from '../../models/video/video-file-interface' |
9 | import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' | 9 | import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' |
10 | import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' | ||
11 | import { AccountInstance } from '../../models/account/account-interface' | ||
12 | |||
13 | function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { | ||
14 | return { | ||
15 | name: videoChannelObject.name, | ||
16 | description: videoChannelObject.content, | ||
17 | uuid: videoChannelObject.uuid, | ||
18 | url: videoChannelObject.id, | ||
19 | createdAt: new Date(videoChannelObject.published), | ||
20 | updatedAt: new Date(videoChannelObject.updated), | ||
21 | remote: true, | ||
22 | accountId: account.id | ||
23 | } | ||
24 | } | ||
10 | 25 | ||
11 | async function videoActivityObjectToDBAttributes ( | 26 | async function videoActivityObjectToDBAttributes ( |
12 | videoChannel: VideoChannelInstance, | 27 | videoChannel: VideoChannelInstance, |
@@ -45,26 +60,32 @@ async function videoActivityObjectToDBAttributes ( | |||
45 | } | 60 | } |
46 | 61 | ||
47 | function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) { | 62 | function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) { |
48 | const fileUrls = videoObject.url | 63 | const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT) |
49 | .filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1 && u.url.startsWith('video/')) | 64 | const fileUrls = videoObject.url.filter(u => { |
65 | return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/') | ||
66 | }) | ||
67 | |||
68 | if (fileUrls.length === 0) { | ||
69 | throw new Error('Cannot find video files for ' + videoCreated.url) | ||
70 | } | ||
50 | 71 | ||
51 | const attributes: VideoFileAttributes[] = [] | 72 | const attributes: VideoFileAttributes[] = [] |
52 | for (const url of fileUrls) { | 73 | for (const fileUrl of fileUrls) { |
53 | // Fetch associated magnet uri | 74 | // Fetch associated magnet uri |
54 | const magnet = videoObject.url | 75 | const magnet = videoObject.url.find(u => { |
55 | .find(u => { | 76 | return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === fileUrl.width |
56 | return u.mimeType === 'application/x-bittorrent;x-scheme-handler/magnet' && u.width === url.width | 77 | }) |
57 | }) | 78 | |
58 | if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + url.url) | 79 | if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.url) |
59 | 80 | ||
60 | const parsed = magnetUtil.decode(magnet.url) | 81 | const parsed = magnetUtil.decode(magnet.url) |
61 | if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url) | 82 | if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.url) |
62 | 83 | ||
63 | const attribute = { | 84 | const attribute = { |
64 | extname: VIDEO_MIMETYPE_EXT[url.mimeType], | 85 | extname: VIDEO_MIMETYPE_EXT[fileUrl.mimeType], |
65 | infoHash: parsed.infoHash, | 86 | infoHash: parsed.infoHash, |
66 | resolution: url.width, | 87 | resolution: fileUrl.width, |
67 | size: url.size, | 88 | size: fileUrl.size, |
68 | videoId: videoCreated.id | 89 | videoId: videoCreated.id |
69 | } | 90 | } |
70 | attributes.push(attribute) | 91 | attributes.push(attribute) |
@@ -77,5 +98,6 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoO | |||
77 | 98 | ||
78 | export { | 99 | export { |
79 | videoFileActivityUrlToDBAttributes, | 100 | videoFileActivityUrlToDBAttributes, |
80 | videoActivityObjectToDBAttributes | 101 | videoActivityObjectToDBAttributes, |
102 | videoChannelActivityObjectToDBAttributes | ||
81 | } | 103 | } |
diff --git a/server/lib/activitypub/process-add.ts b/server/lib/activitypub/process-add.ts index 98e414dbb..df7139d46 100644 --- a/server/lib/activitypub/process-add.ts +++ b/server/lib/activitypub/process-add.ts | |||
@@ -5,6 +5,8 @@ import { database as db } from '../../initializers' | |||
5 | import { AccountInstance } from '../../models/account/account-interface' | 5 | import { AccountInstance } from '../../models/account/account-interface' |
6 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 6 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
7 | import Bluebird = require('bluebird') | 7 | import Bluebird = require('bluebird') |
8 | import { getOrCreateVideoChannel } from '../../helpers/activitypub' | ||
9 | import { VideoChannelInstance } from '../../models/video/video-channel-interface' | ||
8 | 10 | ||
9 | async function processAddActivity (activity: ActivityAdd) { | 11 | async function processAddActivity (activity: ActivityAdd) { |
10 | const activityObject = activity.object | 12 | const activityObject = activity.object |
@@ -12,7 +14,10 @@ async function processAddActivity (activity: ActivityAdd) { | |||
12 | const account = await getOrCreateAccount(activity.actor) | 14 | const account = await getOrCreateAccount(activity.actor) |
13 | 15 | ||
14 | if (activityType === 'Video') { | 16 | if (activityType === 'Video') { |
15 | return processAddVideo(account, activity.id, activityObject as VideoTorrentObject) | 17 | const videoChannelUrl = activity.target |
18 | const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl) | ||
19 | |||
20 | return processAddVideo(account, videoChannel, activityObject as VideoTorrentObject) | ||
16 | } | 21 | } |
17 | 22 | ||
18 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) | 23 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) |
@@ -27,16 +32,16 @@ export { | |||
27 | 32 | ||
28 | // --------------------------------------------------------------------------- | 33 | // --------------------------------------------------------------------------- |
29 | 34 | ||
30 | function processAddVideo (account: AccountInstance, videoChannelUrl: string, video: VideoTorrentObject) { | 35 | function processAddVideo (account: AccountInstance, videoChannel: VideoChannelInstance, video: VideoTorrentObject) { |
31 | const options = { | 36 | const options = { |
32 | arguments: [ account, videoChannelUrl, video ], | 37 | arguments: [ account, videoChannel, video ], |
33 | errorMessage: 'Cannot insert the remote video with many retries.' | 38 | errorMessage: 'Cannot insert the remote video with many retries.' |
34 | } | 39 | } |
35 | 40 | ||
36 | return retryTransactionWrapper(addRemoteVideo, options) | 41 | return retryTransactionWrapper(addRemoteVideo, options) |
37 | } | 42 | } |
38 | 43 | ||
39 | async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string, videoToCreateData: VideoTorrentObject) { | 44 | function addRemoteVideo (account: AccountInstance, videoChannel: VideoChannelInstance, videoToCreateData: VideoTorrentObject) { |
40 | logger.debug('Adding remote video %s.', videoToCreateData.url) | 45 | logger.debug('Adding remote video %s.', videoToCreateData.url) |
41 | 46 | ||
42 | return db.sequelize.transaction(async t => { | 47 | return db.sequelize.transaction(async t => { |
@@ -44,9 +49,6 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string | |||
44 | transaction: t | 49 | transaction: t |
45 | } | 50 | } |
46 | 51 | ||
47 | const videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl, t) | ||
48 | if (!videoChannel) throw new Error('Video channel not found.') | ||
49 | |||
50 | if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.') | 52 | if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.') |
51 | 53 | ||
52 | const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, t) | 54 | const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, t) |
@@ -59,8 +61,11 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string | |||
59 | const videoCreated = await video.save(sequelizeOptions) | 61 | const videoCreated = await video.save(sequelizeOptions) |
60 | 62 | ||
61 | const videoFileAttributes = await videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) | 63 | const videoFileAttributes = await videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) |
64 | if (videoFileAttributes.length === 0) { | ||
65 | throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url) | ||
66 | } | ||
62 | 67 | ||
63 | const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f)) | 68 | const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f, { transaction: t })) |
64 | await Promise.all(tasks) | 69 | await Promise.all(tasks) |
65 | 70 | ||
66 | const tags = videoToCreateData.tag.map(t => t.name) | 71 | const tags = videoToCreateData.tag.map(t => t.name) |
@@ -71,5 +76,4 @@ async function addRemoteVideo (account: AccountInstance, videoChannelUrl: string | |||
71 | 76 | ||
72 | return videoCreated | 77 | return videoCreated |
73 | }) | 78 | }) |
74 | |||
75 | } | 79 | } |
diff --git a/server/lib/activitypub/process-announce.ts b/server/lib/activitypub/process-announce.ts index d67958aec..f9674e095 100644 --- a/server/lib/activitypub/process-announce.ts +++ b/server/lib/activitypub/process-announce.ts | |||
@@ -10,38 +10,33 @@ import { VideoChannelInstance } from '../../models/video/video-channel-interface | |||
10 | import { VideoInstance } from '../../models/index' | 10 | import { VideoInstance } from '../../models/index' |
11 | 11 | ||
12 | async function processAnnounceActivity (activity: ActivityAnnounce) { | 12 | async function processAnnounceActivity (activity: ActivityAnnounce) { |
13 | const activityType = activity.object.type | 13 | const announcedActivity = activity.object |
14 | const accountAnnouncer = await getOrCreateAccount(activity.actor) | 14 | const accountAnnouncer = await getOrCreateAccount(activity.actor) |
15 | 15 | ||
16 | if (activityType === 'VideoChannel') { | 16 | if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') { |
17 | const activityCreate = Object.assign(activity, { | ||
18 | type: 'Create' as 'Create', | ||
19 | actor: activity.object.actor, | ||
20 | object: activity.object as VideoChannelObject | ||
21 | }) | ||
22 | |||
23 | // Add share entry | 17 | // Add share entry |
24 | const videoChannel: VideoChannelInstance = await processCreateActivity(activityCreate) | 18 | const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity) |
25 | await db.VideoChannelShare.create({ | 19 | await db.VideoChannelShare.create({ |
26 | accountId: accountAnnouncer.id, | 20 | accountId: accountAnnouncer.id, |
27 | videoChannelId: videoChannel.id | 21 | videoChannelId: videoChannel.id |
28 | }) | 22 | }) |
29 | } else if (activityType === 'Video') { | ||
30 | const activityAdd = Object.assign(activity, { | ||
31 | type: 'Add' as 'Add', | ||
32 | actor: activity.object.actor, | ||
33 | object: activity.object as VideoTorrentObject | ||
34 | }) | ||
35 | 23 | ||
24 | return undefined | ||
25 | } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') { | ||
36 | // Add share entry | 26 | // Add share entry |
37 | const video: VideoInstance = await processAddActivity(activityAdd) | 27 | const video: VideoInstance = await processAddActivity(announcedActivity) |
38 | await db.VideoShare.create({ | 28 | await db.VideoShare.create({ |
39 | accountId: accountAnnouncer.id, | 29 | accountId: accountAnnouncer.id, |
40 | videoId: video.id | 30 | videoId: video.id |
41 | }) | 31 | }) |
32 | |||
33 | return undefined | ||
42 | } | 34 | } |
43 | 35 | ||
44 | logger.warn('Unknown activity object type %s when announcing activity.', activityType, { activity: activity.id }) | 36 | logger.warn( |
37 | 'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type, | ||
38 | { activity: activity.id } | ||
39 | ) | ||
45 | return Promise.resolve(undefined) | 40 | return Promise.resolve(undefined) |
46 | } | 41 | } |
47 | 42 | ||
diff --git a/server/lib/activitypub/process-create.ts b/server/lib/activitypub/process-create.ts index 4e4c9f703..c4706a66b 100644 --- a/server/lib/activitypub/process-create.ts +++ b/server/lib/activitypub/process-create.ts | |||
@@ -4,6 +4,7 @@ import { logger, retryTransactionWrapper } from '../../helpers' | |||
4 | import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub' | 4 | import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub' |
5 | import { database as db } from '../../initializers' | 5 | import { database as db } from '../../initializers' |
6 | import { AccountInstance } from '../../models/account/account-interface' | 6 | import { AccountInstance } from '../../models/account/account-interface' |
7 | import { videoChannelActivityObjectToDBAttributes } from './misc' | ||
7 | 8 | ||
8 | async function processCreateActivity (activity: ActivityCreate) { | 9 | async function processCreateActivity (activity: ActivityCreate) { |
9 | const activityObject = activity.object | 10 | const activityObject = activity.object |
@@ -37,23 +38,14 @@ function processCreateVideoChannel (account: AccountInstance, videoChannelToCrea | |||
37 | return retryTransactionWrapper(addRemoteVideoChannel, options) | 38 | return retryTransactionWrapper(addRemoteVideoChannel, options) |
38 | } | 39 | } |
39 | 40 | ||
40 | async function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { | 41 | function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { |
41 | logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) | 42 | logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) |
42 | 43 | ||
43 | return db.sequelize.transaction(async t => { | 44 | return db.sequelize.transaction(async t => { |
44 | let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t) | 45 | let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t) |
45 | if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.') | 46 | if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.') |
46 | 47 | ||
47 | const videoChannelData = { | 48 | const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account) |
48 | name: videoChannelToCreateData.name, | ||
49 | description: videoChannelToCreateData.content, | ||
50 | uuid: videoChannelToCreateData.uuid, | ||
51 | createdAt: new Date(videoChannelToCreateData.published), | ||
52 | updatedAt: new Date(videoChannelToCreateData.updated), | ||
53 | remote: true, | ||
54 | accountId: account.id | ||
55 | } | ||
56 | |||
57 | videoChannel = db.VideoChannel.build(videoChannelData) | 49 | videoChannel = db.VideoChannel.build(videoChannelData) |
58 | videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid) | 50 | videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid) |
59 | 51 | ||
@@ -73,7 +65,7 @@ function processCreateVideoAbuse (account: AccountInstance, videoAbuseToCreateDa | |||
73 | return retryTransactionWrapper(addRemoteVideoAbuse, options) | 65 | return retryTransactionWrapper(addRemoteVideoAbuse, options) |
74 | } | 66 | } |
75 | 67 | ||
76 | async function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) { | 68 | function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) { |
77 | logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) | 69 | logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) |
78 | 70 | ||
79 | return db.sequelize.transaction(async t => { | 71 | return db.sequelize.transaction(async t => { |
diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts index 664b9d826..f9b72f2a8 100644 --- a/server/lib/activitypub/send-request.ts +++ b/server/lib/activitypub/send-request.ts | |||
@@ -59,24 +59,21 @@ async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transac | |||
59 | return broadcastToFollowers(data, [ account ], t) | 59 | return broadcastToFollowers(data, [ account ], t) |
60 | } | 60 | } |
61 | 61 | ||
62 | async function sendAnnounce (byAccount: AccountInstance, instance: VideoInstance | VideoChannelInstance, t: Sequelize.Transaction) { | 62 | async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { |
63 | const object = instance.toActivityPubObject() | 63 | const url = getActivityPubUrl('videoChannel', videoChannel.uuid) + '#announce' |
64 | 64 | const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), true) | |
65 | let url = '' | 65 | |
66 | let objectActorUrl: string | 66 | const data = await announceActivityData(url, byAccount, announcedActivity) |
67 | if ((instance as any).VideoChannel !== undefined) { | 67 | return broadcastToFollowers(data, [ byAccount ], t) |
68 | objectActorUrl = (instance as VideoInstance).VideoChannel.Account.url | 68 | } |
69 | url = getActivityPubUrl('video', instance.uuid) + '#announce' | ||
70 | } else { | ||
71 | objectActorUrl = (instance as VideoChannelInstance).Account.url | ||
72 | url = getActivityPubUrl('videoChannel', instance.uuid) + '#announce' | ||
73 | } | ||
74 | 69 | ||
75 | const objectWithActor = Object.assign(object, { | 70 | async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Sequelize.Transaction) { |
76 | actor: objectActorUrl | 71 | const url = getActivityPubUrl('video', video.uuid) + '#announce' |
77 | }) | ||
78 | 72 | ||
79 | const data = await announceActivityData(url, byAccount, objectWithActor) | 73 | const videoChannel = video.VideoChannel |
74 | const announcedActivity = await addActivityData(url, videoChannel.Account, videoChannel.url, video.toActivityPubObject(), true) | ||
75 | |||
76 | const data = await announceActivityData(url, byAccount, announcedActivity) | ||
80 | return broadcastToFollowers(data, [ byAccount ], t) | 77 | return broadcastToFollowers(data, [ byAccount ], t) |
81 | } | 78 | } |
82 | 79 | ||
@@ -117,7 +114,8 @@ export { | |||
117 | sendAccept, | 114 | sendAccept, |
118 | sendFollow, | 115 | sendFollow, |
119 | sendVideoAbuse, | 116 | sendVideoAbuse, |
120 | sendAnnounce | 117 | sendVideoChannelAnnounce, |
118 | sendVideoAnnounce | ||
121 | } | 119 | } |
122 | 120 | ||
123 | // --------------------------------------------------------------------------- | 121 | // --------------------------------------------------------------------------- |
@@ -159,7 +157,7 @@ async function getPublicActivityTo (account: AccountInstance) { | |||
159 | return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public') | 157 | return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public') |
160 | } | 158 | } |
161 | 159 | ||
162 | async function createActivityData (url: string, byAccount: AccountInstance, object: any) { | 160 | async function createActivityData (url: string, byAccount: AccountInstance, object: any, raw = false) { |
163 | const to = await getPublicActivityTo(byAccount) | 161 | const to = await getPublicActivityTo(byAccount) |
164 | const base = { | 162 | const base = { |
165 | type: 'Create', | 163 | type: 'Create', |
@@ -169,6 +167,8 @@ async function createActivityData (url: string, byAccount: AccountInstance, obje | |||
169 | object | 167 | object |
170 | } | 168 | } |
171 | 169 | ||
170 | if (raw === true) return base | ||
171 | |||
172 | return buildSignedActivity(byAccount, base) | 172 | return buildSignedActivity(byAccount, base) |
173 | } | 173 | } |
174 | 174 | ||
@@ -195,7 +195,7 @@ async function deleteActivityData (url: string, byAccount: AccountInstance) { | |||
195 | return buildSignedActivity(byAccount, base) | 195 | return buildSignedActivity(byAccount, base) |
196 | } | 196 | } |
197 | 197 | ||
198 | async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) { | 198 | async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any, raw = false) { |
199 | const to = await getPublicActivityTo(byAccount) | 199 | const to = await getPublicActivityTo(byAccount) |
200 | const base = { | 200 | const base = { |
201 | type: 'Add', | 201 | type: 'Add', |
@@ -206,6 +206,8 @@ async function addActivityData (url: string, byAccount: AccountInstance, target: | |||
206 | target | 206 | target |
207 | } | 207 | } |
208 | 208 | ||
209 | if (raw === true) return base | ||
210 | |||
209 | return buildSignedActivity(byAccount, base) | 211 | return buildSignedActivity(byAccount, base) |
210 | } | 212 | } |
211 | 213 | ||
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts index f0ead24e3..0326e05b9 100644 --- a/server/middlewares/validators/video-channels.ts +++ b/server/middlewares/validators/video-channels.ts | |||
@@ -83,7 +83,7 @@ const videoChannelsRemoveValidator = [ | |||
83 | } | 83 | } |
84 | ] | 84 | ] |
85 | 85 | ||
86 | const videoChannelGetValidator = [ | 86 | const videoChannelsGetValidator = [ |
87 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | 87 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), |
88 | 88 | ||
89 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 89 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
@@ -102,7 +102,7 @@ export { | |||
102 | videoChannelsAddValidator, | 102 | videoChannelsAddValidator, |
103 | videoChannelsUpdateValidator, | 103 | videoChannelsUpdateValidator, |
104 | videoChannelsRemoveValidator, | 104 | videoChannelsRemoveValidator, |
105 | videoChannelGetValidator | 105 | videoChannelsGetValidator |
106 | } | 106 | } |
107 | 107 | ||
108 | // --------------------------------------------------------------------------- | 108 | // --------------------------------------------------------------------------- |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 1f4604f1d..f8414d4a8 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -264,7 +264,8 @@ loadByUrl = function (url: string, t?: Sequelize.Transaction) { | |||
264 | const query: Sequelize.FindOptions<VideoChannelAttributes> = { | 264 | const query: Sequelize.FindOptions<VideoChannelAttributes> = { |
265 | where: { | 265 | where: { |
266 | url | 266 | url |
267 | } | 267 | }, |
268 | include: [ VideoChannel['sequelize'].models.Account ] | ||
268 | } | 269 | } |
269 | 270 | ||
270 | if (t !== undefined) query.transaction = t | 271 | if (t !== undefined) query.transaction = t |