video-channel-sync.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { VideoChannelSync, VideoChannelSyncState, type VideoChannelSyncStateType } from '@peertube/peertube-models'
  2. import { isUrlValid } from '@server/helpers/custom-validators/activitypub/misc.js'
  3. import { isVideoChannelSyncStateValid } from '@server/helpers/custom-validators/video-channel-syncs.js'
  4. import { CONSTRAINTS_FIELDS, VIDEO_CHANNEL_SYNC_STATE } from '@server/initializers/constants.js'
  5. import { MChannelSync, MChannelSyncChannel, MChannelSyncFormattable } from '@server/types/models/index.js'
  6. import { Op } from 'sequelize'
  7. import {
  8. AllowNull,
  9. BelongsTo,
  10. Column,
  11. CreatedAt,
  12. DataType,
  13. Default,
  14. DefaultScope,
  15. ForeignKey,
  16. Is, Table,
  17. UpdatedAt
  18. } from 'sequelize-typescript'
  19. import { AccountModel } from '../account/account.js'
  20. import { SequelizeModel, getChannelSyncSort, throwIfNotValid } from '../shared/index.js'
  21. import { UserModel } from '../user/user.js'
  22. import { VideoChannelModel } from './video-channel.js'
  23. @DefaultScope(() => ({
  24. include: [
  25. {
  26. model: VideoChannelModel, // Default scope includes avatar and server
  27. required: true
  28. }
  29. ]
  30. }))
  31. @Table({
  32. tableName: 'videoChannelSync',
  33. indexes: [
  34. {
  35. fields: [ 'videoChannelId' ]
  36. }
  37. ]
  38. })
  39. export class VideoChannelSyncModel extends SequelizeModel<VideoChannelSyncModel> {
  40. @AllowNull(false)
  41. @Default(null)
  42. @Is('VideoChannelExternalChannelUrl', value => throwIfNotValid(value, isUrlValid, 'externalChannelUrl', true))
  43. @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNEL_SYNCS.EXTERNAL_CHANNEL_URL.max))
  44. externalChannelUrl: string
  45. @AllowNull(false)
  46. @Default(VideoChannelSyncState.WAITING_FIRST_RUN)
  47. @Is('VideoChannelSyncState', value => throwIfNotValid(value, isVideoChannelSyncStateValid, 'state'))
  48. @Column
  49. state: VideoChannelSyncStateType
  50. @AllowNull(true)
  51. @Column(DataType.DATE)
  52. lastSyncAt: Date
  53. @CreatedAt
  54. createdAt: Date
  55. @UpdatedAt
  56. updatedAt: Date
  57. @ForeignKey(() => VideoChannelModel)
  58. @Column
  59. videoChannelId: number
  60. @BelongsTo(() => VideoChannelModel, {
  61. foreignKey: {
  62. allowNull: false
  63. },
  64. onDelete: 'cascade'
  65. })
  66. VideoChannel: Awaited<VideoChannelModel>
  67. static listByAccountForAPI (options: {
  68. accountId: number
  69. start: number
  70. count: number
  71. sort: string
  72. }) {
  73. const getQuery = (forCount: boolean) => {
  74. const videoChannelModel = forCount
  75. ? VideoChannelModel.unscoped()
  76. : VideoChannelModel
  77. return {
  78. offset: options.start,
  79. limit: options.count,
  80. order: getChannelSyncSort(options.sort),
  81. include: [
  82. {
  83. model: videoChannelModel,
  84. required: true,
  85. where: {
  86. accountId: options.accountId
  87. }
  88. }
  89. ]
  90. }
  91. }
  92. return Promise.all([
  93. VideoChannelSyncModel.unscoped().count(getQuery(true)),
  94. VideoChannelSyncModel.unscoped().findAll(getQuery(false))
  95. ]).then(([ total, data ]) => ({ total, data }))
  96. }
  97. static countByAccount (accountId: number) {
  98. const query = {
  99. include: [
  100. {
  101. model: VideoChannelModel.unscoped(),
  102. required: true,
  103. where: {
  104. accountId
  105. }
  106. }
  107. ]
  108. }
  109. return VideoChannelSyncModel.unscoped().count(query)
  110. }
  111. static loadWithChannel (id: number): Promise<MChannelSyncChannel> {
  112. return VideoChannelSyncModel.findByPk(id)
  113. }
  114. static async listSyncs (): Promise<MChannelSync[]> {
  115. const query = {
  116. include: [
  117. {
  118. model: VideoChannelModel.unscoped(),
  119. required: true,
  120. include: [
  121. {
  122. model: AccountModel.unscoped(),
  123. required: true,
  124. include: [ {
  125. attributes: [],
  126. model: UserModel.unscoped(),
  127. required: true,
  128. where: {
  129. videoQuota: {
  130. [Op.ne]: 0
  131. },
  132. videoQuotaDaily: {
  133. [Op.ne]: 0
  134. }
  135. }
  136. } ]
  137. }
  138. ]
  139. }
  140. ]
  141. }
  142. return VideoChannelSyncModel.unscoped().findAll(query)
  143. }
  144. toFormattedJSON (this: MChannelSyncFormattable): VideoChannelSync {
  145. return {
  146. id: this.id,
  147. state: {
  148. id: this.state,
  149. label: VIDEO_CHANNEL_SYNC_STATE[this.state]
  150. },
  151. externalChannelUrl: this.externalChannelUrl,
  152. createdAt: this.createdAt.toISOString(),
  153. channel: this.VideoChannel.toFormattedSummaryJSON(),
  154. lastSyncAt: this.lastSyncAt?.toISOString()
  155. }
  156. }
  157. }