1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import { FfmpegCommand } from 'fluent-ffmpeg'
6 import { checkLiveCleanup } from '@server/tests/shared'
7 import { wait } from '@shared/core-utils'
8 import { HttpStatusCode, LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
12 createMultipleServers,
14 findExternalSavedVideo,
16 setAccessTokensToServers,
17 setDefaultVideoChannel,
19 testFfmpegStreamError,
21 waitUntilLivePublishedOnAllServers,
22 waitUntilLiveReplacedByReplayOnAllServers,
23 waitUntilLiveWaitingOnAllServers
24 } from '@shared/server-commands'
26 const expect = chai.expect
28 describe('Save replay setting', function () {
29 let servers: PeerTubeServer[] = []
30 let liveVideoUUID: string
31 let ffmpegCommand: FfmpegCommand
33 async function createLiveWrapper (options: { permanent: boolean, replay: boolean }) {
36 await servers[0].videos.remove({ id: liveVideoUUID })
37 await waitJobs(servers)
41 const attributes: LiveVideoCreate = {
42 channelId: servers[0].store.channel.id,
43 privacy: VideoPrivacy.PUBLIC,
44 name: 'my super live',
45 saveReplay: options.replay,
46 permanentLive: options.permanent
49 const { uuid } = await servers[0].live.create({ fields: attributes })
53 async function checkVideosExist (videoId: string, existsInList: boolean, expectedStatus?: number) {
54 for (const server of servers) {
55 const length = existsInList ? 1 : 0
57 const { data, total } = await server.videos.list()
58 expect(data).to.have.lengthOf(length)
59 expect(total).to.equal(length)
62 await server.videos.get({ id: videoId, expectedStatus })
67 async function checkVideoState (videoId: string, state: VideoState) {
68 for (const server of servers) {
69 const video = await server.videos.get({ id: videoId })
70 expect(video.state.id).to.equal(state)
74 before(async function () {
77 servers = await createMultipleServers(2)
79 // Get the access tokens
80 await setAccessTokensToServers(servers)
81 await setDefaultVideoChannel(servers)
83 // Server 1 and server 2 follow each other
84 await doubleFollow(servers[0], servers[1])
86 await servers[0].config.updateCustomSubConfig({
94 resolutions: ConfigCommand.getCustomConfigResolutions(true)
101 describe('With save replay disabled', function () {
103 before(async function () {
107 it('Should correctly create and federate the "waiting for stream" live', async function () {
110 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: false })
112 await waitJobs(servers)
114 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
115 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
118 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
121 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
123 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
125 await waitJobs(servers)
127 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
128 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
131 it('Should correctly delete the video files after the stream ended', async function () {
134 await stopFfmpeg(ffmpegCommand)
136 for (const server of servers) {
137 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
139 await waitJobs(servers)
141 // Live still exist, but cannot be played anymore
142 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
143 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
145 // No resolutions saved since we did not save replay
146 await checkLiveCleanup(servers[0], liveVideoUUID, [])
149 it('Should correctly terminate the stream on blacklist and delete the live', async function () {
152 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: false })
154 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
156 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
158 await waitJobs(servers)
159 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
162 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
163 testFfmpegStreamError(ffmpegCommand, true)
166 await waitJobs(servers)
168 await checkVideosExist(liveVideoUUID, false)
170 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
171 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
174 await waitJobs(servers)
175 await checkLiveCleanup(servers[0], liveVideoUUID, [])
178 it('Should correctly terminate the stream on delete and delete the video', async function () {
181 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: false })
183 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
185 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
187 await waitJobs(servers)
188 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
191 testFfmpegStreamError(ffmpegCommand, true),
192 servers[0].videos.remove({ id: liveVideoUUID })
196 await waitJobs(servers)
198 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
199 await checkLiveCleanup(servers[0], liveVideoUUID, [])
203 describe('With save replay enabled on non permanent live', function () {
205 it('Should correctly create and federate the "waiting for stream" live', async function () {
208 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true })
210 await waitJobs(servers)
212 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
213 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
216 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
219 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
220 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
222 await waitJobs(servers)
224 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
225 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
228 it('Should correctly have saved the live and federated it after the streaming', async function () {
231 await stopFfmpeg(ffmpegCommand)
233 await waitUntilLiveReplacedByReplayOnAllServers(servers, liveVideoUUID)
234 await waitJobs(servers)
236 // Live has been transcoded
237 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
238 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
241 it('Should update the saved live and correctly federate the updated attributes', async function () {
244 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated' } })
245 await waitJobs(servers)
247 for (const server of servers) {
248 const video = await server.videos.get({ id: liveVideoUUID })
249 expect(video.name).to.equal('video updated')
250 expect(video.isLive).to.be.false
254 it('Should have cleaned up the live files', async function () {
255 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
258 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
261 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true })
263 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
264 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
266 await waitJobs(servers)
267 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
270 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
271 testFfmpegStreamError(ffmpegCommand, true)
274 await waitJobs(servers)
276 await checkVideosExist(liveVideoUUID, false)
278 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
279 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
282 await waitJobs(servers)
283 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
286 it('Should correctly terminate the stream on delete and delete the video', async function () {
289 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true })
291 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
292 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
294 await waitJobs(servers)
295 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
298 servers[0].videos.remove({ id: liveVideoUUID }),
299 testFfmpegStreamError(ffmpegCommand, true)
303 await waitJobs(servers)
305 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
306 await checkLiveCleanup(servers[0], liveVideoUUID, [])
310 describe('With save replay enabled on permanent live', function () {
311 let lastReplayUUID: string
313 it('Should correctly create and federate the "waiting for stream" live', async function () {
316 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true })
318 await waitJobs(servers)
320 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
321 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
324 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
327 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
328 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
330 await waitJobs(servers)
332 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
333 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
336 it('Should correctly have saved the live and federated it after the streaming', async function () {
339 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
341 await stopFfmpeg(ffmpegCommand)
343 await waitUntilLiveWaitingOnAllServers(servers, liveVideoUUID)
344 await waitJobs(servers)
346 const video = await findExternalSavedVideo(servers[0], liveDetails)
347 expect(video).to.exist
349 for (const server of servers) {
350 await server.videos.get({ id: video.uuid })
353 lastReplayUUID = video.uuid
356 it('Should have cleaned up the live files', async function () {
357 await checkLiveCleanup(servers[0], liveVideoUUID, [])
360 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
363 await servers[0].videos.remove({ id: lastReplayUUID })
365 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true })
367 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
368 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
370 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
372 await waitJobs(servers)
373 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
376 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
377 testFfmpegStreamError(ffmpegCommand, true)
380 await waitJobs(servers)
382 await waitJobs(servers)
384 const replay = await findExternalSavedVideo(servers[0], liveDetails)
385 expect(replay).to.exist
387 for (const videoId of [ liveVideoUUID, replay.uuid ]) {
388 await checkVideosExist(videoId, false)
390 await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
391 await servers[1].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
394 await checkLiveCleanup(servers[0], liveVideoUUID, [])
397 it('Should correctly terminate the stream on delete and not save the video', async function () {
400 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true })
402 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
403 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
405 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
407 await waitJobs(servers)
408 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
411 servers[0].videos.remove({ id: liveVideoUUID }),
412 testFfmpegStreamError(ffmpegCommand, true)
416 await waitJobs(servers)
418 const replay = await findExternalSavedVideo(servers[0], liveDetails)
419 expect(replay).to.not.exist
421 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
422 await checkLiveCleanup(servers[0], liveVideoUUID, [])
426 after(async function () {
427 await cleanupTests(servers)