aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/activitypub/client.ts2
-rw-r--r--server/controllers/activitypub/outbox.ts5
-rw-r--r--server/initializers/constants.ts5
-rw-r--r--server/initializers/migrations/0185-video-share-url.ts38
-rw-r--r--server/lib/activitypub/process/process-announce.ts7
-rw-r--r--server/lib/activitypub/send/send-announce.ts35
-rw-r--r--server/lib/activitypub/share.ts24
-rw-r--r--server/models/video/video-share.ts15
-rw-r--r--server/models/video/video.ts46
9 files changed, 124 insertions, 53 deletions
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index 2587ee212..7b60cc311 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -100,7 +100,7 @@ async function videoController (req: express.Request, res: express.Response, nex
100 100
101async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { 101async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
102 const share = res.locals.videoShare as VideoShareModel 102 const share = res.locals.videoShare as VideoShareModel
103 const object = await buildVideoAnnounceToFollowers(share.Actor, res.locals.video, undefined) 103 const object = await buildVideoAnnounceToFollowers(share.Actor, share, res.locals.video, undefined)
104 104
105 return res.json(activityPubContextify(object)) 105 return res.json(activityPubContextify(object))
106} 106}
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index 41c6ffaeb..86cdcf4cd 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -5,7 +5,6 @@ import { pageToStartAndCount } from '../../helpers/core-utils'
5import { ACTIVITY_PUB } from '../../initializers/constants' 5import { ACTIVITY_PUB } from '../../initializers/constants'
6import { announceActivityData, createActivityData } from '../../lib/activitypub/send' 6import { announceActivityData, createActivityData } from '../../lib/activitypub/send'
7import { buildAudience } from '../../lib/activitypub/send/misc' 7import { buildAudience } from '../../lib/activitypub/send/misc'
8import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
9import { asyncMiddleware, localAccountValidator } from '../../middlewares' 8import { asyncMiddleware, localAccountValidator } from '../../middlewares'
10import { AccountModel } from '../../models/account/account' 9import { AccountModel } from '../../models/account/account'
11import { ActorModel } from '../../models/activitypub/actor' 10import { ActorModel } from '../../models/activitypub/actor'
@@ -48,9 +47,9 @@ async function outboxController (req: express.Request, res: express.Response, ne
48 47
49 // This is a shared video 48 // This is a shared video
50 if (video.VideoShares !== undefined && video.VideoShares.length !== 0) { 49 if (video.VideoShares !== undefined && video.VideoShares.length !== 0) {
50 const videoShare = video.VideoShares[0]
51 const announceAudience = buildAudience(followersMatrix[actor.id]) 51 const announceAudience = buildAudience(followersMatrix[actor.id])
52 const url = getAnnounceActivityPubUrl(video.url, actor) 52 const announceActivity = await announceActivityData(videoShare.url, actor, video.url, undefined, announceAudience)
53 const announceActivity = await announceActivityData(url, actor, video.url, undefined, announceAudience)
54 53
55 activities.push(announceActivity) 54 activities.push(announceActivity)
56 } else { 55 } else {
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 329d0ffe8..a88f9642c 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -12,7 +12,7 @@ let config: IConfig = require('config')
12 12
13// --------------------------------------------------------------------------- 13// ---------------------------------------------------------------------------
14 14
15const LAST_MIGRATION_VERSION = 180 15const LAST_MIGRATION_VERSION = 185
16 16
17// --------------------------------------------------------------------------- 17// ---------------------------------------------------------------------------
18 18
@@ -196,6 +196,9 @@ const CONSTRAINTS_FIELDS = {
196 VIDEO_COMMENTS: { 196 VIDEO_COMMENTS: {
197 TEXT: { min: 2, max: 3000 }, // Length 197 TEXT: { min: 2, max: 3000 }, // Length
198 URL: { min: 3, max: 2000 } // Length 198 URL: { min: 3, max: 2000 } // Length
199 },
200 VIDEO_SHARE: {
201 URL: { min: 3, max: 2000 } // Length
199 } 202 }
200} 203}
201 204
diff --git a/server/initializers/migrations/0185-video-share-url.ts b/server/initializers/migrations/0185-video-share-url.ts
new file mode 100644
index 000000000..f7eeb0878
--- /dev/null
+++ b/server/initializers/migrations/0185-video-share-url.ts
@@ -0,0 +1,38 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction,
5 queryInterface: Sequelize.QueryInterface,
6 sequelize: Sequelize.Sequelize
7}): Promise<void> {
8 {
9 const query = 'DELETE FROM "videoShare" s1 ' +
10 'USING (SELECT MIN(id) as id, "actorId", "videoId" FROM "videoShare" GROUP BY "actorId", "videoId" HAVING COUNT(*) > 1) s2 ' +
11 'WHERE s1."actorId" = s2."actorId" AND s1."videoId" = s2."videoId" AND s1.id <> s2.id'
12 await utils.sequelize.query(query)
13 }
14
15 {
16 const data = {
17 type: Sequelize.STRING,
18 allowNull: true,
19 defaultValue: null
20 }
21 await utils.queryInterface.addColumn('videoShare', 'url', data)
22
23 const query = `UPDATE "videoShare" SET "url" = (SELECT "url" FROM "video" WHERE "id" = "videoId") || '/announces/' || "actorId"`
24 await utils.sequelize.query(query)
25
26 data.allowNull = false
27 await utils.queryInterface.changeColumn('videoShare', 'url', data)
28 }
29}
30
31function down (options) {
32 throw new Error('Not implemented.')
33}
34
35export {
36 up,
37 down
38}
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts
index 7dafc0593..09f2e80f3 100644
--- a/server/lib/activitypub/process/process-announce.ts
+++ b/server/lib/activitypub/process/process-announce.ts
@@ -43,11 +43,14 @@ async function shareVideo (actorAnnouncer: ActorModel, activity: ActivityAnnounc
43 43
44 const share = { 44 const share = {
45 actorId: actorAnnouncer.id, 45 actorId: actorAnnouncer.id,
46 videoId: video.id 46 videoId: video.id,
47 url: activity.id
47 } 48 }
48 49
49 const [ , created ] = await VideoShareModel.findOrCreate({ 50 const [ , created ] = await VideoShareModel.findOrCreate({
50 where: share, 51 where: {
52 url: activity.id
53 },
51 defaults: share, 54 defaults: share,
52 transaction: t 55 transaction: t
53 }) 56 })
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts
index 76cb3f80c..ed551a2b2 100644
--- a/server/lib/activitypub/send/send-announce.ts
+++ b/server/lib/activitypub/send/send-announce.ts
@@ -2,45 +2,23 @@ import { Transaction } from 'sequelize'
2import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' 2import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { getAnnounceActivityPubUrl } from '../url' 5import { VideoShareModel } from '../../../models/video/video-share'
6import { 6import { broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from './misc'
7 broadcastToFollowers,
8 getActorsInvolvedInVideo,
9 getAudience,
10 getObjectFollowersAudience,
11 getOriginVideoAudience,
12 unicastTo
13} from './misc'
14import { createActivityData } from './send-create'
15 7
16async function buildVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { 8async function buildVideoAnnounceToFollowers (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
17 const url = getAnnounceActivityPubUrl(video.url, byActor)
18 const announcedObject = video.url 9 const announcedObject = video.url
19 10
20 const accountsToForwardView = await getActorsInvolvedInVideo(video, t) 11 const accountsToForwardView = await getActorsInvolvedInVideo(video, t)
21 const audience = getObjectFollowersAudience(accountsToForwardView) 12 const audience = getObjectFollowersAudience(accountsToForwardView)
22 return announceActivityData(url, byActor, announcedObject, t, audience) 13 return announceActivityData(videoShare.url, byActor, announcedObject, t, audience)
23} 14}
24 15
25async function sendVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { 16async function sendVideoAnnounceToFollowers (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
26 const data = await buildVideoAnnounceToFollowers(byActor, video, t) 17 const data = await buildVideoAnnounceToFollowers(byActor, videoShare, video, t)
27 18
28 return broadcastToFollowers(data, byActor, [ byActor ], t) 19 return broadcastToFollowers(data, byActor, [ byActor ], t)
29} 20}
30 21
31async function sendVideoAnnounceToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
32 const url = getAnnounceActivityPubUrl(video.url, byActor)
33
34 const videoObject = video.toActivityPubObject()
35 const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t)
36
37 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
38 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
39 const data = await createActivityData(url, byActor, announcedActivity, t, audience)
40
41 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
42}
43
44async function announceActivityData ( 22async function announceActivityData (
45 url: string, 23 url: string,
46 byActor: ActorModel, 24 byActor: ActorModel,
@@ -66,7 +44,6 @@ async function announceActivityData (
66 44
67export { 45export {
68 sendVideoAnnounceToFollowers, 46 sendVideoAnnounceToFollowers,
69 sendVideoAnnounceToOrigin,
70 announceActivityData, 47 announceActivityData,
71 buildVideoAnnounceToFollowers 48 buildVideoAnnounceToFollowers
72} 49}
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index fd374d03d..53ecd3dab 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -4,28 +4,36 @@ import { getServerActor } from '../../helpers/utils'
4import { VideoModel } from '../../models/video/video' 4import { VideoModel } from '../../models/video/video'
5import { VideoShareModel } from '../../models/video/video-share' 5import { VideoShareModel } from '../../models/video/video-share'
6import { sendVideoAnnounceToFollowers } from './send' 6import { sendVideoAnnounceToFollowers } from './send'
7import { getAnnounceActivityPubUrl } from './url'
7 8
8async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { 9async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
9 if (video.privacy === VideoPrivacy.PRIVATE) return undefined 10 if (video.privacy === VideoPrivacy.PRIVATE) return undefined
10 11
11 const serverActor = await getServerActor() 12 const serverActor = await getServerActor()
12 13
13 const serverShare = VideoShareModel.create({ 14 const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor)
15 const serverSharePromise = VideoShareModel.create({
14 actorId: serverActor.id, 16 actorId: serverActor.id,
15 videoId: video.id 17 videoId: video.id,
18 url: serverShareUrl
16 }, { transaction: t }) 19 }, { transaction: t })
17 20
18 const videoChannelShare = VideoShareModel.create({ 21 const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor)
22 const videoChannelSharePromise = VideoShareModel.create({
19 actorId: video.VideoChannel.actorId, 23 actorId: video.VideoChannel.actorId,
20 videoId: video.id 24 videoId: video.id,
25 url: videoChannelShareUrl
21 }, { transaction: t }) 26 }, { transaction: t })
22 27
23 await Promise.all([ 28 const [ serverShare, videoChannelShare ] = await Promise.all([
24 serverShare, 29 serverSharePromise,
25 videoChannelShare 30 videoChannelSharePromise
26 ]) 31 ])
27 32
28 return sendVideoAnnounceToFollowers(serverActor, video, t) 33 return Promise.all([
34 sendVideoAnnounceToFollowers(serverActor, videoChannelShare, video, t),
35 sendVideoAnnounceToFollowers(serverActor, serverShare, video, t)
36 ])
29} 37}
30 38
31export { 39export {
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index 56576f98c..48ba68a4a 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -1,7 +1,10 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
4import { CONSTRAINTS_FIELDS } from '../../initializers'
3import { AccountModel } from '../account/account' 5import { AccountModel } from '../account/account'
4import { ActorModel } from '../activitypub/actor' 6import { ActorModel } from '../activitypub/actor'
7import { throwIfNotValid } from '../utils'
5import { VideoModel } from './video' 8import { VideoModel } from './video'
6import { VideoChannelModel } from './video-channel' 9import { VideoChannelModel } from './video-channel'
7 10
@@ -40,10 +43,20 @@ enum ScopeNames {
40 }, 43 },
41 { 44 {
42 fields: [ 'videoId' ] 45 fields: [ 'videoId' ]
46 },
47 {
48 fields: [ 'url' ],
49 unique: true
43 } 50 }
44 ] 51 ]
45}) 52})
46export class VideoShareModel extends Model<VideoShareModel> { 53export class VideoShareModel extends Model<VideoShareModel> {
54
55 @AllowNull(false)
56 @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
57 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_SHARE.URL.max))
58 url: string
59
47 @CreatedAt 60 @CreatedAt
48 createdAt: Date 61 createdAt: Date
49 62
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 81d8a64ff..bd834b088 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -5,8 +5,26 @@ import * as parseTorrent from 'parse-torrent'
5import { join } from 'path' 5import { join } from 'path'
6import * as Sequelize from 'sequelize' 6import * as Sequelize from 'sequelize'
7import { 7import {
8 AfterDestroy, AllowNull, BeforeDestroy, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, 8 AfterDestroy,
9 IFindOptions, Is, IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt 9 AllowNull,
10 BeforeDestroy,
11 BelongsTo,
12 BelongsToMany,
13 Column,
14 CreatedAt,
15 DataType,
16 Default,
17 ForeignKey,
18 HasMany,
19 IFindOptions,
20 Is,
21 IsInt,
22 IsUUID,
23 Min,
24 Model,
25 Scopes,
26 Table,
27 UpdatedAt
10} from 'sequelize-typescript' 28} from 'sequelize-typescript'
11import { VideoPrivacy, VideoResolution } from '../../../shared' 29import { VideoPrivacy, VideoResolution } from '../../../shared'
12import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 30import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
@@ -16,17 +34,30 @@ import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeF
16import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 34import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
17import { isBooleanValid } from '../../helpers/custom-validators/misc' 35import { isBooleanValid } from '../../helpers/custom-validators/misc'
18import { 36import {
19 isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid, 37 isVideoCategoryValid,
38 isVideoDescriptionValid,
39 isVideoDurationValid,
40 isVideoLanguageValid,
41 isVideoLicenceValid,
42 isVideoNameValid,
20 isVideoPrivacyValid 43 isVideoPrivacyValid
21} from '../../helpers/custom-validators/videos' 44} from '../../helpers/custom-validators/videos'
22import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' 45import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
23import { logger } from '../../helpers/logger' 46import { logger } from '../../helpers/logger'
24import { getServerActor } from '../../helpers/utils' 47import { getServerActor } from '../../helpers/utils'
25import { 48import {
26 API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES, 49 API_VERSION,
27 VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES 50 CONFIG,
51 CONSTRAINTS_FIELDS,
52 PREVIEWS_SIZE,
53 REMOTE_SCHEME,
54 STATIC_PATHS,
55 THUMBNAILS_SIZE,
56 VIDEO_CATEGORIES,
57 VIDEO_LANGUAGES,
58 VIDEO_LICENCES,
59 VIDEO_PRIVACIES
28} from '../../initializers' 60} from '../../initializers'
29import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
30import { sendDeleteVideo } from '../../lib/activitypub/send' 61import { sendDeleteVideo } from '../../lib/activitypub/send'
31import { AccountModel } from '../account/account' 62import { AccountModel } from '../account/account'
32import { AccountVideoRateModel } from '../account/account-video-rate' 63import { AccountVideoRateModel } from '../account/account-video-rate'
@@ -936,8 +967,7 @@ export class VideoModel extends Model<VideoModel> {
936 const shares: string[] = [] 967 const shares: string[] = []
937 968
938 for (const videoShare of this.VideoShares) { 969 for (const videoShare of this.VideoShares) {
939 const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Actor) 970 shares.push(videoShare.url)
940 shares.push(shareUrl)
941 } 971 }
942 972
943 sharesObject = activityPubCollection(shares) 973 sharesObject = activityPubCollection(shares)