live-command.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import { readdir } from 'fs-extra'
  3. import { omit } from 'lodash'
  4. import { join } from 'path'
  5. import { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
  6. import { wait } from '../miscs'
  7. import { unwrapBody } from '../requests'
  8. import { AbstractCommand, OverrideCommandOptions } from '../shared'
  9. import { sendRTMPStream, testFfmpegStreamError } from './live'
  10. export class LiveCommand extends AbstractCommand {
  11. get (options: OverrideCommandOptions & {
  12. videoId: number | string
  13. }) {
  14. const path = '/api/v1/videos/live'
  15. return this.getRequestBody<LiveVideo>({
  16. ...options,
  17. path: path + '/' + options.videoId,
  18. implicitToken: true,
  19. defaultExpectedStatus: HttpStatusCode.OK_200
  20. })
  21. }
  22. update (options: OverrideCommandOptions & {
  23. videoId: number | string
  24. fields: LiveVideoUpdate
  25. }) {
  26. const { videoId, fields } = options
  27. const path = '/api/v1/videos/live'
  28. return this.putBodyRequest({
  29. ...options,
  30. path: path + '/' + videoId,
  31. fields,
  32. implicitToken: true,
  33. defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
  34. })
  35. }
  36. async create (options: OverrideCommandOptions & {
  37. fields: LiveVideoCreate
  38. }) {
  39. const { fields } = options
  40. const path = '/api/v1/videos/live'
  41. const attaches: any = {}
  42. if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
  43. if (fields.previewfile) attaches.previewfile = fields.previewfile
  44. const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
  45. ...options,
  46. path,
  47. attaches,
  48. fields: omit(fields, 'thumbnailfile', 'previewfile'),
  49. implicitToken: true,
  50. defaultExpectedStatus: HttpStatusCode.OK_200
  51. }))
  52. return body.video
  53. }
  54. async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
  55. videoId: number | string
  56. fixtureName?: string
  57. copyCodecs?: boolean
  58. }) {
  59. const { videoId, fixtureName, copyCodecs } = options
  60. const videoLive = await this.get({ videoId })
  61. return sendRTMPStream({ rtmpBaseUrl: videoLive.rtmpUrl, streamKey: videoLive.streamKey, fixtureName, copyCodecs })
  62. }
  63. async runAndTestStreamError (options: OverrideCommandOptions & {
  64. videoId: number | string
  65. shouldHaveError: boolean
  66. }) {
  67. const command = await this.sendRTMPStreamInVideo(options)
  68. return testFfmpegStreamError(command, options.shouldHaveError)
  69. }
  70. waitUntilPublished (options: OverrideCommandOptions & {
  71. videoId: number | string
  72. }) {
  73. const { videoId } = options
  74. return this.waitUntilState({ videoId, state: VideoState.PUBLISHED })
  75. }
  76. waitUntilWaiting (options: OverrideCommandOptions & {
  77. videoId: number | string
  78. }) {
  79. const { videoId } = options
  80. return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE })
  81. }
  82. waitUntilEnded (options: OverrideCommandOptions & {
  83. videoId: number | string
  84. }) {
  85. const { videoId } = options
  86. return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED })
  87. }
  88. waitUntilSegmentGeneration (options: OverrideCommandOptions & {
  89. videoUUID: string
  90. resolution: number
  91. segment: number
  92. }) {
  93. const { resolution, segment, videoUUID } = options
  94. const segmentName = `${resolution}-00000${segment}.ts`
  95. return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false)
  96. }
  97. async waitUntilSaved (options: OverrideCommandOptions & {
  98. videoId: number | string
  99. }) {
  100. let video: VideoDetails
  101. do {
  102. video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
  103. await wait(500)
  104. } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
  105. }
  106. async countPlaylists (options: OverrideCommandOptions & {
  107. videoUUID: string
  108. }) {
  109. const basePath = this.server.servers.buildDirectory('streaming-playlists')
  110. const hlsPath = join(basePath, 'hls', options.videoUUID)
  111. const files = await readdir(hlsPath)
  112. return files.filter(f => f.endsWith('.m3u8')).length
  113. }
  114. private async waitUntilState (options: OverrideCommandOptions & {
  115. videoId: number | string
  116. state: VideoState
  117. }) {
  118. let video: VideoDetails
  119. do {
  120. video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
  121. await wait(500)
  122. } while (video.state.id !== options.state)
  123. }
  124. }