Don't store remote rates of remote videos
In the future we'll stop to expose all available rates to improve users privacy
This commit is contained in:
parent
2e3f7a5a6f
commit
57e4e1c1a9
|
@ -8,6 +8,7 @@ if [ $# -eq 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
retries=3
|
retries=3
|
||||||
|
speedFactor="${2:-1}"
|
||||||
|
|
||||||
runTest () {
|
runTest () {
|
||||||
jobname=$1
|
jobname=$1
|
||||||
|
@ -53,7 +54,7 @@ elif [ "$1" = "client" ]; then
|
||||||
# Not in their own task, they need an index.html
|
# Not in their own task, they need an index.html
|
||||||
pluginFiles="./dist/server/tests/plugins/html-injection.js ./dist/server/tests/api/server/plugins.js"
|
pluginFiles="./dist/server/tests/plugins/html-injection.js ./dist/server/tests/api/server/plugins.js"
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 2 $feedsFiles $helperFiles $miscFiles $pluginFiles $libFiles
|
MOCHA_PARALLEL=true runTest "$1" $((2*$speedFactor)) $feedsFiles $helperFiles $miscFiles $pluginFiles $libFiles
|
||||||
elif [ "$1" = "cli-plugin" ]; then
|
elif [ "$1" = "cli-plugin" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
npm run setup:cli
|
npm run setup:cli
|
||||||
|
@ -61,7 +62,7 @@ elif [ "$1" = "cli-plugin" ]; then
|
||||||
pluginsFiles=$(findTestFiles ./dist/server/tests/plugins html-injection.js)
|
pluginsFiles=$(findTestFiles ./dist/server/tests/plugins html-injection.js)
|
||||||
cliFiles=$(findTestFiles ./dist/server/tests/cli)
|
cliFiles=$(findTestFiles ./dist/server/tests/cli)
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 2 $pluginsFiles
|
MOCHA_PARALLEL=true runTest "$1" $((2*$speedFactor)) $pluginsFiles
|
||||||
runTest "$1" 1 $cliFiles
|
runTest "$1" 1 $cliFiles
|
||||||
elif [ "$1" = "api-1" ]; then
|
elif [ "$1" = "api-1" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
@ -70,7 +71,7 @@ elif [ "$1" = "api-1" ]; then
|
||||||
notificationsFiles=$(findTestFiles ./dist/server/tests/api/notifications)
|
notificationsFiles=$(findTestFiles ./dist/server/tests/api/notifications)
|
||||||
searchFiles=$(findTestFiles ./dist/server/tests/api/search)
|
searchFiles=$(findTestFiles ./dist/server/tests/api/search)
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 3 $notificationsFiles $searchFiles $checkParamFiles
|
MOCHA_PARALLEL=true runTest "$1" $((3*$speedFactor)) $notificationsFiles $searchFiles $checkParamFiles
|
||||||
elif [ "$1" = "api-2" ]; then
|
elif [ "$1" = "api-2" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
|
||||||
|
@ -78,13 +79,13 @@ elif [ "$1" = "api-2" ]; then
|
||||||
serverFiles=$(findTestFiles ./dist/server/tests/api/server plugins.js)
|
serverFiles=$(findTestFiles ./dist/server/tests/api/server plugins.js)
|
||||||
usersFiles=$(findTestFiles ./dist/server/tests/api/users)
|
usersFiles=$(findTestFiles ./dist/server/tests/api/users)
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 3 $liveFiles $serverFiles $usersFiles
|
MOCHA_PARALLEL=true runTest "$1" $((3*$speedFactor)) $liveFiles $serverFiles $usersFiles
|
||||||
elif [ "$1" = "api-3" ]; then
|
elif [ "$1" = "api-3" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
|
||||||
videosFiles=$(findTestFiles ./dist/server/tests/api/videos)
|
videosFiles=$(findTestFiles ./dist/server/tests/api/videos)
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 3 $videosFiles
|
MOCHA_PARALLEL=true runTest "$1" $((3*$speedFactor)) $videosFiles
|
||||||
elif [ "$1" = "api-4" ]; then
|
elif [ "$1" = "api-4" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
|
||||||
|
@ -93,13 +94,13 @@ elif [ "$1" = "api-4" ]; then
|
||||||
objectStorageFiles=$(findTestFiles ./dist/server/tests/api/object-storage)
|
objectStorageFiles=$(findTestFiles ./dist/server/tests/api/object-storage)
|
||||||
activitypubFiles=$(findTestFiles ./dist/server/tests/api/activitypub)
|
activitypubFiles=$(findTestFiles ./dist/server/tests/api/activitypub)
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 2 $moderationFiles $redundancyFiles $activitypubFiles $objectStorageFiles
|
MOCHA_PARALLEL=true runTest "$1" $((2*$speedFactor)) $moderationFiles $redundancyFiles $activitypubFiles $objectStorageFiles
|
||||||
elif [ "$1" = "api-5" ]; then
|
elif [ "$1" = "api-5" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
|
||||||
transcodingFiles=$(findTestFiles ./dist/server/tests/api/transcoding)
|
transcodingFiles=$(findTestFiles ./dist/server/tests/api/transcoding)
|
||||||
|
|
||||||
MOCHA_PARALLEL=true runTest "$1" 2 $transcodingFiles
|
MOCHA_PARALLEL=true runTest "$1" $((2*$speedFactor)) $transcodingFiles
|
||||||
elif [ "$1" = "external-plugins" ]; then
|
elif [ "$1" = "external-plugins" ]; then
|
||||||
npm run build:server
|
npm run build:server
|
||||||
|
|
||||||
|
|
|
@ -66,11 +66,13 @@ activityPubClientRouter.get('/accounts?/:name/playlists',
|
||||||
)
|
)
|
||||||
activityPubClientRouter.get('/accounts?/:name/likes/:videoId',
|
activityPubClientRouter.get('/accounts?/:name/likes/:videoId',
|
||||||
executeIfActivityPub,
|
executeIfActivityPub,
|
||||||
|
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
|
||||||
asyncMiddleware(getAccountVideoRateValidatorFactory('like')),
|
asyncMiddleware(getAccountVideoRateValidatorFactory('like')),
|
||||||
getAccountVideoRateFactory('like')
|
getAccountVideoRateFactory('like')
|
||||||
)
|
)
|
||||||
activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
|
activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
|
||||||
executeIfActivityPub,
|
executeIfActivityPub,
|
||||||
|
cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS),
|
||||||
asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')),
|
asyncMiddleware(getAccountVideoRateValidatorFactory('dislike')),
|
||||||
getAccountVideoRateFactory('dislike')
|
getAccountVideoRateFactory('dislike')
|
||||||
)
|
)
|
||||||
|
|
|
@ -134,8 +134,7 @@ async function searchVideoURI (url: string, res: express.Response) {
|
||||||
if (isUserAbleToSearchRemoteURI(res)) {
|
if (isUserAbleToSearchRemoteURI(res)) {
|
||||||
try {
|
try {
|
||||||
const syncParam = {
|
const syncParam = {
|
||||||
likes: false,
|
rates: false,
|
||||||
dislikes: false,
|
|
||||||
shares: false,
|
shares: false,
|
||||||
comments: false,
|
comments: false,
|
||||||
thumbnail: true,
|
thumbnail: true,
|
||||||
|
|
|
@ -154,7 +154,9 @@ async function activityPubCollectionPagination (
|
||||||
id: baseUrl,
|
id: baseUrl,
|
||||||
type: 'OrderedCollectionPage',
|
type: 'OrderedCollectionPage',
|
||||||
totalItems: result.total,
|
totalItems: result.total,
|
||||||
first: baseUrl + '?page=1'
|
first: result.data.length === 0
|
||||||
|
? undefined
|
||||||
|
: baseUrl + '?page=1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const LAST_MIGRATION_VERSION = 690
|
const LAST_MIGRATION_VERSION = 695
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
28
server/initializers/migrations/0695-remove-remote-rates.ts
Normal file
28
server/initializers/migrations/0695-remove-remote-rates.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
|
|
||||||
|
async function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction
|
||||||
|
queryInterface: Sequelize.QueryInterface
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
db: any
|
||||||
|
}): Promise<void> {
|
||||||
|
const query = 'DELETE FROM "accountVideoRate" ' +
|
||||||
|
'WHERE "accountVideoRate".id IN (' +
|
||||||
|
'SELECT "accountVideoRate".id FROM "accountVideoRate" ' +
|
||||||
|
'INNER JOIN account ON account.id = "accountVideoRate"."accountId" ' +
|
||||||
|
'INNER JOIN actor ON actor.id = account."actorId" ' +
|
||||||
|
'INNER JOIN video ON video.id = "accountVideoRate"."videoId" ' +
|
||||||
|
'WHERE actor."serverId" IS NOT NULL AND video.remote IS TRUE' +
|
||||||
|
')'
|
||||||
|
|
||||||
|
await utils.sequelize.query(query, { type: Sequelize.QueryTypes.BULKDELETE, transaction: utils.transaction })
|
||||||
|
}
|
||||||
|
|
||||||
|
function down () {
|
||||||
|
throw new Error('Not implemented.')
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
up,
|
||||||
|
down
|
||||||
|
}
|
|
@ -1,68 +1,6 @@
|
||||||
import { Transaction } from 'sequelize'
|
|
||||||
import { ActivityAudience } from '../../../shared/models/activitypub'
|
import { ActivityAudience } from '../../../shared/models/activitypub'
|
||||||
import { ACTIVITY_PUB } from '../../initializers/constants'
|
import { ACTIVITY_PUB } from '../../initializers/constants'
|
||||||
import { ActorModel } from '../../models/actor/actor'
|
import { MActorFollowersUrl } from '../../types/models'
|
||||||
import { VideoModel } from '../../models/video/video'
|
|
||||||
import { VideoShareModel } from '../../models/video/video-share'
|
|
||||||
import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '../../types/models'
|
|
||||||
|
|
||||||
function getRemoteVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[]): ActivityAudience {
|
|
||||||
return {
|
|
||||||
to: [ accountActor.url ],
|
|
||||||
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVideoCommentAudience (
|
|
||||||
videoComment: MCommentOwnerVideo,
|
|
||||||
threadParentComments: MCommentOwner[],
|
|
||||||
actorsInvolvedInVideo: MActorFollowersUrl[],
|
|
||||||
isOrigin = false
|
|
||||||
): ActivityAudience {
|
|
||||||
const to = [ ACTIVITY_PUB.PUBLIC ]
|
|
||||||
const cc: string[] = []
|
|
||||||
|
|
||||||
// Owner of the video we comment
|
|
||||||
if (isOrigin === false) {
|
|
||||||
cc.push(videoComment.Video.VideoChannel.Account.Actor.url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Followers of the poster
|
|
||||||
cc.push(videoComment.Account.Actor.followersUrl)
|
|
||||||
|
|
||||||
// Send to actors we reply to
|
|
||||||
for (const parentComment of threadParentComments) {
|
|
||||||
if (parentComment.isDeleted()) continue
|
|
||||||
|
|
||||||
cc.push(parentComment.Account.Actor.url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
to,
|
|
||||||
cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience {
|
|
||||||
return {
|
|
||||||
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
|
|
||||||
cc: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) {
|
|
||||||
const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t)
|
|
||||||
|
|
||||||
const videoAll = video as VideoModel
|
|
||||||
|
|
||||||
const videoActor = videoAll.VideoChannel?.Account
|
|
||||||
? videoAll.VideoChannel.Account.Actor
|
|
||||||
: await ActorModel.loadFromAccountByVideoId(video.id, t)
|
|
||||||
|
|
||||||
actors.push(videoActor)
|
|
||||||
|
|
||||||
return actors
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAudience (actorSender: MActorFollowersUrl, isPublic = true) {
|
function getAudience (actorSender: MActorFollowersUrl, isPublic = true) {
|
||||||
return buildAudience([ actorSender.followersUrl ], isPublic)
|
return buildAudience([ actorSender.followersUrl ], isPublic)
|
||||||
|
@ -92,9 +30,5 @@ function audiencify<T> (object: T, audience: ActivityAudience) {
|
||||||
export {
|
export {
|
||||||
buildAudience,
|
buildAudience,
|
||||||
getAudience,
|
getAudience,
|
||||||
getRemoteVideoAudience,
|
audiencify
|
||||||
getActorsInvolvedInVideo,
|
|
||||||
getAudienceFromFollowersOf,
|
|
||||||
audiencify,
|
|
||||||
getVideoCommentAudience
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ActivityAnnounce } from '../../../../shared/models/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
import { getOrCreateAPVideo } from '../videos'
|
import { getOrCreateAPVideo } from '../videos'
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFile
|
||||||
import { Notifier } from '../../notifier'
|
import { Notifier } from '../../notifier'
|
||||||
import { createOrUpdateCacheFile } from '../cache-file'
|
import { createOrUpdateCacheFile } from '../cache-file'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlists'
|
import { createOrUpdateVideoPlaylist } from '../playlists'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
import { resolveThread } from '../video-comments'
|
import { resolveThread } from '../video-comments'
|
||||||
import { getOrCreateAPVideo } from '../videos'
|
import { getOrCreateAPVideo } from '../videos'
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ export {
|
||||||
async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
|
async function processCreateVideo (activity: ActivityCreate, notify: boolean) {
|
||||||
const videoToCreateData = activity.object as VideoObject
|
const videoToCreateData = activity.object as VideoObject
|
||||||
|
|
||||||
const syncParam = { likes: false, dislikes: false, shares: false, comments: false, thumbnail: true, refreshVideo: false }
|
const syncParam = { rates: false, shares: false, comments: false, thumbnail: true, refreshVideo: false }
|
||||||
const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam })
|
const { video, created } = await getOrCreateAPVideo({ videoObject: videoToCreateData, syncParam })
|
||||||
|
|
||||||
if (created && notify) Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
if (created && notify) Notifier.Instance.notifyOnNewVideoIfNeeded(video)
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
MChannelActor,
|
MChannelActor,
|
||||||
MCommentOwnerVideo
|
MCommentOwnerVideo
|
||||||
} from '../../../types/models'
|
} from '../../../types/models'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
|
|
||||||
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
|
async function processDeleteActivity (options: APProcessorOptions<ActivityDelete>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { ActivityCreate, ActivityDislike, DislikeObject } from '@shared/models'
|
import { ActivityCreate, ActivityDislike, DislikeObject } from '@shared/models'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { sequelizeTypescript } from '../../../initializers/database'
|
import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorSignature } from '../../../types/models'
|
import { MActorSignature } from '../../../types/models'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
|
||||||
import { getOrCreateAPVideo } from '../videos'
|
|
||||||
|
|
||||||
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
|
async function processDislikeActivity (options: APProcessorOptions<ActivityCreate | ActivityDislike>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -29,16 +29,23 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct
|
||||||
|
|
||||||
if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
|
if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
|
||||||
|
|
||||||
const { video } = await getOrCreateAPVideo({ videoObject: dislikeObject })
|
const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: dislikeObject, fetchType: 'only-video' })
|
||||||
|
|
||||||
|
// We don't care about dislikes of remote videos
|
||||||
|
if (!onlyVideo.isOwned()) return
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t)
|
||||||
|
|
||||||
const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t)
|
const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t)
|
||||||
if (existingRate && existingRate.type === 'dislike') return
|
if (existingRate && existingRate.type === 'dislike') return
|
||||||
|
|
||||||
await video.increment('dislikes', { transaction: t })
|
await video.increment('dislikes', { transaction: t })
|
||||||
|
video.dislikes++
|
||||||
|
|
||||||
if (existingRate && existingRate.type === 'like') {
|
if (existingRate && existingRate.type === 'like') {
|
||||||
await video.decrement('likes', { transaction: t })
|
await video.decrement('likes', { transaction: t })
|
||||||
|
video.likes--
|
||||||
}
|
}
|
||||||
|
|
||||||
const rate = existingRate || new AccountVideoRateModel()
|
const rate = existingRate || new AccountVideoRateModel()
|
||||||
|
@ -49,11 +56,6 @@ async function processDislike (activity: ActivityCreate | ActivityDislike, byAct
|
||||||
|
|
||||||
await rate.save({ transaction: t })
|
await rate.save({ transaction: t })
|
||||||
|
|
||||||
if (video.isOwned()) {
|
await federateVideoIfNeeded(video, false, t)
|
||||||
// Don't resend the activity to the sender
|
|
||||||
const exceptions = [ byActor ]
|
|
||||||
|
|
||||||
await forwardVideoRelatedActivity(activity, t, exceptions, video)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { ActivityLike } from '../../../../shared/models/activitypub'
|
import { ActivityLike } from '../../../../shared/models/activitypub'
|
||||||
import { getAPId } from '../../../helpers/activitypub'
|
import { getAPId } from '../../../helpers/activitypub'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
|
@ -5,11 +6,11 @@ import { sequelizeTypescript } from '../../../initializers/database'
|
||||||
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorSignature } from '../../../types/models'
|
import { MActorSignature } from '../../../types/models'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
|
||||||
import { getOrCreateAPVideo } from '../videos'
|
|
||||||
|
|
||||||
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
|
async function processLikeActivity (options: APProcessorOptions<ActivityLike>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
|
||||||
return retryTransactionWrapper(processLikeVideo, byActor, activity)
|
return retryTransactionWrapper(processLikeVideo, byActor, activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,17 +28,24 @@ async function processLikeVideo (byActor: MActorSignature, activity: ActivityLik
|
||||||
const byAccount = byActor.Account
|
const byAccount = byActor.Account
|
||||||
if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url)
|
if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url)
|
||||||
|
|
||||||
const { video } = await getOrCreateAPVideo({ videoObject: videoUrl })
|
const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: videoUrl, fetchType: 'only-video' })
|
||||||
|
|
||||||
|
// We don't care about likes of remote videos
|
||||||
|
if (!onlyVideo.isOwned()) return
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t)
|
||||||
|
|
||||||
const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t)
|
const existingRate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byAccount.id, video.id, activity.id, t)
|
||||||
if (existingRate && existingRate.type === 'like') return
|
if (existingRate && existingRate.type === 'like') return
|
||||||
|
|
||||||
if (existingRate && existingRate.type === 'dislike') {
|
if (existingRate && existingRate.type === 'dislike') {
|
||||||
await video.decrement('dislikes', { transaction: t })
|
await video.decrement('dislikes', { transaction: t })
|
||||||
|
video.dislikes--
|
||||||
}
|
}
|
||||||
|
|
||||||
await video.increment('likes', { transaction: t })
|
await video.increment('likes', { transaction: t })
|
||||||
|
video.likes++
|
||||||
|
|
||||||
const rate = existingRate || new AccountVideoRateModel()
|
const rate = existingRate || new AccountVideoRateModel()
|
||||||
rate.type = 'like'
|
rate.type = 'like'
|
||||||
|
@ -47,11 +55,6 @@ async function processLikeVideo (byActor: MActorSignature, activity: ActivityLik
|
||||||
|
|
||||||
await rate.save({ transaction: t })
|
await rate.save({ transaction: t })
|
||||||
|
|
||||||
if (video.isOwned()) {
|
await federateVideoIfNeeded(video, false, t)
|
||||||
// Don't resend the activity to the sender
|
|
||||||
const exceptions = [ byActor ]
|
|
||||||
|
|
||||||
await forwardVideoRelatedActivity(activity, t, exceptions, video)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo, CacheFileObject } from '../../../../shared/models/activitypub'
|
import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo, CacheFileObject } from '../../../../shared/models/activitypub'
|
||||||
import { DislikeObject } from '../../../../shared/models/activitypub/objects'
|
import { DislikeObject } from '../../../../shared/models/activitypub/objects'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
|
@ -10,8 +11,8 @@ import { VideoRedundancyModel } from '../../../models/redundancy/video-redundanc
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorSignature } from '../../../types/models'
|
import { MActorSignature } from '../../../types/models'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
import { getOrCreateAPVideo } from '../videos'
|
import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
|
||||||
|
|
||||||
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
|
async function processUndoActivity (options: APProcessorOptions<ActivityUndo>) {
|
||||||
const { activity, byActor } = options
|
const { activity, byActor } = options
|
||||||
|
@ -55,23 +56,22 @@ export {
|
||||||
async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) {
|
async function processUndoLike (byActor: MActorSignature, activity: ActivityUndo) {
|
||||||
const likeActivity = activity.object as ActivityLike
|
const likeActivity = activity.object as ActivityLike
|
||||||
|
|
||||||
const { video } = await getOrCreateAPVideo({ videoObject: likeActivity.object })
|
const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: likeActivity.object })
|
||||||
|
// We don't care about likes of remote videos
|
||||||
|
if (!onlyVideo.isOwned()) return
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
if (!byActor.Account) throw new Error('Unknown account ' + byActor.url)
|
if (!byActor.Account) throw new Error('Unknown account ' + byActor.url)
|
||||||
|
|
||||||
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t)
|
||||||
const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, likeActivity.id, t)
|
const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, likeActivity.id, t)
|
||||||
if (!rate || rate.type !== 'like') throw new Error(`Unknown like by account ${byActor.Account.id} for video ${video.id}.`)
|
if (!rate || rate.type !== 'like') throw new Error(`Unknown like by account ${byActor.Account.id} for video ${video.id}.`)
|
||||||
|
|
||||||
await rate.destroy({ transaction: t })
|
await rate.destroy({ transaction: t })
|
||||||
await video.decrement('likes', { transaction: t })
|
await video.decrement('likes', { transaction: t })
|
||||||
|
|
||||||
if (video.isOwned()) {
|
video.likes--
|
||||||
// Don't resend the activity to the sender
|
await federateVideoIfNeeded(video, false, t)
|
||||||
const exceptions = [ byActor ]
|
|
||||||
|
|
||||||
await forwardVideoRelatedActivity(activity, t, exceptions, video)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,26 +80,27 @@ async function processUndoDislike (byActor: MActorSignature, activity: ActivityU
|
||||||
? activity.object
|
? activity.object
|
||||||
: activity.object.object as DislikeObject
|
: activity.object.object as DislikeObject
|
||||||
|
|
||||||
const { video } = await getOrCreateAPVideo({ videoObject: dislike.object })
|
const { video: onlyVideo } = await getOrCreateAPVideo({ videoObject: dislike.object })
|
||||||
|
// We don't care about likes of remote videos
|
||||||
|
if (!onlyVideo.isOwned()) return
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
if (!byActor.Account) throw new Error('Unknown account ' + byActor.url)
|
if (!byActor.Account) throw new Error('Unknown account ' + byActor.url)
|
||||||
|
|
||||||
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(onlyVideo.id, t)
|
||||||
const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, dislike.id, t)
|
const rate = await AccountVideoRateModel.loadByAccountAndVideoOrUrl(byActor.Account.id, video.id, dislike.id, t)
|
||||||
if (!rate || rate.type !== 'dislike') throw new Error(`Unknown dislike by account ${byActor.Account.id} for video ${video.id}.`)
|
if (!rate || rate.type !== 'dislike') throw new Error(`Unknown dislike by account ${byActor.Account.id} for video ${video.id}.`)
|
||||||
|
|
||||||
await rate.destroy({ transaction: t })
|
await rate.destroy({ transaction: t })
|
||||||
await video.decrement('dislikes', { transaction: t })
|
await video.decrement('dislikes', { transaction: t })
|
||||||
|
video.dislikes--
|
||||||
|
|
||||||
if (video.isOwned()) {
|
await federateVideoIfNeeded(video, false, t)
|
||||||
// Don't resend the activity to the sender
|
|
||||||
const exceptions = [ byActor ]
|
|
||||||
|
|
||||||
await forwardVideoRelatedActivity(activity, t, exceptions, video)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) {
|
async function processUndoCacheFile (byActor: MActorSignature, activity: ActivityUndo) {
|
||||||
const cacheFileObject = activity.object.object as CacheFileObject
|
const cacheFileObject = activity.object.object as CacheFileObject
|
||||||
|
|
||||||
|
@ -125,19 +126,6 @@ async function processUndoCacheFile (byActor: MActorSignature, activity: Activit
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) {
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
|
||||||
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
|
|
||||||
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
|
|
||||||
|
|
||||||
if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`)
|
|
||||||
|
|
||||||
await actorFollow.destroy({ transaction: t })
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) {
|
function processUndoAnnounce (byActor: MActorSignature, announceActivity: ActivityAnnounce) {
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
|
const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
|
||||||
|
@ -155,3 +143,18 @@ function processUndoAnnounce (byActor: MActorSignature, announceActivity: Activi
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function processUndoFollow (follower: MActorSignature, followActivity: ActivityFollow) {
|
||||||
|
return sequelizeTypescript.transaction(async t => {
|
||||||
|
const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
|
||||||
|
const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
|
||||||
|
|
||||||
|
if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`)
|
||||||
|
|
||||||
|
await actorFollow.destroy({ transaction: t })
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { MActorFull, MActorSignature } from '../../../types/models'
|
||||||
import { APActorUpdater } from '../actors/updater'
|
import { APActorUpdater } from '../actors/updater'
|
||||||
import { createOrUpdateCacheFile } from '../cache-file'
|
import { createOrUpdateCacheFile } from '../cache-file'
|
||||||
import { createOrUpdateVideoPlaylist } from '../playlists'
|
import { createOrUpdateVideoPlaylist } from '../playlists'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
import { APVideoUpdater, getOrCreateAPVideo } from '../videos'
|
import { APVideoUpdater, getOrCreateAPVideo } from '../videos'
|
||||||
|
|
||||||
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
|
async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { VideoViews } from '@server/lib/video-views'
|
||||||
import { ActivityView } from '../../../../shared/models/activitypub'
|
import { ActivityView } from '../../../../shared/models/activitypub'
|
||||||
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
import { APProcessorOptions } from '../../../types/activitypub-processor.model'
|
||||||
import { MActorSignature } from '../../../types/models'
|
import { MActorSignature } from '../../../types/models'
|
||||||
import { forwardVideoRelatedActivity } from '../send/utils'
|
import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
|
||||||
import { getOrCreateAPVideo } from '../videos'
|
import { getOrCreateAPVideo } from '../videos'
|
||||||
|
|
||||||
async function processViewActivity (options: APProcessorOptions<ActivityView>) {
|
async function processViewActivity (options: APProcessorOptions<ActivityView>) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub'
|
import { ActivityAccept, ActivityFollow } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { MActor, MActorFollowActors } from '../../../types/models'
|
import { MActor, MActorFollowActors } from '../../../types/models'
|
||||||
import { getLocalActorFollowAcceptActivityPubUrl } from '../url'
|
import { getLocalActorFollowAcceptActivityPubUrl } from '../url'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './shared/send-utils'
|
||||||
|
|
||||||
function sendAccept (actorFollow: MActorFollowActors) {
|
function sendAccept (actorFollow: MActorFollowActors) {
|
||||||
const follower = actorFollow.ActorFollower
|
const follower = actorFollow.ActorFollower
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
|
import { ActivityAnnounce, ActivityAudience } from '@shared/models'
|
||||||
import { broadcastToFollowers } from './utils'
|
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience'
|
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { MActorLight, MVideo } from '../../../types/models'
|
import { MActorLight, MVideo } from '../../../types/models'
|
||||||
import { MVideoShare } from '../../../types/models/video'
|
import { MVideoShare } from '../../../types/models/video'
|
||||||
|
import { audiencify, getAudience } from '../audience'
|
||||||
|
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf } from './shared'
|
||||||
|
import { broadcastToFollowers } from './shared/send-utils'
|
||||||
|
|
||||||
async function buildAnnounceWithVideoAudience (
|
async function buildAnnounceWithVideoAudience (
|
||||||
byActor: MActorLight,
|
byActor: MActorLight,
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
import { ActivityAudience, ActivityCreate, ContextType, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
|
||||||
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience'
|
|
||||||
import { logger, loggerTagsFactory } from '../../../helpers/logger'
|
import { logger, loggerTagsFactory } from '../../../helpers/logger'
|
||||||
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import {
|
import {
|
||||||
MActorLight,
|
MActorLight,
|
||||||
MCommentOwnerVideo,
|
MCommentOwnerVideo,
|
||||||
|
@ -15,8 +12,16 @@ import {
|
||||||
MVideoRedundancyFileVideo,
|
MVideoRedundancyFileVideo,
|
||||||
MVideoRedundancyStreamingPlaylistVideo
|
MVideoRedundancyStreamingPlaylistVideo
|
||||||
} from '../../../types/models'
|
} from '../../../types/models'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { audiencify, getAudience } from '../audience'
|
||||||
import { ContextType } from '@shared/models/activitypub/context'
|
import {
|
||||||
|
broadcastToActors,
|
||||||
|
broadcastToFollowers,
|
||||||
|
getActorsInvolvedInVideo,
|
||||||
|
getAudienceFromFollowersOf,
|
||||||
|
getVideoCommentAudience,
|
||||||
|
sendVideoRelatedActivity,
|
||||||
|
unicastTo
|
||||||
|
} from './shared'
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('ap', 'create')
|
const lTags = loggerTagsFactory('ap', 'create')
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityDelete } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActorModel } from '../../../models/actor/actor'
|
import { ActorModel } from '../../../models/actor/actor'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { MActorUrl } from '../../../types/models'
|
import { MActorUrl } from '../../../types/models'
|
||||||
import { MCommentOwnerVideo, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../types/models/video'
|
import { MCommentOwnerVideo, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../types/models/video'
|
||||||
import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
|
import { audiencify } from '../audience'
|
||||||
import { getDeleteActivityPubUrl } from '../url'
|
import { getDeleteActivityPubUrl } from '../url'
|
||||||
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
import { getActorsInvolvedInVideo, getVideoCommentAudience } from './shared'
|
||||||
|
import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './shared/send-utils'
|
||||||
|
|
||||||
async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) {
|
async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) {
|
||||||
logger.info('Creating job to broadcast delete of video %s.', video.url)
|
logger.info('Creating job to broadcast delete of video %s.', video.url)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { getVideoDislikeActivityPubUrlByLocalActor } from '../url'
|
import { ActivityAudience, ActivityDislike } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub'
|
|
||||||
import { sendVideoRelatedActivity } from './utils'
|
|
||||||
import { audiencify, getAudience } from '../audience'
|
|
||||||
import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models'
|
import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models'
|
||||||
|
import { audiencify, getAudience } from '../audience'
|
||||||
|
import { getVideoDislikeActivityPubUrlByLocalActor } from '../url'
|
||||||
|
import { sendVideoActivityToOrigin } from './shared/send-utils'
|
||||||
|
|
||||||
function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
||||||
logger.info('Creating job to dislike %s.', video.url)
|
logger.info('Creating job to dislike %s.', video.url)
|
||||||
|
@ -15,7 +15,7 @@ function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction
|
||||||
return buildDislikeActivity(url, byActor, video, audience)
|
return buildDislikeActivity(url, byActor, video, audience)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
|
return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t })
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike {
|
function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityFlag } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { MAbuseAP, MAccountLight, MActor } from '../../../types/models'
|
import { MAbuseAP, MAccountLight, MActor } from '../../../types/models'
|
||||||
import { audiencify, getAudience } from '../audience'
|
import { audiencify, getAudience } from '../audience'
|
||||||
import { getLocalAbuseActivityPubUrl } from '../url'
|
import { getLocalAbuseActivityPubUrl } from '../url'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './shared/send-utils'
|
||||||
|
|
||||||
function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLight, t: Transaction) {
|
function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLight, t: Transaction) {
|
||||||
if (!flaggedAccount.Actor.serverId) return // Local user
|
if (!flaggedAccount.Actor.serverId) return // Local user
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityFollow } from '../../../../shared/models/activitypub'
|
import { ActivityFollow } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { MActor, MActorFollowActors } from '../../../types/models'
|
import { MActor, MActorFollowActors } from '../../../types/models'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './shared/send-utils'
|
||||||
|
|
||||||
function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
|
function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
|
||||||
const me = actorFollow.ActorFollower
|
const me = actorFollow.ActorFollower
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityLike } from '@shared/models'
|
||||||
import { getVideoLikeActivityPubUrlByLocalActor } from '../url'
|
|
||||||
import { sendVideoRelatedActivity } from './utils'
|
|
||||||
import { audiencify, getAudience } from '../audience'
|
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models'
|
import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models'
|
||||||
|
import { audiencify, getAudience } from '../audience'
|
||||||
|
import { getVideoLikeActivityPubUrlByLocalActor } from '../url'
|
||||||
|
import { sendVideoActivityToOrigin } from './shared/send-utils'
|
||||||
|
|
||||||
function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
||||||
logger.info('Creating job to like %s.', video.url)
|
logger.info('Creating job to like %s.', video.url)
|
||||||
|
@ -15,7 +15,7 @@ function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
||||||
return buildLikeActivity(url, byActor, video, audience)
|
return buildLikeActivity(url, byActor, video, audience)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t })
|
return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t })
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike {
|
function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub'
|
import { ActivityFollow, ActivityReject } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { MActor } from '../../../types/models'
|
import { MActor } from '../../../types/models'
|
||||||
import { getLocalActorFollowRejectActivityPubUrl } from '../url'
|
import { getLocalActorFollowRejectActivityPubUrl } from '../url'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { unicastTo } from './utils'
|
import { unicastTo } from './shared/send-utils'
|
||||||
|
|
||||||
function sendReject (followUrl: string, follower: MActor, following: MActor) {
|
function sendReject (followUrl: string, follower: MActor, following: MActor) {
|
||||||
if (!follower.serverId) { // This should never happen
|
if (!follower.serverId) { // This should never happen
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
ActivityFollow,
|
ActivityFollow,
|
||||||
ActivityLike,
|
ActivityLike,
|
||||||
ActivityUndo
|
ActivityUndo
|
||||||
} from '../../../../shared/models/activitypub'
|
} from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import {
|
import {
|
||||||
|
@ -27,7 +27,7 @@ import { buildCreateActivity } from './send-create'
|
||||||
import { buildDislikeActivity } from './send-dislike'
|
import { buildDislikeActivity } from './send-dislike'
|
||||||
import { buildFollowActivity } from './send-follow'
|
import { buildFollowActivity } from './send-follow'
|
||||||
import { buildLikeActivity } from './send-like'
|
import { buildLikeActivity } from './send-like'
|
||||||
import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils'
|
import { broadcastToFollowers, sendVideoActivityToOrigin, sendVideoRelatedActivity, unicastTo } from './shared/send-utils'
|
||||||
|
|
||||||
function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
|
function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
|
||||||
const me = actorFollow.ActorFollower
|
const me = actorFollow.ActorFollower
|
||||||
|
@ -46,6 +46,8 @@ function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
|
||||||
t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
|
t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
|
async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
|
||||||
logger.info('Creating job to undo announce %s.', videoShare.url)
|
logger.info('Creating job to undo announce %s.', videoShare.url)
|
||||||
|
|
||||||
|
@ -58,24 +60,6 @@ async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare,
|
||||||
return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
|
return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
|
||||||
logger.info('Creating job to undo a like of video %s.', video.url)
|
|
||||||
|
|
||||||
const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video)
|
|
||||||
const likeActivity = buildLikeActivity(likeUrl, byActor, video)
|
|
||||||
|
|
||||||
return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
|
||||||
logger.info('Creating job to undo a dislike of video %s.', video.url)
|
|
||||||
|
|
||||||
const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video)
|
|
||||||
const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video)
|
|
||||||
|
|
||||||
return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) {
|
async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) {
|
||||||
logger.info('Creating job to undo cache file %s.', redundancyModel.url)
|
logger.info('Creating job to undo cache file %s.', redundancyModel.url)
|
||||||
|
|
||||||
|
@ -93,6 +77,26 @@ async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedund
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
||||||
|
logger.info('Creating job to undo a like of video %s.', video.url)
|
||||||
|
|
||||||
|
const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video)
|
||||||
|
const likeActivity = buildLikeActivity(likeUrl, byActor, video)
|
||||||
|
|
||||||
|
return sendUndoVideoToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
|
||||||
|
logger.info('Creating job to undo a dislike of video %s.', video.url)
|
||||||
|
|
||||||
|
const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video)
|
||||||
|
const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video)
|
||||||
|
|
||||||
|
return sendUndoVideoToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
sendUndoFollow,
|
sendUndoFollow,
|
||||||
sendUndoLike,
|
sendUndoLike,
|
||||||
|
@ -126,7 +130,7 @@ async function sendUndoVideoRelatedActivity (options: {
|
||||||
byActor: MActor
|
byActor: MActor
|
||||||
video: MVideoAccountLight
|
video: MVideoAccountLight
|
||||||
url: string
|
url: string
|
||||||
activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce
|
activity: ActivityFollow | ActivityCreate | ActivityAnnounce
|
||||||
transaction: Transaction
|
transaction: Transaction
|
||||||
}) {
|
}) {
|
||||||
const activityBuilder = (audience: ActivityAudience) => {
|
const activityBuilder = (audience: ActivityAudience) => {
|
||||||
|
@ -137,3 +141,19 @@ async function sendUndoVideoRelatedActivity (options: {
|
||||||
|
|
||||||
return sendVideoRelatedActivity(activityBuilder, options)
|
return sendVideoRelatedActivity(activityBuilder, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendUndoVideoToOriginActivity (options: {
|
||||||
|
byActor: MActor
|
||||||
|
video: MVideoAccountLight
|
||||||
|
url: string
|
||||||
|
activity: ActivityLike | ActivityDislike
|
||||||
|
transaction: Transaction
|
||||||
|
}) {
|
||||||
|
const activityBuilder = (audience: ActivityAudience) => {
|
||||||
|
const undoUrl = getUndoActivityPubUrl(options.url)
|
||||||
|
|
||||||
|
return undoActivityData(undoUrl, options.byActor, options.activity, audience)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendVideoActivityToOrigin(activityBuilder, options)
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
import { ActivityAudience, ActivityUpdate, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
|
||||||
|
import { logger } from '../../../helpers/logger'
|
||||||
import { AccountModel } from '../../../models/account/account'
|
import { AccountModel } from '../../../models/account/account'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
import { getUpdateActivityPubUrl } from '../url'
|
|
||||||
import { broadcastToFollowers, sendVideoRelatedActivity } from './utils'
|
|
||||||
import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience'
|
|
||||||
import { logger } from '../../../helpers/logger'
|
|
||||||
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
|
|
||||||
import {
|
import {
|
||||||
MAccountDefault,
|
MAccountDefault,
|
||||||
MActor,
|
MActor,
|
||||||
|
@ -19,7 +15,10 @@ import {
|
||||||
MVideoPlaylistFull,
|
MVideoPlaylistFull,
|
||||||
MVideoRedundancyVideo
|
MVideoRedundancyVideo
|
||||||
} from '../../../types/models'
|
} from '../../../types/models'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { audiencify, getAudience } from '../audience'
|
||||||
|
import { getUpdateActivityPubUrl } from '../url'
|
||||||
|
import { getActorsInvolvedInVideo } from './shared'
|
||||||
|
import { broadcastToFollowers, sendVideoRelatedActivity } from './shared/send-utils'
|
||||||
|
|
||||||
async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
|
async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
|
||||||
const video = videoArg as MVideoAP
|
const video = videoArg as MVideoAP
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { VideoViews } from '@server/lib/video-views'
|
import { VideoViews } from '@server/lib/video-views'
|
||||||
import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models'
|
import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models'
|
||||||
import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub'
|
import { ActivityAudience, ActivityView } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ActorModel } from '../../../models/actor/actor'
|
import { ActorModel } from '../../../models/actor/actor'
|
||||||
import { audiencify, getAudience } from '../audience'
|
import { audiencify, getAudience } from '../audience'
|
||||||
import { getLocalVideoViewActivityPubUrl } from '../url'
|
import { getLocalVideoViewActivityPubUrl } from '../url'
|
||||||
import { sendVideoRelatedActivity } from './utils'
|
import { sendVideoRelatedActivity } from './shared/send-utils'
|
||||||
|
|
||||||
async function sendView (byActor: ActorModel, video: MVideoImmutable, t: Transaction) {
|
async function sendView (byActor: ActorModel, video: MVideoImmutable, t: Transaction) {
|
||||||
logger.info('Creating job to send view of %s.', video.url)
|
logger.info('Creating job to send view of %s.', video.url)
|
||||||
|
|
74
server/lib/activitypub/send/shared/audience-utils.ts
Normal file
74
server/lib/activitypub/send/shared/audience-utils.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { Transaction } from 'sequelize/dist'
|
||||||
|
import { ACTIVITY_PUB } from '@server/initializers/constants'
|
||||||
|
import { ActorModel } from '@server/models/actor/actor'
|
||||||
|
import { VideoModel } from '@server/models/video/video'
|
||||||
|
import { VideoShareModel } from '@server/models/video/video-share'
|
||||||
|
import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '@server/types/models'
|
||||||
|
import { ActivityAudience } from '@shared/models'
|
||||||
|
|
||||||
|
function getOriginVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[] = []): ActivityAudience {
|
||||||
|
return {
|
||||||
|
to: [ accountActor.url ],
|
||||||
|
cc: actorsInvolvedInVideo.map(a => a.followersUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVideoCommentAudience (
|
||||||
|
videoComment: MCommentOwnerVideo,
|
||||||
|
threadParentComments: MCommentOwner[],
|
||||||
|
actorsInvolvedInVideo: MActorFollowersUrl[],
|
||||||
|
isOrigin = false
|
||||||
|
): ActivityAudience {
|
||||||
|
const to = [ ACTIVITY_PUB.PUBLIC ]
|
||||||
|
const cc: string[] = []
|
||||||
|
|
||||||
|
// Owner of the video we comment
|
||||||
|
if (isOrigin === false) {
|
||||||
|
cc.push(videoComment.Video.VideoChannel.Account.Actor.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Followers of the poster
|
||||||
|
cc.push(videoComment.Account.Actor.followersUrl)
|
||||||
|
|
||||||
|
// Send to actors we reply to
|
||||||
|
for (const parentComment of threadParentComments) {
|
||||||
|
if (parentComment.isDeleted()) continue
|
||||||
|
|
||||||
|
cc.push(parentComment.Account.Actor.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
to,
|
||||||
|
cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience {
|
||||||
|
return {
|
||||||
|
to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
|
||||||
|
cc: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) {
|
||||||
|
const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t)
|
||||||
|
|
||||||
|
const videoAll = video as VideoModel
|
||||||
|
|
||||||
|
const videoActor = videoAll.VideoChannel?.Account
|
||||||
|
? videoAll.VideoChannel.Account.Actor
|
||||||
|
: await ActorModel.loadFromAccountByVideoId(video.id, t)
|
||||||
|
|
||||||
|
actors.push(videoActor)
|
||||||
|
|
||||||
|
return actors
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
getOriginVideoAudience,
|
||||||
|
getActorsInvolvedInVideo,
|
||||||
|
getAudienceFromFollowersOf,
|
||||||
|
getVideoCommentAudience
|
||||||
|
}
|
2
server/lib/activitypub/send/shared/index.ts
Normal file
2
server/lib/activitypub/send/shared/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './audience-utils'
|
||||||
|
export * from './send-utils'
|
|
@ -1,15 +1,15 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache'
|
import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache'
|
||||||
import { getServerActor } from '@server/models/application/application'
|
import { getServerActor } from '@server/models/application/application'
|
||||||
|
import { Activity, ActivityAudience } from '@shared/models'
|
||||||
import { ContextType } from '@shared/models/activitypub/context'
|
import { ContextType } from '@shared/models/activitypub/context'
|
||||||
import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
|
import { afterCommitIfTransaction } from '../../../../helpers/database-utils'
|
||||||
import { afterCommitIfTransaction } from '../../../helpers/database-utils'
|
import { logger } from '../../../../helpers/logger'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { ActorModel } from '../../../../models/actor/actor'
|
||||||
import { ActorModel } from '../../../models/actor/actor'
|
import { ActorFollowModel } from '../../../../models/actor/actor-follow'
|
||||||
import { ActorFollowModel } from '../../../models/actor/actor-follow'
|
import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../../types/models'
|
||||||
import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models'
|
import { JobQueue } from '../../../job-queue'
|
||||||
import { JobQueue } from '../../job-queue'
|
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getOriginVideoAudience } from './audience-utils'
|
||||||
import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
|
|
||||||
|
|
||||||
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
|
async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
|
||||||
byActor: MActorLight
|
byActor: MActorLight
|
||||||
|
@ -23,16 +23,7 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
|
||||||
|
|
||||||
// Send to origin
|
// Send to origin
|
||||||
if (video.isOwned() === false) {
|
if (video.isOwned() === false) {
|
||||||
let accountActor: MActorLight = (video as MVideoAccountLight).VideoChannel?.Account?.Actor
|
return sendVideoActivityToOrigin(activityBuilder, options)
|
||||||
|
|
||||||
if (!accountActor) accountActor = await ActorModel.loadAccountActorByVideoId(video.id, transaction)
|
|
||||||
|
|
||||||
const audience = getRemoteVideoAudience(accountActor, actorsInvolvedInVideo)
|
|
||||||
const activity = activityBuilder(audience)
|
|
||||||
|
|
||||||
return afterCommitIfTransaction(transaction, () => {
|
|
||||||
return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to followers
|
// Send to followers
|
||||||
|
@ -44,6 +35,30 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
|
||||||
return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType)
|
return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAudience) => Activity, options: {
|
||||||
|
byActor: MActorLight
|
||||||
|
video: MVideoImmutable | MVideoAccountLight
|
||||||
|
actorsInvolvedInVideo?: MActorLight[]
|
||||||
|
transaction?: Transaction
|
||||||
|
contextType?: ContextType
|
||||||
|
}) {
|
||||||
|
const { byActor, video, actorsInvolvedInVideo, transaction, contextType } = options
|
||||||
|
|
||||||
|
if (video.isOwned()) throw new Error('Cannot send activity to owned video origin ' + video.url)
|
||||||
|
|
||||||
|
let accountActor: MActorLight = (video as MVideoAccountLight).VideoChannel?.Account?.Actor
|
||||||
|
if (!accountActor) accountActor = await ActorModel.loadAccountActorByVideoId(video.id, transaction)
|
||||||
|
|
||||||
|
const audience = getOriginVideoAudience(accountActor, actorsInvolvedInVideo)
|
||||||
|
const activity = activityBuilder(audience)
|
||||||
|
|
||||||
|
return afterCommitIfTransaction(transaction, () => {
|
||||||
|
return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function forwardVideoRelatedActivity (
|
async function forwardVideoRelatedActivity (
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
|
@ -92,6 +107,8 @@ async function forwardActivity (
|
||||||
return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }))
|
return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function broadcastToFollowers (
|
async function broadcastToFollowers (
|
||||||
data: any,
|
data: any,
|
||||||
byActor: MActorId,
|
byActor: MActorId,
|
||||||
|
@ -177,6 +194,7 @@ export {
|
||||||
unicastTo,
|
unicastTo,
|
||||||
forwardActivity,
|
forwardActivity,
|
||||||
broadcastToActors,
|
broadcastToActors,
|
||||||
|
sendVideoActivityToOrigin,
|
||||||
forwardVideoRelatedActivity,
|
forwardVideoRelatedActivity,
|
||||||
sendVideoRelatedActivity
|
sendVideoRelatedActivity
|
||||||
}
|
}
|
|
@ -87,7 +87,7 @@ async function tryToResolveThreadFromVideo (params: ResolveThreadParams) {
|
||||||
|
|
||||||
// Maybe it's a reply to a video?
|
// Maybe it's a reply to a video?
|
||||||
// If yes, it's done: we resolved all the thread
|
// If yes, it's done: we resolved all the thread
|
||||||
const syncParam = { likes: true, dislikes: true, shares: true, comments: false, thumbnail: true, refreshVideo: false }
|
const syncParam = { rates: true, shares: true, comments: false, thumbnail: true, refreshVideo: false }
|
||||||
const { video } = await getOrCreateAPVideo({ videoObject: url, syncParam })
|
const { video } = await getOrCreateAPVideo({ videoObject: url, syncParam })
|
||||||
|
|
||||||
if (video.isOwned() && !video.hasPrivacyForFederation()) {
|
if (video.isOwned() && !video.hasPrivacyForFederation()) {
|
||||||
|
|
|
@ -1,36 +1,48 @@
|
||||||
import { map } from 'bluebird'
|
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { doJSONRequest } from '@server/helpers/requests'
|
|
||||||
import { VideoRateType } from '../../../shared/models/videos'
|
import { VideoRateType } from '../../../shared/models/videos'
|
||||||
import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
|
import { MAccountActor, MActorUrl, MVideoAccountLight, MVideoFullLight, MVideoId } from '../../types/models'
|
||||||
import { logger, loggerTagsFactory } from '../../helpers/logger'
|
|
||||||
import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
|
|
||||||
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
|
|
||||||
import { MAccountActor, MActorUrl, MVideo, MVideoAccountLight, MVideoId } from '../../types/models'
|
|
||||||
import { getOrCreateAPActor } from './actors'
|
|
||||||
import { sendLike, sendUndoDislike, sendUndoLike } from './send'
|
import { sendLike, sendUndoDislike, sendUndoLike } from './send'
|
||||||
import { sendDislike } from './send/send-dislike'
|
import { sendDislike } from './send/send-dislike'
|
||||||
import { getVideoDislikeActivityPubUrlByLocalActor, getVideoLikeActivityPubUrlByLocalActor } from './url'
|
import { getVideoDislikeActivityPubUrlByLocalActor, getVideoLikeActivityPubUrlByLocalActor } from './url'
|
||||||
|
import { federateVideoIfNeeded } from './videos'
|
||||||
const lTags = loggerTagsFactory('ap', 'video-rate', 'create')
|
|
||||||
|
|
||||||
async function createRates (ratesUrl: string[], video: MVideo, rate: VideoRateType) {
|
|
||||||
await map(ratesUrl, async rateUrl => {
|
|
||||||
try {
|
|
||||||
await createRate(rateUrl, video, rate)
|
|
||||||
} catch (err) {
|
|
||||||
logger.info('Cannot add rate %s.', rateUrl, { err, ...lTags(rateUrl, video.uuid, video.url) })
|
|
||||||
}
|
|
||||||
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendVideoRateChange (
|
async function sendVideoRateChange (
|
||||||
|
account: MAccountActor,
|
||||||
|
video: MVideoFullLight,
|
||||||
|
likes: number,
|
||||||
|
dislikes: number,
|
||||||
|
t: Transaction
|
||||||
|
) {
|
||||||
|
if (video.isOwned()) return federateVideoIfNeeded(video, false, t)
|
||||||
|
|
||||||
|
return sendVideoRateChangeToOrigin(account, video, likes, dislikes, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
|
||||||
|
return rateType === 'like'
|
||||||
|
? getVideoLikeActivityPubUrlByLocalActor(actor, video)
|
||||||
|
: getVideoDislikeActivityPubUrlByLocalActor(actor, video)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
getLocalRateUrl,
|
||||||
|
sendVideoRateChange
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function sendVideoRateChangeToOrigin (
|
||||||
account: MAccountActor,
|
account: MAccountActor,
|
||||||
video: MVideoAccountLight,
|
video: MVideoAccountLight,
|
||||||
likes: number,
|
likes: number,
|
||||||
dislikes: number,
|
dislikes: number,
|
||||||
t: Transaction
|
t: Transaction
|
||||||
) {
|
) {
|
||||||
|
// Local video, we don't need to send like
|
||||||
|
if (video.isOwned()) return
|
||||||
|
|
||||||
const actor = account.Actor
|
const actor = account.Actor
|
||||||
|
|
||||||
// Keep the order: first we undo and then we create
|
// Keep the order: first we undo and then we create
|
||||||
|
@ -45,46 +57,3 @@ async function sendVideoRateChange (
|
||||||
// Dislike
|
// Dislike
|
||||||
if (dislikes > 0) await sendDislike(actor, video, t)
|
if (dislikes > 0) await sendDislike(actor, video, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLocalRateUrl (rateType: VideoRateType, actor: MActorUrl, video: MVideoId) {
|
|
||||||
return rateType === 'like'
|
|
||||||
? getVideoLikeActivityPubUrlByLocalActor(actor, video)
|
|
||||||
: getVideoDislikeActivityPubUrlByLocalActor(actor, video)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export {
|
|
||||||
getLocalRateUrl,
|
|
||||||
createRates,
|
|
||||||
sendVideoRateChange
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async function createRate (rateUrl: string, video: MVideo, rate: VideoRateType) {
|
|
||||||
// Fetch url
|
|
||||||
const { body } = await doJSONRequest<any>(rateUrl, { activityPub: true })
|
|
||||||
if (!body || !body.actor) throw new Error('Body or body actor is invalid')
|
|
||||||
|
|
||||||
const actorUrl = getAPId(body.actor)
|
|
||||||
if (checkUrlsSameHost(actorUrl, rateUrl) !== true) {
|
|
||||||
throw new Error(`Rate url ${rateUrl} has not the same host than actor url ${actorUrl}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkUrlsSameHost(body.id, rateUrl) !== true) {
|
|
||||||
throw new Error(`Rate url ${rateUrl} host is different from the AP object id ${body.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const actor = await getOrCreateAPActor(actorUrl)
|
|
||||||
|
|
||||||
const entry = {
|
|
||||||
videoId: video.id,
|
|
||||||
accountId: actor.Account.id,
|
|
||||||
type: rate,
|
|
||||||
url: body.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Video "likes"/"dislikes" will be updated by the caller
|
|
||||||
await AccountVideoRateModel.upsert(entry)
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ async function getOrCreateAPVideo (
|
||||||
options: GetVideoParamAll | GetVideoParamImmutable | GetVideoParamOther
|
options: GetVideoParamAll | GetVideoParamImmutable | GetVideoParamOther
|
||||||
): GetVideoResult<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail | MVideoImmutable> {
|
): GetVideoResult<MVideoAccountLightBlacklistAllFiles | MVideoThumbnail | MVideoImmutable> {
|
||||||
// Default params
|
// Default params
|
||||||
const syncParam = options.syncParam || { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
|
const syncParam = options.syncParam || { rates: true, shares: true, comments: true, thumbnail: true, refreshVideo: false }
|
||||||
const fetchType = options.fetchType || 'all'
|
const fetchType = options.fetchType || 'all'
|
||||||
const allowRefresh = options.allowRefresh !== false
|
const allowRefresh = options.allowRefresh !== false
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
|
import { runInReadCommittedTransaction } from '@server/helpers/database-utils'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
|
import { doJSONRequest } from '@server/helpers/requests'
|
||||||
import { JobQueue } from '@server/lib/job-queue'
|
import { JobQueue } from '@server/lib/job-queue'
|
||||||
import { AccountVideoRateModel } from '@server/models/account/account-video-rate'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { VideoCommentModel } from '@server/models/video/video-comment'
|
import { VideoCommentModel } from '@server/models/video/video-comment'
|
||||||
import { VideoShareModel } from '@server/models/video/video-share'
|
import { VideoShareModel } from '@server/models/video/video-share'
|
||||||
import { MVideo } from '@server/types/models'
|
import { MVideo } from '@server/types/models'
|
||||||
import { ActivitypubHttpFetcherPayload, VideoObject } from '@shared/models'
|
import { ActivitypubHttpFetcherPayload, ActivityPubOrderedCollection, VideoObject } from '@shared/models'
|
||||||
import { crawlCollectionPage } from '../../crawl'
|
import { crawlCollectionPage } from '../../crawl'
|
||||||
import { addVideoShares } from '../../share'
|
import { addVideoShares } from '../../share'
|
||||||
import { addVideoComments } from '../../video-comments'
|
import { addVideoComments } from '../../video-comments'
|
||||||
import { createRates } from '../../video-rates'
|
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('ap', 'video')
|
const lTags = loggerTagsFactory('ap', 'video')
|
||||||
|
|
||||||
type SyncParam = {
|
type SyncParam = {
|
||||||
likes: boolean
|
rates: boolean
|
||||||
dislikes: boolean
|
|
||||||
shares: boolean
|
shares: boolean
|
||||||
comments: boolean
|
comments: boolean
|
||||||
thumbnail: boolean
|
thumbnail: boolean
|
||||||
|
@ -24,45 +24,57 @@ type SyncParam = {
|
||||||
async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoObject, syncParam: SyncParam) {
|
async function syncVideoExternalAttributes (video: MVideo, fetchedVideo: VideoObject, syncParam: SyncParam) {
|
||||||
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
|
||||||
|
|
||||||
await syncRates('like', video, fetchedVideo, syncParam.likes)
|
const ratePromise = updateVideoRates(video, fetchedVideo)
|
||||||
await syncRates('dislike', video, fetchedVideo, syncParam.dislikes)
|
if (syncParam.rates) await ratePromise
|
||||||
|
|
||||||
await syncShares(video, fetchedVideo, syncParam.shares)
|
await syncShares(video, fetchedVideo, syncParam.shares)
|
||||||
|
|
||||||
await syncComments(video, fetchedVideo, syncParam.comments)
|
await syncComments(video, fetchedVideo, syncParam.comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateVideoRates (video: MVideo, fetchedVideo: VideoObject) {
|
||||||
|
const [ likes, dislikes ] = await Promise.all([
|
||||||
|
getRatesCount('like', video, fetchedVideo),
|
||||||
|
getRatesCount('dislike', video, fetchedVideo)
|
||||||
|
])
|
||||||
|
|
||||||
|
return runInReadCommittedTransaction(async t => {
|
||||||
|
await VideoModel.updateRatesOf(video.id, 'like', likes, t)
|
||||||
|
await VideoModel.updateRatesOf(video.id, 'dislike', dislikes, t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
SyncParam,
|
SyncParam,
|
||||||
syncVideoExternalAttributes
|
syncVideoExternalAttributes,
|
||||||
|
updateVideoRates
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function createJob (payload: ActivitypubHttpFetcherPayload) {
|
async function getRatesCount (type: 'like' | 'dislike', video: MVideo, fetchedVideo: VideoObject) {
|
||||||
return JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncRates (type: 'like' | 'dislike', video: MVideo, fetchedVideo: VideoObject, isSync: boolean) {
|
|
||||||
const uri = type === 'like'
|
const uri = type === 'like'
|
||||||
? fetchedVideo.likes
|
? fetchedVideo.likes
|
||||||
: fetchedVideo.dislikes
|
: fetchedVideo.dislikes
|
||||||
|
|
||||||
if (!isSync) {
|
logger.info('Sync %s of video %s', type, video.url)
|
||||||
const jobType = type === 'like'
|
const options = { activityPub: true }
|
||||||
? 'video-likes'
|
|
||||||
: 'video-dislikes'
|
|
||||||
|
|
||||||
return createJob({ uri, videoId: video.id, type: jobType })
|
const response = await doJSONRequest<ActivityPubOrderedCollection<any>>(uri, options)
|
||||||
|
const totalItems = response.body.totalItems
|
||||||
|
|
||||||
|
if (isNaN(totalItems)) {
|
||||||
|
logger.error('Cannot sync %s of video %s, totalItems is not a number', type, video.url, { body: response.body })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = items => createRates(items, video, type)
|
return totalItems
|
||||||
const cleaner = crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, type, crawlStartDate)
|
}
|
||||||
|
|
||||||
return crawlCollectionPage<string>(uri, handler, cleaner)
|
function createJob (payload: ActivitypubHttpFetcherPayload) {
|
||||||
.catch(err => logger.error('Cannot add rate of video %s.', video.uuid, { err, rootUrl: uri, ...lTags(video.uuid, video.url) }))
|
return JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) {
|
function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
|
||||||
import { VideoLiveModel } from '@server/models/video/video-live'
|
import { VideoLiveModel } from '@server/models/video/video-live'
|
||||||
import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
|
import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
|
||||||
import { VideoObject, VideoPrivacy } from '@shared/models'
|
import { VideoObject, VideoPrivacy } from '@shared/models'
|
||||||
import { APVideoAbstractBuilder, getVideoAttributesFromObject } from './shared'
|
import { APVideoAbstractBuilder, getVideoAttributesFromObject, updateVideoRates } from './shared'
|
||||||
|
|
||||||
export class APVideoUpdater extends APVideoAbstractBuilder {
|
export class APVideoUpdater extends APVideoAbstractBuilder {
|
||||||
private readonly wasPrivateVideo: boolean
|
private readonly wasPrivateVideo: boolean
|
||||||
|
@ -74,6 +74,8 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
|
||||||
transaction: undefined
|
transaction: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await updateVideoRates(videoUpdated, this.videoObject)
|
||||||
|
|
||||||
// Notify our users?
|
// Notify our users?
|
||||||
if (this.wasPrivateVideo || this.wasUnlistedVideo) {
|
if (this.wasPrivateVideo || this.wasUnlistedVideo) {
|
||||||
Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated)
|
Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated)
|
||||||
|
|
|
@ -34,7 +34,7 @@ async function processActivityPubCleaner (_job: Job) {
|
||||||
if (result?.status === 'deleted') {
|
if (result?.status === 'deleted') {
|
||||||
const { videoId, type } = result.data
|
const { videoId, type } = result.data
|
||||||
|
|
||||||
await VideoModel.updateRatesOf(videoId, type, undefined)
|
await VideoModel.syncLocalRates(videoId, type, undefined)
|
||||||
}
|
}
|
||||||
}, { concurrency: AP_CLEANER.CONCURRENCY })
|
}, { concurrency: AP_CLEANER.CONCURRENCY })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Job } from 'bull'
|
import { Job } from 'bull'
|
||||||
import { ActivitypubHttpFetcherPayload, FetchType } from '@shared/models'
|
import { ActivitypubHttpFetcherPayload, FetchType } from '@shared/models'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
|
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { VideoCommentModel } from '../../../models/video/video-comment'
|
||||||
import { VideoShareModel } from '../../../models/video/video-share'
|
import { VideoShareModel } from '../../../models/video/video-share'
|
||||||
|
@ -11,7 +10,6 @@ import { createAccountPlaylists } from '../../activitypub/playlists'
|
||||||
import { processActivities } from '../../activitypub/process'
|
import { processActivities } from '../../activitypub/process'
|
||||||
import { addVideoShares } from '../../activitypub/share'
|
import { addVideoShares } from '../../activitypub/share'
|
||||||
import { addVideoComments } from '../../activitypub/video-comments'
|
import { addVideoComments } from '../../activitypub/video-comments'
|
||||||
import { createRates } from '../../activitypub/video-rates'
|
|
||||||
|
|
||||||
async function processActivityPubHttpFetcher (job: Job) {
|
async function processActivityPubHttpFetcher (job: Job) {
|
||||||
logger.info('Processing ActivityPub fetcher in job %d.', job.id)
|
logger.info('Processing ActivityPub fetcher in job %d.', job.id)
|
||||||
|
@ -23,16 +21,12 @@ async function processActivityPubHttpFetcher (job: Job) {
|
||||||
|
|
||||||
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
|
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
|
||||||
'activity': items => processActivities(items, { outboxUrl: payload.uri, fromFetch: true }),
|
'activity': items => processActivities(items, { outboxUrl: payload.uri, fromFetch: true }),
|
||||||
'video-likes': items => createRates(items, video, 'like'),
|
|
||||||
'video-dislikes': items => createRates(items, video, 'dislike'),
|
|
||||||
'video-shares': items => addVideoShares(items, video),
|
'video-shares': items => addVideoShares(items, video),
|
||||||
'video-comments': items => addVideoComments(items),
|
'video-comments': items => addVideoComments(items),
|
||||||
'account-playlists': items => createAccountPlaylists(items)
|
'account-playlists': items => createAccountPlaylists(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Promise<any> } = {
|
const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Promise<any> } = {
|
||||||
'video-likes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'like' as 'like', crawlStartDate),
|
|
||||||
'video-dislikes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'dislike' as 'dislike', crawlStartDate),
|
|
||||||
'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate),
|
'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate),
|
||||||
'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate)
|
'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export {
|
||||||
|
|
||||||
async function refreshVideo (videoUrl: string) {
|
async function refreshVideo (videoUrl: string) {
|
||||||
const fetchType = 'all' as 'all'
|
const fetchType = 'all' as 'all'
|
||||||
const syncParam = { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true }
|
const syncParam = { rates: true, shares: true, comments: true, thumbnail: true }
|
||||||
|
|
||||||
const videoFromDatabase = await loadVideoByUrl(videoUrl, fetchType)
|
const videoFromDatabase = await loadVideoByUrl(videoUrl, fetchType)
|
||||||
if (videoFromDatabase) {
|
if (videoFromDatabase) {
|
||||||
|
|
|
@ -352,7 +352,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
// We need more attributes and check if the video still exists
|
// We need more attributes and check if the video still exists
|
||||||
const getVideoOptions = {
|
const getVideoOptions = {
|
||||||
videoObject: videoUrl,
|
videoObject: videoUrl,
|
||||||
syncParam: { likes: false, dislikes: false, shares: false, comments: false, thumbnail: false, refreshVideo: true },
|
syncParam: { rates: false, shares: false, comments: false, thumbnail: false, refreshVideo: true },
|
||||||
fetchType: 'all' as 'all'
|
fetchType: 'all' as 'all'
|
||||||
}
|
}
|
||||||
const { video } = await getOrCreateAPVideo(getVideoOptions)
|
const { video } = await getOrCreateAPVideo(getVideoOptions)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { AttributesOnly } from '@shared/typescript-utils'
|
||||||
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
|
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
|
||||||
import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants'
|
import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants'
|
||||||
import { ActorModel } from '../actor/actor'
|
import { ActorModel } from '../actor/actor'
|
||||||
import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
|
import { getSort, throwIfNotValid } from '../utils'
|
||||||
import { VideoModel } from '../video/video'
|
import { VideoModel } from '../video/video'
|
||||||
import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
|
import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
|
||||||
import { AccountModel } from './account'
|
import { AccountModel } from './account'
|
||||||
|
@ -249,28 +249,6 @@ export class AccountVideoRateModel extends Model<Partial<AttributesOnly<AccountV
|
||||||
]).then(([ total, data ]) => ({ total, data }))
|
]).then(([ total, data ]) => ({ total, data }))
|
||||||
}
|
}
|
||||||
|
|
||||||
static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) {
|
|
||||||
return AccountVideoRateModel.sequelize.transaction(async t => {
|
|
||||||
const query = {
|
|
||||||
where: {
|
|
||||||
updatedAt: {
|
|
||||||
[Op.lt]: beforeUpdatedAt
|
|
||||||
},
|
|
||||||
videoId,
|
|
||||||
type,
|
|
||||||
accountId: {
|
|
||||||
[Op.notIn]: buildLocalAccountIdsIn()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transaction: t
|
|
||||||
}
|
|
||||||
|
|
||||||
await AccountVideoRateModel.destroy(query)
|
|
||||||
|
|
||||||
return VideoModel.updateRatesOf(videoId, type, t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
toFormattedJSON (this: MAccountVideoRateFormattable): AccountVideoRate {
|
toFormattedJSON (this: MAccountVideoRateFormattable): AccountVideoRate {
|
||||||
return {
|
return {
|
||||||
video: this.Video.toFormattedJSON(),
|
video: this.Video.toFormattedJSON(),
|
||||||
|
|
|
@ -1402,7 +1402,21 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateRatesOf (videoId: number, type: VideoRateType, t: Transaction) {
|
static updateRatesOf (videoId: number, type: VideoRateType, count: number, t: Transaction) {
|
||||||
|
const field = type === 'like'
|
||||||
|
? 'likes'
|
||||||
|
: 'dislikes'
|
||||||
|
|
||||||
|
const rawQuery = `UPDATE "video" SET "${field}" = :count WHERE "video"."id" = :videoId`
|
||||||
|
|
||||||
|
return AccountVideoRateModel.sequelize.query(rawQuery, {
|
||||||
|
transaction: t,
|
||||||
|
replacements: { videoId, rateType: type, count },
|
||||||
|
type: QueryTypes.UPDATE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static syncLocalRates (videoId: number, type: VideoRateType, t: Transaction) {
|
||||||
const field = type === 'like'
|
const field = type === 'like'
|
||||||
? 'likes'
|
? 'likes'
|
||||||
: 'dislikes'
|
: 'dislikes'
|
||||||
|
|
|
@ -606,8 +606,8 @@ describe('Test multiple servers', function () {
|
||||||
|
|
||||||
for (const baseVideo of baseVideos) {
|
for (const baseVideo of baseVideos) {
|
||||||
const sameVideo = data.find(video => video.name === baseVideo.name)
|
const sameVideo = data.find(video => video.name === baseVideo.name)
|
||||||
expect(baseVideo.likes).to.equal(sameVideo.likes)
|
expect(baseVideo.likes).to.equal(sameVideo.likes, `Likes of ${sameVideo.uuid} do not correspond`)
|
||||||
expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
|
expect(baseVideo.dislikes).to.equal(sameVideo.dislikes, `Dislikes of ${sameVideo.uuid} do not correspond`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
export * from './objects'
|
||||||
export * from './activity'
|
export * from './activity'
|
||||||
export * from './activitypub-actor'
|
export * from './activitypub-actor'
|
||||||
export * from './activitypub-collection'
|
export * from './activitypub-collection'
|
||||||
export * from './activitypub-ordered-collection'
|
export * from './activitypub-ordered-collection'
|
||||||
export * from './activitypub-root'
|
export * from './activitypub-root'
|
||||||
export * from './activitypub-signature'
|
export * from './activitypub-signature'
|
||||||
export * from './objects'
|
export * from './context'
|
||||||
export * from './webfinger'
|
export * from './webfinger'
|
||||||
|
|
|
@ -52,7 +52,7 @@ export type ActivitypubFollowPayload = {
|
||||||
assertIsChannel?: boolean
|
assertIsChannel?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists'
|
export type FetchType = 'activity' | 'video-shares' | 'video-comments' | 'account-playlists'
|
||||||
export type ActivitypubHttpFetcherPayload = {
|
export type ActivitypubHttpFetcherPayload = {
|
||||||
uri: string
|
uri: string
|
||||||
type: FetchType
|
type: FetchType
|
||||||
|
|
Loading…
Reference in New Issue
Block a user