diff options
author | Chocobozzz <me@florianbigard.com> | 2023-04-21 15:00:01 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2023-05-09 08:57:34 +0200 |
commit | d102de1b38f2877463529c3b27bd35ffef4fd8bf (patch) | |
tree | 31fa0bdf26ad7a2ee46d600d804a6f03260266c8 /server/tests/shared/streaming-playlists.ts | |
parent | 2fe978744e5b74eb824e4d79c1bb9b840169f125 (diff) | |
download | PeerTube-d102de1b38f2877463529c3b27bd35ffef4fd8bf.tar.gz PeerTube-d102de1b38f2877463529c3b27bd35ffef4fd8bf.tar.zst PeerTube-d102de1b38f2877463529c3b27bd35ffef4fd8bf.zip |
Add runner server tests
Diffstat (limited to 'server/tests/shared/streaming-playlists.ts')
-rw-r--r-- | server/tests/shared/streaming-playlists.ts | 134 |
1 files changed, 91 insertions, 43 deletions
diff --git a/server/tests/shared/streaming-playlists.ts b/server/tests/shared/streaming-playlists.ts index 1c38cb512..acfb2b408 100644 --- a/server/tests/shared/streaming-playlists.ts +++ b/server/tests/shared/streaming-playlists.ts | |||
@@ -4,10 +4,11 @@ import { expect } from 'chai' | |||
4 | import { basename, dirname, join } from 'path' | 4 | import { basename, dirname, join } from 'path' |
5 | import { removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils' | 5 | import { removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils' |
6 | import { sha256 } from '@shared/extra-utils' | 6 | import { sha256 } from '@shared/extra-utils' |
7 | import { HttpStatusCode, VideoStreamingPlaylist, VideoStreamingPlaylistType } from '@shared/models' | 7 | import { HttpStatusCode, VideoPrivacy, VideoResolution, VideoStreamingPlaylist, VideoStreamingPlaylistType } from '@shared/models' |
8 | import { makeRawRequest, PeerTubeServer, webtorrentAdd } from '@shared/server-commands' | 8 | import { makeRawRequest, PeerTubeServer } from '@shared/server-commands' |
9 | import { expectStartWith } from './checks' | 9 | import { expectStartWith } from './checks' |
10 | import { hlsInfohashExist } from './tracker' | 10 | import { hlsInfohashExist } from './tracker' |
11 | import { checkWebTorrentWorks } from './webtorrent' | ||
11 | 12 | ||
12 | async function checkSegmentHash (options: { | 13 | async function checkSegmentHash (options: { |
13 | server: PeerTubeServer | 14 | server: PeerTubeServer |
@@ -15,14 +16,15 @@ async function checkSegmentHash (options: { | |||
15 | baseUrlSegment: string | 16 | baseUrlSegment: string |
16 | resolution: number | 17 | resolution: number |
17 | hlsPlaylist: VideoStreamingPlaylist | 18 | hlsPlaylist: VideoStreamingPlaylist |
19 | token?: string | ||
18 | }) { | 20 | }) { |
19 | const { server, baseUrlPlaylist, baseUrlSegment, resolution, hlsPlaylist } = options | 21 | const { server, baseUrlPlaylist, baseUrlSegment, resolution, hlsPlaylist, token } = options |
20 | const command = server.streamingPlaylists | 22 | const command = server.streamingPlaylists |
21 | 23 | ||
22 | const file = hlsPlaylist.files.find(f => f.resolution.id === resolution) | 24 | const file = hlsPlaylist.files.find(f => f.resolution.id === resolution) |
23 | const videoName = basename(file.fileUrl) | 25 | const videoName = basename(file.fileUrl) |
24 | 26 | ||
25 | const playlist = await command.get({ url: `${baseUrlPlaylist}/${removeFragmentedMP4Ext(videoName)}.m3u8` }) | 27 | const playlist = await command.get({ url: `${baseUrlPlaylist}/${removeFragmentedMP4Ext(videoName)}.m3u8`, token }) |
26 | 28 | ||
27 | const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist) | 29 | const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist) |
28 | 30 | ||
@@ -33,11 +35,12 @@ async function checkSegmentHash (options: { | |||
33 | const segmentBody = await command.getFragmentedSegment({ | 35 | const segmentBody = await command.getFragmentedSegment({ |
34 | url: `${baseUrlSegment}/${videoName}`, | 36 | url: `${baseUrlSegment}/${videoName}`, |
35 | expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206, | 37 | expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206, |
36 | range: `bytes=${range}` | 38 | range: `bytes=${range}`, |
39 | token | ||
37 | }) | 40 | }) |
38 | 41 | ||
39 | const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url }) | 42 | const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, token }) |
40 | expect(sha256(segmentBody)).to.equal(shaBody[videoName][range]) | 43 | expect(sha256(segmentBody)).to.equal(shaBody[videoName][range], `Invalid sha256 result for ${videoName} range ${range}`) |
41 | } | 44 | } |
42 | 45 | ||
43 | // --------------------------------------------------------------------------- | 46 | // --------------------------------------------------------------------------- |
@@ -64,19 +67,24 @@ async function checkResolutionsInMasterPlaylist (options: { | |||
64 | server: PeerTubeServer | 67 | server: PeerTubeServer |
65 | playlistUrl: string | 68 | playlistUrl: string |
66 | resolutions: number[] | 69 | resolutions: number[] |
70 | token?: string | ||
67 | transcoded?: boolean // default true | 71 | transcoded?: boolean // default true |
68 | withRetry?: boolean // default false | 72 | withRetry?: boolean // default false |
69 | }) { | 73 | }) { |
70 | const { server, playlistUrl, resolutions, withRetry = false, transcoded = true } = options | 74 | const { server, playlistUrl, resolutions, token, withRetry = false, transcoded = true } = options |
71 | 75 | ||
72 | const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl, withRetry }) | 76 | const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl, token, withRetry }) |
73 | 77 | ||
74 | for (const resolution of resolutions) { | 78 | for (const resolution of resolutions) { |
75 | const reg = transcoded | 79 | const base = '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution |
76 | ? new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"') | 80 | |
77 | : new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + '') | 81 | if (resolution === VideoResolution.H_NOVIDEO) { |
78 | 82 | expect(masterPlaylist).to.match(new RegExp(`${base},CODECS="mp4a.40.2"`)) | |
79 | expect(masterPlaylist).to.match(reg) | 83 | } else if (transcoded) { |
84 | expect(masterPlaylist).to.match(new RegExp(`${base},(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"`)) | ||
85 | } else { | ||
86 | expect(masterPlaylist).to.match(new RegExp(`${base}`)) | ||
87 | } | ||
80 | } | 88 | } |
81 | 89 | ||
82 | const playlistsLength = masterPlaylist.split('\n').filter(line => line.startsWith('#EXT-X-STREAM-INF:BANDWIDTH=')) | 90 | const playlistsLength = masterPlaylist.split('\n').filter(line => line.startsWith('#EXT-X-STREAM-INF:BANDWIDTH=')) |
@@ -89,14 +97,23 @@ async function completeCheckHlsPlaylist (options: { | |||
89 | hlsOnly: boolean | 97 | hlsOnly: boolean |
90 | 98 | ||
91 | resolutions?: number[] | 99 | resolutions?: number[] |
92 | objectStorageBaseUrl: string | 100 | objectStorageBaseUrl?: string |
93 | }) { | 101 | }) { |
94 | const { videoUUID, hlsOnly, objectStorageBaseUrl } = options | 102 | const { videoUUID, hlsOnly, objectStorageBaseUrl } = options |
95 | 103 | ||
96 | const resolutions = options.resolutions ?? [ 240, 360, 480, 720 ] | 104 | const resolutions = options.resolutions ?? [ 240, 360, 480, 720 ] |
97 | 105 | ||
98 | for (const server of options.servers) { | 106 | for (const server of options.servers) { |
99 | const videoDetails = await server.videos.get({ id: videoUUID }) | 107 | const videoDetails = await server.videos.getWithToken({ id: videoUUID }) |
108 | const requiresAuth = videoDetails.privacy.id === VideoPrivacy.PRIVATE || videoDetails.privacy.id === VideoPrivacy.INTERNAL | ||
109 | |||
110 | const privatePath = requiresAuth | ||
111 | ? 'private/' | ||
112 | : '' | ||
113 | const token = requiresAuth | ||
114 | ? server.accessToken | ||
115 | : undefined | ||
116 | |||
100 | const baseUrl = `http://${videoDetails.account.host}` | 117 | const baseUrl = `http://${videoDetails.account.host}` |
101 | 118 | ||
102 | expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) | 119 | expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) |
@@ -115,35 +132,55 @@ async function completeCheckHlsPlaylist (options: { | |||
115 | const file = hlsFiles.find(f => f.resolution.id === resolution) | 132 | const file = hlsFiles.find(f => f.resolution.id === resolution) |
116 | expect(file).to.not.be.undefined | 133 | expect(file).to.not.be.undefined |
117 | 134 | ||
118 | expect(file.magnetUri).to.have.lengthOf.above(2) | 135 | if (file.resolution.id === VideoResolution.H_NOVIDEO) { |
119 | expect(file.torrentUrl).to.match( | 136 | expect(file.resolution.label).to.equal('Audio') |
120 | new RegExp(`${server.url}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`) | ||
121 | ) | ||
122 | |||
123 | if (objectStorageBaseUrl) { | ||
124 | expectStartWith(file.fileUrl, objectStorageBaseUrl) | ||
125 | } else { | 137 | } else { |
126 | expect(file.fileUrl).to.match( | 138 | expect(file.resolution.label).to.equal(resolution + 'p') |
127 | new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`) | ||
128 | ) | ||
129 | } | 139 | } |
130 | 140 | ||
131 | expect(file.resolution.label).to.equal(resolution + 'p') | 141 | expect(file.magnetUri).to.have.lengthOf.above(2) |
132 | 142 | await checkWebTorrentWorks(file.magnetUri) | |
133 | await makeRawRequest({ url: file.torrentUrl, expectedStatus: HttpStatusCode.OK_200 }) | 143 | |
134 | await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 }) | 144 | { |
145 | const nameReg = `${uuidRegex}-${file.resolution.id}` | ||
146 | |||
147 | expect(file.torrentUrl).to.match(new RegExp(`${server.url}/lazy-static/torrents/${nameReg}-hls.torrent`)) | ||
148 | |||
149 | if (objectStorageBaseUrl && requiresAuth) { | ||
150 | // eslint-disable-next-line max-len | ||
151 | expect(file.fileUrl).to.match(new RegExp(`${server.url}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoDetails.uuid}/${nameReg}-fragmented.mp4`)) | ||
152 | } else if (objectStorageBaseUrl) { | ||
153 | expectStartWith(file.fileUrl, objectStorageBaseUrl) | ||
154 | } else { | ||
155 | expect(file.fileUrl).to.match( | ||
156 | new RegExp(`${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoDetails.uuid}/${nameReg}-fragmented.mp4`) | ||
157 | ) | ||
158 | } | ||
159 | } | ||
135 | 160 | ||
136 | const torrent = await webtorrentAdd(file.magnetUri, true) | 161 | { |
137 | expect(torrent.files).to.be.an('array') | 162 | await Promise.all([ |
138 | expect(torrent.files.length).to.equal(1) | 163 | makeRawRequest({ url: file.torrentUrl, token, expectedStatus: HttpStatusCode.OK_200 }), |
139 | expect(torrent.files[0].path).to.exist.and.to.not.equal('') | 164 | makeRawRequest({ url: file.torrentDownloadUrl, token, expectedStatus: HttpStatusCode.OK_200 }), |
165 | makeRawRequest({ url: file.metadataUrl, token, expectedStatus: HttpStatusCode.OK_200 }), | ||
166 | makeRawRequest({ url: file.fileUrl, token, expectedStatus: HttpStatusCode.OK_200 }), | ||
167 | |||
168 | makeRawRequest({ | ||
169 | url: file.fileDownloadUrl, | ||
170 | token, | ||
171 | expectedStatus: objectStorageBaseUrl | ||
172 | ? HttpStatusCode.FOUND_302 | ||
173 | : HttpStatusCode.OK_200 | ||
174 | }) | ||
175 | ]) | ||
176 | } | ||
140 | } | 177 | } |
141 | 178 | ||
142 | // Check master playlist | 179 | // Check master playlist |
143 | { | 180 | { |
144 | await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions }) | 181 | await checkResolutionsInMasterPlaylist({ server, token, playlistUrl: hlsPlaylist.playlistUrl, resolutions }) |
145 | 182 | ||
146 | const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl }) | 183 | const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl, token }) |
147 | 184 | ||
148 | let i = 0 | 185 | let i = 0 |
149 | for (const resolution of resolutions) { | 186 | for (const resolution of resolutions) { |
@@ -163,11 +200,16 @@ async function completeCheckHlsPlaylist (options: { | |||
163 | const file = hlsFiles.find(f => f.resolution.id === resolution) | 200 | const file = hlsFiles.find(f => f.resolution.id === resolution) |
164 | const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8' | 201 | const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8' |
165 | 202 | ||
166 | const url = objectStorageBaseUrl | 203 | let url: string |
167 | ? `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}` | 204 | if (objectStorageBaseUrl && requiresAuth) { |
168 | : `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}` | 205 | url = `${baseUrl}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}` |
206 | } else if (objectStorageBaseUrl) { | ||
207 | url = `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}` | ||
208 | } else { | ||
209 | url = `${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}` | ||
210 | } | ||
169 | 211 | ||
170 | const subPlaylist = await server.streamingPlaylists.get({ url }) | 212 | const subPlaylist = await server.streamingPlaylists.get({ url, token }) |
171 | 213 | ||
172 | expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`)) | 214 | expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`)) |
173 | expect(subPlaylist).to.contain(basename(file.fileUrl)) | 215 | expect(subPlaylist).to.contain(basename(file.fileUrl)) |
@@ -175,13 +217,19 @@ async function completeCheckHlsPlaylist (options: { | |||
175 | } | 217 | } |
176 | 218 | ||
177 | { | 219 | { |
178 | const baseUrlAndPath = objectStorageBaseUrl | 220 | let baseUrlAndPath: string |
179 | ? objectStorageBaseUrl + 'hls/' + videoUUID | 221 | if (objectStorageBaseUrl && requiresAuth) { |
180 | : baseUrl + '/static/streaming-playlists/hls/' + videoUUID | 222 | baseUrlAndPath = `${baseUrl}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoUUID}` |
223 | } else if (objectStorageBaseUrl) { | ||
224 | baseUrlAndPath = `${objectStorageBaseUrl}hls/${videoUUID}` | ||
225 | } else { | ||
226 | baseUrlAndPath = `${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoUUID}` | ||
227 | } | ||
181 | 228 | ||
182 | for (const resolution of resolutions) { | 229 | for (const resolution of resolutions) { |
183 | await checkSegmentHash({ | 230 | await checkSegmentHash({ |
184 | server, | 231 | server, |
232 | token, | ||
185 | baseUrlPlaylist: baseUrlAndPath, | 233 | baseUrlPlaylist: baseUrlAndPath, |
186 | baseUrlSegment: baseUrlAndPath, | 234 | baseUrlSegment: baseUrlAndPath, |
187 | resolution, | 235 | resolution, |