aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-05-27 16:12:41 +0200
committerChocobozzz <me@florianbigard.com>2021-05-27 16:12:41 +0200
commit8f608a4cb22ab232cfab20665050764b38bac9c7 (patch)
tree6a6785aae79bf5939ad7b7a50a1bd8031268d2b4 /server/models
parent030ccfce59a8cb8f2fee6ea8dd363ba635c5c5c2 (diff)
parentc215e627b575d2c4085ccb222f4ca8d0237b7552 (diff)
downloadPeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.tar.gz
PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.tar.zst
PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.zip
Merge branch 'develop' into shorter-URLs-channels-accounts
Diffstat (limited to 'server/models')
-rw-r--r--server/models/abuse/abuse-message.ts3
-rw-r--r--server/models/abuse/abuse.ts4
-rw-r--r--server/models/abuse/video-abuse.ts3
-rw-r--r--server/models/abuse/video-comment-abuse.ts3
-rw-r--r--server/models/account/account-blocklist.ts5
-rw-r--r--server/models/account/account-video-rate.ts5
-rw-r--r--server/models/account/account.ts14
-rw-r--r--server/models/account/actor-custom-page.ts69
-rw-r--r--server/models/actor/actor-follow.ts (renamed from server/models/activitypub/actor-follow.ts)5
-rw-r--r--server/models/actor/actor-image.ts (renamed from server/models/account/actor-image.ts)3
-rw-r--r--server/models/actor/actor.ts (renamed from server/models/activitypub/actor.ts)21
-rw-r--r--server/models/application/application.ts5
-rw-r--r--server/models/oauth/oauth-client.ts3
-rw-r--r--server/models/oauth/oauth-token.ts7
-rw-r--r--server/models/redundancy/video-redundancy.ts51
-rw-r--r--server/models/server/plugin.ts9
-rw-r--r--server/models/server/server-blocklist.ts3
-rw-r--r--server/models/server/server.ts5
-rw-r--r--server/models/server/tracker.ts3
-rw-r--r--server/models/server/video-tracker.ts3
-rw-r--r--server/models/user/user-notification-setting.ts (renamed from server/models/account/user-notification-setting.ts)3
-rw-r--r--server/models/user/user-notification.ts (renamed from server/models/account/user-notification.ts)11
-rw-r--r--server/models/user/user-video-history.ts (renamed from server/models/account/user-video-history.ts)7
-rw-r--r--server/models/user/user.ts (renamed from server/models/account/user.ts)15
-rw-r--r--server/models/utils.ts7
-rw-r--r--server/models/video/schedule-video-update.ts9
-rw-r--r--server/models/video/tag.ts3
-rw-r--r--server/models/video/thumbnail.ts3
-rw-r--r--server/models/video/video-blacklist.ts3
-rw-r--r--server/models/video/video-caption.ts3
-rw-r--r--server/models/video/video-change-ownership.ts3
-rw-r--r--server/models/video/video-channel.ts17
-rw-r--r--server/models/video/video-comment.ts7
-rw-r--r--server/models/video/video-file.ts7
-rw-r--r--server/models/video/video-format-utils.ts30
-rw-r--r--server/models/video/video-import.ts7
-rw-r--r--server/models/video/video-live.ts3
-rw-r--r--server/models/video/video-playlist-element.ts6
-rw-r--r--server/models/video/video-playlist.ts5
-rw-r--r--server/models/video/video-query-builder.ts22
-rw-r--r--server/models/video/video-share.ts5
-rw-r--r--server/models/video/video-streaming-playlist.ts3
-rw-r--r--server/models/video/video-tag.ts3
-rw-r--r--server/models/video/video-view.ts5
-rw-r--r--server/models/video/video.ts25
45 files changed, 258 insertions, 178 deletions
diff --git a/server/models/abuse/abuse-message.ts b/server/models/abuse/abuse-message.ts
index 7e51b3e07..2c5987e96 100644
--- a/server/models/abuse/abuse-message.ts
+++ b/server/models/abuse/abuse-message.ts
@@ -1,6 +1,7 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' 2import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses'
3import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models' 3import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
4import { AbuseMessage } from '@shared/models' 5import { AbuseMessage } from '@shared/models'
5import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' 6import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
6import { getSort, throwIfNotValid } from '../utils' 7import { getSort, throwIfNotValid } from '../utils'
@@ -17,7 +18,7 @@ import { AbuseModel } from './abuse'
17 } 18 }
18 ] 19 ]
19}) 20})
20export class AbuseMessageModel extends Model { 21export class AbuseMessageModel extends Model<Partial<AttributesOnly<AbuseMessageModel>>> {
21 22
22 @AllowNull(false) 23 @AllowNull(false)
23 @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) 24 @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message'))
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts
index 262f364f1..3518f5c02 100644
--- a/server/models/abuse/abuse.ts
+++ b/server/models/abuse/abuse.ts
@@ -16,7 +16,7 @@ import {
16 UpdatedAt 16 UpdatedAt
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' 18import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
19import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' 19import { abusePredefinedReasonsMap, AttributesOnly } from '@shared/core-utils'
20import { 20import {
21 AbuseFilter, 21 AbuseFilter,
22 AbuseObject, 22 AbuseObject,
@@ -187,7 +187,7 @@ export enum ScopeNames {
187 } 187 }
188 ] 188 ]
189}) 189})
190export class AbuseModel extends Model { 190export class AbuseModel extends Model<Partial<AttributesOnly<AbuseModel>>> {
191 191
192 @AllowNull(false) 192 @AllowNull(false)
193 @Default(null) 193 @Default(null)
diff --git a/server/models/abuse/video-abuse.ts b/server/models/abuse/video-abuse.ts
index 90aa0695e..95bff50d0 100644
--- a/server/models/abuse/video-abuse.ts
+++ b/server/models/abuse/video-abuse.ts
@@ -1,4 +1,5 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { AttributesOnly } from '@shared/core-utils'
2import { VideoDetails } from '@shared/models' 3import { VideoDetails } from '@shared/models'
3import { VideoModel } from '../video/video' 4import { VideoModel } from '../video/video'
4import { AbuseModel } from './abuse' 5import { AbuseModel } from './abuse'
@@ -14,7 +15,7 @@ import { AbuseModel } from './abuse'
14 } 15 }
15 ] 16 ]
16}) 17})
17export class VideoAbuseModel extends Model { 18export class VideoAbuseModel extends Model<Partial<AttributesOnly<VideoAbuseModel>>> {
18 19
19 @CreatedAt 20 @CreatedAt
20 createdAt: Date 21 createdAt: Date
diff --git a/server/models/abuse/video-comment-abuse.ts b/server/models/abuse/video-comment-abuse.ts
index d3fce76a5..32cb2ca64 100644
--- a/server/models/abuse/video-comment-abuse.ts
+++ b/server/models/abuse/video-comment-abuse.ts
@@ -1,4 +1,5 @@
1import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { AttributesOnly } from '@shared/core-utils'
2import { VideoCommentModel } from '../video/video-comment' 3import { VideoCommentModel } from '../video/video-comment'
3import { AbuseModel } from './abuse' 4import { AbuseModel } from './abuse'
4 5
@@ -13,7 +14,7 @@ import { AbuseModel } from './abuse'
13 } 14 }
14 ] 15 ]
15}) 16})
16export class VideoCommentAbuseModel extends Model { 17export class VideoCommentAbuseModel extends Model<Partial<AttributesOnly<VideoCommentAbuseModel>>> {
17 18
18 @CreatedAt 19 @CreatedAt
19 createdAt: Date 20 createdAt: Date
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts
index fe9168ab8..b2375b006 100644
--- a/server/models/account/account-blocklist.ts
+++ b/server/models/account/account-blocklist.ts
@@ -1,8 +1,9 @@
1import { Op } from 'sequelize' 1import { Op } from 'sequelize'
2import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' 3import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
4import { AccountBlock } from '../../../shared/models' 5import { AccountBlock } from '../../../shared/models'
5import { ActorModel } from '../activitypub/actor' 6import { ActorModel } from '../actor/actor'
6import { ServerModel } from '../server/server' 7import { ServerModel } from '../server/server'
7import { getSort, searchAttribute } from '../utils' 8import { getSort, searchAttribute } from '../utils'
8import { AccountModel } from './account' 9import { AccountModel } from './account'
@@ -40,7 +41,7 @@ enum ScopeNames {
40 } 41 }
41 ] 42 ]
42}) 43})
43export class AccountBlocklistModel extends Model { 44export class AccountBlocklistModel extends Model<Partial<AttributesOnly<AccountBlocklistModel>>> {
44 45
45 @CreatedAt 46 @CreatedAt
46 createdAt: Date 47 createdAt: Date
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts
index 801f76bba..ee6dbc6da 100644
--- a/server/models/account/account-video-rate.ts
+++ b/server/models/account/account-video-rate.ts
@@ -7,11 +7,12 @@ import {
7 MAccountVideoRateAccountVideo, 7 MAccountVideoRateAccountVideo,
8 MAccountVideoRateFormattable 8 MAccountVideoRateFormattable
9} from '@server/types/models/video/video-rate' 9} from '@server/types/models/video/video-rate'
10import { AttributesOnly } from '@shared/core-utils'
10import { AccountVideoRate } from '../../../shared' 11import { AccountVideoRate } from '../../../shared'
11import { VideoRateType } from '../../../shared/models/videos' 12import { VideoRateType } from '../../../shared/models/videos'
12import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 13import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
13import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' 14import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants'
14import { ActorModel } from '../activitypub/actor' 15import { ActorModel } from '../actor/actor'
15import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' 16import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
16import { VideoModel } from '../video/video' 17import { VideoModel } from '../video/video'
17import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' 18import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
@@ -42,7 +43,7 @@ import { AccountModel } from './account'
42 } 43 }
43 ] 44 ]
44}) 45})
45export class AccountVideoRateModel extends Model { 46export class AccountVideoRateModel extends Model<Partial<AttributesOnly<AccountVideoRateModel>>> {
46 47
47 @AllowNull(false) 48 @AllowNull(false)
48 @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) 49 @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES)))
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 312451abe..665ecd595 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -17,10 +17,11 @@ import {
17 UpdatedAt 17 UpdatedAt
18} from 'sequelize-typescript' 18} from 'sequelize-typescript'
19import { ModelCache } from '@server/models/model-cache' 19import { ModelCache } from '@server/models/model-cache'
20import { AttributesOnly } from '@shared/core-utils'
20import { Account, AccountSummary } from '../../../shared/models/actors' 21import { Account, AccountSummary } from '../../../shared/models/actors'
21import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' 22import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
22import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' 23import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants'
23import { sendDeleteActor } from '../../lib/activitypub/send' 24import { sendDeleteActor } from '../../lib/activitypub/send/send-delete'
24import { 25import {
25 MAccount, 26 MAccount,
26 MAccountActor, 27 MAccountActor,
@@ -30,19 +31,19 @@ import {
30 MAccountSummaryFormattable, 31 MAccountSummaryFormattable,
31 MChannelActor 32 MChannelActor
32} from '../../types/models' 33} from '../../types/models'
33import { ActorModel } from '../activitypub/actor' 34import { ActorModel } from '../actor/actor'
34import { ActorFollowModel } from '../activitypub/actor-follow' 35import { ActorFollowModel } from '../actor/actor-follow'
36import { ActorImageModel } from '../actor/actor-image'
35import { ApplicationModel } from '../application/application' 37import { ApplicationModel } from '../application/application'
36import { ActorImageModel } from './actor-image'
37import { ServerModel } from '../server/server' 38import { ServerModel } from '../server/server'
38import { ServerBlocklistModel } from '../server/server-blocklist' 39import { ServerBlocklistModel } from '../server/server-blocklist'
40import { UserModel } from '../user/user'
39import { getSort, throwIfNotValid } from '../utils' 41import { getSort, throwIfNotValid } from '../utils'
40import { VideoModel } from '../video/video' 42import { VideoModel } from '../video/video'
41import { VideoChannelModel } from '../video/video-channel' 43import { VideoChannelModel } from '../video/video-channel'
42import { VideoCommentModel } from '../video/video-comment' 44import { VideoCommentModel } from '../video/video-comment'
43import { VideoPlaylistModel } from '../video/video-playlist' 45import { VideoPlaylistModel } from '../video/video-playlist'
44import { AccountBlocklistModel } from './account-blocklist' 46import { AccountBlocklistModel } from './account-blocklist'
45import { UserModel } from './user'
46 47
47export enum ScopeNames { 48export enum ScopeNames {
48 SUMMARY = 'SUMMARY' 49 SUMMARY = 'SUMMARY'
@@ -141,7 +142,7 @@ export type SummaryOptions = {
141 } 142 }
142 ] 143 ]
143}) 144})
144export class AccountModel extends Model { 145export class AccountModel extends Model<Partial<AttributesOnly<AccountModel>>> {
145 146
146 @AllowNull(false) 147 @AllowNull(false)
147 @Column 148 @Column
@@ -411,7 +412,6 @@ export class AccountModel extends Model {
411 id: this.id, 412 id: this.id,
412 displayName: this.getDisplayName(), 413 displayName: this.getDisplayName(),
413 description: this.description, 414 description: this.description,
414 createdAt: this.createdAt,
415 updatedAt: this.updatedAt, 415 updatedAt: this.updatedAt,
416 userId: this.userId ? this.userId : undefined 416 userId: this.userId ? this.userId : undefined
417 } 417 }
diff --git a/server/models/account/actor-custom-page.ts b/server/models/account/actor-custom-page.ts
new file mode 100644
index 000000000..893023181
--- /dev/null
+++ b/server/models/account/actor-custom-page.ts
@@ -0,0 +1,69 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { CustomPage } from '@shared/models'
3import { ActorModel } from '../actor/actor'
4import { getServerActor } from '../application/application'
5
6@Table({
7 tableName: 'actorCustomPage',
8 indexes: [
9 {
10 fields: [ 'actorId', 'type' ],
11 unique: true
12 }
13 ]
14})
15export class ActorCustomPageModel extends Model {
16
17 @AllowNull(true)
18 @Column(DataType.TEXT)
19 content: string
20
21 @AllowNull(false)
22 @Column
23 type: 'homepage'
24
25 @CreatedAt
26 createdAt: Date
27
28 @UpdatedAt
29 updatedAt: Date
30
31 @ForeignKey(() => ActorModel)
32 @Column
33 actorId: number
34
35 @BelongsTo(() => ActorModel, {
36 foreignKey: {
37 name: 'actorId',
38 allowNull: false
39 },
40 onDelete: 'cascade'
41 })
42 Actor: ActorModel
43
44 static async updateInstanceHomepage (content: string) {
45 const serverActor = await getServerActor()
46
47 return ActorCustomPageModel.upsert({
48 content,
49 actorId: serverActor.id,
50 type: 'homepage'
51 })
52 }
53
54 static async loadInstanceHomepage () {
55 const serverActor = await getServerActor()
56
57 return ActorCustomPageModel.findOne({
58 where: {
59 actorId: serverActor.id
60 }
61 })
62 }
63
64 toFormattedJSON (): CustomPage {
65 return {
66 content: this.content
67 }
68 }
69}
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/actor/actor-follow.ts
index 4c5f37620..3a09e51d6 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/actor/actor-follow.ts
@@ -28,6 +28,7 @@ import {
28 MActorFollowFormattable, 28 MActorFollowFormattable,
29 MActorFollowSubscriptions 29 MActorFollowSubscriptions
30} from '@server/types/models' 30} from '@server/types/models'
31import { AttributesOnly } from '@shared/core-utils'
31import { ActivityPubActorType } from '@shared/models' 32import { ActivityPubActorType } from '@shared/models'
32import { FollowState } from '../../../shared/models/actors' 33import { FollowState } from '../../../shared/models/actors'
33import { ActorFollow } from '../../../shared/models/actors/follow.model' 34import { ActorFollow } from '../../../shared/models/actors/follow.model'
@@ -61,7 +62,7 @@ import { ActorModel, unusedActorAttributesForAPI } from './actor'
61 } 62 }
62 ] 63 ]
63}) 64})
64export class ActorFollowModel extends Model { 65export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowModel>>> {
65 66
66 @AllowNull(false) 67 @AllowNull(false)
67 @Column(DataType.ENUM(...values(FOLLOW_STATES))) 68 @Column(DataType.ENUM(...values(FOLLOW_STATES)))
@@ -619,7 +620,7 @@ export class ActorFollowModel extends Model {
619 if (serverIds.length === 0) return 620 if (serverIds.length === 0) return
620 621
621 const me = await getServerActor() 622 const me = await getServerActor()
622 const serverIdsString = createSafeIn(ActorFollowModel, serverIds) 623 const serverIdsString = createSafeIn(ActorFollowModel.sequelize, serverIds)
623 624
624 const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + 625 const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` +
625 'WHERE id IN (' + 626 'WHERE id IN (' +
diff --git a/server/models/account/actor-image.ts b/server/models/actor/actor-image.ts
index ae05b4969..a35f9edb0 100644
--- a/server/models/account/actor-image.ts
+++ b/server/models/actor/actor-image.ts
@@ -2,6 +2,7 @@ import { remove } from 'fs-extra'
2import { join } from 'path' 2import { join } from 'path'
3import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 3import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4import { MActorImageFormattable } from '@server/types/models' 4import { MActorImageFormattable } from '@server/types/models'
5import { AttributesOnly } from '@shared/core-utils'
5import { ActorImageType } from '@shared/models' 6import { ActorImageType } from '@shared/models'
6import { ActorImage } from '../../../shared/models/actors/actor-image.model' 7import { ActorImage } from '../../../shared/models/actors/actor-image.model'
7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 8import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
@@ -19,7 +20,7 @@ import { throwIfNotValid } from '../utils'
19 } 20 }
20 ] 21 ]
21}) 22})
22export class ActorImageModel extends Model { 23export class ActorImageModel extends Model<Partial<AttributesOnly<ActorImageModel>>> {
23 24
24 @AllowNull(false) 25 @AllowNull(false)
25 @Column 26 @Column
diff --git a/server/models/activitypub/actor.ts b/server/models/actor/actor.ts
index 19f3f7e04..65c53f8f8 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/actor/actor.ts
@@ -18,6 +18,7 @@ import {
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { ModelCache } from '@server/models/model-cache' 20import { ModelCache } from '@server/models/model-cache'
21import { AttributesOnly } from '@shared/core-utils'
21import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' 22import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
22import { ActorImage } from '../../../shared/models/actors/actor-image.model' 23import { ActorImage } from '../../../shared/models/actors/actor-image.model'
23import { activityPubContextify } from '../../helpers/activitypub' 24import { activityPubContextify } from '../../helpers/activitypub'
@@ -51,12 +52,12 @@ import {
51 MActorWithInboxes 52 MActorWithInboxes
52} from '../../types/models' 53} from '../../types/models'
53import { AccountModel } from '../account/account' 54import { AccountModel } from '../account/account'
54import { ActorImageModel } from '../account/actor-image'
55import { ServerModel } from '../server/server' 55import { ServerModel } from '../server/server'
56import { isOutdated, throwIfNotValid } from '../utils' 56import { isOutdated, throwIfNotValid } from '../utils'
57import { VideoModel } from '../video/video' 57import { VideoModel } from '../video/video'
58import { VideoChannelModel } from '../video/video-channel' 58import { VideoChannelModel } from '../video/video-channel'
59import { ActorFollowModel } from './actor-follow' 59import { ActorFollowModel } from './actor-follow'
60import { ActorImageModel } from './actor-image'
60 61
61enum ScopeNames { 62enum ScopeNames {
62 FULL = 'FULL' 63 FULL = 'FULL'
@@ -69,9 +70,7 @@ export const unusedActorAttributesForAPI = [
69 'outboxUrl', 70 'outboxUrl',
70 'sharedInboxUrl', 71 'sharedInboxUrl',
71 'followersUrl', 72 'followersUrl',
72 'followingUrl', 73 'followingUrl'
73 'createdAt',
74 'updatedAt'
75] 74]
76 75
77@DefaultScope(() => ({ 76@DefaultScope(() => ({
@@ -161,7 +160,7 @@ export const unusedActorAttributesForAPI = [
161 } 160 }
162 ] 161 ]
163}) 162})
164export class ActorModel extends Model { 163export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
165 164
166 @AllowNull(false) 165 @AllowNull(false)
167 @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) 166 @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES)))
@@ -222,6 +221,10 @@ export class ActorModel extends Model {
222 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) 221 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
223 followingUrl: string 222 followingUrl: string
224 223
224 @AllowNull(true)
225 @Column
226 remoteCreatedAt: Date
227
225 @CreatedAt 228 @CreatedAt
226 createdAt: Date 229 createdAt: Date
227 230
@@ -555,8 +558,7 @@ export class ActorModel extends Model {
555 followingCount: this.followingCount, 558 followingCount: this.followingCount,
556 followersCount: this.followersCount, 559 followersCount: this.followersCount,
557 banner, 560 banner,
558 createdAt: this.createdAt, 561 createdAt: this.getCreatedAt()
559 updatedAt: this.updatedAt
560 }) 562 })
561 } 563 }
562 564
@@ -608,6 +610,7 @@ export class ActorModel extends Model {
608 owner: this.url, 610 owner: this.url,
609 publicKeyPem: this.publicKey 611 publicKeyPem: this.publicKey
610 }, 612 },
613 published: this.getCreatedAt().toISOString(),
611 icon, 614 icon,
612 image 615 image
613 } 616 }
@@ -690,4 +693,8 @@ export class ActorModel extends Model {
690 693
691 return isOutdated(this, ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL) 694 return isOutdated(this, ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL)
692 } 695 }
696
697 getCreatedAt (this: MActorAPChannel | MActorAPAccount | MActorFormattable) {
698 return this.remoteCreatedAt || this.createdAt
699 }
693} 700}
diff --git a/server/models/application/application.ts b/server/models/application/application.ts
index 21f8b1cbc..5531d134a 100644
--- a/server/models/application/application.ts
+++ b/server/models/application/application.ts
@@ -1,6 +1,7 @@
1import * as memoizee from 'memoizee'
1import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' 2import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript'
3import { AttributesOnly } from '@shared/core-utils'
2import { AccountModel } from '../account/account' 4import { AccountModel } from '../account/account'
3import * as memoizee from 'memoizee'
4 5
5export const getServerActor = memoizee(async function () { 6export const getServerActor = memoizee(async function () {
6 const application = await ApplicationModel.load() 7 const application = await ApplicationModel.load()
@@ -24,7 +25,7 @@ export const getServerActor = memoizee(async function () {
24 tableName: 'application', 25 tableName: 'application',
25 timestamps: false 26 timestamps: false
26}) 27})
27export class ApplicationModel extends Model { 28export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationModel>>> {
28 29
29 @AllowNull(false) 30 @AllowNull(false)
30 @Default(0) 31 @Default(0)
diff --git a/server/models/oauth/oauth-client.ts b/server/models/oauth/oauth-client.ts
index 8dbc1c2f5..890954bdb 100644
--- a/server/models/oauth/oauth-client.ts
+++ b/server/models/oauth/oauth-client.ts
@@ -1,4 +1,5 @@
1import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { AttributesOnly } from '@shared/core-utils'
2import { OAuthTokenModel } from './oauth-token' 3import { OAuthTokenModel } from './oauth-token'
3 4
4@Table({ 5@Table({
@@ -14,7 +15,7 @@ import { OAuthTokenModel } from './oauth-token'
14 } 15 }
15 ] 16 ]
16}) 17})
17export class OAuthClientModel extends Model { 18export class OAuthClientModel extends Model<Partial<AttributesOnly<OAuthClientModel>>> {
18 19
19 @AllowNull(false) 20 @AllowNull(false)
20 @Column 21 @Column
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts
index 27e643aa7..af4b0ec42 100644
--- a/server/models/oauth/oauth-token.ts
+++ b/server/models/oauth/oauth-token.ts
@@ -15,10 +15,11 @@ import {
15import { TokensCache } from '@server/lib/auth/tokens-cache' 15import { TokensCache } from '@server/lib/auth/tokens-cache'
16import { MUserAccountId } from '@server/types/models' 16import { MUserAccountId } from '@server/types/models'
17import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' 17import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token'
18import { AttributesOnly } from '@shared/core-utils'
18import { logger } from '../../helpers/logger' 19import { logger } from '../../helpers/logger'
19import { AccountModel } from '../account/account' 20import { AccountModel } from '../account/account'
20import { UserModel } from '../account/user' 21import { ActorModel } from '../actor/actor'
21import { ActorModel } from '../activitypub/actor' 22import { UserModel } from '../user/user'
22import { OAuthClientModel } from './oauth-client' 23import { OAuthClientModel } from './oauth-client'
23 24
24export type OAuthTokenInfo = { 25export type OAuthTokenInfo = {
@@ -78,7 +79,7 @@ enum ScopeNames {
78 } 79 }
79 ] 80 ]
80}) 81})
81export class OAuthTokenModel extends Model { 82export class OAuthTokenModel extends Model<Partial<AttributesOnly<OAuthTokenModel>>> {
82 83
83 @AllowNull(false) 84 @AllowNull(false)
84 @Column 85 @Column
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 53ebadeaf..ca56a57dc 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -1,5 +1,5 @@
1import { sample } from 'lodash' 1import { sample } from 'lodash'
2import { FindOptions, literal, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize' 2import { literal, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize'
3import { 3import {
4 AllowNull, 4 AllowNull,
5 BeforeDestroy, 5 BeforeDestroy,
@@ -16,6 +16,7 @@ import {
16} from 'sequelize-typescript' 16} from 'sequelize-typescript'
17import { getServerActor } from '@server/models/application/application' 17import { getServerActor } from '@server/models/application/application'
18import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models' 18import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models'
19import { AttributesOnly } from '@shared/core-utils'
19import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model' 20import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model'
20import { 21import {
21 FileRedundancyInformation, 22 FileRedundancyInformation,
@@ -29,7 +30,7 @@ import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validato
29import { logger } from '../../helpers/logger' 30import { logger } from '../../helpers/logger'
30import { CONFIG } from '../../initializers/config' 31import { CONFIG } from '../../initializers/config'
31import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' 32import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants'
32import { ActorModel } from '../activitypub/actor' 33import { ActorModel } from '../actor/actor'
33import { ServerModel } from '../server/server' 34import { ServerModel } from '../server/server'
34import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' 35import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils'
35import { ScheduleVideoUpdateModel } from '../video/schedule-video-update' 36import { ScheduleVideoUpdateModel } from '../video/schedule-video-update'
@@ -84,7 +85,7 @@ export enum ScopeNames {
84 } 85 }
85 ] 86 ]
86}) 87})
87export class VideoRedundancyModel extends Model { 88export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedundancyModel>>> {
88 89
89 @CreatedAt 90 @CreatedAt
90 createdAt: Date 91 createdAt: Date
@@ -407,50 +408,6 @@ export class VideoRedundancyModel extends Model {
407 return VideoRedundancyModel.scope([ ScopeNames.WITH_VIDEO ]).findOne(query) 408 return VideoRedundancyModel.scope([ ScopeNames.WITH_VIDEO ]).findOne(query)
408 } 409 }
409 410
410 static async getTotalDuplicated (strategy: VideoRedundancyStrategy) {
411 const actor = await getServerActor()
412 const redundancyInclude = {
413 attributes: [],
414 model: VideoRedundancyModel,
415 required: true,
416 where: {
417 actorId: actor.id,
418 strategy
419 }
420 }
421
422 const queryFiles: FindOptions = {
423 include: [ redundancyInclude ]
424 }
425
426 const queryStreamingPlaylists: FindOptions = {
427 include: [
428 {
429 attributes: [],
430 model: VideoModel.unscoped(),
431 required: true,
432 include: [
433 {
434 required: true,
435 attributes: [],
436 model: VideoStreamingPlaylistModel.unscoped(),
437 include: [
438 redundancyInclude
439 ]
440 }
441 ]
442 }
443 ]
444 }
445
446 return Promise.all([
447 VideoFileModel.aggregate('size', 'SUM', queryFiles),
448 VideoFileModel.aggregate('size', 'SUM', queryStreamingPlaylists)
449 ]).then(([ r1, r2 ]) => {
450 return parseAggregateResult(r1) + parseAggregateResult(r2)
451 })
452 }
453
454 static async listLocalExpired () { 411 static async listLocalExpired () {
455 const actor = await getServerActor() 412 const actor = await getServerActor()
456 413
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts
index 82387af6a..a8de64dd4 100644
--- a/server/models/server/plugin.ts
+++ b/server/models/server/plugin.ts
@@ -1,9 +1,8 @@
1import { FindAndCountOptions, json, QueryTypes } from 'sequelize' 1import { FindAndCountOptions, json, QueryTypes } from 'sequelize'
2import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { MPlugin, MPluginFormattable } from '@server/types/models' 3import { MPlugin, MPluginFormattable } from '@server/types/models'
4import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' 4import { AttributesOnly } from '@shared/core-utils'
5import { PluginType } from '../../../shared/models/plugins/plugin.type' 5import { PeerTubePlugin, PluginType, RegisterServerSettingOptions } from '../../../shared/models'
6import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
7import { 6import {
8 isPluginDescriptionValid, 7 isPluginDescriptionValid,
9 isPluginHomepage, 8 isPluginHomepage,
@@ -28,7 +27,7 @@ import { getSort, throwIfNotValid } from '../utils'
28 } 27 }
29 ] 28 ]
30}) 29})
31export class PluginModel extends Model { 30export class PluginModel extends Model<Partial<AttributesOnly<PluginModel>>> {
32 31
33 @AllowNull(false) 32 @AllowNull(false)
34 @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) 33 @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name'))
@@ -284,7 +283,7 @@ export class PluginModel extends Model {
284 for (const r of registeredSettings) { 283 for (const r of registeredSettings) {
285 if (r.private !== false) continue 284 if (r.private !== false) continue
286 285
287 result[r.name] = settings[r.name] || r.default || null 286 result[r.name] = settings[r.name] ?? r.default ?? null
288 } 287 }
289 288
290 return result 289 return result
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts
index 4dc236537..b3579d589 100644
--- a/server/models/server/server-blocklist.ts
+++ b/server/models/server/server-blocklist.ts
@@ -1,6 +1,7 @@
1import { Op } from 'sequelize' 1import { Op } from 'sequelize'
2import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models' 3import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
4import { ServerBlock } from '@shared/models' 5import { ServerBlock } from '@shared/models'
5import { AccountModel } from '../account/account' 6import { AccountModel } from '../account/account'
6import { getSort, searchAttribute } from '../utils' 7import { getSort, searchAttribute } from '../utils'
@@ -42,7 +43,7 @@ enum ScopeNames {
42 } 43 }
43 ] 44 ]
44}) 45})
45export class ServerBlocklistModel extends Model { 46export class ServerBlocklistModel extends Model<Partial<AttributesOnly<ServerBlocklistModel>>> {
46 47
47 @CreatedAt 48 @CreatedAt
48 createdAt: Date 49 createdAt: Date
diff --git a/server/models/server/server.ts b/server/models/server/server.ts
index 0e58beeaf..25d9924fb 100644
--- a/server/models/server/server.ts
+++ b/server/models/server/server.ts
@@ -1,7 +1,8 @@
1import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { MServer, MServerFormattable } from '@server/types/models/server' 2import { MServer, MServerFormattable } from '@server/types/models/server'
3import { AttributesOnly } from '@shared/core-utils'
3import { isHostValid } from '../../helpers/custom-validators/servers' 4import { isHostValid } from '../../helpers/custom-validators/servers'
4import { ActorModel } from '../activitypub/actor' 5import { ActorModel } from '../actor/actor'
5import { throwIfNotValid } from '../utils' 6import { throwIfNotValid } from '../utils'
6import { ServerBlocklistModel } from './server-blocklist' 7import { ServerBlocklistModel } from './server-blocklist'
7 8
@@ -14,7 +15,7 @@ import { ServerBlocklistModel } from './server-blocklist'
14 } 15 }
15 ] 16 ]
16}) 17})
17export class ServerModel extends Model { 18export class ServerModel extends Model<Partial<AttributesOnly<ServerModel>>> {
18 19
19 @AllowNull(false) 20 @AllowNull(false)
20 @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) 21 @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host'))
diff --git a/server/models/server/tracker.ts b/server/models/server/tracker.ts
index 97520f92d..c09fdd64b 100644
--- a/server/models/server/tracker.ts
+++ b/server/models/server/tracker.ts
@@ -1,6 +1,7 @@
1import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { Transaction } from 'sequelize/types' 2import { Transaction } from 'sequelize/types'
3import { MTracker } from '@server/types/models/server/tracker' 3import { MTracker } from '@server/types/models/server/tracker'
4import { AttributesOnly } from '@shared/core-utils'
4import { VideoModel } from '../video/video' 5import { VideoModel } from '../video/video'
5import { VideoTrackerModel } from './video-tracker' 6import { VideoTrackerModel } from './video-tracker'
6 7
@@ -13,7 +14,7 @@ import { VideoTrackerModel } from './video-tracker'
13 } 14 }
14 ] 15 ]
15}) 16})
16export class TrackerModel extends Model { 17export class TrackerModel extends Model<Partial<AttributesOnly<TrackerModel>>> {
17 18
18 @AllowNull(false) 19 @AllowNull(false)
19 @Column 20 @Column
diff --git a/server/models/server/video-tracker.ts b/server/models/server/video-tracker.ts
index 367bf0117..c49fbd1c6 100644
--- a/server/models/server/video-tracker.ts
+++ b/server/models/server/video-tracker.ts
@@ -1,4 +1,5 @@
1import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { AttributesOnly } from '@shared/core-utils'
2import { VideoModel } from '../video/video' 3import { VideoModel } from '../video/video'
3import { TrackerModel } from './tracker' 4import { TrackerModel } from './tracker'
4 5
@@ -13,7 +14,7 @@ import { TrackerModel } from './tracker'
13 } 14 }
14 ] 15 ]
15}) 16})
16export class VideoTrackerModel extends Model { 17export class VideoTrackerModel extends Model<Partial<AttributesOnly<VideoTrackerModel>>> {
17 @CreatedAt 18 @CreatedAt
18 createdAt: Date 19 createdAt: Date
19 20
diff --git a/server/models/account/user-notification-setting.ts b/server/models/user/user-notification-setting.ts
index 138051528..bee7d7851 100644
--- a/server/models/account/user-notification-setting.ts
+++ b/server/models/user/user-notification-setting.ts
@@ -14,6 +14,7 @@ import {
14} from 'sequelize-typescript' 14} from 'sequelize-typescript'
15import { TokensCache } from '@server/lib/auth/tokens-cache' 15import { TokensCache } from '@server/lib/auth/tokens-cache'
16import { MNotificationSettingFormattable } from '@server/types/models' 16import { MNotificationSettingFormattable } from '@server/types/models'
17import { AttributesOnly } from '@shared/core-utils'
17import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' 18import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model'
18import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' 19import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
19import { throwIfNotValid } from '../utils' 20import { throwIfNotValid } from '../utils'
@@ -28,7 +29,7 @@ import { UserModel } from './user'
28 } 29 }
29 ] 30 ]
30}) 31})
31export class UserNotificationSettingModel extends Model { 32export class UserNotificationSettingModel extends Model<Partial<AttributesOnly<UserNotificationSettingModel>>> {
32 33
33 @AllowNull(false) 34 @AllowNull(false)
34 @Default(null) 35 @Default(null)
diff --git a/server/models/account/user-notification.ts b/server/models/user/user-notification.ts
index 805095002..a7f84e9ca 100644
--- a/server/models/account/user-notification.ts
+++ b/server/models/user/user-notification.ts
@@ -1,14 +1,17 @@
1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' 1import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' 3import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
4import { AttributesOnly } from '@shared/core-utils'
4import { UserNotification, UserNotificationType } from '../../../shared' 5import { UserNotification, UserNotificationType } from '../../../shared'
5import { isBooleanValid } from '../../helpers/custom-validators/misc' 6import { isBooleanValid } from '../../helpers/custom-validators/misc'
6import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' 7import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications'
7import { AbuseModel } from '../abuse/abuse' 8import { AbuseModel } from '../abuse/abuse'
8import { VideoAbuseModel } from '../abuse/video-abuse' 9import { VideoAbuseModel } from '../abuse/video-abuse'
9import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' 10import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
10import { ActorModel } from '../activitypub/actor' 11import { AccountModel } from '../account/account'
11import { ActorFollowModel } from '../activitypub/actor-follow' 12import { ActorModel } from '../actor/actor'
13import { ActorFollowModel } from '../actor/actor-follow'
14import { ActorImageModel } from '../actor/actor-image'
12import { ApplicationModel } from '../application/application' 15import { ApplicationModel } from '../application/application'
13import { PluginModel } from '../server/plugin' 16import { PluginModel } from '../server/plugin'
14import { ServerModel } from '../server/server' 17import { ServerModel } from '../server/server'
@@ -18,8 +21,6 @@ import { VideoBlacklistModel } from '../video/video-blacklist'
18import { VideoChannelModel } from '../video/video-channel' 21import { VideoChannelModel } from '../video/video-channel'
19import { VideoCommentModel } from '../video/video-comment' 22import { VideoCommentModel } from '../video/video-comment'
20import { VideoImportModel } from '../video/video-import' 23import { VideoImportModel } from '../video/video-import'
21import { AccountModel } from './account'
22import { ActorImageModel } from './actor-image'
23import { UserModel } from './user' 24import { UserModel } from './user'
24 25
25enum ScopeNames { 26enum ScopeNames {
@@ -286,7 +287,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
286 } 287 }
287 ] as (ModelIndexesOptions & { where?: WhereOptions })[] 288 ] as (ModelIndexesOptions & { where?: WhereOptions })[]
288}) 289})
289export class UserNotificationModel extends Model { 290export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNotificationModel>>> {
290 291
291 @AllowNull(false) 292 @AllowNull(false)
292 @Default(null) 293 @Default(null)
diff --git a/server/models/account/user-video-history.ts b/server/models/user/user-video-history.ts
index 6be1d65ea..e3dc4a062 100644
--- a/server/models/account/user-video-history.ts
+++ b/server/models/user/user-video-history.ts
@@ -1,8 +1,9 @@
1import { DestroyOptions, Op, Transaction } from 'sequelize'
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { MUserAccountId, MUserId } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
2import { VideoModel } from '../video/video' 5import { VideoModel } from '../video/video'
3import { UserModel } from './user' 6import { UserModel } from './user'
4import { DestroyOptions, Op, Transaction } from 'sequelize'
5import { MUserAccountId, MUserId } from '@server/types/models'
6 7
7@Table({ 8@Table({
8 tableName: 'userVideoHistory', 9 tableName: 'userVideoHistory',
@@ -19,7 +20,7 @@ import { MUserAccountId, MUserId } from '@server/types/models'
19 } 20 }
20 ] 21 ]
21}) 22})
22export class UserVideoHistoryModel extends Model { 23export class UserVideoHistoryModel extends Model<Partial<AttributesOnly<UserVideoHistoryModel>>> {
23 @CreatedAt 24 @CreatedAt
24 createdAt: Date 25 createdAt: Date
25 26
diff --git a/server/models/account/user.ts b/server/models/user/user.ts
index 00c6d73aa..20696b1f4 100644
--- a/server/models/account/user.ts
+++ b/server/models/user/user.ts
@@ -31,6 +31,7 @@ import {
31 MUserWithNotificationSetting, 31 MUserWithNotificationSetting,
32 MVideoWithRights 32 MVideoWithRights
33} from '@server/types/models' 33} from '@server/types/models'
34import { AttributesOnly } from '@shared/core-utils'
34import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' 35import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users'
35import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' 36import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models'
36import { User, UserRole } from '../../../shared/models/users' 37import { User, UserRole } from '../../../shared/models/users'
@@ -60,8 +61,10 @@ import {
60import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' 61import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
61import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants' 62import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants'
62import { getThemeOrDefault } from '../../lib/plugins/theme-utils' 63import { getThemeOrDefault } from '../../lib/plugins/theme-utils'
63import { ActorModel } from '../activitypub/actor' 64import { AccountModel } from '../account/account'
64import { ActorFollowModel } from '../activitypub/actor-follow' 65import { ActorModel } from '../actor/actor'
66import { ActorFollowModel } from '../actor/actor-follow'
67import { ActorImageModel } from '../actor/actor-image'
65import { OAuthTokenModel } from '../oauth/oauth-token' 68import { OAuthTokenModel } from '../oauth/oauth-token'
66import { getSort, throwIfNotValid } from '../utils' 69import { getSort, throwIfNotValid } from '../utils'
67import { VideoModel } from '../video/video' 70import { VideoModel } from '../video/video'
@@ -69,9 +72,7 @@ import { VideoChannelModel } from '../video/video-channel'
69import { VideoImportModel } from '../video/video-import' 72import { VideoImportModel } from '../video/video-import'
70import { VideoLiveModel } from '../video/video-live' 73import { VideoLiveModel } from '../video/video-live'
71import { VideoPlaylistModel } from '../video/video-playlist' 74import { VideoPlaylistModel } from '../video/video-playlist'
72import { AccountModel } from './account'
73import { UserNotificationSettingModel } from './user-notification-setting' 75import { UserNotificationSettingModel } from './user-notification-setting'
74import { ActorImageModel } from './actor-image'
75 76
76enum ScopeNames { 77enum ScopeNames {
77 FOR_ME_API = 'FOR_ME_API', 78 FOR_ME_API = 'FOR_ME_API',
@@ -233,7 +234,7 @@ enum ScopeNames {
233 } 234 }
234 ] 235 ]
235}) 236})
236export class UserModel extends Model { 237export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
237 238
238 @AllowNull(true) 239 @AllowNull(true)
239 @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) 240 @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true))
@@ -565,6 +566,10 @@ export class UserModel extends Model {
565 return UserModel.unscoped().findByPk(id) 566 return UserModel.unscoped().findByPk(id)
566 } 567 }
567 568
569 static loadByIdFull (id: number): Promise<MUserDefault> {
570 return UserModel.findByPk(id)
571 }
572
568 static loadByIdWithChannels (id: number, withStats = false): Promise<MUserDefault> { 573 static loadByIdWithChannels (id: number, withStats = false): Promise<MUserDefault> {
569 const scopes = [ 574 const scopes = [
570 ScopeNames.WITH_VIDEOCHANNELS 575 ScopeNames.WITH_VIDEOCHANNELS
diff --git a/server/models/utils.ts b/server/models/utils.ts
index ec51c66bf..e27625bc8 100644
--- a/server/models/utils.ts
+++ b/server/models/utils.ts
@@ -1,5 +1,4 @@
1import { literal, Op, OrderItem } from 'sequelize' 1import { literal, Op, OrderItem, Sequelize } from 'sequelize'
2import { Model, Sequelize } from 'sequelize-typescript'
3import { Col } from 'sequelize/types/lib/utils' 2import { Col } from 'sequelize/types/lib/utils'
4import validator from 'validator' 3import validator from 'validator'
5 4
@@ -195,11 +194,11 @@ function parseAggregateResult (result: any) {
195 return total 194 return total
196} 195}
197 196
198const createSafeIn = (model: typeof Model, stringArr: (string | number)[]) => { 197function createSafeIn (sequelize: Sequelize, stringArr: (string | number)[]) {
199 return stringArr.map(t => { 198 return stringArr.map(t => {
200 return t === null 199 return t === null
201 ? null 200 ? null
202 : model.sequelize.escape('' + t) 201 : sequelize.escape('' + t)
203 }).join(', ') 202 }).join(', ')
204} 203}
205 204
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts
index 22b08e91a..b0952c431 100644
--- a/server/models/video/schedule-video-update.ts
+++ b/server/models/video/schedule-video-update.ts
@@ -1,8 +1,9 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { ScopeNames as VideoScopeNames, VideoModel } from './video'
3import { VideoPrivacy } from '../../../shared/models/videos'
4import { Op, Transaction } from 'sequelize' 1import { Op, Transaction } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
5import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models' 3import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
5import { VideoPrivacy } from '../../../shared/models/videos'
6import { ScopeNames as VideoScopeNames, VideoModel } from './video'
6 7
7@Table({ 8@Table({
8 tableName: 'scheduleVideoUpdate', 9 tableName: 'scheduleVideoUpdate',
@@ -16,7 +17,7 @@ import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@
16 } 17 }
17 ] 18 ]
18}) 19})
19export class ScheduleVideoUpdateModel extends Model { 20export class ScheduleVideoUpdateModel extends Model<Partial<AttributesOnly<ScheduleVideoUpdateModel>>> {
20 21
21 @AllowNull(false) 22 @AllowNull(false)
22 @Default(null) 23 @Default(null)
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts
index d04205703..c1eebe27f 100644
--- a/server/models/video/tag.ts
+++ b/server/models/video/tag.ts
@@ -1,6 +1,7 @@
1import { col, fn, QueryTypes, Transaction } from 'sequelize' 1import { col, fn, QueryTypes, Transaction } from 'sequelize'
2import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { MTag } from '@server/types/models' 3import { MTag } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
4import { VideoPrivacy, VideoState } from '../../../shared/models/videos' 5import { VideoPrivacy, VideoState } from '../../../shared/models/videos'
5import { isVideoTagValid } from '../../helpers/custom-validators/videos' 6import { isVideoTagValid } from '../../helpers/custom-validators/videos'
6import { throwIfNotValid } from '../utils' 7import { throwIfNotValid } from '../utils'
@@ -21,7 +22,7 @@ import { VideoTagModel } from './video-tag'
21 } 22 }
22 ] 23 ]
23}) 24})
24export class TagModel extends Model { 25export class TagModel extends Model<Partial<AttributesOnly<TagModel>>> {
25 26
26 @AllowNull(false) 27 @AllowNull(false)
27 @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) 28 @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag'))
diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts
index f1187c8d6..3388478d9 100644
--- a/server/models/video/thumbnail.ts
+++ b/server/models/video/thumbnail.ts
@@ -17,6 +17,7 @@ import {
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import { afterCommitIfTransaction } from '@server/helpers/database-utils' 18import { afterCommitIfTransaction } from '@server/helpers/database-utils'
19import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models' 19import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models'
20import { AttributesOnly } from '@shared/core-utils'
20import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' 21import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type'
21import { logger } from '../../helpers/logger' 22import { logger } from '../../helpers/logger'
22import { CONFIG } from '../../initializers/config' 23import { CONFIG } from '../../initializers/config'
@@ -40,7 +41,7 @@ import { VideoPlaylistModel } from './video-playlist'
40 } 41 }
41 ] 42 ]
42}) 43})
43export class ThumbnailModel extends Model { 44export class ThumbnailModel extends Model<Partial<AttributesOnly<ThumbnailModel>>> {
44 45
45 @AllowNull(false) 46 @AllowNull(false)
46 @Column 47 @Column
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index aa18896da..98f4ec9c5 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -1,6 +1,7 @@
1import { FindOptions } from 'sequelize' 1import { FindOptions } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models' 3import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
4import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' 5import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos'
5import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' 6import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist'
6import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 7import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
@@ -18,7 +19,7 @@ import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel
18 } 19 }
19 ] 20 ]
20}) 21})
21export class VideoBlacklistModel extends Model { 22export class VideoBlacklistModel extends Model<Partial<AttributesOnly<VideoBlacklistModel>>> {
22 23
23 @AllowNull(true) 24 @AllowNull(true)
24 @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) 25 @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true))
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
index bfdec73e9..d2c742b66 100644
--- a/server/models/video/video-caption.ts
+++ b/server/models/video/video-caption.ts
@@ -17,6 +17,7 @@ import {
17} from 'sequelize-typescript' 17} from 'sequelize-typescript'
18import { v4 as uuidv4 } from 'uuid' 18import { v4 as uuidv4 } from 'uuid'
19import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' 19import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models'
20import { AttributesOnly } from '@shared/core-utils'
20import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' 21import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
21import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' 22import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions'
22import { logger } from '../../helpers/logger' 23import { logger } from '../../helpers/logger'
@@ -57,7 +58,7 @@ export enum ScopeNames {
57 } 58 }
58 ] 59 ]
59}) 60})
60export class VideoCaptionModel extends Model { 61export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaptionModel>>> {
61 @CreatedAt 62 @CreatedAt
62 createdAt: Date 63 createdAt: Date
63 64
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts
index 298e8bfe2..7d20a954d 100644
--- a/server/models/video/video-change-ownership.ts
+++ b/server/models/video/video-change-ownership.ts
@@ -1,5 +1,6 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
2import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' 2import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership'
3import { AttributesOnly } from '@shared/core-utils'
3import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' 4import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos'
4import { AccountModel } from '../account/account' 5import { AccountModel } from '../account/account'
5import { getSort } from '../utils' 6import { getSort } from '../utils'
@@ -53,7 +54,7 @@ enum ScopeNames {
53 ] 54 ]
54 } 55 }
55})) 56}))
56export class VideoChangeOwnershipModel extends Model { 57export class VideoChangeOwnershipModel extends Model<Partial<AttributesOnly<VideoChangeOwnershipModel>>> {
57 @CreatedAt 58 @CreatedAt
58 createdAt: Date 59 createdAt: Date
59 60
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index b7ffbd3b1..8c4357009 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,4 +1,4 @@
1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions } from 'sequelize' 1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction } from 'sequelize'
2import { 2import {
3 AllowNull, 3 AllowNull,
4 BeforeDestroy, 4 BeforeDestroy,
@@ -17,7 +17,9 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
20import { MAccountActor } from '@server/types/models' 21import { MAccountActor } from '@server/types/models'
22import { AttributesOnly } from '@shared/core-utils'
21import { ActivityPubActor } from '../../../shared/models/activitypub' 23import { ActivityPubActor } from '../../../shared/models/activitypub'
22import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' 24import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
23import { 25import {
@@ -35,9 +37,9 @@ import {
35 MChannelSummaryFormattable 37 MChannelSummaryFormattable
36} from '../../types/models/video' 38} from '../../types/models/video'
37import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' 39import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account'
38import { ActorImageModel } from '../account/actor-image' 40import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
39import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' 41import { ActorFollowModel } from '../actor/actor-follow'
40import { ActorFollowModel } from '../activitypub/actor-follow' 42import { ActorImageModel } from '../actor/actor-image'
41import { ServerModel } from '../server/server' 43import { ServerModel } from '../server/server'
42import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' 44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
43import { VideoModel } from './video' 45import { VideoModel } from './video'
@@ -245,7 +247,7 @@ export type SummaryOptions = {
245 } 247 }
246 ] 248 ]
247}) 249})
248export class VideoChannelModel extends Model { 250export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> {
249 251
250 @AllowNull(false) 252 @AllowNull(false)
251 @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) 253 @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name'))
@@ -653,7 +655,6 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
653 description: this.description, 655 description: this.description,
654 support: this.support, 656 support: this.support,
655 isLocal: this.Actor.isOwned(), 657 isLocal: this.Actor.isOwned(),
656 createdAt: this.createdAt,
657 updatedAt: this.updatedAt, 658 updatedAt: this.updatedAt,
658 ownerAccount: undefined, 659 ownerAccount: undefined,
659 videosCount, 660 videosCount,
@@ -691,4 +692,8 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
691 isOutdated () { 692 isOutdated () {
692 return this.Actor.isOutdated() 693 return this.Actor.isOutdated()
693 } 694 }
695
696 setAsUpdated (transaction: Transaction) {
697 return setAsUpdated('videoChannel', this.id, transaction)
698 }
694} 699}
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 151c2bc81..bdf5d86bc 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -16,10 +16,11 @@ import {
16} from 'sequelize-typescript' 16} from 'sequelize-typescript'
17import { getServerActor } from '@server/models/application/application' 17import { getServerActor } from '@server/models/application/application'
18import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' 18import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
19import { AttributesOnly } from '@shared/core-utils'
19import { VideoPrivacy } from '@shared/models' 20import { VideoPrivacy } from '@shared/models'
20import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' 21import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects'
21import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' 22import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
22import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/video-comment.model' 23import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/comment/video-comment.model'
23import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' 24import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
24import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 25import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
25import { regexpCapture } from '../../helpers/regexp' 26import { regexpCapture } from '../../helpers/regexp'
@@ -39,7 +40,7 @@ import {
39} from '../../types/models/video' 40} from '../../types/models/video'
40import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' 41import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
41import { AccountModel } from '../account/account' 42import { AccountModel } from '../account/account'
42import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' 43import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
43import { 44import {
44 buildBlockedAccountSQL, 45 buildBlockedAccountSQL,
45 buildBlockedAccountSQLOptimized, 46 buildBlockedAccountSQLOptimized,
@@ -173,7 +174,7 @@ export enum ScopeNames {
173 } 174 }
174 ] 175 ]
175}) 176})
176export class VideoCommentModel extends Model { 177export class VideoCommentModel extends Model<Partial<AttributesOnly<VideoCommentModel>>> {
177 @CreatedAt 178 @CreatedAt
178 createdAt: Date 179 createdAt: Date
179 180
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 1ad796104..22cf63804 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -25,6 +25,7 @@ import { logger } from '@server/helpers/logger'
25import { extractVideo } from '@server/helpers/video' 25import { extractVideo } from '@server/helpers/video'
26import { getTorrentFilePath } from '@server/lib/video-paths' 26import { getTorrentFilePath } from '@server/lib/video-paths'
27import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' 27import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models'
28import { AttributesOnly } from '@shared/core-utils'
28import { 29import {
29 isVideoFileExtnameValid, 30 isVideoFileExtnameValid,
30 isVideoFileInfoHashValid, 31 isVideoFileInfoHashValid,
@@ -149,7 +150,7 @@ export enum ScopeNames {
149 } 150 }
150 ] 151 ]
151}) 152})
152export class VideoFileModel extends Model { 153export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>>> {
153 @CreatedAt 154 @CreatedAt
154 createdAt: Date 155 createdAt: Date
155 156
@@ -401,6 +402,10 @@ export class VideoFileModel extends Model {
401 return VideoFileModel.destroy(options) 402 return VideoFileModel.destroy(options)
402 } 403 }
403 404
405 hasTorrent () {
406 return this.infoHash && this.torrentFilename
407 }
408
404 getVideoOrStreamingPlaylist (this: MVideoFileVideo | MVideoFileStreamingPlaylistVideo): MVideo | MStreamingPlaylistVideo { 409 getVideoOrStreamingPlaylist (this: MVideoFileVideo | MVideoFileStreamingPlaylistVideo): MVideo | MStreamingPlaylistVideo {
405 if (this.videoId) return (this as MVideoFileVideo).Video 410 if (this.videoId) return (this as MVideoFileVideo).Video
406 411
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts
index bcba90093..551cb2842 100644
--- a/server/models/video/video-format-utils.ts
+++ b/server/models/video/video-format-utils.ts
@@ -205,7 +205,7 @@ function videoFilesModelToFormattedJSON (
205 label: videoFile.resolution + 'p' 205 label: videoFile.resolution + 'p'
206 }, 206 },
207 207
208 magnetUri: includeMagnet && videoFile.torrentFilename 208 magnetUri: includeMagnet && videoFile.hasTorrent()
209 ? generateMagnetUri(video, videoFile, trackerUrls) 209 ? generateMagnetUri(video, videoFile, trackerUrls)
210 : undefined, 210 : undefined,
211 211
@@ -253,19 +253,21 @@ function addVideoFilesInAPAcc (
253 fps: file.fps 253 fps: file.fps
254 }) 254 })
255 255
256 acc.push({ 256 if (file.hasTorrent()) {
257 type: 'Link', 257 acc.push({
258 mediaType: 'application/x-bittorrent' as 'application/x-bittorrent', 258 type: 'Link',
259 href: file.getTorrentUrl(), 259 mediaType: 'application/x-bittorrent' as 'application/x-bittorrent',
260 height: file.resolution 260 href: file.getTorrentUrl(),
261 }) 261 height: file.resolution
262 262 })
263 acc.push({ 263
264 type: 'Link', 264 acc.push({
265 mediaType: 'application/x-bittorrent;x-scheme-handler/magnet' as 'application/x-bittorrent;x-scheme-handler/magnet', 265 type: 'Link',
266 href: generateMagnetUri(video, file, trackerUrls), 266 mediaType: 'application/x-bittorrent;x-scheme-handler/magnet' as 'application/x-bittorrent;x-scheme-handler/magnet',
267 height: file.resolution 267 href: generateMagnetUri(video, file, trackerUrls),
268 }) 268 height: file.resolution
269 })
270 }
269 } 271 }
270} 272}
271 273
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts
index 8324166cc..5c73fb07c 100644
--- a/server/models/video/video-import.ts
+++ b/server/models/video/video-import.ts
@@ -13,15 +13,16 @@ import {
13 Table, 13 Table,
14 UpdatedAt 14 UpdatedAt
15} from 'sequelize-typescript' 15} from 'sequelize-typescript'
16import { afterCommitIfTransaction } from '@server/helpers/database-utils'
16import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' 17import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import'
18import { AttributesOnly } from '@shared/core-utils'
17import { VideoImport, VideoImportState } from '../../../shared' 19import { VideoImport, VideoImportState } from '../../../shared'
18import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' 20import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
19import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' 21import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
20import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants' 22import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants'
21import { UserModel } from '../account/user' 23import { UserModel } from '../user/user'
22import { getSort, throwIfNotValid } from '../utils' 24import { getSort, throwIfNotValid } from '../utils'
23import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' 25import { ScopeNames as VideoModelScopeNames, VideoModel } from './video'
24import { afterCommitIfTransaction } from '@server/helpers/database-utils'
25 26
26@DefaultScope(() => ({ 27@DefaultScope(() => ({
27 include: [ 28 include: [
@@ -52,7 +53,7 @@ import { afterCommitIfTransaction } from '@server/helpers/database-utils'
52 } 53 }
53 ] 54 ]
54}) 55})
55export class VideoImportModel extends Model { 56export class VideoImportModel extends Model<Partial<AttributesOnly<VideoImportModel>>> {
56 @CreatedAt 57 @CreatedAt
57 createdAt: Date 58 createdAt: Date
58 59
diff --git a/server/models/video/video-live.ts b/server/models/video/video-live.ts
index cb4a9b896..014491d50 100644
--- a/server/models/video/video-live.ts
+++ b/server/models/video/video-live.ts
@@ -1,6 +1,7 @@
1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { WEBSERVER } from '@server/initializers/constants' 2import { WEBSERVER } from '@server/initializers/constants'
3import { MVideoLive, MVideoLiveVideo } from '@server/types/models' 3import { MVideoLive, MVideoLiveVideo } from '@server/types/models'
4import { AttributesOnly } from '@shared/core-utils'
4import { LiveVideo, VideoState } from '@shared/models' 5import { LiveVideo, VideoState } from '@shared/models'
5import { VideoModel } from './video' 6import { VideoModel } from './video'
6import { VideoBlacklistModel } from './video-blacklist' 7import { VideoBlacklistModel } from './video-blacklist'
@@ -28,7 +29,7 @@ import { VideoBlacklistModel } from './video-blacklist'
28 } 29 }
29 ] 30 ]
30}) 31})
31export class VideoLiveModel extends Model { 32export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel>>> {
32 33
33 @AllowNull(true) 34 @AllowNull(true)
34 @Column(DataType.STRING) 35 @Column(DataType.STRING)
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts
index d2d7e2740..e6906cb19 100644
--- a/server/models/video/video-playlist-element.ts
+++ b/server/models/video/video-playlist-element.ts
@@ -32,6 +32,7 @@ import { AccountModel } from '../account/account'
32import { getSort, throwIfNotValid } from '../utils' 32import { getSort, throwIfNotValid } from '../utils'
33import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' 33import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video'
34import { VideoPlaylistModel } from './video-playlist' 34import { VideoPlaylistModel } from './video-playlist'
35import { AttributesOnly } from '@shared/core-utils'
35 36
36@Table({ 37@Table({
37 tableName: 'videoPlaylistElement', 38 tableName: 'videoPlaylistElement',
@@ -48,7 +49,7 @@ import { VideoPlaylistModel } from './video-playlist'
48 } 49 }
49 ] 50 ]
50}) 51})
51export class VideoPlaylistElementModel extends Model { 52export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<VideoPlaylistElementModel>>> {
52 @CreatedAt 53 @CreatedAt
53 createdAt: Date 54 createdAt: Date
54 55
@@ -274,7 +275,8 @@ export class VideoPlaylistElementModel extends Model {
274 validate: false // We use a literal to update the position 275 validate: false // We use a literal to update the position
275 } 276 }
276 277
277 return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query) 278 const positionQuery = Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`)
279 return VideoPlaylistElementModel.update({ position: positionQuery as any }, query)
278 } 280 }
279 281
280 static increasePositionOf ( 282 static increasePositionOf (
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index efe5be36d..c293287d3 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -19,6 +19,7 @@ import {
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { v4 as uuidv4 } from 'uuid' 20import { v4 as uuidv4 } from 'uuid'
21import { MAccountId, MChannelId } from '@server/types/models' 21import { MAccountId, MChannelId } from '@server/types/models'
22import { AttributesOnly } from '@shared/core-utils'
22import { ActivityIconObject } from '../../../shared/models/activitypub/objects' 23import { ActivityIconObject } from '../../../shared/models/activitypub/objects'
23import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' 24import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object'
24import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' 25import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
@@ -50,11 +51,11 @@ import {
50 MVideoPlaylistIdWithElements 51 MVideoPlaylistIdWithElements
51} from '../../types/models/video/video-playlist' 52} from '../../types/models/video/video-playlist'
52import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' 53import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account'
54import { ActorModel } from '../actor/actor'
53import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getPlaylistSort, isOutdated, throwIfNotValid } from '../utils' 55import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getPlaylistSort, isOutdated, throwIfNotValid } from '../utils'
54import { ThumbnailModel } from './thumbnail' 56import { ThumbnailModel } from './thumbnail'
55import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' 57import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel'
56import { VideoPlaylistElementModel } from './video-playlist-element' 58import { VideoPlaylistElementModel } from './video-playlist-element'
57import { ActorModel } from '../activitypub/actor'
58 59
59enum ScopeNames { 60enum ScopeNames {
60 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 61 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
@@ -221,7 +222,7 @@ type AvailableForListOptions = {
221 } 222 }
222 ] 223 ]
223}) 224})
224export class VideoPlaylistModel extends Model { 225export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlaylistModel>>> {
225 @CreatedAt 226 @CreatedAt
226 createdAt: Date 227 createdAt: Date
227 228
diff --git a/server/models/video/video-query-builder.ts b/server/models/video/video-query-builder.ts
index 155afe64b..2aa5e65c8 100644
--- a/server/models/video/video-query-builder.ts
+++ b/server/models/video/video-query-builder.ts
@@ -1,9 +1,9 @@
1import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' 1import { Sequelize } from 'sequelize/types'
2import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
3import { Model } from 'sequelize-typescript'
4import { MUserAccountId, MUserId } from '@server/types/models'
5import validator from 'validator' 2import validator from 'validator'
6import { exists } from '@server/helpers/custom-validators/misc' 3import { exists } from '@server/helpers/custom-validators/misc'
4import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
5import { MUserAccountId, MUserId } from '@server/types/models'
6import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
7 7
8export type BuildVideosQueryOptions = { 8export type BuildVideosQueryOptions = {
9 attributes?: string[] 9 attributes?: string[]
@@ -55,7 +55,7 @@ export type BuildVideosQueryOptions = {
55 having?: string 55 having?: string
56} 56}
57 57
58function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) { 58function buildListQuery (sequelize: Sequelize, options: BuildVideosQueryOptions) {
59 const and: string[] = [] 59 const and: string[] = []
60 const joins: string[] = [] 60 const joins: string[] = []
61 const replacements: any = {} 61 const replacements: any = {}
@@ -77,7 +77,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
77 const blockerIds = [ options.serverAccountId ] 77 const blockerIds = [ options.serverAccountId ]
78 if (options.user) blockerIds.push(options.user.Account.id) 78 if (options.user) blockerIds.push(options.user.Account.id)
79 79
80 const inClause = createSafeIn(model, blockerIds) 80 const inClause = createSafeIn(sequelize, blockerIds)
81 81
82 and.push( 82 and.push(
83 'NOT EXISTS (' + 83 'NOT EXISTS (' +
@@ -179,7 +179,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
179 'EXISTS (' + 179 'EXISTS (' +
180 ' SELECT 1 FROM "videoTag" ' + 180 ' SELECT 1 FROM "videoTag" ' +
181 ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + 181 ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
182 ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsOneOfLower) + ') ' + 182 ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsOneOfLower) + ') ' +
183 ' AND "video"."id" = "videoTag"."videoId"' + 183 ' AND "video"."id" = "videoTag"."videoId"' +
184 ')' 184 ')'
185 ) 185 )
@@ -192,7 +192,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
192 'EXISTS (' + 192 'EXISTS (' +
193 ' SELECT 1 FROM "videoTag" ' + 193 ' SELECT 1 FROM "videoTag" ' +
194 ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + 194 ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' +
195 ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsAllOfLower) + ') ' + 195 ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsAllOfLower) + ') ' +
196 ' AND "video"."id" = "videoTag"."videoId" ' + 196 ' AND "video"."id" = "videoTag"."videoId" ' +
197 ' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length + 197 ' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length +
198 ')' 198 ')'
@@ -232,7 +232,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
232 languagesQueryParts.push( 232 languagesQueryParts.push(
233 'EXISTS (' + 233 'EXISTS (' +
234 ' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' + 234 ' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' +
235 ' IN (' + createSafeIn(model, languages) + ') AND ' + 235 ' IN (' + createSafeIn(sequelize, languages) + ') AND ' +
236 ' "videoCaption"."videoId" = "video"."id"' + 236 ' "videoCaption"."videoId" = "video"."id"' +
237 ')' 237 ')'
238 ) 238 )
@@ -345,8 +345,8 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions)
345 } 345 }
346 346
347 if (options.search) { 347 if (options.search) {
348 const escapedSearch = model.sequelize.escape(options.search) 348 const escapedSearch = sequelize.escape(options.search)
349 const escapedLikeSearch = model.sequelize.escape('%' + options.search + '%') 349 const escapedLikeSearch = sequelize.escape('%' + options.search + '%')
350 350
351 cte.push( 351 cte.push(
352 '"trigramSearch" AS (' + 352 '"trigramSearch" AS (' +
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index 5059c1fa6..505c305e2 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -1,10 +1,11 @@
1import { literal, Op, QueryTypes, Transaction } from 'sequelize' 1import { literal, Op, QueryTypes, Transaction } from 'sequelize'
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { AttributesOnly } from '@shared/core-utils'
3import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 4import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
4import { CONSTRAINTS_FIELDS } from '../../initializers/constants' 5import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
5import { MActorDefault } from '../../types/models' 6import { MActorDefault } from '../../types/models'
6import { MVideoShareActor, MVideoShareFull } from '../../types/models/video' 7import { MVideoShareActor, MVideoShareFull } from '../../types/models/video'
7import { ActorModel } from '../activitypub/actor' 8import { ActorModel } from '../actor/actor'
8import { buildLocalActorIdsIn, throwIfNotValid } from '../utils' 9import { buildLocalActorIdsIn, throwIfNotValid } from '../utils'
9import { VideoModel } from './video' 10import { VideoModel } from './video'
10 11
@@ -50,7 +51,7 @@ enum ScopeNames {
50 } 51 }
51 ] 52 ]
52}) 53})
53export class VideoShareModel extends Model { 54export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareModel>>> {
54 55
55 @AllowNull(false) 56 @AllowNull(false)
56 @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) 57 @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index c9375b433..d627e8c9d 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -13,6 +13,7 @@ import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_
13import { VideoRedundancyModel } from '../redundancy/video-redundancy' 13import { VideoRedundancyModel } from '../redundancy/video-redundancy'
14import { throwIfNotValid } from '../utils' 14import { throwIfNotValid } from '../utils'
15import { VideoModel } from './video' 15import { VideoModel } from './video'
16import { AttributesOnly } from '@shared/core-utils'
16 17
17@Table({ 18@Table({
18 tableName: 'videoStreamingPlaylist', 19 tableName: 'videoStreamingPlaylist',
@@ -30,7 +31,7 @@ import { VideoModel } from './video'
30 } 31 }
31 ] 32 ]
32}) 33})
33export class VideoStreamingPlaylistModel extends Model { 34export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<VideoStreamingPlaylistModel>>> {
34 @CreatedAt 35 @CreatedAt
35 createdAt: Date 36 createdAt: Date
36 37
diff --git a/server/models/video/video-tag.ts b/server/models/video/video-tag.ts
index 5052b8c4d..1285d375b 100644
--- a/server/models/video/video-tag.ts
+++ b/server/models/video/video-tag.ts
@@ -1,4 +1,5 @@
1import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { AttributesOnly } from '@shared/core-utils'
2import { TagModel } from './tag' 3import { TagModel } from './tag'
3import { VideoModel } from './video' 4import { VideoModel } from './video'
4 5
@@ -13,7 +14,7 @@ import { VideoModel } from './video'
13 } 14 }
14 ] 15 ]
15}) 16})
16export class VideoTagModel extends Model { 17export class VideoTagModel extends Model<Partial<AttributesOnly<VideoTagModel>>> {
17 @CreatedAt 18 @CreatedAt
18 createdAt: Date 19 createdAt: Date
19 20
diff --git a/server/models/video/video-view.ts b/server/models/video/video-view.ts
index 992cf258a..dfc6296ce 100644
--- a/server/models/video/video-view.ts
+++ b/server/models/video/video-view.ts
@@ -1,6 +1,7 @@
1import * as Sequelize from 'sequelize'
1import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript' 2import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript'
3import { AttributesOnly } from '@shared/core-utils'
2import { VideoModel } from './video' 4import { VideoModel } from './video'
3import * as Sequelize from 'sequelize'
4 5
5@Table({ 6@Table({
6 tableName: 'videoView', 7 tableName: 'videoView',
@@ -14,7 +15,7 @@ import * as Sequelize from 'sequelize'
14 } 15 }
15 ] 16 ]
16}) 17})
17export class VideoViewModel extends Model { 18export class VideoViewModel extends Model<Partial<AttributesOnly<VideoViewModel>>> {
18 @CreatedAt 19 @CreatedAt
19 createdAt: Date 20 createdAt: Date
20 21
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index e55a21a6b..d4a258187 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -24,12 +24,14 @@ import {
24 Table, 24 Table,
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { setAsUpdated } from '@server/helpers/database-utils'
27import { buildNSFWFilter } from '@server/helpers/express-utils' 28import { buildNSFWFilter } from '@server/helpers/express-utils'
28import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
29import { LiveManager } from '@server/lib/live-manager' 30import { LiveManager } from '@server/lib/live-manager'
30import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' 31import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths'
31import { getServerActor } from '@server/models/application/application' 32import { getServerActor } from '@server/models/application/application'
32import { ModelCache } from '@server/models/model-cache' 33import { ModelCache } from '@server/models/model-cache'
34import { AttributesOnly } from '@shared/core-utils'
33import { VideoFile } from '@shared/models/videos/video-file.model' 35import { VideoFile } from '@shared/models/videos/video-file.model'
34import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' 36import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared'
35import { VideoObject } from '../../../shared/models/activitypub/objects' 37import { VideoObject } from '../../../shared/models/activitypub/objects'
@@ -99,14 +101,14 @@ import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../types/models
99import { VideoAbuseModel } from '../abuse/video-abuse' 101import { VideoAbuseModel } from '../abuse/video-abuse'
100import { AccountModel } from '../account/account' 102import { AccountModel } from '../account/account'
101import { AccountVideoRateModel } from '../account/account-video-rate' 103import { AccountVideoRateModel } from '../account/account-video-rate'
102import { ActorImageModel } from '../account/actor-image' 104import { ActorModel } from '../actor/actor'
103import { UserModel } from '../account/user' 105import { ActorImageModel } from '../actor/actor-image'
104import { UserVideoHistoryModel } from '../account/user-video-history'
105import { ActorModel } from '../activitypub/actor'
106import { VideoRedundancyModel } from '../redundancy/video-redundancy' 106import { VideoRedundancyModel } from '../redundancy/video-redundancy'
107import { ServerModel } from '../server/server' 107import { ServerModel } from '../server/server'
108import { TrackerModel } from '../server/tracker' 108import { TrackerModel } from '../server/tracker'
109import { VideoTrackerModel } from '../server/video-tracker' 109import { VideoTrackerModel } from '../server/video-tracker'
110import { UserModel } from '../user/user'
111import { UserVideoHistoryModel } from '../user/user-video-history'
110import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' 112import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils'
111import { ScheduleVideoUpdateModel } from './schedule-video-update' 113import { ScheduleVideoUpdateModel } from './schedule-video-update'
112import { TagModel } from './tag' 114import { TagModel } from './tag'
@@ -488,7 +490,7 @@ export type AvailableForListIDsOptions = {
488 } 490 }
489 ] 491 ]
490}) 492})
491export class VideoModel extends Model { 493export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
492 494
493 @AllowNull(false) 495 @AllowNull(false)
494 @Default(DataType.UUIDV4) 496 @Default(DataType.UUIDV4)
@@ -1007,6 +1009,7 @@ export class VideoModel extends Model {
1007 attributes: [ 'id' ], 1009 attributes: [ 'id' ],
1008 where: { 1010 where: {
1009 isLive: true, 1011 isLive: true,
1012 remote: false,
1010 state: VideoState.PUBLISHED 1013 state: VideoState.PUBLISHED
1011 } 1014 }
1012 } 1015 }
@@ -1616,7 +1619,7 @@ export class VideoModel extends Model {
1616 includeLocalVideos: true 1619 includeLocalVideos: true
1617 } 1620 }
1618 1621
1619 const { query, replacements } = buildListQuery(VideoModel, queryOptions) 1622 const { query, replacements } = buildListQuery(VideoModel.sequelize, queryOptions)
1620 1623
1621 return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT }) 1624 return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT })
1622 .then(rows => rows.map(r => r[field])) 1625 .then(rows => rows.map(r => r[field]))
@@ -1644,7 +1647,7 @@ export class VideoModel extends Model {
1644 if (countVideos !== true) return Promise.resolve(undefined) 1647 if (countVideos !== true) return Promise.resolve(undefined)
1645 1648
1646 const countOptions = Object.assign({}, options, { isCount: true }) 1649 const countOptions = Object.assign({}, options, { isCount: true })
1647 const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel, countOptions) 1650 const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel.sequelize, countOptions)
1648 1651
1649 return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT }) 1652 return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT })
1650 .then(rows => rows.length !== 0 ? rows[0].total : 0) 1653 .then(rows => rows.length !== 0 ? rows[0].total : 0)
@@ -1653,7 +1656,7 @@ export class VideoModel extends Model {
1653 function getModels () { 1656 function getModels () {
1654 if (options.count === 0) return Promise.resolve([]) 1657 if (options.count === 0) return Promise.resolve([])
1655 1658
1656 const { query, replacements, order } = buildListQuery(VideoModel, options) 1659 const { query, replacements, order } = buildListQuery(VideoModel.sequelize, options)
1657 const queryModels = wrapForAPIResults(query, replacements, options, order) 1660 const queryModels = wrapForAPIResults(query, replacements, options, order)
1658 1661
1659 return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true }) 1662 return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true })
@@ -2053,11 +2056,7 @@ export class VideoModel extends Model {
2053 } 2056 }
2054 2057
2055 setAsRefreshed () { 2058 setAsRefreshed () {
2056 const options = { 2059 return setAsUpdated('video', this.id)
2057 where: { id: this.id }
2058 }
2059
2060 return VideoModel.update({ updatedAt: new Date() }, options)
2061 } 2060 }
2062 2061
2063 requiresAuth () { 2062 requiresAuth () {