2 import { AccountModel } from '@server/models/account/account'
3 import { ActorModel } from '@server/models/actor/actor'
4 import { ActorImageModel } from '@server/models/actor/actor-image'
5 import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
6 import { ServerModel } from '@server/models/server/server'
7 import { TrackerModel } from '@server/models/server/tracker'
8 import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
9 import { ScheduleVideoUpdateModel } from '../../schedule-video-update'
10 import { TagModel } from '../../tag'
11 import { ThumbnailModel } from '../../thumbnail'
12 import { VideoModel } from '../../video'
13 import { VideoBlacklistModel } from '../../video-blacklist'
14 import { VideoChannelModel } from '../../video-channel'
15 import { VideoFileModel } from '../../video-file'
16 import { VideoLiveModel } from '../../video-live'
17 import { VideoStreamingPlaylistModel } from '../../video-streaming-playlist'
18 import { VideoTables } from './video-tables'
20 type SQLRow = { [id: string]: string | number }
24 * Build video models from SQL rows
28 export class VideoModelBuilder {
29 private videosMemo: { [ id: number ]: VideoModel }
30 private videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel }
31 private videoFileMemo: { [ id: number ]: VideoFileModel }
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>
40 private trackersDone: Set<string>
41 private tagsDone: Set<string>
43 private videos: VideoModel[]
45 private readonly buildOpts = { raw: true, isNewRecord: false }
48 readonly mode: 'get' | 'list',
49 readonly tables: VideoTables
54 buildVideosFromRows (rows: SQLRow[], rowsWebTorrentFiles?: SQLRow[], rowsStreamingPlaylist?: SQLRow[]) {
57 for (const row of rows) {
58 this.buildVideoAndAccount(row)
60 const videoModel = this.videosMemo[row.id]
62 this.setUserHistory(row, videoModel)
63 this.addThumbnail(row, videoModel)
65 if (!rowsWebTorrentFiles) {
66 this.addWebTorrentFile(row, videoModel)
69 if (!rowsStreamingPlaylist) {
70 this.addStreamingPlaylist(row, videoModel)
71 this.addStreamingPlaylistFile(row)
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)
83 this.grabSeparateWebTorrentFiles(rowsWebTorrentFiles)
84 this.grabSeparateStreamingPlaylistFiles(rowsStreamingPlaylist)
91 this.videoStreamingPlaylistMemo = {}
92 this.videoFileMemo = {}
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>()
101 this.trackersDone = new Set<string>()
102 this.tagsDone = new Set<string>()
107 private grabSeparateWebTorrentFiles (rowsWebTorrentFiles?: SQLRow[]) {
108 if (!rowsWebTorrentFiles) return
110 for (const row of rowsWebTorrentFiles) {
111 const id = row['VideoFiles.id']
114 const videoModel = this.videosMemo[row.id]
115 this.addWebTorrentFile(row, videoModel)
116 this.addRedundancy(row, 'VideoFiles', this.videoFileMemo[id])
120 private grabSeparateStreamingPlaylistFiles (rowsStreamingPlaylist?: SQLRow[]) {
121 if (!rowsStreamingPlaylist) return
123 for (const row of rowsStreamingPlaylist || []) {
124 const id = row['VideoStreamingPlaylists.id']
127 const videoModel = this.videosMemo[row.id]
129 this.addStreamingPlaylist(row, videoModel)
130 this.addStreamingPlaylistFile(row)
131 this.addRedundancy(row, 'VideoStreamingPlaylists', this.videoStreamingPlaylistMemo[id])
135 private buildVideoAndAccount (row: SQLRow) {
136 if (this.videosMemo[row.id]) return
138 const videoModel = new VideoModel(this.grab(row, this.tables.getVideoAttributes(), ''), this.buildOpts)
140 videoModel.UserVideoHistories = []
141 videoModel.Thumbnails = []
142 videoModel.VideoFiles = []
143 videoModel.VideoStreamingPlaylists = []
145 videoModel.Trackers = []
147 this.buildAccount(row, videoModel)
149 this.videosMemo[row.id] = videoModel
152 this.videos.push(videoModel)
155 private buildAccount (row: SQLRow, videoModel: VideoModel) {
156 const id = row['VideoChannel.Account.id']
159 const channelModel = new VideoChannelModel(this.grab(row, this.tables.getChannelAttributes(), 'VideoChannel'), this.buildOpts)
160 channelModel.Actor = this.buildActor(row, 'VideoChannel')
162 const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
163 accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
165 channelModel.Account = accountModel
167 videoModel.VideoChannel = channelModel
170 private buildActor (row: SQLRow, prefix: string) {
171 const actorPrefix = `${prefix}.Actor`
172 const avatarPrefix = `${actorPrefix}.Avatar`
173 const serverPrefix = `${actorPrefix}.Server`
175 const avatarModel = row[`${avatarPrefix}.id`] !== null
176 ? new ActorImageModel(this.grab(row, this.tables.getAvatarAttributes(), avatarPrefix), this.buildOpts)
179 const serverModel = row[`${serverPrefix}.id`] !== null
180 ? new ServerModel(this.grab(row, this.tables.getServerAttributes(), serverPrefix), this.buildOpts)
183 const actorModel = new ActorModel(this.grab(row, this.tables.getActorAttributes(), actorPrefix), this.buildOpts)
184 actorModel.Avatar = avatarModel
185 actorModel.Server = serverModel
190 private setUserHistory (row: SQLRow, videoModel: VideoModel) {
191 const id = row['userVideoHistory.id']
192 if (!id || this.historyDone.has(id)) return
194 const attributes = this.grab(row, this.tables.getUserHistoryAttributes(), 'userVideoHistory')
195 const historyModel = new UserVideoHistoryModel(attributes, this.buildOpts)
196 videoModel.UserVideoHistories.push(historyModel)
198 this.historyDone.add(id)
201 private addThumbnail (row: SQLRow, videoModel: VideoModel) {
202 const id = row['Thumbnails.id']
203 if (!id || this.thumbnailsDone.has(id)) return
205 const attributes = this.grab(row, this.tables.getThumbnailAttributes(), 'Thumbnails')
206 const thumbnailModel = new ThumbnailModel(attributes, this.buildOpts)
207 videoModel.Thumbnails.push(thumbnailModel)
209 this.thumbnailsDone.add(id)
212 private addWebTorrentFile (row: SQLRow, videoModel: VideoModel) {
213 const id = row['VideoFiles.id']
214 if (!id || this.videoFileMemo[id]) return
216 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoFiles')
217 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
218 videoModel.VideoFiles.push(videoFileModel)
220 this.videoFileMemo[id] = videoFileModel
223 private addStreamingPlaylist (row: SQLRow, videoModel: VideoModel) {
224 const id = row['VideoStreamingPlaylists.id']
225 if (!id || this.videoStreamingPlaylistMemo[id]) return
227 const attributes = this.grab(row, this.tables.getStreamingPlaylistAttributes(), 'VideoStreamingPlaylists')
228 const streamingPlaylist = new VideoStreamingPlaylistModel(attributes, this.buildOpts)
229 streamingPlaylist.VideoFiles = []
231 videoModel.VideoStreamingPlaylists.push(streamingPlaylist)
233 this.videoStreamingPlaylistMemo[id] = streamingPlaylist
236 private addStreamingPlaylistFile (row: SQLRow) {
237 const id = row['VideoStreamingPlaylists.VideoFiles.id']
238 if (!id || this.videoFileMemo[id]) return
240 const streamingPlaylist = this.videoStreamingPlaylistMemo[row['VideoStreamingPlaylists.id']]
242 const attributes = this.grab(row, this.tables.getFileAttributes(), 'VideoStreamingPlaylists.VideoFiles')
243 const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
244 streamingPlaylist.VideoFiles.push(videoFileModel)
246 this.videoFileMemo[id] = videoFileModel
249 private addRedundancy (row: SQLRow, prefix: string, to: VideoFileModel | VideoStreamingPlaylistModel) {
250 if (!to.RedundancyVideos) to.RedundancyVideos = []
252 const redundancyPrefix = `${prefix}.RedundancyVideos`
253 const id = row[`${redundancyPrefix}.id`]
255 if (!id || this.redundancyDone.has(id)) return
257 const attributes = this.grab(row, this.tables.getRedundancyAttributes(), redundancyPrefix)
258 const redundancyModel = new VideoRedundancyModel(attributes, this.buildOpts)
259 to.RedundancyVideos.push(redundancyModel)
261 this.redundancyDone.add(id)
264 private addTag (row: SQLRow, videoModel: VideoModel) {
265 if (!row['Tags.name']) return
267 const key = `${row['Tags.VideoTagModel.videoId']}-${row['Tags.VideoTagModel.tagId']}`
268 if (this.tagsDone.has(key)) return
270 const attributes = this.grab(row, this.tables.getTagAttributes(), 'Tags')
271 const tagModel = new TagModel(attributes, this.buildOpts)
272 videoModel.Tags.push(tagModel)
274 this.tagsDone.add(key)
277 private addTracker (row: SQLRow, videoModel: VideoModel) {
278 if (!row['Trackers.id']) return
280 const key = `${row['Trackers.VideoTrackerModel.videoId']}-${row['Trackers.VideoTrackerModel.trackerId']}`
281 if (this.trackersDone.has(key)) return
283 const attributes = this.grab(row, this.tables.getTrackerAttributes(), 'Trackers')
284 const trackerModel = new TrackerModel(attributes, this.buildOpts)
285 videoModel.Trackers.push(trackerModel)
287 this.trackersDone.add(key)
290 private setBlacklisted (row: SQLRow, videoModel: VideoModel) {
291 const id = row['VideoBlacklist.id']
292 if (!id || this.blacklistDone.has(id)) return
294 const attributes = this.grab(row, this.tables.getBlacklistedAttributes(), 'VideoBlacklist')
295 videoModel.VideoBlacklist = new VideoBlacklistModel(attributes, this.buildOpts)
297 this.blacklistDone.add(id)
300 private setScheduleVideoUpdate (row: SQLRow, videoModel: VideoModel) {
301 const id = row['ScheduleVideoUpdate.id']
302 if (!id || this.scheduleVideoUpdateDone.has(id)) return
304 const attributes = this.grab(row, this.tables.getScheduleUpdateAttributes(), 'ScheduleVideoUpdate')
305 videoModel.ScheduleVideoUpdate = new ScheduleVideoUpdateModel(attributes, this.buildOpts)
307 this.scheduleVideoUpdateDone.add(id)
310 private setLive (row: SQLRow, videoModel: VideoModel) {
311 const id = row['VideoLive.id']
312 if (!id || this.liveDone.has(id)) return
314 const attributes = this.grab(row, this.tables.getLiveAttributes(), 'VideoLive')
315 videoModel.VideoLive = new VideoLiveModel(attributes, this.buildOpts)
317 this.liveDone.add(id)
320 private grab (row: SQLRow, attributes: string[], prefix: string) {
321 const result: { [ id: string ]: string | number } = {}
323 for (const a of attributes) {