1 import * as Sequelize from 'sequelize'
2 import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers'
3 import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
4 import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete'
6 import { addMethodsToModel, getSort } from '../utils'
7 import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
8 import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
9 import { activityPubCollection } from '../../helpers/activitypub'
10 import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
12 let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
13 let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
14 let toActivityPubObject: VideoChannelMethods.ToActivityPubObject
15 let isOwned: VideoChannelMethods.IsOwned
16 let countByAccount: VideoChannelMethods.CountByAccount
17 let listForApi: VideoChannelMethods.ListForApi
18 let listByAccount: VideoChannelMethods.ListByAccount
19 let loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount
20 let loadByUUID: VideoChannelMethods.LoadByUUID
21 let loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount
22 let loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount
23 let loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID
24 let loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos
25 let loadByUrl: VideoChannelMethods.LoadByUrl
26 let loadByUUIDOrUrl: VideoChannelMethods.LoadByUUIDOrUrl
28 export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
29 VideoChannel = sequelize.define<VideoChannelInstance, VideoChannelAttributes>('VideoChannel',
33 defaultValue: DataTypes.UUIDV4,
40 type: DataTypes.STRING,
44 const res = isVideoChannelNameValid(value)
45 if (res === false) throw new Error('Video channel name is not valid.')
50 type: DataTypes.STRING,
53 descriptionValid: value => {
54 const res = isVideoChannelDescriptionValid(value)
55 if (res === false) throw new Error('Video channel description is not valid.')
60 type: DataTypes.BOOLEAN,
65 type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max),
69 const res = isActivityPubUrlValid(value)
70 if (res === false) throw new Error('Video channel URL is not valid.')
78 fields: [ 'accountId' ]
87 const classMethods = [
93 loadAndPopulateAccount,
94 loadByUUIDAndPopulateAccount,
97 loadAndPopulateAccountAndVideos,
102 const instanceMethods = [
107 addMethodsToModel(VideoChannel, classMethods, instanceMethods)
112 // ------------------------------ METHODS ------------------------------
114 isOwned = function (this: VideoChannelInstance) {
115 return this.remote === false
118 toFormattedJSON = function (this: VideoChannelInstance) {
123 description: this.description,
124 isLocal: this.isOwned(),
125 createdAt: this.createdAt,
126 updatedAt: this.updatedAt
129 if (this.Account !== undefined) {
131 name: this.Account.name,
132 uuid: this.Account.uuid
136 if (Array.isArray(this.Videos)) {
137 json['videos'] = this.Videos.map(v => v.toFormattedJSON())
143 toActivityPubObject = function (this: VideoChannelInstance) {
145 if (Array.isArray(this.VideoChannelShares)) {
146 const shares: string[] = []
148 for (const videoChannelShare of this.VideoChannelShares) {
149 const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account)
150 shares.push(shareUrl)
153 sharesObject = activityPubCollection(shares)
157 type: 'VideoChannel' as 'VideoChannel',
160 content: this.description,
162 published: this.createdAt.toISOString(),
163 updated: this.updatedAt.toISOString(),
170 // ------------------------------ STATICS ------------------------------
172 function associate (models) {
173 VideoChannel.belongsTo(models.Account, {
181 VideoChannel.hasMany(models.Video, {
190 function afterDestroy (videoChannel: VideoChannelInstance) {
191 if (videoChannel.isOwned()) {
192 return sendDeleteVideoChannel(videoChannel, undefined)
198 countByAccount = function (accountId: number) {
205 return VideoChannel.count(query)
208 listForApi = function (start: number, count: number, sort: string) {
212 order: [ getSort(sort) ],
215 model: VideoChannel['sequelize'].models.Account,
217 include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
222 return VideoChannel.findAndCountAll(query).then(({ rows, count }) => {
223 return { total: count, data: rows }
227 listByAccount = function (accountId: number) {
229 order: [ getSort('createdAt') ],
232 model: VideoChannel['sequelize'].models.Account,
237 include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
242 return VideoChannel.findAndCountAll(query).then(({ rows, count }) => {
243 return { total: count, data: rows }
247 loadByUUID = function (uuid: string, t?: Sequelize.Transaction) {
248 const query: Sequelize.FindOptions<VideoChannelAttributes> = {
254 if (t !== undefined) query.transaction = t
256 return VideoChannel.findOne(query)
259 loadByUrl = function (url: string, t?: Sequelize.Transaction) {
260 const query: Sequelize.FindOptions<VideoChannelAttributes> = {
264 include: [ VideoChannel['sequelize'].models.Account ]
267 if (t !== undefined) query.transaction = t
269 return VideoChannel.findOne(query)
272 loadByUUIDOrUrl = function (uuid: string, url: string, t?: Sequelize.Transaction) {
273 const query: Sequelize.FindOptions<VideoChannelAttributes> = {
282 if (t !== undefined) query.transaction = t
284 return VideoChannel.findOne(query)
287 loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Transaction) {
288 const query: Sequelize.FindOptions<VideoChannelAttributes> = {
294 model: VideoChannel['sequelize'].models.Account,
297 model: VideoChannel['sequelize'].models.Server,
308 if (t !== undefined) query.transaction = t
310 return VideoChannel.findOne(query)
313 loadByIdAndAccount = function (id: number, accountId: number) {
321 model: VideoChannel['sequelize'].models.Account,
322 include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
327 return VideoChannel.findOne(options)
330 loadAndPopulateAccount = function (id: number) {
334 model: VideoChannel['sequelize'].models.Account,
335 include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
340 return VideoChannel.findById(id, options)
343 loadByUUIDAndPopulateAccount = function (uuid: string) {
350 model: VideoChannel['sequelize'].models.Account,
351 include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
356 return VideoChannel.findOne(options)
359 loadAndPopulateAccountAndVideos = function (id: number) {
363 model: VideoChannel['sequelize'].models.Account,
364 include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
366 VideoChannel['sequelize'].models.Video
370 return VideoChannel.findById(id, options)