aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
Diffstat (limited to 'server/models')
-rw-r--r--server/models/abuse/abuse-query-builder.ts2
-rw-r--r--server/models/shared/abstract-run-query.ts2
-rw-r--r--server/models/user/user.ts27
-rw-r--r--server/models/utils.ts24
-rw-r--r--server/models/video/sql/video/shared/abstract-video-query-builder.ts7
-rw-r--r--server/models/video/sql/video/shared/video-file-query-builder.ts30
-rw-r--r--server/models/video/sql/video/video-model-get-query-builder.ts15
-rw-r--r--server/models/video/sql/video/videos-model-list-query-builder.ts41
-rw-r--r--server/models/video/video-channel.ts13
-rw-r--r--server/models/video/video-live-session.ts2
-rw-r--r--server/models/video/video-source.ts55
-rw-r--r--server/models/video/video.ts10
12 files changed, 176 insertions, 52 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'
35import { AttributesOnly } from '@shared/typescript-utils' 34import { AttributesOnly } from '@shared/typescript-utils'
36import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' 35import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users'
37import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' 36import { AbuseState, MyUser, UserRight, VideoPlaylistType } from '../../../shared/models'
38import { User, UserRole } from '../../../shared/models/users' 37import { User, UserRole } from '../../../shared/models/users'
39import { UserAdminFlag } from '../../../shared/models/users/user-flag.model' 38import { UserAdminFlag } from '../../../shared/models/users/user-flag.model'
40import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type' 39import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
@@ -66,7 +65,7 @@ import { ActorModel } from '../actor/actor'
66import { ActorFollowModel } from '../actor/actor-follow' 65import { ActorFollowModel } from '../actor/actor-follow'
67import { ActorImageModel } from '../actor/actor-image' 66import { ActorImageModel } from '../actor/actor-image'
68import { OAuthTokenModel } from '../oauth/oauth-token' 67import { OAuthTokenModel } from '../oauth/oauth-token'
69import { getSort, throwIfNotValid } from '../utils' 68import { getAdminUsersSort, throwIfNotValid } from '../utils'
70import { VideoModel } from '../video/video' 69import { VideoModel } from '../video/video'
71import { VideoChannelModel } from '../video/video-channel' 70import { VideoChannelModel } from '../video/video-channel'
72import { VideoImportModel } from '../video/video-import' 71import { 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
21function 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
23function getPlaylistSort (value: string, lastSort: OrderItem = [ 'id', 'ASC' ]): OrderItem[] { 40function 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
103function isOutdated (model: { createdAt: Date, updatedAt: Date }, refreshInterval: number) { 120function 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/sql/video/shared/abstract-video-query-builder.ts b/server/models/video/sql/video/shared/abstract-video-query-builder.ts
index b79d20ade..3c74b0ea6 100644
--- a/server/models/video/sql/video/shared/abstract-video-query-builder.ts
+++ b/server/models/video/sql/video/shared/abstract-video-query-builder.ts
@@ -313,7 +313,12 @@ export class AbstractVideoQueryBuilder extends AbstractRunQuery {
313 return result 313 return result
314 } 314 }
315 315
316 protected whereId (options: { id?: string | number, url?: string }) { 316 protected whereId (options: { ids?: number[], id?: string | number, url?: string }) {
317 if (options.ids) {
318 this.where = `WHERE "video"."id" IN (${createSafeIn(this.sequelize, options.ids)})`
319 return
320 }
321
317 if (options.url) { 322 if (options.url) {
318 this.where = 'WHERE "video"."url" = :videoUrl' 323 this.where = 'WHERE "video"."url" = :videoUrl'
319 this.replacements.videoUrl = options.url 324 this.replacements.videoUrl = options.url
diff --git a/server/models/video/sql/video/shared/video-file-query-builder.ts b/server/models/video/sql/video/shared/video-file-query-builder.ts
index 50c12f627..cc53a4860 100644
--- a/server/models/video/sql/video/shared/video-file-query-builder.ts
+++ b/server/models/video/sql/video/shared/video-file-query-builder.ts
@@ -1,7 +1,17 @@
1import { Sequelize } from 'sequelize' 1import { Sequelize, Transaction } from 'sequelize'
2import { BuildVideoGetQueryOptions } from '../video-model-get-query-builder'
3import { AbstractVideoQueryBuilder } from './abstract-video-query-builder' 2import { AbstractVideoQueryBuilder } from './abstract-video-query-builder'
4 3
4export type FileQueryOptions = {
5 id?: string | number
6 url?: string
7
8 includeRedundancy: boolean
9
10 transaction?: Transaction
11
12 logging?: boolean
13}
14
5/** 15/**
6 * 16 *
7 * Fetch files (webtorrent and streaming playlist) according to a video 17 * Fetch files (webtorrent and streaming playlist) according to a video
@@ -15,26 +25,26 @@ export class VideoFileQueryBuilder extends AbstractVideoQueryBuilder {
15 super(sequelize, 'get') 25 super(sequelize, 'get')
16 } 26 }
17 27
18 queryWebTorrentVideos (options: BuildVideoGetQueryOptions) { 28 queryWebTorrentVideos (options: FileQueryOptions) {
19 this.buildWebtorrentFilesQuery(options) 29 this.buildWebtorrentFilesQuery(options)
20 30
21 return this.runQuery(options) 31 return this.runQuery(options)
22 } 32 }
23 33
24 queryStreamingPlaylistVideos (options: BuildVideoGetQueryOptions) { 34 queryStreamingPlaylistVideos (options: FileQueryOptions) {
25 this.buildVideoStreamingPlaylistFilesQuery(options) 35 this.buildVideoStreamingPlaylistFilesQuery(options)
26 36
27 return this.runQuery(options) 37 return this.runQuery(options)
28 } 38 }
29 39
30 private buildWebtorrentFilesQuery (options: BuildVideoGetQueryOptions) { 40 private buildWebtorrentFilesQuery (options: FileQueryOptions) {
31 this.attributes = { 41 this.attributes = {
32 '"video"."id"': '' 42 '"video"."id"': ''
33 } 43 }
34 44
35 this.includeWebtorrentFiles() 45 this.includeWebtorrentFiles()
36 46
37 if (this.shouldIncludeRedundancies(options)) { 47 if (options.includeRedundancy) {
38 this.includeWebTorrentRedundancies() 48 this.includeWebTorrentRedundancies()
39 } 49 }
40 50
@@ -43,14 +53,14 @@ export class VideoFileQueryBuilder extends AbstractVideoQueryBuilder {
43 this.query = this.buildQuery() 53 this.query = this.buildQuery()
44 } 54 }
45 55
46 private buildVideoStreamingPlaylistFilesQuery (options: BuildVideoGetQueryOptions) { 56 private buildVideoStreamingPlaylistFilesQuery (options: FileQueryOptions) {
47 this.attributes = { 57 this.attributes = {
48 '"video"."id"': '' 58 '"video"."id"': ''
49 } 59 }
50 60
51 this.includeStreamingPlaylistFiles() 61 this.includeStreamingPlaylistFiles()
52 62
53 if (this.shouldIncludeRedundancies(options)) { 63 if (options.includeRedundancy) {
54 this.includeStreamingPlaylistRedundancies() 64 this.includeStreamingPlaylistRedundancies()
55 } 65 }
56 66
@@ -62,8 +72,4 @@ export class VideoFileQueryBuilder extends AbstractVideoQueryBuilder {
62 private buildQuery () { 72 private buildQuery () {
63 return `${this.buildSelect()} FROM "video" ${this.joins} ${this.where}` 73 return `${this.buildSelect()} FROM "video" ${this.joins} ${this.where}`
64 } 74 }
65
66 private shouldIncludeRedundancies (options: BuildVideoGetQueryOptions) {
67 return options.type === 'api'
68 }
69} 75}
diff --git a/server/models/video/sql/video/video-model-get-query-builder.ts b/server/models/video/sql/video/video-model-get-query-builder.ts
index b0879c9ac..32e5c4ff7 100644
--- a/server/models/video/sql/video/video-model-get-query-builder.ts
+++ b/server/models/video/sql/video/video-model-get-query-builder.ts
@@ -1,3 +1,4 @@
1import { pick } from 'lodash'
1import { Sequelize, Transaction } from 'sequelize' 2import { Sequelize, Transaction } from 'sequelize'
2import { AbstractVideoQueryBuilder } from './shared/abstract-video-query-builder' 3import { AbstractVideoQueryBuilder } from './shared/abstract-video-query-builder'
3import { VideoFileQueryBuilder } from './shared/video-file-query-builder' 4import { VideoFileQueryBuilder } from './shared/video-file-query-builder'
@@ -50,15 +51,21 @@ export class VideoModelGetQueryBuilder {
50 } 51 }
51 52
52 async queryVideo (options: BuildVideoGetQueryOptions) { 53 async queryVideo (options: BuildVideoGetQueryOptions) {
54 const fileQueryOptions = {
55 ...pick(options, [ 'id', 'url', 'transaction', 'logging' ]),
56
57 includeRedundancy: this.shouldIncludeRedundancies(options)
58 }
59
53 const [ videoRows, webtorrentFilesRows, streamingPlaylistFilesRows ] = await Promise.all([ 60 const [ videoRows, webtorrentFilesRows, streamingPlaylistFilesRows ] = await Promise.all([
54 this.videoQueryBuilder.queryVideos(options), 61 this.videoQueryBuilder.queryVideos(options),
55 62
56 VideoModelGetQueryBuilder.videoFilesInclude.has(options.type) 63 VideoModelGetQueryBuilder.videoFilesInclude.has(options.type)
57 ? this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(options) 64 ? this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(fileQueryOptions)
58 : Promise.resolve(undefined), 65 : Promise.resolve(undefined),
59 66
60 VideoModelGetQueryBuilder.videoFilesInclude.has(options.type) 67 VideoModelGetQueryBuilder.videoFilesInclude.has(options.type)
61 ? this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(options) 68 ? this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(fileQueryOptions)
62 : Promise.resolve(undefined) 69 : Promise.resolve(undefined)
63 ]) 70 ])
64 71
@@ -76,6 +83,10 @@ export class VideoModelGetQueryBuilder {
76 83
77 return videos[0] 84 return videos[0]
78 } 85 }
86
87 private shouldIncludeRedundancies (options: BuildVideoGetQueryOptions) {
88 return options.type === 'api'
89 }
79} 90}
80 91
81export class VideosModelGetQuerySubBuilder extends AbstractVideoQueryBuilder { 92export class VideosModelGetQuerySubBuilder extends AbstractVideoQueryBuilder {
diff --git a/server/models/video/sql/video/videos-model-list-query-builder.ts b/server/models/video/sql/video/videos-model-list-query-builder.ts
index 2a4afc389..4fe6bc321 100644
--- a/server/models/video/sql/video/videos-model-list-query-builder.ts
+++ b/server/models/video/sql/video/videos-model-list-query-builder.ts
@@ -1,6 +1,8 @@
1import { VideoInclude } from '@shared/models' 1import { pick } from 'lodash'
2import { Sequelize } from 'sequelize' 2import { Sequelize } from 'sequelize'
3import { VideoInclude } from '@shared/models'
3import { AbstractVideoQueryBuilder } from './shared/abstract-video-query-builder' 4import { AbstractVideoQueryBuilder } from './shared/abstract-video-query-builder'
5import { VideoFileQueryBuilder } from './shared/video-file-query-builder'
4import { VideoModelBuilder } from './shared/video-model-builder' 6import { VideoModelBuilder } from './shared/video-model-builder'
5import { BuildVideosListQueryOptions, VideosIdListQueryBuilder } from './videos-id-list-query-builder' 7import { BuildVideosListQueryOptions, VideosIdListQueryBuilder } from './videos-id-list-query-builder'
6 8
@@ -16,20 +18,46 @@ export class VideosModelListQueryBuilder extends AbstractVideoQueryBuilder {
16 private innerQuery: string 18 private innerQuery: string
17 private innerSort: string 19 private innerSort: string
18 20
21 webtorrentFilesQueryBuilder: VideoFileQueryBuilder
22 streamingPlaylistFilesQueryBuilder: VideoFileQueryBuilder
23
19 private readonly videoModelBuilder: VideoModelBuilder 24 private readonly videoModelBuilder: VideoModelBuilder
20 25
21 constructor (protected readonly sequelize: Sequelize) { 26 constructor (protected readonly sequelize: Sequelize) {
22 super(sequelize, 'list') 27 super(sequelize, 'list')
23 28
24 this.videoModelBuilder = new VideoModelBuilder(this.mode, this.tables) 29 this.videoModelBuilder = new VideoModelBuilder(this.mode, this.tables)
30 this.webtorrentFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
31 this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
25 } 32 }
26 33
27 queryVideos (options: BuildVideosListQueryOptions) { 34 async queryVideos (options: BuildVideosListQueryOptions) {
28 this.buildInnerQuery(options) 35 this.buildInnerQuery(options)
29 this.buildMainQuery(options) 36 this.buildMainQuery(options)
30 37
31 return this.runQuery() 38 const rows = await this.runQuery()
32 .then(rows => this.videoModelBuilder.buildVideosFromRows({ rows, include: options.include })) 39
40 if (options.include & VideoInclude.FILES) {
41 const videoIds = Array.from(new Set(rows.map(r => r.id)))
42
43 if (videoIds.length !== 0) {
44 const fileQueryOptions = {
45 ...pick(options, [ 'transaction', 'logging' ]),
46
47 ids: videoIds,
48 includeRedundancy: false
49 }
50
51 const [ rowsWebTorrentFiles, rowsStreamingPlaylist ] = await Promise.all([
52 this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(fileQueryOptions),
53 this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(fileQueryOptions)
54 ])
55
56 return this.videoModelBuilder.buildVideosFromRows({ rows, include: options.include, rowsStreamingPlaylist, rowsWebTorrentFiles })
57 }
58 }
59
60 return this.videoModelBuilder.buildVideosFromRows({ rows, include: options.include })
33 } 61 }
34 62
35 private buildInnerQuery (options: BuildVideosListQueryOptions) { 63 private buildInnerQuery (options: BuildVideosListQueryOptions) {
@@ -52,11 +80,6 @@ export class VideosModelListQueryBuilder extends AbstractVideoQueryBuilder {
52 this.includeAccounts() 80 this.includeAccounts()
53 this.includeThumbnails() 81 this.includeThumbnails()
54 82
55 if (options.include & VideoInclude.FILES) {
56 this.includeWebtorrentFiles()
57 this.includeStreamingPlaylistFiles()
58 }
59
60 if (options.user) { 83 if (options.user) {
61 this.includeUserHistory(options.user.id) 84 this.includeUserHistory(options.user.id)
62 } 85 }
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 @@
1import { Op } from 'sequelize'
2import {
3 AllowNull,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 ForeignKey,
8 Model,
9 Table,
10 UpdatedAt
11} from 'sequelize-typescript'
12import { AttributesOnly } from '@shared/typescript-utils'
13import { 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})
28export 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'
136import { VideoShareModel } from './video-share' 136import { VideoShareModel } from './video-share'
137import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 137import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
138import { VideoTagModel } from './video-tag' 138import { VideoTagModel } from './video-tag'
139import { VideoSourceModel } from './video-source'
139 140
140export enum ScopeNames { 141export 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',