Fetch video likes/dislikes too

pull/128/head
Chocobozzz 2017-11-23 16:55:13 +01:00
parent d8553faa49
commit 16b9097594
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
11 changed files with 121 additions and 12 deletions

View File

@ -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<Activity>).orderedItems
} else {
activities = [ rootActivity as Activity ]
}

View File

@ -24,6 +24,14 @@ function activityPubContextify <T> (data: T) {
})
}
function activityPubCollection (results: any[]) {
return {
type: 'OrderedCollection',
totalItems: results.length,
orderedItems: results
}
}
function activityPubCollectionPagination (url: string, page: any, result: ResultList<any>) {
let next: string
let prev: string
@ -74,5 +82,6 @@ function buildSignedActivity (byAccount: AccountInstance, data: Object) {
export {
activityPubContextify,
activityPubCollectionPagination,
activityPubCollection,
buildSignedActivity
}

View File

@ -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<any>[] = []
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
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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<AccountVideoRateInstance>
@ -15,6 +16,8 @@ export interface AccountVideoRateAttributes {
type: VideoRateType
accountId: number
videoId: number
Account?: AccountInstance
}
export interface AccountVideoRateInstance

View File

@ -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<VideoAttributes> {

View File

@ -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
]

View File

@ -1,9 +1,9 @@
import { Activity } from './activity'
export interface ActivityPubOrderedCollection {
export interface ActivityPubOrderedCollection<T> {
'@context': string[]
type: 'OrderedCollection' | 'OrderedCollectionPage'
totalItems: number
partOf?: string
orderedItems: Activity[]
orderedItems: T[]
}

View File

@ -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<Activity>

View File

@ -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<string>
dislikes?: ActivityPubOrderedCollection<string>
}