video-hls.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /* tslint:disable:no-unused-expression */
  2. import * as chai from 'chai'
  3. import 'mocha'
  4. import {
  5. checkDirectoryIsEmpty,
  6. checkSegmentHash,
  7. checkTmpIsEmpty,
  8. cleanupTests,
  9. doubleFollow,
  10. flushAndRunMultipleServers,
  11. getPlaylist,
  12. getVideo, makeGetRequest, makeRawRequest,
  13. removeVideo,
  14. ServerInfo,
  15. setAccessTokensToServers, updateCustomSubConfig,
  16. updateVideo,
  17. uploadVideo,
  18. waitJobs, webtorrentAdd
  19. } from '../../../../shared/extra-utils'
  20. import { VideoDetails } from '../../../../shared/models/videos'
  21. import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
  22. import { join } from 'path'
  23. import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
  24. const expect = chai.expect
  25. async function checkHlsPlaylist (servers: ServerInfo[], videoUUID: string, hlsOnly: boolean, resolutions = [ 240, 360, 480, 720 ]) {
  26. for (const server of servers) {
  27. const resVideoDetails = await getVideo(server.url, videoUUID)
  28. const videoDetails: VideoDetails = resVideoDetails.body
  29. const baseUrl = `http://${videoDetails.account.host}`
  30. expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
  31. const hlsPlaylist = videoDetails.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
  32. expect(hlsPlaylist).to.not.be.undefined
  33. const hlsFiles = hlsPlaylist.files
  34. expect(hlsFiles).to.have.lengthOf(resolutions.length)
  35. if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0)
  36. else expect(videoDetails.files).to.have.lengthOf(resolutions.length)
  37. for (const resolution of resolutions) {
  38. const file = hlsFiles.find(f => f.resolution.id === resolution)
  39. expect(file).to.not.be.undefined
  40. expect(file.magnetUri).to.have.lengthOf.above(2)
  41. expect(file.torrentUrl).to.equal(`${baseUrl}/static/torrents/${videoDetails.uuid}-${file.resolution.id}-hls.torrent`)
  42. expect(file.fileUrl).to.equal(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${videoDetails.uuid}-${file.resolution.id}-fragmented.mp4`)
  43. expect(file.resolution.label).to.equal(resolution + 'p')
  44. await makeRawRequest(file.torrentUrl, 200)
  45. await makeRawRequest(file.fileUrl, 200)
  46. const torrent = await webtorrentAdd(file.magnetUri, true)
  47. expect(torrent.files).to.be.an('array')
  48. expect(torrent.files.length).to.equal(1)
  49. expect(torrent.files[0].path).to.exist.and.to.not.equal('')
  50. }
  51. {
  52. const res = await getPlaylist(hlsPlaylist.playlistUrl)
  53. const masterPlaylist = res.text
  54. for (const resolution of resolutions) {
  55. expect(masterPlaylist).to.match(new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',FRAME-RATE=\\d+'))
  56. expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
  57. }
  58. }
  59. {
  60. for (const resolution of resolutions) {
  61. const res = await getPlaylist(`${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${resolution}.m3u8`)
  62. const subPlaylist = res.text
  63. expect(subPlaylist).to.contain(`${videoUUID}-${resolution}-fragmented.mp4`)
  64. }
  65. }
  66. {
  67. const baseUrlAndPath = baseUrl + '/static/streaming-playlists/hls'
  68. for (const resolution of resolutions) {
  69. await checkSegmentHash(baseUrlAndPath, baseUrlAndPath, videoUUID, resolution, hlsPlaylist)
  70. }
  71. }
  72. }
  73. }
  74. describe('Test HLS videos', function () {
  75. let servers: ServerInfo[] = []
  76. let videoUUID = ''
  77. let videoAudioUUID = ''
  78. function runTestSuite (hlsOnly: boolean) {
  79. it('Should upload a video and transcode it to HLS', async function () {
  80. this.timeout(120000)
  81. const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video 1', fixture: 'video_short.webm' })
  82. videoUUID = res.body.video.uuid
  83. await waitJobs(servers)
  84. await checkHlsPlaylist(servers, videoUUID, hlsOnly)
  85. })
  86. it('Should upload an audio file and transcode it to HLS', async function () {
  87. this.timeout(120000)
  88. const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'video audio', fixture: 'sample.ogg' })
  89. videoAudioUUID = res.body.video.uuid
  90. await waitJobs(servers)
  91. await checkHlsPlaylist(servers, videoAudioUUID, hlsOnly, [ DEFAULT_AUDIO_RESOLUTION ])
  92. })
  93. it('Should update the video', async function () {
  94. this.timeout(10000)
  95. await updateVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, { name: 'video 1 updated' })
  96. await waitJobs(servers)
  97. await checkHlsPlaylist(servers, videoUUID, hlsOnly)
  98. })
  99. it('Should delete videos', async function () {
  100. this.timeout(10000)
  101. await removeVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID)
  102. await removeVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAudioUUID)
  103. await waitJobs(servers)
  104. for (const server of servers) {
  105. await getVideo(server.url, videoUUID, 404)
  106. await getVideo(server.url, videoAudioUUID, 404)
  107. }
  108. })
  109. it('Should have the playlists/segment deleted from the disk', async function () {
  110. for (const server of servers) {
  111. await checkDirectoryIsEmpty(server, 'videos')
  112. await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls'))
  113. }
  114. })
  115. it('Should have an empty tmp directory', async function () {
  116. for (const server of servers) {
  117. await checkTmpIsEmpty(server)
  118. }
  119. })
  120. }
  121. before(async function () {
  122. this.timeout(120000)
  123. const configOverride = {
  124. transcoding: {
  125. enabled: true,
  126. allow_audio_files: true,
  127. hls: {
  128. enabled: true
  129. }
  130. }
  131. }
  132. servers = await flushAndRunMultipleServers(2, configOverride)
  133. // Get the access tokens
  134. await setAccessTokensToServers(servers)
  135. // Server 1 and server 2 follow each other
  136. await doubleFollow(servers[0], servers[1])
  137. })
  138. describe('With WebTorrent & HLS enabled', function () {
  139. runTestSuite(false)
  140. })
  141. describe('With only HLS enabled', function () {
  142. before(async function () {
  143. await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
  144. transcoding: {
  145. enabled: true,
  146. allowAudioFiles: true,
  147. resolutions: {
  148. '240p': true,
  149. '360p': true,
  150. '480p': true,
  151. '720p': true,
  152. '1080p': true,
  153. '2160p': true
  154. },
  155. hls: {
  156. enabled: true
  157. },
  158. webtorrent: {
  159. enabled: false
  160. }
  161. }
  162. })
  163. })
  164. runTestSuite(true)
  165. })
  166. after(async function () {
  167. await cleanupTests(servers)
  168. })
  169. })