aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/sql/shared
diff options
context:
space:
mode:
authorkontrollanten <6680299+kontrollanten@users.noreply.github.com>2022-02-28 08:34:43 +0100
committerGitHub <noreply@github.com>2022-02-28 08:34:43 +0100
commitd0800f7661f13fabe7bb6f4aa0ea50764f106405 (patch)
treed43e6b0b6f4a5a32e03487e6464edbcaf288be2a /server/models/video/sql/shared
parent5cad2ca9db9b9d138f8a33058d10b94a9fd50c69 (diff)
downloadPeerTube-d0800f7661f13fabe7bb6f4aa0ea50764f106405.tar.gz
PeerTube-d0800f7661f13fabe7bb6f4aa0ea50764f106405.tar.zst
PeerTube-d0800f7661f13fabe7bb6f4aa0ea50764f106405.zip
Implement avatar miniatures (#4639)
* client: remove unused file * refactor(client/my-actor-avatar): size from input Read size from component input instead of scss, to make it possible to use smaller avatar images when implemented. * implement avatar miniatures close #4560 * fix(test): max file size * fix(search-index): normalize res acc to avatarMini * refactor avatars to an array * client/search: resize channel avatar to 120 * refactor(client/videos): remove unused function * client(actor-avatar): set default size * fix tests and avatars full result When findOne is used only an array containting one avatar is returned. * update migration version and version notations * server/search: harmonize normalizing * Cleanup avatar miniature PR Co-authored-by: Chocobozzz <me@florianbigard.com>
Diffstat (limited to 'server/models/video/sql/shared')
-rw-r--r--server/models/video/sql/shared/abstract-run-query.ts26
-rw-r--r--server/models/video/sql/shared/abstract-video-query-builder.ts328
-rw-r--r--server/models/video/sql/shared/video-file-query-builder.ts69
-rw-r--r--server/models/video/sql/shared/video-model-builder.ts387
-rw-r--r--server/models/video/sql/shared/video-table-attributes.ts269
5 files changed, 0 insertions, 1079 deletions
diff --git a/server/models/video/sql/shared/abstract-run-query.ts b/server/models/video/sql/shared/abstract-run-query.ts
deleted file mode 100644
index 8e7a7642d..000000000
--- a/server/models/video/sql/shared/abstract-run-query.ts
+++ /dev/null
@@ -1,26 +0,0 @@
1import { QueryTypes, Sequelize, Transaction } from 'sequelize'
2
3/**
4 *
5 * Abstact builder to run video SQL queries
6 *
7 */
8
9export class AbstractRunQuery {
10 protected sequelize: Sequelize
11
12 protected query: string
13 protected replacements: any = {}
14
15 protected runQuery (options: { transaction?: Transaction, logging?: boolean } = {}) {
16 const queryOptions = {
17 transaction: options.transaction,
18 logging: options.logging,
19 replacements: this.replacements,
20 type: QueryTypes.SELECT as QueryTypes.SELECT,
21 nest: false
22 }
23
24 return this.sequelize.query<any>(this.query, queryOptions)
25 }
26}
diff --git a/server/models/video/sql/shared/abstract-video-query-builder.ts b/server/models/video/sql/shared/abstract-video-query-builder.ts
deleted file mode 100644
index a6afb04e4..000000000
--- a/server/models/video/sql/shared/abstract-video-query-builder.ts
+++ /dev/null
@@ -1,328 +0,0 @@
1import { createSafeIn } from '@server/models/utils'
2import { MUserAccountId } from '@server/types/models'
3import validator from 'validator'
4import { AbstractRunQuery } from './abstract-run-query'
5import { VideoTableAttributes } from './video-table-attributes'
6
7/**
8 *
9 * Abstract builder to create SQL query and fetch video models
10 *
11 */
12
13export class AbstractVideoQueryBuilder extends AbstractRunQuery {
14 protected attributes: { [key: string]: string } = {}
15
16 protected joins = ''
17 protected where: string
18
19 protected tables: VideoTableAttributes
20
21 constructor (protected readonly mode: 'list' | 'get') {
22 super()
23
24 this.tables = new VideoTableAttributes(this.mode)
25 }
26
27 protected buildSelect () {
28 return 'SELECT ' + Object.keys(this.attributes).map(key => {
29 const value = this.attributes[key]
30 if (value) return `${key} AS ${value}`
31
32 return key
33 }).join(', ')
34 }
35
36 protected includeChannels () {
37 this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
38 this.addJoin('INNER JOIN "actor" AS "VideoChannel->Actor" ON "VideoChannel"."actorId" = "VideoChannel->Actor"."id"')
39
40 this.addJoin(
41 'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"'
42 )
43
44 this.addJoin(
45 'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Actor->Avatar" ' +
46 'ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"'
47 )
48
49 this.attributes = {
50 ...this.attributes,
51
52 ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
53 ...this.buildActorInclude('VideoChannel->Actor'),
54 ...this.buildAvatarInclude('VideoChannel->Actor->Avatar'),
55 ...this.buildServerInclude('VideoChannel->Actor->Server')
56 }
57 }
58
59 protected includeAccounts () {
60 this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
61 this.addJoin(
62 'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"'
63 )
64
65 this.addJoin(
66 'LEFT OUTER JOIN "server" AS "VideoChannel->Account->Actor->Server" ' +
67 'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"'
68 )
69
70 this.addJoin(
71 'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Account->Actor->Avatar" ' +
72 'ON "VideoChannel->Account->Actor"."avatarId" = "VideoChannel->Account->Actor->Avatar"."id"'
73 )
74
75 this.attributes = {
76 ...this.attributes,
77
78 ...this.buildAttributesObject('VideoChannel->Account', this.tables.getAccountAttributes()),
79 ...this.buildActorInclude('VideoChannel->Account->Actor'),
80 ...this.buildAvatarInclude('VideoChannel->Account->Actor->Avatar'),
81 ...this.buildServerInclude('VideoChannel->Account->Actor->Server')
82 }
83 }
84
85 protected includeOwnerUser () {
86 this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
87 this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
88
89 this.attributes = {
90 ...this.attributes,
91
92 ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
93 ...this.buildAttributesObject('VideoChannel->Account', this.tables.getUserAccountAttributes())
94 }
95 }
96
97 protected includeThumbnails () {
98 this.addJoin('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
99
100 this.attributes = {
101 ...this.attributes,
102
103 ...this.buildAttributesObject('Thumbnails', this.tables.getThumbnailAttributes())
104 }
105 }
106
107 protected includeWebtorrentFiles () {
108 this.addJoin('LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"')
109
110 this.attributes = {
111 ...this.attributes,
112
113 ...this.buildAttributesObject('VideoFiles', this.tables.getFileAttributes())
114 }
115 }
116
117 protected includeStreamingPlaylistFiles () {
118 this.addJoin(
119 'LEFT JOIN "videoStreamingPlaylist" AS "VideoStreamingPlaylists" ON "VideoStreamingPlaylists"."videoId" = "video"."id"'
120 )
121
122 this.addJoin(
123 'LEFT JOIN "videoFile" AS "VideoStreamingPlaylists->VideoFiles" ' +
124 'ON "VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId" = "VideoStreamingPlaylists"."id"'
125 )
126
127 this.attributes = {
128 ...this.attributes,
129
130 ...this.buildAttributesObject('VideoStreamingPlaylists', this.tables.getStreamingPlaylistAttributes()),
131 ...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.tables.getFileAttributes())
132 }
133 }
134
135 protected includeUserHistory (userId: number) {
136 this.addJoin(
137 'LEFT OUTER JOIN "userVideoHistory" ' +
138 'ON "video"."id" = "userVideoHistory"."videoId" AND "userVideoHistory"."userId" = :userVideoHistoryId'
139 )
140
141 this.replacements.userVideoHistoryId = userId
142
143 this.attributes = {
144 ...this.attributes,
145
146 ...this.buildAttributesObject('userVideoHistory', this.tables.getUserHistoryAttributes())
147 }
148 }
149
150 protected includePlaylist (playlistId: number) {
151 this.addJoin(
152 'INNER JOIN "videoPlaylistElement" as "VideoPlaylistElement" ON "videoPlaylistElement"."videoId" = "video"."id" ' +
153 'AND "VideoPlaylistElement"."videoPlaylistId" = :videoPlaylistId'
154 )
155
156 this.replacements.videoPlaylistId = playlistId
157
158 this.attributes = {
159 ...this.attributes,
160
161 ...this.buildAttributesObject('VideoPlaylistElement', this.tables.getPlaylistAttributes())
162 }
163 }
164
165 protected includeTags () {
166 this.addJoin(
167 'LEFT OUTER JOIN (' +
168 '"videoTag" AS "Tags->VideoTagModel" INNER JOIN "tag" AS "Tags" ON "Tags"."id" = "Tags->VideoTagModel"."tagId"' +
169 ') ' +
170 'ON "video"."id" = "Tags->VideoTagModel"."videoId"'
171 )
172
173 this.attributes = {
174 ...this.attributes,
175
176 ...this.buildAttributesObject('Tags', this.tables.getTagAttributes()),
177 ...this.buildAttributesObject('Tags->VideoTagModel', this.tables.getVideoTagAttributes())
178 }
179 }
180
181 protected includeBlacklisted () {
182 this.addJoin(
183 'LEFT OUTER JOIN "videoBlacklist" AS "VideoBlacklist" ON "video"."id" = "VideoBlacklist"."videoId"'
184 )
185
186 this.attributes = {
187 ...this.attributes,
188
189 ...this.buildAttributesObject('VideoBlacklist', this.tables.getBlacklistedAttributes())
190 }
191 }
192
193 protected includeBlockedOwnerAndServer (serverAccountId: number, user?: MUserAccountId) {
194 const blockerIds = [ serverAccountId ]
195 if (user) blockerIds.push(user.Account.id)
196
197 const inClause = createSafeIn(this.sequelize, blockerIds)
198
199 this.addJoin(
200 'LEFT JOIN "accountBlocklist" AS "VideoChannel->Account->AccountBlocklist" ' +
201 'ON "VideoChannel->Account"."id" = "VideoChannel->Account->AccountBlocklist"."targetAccountId" ' +
202 'AND "VideoChannel->Account->AccountBlocklist"."accountId" IN (' + inClause + ')'
203 )
204
205 this.addJoin(
206 'LEFT JOIN "serverBlocklist" AS "VideoChannel->Account->Actor->Server->ServerBlocklist" ' +
207 'ON "VideoChannel->Account->Actor->Server->ServerBlocklist"."targetServerId" = "VideoChannel->Account->Actor"."serverId" ' +
208 'AND "VideoChannel->Account->Actor->Server->ServerBlocklist"."accountId" IN (' + inClause + ') '
209 )
210
211 this.attributes = {
212 ...this.attributes,
213
214 ...this.buildAttributesObject('VideoChannel->Account->AccountBlocklist', this.tables.getBlocklistAttributes()),
215 ...this.buildAttributesObject('VideoChannel->Account->Actor->Server->ServerBlocklist', this.tables.getBlocklistAttributes())
216 }
217 }
218
219 protected includeScheduleUpdate () {
220 this.addJoin(
221 'LEFT OUTER JOIN "scheduleVideoUpdate" AS "ScheduleVideoUpdate" ON "video"."id" = "ScheduleVideoUpdate"."videoId"'
222 )
223
224 this.attributes = {
225 ...this.attributes,
226
227 ...this.buildAttributesObject('ScheduleVideoUpdate', this.tables.getScheduleUpdateAttributes())
228 }
229 }
230
231 protected includeLive () {
232 this.addJoin(
233 'LEFT OUTER JOIN "videoLive" AS "VideoLive" ON "video"."id" = "VideoLive"."videoId"'
234 )
235
236 this.attributes = {
237 ...this.attributes,
238
239 ...this.buildAttributesObject('VideoLive', this.tables.getLiveAttributes())
240 }
241 }
242
243 protected includeTrackers () {
244 this.addJoin(
245 'LEFT OUTER JOIN (' +
246 '"videoTracker" AS "Trackers->VideoTrackerModel" ' +
247 'INNER JOIN "tracker" AS "Trackers" ON "Trackers"."id" = "Trackers->VideoTrackerModel"."trackerId"' +
248 ') ON "video"."id" = "Trackers->VideoTrackerModel"."videoId"'
249 )
250
251 this.attributes = {
252 ...this.attributes,
253
254 ...this.buildAttributesObject('Trackers', this.tables.getTrackerAttributes()),
255 ...this.buildAttributesObject('Trackers->VideoTrackerModel', this.tables.getVideoTrackerAttributes())
256 }
257 }
258
259 protected includeWebTorrentRedundancies () {
260 this.addJoin(
261 'LEFT OUTER JOIN "videoRedundancy" AS "VideoFiles->RedundancyVideos" ON ' +
262 '"VideoFiles"."id" = "VideoFiles->RedundancyVideos"."videoFileId"'
263 )
264
265 this.attributes = {
266 ...this.attributes,
267
268 ...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.tables.getRedundancyAttributes())
269 }
270 }
271
272 protected includeStreamingPlaylistRedundancies () {
273 this.addJoin(
274 'LEFT OUTER JOIN "videoRedundancy" AS "VideoStreamingPlaylists->RedundancyVideos" ' +
275 'ON "VideoStreamingPlaylists"."id" = "VideoStreamingPlaylists->RedundancyVideos"."videoStreamingPlaylistId"'
276 )
277
278 this.attributes = {
279 ...this.attributes,
280
281 ...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.tables.getRedundancyAttributes())
282 }
283 }
284
285 protected buildActorInclude (prefixKey: string) {
286 return this.buildAttributesObject(prefixKey, this.tables.getActorAttributes())
287 }
288
289 protected buildAvatarInclude (prefixKey: string) {
290 return this.buildAttributesObject(prefixKey, this.tables.getAvatarAttributes())
291 }
292
293 protected buildServerInclude (prefixKey: string) {
294 return this.buildAttributesObject(prefixKey, this.tables.getServerAttributes())
295 }
296
297 protected buildAttributesObject (prefixKey: string, attributeKeys: string[]) {
298 const result: { [id: string]: string} = {}
299
300 const prefixValue = prefixKey.replace(/->/g, '.')
301
302 for (const attribute of attributeKeys) {
303 result[`"${prefixKey}"."${attribute}"`] = `"${prefixValue}.${attribute}"`
304 }
305
306 return result
307 }
308
309 protected whereId (options: { id?: string | number, url?: string }) {
310 if (options.url) {
311 this.where = 'WHERE "video"."url" = :videoUrl'
312 this.replacements.videoUrl = options.url
313 return
314 }
315
316 if (validator.isInt('' + options.id)) {
317 this.where = 'WHERE "video".id = :videoId'
318 } else {
319 this.where = 'WHERE uuid = :videoId'
320 }
321
322 this.replacements.videoId = options.id
323 }
324
325 protected addJoin (join: string) {
326 this.joins += join + ' '
327 }
328}
diff --git a/server/models/video/sql/shared/video-file-query-builder.ts b/server/models/video/sql/shared/video-file-query-builder.ts
deleted file mode 100644
index 3eb3dc07d..000000000
--- a/server/models/video/sql/shared/video-file-query-builder.ts
+++ /dev/null
@@ -1,69 +0,0 @@
1import { Sequelize } from 'sequelize'
2import { BuildVideoGetQueryOptions } from '../video-model-get-query-builder'
3import { AbstractVideoQueryBuilder } from './abstract-video-query-builder'
4
5/**
6 *
7 * Fetch files (webtorrent and streaming playlist) according to a video
8 *
9 */
10
11export class VideoFileQueryBuilder extends AbstractVideoQueryBuilder {
12 protected attributes: { [key: string]: string }
13
14 constructor (protected readonly sequelize: Sequelize) {
15 super('get')
16 }
17
18 queryWebTorrentVideos (options: BuildVideoGetQueryOptions) {
19 this.buildWebtorrentFilesQuery(options)
20
21 return this.runQuery(options)
22 }
23
24 queryStreamingPlaylistVideos (options: BuildVideoGetQueryOptions) {
25 this.buildVideoStreamingPlaylistFilesQuery(options)
26
27 return this.runQuery(options)
28 }
29
30 private buildWebtorrentFilesQuery (options: BuildVideoGetQueryOptions) {
31 this.attributes = {
32 '"video"."id"': ''
33 }
34
35 this.includeWebtorrentFiles()
36
37 if (this.shouldIncludeRedundancies(options)) {
38 this.includeWebTorrentRedundancies()
39 }
40
41 this.whereId(options)
42
43 this.query = this.buildQuery()
44 }
45
46 private buildVideoStreamingPlaylistFilesQuery (options: BuildVideoGetQueryOptions) {
47 this.attributes = {
48 '"video"."id"': ''
49 }
50
51 this.includeStreamingPlaylistFiles()
52
53 if (this.shouldIncludeRedundancies(options)) {
54 this.includeStreamingPlaylistRedundancies()
55 }
56
57 this.whereId(options)
58
59 this.query = this.buildQuery()
60 }
61
62 private buildQuery () {
63 return `${this.buildSelect()} FROM "video" ${this.joins} ${this.where}`
64 }
65
66 private shouldIncludeRedundancies (options: BuildVideoGetQueryOptions) {
67 return options.type === 'api'
68 }
69}
diff --git a/server/models/video/sql/shared/video-model-builder.ts b/server/models/video/sql/shared/video-model-builder.ts
deleted file mode 100644
index 7751d8e68..000000000
--- a/server/models/video/sql/shared/video-model-builder.ts
+++ /dev/null
@@ -1,387 +0,0 @@
1
2import { AccountModel } from '@server/models/account/account'
3import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
4import { ActorModel } from '@server/models/actor/actor'
5import { ActorImageModel } from '@server/models/actor/actor-image'
6import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
7import { ServerModel } from '@server/models/server/server'
8import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
9import { TrackerModel } from '@server/models/server/tracker'
10import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
11import { VideoInclude } from '@shared/models'
12import { ScheduleVideoUpdateModel } from '../../schedule-video-update'
13import { TagModel } from '../../tag'
14import { ThumbnailModel } from '../../thumbnail'
15import { VideoModel } from '../../video'
16import { VideoBlacklistModel } from '../../video-blacklist'
17import { VideoChannelModel } from '../../video-channel'
18import { VideoFileModel } from '../../video-file'
19import { VideoLiveModel } from '../../video-live'
20import { VideoStreamingPlaylistModel } from '../../video-streaming-playlist'
21import { VideoTableAttributes } from './video-table-attributes'
22
23type SQLRow = { [id: string]: string | number }
24
25/**
26 *
27 * Build video models from SQL rows
28 *
29 */
30
31export class VideoModelBuilder {
32 private videosMemo: { [ id: number ]: VideoModel }
33 private videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel }
34 private videoFileMemo: { [ id: number ]: VideoFileModel }
35
36 private thumbnailsDone: Set<any>
37 private historyDone: Set<any>
38 private blacklistDone: Set<any>
39 private accountBlocklistDone: Set<any>
40 private serverBlocklistDone: Set<any>
41 private liveDone: Set<any>
42 private redundancyDone: Set<any>
43 private scheduleVideoUpdateDone: Set<any>
44
45 private trackersDone: Set<string>
46 private tagsDone: Set<string>
47
48 private videos: VideoModel[]
49
50 private readonly buildOpts = { raw: true, isNewRecord: false }
51
52 constructor (
53 readonly mode: 'get' | 'list',
54 readonly tables: VideoTableAttributes
55 ) {
56
57 }
58
59 buildVideosFromRows (options: {
60 rows: SQLRow[]
61 include?: VideoInclude
62 rowsWebTorrentFiles?: SQLRow[]
63 rowsStreamingPlaylist?: SQLRow[]
64 }) {
65 const { rows, rowsWebTorrentFiles, rowsStreamingPlaylist, include } = options
66
67 this.reinit()
68
69 for (const row of rows) {
70 this.buildVideoAndAccount(row)
71
72 const videoModel = this.videosMemo[row.id]
73
74 this.setUserHistory(row, videoModel)
75 this.addThumbnail(row, videoModel)
76
77 if (!rowsWebTorrentFiles) {
78 this.addWebTorrentFile(row, videoModel)
79 }
80
81 if (!rowsStreamingPlaylist) {
82 this.addStreamingPlaylist(row, videoModel)
83 this.addStreamingPlaylistFile(row)
84 }
85
86 if (this.mode === 'get') {
87 this.addTag(row, videoModel)
88 this.addTracker(row, videoModel)
89 this.setBlacklisted(row, videoModel)
90 this.setScheduleVideoUpdate(row, videoModel)
91 this.setLive(row, videoModel)
92 } else {
93 if (include & VideoInclude.BLACKLISTED) {
94 this.setBlacklisted(row, videoModel)
95 }
96
97 if (include & VideoInclude.BLOCKED_OWNER) {
98 this.setBlockedOwner(row, videoModel)
99 this.setBlockedServer(row, videoModel)
100 }
101 }
102 }
103
104 this.grabSeparateWebTorrentFiles(rowsWebTorrentFiles)
105 this.grabSeparateStreamingPlaylistFiles(rowsStreamingPlaylist)
106
107 return this.videos
108 }
109
110 private reinit () {
111 this.videosMemo = {}
112 this.videoStreamingPlaylistMemo = {}
113 this.videoFileMemo = {}
114
115 this.thumbnailsDone = new Set()
116 this.historyDone = new Set()
117 this.blacklistDone = new Set()
118 this.liveDone = new Set()
119 this.redundancyDone = new Set()
120 this.scheduleVideoUpdateDone = new Set()
121
122 this.accountBlocklistDone = new Set()
123 this.serverBlocklistDone = new Set()
124
125 this.trackersDone = new Set()
126 this.tagsDone = new Set()
127
128 this.videos = []
129 }
130
131 private grabSeparateWebTorrentFiles (rowsWebTorrentFiles?: SQLRow[]) {
132 if (!rowsWebTorrentFiles) return
133
134 for (const row of rowsWebTorrentFiles) {
135 const id = row['VideoFiles.id']
136 if (!id) continue
137
138 const videoModel = this.videosMemo[row.id]
139 this.addWebTorrentFile(row, videoModel)
140 this.addRedundancy(row, 'VideoFiles', this.videoFileMemo[id])
141 }
142 }
143
144 private grabSeparateStreamingPlaylistFiles (rowsStreamingPlaylist?: SQLRow[]) {
145 if (!rowsStreamingPlaylist) return
146
147 for (const row of rowsStreamingPlaylist) {
148 const id = row['VideoStreamingPlaylists.id']
149 if (!id) continue
150
151 const videoModel = this.videosMemo[row.id]
152
153 this.addStreamingPlaylist(row, videoModel)
154 this.addStreamingPlaylistFile(row)
155 this.addRedundancy(row, 'VideoStreamingPlaylists', this.videoStreamingPlaylistMemo[id])
156 }
157 }
158
159 private buildVideoAndAccount (row: SQLRow) {
160 if (this.videosMemo[row.id]) return
161
162 const videoModel = new VideoModel(this.grab(row, this.tables.getVideoAttributes(), ''), this.buildOpts)
163
164 videoModel.UserVideoHistories = []
165 videoModel.Thumbnails = []
166 videoModel.VideoFiles = []
167 videoModel.VideoStreamingPlaylists = []
168 videoModel.Tags = []
169 videoModel.Trackers = []
170
171 this.buildAccount(row, videoModel)
172
173 this.videosMemo[row.id] = videoModel
174
175 // Keep rows order
176 this.videos.push(videoModel)
177 }
178
179 private buildAccount (row: SQLRow, videoModel: VideoModel) {
180 const id = row['VideoChannel.Account.id']
181 if (!id) return
182
183 const channelModel = new VideoChannelModel(this.grab(row, this.tables.getChannelAttributes(), 'VideoChannel'), this.buildOpts)
184 channelModel.Actor = this.buildActor(row, 'VideoChannel')
185
186 const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
187 accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
188
189 accountModel.BlockedBy = []
190
191 channelModel.Account = accountModel
192
193 videoModel.VideoChannel = channelModel
194 }
195
196 private buildActor (row: SQLRow, prefix: string) {
197 const actorPrefix = `${prefix}.Actor`
198 const avatarPrefix = `${actorPrefix}.Avatar`
199 const serverPrefix = `${actorPrefix}.Server`
200
201 const avatarModel = row[`${avatarPrefix}.id`] !== null
202 ? new ActorImageModel(this.grab(row, this.tables.getAvatarAttributes(), avatarPrefix), this.buildOpts)
203 : null
204
205 const serverModel = row[`${serverPrefix}.id`] !== null
206 ? new ServerModel(this.grab(row, this.tables.getServerAttributes(), serverPrefix), this.buildOpts)
207 : null
208
209 if (serverModel) serverModel.BlockedBy = []
210
211 const actorModel = new ActorModel(this.grab(row, this.tables.getActorAttributes(), actorPrefix), this.buildOpts)
212 actorModel.Avatar = avatarModel
213 actorModel.Server = serverModel
214
215 return actorModel
216 }
217
218 private setUserHistory (row: SQLRow, videoModel: VideoModel) {
219 const id = row['userVideoHistory.id']
220 if (!id || this.historyDone.has(id)) return
221
222 const attributes = this.grab(row, this.tables.getUserHistoryAttributes(), 'userVideoHistory')
223 const historyModel = new UserVideoHistoryModel(attributes, this.buildOpts)
224 videoModel.UserVideoHistories.push(historyModel)
225
226 this.historyDone.add(id)
227 }
228
229 private addThumbnail (row: SQLRow, videoModel: VideoModel) {
230 const id = row['Thumbnails.id']
231 if (!id || this.thumbnailsDone.has(id)) return
232
233 const attributes = this.grab(row, this.tables.getThumbnailAttributes(), 'Thumbnails')
234 const thumbnailModel = new ThumbnailModel(attributes, this.buildOpts)
235 videoModel.Thumbnails.push(thumbnailModel)
236
237 this.thumbnailsDone.add(id)
238 }
239
240 private addWebTorrentFile (row: SQLRow, videoModel: VideoModel) {
241 const id = row['VideoFiles.id']
242 if (!id || this.videoFileMemo[id]) return
243
244 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoFiles')
245 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
246 videoModel.VideoFiles.push(videoFileModel)
247
248 this.videoFileMemo[id] = videoFileModel
249 }
250
251 private addStreamingPlaylist (row: SQLRow, videoModel: VideoModel) {
252 const id = row['VideoStreamingPlaylists.id']
253 if (!id || this.videoStreamingPlaylistMemo[id]) return
254
255 const attributes = this.grab(row, this.tables.getStreamingPlaylistAttributes(), 'VideoStreamingPlaylists')
256 const streamingPlaylist = new VideoStreamingPlaylistModel(attributes, this.buildOpts)
257 streamingPlaylist.VideoFiles = []
258
259 videoModel.VideoStreamingPlaylists.push(streamingPlaylist)
260
261 this.videoStreamingPlaylistMemo[id] = streamingPlaylist
262 }
263
264 private addStreamingPlaylistFile (row: SQLRow) {
265 const id = row['VideoStreamingPlaylists.VideoFiles.id']
266 if (!id || this.videoFileMemo[id]) return
267
268 const streamingPlaylist = this.videoStreamingPlaylistMemo[row['VideoStreamingPlaylists.id']]
269
270 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoStreamingPlaylists.VideoFiles')
271 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
272 streamingPlaylist.VideoFiles.push(videoFileModel)
273
274 this.videoFileMemo[id] = videoFileModel
275 }
276
277 private addRedundancy (row: SQLRow, prefix: string, to: VideoFileModel | VideoStreamingPlaylistModel) {
278 if (!to.RedundancyVideos) to.RedundancyVideos = []
279
280 const redundancyPrefix = `${prefix}.RedundancyVideos`
281 const id = row[`${redundancyPrefix}.id`]
282
283 if (!id || this.redundancyDone.has(id)) return
284
285 const attributes = this.grab(row, this.tables.getRedundancyAttributes(), redundancyPrefix)
286 const redundancyModel = new VideoRedundancyModel(attributes, this.buildOpts)
287 to.RedundancyVideos.push(redundancyModel)
288
289 this.redundancyDone.add(id)
290 }
291
292 private addTag (row: SQLRow, videoModel: VideoModel) {
293 if (!row['Tags.name']) return
294
295 const key = `${row['Tags.VideoTagModel.videoId']}-${row['Tags.VideoTagModel.tagId']}`
296 if (this.tagsDone.has(key)) return
297
298 const attributes = this.grab(row, this.tables.getTagAttributes(), 'Tags')
299 const tagModel = new TagModel(attributes, this.buildOpts)
300 videoModel.Tags.push(tagModel)
301
302 this.tagsDone.add(key)
303 }
304
305 private addTracker (row: SQLRow, videoModel: VideoModel) {
306 if (!row['Trackers.id']) return
307
308 const key = `${row['Trackers.VideoTrackerModel.videoId']}-${row['Trackers.VideoTrackerModel.trackerId']}`
309 if (this.trackersDone.has(key)) return
310
311 const attributes = this.grab(row, this.tables.getTrackerAttributes(), 'Trackers')
312 const trackerModel = new TrackerModel(attributes, this.buildOpts)
313 videoModel.Trackers.push(trackerModel)
314
315 this.trackersDone.add(key)
316 }
317
318 private setBlacklisted (row: SQLRow, videoModel: VideoModel) {
319 const id = row['VideoBlacklist.id']
320 if (!id || this.blacklistDone.has(id)) return
321
322 const attributes = this.grab(row, this.tables.getBlacklistedAttributes(), 'VideoBlacklist')
323 videoModel.VideoBlacklist = new VideoBlacklistModel(attributes, this.buildOpts)
324
325 this.blacklistDone.add(id)
326 }
327
328 private setBlockedOwner (row: SQLRow, videoModel: VideoModel) {
329 const id = row['VideoChannel.Account.AccountBlocklist.id']
330 if (!id) return
331
332 const key = `${videoModel.id}-${id}`
333 if (this.accountBlocklistDone.has(key)) return
334
335 const attributes = this.grab(row, this.tables.getBlocklistAttributes(), 'VideoChannel.Account.AccountBlocklist')
336 videoModel.VideoChannel.Account.BlockedBy.push(new AccountBlocklistModel(attributes, this.buildOpts))
337
338 this.accountBlocklistDone.add(key)
339 }
340
341 private setBlockedServer (row: SQLRow, videoModel: VideoModel) {
342 const id = row['VideoChannel.Account.Actor.Server.ServerBlocklist.id']
343 if (!id || this.serverBlocklistDone.has(id)) return
344
345 const key = `${videoModel.id}-${id}`
346 if (this.serverBlocklistDone.has(key)) return
347
348 const attributes = this.grab(row, this.tables.getBlocklistAttributes(), 'VideoChannel.Account.Actor.Server.ServerBlocklist')
349 videoModel.VideoChannel.Account.Actor.Server.BlockedBy.push(new ServerBlocklistModel(attributes, this.buildOpts))
350
351 this.serverBlocklistDone.add(key)
352 }
353
354 private setScheduleVideoUpdate (row: SQLRow, videoModel: VideoModel) {
355 const id = row['ScheduleVideoUpdate.id']
356 if (!id || this.scheduleVideoUpdateDone.has(id)) return
357
358 const attributes = this.grab(row, this.tables.getScheduleUpdateAttributes(), 'ScheduleVideoUpdate')
359 videoModel.ScheduleVideoUpdate = new ScheduleVideoUpdateModel(attributes, this.buildOpts)
360
361 this.scheduleVideoUpdateDone.add(id)
362 }
363
364 private setLive (row: SQLRow, videoModel: VideoModel) {
365 const id = row['VideoLive.id']
366 if (!id || this.liveDone.has(id)) return
367
368 const attributes = this.grab(row, this.tables.getLiveAttributes(), 'VideoLive')
369 videoModel.VideoLive = new VideoLiveModel(attributes, this.buildOpts)
370
371 this.liveDone.add(id)
372 }
373
374 private grab (row: SQLRow, attributes: string[], prefix: string) {
375 const result: { [ id: string ]: string | number } = {}
376
377 for (const a of attributes) {
378 const key = prefix
379 ? prefix + '.' + a
380 : a
381
382 result[a] = row[key]
383 }
384
385 return result
386 }
387}
diff --git a/server/models/video/sql/shared/video-table-attributes.ts b/server/models/video/sql/shared/video-table-attributes.ts
deleted file mode 100644
index 8a8d2073a..000000000
--- a/server/models/video/sql/shared/video-table-attributes.ts
+++ /dev/null
@@ -1,269 +0,0 @@
1
2/**
3 *
4 * Class to build video attributes/join names we want to fetch from the database
5 *
6 */
7export class VideoTableAttributes {
8
9 constructor (readonly mode: 'get' | 'list') {
10
11 }
12
13 getChannelAttributesForUser () {
14 return [ 'id', 'accountId' ]
15 }
16
17 getChannelAttributes () {
18 let attributeKeys = [
19 'id',
20 'name',
21 'description',
22 'actorId'
23 ]
24
25 if (this.mode === 'get') {
26 attributeKeys = attributeKeys.concat([
27 'support',
28 'createdAt',
29 'updatedAt'
30 ])
31 }
32
33 return attributeKeys
34 }
35
36 getUserAccountAttributes () {
37 return [ 'id', 'userId' ]
38 }
39
40 getAccountAttributes () {
41 let attributeKeys = [ 'id', 'name', 'actorId' ]
42
43 if (this.mode === 'get') {
44 attributeKeys = attributeKeys.concat([
45 'description',
46 'userId',
47 'createdAt',
48 'updatedAt'
49 ])
50 }
51
52 return attributeKeys
53 }
54
55 getThumbnailAttributes () {
56 let attributeKeys = [ 'id', 'type', 'filename' ]
57
58 if (this.mode === 'get') {
59 attributeKeys = attributeKeys.concat([
60 'height',
61 'width',
62 'fileUrl',
63 'automaticallyGenerated',
64 'videoId',
65 'videoPlaylistId',
66 'createdAt',
67 'updatedAt'
68 ])
69 }
70
71 return attributeKeys
72 }
73
74 getFileAttributes () {
75 return [
76 'id',
77 'createdAt',
78 'updatedAt',
79 'resolution',
80 'size',
81 'extname',
82 'filename',
83 'fileUrl',
84 'torrentFilename',
85 'torrentUrl',
86 'infoHash',
87 'fps',
88 'metadataUrl',
89 'videoStreamingPlaylistId',
90 'videoId',
91 'storage'
92 ]
93 }
94
95 getStreamingPlaylistAttributes () {
96 return [
97 'id',
98 'playlistUrl',
99 'playlistFilename',
100 'type',
101 'p2pMediaLoaderInfohashes',
102 'p2pMediaLoaderPeerVersion',
103 'segmentsSha256Filename',
104 'segmentsSha256Url',
105 'videoId',
106 'createdAt',
107 'updatedAt',
108 'storage'
109 ]
110 }
111
112 getUserHistoryAttributes () {
113 return [ 'id', 'currentTime' ]
114 }
115
116 getPlaylistAttributes () {
117 return [
118 'createdAt',
119 'updatedAt',
120 'url',
121 'position',
122 'startTimestamp',
123 'stopTimestamp',
124 'videoPlaylistId'
125 ]
126 }
127
128 getTagAttributes () {
129 return [ 'id', 'name' ]
130 }
131
132 getVideoTagAttributes () {
133 return [ 'videoId', 'tagId', 'createdAt', 'updatedAt' ]
134 }
135
136 getBlacklistedAttributes () {
137 return [ 'id', 'reason', 'unfederated' ]
138 }
139
140 getBlocklistAttributes () {
141 return [ 'id' ]
142 }
143
144 getScheduleUpdateAttributes () {
145 return [
146 'id',
147 'updateAt',
148 'privacy',
149 'videoId',
150 'createdAt',
151 'updatedAt'
152 ]
153 }
154
155 getLiveAttributes () {
156 return [
157 'id',
158 'streamKey',
159 'saveReplay',
160 'permanentLive',
161 'videoId',
162 'createdAt',
163 'updatedAt'
164 ]
165 }
166
167 getTrackerAttributes () {
168 return [ 'id', 'url' ]
169 }
170
171 getVideoTrackerAttributes () {
172 return [
173 'videoId',
174 'trackerId',
175 'createdAt',
176 'updatedAt'
177 ]
178 }
179
180 getRedundancyAttributes () {
181 return [ 'id', 'fileUrl' ]
182 }
183
184 getActorAttributes () {
185 let attributeKeys = [
186 'id',
187 'preferredUsername',
188 'url',
189 'serverId',
190 'avatarId'
191 ]
192
193 if (this.mode === 'get') {
194 attributeKeys = attributeKeys.concat([
195 'type',
196 'followersCount',
197 'followingCount',
198 'inboxUrl',
199 'outboxUrl',
200 'sharedInboxUrl',
201 'followersUrl',
202 'followingUrl',
203 'remoteCreatedAt',
204 'createdAt',
205 'updatedAt'
206 ])
207 }
208
209 return attributeKeys
210 }
211
212 getAvatarAttributes () {
213 let attributeKeys = [
214 'id',
215 'filename',
216 'type',
217 'fileUrl',
218 'onDisk',
219 'createdAt',
220 'updatedAt'
221 ]
222
223 if (this.mode === 'get') {
224 attributeKeys = attributeKeys.concat([
225 'height',
226 'width',
227 'type'
228 ])
229 }
230
231 return attributeKeys
232 }
233
234 getServerAttributes () {
235 return [ 'id', 'host' ]
236 }
237
238 getVideoAttributes () {
239 return [
240 'id',
241 'uuid',
242 'name',
243 'category',
244 'licence',
245 'language',
246 'privacy',
247 'nsfw',
248 'description',
249 'support',
250 'duration',
251 'views',
252 'likes',
253 'dislikes',
254 'remote',
255 'isLive',
256 'url',
257 'commentsEnabled',
258 'downloadEnabled',
259 'waitTranscoding',
260 'state',
261 'publishedAt',
262 'originallyPublishedAt',
263 'channelId',
264 'createdAt',
265 'updatedAt',
266 'moveJobsRunning'
267 ]
268 }
269}