+import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
+import { ServerModel } from '../server/server'
+import { FindOptions, Op, literal, ScopeOptions } from 'sequelize'
+import { AvatarModel } from '../avatar/avatar'
+import { VideoPlaylistModel } from './video-playlist'
+import * as Bluebird from 'bluebird'
+import {
+ MChannelAccountDefault,
+ MChannelActor,
+ MChannelActorAccountDefaultVideos,
+ MChannelAP,
+ MChannelFormattable,
+ MChannelSummaryFormattable
+} from '../../types/models/video'
+
+export enum ScopeNames {
+ FOR_API = 'FOR_API',
+ SUMMARY = 'SUMMARY',
+ WITH_ACCOUNT = 'WITH_ACCOUNT',
+ WITH_ACTOR = 'WITH_ACTOR',
+ WITH_VIDEOS = 'WITH_VIDEOS',
+ WITH_STATS = 'WITH_STATS'
+}
+
+type AvailableForListOptions = {
+ actorId: number
+ search?: string
+}
+
+type AvailableWithStatsOptions = {
+ daysPrior: number
+}
+
+export type SummaryOptions = {
+ actorRequired?: boolean // Default: true
+ withAccount?: boolean // Default: false
+ withAccountBlockerIds?: number[]
+}
+
+@DefaultScope(() => ({
+ include: [
+ {
+ model: ActorModel,
+ required: true
+ }
+ ]
+}))
+@Scopes(() => ({
+ [ScopeNames.FOR_API]: (options: AvailableForListOptions) => {
+ // Only list local channels OR channels that are on an instance followed by actorId
+ const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
+
+ return {
+ include: [
+ {
+ attributes: {
+ exclude: unusedActorAttributesForAPI
+ },
+ model: ActorModel,
+ where: {
+ [Op.or]: [
+ {
+ serverId: null
+ },
+ {
+ serverId: {
+ [Op.in]: Sequelize.literal(inQueryInstanceFollow)
+ }
+ }
+ ]
+ }
+ },
+ {
+ model: AccountModel,
+ required: true,
+ include: [
+ {
+ attributes: {
+ exclude: unusedActorAttributesForAPI
+ },
+ model: ActorModel, // Default scope includes avatar and server
+ required: true
+ }
+ ]
+ }
+ ]
+ }
+ },
+ [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
+ const base: FindOptions = {
+ attributes: [ 'id', 'name', 'description', 'actorId' ],
+ include: [
+ {
+ attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
+ model: ActorModel.unscoped(),
+ required: options.actorRequired ?? true,
+ include: [
+ {
+ attributes: [ 'host' ],
+ model: ServerModel.unscoped(),
+ required: false
+ },
+ {
+ model: AvatarModel.unscoped(),
+ required: false
+ }
+ ]
+ }
+ ]
+ }
+
+ if (options.withAccount === true) {
+ base.include.push({
+ model: AccountModel.scope({
+ method: [ AccountModelScopeNames.SUMMARY, { withAccountBlockerIds: options.withAccountBlockerIds } as AccountSummaryOptions ]
+ }),
+ required: true
+ })
+ }
+
+ return base
+ },
+ [ScopeNames.WITH_ACCOUNT]: {
+ include: [
+ {
+ model: AccountModel,
+ required: true
+ }
+ ]
+ },
+ [ScopeNames.WITH_ACTOR]: {
+ include: [
+ ActorModel
+ ]
+ },
+ [ScopeNames.WITH_VIDEOS]: {
+ include: [
+ VideoModel
+ ]
+ },
+ [ScopeNames.WITH_STATS]: (options: AvailableWithStatsOptions = { daysPrior: 30 }) => {
+ const daysPrior = parseInt(options.daysPrior + '', 10)