diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/accounts.ts | 27 | ||||
-rw-r--r-- | server/controllers/api/users/me.ts | 3 | ||||
-rw-r--r-- | server/controllers/api/users/my-subscriptions.ts | 23 | ||||
-rw-r--r-- | server/controllers/api/video-channel.ts | 23 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 25 | ||||
-rw-r--r-- | server/helpers/custom-validators/search.ts | 4 | ||||
-rw-r--r-- | server/initializers/constants.ts | 5 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-transcoding.ts | 7 | ||||
-rw-r--r-- | server/lib/video.ts | 8 | ||||
-rw-r--r-- | server/middlewares/validators/videos/videos.ts | 8 | ||||
-rw-r--r-- | server/models/video/video-query-builder.ts | 12 | ||||
-rw-r--r-- | server/models/video/video.ts | 52 | ||||
-rw-r--r-- | server/tests/api/live/live.ts | 65 | ||||
-rw-r--r-- | server/tests/api/search/search-videos.ts | 49 | ||||
-rw-r--r-- | server/tests/api/videos/single-server.ts | 4 | ||||
-rw-r--r-- | server/tests/api/videos/video-transcoder.ts | 7 |
16 files changed, 233 insertions, 89 deletions
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index e31924a94..49a8e3195 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getServerActor } from '@server/models/application/application' | 2 | import { getServerActor } from '@server/models/application/application' |
3 | import { VideosWithSearchCommonQuery } from '@shared/models' | ||
3 | import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 4 | import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
4 | import { getFormattedObjects } from '../../helpers/utils' | 5 | import { getFormattedObjects } from '../../helpers/utils' |
5 | import { Hooks } from '../../lib/plugins/hooks' | ||
6 | import { JobQueue } from '../../lib/job-queue' | 6 | import { JobQueue } from '../../lib/job-queue' |
7 | import { Hooks } from '../../lib/plugins/hooks' | ||
7 | import { | 8 | import { |
8 | asyncMiddleware, | 9 | asyncMiddleware, |
9 | authenticate, | 10 | authenticate, |
@@ -158,25 +159,27 @@ async function listAccountVideos (req: express.Request, res: express.Response) { | |||
158 | const account = res.locals.account | 159 | const account = res.locals.account |
159 | const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined | 160 | const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined |
160 | const countVideos = getCountVideos(req) | 161 | const countVideos = getCountVideos(req) |
162 | const query = req.query as VideosWithSearchCommonQuery | ||
161 | 163 | ||
162 | const apiOptions = await Hooks.wrapObject({ | 164 | const apiOptions = await Hooks.wrapObject({ |
163 | followerActorId, | 165 | followerActorId, |
164 | start: req.query.start, | 166 | start: query.start, |
165 | count: req.query.count, | 167 | count: query.count, |
166 | sort: req.query.sort, | 168 | sort: query.sort, |
167 | includeLocalVideos: true, | 169 | includeLocalVideos: true, |
168 | categoryOneOf: req.query.categoryOneOf, | 170 | categoryOneOf: query.categoryOneOf, |
169 | licenceOneOf: req.query.licenceOneOf, | 171 | licenceOneOf: query.licenceOneOf, |
170 | languageOneOf: req.query.languageOneOf, | 172 | languageOneOf: query.languageOneOf, |
171 | tagsOneOf: req.query.tagsOneOf, | 173 | tagsOneOf: query.tagsOneOf, |
172 | tagsAllOf: req.query.tagsAllOf, | 174 | tagsAllOf: query.tagsAllOf, |
173 | filter: req.query.filter, | 175 | filter: query.filter, |
174 | nsfw: buildNSFWFilter(res, req.query.nsfw), | 176 | isLive: query.isLive, |
177 | nsfw: buildNSFWFilter(res, query.nsfw), | ||
175 | withFiles: false, | 178 | withFiles: false, |
176 | accountId: account.id, | 179 | accountId: account.id, |
177 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined, | 180 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined, |
178 | countVideos, | 181 | countVideos, |
179 | search: req.query.search | 182 | search: query.search |
180 | }, 'filter:api.accounts.videos.list.params') | 183 | }, 'filter:api.accounts.videos.list.params') |
181 | 184 | ||
182 | const resultList = await Hooks.wrapPromiseFun( | 185 | const resultList = await Hooks.wrapPromiseFun( |
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 9f9d2d77f..0763d1900 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -111,7 +111,8 @@ async function getUserVideos (req: express.Request, res: express.Response) { | |||
111 | start: req.query.start, | 111 | start: req.query.start, |
112 | count: req.query.count, | 112 | count: req.query.count, |
113 | sort: req.query.sort, | 113 | sort: req.query.sort, |
114 | search: req.query.search | 114 | search: req.query.search, |
115 | isLive: req.query.isLive | ||
115 | }, 'filter:api.user.me.videos.list.params') | 116 | }, 'filter:api.user.me.videos.list.params') |
116 | 117 | ||
117 | const resultList = await Hooks.wrapPromiseFun( | 118 | const resultList = await Hooks.wrapPromiseFun( |
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index e8949ee59..56b93276f 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -2,8 +2,8 @@ import 'multer' | |||
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { sendUndoFollow } from '@server/lib/activitypub/send' | 3 | import { sendUndoFollow } from '@server/lib/activitypub/send' |
4 | import { VideoChannelModel } from '@server/models/video/video-channel' | 4 | import { VideoChannelModel } from '@server/models/video/video-channel' |
5 | import { VideosCommonQuery } from '@shared/models' | ||
5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
6 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | ||
7 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' | 7 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' |
8 | import { getFormattedObjects } from '../../../helpers/utils' | 8 | import { getFormattedObjects } from '../../../helpers/utils' |
9 | import { WEBSERVER } from '../../../initializers/constants' | 9 | import { WEBSERVER } from '../../../initializers/constants' |
@@ -170,19 +170,20 @@ async function getUserSubscriptions (req: express.Request, res: express.Response | |||
170 | async function getUserSubscriptionVideos (req: express.Request, res: express.Response) { | 170 | async function getUserSubscriptionVideos (req: express.Request, res: express.Response) { |
171 | const user = res.locals.oauth.token.User | 171 | const user = res.locals.oauth.token.User |
172 | const countVideos = getCountVideos(req) | 172 | const countVideos = getCountVideos(req) |
173 | const query = req.query as VideosCommonQuery | ||
173 | 174 | ||
174 | const resultList = await VideoModel.listForApi({ | 175 | const resultList = await VideoModel.listForApi({ |
175 | start: req.query.start, | 176 | start: query.start, |
176 | count: req.query.count, | 177 | count: query.count, |
177 | sort: req.query.sort, | 178 | sort: query.sort, |
178 | includeLocalVideos: false, | 179 | includeLocalVideos: false, |
179 | categoryOneOf: req.query.categoryOneOf, | 180 | categoryOneOf: query.categoryOneOf, |
180 | licenceOneOf: req.query.licenceOneOf, | 181 | licenceOneOf: query.licenceOneOf, |
181 | languageOneOf: req.query.languageOneOf, | 182 | languageOneOf: query.languageOneOf, |
182 | tagsOneOf: req.query.tagsOneOf, | 183 | tagsOneOf: query.tagsOneOf, |
183 | tagsAllOf: req.query.tagsAllOf, | 184 | tagsAllOf: query.tagsAllOf, |
184 | nsfw: buildNSFWFilter(res, req.query.nsfw), | 185 | nsfw: buildNSFWFilter(res, query.nsfw), |
185 | filter: req.query.filter as VideoFilter, | 186 | filter: query.filter, |
186 | withFiles: false, | 187 | withFiles: false, |
187 | followerActorId: user.Account.Actor.id, | 188 | followerActorId: user.Account.Actor.id, |
188 | user, | 189 | user, |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 149d6cfb4..a755d7e57 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -2,7 +2,7 @@ import * as express from 'express' | |||
2 | import { Hooks } from '@server/lib/plugins/hooks' | 2 | import { Hooks } from '@server/lib/plugins/hooks' |
3 | import { getServerActor } from '@server/models/application/application' | 3 | import { getServerActor } from '@server/models/application/application' |
4 | import { MChannelBannerAccountDefault } from '@server/types/models' | 4 | import { MChannelBannerAccountDefault } from '@server/types/models' |
5 | import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared' | 5 | import { ActorImageType, VideoChannelCreate, VideoChannelUpdate, VideosCommonQuery } from '../../../shared' |
6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
7 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' | 7 | import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' |
8 | import { resetSequelizeInstance } from '../../helpers/database-utils' | 8 | import { resetSequelizeInstance } from '../../helpers/database-utils' |
@@ -312,20 +312,21 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon | |||
312 | const videoChannelInstance = res.locals.videoChannel | 312 | const videoChannelInstance = res.locals.videoChannel |
313 | const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined | 313 | const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined |
314 | const countVideos = getCountVideos(req) | 314 | const countVideos = getCountVideos(req) |
315 | const query = req.query as VideosCommonQuery | ||
315 | 316 | ||
316 | const apiOptions = await Hooks.wrapObject({ | 317 | const apiOptions = await Hooks.wrapObject({ |
317 | followerActorId, | 318 | followerActorId, |
318 | start: req.query.start, | 319 | start: query.start, |
319 | count: req.query.count, | 320 | count: query.count, |
320 | sort: req.query.sort, | 321 | sort: query.sort, |
321 | includeLocalVideos: true, | 322 | includeLocalVideos: true, |
322 | categoryOneOf: req.query.categoryOneOf, | 323 | categoryOneOf: query.categoryOneOf, |
323 | licenceOneOf: req.query.licenceOneOf, | 324 | licenceOneOf: query.licenceOneOf, |
324 | languageOneOf: req.query.languageOneOf, | 325 | languageOneOf: query.languageOneOf, |
325 | tagsOneOf: req.query.tagsOneOf, | 326 | tagsOneOf: query.tagsOneOf, |
326 | tagsAllOf: req.query.tagsAllOf, | 327 | tagsAllOf: query.tagsAllOf, |
327 | filter: req.query.filter, | 328 | filter: query.filter, |
328 | nsfw: buildNSFWFilter(res, req.query.nsfw), | 329 | nsfw: buildNSFWFilter(res, query.nsfw), |
329 | withFiles: false, | 330 | withFiles: false, |
330 | videoChannelId: videoChannelInstance.id, | 331 | videoChannelId: videoChannelInstance.id, |
331 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined, | 332 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined, |
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 7fee278f2..6ec6478e4 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -10,9 +10,8 @@ import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnail | |||
10 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | 10 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' |
11 | import { getServerActor } from '@server/models/application/application' | 11 | import { getServerActor } from '@server/models/application/application' |
12 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | 12 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' |
13 | import { VideoCreate, VideoState, VideoUpdate } from '../../../../shared' | 13 | import { VideoCreate, VideosCommonQuery, VideoState, VideoUpdate } from '../../../../shared' |
14 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 14 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
15 | import { VideoFilter } from '../../../../shared/models/videos/video-query.type' | ||
16 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 15 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
17 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' | 16 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' |
18 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' | 17 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' |
@@ -494,20 +493,22 @@ async function getVideoFileMetadata (req: express.Request, res: express.Response | |||
494 | } | 493 | } |
495 | 494 | ||
496 | async function listVideos (req: express.Request, res: express.Response) { | 495 | async function listVideos (req: express.Request, res: express.Response) { |
496 | const query = req.query as VideosCommonQuery | ||
497 | const countVideos = getCountVideos(req) | 497 | const countVideos = getCountVideos(req) |
498 | 498 | ||
499 | const apiOptions = await Hooks.wrapObject({ | 499 | const apiOptions = await Hooks.wrapObject({ |
500 | start: req.query.start, | 500 | start: query.start, |
501 | count: req.query.count, | 501 | count: query.count, |
502 | sort: req.query.sort, | 502 | sort: query.sort, |
503 | includeLocalVideos: true, | 503 | includeLocalVideos: true, |
504 | categoryOneOf: req.query.categoryOneOf, | 504 | categoryOneOf: query.categoryOneOf, |
505 | licenceOneOf: req.query.licenceOneOf, | 505 | licenceOneOf: query.licenceOneOf, |
506 | languageOneOf: req.query.languageOneOf, | 506 | languageOneOf: query.languageOneOf, |
507 | tagsOneOf: req.query.tagsOneOf, | 507 | tagsOneOf: query.tagsOneOf, |
508 | tagsAllOf: req.query.tagsAllOf, | 508 | tagsAllOf: query.tagsAllOf, |
509 | nsfw: buildNSFWFilter(res, req.query.nsfw), | 509 | nsfw: buildNSFWFilter(res, query.nsfw), |
510 | filter: req.query.filter as VideoFilter, | 510 | isLive: query.isLive, |
511 | filter: query.filter, | ||
511 | withFiles: false, | 512 | withFiles: false, |
512 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined, | 513 | user: res.locals.oauth ? res.locals.oauth.token.User : undefined, |
513 | countVideos | 514 | countVideos |
diff --git a/server/helpers/custom-validators/search.ts b/server/helpers/custom-validators/search.ts index 429fcafcf..a8f258838 100644 --- a/server/helpers/custom-validators/search.ts +++ b/server/helpers/custom-validators/search.ts | |||
@@ -11,7 +11,7 @@ function isStringArray (value: any) { | |||
11 | return isArray(value) && value.every(v => typeof v === 'string') | 11 | return isArray(value) && value.every(v => typeof v === 'string') |
12 | } | 12 | } |
13 | 13 | ||
14 | function isNSFWQueryValid (value: any) { | 14 | function isBooleanBothQueryValid (value: any) { |
15 | return value === 'true' || value === 'false' || value === 'both' | 15 | return value === 'true' || value === 'false' || value === 'both' |
16 | } | 16 | } |
17 | 17 | ||
@@ -32,6 +32,6 @@ function isSearchTargetValid (value: SearchTargetType) { | |||
32 | export { | 32 | export { |
33 | isNumberArray, | 33 | isNumberArray, |
34 | isStringArray, | 34 | isStringArray, |
35 | isNSFWQueryValid, | 35 | isBooleanBothQueryValid, |
36 | isSearchTargetValid | 36 | isSearchTargetValid |
37 | } | 37 | } |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 37a963760..d390fd95e 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -188,10 +188,7 @@ const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = | |||
188 | } | 188 | } |
189 | } | 189 | } |
190 | const JOB_PRIORITY = { | 190 | const JOB_PRIORITY = { |
191 | TRANSCODING: { | 191 | TRANSCODING: 100 |
192 | OPTIMIZER: 10, | ||
193 | NEW_RESOLUTION: 100 | ||
194 | } | ||
195 | } | 192 | } |
196 | 193 | ||
197 | const BROADCAST_CONCURRENCY = 30 // How many requests in parallel we do in activitypub-http-broadcast job | 194 | const BROADCAST_CONCURRENCY = 30 // How many requests in parallel we do in activitypub-http-broadcast job |
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 4ee2b2df2..010b95b05 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' | 2 | import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' |
3 | import { JOB_PRIORITY } from '@server/initializers/constants' | 3 | import { getTranscodingJobPriority, publishAndFederateIfNeeded } from '@server/lib/video' |
4 | import { getJobTranscodingPriorityMalus, publishAndFederateIfNeeded } from '@server/lib/video' | ||
5 | import { getVideoFilePath } from '@server/lib/video-paths' | 4 | import { getVideoFilePath } from '@server/lib/video-paths' |
6 | import { UserModel } from '@server/models/account/user' | 5 | import { UserModel } from '@server/models/account/user' |
7 | import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' | 6 | import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' |
@@ -215,7 +214,7 @@ async function createHlsJobIfEnabled (user: MUserId, payload: { | |||
215 | if (!payload || CONFIG.TRANSCODING.HLS.ENABLED !== true) return false | 214 | if (!payload || CONFIG.TRANSCODING.HLS.ENABLED !== true) return false |
216 | 215 | ||
217 | const jobOptions = { | 216 | const jobOptions = { |
218 | priority: JOB_PRIORITY.TRANSCODING.NEW_RESOLUTION + await getJobTranscodingPriorityMalus(user) | 217 | priority: await getTranscodingJobPriority(user) |
219 | } | 218 | } |
220 | 219 | ||
221 | const hlsTranscodingPayload: HLSTranscodingPayload = { | 220 | const hlsTranscodingPayload: HLSTranscodingPayload = { |
@@ -272,7 +271,7 @@ async function createLowerResolutionsJobs ( | |||
272 | resolutionCreated.push(resolution) | 271 | resolutionCreated.push(resolution) |
273 | 272 | ||
274 | const jobOptions = { | 273 | const jobOptions = { |
275 | priority: JOB_PRIORITY.TRANSCODING.NEW_RESOLUTION + await getJobTranscodingPriorityMalus(user) | 274 | priority: await getTranscodingJobPriority(user) |
276 | } | 275 | } |
277 | 276 | ||
278 | JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }, jobOptions) | 277 | JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }, jobOptions) |
diff --git a/server/lib/video.ts b/server/lib/video.ts index e381e0a69..9469b8178 100644 --- a/server/lib/video.ts +++ b/server/lib/video.ts | |||
@@ -121,19 +121,19 @@ async function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile, | |||
121 | } | 121 | } |
122 | 122 | ||
123 | const jobOptions = { | 123 | const jobOptions = { |
124 | priority: JOB_PRIORITY.TRANSCODING.OPTIMIZER + await getJobTranscodingPriorityMalus(user) | 124 | priority: await getTranscodingJobPriority(user) |
125 | } | 125 | } |
126 | 126 | ||
127 | return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput }, jobOptions) | 127 | return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput }, jobOptions) |
128 | } | 128 | } |
129 | 129 | ||
130 | async function getJobTranscodingPriorityMalus (user: MUserId) { | 130 | async function getTranscodingJobPriority (user: MUserId) { |
131 | const now = new Date() | 131 | const now = new Date() |
132 | const lastWeek = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7) | 132 | const lastWeek = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7) |
133 | 133 | ||
134 | const videoUploadedByUser = await VideoModel.countVideosUploadedByUserSince(user.id, lastWeek) | 134 | const videoUploadedByUser = await VideoModel.countVideosUploadedByUserSince(user.id, lastWeek) |
135 | 135 | ||
136 | return videoUploadedByUser | 136 | return JOB_PRIORITY.TRANSCODING + videoUploadedByUser |
137 | } | 137 | } |
138 | 138 | ||
139 | // --------------------------------------------------------------------------- | 139 | // --------------------------------------------------------------------------- |
@@ -144,5 +144,5 @@ export { | |||
144 | buildVideoThumbnailsFromReq, | 144 | buildVideoThumbnailsFromReq, |
145 | setVideoTags, | 145 | setVideoTags, |
146 | addOptimizeOrMergeAudioJob, | 146 | addOptimizeOrMergeAudioJob, |
147 | getJobTranscodingPriorityMalus | 147 | getTranscodingJobPriority |
148 | } | 148 | } |
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index 4d31d3dcb..bb617d77c 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -20,7 +20,7 @@ import { | |||
20 | toIntOrNull, | 20 | toIntOrNull, |
21 | toValueOrNull | 21 | toValueOrNull |
22 | } from '../../../helpers/custom-validators/misc' | 22 | } from '../../../helpers/custom-validators/misc' |
23 | import { isNSFWQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' | 23 | import { isBooleanBothQueryValid, isNumberArray, isStringArray } from '../../../helpers/custom-validators/search' |
24 | import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' | 24 | import { checkUserCanTerminateOwnershipChange, doesChangeVideoOwnershipExist } from '../../../helpers/custom-validators/video-ownership' |
25 | import { | 25 | import { |
26 | isScheduleVideoUpdatePrivacyValid, | 26 | isScheduleVideoUpdatePrivacyValid, |
@@ -439,7 +439,11 @@ const commonVideosFiltersValidator = [ | |||
439 | .custom(isStringArray).withMessage('Should have a valid all of tags array'), | 439 | .custom(isStringArray).withMessage('Should have a valid all of tags array'), |
440 | query('nsfw') | 440 | query('nsfw') |
441 | .optional() | 441 | .optional() |
442 | .custom(isNSFWQueryValid).withMessage('Should have a valid NSFW attribute'), | 442 | .custom(isBooleanBothQueryValid).withMessage('Should have a valid NSFW attribute'), |
443 | query('isLive') | ||
444 | .optional() | ||
445 | .customSanitizer(toBooleanOrNull) | ||
446 | .custom(isBooleanValid).withMessage('Should have a valid live boolean'), | ||
443 | query('filter') | 447 | query('filter') |
444 | .optional() | 448 | .optional() |
445 | .custom(isVideoFilterValid).withMessage('Should have a valid filter attribute'), | 449 | .custom(isVideoFilterValid).withMessage('Should have a valid filter attribute'), |
diff --git a/server/models/video/video-query-builder.ts b/server/models/video/video-query-builder.ts index 4d95ddee2..155afe64b 100644 --- a/server/models/video/video-query-builder.ts +++ b/server/models/video/video-query-builder.ts | |||
@@ -16,9 +16,11 @@ export type BuildVideosQueryOptions = { | |||
16 | start: number | 16 | start: number |
17 | sort: string | 17 | sort: string |
18 | 18 | ||
19 | nsfw?: boolean | ||
19 | filter?: VideoFilter | 20 | filter?: VideoFilter |
21 | isLive?: boolean | ||
22 | |||
20 | categoryOneOf?: number[] | 23 | categoryOneOf?: number[] |
21 | nsfw?: boolean | ||
22 | licenceOneOf?: number[] | 24 | licenceOneOf?: number[] |
23 | languageOneOf?: string[] | 25 | languageOneOf?: string[] |
24 | tagsOneOf?: string[] | 26 | tagsOneOf?: string[] |
@@ -199,10 +201,14 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
199 | 201 | ||
200 | if (options.nsfw === true) { | 202 | if (options.nsfw === true) { |
201 | and.push('"video"."nsfw" IS TRUE') | 203 | and.push('"video"."nsfw" IS TRUE') |
204 | } else if (options.nsfw === false) { | ||
205 | and.push('"video"."nsfw" IS FALSE') | ||
202 | } | 206 | } |
203 | 207 | ||
204 | if (options.nsfw === false) { | 208 | if (options.isLive === true) { |
205 | and.push('"video"."nsfw" IS FALSE') | 209 | and.push('"video"."isLive" IS TRUE') |
210 | } else if (options.isLive === false) { | ||
211 | and.push('"video"."isLive" IS FALSE') | ||
206 | } | 212 | } |
207 | 213 | ||
208 | if (options.categoryOneOf) { | 214 | if (options.categoryOneOf) { |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 422bf6deb..e55a21a6b 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1021,14 +1021,28 @@ export class VideoModel extends Model { | |||
1021 | start: number | 1021 | start: number |
1022 | count: number | 1022 | count: number |
1023 | sort: string | 1023 | sort: string |
1024 | isLive?: boolean | ||
1024 | search?: string | 1025 | search?: string |
1025 | }) { | 1026 | }) { |
1026 | const { accountId, start, count, sort, search } = options | 1027 | const { accountId, start, count, sort, search, isLive } = options |
1027 | 1028 | ||
1028 | function buildBaseQuery (): FindOptions { | 1029 | function buildBaseQuery (): FindOptions { |
1029 | let baseQuery = { | 1030 | const where: WhereOptions = {} |
1031 | |||
1032 | if (search) { | ||
1033 | where.name = { | ||
1034 | [Op.iLike]: '%' + search + '%' | ||
1035 | } | ||
1036 | } | ||
1037 | |||
1038 | if (isLive) { | ||
1039 | where.isLive = isLive | ||
1040 | } | ||
1041 | |||
1042 | const baseQuery = { | ||
1030 | offset: start, | 1043 | offset: start, |
1031 | limit: count, | 1044 | limit: count, |
1045 | where, | ||
1032 | order: getVideoSort(sort), | 1046 | order: getVideoSort(sort), |
1033 | include: [ | 1047 | include: [ |
1034 | { | 1048 | { |
@@ -1047,16 +1061,6 @@ export class VideoModel extends Model { | |||
1047 | ] | 1061 | ] |
1048 | } | 1062 | } |
1049 | 1063 | ||
1050 | if (search) { | ||
1051 | baseQuery = Object.assign(baseQuery, { | ||
1052 | where: { | ||
1053 | name: { | ||
1054 | [Op.iLike]: '%' + search + '%' | ||
1055 | } | ||
1056 | } | ||
1057 | }) | ||
1058 | } | ||
1059 | |||
1060 | return baseQuery | 1064 | return baseQuery |
1061 | } | 1065 | } |
1062 | 1066 | ||
@@ -1084,23 +1088,34 @@ export class VideoModel extends Model { | |||
1084 | start: number | 1088 | start: number |
1085 | count: number | 1089 | count: number |
1086 | sort: string | 1090 | sort: string |
1091 | |||
1087 | nsfw: boolean | 1092 | nsfw: boolean |
1093 | filter?: VideoFilter | ||
1094 | isLive?: boolean | ||
1095 | |||
1088 | includeLocalVideos: boolean | 1096 | includeLocalVideos: boolean |
1089 | withFiles: boolean | 1097 | withFiles: boolean |
1098 | |||
1090 | categoryOneOf?: number[] | 1099 | categoryOneOf?: number[] |
1091 | licenceOneOf?: number[] | 1100 | licenceOneOf?: number[] |
1092 | languageOneOf?: string[] | 1101 | languageOneOf?: string[] |
1093 | tagsOneOf?: string[] | 1102 | tagsOneOf?: string[] |
1094 | tagsAllOf?: string[] | 1103 | tagsAllOf?: string[] |
1095 | filter?: VideoFilter | 1104 | |
1096 | accountId?: number | 1105 | accountId?: number |
1097 | videoChannelId?: number | 1106 | videoChannelId?: number |
1107 | |||
1098 | followerActorId?: number | 1108 | followerActorId?: number |
1109 | |||
1099 | videoPlaylistId?: number | 1110 | videoPlaylistId?: number |
1111 | |||
1100 | trendingDays?: number | 1112 | trendingDays?: number |
1113 | |||
1101 | user?: MUserAccountId | 1114 | user?: MUserAccountId |
1102 | historyOfUser?: MUserId | 1115 | historyOfUser?: MUserId |
1116 | |||
1103 | countVideos?: boolean | 1117 | countVideos?: boolean |
1118 | |||
1104 | search?: string | 1119 | search?: string |
1105 | }) { | 1120 | }) { |
1106 | if ((options.filter === 'all-local' || options.filter === 'all') && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { | 1121 | if ((options.filter === 'all-local' || options.filter === 'all') && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { |
@@ -1128,6 +1143,7 @@ export class VideoModel extends Model { | |||
1128 | followerActorId, | 1143 | followerActorId, |
1129 | serverAccountId: serverActor.Account.id, | 1144 | serverAccountId: serverActor.Account.id, |
1130 | nsfw: options.nsfw, | 1145 | nsfw: options.nsfw, |
1146 | isLive: options.isLive, | ||
1131 | categoryOneOf: options.categoryOneOf, | 1147 | categoryOneOf: options.categoryOneOf, |
1132 | licenceOneOf: options.licenceOneOf, | 1148 | licenceOneOf: options.licenceOneOf, |
1133 | languageOneOf: options.languageOneOf, | 1149 | languageOneOf: options.languageOneOf, |
@@ -1160,6 +1176,7 @@ export class VideoModel extends Model { | |||
1160 | originallyPublishedStartDate?: string | 1176 | originallyPublishedStartDate?: string |
1161 | originallyPublishedEndDate?: string | 1177 | originallyPublishedEndDate?: string |
1162 | nsfw?: boolean | 1178 | nsfw?: boolean |
1179 | isLive?: boolean | ||
1163 | categoryOneOf?: number[] | 1180 | categoryOneOf?: number[] |
1164 | licenceOneOf?: number[] | 1181 | licenceOneOf?: number[] |
1165 | languageOneOf?: string[] | 1182 | languageOneOf?: string[] |
@@ -1171,23 +1188,32 @@ export class VideoModel extends Model { | |||
1171 | filter?: VideoFilter | 1188 | filter?: VideoFilter |
1172 | }) { | 1189 | }) { |
1173 | const serverActor = await getServerActor() | 1190 | const serverActor = await getServerActor() |
1191 | |||
1174 | const queryOptions = { | 1192 | const queryOptions = { |
1175 | followerActorId: serverActor.id, | 1193 | followerActorId: serverActor.id, |
1176 | serverAccountId: serverActor.Account.id, | 1194 | serverAccountId: serverActor.Account.id, |
1195 | |||
1177 | includeLocalVideos: options.includeLocalVideos, | 1196 | includeLocalVideos: options.includeLocalVideos, |
1178 | nsfw: options.nsfw, | 1197 | nsfw: options.nsfw, |
1198 | isLive: options.isLive, | ||
1199 | |||
1179 | categoryOneOf: options.categoryOneOf, | 1200 | categoryOneOf: options.categoryOneOf, |
1180 | licenceOneOf: options.licenceOneOf, | 1201 | licenceOneOf: options.licenceOneOf, |
1181 | languageOneOf: options.languageOneOf, | 1202 | languageOneOf: options.languageOneOf, |
1203 | |||
1182 | tagsOneOf: options.tagsOneOf, | 1204 | tagsOneOf: options.tagsOneOf, |
1183 | tagsAllOf: options.tagsAllOf, | 1205 | tagsAllOf: options.tagsAllOf, |
1206 | |||
1184 | user: options.user, | 1207 | user: options.user, |
1185 | filter: options.filter, | 1208 | filter: options.filter, |
1209 | |||
1186 | start: options.start, | 1210 | start: options.start, |
1187 | count: options.count, | 1211 | count: options.count, |
1188 | sort: options.sort, | 1212 | sort: options.sort, |
1213 | |||
1189 | startDate: options.startDate, | 1214 | startDate: options.startDate, |
1190 | endDate: options.endDate, | 1215 | endDate: options.endDate, |
1216 | |||
1191 | originallyPublishedStartDate: options.originallyPublishedStartDate, | 1217 | originallyPublishedStartDate: options.originallyPublishedStartDate, |
1192 | originallyPublishedEndDate: options.originallyPublishedEndDate, | 1218 | originallyPublishedEndDate: options.originallyPublishedEndDate, |
1193 | 1219 | ||
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts index d48e2a8ee..57fb58150 100644 --- a/server/tests/api/live/live.ts +++ b/server/tests/api/live/live.ts | |||
@@ -19,10 +19,12 @@ import { | |||
19 | doubleFollow, | 19 | doubleFollow, |
20 | flushAndRunMultipleServers, | 20 | flushAndRunMultipleServers, |
21 | getLive, | 21 | getLive, |
22 | getMyVideosWithFilter, | ||
22 | getPlaylist, | 23 | getPlaylist, |
23 | getVideo, | 24 | getVideo, |
24 | getVideoIdFromUUID, | 25 | getVideoIdFromUUID, |
25 | getVideosList, | 26 | getVideosList, |
27 | getVideosWithFilters, | ||
26 | killallServers, | 28 | killallServers, |
27 | makeRawRequest, | 29 | makeRawRequest, |
28 | removeVideo, | 30 | removeVideo, |
@@ -37,6 +39,7 @@ import { | |||
37 | testImage, | 39 | testImage, |
38 | updateCustomSubConfig, | 40 | updateCustomSubConfig, |
39 | updateLive, | 41 | updateLive, |
42 | uploadVideoAndGetId, | ||
40 | viewVideo, | 43 | viewVideo, |
41 | wait, | 44 | wait, |
42 | waitJobs, | 45 | waitJobs, |
@@ -229,6 +232,68 @@ describe('Test live', function () { | |||
229 | }) | 232 | }) |
230 | }) | 233 | }) |
231 | 234 | ||
235 | describe('Live filters', function () { | ||
236 | let command: any | ||
237 | let liveVideoId: string | ||
238 | let vodVideoId: string | ||
239 | |||
240 | before(async function () { | ||
241 | this.timeout(120000) | ||
242 | |||
243 | vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid | ||
244 | |||
245 | const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id } | ||
246 | const resLive = await createLive(servers[0].url, servers[0].accessToken, liveOptions) | ||
247 | liveVideoId = resLive.body.video.uuid | ||
248 | |||
249 | command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) | ||
250 | await waitUntilLivePublishedOnAllServers(liveVideoId) | ||
251 | await waitJobs(servers) | ||
252 | }) | ||
253 | |||
254 | it('Should only display lives', async function () { | ||
255 | const res = await getVideosWithFilters(servers[0].url, { isLive: true }) | ||
256 | |||
257 | expect(res.body.total).to.equal(1) | ||
258 | expect(res.body.data).to.have.lengthOf(1) | ||
259 | expect(res.body.data[0].name).to.equal('live') | ||
260 | }) | ||
261 | |||
262 | it('Should not display lives', async function () { | ||
263 | const res = await getVideosWithFilters(servers[0].url, { isLive: false }) | ||
264 | |||
265 | expect(res.body.total).to.equal(1) | ||
266 | expect(res.body.data).to.have.lengthOf(1) | ||
267 | expect(res.body.data[0].name).to.equal('vod video') | ||
268 | }) | ||
269 | |||
270 | it('Should display my lives', async function () { | ||
271 | this.timeout(60000) | ||
272 | |||
273 | await stopFfmpeg(command) | ||
274 | await waitJobs(servers) | ||
275 | |||
276 | const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true }) | ||
277 | const videos = res.body.data as Video[] | ||
278 | |||
279 | const result = videos.every(v => v.isLive) | ||
280 | expect(result).to.be.true | ||
281 | }) | ||
282 | |||
283 | it('Should not display my lives', async function () { | ||
284 | const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: false }) | ||
285 | const videos = res.body.data as Video[] | ||
286 | |||
287 | const result = videos.every(v => !v.isLive) | ||
288 | expect(result).to.be.true | ||
289 | }) | ||
290 | |||
291 | after(async function () { | ||
292 | await removeVideo(servers[0].url, servers[0].accessToken, vodVideoId) | ||
293 | await removeVideo(servers[0].url, servers[0].accessToken, liveVideoId) | ||
294 | }) | ||
295 | }) | ||
296 | |||
232 | describe('Stream checks', function () { | 297 | describe('Stream checks', function () { |
233 | let liveVideo: LiveVideo & VideoDetails | 298 | let liveVideo: LiveVideo & VideoDetails |
234 | let rtmpUrl: string | 299 | let rtmpUrl: string |
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts index e05c3a269..5b8907961 100644 --- a/server/tests/api/search/search-videos.ts +++ b/server/tests/api/search/search-videos.ts | |||
@@ -1,17 +1,24 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | ||
5 | import { VideoPrivacy } from '@shared/models' | ||
5 | import { | 6 | import { |
6 | advancedVideosSearch, | 7 | advancedVideosSearch, |
7 | cleanupTests, | 8 | cleanupTests, |
9 | createLive, | ||
8 | flushAndRunServer, | 10 | flushAndRunServer, |
9 | immutableAssign, | 11 | immutableAssign, |
10 | searchVideo, | 12 | searchVideo, |
13 | sendRTMPStreamInVideo, | ||
11 | ServerInfo, | 14 | ServerInfo, |
12 | setAccessTokensToServers, | 15 | setAccessTokensToServers, |
16 | setDefaultVideoChannel, | ||
17 | stopFfmpeg, | ||
18 | updateCustomSubConfig, | ||
13 | uploadVideo, | 19 | uploadVideo, |
14 | wait | 20 | wait, |
21 | waitUntilLivePublished | ||
15 | } from '../../../../shared/extra-utils' | 22 | } from '../../../../shared/extra-utils' |
16 | import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions' | 23 | import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions' |
17 | 24 | ||
@@ -28,6 +35,7 @@ describe('Test videos search', function () { | |||
28 | server = await flushAndRunServer(1) | 35 | server = await flushAndRunServer(1) |
29 | 36 | ||
30 | await setAccessTokensToServers([ server ]) | 37 | await setAccessTokensToServers([ server ]) |
38 | await setDefaultVideoChannel([ server ]) | ||
31 | 39 | ||
32 | { | 40 | { |
33 | const attributes1 = { | 41 | const attributes1 = { |
@@ -449,6 +457,43 @@ describe('Test videos search', function () { | |||
449 | expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') | 457 | expect(res.body.data[0].name).to.equal('1111 2222 3333 - 3') |
450 | }) | 458 | }) |
451 | 459 | ||
460 | it('Should search by live', async function () { | ||
461 | this.timeout(30000) | ||
462 | |||
463 | { | ||
464 | const options = { | ||
465 | search: { | ||
466 | searchIndex: { enabled: false } | ||
467 | }, | ||
468 | live: { enabled: true } | ||
469 | } | ||
470 | await updateCustomSubConfig(server.url, server.accessToken, options) | ||
471 | } | ||
472 | |||
473 | { | ||
474 | const res = await advancedVideosSearch(server.url, { isLive: true }) | ||
475 | |||
476 | expect(res.body.total).to.equal(0) | ||
477 | expect(res.body.data).to.have.lengthOf(0) | ||
478 | } | ||
479 | |||
480 | { | ||
481 | const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id } | ||
482 | const resLive = await createLive(server.url, server.accessToken, liveOptions) | ||
483 | const liveVideoId = resLive.body.video.uuid | ||
484 | |||
485 | const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId) | ||
486 | await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) | ||
487 | |||
488 | const res = await advancedVideosSearch(server.url, { isLive: true }) | ||
489 | |||
490 | expect(res.body.total).to.equal(1) | ||
491 | expect(res.body.data[0].name).to.equal('live') | ||
492 | |||
493 | await stopFfmpeg(command) | ||
494 | } | ||
495 | }) | ||
496 | |||
452 | after(async function () { | 497 | after(async function () { |
453 | await cleanupTests([ server ]) | 498 | await cleanupTests([ server ]) |
454 | }) | 499 | }) |
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts index da90223b8..a79648bf7 100644 --- a/server/tests/api/videos/single-server.ts +++ b/server/tests/api/videos/single-server.ts | |||
@@ -387,11 +387,11 @@ describe('Test a single server', function () { | |||
387 | }) | 387 | }) |
388 | 388 | ||
389 | it('Should filter by tags and category', async function () { | 389 | it('Should filter by tags and category', async function () { |
390 | const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: 4 }) | 390 | const res1 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 4 ] }) |
391 | expect(res1.body.total).to.equal(1) | 391 | expect(res1.body.total).to.equal(1) |
392 | expect(res1.body.data[0].name).to.equal('my super video updated') | 392 | expect(res1.body.data[0].name).to.equal('my super video updated') |
393 | 393 | ||
394 | const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: 3 }) | 394 | const res2 = await getVideosWithFilters(server.url, { tagsAllOf: [ 'tagup1', 'tagup2' ], categoryOneOf: [ 3 ] }) |
395 | expect(res2.body.total).to.equal(0) | 395 | expect(res2.body.total).to.equal(0) |
396 | }) | 396 | }) |
397 | 397 | ||
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index 1058baaa3..1c99f26df 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts | |||
@@ -721,12 +721,7 @@ describe('Test video transcoding', function () { | |||
721 | expect(webtorrentJobs).to.have.lengthOf(6) | 721 | expect(webtorrentJobs).to.have.lengthOf(6) |
722 | expect(optimizeJobs).to.have.lengthOf(1) | 722 | expect(optimizeJobs).to.have.lengthOf(1) |
723 | 723 | ||
724 | for (const j of optimizeJobs) { | 724 | for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) { |
725 | expect(j.priority).to.be.greaterThan(11) | ||
726 | expect(j.priority).to.be.lessThan(50) | ||
727 | } | ||
728 | |||
729 | for (const j of hlsJobs.concat(webtorrentJobs)) { | ||
730 | expect(j.priority).to.be.greaterThan(100) | 725 | expect(j.priority).to.be.greaterThan(100) |
731 | expect(j.priority).to.be.lessThan(150) | 726 | expect(j.priority).to.be.lessThan(150) |
732 | } | 727 | } |