diff options
Diffstat (limited to 'server/models/video')
-rw-r--r-- | server/models/video/sql/videos-id-list-query-builder.ts | 10 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 76 | ||||
-rw-r--r-- | server/models/video/video-playlist.ts | 16 | ||||
-rw-r--r-- | server/models/video/video.ts | 3 |
4 files changed, 75 insertions, 30 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 d4260c69c..7625c003d 100644 --- a/server/models/video/sql/videos-id-list-query-builder.ts +++ b/server/models/video/sql/videos-id-list-query-builder.ts | |||
@@ -35,6 +35,8 @@ export type BuildVideosListQueryOptions = { | |||
35 | tagsOneOf?: string[] | 35 | tagsOneOf?: string[] |
36 | tagsAllOf?: string[] | 36 | tagsAllOf?: string[] |
37 | 37 | ||
38 | uuids?: string[] | ||
39 | |||
38 | withFiles?: boolean | 40 | withFiles?: boolean |
39 | 41 | ||
40 | accountId?: number | 42 | accountId?: number |
@@ -161,6 +163,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { | |||
161 | this.whereTagsAllOf(options.tagsAllOf) | 163 | this.whereTagsAllOf(options.tagsAllOf) |
162 | } | 164 | } |
163 | 165 | ||
166 | if (options.uuids) { | ||
167 | this.whereUUIDs(options.uuids) | ||
168 | } | ||
169 | |||
164 | if (options.nsfw === true) { | 170 | if (options.nsfw === true) { |
165 | this.whereNSFW() | 171 | this.whereNSFW() |
166 | } else if (options.nsfw === false) { | 172 | } else if (options.nsfw === false) { |
@@ -386,6 +392,10 @@ export class VideosIdListQueryBuilder extends AbstractVideosQueryBuilder { | |||
386 | ) | 392 | ) |
387 | } | 393 | } |
388 | 394 | ||
395 | private whereUUIDs (uuids: string[]) { | ||
396 | this.and.push('"video"."uuid" IN (' + createSafeIn(this.sequelize, uuids) + ')') | ||
397 | } | ||
398 | |||
389 | private whereCategoryOneOf (categoryOneOf: number[]) { | 399 | private whereCategoryOneOf (categoryOneOf: number[]) { |
390 | this.and.push('"video"."category" IN (:categoryOneOf)') | 400 | this.and.push('"video"."category" IN (:categoryOneOf)') |
391 | this.replacements.categoryOneOf = categoryOneOf | 401 | this.replacements.categoryOneOf = categoryOneOf |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 9aa271711..327f49304 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -59,6 +59,7 @@ type AvailableForListOptions = { | |||
59 | actorId: number | 59 | actorId: number |
60 | search?: string | 60 | search?: string |
61 | host?: string | 61 | host?: string |
62 | names?: string[] | ||
62 | } | 63 | } |
63 | 64 | ||
64 | type AvailableWithStatsOptions = { | 65 | type AvailableWithStatsOptions = { |
@@ -84,18 +85,20 @@ export type SummaryOptions = { | |||
84 | // Only list local channels OR channels that are on an instance followed by actorId | 85 | // Only list local channels OR channels that are on an instance followed by actorId |
85 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) | 86 | const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) |
86 | 87 | ||
87 | const whereActor = { | 88 | const whereActorAnd: WhereOptions[] = [ |
88 | [Op.or]: [ | 89 | { |
89 | { | 90 | [Op.or]: [ |
90 | serverId: null | 91 | { |
91 | }, | 92 | serverId: null |
92 | { | 93 | }, |
93 | serverId: { | 94 | { |
94 | [Op.in]: Sequelize.literal(inQueryInstanceFollow) | 95 | serverId: { |
96 | [Op.in]: Sequelize.literal(inQueryInstanceFollow) | ||
97 | } | ||
95 | } | 98 | } |
96 | } | 99 | ] |
97 | ] | 100 | } |
98 | } | 101 | ] |
99 | 102 | ||
100 | let serverRequired = false | 103 | let serverRequired = false |
101 | let whereServer: WhereOptions | 104 | let whereServer: WhereOptions |
@@ -106,8 +109,16 @@ export type SummaryOptions = { | |||
106 | } | 109 | } |
107 | 110 | ||
108 | if (options.host === WEBSERVER.HOST) { | 111 | if (options.host === WEBSERVER.HOST) { |
109 | Object.assign(whereActor, { | 112 | whereActorAnd.push({ |
110 | [Op.and]: [ { serverId: null } ] | 113 | serverId: null |
114 | }) | ||
115 | } | ||
116 | |||
117 | if (options.names) { | ||
118 | whereActorAnd.push({ | ||
119 | preferredUsername: { | ||
120 | [Op.in]: options.names | ||
121 | } | ||
111 | }) | 122 | }) |
112 | } | 123 | } |
113 | 124 | ||
@@ -118,7 +129,9 @@ export type SummaryOptions = { | |||
118 | exclude: unusedActorAttributesForAPI | 129 | exclude: unusedActorAttributesForAPI |
119 | }, | 130 | }, |
120 | model: ActorModel, | 131 | model: ActorModel, |
121 | where: whereActor, | 132 | where: { |
133 | [Op.and]: whereActorAnd | ||
134 | }, | ||
122 | include: [ | 135 | include: [ |
123 | { | 136 | { |
124 | model: ServerModel, | 137 | model: ServerModel, |
@@ -454,26 +467,23 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"` | |||
454 | 467 | ||
455 | static searchForApi (options: { | 468 | static searchForApi (options: { |
456 | actorId: number | 469 | actorId: number |
457 | search: string | 470 | search?: string |
458 | start: number | 471 | start: number |
459 | count: number | 472 | count: number |
460 | sort: string | 473 | sort: string |
461 | 474 | ||
462 | host?: string | 475 | host?: string |
476 | names?: string[] | ||
463 | }) { | 477 | }) { |
464 | const attributesInclude = [] | 478 | let attributesInclude: any[] = [ literal('0 as similarity') ] |
465 | const escapedSearch = VideoChannelModel.sequelize.escape(options.search) | 479 | let where: WhereOptions |
466 | const escapedLikeSearch = VideoChannelModel.sequelize.escape('%' + options.search + '%') | ||
467 | attributesInclude.push(createSimilarityAttribute('VideoChannelModel.name', options.search)) | ||
468 | 480 | ||
469 | const query = { | 481 | if (options.search) { |
470 | attributes: { | 482 | const escapedSearch = VideoChannelModel.sequelize.escape(options.search) |
471 | include: attributesInclude | 483 | const escapedLikeSearch = VideoChannelModel.sequelize.escape('%' + options.search + '%') |
472 | }, | 484 | attributesInclude = [ createSimilarityAttribute('VideoChannelModel.name', options.search) ] |
473 | offset: options.start, | 485 | |
474 | limit: options.count, | 486 | where = { |
475 | order: getSort(options.sort), | ||
476 | where: { | ||
477 | [Op.or]: [ | 487 | [Op.or]: [ |
478 | Sequelize.literal( | 488 | Sequelize.literal( |
479 | 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' | 489 | 'lower(immutable_unaccent("VideoChannelModel"."name")) % lower(immutable_unaccent(' + escapedSearch + '))' |
@@ -485,9 +495,19 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"` | |||
485 | } | 495 | } |
486 | } | 496 | } |
487 | 497 | ||
498 | const query = { | ||
499 | attributes: { | ||
500 | include: attributesInclude | ||
501 | }, | ||
502 | offset: options.start, | ||
503 | limit: options.count, | ||
504 | order: getSort(options.sort), | ||
505 | where | ||
506 | } | ||
507 | |||
488 | return VideoChannelModel | 508 | return VideoChannelModel |
489 | .scope({ | 509 | .scope({ |
490 | method: [ ScopeNames.FOR_API, { actorId: options.actorId, host: options.host } as AvailableForListOptions ] | 510 | method: [ ScopeNames.FOR_API, { actorId: options.actorId, host: options.host, names: options.names } as AvailableForListOptions ] |
491 | }) | 511 | }) |
492 | .findAndCountAll(query) | 512 | .findAndCountAll(query) |
493 | .then(({ rows, count }) => { | 513 | .then(({ rows, count }) => { |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index a2dc7075d..caa79952d 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -83,6 +83,7 @@ type AvailableForListOptions = { | |||
83 | listMyPlaylists?: boolean | 83 | listMyPlaylists?: boolean |
84 | search?: string | 84 | search?: string |
85 | host?: string | 85 | host?: string |
86 | uuids?: string[] | ||
86 | withVideos?: boolean | 87 | withVideos?: boolean |
87 | } | 88 | } |
88 | 89 | ||
@@ -200,18 +201,26 @@ function getVideoLengthSelect () { | |||
200 | }) | 201 | }) |
201 | } | 202 | } |
202 | 203 | ||
204 | if (options.uuids) { | ||
205 | whereAnd.push({ | ||
206 | uuid: { | ||
207 | [Op.in]: options.uuids | ||
208 | } | ||
209 | }) | ||
210 | } | ||
211 | |||
203 | if (options.withVideos === true) { | 212 | if (options.withVideos === true) { |
204 | whereAnd.push( | 213 | whereAnd.push( |
205 | literal(`(${getVideoLengthSelect()}) != 0`) | 214 | literal(`(${getVideoLengthSelect()}) != 0`) |
206 | ) | 215 | ) |
207 | } | 216 | } |
208 | 217 | ||
209 | const attributesInclude = [] | 218 | let attributesInclude: any[] = [ literal('0 as similarity') ] |
210 | 219 | ||
211 | if (options.search) { | 220 | if (options.search) { |
212 | const escapedSearch = VideoPlaylistModel.sequelize.escape(options.search) | 221 | const escapedSearch = VideoPlaylistModel.sequelize.escape(options.search) |
213 | const escapedLikeSearch = VideoPlaylistModel.sequelize.escape('%' + options.search + '%') | 222 | const escapedLikeSearch = VideoPlaylistModel.sequelize.escape('%' + options.search + '%') |
214 | attributesInclude.push(createSimilarityAttribute('VideoPlaylistModel.name', options.search)) | 223 | attributesInclude = [ createSimilarityAttribute('VideoPlaylistModel.name', options.search) ] |
215 | 224 | ||
216 | whereAnd.push({ | 225 | whereAnd.push({ |
217 | [Op.or]: [ | 226 | [Op.or]: [ |
@@ -359,6 +368,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli | |||
359 | listMyPlaylists?: boolean | 368 | listMyPlaylists?: boolean |
360 | search?: string | 369 | search?: string |
361 | host?: string | 370 | host?: string |
371 | uuids?: string[] | ||
362 | withVideos?: boolean // false by default | 372 | withVideos?: boolean // false by default |
363 | }) { | 373 | }) { |
364 | const query = { | 374 | const query = { |
@@ -379,6 +389,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli | |||
379 | listMyPlaylists: options.listMyPlaylists, | 389 | listMyPlaylists: options.listMyPlaylists, |
380 | search: options.search, | 390 | search: options.search, |
381 | host: options.host, | 391 | host: options.host, |
392 | uuids: options.uuids, | ||
382 | withVideos: options.withVideos || false | 393 | withVideos: options.withVideos || false |
383 | } as AvailableForListOptions | 394 | } as AvailableForListOptions |
384 | ] | 395 | ] |
@@ -402,6 +413,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli | |||
402 | sort: string | 413 | sort: string |
403 | search?: string | 414 | search?: string |
404 | host?: string | 415 | host?: string |
416 | uuids?: string[] | ||
405 | }) { | 417 | }) { |
406 | return VideoPlaylistModel.listForApi({ | 418 | return VideoPlaylistModel.listForApi({ |
407 | ...options, | 419 | ...options, |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index c444f381e..fe92ead04 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1132,6 +1132,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { | |||
1132 | durationMax?: number // seconds | 1132 | durationMax?: number // seconds |
1133 | user?: MUserAccountId | 1133 | user?: MUserAccountId |
1134 | filter?: VideoFilter | 1134 | filter?: VideoFilter |
1135 | uuids?: string[] | ||
1135 | }) { | 1136 | }) { |
1136 | const serverActor = await getServerActor() | 1137 | const serverActor = await getServerActor() |
1137 | 1138 | ||
@@ -1167,6 +1168,8 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { | |||
1167 | durationMin: options.durationMin, | 1168 | durationMin: options.durationMin, |
1168 | durationMax: options.durationMax, | 1169 | durationMax: options.durationMax, |
1169 | 1170 | ||
1171 | uuids: options.uuids, | ||
1172 | |||
1170 | search: options.search | 1173 | search: options.search |
1171 | } | 1174 | } |
1172 | 1175 | ||