]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/shared/live.ts
Merge branch 'release/4.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / tests / shared / live.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
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'
9
10 async function checkLiveCleanup (options: {
11 server: PeerTubeServer
12 videoUUID: string
13 permanent: boolean
14 savedResolutions?: number[]
15 }) {
16 const { server, videoUUID, permanent, savedResolutions = [] } = options
17
18 const basePath = server.servers.buildDirectory('streaming-playlists')
19 const hlsPath = join(basePath, 'hls', videoUUID)
20
21 if (permanent) {
22 if (!await pathExists(hlsPath)) return
23
24 const files = await readdir(hlsPath)
25 expect(files).to.have.lengthOf(0)
26 return
27 }
28
29 if (savedResolutions.length === 0) {
30 return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
31 }
32
33 return checkSavedLiveCleanup(hlsPath, savedResolutions)
34 }
35
36 // ---------------------------------------------------------------------------
37
38 async function testVideoResolutions (options: {
39 originServer: PeerTubeServer
40 servers: PeerTubeServer[]
41 liveVideoId: string
42 resolutions: number[]
43 transcoded: boolean
44 objectStorage: boolean
45 }) {
46 const { originServer, servers, liveVideoId, resolutions, transcoded, objectStorage } = options
47
48 for (const server of servers) {
49 const { data } = await server.videos.list()
50 expect(data.find(v => v.uuid === liveVideoId)).to.exist
51
52 const video = await server.videos.get({ id: liveVideoId })
53 expect(video.streamingPlaylists).to.have.lengthOf(1)
54
55 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
56 expect(hlsPlaylist).to.exist
57 expect(hlsPlaylist.files).to.have.lengthOf(0) // Only fragmented mp4 files are displayed
58
59 await checkResolutionsInMasterPlaylist({
60 server,
61 playlistUrl: hlsPlaylist.playlistUrl,
62 resolutions,
63 transcoded,
64 withRetry: objectStorage
65 })
66
67 if (objectStorage) {
68 expect(hlsPlaylist.playlistUrl).to.contain(ObjectStorageCommand.getMockPlaylistBaseUrl())
69 }
70
71 for (let i = 0; i < resolutions.length; i++) {
72 const segmentNum = 3
73 const segmentName = `${i}-00000${segmentNum}.ts`
74 await originServer.live.waitUntilSegmentGeneration({
75 server: originServer,
76 videoUUID: video.uuid,
77 playlistNumber: i,
78 segment: segmentNum,
79 objectStorage
80 })
81
82 const baseUrl = objectStorage
83 ? ObjectStorageCommand.getMockPlaylistBaseUrl() + 'hls'
84 : originServer.url + '/static/streaming-playlists/hls'
85
86 if (objectStorage) {
87 expect(hlsPlaylist.segmentsSha256Url).to.contain(ObjectStorageCommand.getMockPlaylistBaseUrl())
88 }
89
90 const subPlaylist = await originServer.streamingPlaylists.get({
91 url: `${baseUrl}/${video.uuid}/${i}.m3u8`,
92 withRetry: objectStorage // With object storage, the request may fail because of inconsistent data in S3
93 })
94
95 expect(subPlaylist).to.contain(segmentName)
96
97 await checkLiveSegmentHash({
98 server,
99 baseUrlSegment: baseUrl,
100 videoUUID: video.uuid,
101 segmentName,
102 hlsPlaylist
103 })
104 }
105 }
106 }
107
108 // ---------------------------------------------------------------------------
109
110 export {
111 checkLiveCleanup,
112 testVideoResolutions
113 }
114
115 // ---------------------------------------------------------------------------
116
117 async function checkSavedLiveCleanup (hlsPath: string, savedResolutions: number[] = []) {
118 const files = await readdir(hlsPath)
119
120 // fragmented file and playlist per resolution + master playlist + segments sha256 json file
121 expect(files).to.have.lengthOf(savedResolutions.length * 2 + 2)
122
123 for (const resolution of savedResolutions) {
124 const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
125 expect(fragmentedFile).to.exist
126
127 const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
128 expect(playlistFile).to.exist
129 }
130
131 const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
132 expect(masterPlaylistFile).to.exist
133
134 const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
135 expect(shaFile).to.exist
136 }
137
138 async function checkUnsavedLiveCleanup (server: PeerTubeServer, videoUUID: string, hlsPath: string) {
139 let live: LiveVideo
140
141 try {
142 live = await server.live.get({ videoId: videoUUID })
143 } catch {}
144
145 if (live?.permanentLive) {
146 expect(await pathExists(hlsPath)).to.be.true
147
148 const hlsFiles = await readdir(hlsPath)
149 expect(hlsFiles).to.have.lengthOf(1) // Only replays directory
150
151 const replayDir = join(hlsPath, 'replay')
152 expect(await pathExists(replayDir)).to.be.true
153
154 const replayFiles = await readdir(join(hlsPath, 'replay'))
155 expect(replayFiles).to.have.lengthOf(0)
156
157 return
158 }
159
160 expect(await pathExists(hlsPath)).to.be.false
161 }