Optimize SQL requests of watch page API endpoints
This commit is contained in:
parent
e972e046db
commit
627621c1e8
|
@ -25,7 +25,7 @@ run()
|
||||||
async function run () {
|
async function run () {
|
||||||
await initDatabaseModels(true)
|
await initDatabaseModels(true)
|
||||||
|
|
||||||
const video = await VideoModel.loadByUUID(program['video'])
|
const video = await VideoModel.loadByUUIDWithFile(program['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.')
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ run()
|
||||||
async function run () {
|
async function run () {
|
||||||
await initDatabaseModels(true)
|
await initDatabaseModels(true)
|
||||||
|
|
||||||
const video = await VideoModel.loadByUUID(program['video'])
|
const video = await VideoModel.loadByUUIDWithFile(program['video'])
|
||||||
if (!video) throw new Error('Video not found.')
|
if (!video) throw new Error('Video not found.')
|
||||||
|
|
||||||
const dataInput = {
|
const dataInput = {
|
||||||
|
|
|
@ -56,7 +56,7 @@ async function pruneDirectory (directory: string) {
|
||||||
const uuid = getUUIDFromFilename(file)
|
const uuid = getUUIDFromFilename(file)
|
||||||
let video: VideoModel
|
let video: VideoModel
|
||||||
|
|
||||||
if (uuid) video = await VideoModel.loadByUUID(uuid)
|
if (uuid) video = await VideoModel.loadByUUIDWithFile(uuid)
|
||||||
|
|
||||||
if (!uuid || !video) toDelete.push(join(directory, file))
|
if (!uuid || !video) toDelete.push(join(directory, file))
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,7 +293,7 @@ async function getUserVideoQuotaUsed (req: express.Request, res: express.Respons
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||||
const videoId = +req.params.videoId
|
const videoId = res.locals.video.id
|
||||||
const accountId = +res.locals.oauth.token.User.Account.id
|
const accountId = +res.locals.oauth.token.User.Account.id
|
||||||
|
|
||||||
const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
|
const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
|
||||||
|
|
|
@ -86,7 +86,7 @@ async function listVideoThreadComments (req: express.Request, res: express.Respo
|
||||||
let resultList: ResultList<VideoCommentModel>
|
let resultList: ResultList<VideoCommentModel>
|
||||||
|
|
||||||
if (video.commentsEnabled === true) {
|
if (video.commentsEnabled === true) {
|
||||||
resultList = await VideoCommentModel.listThreadCommentsForApi(res.locals.video.id, res.locals.videoCommentThread.id)
|
resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id)
|
||||||
} else {
|
} else {
|
||||||
resultList = {
|
resultList = {
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
|
@ -152,13 +152,15 @@ function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: Use
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isVideoExist (id: string, res: Response) {
|
async function isVideoExist (id: string, res: Response, fetchType: 'all' | 'only-video' | 'id' | 'none' = 'all') {
|
||||||
let video: VideoModel | null
|
let video: VideoModel | null
|
||||||
|
|
||||||
if (validator.isInt(id)) {
|
if (fetchType === 'all') {
|
||||||
video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
|
video = await VideoModel.loadAndPopulateAccountAndServerAndTags(id)
|
||||||
} else { // UUID
|
} else if (fetchType === 'only-video') {
|
||||||
video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
|
video = await VideoModel.load(id)
|
||||||
|
} else if (fetchType === 'id' || fetchType === 'none') {
|
||||||
|
video = await VideoModel.loadOnlyId(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video === null) {
|
if (video === null) {
|
||||||
|
@ -169,7 +171,7 @@ async function isVideoExist (id: string, res: Response) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
res.locals.video = video
|
if (fetchType !== 'none') res.locals.video = video
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class VideosCaptionCache extends AbstractVideoStaticFileCache <GetPathParam> {
|
||||||
if (videoCaption.isOwned()) throw new Error('Cannot load remote caption of owned video.')
|
if (videoCaption.isOwned()) throw new Error('Cannot load remote caption of owned video.')
|
||||||
|
|
||||||
// Used to fetch the path
|
// Used to fetch the path
|
||||||
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
|
||||||
if (!video) return undefined
|
if (!video) return undefined
|
||||||
|
|
||||||
const remoteStaticPath = videoCaption.getCaptionStaticPath()
|
const remoteStaticPath = videoCaption.getCaptionStaticPath()
|
||||||
|
|
|
@ -16,7 +16,7 @@ class VideosPreviewCache extends AbstractVideoStaticFileCache <string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFilePath (videoUUID: string) {
|
async getFilePath (videoUUID: string) {
|
||||||
const video = await VideoModel.loadByUUID(videoUUID)
|
const video = await VideoModel.loadByUUIDWithFile(videoUUID)
|
||||||
if (!video) return undefined
|
if (!video) return undefined
|
||||||
|
|
||||||
if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName())
|
if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName())
|
||||||
|
@ -25,7 +25,7 @@ class VideosPreviewCache extends AbstractVideoStaticFileCache <string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async loadRemoteFile (key: string) {
|
protected async loadRemoteFile (key: string) {
|
||||||
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(key)
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(key)
|
||||||
if (!video) return undefined
|
if (!video) return undefined
|
||||||
|
|
||||||
if (video.isOwned()) throw new Error('Cannot load remote preview of owned video.')
|
if (video.isOwned()) throw new Error('Cannot load remote preview of owned video.')
|
||||||
|
|
|
@ -39,10 +39,8 @@ export class ClientHtml {
|
||||||
let videoPromise: Bluebird<VideoModel>
|
let videoPromise: Bluebird<VideoModel>
|
||||||
|
|
||||||
// Let Angular application handle errors
|
// Let Angular application handle errors
|
||||||
if (validator.isUUID(videoId, 4)) {
|
if (validator.isInt(videoId) || validator.isUUID(videoId, 4)) {
|
||||||
videoPromise = VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
|
videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
|
||||||
} else if (validator.isInt(videoId)) {
|
|
||||||
videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(+videoId)
|
|
||||||
} else {
|
} else {
|
||||||
return ClientHtml.getIndexHTML(req, res)
|
return ClientHtml.getIndexHTML(req, res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ async function processVideoFileImport (job: Bull.Job) {
|
||||||
const payload = job.data as VideoFileImportPayload
|
const payload = job.data as VideoFileImportPayload
|
||||||
logger.info('Processing video file import in job %d.', job.id)
|
logger.info('Processing video file import in job %d.', job.id)
|
||||||
|
|
||||||
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoUUID)
|
||||||
// No video, maybe deleted?
|
// No video, maybe deleted?
|
||||||
if (!video) {
|
if (!video) {
|
||||||
logger.info('Do not process job %d, video does not exist.', job.id)
|
logger.info('Do not process job %d, video does not exist.', job.id)
|
||||||
|
@ -43,7 +43,7 @@ async function processVideoFile (job: Bull.Job) {
|
||||||
const payload = job.data as VideoFilePayload
|
const payload = job.data as VideoFilePayload
|
||||||
logger.info('Processing video file in job %d.', job.id)
|
logger.info('Processing video file in job %d.', job.id)
|
||||||
|
|
||||||
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoUUID)
|
||||||
// No video, maybe deleted?
|
// No video, maybe deleted?
|
||||||
if (!video) {
|
if (!video) {
|
||||||
logger.info('Do not process job %d, video does not exist.', job.id)
|
logger.info('Do not process job %d, video does not exist.', job.id)
|
||||||
|
@ -69,7 +69,7 @@ async function onVideoFileTranscoderOrImportSuccess (video: VideoModel) {
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
// Maybe the video changed in database, refresh it
|
// Maybe the video changed in database, refresh it
|
||||||
let videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid, t)
|
let videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
|
||||||
// Video does not exist anymore
|
// Video does not exist anymore
|
||||||
if (!videoDatabase) return undefined
|
if (!videoDatabase) return undefined
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boole
|
||||||
|
|
||||||
return sequelizeTypescript.transaction(async t => {
|
return sequelizeTypescript.transaction(async t => {
|
||||||
// Maybe the video changed in database, refresh it
|
// Maybe the video changed in database, refresh it
|
||||||
const videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid, t)
|
const videoDatabase = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
|
||||||
// Video does not exist anymore
|
// Video does not exist anymore
|
||||||
if (!videoDatabase) return undefined
|
if (!videoDatabase) return undefined
|
||||||
|
|
||||||
|
|
|
@ -183,7 +183,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
|
||||||
const videoUpdated = await video.save({ transaction: t })
|
const videoUpdated = await video.save({ transaction: t })
|
||||||
|
|
||||||
// Now we can federate the video (reload from database, we need more attributes)
|
// Now we can federate the video (reload from database, we need more attributes)
|
||||||
const videoForFederation = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid, t)
|
const videoForFederation = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.uuid, t)
|
||||||
await federateVideoIfNeeded(videoForFederation, true, t)
|
await federateVideoIfNeeded(videoForFederation, true, t)
|
||||||
|
|
||||||
// Update video import object
|
// Update video import object
|
||||||
|
|
|
@ -172,7 +172,7 @@ const usersVideoRatingValidator = [
|
||||||
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
|
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await isVideoExist(req.params.videoId, res)) return
|
if (!await isVideoExist(req.params.videoId, res, 'id')) return
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ const listVideoCaptionsValidator = [
|
||||||
logger.debug('Checking listVideoCaptions parameters', { parameters: req.params })
|
logger.debug('Checking listVideoCaptions parameters', { parameters: req.params })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await isVideoExist(req.params.videoId, res)) return
|
if (!await isVideoExist(req.params.videoId, res, 'id')) return
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ const listVideoCommentThreadsValidator = [
|
||||||
logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
|
logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await isVideoExist(req.params.videoId, res)) return
|
if (!await isVideoExist(req.params.videoId, res, 'only-video')) return
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ const listVideoThreadCommentsValidator = [
|
||||||
logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
|
logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
if (areValidationErrors(req, res)) return
|
||||||
if (!await isVideoExist(req.params.videoId, res)) return
|
if (!await isVideoExist(req.params.videoId, res, 'only-video')) return
|
||||||
if (!await isVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return
|
if (!await isVideoCommentThreadExist(req.params.threadId, res.locals.video, res)) return
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
|
|
|
@ -91,6 +91,7 @@ import {
|
||||||
videoModelToFormattedDetailsJSON,
|
videoModelToFormattedDetailsJSON,
|
||||||
videoModelToFormattedJSON
|
videoModelToFormattedJSON
|
||||||
} from './video-format-utils'
|
} from './video-format-utils'
|
||||||
|
import * as validator from 'validator'
|
||||||
|
|
||||||
// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
|
// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
|
||||||
const indexes: Sequelize.DefineIndexesOptions[] = [
|
const indexes: Sequelize.DefineIndexesOptions[] = [
|
||||||
|
@ -466,6 +467,7 @@ type AvailableForListIDsOptions = {
|
||||||
required: false,
|
required: false,
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
|
attributes: [ 'fileUrl' ],
|
||||||
model: () => VideoRedundancyModel.unscoped(),
|
model: () => VideoRedundancyModel.unscoped(),
|
||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
|
@ -1062,8 +1064,26 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return VideoModel.getAvailableForApi(query, queryOptions)
|
return VideoModel.getAvailableForApi(query, queryOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
static load (id: number, t?: Sequelize.Transaction) {
|
static load (id: number | string, t?: Sequelize.Transaction) {
|
||||||
return VideoModel.findById(id, { transaction: t })
|
const where = VideoModel.buildWhereIdOrUUID(id)
|
||||||
|
const options = {
|
||||||
|
where,
|
||||||
|
transaction: t
|
||||||
|
}
|
||||||
|
|
||||||
|
return VideoModel.findOne(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadOnlyId (id: number | string, t?: Sequelize.Transaction) {
|
||||||
|
const where = VideoModel.buildWhereIdOrUUID(id)
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
attributes: [ 'id' ],
|
||||||
|
where,
|
||||||
|
transaction: t
|
||||||
|
}
|
||||||
|
|
||||||
|
return VideoModel.findOne(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
static loadWithFile (id: number, t?: Sequelize.Transaction, logging?: boolean) {
|
static loadWithFile (id: number, t?: Sequelize.Transaction, logging?: boolean) {
|
||||||
|
@ -1071,6 +1091,18 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
.findById(id, { transaction: t, logging })
|
.findById(id, { transaction: t, logging })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static loadByUUIDWithFile (uuid: string) {
|
||||||
|
const options = {
|
||||||
|
where: {
|
||||||
|
uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VideoModel
|
||||||
|
.scope([ ScopeNames.WITH_FILES ])
|
||||||
|
.findOne(options)
|
||||||
|
}
|
||||||
|
|
||||||
static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
|
static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
|
||||||
const query: IFindOptions<VideoModel> = {
|
const query: IFindOptions<VideoModel> = {
|
||||||
where: {
|
where: {
|
||||||
|
@ -1083,40 +1115,12 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return VideoModel.scope([ ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_FILES ]).findOne(query)
|
return VideoModel.scope([ ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_FILES ]).findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
static loadAndPopulateAccountAndServerAndTags (id: number) {
|
static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction) {
|
||||||
const options = {
|
const where = VideoModel.buildWhereIdOrUUID(id)
|
||||||
order: [ [ 'Tags', 'name', 'ASC' ] ]
|
|
||||||
}
|
|
||||||
|
|
||||||
return VideoModel
|
|
||||||
.scope([
|
|
||||||
ScopeNames.WITH_TAGS,
|
|
||||||
ScopeNames.WITH_BLACKLISTED,
|
|
||||||
ScopeNames.WITH_FILES,
|
|
||||||
ScopeNames.WITH_ACCOUNT_DETAILS,
|
|
||||||
ScopeNames.WITH_SCHEDULED_UPDATE
|
|
||||||
])
|
|
||||||
.findById(id, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
static loadByUUID (uuid: string) {
|
|
||||||
const options = {
|
|
||||||
where: {
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return VideoModel
|
|
||||||
.scope([ ScopeNames.WITH_FILES ])
|
|
||||||
.findOne(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string, t?: Sequelize.Transaction) {
|
|
||||||
const options = {
|
const options = {
|
||||||
order: [ [ 'Tags', 'name', 'ASC' ] ],
|
order: [ [ 'Tags', 'name', 'ASC' ] ],
|
||||||
where: {
|
where,
|
||||||
uuid
|
|
||||||
},
|
|
||||||
transaction: t
|
transaction: t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1277,6 +1281,10 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
return VIDEO_STATES[ id ] || 'Unknown'
|
return VIDEO_STATES[ id ] || 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static buildWhereIdOrUUID (id: number | string) {
|
||||||
|
return validator.isInt('' + id) ? { id } : { uuid: id }
|
||||||
|
}
|
||||||
|
|
||||||
getOriginalFile () {
|
getOriginalFile () {
|
||||||
if (Array.isArray(this.VideoFiles) === false) return undefined
|
if (Array.isArray(this.VideoFiles) === false) return undefined
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue