aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account-blocklist.ts111
-rw-r--r--server/models/server/server-blocklist.ts121
-rw-r--r--server/models/server/server.ts6
-rw-r--r--server/models/utils.ts18
-rw-r--r--server/models/video/video-comment.ts95
-rw-r--r--server/models/video/video.ts40
6 files changed, 360 insertions, 31 deletions
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts
new file mode 100644
index 000000000..bacd122e8
--- /dev/null
+++ b/server/models/account/account-blocklist.ts
@@ -0,0 +1,111 @@
1import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
2import { AccountModel } from './account'
3import { getSort } from '../utils'
4import { AccountBlock } from '../../../shared/models/blocklist'
5
6enum ScopeNames {
7 WITH_ACCOUNTS = 'WITH_ACCOUNTS'
8}
9
10@Scopes({
11 [ScopeNames.WITH_ACCOUNTS]: {
12 include: [
13 {
14 model: () => AccountModel,
15 required: true,
16 as: 'ByAccount'
17 },
18 {
19 model: () => AccountModel,
20 required: true,
21 as: 'AccountBlocked'
22 }
23 ]
24 }
25})
26
27@Table({
28 tableName: 'accountBlocklist',
29 indexes: [
30 {
31 fields: [ 'accountId', 'targetAccountId' ],
32 unique: true
33 },
34 {
35 fields: [ 'targetAccountId' ]
36 }
37 ]
38})
39export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
40
41 @CreatedAt
42 createdAt: Date
43
44 @UpdatedAt
45 updatedAt: Date
46
47 @ForeignKey(() => AccountModel)
48 @Column
49 accountId: number
50
51 @BelongsTo(() => AccountModel, {
52 foreignKey: {
53 name: 'accountId',
54 allowNull: false
55 },
56 as: 'ByAccount',
57 onDelete: 'CASCADE'
58 })
59 ByAccount: AccountModel
60
61 @ForeignKey(() => AccountModel)
62 @Column
63 targetAccountId: number
64
65 @BelongsTo(() => AccountModel, {
66 foreignKey: {
67 name: 'targetAccountId',
68 allowNull: false
69 },
70 as: 'AccountBlocked',
71 onDelete: 'CASCADE'
72 })
73 AccountBlocked: AccountModel
74
75 static loadByAccountAndTarget (accountId: number, targetAccountId: number) {
76 const query = {
77 where: {
78 accountId,
79 targetAccountId
80 }
81 }
82
83 return AccountBlocklistModel.findOne(query)
84 }
85
86 static listForApi (accountId: number, start: number, count: number, sort: string) {
87 const query = {
88 offset: start,
89 limit: count,
90 order: getSort(sort),
91 where: {
92 accountId
93 }
94 }
95
96 return AccountBlocklistModel
97 .scope([ ScopeNames.WITH_ACCOUNTS ])
98 .findAndCountAll(query)
99 .then(({ rows, count }) => {
100 return { total: count, data: rows }
101 })
102 }
103
104 toFormattedJSON (): AccountBlock {
105 return {
106 byAccount: this.ByAccount.toFormattedJSON(),
107 accountBlocked: this.AccountBlocked.toFormattedJSON(),
108 createdAt: this.createdAt
109 }
110 }
111}
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts
new file mode 100644
index 000000000..705ed2c6b
--- /dev/null
+++ b/server/models/server/server-blocklist.ts
@@ -0,0 +1,121 @@
1import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
2import { AccountModel } from '../account/account'
3import { ServerModel } from './server'
4import { ServerBlock } from '../../../shared/models/blocklist'
5import { getSort } from '../utils'
6
7enum ScopeNames {
8 WITH_ACCOUNT = 'WITH_ACCOUNT',
9 WITH_SERVER = 'WITH_SERVER'
10}
11
12@Scopes({
13 [ScopeNames.WITH_ACCOUNT]: {
14 include: [
15 {
16 model: () => AccountModel,
17 required: true
18 }
19 ]
20 },
21 [ScopeNames.WITH_SERVER]: {
22 include: [
23 {
24 model: () => ServerModel,
25 required: true
26 }
27 ]
28 }
29})
30
31@Table({
32 tableName: 'serverBlocklist',
33 indexes: [
34 {
35 fields: [ 'accountId', 'targetServerId' ],
36 unique: true
37 },
38 {
39 fields: [ 'targetServerId' ]
40 }
41 ]
42})
43export class ServerBlocklistModel extends Model<ServerBlocklistModel> {
44
45 @CreatedAt
46 createdAt: Date
47
48 @UpdatedAt
49 updatedAt: Date
50
51 @ForeignKey(() => AccountModel)
52 @Column
53 accountId: number
54
55 @BelongsTo(() => AccountModel, {
56 foreignKey: {
57 name: 'accountId',
58 allowNull: false
59 },
60 onDelete: 'CASCADE'
61 })
62 ByAccount: AccountModel
63
64 @ForeignKey(() => ServerModel)
65 @Column
66 targetServerId: number
67
68 @BelongsTo(() => ServerModel, {
69 foreignKey: {
70 name: 'targetServerId',
71 allowNull: false
72 },
73 onDelete: 'CASCADE'
74 })
75 ServerBlocked: ServerModel
76
77 static loadByAccountAndHost (accountId: number, host: string) {
78 const query = {
79 where: {
80 accountId
81 },
82 include: [
83 {
84 model: ServerModel,
85 where: {
86 host
87 },
88 required: true
89 }
90 ]
91 }
92
93 return ServerBlocklistModel.findOne(query)
94 }
95
96 static listForApi (accountId: number, start: number, count: number, sort: string) {
97 const query = {
98 offset: start,
99 limit: count,
100 order: getSort(sort),
101 where: {
102 accountId
103 }
104 }
105
106 return ServerBlocklistModel
107 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_SERVER ])
108 .findAndCountAll(query)
109 .then(({ rows, count }) => {
110 return { total: count, data: rows }
111 })
112 }
113
114 toFormattedJSON (): ServerBlock {
115 return {
116 byAccount: this.ByAccount.toFormattedJSON(),
117 serverBlocked: this.ServerBlocked.toFormattedJSON(),
118 createdAt: this.createdAt
119 }
120 }
121}
diff --git a/server/models/server/server.ts b/server/models/server/server.ts
index ca3b24d51..300d70938 100644
--- a/server/models/server/server.ts
+++ b/server/models/server/server.ts
@@ -49,4 +49,10 @@ export class ServerModel extends Model<ServerModel> {
49 49
50 return ServerModel.findOne(query) 50 return ServerModel.findOne(query)
51 } 51 }
52
53 toFormattedJSON () {
54 return {
55 host: this.host
56 }
57 }
52} 58}
diff --git a/server/models/utils.ts b/server/models/utils.ts
index e0bf091ad..50c865e75 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -64,9 +64,27 @@ function createSimilarityAttribute (col: string, value: string) {
64 ) 64 )
65} 65}
66 66
67function buildBlockedAccountSQL (serverAccountId: number, userAccountId?: number) {
68 const blockerIds = [ serverAccountId ]
69 if (userAccountId) blockerIds.push(userAccountId)
70
71 const blockerIdsString = blockerIds.join(', ')
72
73 const query = 'SELECT "targetAccountId" AS "id" FROM "accountBlocklist" WHERE "accountId" IN (' + blockerIdsString + ')' +
74 ' UNION ALL ' +
75 // 'SELECT "accountId" FROM "accountBlocklist" WHERE "targetAccountId" = user.account.id
76 // UNION ALL
77 'SELECT "account"."id" AS "id" FROM account INNER JOIN "actor" ON account."actorId" = actor.id ' +
78 'INNER JOIN "serverBlocklist" ON "actor"."serverId" = "serverBlocklist"."targetServerId" ' +
79 'WHERE "serverBlocklist"."accountId" IN (' + blockerIdsString + ')'
80
81 return query
82}
83
67// --------------------------------------------------------------------------- 84// ---------------------------------------------------------------------------
68 85
69export { 86export {
87 buildBlockedAccountSQL,
70 SortType, 88 SortType,
71 getSort, 89 getSort,
72 getVideoSort, 90 getVideoSort,
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index f84c1880c..08c6b3ff0 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,6 +1,17 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, 3 AllowNull,
4 BeforeDestroy,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 DataType,
9 ForeignKey,
10 IFindOptions,
11 Is,
12 Model,
13 Scopes,
14 Table,
4 UpdatedAt 15 UpdatedAt
5} from 'sequelize-typescript' 16} from 'sequelize-typescript'
6import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' 17import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
@@ -13,9 +24,11 @@ import { AccountModel } from '../account/account'
13import { ActorModel } from '../activitypub/actor' 24import { ActorModel } from '../activitypub/actor'
14import { AvatarModel } from '../avatar/avatar' 25import { AvatarModel } from '../avatar/avatar'
15import { ServerModel } from '../server/server' 26import { ServerModel } from '../server/server'
16import { getSort, throwIfNotValid } from '../utils' 27import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
17import { VideoModel } from './video' 28import { VideoModel } from './video'
18import { VideoChannelModel } from './video-channel' 29import { VideoChannelModel } from './video-channel'
30import { getServerActor } from '../../helpers/utils'
31import { UserModel } from '../account/user'
19 32
20enum ScopeNames { 33enum ScopeNames {
21 WITH_ACCOUNT = 'WITH_ACCOUNT', 34 WITH_ACCOUNT = 'WITH_ACCOUNT',
@@ -25,18 +38,29 @@ enum ScopeNames {
25} 38}
26 39
27@Scopes({ 40@Scopes({
28 [ScopeNames.ATTRIBUTES_FOR_API]: { 41 [ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => {
29 attributes: { 42 return {
30 include: [ 43 attributes: {
31 [ 44 include: [
32 Sequelize.literal( 45 [
33 '(SELECT COUNT("replies"."id") ' + 46 Sequelize.literal(
34 'FROM "videoComment" AS "replies" ' + 47 '(' +
35 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id")' 48 'WITH "blocklist" AS (' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' +
36 ), 49 'SELECT COUNT("replies"."id") - (' +
37 'totalReplies' 50 'SELECT COUNT("replies"."id") ' +
51 'FROM "videoComment" AS "replies" ' +
52 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ' +
53 'AND "accountId" IN (SELECT "id" FROM "blocklist")' +
54 ')' +
55 'FROM "videoComment" AS "replies" ' +
56 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id" ' +
57 'AND "accountId" NOT IN (SELECT "id" FROM "blocklist")' +
58 ')'
59 ),
60 'totalReplies'
61 ]
38 ] 62 ]
39 ] 63 }
40 } 64 }
41 }, 65 },
42 [ScopeNames.WITH_ACCOUNT]: { 66 [ScopeNames.WITH_ACCOUNT]: {
@@ -267,26 +291,47 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
267 return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_VIDEO ]).findOne(query) 291 return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_VIDEO ]).findOne(query)
268 } 292 }
269 293
270 static listThreadsForApi (videoId: number, start: number, count: number, sort: string) { 294 static async listThreadsForApi (videoId: number, start: number, count: number, sort: string, user?: UserModel) {
295 const serverActor = await getServerActor()
296 const serverAccountId = serverActor.Account.id
297 const userAccountId = user.Account.id
298
271 const query = { 299 const query = {
272 offset: start, 300 offset: start,
273 limit: count, 301 limit: count,
274 order: getSort(sort), 302 order: getSort(sort),
275 where: { 303 where: {
276 videoId, 304 videoId,
277 inReplyToCommentId: null 305 inReplyToCommentId: null,
306 accountId: {
307 [Sequelize.Op.notIn]: Sequelize.literal(
308 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
309 )
310 }
278 } 311 }
279 } 312 }
280 313
314 // FIXME: typings
315 const scopes: any[] = [
316 ScopeNames.WITH_ACCOUNT,
317 {
318 method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
319 }
320 ]
321
281 return VideoCommentModel 322 return VideoCommentModel
282 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ]) 323 .scope(scopes)
283 .findAndCountAll(query) 324 .findAndCountAll(query)
284 .then(({ rows, count }) => { 325 .then(({ rows, count }) => {
285 return { total: count, data: rows } 326 return { total: count, data: rows }
286 }) 327 })
287 } 328 }
288 329
289 static listThreadCommentsForApi (videoId: number, threadId: number) { 330 static async listThreadCommentsForApi (videoId: number, threadId: number, user?: UserModel) {
331 const serverActor = await getServerActor()
332 const serverAccountId = serverActor.Account.id
333 const userAccountId = user.Account.id
334
290 const query = { 335 const query = {
291 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], 336 order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ],
292 where: { 337 where: {
@@ -294,12 +339,24 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
294 [ Sequelize.Op.or ]: [ 339 [ Sequelize.Op.or ]: [
295 { id: threadId }, 340 { id: threadId },
296 { originCommentId: threadId } 341 { originCommentId: threadId }
297 ] 342 ],
343 accountId: {
344 [Sequelize.Op.notIn]: Sequelize.literal(
345 '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
346 )
347 }
298 } 348 }
299 } 349 }
300 350
351 const scopes: any[] = [
352 ScopeNames.WITH_ACCOUNT,
353 {
354 method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
355 }
356 ]
357
301 return VideoCommentModel 358 return VideoCommentModel
302 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ]) 359 .scope(scopes)
303 .findAndCountAll(query) 360 .findAndCountAll(query)
304 .then(({ rows, count }) => { 361 .then(({ rows, count }) => {
305 return { total: count, data: rows } 362 return { total: count, data: rows }
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 4f3f75613..eab99cba7 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -27,7 +27,7 @@ import {
27 Table, 27 Table,
28 UpdatedAt 28 UpdatedAt
29} from 'sequelize-typescript' 29} from 'sequelize-typescript'
30import { VideoPrivacy, VideoState } from '../../../shared' 30import { UserRight, VideoPrivacy, VideoState } from '../../../shared'
31import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 31import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
32import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos' 32import { Video, VideoDetails, VideoFile } from '../../../shared/models/videos'
33import { VideoFilter } from '../../../shared/models/videos/video-query.type' 33import { VideoFilter } from '../../../shared/models/videos/video-query.type'
@@ -70,7 +70,7 @@ import { AccountVideoRateModel } from '../account/account-video-rate'
70import { ActorModel } from '../activitypub/actor' 70import { ActorModel } from '../activitypub/actor'
71import { AvatarModel } from '../avatar/avatar' 71import { AvatarModel } from '../avatar/avatar'
72import { ServerModel } from '../server/server' 72import { ServerModel } from '../server/server'
73import { buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils' 73import { buildBlockedAccountSQL, buildTrigramSearchIndex, createSimilarityAttribute, getVideoSort, throwIfNotValid } from '../utils'
74import { TagModel } from './tag' 74import { TagModel } from './tag'
75import { VideoAbuseModel } from './video-abuse' 75import { VideoAbuseModel } from './video-abuse'
76import { VideoChannelModel } from './video-channel' 76import { VideoChannelModel } from './video-channel'
@@ -93,6 +93,7 @@ import {
93} from './video-format-utils' 93} from './video-format-utils'
94import * as validator from 'validator' 94import * as validator from 'validator'
95import { UserVideoHistoryModel } from '../account/user-video-history' 95import { UserVideoHistoryModel } from '../account/user-video-history'
96import { UserModel } from '../account/user'
96 97
97// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation 98// FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation
98const indexes: Sequelize.DefineIndexesOptions[] = [ 99const indexes: Sequelize.DefineIndexesOptions[] = [
@@ -138,6 +139,7 @@ type ForAPIOptions = {
138} 139}
139 140
140type AvailableForListIDsOptions = { 141type AvailableForListIDsOptions = {
142 serverAccountId: number
141 actorId: number 143 actorId: number
142 includeLocalVideos: boolean 144 includeLocalVideos: boolean
143 filter?: VideoFilter 145 filter?: VideoFilter
@@ -151,6 +153,7 @@ type AvailableForListIDsOptions = {
151 accountId?: number 153 accountId?: number
152 videoChannelId?: number 154 videoChannelId?: number
153 trendingDays?: number 155 trendingDays?: number
156 user?: UserModel
154} 157}
155 158
156@Scopes({ 159@Scopes({
@@ -235,6 +238,15 @@ type AvailableForListIDsOptions = {
235 ) 238 )
236 } 239 }
237 ] 240 ]
241 },
242 channelId: {
243 [ Sequelize.Op.notIn ]: Sequelize.literal(
244 '(' +
245 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' +
246 buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) +
247 ')' +
248 ')'
249 )
238 } 250 }
239 }, 251 },
240 include: [] 252 include: []
@@ -975,10 +987,10 @@ export class VideoModel extends Model<VideoModel> {
975 videoChannelId?: number, 987 videoChannelId?: number,
976 actorId?: number 988 actorId?: number
977 trendingDays?: number, 989 trendingDays?: number,
978 userId?: number 990 user?: UserModel
979 }, countVideos = true) { 991 }, countVideos = true) {
980 if (options.filter && options.filter === 'all-local' && !options.userId) { 992 if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
981 throw new Error('Try to filter all-local but no userId is provided') 993 throw new Error('Try to filter all-local but no user has not the see all videos right')
982 } 994 }
983 995
984 const query: IFindOptions<VideoModel> = { 996 const query: IFindOptions<VideoModel> = {
@@ -994,11 +1006,14 @@ export class VideoModel extends Model<VideoModel> {
994 query.group = 'VideoModel.id' 1006 query.group = 'VideoModel.id'
995 } 1007 }
996 1008
1009 const serverActor = await getServerActor()
1010
997 // actorId === null has a meaning, so just check undefined 1011 // actorId === null has a meaning, so just check undefined
998 const actorId = options.actorId !== undefined ? options.actorId : (await getServerActor()).id 1012 const actorId = options.actorId !== undefined ? options.actorId : serverActor.id
999 1013
1000 const queryOptions = { 1014 const queryOptions = {
1001 actorId, 1015 actorId,
1016 serverAccountId: serverActor.Account.id,
1002 nsfw: options.nsfw, 1017 nsfw: options.nsfw,
1003 categoryOneOf: options.categoryOneOf, 1018 categoryOneOf: options.categoryOneOf,
1004 licenceOneOf: options.licenceOneOf, 1019 licenceOneOf: options.licenceOneOf,
@@ -1010,7 +1025,7 @@ export class VideoModel extends Model<VideoModel> {
1010 accountId: options.accountId, 1025 accountId: options.accountId,
1011 videoChannelId: options.videoChannelId, 1026 videoChannelId: options.videoChannelId,
1012 includeLocalVideos: options.includeLocalVideos, 1027 includeLocalVideos: options.includeLocalVideos,
1013 userId: options.userId, 1028 user: options.user,
1014 trendingDays 1029 trendingDays
1015 } 1030 }
1016 1031
@@ -1033,7 +1048,7 @@ export class VideoModel extends Model<VideoModel> {
1033 tagsAllOf?: string[] 1048 tagsAllOf?: string[]
1034 durationMin?: number // seconds 1049 durationMin?: number // seconds
1035 durationMax?: number // seconds 1050 durationMax?: number // seconds
1036 userId?: number, 1051 user?: UserModel,
1037 filter?: VideoFilter 1052 filter?: VideoFilter
1038 }) { 1053 }) {
1039 const whereAnd = [] 1054 const whereAnd = []
@@ -1104,6 +1119,7 @@ export class VideoModel extends Model<VideoModel> {
1104 const serverActor = await getServerActor() 1119 const serverActor = await getServerActor()
1105 const queryOptions = { 1120 const queryOptions = {
1106 actorId: serverActor.id, 1121 actorId: serverActor.id,
1122 serverAccountId: serverActor.Account.id,
1107 includeLocalVideos: options.includeLocalVideos, 1123 includeLocalVideos: options.includeLocalVideos,
1108 nsfw: options.nsfw, 1124 nsfw: options.nsfw,
1109 categoryOneOf: options.categoryOneOf, 1125 categoryOneOf: options.categoryOneOf,
@@ -1111,7 +1127,7 @@ export class VideoModel extends Model<VideoModel> {
1111 languageOneOf: options.languageOneOf, 1127 languageOneOf: options.languageOneOf,
1112 tagsOneOf: options.tagsOneOf, 1128 tagsOneOf: options.tagsOneOf,
1113 tagsAllOf: options.tagsAllOf, 1129 tagsAllOf: options.tagsAllOf,
1114 userId: options.userId, 1130 user: options.user,
1115 filter: options.filter 1131 filter: options.filter
1116 } 1132 }
1117 1133
@@ -1287,7 +1303,7 @@ export class VideoModel extends Model<VideoModel> {
1287 1303
1288 private static async getAvailableForApi ( 1304 private static async getAvailableForApi (
1289 query: IFindOptions<VideoModel>, 1305 query: IFindOptions<VideoModel>,
1290 options: AvailableForListIDsOptions & { userId?: number}, 1306 options: AvailableForListIDsOptions,
1291 countVideos = true 1307 countVideos = true
1292 ) { 1308 ) {
1293 const idsScope = { 1309 const idsScope = {
@@ -1320,8 +1336,8 @@ export class VideoModel extends Model<VideoModel> {
1320 } 1336 }
1321 ] 1337 ]
1322 1338
1323 if (options.userId) { 1339 if (options.user) {
1324 apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.userId ] }) 1340 apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.user.id ] })
1325 } 1341 }
1326 1342
1327 const secondQuery = { 1343 const secondQuery = {