]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/transcoding/transcoder.ts
Merge branch 'release/5.1.0' into develop
[github/Chocobozzz/PeerTube.git] / server / tests / api / transcoding / transcoder.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { expect } from 'chai'
4 import { canDoQuickTranscode } from '@server/helpers/ffmpeg'
5 import { generateHighBitrateVideo, generateVideoWithFramerate } from '@server/tests/shared'
6 import { buildAbsoluteFixturePath, getAllFiles, getMaxBitrate, getMinLimitBitrate, omit } from '@shared/core-utils'
7 import {
8 buildFileMetadata,
9 getAudioStream,
10 getVideoStreamBitrate,
11 getVideoStreamDimensionsInfo,
12 getVideoStreamFPS,
13 hasAudioStream
14 } from '@shared/extra-utils'
15 import { HttpStatusCode, VideoState } from '@shared/models'
16 import {
17 cleanupTests,
18 createMultipleServers,
19 doubleFollow,
20 makeGetRequest,
21 PeerTubeServer,
22 setAccessTokensToServers,
23 waitJobs,
24 webtorrentAdd
25 } from '@shared/server-commands'
26
27 function updateConfigForTranscoding (server: PeerTubeServer) {
28 return server.config.updateCustomSubConfig({
29 newConfig: {
30 transcoding: {
31 enabled: true,
32 allowAdditionalExtensions: true,
33 allowAudioFiles: true,
34 hls: { enabled: true },
35 webtorrent: { enabled: true },
36 resolutions: {
37 '0p': false,
38 '144p': true,
39 '240p': true,
40 '360p': true,
41 '480p': true,
42 '720p': true,
43 '1080p': true,
44 '1440p': true,
45 '2160p': true
46 }
47 }
48 }
49 })
50 }
51
52 describe('Test video transcoding', function () {
53 let servers: PeerTubeServer[] = []
54 let video4k: string
55
56 before(async function () {
57 this.timeout(30_000)
58
59 // Run servers
60 servers = await createMultipleServers(2)
61
62 await setAccessTokensToServers(servers)
63
64 await doubleFollow(servers[0], servers[1])
65
66 await updateConfigForTranscoding(servers[1])
67 })
68
69 describe('Basic transcoding (or not)', function () {
70
71 it('Should not transcode video on server 1', async function () {
72 this.timeout(60_000)
73
74 const attributes = {
75 name: 'my super name for server 1',
76 description: 'my super description for server 1',
77 fixture: 'video_short.webm'
78 }
79 await servers[0].videos.upload({ attributes })
80
81 await waitJobs(servers)
82
83 for (const server of servers) {
84 const { data } = await server.videos.list()
85 const video = data[0]
86
87 const videoDetails = await server.videos.get({ id: video.id })
88 expect(videoDetails.files).to.have.lengthOf(1)
89
90 const magnetUri = videoDetails.files[0].magnetUri
91 expect(magnetUri).to.match(/\.webm/)
92
93 const torrent = await webtorrentAdd(magnetUri, true)
94 expect(torrent.files).to.be.an('array')
95 expect(torrent.files.length).to.equal(1)
96 expect(torrent.files[0].path).match(/\.webm$/)
97 }
98 })
99
100 it('Should transcode video on server 2', async function () {
101 this.timeout(120_000)
102
103 const attributes = {
104 name: 'my super name for server 2',
105 description: 'my super description for server 2',
106 fixture: 'video_short.webm'
107 }
108 await servers[1].videos.upload({ attributes })
109
110 await waitJobs(servers)
111
112 for (const server of servers) {
113 const { data } = await server.videos.list()
114
115 const video = data.find(v => v.name === attributes.name)
116 const videoDetails = await server.videos.get({ id: video.id })
117
118 expect(videoDetails.files).to.have.lengthOf(5)
119
120 const magnetUri = videoDetails.files[0].magnetUri
121 expect(magnetUri).to.match(/\.mp4/)
122
123 const torrent = await webtorrentAdd(magnetUri, true)
124 expect(torrent.files).to.be.an('array')
125 expect(torrent.files.length).to.equal(1)
126 expect(torrent.files[0].path).match(/\.mp4$/)
127 }
128 })
129
130 it('Should wait for transcoding before publishing the video', async function () {
131 this.timeout(160_000)
132
133 {
134 // Upload the video, but wait transcoding
135 const attributes = {
136 name: 'waiting video',
137 fixture: 'video_short1.webm',
138 waitTranscoding: true
139 }
140 const { uuid } = await servers[1].videos.upload({ attributes })
141 const videoId = uuid
142
143 // Should be in transcode state
144 const body = await servers[1].videos.get({ id: videoId })
145 expect(body.name).to.equal('waiting video')
146 expect(body.state.id).to.equal(VideoState.TO_TRANSCODE)
147 expect(body.state.label).to.equal('To transcode')
148 expect(body.waitTranscoding).to.be.true
149
150 {
151 // Should have my video
152 const { data } = await servers[1].videos.listMyVideos()
153 const videoToFindInMine = data.find(v => v.name === attributes.name)
154 expect(videoToFindInMine).not.to.be.undefined
155 expect(videoToFindInMine.state.id).to.equal(VideoState.TO_TRANSCODE)
156 expect(videoToFindInMine.state.label).to.equal('To transcode')
157 expect(videoToFindInMine.waitTranscoding).to.be.true
158 }
159
160 {
161 // Should not list this video
162 const { data } = await servers[1].videos.list()
163 const videoToFindInList = data.find(v => v.name === attributes.name)
164 expect(videoToFindInList).to.be.undefined
165 }
166
167 // Server 1 should not have the video yet
168 await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
169 }
170
171 await waitJobs(servers)
172
173 for (const server of servers) {
174 const { data } = await server.videos.list()
175 const videoToFind = data.find(v => v.name === 'waiting video')
176 expect(videoToFind).not.to.be.undefined
177
178 const videoDetails = await server.videos.get({ id: videoToFind.id })
179
180 expect(videoDetails.state.id).to.equal(VideoState.PUBLISHED)
181 expect(videoDetails.state.label).to.equal('Published')
182 expect(videoDetails.waitTranscoding).to.be.true
183 }
184 })
185
186 it('Should accept and transcode additional extensions', async function () {
187 this.timeout(300_000)
188
189 for (const fixture of [ 'video_short.mkv', 'video_short.avi' ]) {
190 const attributes = {
191 name: fixture,
192 fixture
193 }
194
195 await servers[1].videos.upload({ attributes })
196
197 await waitJobs(servers)
198
199 for (const server of servers) {
200 const { data } = await server.videos.list()
201
202 const video = data.find(v => v.name === attributes.name)
203 const videoDetails = await server.videos.get({ id: video.id })
204 expect(videoDetails.files).to.have.lengthOf(5)
205
206 const magnetUri = videoDetails.files[0].magnetUri
207 expect(magnetUri).to.contain('.mp4')
208 }
209 }
210 })
211
212 it('Should transcode a 4k video', async function () {
213 this.timeout(200_000)
214
215 const attributes = {
216 name: '4k video',
217 fixture: 'video_short_4k.mp4'
218 }
219
220 const { uuid } = await servers[1].videos.upload({ attributes })
221 video4k = uuid
222
223 await waitJobs(servers)
224
225 const resolutions = [ 144, 240, 360, 480, 720, 1080, 1440, 2160 ]
226
227 for (const server of servers) {
228 const videoDetails = await server.videos.get({ id: video4k })
229 expect(videoDetails.files).to.have.lengthOf(resolutions.length)
230
231 for (const r of resolutions) {
232 expect(videoDetails.files.find(f => f.resolution.id === r)).to.not.be.undefined
233 expect(videoDetails.streamingPlaylists[0].files.find(f => f.resolution.id === r)).to.not.be.undefined
234 }
235 }
236 })
237 })
238
239 describe('Audio transcoding', function () {
240
241 it('Should transcode high bit rate mp3 to proper bit rate', async function () {
242 this.timeout(60_000)
243
244 const attributes = {
245 name: 'mp3_256k',
246 fixture: 'video_short_mp3_256k.mp4'
247 }
248 await servers[1].videos.upload({ attributes })
249
250 await waitJobs(servers)
251
252 for (const server of servers) {
253 const { data } = await server.videos.list()
254
255 const video = data.find(v => v.name === attributes.name)
256 const videoDetails = await server.videos.get({ id: video.id })
257
258 expect(videoDetails.files).to.have.lengthOf(5)
259
260 const file = videoDetails.files.find(f => f.resolution.id === 240)
261 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
262 const probe = await getAudioStream(path)
263
264 if (probe.audioStream) {
265 expect(probe.audioStream['codec_name']).to.be.equal('aac')
266 expect(probe.audioStream['bit_rate']).to.be.at.most(384 * 8000)
267 } else {
268 this.fail('Could not retrieve the audio stream on ' + probe.absolutePath)
269 }
270 }
271 })
272
273 it('Should transcode video with no audio and have no audio itself', async function () {
274 this.timeout(60_000)
275
276 const attributes = {
277 name: 'no_audio',
278 fixture: 'video_short_no_audio.mp4'
279 }
280 await servers[1].videos.upload({ attributes })
281
282 await waitJobs(servers)
283
284 for (const server of servers) {
285 const { data } = await server.videos.list()
286
287 const video = data.find(v => v.name === attributes.name)
288 const videoDetails = await server.videos.get({ id: video.id })
289
290 const file = videoDetails.files.find(f => f.resolution.id === 240)
291 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
292
293 expect(await hasAudioStream(path)).to.be.false
294 }
295 })
296
297 it('Should leave the audio untouched, but properly transcode the video', async function () {
298 this.timeout(60_000)
299
300 const attributes = {
301 name: 'untouched_audio',
302 fixture: 'video_short.mp4'
303 }
304 await servers[1].videos.upload({ attributes })
305
306 await waitJobs(servers)
307
308 for (const server of servers) {
309 const { data } = await server.videos.list()
310
311 const video = data.find(v => v.name === attributes.name)
312 const videoDetails = await server.videos.get({ id: video.id })
313
314 expect(videoDetails.files).to.have.lengthOf(5)
315
316 const fixturePath = buildAbsoluteFixturePath(attributes.fixture)
317 const fixtureVideoProbe = await getAudioStream(fixturePath)
318
319 const file = videoDetails.files.find(f => f.resolution.id === 240)
320 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
321
322 const videoProbe = await getAudioStream(path)
323
324 if (videoProbe.audioStream && fixtureVideoProbe.audioStream) {
325 const toOmit = [ 'max_bit_rate', 'duration', 'duration_ts', 'nb_frames', 'start_time', 'start_pts' ]
326 expect(omit(videoProbe.audioStream, toOmit)).to.be.deep.equal(omit(fixtureVideoProbe.audioStream, toOmit))
327 } else {
328 this.fail('Could not retrieve the audio stream on ' + videoProbe.absolutePath)
329 }
330 }
331 })
332 })
333
334 describe('Audio upload', function () {
335
336 function runSuite (mode: 'legacy' | 'resumable') {
337
338 before(async function () {
339 await servers[1].config.updateCustomSubConfig({
340 newConfig: {
341 transcoding: {
342 hls: { enabled: true },
343 webtorrent: { enabled: true },
344 resolutions: {
345 '0p': false,
346 '144p': false,
347 '240p': false,
348 '360p': false,
349 '480p': false,
350 '720p': false,
351 '1080p': false,
352 '1440p': false,
353 '2160p': false
354 }
355 }
356 }
357 })
358 })
359
360 it('Should merge an audio file with the preview file', async function () {
361 this.timeout(60_000)
362
363 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
364 await servers[1].videos.upload({ attributes, mode })
365
366 await waitJobs(servers)
367
368 for (const server of servers) {
369 const { data } = await server.videos.list()
370
371 const video = data.find(v => v.name === 'audio_with_preview')
372 const videoDetails = await server.videos.get({ id: video.id })
373
374 expect(videoDetails.files).to.have.lengthOf(1)
375
376 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
377 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
378
379 const magnetUri = videoDetails.files[0].magnetUri
380 expect(magnetUri).to.contain('.mp4')
381 }
382 })
383
384 it('Should upload an audio file and choose a default background image', async function () {
385 this.timeout(60_000)
386
387 const attributes = { name: 'audio_without_preview', fixture: 'sample.ogg' }
388 await servers[1].videos.upload({ attributes, mode })
389
390 await waitJobs(servers)
391
392 for (const server of servers) {
393 const { data } = await server.videos.list()
394
395 const video = data.find(v => v.name === 'audio_without_preview')
396 const videoDetails = await server.videos.get({ id: video.id })
397
398 expect(videoDetails.files).to.have.lengthOf(1)
399
400 await makeGetRequest({ url: server.url, path: videoDetails.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
401 await makeGetRequest({ url: server.url, path: videoDetails.previewPath, expectedStatus: HttpStatusCode.OK_200 })
402
403 const magnetUri = videoDetails.files[0].magnetUri
404 expect(magnetUri).to.contain('.mp4')
405 }
406 })
407
408 it('Should upload an audio file and create an audio version only', async function () {
409 this.timeout(60_000)
410
411 await servers[1].config.updateCustomSubConfig({
412 newConfig: {
413 transcoding: {
414 hls: { enabled: true },
415 webtorrent: { enabled: true },
416 resolutions: {
417 '0p': true,
418 '144p': false,
419 '240p': false,
420 '360p': false
421 }
422 }
423 }
424 })
425
426 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' }
427 const { id } = await servers[1].videos.upload({ attributes, mode })
428
429 await waitJobs(servers)
430
431 for (const server of servers) {
432 const videoDetails = await server.videos.get({ id })
433
434 for (const files of [ videoDetails.files, videoDetails.streamingPlaylists[0].files ]) {
435 expect(files).to.have.lengthOf(2)
436 expect(files.find(f => f.resolution.id === 0)).to.not.be.undefined
437 }
438 }
439
440 await updateConfigForTranscoding(servers[1])
441 })
442 }
443
444 describe('Legacy upload', function () {
445 runSuite('legacy')
446 })
447
448 describe('Resumable upload', function () {
449 runSuite('resumable')
450 })
451 })
452
453 describe('Framerate', function () {
454
455 it('Should transcode a 60 FPS video', async function () {
456 this.timeout(60_000)
457
458 const attributes = {
459 name: 'my super 30fps name for server 2',
460 description: 'my super 30fps description for server 2',
461 fixture: '60fps_720p_small.mp4'
462 }
463 await servers[1].videos.upload({ attributes })
464
465 await waitJobs(servers)
466
467 for (const server of servers) {
468 const { data } = await server.videos.list()
469
470 const video = data.find(v => v.name === attributes.name)
471 const videoDetails = await server.videos.get({ id: video.id })
472
473 expect(videoDetails.files).to.have.lengthOf(5)
474 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62)
475 expect(videoDetails.files[1].fps).to.be.below(31)
476 expect(videoDetails.files[2].fps).to.be.below(31)
477 expect(videoDetails.files[3].fps).to.be.below(31)
478 expect(videoDetails.files[4].fps).to.be.below(31)
479
480 for (const resolution of [ 144, 240, 360, 480 ]) {
481 const file = videoDetails.files.find(f => f.resolution.id === resolution)
482 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
483 const fps = await getVideoStreamFPS(path)
484
485 expect(fps).to.be.below(31)
486 }
487
488 const file = videoDetails.files.find(f => f.resolution.id === 720)
489 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
490 const fps = await getVideoStreamFPS(path)
491
492 expect(fps).to.be.above(58).and.below(62)
493 }
494 })
495
496 it('Should downscale to the closest divisor standard framerate', async function () {
497 this.timeout(200_000)
498
499 let tempFixturePath: string
500
501 {
502 tempFixturePath = await generateVideoWithFramerate(59)
503
504 const fps = await getVideoStreamFPS(tempFixturePath)
505 expect(fps).to.be.equal(59)
506 }
507
508 const attributes = {
509 name: '59fps video',
510 description: '59fps video',
511 fixture: tempFixturePath
512 }
513
514 await servers[1].videos.upload({ attributes })
515
516 await waitJobs(servers)
517
518 for (const server of servers) {
519 const { data } = await server.videos.list()
520
521 const { id } = data.find(v => v.name === attributes.name)
522 const video = await server.videos.get({ id })
523
524 {
525 const file = video.files.find(f => f.resolution.id === 240)
526 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
527 const fps = await getVideoStreamFPS(path)
528 expect(fps).to.be.equal(25)
529 }
530
531 {
532 const file = video.files.find(f => f.resolution.id === 720)
533 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
534 const fps = await getVideoStreamFPS(path)
535 expect(fps).to.be.equal(59)
536 }
537 }
538 })
539 })
540
541 describe('Bitrate control', function () {
542
543 it('Should respect maximum bitrate values', async function () {
544 this.timeout(160_000)
545
546 const tempFixturePath = await generateHighBitrateVideo()
547
548 const attributes = {
549 name: 'high bitrate video',
550 description: 'high bitrate video',
551 fixture: tempFixturePath
552 }
553
554 await servers[1].videos.upload({ attributes })
555
556 await waitJobs(servers)
557
558 for (const server of servers) {
559 const { data } = await server.videos.list()
560
561 const { id } = data.find(v => v.name === attributes.name)
562 const video = await server.videos.get({ id })
563
564 for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
565 const file = video.files.find(f => f.resolution.id === resolution)
566 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
567
568 const bitrate = await getVideoStreamBitrate(path)
569 const fps = await getVideoStreamFPS(path)
570 const dataResolution = await getVideoStreamDimensionsInfo(path)
571
572 expect(resolution).to.equal(resolution)
573
574 const maxBitrate = getMaxBitrate({ ...dataResolution, fps })
575 expect(bitrate).to.be.below(maxBitrate)
576 }
577 }
578 })
579
580 it('Should not transcode to an higher bitrate than the original file but above our low limit', async function () {
581 this.timeout(160_000)
582
583 const newConfig = {
584 transcoding: {
585 enabled: true,
586 resolutions: {
587 '144p': true,
588 '240p': true,
589 '360p': true,
590 '480p': true,
591 '720p': true,
592 '1080p': true,
593 '1440p': true,
594 '2160p': true
595 },
596 webtorrent: { enabled: true },
597 hls: { enabled: true }
598 }
599 }
600 await servers[1].config.updateCustomSubConfig({ newConfig })
601
602 const attributes = {
603 name: 'low bitrate',
604 fixture: 'low-bitrate.mp4'
605 }
606
607 const { id } = await servers[1].videos.upload({ attributes })
608
609 await waitJobs(servers)
610
611 const video = await servers[1].videos.get({ id })
612
613 const resolutions = [ 240, 360, 480, 720, 1080 ]
614 for (const r of resolutions) {
615 const file = video.files.find(f => f.resolution.id === r)
616
617 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
618 const bitrate = await getVideoStreamBitrate(path)
619
620 const inputBitrate = 60_000
621 const limit = getMinLimitBitrate({ fps: 10, ratio: 1, resolution: r })
622 let belowValue = Math.max(inputBitrate, limit)
623 belowValue += belowValue * 0.20 // Apply 20% margin because bitrate control is not very precise
624
625 expect(bitrate, `${path} not below ${limit}`).to.be.below(belowValue)
626 }
627 })
628 })
629
630 describe('FFprobe', function () {
631
632 it('Should provide valid ffprobe data', async function () {
633 this.timeout(160_000)
634
635 const videoUUID = (await servers[1].videos.quickUpload({ name: 'ffprobe data' })).uuid
636 await waitJobs(servers)
637
638 {
639 const video = await servers[1].videos.get({ id: videoUUID })
640 const file = video.files.find(f => f.resolution.id === 240)
641 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
642 const metadata = await buildFileMetadata(path)
643
644 // expected format properties
645 for (const p of [
646 'tags.encoder',
647 'format_long_name',
648 'size',
649 'bit_rate'
650 ]) {
651 expect(metadata.format).to.have.nested.property(p)
652 }
653
654 // expected stream properties
655 for (const p of [
656 'codec_long_name',
657 'profile',
658 'width',
659 'height',
660 'display_aspect_ratio',
661 'avg_frame_rate',
662 'pix_fmt'
663 ]) {
664 expect(metadata.streams[0]).to.have.nested.property(p)
665 }
666
667 expect(metadata).to.not.have.nested.property('format.filename')
668 }
669
670 for (const server of servers) {
671 const videoDetails = await server.videos.get({ id: videoUUID })
672
673 const videoFiles = getAllFiles(videoDetails)
674 expect(videoFiles).to.have.lengthOf(10)
675
676 for (const file of videoFiles) {
677 expect(file.metadata).to.be.undefined
678 expect(file.metadataUrl).to.exist
679 expect(file.metadataUrl).to.contain(servers[1].url)
680 expect(file.metadataUrl).to.contain(videoUUID)
681
682 const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
683 expect(metadata).to.have.nested.property('format.size')
684 }
685 }
686 })
687
688 it('Should correctly detect if quick transcode is possible', async function () {
689 this.timeout(10_000)
690
691 expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.mp4'))).to.be.true
692 expect(await canDoQuickTranscode(buildAbsoluteFixturePath('video_short.webm'))).to.be.false
693 })
694 })
695
696 describe('Transcoding job queue', function () {
697
698 it('Should have the appropriate priorities for transcoding jobs', async function () {
699 const body = await servers[1].jobs.list({
700 start: 0,
701 count: 100,
702 sort: 'createdAt',
703 jobType: 'video-transcoding'
704 })
705
706 const jobs = body.data
707 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k)
708
709 expect(transcodingJobs).to.have.lengthOf(16)
710
711 const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls')
712 const webtorrentJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-webtorrent')
713 const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-webtorrent')
714
715 expect(hlsJobs).to.have.lengthOf(8)
716 expect(webtorrentJobs).to.have.lengthOf(7)
717 expect(optimizeJobs).to.have.lengthOf(1)
718
719 for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) {
720 expect(j.priority).to.be.greaterThan(100)
721 expect(j.priority).to.be.lessThan(150)
722 }
723 })
724 })
725
726 describe('Bounded transcoding', function () {
727
728 it('Should not generate an upper resolution than original file', async function () {
729 this.timeout(120_000)
730
731 await servers[0].config.updateExistingSubConfig({
732 newConfig: {
733 transcoding: {
734 enabled: true,
735 hls: { enabled: true },
736 webtorrent: { enabled: true },
737 resolutions: {
738 '0p': false,
739 '144p': false,
740 '240p': true,
741 '360p': false,
742 '480p': true,
743 '720p': false,
744 '1080p': false,
745 '1440p': false,
746 '2160p': false
747 },
748 alwaysTranscodeOriginalResolution: false
749 }
750 }
751 })
752
753 const { uuid } = await servers[0].videos.quickUpload({ name: 'video', fixture: 'video_short.webm' })
754 await waitJobs(servers)
755
756 const video = await servers[0].videos.get({ id: uuid })
757 const hlsFiles = video.streamingPlaylists[0].files
758
759 expect(video.files).to.have.lengthOf(2)
760 expect(hlsFiles).to.have.lengthOf(2)
761
762 // eslint-disable-next-line @typescript-eslint/require-array-sort-compare
763 const resolutions = getAllFiles(video).map(f => f.resolution.id).sort()
764 expect(resolutions).to.deep.equal([ 240, 240, 480, 480 ])
765 })
766
767 it('Should only keep the original resolution if all resolutions are disabled', async function () {
768 this.timeout(120_000)
769
770 await servers[0].config.updateExistingSubConfig({
771 newConfig: {
772 transcoding: {
773 resolutions: {
774 '0p': false,
775 '144p': false,
776 '240p': false,
777 '360p': false,
778 '480p': false,
779 '720p': false,
780 '1080p': false,
781 '1440p': false,
782 '2160p': false
783 }
784 }
785 }
786 })
787
788 const { uuid } = await servers[0].videos.quickUpload({ name: 'video', fixture: 'video_short.webm' })
789 await waitJobs(servers)
790
791 const video = await servers[0].videos.get({ id: uuid })
792 const hlsFiles = video.streamingPlaylists[0].files
793
794 expect(video.files).to.have.lengthOf(1)
795 expect(hlsFiles).to.have.lengthOf(1)
796
797 expect(video.files[0].resolution.id).to.equal(720)
798 expect(hlsFiles[0].resolution.id).to.equal(720)
799 })
800 })
801
802 after(async function () {
803 await cleanupTests(servers)
804 })
805 })