1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { pathExists, readdir } from 'fs-extra'
5 import { join } from 'path'
6 import { LiveVideo, VideoStreamingPlaylistType } from '@shared/models'
7 import { ObjectStorageCommand, PeerTubeServer } from '@shared/server-commands'
8 import { checkLiveSegmentHash, checkResolutionsInMasterPlaylist } from './streaming-playlists'
10 async function checkLiveCleanup (server: PeerTubeServer, videoUUID: string, savedResolutions: number[] = []) {
11 const basePath = server.servers.buildDirectory('streaming-playlists')
12 const hlsPath = join(basePath, 'hls', videoUUID)
14 if (savedResolutions.length === 0) {
15 return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
18 return checkSavedLiveCleanup(hlsPath, savedResolutions)
21 // ---------------------------------------------------------------------------
23 async function testVideoResolutions (options: {
24 originServer: PeerTubeServer
25 servers: PeerTubeServer[]
29 objectStorage: boolean
31 const { originServer, servers, liveVideoId, resolutions, transcoded, objectStorage } = options
33 for (const server of servers) {
34 const { data } = await server.videos.list()
35 expect(data.find(v => v.uuid === liveVideoId)).to.exist
37 const video = await server.videos.get({ id: liveVideoId })
38 expect(video.streamingPlaylists).to.have.lengthOf(1)
40 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
41 expect(hlsPlaylist).to.exist
42 expect(hlsPlaylist.files).to.have.lengthOf(0) // Only fragmented mp4 files are displayed
44 await checkResolutionsInMasterPlaylist({
46 playlistUrl: hlsPlaylist.playlistUrl,
49 withRetry: objectStorage
53 expect(hlsPlaylist.playlistUrl).to.contain(ObjectStorageCommand.getPlaylistBaseUrl())
56 for (let i = 0; i < resolutions.length; i++) {
58 const segmentName = `${i}-00000${segmentNum}.ts`
59 await originServer.live.waitUntilSegmentGeneration({
61 videoUUID: video.uuid,
67 const baseUrl = objectStorage
68 ? ObjectStorageCommand.getPlaylistBaseUrl() + 'hls'
69 : originServer.url + '/static/streaming-playlists/hls'
72 expect(hlsPlaylist.segmentsSha256Url).to.contain(ObjectStorageCommand.getPlaylistBaseUrl())
75 const subPlaylist = await originServer.streamingPlaylists.get({
76 url: `${baseUrl}/${video.uuid}/${i}.m3u8`,
77 withRetry: objectStorage // With object storage, the request may fail because of inconsistent data in S3
80 expect(subPlaylist).to.contain(segmentName)
82 await checkLiveSegmentHash({
84 baseUrlSegment: baseUrl,
85 videoUUID: video.uuid,
93 // ---------------------------------------------------------------------------
100 // ---------------------------------------------------------------------------
102 async function checkSavedLiveCleanup (hlsPath: string, savedResolutions: number[] = []) {
103 const files = await readdir(hlsPath)
105 // fragmented file and playlist per resolution + master playlist + segments sha256 json file
106 expect(files).to.have.lengthOf(savedResolutions.length * 2 + 2)
108 for (const resolution of savedResolutions) {
109 const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
110 expect(fragmentedFile).to.exist
112 const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
113 expect(playlistFile).to.exist
116 const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
117 expect(masterPlaylistFile).to.exist
119 const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
120 expect(shaFile).to.exist
123 async function checkUnsavedLiveCleanup (server: PeerTubeServer, videoUUID: string, hlsPath: string) {
127 live = await server.live.get({ videoId: videoUUID })
130 if (live?.permanentLive) {
131 expect(await pathExists(hlsPath)).to.be.true
133 const hlsFiles = await readdir(hlsPath)
134 expect(hlsFiles).to.have.lengthOf(1) // Only replays directory
136 const replayDir = join(hlsPath, 'replay')
137 expect(await pathExists(replayDir)).to.be.true
139 const replayFiles = await readdir(join(hlsPath, 'replay'))
140 expect(replayFiles).to.have.lengthOf(0)
145 expect(await pathExists(hlsPath)).to.be.false