aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/videos/index.ts10
-rw-r--r--server/initializers/migrations/0100-activitypub.ts4
-rw-r--r--server/initializers/migrations/0130-video-channel-actor.ts235
-rw-r--r--server/lib/activitypub/share.ts16
-rw-r--r--server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts4
5 files changed, 259 insertions, 10 deletions
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index d6934748f..3e65e844b 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -21,7 +21,11 @@ import {
21 VIDEO_MIMETYPE_EXT, 21 VIDEO_MIMETYPE_EXT,
22 VIDEO_PRIVACIES 22 VIDEO_PRIVACIES
23} from '../../../initializers' 23} from '../../../initializers'
24import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServer } from '../../../lib/activitypub' 24import {
25 fetchRemoteVideoDescription,
26 getVideoActivityPubUrl,
27 shareVideoByServerAndChannel
28} from '../../../lib/activitypub'
25import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send' 29import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send'
26import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler' 30import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler'
27import { 31import {
@@ -249,7 +253,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
249 253
250 await sendCreateVideo(video, t) 254 await sendCreateVideo(video, t)
251 // TODO: share by video channel 255 // TODO: share by video channel
252 await shareVideoByServer(video, t) 256 await shareVideoByServerAndChannel(video, t)
253 257
254 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) 258 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid)
255 259
@@ -306,7 +310,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
306 if (wasPrivateVideo === true && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE) { 310 if (wasPrivateVideo === true && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE) {
307 await sendCreateVideo(videoInstanceUpdated, t) 311 await sendCreateVideo(videoInstanceUpdated, t)
308 // TODO: Send by video channel 312 // TODO: Send by video channel
309 await shareVideoByServer(videoInstanceUpdated, t) 313 await shareVideoByServerAndChannel(videoInstanceUpdated, t)
310 } 314 }
311 }) 315 })
312 316
diff --git a/server/initializers/migrations/0100-activitypub.ts b/server/initializers/migrations/0100-activitypub.ts
index d896b3205..8c5198f85 100644
--- a/server/initializers/migrations/0100-activitypub.ts
+++ b/server/initializers/migrations/0100-activitypub.ts
@@ -1,7 +1,7 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import * as Sequelize from 'sequelize' 2import * as Sequelize from 'sequelize'
3import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto' 3import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto'
4import { shareVideoByServer } from '../../lib/activitypub/share' 4import { shareVideoByServerAndChannel } from '../../lib/activitypub/share'
5import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url' 5import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
6import { createLocalAccountWithoutKeys } from '../../lib/user' 6import { createLocalAccountWithoutKeys } from '../../lib/user'
7import { ApplicationModel } from '../../models/application/application' 7import { ApplicationModel } from '../../models/application/application'
@@ -197,7 +197,7 @@ async function up (utils: {
197 }) 197 })
198 198
199 for (const video of videos) { 199 for (const video of videos) {
200 await shareVideoByServer(video, undefined) 200 await shareVideoByServerAndChannel(video, undefined)
201 } 201 }
202 } 202 }
203} 203}
diff --git a/server/initializers/migrations/0130-video-channel-actor.ts b/server/initializers/migrations/0130-video-channel-actor.ts
new file mode 100644
index 000000000..72a0daef7
--- /dev/null
+++ b/server/initializers/migrations/0130-video-channel-actor.ts
@@ -0,0 +1,235 @@
1import * as Sequelize from 'sequelize'
2import { DataType } from 'sequelize-typescript'
3
4async function up (utils: {
5 transaction: Sequelize.Transaction,
6 queryInterface: Sequelize.QueryInterface,
7 sequelize: Sequelize.Sequelize
8}): Promise<void> {
9 // Create actor table
10 {
11 const queries = [
12 `
13 CREATE TYPE enum_actor_type AS ENUM (
14 'Group',
15 'Person',
16 'Application'
17 )
18 `,
19 `
20 CREATE TABLE actor (
21 id integer NOT NULL,
22 type enum_actor_type NOT NULL,
23 uuid uuid NOT NULL,
24 name character varying(255) NOT NULL,
25 url character varying(2000) NOT NULL,
26 "publicKey" character varying(5000),
27 "privateKey" character varying(5000),
28 "followersCount" integer NOT NULL,
29 "followingCount" integer NOT NULL,
30 "inboxUrl" character varying(2000) NOT NULL,
31 "outboxUrl" character varying(2000) NOT NULL,
32 "sharedInboxUrl" character varying(2000) NOT NULL,
33 "followersUrl" character varying(2000) NOT NULL,
34 "followingUrl" character varying(2000) NOT NULL,
35 "avatarId" integer,
36 "serverId" integer,
37 "createdAt" timestamp with time zone NOT NULL,
38 "updatedAt" timestamp with time zone NOT NULL
39 );`,
40 `
41 CREATE SEQUENCE actor_id_seq
42 AS integer
43 START WITH 1
44 INCREMENT BY 1
45 NO MINVALUE
46 NO MAXVALUE
47 CACHE 1
48 `,
49 `ALTER SEQUENCE actor_id_seq OWNED BY actor.id`,
50 `ALTER TABLE ONLY actor ALTER COLUMN id SET DEFAULT nextval('actor_id_seq'::regclass)`,
51 `ALTER TABLE ONLY actor ADD CONSTRAINT actor_pkey PRIMARY KEY (id);`,
52 `CREATE UNIQUE INDEX actor_name_server_id ON actor USING btree (name, "serverId")`,
53 `ALTER TABLE ONLY actor
54 ADD CONSTRAINT "actor_avatarId_fkey" FOREIGN KEY ("avatarId") REFERENCES avatar(id) ON UPDATE CASCADE ON DELETE CASCADE`,
55 `ALTER TABLE ONLY actor
56 ADD CONSTRAINT "actor_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES server(id) ON UPDATE CASCADE ON DELETE CASCADE;`
57 ]
58
59 for (const query of queries) {
60 await utils.sequelize.query(query)
61 }
62 }
63
64 {
65 // tslint:disable:no-trailing-whitespace
66 const query1 =
67 `
68 INSERT INTO "actor"
69 (
70 type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
71 "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
72 )
73 SELECT
74 'Application', uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
75 "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
76 FROM account
77 WHERE "applicationId" IS NOT NULL
78 `
79 await utils.sequelize.query(query1)
80
81 const query2 =
82 `
83 INSERT INTO "actor"
84 (
85 type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
86 "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
87 )
88 SELECT
89 'Person', uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
90 "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
91 FROM account
92 WHERE "applicationId" IS NULL
93 `
94 await utils.sequelize.query(query2)
95 }
96
97 {
98 const data = {
99 type: DataType.INTEGER,
100 allowNull: true,
101 defaultValue: null
102 }
103 await utils.queryInterface.addColumn('account', 'actorId', data)
104
105 const query1 = `UPDATE account SET "actorId" = (SELECT id FROM actor WHERE actor.url = account.url)`
106 await utils.sequelize.query(query1)
107
108 data.allowNull = false
109 await utils.queryInterface.changeColumn('account', 'actorId', data)
110
111 const query2 = `ALTER TABLE ONLY account
112 ADD CONSTRAINT "account_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;
113 `
114 await utils.sequelize.query(query2)
115 }
116
117 {
118 const query = `
119 INSERT INTO actor
120 (
121 type, uuid, name, url, "publicKey", "privateKey", "followersCount", "followingCount", "inboxUrl", "outboxUrl",
122 "sharedInboxUrl", "followersUrl", "followingUrl", "avatarId", "serverId", "createdAt", "updatedAt"
123 )
124 SELECT
125 'Group', "videoChannel".uuid, "videoChannel".uuid, "videoChannel".url, null, null, 0, 0, "videoChannel".url || '/inbox',
126 "videoChannel".url || '/outbox', "videoChannel".url || '/inbox', "videoChannel".url || '/followers', "videoChannel".url || '/following',
127 null, account."serverId", "videoChannel"."createdAt", "videoChannel"."updatedAt"
128 FROM "videoChannel"
129 INNER JOIN "account" on "videoChannel"."accountId" = "account".id
130 `
131 await utils.sequelize.query(query)
132 }
133
134 {
135 const data = {
136 type: DataType.INTEGER,
137 allowNull: true,
138 defaultValue: null
139 }
140 await utils.queryInterface.addColumn('videoChannel', 'actorId', data)
141
142 const query1 = `UPDATE "videoChannel" SET "actorId" = (SELECT id FROM actor WHERE actor.url = "videoChannel".url)`
143 await utils.sequelize.query(query1)
144
145 data.allowNull = false
146 await utils.queryInterface.changeColumn('videoChannel', 'actorId', data)
147
148 const query2 = `
149 ALTER TABLE ONLY "videoChannel"
150 ADD CONSTRAINT "videoChannel_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES actor(id) ON UPDATE CASCADE ON DELETE CASCADE;
151 `
152 await utils.sequelize.query(query2)
153 }
154
155 {
156 await utils.queryInterface.renameTable('accountFollow', 'actorFollow')
157 await utils.queryInterface.renameColumn('actorFollow', 'accountId', 'actorId')
158 await utils.queryInterface.renameColumn('actorFollow', 'targetAccountId', 'targetActorId')
159
160 try {
161 await utils.queryInterface.removeConstraint('actorFollow', 'AccountFollows_accountId_fkey')
162 await utils.queryInterface.removeConstraint('actorFollow', 'AccountFollows_targetAccountId_fkey')
163 } catch {
164 await utils.queryInterface.removeConstraint('actorFollow', 'accountFollow_accountId_fkey')
165 await utils.queryInterface.removeConstraint('actorFollow', 'accountFollow_targetAccountId_fkey')
166 }
167
168 const query1 = `UPDATE "actorFollow"
169 SET "actorId" =
170 (SELECT "actorId" FROM account WHERE id = "actorFollow"."actorId")`
171 await utils.sequelize.query(query1)
172
173 const query2 = `UPDATE "actorFollow"
174 SET "targetActorId" =
175 (SELECT "actorId" FROM account WHERE id = "actorFollow"."actorId")`
176
177 await utils.sequelize.query(query2)
178 }
179
180 {
181 await utils.queryInterface.renameColumn('videoShare', 'accountId', 'actorId')
182
183 try {
184 await utils.queryInterface.removeConstraint('videoShare', 'VideoShares_accountId_fkey')
185 } catch {
186 await utils.queryInterface.removeConstraint('videoShare', 'videoShare_accountId_fkey')
187 }
188
189 const query = `UPDATE "videoShare"
190 SET "actorId" =
191 (SELECT "actorId" FROM account WHERE id = "videoShare"."actorId")`
192 await utils.sequelize.query(query)
193 }
194
195 {
196 const columnsToDelete = [
197 'uuid',
198 'url',
199 'publicKey',
200 'privateKey',
201 'followersCount',
202 'followingCount',
203 'inboxUrl',
204 'outboxUrl',
205 'sharedInboxUrl',
206 'followersUrl',
207 'followingUrl',
208 'serverId',
209 'avatarId'
210 ]
211 for (const columnToDelete of columnsToDelete) {
212 await utils.queryInterface.removeColumn('account', columnToDelete)
213 }
214 }
215
216 {
217 const columnsToDelete = [
218 'uuid',
219 'remote',
220 'url'
221 ]
222 for (const columnToDelete of columnsToDelete) {
223 await utils.queryInterface.removeColumn('videoChannel', columnToDelete)
224 }
225 }
226}
227
228function down (options) {
229 throw new Error('Not implemented.')
230}
231
232export {
233 up,
234 down
235}
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index f79c4e532..fb01368ec 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -4,17 +4,27 @@ import { VideoModel } from '../../models/video/video'
4import { VideoShareModel } from '../../models/video/video-share' 4import { VideoShareModel } from '../../models/video/video-share'
5import { sendVideoAnnounceToFollowers } from './send' 5import { sendVideoAnnounceToFollowers } from './send'
6 6
7async function shareVideoByServer (video: VideoModel, t: Transaction) { 7async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) {
8 const serverActor = await getServerActor() 8 const serverActor = await getServerActor()
9 9
10 await VideoShareModel.create({ 10 const serverShare = VideoShareModel.create({
11 actorId: serverActor.id, 11 actorId: serverActor.id,
12 videoId: video.id 12 videoId: video.id
13 }, { transaction: t }) 13 }, { transaction: t })
14 14
15 const videoChannelShare = VideoShareModel.create({
16 actorId: video.VideoChannel.actorId,
17 videoId: video.id
18 }, { transaction: t })
19
20 await Promise.all([
21 serverShare,
22 videoChannelShare
23 ])
24
15 return sendVideoAnnounceToFollowers(serverActor, video, t) 25 return sendVideoAnnounceToFollowers(serverActor, video, t)
16} 26}
17 27
18export { 28export {
19 shareVideoByServer 29 shareVideoByServerAndChannel
20} 30}
diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
index 7df048006..cde4948de 100644
--- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
+++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts
@@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
2import { computeResolutionsToTranscode, logger } from '../../../helpers' 2import { computeResolutionsToTranscode, logger } from '../../../helpers'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { shareVideoByServer } from '../../activitypub' 5import { shareVideoByServerAndChannel } from '../../activitypub'
6import { sendCreateVideo } from '../../activitypub/send' 6import { sendCreateVideo } from '../../activitypub/send'
7import { JobScheduler } from '../job-scheduler' 7import { JobScheduler } from '../job-scheduler'
8import { TranscodingJobPayload } from './transcoding-job-scheduler' 8import { TranscodingJobPayload } from './transcoding-job-scheduler'
@@ -38,7 +38,7 @@ async function onSuccess (jobId: number, video: VideoModel, jobScheduler: JobSch
38 // Now we'll add the video's meta data to our followers 38 // Now we'll add the video's meta data to our followers
39 await sendCreateVideo(video, undefined) 39 await sendCreateVideo(video, undefined)
40 // TODO: share by channel 40 // TODO: share by channel
41 await shareVideoByServer(video, undefined) 41 await shareVideoByServerAndChannel(video, undefined)
42 42
43 const originalFileHeight = await videoDatabase.getOriginalFileHeight() 43 const originalFileHeight = await videoDatabase.getOriginalFileHeight()
44 44