aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/video-channel.ts26
-rw-r--r--server/lib/activitypub/process/process-create.ts1
-rw-r--r--server/lib/video-channel.ts16
-rw-r--r--server/middlewares/validators/videos/video-channels.ts16
-rw-r--r--server/models/video/video.ts23
-rw-r--r--server/tests/api/check-params/video-channels.ts12
-rw-r--r--server/tests/api/videos/video-channels.ts42
-rw-r--r--shared/extra-utils/videos/video-channels.ts9
-rw-r--r--shared/extra-utils/videos/videos.ts1
-rw-r--r--shared/models/videos/channel/video-channel-update.model.ts4
-rw-r--r--support/doc/api/openapi.yaml36
11 files changed, 154 insertions, 32 deletions
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index c98a39be2..81a03a62b 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -19,7 +19,7 @@ import { VideoChannelModel } from '../../models/video/video-channel'
19import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators' 19import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators'
20import { sendUpdateActor } from '../../lib/activitypub/send' 20import { sendUpdateActor } from '../../lib/activitypub/send'
21import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared' 21import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
22import { createVideoChannel } from '../../lib/video-channel' 22import { createVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
23import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' 23import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
24import { setAsyncActorKeys } from '../../lib/activitypub' 24import { setAsyncActorKeys } from '../../lib/activitypub'
25import { AccountModel } from '../../models/account/account' 25import { AccountModel } from '../../models/account/account'
@@ -160,6 +160,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
160 const videoChannelFieldsSave = videoChannelInstance.toJSON() 160 const videoChannelFieldsSave = videoChannelInstance.toJSON()
161 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON()) 161 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
162 const videoChannelInfoToUpdate = req.body as VideoChannelUpdate 162 const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
163 let doBulkVideoUpdate = false
163 164
164 try { 165 try {
165 await sequelizeTypescript.transaction(async t => { 166 await sequelizeTypescript.transaction(async t => {
@@ -167,9 +168,18 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
167 transaction: t 168 transaction: t
168 } 169 }
169 170
170 if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.displayName) 171 if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName
171 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) 172 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description
172 if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support) 173
174 if (videoChannelInfoToUpdate.support !== undefined) {
175 const oldSupportField = videoChannelInstance.support
176 videoChannelInstance.support = videoChannelInfoToUpdate.support
177
178 if (videoChannelInfoToUpdate.bulkVideosSupportUpdate === true && oldSupportField !== videoChannelInfoToUpdate.support) {
179 doBulkVideoUpdate = true
180 await VideoModel.bulkUpdateSupportField(videoChannelInstance, t)
181 }
182 }
173 183
174 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) 184 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
175 await sendUpdateActor(videoChannelInstanceUpdated, t) 185 await sendUpdateActor(videoChannelInstanceUpdated, t)
@@ -179,6 +189,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
179 new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()), 189 new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
180 oldVideoChannelAuditKeys 190 oldVideoChannelAuditKeys
181 ) 191 )
192
182 logger.info('Video channel %s updated.', videoChannelInstance.Actor.url) 193 logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
183 }) 194 })
184 } catch (err) { 195 } catch (err) {
@@ -192,7 +203,12 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
192 throw err 203 throw err
193 } 204 }
194 205
195 return res.type('json').status(204).end() 206 res.type('json').status(204).end()
207
208 // Don't process in a transaction, and after the response because it could be long
209 if (doBulkVideoUpdate) {
210 await federateAllVideosOfChannel(videoChannelInstance)
211 }
196} 212}
197 213
198async function removeVideoChannel (req: express.Request, res: express.Response) { 214async function removeVideoChannel (req: express.Request, res: express.Response) {
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 3b6eb45c4..daf846513 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -87,6 +87,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Act
87 commentObject.inReplyTo, 87 commentObject.inReplyTo,
88 { err } 88 { err }
89 ) 89 )
90 return
90 } 91 }
91 92
92 const { comment, created } = await addVideoComment(video, commentObject.id) 93 const { comment, created } = await addVideoComment(video, commentObject.id)
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index 0fe95ca09..ee0482c36 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -3,7 +3,8 @@ import * as uuidv4 from 'uuid/v4'
3import { VideoChannelCreate } from '../../shared/models' 3import { VideoChannelCreate } from '../../shared/models'
4import { AccountModel } from '../models/account/account' 4import { AccountModel } from '../models/account/account'
5import { VideoChannelModel } from '../models/video/video-channel' 5import { VideoChannelModel } from '../models/video/video-channel'
6import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub' 6import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
7import { VideoModel } from '../models/video/video'
7 8
8async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { 9async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
9 const uuid = uuidv4() 10 const uuid = uuidv4()
@@ -33,8 +34,19 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
33 return videoChannelCreated 34 return videoChannelCreated
34} 35}
35 36
37async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) {
38 const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
39
40 for (const videoId of videoIds) {
41 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
42
43 await federateVideoIfNeeded(video, false)
44 }
45}
46
36// --------------------------------------------------------------------------- 47// ---------------------------------------------------------------------------
37 48
38export { 49export {
39 createVideoChannel 50 createVideoChannel,
51 federateAllVideosOfChannel
40} 52}
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts
index 4b26f0bc4..f5a59cacb 100644
--- a/server/middlewares/validators/videos/video-channels.ts
+++ b/server/middlewares/validators/videos/video-channels.ts
@@ -14,6 +14,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel'
14import { areValidationErrors } from '../utils' 14import { areValidationErrors } from '../utils'
15import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' 15import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
16import { ActorModel } from '../../../models/activitypub/actor' 16import { ActorModel } from '../../../models/activitypub/actor'
17import { isBooleanValid } from '../../../helpers/custom-validators/misc'
17 18
18const videoChannelsAddValidator = [ 19const videoChannelsAddValidator = [
19 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), 20 body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
@@ -40,9 +41,18 @@ const videoChannelsAddValidator = [
40 41
41const videoChannelsUpdateValidator = [ 42const videoChannelsUpdateValidator = [
42 param('nameWithHost').exists().withMessage('Should have an video channel name with host'), 43 param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
43 body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'), 44 body('displayName')
44 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 45 .optional()
45 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), 46 .custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
47 body('description')
48 .optional()
49 .custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
50 body('support')
51 .optional()
52 .custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
53 body('bulkVideosSupportUpdate')
54 .optional()
55 .custom(isBooleanValid).withMessage('Should have a valid bulkVideosSupportUpdate boolean field'),
46 56
47 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 57 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
48 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 58 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index c0a7892a4..eccf0a4fa 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1515,6 +1515,29 @@ export class VideoModel extends Model<VideoModel> {
1515 .then(results => results.length === 1) 1515 .then(results => results.length === 1)
1516 } 1516 }
1517 1517
1518 static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) {
1519 const options = {
1520 where: {
1521 channelId: videoChannel.id
1522 },
1523 transaction: t
1524 }
1525
1526 return VideoModel.update({ support: videoChannel.support }, options)
1527 }
1528
1529 static getAllIdsFromChannel (videoChannel: VideoChannelModel) {
1530 const query = {
1531 attributes: [ 'id' ],
1532 where: {
1533 channelId: videoChannel.id
1534 }
1535 }
1536
1537 return VideoModel.findAll(query)
1538 .then(videos => videos.map(v => v.id))
1539 }
1540
1518 // threshold corresponds to how many video the field should have to be returned 1541 // threshold corresponds to how many video the field should have to be returned
1519 static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) { 1542 static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) {
1520 const serverActor = await getServerActor() 1543 const serverActor = await getServerActor()
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index ff04f6b03..de88298d1 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -24,6 +24,7 @@ import {
24 checkBadStartPagination 24 checkBadStartPagination
25} from '../../../../shared/extra-utils/requests/check-api-params' 25} from '../../../../shared/extra-utils/requests/check-api-params'
26import { join } from 'path' 26import { join } from 'path'
27import { VideoChannelUpdate } from '../../../../shared/models/videos'
27 28
28const expect = chai.expect 29const expect = chai.expect
29 30
@@ -169,9 +170,11 @@ describe('Test video channels API validator', function () {
169 }) 170 })
170 171
171 describe('When updating a video channel', function () { 172 describe('When updating a video channel', function () {
172 const baseCorrectParams = { 173 const baseCorrectParams: VideoChannelUpdate = {
173 displayName: 'hello', 174 displayName: 'hello',
174 description: 'super description' 175 description: 'super description',
176 support: 'toto',
177 bulkVideosSupportUpdate: false
175 } 178 }
176 let path: string 179 let path: string
177 180
@@ -214,6 +217,11 @@ describe('Test video channels API validator', function () {
214 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 217 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
215 }) 218 })
216 219
220 it('Should fail with a bad bulkVideosSupportUpdate field', async function () {
221 const fields = immutableAssign(baseCorrectParams, { bulkVideosSupportUpdate: 'super' })
222 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
223 })
224
217 it('Should succeed with the correct parameters', async function () { 225 it('Should succeed with the correct parameters', async function () {
218 await makePutBodyRequest({ 226 await makePutBodyRequest({
219 url: server.url, 227 url: server.url,
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index 2d298dd3f..4f600cae8 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -2,12 +2,12 @@
2 2
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { User, Video, VideoChannel } from '../../../../shared/index' 5import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index'
6import { 6import {
7 cleanupTests, 7 cleanupTests,
8 createUser, 8 createUser,
9 doubleFollow, 9 doubleFollow,
10 flushAndRunMultipleServers, 10 flushAndRunMultipleServers, getVideo,
11 getVideoChannelVideos, 11 getVideoChannelVideos,
12 testImage, 12 testImage,
13 updateVideo, 13 updateVideo,
@@ -79,7 +79,8 @@ describe('Test video channels', function () {
79 79
80 // The channel is 1 is propagated to servers 2 80 // The channel is 1 is propagated to servers 2
81 { 81 {
82 const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'my video name', channelId: secondVideoChannelId }) 82 const videoAttributesArg = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' }
83 const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributesArg)
83 videoUUID = res.body.video.uuid 84 videoUUID = res.body.video.uuid
84 } 85 }
85 86
@@ -201,12 +202,12 @@ describe('Test video channels', function () {
201 }) 202 })
202 203
203 it('Should update video channel', async function () { 204 it('Should update video channel', async function () {
204 this.timeout(5000) 205 this.timeout(15000)
205 206
206 const videoChannelAttributes = { 207 const videoChannelAttributes = {
207 displayName: 'video channel updated', 208 displayName: 'video channel updated',
208 description: 'video channel description updated', 209 description: 'video channel description updated',
209 support: 'video channel support text updated' 210 support: 'support updated'
210 } 211 }
211 212
212 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes) 213 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
@@ -224,7 +225,36 @@ describe('Test video channels', function () {
224 expect(res.body.data[0].name).to.equal('second_video_channel') 225 expect(res.body.data[0].name).to.equal('second_video_channel')
225 expect(res.body.data[0].displayName).to.equal('video channel updated') 226 expect(res.body.data[0].displayName).to.equal('video channel updated')
226 expect(res.body.data[0].description).to.equal('video channel description updated') 227 expect(res.body.data[0].description).to.equal('video channel description updated')
227 expect(res.body.data[0].support).to.equal('video channel support text updated') 228 expect(res.body.data[0].support).to.equal('support updated')
229 }
230 })
231
232 it('Should not have updated the video support field', async function () {
233 for (const server of servers) {
234 const res = await getVideo(server.url, videoUUID)
235 const video: VideoDetails = res.body
236
237 expect(video.support).to.equal('video support field')
238 }
239 })
240
241 it('Should update the channel support field and update videos too', async function () {
242 this.timeout(35000)
243
244 const videoChannelAttributes = {
245 support: 'video channel support text updated',
246 bulkVideosSupportUpdate: true
247 }
248
249 await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
250
251 await waitJobs(servers)
252
253 for (const server of servers) {
254 const res = await getVideo(server.url, videoUUID)
255 const video: VideoDetails = res.body
256
257 expect(video.support).to.equal(videoChannelAttributes.support)
228 } 258 }
229 }) 259 })
230 260
diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts
index b4755b486..3e79cf15a 100644
--- a/shared/extra-utils/videos/video-channels.ts
+++ b/shared/extra-utils/videos/video-channels.ts
@@ -74,12 +74,13 @@ function updateVideoChannel (
74 attributes: VideoChannelUpdate, 74 attributes: VideoChannelUpdate,
75 expectedStatus = 204 75 expectedStatus = 204
76) { 76) {
77 const body = {} 77 const body: any = {}
78 const path = '/api/v1/video-channels/' + channelName 78 const path = '/api/v1/video-channels/' + channelName
79 79
80 if (attributes.displayName) body['displayName'] = attributes.displayName 80 if (attributes.displayName) body.displayName = attributes.displayName
81 if (attributes.description) body['description'] = attributes.description 81 if (attributes.description) body.description = attributes.description
82 if (attributes.support) body['support'] = attributes.support 82 if (attributes.support) body.support = attributes.support
83 if (attributes.bulkVideosSupportUpdate) body.bulkVideosSupportUpdate = attributes.bulkVideosSupportUpdate
83 84
84 return request(url) 85 return request(url)
85 .put(path) 86 .put(path)
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index a4ca43f26..debaaf9a7 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -355,6 +355,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
355 .set('Accept', 'application/json') 355 .set('Accept', 'application/json')
356 .set('Authorization', 'Bearer ' + accessToken) 356 .set('Authorization', 'Bearer ' + accessToken)
357 .field('name', attributes.name) 357 .field('name', attributes.name)
358 .field('support', attributes.support)
358 .field('nsfw', JSON.stringify(attributes.nsfw)) 359 .field('nsfw', JSON.stringify(attributes.nsfw))
359 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled)) 360 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
360 .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled)) 361 .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
diff --git a/shared/models/videos/channel/video-channel-update.model.ts b/shared/models/videos/channel/video-channel-update.model.ts
index 3626ce8a9..8dde9188b 100644
--- a/shared/models/videos/channel/video-channel-update.model.ts
+++ b/shared/models/videos/channel/video-channel-update.model.ts
@@ -1,5 +1,7 @@
1export interface VideoChannelUpdate { 1export interface VideoChannelUpdate {
2 displayName: string 2 displayName?: string
3 description?: string 3 description?: string
4 support?: string 4 support?: string
5
6 bulkVideosSupportUpdate?: boolean
5} 7}
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index a8a064fd0..f3f565694 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -1322,7 +1322,10 @@ paths:
1322 '204': 1322 '204':
1323 $ref: '#/paths/~1users~1me/put/responses/204' 1323 $ref: '#/paths/~1users~1me/put/responses/204'
1324 requestBody: 1324 requestBody:
1325 $ref: '#/components/requestBodies/VideoChannelInput' 1325 content:
1326 application/json:
1327 schema:
1328 $ref: '#/components/schemas/VideoChannelCreate'
1326 '/video-channels/{channelHandle}': 1329 '/video-channels/{channelHandle}':
1327 get: 1330 get:
1328 summary: Get a video channel by its id 1331 summary: Get a video channel by its id
@@ -1349,7 +1352,10 @@ paths:
1349 '204': 1352 '204':
1350 $ref: '#/paths/~1users~1me/put/responses/204' 1353 $ref: '#/paths/~1users~1me/put/responses/204'
1351 requestBody: 1354 requestBody:
1352 $ref: '#/components/requestBodies/VideoChannelInput' 1355 content:
1356 application/json:
1357 schema:
1358 $ref: '#/components/schemas/VideoChannelUpdate'
1353 delete: 1359 delete:
1354 summary: Delete a video channel by its id 1360 summary: Delete a video channel by its id
1355 security: 1361 security:
@@ -1775,12 +1781,6 @@ components:
1775 type: array 1781 type: array
1776 items: 1782 items:
1777 type: string 1783 type: string
1778 requestBodies:
1779 VideoChannelInput:
1780 content:
1781 application/json:
1782 schema:
1783 $ref: '#/components/schemas/VideoChannelInput'
1784 securitySchemes: 1784 securitySchemes:
1785 OAuth2: 1785 OAuth2:
1786 description: > 1786 description: >
@@ -2294,10 +2294,28 @@ components:
2294 - username 2294 - username
2295 - password 2295 - password
2296 - email 2296 - email
2297 VideoChannelInput: 2297 VideoChannelCreate:
2298 properties: 2298 properties:
2299 name: 2299 name:
2300 type: string 2300 type: string
2301 displayName:
2302 type: string
2301 description: 2303 description:
2302 type: string 2304 type: string
2305 support:
2306 type: string
2307 required:
2308 - name
2309 - displayName
2310 VideoChannelUpdate:
2311 properties:
2312 displayName:
2313 type: string
2314 description:
2315 type: string
2316 support:
2317 type: string
2318 bulkVideosSupportUpdate:
2319 type: boolean
2320 description: 'Update all videos support field of this channel'
2303 2321