]>
Commit | Line | Data |
---|---|---|
72c7248b | 1 | import * as Sequelize from 'sequelize' |
54141398 | 2 | import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers' |
e34c85e5 | 3 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
54141398 C |
4 | import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete' |
5 | ||
6 | import { addMethodsToModel, getSort } from '../utils' | |
7 | import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface' | |
4e50b6a1 C |
8 | import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url' |
9 | import { activityPubCollection } from '../../helpers/activitypub' | |
a2431b7d | 10 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
72c7248b C |
11 | |
12 | let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> | |
13 | let toFormattedJSON: VideoChannelMethods.ToFormattedJSON | |
e4f97bab | 14 | let toActivityPubObject: VideoChannelMethods.ToActivityPubObject |
72c7248b | 15 | let isOwned: VideoChannelMethods.IsOwned |
e4f97bab | 16 | let countByAccount: VideoChannelMethods.CountByAccount |
72c7248b | 17 | let listForApi: VideoChannelMethods.ListForApi |
e4f97bab C |
18 | let listByAccount: VideoChannelMethods.ListByAccount |
19 | let loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount | |
72c7248b | 20 | let loadByUUID: VideoChannelMethods.LoadByUUID |
e4f97bab C |
21 | let loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount |
22 | let loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount | |
72c7248b | 23 | let loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID |
e4f97bab | 24 | let loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos |
0d0e8dd0 C |
25 | let loadByUrl: VideoChannelMethods.LoadByUrl |
26 | let loadByUUIDOrUrl: VideoChannelMethods.LoadByUUIDOrUrl | |
72c7248b C |
27 | |
28 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | |
29 | VideoChannel = sequelize.define<VideoChannelInstance, VideoChannelAttributes>('VideoChannel', | |
30 | { | |
31 | uuid: { | |
32 | type: DataTypes.UUID, | |
33 | defaultValue: DataTypes.UUIDV4, | |
34 | allowNull: false, | |
35 | validate: { | |
36 | isUUID: 4 | |
37 | } | |
38 | }, | |
39 | name: { | |
40 | type: DataTypes.STRING, | |
41 | allowNull: false, | |
42 | validate: { | |
43 | nameValid: value => { | |
44 | const res = isVideoChannelNameValid(value) | |
45 | if (res === false) throw new Error('Video channel name is not valid.') | |
46 | } | |
47 | } | |
48 | }, | |
49 | description: { | |
50 | type: DataTypes.STRING, | |
51 | allowNull: true, | |
52 | validate: { | |
53 | descriptionValid: value => { | |
54 | const res = isVideoChannelDescriptionValid(value) | |
55 | if (res === false) throw new Error('Video channel description is not valid.') | |
56 | } | |
57 | } | |
58 | }, | |
59 | remote: { | |
60 | type: DataTypes.BOOLEAN, | |
61 | allowNull: false, | |
62 | defaultValue: false | |
e4f97bab C |
63 | }, |
64 | url: { | |
e34c85e5 | 65 | type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max), |
e4f97bab C |
66 | allowNull: false, |
67 | validate: { | |
e34c85e5 | 68 | urlValid: value => { |
a2431b7d | 69 | const res = isActivityPubUrlValid(value) |
e34c85e5 C |
70 | if (res === false) throw new Error('Video channel URL is not valid.') |
71 | } | |
e4f97bab | 72 | } |
72c7248b C |
73 | } |
74 | }, | |
75 | { | |
76 | indexes: [ | |
77 | { | |
e4f97bab | 78 | fields: [ 'accountId' ] |
72c7248b C |
79 | } |
80 | ], | |
81 | hooks: { | |
82 | afterDestroy | |
83 | } | |
84 | } | |
85 | ) | |
86 | ||
87 | const classMethods = [ | |
88 | associate, | |
89 | ||
90 | listForApi, | |
e4f97bab | 91 | listByAccount, |
e4f97bab C |
92 | loadByIdAndAccount, |
93 | loadAndPopulateAccount, | |
94 | loadByUUIDAndPopulateAccount, | |
72c7248b C |
95 | loadByUUID, |
96 | loadByHostAndUUID, | |
e4f97bab | 97 | loadAndPopulateAccountAndVideos, |
0d0e8dd0 C |
98 | countByAccount, |
99 | loadByUrl, | |
100 | loadByUUIDOrUrl | |
72c7248b C |
101 | ] |
102 | const instanceMethods = [ | |
103 | isOwned, | |
104 | toFormattedJSON, | |
0d0e8dd0 | 105 | toActivityPubObject |
72c7248b C |
106 | ] |
107 | addMethodsToModel(VideoChannel, classMethods, instanceMethods) | |
108 | ||
109 | return VideoChannel | |
110 | } | |
111 | ||
112 | // ------------------------------ METHODS ------------------------------ | |
113 | ||
114 | isOwned = function (this: VideoChannelInstance) { | |
115 | return this.remote === false | |
116 | } | |
117 | ||
118 | toFormattedJSON = function (this: VideoChannelInstance) { | |
119 | const json = { | |
120 | id: this.id, | |
121 | uuid: this.uuid, | |
122 | name: this.name, | |
123 | description: this.description, | |
124 | isLocal: this.isOwned(), | |
125 | createdAt: this.createdAt, | |
126 | updatedAt: this.updatedAt | |
127 | } | |
128 | ||
e4f97bab | 129 | if (this.Account !== undefined) { |
72c7248b | 130 | json['owner'] = { |
e4f97bab C |
131 | name: this.Account.name, |
132 | uuid: this.Account.uuid | |
72c7248b C |
133 | } |
134 | } | |
135 | ||
136 | if (Array.isArray(this.Videos)) { | |
137 | json['videos'] = this.Videos.map(v => v.toFormattedJSON()) | |
138 | } | |
139 | ||
140 | return json | |
141 | } | |
142 | ||
e4f97bab | 143 | toActivityPubObject = function (this: VideoChannelInstance) { |
4e50b6a1 C |
144 | let sharesObject |
145 | if (Array.isArray(this.VideoChannelShares)) { | |
146 | const shares: string[] = [] | |
147 | ||
148 | for (const videoChannelShare of this.VideoChannelShares) { | |
149 | const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account) | |
150 | shares.push(shareUrl) | |
151 | } | |
152 | ||
153 | sharesObject = activityPubCollection(shares) | |
154 | } | |
155 | ||
72c7248b | 156 | const json = { |
571389d4 C |
157 | type: 'VideoChannel' as 'VideoChannel', |
158 | id: this.url, | |
72c7248b | 159 | uuid: this.uuid, |
571389d4 | 160 | content: this.description, |
72c7248b | 161 | name: this.name, |
efc32059 | 162 | published: this.createdAt.toISOString(), |
4e50b6a1 C |
163 | updated: this.updatedAt.toISOString(), |
164 | shares: sharesObject | |
72c7248b C |
165 | } |
166 | ||
167 | return json | |
168 | } | |
169 | ||
170 | // ------------------------------ STATICS ------------------------------ | |
171 | ||
172 | function associate (models) { | |
e4f97bab | 173 | VideoChannel.belongsTo(models.Account, { |
72c7248b | 174 | foreignKey: { |
e4f97bab | 175 | name: 'accountId', |
72c7248b C |
176 | allowNull: false |
177 | }, | |
178 | onDelete: 'CASCADE' | |
179 | }) | |
180 | ||
181 | VideoChannel.hasMany(models.Video, { | |
182 | foreignKey: { | |
183 | name: 'channelId', | |
184 | allowNull: false | |
185 | }, | |
186 | onDelete: 'CASCADE' | |
187 | }) | |
188 | } | |
189 | ||
911238e3 | 190 | function afterDestroy (videoChannel: VideoChannelInstance) { |
72c7248b | 191 | if (videoChannel.isOwned()) { |
7a7724e6 | 192 | return sendDeleteVideoChannel(videoChannel, undefined) |
72c7248b C |
193 | } |
194 | ||
195 | return undefined | |
196 | } | |
197 | ||
e4f97bab | 198 | countByAccount = function (accountId: number) { |
72c7248b C |
199 | const query = { |
200 | where: { | |
e4f97bab | 201 | accountId |
72c7248b C |
202 | } |
203 | } | |
204 | ||
205 | return VideoChannel.count(query) | |
206 | } | |
207 | ||
72c7248b C |
208 | listForApi = function (start: number, count: number, sort: string) { |
209 | const query = { | |
210 | offset: start, | |
211 | limit: count, | |
212 | order: [ getSort(sort) ], | |
213 | include: [ | |
214 | { | |
e4f97bab | 215 | model: VideoChannel['sequelize'].models.Account, |
72c7248b | 216 | required: true, |
60862425 | 217 | include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ] |
72c7248b C |
218 | } |
219 | ] | |
220 | } | |
221 | ||
222 | return VideoChannel.findAndCountAll(query).then(({ rows, count }) => { | |
223 | return { total: count, data: rows } | |
224 | }) | |
225 | } | |
226 | ||
e4f97bab | 227 | listByAccount = function (accountId: number) { |
72c7248b C |
228 | const query = { |
229 | order: [ getSort('createdAt') ], | |
230 | include: [ | |
231 | { | |
e4f97bab | 232 | model: VideoChannel['sequelize'].models.Account, |
72c7248b | 233 | where: { |
e4f97bab | 234 | id: accountId |
72c7248b C |
235 | }, |
236 | required: true, | |
60862425 | 237 | include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ] |
72c7248b C |
238 | } |
239 | ] | |
240 | } | |
241 | ||
242 | return VideoChannel.findAndCountAll(query).then(({ rows, count }) => { | |
243 | return { total: count, data: rows } | |
244 | }) | |
245 | } | |
246 | ||
247 | loadByUUID = function (uuid: string, t?: Sequelize.Transaction) { | |
248 | const query: Sequelize.FindOptions<VideoChannelAttributes> = { | |
249 | where: { | |
250 | uuid | |
251 | } | |
252 | } | |
253 | ||
254 | if (t !== undefined) query.transaction = t | |
255 | ||
256 | return VideoChannel.findOne(query) | |
257 | } | |
258 | ||
0d0e8dd0 C |
259 | loadByUrl = function (url: string, t?: Sequelize.Transaction) { |
260 | const query: Sequelize.FindOptions<VideoChannelAttributes> = { | |
261 | where: { | |
262 | url | |
20494f12 C |
263 | }, |
264 | include: [ VideoChannel['sequelize'].models.Account ] | |
0d0e8dd0 C |
265 | } |
266 | ||
267 | if (t !== undefined) query.transaction = t | |
268 | ||
269 | return VideoChannel.findOne(query) | |
270 | } | |
271 | ||
272 | loadByUUIDOrUrl = function (uuid: string, url: string, t?: Sequelize.Transaction) { | |
273 | const query: Sequelize.FindOptions<VideoChannelAttributes> = { | |
274 | where: { | |
275 | [Sequelize.Op.or]: [ | |
276 | { uuid }, | |
277 | { url } | |
278 | ] | |
571389d4 | 279 | } |
0d0e8dd0 C |
280 | } |
281 | ||
282 | if (t !== undefined) query.transaction = t | |
283 | ||
284 | return VideoChannel.findOne(query) | |
285 | } | |
286 | ||
72c7248b C |
287 | loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Transaction) { |
288 | const query: Sequelize.FindOptions<VideoChannelAttributes> = { | |
289 | where: { | |
290 | uuid | |
291 | }, | |
292 | include: [ | |
293 | { | |
e4f97bab | 294 | model: VideoChannel['sequelize'].models.Account, |
72c7248b C |
295 | include: [ |
296 | { | |
60862425 | 297 | model: VideoChannel['sequelize'].models.Server, |
72c7248b C |
298 | required: true, |
299 | where: { | |
300 | host: fromHost | |
301 | } | |
302 | } | |
303 | ] | |
304 | } | |
305 | ] | |
306 | } | |
307 | ||
308 | if (t !== undefined) query.transaction = t | |
309 | ||
310 | return VideoChannel.findOne(query) | |
311 | } | |
312 | ||
e4f97bab | 313 | loadByIdAndAccount = function (id: number, accountId: number) { |
72c7248b C |
314 | const options = { |
315 | where: { | |
316 | id, | |
e4f97bab | 317 | accountId |
72c7248b C |
318 | }, |
319 | include: [ | |
320 | { | |
e4f97bab | 321 | model: VideoChannel['sequelize'].models.Account, |
60862425 | 322 | include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ] |
72c7248b C |
323 | } |
324 | ] | |
325 | } | |
326 | ||
327 | return VideoChannel.findOne(options) | |
328 | } | |
329 | ||
e4f97bab | 330 | loadAndPopulateAccount = function (id: number) { |
72c7248b C |
331 | const options = { |
332 | include: [ | |
333 | { | |
e4f97bab | 334 | model: VideoChannel['sequelize'].models.Account, |
60862425 | 335 | include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ] |
72c7248b C |
336 | } |
337 | ] | |
338 | } | |
339 | ||
340 | return VideoChannel.findById(id, options) | |
341 | } | |
342 | ||
e4f97bab | 343 | loadByUUIDAndPopulateAccount = function (uuid: string) { |
72c7248b C |
344 | const options = { |
345 | where: { | |
346 | uuid | |
347 | }, | |
348 | include: [ | |
349 | { | |
e4f97bab | 350 | model: VideoChannel['sequelize'].models.Account, |
60862425 | 351 | include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ] |
72c7248b C |
352 | } |
353 | ] | |
354 | } | |
355 | ||
356 | return VideoChannel.findOne(options) | |
357 | } | |
358 | ||
e4f97bab | 359 | loadAndPopulateAccountAndVideos = function (id: number) { |
72c7248b C |
360 | const options = { |
361 | include: [ | |
362 | { | |
e4f97bab | 363 | model: VideoChannel['sequelize'].models.Account, |
60862425 | 364 | include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ] |
72c7248b C |
365 | }, |
366 | VideoChannel['sequelize'].models.Video | |
367 | ] | |
368 | } | |
369 | ||
370 | return VideoChannel.findById(id, options) | |
371 | } |