Use sequelize scopes

pull/159/head
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.channel = hash.channel
this.account = hash.account
this.tags = hash.tags
this.likesPercent = (this.likes / (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
name: string
serverHost: string
tags: string[]
thumbnailPath: string
thumbnailUrl: string
previewPath: string
@ -71,7 +70,6 @@ export class Video implements VideoServerModel {
this.isLocal = hash.isLocal
this.name = hash.name
this.serverHost = hash.serverHost
this.tags = hash.tags
this.thumbnailPath = hash.thumbnailPath
this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath
this.previewPath = hash.previewPath

View File

@ -78,7 +78,7 @@ const videoChannelsRemoveValidator = [
if (!await isVideoChannelExist(req.params.id, res)) return
// 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
return next()

View File

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

View File

@ -1,8 +1,35 @@
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 { 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({
tableName: 'videoChannelShare',
indexes: [
@ -46,15 +73,11 @@ export class VideoChannelShareModel extends Model<VideoChannelShareModel> {
VideoChannel: VideoChannelModel
static load (accountId: number, videoChannelId: number, t: Sequelize.Transaction) {
return VideoChannelShareModel.findOne({
return VideoChannelShareModel.scope(ScopeNames.FULL).findOne({
where: {
accountId,
videoChannelId
},
include: [
AccountModel,
VideoChannelModel
],
transaction: t
})
}
@ -64,16 +87,10 @@ export class VideoChannelShareModel extends Model<VideoChannelShareModel> {
where: {
videoChannelId
},
include: [
{
model: AccountModel,
required: true
}
],
transaction: t
}
return VideoChannelShareModel.findAll(query)
return VideoChannelShareModel.scope(ScopeNames.WITH_ACCOUNT).findAll(query)
.then(res => res.map(r => r.Account))
}
}

View File

@ -11,7 +11,7 @@ import {
HasMany,
Is,
IsUUID,
Model,
Model, Scopes,
Table,
UpdatedAt
} from 'sequelize-typescript'
@ -28,6 +28,26 @@ import { getSort, throwIfNotValid } from '../utils'
import { VideoModel } from './video'
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({
tableName: 'videoChannel',
indexes: [
@ -122,17 +142,10 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
const query = {
offset: start,
limit: count,
order: [ getSort(sort) ],
include: [
{
model: AccountModel,
required: true,
include: [ { model: ServerModel, required: false } ]
}
]
order: [ getSort(sort) ]
}
return VideoChannelModel.findAndCountAll(query)
return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findAndCountAll(query)
.then(({ rows, count }) => {
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> = {
where: {
uuid
url
}
}
if (t !== undefined) query.transaction = t
return VideoChannelModel.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)
return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findOne(query)
}
static loadByUUIDOrUrl (uuid: string, url: string, t?: Sequelize.Transaction) {
@ -199,90 +199,39 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
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) {
const options = {
where: {
id,
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) {
const options = {
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
}
]
}
return VideoChannelModel.findById(id, options)
return VideoChannelModel.scope(ScopeNames.WITH_ACCOUNT).findById(id)
}
static loadByUUIDAndPopulateAccount (uuid: string) {
const options = {
where: {
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) {
const options = {
include: [
{
model: AccountModel,
include: [ { model: ServerModel, required: false } ]
},
VideoModel
]
}
return VideoChannelModel.findById(id, options)
return VideoChannelModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]).findById(id, options)
}
isOwned () {

View File

@ -1,8 +1,35 @@
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 { 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({
tableName: 'videoShare',
indexes: [
@ -46,14 +73,11 @@ export class VideoShareModel extends Model<VideoShareModel> {
Video: VideoModel
static load (accountId: number, videoId: number, t: Sequelize.Transaction) {
return VideoShareModel.findOne({
return VideoShareModel.scope(ScopeNames.WITH_ACCOUNT).findOne({
where: {
accountId,
videoId
},
include: [
AccountModel
],
transaction: t
})
}
@ -72,7 +96,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
transaction: t
}
return VideoShareModel.findAll(query)
return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
.then(res => res.map(r => r.Account))
}
}

View File

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

View File

@ -132,7 +132,6 @@ describe('Test a single server', function () {
expect(video.serverHost).to.equal('localhost:9001')
expect(video.accountName).to.equal('root')
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.updatedAt)).to.be.true
@ -181,7 +180,6 @@ describe('Test a single server', function () {
expect(video.serverHost).to.equal('localhost:9001')
expect(video.accountName).to.equal('root')
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.updatedAt)).to.be.true
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.accountName).to.equal('root')
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.updatedAt)).to.be.true

View File

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

View File

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