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