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 publishLive (options: { permanent: boolean, replay: boolean }) {
54 liveVideoUUID = await createLiveWrapper(options)
56 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
57 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
59 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
61 await waitJobs(servers)
62 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
64 return { ffmpegCommand, liveDetails }
67 async function publishLiveAndDelete (options: { permanent: boolean, replay: boolean }) {
68 const { ffmpegCommand, liveDetails } = await publishLive(options)
71 servers[0].videos.remove({ id: liveVideoUUID }),
72 testFfmpegStreamError(ffmpegCommand, true)
75 await waitJobs(servers)
77 await waitJobs(servers)
79 return { liveDetails }
82 async function publishLiveAndBlacklist (options: { permanent: boolean, replay: boolean }) {
83 const { ffmpegCommand, liveDetails } = await publishLive(options)
86 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
87 testFfmpegStreamError(ffmpegCommand, true)
90 await waitJobs(servers)
92 await waitJobs(servers)
94 return { liveDetails }
97 async function checkVideosExist (videoId: string, existsInList: boolean, expectedStatus?: number) {
98 for (const server of servers) {
99 const length = existsInList ? 1 : 0
101 const { data, total } = await server.videos.list()
102 expect(data).to.have.lengthOf(length)
103 expect(total).to.equal(length)
105 if (expectedStatus) {
106 await server.videos.get({ id: videoId, expectedStatus })
111 async function checkVideoState (videoId: string, state: VideoState) {
112 for (const server of servers) {
113 const video = await server.videos.get({ id: videoId })
114 expect(video.state.id).to.equal(state)
118 before(async function () {
121 servers = await createMultipleServers(2)
123 // Get the access tokens
124 await setAccessTokensToServers(servers)
125 await setDefaultVideoChannel(servers)
127 // Server 1 and server 2 follow each other
128 await doubleFollow(servers[0], servers[1])
130 await servers[0].config.updateCustomSubConfig({
138 resolutions: ConfigCommand.getCustomConfigResolutions(true)
145 describe('With save replay disabled', function () {
147 it('Should correctly create and federate the "waiting for stream" live', async function () {
150 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: false })
152 await waitJobs(servers)
154 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
155 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
158 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
161 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
163 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
165 await waitJobs(servers)
167 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
168 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
171 it('Should correctly delete the video files after the stream ended', async function () {
174 await stopFfmpeg(ffmpegCommand)
176 for (const server of servers) {
177 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
179 await waitJobs(servers)
181 // Live still exist, but cannot be played anymore
182 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
183 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
185 // No resolutions saved since we did not save replay
186 await checkLiveCleanup(servers[0], liveVideoUUID, [])
189 it('Should correctly terminate the stream on blacklist and delete the live', async function () {
192 await publishLiveAndBlacklist({ permanent: false, replay: false })
194 await checkVideosExist(liveVideoUUID, false)
196 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
197 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
200 await waitJobs(servers)
201 await checkLiveCleanup(servers[0], liveVideoUUID, [])
204 it('Should correctly terminate the stream on delete and delete the video', async function () {
207 await publishLiveAndDelete({ permanent: false, replay: false })
209 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
210 await checkLiveCleanup(servers[0], liveVideoUUID, [])
214 describe('With save replay enabled on non permanent live', function () {
216 it('Should correctly create and federate the "waiting for stream" live', async function () {
219 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true })
221 await waitJobs(servers)
223 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
224 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
227 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
230 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
231 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
233 await waitJobs(servers)
235 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
236 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
239 it('Should correctly have saved the live and federated it after the streaming', async function () {
242 await stopFfmpeg(ffmpegCommand)
244 await waitUntilLiveReplacedByReplayOnAllServers(servers, liveVideoUUID)
245 await waitJobs(servers)
247 // Live has been transcoded
248 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
249 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
252 it('Should update the saved live and correctly federate the updated attributes', async function () {
255 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated' } })
256 await waitJobs(servers)
258 for (const server of servers) {
259 const video = await server.videos.get({ id: liveVideoUUID })
260 expect(video.name).to.equal('video updated')
261 expect(video.isLive).to.be.false
265 it('Should have cleaned up the live files', async function () {
266 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
269 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
272 await publishLiveAndBlacklist({ permanent: false, replay: true })
274 await checkVideosExist(liveVideoUUID, false)
276 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
277 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
280 await waitJobs(servers)
281 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
284 it('Should correctly terminate the stream on delete and delete the video', async function () {
287 await publishLiveAndDelete({ permanent: false, replay: true })
289 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
290 await checkLiveCleanup(servers[0], liveVideoUUID, [])
294 describe('With save replay enabled on permanent live', function () {
295 let lastReplayUUID: string
297 it('Should correctly create and federate the "waiting for stream" live', async function () {
300 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true })
302 await waitJobs(servers)
304 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
305 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
308 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
311 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
312 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
314 await waitJobs(servers)
316 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
317 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
320 it('Should correctly have saved the live and federated it after the streaming', async function () {
323 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
325 await stopFfmpeg(ffmpegCommand)
327 await waitUntilLiveWaitingOnAllServers(servers, liveVideoUUID)
328 await waitJobs(servers)
330 const video = await findExternalSavedVideo(servers[0], liveDetails)
331 expect(video).to.exist
333 for (const server of servers) {
334 await server.videos.get({ id: video.uuid })
337 lastReplayUUID = video.uuid
340 it('Should have cleaned up the live files', async function () {
341 await checkLiveCleanup(servers[0], liveVideoUUID, [])
344 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
347 await servers[0].videos.remove({ id: lastReplayUUID })
348 const { liveDetails } = await publishLiveAndBlacklist({ permanent: true, replay: true })
350 const replay = await findExternalSavedVideo(servers[0], liveDetails)
351 expect(replay).to.exist
353 for (const videoId of [ liveVideoUUID, replay.uuid ]) {
354 await checkVideosExist(videoId, false)
356 await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
357 await servers[1].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
360 await checkLiveCleanup(servers[0], liveVideoUUID, [])
363 it('Should correctly terminate the stream on delete and not save the video', async function () {
366 const { liveDetails } = await publishLiveAndDelete({ permanent: true, replay: true })
368 const replay = await findExternalSavedVideo(servers[0], liveDetails)
369 expect(replay).to.not.exist
371 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
372 await checkLiveCleanup(servers[0], liveVideoUUID, [])
376 after(async function () {
377 await cleanupTests(servers)