X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fsql%2Fvideos-id-list-query-builder.ts;h=4a882e7905b3fdf08a2fb59b728fa0c24e5430ee;hb=d324756edb836672f12284cd18e642a658b273d8;hp=054f71c8c0e48a5b5166ebef9d2c4da93edbb562;hpb=ac27887774e63d99f4e227fbe18846f143cc4b3c;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/sql/videos-id-list-query-builder.ts b/server/models/video/sql/videos-id-list-query-builder.ts index 054f71c8c..4a882e790 100644 --- a/server/models/video/sql/videos-id-list-query-builder.ts +++ b/server/models/video/sql/videos-id-list-query-builder.ts @@ -1,10 +1,11 @@ import { Sequelize } from 'sequelize' import validator from 'validator' import { exists } from '@server/helpers/custom-validators/misc' +import { WEBSERVER } from '@server/initializers/constants' import { buildDirectionAndField, createSafeIn } from '@server/models/utils' import { MUserAccountId, MUserId } from '@server/types/models' -import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' -import { AbstractVideosQueryBuilder } from './shared/abstract-videos-query-builder' +import { VideoInclude, VideoPrivacy, VideoState } from '@shared/models' +import { AbstractRunQuery } from './shared/abstract-run-query' /** * @@ -12,20 +13,27 @@ import { AbstractVideosQueryBuilder } from './shared/abstract-videos-query-build * */ +export type DisplayOnlyForFollowerOptions = { + actorId: number + orLocalVideos: boolean +} + export type BuildVideosListQueryOptions = { attributes?: string[] - serverAccountId: number - followerActorId: number - includeLocalVideos: boolean + serverAccountIdForBlock: number + + displayOnlyForFollower: DisplayOnlyForFollowerOptions count: number start: number sort: string nsfw?: boolean - filter?: VideoFilter + host?: string isLive?: boolean + isLocal?: boolean + include?: VideoInclude categoryOneOf?: number[] licenceOneOf?: number[] @@ -33,7 +41,11 @@ export type BuildVideosListQueryOptions = { tagsOneOf?: string[] tagsAllOf?: string[] - withFiles?: boolean + uuids?: string[] + + hasFiles?: boolean + hasHLSFiles?: boolean + hasWebtorrentFiles?: boolean accountId?: number videoChannelId?: number @@ -62,7 +74,7 @@ export type BuildVideosListQueryOptions = { having?: string } -export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { +export class VideosIdListQueryBuilder extends AbstractRunQuery { protected replacements: any = {} private attributes: string[] @@ -95,8 +107,9 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { return this.runQuery().then(rows => rows.length !== 0 ? rows[0].total : 0) } - getIdsListQueryAndSort (options: BuildVideosListQueryOptions) { + getQuery (options: BuildVideosListQueryOptions) { this.buildIdsListQuery(options) + return { query: this.query, sort: this.sort, replacements: this.replacements } } @@ -112,23 +125,34 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { 'INNER JOIN "actor" "accountActor" ON "account"."actorId" = "accountActor"."id"' ]) - this.whereNotBlacklisted() + if (!(options.include & VideoInclude.BLACKLISTED)) { + this.whereNotBlacklisted() + } + + if (options.serverAccountIdForBlock && !(options.include & VideoInclude.BLOCKED_OWNER)) { + this.whereNotBlocked(options.serverAccountIdForBlock, options.user) + } - if (options.serverAccountId) { - this.whereNotBlocked(options.serverAccountId, options.user) + // Only list published videos + if (!(options.include & VideoInclude.NOT_PUBLISHED_STATE)) { + this.whereStateAvailable() } - // Only list public/published videos - if (!options.filter || (options.filter !== 'all-local' && options.filter !== 'all')) { - this.whereStateAndPrivacyAvailable(options.user) + // Only list videos with the appropriate priavcy + if (!(options.include & VideoInclude.HIDDEN_PRIVACY)) { + this.wherePrivacyAvailable(options.user) } if (options.videoPlaylistId) { this.joinPlaylist(options.videoPlaylistId) } - if (options.filter && (options.filter === 'local' || options.filter === 'all-local')) { - this.whereOnlyLocal() + if (exists(options.isLocal)) { + this.whereLocal(options.isLocal) + } + + if (options.host) { + this.whereHost(options.host) } if (options.accountId) { @@ -139,14 +163,22 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { this.whereChannelId(options.videoChannelId) } - if (options.followerActorId) { - this.whereFollowerActorId(options.followerActorId, options.includeLocalVideos) + if (options.displayOnlyForFollower) { + this.whereFollowerActorId(options.displayOnlyForFollower) } - if (options.withFiles === true) { + if (options.hasFiles === true) { this.whereFileExists() } + if (exists(options.hasWebtorrentFiles)) { + this.whereWebTorrentFileExists(options.hasWebtorrentFiles) + } + + if (exists(options.hasHLSFiles)) { + this.whereHLSFileExists(options.hasHLSFiles) + } + if (options.tagsOneOf) { this.whereTagsOneOf(options.tagsOneOf) } @@ -155,6 +187,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { this.whereTagsAllOf(options.tagsAllOf) } + if (options.uuids) { + this.whereUUIDs(options.uuids) + } + if (options.nsfw === true) { this.whereNSFW() } else if (options.nsfw === false) { @@ -270,12 +306,14 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { this.replacements.videoPlaylistId = playlistId } - private whereStateAndPrivacyAvailable (user?: MUserAccountId) { + private whereStateAvailable () { this.and.push( `("video"."state" = ${VideoState.PUBLISHED} OR ` + `("video"."state" = ${VideoState.TO_TRANSCODE} AND "video"."waitTranscoding" IS false))` ) + } + private wherePrivacyAvailable (user?: MUserAccountId) { if (user) { this.and.push( `("video"."privacy" = ${VideoPrivacy.PUBLIC} OR "video"."privacy" = ${VideoPrivacy.INTERNAL})` @@ -287,8 +325,23 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { } } - private whereOnlyLocal () { - this.and.push('"video"."remote" IS FALSE') + private whereLocal (isLocal: boolean) { + const isRemote = isLocal ? 'FALSE' : 'TRUE' + + this.and.push('"video"."remote" IS ' + isRemote) + } + + private whereHost (host: string) { + // Local instance + if (host === WEBSERVER.HOST) { + this.and.push('"accountActor"."serverId" IS NULL') + return + } + + this.joins.push('INNER JOIN "server" ON "server"."id" = "accountActor"."serverId"') + + this.and.push('"server"."host" = :host') + this.replacements.host = host } private whereAccountId (accountId: number) { @@ -301,7 +354,7 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { this.replacements.videoChannelId = channelId } - private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) { + private whereFollowerActorId (options: { actorId: number, orLocalVideos: boolean }) { let query = '(' + ' EXISTS (' + // Videos shared by actors we follow @@ -317,27 +370,42 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { ' AND "actorFollow"."state" = \'accepted\'' + ' )' - if (includeLocalVideos) { + if (options.orLocalVideos) { query += ' OR "video"."remote" IS FALSE' } query += ')' this.and.push(query) - this.replacements.followerActorId = followerActorId + this.replacements.followerActorId = options.actorId } private whereFileExists () { - this.and.push( - '(' + - ' EXISTS (SELECT 1 FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id") ' + - ' OR EXISTS (' + - ' SELECT 1 FROM "videoStreamingPlaylist" ' + - ' INNER JOIN "videoFile" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist"."id" ' + - ' WHERE "videoStreamingPlaylist"."videoId" = "video"."id"' + - ' )' + - ')' - ) + this.and.push(`(${this.buildWebTorrentFileExistsQuery(true)} OR ${this.buildHLSFileExistsQuery(true)})`) + } + + private whereWebTorrentFileExists (exists: boolean) { + this.and.push(this.buildWebTorrentFileExistsQuery(exists)) + } + + private whereHLSFileExists (exists: boolean) { + this.and.push(this.buildHLSFileExistsQuery(exists)) + } + + private buildWebTorrentFileExistsQuery (exists: boolean) { + const prefix = exists ? '' : 'NOT ' + + return prefix + 'EXISTS (SELECT 1 FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id")' + } + + private buildHLSFileExistsQuery (exists: boolean) { + const prefix = exists ? '' : 'NOT ' + + return prefix + 'EXISTS (' + + ' SELECT 1 FROM "videoStreamingPlaylist" ' + + ' INNER JOIN "videoFile" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist"."id" ' + + ' WHERE "videoStreamingPlaylist"."videoId" = "video"."id"' + + ')' } private whereTagsOneOf (tagsOneOf: string[]) { @@ -367,6 +435,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { ) } + private whereUUIDs (uuids: string[]) { + this.and.push('"video"."uuid" IN (' + createSafeIn(this.sequelize, uuids) + ')') + } + private whereCategoryOneOf (categoryOneOf: number[]) { this.and.push('"video"."category" IN (:categoryOneOf)') this.replacements.categoryOneOf = categoryOneOf