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