Use sequelize scopes
authorChocobozzz <me@florianbigard.com>
Thu, 14 Dec 2017 09:07:57 +0000 (10:07 +0100)
committerChocobozzz <me@florianbigard.com>
Thu, 14 Dec 2017 09:07:57 +0000 (10:07 +0100)
13 files changed:
client/src/app/shared/video/video-details.model.ts
client/src/app/shared/video/video.model.ts
server/middlewares/validators/video-channels.ts
server/models/account/user.ts
server/models/application/application.ts
server/models/oauth/oauth-token.ts
server/models/video/video-channel-share.ts
server/models/video/video-channel.ts
server/models/video/video-share.ts
server/models/video/video.ts
server/tests/api/single-server.ts
server/tests/api/video-abuse.ts
shared/models/videos/video.model.ts

index b96f8f6c82572680f5332b3c9f2fbf036d1b2b11..d51bc01a787aefdb7beb07e4d2dec65ce6dcf667 100644 (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
index 32c33829d4a0bf85f03318c7202e7f07181a3181..c3759cb65e735da164f49d612b7723a645b599f9 100644 (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
index 660390080203c9d77df01bcef13eebdabf22ccee..068fd210f97fddde835fb11e3c7d95524691eb97 100644 (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()
index 84adad96e685409863c3474bec75f976f071f3fb..26f04dcb5457a5f227b4ea4d7ec62a32caf9cfc2 100644 (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) {
index f3c0f1052735c2749110a0c7bf2296086763fa84..9fc07e8505c311c33667e53e7d0ab218ecf8fa3d 100644 (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)
-  }
 }
index 0d21c42fd26a31f02cca7718fdd73d27af5d0b87..995fa33d5926b93e024e287b408b0af6bca569c3 100644 (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
+      })
   }
 }
index cdba32fcdc92dbe7cb23cf224d949db8e269608b..f5b7a7cd519b63959758ecb8adac8ef983fabb6e 100644 (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))
   }
 }
index 9b545a4efc1e9ca6595c9782664e83fed9365e6a..068c8029d033eb0cec79c446495bc42fa63ae7e2 100644 (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) {
-    const query: IFindOptions<VideoChannelModel> = {
-      where: {
-        uuid
-      }
-    }
-
-    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 () {
index 01b6d3d34ab698c5f25eb821cb0c89288f0b8239..e1733b3a74928ac3cd1be7b7f9c5be187ac738bf 100644 (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))
   }
 }
index 9e26f9bbedb2c34568fd7fb814524e433eb7d9e4..1f940a50d1cba97584c225084f84dbb4e9d62e7a 100644 (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 {
index 174fb480d749c28098c03c7fb2582c3335282e65..7f4351f5eea621b63032efe8b824a820ecd9b464 100644 (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
 
index 60bee9c3d40d51d8b3443a658da86fe26c94c6b5..4a0b6b5041eb6798d0e21051f72566b11116a901 100644 (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
index dc12a05d916e3fd8655e8ff82d5d8c9db4dcc800..3a378419f236a3779730107a365cd48fa35987d6 100644 (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
 }