Use sequelize scopes

This commit is contained in:
Chocobozzz 2017-12-14 10:07:57 +01:00
parent 94edfc3b2a
commit d48ff09d27
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
13 changed files with 264 additions and 312 deletions

View File

@ -58,6 +58,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
this.files = hash.files this.files = hash.files
this.channel = hash.channel this.channel = hash.channel
this.account = hash.account this.account = hash.account
this.tags = hash.tags
this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100 this.likesPercent = (this.likes / (this.likes + this.dislikes)) * 100
this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100 this.dislikesPercent = (this.dislikes / (this.likes + this.dislikes)) * 100

View File

@ -22,7 +22,6 @@ export class Video implements VideoServerModel {
isLocal: boolean isLocal: boolean
name: string name: string
serverHost: string serverHost: string
tags: string[]
thumbnailPath: string thumbnailPath: string
thumbnailUrl: string thumbnailUrl: string
previewPath: string previewPath: string
@ -71,7 +70,6 @@ export class Video implements VideoServerModel {
this.isLocal = hash.isLocal this.isLocal = hash.isLocal
this.name = hash.name this.name = hash.name
this.serverHost = hash.serverHost this.serverHost = hash.serverHost
this.tags = hash.tags
this.thumbnailPath = hash.thumbnailPath this.thumbnailPath = hash.thumbnailPath
this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath
this.previewPath = hash.previewPath this.previewPath = hash.previewPath

View File

@ -78,7 +78,7 @@ const videoChannelsRemoveValidator = [
if (!await isVideoChannelExist(req.params.id, res)) return if (!await isVideoChannelExist(req.params.id, res)) return
// Check if the user who did the request is able to delete the video // Check if the user who did the request is able to delete the video
if (!checkUserCanDeleteVideoChannel(res.locals.user, res.locals.videoChannel, res)) return if (!checkUserCanDeleteVideoChannel(res.locals.oauth.token.User, res.locals.videoChannel, res)) return
if (!await checkVideoChannelIsNotTheLastOne(res)) return if (!await checkVideoChannelIsNotTheLastOne(res)) return
return next() return next()

View File

@ -5,12 +5,12 @@ import {
BeforeUpdate, BeforeUpdate,
Column, CreatedAt, Column, CreatedAt,
DataType, DataType,
Default, Default, DefaultScope,
HasMany, HasMany,
HasOne, HasOne,
Is, Is,
IsEmail, IsEmail,
Model, Model, Scopes,
Table, UpdatedAt Table, UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
@ -27,6 +27,25 @@ import { getSort, throwIfNotValid } from '../utils'
import { VideoChannelModel } from '../video/video-channel' import { VideoChannelModel } from '../video/video-channel'
import { AccountModel } from './account' import { AccountModel } from './account'
@DefaultScope({
include: [
{
model: () => AccountModel,
required: true
}
]
})
@Scopes({
withVideoChannel: {
include: [
{
model: () => AccountModel,
required: true,
include: [ () => VideoChannelModel ]
}
]
}
})
@Table({ @Table({
tableName: 'user', tableName: 'user',
indexes: [ indexes: [
@ -122,8 +141,7 @@ export class UserModel extends Model<UserModel> {
const query = { const query = {
offset: start, offset: start,
limit: count, limit: count,
order: [ getSort(sort) ], order: [ getSort(sort) ]
include: [ { model: AccountModel, required: true } ]
} }
return UserModel.findAndCountAll(query) return UserModel.findAndCountAll(query)
@ -136,19 +154,14 @@ export class UserModel extends Model<UserModel> {
} }
static loadById (id: number) { static loadById (id: number) {
const options = { return UserModel.findById(id)
include: [ { model: AccountModel, required: true } ]
}
return UserModel.findById(id, options)
} }
static loadByUsername (username: string) { static loadByUsername (username: string) {
const query = { const query = {
where: { where: {
username username
}, }
include: [ { model: AccountModel, required: true } ]
} }
return UserModel.findOne(query) return UserModel.findOne(query)
@ -158,29 +171,20 @@ export class UserModel extends Model<UserModel> {
const query = { const query = {
where: { where: {
username username
}, }
include: [
{
model: AccountModel,
required: true,
include: [ VideoChannelModel ]
}
]
} }
return UserModel.findOne(query) return UserModel.scope('withVideoChannel').findOne(query)
} }
static loadByUsernameOrEmail (username: string, email: string) { static loadByUsernameOrEmail (username: string, email: string) {
const query = { const query = {
include: [ { model: AccountModel, required: true } ],
where: { where: {
[ Sequelize.Op.or ]: [ { username }, { email } ] [ Sequelize.Op.or ]: [ { username }, { email } ]
} }
} }
// FIXME: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18387 return UserModel.findOne(query)
return (UserModel as any).findOne(query)
} }
private static getOriginalVideoFileTotalFromUser (user: UserModel) { private static getOriginalVideoFileTotalFromUser (user: UserModel) {

View File

@ -1,4 +1,3 @@
import { Transaction } from 'sequelize'
import { AllowNull, Column, Default, IsInt, Model, Table } from 'sequelize-typescript' import { AllowNull, Column, Default, IsInt, Model, Table } from 'sequelize-typescript'
@Table({ @Table({
@ -15,21 +14,4 @@ export class ApplicationModel extends Model<ApplicationModel> {
static countTotal () { static countTotal () {
return ApplicationModel.count() return ApplicationModel.count()
} }
static loadMigrationVersion () {
const query = {
attributes: [ 'migrationVersion' ]
}
return ApplicationModel.findOne(query).then(data => data ? data.migrationVersion : null)
}
static updateMigrationVersion (newVersion: number, transaction: Transaction) {
const options = {
where: {},
transaction: transaction
}
return ApplicationModel.update({ migrationVersion: newVersion }, options)
}
} }

View File

@ -1,4 +1,4 @@
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { logger } from '../../helpers' import { logger } from '../../helpers'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { UserModel } from '../account/user' import { UserModel } from '../account/user'
@ -15,6 +15,25 @@ export type OAuthTokenInfo = {
} }
} }
enum ScopeNames {
WITH_ACCOUNT = 'WITH_ACCOUNT'
}
@Scopes({
[ScopeNames.WITH_ACCOUNT]: {
include: [
{
model: () => UserModel,
include: [
{
model: () => AccountModel,
required: true
}
]
}
]
}
})
@Table({ @Table({
tableName: 'oAuthToken', tableName: 'oAuthToken',
indexes: [ indexes: [
@ -115,21 +134,10 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
const query = { const query = {
where: { where: {
accessToken: bearerToken accessToken: bearerToken
}, }
include: [
{
model: UserModel,
include: [
{
model: AccountModel,
required: true
}
]
}
]
} }
return OAuthTokenModel.findOne(query).then(token => { return OAuthTokenModel.scope(ScopeNames.WITH_ACCOUNT).findOne(query).then(token => {
if (token) token['user'] = token.User if (token) token['user'] = token.User
return token return token
@ -140,24 +148,15 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
const query = { const query = {
where: { where: {
refreshToken: refreshToken refreshToken: refreshToken
}, }
include: [
{
model: UserModel,
include: [
{
model: AccountModel,
required: true
}
]
}
]
} }
return OAuthTokenModel.findOne(query).then(token => { return OAuthTokenModel.scope(ScopeNames.WITH_ACCOUNT)
token['user'] = token.User .findOne(query)
.then(token => {
token['user'] = token.User
return token return token
}) })
} }
} }

View File

@ -1,8 +1,35 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { VideoChannelModel } from './video-channel' import { VideoChannelModel } from './video-channel'
enum ScopeNames {
FULL = 'FULL',
WITH_ACCOUNT = 'WITH_ACCOUNT'
}
@Scopes({
[ScopeNames.FULL]: {
include: [
{
model: () => AccountModel,
required: true
},
{
model: () => VideoChannelModel,
required: true
}
]
},
[ScopeNames.WITH_ACCOUNT]: {
include: [
{
model: () => AccountModel,
required: true
}
]
}
})
@Table({ @Table({
tableName: 'videoChannelShare', tableName: 'videoChannelShare',
indexes: [ indexes: [
@ -46,15 +73,11 @@ export class VideoChannelShareModel extends Model<VideoChannelShareModel> {
VideoChannel: VideoChannelModel VideoChannel: VideoChannelModel
static load (accountId: number, videoChannelId: number, t: Sequelize.Transaction) { static load (accountId: number, videoChannelId: number, t: Sequelize.Transaction) {
return VideoChannelShareModel.findOne({ return VideoChannelShareModel.scope(ScopeNames.FULL).findOne({
where: { where: {
accountId, accountId,
videoChannelId videoChannelId
}, },
include: [
AccountModel,
VideoChannelModel
],
transaction: t transaction: t
}) })
} }
@ -64,16 +87,10 @@ export class VideoChannelShareModel extends Model<VideoChannelShareModel> {
where: { where: {
videoChannelId videoChannelId
}, },
include: [
{
model: AccountModel,
required: true
}
],
transaction: t transaction: t
} }
return VideoChannelShareModel.findAll(query) return VideoChannelShareModel.scope(ScopeNames.WITH_ACCOUNT).findAll(query)
.then(res => res.map(r => r.Account)) .then(res => res.map(r => r.Account))
} }
} }

View File

@ -11,7 +11,7 @@ import {
HasMany, HasMany,
Is, Is,
IsUUID, IsUUID,
Model, Model, Scopes,
Table, Table,
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
@ -28,6 +28,26 @@ import { getSort, throwIfNotValid } from '../utils'
import { VideoModel } from './video' import { VideoModel } from './video'
import { VideoChannelShareModel } from './video-channel-share' import { VideoChannelShareModel } from './video-channel-share'
enum ScopeNames {
WITH_ACCOUNT = 'WITH_ACCOUNT',
WITH_VIDEOS = 'WITH_VIDEOS'
}
@Scopes({
[ScopeNames.WITH_ACCOUNT]: {
include: [
{
model: () => AccountModel,
include: [ { model: () => ServerModel, required: false } ]
}
]
},
[ScopeNames.WITH_VIDEOS]: {
include: [
() => VideoModel
]
}
})
@Table({ @Table({
tableName: 'videoChannel', tableName: 'videoChannel',
indexes: [ indexes: [
@ -122,17 +142,10 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
const query = { const query = {
offset: start, offset: start,
limit: count, limit: count,
order: [ getSort(sort) ], order: [ getSort(sort) ]
include: [
{
model: AccountModel,
required: true,
include: [ { model: ServerModel, required: false } ]
}
]
} }
return VideoChannelModel.findAndCountAll(query) return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findAndCountAll(query)
.then(({ rows, count }) => { .then(({ rows, count }) => {
return { total: count, data: rows } return { total: count, data: rows }
}) })
@ -159,29 +172,16 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
}) })
} }
static loadByUUID (uuid: string, t?: Sequelize.Transaction) { static loadByUrl (url: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoChannelModel> = { const query: IFindOptions<VideoChannelModel> = {
where: { where: {
uuid url
} }
} }
if (t !== undefined) query.transaction = t if (t !== undefined) query.transaction = t
return VideoChannelModel.findOne(query) return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findOne(query)
}
static loadByUrl (url: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoChannelModel> = {
where: {
url
},
include: [ AccountModel ]
}
if (t !== undefined) query.transaction = t
return VideoChannelModel.findOne(query)
} }
static loadByUUIDOrUrl (uuid: string, url: string, t?: Sequelize.Transaction) { static loadByUUIDOrUrl (uuid: string, url: string, t?: Sequelize.Transaction) {
@ -199,90 +199,39 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
return VideoChannelModel.findOne(query) return VideoChannelModel.findOne(query)
} }
static loadByHostAndUUID (fromHost: string, uuid: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoChannelModel> = {
where: {
uuid
},
include: [
{
model: AccountModel,
include: [
{
model: ServerModel,
required: true,
where: {
host: fromHost
}
}
]
}
]
}
if (t !== undefined) query.transaction = t
return VideoChannelModel.findOne(query)
}
static loadByIdAndAccount (id: number, accountId: number) { static loadByIdAndAccount (id: number, accountId: number) {
const options = { const options = {
where: { where: {
id, id,
accountId accountId
}, }
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
}
]
} }
return VideoChannelModel.findOne(options) return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findOne(options)
} }
static loadAndPopulateAccount (id: number) { static loadAndPopulateAccount (id: number) {
const options = { return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findById(id)
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
}
]
}
return VideoChannelModel.findById(id, options)
} }
static loadByUUIDAndPopulateAccount (uuid: string) { static loadByUUIDAndPopulateAccount (uuid: string) {
const options = { const options = {
where: { where: {
uuid uuid
}, }
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
}
]
} }
return VideoChannelModel.findOne(options) return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findOne(options)
} }
static loadAndPopulateAccountAndVideos (id: number) { static loadAndPopulateAccountAndVideos (id: number) {
const options = { const options = {
include: [ include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
},
VideoModel VideoModel
] ]
} }
return VideoChannelModel.findById(id, options) return VideoChannelModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]).findById(id, options)
} }
isOwned () { isOwned () {

View File

@ -1,8 +1,35 @@
import * as Sequelize from 'sequelize' import * as Sequelize from 'sequelize'
import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { AccountModel } from '../account/account' import { AccountModel } from '../account/account'
import { VideoModel } from './video' import { VideoModel } from './video'
enum ScopeNames {
FULL = 'FULL',
WITH_ACCOUNT = 'WITH_ACCOUNT'
}
@Scopes({
[ScopeNames.FULL]: {
include: [
{
model: () => AccountModel,
required: true
},
{
model: () => VideoModel,
required: true
}
]
},
[ScopeNames.WITH_ACCOUNT]: {
include: [
{
model: () => AccountModel,
required: true
}
]
}
})
@Table({ @Table({
tableName: 'videoShare', tableName: 'videoShare',
indexes: [ indexes: [
@ -46,14 +73,11 @@ export class VideoShareModel extends Model<VideoShareModel> {
Video: VideoModel Video: VideoModel
static load (accountId: number, videoId: number, t: Sequelize.Transaction) { static load (accountId: number, videoId: number, t: Sequelize.Transaction) {
return VideoShareModel.findOne({ return VideoShareModel.scope(ScopeNames.WITH_ACCOUNT).findOne({
where: { where: {
accountId, accountId,
videoId videoId
}, },
include: [
AccountModel
],
transaction: t transaction: t
}) })
} }
@ -72,7 +96,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
transaction: t transaction: t
} }
return VideoShareModel.findAll(query) return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
.then(res => res.map(r => r.Account)) .then(res => res.map(r => r.Account))
} }
} }

View File

@ -21,12 +21,14 @@ import {
IsUUID, IsUUID,
Min, Min,
Model, Model,
Scopes,
Table, Table,
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } from 'sequelize-typescript'
import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions' import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions'
import { VideoPrivacy, VideoResolution } from '../../../shared' import { VideoPrivacy, VideoResolution } from '../../../shared'
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
import { Video, VideoDetails } from '../../../shared/models/videos'
import { import {
activityPubCollection, activityPubCollection,
createTorrentPromise, createTorrentPromise,
@ -76,6 +78,79 @@ import { VideoFileModel } from './video-file'
import { VideoShareModel } from './video-share' import { VideoShareModel } from './video-share'
import { VideoTagModel } from './video-tag' import { VideoTagModel } from './video-tag'
enum ScopeNames {
NOT_IN_BLACKLIST = 'NOT_IN_BLACKLIST',
PUBLIC = 'PUBLIC',
WITH_ACCOUNT = 'WITH_ACCOUNT',
WITH_TAGS = 'WITH_TAGS',
WITH_FILES = 'WITH_FILES',
WITH_SHARES = 'WITH_SHARES',
WITH_RATES = 'WITH_RATES'
}
@Scopes({
[ScopeNames.NOT_IN_BLACKLIST]: {
where: {
id: {
[Sequelize.Op.notIn]: Sequelize.literal(
'(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
)
}
}
},
[ScopeNames.PUBLIC]: {
where: {
privacy: VideoPrivacy.PUBLIC
}
},
[ScopeNames.WITH_ACCOUNT]: {
include: [
{
model: () => VideoChannelModel,
required: true,
include: [
{
model: () => AccountModel,
required: true,
include: [
{
model: () => ServerModel,
required: false
}
]
}
]
}
]
},
[ScopeNames.WITH_TAGS]: {
include: [ () => TagModel ]
},
[ScopeNames.WITH_FILES]: {
include: [
{
model: () => VideoFileModel,
required: true
}
]
},
[ScopeNames.WITH_SHARES]: {
include: [
{
model: () => VideoShareModel,
include: [ () => AccountModel ]
}
]
},
[ScopeNames.WITH_RATES]: {
include: [
{
model: () => AccountVideoRateModel,
include: [ () => AccountModel ]
}
]
}
})
@Table({ @Table({
tableName: 'video', tableName: 'video',
indexes: [ indexes: [
@ -273,11 +348,7 @@ export class VideoModel extends Model<VideoModel> {
} }
static list () { static list () {
const query = { return VideoModel.scope(ScopeNames.WITH_FILES).findAll()
include: [ VideoFileModel ]
}
return VideoModel.findAll(query)
} }
static listAllAndSharedByAccountForOutbox (accountId: number, start: number, count: number) { static listAllAndSharedByAccountForOutbox (accountId: number, start: number, count: number) {
@ -363,10 +434,9 @@ export class VideoModel extends Model<VideoModel> {
static listUserVideosForApi (userId: number, start: number, count: number, sort: string) { static listUserVideosForApi (userId: number, start: number, count: number, sort: string) {
const query = { const query = {
distinct: true,
offset: start, offset: start,
limit: count, limit: count,
order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ], order: [ getSort(sort) ],
include: [ include: [
{ {
model: VideoChannelModel, model: VideoChannelModel,
@ -380,8 +450,7 @@ export class VideoModel extends Model<VideoModel> {
required: true required: true
} }
] ]
}, }
TagModel
] ]
} }
@ -395,74 +464,35 @@ export class VideoModel extends Model<VideoModel> {
static listForApi (start: number, count: number, sort: string) { static listForApi (start: number, count: number, sort: string) {
const query = { const query = {
distinct: true,
offset: start, offset: start,
limit: count, limit: count,
order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ], order: [ getSort(sort) ]
include: [
{
model: VideoChannelModel,
required: true,
include: [
{
model: AccountModel,
required: true,
include: [
{
model: ServerModel,
required: false
}
]
}
]
},
TagModel
],
where: this.createBaseVideosWhere()
} }
return VideoModel.findAndCountAll(query).then(({ rows, count }) => { return VideoModel.scope([ ScopeNames.NOT_IN_BLACKLIST, ScopeNames.PUBLIC, ScopeNames.WITH_ACCOUNT ])
return { .findAndCountAll(query)
data: rows, .then(({ rows, count }) => {
total: count return {
} data: rows,
}) total: count
}
})
} }
static load (id: number) { static load (id: number) {
return VideoModel.findById(id) return VideoModel.findById(id)
} }
static loadByUUID (uuid: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoModel> = {
where: {
uuid
},
include: [ VideoFileModel ]
}
if (t !== undefined) query.transaction = t
return VideoModel.findOne(query)
}
static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
const query: IFindOptions<VideoModel> = { const query: IFindOptions<VideoModel> = {
where: { where: {
url url
}, }
include: [
VideoFileModel,
{
model: VideoChannelModel,
include: [ AccountModel ]
}
]
} }
if (t !== undefined) query.transaction = t if (t !== undefined) query.transaction = t
return VideoModel.findOne(query) return VideoModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_FILES ]).findOne(query)
} }
static loadByUUIDOrURL (uuid: string, url: string, t?: Sequelize.Transaction) { static loadByUUIDOrURL (uuid: string, url: string, t?: Sequelize.Transaction) {
@ -472,42 +502,22 @@ export class VideoModel extends Model<VideoModel> {
{ uuid }, { uuid },
{ url } { url }
] ]
}, }
include: [ VideoFileModel ]
} }
if (t !== undefined) query.transaction = t if (t !== undefined) query.transaction = t
return VideoModel.findOne(query) return VideoModel.scope(ScopeNames.WITH_FILES).findOne(query)
} }
static loadAndPopulateAccountAndServerAndTags (id: number) { static loadAndPopulateAccountAndServerAndTags (id: number) {
const options = { const options = {
order: [ [ 'Tags', 'name', 'ASC' ] ], order: [ [ 'Tags', 'name', 'ASC' ] ]
include: [
{
model: VideoChannelModel,
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
}
]
},
{
model: AccountVideoRateModel,
include: [ AccountModel ]
},
{
model: VideoShareModel,
include: [ AccountModel ]
},
TagModel,
VideoFileModel
]
} }
return VideoModel.findById(id, options) return VideoModel
.scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
.findById(id, options)
} }
static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string) { static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string) {
@ -515,31 +525,12 @@ export class VideoModel extends Model<VideoModel> {
order: [ [ 'Tags', 'name', 'ASC' ] ], order: [ [ 'Tags', 'name', 'ASC' ] ],
where: { where: {
uuid uuid
}, }
include: [
{
model: VideoChannelModel,
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
}
]
},
{
model: AccountVideoRateModel,
include: [ AccountModel ]
},
{
model: VideoShareModel,
include: [ AccountModel ]
},
TagModel,
VideoFileModel
]
} }
return VideoModel.findOne(options) return VideoModel
.scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
.findOne(options)
} }
static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) { static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
@ -564,11 +555,11 @@ export class VideoModel extends Model<VideoModel> {
} }
const query: IFindOptions<VideoModel> = { const query: IFindOptions<VideoModel> = {
distinct: true, distinct: true, // Because we have tags
where: this.createBaseVideosWhere(),
offset: start, offset: start,
limit: count, limit: count,
order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ] order: [ getSort(sort) ],
where: {}
} }
// TODO: search on tags too // TODO: search on tags too
@ -595,23 +586,13 @@ export class VideoModel extends Model<VideoModel> {
videoChannelInclude, tagInclude videoChannelInclude, tagInclude
] ]
return VideoModel.findAndCountAll(query).then(({ rows, count }) => { return VideoModel.scope([ ScopeNames.NOT_IN_BLACKLIST, ScopeNames.PUBLIC ])
return { .findAndCountAll(query).then(({ rows, count }) => {
data: rows, return {
total: count data: rows,
} total: count
}) }
} })
private static createBaseVideosWhere () {
return {
id: {
[Sequelize.Op.notIn]: VideoModel.sequelize.literal(
'(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
)
},
privacy: VideoPrivacy.PUBLIC
}
} }
getOriginalFile () { getOriginalFile () {
@ -733,13 +714,12 @@ export class VideoModel extends Model<VideoModel> {
views: this.views, views: this.views,
likes: this.likes, likes: this.likes,
dislikes: this.dislikes, dislikes: this.dislikes,
tags: map<TagModel, string>(this.Tags, 'name'),
thumbnailPath: this.getThumbnailPath(), thumbnailPath: this.getThumbnailPath(),
previewPath: this.getPreviewPath(), previewPath: this.getPreviewPath(),
embedPath: this.getEmbedPath(), embedPath: this.getEmbedPath(),
createdAt: this.createdAt, createdAt: this.createdAt,
updatedAt: this.updatedAt updatedAt: this.updatedAt
} } as Video
} }
toFormattedDetailsJSON () { toFormattedDetailsJSON () {
@ -755,6 +735,7 @@ export class VideoModel extends Model<VideoModel> {
descriptionPath: this.getDescriptionPath(), descriptionPath: this.getDescriptionPath(),
channel: this.VideoChannel.toFormattedJSON(), channel: this.VideoChannel.toFormattedJSON(),
account: this.VideoChannel.Account.toFormattedJSON(), account: this.VideoChannel.Account.toFormattedJSON(),
tags: map<TagModel, string>(this.Tags, 'name'),
files: [] files: []
} }
@ -779,7 +760,7 @@ export class VideoModel extends Model<VideoModel> {
return -1 return -1
}) })
return Object.assign(formattedJson, detailsJson) return Object.assign(formattedJson, detailsJson) as VideoDetails
} }
toActivityPubObject (): VideoTorrentObject { toActivityPubObject (): VideoTorrentObject {

View File

@ -132,7 +132,6 @@ describe('Test a single server', function () {
expect(video.serverHost).to.equal('localhost:9001') expect(video.serverHost).to.equal('localhost:9001')
expect(video.accountName).to.equal('root') expect(video.accountName).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(dateIsValid(video.createdAt)).to.be.true expect(dateIsValid(video.createdAt)).to.be.true
expect(dateIsValid(video.updatedAt)).to.be.true expect(dateIsValid(video.updatedAt)).to.be.true
@ -181,7 +180,6 @@ describe('Test a single server', function () {
expect(video.serverHost).to.equal('localhost:9001') expect(video.serverHost).to.equal('localhost:9001')
expect(video.accountName).to.equal('root') expect(video.accountName).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(dateIsValid(video.createdAt)).to.be.true expect(dateIsValid(video.createdAt)).to.be.true
expect(dateIsValid(video.updatedAt)).to.be.true expect(dateIsValid(video.updatedAt)).to.be.true
expect(video.channel.name).to.equal('Default root channel') expect(video.channel.name).to.equal('Default root channel')
@ -248,7 +246,6 @@ describe('Test a single server', function () {
expect(video.serverHost).to.equal('localhost:9001') expect(video.serverHost).to.equal('localhost:9001')
expect(video.accountName).to.equal('root') expect(video.accountName).to.equal('root')
expect(video.isLocal).to.be.true expect(video.isLocal).to.be.true
expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ])
expect(dateIsValid(video.createdAt)).to.be.true expect(dateIsValid(video.createdAt)).to.be.true
expect(dateIsValid(video.updatedAt)).to.be.true expect(dateIsValid(video.updatedAt)).to.be.true

View File

@ -47,7 +47,7 @@ describe('Test video abuses', function () {
await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes) await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes)
// Wait videos propagation, server 2 has transcoding enabled // Wait videos propagation, server 2 has transcoding enabled
await wait(10000) await wait(15000)
const res = await getVideosList(servers[0].url) const res = await getVideosList(servers[0].url)
const videos = res.body.data const videos = res.body.data

View File

@ -28,7 +28,6 @@ export interface Video {
isLocal: boolean isLocal: boolean
name: string name: string
serverHost: string serverHost: string
tags: string[]
thumbnailPath: string thumbnailPath: string
previewPath: string previewPath: string
embedPath: string embedPath: string
@ -43,6 +42,7 @@ export interface VideoDetails extends Video {
privacyLabel: string privacyLabel: string
descriptionPath: string descriptionPath: string
channel: VideoChannel channel: VideoChannel
tags: string[]
files: VideoFile[] files: VideoFile[]
account: Account account: Account
} }