]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/api/transcoding/hls.ts
Allow admins to disable two factor auth
[github/Chocobozzz/PeerTube.git] / server / tests / api / transcoding / hls.ts
CommitLineData
a1587156 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
09209296 2
86347717 3import { expect } from 'chai'
764b1a14 4import { basename, join } from 'path'
09209296
C
5import {
6 checkDirectoryIsEmpty,
bd54ad19 7 checkResolutionsInMasterPlaylist,
4c280004 8 checkSegmentHash,
7243f84d 9 checkTmpIsEmpty,
c55e3d72
C
10 expectStartWith,
11 hlsInfohashExist
12} from '@server/tests/shared'
13import { areObjectStorageTestsDisabled, removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
14import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
15import {
7243f84d 16 cleanupTests,
254d3579 17 createMultipleServers,
4c7e60bc 18 doubleFollow,
a1587156 19 makeRawRequest,
0305db28 20 ObjectStorageCommand,
254d3579 21 PeerTubeServer,
a1587156 22 setAccessTokensToServers,
a1587156
C
23 waitJobs,
24 webtorrentAdd
bf54587a 25} from '@shared/server-commands'
b345a804 26import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
09209296 27
0305db28
JB
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) {
89d241a7 41 const videoDetails = await server.videos.get({ id: videoUUID })
d7a25329 42 const baseUrl = `http://${videoDetails.account.host}`
09209296
C
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
d7a25329
C
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
83903cb6 55 // Check JSON files
d7a25329
C
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)
83903cb6
C
61 expect(file.torrentUrl).to.match(
62 new RegExp(`http://${server.host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
63 )
0305db28
JB
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
d7a25329
C
73 expect(file.resolution.label).to.equal(resolution + 'p')
74
f2eb23cd
RK
75 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200)
76 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
d7a25329
C
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
83903cb6 84 // Check master playlist
09209296 85 {
57f879a5 86 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
09209296 87
89d241a7 88 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
09209296 89
fb72d2e1 90 let i = 0
09209296 91 for (const resolution of resolutions) {
52201311 92 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
09209296 93 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
fb72d2e1
C
94
95 const url = 'http://' + videoDetails.account.host
96 await hlsInfohashExist(url, hlsPlaylist.playlistUrl, i)
97
98 i++
09209296
C
99 }
100 }
101
83903cb6 102 // Check resolution playlists
09209296
C
103 {
104 for (const resolution of resolutions) {
764b1a14
C
105 const file = hlsFiles.find(f => f.resolution.id === resolution)
106 const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
107
0305db28 108 const url = objectStorageBaseUrl
cbb1d46b 109 ? `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}`
0305db28
JB
110 : `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
111
112 const subPlaylist = await server.streamingPlaylists.get({ url })
09209296 113
83903cb6
C
114 expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
115 expect(subPlaylist).to.contain(basename(file.fileUrl))
09209296
C
116 }
117 }
118
119 {
0305db28 120 const baseUrlAndPath = objectStorageBaseUrl
cbb1d46b 121 ? objectStorageBaseUrl + 'hls/' + videoUUID
0305db28 122 : baseUrl + '/static/streaming-playlists/hls/' + videoUUID
09209296 123
4c280004 124 for (const resolution of resolutions) {
57f879a5
C
125 await checkSegmentHash({
126 server,
127 baseUrlPlaylist: baseUrlAndPath,
128 baseUrlSegment: baseUrlAndPath,
57f879a5
C
129 resolution,
130 hlsPlaylist
131 })
09209296
C
132 }
133 }
134 }
135}
136
137describe('Test HLS videos', function () {
254d3579 138 let servers: PeerTubeServer[] = []
09209296 139 let videoUUID = ''
b345a804 140 let videoAudioUUID = ''
09209296 141
0305db28 142 function runTestSuite (hlsOnly: boolean, objectStorageBaseUrl?: string) {
dd0ebb71 143
d7a25329
C
144 it('Should upload a video and transcode it to HLS', async function () {
145 this.timeout(120000)
09209296 146
89d241a7 147 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } })
d23dd9fb 148 videoUUID = uuid
09209296 149
d7a25329 150 await waitJobs(servers)
09209296 151
0305db28 152 await checkHlsPlaylist({ servers, videoUUID, hlsOnly, objectStorageBaseUrl })
d7a25329 153 })
09209296 154
d7a25329
C
155 it('Should upload an audio file and transcode it to HLS', async function () {
156 this.timeout(120000)
09209296 157
89d241a7 158 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } })
d23dd9fb 159 videoAudioUUID = uuid
09209296 160
d7a25329 161 await waitJobs(servers)
09209296 162
0305db28
JB
163 await checkHlsPlaylist({
164 servers,
165 videoUUID: videoAudioUUID,
166 hlsOnly,
167 resolutions: [ DEFAULT_AUDIO_RESOLUTION, 360, 240 ],
168 objectStorageBaseUrl
169 })
d7a25329 170 })
09209296 171
d7a25329 172 it('Should update the video', async function () {
cbb1d46b 173 this.timeout(30000)
b345a804 174
89d241a7 175 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video 1 updated' } })
b345a804 176
d7a25329 177 await waitJobs(servers)
b345a804 178
0305db28 179 await checkHlsPlaylist({ servers, videoUUID, hlsOnly, objectStorageBaseUrl })
d7a25329 180 })
b345a804 181
d7a25329
C
182 it('Should delete videos', async function () {
183 this.timeout(10000)
80b8ad2a 184
89d241a7
C
185 await servers[0].videos.remove({ id: videoUUID })
186 await servers[0].videos.remove({ id: videoAudioUUID })
09209296 187
d7a25329 188 await waitJobs(servers)
09209296 189
d7a25329 190 for (const server of servers) {
89d241a7
C
191 await server.videos.get({ id: videoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
192 await server.videos.get({ id: videoAudioUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
d7a25329
C
193 }
194 })
09209296 195
d7a25329
C
196 it('Should have the playlists/segment deleted from the disk', async function () {
197 for (const server of servers) {
198 await checkDirectoryIsEmpty(server, 'videos')
199 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls'))
200 }
201 })
80b8ad2a 202
d7a25329
C
203 it('Should have an empty tmp directory', async function () {
204 for (const server of servers) {
205 await checkTmpIsEmpty(server)
206 }
207 })
208 }
09209296 209
d7a25329
C
210 before(async function () {
211 this.timeout(120000)
09209296 212
d7a25329
C
213 const configOverride = {
214 transcoding: {
215 enabled: true,
216 allow_audio_files: true,
217 hls: {
218 enabled: true
219 }
220 }
09209296 221 }
254d3579 222 servers = await createMultipleServers(2, configOverride)
d7a25329
C
223
224 // Get the access tokens
225 await setAccessTokensToServers(servers)
226
227 // Server 1 and server 2 follow each other
228 await doubleFollow(servers[0], servers[1])
09209296
C
229 })
230
d7a25329
C
231 describe('With WebTorrent & HLS enabled', function () {
232 runTestSuite(false)
09209296
C
233 })
234
d7a25329
C
235 describe('With only HLS enabled', function () {
236
237 before(async function () {
89d241a7 238 await servers[0].config.updateCustomSubConfig({
65e6e260
C
239 newConfig: {
240 transcoding: {
241 enabled: true,
242 allowAudioFiles: true,
243 resolutions: {
8dd754c7 244 '144p': false,
65e6e260
C
245 '240p': true,
246 '360p': true,
247 '480p': true,
248 '720p': true,
249 '1080p': true,
250 '1440p': true,
251 '2160p': true
252 },
253 hls: {
254 enabled: true
255 },
256 webtorrent: {
257 enabled: false
258 }
d7a25329
C
259 }
260 }
261 })
262 })
263
264 runTestSuite(true)
09209296
C
265 })
266
0305db28
JB
267 describe('With object storage enabled', function () {
268 if (areObjectStorageTestsDisabled()) return
269
270 before(async function () {
271 this.timeout(120000)
272
273 const configOverride = ObjectStorageCommand.getDefaultConfig()
274 await ObjectStorageCommand.prepareDefaultBuckets()
275
276 await servers[0].kill()
277 await servers[0].run(configOverride)
278 })
279
280 runTestSuite(true, ObjectStorageCommand.getPlaylistBaseUrl())
281 })
282
7c3b7976
C
283 after(async function () {
284 await cleanupTests(servers)
09209296
C
285 })
286})