redundancy.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import express from 'express'
  2. import { body, param, query } from 'express-validator'
  3. import { forceNumber } from '@peertube/peertube-core-utils'
  4. import { HttpStatusCode } from '@peertube/peertube-models'
  5. import { isVideoRedundancyTarget } from '@server/helpers/custom-validators/video-redundancies.js'
  6. import {
  7. exists,
  8. isBooleanValid,
  9. isIdOrUUIDValid,
  10. isIdValid,
  11. toBooleanOrNull,
  12. toCompleteUUID,
  13. toIntOrNull
  14. } from '../../helpers/custom-validators/misc.js'
  15. import { isHostValid } from '../../helpers/custom-validators/servers.js'
  16. import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy.js'
  17. import { ServerModel } from '../../models/server/server.js'
  18. import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from './shared/index.js'
  19. const videoFileRedundancyGetValidator = [
  20. isValidVideoIdParam('videoId'),
  21. param('resolution')
  22. .customSanitizer(toIntOrNull)
  23. .custom(exists),
  24. param('fps')
  25. .optional()
  26. .customSanitizer(toIntOrNull)
  27. .custom(exists),
  28. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  29. if (areValidationErrors(req, res)) return
  30. if (!await doesVideoExist(req.params.videoId, res)) return
  31. const video = res.locals.videoAll
  32. const paramResolution = req.params.resolution as unknown as number // We casted to int above
  33. const paramFPS = req.params.fps as unknown as number // We casted to int above
  34. const videoFile = video.VideoFiles.find(f => {
  35. return f.resolution === paramResolution && (!req.params.fps || paramFPS)
  36. })
  37. if (!videoFile) {
  38. return res.fail({
  39. status: HttpStatusCode.NOT_FOUND_404,
  40. message: 'Video file not found.'
  41. })
  42. }
  43. res.locals.videoFile = videoFile
  44. const videoRedundancy = await VideoRedundancyModel.loadLocalByFileId(videoFile.id)
  45. if (!videoRedundancy) {
  46. return res.fail({
  47. status: HttpStatusCode.NOT_FOUND_404,
  48. message: 'Video redundancy not found.'
  49. })
  50. }
  51. res.locals.videoRedundancy = videoRedundancy
  52. return next()
  53. }
  54. ]
  55. const videoPlaylistRedundancyGetValidator = [
  56. isValidVideoIdParam('videoId'),
  57. param('streamingPlaylistType')
  58. .customSanitizer(toIntOrNull)
  59. .custom(exists),
  60. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  61. if (areValidationErrors(req, res)) return
  62. if (!await doesVideoExist(req.params.videoId, res)) return
  63. const video = res.locals.videoAll
  64. const paramPlaylistType = req.params.streamingPlaylistType as unknown as number // We casted to int above
  65. const videoStreamingPlaylist = video.VideoStreamingPlaylists.find(p => p.type === paramPlaylistType)
  66. if (!videoStreamingPlaylist) {
  67. return res.fail({
  68. status: HttpStatusCode.NOT_FOUND_404,
  69. message: 'Video playlist not found.'
  70. })
  71. }
  72. res.locals.videoStreamingPlaylist = videoStreamingPlaylist
  73. const videoRedundancy = await VideoRedundancyModel.loadLocalByStreamingPlaylistId(videoStreamingPlaylist.id)
  74. if (!videoRedundancy) {
  75. return res.fail({
  76. status: HttpStatusCode.NOT_FOUND_404,
  77. message: 'Video redundancy not found.'
  78. })
  79. }
  80. res.locals.videoRedundancy = videoRedundancy
  81. return next()
  82. }
  83. ]
  84. const updateServerRedundancyValidator = [
  85. param('host')
  86. .custom(isHostValid),
  87. body('redundancyAllowed')
  88. .customSanitizer(toBooleanOrNull)
  89. .custom(isBooleanValid).withMessage('Should have a valid redundancyAllowed boolean'),
  90. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  91. if (areValidationErrors(req, res)) return
  92. const server = await ServerModel.loadByHost(req.params.host)
  93. if (!server) {
  94. return res.fail({
  95. status: HttpStatusCode.NOT_FOUND_404,
  96. message: `Server ${req.params.host} not found.`
  97. })
  98. }
  99. res.locals.server = server
  100. return next()
  101. }
  102. ]
  103. const listVideoRedundanciesValidator = [
  104. query('target')
  105. .custom(isVideoRedundancyTarget),
  106. (req: express.Request, res: express.Response, next: express.NextFunction) => {
  107. if (areValidationErrors(req, res)) return
  108. return next()
  109. }
  110. ]
  111. const addVideoRedundancyValidator = [
  112. body('videoId')
  113. .customSanitizer(toCompleteUUID)
  114. .custom(isIdOrUUIDValid),
  115. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  116. if (areValidationErrors(req, res)) return
  117. if (!await doesVideoExist(req.body.videoId, res, 'only-video')) return
  118. if (res.locals.onlyVideo.remote === false) {
  119. return res.fail({ message: 'Cannot create a redundancy on a local video' })
  120. }
  121. if (res.locals.onlyVideo.isLive) {
  122. return res.fail({ message: 'Cannot create a redundancy of a live video' })
  123. }
  124. const alreadyExists = await VideoRedundancyModel.isLocalByVideoUUIDExists(res.locals.onlyVideo.uuid)
  125. if (alreadyExists) {
  126. return res.fail({
  127. status: HttpStatusCode.CONFLICT_409,
  128. message: 'This video is already duplicated by your instance.'
  129. })
  130. }
  131. return next()
  132. }
  133. ]
  134. const removeVideoRedundancyValidator = [
  135. param('redundancyId')
  136. .custom(isIdValid),
  137. async (req: express.Request, res: express.Response, next: express.NextFunction) => {
  138. if (areValidationErrors(req, res)) return
  139. const redundancy = await VideoRedundancyModel.loadByIdWithVideo(forceNumber(req.params.redundancyId))
  140. if (!redundancy) {
  141. return res.fail({
  142. status: HttpStatusCode.NOT_FOUND_404,
  143. message: 'Video redundancy not found'
  144. })
  145. }
  146. res.locals.videoRedundancy = redundancy
  147. return next()
  148. }
  149. ]
  150. // ---------------------------------------------------------------------------
  151. export {
  152. videoFileRedundancyGetValidator,
  153. videoPlaylistRedundancyGetValidator,
  154. updateServerRedundancyValidator,
  155. listVideoRedundanciesValidator,
  156. addVideoRedundancyValidator,
  157. removeVideoRedundancyValidator
  158. }