diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-11-27 14:44:51 +0100 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-11-27 19:40:53 +0100 |
commit | 4e50b6a1c9a3eb261e04ede73241648e6edf21d6 (patch) | |
tree | e1c6c121d561ffc1cf2996daec03a1e7f27f0a25 | |
parent | 74bb2cb8348d6794ed3a0e2ec94c8c9abdde82cf (diff) | |
download | PeerTube-4e50b6a1c9a3eb261e04ede73241648e6edf21d6.tar.gz PeerTube-4e50b6a1c9a3eb261e04ede73241648e6edf21d6.tar.zst PeerTube-4e50b6a1c9a3eb261e04ede73241648e6edf21d6.zip |
Add shares forward and collection on videos/video channels
29 files changed, 546 insertions, 133 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index eee89e2fd..41272bca0 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -1,22 +1,26 @@ | |||
1 | // Intercept ActivityPub client requests | 1 | // Intercept ActivityPub client requests |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | |||
4 | import { database as db } from '../../initializers' | ||
5 | import { executeIfActivityPub, localAccountValidator } from '../../middlewares' | ||
6 | import { pageToStartAndCount } from '../../helpers' | 3 | import { pageToStartAndCount } from '../../helpers' |
7 | import { AccountInstance, VideoChannelInstance } from '../../models' | ||
8 | import { activityPubCollectionPagination } from '../../helpers/activitypub' | 4 | import { activityPubCollectionPagination } from '../../helpers/activitypub' |
5 | |||
6 | import { database as db } from '../../initializers' | ||
9 | import { ACTIVITY_PUB, CONFIG } from '../../initializers/constants' | 7 | import { ACTIVITY_PUB, CONFIG } from '../../initializers/constants' |
8 | import { buildVideoChannelAnnounceToFollowers } from '../../lib/activitypub/send/send-announce' | ||
9 | import { buildVideoAnnounceToFollowers } from '../../lib/index' | ||
10 | import { executeIfActivityPub, localAccountValidator } from '../../middlewares' | ||
10 | import { asyncMiddleware } from '../../middlewares/async' | 11 | import { asyncMiddleware } from '../../middlewares/async' |
11 | import { videosGetValidator } from '../../middlewares/validators/videos' | 12 | import { videoChannelsGetValidator, videoChannelsShareValidator } from '../../middlewares/validators/video-channels' |
13 | import { videosGetValidator, videosShareValidator } from '../../middlewares/validators/videos' | ||
14 | import { AccountInstance, VideoChannelInstance } from '../../models' | ||
15 | import { VideoChannelShareInstance } from '../../models/video/video-channel-share-interface' | ||
12 | import { VideoInstance } from '../../models/video/video-interface' | 16 | import { VideoInstance } from '../../models/video/video-interface' |
13 | import { videoChannelsGetValidator } from '../../middlewares/validators/video-channels' | 17 | import { VideoShareInstance } from '../../models/video/video-share-interface' |
14 | 18 | ||
15 | const activityPubClientRouter = express.Router() | 19 | const activityPubClientRouter = express.Router() |
16 | 20 | ||
17 | activityPubClientRouter.get('/account/:name', | 21 | activityPubClientRouter.get('/account/:name', |
18 | executeIfActivityPub(localAccountValidator), | 22 | executeIfActivityPub(localAccountValidator), |
19 | executeIfActivityPub(asyncMiddleware(accountController)) | 23 | executeIfActivityPub(accountController) |
20 | ) | 24 | ) |
21 | 25 | ||
22 | activityPubClientRouter.get('/account/:name/followers', | 26 | activityPubClientRouter.get('/account/:name/followers', |
@@ -31,7 +35,12 @@ activityPubClientRouter.get('/account/:name/following', | |||
31 | 35 | ||
32 | activityPubClientRouter.get('/videos/watch/:id', | 36 | activityPubClientRouter.get('/videos/watch/:id', |
33 | executeIfActivityPub(videosGetValidator), | 37 | executeIfActivityPub(videosGetValidator), |
34 | executeIfActivityPub(asyncMiddleware(videoController)) | 38 | executeIfActivityPub(videoController) |
39 | ) | ||
40 | |||
41 | activityPubClientRouter.get('/videos/watch/:id/announces/:accountId', | ||
42 | executeIfActivityPub(asyncMiddleware(videosShareValidator)), | ||
43 | executeIfActivityPub(asyncMiddleware(videoAnnounceController)) | ||
35 | ) | 44 | ) |
36 | 45 | ||
37 | activityPubClientRouter.get('/video-channels/:id', | 46 | activityPubClientRouter.get('/video-channels/:id', |
@@ -39,6 +48,11 @@ activityPubClientRouter.get('/video-channels/:id', | |||
39 | executeIfActivityPub(asyncMiddleware(videoChannelController)) | 48 | executeIfActivityPub(asyncMiddleware(videoChannelController)) |
40 | ) | 49 | ) |
41 | 50 | ||
51 | activityPubClientRouter.get('/video-channels/:id/announces/:accountId', | ||
52 | executeIfActivityPub(asyncMiddleware(videoChannelsShareValidator)), | ||
53 | executeIfActivityPub(asyncMiddleware(videoChannelAnnounceController)) | ||
54 | ) | ||
55 | |||
42 | // --------------------------------------------------------------------------- | 56 | // --------------------------------------------------------------------------- |
43 | 57 | ||
44 | export { | 58 | export { |
@@ -47,7 +61,7 @@ export { | |||
47 | 61 | ||
48 | // --------------------------------------------------------------------------- | 62 | // --------------------------------------------------------------------------- |
49 | 63 | ||
50 | async function accountController (req: express.Request, res: express.Response, next: express.NextFunction) { | 64 | function accountController (req: express.Request, res: express.Response, next: express.NextFunction) { |
51 | const account: AccountInstance = res.locals.account | 65 | const account: AccountInstance = res.locals.account |
52 | 66 | ||
53 | return res.json(account.toActivityPubObject()).end() | 67 | return res.json(account.toActivityPubObject()).end() |
@@ -77,12 +91,26 @@ async function accountFollowingController (req: express.Request, res: express.Re | |||
77 | return res.json(activityPubResult) | 91 | return res.json(activityPubResult) |
78 | } | 92 | } |
79 | 93 | ||
80 | async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { | 94 | function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { |
81 | const video: VideoInstance = res.locals.video | 95 | const video: VideoInstance = res.locals.video |
82 | 96 | ||
83 | return res.json(video.toActivityPubObject()) | 97 | return res.json(video.toActivityPubObject()) |
84 | } | 98 | } |
85 | 99 | ||
100 | async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
101 | const share = res.locals.videoShare as VideoShareInstance | ||
102 | const object = await buildVideoAnnounceToFollowers(share.Account, res.locals.video, undefined) | ||
103 | |||
104 | return res.json(object) | ||
105 | } | ||
106 | |||
107 | async function videoChannelAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
108 | const share = res.locals.videoChannelShare as VideoChannelShareInstance | ||
109 | const object = await buildVideoChannelAnnounceToFollowers(share.Account, share.VideoChannel, undefined) | ||
110 | |||
111 | return res.json(object) | ||
112 | } | ||
113 | |||
86 | async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) { | 114 | async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) { |
87 | const videoChannel: VideoChannelInstance = res.locals.videoChannel | 115 | const videoChannel: VideoChannelInstance = res.locals.videoChannel |
88 | 116 | ||
diff --git a/server/helpers/custom-validators/accounts.ts b/server/helpers/custom-validators/accounts.ts index fe0fc650a..a6d7f2b82 100644 --- a/server/helpers/custom-validators/accounts.ts +++ b/server/helpers/custom-validators/accounts.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import * as Promise from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import 'express-validator' | 3 | import 'express-validator' |
4 | import * as validator from 'validator' | 4 | import * as validator from 'validator' |
@@ -11,33 +11,45 @@ function isAccountNameValid (value: string) { | |||
11 | return isUserUsernameValid(value) | 11 | return isUserUsernameValid(value) |
12 | } | 12 | } |
13 | 13 | ||
14 | function checkVideoAccountExists (id: string, res: express.Response, callback: () => void) { | 14 | function checkAccountIdExists (id: number | string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { |
15 | let promise: Promise<AccountInstance> | 15 | let promise: Bluebird<AccountInstance> |
16 | if (validator.isInt(id)) { | 16 | |
17 | if (validator.isInt('' + id)) { | ||
17 | promise = db.Account.load(+id) | 18 | promise = db.Account.load(+id) |
18 | } else { // UUID | 19 | } else { // UUID |
19 | promise = db.Account.loadByUUID(id) | 20 | promise = db.Account.loadByUUID('' + id) |
20 | } | 21 | } |
21 | 22 | ||
22 | promise.then(account => { | 23 | return checkAccountExists(promise, res, callback) |
24 | } | ||
25 | |||
26 | function checkLocalAccountNameExists (name: string, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { | ||
27 | const p = db.Account.loadLocalByName(name) | ||
28 | |||
29 | return checkAccountExists(p, res, callback) | ||
30 | } | ||
31 | |||
32 | function checkAccountExists (p: Bluebird<AccountInstance>, res: express.Response, callback: (err: Error, account: AccountInstance) => any) { | ||
33 | p.then(account => { | ||
23 | if (!account) { | 34 | if (!account) { |
24 | return res.status(404) | 35 | return res.status(404) |
25 | .json({ error: 'Video account not found' }) | 36 | .send({ error: 'Account not found' }) |
26 | .end() | 37 | .end() |
27 | } | 38 | } |
28 | 39 | ||
29 | res.locals.account = account | 40 | res.locals.account = account |
30 | callback() | 41 | return callback(null, account) |
31 | }) | ||
32 | .catch(err => { | ||
33 | logger.error('Error in video account request validator.', err) | ||
34 | return res.sendStatus(500) | ||
35 | }) | 42 | }) |
43 | .catch(err => { | ||
44 | logger.error('Error in account request validator.', err) | ||
45 | return res.sendStatus(500) | ||
46 | }) | ||
36 | } | 47 | } |
37 | 48 | ||
38 | // --------------------------------------------------------------------------- | 49 | // --------------------------------------------------------------------------- |
39 | 50 | ||
40 | export { | 51 | export { |
41 | checkVideoAccountExists, | 52 | checkAccountIdExists, |
53 | checkLocalAccountNameExists, | ||
42 | isAccountNameValid | 54 | isAccountNameValid |
43 | } | 55 | } |
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts index 5de01f74b..267d987fc 100644 --- a/server/helpers/custom-validators/video-channels.ts +++ b/server/helpers/custom-validators/video-channels.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import * as Promise from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as validator from 'validator' | ||
3 | import * as express from 'express' | 2 | import * as express from 'express' |
4 | import 'express-validator' | 3 | import 'express-validator' |
5 | import 'multer' | 4 | import 'multer' |
5 | import * as validator from 'validator' | ||
6 | 6 | ||
7 | import { database as db, CONSTRAINTS_FIELDS } from '../../initializers' | 7 | import { CONSTRAINTS_FIELDS, database as db } from '../../initializers' |
8 | import { VideoChannelInstance } from '../../models' | 8 | import { VideoChannelInstance } from '../../models' |
9 | import { logger } from '../logger' | 9 | import { logger } from '../logger' |
10 | import { exists } from './misc' | ||
11 | import { isActivityPubUrlValid } from './index' | 10 | import { isActivityPubUrlValid } from './index' |
11 | import { exists } from './misc' | ||
12 | 12 | ||
13 | const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS | 13 | const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS |
14 | 14 | ||
@@ -25,7 +25,7 @@ function isVideoChannelNameValid (value: string) { | |||
25 | } | 25 | } |
26 | 26 | ||
27 | function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) { | 27 | function checkVideoChannelExists (id: string, res: express.Response, callback: () => void) { |
28 | let promise: Promise<VideoChannelInstance> | 28 | let promise: Bluebird<VideoChannelInstance> |
29 | if (validator.isInt(id)) { | 29 | if (validator.isInt(id)) { |
30 | promise = db.VideoChannel.loadAndPopulateAccount(+id) | 30 | promise = db.VideoChannel.loadAndPopulateAccount(+id) |
31 | } else { // UUID | 31 | } else { // UUID |
@@ -48,11 +48,32 @@ function checkVideoChannelExists (id: string, res: express.Response, callback: ( | |||
48 | }) | 48 | }) |
49 | } | 49 | } |
50 | 50 | ||
51 | async function isVideoChannelExistsPromise (id: string, res: express.Response) { | ||
52 | let videoChannel: VideoChannelInstance | ||
53 | if (validator.isInt(id)) { | ||
54 | videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id) | ||
55 | } else { // UUID | ||
56 | videoChannel = await db.VideoChannel.loadByUUIDAndPopulateAccount(id) | ||
57 | } | ||
58 | |||
59 | if (!videoChannel) { | ||
60 | res.status(404) | ||
61 | .json({ error: 'Video channel not found' }) | ||
62 | .end() | ||
63 | |||
64 | return false | ||
65 | } | ||
66 | |||
67 | res.locals.videoChannel = videoChannel | ||
68 | return true | ||
69 | } | ||
70 | |||
51 | // --------------------------------------------------------------------------- | 71 | // --------------------------------------------------------------------------- |
52 | 72 | ||
53 | export { | 73 | export { |
54 | isVideoChannelDescriptionValid, | 74 | isVideoChannelDescriptionValid, |
55 | isVideoChannelNameValid, | ||
56 | checkVideoChannelExists, | 75 | checkVideoChannelExists, |
76 | isVideoChannelNameValid, | ||
77 | isVideoChannelExistsPromise, | ||
57 | isVideoChannelUrlValid | 78 | isVideoChannelUrlValid |
58 | } | 79 | } |
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index 205d8c62f..276354626 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts | |||
@@ -130,6 +130,27 @@ function checkVideoExists (id: string, res: Response, callback: () => void) { | |||
130 | }) | 130 | }) |
131 | } | 131 | } |
132 | 132 | ||
133 | async function isVideoExistsPromise (id: string, res: Response) { | ||
134 | let video: VideoInstance | ||
135 | |||
136 | if (validator.isInt(id)) { | ||
137 | video = await db.Video.loadAndPopulateAccountAndServerAndTags(+id) | ||
138 | } else { // UUID | ||
139 | video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id) | ||
140 | } | ||
141 | |||
142 | if (!video) { | ||
143 | res.status(404) | ||
144 | .json({ error: 'Video not found' }) | ||
145 | .end() | ||
146 | |||
147 | return false | ||
148 | } | ||
149 | |||
150 | res.locals.video = video | ||
151 | return true | ||
152 | } | ||
153 | |||
133 | // --------------------------------------------------------------------------- | 154 | // --------------------------------------------------------------------------- |
134 | 155 | ||
135 | export { | 156 | export { |
@@ -152,5 +173,6 @@ export { | |||
152 | isVideoPrivacyValid, | 173 | isVideoPrivacyValid, |
153 | isVideoFileResolutionValid, | 174 | isVideoFileResolutionValid, |
154 | isVideoFileSizeValid, | 175 | isVideoFileSizeValid, |
155 | checkVideoExists | 176 | checkVideoExists, |
177 | isVideoExistsPromise | ||
156 | } | 178 | } |
diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts index eefbe2884..f20e588ab 100644 --- a/server/lib/activitypub/process/misc.ts +++ b/server/lib/activitypub/process/misc.ts | |||
@@ -1,13 +1,16 @@ | |||
1 | import * as magnetUtil from 'magnet-uri' | 1 | import * as magnetUtil from 'magnet-uri' |
2 | import { VideoTorrentObject } from '../../../../shared' | 2 | import { VideoTorrentObject } from '../../../../shared' |
3 | import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object' | 3 | import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object' |
4 | import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum' | ||
4 | import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos' | 5 | import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos' |
6 | import { doRequest } from '../../../helpers/requests' | ||
7 | import { database as db } from '../../../initializers' | ||
5 | import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants' | 8 | import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants' |
6 | import { AccountInstance } from '../../../models/account/account-interface' | 9 | import { AccountInstance } from '../../../models/account/account-interface' |
7 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 10 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
8 | import { VideoFileAttributes } from '../../../models/video/video-file-interface' | 11 | import { VideoFileAttributes } from '../../../models/video/video-file-interface' |
9 | import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface' | 12 | import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface' |
10 | import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum' | 13 | import { getOrCreateAccountAndServer } from '../account' |
11 | 14 | ||
12 | function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { | 15 | function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { |
13 | return { | 16 | return { |
@@ -97,10 +100,60 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoO | |||
97 | return attributes | 100 | return attributes |
98 | } | 101 | } |
99 | 102 | ||
103 | async function addVideoShares (instance: VideoInstance, shares: string[]) { | ||
104 | for (const share of shares) { | ||
105 | // Fetch url | ||
106 | const json = await doRequest({ | ||
107 | uri: share, | ||
108 | json: true | ||
109 | }) | ||
110 | const actor = json['actor'] | ||
111 | if (!actor) continue | ||
112 | |||
113 | const account = await getOrCreateAccountAndServer(actor) | ||
114 | |||
115 | const entry = { | ||
116 | accountId: account.id, | ||
117 | videoId: instance.id | ||
118 | } | ||
119 | |||
120 | await db.VideoShare.findOrCreate({ | ||
121 | where: entry, | ||
122 | defaults: entry | ||
123 | }) | ||
124 | } | ||
125 | } | ||
126 | |||
127 | async function addVideoChannelShares (instance: VideoChannelInstance, shares: string[]) { | ||
128 | for (const share of shares) { | ||
129 | // Fetch url | ||
130 | const json = await doRequest({ | ||
131 | uri: share, | ||
132 | json: true | ||
133 | }) | ||
134 | const actor = json['actor'] | ||
135 | if (!actor) continue | ||
136 | |||
137 | const account = await getOrCreateAccountAndServer(actor) | ||
138 | |||
139 | const entry = { | ||
140 | accountId: account.id, | ||
141 | videoChannelId: instance.id | ||
142 | } | ||
143 | |||
144 | await db.VideoChannelShare.findOrCreate({ | ||
145 | where: entry, | ||
146 | defaults: entry | ||
147 | }) | ||
148 | } | ||
149 | } | ||
150 | |||
100 | // --------------------------------------------------------------------------- | 151 | // --------------------------------------------------------------------------- |
101 | 152 | ||
102 | export { | 153 | export { |
103 | videoFileActivityUrlToDBAttributes, | 154 | videoFileActivityUrlToDBAttributes, |
104 | videoActivityObjectToDBAttributes, | 155 | videoActivityObjectToDBAttributes, |
105 | videoChannelActivityObjectToDBAttributes | 156 | videoChannelActivityObjectToDBAttributes, |
157 | addVideoChannelShares, | ||
158 | addVideoShares | ||
106 | } | 159 | } |
diff --git a/server/lib/activitypub/process/process-add.ts b/server/lib/activitypub/process/process-add.ts index 98280b9f0..e6bf63eb2 100644 --- a/server/lib/activitypub/process/process-add.ts +++ b/server/lib/activitypub/process/process-add.ts | |||
@@ -11,7 +11,7 @@ import { VideoInstance } from '../../../models/video/video-interface' | |||
11 | import { getOrCreateAccountAndServer } from '../account' | 11 | import { getOrCreateAccountAndServer } from '../account' |
12 | import { getOrCreateVideoChannel } from '../video-channels' | 12 | import { getOrCreateVideoChannel } from '../video-channels' |
13 | import { generateThumbnailFromUrl } from '../videos' | 13 | import { generateThumbnailFromUrl } from '../videos' |
14 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 14 | import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
15 | 15 | ||
16 | async function processAddActivity (activity: ActivityAdd) { | 16 | async function processAddActivity (activity: ActivityAdd) { |
17 | const activityObject = activity.object | 17 | const activityObject = activity.object |
@@ -37,12 +37,10 @@ export { | |||
37 | 37 | ||
38 | // --------------------------------------------------------------------------- | 38 | // --------------------------------------------------------------------------- |
39 | 39 | ||
40 | async function processAddVideo ( | 40 | async function processAddVideo (account: AccountInstance, |
41 | account: AccountInstance, | 41 | activity: ActivityAdd, |
42 | activity: ActivityAdd, | 42 | videoChannel: VideoChannelInstance, |
43 | videoChannel: VideoChannelInstance, | 43 | videoToCreateData: VideoTorrentObject) { |
44 | videoToCreateData: VideoTorrentObject | ||
45 | ) { | ||
46 | const options = { | 44 | const options = { |
47 | arguments: [ account, activity, videoChannel, videoToCreateData ], | 45 | arguments: [ account, activity, videoChannel, videoToCreateData ], |
48 | errorMessage: 'Cannot insert the remote video with many retries.' | 46 | errorMessage: 'Cannot insert the remote video with many retries.' |
@@ -59,6 +57,10 @@ async function processAddVideo ( | |||
59 | await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') | 57 | await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') |
60 | } | 58 | } |
61 | 59 | ||
60 | if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) { | ||
61 | await addVideoShares(video, videoToCreateData.shares.orderedItems) | ||
62 | } | ||
63 | |||
62 | return video | 64 | return video |
63 | } | 65 | } |
64 | 66 | ||
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index d8532d3a1..2aa9ad5c7 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts | |||
@@ -1,34 +1,23 @@ | |||
1 | import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub/activity' |
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | ||
2 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
3 | import { database as db } from '../../../initializers/index' | 4 | import { database as db } from '../../../initializers/index' |
5 | import { AccountInstance } from '../../../models/account/account-interface' | ||
4 | import { VideoInstance } from '../../../models/index' | 6 | import { VideoInstance } from '../../../models/index' |
5 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 7 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
8 | import { getOrCreateAccountAndServer } from '../account' | ||
9 | import { forwardActivity } from '../send/misc' | ||
6 | import { processAddActivity } from './process-add' | 10 | import { processAddActivity } from './process-add' |
7 | import { processCreateActivity } from './process-create' | 11 | import { processCreateActivity } from './process-create' |
8 | import { getOrCreateAccountAndServer } from '../account' | ||
9 | 12 | ||
10 | async function processAnnounceActivity (activity: ActivityAnnounce) { | 13 | async function processAnnounceActivity (activity: ActivityAnnounce) { |
11 | const announcedActivity = activity.object | 14 | const announcedActivity = activity.object |
12 | const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor) | 15 | const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor) |
13 | 16 | ||
14 | if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') { | 17 | if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') { |
15 | // Add share entry | 18 | return processVideoChannelShare(accountAnnouncer, activity) |
16 | const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity) | ||
17 | await db.VideoChannelShare.create({ | ||
18 | accountId: accountAnnouncer.id, | ||
19 | videoChannelId: videoChannel.id | ||
20 | }) | ||
21 | |||
22 | return undefined | ||
23 | } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') { | 19 | } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') { |
24 | // Add share entry | 20 | return processVideoShare(accountAnnouncer, activity) |
25 | const video: VideoInstance = await processAddActivity(announcedActivity) | ||
26 | await db.VideoShare.create({ | ||
27 | accountId: accountAnnouncer.id, | ||
28 | videoId: video.id | ||
29 | }) | ||
30 | |||
31 | return undefined | ||
32 | } | 21 | } |
33 | 22 | ||
34 | logger.warn( | 23 | logger.warn( |
@@ -44,3 +33,78 @@ async function processAnnounceActivity (activity: ActivityAnnounce) { | |||
44 | export { | 33 | export { |
45 | processAnnounceActivity | 34 | processAnnounceActivity |
46 | } | 35 | } |
36 | |||
37 | // --------------------------------------------------------------------------- | ||
38 | |||
39 | function processVideoChannelShare (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) { | ||
40 | const options = { | ||
41 | arguments: [ accountAnnouncer, activity ], | ||
42 | errorMessage: 'Cannot share the video channel with many retries.' | ||
43 | } | ||
44 | |||
45 | return retryTransactionWrapper(shareVideoChannel, options) | ||
46 | } | ||
47 | |||
48 | async function shareVideoChannel (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) { | ||
49 | const announcedActivity = activity.object as ActivityCreate | ||
50 | |||
51 | return db.sequelize.transaction(async t => { | ||
52 | // Add share entry | ||
53 | const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity) | ||
54 | const share = { | ||
55 | accountId: accountAnnouncer.id, | ||
56 | videoChannelId: videoChannel.id | ||
57 | } | ||
58 | |||
59 | const [ , created ] = await db.VideoChannelShare.findOrCreate({ | ||
60 | where: share, | ||
61 | defaults: share, | ||
62 | transaction: t | ||
63 | }) | ||
64 | |||
65 | if (videoChannel.isOwned() && created === true) { | ||
66 | // Don't resend the activity to the sender | ||
67 | const exceptions = [ accountAnnouncer ] | ||
68 | await forwardActivity(activity, t, exceptions) | ||
69 | } | ||
70 | |||
71 | return undefined | ||
72 | }) | ||
73 | } | ||
74 | |||
75 | function processVideoShare (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) { | ||
76 | const options = { | ||
77 | arguments: [ accountAnnouncer, activity ], | ||
78 | errorMessage: 'Cannot share the video with many retries.' | ||
79 | } | ||
80 | |||
81 | return retryTransactionWrapper(shareVideo, options) | ||
82 | } | ||
83 | |||
84 | function shareVideo (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) { | ||
85 | const announcedActivity = activity.object as ActivityAdd | ||
86 | |||
87 | return db.sequelize.transaction(async t => { | ||
88 | // Add share entry | ||
89 | const video: VideoInstance = await processAddActivity(announcedActivity) | ||
90 | |||
91 | const share = { | ||
92 | accountId: accountAnnouncer.id, | ||
93 | videoId: video.id | ||
94 | } | ||
95 | |||
96 | const [ , created ] = await db.VideoShare.findOrCreate({ | ||
97 | where: share, | ||
98 | defaults: share, | ||
99 | transaction: t | ||
100 | }) | ||
101 | |||
102 | if (video.isOwned() && created === true) { | ||
103 | // Don't resend the activity to the sender | ||
104 | const exceptions = [ accountAnnouncer ] | ||
105 | await forwardActivity(activity, t, exceptions) | ||
106 | } | ||
107 | |||
108 | return undefined | ||
109 | }) | ||
110 | } | ||
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 1f982598b..c88082bbf 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -8,7 +8,7 @@ import { AccountInstance } from '../../../models/account/account-interface' | |||
8 | import { getOrCreateAccountAndServer } from '../account' | 8 | import { getOrCreateAccountAndServer } from '../account' |
9 | import { forwardActivity } from '../send/misc' | 9 | import { forwardActivity } from '../send/misc' |
10 | import { getVideoChannelActivityPubUrl } from '../url' | 10 | import { getVideoChannelActivityPubUrl } from '../url' |
11 | import { videoChannelActivityObjectToDBAttributes } from './misc' | 11 | import { addVideoChannelShares, videoChannelActivityObjectToDBAttributes } from './misc' |
12 | 12 | ||
13 | async function processCreateActivity (activity: ActivityCreate) { | 13 | async function processCreateActivity (activity: ActivityCreate) { |
14 | const activityObject = activity.object | 14 | const activityObject = activity.object |
@@ -92,13 +92,19 @@ async function processCreateView (byAccount: AccountInstance, activity: Activity | |||
92 | } | 92 | } |
93 | } | 93 | } |
94 | 94 | ||
95 | function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { | 95 | async function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { |
96 | const options = { | 96 | const options = { |
97 | arguments: [ account, videoChannelToCreateData ], | 97 | arguments: [ account, videoChannelToCreateData ], |
98 | errorMessage: 'Cannot insert the remote video channel with many retries.' | 98 | errorMessage: 'Cannot insert the remote video channel with many retries.' |
99 | } | 99 | } |
100 | 100 | ||
101 | return retryTransactionWrapper(addRemoteVideoChannel, options) | 101 | const videoChannel = await retryTransactionWrapper(addRemoteVideoChannel, options) |
102 | |||
103 | if (videoChannelToCreateData.shares && Array.isArray(videoChannelToCreateData.shares.orderedItems)) { | ||
104 | await addVideoChannelShares(videoChannel, videoChannelToCreateData.shares.orderedItems) | ||
105 | } | ||
106 | |||
107 | return videoChannel | ||
102 | } | 108 | } |
103 | 109 | ||
104 | function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { | 110 | function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) { |
diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts index 444c1cbd6..fd1add68e 100644 --- a/server/lib/activitypub/send/misc.ts +++ b/server/lib/activitypub/send/misc.ts | |||
@@ -1,13 +1,14 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { Activity } from '../../../../shared/models/activitypub/activity' | ||
2 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
3 | import { ACTIVITY_PUB, database as db } from '../../../initializers' | 4 | import { ACTIVITY_PUB, database as db } from '../../../initializers' |
4 | import { AccountInstance } from '../../../models/account/account-interface' | 5 | import { AccountInstance } from '../../../models/account/account-interface' |
6 | import { VideoChannelInstance } from '../../../models/index' | ||
7 | import { VideoInstance } from '../../../models/video/video-interface' | ||
5 | import { | 8 | import { |
6 | activitypubHttpJobScheduler, | 9 | activitypubHttpJobScheduler, |
7 | ActivityPubHttpPayload | 10 | ActivityPubHttpPayload |
8 | } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler' | 11 | } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler' |
9 | import { VideoInstance } from '../../../models/video/video-interface' | ||
10 | import { Activity } from '../../../../shared/models/activitypub/activity' | ||
11 | 12 | ||
12 | async function forwardActivity ( | 13 | async function forwardActivity ( |
13 | activity: Activity, | 14 | activity: Activity, |
@@ -85,9 +86,16 @@ function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo: | |||
85 | } | 86 | } |
86 | } | 87 | } |
87 | 88 | ||
88 | function getVideoFollowersAudience (accountsInvolvedInVideo: AccountInstance[]) { | 89 | function getOriginVideoChannelAudience (videoChannel: VideoChannelInstance, accountsInvolved: AccountInstance[]) { |
90 | return { | ||
91 | to: [ videoChannel.Account.url ], | ||
92 | cc: accountsInvolved.map(a => a.followersUrl) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | function getObjectFollowersAudience (accountsInvolvedInObject: AccountInstance[]) { | ||
89 | return { | 97 | return { |
90 | to: accountsInvolvedInVideo.map(a => a.followersUrl), | 98 | to: accountsInvolvedInObject.map(a => a.followersUrl), |
91 | cc: [] | 99 | cc: [] |
92 | } | 100 | } |
93 | } | 101 | } |
@@ -99,6 +107,13 @@ async function getAccountsInvolvedInVideo (video: VideoInstance) { | |||
99 | return accountsToForwardView | 107 | return accountsToForwardView |
100 | } | 108 | } |
101 | 109 | ||
110 | async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelInstance) { | ||
111 | const accountsToForwardView = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id) | ||
112 | accountsToForwardView.push(videoChannel.Account) | ||
113 | |||
114 | return accountsToForwardView | ||
115 | } | ||
116 | |||
102 | async function getAudience (accountSender: AccountInstance, isPublic = true) { | 117 | async function getAudience (accountSender: AccountInstance, isPublic = true) { |
103 | const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls() | 118 | const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls() |
104 | 119 | ||
@@ -131,10 +146,12 @@ async function computeFollowerUris (toAccountFollower: AccountInstance[], follow | |||
131 | 146 | ||
132 | export { | 147 | export { |
133 | broadcastToFollowers, | 148 | broadcastToFollowers, |
149 | getOriginVideoChannelAudience, | ||
134 | unicastTo, | 150 | unicastTo, |
135 | getAudience, | 151 | getAudience, |
136 | getOriginVideoAudience, | 152 | getOriginVideoAudience, |
137 | getAccountsInvolvedInVideo, | 153 | getAccountsInvolvedInVideo, |
138 | getVideoFollowersAudience, | 154 | getAccountsInvolvedInVideoChannel, |
155 | getObjectFollowersAudience, | ||
139 | forwardActivity | 156 | forwardActivity |
140 | } | 157 | } |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index b8ea51bc0..efc23af46 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -1,34 +1,96 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAdd } from '../../../../shared/index' | 2 | import { ActivityAdd } from '../../../../shared/index' |
3 | import { ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub/activity' | 3 | import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub/activity' |
4 | import { AccountInstance, VideoInstance } from '../../../models' | 4 | import { AccountInstance, VideoInstance } from '../../../models' |
5 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 5 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
6 | import { getAnnounceActivityPubUrl } from '../url' | 6 | import { getAnnounceActivityPubUrl } from '../url' |
7 | import { broadcastToFollowers } from './misc' | 7 | import { |
8 | broadcastToFollowers, | ||
9 | getAccountsInvolvedInVideo, | ||
10 | getAccountsInvolvedInVideoChannel, | ||
11 | getAudience, | ||
12 | getObjectFollowersAudience, | ||
13 | getOriginVideoAudience, | ||
14 | getOriginVideoChannelAudience, | ||
15 | unicastTo | ||
16 | } from './misc' | ||
8 | import { addActivityData } from './send-add' | 17 | import { addActivityData } from './send-add' |
9 | import { createActivityData } from './send-create' | 18 | import { createActivityData } from './send-create' |
10 | 19 | ||
11 | async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { | 20 | async function buildVideoAnnounceToFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { |
12 | const url = getAnnounceActivityPubUrl(video.url, byAccount) | 21 | const url = getAnnounceActivityPubUrl(video.url, byAccount) |
13 | 22 | ||
14 | const videoChannel = video.VideoChannel | 23 | const videoChannel = video.VideoChannel |
15 | const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject()) | 24 | const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject()) |
16 | 25 | ||
17 | const data = await announceActivityData(url, byAccount, announcedActivity) | 26 | const accountsToForwardView = await getAccountsInvolvedInVideo(video) |
27 | const audience = getObjectFollowersAudience(accountsToForwardView) | ||
28 | const data = await announceActivityData(url, byAccount, announcedActivity, audience) | ||
29 | |||
30 | return data | ||
31 | } | ||
32 | |||
33 | async function sendVideoAnnounceToFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { | ||
34 | const data = await buildVideoAnnounceToFollowers(byAccount, video, t) | ||
35 | |||
18 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) | 36 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) |
19 | } | 37 | } |
20 | 38 | ||
21 | async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) { | 39 | async function sendVideoAnnounceToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { |
40 | const url = getAnnounceActivityPubUrl(video.url, byAccount) | ||
41 | |||
42 | const videoChannel = video.VideoChannel | ||
43 | const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject()) | ||
44 | |||
45 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video) | ||
46 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | ||
47 | const data = await createActivityData(url, byAccount, announcedActivity, audience) | ||
48 | |||
49 | return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) | ||
50 | } | ||
51 | |||
52 | async function buildVideoChannelAnnounceToFollowers (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) { | ||
22 | const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) | 53 | const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) |
23 | const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject()) | 54 | const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject()) |
24 | 55 | ||
25 | const data = await announceActivityData(url, byAccount, announcedActivity) | 56 | const accountsToForwardView = await getAccountsInvolvedInVideoChannel(videoChannel) |
57 | const audience = getObjectFollowersAudience(accountsToForwardView) | ||
58 | const data = await announceActivityData(url, byAccount, announcedActivity, audience) | ||
59 | |||
60 | return data | ||
61 | } | ||
62 | |||
63 | async function sendVideoChannelAnnounceToFollowers (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) { | ||
64 | const data = await buildVideoChannelAnnounceToFollowers(byAccount, videoChannel, t) | ||
65 | |||
26 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) | 66 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) |
27 | } | 67 | } |
28 | 68 | ||
29 | async function announceActivityData (url: string, byAccount: AccountInstance, object: ActivityCreate | ActivityAdd) { | 69 | async function sendVideoChannelAnnounceToOrigin (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) { |
70 | const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) | ||
71 | const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject()) | ||
72 | |||
73 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideoChannel(videoChannel) | ||
74 | const audience = getOriginVideoChannelAudience(videoChannel, accountsInvolvedInVideo) | ||
75 | const data = await createActivityData(url, byAccount, announcedActivity, audience) | ||
76 | |||
77 | return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) | ||
78 | } | ||
79 | |||
80 | async function announceActivityData ( | ||
81 | url: string, | ||
82 | byAccount: AccountInstance, | ||
83 | object: ActivityCreate | ActivityAdd, | ||
84 | audience?: ActivityAudience | ||
85 | ) { | ||
86 | if (!audience) { | ||
87 | audience = await getAudience(byAccount) | ||
88 | } | ||
89 | |||
30 | const activity: ActivityAnnounce = { | 90 | const activity: ActivityAnnounce = { |
31 | type: 'Announce', | 91 | type: 'Announce', |
92 | to: audience.to, | ||
93 | cc: audience.cc, | ||
32 | id: url, | 94 | id: url, |
33 | actor: byAccount.url, | 95 | actor: byAccount.url, |
34 | object | 96 | object |
@@ -40,7 +102,11 @@ async function announceActivityData (url: string, byAccount: AccountInstance, ob | |||
40 | // --------------------------------------------------------------------------- | 102 | // --------------------------------------------------------------------------- |
41 | 103 | ||
42 | export { | 104 | export { |
43 | sendVideoAnnounce, | 105 | sendVideoAnnounceToFollowers, |
44 | sendVideoChannelAnnounce, | 106 | sendVideoChannelAnnounceToFollowers, |
45 | announceActivityData | 107 | sendVideoAnnounceToOrigin, |
108 | sendVideoChannelAnnounceToOrigin, | ||
109 | announceActivityData, | ||
110 | buildVideoAnnounceToFollowers, | ||
111 | buildVideoChannelAnnounceToFollowers | ||
46 | } | 112 | } |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 113d89233..bf66606c1 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | getAccountsInvolvedInVideo, | 9 | getAccountsInvolvedInVideo, |
10 | getAudience, | 10 | getAudience, |
11 | getOriginVideoAudience, | 11 | getOriginVideoAudience, |
12 | getVideoFollowersAudience, | 12 | getObjectFollowersAudience, |
13 | unicastTo | 13 | unicastTo |
14 | } from './misc' | 14 | } from './misc' |
15 | 15 | ||
@@ -47,7 +47,7 @@ async function sendCreateViewToVideoFollowers (byAccount: AccountInstance, video | |||
47 | const viewActivity = createViewActivityData(byAccount, video) | 47 | const viewActivity = createViewActivityData(byAccount, video) |
48 | 48 | ||
49 | const accountsToForwardView = await getAccountsInvolvedInVideo(video) | 49 | const accountsToForwardView = await getAccountsInvolvedInVideo(video) |
50 | const audience = getVideoFollowersAudience(accountsToForwardView) | 50 | const audience = getObjectFollowersAudience(accountsToForwardView) |
51 | const data = await createActivityData(url, byAccount, viewActivity, audience) | 51 | const data = await createActivityData(url, byAccount, viewActivity, audience) |
52 | 52 | ||
53 | // Use the server account to send the view, because it could be an unregistered account | 53 | // Use the server account to send the view, because it could be an unregistered account |
@@ -72,7 +72,7 @@ async function sendCreateDislikeToVideoFollowers (byAccount: AccountInstance, vi | |||
72 | const dislikeActivity = createDislikeActivityData(byAccount, video) | 72 | const dislikeActivity = createDislikeActivityData(byAccount, video) |
73 | 73 | ||
74 | const accountsToForwardView = await getAccountsInvolvedInVideo(video) | 74 | const accountsToForwardView = await getAccountsInvolvedInVideo(video) |
75 | const audience = getVideoFollowersAudience(accountsToForwardView) | 75 | const audience = getObjectFollowersAudience(accountsToForwardView) |
76 | const data = await createActivityData(url, byAccount, dislikeActivity, audience) | 76 | const data = await createActivityData(url, byAccount, dislikeActivity, audience) |
77 | 77 | ||
78 | const followersException = [ byAccount ] | 78 | const followersException = [ byAccount ] |
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 8ca775bf3..41b879b8a 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts | |||
@@ -7,7 +7,7 @@ import { | |||
7 | getAccountsInvolvedInVideo, | 7 | getAccountsInvolvedInVideo, |
8 | getAudience, | 8 | getAudience, |
9 | getOriginVideoAudience, | 9 | getOriginVideoAudience, |
10 | getVideoFollowersAudience, | 10 | getObjectFollowersAudience, |
11 | unicastTo | 11 | unicastTo |
12 | } from './misc' | 12 | } from './misc' |
13 | 13 | ||
@@ -25,7 +25,7 @@ async function sendLikeToVideoFollowers (byAccount: AccountInstance, video: Vide | |||
25 | const url = getVideoLikeActivityPubUrl(byAccount, video) | 25 | const url = getVideoLikeActivityPubUrl(byAccount, video) |
26 | 26 | ||
27 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video) | 27 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video) |
28 | const audience = getVideoFollowersAudience(accountsInvolvedInVideo) | 28 | const audience = getObjectFollowersAudience(accountsInvolvedInVideo) |
29 | const data = await likeActivityData(url, byAccount, video, audience) | 29 | const data = await likeActivityData(url, byAccount, video, audience) |
30 | 30 | ||
31 | const toAccountsFollowers = await getAccountsInvolvedInVideo(video) | 31 | const toAccountsFollowers = await getAccountsInvolvedInVideo(video) |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 79fc113f0..9b732df40 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -10,7 +10,7 @@ import { AccountInstance } from '../../../models' | |||
10 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 10 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
11 | import { VideoInstance } from '../../../models/video/video-interface' | 11 | import { VideoInstance } from '../../../models/video/video-interface' |
12 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' | 12 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' |
13 | import { broadcastToFollowers, getAccountsInvolvedInVideo, getAudience, getVideoFollowersAudience, unicastTo } from './misc' | 13 | import { broadcastToFollowers, getAccountsInvolvedInVideo, getAudience, getObjectFollowersAudience, unicastTo } from './misc' |
14 | import { createActivityData, createDislikeActivityData } from './send-create' | 14 | import { createActivityData, createDislikeActivityData } from './send-create' |
15 | import { followActivityData } from './send-follow' | 15 | import { followActivityData } from './send-follow' |
16 | import { likeActivityData } from './send-like' | 16 | import { likeActivityData } from './send-like' |
@@ -43,7 +43,7 @@ async function sendUndoLikeToVideoFollowers (byAccount: AccountInstance, video: | |||
43 | const undoUrl = getUndoActivityPubUrl(likeUrl) | 43 | const undoUrl = getUndoActivityPubUrl(likeUrl) |
44 | 44 | ||
45 | const toAccountsFollowers = await getAccountsInvolvedInVideo(video) | 45 | const toAccountsFollowers = await getAccountsInvolvedInVideo(video) |
46 | const audience = getVideoFollowersAudience(toAccountsFollowers) | 46 | const audience = getObjectFollowersAudience(toAccountsFollowers) |
47 | const object = await likeActivityData(likeUrl, byAccount, video) | 47 | const object = await likeActivityData(likeUrl, byAccount, video) |
48 | const data = await undoActivityData(undoUrl, byAccount, object, audience) | 48 | const data = await undoActivityData(undoUrl, byAccount, object, audience) |
49 | 49 | ||
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 689e200a6..e14b0f50c 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts | |||
@@ -3,7 +3,7 @@ import { getServerAccount } from '../../helpers/utils' | |||
3 | import { database as db } from '../../initializers' | 3 | import { database as db } from '../../initializers' |
4 | import { VideoChannelInstance } from '../../models/index' | 4 | import { VideoChannelInstance } from '../../models/index' |
5 | import { VideoInstance } from '../../models/video/video-interface' | 5 | import { VideoInstance } from '../../models/video/video-interface' |
6 | import { sendVideoAnnounce, sendVideoChannelAnnounce } from './send/send-announce' | 6 | import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send/send-announce' |
7 | 7 | ||
8 | async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) { | 8 | async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) { |
9 | const serverAccount = await getServerAccount() | 9 | const serverAccount = await getServerAccount() |
@@ -13,7 +13,7 @@ async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: | |||
13 | videoChannelId: videoChannel.id | 13 | videoChannelId: videoChannel.id |
14 | }, { transaction: t }) | 14 | }, { transaction: t }) |
15 | 15 | ||
16 | return sendVideoChannelAnnounce(serverAccount, videoChannel, t) | 16 | return sendVideoChannelAnnounceToFollowers(serverAccount, videoChannel, t) |
17 | } | 17 | } |
18 | 18 | ||
19 | async function shareVideoByServer (video: VideoInstance, t: Transaction) { | 19 | async function shareVideoByServer (video: VideoInstance, t: Transaction) { |
@@ -24,7 +24,7 @@ async function shareVideoByServer (video: VideoInstance, t: Transaction) { | |||
24 | videoId: video.id | 24 | videoId: video.id |
25 | }, { transaction: t }) | 25 | }, { transaction: t }) |
26 | 26 | ||
27 | return sendVideoAnnounce(serverAccount, video, t) | 27 | return sendVideoAnnounceToFollowers(serverAccount, video, t) |
28 | } | 28 | } |
29 | 29 | ||
30 | export { | 30 | export { |
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index 17395a99b..6475c4218 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts | |||
@@ -22,37 +22,37 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) { | |||
22 | } | 22 | } |
23 | 23 | ||
24 | function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { | 24 | function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { |
25 | return video.url + '#views/' + byAccount.uuid + '/' + new Date().toISOString() | 25 | return video.url + '/views/' + byAccount.uuid + '/' + new Date().toISOString() |
26 | } | 26 | } |
27 | 27 | ||
28 | function getVideoLikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { | 28 | function getVideoLikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { |
29 | return byAccount.url + '#likes/' + video.id | 29 | return byAccount.url + '/likes/' + video.id |
30 | } | 30 | } |
31 | 31 | ||
32 | function getVideoDislikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { | 32 | function getVideoDislikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) { |
33 | return byAccount.url + '#dislikes/' + video.id | 33 | return byAccount.url + '/dislikes/' + video.id |
34 | } | 34 | } |
35 | 35 | ||
36 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { | 36 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { |
37 | const me = accountFollow.AccountFollower | 37 | const me = accountFollow.AccountFollower |
38 | const following = accountFollow.AccountFollowing | 38 | const following = accountFollow.AccountFollowing |
39 | 39 | ||
40 | return me.url + '#follows/' + following.id | 40 | return me.url + '/follows/' + following.id |
41 | } | 41 | } |
42 | 42 | ||
43 | function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) { | 43 | function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) { |
44 | const follower = accountFollow.AccountFollower | 44 | const follower = accountFollow.AccountFollower |
45 | const me = accountFollow.AccountFollowing | 45 | const me = accountFollow.AccountFollowing |
46 | 46 | ||
47 | return follower.url + '#accepts/follows/' + me.id | 47 | return follower.url + '/accepts/follows/' + me.id |
48 | } | 48 | } |
49 | 49 | ||
50 | function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) { | 50 | function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) { |
51 | return originalUrl + '#announces/' + byAccount.id | 51 | return originalUrl + '/announces/' + byAccount.id |
52 | } | 52 | } |
53 | 53 | ||
54 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { | 54 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { |
55 | return originalUrl + '#updates/' + updatedAt | 55 | return originalUrl + '/updates/' + updatedAt |
56 | } | 56 | } |
57 | 57 | ||
58 | function getUndoActivityPubUrl (originalUrl: string) { | 58 | function getUndoActivityPubUrl (originalUrl: string) { |
diff --git a/server/middlewares/async.ts b/server/middlewares/async.ts index 29ebd169d..9692f9be7 100644 --- a/server/middlewares/async.ts +++ b/server/middlewares/async.ts | |||
@@ -1,10 +1,18 @@ | |||
1 | import { Request, Response, NextFunction } from 'express' | 1 | import { Request, Response, NextFunction, RequestHandler } from 'express' |
2 | import { eachSeries } from 'async' | ||
2 | 3 | ||
3 | // Syntactic sugar to avoid try/catch in express controllers | 4 | // Syntactic sugar to avoid try/catch in express controllers |
4 | // Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 | 5 | // Thanks: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 |
5 | function asyncMiddleware (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) { | 6 | function asyncMiddleware (fun: RequestHandler | RequestHandler[]) { |
6 | return (req: Request, res: Response, next: NextFunction) => { | 7 | return (req: Request, res: Response, next: NextFunction) => { |
7 | return Promise.resolve(fn(req, res, next)) | 8 | if (Array.isArray(fun) === true) { |
9 | return eachSeries(fun as RequestHandler[], (f, cb) => { | ||
10 | Promise.resolve(f(req, res, cb)) | ||
11 | .catch(next) | ||
12 | }, next) | ||
13 | } | ||
14 | |||
15 | return Promise.resolve((fun as RequestHandler)(req, res, next)) | ||
8 | .catch(next) | 16 | .catch(next) |
9 | } | 17 | } |
10 | } | 18 | } |
diff --git a/server/middlewares/validators/account.ts b/server/middlewares/validators/account.ts index 07ae76b63..47ed6a7bb 100644 --- a/server/middlewares/validators/account.ts +++ b/server/middlewares/validators/account.ts | |||
@@ -1,9 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { param } from 'express-validator/check' | 2 | import { param } from 'express-validator/check' |
3 | import { logger } from '../../helpers' | 3 | import { logger } from '../../helpers' |
4 | import { isAccountNameValid } from '../../helpers/custom-validators/accounts' | 4 | import { checkLocalAccountNameExists, isAccountNameValid } from '../../helpers/custom-validators/accounts' |
5 | import { database as db } from '../../initializers/database' | ||
6 | import { AccountInstance } from '../../models' | ||
7 | import { checkErrors } from './utils' | 5 | import { checkErrors } from './utils' |
8 | 6 | ||
9 | const localAccountValidator = [ | 7 | const localAccountValidator = [ |
@@ -13,7 +11,7 @@ const localAccountValidator = [ | |||
13 | logger.debug('Checking localAccountValidator parameters', { parameters: req.params }) | 11 | logger.debug('Checking localAccountValidator parameters', { parameters: req.params }) |
14 | 12 | ||
15 | checkErrors(req, res, () => { | 13 | checkErrors(req, res, () => { |
16 | checkLocalAccountExists(req.params.name, res, next) | 14 | checkLocalAccountNameExists(req.params.name, res, next) |
17 | }) | 15 | }) |
18 | } | 16 | } |
19 | ] | 17 | ] |
@@ -23,23 +21,3 @@ const localAccountValidator = [ | |||
23 | export { | 21 | export { |
24 | localAccountValidator | 22 | localAccountValidator |
25 | } | 23 | } |
26 | |||
27 | // --------------------------------------------------------------------------- | ||
28 | |||
29 | function checkLocalAccountExists (name: string, res: express.Response, callback: (err: Error, account: AccountInstance) => void) { | ||
30 | db.Account.loadLocalByName(name) | ||
31 | .then(account => { | ||
32 | if (!account) { | ||
33 | return res.status(404) | ||
34 | .send({ error: 'Account not found' }) | ||
35 | .end() | ||
36 | } | ||
37 | |||
38 | res.locals.account = account | ||
39 | return callback(null, account) | ||
40 | }) | ||
41 | .catch(err => { | ||
42 | logger.error('Error in account request validator.', err) | ||
43 | return res.sendStatus(500) | ||
44 | }) | ||
45 | } | ||
diff --git a/server/middlewares/validators/utils.ts b/server/middlewares/validators/utils.ts index ea107bbe8..77a1a0d4b 100644 --- a/server/middlewares/validators/utils.ts +++ b/server/middlewares/validators/utils.ts | |||
@@ -14,8 +14,22 @@ function checkErrors (req: express.Request, res: express.Response, next: express | |||
14 | return next() | 14 | return next() |
15 | } | 15 | } |
16 | 16 | ||
17 | function areValidationErrors (req: express.Request, res: express.Response) { | ||
18 | const errors = validationResult(req) | ||
19 | |||
20 | if (!errors.isEmpty()) { | ||
21 | logger.warn('Incorrect request parameters', { path: req.originalUrl, err: errors.mapped() }) | ||
22 | res.status(400).json({ errors: errors.mapped() }) | ||
23 | |||
24 | return true | ||
25 | } | ||
26 | |||
27 | return false | ||
28 | } | ||
29 | |||
17 | // --------------------------------------------------------------------------- | 30 | // --------------------------------------------------------------------------- |
18 | 31 | ||
19 | export { | 32 | export { |
20 | checkErrors | 33 | checkErrors, |
34 | areValidationErrors | ||
21 | } | 35 | } |
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts index c6fd3b59d..f30fbf0dc 100644 --- a/server/middlewares/validators/video-channels.ts +++ b/server/middlewares/validators/video-channels.ts | |||
@@ -1,13 +1,19 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param } from 'express-validator/check' | 2 | import { body, param } from 'express-validator/check' |
3 | import { UserRight } from '../../../shared' | 3 | import { UserRight } from '../../../shared' |
4 | import { checkVideoAccountExists } from '../../helpers/custom-validators/accounts' | 4 | import { checkAccountIdExists } from '../../helpers/custom-validators/accounts' |
5 | import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' | 5 | import { isIdValid } from '../../helpers/custom-validators/misc' |
6 | import { checkVideoChannelExists, isIdOrUUIDValid } from '../../helpers/index' | 6 | import { |
7 | checkVideoChannelExists, | ||
8 | isVideoChannelDescriptionValid, | ||
9 | isVideoChannelExistsPromise, | ||
10 | isVideoChannelNameValid | ||
11 | } from '../../helpers/custom-validators/video-channels' | ||
12 | import { isIdOrUUIDValid } from '../../helpers/index' | ||
7 | import { logger } from '../../helpers/logger' | 13 | import { logger } from '../../helpers/logger' |
8 | import { database as db } from '../../initializers' | 14 | import { database as db } from '../../initializers' |
9 | import { UserInstance } from '../../models' | 15 | import { UserInstance } from '../../models' |
10 | import { checkErrors } from './utils' | 16 | import { areValidationErrors, checkErrors } from './utils' |
11 | 17 | ||
12 | const listVideoAccountChannelsValidator = [ | 18 | const listVideoAccountChannelsValidator = [ |
13 | param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), | 19 | param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'), |
@@ -16,7 +22,7 @@ const listVideoAccountChannelsValidator = [ | |||
16 | logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body }) | 22 | logger.debug('Checking listVideoAccountChannelsValidator parameters', { parameters: req.body }) |
17 | 23 | ||
18 | checkErrors(req, res, () => { | 24 | checkErrors(req, res, () => { |
19 | checkVideoAccountExists(req.params.accountId, res, next) | 25 | checkAccountIdExists(req.params.accountId, res, next) |
20 | }) | 26 | }) |
21 | } | 27 | } |
22 | ] | 28 | ] |
@@ -90,6 +96,28 @@ const videoChannelsGetValidator = [ | |||
90 | } | 96 | } |
91 | ] | 97 | ] |
92 | 98 | ||
99 | const videoChannelsShareValidator = [ | ||
100 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
101 | param('accountId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid account id'), | ||
102 | |||
103 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
104 | logger.debug('Checking videoChannelShare parameters', { parameters: req.params }) | ||
105 | |||
106 | if (areValidationErrors(req, res)) return | ||
107 | if (!await isVideoChannelExistsPromise(req.params.id, res)) return | ||
108 | |||
109 | const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId) | ||
110 | if (!share) { | ||
111 | return res.status(404) | ||
112 | .end() | ||
113 | } | ||
114 | |||
115 | res.locals.videoChannelShare = share | ||
116 | |||
117 | return next() | ||
118 | } | ||
119 | ] | ||
120 | |||
93 | // --------------------------------------------------------------------------- | 121 | // --------------------------------------------------------------------------- |
94 | 122 | ||
95 | export { | 123 | export { |
@@ -97,7 +125,8 @@ export { | |||
97 | videoChannelsAddValidator, | 125 | videoChannelsAddValidator, |
98 | videoChannelsUpdateValidator, | 126 | videoChannelsUpdateValidator, |
99 | videoChannelsRemoveValidator, | 127 | videoChannelsRemoveValidator, |
100 | videoChannelsGetValidator | 128 | videoChannelsGetValidator, |
129 | videoChannelsShareValidator | ||
101 | } | 130 | } |
102 | 131 | ||
103 | // --------------------------------------------------------------------------- | 132 | // --------------------------------------------------------------------------- |
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index df0eb7b96..5ffc85210 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts | |||
@@ -24,7 +24,8 @@ import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' | |||
24 | import { database as db } from '../../initializers/database' | 24 | import { database as db } from '../../initializers/database' |
25 | import { UserInstance } from '../../models/account/user-interface' | 25 | import { UserInstance } from '../../models/account/user-interface' |
26 | import { authenticate } from '../oauth' | 26 | import { authenticate } from '../oauth' |
27 | import { checkErrors } from './utils' | 27 | import { areValidationErrors, checkErrors } from './utils' |
28 | import { isVideoExistsPromise } from '../../helpers/index' | ||
28 | 29 | ||
29 | const videosAddValidator = [ | 30 | const videosAddValidator = [ |
30 | body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( | 31 | body('videofile').custom((value, { req }) => isVideoFile(req.files)).withMessage( |
@@ -230,6 +231,28 @@ const videoRateValidator = [ | |||
230 | } | 231 | } |
231 | ] | 232 | ] |
232 | 233 | ||
234 | const videosShareValidator = [ | ||
235 | param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), | ||
236 | param('accountId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid account id'), | ||
237 | |||
238 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
239 | logger.debug('Checking videoShare parameters', { parameters: req.params }) | ||
240 | |||
241 | if (areValidationErrors(req, res)) return | ||
242 | if (!await isVideoExistsPromise(req.params.id, res)) return | ||
243 | |||
244 | const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id) | ||
245 | if (!share) { | ||
246 | return res.status(404) | ||
247 | .end() | ||
248 | } | ||
249 | |||
250 | res.locals.videoShare = share | ||
251 | |||
252 | return next() | ||
253 | } | ||
254 | ] | ||
255 | |||
233 | // --------------------------------------------------------------------------- | 256 | // --------------------------------------------------------------------------- |
234 | 257 | ||
235 | export { | 258 | export { |
@@ -238,6 +261,7 @@ export { | |||
238 | videosGetValidator, | 261 | videosGetValidator, |
239 | videosRemoveValidator, | 262 | videosRemoveValidator, |
240 | videosSearchValidator, | 263 | videosSearchValidator, |
264 | videosShareValidator, | ||
241 | 265 | ||
242 | videoAbuseReportValidator, | 266 | videoAbuseReportValidator, |
243 | 267 | ||
diff --git a/server/models/video/video-channel-interface.ts b/server/models/video/video-channel-interface.ts index b409d1db3..21f81e901 100644 --- a/server/models/video/video-channel-interface.ts +++ b/server/models/video/video-channel-interface.ts | |||
@@ -6,6 +6,7 @@ import { VideoChannelObject } from '../../../shared/models/activitypub/objects/v | |||
6 | import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model' | 6 | import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model' |
7 | import { AccountInstance } from '../account/account-interface' | 7 | import { AccountInstance } from '../account/account-interface' |
8 | import { VideoInstance } from './video-interface' | 8 | import { VideoInstance } from './video-interface' |
9 | import { VideoChannelShareInstance } from './video-channel-share-interface' | ||
9 | 10 | ||
10 | export namespace VideoChannelMethods { | 11 | export namespace VideoChannelMethods { |
11 | export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel | 12 | export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel |
@@ -47,6 +48,7 @@ export interface VideoChannelAttributes { | |||
47 | 48 | ||
48 | Account?: AccountInstance | 49 | Account?: AccountInstance |
49 | Videos?: VideoInstance[] | 50 | Videos?: VideoInstance[] |
51 | VideoChannelShares?: VideoChannelShareInstance[] | ||
50 | } | 52 | } |
51 | 53 | ||
52 | export interface VideoChannelInstance extends VideoChannelClass, VideoChannelAttributes, Sequelize.Instance<VideoChannelAttributes> { | 54 | export interface VideoChannelInstance extends VideoChannelClass, VideoChannelAttributes, Sequelize.Instance<VideoChannelAttributes> { |
diff --git a/server/models/video/video-channel-share-interface.ts b/server/models/video/video-channel-share-interface.ts index 8bb531af2..bcb3a0e24 100644 --- a/server/models/video/video-channel-share-interface.ts +++ b/server/models/video/video-channel-share-interface.ts | |||
@@ -5,10 +5,12 @@ import { VideoChannelInstance } from './video-channel-interface' | |||
5 | 5 | ||
6 | export namespace VideoChannelShareMethods { | 6 | export namespace VideoChannelShareMethods { |
7 | export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]> | 7 | export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]> |
8 | export type Load = (accountId: number, videoId: number) => Bluebird<VideoChannelShareInstance> | ||
8 | } | 9 | } |
9 | 10 | ||
10 | export interface VideoChannelShareClass { | 11 | export interface VideoChannelShareClass { |
11 | loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare | 12 | loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare |
13 | load: VideoChannelShareMethods.Load | ||
12 | } | 14 | } |
13 | 15 | ||
14 | export interface VideoChannelShareAttributes { | 16 | export interface VideoChannelShareAttributes { |
diff --git a/server/models/video/video-channel-share.ts b/server/models/video/video-channel-share.ts index 01f84c806..e47c0dae7 100644 --- a/server/models/video/video-channel-share.ts +++ b/server/models/video/video-channel-share.ts | |||
@@ -5,6 +5,7 @@ import { VideoChannelShareAttributes, VideoChannelShareInstance, VideoChannelSha | |||
5 | 5 | ||
6 | let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes> | 6 | let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes> |
7 | let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare | 7 | let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare |
8 | let load: VideoChannelShareMethods.Load | ||
8 | 9 | ||
9 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | 10 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { |
10 | VideoChannelShare = sequelize.define<VideoChannelShareInstance, VideoChannelShareAttributes>('VideoChannelShare', | 11 | VideoChannelShare = sequelize.define<VideoChannelShareInstance, VideoChannelShareAttributes>('VideoChannelShare', |
@@ -23,6 +24,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
23 | 24 | ||
24 | const classMethods = [ | 25 | const classMethods = [ |
25 | associate, | 26 | associate, |
27 | load, | ||
26 | loadAccountsByShare | 28 | loadAccountsByShare |
27 | ] | 29 | ] |
28 | addMethodsToModel(VideoChannelShare, classMethods) | 30 | addMethodsToModel(VideoChannelShare, classMethods) |
@@ -50,6 +52,19 @@ function associate (models) { | |||
50 | }) | 52 | }) |
51 | } | 53 | } |
52 | 54 | ||
55 | load = function (accountId: number, videoChannelId: number) { | ||
56 | return VideoChannelShare.findOne({ | ||
57 | where: { | ||
58 | accountId, | ||
59 | videoChannelId | ||
60 | }, | ||
61 | include: [ | ||
62 | VideoChannelShare['sequelize'].models.Account, | ||
63 | VideoChannelShare['sequelize'].models.VideoChannel | ||
64 | ] | ||
65 | }) | ||
66 | } | ||
67 | |||
53 | loadAccountsByShare = function (videoChannelId: number) { | 68 | loadAccountsByShare = function (videoChannelId: number) { |
54 | const query = { | 69 | const query = { |
55 | where: { | 70 | where: { |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 64130310d..e11268b2c 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -6,6 +6,8 @@ import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete' | |||
6 | 6 | ||
7 | import { addMethodsToModel, getSort } from '../utils' | 7 | import { addMethodsToModel, getSort } from '../utils' |
8 | import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface' | 8 | import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface' |
9 | import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url' | ||
10 | import { activityPubCollection } from '../../helpers/activitypub' | ||
9 | 11 | ||
10 | let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> | 12 | let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> |
11 | let toFormattedJSON: VideoChannelMethods.ToFormattedJSON | 13 | let toFormattedJSON: VideoChannelMethods.ToFormattedJSON |
@@ -139,6 +141,18 @@ toFormattedJSON = function (this: VideoChannelInstance) { | |||
139 | } | 141 | } |
140 | 142 | ||
141 | toActivityPubObject = function (this: VideoChannelInstance) { | 143 | toActivityPubObject = function (this: VideoChannelInstance) { |
144 | let sharesObject | ||
145 | if (Array.isArray(this.VideoChannelShares)) { | ||
146 | const shares: string[] = [] | ||
147 | |||
148 | for (const videoChannelShare of this.VideoChannelShares) { | ||
149 | const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account) | ||
150 | shares.push(shareUrl) | ||
151 | } | ||
152 | |||
153 | sharesObject = activityPubCollection(shares) | ||
154 | } | ||
155 | |||
142 | const json = { | 156 | const json = { |
143 | type: 'VideoChannel' as 'VideoChannel', | 157 | type: 'VideoChannel' as 'VideoChannel', |
144 | id: this.url, | 158 | id: this.url, |
@@ -146,7 +160,8 @@ toActivityPubObject = function (this: VideoChannelInstance) { | |||
146 | content: this.description, | 160 | content: this.description, |
147 | name: this.name, | 161 | name: this.name, |
148 | published: this.createdAt.toISOString(), | 162 | published: this.createdAt.toISOString(), |
149 | updated: this.updatedAt.toISOString() | 163 | updated: this.updatedAt.toISOString(), |
164 | shares: sharesObject | ||
150 | } | 165 | } |
151 | 166 | ||
152 | return json | 167 | return json |
diff --git a/server/models/video/video-share-interface.ts b/server/models/video/video-share-interface.ts index 569568842..ad23444b6 100644 --- a/server/models/video/video-share-interface.ts +++ b/server/models/video/video-share-interface.ts | |||
@@ -1,14 +1,16 @@ | |||
1 | import * as Bluebird from 'bluebird' | ||
1 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
2 | import { AccountInstance } from '../account/account-interface' | 3 | import { AccountInstance } from '../account/account-interface' |
3 | import { VideoInstance } from './video-interface' | 4 | import { VideoInstance } from './video-interface' |
4 | import * as Bluebird from 'bluebird' | ||
5 | 5 | ||
6 | export namespace VideoShareMethods { | 6 | export namespace VideoShareMethods { |
7 | export type LoadAccountsByShare = (videoChannelId: number) => Bluebird<AccountInstance[]> | 7 | export type LoadAccountsByShare = (videoId: number) => Bluebird<AccountInstance[]> |
8 | export type Load = (accountId: number, videoId: number) => Bluebird<VideoShareInstance> | ||
8 | } | 9 | } |
9 | 10 | ||
10 | export interface VideoShareClass { | 11 | export interface VideoShareClass { |
11 | loadAccountsByShare: VideoShareMethods.LoadAccountsByShare | 12 | loadAccountsByShare: VideoShareMethods.LoadAccountsByShare |
13 | load: VideoShareMethods.Load | ||
12 | } | 14 | } |
13 | 15 | ||
14 | export interface VideoShareAttributes { | 16 | export interface VideoShareAttributes { |
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts index 22ac31a4a..fe5d56d42 100644 --- a/server/models/video/video-share.ts +++ b/server/models/video/video-share.ts | |||
@@ -5,6 +5,7 @@ import { VideoShareAttributes, VideoShareInstance, VideoShareMethods } from './v | |||
5 | 5 | ||
6 | let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes> | 6 | let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes> |
7 | let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare | 7 | let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare |
8 | let load: VideoShareMethods.Load | ||
8 | 9 | ||
9 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | 10 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { |
10 | VideoShare = sequelize.define<VideoShareInstance, VideoShareAttributes>('VideoShare', | 11 | VideoShare = sequelize.define<VideoShareInstance, VideoShareAttributes>('VideoShare', |
@@ -23,7 +24,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
23 | 24 | ||
24 | const classMethods = [ | 25 | const classMethods = [ |
25 | associate, | 26 | associate, |
26 | loadAccountsByShare | 27 | loadAccountsByShare, |
28 | load | ||
27 | ] | 29 | ] |
28 | addMethodsToModel(VideoShare, classMethods) | 30 | addMethodsToModel(VideoShare, classMethods) |
29 | 31 | ||
@@ -50,6 +52,18 @@ function associate (models) { | |||
50 | }) | 52 | }) |
51 | } | 53 | } |
52 | 54 | ||
55 | load = function (accountId: number, videoId: number) { | ||
56 | return VideoShare.findOne({ | ||
57 | where: { | ||
58 | accountId, | ||
59 | videoId | ||
60 | }, | ||
61 | include: [ | ||
62 | VideoShare['sequelize'].models.Account | ||
63 | ] | ||
64 | }) | ||
65 | } | ||
66 | |||
53 | loadAccountsByShare = function (videoId: number) { | 67 | loadAccountsByShare = function (videoId: number) { |
54 | const query = { | 68 | const query = { |
55 | where: { | 69 | where: { |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 457bfce77..4956b57ee 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -32,6 +32,7 @@ import { isVideoNameValid, isVideoLicenceValid, isVideoNSFWValid, isVideoDescrip | |||
32 | import { logger } from '../../helpers/logger' | 32 | import { logger } from '../../helpers/logger' |
33 | import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils' | 33 | import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils' |
34 | import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils' | 34 | import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils' |
35 | import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url' | ||
35 | 36 | ||
36 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> | 37 | let Video: Sequelize.Model<VideoInstance, VideoAttributes> |
37 | let getOriginalFile: VideoMethods.GetOriginalFile | 38 | let getOriginalFile: VideoMethods.GetOriginalFile |
@@ -573,6 +574,18 @@ toActivityPubObject = function (this: VideoInstance) { | |||
573 | dislikesObject = activityPubCollection(dislikes) | 574 | dislikesObject = activityPubCollection(dislikes) |
574 | } | 575 | } |
575 | 576 | ||
577 | let sharesObject | ||
578 | if (Array.isArray(this.VideoShares)) { | ||
579 | const shares: string[] = [] | ||
580 | |||
581 | for (const videoShare of this.VideoShares) { | ||
582 | const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Account) | ||
583 | shares.push(shareUrl) | ||
584 | } | ||
585 | |||
586 | sharesObject = activityPubCollection(shares) | ||
587 | } | ||
588 | |||
576 | const url = [] | 589 | const url = [] |
577 | for (const file of this.VideoFiles) { | 590 | for (const file of this.VideoFiles) { |
578 | url.push({ | 591 | url.push({ |
@@ -630,7 +643,8 @@ toActivityPubObject = function (this: VideoInstance) { | |||
630 | }, | 643 | }, |
631 | url, | 644 | url, |
632 | likes: likesObject, | 645 | likes: likesObject, |
633 | dislikes: dislikesObject | 646 | dislikes: dislikesObject, |
647 | shares: sharesObject | ||
634 | } | 648 | } |
635 | 649 | ||
636 | return videoObject | 650 | return videoObject |
@@ -823,7 +837,8 @@ listAllAndSharedByAccountForOutbox = function (accountId: number, start: number, | |||
823 | accountId | 837 | accountId |
824 | } | 838 | } |
825 | ] | 839 | ] |
826 | } | 840 | }, |
841 | include: [ Video['sequelize'].models.Account ] | ||
827 | }, | 842 | }, |
828 | { | 843 | { |
829 | model: Video['sequelize'].models.VideoChannel, | 844 | model: Video['sequelize'].models.VideoChannel, |
diff --git a/shared/models/activitypub/objects/video-channel-object.ts b/shared/models/activitypub/objects/video-channel-object.ts index c9325b5df..dcce8696b 100644 --- a/shared/models/activitypub/objects/video-channel-object.ts +++ b/shared/models/activitypub/objects/video-channel-object.ts | |||
@@ -1,3 +1,5 @@ | |||
1 | import { ActivityPubOrderedCollection } from '../activitypub-ordered-collection' | ||
2 | |||
1 | export interface VideoChannelObject { | 3 | export interface VideoChannelObject { |
2 | type: 'VideoChannel' | 4 | type: 'VideoChannel' |
3 | id: string | 5 | id: string |
@@ -7,4 +9,5 @@ export interface VideoChannelObject { | |||
7 | published: string | 9 | published: string |
8 | updated: string | 10 | updated: string |
9 | actor?: string | 11 | actor?: string |
12 | shares?: ActivityPubOrderedCollection<string> | ||
10 | } | 13 | } |
diff --git a/shared/models/activitypub/objects/video-torrent-object.ts b/shared/models/activitypub/objects/video-torrent-object.ts index a4e032d04..a15ec7142 100644 --- a/shared/models/activitypub/objects/video-torrent-object.ts +++ b/shared/models/activitypub/objects/video-torrent-object.ts | |||
@@ -27,4 +27,5 @@ export interface VideoTorrentObject { | |||
27 | actor?: string | 27 | actor?: string |
28 | likes?: ActivityPubOrderedCollection<string> | 28 | likes?: ActivityPubOrderedCollection<string> |
29 | dislikes?: ActivityPubOrderedCollection<string> | 29 | dislikes?: ActivityPubOrderedCollection<string> |
30 | shares?: ActivityPubOrderedCollection<string> | ||
30 | } | 31 | } |