Fetch video likes/dislikes too
This commit is contained in:
parent
d8553faa49
commit
16b9097594
|
@ -38,7 +38,7 @@ async function inboxController (req: express.Request, res: express.Response, nex
|
||||||
if ([ 'Collection', 'CollectionPage' ].indexOf(rootActivity.type) !== -1) {
|
if ([ 'Collection', 'CollectionPage' ].indexOf(rootActivity.type) !== -1) {
|
||||||
activities = (rootActivity as ActivityPubCollection).items
|
activities = (rootActivity as ActivityPubCollection).items
|
||||||
} else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].indexOf(rootActivity.type) !== -1) {
|
} else if ([ 'OrderedCollection', 'OrderedCollectionPage' ].indexOf(rootActivity.type) !== -1) {
|
||||||
activities = (rootActivity as ActivityPubOrderedCollection).orderedItems
|
activities = (rootActivity as ActivityPubOrderedCollection<Activity>).orderedItems
|
||||||
} else {
|
} else {
|
||||||
activities = [ rootActivity as Activity ]
|
activities = [ rootActivity as Activity ]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>) {
|
function activityPubCollectionPagination (url: string, page: any, result: ResultList<any>) {
|
||||||
let next: string
|
let next: string
|
||||||
let prev: string
|
let prev: string
|
||||||
|
@ -74,5 +82,6 @@ function buildSignedActivity (byAccount: AccountInstance, data: Object) {
|
||||||
export {
|
export {
|
||||||
activityPubContextify,
|
activityPubContextify,
|
||||||
activityPubCollectionPagination,
|
activityPubCollectionPagination,
|
||||||
|
activityPubCollection,
|
||||||
buildSignedActivity
|
buildSignedActivity
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { VideoTorrentObject } from '../../../../shared'
|
import { VideoTorrentObject } from '../../../../shared'
|
||||||
import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
|
import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { VideoRateType } from '../../../../shared/models/videos/video-rate.type'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { database as db } from '../../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
|
||||||
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { getOrCreateAccountAndServer } from '../account'
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
import { getOrCreateVideoChannel } from '../video-channels'
|
import { getOrCreateVideoChannel } from '../video-channels'
|
||||||
import { generateThumbnailFromUrl } from '../videos'
|
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 = {
|
const options = {
|
||||||
arguments: [ account, activity, videoChannel, video ],
|
arguments: [ account, activity, videoChannel, videoToCreateData ],
|
||||||
errorMessage: 'Cannot insert the remote video with many retries.'
|
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,
|
function addRemoteVideo (account: AccountInstance,
|
||||||
|
@ -86,3 +104,30 @@ function addRemoteVideo (account: AccountInstance,
|
||||||
return videoCreated
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,11 @@ async function processActivities (activities: Activity[], inboxAccount?: Account
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
await activityProcessor(activity, inboxAccount)
|
try {
|
||||||
|
await activityProcessor(activity, inboxAccount)
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('Cannot process activity %s.', activity.type, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ async function process (payload: ActivityPubHttpPayload, jobId: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onError (err: Error, 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()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as Sequelize from 'sequelize'
|
||||||
import * as Promise from 'bluebird'
|
import * as Promise from 'bluebird'
|
||||||
|
|
||||||
import { VideoRateType } from '../../../shared/models/videos/video-rate.type'
|
import { VideoRateType } from '../../../shared/models/videos/video-rate.type'
|
||||||
|
import { AccountInstance } from './account-interface'
|
||||||
|
|
||||||
export namespace AccountVideoRateMethods {
|
export namespace AccountVideoRateMethods {
|
||||||
export type Load = (accountId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<AccountVideoRateInstance>
|
export type Load = (accountId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<AccountVideoRateInstance>
|
||||||
|
@ -15,6 +16,8 @@ export interface AccountVideoRateAttributes {
|
||||||
type: VideoRateType
|
type: VideoRateType
|
||||||
accountId: number
|
accountId: number
|
||||||
videoId: number
|
videoId: number
|
||||||
|
|
||||||
|
Account?: AccountInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountVideoRateInstance
|
export interface AccountVideoRateInstance
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { TagAttributes, TagInstance } from './tag-interface'
|
||||||
import { VideoChannelInstance } from './video-channel-interface'
|
import { VideoChannelInstance } from './video-channel-interface'
|
||||||
import { VideoFileAttributes, VideoFileInstance } from './video-file-interface'
|
import { VideoFileAttributes, VideoFileInstance } from './video-file-interface'
|
||||||
import { VideoShareInstance } from './video-share-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 namespace VideoMethods {
|
||||||
export type GetThumbnailName = (this: VideoInstance) => string
|
export type GetThumbnailName = (this: VideoInstance) => string
|
||||||
|
@ -123,6 +125,7 @@ export interface VideoAttributes {
|
||||||
Tags?: TagInstance[]
|
Tags?: TagInstance[]
|
||||||
VideoFiles?: VideoFileInstance[]
|
VideoFiles?: VideoFileInstance[]
|
||||||
VideoShares?: VideoShareInstance[]
|
VideoShares?: VideoShareInstance[]
|
||||||
|
AccountVideoRates?: AccountVideoRateInstance[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
|
export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
|
||||||
|
|
|
@ -47,6 +47,7 @@ import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
||||||
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
||||||
import { sendDeleteVideo } from '../../lib/index'
|
import { sendDeleteVideo } from '../../lib/index'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
|
import { activityPubCollection } from '../../helpers/activitypub'
|
||||||
|
|
||||||
const Buffer = safeBuffer.Buffer
|
const Buffer = safeBuffer.Buffer
|
||||||
|
|
||||||
|
@ -359,6 +360,14 @@ function associate (models) {
|
||||||
},
|
},
|
||||||
onDelete: 'cascade'
|
onDelete: 'cascade'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Video.hasMany(models.AccountVideoRate, {
|
||||||
|
foreignKey: {
|
||||||
|
name: 'videoId',
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
onDelete: 'cascade'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterDestroy (video: VideoInstance) {
|
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 = []
|
const url = []
|
||||||
for (const file of this.VideoFiles) {
|
for (const file of this.VideoFiles) {
|
||||||
url.push({
|
url.push({
|
||||||
|
@ -630,7 +658,9 @@ toActivityPubObject = function (this: VideoInstance) {
|
||||||
width: THUMBNAILS_SIZE.width,
|
width: THUMBNAILS_SIZE.width,
|
||||||
height: THUMBNAILS_SIZE.height
|
height: THUMBNAILS_SIZE.height
|
||||||
},
|
},
|
||||||
url // FIXME: needed?
|
url,
|
||||||
|
likes: likesObject,
|
||||||
|
dislikes: dislikesObject
|
||||||
}
|
}
|
||||||
|
|
||||||
return videoObject
|
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.Tag,
|
||||||
Video['sequelize'].models.VideoFile
|
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.Tag,
|
||||||
Video['sequelize'].models.VideoFile
|
Video['sequelize'].models.VideoFile
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Activity } from './activity'
|
import { Activity } from './activity'
|
||||||
|
|
||||||
export interface ActivityPubOrderedCollection {
|
export interface ActivityPubOrderedCollection<T> {
|
||||||
'@context': string[]
|
'@context': string[]
|
||||||
type: 'OrderedCollection' | 'OrderedCollectionPage'
|
type: 'OrderedCollection' | 'OrderedCollectionPage'
|
||||||
totalItems: number
|
totalItems: number
|
||||||
partOf?: string
|
partOf?: string
|
||||||
orderedItems: Activity[]
|
orderedItems: T[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,4 @@ import { Activity } from './activity'
|
||||||
import { ActivityPubCollection } from './activitypub-collection'
|
import { ActivityPubCollection } from './activitypub-collection'
|
||||||
import { ActivityPubOrderedCollection } from './activitypub-ordered-collection'
|
import { ActivityPubOrderedCollection } from './activitypub-ordered-collection'
|
||||||
|
|
||||||
export type RootActivity = Activity | ActivityPubCollection | ActivityPubOrderedCollection
|
export type RootActivity = Activity | ActivityPubCollection | ActivityPubOrderedCollection<Activity>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
ActivityTagObject,
|
ActivityTagObject,
|
||||||
ActivityUrlObject
|
ActivityUrlObject
|
||||||
} from './common-objects'
|
} from './common-objects'
|
||||||
|
import { ActivityPubOrderedCollection } from '../activitypub-ordered-collection'
|
||||||
|
|
||||||
export interface VideoTorrentObject {
|
export interface VideoTorrentObject {
|
||||||
type: 'Video'
|
type: 'Video'
|
||||||
|
@ -24,4 +25,6 @@ export interface VideoTorrentObject {
|
||||||
icon: ActivityIconObject
|
icon: ActivityIconObject
|
||||||
url: ActivityUrlObject[]
|
url: ActivityUrlObject[]
|
||||||
actor?: string
|
actor?: string
|
||||||
|
likes?: ActivityPubOrderedCollection<string>
|
||||||
|
dislikes?: ActivityPubOrderedCollection<string>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue