aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video-channel.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/video-channel.ts')
-rw-r--r--server/models/video/video-channel.ts145
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 @@
1import { FindOptions, Includeable, literal, Op, ScopeOptions } from 'sequelize' 1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions } from 'sequelize'
2import { 2import {
3 AllowNull, 3 AllowNull,
4 BeforeDestroy, 4 BeforeDestroy,
@@ -28,17 +28,16 @@ import {
28import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 28import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
29import { sendDeleteActor } from '../../lib/activitypub/send' 29import { sendDeleteActor } from '../../lib/activitypub/send'
30import { 30import {
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'
38import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' 37import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account'
38import { ActorImageModel } from '../account/actor-image'
39import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' 39import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
40import { ActorFollowModel } from '../activitypub/actor-follow' 40import { ActorFollowModel } from '../activitypub/actor-follow'
41import { AvatarModel } from '../avatar/avatar'
42import { ServerModel } from '../server/server' 41import { ServerModel } from '../server/server'
43import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' 42import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
44import { VideoModel } from './video' 43import { 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 = `
350SELECT COUNT(DISTINCT("VideoChannelModel"."id")) AS "count"
351FROM "videoChannel" AS "VideoChannelModel"
352INNER JOIN "video" AS "Videos"
353ON "VideoChannelModel"."id" = "Videos"."channelId"
354AND ("Videos"."publishedAt" > Now() - interval '${days}d')
355INNER JOIN "account" AS "Account"
356ON "VideoChannelModel"."accountId" = "Account"."id"
357INNER JOIN "actor" AS "Account->Actor"
358ON "Account"."actorId" = "Account->Actor"."id"
359AND "Account->Actor"."serverId" IS NULL
360LEFT OUTER JOIN "server" AS "Account->Actor->Server"
361ON "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