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