aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api/transcoding/hls.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/tests/api/transcoding/hls.ts')
-rw-r--r--server/tests/api/transcoding/hls.ts163
1 files changed, 24 insertions, 139 deletions
diff --git a/server/tests/api/transcoding/hls.ts b/server/tests/api/transcoding/hls.ts
index 252422e5d..7b5492cd4 100644
--- a/server/tests/api/transcoding/hls.ts
+++ b/server/tests/api/transcoding/hls.ts
@@ -1,168 +1,48 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import { expect } from 'chai' 3import { join } from 'path'
4import { basename, join } from 'path' 4import { checkDirectoryIsEmpty, checkTmpIsEmpty, completeCheckHlsPlaylist } from '@server/tests/shared'
5import { 5import { areObjectStorageTestsDisabled } from '@shared/core-utils'
6 checkDirectoryIsEmpty, 6import { HttpStatusCode } from '@shared/models'
7 checkResolutionsInMasterPlaylist,
8 checkSegmentHash,
9 checkTmpIsEmpty,
10 expectStartWith,
11 hlsInfohashExist
12} from '@server/tests/shared'
13import { areObjectStorageTestsDisabled, removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
14import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
15import { 7import {
16 cleanupTests, 8 cleanupTests,
17 createMultipleServers, 9 createMultipleServers,
18 doubleFollow, 10 doubleFollow,
19 makeRawRequest,
20 ObjectStorageCommand, 11 ObjectStorageCommand,
21 PeerTubeServer, 12 PeerTubeServer,
22 setAccessTokensToServers, 13 setAccessTokensToServers,
23 waitJobs, 14 waitJobs
24 webtorrentAdd
25} from '@shared/server-commands' 15} from '@shared/server-commands'
26import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' 16import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
27 17
28async function checkHlsPlaylist (options: {
29 servers: PeerTubeServer[]
30 videoUUID: string
31 hlsOnly: boolean
32
33 resolutions?: number[]
34 objectStorageBaseUrl: string
35}) {
36 const { videoUUID, hlsOnly, objectStorageBaseUrl } = options
37
38 const resolutions = options.resolutions ?? [ 240, 360, 480, 720 ]
39
40 for (const server of options.servers) {
41 const videoDetails = await server.videos.get({ id: videoUUID })
42 const baseUrl = `http://${videoDetails.account.host}`
43
44 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
45
46 const hlsPlaylist = videoDetails.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
47 expect(hlsPlaylist).to.not.be.undefined
48
49 const hlsFiles = hlsPlaylist.files
50 expect(hlsFiles).to.have.lengthOf(resolutions.length)
51
52 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0)
53 else expect(videoDetails.files).to.have.lengthOf(resolutions.length)
54
55 // Check JSON files
56 for (const resolution of resolutions) {
57 const file = hlsFiles.find(f => f.resolution.id === resolution)
58 expect(file).to.not.be.undefined
59
60 expect(file.magnetUri).to.have.lengthOf.above(2)
61 expect(file.torrentUrl).to.match(
62 new RegExp(`http://${server.host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
63 )
64
65 if (objectStorageBaseUrl) {
66 expectStartWith(file.fileUrl, objectStorageBaseUrl)
67 } else {
68 expect(file.fileUrl).to.match(
69 new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`)
70 )
71 }
72
73 expect(file.resolution.label).to.equal(resolution + 'p')
74
75 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200)
76 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
77
78 const torrent = await webtorrentAdd(file.magnetUri, true)
79 expect(torrent.files).to.be.an('array')
80 expect(torrent.files.length).to.equal(1)
81 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
82 }
83
84 // Check master playlist
85 {
86 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
87
88 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
89
90 let i = 0
91 for (const resolution of resolutions) {
92 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
93 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
94
95 const url = 'http://' + videoDetails.account.host
96 await hlsInfohashExist(url, hlsPlaylist.playlistUrl, i)
97
98 i++
99 }
100 }
101
102 // Check resolution playlists
103 {
104 for (const resolution of resolutions) {
105 const file = hlsFiles.find(f => f.resolution.id === resolution)
106 const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
107
108 const url = objectStorageBaseUrl
109 ? `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}`
110 : `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
111
112 const subPlaylist = await server.streamingPlaylists.get({ url })
113
114 expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
115 expect(subPlaylist).to.contain(basename(file.fileUrl))
116 }
117 }
118
119 {
120 const baseUrlAndPath = objectStorageBaseUrl
121 ? objectStorageBaseUrl + 'hls/' + videoUUID
122 : baseUrl + '/static/streaming-playlists/hls/' + videoUUID
123
124 for (const resolution of resolutions) {
125 await checkSegmentHash({
126 server,
127 baseUrlPlaylist: baseUrlAndPath,
128 baseUrlSegment: baseUrlAndPath,
129 resolution,
130 hlsPlaylist
131 })
132 }
133 }
134 }
135}
136
137describe('Test HLS videos', function () { 18describe('Test HLS videos', function () {
138 let servers: PeerTubeServer[] = [] 19 let servers: PeerTubeServer[] = []
139 let videoUUID = ''
140 let videoAudioUUID = ''
141 20
142 function runTestSuite (hlsOnly: boolean, objectStorageBaseUrl?: string) { 21 function runTestSuite (hlsOnly: boolean, objectStorageBaseUrl?: string) {
22 const videoUUIDs: string[] = []
143 23
144 it('Should upload a video and transcode it to HLS', async function () { 24 it('Should upload a video and transcode it to HLS', async function () {
145 this.timeout(120000) 25 this.timeout(120000)
146 26
147 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } }) 27 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } })
148 videoUUID = uuid 28 videoUUIDs.push(uuid)
149 29
150 await waitJobs(servers) 30 await waitJobs(servers)
151 31
152 await checkHlsPlaylist({ servers, videoUUID, hlsOnly, objectStorageBaseUrl }) 32 await completeCheckHlsPlaylist({ servers, videoUUID: uuid, hlsOnly, objectStorageBaseUrl })
153 }) 33 })
154 34
155 it('Should upload an audio file and transcode it to HLS', async function () { 35 it('Should upload an audio file and transcode it to HLS', async function () {
156 this.timeout(120000) 36 this.timeout(120000)
157 37
158 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } }) 38 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } })
159 videoAudioUUID = uuid 39 videoUUIDs.push(uuid)
160 40
161 await waitJobs(servers) 41 await waitJobs(servers)
162 42
163 await checkHlsPlaylist({ 43 await completeCheckHlsPlaylist({
164 servers, 44 servers,
165 videoUUID: videoAudioUUID, 45 videoUUID: uuid,
166 hlsOnly, 46 hlsOnly,
167 resolutions: [ DEFAULT_AUDIO_RESOLUTION, 360, 240 ], 47 resolutions: [ DEFAULT_AUDIO_RESOLUTION, 360, 240 ],
168 objectStorageBaseUrl 48 objectStorageBaseUrl
@@ -172,31 +52,36 @@ describe('Test HLS videos', function () {
172 it('Should update the video', async function () { 52 it('Should update the video', async function () {
173 this.timeout(30000) 53 this.timeout(30000)
174 54
175 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video 1 updated' } }) 55 await servers[0].videos.update({ id: videoUUIDs[0], attributes: { name: 'video 1 updated' } })
176 56
177 await waitJobs(servers) 57 await waitJobs(servers)
178 58
179 await checkHlsPlaylist({ servers, videoUUID, hlsOnly, objectStorageBaseUrl }) 59 await completeCheckHlsPlaylist({ servers, videoUUID: videoUUIDs[0], hlsOnly, objectStorageBaseUrl })
180 }) 60 })
181 61
182 it('Should delete videos', async function () { 62 it('Should delete videos', async function () {
183 this.timeout(10000) 63 this.timeout(10000)
184 64
185 await servers[0].videos.remove({ id: videoUUID }) 65 for (const uuid of videoUUIDs) {
186 await servers[0].videos.remove({ id: videoAudioUUID }) 66 await servers[0].videos.remove({ id: uuid })
67 }
187 68
188 await waitJobs(servers) 69 await waitJobs(servers)
189 70
190 for (const server of servers) { 71 for (const server of servers) {
191 await server.videos.get({ id: videoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 72 for (const uuid of videoUUIDs) {
192 await server.videos.get({ id: videoAudioUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 73 await server.videos.get({ id: uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
74 }
193 } 75 }
194 }) 76 })
195 77
196 it('Should have the playlists/segment deleted from the disk', async function () { 78 it('Should have the playlists/segment deleted from the disk', async function () {
197 for (const server of servers) { 79 for (const server of servers) {
198 await checkDirectoryIsEmpty(server, 'videos') 80 await checkDirectoryIsEmpty(server, 'videos', [ 'private' ])
199 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls')) 81 await checkDirectoryIsEmpty(server, join('videos', 'private'))
82
83 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls'), [ 'private' ])
84 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls', 'private'))
200 } 85 }
201 }) 86 })
202 87