Correctly cleanup permanent live empty directories

This commit is contained in:
Chocobozzz 2024-09-11 14:45:02 +02:00
parent be7bc3a6a9
commit 71bdad9f5e
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
3 changed files with 40 additions and 17 deletions

View File

@ -269,7 +269,7 @@ describe('Save replay setting', function () {
await publishLiveAndDelete({ permanent: false, replay: false }) await publishLiveAndDelete({ permanent: false, replay: false })
await checkVideosExist(liveVideoUUID, 0, HttpStatusCode.NOT_FOUND_404) await checkVideosExist(liveVideoUUID, 0, HttpStatusCode.NOT_FOUND_404)
await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false }) await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false, deleted: true })
}) })
}) })
@ -381,7 +381,7 @@ describe('Save replay setting', function () {
await publishLiveAndDelete({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.PUBLIC } }) await publishLiveAndDelete({ permanent: false, replay: true, replaySettings: { privacy: VideoPrivacy.PUBLIC } })
await checkVideosExist(liveVideoUUID, 0, HttpStatusCode.NOT_FOUND_404) await checkVideosExist(liveVideoUUID, 0, HttpStatusCode.NOT_FOUND_404)
await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false }) await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false, deleted: true })
}) })
}) })
@ -563,7 +563,14 @@ describe('Save replay setting', function () {
}) })
it('Should have cleaned up the live files', async function () { it('Should have cleaned up the live files', async function () {
await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false }) await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: true })
})
it('Should delete the empty live and also delete the empty directory', async function () {
await servers[0].videos.remove({ id: liveVideoUUID })
await waitJobs(servers)
await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: true, deleted: true })
}) })
it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () { it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
@ -586,7 +593,7 @@ describe('Save replay setting', function () {
await servers[1].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) await servers[1].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
} }
await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false }) await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: true })
}) })
it('Should correctly terminate the stream on delete and not save the video', async function () { it('Should correctly terminate the stream on delete and not save the video', async function () {
@ -602,7 +609,7 @@ describe('Save replay setting', function () {
expect(replay).to.not.exist expect(replay).to.not.exist
await checkVideosExist(liveVideoUUID, 1, HttpStatusCode.NOT_FOUND_404) await checkVideosExist(liveVideoUUID, 1, HttpStatusCode.NOT_FOUND_404)
await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false }) await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: true, deleted: true })
}) })
}) })
}) })

View File

@ -16,14 +16,21 @@ async function checkLiveCleanup (options: {
videoUUID: string videoUUID: string
permanent: boolean permanent: boolean
savedResolutions?: number[] savedResolutions?: number[]
deleted?: boolean // default false
}) { }) {
const { server, videoUUID, permanent, savedResolutions = [] } = options const { server, videoUUID, permanent, savedResolutions = [], deleted = false } = options
const basePath = server.servers.buildDirectory('streaming-playlists') const basePath = server.servers.buildDirectory('streaming-playlists')
const hlsPath = join(basePath, 'hls', videoUUID) const hlsPath = join(basePath, 'hls', videoUUID)
const hlsPathExists = await pathExists(hlsPath)
if (deleted) {
expect(hlsPathExists).to.be.false
return
}
if (permanent) { if (permanent) {
if (!await pathExists(hlsPath)) return if (!hlsPathExists) return
const files = await readdir(hlsPath) const files = await readdir(hlsPath)
expect(files.filter(f => f !== 'replay')).to.have.lengthOf(0) expect(files.filter(f => f !== 'replay')).to.have.lengthOf(0)
@ -32,15 +39,13 @@ async function checkLiveCleanup (options: {
if (await pathExists(replayDir)) { if (await pathExists(replayDir)) {
expect(await readdir(replayDir)).to.have.lengthOf(0) expect(await readdir(replayDir)).to.have.lengthOf(0)
} }
} else {
if (savedResolutions.length === 0) {
return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
}
return return checkSavedLiveCleanup(hlsPath, savedResolutions)
} }
if (savedResolutions.length === 0) {
return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
}
return checkSavedLiveCleanup(hlsPath, savedResolutions)
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -1,12 +1,12 @@
import { pathExists, remove } from 'fs-extra/esm' import { pathExists, remove } from 'fs-extra/esm'
import { readdir } from 'fs/promises' import { readdir, rmdir } from 'fs/promises'
import { basename, join } from 'path' import { basename, join } from 'path'
import { LiveVideoLatencyMode, LiveVideoLatencyModeType, FileStorage } from '@peertube/peertube-models' import { LiveVideoLatencyMode, LiveVideoLatencyModeType, FileStorage, VideoState } from '@peertube/peertube-models'
import { logger } from '@server/helpers/logger.js' import { logger } from '@server/helpers/logger.js'
import { VIDEO_LIVE } from '@server/initializers/constants.js' import { VIDEO_LIVE } from '@server/initializers/constants.js'
import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/types/models/index.js' import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/types/models/index.js'
import { listHLSFileKeysOf, removeHLSFileObjectStorageByFullKey, removeHLSObjectStorage } from '../object-storage/index.js' import { listHLSFileKeysOf, removeHLSFileObjectStorageByFullKey, removeHLSObjectStorage } from '../object-storage/index.js'
import { getLiveDirectory } from '../paths.js' import { getLiveDirectory, getLiveReplayBaseDirectory } from '../paths.js'
function buildConcatenatedName (segmentOrPlaylistPath: string) { function buildConcatenatedName (segmentOrPlaylistPath: string) {
const num = basename(segmentOrPlaylistPath).match(/^(\d+)(-|\.)/) const num = basename(segmentOrPlaylistPath).match(/^(\d+)(-|\.)/)
@ -17,6 +17,17 @@ function buildConcatenatedName (segmentOrPlaylistPath: string) {
async function cleanupAndDestroyPermanentLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) { async function cleanupAndDestroyPermanentLive (video: MVideo, streamingPlaylist: MStreamingPlaylist) {
await cleanupTMPLiveFiles(video, streamingPlaylist) await cleanupTMPLiveFiles(video, streamingPlaylist)
if (video.state === VideoState.WAITING_FOR_LIVE) {
// Try to delete local filesystem empty paths
// Object storage doesn't have the concept of directories so we don't need to duplicate the logic here
try {
await rmdir(getLiveReplayBaseDirectory(video))
await rmdir(getLiveDirectory(video))
} catch (err) {
logger.debug('Cannot cleanup permanent local live files', { err })
}
}
await streamingPlaylist.destroy() await streamingPlaylist.destroy()
} }