aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/sql
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/sql')
-rw-r--r--server/models/video/sql/shared/abstract-videos-model-query-builder.ts28
-rw-r--r--server/models/video/sql/shared/video-model-builder.ts72
-rw-r--r--server/models/video/sql/shared/video-tables.ts4
-rw-r--r--server/models/video/sql/video-model-get-query-builder.ts6
-rw-r--r--server/models/video/sql/videos-id-list-query-builder.ts60
-rw-r--r--server/models/video/sql/videos-model-list-query-builder.ts11
6 files changed, 149 insertions, 32 deletions
diff --git a/server/models/video/sql/shared/abstract-videos-model-query-builder.ts b/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
index 0d7e64574..29827db2a 100644
--- a/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
+++ b/server/models/video/sql/shared/abstract-videos-model-query-builder.ts
@@ -1,3 +1,5 @@
1import { createSafeIn } from '@server/models/utils'
2import { MUserAccountId } from '@server/types/models'
1import validator from 'validator' 3import validator from 'validator'
2import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder' 4import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
3import { VideoTables } from './video-tables' 5import { VideoTables } from './video-tables'
@@ -188,6 +190,32 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
188 } 190 }
189 } 191 }
190 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
191 protected includeScheduleUpdate () { 219 protected includeScheduleUpdate () {
192 this.addJoin( 220 this.addJoin(
193 'LEFT OUTER JOIN "scheduleVideoUpdate" AS "ScheduleVideoUpdate" ON "video"."id" = "ScheduleVideoUpdate"."videoId"' 221 'LEFT OUTER JOIN "scheduleVideoUpdate" AS "ScheduleVideoUpdate" ON "video"."id" = "ScheduleVideoUpdate"."videoId"'
diff --git a/server/models/video/sql/shared/video-model-builder.ts b/server/models/video/sql/shared/video-model-builder.ts
index 33a0181e9..0eac95661 100644
--- a/server/models/video/sql/shared/video-model-builder.ts
+++ b/server/models/video/sql/shared/video-model-builder.ts
@@ -1,11 +1,14 @@
1 1
2import { AccountModel } from '@server/models/account/account' 2import { AccountModel } from '@server/models/account/account'
3import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
3import { ActorModel } from '@server/models/actor/actor' 4import { ActorModel } from '@server/models/actor/actor'
4import { ActorImageModel } from '@server/models/actor/actor-image' 5import { ActorImageModel } from '@server/models/actor/actor-image'
5import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy' 6import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
6import { ServerModel } from '@server/models/server/server' 7import { ServerModel } from '@server/models/server/server'
8import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
7import { TrackerModel } from '@server/models/server/tracker' 9import { TrackerModel } from '@server/models/server/tracker'
8import { UserVideoHistoryModel } from '@server/models/user/user-video-history' 10import { UserVideoHistoryModel } from '@server/models/user/user-video-history'
11import { VideoInclude } from '@shared/models'
9import { ScheduleVideoUpdateModel } from '../../schedule-video-update' 12import { ScheduleVideoUpdateModel } from '../../schedule-video-update'
10import { TagModel } from '../../tag' 13import { TagModel } from '../../tag'
11import { ThumbnailModel } from '../../thumbnail' 14import { ThumbnailModel } from '../../thumbnail'
@@ -33,6 +36,8 @@ export class VideoModelBuilder {
33 private thumbnailsDone: Set<any> 36 private thumbnailsDone: Set<any>
34 private historyDone: Set<any> 37 private historyDone: Set<any>
35 private blacklistDone: Set<any> 38 private blacklistDone: Set<any>
39 private accountBlocklistDone: Set<any>
40 private serverBlocklistDone: Set<any>
36 private liveDone: Set<any> 41 private liveDone: Set<any>
37 private redundancyDone: Set<any> 42 private redundancyDone: Set<any>
38 private scheduleVideoUpdateDone: Set<any> 43 private scheduleVideoUpdateDone: Set<any>
@@ -51,7 +56,14 @@ export class VideoModelBuilder {
51 56
52 } 57 }
53 58
54 buildVideosFromRows (rows: SQLRow[], rowsWebTorrentFiles?: SQLRow[], rowsStreamingPlaylist?: SQLRow[]) { 59 buildVideosFromRows (options: {
60 rows: SQLRow[]
61 include?: VideoInclude
62 rowsWebTorrentFiles?: SQLRow[]
63 rowsStreamingPlaylist?: SQLRow[]
64 }) {
65 const { rows, rowsWebTorrentFiles, rowsStreamingPlaylist, include } = options
66
55 this.reinit() 67 this.reinit()
56 68
57 for (const row of rows) { 69 for (const row of rows) {
@@ -77,6 +89,15 @@ export class VideoModelBuilder {
77 this.setBlacklisted(row, videoModel) 89 this.setBlacklisted(row, videoModel)
78 this.setScheduleVideoUpdate(row, videoModel) 90 this.setScheduleVideoUpdate(row, videoModel)
79 this.setLive(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 }
80 } 101 }
81 } 102 }
82 103
@@ -91,15 +112,18 @@ export class VideoModelBuilder {
91 this.videoStreamingPlaylistMemo = {} 112 this.videoStreamingPlaylistMemo = {}
92 this.videoFileMemo = {} 113 this.videoFileMemo = {}
93 114
94 this.thumbnailsDone = new Set<number>() 115 this.thumbnailsDone = new Set()
95 this.historyDone = new Set<number>() 116 this.historyDone = new Set()
96 this.blacklistDone = new Set<number>() 117 this.blacklistDone = new Set()
97 this.liveDone = new Set<number>() 118 this.liveDone = new Set()
98 this.redundancyDone = new Set<number>() 119 this.redundancyDone = new Set()
99 this.scheduleVideoUpdateDone = new Set<number>() 120 this.scheduleVideoUpdateDone = new Set()
121
122 this.accountBlocklistDone = new Set()
123 this.serverBlocklistDone = new Set()
100 124
101 this.trackersDone = new Set<string>() 125 this.trackersDone = new Set()
102 this.tagsDone = new Set<string>() 126 this.tagsDone = new Set()
103 127
104 this.videos = [] 128 this.videos = []
105 } 129 }
@@ -162,6 +186,8 @@ export class VideoModelBuilder {
162 const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts) 186 const accountModel = new AccountModel(this.grab(row, this.tables.getAccountAttributes(), 'VideoChannel.Account'), this.buildOpts)
163 accountModel.Actor = this.buildActor(row, 'VideoChannel.Account') 187 accountModel.Actor = this.buildActor(row, 'VideoChannel.Account')
164 188
189 accountModel.BlockedBy = []
190
165 channelModel.Account = accountModel 191 channelModel.Account = accountModel
166 192
167 videoModel.VideoChannel = channelModel 193 videoModel.VideoChannel = channelModel
@@ -180,6 +206,8 @@ export class VideoModelBuilder {
180 ? new ServerModel(this.grab(row, this.tables.getServerAttributes(), serverPrefix), this.buildOpts) 206 ? new ServerModel(this.grab(row, this.tables.getServerAttributes(), serverPrefix), this.buildOpts)
181 : null 207 : null
182 208
209 if (serverModel) serverModel.BlockedBy = []
210
183 const actorModel = new ActorModel(this.grab(row, this.tables.getActorAttributes(), actorPrefix), this.buildOpts) 211 const actorModel = new ActorModel(this.grab(row, this.tables.getActorAttributes(), actorPrefix), this.buildOpts)
184 actorModel.Avatar = avatarModel 212 actorModel.Avatar = avatarModel
185 actorModel.Server = serverModel 213 actorModel.Server = serverModel
@@ -297,6 +325,32 @@ export class VideoModelBuilder {
297 this.blacklistDone.add(id) 325 this.blacklistDone.add(id)
298 } 326 }
299 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
300 private setScheduleVideoUpdate (row: SQLRow, videoModel: VideoModel) { 354 private setScheduleVideoUpdate (row: SQLRow, videoModel: VideoModel) {
301 const id = row['ScheduleVideoUpdate.id'] 355 const id = row['ScheduleVideoUpdate.id']
302 if (!id || this.scheduleVideoUpdateDone.has(id)) return 356 if (!id || this.scheduleVideoUpdateDone.has(id)) return
diff --git a/server/models/video/sql/shared/video-tables.ts b/server/models/video/sql/shared/video-tables.ts
index 75823864d..042b9d5da 100644
--- a/server/models/video/sql/shared/video-tables.ts
+++ b/server/models/video/sql/shared/video-tables.ts
@@ -139,6 +139,10 @@ export class VideoTables {
139 return [ 'id', 'reason', 'unfederated' ] 139 return [ 'id', 'reason', 'unfederated' ]
140 } 140 }
141 141
142 getBlocklistAttributes () {
143 return [ 'id' ]
144 }
145
142 getScheduleUpdateAttributes () { 146 getScheduleUpdateAttributes () {
143 return [ 147 return [
144 'id', 148 'id',
diff --git a/server/models/video/sql/video-model-get-query-builder.ts b/server/models/video/sql/video-model-get-query-builder.ts
index f234e8778..d18ddae67 100644
--- a/server/models/video/sql/video-model-get-query-builder.ts
+++ b/server/models/video/sql/video-model-get-query-builder.ts
@@ -62,7 +62,11 @@ export class VideosModelGetQueryBuilder {
62 : Promise.resolve(undefined) 62 : Promise.resolve(undefined)
63 ]) 63 ])
64 64
65 const videos = this.videoModelBuilder.buildVideosFromRows(videoRows, webtorrentFilesRows, streamingPlaylistFilesRows) 65 const videos = this.videoModelBuilder.buildVideosFromRows({
66 rows: videoRows,
67 rowsWebTorrentFiles: webtorrentFilesRows,
68 rowsStreamingPlaylist: streamingPlaylistFilesRows
69 })
66 70
67 if (videos.length > 1) { 71 if (videos.length > 1) {
68 throw new Error('Video results is more than ') 72 throw new Error('Video results is more than ')
diff --git a/server/models/video/sql/videos-id-list-query-builder.ts b/server/models/video/sql/videos-id-list-query-builder.ts
index 7625c003d..3eb547e75 100644
--- a/server/models/video/sql/videos-id-list-query-builder.ts
+++ b/server/models/video/sql/videos-id-list-query-builder.ts
@@ -4,7 +4,7 @@ import { exists } from '@server/helpers/custom-validators/misc'
4import { WEBSERVER } from '@server/initializers/constants' 4import { WEBSERVER } from '@server/initializers/constants'
5import { buildDirectionAndField, createSafeIn } from '@server/models/utils' 5import { buildDirectionAndField, createSafeIn } from '@server/models/utils'
6import { MUserAccountId, MUserId } from '@server/types/models' 6import { MUserAccountId, MUserId } from '@server/types/models'
7import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' 7import { VideoInclude, VideoPrivacy, VideoState } from '@shared/models'
8import { AbstractVideosQueryBuilder } from './shared/abstract-videos-query-builder' 8import { AbstractVideosQueryBuilder } from './shared/abstract-videos-query-builder'
9 9
10/** 10/**
@@ -13,21 +13,27 @@ import { AbstractVideosQueryBuilder } from './shared/abstract-videos-query-build
13 * 13 *
14 */ 14 */
15 15
16export type DisplayOnlyForFollowerOptions = {
17 actorId: number
18 orLocalVideos: boolean
19}
20
16export type BuildVideosListQueryOptions = { 21export type BuildVideosListQueryOptions = {
17 attributes?: string[] 22 attributes?: string[]
18 23
19 serverAccountId: number 24 serverAccountIdForBlock: number
20 followerActorId: number 25
21 includeLocalVideos: boolean 26 displayOnlyForFollower: DisplayOnlyForFollowerOptions
22 27
23 count: number 28 count: number
24 start: number 29 start: number
25 sort: string 30 sort: string
26 31
27 nsfw?: boolean 32 nsfw?: boolean
28 filter?: VideoFilter
29 host?: string 33 host?: string
30 isLive?: boolean 34 isLive?: boolean
35 isLocal?: boolean
36 include?: VideoInclude
31 37
32 categoryOneOf?: number[] 38 categoryOneOf?: number[]
33 licenceOneOf?: number[] 39 licenceOneOf?: number[]
@@ -101,6 +107,7 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
101 107
102 getIdsListQueryAndSort (options: BuildVideosListQueryOptions) { 108 getIdsListQueryAndSort (options: BuildVideosListQueryOptions) {
103 this.buildIdsListQuery(options) 109 this.buildIdsListQuery(options)
110
104 return { query: this.query, sort: this.sort, replacements: this.replacements } 111 return { query: this.query, sort: this.sort, replacements: this.replacements }
105 } 112 }
106 113
@@ -116,23 +123,30 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
116 'INNER JOIN "actor" "accountActor" ON "account"."actorId" = "accountActor"."id"' 123 'INNER JOIN "actor" "accountActor" ON "account"."actorId" = "accountActor"."id"'
117 ]) 124 ])
118 125
119 this.whereNotBlacklisted() 126 if (!(options.include & VideoInclude.BLACKLISTED)) {
127 this.whereNotBlacklisted()
128 }
120 129
121 if (options.serverAccountId) { 130 if (options.serverAccountIdForBlock && !(options.include & VideoInclude.BLOCKED_OWNER)) {
122 this.whereNotBlocked(options.serverAccountId, options.user) 131 this.whereNotBlocked(options.serverAccountIdForBlock, options.user)
123 } 132 }
124 133
125 // Only list public/published videos 134 // Only list published videos
126 if (!options.filter || (options.filter !== 'all-local' && options.filter !== 'all')) { 135 if (!(options.include & VideoInclude.NOT_PUBLISHED_STATE)) {
127 this.whereStateAndPrivacyAvailable(options.user) 136 this.whereStateAvailable()
137 }
138
139 // Only list videos with the appropriate priavcy
140 if (!(options.include & VideoInclude.HIDDEN_PRIVACY)) {
141 this.wherePrivacyAvailable(options.user)
128 } 142 }
129 143
130 if (options.videoPlaylistId) { 144 if (options.videoPlaylistId) {
131 this.joinPlaylist(options.videoPlaylistId) 145 this.joinPlaylist(options.videoPlaylistId)
132 } 146 }
133 147
134 if (options.filter && (options.filter === 'local' || options.filter === 'all-local')) { 148 if (exists(options.isLocal)) {
135 this.whereOnlyLocal() 149 this.whereLocal(options.isLocal)
136 } 150 }
137 151
138 if (options.host) { 152 if (options.host) {
@@ -147,8 +161,8 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
147 this.whereChannelId(options.videoChannelId) 161 this.whereChannelId(options.videoChannelId)
148 } 162 }
149 163
150 if (options.followerActorId) { 164 if (options.displayOnlyForFollower) {
151 this.whereFollowerActorId(options.followerActorId, options.includeLocalVideos) 165 this.whereFollowerActorId(options.displayOnlyForFollower)
152 } 166 }
153 167
154 if (options.withFiles === true) { 168 if (options.withFiles === true) {
@@ -282,12 +296,14 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
282 this.replacements.videoPlaylistId = playlistId 296 this.replacements.videoPlaylistId = playlistId
283 } 297 }
284 298
285 private whereStateAndPrivacyAvailable (user?: MUserAccountId) { 299 private whereStateAvailable () {
286 this.and.push( 300 this.and.push(
287 `("video"."state" = ${VideoState.PUBLISHED} OR ` + 301 `("video"."state" = ${VideoState.PUBLISHED} OR ` +
288 `("video"."state" = ${VideoState.TO_TRANSCODE} AND "video"."waitTranscoding" IS false))` 302 `("video"."state" = ${VideoState.TO_TRANSCODE} AND "video"."waitTranscoding" IS false))`
289 ) 303 )
304 }
290 305
306 private wherePrivacyAvailable (user?: MUserAccountId) {
291 if (user) { 307 if (user) {
292 this.and.push( 308 this.and.push(
293 `("video"."privacy" = ${VideoPrivacy.PUBLIC} OR "video"."privacy" = ${VideoPrivacy.INTERNAL})` 309 `("video"."privacy" = ${VideoPrivacy.PUBLIC} OR "video"."privacy" = ${VideoPrivacy.INTERNAL})`
@@ -299,8 +315,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
299 } 315 }
300 } 316 }
301 317
302 private whereOnlyLocal () { 318 private whereLocal (isLocal: boolean) {
303 this.and.push('"video"."remote" IS FALSE') 319 const isRemote = isLocal ? 'FALSE' : 'TRUE'
320
321 this.and.push('"video"."remote" IS ' + isRemote)
304 } 322 }
305 323
306 private whereHost (host: string) { 324 private whereHost (host: string) {
@@ -326,7 +344,7 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
326 this.replacements.videoChannelId = channelId 344 this.replacements.videoChannelId = channelId
327 } 345 }
328 346
329 private whereFollowerActorId (followerActorId: number, includeLocalVideos: boolean) { 347 private whereFollowerActorId (options: { actorId: number, orLocalVideos: boolean }) {
330 let query = 348 let query =
331 '(' + 349 '(' +
332 ' EXISTS (' + // Videos shared by actors we follow 350 ' EXISTS (' + // Videos shared by actors we follow
@@ -342,14 +360,14 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder {
342 ' AND "actorFollow"."state" = \'accepted\'' + 360 ' AND "actorFollow"."state" = \'accepted\'' +
343 ' )' 361 ' )'
344 362
345 if (includeLocalVideos) { 363 if (options.orLocalVideos) {
346 query += ' OR "video"."remote" IS FALSE' 364 query += ' OR "video"."remote" IS FALSE'
347 } 365 }
348 366
349 query += ')' 367 query += ')'
350 368
351 this.and.push(query) 369 this.and.push(query)
352 this.replacements.followerActorId = followerActorId 370 this.replacements.followerActorId = options.actorId
353 } 371 }
354 372
355 private whereFileExists () { 373 private whereFileExists () {
diff --git a/server/models/video/sql/videos-model-list-query-builder.ts b/server/models/video/sql/videos-model-list-query-builder.ts
index e61c51de8..ef92bd2b0 100644
--- a/server/models/video/sql/videos-model-list-query-builder.ts
+++ b/server/models/video/sql/videos-model-list-query-builder.ts
@@ -1,3 +1,4 @@
1import { VideoInclude } from '@shared/models'
1import { Sequelize } from 'sequelize' 2import { Sequelize } from 'sequelize'
2import { AbstractVideosModelQueryBuilder } from './shared/abstract-videos-model-query-builder' 3import { AbstractVideosModelQueryBuilder } from './shared/abstract-videos-model-query-builder'
3import { VideoModelBuilder } from './shared/video-model-builder' 4import { VideoModelBuilder } from './shared/video-model-builder'
@@ -28,7 +29,7 @@ export class VideosModelListQueryBuilder extends AbstractVideosModelQueryBuilder
28 this.buildListQueryFromIdsQuery(options) 29 this.buildListQueryFromIdsQuery(options)
29 30
30 return this.runQuery() 31 return this.runQuery()
31 .then(rows => this.videoModelBuilder.buildVideosFromRows(rows)) 32 .then(rows => this.videoModelBuilder.buildVideosFromRows({ rows, include: options.include }))
32 } 33 }
33 34
34 private buildInnerQuery (options: BuildVideosListQueryOptions) { 35 private buildInnerQuery (options: BuildVideosListQueryOptions) {
@@ -64,6 +65,14 @@ export class VideosModelListQueryBuilder extends AbstractVideosModelQueryBuilder
64 this.includePlaylist(options.videoPlaylistId) 65 this.includePlaylist(options.videoPlaylistId)
65 } 66 }
66 67
68 if (options.include & VideoInclude.BLACKLISTED) {
69 this.includeBlacklisted()
70 }
71
72 if (options.include & VideoInclude.BLOCKED_OWNER) {
73 this.includeBlockedOwnerAndServer(options.serverAccountIdForBlock, options.user)
74 }
75
67 const select = this.buildSelect() 76 const select = this.buildSelect()
68 77
69 this.query = `${select} FROM (${this.innerQuery}) AS "tmp" ${this.joins} ${this.innerSort}` 78 this.query = `${select} FROM (${this.innerQuery}) AS "tmp" ${this.joins} ${this.innerSort}`