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'
10 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 (servers: PeerTubeServer[], videoUUID: string, numberOfFiles: number) {
40 for (const server of servers) {
41 const video = await server.videos.get({ id: videoUUID })
43 expect(video.files).to.have.lengthOf(0)
44 expect(video.streamingPlaylists).to.have.lengthOf(1)
46 const files = video.streamingPlaylists[0].files
47 expect(files).to.have.lengthOf(numberOfFiles)
49 for (const file of files) {
50 expectStartWith(file.fileUrl, ObjectStorageCommand.getMockPlaylistBaseUrl())
52 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
57 async function checkFilesCleanup (server: PeerTubeServer, videoUUID: string, resolutions: number[]) {
58 const resolutionFiles = resolutions.map((_value, i) => `${i}.m3u8`)
60 for (const playlistName of [ 'master.m3u8' ].concat(resolutionFiles)) {
61 await server.live.getPlaylistFile({
64 expectedStatus: HttpStatusCode.NOT_FOUND_404,
69 await server.live.getSegmentFile({
74 expectedStatus: HttpStatusCode.NOT_FOUND_404
78 describe('Object storage for lives', function () {
79 if (areMockObjectStorageTestsDisabled()) return
81 let servers: PeerTubeServer[]
82 let sqlCommandServer1: SQLCommand
84 before(async function () {
87 await ObjectStorageCommand.prepareDefaultMockBuckets()
89 servers = await createMultipleServers(2, ObjectStorageCommand.getDefaultMockConfig())
91 await setAccessTokensToServers(servers)
92 await setDefaultVideoChannel(servers)
93 await doubleFollow(servers[0], servers[1])
95 await servers[0].config.enableTranscoding()
97 sqlCommandServer1 = new SQLCommand(servers[0])
100 describe('Without live transcoding', function () {
101 let videoUUID: string
103 before(async function () {
104 await servers[0].config.enableLive({ transcoding: false })
106 videoUUID = await createLive(servers[0], false)
109 it('Should create a live and publish it on object storage', async function () {
112 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUID })
113 await waitUntilLivePublishedOnAllServers(servers, videoUUID)
115 await testLiveVideoResolutions({
116 originServer: servers[0],
117 sqlCommand: sqlCommandServer1,
119 liveVideoId: videoUUID,
120 resolutions: [ 720 ],
125 await stopFfmpeg(ffmpegCommand)
128 it('Should have saved the replay on object storage', async function () {
131 await waitUntilLiveReplacedByReplayOnAllServers(servers, videoUUID)
132 await waitJobs(servers)
134 await checkFilesExist(servers, videoUUID, 1)
137 it('Should have cleaned up live files from object storage', async function () {
138 await checkFilesCleanup(servers[0], videoUUID, [ 720 ])
142 describe('With live transcoding', function () {
143 const resolutions = [ 720, 480, 360, 240, 144 ]
145 before(async function () {
146 await servers[0].config.enableLive({ transcoding: true })
149 describe('Normal replay', function () {
150 let videoUUIDNonPermanent: string
152 before(async function () {
153 videoUUIDNonPermanent = await createLive(servers[0], false)
156 it('Should create a live and publish it on object storage', async function () {
159 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUIDNonPermanent })
160 await waitUntilLivePublishedOnAllServers(servers, videoUUIDNonPermanent)
162 await testLiveVideoResolutions({
163 originServer: servers[0],
164 sqlCommand: sqlCommandServer1,
166 liveVideoId: videoUUIDNonPermanent,
172 await stopFfmpeg(ffmpegCommand)
175 it('Should have saved the replay on object storage', async function () {
178 await waitUntilLiveReplacedByReplayOnAllServers(servers, videoUUIDNonPermanent)
179 await waitJobs(servers)
181 await checkFilesExist(servers, videoUUIDNonPermanent, 5)
184 it('Should have cleaned up live files from object storage', async function () {
185 await checkFilesCleanup(servers[0], videoUUIDNonPermanent, resolutions)
189 describe('Permanent replay', function () {
190 let videoUUIDPermanent: string
192 before(async function () {
193 videoUUIDPermanent = await createLive(servers[0], true)
196 it('Should create a live and publish it on object storage', async function () {
199 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUIDPermanent })
200 await waitUntilLivePublishedOnAllServers(servers, videoUUIDPermanent)
202 await testLiveVideoResolutions({
203 originServer: servers[0],
204 sqlCommand: sqlCommandServer1,
206 liveVideoId: videoUUIDPermanent,
212 await stopFfmpeg(ffmpegCommand)
215 it('Should have saved the replay on object storage', async function () {
218 await waitUntilLiveWaitingOnAllServers(servers, videoUUIDPermanent)
219 await waitJobs(servers)
221 const videoLiveDetails = await servers[0].videos.get({ id: videoUUIDPermanent })
222 const replay = await findExternalSavedVideo(servers[0], videoLiveDetails)
224 await checkFilesExist(servers, replay.uuid, 5)
227 it('Should have cleaned up live files from object storage', async function () {
228 await checkFilesCleanup(servers[0], videoUUIDPermanent, resolutions)
233 describe('With object storage base url', function () {
234 const mockObjectStorageProxy = new MockObjectStorageProxy()
235 let baseMockUrl: string
237 before(async function () {
240 const port = await mockObjectStorageProxy.initialize()
241 baseMockUrl = `http://127.0.0.1:${port}/streaming-playlists`
243 await ObjectStorageCommand.createMockBucket('streaming-playlists')
248 endpoint: 'http://' + ObjectStorageCommand.getMockEndpointHost(),
249 region: ObjectStorageCommand.getMockRegion(),
251 credentials: ObjectStorageCommand.getMockCredentialsConfig(),
253 streaming_playlists: {
254 bucket_name: 'streaming-playlists',
256 base_url: baseMockUrl
261 await servers[0].kill()
262 await servers[0].run(config)
264 await servers[0].config.enableLive({ transcoding: true, resolutions: 'min' })
267 it('Should publish a live and replace the base url', async function () {
270 const videoUUIDPermanent = await createLive(servers[0], true)
272 const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: videoUUIDPermanent })
273 await waitUntilLivePublishedOnAllServers(servers, videoUUIDPermanent)
275 await testLiveVideoResolutions({
276 originServer: servers[0],
277 sqlCommand: sqlCommandServer1,
279 liveVideoId: videoUUIDPermanent,
280 resolutions: [ 720 ],
283 objectStorageBaseUrl: baseMockUrl
286 await stopFfmpeg(ffmpegCommand)
290 after(async function () {
291 await sqlCommandServer1.cleanup()
293 await killallServers(servers)