]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video-channel.ts
Add ability for plugins to alter video jsonld
[github/Chocobozzz/PeerTube.git] / server / models / video / video-channel.ts
index 410fd6d3f777c4e15c2bb0f509eb2012eb0210de..67fccab68c11527b5a045f09503bd1d8e39d8874 100644 (file)
@@ -19,7 +19,7 @@ import {
 } from 'sequelize-typescript'
 import { CONFIG } from '@server/initializers/config'
 import { MAccountActor } from '@server/types/models'
-import { pick } from '@shared/core-utils'
+import { forceNumber, pick } from '@shared/core-utils'
 import { AttributesOnly } from '@shared/typescript-utils'
 import { ActivityPubActor } from '../../../shared/models/activitypub'
 import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
@@ -43,8 +43,14 @@ import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
 import { ActorFollowModel } from '../actor/actor-follow'
 import { ActorImageModel } from '../actor/actor-image'
 import { ServerModel } from '../server/server'
-import { setAsUpdated } from '../shared'
-import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
+import {
+  buildServerIdsFollowedBy,
+  buildTrigramSearchIndex,
+  createSimilarityAttribute,
+  getSort,
+  setAsUpdated,
+  throwIfNotValid
+} from '../shared'
 import { VideoModel } from './video'
 import { VideoPlaylistModel } from './video-playlist'
 
@@ -143,28 +149,28 @@ export type SummaryOptions = {
       })
     }
 
