aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-10-12 16:09:02 +0200
committerChocobozzz <chocobozzz@cpy.re>2022-10-24 14:48:24 +0200
commit3545e72c686ff1725bbdfd8d16d693e2f4aa75a3 (patch)
treee7f1d12ef5dae1e1142c3a8d0b681c1dbbb0de10 /server/tests
parent38a3ccc7f8ad0ea94362b58c732af7c387ab46be (diff)
downloadPeerTube-3545e72c686ff1725bbdfd8d16d693e2f4aa75a3.tar.gz
PeerTube-3545e72c686ff1725bbdfd8d16d693e2f4aa75a3.tar.zst
PeerTube-3545e72c686ff1725bbdfd8d16d693e2f4aa75a3.zip
Put private videos under a specific subdirectory
Diffstat (limited to 'server/tests')
-rw-r--r--server/tests/api/check-params/index.ts1
-rw-r--r--server/tests/api/check-params/live.ts17
-rw-r--r--server/tests/api/check-params/video-files.ts217
-rw-r--r--server/tests/api/check-params/video-token.ts44
-rw-r--r--server/tests/api/live/live-fast-restream.ts4
-rw-r--r--server/tests/api/live/live.ts13
-rw-r--r--server/tests/api/object-storage/live.ts2
-rw-r--r--server/tests/api/object-storage/video-imports.ts6
-rw-r--r--server/tests/api/object-storage/videos.ts20
-rw-r--r--server/tests/api/redundancy/redundancy.ts2
-rw-r--r--server/tests/api/server/open-telemetry.ts6
-rw-r--r--server/tests/api/transcoding/create-transcoding.ts10
-rw-r--r--server/tests/api/transcoding/hls.ts163
-rw-r--r--server/tests/api/transcoding/index.ts1
-rw-r--r--server/tests/api/transcoding/update-while-transcoding.ts151
-rw-r--r--server/tests/api/videos/index.ts1
-rw-r--r--server/tests/api/videos/video-files.ts2
-rw-r--r--server/tests/api/videos/video-static-file-privacy.ts389
-rw-r--r--server/tests/cli/create-import-video-file-job.ts2
-rw-r--r--server/tests/cli/create-move-video-storage-job.ts4
-rw-r--r--server/tests/cli/create-transcoding-job.ts2
-rw-r--r--server/tests/cli/prune-storage.ts41
-rw-r--r--server/tests/cli/regenerate-thumbnails.ts20
-rw-r--r--server/tests/feeds/feeds.ts2
-rw-r--r--server/tests/plugins/filter-hooks.ts36
-rw-r--r--server/tests/plugins/plugin-helpers.ts6
-rw-r--r--server/tests/shared/streaming-playlists.ts122
-rw-r--r--server/tests/shared/videos.ts6
28 files changed, 977 insertions, 313 deletions
diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts
index 33dc8fb76..961093bb5 100644
--- a/server/tests/api/check-params/index.ts
+++ b/server/tests/api/check-params/index.ts
@@ -34,6 +34,7 @@ import './video-imports'
34import './video-playlists' 34import './video-playlists'
35import './video-source' 35import './video-source'
36import './video-studio' 36import './video-studio'
37import './video-token'
37import './videos-common-filters' 38import './videos-common-filters'
38import './videos-history' 39import './videos-history'
39import './videos-overviews' 40import './videos-overviews'
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts
index 3f553c42b..2eff9414b 100644
--- a/server/tests/api/check-params/live.ts
+++ b/server/tests/api/check-params/live.ts
@@ -502,6 +502,23 @@ describe('Test video lives API validator', function () {
502 await stopFfmpeg(ffmpegCommand) 502 await stopFfmpeg(ffmpegCommand)
503 }) 503 })
504 504
505 it('Should fail to change live privacy if it has already started', async function () {
506 this.timeout(40000)
507
508 const live = await command.get({ videoId: video.id })
509
510 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
511
512 await command.waitUntilPublished({ videoId: video.id })
513 await server.videos.update({
514 id: video.id,
515 attributes: { privacy: VideoPrivacy.PUBLIC },
516 expectedStatus: HttpStatusCode.BAD_REQUEST_400
517 })
518
519 await stopFfmpeg(ffmpegCommand)
520 })
521
505 it('Should fail to stream twice in the save live', async function () { 522 it('Should fail to stream twice in the save live', async function () {
506 this.timeout(40000) 523 this.timeout(40000)
507 524
diff --git a/server/tests/api/check-params/video-files.ts b/server/tests/api/check-params/video-files.ts
index aa4de2c83..9dc59a1b5 100644
--- a/server/tests/api/check-params/video-files.ts
+++ b/server/tests/api/check-params/video-files.ts
@@ -1,10 +1,12 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import { HttpStatusCode, UserRole } from '@shared/models' 3import { getAllFiles } from '@shared/core-utils'
4import { HttpStatusCode, UserRole, VideoDetails, VideoPrivacy } from '@shared/models'
4import { 5import {
5 cleanupTests, 6 cleanupTests,
6 createMultipleServers, 7 createMultipleServers,
7 doubleFollow, 8 doubleFollow,
9 makeRawRequest,
8 PeerTubeServer, 10 PeerTubeServer,
9 setAccessTokensToServers, 11 setAccessTokensToServers,
10 waitJobs 12 waitJobs
@@ -13,22 +15,9 @@ import {
13describe('Test videos files', function () { 15describe('Test videos files', function () {
14 let servers: PeerTubeServer[] 16 let servers: PeerTubeServer[]
15 17
16 let webtorrentId: string
17 let hlsId: string
18 let remoteId: string
19
20 let userToken: string 18 let userToken: string
21 let moderatorToken: string 19 let moderatorToken: string
22 20
23 let validId1: string
24 let validId2: string
25
26 let hlsFileId: number
27 let webtorrentFileId: number
28
29 let remoteHLSFileId: number
30 let remoteWebtorrentFileId: number
31
32 // --------------------------------------------------------------- 21 // ---------------------------------------------------------------
33 22
34 before(async function () { 23 before(async function () {
@@ -41,117 +30,163 @@ describe('Test videos files', function () {
41 30
42 userToken = await servers[0].users.generateUserAndToken('user', UserRole.USER) 31 userToken = await servers[0].users.generateUserAndToken('user', UserRole.USER)
43 moderatorToken = await servers[0].users.generateUserAndToken('moderator', UserRole.MODERATOR) 32 moderatorToken = await servers[0].users.generateUserAndToken('moderator', UserRole.MODERATOR)
33 })
44 34
45 { 35 describe('Getting metadata', function () {
46 const { uuid } = await servers[1].videos.quickUpload({ name: 'remote video' }) 36 let video: VideoDetails
47 await waitJobs(servers) 37
38 before(async function () {
39 const { uuid } = await servers[0].videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
40 video = await servers[0].videos.getWithToken({ id: uuid })
41 })
42
43 it('Should not get metadata of private video without token', async function () {
44 for (const file of getAllFiles(video)) {
45 await makeRawRequest({ url: file.metadataUrl, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
46 }
47 })
48
49 it('Should not get metadata of private video without the appropriate token', async function () {
50 for (const file of getAllFiles(video)) {
51 await makeRawRequest({ url: file.metadataUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
52 }
53 })
54
55 it('Should get metadata of private video with the appropriate token', async function () {
56 for (const file of getAllFiles(video)) {
57 await makeRawRequest({ url: file.metadataUrl, token: servers[0].accessToken, expectedStatus: HttpStatusCode.OK_200 })
58 }
59 })
60 })
61
62 describe('Deleting files', function () {
63 let webtorrentId: string
64 let hlsId: string
65 let remoteId: string
66
67 let validId1: string
68 let validId2: string
48 69
49 const video = await servers[1].videos.get({ id: uuid }) 70 let hlsFileId: number
50 remoteId = video.uuid 71 let webtorrentFileId: number
51 remoteHLSFileId = video.streamingPlaylists[0].files[0].id
52 remoteWebtorrentFileId = video.files[0].id
53 }
54 72
55 { 73 let remoteHLSFileId: number
56 await servers[0].config.enableTranscoding(true, true) 74 let remoteWebtorrentFileId: number
75
76 before(async function () {
77 this.timeout(300_000)
57 78
58 { 79 {
59 const { uuid } = await servers[0].videos.quickUpload({ name: 'both 1' }) 80 const { uuid } = await servers[1].videos.quickUpload({ name: 'remote video' })
60 await waitJobs(servers) 81 await waitJobs(servers)
61 82
62 const video = await servers[0].videos.get({ id: uuid }) 83 const video = await servers[1].videos.get({ id: uuid })
63 validId1 = video.uuid 84 remoteId = video.uuid
64 hlsFileId = video.streamingPlaylists[0].files[0].id 85 remoteHLSFileId = video.streamingPlaylists[0].files[0].id
65 webtorrentFileId = video.files[0].id 86 remoteWebtorrentFileId = video.files[0].id
66 } 87 }
67 88
68 { 89 {
69 const { uuid } = await servers[0].videos.quickUpload({ name: 'both 2' }) 90 await servers[0].config.enableTranscoding(true, true)
70 validId2 = uuid 91
92 {
93 const { uuid } = await servers[0].videos.quickUpload({ name: 'both 1' })
94 await waitJobs(servers)
95
96 const video = await servers[0].videos.get({ id: uuid })
97 validId1 = video.uuid
98 hlsFileId = video.streamingPlaylists[0].files[0].id
99 webtorrentFileId = video.files[0].id
100 }
101
102 {
103 const { uuid } = await servers[0].videos.quickUpload({ name: 'both 2' })
104 validId2 = uuid
105 }
71 } 106 }
72 }
73 107
74 await waitJobs(servers) 108 await waitJobs(servers)
75 109
76 { 110 {
77 await servers[0].config.enableTranscoding(false, true) 111 await servers[0].config.enableTranscoding(false, true)
78 const { uuid } = await servers[0].videos.quickUpload({ name: 'hls' }) 112 const { uuid } = await servers[0].videos.quickUpload({ name: 'hls' })
79 hlsId = uuid 113 hlsId = uuid
80 } 114 }
81 115
82 await waitJobs(servers) 116 await waitJobs(servers)
83 117
84 { 118 {
85 await servers[0].config.enableTranscoding(false, true) 119 await servers[0].config.enableTranscoding(false, true)
86 const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' }) 120 const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' })
87 webtorrentId = uuid 121 webtorrentId = uuid
88 } 122 }
89 123
90 await waitJobs(servers) 124 await waitJobs(servers)
91 }) 125 })
92 126
93 it('Should not delete files of a unknown video', async function () { 127 it('Should not delete files of a unknown video', async function () {
94 const expectedStatus = HttpStatusCode.NOT_FOUND_404 128 const expectedStatus = HttpStatusCode.NOT_FOUND_404
95 129
96 await servers[0].videos.removeHLSPlaylist({ videoId: 404, expectedStatus }) 130 await servers[0].videos.removeHLSPlaylist({ videoId: 404, expectedStatus })
97 await servers[0].videos.removeAllWebTorrentFiles({ videoId: 404, expectedStatus }) 131 await servers[0].videos.removeAllWebTorrentFiles({ videoId: 404, expectedStatus })
98 132
99 await servers[0].videos.removeHLSFile({ videoId: 404, fileId: hlsFileId, expectedStatus }) 133 await servers[0].videos.removeHLSFile({ videoId: 404, fileId: hlsFileId, expectedStatus })
100 await servers[0].videos.removeWebTorrentFile({ videoId: 404, fileId: webtorrentFileId, expectedStatus }) 134 await servers[0].videos.removeWebTorrentFile({ videoId: 404, fileId: webtorrentFileId, expectedStatus })
101 }) 135 })
102 136
103 it('Should not delete unknown files', async function () { 137 it('Should not delete unknown files', async function () {
104 const expectedStatus = HttpStatusCode.NOT_FOUND_404 138 const expectedStatus = HttpStatusCode.NOT_FOUND_404
105 139
106 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: webtorrentFileId, expectedStatus }) 140 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: webtorrentFileId, expectedStatus })
107 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: hlsFileId, expectedStatus }) 141 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: hlsFileId, expectedStatus })
108 }) 142 })
109 143
110 it('Should not delete files of a remote video', async function () { 144 it('Should not delete files of a remote video', async function () {
111 const expectedStatus = HttpStatusCode.BAD_REQUEST_400 145 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
112 146
113 await servers[0].videos.removeHLSPlaylist({ videoId: remoteId, expectedStatus }) 147 await servers[0].videos.removeHLSPlaylist({ videoId: remoteId, expectedStatus })
114 await servers[0].videos.removeAllWebTorrentFiles({ videoId: remoteId, expectedStatus }) 148 await servers[0].videos.removeAllWebTorrentFiles({ videoId: remoteId, expectedStatus })
115 149
116 await servers[0].videos.removeHLSFile({ videoId: remoteId, fileId: remoteHLSFileId, expectedStatus }) 150 await servers[0].videos.removeHLSFile({ videoId: remoteId, fileId: remoteHLSFileId, expectedStatus })
117 await servers[0].videos.removeWebTorrentFile({ videoId: remoteId, fileId: remoteWebtorrentFileId, expectedStatus }) 151 await servers[0].videos.removeWebTorrentFile({ videoId: remoteId, fileId: remoteWebtorrentFileId, expectedStatus })
118 }) 152 })
119 153
120 it('Should not delete files by a non admin user', async function () { 154 it('Should not delete files by a non admin user', async function () {
121 const expectedStatus = HttpStatusCode.FORBIDDEN_403 155 const expectedStatus = HttpStatusCode.FORBIDDEN_403
122 156
123 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: userToken, expectedStatus }) 157 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: userToken, expectedStatus })
124 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: moderatorToken, expectedStatus }) 158 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: moderatorToken, expectedStatus })
125 159
126 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: userToken, expectedStatus }) 160 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: userToken, expectedStatus })
127 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: moderatorToken, expectedStatus }) 161 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: moderatorToken, expectedStatus })
128 162
129 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: userToken, expectedStatus }) 163 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: userToken, expectedStatus })
130 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: moderatorToken, expectedStatus }) 164 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: moderatorToken, expectedStatus })
131 165
132 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: userToken, expectedStatus }) 166 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: userToken, expectedStatus })
133 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: moderatorToken, expectedStatus }) 167 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: moderatorToken, expectedStatus })
134 }) 168 })
135 169
136 it('Should not delete files if the files are not available', async function () { 170 it('Should not delete files if the files are not available', async function () {
137 await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 171 await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
138 await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 172 await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
139 173
140 await servers[0].videos.removeHLSFile({ videoId: hlsId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 174 await servers[0].videos.removeHLSFile({ videoId: hlsId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
141 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 175 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
142 }) 176 })
143 177
144 it('Should not delete files if no both versions are available', async function () { 178 it('Should not delete files if no both versions are available', async function () {
145 await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 179 await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
146 await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 180 await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
147 }) 181 })
148 182
149 it('Should delete files if both versions are available', async function () { 183 it('Should delete files if both versions are available', async function () {
150 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId }) 184 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId })
151 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId }) 185 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId })
152 186
153 await servers[0].videos.removeHLSPlaylist({ videoId: validId1 }) 187 await servers[0].videos.removeHLSPlaylist({ videoId: validId1 })
154 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId2 }) 188 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId2 })
189 })
155 }) 190 })
156 191
157 after(async function () { 192 after(async function () {
diff --git a/server/tests/api/check-params/video-token.ts b/server/tests/api/check-params/video-token.ts
new file mode 100644
index 000000000..7acb9d580
--- /dev/null
+++ b/server/tests/api/check-params/video-token.ts
@@ -0,0 +1,44 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { HttpStatusCode, VideoPrivacy } from '@shared/models'
4import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServers } from '@shared/server-commands'
5
6describe('Test video tokens', function () {
7 let server: PeerTubeServer
8 let videoId: string
9 let userToken: string
10
11 // ---------------------------------------------------------------
12
13 before(async function () {
14 this.timeout(300_000)
15
16 server = await createSingleServer(1)
17 await setAccessTokensToServers([ server ])
18
19 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
20 videoId = uuid
21
22 userToken = await server.users.generateUserAndToken('user1')
23 })
24
25 it('Should not generate tokens for unauthenticated user', async function () {
26 await server.videoToken.create({ videoId, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
27 })
28
29 it('Should not generate tokens of unknown video', async function () {
30 await server.videoToken.create({ videoId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
31 })
32
33 it('Should not generate tokens of a non owned video', async function () {
34 await server.videoToken.create({ videoId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
35 })
36
37 it('Should generate token', async function () {
38 await server.videoToken.create({ videoId })
39 })
40
41 after(async function () {
42 await cleanupTests([ server ])
43 })
44})
diff --git a/server/tests/api/live/live-fast-restream.ts b/server/tests/api/live/live-fast-restream.ts
index 772ea792d..971df1a61 100644
--- a/server/tests/api/live/live-fast-restream.ts
+++ b/server/tests/api/live/live-fast-restream.ts
@@ -79,8 +79,8 @@ describe('Fast restream in live', function () {
79 expect(video.streamingPlaylists).to.have.lengthOf(1) 79 expect(video.streamingPlaylists).to.have.lengthOf(1)
80 80
81 await server.live.getSegmentFile({ videoUUID: liveId, segment: 0, playlistNumber: 0 }) 81 await server.live.getSegmentFile({ videoUUID: liveId, segment: 0, playlistNumber: 0 })
82 await makeRawRequest(video.streamingPlaylists[0].playlistUrl, HttpStatusCode.OK_200) 82 await makeRawRequest({ url: video.streamingPlaylists[0].playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
83 await makeRawRequest(video.streamingPlaylists[0].segmentsSha256Url, HttpStatusCode.OK_200) 83 await makeRawRequest({ url: video.streamingPlaylists[0].segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
84 84
85 await wait(100) 85 await wait(100)
86 } 86 }
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 3f2a304be..003cc934f 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -21,6 +21,7 @@ import {
21 doubleFollow, 21 doubleFollow,
22 killallServers, 22 killallServers,
23 LiveCommand, 23 LiveCommand,
24 makeGetRequest,
24 makeRawRequest, 25 makeRawRequest,
25 PeerTubeServer, 26 PeerTubeServer,
26 sendRTMPStream, 27 sendRTMPStream,
@@ -157,8 +158,8 @@ describe('Test live', function () {
157 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED) 158 expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
158 expect(video.nsfw).to.be.true 159 expect(video.nsfw).to.be.true
159 160
160 await makeRawRequest(server.url + video.thumbnailPath, HttpStatusCode.OK_200) 161 await makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
161 await makeRawRequest(server.url + video.previewPath, HttpStatusCode.OK_200) 162 await makeGetRequest({ url: server.url, path: video.previewPath, expectedStatus: HttpStatusCode.OK_200 })
162 } 163 }
163 }) 164 })
164 165
@@ -532,8 +533,8 @@ describe('Test live', function () {
532 expect(video.files).to.have.lengthOf(0) 533 expect(video.files).to.have.lengthOf(0)
533 534
534 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS) 535 const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
535 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200) 536 await makeRawRequest({ url: hlsPlaylist.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
536 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200) 537 await makeRawRequest({ url: hlsPlaylist.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
537 538
538 // We should have generated random filenames 539 // We should have generated random filenames
539 expect(basename(hlsPlaylist.playlistUrl)).to.not.equal('master.m3u8') 540 expect(basename(hlsPlaylist.playlistUrl)).to.not.equal('master.m3u8')
@@ -564,8 +565,8 @@ describe('Test live', function () {
564 expect(probe.format.bit_rate).to.be.below(maxBitrateLimits[videoStream.height]) 565 expect(probe.format.bit_rate).to.be.below(maxBitrateLimits[videoStream.height])
565 expect(probe.format.bit_rate).to.be.at.least(minBitrateLimits[videoStream.height]) 566 expect(probe.format.bit_rate).to.be.at.least(minBitrateLimits[videoStream.height])
566 567
567 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200) 568 await makeRawRequest({ url: file.torrentUrl, expectedStatus: HttpStatusCode.OK_200 })
568 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 569 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
569 } 570 }
570 } 571 }
571 }) 572 })
diff --git a/server/tests/api/object-storage/live.ts b/server/tests/api/object-storage/live.ts
index 7e16b4c89..77f3a8066 100644
--- a/server/tests/api/object-storage/live.ts
+++ b/server/tests/api/object-storage/live.ts
@@ -48,7 +48,7 @@ async function checkFilesExist (servers: PeerTubeServer[], videoUUID: string, nu
48 for (const file of files) { 48 for (const file of files) {
49 expectStartWith(file.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl()) 49 expectStartWith(file.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl())
50 50
51 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 51 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
52 } 52 }
53 } 53 }
54} 54}
diff --git a/server/tests/api/object-storage/video-imports.ts b/server/tests/api/object-storage/video-imports.ts
index f688c7018..90988ea9a 100644
--- a/server/tests/api/object-storage/video-imports.ts
+++ b/server/tests/api/object-storage/video-imports.ts
@@ -66,7 +66,7 @@ describe('Object storage for video import', function () {
66 const fileUrl = video.files[0].fileUrl 66 const fileUrl = video.files[0].fileUrl
67 expectStartWith(fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl()) 67 expectStartWith(fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
68 68
69 await makeRawRequest(fileUrl, HttpStatusCode.OK_200) 69 await makeRawRequest({ url: fileUrl, expectedStatus: HttpStatusCode.OK_200 })
70 }) 70 })
71 }) 71 })
72 72
@@ -91,13 +91,13 @@ describe('Object storage for video import', function () {
91 for (const file of video.files) { 91 for (const file of video.files) {
92 expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl()) 92 expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
93 93
94 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 94 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
95 } 95 }
96 96
97 for (const file of video.streamingPlaylists[0].files) { 97 for (const file of video.streamingPlaylists[0].files) {
98 expectStartWith(file.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl()) 98 expectStartWith(file.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl())
99 99
100 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 100 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
101 } 101 }
102 }) 102 })
103 }) 103 })
diff --git a/server/tests/api/object-storage/videos.ts b/server/tests/api/object-storage/videos.ts
index 3e65e1093..63f5179c7 100644
--- a/server/tests/api/object-storage/videos.ts
+++ b/server/tests/api/object-storage/videos.ts
@@ -59,11 +59,11 @@ async function checkFiles (options: {
59 59
60 expectStartWith(file.fileUrl, start) 60 expectStartWith(file.fileUrl, start)
61 61
62 const res = await makeRawRequest(file.fileDownloadUrl, HttpStatusCode.FOUND_302) 62 const res = await makeRawRequest({ url: file.fileDownloadUrl, expectedStatus: HttpStatusCode.FOUND_302 })
63 const location = res.headers['location'] 63 const location = res.headers['location']
64 expectStartWith(location, start) 64 expectStartWith(location, start)
65 65
66 await makeRawRequest(location, HttpStatusCode.OK_200) 66 await makeRawRequest({ url: location, expectedStatus: HttpStatusCode.OK_200 })
67 } 67 }
68 68
69 const hls = video.streamingPlaylists[0] 69 const hls = video.streamingPlaylists[0]
@@ -81,19 +81,19 @@ async function checkFiles (options: {
81 expectStartWith(hls.playlistUrl, start) 81 expectStartWith(hls.playlistUrl, start)
82 expectStartWith(hls.segmentsSha256Url, start) 82 expectStartWith(hls.segmentsSha256Url, start)
83 83
84 await makeRawRequest(hls.playlistUrl, HttpStatusCode.OK_200) 84 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
85 85
86 const resSha = await makeRawRequest(hls.segmentsSha256Url, HttpStatusCode.OK_200) 86 const resSha = await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
87 expect(JSON.stringify(resSha.body)).to.not.throw 87 expect(JSON.stringify(resSha.body)).to.not.throw
88 88
89 for (const file of hls.files) { 89 for (const file of hls.files) {
90 expectStartWith(file.fileUrl, start) 90 expectStartWith(file.fileUrl, start)
91 91
92 const res = await makeRawRequest(file.fileDownloadUrl, HttpStatusCode.FOUND_302) 92 const res = await makeRawRequest({ url: file.fileDownloadUrl, expectedStatus: HttpStatusCode.FOUND_302 })
93 const location = res.headers['location'] 93 const location = res.headers['location']
94 expectStartWith(location, start) 94 expectStartWith(location, start)
95 95
96 await makeRawRequest(location, HttpStatusCode.OK_200) 96 await makeRawRequest({ url: location, expectedStatus: HttpStatusCode.OK_200 })
97 } 97 }
98 } 98 }
99 99
@@ -104,7 +104,7 @@ async function checkFiles (options: {
104 expect(torrent.files.length).to.equal(1) 104 expect(torrent.files.length).to.equal(1)
105 expect(torrent.files[0].path).to.exist.and.to.not.equal('') 105 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
106 106
107 const res = await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 107 const res = await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
108 expect(res.body).to.have.length.above(100) 108 expect(res.body).to.have.length.above(100)
109 } 109 }
110 110
@@ -220,7 +220,7 @@ function runTestSuite (options: {
220 220
221 it('Should fetch correctly all the files', async function () { 221 it('Should fetch correctly all the files', async function () {
222 for (const url of deletedUrls.concat(keptUrls)) { 222 for (const url of deletedUrls.concat(keptUrls)) {
223 await makeRawRequest(url, HttpStatusCode.OK_200) 223 await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
224 } 224 }
225 }) 225 })
226 226
@@ -231,13 +231,13 @@ function runTestSuite (options: {
231 await waitJobs(servers) 231 await waitJobs(servers)
232 232
233 for (const url of deletedUrls) { 233 for (const url of deletedUrls) {
234 await makeRawRequest(url, HttpStatusCode.NOT_FOUND_404) 234 await makeRawRequest({ url, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
235 } 235 }
236 }) 236 })
237 237
238 it('Should have kept other files', async function () { 238 it('Should have kept other files', async function () {
239 for (const url of keptUrls) { 239 for (const url of keptUrls) {
240 await makeRawRequest(url, HttpStatusCode.OK_200) 240 await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
241 } 241 }
242 }) 242 })
243 243
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts
index f349a7a76..ba6b00e0b 100644
--- a/server/tests/api/redundancy/redundancy.ts
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -39,7 +39,7 @@ async function checkMagnetWebseeds (file: VideoFile, baseWebseeds: string[], ser
39 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length) 39 expect(parsed.urlList).to.have.lengthOf(baseWebseeds.length)
40 40
41 for (const url of parsed.urlList) { 41 for (const url of parsed.urlList) {
42 await makeRawRequest(url, HttpStatusCode.OK_200) 42 await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
43 } 43 }
44} 44}
45 45
diff --git a/server/tests/api/server/open-telemetry.ts b/server/tests/api/server/open-telemetry.ts
index 43a27cc32..7a294be82 100644
--- a/server/tests/api/server/open-telemetry.ts
+++ b/server/tests/api/server/open-telemetry.ts
@@ -18,7 +18,7 @@ describe('Open Telemetry', function () {
18 18
19 let hasError = false 19 let hasError = false
20 try { 20 try {
21 await makeRawRequest(metricsUrl, HttpStatusCode.NOT_FOUND_404) 21 await makeRawRequest({ url: metricsUrl, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
22 } catch (err) { 22 } catch (err) {
23 hasError = err.message.includes('ECONNREFUSED') 23 hasError = err.message.includes('ECONNREFUSED')
24 } 24 }
@@ -37,7 +37,7 @@ describe('Open Telemetry', function () {
37 } 37 }
38 }) 38 })
39 39
40 const res = await makeRawRequest(metricsUrl, HttpStatusCode.OK_200) 40 const res = await makeRawRequest({ url: metricsUrl, expectedStatus: HttpStatusCode.OK_200 })
41 expect(res.text).to.contain('peertube_job_queue_total{') 41 expect(res.text).to.contain('peertube_job_queue_total{')
42 }) 42 })
43 43
@@ -60,7 +60,7 @@ describe('Open Telemetry', function () {
60 } 60 }
61 }) 61 })
62 62
63 const res = await makeRawRequest(metricsUrl, HttpStatusCode.OK_200) 63 const res = await makeRawRequest({ url: metricsUrl, expectedStatus: HttpStatusCode.OK_200 })
64 expect(res.text).to.contain('peertube_playback_http_downloaded_bytes_total{') 64 expect(res.text).to.contain('peertube_playback_http_downloaded_bytes_total{')
65 }) 65 })
66 66
diff --git a/server/tests/api/transcoding/create-transcoding.ts b/server/tests/api/transcoding/create-transcoding.ts
index a50bf7654..372f5332a 100644
--- a/server/tests/api/transcoding/create-transcoding.ts
+++ b/server/tests/api/transcoding/create-transcoding.ts
@@ -20,7 +20,7 @@ import {
20async function checkFilesInObjectStorage (video: VideoDetails) { 20async function checkFilesInObjectStorage (video: VideoDetails) {
21 for (const file of video.files) { 21 for (const file of video.files) {
22 expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl()) 22 expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
23 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 23 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
24 } 24 }
25 25
26 if (video.streamingPlaylists.length === 0) return 26 if (video.streamingPlaylists.length === 0) return
@@ -28,14 +28,14 @@ async function checkFilesInObjectStorage (video: VideoDetails) {
28 const hlsPlaylist = video.streamingPlaylists[0] 28 const hlsPlaylist = video.streamingPlaylists[0]
29 for (const file of hlsPlaylist.files) { 29 for (const file of hlsPlaylist.files) {
30 expectStartWith(file.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl()) 30 expectStartWith(file.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl())
31 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 31 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
32 } 32 }
33 33
34 expectStartWith(hlsPlaylist.playlistUrl, ObjectStorageCommand.getPlaylistBaseUrl()) 34 expectStartWith(hlsPlaylist.playlistUrl, ObjectStorageCommand.getPlaylistBaseUrl())
35 await makeRawRequest(hlsPlaylist.playlistUrl, HttpStatusCode.OK_200) 35 await makeRawRequest({ url: hlsPlaylist.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
36 36
37 expectStartWith(hlsPlaylist.segmentsSha256Url, ObjectStorageCommand.getPlaylistBaseUrl()) 37 expectStartWith(hlsPlaylist.segmentsSha256Url, ObjectStorageCommand.getPlaylistBaseUrl())
38 await makeRawRequest(hlsPlaylist.segmentsSha256Url, HttpStatusCode.OK_200) 38 await makeRawRequest({ url: hlsPlaylist.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
39} 39}
40 40
41function runTests (objectStorage: boolean) { 41function runTests (objectStorage: boolean) {
@@ -234,7 +234,7 @@ function runTests (objectStorage: boolean) {
234 234
235 it('Should have correctly deleted previous files', async function () { 235 it('Should have correctly deleted previous files', async function () {
236 for (const fileUrl of shouldBeDeleted) { 236 for (const fileUrl of shouldBeDeleted) {
237 await makeRawRequest(fileUrl, HttpStatusCode.NOT_FOUND_404) 237 await makeRawRequest({ url: fileUrl, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
238 } 238 }
239 }) 239 })
240 240
diff --git a/server/tests/api/transcoding/hls.ts b/server/tests/api/transcoding/hls.ts
index 252422e5d..7b5492cd4 100644
--- a/server/tests/api/transcoding/hls.ts
+++ b/server/tests/api/transcoding/hls.ts
@@ -1,168 +1,48 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2 2
3import { expect } from 'chai' 3import { join } from 'path'
4import { basename, join } from 'path' 4import { checkDirectoryIsEmpty, checkTmpIsEmpty, completeCheckHlsPlaylist } from '@server/tests/shared'
5import { 5import { areObjectStorageTestsDisabled } from '@shared/core-utils'
6 checkDirectoryIsEmpty, 6import { HttpStatusCode } from '@shared/models'
7 checkResolutionsInMasterPlaylist,
8 checkSegmentHash,
9 checkTmpIsEmpty,
10 expectStartWith,
11 hlsInfohashExist
12} from '@server/tests/shared'
13import { areObjectStorageTestsDisabled, removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
14import { HttpStatusCode, VideoStreamingPlaylistType } from '@shared/models'
15import { 7import {
16 cleanupTests, 8 cleanupTests,
17 createMultipleServers, 9 createMultipleServers,
18 doubleFollow, 10 doubleFollow,
19 makeRawRequest,
20 ObjectStorageCommand, 11 ObjectStorageCommand,
21 PeerTubeServer, 12 PeerTubeServer,
22 setAccessTokensToServers, 13 setAccessTokensToServers,
23 waitJobs, 14 waitJobs
24 webtorrentAdd
25} from '@shared/server-commands' 15} from '@shared/server-commands'
26import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants' 16import { DEFAULT_AUDIO_RESOLUTION } from '../../../initializers/constants'
27 17
28async function checkHlsPlaylist (options: {
29 servers: PeerTubeServer[]
30 videoUUID: string
31 hlsOnly: boolean
32
33 resolutions?: number[]
34 objectStorageBaseUrl: string
35}) {
36 const { videoUUID, hlsOnly, objectStorageBaseUrl } = options
37
38 const resolutions = options.resolutions ?? [ 240, 360, 480, 720 ]
39
40 for (const server of options.servers) {
41 const videoDetails = await server.videos.get({ id: videoUUID })
42 const baseUrl = `http://${videoDetails.account.host}`
43
44 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
45
46 const hlsPlaylist = videoDetails.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
47 expect(hlsPlaylist).to.not.be.undefined
48
49 const hlsFiles = hlsPlaylist.files
50 expect(hlsFiles).to.have.lengthOf(resolutions.length)
51
52 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0)
53 else expect(videoDetails.files).to.have.lengthOf(resolutions.length)
54
55 // Check JSON files
56 for (const resolution of resolutions) {
57 const file = hlsFiles.find(f => f.resolution.id === resolution)
58 expect(file).to.not.be.undefined
59
60 expect(file.magnetUri).to.have.lengthOf.above(2)
61 expect(file.torrentUrl).to.match(
62 new RegExp(`http://${server.host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
63 )
64
65 if (objectStorageBaseUrl) {
66 expectStartWith(file.fileUrl, objectStorageBaseUrl)
67 } else {
68 expect(file.fileUrl).to.match(
69 new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`)
70 )
71 }
72
73 expect(file.resolution.label).to.equal(resolution + 'p')
74
75 await makeRawRequest(file.torrentUrl, HttpStatusCode.OK_200)
76 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200)
77
78 const torrent = await webtorrentAdd(file.magnetUri, true)
79 expect(torrent.files).to.be.an('array')
80 expect(torrent.files.length).to.equal(1)
81 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
82 }
83
84 // Check master playlist
85 {
86 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
87
88 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
89
90 let i = 0
91 for (const resolution of resolutions) {
92 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
93 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
94
95 const url = 'http://' + videoDetails.account.host
96 await hlsInfohashExist(url, hlsPlaylist.playlistUrl, i)
97
98 i++
99 }
100 }
101
102 // Check resolution playlists
103 {
104 for (const resolution of resolutions) {
105 const file = hlsFiles.find(f => f.resolution.id === resolution)
106 const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
107
108 const url = objectStorageBaseUrl
109 ? `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}`
110 : `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
111
112 const subPlaylist = await server.streamingPlaylists.get({ url })
113
114 expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
115 expect(subPlaylist).to.contain(basename(file.fileUrl))
116 }
117 }
118
119 {
120 const baseUrlAndPath = objectStorageBaseUrl
121 ? objectStorageBaseUrl + 'hls/' + videoUUID
122 : baseUrl + '/static/streaming-playlists/hls/' + videoUUID
123
124 for (const resolution of resolutions) {
125 await checkSegmentHash({
126 server,
127 baseUrlPlaylist: baseUrlAndPath,
128 baseUrlSegment: baseUrlAndPath,
129 resolution,
130 hlsPlaylist
131 })
132 }
133 }
134 }
135}
136
137describe('Test HLS videos', function () { 18describe('Test HLS videos', function () {
138 let servers: PeerTubeServer[] = [] 19 let servers: PeerTubeServer[] = []
139 let videoUUID = ''
140 let videoAudioUUID = ''
141 20
142 function runTestSuite (hlsOnly: boolean, objectStorageBaseUrl?: string) { 21 function runTestSuite (hlsOnly: boolean, objectStorageBaseUrl?: string) {
22 const videoUUIDs: string[] = []
143 23
144 it('Should upload a video and transcode it to HLS', async function () { 24 it('Should upload a video and transcode it to HLS', async function () {
145 this.timeout(120000) 25 this.timeout(120000)
146 26
147 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } }) 27 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video 1', fixture: 'video_short.webm' } })
148 videoUUID = uuid 28 videoUUIDs.push(uuid)
149 29
150 await waitJobs(servers) 30 await waitJobs(servers)
151 31
152 await checkHlsPlaylist({ servers, videoUUID, hlsOnly, objectStorageBaseUrl }) 32 await completeCheckHlsPlaylist({ servers, videoUUID: uuid, hlsOnly, objectStorageBaseUrl })
153 }) 33 })
154 34
155 it('Should upload an audio file and transcode it to HLS', async function () { 35 it('Should upload an audio file and transcode it to HLS', async function () {
156 this.timeout(120000) 36 this.timeout(120000)
157 37
158 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } }) 38 const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video audio', fixture: 'sample.ogg' } })
159 videoAudioUUID = uuid 39 videoUUIDs.push(uuid)
160 40
161 await waitJobs(servers) 41 await waitJobs(servers)
162 42
163 await checkHlsPlaylist({ 43 await completeCheckHlsPlaylist({
164 servers, 44 servers,
165 videoUUID: videoAudioUUID, 45 videoUUID: uuid,
166 hlsOnly, 46 hlsOnly,
167 resolutions: [ DEFAULT_AUDIO_RESOLUTION, 360, 240 ], 47 resolutions: [ DEFAULT_AUDIO_RESOLUTION, 360, 240 ],
168 objectStorageBaseUrl 48 objectStorageBaseUrl
@@ -172,31 +52,36 @@ describe('Test HLS videos', function () {
172 it('Should update the video', async function () { 52 it('Should update the video', async function () {
173 this.timeout(30000) 53 this.timeout(30000)
174 54
175 await servers[0].videos.update({ id: videoUUID, attributes: { name: 'video 1 updated' } }) 55 await servers[0].videos.update({ id: videoUUIDs[0], attributes: { name: 'video 1 updated' } })
176 56
177 await waitJobs(servers) 57 await waitJobs(servers)
178 58
179 await checkHlsPlaylist({ servers, videoUUID, hlsOnly, objectStorageBaseUrl }) 59 await completeCheckHlsPlaylist({ servers, videoUUID: videoUUIDs[0], hlsOnly, objectStorageBaseUrl })
180 }) 60 })
181 61
182 it('Should delete videos', async function () { 62 it('Should delete videos', async function () {
183 this.timeout(10000) 63 this.timeout(10000)
184 64
185 await servers[0].videos.remove({ id: videoUUID }) 65 for (const uuid of videoUUIDs) {
186 await servers[0].videos.remove({ id: videoAudioUUID }) 66 await servers[0].videos.remove({ id: uuid })
67 }
187 68
188 await waitJobs(servers) 69 await waitJobs(servers)
189 70
190 for (const server of servers) { 71 for (const server of servers) {
191 await server.videos.get({ id: videoUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 72 for (const uuid of videoUUIDs) {
192 await server.videos.get({ id: videoAudioUUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 73 await server.videos.get({ id: uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
74 }
193 } 75 }
194 }) 76 })
195 77
196 it('Should have the playlists/segment deleted from the disk', async function () { 78 it('Should have the playlists/segment deleted from the disk', async function () {
197 for (const server of servers) { 79 for (const server of servers) {
198 await checkDirectoryIsEmpty(server, 'videos') 80 await checkDirectoryIsEmpty(server, 'videos', [ 'private' ])
199 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls')) 81 await checkDirectoryIsEmpty(server, join('videos', 'private'))
82
83 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls'), [ 'private' ])
84 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls', 'private'))
200 } 85 }
201 }) 86 })
202 87
diff --git a/server/tests/api/transcoding/index.ts b/server/tests/api/transcoding/index.ts
index 0cc28b4a4..9866418d6 100644
--- a/server/tests/api/transcoding/index.ts
+++ b/server/tests/api/transcoding/index.ts
@@ -2,4 +2,5 @@ export * from './audio-only'
2export * from './create-transcoding' 2export * from './create-transcoding'
3export * from './hls' 3export * from './hls'
4export * from './transcoder' 4export * from './transcoder'
5export * from './update-while-transcoding'
5export * from './video-studio' 6export * from './video-studio'
diff --git a/server/tests/api/transcoding/update-while-transcoding.ts b/server/tests/api/transcoding/update-while-transcoding.ts
new file mode 100644
index 000000000..5ca923392
--- /dev/null
+++ b/server/tests/api/transcoding/update-while-transcoding.ts
@@ -0,0 +1,151 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { completeCheckHlsPlaylist } from '@server/tests/shared'
4import { areObjectStorageTestsDisabled, wait } from '@shared/core-utils'
5import { VideoPrivacy } from '@shared/models'
6import {
7 cleanupTests,
8 createMultipleServers,
9 doubleFollow,
10 ObjectStorageCommand,
11 PeerTubeServer,
12 setAccessTokensToServers,
13 waitJobs
14} from '@shared/server-commands'
15
16describe('Test update video privacy while transcoding', function () {
17 let servers: PeerTubeServer[] = []
18
19 const videoUUIDs: string[] = []
20
21 function runTestSuite (hlsOnly: boolean, objectStorageBaseUrl?: string) {
22
23 it('Should not have an error while quickly updating a private video to public after upload #1', async function () {
24 this.timeout(360_000)
25
26 const attributes = {
27 name: 'quick update',
28 privacy: VideoPrivacy.PRIVATE
29 }
30
31 const { uuid } = await servers[0].videos.upload({ attributes, waitTorrentGeneration: false })
32 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
33 videoUUIDs.push(uuid)
34
35 await waitJobs(servers)
36
37 await completeCheckHlsPlaylist({ servers, videoUUID: uuid, hlsOnly, objectStorageBaseUrl })
38 })
39
40 it('Should not have an error while quickly updating a private video to public after upload #2', async function () {
41
42 {
43 const attributes = {
44 name: 'quick update 2',
45 privacy: VideoPrivacy.PRIVATE
46 }
47
48 const { uuid } = await servers[0].videos.upload({ attributes, waitTorrentGeneration: true })
49 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
50 videoUUIDs.push(uuid)
51
52 await waitJobs(servers)
53
54 await completeCheckHlsPlaylist({ servers, videoUUID: uuid, hlsOnly, objectStorageBaseUrl })
55 }
56 })
57
58 it('Should not have an error while quickly updating a private video to public after upload #3', async function () {
59 const attributes = {
60 name: 'quick update 3',
61 privacy: VideoPrivacy.PRIVATE
62 }
63
64 const { uuid } = await servers[0].videos.upload({ attributes, waitTorrentGeneration: true })
65 await wait(1000)
66 await servers[0].videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
67 videoUUIDs.push(uuid)
68
69 await waitJobs(servers)
70
71 await completeCheckHlsPlaylist({ servers, videoUUID: uuid, hlsOnly, objectStorageBaseUrl })
72 })
73 }
74
75 before(async function () {
76 this.timeout(120000)
77
78 const configOverride = {
79 transcoding: {
80 enabled: true,
81 allow_audio_files: true,
82 hls: {
83 enabled: true
84 }
85 }
86 }
87 servers = await createMultipleServers(2, configOverride)
88
89 // Get the access tokens
90 await setAccessTokensToServers(servers)
91
92 // Server 1 and server 2 follow each other
93 await doubleFollow(servers[0], servers[1])
94 })
95
96 describe('With WebTorrent & HLS enabled', function () {
97 runTestSuite(false)
98 })
99
100 describe('With only HLS enabled', function () {
101
102 before(async function () {
103 await servers[0].config.updateCustomSubConfig({
104 newConfig: {
105 transcoding: {
106 enabled: true,
107 allowAudioFiles: true,
108 resolutions: {
109 '144p': false,
110 '240p': true,
111 '360p': true,
112 '480p': true,
113 '720p': true,
114 '1080p': true,
115 '1440p': true,
116 '2160p': true
117 },
118 hls: {
119 enabled: true
120 },
121 webtorrent: {
122 enabled: false
123 }
124 }
125 }
126 })
127 })
128
129 runTestSuite(true)
130 })
131
132 describe('With object storage enabled', function () {
133 if (areObjectStorageTestsDisabled()) return
134
135 before(async function () {
136 this.timeout(120000)
137
138 const configOverride = ObjectStorageCommand.getDefaultConfig()
139 await ObjectStorageCommand.prepareDefaultBuckets()
140
141 await servers[0].kill()
142 await servers[0].run(configOverride)
143 })
144
145 runTestSuite(true, ObjectStorageCommand.getPlaylistBaseUrl())
146 })
147
148 after(async function () {
149 await cleanupTests(servers)
150 })
151})
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts
index 266155297..357c08199 100644
--- a/server/tests/api/videos/index.ts
+++ b/server/tests/api/videos/index.ts
@@ -19,3 +19,4 @@ import './videos-common-filters'
19import './videos-history' 19import './videos-history'
20import './videos-overview' 20import './videos-overview'
21import './video-source' 21import './video-source'
22import './video-static-file-privacy'
diff --git a/server/tests/api/videos/video-files.ts b/server/tests/api/videos/video-files.ts
index c0b886aad..8c913bf31 100644
--- a/server/tests/api/videos/video-files.ts
+++ b/server/tests/api/videos/video-files.ts
@@ -153,7 +153,7 @@ describe('Test videos files', function () {
153 expect(video.streamingPlaylists[0].files).to.have.lengthOf(files.length - 1) 153 expect(video.streamingPlaylists[0].files).to.have.lengthOf(files.length - 1)
154 expect(video.streamingPlaylists[0].files.find(f => f.id === toDelete.id)).to.not.exist 154 expect(video.streamingPlaylists[0].files.find(f => f.id === toDelete.id)).to.not.exist
155 155
156 const { text } = await makeRawRequest(video.streamingPlaylists[0].playlistUrl) 156 const { text } = await makeRawRequest({ url: video.streamingPlaylists[0].playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
157 157
158 expect(text.includes(`-${toDelete.resolution.id}.m3u8`)).to.be.false 158 expect(text.includes(`-${toDelete.resolution.id}.m3u8`)).to.be.false
159 expect(text.includes(`-${video.streamingPlaylists[0].files[0].resolution.id}.m3u8`)).to.be.true 159 expect(text.includes(`-${video.streamingPlaylists[0].files[0].resolution.id}.m3u8`)).to.be.true
diff --git a/server/tests/api/videos/video-static-file-privacy.ts b/server/tests/api/videos/video-static-file-privacy.ts
new file mode 100644
index 000000000..e38fdec6e
--- /dev/null
+++ b/server/tests/api/videos/video-static-file-privacy.ts
@@ -0,0 +1,389 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { decode } from 'magnet-uri'
5import { expectStartWith } from '@server/tests/shared'
6import { getAllFiles, wait } from '@shared/core-utils'
7import { HttpStatusCode, LiveVideo, VideoDetails, VideoPrivacy } from '@shared/models'
8import {
9 cleanupTests,
10 createSingleServer,
11 findExternalSavedVideo,
12 makeRawRequest,
13 parseTorrentVideo,
14 PeerTubeServer,
15 sendRTMPStream,
16 setAccessTokensToServers,
17 setDefaultVideoChannel,
18 stopFfmpeg,
19 waitJobs
20} from '@shared/server-commands'
21
22describe('Test video static file privacy', function () {
23 let server: PeerTubeServer
24 let userToken: string
25
26 before(async function () {
27 this.timeout(50000)
28
29 server = await createSingleServer(1)
30 await setAccessTokensToServers([ server ])
31 await setDefaultVideoChannel([ server ])
32
33 userToken = await server.users.generateUserAndToken('user1')
34 })
35
36 describe('VOD static file path', function () {
37
38 function runSuite () {
39
40 async function checkPrivateWebTorrentFiles (uuid: string) {
41 const video = await server.videos.getWithToken({ id: uuid })
42
43 for (const file of video.files) {
44 expect(file.fileDownloadUrl).to.not.include('/private/')
45 expectStartWith(file.fileUrl, server.url + '/static/webseed/private/')
46
47 const torrent = await parseTorrentVideo(server, file)
48 expect(torrent.urlList).to.have.lengthOf(0)
49
50 const magnet = decode(file.magnetUri)
51 expect(magnet.urlList).to.have.lengthOf(0)
52
53 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
54 }
55
56 const hls = video.streamingPlaylists[0]
57 if (hls) {
58 expectStartWith(hls.playlistUrl, server.url + '/static/streaming-playlists/hls/private/')
59 expectStartWith(hls.segmentsSha256Url, server.url + '/static/streaming-playlists/hls/private/')
60
61 await makeRawRequest({ url: hls.playlistUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
62 await makeRawRequest({ url: hls.segmentsSha256Url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
63 }
64 }
65
66 async function checkPublicWebTorrentFiles (uuid: string) {
67 const video = await server.videos.get({ id: uuid })
68
69 for (const file of getAllFiles(video)) {
70 expect(file.fileDownloadUrl).to.not.include('/private/')
71 expect(file.fileUrl).to.not.include('/private/')
72
73 const torrent = await parseTorrentVideo(server, file)
74 expect(torrent.urlList[0]).to.not.include('private')
75
76 const magnet = decode(file.magnetUri)
77 expect(magnet.urlList[0]).to.not.include('private')
78
79 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
80 await makeRawRequest({ url: torrent.urlList[0], expectedStatus: HttpStatusCode.OK_200 })
81 await makeRawRequest({ url: magnet.urlList[0], expectedStatus: HttpStatusCode.OK_200 })
82 }
83
84 const hls = video.streamingPlaylists[0]
85 if (hls) {
86 expect(hls.playlistUrl).to.not.include('private')
87 expect(hls.segmentsSha256Url).to.not.include('private')
88
89 await makeRawRequest({ url: hls.playlistUrl, expectedStatus: HttpStatusCode.OK_200 })
90 await makeRawRequest({ url: hls.segmentsSha256Url, expectedStatus: HttpStatusCode.OK_200 })
91 }
92 }
93
94 it('Should upload a private/internal video and have a private static path', async function () {
95 this.timeout(120000)
96
97 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
98 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy })
99 await waitJobs([ server ])
100
101 await checkPrivateWebTorrentFiles(uuid)
102 }
103 })
104
105 it('Should upload a public video and update it as private/internal to have a private static path', async function () {
106 this.timeout(120000)
107
108 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
109 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PUBLIC })
110 await waitJobs([ server ])
111
112 await server.videos.update({ id: uuid, attributes: { privacy } })
113 await waitJobs([ server ])
114
115 await checkPrivateWebTorrentFiles(uuid)
116 }
117 })
118
119 it('Should upload a private video and update it to unlisted to have a public static path', async function () {
120 this.timeout(120000)
121
122 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
123 await waitJobs([ server ])
124
125 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.UNLISTED } })
126 await waitJobs([ server ])
127
128 await checkPublicWebTorrentFiles(uuid)
129 })
130
131 it('Should upload an internal video and update it to public to have a public static path', async function () {
132 this.timeout(120000)
133
134 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
135 await waitJobs([ server ])
136
137 await server.videos.update({ id: uuid, attributes: { privacy: VideoPrivacy.PUBLIC } })
138 await waitJobs([ server ])
139
140 await checkPublicWebTorrentFiles(uuid)
141 })
142
143 it('Should upload an internal video and schedule a public publish', async function () {
144 this.timeout(120000)
145
146 const attributes = {
147 name: 'video',
148 privacy: VideoPrivacy.PRIVATE,
149 scheduleUpdate: {
150 updateAt: new Date(Date.now() + 1000).toISOString(),
151 privacy: VideoPrivacy.PUBLIC as VideoPrivacy.PUBLIC
152 }
153 }
154
155 const { uuid } = await server.videos.upload({ attributes })
156
157 await waitJobs([ server ])
158 await wait(1000)
159 await server.debug.sendCommand({ body: { command: 'process-update-videos-scheduler' } })
160
161 await waitJobs([ server ])
162
163 await checkPublicWebTorrentFiles(uuid)
164 })
165 }
166
167 describe('Without transcoding', function () {
168 runSuite()
169 })
170
171 describe('With transcoding', function () {
172
173 before(async function () {
174 await server.config.enableMinimumTranscoding()
175 })
176
177 runSuite()
178 })
179 })
180
181 describe('VOD static file right check', function () {
182 let unrelatedFileToken: string
183
184 async function checkVideoFiles (options: {
185 id: string
186 expectedStatus: HttpStatusCode
187 token: string
188 videoFileToken: string
189 }) {
190 const { id, expectedStatus, token, videoFileToken } = options
191
192 const video = await server.videos.getWithToken({ id })
193
194 for (const file of getAllFiles(video)) {
195 await makeRawRequest({ url: file.fileUrl, token, expectedStatus })
196 await makeRawRequest({ url: file.fileDownloadUrl, token, expectedStatus })
197
198 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus })
199 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus })
200 }
201
202 const hls = video.streamingPlaylists[0]
203 await makeRawRequest({ url: hls.playlistUrl, token, expectedStatus })
204 await makeRawRequest({ url: hls.segmentsSha256Url, token, expectedStatus })
205
206 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus })
207 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus })
208 }
209
210 before(async function () {
211 await server.config.enableMinimumTranscoding()
212
213 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
214 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
215 })
216
217 it('Should not be able to access a private video files without OAuth token and file token', async function () {
218 this.timeout(120000)
219
220 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL })
221 await waitJobs([ server ])
222
223 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null })
224 })
225
226 it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () {
227 this.timeout(120000)
228
229 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
230 await waitJobs([ server ])
231
232 await checkVideoFiles({
233 id: uuid,
234 expectedStatus: HttpStatusCode.FORBIDDEN_403,
235 token: userToken,
236 videoFileToken: unrelatedFileToken
237 })
238 })
239
240 it('Should be able to access a private video files with appropriate OAuth token or file token', async function () {
241 this.timeout(120000)
242
243 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
244 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
245
246 await waitJobs([ server ])
247
248 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
249 })
250
251 it('Should be able to access a private video of another user with an admin OAuth token or file token', async function () {
252 this.timeout(120000)
253
254 const { uuid } = await server.videos.quickUpload({ name: 'video', token: userToken, privacy: VideoPrivacy.PRIVATE })
255 const videoFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
256
257 await waitJobs([ server ])
258
259 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
260 })
261 })
262
263 describe('Live static file path and check', function () {
264 let normalLiveId: string
265 let normalLive: LiveVideo
266
267 let permanentLiveId: string
268 let permanentLive: LiveVideo
269
270 let unrelatedFileToken: string
271
272 async function checkLiveFiles (live: LiveVideo, liveId: string) {
273 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
274 await server.live.waitUntilPublished({ videoId: liveId })
275
276 const video = await server.videos.getWithToken({ id: liveId })
277 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
278
279 const hls = video.streamingPlaylists[0]
280
281 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
282 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
283
284 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
285 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
286
287 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
288 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
289 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
290 }
291
292 await stopFfmpeg(ffmpegCommand)
293 }
294
295 async function checkReplay (replay: VideoDetails) {
296 const fileToken = await server.videoToken.getVideoFileToken({ videoId: replay.uuid })
297
298 const hls = replay.streamingPlaylists[0]
299 expect(hls.files).to.not.have.lengthOf(0)
300
301 for (const file of hls.files) {
302 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
303 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
304
305 await makeRawRequest({ url: file.fileUrl, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
306 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
307 await makeRawRequest({
308 url: file.fileUrl,
309 query: { videoFileToken: unrelatedFileToken },
310 expectedStatus: HttpStatusCode.FORBIDDEN_403
311 })
312 }
313
314 for (const url of [ hls.playlistUrl, hls.segmentsSha256Url ]) {
315 expectStartWith(url, server.url + '/static/streaming-playlists/hls/private/')
316
317 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
318 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
319
320 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
321 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
322 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
323 }
324 }
325
326 before(async function () {
327 await server.config.enableMinimumTranscoding()
328
329 const { uuid } = await server.videos.quickUpload({ name: 'another video' })
330 unrelatedFileToken = await server.videoToken.getVideoFileToken({ videoId: uuid })
331
332 await server.config.enableLive({
333 allowReplay: true,
334 transcoding: true,
335 resolutions: 'min'
336 })
337
338 {
339 const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: false, privacy: VideoPrivacy.PRIVATE })
340 normalLiveId = video.uuid
341 normalLive = live
342 }
343
344 {
345 const { video, live } = await server.live.quickCreate({ saveReplay: true, permanentLive: true, privacy: VideoPrivacy.PRIVATE })
346 permanentLiveId = video.uuid
347 permanentLive = live
348 }
349 })
350
351 it('Should create a private normal live and have a private static path', async function () {
352 this.timeout(240000)
353
354 await checkLiveFiles(normalLive, normalLiveId)
355 })
356
357 it('Should create a private permanent live and have a private static path', async function () {
358 this.timeout(240000)
359
360 await checkLiveFiles(permanentLive, permanentLiveId)
361 })
362
363 it('Should have created a replay of the normal live with a private static path', async function () {
364 this.timeout(240000)
365
366 await server.live.waitUntilReplacedByReplay({ videoId: normalLiveId })
367
368 const replay = await server.videos.getWithToken({ id: normalLiveId })
369 await checkReplay(replay)
370 })
371
372 it('Should have created a replay of the permanent live with a private static path', async function () {
373 this.timeout(240000)
374
375 await server.live.waitUntilWaiting({ videoId: permanentLiveId })
376 await waitJobs([ server ])
377
378 const live = await server.videos.getWithToken({ id: permanentLiveId })
379 const replayFromList = await findExternalSavedVideo(server, live)
380 const replay = await server.videos.getWithToken({ id: replayFromList.id })
381
382 await checkReplay(replay)
383 })
384 })
385
386 after(async function () {
387 await cleanupTests([ server ])
388 })
389})
diff --git a/server/tests/cli/create-import-video-file-job.ts b/server/tests/cli/create-import-video-file-job.ts
index 2cf2dd8f8..a4aa5f699 100644
--- a/server/tests/cli/create-import-video-file-job.ts
+++ b/server/tests/cli/create-import-video-file-job.ts
@@ -29,7 +29,7 @@ async function checkFiles (video: VideoDetails, objectStorage: boolean) {
29 for (const file of video.files) { 29 for (const file of video.files) {
30 if (objectStorage) expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl()) 30 if (objectStorage) expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
31 31
32 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 32 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
33 } 33 }
34} 34}
35 35
diff --git a/server/tests/cli/create-move-video-storage-job.ts b/server/tests/cli/create-move-video-storage-job.ts
index 6a12a2c6c..ecdd75b76 100644
--- a/server/tests/cli/create-move-video-storage-job.ts
+++ b/server/tests/cli/create-move-video-storage-job.ts
@@ -22,7 +22,7 @@ async function checkFiles (origin: PeerTubeServer, video: VideoDetails, inObject
22 22
23 expectStartWith(file.fileUrl, start) 23 expectStartWith(file.fileUrl, start)
24 24
25 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 25 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
26 } 26 }
27 27
28 const start = inObjectStorage 28 const start = inObjectStorage
@@ -36,7 +36,7 @@ async function checkFiles (origin: PeerTubeServer, video: VideoDetails, inObject
36 for (const file of hls.files) { 36 for (const file of hls.files) {
37 expectStartWith(file.fileUrl, start) 37 expectStartWith(file.fileUrl, start)
38 38
39 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 39 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
40 } 40 }
41} 41}
42 42
diff --git a/server/tests/cli/create-transcoding-job.ts b/server/tests/cli/create-transcoding-job.ts
index 8897d8c23..51bf04a80 100644
--- a/server/tests/cli/create-transcoding-job.ts
+++ b/server/tests/cli/create-transcoding-job.ts
@@ -23,7 +23,7 @@ async function checkFilesInObjectStorage (files: VideoFile[], type: 'webtorrent'
23 23
24 expectStartWith(file.fileUrl, shouldStartWith) 24 expectStartWith(file.fileUrl, shouldStartWith)
25 25
26 await makeRawRequest(file.fileUrl, HttpStatusCode.OK_200) 26 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
27 } 27 }
28} 28}
29 29
diff --git a/server/tests/cli/prune-storage.ts b/server/tests/cli/prune-storage.ts
index a89e17e3c..ba0fa1f86 100644
--- a/server/tests/cli/prune-storage.ts
+++ b/server/tests/cli/prune-storage.ts
@@ -5,7 +5,7 @@ import { createFile, readdir } from 'fs-extra'
5import { join } from 'path' 5import { join } from 'path'
6import { wait } from '@shared/core-utils' 6import { wait } from '@shared/core-utils'
7import { buildUUID } from '@shared/extra-utils' 7import { buildUUID } from '@shared/extra-utils'
8import { HttpStatusCode, VideoPlaylistPrivacy } from '@shared/models' 8import { HttpStatusCode, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models'
9import { 9import {
10 cleanupTests, 10 cleanupTests,
11 CLICommand, 11 CLICommand,
@@ -36,22 +36,28 @@ async function assertNotExists (server: PeerTubeServer, directory: string, subst
36async function assertCountAreOkay (servers: PeerTubeServer[]) { 36async function assertCountAreOkay (servers: PeerTubeServer[]) {
37 for (const server of servers) { 37 for (const server of servers) {
38 const videosCount = await countFiles(server, 'videos') 38 const videosCount = await countFiles(server, 'videos')
39 expect(videosCount).to.equal(8) 39 expect(videosCount).to.equal(9) // 2 videos with 4 resolutions + private directory
40
41 const privateVideosCount = await countFiles(server, 'videos/private')
42 expect(privateVideosCount).to.equal(4)
40 43
41 const torrentsCount = await countFiles(server, 'torrents') 44 const torrentsCount = await countFiles(server, 'torrents')
42 expect(torrentsCount).to.equal(16) 45 expect(torrentsCount).to.equal(24)
43 46
44 const previewsCount = await countFiles(server, 'previews') 47 const previewsCount = await countFiles(server, 'previews')
45 expect(previewsCount).to.equal(2) 48 expect(previewsCount).to.equal(3)
46 49
47 const thumbnailsCount = await countFiles(server, 'thumbnails') 50 const thumbnailsCount = await countFiles(server, 'thumbnails')
48 expect(thumbnailsCount).to.equal(6) 51 expect(thumbnailsCount).to.equal(7) // 3 local videos, 1 local playlist, 2 remotes videos and 1 remote playlist
49 52
50 const avatarsCount = await countFiles(server, 'avatars') 53 const avatarsCount = await countFiles(server, 'avatars')
51 expect(avatarsCount).to.equal(4) 54 expect(avatarsCount).to.equal(4)
52 55
53 const hlsRootCount = await countFiles(server, 'streaming-playlists/hls') 56 const hlsRootCount = await countFiles(server, join('streaming-playlists', 'hls'))
54 expect(hlsRootCount).to.equal(2) 57 expect(hlsRootCount).to.equal(3) // 2 videos + private directory
58
59 const hlsPrivateRootCount = await countFiles(server, join('streaming-playlists', 'hls', 'private'))
60 expect(hlsPrivateRootCount).to.equal(1)
55 } 61 }
56} 62}
57 63
@@ -67,8 +73,10 @@ describe('Test prune storage scripts', function () {
67 await setDefaultVideoChannel(servers) 73 await setDefaultVideoChannel(servers)
68 74
69 for (const server of servers) { 75 for (const server of servers) {
70 await server.videos.upload({ attributes: { name: 'video 1' } }) 76 await server.videos.upload({ attributes: { name: 'video 1', privacy: VideoPrivacy.PUBLIC } })
71 await server.videos.upload({ attributes: { name: 'video 2' } }) 77 await server.videos.upload({ attributes: { name: 'video 2', privacy: VideoPrivacy.PUBLIC } })
78
79 await server.videos.upload({ attributes: { name: 'video 3', privacy: VideoPrivacy.PRIVATE } })
72 80
73 await server.users.updateMyAvatar({ fixture: 'avatar.png' }) 81 await server.users.updateMyAvatar({ fixture: 'avatar.png' })
74 82
@@ -123,13 +131,16 @@ describe('Test prune storage scripts', function () {
123 it('Should create some dirty files', async function () { 131 it('Should create some dirty files', async function () {
124 for (let i = 0; i < 2; i++) { 132 for (let i = 0; i < 2; i++) {
125 { 133 {
126 const base = servers[0].servers.buildDirectory('videos') 134 const basePublic = servers[0].servers.buildDirectory('videos')
135 const basePrivate = servers[0].servers.buildDirectory(join('videos', 'private'))
127 136
128 const n1 = buildUUID() + '.mp4' 137 const n1 = buildUUID() + '.mp4'
129 const n2 = buildUUID() + '.webm' 138 const n2 = buildUUID() + '.webm'
130 139
131 await createFile(join(base, n1)) 140 await createFile(join(basePublic, n1))
132 await createFile(join(base, n2)) 141 await createFile(join(basePublic, n2))
142 await createFile(join(basePrivate, n1))
143 await createFile(join(basePrivate, n2))
133 144
134 badNames['videos'] = [ n1, n2 ] 145 badNames['videos'] = [ n1, n2 ]
135 } 146 }
@@ -184,10 +195,12 @@ describe('Test prune storage scripts', function () {
184 195
185 { 196 {
186 const directory = join('streaming-playlists', 'hls') 197 const directory = join('streaming-playlists', 'hls')
187 const base = servers[0].servers.buildDirectory(directory) 198 const basePublic = servers[0].servers.buildDirectory(directory)
199 const basePrivate = servers[0].servers.buildDirectory(join(directory, 'private'))
188 200
189 const n1 = buildUUID() 201 const n1 = buildUUID()
190 await createFile(join(base, n1)) 202 await createFile(join(basePublic, n1))
203 await createFile(join(basePrivate, n1))
191 badNames[directory] = [ n1 ] 204 badNames[directory] = [ n1 ]
192 } 205 }
193 } 206 }
diff --git a/server/tests/cli/regenerate-thumbnails.ts b/server/tests/cli/regenerate-thumbnails.ts
index f459b11b8..16a8adcda 100644
--- a/server/tests/cli/regenerate-thumbnails.ts
+++ b/server/tests/cli/regenerate-thumbnails.ts
@@ -6,7 +6,7 @@ import {
6 cleanupTests, 6 cleanupTests,
7 createMultipleServers, 7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 makeRawRequest, 9 makeGetRequest,
10 PeerTubeServer, 10 PeerTubeServer,
11 setAccessTokensToServers, 11 setAccessTokensToServers,
12 waitJobs 12 waitJobs
@@ -16,8 +16,8 @@ async function testThumbnail (server: PeerTubeServer, videoId: number | string)
16 const video = await server.videos.get({ id: videoId }) 16 const video = await server.videos.get({ id: videoId })
17 17
18 const requests = [ 18 const requests = [
19 makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200), 19 makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 }),
20 makeRawRequest(join(server.url, video.thumbnailPath), HttpStatusCode.OK_200) 20 makeGetRequest({ url: server.url, path: video.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
21 ] 21 ]
22 22
23 for (const req of requests) { 23 for (const req of requests) {
@@ -69,17 +69,17 @@ describe('Test regenerate thumbnails script', function () {
69 69
70 it('Should have empty thumbnails', async function () { 70 it('Should have empty thumbnails', async function () {
71 { 71 {
72 const res = await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.OK_200) 72 const res = await makeGetRequest({ url: servers[0].url, path: video1.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
73 expect(res.body).to.have.lengthOf(0) 73 expect(res.body).to.have.lengthOf(0)
74 } 74 }
75 75
76 { 76 {
77 const res = await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.OK_200) 77 const res = await makeGetRequest({ url: servers[0].url, path: video2.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
78 expect(res.body).to.not.have.lengthOf(0) 78 expect(res.body).to.not.have.lengthOf(0)
79 } 79 }
80 80
81 { 81 {
82 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200) 82 const res = await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
83 expect(res.body).to.have.lengthOf(0) 83 expect(res.body).to.have.lengthOf(0)
84 } 84 }
85 }) 85 })
@@ -94,21 +94,21 @@ describe('Test regenerate thumbnails script', function () {
94 await testThumbnail(servers[0], video1.uuid) 94 await testThumbnail(servers[0], video1.uuid)
95 await testThumbnail(servers[0], video2.uuid) 95 await testThumbnail(servers[0], video2.uuid)
96 96
97 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200) 97 const res = await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
98 expect(res.body).to.have.lengthOf(0) 98 expect(res.body).to.have.lengthOf(0)
99 }) 99 })
100 100
101 it('Should have deleted old thumbnail files', async function () { 101 it('Should have deleted old thumbnail files', async function () {
102 { 102 {
103 await makeRawRequest(join(servers[0].url, video1.thumbnailPath), HttpStatusCode.NOT_FOUND_404) 103 await makeGetRequest({ url: servers[0].url, path: video1.thumbnailPath, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
104 } 104 }
105 105
106 { 106 {
107 await makeRawRequest(join(servers[0].url, video2.thumbnailPath), HttpStatusCode.NOT_FOUND_404) 107 await makeGetRequest({ url: servers[0].url, path: video2.thumbnailPath, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
108 } 108 }
109 109
110 { 110 {
111 const res = await makeRawRequest(join(servers[0].url, remoteVideo.thumbnailPath), HttpStatusCode.OK_200) 111 const res = await makeGetRequest({ url: servers[0].url, path: remoteVideo.thumbnailPath, expectedStatus: HttpStatusCode.OK_200 })
112 expect(res.body).to.have.lengthOf(0) 112 expect(res.body).to.have.lengthOf(0)
113 } 113 }
114 }) 114 })
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 0ddb641e6..c49175d5e 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -314,7 +314,7 @@ describe('Test syndication feeds', () => {
314 const jsonObj = JSON.parse(json) 314 const jsonObj = JSON.parse(json)
315 const imageUrl = jsonObj.icon 315 const imageUrl = jsonObj.icon
316 expect(imageUrl).to.include('/lazy-static/avatars/') 316 expect(imageUrl).to.include('/lazy-static/avatars/')
317 await makeRawRequest(imageUrl) 317 await makeRawRequest({ url: imageUrl })
318 }) 318 })
319 }) 319 })
320 320
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index ae4b3cf5f..083fd43ca 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -6,6 +6,7 @@ import {
6 cleanupTests, 6 cleanupTests,
7 createMultipleServers, 7 createMultipleServers,
8 doubleFollow, 8 doubleFollow,
9 makeGetRequest,
9 makeRawRequest, 10 makeRawRequest,
10 PeerTubeServer, 11 PeerTubeServer,
11 PluginsCommand, 12 PluginsCommand,
@@ -461,30 +462,41 @@ describe('Test plugin filter hooks', function () {
461 }) 462 })
462 463
463 it('Should run filter:api.download.torrent.allowed.result', async function () { 464 it('Should run filter:api.download.torrent.allowed.result', async function () {
464 const res = await makeRawRequest(downloadVideos[0].files[0].torrentDownloadUrl, 403) 465 const res = await makeRawRequest({ url: downloadVideos[0].files[0].torrentDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
465 expect(res.body.error).to.equal('Liu Bei') 466 expect(res.body.error).to.equal('Liu Bei')
466 467
467 await makeRawRequest(downloadVideos[1].files[0].torrentDownloadUrl, 200) 468 await makeRawRequest({ url: downloadVideos[1].files[0].torrentDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
468 await makeRawRequest(downloadVideos[2].files[0].torrentDownloadUrl, 200) 469 await makeRawRequest({ url: downloadVideos[2].files[0].torrentDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
469 }) 470 })
470 471
471 it('Should run filter:api.download.video.allowed.result', async function () { 472 it('Should run filter:api.download.video.allowed.result', async function () {
472 { 473 {
473 const res = await makeRawRequest(downloadVideos[1].files[0].fileDownloadUrl, 403) 474 const res = await makeRawRequest({ url: downloadVideos[1].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
474 expect(res.body.error).to.equal('Cao Cao') 475 expect(res.body.error).to.equal('Cao Cao')
475 476
476 await makeRawRequest(downloadVideos[0].files[0].fileDownloadUrl, 200) 477 await makeRawRequest({ url: downloadVideos[0].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
477 await makeRawRequest(downloadVideos[2].files[0].fileDownloadUrl, 200) 478 await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
478 } 479 }
479 480
480 { 481 {
481 const res = await makeRawRequest(downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl, 403) 482 const res = await makeRawRequest({
483 url: downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl,
484 expectedStatus: HttpStatusCode.FORBIDDEN_403
485 })
486
482 expect(res.body.error).to.equal('Sun Jian') 487 expect(res.body.error).to.equal('Sun Jian')
483 488
484 await makeRawRequest(downloadVideos[2].files[0].fileDownloadUrl, 200) 489 await makeRawRequest({ url: downloadVideos[2].files[0].fileDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
490
491 await makeRawRequest({
492 url: downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl,
493 expectedStatus: HttpStatusCode.OK_200
494 })
485 495
486 await makeRawRequest(downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl, 200) 496 await makeRawRequest({
487 await makeRawRequest(downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl, 200) 497 url: downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl,
498 expectedStatus: HttpStatusCode.OK_200
499 })
488 } 500 }
489 }) 501 })
490 }) 502 })
@@ -515,12 +527,12 @@ describe('Test plugin filter hooks', function () {
515 }) 527 })
516 528
517 it('Should run filter:html.embed.video.allowed.result', async function () { 529 it('Should run filter:html.embed.video.allowed.result', async function () {
518 const res = await makeRawRequest(servers[0].url + embedVideos[0].embedPath, 200) 530 const res = await makeGetRequest({ url: servers[0].url, path: embedVideos[0].embedPath, expectedStatus: HttpStatusCode.OK_200 })
519 expect(res.text).to.equal('Lu Bu') 531 expect(res.text).to.equal('Lu Bu')
520 }) 532 })
521 533
522 it('Should run filter:html.embed.video-playlist.allowed.result', async function () { 534 it('Should run filter:html.embed.video-playlist.allowed.result', async function () {
523 const res = await makeRawRequest(servers[0].url + embedPlaylists[0].embedPath, 200) 535 const res = await makeGetRequest({ url: servers[0].url, path: embedPlaylists[0].embedPath, expectedStatus: HttpStatusCode.OK_200 })
524 expect(res.text).to.equal('Diao Chan') 536 expect(res.text).to.equal('Diao Chan')
525 }) 537 })
526 }) 538 })
diff --git a/server/tests/plugins/plugin-helpers.ts b/server/tests/plugins/plugin-helpers.ts
index 31c18350a..f2bada4ee 100644
--- a/server/tests/plugins/plugin-helpers.ts
+++ b/server/tests/plugins/plugin-helpers.ts
@@ -307,7 +307,7 @@ describe('Test plugin helpers', function () {
307 expect(file.fps).to.equal(25) 307 expect(file.fps).to.equal(25)
308 308
309 expect(await pathExists(file.path)).to.be.true 309 expect(await pathExists(file.path)).to.be.true
310 await makeRawRequest(file.url, HttpStatusCode.OK_200) 310 await makeRawRequest({ url: file.url, expectedStatus: HttpStatusCode.OK_200 })
311 } 311 }
312 } 312 }
313 313
@@ -321,12 +321,12 @@ describe('Test plugin helpers', function () {
321 const miniature = body.thumbnails.find(t => t.type === ThumbnailType.MINIATURE) 321 const miniature = body.thumbnails.find(t => t.type === ThumbnailType.MINIATURE)
322 expect(miniature).to.exist 322 expect(miniature).to.exist
323 expect(await pathExists(miniature.path)).to.be.true 323 expect(await pathExists(miniature.path)).to.be.true
324 await makeRawRequest(miniature.url, HttpStatusCode.OK_200) 324 await makeRawRequest({ url: miniature.url, expectedStatus: HttpStatusCode.OK_200 })
325 325
326 const preview = body.thumbnails.find(t => t.type === ThumbnailType.PREVIEW) 326 const preview = body.thumbnails.find(t => t.type === ThumbnailType.PREVIEW)
327 expect(preview).to.exist 327 expect(preview).to.exist
328 expect(await pathExists(preview.path)).to.be.true 328 expect(await pathExists(preview.path)).to.be.true
329 await makeRawRequest(preview.url, HttpStatusCode.OK_200) 329 await makeRawRequest({ url: preview.url, expectedStatus: HttpStatusCode.OK_200 })
330 } 330 }
331 }) 331 })
332 332
diff --git a/server/tests/shared/streaming-playlists.ts b/server/tests/shared/streaming-playlists.ts
index 74c25e99c..8ee04d921 100644
--- a/server/tests/shared/streaming-playlists.ts
+++ b/server/tests/shared/streaming-playlists.ts
@@ -1,9 +1,13 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
1import { expect } from 'chai' 3import { expect } from 'chai'
2import { basename } from 'path' 4import { basename } from 'path'
3import { removeFragmentedMP4Ext } from '@shared/core-utils' 5import { removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
4import { sha256 } from '@shared/extra-utils' 6import { sha256 } from '@shared/extra-utils'
5import { HttpStatusCode, VideoStreamingPlaylist } from '@shared/models' 7import { HttpStatusCode, VideoStreamingPlaylist, VideoStreamingPlaylistType } from '@shared/models'
6import { PeerTubeServer } from '@shared/server-commands' 8import { makeRawRequest, PeerTubeServer, webtorrentAdd } from '@shared/server-commands'
9import { expectStartWith } from './checks'
10import { hlsInfohashExist } from './tracker'
7 11
8async function checkSegmentHash (options: { 12async function checkSegmentHash (options: {
9 server: PeerTubeServer 13 server: PeerTubeServer
@@ -75,8 +79,118 @@ async function checkResolutionsInMasterPlaylist (options: {
75 expect(playlistsLength).to.have.lengthOf(resolutions.length) 79 expect(playlistsLength).to.have.lengthOf(resolutions.length)
76} 80}
77 81
82async function completeCheckHlsPlaylist (options: {
83 servers: PeerTubeServer[]
84 videoUUID: string
85 hlsOnly: boolean
86
87 resolutions?: number[]
88 objectStorageBaseUrl: string
89}) {
90 const { videoUUID, hlsOnly, objectStorageBaseUrl } = options
91
92 const resolutions = options.resolutions ?? [ 240, 360, 480, 720 ]
93
94 for (const server of options.servers) {
95 const videoDetails = await server.videos.get({ id: videoUUID })
96 const baseUrl = `http://${videoDetails.account.host}`
97
98 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
99
100 const hlsPlaylist = videoDetails.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
101 expect(hlsPlaylist).to.not.be.undefined
102
103 const hlsFiles = hlsPlaylist.files
104 expect(hlsFiles).to.have.lengthOf(resolutions.length)
105
106 if (hlsOnly) expect(videoDetails.files).to.have.lengthOf(0)
107 else expect(videoDetails.files).to.have.lengthOf(resolutions.length)
108
109 // Check JSON files
110 for (const resolution of resolutions) {
111 const file = hlsFiles.find(f => f.resolution.id === resolution)
112 expect(file).to.not.be.undefined
113
114 expect(file.magnetUri).to.have.lengthOf.above(2)
115 expect(file.torrentUrl).to.match(
116 new RegExp(`http://${server.host}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
117 )
118
119 if (objectStorageBaseUrl) {
120 expectStartWith(file.fileUrl, objectStorageBaseUrl)
121 } else {
122 expect(file.fileUrl).to.match(
123 new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`)
124 )
125 }
126
127 expect(file.resolution.label).to.equal(resolution + 'p')
128
129 await makeRawRequest({ url: file.torrentUrl, expectedStatus: HttpStatusCode.OK_200 })
130 await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
131
132 const torrent = await webtorrentAdd(file.magnetUri, true)
133 expect(torrent.files).to.be.an('array')
134 expect(torrent.files.length).to.equal(1)
135 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
136 }
137
138 // Check master playlist
139 {
140 await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
141
142 const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
143
144 let i = 0
145 for (const resolution of resolutions) {
146 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
147 expect(masterPlaylist).to.contain(`${resolution}.m3u8`)
148
149 const url = 'http://' + videoDetails.account.host
150 await hlsInfohashExist(url, hlsPlaylist.playlistUrl, i)
151
152 i++
153 }
154 }
155
156 // Check resolution playlists
157 {
158 for (const resolution of resolutions) {
159 const file = hlsFiles.find(f => f.resolution.id === resolution)
160 const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
161
162 const url = objectStorageBaseUrl
163 ? `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}`
164 : `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
165
166 const subPlaylist = await server.streamingPlaylists.get({ url })
167
168 expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
169 expect(subPlaylist).to.contain(basename(file.fileUrl))
170 }
171 }
172
173 {
174 const baseUrlAndPath = objectStorageBaseUrl
175 ? objectStorageBaseUrl + 'hls/' + videoUUID
176 : baseUrl + '/static/streaming-playlists/hls/' + videoUUID
177
178 for (const resolution of resolutions) {
179 await checkSegmentHash({
180 server,
181 baseUrlPlaylist: baseUrlAndPath,
182 baseUrlSegment: baseUrlAndPath,
183 resolution,
184 hlsPlaylist
185 })
186 }
187 }
188 }
189}
190
78export { 191export {
79 checkSegmentHash, 192 checkSegmentHash,
80 checkLiveSegmentHash, 193 checkLiveSegmentHash,
81 checkResolutionsInMasterPlaylist 194 checkResolutionsInMasterPlaylist,
195 completeCheckHlsPlaylist
82} 196}
diff --git a/server/tests/shared/videos.ts b/server/tests/shared/videos.ts
index e18329e07..c8339584b 100644
--- a/server/tests/shared/videos.ts
+++ b/server/tests/shared/videos.ts
@@ -125,9 +125,9 @@ async function completeVideoCheck (
125 expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`)) 125 expect(file.fileDownloadUrl).to.match(new RegExp(`http://${originHost}/download/videos/${uuidRegex}-${file.resolution.id}${extension}`))
126 126
127 await Promise.all([ 127 await Promise.all([
128 makeRawRequest(file.torrentUrl, 200), 128 makeRawRequest({ url: file.torrentUrl, expectedStatus: HttpStatusCode.OK_200 }),
129 makeRawRequest(file.torrentDownloadUrl, 200), 129 makeRawRequest({ url: file.torrentDownloadUrl, expectedStatus: HttpStatusCode.OK_200 }),
130 makeRawRequest(file.metadataUrl, 200) 130 makeRawRequest({ url: file.metadataUrl, expectedStatus: HttpStatusCode.OK_200 })
131 ]) 131 ])
132 132
133 expect(file.resolution.id).to.equal(attributeFile.resolution) 133 expect(file.resolution.id).to.equal(attributeFile.resolution)