Optimize SQL requests of watch page API endpoints

parent e972e046
......@@ -25,7 +25,7 @@ run()
async function run () {
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.isOwned() === false) throw new Error('Cannot import files of a non owned video.')
......
......@@ -28,7 +28,7 @@ run()
async function run () {
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.')
const dataInput = {
......
......@@ -56,7 +56,7 @@ async function pruneDirectory (directory: string) {
const uuid = getUUIDFromFilename(file)
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))
}
......
......@@ -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) {
const videoId = +req.params.videoId
const videoId = res.locals.video.id
const accountId = +res.locals.oauth.token.User.Account.id
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>
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 {
resultList = {
total: 0,
......
......@@ -152,13 +152,15 @@ function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: Use
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
if (validator.isInt(id)) {
video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
} else { // UUID
video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
if (fetchType === 'all') {
video = await VideoModel.loadAndPopulateAccountAndServerAndTags(id)
} else if (fetchType === 'only-video') {
video = await VideoModel.load(id)
} else if (fetchType === 'id' || fetchType === 'none') {
video = await VideoModel.loadOnlyId(id)
}
if (video === null) {
......@@ -169,7 +171,7 @@ async function isVideoExist (id: string, res: Response) {
return false
}
res.locals.video = video
if (fetchType !== 'none') res.locals.video = video
return true
}
......
......@@ -38,7 +38,7 @@ class VideosCaptionCache extends AbstractVideoStaticFileCache <GetPathParam> {
if (videoCaption.isOwned()) throw new Error('Cannot load remote caption of owned video.')
// Used to fetch the path
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
if (!video) return undefined
const remoteStaticPath = videoCaption.getCaptionStaticPath()
......
......@@ -16,7 +16,7 @@ class VideosPreviewCache extends AbstractVideoStaticFileCache <string> {
}
async getFilePath (videoUUID: string) {
const video = await VideoModel.loadByUUID(videoUUID)
const video = await VideoModel.loadByUUIDWithFile(videoUUID)
if (!video) return undefined
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) {
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(key)
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(key)
if (!video) return undefined
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 Angular application handle errors
if (validator.isUUID(videoId, 4)) {
videoPromise = VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
} else if (validator.isInt(videoId)) {
videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(+videoId)
if (validator.isInt(videoId) || validator.isUUID(videoId, 4)) {
videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
} else {
return ClientHtml.getIndexHTML(req, res)
}
......
......@@ -26,7 +26,7 @@ async function processVideoFileImport (job: Bull.Job) {
const payload = job.data as VideoFileImportPayload
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?
if (!video) {
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
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?
if (!video) {
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 => {
// 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
if (!videoDatabase) return undefined
......@@ -99,7 +99,7 @@ async function onVideoFileOptimizerSuccess (video: VideoModel, isNewVideo: boole
return sequelizeTypescript.transaction(async t => {
// 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
if (!videoDatabase) return undefined
......
......@@ -183,7 +183,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: Vide
const videoUpdated = await video.save({ transaction: t })
// 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)
// Update video import object
......
......@@ -172,7 +172,7 @@ const usersVideoRatingValidator = [
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return
if (!await isVideoExist(req.params.videoId, res)) return
if (!await isVideoExist(req.params.videoId, res, 'id')) return
return next()
}
......
......@@ -58,7 +58,7 @@ const listVideoCaptionsValidator = [
logger.debug('Checking listVideoCaptions parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return
if (!await isVideoExist(req.params.videoId, res)) return
if (!await isVideoExist(req.params.videoId, res, 'id')) return
return next()
}
......
......@@ -17,7 +17,7 @@ const listVideoCommentThreadsValidator = [
logger.debug('Checking listVideoCommentThreads parameters.', { parameters: req.params })
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()
}
......@@ -31,7 +31,7 @@ const listVideoThreadCommentsValidator = [
logger.debug('Checking listVideoThreadComments parameters.', { parameters: req.params })
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
return next()
......
......@@ -91,6 +91,7 @@ import {
videoModelToFormattedDetailsJSON,
videoModelToFormattedJSON
} 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
const indexes: Sequelize.DefineIndexesOptions[] = [
......@@ -466,6 +467,7 @@ type AvailableForListIDsOptions = {
required: false,
include: [
{
attributes: [ 'fileUrl' ],
model: () => VideoRedundancyModel.unscoped(),
required: false
}
......@@ -1062,44 +1064,34 @@ export class VideoModel extends Model<VideoModel> {
return VideoModel.getAvailableForApi(query, queryOptions)
}
static load (id: number, t?: Sequelize.Transaction) {
return VideoModel.findById(id, { transaction: t })
}
static loadWithFile (id: number, t?: Sequelize.Transaction, logging?: boolean) {
return VideoModel.scope(ScopeNames.WITH_FILES)
.findById(id, { transaction: t, logging })
}
static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoModel> = {
where: {
url
}
static load (id: number | string, t?: Sequelize.Transaction) {
const where = VideoModel.buildWhereIdOrUUID(id)
const options = {
where,
transaction: t
}
if (t !== undefined) query.transaction = t
return VideoModel.scope([ ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_FILES ]).findOne(query)
return VideoModel.findOne(options)
}
static loadAndPopulateAccountAndServerAndTags (id: number) {
static loadOnlyId (id: number | string, t?: Sequelize.Transaction) {
const where = VideoModel.buildWhereIdOrUUID(id)
const options = {
order: [ [ 'Tags', 'name', 'ASC' ] ]
attributes: [ 'id' ],
where,
transaction: t
}
return VideoModel
.scope([
ScopeNames.WITH_TAGS,
ScopeNames.WITH_BLACKLISTED,
ScopeNames.WITH_FILES,
ScopeNames.WITH_ACCOUNT_DETAILS,
ScopeNames.WITH_SCHEDULED_UPDATE
])
.findById(id, options)
return VideoModel.findOne(options)
}
static loadWithFile (id: number, t?: Sequelize.Transaction, logging?: boolean) {
return VideoModel.scope(ScopeNames.WITH_FILES)
.findById(id, { transaction: t, logging })
}
static loadByUUID (uuid: string) {
static loadByUUIDWithFile (uuid: string) {
const options = {
where: {
uuid
......@@ -1111,12 +1103,24 @@ export class VideoModel extends Model<VideoModel> {
.findOne(options)
}
static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string, t?: Sequelize.Transaction) {
static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoModel> = {
where: {
url
}
}
if (t !== undefined) query.transaction = t
return VideoModel.scope([ ScopeNames.WITH_ACCOUNT_DETAILS, ScopeNames.WITH_FILES ]).findOne(query)
}
static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction) {
const where = VideoModel.buildWhereIdOrUUID(id)
const options = {
order: [ [ 'Tags', 'name', 'ASC' ] ],
where: {
uuid
},
where,
transaction: t
}
......@@ -1277,6 +1281,10 @@ export class VideoModel extends Model<VideoModel> {
return VIDEO_STATES[ id ] || 'Unknown'
}
static buildWhereIdOrUUID (id: number | string) {
return validator.isInt('' + id) ? { id } : { uuid: id }
}
getOriginalFile () {
if (Array.isArray(this.VideoFiles) === false) return undefined
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment