Don't rehost announced video activities

This commit is contained in:
Chocobozzz 2018-01-26 15:49:57 +01:00
parent 7859b5800c
commit 4ba3b8ea1b
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
9 changed files with 124 additions and 53 deletions

View File

@ -100,7 +100,7 @@ async function videoController (req: express.Request, res: express.Response, nex
async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
const share = res.locals.videoShare as VideoShareModel const share = res.locals.videoShare as VideoShareModel
const object = await buildVideoAnnounceToFollowers(share.Actor, res.locals.video, undefined) const object = await buildVideoAnnounceToFollowers(share.Actor, share, res.locals.video, undefined)
return res.json(activityPubContextify(object)) return res.json(activityPubContextify(object))
} }

View File

@ -5,7 +5,6 @@ import { pageToStartAndCount } from '../../helpers/core-utils'
import { ACTIVITY_PUB } from '../../initializers/constants' import { ACTIVITY_PUB } from '../../initializers/constants'
import { announceActivityData, createActivityData } from '../../lib/activitypub/send' import { announceActivityData, createActivityData } from '../../lib/activitypub/send'
import { buildAudience } from '../../lib/activitypub/send/misc' import { buildAudience } from '../../lib/activitypub/send/misc'
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
import { asyncMiddleware, localAccountValidator } from '../../middlewares' import { asyncMiddleware, localAccountValidator } from '../../middlewares'
import { AccountModel } from '../../models/account/account' import { AccountModel } from '../../models/account/account'
import { ActorModel } from '../../models/activitypub/actor' import { ActorModel } from '../../models/activitypub/actor'
@ -48,9 +47,9 @@ async function outboxController (req: express.Request, res: express.Response, ne
// This is a shared video // This is a shared video
if (video.VideoShares !== undefined && video.VideoShares.length !== 0) { if (video.VideoShares !== undefined && video.VideoShares.length !== 0) {
const videoShare = video.VideoShares[0]
const announceAudience = buildAudience(followersMatrix[actor.id]) const announceAudience = buildAudience(followersMatrix[actor.id])
const url = getAnnounceActivityPubUrl(video.url, actor) const announceActivity = await announceActivityData(videoShare.url, actor, video.url, undefined, announceAudience)
const announceActivity = await announceActivityData(url, actor, video.url, undefined, announceAudience)
activities.push(announceActivity) activities.push(announceActivity)
} else { } else {

View File

@ -12,7 +12,7 @@ let config: IConfig = require('config')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const LAST_MIGRATION_VERSION = 180 const LAST_MIGRATION_VERSION = 185
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -196,6 +196,9 @@ const CONSTRAINTS_FIELDS = {
VIDEO_COMMENTS: { VIDEO_COMMENTS: {
TEXT: { min: 2, max: 3000 }, // Length TEXT: { min: 2, max: 3000 }, // Length
URL: { min: 3, max: 2000 } // Length URL: { min: 3, max: 2000 } // Length
},
VIDEO_SHARE: {
URL: { min: 3, max: 2000 } // Length
} }
} }

View File

@ -0,0 +1,38 @@
import * as Sequelize from 'sequelize'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize
}): Promise<void> {
{
const query = 'DELETE FROM "videoShare" s1 ' +
'USING (SELECT MIN(id) as id, "actorId", "videoId" FROM "videoShare" GROUP BY "actorId", "videoId" HAVING COUNT(*) > 1) s2 ' +
'WHERE s1."actorId" = s2."actorId" AND s1."videoId" = s2."videoId" AND s1.id <> s2.id'
await utils.sequelize.query(query)
}
{
const data = {
type: Sequelize.STRING,
allowNull: true,
defaultValue: null
}
await utils.queryInterface.addColumn('videoShare', 'url', data)
const query = `UPDATE "videoShare" SET "url" = (SELECT "url" FROM "video" WHERE "id" = "videoId") || '/announces/' || "actorId"`
await utils.sequelize.query(query)
data.allowNull = false
await utils.queryInterface.changeColumn('videoShare', 'url', data)
}
}
function down (options) {
throw new Error('Not implemented.')
}
export {
up,
down
}

