aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-10-10 11:46:50 +0200
committerChocobozzz <me@florianbigard.com>2018-10-10 11:46:50 +0200
commit1cd3facc3de899ac864e979cd6d6a704b712cce3 (patch)
tree7a781918d6bf1426ec76cc18390922f65971a278
parentb014b6b9c7cb68d09c52b44046afe486c0736426 (diff)
downloadPeerTube-1cd3facc3de899ac864e979cd6d6a704b712cce3.tar.gz
PeerTube-1cd3facc3de899ac864e979cd6d6a704b712cce3.tar.zst
PeerTube-1cd3facc3de899ac864e979cd6d6a704b712cce3.zip
Add ability to list all local videos
Including private/unlisted for moderators/admins
-rw-r--r--server/controllers/api/accounts.ts4
-rw-r--r--server/controllers/api/search.ts1
-rw-r--r--server/controllers/api/video-channel.ts4
-rw-r--r--server/controllers/feeds.ts10
-rw-r--r--server/helpers/custom-validators/videos.ts9
-rw-r--r--server/helpers/express-utils.ts3
-rw-r--r--server/middlewares/validators/search.ts38
-rw-r--r--server/middlewares/validators/videos/videos.ts54
-rw-r--r--server/models/video/video.ts26
-rw-r--r--server/tests/api/check-params/index.ts1
-rw-r--r--server/tests/api/check-params/videos-filter.ts127
-rw-r--r--server/tests/api/videos/index.ts1
-rw-r--r--server/tests/api/videos/videos-filter.ts130
-rw-r--r--shared/models/search/videos-search-query.model.ts3
-rw-r--r--shared/models/users/user-right.enum.ts1
-rw-r--r--shared/models/users/user-role.ts3
-rw-r--r--shared/models/videos/video-query.type.ts2
17 files changed, 363 insertions, 54 deletions
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index b7691ccba..8e3f60010 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -86,9 +86,11 @@ async function listAccountVideos (req: express.Request, res: express.Response, n
86 languageOneOf: req.query.languageOneOf, 86 languageOneOf: req.query.languageOneOf,
87 tagsOneOf: req.query.tagsOneOf, 87 tagsOneOf: req.query.tagsOneOf,
88 tagsAllOf: req.query.tagsAllOf, 88 tagsAllOf: req.query.tagsAllOf,
89 filter: req.query.filter,
89 nsfw: buildNSFWFilter(res, req.query.nsfw), 90 nsfw: buildNSFWFilter(res, req.query.nsfw),
90 withFiles: false, 91 withFiles: false,
91 accountId: account.id 92 accountId: account.id,
93 userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
92 }) 94 })
93 95
94 return res.json(getFormattedObjects(resultList.data, resultList.total)) 96 return res.json(getFormattedObjects(resultList.data, resultList.total))
diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts
index 4be2b5ef7..a8a6cfb08 100644
--- a/server/controllers/api/search.ts
+++ b/server/controllers/api/search.ts
@@ -118,6 +118,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response)
118 const options = Object.assign(query, { 118 const options = Object.assign(query, {
119 includeLocalVideos: true, 119 includeLocalVideos: true,
120 nsfw: buildNSFWFilter(res, query.nsfw), 120 nsfw: buildNSFWFilter(res, query.nsfw),
121 filter: query.filter,
121 userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined 122 userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
122 }) 123 })
123 const resultList = await VideoModel.searchAndPopulateAccountAndServer(options) 124 const resultList = await VideoModel.searchAndPopulateAccountAndServer(options)
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 1fa842d9c..c84d1be58 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -215,9 +215,11 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon
215 languageOneOf: req.query.languageOneOf, 215 languageOneOf: req.query.languageOneOf,
216 tagsOneOf: req.query.tagsOneOf, 216 tagsOneOf: req.query.tagsOneOf,
217 tagsAllOf: req.query.tagsAllOf, 217 tagsAllOf: req.query.tagsAllOf,
218 filter: req.query.filter,
218 nsfw: buildNSFWFilter(res, req.query.nsfw), 219 nsfw: buildNSFWFilter(res, req.query.nsfw),
219 withFiles: false, 220 withFiles: false,
220 videoChannelId: videoChannelInstance.id 221 videoChannelId: videoChannelInstance.id,
222 userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined
221 }) 223 })
222 224
223 return res.json(getFormattedObjects(resultList.data, resultList.total)) 225 return res.json(getFormattedObjects(resultList.data, resultList.total))
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index b30ad8e8d..ccb9b6029 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -1,7 +1,14 @@
1import * as express from 'express' 1import * as express from 'express'
2import { CONFIG, FEEDS, ROUTE_CACHE_LIFETIME } from '../initializers/constants' 2import { CONFIG, FEEDS, ROUTE_CACHE_LIFETIME } from '../initializers/constants'
3import { THUMBNAILS_SIZE } from '../initializers' 3import { THUMBNAILS_SIZE } from '../initializers'
4import { asyncMiddleware, setDefaultSort, videoCommentsFeedsValidator, videoFeedsValidator, videosSortValidator } from '../middlewares' 4import {
5 asyncMiddleware,
6 commonVideosFiltersValidator,
7 setDefaultSort,
8 videoCommentsFeedsValidator,
9 videoFeedsValidator,
10 videosSortValidator
11} from '../middlewares'
5import { VideoModel } from '../models/video/video' 12import { VideoModel } from '../models/video/video'
6import * as Feed from 'pfeed' 13import * as Feed from 'pfeed'
7import { AccountModel } from '../models/account/account' 14import { AccountModel } from '../models/account/account'
@@ -22,6 +29,7 @@ feedsRouter.get('/feeds/videos.:format',
22 videosSortValidator, 29 videosSortValidator,
23 setDefaultSort, 30 setDefaultSort,
24 asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS)), 31 asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS)),
32 commonVideosFiltersValidator,
25 asyncMiddleware(videoFeedsValidator), 33 asyncMiddleware(videoFeedsValidator),
26 asyncMiddleware(generateVideoFeed) 34 asyncMiddleware(generateVideoFeed)
27) 35)
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 714f7ac95..a13b09ac8 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -3,7 +3,7 @@ import 'express-validator'
3import { values } from 'lodash' 3import { values } from 'lodash'
4import 'multer' 4import 'multer'
5import * as validator from 'validator' 5import * as validator from 'validator'
6import { UserRight, VideoPrivacy, VideoRateType } from '../../../shared' 6import { UserRight, VideoFilter, VideoPrivacy, VideoRateType } from '../../../shared'
7import { 7import {
8 CONSTRAINTS_FIELDS, 8 CONSTRAINTS_FIELDS,
9 VIDEO_CATEGORIES, 9 VIDEO_CATEGORIES,
@@ -22,6 +22,10 @@ import { fetchVideo, VideoFetchType } from '../video'
22 22
23const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS 23const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
24 24
25function isVideoFilterValid (filter: VideoFilter) {
26 return filter === 'local' || filter === 'all-local'
27}
28
25function isVideoCategoryValid (value: any) { 29function isVideoCategoryValid (value: any) {
26 return value === null || VIDEO_CATEGORIES[ value ] !== undefined 30 return value === null || VIDEO_CATEGORIES[ value ] !== undefined
27} 31}
@@ -225,5 +229,6 @@ export {
225 isVideoExist, 229 isVideoExist,
226 isVideoImage, 230 isVideoImage,
227 isVideoChannelOfAccountExist, 231 isVideoChannelOfAccountExist,
228 isVideoSupportValid 232 isVideoSupportValid,
233 isVideoFilterValid
229} 234}
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts
index 8a9cee8c5..162fe2244 100644
--- a/server/helpers/express-utils.ts
+++ b/server/helpers/express-utils.ts
@@ -2,7 +2,6 @@ import * as express from 'express'
2import * as multer from 'multer' 2import * as multer from 'multer'
3import { CONFIG, REMOTE_SCHEME } from '../initializers' 3import { CONFIG, REMOTE_SCHEME } from '../initializers'
4import { logger } from './logger' 4import { logger } from './logger'
5import { User } from '../../shared/models/users'
6import { deleteFileAsync, generateRandomString } from './utils' 5import { deleteFileAsync, generateRandomString } from './utils'
7import { extname } from 'path' 6import { extname } from 'path'
8import { isArray } from './custom-validators/misc' 7import { isArray } from './custom-validators/misc'
@@ -101,7 +100,7 @@ function createReqFiles (
101} 100}
102 101
103function isUserAbleToSearchRemoteURI (res: express.Response) { 102function isUserAbleToSearchRemoteURI (res: express.Response) {
104 const user: User = res.locals.oauth ? res.locals.oauth.token.User : undefined 103 const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined
105 104
106 return CONFIG.SEARCH.REMOTE_URI.ANONYMOUS === true || 105 return CONFIG.SEARCH.REMOTE_URI.ANONYMOUS === true ||
107 (CONFIG.SEARCH.REMOTE_URI.USERS === true && user !== undefined) 106 (CONFIG.SEARCH.REMOTE_URI.USERS === true && user !== undefined)
diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts
index 8baf643a5..6a95d6095 100644
--- a/server/middlewares/validators/search.ts
+++ b/server/middlewares/validators/search.ts
@@ -2,8 +2,7 @@ import * as express from 'express'
2import { areValidationErrors } from './utils' 2import { areValidationErrors } from './utils'
3import { logger } from '../../helpers/logger' 3import { logger } from '../../helpers/logger'
4import { query } from 'express-validator/check' 4import { query } from 'express-validator/check'
5import { isNumberArray, isStringArray, isNSFWQueryValid } from '../../helpers/custom-validators/search' 5import { isDateValid } from '../../helpers/custom-validators/misc'
6import { isBooleanValid, isDateValid, toArray } from '../../helpers/custom-validators/misc'
7 6
8const videosSearchValidator = [ 7const videosSearchValidator = [
9 query('search').optional().not().isEmpty().withMessage('Should have a valid search'), 8 query('search').optional().not().isEmpty().withMessage('Should have a valid search'),
@@ -35,44 +34,9 @@ const videoChannelsSearchValidator = [
35 } 34 }
36] 35]
37 36
38const commonVideosFiltersValidator = [
39 query('categoryOneOf')
40 .optional()
41 .customSanitizer(toArray)
42 .custom(isNumberArray).withMessage('Should have a valid one of category array'),
43 query('licenceOneOf')
44 .optional()
45 .customSanitizer(toArray)
46 .custom(isNumberArray).withMessage('Should have a valid one of licence array'),
47 query('languageOneOf')
48 .optional()
49 .customSanitizer(toArray)
50 .custom(isStringArray).withMessage('Should have a valid one of language array'),
51 query('tagsOneOf')
52 .optional()
53 .customSanitizer(toArray)
54 .custom(isStringArray).withMessage('Should have a valid one of tags array'),
55 query('tagsAllOf')
56 .optional()
57 .customSanitizer(toArray)
58 .custom(isStringArray).withMessage('Should have a valid all of tags array'),
59 query('nsfw')
60 .optional()
61 .custom(isNSFWQueryValid).withMessage('Should have a valid NSFW attribute'),
62
63 (req: express.Request, res: express.Response, next: express.NextFunction) => {
64 logger.debug('Checking commons video filters query', { parameters: req.query })
65
66 if (areValidationErrors(req, res)) return
67
68 return next()
69 }
70]
71
72// --------------------------------------------------------------------------- 37// ---------------------------------------------------------------------------
73 38
74export { 39export {
75 commonVideosFiltersValidator,
76 videoChannelsSearchValidator, 40 videoChannelsSearchValidator,
77 videosSearchValidator 41 videosSearchValidator
78} 42}
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts
index 1d0a64bb1..9dc52a134 100644
--- a/server/middlewares/validators/videos/videos.ts
+++ b/server/middlewares/validators/videos/videos.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import 'express-validator' 2import 'express-validator'
3import { body, param, ValidationChain } from 'express-validator/check' 3import { body, param, query, ValidationChain } from 'express-validator/check'
4import { UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' 4import { UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared'
5import { 5import {
6 isBooleanValid, 6 isBooleanValid,
@@ -8,6 +8,7 @@ import {
8 isIdOrUUIDValid, 8 isIdOrUUIDValid,
9 isIdValid, 9 isIdValid,
10 isUUIDValid, 10 isUUIDValid,
11 toArray,
11 toIntOrNull, 12 toIntOrNull,
12 toValueOrNull 13 toValueOrNull
13} from '../../../helpers/custom-validators/misc' 14} from '../../../helpers/custom-validators/misc'
@@ -19,6 +20,7 @@ import {
19 isVideoDescriptionValid, 20 isVideoDescriptionValid,
20 isVideoExist, 21 isVideoExist,
21 isVideoFile, 22 isVideoFile,
23 isVideoFilterValid,
22 isVideoImage, 24 isVideoImage,
23 isVideoLanguageValid, 25 isVideoLanguageValid,
24 isVideoLicenceValid, 26 isVideoLicenceValid,
@@ -42,6 +44,7 @@ import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/vid
42import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership' 44import { VideoChangeOwnershipModel } from '../../../models/video/video-change-ownership'
43import { AccountModel } from '../../../models/account/account' 45import { AccountModel } from '../../../models/account/account'
44import { VideoFetchType } from '../../../helpers/video' 46import { VideoFetchType } from '../../../helpers/video'
47import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search'
45 48
46const videosAddValidator = getCommonVideoAttributes().concat([ 49const videosAddValidator = getCommonVideoAttributes().concat([
47 body('videofile') 50 body('videofile')
@@ -359,6 +362,51 @@ function getCommonVideoAttributes () {
359 ] as (ValidationChain | express.Handler)[] 362 ] as (ValidationChain | express.Handler)[]
360} 363}
361 364
365const commonVideosFiltersValidator = [
366 query('categoryOneOf')
367 .optional()
368 .customSanitizer(toArray)
369 .custom(isNumberArray).withMessage('Should have a valid one of category array'),
370 query('licenceOneOf')
371 .optional()
372 .customSanitizer(toArray)
373 .custom(isNumberArray).withMessage('Should have a valid one of licence array'),
374 query('languageOneOf')
375 .optional()
376 .customSanitizer(toArray)
377 .custom(isStringArray).withMessage('Should have a valid one of language array'),
378 query('tagsOneOf')
379 .optional()
380 .customSanitizer(toArray)
381 .custom(isStringArray).withMessage('Should have a valid one of tags array'),
382 query('tagsAllOf')
383 .optional()
384 .customSanitizer(toArray)
385 .custom(isStringArray).withMessage('Should have a valid all of tags array'),
386 query('nsfw')
387 .optional()
388 .custom(isNSFWQueryValid).withMessage('Should have a valid NSFW attribute'),
389 query('filter')
390 .optional()
391 .custom(isVideoFilterValid).withMessage('Should have a valid filter attribute'),
392
393 (req: express.Request, res: express.Response, next: express.NextFunction) => {
394 logger.debug('Checking commons video filters query', { parameters: req.query })
395
396 if (areValidationErrors(req, res)) return
397
398 const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined
399 if (req.query.filter === 'all-local' && (!user || user.hasRight(UserRight.SEE_ALL_VIDEOS) === false)) {
400 res.status(401)
401 .json({ error: 'You are not allowed to see all local videos.' })
402
403 return
404 }
405
406 return next()
407 }
408]
409
362// --------------------------------------------------------------------------- 410// ---------------------------------------------------------------------------
363 411
364export { 412export {
@@ -375,7 +423,9 @@ export {
375 videosTerminateChangeOwnershipValidator, 423 videosTerminateChangeOwnershipValidator,
376 videosAcceptChangeOwnershipValidator, 424 videosAcceptChangeOwnershipValidator,
377 425
378 getCommonVideoAttributes 426 getCommonVideoAttributes,
427
428 commonVideosFiltersValidator
379} 429}
380 430
381// --------------------------------------------------------------------------- 431// ---------------------------------------------------------------------------
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 070ac7623..4f3f75613 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -235,7 +235,14 @@ type AvailableForListIDsOptions = {
235 ) 235 )
236 } 236 }
237 ] 237 ]
238 }, 238 }
239 },
240 include: []
241 }
242
243 // Only list public/published videos
244 if (!options.filter || options.filter !== 'all-local') {
245 const privacyWhere = {
239 // Always list public videos 246 // Always list public videos
240 privacy: VideoPrivacy.PUBLIC, 247 privacy: VideoPrivacy.PUBLIC,
241 // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding 248 // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding
@@ -250,8 +257,9 @@ type AvailableForListIDsOptions = {
250 } 257 }
251 } 258 }
252 ] 259 ]
253 }, 260 }
254 include: [] 261
262 Object.assign(query.where, privacyWhere)
255 } 263 }
256 264
257 if (options.filter || options.accountId || options.videoChannelId) { 265 if (options.filter || options.accountId || options.videoChannelId) {
@@ -969,6 +977,10 @@ export class VideoModel extends Model<VideoModel> {
969 trendingDays?: number, 977 trendingDays?: number,
970 userId?: number 978 userId?: number
971 }, countVideos = true) { 979 }, countVideos = true) {
980 if (options.filter && options.filter === 'all-local' && !options.userId) {
981 throw new Error('Try to filter all-local but no userId is provided')
982 }
983
972 const query: IFindOptions<VideoModel> = { 984 const query: IFindOptions<VideoModel> = {
973 offset: options.start, 985 offset: options.start,
974 limit: options.count, 986 limit: options.count,
@@ -1021,7 +1033,8 @@ export class VideoModel extends Model<VideoModel> {
1021 tagsAllOf?: string[] 1033 tagsAllOf?: string[]
1022 durationMin?: number // seconds 1034 durationMin?: number // seconds
1023 durationMax?: number // seconds 1035 durationMax?: number // seconds
1024 userId?: number 1036 userId?: number,
1037 filter?: VideoFilter
1025 }) { 1038 }) {
1026 const whereAnd = [] 1039 const whereAnd = []
1027 1040
@@ -1098,7 +1111,8 @@ export class VideoModel extends Model<VideoModel> {
1098 languageOneOf: options.languageOneOf, 1111 languageOneOf: options.languageOneOf,
1099 tagsOneOf: options.tagsOneOf, 1112 tagsOneOf: options.tagsOneOf,
1100 tagsAllOf: options.tagsAllOf, 1113 tagsAllOf: options.tagsAllOf,
1101 userId: options.userId 1114 userId: options.userId,
1115 filter: options.filter
1102 } 1116 }
1103 1117
1104 return VideoModel.getAvailableForApi(query, queryOptions) 1118 return VideoModel.getAvailableForApi(query, queryOptions)
@@ -1262,7 +1276,7 @@ export class VideoModel extends Model<VideoModel> {
1262 } 1276 }
1263 1277
1264 private static buildActorWhereWithFilter (filter?: VideoFilter) { 1278 private static buildActorWhereWithFilter (filter?: VideoFilter) {
1265 if (filter && filter === 'local') { 1279 if (filter && (filter === 'local' || filter === 'all-local')) {
1266 return { 1280 return {
1267 serverId: null 1281 serverId: null
1268 } 1282 }
diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts
index 71a217649..bfc550ae5 100644
--- a/server/tests/api/check-params/index.ts
+++ b/server/tests/api/check-params/index.ts
@@ -15,4 +15,5 @@ import './video-channels'
15import './video-comments' 15import './video-comments'
16import './video-imports' 16import './video-imports'
17import './videos' 17import './videos'
18import './videos-filter'
18import './videos-history' 19import './videos-history'
diff --git a/server/tests/api/check-params/videos-filter.ts b/server/tests/api/check-params/videos-filter.ts
new file mode 100644
index 000000000..784cd8ba1
--- /dev/null
+++ b/server/tests/api/check-params/videos-filter.ts
@@ -0,0 +1,127 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 createUser,
7 flushTests,
8 killallServers,
9 makeGetRequest,
10 runServer,
11 ServerInfo,
12 setAccessTokensToServers,
13 userLogin
14} from '../../utils'
15import { UserRole } from '../../../../shared/models/users'
16
17const expect = chai.expect
18
19async function testEndpoints (server: ServerInfo, token: string, filter: string, statusCodeExpected: number) {
20 const paths = [
21 '/api/v1/video-channels/root_channel/videos',
22 '/api/v1/accounts/root/videos',
23 '/api/v1/videos',
24 '/api/v1/search/videos'
25 ]
26
27 for (const path of paths) {
28 await makeGetRequest({
29 url: server.url,
30 path,
31 token,
32 query: {
33 filter
34 },
35 statusCodeExpected
36 })
37 }
38}
39
40describe('Test videos filters', function () {
41 let server: ServerInfo
42 let userAccessToken: string
43 let moderatorAccessToken: string
44
45 // ---------------------------------------------------------------
46
47 before(async function () {
48 this.timeout(30000)
49
50 await flushTests()
51
52 server = await runServer(1)
53
54 await setAccessTokensToServers([ server ])
55
56 const user = { username: 'user1', password: 'my super password' }
57 await createUser(server.url, server.accessToken, user.username, user.password)
58 userAccessToken = await userLogin(server, user)
59
60 const moderator = { username: 'moderator', password: 'my super password' }
61 await createUser(
62 server.url,
63 server.accessToken,
64 moderator.username,
65 moderator.password,
66 undefined,
67 undefined,
68 UserRole.MODERATOR
69 )
70 moderatorAccessToken = await userLogin(server, moderator)
71 })
72
73 describe('When setting a video filter', function () {
74
75 it('Should fail with a bad filter', async function () {
76 await testEndpoints(server, server.accessToken, 'bad-filter', 400)
77 })
78
79 it('Should succeed with a good filter', async function () {
80 await testEndpoints(server, server.accessToken,'local', 200)
81 })
82
83 it('Should fail to list all-local with a simple user', async function () {
84 await testEndpoints(server, userAccessToken, 'all-local', 401)
85 })
86
87 it('Should succeed to list all-local with a moderator', async function () {
88 await testEndpoints(server, moderatorAccessToken, 'all-local', 200)
89 })
90
91 it('Should succeed to list all-local with an admin', async function () {
92 await testEndpoints(server, server.accessToken, 'all-local', 200)
93 })
94
95 // Because we cannot authenticate the user on the RSS endpoint
96 it('Should fail on the feeds endpoint with the all-local filter', async function () {
97 await makeGetRequest({
98 url: server.url,
99 path: '/feeds/videos.json',
100 statusCodeExpected: 401,
101 query: {
102 filter: 'all-local'
103 }
104 })
105 })
106
107 it('Should succed on the feeds endpoint with the local filter', async function () {
108 await makeGetRequest({
109 url: server.url,
110 path: '/feeds/videos.json',
111 statusCodeExpected: 200,
112 query: {
113 filter: 'local'
114 }
115 })
116 })
117 })
118
119 after(async function () {
120 killallServers([ server ])
121
122 // Keep the logs if the test failed
123 if (this['ok']) {
124 await flushTests()
125 }
126 })
127})
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts
index 09bb62a8d..9bdb78491 100644
--- a/server/tests/api/videos/index.ts
+++ b/server/tests/api/videos/index.ts
@@ -14,5 +14,6 @@ import './video-nsfw'
14import './video-privacy' 14import './video-privacy'
15import './video-schedule-update' 15import './video-schedule-update'
16import './video-transcoder' 16import './video-transcoder'
17import './videos-filter'
17import './videos-history' 18import './videos-history'
18import './videos-overview' 19import './videos-overview'
diff --git a/server/tests/api/videos/videos-filter.ts b/server/tests/api/videos/videos-filter.ts
new file mode 100644
index 000000000..a7588129f
--- /dev/null
+++ b/server/tests/api/videos/videos-filter.ts
@@ -0,0 +1,130 @@
1/* tslint:disable:no-unused-expression */
2
3import * as chai from 'chai'
4import 'mocha'
5import {
6 createUser,
7 doubleFollow,
8 flushAndRunMultipleServers,
9 flushTests,
10 killallServers,
11 makeGetRequest,
12 ServerInfo,
13 setAccessTokensToServers,
14 uploadVideo,
15 userLogin
16} from '../../utils'
17import { Video, VideoPrivacy } from '../../../../shared/models/videos'
18import { UserRole } from '../../../../shared/models/users'
19
20const expect = chai.expect
21
22async function getVideosNames (server: ServerInfo, token: string, filter: string, statusCodeExpected = 200) {
23 const paths = [
24 '/api/v1/video-channels/root_channel/videos',
25 '/api/v1/accounts/root/videos',
26 '/api/v1/videos',
27 '/api/v1/search/videos'
28 ]
29
30 const videosResults: Video[][] = []
31
32 for (const path of paths) {
33 const res = await makeGetRequest({
34 url: server.url,
35 path,
36 token,
37 query: {
38 sort: 'createdAt',
39 filter
40 },
41 statusCodeExpected
42 })
43
44 videosResults.push(res.body.data.map(v => v.name))
45 }
46
47 return videosResults
48}
49
50describe('Test videos filter validator', function () {
51 let servers: ServerInfo[]
52
53 // ---------------------------------------------------------------
54
55 before(async function () {
56 this.timeout(120000)
57
58 await flushTests()
59
60 servers = await flushAndRunMultipleServers(2)
61
62 await setAccessTokensToServers(servers)
63
64 for (const server of servers) {
65 const moderator = { username: 'moderator', password: 'my super password' }
66 await createUser(
67 server.url,
68 server.accessToken,
69 moderator.username,
70 moderator.password,
71 undefined,
72 undefined,
73 UserRole.MODERATOR
74 )
75 server['moderatorAccessToken'] = await userLogin(server, moderator)
76
77 await uploadVideo(server.url, server.accessToken, { name: 'public ' + server.serverNumber })
78
79 {
80 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED }
81 await uploadVideo(server.url, server.accessToken, attributes)
82 }
83
84 {
85 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE }
86 await uploadVideo(server.url, server.accessToken, attributes)
87 }
88 }
89
90 await doubleFollow(servers[0], servers[1])
91 })
92
93 describe('Check videos filter', function () {
94
95 it('Should display local videos', async function () {
96 for (const server of servers) {
97 const namesResults = await getVideosNames(server, server.accessToken, 'local')
98 for (const names of namesResults) {
99 expect(names).to.have.lengthOf(1)
100 expect(names[ 0 ]).to.equal('public ' + server.serverNumber)
101 }
102 }
103 })
104
105 it('Should display all local videos by the admin or the moderator', async function () {
106 for (const server of servers) {
107 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
108
109 const namesResults = await getVideosNames(server, token, 'all-local')
110 for (const names of namesResults) {
111 expect(names).to.have.lengthOf(3)
112
113 expect(names[ 0 ]).to.equal('public ' + server.serverNumber)
114 expect(names[ 1 ]).to.equal('unlisted ' + server.serverNumber)
115 expect(names[ 2 ]).to.equal('private ' + server.serverNumber)
116 }
117 }
118 }
119 })
120 })
121
122 after(async function () {
123 killallServers(servers)
124
125 // Keep the logs if the test failed
126 if (this['ok']) {
127 await flushTests()
128 }
129 })
130})
diff --git a/shared/models/search/videos-search-query.model.ts b/shared/models/search/videos-search-query.model.ts
index 29aa5c100..0db220758 100644
--- a/shared/models/search/videos-search-query.model.ts
+++ b/shared/models/search/videos-search-query.model.ts
@@ -1,4 +1,5 @@
1import { NSFWQuery } from './nsfw-query.model' 1import { NSFWQuery } from './nsfw-query.model'
2import { VideoFilter } from '../videos'
2 3
3export interface VideosSearchQuery { 4export interface VideosSearchQuery {
4 search?: string 5 search?: string
@@ -23,4 +24,6 @@ export interface VideosSearchQuery {
23 24
24 durationMin?: number // seconds 25 durationMin?: number // seconds
25 durationMax?: number // seconds 26 durationMax?: number // seconds
27
28 filter?: VideoFilter
26} 29}
diff --git a/shared/models/users/user-right.enum.ts b/shared/models/users/user-right.enum.ts
index c4ccd632f..ed2c536ce 100644
--- a/shared/models/users/user-right.enum.ts
+++ b/shared/models/users/user-right.enum.ts
@@ -14,5 +14,6 @@ export enum UserRight {
14 REMOVE_ANY_VIDEO_CHANNEL, 14 REMOVE_ANY_VIDEO_CHANNEL,
15 REMOVE_ANY_VIDEO_COMMENT, 15 REMOVE_ANY_VIDEO_COMMENT,
16 UPDATE_ANY_VIDEO, 16 UPDATE_ANY_VIDEO,
17 SEE_ALL_VIDEOS,
17 CHANGE_VIDEO_OWNERSHIP 18 CHANGE_VIDEO_OWNERSHIP
18} 19}
diff --git a/shared/models/users/user-role.ts b/shared/models/users/user-role.ts
index 552aad999..d7020c0f2 100644
--- a/shared/models/users/user-role.ts
+++ b/shared/models/users/user-role.ts
@@ -26,7 +26,8 @@ const userRoleRights: { [ id: number ]: UserRight[] } = {
26 UserRight.REMOVE_ANY_VIDEO, 26 UserRight.REMOVE_ANY_VIDEO,
27 UserRight.REMOVE_ANY_VIDEO_CHANNEL, 27 UserRight.REMOVE_ANY_VIDEO_CHANNEL,
28 UserRight.REMOVE_ANY_VIDEO_COMMENT, 28 UserRight.REMOVE_ANY_VIDEO_COMMENT,
29 UserRight.UPDATE_ANY_VIDEO 29 UserRight.UPDATE_ANY_VIDEO,
30 UserRight.SEE_ALL_VIDEOS
30 ], 31 ],
31 32
32 [UserRole.USER]: [] 33 [UserRole.USER]: []
diff --git a/shared/models/videos/video-query.type.ts b/shared/models/videos/video-query.type.ts
index ff0f527f3..f76a91aad 100644
--- a/shared/models/videos/video-query.type.ts
+++ b/shared/models/videos/video-query.type.ts
@@ -1 +1 @@
export type VideoFilter = 'local' export type VideoFilter = 'local' | 'all-local'