} 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'
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'
})
}
- 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
}
if (options.forCount !== true || serverRequired) {
- channelInclude.push({
+ channelActorInclude.push({
model: ServerModel,
duplicating: false,
required: serverRequired,
where: {
[Op.and]: whereActorAnd
},
- include: channelInclude
+ include: channelActorInclude
},
{
model: AccountModel.unscoped(),
},
model: ActorModel.unscoped(),
required: true,
- include: accountInclude
+ include: accountActorInclude
}
]
}
]
},
[ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => {
- const daysPrior = parseInt(options.daysPrior + '', 10)
+ const daysPrior = forceNumber(options.daysPrior)
return {
attributes: {
')'
),
'viewsPerDay'
+ ],
+ [
+ literal(
+ '(' +
+ 'SELECT COALESCE(SUM("video".views), 0) AS totalViews ' +
+ 'FROM "video" ' +
+ 'WHERE "video"."channelId" = "VideoChannelModel"."id"' +
+ ')'
+ ),
+ 'totalViews'
]
]
}
}
}
- 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
}
}
}
}
- 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 }))
}
})
}
+ const totalViews = this.get('totalViews') as number
+
const actor = this.Actor.toFormattedJSON()
const videoChannel = {
id: this.id,
videosCount,
viewsPerDay,
+ totalViews,
avatars: actor.avatars,
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,
}
setAsUpdated (transaction?: Transaction) {
- return setAsUpdated('videoChannel', this.id, transaction)
+ return setAsUpdated({ sequelize: this.sequelize, table: 'videoChannel', id: this.id, transaction })
}
}