1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { FfmpegCommand } from 'fluent-ffmpeg'
5 import { checkLiveCleanup } from '@server/tests/shared'
6 import { wait } from '@shared/core-utils'
7 import { HttpStatusCode, LiveVideoCreate, LiveVideoError, VideoPrivacy, VideoState } from '@shared/models'
11 createMultipleServers,
13 findExternalSavedVideo,
15 setAccessTokensToServers,
16 setDefaultVideoChannel,
18 testFfmpegStreamError,
20 waitUntilLivePublishedOnAllServers,
21 waitUntilLiveReplacedByReplayOnAllServers,
22 waitUntilLiveWaitingOnAllServers
23 } from '@shared/server-commands'
25 describe('Save replay setting', function () {
26 let servers: PeerTubeServer[] = []
27 let liveVideoUUID: string
28 let ffmpegCommand: FfmpegCommand
30 async function createLiveWrapper (options: { permanent: boolean, replay: boolean }) {
33 await servers[0].videos.remove({ id: liveVideoUUID })
34 await waitJobs(servers)
38 const attributes: LiveVideoCreate = {
39 channelId: servers[0].store.channel.id,
40 privacy: VideoPrivacy.PUBLIC,
41 name: 'my super live',
42 saveReplay: options.replay,
43 permanentLive: options.permanent
46 const { uuid } = await servers[0].live.create({ fields: attributes })
50 async function publishLive (options: { permanent: boolean, replay: boolean }) {
51 liveVideoUUID = await createLiveWrapper(options)
53 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
54 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
56 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
58 await waitJobs(servers)
59 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
61 return { ffmpegCommand, liveDetails }
64 async function publishLiveAndDelete (options: { permanent: boolean, replay: boolean }) {
65 const { ffmpegCommand, liveDetails } = await publishLive(options)
68 servers[0].videos.remove({ id: liveVideoUUID }),
69 testFfmpegStreamError(ffmpegCommand, true)
72 await waitJobs(servers)
74 await waitJobs(servers)
76 return { liveDetails }
79 async function publishLiveAndBlacklist (options: { permanent: boolean, replay: boolean }) {
80 const { ffmpegCommand, liveDetails } = await publishLive(options)
83 servers[0].blacklist.add({ videoId: liveVideoUUID, reason: 'bad live', unfederate: true }),
84 testFfmpegStreamError(ffmpegCommand, true)
87 await waitJobs(servers)
89 await waitJobs(servers)
91 return { liveDetails }
94 async function checkVideosExist (videoId: string, existsInList: boolean, expectedStatus?: number) {
95 for (const server of servers) {
96 const length = existsInList ? 1 : 0
98 const { data, total } = await server.videos.list()
99 expect(data).to.have.lengthOf(length)
100 expect(total).to.equal(length)
102 if (expectedStatus) {
103 await server.videos.get({ id: videoId, expectedStatus })
108 async function checkVideoState (videoId: string, state: VideoState) {
109 for (const server of servers) {
110 const video = await server.videos.get({ id: videoId })
111 expect(video.state.id).to.equal(state)
115 before(async function () {
118 servers = await createMultipleServers(2)
120 // Get the access tokens
121 await setAccessTokensToServers(servers)
122 await setDefaultVideoChannel(servers)
124 // Server 1 and server 2 follow each other
125 await doubleFollow(servers[0], servers[1])
127 await servers[0].config.updateCustomSubConfig({
135 resolutions: ConfigCommand.getCustomConfigResolutions(true)
142 describe('With save replay disabled', function () {
143 let sessionStartDateMin: Date
144 let sessionStartDateMax: Date
145 let sessionEndDateMin: Date
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 sessionStartDateMin = new Date()
164 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
165 sessionStartDateMax = new Date()
167 await waitJobs(servers)
169 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
170 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
173 it('Should correctly delete the video files after the stream ended', async function () {
176 sessionEndDateMin = new Date()
177 await stopFfmpeg(ffmpegCommand)
179 for (const server of servers) {
180 await server.live.waitUntilEnded({ videoId: liveVideoUUID })
182 await waitJobs(servers)
184 // Live still exist, but cannot be played anymore
185 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
186 await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
188 // No resolutions saved since we did not save replay
189 await checkLiveCleanup(servers[0], liveVideoUUID, [])
192 it('Should have appropriate ended session', async function () {
193 const { data, total } = await servers[0].live.listSessions({ videoId: liveVideoUUID })
194 expect(total).to.equal(1)
195 expect(data).to.have.lengthOf(1)
197 const session = data[0]
199 const startDate = new Date(session.startDate)
200 expect(startDate).to.be.above(sessionStartDateMin)
201 expect(startDate).to.be.below(sessionStartDateMax)
203 expect(session.endDate).to.exist
204 expect(new Date(session.endDate)).to.be.above(sessionEndDateMin)
206 expect(session.saveReplay).to.be.false
207 expect(session.error).to.not.exist
208 expect(session.replayVideo).to.not.exist
211 it('Should correctly terminate the stream on blacklist and delete the live', async function () {
214 await publishLiveAndBlacklist({ permanent: false, replay: false })
216 await checkVideosExist(liveVideoUUID, false)
218 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
219 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
222 await waitJobs(servers)
223 await checkLiveCleanup(servers[0], liveVideoUUID, [])
226 it('Should have blacklisted session error', async function () {
227 const session = await servers[0].live.findLatestSession({ videoId: liveVideoUUID })
228 expect(session.startDate).to.exist
229 expect(session.endDate).to.exist
231 expect(session.error).to.equal(LiveVideoError.BLACKLISTED)
232 expect(session.replayVideo).to.not.exist
235 it('Should correctly terminate the stream on delete and delete the video', async function () {
238 await publishLiveAndDelete({ permanent: false, replay: false })
240 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
241 await checkLiveCleanup(servers[0], liveVideoUUID, [])
245 describe('With save replay enabled on non permanent live', function () {
247 it('Should correctly create and federate the "waiting for stream" live', async function () {
250 liveVideoUUID = await createLiveWrapper({ permanent: false, replay: true })
252 await waitJobs(servers)
254 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
255 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
258 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
261 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
262 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
264 await waitJobs(servers)
266 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
267 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
270 it('Should correctly have saved the live and federated it after the streaming', async function () {
273 const session = await servers[0].live.findLatestSession({ videoId: liveVideoUUID })
274 expect(session.endDate).to.not.exist
275 expect(session.endingProcessed).to.be.false
276 expect(session.saveReplay).to.be.true
278 await stopFfmpeg(ffmpegCommand)
280 await waitUntilLiveReplacedByReplayOnAllServers(servers, liveVideoUUID)
281 await waitJobs(servers)
283 // Live has been transcoded
284 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
285 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
288 it('Should find the replay live session', async function () {
289 const session = await servers[0].live.getReplaySession({ videoId: liveVideoUUID })
291 expect(session).to.exist
293 expect(session.startDate).to.exist
294 expect(session.endDate).to.exist
296 expect(session.error).to.not.exist
297 expect(session.saveReplay).to.be.true
298 expect(session.endingProcessed).to.be.true
300 expect(session.replayVideo).to.exist
301 expect(session.replayVideo.id).to.exist
302 expect(session.replayVideo.shortUUID).to.exist
303 expect(session.replayVideo.uuid).to.equal(liveVideoUUID)
306 it('Should update the saved live and correctly federate the updated attributes', async function () {
309 await servers[0].videos.update({ id: liveVideoUUID, attributes: { name: 'video updated' } })
310 await waitJobs(servers)
312 for (const server of servers) {
313 const video = await server.videos.get({ id: liveVideoUUID })
314 expect(video.name).to.equal('video updated')
315 expect(video.isLive).to.be.false
319 it('Should have cleaned up the live files', async function () {
320 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
323 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
326 await publishLiveAndBlacklist({ permanent: false, replay: true })
328 await checkVideosExist(liveVideoUUID, false)
330 await servers[0].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
331 await servers[1].videos.get({ id: liveVideoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
334 await waitJobs(servers)
335 await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
338 it('Should correctly terminate the stream on delete and delete the video', async function () {
341 await publishLiveAndDelete({ permanent: false, replay: true })
343 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
344 await checkLiveCleanup(servers[0], liveVideoUUID, [])
348 describe('With save replay enabled on permanent live', function () {
349 let lastReplayUUID: string
351 it('Should correctly create and federate the "waiting for stream" live', async function () {
354 liveVideoUUID = await createLiveWrapper({ permanent: true, replay: true })
356 await waitJobs(servers)
358 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.OK_200)
359 await checkVideoState(liveVideoUUID, VideoState.WAITING_FOR_LIVE)
362 it('Should correctly have updated the live and federated it when streaming in the live', async function () {
365 ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
366 await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
368 await waitJobs(servers)
370 await checkVideosExist(liveVideoUUID, true, HttpStatusCode.OK_200)
371 await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
374 it('Should correctly have saved the live and federated it after the streaming', async function () {
377 const liveDetails = await servers[0].videos.get({ id: liveVideoUUID })
379 await stopFfmpeg(ffmpegCommand)
381 await waitUntilLiveWaitingOnAllServers(servers, liveVideoUUID)
382 await waitJobs(servers)
384 const video = await findExternalSavedVideo(servers[0], liveDetails)
385 expect(video).to.exist
387 for (const server of servers) {
388 await server.videos.get({ id: video.uuid })
391 lastReplayUUID = video.uuid
394 it('Should have appropriate ended session and replay live session', async function () {
395 const { data, total } = await servers[0].live.listSessions({ videoId: liveVideoUUID })
396 expect(total).to.equal(1)
397 expect(data).to.have.lengthOf(1)
399 const sessionFromLive = data[0]
400 const sessionFromReplay = await servers[0].live.getReplaySession({ videoId: lastReplayUUID })
402 for (const session of [ sessionFromLive, sessionFromReplay ]) {
403 expect(session.startDate).to.exist
404 expect(session.endDate).to.exist
406 expect(session.error).to.not.exist
408 expect(session.replayVideo).to.exist
409 expect(session.replayVideo.id).to.exist
410 expect(session.replayVideo.shortUUID).to.exist
411 expect(session.replayVideo.uuid).to.equal(lastReplayUUID)
415 it('Should have cleaned up the live files', async function () {
416 await checkLiveCleanup(servers[0], liveVideoUUID, [])
419 it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
422 await servers[0].videos.remove({ id: lastReplayUUID })
423 const { liveDetails } = await publishLiveAndBlacklist({ permanent: true, replay: true })
425 const replay = await findExternalSavedVideo(servers[0], liveDetails)
426 expect(replay).to.exist
428 for (const videoId of [ liveVideoUUID, replay.uuid ]) {
429 await checkVideosExist(videoId, false)
431 await servers[0].videos.get({ id: videoId, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
432 await servers[1].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
435 await checkLiveCleanup(servers[0], liveVideoUUID, [])
438 it('Should correctly terminate the stream on delete and not save the video', async function () {
441 const { liveDetails } = await publishLiveAndDelete({ permanent: true, replay: true })
443 const replay = await findExternalSavedVideo(servers[0], liveDetails)
444 expect(replay).to.not.exist
446 await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
447 await checkLiveCleanup(servers[0], liveVideoUUID, [])
451 after(async function () {
452 await cleanupTests(servers)