View File

@ -43,11 +43,14 @@ async function shareVideo (actorAnnouncer: ActorModel, activity: ActivityAnnounc
const share = { const share = {
actorId: actorAnnouncer.id, actorId: actorAnnouncer.id,
videoId: video.id videoId: video.id,
url: activity.id
} }
const [ , created ] = await VideoShareModel.findOrCreate({ const [ , created ] = await VideoShareModel.findOrCreate({
where: share, where: {
url: activity.id
},
defaults: share, defaults: share,
transaction: t transaction: t
}) })

View File

@ -2,45 +2,23 @@ import { Transaction } from 'sequelize'
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
import { ActorModel } from '../../../models/activitypub/actor' import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video' import { VideoModel } from '../../../models/video/video'
import { getAnnounceActivityPubUrl } from '../url' import { VideoShareModel } from '../../../models/video/video-share'
import { import { broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from './misc'
broadcastToFollowers,
getActorsInvolvedInVideo,
getAudience,
getObjectFollowersAudience,
getOriginVideoAudience,
unicastTo
} from './misc'
import { createActivityData } from './send-create'
async function buildVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { async function buildVideoAnnounceToFollowers (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
const url = getAnnounceActivityPubUrl(video.url, byActor)
const announcedObject = video.url const announcedObject = video.url
const accountsToForwardView = await getActorsInvolvedInVideo(video, t) const accountsToForwardView = await getActorsInvolvedInVideo(video, t)
const audience = getObjectFollowersAudience(accountsToForwardView) const audience = getObjectFollowersAudience(accountsToForwardView)
return announceActivityData(url, byActor, announcedObject, t, audience) return announceActivityData(videoShare.url, byActor, announcedObject, t, audience)
} }
async function sendVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { async function sendVideoAnnounceToFollowers (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
const data = await buildVideoAnnounceToFollowers(byActor, video, t) const data = await buildVideoAnnounceToFollowers(byActor, videoShare, video, t)
return broadcastToFollowers(data, byActor, [ byActor ], t) return broadcastToFollowers(data, byActor, [ byActor ], t)
} }
async function sendVideoAnnounceToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
const url = getAnnounceActivityPubUrl(video.url, byActor)
const videoObject = video.toActivityPubObject()
const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t)
const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
const data = await createActivityData(url, byActor, announcedActivity, t, audience)
return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
}
async function announceActivityData ( async function announceActivityData (
url: string, url: string,
byActor: ActorModel, byActor: ActorModel,
@ -66,7 +44,6 @@ async function announceActivityData (
export { export {
sendVideoAnnounceToFollowers, sendVideoAnnounceToFollowers,
sendVideoAnnounceToOrigin,
announceActivityData, announceActivityData,
buildVideoAnnounceToFollowers buildVideoAnnounceToFollowers
} }

View File

@ -4,28 +4,36 @@ import { getServerActor } from '../../helpers/utils'
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 { sendVideoAnnounceToFollowers } from './send' import { sendVideoAnnounceToFollowers } from './send'
import { getAnnounceActivityPubUrl } from './url'
async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
if (video.privacy === VideoPrivacy.PRIVATE) return undefined if (video.privacy === VideoPrivacy.PRIVATE) return undefined
const serverActor = await getServerActor() const serverActor = await getServerActor()
const serverShare = VideoShareModel.create({ const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor)
const serverSharePromise = VideoShareModel.create({
actorId: serverActor.id, actorId: serverActor.id,
videoId: video.id videoId: video.id,
url: serverShareUrl
}, { transaction: t }) }, { transaction: t })
const videoChannelShare = VideoShareModel.create({ const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor)
const videoChannelSharePromise = VideoShareModel.create({
actorId: video.VideoChannel.actorId, actorId: video.VideoChannel.actorId,
videoId: video.id videoId: video.id,
url: videoChannelShareUrl
}, { transaction: t }) }, { transaction: t })
await Promise.all([ const [ serverShare, videoChannelShare ] = await Promise.all([
serverShare, serverSharePromise,
videoChannelShare videoChannelSharePromise
]) ])
return sendVideoAnnounceToFollowers(serverActor, video, t) return Promise.all([
sendVideoAnnounceToFollowers(serverActor, videoChannelShare, video, t),
sendVideoAnnounceToFollowers(serverActor, serverShare, video, t)
])
} }
export { export {

View File

@ -1,7 +1,10 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONSTRAINTS_FIELDS } from '../../initializers'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { ActorModel } from '../activitypub/actor' import { ActorModel } from '../activitypub/actor'
import { throwIfNotValid } from '../utils'
import { VideoModel } from './video' import { VideoModel } from './video'
import { VideoChannelModel } from './video-channel' import { VideoChannelModel } from './video-channel'
@ -40,10 +43,20 @@ enum ScopeNames {
}, },
{ {
fields: [ 'videoId' ] fields: [ 'videoId' ]
},
{
fields: [ 'url' ],
unique: true
} }
] ]
}) })
export class VideoShareModel extends Model<VideoShareModel> { export class VideoShareModel extends Model<VideoShareModel> {
@AllowNull(false)
@Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_SHARE.URL.max))
url: string
@CreatedAt @CreatedAt
createdAt: Date createdAt: Date

