aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/video.ts')
-rw-r--r--server/models/video/video.ts243
1 files changed, 112 insertions, 131 deletions
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 9e26f9bbe..1f940a50d 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -21,12 +21,14 @@ import {
21 IsUUID, 21 IsUUID,
22 Min, 22 Min,
23 Model, 23 Model,
24 Scopes,
24 Table, 25 Table,
25 UpdatedAt 26 UpdatedAt
26} from 'sequelize-typescript' 27} from 'sequelize-typescript'
27import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions' 28import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions'
28import { VideoPrivacy, VideoResolution } from '../../../shared' 29import { VideoPrivacy, VideoResolution } from '../../../shared'
29import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 30import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
31import { Video, VideoDetails } from '../../../shared/models/videos'
30import { 32import {
31 activityPubCollection, 33 activityPubCollection,
32 createTorrentPromise, 34 createTorrentPromise,
@@ -76,6 +78,79 @@ import { VideoFileModel } from './video-file'
76import { VideoShareModel } from './video-share' 78import { VideoShareModel } from './video-share'
77import { VideoTagModel } from './video-tag' 79import { VideoTagModel } from './video-tag'
78 80
81enum ScopeNames {
82 NOT_IN_BLACKLIST = 'NOT_IN_BLACKLIST',
83 PUBLIC = 'PUBLIC',
84 WITH_ACCOUNT = 'WITH_ACCOUNT',
85 WITH_TAGS = 'WITH_TAGS',
86 WITH_FILES = 'WITH_FILES',
87 WITH_SHARES = 'WITH_SHARES',
88 WITH_RATES = 'WITH_RATES'
89}
90
91@Scopes({
92 [ScopeNames.NOT_IN_BLACKLIST]: {
93 where: {
94 id: {
95 [Sequelize.Op.notIn]: Sequelize.literal(
96 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
97 )
98 }
99 }
100 },
101 [ScopeNames.PUBLIC]: {
102 where: {
103 privacy: VideoPrivacy.PUBLIC
104 }
105 },
106 [ScopeNames.WITH_ACCOUNT]: {
107 include: [
108 {
109 model: () => VideoChannelModel,
110 required: true,
111 include: [
112 {
113 model: () => AccountModel,
114 required: true,
115 include: [
116 {
117 model: () => ServerModel,
118 required: false
119 }
120 ]
121 }
122 ]
123 }
124 ]
125 },
126 [ScopeNames.WITH_TAGS]: {
127 include: [ () => TagModel ]
128 },
129 [ScopeNames.WITH_FILES]: {
130 include: [
131 {
132 model: () => VideoFileModel,
133 required: true
134 }
135 ]
136 },
137 [ScopeNames.WITH_SHARES]: {
138 include: [
139 {
140 model: () => VideoShareModel,
141 include: [ () => AccountModel ]
142 }
143 ]
144 },
145 [ScopeNames.WITH_RATES]: {
146 include: [
147 {
148 model: () => AccountVideoRateModel,
149 include: [ () => AccountModel ]
150 }
151 ]
152 }
153})
79@Table({ 154@Table({
80 tableName: 'video', 155 tableName: 'video',
81 indexes: [ 156 indexes: [
@@ -273,11 +348,7 @@ export class VideoModel extends Model<VideoModel> {
273 } 348 }
274 349
275 static list () { 350 static list () {
276 const query = { 351 return VideoModel.scope(ScopeNames.WITH_FILES).findAll()
277 include: [ VideoFileModel ]
278 }
279
280 return VideoModel.findAll(query)
281 } 352 }
282 353
283 static listAllAndSharedByAccountForOutbox (accountId: number, start: number, count: number) { 354 static listAllAndSharedByAccountForOutbox (accountId: number, start: number, count: number) {
@@ -363,10 +434,9 @@ export class VideoModel extends Model<VideoModel> {
363 434
364 static listUserVideosForApi (userId: number, start: number, count: number, sort: string) { 435 static listUserVideosForApi (userId: number, start: number, count: number, sort: string) {
365 const query = { 436 const query = {
366 distinct: true,
367 offset: start, 437 offset: start,
368 limit: count, 438 limit: count,
369 order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ], 439 order: [ getSort(sort) ],
370 include: [ 440 include: [
371 { 441 {
372 model: VideoChannelModel, 442 model: VideoChannelModel,
@@ -380,8 +450,7 @@ export class VideoModel extends Model<VideoModel> {
380 required: true 450 required: true
381 } 451 }
382 ] 452 ]
383 }, 453 }
384 TagModel
385 ] 454 ]
386 } 455 }
387 456
@@ -395,74 +464,35 @@ export class VideoModel extends Model<VideoModel> {
395 464
396 static listForApi (start: number, count: number, sort: string) { 465 static listForApi (start: number, count: number, sort: string) {
397 const query = { 466 const query = {
398 distinct: true,
399 offset: start, 467 offset: start,
400 limit: count, 468 limit: count,
401 order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ], 469 order: [ getSort(sort) ]
402 include: [
403 {
404 model: VideoChannelModel,
405 required: true,
406 include: [
407 {
408 model: AccountModel,
409 required: true,
410 include: [
411 {
412 model: ServerModel,
413 required: false
414 }
415 ]
416 }
417 ]
418 },
419 TagModel
420 ],
421 where: this.createBaseVideosWhere()
422 } 470 }
423 471
424 return VideoModel.findAndCountAll(query).then(({ rows, count }) => { 472 return VideoModel.scope([ ScopeNames.NOT_IN_BLACKLIST, ScopeNames.PUBLIC, ScopeNames.WITH_ACCOUNT ])
425 return { 473 .findAndCountAll(query)
426 data: rows, 474 .then(({ rows, count }) => {
427 total: count 475 return {
428 } 476 data: rows,
429 }) 477 total: count
478 }
479 })
430 } 480 }
431 481
432 static load (id: number) { 482 static load (id: number) {
433 return VideoModel.findById(id) 483 return VideoModel.findById(id)
434 } 484 }
435 485
436 static loadByUUID (uuid: string, t?: Sequelize.Transaction) {
437 const query: IFindOptions<VideoModel> = {
438 where: {
439 uuid
440 },
441 include: [ VideoFileModel ]
442 }
443
444 if (t !== undefined) query.transaction = t
445
446 return VideoModel.findOne(query)
447 }
448
449 static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { 486 static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
450 const query: IFindOptions<VideoModel> = { 487 const query: IFindOptions<VideoModel> = {
451 where: { 488 where: {
452 url 489 url
453 }, 490 }
454 include: [
455 VideoFileModel,
456 {
457 model: VideoChannelModel,
458 include: [ AccountModel ]
459 }
460 ]
461 } 491 }
462 492
463 if (t !== undefined) query.transaction = t 493 if (t !== undefined) query.transaction = t
464 494
465 return VideoModel.findOne(query) 495 return VideoModel.scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_FILES ]).findOne(query)
466 } 496 }
467 497
468 static loadByUUIDOrURL (uuid: string, url: string, t?: Sequelize.Transaction) { 498 static loadByUUIDOrURL (uuid: string, url: string, t?: Sequelize.Transaction) {
@@ -472,42 +502,22 @@ export class VideoModel extends Model<VideoModel> {
472 { uuid }, 502 { uuid },
473 { url } 503 { url }
474 ] 504 ]
475 }, 505 }
476 include: [ VideoFileModel ]
477 } 506 }
478 507
479 if (t !== undefined) query.transaction = t 508 if (t !== undefined) query.transaction = t
480 509
481 return VideoModel.findOne(query) 510 return VideoModel.scope(ScopeNames.WITH_FILES).findOne(query)
482 } 511 }
483 512
484 static loadAndPopulateAccountAndServerAndTags (id: number) { 513 static loadAndPopulateAccountAndServerAndTags (id: number) {
485 const options = { 514 const options = {
486 order: [ [ 'Tags', 'name', 'ASC' ] ], 515 order: [ [ 'Tags', 'name', 'ASC' ] ]
487 include: [
488 {
489 model: VideoChannelModel,
490 include: [
491 {
492 model: AccountModel,
493 include: [ { model: ServerModel, required: false } ]
494 }
495 ]
496 },
497 {
498 model: AccountVideoRateModel,
499 include: [ AccountModel ]
500 },
501 {
502 model: VideoShareModel,
503 include: [ AccountModel ]
504 },
505 TagModel,
506 VideoFileModel
507 ]
508 } 516 }
509 517
510 return VideoModel.findById(id, options) 518 return VideoModel
519 .scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
520 .findById(id, options)
511 } 521 }
512 522
513 static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string) { 523 static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string) {
@@ -515,31 +525,12 @@ export class VideoModel extends Model<VideoModel> {
515 order: [ [ 'Tags', 'name', 'ASC' ] ], 525 order: [ [ 'Tags', 'name', 'ASC' ] ],
516 where: { 526 where: {
517 uuid 527 uuid
518 }, 528 }
519 include: [
520 {
521 model: VideoChannelModel,
522 include: [
523 {
524 model: AccountModel,
525 include: [ { model: ServerModel, required: false } ]
526 }
527 ]
528 },
529 {
530 model: AccountVideoRateModel,
531 include: [ AccountModel ]
532 },
533 {
534 model: VideoShareModel,
535 include: [ AccountModel ]
536 },
537 TagModel,
538 VideoFileModel
539 ]
540 } 529 }
541 530
542 return VideoModel.findOne(options) 531 return VideoModel
532 .scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ])
533 .findOne(options)
543 } 534 }
544 535
545 static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) { 536 static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
@@ -564,11 +555,11 @@ export class VideoModel extends Model<VideoModel> {
564 } 555 }
565 556
566 const query: IFindOptions<VideoModel> = { 557 const query: IFindOptions<VideoModel> = {
567 distinct: true, 558 distinct: true, // Because we have tags
568 where: this.createBaseVideosWhere(),
569 offset: start, 559 offset: start,
570 limit: count, 560 limit: count,
571 order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ] 561 order: [ getSort(sort) ],
562 where: {}
572 } 563 }
573 564
574 // TODO: search on tags too 565 // TODO: search on tags too
@@ -595,23 +586,13 @@ export class VideoModel extends Model<VideoModel> {
595 videoChannelInclude, tagInclude 586 videoChannelInclude, tagInclude
596 ] 587 ]
597 588
598 return VideoModel.findAndCountAll(query).then(({ rows, count }) => { 589 return VideoModel.scope([ ScopeNames.NOT_IN_BLACKLIST, ScopeNames.PUBLIC ])
599 return { 590 .findAndCountAll(query).then(({ rows, count }) => {
600 data: rows, 591 return {
601 total: count 592 data: rows,
602 } 593 total: count
603 }) 594 }
604 } 595 })
605
606 private static createBaseVideosWhere () {
607 return {
608 id: {
609 [Sequelize.Op.notIn]: VideoModel.sequelize.literal(
610 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
611 )
612 },
613 privacy: VideoPrivacy.PUBLIC
614 }
615 } 596 }
616 597
617 getOriginalFile () { 598 getOriginalFile () {
@@ -733,13 +714,12 @@ export class VideoModel extends Model<VideoModel> {
733 views: this.views, 714 views: this.views,
734 likes: this.likes, 715 likes: this.likes,
735 dislikes: this.dislikes, 716 dislikes: this.dislikes,
736 tags: map<TagModel, string>(this.Tags, 'name'),
737 thumbnailPath: this.getThumbnailPath(), 717 thumbnailPath: this.getThumbnailPath(),
738 previewPath: this.getPreviewPath(), 718 previewPath: this.getPreviewPath(),
739 embedPath: this.getEmbedPath(), 719 embedPath: this.getEmbedPath(),
740 createdAt: this.createdAt, 720 createdAt: this.createdAt,
741 updatedAt: this.updatedAt 721 updatedAt: this.updatedAt
742 } 722 } as Video
743 } 723 }
744 724
745 toFormattedDetailsJSON () { 725 toFormattedDetailsJSON () {
@@ -755,6 +735,7 @@ export class VideoModel extends Model<VideoModel> {
755 descriptionPath: this.getDescriptionPath(), 735 descriptionPath: this.getDescriptionPath(),
756 channel: this.VideoChannel.toFormattedJSON(), 736 channel: this.VideoChannel.toFormattedJSON(),
757 account: this.VideoChannel.Account.toFormattedJSON(), 737 account: this.VideoChannel.Account.toFormattedJSON(),
738 tags: map<TagModel, string>(this.Tags, 'name'),
758 files: [] 739 files: []
759 } 740 }
760 741
@@ -779,7 +760,7 @@ export class VideoModel extends Model<VideoModel> {
779 return -1 760 return -1
780 }) 761 })
781 762
782 return Object.assign(formattedJson, detailsJson) 763 return Object.assign(formattedJson, detailsJson) as VideoDetails
783 } 764 }
784 765
785 toActivityPubObject (): VideoTorrentObject { 766 toActivityPubObject (): VideoTorrentObject {