aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-05-04 15:29:34 +0200
committerChocobozzz <chocobozzz@cpy.re>2023-05-09 08:57:34 +0200
commit5e47f6ab984a7d00782e4c7030afffa1ba480add (patch)
tree1ce586b591a8d71acbc301eba29b9a5e6490439e /server/tests/api
parent6a4905602636afd6650c9e6f4d0fcc2105d91100 (diff)
downloadPeerTube-5e47f6ab984a7d00782e4c7030afffa1ba480add.tar.gz
PeerTube-5e47f6ab984a7d00782e4c7030afffa1ba480add.tar.zst
PeerTube-5e47f6ab984a7d00782e4c7030afffa1ba480add.zip
Support studio transcoding in peertube runner
Diffstat (limited to 'server/tests/api')
-rw-r--r--server/tests/api/check-params/config.ts5
-rw-r--r--server/tests/api/check-params/runners.ts211
-rw-r--r--server/tests/api/runners/index.ts1
-rw-r--r--server/tests/api/runners/runner-common.ts12
-rw-r--r--server/tests/api/runners/runner-studio-transcoding.ts168
-rw-r--r--server/tests/api/runners/runner-vod-transcoding.ts16
-rw-r--r--server/tests/api/server/config.ts7
-rw-r--r--server/tests/api/transcoding/video-studio.ts42
8 files changed, 401 insertions, 61 deletions
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index c5cda203e..472cad182 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -162,7 +162,10 @@ describe('Test config API validators', function () {
162 } 162 }
163 }, 163 },
164 videoStudio: { 164 videoStudio: {
165 enabled: true 165 enabled: true,
166 remoteRunners: {
167 enabled: true
168 }
166 }, 169 },
167 import: { 170 import: {
168 videos: { 171 videos: {
diff --git a/server/tests/api/check-params/runners.ts b/server/tests/api/check-params/runners.ts
index 4da6fd91d..90a301392 100644
--- a/server/tests/api/check-params/runners.ts
+++ b/server/tests/api/check-params/runners.ts
@@ -1,6 +1,17 @@
1import { basename } from 'path'
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 2/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared' 3import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '@server/tests/shared'
3import { HttpStatusCode, RunnerJob, RunnerJobState, RunnerJobSuccessPayload, RunnerJobUpdatePayload, VideoPrivacy } from '@shared/models' 4import {
5 HttpStatusCode,
6 isVideoStudioTaskIntro,
7 RunnerJob,
8 RunnerJobState,
9 RunnerJobSuccessPayload,
10 RunnerJobUpdatePayload,
11 RunnerJobVideoEditionTranscodingPayload,
12 VideoPrivacy,
13 VideoStudioTaskIntro
14} from '@shared/models'
4import { 15import {
5 cleanupTests, 16 cleanupTests,
6 createSingleServer, 17 createSingleServer,
@@ -10,6 +21,7 @@ import {
10 setAccessTokensToServers, 21 setAccessTokensToServers,
11 setDefaultVideoChannel, 22 setDefaultVideoChannel,
12 stopFfmpeg, 23 stopFfmpeg,
24 VideoStudioCommand,
13 waitJobs 25 waitJobs
14} from '@shared/server-commands' 26} from '@shared/server-commands'
15 27
@@ -53,7 +65,10 @@ describe('Test managing runners', function () {
53 registrationTokenId = data[0].id 65 registrationTokenId = data[0].id
54 66
55 await server.config.enableTranscoding(true, true) 67 await server.config.enableTranscoding(true, true)
68 await server.config.enableStudio()
56 await server.config.enableRemoteTranscoding() 69 await server.config.enableRemoteTranscoding()
70 await server.config.enableRemoteStudio()
71
57 runnerToken = await server.runners.autoRegisterRunner() 72 runnerToken = await server.runners.autoRegisterRunner()
58 runnerToken2 = await server.runners.autoRegisterRunner() 73 runnerToken2 = await server.runners.autoRegisterRunner()
59 74
@@ -249,6 +264,10 @@ describe('Test managing runners', function () {
249 await server.runnerJobs.cancelByAdmin({ jobUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 264 await server.runnerJobs.cancelByAdmin({ jobUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
250 }) 265 })
251 266
267 it('Should fail with an already cancelled job', async function () {
268 await server.runnerJobs.cancelByAdmin({ jobUUID: cancelledJobUUID, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
269 })
270
252 it('Should succeed with the correct params', async function () { 271 it('Should succeed with the correct params', async function () {
253 await server.runnerJobs.cancelByAdmin({ jobUUID }) 272 await server.runnerJobs.cancelByAdmin({ jobUUID })
254 }) 273 })
@@ -296,9 +315,13 @@ describe('Test managing runners', function () {
296 315
297 let pendingUUID: string 316 let pendingUUID: string
298 317
318 let videoStudioUUID: string
319 let studioFile: string
320
299 let liveAcceptedJob: RunnerJob & { jobToken: string } 321 let liveAcceptedJob: RunnerJob & { jobToken: string }
322 let studioAcceptedJob: RunnerJob & { jobToken: string }
300 323
301 async function fetchFiles (options: { 324 async function fetchVideoInputFiles (options: {
302 jobUUID: string 325 jobUUID: string
303 videoUUID: string 326 videoUUID: string
304 runnerToken: string 327 runnerToken: string
@@ -315,6 +338,21 @@ describe('Test managing runners', function () {
315 } 338 }
316 } 339 }
317 340
341 async function fetchStudioFiles (options: {
342 jobUUID: string
343 videoUUID: string
344 runnerToken: string
345 jobToken: string
346 studioFile?: string
347 expectedStatus: HttpStatusCode
348 }) {
349 const { jobUUID, expectedStatus, videoUUID, runnerToken, jobToken, studioFile } = options
350
351 const path = `/api/v1/runners/jobs/${jobUUID}/files/videos/${videoUUID}/studio/task-files/${studioFile}`
352
353 await makePostBodyRequest({ url: server.url, path, fields: { runnerToken, jobToken }, expectedStatus })
354 }
355
318 before(async function () { 356 before(async function () {
319 this.timeout(120000) 357 this.timeout(120000)
320 358
@@ -353,6 +391,28 @@ describe('Test managing runners', function () {
353 } 391 }
354 392
355 { 393 {
394 await server.config.disableTranscoding()
395
396 const { uuid } = await server.videos.quickUpload({ name: 'video studio' })
397 videoStudioUUID = uuid
398
399 await server.config.enableTranscoding(true, true)
400 await server.config.enableStudio()
401
402 await server.videoStudio.createEditionTasks({
403 videoId: videoStudioUUID,
404 tasks: VideoStudioCommand.getComplexTask()
405 })
406
407 const { job } = await server.runnerJobs.autoAccept({ runnerToken, type: 'video-edition-transcoding' })
408 studioAcceptedJob = job
409
410 const tasks = (job.payload as RunnerJobVideoEditionTranscodingPayload).tasks
411 const fileUrl = (tasks.find(t => isVideoStudioTaskIntro(t)) as VideoStudioTaskIntro).options.file as string
412 studioFile = basename(fileUrl)
413 }
414
415 {
356 await server.config.enableLive({ 416 await server.config.enableLive({
357 allowReplay: false, 417 allowReplay: false,
358 resolutions: 'max', 418 resolutions: 'max',
@@ -381,8 +441,6 @@ describe('Test managing runners', function () {
381 jobToken: string 441 jobToken: string
382 expectedStatus: HttpStatusCode 442 expectedStatus: HttpStatusCode
383 }) { 443 }) {
384 await fetchFiles({ ...options, videoUUID })
385
386 await server.runnerJobs.abort({ ...options, reason: 'reason' }) 444 await server.runnerJobs.abort({ ...options, reason: 'reason' })
387 await server.runnerJobs.update({ ...options }) 445 await server.runnerJobs.update({ ...options })
388 await server.runnerJobs.error({ ...options, message: 'message' }) 446 await server.runnerJobs.error({ ...options, message: 'message' })
@@ -390,39 +448,95 @@ describe('Test managing runners', function () {
390 } 448 }
391 449
392 it('Should fail with an invalid job uuid', async function () { 450 it('Should fail with an invalid job uuid', async function () {
393 await testEndpoints({ jobUUID: 'a', runnerToken, jobToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 451 const options = { jobUUID: 'a', runnerToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
452
453 await testEndpoints({ ...options, jobToken })
454 await fetchVideoInputFiles({ ...options, videoUUID, jobToken })
455 await fetchStudioFiles({ ...options, videoUUID, jobToken: studioAcceptedJob.jobToken, studioFile })
394 }) 456 })
395 457
396 it('Should fail with an unknown job uuid', async function () { 458 it('Should fail with an unknown job uuid', async function () {
397 const jobUUID = badUUID 459 const options = { jobUUID: badUUID, runnerToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
398 await testEndpoints({ jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 460
461 await testEndpoints({ ...options, jobToken })
462 await fetchVideoInputFiles({ ...options, videoUUID, jobToken })
463 await fetchStudioFiles({ ...options, jobToken: studioAcceptedJob.jobToken, videoUUID, studioFile })
399 }) 464 })
400 465
401 it('Should fail with an invalid runner token', async function () { 466 it('Should fail with an invalid runner token', async function () {
402 await testEndpoints({ jobUUID, runnerToken: '', jobToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 467 const options = { runnerToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
468
469 await testEndpoints({ ...options, jobUUID, jobToken })
470 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID, jobToken })
471 await fetchStudioFiles({
472 ...options,
473 jobToken: studioAcceptedJob.jobToken,
474 jobUUID: studioAcceptedJob.uuid,
475 videoUUID: videoStudioUUID,
476 studioFile
477 })
403 }) 478 })
404 479
405 it('Should fail with an unknown runner token', async function () { 480 it('Should fail with an unknown runner token', async function () {
406 const runnerToken = badUUID 481 const options = { runnerToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
407 await testEndpoints({ jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 482
483 await testEndpoints({ ...options, jobUUID, jobToken })
484 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID, jobToken })
485 await fetchStudioFiles({
486 ...options,
487 jobToken: studioAcceptedJob.jobToken,
488 jobUUID: studioAcceptedJob.uuid,
489 videoUUID: videoStudioUUID,
490 studioFile
491 })
408 }) 492 })
409 493
410 it('Should fail with an invalid job token job uuid', async function () { 494 it('Should fail with an invalid job token job uuid', async function () {
411 await testEndpoints({ jobUUID, runnerToken, jobToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 495 const options = { runnerToken, jobToken: '', expectedStatus: HttpStatusCode.BAD_REQUEST_400 }
496
497 await testEndpoints({ ...options, jobUUID })
498 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID })
499 await fetchStudioFiles({ ...options, jobUUID: studioAcceptedJob.uuid, videoUUID: videoStudioUUID, studioFile })
412 }) 500 })
413 501
414 it('Should fail with an unknown job token job uuid', async function () { 502 it('Should fail with an unknown job token job uuid', async function () {
415 const jobToken = badUUID 503 const options = { runnerToken, jobToken: badUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
416 await testEndpoints({ jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 504
505 await testEndpoints({ ...options, jobUUID })
506 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID })
507 await fetchStudioFiles({ ...options, jobUUID: studioAcceptedJob.uuid, videoUUID: videoStudioUUID, studioFile })
417 }) 508 })
418 509
419 it('Should fail with a runner token not associated to this job', async function () { 510 it('Should fail with a runner token not associated to this job', async function () {
420 await testEndpoints({ jobUUID, runnerToken: runnerToken2, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 511 const options = { runnerToken: runnerToken2, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
512
513 await testEndpoints({ ...options, jobUUID, jobToken })
514 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID, jobToken })
515 await fetchStudioFiles({
516 ...options,
517 jobToken: studioAcceptedJob.jobToken,
518 jobUUID: studioAcceptedJob.uuid,
519 videoUUID: videoStudioUUID,
520 studioFile
521 })
421 }) 522 })
422 523
423 it('Should fail with a job uuid not associated to the job token', async function () { 524 it('Should fail with a job uuid not associated to the job token', async function () {
424 await testEndpoints({ jobUUID: jobUUID2, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 525 {
425 await testEndpoints({ jobUUID, runnerToken, jobToken: jobToken2, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 526 const options = { jobUUID: jobUUID2, runnerToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
527
528 await testEndpoints({ ...options, jobToken })
529 await fetchVideoInputFiles({ ...options, jobToken, videoUUID })
530 await fetchStudioFiles({ ...options, jobToken: studioAcceptedJob.jobToken, videoUUID: videoStudioUUID, studioFile })
531 }
532
533 {
534 const options = { runnerToken, jobToken: jobToken2, expectedStatus: HttpStatusCode.NOT_FOUND_404 }
535
536 await testEndpoints({ ...options, jobUUID })
537 await fetchVideoInputFiles({ ...options, jobUUID, videoUUID })
538 await fetchStudioFiles({ ...options, jobUUID: studioAcceptedJob.uuid, videoUUID: videoStudioUUID, studioFile })
539 }
426 }) 540 })
427 }) 541 })
428 542
@@ -670,27 +784,82 @@ describe('Test managing runners', function () {
670 }) 784 })
671 }) 785 })
672 }) 786 })
787
788 describe('Video studio', function () {
789
790 it('Should fail with an invalid video edition transcoding payload', async function () {
791 await server.runnerJobs.success({
792 jobUUID: studioAcceptedJob.uuid,
793 jobToken: studioAcceptedJob.jobToken,
794 payload: { hello: 'video_short.mp4' } as any,
795 runnerToken,
796 expectedStatus: HttpStatusCode.BAD_REQUEST_400
797 })
798 })
799 })
673 }) 800 })
674 801
675 describe('Job files', function () { 802 describe('Job files', function () {
676 803
677 describe('Video files', function () { 804 describe('Check video param for common job file routes', function () {
805
806 async function fetchFiles (options: {
807 videoUUID?: string
808 expectedStatus: HttpStatusCode
809 }) {
810 await fetchVideoInputFiles({ videoUUID, ...options, jobToken, jobUUID, runnerToken })
811
812 await fetchStudioFiles({
813 videoUUID: videoStudioUUID,
814
815 ...options,
816
817 jobToken: studioAcceptedJob.jobToken,
818 jobUUID: studioAcceptedJob.uuid,
819 runnerToken,
820 studioFile
821 })
822 }
678 823
679 it('Should fail with an invalid video id', async function () { 824 it('Should fail with an invalid video id', async function () {
680 await fetchFiles({ videoUUID: 'a', jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 825 await fetchFiles({
826 videoUUID: 'a',
827 expectedStatus: HttpStatusCode.BAD_REQUEST_400
828 })
681 }) 829 })
682 830
683 it('Should fail with an unknown video id', async function () { 831 it('Should fail with an unknown video id', async function () {
684 const videoUUID = '910ec12a-d9e6-458b-a274-0abb655f9464' 832 const videoUUID = '910ec12a-d9e6-458b-a274-0abb655f9464'
685 await fetchFiles({ videoUUID, jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 833
834 await fetchFiles({
835 videoUUID,
836 expectedStatus: HttpStatusCode.NOT_FOUND_404
837 })
686 }) 838 })
687 839
688 it('Should fail with a video id not associated to this job', async function () { 840 it('Should fail with a video id not associated to this job', async function () {
689 await fetchFiles({ videoUUID: videoUUID2, jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 841 await fetchFiles({
842 videoUUID: videoUUID2,
843 expectedStatus: HttpStatusCode.FORBIDDEN_403
844 })
690 }) 845 })
691 846
692 it('Should succeed with the correct params', async function () { 847 it('Should succeed with the correct params', async function () {
693 await fetchFiles({ videoUUID, jobUUID, runnerToken, jobToken, expectedStatus: HttpStatusCode.OK_200 }) 848 await fetchFiles({ expectedStatus: HttpStatusCode.OK_200 })
849 })
850 })
851
852 describe('Video edition tasks file routes', function () {
853
854 it('Should fail with an invalid studio filename', async function () {
855 await fetchStudioFiles({
856 videoUUID: videoStudioUUID,
857 jobUUID: studioAcceptedJob.uuid,
858 runnerToken,
859 jobToken: studioAcceptedJob.jobToken,
860 studioFile: 'toto',
861 expectedStatus: HttpStatusCode.BAD_REQUEST_400
862 })
694 }) 863 })
695 }) 864 })
696 }) 865 })
diff --git a/server/tests/api/runners/index.ts b/server/tests/api/runners/index.ts
index 7f33ec8dd..642a3a96d 100644
--- a/server/tests/api/runners/index.ts
+++ b/server/tests/api/runners/index.ts
@@ -1,4 +1,5 @@
1export * from './runner-common' 1export * from './runner-common'
2export * from './runner-live-transcoding' 2export * from './runner-live-transcoding'
3export * from './runner-socket' 3export * from './runner-socket'
4export * from './runner-studio-transcoding'
4export * from './runner-vod-transcoding' 5export * from './runner-vod-transcoding'
diff --git a/server/tests/api/runners/runner-common.ts b/server/tests/api/runners/runner-common.ts
index a2204753b..554024190 100644
--- a/server/tests/api/runners/runner-common.ts
+++ b/server/tests/api/runners/runner-common.ts
@@ -2,7 +2,15 @@
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { wait } from '@shared/core-utils' 4import { wait } from '@shared/core-utils'
5import { HttpStatusCode, Runner, RunnerJob, RunnerJobAdmin, RunnerJobState, RunnerRegistrationToken } from '@shared/models' 5import {
6 HttpStatusCode,
7 Runner,
8 RunnerJob,
9 RunnerJobAdmin,
10 RunnerJobState,
11 RunnerJobVODWebVideoTranscodingPayload,
12 RunnerRegistrationToken
13} from '@shared/models'
6import { 14import {
7 cleanupTests, 15 cleanupTests,
8 createSingleServer, 16 createSingleServer,
@@ -349,7 +357,7 @@ describe('Test runner common actions', function () {
349 for (const job of availableJobs) { 357 for (const job of availableJobs) {
350 expect(job.uuid).to.exist 358 expect(job.uuid).to.exist
351 expect(job.payload.input).to.exist 359 expect(job.payload.input).to.exist
352 expect(job.payload.output).to.exist 360 expect((job.payload as RunnerJobVODWebVideoTranscodingPayload).output).to.exist
353 361
354 expect((job as RunnerJobAdmin).privatePayload).to.not.exist 362 expect((job as RunnerJobAdmin).privatePayload).to.not.exist
355 } 363 }
diff --git a/server/tests/api/runners/runner-studio-transcoding.ts b/server/tests/api/runners/runner-studio-transcoding.ts
new file mode 100644
index 000000000..9ae629be6
--- /dev/null
+++ b/server/tests/api/runners/runner-studio-transcoding.ts
@@ -0,0 +1,168 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { readFile } from 'fs-extra'
5import { checkPersistentTmpIsEmpty, checkVideoDuration } from '@server/tests/shared'
6import { buildAbsoluteFixturePath } from '@shared/core-utils'
7import {
8 RunnerJobVideoEditionTranscodingPayload,
9 VideoEditionTranscodingSuccess,
10 VideoState,
11 VideoStudioTask,
12 VideoStudioTaskIntro
13} from '@shared/models'
14import {
15 cleanupTests,
16 createMultipleServers,
17 doubleFollow,
18 PeerTubeServer,
19 setAccessTokensToServers,
20 setDefaultVideoChannel,
21 VideoStudioCommand,
22 waitJobs
23} from '@shared/server-commands'
24
25describe('Test runner video studio transcoding', function () {
26 let servers: PeerTubeServer[] = []
27 let runnerToken: string
28 let videoUUID: string
29 let jobUUID: string
30
31 async function renewStudio (tasks: VideoStudioTask[] = VideoStudioCommand.getComplexTask()) {
32 const { uuid } = await servers[0].videos.quickUpload({ name: 'video' })
33 videoUUID = uuid
34
35 await waitJobs(servers)
36
37 await servers[0].videoStudio.createEditionTasks({ videoId: uuid, tasks })
38 await waitJobs(servers)
39
40 const { availableJobs } = await servers[0].runnerJobs.request({ runnerToken })
41 expect(availableJobs).to.have.lengthOf(1)
42
43 jobUUID = availableJobs[0].uuid
44 }
45
46 before(async function () {
47 this.timeout(120_000)
48
49 servers = await createMultipleServers(2)
50
51 await setAccessTokensToServers(servers)
52 await setDefaultVideoChannel(servers)
53
54 await doubleFollow(servers[0], servers[1])
55
56 await servers[0].config.enableTranscoding(true, true)
57 await servers[0].config.enableStudio()
58 await servers[0].config.enableRemoteStudio()
59
60 runnerToken = await servers[0].runners.autoRegisterRunner()
61 })
62
63 it('Should error a studio transcoding job', async function () {
64 this.timeout(60000)
65
66 await renewStudio()
67
68 for (let i = 0; i < 5; i++) {
69 const { job } = await servers[0].runnerJobs.accept({ runnerToken, jobUUID })
70 const jobToken = job.jobToken
71
72 await servers[0].runnerJobs.error({ runnerToken, jobUUID, jobToken, message: 'Error' })
73 }
74
75 const video = await servers[0].videos.get({ id: videoUUID })
76 expect(video.state.id).to.equal(VideoState.PUBLISHED)
77
78 await checkPersistentTmpIsEmpty(servers[0])
79 })
80
81 it('Should cancel a transcoding job', async function () {
82 this.timeout(60000)
83
84 await renewStudio()
85
86 await servers[0].runnerJobs.cancelByAdmin({ jobUUID })
87
88 const video = await servers[0].videos.get({ id: videoUUID })
89 expect(video.state.id).to.equal(VideoState.PUBLISHED)
90
91 await checkPersistentTmpIsEmpty(servers[0])
92 })
93
94 it('Should execute a remote studio job', async function () {
95 this.timeout(240_000)
96
97 const tasks = [
98 {
99 name: 'add-outro' as 'add-outro',
100 options: {
101 file: 'video_short.webm'
102 }
103 },
104 {
105 name: 'add-watermark' as 'add-watermark',
106 options: {
107 file: 'thumbnail.png'
108 }
109 },
110 {
111 name: 'add-intro' as 'add-intro',
112 options: {
113 file: 'video_very_short_240p.mp4'
114 }
115 }
116 ]
117
118 await renewStudio(tasks)
119
120 for (const server of servers) {
121 await checkVideoDuration(server, videoUUID, 5)
122 }
123
124 const { job } = await servers[0].runnerJobs.accept<RunnerJobVideoEditionTranscodingPayload>({ runnerToken, jobUUID })
125 const jobToken = job.jobToken
126
127 expect(job.type === 'video-edition-transcoding')
128 expect(job.payload.input.videoFileUrl).to.exist
129
130 // Check video input file
131 {
132 await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
133 }
134
135 // Check task files
136 for (let i = 0; i < tasks.length; i++) {
137 const task = tasks[i]
138 const payloadTask = job.payload.tasks[i]
139
140 expect(payloadTask.name).to.equal(task.name)
141
142 const inputFile = await readFile(buildAbsoluteFixturePath(task.options.file))
143
144 const { body } = await servers[0].runnerJobs.getJobFile({
145 url: (payloadTask as VideoStudioTaskIntro).options.file as string,
146 jobToken,
147 runnerToken
148 })
149
150 expect(body).to.deep.equal(inputFile)
151 }
152
153 const payload: VideoEditionTranscodingSuccess = { videoFile: 'video_very_short_240p.mp4' }
154 await servers[0].runnerJobs.success({ runnerToken, jobUUID, jobToken, payload })
155
156 await waitJobs(servers)
157
158 for (const server of servers) {
159 await checkVideoDuration(server, videoUUID, 2)
160 }
161
162 await checkPersistentTmpIsEmpty(servers[0])
163 })
164
165 after(async function () {
166 await cleanupTests(servers)
167 })
168})
diff --git a/server/tests/api/runners/runner-vod-transcoding.ts b/server/tests/api/runners/runner-vod-transcoding.ts
index 92a47ac3b..b08ee312c 100644
--- a/server/tests/api/runners/runner-vod-transcoding.ts
+++ b/server/tests/api/runners/runner-vod-transcoding.ts
@@ -155,7 +155,7 @@ describe('Test runner VOD transcoding', function () {
155 expect(job.payload.output.resolution).to.equal(720) 155 expect(job.payload.output.resolution).to.equal(720)
156 expect(job.payload.output.fps).to.equal(25) 156 expect(job.payload.output.fps).to.equal(25)
157 157
158 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) 158 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
159 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.webm')) 159 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.webm'))
160 160
161 expect(body).to.deep.equal(inputFile) 161 expect(body).to.deep.equal(inputFile)
@@ -200,7 +200,7 @@ describe('Test runner VOD transcoding', function () {
200 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID }) 200 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID })
201 jobToken = job.jobToken 201 jobToken = job.jobToken
202 202
203 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) 203 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
204 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) 204 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4'))
205 205
206 expect(body).to.deep.equal(inputFile) 206 expect(body).to.deep.equal(inputFile)
@@ -221,7 +221,7 @@ describe('Test runner VOD transcoding', function () {
221 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID }) 221 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODWebVideoTranscodingPayload>({ runnerToken, jobUUID })
222 jobToken = job.jobToken 222 jobToken = job.jobToken
223 223
224 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) 224 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
225 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) 225 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4'))
226 expect(body).to.deep.equal(inputFile) 226 expect(body).to.deep.equal(inputFile)
227 227
@@ -293,7 +293,7 @@ describe('Test runner VOD transcoding', function () {
293 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) 293 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID })
294 jobToken = job.jobToken 294 jobToken = job.jobToken
295 295
296 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) 296 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
297 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4')) 297 const inputFile = await readFile(buildAbsoluteFixturePath('video_short.mp4'))
298 298
299 expect(body).to.deep.equal(inputFile) 299 expect(body).to.deep.equal(inputFile)
@@ -337,7 +337,7 @@ describe('Test runner VOD transcoding', function () {
337 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) 337 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID })
338 jobToken = job.jobToken 338 jobToken = job.jobToken
339 339
340 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) 340 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
341 const inputFile = await readFile(buildAbsoluteFixturePath(maxQualityFile)) 341 const inputFile = await readFile(buildAbsoluteFixturePath(maxQualityFile))
342 expect(body).to.deep.equal(inputFile) 342 expect(body).to.deep.equal(inputFile)
343 343
@@ -446,13 +446,13 @@ describe('Test runner VOD transcoding', function () {
446 expect(job.payload.output.resolution).to.equal(480) 446 expect(job.payload.output.resolution).to.equal(480)
447 447
448 { 448 {
449 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.audioFileUrl, jobToken, runnerToken }) 449 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.audioFileUrl, jobToken, runnerToken })
450 const inputFile = await readFile(buildAbsoluteFixturePath('sample.ogg')) 450 const inputFile = await readFile(buildAbsoluteFixturePath('sample.ogg'))
451 expect(body).to.deep.equal(inputFile) 451 expect(body).to.deep.equal(inputFile)
452 } 452 }
453 453
454 { 454 {
455 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.previewFileUrl, jobToken, runnerToken }) 455 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.previewFileUrl, jobToken, runnerToken })
456 456
457 const video = await servers[0].videos.get({ id: videoUUID }) 457 const video = await servers[0].videos.get({ id: videoUUID })
458 const { body: inputFile } = await makeGetRequest({ 458 const { body: inputFile } = await makeGetRequest({
@@ -503,7 +503,7 @@ describe('Test runner VOD transcoding', function () {
503 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID }) 503 const { job } = await servers[0].runnerJobs.accept<RunnerJobVODHLSTranscodingPayload>({ runnerToken, jobUUID })
504 jobToken = job.jobToken 504 jobToken = job.jobToken
505 505
506 const { body } = await servers[0].runnerJobs.getInputFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken }) 506 const { body } = await servers[0].runnerJobs.getJobFile({ url: job.payload.input.videoFileUrl, jobToken, runnerToken })
507 const inputFile = await readFile(buildAbsoluteFixturePath('video_short_480p.mp4')) 507 const inputFile = await readFile(buildAbsoluteFixturePath('video_short_480p.mp4'))
508 expect(body).to.deep.equal(inputFile) 508 expect(body).to.deep.equal(inputFile)
509 509
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 54a40b994..011ba268c 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -102,6 +102,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
102 expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.true 102 expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.true
103 103
104 expect(data.videoStudio.enabled).to.be.false 104 expect(data.videoStudio.enabled).to.be.false
105 expect(data.videoStudio.remoteRunners.enabled).to.be.false
105 106
106 expect(data.import.videos.concurrency).to.equal(2) 107 expect(data.import.videos.concurrency).to.equal(2)
107 expect(data.import.videos.http.enabled).to.be.true 108 expect(data.import.videos.http.enabled).to.be.true
@@ -211,6 +212,7 @@ function checkUpdatedConfig (data: CustomConfig) {
211 expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.false 212 expect(data.live.transcoding.alwaysTranscodeOriginalResolution).to.be.false
212 213
213 expect(data.videoStudio.enabled).to.be.true 214 expect(data.videoStudio.enabled).to.be.true
215 expect(data.videoStudio.remoteRunners.enabled).to.be.true
214 216
215 expect(data.import.videos.concurrency).to.equal(4) 217 expect(data.import.videos.concurrency).to.equal(4)
216 expect(data.import.videos.http.enabled).to.be.false 218 expect(data.import.videos.http.enabled).to.be.false
@@ -374,7 +376,10 @@ const newCustomConfig: CustomConfig = {
374 } 376 }
375 }, 377 },
376 videoStudio: { 378 videoStudio: {
377 enabled: true 379 enabled: true,
380 remoteRunners: {
381 enabled: true
382 }
378 }, 383 },
379 import: { 384 import: {
380 videos: { 385 videos: {
diff --git a/server/tests/api/transcoding/video-studio.ts b/server/tests/api/transcoding/video-studio.ts
index 30f72e6e9..2f64ef6bd 100644
--- a/server/tests/api/transcoding/video-studio.ts
+++ b/server/tests/api/transcoding/video-studio.ts
@@ -1,5 +1,5 @@
1import { expect } from 'chai' 1import { expect } from 'chai'
2import { checkPersistentTmpIsEmpty, expectStartWith } from '@server/tests/shared' 2import { checkPersistentTmpIsEmpty, checkVideoDuration, expectStartWith } from '@server/tests/shared'
3import { areMockObjectStorageTestsDisabled, getAllFiles } from '@shared/core-utils' 3import { areMockObjectStorageTestsDisabled, getAllFiles } from '@shared/core-utils'
4import { VideoStudioTask } from '@shared/models' 4import { VideoStudioTask } from '@shared/models'
5import { 5import {
@@ -18,20 +18,6 @@ describe('Test video studio', function () {
18 let servers: PeerTubeServer[] = [] 18 let servers: PeerTubeServer[] = []
19 let videoUUID: string 19 let videoUUID: string
20 20
21 async function checkDuration (server: PeerTubeServer, duration: number) {
22 const video = await server.videos.get({ id: videoUUID })
23
24 expect(video.duration).to.be.approximately(duration, 1)
25
26 for (const file of video.files) {
27 const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
28
29 for (const stream of metadata.streams) {
30 expect(Math.round(stream.duration)).to.be.approximately(duration, 1)
31 }
32 }
33 }
34
35 async function renewVideo (fixture = 'video_short.webm') { 21 async function renewVideo (fixture = 'video_short.webm') {
36 const video = await servers[0].videos.quickUpload({ name: 'video', fixture }) 22 const video = await servers[0].videos.quickUpload({ name: 'video', fixture })
37 videoUUID = video.uuid 23 videoUUID = video.uuid
@@ -79,7 +65,7 @@ describe('Test video studio', function () {
79 ]) 65 ])
80 66
81 for (const server of servers) { 67 for (const server of servers) {
82 await checkDuration(server, 3) 68 await checkVideoDuration(server, videoUUID, 3)
83 69
84 const video = await server.videos.get({ id: videoUUID }) 70 const video = await server.videos.get({ id: videoUUID })
85 expect(new Date(video.publishedAt)).to.be.below(beforeTasks) 71 expect(new Date(video.publishedAt)).to.be.below(beforeTasks)
@@ -100,7 +86,7 @@ describe('Test video studio', function () {
100 ]) 86 ])
101 87
102 for (const server of servers) { 88 for (const server of servers) {
103 await checkDuration(server, 2) 89 await checkVideoDuration(server, videoUUID, 2)
104 } 90 }
105 }) 91 })
106 92
@@ -119,7 +105,7 @@ describe('Test video studio', function () {
119 ]) 105 ])
120 106
121 for (const server of servers) { 107 for (const server of servers) {
122 await checkDuration(server, 4) 108 await checkVideoDuration(server, videoUUID, 4)
123 } 109 }
124 }) 110 })
125 }) 111 })
@@ -140,7 +126,7 @@ describe('Test video studio', function () {
140 ]) 126 ])
141 127
142 for (const server of servers) { 128 for (const server of servers) {
143 await checkDuration(server, 10) 129 await checkVideoDuration(server, videoUUID, 10)
144 } 130 }
145 }) 131 })
146 132
@@ -158,7 +144,7 @@ describe('Test video studio', function () {
158 ]) 144 ])
159 145
160 for (const server of servers) { 146 for (const server of servers) {
161 await checkDuration(server, 7) 147 await checkVideoDuration(server, videoUUID, 7)
162 } 148 }
163 }) 149 })
164 150
@@ -183,7 +169,7 @@ describe('Test video studio', function () {
183 ]) 169 ])
184 170
185 for (const server of servers) { 171 for (const server of servers) {
186 await checkDuration(server, 12) 172 await checkVideoDuration(server, videoUUID, 12)
187 } 173 }
188 }) 174 })
189 175
@@ -201,7 +187,7 @@ describe('Test video studio', function () {
201 ]) 187 ])
202 188
203 for (const server of servers) { 189 for (const server of servers) {
204 await checkDuration(server, 7) 190 await checkVideoDuration(server, videoUUID, 7)
205 } 191 }
206 }) 192 })
207 193
@@ -219,7 +205,7 @@ describe('Test video studio', function () {
219 ]) 205 ])
220 206
221 for (const server of servers) { 207 for (const server of servers) {
222 await checkDuration(server, 10) 208 await checkVideoDuration(server, videoUUID, 10)
223 } 209 }
224 }) 210 })
225 211
@@ -237,7 +223,7 @@ describe('Test video studio', function () {
237 ]) 223 ])
238 224
239 for (const server of servers) { 225 for (const server of servers) {
240 await checkDuration(server, 10) 226 await checkVideoDuration(server, videoUUID, 10)
241 } 227 }
242 }) 228 })
243 }) 229 })
@@ -279,7 +265,7 @@ describe('Test video studio', function () {
279 await createTasks(VideoStudioCommand.getComplexTask()) 265 await createTasks(VideoStudioCommand.getComplexTask())
280 266
281 for (const server of servers) { 267 for (const server of servers) {
282 await checkDuration(server, 9) 268 await checkVideoDuration(server, videoUUID, 9)
283 } 269 }
284 }) 270 })
285 }) 271 })
@@ -309,7 +295,7 @@ describe('Test video studio', function () {
309 const video = await server.videos.get({ id: videoUUID }) 295 const video = await server.videos.get({ id: videoUUID })
310 expect(video.files).to.have.lengthOf(0) 296 expect(video.files).to.have.lengthOf(0)
311 297
312 await checkDuration(server, 9) 298 await checkVideoDuration(server, videoUUID, 9)
313 } 299 }
314 }) 300 })
315 }) 301 })
@@ -351,7 +337,7 @@ describe('Test video studio', function () {
351 expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getMockPlaylistBaseUrl()) 337 expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getMockPlaylistBaseUrl())
352 } 338 }
353 339
354 await checkDuration(server, 9) 340 await checkVideoDuration(server, videoUUID, 9)
355 } 341 }
356 }) 342 })
357 }) 343 })
@@ -370,7 +356,7 @@ describe('Test video studio', function () {
370 await waitJobs(servers) 356 await waitJobs(servers)
371 357
372 for (const server of servers) { 358 for (const server of servers) {
373 await checkDuration(server, 9) 359 await checkVideoDuration(server, videoUUID, 9)
374 } 360 }
375 }) 361 })
376 362