video-file.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import {
  2. AllowNull,
  3. BelongsTo,
  4. Column,
  5. CreatedAt,
  6. DataType,
  7. Default,
  8. ForeignKey,
  9. HasMany,
  10. Is,
  11. Model,
  12. Table,
  13. UpdatedAt
  14. } from 'sequelize-typescript'
  15. import {
  16. isVideoFileExtnameValid,
  17. isVideoFileInfoHashValid,
  18. isVideoFileResolutionValid,
  19. isVideoFileSizeValid,
  20. isVideoFPSResolutionValid
  21. } from '../../helpers/custom-validators/videos'
  22. import { parseAggregateResult, throwIfNotValid } from '../utils'
  23. import { VideoModel } from './video'
  24. import { VideoRedundancyModel } from '../redundancy/video-redundancy'
  25. import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
  26. import { FindOptions, QueryTypes, Transaction } from 'sequelize'
  27. import { MIMETYPES } from '../../initializers/constants'
  28. @Table({
  29. tableName: 'videoFile',
  30. indexes: [
  31. {
  32. fields: [ 'videoId' ]
  33. },
  34. {
  35. fields: [ 'infoHash' ]
  36. },
  37. {
  38. fields: [ 'videoId', 'resolution', 'fps' ],
  39. unique: true
  40. }
  41. ]
  42. })
  43. export class VideoFileModel extends Model<VideoFileModel> {
  44. @CreatedAt
  45. createdAt: Date
  46. @UpdatedAt
  47. updatedAt: Date
  48. @AllowNull(false)
  49. @Is('VideoFileResolution', value => throwIfNotValid(value, isVideoFileResolutionValid, 'resolution'))
  50. @Column
  51. resolution: number
  52. @AllowNull(false)
  53. @Is('VideoFileSize', value => throwIfNotValid(value, isVideoFileSizeValid, 'size'))
  54. @Column(DataType.BIGINT)
  55. size: number
  56. @AllowNull(false)
  57. @Is('VideoFileExtname', value => throwIfNotValid(value, isVideoFileExtnameValid, 'extname'))
  58. @Column
  59. extname: string
  60. @AllowNull(false)
  61. @Is('VideoFileInfohash', value => throwIfNotValid(value, isVideoFileInfoHashValid, 'info hash'))
  62. @Column
  63. infoHash: string
  64. @AllowNull(false)
  65. @Default(-1)
  66. @Is('VideoFileFPS', value => throwIfNotValid(value, isVideoFPSResolutionValid, 'fps'))
  67. @Column
  68. fps: number
  69. @ForeignKey(() => VideoModel)
  70. @Column
  71. videoId: number
  72. @BelongsTo(() => VideoModel, {
  73. foreignKey: {
  74. allowNull: false
  75. },
  76. onDelete: 'CASCADE'
  77. })
  78. Video: VideoModel
  79. @HasMany(() => VideoRedundancyModel, {
  80. foreignKey: {
  81. allowNull: true
  82. },
  83. onDelete: 'CASCADE',
  84. hooks: true
  85. })
  86. RedundancyVideos: VideoRedundancyModel[]
  87. static doesInfohashExist (infoHash: string) {
  88. const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1'
  89. const options = {
  90. type: QueryTypes.SELECT,
  91. bind: { infoHash },
  92. raw: true
  93. }
  94. return VideoModel.sequelize.query(query, options)
  95. .then(results => results.length === 1)
  96. }
  97. static loadWithVideo (id: number) {
  98. const options = {
  99. include: [
  100. {
  101. model: VideoModel.unscoped(),
  102. required: true
  103. }
  104. ]
  105. }
  106. return VideoFileModel.findByPk(id, options)
  107. }
  108. static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Transaction) {
  109. const query = {
  110. include: [
  111. {
  112. model: VideoModel.unscoped(),
  113. required: true,
  114. include: [
  115. {
  116. model: VideoStreamingPlaylistModel.unscoped(),
  117. required: true,
  118. where: {
  119. id: streamingPlaylistId
  120. }
  121. }
  122. ]
  123. }
  124. ],
  125. transaction
  126. }
  127. return VideoFileModel.findAll(query)
  128. }
  129. static getStats () {
  130. const query: FindOptions = {
  131. include: [
  132. {
  133. attributes: [],
  134. model: VideoModel.unscoped(),
  135. where: {
  136. remote: false
  137. }
  138. }
  139. ]
  140. }
  141. return VideoFileModel.aggregate('size', 'SUM', query)
  142. .then(result => ({
  143. totalLocalVideoFilesSize: parseAggregateResult(result)
  144. }))
  145. }
  146. isAudio () {
  147. return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
  148. }
  149. hasSameUniqueKeysThan (other: VideoFileModel) {
  150. return this.fps === other.fps &&
  151. this.resolution === other.resolution &&
  152. this.videoId === other.videoId
  153. }
  154. }