aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/activitypub/inbox.ts2
-rw-r--r--server/helpers/activitypub.ts9
-rw-r--r--server/lib/activitypub/process/process-add.ts51
-rw-r--r--server/lib/activitypub/process/process.ts6
-rw-r--r--server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts2
-rw-r--r--server/models/account/account-video-rate-interface.ts3
-rw-r--r--server/models/video/video-interface.ts3
-rw-r--r--server/models/video/video.ts48
8 files changed, 115 insertions, 9 deletions
diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts
index 30e7f706b..243ae7381 100644
--- a/server/controllers/activitypub/inbox.ts
+++ b/server/controllers/activitypub/inbox.ts
@@ -38,7 +38,7 @@ async function inboxController (req: express.Request, res: express.Response, nex
38 if ([ 'Collection', 'CollectionPage' ].indexOf(rootActivity.type) !== -1) { 38 if ([ 'Collection', 'CollectionPage' ].indexOf(rootActivity.type) !== -1) {
39 activities = (rootActivity as ActivityPubCollection).items 39 activities = (rootActivity as ActivityPubCollection).items
40 } else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].indexOf(rootActivity.type) !== -1) { 40 } else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].indexOf(rootActivity.type) !== -1) {
41 activities = (rootActivity as ActivityPubOrderedCollection).orderedItems 41 activities = (rootActivity as ActivityPubOrderedCollection<Activity>).orderedItems
42 } else { 42 } else {
43 activities = [ rootActivity as Activity ] 43 activities = [ rootActivity as Activity ]
44 } 44 }
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts
index 54c460200..1ea6422ca 100644
--- a/server/helpers/activitypub.ts
+++ b/server/helpers/activitypub.ts
@@ -24,6 +24,14 @@ function activityPubContextify <T> (data: T) {
24 }) 24 })
25} 25}
26 26
27function activityPubCollection (results: any[]) {
28 return {
29 type: 'OrderedCollection',
30 totalItems: results.length,
31 orderedItems: results
32 }
33}
34
27function activityPubCollectionPagination (url: string, page: any, result: ResultList<any>) { 35function activityPubCollectionPagination (url: string, page: any, result: ResultList<any>) {
28 let next: string 36 let next: string
29 let prev: string 37 let prev: string
@@ -74,5 +82,6 @@ function buildSignedActivity (byAccount: AccountInstance, data: Object) {
74export { 82export {
75 activityPubContextify, 83 activityPubContextify,
76 activityPubCollectionPagination, 84 activityPubCollectionPagination,
85 activityPubCollection,
77 buildSignedActivity 86 buildSignedActivity
78} 87}
diff --git a/server/lib/activitypub/process/process-add.ts b/server/lib/activitypub/process/process-add.ts
index 332c18cc0..433e68eb6 100644
--- a/server/lib/activitypub/process/process-add.ts
+++ b/server/lib/activitypub/process/process-add.ts
@@ -1,11 +1,13 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { VideoTorrentObject } from '../../../../shared' 2import { VideoTorrentObject } from '../../../../shared'
3import { ActivityAdd } from '../../../../shared/models/activitypub/activity' 3import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
4import { VideoRateType } from '../../../../shared/models/videos/video-rate.type'
4import { retryTransactionWrapper } from '../../../helpers/database-utils' 5import { retryTransactionWrapper } from '../../../helpers/database-utils'
5import { logger } from '../../../helpers/logger' 6import { logger } from '../../../helpers/logger'
6import { database as db } from '../../../initializers' 7import { database as db } from '../../../initializers'
7import { AccountInstance } from '../../../models/account/account-interface' 8import { AccountInstance } from '../../../models/account/account-interface'
8import { VideoChannelInstance } from '../../../models/video/video-channel-interface' 9import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
10import { VideoInstance } from '../../../models/video/video-interface'
9import { getOrCreateAccountAndServer } from '../account' 11import { getOrCreateAccountAndServer } from '../account'
10import { getOrCreateVideoChannel } from '../video-channels' 12import { getOrCreateVideoChannel } from '../video-channels'
11import { generateThumbnailFromUrl } from '../videos' 13import { generateThumbnailFromUrl } from '../videos'
@@ -35,13 +37,29 @@ export {
35 37
36// --------------------------------------------------------------------------- 38// ---------------------------------------------------------------------------
37 39
38function processAddVideo (account: AccountInstance, activity: ActivityAdd, videoChannel: VideoChannelInstance, video: VideoTorrentObject) { 40async function processAddVideo (
41 account: AccountInstance,
42 activity: ActivityAdd,
43 videoChannel: VideoChannelInstance,
44 videoToCreateData: VideoTorrentObject
45) {
39 const options = { 46 const options = {
40 arguments: [ account, activity, videoChannel, video ], 47 arguments: [ account, activity, videoChannel, videoToCreateData ],
41 errorMessage: 'Cannot insert the remote video with many retries.' 48 errorMessage: 'Cannot insert the remote video with many retries.'
42 } 49 }
43 50
44 return retryTransactionWrapper(addRemoteVideo, options) 51 const video = await retryTransactionWrapper(addRemoteVideo, options)
52
53 // Process outside the transaction because we could fetch remote data
54 if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) {
55 await createRates(videoToCreateData.likes.orderedItems, video, 'like')
56 }
57
58 if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) {
59 await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike')
60 }
61
62 return video
45} 63}
46 64
47function addRemoteVideo (account: AccountInstance, 65function addRemoteVideo (account: AccountInstance,
@@ -86,3 +104,30 @@ function addRemoteVideo (account: AccountInstance,
86 return videoCreated 104 return videoCreated
87 }) 105 })
88} 106}
107
108async function createRates (accountUrls: string[], video: VideoInstance, rate: VideoRateType) {
109 let rateCounts = 0
110 const tasks: Bluebird<any>[] = []
111
112 for (const accountUrl of accountUrls) {
113 const account = await getOrCreateAccountAndServer(accountUrl)
114 const p = db.AccountVideoRate
115 .create({
116 videoId: video.id,
117 accountId: account.id,
118 type: rate
119 })
120 .then(() => rateCounts += 1)
121
122 tasks.push(p)
123 }
124
125 await Promise.all(tasks)
126
127 logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid)
128
129 // This is "likes" and "dislikes"
130 await video.increment(rate + 's', { by: rateCounts })
131
132 return
133}
diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts
index 942bce0e6..40f19c701 100644
--- a/server/lib/activitypub/process/process.ts
+++ b/server/lib/activitypub/process/process.ts
@@ -31,7 +31,11 @@ async function processActivities (activities: Activity[], inboxAccount?: Account
31 continue 31 continue
32 } 32 }
33 33
34 await activityProcessor(activity, inboxAccount) 34 try {
35 await activityProcessor(activity, inboxAccount)
36 } catch (err) {
37 logger.warn('Cannot process activity %s.', activity.type, err)
38 }
35 } 39 }
36} 40}
37 41
diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts
index bda319592..9adceab84 100644
--- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts
+++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-fetcher-handler.ts
@@ -49,7 +49,7 @@ async function process (payload: ActivityPubHttpPayload, jobId: number) {
49} 49}
50 50
51function onError (err: Error, jobId: number) { 51function onError (err: Error, jobId: number) {
52 logger.error('Error when broadcasting ActivityPub request in job %d.', jobId, err) 52 logger.error('Error when fetcher ActivityPub request in job %d.', jobId, err)
53 return Promise.resolve() 53 return Promise.resolve()
54} 54}
55 55
diff --git a/server/models/account/account-video-rate-interface.ts b/server/models/account/account-video-rate-interface.ts
index 316056246..1f395bc45 100644
--- a/server/models/account/account-video-rate-interface.ts
+++ b/server/models/account/account-video-rate-interface.ts
@@ -2,6 +2,7 @@ import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird' 2import * as Promise from 'bluebird'
3 3
4import { VideoRateType } from '../../../shared/models/videos/video-rate.type' 4import { VideoRateType } from '../../../shared/models/videos/video-rate.type'
5import { AccountInstance } from './account-interface'
5 6
6export namespace AccountVideoRateMethods { 7export namespace AccountVideoRateMethods {
7 export type Load = (accountId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<AccountVideoRateInstance> 8 export type Load = (accountId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<AccountVideoRateInstance>
@@ -15,6 +16,8 @@ export interface AccountVideoRateAttributes {
15 type: VideoRateType 16 type: VideoRateType
16 accountId: number 17 accountId: number
17 videoId: number 18 videoId: number
19
20 Account?: AccountInstance
18} 21}
19 22
20export interface AccountVideoRateInstance 23export interface AccountVideoRateInstance
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts
index b97f163ab..89e528acf 100644
--- a/server/models/video/video-interface.ts
+++ b/server/models/video/video-interface.ts
@@ -8,6 +8,8 @@ import { TagAttributes, TagInstance } from './tag-interface'
8import { VideoChannelInstance } from './video-channel-interface' 8import { VideoChannelInstance } from './video-channel-interface'
9import { VideoFileAttributes, VideoFileInstance } from './video-file-interface' 9import { VideoFileAttributes, VideoFileInstance } from './video-file-interface'
10import { VideoShareInstance } from './video-share-interface' 10import { VideoShareInstance } from './video-share-interface'
11import { UserVideoRate } from '../../../shared/models/videos/user-video-rate.model'
12import { AccountVideoRateInstance } from '../account/account-video-rate-interface'
11 13
12export namespace VideoMethods { 14export namespace VideoMethods {
13 export type GetThumbnailName = (this: VideoInstance) => string 15 export type GetThumbnailName = (this: VideoInstance) => string
@@ -123,6 +125,7 @@ export interface VideoAttributes {
123 Tags?: TagInstance[] 125 Tags?: TagInstance[]
124 VideoFiles?: VideoFileInstance[] 126 VideoFiles?: VideoFileInstance[]
125 VideoShares?: VideoShareInstance[] 127 VideoShares?: VideoShareInstance[]
128 AccountVideoRates?: AccountVideoRateInstance[]
126} 129}
127 130
128export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> { 131export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 052fc0ae8..592fc2d59 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -47,6 +47,7 @@ import { VideoFileInstance, VideoFileModel } from './video-file-interface'
47import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface' 47import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
48import { sendDeleteVideo } from '../../lib/index' 48import { sendDeleteVideo } from '../../lib/index'
49import * as Bluebird from 'bluebird' 49import * as Bluebird from 'bluebird'
50import { activityPubCollection } from '../../helpers/activitypub'
50 51
51const Buffer = safeBuffer.Buffer 52const Buffer = safeBuffer.Buffer
52 53
@@ -359,6 +360,14 @@ function associate (models) {
359 }, 360 },
360 onDelete: 'cascade' 361 onDelete: 'cascade'
361 }) 362 })
363
364 Video.hasMany(models.AccountVideoRate, {
365 foreignKey: {
366 name: 'videoId',
367 allowNull: false
368 },
369 onDelete: 'cascade'
370 })
362} 371}
363 372
364function afterDestroy (video: VideoInstance) { 373function afterDestroy (video: VideoInstance) {
@@ -575,6 +584,25 @@ toActivityPubObject = function (this: VideoInstance) {
575 } 584 }
576 } 585 }
577 586
587 let likesObject
588 let dislikesObject
589
590 if (Array.isArray(this.AccountVideoRates)) {
591 const likes: string[] = []
592 const dislikes: string[] = []
593
594 for (const rate of this.AccountVideoRates) {
595 if (rate.type === 'like') {
596 likes.push(rate.Account.url)
597 } else if (rate.type === 'dislike') {
598 dislikes.push(rate.Account.url)
599 }
600 }
601
602 likesObject = activityPubCollection(likes)
603 dislikesObject = activityPubCollection(dislikes)
604 }
605
578 const url = [] 606 const url = []
579 for (const file of this.VideoFiles) { 607 for (const file of this.VideoFiles) {
580 url.push({ 608 url.push({
@@ -630,7 +658,9 @@ toActivityPubObject = function (this: VideoInstance) {
630 width: THUMBNAILS_SIZE.width, 658 width: THUMBNAILS_SIZE.width,
631 height: THUMBNAILS_SIZE.height 659 height: THUMBNAILS_SIZE.height
632 }, 660 },
633 url // FIXME: needed? 661 url,
662 likes: likesObject,
663 dislikes: dislikesObject
634 } 664 }
635 665
636 return videoObject 666 return videoObject
@@ -845,8 +875,12 @@ listAllAndSharedByAccountForOutbox = function (accountId: number, start: number,
845 } 875 }
846 ] 876 ]
847 }, 877 },
848 Video['sequelize'].models.Tag, 878 {
849 Video['sequelize'].models.VideoFile 879 model: Video['sequelize'].models.AccountVideoRate,
880 include: [ Video['sequelize'].models.Account ]
881 },
882 Video['sequelize'].models.VideoFile,
883 Video['sequelize'].models.Tag
850 ] 884 ]
851 } 885 }
852 886
@@ -1106,6 +1140,10 @@ loadAndPopulateAccountAndServerAndTags = function (id: number) {
1106 } 1140 }
1107 ] 1141 ]
1108 }, 1142 },
1143 {
1144 model: Video['sequelize'].models.AccountVideoRate,
1145 include: [ Video['sequelize'].models.Account ]
1146 },
1109 Video['sequelize'].models.Tag, 1147 Video['sequelize'].models.Tag,
1110 Video['sequelize'].models.VideoFile 1148 Video['sequelize'].models.VideoFile
1111 ] 1149 ]
@@ -1129,6 +1167,10 @@ loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) {
1129 } 1167 }
1130 ] 1168 ]
1131 }, 1169 },
1170 {
1171 model: Video['sequelize'].models.AccountVideoRate,
1172 include: [ Video['sequelize'].models.Account ]
1173 },
1132 Video['sequelize'].models.Tag, 1174 Video['sequelize'].models.Tag,
1133 Video['sequelize'].models.VideoFile 1175 Video['sequelize'].models.VideoFile
1134 ] 1176 ]