Add host to common video filters

This commit is contained in:
Chocobozzz 2025-01-31 15:49:38 +01:00
parent e433a74bb6
commit 94a7879d7d
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
12 changed files with 103 additions and 29 deletions

View File

@ -210,6 +210,8 @@ export class CustomMarkupService {
accountHandle: data.accountHandle || undefined,
channelHandle: data.channelHandle || undefined,
host: data.host || undefined,
isLive: this.buildBoolean(data.isLive),
isLocal: this.buildBoolean(data.onlyLocal) ? true : undefined

View File

@ -1,14 +1,14 @@
import { finalize, map } from 'rxjs/operators'
import { NgFor, NgStyle } from '@angular/common'
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { AuthService, Notifier } from '@app/core'
import { Video } from '@app/shared/shared-main/video/video.model'
import { CommonVideoParams, VideoService } from '@app/shared/shared-main/video/video.service'
import { objectKeysTyped } from '@peertube/peertube-core-utils'
import { ResultList, VideoSortField } from '@peertube/peertube-models'
import { CustomMarkupComponent } from './shared'
import { Observable } from 'rxjs'
import { finalize, map } from 'rxjs/operators'
import { MiniatureDisplayOptions, VideoMiniatureComponent } from '../../shared-video-miniature/video-miniature.component'
import { NgStyle, NgFor } from '@angular/common'
import { Video } from '@app/shared/shared-main/video/video.model'
import { VideoService } from '@app/shared/shared-main/video/video.service'
import { CustomMarkupComponent } from './shared'
/*
* Markup component list videos depending on criteria
@ -33,6 +33,7 @@ export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit
@Input() maxRows: number
@Input() channelHandle: string
@Input() accountHandle: string
@Input() host: string
@Output() loaded = new EventEmitter<boolean>()
@ -90,26 +91,37 @@ export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit
}
getVideosObservable () {
const options = {
const options: CommonVideoParams = {
videoPagination: {
currentPage: 1,
itemsPerPage: this.count
},
categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf,
host: this.host,
isLocal: this.isLocal,
isLive: this.isLive,
sort: this.sort as VideoSortField,
account: { nameWithHost: this.accountHandle },
videoChannel: { nameWithHost: this.channelHandle },
skipCount: true
}
let obs: Observable<ResultList<Video>>
if (this.channelHandle) obs = this.videoService.getVideoChannelVideos(options)
else if (this.accountHandle) obs = this.videoService.getAccountVideos(options)
else obs = this.videoService.getVideos(options)
if (this.channelHandle) {
obs = this.videoService.getVideoChannelVideos({
...options,
videoChannel: { nameWithHost: this.channelHandle }
})
} else if (this.accountHandle) {
obs = this.videoService.getAccountVideos({
...options,
account: { nameWithHost: this.accountHandle }
})
} else {
obs = this.videoService.getVideos(options)
}
return obs.pipe(map(({ data }) => data))
}

View File

@ -51,6 +51,7 @@ export type CommonVideoParams = {
isLive?: boolean
skipCount?: boolean
nsfw?: BooleanBothQuery
host?: string
search?: string
}
@ -548,6 +549,7 @@ export class VideoService {
isLive,
nsfw,
search,
host,
...otherOptions
} = options
@ -568,6 +570,7 @@ export class VideoService {
if (categoryOneOf !== undefined) newParams = this.restService.addArrayParams(newParams, 'categoryOneOf', categoryOneOf)
if (privacyOneOf !== undefined) newParams = this.restService.addArrayParams(newParams, 'privacyOneOf', privacyOneOf)
if (search) newParams = newParams.set('search', search)
if (host) newParams = newParams.set('host', host)
newParams = this.restService.addObjectParams(newParams, otherOptions)

View File

@ -38,6 +38,8 @@ export type VideosListMarkupData = {
channelHandle?: string
accountHandle?: string
host?: string
isLive?: string // number
onlyLocal?: StringBoolean

View File

@ -34,6 +34,8 @@ export interface VideosCommonQuery {
excludeAlreadyWatched?: boolean
host?: string
// Only available with special user right
autoTagOneOf?: string[]
privacyOneOf?: VideoPrivacyType[]

View File

@ -615,13 +615,15 @@ export class VideosCommand extends AbstractCommand {
'categoryOneOf',
'licenceOneOf',
'languageOneOf',
'host',
'privacyOneOf',
'tagsOneOf',
'tagsAllOf',
'isLocal',
'include',
'skipCount',
'autoTagOneOf'
'autoTagOneOf',
'search'
])
}

View File

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import { expect } from 'chai'
import { pick } from '@peertube/peertube-core-utils'
import {
HttpStatusCode,
@ -11,7 +10,8 @@ import {
VideoInclude,
VideoIncludeType,
VideoPrivacy,
VideoPrivacyType
VideoPrivacyType,
VideosCommonQuery
} from '@peertube/peertube-models'
import {
cleanupTests,
@ -24,6 +24,7 @@ import {
setDefaultVideoChannel,
waitJobs
} from '@peertube/peertube-server-commands'
import { expect } from 'chai'
describe('Test videos filter', function () {
let servers: PeerTubeServer[]
@ -89,17 +90,11 @@ describe('Test videos filter', function () {
async function listVideos (options: {
server: PeerTubeServer
path: string
isLocal?: boolean
hasWebVideoFiles?: boolean
hasHLSFiles?: boolean
include?: VideoIncludeType
privacyOneOf?: VideoPrivacyType[]
category?: number
tagsAllOf?: string[]
token?: string
expectedStatus?: HttpStatusCodeType
excludeAlreadyWatched?: boolean
}) {
} & VideosCommonQuery) {
const res = await makeGetRequest({
url: options.server.url,
path: options.path,
@ -108,12 +103,15 @@ describe('Test videos filter', function () {
...pick(options, [
'isLocal',
'include',
'category',
'tagsAllOf',
'hasWebVideoFiles',
'hasHLSFiles',
'tagsAllOf',
'categoryOneOf',
'languageOneOf',
'privacyOneOf',
'excludeAlreadyWatched'
'excludeAlreadyWatched',
'host',
'search'
]),
sort: 'createdAt'
@ -399,6 +397,51 @@ describe('Test videos filter', function () {
}
})
it('Should filter by language', async function () {
await servers[0].videos.upload({ attributes: { name: 'english', language: 'en' } })
await servers[0].videos.upload({ attributes: { name: 'french', language: 'fr' } })
for (const path of paths) {
{
const videos = await listVideos({ server: servers[0], path, languageOneOf: [ 'fr', 'en' ] })
expect(videos).to.have.lengthOf(2)
expect(videos.map(v => v.name)).to.have.members([ 'english', 'french' ])
}
{
const videos = await listVideos({ server: servers[0], path, languageOneOf: [ 'ca', 'es' ] })
expect(videos).to.have.lengthOf(0)
}
}
})
it('Should filter by host', async function () {
await servers[0].videos.upload({ attributes: { name: 'filter host 1' } })
await servers[1].videos.upload({ attributes: { name: 'filter host 2' } })
await waitJobs(servers)
const getVideos = (videos: Video[]) => videos.filter(v => v.name.includes('filter host'))
{
const { data } = await servers[0].videos.list({ search: 'filter host' })
expect(getVideos(data)).to.have.lengthOf(2)
}
{
const { data } = await servers[0].videos.list({ search: 'filter host', host: servers[0].host })
const videos = getVideos(data)
expect(videos).to.have.lengthOf(1)
expect(videos.map(v => v.name)).to.have.members([ 'filter host 1' ])
}
{
const { data } = await servers[0].videos.list({ host: 'example.com' })
expect(data).to.have.lengthOf(0)
}
})
it('Should filter by HLS or Web Video files', async function () {
this.timeout(360000)

View File

@ -16,6 +16,7 @@ function pickCommonVideoQuery (query: VideosCommonQueryAfterSanitize) {
'categoryOneOf',
'licenceOneOf',
'languageOneOf',
'host',
'privacyOneOf',
'tagsOneOf',
'tagsAllOf',

View File

@ -10,10 +10,6 @@ const videosSearchValidator = [
.optional()
.not().isEmpty(),
query('host')
.optional()
.custom(isHostValid),
query('startDate')
.optional()
.custom(isDateValid).withMessage('Should have a start date that conforms to ISO 8601'),

View File

@ -56,6 +56,7 @@ import {
} from '../shared/index.js'
import { addDurationToVideoFileIfNeeded, commonVideoFileChecks, isVideoFileAccepted } from './shared/index.js'
import { VideoLoadType } from '@server/lib/model-loaders/video.js'
import { isHostValid } from '@server/helpers/custom-validators/servers.js'
export const videosAddLegacyValidator = getCommonVideoEditAttributes().concat([
body('videofile')
@ -491,6 +492,9 @@ export const commonVideosFiltersValidator = [
.optional()
.customSanitizer(arrayify)
.custom(isStringArray).withMessage('Should have a valid autoTagOneOf array'),
query('host')
.optional()
.custom(isHostValid),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return

View File

@ -1141,6 +1141,8 @@ export class VideoModel extends SequelizeModel<VideoModel> {
tagsAllOf?: string[]
privacyOneOf?: VideoPrivacyType[]
host?: string
accountId?: number
videoChannelId?: number
@ -1184,6 +1186,7 @@ export class VideoModel extends SequelizeModel<VideoModel> {
'categoryOneOf',
'licenceOneOf',
'languageOneOf',
'host',
'autoTagOneOf',
'tagsOneOf',
'tagsAllOf',

View File

@ -794,6 +794,7 @@ paths:
- $ref: '#/components/parameters/languageOneOf'
- $ref: '#/components/parameters/autoTagOneOfVideo'
- $ref: '#/components/parameters/nsfw'
- $ref: '#/components/parameters/host'
- $ref: '#/components/parameters/isLocal'
- $ref: '#/components/parameters/include'
- $ref: '#/components/parameters/privacyOneOf'
@ -2256,6 +2257,7 @@ paths:
- $ref: '#/components/parameters/tagsAllOf'
- $ref: '#/components/parameters/licenceOneOf'
- $ref: '#/components/parameters/languageOneOf'
- $ref: '#/components/parameters/host'
- $ref: '#/components/parameters/autoTagOneOfVideo'
- $ref: '#/components/parameters/nsfw'
- $ref: '#/components/parameters/isLocal'
@ -2803,6 +2805,7 @@ paths:
- $ref: '#/components/parameters/tagsAllOf'
- $ref: '#/components/parameters/licenceOneOf'
- $ref: '#/components/parameters/languageOneOf'
- $ref: '#/components/parameters/host'
- $ref: '#/components/parameters/autoTagOneOfVideo'
- $ref: '#/components/parameters/nsfw'
- $ref: '#/components/parameters/isLocal'
@ -4497,6 +4500,7 @@ paths:
- $ref: '#/components/parameters/tagsAllOf'
- $ref: '#/components/parameters/licenceOneOf'
- $ref: '#/components/parameters/languageOneOf'
- $ref: '#/components/parameters/host'
- $ref: '#/components/parameters/autoTagOneOfVideo'
- $ref: '#/components/parameters/nsfw'
- $ref: '#/components/parameters/isLocal'