1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { expectStartWith, MockObjectStorageProxy, SQLCommand, testLiveVideoResolutions } from '@server/tests/shared'
5 import { areMockObjectStorageTestsDisabled } from '@shared/core-utils'
6 import { HttpStatusCode, LiveVideoCreate, VideoPrivacy } from '@shared/models'
11 findExternalSavedVideo,
15 setAccessTokensToServers,
16 setDefaultVideoChannel,
19 waitUntilLivePublishedOnAllServers,
20 waitUntilLiveReplacedByReplayOnAllServers,
21 waitUntilLiveWaitingOnAllServers
22 } from '@shared/server-commands'
24 async function createLive (server: PeerTubeServer, permanent: boolean) {
25 const attributes: LiveVideoCreate = {
26 channelId: server.store.channel.id,
27 privacy: VideoPrivacy.PUBLIC,
28 name: 'my super live',
30 replaySettings: { privacy: VideoPrivacy.PUBLIC },
31 permanentLive: permanent
34 const { uuid } = await server.live.create({ fields: attributes })
39 async function checkFilesExist (options: {
40 servers: PeerTubeServer[]
43 objectStorage: ObjectStorageCommand
45 const { servers, videoUUID, numberOfFiles, objectStorage } = options
47 for (const server of servers) {
48 const video = await server.videos.get({ id: videoUUID })
50 expect(video.files).to.have.lengthOf(0)
51 expect(video.streamingPlaylists).to.have.lengthOf(1)
53 const files = video.streamingPlaylists[0].files
54 expect(files).to.have.lengthOf(numberOfFiles)
56 for (const file of files) {
57 expectStartWith(file.fileUrl, objectStorage.getMockPlaylistBaseUrl())
59 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
64 async function checkFilesCleanup (options: {
65 server: PeerTubeServer
68 objectStorage: ObjectStorageCommand
70 const { server, videoUUID, resolutions, objectStorage } = options
72 const resolutionFiles = resolutions.map((_value, i) => `${i}.m3u8`)
74 for (const playlistName of [ 'master.m3u8' ].concat(resolutionFiles)) {
75 await server.live.getPlaylistFile({
78 expectedStatus: HttpStatusCode.NOT_FOUND_404,
83 await server.live.getSegmentFile({
88 expectedStatus: HttpStatusCode.NOT_FOUND_404
92 describe('Object storage for lives', function () {
93 if (areMockObjectStorageTestsDisabled()) return
95 let servers: PeerTubeServer[]
96 let sqlCommandServer1: SQLCommand
97 const objectStorage = new ObjectStorageCommand()
99 before(async function () {
102 await objectStorage.prepareDefaultMockBuckets()
103 servers = await createMultipleServers(2, objectStorage.getDefaultMockConfig())
105 await setAccessTokensToServers(servers)
106 await setDefaultVideoChannel(servers)
107 await doubleFollow(servers[0], servers[1])
109 await servers[0].config.enableTranscoding()
111 sqlCommandServer1 = new SQLCommand(servers[0])
114 describe('Without live transcoding', function () {
115 let videoUUID: string
117 before(async function () {
118 await servers[0].config.enableLive({ transcoding: false })
120 videoUUID = await createLive(servers[0], false)
123 it('Should create a live and publish it on object storage', async function () {
126 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID })
127 await waitUntilLivePublishedOnAllServers(servers, videoUUID)
129 await testLiveVideoResolutions({
130 originServer: servers[0],
131 sqlCommand: sqlCommandServer1,
133 liveVideoId: videoUUID,
134 resolutions: [ 720 ],
139 await stopFfmpeg(ffmpegCommand)
142 it('Should have saved the replay on object storage', async function () {
145 await waitUntilLiveReplacedByReplayOnAllServers(servers, videoUUID)
146 await waitJobs(servers)
148 await checkFilesExist({ servers, videoUUID, numberOfFiles: 1, objectStorage })
151 it('Should have cleaned up live files from object storage', async function () {
152 await checkFilesCleanup({ server: servers[0], videoUUID, resolutions: [ 720 ], objectStorage })
156 describe('With live transcoding', function () {
157 const resolutions = [ 720, 480, 360, 240, 144 ]
159 before(async function () {
160 await servers[0].config.enableLive({ transcoding: true })
163 describe('Normal replay', function () {
164 let videoUUIDNonPermanent: string
166 before(async function () {
167 videoUUIDNonPermanent = await createLive(servers[0], false)
170 it('Should create a live and publish it on object storage', async function () {
173 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUIDNonPermanent })
174 await waitUntilLivePublishedOnAllServers(servers, videoUUIDNonPermanent)
176 await testLiveVideoResolutions({
177 originServer: servers[0],
178 sqlCommand: sqlCommandServer1,
180 liveVideoId: videoUUIDNonPermanent,
186 await stopFfmpeg(ffmpegCommand)
189 it('Should have saved the replay on object storage', async function () {
192 await waitUntilLiveReplacedByReplayOnAllServers(servers, videoUUIDNonPermanent)
193 await waitJobs(servers)
195 await checkFilesExist({ servers, videoUUID: videoUUIDNonPermanent, numberOfFiles: 5, objectStorage })
198 it('Should have cleaned up live files from object storage', async function () {
199 await checkFilesCleanup({ server: servers[0], videoUUID: videoUUIDNonPermanent, resolutions, objectStorage })
203 describe('Permanent replay', function () {
204 let videoUUIDPermanent: string
206 before(async function () {
207 videoUUIDPermanent = await createLive(servers[0], true)
210 it('Should create a live and publish it on object storage', async function () {
213 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUIDPermanent })
214 await waitUntilLivePublishedOnAllServers(servers, videoUUIDPermanent)
216 await testLiveVideoResolutions({
217 originServer: servers[0],
218 sqlCommand: sqlCommandServer1,
220 liveVideoId: videoUUIDPermanent,
226 await stopFfmpeg(ffmpegCommand)
229 it('Should have saved the replay on object storage', async function () {
232 await waitUntilLiveWaitingOnAllServers(servers, videoUUIDPermanent)
233 await waitJobs(servers)
235 const videoLiveDetails = await servers[0].videos.get({ id: videoUUIDPermanent })
236 const replay = await findExternalSavedVideo(servers[0], videoLiveDetails)
238 await checkFilesExist({ servers, videoUUID: replay.uuid, numberOfFiles: 5, objectStorage })
241 it('Should have cleaned up live files from object storage', async function () {
242 await checkFilesCleanup({ server: servers[0], videoUUID: videoUUIDPermanent, resolutions, objectStorage })
247 describe('With object storage base url', function () {
248 const mockObjectStorageProxy = new MockObjectStorageProxy()
249 let baseMockUrl: string
251 before(async function () {
254 const port = await mockObjectStorageProxy.initialize()
255 const bucketName = objectStorage.getMockStreamingPlaylistsBucketName()
256 baseMockUrl = `http://127.0.0.1:${port}/${bucketName}`
258 await objectStorage.prepareDefaultMockBuckets()
263 endpoint: 'http://' + ObjectStorageCommand.getMockEndpointHost(),
264 region: ObjectStorageCommand.getMockRegion(),
266 credentials: ObjectStorageCommand.getMockCredentialsConfig(),
268 streaming_playlists: {
269 bucket_name: bucketName,
271 base_url: baseMockUrl
276 await servers[0].kill()
277 await servers[0].run(config)
279 await servers[0].config.enableLive({ transcoding: true, resolutions: 'min' })
282 it('Should publish a live and replace the base url', async function () {
285 const videoUUIDPermanent = await createLive(servers[0], true)
287 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUIDPermanent })
288 await waitUntilLivePublishedOnAllServers(servers, videoUUIDPermanent)
290 await testLiveVideoResolutions({
291 originServer: servers[0],
292 sqlCommand: sqlCommandServer1,
294 liveVideoId: videoUUIDPermanent,
295 resolutions: [ 720 ],
298 objectStorageBaseUrl: baseMockUrl
301 await stopFfmpeg(ffmpegCommand)
305 after(async function () {
306 await sqlCommandServer1.cleanup()
307 await objectStorage.cleanupMock()
309 await cleanupTests(servers)