diff options
Diffstat (limited to 'server/models/video/video.ts')
-rw-r--r-- | server/models/video/video.ts | 169 |
1 files changed, 88 insertions, 81 deletions
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 9840d17fd..329cebd28 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -3,7 +3,18 @@ import { maxBy } from 'lodash' | |||
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import * as parseTorrent from 'parse-torrent' | 4 | import * as parseTorrent from 'parse-torrent' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as Sequelize from 'sequelize' | 6 | import { |
7 | CountOptions, | ||
8 | FindOptions, | ||
9 | IncludeOptions, | ||
10 | ModelIndexesOptions, | ||
11 | Op, | ||
12 | QueryTypes, | ||
13 | ScopeOptions, | ||
14 | Sequelize, | ||
15 | Transaction, | ||
16 | WhereOptions | ||
17 | } from 'sequelize' | ||
7 | import { | 18 | import { |
8 | AllowNull, | 19 | AllowNull, |
9 | BeforeDestroy, | 20 | BeforeDestroy, |
@@ -16,8 +27,6 @@ import { | |||
16 | ForeignKey, | 27 | ForeignKey, |
17 | HasMany, | 28 | HasMany, |
18 | HasOne, | 29 | HasOne, |
19 | IFindOptions, | ||
20 | IIncludeOptions, | ||
21 | Is, | 30 | Is, |
22 | IsInt, | 31 | IsInt, |
23 | IsUUID, | 32 | IsUUID, |
@@ -45,7 +54,7 @@ import { | |||
45 | isVideoStateValid, | 54 | isVideoStateValid, |
46 | isVideoSupportValid | 55 | isVideoSupportValid |
47 | } from '../../helpers/custom-validators/videos' | 56 | } from '../../helpers/custom-validators/videos' |
48 | import { generateImageFromVideoFile, getVideoFileResolution } from '../../helpers/ffmpeg-utils' | 57 | import { getVideoFileResolution } from '../../helpers/ffmpeg-utils' |
49 | import { logger } from '../../helpers/logger' | 58 | import { logger } from '../../helpers/logger' |
50 | import { getServerActor } from '../../helpers/utils' | 59 | import { getServerActor } from '../../helpers/utils' |
51 | import { | 60 | import { |
@@ -54,11 +63,9 @@ import { | |||
54 | CONSTRAINTS_FIELDS, | 63 | CONSTRAINTS_FIELDS, |
55 | HLS_REDUNDANCY_DIRECTORY, | 64 | HLS_REDUNDANCY_DIRECTORY, |
56 | HLS_STREAMING_PLAYLIST_DIRECTORY, | 65 | HLS_STREAMING_PLAYLIST_DIRECTORY, |
57 | PREVIEWS_SIZE, | ||
58 | REMOTE_SCHEME, | 66 | REMOTE_SCHEME, |
59 | STATIC_DOWNLOAD_PATHS, | 67 | STATIC_DOWNLOAD_PATHS, |
60 | STATIC_PATHS, | 68 | STATIC_PATHS, |
61 | THUMBNAILS_SIZE, | ||
62 | VIDEO_CATEGORIES, | 69 | VIDEO_CATEGORIES, |
63 | VIDEO_LANGUAGES, | 70 | VIDEO_LANGUAGES, |
64 | VIDEO_LICENCES, | 71 | VIDEO_LICENCES, |
@@ -111,7 +118,7 @@ import { ThumbnailModel } from './thumbnail' | |||
111 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 118 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
112 | 119 | ||
113 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | 120 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation |
114 | const indexes: Sequelize.DefineIndexesOptions[] = [ | 121 | const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [ |
115 | buildTrigramSearchIndex('video_name_trigram', 'name'), | 122 | buildTrigramSearchIndex('video_name_trigram', 'name'), |
116 | 123 | ||
117 | { fields: [ 'createdAt' ] }, | 124 | { fields: [ 'createdAt' ] }, |
@@ -123,7 +130,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [ | |||
123 | fields: [ 'originallyPublishedAt' ], | 130 | fields: [ 'originallyPublishedAt' ], |
124 | where: { | 131 | where: { |
125 | originallyPublishedAt: { | 132 | originallyPublishedAt: { |
126 | [Sequelize.Op.ne]: null | 133 | [Op.ne]: null |
127 | } | 134 | } |
128 | } | 135 | } |
129 | }, | 136 | }, |
@@ -131,7 +138,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [ | |||
131 | fields: [ 'category' ], // We don't care videos with an unknown category | 138 | fields: [ 'category' ], // We don't care videos with an unknown category |
132 | where: { | 139 | where: { |
133 | category: { | 140 | category: { |
134 | [Sequelize.Op.ne]: null | 141 | [Op.ne]: null |
135 | } | 142 | } |
136 | } | 143 | } |
137 | }, | 144 | }, |
@@ -139,7 +146,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [ | |||
139 | fields: [ 'licence' ], // We don't care videos with an unknown licence | 146 | fields: [ 'licence' ], // We don't care videos with an unknown licence |
140 | where: { | 147 | where: { |
141 | licence: { | 148 | licence: { |
142 | [Sequelize.Op.ne]: null | 149 | [Op.ne]: null |
143 | } | 150 | } |
144 | } | 151 | } |
145 | }, | 152 | }, |
@@ -147,7 +154,7 @@ const indexes: Sequelize.DefineIndexesOptions[] = [ | |||
147 | fields: [ 'language' ], // We don't care videos with an unknown language | 154 | fields: [ 'language' ], // We don't care videos with an unknown language |
148 | where: { | 155 | where: { |
149 | language: { | 156 | language: { |
150 | [Sequelize.Op.ne]: null | 157 | [Op.ne]: null |
151 | } | 158 | } |
152 | } | 159 | } |
153 | }, | 160 | }, |
@@ -222,10 +229,10 @@ type AvailableForListIDsOptions = { | |||
222 | 229 | ||
223 | @Scopes({ | 230 | @Scopes({ |
224 | [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { | 231 | [ ScopeNames.FOR_API ]: (options: ForAPIOptions) => { |
225 | const query: IFindOptions<VideoModel> = { | 232 | const query: FindOptions = { |
226 | where: { | 233 | where: { |
227 | id: { | 234 | id: { |
228 | [ Sequelize.Op.any ]: options.ids | 235 | [ Op.in ]: options.ids // FIXME: sequelize any seems broken |
229 | } | 236 | } |
230 | }, | 237 | }, |
231 | include: [ | 238 | include: [ |
@@ -256,21 +263,21 @@ type AvailableForListIDsOptions = { | |||
256 | return query | 263 | return query |
257 | }, | 264 | }, |
258 | [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { | 265 | [ ScopeNames.AVAILABLE_FOR_LIST_IDS ]: (options: AvailableForListIDsOptions) => { |
259 | const query: IFindOptions<VideoModel> = { | 266 | const query: FindOptions = { |
260 | raw: true, | 267 | raw: true, |
261 | attributes: [ 'id' ], | 268 | attributes: [ 'id' ], |
262 | where: { | 269 | where: { |
263 | id: { | 270 | id: { |
264 | [ Sequelize.Op.and ]: [ | 271 | [ Op.and ]: [ |
265 | { | 272 | { |
266 | [ Sequelize.Op.notIn ]: Sequelize.literal( | 273 | [ Op.notIn ]: Sequelize.literal( |
267 | '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' | 274 | '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' |
268 | ) | 275 | ) |
269 | } | 276 | } |
270 | ] | 277 | ] |
271 | }, | 278 | }, |
272 | channelId: { | 279 | channelId: { |
273 | [ Sequelize.Op.notIn ]: Sequelize.literal( | 280 | [ Op.notIn ]: Sequelize.literal( |
274 | '(' + | 281 | '(' + |
275 | 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' + | 282 | 'SELECT id FROM "videoChannel" WHERE "accountId" IN (' + |
276 | buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) + | 283 | buildBlockedAccountSQL(options.serverAccountId, options.user ? options.user.Account.id : undefined) + |
@@ -288,12 +295,12 @@ type AvailableForListIDsOptions = { | |||
288 | // Always list public videos | 295 | // Always list public videos |
289 | privacy: VideoPrivacy.PUBLIC, | 296 | privacy: VideoPrivacy.PUBLIC, |
290 | // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding | 297 | // Always list published videos, or videos that are being transcoded but on which we don't want to wait for transcoding |
291 | [ Sequelize.Op.or ]: [ | 298 | [ Op.or ]: [ |
292 | { | 299 | { |
293 | state: VideoState.PUBLISHED | 300 | state: VideoState.PUBLISHED |
294 | }, | 301 | }, |
295 | { | 302 | { |
296 | [ Sequelize.Op.and ]: { | 303 | [ Op.and ]: { |
297 | state: VideoState.TO_TRANSCODE, | 304 | state: VideoState.TO_TRANSCODE, |
298 | waitTranscoding: false | 305 | waitTranscoding: false |
299 | } | 306 | } |
@@ -318,7 +325,7 @@ type AvailableForListIDsOptions = { | |||
318 | } | 325 | } |
319 | 326 | ||
320 | if (options.filter || options.accountId || options.videoChannelId) { | 327 | if (options.filter || options.accountId || options.videoChannelId) { |
321 | const videoChannelInclude: IIncludeOptions = { | 328 | const videoChannelInclude: IncludeOptions = { |
322 | attributes: [], | 329 | attributes: [], |
323 | model: VideoChannelModel.unscoped(), | 330 | model: VideoChannelModel.unscoped(), |
324 | required: true | 331 | required: true |
@@ -331,7 +338,7 @@ type AvailableForListIDsOptions = { | |||
331 | } | 338 | } |
332 | 339 | ||
333 | if (options.filter || options.accountId) { | 340 | if (options.filter || options.accountId) { |
334 | const accountInclude: IIncludeOptions = { | 341 | const accountInclude: IncludeOptions = { |
335 | attributes: [], | 342 | attributes: [], |
336 | model: AccountModel.unscoped(), | 343 | model: AccountModel.unscoped(), |
337 | required: true | 344 | required: true |
@@ -371,8 +378,8 @@ type AvailableForListIDsOptions = { | |||
371 | 378 | ||
372 | // Force actorId to be a number to avoid SQL injections | 379 | // Force actorId to be a number to avoid SQL injections |
373 | const actorIdNumber = parseInt(options.followerActorId.toString(), 10) | 380 | const actorIdNumber = parseInt(options.followerActorId.toString(), 10) |
374 | query.where[ 'id' ][ Sequelize.Op.and ].push({ | 381 | query.where[ 'id' ][ Op.and ].push({ |
375 | [ Sequelize.Op.in ]: Sequelize.literal( | 382 | [ Op.in ]: Sequelize.literal( |
376 | '(' + | 383 | '(' + |
377 | 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + | 384 | 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + |
378 | 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + | 385 | 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + |
@@ -391,8 +398,8 @@ type AvailableForListIDsOptions = { | |||
391 | } | 398 | } |
392 | 399 | ||
393 | if (options.withFiles === true) { | 400 | if (options.withFiles === true) { |
394 | query.where[ 'id' ][ Sequelize.Op.and ].push({ | 401 | query.where[ 'id' ][ Op.and ].push({ |
395 | [ Sequelize.Op.in ]: Sequelize.literal( | 402 | [ Op.in ]: Sequelize.literal( |
396 | '(SELECT "videoId" FROM "videoFile")' | 403 | '(SELECT "videoId" FROM "videoFile")' |
397 | ) | 404 | ) |
398 | }) | 405 | }) |
@@ -406,8 +413,8 @@ type AvailableForListIDsOptions = { | |||
406 | } | 413 | } |
407 | 414 | ||
408 | if (options.tagsOneOf) { | 415 | if (options.tagsOneOf) { |
409 | query.where[ 'id' ][ Sequelize.Op.and ].push({ | 416 | query.where[ 'id' ][ Op.and ].push({ |
410 | [ Sequelize.Op.in ]: Sequelize.literal( | 417 | [ Op.in ]: Sequelize.literal( |
411 | '(' + | 418 | '(' + |
412 | 'SELECT "videoId" FROM "videoTag" ' + | 419 | 'SELECT "videoId" FROM "videoTag" ' + |
413 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 420 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
@@ -418,8 +425,8 @@ type AvailableForListIDsOptions = { | |||
418 | } | 425 | } |
419 | 426 | ||
420 | if (options.tagsAllOf) { | 427 | if (options.tagsAllOf) { |
421 | query.where[ 'id' ][ Sequelize.Op.and ].push({ | 428 | query.where[ 'id' ][ Op.and ].push({ |
422 | [ Sequelize.Op.in ]: Sequelize.literal( | 429 | [ Op.in ]: Sequelize.literal( |
423 | '(' + | 430 | '(' + |
424 | 'SELECT "videoId" FROM "videoTag" ' + | 431 | 'SELECT "videoId" FROM "videoTag" ' + |
425 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 432 | 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
@@ -437,19 +444,19 @@ type AvailableForListIDsOptions = { | |||
437 | 444 | ||
438 | if (options.categoryOneOf) { | 445 | if (options.categoryOneOf) { |
439 | query.where[ 'category' ] = { | 446 | query.where[ 'category' ] = { |
440 | [ Sequelize.Op.or ]: options.categoryOneOf | 447 | [ Op.or ]: options.categoryOneOf |
441 | } | 448 | } |
442 | } | 449 | } |
443 | 450 | ||
444 | if (options.licenceOneOf) { | 451 | if (options.licenceOneOf) { |
445 | query.where[ 'licence' ] = { | 452 | query.where[ 'licence' ] = { |
446 | [ Sequelize.Op.or ]: options.licenceOneOf | 453 | [ Op.or ]: options.licenceOneOf |
447 | } | 454 | } |
448 | } | 455 | } |
449 | 456 | ||
450 | if (options.languageOneOf) { | 457 | if (options.languageOneOf) { |
451 | query.where[ 'language' ] = { | 458 | query.where[ 'language' ] = { |
452 | [ Sequelize.Op.or ]: options.languageOneOf | 459 | [ Op.or ]: options.languageOneOf |
453 | } | 460 | } |
454 | } | 461 | } |
455 | 462 | ||
@@ -498,7 +505,7 @@ type AvailableForListIDsOptions = { | |||
498 | } | 505 | } |
499 | ] | 506 | ] |
500 | } | 507 | } |
501 | ] | 508 | ] as any // FIXME: sequelize typings |
502 | }, | 509 | }, |
503 | [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { | 510 | [ ScopeNames.WITH_ACCOUNT_DETAILS ]: { |
504 | include: [ | 511 | include: [ |
@@ -550,7 +557,7 @@ type AvailableForListIDsOptions = { | |||
550 | } | 557 | } |
551 | ] | 558 | ] |
552 | } | 559 | } |
553 | ] | 560 | ] as any // FIXME: sequelize typings |
554 | }, | 561 | }, |
555 | [ ScopeNames.WITH_TAGS ]: { | 562 | [ ScopeNames.WITH_TAGS ]: { |
556 | include: [ () => TagModel ] | 563 | include: [ () => TagModel ] |
@@ -656,19 +663,19 @@ export class VideoModel extends Model<VideoModel> { | |||
656 | 663 | ||
657 | @AllowNull(true) | 664 | @AllowNull(true) |
658 | @Default(null) | 665 | @Default(null) |
659 | @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category')) | 666 | @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category', true)) |
660 | @Column | 667 | @Column |
661 | category: number | 668 | category: number |
662 | 669 | ||
663 | @AllowNull(true) | 670 | @AllowNull(true) |
664 | @Default(null) | 671 | @Default(null) |
665 | @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence')) | 672 | @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence', true)) |
666 | @Column | 673 | @Column |
667 | licence: number | 674 | licence: number |
668 | 675 | ||
669 | @AllowNull(true) | 676 | @AllowNull(true) |
670 | @Default(null) | 677 | @Default(null) |
671 | @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language')) | 678 | @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language', true)) |
672 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.LANGUAGE.max)) | 679 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.LANGUAGE.max)) |
673 | language: string | 680 | language: string |
674 | 681 | ||
@@ -684,13 +691,13 @@ export class VideoModel extends Model<VideoModel> { | |||
684 | 691 | ||
685 | @AllowNull(true) | 692 | @AllowNull(true) |
686 | @Default(null) | 693 | @Default(null) |
687 | @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description')) | 694 | @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description', true)) |
688 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max)) | 695 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max)) |
689 | description: string | 696 | description: string |
690 | 697 | ||
691 | @AllowNull(true) | 698 | @AllowNull(true) |
692 | @Default(null) | 699 | @Default(null) |
693 | @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support')) | 700 | @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support', true)) |
694 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max)) | 701 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max)) |
695 | support: string | 702 | support: string |
696 | 703 | ||
@@ -754,7 +761,7 @@ export class VideoModel extends Model<VideoModel> { | |||
754 | updatedAt: Date | 761 | updatedAt: Date |
755 | 762 | ||
756 | @AllowNull(false) | 763 | @AllowNull(false) |
757 | @Default(Sequelize.NOW) | 764 | @Default(DataType.NOW) |
758 | @Column | 765 | @Column |
759 | publishedAt: Date | 766 | publishedAt: Date |
760 | 767 | ||
@@ -999,12 +1006,12 @@ export class VideoModel extends Model<VideoModel> { | |||
999 | distinct: true, | 1006 | distinct: true, |
1000 | offset: start, | 1007 | offset: start, |
1001 | limit: count, | 1008 | limit: count, |
1002 | order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ]), | 1009 | order: getVideoSort('createdAt', [ 'Tags', 'name', 'ASC' ] as any), // FIXME: sequelize typings |
1003 | where: { | 1010 | where: { |
1004 | id: { | 1011 | id: { |
1005 | [ Sequelize.Op.in ]: Sequelize.literal('(' + rawQuery + ')') | 1012 | [ Op.in ]: Sequelize.literal('(' + rawQuery + ')') |
1006 | }, | 1013 | }, |
1007 | [ Sequelize.Op.or ]: [ | 1014 | [ Op.or ]: [ |
1008 | { privacy: VideoPrivacy.PUBLIC }, | 1015 | { privacy: VideoPrivacy.PUBLIC }, |
1009 | { privacy: VideoPrivacy.UNLISTED } | 1016 | { privacy: VideoPrivacy.UNLISTED } |
1010 | ] | 1017 | ] |
@@ -1021,10 +1028,10 @@ export class VideoModel extends Model<VideoModel> { | |||
1021 | required: false, | 1028 | required: false, |
1022 | // We only want videos shared by this actor | 1029 | // We only want videos shared by this actor |
1023 | where: { | 1030 | where: { |
1024 | [ Sequelize.Op.and ]: [ | 1031 | [ Op.and ]: [ |
1025 | { | 1032 | { |
1026 | id: { | 1033 | id: { |
1027 | [ Sequelize.Op.not ]: null | 1034 | [ Op.not ]: null |
1028 | } | 1035 | } |
1029 | }, | 1036 | }, |
1030 | { | 1037 | { |
@@ -1070,13 +1077,13 @@ export class VideoModel extends Model<VideoModel> { | |||
1070 | return Bluebird.all([ | 1077 | return Bluebird.all([ |
1071 | // FIXME: typing issue | 1078 | // FIXME: typing issue |
1072 | VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any), | 1079 | VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findAll(query as any), |
1073 | VideoModel.sequelize.query(rawCountQuery, { type: Sequelize.QueryTypes.SELECT }) | 1080 | VideoModel.sequelize.query<{ total: number }>(rawCountQuery, { type: QueryTypes.SELECT }) |
1074 | ]).then(([ rows, totals ]) => { | 1081 | ]).then(([ rows, totals ]) => { |
1075 | // totals: totalVideos + totalVideoShares | 1082 | // totals: totalVideos + totalVideoShares |
1076 | let totalVideos = 0 | 1083 | let totalVideos = 0 |
1077 | let totalVideoShares = 0 | 1084 | let totalVideoShares = 0 |
1078 | if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total, 10) | 1085 | if (totals[ 0 ]) totalVideos = parseInt(totals[ 0 ].total + '', 10) |
1079 | if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total, 10) | 1086 | if (totals[ 1 ]) totalVideoShares = parseInt(totals[ 1 ].total + '', 10) |
1080 | 1087 | ||
1081 | const total = totalVideos + totalVideoShares | 1088 | const total = totalVideos + totalVideoShares |
1082 | return { | 1089 | return { |
@@ -1087,7 +1094,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1087 | } | 1094 | } |
1088 | 1095 | ||
1089 | static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { | 1096 | static listUserVideosForApi (accountId: number, start: number, count: number, sort: string, withFiles = false) { |
1090 | const query: IFindOptions<VideoModel> = { | 1097 | const query: FindOptions = { |
1091 | offset: start, | 1098 | offset: start, |
1092 | limit: count, | 1099 | limit: count, |
1093 | order: getVideoSort(sort), | 1100 | order: getVideoSort(sort), |
@@ -1158,7 +1165,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1158 | throw new Error('Try to filter all-local but no user has not the see all videos right') | 1165 | throw new Error('Try to filter all-local but no user has not the see all videos right') |
1159 | } | 1166 | } |
1160 | 1167 | ||
1161 | const query: IFindOptions<VideoModel> = { | 1168 | const query: FindOptions = { |
1162 | offset: options.start, | 1169 | offset: options.start, |
1163 | limit: options.count, | 1170 | limit: options.count, |
1164 | order: getVideoSort(options.sort) | 1171 | order: getVideoSort(options.sort) |
@@ -1225,8 +1232,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1225 | if (options.startDate || options.endDate) { | 1232 | if (options.startDate || options.endDate) { |
1226 | const publishedAtRange = {} | 1233 | const publishedAtRange = {} |
1227 | 1234 | ||
1228 | if (options.startDate) publishedAtRange[ Sequelize.Op.gte ] = options.startDate | 1235 | if (options.startDate) publishedAtRange[ Op.gte ] = options.startDate |
1229 | if (options.endDate) publishedAtRange[ Sequelize.Op.lte ] = options.endDate | 1236 | if (options.endDate) publishedAtRange[ Op.lte ] = options.endDate |
1230 | 1237 | ||
1231 | whereAnd.push({ publishedAt: publishedAtRange }) | 1238 | whereAnd.push({ publishedAt: publishedAtRange }) |
1232 | } | 1239 | } |
@@ -1234,8 +1241,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1234 | if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) { | 1241 | if (options.originallyPublishedStartDate || options.originallyPublishedEndDate) { |
1235 | const originallyPublishedAtRange = {} | 1242 | const originallyPublishedAtRange = {} |
1236 | 1243 | ||
1237 | if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Sequelize.Op.gte ] = options.originallyPublishedStartDate | 1244 | if (options.originallyPublishedStartDate) originallyPublishedAtRange[ Op.gte ] = options.originallyPublishedStartDate |
1238 | if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Sequelize.Op.lte ] = options.originallyPublishedEndDate | 1245 | if (options.originallyPublishedEndDate) originallyPublishedAtRange[ Op.lte ] = options.originallyPublishedEndDate |
1239 | 1246 | ||
1240 | whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange }) | 1247 | whereAnd.push({ originallyPublishedAt: originallyPublishedAtRange }) |
1241 | } | 1248 | } |
@@ -1243,8 +1250,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1243 | if (options.durationMin || options.durationMax) { | 1250 | if (options.durationMin || options.durationMax) { |
1244 | const durationRange = {} | 1251 | const durationRange = {} |
1245 | 1252 | ||
1246 | if (options.durationMin) durationRange[ Sequelize.Op.gte ] = options.durationMin | 1253 | if (options.durationMin) durationRange[ Op.gte ] = options.durationMin |
1247 | if (options.durationMax) durationRange[ Sequelize.Op.lte ] = options.durationMax | 1254 | if (options.durationMax) durationRange[ Op.lte ] = options.durationMax |
1248 | 1255 | ||
1249 | whereAnd.push({ duration: durationRange }) | 1256 | whereAnd.push({ duration: durationRange }) |
1250 | } | 1257 | } |
@@ -1256,7 +1263,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1256 | whereAnd.push( | 1263 | whereAnd.push( |
1257 | { | 1264 | { |
1258 | id: { | 1265 | id: { |
1259 | [ Sequelize.Op.in ]: Sequelize.literal( | 1266 | [ Op.in ]: Sequelize.literal( |
1260 | '(' + | 1267 | '(' + |
1261 | 'SELECT "video"."id" FROM "video" ' + | 1268 | 'SELECT "video"."id" FROM "video" ' + |
1262 | 'WHERE ' + | 1269 | 'WHERE ' + |
@@ -1282,7 +1289,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1282 | ) | 1289 | ) |
1283 | } | 1290 | } |
1284 | 1291 | ||
1285 | const query: IFindOptions<VideoModel> = { | 1292 | const query: FindOptions = { |
1286 | attributes: { | 1293 | attributes: { |
1287 | include: attributesInclude | 1294 | include: attributesInclude |
1288 | }, | 1295 | }, |
@@ -1290,7 +1297,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1290 | limit: options.count, | 1297 | limit: options.count, |
1291 | order: getVideoSort(options.sort), | 1298 | order: getVideoSort(options.sort), |
1292 | where: { | 1299 | where: { |
1293 | [ Sequelize.Op.and ]: whereAnd | 1300 | [ Op.and ]: whereAnd |
1294 | } | 1301 | } |
1295 | } | 1302 | } |
1296 | 1303 | ||
@@ -1312,7 +1319,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1312 | return VideoModel.getAvailableForApi(query, queryOptions) | 1319 | return VideoModel.getAvailableForApi(query, queryOptions) |
1313 | } | 1320 | } |
1314 | 1321 | ||
1315 | static load (id: number | string, t?: Sequelize.Transaction) { | 1322 | static load (id: number | string, t?: Transaction) { |
1316 | const where = buildWhereIdOrUUID(id) | 1323 | const where = buildWhereIdOrUUID(id) |
1317 | const options = { | 1324 | const options = { |
1318 | where, | 1325 | where, |
@@ -1322,7 +1329,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1322 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) | 1329 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) |
1323 | } | 1330 | } |
1324 | 1331 | ||
1325 | static loadWithRights (id: number | string, t?: Sequelize.Transaction) { | 1332 | static loadWithRights (id: number | string, t?: Transaction) { |
1326 | const where = buildWhereIdOrUUID(id) | 1333 | const where = buildWhereIdOrUUID(id) |
1327 | const options = { | 1334 | const options = { |
1328 | where, | 1335 | where, |
@@ -1336,7 +1343,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1336 | ]).findOne(options) | 1343 | ]).findOne(options) |
1337 | } | 1344 | } |
1338 | 1345 | ||
1339 | static loadOnlyId (id: number | string, t?: Sequelize.Transaction) { | 1346 | static loadOnlyId (id: number | string, t?: Transaction) { |
1340 | const where = buildWhereIdOrUUID(id) | 1347 | const where = buildWhereIdOrUUID(id) |
1341 | 1348 | ||
1342 | const options = { | 1349 | const options = { |
@@ -1348,7 +1355,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1348 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) | 1355 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) |
1349 | } | 1356 | } |
1350 | 1357 | ||
1351 | static loadWithFiles (id: number, t?: Sequelize.Transaction, logging?: boolean) { | 1358 | static loadWithFiles (id: number, t?: Transaction, logging?: boolean) { |
1352 | return VideoModel.scope([ | 1359 | return VideoModel.scope([ |
1353 | ScopeNames.WITH_FILES, | 1360 | ScopeNames.WITH_FILES, |
1354 | ScopeNames.WITH_STREAMING_PLAYLISTS, | 1361 | ScopeNames.WITH_STREAMING_PLAYLISTS, |
@@ -1366,8 +1373,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1366 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) | 1373 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(options) |
1367 | } | 1374 | } |
1368 | 1375 | ||
1369 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { | 1376 | static loadByUrl (url: string, transaction?: Transaction) { |
1370 | const query: IFindOptions<VideoModel> = { | 1377 | const query: FindOptions = { |
1371 | where: { | 1378 | where: { |
1372 | url | 1379 | url |
1373 | }, | 1380 | }, |
@@ -1377,8 +1384,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1377 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) | 1384 | return VideoModel.scope(ScopeNames.WITH_THUMBNAILS).findOne(query) |
1378 | } | 1385 | } |
1379 | 1386 | ||
1380 | static loadByUrlAndPopulateAccount (url: string, transaction?: Sequelize.Transaction) { | 1387 | static loadByUrlAndPopulateAccount (url: string, transaction?: Transaction) { |
1381 | const query: IFindOptions<VideoModel> = { | 1388 | const query: FindOptions = { |
1382 | where: { | 1389 | where: { |
1383 | url | 1390 | url |
1384 | }, | 1391 | }, |
@@ -1393,11 +1400,11 @@ export class VideoModel extends Model<VideoModel> { | |||
1393 | ]).findOne(query) | 1400 | ]).findOne(query) |
1394 | } | 1401 | } |
1395 | 1402 | ||
1396 | static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Sequelize.Transaction, userId?: number) { | 1403 | static loadAndPopulateAccountAndServerAndTags (id: number | string, t?: Transaction, userId?: number) { |
1397 | const where = buildWhereIdOrUUID(id) | 1404 | const where = buildWhereIdOrUUID(id) |
1398 | 1405 | ||
1399 | const options = { | 1406 | const options = { |
1400 | order: [ [ 'Tags', 'name', 'ASC' ] ], | 1407 | order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings |
1401 | where, | 1408 | where, |
1402 | transaction: t | 1409 | transaction: t |
1403 | } | 1410 | } |
@@ -1421,11 +1428,11 @@ export class VideoModel extends Model<VideoModel> { | |||
1421 | .findOne(options) | 1428 | .findOne(options) |
1422 | } | 1429 | } |
1423 | 1430 | ||
1424 | static loadForGetAPI (id: number | string, t?: Sequelize.Transaction, userId?: number) { | 1431 | static loadForGetAPI (id: number | string, t?: Transaction, userId?: number) { |
1425 | const where = buildWhereIdOrUUID(id) | 1432 | const where = buildWhereIdOrUUID(id) |
1426 | 1433 | ||
1427 | const options = { | 1434 | const options = { |
1428 | order: [ [ 'Tags', 'name', 'ASC' ] ], | 1435 | order: [ [ 'Tags', 'name', 'ASC' ] ] as any, // FIXME: sequelize typings |
1429 | where, | 1436 | where, |
1430 | transaction: t | 1437 | transaction: t |
1431 | } | 1438 | } |
@@ -1489,7 +1496,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1489 | 'LIMIT 1' | 1496 | 'LIMIT 1' |
1490 | 1497 | ||
1491 | const options = { | 1498 | const options = { |
1492 | type: Sequelize.QueryTypes.SELECT, | 1499 | type: QueryTypes.SELECT, |
1493 | bind: { followerActorId, videoId }, | 1500 | bind: { followerActorId, videoId }, |
1494 | raw: true | 1501 | raw: true |
1495 | } | 1502 | } |
@@ -1509,14 +1516,14 @@ export class VideoModel extends Model<VideoModel> { | |||
1509 | includeLocalVideos: true | 1516 | includeLocalVideos: true |
1510 | } | 1517 | } |
1511 | 1518 | ||
1512 | const query: IFindOptions<VideoModel> = { | 1519 | const query: FindOptions = { |
1513 | attributes: [ field ], | 1520 | attributes: [ field ], |
1514 | limit: count, | 1521 | limit: count, |
1515 | group: field, | 1522 | group: field, |
1516 | having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), { | 1523 | having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col(field)), { |
1517 | [ Sequelize.Op.gte ]: threshold | 1524 | [ Op.gte ]: threshold |
1518 | }) as any, // FIXME: typings | 1525 | }) as any, // FIXME: typings |
1519 | order: [ this.sequelize.random() ] | 1526 | order: [ (this.sequelize as any).random() ] |
1520 | } | 1527 | } |
1521 | 1528 | ||
1522 | return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] }) | 1529 | return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST_IDS, scopeOptions ] }) |
@@ -1532,7 +1539,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1532 | required: false, | 1539 | required: false, |
1533 | where: { | 1540 | where: { |
1534 | startDate: { | 1541 | startDate: { |
1535 | [ Sequelize.Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays) | 1542 | [ Op.gte ]: new Date(new Date().getTime() - (24 * 3600 * 1000) * trendingDays) |
1536 | } | 1543 | } |
1537 | } | 1544 | } |
1538 | } | 1545 | } |
@@ -1549,11 +1556,11 @@ export class VideoModel extends Model<VideoModel> { | |||
1549 | } | 1556 | } |
1550 | 1557 | ||
1551 | private static async getAvailableForApi ( | 1558 | private static async getAvailableForApi ( |
1552 | query: IFindOptions<VideoModel>, | 1559 | query: FindOptions, |
1553 | options: AvailableForListIDsOptions, | 1560 | options: AvailableForListIDsOptions, |
1554 | countVideos = true | 1561 | countVideos = true |
1555 | ) { | 1562 | ) { |
1556 | const idsScope = { | 1563 | const idsScope: ScopeOptions = { |
1557 | method: [ | 1564 | method: [ |
1558 | ScopeNames.AVAILABLE_FOR_LIST_IDS, options | 1565 | ScopeNames.AVAILABLE_FOR_LIST_IDS, options |
1559 | ] | 1566 | ] |
@@ -1561,8 +1568,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1561 | 1568 | ||
1562 | // Remove trending sort on count, because it uses a group by | 1569 | // Remove trending sort on count, because it uses a group by |
1563 | const countOptions = Object.assign({}, options, { trendingDays: undefined }) | 1570 | const countOptions = Object.assign({}, options, { trendingDays: undefined }) |
1564 | const countQuery = Object.assign({}, query, { attributes: undefined, group: undefined }) | 1571 | const countQuery: CountOptions = Object.assign({}, query, { attributes: undefined, group: undefined }) |
1565 | const countScope = { | 1572 | const countScope: ScopeOptions = { |
1566 | method: [ | 1573 | method: [ |
1567 | ScopeNames.AVAILABLE_FOR_LIST_IDS, countOptions | 1574 | ScopeNames.AVAILABLE_FOR_LIST_IDS, countOptions |
1568 | ] | 1575 | ] |
@@ -1576,7 +1583,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1576 | 1583 | ||
1577 | if (ids.length === 0) return { data: [], total: count } | 1584 | if (ids.length === 0) return { data: [], total: count } |
1578 | 1585 | ||
1579 | const secondQuery: IFindOptions<VideoModel> = { | 1586 | const secondQuery: FindOptions = { |
1580 | offset: 0, | 1587 | offset: 0, |
1581 | limit: query.limit, | 1588 | limit: query.limit, |
1582 | attributes: query.attributes, | 1589 | attributes: query.attributes, |