Support live session in server

This commit is contained in:
Chocobozzz
2022-05-03 11:38:07 +02:00
parent 86c5229b4d
commit 26e3e98ff0
29 changed files with 814 additions and 66 deletions

View File

@@ -388,6 +388,52 @@ describe('Test video lives API validator', function () {
})
})
describe('When getting live sessions', function () {
it('Should fail with a bad access token', async function () {
await command.listSessions({ token: 'toto', videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
})
it('Should fail without token', async function () {
await command.listSessions({ token: null, videoId: video.id, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
})
it('Should fail with the token of another user', async function () {
await command.listSessions({ token: userAccessToken, videoId: video.id, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
})
it('Should fail with a bad video id', async function () {
await command.listSessions({ videoId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
})
it('Should fail with an unknown video id', async function () {
await command.listSessions({ videoId: 454555, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
})
it('Should fail with a non live video', async function () {
await command.listSessions({ videoId: videoIdNotLive, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
})
it('Should succeed with the correct params', async function () {
await command.listSessions({ videoId: video.id })
})
})
describe('When getting live session of a replay', function () {
it('Should fail with a bad video id', async function () {
await command.getReplaySession({ videoId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
})
it('Should fail with an unknown video id', async function () {
await command.getReplaySession({ videoId: 454555, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
})
it('Should fail with a non replay video', async function () {
await command.getReplaySession({ videoId: videoIdNotLive, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
})
})
describe('When updating live information', async function () {
it('Should fail without access token', async function () {

View File

@@ -3,7 +3,7 @@
import 'mocha'
import * as chai from 'chai'
import { wait } from '@shared/core-utils'
import { VideoPrivacy } from '@shared/models'
import { LiveVideoError, VideoPrivacy } from '@shared/models'
import {
cleanupTests,
ConfigCommand,
@@ -12,7 +12,8 @@ import {
PeerTubeServer,
setAccessTokensToServers,
setDefaultVideoChannel,
waitJobs
waitJobs,
waitUntilLiveWaitingOnAllServers
} from '@shared/server-commands'
import { checkLiveCleanup } from '../../shared'
@@ -24,12 +25,18 @@ describe('Test live constraints', function () {
let userAccessToken: string
let userChannelId: number
async function createLiveWrapper (saveReplay: boolean) {
async function createLiveWrapper (options: {
replay: boolean
permanent: boolean
}) {
const { replay, permanent } = options
const liveAttributes = {
name: 'user live',
channelId: userChannelId,
privacy: VideoPrivacy.PUBLIC,
saveReplay
saveReplay: replay,
permanentLive: permanent
}
const { uuid } = await servers[0].live.create({ token: userAccessToken, fields: liveAttributes })
@@ -97,23 +104,42 @@ describe('Test live constraints', function () {
it('Should not have size limit if save replay is disabled', async function () {
this.timeout(60000)
const userVideoLiveoId = await createLiveWrapper(false)
const userVideoLiveoId = await createLiveWrapper({ replay: false, permanent: false })
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
})
it('Should have size limit depending on user global quota if save replay is enabled', async function () {
it('Should have size limit depending on user global quota if save replay is enabled on non permanent live', async function () {
this.timeout(60000)
// Wait for user quota memoize cache invalidation
await wait(5000)
const userVideoLiveoId = await createLiveWrapper(true)
const userVideoLiveoId = await createLiveWrapper({ replay: true, permanent: false })
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
await waitJobs(servers)
await checkSaveReplay(userVideoLiveoId)
const session = await servers[0].live.getReplaySession({ videoId: userVideoLiveoId })
expect(session.error).to.equal(LiveVideoError.QUOTA_EXCEEDED)
})
it('Should have size limit depending on user global quota if save replay is enabled on a permanent live', async function () {
this.timeout(60000)
// Wait for user quota memoize cache invalidation
await wait(5000)
const userVideoLiveoId = await createLiveWrapper({ replay: true, permanent: true })
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
await waitJobs(servers)
await waitUntilLiveWaitingOnAllServers(servers, userVideoLiveoId)
const session = await servers[0].live.findLatestSession({ videoId: userVideoLiveoId })
expect(session.error).to.equal(LiveVideoError.QUOTA_EXCEEDED)
})
it('Should have size limit depending on user daily quota if save replay is enabled', async function () {
@@ -124,13 +150,16 @@ describe('Test live constraints', function () {
await updateQuota({ total: -1, daily: 1 })
const userVideoLiveoId = await createLiveWrapper(true)
const userVideoLiveoId = await createLiveWrapper({ replay: true, permanent: false })
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
await waitJobs(servers)
await checkSaveReplay(userVideoLiveoId)
const session = await servers[0].live.getReplaySession({ videoId: userVideoLiveoId })
expect(session.error).to.equal(LiveVideoError.QUOTA_EXCEEDED)
})
it('Should succeed without quota limit', async function () {
@@ -141,7 +170,7 @@ describe('Test live constraints', function () {
await updateQuota({ total: 10 * 1000 * 1000, daily: -1 })
const userVideoLiveoId = await createLiveWrapper(true)
const userVideoLiveoId = await createLiveWrapper({ replay: true, permanent: false })
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
})
@@ -162,13 +191,16 @@ describe('Test live constraints', function () {
}
})
const userVideoLiveoId = await createLiveWrapper(true)
const userVideoLiveoId = await createLiveWrapper({ replay: true, permanent: false })
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: true })
await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
await waitJobs(servers)
await checkSaveReplay(userVideoLiveoId, [ 720, 480, 360, 240, 144 ])
const session = await servers[0].live.getReplaySession({ videoId: userVideoLiveoId })
expect(session.error).to.equal(LiveVideoError.DURATION_EXCEEDED)
})
after(async function () {

View File

@@ -172,6 +172,23 @@ describe('Permanent live', function () {
await stopFfmpeg(ffmpegCommand)
})
it('Should have appropriate sessions', async function () {
this.timeout(60000)
await servers[0].live.waitUntilWaiting({ videoId: videoUUID })
const { data, total } = await servers[0].live.listSessions({ videoId: videoUUID })
expect(total).to.equal(2)
expect(data).to.have.lengthOf(2)
for (const session of data) {
expect(session.startDate).to.exist
expect(session.endDate).to.exist
expect(session.error).to.not.exist
}
})
after(async function () {
await cleanupTests(servers)
})

View File

@@ -5,7 +5,7 @@ import * as chai from 'chai'
import { FfmpegCommand } from 'fluent-ffmpeg'
import { checkLiveCleanup } from '@server/tests/shared'
import { wait } from '@shared/core-utils'
import { HttpStatusCode, LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
import { HttpStatusCode, LiveVideoCreate, LiveVideoError, VideoPrivacy, VideoState } from '@shared/models'
import {
cleanupTests,
ConfigCommand,
@@ -143,6 +143,9 @@ describe('Save replay setting', function () {
})
describe('With save replay disabled', function () {
let sessionStartDateMin: Date
let sessionStartDateMax: Date
let sessionEndDateMin: Date
it('Should correctly create and federate the "waiting for stream" live', async function () {
this.timeout(20000)
@@ -160,7 +163,9 @@ describe('Save replay setting', function () {
ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveVideoUUID })
sessionStartDateMin = new Date()
await waitUntilLivePublishedOnAllServers(servers, liveVideoUUID)
sessionStartDateMax = new Date()
await waitJobs(servers)
@@ -171,6 +176,7 @@ describe('Save replay setting', function () {
it('Should correctly delete the video files after the stream ended', async function () {
this.timeout(40000)
sessionEndDateMin = new Date()
await stopFfmpeg(ffmpegCommand)
for (const server of servers) {
@@ -186,6 +192,24 @@ describe('Save replay setting', function () {
await checkLiveCleanup(servers[0], liveVideoUUID, [])
})
it('Should have appropriate ended session', async function () {
const { data, total } = await servers[0].live.listSessions({ videoId: liveVideoUUID })
expect(total).to.equal(1)
expect(data).to.have.lengthOf(1)
const session = data[0]
const startDate = new Date(session.startDate)
expect(startDate).to.be.above(sessionStartDateMin)
expect(startDate).to.be.below(sessionStartDateMax)
expect(session.endDate).to.exist
expect(new Date(session.endDate)).to.be.above(sessionEndDateMin)
expect(session.error).to.not.exist
expect(session.replayVideo).to.not.exist
})
it('Should correctly terminate the stream on blacklist and delete the live', async function () {
this.timeout(40000)
@@ -201,6 +225,15 @@ describe('Save replay setting', function () {
await checkLiveCleanup(servers[0], liveVideoUUID, [])
})
it('Should have blacklisted session error', async function () {
const session = await servers[0].live.findLatestSession({ videoId: liveVideoUUID })
expect(session.startDate).to.exist
expect(session.endDate).to.exist
expect(session.error).to.equal(LiveVideoError.BLACKLISTED)
expect(session.replayVideo).to.not.exist
})
it('Should correctly terminate the stream on delete and delete the video', async function () {
this.timeout(40000)
@@ -249,6 +282,22 @@ describe('Save replay setting', function () {
await checkVideoState(liveVideoUUID, VideoState.PUBLISHED)
})
it('Should find the replay live session', async function () {
const session = await servers[0].live.getReplaySession({ videoId: liveVideoUUID })
expect(session).to.exist
expect(session.startDate).to.exist
expect(session.endDate).to.exist
expect(session.error).to.not.exist
expect(session.replayVideo).to.exist
expect(session.replayVideo.id).to.exist
expect(session.replayVideo.shortUUID).to.exist
expect(session.replayVideo.uuid).to.equal(liveVideoUUID)
})
it('Should update the saved live and correctly federate the updated attributes', async function () {
this.timeout(30000)
@@ -337,6 +386,27 @@ describe('Save replay setting', function () {
lastReplayUUID = video.uuid
})
it('Should have appropriate ended session and replay live session', async function () {
const { data, total } = await servers[0].live.listSessions({ videoId: liveVideoUUID })
expect(total).to.equal(1)
expect(data).to.have.lengthOf(1)
const sessionFromLive = data[0]
const sessionFromReplay = await servers[0].live.getReplaySession({ videoId: lastReplayUUID })
for (const session of [ sessionFromLive, sessionFromReplay ]) {
expect(session.startDate).to.exist
expect(session.endDate).to.exist
expect(session.error).to.not.exist
expect(session.replayVideo).to.exist
expect(session.replayVideo.id).to.exist
expect(session.replayVideo.shortUUID).to.exist
expect(session.replayVideo.uuid).to.equal(lastReplayUUID)
}
})
it('Should have cleaned up the live files', async function () {
await checkLiveCleanup(servers[0], liveVideoUUID, [])
})

View File

@@ -594,6 +594,8 @@ describe('Test live', function () {
let permanentLiveReplayName: string
let beforeServerRestart: Date
async function createLiveWrapper (options: { saveReplay: boolean, permanent: boolean }) {
const liveAttributes: LiveVideoCreate = {
name: 'live video',
@@ -636,6 +638,8 @@ describe('Test live', function () {
}
await killallServers([ servers[0] ])
beforeServerRestart = new Date()
await servers[0].run()
await wait(5000)
@@ -653,6 +657,10 @@ describe('Test live', function () {
this.timeout(120000)
await commands[0].waitUntilPublished({ videoId: liveVideoReplayId })
const session = await commands[0].getReplaySession({ videoId: liveVideoReplayId })
expect(session.endDate).to.exist
expect(new Date(session.endDate)).to.be.above(beforeServerRestart)
})
it('Should have saved a permanent live replay', async function () {

View File

@@ -7,8 +7,8 @@ import {
checkMyVideoImportIsFinished,
checkNewActorFollow,
checkNewVideoFromSubscription,
checkVideoStudioEditionIsFinished,
checkVideoIsPublished,
checkVideoStudioEditionIsFinished,
FIXTURE_URLS,
MockSmtpServer,
prepareNotificationsTest,
@@ -16,8 +16,8 @@ import {
} from '@server/tests/shared'
import { wait } from '@shared/core-utils'
import { buildUUID } from '@shared/extra-utils'
import { UserNotification, UserNotificationType, VideoStudioTask, VideoPrivacy } from '@shared/models'
import { cleanupTests, PeerTubeServer, waitJobs } from '@shared/server-commands'
import { UserNotification, UserNotificationType, VideoPrivacy, VideoStudioTask } from '@shared/models'
import { cleanupTests, findExternalSavedVideo, PeerTubeServer, stopFfmpeg, waitJobs } from '@shared/server-commands'
const expect = chai.expect
@@ -323,6 +323,76 @@ describe('Test user notifications', function () {
})
})
describe('My live replay is published', function () {
let baseParams: CheckerBaseParams
before(() => {
baseParams = {
server: servers[1],
emails,
socketNotifications: adminNotificationsServer2,
token: servers[1].accessToken
}
})
it('Should send a notification is a live replay of a non permanent live is published', async function () {
this.timeout(120000)
const { shortUUID } = await servers[1].live.create({
fields: {
name: 'non permanent live',
privacy: VideoPrivacy.PUBLIC,
channelId: servers[1].store.channel.id,
saveReplay: true,
permanentLive: false
}
})
const ffmpegCommand = await servers[1].live.sendRTMPStreamInVideo({ videoId: shortUUID })
await waitJobs(servers)
await servers[1].live.waitUntilPublished({ videoId: shortUUID })
await stopFfmpeg(ffmpegCommand)
await servers[1].live.waitUntilReplacedByReplay({ videoId: shortUUID })
await waitJobs(servers)
await checkVideoIsPublished({ ...baseParams, videoName: 'non permanent live', shortUUID, checkType: 'presence' })
})
it('Should send a notification is a live replay of a permanent live is published', async function () {
this.timeout(120000)
const { shortUUID } = await servers[1].live.create({
fields: {
name: 'permanent live',
privacy: VideoPrivacy.PUBLIC,
channelId: servers[1].store.channel.id,
saveReplay: true,
permanentLive: true
}
})
const ffmpegCommand = await servers[1].live.sendRTMPStreamInVideo({ videoId: shortUUID })
await waitJobs(servers)
await servers[1].live.waitUntilPublished({ videoId: shortUUID })
const liveDetails = await servers[1].videos.get({ id: shortUUID })
await stopFfmpeg(ffmpegCommand)
await servers[1].live.waitUntilWaiting({ videoId: shortUUID })
await waitJobs(servers)
const video = await findExternalSavedVideo(servers[1], liveDetails)
expect(video).to.exist
await checkVideoIsPublished({ ...baseParams, videoName: video.name, shortUUID: video.shortUUID, checkType: 'presence' })
})
})
describe('Video studio', function () {
let baseParams: CheckerBaseParams