1
0
Fork 0

Use raw SQL for most of video queries

This commit is contained in:
Chocobozzz 2021-06-11 14:09:33 +02:00
parent 3c79c2ce86
commit 71d4af1efc
No known key found for this signature in database
GPG key ID: 583A612D890159BE
16 changed files with 208 additions and 198 deletions

View file

@ -36,7 +36,7 @@ async function run () {
return return
} }
const video = await VideoModel.loadByUUID(options.video) const video = await VideoModel.load(options.video)
if (!video) throw new Error('Video not found.') if (!video) throw new Error('Video not found.')
if (video.isOwned() === false) throw new Error('Cannot import files of a non owned video.') if (video.isOwned() === false) throw new Error('Cannot import files of a non owned video.')
@ -45,7 +45,7 @@ async function run () {
filePath: resolve(options.import) filePath: resolve(options.import)
} }
await JobQueue.Instance.init() JobQueue.Instance.init()
await JobQueue.Instance.createJobWithPromise({ type: 'video-file-import', payload: dataInput }) await JobQueue.Instance.createJobWithPromise({ type: 'video-file-import', payload: dataInput })
console.log('Import job for video %s created.', video.uuid) console.log('Import job for video %s created.', video.uuid)
} }

View file

@ -89,7 +89,7 @@ async function pruneDirectory (directory: string, existFun: ExistFun) {
function doesVideoExist (keepOnlyOwned: boolean) { function doesVideoExist (keepOnlyOwned: boolean) {
return async (file: string) => { return async (file: string) => {
const uuid = getUUIDFromFilename(file) const uuid = getUUIDFromFilename(file)
const video = await VideoModel.loadByUUID(uuid) const video = await VideoModel.load(uuid)
return video && (keepOnlyOwned === false || video.isOwned()) return video && (keepOnlyOwned === false || video.isOwned())
} }

View file

@ -78,12 +78,12 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId',
activityPubClientRouter.get('/videos/watch/:id', activityPubClientRouter.get('/videos/watch/:id',
executeIfActivityPub, executeIfActivityPub,
asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS)), asyncMiddleware(cacheRoute()(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS)),
asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), asyncMiddleware(videosCustomGetValidator('all')),
asyncMiddleware(videoController) asyncMiddleware(videoController)
) )
activityPubClientRouter.get('/videos/watch/:id/activity', activityPubClientRouter.get('/videos/watch/:id/activity',
executeIfActivityPub, executeIfActivityPub,
asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), asyncMiddleware(videosCustomGetValidator('all')),
asyncMiddleware(videoController) asyncMiddleware(videoController)
) )
activityPubClientRouter.get('/videos/watch/:id/announces', activityPubClientRouter.get('/videos/watch/:id/announces',
@ -222,8 +222,7 @@ function getAccountVideoRateFactory (rateType: VideoRateType) {
} }
async function videoController (req: express.Request, res: express.Response) { async function videoController (req: express.Request, res: express.Response) {
// We need more attributes const video = res.locals.videoAll
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(res.locals.onlyVideoWithRights.id)
if (redirectIfNotOwned(video.url, res)) return if (redirectIfNotOwned(video.url, res)) return

View file

@ -4,7 +4,7 @@ import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/ty
import { VideoPrivacy, VideoState } from '@shared/models' import { VideoPrivacy, VideoState } from '@shared/models'
function getVideoWithAttributes (res: Response) { function getVideoWithAttributes (res: Response) {
return res.locals.videoAPI || res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights return res.locals.videoAPI || res.locals.videoAll || res.locals.onlyVideo
} }
function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) { function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {

View file

@ -3,31 +3,30 @@ import {
MVideoAccountLightBlacklistAllFiles, MVideoAccountLightBlacklistAllFiles,
MVideoFormattableDetails, MVideoFormattableDetails,
MVideoFullLight, MVideoFullLight,
MVideoIdThumbnail, MVideoId,
MVideoImmutable, MVideoImmutable,
MVideoThumbnail, MVideoThumbnail,
MVideoWithRights MVideoWithRights
} from '@server/types/models' } from '@server/types/models'
import { Hooks } from '../plugins/hooks' import { Hooks } from '../plugins/hooks'
type VideoLoadType = 'for-api' | 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' | 'only-immutable-attributes' type VideoLoadType = 'for-api' | 'all' | 'only-video' | 'id' | 'none' | 'only-immutable-attributes'
function loadVideo (id: number | string, fetchType: 'for-api', userId?: number): Promise<MVideoFormattableDetails> function loadVideo (id: number | string, fetchType: 'for-api', userId?: number): Promise<MVideoFormattableDetails>
function loadVideo (id: number | string, fetchType: 'all', userId?: number): Promise<MVideoFullLight> function loadVideo (id: number | string, fetchType: 'all', userId?: number): Promise<MVideoFullLight>
function loadVideo (id: number | string, fetchType: 'only-immutable-attributes'): Promise<MVideoImmutable> function loadVideo (id: number | string, fetchType: 'only-immutable-attributes'): Promise<MVideoImmutable>
function loadVideo (id: number | string, fetchType: 'only-video', userId?: number): Promise<MVideoThumbnail> function loadVideo (id: number | string, fetchType: 'only-video', userId?: number): Promise<MVideoThumbnail>
function loadVideo (id: number | string, fetchType: 'only-video-with-rights', userId?: number): Promise<MVideoWithRights> function loadVideo (id: number | string, fetchType: 'id' | 'none', userId?: number): Promise<MVideoId>
function loadVideo (id: number | string, fetchType: 'id' | 'none', userId?: number): Promise<MVideoIdThumbnail>
function loadVideo ( function loadVideo (
id: number | string, id: number | string,
fetchType: VideoLoadType, fetchType: VideoLoadType,
userId?: number userId?: number
): Promise<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail | MVideoImmutable> ): Promise<MVideoFullLight | MVideoThumbnail | MVideoId | MVideoImmutable>
function loadVideo ( function loadVideo (
id: number | string, id: number | string,
fetchType: VideoLoadType, fetchType: VideoLoadType,
userId?: number userId?: number
): Promise<MVideoFullLight | MVideoThumbnail | MVideoWithRights | MVideoIdThumbnail | MVideoImmutable> { ): Promise<MVideoFullLight | MVideoThumbnail | MVideoId | MVideoImmutable> {
if (fetchType === 'for-api') { if (fetchType === 'for-api') {
return Hooks.wrapPromiseFun( return Hooks.wrapPromiseFun(
@ -41,8 +40,6 @@ function loadVideo (
if (fetchType === 'only-immutable-attributes') return VideoModel.loadImmutableAttributes(id) if (fetchType === 'only-immutable-attributes') return VideoModel.loadImmutableAttributes(id)
if (fetchType === 'only-video-with-rights') return VideoModel.loadWithRights(id)
if (fetchType === 'only-video') return VideoModel.load(id) if (fetchType === 'only-video') return VideoModel.load(id)
if (fetchType === 'id' || fetchType === 'none') return VideoModel.loadOnlyId(id) if (fetchType === 'id' || fetchType === 'none') return VideoModel.loadOnlyId(id)

View file

@ -8,7 +8,7 @@ import {
MVideoAccountLight, MVideoAccountLight,
MVideoFormattableDetails, MVideoFormattableDetails,
MVideoFullLight, MVideoFullLight,
MVideoIdThumbnail, MVideoId,
MVideoImmutable, MVideoImmutable,
MVideoThumbnail, MVideoThumbnail,
MVideoWithRights MVideoWithRights
@ -43,16 +43,12 @@ async function doesVideoExist (id: number | string, res: Response, fetchType: Vi
break break
case 'id': case 'id':
res.locals.videoId = video as MVideoIdThumbnail res.locals.videoId = video as MVideoId
break break
case 'only-video': case 'only-video':
res.locals.onlyVideo = video as MVideoThumbnail res.locals.onlyVideo = video as MVideoThumbnail
break break
case 'only-video-with-rights':
res.locals.onlyVideoWithRights = video as MVideoWithRights
break
} }
return true return true

View file

@ -4,7 +4,7 @@ import { getResumableUploadPath } from '@server/helpers/upload'
import { isAbleToUploadVideo } from '@server/lib/user' import { isAbleToUploadVideo } from '@server/lib/user'
import { getServerActor } from '@server/models/application/application' import { getServerActor } from '@server/models/application/application'
import { ExpressPromiseHandler } from '@server/types/express' import { ExpressPromiseHandler } from '@server/types/express'
import { MUserAccountId, MVideoWithRights } from '@server/types/models' import { MUserAccountId, MVideoFullLight } from '@server/types/models'
import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/change-ownership/video-change-ownership-accept.model' import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/change-ownership/video-change-ownership-accept.model'
@ -258,7 +258,7 @@ async function checkVideoFollowConstraints (req: express.Request, res: express.R
} }
const videosCustomGetValidator = ( const videosCustomGetValidator = (
fetchType: 'for-api' | 'all' | 'only-video' | 'only-video-with-rights' | 'only-immutable-attributes', fetchType: 'for-api' | 'all' | 'only-video' | 'only-immutable-attributes',
authenticateInQuery = false authenticateInQuery = false
) => { ) => {
return [ return [
@ -273,7 +273,7 @@ const videosCustomGetValidator = (
// Controllers does not need to check video rights // Controllers does not need to check video rights
if (fetchType === 'only-immutable-attributes') return next() if (fetchType === 'only-immutable-attributes') return next()
const video = getVideoWithAttributes(res) as MVideoWithRights const video = getVideoWithAttributes(res) as MVideoFullLight
// Video private or blacklisted // Video private or blacklisted
if (video.requiresAuth()) { if (video.requiresAuth()) {

View file

@ -80,6 +80,18 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
} }
} }
protected includeOwnerUser () {
this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
this.attributes = {
...this.attributes,
...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
...this.buildAttributesObject('VideoChannel->Account', this.tables.getUserAccountAttributes())
}
}
protected includeThumbnails () { protected includeThumbnails () {
this.addJoin('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"') this.addJoin('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
@ -269,14 +281,20 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
return result return result
} }
protected whereId (id: string | number) { protected whereId (options: { id?: string | number, url?: string }) {
if (validator.isInt('' + id)) { if (options.url) {
this.where = 'WHERE "video"."url" = :videoUrl'
this.replacements.videoUrl = options.url
return
}
if (validator.isInt('' + options.id)) {
this.where = 'WHERE "video".id = :videoId' this.where = 'WHERE "video".id = :videoId'
} else { } else {
this.where = 'WHERE uuid = :videoId' this.where = 'WHERE uuid = :videoId'
} }
this.replacements.videoId = id this.replacements.videoId = options.id
} }
protected addJoin (join: string) { protected addJoin (join: string) {

View file

@ -13,16 +13,17 @@ export class AbstractVideosQueryBuilder {
protected query: string protected query: string
protected replacements: any = {} protected replacements: any = {}
protected runQuery (transaction?: Transaction) { protected runQuery (options: { transaction?: Transaction, logging?: boolean } = {}) {
logger.debug('Running videos query.', { query: this.query, replacements: this.replacements }) logger.debug('Running videos query.', { query: this.query, replacements: this.replacements })
const options = { const queryOptions = {
transaction, transaction: options.transaction,
logging: options.logging,
replacements: this.replacements, replacements: this.replacements,
type: QueryTypes.SELECT as QueryTypes.SELECT, type: QueryTypes.SELECT as QueryTypes.SELECT,
next: false next: false
} }
return this.sequelize.query<any>(this.query, options) return this.sequelize.query<any>(this.query, queryOptions)
} }
} }

View file

@ -18,13 +18,13 @@ export class VideoFileQueryBuilder extends AbstractVideosModelQueryBuilder {
queryWebTorrentVideos (options: BuildVideoGetQueryOptions) { queryWebTorrentVideos (options: BuildVideoGetQueryOptions) {
this.buildWebtorrentFilesQuery(options) this.buildWebtorrentFilesQuery(options)
return this.runQuery(options.transaction) return this.runQuery(options)
} }
queryStreamingPlaylistVideos (options: BuildVideoGetQueryOptions) { queryStreamingPlaylistVideos (options: BuildVideoGetQueryOptions) {
this.buildVideoStreamingPlaylistFilesQuery(options) this.buildVideoStreamingPlaylistFilesQuery(options)
return this.runQuery(options.transaction) return this.runQuery(options)
} }
private buildWebtorrentFilesQuery (options: BuildVideoGetQueryOptions) { private buildWebtorrentFilesQuery (options: BuildVideoGetQueryOptions) {
@ -34,11 +34,11 @@ export class VideoFileQueryBuilder extends AbstractVideosModelQueryBuilder {
this.includeWebtorrentFiles(true) this.includeWebtorrentFiles(true)
if (options.forGetAPI === true) { if (this.shouldIncludeRedundancies(options)) {
this.includeWebTorrentRedundancies() this.includeWebTorrentRedundancies()
} }
this.whereId(options.id) this.whereId(options)
this.query = this.buildQuery() this.query = this.buildQuery()
} }
@ -50,11 +50,11 @@ export class VideoFileQueryBuilder extends AbstractVideosModelQueryBuilder {
this.includeStreamingPlaylistFiles(true) this.includeStreamingPlaylistFiles(true)
if (options.forGetAPI === true) { if (this.shouldIncludeRedundancies(options)) {
this.includeStreamingPlaylistRedundancies() this.includeStreamingPlaylistRedundancies()
} }
this.whereId(options.id) this.whereId(options)
this.query = this.buildQuery() this.query = this.buildQuery()
} }
@ -62,4 +62,8 @@ export class VideoFileQueryBuilder extends AbstractVideosModelQueryBuilder {
private buildQuery () { private buildQuery () {
return `${this.buildSelect()} FROM "video" ${this.joins} ${this.where}` return `${this.buildSelect()} FROM "video" ${this.joins} ${this.where}`
} }
private shouldIncludeRedundancies (options: BuildVideoGetQueryOptions) {
return options.type === 'api'
}
} }

View file

@ -1,5 +1,4 @@
import { logger } from '@server/helpers/logger'
import { AccountModel } from '@server/models/account/account' import { AccountModel } from '@server/models/account/account'
import { ActorModel } from '@server/models/actor/actor' import { ActorModel } from '@server/models/actor/actor'
import { ActorImageModel } from '@server/models/actor/actor-image' import { ActorImageModel } from '@server/models/actor/actor-image'
@ -56,7 +55,7 @@ export class VideoModelBuilder {
this.reinit() this.reinit()
for (const row of rows) { for (const row of rows) {
this.buildVideo(row) this.buildVideoAndAccount(row)
const videoModel = this.videosMemo[row.id] const videoModel = this.videosMemo[row.id]
@ -131,22 +130,10 @@ export class VideoModelBuilder {
} }
} }
private buildVideo (row: SQLRow) { private buildVideoAndAccount (row: SQLRow) {
if (this.videosMemo[row.id]) return if (this.videosMemo[row.id]) return
// Build Channel
const channelModel = new VideoChannelModel(this.grab(row, this.tables.getChannelAttributes(), 'VideoChannel'), this.buildOpts)
channelModel.Actor = this.buildActor(row, 'VideoChannel')
const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
channelModel.Account = accountModel
const videoModel = new VideoModel(this.grab(row, this.tables.getVideoAttributes(), ''), this.buildOpts) const videoModel = new VideoModel(this.grab(row, this.tables.getVideoAttributes(), ''), this.buildOpts)
videoModel.VideoChannel = channelModel
this.videosMemo[row.id] = videoModel
videoModel.UserVideoHistories = [] videoModel.UserVideoHistories = []
videoModel.Thumbnails = [] videoModel.Thumbnails = []
@ -155,10 +142,29 @@ export class VideoModelBuilder {
videoModel.Tags = [] videoModel.Tags = []
videoModel.Trackers = [] videoModel.Trackers = []
this.buildAccount(row, videoModel)
this.videosMemo[row.id] = videoModel
// Keep rows order // Keep rows order
this.videos.push(videoModel) this.videos.push(videoModel)
} }
private buildAccount (row: SQLRow, videoModel: VideoModel) {
const id = row['VideoChannel.Account.id']
if (!id) return
const channelModel = new VideoChannelModel(this.grab(row, this.tables.getChannelAttributes(), 'VideoChannel'), this.buildOpts)
channelModel.Actor = this.buildActor(row, 'VideoChannel')
const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
channelModel.Account = accountModel
videoModel.VideoChannel = channelModel
}
private buildActor (row: SQLRow, prefix: string) { private buildActor (row: SQLRow, prefix: string) {
const actorPrefix = `${prefix}.Actor` const actorPrefix = `${prefix}.Actor`
const avatarPrefix = `${actorPrefix}.Avatar` const avatarPrefix = `${actorPrefix}.Avatar`

View file

@ -10,6 +10,10 @@ export class VideoTables {
} }
getChannelAttributesForUser () {
return [ 'id', 'accountId' ]
}
getChannelAttributes () { getChannelAttributes () {
let attributeKeys = [ let attributeKeys = [
'id', 'id',
@ -29,6 +33,10 @@ export class VideoTables {
return attributeKeys return attributeKeys
} }
getUserAccountAttributes () {
return [ 'id', 'userId' ]
}
getAccountAttributes () { getAccountAttributes () {
let attributeKeys = [ 'id', 'name', 'actorId' ] let attributeKeys = [ 'id', 'name', 'actorId' ]

View file

@ -11,10 +11,15 @@ import { VideoTables } from './shared/video-tables'
*/ */
export type BuildVideoGetQueryOptions = { export type BuildVideoGetQueryOptions = {
id: number | string id?: number | string
transaction?: Transaction url?: string
type: 'api' | 'full-light' | 'account-blacklist-files' | 'all-files' | 'thumbnails' | 'thumbnails-blacklist' | 'id' | 'blacklist-rights'
userId?: number userId?: number
forGetAPI?: boolean transaction?: Transaction
logging?: boolean
} }
export class VideosModelGetQueryBuilder { export class VideosModelGetQueryBuilder {
@ -32,11 +37,17 @@ export class VideosModelGetQueryBuilder {
this.videoModelBuilder = new VideoModelBuilder('get', new VideoTables('get')) this.videoModelBuilder = new VideoModelBuilder('get', new VideoTables('get'))
} }
async queryVideos (options: BuildVideoGetQueryOptions) { async queryVideo (options: BuildVideoGetQueryOptions) {
const [ videoRows, webtorrentFilesRows, streamingPlaylistFilesRows ] = await Promise.all([ const [ videoRows, webtorrentFilesRows, streamingPlaylistFilesRows ] = await Promise.all([
this.videoQueryBuilder.queryVideos(options), this.videoQueryBuilder.queryVideos(options),
this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(options),
this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(options) this.shouldQueryVideoFiles(options)
? this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(options)
: Promise.resolve(undefined),
this.shouldQueryVideoFiles(options)
? this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(options)
: Promise.resolve(undefined)
]) ])
const videos = this.videoModelBuilder.buildVideosFromRows(videoRows, webtorrentFilesRows, streamingPlaylistFilesRows) const videos = this.videoModelBuilder.buildVideosFromRows(videoRows, webtorrentFilesRows, streamingPlaylistFilesRows)
@ -48,6 +59,10 @@ export class VideosModelGetQueryBuilder {
if (videos.length === 0) return null if (videos.length === 0) return null
return videos[0] return videos[0]
} }
private shouldQueryVideoFiles (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light', 'account-blacklist-files', 'all-files' ].includes(options.type)
}
} }
export class VideosModelGetQuerySubBuilder extends AbstractVideosModelQueryBuilder { export class VideosModelGetQuerySubBuilder extends AbstractVideosModelQueryBuilder {
@ -63,7 +78,7 @@ export class VideosModelGetQuerySubBuilder extends AbstractVideosModelQueryBuild
queryVideos (options: BuildVideoGetQueryOptions) { queryVideos (options: BuildVideoGetQueryOptions) {
this.buildMainGetQuery(options) this.buildMainGetQuery(options)
return this.runQuery(options.transaction) return this.runQuery(options)
} }
private buildMainGetQuery (options: BuildVideoGetQueryOptions) { private buildMainGetQuery (options: BuildVideoGetQueryOptions) {
@ -71,36 +86,91 @@ export class VideosModelGetQuerySubBuilder extends AbstractVideosModelQueryBuild
'"video".*': '' '"video".*': ''
} }
if (this.shouldIncludeThumbnails(options)) {
this.includeThumbnails()
}
if (this.shouldIncludeBlacklisted(options)) {
this.includeBlacklisted()
}
if (this.shouldIncludeAccount(options)) {
this.includeChannels() this.includeChannels()
this.includeAccounts() this.includeAccounts()
}
if (this.shouldIncludeTags(options)) {
this.includeTags() this.includeTags()
}
this.includeThumbnails() if (this.shouldIncludeScheduleUpdate(options)) {
this.includeBlacklisted()
this.includeScheduleUpdate() this.includeScheduleUpdate()
}
if (this.shouldIncludeLive(options)) {
this.includeLive() this.includeLive()
}
if (options.userId) { if (options.userId && this.shouldIncludeUserHistory(options)) {
this.includeUserHistory(options.userId) this.includeUserHistory(options.userId)
} }
if (options.forGetAPI === true) { if (this.shouldIncludeOwnerUser(options)) {
this.includeOwnerUser()
}
if (this.shouldIncludeTrackers(options)) {
this.includeTrackers() this.includeTrackers()
} }
this.whereId(options.id) this.whereId(options)
this.query = this.buildQuery() this.query = this.buildQuery(options)
} }
private buildQuery () { private buildQuery (options: BuildVideoGetQueryOptions) {
const order = 'ORDER BY "Tags"."name" ASC' const order = this.shouldIncludeTags(options)
? 'ORDER BY "Tags"."name" ASC'
: ''
const from = `SELECT * FROM "video" ${this.where} LIMIT 1` const from = `SELECT * FROM "video" ${this.where} LIMIT 1`
return `${this.buildSelect()} FROM (${from}) AS "video" ${this.joins} ${order}` return `${this.buildSelect()} FROM (${from}) AS "video" ${this.joins} ${order}`
} }
private shouldIncludeTrackers (options: BuildVideoGetQueryOptions) {
return options.type === 'api'
}
private shouldIncludeLive (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light' ].includes(options.type)
}
private shouldIncludeScheduleUpdate (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light' ].includes(options.type)
}
private shouldIncludeTags (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light' ].includes(options.type)
}
private shouldIncludeUserHistory (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light' ].includes(options.type)
}
private shouldIncludeAccount (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light', 'account-blacklist-files' ].includes(options.type)
}
private shouldIncludeBlacklisted (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light', 'account-blacklist-files', 'thumbnails-blacklist', 'blacklist-rights' ].includes(options.type)
}
private shouldIncludeOwnerUser (options: BuildVideoGetQueryOptions) {
return options.type === 'blacklist-rights'
}
private shouldIncludeThumbnails (options: BuildVideoGetQueryOptions) {
return [ 'api', 'full-light', 'account-blacklist-files', 'thumbnails', 'thumbnails-blacklist' ].includes(options.type)
}
} }

View file

@ -27,7 +27,8 @@ export class VideosModelListQueryBuilder extends AbstractVideosModelQueryBuilder
this.buildInnerQuery(options) this.buildInnerQuery(options)
this.buildListQueryFromIdsQuery(options) this.buildListQueryFromIdsQuery(options)
return this.runQuery(undefined).then(rows => this.videoModelBuilder.buildVideosFromRows(rows)) return this.runQuery()
.then(rows => this.videoModelBuilder.buildVideosFromRows(rows))
} }
private buildInnerQuery (options: BuildVideosListQueryOptions) { private buildInnerQuery (options: BuildVideosListQueryOptions) {

View file

@ -88,13 +88,12 @@ import {
MVideoFormattableDetails, MVideoFormattableDetails,
MVideoForUser, MVideoForUser,
MVideoFullLight, MVideoFullLight,
MVideoIdThumbnail, MVideoId,
MVideoImmutable, MVideoImmutable,
MVideoThumbnail, MVideoThumbnail,
MVideoThumbnailBlacklist, MVideoThumbnailBlacklist,
MVideoWithAllFiles, MVideoWithAllFiles,
MVideoWithFile, MVideoWithFile
MVideoWithRights
} from '../../types/models' } from '../../types/models'
import { MThumbnail } from '../../types/models/video/thumbnail' import { MThumbnail } from '../../types/models/video/thumbnail'
import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../types/models/video/video-file' import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../types/models/video/video-file'
@ -1301,27 +1300,16 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
return VideoModel.count(options) return VideoModel.count(options)
} }
static load (id: number | string, t?: Transaction): Promise<MVideoThumbnail> { static load (id: number | string, transaction?: Transaction): Promise<MVideoThumbnail> {
const where = buildWhereIdOrUUID(id) const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
const options = {
where, return queryBuilder.queryVideo({ id, transaction, type: 'thumbnails' })
transaction: t
} }
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) static loadWithBlacklist (id: number | string, transaction?: Transaction): Promise<MVideoThumbnailBlacklist> {
} const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
static loadWithBlacklist (id: number | string, t?: Transaction): Promise<MVideoThumbnailBlacklist> { return queryBuilder.queryVideo({ id, transaction, type: 'thumbnails-blacklist' })
const where = buildWhereIdOrUUID(id)
const options = {
where,
transaction: t
}
return VideoModel.scope([
ScopeNames.WITH_THUMBNAILS,
ScopeNames.WITH_BLACKLISTED
]).findOne(options)
} }
static loadImmutableAttributes (id: number | string, t?: Transaction): Promise<MVideoImmutable> { static loadImmutableAttributes (id: number | string, t?: Transaction): Promise<MVideoImmutable> {
@ -1342,68 +1330,6 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
}) })
} }
static loadWithRights (id: number | string, t?: Transaction): Promise<MVideoWithRights> {
const where = buildWhereIdOrUUID(id)
const options = {
where,
transaction: t
}
return VideoModel.scope([
ScopeNames.WITH_BLACKLISTED,
ScopeNames.WITH_USER_ID
]).findOne(options)
}
static loadOnlyId (id: number | string, t?: Transaction): Promise<MVideoIdThumbnail> {
const where = buildWhereIdOrUUID(id)
const options = {
attributes: [ 'id' ],
where,
transaction: t
}
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
static loadWithFiles (id: number | string, t?: Transaction, logging?: boolean): Promise<MVideoWithAllFiles> {
const where = buildWhereIdOrUUID(id)
const query = {
where,
transaction: t,
logging
}
return VideoModel.scope([
ScopeNames.WITH_WEBTORRENT_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
ScopeNames.WITH_THUMBNAILS
]).findOne(query)
}
static loadByUUID (uuid: string): Promise<MVideoThumbnail> {
const options = {
where: {
uuid
}
}
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options)
}
static loadByUrl (url: string, transaction?: Transaction): Promise<MVideoThumbnail> {
const query: FindOptions = {
where: {
url
},
transaction
}
return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query)
}
static loadByUrlImmutableAttributes (url: string, transaction?: Transaction): Promise<MVideoImmutable> { static loadByUrlImmutableAttributes (url: string, transaction?: Transaction): Promise<MVideoImmutable> {
const fun = () => { const fun = () => {
const query: FindOptions = { const query: FindOptions = {
@ -1424,50 +1350,34 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
}) })
} }
static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Promise<MVideoAccountLightBlacklistAllFiles> { static loadOnlyId (id: number | string, transaction?: Transaction): Promise<MVideoId> {
const query: FindOptions = { const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
where: {
url return queryBuilder.queryVideo({ id, transaction, type: 'id' })
},
transaction
} }
return VideoModel.scope([ static loadWithFiles (id: number | string, transaction?: Transaction, logging?: boolean): Promise<MVideoWithAllFiles> {
ScopeNames.WITH_ACCOUNT_DETAILS, const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
ScopeNames.WITH_WEBTORRENT_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS, return queryBuilder.queryVideo({ id, transaction, type: 'all-files', logging })
ScopeNames.WITH_THUMBNAILS, }
ScopeNames.WITH_BLACKLISTED
]).findOne(query) static loadByUrl (url: string, transaction?: Transaction): Promise<MVideoThumbnail> {
const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
return queryBuilder.queryVideo({ url, transaction, type: 'thumbnails' })
}
static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction): Promise<MVideoAccountLightBlacklistAllFiles> {
const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
return queryBuilder.queryVideo({ url, transaction, type: 'account-blacklist-files' })
} }
static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Promise<MVideoFullLight> { static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number): Promise<MVideoFullLight> {
const where = buildWhereIdOrUUID(id) const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
const options = { return queryBuilder.queryVideo({ id, transaction: t, type: 'full-light', userId })
order: [ [ 'Tags', 'name', 'ASC' ] ] as any,
where,
transaction: t
}
const scopes: (string | ScopeOptions)[] = [
ScopeNames.WITH_TAGS,
ScopeNames.WITH_BLACKLISTED,
ScopeNames.WITH_ACCOUNT_DETAILS,
ScopeNames.WITH_SCHEDULED_UPDATE,
ScopeNames.WITH_WEBTORRENT_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
ScopeNames.WITH_THUMBNAILS,
ScopeNames.WITH_LIVE
]
if (userId) {
scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] })
}
return VideoModel
.scope(scopes)
.findOne(options)
} }
static loadForGetAPI (parameters: { static loadForGetAPI (parameters: {
@ -1478,7 +1388,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
const { id, transaction, userId } = parameters const { id, transaction, userId } = parameters
const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize) const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
return queryBuilder.queryVideos({ id, transaction, forGetAPI: true, userId }) return queryBuilder.queryVideo({ id, transaction, type: 'api', userId })
} }
static async getStats () { static async getStats () {

View file

@ -11,6 +11,7 @@ import {
MVideoChangeOwnershipFull, MVideoChangeOwnershipFull,
MVideoFile, MVideoFile,
MVideoFormattableDetails, MVideoFormattableDetails,
MVideoId,
MVideoImmutable, MVideoImmutable,
MVideoLive, MVideoLive,
MVideoPlaylistFull, MVideoPlaylistFull,
@ -106,8 +107,7 @@ declare module 'express' {
videoAll?: MVideoFullLight videoAll?: MVideoFullLight
onlyImmutableVideo?: MVideoImmutable onlyImmutableVideo?: MVideoImmutable
onlyVideo?: MVideoThumbnail onlyVideo?: MVideoThumbnail
onlyVideoWithRights?: MVideoWithRights videoId?: MVideoId
videoId?: MVideoIdThumbnail
videoLive?: MVideoLive videoLive?: MVideoLive