]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/models/video/video-channel.ts
Use height instead of width to represent the video resolution
[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 { sendDeleteActor } from '../../lib/activitypub/send'
26 import { AccountModel } from '../account/account'
27 import { ActorModel } from '../activitypub/actor'
28 import { getSort, throwIfNotValid } from '../utils'
29 import { VideoModel } from './video'
30 import { CONSTRAINTS_FIELDS } from '../../initializers'
31 import { AvatarModel } from '../avatar/avatar'
32
33 enum ScopeNames {
34 WITH_ACCOUNT = 'WITH_ACCOUNT',
35 WITH_ACTOR = 'WITH_ACTOR',
36 WITH_VIDEOS = 'WITH_VIDEOS'
37 }
38
39 @DefaultScope({
40 include: [
41 {
42 model: () => ActorModel,
43 required: true
44 }
45 ]
46 })
47 @Scopes({
48 [ScopeNames.WITH_ACCOUNT]: {
49 include: [
50 {
51 model: () => AccountModel.unscoped(),
52 required: true,
53 include: [
54 {
55 model: () => ActorModel.unscoped(),
56 required: true,
57 include: [
58 {
59 model: () => AvatarModel.unscoped(),
60 required: false
61 }
62 ]
63 }
64 ]
65 }
66 ]
67 },
68 [ScopeNames.WITH_VIDEOS]: {
69 include: [
70 () => VideoModel
71 ]
72 },
73 [ScopeNames.WITH_ACTOR]: {
74 include: [
75 () => ActorModel
76 ]
77 }
78 })
79 @Table({
80 tableName: 'videoChannel',
81 indexes: [
82 {
83 fields: [ 'accountId' ]
84 },
85 {
86 fields: [ 'actorId' ]
87 }
88 ]
89 })
90 export class VideoChannelModel extends Model<VideoChannelModel> {
91
92 @AllowNull(false)
93 @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name'))
94 @Column
95 name: string
96
97 @AllowNull(true)
98 @Default(null)
99 @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description'))
100 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.DESCRIPTION.max))
101 description: string
102
103 @AllowNull(true)
104 @Default(null)
105 @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support'))
106 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max))
107 support: string
108
109 @CreatedAt
110 createdAt: Date
111
112 @UpdatedAt
113 updatedAt: Date
114
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
127 @ForeignKey(() => AccountModel)
128 @Column
129 accountId: number
130
131 @BelongsTo(() => AccountModel, {
132 foreignKey: {
133 allowNull: false
134 },
135 hooks: true
136 })
137 Account: AccountModel
138
139 @HasMany(() => VideoModel, {
140 foreignKey: {
141 name: 'channelId',
142 allowNull: false
143 },
144 onDelete: 'CASCADE',
145 hooks: true
146 })
147 Videos: VideoModel[]
148
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
155 if (instance.Actor.isOwned()) {
156 return sendDeleteActor(instance.Actor, options.transaction)
157 }
158
159 return undefined
160 }
161
162 static countByAccount (accountId: number) {
163 const query = {
164 where: {
165 accountId
166 }
167 }
168
169 return VideoChannelModel.count(query)
170 }
171
172 static listForApi (start: number, count: number, sort: string) {
173 const query = {
174 offset: start,
175 limit: count,
176 order: getSort(sort)
177 }
178
179 return VideoChannelModel
180 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
181 .findAndCountAll(query)
182 .then(({ rows, count }) => {
183 return { total: count, data: rows }
184 })
185 }
186
187 static listByAccount (accountId: number) {
188 const query = {
189 order: getSort('createdAt'),
190 include: [
191 {
192 model: AccountModel,
193 where: {
194 id: accountId
195 },
196 required: true
197 }
198 ]
199 }
200
201 return VideoChannelModel
202 .findAndCountAll(query)
203 .then(({ rows, count }) => {
204 return { total: count, data: rows }
205 })
206 }
207
208 static loadByIdAndAccount (id: number, accountId: number) {
209 const options = {
210 where: {
211 id,
212 accountId
213 }
214 }
215
216 return VideoChannelModel
217 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
218 .findOne(options)
219 }
220
221 static loadAndPopulateAccount (id: number) {
222 return VideoChannelModel
223 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
224 .findById(id)
225 }
226
227 static loadByUUIDAndPopulateAccount (uuid: string) {
228 const options = {
229 include: [
230 {
231 model: ActorModel,
232 required: true,
233 where: {
234 uuid
235 }
236 }
237 ]
238 }
239
240 return VideoChannelModel
241 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT ])
242 .findOne(options)
243 }
244
245 static loadAndPopulateAccountAndVideos (id: number) {
246 const options = {
247 include: [
248 VideoModel
249 ]
250 }
251
252 return VideoChannelModel
253 .scope([ ScopeNames.WITH_ACTOR, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_VIDEOS ])
254 .findById(id, options)
255 }
256
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
274 toFormattedJSON (): VideoChannel {
275 const actor = this.Actor.toFormattedJSON()
276 const videoChannel = {
277 id: this.id,
278 displayName: this.getDisplayName(),
279 description: this.description,
280 support: this.support,
281 isLocal: this.Actor.isOwned(),
282 createdAt: this.createdAt,
283 updatedAt: this.updatedAt,
284 ownerAccount: undefined
285 }
286
287 if (this.Account) videoChannel.ownerAccount = this.Account.toFormattedJSON()
288
289 return Object.assign(actor, videoChannel)
290 }
291
292 toActivityPubObject (): ActivityPubActor {
293 const obj = this.Actor.toActivityPubObject(this.name, 'VideoChannel')
294
295 return Object.assign(obj, {
296 summary: this.description,
297 support: this.support,
298 attributedTo: [
299 {
300 type: 'Person' as 'Person',
301 id: this.Account.Actor.url
302 }
303 ]
304 })
305 }
306
307 getDisplayName () {
308 return this.name
309 }
310 }