Use ISO 639 for languages
[oweals/peertube.git] / server / models / video / video-channel.ts
1 import {
2   AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table,
3   UpdatedAt, Default
4 } from 'sequelize-typescript'
5 import { ActivityPubActor } from '../../../shared/models/activitypub'
6 import { VideoChannel } from '../../../shared/models/videos'
7 import {
8   isVideoChannelDescriptionValid, isVideoChannelNameValid,
9   isVideoChannelSupportValid
10 } from '../../helpers/custom-validators/video-channels'
11 import { logger } from '../../helpers/logger'
12 import { sendDeleteActor } from '../../lib/activitypub/send'
13 import { AccountModel } from '../account/account'
14 import { ActorModel } from '../activitypub/actor'
15 import { getSort, throwIfNotValid } from '../utils'
16 import { VideoModel } from './video'
17
18 enum ScopeNames {
19   WITH_ACCOUNT = 'WITH_ACCOUNT',
20   WITH_ACTOR = 'WITH_ACTOR',
21   WITH_VIDEOS = 'WITH_VIDEOS'
22 }
23
24 @DefaultScope({
25   include: [
26     {
27       model: () => ActorModel,
28       required: true
29     }
30   ]
31 })
32 @Scopes({
33   [ScopeNames.WITH_ACCOUNT]: {
34     include: [
35       {
36         model: () => AccountModel,
37         required: true,
38         include: [
39           {
40             model: () => ActorModel,
41             required: true
42           }
43         ]
44       }
45     ]
46   },
47   [ScopeNames.WITH_VIDEOS]: {
48     include: [
49       () => VideoModel
50     ]
51   },
52   [ScopeNames.WITH_ACTOR]: {
53     include: [
54       () => ActorModel
55     ]
56   }
57 })
58 @Table({
59   tableName: 'videoChannel',
60   indexes: [
61     {
62       fields: [ 'accountId' ]
63     }
64   ]
65 })
66 export class VideoChannelModel extends Model<VideoChannelModel> {
67
68   @AllowNull(false)
69   @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name'))
70   @Column
71   name: string
72
73   @AllowNull(true)
74   @Default(null)
75   @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description'))
76   @Column
77   description: string
78
79   @AllowNull(true)
80   @Default(null)
81   @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support'))
82   @Column
83   support: string
84
85   @CreatedAt
86   createdAt: Date
87
88   @UpdatedAt
89   updatedAt: Date
90
91   @ForeignKey(() => ActorModel)
92   @Column
93   actorId: number
94
95   @BelongsTo(() => ActorModel, {
96     foreignKey: {
97       allowNull: false
98     },
99     onDelete: 'cascade'
100   })
101   Actor: ActorModel
102
103   @ForeignKey(() => AccountModel)
104   @Column
105   accountId: number
106
107   @BelongsTo(() => AccountModel, {
108     foreignKey: {
109       allowNull: false
110     },
111     onDelete: 'CASCADE'
112   })
113   Account: AccountModel
114
115   @HasMany(() => VideoModel, {
116     foreignKey: {
117       name: 'channelId',
118       allowNull: false
119     },
120     onDelete: 'CASCADE',
121     hooks: true
122   })
123   Videos: VideoModel[]
124
125   @BeforeDestroy
126   static async sendDeleteIfOwned (instance: VideoChannelModel, options) {
127     if (!instance.Actor) {
128       instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
129     }
130
131     if (instance.Actor.isOwned()) {
132       logger.debug('Sending delete of actor of video channel %s.', instance.Actor.url)
133
134       return sendDeleteActor(instance.Actor, options.transaction)
135     }
136
137     return undefined
138   }
139
140   static countByAccount (accountId: number) {
141     const query = {
142       where: {
143         accountId
144       }
145     }
146
147     return VideoChannelModel.count(query)
148   }
149
150   static listForApi (start: number, count: number, sort: string) {
151     const query = {
152       offset: start,
153       limit: count,
154       order: getSort(sort)
155     }
156
157     return VideoChannelModel
158       .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
159       .findAndCountAll(query)
160       .then(({ rows, count }) => {
161         return { total: count, data: rows }
162       })
163   }
164
165   static listByAccount (accountId: number) {
166     const query = {
167       order: getSort('createdAt'),
168       include: [
169         {
170           model: AccountModel,
171           where: {
172             id: accountId
173           },
174           required: true
175         }
176       ]
177     }
178
179     return VideoChannelModel
180       .findAndCountAll(query)
181       .then(({ rows, count }) => {
182         return { total: count, data: rows }
183       })
184   }
185
186   static loadByIdAndAccount (id: number, accountId: number) {
187     const options = {
188       where: {
189         id,
190         accountId
191       }
192     }
193
194     return VideoChannelModel
195       .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
196       .findOne(options)
197   }
198
199   static loadAndPopulateAccount (id: number) {
200     return VideoChannelModel
201       .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
202       .findById(id)
203   }
204
205   static loadByUUIDAndPopulateAccount (uuid: string) {
206     const options = {
207       include: [
208         {
209           model: ActorModel,
210           required: true,
211           where: {
212             uuid
213           }
214         }
215       ]
216     }
217
218     return VideoChannelModel
219       .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
220       .findOne(options)
221   }
222
223   static loadAndPopulateAccountAndVideos (id: number) {
224     const options = {
225       include: [
226         VideoModel
227       ]
228     }
229
230     return VideoChannelModel
231       .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ])
232       .findById(id, options)
233   }
234
235   toFormattedJSON (): VideoChannel {
236     const actor = this.Actor.toFormattedJSON()
237     const account = {
238       id: this.id,
239       displayName: this.name,
240       description: this.description,
241       support: this.support,
242       isLocal: this.Actor.isOwned(),
243       createdAt: this.createdAt,
244       updatedAt: this.updatedAt
245     }
246
247     return Object.assign(actor, account)
248   }
249
250   toActivityPubObject (): ActivityPubActor {
251     const obj = this.Actor.toActivityPubObject(this.name, 'VideoChannel')
252
253     return Object.assign(obj, {
254       summary: this.description,
255       support: this.support,
256       attributedTo: [
257         {
258           type: 'Person' as 'Person',
259           id: this.Account.Actor.url
260         }
261       ]
262     })
263   }
264 }