Don't rehost announced video activities
This commit is contained in:
parent
7859b5800c
commit
4ba3b8ea1b
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue