diff options
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/abuse/abuse-query-builder.ts | 2 | ||||
-rw-r--r-- | server/models/shared/abstract-run-query.ts | 2 | ||||
-rw-r--r-- | server/models/user/user.ts | 27 | ||||
-rw-r--r-- | server/models/utils.ts | 24 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 13 | ||||
-rw-r--r-- | server/models/video/video-live-session.ts | 2 | ||||
-rw-r--r-- | server/models/video/video-source.ts | 55 | ||||
-rw-r--r-- | server/models/video/video.ts | 10 |
8 files changed, 107 insertions, 28 deletions
diff --git a/server/models/abuse/abuse-query-builder.ts b/server/models/abuse/abuse-query-builder.ts index 025e6ba55..cfc924ba4 100644 --- a/server/models/abuse/abuse-query-builder.ts +++ b/server/models/abuse/abuse-query-builder.ts | |||
@@ -13,7 +13,7 @@ export type BuildAbusesQueryOptions = { | |||
13 | searchReporter?: string | 13 | searchReporter?: string |
14 | searchReportee?: string | 14 | searchReportee?: string |
15 | 15 | ||
16 | // video releated | 16 | // video related |
17 | searchVideo?: string | 17 | searchVideo?: string |
18 | searchVideoChannel?: string | 18 | searchVideoChannel?: string |
19 | videoIs?: AbuseVideoIs | 19 | videoIs?: AbuseVideoIs |
diff --git a/server/models/shared/abstract-run-query.ts b/server/models/shared/abstract-run-query.ts index f1182c7be..7f27a0c4b 100644 --- a/server/models/shared/abstract-run-query.ts +++ b/server/models/shared/abstract-run-query.ts | |||
@@ -2,7 +2,7 @@ import { QueryTypes, Sequelize, Transaction } from 'sequelize' | |||
2 | 2 | ||
3 | /** | 3 | /** |
4 | * | 4 | * |
5 | * Abstact builder to run video SQL queries | 5 | * Abstract builder to run video SQL queries |
6 | * | 6 | * |
7 | */ | 7 | */ |
8 | 8 | ||
diff --git a/server/models/user/user.ts b/server/models/user/user.ts index 68b2bf523..dc260e512 100644 --- a/server/models/user/user.ts +++ b/server/models/user/user.ts | |||
@@ -29,12 +29,11 @@ import { | |||
29 | MUserDefault, | 29 | MUserDefault, |
30 | MUserFormattable, | 30 | MUserFormattable, |
31 | MUserNotifSettingChannelDefault, | 31 | MUserNotifSettingChannelDefault, |
32 | MUserWithNotificationSetting, | 32 | MUserWithNotificationSetting |
33 | MVideoWithRights | ||
34 | } from '@server/types/models' | 33 | } from '@server/types/models' |
35 | import { AttributesOnly } from '@shared/typescript-utils' | 34 | import { AttributesOnly } from '@shared/typescript-utils' |
36 | import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' | 35 | import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' |
37 | import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' | 36 | import { AbuseState, MyUser, UserRight, VideoPlaylistType } from '../../../shared/models' |
38 | import { User, UserRole } from '../../../shared/models/users' | 37 | import { User, UserRole } from '../../../shared/models/users' |
39 | import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' | 38 | import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' |
40 | import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' | 39 | import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' |
@@ -66,7 +65,7 @@ import { ActorModel } from '../actor/actor' | |||
66 | import { ActorFollowModel } from '../actor/actor-follow' | 65 | import { ActorFollowModel } from '../actor/actor-follow' |
67 | import { ActorImageModel } from '../actor/actor-image' | 66 | import { ActorImageModel } from '../actor/actor-image' |
68 | import { OAuthTokenModel } from '../oauth/oauth-token' | 67 | import { OAuthTokenModel } from '../oauth/oauth-token' |
69 | import { getSort, throwIfNotValid } from '../utils' | 68 | import { getAdminUsersSort, throwIfNotValid } from '../utils' |
70 | import { VideoModel } from '../video/video' | 69 | import { VideoModel } from '../video/video' |
71 | import { VideoChannelModel } from '../video/video-channel' | 70 | import { VideoChannelModel } from '../video/video-channel' |
72 | import { VideoImportModel } from '../video/video-import' | 71 | import { VideoImportModel } from '../video/video-import' |
@@ -461,7 +460,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { | |||
461 | return this.count() | 460 | return this.count() |
462 | } | 461 | } |
463 | 462 | ||
464 | static listForApi (parameters: { | 463 | static listForAdminApi (parameters: { |
465 | start: number | 464 | start: number |
466 | count: number | 465 | count: number |
467 | sort: string | 466 | sort: string |
@@ -497,7 +496,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { | |||
497 | const query: FindOptions = { | 496 | const query: FindOptions = { |
498 | offset: start, | 497 | offset: start, |
499 | limit: count, | 498 | limit: count, |
500 | order: getSort(sort), | 499 | order: getAdminUsersSort(sort), |
501 | where | 500 | where |
502 | } | 501 | } |
503 | 502 | ||
@@ -851,22 +850,6 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { | |||
851 | .then(u => u.map(u => u.username)) | 850 | .then(u => u.map(u => u.username)) |
852 | } | 851 | } |
853 | 852 | ||
854 | canGetVideo (video: MVideoWithRights) { | ||
855 | const videoUserId = video.VideoChannel.Account.userId | ||
856 | |||
857 | if (video.isBlacklisted()) { | ||
858 | return videoUserId === this.id || this.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) | ||
859 | } | ||
860 | |||
861 | if (video.privacy === VideoPrivacy.PRIVATE) { | ||
862 | return video.VideoChannel && videoUserId === this.id || this.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) | ||
863 | } | ||
864 | |||
865 | if (video.privacy === VideoPrivacy.INTERNAL) return true | ||
866 | |||
867 | return false | ||
868 | } | ||
869 | |||
870 | hasRight (right: UserRight) { | 853 | hasRight (right: UserRight) { |
871 | return hasUserRight(this.role, right) | 854 | return hasUserRight(this.role, right) |
872 | } | 855 | } |
diff --git a/server/models/utils.ts b/server/models/utils.ts index b57290aff..c468f748d 100644 --- a/server/models/utils.ts +++ b/server/models/utils.ts | |||
@@ -11,8 +11,6 @@ function getSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderIt | |||
11 | 11 | ||
12 | if (field.toLowerCase() === 'match') { // Search | 12 | if (field.toLowerCase() === 'match') { // Search |
13 | finalField = Sequelize.col('similarity') | 13 | finalField = Sequelize.col('similarity') |
14 | } else if (field === 'videoQuotaUsed') { // Users list | ||
15 | finalField = Sequelize.col('videoQuotaUsed') | ||
16 | } else { | 14 | } else { |
17 | finalField = field | 15 | finalField = field |
18 | } | 16 | } |
@@ -20,6 +18,25 @@ function getSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderIt | |||
20 | return [ [ finalField, direction ], lastSort ] | 18 | return [ [ finalField, direction ], lastSort ] |
21 | } | 19 | } |
22 | 20 | ||
21 | function getAdminUsersSort (value: string): OrderItem[] { | ||
22 | const { direction, field } = buildDirectionAndField(value) | ||
23 | |||
24 | let finalField: string | ReturnType<typeof Sequelize.col> | ||
25 | |||
26 | if (field === 'videoQuotaUsed') { // Users list | ||
27 | finalField = Sequelize.col('videoQuotaUsed') | ||
28 | } else { | ||
29 | finalField = field | ||
30 | } | ||
31 | |||
32 | const nullPolicy = direction === 'ASC' | ||
33 | ? 'NULLS FIRST' | ||
34 | : 'NULLS LAST' | ||
35 | |||
36 | // FIXME: typings | ||
37 | return [ [ finalField as any, direction, nullPolicy ], [ 'id', 'ASC' ] ] | ||
38 | } | ||
39 | |||
23 | function getPlaylistSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { | 40 | function getPlaylistSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { |
24 | const { direction, field } = buildDirectionAndField(value) | 41 | const { direction, field } = buildDirectionAndField(value) |
25 | 42 | ||
@@ -102,7 +119,7 @@ function getInstanceFollowsSort (value: string, lastSort: OrderItem = [ 'id', 'A | |||
102 | 119 | ||
103 | function isOutdated (model: { createdAt: Date, updatedAt: Date }, refreshInterval: number) { | 120 | function isOutdated (model: { createdAt: Date, updatedAt: Date }, refreshInterval: number) { |
104 | if (!model.createdAt || !model.updatedAt) { | 121 | if (!model.createdAt || !model.updatedAt) { |
105 | throw new Error('Miss createdAt & updatedAt attribuets to model') | 122 | throw new Error('Miss createdAt & updatedAt attributes to model') |
106 | } | 123 | } |
107 | 124 | ||
108 | const now = Date.now() | 125 | const now = Date.now() |
@@ -260,6 +277,7 @@ export { | |||
260 | buildLocalAccountIdsIn, | 277 | buildLocalAccountIdsIn, |
261 | getSort, | 278 | getSort, |
262 | getCommentSort, | 279 | getCommentSort, |
280 | getAdminUsersSort, | ||
263 | getVideoSort, | 281 | getVideoSort, |
264 | getBlacklistSort, | 282 | getBlacklistSort, |
265 | createSimilarityAttribute, | 283 | createSimilarityAttribute, |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index d6dd1b8bb..91dafbcf1 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -311,6 +311,16 @@ export type SummaryOptions = { | |||
311 | ')' | 311 | ')' |
312 | ), | 312 | ), |
313 | 'viewsPerDay' | 313 | 'viewsPerDay' |
314 | ], | ||
315 | [ | ||
316 | literal( | ||
317 | '(' + | ||
318 | 'SELECT COALESCE(SUM("video".views), 0) AS totalViews ' + | ||
319 | 'FROM "video" ' + | ||
320 | 'WHERE "video"."channelId" = "VideoChannelModel"."id"' + | ||
321 | ')' | ||
322 | ), | ||
323 | 'totalViews' | ||
314 | ] | 324 | ] |
315 | ] | 325 | ] |
316 | } | 326 | } |
@@ -766,6 +776,8 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"` | |||
766 | }) | 776 | }) |
767 | } | 777 | } |
768 | 778 | ||
779 | const totalViews = this.get('totalViews') as number | ||
780 | |||
769 | const actor = this.Actor.toFormattedJSON() | 781 | const actor = this.Actor.toFormattedJSON() |
770 | const videoChannel = { | 782 | const videoChannel = { |
771 | id: this.id, | 783 | id: this.id, |
@@ -779,6 +791,7 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"` | |||
779 | 791 | ||
780 | videosCount, | 792 | videosCount, |
781 | viewsPerDay, | 793 | viewsPerDay, |
794 | totalViews, | ||
782 | 795 | ||
783 | avatars: actor.avatars, | 796 | avatars: actor.avatars, |
784 | 797 | ||
diff --git a/server/models/video/video-live-session.ts b/server/models/video/video-live-session.ts index 2b4cde9f8..836620872 100644 --- a/server/models/video/video-live-session.ts +++ b/server/models/video/video-live-session.ts | |||
@@ -110,7 +110,7 @@ export class VideoLiveSessionModel extends Model<Partial<AttributesOnly<VideoLiv | |||
110 | static listSessionsOfLiveForAPI (options: { videoId: number }) { | 110 | static listSessionsOfLiveForAPI (options: { videoId: number }) { |
111 | const { videoId } = options | 111 | const { videoId } = options |
112 | 112 | ||
113 | const query: FindOptions<VideoLiveSessionModel> = { | 113 | const query: FindOptions<AttributesOnly<VideoLiveSessionModel>> = { |
114 | where: { | 114 | where: { |
115 | liveVideoId: videoId | 115 | liveVideoId: videoId |
116 | }, | 116 | }, |
diff --git a/server/models/video/video-source.ts b/server/models/video/video-source.ts new file mode 100644 index 000000000..e306b160d --- /dev/null +++ b/server/models/video/video-source.ts | |||
@@ -0,0 +1,55 @@ | |||
1 | import { Op } from 'sequelize' | ||
2 | import { | ||
3 | AllowNull, | ||
4 | BelongsTo, | ||
5 | Column, | ||
6 | CreatedAt, | ||
7 | ForeignKey, | ||
8 | Model, | ||
9 | Table, | ||
10 | UpdatedAt | ||
11 | } from 'sequelize-typescript' | ||
12 | import { AttributesOnly } from '@shared/typescript-utils' | ||
13 | import { VideoModel } from './video' | ||
14 | |||
15 | @Table({ | ||
16 | tableName: 'videoSource', | ||
17 | indexes: [ | ||
18 | { | ||
19 | fields: [ 'videoId' ], | ||
20 | where: { | ||
21 | videoId: { | ||
22 | [Op.ne]: null | ||
23 | } | ||
24 | } | ||
25 | } | ||
26 | ] | ||
27 | }) | ||
28 | export class VideoSourceModel extends Model<Partial<AttributesOnly<VideoSourceModel>>> { | ||
29 | @CreatedAt | ||
30 | createdAt: Date | ||
31 | |||
32 | @UpdatedAt | ||
33 | updatedAt: Date | ||
34 | |||
35 | @AllowNull(false) | ||
36 | @Column | ||
37 | filename: string | ||
38 | |||
39 | @ForeignKey(() => VideoModel) | ||
40 | @Column | ||
41 | videoId: number | ||
42 | |||
43 | @BelongsTo(() => VideoModel) | ||
44 | Video: VideoModel | ||
45 | |||
46 | static loadByVideoId (videoId) { | ||
47 | return VideoSourceModel.findOne({ where: { videoId } }) | ||
48 | } | ||
49 | |||
50 | toFormattedJSON () { | ||
51 | return { | ||
52 | filename: this.filename | ||
53 | } | ||
54 | } | ||
55 | } | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index e6a8d3f95..08adbced6 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -136,6 +136,7 @@ import { VideoPlaylistElementModel } from './video-playlist-element' | |||
136 | import { VideoShareModel } from './video-share' | 136 | import { VideoShareModel } from './video-share' |
137 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' | 137 | import { VideoStreamingPlaylistModel } from './video-streaming-playlist' |
138 | import { VideoTagModel } from './video-tag' | 138 | import { VideoTagModel } from './video-tag' |
139 | import { VideoSourceModel } from './video-source' | ||
139 | 140 | ||
140 | export enum ScopeNames { | 141 | export enum ScopeNames { |
141 | FOR_API = 'FOR_API', | 142 | FOR_API = 'FOR_API', |
@@ -597,6 +598,15 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { | |||
597 | }) | 598 | }) |
598 | VideoPlaylistElements: VideoPlaylistElementModel[] | 599 | VideoPlaylistElements: VideoPlaylistElementModel[] |
599 | 600 | ||
601 | @HasOne(() => VideoSourceModel, { | ||
602 | foreignKey: { | ||
603 | name: 'videoId', | ||
604 | allowNull: true | ||
605 | }, | ||
606 | onDelete: 'CASCADE' | ||
607 | }) | ||
608 | VideoSource: VideoSourceModel | ||
609 | |||
600 | @HasMany(() => VideoAbuseModel, { | 610 | @HasMany(() => VideoAbuseModel, { |
601 | foreignKey: { | 611 | foreignKey: { |
602 | name: 'videoId', | 612 | name: 'videoId', |