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