2021-12-17 04:58:15 -06:00
|
|
|
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
|
|
|
|
|
|
|
|
import { expect } from 'chai'
|
|
|
|
import { pathExists, readdir } from 'fs-extra'
|
|
|
|
import { join } from 'path'
|
2023-01-27 01:30:56 -06:00
|
|
|
import { sha1 } from '@shared/extra-utils'
|
2022-10-04 03:03:17 -05:00
|
|
|
import { LiveVideo, VideoStreamingPlaylistType } from '@shared/models'
|
|
|
|
import { ObjectStorageCommand, PeerTubeServer } from '@shared/server-commands'
|
2023-04-21 08:00:01 -05:00
|
|
|
import { SQLCommand } from './sql-command'
|
2022-10-04 03:03:17 -05:00
|
|
|
import { checkLiveSegmentHash, checkResolutionsInMasterPlaylist } from './streaming-playlists'
|
2021-12-17 04:58:15 -06:00
|
|
|
|
2022-10-25 07:18:59 -05:00
|
|
|
async function checkLiveCleanup (options: {
|
|
|
|
server: PeerTubeServer
|
|
|
|
videoUUID: string
|
|
|
|
permanent: boolean
|
|
|
|
savedResolutions?: number[]
|
|
|
|
}) {
|
|
|
|
const { server, videoUUID, permanent, savedResolutions = [] } = options
|
|
|
|
|
2021-12-17 04:58:15 -06:00
|
|
|
const basePath = server.servers.buildDirectory('streaming-playlists')
|
|
|
|
const hlsPath = join(basePath, 'hls', videoUUID)
|
|
|
|
|
2022-10-25 07:18:59 -05:00
|
|
|
if (permanent) {
|
|
|
|
if (!await pathExists(hlsPath)) return
|
|
|
|
|
|
|
|
const files = await readdir(hlsPath)
|
|
|
|
expect(files).to.have.lengthOf(0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-04-21 02:06:52 -05:00
|
|
|
if (savedResolutions.length === 0) {
|
2022-10-04 03:03:17 -05:00
|
|
|
return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
|
|
|
|
}
|
|
|
|
|
|
|
|
return checkSavedLiveCleanup(hlsPath, savedResolutions)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
2022-05-25 07:54:16 -05:00
|
|
|
|
2023-04-21 08:00:01 -05:00
|
|
|
async function testLiveVideoResolutions (options: {
|
|
|
|
sqlCommand: SQLCommand
|
2022-10-04 03:03:17 -05:00
|
|
|
originServer: PeerTubeServer
|
2023-04-21 08:00:01 -05:00
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
servers: PeerTubeServer[]
|
|
|
|
liveVideoId: string
|
|
|
|
resolutions: number[]
|
|
|
|
transcoded: boolean
|
2023-01-27 01:30:56 -06:00
|
|
|
|
2023-05-23 03:49:45 -05:00
|
|
|
objectStorage?: ObjectStorageCommand
|
2023-01-27 01:30:56 -06:00
|
|
|
objectStorageBaseUrl?: string
|
2022-10-04 03:03:17 -05:00
|
|
|
}) {
|
2023-01-27 01:30:56 -06:00
|
|
|
const {
|
|
|
|
originServer,
|
2023-04-21 08:00:01 -05:00
|
|
|
sqlCommand,
|
2023-01-27 01:30:56 -06:00
|
|
|
servers,
|
|
|
|
liveVideoId,
|
|
|
|
resolutions,
|
|
|
|
transcoded,
|
|
|
|
objectStorage,
|
2023-05-23 03:49:45 -05:00
|
|
|
objectStorageBaseUrl = objectStorage?.getMockPlaylistBaseUrl()
|
2023-01-27 01:30:56 -06:00
|
|
|
} = options
|
2022-05-25 07:54:16 -05:00
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
for (const server of servers) {
|
|
|
|
const { data } = await server.videos.list()
|
|
|
|
expect(data.find(v => v.uuid === liveVideoId)).to.exist
|
2022-05-25 07:54:16 -05:00
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
const video = await server.videos.get({ id: liveVideoId })
|
|
|
|
expect(video.streamingPlaylists).to.have.lengthOf(1)
|
2022-05-25 07:54:16 -05:00
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
|
|
|
|
expect(hlsPlaylist).to.exist
|
|
|
|
expect(hlsPlaylist.files).to.have.lengthOf(0) // Only fragmented mp4 files are displayed
|
|
|
|
|
2022-10-11 07:20:19 -05:00
|
|
|
await checkResolutionsInMasterPlaylist({
|
|
|
|
server,
|
|
|
|
playlistUrl: hlsPlaylist.playlistUrl,
|
|
|
|
resolutions,
|
|
|
|
transcoded,
|
2023-05-23 03:49:45 -05:00
|
|
|
withRetry: !!objectStorage
|
2022-10-11 07:20:19 -05:00
|
|
|
})
|
2022-10-04 03:03:17 -05:00
|
|
|
|
|
|
|
if (objectStorage) {
|
2023-01-27 01:30:56 -06:00
|
|
|
expect(hlsPlaylist.playlistUrl).to.contain(objectStorageBaseUrl)
|
2022-05-25 07:54:16 -05:00
|
|
|
}
|
2021-12-17 04:58:15 -06:00
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
for (let i = 0; i < resolutions.length; i++) {
|
|
|
|
const segmentNum = 3
|
|
|
|
const segmentName = `${i}-00000${segmentNum}.ts`
|
2022-10-11 09:00:11 -05:00
|
|
|
await originServer.live.waitUntilSegmentGeneration({
|
|
|
|
server: originServer,
|
|
|
|
videoUUID: video.uuid,
|
|
|
|
playlistNumber: i,
|
|
|
|
segment: segmentNum,
|
2023-01-27 01:30:56 -06:00
|
|
|
objectStorage,
|
|
|
|
objectStorageBaseUrl
|
2022-10-11 09:00:11 -05:00
|
|
|
})
|
2022-10-04 03:03:17 -05:00
|
|
|
|
|
|
|
const baseUrl = objectStorage
|
2023-01-27 01:30:56 -06:00
|
|
|
? join(objectStorageBaseUrl, 'hls')
|
2022-10-04 03:03:17 -05:00
|
|
|
: originServer.url + '/static/streaming-playlists/hls'
|
|
|
|
|
|
|
|
if (objectStorage) {
|
2023-01-27 01:30:56 -06:00
|
|
|
expect(hlsPlaylist.segmentsSha256Url).to.contain(objectStorageBaseUrl)
|
2022-10-04 03:03:17 -05:00
|
|
|
}
|
|
|
|
|
2022-10-10 04:31:01 -05:00
|
|
|
const subPlaylist = await originServer.streamingPlaylists.get({
|
|
|
|
url: `${baseUrl}/${video.uuid}/${i}.m3u8`,
|
2023-05-23 03:49:45 -05:00
|
|
|
withRetry: !!objectStorage // With object storage, the request may fail because of inconsistent data in S3
|
2022-10-10 04:31:01 -05:00
|
|
|
})
|
2022-10-04 03:03:17 -05:00
|
|
|
|
|
|
|
expect(subPlaylist).to.contain(segmentName)
|
|
|
|
|
|
|
|
await checkLiveSegmentHash({
|
|
|
|
server,
|
|
|
|
baseUrlSegment: baseUrl,
|
|
|
|
videoUUID: video.uuid,
|
|
|
|
segmentName,
|
2023-05-10 02:28:42 -05:00
|
|
|
hlsPlaylist,
|
2023-05-23 03:49:45 -05:00
|
|
|
withRetry: !!objectStorage // With object storage, the request may fail because of inconsistent data in S3
|
2022-10-04 03:03:17 -05:00
|
|
|
})
|
2022-12-30 08:05:14 -06:00
|
|
|
|
|
|
|
if (originServer.internalServerNumber === server.internalServerNumber) {
|
|
|
|
const infohash = sha1(`${2 + hlsPlaylist.playlistUrl}+V${i}`)
|
2023-04-21 08:00:01 -05:00
|
|
|
const dbInfohashes = await sqlCommand.getPlaylistInfohash(hlsPlaylist.id)
|
2022-12-30 08:05:14 -06:00
|
|
|
|
|
|
|
expect(dbInfohashes).to.include(infohash)
|
|
|
|
}
|
2022-10-04 03:03:17 -05:00
|
|
|
}
|
2021-12-17 04:58:15 -06:00
|
|
|
}
|
2022-10-04 03:03:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
export {
|
|
|
|
checkLiveCleanup,
|
2023-04-21 08:00:01 -05:00
|
|
|
testLiveVideoResolutions
|
2022-10-04 03:03:17 -05:00
|
|
|
}
|
2021-12-17 04:58:15 -06:00
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
async function checkSavedLiveCleanup (hlsPath: string, savedResolutions: number[] = []) {
|
2021-12-17 04:58:15 -06:00
|
|
|
const files = await readdir(hlsPath)
|
|
|
|
|
|
|
|
// fragmented file and playlist per resolution + master playlist + segments sha256 json file
|
2023-05-19 07:49:00 -05:00
|
|
|
expect(files, `Directory content: ${files.join(', ')}`).to.have.lengthOf(savedResolutions.length * 2 + 2)
|
2021-12-17 04:58:15 -06:00
|
|
|
|
2022-04-21 02:06:52 -05:00
|
|
|
for (const resolution of savedResolutions) {
|
2021-12-17 04:58:15 -06:00
|
|
|
const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
|
|
|
|
expect(fragmentedFile).to.exist
|
|
|
|
|
|
|
|
const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
|
|
|
|
expect(playlistFile).to.exist
|
|
|
|
}
|
|
|
|
|
|
|
|
const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
|
|
|
|
expect(masterPlaylistFile).to.exist
|
|
|
|
|
|
|
|
const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
|
|
|
|
expect(shaFile).to.exist
|
|
|
|
}
|
|
|
|
|
2022-10-04 03:03:17 -05:00
|
|
|
async function checkUnsavedLiveCleanup (server: PeerTubeServer, videoUUID: string, hlsPath: string) {
|
|
|
|
let live: LiveVideo
|
|
|
|
|
|
|
|
try {
|
|
|
|
live = await server.live.get({ videoId: videoUUID })
|
|
|
|
} catch {}
|
|
|
|
|
|
|
|
if (live?.permanentLive) {
|
|
|
|
expect(await pathExists(hlsPath)).to.be.true
|
|
|
|
|
|
|
|
const hlsFiles = await readdir(hlsPath)
|
|
|
|
expect(hlsFiles).to.have.lengthOf(1) // Only replays directory
|
|
|
|
|
|
|
|
const replayDir = join(hlsPath, 'replay')
|
|
|
|
expect(await pathExists(replayDir)).to.be.true
|
|
|
|
|
|
|
|
const replayFiles = await readdir(join(hlsPath, 'replay'))
|
|
|
|
expect(replayFiles).to.have.lengthOf(0)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
expect(await pathExists(hlsPath)).to.be.false
|
2021-12-17 04:58:15 -06:00
|
|
|
}
|