async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
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))
}
import { ACTIVITY_PUB } from '../../initializers/constants'
import { announceActivityData, createActivityData } from '../../lib/activitypub/send'
import { buildAudience } from '../../lib/activitypub/send/misc'
-import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
import { asyncMiddleware, localAccountValidator } from '../../middlewares'
import { AccountModel } from '../../models/account/account'
import { ActorModel } from '../../models/activitypub/actor'
// This is a shared video
if (video.VideoShares !== undefined && video.VideoShares.length !== 0) {
+ const videoShare = video.VideoShares[0]
const announceAudience = buildAudience(followersMatrix[actor.id])
- const url = getAnnounceActivityPubUrl(video.url, actor)
- const announceActivity = await announceActivityData(url, actor, video.url, undefined, announceAudience)
+ const announceActivity = await announceActivityData(videoShare.url, actor, video.url, undefined, announceAudience)
activities.push(announceActivity)
} else {
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 180
+const LAST_MIGRATION_VERSION = 185
// ---------------------------------------------------------------------------
VIDEO_COMMENTS: {
TEXT: { min: 2, max: 3000 }, // Length
URL: { min: 3, max: 2000 } // Length
+ },
+ VIDEO_SHARE: {
+ URL: { min: 3, max: 2000 } // Length
}
}
--- /dev/null
+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
+}
const share = {
actorId: actorAnnouncer.id,
- videoId: video.id
+ videoId: video.id,
+ url: activity.id
}
const [ , created ] = await VideoShareModel.findOrCreate({
- where: share,
+ where: {
+ url: activity.id
+ },
defaults: share,
transaction: t
})
import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
-import { getAnnounceActivityPubUrl } from '../url'
-import {
- broadcastToFollowers,
- getActorsInvolvedInVideo,
- getAudience,
- getObjectFollowersAudience,
- getOriginVideoAudience,
- unicastTo
-} from './misc'
-import { createActivityData } from './send-create'
+import { VideoShareModel } from '../../../models/video/video-share'
+import { broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from './misc'
-async function buildVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
- const url = getAnnounceActivityPubUrl(video.url, byActor)
+async function buildVideoAnnounceToFollowers (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
const announcedObject = video.url
const accountsToForwardView = await getActorsInvolvedInVideo(video, t)
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) {
- const data = await buildVideoAnnounceToFollowers(byActor, video, t)
+async function sendVideoAnnounceToFollowers (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
+ const data = await buildVideoAnnounceToFollowers(byActor, videoShare, video, 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 (
url: string,
byActor: ActorModel,
export {
sendVideoAnnounceToFollowers,
- sendVideoAnnounceToOrigin,
announceActivityData,
buildVideoAnnounceToFollowers
}
import { VideoModel } from '../../models/video/video'
import { VideoShareModel } from '../../models/video/video-share'
import { sendVideoAnnounceToFollowers } from './send'
+import { getAnnounceActivityPubUrl } from './url'
async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
if (video.privacy === VideoPrivacy.PRIVATE) return undefined
const serverActor = await getServerActor()
- const serverShare = VideoShareModel.create({
+ const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor)
+ const serverSharePromise = VideoShareModel.create({
actorId: serverActor.id,
- videoId: video.id
+ videoId: video.id,
+ url: serverShareUrl
}, { transaction: t })
- const videoChannelShare = VideoShareModel.create({
+ const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor)
+ const videoChannelSharePromise = VideoShareModel.create({
actorId: video.VideoChannel.actorId,
- videoId: video.id
+ videoId: video.id,
+ url: videoChannelShareUrl
}, { transaction: t })
- await Promise.all([
- serverShare,
- videoChannelShare
+ const [ serverShare, videoChannelShare ] = await Promise.all([
+ serverSharePromise,
+ videoChannelSharePromise
])
- return sendVideoAnnounceToFollowers(serverActor, video, t)
+ return Promise.all([
+ sendVideoAnnounceToFollowers(serverActor, videoChannelShare, video, t),
+ sendVideoAnnounceToFollowers(serverActor, serverShare, video, t)
+ ])
}
export {
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 { ActorModel } from '../activitypub/actor'
+import { throwIfNotValid } from '../utils'
import { VideoModel } from './video'
import { VideoChannelModel } from './video-channel'
},
{
fields: [ 'videoId' ]
+ },
+ {
+ fields: [ 'url' ],
+ unique: true
}
]
})
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: Date
import { join } from 'path'
import * as Sequelize from 'sequelize'
import {
- AfterDestroy, AllowNull, BeforeDestroy, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany,
- IFindOptions, Is, IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt
+ AfterDestroy,
+ AllowNull,
+ BeforeDestroy,
+ BelongsTo,
+ BelongsToMany,
+ Column,
+ CreatedAt,
+ DataType,
+ Default,
+ ForeignKey,
+ HasMany,
+ IFindOptions,
+ Is,
+ IsInt,
+ IsUUID,
+ Min,
+ Model,
+ Scopes,
+ Table,
+ UpdatedAt
} from 'sequelize-typescript'
import { VideoPrivacy, VideoResolution } from '../../../shared'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { isBooleanValid } from '../../helpers/custom-validators/misc'
import {
- isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid,
+ isVideoCategoryValid,
+ isVideoDescriptionValid,
+ isVideoDurationValid,
+ isVideoLanguageValid,
+ isVideoLicenceValid,
+ isVideoNameValid,
isVideoPrivacyValid
} from '../../helpers/custom-validators/videos'
import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
import { logger } from '../../helpers/logger'
import { getServerActor } from '../../helpers/utils'
import {
- API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES,
- VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES
+ API_VERSION,
+ CONFIG,
+ CONSTRAINTS_FIELDS,
+ PREVIEWS_SIZE,
+ REMOTE_SCHEME,
+ STATIC_PATHS,
+ THUMBNAILS_SIZE,
+ VIDEO_CATEGORIES,
+ VIDEO_LANGUAGES,
+ VIDEO_LICENCES,
+ VIDEO_PRIVACIES
} from '../../initializers'
-import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
import { sendDeleteVideo } from '../../lib/activitypub/send'
import { AccountModel } from '../account/account'
import { AccountVideoRateModel } from '../account/account-video-rate'
const shares: string[] = []
for (const videoShare of this.VideoShares) {
- const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Actor)
- shares.push(shareUrl)
+ shares.push(videoShare.url)
}
sharesObject = activityPubCollection(shares)