-import { FindOptions, Includeable, literal, Op, ScopeOptions } from 'sequelize'
+import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction } from 'sequelize'
import {
AllowNull,
BeforeDestroy,
Table,
UpdatedAt
} from 'sequelize-typescript'
+import { setAsUpdated } from '@server/helpers/database-utils'
import { MAccountActor } from '@server/types/models'
+import { AttributesOnly } from '@shared/core-utils'
import { ActivityPubActor } from '../../../shared/models/activitypub'
import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
import {
MChannelSummaryFormattable
} from '../../types/models/video'
import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account'
-import { ActorImageModel } from '../account/actor-image'
-import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
-import { ActorFollowModel } from '../activitypub/actor-follow'
+import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
+import { ActorFollowModel } from '../actor/actor-follow'
+import { ActorImageModel } from '../actor/actor-image'
import { ServerModel } from '../server/server'
import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
import { VideoModel } from './video'
}
}
]
- }
+ },
+ include: [
+ {
+ model: ActorImageModel,
+ as: 'Banner',
+ required: false
+ }
+ ]
},
{
model: AccountModel,
}
]
})
-export class VideoChannelModel extends Model {
+export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> {
@AllowNull(false)
@Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name'))
return VideoChannelModel.count(query)
}
+ static async getStats () {
+
+ function getActiveVideoChannels (days: number) {
+ const options = {
+ type: QueryTypes.SELECT as QueryTypes.SELECT,
+ raw: true
+ }
+
+ 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"`
+
+ 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)
+
+ return {
+ totalLocalVideoChannels,
+ totalLocalDailyActiveVideoChannels,
+ totalLocalWeeklyActiveVideoChannels,
+ totalLocalMonthlyActiveVideoChannels,
+ totalHalfYearActiveVideoChannels
+ }
+ }
+
static listForApi (parameters: {
actorId: number
start: number
})
}
- static loadAndPopulateAccount (id: number): Promise<MChannelBannerAccountDefault> {
+ static loadAndPopulateAccount (id: number, transaction?: Transaction): Promise<MChannelBannerAccountDefault> {
return VideoChannelModel.unscoped()
.scope([ ScopeNames.WITH_ACTOR_BANNER, ScopeNames.WITH_ACCOUNT ])
- .findByPk(id)
+ .findByPk(id, { transaction })
}
static loadByUrlAndPopulateAccount (url: string): Promise<MChannelBannerAccountDefault> {
description: this.description,
support: this.support,
isLocal: this.Actor.isOwned(),
- createdAt: this.createdAt,
updatedAt: this.updatedAt,
ownerAccount: undefined,
videosCount,
isOutdated () {
return this.Actor.isOutdated()
}
+
+ setAsUpdated (transaction: Transaction) {
+ return setAsUpdated('videoChannel', this.id, transaction)
+ }
}