aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-08 10:18:40 +0200
committerChocobozzz <me@florianbigard.com>2021-07-20 15:27:17 +0200
commit4f2199144e428c16460750305f737b890c1ac322 (patch)
treea19c5c0f254ab0b32d6c8838af33a1ba260e4877
parent2c27e70471120c92e0bc8c8114141fbb31ff98ac (diff)
downloadPeerTube-4f2199144e428c16460750305f737b890c1ac322.tar.gz
PeerTube-4f2199144e428c16460750305f737b890c1ac322.tar.zst
PeerTube-4f2199144e428c16460750305f737b890c1ac322.zip
Introduce live command
-rw-r--r--server/tests/api/check-params/live.ts72
-rw-r--r--server/tests/api/live/live-constraints.ts21
-rw-r--r--server/tests/api/live/live-permanent.ts43
-rw-r--r--server/tests/api/live/live-save-replay.ts29
-rw-r--r--server/tests/api/live/live-socket-messages.ts19
-rw-r--r--server/tests/api/live/live-views.ts8
-rw-r--r--server/tests/api/live/live.ts90
-rw-r--r--server/tests/api/search/search-videos.ts22
-rw-r--r--server/tests/api/videos/video-change-ownership.ts5
-rw-r--r--server/tests/plugins/action-hooks.ts3
-rw-r--r--server/tests/plugins/filter-hooks.ts3
-rw-r--r--server/tests/plugins/plugin-transcoding.ts28
-rw-r--r--shared/extra-utils/index.ts16
-rw-r--r--shared/extra-utils/requests/index.ts3
-rw-r--r--shared/extra-utils/server/servers.ts3
-rw-r--r--shared/extra-utils/shared/abstract-command.ts32
-rw-r--r--shared/extra-utils/users/index.ts1
-rw-r--r--shared/extra-utils/videos/index.ts13
-rw-r--r--shared/extra-utils/videos/live-command.ts156
-rw-r--r--shared/extra-utils/videos/live.ts139
20 files changed, 369 insertions, 337 deletions
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts
index 7a623c169..56116848f 100644
--- a/server/tests/api/check-params/live.ts
+++ b/server/tests/api/check-params/live.ts
@@ -2,27 +2,24 @@
2 2
3import 'mocha' 3import 'mocha'
4import { omit } from 'lodash' 4import { omit } from 'lodash'
5import { LiveVideo, VideoCreateResult, VideoPrivacy } from '@shared/models' 5import { VideoCreateResult, VideoPrivacy } from '@shared/models'
6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 6import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
7import { 7import {
8 buildAbsoluteFixturePath, 8 buildAbsoluteFixturePath,
9 cleanupTests, 9 cleanupTests,
10 createUser, 10 createUser,
11 flushAndRunServer, 11 flushAndRunServer,
12 getLive,
13 getMyUserInformation, 12 getMyUserInformation,
14 immutableAssign, 13 immutableAssign,
14 LiveCommand,
15 makePostBodyRequest, 15 makePostBodyRequest,
16 makeUploadRequest, 16 makeUploadRequest,
17 runAndTestFfmpegStreamError,
18 sendRTMPStream, 17 sendRTMPStream,
19 ServerInfo, 18 ServerInfo,
20 setAccessTokensToServers, 19 setAccessTokensToServers,
21 stopFfmpeg, 20 stopFfmpeg,
22 updateLive,
23 uploadVideoAndGetId, 21 uploadVideoAndGetId,
24 userLogin, 22 userLogin
25 waitUntilLivePublished
26} from '../../../../shared/extra-utils' 23} from '../../../../shared/extra-utils'
27 24
28describe('Test video lives API validator', function () { 25describe('Test video lives API validator', function () {
@@ -32,6 +29,7 @@ describe('Test video lives API validator', function () {
32 let channelId: number 29 let channelId: number
33 let video: VideoCreateResult 30 let video: VideoCreateResult
34 let videoIdNotLive: number 31 let videoIdNotLive: number
32 let command: LiveCommand
35 33
36 // --------------------------------------------------------------- 34 // ---------------------------------------------------------------
37 35
@@ -66,6 +64,8 @@ describe('Test video lives API validator', function () {
66 { 64 {
67 videoIdNotLive = (await uploadVideoAndGetId({ server, videoName: 'not live' })).id 65 videoIdNotLive = (await uploadVideoAndGetId({ server, videoName: 'not live' })).id
68 } 66 }
67
68 command = server.liveCommand
69 }) 69 })
70 70
71 describe('When creating a live', function () { 71 describe('When creating a live', function () {
@@ -337,70 +337,72 @@ describe('Test video lives API validator', function () {
337 describe('When getting live information', function () { 337 describe('When getting live information', function () {
338 338
339 it('Should fail without access token', async function () { 339 it('Should fail without access token', async function () {
340 await getLive(server.url, '', video.id, HttpStatusCode.UNAUTHORIZED_401) 340 await command.getLive({ token: '', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
341 }) 341 })
342 342
343 it('Should fail with a bad access token', async function () { 343 it('Should fail with a bad access token', async function () {
344 await getLive(server.url, 'toto', video.id, HttpStatusCode.UNAUTHORIZED_401) 344 await command.getLive({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
345 }) 345 })
346 346
347 it('Should fail with access token of another user', async function () { 347 it('Should fail with access token of another user', async function () {
348 await getLive(server.url, userAccessToken, video.id, HttpStatusCode.FORBIDDEN_403) 348 await command.getLive({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
349 }) 349 })
350 350
351 it('Should fail with a bad video id', async function () { 351 it('Should fail with a bad video id', async function () {
352 await getLive(server.url, server.accessToken, 'toto', HttpStatusCode.BAD_REQUEST_400) 352 await command.getLive({ videoId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
353 }) 353 })
354 354
355 it('Should fail with an unknown video id', async function () { 355 it('Should fail with an unknown video id', async function () {
356 await getLive(server.url, server.accessToken, 454555, HttpStatusCode.NOT_FOUND_404) 356 await command.getLive({ videoId: 454555, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
357 }) 357 })
358 358
359 it('Should fail with a non live video', async function () { 359 it('Should fail with a non live video', async function () {
360 await getLive(server.url, server.accessToken, videoIdNotLive, HttpStatusCode.NOT_FOUND_404) 360 await command.getLive({ videoId: videoIdNotLive, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
361 }) 361 })
362 362
363 it('Should succeed with the correct params', async function () { 363 it('Should succeed with the correct params', async function () {
364 await getLive(server.url, server.accessToken, video.id) 364 await command.getLive({ videoId: video.id })
365 await getLive(server.url, server.accessToken, video.shortUUID) 365 await command.getLive({ videoId: video.uuid })
366 await command.getLive({ videoId: video.shortUUID })
366 }) 367 })
367 }) 368 })
368 369
369 describe('When updating live information', async function () { 370 describe('When updating live information', async function () {
370 371
371 it('Should fail without access token', async function () { 372 it('Should fail without access token', async function () {
372 await updateLive(server.url, '', video.id, {}, HttpStatusCode.UNAUTHORIZED_401) 373 await command.updateLive({ token: '', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
373 }) 374 })
374 375
375 it('Should fail with a bad access token', async function () { 376 it('Should fail with a bad access token', async function () {
376 await updateLive(server.url, 'toto', video.id, {}, HttpStatusCode.UNAUTHORIZED_401) 377 await command.updateLive({ token: 'toto', videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
377 }) 378 })
378 379
379 it('Should fail with access token of another user', async function () { 380 it('Should fail with access token of another user', async function () {
380 await updateLive(server.url, userAccessToken, video.id, {}, HttpStatusCode.FORBIDDEN_403) 381 await command.updateLive({ token: userAccessToken, videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
381 }) 382 })
382 383
383 it('Should fail with a bad video id', async function () { 384 it('Should fail with a bad video id', async function () {
384 await updateLive(server.url, server.accessToken, 'toto', {}, HttpStatusCode.BAD_REQUEST_400) 385 await command.updateLive({ videoId: 'toto', fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
385 }) 386 })
386 387
387 it('Should fail with an unknown video id', async function () { 388 it('Should fail with an unknown video id', async function () {
388 await updateLive(server.url, server.accessToken, 454555, {}, HttpStatusCode.NOT_FOUND_404) 389 await command.updateLive({ videoId: 454555, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
389 }) 390 })
390 391
391 it('Should fail with a non live video', async function () { 392 it('Should fail with a non live video', async function () {
392 await updateLive(server.url, server.accessToken, videoIdNotLive, {}, HttpStatusCode.NOT_FOUND_404) 393 await command.updateLive({ videoId: videoIdNotLive, fields: {}, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
393 }) 394 })
394 395
395 it('Should fail with save replay and permanent live set to true', async function () { 396 it('Should fail with save replay and permanent live set to true', async function () {
396 const fields = { saveReplay: true, permanentLive: true } 397 const fields = { saveReplay: true, permanentLive: true }
397 398
398 await updateLive(server.url, server.accessToken, video.id, fields, HttpStatusCode.BAD_REQUEST_400) 399 await command.updateLive({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
399 }) 400 })
400 401
401 it('Should succeed with the correct params', async function () { 402 it('Should succeed with the correct params', async function () {
402 await updateLive(server.url, server.accessToken, video.id, { saveReplay: false }) 403 await command.updateLive({ videoId: video.id, fields: { saveReplay: false } })
403 await updateLive(server.url, server.accessToken, video.shortUUID, { saveReplay: false }) 404 await command.updateLive({ videoId: video.uuid, fields: { saveReplay: false } })
405 await command.updateLive({ videoId: video.shortUUID, fields: { saveReplay: false } })
404 }) 406 })
405 407
406 it('Should fail to update replay status if replay is not allowed on the instance', async function () { 408 it('Should fail to update replay status if replay is not allowed on the instance', async function () {
@@ -413,36 +415,34 @@ describe('Test video lives API validator', function () {
413 } 415 }
414 }) 416 })
415 417
416 await updateLive(server.url, server.accessToken, video.id, { saveReplay: true }, HttpStatusCode.FORBIDDEN_403) 418 await command.updateLive({ videoId: video.id, fields: { saveReplay: true }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
417 }) 419 })
418 420
419 it('Should fail to update a live if it has already started', async function () { 421 it('Should fail to update a live if it has already started', async function () {
420 this.timeout(40000) 422 this.timeout(40000)
421 423
422 const resLive = await getLive(server.url, server.accessToken, video.id) 424 const live = await command.getLive({ videoId: video.id })
423 const live: LiveVideo = resLive.body
424 425
425 const command = sendRTMPStream(live.rtmpUrl, live.streamKey) 426 const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
426 427
427 await waitUntilLivePublished(server.url, server.accessToken, video.id) 428 await command.waitUntilLivePublished({ videoId: video.id })
428 await updateLive(server.url, server.accessToken, video.id, {}, HttpStatusCode.BAD_REQUEST_400) 429 await command.updateLive({ videoId: video.id, fields: {}, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
429 430
430 await stopFfmpeg(command) 431 await stopFfmpeg(ffmpegCommand)
431 }) 432 })
432 433
433 it('Should fail to stream twice in the save live', async function () { 434 it('Should fail to stream twice in the save live', async function () {
434 this.timeout(40000) 435 this.timeout(40000)
435 436
436 const resLive = await getLive(server.url, server.accessToken, video.id) 437 const live = await command.getLive({ videoId: video.id })
437 const live: LiveVideo = resLive.body
438 438
439 const command = sendRTMPStream(live.rtmpUrl, live.streamKey) 439 const ffmpegCommand = sendRTMPStream(live.rtmpUrl, live.streamKey)
440 440
441 await waitUntilLivePublished(server.url, server.accessToken, video.id) 441 await command.waitUntilLivePublished({ videoId: video.id })
442 442
443 await runAndTestFfmpegStreamError(server.url, server.accessToken, video.id, true) 443 await command.runAndTestFfmpegStreamError({ videoId: video.id, shouldHaveError: true })
444 444
445 await stopFfmpeg(command) 445 await stopFfmpeg(ffmpegCommand)
446 }) 446 })
447 }) 447 })
448 448
diff --git a/server/tests/api/live/live-constraints.ts b/server/tests/api/live/live-constraints.ts
index c64d10dcd..5c4817b40 100644
--- a/server/tests/api/live/live-constraints.ts
+++ b/server/tests/api/live/live-constraints.ts
@@ -7,19 +7,16 @@ import {
7 checkLiveCleanup, 7 checkLiveCleanup,
8 cleanupTests, 8 cleanupTests,
9 ConfigCommand, 9 ConfigCommand,
10 createLive,
11 doubleFollow, 10 doubleFollow,
12 flushAndRunMultipleServers, 11 flushAndRunMultipleServers,
13 generateUser, 12 generateUser,
14 getVideo, 13 getVideo,
15 runAndTestFfmpegStreamError,
16 ServerInfo, 14 ServerInfo,
17 setAccessTokensToServers, 15 setAccessTokensToServers,
18 setDefaultVideoChannel, 16 setDefaultVideoChannel,
19 updateUser, 17 updateUser,
20 wait, 18 wait,
21 waitJobs, 19 waitJobs
22 waitUntilLivePublished
23} from '../../../../shared/extra-utils' 20} from '../../../../shared/extra-utils'
24 21
25const expect = chai.expect 22const expect = chai.expect
@@ -38,8 +35,8 @@ describe('Test live constraints', function () {
38 saveReplay 35 saveReplay
39 } 36 }
40 37
41 const res = await createLive(servers[0].url, userAccessToken, liveAttributes) 38 const { uuid } = await servers[0].liveCommand.createLive({ token: userAccessToken, fields: liveAttributes })
42 return res.body.video.uuid as string 39 return uuid
43 } 40 }
44 41
45 async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) { 42 async function checkSaveReplay (videoId: string, resolutions = [ 720 ]) {
@@ -56,7 +53,7 @@ describe('Test live constraints', function () {
56 53
57 async function waitUntilLivePublishedOnAllServers (videoId: string) { 54 async function waitUntilLivePublishedOnAllServers (videoId: string) {
58 for (const server of servers) { 55 for (const server of servers) {
59 await waitUntilLivePublished(server.url, server.accessToken, videoId) 56 await server.liveCommand.waitUntilLivePublished({ videoId })
60 } 57 }
61 } 58 }
62 59
@@ -108,7 +105,7 @@ describe('Test live constraints', function () {
108 this.timeout(60000) 105 this.timeout(60000)
109 106
110 const userVideoLiveoId = await createLiveWrapper(false) 107 const userVideoLiveoId = await createLiveWrapper(false)
111 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) 108 await servers[0].liveCommand.runAndTestFfmpegStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
112 }) 109 })
113 110
114 it('Should have size limit depending on user global quota if save replay is enabled', async function () { 111 it('Should have size limit depending on user global quota if save replay is enabled', async function () {
@@ -118,7 +115,7 @@ describe('Test live constraints', function () {
118 await wait(5000) 115 await wait(5000)
119 116
120 const userVideoLiveoId = await createLiveWrapper(true) 117 const userVideoLiveoId = await createLiveWrapper(true)
121 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 118 await servers[0].liveCommand.runAndTestFfmpegStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
122 119
123 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 120 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
124 await waitJobs(servers) 121 await waitJobs(servers)
@@ -135,7 +132,7 @@ describe('Test live constraints', function () {
135 await updateQuota({ total: -1, daily: 1 }) 132 await updateQuota({ total: -1, daily: 1 })
136 133
137 const userVideoLiveoId = await createLiveWrapper(true) 134 const userVideoLiveoId = await createLiveWrapper(true)
138 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 135 await servers[0].liveCommand.runAndTestFfmpegStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
139 136
140 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 137 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
141 await waitJobs(servers) 138 await waitJobs(servers)
@@ -152,7 +149,7 @@ describe('Test live constraints', function () {
152 await updateQuota({ total: 10 * 1000 * 1000, daily: -1 }) 149 await updateQuota({ total: 10 * 1000 * 1000, daily: -1 })
153 150
154 const userVideoLiveoId = await createLiveWrapper(true) 151 const userVideoLiveoId = await createLiveWrapper(true)
155 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, false) 152 await servers[0].liveCommand.runAndTestFfmpegStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
156 }) 153 })
157 154
158 it('Should have max duration limit', async function () { 155 it('Should have max duration limit', async function () {
@@ -173,7 +170,7 @@ describe('Test live constraints', function () {
173 }) 170 })
174 171
175 const userVideoLiveoId = await createLiveWrapper(true) 172 const userVideoLiveoId = await createLiveWrapper(true)
176 await runAndTestFfmpegStreamError(servers[0].url, userAccessToken, userVideoLiveoId, true) 173 await servers[0].liveCommand.runAndTestFfmpegStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
177 174
178 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 175 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
179 await waitJobs(servers) 176 await waitJobs(servers)
diff --git a/server/tests/api/live/live-permanent.ts b/server/tests/api/live/live-permanent.ts
index b9e37c834..a0f70dfdb 100644
--- a/server/tests/api/live/live-permanent.ts
+++ b/server/tests/api/live/live-permanent.ts
@@ -6,22 +6,15 @@ import { LiveVideoCreate, VideoDetails, VideoPrivacy, VideoState } from '@shared
6import { 6import {
7 cleanupTests, 7 cleanupTests,
8 ConfigCommand, 8 ConfigCommand,
9 createLive,
10 doubleFollow, 9 doubleFollow,
11 flushAndRunMultipleServers, 10 flushAndRunMultipleServers,
12 getLive,
13 getPlaylistsCount,
14 getVideo, 11 getVideo,
15 sendRTMPStreamInVideo,
16 ServerInfo, 12 ServerInfo,
17 setAccessTokensToServers, 13 setAccessTokensToServers,
18 setDefaultVideoChannel, 14 setDefaultVideoChannel,
19 stopFfmpeg, 15 stopFfmpeg,
20 updateLive,
21 wait, 16 wait,
22 waitJobs, 17 waitJobs
23 waitUntilLivePublished,
24 waitUntilLiveWaiting
25} from '../../../../shared/extra-utils' 18} from '../../../../shared/extra-utils'
26 19
27const expect = chai.expect 20const expect = chai.expect
@@ -39,8 +32,8 @@ describe('Permanent live', function () {
39 permanentLive 32 permanentLive
40 } 33 }
41 34
42 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 35 const { uuid } = await servers[0].liveCommand.createLive({ fields: attributes })
43 return res.body.video.uuid 36 return uuid
44 } 37 }
45 38
46 async function checkVideoState (videoId: string, state: VideoState) { 39 async function checkVideoState (videoId: string, state: VideoState) {
@@ -83,15 +76,15 @@ describe('Permanent live', function () {
83 const videoUUID = await createLiveWrapper(false) 76 const videoUUID = await createLiveWrapper(false)
84 77
85 { 78 {
86 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 79 const live = await servers[0].liveCommand.getLive({ videoId: videoUUID })
87 expect(res.body.permanentLive).to.be.false 80 expect(live.permanentLive).to.be.false
88 } 81 }
89 82
90 await updateLive(servers[0].url, servers[0].accessToken, videoUUID, { permanentLive: true }) 83 await servers[0].liveCommand.updateLive({ videoId: videoUUID, fields: { permanentLive: true } })
91 84
92 { 85 {
93 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 86 const live = await servers[0].liveCommand.getLive({ videoId: videoUUID })
94 expect(res.body.permanentLive).to.be.true 87 expect(live.permanentLive).to.be.true
95 } 88 }
96 }) 89 })
97 90
@@ -100,8 +93,8 @@ describe('Permanent live', function () {
100 93
101 videoUUID = await createLiveWrapper(true) 94 videoUUID = await createLiveWrapper(true)
102 95
103 const res = await getLive(servers[0].url, servers[0].accessToken, videoUUID) 96 const live = await servers[0].liveCommand.getLive({ videoId: videoUUID })
104 expect(res.body.permanentLive).to.be.true 97 expect(live.permanentLive).to.be.true
105 98
106 await waitJobs(servers) 99 await waitJobs(servers)
107 }) 100 })
@@ -109,16 +102,16 @@ describe('Permanent live', function () {
109 it('Should stream into this permanent live', async function () { 102 it('Should stream into this permanent live', async function () {
110 this.timeout(120000) 103 this.timeout(120000)
111 104
112 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, videoUUID) 105 const ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: videoUUID })
113 106
114 for (const server of servers) { 107 for (const server of servers) {
115 await waitUntilLivePublished(server.url, server.accessToken, videoUUID) 108 await server.liveCommand.waitUntilLivePublished({ videoId: videoUUID })
116 } 109 }
117 110
118 await checkVideoState(videoUUID, VideoState.PUBLISHED) 111 await checkVideoState(videoUUID, VideoState.PUBLISHED)
119 112
120 await stopFfmpeg(command) 113 await stopFfmpeg(ffmpegCommand)
121 await waitUntilLiveWaiting(servers[0].url, servers[0].accessToken, videoUUID) 114 await servers[0].liveCommand.waitUntilLiveWaiting({ videoId: videoUUID })
122 115
123 await waitJobs(servers) 116 await waitJobs(servers)
124 }) 117 })
@@ -160,19 +153,19 @@ describe('Permanent live', function () {
160 } 153 }
161 }) 154 })
162 155
163 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, videoUUID) 156 const ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: videoUUID })
164 157
165 for (const server of servers) { 158 for (const server of servers) {
166 await waitUntilLivePublished(server.url, server.accessToken, videoUUID) 159 await server.liveCommand.waitUntilLivePublished({ videoId: videoUUID })
167 } 160 }
168 161
169 await checkVideoState(videoUUID, VideoState.PUBLISHED) 162 await checkVideoState(videoUUID, VideoState.PUBLISHED)
170 163
171 const count = await getPlaylistsCount(servers[0], videoUUID) 164 const count = await servers[0].liveCommand.getPlaylistsCount({ videoUUID })
172 // master playlist and 720p playlist 165 // master playlist and 720p playlist
173 expect(count).to.equal(2) 166 expect(count).to.equal(2)
174 167
175 await stopFfmpeg(command) 168 await stopFfmpeg(ffmpegCommand)
176 }) 169 })
177 170
178 after(async function () { 171 after(async function () {
diff --git a/server/tests/api/live/live-save-replay.ts b/server/tests/api/live/live-save-replay.ts
index e74bc3e8d..d3c252ffd 100644
--- a/server/tests/api/live/live-save-replay.ts
+++ b/server/tests/api/live/live-save-replay.ts
@@ -10,13 +10,11 @@ import {
10 checkLiveCleanup, 10 checkLiveCleanup,
11 cleanupTests, 11 cleanupTests,
12 ConfigCommand, 12 ConfigCommand,
13 createLive,
14 doubleFollow, 13 doubleFollow,
15 flushAndRunMultipleServers, 14 flushAndRunMultipleServers,
16 getVideo, 15 getVideo,
17 getVideosList, 16 getVideosList,
18 removeVideo, 17 removeVideo,
19 sendRTMPStreamInVideo,
20 ServerInfo, 18 ServerInfo,
21 setAccessTokensToServers, 19 setAccessTokensToServers,
22 setDefaultVideoChannel, 20 setDefaultVideoChannel,
@@ -24,10 +22,7 @@ import {
24 testFfmpegStreamError, 22 testFfmpegStreamError,
25 updateVideo, 23 updateVideo,
26 wait, 24 wait,
27 waitJobs, 25 waitJobs
28 waitUntilLiveEnded,
29 waitUntilLivePublished,
30 waitUntilLiveSaved
31} from '../../../../shared/extra-utils' 26} from '../../../../shared/extra-utils'
32 27
33const expect = chai.expect 28const expect = chai.expect
@@ -52,8 +47,8 @@ describe('Save replay setting', function () {
52 saveReplay 47 saveReplay
53 } 48 }
54 49
55 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 50 const { uuid } = await servers[0].liveCommand.createLive({ fields: attributes })
56 return res.body.video.uuid 51 return uuid
57 } 52 }
58 53
59 async function checkVideosExist (videoId: string, existsInList: boolean, getStatus?: number) { 54 async function checkVideosExist (videoId: string, existsInList: boolean, getStatus?: number) {
@@ -79,13 +74,13 @@ describe('Save replay setting', function () {
79 74
80 async function waitUntilLivePublishedOnAllServers (videoId: string) { 75 async function waitUntilLivePublishedOnAllServers (videoId: string) {
81 for (const server of servers) { 76 for (const server of servers) {
82 await waitUntilLivePublished(server.url, server.accessToken, videoId) 77 await server.liveCommand.waitUntilLivePublished({ videoId })
83 } 78 }
84 } 79 }
85 80
86 async function waitUntilLiveSavedOnAllServers (videoId: string) { 81 async function waitUntilLiveSavedOnAllServers (videoId: string) {
87 for (const server of servers) { 82 for (const server of servers) {
88 await waitUntilLiveSaved(server.url, server.accessToken, videoId) 83 await server.liveCommand.waitUntilLiveSaved({ videoId })
89 } 84 }
90 } 85 }
91 86
@@ -136,7 +131,7 @@ describe('Save replay setting', function () {
136 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 131 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
137 this.timeout(30000) 132 this.timeout(30000)
138 133
139 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 134 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
140 135
141 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 136 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
142 137
@@ -152,7 +147,7 @@ describe('Save replay setting', function () {
152 await stopFfmpeg(ffmpegCommand) 147 await stopFfmpeg(ffmpegCommand)
153 148
154 for (const server of servers) { 149 for (const server of servers) {
155 await waitUntilLiveEnded(server.url, server.accessToken, liveVideoUUID) 150 await server.liveCommand.waitUntilLiveEnded({ videoId: liveVideoUUID })
156 } 151 }
157 await waitJobs(servers) 152 await waitJobs(servers)
158 153
@@ -169,7 +164,7 @@ describe('Save replay setting', function () {
169 164
170 liveVideoUUID = await createLiveWrapper(false) 165 liveVideoUUID = await createLiveWrapper(false)
171 166
172 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 167 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
173 168
174 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 169 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
175 170
@@ -198,7 +193,7 @@ describe('Save replay setting', function () {
198 193
199 liveVideoUUID = await createLiveWrapper(false) 194 liveVideoUUID = await createLiveWrapper(false)
200 195
201 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 196 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
202 197
203 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 198 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
204 199
@@ -234,7 +229,7 @@ describe('Save replay setting', function () {
234 it('Should correctly have updated the live and federated it when streaming in the live', async function () { 229 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
235 this.timeout(20000) 230 this.timeout(20000)
236 231
237 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 232 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
238 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 233 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
239 234
240 await waitJobs(servers) 235 await waitJobs(servers)
@@ -278,7 +273,7 @@ describe('Save replay setting', function () {
278 273
279 liveVideoUUID = await createLiveWrapper(true) 274 liveVideoUUID = await createLiveWrapper(true)
280 275
281 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 276 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
282 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 277 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
283 278
284 await waitJobs(servers) 279 await waitJobs(servers)
@@ -306,7 +301,7 @@ describe('Save replay setting', function () {
306 301
307 liveVideoUUID = await createLiveWrapper(true) 302 liveVideoUUID = await createLiveWrapper(true)
308 303
309 ffmpegCommand = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 304 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
310 await waitUntilLivePublishedOnAllServers(liveVideoUUID) 305 await waitUntilLivePublishedOnAllServers(liveVideoUUID)
311 306
312 await waitJobs(servers) 307 await waitJobs(servers)
diff --git a/server/tests/api/live/live-socket-messages.ts b/server/tests/api/live/live-socket-messages.ts
index 20fec16a9..73a300384 100644
--- a/server/tests/api/live/live-socket-messages.ts
+++ b/server/tests/api/live/live-socket-messages.ts
@@ -5,11 +5,9 @@ import * as chai from 'chai'
5import { VideoPrivacy, VideoState } from '@shared/models' 5import { VideoPrivacy, VideoState } from '@shared/models'
6import { 6import {
7 cleanupTests, 7 cleanupTests,
8 createLive,
9 doubleFollow, 8 doubleFollow,
10 flushAndRunMultipleServers, 9 flushAndRunMultipleServers,
11 getVideoIdFromUUID, 10 getVideoIdFromUUID,
12 sendRTMPStreamInVideo,
13 ServerInfo, 11 ServerInfo,
14 setAccessTokensToServers, 12 setAccessTokensToServers,
15 setDefaultVideoChannel, 13 setDefaultVideoChannel,
@@ -17,7 +15,6 @@ import {
17 viewVideo, 15 viewVideo,
18 wait, 16 wait,
19 waitJobs, 17 waitJobs,
20 waitUntilLiveEnded,
21 waitUntilLivePublishedOnAllServers 18 waitUntilLivePublishedOnAllServers
22} from '../../../../shared/extra-utils' 19} from '../../../../shared/extra-utils'
23 20
@@ -60,8 +57,8 @@ describe('Test live', function () {
60 privacy: VideoPrivacy.PUBLIC 57 privacy: VideoPrivacy.PUBLIC
61 } 58 }
62 59
63 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 60 const { uuid } = await servers[0].liveCommand.createLive({ fields: liveAttributes })
64 return res.body.video.uuid 61 return uuid
65 } 62 }
66 63
67 it('Should correctly send a message when the live starts and ends', async function () { 64 it('Should correctly send a message when the live starts and ends', async function () {
@@ -89,7 +86,7 @@ describe('Test live', function () {
89 remoteSocket.emit('subscribe', { videoId }) 86 remoteSocket.emit('subscribe', { videoId })
90 } 87 }
91 88
92 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 89 const ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
93 90
94 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 91 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
95 await waitJobs(servers) 92 await waitJobs(servers)
@@ -99,10 +96,10 @@ describe('Test live', function () {
99 expect(stateChanges[stateChanges.length - 1]).to.equal(VideoState.PUBLISHED) 96 expect(stateChanges[stateChanges.length - 1]).to.equal(VideoState.PUBLISHED)
100 } 97 }
101 98
102 await stopFfmpeg(command) 99 await stopFfmpeg(ffmpegCommand)
103 100
104 for (const server of servers) { 101 for (const server of servers) {
105 await waitUntilLiveEnded(server.url, server.accessToken, liveVideoUUID) 102 await server.liveCommand.waitUntilLiveEnded({ videoId: liveVideoUUID })
106 } 103 }
107 await waitJobs(servers) 104 await waitJobs(servers)
108 105
@@ -137,7 +134,7 @@ describe('Test live', function () {
137 remoteSocket.emit('subscribe', { videoId }) 134 remoteSocket.emit('subscribe', { videoId })
138 } 135 }
139 136
140 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 137 const ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
141 138
142 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 139 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
143 await waitJobs(servers) 140 await waitJobs(servers)
@@ -155,7 +152,7 @@ describe('Test live', function () {
155 expect(localLastVideoViews).to.equal(2) 152 expect(localLastVideoViews).to.equal(2)
156 expect(remoteLastVideoViews).to.equal(2) 153 expect(remoteLastVideoViews).to.equal(2)
157 154
158 await stopFfmpeg(command) 155 await stopFfmpeg(ffmpegCommand)
159 }) 156 })
160 157
161 it('Should not receive a notification after unsubscribe', async function () { 158 it('Should not receive a notification after unsubscribe', async function () {
@@ -172,7 +169,7 @@ describe('Test live', function () {
172 socket.on('state-change', data => stateChanges.push(data.state)) 169 socket.on('state-change', data => stateChanges.push(data.state))
173 socket.emit('subscribe', { videoId }) 170 socket.emit('subscribe', { videoId })
174 171
175 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoUUID) 172 const command = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
176 173
177 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID) 174 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
178 await waitJobs(servers) 175 await waitJobs(servers)
diff --git a/server/tests/api/live/live-views.ts b/server/tests/api/live/live-views.ts
index ca571c962..ae6af7cfd 100644
--- a/server/tests/api/live/live-views.ts
+++ b/server/tests/api/live/live-views.ts
@@ -6,11 +6,9 @@ import { FfmpegCommand } from 'fluent-ffmpeg'
6import { VideoDetails, VideoPrivacy } from '@shared/models' 6import { VideoDetails, VideoPrivacy } from '@shared/models'
7import { 7import {
8 cleanupTests, 8 cleanupTests,
9 createLive,
10 doubleFollow, 9 doubleFollow,
11 flushAndRunMultipleServers, 10 flushAndRunMultipleServers,
12 getVideo, 11 getVideo,
13 sendRTMPStreamInVideo,
14 ServerInfo, 12 ServerInfo,
15 setAccessTokensToServers, 13 setAccessTokensToServers,
16 setDefaultVideoChannel, 14 setDefaultVideoChannel,
@@ -73,10 +71,10 @@ describe('Test live', function () {
73 privacy: VideoPrivacy.PUBLIC 71 privacy: VideoPrivacy.PUBLIC
74 } 72 }
75 73
76 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 74 const live = await servers[0].liveCommand.createLive({ fields: liveAttributes })
77 liveVideoId = res.body.video.uuid 75 liveVideoId = live.uuid
78 76
79 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 77 command = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId })
80 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 78 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
81 await waitJobs(servers) 79 await waitJobs(servers)
82 }) 80 })
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 2c3102994..5b4e479b6 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -13,41 +13,36 @@ import {
13 checkLiveSegmentHash, 13 checkLiveSegmentHash,
14 checkResolutionsInMasterPlaylist, 14 checkResolutionsInMasterPlaylist,
15 cleanupTests, 15 cleanupTests,
16 createLive,
17 doubleFollow, 16 doubleFollow,
18 flushAndRunMultipleServers, 17 flushAndRunMultipleServers,
19 getLive,
20 getMyVideosWithFilter, 18 getMyVideosWithFilter,
21 getPlaylist, 19 getPlaylist,
22 getVideo, 20 getVideo,
23 getVideosList, 21 getVideosList,
24 getVideosWithFilters, 22 getVideosWithFilters,
25 killallServers, 23 killallServers,
24 LiveCommand,
26 makeRawRequest, 25 makeRawRequest,
27 removeVideo, 26 removeVideo,
28 reRunServer, 27 reRunServer,
29 sendRTMPStream, 28 sendRTMPStream,
30 sendRTMPStreamInVideo,
31 ServerInfo, 29 ServerInfo,
32 setAccessTokensToServers, 30 setAccessTokensToServers,
33 setDefaultVideoChannel, 31 setDefaultVideoChannel,
34 stopFfmpeg, 32 stopFfmpeg,
35 testFfmpegStreamError, 33 testFfmpegStreamError,
36 testImage, 34 testImage,
37 updateLive,
38 uploadVideoAndGetId, 35 uploadVideoAndGetId,
39 wait, 36 wait,
40 waitJobs, 37 waitJobs,
41 waitUntilLiveEnded, 38 waitUntilLivePublishedOnAllServers
42 waitUntilLivePublished,
43 waitUntilLivePublishedOnAllServers,
44 waitUntilLiveSegmentGeneration
45} from '../../../../shared/extra-utils' 39} from '../../../../shared/extra-utils'
46 40
47const expect = chai.expect 41const expect = chai.expect
48 42
49describe('Test live', function () { 43describe('Test live', function () {
50 let servers: ServerInfo[] = [] 44 let servers: ServerInfo[] = []
45 let commands: LiveCommand[]
51 46
52 before(async function () { 47 before(async function () {
53 this.timeout(120000) 48 this.timeout(120000)
@@ -72,6 +67,8 @@ describe('Test live', function () {
72 67
73 // Server 1 and server 2 follow each other 68 // Server 1 and server 2 follow each other
74 await doubleFollow(servers[0], servers[1]) 69 await doubleFollow(servers[0], servers[1])
70
71 commands = servers.map(s => s.liveCommand)
75 }) 72 })
76 73
77 describe('Live creation, update and delete', function () { 74 describe('Live creation, update and delete', function () {
@@ -99,8 +96,8 @@ describe('Test live', function () {
99 thumbnailfile: 'video_short1.webm.jpg' 96 thumbnailfile: 'video_short1.webm.jpg'
100 } 97 }
101 98
102 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 99 const live = await commands[0].createLive({ fields: attributes })
103 liveVideoUUID = res.body.video.uuid 100 liveVideoUUID = live.uuid
104 101
105 await waitJobs(servers) 102 await waitJobs(servers)
106 103
@@ -130,8 +127,7 @@ describe('Test live', function () {
130 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 127 await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
131 await testImage(server.url, 'video_short1.webm', video.thumbnailPath) 128 await testImage(server.url, 'video_short1.webm', video.thumbnailPath)
132 129
133 const resLive = await getLive(server.url, server.accessToken, liveVideoUUID) 130 const live = await server.liveCommand.getLive({ videoId: liveVideoUUID })
134 const live: LiveVideo = resLive.body
135 131
136 if (server.url === servers[0].url) { 132 if (server.url === servers[0].url) {
137 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live') 133 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
@@ -155,8 +151,8 @@ describe('Test live', function () {
155 nsfw: true 151 nsfw: true
156 } 152 }
157 153
158 const res = await createLive(servers[0].url, servers[0].accessToken, attributes) 154 const live = await commands[0].createLive({ fields: attributes })
159 const videoId = res.body.video.uuid 155 const videoId = live.uuid
160 156
161 await waitJobs(servers) 157 await waitJobs(servers)
162 158
@@ -182,20 +178,19 @@ describe('Test live', function () {
182 }) 178 })
183 179
184 it('Should not be able to update a live of another server', async function () { 180 it('Should not be able to update a live of another server', async function () {
185 await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, HttpStatusCode.FORBIDDEN_403) 181 await commands[1].updateLive({ videoId: liveVideoUUID, fields: { saveReplay: false }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
186 }) 182 })
187 183
188 it('Should update the live', async function () { 184 it('Should update the live', async function () {
189 this.timeout(10000) 185 this.timeout(10000)
190 186
191 await updateLive(servers[0].url, servers[0].accessToken, liveVideoUUID, { saveReplay: false }) 187 await commands[0].updateLive({ videoId: liveVideoUUID, fields: { saveReplay: false } })
192 await waitJobs(servers) 188 await waitJobs(servers)
193 }) 189 })
194 190
195 it('Have the live updated', async function () { 191 it('Have the live updated', async function () {
196 for (const server of servers) { 192 for (const server of servers) {
197 const res = await getLive(server.url, server.accessToken, liveVideoUUID) 193 const live = await server.liveCommand.getLive({ videoId: liveVideoUUID })
198 const live: LiveVideo = res.body
199 194
200 if (server.url === servers[0].url) { 195 if (server.url === servers[0].url) {
201 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live') 196 expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':' + servers[0].rtmpPort + '/live')
@@ -219,13 +214,13 @@ describe('Test live', function () {
219 it('Should have the live deleted', async function () { 214 it('Should have the live deleted', async function () {
220 for (const server of servers) { 215 for (const server of servers) {
221 await getVideo(server.url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 216 await getVideo(server.url, liveVideoUUID, HttpStatusCode.NOT_FOUND_404)
222 await getLive(server.url, server.accessToken, liveVideoUUID, HttpStatusCode.NOT_FOUND_404) 217 await server.liveCommand.getLive({ videoId: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
223 } 218 }
224 }) 219 })
225 }) 220 })
226 221
227 describe('Live filters', function () { 222 describe('Live filters', function () {
228 let command: any 223 let ffmpegCommand: any
229 let liveVideoId: string 224 let liveVideoId: string
230 let vodVideoId: string 225 let vodVideoId: string
231 226
@@ -235,10 +230,10 @@ describe('Test live', function () {
235 vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid 230 vodVideoId = (await uploadVideoAndGetId({ server: servers[0], videoName: 'vod video' })).uuid
236 231
237 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id } 232 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: servers[0].videoChannel.id }
238 const resLive = await createLive(servers[0].url, servers[0].accessToken, liveOptions) 233 const live = await commands[0].createLive({ fields: liveOptions })
239 liveVideoId = resLive.body.video.uuid 234 liveVideoId = live.uuid
240 235
241 command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 236 ffmpegCommand = await servers[0].liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId })
242 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 237 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
243 await waitJobs(servers) 238 await waitJobs(servers)
244 }) 239 })
@@ -262,7 +257,7 @@ describe('Test live', function () {
262 it('Should display my lives', async function () { 257 it('Should display my lives', async function () {
263 this.timeout(60000) 258 this.timeout(60000)
264 259
265 await stopFfmpeg(command) 260 await stopFfmpeg(ffmpegCommand)
266 await waitJobs(servers) 261 await waitJobs(servers)
267 262
268 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true }) 263 const res = await getMyVideosWithFilter(servers[0].url, servers[0].accessToken, { isLive: true })
@@ -302,13 +297,12 @@ describe('Test live', function () {
302 saveReplay: false 297 saveReplay: false
303 } 298 }
304 299
305 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 300 const { uuid } = await commands[0].createLive({ fields: liveAttributes })
306 const uuid = res.body.video.uuid
307 301
308 const resLive = await getLive(servers[0].url, servers[0].accessToken, uuid) 302 const live = await commands[0].getLive({ videoId: uuid })
309 const resVideo = await getVideo(servers[0].url, uuid) 303 const resVideo = await getVideo(servers[0].url, uuid)
310 304
311 return Object.assign(resVideo.body, resLive.body) as LiveVideo & VideoDetails 305 return Object.assign(resVideo.body as VideoDetails, live)
312 } 306 }
313 307
314 it('Should not allow a stream without the appropriate path', async function () { 308 it('Should not allow a stream without the appropriate path', async function () {
@@ -382,8 +376,8 @@ describe('Test live', function () {
382 saveReplay 376 saveReplay
383 } 377 }
384 378
385 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 379 const { uuid } = await commands[0].createLive({ fields: liveAttributes })
386 return res.body.video.uuid 380 return uuid
387 } 381 }
388 382
389 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) { 383 async function testVideoResolutions (liveVideoId: string, resolutions: number[]) {
@@ -409,7 +403,7 @@ describe('Test live', function () {
409 for (let i = 0; i < resolutions.length; i++) { 403 for (let i = 0; i < resolutions.length; i++) {
410 const segmentNum = 3 404 const segmentNum = 3
411 const segmentName = `${i}-00000${segmentNum}.ts` 405 const segmentName = `${i}-00000${segmentNum}.ts`
412 await waitUntilLiveSegmentGeneration(servers[0], video.uuid, i, segmentNum) 406 await commands[0].waitUntilLiveSegmentGeneration({ videoUUID: video.uuid, resolution: i, segment: segmentNum })
413 407
414 const res = await getPlaylist(`${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`) 408 const res = await getPlaylist(`${servers[0].url}/static/streaming-playlists/hls/${video.uuid}/${i}.m3u8`)
415 const subPlaylist = res.text 409 const subPlaylist = res.text
@@ -454,13 +448,13 @@ describe('Test live', function () {
454 448
455 liveVideoId = await createLiveWrapper(false) 449 liveVideoId = await createLiveWrapper(false)
456 450
457 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 451 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId })
458 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 452 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
459 await waitJobs(servers) 453 await waitJobs(servers)
460 454
461 await testVideoResolutions(liveVideoId, [ 720 ]) 455 await testVideoResolutions(liveVideoId, [ 720 ])
462 456
463 await stopFfmpeg(command) 457 await stopFfmpeg(ffmpegCommand)
464 }) 458 })
465 459
466 it('Should enable transcoding with some resolutions', async function () { 460 it('Should enable transcoding with some resolutions', async function () {
@@ -470,13 +464,13 @@ describe('Test live', function () {
470 await updateConf(resolutions) 464 await updateConf(resolutions)
471 liveVideoId = await createLiveWrapper(false) 465 liveVideoId = await createLiveWrapper(false)
472 466
473 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId) 467 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId })
474 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 468 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
475 await waitJobs(servers) 469 await waitJobs(servers)
476 470
477 await testVideoResolutions(liveVideoId, resolutions) 471 await testVideoResolutions(liveVideoId, resolutions)
478 472
479 await stopFfmpeg(command) 473 await stopFfmpeg(ffmpegCommand)
480 }) 474 })
481 475
482 it('Should enable transcoding with some resolutions and correctly save them', async function () { 476 it('Should enable transcoding with some resolutions and correctly save them', async function () {
@@ -487,14 +481,14 @@ describe('Test live', function () {
487 await updateConf(resolutions) 481 await updateConf(resolutions)
488 liveVideoId = await createLiveWrapper(true) 482 liveVideoId = await createLiveWrapper(true)
489 483
490 const command = await sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId, 'video_short2.webm') 484 const ffmpegCommand = await commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
491 await waitUntilLivePublishedOnAllServers(servers, liveVideoId) 485 await waitUntilLivePublishedOnAllServers(servers, liveVideoId)
492 await waitJobs(servers) 486 await waitJobs(servers)
493 487
494 await testVideoResolutions(liveVideoId, resolutions) 488 await testVideoResolutions(liveVideoId, resolutions)
495 489
496 await stopFfmpeg(command) 490 await stopFfmpeg(ffmpegCommand)
497 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 491 await commands[0].waitUntilLiveEnded({ videoId: liveVideoId })
498 492
499 await waitJobs(servers) 493 await waitJobs(servers)
500 494
@@ -565,8 +559,8 @@ describe('Test live', function () {
565 saveReplay 559 saveReplay
566 } 560 }
567 561
568 const res = await createLive(servers[0].url, servers[0].accessToken, liveAttributes) 562 const { uuid } = await commands[0].createLive({ fields: liveAttributes })
569 return res.body.video.uuid 563 return uuid
570 } 564 }
571 565
572 before(async function () { 566 before(async function () {
@@ -576,17 +570,17 @@ describe('Test live', function () {
576 liveVideoReplayId = await createLiveWrapper(true) 570 liveVideoReplayId = await createLiveWrapper(true)
577 571
578 await Promise.all([ 572 await Promise.all([
579 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoId), 573 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoId }),
580 sendRTMPStreamInVideo(servers[0].url, servers[0].accessToken, liveVideoReplayId) 574 commands[0].sendRTMPStreamInVideo({ videoId: liveVideoReplayId })
581 ]) 575 ])
582 576
583 await Promise.all([ 577 await Promise.all([
584 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoId), 578 commands[0].waitUntilLivePublished({ videoId: liveVideoId }),
585 waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 579 commands[0].waitUntilLivePublished({ videoId: liveVideoReplayId })
586 ]) 580 ])
587 581
588 await waitUntilLiveSegmentGeneration(servers[0], liveVideoId, 0, 2) 582 await commands[0].waitUntilLiveSegmentGeneration({ videoUUID: liveVideoId, resolution: 0, segment: 2 })
589 await waitUntilLiveSegmentGeneration(servers[0], liveVideoReplayId, 0, 2) 583 await commands[0].waitUntilLiveSegmentGeneration({ videoUUID: liveVideoReplayId, resolution: 0, segment: 2 })
590 584
591 await killallServers([ servers[0] ]) 585 await killallServers([ servers[0] ])
592 await reRunServer(servers[0]) 586 await reRunServer(servers[0])
@@ -597,13 +591,13 @@ describe('Test live', function () {
597 it('Should cleanup lives', async function () { 591 it('Should cleanup lives', async function () {
598 this.timeout(60000) 592 this.timeout(60000)
599 593
600 await waitUntilLiveEnded(servers[0].url, servers[0].accessToken, liveVideoId) 594 await commands[0].waitUntilLiveEnded({ videoId: liveVideoId })
601 }) 595 })
602 596
603 it('Should save a live replay', async function () { 597 it('Should save a live replay', async function () {
604 this.timeout(120000) 598 this.timeout(120000)
605 599
606 await waitUntilLivePublished(servers[0].url, servers[0].accessToken, liveVideoReplayId) 600 await commands[0].waitUntilLivePublished({ videoId: liveVideoReplayId })
607 }) 601 })
608 }) 602 })
609 603
diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts
index 7dc89c447..af74b26a7 100644
--- a/server/tests/api/search/search-videos.ts
+++ b/server/tests/api/search/search-videos.ts
@@ -2,23 +2,20 @@
2 2
3import 'mocha' 3import 'mocha'
4import * as chai from 'chai' 4import * as chai from 'chai'
5import { VideoPrivacy } from '@shared/models'
6import { 5import {
7 cleanupTests, 6 cleanupTests,
8 createLive, 7 createVideoCaption,
9 flushAndRunServer, 8 flushAndRunServer,
10 immutableAssign, 9 immutableAssign,
11 SearchCommand, 10 SearchCommand,
12 sendRTMPStreamInVideo,
13 ServerInfo, 11 ServerInfo,
14 setAccessTokensToServers, 12 setAccessTokensToServers,
15 setDefaultVideoChannel, 13 setDefaultVideoChannel,
16 stopFfmpeg, 14 stopFfmpeg,
17 uploadVideo, 15 uploadVideo,
18 wait, 16 wait
19 waitUntilLivePublished 17} from '@shared/extra-utils'
20} from '../../../../shared/extra-utils' 18import { VideoPrivacy } from '@shared/models'
21import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
22 19
23const expect = chai.expect 20const expect = chai.expect
24 21
@@ -502,12 +499,13 @@ describe('Test videos search', function () {
502 } 499 }
503 500
504 { 501 {
505 const liveOptions = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id } 502 const liveCommand = server.liveCommand
506 const resLive = await createLive(server.url, server.accessToken, liveOptions) 503
507 const liveVideoId = resLive.body.video.uuid 504 const liveAttributes = { name: 'live', privacy: VideoPrivacy.PUBLIC, channelId: server.videoChannel.id }
505 const live = await liveCommand.createLive({ fields: liveAttributes })
508 506
509 const ffmpegCommand = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId) 507 const ffmpegCommand = await liveCommand.sendRTMPStreamInVideo({ videoId: live.id })
510 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 508 await liveCommand.waitUntilLivePublished({ videoId: live.id })
511 509
512 const body = await command.advancedVideoSearch({ search: { isLive: true } }) 510 const body = await command.advancedVideoSearch({ search: { isLive: true } })
513 511
diff --git a/server/tests/api/videos/video-change-ownership.ts b/server/tests/api/videos/video-change-ownership.ts
index 89dba14b1..88e4d51a2 100644
--- a/server/tests/api/videos/video-change-ownership.ts
+++ b/server/tests/api/videos/video-change-ownership.ts
@@ -7,7 +7,6 @@ import {
7 acceptChangeOwnership, 7 acceptChangeOwnership,
8 changeVideoOwnership, 8 changeVideoOwnership,
9 cleanupTests, 9 cleanupTests,
10 createLive,
11 createUser, 10 createUser,
12 doubleFollow, 11 doubleFollow,
13 flushAndRunMultipleServers, 12 flushAndRunMultipleServers,
@@ -112,9 +111,9 @@ describe('Test video change ownership - nominal', function () {
112 111
113 { 112 {
114 const attributes = { name: 'live', channelId: firstUserChannelId, privacy: VideoPrivacy.PUBLIC } 113 const attributes = { name: 'live', channelId: firstUserChannelId, privacy: VideoPrivacy.PUBLIC }
115 const res = await createLive(servers[0].url, firstUserAccessToken, attributes) 114 const video = await servers[0].liveCommand.createLive({ token: firstUserAccessToken, fields: attributes })
116 115
117 liveId = res.body.video.id 116 liveId = video.id
118 } 117 }
119 118
120 await doubleFollow(servers[0], servers[1]) 119 await doubleFollow(servers[0], servers[1])
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts
index 0de5b523b..39266c62f 100644
--- a/server/tests/plugins/action-hooks.ts
+++ b/server/tests/plugins/action-hooks.ts
@@ -7,7 +7,6 @@ import {
7 addVideoCommentThread, 7 addVideoCommentThread,
8 addVideoInPlaylist, 8 addVideoInPlaylist,
9 blockUser, 9 blockUser,
10 createLive,
11 createUser, 10 createUser,
12 createVideoPlaylist, 11 createVideoPlaylist,
13 deleteVideoComment, 12 deleteVideoComment,
@@ -96,7 +95,7 @@ describe('Test plugin action hooks', function () {
96 channelId: servers[0].videoChannel.id 95 channelId: servers[0].videoChannel.id
97 } 96 }
98 97
99 await createLive(servers[0].url, servers[0].accessToken, attributes) 98 await servers[0].liveCommand.createLive({ fields: attributes })
100 99
101 await checkHook('action:api.live-video.created') 100 await checkHook('action:api.live-video.created')
102 }) 101 })
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index e254046bf..e60bad38d 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -7,7 +7,6 @@ import {
7 addVideoCommentReply, 7 addVideoCommentReply,
8 addVideoCommentThread, 8 addVideoCommentThread,
9 cleanupTests, 9 cleanupTests,
10 createLive,
11 createVideoPlaylist, 10 createVideoPlaylist,
12 doubleFollow, 11 doubleFollow,
13 flushAndRunMultipleServers, 12 flushAndRunMultipleServers,
@@ -156,7 +155,7 @@ describe('Test plugin filter hooks', function () {
156 channelId: servers[0].videoChannel.id 155 channelId: servers[0].videoChannel.id
157 } 156 }
158 157
159 await createLive(servers[0].url, servers[0].accessToken, attributes, HttpStatusCode.FORBIDDEN_403) 158 await servers[0].liveCommand.createLive({ fields: attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
160 }) 159 })
161 160
162 it('Should run filter:api.video.pre-import-url.accept.result', async function () { 161 it('Should run filter:api.video.pre-import-url.accept.result', async function () {
diff --git a/server/tests/plugins/plugin-transcoding.ts b/server/tests/plugins/plugin-transcoding.ts
index 71c2adc72..65282419e 100644
--- a/server/tests/plugins/plugin-transcoding.ts
+++ b/server/tests/plugins/plugin-transcoding.ts
@@ -7,18 +7,15 @@ import { getAudioStream, getVideoFileFPS, getVideoStreamFromFile } from '@server
7import { 7import {
8 buildServerDirectory, 8 buildServerDirectory,
9 cleanupTests, 9 cleanupTests,
10 createLive,
11 flushAndRunServer, 10 flushAndRunServer,
12 getVideo, 11 getVideo,
13 PluginsCommand, 12 PluginsCommand,
14 sendRTMPStreamInVideo,
15 ServerInfo, 13 ServerInfo,
16 setAccessTokensToServers, 14 setAccessTokensToServers,
17 setDefaultVideoChannel, 15 setDefaultVideoChannel,
18 testFfmpegStreamError, 16 testFfmpegStreamError,
19 uploadVideoAndGetId, 17 uploadVideoAndGetId,
20 waitJobs, 18 waitJobs
21 waitUntilLivePublished
22} from '@shared/extra-utils' 19} from '@shared/extra-utils'
23import { VideoDetails, VideoPrivacy } from '@shared/models' 20import { VideoDetails, VideoPrivacy } from '@shared/models'
24 21
@@ -29,8 +26,9 @@ async function createLiveWrapper (server: ServerInfo) {
29 privacy: VideoPrivacy.PUBLIC 26 privacy: VideoPrivacy.PUBLIC
30 } 27 }
31 28
32 const res = await createLive(server.url, server.accessToken, liveAttributes) 29 const { uuid } = await server.liveCommand.createLive({ fields: liveAttributes })
33 return res.body.video.uuid 30
31 return uuid
34} 32}
35 33
36function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) { 34function updateConf (server: ServerInfo, vodProfile: string, liveProfile: string) {
@@ -171,8 +169,8 @@ describe('Test transcoding plugins', function () {
171 169
172 const liveVideoId = await createLiveWrapper(server) 170 const liveVideoId = await createLiveWrapper(server)
173 171
174 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 172 await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
175 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 173 await server.liveCommand.waitUntilLivePublished({ videoId: liveVideoId })
176 await waitJobs([ server ]) 174 await waitJobs([ server ])
177 175
178 await checkLiveFPS(liveVideoId, 'above', 20) 176 await checkLiveFPS(liveVideoId, 'above', 20)
@@ -185,8 +183,8 @@ describe('Test transcoding plugins', function () {
185 183
186 const liveVideoId = await createLiveWrapper(server) 184 const liveVideoId = await createLiveWrapper(server)
187 185
188 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 186 await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
189 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 187 await server.liveCommand.waitUntilLivePublished({ videoId: liveVideoId })
190 await waitJobs([ server ]) 188 await waitJobs([ server ])
191 189
192 await checkLiveFPS(liveVideoId, 'below', 12) 190 await checkLiveFPS(liveVideoId, 'below', 12)
@@ -199,8 +197,8 @@ describe('Test transcoding plugins', function () {
199 197
200 const liveVideoId = await createLiveWrapper(server) 198 const liveVideoId = await createLiveWrapper(server)
201 199
202 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 200 await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
203 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 201 await server.liveCommand.waitUntilLivePublished({ videoId: liveVideoId })
204 await waitJobs([ server ]) 202 await waitJobs([ server ])
205 203
206 await checkLiveFPS(liveVideoId, 'below', 6) 204 await checkLiveFPS(liveVideoId, 'below', 6)
@@ -213,7 +211,7 @@ describe('Test transcoding plugins', function () {
213 211
214 const liveVideoId = await createLiveWrapper(server) 212 const liveVideoId = await createLiveWrapper(server)
215 213
216 const command = await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 214 const command = await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
217 await testFfmpegStreamError(command, true) 215 await testFfmpegStreamError(command, true)
218 }) 216 })
219 217
@@ -262,8 +260,8 @@ describe('Test transcoding plugins', function () {
262 260
263 const liveVideoId = await createLiveWrapper(server) 261 const liveVideoId = await createLiveWrapper(server)
264 262
265 await sendRTMPStreamInVideo(server.url, server.accessToken, liveVideoId, 'video_short2.webm') 263 await server.liveCommand.sendRTMPStreamInVideo({ videoId: liveVideoId, fixtureName: 'video_short2.webm' })
266 await waitUntilLivePublished(server.url, server.accessToken, liveVideoId) 264 await server.liveCommand.waitUntilLivePublished({ videoId: liveVideoId })
267 await waitJobs([ server ]) 265 await waitJobs([ server ])
268 266
269 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8` 267 const playlistUrl = `${server.url}/static/streaming-playlists/hls/${liveVideoId}/0.m3u8`
diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts
index 68900af26..4b3636d06 100644
--- a/shared/extra-utils/index.ts
+++ b/shared/extra-utils/index.ts
@@ -7,21 +7,9 @@ export * from './miscs'
7export * from './mock-servers' 7export * from './mock-servers'
8export * from './moderation' 8export * from './moderation'
9export * from './overviews' 9export * from './overviews'
10export * from './requests'
10export * from './search' 11export * from './search'
11export * from './server' 12export * from './server'
12export * from './socket' 13export * from './socket'
13export * from './users' 14export * from './users'
14 15export * from './videos'
15export * from './requests/check-api-params'
16export * from './requests/requests'
17
18export * from './videos/live'
19export * from './videos/services'
20export * from './videos/video-blacklist'
21export * from './videos/video-captions'
22export * from './videos/video-change-ownership'
23export * from './videos/video-channels'
24export * from './videos/video-comments'
25export * from './videos/video-playlists'
26export * from './videos/video-streaming-playlists'
27export * from './videos/videos'
diff --git a/shared/extra-utils/requests/index.ts b/shared/extra-utils/requests/index.ts
new file mode 100644
index 000000000..501163f92
--- /dev/null
+++ b/shared/extra-utils/requests/index.ts
@@ -0,0 +1,3 @@
1// Don't include activitypub that import stuff from server
2export * from './check-api-params'
3export * from './requests'
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts
index 57b37728a..eca0689aa 100644
--- a/shared/extra-utils/server/servers.ts
+++ b/shared/extra-utils/server/servers.ts
@@ -18,6 +18,7 @@ import { makeGetRequest } from '../requests/requests'
18import { SearchCommand } from '../search' 18import { SearchCommand } from '../search'
19import { SocketIOCommand } from '../socket' 19import { SocketIOCommand } from '../socket'
20import { AccountsCommand, BlocklistCommand, SubscriptionsCommand } from '../users' 20import { AccountsCommand, BlocklistCommand, SubscriptionsCommand } from '../users'
21import { LiveCommand } from '../videos'
21import { ConfigCommand } from './config-command' 22import { ConfigCommand } from './config-command'
22import { ContactFormCommand } from './contact-form-command' 23import { ContactFormCommand } from './contact-form-command'
23import { DebugCommand } from './debug-command' 24import { DebugCommand } from './debug-command'
@@ -99,6 +100,7 @@ interface ServerInfo {
99 accountsCommand?: AccountsCommand 100 accountsCommand?: AccountsCommand
100 blocklistCommand?: BlocklistCommand 101 blocklistCommand?: BlocklistCommand
101 subscriptionsCommand?: SubscriptionsCommand 102 subscriptionsCommand?: SubscriptionsCommand
103 liveCommand?: LiveCommand
102} 104}
103 105
104function parallelTests () { 106function parallelTests () {
@@ -324,6 +326,7 @@ async function runServer (server: ServerInfo, configOverrideArg?: any, args = []
324 server.accountsCommand = new AccountsCommand(server) 326 server.accountsCommand = new AccountsCommand(server)
325 server.blocklistCommand = new BlocklistCommand(server) 327 server.blocklistCommand = new BlocklistCommand(server)
326 server.subscriptionsCommand = new SubscriptionsCommand(server) 328 server.subscriptionsCommand = new SubscriptionsCommand(server)
329 server.liveCommand = new LiveCommand(server)
327 330
328 res(server) 331 res(server)
329 }) 332 })
diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts
index dd4598a91..38129d559 100644
--- a/shared/extra-utils/shared/abstract-command.ts
+++ b/shared/extra-utils/shared/abstract-command.ts
@@ -1,5 +1,5 @@
1import { HttpStatusCode } from '@shared/core-utils' 1import { HttpStatusCode } from '@shared/core-utils'
2import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, unwrapBody, unwrapText } from '../requests/requests' 2import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest, unwrapBody, unwrapText } from '../requests/requests'
3import { ServerInfo } from '../server/servers' 3import { ServerInfo } from '../server/servers'
4 4
5export interface OverrideCommandOptions { 5export interface OverrideCommandOptions {
@@ -86,6 +86,36 @@ abstract class AbstractCommand {
86 }) 86 })
87 } 87 }
88 88
89 protected postUploadRequest (options: CommonCommandOptions & {
90 fields?: { [ fieldName: string ]: any }
91 attaches?: any
92 }) {
93 const { fields, attaches } = options
94
95 return makeUploadRequest({
96 ...this.buildCommonRequestOptions(options),
97
98 method: 'POST',
99 fields,
100 attaches
101 })
102 }
103
104 protected putUploadRequest (options: CommonCommandOptions & {
105 fields?: { [ fieldName: string ]: any }
106 attaches?: any
107 }) {
108 const { fields, attaches } = options
109
110 return makeUploadRequest({
111 ...this.buildCommonRequestOptions(options),
112
113 method: 'PUT',
114 fields,
115 attaches
116 })
117 }
118
89 private buildCommonRequestOptions (options: CommonCommandOptions) { 119 private buildCommonRequestOptions (options: CommonCommandOptions) {
90 const { token, expectedStatus, defaultExpectedStatus, path } = options 120 const { token, expectedStatus, defaultExpectedStatus, path } = options
91 121
diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts
index 94ad37242..9f760d7fd 100644
--- a/shared/extra-utils/users/index.ts
+++ b/shared/extra-utils/users/index.ts
@@ -1,7 +1,6 @@
1export * from './accounts-command' 1export * from './accounts-command'
2export * from './accounts' 2export * from './accounts'
3export * from './blocklist-command' 3export * from './blocklist-command'
4
5export * from './login' 4export * from './login'
6export * from './user-notifications' 5export * from './user-notifications'
7export * from './subscriptions-command' 6export * from './subscriptions-command'
diff --git a/shared/extra-utils/videos/index.ts b/shared/extra-utils/videos/index.ts
new file mode 100644
index 000000000..c9c884285
--- /dev/null
+++ b/shared/extra-utils/videos/index.ts
@@ -0,0 +1,13 @@
1export * from './live-command'
2export * from './live'
3export * from './services'
4export * from './video-blacklist'
5export * from './video-captions'
6export * from './video-change-ownership'
7export * from './video-channels'
8export * from './video-comments'
9export * from './video-history'
10export * from './video-imports'
11export * from './video-playlists'
12export * from './video-streaming-playlists'
13export * from './videos'
diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts
new file mode 100644
index 000000000..55811b8ba
--- /dev/null
+++ b/shared/extra-utils/videos/live-command.ts
@@ -0,0 +1,156 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readdir } from 'fs-extra'
4import { omit } from 'lodash'
5import { join } from 'path'
6import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
7import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
8import { buildServerDirectory, wait } from '../miscs/miscs'
9import { unwrapBody } from '../requests'
10import { waitUntilLog } from '../server/servers'
11import { AbstractCommand, OverrideCommandOptions } from '../shared'
12import { sendRTMPStream, testFfmpegStreamError } from './live'
13import { getVideoWithToken } from './videos'
14
15export class LiveCommand extends AbstractCommand {
16
17 getLive (options: OverrideCommandOptions & {
18 videoId: number | string
19 }) {
20 const path = '/api/v1/videos/live'
21
22 return this.getRequestBody<LiveVideo>({
23 ...options,
24
25 path: path + '/' + options.videoId,
26 defaultExpectedStatus: HttpStatusCode.OK_200
27 })
28 }
29
30 updateLive (options: OverrideCommandOptions & {
31 videoId: number | string
32 fields: LiveVideoUpdate
33 }) {
34 const { videoId, fields } = options
35 const path = '/api/v1/videos/live'
36
37 return this.putBodyRequest({
38 ...options,
39
40 path: path + '/' + videoId,
41 fields,
42 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
43 })
44 }
45
46 async createLive (options: OverrideCommandOptions & {
47 fields: LiveVideoCreate
48 }) {
49 const { fields } = options
50 const path = '/api/v1/videos/live'
51
52 const attaches: any = {}
53 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
54 if (fields.previewfile) attaches.previewfile = fields.previewfile
55
56 const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
57 ...options,
58
59 path,
60 attaches,
61 fields: omit(fields, 'thumbnailfile', 'previewfile'),
62 defaultExpectedStatus: HttpStatusCode.OK_200
63 }))
64
65 return body.video
66 }
67
68 async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
69 videoId: number | string
70 fixtureName?: string
71 }) {
72 const { videoId, fixtureName } = options
73 const videoLive = await this.getLive({ videoId })
74
75 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
76 }
77
78 async runAndTestFfmpegStreamError (options: OverrideCommandOptions & {
79 videoId: number | string
80 shouldHaveError: boolean
81 }) {
82 const command = await this.sendRTMPStreamInVideo(options)
83
84 return testFfmpegStreamError(command, options.shouldHaveError)
85 }
86
87 waitUntilLivePublished (options: OverrideCommandOptions & {
88 videoId: number | string
89 }) {
90 const { videoId } = options
91 return this.waitUntilLiveState({ videoId, state: VideoState.PUBLISHED })
92 }
93
94 waitUntilLiveWaiting (options: OverrideCommandOptions & {
95 videoId: number | string
96 }) {
97 const { videoId } = options
98 return this.waitUntilLiveState({ videoId, state: VideoState.WAITING_FOR_LIVE })
99 }
100
101 waitUntilLiveEnded (options: OverrideCommandOptions & {
102 videoId: number | string
103 }) {
104 const { videoId } = options
105 return this.waitUntilLiveState({ videoId, state: VideoState.LIVE_ENDED })
106 }
107
108 waitUntilLiveSegmentGeneration (options: OverrideCommandOptions & {
109 videoUUID: string
110 resolution: number
111 segment: number
112 }) {
113 const { resolution, segment, videoUUID } = options
114 const segmentName = `${resolution}-00000${segment}.ts`
115
116 return waitUntilLog(this.server, `${videoUUID}/${segmentName}`, 2, false)
117 }
118
119 async waitUntilLiveSaved (options: OverrideCommandOptions & {
120 videoId: number | string
121 }) {
122 let video: VideoDetails
123
124 do {
125 const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId)
126 video = res.body
127
128 await wait(500)
129 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
130 }
131
132 async getPlaylistsCount (options: OverrideCommandOptions & {
133 videoUUID: string
134 }) {
135 const basePath = buildServerDirectory(this.server, 'streaming-playlists')
136 const hlsPath = join(basePath, 'hls', options.videoUUID)
137
138 const files = await readdir(hlsPath)
139
140 return files.filter(f => f.endsWith('.m3u8')).length
141 }
142
143 private async waitUntilLiveState (options: OverrideCommandOptions & {
144 videoId: number | string
145 state: VideoState
146 }) {
147 let video: VideoDetails
148
149 do {
150 const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId)
151 video = res.body
152
153 await wait(500)
154 } while (video.state.id !== options.state)
155 }
156}
diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts
index c0384769b..285a39c7e 100644
--- a/shared/extra-utils/videos/live.ts
+++ b/shared/extra-utils/videos/live.ts
@@ -3,69 +3,9 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg' 4import * as ffmpeg from 'fluent-ffmpeg'
5import { pathExists, readdir } from 'fs-extra' 5import { pathExists, readdir } from 'fs-extra'
6import { omit } from 'lodash'
7import { join } from 'path' 6import { join } from 'path'
8import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoDetails, VideoState } from '@shared/models'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
10import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs' 7import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs'
11import { makeGetRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests' 8import { ServerInfo } from '../server/servers'
12import { ServerInfo, waitUntilLog } from '../server/servers'
13import { getVideoWithToken } from './videos'
14
15function getLive (url: string, token: string, videoId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
16 const path = '/api/v1/videos/live'
17
18 return makeGetRequest({
19 url,
20 token,
21 path: path + '/' + videoId,
22 statusCodeExpected
23 })
24}
25
26function updateLive (
27 url: string,
28 token: string,
29 videoId: number | string,
30 fields: LiveVideoUpdate,
31 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
32) {
33 const path = '/api/v1/videos/live'
34
35 return makePutBodyRequest({
36 url,
37 token,
38 path: path + '/' + videoId,
39 fields,
40 statusCodeExpected
41 })
42}
43
44function createLive (url: string, token: string, fields: LiveVideoCreate, statusCodeExpected = HttpStatusCode.OK_200) {
45 const path = '/api/v1/videos/live'
46
47 const attaches: any = {}
48 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
49 if (fields.previewfile) attaches.previewfile = fields.previewfile
50
51 const updatedFields = omit(fields, 'thumbnailfile', 'previewfile')
52
53 return makeUploadRequest({
54 url,
55 path,
56 token,
57 attaches,
58 fields: updatedFields,
59 statusCodeExpected
60 })
61}
62
63async function sendRTMPStreamInVideo (url: string, token: string, videoId: number | string, fixtureName?: string) {
64 const res = await getLive(url, token, videoId)
65 const videoLive = res.body as LiveVideo
66
67 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
68}
69 9
70function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') { 10function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
71 const fixture = buildAbsoluteFixturePath(fixtureName) 11 const fixture = buildAbsoluteFixturePath(fixtureName)
@@ -109,12 +49,6 @@ function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 1
109 }) 49 })
110} 50}
111 51
112async function runAndTestFfmpegStreamError (url: string, token: string, videoId: number | string, shouldHaveError: boolean) {
113 const command = await sendRTMPStreamInVideo(url, token, videoId)
114
115 return testFfmpegStreamError(command, shouldHaveError)
116}
117
118async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) { 52async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
119 let error: Error 53 let error: Error
120 54
@@ -136,48 +70,9 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
136 await wait(500) 70 await wait(500)
137} 71}
138 72
139function waitUntilLivePublished (url: string, token: string, videoId: number | string) {
140 return waitUntilLiveState(url, token, videoId, VideoState.PUBLISHED)
141}
142
143function waitUntilLiveWaiting (url: string, token: string, videoId: number | string) {
144 return waitUntilLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE)
145}
146
147function waitUntilLiveEnded (url: string, token: string, videoId: number | string) {
148 return waitUntilLiveState(url, token, videoId, VideoState.LIVE_ENDED)
149}
150
151function waitUntilLiveSegmentGeneration (server: ServerInfo, videoUUID: string, resolutionNum: number, segmentNum: number) {
152 const segmentName = `${resolutionNum}-00000${segmentNum}.ts`
153 return waitUntilLog(server, `${videoUUID}/${segmentName}`, 2, false)
154}
155
156async function waitUntilLiveState (url: string, token: string, videoId: number | string, state: VideoState) {
157 let video: VideoDetails
158
159 do {
160 const res = await getVideoWithToken(url, token, videoId)
161 video = res.body
162
163 await wait(500)
164 } while (video.state.id !== state)
165}
166
167async function waitUntilLiveSaved (url: string, token: string, videoId: number | string) {
168 let video: VideoDetails
169
170 do {
171 const res = await getVideoWithToken(url, token, videoId)
172 video = res.body
173
174 await wait(500)
175 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
176}
177
178async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) { 73async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) {
179 for (const server of servers) { 74 for (const server of servers) {
180 await waitUntilLivePublished(server.url, server.accessToken, videoId) 75 await server.liveCommand.waitUntilLivePublished({ videoId })
181 } 76 }
182} 77}
183 78
@@ -206,33 +101,11 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti
206 expect(files).to.contain('segments-sha256.json') 101 expect(files).to.contain('segments-sha256.json')
207} 102}
208 103
209async function getPlaylistsCount (server: ServerInfo, videoUUID: string) {
210 const basePath = buildServerDirectory(server, 'streaming-playlists')
211 const hlsPath = join(basePath, 'hls', videoUUID)
212
213 const files = await readdir(hlsPath)
214
215 return files.filter(f => f.endsWith('.m3u8')).length
216}
217
218// ---------------------------------------------------------------------------
219
220export { 104export {
221 getLive, 105 sendRTMPStream,
222 getPlaylistsCount,
223 waitUntilLiveSaved,
224 waitUntilLivePublished,
225 updateLive,
226 createLive,
227 runAndTestFfmpegStreamError,
228 checkLiveCleanup,
229 waitUntilLiveSegmentGeneration,
230 stopFfmpeg,
231 waitUntilLiveWaiting,
232 sendRTMPStreamInVideo,
233 waitUntilLiveEnded,
234 waitFfmpegUntilError, 106 waitFfmpegUntilError,
107 testFfmpegStreamError,
108 stopFfmpeg,
235 waitUntilLivePublishedOnAllServers, 109 waitUntilLivePublishedOnAllServers,
236 sendRTMPStream, 110 checkLiveCleanup
237 testFfmpegStreamError
238} 111}