aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/accounts.ts209
-rw-r--r--server/controllers/api/index.ts2
-rw-r--r--server/controllers/api/video-channel.ts34
-rw-r--r--server/controllers/api/videos/channel.ts177
-rw-r--r--server/controllers/api/videos/index.ts17
-rw-r--r--server/controllers/feeds.ts14
-rw-r--r--server/middlewares/validators/follows.ts2
-rw-r--r--server/middlewares/validators/video-channels.ts8
-rw-r--r--server/models/video/video.ts92
-rw-r--r--server/tests/api/check-params/video-channels.ts101
-rw-r--r--server/tests/api/videos/multiple-servers.ts21
-rw-r--r--server/tests/api/videos/video-channels.ts15
-rw-r--r--server/tests/utils/videos/video-channels.ts35
13 files changed, 441 insertions, 286 deletions
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 06ab04033..04c5897c5 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,11 +1,30 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getFormattedObjects } from '../../helpers/utils' 2import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils'
3import { asyncMiddleware, optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort } from '../../middlewares' 3import {
4 asyncMiddleware,
5 authenticate,
6 listVideoAccountChannelsValidator,
7 optionalAuthenticate,
8 paginationValidator,
9 setDefaultPagination,
10 setDefaultSort,
11 videoChannelsAddValidator,
12 videoChannelsGetValidator,
13 videoChannelsRemoveValidator,
14 videoChannelsUpdateValidator
15} from '../../middlewares'
4import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators' 16import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators'
5import { AccountModel } from '../../models/account/account' 17import { AccountModel } from '../../models/account/account'
6import { VideoModel } from '../../models/video/video' 18import { VideoModel } from '../../models/video/video'
7import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
8import { isNSFWHidden } from '../../helpers/express-utils' 19import { isNSFWHidden } from '../../helpers/express-utils'
20import { VideoChannelModel } from '../../models/video/video-channel'
21import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
22import { sendUpdateActor } from '../../lib/activitypub/send'
23import { createVideoChannel } from '../../lib/video-channel'
24import { setAsyncActorKeys } from '../../lib/activitypub'
25import { sequelizeTypescript } from '../../initializers'
26import { logger } from '../../helpers/logger'
27import { retryTransactionWrapper } from '../../helpers/database-utils'
9 28
10const accountsRouter = express.Router() 29const accountsRouter = express.Router()
11 30
@@ -29,7 +48,45 @@ accountsRouter.get('/:id/videos',
29 setDefaultSort, 48 setDefaultSort,
30 setDefaultPagination, 49 setDefaultPagination,
31 optionalAuthenticate, 50 optionalAuthenticate,
32 asyncMiddleware(getAccountVideos) 51 asyncMiddleware(listAccountVideos)
52)
53
54accountsRouter.get('/:accountId/video-channels',
55 asyncMiddleware(listVideoAccountChannelsValidator),
56 asyncMiddleware(listVideoAccountChannels)
57)
58
59accountsRouter.post('/:accountId/video-channels',
60 authenticate,
61 videoChannelsAddValidator,
62 asyncMiddleware(addVideoChannelRetryWrapper)
63)
64
65accountsRouter.put('/:accountId/video-channels/:id',
66 authenticate,
67 asyncMiddleware(videoChannelsUpdateValidator),
68 updateVideoChannelRetryWrapper
69)
70
71accountsRouter.delete('/:accountId/video-channels/:id',
72 authenticate,
73 asyncMiddleware(videoChannelsRemoveValidator),
74 asyncMiddleware(removeVideoChannelRetryWrapper)
75)
76
77accountsRouter.get('/:accountId/video-channels/:id',
78 asyncMiddleware(videoChannelsGetValidator),
79 asyncMiddleware(getVideoChannel)
80)
81
82accountsRouter.get('/:accountId/video-channels/:id/videos',
83 asyncMiddleware(videoChannelsGetValidator),
84 paginationValidator,
85 videosSortValidator,
86 setDefaultSort,
87 setDefaultPagination,
88 optionalAuthenticate,
89 asyncMiddleware(listVideoChannelVideos)
33) 90)
34 91
35// --------------------------------------------------------------------------- 92// ---------------------------------------------------------------------------
@@ -52,18 +109,142 @@ async function listAccounts (req: express.Request, res: express.Response, next:
52 return res.json(getFormattedObjects(resultList.data, resultList.total)) 109 return res.json(getFormattedObjects(resultList.data, resultList.total))
53} 110}
54 111
55async function getAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { 112async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
113 const resultList = await VideoChannelModel.listByAccount(res.locals.account.id)
114
115 return res.json(getFormattedObjects(resultList.data, resultList.total))
116}
117
118// Wrapper to video channel add that retry the async function if there is a database error
119// We need this because we run the transaction in SERIALIZABLE isolation that can fail
120async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
121 const options = {
122 arguments: [ req, res ],
123 errorMessage: 'Cannot insert the video video channel with many retries.'
124 }
125
126 const videoChannel = await retryTransactionWrapper(addVideoChannel, options)
127 return res.json({
128 videoChannel: {
129 id: videoChannel.id
130 }
131 }).end()
132}
133
134async function addVideoChannel (req: express.Request, res: express.Response) {
135 const videoChannelInfo: VideoChannelCreate = req.body
136 const account: AccountModel = res.locals.oauth.token.User.Account
137
138 const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => {
139 return createVideoChannel(videoChannelInfo, account, t)
140 })
141
142 setAsyncActorKeys(videoChannelCreated.Actor)
143 .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err }))
144
145 logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
146
147 return videoChannelCreated
148}
149
150async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
151 const options = {
152 arguments: [ req, res ],
153 errorMessage: 'Cannot update the video with many retries.'
154 }
155
156 await retryTransactionWrapper(updateVideoChannel, options)
157
158 return res.type('json').status(204).end()
159}
160
161async function updateVideoChannel (req: express.Request, res: express.Response) {
162 const videoChannelInstance = res.locals.videoChannel as VideoChannelModel
163 const videoChannelFieldsSave = videoChannelInstance.toJSON()
164 const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
165
166 try {
167 await sequelizeTypescript.transaction(async t => {
168 const sequelizeOptions = {
169 transaction: t
170 }
171
172 if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
173 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
174 if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
175
176 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
177 await sendUpdateActor(videoChannelInstanceUpdated, t)
178 })
179
180 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
181 } catch (err) {
182 logger.debug('Cannot update the video channel.', { err })
183
184 // Force fields we want to update
185 // If the transaction is retried, sequelize will think the object has not changed
186 // So it will skip the SQL request, even if the last one was ROLLBACKed!
187 resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave)
188
189 throw err
190 }
191}
192
193async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
194 const options = {
195 arguments: [ req, res ],
196 errorMessage: 'Cannot remove the video channel with many retries.'
197 }
198
199 await retryTransactionWrapper(removeVideoChannel, options)
200
201 return res.type('json').status(204).end()
202}
203
204async function removeVideoChannel (req: express.Request, res: express.Response) {
205 const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
206
207 return sequelizeTypescript.transaction(async t => {
208 await videoChannelInstance.destroy({ transaction: t })
209
210 logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
211 })
212
213}
214
215async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
216 const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
217
218 return res.json(videoChannelWithVideos.toFormattedJSON())
219}
220
221async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
222 const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
223
224 const resultList = await VideoModel.listForApi({
225 start: req.query.start,
226 count: req.query.count,
227 sort: req.query.sort,
228 hideNSFW: isNSFWHidden(res),
229 withFiles: false,
230 videoChannelId: videoChannelInstance.id
231 })
232
233 return res.json(getFormattedObjects(resultList.data, resultList.total))
234}
235
236
237async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
56 const account: AccountModel = res.locals.account 238 const account: AccountModel = res.locals.account
57 239
58 const resultList = await VideoModel.listForApi( 240 const resultList = await VideoModel.listForApi({
59 req.query.start as number, 241 start: req.query.start,
60 req.query.count as number, 242 count: req.query.count,
61 req.query.sort as VideoSortField, 243 sort: req.query.sort,
62 isNSFWHidden(res), 244 hideNSFW: isNSFWHidden(res),
63 null, 245 withFiles: false,
64 false, 246 accountId: account.id
65 account.id 247 })
66 )
67 248
68 return res.json(getFormattedObjects(resultList.data, resultList.total)) 249 return res.json(getFormattedObjects(resultList.data, resultList.total))
69} 250}
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts
index 964d5d04c..8f63b9535 100644
--- a/server/controllers/api/index.ts
+++ b/server/controllers/api/index.ts
@@ -7,6 +7,7 @@ import { usersRouter } from './users'
7import { accountsRouter } from './accounts' 7import { accountsRouter } from './accounts'
8import { videosRouter } from './videos' 8import { videosRouter } from './videos'
9import { badRequest } from '../../helpers/express-utils' 9import { badRequest } from '../../helpers/express-utils'
10import { videoChannelRouter } from './video-channel'
10 11
11const apiRouter = express.Router() 12const apiRouter = express.Router()
12 13
@@ -15,6 +16,7 @@ apiRouter.use('/oauth-clients', oauthClientsRouter)
15apiRouter.use('/config', configRouter) 16apiRouter.use('/config', configRouter)
16apiRouter.use('/users', usersRouter) 17apiRouter.use('/users', usersRouter)
17apiRouter.use('/accounts', accountsRouter) 18apiRouter.use('/accounts', accountsRouter)
19apiRouter.use('/video-channels', videoChannelRouter)
18apiRouter.use('/videos', videosRouter) 20apiRouter.use('/videos', videosRouter)
19apiRouter.use('/jobs', jobsRouter) 21apiRouter.use('/jobs', jobsRouter)
20apiRouter.use('/ping', pong) 22apiRouter.use('/ping', pong)
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
new file mode 100644
index 000000000..d57273747
--- /dev/null
+++ b/server/controllers/api/video-channel.ts
@@ -0,0 +1,34 @@
1import * as express from 'express'
2import { getFormattedObjects } from '../../helpers/utils'
3import {
4 asyncMiddleware,
5 paginationValidator,
6 setDefaultPagination,
7 setDefaultSort,
8 videoChannelsSortValidator
9} from '../../middlewares'
10import { VideoChannelModel } from '../../models/video/video-channel'
11
12const videoChannelRouter = express.Router()
13
14videoChannelRouter.get('/',
15 paginationValidator,
16 videoChannelsSortValidator,
17 setDefaultSort,
18 setDefaultPagination,
19 asyncMiddleware(listVideoChannels)
20)
21
22// ---------------------------------------------------------------------------
23
24export {
25 videoChannelRouter
26}
27
28// ---------------------------------------------------------------------------
29
30async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
31 const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort)
32
33 return res.json(getFormattedObjects(resultList.data, resultList.total))
34}
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts
deleted file mode 100644
index e547d375f..000000000
--- a/server/controllers/api/videos/channel.ts
+++ /dev/null
@@ -1,177 +0,0 @@
1import * as express from 'express'
2import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
3import { retryTransactionWrapper } from '../../../helpers/database-utils'
4import { logger } from '../../../helpers/logger'
5import { getFormattedObjects, resetSequelizeInstance } from '../../../helpers/utils'
6import { sequelizeTypescript } from '../../../initializers'
7import { setAsyncActorKeys } from '../../../lib/activitypub'
8import { sendUpdateActor } from '../../../lib/activitypub/send'
9import { createVideoChannel } from '../../../lib/video-channel'
10import {
11 asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setDefaultPagination,
12 videoChannelsAddValidator, videoChannelsGetValidator, videoChannelsRemoveValidator, videoChannelsSortValidator,
13 videoChannelsUpdateValidator
14} from '../../../middlewares'
15import { AccountModel } from '../../../models/account/account'
16import { VideoChannelModel } from '../../../models/video/video-channel'
17
18const videoChannelRouter = express.Router()
19
20videoChannelRouter.get('/channels',
21 paginationValidator,
22 videoChannelsSortValidator,
23 setDefaultSort,
24 setDefaultPagination,
25 asyncMiddleware(listVideoChannels)
26)
27
28videoChannelRouter.get('/accounts/:accountId/channels',
29 asyncMiddleware(listVideoAccountChannelsValidator),
30 asyncMiddleware(listVideoAccountChannels)
31)
32
33videoChannelRouter.post('/channels',
34 authenticate,
35 videoChannelsAddValidator,
36 asyncMiddleware(addVideoChannelRetryWrapper)
37)
38
39videoChannelRouter.put('/channels/:id',
40 authenticate,
41 asyncMiddleware(videoChannelsUpdateValidator),
42 updateVideoChannelRetryWrapper
43)
44
45videoChannelRouter.delete('/channels/:id',
46 authenticate,
47 asyncMiddleware(videoChannelsRemoveValidator),
48 asyncMiddleware(removeVideoChannelRetryWrapper)
49)
50
51videoChannelRouter.get('/channels/:id',
52 asyncMiddleware(videoChannelsGetValidator),
53 asyncMiddleware(getVideoChannel)
54)
55
56// ---------------------------------------------------------------------------
57
58export {
59 videoChannelRouter
60}
61
62// ---------------------------------------------------------------------------
63
64async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
65 const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort)
66
67 return res.json(getFormattedObjects(resultList.data, resultList.total))
68}
69
70async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
71 const resultList = await VideoChannelModel.listByAccount(res.locals.account.id)
72
73 return res.json(getFormattedObjects(resultList.data, resultList.total))
74}
75
76// Wrapper to video channel add that retry the async function if there is a database error
77// We need this because we run the transaction in SERIALIZABLE isolation that can fail
78async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
79 const options = {
80 arguments: [ req, res ],
81 errorMessage: 'Cannot insert the video video channel with many retries.'
82 }
83
84 const videoChannel = await retryTransactionWrapper(addVideoChannel, options)
85 return res.json({
86 videoChannel: {
87 id: videoChannel.id
88 }
89 }).end()
90}
91
92async function addVideoChannel (req: express.Request, res: express.Response) {
93 const videoChannelInfo: VideoChannelCreate = req.body
94 const account: AccountModel = res.locals.oauth.token.User.Account
95
96 const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => {
97 return createVideoChannel(videoChannelInfo, account, t)
98 })
99
100 setAsyncActorKeys(videoChannelCreated.Actor)
101 .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err }))
102
103 logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
104
105 return videoChannelCreated
106}
107
108async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
109 const options = {
110 arguments: [ req, res ],
111 errorMessage: 'Cannot update the video with many retries.'
112 }
113
114 await retryTransactionWrapper(updateVideoChannel, options)
115
116 return res.type('json').status(204).end()
117}
118
119async function updateVideoChannel (req: express.Request, res: express.Response) {
120 const videoChannelInstance = res.locals.videoChannel as VideoChannelModel
121 const videoChannelFieldsSave = videoChannelInstance.toJSON()
122 const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
123
124 try {
125 await sequelizeTypescript.transaction(async t => {
126 const sequelizeOptions = {
127 transaction: t
128 }
129
130 if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
131 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
132 if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
133
134 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
135 await sendUpdateActor(videoChannelInstanceUpdated, t)
136 })
137
138 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
139 } catch (err) {
140 logger.debug('Cannot update the video channel.', { err })
141
142 // Force fields we want to update
143 // If the transaction is retried, sequelize will think the object has not changed
144 // So it will skip the SQL request, even if the last one was ROLLBACKed!
145 resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave)
146
147 throw err
148 }
149}
150
151async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
152 const options = {
153 arguments: [ req, res ],
154 errorMessage: 'Cannot remove the video channel with many retries.'
155 }
156
157 await retryTransactionWrapper(removeVideoChannel, options)
158
159 return res.type('json').status(204).end()
160}
161
162async function removeVideoChannel (req: express.Request, res: express.Response) {
163 const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
164
165 return sequelizeTypescript.transaction(async t => {
166 await videoChannelInstance.destroy({ transaction: t })
167
168 logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
169 })
170
171}
172
173async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
174 const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
175
176 return res.json(videoChannelWithVideos.toFormattedJSON())
177}
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 61b6c5826..4b3198a74 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -42,7 +42,6 @@ import { VideoModel } from '../../../models/video/video'
42import { VideoFileModel } from '../../../models/video/video-file' 42import { VideoFileModel } from '../../../models/video/video-file'
43import { abuseVideoRouter } from './abuse' 43import { abuseVideoRouter } from './abuse'
44import { blacklistRouter } from './blacklist' 44import { blacklistRouter } from './blacklist'
45import { videoChannelRouter } from './channel'
46import { videoCommentRouter } from './comment' 45import { videoCommentRouter } from './comment'
47import { rateVideoRouter } from './rate' 46import { rateVideoRouter } from './rate'
48import { VideoFilter } from '../../../../shared/models/videos/video-query.type' 47import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
@@ -72,7 +71,6 @@ const reqVideoFileUpdate = createReqFiles(
72videosRouter.use('/', abuseVideoRouter) 71videosRouter.use('/', abuseVideoRouter)
73videosRouter.use('/', blacklistRouter) 72videosRouter.use('/', blacklistRouter)
74videosRouter.use('/', rateVideoRouter) 73videosRouter.use('/', rateVideoRouter)
75videosRouter.use('/', videoChannelRouter)
76videosRouter.use('/', videoCommentRouter) 74videosRouter.use('/', videoCommentRouter)
77 75
78videosRouter.get('/categories', listVideoCategories) 76videosRouter.get('/categories', listVideoCategories)
@@ -397,13 +395,14 @@ async function getVideoDescription (req: express.Request, res: express.Response)
397} 395}
398 396
399async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { 397async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
400 const resultList = await VideoModel.listForApi( 398 const resultList = await VideoModel.listForApi({
401 req.query.start as number, 399 start: req.query.start,
402 req.query.count as number, 400 count: req.query.count,
403 req.query.sort as VideoSortField, 401 sort: req.query.sort,
404 isNSFWHidden(res), 402 hideNSFW: isNSFWHidden(res),
405 req.query.filter as VideoFilter 403 filter: req.query.filter as VideoFilter,
406 ) 404 withFiles: false
405 })
407 406
408 return res.json(getFormattedObjects(resultList.data, resultList.total)) 407 return res.json(getFormattedObjects(resultList.data, resultList.total))
409} 408}
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 6a6af3e09..7dcaf7004 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -33,15 +33,15 @@ async function generateFeed (req: express.Request, res: express.Response, next:
33 const account: AccountModel = res.locals.account 33 const account: AccountModel = res.locals.account
34 const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list' 34 const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
35 35
36 const resultList = await VideoModel.listForApi( 36 const resultList = await VideoModel.listForApi({
37 start, 37 start,
38 FEEDS.COUNT, 38 count: FEEDS.COUNT,
39 req.query.sort as VideoSortField, 39 sort: req.query.sort,
40 hideNSFW, 40 hideNSFW,
41 req.query.filter, 41 filter: req.query.filter,
42 true, 42 withFiles: true,
43 account ? account.id : null 43 accountId: account ? account.id : null
44 ) 44 })
45 45
46 // Adding video items to the feed, one at a time 46 // Adding video items to the feed, one at a time
47 resultList.data.forEach(video => { 47 resultList.data.forEach(video => {
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts
index 991a2e175..bdf39eb9c 100644
--- a/server/middlewares/validators/follows.ts
+++ b/server/middlewares/validators/follows.ts
@@ -16,7 +16,7 @@ const followValidator = [
16 if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') { 16 if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') {
17 return res.status(400) 17 return res.status(400)
18 .json({ 18 .json({
19 error: 'Cannot follow non HTTPS web server.' 19 error: 'Cannot follow on a non HTTPS web server.'
20 }) 20 })
21 .end() 21 .end()
22 } 22 }
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts
index fe42105e8..e3a11a41b 100644
--- a/server/middlewares/validators/video-channels.ts
+++ b/server/middlewares/validators/video-channels.ts
@@ -26,6 +26,7 @@ const listVideoAccountChannelsValidator = [
26] 26]
27 27
28const videoChannelsAddValidator = [ 28const videoChannelsAddValidator = [
29 param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
29 body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'), 30 body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
30 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 31 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
31 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), 32 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
@@ -41,6 +42,7 @@ const videoChannelsAddValidator = [
41 42
42const videoChannelsUpdateValidator = [ 43const videoChannelsUpdateValidator = [
43 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 44 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
45 param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
44 body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), 46 body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
45 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 47 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
46 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), 48 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
@@ -49,6 +51,7 @@ const videoChannelsUpdateValidator = [
49 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 51 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
50 52
51 if (areValidationErrors(req, res)) return 53 if (areValidationErrors(req, res)) return
54 if (!await isAccountIdExist(req.params.accountId, res)) return
52 if (!await isVideoChannelExist(req.params.id, res)) return 55 if (!await isVideoChannelExist(req.params.id, res)) return
53 56
54 // We need to make additional checks 57 // We need to make additional checks
@@ -70,11 +73,13 @@ const videoChannelsUpdateValidator = [
70 73
71const videoChannelsRemoveValidator = [ 74const videoChannelsRemoveValidator = [
72 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 75 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
76 param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
73 77
74 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 78 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
75 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) 79 logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
76 80
77 if (areValidationErrors(req, res)) return 81 if (areValidationErrors(req, res)) return
82 if (!await isAccountIdExist(req.params.accountId, res)) return
78 if (!await isVideoChannelExist(req.params.id, res)) return 83 if (!await isVideoChannelExist(req.params.id, res)) return
79 84
80 // Check if the user who did the request is able to delete the video 85 // Check if the user who did the request is able to delete the video
@@ -87,11 +92,14 @@ const videoChannelsRemoveValidator = [
87 92
88const videoChannelsGetValidator = [ 93const videoChannelsGetValidator = [
89 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 94 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
95 param('accountId').optional().custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
90 96
91 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 97 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
92 logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) 98 logger.debug('Checking videoChannelsGet parameters', { parameters: req.params })
93 99
94 if (areValidationErrors(req, res)) return 100 if (areValidationErrors(req, res)) return
101 // On some routes, accountId is optional (for example in the ActivityPub route)
102 if (req.params.accountId && !await isAccountIdExist(req.params.accountId, res)) return
95 if (!await isVideoChannelExist(req.params.id, res)) return 103 if (!await isVideoChannelExist(req.params.id, res)) return
96 104
97 return next() 105 return next()
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 2ad9c00dd..7ababbf23 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -95,7 +95,14 @@ enum ScopeNames {
95} 95}
96 96
97@Scopes({ 97@Scopes({
98 [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean, accountId?: number) => { 98 [ScopeNames.AVAILABLE_FOR_LIST]: (options: {
99 actorId: number,
100 hideNSFW: boolean,
101 filter?: VideoFilter,
102 withFiles?: boolean,
103 accountId?: number,
104 videoChannelId?: number
105 }) => {
99 const accountInclude = { 106 const accountInclude = {
100 attributes: [ 'name' ], 107 attributes: [ 'name' ],
101 model: AccountModel.unscoped(), 108 model: AccountModel.unscoped(),
@@ -106,7 +113,7 @@ enum ScopeNames {
106 attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], 113 attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
107 model: ActorModel.unscoped(), 114 model: ActorModel.unscoped(),
108 required: true, 115 required: true,
109 where: VideoModel.buildActorWhereWithFilter(filter), 116 where: VideoModel.buildActorWhereWithFilter(options.filter),
110 include: [ 117 include: [
111 { 118 {
112 attributes: [ 'host' ], 119 attributes: [ 'host' ],
@@ -122,6 +129,18 @@ enum ScopeNames {
122 ] 129 ]
123 } 130 }
124 131
132 const videoChannelInclude = {
133 attributes: [ 'name', 'description' ],
134 model: VideoChannelModel.unscoped(),
135 required: true,
136 where: {},
137 include: [
138 accountInclude
139 ]
140 }
141
142 // Force actorId to be a number to avoid SQL injections
143 const actorIdNumber = parseInt(options.actorId.toString(), 10)
125 const query: IFindOptions<VideoModel> = { 144 const query: IFindOptions<VideoModel> = {
126 where: { 145 where: {
127 id: { 146 id: {
@@ -132,32 +151,23 @@ enum ScopeNames {
132 '(' + 151 '(' +
133 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 152 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' +
134 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 153 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
135 'WHERE "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) + 154 'WHERE "actorFollow"."actorId" = ' + actorIdNumber +
136 ' UNION ' + 155 ' UNION ' +
137 'SELECT "video"."id" AS "id" FROM "video" ' + 156 'SELECT "video"."id" AS "id" FROM "video" ' +
138 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 157 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
139 'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' + 158 'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' +
140 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' + 159 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' +
141 'LEFT JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' + 160 'LEFT JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' +
142 'WHERE "actor"."serverId" IS NULL OR "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) + 161 'WHERE "actor"."serverId" IS NULL OR "actorFollow"."actorId" = ' + actorIdNumber +
143 ')' 162 ')'
144 ) 163 )
145 }, 164 },
146 privacy: VideoPrivacy.PUBLIC 165 privacy: VideoPrivacy.PUBLIC
147 }, 166 },
148 include: [ 167 include: [ videoChannelInclude ]
149 {
150 attributes: [ 'name', 'description' ],
151 model: VideoChannelModel.unscoped(),
152 required: true,
153 include: [
154 accountInclude
155 ]
156 }
157 ]
158 } 168 }
159 169
160 if (withFiles === true) { 170 if (options.withFiles === true) {
161 query.include.push({ 171 query.include.push({
162 model: VideoFileModel.unscoped(), 172 model: VideoFileModel.unscoped(),
163 required: true 173 required: true
@@ -165,13 +175,19 @@ enum ScopeNames {
165 } 175 }
166 176
167 // Hide nsfw videos? 177 // Hide nsfw videos?
168 if (hideNSFW === true) { 178 if (options.hideNSFW === true) {
169 query.where['nsfw'] = false 179 query.where['nsfw'] = false
170 } 180 }
171 181
172 if (accountId) { 182 if (options.accountId) {
173 accountInclude.where = { 183 accountInclude.where = {
174 id: accountId 184 id: options.accountId
185 }
186 }
187
188 if (options.videoChannelId) {
189 videoChannelInclude.where = {
190 id: options.videoChannelId
175 } 191 }
176 } 192 }
177 193
@@ -697,23 +713,37 @@ export class VideoModel extends Model<VideoModel> {
697 }) 713 })
698 } 714 }
699 715
700 static async listForApi ( 716 static async listForApi (options: {
701 start: number, 717 start: number,
702 count: number, 718 count: number,
703 sort: string, 719 sort: string,
704 hideNSFW: boolean, 720 hideNSFW: boolean,
721 withFiles: boolean,
705 filter?: VideoFilter, 722 filter?: VideoFilter,
706 withFiles = false, 723 accountId?: number,
707 accountId?: number 724 videoChannelId?: number
708 ) { 725 }) {
709 const query = { 726 const query = {
710 offset: start, 727 offset: options.start,
711 limit: count, 728 limit: options.count,
712 order: getSort(sort) 729 order: getSort(options.sort)
713 } 730 }
714 731
715 const serverActor = await getServerActor() 732 const serverActor = await getServerActor()
716 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles, accountId ] }) 733 const scopes = {
734 method: [
735 ScopeNames.AVAILABLE_FOR_LIST, {
736 actorId: serverActor.id,
737 hideNSFW: options.hideNSFW,
738 filter: options.filter,
739 withFiles: options.withFiles,
740 accountId: options.accountId,
741 videoChannelId: options.videoChannelId
742 }
743 ]
744 }
745
746 return VideoModel.scope(scopes)
717 .findAndCountAll(query) 747 .findAndCountAll(query)
718 .then(({ rows, count }) => { 748 .then(({ rows, count }) => {
719 return { 749 return {
@@ -750,8 +780,16 @@ export class VideoModel extends Model<VideoModel> {
750 } 780 }
751 781
752 const serverActor = await getServerActor() 782 const serverActor = await getServerActor()
783 const scopes = {
784 method: [
785 ScopeNames.AVAILABLE_FOR_LIST, {
786 actorId: serverActor.id,
787 hideNSFW
788 }
789 ]
790 }
753 791
754 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW ] }) 792 return VideoModel.scope(scopes)
755 .findAndCountAll(query) 793 .findAndCountAll(query)
756 .then(({ rows, count }) => { 794 .then(({ rows, count }) => {
757 return { 795 return {
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index 43c5462ee..acb6bdd57 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -4,15 +4,29 @@ import * as chai from 'chai'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import 'mocha' 5import 'mocha'
6import { 6import {
7 createUser, deleteVideoChannel, flushTests, getAccountVideoChannelsList, getVideoChannelsList, immutableAssign, killallServers, 7 createUser,
8 makeGetRequest, makePostBodyRequest, makePutBodyRequest, runServer, ServerInfo, setAccessTokensToServers, userLogin 8 deleteVideoChannel,
9 flushTests,
10 getAccountVideoChannelsList,
11 getVideoChannelsList,
12 immutableAssign,
13 killallServers,
14 makeGetRequest,
15 makePostBodyRequest,
16 makePutBodyRequest,
17 runServer,
18 ServerInfo,
19 setAccessTokensToServers,
20 userLogin
9} from '../../utils' 21} from '../../utils'
10import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' 22import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
23import { getAccountsList } from '../../utils/users/accounts'
11 24
12const expect = chai.expect 25const expect = chai.expect
13 26
14describe('Test videos API validator', function () { 27describe('Test videos API validator', function () {
15 const path = '/api/v1/videos/channels' 28 const videoChannelPath = '/api/v1/video-channels'
29 const accountPath = '/api/v1/accounts/'
16 let server: ServerInfo 30 let server: ServerInfo
17 let accessTokenUser: string 31 let accessTokenUser: string
18 32
@@ -37,15 +51,15 @@ describe('Test videos API validator', function () {
37 51
38 describe('When listing a video channels', function () { 52 describe('When listing a video channels', function () {
39 it('Should fail with a bad start pagination', async function () { 53 it('Should fail with a bad start pagination', async function () {
40 await checkBadStartPagination(server.url, path, server.accessToken) 54 await checkBadStartPagination(server.url, videoChannelPath, server.accessToken)
41 }) 55 })
42 56
43 it('Should fail with a bad count pagination', async function () { 57 it('Should fail with a bad count pagination', async function () {
44 await checkBadCountPagination(server.url, path, server.accessToken) 58 await checkBadCountPagination(server.url, videoChannelPath, server.accessToken)
45 }) 59 })
46 60
47 it('Should fail with an incorrect sort', async function () { 61 it('Should fail with an incorrect sort', async function () {
48 await checkBadSortPagination(server.url, path, server.accessToken) 62 await checkBadSortPagination(server.url, videoChannelPath, server.accessToken)
49 }) 63 })
50 }) 64 })
51 65
@@ -60,12 +74,20 @@ describe('Test videos API validator', function () {
60 }) 74 })
61 75
62 describe('When adding a video channel', function () { 76 describe('When adding a video channel', function () {
77 let path: string
78
63 const baseCorrectParams = { 79 const baseCorrectParams = {
64 name: 'hello', 80 name: 'hello',
65 description: 'super description', 81 description: 'super description',
66 support: 'super support text' 82 support: 'super support text'
67 } 83 }
68 84
85 before(async function () {
86 const res = await getAccountsList(server.url)
87 const accountId = res.body.data[0].id
88 path = accountPath + accountId + '/video-channels'
89 })
90
69 it('Should fail with a non authenticated user', async function () { 91 it('Should fail with a non authenticated user', async function () {
70 await makePostBodyRequest({ url: server.url, path, token: 'none', fields: baseCorrectParams, statusCodeExpected: 401 }) 92 await makePostBodyRequest({ url: server.url, path, token: 'none', fields: baseCorrectParams, statusCodeExpected: 401 })
71 }) 93 })
@@ -107,22 +129,27 @@ describe('Test videos API validator', function () {
107 }) 129 })
108 130
109 describe('When updating a video channel', function () { 131 describe('When updating a video channel', function () {
132 let path: string
133
110 const baseCorrectParams = { 134 const baseCorrectParams = {
111 name: 'hello', 135 name: 'hello',
112 description: 'super description' 136 description: 'super description'
113 } 137 }
114 138
115 let videoChannelId
116
117 before(async function () { 139 before(async function () {
118 const res = await getVideoChannelsList(server.url, 0, 1) 140 const res1 = await getVideoChannelsList(server.url, 0, 1)
119 videoChannelId = res.body.data[0].id 141 const videoChannelId = res1.body.data[0].id
142
143 const res2 = await getAccountsList(server.url)
144 const accountId = res2.body.data[0].id
145
146 path = accountPath + accountId + '/video-channels/' + videoChannelId
120 }) 147 })
121 148
122 it('Should fail with a non authenticated user', async function () { 149 it('Should fail with a non authenticated user', async function () {
123 await makePutBodyRequest({ 150 await makePutBodyRequest({
124 url: server.url, 151 url: server.url,
125 path: path + '/' + videoChannelId, 152 path,
126 token: 'hi', 153 token: 'hi',
127 fields: baseCorrectParams, 154 fields: baseCorrectParams,
128 statusCodeExpected: 401 155 statusCodeExpected: 401
@@ -132,7 +159,7 @@ describe('Test videos API validator', function () {
132 it('Should fail with another authenticated user', async function () { 159 it('Should fail with another authenticated user', async function () {
133 await makePutBodyRequest({ 160 await makePutBodyRequest({
134 url: server.url, 161 url: server.url,
135 path: path + '/' + videoChannelId, 162 path,
136 token: accessTokenUser, 163 token: accessTokenUser,
137 fields: baseCorrectParams, 164 fields: baseCorrectParams,
138 statusCodeExpected: 403 165 statusCodeExpected: 403
@@ -141,23 +168,23 @@ describe('Test videos API validator', function () {
141 168
142 it('Should fail with a long name', async function () { 169 it('Should fail with a long name', async function () {
143 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) }) 170 const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) })
144 await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) 171 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
145 }) 172 })
146 173
147 it('Should fail with a long description', async function () { 174 it('Should fail with a long description', async function () {
148 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(60) }) 175 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(60) })
149 await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) 176 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
150 }) 177 })
151 178
152 it('Should fail with a long support text', async function () { 179 it('Should fail with a long support text', async function () {
153 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) }) 180 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
154 await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) 181 await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
155 }) 182 })
156 183
157 it('Should succeed with the correct parameters', async function () { 184 it('Should succeed with the correct parameters', async function () {
158 await makePutBodyRequest({ 185 await makePutBodyRequest({
159 url: server.url, 186 url: server.url,
160 path: path + '/' + videoChannelId, 187 path,
161 token: server.accessToken, 188 token: server.accessToken,
162 fields: baseCorrectParams, 189 fields: baseCorrectParams,
163 statusCodeExpected: 204 190 statusCodeExpected: 204
@@ -166,17 +193,23 @@ describe('Test videos API validator', function () {
166 }) 193 })
167 194
168 describe('When getting a video channel', function () { 195 describe('When getting a video channel', function () {
196 let basePath: string
169 let videoChannelId: number 197 let videoChannelId: number
170 198
171 before(async function () { 199 before(async function () {
172 const res = await getVideoChannelsList(server.url, 0, 1) 200 const res1 = await getVideoChannelsList(server.url, 0, 1)
173 videoChannelId = res.body.data[0].id 201 videoChannelId = res1.body.data[0].id
202
203 const res2 = await getAccountsList(server.url)
204 const accountId = res2.body.data[0].id
205
206 basePath = accountPath + accountId + '/video-channels'
174 }) 207 })
175 208
176 it('Should return the list of the video channels with nothing', async function () { 209 it('Should return the list of the video channels with nothing', async function () {
177 const res = await makeGetRequest({ 210 const res = await makeGetRequest({
178 url: server.url, 211 url: server.url,
179 path, 212 path: basePath,
180 statusCodeExpected: 200 213 statusCodeExpected: 200
181 }) 214 })
182 215
@@ -186,7 +219,7 @@ describe('Test videos API validator', function () {
186 it('Should fail without a correct uuid', async function () { 219 it('Should fail without a correct uuid', async function () {
187 await makeGetRequest({ 220 await makeGetRequest({
188 url: server.url, 221 url: server.url,
189 path: path + '/coucou', 222 path: basePath + '/coucou',
190 statusCodeExpected: 400 223 statusCodeExpected: 400
191 }) 224 })
192 }) 225 })
@@ -194,7 +227,7 @@ describe('Test videos API validator', function () {
194 it('Should return 404 with an incorrect video channel', async function () { 227 it('Should return 404 with an incorrect video channel', async function () {
195 await makeGetRequest({ 228 await makeGetRequest({
196 url: server.url, 229 url: server.url,
197 path: path + '/4da6fde3-88f7-4d16-b119-108df5630b06', 230 path: basePath + '/4da6fde3-88f7-4d16-b119-108df5630b06',
198 statusCodeExpected: 404 231 statusCodeExpected: 404
199 }) 232 })
200 }) 233 })
@@ -202,7 +235,7 @@ describe('Test videos API validator', function () {
202 it('Should succeed with the correct parameters', async function () { 235 it('Should succeed with the correct parameters', async function () {
203 await makeGetRequest({ 236 await makeGetRequest({
204 url: server.url, 237 url: server.url,
205 path: path + '/' + videoChannelId, 238 path: basePath + '/' + videoChannelId,
206 statusCodeExpected: 200 239 statusCodeExpected: 200
207 }) 240 })
208 }) 241 })
@@ -210,33 +243,41 @@ describe('Test videos API validator', function () {
210 243
211 describe('When deleting a video channel', function () { 244 describe('When deleting a video channel', function () {
212 let videoChannelId: number 245 let videoChannelId: number
246 let accountId: number
213 247
214 before(async function () { 248 before(async function () {
215 const res = await getVideoChannelsList(server.url, 0, 1) 249 const res1 = await getVideoChannelsList(server.url, 0, 1)
216 videoChannelId = res.body.data[0].id 250 videoChannelId = res1.body.data[0].id
251
252 const res2 = await getAccountsList(server.url)
253 accountId = res2.body.data[0].id
217 }) 254 })
218 255
219 it('Should fail with a non authenticated user', async function () { 256 it('Should fail with a non authenticated user', async function () {
220 await deleteVideoChannel(server.url, 'coucou', videoChannelId, 401) 257 await deleteVideoChannel(server.url, 'coucou', accountId, videoChannelId, 401)
221 }) 258 })
222 259
223 it('Should fail with another authenticated user', async function () { 260 it('Should fail with another authenticated user', async function () {
224 await deleteVideoChannel(server.url, accessTokenUser, videoChannelId, 403) 261 await deleteVideoChannel(server.url, accessTokenUser, accountId, videoChannelId, 403)
262 })
263
264 it('Should fail with an unknown account id', async function () {
265 await deleteVideoChannel(server.url, server.accessToken, 454554,videoChannelId, 404)
225 }) 266 })
226 267
227 it('Should fail with an unknown id', async function () { 268 it('Should fail with an unknown video channel id', async function () {
228 await deleteVideoChannel(server.url, server.accessToken, 454554, 404) 269 await deleteVideoChannel(server.url, server.accessToken, accountId,454554, 404)
229 }) 270 })
230 271
231 it('Should succeed with the correct parameters', async function () { 272 it('Should succeed with the correct parameters', async function () {
232 await deleteVideoChannel(server.url, server.accessToken, videoChannelId) 273 await deleteVideoChannel(server.url, server.accessToken, accountId, videoChannelId)
233 }) 274 })
234 275
235 it('Should fail to delete the last user video channel', async function () { 276 it('Should fail to delete the last user video channel', async function () {
236 const res = await getVideoChannelsList(server.url, 0, 1) 277 const res = await getVideoChannelsList(server.url, 0, 1)
237 videoChannelId = res.body.data[0].id 278 videoChannelId = res.body.data[0].id
238 279
239 await deleteVideoChannel(server.url, server.accessToken, videoChannelId, 409) 280 await deleteVideoChannel(server.url, server.accessToken, accountId, videoChannelId, 409)
240 }) 281 })
241 }) 282 })
242 283
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index 2563939ec..6238cdc08 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -39,6 +39,7 @@ import {
39 getVideoCommentThreads, 39 getVideoCommentThreads,
40 getVideoThreadComments 40 getVideoThreadComments
41} from '../../utils/videos/video-comments' 41} from '../../utils/videos/video-comments'
42import { getAccountsList } from '../../utils/users/accounts'
42 43
43const expect = chai.expect 44const expect = chai.expect
44 45
@@ -46,6 +47,7 @@ describe('Test multiple servers', function () {
46 let servers: ServerInfo[] = [] 47 let servers: ServerInfo[] = []
47 const toRemove = [] 48 const toRemove = []
48 let videoUUID = '' 49 let videoUUID = ''
50 let accountId: number
49 let videoChannelId: number 51 let videoChannelId: number
50 52
51 before(async function () { 53 before(async function () {
@@ -56,13 +58,20 @@ describe('Test multiple servers', function () {
56 // Get the access tokens 58 // Get the access tokens
57 await setAccessTokensToServers(servers) 59 await setAccessTokensToServers(servers)
58 60
59 const videoChannel = { 61 {
60 name: 'my channel', 62 const res = await getAccountsList(servers[0].url)
61 description: 'super channel' 63 accountId = res.body.data[0].id
64 }
65
66 {
67 const videoChannel = {
68 name: 'my channel',
69 description: 'super channel'
70 }
71 await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, accountId, videoChannel)
72 const channelRes = await getVideoChannelsList(servers[ 0 ].url, 0, 1)
73 videoChannelId = channelRes.body.data[ 0 ].id
62 } 74 }
63 await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
64 const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
65 videoChannelId = channelRes.body.data[0].id
66 75
67 // Server 1 and server 2 follow each other 76 // Server 1 and server 2 follow each other
68 await doubleFollow(servers[0], servers[1]) 77 await doubleFollow(servers[0], servers[1])
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index b9c9bbf3c..a7552a83a 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -17,12 +17,14 @@ import {
17 setAccessTokensToServers, 17 setAccessTokensToServers,
18 updateVideoChannel 18 updateVideoChannel
19} from '../../utils/index' 19} from '../../utils/index'
20import { getAccountsList } from '../../utils/users/accounts'
20 21
21const expect = chai.expect 22const expect = chai.expect
22 23
23describe('Test video channels', function () { 24describe('Test video channels', function () {
24 let servers: ServerInfo[] 25 let servers: ServerInfo[]
25 let userInfo: User 26 let userInfo: User
27 let accountId: number
26 let videoChannelId: number 28 let videoChannelId: number
27 29
28 before(async function () { 30 before(async function () {
@@ -35,6 +37,11 @@ describe('Test video channels', function () {
35 await setAccessTokensToServers(servers) 37 await setAccessTokensToServers(servers)
36 await doubleFollow(servers[0], servers[1]) 38 await doubleFollow(servers[0], servers[1])
37 39
40 {
41 const res = await getAccountsList(servers[0].url)
42 accountId = res.body.data[0].id
43 }
44
38 await wait(5000) 45 await wait(5000)
39 }) 46 })
40 47
@@ -54,7 +61,7 @@ describe('Test video channels', function () {
54 description: 'super video channel description', 61 description: 'super video channel description',
55 support: 'super video channel support text' 62 support: 'super video channel support text'
56 } 63 }
57 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) 64 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannel)
58 videoChannelId = res.body.videoChannel.id 65 videoChannelId = res.body.videoChannel.id
59 66
60 // The channel is 1 is propagated to servers 2 67 // The channel is 1 is propagated to servers 2
@@ -120,7 +127,7 @@ describe('Test video channels', function () {
120 support: 'video channel support text updated' 127 support: 'video channel support text updated'
121 } 128 }
122 129
123 await updateVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId, videoChannelAttributes) 130 await updateVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannelId, videoChannelAttributes)
124 131
125 await wait(3000) 132 await wait(3000)
126 }) 133 })
@@ -139,7 +146,7 @@ describe('Test video channels', function () {
139 }) 146 })
140 147
141 it('Should get video channel', async function () { 148 it('Should get video channel', async function () {
142 const res = await getVideoChannel(servers[0].url, videoChannelId) 149 const res = await getVideoChannel(servers[0].url, accountId, videoChannelId)
143 150
144 const videoChannel = res.body 151 const videoChannel = res.body
145 expect(videoChannel.displayName).to.equal('video channel updated') 152 expect(videoChannel.displayName).to.equal('video channel updated')
@@ -148,7 +155,7 @@ describe('Test video channels', function () {
148 }) 155 })
149 156
150 it('Should delete video channel', async function () { 157 it('Should delete video channel', async function () {
151 await deleteVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId) 158 await deleteVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannelId)
152 }) 159 })
153 160
154 it('Should have video channel deleted', async function () { 161 it('Should have video channel deleted', async function () {
diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts
index 2d095d8ab..cfc541431 100644
--- a/server/tests/utils/videos/video-channels.ts
+++ b/server/tests/utils/videos/video-channels.ts
@@ -7,7 +7,7 @@ type VideoChannelAttributes = {
7} 7}
8 8
9function getVideoChannelsList (url: string, start: number, count: number, sort?: string) { 9function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
10 const path = '/api/v1/videos/channels' 10 const path = '/api/v1/video-channels'
11 11
12 const req = request(url) 12 const req = request(url)
13 .get(path) 13 .get(path)
@@ -22,7 +22,7 @@ function getVideoChannelsList (url: string, start: number, count: number, sort?:
22} 22}
23 23
24function getAccountVideoChannelsList (url: string, accountId: number | string, specialStatus = 200) { 24function getAccountVideoChannelsList (url: string, accountId: number | string, specialStatus = 200) {
25 const path = '/api/v1/videos/accounts/' + accountId + '/channels' 25 const path = '/api/v1/accounts/' + accountId + '/video-channels'
26 26
27 return request(url) 27 return request(url)
28 .get(path) 28 .get(path)
@@ -31,8 +31,14 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s
31 .expect('Content-Type', /json/) 31 .expect('Content-Type', /json/)
32} 32}
33 33
34function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 200) { 34function addVideoChannel (
35 const path = '/api/v1/videos/channels' 35 url: string,
36 token: string,
37 accountId: number,
38 videoChannelAttributesArg: VideoChannelAttributes,
39 expectedStatus = 200
40) {
41 const path = '/api/v1/accounts/' + accountId + '/video-channels/'
36 42
37 // Default attributes 43 // Default attributes
38 let attributes = { 44 let attributes = {
@@ -50,9 +56,16 @@ function addVideoChannel (url: string, token: string, videoChannelAttributesArg:
50 .expect(expectedStatus) 56 .expect(expectedStatus)
51} 57}
52 58
53function updateVideoChannel (url: string, token: string, channelId: number, attributes: VideoChannelAttributes, expectedStatus = 204) { 59function updateVideoChannel (
60 url: string,
61 token: string,
62 accountId: number,
63 channelId: number,
64 attributes: VideoChannelAttributes,
65 expectedStatus = 204
66) {
54 const body = {} 67 const body = {}
55 const path = '/api/v1/videos/channels/' + channelId 68 const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
56 69
57 if (attributes.name) body['name'] = attributes.name 70 if (attributes.name) body['name'] = attributes.name
58 if (attributes.description) body['description'] = attributes.description 71 if (attributes.description) body['description'] = attributes.description
@@ -66,18 +79,18 @@ function updateVideoChannel (url: string, token: string, channelId: number, attr
66 .expect(expectedStatus) 79 .expect(expectedStatus)
67} 80}
68 81
69function deleteVideoChannel (url: string, token: string, channelId: number, expectedStatus = 204) { 82function deleteVideoChannel (url: string, token: string, accountId: number, channelId: number, expectedStatus = 204) {
70 const path = '/api/v1/videos/channels/' 83 const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
71 84
72 return request(url) 85 return request(url)
73 .delete(path + channelId) 86 .delete(path)
74 .set('Accept', 'application/json') 87 .set('Accept', 'application/json')
75 .set('Authorization', 'Bearer ' + token) 88 .set('Authorization', 'Bearer ' + token)
76 .expect(expectedStatus) 89 .expect(expectedStatus)
77} 90}
78 91
79function getVideoChannel (url: string, channelId: number) { 92function getVideoChannel (url: string, accountId: number, channelId: number) {
80 const path = '/api/v1/videos/channels/' + channelId 93 const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
81 94
82 return request(url) 95 return request(url)
83 .get(path) 96 .get(path)