diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-11-16 11:08:25 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-11-27 19:40:52 +0100 |
commit | efc32059d980c51793e8e9ac0fb6a885a8026f94 (patch) | |
tree | c272e63fd57a9625b53dc26ceb1b46aee35d21d3 | |
parent | d846501818c2d29e66e6fd141789cb04fd55a437 (diff) | |
download | PeerTube-efc32059d980c51793e8e9ac0fb6a885a8026f94.tar.gz PeerTube-efc32059d980c51793e8e9ac0fb6a885a8026f94.tar.zst PeerTube-efc32059d980c51793e8e9ac0fb6a885a8026f94.zip |
Send server announce when users upload a video
22 files changed, 161 insertions, 80 deletions
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 0aa276c69..9e6c6b888 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -257,6 +257,7 @@ export class AuthService { | |||
257 | this.user.save() | 257 | this.user.save() |
258 | 258 | ||
259 | this.setStatus(AuthStatus.LoggedIn) | 259 | this.setStatus(AuthStatus.LoggedIn) |
260 | this.userInformationLoaded.next(true) | ||
260 | } | 261 | } |
261 | 262 | ||
262 | private handleRefreshToken (obj: UserRefreshToken) { | 263 | private handleRefreshToken (obj: UserRefreshToken) { |
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 49dd24e79..76049f496 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -46,7 +46,7 @@ async function accountFollowersController (req: express.Request, res: express.Re | |||
46 | const page = req.params.page || 1 | 46 | const page = req.params.page || 1 |
47 | const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE) | 47 | const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE) |
48 | 48 | ||
49 | const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi(account.id, start, count) | 49 | const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi([ account.id ], start, count) |
50 | const activityPubResult = activityPubCollectionPagination(req.url, page, result) | 50 | const activityPubResult = activityPubCollectionPagination(req.url, page, result) |
51 | 51 | ||
52 | return res.json(activityPubResult) | 52 | return res.json(activityPubResult) |
@@ -58,7 +58,7 @@ async function accountFollowingController (req: express.Request, res: express.Re | |||
58 | const page = req.params.page || 1 | 58 | const page = req.params.page || 1 |
59 | const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE) | 59 | const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE) |
60 | 60 | ||
61 | const result = await db.AccountFollow.listAcceptedFollowingUrlsForApi(account.id, start, count) | 61 | const result = await db.AccountFollow.listAcceptedFollowingUrlsForApi([ account.id ], start, count) |
62 | const activityPubResult = activityPubCollectionPagination(req.url, page, result) | 62 | const activityPubResult = activityPubCollectionPagination(req.url, page, result) |
63 | 63 | ||
64 | return res.json(activityPubResult) | 64 | return res.json(activityPubResult) |
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index ac8ea87f9..e00787f02 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -2,7 +2,7 @@ import * as express from 'express' | |||
2 | import { UserRight } from '../../../../shared/models/users/user-right.enum' | 2 | import { UserRight } from '../../../../shared/models/users/user-right.enum' |
3 | import { getFormattedObjects } from '../../../helpers' | 3 | import { getFormattedObjects } from '../../../helpers' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { getApplicationAccount } from '../../../helpers/utils' | 5 | import { getServerAccount } from '../../../helpers/utils' |
6 | import { getAccountFromWebfinger } from '../../../helpers/webfinger' | 6 | import { getAccountFromWebfinger } from '../../../helpers/webfinger' |
7 | import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants' | 7 | import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants' |
8 | import { database as db } from '../../../initializers/database' | 8 | import { database as db } from '../../../initializers/database' |
@@ -50,14 +50,14 @@ export { | |||
50 | // --------------------------------------------------------------------------- | 50 | // --------------------------------------------------------------------------- |
51 | 51 | ||
52 | async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) { | 52 | async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) { |
53 | const applicationAccount = await getApplicationAccount() | 53 | const applicationAccount = await getServerAccount() |
54 | const resultList = await db.AccountFollow.listFollowingForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort) | 54 | const resultList = await db.AccountFollow.listFollowingForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort) |
55 | 55 | ||
56 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 56 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
57 | } | 57 | } |
58 | 58 | ||
59 | async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) { | 59 | async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) { |
60 | const applicationAccount = await getApplicationAccount() | 60 | const applicationAccount = await getServerAccount() |
61 | const resultList = await db.AccountFollow.listFollowersForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort) | 61 | const resultList = await db.AccountFollow.listFollowersForApi(applicationAccount.id, req.query.start, req.query.count, req.query.sort) |
62 | 62 | ||
63 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 63 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
@@ -65,7 +65,7 @@ async function listFollowers (req: express.Request, res: express.Response, next: | |||
65 | 65 | ||
66 | async function follow (req: express.Request, res: express.Response, next: express.NextFunction) { | 66 | async function follow (req: express.Request, res: express.Response, next: express.NextFunction) { |
67 | const hosts = req.body.hosts as string[] | 67 | const hosts = req.body.hosts as string[] |
68 | const fromAccount = await getApplicationAccount() | 68 | const fromAccount = await getServerAccount() |
69 | 69 | ||
70 | const tasks: Promise<any>[] = [] | 70 | const tasks: Promise<any>[] = [] |
71 | const accountName = SERVER_ACCOUNT_NAME | 71 | const accountName = SERVER_ACCOUNT_NAME |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index ebc07e179..a5414cc50 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -12,10 +12,10 @@ import { | |||
12 | resetSequelizeInstance, | 12 | resetSequelizeInstance, |
13 | retryTransactionWrapper | 13 | retryTransactionWrapper |
14 | } from '../../../helpers' | 14 | } from '../../../helpers' |
15 | import { getActivityPubUrl } from '../../../helpers/activitypub' | 15 | import { getActivityPubUrl, shareVideoByServer } from '../../../helpers/activitypub' |
16 | import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers' | 16 | import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers' |
17 | import { database as db } from '../../../initializers/database' | 17 | import { database as db } from '../../../initializers/database' |
18 | import { sendAddVideo, sendUpdateVideoChannel } from '../../../lib/activitypub/send-request' | 18 | import { sendAddVideo, sendUpdateVideo } from '../../../lib/activitypub/send-request' |
19 | import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler' | 19 | import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler' |
20 | import { | 20 | import { |
21 | asyncMiddleware, | 21 | asyncMiddleware, |
@@ -56,7 +56,7 @@ const storage = multer.diskStorage({ | |||
56 | randomString = 'fake-random-string' | 56 | randomString = 'fake-random-string' |
57 | } | 57 | } |
58 | 58 | ||
59 | cb(null, randomString + '.' + extension) | 59 | cb(null, randomString + extension) |
60 | } | 60 | } |
61 | }) | 61 | }) |
62 | 62 | ||
@@ -237,6 +237,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi | |||
237 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 237 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
238 | 238 | ||
239 | await sendAddVideo(video, t) | 239 | await sendAddVideo(video, t) |
240 | await shareVideoByServer(video, t) | ||
240 | }) | 241 | }) |
241 | 242 | ||
242 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID) | 243 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoUUID) |
@@ -254,7 +255,7 @@ async function updateVideoRetryWrapper (req: express.Request, res: express.Respo | |||
254 | } | 255 | } |
255 | 256 | ||
256 | async function updateVideo (req: express.Request, res: express.Response) { | 257 | async function updateVideo (req: express.Request, res: express.Response) { |
257 | const videoInstance = res.locals.video | 258 | const videoInstance: VideoInstance = res.locals.video |
258 | const videoFieldsSave = videoInstance.toJSON() | 259 | const videoFieldsSave = videoInstance.toJSON() |
259 | const videoInfoToUpdate: VideoUpdate = req.body | 260 | const videoInfoToUpdate: VideoUpdate = req.body |
260 | const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE | 261 | const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE |
@@ -284,7 +285,7 @@ async function updateVideo (req: express.Request, res: express.Response) { | |||
284 | 285 | ||
285 | // Now we'll update the video's meta data to our friends | 286 | // Now we'll update the video's meta data to our friends |
286 | if (wasPrivateVideo === false) { | 287 | if (wasPrivateVideo === false) { |
287 | await sendUpdateVideoChannel(videoInstance, t) | 288 | await sendUpdateVideo(videoInstance, t) |
288 | } | 289 | } |
289 | 290 | ||
290 | // Video is not private anymore, send a create action to remote servers | 291 | // Video is not private anymore, send a create action to remote servers |
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts index de20ba55d..b376b8ca2 100644 --- a/server/helpers/activitypub.ts +++ b/server/helpers/activitypub.ts | |||
@@ -1,15 +1,19 @@ | |||
1 | import { join } from 'path' | 1 | import { join } from 'path' |
2 | import * as request from 'request' | 2 | import * as request from 'request' |
3 | import * as Sequelize from 'sequelize' | ||
3 | import * as url from 'url' | 4 | import * as url from 'url' |
4 | import { ActivityIconObject } from '../../shared/index' | 5 | import { ActivityIconObject } from '../../shared/index' |
5 | import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' | 6 | import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' |
6 | import { ResultList } from '../../shared/models/result-list.model' | 7 | import { ResultList } from '../../shared/models/result-list.model' |
7 | import { database as db, REMOTE_SCHEME } from '../initializers' | 8 | import { database as db, REMOTE_SCHEME } from '../initializers' |
8 | import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' | 9 | import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' |
10 | import { sendAnnounce } from '../lib/activitypub/send-request' | ||
11 | import { VideoChannelInstance } from '../models/video/video-channel-interface' | ||
9 | import { VideoInstance } from '../models/video/video-interface' | 12 | import { VideoInstance } from '../models/video/video-interface' |
10 | import { isRemoteAccountValid } from './custom-validators' | 13 | import { isRemoteAccountValid } from './custom-validators' |
11 | import { logger } from './logger' | 14 | import { logger } from './logger' |
12 | import { doRequest, doRequestAndSaveToFile } from './requests' | 15 | import { doRequest, doRequestAndSaveToFile } from './requests' |
16 | import { getServerAccount } from './utils' | ||
13 | 17 | ||
14 | function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) { | 18 | function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) { |
15 | const thumbnailName = video.getThumbnailName() | 19 | const thumbnailName = video.getThumbnailName() |
@@ -22,6 +26,28 @@ function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObjec | |||
22 | return doRequestAndSaveToFile(options, thumbnailPath) | 26 | return doRequestAndSaveToFile(options, thumbnailPath) |
23 | } | 27 | } |
24 | 28 | ||
29 | async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { | ||
30 | const serverAccount = await getServerAccount() | ||
31 | |||
32 | await db.VideoChannelShare.create({ | ||
33 | accountId: serverAccount.id, | ||
34 | videoChannelId: videoChannel.id | ||
35 | }, { transaction: t }) | ||
36 | |||
37 | return sendAnnounce(serverAccount, videoChannel, t) | ||
38 | } | ||
39 | |||
40 | async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) { | ||
41 | const serverAccount = await getServerAccount() | ||
42 | |||
43 | await db.VideoShare.create({ | ||
44 | accountId: serverAccount.id, | ||
45 | videoId: video.id | ||
46 | }, { transaction: t }) | ||
47 | |||
48 | return sendAnnounce(serverAccount, video, t) | ||
49 | } | ||
50 | |||
25 | function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) { | 51 | function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) { |
26 | if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id | 52 | if (type === 'video') return CONFIG.WEBSERVER.URL + '/videos/watch/' + id |
27 | else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id | 53 | else if (type === 'videoChannel') return CONFIG.WEBSERVER.URL + '/video-channels/' + id |
@@ -172,7 +198,9 @@ export { | |||
172 | generateThumbnailFromUrl, | 198 | generateThumbnailFromUrl, |
173 | getOrCreateAccount, | 199 | getOrCreateAccount, |
174 | fetchRemoteVideoPreview, | 200 | fetchRemoteVideoPreview, |
175 | fetchRemoteVideoDescription | 201 | fetchRemoteVideoDescription, |
202 | shareVideoChannelByServer, | ||
203 | shareVideoByServer | ||
176 | } | 204 | } |
177 | 205 | ||
178 | // --------------------------------------------------------------------------- | 206 | // --------------------------------------------------------------------------- |
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts index d8748e1d7..4ff07848c 100644 --- a/server/helpers/core-utils.ts +++ b/server/helpers/core-utils.ts | |||
@@ -20,9 +20,6 @@ import * as bcrypt from 'bcrypt' | |||
20 | import * as createTorrent from 'create-torrent' | 20 | import * as createTorrent from 'create-torrent' |
21 | import * as rimraf from 'rimraf' | 21 | import * as rimraf from 'rimraf' |
22 | import * as pem from 'pem' | 22 | import * as pem from 'pem' |
23 | import * as jsonld from 'jsonld' | ||
24 | import * as jsig from 'jsonld-signatures' | ||
25 | jsig.use('jsonld', jsonld) | ||
26 | 23 | ||
27 | function isTestInstance () { | 24 | function isTestInstance () { |
28 | return process.env.NODE_ENV === 'test' | 25 | return process.env.NODE_ENV === 'test' |
@@ -120,8 +117,6 @@ const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash) | |||
120 | const createTorrentPromise = promisify2<string, any, any>(createTorrent) | 117 | const createTorrentPromise = promisify2<string, any, any>(createTorrent) |
121 | const rimrafPromise = promisify1WithVoid<string>(rimraf) | 118 | const rimrafPromise = promisify1WithVoid<string>(rimraf) |
122 | const statPromise = promisify1<string, Stats>(stat) | 119 | const statPromise = promisify1<string, Stats>(stat) |
123 | const jsonldSignPromise = promisify2<object, { privateKeyPem: string, creator: string }, object>(jsig.sign) | ||
124 | const jsonldVerifyPromise = promisify2<object, object, object>(jsig.verify) | ||
125 | 120 | ||
126 | // --------------------------------------------------------------------------- | 121 | // --------------------------------------------------------------------------- |
127 | 122 | ||
@@ -150,7 +145,5 @@ export { | |||
150 | bcryptHashPromise, | 145 | bcryptHashPromise, |
151 | createTorrentPromise, | 146 | createTorrentPromise, |
152 | rimrafPromise, | 147 | rimrafPromise, |
153 | statPromise, | 148 | statPromise |
154 | jsonldSignPromise, | ||
155 | jsonldVerifyPromise | ||
156 | } | 149 | } |
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts index 9ddacd601..89c49b0df 100644 --- a/server/helpers/custom-validators/activitypub/videos.ts +++ b/server/helpers/custom-validators/activitypub/videos.ts | |||
@@ -34,7 +34,7 @@ function isActivityPubVideoDurationValid (value: string) { | |||
34 | typeof value === 'string' && | 34 | typeof value === 'string' && |
35 | value.startsWith('PT') && | 35 | value.startsWith('PT') && |
36 | value.endsWith('S') && | 36 | value.endsWith('S') && |
37 | isVideoDurationValid(value.replace(/[^0-9]+/, '')) | 37 | isVideoDurationValid(value.replace(/[^0-9]+/g, '')) |
38 | } | 38 | } |
39 | 39 | ||
40 | function isVideoTorrentObjectValid (video: any) { | 40 | function isVideoTorrentObjectValid (video: any) { |
@@ -46,13 +46,14 @@ function isVideoTorrentObjectValid (video: any) { | |||
46 | isRemoteIdentifierValid(video.category) && | 46 | isRemoteIdentifierValid(video.category) && |
47 | isRemoteIdentifierValid(video.licence) && | 47 | isRemoteIdentifierValid(video.licence) && |
48 | isRemoteIdentifierValid(video.language) && | 48 | isRemoteIdentifierValid(video.language) && |
49 | isVideoViewsValid(video.video) && | 49 | isVideoViewsValid(video.views) && |
50 | isVideoNSFWValid(video.nsfw) && | 50 | isVideoNSFWValid(video.nsfw) && |
51 | isDateValid(video.published) && | 51 | isDateValid(video.published) && |
52 | isDateValid(video.updated) && | 52 | isDateValid(video.updated) && |
53 | isRemoteVideoContentValid(video.mediaType, video.content) && | 53 | isRemoteVideoContentValid(video.mediaType, video.content) && |
54 | isRemoteVideoIconValid(video.icon) && | 54 | isRemoteVideoIconValid(video.icon) && |
55 | setValidRemoteVideoUrls(video.url) | 55 | setValidRemoteVideoUrls(video) && |
56 | video.url.length !== 0 | ||
56 | } | 57 | } |
57 | 58 | ||
58 | function isVideoFlagValid (activity: any) { | 59 | function isVideoFlagValid (activity: any) { |
@@ -132,8 +133,8 @@ function isRemoteVideoIconValid (icon: any) { | |||
132 | return icon.type === 'Image' && | 133 | return icon.type === 'Image' && |
133 | isVideoUrlValid(icon.url) && | 134 | isVideoUrlValid(icon.url) && |
134 | icon.mediaType === 'image/jpeg' && | 135 | icon.mediaType === 'image/jpeg' && |
135 | validator.isInt(icon.width, { min: 0 }) && | 136 | validator.isInt(icon.width + '', { min: 0 }) && |
136 | validator.isInt(icon.height, { min: 0 }) | 137 | validator.isInt(icon.height + '', { min: 0 }) |
137 | } | 138 | } |
138 | 139 | ||
139 | function setValidRemoteVideoUrls (video: any) { | 140 | function setValidRemoteVideoUrls (video: any) { |
@@ -149,6 +150,6 @@ function isRemoteVideoUrlValid (url: any) { | |||
149 | return url.type === 'Link' && | 150 | return url.type === 'Link' && |
150 | ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 && | 151 | ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 && |
151 | isVideoUrlValid(url.url) && | 152 | isVideoUrlValid(url.url) && |
152 | validator.isInt(url.width, { min: 0 }) && | 153 | validator.isInt(url.width + '', { min: 0 }) && |
153 | validator.isInt(url.size, { min: 0 }) | 154 | validator.isInt(url.size + '', { min: 0 }) |
154 | } | 155 | } |
diff --git a/server/helpers/peertube-crypto.ts b/server/helpers/peertube-crypto.ts index 6d50e446f..04a8d5681 100644 --- a/server/helpers/peertube-crypto.ts +++ b/server/helpers/peertube-crypto.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | import * as jsonld from 'jsonld' | ||
1 | import * as jsig from 'jsonld-signatures' | 2 | import * as jsig from 'jsonld-signatures' |
3 | jsig.use('jsonld', jsonld) | ||
2 | 4 | ||
3 | import { | 5 | import { |
4 | PRIVATE_RSA_KEY_SIZE, | 6 | PRIVATE_RSA_KEY_SIZE, |
@@ -9,9 +11,7 @@ import { | |||
9 | bcryptGenSaltPromise, | 11 | bcryptGenSaltPromise, |
10 | bcryptHashPromise, | 12 | bcryptHashPromise, |
11 | createPrivateKey, | 13 | createPrivateKey, |
12 | getPublicKey, | 14 | getPublicKey |
13 | jsonldSignPromise, | ||
14 | jsonldVerifyPromise | ||
15 | } from './core-utils' | 15 | } from './core-utils' |
16 | import { logger } from './logger' | 16 | import { logger } from './logger' |
17 | import { AccountInstance } from '../models/account/account-interface' | 17 | import { AccountInstance } from '../models/account/account-interface' |
@@ -45,7 +45,7 @@ function isSignatureVerified (fromAccount: AccountInstance, signedDocument: obje | |||
45 | publicKeyOwner: publicKeyOwnerObject | 45 | publicKeyOwner: publicKeyOwnerObject |
46 | } | 46 | } |
47 | 47 | ||
48 | return jsonldVerifyPromise(signedDocument, options) | 48 | return jsig.promises.verify(signedDocument, options) |
49 | .catch(err => { | 49 | .catch(err => { |
50 | logger.error('Cannot check signature.', err) | 50 | logger.error('Cannot check signature.', err) |
51 | return false | 51 | return false |
@@ -58,7 +58,7 @@ function signObject (byAccount: AccountInstance, data: any) { | |||
58 | creator: byAccount.url | 58 | creator: byAccount.url |
59 | } | 59 | } |
60 | 60 | ||
61 | return jsonldSignPromise(data, options) | 61 | return jsig.promises.sign(data, options) |
62 | } | 62 | } |
63 | 63 | ||
64 | function comparePassword (plainPassword: string, hashPassword: string) { | 64 | function comparePassword (plainPassword: string, hashPassword: string) { |
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index 39957c90f..3af14a68a 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts | |||
@@ -6,6 +6,7 @@ import { CONFIG, database as db } from '../initializers' | |||
6 | import { ResultList } from '../../shared' | 6 | import { ResultList } from '../../shared' |
7 | import { VideoResolution } from '../../shared/models/videos/video-resolution.enum' | 7 | import { VideoResolution } from '../../shared/models/videos/video-resolution.enum' |
8 | import { AccountInstance } from '../models/account/account-interface' | 8 | import { AccountInstance } from '../models/account/account-interface' |
9 | import { logger } from './logger' | ||
9 | 10 | ||
10 | function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) { | 11 | function badRequest (req: express.Request, res: express.Response, next: express.NextFunction) { |
11 | return res.type('json').status(400).end() | 12 | return res.type('json').status(400).end() |
@@ -79,13 +80,18 @@ function resetSequelizeInstance (instance: Sequelize.Instance<any>, savedFields: | |||
79 | }) | 80 | }) |
80 | } | 81 | } |
81 | 82 | ||
82 | let applicationAccount: AccountInstance | 83 | let serverAccount: AccountInstance |
83 | async function getApplicationAccount () { | 84 | async function getServerAccount () { |
84 | if (applicationAccount === undefined) { | 85 | if (serverAccount === undefined) { |
85 | applicationAccount = await db.Account.loadApplication() | 86 | serverAccount = await db.Account.loadApplication() |
86 | } | 87 | } |
87 | 88 | ||
88 | return Promise.resolve(applicationAccount) | 89 | if (!serverAccount) { |
90 | logger.error('Cannot load server account.') | ||
91 | process.exit(0) | ||
92 | } | ||
93 | |||
94 | return Promise.resolve(serverAccount) | ||
89 | } | 95 | } |
90 | 96 | ||
91 | type SortType = { sortModel: any, sortValue: string } | 97 | type SortType = { sortModel: any, sortValue: string } |
@@ -99,6 +105,6 @@ export { | |||
99 | isSignupAllowed, | 105 | isSignupAllowed, |
100 | computeResolutionsToTranscode, | 106 | computeResolutionsToTranscode, |
101 | resetSequelizeInstance, | 107 | resetSequelizeInstance, |
102 | getApplicationAccount, | 108 | getServerAccount, |
103 | SortType | 109 | SortType |
104 | } | 110 | } |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 99da95ab9..dca223370 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -209,9 +209,9 @@ const VIDEO_PRIVACIES = { | |||
209 | } | 209 | } |
210 | 210 | ||
211 | const VIDEO_MIMETYPE_EXT = { | 211 | const VIDEO_MIMETYPE_EXT = { |
212 | 'video/webm': 'webm', | 212 | 'video/webm': '.webm', |
213 | 'video/ogg': 'ogv', | 213 | 'video/ogg': '.ogv', |
214 | 'video/mp4': 'mp4' | 214 | 'video/mp4': '.mp4' |
215 | } | 215 | } |
216 | 216 | ||
217 | // --------------------------------------------------------------------------- | 217 | // --------------------------------------------------------------------------- |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index c3521a9e4..3f4c4dfbb 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -13,9 +13,9 @@ async function installApplication () { | |||
13 | await db.sequelize.sync() | 13 | await db.sequelize.sync() |
14 | await removeCacheDirectories() | 14 | await removeCacheDirectories() |
15 | await createDirectoriesIfNotExist() | 15 | await createDirectoriesIfNotExist() |
16 | await createApplicationIfNotExist() | ||
16 | await createOAuthClientIfNotExist() | 17 | await createOAuthClientIfNotExist() |
17 | await createOAuthAdminIfNotExist() | 18 | await createOAuthAdminIfNotExist() |
18 | await createApplicationIfNotExist() | ||
19 | } catch (err) { | 19 | } catch (err) { |
20 | logger.error('Cannot install application.', err) | 20 | logger.error('Cannot install application.', err) |
21 | throw err | 21 | throw err |
diff --git a/server/lib/activitypub/misc.ts b/server/lib/activitypub/misc.ts index 2cf0c4fd1..43d26c328 100644 --- a/server/lib/activitypub/misc.ts +++ b/server/lib/activitypub/misc.ts | |||
@@ -28,9 +28,9 @@ async function videoActivityObjectToDBAttributes ( | |||
28 | description: videoObject.content, | 28 | description: videoObject.content, |
29 | channelId: videoChannel.id, | 29 | channelId: videoChannel.id, |
30 | duration: parseInt(duration, 10), | 30 | duration: parseInt(duration, 10), |
31 | createdAt: videoObject.published, | 31 | createdAt: new Date(videoObject.published), |
32 | // FIXME: updatedAt does not seems to be considered by Sequelize | 32 | // FIXME: updatedAt does not seems to be considered by Sequelize |
33 | updatedAt: videoObject.updated, | 33 | updatedAt: new Date(videoObject.updated), |
34 | views: videoObject.views, | 34 | views: videoObject.views, |
35 | likes: 0, | 35 | likes: 0, |
36 | dislikes: 0, | 36 | dislikes: 0, |
@@ -46,7 +46,7 @@ async function videoActivityObjectToDBAttributes ( | |||
46 | 46 | ||
47 | function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) { | 47 | function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) { |
48 | const fileUrls = videoObject.url | 48 | const fileUrls = videoObject.url |
49 | .filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1) | 49 | .filter(u => Object.keys(VIDEO_MIMETYPE_EXT).indexOf(u.mimeType) !== -1 && u.url.startsWith('video/')) |
50 | 50 | ||
51 | const attributes: VideoFileAttributes[] = [] | 51 | const attributes: VideoFileAttributes[] = [] |
52 | for (const url of fileUrls) { | 52 | for (const url of fileUrls) { |
diff --git a/server/lib/activitypub/process-create.ts b/server/lib/activitypub/process-create.ts index 1b825ebbc..4e4c9f703 100644 --- a/server/lib/activitypub/process-create.ts +++ b/server/lib/activitypub/process-create.ts | |||
@@ -48,8 +48,8 @@ async function addRemoteVideoChannel (account: AccountInstance, videoChannelToCr | |||
48 | name: videoChannelToCreateData.name, | 48 | name: videoChannelToCreateData.name, |
49 | description: videoChannelToCreateData.content, | 49 | description: videoChannelToCreateData.content, |
50 | uuid: videoChannelToCreateData.uuid, | 50 | uuid: videoChannelToCreateData.uuid, |
51 | createdAt: videoChannelToCreateData.published, | 51 | createdAt: new Date(videoChannelToCreateData.published), |
52 | updatedAt: videoChannelToCreateData.updated, | 52 | updatedAt: new Date(videoChannelToCreateData.updated), |
53 | remote: true, | 53 | remote: true, |
54 | accountId: account.id | 54 | accountId: account.id |
55 | } | 55 | } |
diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts index 1dad51828..664b9d826 100644 --- a/server/lib/activitypub/send-request.ts +++ b/server/lib/activitypub/send-request.ts | |||
@@ -17,46 +17,67 @@ async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Se | |||
17 | const videoChannelObject = videoChannel.toActivityPubObject() | 17 | const videoChannelObject = videoChannel.toActivityPubObject() |
18 | const data = await createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) | 18 | const data = await createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) |
19 | 19 | ||
20 | return broadcastToFollowers(data, videoChannel.Account, t) | 20 | return broadcastToFollowers(data, [ videoChannel.Account ], t) |
21 | } | 21 | } |
22 | 22 | ||
23 | async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { | 23 | async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { |
24 | const videoChannelObject = videoChannel.toActivityPubObject() | 24 | const videoChannelObject = videoChannel.toActivityPubObject() |
25 | const data = await updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) | 25 | const data = await updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) |
26 | 26 | ||
27 | return broadcastToFollowers(data, videoChannel.Account, t) | 27 | return broadcastToFollowers(data, [ videoChannel.Account ], t) |
28 | } | 28 | } |
29 | 29 | ||
30 | async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { | 30 | async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { |
31 | const data = await deleteActivityData(videoChannel.url, videoChannel.Account) | 31 | const data = await deleteActivityData(videoChannel.url, videoChannel.Account) |
32 | 32 | ||
33 | return broadcastToFollowers(data, videoChannel.Account, t) | 33 | return broadcastToFollowers(data, [ videoChannel.Account ], t) |
34 | } | 34 | } |
35 | 35 | ||
36 | async function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) { | 36 | async function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) { |
37 | const videoObject = video.toActivityPubObject() | 37 | const videoObject = video.toActivityPubObject() |
38 | const data = await addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject) | 38 | const data = await addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject) |
39 | 39 | ||
40 | return broadcastToFollowers(data, video.VideoChannel.Account, t) | 40 | return broadcastToFollowers(data, [ video.VideoChannel.Account ], t) |
41 | } | 41 | } |
42 | 42 | ||
43 | async function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) { | 43 | async function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) { |
44 | const videoObject = video.toActivityPubObject() | 44 | const videoObject = video.toActivityPubObject() |
45 | const data = await updateActivityData(video.url, video.VideoChannel.Account, videoObject) | 45 | const data = await updateActivityData(video.url, video.VideoChannel.Account, videoObject) |
46 | 46 | ||
47 | return broadcastToFollowers(data, video.VideoChannel.Account, t) | 47 | return broadcastToFollowers(data, [ video.VideoChannel.Account ], t) |
48 | } | 48 | } |
49 | 49 | ||
50 | async function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) { | 50 | async function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) { |
51 | const data = await deleteActivityData(video.url, video.VideoChannel.Account) | 51 | const data = await deleteActivityData(video.url, video.VideoChannel.Account) |
52 | 52 | ||
53 | return broadcastToFollowers(data, video.VideoChannel.Account, t) | 53 | return broadcastToFollowers(data, [ video.VideoChannel.Account ], t) |
54 | } | 54 | } |
55 | 55 | ||
56 | async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) { | 56 | async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) { |
57 | const data = await deleteActivityData(account.url, account) | 57 | const data = await deleteActivityData(account.url, account) |
58 | 58 | ||
59 | return broadcastToFollowers(data, account, t) | 59 | return broadcastToFollowers(data, [ account ], t) |
60 | } | ||
61 | |||
62 | async function sendAnnounce (byAccount: AccountInstance, instance: VideoInstance | VideoChannelInstance, t: Sequelize.Transaction) { | ||
63 | const object = instance.toActivityPubObject() | ||
64 | |||
65 | let url = '' | ||
66 | let objectActorUrl: string | ||
67 | if ((instance as any).VideoChannel !== undefined) { | ||
68 | objectActorUrl = (instance as VideoInstance).VideoChannel.Account.url | ||
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 | |||
75 | const objectWithActor = Object.assign(object, { | ||
76 | actor: objectActorUrl | ||
77 | }) | ||
78 | |||
79 | const data = await announceActivityData(url, byAccount, objectWithActor) | ||
80 | return broadcastToFollowers(data, [ byAccount ], t) | ||
60 | } | 81 | } |
61 | 82 | ||
62 | async function sendVideoAbuse ( | 83 | async function sendVideoAbuse ( |
@@ -95,15 +116,17 @@ export { | |||
95 | sendDeleteAccount, | 116 | sendDeleteAccount, |
96 | sendAccept, | 117 | sendAccept, |
97 | sendFollow, | 118 | sendFollow, |
98 | sendVideoAbuse | 119 | sendVideoAbuse, |
120 | sendAnnounce | ||
99 | } | 121 | } |
100 | 122 | ||
101 | // --------------------------------------------------------------------------- | 123 | // --------------------------------------------------------------------------- |
102 | 124 | ||
103 | async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) { | 125 | async function broadcastToFollowers (data: any, toAccountFollowers: AccountInstance[], t: Sequelize.Transaction) { |
104 | const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi(fromAccount.id) | 126 | const toAccountFollowerIds = toAccountFollowers.map(a => a.id) |
127 | const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds) | ||
105 | if (result.data.length === 0) { | 128 | if (result.data.length === 0) { |
106 | logger.info('Not broadcast because of 0 followers.') | 129 | logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', ')) |
107 | return | 130 | return |
108 | } | 131 | } |
109 | 132 | ||
@@ -186,6 +209,17 @@ async function addActivityData (url: string, byAccount: AccountInstance, target: | |||
186 | return buildSignedActivity(byAccount, base) | 209 | return buildSignedActivity(byAccount, base) |
187 | } | 210 | } |
188 | 211 | ||
212 | async function announceActivityData (url: string, byAccount: AccountInstance, object: any) { | ||
213 | const base = { | ||
214 | type: 'Announce', | ||
215 | id: url, | ||
216 | actor: byAccount.url, | ||
217 | object | ||
218 | } | ||
219 | |||
220 | return buildSignedActivity(byAccount, base) | ||
221 | } | ||
222 | |||
189 | async function followActivityData (url: string, byAccount: AccountInstance) { | 223 | async function followActivityData (url: string, byAccount: AccountInstance) { |
190 | const base = { | 224 | const base = { |
191 | type: 'Follow', | 225 | type: 'Follow', |
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 f6d9627a5..6443899d3 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 | |||
@@ -6,6 +6,7 @@ import { VideoInstance } from '../../../models' | |||
6 | import { sendAddVideo } from '../../activitypub/send-request' | 6 | import { sendAddVideo } from '../../activitypub/send-request' |
7 | import { JobScheduler } from '../job-scheduler' | 7 | import { JobScheduler } from '../job-scheduler' |
8 | import { TranscodingJobPayload } from './transcoding-job-scheduler' | 8 | import { TranscodingJobPayload } from './transcoding-job-scheduler' |
9 | import { shareVideoByServer } from '../../../helpers/activitypub' | ||
9 | 10 | ||
10 | async function process (data: TranscodingJobPayload, jobId: number) { | 11 | async function process (data: TranscodingJobPayload, jobId: number) { |
11 | const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID) | 12 | const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID) |
@@ -37,6 +38,7 @@ async function onSuccess (jobId: number, video: VideoInstance, jobScheduler: Job | |||
37 | 38 | ||
38 | // Now we'll add the video's meta data to our followers | 39 | // Now we'll add the video's meta data to our followers |
39 | await sendAddVideo(video, undefined) | 40 | await sendAddVideo(video, undefined) |
41 | await shareVideoByServer(video, undefined) | ||
40 | 42 | ||
41 | const originalFileHeight = await videoDatabase.getOriginalFileHeight() | 43 | const originalFileHeight = await videoDatabase.getOriginalFileHeight() |
42 | 44 | ||
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 80303fb83..e69ec062f 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -5,7 +5,7 @@ import { logger } from '../helpers' | |||
5 | import { AccountInstance } from '../models' | 5 | import { AccountInstance } from '../models' |
6 | import { VideoChannelCreate } from '../../shared/models' | 6 | import { VideoChannelCreate } from '../../shared/models' |
7 | import { sendCreateVideoChannel } from './activitypub/send-request' | 7 | import { sendCreateVideoChannel } from './activitypub/send-request' |
8 | import { getActivityPubUrl } from '../helpers/activitypub' | 8 | import { getActivityPubUrl, shareVideoChannelByServer } from '../helpers/activitypub' |
9 | 9 | ||
10 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { | 10 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { |
11 | const videoChannelData = { | 11 | const videoChannelData = { |
@@ -25,7 +25,8 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account | |||
25 | // Do not forget to add Account information to the created video channel | 25 | // Do not forget to add Account information to the created video channel |
26 | videoChannelCreated.Account = account | 26 | videoChannelCreated.Account = account |
27 | 27 | ||
28 | sendCreateVideoChannel(videoChannelCreated, t) | 28 | await sendCreateVideoChannel(videoChannelCreated, t) |
29 | await shareVideoChannelByServer(videoChannelCreated, t) | ||
29 | 30 | ||
30 | return videoChannelCreated | 31 | return videoChannelCreated |
31 | } | 32 | } |
diff --git a/server/models/account/account-follow-interface.ts b/server/models/account/account-follow-interface.ts index 54baf45ed..21fda98ce 100644 --- a/server/models/account/account-follow-interface.ts +++ b/server/models/account/account-follow-interface.ts | |||
@@ -10,8 +10,9 @@ export namespace AccountFollowMethods { | |||
10 | export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> > | 10 | export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> > |
11 | export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> > | 11 | export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountInstance> > |
12 | 12 | ||
13 | export type ListAcceptedFollowerUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList<string> > | 13 | export type ListAcceptedFollowerUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> > |
14 | export type ListAcceptedFollowingUrlsForApi = (id: number, start?: number, count?: number) => Promise< ResultList<string> > | 14 | export type ListAcceptedFollowingUrlsForApi = (accountId: number[], start?: number, count?: number) => Promise< ResultList<string> > |
15 | export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[]) => Promise< ResultList<string> > | ||
15 | } | 16 | } |
16 | 17 | ||
17 | export interface AccountFollowClass { | 18 | export interface AccountFollowClass { |
@@ -21,6 +22,7 @@ export interface AccountFollowClass { | |||
21 | 22 | ||
22 | listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi | 23 | listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi |
23 | listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi | 24 | listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi |
25 | listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls | ||
24 | } | 26 | } |
25 | 27 | ||
26 | export interface AccountFollowAttributes { | 28 | export interface AccountFollowAttributes { |
diff --git a/server/models/account/account-follow.ts b/server/models/account/account-follow.ts index f457e43e9..8a7474c9d 100644 --- a/server/models/account/account-follow.ts +++ b/server/models/account/account-follow.ts | |||
@@ -11,6 +11,7 @@ let listFollowingForApi: AccountFollowMethods.ListFollowingForApi | |||
11 | let listFollowersForApi: AccountFollowMethods.ListFollowersForApi | 11 | let listFollowersForApi: AccountFollowMethods.ListFollowersForApi |
12 | let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi | 12 | let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi |
13 | let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi | 13 | let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi |
14 | let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls | ||
14 | 15 | ||
15 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | 16 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { |
16 | AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow', | 17 | AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow', |
@@ -42,7 +43,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
42 | listFollowingForApi, | 43 | listFollowingForApi, |
43 | listFollowersForApi, | 44 | listFollowersForApi, |
44 | listAcceptedFollowerUrlsForApi, | 45 | listAcceptedFollowerUrlsForApi, |
45 | listAcceptedFollowingUrlsForApi | 46 | listAcceptedFollowingUrlsForApi, |
47 | listAcceptedFollowerSharedInboxUrls | ||
46 | ] | 48 | ] |
47 | addMethodsToModel(AccountFollow, classMethods) | 49 | addMethodsToModel(AccountFollow, classMethods) |
48 | 50 | ||
@@ -146,17 +148,27 @@ listFollowersForApi = function (id: number, start: number, count: number, sort: | |||
146 | }) | 148 | }) |
147 | } | 149 | } |
148 | 150 | ||
149 | listAcceptedFollowerUrlsForApi = function (accountId: number, start?: number, count?: number) { | 151 | listAcceptedFollowerUrlsForApi = function (accountIds: number[], start?: number, count?: number) { |
150 | return createListAcceptedFollowForApiQuery('followers', accountId, start, count) | 152 | return createListAcceptedFollowForApiQuery('followers', accountIds, start, count) |
151 | } | 153 | } |
152 | 154 | ||
153 | listAcceptedFollowingUrlsForApi = function (accountId: number, start?: number, count?: number) { | 155 | listAcceptedFollowerSharedInboxUrls = function (accountIds: number[]) { |
154 | return createListAcceptedFollowForApiQuery('following', accountId, start, count) | 156 | return createListAcceptedFollowForApiQuery('followers', accountIds, undefined, undefined, 'sharedInboxUrl') |
157 | } | ||
158 | |||
159 | listAcceptedFollowingUrlsForApi = function (accountIds: number[], start?: number, count?: number) { | ||
160 | return createListAcceptedFollowForApiQuery('following', accountIds, start, count) | ||
155 | } | 161 | } |
156 | 162 | ||
157 | // ------------------------------ UTILS ------------------------------ | 163 | // ------------------------------ UTILS ------------------------------ |
158 | 164 | ||
159 | async function createListAcceptedFollowForApiQuery (type: 'followers' | 'following', accountId: number, start?: number, count?: number) { | 165 | async function createListAcceptedFollowForApiQuery ( |
166 | type: 'followers' | 'following', | ||
167 | accountIds: number[], | ||
168 | start?: number, | ||
169 | count?: number, | ||
170 | columnUrl = 'url' | ||
171 | ) { | ||
160 | let firstJoin: string | 172 | let firstJoin: string |
161 | let secondJoin: string | 173 | let secondJoin: string |
162 | 174 | ||
@@ -168,20 +180,20 @@ async function createListAcceptedFollowForApiQuery (type: 'followers' | 'followi | |||
168 | secondJoin = 'targetAccountId' | 180 | secondJoin = 'targetAccountId' |
169 | } | 181 | } |
170 | 182 | ||
171 | const selections = [ '"Follows"."url" AS "url"', 'COUNT(*) AS "total"' ] | 183 | const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ] |
172 | const tasks: Promise<any>[] = [] | 184 | const tasks: Promise<any>[] = [] |
173 | 185 | ||
174 | for (const selection of selections) { | 186 | for (const selection of selections) { |
175 | let query = 'SELECT ' + selection + ' FROM "Accounts" ' + | 187 | let query = 'SELECT ' + selection + ' FROM "Accounts" ' + |
176 | 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' + | 188 | 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' + |
177 | 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' + | 189 | 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' + |
178 | 'WHERE "Accounts"."id" = $accountId AND "AccountFollows"."state" = \'accepted\' ' | 190 | 'WHERE "Accounts"."id" IN ($accountIds) AND "AccountFollows"."state" = \'accepted\' ' |
179 | 191 | ||
180 | if (start !== undefined) query += 'LIMIT ' + start | 192 | if (start !== undefined) query += 'LIMIT ' + start |
181 | if (count !== undefined) query += ', ' + count | 193 | if (count !== undefined) query += ', ' + count |
182 | 194 | ||
183 | const options = { | 195 | const options = { |
184 | bind: { accountId }, | 196 | bind: { accountIds: accountIds.join(',') }, |
185 | type: Sequelize.QueryTypes.SELECT | 197 | type: Sequelize.QueryTypes.SELECT |
186 | } | 198 | } |
187 | tasks.push(AccountFollow['sequelize'].query(query, options)) | 199 | tasks.push(AccountFollow['sequelize'].query(query, options)) |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 3cb4a33b9..1f4604f1d 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -153,8 +153,8 @@ toActivityPubObject = function (this: VideoChannelInstance) { | |||
153 | uuid: this.uuid, | 153 | uuid: this.uuid, |
154 | content: this.description, | 154 | content: this.description, |
155 | name: this.name, | 155 | name: this.name, |
156 | published: this.createdAt, | 156 | published: this.createdAt.toISOString(), |
157 | updated: this.updatedAt | 157 | updated: this.updatedAt.toISOString() |
158 | } | 158 | } |
159 | 159 | ||
160 | return json | 160 | return json |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 480e54276..64ee7ae34 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -558,7 +558,7 @@ toActivityPubObject = function (this: VideoInstance) { | |||
558 | for (const file of this.VideoFiles) { | 558 | for (const file of this.VideoFiles) { |
559 | url.push({ | 559 | url.push({ |
560 | type: 'Link', | 560 | type: 'Link', |
561 | mimeType: 'video/' + file.extname, | 561 | mimeType: 'video/' + file.extname.replace('.', ''), |
562 | url: getVideoFileUrl(this, file, baseUrlHttp), | 562 | url: getVideoFileUrl(this, file, baseUrlHttp), |
563 | width: file.resolution, | 563 | width: file.resolution, |
564 | size: file.size | 564 | size: file.size |
@@ -601,8 +601,8 @@ toActivityPubObject = function (this: VideoInstance) { | |||
601 | }, | 601 | }, |
602 | views: this.views, | 602 | views: this.views, |
603 | nsfw: this.nsfw, | 603 | nsfw: this.nsfw, |
604 | published: this.createdAt, | 604 | published: this.createdAt.toISOString(), |
605 | updated: this.updatedAt, | 605 | updated: this.updatedAt.toISOString(), |
606 | mediaType: 'text/markdown', | 606 | mediaType: 'text/markdown', |
607 | content: this.getTruncatedDescription(), | 607 | content: this.getTruncatedDescription(), |
608 | icon: { | 608 | icon: { |
diff --git a/shared/models/activitypub/objects/video-channel-object.ts b/shared/models/activitypub/objects/video-channel-object.ts index 468e1535e..c9325b5df 100644 --- a/shared/models/activitypub/objects/video-channel-object.ts +++ b/shared/models/activitypub/objects/video-channel-object.ts | |||
@@ -4,7 +4,7 @@ export interface VideoChannelObject { | |||
4 | name: string | 4 | name: string |
5 | content: string | 5 | content: string |
6 | uuid: string | 6 | uuid: string |
7 | published: Date | 7 | published: string |
8 | updated: Date | 8 | updated: string |
9 | actor?: string | 9 | actor?: string |
10 | } | 10 | } |
diff --git a/shared/models/activitypub/objects/video-torrent-object.ts b/shared/models/activitypub/objects/video-torrent-object.ts index 99e7157b8..ae8f807c8 100644 --- a/shared/models/activitypub/objects/video-torrent-object.ts +++ b/shared/models/activitypub/objects/video-torrent-object.ts | |||
@@ -17,8 +17,8 @@ export interface VideoTorrentObject { | |||
17 | language: ActivityIdentifierObject | 17 | language: ActivityIdentifierObject |
18 | views: number | 18 | views: number |
19 | nsfw: boolean | 19 | nsfw: boolean |
20 | published: Date | 20 | published: string |
21 | updated: Date | 21 | updated: string |
22 | mediaType: 'text/markdown' | 22 | mediaType: 'text/markdown' |
23 | content: string | 23 | content: string |
24 | icon: ActivityIconObject | 24 | icon: ActivityIconObject |