diff options
Diffstat (limited to 'server/models/video/video-channel.ts')
-rw-r--r-- | server/models/video/video-channel.ts | 145 |
1 files changed, 98 insertions, 47 deletions
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 178878c55..b7ffbd3b1 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { FindOptions, Includeable, literal, Op, ScopeOptions } from 'sequelize' | 1 | import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions } from 'sequelize' |
2 | import { | 2 | import { |
3 | AllowNull, | 3 | AllowNull, |
4 | BeforeDestroy, | 4 | BeforeDestroy, |
@@ -28,17 +28,16 @@ import { | |||
28 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' | 28 | import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' |
29 | import { sendDeleteActor } from '../../lib/activitypub/send' | 29 | import { sendDeleteActor } from '../../lib/activitypub/send' |
30 | import { | 30 | import { |
31 | MChannelAccountDefault, | ||
32 | MChannelActor, | 31 | MChannelActor, |
33 | MChannelActorAccountDefaultVideos, | ||
34 | MChannelAP, | 32 | MChannelAP, |
33 | MChannelBannerAccountDefault, | ||
35 | MChannelFormattable, | 34 | MChannelFormattable, |
36 | MChannelSummaryFormattable | 35 | MChannelSummaryFormattable |
37 | } from '../../types/models/video' | 36 | } from '../../types/models/video' |
38 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' | 37 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' |
38 | import { ActorImageModel } from '../account/actor-image' | ||
39 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' | 39 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' |
40 | import { ActorFollowModel } from '../activitypub/actor-follow' | 40 | import { ActorFollowModel } from '../activitypub/actor-follow' |
41 | import { AvatarModel } from '../avatar/avatar' | ||
42 | import { ServerModel } from '../server/server' | 41 | import { ServerModel } from '../server/server' |
43 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' | 42 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' |
44 | import { VideoModel } from './video' | 43 | import { VideoModel } from './video' |
@@ -49,6 +48,7 @@ export enum ScopeNames { | |||
49 | SUMMARY = 'SUMMARY', | 48 | SUMMARY = 'SUMMARY', |
50 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 49 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
51 | WITH_ACTOR = 'WITH_ACTOR', | 50 | WITH_ACTOR = 'WITH_ACTOR', |
51 | WITH_ACTOR_BANNER = 'WITH_ACTOR_BANNER', | ||
52 | WITH_VIDEOS = 'WITH_VIDEOS', | 52 | WITH_VIDEOS = 'WITH_VIDEOS', |
53 | WITH_STATS = 'WITH_STATS' | 53 | WITH_STATS = 'WITH_STATS' |
54 | } | 54 | } |
@@ -99,7 +99,14 @@ export type SummaryOptions = { | |||
99 | } | 99 | } |
100 | } | 100 | } |
101 | ] | 101 | ] |
102 | } | 102 | }, |
103 | include: [ | ||
104 | { | ||
105 | model: ActorImageModel, | ||
106 | as: 'Banner', | ||
107 | required: false | ||
108 | } | ||
109 | ] | ||
103 | }, | 110 | }, |
104 | { | 111 | { |
105 | model: AccountModel, | 112 | model: AccountModel, |
@@ -130,7 +137,8 @@ export type SummaryOptions = { | |||
130 | required: false | 137 | required: false |
131 | }, | 138 | }, |
132 | { | 139 | { |
133 | model: AvatarModel.unscoped(), | 140 | model: ActorImageModel.unscoped(), |
141 | as: 'Avatar', | ||
134 | required: false | 142 | required: false |
135 | } | 143 | } |
136 | ] | 144 | ] |
@@ -167,6 +175,20 @@ export type SummaryOptions = { | |||
167 | ActorModel | 175 | ActorModel |
168 | ] | 176 | ] |
169 | }, | 177 | }, |
178 | [ScopeNames.WITH_ACTOR_BANNER]: { | ||
179 | include: [ | ||
180 | { | ||
181 | model: ActorModel, | ||
182 | include: [ | ||
183 | { | ||
184 | model: ActorImageModel, | ||
185 | required: false, | ||
186 | as: 'Banner' | ||
187 | } | ||
188 | ] | ||
189 | } | ||
190 | ] | ||
191 | }, | ||
170 | [ScopeNames.WITH_VIDEOS]: { | 192 | [ScopeNames.WITH_VIDEOS]: { |
171 | include: [ | 193 | include: [ |
172 | VideoModel | 194 | VideoModel |
@@ -316,6 +338,47 @@ export class VideoChannelModel extends Model { | |||
316 | return VideoChannelModel.count(query) | 338 | return VideoChannelModel.count(query) |
317 | } | 339 | } |
318 | 340 | ||
341 | static async getStats () { | ||
342 | |||
343 | function getActiveVideoChannels (days: number) { | ||
344 | const options = { | ||
345 | type: QueryTypes.SELECT as QueryTypes.SELECT, | ||
346 | raw: true | ||
347 | } | ||
348 | |||
349 | const query = ` | ||
350 | SELECT COUNT(DISTINCT("VideoChannelModel"."id")) AS "count" | ||
351 | FROM "videoChannel" AS "VideoChannelModel" | ||
352 | INNER JOIN "video" AS "Videos" | ||
353 | ON "VideoChannelModel"."id" = "Videos"."channelId" | ||
354 | AND ("Videos"."publishedAt" > Now() - interval '${days}d') | ||
355 | INNER JOIN "account" AS "Account" | ||
356 | ON "VideoChannelModel"."accountId" = "Account"."id" | ||
357 | INNER JOIN "actor" AS "Account->Actor" | ||
358 | ON "Account"."actorId" = "Account->Actor"."id" | ||
359 | AND "Account->Actor"."serverId" IS NULL | ||
360 | LEFT OUTER JOIN "server" AS "Account->Actor->Server" | ||
361 | ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"` | ||
362 | |||
363 | return VideoChannelModel.sequelize.query<{ count: string }>(query, options) | ||
364 | .then(r => parseInt(r[0].count, 10)) | ||
365 | } | ||
366 | |||
367 | const totalLocalVideoChannels = await VideoChannelModel.count() | ||
368 | const totalLocalDailyActiveVideoChannels = await getActiveVideoChannels(1) | ||
369 | const totalLocalWeeklyActiveVideoChannels = await getActiveVideoChannels(7) | ||
370 | const totalLocalMonthlyActiveVideoChannels = await getActiveVideoChannels(30) | ||
371 | const totalHalfYearActiveVideoChannels = await getActiveVideoChannels(180) | ||
372 | |||
373 | return { | ||
374 | totalLocalVideoChannels, | ||
375 | totalLocalDailyActiveVideoChannels, | ||
376 | totalLocalWeeklyActiveVideoChannels, | ||
377 | totalLocalMonthlyActiveVideoChannels, | ||
378 | totalHalfYearActiveVideoChannels | ||
379 | } | ||
380 | } | ||
381 | |||
319 | static listForApi (parameters: { | 382 | static listForApi (parameters: { |
320 | actorId: number | 383 | actorId: number |
321 | start: number | 384 | start: number |
@@ -441,7 +504,7 @@ export class VideoChannelModel extends Model { | |||
441 | where | 504 | where |
442 | } | 505 | } |
443 | 506 | ||
444 | const scopes: string | ScopeOptions | (string | ScopeOptions)[] = [ ScopeNames.WITH_ACTOR ] | 507 | const scopes: string | ScopeOptions | (string | ScopeOptions)[] = [ ScopeNames.WITH_ACTOR_BANNER ] |
445 | 508 | ||
446 | if (options.withStats === true) { | 509 | if (options.withStats === true) { |
447 | scopes.push({ | 510 | scopes.push({ |
@@ -457,32 +520,13 @@ export class VideoChannelModel extends Model { | |||
457 | }) | 520 | }) |
458 | } | 521 | } |
459 | 522 | ||
460 | static loadByIdAndPopulateAccount (id: number): Promise<MChannelAccountDefault> { | 523 | static loadAndPopulateAccount (id: number): Promise<MChannelBannerAccountDefault> { |
461 | return VideoChannelModel.unscoped() | 524 | return VideoChannelModel.unscoped() |
462 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | 525 | .scope([ ScopeNames.WITH_ACTOR_BANNER, ScopeNames.WITH_ACCOUNT ]) |
463 | .findByPk(id) | 526 | .findByPk(id) |
464 | } | 527 | } |
465 | 528 | ||
466 | static loadByIdAndAccount (id: number, accountId: number): Promise<MChannelAccountDefault> { | 529 | static loadByUrlAndPopulateAccount (url: string): Promise<MChannelBannerAccountDefault> { |
467 | const query = { | ||
468 | where: { | ||
469 | id, | ||
470 | accountId | ||
471 | } | ||
472 | } | ||
473 | |||
474 | return VideoChannelModel.unscoped() | ||
475 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | ||
476 | .findOne(query) | ||
477 | } | ||
478 | |||
479 | static loadAndPopulateAccount (id: number): Promise<MChannelAccountDefault> { | ||
480 | return VideoChannelModel.unscoped() | ||
481 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | ||
482 | .findByPk(id) | ||
483 | } | ||
484 | |||
485 | static loadByUrlAndPopulateAccount (url: string): Promise<MChannelAccountDefault> { | ||
486 | const query = { | 530 | const query = { |
487 | include: [ | 531 | include: [ |
488 | { | 532 | { |
@@ -490,7 +534,14 @@ export class VideoChannelModel extends Model { | |||
490 | required: true, | 534 | required: true, |
491 | where: { | 535 | where: { |
492 | url | 536 | url |
493 | } | 537 | }, |
538 | include: [ | ||
539 | { | ||
540 | model: ActorImageModel, | ||
541 | required: false, | ||
542 | as: 'Banner' | ||
543 | } | ||
544 | ] | ||
494 | } | 545 | } |
495 | ] | 546 | ] |
496 | } | 547 | } |
@@ -508,7 +559,7 @@ export class VideoChannelModel extends Model { | |||
508 | return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) | 559 | return VideoChannelModel.loadByNameAndHostAndPopulateAccount(name, host) |
509 | } | 560 | } |
510 | 561 | ||
511 | static loadLocalByNameAndPopulateAccount (name: string): Promise<MChannelAccountDefault> { | 562 | static loadLocalByNameAndPopulateAccount (name: string): Promise<MChannelBannerAccountDefault> { |
512 | const query = { | 563 | const query = { |
513 | include: [ | 564 | include: [ |
514 | { | 565 | { |
@@ -517,17 +568,24 @@ export class VideoChannelModel extends Model { | |||
517 | where: { | 568 | where: { |
518 | preferredUsername: name, | 569 | preferredUsername: name, |
519 | serverId: null | 570 | serverId: null |
520 | } | 571 | }, |
572 | include: [ | ||
573 | { | ||
574 | model: ActorImageModel, | ||
575 | required: false, | ||
576 | as: 'Banner' | ||
577 | } | ||
578 | ] | ||
521 | } | 579 | } |
522 | ] | 580 | ] |
523 | } | 581 | } |
524 | 582 | ||
525 | return VideoChannelModel.unscoped() | 583 | return VideoChannelModel.unscoped() |
526 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | 584 | .scope([ ScopeNames.WITH_ACCOUNT ]) |
527 | .findOne(query) | 585 | .findOne(query) |
528 | } | 586 | } |
529 | 587 | ||
530 | static loadByNameAndHostAndPopulateAccount (name: string, host: string): Promise<MChannelAccountDefault> { | 588 | static loadByNameAndHostAndPopulateAccount (name: string, host: string): Promise<MChannelBannerAccountDefault> { |
531 | const query = { | 589 | const query = { |
532 | include: [ | 590 | include: [ |
533 | { | 591 | { |
@@ -541,6 +599,11 @@ export class VideoChannelModel extends Model { | |||
541 | model: ServerModel, | 599 | model: ServerModel, |
542 | required: true, | 600 | required: true, |
543 | where: { host } | 601 | where: { host } |
602 | }, | ||
603 | { | ||
604 | model: ActorImageModel, | ||
605 | required: false, | ||
606 | as: 'Banner' | ||
544 | } | 607 | } |
545 | ] | 608 | ] |
546 | } | 609 | } |
@@ -548,22 +611,10 @@ export class VideoChannelModel extends Model { | |||
548 | } | 611 | } |
549 | 612 | ||
550 | return VideoChannelModel.unscoped() | 613 | return VideoChannelModel.unscoped() |
551 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ]) | 614 | .scope([ ScopeNames.WITH_ACCOUNT ]) |
552 | .findOne(query) | 615 | .findOne(query) |
553 | } | 616 | } |
554 | 617 | ||
555 | static loadAndPopulateAccountAndVideos (id: number): Promise<MChannelActorAccountDefaultVideos> { | ||
556 | const options = { | ||
557 | include: [ | ||
558 | VideoModel | ||
559 | ] | ||
560 | } | ||
561 | |||
562 | return VideoChannelModel.unscoped() | ||
563 | .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ]) | ||
564 | .findByPk(id, options) | ||
565 | } | ||
566 | |||
567 | toFormattedSummaryJSON (this: MChannelSummaryFormattable): VideoChannelSummary { | 618 | toFormattedSummaryJSON (this: MChannelSummaryFormattable): VideoChannelSummary { |
568 | const actor = this.Actor.toFormattedSummaryJSON() | 619 | const actor = this.Actor.toFormattedSummaryJSON() |
569 | 620 | ||