import { logger } from '@server/helpers/logger'
import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/types/models'
import { VideoStorage } from '@shared/models'
-import { listHLSFileKeysOf, removeHLSFileObjectStorage, removeHLSObjectStorage } from '../object-storage'
+import { listHLSFileKeysOf, removeHLSFileObjectStorageByFullKey, removeHLSObjectStorage } from '../object-storage'
import { getLiveDirectory } from '../paths'
function buildConcatenatedName (segmentOrPlaylistPath: string) {
async function cleanupTMPLiveFilesFromObjectStorage (streamingPlaylist: MStreamingPlaylistVideo) {
if (streamingPlaylist.storage !== VideoStorage.OBJECT_STORAGE) return
+ logger.info('Cleanup TMP live files from object storage for %s.', streamingPlaylist.Video.uuid)
+
const keys = await listHLSFileKeysOf(streamingPlaylist)
for (const key of keys) {
if (isTMPLiveFile(key)) {
- await removeHLSFileObjectStorage(streamingPlaylist, key)
+ await removeHLSFileObjectStorageByFullKey(key)
}
}
}
import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
import { CONFIG } from '@server/initializers/config'
import { MEMOIZE_TTL, VIDEO_LIVE } from '@server/initializers/constants'
-import { removeHLSFileObjectStorage, storeHLSFileFromFilename, storeHLSFileFromPath } from '@server/lib/object-storage'
+import { removeHLSFileObjectStorageByPath, storeHLSFileFromFilename, storeHLSFileFromPath } from '@server/lib/object-storage'
import { VideoFileModel } from '@server/models/video/video-file'
import { MStreamingPlaylistVideo, MUserId, MVideoLiveVideo } from '@server/types/models'
import { VideoStorage } from '@shared/models'
if (this.streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) {
try {
- await removeHLSFileObjectStorage(this.streamingPlaylist, segmentPath)
+ await removeHLSFileObjectStorageByPath(this.streamingPlaylist, segmentPath)
} catch (err) {
logger.error('Cannot remove segment %s from object storage', segmentPath, { err, ...this.lTags() })
}
function removeObject (objectStorageKey: string, bucketInfo: BucketInfo) {
const key = buildKey(objectStorageKey, bucketInfo)
- logger.debug('Removing file %s in bucket %s', key, bucketInfo.BUCKET_NAME, lTags())
+ return removeObjectByFullKey(key, bucketInfo)
+}
+
+function removeObjectByFullKey (fullKey: string, bucketInfo: BucketInfo) {
+ logger.debug('Removing file %s in bucket %s', fullKey, bucketInfo.BUCKET_NAME, lTags())
const command = new DeleteObjectCommand({
Bucket: bucketInfo.BUCKET_NAME,
- Key: key
+ Key: fullKey
})
return getClient().send(command)
storeObject,
removeObject,
+ removeObjectByFullKey,
removePrefix,
makeAvailable,
lTags,
makeAvailable,
removeObject,
+ removeObjectByFullKey,
removePrefix,
storeObject,
updateObjectACL,
return removePrefix(generateHLSObjectBaseStorageKey(playlist), CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS)
}
-function removeHLSFileObjectStorage (playlist: MStreamingPlaylistVideo, filename: string) {
+function removeHLSFileObjectStorageByFilename (playlist: MStreamingPlaylistVideo, filename: string) {
return removeObject(generateHLSObjectStorageKey(playlist, filename), CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS)
}
+function removeHLSFileObjectStorageByPath (playlist: MStreamingPlaylistVideo, path: string) {
+ return removeObject(generateHLSObjectStorageKey(playlist, basename(path)), CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS)
+}
+
+function removeHLSFileObjectStorageByFullKey (key: string) {
+ return removeObjectByFullKey(key, CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS)
+}
+
// ---------------------------------------------------------------------------
function removeWebTorrentObjectStorage (videoFile: MVideoFile) {
updateHLSFilesACL,
removeHLSObjectStorage,
- removeHLSFileObjectStorage,
+ removeHLSFileObjectStorageByFilename,
+ removeHLSFileObjectStorageByPath,
+ removeHLSFileObjectStorageByFullKey,
+
removeWebTorrentObjectStorage,
makeWebTorrentFileAvailable,
} from 'sequelize-typescript'
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
import { LiveManager } from '@server/lib/live/live-manager'
-import { removeHLSFileObjectStorage, removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
+import { removeHLSFileObjectStorageByFilename, removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
import { tracer } from '@server/lib/opentelemetry/tracing'
import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
import { VideoPathManager } from '@server/lib/video-path-manager'
await remove(VideoPathManager.Instance.getFSHLSOutputPath(this, resolutionFilename))
if (videoFile.storage === VideoStorage.OBJECT_STORAGE) {
- await removeHLSFileObjectStorage(streamingPlaylist.withVideo(this), videoFile.filename)
- await removeHLSFileObjectStorage(streamingPlaylist.withVideo(this), resolutionFilename)
+ await removeHLSFileObjectStorageByFilename(streamingPlaylist.withVideo(this), videoFile.filename)
+ await removeHLSFileObjectStorageByFilename(streamingPlaylist.withVideo(this), resolutionFilename)
}
}
await remove(filePath)
if (streamingPlaylist.storage === VideoStorage.OBJECT_STORAGE) {
- await removeHLSFileObjectStorage(streamingPlaylist.withVideo(this), filename)
+ await removeHLSFileObjectStorageByFilename(streamingPlaylist.withVideo(this), filename)
}
}
expect(video.duration).to.be.greaterThan(0)
}
- await checkLiveCleanup(servers[0], videoId, resolutions)
+ await checkLiveCleanup({ server: servers[0], permanent: false, videoUUID: videoId, savedResolutions: resolutions })
}
function updateQuota (options: { total: number, daily: number }) {
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import { expect } from 'chai'
+import { checkLiveCleanup } from '@server/tests/shared'
import { wait } from '@shared/core-utils'
import { LiveVideoCreate, VideoPrivacy, VideoState } from '@shared/models'
import {
expect(videoDetails.streamingPlaylists).to.have.lengthOf(0)
}
+
+ await checkLiveCleanup({ server: servers[0], permanent: true, videoUUID })
})
it('Should have set this live to waiting for live state', async function () {
}
})
+ it('Should remove the live and have cleaned up the directory', async function () {
+ this.timeout(60000)
+
+ await servers[0].videos.remove({ id: videoUUID })
+ await waitJobs(servers)
+
+ await checkLiveCleanup({ server: servers[0], permanent: true, videoUUID })
+ })
+
after(async function () {
await cleanupTests(servers)
})
await checkVideoState(liveVideoUUID, VideoState.LIVE_ENDED)
// No resolutions saved since we did not save replay
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
it('Should have appropriate ended session', async function () {
await wait(5000)
await waitJobs(servers)
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
it('Should have blacklisted session error', async function () {
await publishLiveAndDelete({ permanent: false, replay: false })
await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
})
})
it('Should have cleaned up the live files', async function () {
- await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false, savedResolutions: [ 720 ] })
})
it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
await wait(5000)
await waitJobs(servers)
- await checkLiveCleanup(servers[0], liveVideoUUID, [ 720 ])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false, savedResolutions: [ 720 ] })
})
it('Should correctly terminate the stream on delete and delete the video', async function () {
await publishLiveAndDelete({ permanent: false, replay: true })
await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
})
})
it('Should have cleaned up the live files', async function () {
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
await servers[1].videos.get({ id: videoId, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
}
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
it('Should correctly terminate the stream on delete and not save the video', async function () {
expect(replay).to.not.exist
await checkVideosExist(liveVideoUUID, false, HttpStatusCode.NOT_FOUND_404)
- await checkLiveCleanup(servers[0], liveVideoUUID, [])
+ await checkLiveCleanup({ server: servers[0], videoUUID: liveVideoUUID, permanent: false })
})
})
import { ObjectStorageCommand, PeerTubeServer } from '@shared/server-commands'
import { checkLiveSegmentHash, checkResolutionsInMasterPlaylist } from './streaming-playlists'
-async function checkLiveCleanup (server: PeerTubeServer, videoUUID: string, savedResolutions: number[] = []) {
+async function checkLiveCleanup (options: {
+ server: PeerTubeServer
+ videoUUID: string
+ permanent: boolean
+ savedResolutions?: number[]
+}) {
+ const { server, videoUUID, permanent, savedResolutions = [] } = options
+
const basePath = server.servers.buildDirectory('streaming-playlists')
const hlsPath = join(basePath, 'hls', videoUUID)
+ if (permanent) {
+ if (!await pathExists(hlsPath)) return
+
+ const files = await readdir(hlsPath)
+ expect(files).to.have.lengthOf(0)
+ return
+ }
+
if (savedResolutions.length === 0) {
return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
}