diff options
Diffstat (limited to 'server/models')
24 files changed, 322 insertions, 324 deletions
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index efd6ed59e..d5746ad76 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -8,22 +8,22 @@ enum ScopeNames { | |||
8 | WITH_ACCOUNTS = 'WITH_ACCOUNTS' | 8 | WITH_ACCOUNTS = 'WITH_ACCOUNTS' |
9 | } | 9 | } |
10 | 10 | ||
11 | @Scopes({ | 11 | @Scopes(() => ({ |
12 | [ScopeNames.WITH_ACCOUNTS]: { | 12 | [ScopeNames.WITH_ACCOUNTS]: { |
13 | include: [ | 13 | include: [ |
14 | { | 14 | { |
15 | model: () => AccountModel, | 15 | model: AccountModel, |
16 | required: true, | 16 | required: true, |
17 | as: 'ByAccount' | 17 | as: 'ByAccount' |
18 | }, | 18 | }, |
19 | { | 19 | { |
20 | model: () => AccountModel, | 20 | model: AccountModel, |
21 | required: true, | 21 | required: true, |
22 | as: 'BlockedAccount' | 22 | as: 'BlockedAccount' |
23 | } | 23 | } |
24 | ] | 24 | ] |
25 | } | 25 | } |
26 | }) | 26 | })) |
27 | 27 | ||
28 | @Table({ | 28 | @Table({ |
29 | tableName: 'accountBlocklist', | 29 | tableName: 'accountBlocklist', |
@@ -83,7 +83,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> { | |||
83 | attributes: [ 'accountId', 'id' ], | 83 | attributes: [ 'accountId', 'id' ], |
84 | where: { | 84 | where: { |
85 | accountId: { | 85 | accountId: { |
86 | [Op.any]: accountIds | 86 | [Op.in]: accountIds // FIXME: sequelize ANY seems broken |
87 | }, | 87 | }, |
88 | targetAccountId | 88 | targetAccountId |
89 | }, | 89 | }, |
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index bf2ed0a61..c53312990 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -33,15 +33,15 @@ export enum ScopeNames { | |||
33 | SUMMARY = 'SUMMARY' | 33 | SUMMARY = 'SUMMARY' |
34 | } | 34 | } |
35 | 35 | ||
36 | @DefaultScope({ | 36 | @DefaultScope(() => ({ |
37 | include: [ | 37 | include: [ |
38 | { | 38 | { |
39 | model: () => ActorModel, // Default scope includes avatar and server | 39 | model: ActorModel, // Default scope includes avatar and server |
40 | required: true | 40 | required: true |
41 | } | 41 | } |
42 | ] | 42 | ] |
43 | }) | 43 | })) |
44 | @Scopes({ | 44 | @Scopes(() => ({ |
45 | [ ScopeNames.SUMMARY ]: (whereActor?: WhereOptions) => { | 45 | [ ScopeNames.SUMMARY ]: (whereActor?: WhereOptions) => { |
46 | return { | 46 | return { |
47 | attributes: [ 'id', 'name' ], | 47 | attributes: [ 'id', 'name' ], |
@@ -66,7 +66,7 @@ export enum ScopeNames { | |||
66 | ] | 66 | ] |
67 | } | 67 | } |
68 | } | 68 | } |
69 | }) | 69 | })) |
70 | @Table({ | 70 | @Table({ |
71 | tableName: 'account', | 71 | tableName: 'account', |
72 | indexes: [ | 72 | indexes: [ |
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index 08388f268..a4f97037b 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts | |||
@@ -6,7 +6,7 @@ import { isUserNotificationTypeValid } from '../../helpers/custom-validators/use | |||
6 | import { UserModel } from './user' | 6 | import { UserModel } from './user' |
7 | import { VideoModel } from '../video/video' | 7 | import { VideoModel } from '../video/video' |
8 | import { VideoCommentModel } from '../video/video-comment' | 8 | import { VideoCommentModel } from '../video/video-comment' |
9 | import { FindOptions, Op } from 'sequelize' | 9 | import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' |
10 | import { VideoChannelModel } from '../video/video-channel' | 10 | import { VideoChannelModel } from '../video/video-channel' |
11 | import { AccountModel } from './account' | 11 | import { AccountModel } from './account' |
12 | import { VideoAbuseModel } from '../video/video-abuse' | 12 | import { VideoAbuseModel } from '../video/video-abuse' |
@@ -24,17 +24,17 @@ enum ScopeNames { | |||
24 | function buildActorWithAvatarInclude () { | 24 | function buildActorWithAvatarInclude () { |
25 | return { | 25 | return { |
26 | attributes: [ 'preferredUsername' ], | 26 | attributes: [ 'preferredUsername' ], |
27 | model: () => ActorModel.unscoped(), | 27 | model: ActorModel.unscoped(), |
28 | required: true, | 28 | required: true, |
29 | include: [ | 29 | include: [ |
30 | { | 30 | { |
31 | attributes: [ 'filename' ], | 31 | attributes: [ 'filename' ], |
32 | model: () => AvatarModel.unscoped(), | 32 | model: AvatarModel.unscoped(), |
33 | required: false | 33 | required: false |
34 | }, | 34 | }, |
35 | { | 35 | { |
36 | attributes: [ 'host' ], | 36 | attributes: [ 'host' ], |
37 | model: () => ServerModel.unscoped(), | 37 | model: ServerModel.unscoped(), |
38 | required: false | 38 | required: false |
39 | } | 39 | } |
40 | ] | 40 | ] |
@@ -44,7 +44,7 @@ function buildActorWithAvatarInclude () { | |||
44 | function buildVideoInclude (required: boolean) { | 44 | function buildVideoInclude (required: boolean) { |
45 | return { | 45 | return { |
46 | attributes: [ 'id', 'uuid', 'name' ], | 46 | attributes: [ 'id', 'uuid', 'name' ], |
47 | model: () => VideoModel.unscoped(), | 47 | model: VideoModel.unscoped(), |
48 | required | 48 | required |
49 | } | 49 | } |
50 | } | 50 | } |
@@ -53,7 +53,7 @@ function buildChannelInclude (required: boolean, withActor = false) { | |||
53 | return { | 53 | return { |
54 | required, | 54 | required, |
55 | attributes: [ 'id', 'name' ], | 55 | attributes: [ 'id', 'name' ], |
56 | model: () => VideoChannelModel.unscoped(), | 56 | model: VideoChannelModel.unscoped(), |
57 | include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] | 57 | include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] |
58 | } | 58 | } |
59 | } | 59 | } |
@@ -62,12 +62,12 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
62 | return { | 62 | return { |
63 | required, | 63 | required, |
64 | attributes: [ 'id', 'name' ], | 64 | attributes: [ 'id', 'name' ], |
65 | model: () => AccountModel.unscoped(), | 65 | model: AccountModel.unscoped(), |
66 | include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] | 66 | include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] |
67 | } | 67 | } |
68 | } | 68 | } |
69 | 69 | ||
70 | @Scopes({ | 70 | @Scopes(() => ({ |
71 | [ScopeNames.WITH_ALL]: { | 71 | [ScopeNames.WITH_ALL]: { |
72 | include: [ | 72 | include: [ |
73 | Object.assign(buildVideoInclude(false), { | 73 | Object.assign(buildVideoInclude(false), { |
@@ -76,7 +76,7 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
76 | 76 | ||
77 | { | 77 | { |
78 | attributes: [ 'id', 'originCommentId' ], | 78 | attributes: [ 'id', 'originCommentId' ], |
79 | model: () => VideoCommentModel.unscoped(), | 79 | model: VideoCommentModel.unscoped(), |
80 | required: false, | 80 | required: false, |
81 | include: [ | 81 | include: [ |
82 | buildAccountInclude(true, true), | 82 | buildAccountInclude(true, true), |
@@ -86,56 +86,56 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
86 | 86 | ||
87 | { | 87 | { |
88 | attributes: [ 'id' ], | 88 | attributes: [ 'id' ], |
89 | model: () => VideoAbuseModel.unscoped(), | 89 | model: VideoAbuseModel.unscoped(), |
90 | required: false, | 90 | required: false, |
91 | include: [ buildVideoInclude(true) ] | 91 | include: [ buildVideoInclude(true) ] |
92 | }, | 92 | }, |
93 | 93 | ||
94 | { | 94 | { |
95 | attributes: [ 'id' ], | 95 | attributes: [ 'id' ], |
96 | model: () => VideoBlacklistModel.unscoped(), | 96 | model: VideoBlacklistModel.unscoped(), |
97 | required: false, | 97 | required: false, |
98 | include: [ buildVideoInclude(true) ] | 98 | include: [ buildVideoInclude(true) ] |
99 | }, | 99 | }, |
100 | 100 | ||
101 | { | 101 | { |
102 | attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ], | 102 | attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ], |
103 | model: () => VideoImportModel.unscoped(), | 103 | model: VideoImportModel.unscoped(), |
104 | required: false, | 104 | required: false, |
105 | include: [ buildVideoInclude(false) ] | 105 | include: [ buildVideoInclude(false) ] |
106 | }, | 106 | }, |
107 | 107 | ||
108 | { | 108 | { |
109 | attributes: [ 'id', 'state' ], | 109 | attributes: [ 'id', 'state' ], |
110 | model: () => ActorFollowModel.unscoped(), | 110 | model: ActorFollowModel.unscoped(), |
111 | required: false, | 111 | required: false, |
112 | include: [ | 112 | include: [ |
113 | { | 113 | { |
114 | attributes: [ 'preferredUsername' ], | 114 | attributes: [ 'preferredUsername' ], |
115 | model: () => ActorModel.unscoped(), | 115 | model: ActorModel.unscoped(), |
116 | required: true, | 116 | required: true, |
117 | as: 'ActorFollower', | 117 | as: 'ActorFollower', |
118 | include: [ | 118 | include: [ |
119 | { | 119 | { |
120 | attributes: [ 'id', 'name' ], | 120 | attributes: [ 'id', 'name' ], |
121 | model: () => AccountModel.unscoped(), | 121 | model: AccountModel.unscoped(), |
122 | required: true | 122 | required: true |
123 | }, | 123 | }, |
124 | { | 124 | { |
125 | attributes: [ 'filename' ], | 125 | attributes: [ 'filename' ], |
126 | model: () => AvatarModel.unscoped(), | 126 | model: AvatarModel.unscoped(), |
127 | required: false | 127 | required: false |
128 | }, | 128 | }, |
129 | { | 129 | { |
130 | attributes: [ 'host' ], | 130 | attributes: [ 'host' ], |
131 | model: () => ServerModel.unscoped(), | 131 | model: ServerModel.unscoped(), |
132 | required: false | 132 | required: false |
133 | } | 133 | } |
134 | ] | 134 | ] |
135 | }, | 135 | }, |
136 | { | 136 | { |
137 | attributes: [ 'preferredUsername' ], | 137 | attributes: [ 'preferredUsername' ], |
138 | model: () => ActorModel.unscoped(), | 138 | model: ActorModel.unscoped(), |
139 | required: true, | 139 | required: true, |
140 | as: 'ActorFollowing', | 140 | as: 'ActorFollowing', |
141 | include: [ | 141 | include: [ |
@@ -147,9 +147,9 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
147 | }, | 147 | }, |
148 | 148 | ||
149 | buildAccountInclude(false, true) | 149 | buildAccountInclude(false, true) |
150 | ] as any // FIXME: sequelize typings | 150 | ] |
151 | } | 151 | } |
152 | }) | 152 | })) |
153 | @Table({ | 153 | @Table({ |
154 | tableName: 'userNotification', | 154 | tableName: 'userNotification', |
155 | indexes: [ | 155 | indexes: [ |
@@ -212,7 +212,7 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
212 | } | 212 | } |
213 | } | 213 | } |
214 | } | 214 | } |
215 | ] as any // FIXME: sequelize typings | 215 | ] as (ModelIndexesOptions & { where?: WhereOptions })[] |
216 | }) | 216 | }) |
217 | export class UserNotificationModel extends Model<UserNotificationModel> { | 217 | export class UserNotificationModel extends Model<UserNotificationModel> { |
218 | 218 | ||
@@ -357,7 +357,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
357 | where: { | 357 | where: { |
358 | userId, | 358 | userId, |
359 | id: { | 359 | id: { |
360 | [Op.any]: notificationIds | 360 | [Op.in]: notificationIds // FIXME: sequelize ANY seems broken |
361 | } | 361 | } |
362 | } | 362 | } |
363 | } | 363 | } |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 8bd0397dd..4a9acd703 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import { FindOptions, literal, Op, QueryTypes } from 'sequelize' |
2 | import { | 2 | import { |
3 | AfterDestroy, | 3 | AfterDestroy, |
4 | AfterUpdate, | 4 | AfterUpdate, |
@@ -56,33 +56,33 @@ enum ScopeNames { | |||
56 | WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' | 56 | WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL' |
57 | } | 57 | } |
58 | 58 | ||
59 | @DefaultScope({ | 59 | @DefaultScope(() => ({ |
60 | include: [ | 60 | include: [ |
61 | { | 61 | { |
62 | model: () => AccountModel, | 62 | model: AccountModel, |
63 | required: true | 63 | required: true |
64 | }, | 64 | }, |
65 | { | 65 | { |
66 | model: () => UserNotificationSettingModel, | 66 | model: UserNotificationSettingModel, |
67 | required: true | 67 | required: true |
68 | } | 68 | } |
69 | ] | 69 | ] |
70 | }) | 70 | })) |
71 | @Scopes({ | 71 | @Scopes(() => ({ |
72 | [ScopeNames.WITH_VIDEO_CHANNEL]: { | 72 | [ScopeNames.WITH_VIDEO_CHANNEL]: { |
73 | include: [ | 73 | include: [ |
74 | { | 74 | { |
75 | model: () => AccountModel, | 75 | model: AccountModel, |
76 | required: true, | 76 | required: true, |
77 | include: [ () => VideoChannelModel ] | 77 | include: [ VideoChannelModel ] |
78 | }, | 78 | }, |
79 | { | 79 | { |
80 | model: () => UserNotificationSettingModel, | 80 | model: UserNotificationSettingModel, |
81 | required: true | 81 | required: true |
82 | } | 82 | } |
83 | ] as any // FIXME: sequelize typings | 83 | ] |
84 | } | 84 | } |
85 | }) | 85 | })) |
86 | @Table({ | 86 | @Table({ |
87 | tableName: 'user', | 87 | tableName: 'user', |
88 | indexes: [ | 88 | indexes: [ |
@@ -233,26 +233,26 @@ export class UserModel extends Model<UserModel> { | |||
233 | let where = undefined | 233 | let where = undefined |
234 | if (search) { | 234 | if (search) { |
235 | where = { | 235 | where = { |
236 | [Sequelize.Op.or]: [ | 236 | [Op.or]: [ |
237 | { | 237 | { |
238 | email: { | 238 | email: { |
239 | [Sequelize.Op.iLike]: '%' + search + '%' | 239 | [Op.iLike]: '%' + search + '%' |
240 | } | 240 | } |
241 | }, | 241 | }, |
242 | { | 242 | { |
243 | username: { | 243 | username: { |
244 | [ Sequelize.Op.iLike ]: '%' + search + '%' | 244 | [ Op.iLike ]: '%' + search + '%' |
245 | } | 245 | } |
246 | } | 246 | } |
247 | ] | 247 | ] |
248 | } | 248 | } |
249 | } | 249 | } |
250 | 250 | ||
251 | const query = { | 251 | const query: FindOptions = { |
252 | attributes: { | 252 | attributes: { |
253 | include: [ | 253 | include: [ |
254 | [ | 254 | [ |
255 | Sequelize.literal( | 255 | literal( |
256 | '(' + | 256 | '(' + |
257 | 'SELECT COALESCE(SUM("size"), 0) ' + | 257 | 'SELECT COALESCE(SUM("size"), 0) ' + |
258 | 'FROM (' + | 258 | 'FROM (' + |
@@ -265,7 +265,7 @@ export class UserModel extends Model<UserModel> { | |||
265 | ')' | 265 | ')' |
266 | ), | 266 | ), |
267 | 'videoQuotaUsed' | 267 | 'videoQuotaUsed' |
268 | ] as any // FIXME: typings | 268 | ] |
269 | ] | 269 | ] |
270 | }, | 270 | }, |
271 | offset: start, | 271 | offset: start, |
@@ -291,7 +291,7 @@ export class UserModel extends Model<UserModel> { | |||
291 | const query = { | 291 | const query = { |
292 | where: { | 292 | where: { |
293 | role: { | 293 | role: { |
294 | [Sequelize.Op.in]: roles | 294 | [Op.in]: roles |
295 | } | 295 | } |
296 | } | 296 | } |
297 | } | 297 | } |
@@ -387,7 +387,7 @@ export class UserModel extends Model<UserModel> { | |||
387 | 387 | ||
388 | const query = { | 388 | const query = { |
389 | where: { | 389 | where: { |
390 | [ Sequelize.Op.or ]: [ { username }, { email } ] | 390 | [ Op.or ]: [ { username }, { email } ] |
391 | } | 391 | } |
392 | } | 392 | } |
393 | 393 | ||
@@ -510,7 +510,7 @@ export class UserModel extends Model<UserModel> { | |||
510 | const query = { | 510 | const query = { |
511 | where: { | 511 | where: { |
512 | username: { | 512 | username: { |
513 | [ Sequelize.Op.like ]: `%${search}%` | 513 | [ Op.like ]: `%${search}%` |
514 | } | 514 | } |
515 | }, | 515 | }, |
516 | limit: 10 | 516 | limit: 10 |
@@ -591,15 +591,11 @@ export class UserModel extends Model<UserModel> { | |||
591 | 591 | ||
592 | const uploadedTotal = videoFile.size + totalBytes | 592 | const uploadedTotal = videoFile.size + totalBytes |
593 | const uploadedDaily = videoFile.size + totalBytesDaily | 593 | const uploadedDaily = videoFile.size + totalBytesDaily |
594 | if (this.videoQuotaDaily === -1) { | ||
595 | return uploadedTotal < this.videoQuota | ||
596 | } | ||
597 | if (this.videoQuota === -1) { | ||
598 | return uploadedDaily < this.videoQuotaDaily | ||
599 | } | ||
600 | 594 | ||
601 | return (uploadedTotal < this.videoQuota) && | 595 | if (this.videoQuotaDaily === -1) return uploadedTotal < this.videoQuota |
602 | (uploadedDaily < this.videoQuotaDaily) | 596 | if (this.videoQuota === -1) return uploadedDaily < this.videoQuotaDaily |
597 | |||
598 | return uploadedTotal < this.videoQuota && uploadedDaily < this.videoQuotaDaily | ||
603 | } | 599 | } |
604 | 600 | ||
605 | private static generateUserQuotaBaseSQL (where?: string) { | 601 | private static generateUserQuotaBaseSQL (where?: string) { |
@@ -619,14 +615,14 @@ export class UserModel extends Model<UserModel> { | |||
619 | private static getTotalRawQuery (query: string, userId: number) { | 615 | private static getTotalRawQuery (query: string, userId: number) { |
620 | const options = { | 616 | const options = { |
621 | bind: { userId }, | 617 | bind: { userId }, |
622 | type: Sequelize.QueryTypes.SELECT as Sequelize.QueryTypes.SELECT | 618 | type: QueryTypes.SELECT as QueryTypes.SELECT |
623 | } | 619 | } |
624 | 620 | ||
625 | return UserModel.sequelize.query<{ total: number }>(query, options) | 621 | return UserModel.sequelize.query<{ total: string }>(query, options) |
626 | .then(([ { total } ]) => { | 622 | .then(([ { total } ]) => { |
627 | if (total === null) return 0 | 623 | if (total === null) return 0 |
628 | 624 | ||
629 | return parseInt(total + '', 10) | 625 | return parseInt(total, 10) |
630 | }) | 626 | }) |
631 | } | 627 | } |
632 | } | 628 | } |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 1ebee8df5..4a466441c 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -56,46 +56,46 @@ export const unusedActorAttributesForAPI = [ | |||
56 | 'updatedAt' | 56 | 'updatedAt' |
57 | ] | 57 | ] |
58 | 58 | ||
59 | @DefaultScope({ | 59 | @DefaultScope(() => ({ |
60 | include: [ | 60 | include: [ |
61 | { | 61 | { |
62 | model: () => ServerModel, | 62 | model: ServerModel, |
63 | required: false | 63 | required: false |
64 | }, | 64 | }, |
65 | { | 65 | { |
66 | model: () => AvatarModel, | 66 | model: AvatarModel, |
67 | required: false | 67 | required: false |
68 | } | 68 | } |
69 | ] | 69 | ] |
70 | }) | 70 | })) |
71 | @Scopes({ | 71 | @Scopes(() => ({ |
72 | [ScopeNames.FULL]: { | 72 | [ScopeNames.FULL]: { |
73 | include: [ | 73 | include: [ |
74 | { | 74 | { |
75 | model: () => AccountModel.unscoped(), | 75 | model: AccountModel.unscoped(), |
76 | required: false | 76 | required: false |
77 | }, | 77 | }, |
78 | { | 78 | { |
79 | model: () => VideoChannelModel.unscoped(), | 79 | model: VideoChannelModel.unscoped(), |
80 | required: false, | 80 | required: false, |
81 | include: [ | 81 | include: [ |
82 | { | 82 | { |
83 | model: () => AccountModel, | 83 | model: AccountModel, |
84 | required: true | 84 | required: true |
85 | } | 85 | } |
86 | ] | 86 | ] |
87 | }, | 87 | }, |
88 | { | 88 | { |
89 | model: () => ServerModel, | 89 | model: ServerModel, |
90 | required: false | 90 | required: false |
91 | }, | 91 | }, |
92 | { | 92 | { |
93 | model: () => AvatarModel, | 93 | model: AvatarModel, |
94 | required: false | 94 | required: false |
95 | } | 95 | } |
96 | ] as any // FIXME: sequelize typings | 96 | ] |
97 | } | 97 | } |
98 | }) | 98 | })) |
99 | @Table({ | 99 | @Table({ |
100 | tableName: 'actor', | 100 | tableName: 'actor', |
101 | indexes: [ | 101 | indexes: [ |
@@ -131,7 +131,7 @@ export const unusedActorAttributesForAPI = [ | |||
131 | export class ActorModel extends Model<ActorModel> { | 131 | export class ActorModel extends Model<ActorModel> { |
132 | 132 | ||
133 | @AllowNull(false) | 133 | @AllowNull(false) |
134 | @Column({ type: DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES)) }) // FIXME: sequelize typings | 134 | @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) |
135 | type: ActivityPubActorType | 135 | type: ActivityPubActorType |
136 | 136 | ||
137 | @AllowNull(false) | 137 | @AllowNull(false) |
@@ -280,14 +280,16 @@ export class ActorModel extends Model<ActorModel> { | |||
280 | attributes: [ 'id' ], | 280 | attributes: [ 'id' ], |
281 | model: VideoChannelModel.unscoped(), | 281 | model: VideoChannelModel.unscoped(), |
282 | required: true, | 282 | required: true, |
283 | include: { | 283 | include: [ |
284 | attributes: [ 'id' ], | 284 | { |
285 | model: VideoModel.unscoped(), | 285 | attributes: [ 'id' ], |
286 | required: true, | 286 | model: VideoModel.unscoped(), |
287 | where: { | 287 | required: true, |
288 | id: videoId | 288 | where: { |
289 | id: videoId | ||
290 | } | ||
289 | } | 291 | } |
290 | } | 292 | ] |
291 | } | 293 | } |
292 | ] | 294 | ] |
293 | } | 295 | } |
@@ -295,7 +297,7 @@ export class ActorModel extends Model<ActorModel> { | |||
295 | transaction | 297 | transaction |
296 | } | 298 | } |
297 | 299 | ||
298 | return ActorModel.unscoped().findOne(query as any) // FIXME: typings | 300 | return ActorModel.unscoped().findOne(query) |
299 | } | 301 | } |
300 | 302 | ||
301 | static isActorUrlExist (url: string) { | 303 | static isActorUrlExist (url: string) { |
@@ -389,8 +391,7 @@ export class ActorModel extends Model<ActorModel> { | |||
389 | } | 391 | } |
390 | 392 | ||
391 | static incrementFollows (id: number, column: 'followersCount' | 'followingCount', by: number) { | 393 | static incrementFollows (id: number, column: 'followersCount' | 'followingCount', by: number) { |
392 | // FIXME: typings | 394 | return ActorModel.increment(column, { |
393 | return (ActorModel as any).increment(column, { | ||
394 | by, | 395 | by, |
395 | where: { | 396 | where: { |
396 | id | 397 | id |
diff --git a/server/models/application/application.ts b/server/models/application/application.ts index 854a5fb36..a02208b4e 100644 --- a/server/models/application/application.ts +++ b/server/models/application/application.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' | 1 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' |
2 | import { AccountModel } from '../account/account' | 2 | import { AccountModel } from '../account/account' |
3 | 3 | ||
4 | @DefaultScope({ | 4 | @DefaultScope(() => ({ |
5 | include: [ | 5 | include: [ |
6 | { | 6 | { |
7 | model: () => AccountModel, | 7 | model: AccountModel, |
8 | required: true | 8 | required: true |
9 | } | 9 | } |
10 | ] | 10 | ] |
11 | }) | 11 | })) |
12 | @Table({ | 12 | @Table({ |
13 | tableName: 'application' | 13 | tableName: 'application' |
14 | }) | 14 | }) |
diff --git a/server/models/oauth/oauth-client.ts b/server/models/oauth/oauth-client.ts index b4a841edd..42c59bb79 100644 --- a/server/models/oauth/oauth-client.ts +++ b/server/models/oauth/oauth-client.ts | |||
@@ -24,10 +24,10 @@ export class OAuthClientModel extends Model<OAuthClientModel> { | |||
24 | @Column | 24 | @Column |
25 | clientSecret: string | 25 | clientSecret: string |
26 | 26 | ||
27 | @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: sequelize typings | 27 | @Column(DataType.ARRAY(DataType.STRING)) |
28 | grants: string[] | 28 | grants: string[] |
29 | 29 | ||
30 | @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: sequelize typings | 30 | @Column(DataType.ARRAY(DataType.STRING)) |
31 | redirectUris: string[] | 31 | redirectUris: string[] |
32 | 32 | ||
33 | @CreatedAt | 33 | @CreatedAt |
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts index 3f41ee63b..903d551df 100644 --- a/server/models/oauth/oauth-token.ts +++ b/server/models/oauth/oauth-token.ts | |||
@@ -34,30 +34,30 @@ enum ScopeNames { | |||
34 | WITH_USER = 'WITH_USER' | 34 | WITH_USER = 'WITH_USER' |
35 | } | 35 | } |
36 | 36 | ||
37 | @Scopes({ | 37 | @Scopes(() => ({ |
38 | [ScopeNames.WITH_USER]: { | 38 | [ScopeNames.WITH_USER]: { |
39 | include: [ | 39 | include: [ |
40 | { | 40 | { |
41 | model: () => UserModel.unscoped(), | 41 | model: UserModel.unscoped(), |
42 | required: true, | 42 | required: true, |
43 | include: [ | 43 | include: [ |
44 | { | 44 | { |
45 | attributes: [ 'id' ], | 45 | attributes: [ 'id' ], |
46 | model: () => AccountModel.unscoped(), | 46 | model: AccountModel.unscoped(), |
47 | required: true, | 47 | required: true, |
48 | include: [ | 48 | include: [ |
49 | { | 49 | { |
50 | attributes: [ 'id', 'url' ], | 50 | attributes: [ 'id', 'url' ], |
51 | model: () => ActorModel.unscoped(), | 51 | model: ActorModel.unscoped(), |
52 | required: true | 52 | required: true |
53 | } | 53 | } |
54 | ] | 54 | ] |
55 | } | 55 | } |
56 | ] | 56 | ] |
57 | } | 57 | } |
58 | ] as any // FIXME: sequelize typings | 58 | ] |
59 | } | 59 | } |
60 | }) | 60 | })) |
61 | @Table({ | 61 | @Table({ |
62 | tableName: 'oAuthToken', | 62 | tableName: 'oAuthToken', |
63 | indexes: [ | 63 | indexes: [ |
@@ -167,11 +167,13 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> { | |||
167 | } | 167 | } |
168 | } | 168 | } |
169 | 169 | ||
170 | return OAuthTokenModel.scope(ScopeNames.WITH_USER).findOne(query).then(token => { | 170 | return OAuthTokenModel.scope(ScopeNames.WITH_USER) |
171 | if (token) token['user'] = token.User | 171 | .findOne(query) |
172 | .then(token => { | ||
173 | if (token) token[ 'user' ] = token.User | ||
172 | 174 | ||
173 | return token | 175 | return token |
174 | }) | 176 | }) |
175 | } | 177 | } |
176 | 178 | ||
177 | static getByRefreshTokenAndPopulateUser (refreshToken: string) { | 179 | static getByRefreshTokenAndPopulateUser (refreshToken: string) { |
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index cbeaa662b..eb2222256 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -13,7 +13,7 @@ import { | |||
13 | UpdatedAt | 13 | UpdatedAt |
14 | } from 'sequelize-typescript' | 14 | } from 'sequelize-typescript' |
15 | import { ActorModel } from '../activitypub/actor' | 15 | import { ActorModel } from '../activitypub/actor' |
16 | import { getVideoSort, throwIfNotValid } from '../utils' | 16 | import { getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' |
17 | import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 17 | import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
18 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' | 18 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' |
19 | import { VideoFileModel } from '../video/video-file' | 19 | import { VideoFileModel } from '../video/video-file' |
@@ -27,7 +27,7 @@ import { ServerModel } from '../server/server' | |||
27 | import { sample } from 'lodash' | 27 | import { sample } from 'lodash' |
28 | import { isTestInstance } from '../../helpers/core-utils' | 28 | import { isTestInstance } from '../../helpers/core-utils' |
29 | import * as Bluebird from 'bluebird' | 29 | import * as Bluebird from 'bluebird' |
30 | import * as Sequelize from 'sequelize' | 30 | import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize' |
31 | import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' | 31 | import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' |
32 | import { CONFIG } from '../../initializers/config' | 32 | import { CONFIG } from '../../initializers/config' |
33 | 33 | ||
@@ -35,32 +35,32 @@ export enum ScopeNames { | |||
35 | WITH_VIDEO = 'WITH_VIDEO' | 35 | WITH_VIDEO = 'WITH_VIDEO' |
36 | } | 36 | } |
37 | 37 | ||
38 | @Scopes({ | 38 | @Scopes(() => ({ |
39 | [ ScopeNames.WITH_VIDEO ]: { | 39 | [ ScopeNames.WITH_VIDEO ]: { |
40 | include: [ | 40 | include: [ |
41 | { | 41 | { |
42 | model: () => VideoFileModel, | 42 | model: VideoFileModel, |
43 | required: false, | 43 | required: false, |
44 | include: [ | 44 | include: [ |
45 | { | 45 | { |
46 | model: () => VideoModel, | 46 | model: VideoModel, |
47 | required: true | 47 | required: true |
48 | } | 48 | } |
49 | ] | 49 | ] |
50 | }, | 50 | }, |
51 | { | 51 | { |
52 | model: () => VideoStreamingPlaylistModel, | 52 | model: VideoStreamingPlaylistModel, |
53 | required: false, | 53 | required: false, |
54 | include: [ | 54 | include: [ |
55 | { | 55 | { |
56 | model: () => VideoModel, | 56 | model: VideoModel, |
57 | required: true | 57 | required: true |
58 | } | 58 | } |
59 | ] | 59 | ] |
60 | } | 60 | } |
61 | ] as any // FIXME: sequelize typings | 61 | ] |
62 | } | 62 | } |
63 | }) | 63 | })) |
64 | 64 | ||
65 | @Table({ | 65 | @Table({ |
66 | tableName: 'videoRedundancy', | 66 | tableName: 'videoRedundancy', |
@@ -192,7 +192,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
192 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) | 192 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) |
193 | } | 193 | } |
194 | 194 | ||
195 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { | 195 | static loadByUrl (url: string, transaction?: Transaction) { |
196 | const query = { | 196 | const query = { |
197 | where: { | 197 | where: { |
198 | url | 198 | url |
@@ -292,7 +292,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
292 | where: { | 292 | where: { |
293 | privacy: VideoPrivacy.PUBLIC, | 293 | privacy: VideoPrivacy.PUBLIC, |
294 | views: { | 294 | views: { |
295 | [ Sequelize.Op.gte ]: minViews | 295 | [ Op.gte ]: minViews |
296 | } | 296 | } |
297 | }, | 297 | }, |
298 | include: [ | 298 | include: [ |
@@ -315,7 +315,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
315 | actorId: actor.id, | 315 | actorId: actor.id, |
316 | strategy, | 316 | strategy, |
317 | createdAt: { | 317 | createdAt: { |
318 | [ Sequelize.Op.lt ]: expiredDate | 318 | [ Op.lt ]: expiredDate |
319 | } | 319 | } |
320 | } | 320 | } |
321 | } | 321 | } |
@@ -326,7 +326,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
326 | static async getTotalDuplicated (strategy: VideoRedundancyStrategy) { | 326 | static async getTotalDuplicated (strategy: VideoRedundancyStrategy) { |
327 | const actor = await getServerActor() | 327 | const actor = await getServerActor() |
328 | 328 | ||
329 | const options = { | 329 | const query: FindOptions = { |
330 | include: [ | 330 | include: [ |
331 | { | 331 | { |
332 | attributes: [], | 332 | attributes: [], |
@@ -340,12 +340,8 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
340 | ] | 340 | ] |
341 | } | 341 | } |
342 | 342 | ||
343 | return VideoFileModel.sum('size', options as any) // FIXME: typings | 343 | return VideoFileModel.aggregate('size', 'SUM', query) |
344 | .then(v => { | 344 | .then(result => parseAggregateResult(result)) |
345 | if (!v || isNaN(v)) return 0 | ||
346 | |||
347 | return v | ||
348 | }) | ||
349 | } | 345 | } |
350 | 346 | ||
351 | static async listLocalExpired () { | 347 | static async listLocalExpired () { |
@@ -355,7 +351,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
355 | where: { | 351 | where: { |
356 | actorId: actor.id, | 352 | actorId: actor.id, |
357 | expiresOn: { | 353 | expiresOn: { |
358 | [ Sequelize.Op.lt ]: new Date() | 354 | [ Op.lt ]: new Date() |
359 | } | 355 | } |
360 | } | 356 | } |
361 | } | 357 | } |
@@ -369,10 +365,10 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
369 | const query = { | 365 | const query = { |
370 | where: { | 366 | where: { |
371 | actorId: { | 367 | actorId: { |
372 | [Sequelize.Op.ne]: actor.id | 368 | [Op.ne]: actor.id |
373 | }, | 369 | }, |
374 | expiresOn: { | 370 | expiresOn: { |
375 | [ Sequelize.Op.lt ]: new Date() | 371 | [ Op.lt ]: new Date() |
376 | } | 372 | } |
377 | } | 373 | } |
378 | } | 374 | } |
@@ -428,12 +424,12 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
428 | static async getStats (strategy: VideoRedundancyStrategy) { | 424 | static async getStats (strategy: VideoRedundancyStrategy) { |
429 | const actor = await getServerActor() | 425 | const actor = await getServerActor() |
430 | 426 | ||
431 | const query = { | 427 | const query: FindOptions = { |
432 | raw: true, | 428 | raw: true, |
433 | attributes: [ | 429 | attributes: [ |
434 | [ Sequelize.fn('COALESCE', Sequelize.fn('SUM', Sequelize.col('VideoFile.size')), '0'), 'totalUsed' ], | 430 | [ fn('COALESCE', fn('SUM', col('VideoFile.size')), '0'), 'totalUsed' ], |
435 | [ Sequelize.fn('COUNT', Sequelize.fn('DISTINCT', Sequelize.col('videoId'))), 'totalVideos' ], | 431 | [ fn('COUNT', fn('DISTINCT', col('videoId'))), 'totalVideos' ], |
436 | [ Sequelize.fn('COUNT', Sequelize.col('videoFileId')), 'totalVideoFiles' ] | 432 | [ fn('COUNT', col('videoFileId')), 'totalVideoFiles' ] |
437 | ], | 433 | ], |
438 | where: { | 434 | where: { |
439 | strategy, | 435 | strategy, |
@@ -448,9 +444,9 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
448 | ] | 444 | ] |
449 | } | 445 | } |
450 | 446 | ||
451 | return VideoRedundancyModel.findOne(query as any) // FIXME: typings | 447 | return VideoRedundancyModel.findOne(query) |
452 | .then((r: any) => ({ | 448 | .then((r: any) => ({ |
453 | totalUsed: parseInt(r.totalUsed.toString(), 10), | 449 | totalUsed: parseAggregateResult(r.totalUsed), |
454 | totalVideos: r.totalVideos, | 450 | totalVideos: r.totalVideos, |
455 | totalVideoFiles: r.totalVideoFiles | 451 | totalVideoFiles: r.totalVideoFiles |
456 | })) | 452 | })) |
@@ -503,7 +499,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
503 | private static async buildVideoFileForDuplication () { | 499 | private static async buildVideoFileForDuplication () { |
504 | const actor = await getServerActor() | 500 | const actor = await getServerActor() |
505 | 501 | ||
506 | const notIn = Sequelize.literal( | 502 | const notIn = literal( |
507 | '(' + | 503 | '(' + |
508 | `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` + | 504 | `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` + |
509 | ')' | 505 | ')' |
@@ -515,7 +511,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
515 | required: true, | 511 | required: true, |
516 | where: { | 512 | where: { |
517 | id: { | 513 | id: { |
518 | [ Sequelize.Op.notIn ]: notIn | 514 | [ Op.notIn ]: notIn |
519 | } | 515 | } |
520 | } | 516 | } |
521 | } | 517 | } |
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts index 450f27152..92c01f642 100644 --- a/server/models/server/server-blocklist.ts +++ b/server/models/server/server-blocklist.ts | |||
@@ -9,11 +9,11 @@ enum ScopeNames { | |||
9 | WITH_SERVER = 'WITH_SERVER' | 9 | WITH_SERVER = 'WITH_SERVER' |
10 | } | 10 | } |
11 | 11 | ||
12 | @Scopes({ | 12 | @Scopes(() => ({ |
13 | [ScopeNames.WITH_ACCOUNT]: { | 13 | [ScopeNames.WITH_ACCOUNT]: { |
14 | include: [ | 14 | include: [ |
15 | { | 15 | { |
16 | model: () => AccountModel, | 16 | model: AccountModel, |
17 | required: true | 17 | required: true |
18 | } | 18 | } |
19 | ] | 19 | ] |
@@ -21,12 +21,12 @@ enum ScopeNames { | |||
21 | [ScopeNames.WITH_SERVER]: { | 21 | [ScopeNames.WITH_SERVER]: { |
22 | include: [ | 22 | include: [ |
23 | { | 23 | { |
24 | model: () => ServerModel, | 24 | model: ServerModel, |
25 | required: true | 25 | required: true |
26 | } | 26 | } |
27 | ] | 27 | ] |
28 | } | 28 | } |
29 | }) | 29 | })) |
30 | 30 | ||
31 | @Table({ | 31 | @Table({ |
32 | tableName: 'serverBlocklist', | 32 | tableName: 'serverBlocklist', |
diff --git a/server/models/utils.ts b/server/models/utils.ts index 98170a00e..2b172f608 100644 --- a/server/models/utils.ts +++ b/server/models/utils.ts | |||
@@ -118,6 +118,15 @@ function buildWhereIdOrUUID (id: number | string) { | |||
118 | return validator.isInt('' + id) ? { id } : { uuid: id } | 118 | return validator.isInt('' + id) ? { id } : { uuid: id } |
119 | } | 119 | } |
120 | 120 | ||
121 | function parseAggregateResult (result: any) { | ||
122 | if (!result) return 0 | ||
123 | |||
124 | const total = parseInt(result + '', 10) | ||
125 | if (isNaN(total)) return 0 | ||
126 | |||
127 | return total | ||
128 | } | ||
129 | |||
121 | // --------------------------------------------------------------------------- | 130 | // --------------------------------------------------------------------------- |
122 | 131 | ||
123 | export { | 132 | export { |
@@ -131,7 +140,8 @@ export { | |||
131 | buildServerIdsFollowedBy, | 140 | buildServerIdsFollowedBy, |
132 | buildTrigramSearchIndex, | 141 | buildTrigramSearchIndex, |
133 | buildWhereIdOrUUID, | 142 | buildWhereIdOrUUID, |
134 | isOutdated | 143 | isOutdated, |
144 | parseAggregateResult | ||
135 | } | 145 | } |
136 | 146 | ||
137 | // --------------------------------------------------------------------------- | 147 | // --------------------------------------------------------------------------- |
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts index 048b47613..0fc3cfd4c 100644 --- a/server/models/video/tag.ts +++ b/server/models/video/tag.ts | |||
@@ -75,7 +75,7 @@ export class TagModel extends Model<TagModel> { | |||
75 | type: QueryTypes.SELECT as QueryTypes.SELECT | 75 | type: QueryTypes.SELECT as QueryTypes.SELECT |
76 | } | 76 | } |
77 | 77 | ||
78 | return TagModel.sequelize.query<{ name }>(query, options) | 78 | return TagModel.sequelize.query<{ name: string }>(query, options) |
79 | .then(data => data.map(d => d.name)) | 79 | .then(data => data.map(d => d.name)) |
80 | } | 80 | } |
81 | } | 81 | } |
diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts index baa5533ac..ec945893f 100644 --- a/server/models/video/thumbnail.ts +++ b/server/models/video/thumbnail.ts | |||
@@ -75,8 +75,8 @@ export class ThumbnailModel extends Model<ThumbnailModel> { | |||
75 | updatedAt: Date | 75 | updatedAt: Date |
76 | 76 | ||
77 | private static types: { [ id in ThumbnailType ]: { label: string, directory: string, staticPath: string } } = { | 77 | private static types: { [ id in ThumbnailType ]: { label: string, directory: string, staticPath: string } } = { |
78 | [ThumbnailType.THUMBNAIL]: { | 78 | [ThumbnailType.MINIATURE]: { |
79 | label: 'thumbnail', | 79 | label: 'miniature', |
80 | directory: CONFIG.STORAGE.THUMBNAILS_DIR, | 80 | directory: CONFIG.STORAGE.THUMBNAILS_DIR, |
81 | staticPath: STATIC_PATHS.THUMBNAILS | 81 | staticPath: STATIC_PATHS.THUMBNAILS |
82 | }, | 82 | }, |
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index 45c60e26b..76243bf48 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts | |||
@@ -12,7 +12,7 @@ import { | |||
12 | Table, | 12 | Table, |
13 | UpdatedAt | 13 | UpdatedAt |
14 | } from 'sequelize-typescript' | 14 | } from 'sequelize-typescript' |
15 | import { throwIfNotValid } from '../utils' | 15 | import { buildWhereIdOrUUID, throwIfNotValid } from '../utils' |
16 | import { VideoModel } from './video' | 16 | import { VideoModel } from './video' |
17 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' | 17 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' |
18 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' | 18 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' |
@@ -26,17 +26,17 @@ export enum ScopeNames { | |||
26 | WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' | 26 | WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE' |
27 | } | 27 | } |
28 | 28 | ||
29 | @Scopes({ | 29 | @Scopes(() => ({ |
30 | [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { | 30 | [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: { |
31 | include: [ | 31 | include: [ |
32 | { | 32 | { |
33 | attributes: [ 'uuid', 'remote' ], | 33 | attributes: [ 'uuid', 'remote' ], |
34 | model: () => VideoModel.unscoped(), | 34 | model: VideoModel.unscoped(), |
35 | required: true | 35 | required: true |
36 | } | 36 | } |
37 | ] | 37 | ] |
38 | } | 38 | } |
39 | }) | 39 | })) |
40 | 40 | ||
41 | @Table({ | 41 | @Table({ |
42 | tableName: 'videoCaption', | 42 | tableName: 'videoCaption', |
@@ -97,12 +97,9 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> { | |||
97 | const videoInclude = { | 97 | const videoInclude = { |
98 | model: VideoModel.unscoped(), | 98 | model: VideoModel.unscoped(), |
99 | attributes: [ 'id', 'remote', 'uuid' ], | 99 | attributes: [ 'id', 'remote', 'uuid' ], |
100 | where: { } | 100 | where: buildWhereIdOrUUID(videoId) |
101 | } | 101 | } |
102 | 102 | ||
103 | if (typeof videoId === 'string') videoInclude.where['uuid'] = videoId | ||
104 | else videoInclude.where['id'] = videoId | ||
105 | |||
106 | const query = { | 103 | const query = { |
107 | where: { | 104 | where: { |
108 | language | 105 | language |
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts index a4f4d53f1..171d4574d 100644 --- a/server/models/video/video-change-ownership.ts +++ b/server/models/video/video-change-ownership.ts | |||
@@ -23,29 +23,29 @@ enum ScopeNames { | |||
23 | } | 23 | } |
24 | ] | 24 | ] |
25 | }) | 25 | }) |
26 | @Scopes({ | 26 | @Scopes(() => ({ |
27 | [ScopeNames.FULL]: { | 27 | [ScopeNames.FULL]: { |
28 | include: [ | 28 | include: [ |
29 | { | 29 | { |
30 | model: () => AccountModel, | 30 | model: AccountModel, |
31 | as: 'Initiator', | 31 | as: 'Initiator', |
32 | required: true | 32 | required: true |
33 | }, | 33 | }, |
34 | { | 34 | { |
35 | model: () => AccountModel, | 35 | model: AccountModel, |
36 | as: 'NextOwner', | 36 | as: 'NextOwner', |
37 | required: true | 37 | required: true |
38 | }, | 38 | }, |
39 | { | 39 | { |
40 | model: () => VideoModel, | 40 | model: VideoModel, |
41 | required: true, | 41 | required: true, |
42 | include: [ | 42 | include: [ |
43 | { model: () => VideoFileModel } | 43 | { model: VideoFileModel } |
44 | ] | 44 | ] |
45 | } | 45 | } |
46 | ] as any // FIXME: sequelize typings | 46 | ] |
47 | } | 47 | } |
48 | }) | 48 | })) |
49 | export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel> { | 49 | export class VideoChangeOwnershipModel extends Model<VideoChangeOwnershipModel> { |
50 | @CreatedAt | 50 | @CreatedAt |
51 | createdAt: Date | 51 | createdAt: Date |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 901006dea..fb70e6625 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -58,15 +58,15 @@ type AvailableForListOptions = { | |||
58 | actorId: number | 58 | actorId: number |
59 | } | 59 | } |
60 | 60 | ||
61 | @DefaultScope({ | 61 | @DefaultScope(() => ({ |
62 | include: [ | 62 | include: [ |
63 | { | 63 | { |
64 | model: () => ActorModel, | 64 | model: ActorModel, |
65 | required: true | 65 | required: true |
66 | } | 66 | } |
67 | ] | 67 | ] |
68 | }) | 68 | })) |
69 | @Scopes({ | 69 | @Scopes(() => ({ |
70 | [ScopeNames.SUMMARY]: (withAccount = false) => { | 70 | [ScopeNames.SUMMARY]: (withAccount = false) => { |
71 | const base: FindOptions = { | 71 | const base: FindOptions = { |
72 | attributes: [ 'name', 'description', 'id', 'actorId' ], | 72 | attributes: [ 'name', 'description', 'id', 'actorId' ], |
@@ -142,22 +142,22 @@ type AvailableForListOptions = { | |||
142 | [ScopeNames.WITH_ACCOUNT]: { | 142 | [ScopeNames.WITH_ACCOUNT]: { |
143 | include: [ | 143 | include: [ |
144 | { | 144 | { |
145 | model: () => AccountModel, | 145 | model: AccountModel, |
146 | required: true | 146 | required: true |
147 | } | 147 | } |
148 | ] | 148 | ] |
149 | }, | 149 | }, |
150 | [ScopeNames.WITH_VIDEOS]: { | 150 | [ScopeNames.WITH_VIDEOS]: { |
151 | include: [ | 151 | include: [ |
152 | () => VideoModel | 152 | VideoModel |
153 | ] | 153 | ] |
154 | }, | 154 | }, |
155 | [ScopeNames.WITH_ACTOR]: { | 155 | [ScopeNames.WITH_ACTOR]: { |
156 | include: [ | 156 | include: [ |
157 | () => ActorModel | 157 | ActorModel |
158 | ] | 158 | ] |
159 | } | 159 | } |
160 | }) | 160 | })) |
161 | @Table({ | 161 | @Table({ |
162 | tableName: 'videoChannel', | 162 | tableName: 'videoChannel', |
163 | indexes | 163 | indexes |
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 5f7cd3671..fee11ec5f 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -30,7 +30,7 @@ import { UserModel } from '../account/user' | |||
30 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' | 30 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' |
31 | import { regexpCapture } from '../../helpers/regexp' | 31 | import { regexpCapture } from '../../helpers/regexp' |
32 | import { uniq } from 'lodash' | 32 | import { uniq } from 'lodash' |
33 | import { FindOptions, Op, Order, Sequelize, Transaction } from 'sequelize' | 33 | import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' |
34 | 34 | ||
35 | enum ScopeNames { | 35 | enum ScopeNames { |
36 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 36 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
@@ -39,7 +39,7 @@ enum ScopeNames { | |||
39 | ATTRIBUTES_FOR_API = 'ATTRIBUTES_FOR_API' | 39 | ATTRIBUTES_FOR_API = 'ATTRIBUTES_FOR_API' |
40 | } | 40 | } |
41 | 41 | ||
42 | @Scopes({ | 42 | @Scopes(() => ({ |
43 | [ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => { | 43 | [ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => { |
44 | return { | 44 | return { |
45 | attributes: { | 45 | attributes: { |
@@ -63,34 +63,34 @@ enum ScopeNames { | |||
63 | ] | 63 | ] |
64 | ] | 64 | ] |
65 | } | 65 | } |
66 | } | 66 | } as FindOptions |
67 | }, | 67 | }, |
68 | [ScopeNames.WITH_ACCOUNT]: { | 68 | [ScopeNames.WITH_ACCOUNT]: { |
69 | include: [ | 69 | include: [ |
70 | { | 70 | { |
71 | model: () => AccountModel, | 71 | model: AccountModel, |
72 | include: [ | 72 | include: [ |
73 | { | 73 | { |
74 | model: () => ActorModel, | 74 | model: ActorModel, |
75 | include: [ | 75 | include: [ |
76 | { | 76 | { |
77 | model: () => ServerModel, | 77 | model: ServerModel, |
78 | required: false | 78 | required: false |
79 | }, | 79 | }, |
80 | { | 80 | { |
81 | model: () => AvatarModel, | 81 | model: AvatarModel, |
82 | required: false | 82 | required: false |
83 | } | 83 | } |
84 | ] | 84 | ] |
85 | } | 85 | } |
86 | ] | 86 | ] |
87 | } | 87 | } |
88 | ] as any // FIXME: sequelize typings | 88 | ] |
89 | }, | 89 | }, |
90 | [ScopeNames.WITH_IN_REPLY_TO]: { | 90 | [ScopeNames.WITH_IN_REPLY_TO]: { |
91 | include: [ | 91 | include: [ |
92 | { | 92 | { |
93 | model: () => VideoCommentModel, | 93 | model: VideoCommentModel, |
94 | as: 'InReplyToVideoComment' | 94 | as: 'InReplyToVideoComment' |
95 | } | 95 | } |
96 | ] | 96 | ] |
@@ -98,19 +98,19 @@ enum ScopeNames { | |||
98 | [ScopeNames.WITH_VIDEO]: { | 98 | [ScopeNames.WITH_VIDEO]: { |
99 | include: [ | 99 | include: [ |
100 | { | 100 | { |
101 | model: () => VideoModel, | 101 | model: VideoModel, |
102 | required: true, | 102 | required: true, |
103 | include: [ | 103 | include: [ |
104 | { | 104 | { |
105 | model: () => VideoChannelModel.unscoped(), | 105 | model: VideoChannelModel.unscoped(), |
106 | required: true, | 106 | required: true, |
107 | include: [ | 107 | include: [ |
108 | { | 108 | { |
109 | model: () => AccountModel, | 109 | model: AccountModel, |
110 | required: true, | 110 | required: true, |
111 | include: [ | 111 | include: [ |
112 | { | 112 | { |
113 | model: () => ActorModel, | 113 | model: ActorModel, |
114 | required: true | 114 | required: true |
115 | } | 115 | } |
116 | ] | 116 | ] |
@@ -119,9 +119,9 @@ enum ScopeNames { | |||
119 | } | 119 | } |
120 | ] | 120 | ] |
121 | } | 121 | } |
122 | ] as any // FIXME: sequelize typings | 122 | ] |
123 | } | 123 | } |
124 | }) | 124 | })) |
125 | @Table({ | 125 | @Table({ |
126 | tableName: 'videoComment', | 126 | tableName: 'videoComment', |
127 | indexes: [ | 127 | indexes: [ |
@@ -313,8 +313,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
313 | } | 313 | } |
314 | } | 314 | } |
315 | 315 | ||
316 | // FIXME: typings | 316 | const scopes: (string | ScopeOptions)[] = [ |
317 | const scopes: any[] = [ | ||
318 | ScopeNames.WITH_ACCOUNT, | 317 | ScopeNames.WITH_ACCOUNT, |
319 | { | 318 | { |
320 | method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ] | 319 | method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ] |
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index c14d96bc5..2203a7aba 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -19,11 +19,11 @@ import { | |||
19 | isVideoFileSizeValid, | 19 | isVideoFileSizeValid, |
20 | isVideoFPSResolutionValid | 20 | isVideoFPSResolutionValid |
21 | } from '../../helpers/custom-validators/videos' | 21 | } from '../../helpers/custom-validators/videos' |
22 | import { throwIfNotValid } from '../utils' | 22 | import { parseAggregateResult, throwIfNotValid } from '../utils' |
23 | import { VideoModel } from './video' | 23 | import { VideoModel } from './video' |
24 | import * as Sequelize from 'sequelize' | ||
25 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 24 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
26 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 25 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
26 | import { FindOptions, QueryTypes, Transaction } from 'sequelize' | ||
27 | 27 | ||
28 | @Table({ | 28 | @Table({ |
29 | tableName: 'videoFile', | 29 | tableName: 'videoFile', |
@@ -97,15 +97,13 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
97 | static doesInfohashExist (infoHash: string) { | 97 | static doesInfohashExist (infoHash: string) { |
98 | const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1' | 98 | const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1' |
99 | const options = { | 99 | const options = { |
100 | type: Sequelize.QueryTypes.SELECT, | 100 | type: QueryTypes.SELECT, |
101 | bind: { infoHash }, | 101 | bind: { infoHash }, |
102 | raw: true | 102 | raw: true |
103 | } | 103 | } |
104 | 104 | ||
105 | return VideoModel.sequelize.query(query, options) | 105 | return VideoModel.sequelize.query(query, options) |
106 | .then(results => { | 106 | .then(results => results.length === 1) |
107 | return results.length === 1 | ||
108 | }) | ||
109 | } | 107 | } |
110 | 108 | ||
111 | static loadWithVideo (id: number) { | 109 | static loadWithVideo (id: number) { |
@@ -121,7 +119,7 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
121 | return VideoFileModel.findByPk(id, options) | 119 | return VideoFileModel.findByPk(id, options) |
122 | } | 120 | } |
123 | 121 | ||
124 | static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Sequelize.Transaction) { | 122 | static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Transaction) { |
125 | const query = { | 123 | const query = { |
126 | include: [ | 124 | include: [ |
127 | { | 125 | { |
@@ -144,8 +142,8 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
144 | return VideoFileModel.findAll(query) | 142 | return VideoFileModel.findAll(query) |
145 | } | 143 | } |
146 | 144 | ||
147 | static async getStats () { | 145 | static getStats () { |
148 | let totalLocalVideoFilesSize = await VideoFileModel.sum('size', { | 146 | const query: FindOptions = { |
149 | include: [ | 147 | include: [ |
150 | { | 148 | { |
151 | attributes: [], | 149 | attributes: [], |
@@ -155,13 +153,12 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
155 | } | 153 | } |
156 | } | 154 | } |
157 | ] | 155 | ] |
158 | } as any) | ||
159 | // Sequelize could return null... | ||
160 | if (!totalLocalVideoFilesSize) totalLocalVideoFilesSize = 0 | ||
161 | |||
162 | return { | ||
163 | totalLocalVideoFilesSize | ||
164 | } | 156 | } |
157 | |||
158 | return VideoFileModel.aggregate('size', 'SUM', query) | ||
159 | .then(result => ({ | ||
160 | totalLocalVideoFilesSize: parseAggregateResult(result) | ||
161 | })) | ||
165 | } | 162 | } |
166 | 163 | ||
167 | hasSameUniqueKeysThan (other: VideoFileModel) { | 164 | hasSameUniqueKeysThan (other: VideoFileModel) { |
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index 89992a5a8..877fcbc57 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts | |||
@@ -59,7 +59,7 @@ function videoModelToFormattedJSON (video: VideoModel, options?: VideoFormatting | |||
59 | views: video.views, | 59 | views: video.views, |
60 | likes: video.likes, | 60 | likes: video.likes, |
61 | dislikes: video.dislikes, | 61 | dislikes: video.dislikes, |
62 | thumbnailPath: video.getThumbnailStaticPath(), | 62 | thumbnailPath: video.getMiniatureStaticPath(), |
63 | previewPath: video.getPreviewStaticPath(), | 63 | previewPath: video.getPreviewStaticPath(), |
64 | embedPath: video.getEmbedStaticPath(), | 64 | embedPath: video.getEmbedStaticPath(), |
65 | createdAt: video.createdAt, | 65 | createdAt: video.createdAt, |
@@ -301,6 +301,8 @@ function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { | |||
301 | }) | 301 | }) |
302 | } | 302 | } |
303 | 303 | ||
304 | const miniature = video.getMiniature() | ||
305 | |||
304 | return { | 306 | return { |
305 | type: 'Video' as 'Video', | 307 | type: 'Video' as 'Video', |
306 | id: video.url, | 308 | id: video.url, |
@@ -326,10 +328,10 @@ function videoModelToActivityPubObject (video: VideoModel): VideoTorrentObject { | |||
326 | subtitleLanguage, | 328 | subtitleLanguage, |
327 | icon: { | 329 | icon: { |
328 | type: 'Image', | 330 | type: 'Image', |
329 | url: video.getThumbnail().getUrl(), | 331 | url: miniature.getUrl(), |
330 | mediaType: 'image/jpeg', | 332 | mediaType: 'image/jpeg', |
331 | width: video.getThumbnail().width, | 333 | width: miniature.width, |
332 | height: video.getThumbnail().height | 334 | height: miniature.height |
333 | }, | 335 | }, |
334 | url, | 336 | url, |
335 | likes: getVideoLikesActivityPubUrl(video), | 337 | likes: getVideoLikesActivityPubUrl(video), |
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 588a13a4f..480a671c8 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts | |||
@@ -21,18 +21,18 @@ import { VideoImport, VideoImportState } from '../../../shared' | |||
21 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' | 21 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' |
22 | import { UserModel } from '../account/user' | 22 | import { UserModel } from '../account/user' |
23 | 23 | ||
24 | @DefaultScope({ | 24 | @DefaultScope(() => ({ |
25 | include: [ | 25 | include: [ |
26 | { | 26 | { |
27 | model: () => UserModel.unscoped(), | 27 | model: UserModel.unscoped(), |
28 | required: true | 28 | required: true |
29 | }, | 29 | }, |
30 | { | 30 | { |
31 | model: () => VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]), | 31 | model: VideoModel.scope([ VideoModelScopeNames.WITH_ACCOUNT_DETAILS, VideoModelScopeNames.WITH_TAGS]), |
32 | required: false | 32 | required: false |
33 | } | 33 | } |
34 | ] | 34 | ] |
35 | }) | 35 | })) |
36 | 36 | ||
37 | @Table({ | 37 | @Table({ |
38 | tableName: 'videoImport', | 38 | tableName: 'videoImport', |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index 3e436acfc..63b4a0715 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -42,7 +42,7 @@ import { activityPubCollectionPagination } from '../../helpers/activitypub' | |||
42 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' | 42 | import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model' |
43 | import { ThumbnailModel } from './thumbnail' | 43 | import { ThumbnailModel } from './thumbnail' |
44 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' | 44 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' |
45 | import { fn, literal, Op, Transaction } from 'sequelize' | 45 | import { FindOptions, literal, Op, ScopeOptions, Transaction, WhereOptions } from 'sequelize' |
46 | 46 | ||
47 | enum ScopeNames { | 47 | enum ScopeNames { |
48 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 48 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', |
@@ -61,11 +61,11 @@ type AvailableForListOptions = { | |||
61 | privateAndUnlisted?: boolean | 61 | privateAndUnlisted?: boolean |
62 | } | 62 | } |
63 | 63 | ||
64 | @Scopes({ | 64 | @Scopes(() => ({ |
65 | [ ScopeNames.WITH_THUMBNAIL ]: { | 65 | [ ScopeNames.WITH_THUMBNAIL ]: { |
66 | include: [ | 66 | include: [ |
67 | { | 67 | { |
68 | model: () => ThumbnailModel, | 68 | model: ThumbnailModel, |
69 | required: false | 69 | required: false |
70 | } | 70 | } |
71 | ] | 71 | ] |
@@ -74,20 +74,16 @@ type AvailableForListOptions = { | |||
74 | attributes: { | 74 | attributes: { |
75 | include: [ | 75 | include: [ |
76 | [ | 76 | [ |
77 | fn('COUNT', 'toto'), | ||
78 | 'coucou' | ||
79 | ], | ||
80 | [ | ||
81 | literal('(SELECT COUNT("id") FROM "videoPlaylistElement" WHERE "videoPlaylistId" = "VideoPlaylistModel"."id")'), | 77 | literal('(SELECT COUNT("id") FROM "videoPlaylistElement" WHERE "videoPlaylistId" = "VideoPlaylistModel"."id")'), |
82 | 'videosLength' | 78 | 'videosLength' |
83 | ] | 79 | ] |
84 | ] | 80 | ] |
85 | } | 81 | } |
86 | }, | 82 | } as FindOptions, |
87 | [ ScopeNames.WITH_ACCOUNT ]: { | 83 | [ ScopeNames.WITH_ACCOUNT ]: { |
88 | include: [ | 84 | include: [ |
89 | { | 85 | { |
90 | model: () => AccountModel, | 86 | model: AccountModel, |
91 | required: true | 87 | required: true |
92 | } | 88 | } |
93 | ] | 89 | ] |
@@ -95,11 +91,11 @@ type AvailableForListOptions = { | |||
95 | [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY ]: { | 91 | [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL_SUMMARY ]: { |
96 | include: [ | 92 | include: [ |
97 | { | 93 | { |
98 | model: () => AccountModel.scope(AccountScopeNames.SUMMARY), | 94 | model: AccountModel.scope(AccountScopeNames.SUMMARY), |
99 | required: true | 95 | required: true |
100 | }, | 96 | }, |
101 | { | 97 | { |
102 | model: () => VideoChannelModel.scope(VideoChannelScopeNames.SUMMARY), | 98 | model: VideoChannelModel.scope(VideoChannelScopeNames.SUMMARY), |
103 | required: false | 99 | required: false |
104 | } | 100 | } |
105 | ] | 101 | ] |
@@ -107,11 +103,11 @@ type AvailableForListOptions = { | |||
107 | [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL ]: { | 103 | [ ScopeNames.WITH_ACCOUNT_AND_CHANNEL ]: { |
108 | include: [ | 104 | include: [ |
109 | { | 105 | { |
110 | model: () => AccountModel, | 106 | model: AccountModel, |
111 | required: true | 107 | required: true |
112 | }, | 108 | }, |
113 | { | 109 | { |
114 | model: () => VideoChannelModel, | 110 | model: VideoChannelModel, |
115 | required: false | 111 | required: false |
116 | } | 112 | } |
117 | ] | 113 | ] |
@@ -132,7 +128,7 @@ type AvailableForListOptions = { | |||
132 | ] | 128 | ] |
133 | } | 129 | } |
134 | 130 | ||
135 | const whereAnd: any[] = [] | 131 | const whereAnd: WhereOptions[] = [] |
136 | 132 | ||
137 | if (options.privateAndUnlisted !== true) { | 133 | if (options.privateAndUnlisted !== true) { |
138 | whereAnd.push({ | 134 | whereAnd.push({ |
@@ -178,9 +174,9 @@ type AvailableForListOptions = { | |||
178 | required: false | 174 | required: false |
179 | } | 175 | } |
180 | ] | 176 | ] |
181 | } | 177 | } as FindOptions |
182 | } | 178 | } |
183 | }) | 179 | })) |
184 | 180 | ||
185 | @Table({ | 181 | @Table({ |
186 | tableName: 'videoPlaylist', | 182 | tableName: 'videoPlaylist', |
@@ -269,6 +265,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
269 | VideoPlaylistElements: VideoPlaylistElementModel[] | 265 | VideoPlaylistElements: VideoPlaylistElementModel[] |
270 | 266 | ||
271 | @HasOne(() => ThumbnailModel, { | 267 | @HasOne(() => ThumbnailModel, { |
268 | |||
272 | foreignKey: { | 269 | foreignKey: { |
273 | name: 'videoPlaylistId', | 270 | name: 'videoPlaylistId', |
274 | allowNull: true | 271 | allowNull: true |
@@ -294,7 +291,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
294 | order: getSort(options.sort) | 291 | order: getSort(options.sort) |
295 | } | 292 | } |
296 | 293 | ||
297 | const scopes = [ | 294 | const scopes: (string | ScopeOptions)[] = [ |
298 | { | 295 | { |
299 | method: [ | 296 | method: [ |
300 | ScopeNames.AVAILABLE_FOR_LIST, | 297 | ScopeNames.AVAILABLE_FOR_LIST, |
@@ -306,7 +303,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
306 | privateAndUnlisted: options.privateAndUnlisted | 303 | privateAndUnlisted: options.privateAndUnlisted |
307 | } as AvailableForListOptions | 304 | } as AvailableForListOptions |
308 | ] | 305 | ] |
309 | } as any, // FIXME: typings | 306 | }, |
310 | ScopeNames.WITH_VIDEOS_LENGTH, | 307 | ScopeNames.WITH_VIDEOS_LENGTH, |
311 | ScopeNames.WITH_THUMBNAIL | 308 | ScopeNames.WITH_THUMBNAIL |
312 | ] | 309 | ] |
@@ -348,7 +345,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
348 | model: VideoPlaylistElementModel.unscoped(), | 345 | model: VideoPlaylistElementModel.unscoped(), |
349 | where: { | 346 | where: { |
350 | videoId: { | 347 | videoId: { |
351 | [Op.any]: videoIds | 348 | [Op.in]: videoIds // FIXME: sequelize ANY seems broken |
352 | } | 349 | } |
353 | }, | 350 | }, |
354 | required: true | 351 | required: true |
@@ -427,12 +424,10 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
427 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) | 424 | return VideoPlaylistModel.update({ privacy: VideoPlaylistPrivacy.PRIVATE, videoChannelId: null }, query) |
428 | } | 425 | } |
429 | 426 | ||
430 | setThumbnail (thumbnail: ThumbnailModel) { | 427 | async setAndSaveThumbnail (thumbnail: ThumbnailModel, t: Transaction) { |
431 | this.Thumbnail = thumbnail | 428 | thumbnail.videoPlaylistId = this.id |
432 | } | ||
433 | 429 | ||
434 | getThumbnail () { | 430 | this.Thumbnail = await thumbnail.save({ transaction: t }) |
435 | return this.Thumbnail | ||
436 | } | 431 | } |
437 | 432 | ||
438 | hasThumbnail () { | 433 | hasThumbnail () { |
@@ -448,13 +443,13 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
448 | getThumbnailUrl () { | 443 | getThumbnailUrl () { |
449 | if (!this.hasThumbnail()) return null | 444 | if (!this.hasThumbnail()) return null |
450 | 445 | ||
451 | return WEBSERVER.URL + STATIC_PATHS.THUMBNAILS + this.getThumbnail().filename | 446 | return WEBSERVER.URL + STATIC_PATHS.THUMBNAILS + this.Thumbnail.filename |
452 | } | 447 | } |
453 | 448 | ||
454 | getThumbnailStaticPath () { | 449 | getThumbnailStaticPath () { |
455 | if (!this.hasThumbnail()) return null | 450 | if (!this.hasThumbnail()) return null |
456 | 451 | ||
457 | return join(STATIC_PATHS.THUMBNAILS, this.getThumbnail().filename) | 452 | return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename) |
458 | } | 453 | } |
459 | 454 | ||
460 | setAsRefreshed () { | 455 | setAsRefreshed () { |
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts index c83f6c5b0..fda2d7cea 100644 --- a/server/models/video/video-share.ts +++ b/server/models/video/video-share.ts | |||
@@ -14,15 +14,15 @@ enum ScopeNames { | |||
14 | WITH_ACTOR = 'WITH_ACTOR' | 14 | WITH_ACTOR = 'WITH_ACTOR' |
15 | } | 15 | } |
16 | 16 | ||
17 | @Scopes({ | 17 | @Scopes(() => ({ |
18 | [ScopeNames.FULL]: { | 18 | [ScopeNames.FULL]: { |
19 | include: [ | 19 | include: [ |
20 | { | 20 | { |
21 | model: () => ActorModel, | 21 | model: ActorModel, |
22 | required: true | 22 | required: true |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | model: () => VideoModel, | 25 | model: VideoModel, |
26 | required: true | 26 | required: true |
27 | } | 27 | } |
28 | ] | 28 | ] |
@@ -30,12 +30,12 @@ enum ScopeNames { | |||
30 | [ScopeNames.WITH_ACTOR]: { | 30 | [ScopeNames.WITH_ACTOR]: { |
31 | include: [ | 31 | include: [ |
32 | { | 32 | { |
33 | model: () => ActorModel, | 33 | model: ActorModel, |
34 | required: true | 34 | required: true |
35 | } | 35 | } |
36 | ] | 36 | ] |
37 | } | 37 | } |
38 | }) | 38 | })) |
39 | @Table({ | 39 | @Table({ |
40 | tableName: 'videoShare', | 40 | tableName: 'videoShare', |
41 | indexes: [ | 41 | indexes: [ |
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts index b30267e09..31dc82c54 100644 --- a/server/models/video/video-streaming-playlist.ts +++ b/server/models/video/video-streaming-playlist.ts | |||
@@ -26,7 +26,7 @@ import { QueryTypes, Op } from 'sequelize' | |||
26 | fields: [ 'p2pMediaLoaderInfohashes' ], | 26 | fields: [ 'p2pMediaLoaderInfohashes' ], |
27 | using: 'gin' | 27 | using: 'gin' |
28 | } | 28 | } |
29 | ] as any // FIXME: sequelize typings | 29 | ] |
30 | }) | 30 | }) |
31 | export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistModel> { | 31 | export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistModel> { |
32 | @CreatedAt | 32 | @CreatedAt |
@@ -46,7 +46,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod | |||
46 | 46 | ||
47 | @AllowNull(false) | 47 | @AllowNull(false) |
48 | @Is('VideoStreamingPlaylistInfoHashes', value => throwIfNotValid(value, v => isArrayOf(v, isVideoFileInfoHashValid), 'info hashes')) | 48 | @Is('VideoStreamingPlaylistInfoHashes', value => throwIfNotValid(value, v => isArrayOf(v, isVideoFileInfoHashValid), 'info hashes')) |
49 | @Column({ type: DataType.ARRAY(DataType.STRING) }) // FIXME: typings | 49 | @Column(DataType.ARRAY(DataType.STRING)) |
50 | p2pMediaLoaderInfohashes: string[] | 50 | p2pMediaLoaderInfohashes: string[] |
51 | 51 | ||
52 | @AllowNull(false) | 52 | @AllowNull(false) |
@@ -87,7 +87,7 @@ export class VideoStreamingPlaylistModel extends Model<VideoStreamingPlaylistMod | |||
87 | raw: true | 87 | raw: true |
88 | } | 88 | } |
89 | 89 | ||
90 | return VideoModel.sequelize.query<any>(query, options) | 90 | return VideoModel.sequelize.query<object>(query, options) |
91 | .then(results => results.length === 1) | 91 | .then(results => results.length === 1) |
92 | } | 92 | } |
93 | 93 | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 329cebd28..18f18795e 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -227,12 +227,12 @@ type AvailableForListIDsOptions = { | |||
227 | historyOfUser?: UserModel | 227 | historyOfUser?: UserModel |
228 | } | 228 | } |
229 | 229 | ||
230 | @Scopes({ | 230 | @Scopes(() => ({ |
231 | [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { | 231 | [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { |
232 | const query: FindOptions = { | 232 | const query: FindOptions = { |
233 | where: { | 233 | where: { |
234 | id: { | 234 | id: { |
235 | [ Op.in ]: options.ids // FIXME: sequelize any seems broken | 235 | [ Op.in ]: options.ids // FIXME: sequelize ANY seems broken |
236 | } | 236 | } |
237 | }, | 237 | }, |
238 | include: [ | 238 | include: [ |
@@ -486,7 +486,7 @@ type AvailableForListIDsOptions = { | |||
486 | [ ScopeNames.WITH_THUMBNAILS ]: { | 486 | [ ScopeNames.WITH_THUMBNAILS ]: { |
487 | include: [ | 487 | include: [ |
488 | { | 488 | { |
489 | model: () => ThumbnailModel, | 489 | model: ThumbnailModel, |
490 | required: false | 490 | required: false |
491 | } | 491 | } |
492 | ] | 492 | ] |
@@ -495,48 +495,48 @@ type AvailableForListIDsOptions = { | |||
495 | include: [ | 495 | include: [ |
496 | { | 496 | { |
497 | attributes: [ 'accountId' ], | 497 | attributes: [ 'accountId' ], |
498 | model: () => VideoChannelModel.unscoped(), | 498 | model: VideoChannelModel.unscoped(), |
499 | required: true, | 499 | required: true, |
500 | include: [ | 500 | include: [ |
501 | { | 501 | { |
502 | attributes: [ 'userId' ], | 502 | attributes: [ 'userId' ], |
503 | model: () => AccountModel.unscoped(), | 503 | model: AccountModel.unscoped(), |
504 | required: true | 504 | required: true |
505 | } | 505 | } |
506 | ] | 506 | ] |
507 | } | 507 | } |
508 | ] as any // FIXME: sequelize typings | 508 | ] |
509 | }, | 509 | }, |
510 | [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { | 510 | [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { |
511 | include: [ | 511 | include: [ |
512 | { | 512 | { |
513 | model: () => VideoChannelModel.unscoped(), | 513 | model: VideoChannelModel.unscoped(), |
514 | required: true, | 514 | required: true, |
515 | include: [ | 515 | include: [ |
516 | { | 516 | { |
517 | attributes: { | 517 | attributes: { |
518 | exclude: [ 'privateKey', 'publicKey' ] | 518 | exclude: [ 'privateKey', 'publicKey' ] |
519 | }, | 519 | }, |
520 | model: () => ActorModel.unscoped(), | 520 | model: ActorModel.unscoped(), |
521 | required: true, | 521 | required: true, |
522 | include: [ | 522 | include: [ |
523 | { | 523 | { |
524 | attributes: [ 'host' ], | 524 | attributes: [ 'host' ], |
525 | model: () => ServerModel.unscoped(), | 525 | model: ServerModel.unscoped(), |
526 | required: false | 526 | required: false |
527 | }, | 527 | }, |
528 | { | 528 | { |
529 | model: () => AvatarModel.unscoped(), | 529 | model: AvatarModel.unscoped(), |
530 | required: false | 530 | required: false |
531 | } | 531 | } |
532 | ] | 532 | ] |
533 | }, | 533 | }, |
534 | { | 534 | { |
535 | model: () => AccountModel.unscoped(), | 535 | model: AccountModel.unscoped(), |
536 | required: true, | 536 | required: true, |
537 | include: [ | 537 | include: [ |
538 | { | 538 | { |
539 | model: () => ActorModel.unscoped(), | 539 | model: ActorModel.unscoped(), |
540 | attributes: { | 540 | attributes: { |
541 | exclude: [ 'privateKey', 'publicKey' ] | 541 | exclude: [ 'privateKey', 'publicKey' ] |
542 | }, | 542 | }, |
@@ -544,11 +544,11 @@ type AvailableForListIDsOptions = { | |||
544 | include: [ | 544 | include: [ |
545 | { | 545 | { |
546 | attributes: [ 'host' ], | 546 | attributes: [ 'host' ], |
547 | model: () => ServerModel.unscoped(), | 547 | model: ServerModel.unscoped(), |
548 | required: false | 548 | required: false |
549 | }, | 549 | }, |
550 | { | 550 | { |
551 | model: () => AvatarModel.unscoped(), | 551 | model: AvatarModel.unscoped(), |
552 | required: false | 552 | required: false |
553 | } | 553 | } |
554 | ] | 554 | ] |
@@ -557,16 +557,16 @@ type AvailableForListIDsOptions = { | |||
557 | } | 557 | } |
558 | ] | 558 | ] |
559 | } | 559 | } |
560 | ] as any // FIXME: sequelize typings | 560 | ] |
561 | }, | 561 | }, |
562 | [ ScopeNames.WITH_TAGS ]: { | 562 | [ ScopeNames.WITH_TAGS ]: { |
563 | include: [ () => TagModel ] | 563 | include: [ TagModel ] |
564 | }, | 564 | }, |
565 | [ ScopeNames.WITH_BLACKLISTED ]: { | 565 | [ ScopeNames.WITH_BLACKLISTED ]: { |
566 | include: [ | 566 | include: [ |
567 | { | 567 | { |
568 | attributes: [ 'id', 'reason' ], | 568 | attributes: [ 'id', 'reason' ], |
569 | model: () => VideoBlacklistModel, | 569 | model: VideoBlacklistModel, |
570 | required: false | 570 | required: false |
571 | } | 571 | } |
572 | ] | 572 | ] |
@@ -588,8 +588,7 @@ type AvailableForListIDsOptions = { | |||
588 | include: [ | 588 | include: [ |
589 | { | 589 | { |
590 | model: VideoFileModel.unscoped(), | 590 | model: VideoFileModel.unscoped(), |
591 | // FIXME: typings | 591 | separate: true, // We may have multiple files, having multiple redundancies so let's separate this join |
592 | [ 'separate' as any ]: true, // We may have multiple files, having multiple redundancies so let's separate this join | ||
593 | required: false, | 592 | required: false, |
594 | include: subInclude | 593 | include: subInclude |
595 | } | 594 | } |
@@ -613,8 +612,7 @@ type AvailableForListIDsOptions = { | |||
613 | include: [ | 612 | include: [ |
614 | { | 613 | { |
615 | model: VideoStreamingPlaylistModel.unscoped(), | 614 | model: VideoStreamingPlaylistModel.unscoped(), |
616 | // FIXME: typings | 615 | separate: true, // We may have multiple streaming playlists, having multiple redundancies so let's separate this join |
617 | [ 'separate' as any ]: true, // We may have multiple streaming playlists, having multiple redundancies so let's separate this join | ||
618 | required: false, | 616 | required: false, |
619 | include: subInclude | 617 | include: subInclude |
620 | } | 618 | } |
@@ -624,7 +622,7 @@ type AvailableForListIDsOptions = { | |||
624 | [ ScopeNames.WITH_SCHEDULED_UPDATE ]: { | 622 | [ ScopeNames.WITH_SCHEDULED_UPDATE ]: { |
625 | include: [ | 623 | include: [ |
626 | { | 624 | { |
627 | model: () => ScheduleVideoUpdateModel.unscoped(), | 625 | model: ScheduleVideoUpdateModel.unscoped(), |
628 | required: false | 626 | required: false |
629 | } | 627 | } |
630 | ] | 628 | ] |
@@ -643,7 +641,7 @@ type AvailableForListIDsOptions = { | |||
643 | ] | 641 | ] |
644 | } | 642 | } |
645 | } | 643 | } |
646 | }) | 644 | })) |
647 | @Table({ | 645 | @Table({ |
648 | tableName: 'video', | 646 | tableName: 'video', |
649 | indexes | 647 | indexes |
@@ -1075,15 +1073,14 @@ export class VideoModel extends Model<VideoModel> { | |||
1075 | } | 1073 | } |
1076 | 1074 | ||
1077 | return Bluebird.all([ | 1075 | return Bluebird.all([ |
1078 | // FIXME: typing issue | 1076 | VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query), |
1079 | VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any), | 1077 | VideoModel.sequelize.query<{ total: string }>(rawCountQuery, { type: QueryTypes.SELECT }) |
1080 | VideoModel.sequelize.query<{ total: number }>(rawCountQuery, { type: QueryTypes.SELECT }) | ||
1081 | ]).then(([ rows, totals ]) => { | 1078 | ]).then(([ rows, totals ]) => { |
1082 | // totals: totalVideos + totalVideoShares | 1079 | // totals: totalVideos + totalVideoShares |
1083 | let totalVideos = 0 | 1080 | let totalVideos = 0 |
1084 | let totalVideoShares = 0 | 1081 | let totalVideoShares = 0 |
1085 | if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total + '', 10) | 1082 | if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total, 10) |
1086 | if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total + '', 10) | 1083 | if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total, 10) |
1087 | 1084 | ||
1088 | const total = totalVideos + totalVideoShares | 1085 | const total = totalVideos + totalVideoShares |
1089 | return { | 1086 | return { |
@@ -1094,50 +1091,58 @@ export class VideoModel extends Model<VideoModel> { | |||
1094 | } | 1091 | } |
1095 | 1092 | ||
1096 | static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { | 1093 | static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { |
1097 | const query: FindOptions = { | 1094 | function buildBaseQuery (): FindOptions { |
1098 | offset: start, | 1095 | return { |
1099 | limit: count, | 1096 | offset: start, |
1100 | order: getVideoSort(sort), | 1097 | limit: count, |
1101 | include: [ | 1098 | order: getVideoSort(sort), |
1102 | { | 1099 | include: [ |
1103 | model: VideoChannelModel, | 1100 | { |
1104 | required: true, | 1101 | model: VideoChannelModel, |
1105 | include: [ | 1102 | required: true, |
1106 | { | 1103 | include: [ |
1107 | model: AccountModel, | 1104 | { |
1108 | where: { | 1105 | model: AccountModel, |
1109 | id: accountId | 1106 | where: { |
1110 | }, | 1107 | id: accountId |
1111 | required: true | 1108 | }, |
1112 | } | 1109 | required: true |
1113 | ] | 1110 | } |
1114 | }, | 1111 | ] |
1115 | { | 1112 | } |
1116 | model: ScheduleVideoUpdateModel, | 1113 | ] |
1117 | required: false | 1114 | } |
1118 | }, | ||
1119 | { | ||
1120 | model: VideoBlacklistModel, | ||
1121 | required: false | ||
1122 | } | ||
1123 | ] | ||
1124 | } | 1115 | } |
1125 | 1116 | ||
1117 | const countQuery = buildBaseQuery() | ||
1118 | const findQuery = buildBaseQuery() | ||
1119 | |||
1120 | findQuery.include.push({ | ||
1121 | model: ScheduleVideoUpdateModel, | ||
1122 | required: false | ||
1123 | }) | ||
1124 | |||
1125 | findQuery.include.push({ | ||
1126 | model: VideoBlacklistModel, | ||
1127 | required: false | ||
1128 | }) | ||
1129 | |||
1126 | if (withFiles === true) { | 1130 | if (withFiles === true) { |
1127 | query.include.push({ | 1131 | findQuery.include.push({ |
1128 | model: VideoFileModel.unscoped(), | 1132 | model: VideoFileModel.unscoped(), |
1129 | required: true | 1133 | required: true |
1130 | }) | 1134 | }) |
1131 | } | 1135 | } |
1132 | 1136 | ||
1133 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS) | 1137 | return Promise.all([ |
1134 | .findAndCountAll(query) | 1138 | VideoModel.count(countQuery), |
1135 | .then(({ rows, count }) => { | 1139 | VideoModel.findAll(findQuery) |
1136 | return { | 1140 | ]).then(([ count, rows ]) => { |
1137 | data: rows, | 1141 | return { |
1138 | total: count | 1142 | data: rows, |
1139 | } | 1143 | total: count |
1140 | }) | 1144 | } |
1145 | }) | ||
1141 | } | 1146 | } |
1142 | 1147 | ||
1143 | static async listForApi (options: { | 1148 | static async listForApi (options: { |
@@ -1404,12 +1409,12 @@ export class VideoModel extends Model<VideoModel> { | |||
1404 | const where = buildWhereIdOrUUID(id) | 1409 | const where = buildWhereIdOrUUID(id) |
1405 | 1410 | ||
1406 | const options = { | 1411 | const options = { |
1407 | order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings | 1412 | order: [ [ 'Tags', 'name', 'ASC' ] ] as any, |
1408 | where, | 1413 | where, |
1409 | transaction: t | 1414 | transaction: t |
1410 | } | 1415 | } |
1411 | 1416 | ||
1412 | const scopes = [ | 1417 | const scopes: (string | ScopeOptions)[] = [ |
1413 | ScopeNames.WITH_TAGS, | 1418 | ScopeNames.WITH_TAGS, |
1414 | ScopeNames.WITH_BLACKLISTED, | 1419 | ScopeNames.WITH_BLACKLISTED, |
1415 | ScopeNames.WITH_ACCOUNT_DETAILS, | 1420 | ScopeNames.WITH_ACCOUNT_DETAILS, |
@@ -1420,7 +1425,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1420 | ] | 1425 | ] |
1421 | 1426 | ||
1422 | if (userId) { | 1427 | if (userId) { |
1423 | scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] } as any) // FIXME: typings | 1428 | scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] }) |
1424 | } | 1429 | } |
1425 | 1430 | ||
1426 | return VideoModel | 1431 | return VideoModel |
@@ -1437,18 +1442,18 @@ export class VideoModel extends Model<VideoModel> { | |||
1437 | transaction: t | 1442 | transaction: t |
1438 | } | 1443 | } |
1439 | 1444 | ||
1440 | const scopes = [ | 1445 | const scopes: (string | ScopeOptions)[] = [ |
1441 | ScopeNames.WITH_TAGS, | 1446 | ScopeNames.WITH_TAGS, |
1442 | ScopeNames.WITH_BLACKLISTED, | 1447 | ScopeNames.WITH_BLACKLISTED, |
1443 | ScopeNames.WITH_ACCOUNT_DETAILS, | 1448 | ScopeNames.WITH_ACCOUNT_DETAILS, |
1444 | ScopeNames.WITH_SCHEDULED_UPDATE, | 1449 | ScopeNames.WITH_SCHEDULED_UPDATE, |
1445 | ScopeNames.WITH_THUMBNAILS, | 1450 | ScopeNames.WITH_THUMBNAILS, |
1446 | { method: [ ScopeNames.WITH_FILES, true ] } as any, // FIXME: typings | 1451 | { method: [ ScopeNames.WITH_FILES, true ] }, |
1447 | { method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] } as any // FIXME: typings | 1452 | { method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] } |
1448 | ] | 1453 | ] |
1449 | 1454 | ||
1450 | if (userId) { | 1455 | if (userId) { |
1451 | scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] } as any) // FIXME: typings | 1456 | scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] }) |
1452 | } | 1457 | } |
1453 | 1458 | ||
1454 | return VideoModel | 1459 | return VideoModel |
@@ -1520,9 +1525,9 @@ export class VideoModel extends Model<VideoModel> { | |||
1520 | attributes: [ field ], | 1525 | attributes: [ field ], |
1521 | limit: count, | 1526 | limit: count, |
1522 | group: field, | 1527 | group: field, |
1523 | having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), { | 1528 | having: Sequelize.where( |
1524 | [ Op.gte ]: threshold | 1529 | Sequelize.fn('COUNT', Sequelize.col(field)), { [ Op.gte ]: threshold } |
1525 | }) as any, // FIXME: typings | 1530 | ), |
1526 | order: [ (this.sequelize as any).random() ] | 1531 | order: [ (this.sequelize as any).random() ] |
1527 | } | 1532 | } |
1528 | 1533 | ||
@@ -1594,16 +1599,10 @@ export class VideoModel extends Model<VideoModel> { | |||
1594 | ] | 1599 | ] |
1595 | } | 1600 | } |
1596 | 1601 | ||
1597 | // FIXME: typing | 1602 | const apiScope: (string | ScopeOptions)[] = [ ScopeNames.WITH_THUMBNAILS ] |
1598 | const apiScope: any[] = [ ScopeNames.WITH_THUMBNAILS ] | ||
1599 | 1603 | ||
1600 | if (options.user) { | 1604 | if (options.user) { |
1601 | apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.user.id ] }) | 1605 | apiScope.push({ method: [ ScopeNames.WITH_USER_HISTORY, options.user.id ] }) |
1602 | |||
1603 | // Even if the relation is n:m, we know that a user only have 0..1 video history | ||
1604 | // So we won't have multiple rows for the same video | ||
1605 | // A subquery adds some bugs in our query so disable it | ||
1606 | secondQuery.subQuery = false | ||
1607 | } | 1606 | } |
1608 | 1607 | ||
1609 | apiScope.push({ | 1608 | apiScope.push({ |
@@ -1651,13 +1650,17 @@ export class VideoModel extends Model<VideoModel> { | |||
1651 | return maxBy(this.VideoFiles, file => file.resolution) | 1650 | return maxBy(this.VideoFiles, file => file.resolution) |
1652 | } | 1651 | } |
1653 | 1652 | ||
1654 | addThumbnail (thumbnail: ThumbnailModel) { | 1653 | async addAndSaveThumbnail (thumbnail: ThumbnailModel, transaction: Transaction) { |
1654 | thumbnail.videoId = this.id | ||
1655 | |||
1656 | const savedThumbnail = await thumbnail.save({ transaction }) | ||
1657 | |||
1655 | if (Array.isArray(this.Thumbnails) === false) this.Thumbnails = [] | 1658 | if (Array.isArray(this.Thumbnails) === false) this.Thumbnails = [] |
1656 | 1659 | ||
1657 | // Already have this thumbnail, skip | 1660 | // Already have this thumbnail, skip |
1658 | if (this.Thumbnails.find(t => t.id === thumbnail.id)) return | 1661 | if (this.Thumbnails.find(t => t.id === savedThumbnail.id)) return |
1659 | 1662 | ||
1660 | this.Thumbnails.push(thumbnail) | 1663 | this.Thumbnails.push(savedThumbnail) |
1661 | } | 1664 | } |
1662 | 1665 | ||
1663 | getVideoFilename (videoFile: VideoFileModel) { | 1666 | getVideoFilename (videoFile: VideoFileModel) { |
@@ -1668,10 +1671,10 @@ export class VideoModel extends Model<VideoModel> { | |||
1668 | return this.uuid + '.jpg' | 1671 | return this.uuid + '.jpg' |
1669 | } | 1672 | } |
1670 | 1673 | ||
1671 | getThumbnail () { | 1674 | getMiniature () { |
1672 | if (Array.isArray(this.Thumbnails) === false) return undefined | 1675 | if (Array.isArray(this.Thumbnails) === false) return undefined |
1673 | 1676 | ||
1674 | return this.Thumbnails.find(t => t.type === ThumbnailType.THUMBNAIL) | 1677 | return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE) |
1675 | } | 1678 | } |
1676 | 1679 | ||
1677 | generatePreviewName () { | 1680 | generatePreviewName () { |
@@ -1732,8 +1735,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1732 | return '/videos/embed/' + this.uuid | 1735 | return '/videos/embed/' + this.uuid |
1733 | } | 1736 | } |
1734 | 1737 | ||
1735 | getThumbnailStaticPath () { | 1738 | getMiniatureStaticPath () { |
1736 | const thumbnail = this.getThumbnail() | 1739 | const thumbnail = this.getMiniature() |
1737 | if (!thumbnail) return null | 1740 | if (!thumbnail) return null |
1738 | 1741 | ||
1739 | return join(STATIC_PATHS.THUMBNAILS, thumbnail.filename) | 1742 | return join(STATIC_PATHS.THUMBNAILS, thumbnail.filename) |