From 16b90975941b78d01d7202d441bf731a10048c16 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 23 Nov 2017 16:55:13 +0100 Subject: [PATCH] Fetch video likes/dislikes too --- server/controllers/activitypub/inbox.ts | 2 +- server/helpers/activitypub.ts | 9 ++++ server/lib/activitypub/process/process-add.ts | 51 +++++++++++++++++-- server/lib/activitypub/process/process.ts | 6 ++- .../activitypub-http-fetcher-handler.ts | 2 +- .../account/account-video-rate-interface.ts | 3 ++ server/models/video/video-interface.ts | 3 ++ server/models/video/video.ts | 48 +++++++++++++++-- .../activitypub-ordered-collection.ts | 4 +- shared/models/activitypub/activitypub-root.ts | 2 +- .../objects/video-torrent-object.ts | 3 ++ 11 files changed, 121 insertions(+), 12 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 if ([ 'Collection', 'CollectionPage' ].indexOf(rootActivity.type) !== -1) { activities = (rootActivity as ActivityPubCollection).items } else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].indexOf(rootActivity.type) !== -1) { - activities = (rootActivity as ActivityPubOrderedCollection).orderedItems + activities = (rootActivity as ActivityPubOrderedCollection).orderedItems } else { activities = [ rootActivity as Activity ] } 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 (data: T) { }) } +function activityPubCollection (results: any[]) { + return { + type: 'OrderedCollection', + totalItems: results.length, + orderedItems: results + } +} + function activityPubCollectionPagination (url: string, page: any, result: ResultList) { let next: string let prev: string @@ -74,5 +82,6 @@ function buildSignedActivity (byAccount: AccountInstance, data: Object) { export { activityPubContextify, activityPubCollectionPagination, + activityPubCollection, buildSignedActivity } 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 @@ import * as Bluebird from 'bluebird' import { VideoTorrentObject } from '../../../../shared' import { ActivityAdd } from '../../../../shared/models/activitypub/activity' +import { VideoRateType } from '../../../../shared/models/videos/video-rate.type' import { retryTransactionWrapper } from '../../../helpers/database-utils' import { logger } from '../../../helpers/logger' import { database as db } from '../../../initializers' import { AccountInstance } from '../../../models/account/account-interface' import { VideoChannelInstance } from '../../../models/video/video-channel-interface' +import { VideoInstance } from '../../../models/video/video-interface' import { getOrCreateAccountAndServer } from '../account' import { getOrCreateVideoChannel } from '../video-channels' import { generateThumbnailFromUrl } from '../videos' @@ -35,13 +37,29 @@ export { // --------------------------------------------------------------------------- -function processAddVideo (account: AccountInstance, activity: ActivityAdd, videoChannel: VideoChannelInstance, video: VideoTorrentObject) { +async function processAddVideo ( + account: AccountInstance, + activity: ActivityAdd, + videoChannel: VideoChannelInstance, + videoToCreateData: VideoTorrentObject +) { const options = { - arguments: [ account, activity, videoChannel, video ], + arguments: [ account, activity, videoChannel, videoToCreateData ], errorMessage: 'Cannot insert the remote video with many retries.' } - return retryTransactionWrapper(addRemoteVideo, options) + const video = await retryTransactionWrapper(addRemoteVideo, options) + + // Process outside the transaction because we could fetch remote data + if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) { + await createRates(videoToCreateData.likes.orderedItems, video, 'like') + } + + if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) { + await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') + } + + return video } function addRemoteVideo (account: AccountInstance, @@ -86,3 +104,30 @@ function addRemoteVideo (account: AccountInstance, return videoCreated }) } + +async function createRates (accountUrls: string[], video: VideoInstance, rate: VideoRateType) { + let rateCounts = 0 + const tasks: Bluebird[] = [] + + for (const accountUrl of accountUrls) { + const account = await getOrCreateAccountAndServer(accountUrl) + const p = db.AccountVideoRate + .create({ + videoId: video.id, + accountId: account.id, + type: rate + }) + .then(() => rateCounts += 1) + + tasks.push(p) + } + + await Promise.all(tasks) + + logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid) + + // This is "likes" and "dislikes" + await video.increment(rate + 's', { by: rateCounts }) + + return +} 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 continue } - await activityProcessor(activity, inboxAccount) + try { + await activityProcessor(activity, inboxAccount) + } catch (err) { + logger.warn('Cannot process activity %s.', activity.type, err) + } } } 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) { } function onError (err: Error, jobId: number) { - logger.error('Error when broadcasting ActivityPub request in job %d.', jobId, err) + logger.error('Error when fetcher ActivityPub request in job %d.', jobId, err) return Promise.resolve() } 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' import * as Promise from 'bluebird' import { VideoRateType } from '../../../shared/models/videos/video-rate.type' +import { AccountInstance } from './account-interface' export namespace AccountVideoRateMethods { export type Load = (accountId: number, videoId: number, transaction: Sequelize.Transaction) => Promise @@ -15,6 +16,8 @@ export interface AccountVideoRateAttributes { type: VideoRateType accountId: number videoId: number + + Account?: AccountInstance } export 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' import { VideoChannelInstance } from './video-channel-interface' import { VideoFileAttributes, VideoFileInstance } from './video-file-interface' import { VideoShareInstance } from './video-share-interface' +import { UserVideoRate } from '../../../shared/models/videos/user-video-rate.model' +import { AccountVideoRateInstance } from '../account/account-video-rate-interface' export namespace VideoMethods { export type GetThumbnailName = (this: VideoInstance) => string @@ -123,6 +125,7 @@ export interface VideoAttributes { Tags?: TagInstance[] VideoFiles?: VideoFileInstance[] VideoShares?: VideoShareInstance[] + AccountVideoRates?: AccountVideoRateInstance[] } export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance { 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' import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface' import { sendDeleteVideo } from '../../lib/index' import * as Bluebird from 'bluebird' +import { activityPubCollection } from '../../helpers/activitypub' const Buffer = safeBuffer.Buffer @@ -359,6 +360,14 @@ function associate (models) { }, onDelete: 'cascade' }) + + Video.hasMany(models.AccountVideoRate, { + foreignKey: { + name: 'videoId', + allowNull: false + }, + onDelete: 'cascade' + }) } function afterDestroy (video: VideoInstance) { @@ -575,6 +584,25 @@ toActivityPubObject = function (this: VideoInstance) { } } + let likesObject + let dislikesObject + + if (Array.isArray(this.AccountVideoRates)) { + const likes: string[] = [] + const dislikes: string[] = [] + + for (const rate of this.AccountVideoRates) { + if (rate.type === 'like') { + likes.push(rate.Account.url) + } else if (rate.type === 'dislike') { + dislikes.push(rate.Account.url) + } + } + + likesObject = activityPubCollection(likes) + dislikesObject = activityPubCollection(dislikes) + } + const url = [] for (const file of this.VideoFiles) { url.push({ @@ -630,7 +658,9 @@ toActivityPubObject = function (this: VideoInstance) { width: THUMBNAILS_SIZE.width, height: THUMBNAILS_SIZE.height }, - url // FIXME: needed? + url, + likes: likesObject, + dislikes: dislikesObject } return videoObject @@ -845,8 +875,12 @@ listAllAndSharedByAccountForOutbox = function (accountId: number, start: number, } ] }, - Video['sequelize'].models.Tag, - Video['sequelize'].models.VideoFile + { + model: Video['sequelize'].models.AccountVideoRate, + include: [ Video['sequelize'].models.Account ] + }, + Video['sequelize'].models.VideoFile, + Video['sequelize'].models.Tag ] } @@ -1106,6 +1140,10 @@ loadAndPopulateAccountAndServerAndTags = function (id: number) { } ] }, + { + model: Video['sequelize'].models.AccountVideoRate, + include: [ Video['sequelize'].models.Account ] + }, Video['sequelize'].models.Tag, Video['sequelize'].models.VideoFile ] @@ -1129,6 +1167,10 @@ loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) { } ] }, + { + model: Video['sequelize'].models.AccountVideoRate, + include: [ Video['sequelize'].models.Account ] + }, Video['sequelize'].models.Tag, Video['sequelize'].models.VideoFile ] diff --git a/shared/models/activitypub/activitypub-ordered-collection.ts b/shared/models/activitypub/activitypub-ordered-collection.ts index 4080fd740..487d8cee0 100644 --- a/shared/models/activitypub/activitypub-ordered-collection.ts +++ b/shared/models/activitypub/activitypub-ordered-collection.ts @@ -1,9 +1,9 @@ import { Activity } from './activity' -export interface ActivityPubOrderedCollection { +export interface ActivityPubOrderedCollection { '@context': string[] type: 'OrderedCollection' | 'OrderedCollectionPage' totalItems: number partOf?: string - orderedItems: Activity[] + orderedItems: T[] } diff --git a/shared/models/activitypub/activitypub-root.ts b/shared/models/activitypub/activitypub-root.ts index 6a67f3101..22dff83a2 100644 --- a/shared/models/activitypub/activitypub-root.ts +++ b/shared/models/activitypub/activitypub-root.ts @@ -2,4 +2,4 @@ import { Activity } from './activity' import { ActivityPubCollection } from './activitypub-collection' import { ActivityPubOrderedCollection } from './activitypub-ordered-collection' -export type RootActivity = Activity | ActivityPubCollection | ActivityPubOrderedCollection +export type RootActivity = Activity | ActivityPubCollection | ActivityPubOrderedCollection diff --git a/shared/models/activitypub/objects/video-torrent-object.ts b/shared/models/activitypub/objects/video-torrent-object.ts index ae8f807c8..a4e032d04 100644 --- a/shared/models/activitypub/objects/video-torrent-object.ts +++ b/shared/models/activitypub/objects/video-torrent-object.ts @@ -4,6 +4,7 @@ import { ActivityTagObject, ActivityUrlObject } from './common-objects' +import { ActivityPubOrderedCollection } from '../activitypub-ordered-collection' export interface VideoTorrentObject { type: 'Video' @@ -24,4 +25,6 @@ export interface VideoTorrentObject { icon: ActivityIconObject url: ActivityUrlObject[] actor?: string + likes?: ActivityPubOrderedCollection + dislikes?: ActivityPubOrderedCollection } -- 2.25.1