View File

@ -5,8 +5,26 @@ import * as parseTorrent from 'parse-torrent'
import { join } from 'path' import { join } from 'path'
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { import {
AfterDestroy, AllowNull, BeforeDestroy, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, AfterDestroy,
IFindOptions, Is, IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt AllowNull,
BeforeDestroy,
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
Default,
ForeignKey,
HasMany,
IFindOptions,
Is,
IsInt,
IsUUID,
Min,
Model,
Scopes,
Table,
UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { VideoPrivacy, VideoResolution } from '../../../shared' import { VideoPrivacy, VideoResolution } from '../../../shared'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
@ -16,17 +34,30 @@ import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeF
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { isBooleanValid } from '../../helpers/custom-validators/misc' import { isBooleanValid } from '../../helpers/custom-validators/misc'
import { import {
isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid, isVideoCategoryValid,
isVideoDescriptionValid,
isVideoDurationValid,
isVideoLanguageValid,
isVideoLicenceValid,
isVideoNameValid,
isVideoPrivacyValid isVideoPrivacyValid
} from '../../helpers/custom-validators/videos' } from '../../helpers/custom-validators/videos'
import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { getServerActor } from '../../helpers/utils' import { getServerActor } from '../../helpers/utils'
import { import {
API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES, API_VERSION,
VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES CONFIG,
CONSTRAINTS_FIELDS,
PREVIEWS_SIZE,
REMOTE_SCHEME,
STATIC_PATHS,
THUMBNAILS_SIZE,
VIDEO_CATEGORIES,
VIDEO_LANGUAGES,
VIDEO_LICENCES,
VIDEO_PRIVACIES
} from '../../initializers' } from '../../initializers'
import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
import { sendDeleteVideo } from '../../lib/activitypub/send' import { sendDeleteVideo } from '../../lib/activitypub/send'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { AccountVideoRateModel } from '../account/account-video-rate' import { AccountVideoRateModel } from '../account/account-video-rate'
@ -936,8 +967,7 @@ export class VideoModel extends Model<VideoModel> {
const shares: string[] = [] const shares: string[] = []
for (const videoShare of this.VideoShares) { for (const videoShare of this.VideoShares) {
const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Actor) shares.push(videoShare.url)
shares.push(shareUrl)
} }
sharesObject = activityPubCollection(shares) sharesObject = activityPubCollection(shares)