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'
/**
*
*
*/
+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[]
tagsOneOf?: string[]
tagsAllOf?: string[]
- withFiles?: boolean
+ uuids?: string[]
+
+ hasFiles?: boolean
+ hasHLSFiles?: boolean
+ hasWebtorrentFiles?: boolean
accountId?: number
videoChannelId?: number
having?: string
}
-export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
+export class VideosIdListQueryBuilder extends AbstractRunQuery {
protected replacements: any = {}
private attributes: string[]
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 }
}
'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) {
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)
}
this.whereTagsAllOf(options.tagsAllOf)
}
+ if (options.uuids) {
+ this.whereUUIDs(options.uuids)
+ }
+
if (options.nsfw === true) {
this.whereNSFW()
} else if (options.nsfw === false) {
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})`
}
}
- 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) {
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
' 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[]) {
)
}
+ 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