diff options
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/account/account-blocklist.ts | 2 | ||||
-rw-r--r-- | server/models/account/user-notification.ts | 2 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 37 | ||||
-rw-r--r-- | server/models/redundancy/video-redundancy.ts | 186 | ||||
-rw-r--r-- | server/models/server/server-blocklist.ts | 2 | ||||
-rw-r--r-- | server/models/video/video-caption.ts | 2 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 23 | ||||
-rw-r--r-- | server/models/video/video-playlist.ts | 2 | ||||
-rw-r--r-- | server/models/video/video.ts | 153 |
9 files changed, 293 insertions, 116 deletions
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index 6ebe32556..e2f66d733 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -80,7 +80,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> { | |||
80 | attributes: [ 'accountId', 'id' ], | 80 | attributes: [ 'accountId', 'id' ], |
81 | where: { | 81 | where: { |
82 | accountId: { | 82 | accountId: { |
83 | [Op.in]: accountIds // FIXME: sequelize ANY seems broken | 83 | [Op.in]: accountIds |
84 | }, | 84 | }, |
85 | targetAccountId | 85 | targetAccountId |
86 | }, | 86 | }, |
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index a05f30175..f649b8f95 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts | |||
@@ -363,7 +363,7 @@ export class UserNotificationModel extends Model<UserNotificationModel> { | |||
363 | where: { | 363 | where: { |
364 | userId, | 364 | userId, |
365 | id: { | 365 | id: { |
366 | [Op.in]: notificationIds // FIXME: sequelize ANY seems broken | 366 | [Op.in]: notificationIds |
367 | } | 367 | } |
368 | } | 368 | } |
369 | } | 369 | } |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 007647ced..d651a281a 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -43,7 +43,7 @@ import { | |||
43 | MActorFull, | 43 | MActorFull, |
44 | MActorHost, | 44 | MActorHost, |
45 | MActorServer, | 45 | MActorServer, |
46 | MActorSummaryFormattable, | 46 | MActorSummaryFormattable, MActorUrl, |
47 | MActorWithInboxes | 47 | MActorWithInboxes |
48 | } from '../../typings/models' | 48 | } from '../../typings/models' |
49 | import * as Bluebird from 'bluebird' | 49 | import * as Bluebird from 'bluebird' |
@@ -276,7 +276,8 @@ export class ActorModel extends Model<ActorModel> { | |||
276 | }) | 276 | }) |
277 | VideoChannel: VideoChannelModel | 277 | VideoChannel: VideoChannelModel |
278 | 278 | ||
279 | private static cache: { [ id: string ]: any } = {} | 279 | private static localNameCache: { [ id: string ]: any } = {} |
280 | private static localUrlCache: { [ id: string ]: any } = {} | ||
280 | 281 | ||
281 | static load (id: number): Bluebird<MActor> { | 282 | static load (id: number): Bluebird<MActor> { |
282 | return ActorModel.unscoped().findByPk(id) | 283 | return ActorModel.unscoped().findByPk(id) |
@@ -345,8 +346,8 @@ export class ActorModel extends Model<ActorModel> { | |||
345 | 346 | ||
346 | static loadLocalByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorFull> { | 347 | static loadLocalByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorFull> { |
347 | // The server actor never change, so we can easily cache it | 348 | // The server actor never change, so we can easily cache it |
348 | if (preferredUsername === SERVER_ACTOR_NAME && ActorModel.cache[preferredUsername]) { | 349 | if (preferredUsername === SERVER_ACTOR_NAME && ActorModel.localNameCache[preferredUsername]) { |
349 | return Bluebird.resolve(ActorModel.cache[preferredUsername]) | 350 | return Bluebird.resolve(ActorModel.localNameCache[preferredUsername]) |
350 | } | 351 | } |
351 | 352 | ||
352 | const query = { | 353 | const query = { |
@@ -361,7 +362,33 @@ export class ActorModel extends Model<ActorModel> { | |||
361 | .findOne(query) | 362 | .findOne(query) |
362 | .then(actor => { | 363 | .then(actor => { |
363 | if (preferredUsername === SERVER_ACTOR_NAME) { | 364 | if (preferredUsername === SERVER_ACTOR_NAME) { |
364 | ActorModel.cache[ preferredUsername ] = actor | 365 | ActorModel.localNameCache[ preferredUsername ] = actor |
366 | } | ||
367 | |||
368 | return actor | ||
369 | }) | ||
370 | } | ||
371 | |||
372 | static loadLocalUrlByName (preferredUsername: string, transaction?: Transaction): Bluebird<MActorUrl> { | ||
373 | // The server actor never change, so we can easily cache it | ||
374 | if (preferredUsername === SERVER_ACTOR_NAME && ActorModel.localUrlCache[preferredUsername]) { | ||
375 | return Bluebird.resolve(ActorModel.localUrlCache[preferredUsername]) | ||
376 | } | ||
377 | |||
378 | const query = { | ||
379 | attributes: [ 'url' ], | ||
380 | where: { | ||
381 | preferredUsername, | ||
382 | serverId: null | ||
383 | }, | ||
384 | transaction | ||
385 | } | ||
386 | |||
387 | return ActorModel.unscoped() | ||
388 | .findOne(query) | ||
389 | .then(actor => { | ||
390 | if (preferredUsername === SERVER_ACTOR_NAME) { | ||
391 | ActorModel.localUrlCache[ preferredUsername ] = actor | ||
365 | } | 392 | } |
366 | 393 | ||
367 | return actor | 394 | return actor |
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index 8c9a7eabf..4e66d72e3 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -13,13 +13,13 @@ import { | |||
13 | UpdatedAt | 13 | UpdatedAt |
14 | } from 'sequelize-typescript' | 14 | } from 'sequelize-typescript' |
15 | import { ActorModel } from '../activitypub/actor' | 15 | import { ActorModel } from '../activitypub/actor' |
16 | import { getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' | 16 | import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' |
17 | import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 17 | import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
18 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' | 18 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' |
19 | import { VideoFileModel } from '../video/video-file' | 19 | import { VideoFileModel } from '../video/video-file' |
20 | import { getServerActor } from '../../helpers/utils' | 20 | import { getServerActor } from '../../helpers/utils' |
21 | import { VideoModel } from '../video/video' | 21 | import { VideoModel } from '../video/video' |
22 | import { VideoRedundancyStrategy } from '../../../shared/models/redundancy' | 22 | import { VideoRedundancyStrategy, VideoRedundancyStrategyWithManual } from '../../../shared/models/redundancy' |
23 | import { logger } from '../../helpers/logger' | 23 | import { logger } from '../../helpers/logger' |
24 | import { CacheFileObject, VideoPrivacy } from '../../../shared' | 24 | import { CacheFileObject, VideoPrivacy } from '../../../shared' |
25 | import { VideoChannelModel } from '../video/video-channel' | 25 | import { VideoChannelModel } from '../video/video-channel' |
@@ -27,10 +27,16 @@ import { ServerModel } from '../server/server' | |||
27 | import { sample } from 'lodash' | 27 | import { sample } from 'lodash' |
28 | import { isTestInstance } from '../../helpers/core-utils' | 28 | import { isTestInstance } from '../../helpers/core-utils' |
29 | import * as Bluebird from 'bluebird' | 29 | import * as Bluebird from 'bluebird' |
30 | import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize' | 30 | import { col, FindOptions, fn, literal, Op, Transaction, WhereOptions } from 'sequelize' |
31 | import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' | 31 | import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist' |
32 | import { CONFIG } from '../../initializers/config' | 32 | import { CONFIG } from '../../initializers/config' |
33 | import { MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/typings/models' | 33 | import { MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/typings/models' |
34 | import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model' | ||
35 | import { | ||
36 | FileRedundancyInformation, | ||
37 | StreamingPlaylistRedundancyInformation, | ||
38 | VideoRedundancy | ||
39 | } from '@shared/models/redundancy/video-redundancy.model' | ||
34 | 40 | ||
35 | export enum ScopeNames { | 41 | export enum ScopeNames { |
36 | WITH_VIDEO = 'WITH_VIDEO' | 42 | WITH_VIDEO = 'WITH_VIDEO' |
@@ -86,7 +92,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
86 | @UpdatedAt | 92 | @UpdatedAt |
87 | updatedAt: Date | 93 | updatedAt: Date |
88 | 94 | ||
89 | @AllowNull(false) | 95 | @AllowNull(true) |
90 | @Column | 96 | @Column |
91 | expiresOn: Date | 97 | expiresOn: Date |
92 | 98 | ||
@@ -193,6 +199,15 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
193 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) | 199 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) |
194 | } | 200 | } |
195 | 201 | ||
202 | static loadByIdWithVideo (id: number, transaction?: Transaction): Bluebird<MVideoRedundancyVideo> { | ||
203 | const query = { | ||
204 | where: { id }, | ||
205 | transaction | ||
206 | } | ||
207 | |||
208 | return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) | ||
209 | } | ||
210 | |||
196 | static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoRedundancy> { | 211 | static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoRedundancy> { |
197 | const query = { | 212 | const query = { |
198 | where: { | 213 | where: { |
@@ -394,7 +409,8 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
394 | [Op.ne]: actor.id | 409 | [Op.ne]: actor.id |
395 | }, | 410 | }, |
396 | expiresOn: { | 411 | expiresOn: { |
397 | [ Op.lt ]: new Date() | 412 | [ Op.lt ]: new Date(), |
413 | [ Op.ne ]: null | ||
398 | } | 414 | } |
399 | } | 415 | } |
400 | } | 416 | } |
@@ -447,7 +463,112 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
447 | return VideoRedundancyModel.findAll(query) | 463 | return VideoRedundancyModel.findAll(query) |
448 | } | 464 | } |
449 | 465 | ||
450 | static async getStats (strategy: VideoRedundancyStrategy) { | 466 | static listForApi (options: { |
467 | start: number, | ||
468 | count: number, | ||
469 | sort: string, | ||
470 | target: VideoRedundanciesTarget, | ||
471 | strategy?: string | ||
472 | }) { | ||
473 | const { start, count, sort, target, strategy } = options | ||
474 | let redundancyWhere: WhereOptions = {} | ||
475 | let videosWhere: WhereOptions = {} | ||
476 | let redundancySqlSuffix = '' | ||
477 | |||
478 | if (target === 'my-videos') { | ||
479 | Object.assign(videosWhere, { remote: false }) | ||
480 | } else if (target === 'remote-videos') { | ||
481 | Object.assign(videosWhere, { remote: true }) | ||
482 | Object.assign(redundancyWhere, { strategy: { [Op.ne]: null } }) | ||
483 | redundancySqlSuffix = ' AND "videoRedundancy"."strategy" IS NOT NULL' | ||
484 | } | ||
485 | |||
486 | if (strategy) { | ||
487 | Object.assign(redundancyWhere, { strategy: strategy }) | ||
488 | } | ||
489 | |||
490 | const videoFilterWhere = { | ||
491 | [Op.and]: [ | ||
492 | { | ||
493 | [ Op.or ]: [ | ||
494 | { | ||
495 | id: { | ||
496 | [ Op.in ]: literal( | ||
497 | '(' + | ||
498 | 'SELECT "videoId" FROM "videoFile" ' + | ||
499 | 'INNER JOIN "videoRedundancy" ON "videoRedundancy"."videoFileId" = "videoFile".id' + | ||
500 | redundancySqlSuffix + | ||
501 | ')' | ||
502 | ) | ||
503 | } | ||
504 | }, | ||
505 | { | ||
506 | id: { | ||
507 | [ Op.in ]: literal( | ||
508 | '(' + | ||
509 | 'select "videoId" FROM "videoStreamingPlaylist" ' + | ||
510 | 'INNER JOIN "videoRedundancy" ON "videoRedundancy"."videoStreamingPlaylistId" = "videoStreamingPlaylist".id' + | ||
511 | redundancySqlSuffix + | ||
512 | ')' | ||
513 | ) | ||
514 | } | ||
515 | } | ||
516 | ] | ||
517 | }, | ||
518 | |||
519 | videosWhere | ||
520 | ] | ||
521 | } | ||
522 | |||
523 | // /!\ On video model /!\ | ||
524 | const findOptions = { | ||
525 | offset: start, | ||
526 | limit: count, | ||
527 | order: getSort(sort), | ||
528 | include: [ | ||
529 | { | ||
530 | required: false, | ||
531 | model: VideoFileModel.unscoped(), | ||
532 | include: [ | ||
533 | { | ||
534 | model: VideoRedundancyModel.unscoped(), | ||
535 | required: false, | ||
536 | where: redundancyWhere | ||
537 | } | ||
538 | ] | ||
539 | }, | ||
540 | { | ||
541 | required: false, | ||
542 | model: VideoStreamingPlaylistModel.unscoped(), | ||
543 | include: [ | ||
544 | { | ||
545 | model: VideoRedundancyModel.unscoped(), | ||
546 | required: false, | ||
547 | where: redundancyWhere | ||
548 | }, | ||
549 | { | ||
550 | model: VideoFileModel.unscoped(), | ||
551 | required: false | ||
552 | } | ||
553 | ] | ||
554 | } | ||
555 | ], | ||
556 | where: videoFilterWhere | ||
557 | } | ||
558 | |||
559 | // /!\ On video model /!\ | ||
560 | const countOptions = { | ||
561 | where: videoFilterWhere | ||
562 | } | ||
563 | |||
564 | return Promise.all([ | ||
565 | VideoModel.findAll(findOptions), | ||
566 | |||
567 | VideoModel.count(countOptions) | ||
568 | ]).then(([ data, total ]) => ({ total, data })) | ||
569 | } | ||
570 | |||
571 | static async getStats (strategy: VideoRedundancyStrategyWithManual) { | ||
451 | const actor = await getServerActor() | 572 | const actor = await getServerActor() |
452 | 573 | ||
453 | const query: FindOptions = { | 574 | const query: FindOptions = { |
@@ -478,6 +599,53 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
478 | })) | 599 | })) |
479 | } | 600 | } |
480 | 601 | ||
602 | static toFormattedJSONStatic (video: MVideoForRedundancyAPI): VideoRedundancy { | ||
603 | let filesRedundancies: FileRedundancyInformation[] = [] | ||
604 | let streamingPlaylistsRedundancies: StreamingPlaylistRedundancyInformation[] = [] | ||
605 | |||
606 | for (const file of video.VideoFiles) { | ||
607 | for (const redundancy of file.RedundancyVideos) { | ||
608 | filesRedundancies.push({ | ||
609 | id: redundancy.id, | ||
610 | fileUrl: redundancy.fileUrl, | ||
611 | strategy: redundancy.strategy, | ||
612 | createdAt: redundancy.createdAt, | ||
613 | updatedAt: redundancy.updatedAt, | ||
614 | expiresOn: redundancy.expiresOn, | ||
615 | size: file.size | ||
616 | }) | ||
617 | } | ||
618 | } | ||
619 | |||
620 | for (const playlist of video.VideoStreamingPlaylists) { | ||
621 | const size = playlist.VideoFiles.reduce((a, b) => a + b.size, 0) | ||
622 | |||
623 | for (const redundancy of playlist.RedundancyVideos) { | ||
624 | streamingPlaylistsRedundancies.push({ | ||
625 | id: redundancy.id, | ||
626 | fileUrl: redundancy.fileUrl, | ||
627 | strategy: redundancy.strategy, | ||
628 | createdAt: redundancy.createdAt, | ||
629 | updatedAt: redundancy.updatedAt, | ||
630 | expiresOn: redundancy.expiresOn, | ||
631 | size | ||
632 | }) | ||
633 | } | ||
634 | } | ||
635 | |||
636 | return { | ||
637 | id: video.id, | ||
638 | name: video.name, | ||
639 | url: video.url, | ||
640 | uuid: video.uuid, | ||
641 | |||
642 | redundancies: { | ||
643 | files: filesRedundancies, | ||
644 | streamingPlaylists: streamingPlaylistsRedundancies | ||
645 | } | ||
646 | } | ||
647 | } | ||
648 | |||
481 | getVideo () { | 649 | getVideo () { |
482 | if (this.VideoFile) return this.VideoFile.Video | 650 | if (this.VideoFile) return this.VideoFile.Video |
483 | 651 | ||
@@ -494,7 +662,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
494 | id: this.url, | 662 | id: this.url, |
495 | type: 'CacheFile' as 'CacheFile', | 663 | type: 'CacheFile' as 'CacheFile', |
496 | object: this.VideoStreamingPlaylist.Video.url, | 664 | object: this.VideoStreamingPlaylist.Video.url, |
497 | expires: this.expiresOn.toISOString(), | 665 | expires: this.expiresOn ? this.expiresOn.toISOString() : null, |
498 | url: { | 666 | url: { |
499 | type: 'Link', | 667 | type: 'Link', |
500 | mediaType: 'application/x-mpegURL', | 668 | mediaType: 'application/x-mpegURL', |
@@ -507,7 +675,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> { | |||
507 | id: this.url, | 675 | id: this.url, |
508 | type: 'CacheFile' as 'CacheFile', | 676 | type: 'CacheFile' as 'CacheFile', |
509 | object: this.VideoFile.Video.url, | 677 | object: this.VideoFile.Video.url, |
510 | expires: this.expiresOn.toISOString(), | 678 | expires: this.expiresOn ? this.expiresOn.toISOString() : null, |
511 | url: { | 679 | url: { |
512 | type: 'Link', | 680 | type: 'Link', |
513 | mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[ this.VideoFile.extname ] as any, | 681 | mediaType: MIMETYPES.VIDEO.EXT_MIMETYPE[ this.VideoFile.extname ] as any, |
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts index b88df4fd5..883ae47ab 100644 --- a/server/models/server/server-blocklist.ts +++ b/server/models/server/server-blocklist.ts | |||
@@ -81,7 +81,7 @@ export class ServerBlocklistModel extends Model<ServerBlocklistModel> { | |||
81 | attributes: [ 'accountId', 'id' ], | 81 | attributes: [ 'accountId', 'id' ], |
82 | where: { | 82 | where: { |
83 | accountId: { | 83 | accountId: { |
84 | [Op.in]: accountIds // FIXME: sequelize ANY seems broken | 84 | [Op.in]: accountIds |
85 | }, | 85 | }, |
86 | targetServerId | 86 | targetServerId |
87 | }, | 87 | }, |
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index eeb2a4afd..6335d44e4 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts | |||
@@ -120,7 +120,7 @@ export class VideoCaptionModel extends Model<VideoCaptionModel> { | |||
120 | language | 120 | language |
121 | } | 121 | } |
122 | 122 | ||
123 | return (VideoCaptionModel.upsert<VideoCaptionModel>(values, { transaction, returning: true }) as any) // FIXME: typings | 123 | return VideoCaptionModel.upsert(values, { transaction, returning: true }) |
124 | .then(([ caption ]) => caption) | 124 | .then(([ caption ]) => caption) |
125 | } | 125 | } |
126 | 126 | ||
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index e10adcb3a..4e98e7ba3 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -43,18 +43,6 @@ import { | |||
43 | MChannelSummaryFormattable | 43 | MChannelSummaryFormattable |
44 | } from '../../typings/models/video' | 44 | } from '../../typings/models/video' |
45 | 45 | ||
46 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | ||
47 | const indexes: ModelIndexesOptions[] = [ | ||
48 | buildTrigramSearchIndex('video_channel_name_trigram', 'name'), | ||
49 | |||
50 | { | ||
51 | fields: [ 'accountId' ] | ||
52 | }, | ||
53 | { | ||
54 | fields: [ 'actorId' ] | ||
55 | } | ||
56 | ] | ||
57 | |||
58 | export enum ScopeNames { | 46 | export enum ScopeNames { |
59 | FOR_API = 'FOR_API', | 47 | FOR_API = 'FOR_API', |
60 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 48 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
@@ -176,7 +164,16 @@ export type SummaryOptions = { | |||
176 | })) | 164 | })) |
177 | @Table({ | 165 | @Table({ |
178 | tableName: 'videoChannel', | 166 | tableName: 'videoChannel', |
179 | indexes | 167 | indexes: [ |
168 | buildTrigramSearchIndex('video_channel_name_trigram', 'name'), | ||
169 | |||
170 | { | ||
171 | fields: [ 'accountId' ] | ||
172 | }, | ||
173 | { | ||
174 | fields: [ 'actorId' ] | ||
175 | } | ||
176 | ] | ||
180 | }) | 177 | }) |
181 | export class VideoChannelModel extends Model<VideoChannelModel> { | 178 | export class VideoChannelModel extends Model<VideoChannelModel> { |
182 | 179 | ||
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index bcdda36e5..ba1fc23ea 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -369,7 +369,7 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> { | |||
369 | model: VideoPlaylistElementModel.unscoped(), | 369 | model: VideoPlaylistElementModel.unscoped(), |
370 | where: { | 370 | where: { |
371 | videoId: { | 371 | videoId: { |
372 | [Op.in]: videoIds // FIXME: sequelize ANY seems broken | 372 | [Op.in]: videoIds |
373 | } | 373 | } |
374 | }, | 374 | }, |
375 | required: true | 375 | required: true |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index eacffe186..20e1f1c4a 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1,18 +1,7 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { maxBy, minBy } from 'lodash' | 2 | import { maxBy, minBy } from 'lodash' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import { | 4 | import { CountOptions, FindOptions, IncludeOptions, Op, QueryTypes, ScopeOptions, Sequelize, Transaction, WhereOptions } from 'sequelize' |
5 | CountOptions, | ||
6 | FindOptions, | ||
7 | IncludeOptions, | ||
8 | ModelIndexesOptions, | ||
9 | Op, | ||
10 | QueryTypes, | ||
11 | ScopeOptions, | ||
12 | Sequelize, | ||
13 | Transaction, | ||
14 | WhereOptions | ||
15 | } from 'sequelize' | ||
16 | import { | 5 | import { |
17 | AllowNull, | 6 | AllowNull, |
18 | BeforeDestroy, | 7 | BeforeDestroy, |
@@ -136,8 +125,7 @@ import { | |||
136 | MVideoThumbnailBlacklist, | 125 | MVideoThumbnailBlacklist, |
137 | MVideoWithAllFiles, | 126 | MVideoWithAllFiles, |
138 | MVideoWithFile, | 127 | MVideoWithFile, |
139 | MVideoWithRights, | 128 | MVideoWithRights |
140 | MStreamingPlaylistFiles | ||
141 | } from '../../typings/models' | 129 | } from '../../typings/models' |
142 | import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../typings/models/video/video-file' | 130 | import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../typings/models/video/video-file' |
143 | import { MThumbnail } from '../../typings/models/video/thumbnail' | 131 | import { MThumbnail } from '../../typings/models/video/thumbnail' |
@@ -145,74 +133,6 @@ import { VideoFile } from '@shared/models/videos/video-file.model' | |||
145 | import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | 133 | import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths' |
146 | import validator from 'validator' | 134 | import validator from 'validator' |
147 | 135 | ||
148 | // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation | ||
149 | const indexes: (ModelIndexesOptions & { where?: WhereOptions })[] = [ | ||
150 | buildTrigramSearchIndex('video_name_trigram', 'name'), | ||
151 | |||
152 | { fields: [ 'createdAt' ] }, | ||
153 | { | ||
154 | fields: [ | ||
155 | { name: 'publishedAt', order: 'DESC' }, | ||
156 | { name: 'id', order: 'ASC' } | ||
157 | ] | ||
158 | }, | ||
159 | { fields: [ 'duration' ] }, | ||
160 | { fields: [ 'views' ] }, | ||
161 | { fields: [ 'channelId' ] }, | ||
162 | { | ||
163 | fields: [ 'originallyPublishedAt' ], | ||
164 | where: { | ||
165 | originallyPublishedAt: { | ||
166 | [Op.ne]: null | ||
167 | } | ||
168 | } | ||
169 | }, | ||
170 | { | ||
171 | fields: [ 'category' ], // We don't care videos with an unknown category | ||
172 | where: { | ||
173 | category: { | ||
174 | [Op.ne]: null | ||
175 | } | ||
176 | } | ||
177 | }, | ||
178 | { | ||
179 | fields: [ 'licence' ], // We don't care videos with an unknown licence | ||
180 | where: { | ||
181 | licence: { | ||
182 | [Op.ne]: null | ||
183 | } | ||
184 | } | ||
185 | }, | ||
186 | { | ||
187 | fields: [ 'language' ], // We don't care videos with an unknown language | ||
188 | where: { | ||
189 | language: { | ||
190 | [Op.ne]: null | ||
191 | } | ||
192 | } | ||
193 | }, | ||
194 | { | ||
195 | fields: [ 'nsfw' ], // Most of the videos are not NSFW | ||
196 | where: { | ||
197 | nsfw: true | ||
198 | } | ||
199 | }, | ||
200 | { | ||
201 | fields: [ 'remote' ], // Only index local videos | ||
202 | where: { | ||
203 | remote: false | ||
204 | } | ||
205 | }, | ||
206 | { | ||
207 | fields: [ 'uuid' ], | ||
208 | unique: true | ||
209 | }, | ||
210 | { | ||
211 | fields: [ 'url' ], | ||
212 | unique: true | ||
213 | } | ||
214 | ] | ||
215 | |||
216 | export enum ScopeNames { | 136 | export enum ScopeNames { |
217 | AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS', | 137 | AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS', |
218 | FOR_API = 'FOR_API', | 138 | FOR_API = 'FOR_API', |
@@ -292,7 +212,7 @@ export type AvailableForListIDsOptions = { | |||
292 | if (options.ids) { | 212 | if (options.ids) { |
293 | query.where = { | 213 | query.where = { |
294 | id: { | 214 | id: { |
295 | [ Op.in ]: options.ids // FIXME: sequelize ANY seems broken | 215 | [ Op.in ]: options.ids |
296 | } | 216 | } |
297 | } | 217 | } |
298 | } | 218 | } |
@@ -760,7 +680,72 @@ export type AvailableForListIDsOptions = { | |||
760 | })) | 680 | })) |
761 | @Table({ | 681 | @Table({ |
762 | tableName: 'video', | 682 | tableName: 'video', |
763 | indexes | 683 | indexes: [ |
684 | buildTrigramSearchIndex('video_name_trigram', 'name'), | ||
685 | |||
686 | { fields: [ 'createdAt' ] }, | ||
687 | { | ||
688 | fields: [ | ||
689 | { name: 'publishedAt', order: 'DESC' }, | ||
690 | { name: 'id', order: 'ASC' } | ||
691 | ] | ||
692 | }, | ||
693 | { fields: [ 'duration' ] }, | ||
694 | { fields: [ 'views' ] }, | ||
695 | { fields: [ 'channelId' ] }, | ||
696 | { | ||
697 | fields: [ 'originallyPublishedAt' ], | ||
698 | where: { | ||
699 | originallyPublishedAt: { | ||
700 | [Op.ne]: null | ||
701 | } | ||
702 | } | ||
703 | }, | ||
704 | { | ||
705 | fields: [ 'category' ], // We don't care videos with an unknown category | ||
706 | where: { | ||
707 | category: { | ||
708 | [Op.ne]: null | ||
709 | } | ||
710 | } | ||
711 | }, | ||
712 | { | ||
713 | fields: [ 'licence' ], // We don't care videos with an unknown licence | ||
714 | where: { | ||
715 | licence: { | ||
716 | [Op.ne]: null | ||
717 | } | ||
718 | } | ||
719 | }, | ||
720 | { | ||
721 | fields: [ 'language' ], // We don't care videos with an unknown language | ||
722 | where: { | ||
723 | language: { | ||
724 | [Op.ne]: null | ||
725 | } | ||
726 | } | ||
727 | }, | ||
728 | { | ||
729 | fields: [ 'nsfw' ], // Most of the videos are not NSFW | ||
730 | where: { | ||
731 | nsfw: true | ||
732 | } | ||
733 | }, | ||
734 | { | ||
735 | fields: [ 'remote' ], // Only index local videos | ||
736 | where: { | ||
737 | remote: false | ||
738 | } | ||
739 | }, | ||
740 | { | ||
741 | fields: [ 'uuid' ], | ||
742 | unique: true | ||
743 | }, | ||
744 | { | ||
745 | fields: [ 'url' ], | ||
746 | unique: true | ||
747 | } | ||
748 | ] | ||
764 | }) | 749 | }) |
765 | export class VideoModel extends Model<VideoModel> { | 750 | export class VideoModel extends Model<VideoModel> { |
766 | 751 | ||