Use raw SQL for video get request
This commit is contained in:
parent
e5dbd5084e
commit
d9bf974f5d
10 changed files with 877 additions and 389 deletions
|
@ -1,15 +0,0 @@
|
|||
import { logger } from '@server/helpers/logger'
|
||||
import { Sequelize, QueryTypes } from 'sequelize'
|
||||
|
||||
export class AbstractVideosQueryBuilder {
|
||||
protected sequelize: Sequelize
|
||||
|
||||
protected query: string
|
||||
protected replacements: any = {}
|
||||
|
||||
protected runQuery (nest?: boolean) {
|
||||
logger.info('Running video query.', { query: this.query, replacements: this.replacements })
|
||||
|
||||
return this.sequelize.query<any>(this.query, { replacements: this.replacements, type: QueryTypes.SELECT, nest })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
|
||||
import { VideoAttributes } from './video-attributes'
|
||||
import { VideoModelBuilder } from './video-model-builder'
|
||||
|
||||
export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder {
|
||||
protected attributes: { [key: string]: string } = {}
|
||||
protected joins: string[] = []
|
||||
|
||||
protected videoAttributes: VideoAttributes
|
||||
protected videoModelBuilder: VideoModelBuilder
|
||||
|
||||
constructor (private readonly mode: 'list' | 'get') {
|
||||
super()
|
||||
|
||||
this.videoAttributes = new VideoAttributes(this.mode)
|
||||
this.videoModelBuilder = new VideoModelBuilder(this.mode, this.videoAttributes)
|
||||
}
|
||||
|
||||
protected buildSelect () {
|
||||
return 'SELECT ' + Object.keys(this.attributes).map(key => {
|
||||
const value = this.attributes[key]
|
||||
if (value) return `${key} AS ${value}`
|
||||
|
||||
return key
|
||||
}).join(', ')
|
||||
}
|
||||
|
||||
protected includeChannels () {
|
||||
this.joins.push(
|
||||
'INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"',
|
||||
'INNER JOIN "actor" AS "VideoChannel->Actor" ON "VideoChannel"."actorId" = "VideoChannel->Actor"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Actor->Avatar" ' +
|
||||
'ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoChannel', this.videoAttributes.getChannelAttributes()),
|
||||
...this.buildActorInclude('VideoChannel->Actor'),
|
||||
...this.buildAvatarInclude('VideoChannel->Actor->Avatar'),
|
||||
...this.buildServerInclude('VideoChannel->Actor->Server')
|
||||
}
|
||||
}
|
||||
|
||||
protected includeAccounts () {
|
||||
this.joins.push(
|
||||
'INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"',
|
||||
'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "server" AS "VideoChannel->Account->Actor->Server" ' +
|
||||
'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Account->Actor->Avatar" ' +
|
||||
'ON "VideoChannel->Account->Actor"."avatarId" = "VideoChannel->Account->Actor->Avatar"."id"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoChannel->Account', this.videoAttributes.getAccountAttributes()),
|
||||
...this.buildActorInclude('VideoChannel->Account->Actor'),
|
||||
...this.buildAvatarInclude('VideoChannel->Account->Actor->Avatar'),
|
||||
...this.buildServerInclude('VideoChannel->Account->Actor->Server')
|
||||
}
|
||||
}
|
||||
|
||||
protected includeThumbnails () {
|
||||
this.joins.push('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('Thumbnails', this.videoAttributes.getThumbnailAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeFiles () {
|
||||
this.joins.push(
|
||||
'LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"',
|
||||
|
||||
'LEFT JOIN "videoStreamingPlaylist" AS "VideoStreamingPlaylists" ON "VideoStreamingPlaylists"."videoId" = "video"."id"',
|
||||
|
||||
'LEFT JOIN "videoFile" AS "VideoStreamingPlaylists->VideoFiles" ' +
|
||||
'ON "VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId" = "VideoStreamingPlaylists"."id"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoFiles', this.videoAttributes.getFileAttributes()),
|
||||
|
||||
...this.buildAttributesObject('VideoStreamingPlaylists', this.videoAttributes.getStreamingPlaylistAttributes()),
|
||||
...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.videoAttributes.getFileAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeUserHistory (userId: number) {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN "userVideoHistory" ' +
|
||||
'ON "video"."id" = "userVideoHistory"."videoId" AND "userVideoHistory"."userId" = :userVideoHistoryId'
|
||||
)
|
||||
|
||||
this.replacements.userVideoHistoryId = userId
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('userVideoHistory', this.videoAttributes.getUserHistoryAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includePlaylist (playlistId: number) {
|
||||
this.joins.push(
|
||||
'INNER JOIN "videoPlaylistElement" as "VideoPlaylistElement" ON "videoPlaylistElement"."videoId" = "video"."id" ' +
|
||||
'AND "VideoPlaylistElement"."videoPlaylistId" = :videoPlaylistId'
|
||||
)
|
||||
|
||||
this.replacements.videoPlaylistId = playlistId
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoPlaylistElement', this.videoAttributes.getPlaylistAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeTags () {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN (' +
|
||||
'"videoTag" AS "Tags->VideoTagModel" INNER JOIN "tag" AS "Tags" ON "Tags"."id" = "Tags->VideoTagModel"."tagId"' +
|
||||
') ' +
|
||||
'ON "video"."id" = "Tags->VideoTagModel"."videoId"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('Tags', this.videoAttributes.getTagAttributes()),
|
||||
...this.buildAttributesObject('Tags->VideoTagModel', this.videoAttributes.getVideoTagAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeBlacklisted () {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN "videoBlacklist" AS "VideoBlacklist" ON "video"."id" = "VideoBlacklist"."videoId"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoBlacklist', this.videoAttributes.getBlacklistedAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeScheduleUpdate () {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN "scheduleVideoUpdate" AS "ScheduleVideoUpdate" ON "video"."id" = "ScheduleVideoUpdate"."videoId"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('ScheduleVideoUpdate', this.videoAttributes.getScheduleUpdateAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeLive () {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN "videoLive" AS "VideoLive" ON "video"."id" = "VideoLive"."videoId"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoLive', this.videoAttributes.getLiveAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeTrackers () {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN (' +
|
||||
'"videoTracker" AS "Trackers->VideoTrackerModel" ' +
|
||||
'INNER JOIN "tracker" AS "Trackers" ON "Trackers"."id" = "Trackers->VideoTrackerModel"."trackerId"' +
|
||||
') ON "video"."id" = "Trackers->VideoTrackerModel"."videoId"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('Trackers', this.videoAttributes.getTrackerAttributes()),
|
||||
...this.buildAttributesObject('Trackers->VideoTrackerModel', this.videoAttributes.getVideoTrackerAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected includeRedundancies () {
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN "videoRedundancy" AS "VideoStreamingPlaylists->RedundancyVideos" ' +
|
||||
'ON "VideoStreamingPlaylists"."id" = "VideoStreamingPlaylists->RedundancyVideos"."videoStreamingPlaylistId"',
|
||||
|
||||
'LEFT OUTER JOIN "videoRedundancy" AS "VideoFiles->RedundancyVideos" ON ' +
|
||||
'"VideoFiles"."id" = "VideoFiles->RedundancyVideos"."videoFileId"'
|
||||
)
|
||||
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.videoAttributes.getRedundancyAttributes()),
|
||||
...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.videoAttributes.getRedundancyAttributes())
|
||||
}
|
||||
}
|
||||
|
||||
protected buildActorInclude (prefixKey: string) {
|
||||
return this.buildAttributesObject(prefixKey, this.videoAttributes.getActorAttributes())
|
||||
}
|
||||
|
||||
protected buildAvatarInclude (prefixKey: string) {
|
||||
return this.buildAttributesObject(prefixKey, this.videoAttributes.getAvatarAttributes())
|
||||
}
|
||||
|
||||
protected buildServerInclude (prefixKey: string) {
|
||||
return this.buildAttributesObject(prefixKey, this.videoAttributes.getServerAttributes())
|
||||
}
|
||||
|
||||
protected buildAttributesObject (prefixKey: string, attributeKeys: string[]) {
|
||||
const result: { [id: string]: string} = {}
|
||||
|
||||
const prefixValue = prefixKey.replace(/->/g, '.')
|
||||
|
||||
for (const attribute of attributeKeys) {
|
||||
result[`"${prefixKey}"."${attribute}"`] = `"${prefixValue}.${attribute}"`
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { QueryTypes, Sequelize, Transaction } from 'sequelize'
|
||||
import { logger } from '@server/helpers/logger'
|
||||
|
||||
export class AbstractVideosQueryBuilder {
|
||||
protected sequelize: Sequelize
|
||||
|
||||
protected query: string
|
||||
protected replacements: any = {}
|
||||
|
||||
protected runQuery (transaction?: Transaction, nest?: boolean) {
|
||||
logger.debug('Running videos query.', { query: this.query, replacements: this.replacements })
|
||||
|
||||
const options = {
|
||||
transaction,
|
||||
replacements: this.replacements,
|
||||
type: QueryTypes.SELECT as QueryTypes.SELECT,
|
||||
nest
|
||||
}
|
||||
|
||||
return this.sequelize.query<any>(this.query, options)
|
||||
}
|
||||
}
|
247
server/models/video/sql/shared/video-attributes.ts
Normal file
247
server/models/video/sql/shared/video-attributes.ts
Normal file
|
@ -0,0 +1,247 @@
|
|||
export class VideoAttributes {
|
||||
|
||||
constructor (readonly mode: 'get' | 'list') {
|
||||
|
||||
}
|
||||
|
||||
getChannelAttributes () {
|
||||
let attributeKeys = [
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
'actorId'
|
||||
]
|
||||
|
||||
if (this.mode === 'get') {
|
||||
attributeKeys = attributeKeys.concat([
|
||||
'support',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
])
|
||||
}
|
||||
|
||||
return attributeKeys
|
||||
}
|
||||
|
||||
getAccountAttributes () {
|
||||
let attributeKeys = [ 'id', 'name', 'actorId' ]
|
||||
|
||||
if (this.mode === 'get') {
|
||||
attributeKeys = attributeKeys.concat([
|
||||
'description',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
])
|
||||
}
|
||||
|
||||
return attributeKeys
|
||||
}
|
||||
|
||||
getThumbnailAttributes () {
|
||||
let attributeKeys = [ 'id', 'type', 'filename' ]
|
||||
|
||||
if (this.mode === 'get') {
|
||||
attributeKeys = attributeKeys.concat([
|
||||
'height',
|
||||
'width',
|
||||
'fileUrl',
|
||||
'automaticallyGenerated',
|
||||
'videoId',
|
||||
'videoPlaylistId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
])
|
||||
}
|
||||
|
||||
return attributeKeys
|
||||
}
|
||||
|
||||
getFileAttributes () {
|
||||
return [
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'resolution',
|
||||
'size',
|
||||
'extname',
|
||||
'filename',
|
||||
'fileUrl',
|
||||
'torrentFilename',
|
||||
'torrentUrl',
|
||||
'infoHash',
|
||||
'fps',
|
||||
'metadataUrl',
|
||||
'videoStreamingPlaylistId',
|
||||
'videoId'
|
||||
]
|
||||
}
|
||||
|
||||
getStreamingPlaylistAttributes () {
|
||||
let playlistKeys = [ 'id', 'playlistUrl', 'type' ]
|
||||
|
||||
if (this.mode === 'get') {
|
||||
playlistKeys = playlistKeys.concat([
|
||||
'p2pMediaLoaderInfohashes',
|
||||
'p2pMediaLoaderPeerVersion',
|
||||
'segmentsSha256Url',
|
||||
'videoId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
])
|
||||
}
|
||||
|
||||
return playlistKeys
|
||||
}
|
||||
|
||||
getUserHistoryAttributes () {
|
||||
return [ 'id', 'currentTime' ]
|
||||
}
|
||||
|
||||
getPlaylistAttributes () {
|
||||
return [
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'url',
|
||||
'position',
|
||||
'startTimestamp',
|
||||
'stopTimestamp',
|
||||
'videoPlaylistId'
|
||||
]
|
||||
}
|
||||
|
||||
getTagAttributes () {
|
||||
return [ 'id', 'name' ]
|
||||
}
|
||||
|
||||
getVideoTagAttributes () {
|
||||
return [ 'videoId', 'tagId', 'createdAt', 'updatedAt' ]
|
||||
}
|
||||
|
||||
getBlacklistedAttributes () {
|
||||
return [ 'id', 'reason', 'unfederated' ]
|
||||
}
|
||||
|
||||
getScheduleUpdateAttributes () {
|
||||
return [
|
||||
'id',
|
||||
'updateAt',
|
||||
'privacy',
|
||||
'videoId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
]
|
||||
}
|
||||
|
||||
getLiveAttributes () {
|
||||
return [
|
||||
'id',
|
||||
'streamKey',
|
||||
'saveReplay',
|
||||
'permanentLive',
|
||||
'videoId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
]
|
||||
}
|
||||
|
||||
getTrackerAttributes () {
|
||||
return [ 'id', 'url' ]
|
||||
}
|
||||
|
||||
getVideoTrackerAttributes () {
|
||||
return [
|
||||
'videoId',
|
||||
'trackerId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
]
|
||||
}
|
||||
|
||||
getRedundancyAttributes () {
|
||||
return [ 'id', 'fileUrl' ]
|
||||
}
|
||||
|
||||
getActorAttributes () {
|
||||
let attributeKeys = [
|
||||
'id',
|
||||
'preferredUsername',
|
||||
'url',
|
||||
'serverId',
|
||||
'avatarId'
|
||||
]
|
||||
|
||||
if (this.mode === 'get') {
|
||||
attributeKeys = attributeKeys.concat([
|
||||
'type',
|
||||
'followersCount',
|
||||
'followingCount',
|
||||
'inboxUrl',
|
||||
'outboxUrl',
|
||||
'sharedInboxUrl',
|
||||
'followersUrl',
|
||||
'followingUrl',
|
||||
'remoteCreatedAt',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
])
|
||||
}
|
||||
|
||||
return attributeKeys
|
||||
}
|
||||
|
||||
getAvatarAttributes () {
|
||||
let attributeKeys = [
|
||||
'id',
|
||||
'filename',
|
||||
'fileUrl',
|
||||
'onDisk',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
]
|
||||
|
||||
if (this.mode === 'get') {
|
||||
attributeKeys = attributeKeys.concat([
|
||||
'height',
|
||||
'width',
|
||||
'type'
|
||||
])
|
||||
}
|
||||
|
||||
return attributeKeys
|
||||
}
|
||||
|
||||
getServerAttributes () {
|
||||
return [ 'id', 'host' ]
|
||||
}
|
||||
|
||||
getVideoAttributes () {
|
||||
return [
|
||||
'id',
|
||||
'uuid',
|
||||
'name',
|
||||
'category',
|
||||
'licence',
|
||||
'language',
|
||||
'privacy',
|
||||
'nsfw',
|
||||
'description',
|
||||
'support',
|
||||
'duration',
|
||||
'views',
|
||||
'likes',
|
||||
'dislikes',
|
||||
'remote',
|
||||
'isLive',
|
||||
'url',
|
||||
'commentsEnabled',
|
||||
'downloadEnabled',
|
||||
'waitTranscoding',
|
||||
'state',
|
||||
'publishedAt',
|
||||
'originallyPublishedAt',
|
||||
'channelId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
]
|
||||
}
|
||||
}
|
268
server/models/video/sql/shared/video-model-builder.ts
Normal file
268
server/models/video/sql/shared/video-model-builder.ts
Normal file
|
@ -0,0 +1,268 @@
|
|||
import { pick } from 'lodash'
|
||||
import { AccountModel } from '@server/models/account/account'
|
||||
import { ActorModel } from '@server/models/actor/actor'
|
||||
import { ActorImageModel } from '@server/models/actor/actor-image'
|
||||
import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
|
||||
import { ServerModel } from '@server/models/server/server'
|
||||
import { TrackerModel } from '@server/models/server/tracker'
|
||||
import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
|
||||
import { ScheduleVideoUpdateModel } from '../../schedule-video-update'
|
||||
import { TagModel } from '../../tag'
|
||||
import { ThumbnailModel } from '../../thumbnail'
|
||||
import { VideoModel } from '../../video'
|
||||
import { VideoBlacklistModel } from '../../video-blacklist'
|
||||
import { VideoChannelModel } from '../../video-channel'
|
||||
import { VideoFileModel } from '../../video-file'
|
||||
import { VideoLiveModel } from '../../video-live'
|
||||
import { VideoStreamingPlaylistModel } from '../../video-streaming-playlist'
|
||||
import { VideoAttributes } from './video-attributes'
|
||||
|
||||
export class VideoModelBuilder {
|
||||
private videosMemo: { [ id: number ]: VideoModel }
|
||||
private videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel }
|
||||
private videoFileMemo: { [ id: number ]: VideoFileModel }
|
||||
|
||||
private thumbnailsDone: Set<number>
|
||||
private historyDone: Set<number>
|
||||
private blacklistDone: Set<number>
|
||||
private liveDone: Set<number>
|
||||
private redundancyDone: Set<number>
|
||||
private scheduleVideoUpdateDone: Set<number>
|
||||
|
||||
private trackersDone: Set<string>
|
||||
private tagsDone: Set<string>
|
||||
|
||||
private videos: VideoModel[]
|
||||
|
||||
private readonly buildOpts = { raw: true, isNewRecord: false }
|
||||
|
||||
constructor (
|
||||
readonly mode: 'get' | 'list',
|
||||
readonly videoAttributes: VideoAttributes
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
buildVideosFromRows (rows: any[]) {
|
||||
this.reinit()
|
||||
|
||||
for (const row of rows) {
|
||||
this.buildVideo(row)
|
||||
|
||||
const videoModel = this.videosMemo[row.id]
|
||||
|
||||
this.setUserHistory(row, videoModel)
|
||||
this.addThumbnail(row, videoModel)
|
||||
this.addWebTorrentFile(row, videoModel)
|
||||
|
||||
this.addStreamingPlaylist(row, videoModel)
|
||||
this.addStreamingPlaylistFile(row)
|
||||
|
||||
if (this.mode === 'get') {
|
||||
this.addTag(row, videoModel)
|
||||
this.addTracker(row, videoModel)
|
||||
this.setBlacklisted(row, videoModel)
|
||||
this.setScheduleVideoUpdate(row, videoModel)
|
||||
this.setLive(row, videoModel)
|
||||
|
||||
if (row.VideoFiles.id) {
|
||||
this.addRedundancy(row.VideoFiles.RedundancyVideos, this.videoFileMemo[row.VideoFiles.id])
|
||||
}
|
||||
|
||||
if (row.VideoStreamingPlaylists.id) {
|
||||
this.addRedundancy(row.VideoStreamingPlaylists.RedundancyVideos, this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.videos
|
||||
}
|
||||
|
||||
private reinit () {
|
||||
this.videosMemo = {}
|
||||
this.videoStreamingPlaylistMemo = {}
|
||||
this.videoFileMemo = {}
|
||||
|
||||
this.thumbnailsDone = new Set<number>()
|
||||
this.historyDone = new Set<number>()
|
||||
this.blacklistDone = new Set<number>()
|
||||
this.liveDone = new Set<number>()
|
||||
this.redundancyDone = new Set<number>()
|
||||
this.scheduleVideoUpdateDone = new Set<number>()
|
||||
|
||||
this.trackersDone = new Set<string>()
|
||||
this.tagsDone = new Set<string>()
|
||||
|
||||
this.videos = []
|
||||
}
|
||||
|
||||
private buildVideo (row: any) {
|
||||
if (this.videosMemo[row.id]) return
|
||||
|
||||
// Build Channel
|
||||
const channel = row.VideoChannel
|
||||
const channelModel = new VideoChannelModel(pick(channel, this.videoAttributes.getChannelAttributes()), this.buildOpts)
|
||||
channelModel.Actor = this.buildActor(channel.Actor)
|
||||
|
||||
const account = row.VideoChannel.Account
|
||||
const accountModel = new AccountModel(pick(account, this.videoAttributes.getAccountAttributes()), this.buildOpts)
|
||||
accountModel.Actor = this.buildActor(account.Actor)
|
||||
|
||||
channelModel.Account = accountModel
|
||||
|
||||
const videoModel = new VideoModel(pick(row, this.videoAttributes.getVideoAttributes()), this.buildOpts)
|
||||
videoModel.VideoChannel = channelModel
|
||||
|
||||
this.videosMemo[row.id] = videoModel
|
||||
|
||||
videoModel.UserVideoHistories = []
|
||||
videoModel.Thumbnails = []
|
||||
videoModel.VideoFiles = []
|
||||
videoModel.VideoStreamingPlaylists = []
|
||||
videoModel.Tags = []
|
||||
videoModel.Trackers = []
|
||||
|
||||
// Keep rows order
|
||||
this.videos.push(videoModel)
|
||||
}
|
||||
|
||||
private buildActor (rowActor: any) {
|
||||
const avatarModel = rowActor.Avatar.id !== null
|
||||
? new ActorImageModel(pick(rowActor.Avatar, this.videoAttributes.getAvatarAttributes()), this.buildOpts)
|
||||
: null
|
||||
|
||||
const serverModel = rowActor.Server.id !== null
|
||||
? new ServerModel(pick(rowActor.Server, this.videoAttributes.getServerAttributes()), this.buildOpts)
|
||||
: null
|
||||
|
||||
const actorModel = new ActorModel(pick(rowActor, this.videoAttributes.getActorAttributes()), this.buildOpts)
|
||||
actorModel.Avatar = avatarModel
|
||||
actorModel.Server = serverModel
|
||||
|
||||
return actorModel
|
||||
}
|
||||
|
||||
private setUserHistory (row: any, videoModel: VideoModel) {
|
||||
if (!row.userVideoHistory?.id || this.historyDone.has(row.userVideoHistory.id)) return
|
||||
|
||||
const attributes = pick(row.userVideoHistory, this.videoAttributes.getUserHistoryAttributes())
|
||||
const historyModel = new UserVideoHistoryModel(attributes, this.buildOpts)
|
||||
videoModel.UserVideoHistories.push(historyModel)
|
||||
|
||||
this.historyDone.add(row.userVideoHistory.id)
|
||||
}
|
||||
|
||||
private addThumbnail (row: any, videoModel: VideoModel) {
|
||||
if (!row.Thumbnails?.id || this.thumbnailsDone.has(row.Thumbnails.id)) return
|
||||
|
||||
const attributes = pick(row.Thumbnails, this.videoAttributes.getThumbnailAttributes())
|
||||
const thumbnailModel = new ThumbnailModel(attributes, this.buildOpts)
|
||||
videoModel.Thumbnails.push(thumbnailModel)
|
||||
|
||||
this.thumbnailsDone.add(row.Thumbnails.id)
|
||||
}
|
||||
|
||||
private addWebTorrentFile (row: any, videoModel: VideoModel) {
|
||||
if (!row.VideoFiles?.id || this.videoFileMemo[row.VideoFiles.id]) return
|
||||
|
||||
const attributes = pick(row.VideoFiles, this.videoAttributes.getFileAttributes())
|
||||
const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
|
||||
videoModel.VideoFiles.push(videoFileModel)
|
||||
|
||||
this.videoFileMemo[row.VideoFiles.id] = videoFileModel
|
||||
}
|
||||
|
||||
private addStreamingPlaylist (row: any, videoModel: VideoModel) {
|
||||
if (!row.VideoStreamingPlaylists?.id || this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id]) return
|
||||
|
||||
const attributes = pick(row.VideoStreamingPlaylists, this.videoAttributes.getStreamingPlaylistAttributes())
|
||||
const streamingPlaylist = new VideoStreamingPlaylistModel(attributes, this.buildOpts)
|
||||
streamingPlaylist.VideoFiles = []
|
||||
|
||||
videoModel.VideoStreamingPlaylists.push(streamingPlaylist)
|
||||
|
||||
this.videoStreamingPlaylistMemo[streamingPlaylist.id] = streamingPlaylist
|
||||
}
|
||||
|
||||
private addStreamingPlaylistFile (row: any) {
|
||||
if (!row.VideoStreamingPlaylists?.VideoFiles?.id || this.videoFileMemo[row.VideoStreamingPlaylists.VideoFiles.id]) return
|
||||
|
||||
const streamingPlaylist = this.videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id]
|
||||
|
||||
const attributes = pick(row.VideoStreamingPlaylists.VideoFiles, this.videoAttributes.getFileAttributes())
|
||||
const videoFileModel = new VideoFileModel(attributes, this.buildOpts)
|
||||
streamingPlaylist.VideoFiles.push(videoFileModel)
|
||||
|
||||
this.videoFileMemo[row.VideoStreamingPlaylists.VideoFiles.id] = videoFileModel
|
||||
}
|
||||
|
||||
private addRedundancy (redundancyRow: any, to: VideoFileModel | VideoStreamingPlaylistModel) {
|
||||
if (!to.RedundancyVideos) to.RedundancyVideos = []
|
||||
|
||||
if (!redundancyRow?.id || this.redundancyDone.has(redundancyRow.id)) return
|
||||
|
||||
const attributes = pick(redundancyRow, this.videoAttributes.getRedundancyAttributes())
|
||||
const redundancyModel = new VideoRedundancyModel(attributes, this.buildOpts)
|
||||
to.RedundancyVideos.push(redundancyModel)
|
||||
|
||||
this.redundancyDone.add(redundancyRow.id)
|
||||
}
|
||||
|
||||
private addTag (row: any, videoModel: VideoModel) {
|
||||
if (!row.Tags?.name) return
|
||||
const association = row.Tags.VideoTagModel
|
||||
|
||||
const key = `${association.videoId}-${association.tagId}`
|
||||
if (this.tagsDone.has(key)) return
|
||||
|
||||
const attributes = pick(row.Tags, this.videoAttributes.getTagAttributes())
|
||||
const tagModel = new TagModel(attributes, this.buildOpts)
|
||||
videoModel.Tags.push(tagModel)
|
||||
|
||||
this.tagsDone.add(key)
|
||||
}
|
||||
|
||||
private addTracker (row: any, videoModel: VideoModel) {
|
||||
if (!row.Trackers?.id) return
|
||||
const association = row.Trackers.VideoTrackerModel
|
||||
|
||||
const key = `${association.videoId}-${association.trackerId}`
|
||||
if (this.trackersDone.has(key)) return
|
||||
|
||||
const attributes = pick(row.Trackers, this.videoAttributes.getTrackerAttributes())
|
||||
const trackerModel = new TrackerModel(attributes, this.buildOpts)
|
||||
videoModel.Trackers.push(trackerModel)
|
||||
|
||||
this.trackersDone.add(key)
|
||||
}
|
||||
|
||||
private setBlacklisted (row: any, videoModel: VideoModel) {
|
||||
if (!row.VideoBlacklist?.id) return
|
||||
if (this.blacklistDone.has(row.VideoBlacklist.id)) return
|
||||
|
||||
const attributes = pick(row.VideoBlacklist, this.videoAttributes.getBlacklistedAttributes())
|
||||
videoModel.VideoBlacklist = new VideoBlacklistModel(attributes, this.buildOpts)
|
||||
|
||||
this.blacklistDone.add(row.VideoBlacklist.id)
|
||||
}
|
||||
|
||||
private setScheduleVideoUpdate (row: any, videoModel: VideoModel) {
|
||||
if (!row.ScheduleVideoUpdate?.id) return
|
||||
if (this.scheduleVideoUpdateDone.has(row.ScheduleVideoUpdate.id)) return
|
||||
|
||||
const attributes = pick(row.ScheduleVideoUpdate, this.videoAttributes.getScheduleUpdateAttributes())
|
||||
videoModel.ScheduleVideoUpdate = new ScheduleVideoUpdateModel(attributes, this.buildOpts)
|
||||
|
||||
this.scheduleVideoUpdateDone.add(row.ScheduleVideoUpdate.id)
|
||||
}
|
||||
|
||||
private setLive (row: any, videoModel: VideoModel) {
|
||||
if (!row.VideoLive?.id) return
|
||||
if (this.liveDone.has(row.VideoLive.id)) return
|
||||
|
||||
const attributes = pick(row.VideoLive, this.videoAttributes.getLiveAttributes())
|
||||
videoModel.VideoLive = new VideoLiveModel(attributes, this.buildOpts)
|
||||
|
||||
this.liveDone.add(row.ScheduleVideoUpdate.id)
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
import { pick } from 'lodash'
|
||||
import { AccountModel } from '@server/models/account/account'
|
||||
import { ActorModel } from '@server/models/actor/actor'
|
||||
import { ActorImageModel } from '@server/models/actor/actor-image'
|
||||
import { ServerModel } from '@server/models/server/server'
|
||||
import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
|
||||
import { ThumbnailModel } from '../thumbnail'
|
||||
import { VideoModel } from '../video'
|
||||
import { VideoChannelModel } from '../video-channel'
|
||||
import { VideoFileModel } from '../video-file'
|
||||
import { VideoStreamingPlaylistModel } from '../video-streaming-playlist'
|
||||
|
||||
function buildVideosFromRows (rows: any[]) {
|
||||
const videosMemo: { [ id: number ]: VideoModel } = {}
|
||||
const videoStreamingPlaylistMemo: { [ id: number ]: VideoStreamingPlaylistModel } = {}
|
||||
|
||||
const thumbnailsDone = new Set<number>()
|
||||
const historyDone = new Set<number>()
|
||||
const videoFilesDone = new Set<number>()
|
||||
|
||||
const videos: VideoModel[] = []
|
||||
|
||||
const avatarKeys = [ 'id', 'filename', 'fileUrl', 'onDisk', 'createdAt', 'updatedAt' ]
|
||||
const actorKeys = [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ]
|
||||
const serverKeys = [ 'id', 'host' ]
|
||||
const videoFileKeys = [
|
||||
'id',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'resolution',
|
||||
'size',
|
||||
'extname',
|
||||
'filename',
|
||||
'fileUrl',
|
||||
'torrentFilename',
|
||||
'torrentUrl',
|
||||
'infoHash',
|
||||
'fps',
|
||||
'videoId',
|
||||
'videoStreamingPlaylistId'
|
||||
]
|
||||
const videoStreamingPlaylistKeys = [ 'id', 'type', 'playlistUrl' ]
|
||||
const videoKeys = [
|
||||
'id',
|
||||
'uuid',
|
||||
'name',
|
||||
'category',
|
||||
'licence',
|
||||
'language',
|
||||
'privacy',
|
||||
'nsfw',
|
||||
'description',
|
||||
'support',
|
||||
'duration',
|
||||
'views',
|
||||
'likes',
|
||||
'dislikes',
|
||||
'remote',
|
||||
'isLive',
|
||||
'url',
|
||||
'commentsEnabled',
|
||||
'downloadEnabled',
|
||||
'waitTranscoding',
|
||||
'state',
|
||||
'publishedAt',
|
||||
'originallyPublishedAt',
|
||||
'channelId',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
]
|
||||
const buildOpts = { raw: true }
|
||||
|
||||
function buildActor (rowActor: any) {
|
||||
const avatarModel = rowActor.Avatar.id !== null
|
||||
? new ActorImageModel(pick(rowActor.Avatar, avatarKeys), buildOpts)
|
||||
: null
|
||||
|
||||
const serverModel = rowActor.Server.id !== null
|
||||
? new ServerModel(pick(rowActor.Server, serverKeys), buildOpts)
|
||||
: null
|
||||
|
||||
const actorModel = new ActorModel(pick(rowActor, actorKeys), buildOpts)
|
||||
actorModel.Avatar = avatarModel
|
||||
actorModel.Server = serverModel
|
||||
|
||||
return actorModel
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
if (!videosMemo[row.id]) {
|
||||
// Build Channel
|
||||
const channel = row.VideoChannel
|
||||
const channelModel = new VideoChannelModel(pick(channel, [ 'id', 'name', 'description', 'actorId' ]), buildOpts)
|
||||
channelModel.Actor = buildActor(channel.Actor)
|
||||
|
||||
const account = row.VideoChannel.Account
|
||||
const accountModel = new AccountModel(pick(account, [ 'id', 'name' ]), buildOpts)
|
||||
accountModel.Actor = buildActor(account.Actor)
|
||||
|
||||
channelModel.Account = accountModel
|
||||
|
||||
const videoModel = new VideoModel(pick(row, videoKeys), buildOpts)
|
||||
videoModel.VideoChannel = channelModel
|
||||
|
||||
videoModel.UserVideoHistories = []
|
||||
videoModel.Thumbnails = []
|
||||
videoModel.VideoFiles = []
|
||||
videoModel.VideoStreamingPlaylists = []
|
||||
|
||||
videosMemo[row.id] = videoModel
|
||||
// Don't take object value to have a sorted array
|
||||
videos.push(videoModel)
|
||||
}
|
||||
|
||||
const videoModel = videosMemo[row.id]
|
||||
|
||||
if (row.userVideoHistory?.id && !historyDone.has(row.userVideoHistory.id)) {
|
||||
const historyModel = new UserVideoHistoryModel(pick(row.userVideoHistory, [ 'id', 'currentTime' ]), buildOpts)
|
||||
videoModel.UserVideoHistories.push(historyModel)
|
||||
|
||||
historyDone.add(row.userVideoHistory.id)
|
||||
}
|
||||
|
||||
if (row.Thumbnails?.id && !thumbnailsDone.has(row.Thumbnails.id)) {
|
||||
const thumbnailModel = new ThumbnailModel(pick(row.Thumbnails, [ 'id', 'type', 'filename' ]), buildOpts)
|
||||
videoModel.Thumbnails.push(thumbnailModel)
|
||||
|
||||
thumbnailsDone.add(row.Thumbnails.id)
|
||||
}
|
||||
|
||||
if (row.VideoFiles?.id && !videoFilesDone.has(row.VideoFiles.id)) {
|
||||
const videoFileModel = new VideoFileModel(pick(row.VideoFiles, videoFileKeys), buildOpts)
|
||||
videoModel.VideoFiles.push(videoFileModel)
|
||||
|
||||
videoFilesDone.add(row.VideoFiles.id)
|
||||
}
|
||||
|
||||
if (row.VideoStreamingPlaylists?.id && !videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id]) {
|
||||
const streamingPlaylist = new VideoStreamingPlaylistModel(pick(row.VideoStreamingPlaylists, videoStreamingPlaylistKeys), buildOpts)
|
||||
streamingPlaylist.VideoFiles = []
|
||||
|
||||
videoModel.VideoStreamingPlaylists.push(streamingPlaylist)
|
||||
|
||||
videoStreamingPlaylistMemo[streamingPlaylist.id] = streamingPlaylist
|
||||
}
|
||||
|
||||
if (row.VideoStreamingPlaylists?.VideoFiles?.id && !videoFilesDone.has(row.VideoStreamingPlaylists.VideoFiles.id)) {
|
||||
const streamingPlaylist = videoStreamingPlaylistMemo[row.VideoStreamingPlaylists.id]
|
||||
|
||||
const videoFileModel = new VideoFileModel(pick(row.VideoStreamingPlaylists.VideoFiles, videoFileKeys), buildOpts)
|
||||
streamingPlaylist.VideoFiles.push(videoFileModel)
|
||||
|
||||
videoFilesDone.add(row.VideoStreamingPlaylists.VideoFiles.id)
|
||||
}
|
||||
}
|
||||
|
||||
return videos
|
||||
}
|
||||
|
||||
export {
|
||||
buildVideosFromRows
|
||||
}
|
86
server/models/video/sql/video-model-get-query-builder.ts
Normal file
86
server/models/video/sql/video-model-get-query-builder.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { Sequelize, Transaction } from 'sequelize'
|
||||
import validator from 'validator'
|
||||
import { AbstractVideosModelQueryBuilder } from './shared/abstract-videos-model-query-builder'
|
||||
|
||||
export type BuildVideoGetQueryOptions = {
|
||||
id: number | string
|
||||
transaction?: Transaction
|
||||
userId?: number
|
||||
forGetAPI?: boolean
|
||||
}
|
||||
|
||||
export class VideosModelGetQueryBuilder extends AbstractVideosModelQueryBuilder {
|
||||
protected attributes: { [key: string]: string }
|
||||
protected joins: string[] = []
|
||||
protected where: string
|
||||
|
||||
constructor (protected readonly sequelize: Sequelize) {
|
||||
super('get')
|
||||
}
|
||||
|
||||
queryVideos (options: BuildVideoGetQueryOptions) {
|
||||
this.buildGetQuery(options)
|
||||
|
||||
return this.runQuery(options.transaction, true).then(rows => {
|
||||
const videos = this.videoModelBuilder.buildVideosFromRows(rows)
|
||||
|
||||
if (videos.length > 1) {
|
||||
throw new Error('Video results is more than ')
|
||||
}
|
||||
|
||||
if (videos.length === 0) return null
|
||||
return videos[0]
|
||||
})
|
||||
}
|
||||
|
||||
private buildGetQuery (options: BuildVideoGetQueryOptions) {
|
||||
this.attributes = {
|
||||
'"video".*': ''
|
||||
}
|
||||
|
||||
this.includeChannels()
|
||||
this.includeAccounts()
|
||||
|
||||
this.includeTags()
|
||||
|
||||
this.includeThumbnails()
|
||||
|
||||
this.includeFiles()
|
||||
|
||||
this.includeBlacklisted()
|
||||
|
||||
this.includeScheduleUpdate()
|
||||
|
||||
this.includeLive()
|
||||
|
||||
if (options.userId) {
|
||||
this.includeUserHistory(options.userId)
|
||||
}
|
||||
|
||||
if (options.forGetAPI === true) {
|
||||
this.includeTrackers()
|
||||
this.includeRedundancies()
|
||||
}
|
||||
|
||||
this.whereId(options.id)
|
||||
|
||||
const select = this.buildSelect()
|
||||
const order = this.buildOrder()
|
||||
|
||||
this.query = `${select} FROM "video" ${this.joins.join(' ')} ${this.where} ${order}`
|
||||
}
|
||||
|
||||
private whereId (id: string | number) {
|
||||
if (validator.isInt('' + id)) {
|
||||
this.where = 'WHERE "video".id = :videoId'
|
||||
} else {
|
||||
this.where = 'WHERE uuid = :videoId'
|
||||
}
|
||||
|
||||
this.replacements.videoId = id
|
||||
}
|
||||
|
||||
private buildOrder () {
|
||||
return 'ORDER BY "Tags"."name" ASC'
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import { exists } from '@server/helpers/custom-validators/misc'
|
|||
import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
|
||||
import { MUserAccountId, MUserId } from '@server/types/models'
|
||||
import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models'
|
||||
import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
|
||||
import { AbstractVideosQueryBuilder } from './shared/abstract-videos-query-builder'
|
||||
|
||||
export type BuildVideosListQueryOptions = {
|
||||
attributes?: string[]
|
||||
|
@ -57,12 +57,13 @@ export type BuildVideosListQueryOptions = {
|
|||
}
|
||||
|
||||
export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
|
||||
private attributes: string[]
|
||||
|
||||
protected replacements: any = {}
|
||||
private readonly and: string[] = []
|
||||
|
||||
private attributes: string[]
|
||||
private joins: string[] = []
|
||||
|
||||
private readonly and: string[] = []
|
||||
|
||||
private readonly cte: string[] = []
|
||||
|
||||
private group = ''
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
|
||||
import { MUserId } from '@server/types/models'
|
||||
import { Sequelize } from 'sequelize'
|
||||
import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
|
||||
import { buildVideosFromRows } from './video-model-builder'
|
||||
import { AbstractVideosModelQueryBuilder } from './shared/abstract-videos-model-query-builder'
|
||||
import { BuildVideosListQueryOptions, VideosIdListQueryBuilder } from './videos-id-list-query-builder'
|
||||
|
||||
export class VideosModelListQueryBuilder extends AbstractVideosQueryBuilder {
|
||||
private attributes: { [key: string]: string }
|
||||
|
||||
private joins: string[] = []
|
||||
export class VideosModelListQueryBuilder extends AbstractVideosModelQueryBuilder {
|
||||
protected attributes: { [key: string]: string }
|
||||
protected joins: string[] = []
|
||||
|
||||
private innerQuery: string
|
||||
private innerSort: string
|
||||
|
||||
constructor (protected readonly sequelize: Sequelize) {
|
||||
super()
|
||||
super('list')
|
||||
}
|
||||
|
||||
queryVideos (options: BuildVideosListQueryOptions) {
|
||||
this.buildInnerQuery(options)
|
||||
this.buildListQueryFromIdsQuery(options)
|
||||
|
||||
return this.runQuery(true).then(rows => buildVideosFromRows(rows))
|
||||
return this.runQuery(undefined, true).then(rows => this.videoModelBuilder.buildVideosFromRows(rows))
|
||||
}
|
||||
|
||||
private buildInnerQuery (options: BuildVideosListQueryOptions) {
|
||||
|
@ -49,7 +45,7 @@ export class VideosModelListQueryBuilder extends AbstractVideosQueryBuilder {
|
|||
}
|
||||
|
||||
if (options.user) {
|
||||
this.includeUserHistory(options.user)
|
||||
this.includeUserHistory(options.user.id)
|
||||
}
|
||||
|
||||
if (options.videoPlaylistId) {
|
||||
|
@ -60,175 +56,4 @@ export class VideosModelListQueryBuilder extends AbstractVideosQueryBuilder {
|
|||
|
||||
this.query = `${select} FROM (${this.innerQuery}) AS "tmp" ${this.joins.join(' ')} ${this.innerSort}`
|
||||
}
|
||||
|
||||
private includeChannels () {
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
'"VideoChannel"."id"': '"VideoChannel.id"',
|
||||
'"VideoChannel"."name"': '"VideoChannel.name"',
|
||||
'"VideoChannel"."description"': '"VideoChannel.description"',
|
||||
'"VideoChannel"."actorId"': '"VideoChannel.actorId"',
|
||||
'"VideoChannel->Actor"."id"': '"VideoChannel.Actor.id"',
|
||||
'"VideoChannel->Actor"."preferredUsername"': '"VideoChannel.Actor.preferredUsername"',
|
||||
'"VideoChannel->Actor"."url"': '"VideoChannel.Actor.url"',
|
||||
'"VideoChannel->Actor"."serverId"': '"VideoChannel.Actor.serverId"',
|
||||
'"VideoChannel->Actor"."avatarId"': '"VideoChannel.Actor.avatarId"',
|
||||
'"VideoChannel->Actor->Server"."id"': '"VideoChannel.Actor.Server.id"',
|
||||
'"VideoChannel->Actor->Server"."host"': '"VideoChannel.Actor.Server.host"',
|
||||
'"VideoChannel->Actor->Avatar"."id"': '"VideoChannel.Actor.Avatar.id"',
|
||||
'"VideoChannel->Actor->Avatar"."filename"': '"VideoChannel.Actor.Avatar.filename"',
|
||||
'"VideoChannel->Actor->Avatar"."fileUrl"': '"VideoChannel.Actor.Avatar.fileUrl"',
|
||||
'"VideoChannel->Actor->Avatar"."onDisk"': '"VideoChannel.Actor.Avatar.onDisk"',
|
||||
'"VideoChannel->Actor->Avatar"."createdAt"': '"VideoChannel.Actor.Avatar.createdAt"',
|
||||
'"VideoChannel->Actor->Avatar"."updatedAt"': '"VideoChannel.Actor.Avatar.updatedAt"'
|
||||
}
|
||||
|
||||
this.joins = this.joins.concat([
|
||||
'INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"',
|
||||
'INNER JOIN "actor" AS "VideoChannel->Actor" ON "VideoChannel"."actorId" = "VideoChannel->Actor"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"',
|
||||
'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Actor->Avatar" ' +
|
||||
'ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"'
|
||||
])
|
||||
}
|
||||
|
||||
private includeAccounts () {
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
'"VideoChannel->Account"."id"': '"VideoChannel.Account.id"',
|
||||
'"VideoChannel->Account"."name"': '"VideoChannel.Account.name"',
|
||||
'"VideoChannel->Account->Actor"."id"': '"VideoChannel.Account.Actor.id"',
|
||||
'"VideoChannel->Account->Actor"."preferredUsername"': '"VideoChannel.Account.Actor.preferredUsername"',
|
||||
'"VideoChannel->Account->Actor"."url"': '"VideoChannel.Account.Actor.url"',
|
||||
'"VideoChannel->Account->Actor"."serverId"': '"VideoChannel.Account.Actor.serverId"',
|
||||
'"VideoChannel->Account->Actor"."avatarId"': '"VideoChannel.Account.Actor.avatarId"',
|
||||
'"VideoChannel->Account->Actor->Server"."id"': '"VideoChannel.Account.Actor.Server.id"',
|
||||
'"VideoChannel->Account->Actor->Server"."host"': '"VideoChannel.Account.Actor.Server.host"',
|
||||
'"VideoChannel->Account->Actor->Avatar"."id"': '"VideoChannel.Account.Actor.Avatar.id"',
|
||||
'"VideoChannel->Account->Actor->Avatar"."filename"': '"VideoChannel.Account.Actor.Avatar.filename"',
|
||||
'"VideoChannel->Account->Actor->Avatar"."fileUrl"': '"VideoChannel.Account.Actor.Avatar.fileUrl"',
|
||||
'"VideoChannel->Account->Actor->Avatar"."onDisk"': '"VideoChannel.Account.Actor.Avatar.onDisk"',
|
||||
'"VideoChannel->Account->Actor->Avatar"."createdAt"': '"VideoChannel.Account.Actor.Avatar.createdAt"',
|
||||
'"VideoChannel->Account->Actor->Avatar"."updatedAt"': '"VideoChannel.Account.Actor.Avatar.updatedAt"'
|
||||
}
|
||||
|
||||
this.joins = this.joins.concat([
|
||||
'INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"',
|
||||
'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "server" AS "VideoChannel->Account->Actor->Server" ' +
|
||||
'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"',
|
||||
|
||||
'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Account->Actor->Avatar" ' +
|
||||
'ON "VideoChannel->Account->Actor"."avatarId" = "VideoChannel->Account->Actor->Avatar"."id"'
|
||||
])
|
||||
}
|
||||
|
||||
private includeThumbnails () {
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
'"Thumbnails"."id"': '"Thumbnails.id"',
|
||||
'"Thumbnails"."type"': '"Thumbnails.type"',
|
||||
'"Thumbnails"."filename"': '"Thumbnails.filename"'
|
||||
}
|
||||
|
||||
this.joins.push('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
|
||||
}
|
||||
|
||||
private includeFiles () {
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
'"VideoFiles"."id"': '"VideoFiles.id"',
|
||||
'"VideoFiles"."createdAt"': '"VideoFiles.createdAt"',
|
||||
'"VideoFiles"."updatedAt"': '"VideoFiles.updatedAt"',
|
||||
'"VideoFiles"."resolution"': '"VideoFiles.resolution"',
|
||||
'"VideoFiles"."size"': '"VideoFiles.size"',
|
||||
'"VideoFiles"."extname"': '"VideoFiles.extname"',
|
||||
'"VideoFiles"."filename"': '"VideoFiles.filename"',
|
||||
'"VideoFiles"."fileUrl"': '"VideoFiles.fileUrl"',
|
||||
'"VideoFiles"."torrentFilename"': '"VideoFiles.torrentFilename"',
|
||||
'"VideoFiles"."torrentUrl"': '"VideoFiles.torrentUrl"',
|
||||
'"VideoFiles"."infoHash"': '"VideoFiles.infoHash"',
|
||||
'"VideoFiles"."fps"': '"VideoFiles.fps"',
|
||||
'"VideoFiles"."videoId"': '"VideoFiles.videoId"',
|
||||
|
||||
'"VideoStreamingPlaylists"."id"': '"VideoStreamingPlaylists.id"',
|
||||
'"VideoStreamingPlaylists"."playlistUrl"': '"VideoStreamingPlaylists.playlistUrl"',
|
||||
'"VideoStreamingPlaylists"."type"': '"VideoStreamingPlaylists.type"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."id"': '"VideoStreamingPlaylists.VideoFiles.id"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."createdAt"': '"VideoStreamingPlaylists.VideoFiles.createdAt"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."updatedAt"': '"VideoStreamingPlaylists.VideoFiles.updatedAt"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."resolution"': '"VideoStreamingPlaylists.VideoFiles.resolution"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."size"': '"VideoStreamingPlaylists.VideoFiles.size"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."extname"': '"VideoStreamingPlaylists.VideoFiles.extname"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."filename"': '"VideoStreamingPlaylists.VideoFiles.filename"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."fileUrl"': '"VideoStreamingPlaylists.VideoFiles.fileUrl"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."torrentFilename"': '"VideoStreamingPlaylists.VideoFiles.torrentFilename"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."torrentUrl"': '"VideoStreamingPlaylists.VideoFiles.torrentUrl"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."infoHash"': '"VideoStreamingPlaylists.VideoFiles.infoHash"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."fps"': '"VideoStreamingPlaylists.VideoFiles.fps"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId"': '"VideoStreamingPlaylists.VideoFiles.videoStreamingPlaylistId"',
|
||||
'"VideoStreamingPlaylists->VideoFiles"."videoId"': '"VideoStreamingPlaylists.VideoFiles.videoId"'
|
||||
}
|
||||
|
||||
this.joins = this.joins.concat([
|
||||
'LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"',
|
||||
|
||||
'LEFT JOIN "videoStreamingPlaylist" AS "VideoStreamingPlaylists" ON "VideoStreamingPlaylists"."videoId" = "video"."id"',
|
||||
|
||||
'LEFT JOIN "videoFile" AS "VideoStreamingPlaylists->VideoFiles" ' +
|
||||
'ON "VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId" = "VideoStreamingPlaylists"."id"'
|
||||
])
|
||||
}
|
||||
|
||||
private includeUserHistory (user: MUserId) {
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
'"userVideoHistory"."id"': '"userVideoHistory.id"',
|
||||
'"userVideoHistory"."currentTime"': '"userVideoHistory.currentTime"'
|
||||
}
|
||||
|
||||
this.joins.push(
|
||||
'LEFT OUTER JOIN "userVideoHistory" ' +
|
||||
'ON "video"."id" = "userVideoHistory"."videoId" AND "userVideoHistory"."userId" = :userVideoHistoryId'
|
||||
)
|
||||
|
||||
this.replacements.userVideoHistoryId = user.id
|
||||
}
|
||||
|
||||
private includePlaylist (playlistId: number) {
|
||||
this.attributes = {
|
||||
...this.attributes,
|
||||
|
||||
'"VideoPlaylistElement"."createdAt"': '"VideoPlaylistElement.createdAt"',
|
||||
'"VideoPlaylistElement"."updatedAt"': '"VideoPlaylistElement.updatedAt"',
|
||||
'"VideoPlaylistElement"."url"': '"VideoPlaylistElement.url"',
|
||||
'"VideoPlaylistElement"."position"': '"VideoPlaylistElement.position"',
|
||||
'"VideoPlaylistElement"."startTimestamp"': '"VideoPlaylistElement.startTimestamp"',
|
||||
'"VideoPlaylistElement"."stopTimestamp"': '"VideoPlaylistElement.stopTimestamp"',
|
||||
'"VideoPlaylistElement"."videoPlaylistId"': '"VideoPlaylistElement.videoPlaylistId"'
|
||||
}
|
||||
|
||||
this.joins.push(
|
||||
'INNER JOIN "videoPlaylistElement" as "VideoPlaylistElement" ON "videoPlaylistElement"."videoId" = "video"."id" ' +
|
||||
'AND "VideoPlaylistElement"."videoPlaylistId" = :videoPlaylistId'
|
||||
)
|
||||
|
||||
this.replacements.videoPlaylistId = playlistId
|
||||
}
|
||||
|
||||
private buildSelect () {
|
||||
return 'SELECT ' + Object.keys(this.attributes).map(key => {
|
||||
const value = this.attributes[key]
|
||||
if (value) return `${key} AS ${value}`
|
||||
|
||||
return key
|
||||
}).join(', ')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ import {
|
|||
videoModelToFormattedJSON
|
||||
} from './formatter/video-format-utils'
|
||||
import { ScheduleVideoUpdateModel } from './schedule-video-update'
|
||||
import { VideosModelGetQueryBuilder } from './sql/video-model-get-query-builder'
|
||||
import { BuildVideosListQueryOptions, VideosIdListQueryBuilder } from './sql/videos-id-list-query-builder'
|
||||
import { VideosModelListQueryBuilder } from './sql/videos-model-list-query-builder'
|
||||
import { TagModel } from './tag'
|
||||
|
@ -1475,33 +1476,9 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
|||
userId?: number
|
||||
}): Promise<MVideoDetails> {
|
||||
const { id, t, userId } = parameters
|
||||
const where = buildWhereIdOrUUID(id)
|
||||
const queryBuilder = new VideosModelGetQueryBuilder(VideoModel.sequelize)
|
||||
|
||||
const options = {
|
||||
order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings
|
||||
where,
|
||||
transaction: t
|
||||
}
|
||||
|
||||
const scopes: (string | ScopeOptions)[] = [
|
||||
ScopeNames.WITH_TAGS,
|
||||
ScopeNames.WITH_BLACKLISTED,
|
||||
ScopeNames.WITH_ACCOUNT_DETAILS,
|
||||
ScopeNames.WITH_SCHEDULED_UPDATE,
|
||||
ScopeNames.WITH_THUMBNAILS,
|
||||
ScopeNames.WITH_LIVE,
|
||||
ScopeNames.WITH_TRACKERS,
|
||||
{ method: [ ScopeNames.WITH_WEBTORRENT_FILES, true ] },
|
||||
{ method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] }
|
||||
]
|
||||
|
||||
if (userId) {
|
||||
scopes.push({ method: [ ScopeNames.WITH_USER_HISTORY, userId ] })
|
||||
}
|
||||
|
||||
return VideoModel
|
||||
.scope(scopes)
|
||||
.findOne(options)
|
||||
return queryBuilder.queryVideos({ id, transaction: t, forGetAPI: true, userId })
|
||||
}
|
||||
|
||||
static async getStats () {
|
||||
|
|
Loading…
Reference in a new issue