aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/sql/shared
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/sql/shared')
-rw-r--r--server/models/video/sql/shared/abstract-videos-model-query-builder.ts300
-rw-r--r--server/models/video/sql/shared/abstract-videos-query-builder.ts26
-rw-r--r--server/models/video/sql/shared/video-file-query-builder.ts69
-rw-r--r--server/models/video/sql/shared/video-model-builder.ts333
-rw-r--r--server/models/video/sql/shared/video-tables.ts263
5 files changed, 991 insertions, 0 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
new file mode 100644
index 000000000..0d7e64574
--- /dev/null
+++ b/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
@@ -0,0 +1,300 @@
1import validator from 'validator'
2import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
3import { VideoTables } from './video-tables'
4
5/**
6 *
7 * Abstract builder to create SQL query and fetch video models
8 *
9 */
10
11export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder {
12 protected attributes: { [key: string]: string } = {}
13
14 protected joins = ''
15 protected where: string
16
17 protected tables: VideoTables
18
19 constructor (protected readonly mode: 'list' | 'get') {
20 super()
21
22 this.tables = new VideoTables(this.mode)
23 }
24
25 protected buildSelect () {
26 return 'SELECT ' + Object.keys(this.attributes).map(key => {
27 const value = this.attributes[key]
28 if (value) return `${key} AS ${value}`
29
30 return key
31 }).join(', ')
32 }
33
34 protected includeChannels () {
35 this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
36 this.addJoin('INNER JOIN "actor" AS "VideoChannel->Actor" ON "VideoChannel"."actorId" = "VideoChannel->Actor"."id"')
37
38 this.addJoin(
39 'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"'
40 )
41
42 this.addJoin(
43 'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Actor->Avatar" ' +
44 'ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"'
45 )
46
47 this.attributes = {
48 ...this.attributes,
49
50 ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
51 ...this.buildActorInclude('VideoChannel->Actor'),
52 ...this.buildAvatarInclude('VideoChannel->Actor->Avatar'),
53 ...this.buildServerInclude('VideoChannel->Actor->Server')
54 }
55 }
56
57 protected includeAccounts () {
58 this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
59 this.addJoin(
60 'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"'
61 )
62
63 this.addJoin(
64 'LEFT OUTER JOIN "server" AS "VideoChannel->Account->Actor->Server" ' +
65 'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"'
66 )
67
68 this.addJoin(
69 'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Account->Actor->Avatar" ' +
70 'ON "VideoChannel->Account->Actor"."avatarId" = "VideoChannel->Account->Actor->Avatar"."id"'
71 )
72
73 this.attributes = {
74 ...this.attributes,
75
76 ...this.buildAttributesObject('VideoChannel->Account', this.tables.getAccountAttributes()),
77 ...this.buildActorInclude('VideoChannel->Account->Actor'),
78 ...this.buildAvatarInclude('VideoChannel->Account->Actor->Avatar'),
79 ...this.buildServerInclude('VideoChannel->Account->Actor->Server')
80 }
81 }
82
83 protected includeOwnerUser () {
84 this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
85 this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
86
87 this.attributes = {
88 ...this.attributes,
89
90 ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
91 ...this.buildAttributesObject('VideoChannel->Account', this.tables.getUserAccountAttributes())
92 }
93 }
94
95 protected includeThumbnails () {
96 this.addJoin('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
97
98 this.attributes = {
99 ...this.attributes,
100
101 ...this.buildAttributesObject('Thumbnails', this.tables.getThumbnailAttributes())
102 }
103 }
104
105 protected includeWebtorrentFiles () {
106 this.addJoin('LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"')
107
108 this.attributes = {
109 ...this.attributes,
110
111 ...this.buildAttributesObject('VideoFiles', this.tables.getFileAttributes())
112 }
113 }
114
115 protected includeStreamingPlaylistFiles () {
116 this.addJoin(
117 'LEFT JOIN "videoStreamingPlaylist" AS "VideoStreamingPlaylists" ON "VideoStreamingPlaylists"."videoId" = "video"."id"'
118 )
119
120 this.addJoin(
121 'LEFT JOIN "videoFile" AS "VideoStreamingPlaylists->VideoFiles" ' +
122 'ON "VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId" = "VideoStreamingPlaylists"."id"'
123 )
124
125 this.attributes = {
126 ...this.attributes,
127
128 ...this.buildAttributesObject('VideoStreamingPlaylists', this.tables.getStreamingPlaylistAttributes()),
129 ...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.tables.getFileAttributes())
130 }
131 }
132
133 protected includeUserHistory (userId: number) {
134 this.addJoin(
135 'LEFT OUTER JOIN "userVideoHistory" ' +
136 'ON "video"."id" = "userVideoHistory"."videoId" AND "userVideoHistory"."userId" = :userVideoHistoryId'
137 )
138
139 this.replacements.userVideoHistoryId = userId
140
141 this.attributes = {
142 ...this.attributes,
143
144 ...this.buildAttributesObject('userVideoHistory', this.tables.getUserHistoryAttributes())
145 }
146 }
147
148 protected includePlaylist (playlistId: number) {
149 this.addJoin(
150 'INNER JOIN "videoPlaylistElement" as "VideoPlaylistElement" ON "videoPlaylistElement"."videoId" = "video"."id" ' +
151 'AND "VideoPlaylistElement"."videoPlaylistId" = :videoPlaylistId'
152 )
153
154 this.replacements.videoPlaylistId = playlistId
155
156 this.attributes = {
157 ...this.attributes,
158
159 ...this.buildAttributesObject('VideoPlaylistElement', this.tables.getPlaylistAttributes())
160 }
161 }
162
163 protected includeTags () {
164 this.addJoin(
165 'LEFT OUTER JOIN (' +
166 '"videoTag" AS "Tags->VideoTagModel" INNER JOIN "tag" AS "Tags" ON "Tags"."id" = "Tags->VideoTagModel"."tagId"' +
167 ') ' +
168 'ON "video"."id" = "Tags->VideoTagModel"."videoId"'
169 )
170
171 this.attributes = {
172 ...this.attributes,
173
174 ...this.buildAttributesObject('Tags', this.tables.getTagAttributes()),
175 ...this.buildAttributesObject('Tags->VideoTagModel', this.tables.getVideoTagAttributes())
176 }
177 }
178
179 protected includeBlacklisted () {
180 this.addJoin(
181 'LEFT OUTER JOIN "videoBlacklist" AS "VideoBlacklist" ON "video"."id" = "VideoBlacklist"."videoId"'
182 )
183
184 this.attributes = {
185 ...this.attributes,
186
187 ...this.buildAttributesObject('VideoBlacklist', this.tables.getBlacklistedAttributes())
188 }
189 }
190
191 protected includeScheduleUpdate () {
192 this.addJoin(
193 'LEFT OUTER JOIN "scheduleVideoUpdate" AS "ScheduleVideoUpdate" ON "video"."id" = "ScheduleVideoUpdate"."videoId"'
194 )
195
196 this.attributes = {
197 ...this.attributes,
198
199 ...this.buildAttributesObject('ScheduleVideoUpdate', this.tables.getScheduleUpdateAttributes())
200 }
201 }
202
203 protected includeLive () {
204 this.addJoin(
205 'LEFT OUTER JOIN "videoLive" AS "VideoLive" ON "video"."id" = "VideoLive"."videoId"'
206 )
207
208 this.attributes = {
209 ...this.attributes,
210
211 ...this.buildAttributesObject('VideoLive', this.tables.getLiveAttributes())
212 }
213 }
214
215 protected includeTrackers () {
216 this.addJoin(
217 'LEFT OUTER JOIN (' +
218 '"videoTracker" AS "Trackers->VideoTrackerModel" ' +
219 'INNER JOIN "tracker" AS "Trackers" ON "Trackers"."id" = "Trackers->VideoTrackerModel"."trackerId"' +
220 ') ON "video"."id" = "Trackers->VideoTrackerModel"."videoId"'
221 )
222
223 this.attributes = {
224 ...this.attributes,
225
226 ...this.buildAttributesObject('Trackers', this.tables.getTrackerAttributes()),
227 ...this.buildAttributesObject('Trackers->VideoTrackerModel', this.tables.getVideoTrackerAttributes())
228 }
229 }
230
231 protected includeWebTorrentRedundancies () {
232 this.addJoin(
233 'LEFT OUTER JOIN "videoRedundancy" AS "VideoFiles->RedundancyVideos" ON ' +
234 '"VideoFiles"."id" = "VideoFiles->RedundancyVideos"."videoFileId"'
235 )
236
237 this.attributes = {
238 ...this.attributes,
239
240 ...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.tables.getRedundancyAttributes())
241 }
242 }
243
244 protected includeStreamingPlaylistRedundancies () {
245 this.addJoin(
246 'LEFT OUTER JOIN "videoRedundancy" AS "VideoStreamingPlaylists->RedundancyVideos" ' +
247 'ON "VideoStreamingPlaylists"."id" = "VideoStreamingPlaylists->RedundancyVideos"."videoStreamingPlaylistId"'
248 )
249
250 this.attributes = {
251 ...this.attributes,
252
253 ...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.tables.getRedundancyAttributes())
254 }
255 }
256
257 protected buildActorInclude (prefixKey: string) {
258 return this.buildAttributesObject(prefixKey, this.tables.getActorAttributes())
259 }
260
261 protected buildAvatarInclude (prefixKey: string) {
262 return this.buildAttributesObject(prefixKey, this.tables.getAvatarAttributes())
263 }
264
265 protected buildServerInclude (prefixKey: string) {
266 return this.buildAttributesObject(prefixKey, this.tables.getServerAttributes())
267 }
268
269 protected buildAttributesObject (prefixKey: string, attributeKeys: string[]) {
270 const result: { [id: string]: string} = {}
271
272 const prefixValue = prefixKey.replace(/->/g, '.')
273
274 for (const attribute of attributeKeys) {
275 result[`"${prefixKey}"."${attribute}"`] = `"${prefixValue}.${attribute}"`
276 }
277
278 return result
279 }
280
281 protected whereId (options: { id?: string | number, url?: string }) {
282 if (options.url) {
283 this.where = 'WHERE "video"."url" = :videoUrl'
284 this.replacements.videoUrl = options.url
285 return
286 }
287
288 if (validator.isInt('' + options.id)) {
289 this.where = 'WHERE "video".id = :videoId'
290 } else {
291 this.where = 'WHERE uuid = :videoId'
292 }
293
294 this.replacements.videoId = options.id
295 }
296
297 protected addJoin (join: string) {
298 this.joins += join + ' '
299 }
300}
diff --git a/server/models/video/sql/shared/abstract-videos-query-builder.ts b/server/models/video/sql/shared/abstract-videos-query-builder.ts
new file mode 100644
index 000000000..09776bcb0
--- /dev/null
+++ b/server/models/video/sql/shared/abstract-videos-query-builder.ts
@@ -0,0 +1,26 @@
1import { QueryTypes, Sequelize, Transaction } from 'sequelize'
2
3/**
4 *
5 * Abstact builder to run video SQL queries
6 *
7 */
8
9export class AbstractVideosQueryBuilder {
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/video-file-query-builder.ts b/server/models/video/sql/shared/video-file-query-builder.ts
new file mode 100644
index 000000000..6b15c3b69
--- /dev/null
+++ b/server/models/video/sql/shared/video-file-query-builder.ts
@@ -0,0 +1,69 @@
1import { Sequelize } from 'sequelize'
2import { BuildVideoGetQueryOptions } from '../video-model-get-query-builder'
3import { AbstractVideosModelQueryBuilder } from './abstract-videos-model-query-builder'
4
5/**
6 *
7 * Fetch files (webtorrent and streaming playlist) according to a video
8 *
9 */
10
11export class VideoFileQueryBuilder extends AbstractVideosModelQueryBuilder {
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
new file mode 100644
index 000000000..e7e2aa1ca
--- /dev/null
+++ b/server/models/video/sql/shared/video-model-builder.ts
@@ -0,0 +1,333 @@
1
2import { AccountModel } from '@server/models/account/account'
3import { ActorModel } from '@server/models/actor/actor'
4import { ActorImageModel } from '@server/models/actor/actor-image'
5import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
6import { ServerModel } from '@server/models/server/server'
7import { TrackerModel } from '@server/models/server/tracker'
8import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
9import { ScheduleVideoUpdateModel } from '../../schedule-video-update'
10import { TagModel } from '../../tag'
11import { ThumbnailModel } from '../../thumbnail'
12import { VideoModel } from '../../video'
13import { VideoBlacklistModel } from '../../video-blacklist'
14import { VideoChannelModel } from '../../video-channel'
15import { VideoFileModel } from '../../video-file'
16import { VideoLiveModel } from '../../video-live'
17import { VideoStreamingPlaylistModel } from '../../video-streaming-playlist'
18import { VideoTables } from './video-tables'
19
20type SQLRow = { [id: string]: string | number }
21
22/**
23 *
24 * Build video models from SQL rows
25 *
26 */
27
28export class VideoModelBuilder {
29 private videosMemo: { [ id: number ]: VideoModel }
30 private videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel }
31 private videoFileMemo: { [ id: number ]: VideoFileModel }
32
33 private thumbnailsDone: Set<any>
34 private historyDone: Set<any>
35 private blacklistDone: Set<any>
36 private liveDone: Set<any>
37 private redundancyDone: Set<any>
38 private scheduleVideoUpdateDone: Set<any>
39
40 private trackersDone: Set<string>
41 private tagsDone: Set<string>
42
43 private videos: VideoModel[]
44
45 private readonly buildOpts = { raw: true, isNewRecord: false }
46
47 constructor (
48 readonly mode: 'get' | 'list',
49 readonly tables: VideoTables
50 ) {
51
52 }
53
54 buildVideosFromRows (rows: SQLRow[], rowsWebTorrentFiles?: SQLRow[], rowsStreamingPlaylist?: SQLRow[]) {
55 this.reinit()
56
57 for (const row of rows) {
58 this.buildVideoAndAccount(row)
59
60 const videoModel = this.videosMemo[row.id]
61
62 this.setUserHistory(row, videoModel)
63 this.addThumbnail(row, videoModel)
64
65 if (!rowsWebTorrentFiles) {
66 this.addWebTorrentFile(row, videoModel)
67 }
68
69 if (!rowsStreamingPlaylist) {
70 this.addStreamingPlaylist(row, videoModel)
71 this.addStreamingPlaylistFile(row)
72 }
73
74 if (this.mode === 'get') {
75 this.addTag(row, videoModel)
76 this.addTracker(row, videoModel)
77 this.setBlacklisted(row, videoModel)
78 this.setScheduleVideoUpdate(row, videoModel)
79 this.setLive(row, videoModel)
80 }
81 }
82
83 this.grabSeparateWebTorrentFiles(rowsWebTorrentFiles)
84 this.grabSeparateStreamingPlaylistFiles(rowsStreamingPlaylist)
85
86 return this.videos
87 }
88
89 private reinit () {
90 this.videosMemo = {}
91 this.videoStreamingPlaylistMemo = {}
92 this.videoFileMemo = {}
93
94 this.thumbnailsDone = new Set<number>()
95 this.historyDone = new Set<number>()
96 this.blacklistDone = new Set<number>()
97 this.liveDone = new Set<number>()
98 this.redundancyDone = new Set<number>()
99 this.scheduleVideoUpdateDone = new Set<number>()
100
101 this.trackersDone = new Set<string>()
102 this.tagsDone = new Set<string>()
103
104 this.videos = []
105 }
106
107 private grabSeparateWebTorrentFiles (rowsWebTorrentFiles?: SQLRow[]) {
108 if (!rowsWebTorrentFiles) return
109
110 for (const row of rowsWebTorrentFiles) {
111 const id = row['VideoFiles.id']
112 if (!id) continue
113
114 const videoModel = this.videosMemo[row.id]
115 this.addWebTorrentFile(row, videoModel)
116 this.addRedundancy(row, 'VideoFiles', this.videoFileMemo[id])
117 }
118 }
119
120 private grabSeparateStreamingPlaylistFiles (rowsStreamingPlaylist?: SQLRow[]) {
121 if (!rowsStreamingPlaylist) return
122
123 for (const row of rowsStreamingPlaylist || []) {
124 const id = row['VideoStreamingPlaylists.id']
125 if (!id) continue
126
127 const videoModel = this.videosMemo[row.id]
128
129 this.addStreamingPlaylist(row, videoModel)
130 this.addStreamingPlaylistFile(row)
131 this.addRedundancy(row, 'VideoStreamingPlaylists', this.videoStreamingPlaylistMemo[id])
132 }
133 }
134
135 private buildVideoAndAccount (row: SQLRow) {
136 if (this.videosMemo[row.id]) return
137
138 const videoModel = new VideoModel(this.grab(row, this.tables.getVideoAttributes(), ''), this.buildOpts)
139
140 videoModel.UserVideoHistories = []
141 videoModel.Thumbnails = []
142 videoModel.VideoFiles = []
143 videoModel.VideoStreamingPlaylists = []
144 videoModel.Tags = []
145 videoModel.Trackers = []
146
147 this.buildAccount(row, videoModel)
148
149 this.videosMemo[row.id] = videoModel
150
151 // Keep rows order
152 this.videos.push(videoModel)
153 }
154
155 private buildAccount (row: SQLRow, videoModel: VideoModel) {
156 const id = row['VideoChannel.Account.id']
157 if (!id) return
158
159 const channelModel = new VideoChannelModel(this.grab(row, this.tables.getChannelAttributes(), 'VideoChannel'), this.buildOpts)
160 channelModel.Actor = this.buildActor(row, 'VideoChannel')
161
162 const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
163 accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
164
165 channelModel.Account = accountModel
166
167 videoModel.VideoChannel = channelModel
168 }
169
170 private buildActor (row: SQLRow, prefix: string) {
171 const actorPrefix = `${prefix}.Actor`
172 const avatarPrefix = `${actorPrefix}.Avatar`
173 const serverPrefix = `${actorPrefix}.Server`
174
175 const avatarModel = row[`${avatarPrefix}.id`] !== null
176 ? new ActorImageModel(this.grab(row, this.tables.getAvatarAttributes(), avatarPrefix), this.buildOpts)
177 : null
178
179 const serverModel = row[`${serverPrefix}.id`] !== null
180 ? new ServerModel(this.grab(row, this.tables.getServerAttributes(), serverPrefix), this.buildOpts)
181 : null
182
183 const actorModel = new ActorModel(this.grab(row, this.tables.getActorAttributes(), actorPrefix), this.buildOpts)
184 actorModel.Avatar = avatarModel
185 actorModel.Server = serverModel
186
187 return actorModel
188 }
189
190 private setUserHistory (row: SQLRow, videoModel: VideoModel) {
191 const id = row['userVideoHistory.id']
192 if (!id || this.historyDone.has(id)) return
193
194 const attributes = this.grab(row, this.tables.getUserHistoryAttributes(), 'userVideoHistory')
195 const historyModel = new UserVideoHistoryModel(attributes, this.buildOpts)
196 videoModel.UserVideoHistories.push(historyModel)
197
198 this.historyDone.add(id)
199 }
200
201 private addThumbnail (row: SQLRow, videoModel: VideoModel) {
202 const id = row['Thumbnails.id']
203 if (!id || this.thumbnailsDone.has(id)) return
204
205 const attributes = this.grab(row, this.tables.getThumbnailAttributes(), 'Thumbnails')
206 const thumbnailModel = new ThumbnailModel(attributes, this.buildOpts)
207 videoModel.Thumbnails.push(thumbnailModel)
208
209 this.thumbnailsDone.add(id)
210 }
211
212 private addWebTorrentFile (row: SQLRow, videoModel: VideoModel) {
213 const id = row['VideoFiles.id']
214 if (!id || this.videoFileMemo[id]) return
215
216 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoFiles')
217 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
218 videoModel.VideoFiles.push(videoFileModel)
219
220 this.videoFileMemo[id] = videoFileModel
221 }
222
223 private addStreamingPlaylist (row: SQLRow, videoModel: VideoModel) {
224 const id = row['VideoStreamingPlaylists.id']
225 if (!id || this.videoStreamingPlaylistMemo[id]) return
226
227 const attributes = this.grab(row, this.tables.getStreamingPlaylistAttributes(), 'VideoStreamingPlaylists')
228 const streamingPlaylist = new VideoStreamingPlaylistModel(attributes, this.buildOpts)
229 streamingPlaylist.VideoFiles = []
230
231 videoModel.VideoStreamingPlaylists.push(streamingPlaylist)
232
233 this.videoStreamingPlaylistMemo[id] = streamingPlaylist
234 }
235
236 private addStreamingPlaylistFile (row: SQLRow) {
237 const id = row['VideoStreamingPlaylists.VideoFiles.id']
238 if (!id || this.videoFileMemo[id]) return
239
240 const streamingPlaylist = this.videoStreamingPlaylistMemo[row['VideoStreamingPlaylists.id']]
241
242 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoStreamingPlaylists.VideoFiles')
243 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
244 streamingPlaylist.VideoFiles.push(videoFileModel)
245
246 this.videoFileMemo[id] = videoFileModel
247 }
248
249 private addRedundancy (row: SQLRow, prefix: string, to: VideoFileModel | VideoStreamingPlaylistModel) {
250 if (!to.RedundancyVideos) to.RedundancyVideos = []
251
252 const redundancyPrefix = `${prefix}.RedundancyVideos`
253 const id = row[`${redundancyPrefix}.id`]
254
255 if (!id || this.redundancyDone.has(id)) return
256
257 const attributes = this.grab(row, this.tables.getRedundancyAttributes(), redundancyPrefix)
258 const redundancyModel = new VideoRedundancyModel(attributes, this.buildOpts)
259 to.RedundancyVideos.push(redundancyModel)
260
261 this.redundancyDone.add(id)
262 }
263
264 private addTag (row: SQLRow, videoModel: VideoModel) {
265 if (!row['Tags.name']) return
266
267 const key = `${row['Tags.VideoTagModel.videoId']}-${row['Tags.VideoTagModel.tagId']}`
268 if (this.tagsDone.has(key)) return
269
270 const attributes = this.grab(row, this.tables.getTagAttributes(), 'Tags')
271 const tagModel = new TagModel(attributes, this.buildOpts)
272 videoModel.Tags.push(tagModel)
273
274 this.tagsDone.add(key)
275 }
276
277 private addTracker (row: SQLRow, videoModel: VideoModel) {
278 if (!row['Trackers.id']) return
279
280 const key = `${row['Trackers.VideoTrackerModel.videoId']}-${row['Trackers.VideoTrackerModel.trackerId']}`
281 if (this.trackersDone.has(key)) return
282
283 const attributes = this.grab(row, this.tables.getTrackerAttributes(), 'Trackers')
284 const trackerModel = new TrackerModel(attributes, this.buildOpts)
285 videoModel.Trackers.push(trackerModel)
286
287 this.trackersDone.add(key)
288 }
289
290 private setBlacklisted (row: SQLRow, videoModel: VideoModel) {
291 const id = row['VideoBlacklist.id']
292 if (!id || this.blacklistDone.has(id)) return
293
294 const attributes = this.grab(row, this.tables.getBlacklistedAttributes(), 'VideoBlacklist')
295 videoModel.VideoBlacklist = new VideoBlacklistModel(attributes, this.buildOpts)
296
297 this.blacklistDone.add(id)
298 }
299
300 private setScheduleVideoUpdate (row: SQLRow, videoModel: VideoModel) {
301 const id = row['ScheduleVideoUpdate.id']
302 if (!id || this.scheduleVideoUpdateDone.has(id)) return
303
304 const attributes = this.grab(row, this.tables.getScheduleUpdateAttributes(), 'ScheduleVideoUpdate')
305 videoModel.ScheduleVideoUpdate = new ScheduleVideoUpdateModel(attributes, this.buildOpts)
306
307 this.scheduleVideoUpdateDone.add(id)
308 }
309
310 private setLive (row: SQLRow, videoModel: VideoModel) {
311 const id = row['VideoLive.id']
312 if (!id || this.liveDone.has(id)) return
313
314 const attributes = this.grab(row, this.tables.getLiveAttributes(), 'VideoLive')
315 videoModel.VideoLive = new VideoLiveModel(attributes, this.buildOpts)
316
317 this.liveDone.add(id)
318 }
319
320 private grab (row: SQLRow, attributes: string[], prefix: string) {
321 const result: { [ id: string ]: string | number } = {}
322
323 for (const a of attributes) {
324 const key = prefix
325 ? prefix + '.' + a
326 : a
327
328 result[a] = row[key]
329 }
330
331 return result
332 }
333}
diff --git a/server/models/video/sql/shared/video-tables.ts b/server/models/video/sql/shared/video-tables.ts
new file mode 100644
index 000000000..abdd22188
--- /dev/null
+++ b/server/models/video/sql/shared/video-tables.ts
@@ -0,0 +1,263 @@
1
2/**
3 *
4 * Class to build video attributes/join names we want to fetch from the database
5 *
6 */
7export class VideoTables {
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 ]
92 }
93
94 getStreamingPlaylistAttributes () {
95 let playlistKeys = [ 'id', 'playlistUrl', 'type' ]
96
97 if (this.mode === 'get') {
98 playlistKeys = playlistKeys.concat([
99 'p2pMediaLoaderInfohashes',
100 'p2pMediaLoaderPeerVersion',
101 'segmentsSha256Url',
102 'videoId',
103 'createdAt',
104 'updatedAt'
105 ])
106 }
107
108 return playlistKeys
109 }
110
111 getUserHistoryAttributes () {
112 return [ 'id', 'currentTime' ]
113 }
114
115 getPlaylistAttributes () {
116 return [
117 'createdAt',
118 'updatedAt',
119 'url',
120 'position',
121 'startTimestamp',
122 'stopTimestamp',
123 'videoPlaylistId'
124 ]
125 }
126
127 getTagAttributes () {
128 return [ 'id', 'name' ]
129 }
130
131 getVideoTagAttributes () {
132 return [ 'videoId', 'tagId', 'createdAt', 'updatedAt' ]
133 }
134
135 getBlacklistedAttributes () {
136 return [ 'id', 'reason', 'unfederated' ]
137 }
138
139 getScheduleUpdateAttributes () {
140 return [
141 'id',
142 'updateAt',
143 'privacy',
144 'videoId',
145 'createdAt',
146 'updatedAt'
147 ]
148 }
149
150 getLiveAttributes () {
151 return [
152 'id',
153 'streamKey',
154 'saveReplay',
155 'permanentLive',
156 'videoId',
157 'createdAt',
158 'updatedAt'
159 ]
160 }
161
162 getTrackerAttributes () {
163 return [ 'id', 'url' ]
164 }
165
166 getVideoTrackerAttributes () {
167 return [
168 'videoId',
169 'trackerId',
170 'createdAt',
171 'updatedAt'
172 ]
173 }
174
175 getRedundancyAttributes () {
176 return [ 'id', 'fileUrl' ]
177 }
178
179 getActorAttributes () {
180 let attributeKeys = [
181 'id',
182 'preferredUsername',
183 'url',
184 'serverId',
185 'avatarId'
186 ]
187
188 if (this.mode === 'get') {
189 attributeKeys = attributeKeys.concat([
190 'type',
191 'followersCount',
192 'followingCount',
193 'inboxUrl',
194 'outboxUrl',
195 'sharedInboxUrl',
196 'followersUrl',
197 'followingUrl',
198 'remoteCreatedAt',
199 'createdAt',
200 'updatedAt'
201 ])
202 }
203
204 return attributeKeys
205 }
206
207 getAvatarAttributes () {
208 let attributeKeys = [
209 'id',
210 'filename',
211 'type',
212 'fileUrl',
213 'onDisk',
214 'createdAt',
215 'updatedAt'
216 ]
217
218 if (this.mode === 'get') {
219 attributeKeys = attributeKeys.concat([
220 'height',
221 'width',
222 'type'
223 ])
224 }
225
226 return attributeKeys
227 }
228
229 getServerAttributes () {
230 return [ 'id', 'host' ]
231 }
232
233 getVideoAttributes () {
234 return [
235 'id',
236 'uuid',
237 'name',
238 'category',
239 'licence',
240 'language',
241 'privacy',
242 'nsfw',
243 'description',
244 'support',
245 'duration',
246 'views',
247 'likes',
248 'dislikes',
249 'remote',
250 'isLive',
251 'url',
252 'commentsEnabled',
253 'downloadEnabled',
254 'waitTranscoding',
255 'state',
256 'publishedAt',
257 'originallyPublishedAt',
258 'channelId',
259 'createdAt',
260 'updatedAt'
261 ]
262 }
263}