-    const channelInclude: Includeable[] = []
-    const accountInclude: Includeable[] = []
+    const channelActorInclude: Includeable[] = []
+    const accountActorInclude: Includeable[] = []
 
     if (options.forCount !== true) {
-      accountInclude.push({
+      accountActorInclude.push({
         model: ServerModel,
         required: false
       })
 
-      accountInclude.push({
+      accountActorInclude.push({
         model: ActorImageModel,
         as: 'Avatars',
         required: false
       })
 
-      channelInclude.push({
+      channelActorInclude.push({
         model: ActorImageModel,
         as: 'Avatars',
         required: false
       })
 
-      channelInclude.push({
+      channelActorInclude.push({
         model: ActorImageModel,
         as: 'Banners',
         required: false
@@ -172,7 +178,7 @@ export type SummaryOptions = {
     }
 
     if (options.forCount !== true || serverRequired) {
-      channelInclude.push({
+      channelActorInclude.push({
         model: ServerModel,
         duplicating: false,
         required: serverRequired,
@@ -190,7 +196,7 @@ export type SummaryOptions = {
           where: {
             [Op.and]: whereActorAnd
           },
-          include: channelInclude
+          include: channelActorInclude
         },
         {
           model: AccountModel.unscoped(),
@@ -202,7 +208,7 @@ export type SummaryOptions = {
               },
               model: ActorModel.unscoped(),
               required: true,
-              include: accountInclude
+              include: accountActorInclude
             }
           ]
         }
@@ -280,7 +286,7 @@ export type SummaryOptions = {
     ]
   },
   [ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => {
-    const daysPrior = parseInt(options.daysPrior + '', 10)
+    const daysPrior = forceNumber(options.daysPrior)
 
     return {
       attributes: {
@@ -311,6 +317,16 @@ export type SummaryOptions = {
               ')'
             ),
             'viewsPerDay'
+          ],
+          [
+            literal(
+              '(' +
+              'SELECT COALESCE(SUM("video".views), 0) AS totalViews ' +
+              'FROM "video" ' +
+              'WHERE "video"."channelId" = "VideoChannelModel"."id"' +
+              ')'
+            ),
+            'totalViews'
           ]
         ]
       }
@@ -419,47 +435,46 @@ export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannel
       }
     }
 
-    return VideoChannelModel.count(query)
+    return VideoChannelModel.unscoped().count(query)
   }
 
   static async getStats () {
 
-    function getActiveVideoChannels (days: number) {
+    function getLocalVideoChannelStats (days?: number) {
       const options = {
         type: QueryTypes.SELECT as QueryTypes.SELECT,
         raw: true
       }
 
+      const videoJoin = days
+        ? `INNER JOIN "video" AS "Videos" ON "VideoChannelModel"."id" = "Videos"."channelId" ` +
+             `AND ("Videos"."publishedAt" > Now() - interval '${days}d')`
+        : ''
+
       const query = `
-SELECT          COUNT(DISTINCT("VideoChannelModel"."id")) AS "count"
-FROM            "videoChannel"                            AS "VideoChannelModel"
-INNER JOIN      "video"                                   AS "Videos"
-ON              "VideoChannelModel"."id" = "Videos"."channelId"
-AND             ("Videos"."publishedAt" > Now() - interval '${days}d')
-INNER JOIN      "account" AS "Account"
-ON              "VideoChannelModel"."accountId" = "Account"."id"
-INNER JOIN      "actor" AS "Account->Actor"
-ON              "Account"."actorId" = "Account->Actor"."id"
-AND             "Account->Actor"."serverId" IS NULL
-LEFT OUTER JOIN "server" AS "Account->Actor->Server"
-ON              "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
+      SELECT COUNT(DISTINCT("VideoChannelModel"."id")) AS "count"
+      FROM "videoChannel" AS "VideoChannelModel"
+      ${videoJoin}
+      INNER JOIN "account" AS "Account" ON "VideoChannelModel"."accountId" = "Account"."id"
+      INNER JOIN "actor" AS "Account->Actor" ON "Account"."actorId" = "Account->Actor"."id"
+        AND "Account->Actor"."serverId" IS NULL`
 
       return VideoChannelModel.sequelize.query<{ count: string }>(query, options)
                               .then(r => parseInt(r[0].count, 10))
     }
 
-    const totalLocalVideoChannels = await VideoChannelModel.count()
-    const totalLocalDailyActiveVideoChannels = await getActiveVideoChannels(1)
-    const totalLocalWeeklyActiveVideoChannels = await getActiveVideoChannels(7)
-    const totalLocalMonthlyActiveVideoChannels = await getActiveVideoChannels(30)
-    const totalHalfYearActiveVideoChannels = await getActiveVideoChannels(180)
+    const totalLocalVideoChannels = await getLocalVideoChannelStats()
+    const totalLocalDailyActiveVideoChannels = await getLocalVideoChannelStats(1)
+    const totalLocalWeeklyActiveVideoChannels = await getLocalVideoChannelStats(7)
+    const totalLocalMonthlyActiveVideoChannels = await getLocalVideoChannelStats(30)
+    const totalLocalHalfYearActiveVideoChannels = await getLocalVideoChannelStats(180)
 
     return {
       totalLocalVideoChannels,
       totalLocalDailyActiveVideoChannels,
       totalLocalWeeklyActiveVideoChannels,
       totalLocalMonthlyActiveVideoChannels,
-      totalHalfYearActiveVideoChannels
+      totalLocalHalfYearActiveVideoChannels
     }
   }
 
@@ -605,17 +620,17 @@ ON              "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
       }
     }
 
-    const scopes: string | ScopeOptions | (string | ScopeOptions)[] = [ ScopeNames.WITH_ACTOR_BANNER ]
+    const findScopes: string | ScopeOptions | (string | ScopeOptions)[] = [ ScopeNames.WITH_ACTOR_BANNER ]
 
     if (options.withStats === true) {
-      scopes.push({
+      findScopes.push({
         method: [ ScopeNames.WITH_STATS, { daysPrior: 30 } as AvailableWithStatsOptions ]
       })
     }
 
     return Promise.all([
-      VideoChannelModel.scope(scopes).count(getQuery(true)),
-      VideoChannelModel.scope(scopes).findAll(getQuery(false))
+      VideoChannelModel.unscoped().count(getQuery(true)),
+      VideoChannelModel.scope(findScopes).findAll(getQuery(false))
     ]).then(([ total, data ]) => ({ total, data }))
   }
 
@@ -766,6 +781,8 @@ ON              "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
         })
     }
 
+    const totalViews = this.get('totalViews') as number
+
     const actor = this.Actor.toFormattedJSON()
     const videoChannel = {
       id: this.id,
@@ -779,6 +796,7 @@ ON              "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
 
       videosCount,
       viewsPerDay,
+      totalViews,
 
       avatars: actor.avatars,
 
@@ -791,8 +809,8 @@ ON              "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
     return Object.assign(actor, videoChannel)
   }
 
-  toActivityPubObject (this: MChannelAP): ActivityPubActor {
-    const obj = this.Actor.toActivityPubObject(this.name)
+  async toActivityPubObject (this: MChannelAP): Promise<ActivityPubActor> {
+    const obj = await this.Actor.toActivityPubObject(this.name)
 
     return Object.assign(obj, {
       summary: this.description,
@@ -819,6 +837,6 @@ ON              "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
   }
 
   setAsUpdated (transaction?: Transaction) {
-    return setAsUpdated('videoChannel', this.id, transaction)
+    return setAsUpdated({ sequelize: this.sequelize, table: 'videoChannel', id: this.id, transaction })
   }
 }