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