aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/sql
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-06-11 10:59:27 +0200
committerChocobozzz <me@florianbigard.com>2021-06-11 11:15:44 +0200
commit17bb45388ec319d288a1b8387c6c199fe2f6b64f (patch)
tree96a6b331ce1766eced50f78ab3ce0ed9197d8833 /server/models/video/sql
parentca4b4b2e5590c1b37cff1fe1be7f797b93351229 (diff)
downloadPeerTube-17bb45388ec319d288a1b8387c6c199fe2f6b64f.tar.gz
PeerTube-17bb45388ec319d288a1b8387c6c199fe2f6b64f.tar.zst
PeerTube-17bb45388ec319d288a1b8387c6c199fe2f6b64f.zip
Optimize rows parsing
Diffstat (limited to 'server/models/video/sql')
-rw-r--r--server/models/video/sql/shared/abstract-videos-model-query-builder.ts46
-rw-r--r--server/models/video/sql/shared/abstract-videos-query-builder.ts4
-rw-r--r--server/models/video/sql/shared/video-file-query-builder.ts4
-rw-r--r--server/models/video/sql/shared/video-model-builder.ts224
-rw-r--r--server/models/video/sql/shared/video-tables.ts (renamed from server/models/video/sql/shared/video-attributes.ts)5
-rw-r--r--server/models/video/sql/video-model-get-query-builder.ts8
-rw-r--r--server/models/video/sql/videos-model-list-query-builder.ts4
7 files changed, 164 insertions, 131 deletions
diff --git a/server/models/video/sql/shared/abstract-videos-model-query-builder.ts b/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
index 8ed207eea..8eff59db0 100644
--- a/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
+++ b/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
@@ -1,6 +1,6 @@
1import validator from 'validator' 1import validator from 'validator'
2import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder' 2import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
3import { VideoAttributes } from './video-attributes' 3import { VideoTables } from './video-tables'
4 4
5/** 5/**
6 * 6 *
@@ -13,12 +13,12 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
13 protected joins: string[] = [] 13 protected joins: string[] = []
14 protected where: string 14 protected where: string
15 15
16 protected videoAttributes: VideoAttributes 16 protected tables: VideoTables
17 17
18 constructor (protected readonly mode: 'list' | 'get') { 18 constructor (protected readonly mode: 'list' | 'get') {
19 super() 19 super()
20 20
21 this.videoAttributes = new VideoAttributes(this.mode) 21 this.tables = new VideoTables(this.mode)
22 } 22 }
23 23
24 protected buildSelect () { 24 protected buildSelect () {
@@ -44,7 +44,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
44 this.attributes = { 44 this.attributes = {
45 ...this.attributes, 45 ...this.attributes,
46 46
47 ...this.buildAttributesObject('VideoChannel', this.videoAttributes.getChannelAttributes()), 47 ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
48 ...this.buildActorInclude('VideoChannel->Actor'), 48 ...this.buildActorInclude('VideoChannel->Actor'),
49 ...this.buildAvatarInclude('VideoChannel->Actor->Avatar'), 49 ...this.buildAvatarInclude('VideoChannel->Actor->Avatar'),
50 ...this.buildServerInclude('VideoChannel->Actor->Server') 50 ...this.buildServerInclude('VideoChannel->Actor->Server')
@@ -66,7 +66,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
66 this.attributes = { 66 this.attributes = {
67 ...this.attributes, 67 ...this.attributes,
68 68
69 ...this.buildAttributesObject('VideoChannel->Account', this.videoAttributes.getAccountAttributes()), 69 ...this.buildAttributesObject('VideoChannel->Account', this.tables.getAccountAttributes()),
70 ...this.buildActorInclude('VideoChannel->Account->Actor'), 70 ...this.buildActorInclude('VideoChannel->Account->Actor'),
71 ...this.buildAvatarInclude('VideoChannel->Account->Actor->Avatar'), 71 ...this.buildAvatarInclude('VideoChannel->Account->Actor->Avatar'),
72 ...this.buildServerInclude('VideoChannel->Account->Actor->Server') 72 ...this.buildServerInclude('VideoChannel->Account->Actor->Server')
@@ -79,7 +79,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
79 this.attributes = { 79 this.attributes = {
80 ...this.attributes, 80 ...this.attributes,
81 81
82 ...this.buildAttributesObject('Thumbnails', this.videoAttributes.getThumbnailAttributes()) 82 ...this.buildAttributesObject('Thumbnails', this.tables.getThumbnailAttributes())
83 } 83 }
84 } 84 }
85 85
@@ -90,7 +90,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
90 this.attributes = { 90 this.attributes = {
91 ...this.attributes, 91 ...this.attributes,
92 92
93 ...this.buildAttributesObject('VideoFiles', this.videoAttributes.getFileAttributes()) 93 ...this.buildAttributesObject('VideoFiles', this.tables.getFileAttributes())
94 } 94 }
95 } 95 }
96 96
@@ -107,8 +107,8 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
107 this.attributes = { 107 this.attributes = {
108 ...this.attributes, 108 ...this.attributes,
109 109
110 ...this.buildAttributesObject('VideoStreamingPlaylists', this.videoAttributes.getStreamingPlaylistAttributes()), 110 ...this.buildAttributesObject('VideoStreamingPlaylists', this.tables.getStreamingPlaylistAttributes()),
111 ...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.videoAttributes.getFileAttributes()) 111 ...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.tables.getFileAttributes())
112 } 112 }
113 } 113 }
114 114
@@ -123,7 +123,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
123 this.attributes = { 123 this.attributes = {
124 ...this.attributes, 124 ...this.attributes,
125 125
126 ...this.buildAttributesObject('userVideoHistory', this.videoAttributes.getUserHistoryAttributes()) 126 ...this.buildAttributesObject('userVideoHistory', this.tables.getUserHistoryAttributes())
127 } 127 }
128 } 128 }
129 129
@@ -138,7 +138,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
138 this.attributes = { 138 this.attributes = {
139 ...this.attributes, 139 ...this.attributes,
140 140
141 ...this.buildAttributesObject('VideoPlaylistElement', this.videoAttributes.getPlaylistAttributes()) 141 ...this.buildAttributesObject('VideoPlaylistElement', this.tables.getPlaylistAttributes())
142 } 142 }
143 } 143 }
144 144
@@ -153,8 +153,8 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
153 this.attributes = { 153 this.attributes = {
154 ...this.attributes, 154 ...this.attributes,
155 155
156 ...this.buildAttributesObject('Tags', this.videoAttributes.getTagAttributes()), 156 ...this.buildAttributesObject('Tags', this.tables.getTagAttributes()),
157 ...this.buildAttributesObject('Tags->VideoTagModel', this.videoAttributes.getVideoTagAttributes()) 157 ...this.buildAttributesObject('Tags->VideoTagModel', this.tables.getVideoTagAttributes())
158 } 158 }
159 } 159 }
160 160
@@ -166,7 +166,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
166 this.attributes = { 166 this.attributes = {
167 ...this.attributes, 167 ...this.attributes,
168 168
169 ...this.buildAttributesObject('VideoBlacklist', this.videoAttributes.getBlacklistedAttributes()) 169 ...this.buildAttributesObject('VideoBlacklist', this.tables.getBlacklistedAttributes())
170 } 170 }
171 } 171 }
172 172
@@ -178,7 +178,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
178 this.attributes = { 178 this.attributes = {
179 ...this.attributes, 179 ...this.attributes,
180 180
181 ...this.buildAttributesObject('ScheduleVideoUpdate', this.videoAttributes.getScheduleUpdateAttributes()) 181 ...this.buildAttributesObject('ScheduleVideoUpdate', this.tables.getScheduleUpdateAttributes())
182 } 182 }
183 } 183 }
184 184
@@ -190,7 +190,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
190 this.attributes = { 190 this.attributes = {
191 ...this.attributes, 191 ...this.attributes,
192 192
193 ...this.buildAttributesObject('VideoLive', this.videoAttributes.getLiveAttributes()) 193 ...this.buildAttributesObject('VideoLive', this.tables.getLiveAttributes())
194 } 194 }
195 } 195 }
196 196
@@ -205,8 +205,8 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
205 this.attributes = { 205 this.attributes = {
206 ...this.attributes, 206 ...this.attributes,
207 207
208 ...this.buildAttributesObject('Trackers', this.videoAttributes.getTrackerAttributes()), 208 ...this.buildAttributesObject('Trackers', this.tables.getTrackerAttributes()),
209 ...this.buildAttributesObject('Trackers->VideoTrackerModel', this.videoAttributes.getVideoTrackerAttributes()) 209 ...this.buildAttributesObject('Trackers->VideoTrackerModel', this.tables.getVideoTrackerAttributes())
210 } 210 }
211 } 211 }
212 212
@@ -219,7 +219,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
219 this.attributes = { 219 this.attributes = {
220 ...this.attributes, 220 ...this.attributes,
221 221
222 ...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.videoAttributes.getRedundancyAttributes()) 222 ...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.tables.getRedundancyAttributes())
223 } 223 }
224 } 224 }
225 225
@@ -232,20 +232,20 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
232 this.attributes = { 232 this.attributes = {
233 ...this.attributes, 233 ...this.attributes,
234 234
235 ...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.videoAttributes.getRedundancyAttributes()) 235 ...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.tables.getRedundancyAttributes())
236 } 236 }
237 } 237 }
238 238
239 protected buildActorInclude (prefixKey: string) { 239 protected buildActorInclude (prefixKey: string) {
240 return this.buildAttributesObject(prefixKey, this.videoAttributes.getActorAttributes()) 240 return this.buildAttributesObject(prefixKey, this.tables.getActorAttributes())
241 } 241 }
242 242
243 protected buildAvatarInclude (prefixKey: string) { 243 protected buildAvatarInclude (prefixKey: string) {
244 return this.buildAttributesObject(prefixKey, this.videoAttributes.getAvatarAttributes()) 244 return this.buildAttributesObject(prefixKey, this.tables.getAvatarAttributes())
245 } 245 }
246 246
247 protected buildServerInclude (prefixKey: string) { 247 protected buildServerInclude (prefixKey: string) {
248 return this.buildAttributesObject(prefixKey, this.videoAttributes.getServerAttributes()) 248 return this.buildAttributesObject(prefixKey, this.tables.getServerAttributes())
249 } 249 }
250 250
251 protected buildAttributesObject (prefixKey: string, attributeKeys: string[]) { 251 protected buildAttributesObject (prefixKey: string, attributeKeys: string[]) {
diff --git a/server/models/video/sql/shared/abstract-videos-query-builder.ts b/server/models/video/sql/shared/abstract-videos-query-builder.ts
index c1bbeb71e..7e67fa34f 100644
--- a/server/models/video/sql/shared/abstract-videos-query-builder.ts
+++ b/server/models/video/sql/shared/abstract-videos-query-builder.ts
@@ -13,14 +13,14 @@ export class AbstractVideosQueryBuilder {
13 protected query: string 13 protected query: string
14 protected replacements: any = {} 14 protected replacements: any = {}
15 15
16 protected runQuery (transaction?: Transaction, nest?: boolean) { 16 protected runQuery (transaction?: Transaction) {
17 logger.debug('Running videos query.', { query: this.query, replacements: this.replacements }) 17 logger.debug('Running videos query.', { query: this.query, replacements: this.replacements })
18 18
19 const options = { 19 const options = {
20 transaction, 20 transaction,
21 replacements: this.replacements, 21 replacements: this.replacements,
22 type: QueryTypes.SELECT as QueryTypes.SELECT, 22 type: QueryTypes.SELECT as QueryTypes.SELECT,
23 nest 23 next: false
24 } 24 }
25 25
26 return this.sequelize.query<any>(this.query, options) 26 return this.sequelize.query<any>(this.query, options)
diff --git a/server/models/video/sql/shared/video-file-query-builder.ts b/server/models/video/sql/shared/video-file-query-builder.ts
index 29b11a298..ad47905c6 100644
--- a/server/models/video/sql/shared/video-file-query-builder.ts
+++ b/server/models/video/sql/shared/video-file-query-builder.ts
@@ -19,13 +19,13 @@ export class VideoFileQueryBuilder extends AbstractVideosModelQueryBuilder {
19 queryWebTorrentVideos (options: BuildVideoGetQueryOptions) { 19 queryWebTorrentVideos (options: BuildVideoGetQueryOptions) {
20 this.buildWebtorrentFilesQuery(options) 20 this.buildWebtorrentFilesQuery(options)
21 21
22 return this.runQuery(options.transaction, true) 22 return this.runQuery(options.transaction)
23 } 23 }
24 24
25 queryStreamingPlaylistVideos (options: BuildVideoGetQueryOptions) { 25 queryStreamingPlaylistVideos (options: BuildVideoGetQueryOptions) {
26 this.buildVideoStreamingPlaylistFilesQuery(options) 26 this.buildVideoStreamingPlaylistFilesQuery(options)
27 27
28 return this.runQuery(options.transaction, true) 28 return this.runQuery(options.transaction)
29 } 29 }
30 30
31 private buildWebtorrentFilesQuery (options: BuildVideoGetQueryOptions) { 31 private buildWebtorrentFilesQuery (options: BuildVideoGetQueryOptions) {
diff --git a/server/models/video/sql/shared/video-model-builder.ts b/server/models/video/sql/shared/video-model-builder.ts
index 627ea6443..2a60dab04 100644
--- a/server/models/video/sql/shared/video-model-builder.ts
+++ b/server/models/video/sql/shared/video-model-builder.ts
@@ -1,4 +1,5 @@
1import { pick } from 'lodash' 1
2import { logger } from '@server/helpers/logger'
2import { AccountModel } from '@server/models/account/account' 3import { AccountModel } from '@server/models/account/account'
3import { ActorModel } from '@server/models/actor/actor' 4import { ActorModel } from '@server/models/actor/actor'
4import { ActorImageModel } from '@server/models/actor/actor-image' 5import { ActorImageModel } from '@server/models/actor/actor-image'
@@ -15,7 +16,9 @@ import { VideoChannelModel } from '../../video-channel'
15import { VideoFileModel } from '../../video-file' 16import { VideoFileModel } from '../../video-file'
16import { VideoLiveModel } from '../../video-live' 17import { VideoLiveModel } from '../../video-live'
17import { VideoStreamingPlaylistModel } from '../../video-streaming-playlist' 18import { VideoStreamingPlaylistModel } from '../../video-streaming-playlist'
18import { VideoAttributes } from './video-attributes' 19import { VideoTables } from './video-tables'
20
21type SQLRow = { [id: string]: string | number }
19 22
20/** 23/**
21 * 24 *
@@ -28,12 +31,12 @@ export class VideoModelBuilder {
28 private videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel } 31 private videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel }
29 private videoFileMemo: { [ id: number ]: VideoFileModel } 32 private videoFileMemo: { [ id: number ]: VideoFileModel }
30 33
31 private thumbnailsDone: Set<number> 34 private thumbnailsDone: Set<any>
32 private historyDone: Set<number> 35 private historyDone: Set<any>
33 private blacklistDone: Set<number> 36 private blacklistDone: Set<any>
34 private liveDone: Set<number> 37 private liveDone: Set<any>
35 private redundancyDone: Set<number> 38 private redundancyDone: Set<any>
36 private scheduleVideoUpdateDone: Set<number> 39 private scheduleVideoUpdateDone: Set<any>
37 40
38 private trackersDone: Set<string> 41 private trackersDone: Set<string>
39 private tagsDone: Set<string> 42 private tagsDone: Set<string>
@@ -44,12 +47,12 @@ export class VideoModelBuilder {
44 47
45 constructor ( 48 constructor (
46 readonly mode: 'get' | 'list', 49 readonly mode: 'get' | 'list',
47 readonly videoAttributes: VideoAttributes 50 readonly tables: VideoTables
48 ) { 51 ) {
49 52
50 } 53 }
51 54
52 buildVideosFromRows (rows: any[], rowsWebtorrentFiles?: any[], rowsStreamingPlaylist?: any[]) { 55 buildVideosFromRows (rows: SQLRow[], rowsWebTorrentFiles?: SQLRow[], rowsStreamingPlaylist?: SQLRow[]) {
53 this.reinit() 56 this.reinit()
54 57
55 for (const row of rows) { 58 for (const row of rows) {
@@ -60,7 +63,7 @@ export class VideoModelBuilder {
60 this.setUserHistory(row, videoModel) 63 this.setUserHistory(row, videoModel)
61 this.addThumbnail(row, videoModel) 64 this.addThumbnail(row, videoModel)
62 65
63 if (!rowsWebtorrentFiles) { 66 if (!rowsWebTorrentFiles) {
64 this.addWebTorrentFile(row, videoModel) 67 this.addWebTorrentFile(row, videoModel)
65 } 68 }
66 69
@@ -75,30 +78,11 @@ export class VideoModelBuilder {
75 this.setBlacklisted(row, videoModel) 78 this.setBlacklisted(row, videoModel)
76 this.setScheduleVideoUpdate(row, videoModel) 79 this.setScheduleVideoUpdate(row, videoModel)
77 this.setLive(row, videoModel) 80 this.setLive(row, videoModel)
78
79 if (!rowsWebtorrentFiles && row.VideoFiles.id) {
80 this.addRedundancy(row.VideoFiles.RedundancyVideos, this.videoFileMemo[row.VideoFiles.id])
81 }
82
83 if (!rowsStreamingPlaylist && row.VideoStreamingPlaylists.id) {
84 this.addRedundancy(row.VideoStreamingPlaylists.RedundancyVideos, this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id])
85 }
86 } 81 }
87 } 82 }
88 83
89 for (const row of rowsWebtorrentFiles || []) { 84 this.grabSeparateWebTorrentFiles(rowsWebTorrentFiles)
90 const videoModel = this.videosMemo[row.id] 85 this.grabSeparateStreamingPlaylistFiles(rowsStreamingPlaylist)
91 this.addWebTorrentFile(row, videoModel)
92 this.addRedundancy(row.VideoFiles.RedundancyVideos, this.videoFileMemo[row.VideoFiles.id])
93 }
94
95 for (const row of rowsStreamingPlaylist || []) {
96 const videoModel = this.videosMemo[row.id]
97
98 this.addStreamingPlaylist(row, videoModel)
99 this.addStreamingPlaylistFile(row)
100 this.addRedundancy(row.VideoStreamingPlaylists.RedundancyVideos, this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id])
101 }
102 86
103 return this.videos 87 return this.videos
104 } 88 }
@@ -121,21 +105,45 @@ export class VideoModelBuilder {
121 this.videos = [] 105 this.videos = []
122 } 106 }
123 107
124 private buildVideo (row: any) { 108 private grabSeparateWebTorrentFiles (rowsWebTorrentFiles?: SQLRow[]) {
109 if (!rowsWebTorrentFiles) return
110
111 for (const row of rowsWebTorrentFiles) {
112 const videoModel = this.videosMemo[row.id]
113 this.addWebTorrentFile(row, videoModel)
114 this.addRedundancy(row, 'VideoFiles.RedundancyVideos', this.videoFileMemo[row['VideoFiles.id']])
115 }
116 }
117
118 private grabSeparateStreamingPlaylistFiles (rowsStreamingPlaylist?: SQLRow[]) {
119 if (!rowsStreamingPlaylist) return
120
121 for (const row of rowsStreamingPlaylist || []) {
122 const videoModel = this.videosMemo[row.id]
123
124 this.addStreamingPlaylist(row, videoModel)
125 this.addStreamingPlaylistFile(row)
126 this.addRedundancy(
127 row,
128 'VideoStreamingPlaylists.RedundancyVideos',
129 this.videoStreamingPlaylistMemo[row['VideoStreamingPlaylists.id']]
130 )
131 }
132 }
133
134 private buildVideo (row: SQLRow) {
125 if (this.videosMemo[row.id]) return 135 if (this.videosMemo[row.id]) return
126 136
127 // Build Channel 137 // Build Channel
128 const channel = row.VideoChannel 138 const channelModel = new VideoChannelModel(this.grab(row, this.tables.getChannelAttributes(), 'VideoChannel'), this.buildOpts)
129 const channelModel = new VideoChannelModel(pick(channel, this.videoAttributes.getChannelAttributes()), this.buildOpts) 139 channelModel.Actor = this.buildActor(row, 'VideoChannel')
130 channelModel.Actor = this.buildActor(channel.Actor)
131 140
132 const account = row.VideoChannel.Account 141 const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
133 const accountModel = new AccountModel(pick(account, this.videoAttributes.getAccountAttributes()), this.buildOpts) 142 accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
134 accountModel.Actor = this.buildActor(account.Actor)
135 143
136 channelModel.Account = accountModel 144 channelModel.Account = accountModel
137 145
138 const videoModel = new VideoModel(pick(row, this.videoAttributes.getVideoAttributes()), this.buildOpts) 146 const videoModel = new VideoModel(this.grab(row, this.tables.getVideoAttributes(), ''), this.buildOpts)
139 videoModel.VideoChannel = channelModel 147 videoModel.VideoChannel = channelModel
140 148
141 this.videosMemo[row.id] = videoModel 149 this.videosMemo[row.id] = videoModel
@@ -151,143 +159,167 @@ export class VideoModelBuilder {
151 this.videos.push(videoModel) 159 this.videos.push(videoModel)
152 } 160 }
153 161
154 private buildActor (rowActor: any) { 162 private buildActor (row: SQLRow, prefix: string) {
155 const avatarModel = rowActor.Avatar.id !== null 163 const actorPrefix = `${prefix}.Actor`
156 ? new ActorImageModel(pick(rowActor.Avatar, this.videoAttributes.getAvatarAttributes()), this.buildOpts) 164 const avatarPrefix = `${actorPrefix}.Avatar`
165 const serverPrefix = `${actorPrefix}.Server`
166
167 const avatarModel = row[`${avatarPrefix}.id`] !== null
168 ? new ActorImageModel(this.grab(row, this.tables.getAvatarAttributes(), avatarPrefix), this.buildOpts)
157 : null 169 : null
158 170
159 const serverModel = rowActor.Server.id !== null 171 const serverModel = row[`${serverPrefix}.id`] !== null
160 ? new ServerModel(pick(rowActor.Server, this.videoAttributes.getServerAttributes()), this.buildOpts) 172 ? new ServerModel(this.grab(row, this.tables.getServerAttributes(), serverPrefix), this.buildOpts)
161 : null 173 : null
162 174
163 const actorModel = new ActorModel(pick(rowActor, this.videoAttributes.getActorAttributes()), this.buildOpts) 175 const actorModel = new ActorModel(this.grab(row, this.tables.getActorAttributes(), actorPrefix), this.buildOpts)
164 actorModel.Avatar = avatarModel 176 actorModel.Avatar = avatarModel
165 actorModel.Server = serverModel 177 actorModel.Server = serverModel
166 178
167 return actorModel 179 return actorModel
168 } 180 }
169 181
170 private setUserHistory (row: any, videoModel: VideoModel) { 182 private setUserHistory (row: SQLRow, videoModel: VideoModel) {
171 if (!row.userVideoHistory?.id || this.historyDone.has(row.userVideoHistory.id)) return 183 const id = row['userVideoHistory.id']
184 if (!id || this.historyDone.has(id)) return
172 185
173 const attributes = pick(row.userVideoHistory, this.videoAttributes.getUserHistoryAttributes()) 186 const attributes = this.grab(row, this.tables.getUserHistoryAttributes(), 'userVideoHistory')
174 const historyModel = new UserVideoHistoryModel(attributes, this.buildOpts) 187 const historyModel = new UserVideoHistoryModel(attributes, this.buildOpts)
175 videoModel.UserVideoHistories.push(historyModel) 188 videoModel.UserVideoHistories.push(historyModel)
176 189
177 this.historyDone.add(row.userVideoHistory.id) 190 this.historyDone.add(id)
178 } 191 }
179 192
180 private addThumbnail (row: any, videoModel: VideoModel) { 193 private addThumbnail (row: SQLRow, videoModel: VideoModel) {
181 if (!row.Thumbnails?.id || this.thumbnailsDone.has(row.Thumbnails.id)) return 194 const id = row['Thumbnails.id']
195 if (!id || this.thumbnailsDone.has(id)) return
182 196
183 const attributes = pick(row.Thumbnails, this.videoAttributes.getThumbnailAttributes()) 197 const attributes = this.grab(row, this.tables.getThumbnailAttributes(), 'Thumbnails')
184 const thumbnailModel = new ThumbnailModel(attributes, this.buildOpts) 198 const thumbnailModel = new ThumbnailModel(attributes, this.buildOpts)
185 videoModel.Thumbnails.push(thumbnailModel) 199 videoModel.Thumbnails.push(thumbnailModel)
186 200
187 this.thumbnailsDone.add(row.Thumbnails.id) 201 this.thumbnailsDone.add(id)
188 } 202 }
189 203
190 private addWebTorrentFile (row: any, videoModel: VideoModel) { 204 private addWebTorrentFile (row: SQLRow, videoModel: VideoModel) {
191 if (!row.VideoFiles?.id || this.videoFileMemo[row.VideoFiles.id]) return 205 const id = row['VideoFiles.id']
206 if (!id || this.videoFileMemo[id]) return
192 207
193 const attributes = pick(row.VideoFiles, this.videoAttributes.getFileAttributes()) 208 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoFiles')
194 const videoFileModel = new VideoFileModel(attributes, this.buildOpts) 209 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
195 videoModel.VideoFiles.push(videoFileModel) 210 videoModel.VideoFiles.push(videoFileModel)
196 211
197 this.videoFileMemo[row.VideoFiles.id] = videoFileModel 212 this.videoFileMemo[id] = videoFileModel
198 } 213 }
199 214
200 private addStreamingPlaylist (row: any, videoModel: VideoModel) { 215 private addStreamingPlaylist (row: SQLRow, videoModel: VideoModel) {
201 if (!row.VideoStreamingPlaylists?.id || this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id]) return 216 const id = row['VideoStreamingPlaylists.id']
217 if (!id || this.videoStreamingPlaylistMemo[id]) return
202 218
203 const attributes = pick(row.VideoStreamingPlaylists, this.videoAttributes.getStreamingPlaylistAttributes()) 219 const attributes = this.grab(row, this.tables.getStreamingPlaylistAttributes(), 'VideoStreamingPlaylists')
204 const streamingPlaylist = new VideoStreamingPlaylistModel(attributes, this.buildOpts) 220 const streamingPlaylist = new VideoStreamingPlaylistModel(attributes, this.buildOpts)
205 streamingPlaylist.VideoFiles = [] 221 streamingPlaylist.VideoFiles = []
206 222
207 videoModel.VideoStreamingPlaylists.push(streamingPlaylist) 223 videoModel.VideoStreamingPlaylists.push(streamingPlaylist)
208 224
209 this.videoStreamingPlaylistMemo[streamingPlaylist.id] = streamingPlaylist 225 this.videoStreamingPlaylistMemo[id] = streamingPlaylist
210 } 226 }
211 227
212 private addStreamingPlaylistFile (row: any) { 228 private addStreamingPlaylistFile (row: SQLRow) {
213 if (!row.VideoStreamingPlaylists?.VideoFiles?.id || this.videoFileMemo[row.VideoStreamingPlaylists.VideoFiles.id]) return 229 const id = row['VideoStreamingPlaylists.VideoFiles.id']
230 if (!id || this.videoFileMemo[id]) return
214 231
215 const streamingPlaylist = this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id] 232 const streamingPlaylist = this.videoStreamingPlaylistMemo[row['VideoStreamingPlaylists.id']]
216 233
217 const attributes = pick(row.VideoStreamingPlaylists.VideoFiles, this.videoAttributes.getFileAttributes()) 234 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoStreamingPlaylists.VideoFiles')
218 const videoFileModel = new VideoFileModel(attributes, this.buildOpts) 235 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
219 streamingPlaylist.VideoFiles.push(videoFileModel) 236 streamingPlaylist.VideoFiles.push(videoFileModel)
220 237
221 this.videoFileMemo[row.VideoStreamingPlaylists.VideoFiles.id] = videoFileModel 238 this.videoFileMemo[id] = videoFileModel
222 } 239 }
223 240
224 private addRedundancy (redundancyRow: any, to: VideoFileModel | VideoStreamingPlaylistModel) { 241 private addRedundancy (row: SQLRow, prefix: string, to: VideoFileModel | VideoStreamingPlaylistModel) {
225 if (!to.RedundancyVideos) to.RedundancyVideos = [] 242 if (!to.RedundancyVideos) to.RedundancyVideos = []
226 243
227 if (!redundancyRow?.id || this.redundancyDone.has(redundancyRow.id)) return 244 const redundancyPrefix = `${prefix}.RedundancyVideos`
245 const id = row[`${redundancyPrefix}.id`]
246
247 if (!id || this.redundancyDone.has(id)) return
228 248
229 const attributes = pick(redundancyRow, this.videoAttributes.getRedundancyAttributes()) 249 const attributes = this.grab(row, this.tables.getRedundancyAttributes(), redundancyPrefix)
230 const redundancyModel = new VideoRedundancyModel(attributes, this.buildOpts) 250 const redundancyModel = new VideoRedundancyModel(attributes, this.buildOpts)
231 to.RedundancyVideos.push(redundancyModel) 251 to.RedundancyVideos.push(redundancyModel)
232 252
233 this.redundancyDone.add(redundancyRow.id) 253 this.redundancyDone.add(id)
234 } 254 }
235 255
236 private addTag (row: any, videoModel: VideoModel) { 256 private addTag (row: SQLRow, videoModel: VideoModel) {
237 if (!row.Tags?.name) return 257 if (!row['Tags.name']) return
238 const association = row.Tags.VideoTagModel
239 258
240 const key = `${association.videoId}-${association.tagId}` 259 const key = `${row['Tags.VideoTagModel.videoId']}-${row['Tags.VideoTagModel.tagId']}`
241 if (this.tagsDone.has(key)) return 260 if (this.tagsDone.has(key)) return
242 261
243 const attributes = pick(row.Tags, this.videoAttributes.getTagAttributes()) 262 const attributes = this.grab(row, this.tables.getTagAttributes(), 'Tags')
244 const tagModel = new TagModel(attributes, this.buildOpts) 263 const tagModel = new TagModel(attributes, this.buildOpts)
245 videoModel.Tags.push(tagModel) 264 videoModel.Tags.push(tagModel)
246 265
247 this.tagsDone.add(key) 266 this.tagsDone.add(key)
248 } 267 }
249 268
250 private addTracker (row: any, videoModel: VideoModel) { 269 private addTracker (row: SQLRow, videoModel: VideoModel) {
251 if (!row.Trackers?.id) return 270 if (!row['Trackers.id']) return
252 const association = row.Trackers.VideoTrackerModel
253 271
254 const key = `${association.videoId}-${association.trackerId}` 272 const key = `${row['Trackers.VideoTrackerModel.videoId']}-${row['Trackers.VideoTrackerModel.trackerId']}`
255 if (this.trackersDone.has(key)) return 273 if (this.trackersDone.has(key)) return
256 274
257 const attributes = pick(row.Trackers, this.videoAttributes.getTrackerAttributes()) 275 const attributes = this.grab(row, this.tables.getTrackerAttributes(), 'Trackers')
258 const trackerModel = new TrackerModel(attributes, this.buildOpts) 276 const trackerModel = new TrackerModel(attributes, this.buildOpts)
259 videoModel.Trackers.push(trackerModel) 277 videoModel.Trackers.push(trackerModel)
260 278
261 this.trackersDone.add(key) 279 this.trackersDone.add(key)
262 } 280 }
263 281
264 private setBlacklisted (row: any, videoModel: VideoModel) { 282 private setBlacklisted (row: SQLRow, videoModel: VideoModel) {
265 if (!row.VideoBlacklist?.id) return 283 const id = row['VideoBlacklist.id']
266 if (this.blacklistDone.has(row.VideoBlacklist.id)) return 284 if (!id || this.blacklistDone.has(id)) return
267 285
268 const attributes = pick(row.VideoBlacklist, this.videoAttributes.getBlacklistedAttributes()) 286 const attributes = this.grab(row, this.tables.getBlacklistedAttributes(), 'VideoBlacklist')
269 videoModel.VideoBlacklist = new VideoBlacklistModel(attributes, this.buildOpts) 287 videoModel.VideoBlacklist = new VideoBlacklistModel(attributes, this.buildOpts)
270 288
271 this.blacklistDone.add(row.VideoBlacklist.id) 289 this.blacklistDone.add(id)
272 } 290 }
273 291
274 private setScheduleVideoUpdate (row: any, videoModel: VideoModel) { 292 private setScheduleVideoUpdate (row: SQLRow, videoModel: VideoModel) {
275 if (!row.ScheduleVideoUpdate?.id) return 293 const id = row['ScheduleVideoUpdate.id']
276 if (this.scheduleVideoUpdateDone.has(row.ScheduleVideoUpdate.id)) return 294 if (!id || this.scheduleVideoUpdateDone.has(id)) return
277 295
278 const attributes = pick(row.ScheduleVideoUpdate, this.videoAttributes.getScheduleUpdateAttributes()) 296 const attributes = this.grab(row, this.tables.getScheduleUpdateAttributes(), 'ScheduleVideoUpdate')
279 videoModel.ScheduleVideoUpdate = new ScheduleVideoUpdateModel(attributes, this.buildOpts) 297 videoModel.ScheduleVideoUpdate = new ScheduleVideoUpdateModel(attributes, this.buildOpts)
280 298
281 this.scheduleVideoUpdateDone.add(row.ScheduleVideoUpdate.id) 299 this.scheduleVideoUpdateDone.add(id)
282 } 300 }
283 301
284 private setLive (row: any, videoModel: VideoModel) { 302 private setLive (row: SQLRow, videoModel: VideoModel) {
285 if (!row.VideoLive?.id) return 303 const id = row['VideoLive.id']
286 if (this.liveDone.has(row.VideoLive.id)) return 304 if (!id || this.liveDone.has(id)) return
287 305
288 const attributes = pick(row.VideoLive, this.videoAttributes.getLiveAttributes()) 306 const attributes = this.grab(row, this.tables.getLiveAttributes(), 'VideoLive')
289 videoModel.VideoLive = new VideoLiveModel(attributes, this.buildOpts) 307 videoModel.VideoLive = new VideoLiveModel(attributes, this.buildOpts)
290 308
291 this.liveDone.add(row.ScheduleVideoUpdate.id) 309 this.liveDone.add(id)
310 }
311
312 private grab (row: SQLRow, attributes: string[], prefix: string) {
313 const result: { [ id: string ]: string | number } = {}
314
315 for (const a of attributes) {
316 const key = prefix
317 ? prefix + '.' + a
318 : a
319
320 result[a] = row[key]
321 }
322
323 return result
292 } 324 }
293} 325}
diff --git a/server/models/video/sql/shared/video-attributes.ts b/server/models/video/sql/shared/video-tables.ts
index e21b33c73..fddf1210c 100644
--- a/server/models/video/sql/shared/video-attributes.ts
+++ b/server/models/video/sql/shared/video-tables.ts
@@ -1,10 +1,10 @@
1 1
2/** 2/**
3 * 3 *
4 * Class to build video attributes we want to fetch from the database 4 * Class to build video attributes/join names we want to fetch from the database
5 * 5 *
6 */ 6 */
7export class VideoAttributes { 7export class VideoTables {
8 8
9 constructor (readonly mode: 'get' | 'list') { 9 constructor (readonly mode: 'get' | 'list') {
10 10
@@ -199,6 +199,7 @@ export class VideoAttributes {
199 let attributeKeys = [ 199 let attributeKeys = [
200 'id', 200 'id',
201 'filename', 201 'filename',
202 'type',
202 'fileUrl', 203 'fileUrl',
203 'onDisk', 204 'onDisk',
204 'createdAt', 205 'createdAt',
diff --git a/server/models/video/sql/video-model-get-query-builder.ts b/server/models/video/sql/video-model-get-query-builder.ts
index 1a921d802..892639076 100644
--- a/server/models/video/sql/video-model-get-query-builder.ts
+++ b/server/models/video/sql/video-model-get-query-builder.ts
@@ -1,8 +1,8 @@
1import { Sequelize, Transaction } from 'sequelize' 1import { Sequelize, Transaction } from 'sequelize'
2import { AbstractVideosModelQueryBuilder } from './shared/abstract-videos-model-query-builder' 2import { AbstractVideosModelQueryBuilder } from './shared/abstract-videos-model-query-builder'
3import { VideoAttributes } from './shared/video-attributes'
4import { VideoFileQueryBuilder } from './shared/video-file-query-builder' 3import { VideoFileQueryBuilder } from './shared/video-file-query-builder'
5import { VideoModelBuilder } from './shared/video-model-builder' 4import { VideoModelBuilder } from './shared/video-model-builder'
5import { VideoTables } from './shared/video-tables'
6 6
7/** 7/**
8 * 8 *
@@ -29,7 +29,7 @@ export class VideosModelGetQueryBuilder {
29 this.webtorrentFilesQueryBuilder = new VideoFileQueryBuilder(sequelize) 29 this.webtorrentFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
30 this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize) 30 this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
31 31
32 this.videoModelBuilder = new VideoModelBuilder('get', new VideoAttributes('get')) 32 this.videoModelBuilder = new VideoModelBuilder('get', new VideoTables('get'))
33 } 33 }
34 34
35 async queryVideos (options: BuildVideoGetQueryOptions) { 35 async queryVideos (options: BuildVideoGetQueryOptions) {
@@ -64,7 +64,7 @@ export class VideosModelGetQuerySubBuilder extends AbstractVideosModelQueryBuild
64 queryVideos (options: BuildVideoGetQueryOptions) { 64 queryVideos (options: BuildVideoGetQueryOptions) {
65 this.buildMainGetQuery(options) 65 this.buildMainGetQuery(options)
66 66
67 return this.runQuery(options.transaction, true) 67 return this.runQuery(options.transaction)
68 } 68 }
69 69
70 private buildMainGetQuery (options: BuildVideoGetQueryOptions) { 70 private buildMainGetQuery (options: BuildVideoGetQueryOptions) {
@@ -102,6 +102,6 @@ export class VideosModelGetQuerySubBuilder extends AbstractVideosModelQueryBuild
102 const order = 'ORDER BY "Tags"."name" ASC' 102 const order = 'ORDER BY "Tags"."name" ASC'
103 const from = `SELECT * FROM "video" ${this.where} LIMIT 1` 103 const from = `SELECT * FROM "video" ${this.where} LIMIT 1`
104 104
105 return `${this.buildSelect()} FROM (${from}) AS "video" ${this.joins.join(' ')} ${this.where} ${order}` 105 return `${this.buildSelect()} FROM (${from}) AS "video" ${this.joins.join(' ')} ${order}`
106 } 106 }
107} 107}
diff --git a/server/models/video/sql/videos-model-list-query-builder.ts b/server/models/video/sql/videos-model-list-query-builder.ts
index acb76d80a..459f542a4 100644
--- a/server/models/video/sql/videos-model-list-query-builder.ts
+++ b/server/models/video/sql/videos-model-list-query-builder.ts
@@ -21,14 +21,14 @@ export class VideosModelListQueryBuilder extends AbstractVideosModelQueryBuilder
21 constructor (protected readonly sequelize: Sequelize) { 21 constructor (protected readonly sequelize: Sequelize) {
22 super('list') 22 super('list')
23 23
24 this.videoModelBuilder = new VideoModelBuilder(this.mode, this.videoAttributes) 24 this.videoModelBuilder = new VideoModelBuilder(this.mode, this.tables)
25 } 25 }
26 26
27 queryVideos (options: BuildVideosListQueryOptions) { 27 queryVideos (options: BuildVideosListQueryOptions) {
28 this.buildInnerQuery(options) 28 this.buildInnerQuery(options)
29 this.buildListQueryFromIdsQuery(options) 29 this.buildListQueryFromIdsQuery(options)
30 30
31 return this.runQuery(undefined, true).then(rows => this.videoModelBuilder.buildVideosFromRows(rows)) 31 return this.runQuery(undefined).then(rows => this.videoModelBuilder.buildVideosFromRows(rows))
32 } 32 }
33 33
34 private buildInnerQuery (options: BuildVideosListQueryOptions) { 34 private buildInnerQuery (options: BuildVideosListQueryOptions) {