aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video')
-rw-r--r--server/models/video/sql/videos-id-list-query-builder.ts45
-rw-r--r--server/models/video/video.ts44
2 files changed, 68 insertions, 21 deletions
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 5064afafe..4a882e790 100644
--- a/server/models/video/sql/videos-id-list-query-builder.ts
+++ b/server/models/video/sql/videos-id-list-query-builder.ts
@@ -44,6 +44,8 @@ export type BuildVideosListQueryOptions = {
44 uuids?: string[] 44 uuids?: string[]
45 45
46 hasFiles?: boolean 46 hasFiles?: boolean
47 hasHLSFiles?: boolean
48 hasWebtorrentFiles?: boolean
47 49
48 accountId?: number 50 accountId?: number
49 videoChannelId?: number 51 videoChannelId?: number
@@ -169,6 +171,14 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
169 this.whereFileExists() 171 this.whereFileExists()
170 } 172 }
171 173
174 if (exists(options.hasWebtorrentFiles)) {
175 this.whereWebTorrentFileExists(options.hasWebtorrentFiles)
176 }
177
178 if (exists(options.hasHLSFiles)) {
179 this.whereHLSFileExists(options.hasHLSFiles)
180 }
181
172 if (options.tagsOneOf) { 182 if (options.tagsOneOf) {
173 this.whereTagsOneOf(options.tagsOneOf) 183 this.whereTagsOneOf(options.tagsOneOf)
174 } 184 }
@@ -371,16 +381,31 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
371 } 381 }
372 382
373 private whereFileExists () { 383 private whereFileExists () {
374 this.and.push( 384 this.and.push(`(${this.buildWebTorrentFileExistsQuery(true)} OR ${this.buildHLSFileExistsQuery(true)})`)
375 '(' + 385 }
376 ' EXISTS (SELECT 1 FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id") ' + 386
377 ' OR EXISTS (' + 387 private whereWebTorrentFileExists (exists: boolean) {
378 ' SELECT 1 FROM "videoStreamingPlaylist" ' + 388 this.and.push(this.buildWebTorrentFileExistsQuery(exists))
379 ' INNER JOIN "videoFile" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist"."id" ' + 389 }
380 ' WHERE "videoStreamingPlaylist"."videoId" = "video"."id"' + 390
381 ' )' + 391 private whereHLSFileExists (exists: boolean) {
382 ')' 392 this.and.push(this.buildHLSFileExistsQuery(exists))
383 ) 393 }
394
395 private buildWebTorrentFileExistsQuery (exists: boolean) {
396 const prefix = exists ? '' : 'NOT '
397
398 return prefix + 'EXISTS (SELECT 1 FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id")'
399 }
400
401 private buildHLSFileExistsQuery (exists: boolean) {
402 const prefix = exists ? '' : 'NOT '
403
404 return prefix + 'EXISTS (' +
405 ' SELECT 1 FROM "videoStreamingPlaylist" ' +
406 ' INNER JOIN "videoFile" ON "videoFile"."videoStreamingPlaylistId" = "videoStreamingPlaylist"."id" ' +
407 ' WHERE "videoStreamingPlaylist"."videoId" = "video"."id"' +
408 ')'
384 } 409 }
385 410
386 private whereTagsOneOf (tagsOneOf: string[]) { 411 private whereTagsOneOf (tagsOneOf: string[]) {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index f9618c102..aef4fd20a 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1030,6 +1030,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1030 include?: VideoInclude 1030 include?: VideoInclude
1031 1031
1032 hasFiles?: boolean // default false 1032 hasFiles?: boolean // default false
1033 hasWebtorrentFiles?: boolean
1034 hasHLSFiles?: boolean
1033 1035
1034 categoryOneOf?: number[] 1036 categoryOneOf?: number[]
1035 licenceOneOf?: number[] 1037 licenceOneOf?: number[]
@@ -1053,9 +1055,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1053 1055
1054 search?: string 1056 search?: string
1055 }) { 1057 }) {
1056 if (VideoModel.isPrivateInclude(options.include) && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) { 1058 VideoModel.throwIfPrivateIncludeWithoutUser(options.include, options.user)
1057 throw new Error('Try to filter all-local but no user has not the see all videos right')
1058 }
1059 1059
1060 const trendingDays = options.sort.endsWith('trending') 1060 const trendingDays = options.sort.endsWith('trending')
1061 ? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS 1061 ? CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
@@ -1088,6 +1088,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1088 'videoPlaylistId', 1088 'videoPlaylistId',
1089 'user', 1089 'user',
1090 'historyOfUser', 1090 'historyOfUser',
1091 'hasHLSFiles',
1092 'hasWebtorrentFiles',
1091 'search' 1093 'search'
1092 ]), 1094 ]),
1093 1095
@@ -1103,27 +1105,39 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1103 start: number 1105 start: number
1104 count: number 1106 count: number
1105 sort: string 1107 sort: string
1106 search?: string 1108
1107 host?: string
1108 startDate?: string // ISO 8601
1109 endDate?: string // ISO 8601
1110 originallyPublishedStartDate?: string
1111 originallyPublishedEndDate?: string
1112 nsfw?: boolean 1109 nsfw?: boolean
1113 isLive?: boolean 1110 isLive?: boolean
1114 isLocal?: boolean 1111 isLocal?: boolean
1115 include?: VideoInclude 1112 include?: VideoInclude
1113
1116 categoryOneOf?: number[] 1114 categoryOneOf?: number[]
1117 licenceOneOf?: number[] 1115 licenceOneOf?: number[]
1118 languageOneOf?: string[] 1116 languageOneOf?: string[]
1119 tagsOneOf?: string[] 1117 tagsOneOf?: string[]
1120 tagsAllOf?: string[] 1118 tagsAllOf?: string[]
1119
1120 displayOnlyForFollower: DisplayOnlyForFollowerOptions | null
1121
1122 user?: MUserAccountId
1123
1124 hasWebtorrentFiles?: boolean
1125 hasHLSFiles?: boolean
1126
1127 search?: string
1128
1129 host?: string
1130 startDate?: string // ISO 8601
1131 endDate?: string // ISO 8601
1132 originallyPublishedStartDate?: string
1133 originallyPublishedEndDate?: string
1134
1121 durationMin?: number // seconds 1135 durationMin?: number // seconds
1122 durationMax?: number // seconds 1136 durationMax?: number // seconds
1123 user?: MUserAccountId
1124 uuids?: string[] 1137 uuids?: string[]
1125 displayOnlyForFollower: DisplayOnlyForFollowerOptions | null
1126 }) { 1138 }) {
1139 VideoModel.throwIfPrivateIncludeWithoutUser(options.include, options.user)
1140
1127 const serverActor = await getServerActor() 1141 const serverActor = await getServerActor()
1128 1142
1129 const queryOptions = { 1143 const queryOptions = {
@@ -1148,6 +1162,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1148 'originallyPublishedEndDate', 1162 'originallyPublishedEndDate',
1149 'durationMin', 1163 'durationMin',
1150 'durationMax', 1164 'durationMax',
1165 'hasHLSFiles',
1166 'hasWebtorrentFiles',
1151 'uuids', 1167 'uuids',
1152 'search', 1168 'search',
1153 'displayOnlyForFollower' 1169 'displayOnlyForFollower'
@@ -1489,6 +1505,12 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1489 } 1505 }
1490 } 1506 }
1491 1507
1508 private static throwIfPrivateIncludeWithoutUser (include: VideoInclude, user: MUserAccountId) {
1509 if (VideoModel.isPrivateInclude(include) && !user?.hasRight(UserRight.SEE_ALL_VIDEOS)) {
1510 throw new Error('Try to filter all-local but no user has not the see all videos right')
1511 }
1512 }
1513
1492 private static isPrivateInclude (include: VideoInclude) { 1514 private static isPrivateInclude (include: VideoInclude) {
1493 return include & VideoInclude.BLACKLISTED || 1515 return include & VideoInclude.BLACKLISTED ||
1494 include & VideoInclude.BLOCKED_OWNER || 1516 include & VideoInclude.BLOCKED_OWNER ||