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