瀏覽代碼

Don't rehost announced video activities

Chocobozzz 6 年之前
父節點
當前提交
4ba3b8ea1b

+ 1 - 1
server/controllers/activitypub/client.ts

@@ -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) {
   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))
 }

+ 2 - 3
server/controllers/activitypub/outbox.ts

@@ -5,7 +5,6 @@ import { pageToStartAndCount } from '../../helpers/core-utils'
 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'
@@ -48,9 +47,9 @@ async function outboxController (req: express.Request, res: express.Response, ne
 
     // 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 {

+ 4 - 1
server/initializers/constants.ts

@@ -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: {
     TEXT: { min: 2, max: 3000 }, // Length
     URL: { min: 3, max: 2000 } // Length
+  },
+  VIDEO_SHARE: {
+    URL: { min: 3, max: 2000 } // Length
   }
 }
 

+ 38 - 0
server/initializers/migrations/0185-video-share-url.ts

@@ -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
+}

+ 5 - 2
server/lib/activitypub/process/process-announce.ts

@@ -43,11 +43,14 @@ async function shareVideo (actorAnnouncer: ActorModel, activity: ActivityAnnounc
 
     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
     })

+ 6 - 29
server/lib/activitypub/send/send-announce.ts

@@ -2,45 +2,23 @@ import { Transaction } from 'sequelize'
 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,
@@ -66,7 +44,6 @@ async function announceActivityData (
 
 export {
   sendVideoAnnounceToFollowers,
-  sendVideoAnnounceToOrigin,
   announceActivityData,
   buildVideoAnnounceToFollowers
 }

+ 16 - 8
server/lib/activitypub/share.ts

@@ -4,28 +4,36 @@ import { getServerActor } from '../../helpers/utils'
 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 {

+ 14 - 1
server/models/video/video-share.ts

@@ -1,7 +1,10 @@
 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'
 
@@ -40,10 +43,20 @@ enum ScopeNames {
     },
     {
       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
 

+ 38 - 8
server/models/video/video.ts

@@ -5,8 +5,26 @@ import * as parseTorrent from 'parse-torrent'
 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'
@@ -16,17 +34,30 @@ import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeF
 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'
@@ -936,8 +967,7 @@ export class VideoModel extends Model<VideoModel> {
       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)